Bad file descriptor after read syscall into mmap allocated buffer in nasm on x86_64 - linux

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.

Related

Nasm: Print on console

I made this program that open a image file and find a hidden message in the file. I have to print the message on the console.
i think the part where i open the file and search for the hidden message is right, but i don´t know for sure because i can´t print the output to confirm.
this is the code:
; Example program to demonstrate file I/O.
; This example will open/create a file, write some
; information to the file, and close the file.
; Note, the file name is hard-coded for this example.
; This example program will open a file, read the
; contents, and write the contents to the screen.
; This routine also provides some very simple examples
; regarding handling various errors on system services.
; -------------------------------------------------------
section .data
; -----
; Define standard constants.
LF equ 10 ; line feed
NULL equ 0 ; end of string
TRUE equ 1
FALSE equ 0
EXIT_SUCCESS equ 0 ; success code
STDIN equ 0 ; standard input
STDOUT equ 1 ; standard output
STDERR equ 2 ; standard error
SYS_write equ 1; write
SYS_read equ 0 ; read
SYS_open equ 2 ; file open
SYS_close equ 3 ; file close
SYS_exit equ 60 ; terminate
SYS_creat equ 85 ; file open/create
SYS_time equ 201 ; get time
O_CREAT equ 0x40
O_TRUNC equ 0x200
O_APPEND equ 0x400
O_RDONLY equ 000000q ; read only
O_WRONLY equ 000001q ; write only
S_IRUSR equ 00400q
S_IWUSR equ 00200q
S_IXUSR equ 00100q
; -----
; Variables/constants for main.
BUFF_SIZE equ 256
newLine db LF, NULL
db LF, LF, NULL
fileName dq 6
fileDesc dq 0
errMsgOpen db "Error opening the file.", LF, NULL
errMsgRead db "Error reading from the file.", LF, NULL
offset db 1
size db 1
; -------------------------------------------------------
section .bss
readBuffer: resb BUFF_SIZE
; -------------------------------------------------------
section .text
global _start
_start:
xor rax, rax
xor rcx, rcx
xor rdx, rdx
xor rbx, rbx
mov rbx, rsp
mov rax, qword [rbx+16]
mov [fileName], rax
xor rax, rax
push rsp
; Attempt to open file - Use system service for file open
openInputFile:
pop rax ; pop argc value - should be 2
cmp rax, 0x2
jne done
mov rax, SYS_open ; file open
mov rdi, fileName ; file name string
mov rsi, O_RDONLY ; read only access
syscall ; call the kernel
cmp rax, 0 ; check for success
jl errorOnOpen
mov qword [fileDesc], rax ; save descriptor
; -----
; Read from file.
;
mov rax, SYS_read
mov rdi, qword [fileDesc]
mov rsi, readBuffer
mov rdx, BUFF_SIZE
syscall
cmp rax, 0
jl errorOnRead
; -----
; Print the buffer.
; add the NULL for the print string
mov rax, qword [readBuffer+2] ;SSSS
mov qword [size], rax
mov rax, qword [readBuffer+10] ;OOOO
mov [offset], rax
mov rcx, 0
ler: ;ler um caracter
mov rbp, 0
xor rbx, rbx
xor rdx, rdx
lerloop:
inc rax
cmp rax, [size]
je errorOnRead
saltaQuartobyte:
inc ecx
cmp ecx, 4
jne continua
inc rax ;incremneta rax outra vez para saltar o quarto
cmp rax, [size]
je errorOnRead
mov ecx, 0
continua:
mov bl, byte [readBuffer+rax]
shl rdx, 1
shr rbx, 1
adc rdx, 0
inc rbp
cmp rbp, 7 ;fim do caracter ASCII, 0X 0XXX XXXX
jne lerloop
mov rdi, rdx
call printString ; imprime caracter ASCII
cmp rax, [size] ;comea o priximo
jne ler
; -----
; Close the file.
mov rax, SYS_close
mov rdi, qword [fileDesc]
syscall
jmp done
; -----
; Error on open.
errorOnOpen:
mov rdi, errMsgOpen
call printString
jmp done
; Error on read.
errorOnRead:
mov rdi, errMsgRead
call printString
jmp done
; -----
; program done.
done:
mov rax, SYS_exit
mov rdi, EXIT_SUCCESS
syscall
; **********************************************************
; Generic procedure to display a string to the screen.
global printString
printString:
push rbp
mov rbp, rsp
push rbp
; -----
; Call OS to output string.
mov rax, SYS_write ; code for write()
mov rsi, rdi ; addr of characters
mov rdi, STDOUT ; file descriptor
; count set above
syscall ; system call
; -----
; String printed, return to calling routine.
prtDone:
pop rbx
pop rbp
ret
this is my last attempt of the print but the program still don't print anything, and I don't understand why? or what's wrong?
A fundamental error
push rsp
; Attempt to open file - Use system service for file open
openInputFile:
pop rax ; pop argc value - should be 2
cmp rax, 0x2
jne done
mov rax, SYS_open ; file open
Your program starts with copying the stackpointer RSP to the accumulator RAX, and then compares to see if the value is 2, but when is the stackpointer ever going to be 2? And so, the program will always prematurely exit. No wonder that nothing gets printed.
A selection of other errors
offset db 1
size db 1
...
mov rax, qword [readBuffer+2] ;SSSS
mov qword [size], rax
mov rax, qword [readBuffer+10] ;OOOO
mov [offset], rax
You have reserved not enough room to store the qwords size and offset!
Better store them in the .bss section using resq:
section .bss
readBuffer: resb BUFF_SIZE
offset resq 1
size resq 1
mov rax, qword [readBuffer+2] ;SSSS
mov qword [size], rax
mov rax, qword [readBuffer+10] ;OOOO
mov [offset], rax
If the file is a .BMP (I'm considering those well-known offsets), then the size and offset fields are actually dwords. You would have to write instead (loading EAX automatically zero extends the value into RAX):
mov eax, [readBuffer+2] ;SSSS
mov [size], rax
mov eax, [readBuffer+10] ;OOOO
mov [offset], rax
printString:
push rbp
mov rbp, rsp
push rbp <<<<
...
pop rbx <<<<
pop rbp
ret
Because of the mismatch, you're destroying RBX on return.
The code that you used depends on calculating the length of the ASCIIZ string. You cannot just leave that out!

How do I open a file in assembler and print the content of it?

I want to open a file in assembly. The file is called test.txt. I tried to open the file with open, and then the content is read into content. At last, I want to output the content with write, however I get a segfault. When I look at strace:
execve("./sh", ["./sh"], 0x7fffffffe980 /* 34 vars */) = 0
open("test.txt", O_WRONLY) = 3
read(3, 0x402000, 48) = -1 EBADF (Bad file descriptor)
write(1, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 48) = 48
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x30} ---
+++ killed by SIGSEGV +++
Some 0's are outputted. That means that this address contains nothing, and the read must have failed. Can someone help?
Here is the code:
section .bss
content: resb 0x30
section .text
global start
_start:
push rax
mov rbx, 0x7478742e74736574 ; test.txt
push qword rbx
mov rdi, rsp
push 0x1
pop rsi
xor rdx, rdx
mov al, 0x2
syscall
push rax
pop rdi
mov rsi, content
mov dl, 0x30
xor rax, rax
syscall
push 0x1
pop rdi
mov rsi, content
xor rax, rax
mov al, 0x1
syscall
mov al, 0x3c
xor rdi, rdi
syscall

how to fix killed by SIGSEGV (core dumped) error

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

Printing binary string in assembly

I'm writing a program to print binary string of a hardcoded word. Here is how it looks like currently:
main.asm
section .text
global _start
extern _print_binary_content
_start:
push word [word_to_print] ; pushing word. Can we push just one byte?
call _print_binary_content
mov rax, 60
mov rdi, 0
syscall
section .data
word_to_print: dw 0xAB0F
printer.asm
SYS_BRK_NUM equ 0x0C
BITS_IN_WORD equ 0x10
SYS_WRITE_NUM equ 0x01
STD_OUT_FD equ 0x01
FIRST_BIT_BIT_MASK equ 0x01
ASCII_NUMBER_OFFSET equ 0x30
section .text
global _print_binary_content
_print_binary_content:
pop rbp
xor ecx, ecx ;zeroing rcx
xor ebx, ebx ;zeroing rbx
pop bx ;the word to print the binary content of
;sys_brk for current location
mov rax, SYS_BRK_NUM
mov rdi, 0
syscall
;end sys_brk
mov r12, rax ;save the current brake location
;sys_brk for memory allocation 16 bytes
lea rdi, [rax + BITS_IN_WORD]
mov rax, SYS_BRK_NUM
syscall
;end sys_brk
xor ecx, ecx
mov cl, byte BITS_IN_WORD - 1; used as a counter in the loop below
loop:
mov dx, bx
and dx, FIRST_BIT_BIT_MASK
add dx, ASCII_NUMBER_OFFSET
mov [r12 + rcx], dl
shr bx, 0x01
dec cl
cmp cl, 0
jge loop
mov rsi, r12
mov rax, SYS_WRITE_NUM
mov rdi, STD_OUT_FD
mov rdx, BITS_IN_WORD
syscall
push rbp ; pushing return address back
ret
If I compile link and run this program it works. But the question is about performance and maybe conventions of writing assembly programs. In the file printer.asm I cleaned ecx twice which looks kind of not optimal. Maybe some registers were used not by their purpose (I used intel-manual).
Can you please help me to improve this very simple program?

64bit NASM file handling problems

I managed to write a NASM program on my 64bit Linux system which removes non-letter symbols from an input and prints each word in separate line. The problem is that I get RCX = -1 where i have to get the readed character number , and as a result I get segmentation fault. I've already spent hours trying to figure out how to fix this bug. Hope you guys will be able to help me. Thanks in advance.
Heres my code:
section .data
file1 db "data", 0
file2 db "results", 0
text times 255 db 0
textSize equ $ - text
buff times 255 db 0
buffSize equ $ - buff
section .text
global main
main:
mov rax, 2
mov rdi, file1
mov rsi, 0 ;read only
mov rdx, 0x7777
syscall ;open file1
mov rbx, rax ;save fd to rbx
mov rsi, text ; a pointer to the current character
mov rax, 0
mov rdi, rbx ;fd of file1
mov rsi, text
mov rdx, textSize
syscall ;read the text from file1
mov rax, 3
mov rdi, rbx
syscall ;close file1
mov rcx, rax ; rcx - character counter
mov rbx, buff ;rbx will be our buffer
cmp rcx, 0
je exit ; if nothing to read - exit
process_loop1:
mov dl, byte[rsi]
cmp byte[rsi], 0x41 ; "A"
jl inc1
cmp byte[rsi], 0x5a ; "Z"
jle save
cmp byte[rsi], 0x61 ; "a"
jl inc1
cmp byte[rsi], 0x7a ; "z"
jle save
jmp inc1 ;check text
inc1:
inc rsi
dec rcx
jnz process_loop1
jmp print
save:
mov byte [ebx], dl
jmp inc2 ;save letters
inc2:
inc rsi
inc rbx
dec rcx
jnz process_loop2
jmp print
process_loop2:
mov dl, byte[rsi]
cmp byte[rsi], 0x41 ; "A"
jl enter
cmp byte[rsi], 0x5a ; "Z"
jle save
cmp byte[rsi], 0x61 ; "a"
jl enter
cmp byte[rsi], 0x7a ; "z"
jle save
jmp enter
enter:
mov byte [ebx], 10 ;enter
inc rsi
inc rbx
dec rcx
jnz process_loop1
jmp print
print:
mov rax, 2
mov rdi, file2
mov rsi, 1 ;write only
mov rdx, 0x7777
syscall ;open file2
mov rbx, rax ;save fd to rbx
mov rax, 1
mov rdi, rbx
mov rsi, buff
mov rdx, buffSize
syscall ;print result
mov rax, 3
mov rdi, rbx
syscall ;close file2
jmp exit
exit:
mov rax, 60
mov rdi, 0
syscall
You have a sys_close between the sys_read and the time you try to check the number of bytes received. Thus, you are checking the return value of the close, not the read. Also note that rcx is destroyed by syscall so you can't just move up the mov rcx, rax line.
Also, in a few places you use [ebx] instead of [rbx].
Furthermore, you probably want use O_CREAT for the result file and only write as many bytes as you have processed, not buffSize.
section .data
filename db 'AVG.asm'
section .bss
buffer resb 2000
fd_in resb 1
section .text
global _start
_start:
mov rax,2
mov rdi,filename
mov rsi,0
mov rdx,0777
syscall
mov [fd_in],rax
mov rax,0
mov rdi,[fd_in]
mov rsi,buffer
mov rdx,2000
syscall
mov rax,1
mov rdi,1
mov rsi,buffer
mov rdx,2000
syscall
mov rax,3
mov rdi,[fd_in]
syscall
mov rax,60
mov rdi,0
syscall

Resources