Assembly x86 (32-bit), call to NR_creat (8) Corrupts Filename Storage
All, I have pulled my hair out trying to determine how the reserved storage I have for the filename is being corrupted by the call to file creat (NR_creat 8)
. The assembler is nasm
and the code is 32-bit compiled and running on an x86_64
box. The routine is a simple bit of code that takes the filename from the program arguments argv[1]
and then uses the name to create a file with octal permission of 0644. The file is written to and then the program exits. The file operations work, the problem is that I lose the filename stored in fnbuf
upon calling file creat
.
The reserved storage named fnbuf
is 32-bytes and is filled with argv[1]
in a simple mov [fnbuf], ebx
operation. The information in fnbuf
is fine up until the file is created, thereafter, the information in fnbuf
is corrupted and the address is changed. (all other stored information is fine). To retain the filename, I have ended up pushing it onto the stack (which works fine following the file create) I do not understand why the information in fnbuf
is corrupted and need help.
The relevant code is as follows along with brief gdb output (the full code follows at the end).
section .data
buflen equ 32
section .bss
fd_out resb 1
fd_in resb 1
fnbuf resb buflen
section .text
global _start
_start: ; linker entry point
; get the file_name from stdin (argv[1])
add esp, 8 ; skip over argc and argv[0] on stack (2 ptr = +8)
pop ebx ; pop argv[1] to ebx
test ebx, ebx ; test if null, jump
jz noarg ; (if pop to ecx, use jecxz - no test required)
mov [fnbuf], ebx ; save the filename in fnbuf (FIXME)
push ebx ; save on stack since fnbuf is getting whacked
; output fnbuf to stdout (fnbuf is fine here)
mov edi, [fnbuf] ; load string in edi for length calc & output
call strprn ; calc length and output to stdout
call newln ; output newline to stdout
; create the file (fnbuf is corrupted by this call)
mov eax, 8 ; system call number (sys_creat)
mov ebx, [fnbuf] ; set ebx to filename (fine here)
mov ecx, 0420 ; 644 octal -rw-r--r--
int 0x80 ; call kernel
jc errcf ; if carry flag non-zero, jmp errcf
mov [fd_out], eax ; save file descriptor in fd_out
; write msg to file
mov edi, msg ; msg address to edi for length
call strsz ; calc length of message to write (ret in edx)
mov eax, 4 ; system call number (sys_write)
mov ebx, [fd_out] ; file descriptor
mov ecx, msg ; message to write
int 0x80 ; call kernel
The code is built with the following and provides the following output using [fnbuf]
to write the filename to stdout
before the file creat
call, but afterward, must pop the saved argv[1]
from the stack to output the filename following file creat
. The use of [fnbuf]
works fine as well:
nasm -f elf -o ./obj/filecwr_32.o filecwr_32.asm -g
ld -m elf_i386 -o ./bin/filecwr_32 ./obj/filecwr_32.o
$ ./bin/filecwr_32 newfile.txt
newfile.txt
File write complete.
newfile.txt
$ cat newfile.txt
To whom it may concern, this information was written to the file
Stepping through the program with gdb
shows the corruption occurs at the kernel call for file creat
:
gdb ./bin/filecwr_32
(gdb) set args newfile.txt
(gdb) break 1
Breakpoint 1 at 0x8048080: file filecwr_32.asm, line 1.
(gdb) run
(gdb) watch fnbuf
Hardware watchpoint 2: fnbuf
(gdb) step
Single stepping until exit from function strsz,
which has no line number information.
0x08048095 in strprn ()
(gdb)
Single stepping until exit from function strprn,
which has no line number information.
newfile.txt0x080480f2 in _start ()
(gdb) x/s fnbuf
0xffffd2fd: "newfile.txt"
(gdb) step
...
Hardware watchpoint 2: fnbuf
Old value = -11523
New value = -11776
0x08048120 in _start ()
(gdb) x/s fnbuf
0xffffd200: "\376\336\377\377\023\337\377\377\036\337\377\377<\337\377\377\227\337\377\377"
...
[Inferior 1 (process 30000) exited normally]
(gdb) quit
Looking at the gdb
output above, the address reported for fnbuf
has changed? It was originally at 0xffffd2fd
, but then is reported at 0xffffd200
-- some 253 bytes further down the stack. This is bewildering and where I'm stuck. It is almost like one of the segment addresses is shifter around, but then I would expect the rest of the information to get corrupted as well. The other thoughts I had was that somehow fnbuf
was not being explicitly NUL-terminated
. I've set it to NUL and the problem and the problem persists. Other than that, I can't think of anything other than a stray x86 execution on x86_64 issue, but that seems like a stretch.
The complete code listing:
section .data
msg db 'To whom it may concern, this information was written to the file', 0xa, 0
msg_done db 'File write complete.', 0xa, 0
msg_noarg db 'No argument available for filename.', 0xa, 0
msg_create_fail db 'File create failed.', 0xa, 0
buflen equ 32
nwln db 0xa
section .bss
fd_out resb 1
fd_in resb 1
flen resb 1
fnbuf resb buflen
section .text
global _start
; szstr computes the length of a string.
; edi - string address
; edx - contains string length (returned)
strsz:
xor ecx, ecx ; zero rcx
not ecx ; set rcx = -1 (uses bitwise id: ~x = -x-1)
xor al,al ; zero the al register (initialize to NUL)
cld ; clear the direction flag
repnz scasb ; get the string length (dec ecx through NUL)
not ecx ; rev all bits of negative -> absolute value
dec ecx ; -1 to skip the null-term, ecx contains length
mov edx, ecx ; size returned in edx, ready to call write
ret
; strprn writes a string to the file descriptor.
; edi - string address
; edx - contains string length
strprn:
push edi ; push string address onto stack
call strsz ; call strsz to get length
pop ecx ; pop string to ecx esi (source index)
mov eax, 0x4 ; write/stdout number in eax (sys_write 4)
mov ebx, 0x1 ; set destination index to ebx (stdout 1)
int 0x80 ; call kernel
ret
; newln writes a newline to the file descriptor.
newln:
mov ecx, nwln ; set string index in ecx
mov ebx, 0x1 ; set destination index to (stdout 1)
mov edx, 0x1 ; set length of string in edx
mov eax, 0x4 ; mov write syscall number (4) to eax
int 0x80 ; call kernel
ret
; error function for no argument
noarg:
mov edi, msg_noarg ; error msg to edi for length calc
call strprn ; calc length and output to stdout
jmp exit
; error on fail to create file
errcf:
mov edi, msg_create_fail ; error msg to edi for length calc
call strprn ; calc length and output to stdout
jmp exit
_start: ; linker entry point
; get the file_name from stdin (argv[1])
add esp, 8 ; skip over argc and argv[0] on stack (2 ptr = +8)
pop ebx ; pop argv[1] to ebx
test ebx, ebx ; test if null, jump
jz noarg ; (if pop to ecx, use jecxz - no test required)
mov [fnbuf], ebx ; save the filename in fnbuf (FIXME)
push ebx ; save on stack since fnbuf is getting whacked
; output fnbuf to stdout (fnbuf is fine here)
mov edi, [fnbuf] ; load string in edi for length calc & output
call strprn ; calc length and output to stdout
call newln ; output newline to stdout
; create the file (fnbuf is corrupted by this call)
mov eax, 8 ; system call number (sys_creat)
mov ebx, [fnbuf] ; set ebx to filename (fine here)
mov ecx, 0420 ; 644 octal -rw-r--r--
int 0x80 ; call kernel
jc errcf ; if carry flag non-zero, jmp errcf
mov [fd_out], eax ; save file descriptor in fd_out
; write msg to file
mov edi, msg ; msg address to edi for length
call strsz ; calc length of message to write (ret in edx)
mov eax, 4 ; system call number (sys_write)
mov ebx, [fd_out] ; file descriptor
mov ecx, msg ; message to write
int 0x80 ; call kernel
; close the file
mov eax, 6 ; set eax sys_close
mov ebx, [fd_out] ; file descriptor in ebx
int 0x80
; print write done to stdout
mov edi, msg_done ; msg_done in ecx
call strprn ; calc length and output to stdout
; print file name to stdout
; mov edi, [fnbuf] ; fnbuf corrupted? Segment smashed?
pop edi ; pop original filename from stack
push edi ; save another copy since fnbuf is messed up
call strprn ; calc length and output to stdout
call newln ; output newline
jmp exit
exit:
xor ebx, ebx ; set exit code to 0
mov eax,1 ; system call number (sys_exit)
int 0x80 ; call kernel
I'm at a loss as to what has caused the corruption of fnbuf
. Moreover, what could be affecting that address alone while everthing else seems to be working as intended. Any help will be appreciated.