title: Bad file descriptor after read syscall into mmap allocated buffer in nasm on x86_64
body: <p>What's wrong with my <a href="https://github.com/yaccz/code-samples-asm-cat/tree/mmap" rel="nofollow">code at github</a>?</p>
  
  <ul>
  <li>Strace shows the first <code>read</code> is ok but second one suddenly <code>EBADF</code>.</li>
  <li>Hardcoding <code>fd</code> to 3 makes it successfully read and print a file in several chunks.</li>
  <li>Using statically allocated buffer with descriptor still passed through the <code>fd</code> binding as shown in the master branch on github makes it also work.</li>
  <li>gdb shows the memory contents at the location of <code>fd</code> gets corrupted right after making the first <code>read</code> syscall.</li>
  </ul>
  
  <p>Makes me think the memory behind <code>fd</code> gets overriden when read into the <code>buf</code> happens but I have no idea why.</p>
  
  <p>main.asm:</p>
  
  <pre><code>%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
  </code></pre>
  
  <p>Makefile:</p>
  
  <pre><code>all:
  
      nasm -g -f elf64 -o main.o main.asm
      ld -o main main.o
  </code></pre>
  
  <p>strace:</p>
  
  <pre><code>% 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 +++
  </code></pre>
  
  <p>gdb:</p>
  
  <pre><code>(gdb) disassemble
  Dump of assembler code for function read_chunk:
  =&gt; 0x000000000040014c &lt;+0&gt;:     mov    $0x0,%eax
     0x0000000000400151 &lt;+5&gt;:     mov    0x6001ec,%rdi
     0x0000000000400159 &lt;+13&gt;:    movabs $0x6001e4,%rsi
     0x0000000000400163 &lt;+23&gt;:    mov    $0x10,%edx
     0x0000000000400168 &lt;+28&gt;:    syscall
     0x000000000040016a &lt;+30&gt;:    cmp    $0x0,%rax
     0x000000000040016e &lt;+34&gt;:    jl     0x4001b1 &lt;err_read_failed&gt;
     0x0000000000400170 &lt;+36&gt;:    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 &lt;fd&gt;:  0x00000003
  (gdb) si
  0x0000000000400159 in read_chunk ()
  (gdb) x 0x6001ec
  0x6001ec &lt;fd&gt;:  0x00000003
  (gdb) si
  0x0000000000400163 in read_chunk ()
  (gdb) x 0x6001ec
  0x6001ec &lt;fd&gt;:  0x00000003
  (gdb) si
  0x0000000000400168 in read_chunk ()
  (gdb) x 0x6001ec
  0x6001ec &lt;fd&gt;:  0x00000003
  (gdb) si
  0x000000000040016a in read_chunk ()
  (gdb) x 0x6001ec
  0x6001ec &lt;fd&gt;:  0x206d7361
  (gdb) disassemble
  Dump of assembler code for function read_chunk:
     0x000000000040014c &lt;+0&gt;:     mov    $0x0,%eax
     0x0000000000400151 &lt;+5&gt;:     mov    0x6001ec,%rdi
     0x0000000000400159 &lt;+13&gt;:    movabs $0x6001e4,%rsi
     0x0000000000400163 &lt;+23&gt;:    mov    $0x10,%edx
     0x0000000000400168 &lt;+28&gt;:    syscall
  =&gt; 0x000000000040016a &lt;+30&gt;:    cmp    $0x0,%rax
     0x000000000040016e &lt;+34&gt;:    jl     0x4001b1 &lt;err_read_failed&gt;
     0x0000000000400170 &lt;+36&gt;:    mov    %rax,%r10
  End of assembler dump.
  (gdb)
  </code></pre>