2011-07-28 8 views
0

私は自分のプログラムで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です。

+0

gdbを信頼しないでください。すべてをダブルチェックしてください。 gdbがメモリ領域を読み書きできる場合、その領域が有効であるとは限りません。できない場合、その地域は必ずしも無効であるとは限りません。それが示すスレッドは、常に停止/クラッシュしたスレッドではありません。私はこれに数回、特に6.xでも7.xでも噛まれました。 –

+0

確かに、16進数ではなく10進数でテストしましたか? –

+0

サイズが256より小さいと確信していますか? –

答えて

1

"クラムゾーン"を作成する可能性はありますか?

つまり、意図的に2つのバッファのサイズを増やすか、目的地の後に余分な未使用要素を置く構造の場合は?

次に、 "0xDEADBEEF"のようなものでソースcrumpleをシードし、何かniceのあるsomで宛先をシードします。目的地が変わるたびに、あなたは何かに取り組んでいます。

256は少し示唆していますが、何らかの理由で符号付き量として扱われて-1になる可能性があります。 gdbがどのように表示されないのか分かりませんが...明らかに

+0

libcurlにいくつかのデバッグコードを追加してクラムゾーンを作成してみましょう – ryan

3

libcurlは、ソースバッファをオーバー読み取り、読み取り不可能なメモリに( - あなたはメモリがデバッグ中のプログラムのために/proc/<pid>/mapsを見て読めないことを確認することができます0x7fffe3e1f000でページ)をステッピングあります。

ここで奇妙なことが起こります。私は手動で、メモリがアクセスできないと不平を言うgdbなしで
bufとbiomem->dataのすべての256バイトを検査することができます。

よく知られているLinuxカーネルの脆弱性があります。でもPROT_NONEを持っているメモリのためには、(およびプロセス自体からそれを読み取ろうにSIGSEGVの原因となる)ptrace(PEEK_DATA,...)からGDBの試みが成功しました、。つまり、実際にアクセス可能なのは96個だけですが、GDB内の256バイトのソースバッファを調べることができます。

Valgrindの下でプログラムを実行してみてください。memcpyが小さすぎるというヒープ割り当てバッファに入っている可能性があります。

+0

ああ、ポインタを検証するためにgdbを使うことはできません。どのように前にそれを見逃したのか分かりませんが、ソースバッファ(biomem-> data)の長さは(biomem-> length)0x7fff000000a5であることが判明しました。 biomem構造体に値を設定するOpenSSLメソッドも、書き込まれたバイト数を返します。 biomem-> lengthと戻り値の小さい方を使うようにlibcurlを修正したところ、問題は解決しました。しかし、私はこれが有効な修正ではないと考えています。なぜなら、libcurlはbiomem-> lengthに頼るべきだからです。まだbiomem-> lengthがどこに壊れているかを見つけるために掘り起こす。 – ryan

関連する問題