내 프로그램에서 libcurl을 사용 중이며 segfault로 실행 중입니다. 컬 프로젝트에 버그를 제기하기 전에 약간의 디버깅을 할 것이라고 생각했습니다. 내가 찾은 것은 나에게 매우 이상하게 보였고 나는 아직 그것을 이해할 수 없었다.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(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
호출 함수에 정의 된 지역 변수 :
char buf[256];
여기가 이상하게 들릴 곳이 있습니다. 나는 메모리가 접근 가능하지 않다는 불평을하지 않고 buf와 biomem-> data의 256 바이트를 수동으로 검사 할 수있다. 또한 오류없이 gdb set 명령을 사용하여 256 바이트의 buf를 수동으로 작성할 수 있습니다. 따라서 관련된 모든 메모리가 읽기 쉽고 쓰기 가능한 경우 memcpy가 실패하는 이유는 무엇입니까?
흥미로운 점은 gdb를 사용하여 포인터가 포함 된 memcpy를 수동으로 호출 할 수 있다는 것입니다. 크기가 < = 160이면 문제없이 실행됩니다. 161 이상을 통과하자마자 gdb는 sigsegv를 얻습니다. buf가 256보다 큰 스택을 생성했기 때문에 buf가 160보다 큰 것을 알고 있습니다. biomem-> data는 그림을 그리는 것이 조금 어렵지만, gdb를 사용하여 바이트 160을 훨씬 앞선 것으로 읽을 수 있습니다.
또한이 함수 (또는이 호출의 컬 메소드)가 크래시 전에 여러 번 성공적으로 완료되었음을 언급해야합니다. 내 프로그램은 curl을 사용하여 웹 서비스 API가 실행되는 동안 반복적으로 호출합니다. API가 5 초마다 호출되며 충돌하기 전에 약 14 시간 동안 실행됩니다. 내 응용 프로그램의 다른 무언가가 범위를 벗어나서 오류 조건을 만드는 무언가를 쾅쾅 치고있을 가능성이 있습니다. 그러나 타이밍이 다를지라도 매번 정확히 같은 시점에 충돌하는 것은 의심스러운 것 같습니다. 그리고 모든 포인터는 gdb에서 괜찮아 보이지만 memcpy는 여전히 실패합니다. Valgrind는 경계 오류를 찾지 못하지만 14 시간 동안 valgrind로 프로그램을 실행시키지 않았습니다. 방어 적이기 자체 내에서
은 분해는 다음과 같습니다
(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입니다.
gdb를 신뢰하지 마십시오. 모든 것을 다시 확인하십시오. gdb가 메모리 영역을 읽거나 쓸 수있는 영역이라면 해당 영역이 유효하다는 의미는 아닙니다. 불가능할 경우 지역이 반드시 유효하지는 않습니다. 스레드가 보여주는 스레드가 항상 중지/크래시 된 스레드는 아닙니다. 나는 이것을 몇 번이나, 특히 6.x뿐만 아니라 7.x에도 물 렸습니다. –
16 진수가 아닌 10 진수를 테스트 했습니까? –
크기가 256보다 작습니까? –