2011-07-28 2 views
0

Я использую libcurl в своей программе и нахожусь в segfault. Прежде чем я подал ошибку с проектом curl, я подумал, что сделаю небольшую отладку. То, что я нашел, показалось мне очень странным, и я еще не смог понять это.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 

Если я подхожу кадр, я вижу, что указатель, переданный в течение BUF пришел из локальная переменная, определенная в вызывающей функции:

char buf[256]; 

Вот где это начинает странно. Я могу вручную проверить все 256 байтов данных buf и biomem-> без gdb, жалуясь на то, что память не доступна. Я также могу вручную написать все 256 байтов buf с помощью команды set gdb без каких-либо ошибок. Так что, если вся используемая память читается и записывается, почему memcpy терпит неудачу?

Также интересно, что я могу использовать gdb для ручного вызова memcpy с помощью указателей. Пока я пропускаю размер < = 160, он работает без проблем. Как только я прохожу 161 или выше, gdb получает sigsegv. Я знаю, что buf больше 160, потому что он был создан в стеке, поскольку массив из 256. biomem-> data немного сложнее, но я хорошо читаю прошлый байт 160 с gdb.

Следует также упомянуть, что эта функция (или, скорее, метод завитки, который я вызываю, что приводит к этому) успешно завершается много раз перед сбоем. Моя программа использует curl для многократного вызова API веб-сервиса во время его запуска. Он вызывает API каждые пять секунд или около того, и работает около 14 часов, прежде чем он сработает. Возможно, что что-то еще в моем приложении выписывает за пределы и топает то, что создает условие ошибки. Но кажется подозрительным, что каждый раз он падает в точно такой же точке, хотя время меняется. И все указатели выглядят нормально в gdb, но memcpy все еще не работает. Valgrind не обнаруживает ошибок в границах, но я не позволяю своей программе работать с valgrind в течение 14 часов.

В тетсру сам, разборка выглядит следующим образом:

(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, с-ары версию 1.7.4 и OpenSSL версию 1.0.0d. Моя программа многопоточная, но я зарегистрировал обратные вызовы mutex с помощью 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 для отлаживаемой программы).

Вот где это начинает странно. Я могу вручную проверить все 256 байтов как
buf, так и biomem->data без gdb, жалующихся на то, что память не доступна.

Существует хорошо известное ядро ​​Linux недостаток: даже для памяти, которая имеет PROT_NONE (и вызывает SIGSEGV на попытку прочитать его от самого процесса), попытка GDB в ptrace(PEEK_DATA,...) успешно. Это объясняет, почему вы можете проверить 256 байт исходного буфера в GDB, хотя только 96 из них действительно доступны.

Попробуйте запустить программу под Valgrind, скорее всего, вы скажете, что вы находитесь в буфере, выделенном кучкой, слишком малом.

+0

А, хорошо знать, что я не могу полагаться на gdb для проверки указателей. Я не знаю, как я пропустил это раньше, но оказывается, что исходный буфер (biomem-> data) имеет абсурдно большую длину (biomem-> length) 0x7fff000000a5. Метод OpenSSL, который заполняет структуру biomem, также возвращает возвращаемое количество байтов. Когда я модифицировал libcurl для использования меньшей длины biomem-> и возвращаемого значения, проблема исчезла. Но я не думаю, что это действительное исправление, поскольку libcurl должен иметь возможность полагаться на длину biomem->. Еще рыть, чтобы найти, где biomem-> длина становится коррумпированной. – ryan

 Смежные вопросы

  • Нет связанных вопросов^_^