If I restart Asterisk, about 10 core dumps appear in /tmp directory, all with the same crash. Executing ps -ef manually does not reproduce crash.
gdb output:
Core was generated by `ps -ef'.
Program terminated with signal 11, Segmentation fault.
#0 reset_global () at ps/global.c:362
362 look_up_our_self(&p);
(gdb)
Disassmble:
0x0000000000403040 <+0>: push %rbp
0x0000000000403041 <+1>: mov $0xdeadbeef,%eax
0x0000000000403046 <+6>: push %rbx
0x0000000000403047 <+7>: sub $0x80028,%rsp
0x000000000040304e <+14>: mov 0x21147b(%rip),%rbx # 0x6144d0 <selection_list>
0x0000000000403055 <+21>: cmp %rax,%rbx
0x0000000000403058 <+24>: je 0x403084 <reset_global+68>
0x000000000040305a <+26>: test %rbx,%rbx
0x000000000040305d <+29>: jne 0x40306b <reset_global+43>
0x000000000040305f <+31>: jmp 0x403084 <reset_global+68>
0x0000000000403061 <+33>: nopl 0x0(%rax)
0x0000000000403068 <+40>: mov %rbp,%rbx
0x000000000040306b <+43>: mov 0x8(%rbx),%rdi
0x000000000040306f <+47>: mov (%rbx),%rbp
0x0000000000403072 <+50>: callq 0x4017e8 <free#plt>
0x0000000000403077 <+55>: mov %rbx,%rdi
0x000000000040307a <+58>: callq 0x4017e8 <free#plt>
0x000000000040307f <+63>: test %rbp,%rbp
0x0000000000403082 <+66>: jne 0x403068 <reset_global+40>
0x0000000000403084 <+68>: lea 0x80010(%rsp),%rbx
0x000000000040308c <+76>: mov $0x634680,%edi
0x0000000000403091 <+81>: movq $0x0,0x211434(%rip) # 0x6144d0 <selection_list>
=> 0x000000000040309c <+92>: callq 0x401908 <look_up_our_self#plt>
0x00000000004030a1 <+97>: xor %eax,%eax
0x00000000004030a3 <+99>: mov %rbx,%rdx
0x00000000004030a6 <+102>: mov $0x5413,%esi
0x00000000004030ab <+107>: mov $0x1,%edi
0x00000000004030b0 <+112>: callq 0x401698 <ioctl#plt>
0x00000000004030b5 <+117>: cmp $0xffffffffffffffff,%eax
0x00000000004030b8 <+120>: je 0x4032e0 <reset_global+672>
...
What is this: 0x0000000000403041 <+1>: mov $0xdeadbeef,%eax ?
info registers:
(gdb) info registers
rax 0xdeadbeef 3735928559
rbx 0x7849d15d9e60 132258440519264
rcx 0x0 0
rdx 0x0 0
rsi 0x7849d15d9de0 132258440519136
rdi 0x634680 6506112
rbp 0x0 0x0
rsp 0x7849d1559e50 0x7849d1559e50
r8 0x0 0
r9 0xff3212ff2a1f09ff -57962958069757441
r10 0x8 8
r11 0x206 518
r12 0x2 2
r13 0x7849d15da0a8 132258440519848
r14 0x0 0
r15 0x0 0
rip 0x40309c 0x40309c <reset_global+92>
eflags 0x10246 [ PF ZF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
System info:
3.14.32-xxxx-grs-ipv6-64 #7 SMP Wed Jan 27 18:05:09 CET 2016 x86_64 x86_64 x86_64 GNU/Linux
rpm -qa | grep glibc
glibc-2.12-1.166.el6_7.7.x86_64
glibc-debuginfo-common-2.12-1.166.el6_7.7.x86_64
glibc-headers-2.12-1.166.el6_7.7.x86_64
glibc-2.12-1.166.el6_7.7.i686
glibc-common-2.12-1.166.el6_7.7.x86_64
glibc-debuginfo-2.12-1.166.el6_7.7.x86_64
glibc-devel-2.12-1.166.el6_7.7.x86_64
I am not sure how to proceed from here, tried to reinstall Linux from scratch but getting same result.
=> 0x000000000040309c <+92>: callq 0x401908 <look_up_our_self#plt>
It is somewhat unusual for a program to die on CALL (or PUSH) instruction, and whenever that happens, it's almost guaranteed that you have a stack overflow. Further,
0x0000000000403047 <+7>: sub $0x80028,%rsp
this function requires half MiB of stack, which is also quite unusual, and large. Looking at the rest of disassembly, if JE at 0x403058 was taken, then CALL at 0x40309c would be the first instruction trying to push something onto stack after the large decrement.
Conclusion: the environment in which Asterix executes ps -ef has a uimit -s that is set too small.
What is this: 0x0000000000403041 <+1>: mov $0xdeadbeef,%eax ?
The code compares %RAX with selection_list. The 0xdeadbeef is clearly a sentinel value.
Related
I'm trying to copy a string s1 into s2 in assembly, here's my code:
global main
main:
.init:
; push r8, r9, r12, rcx
.strcpy:
lea r8, [rel s1]
lea r9, [rel s2]
mov rcx, 0
.copy:
cmp rcx, 7
je .fini
xor r12, r12
mov r12b, [byte r9]
mov [r8], r12b
inc r8
inc r9
inc rcx
jmp .copy
.fini:
; pop rcx, r12, r9, r8
ret
.data:
s1 db "coucou"
s2 db "bonjour"
I got a segfault line 16 (mov [r8], r12b). I've set a breakpoint at this line in gdb, the register r12 well contains 0x62 ('b'), and the registers r8 / r9 contains respectively the addresses of s1 and s2.
Am I doing it wrong? I first thought it was a permission problem, but in my real program (this one is very simplified), I add the write permission to the text section with mprotect (and check that it succeeds).
Note that I know the .data label is in the text section but I have to do it this way.
I commented out a short version for the actual push / pop operations for clarity.
EDIT:
Here's the code that fails, with more context:
global main
;##############################################################################
; Just some macros to avoid heavy push / pop boilerplates in the code
%macro pushx 1-*
%rep %0
push %1
%rotate 1
%endrep
%endmacro
%macro popx 1-*
%rep %0
%rotate -1
pop %1
%endrep
%endmacro
;##############################################################################
main:
.init:
pushx rdi, rsi, rdx, rax, r10, r11
mov r10, 0xff ; base addr of the first page containing the .text section
mov r11, 0xff ; len for mrpotects calls
.addrights:
pushx r10, r11
mov rdi, r10 ;
mov rsi, r11 ;
mov rdx, 7 ; PROT_WRITE | PROT_READ | PROT_EXEC
mov rax, 10 ; sys_mprotect
syscall ;
cmp rax, 0 ; check for return value
jl .err ; infinite loop on error
popx r10, r11
.matcpy:
call matcpy
.removerights:
mov rdi, r10 ;
mov rsi, r11 ;
mov rdx, 5 ; PROT_EXEC | PROT_READ
mov rax, 10 ; sys_mprotect
syscall
.fini:
popx rdi, rsi, rdx, rax, r10, r11
ret
.err:
jmp $
;##############################################################################
.data:
mat dd 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, \
0x00000000, 0x00000000, 0x00000000, 0x00000000, \
0x00000000, 0x00000000, 0x00000000, 0x00000042, \
0x00000000, 0x00000000, 0x00000042, 0x00000042
cpy dd 0, 0, 0, 0 ,\
0, 0, 0, 0, \
0, 0, 0, 0, \
0, 0, 0, 0
;##############################################################################
matcpy:
.init:
pushx r10, r11, r12, rcx
.code:
lea r10, [rel mat]
lea r11, [rel cpy]
mov rcx, 0
.copy:
cmp rcx, 64
je .fini
xor r12, r12
mov r12b, byte [r10]
mov [r11], r12b
inc r10
inc r11
inc rcx
jmp .copy
.fini:
popx r10, r11, r12, rcx
ret
r10 and r11 are hardcoded values that I replace when I create the segment (Im doing a packer and have to inject code).
Here's an exemple of their content at runtime: (before the matcpy call)
r10 0x401000 4198400
r11 0x215 533
And here's the mappings for of my program:
00400000-00401000 r--p 00000000 00:36 31195213 /mnt/nfs/homes/...
00401000-00402000 rwxp 00001000 00:36 31195213 /mnt/nfs/homes/...
00402000-00403000 r--p 00002000 00:36 31195213 /mnt/nfs/homes/...
00403000-00404000 r--p 00002000 00:36 31195213 /mnt/nfs/homes/...
00404000-00405000 rw-p 00003000 00:36 31195213 /mnt/nfs/homes/...
0c001000-0c002000 r-xp 00005000 00:36 31195213 /mnt/nfs/homes/...
More, the program doesn't loops, meaning rax isn't negative (mprotect succeeds).
Note that it's working if I try the same thing in the .data section. Seems it's a permission issue even if I have the write access mode set on my code section.
Yes, another stupid mistake.
I added the write permission at runtime for the .text section.
But the code that I inject was in another section, and the used data was so.
I just added a PF_W flag in the program header's p_flags field of the corresponding segment and it worked like a charm.
So i am writing a program in linux x86_64 assembly, the program needs to open a test directory specified in a "section data" , then list the bytes readed from a file with system call "getdents64" , then parse the bytes to get the filenames since "getdents64" returns the number of bytes.
here is the code
global _start
section .data
dir : db "test",0
len: equ 1024 ;define buffer size
section .bss
buffer: resb len
section .text
_start:
;open folder
mov rax, 2 ;sys_open
mov rdi, dir ;folder to open
mov rsi, 0 ;read only
mov rdx, 0
syscall
cmp rax, 0 ;if there is no folder go to exit
jbe exit
list:
mov rdi, rax ;directory in rdi
mov rax, 217 ;sys_getdents64
mov rsi, buffer
mov rdx, len ;length of the buffer
syscall
xchg r10, rax ;save buffer in r10 to loop through it
xor rax, rax ;zero out rax for the next system call
close:
mov rax, 3 ;sys_close
syscall
find_fname_start:
; look for the sequence 0008 which occurs before the start of a filename
add r15, 1
cmp r15, len
jge exit
cmp byte [buffer+r15],
jnz find_fname_start
add r15, 1
cmp byte [buffer+r15], 0x08
jnz find_fname_start
xor rcx, rcx
find_fname_end:
; look for the 00 which denotes the end of a filename
add r15, 1 ;
cmp r15, len
jge exit
mov rdi, [buffer+r15] ;<<< PROBLEM
mov [r15+rcx], rdi
inc rcx ;increment offset stored in rcx
cmp byte [buffer+r15], 0x00 ;denotes end of the filename
jnz find_fname_end
mov byte [r15+rcx], 0x00 ;filename should be in r15
;delete the file
jmp find_fname_start
unlink:
cmp r10, 0 ;if done, exit the program
jbe exit
mov rax, 87 ;sys_unlink
mov rdi, buffer ;list of files
syscall
jmp unlink
exit:
mov rax, 60 ;sys_exit
mov rdi, 80
syscall
i used gdb to investigate the problem and at the in "find_fname_end", when i try to move byte from a file to a buffer it gets an error
"Program received signal SIGSEGV, Segmentation fault.
0x0000000000400136 in find_fname_end ()"
i put a arrow in code to show you the line that gets this output
Program terminated with signal 11, Segmentation fault.
(gdb) info registers
rax 0x0 0
rip 0x496272 0x496272 <my_function+464>
eflags 0x10202 [ IF RF ]
The code in question looks like this.
if ( ... a->b->c.d & 0x100 ... )
The disassembly is
0x0000000000496262 <+448>: mov rax,QWORD PTR [rbp-0x30]
0x0000000000496266 <+452>: mov rax,QWORD PTR [rax+0x10]
0x000000000049626a <+456>: mov eax,DWORD PTR [rax+0x48]
0x000000000049626d <+459>: and eax,0x100
=> 0x0000000000496272 <+464>: test eax,eax
0x0000000000496274 <+466>: je 0x49628b <my_function+489>
...
0x0000000000496286 <+484>: jmp 0x49636f <my_function+717>
0x000000000049628b <+489>: mov rax,QWORD PTR [rbp-0x30]
0x000000000049628f <+493>: mov rax,QWORD PTR [rax+0x10]
There doesn't appear to be any memory access problems since GDB can print the accessed field, and the code didn't fault on the mov operation.
(gdb) p a->b->c.d
$1 = 0
There are no other threads running at the same time.
My guess currently is that the problem is somewhere else and GDB or the core file has incomplete or incorrect information.
The build machine is SLES9
The compiler is GCC 3.4.4
The execution host is RHEL6.4
GDB is 7.2-60.el6
I use nasm to study assembly. Below is the source code:
[BITS 32]
mov ebx, 0
mov bx, 0
mov bl, 0
then I use 'ndisasm -b 32 test.bin' to get output as following:
00000000 BB00000000 mov ebx,0x0
00000005 66BB0000 mov bx,0x0
00000009 B300 mov bl,0x0
My question is the difference of BB00000000 and 66BB0000, I know the opcode of MOV is B, but what is the 66 before B?
0x66 is operand-size override prefix which means that if the default operand size is 32-bit (as your case is), it becomes 16-bit a the opcode works on 16-bit size registers (bx in your case)
What's wrong with my code at github?
Strace shows the first read is ok but second one suddenly EBADF.
Hardcoding fd to 3 makes it successfully read and print a file in several chunks.
Using statically allocated buffer with descriptor still passed through the fd binding as shown in the master branch on github makes it also work.
gdb shows the memory contents at the location of fd gets corrupted right after making the first read syscall.
Makes me think the memory behind fd gets overriden when read into the buf happens but I have no idea why.
main.asm:
%define SYS_READ 0
%define SYS_WRITE 1
%define SYS_OPEN 2
%define SYS_MMAP 9
%define SYS_EXIT 60
%define PROT_READ 0x1
%define PROT_WRITE 0x2
%define MAP_ANONYMOUS 0x20
%define MAP_PRIVATE 0x02
%define STD_OUT 1
%define STD_ERR 2
%define O_RDONLY 0
%define CHUNK_SIZE 0x10 ; small to get multiple chunk reads on
; small input
; %define CHUNK_SIZE 0x1000 ; 4KiB
section .data
buf dq 0
fd dq 0
merr_read_failed db "read() failed", 0xa
merr_read_failed_size equ $-merr_read_failed
merr_open_failed db "open() failed", 0xa
merr_open_failed_size equ $-merr_read_failed
section .text
global _start
_start:
; unused labels make it easier to set breakpoints in gdb
open_file:
pop rdi
pop rdi
; pop argv[1] into rdi
pop rdi
; exit if NULL
cmp rdi, 0
je exit_bad
; open argv[1]
mov rax, SYS_OPEN
mov rsi, O_RDONLY
syscall
cmp rax, 0
jl err_open_failed
mov [fd], rax
jmp read_chunk
allocate_buffer:
mov rax, SYS_MMAP
mov rdi, 0x0
mov rsi, CHUNK_SIZE
mov rdx, PROT_READ | PROT_WRITE
mov r10, MAP_ANONYMOUS | MAP_PRIVATE
mov r8, -1
mov r9, 0
syscall
mov [buf], rax
read_chunk:
mov rax, SYS_READ
mov rdi, [fd]
mov rsi, buf
mov rdx, CHUNK_SIZE
syscall
; check for error
cmp rax, 0
jl err_read_failed
; save read byte count to r10
mov r10, rax
print_chunk:
; if last read yielded 0 bytes, exit.
; as 0 signifies an EOF
cmp r10, 0
je exit_ok
mov rax, SYS_WRITE
mov rdi, STD_OUT
mov rsi, buf
mov rdx, r10
syscall
; repeat
jmp read_chunk
err_open_failed:
mov rax, SYS_WRITE
mov rdi, STD_ERR
mov rsi, merr_open_failed
mov rdx, merr_open_failed_size
syscall
jmp exit_bad
err_read_failed:
mov rax, SYS_WRITE
mov rdi, STD_ERR
mov rsi, merr_read_failed
mov rdx, merr_read_failed_size
syscall
jmp exit_bad
exit_bad:
mov rdi, 1
jmp exit
exit_ok:
mov rdi, 0
jmp exit
exit:
mov rax, SYS_EXIT
syscall
Makefile:
all:
nasm -g -f elf64 -o main.o main.asm
ld -o main main.o
strace:
% strace ./main Makefile
execve("./main", ["./main", "Makefile"], [/* 54 vars */]) = 0
open("Makefile", O_RDONLY) = 3
read(3, "all:\n\n\tnasm -g -", 16) = 16
write(1, "all:\n\n\tnasm -g -", 16all:
nasm -g -) = 16
read(544043873, 0x6001e8, 16) = -1 EBADF (Bad file descriptor)
write(2, "read() failed\n", 14read() failed
) = 14
_exit(1) = ?
+++ exited with 1 +++
gdb:
(gdb) disassemble
Dump of assembler code for function read_chunk:
=> 0x000000000040014c <+0>: mov $0x0,%eax
0x0000000000400151 <+5>: mov 0x6001ec,%rdi
0x0000000000400159 <+13>: movabs $0x6001e4,%rsi
0x0000000000400163 <+23>: mov $0x10,%edx
0x0000000000400168 <+28>: syscall
0x000000000040016a <+30>: cmp $0x0,%rax
0x000000000040016e <+34>: jl 0x4001b1 <err_read_failed>
0x0000000000400170 <+36>: mov %rax,%r10
End of assembler dump.
(gdb) p 0x6001ec
$1 = 6291948
(gdb) i r rdi
rdi 0x0 0
(gdb) si
0x0000000000400151 in read_chunk ()
(gdb) i r rdi
rdi 0x0 0
(gdb) p 0x6001ec
$2 = 6291948
(gdb) x 0x6001ec
0x6001ec <fd>: 0x00000003
(gdb) si
0x0000000000400159 in read_chunk ()
(gdb) x 0x6001ec
0x6001ec <fd>: 0x00000003
(gdb) si
0x0000000000400163 in read_chunk ()
(gdb) x 0x6001ec
0x6001ec <fd>: 0x00000003
(gdb) si
0x0000000000400168 in read_chunk ()
(gdb) x 0x6001ec
0x6001ec <fd>: 0x00000003
(gdb) si
0x000000000040016a in read_chunk ()
(gdb) x 0x6001ec
0x6001ec <fd>: 0x206d7361
(gdb) disassemble
Dump of assembler code for function read_chunk:
0x000000000040014c <+0>: mov $0x0,%eax
0x0000000000400151 <+5>: mov 0x6001ec,%rdi
0x0000000000400159 <+13>: movabs $0x6001e4,%rsi
0x0000000000400163 <+23>: mov $0x10,%edx
0x0000000000400168 <+28>: syscall
=> 0x000000000040016a <+30>: cmp $0x0,%rax
0x000000000040016e <+34>: jl 0x4001b1 <err_read_failed>
0x0000000000400170 <+36>: mov %rax,%r10
End of assembler dump.
(gdb)
You never call the allocate_buffer instructions and you use bad level of indirection in read_chunk and print_chunk. With this patch applied, your code works:
diff --git a/main.asm b/main.asm
index c9c98e4..8c44223 100644
--- a/main.asm
+++ b/main.asm
## -51,7 +51,6 ## open_file:
cmp rax, 0
jl err_open_failed
mov [fd], rax
- jmp read_chunk
allocate_buffer:
mov rax, SYS_MMAP
## -67,7 +66,7 ## allocate_buffer:
read_chunk:
mov rax, SYS_READ
mov rdi, [fd]
- mov rsi, buf
+ mov rsi, [buf]
mov rdx, CHUNK_SIZE
syscall
## -85,7 +84,7 ## print_chunk:
mov rax, SYS_WRITE
mov rdi, STD_OUT
- mov rsi, buf
+ mov rsi, [buf]
mov rdx, r10
syscall
The jmp removal lets allocate_buffer instructions be executed, the other two changes make the syscalls use the address of allocated memory instead of address, where this address is stored.
What happened?
You did not allocate the memory for the buffer as your strace shows (no mmap syscall is performed). The buf is address of 8 bytes of memory (dq) in the data section, which is initialized to 0 when the program starts.
Your code reads into buf directly and thus overwrites contents of the data section, including fp which is right after buf. Second iteration of read_chunk finds a strange value in fp and crashes.
When you skipped the allocation and changed your code to read into [buf], as we discussed in the comments, you read into address 0, which crashed your program even earlier from now obvious reasons.