Search code examples
assemblygdbsegmentation-faultmmapmemcpy

memcpy() segmentation fault debugging exercise on PROT_READ in mmap()


I have generated SIGSEGV in my code intentionally to learn to debug it under various utilities like catchsegv, gdb, valgrind & pmap. I intentionally used memcpy() on mmap'ed file which is having flag of PROT_READ set by mprotect(). Now when I am debugging it under gdb I am not able to make any conclusion from assembly output. I am quite a newbie to assembly.

Details -

Memory Stuff :

    (gdb) x/35i 0x00002aaaaad55500
    0x2aaaaad55500 <memccpy+32>: jne    0x2aaaaad554e8 <memccpy+8>
    0x2aaaaad55502 <memccpy+34>: mov    %rdi,%rax
    0x2aaaaad55505 <memccpy+37>: retq
    0x2aaaaad55506 <memccpy+38>: nopw   %cs:0x0(%rax,%rax,1)
    0x2aaaaad55510 <memccpy+48>: xor    %eax,%eax
    0x2aaaaad55512 <memccpy+50>: retq
    0x2aaaaad55513:      nop
    0x2aaaaad55514:      nop
    0x2aaaaad55515:      nop
    0x2aaaaad55516:      nop
    0x2aaaaad55517:      nop
    0x2aaaaad55518:      nop
    0x2aaaaad55519:      nop
    0x2aaaaad5551a:      nop
    0x2aaaaad5551b:      nop
    0x2aaaaad5551c:      nop
    0x2aaaaad5551d:      nop
    0x2aaaaad5551e:      nop
    0x2aaaaad5551f:      nop
    0x2aaaaad55520 <__memcpy_chk>:       cmp    %rdx,%rcx
    0x2aaaaad55523 <__memcpy_chk+3>:     jb     0x2aaaaadcb590 <__chk_fail>
    0x2aaaaad55529:      nopl   0x0(%rax)
    0x2aaaaad55530 <memcpy>:     cmp    $0x20,%rdx
    0x2aaaaad55534 <memcpy+4>:   mov    %rdi,%rax
    0x2aaaaad55537 <memcpy+7>:   jae    0x2aaaaad555b0 <memcpy+128>
    0x2aaaaad55539 <memcpy+9>:   test   $0x1,%dl
    0x2aaaaad5553c <memcpy+12>:  je     0x2aaaaad55549 <memcpy+25>
    0x2aaaaad5553e <memcpy+14>:  movzbl (%rsi),%ecx
    => 0x2aaaaad55541 <memcpy+17>:  mov    %cl,(%rdi)
    0x2aaaaad55543 <memcpy+19>:  inc    %rsi
    0x2aaaaad55546 <memcpy+22>:  inc    %rdi
    0x2aaaaad55549 <memcpy+25>:  test   $0x2,%dl
    0x2aaaaad5554c <memcpy+28>:  je     0x2aaaaad55560 <memcpy+48>
    0x2aaaaad5554e <memcpy+30>:  movzwl (%rsi),%ecx
    0x2aaaaad55551 <memcpy+33>:  mov    %cx,(%rdi)

As you can see that SEGFAULT happened at instruction 0x2aaaaad55541, which is also indicated in 64bit Instruction Pointer register RIP.

Registers :

    (gdb) info registers
    rax            0x2aaaaaacf002   46912496267266
    rbx            0x40146a 4199530
    rcx            0x69     105
    rdx            0x1      1
    rsi            0x40146a 4199530
    rdi            0x2aaaaaacf002   46912496267266
    rbp            0x7      0x7
    rsp            0x7fffffffe468   0x7fffffffe468
    r8             0x40146a 4199530
    r9             0x53202c444145525f       5989836176067220063
    r10            0x7fffffffe1f0   140737488347632
    r11            0x2aaaaad55530   46912498914608
    r12            0x2aaaab05eac8   46912502098632
    r13            0x7fffffffe5a0   140737488348576
    r14            0x7fffffffe590   140737488348560
    r15            0x2aaaaaacf000   46912496267264
    rip            0x2aaaaad55541   0x2aaaaad55541 <memcpy+17>
    eflags         0x10202  [ IF RF ]
    cs             0x33     51
    ss             0x2b     43
    ds             0x0      0
    es             0x0      0
    fs             0x0      0
    gs             0x0      0

Process mappings :

Below is the output of "info proc mappings" command inside gdb.

    [excerpt]
    0x2aaaaaacf000     0x2aaaaaad0000     0x1000          0             /tmp/dst_file
    [/excerpt]

/tmp/dst_file is mapped into memory using mmap() and made READ ONLY using PROT_READ before memcpy() call & memcpy() tries to write content on it and thus SEGFAULT appears.

Memcpy() call used :

    memcpy (mmap_start + myfilestat1.st_size, str1, strlen(str1))

mmap_start is the starting address and myfilestat1.st_size is new truncated file size. str1 is the string to append to the file.

Here is a little flow of my source code -

    1. Fstat original size of file 
    2. Set Offset of file to grow depending upon strlen() of string to append 
    3. Ftruncate() to grow file size 
    4. Fstat new file size. 
    5. Mmap the newly truncated file 
    6. If segmentation fault need to be generated, call mprotect() to convert to PROT_READ. 
    7. Memcpy() with Orignal fstat size to append contents to text file. 

Question -

Now what I am not able to understand is

    0x2aaaaad55541 <memcpy+17>:  mov    %cl,(%rdi)

What does this instruction exactly do? RDI register contains end address of file mapped to start writing, why does the value at RDI address is being written at CX register?

Any help is appreciated. Let me know if more info is needed.


Solution

  • RDI is the argument passed to memcpy as destination. In your case that seems to be offset 2 in the mapped area. You didn't provide what memcpy call you used.

    CL holds the first byte of the source memory region, it has been loaded by the instruction directly preceding the fault: movzbl (%rsi),%ecx. I assume you know that CL is the low 8 bits of ECX (which in turn is the low 32 bits of RCX).