私は自分のプログラムでlibcurlを使っていて、segfaultを実行しています。私がcurlプロジェクトのバグを提出する前に、私は少しデバッグすると思っていました。私が見つけたものは私にとっては非常に奇妙に思えましたが、私はまだそれを理解できませんでした。Memcpyが有効なポインタでセグメンテーションする
まず、セグメンテーション違反のトレースバック:
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のために渡されたポインタがから来ていることがわかり
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はメモリがアクセスできないと不平を言っています。また、gdb setコマンドを使って256バイトのbufを手動で書き込むこともできます。したがって、関係するすべてのメモリが読み書き可能であれば、memcpyはなぜ失敗しますか?
また興味深いのは、gdbを使用して手動でmemcpyを呼び出すことができる点です。私がサイズ< = 160を渡す限り、問題なく実行されます。 161以上を渡すと、gdbはsigsegvを取得します。私はbufが160よりも大きいことを知っています。なぜなら、それは256の配列としてスタック上に作られているからです。biomem-> dataは少し難しいですが、gdbで160バイトを超えて読むことができます。
また、この関数(または私が呼び出すcurlメソッド)がクラッシュする前に何度も正常に完了することにも言及する必要があります。私のプログラムはcurlを使って、実行中にWebサービスAPIを繰り返し呼び出す。 APIは5秒ごとに呼び出され、クラッシュする前に約14時間実行されます。私のアプリの他の何かが限界を書き込んで、エラー状態を作り出す何かを踏んでいる可能性があります。しかし、タイミングはさまざまですが、毎回まったく同じ時点でクラッシュすることは疑わしいと思われます。そして、すべてのポインタはgdbでOKだと思われますが、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を使用しています。私のプログラムはマルチスレッドですが、私はmutexコールバックをopensslに登録しています。このプログラムは、Ubuntu 11.04デスクトップ(64ビット)で動作しています。 libcは2.13です。
gdbを信頼しないでください。すべてをダブルチェックしてください。 gdbがメモリ領域を読み書きできる場合、その領域が有効であるとは限りません。できない場合、その地域は必ずしも無効であるとは限りません。それが示すスレッドは、常に停止/クラッシュしたスレッドではありません。私はこれに数回、特に6.xでも7.xでも噛まれました。 –
確かに、16進数ではなく10進数でテストしましたか? –
サイズが256より小さいと確信していますか? –