2011-07-28 120 views
0

我在我的程序中使用libcurl,并运行到段错误。在我提交curl项目的bug之前,我想我会做一些调试。我发现的东西对我来说似乎很奇怪,我还没有弄清楚它的意义。用有效指针Memcpy segfaulting

首先,段错误回溯:

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) 
... 

到memcpy的调用是这样的:

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 

如果我去了一个框架,我看到了BUF传入的指针是从哪里来的在调用函数中定义的局部变量:

char buf[256]; 

这里是它开始变得奇怪的地方。我可以手动检查buf和biomem-> data中的所有256个字节,而无需gdb抱怨内存不可访问。我也可以使用gdb set命令手动编写所有256字节的buf,而不会出现任何错误。因此,如果涉及的所有内存都是可读写的,为什么memcpy会失败?

另一个有趣的是,我可以使用gdb手动调用memcpy与指针有关。只要我通过一个大小< = 160,它运行没有问题。只要我通过161或更高,gdb得到一个sigsegv。我知道buf大于160,因为它是以256的数组形式在堆栈上创建的。biomem-> data有点难以确定,但我可以用gdb在字节160之后很好地读取。

我还应该提到,这个函数(或更确切地说,我称之为curl的方法导致了这种情况)在崩溃前多次成功完成。我的程序使用curl在运行时反复调用Web服务API。它每五秒左右调用一次API,并在崩溃之前运行约14个小时。我的应用程序中的其他内容可能会写出越界并跺脚,导致产生错误状况。但似乎每次都在同一时刻崩溃,尽管时间不同。所有的指针在gdb中看起来都不错,但memcpy仍然失败。 Valgrind没有发现任何界限错误,但我没有让我的程序在valgrind下运行14个小时。

内的memcpy本身,拆装看起来是这样的:

(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 

我使用的libcurl版本7.21.6,C-战神版1.7.4,和OpenSSL版本1.0.0d。我的程序是多线程的,但我使用openssl注册了互斥量回调函数。该程序在Ubuntu 11.04桌面上运行,64位。 libc是2.13。

+0

不要相信gdb,仔细检查一切。如果gdb可以读取或写入一个内存区域,这并不意味着该区域是有效的。如果不能,该地区不一定是无效的。它显示的线程并不总是停止/崩溃的线程。我被这几次咬了,尤其是6.x,还有7.x –

+0

只是为了确保 - 你测试了161十进制数,而不是十六进制数? –

+0

你确定这个尺寸<= 256? –

回答

1

你有没有可能创建一个“皱折区”?

也就是说,故意增加两个缓冲区的大小,或者在结构中在目标之后放置一个额外的未使用元素?

然后,您就会用诸如“0xDEADBEEF”之类的东西将源代码弄碎,并且将目标与som配合使用。如果目的地每改变一次,你都可以使用。

256有点暗示,它可能以某种方式被视为有符号数量,成为-1,因此非常大?看不到gdb不会显示它,但是...

+0

我会尝试添加一些调试代码到libcurl做一个皱巴巴的区域 – ryan

3

显然libcurl是在读源缓冲区,并步入读取内存(页面在0x7fffe3e1f000 - 您可以确认内存不可读通过查看/proc/<pid>/maps对于正在调试的程序)。

这里是它开始变得奇怪的地方。我可以手动检查
buf和biomem->data的所有256个字节,没有gdb抱怨内存不可访问。

有一个著名的Linux内核的缺陷:即使对于具有PROT_NONE内存(并导致在尝试SIGSEGV从过程本身阅读)由GDB,企图ptrace(PEEK_DATA,...)成功。这就解释了为什么您可以在GDB中检查256个字节的源缓冲区,即使其中只有96个实际可访问。

尝试在Valgrind下运行你的程序,它很可能会告诉你,你是memcpy ing分配到缓冲区太小。

+0

啊,很高兴知道我不能依靠gdb来验证指针。我不知道我以前是如何错过它的,但事实证明源缓冲区(biomem-> data)的长度非常大(生物组长 - >长度)为0x7fff000000a5。填充biomem结构体的OpenSSL方法也恰好返回写入的字节数。当我修改libcurl使用biomem-> length和返回值中的较小值时,问题就消失了。但我不认为这是一个有效的解决方案,因为libcurl应该能够依赖生物组织 - >长度。仍然在挖掘生物组织 - >长度变得腐败的地方。 – ryan