在x86_64上读取syscall到nasm中的mmap分配缓冲区之后的错误文件描述符

时间:2014-12-20 14:45:16

标签: linux nasm x86-64

我的code at github出了什么问题?

  • Strace显示第一个read正常但第二个突然显示EBADF
  • fd硬编码为3使其成功读取并以多个块打印文件。
  • 使用静态分配的缓冲区,其中描述符仍然通过fd绑定传递,如github上的master分支所示,这样它也可以工作。
  • gdb显示fd位置的内存内容在发出第一个read系统调用后立即被损坏。

让我觉得fd后面的内存在读入buf时会被覆盖,但我不明白为什么。

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

生成文件:

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)

1 个答案:

答案 0 :(得分:1)

您永远不会调用allocate_buffer指令,并且在read_chunkprint_chunk中使用了错误的间接级别。应用此修补程序后,您的代码将起作用:

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

jmp删除允许执行allocate_buffer指令,另外两个更改使系统调用使用已分配内存的地址而不是存储此地址的地址。

发生了什么事?

您没有为strace显示缓冲区分配内存(不执行mmap系统调用)。 buf是数据部分中8字节内存(dq)的地址,在程序启动时初始化为0。

您的代码会直接读入buf,从而覆盖数据部分的内容,包括fp之后的bufread_chunk的第二次迭代在fp中找到一个奇怪的值并崩溃。

当您跳过分配并更改代码以读入[buf]时,正如我们在评论中所讨论的那样,您会读到地址0,从现在出现明显的原因,它甚至更早地破坏了您的程序。