2

Androidでネイティブクラッシュレポートを機能させようとしています。breakpad を使用してダンプ ファイルを作成し、curl を使用してアップロードします (breakpad に付属の minidump_upload ツールと非常によく似た方法で)。

テストのために、逆参照された NULL ポインターに書き込もうとして、segfault を引き起こします。

ミニダンプが正常に作成された後にブレークパッドのシグナル ハンドラーから呼び出されるダンプ コールバック関数で、fork() を実行し、アップロード コードを実行します。

libCurl のセットアップは正常に機能しますが、curl_easy_perform 内でクラッシュし、次のバックトレースが発生します (ファイル名は読みやすくするために切り捨てられ、10 を超えるフレームは削除されます)。

Program received signal SIGSEGV, Segmentation fault.
0x7356d700 in dprintf_formatf (data=0x7393ba34, stream=0x65, format=0x739458a4 "l>\016", ap_save=...) at .../lib/mprintf.c:518
518 {
(gdb) bt
#0  0x7356d700 in dprintf_formatf (data=0x7393ba34, stream=0x65, format=0x739458a4 "l>\016", ap_save=...) at .../lib/mprintf.c:518
#1  0x7356e5fe in curl_mvsnprintf (buffer=0x73860540 "\b\025\206sa", maxlength=4096, format=0x737a1528 "%s; boundary=%s\r\n", ap_save=...) at .../lib/mprintf.c:932
#2  0x7355d092 in AddFormDataf (formp=0x73861780, size=0x0, fmt=0x737a1528 "%s; boundary=%s\r\n") at .../lib/formdata.c:918
#3  0x7355d606 in Curl_getformdata (data=0x75527970, finalform=0x75511ec0, post=0x75482af0, custom_content_type=0x0, sizep=0x75511ec8) at .../lib/formdata.c:1172
#4  0x73561dfc in Curl_http (conn=0x755300a0, done=0x73861928) at .../lib/http.c:1969
#5  0x7356badc in Curl_do (connp=0x75527978, done=0x73861928) at .../lib/url.c:5897
#6  0x735771f0 in multi_runsingle (multi=0x7551dfa0, now=..., data=0x75527970) at .../lib/multi.c:1218
#7  0x73577aac in curl_multi_perform (multi_handle=0x7551dfa0, running_handles=0x738619cc) at .../lib/multi.c:1714
#8  0x73571fa4 in easy_transfer (multi=0x7551dfa0) at .../lib/easy.c:759
#9  0x7357209a in easy_perform (data=0x75527970, events=false) at .../lib/easy.c:838
#10 0x735720c4 in curl_easy_perform (easy=0x75527970) at .../lib/easy.c:857

ここで得たものを分析してみましょう。フレーム 3 で面白くなり始めます。

  • フォームの NULL ポインターへのポインター (予想どおり、フォームはまだ空です)
  • サイズ ポインタの場合は NULL
  • 有効なフォーマット文字列 "%s; 境界=%s\r\n"
  • および可変部分への 2 つの文字列引数: "Content-Type: multipart/form-data" および "------------------------a7584d0e40b10c4a"

フレーム 2 では、まだすべて問題なく、va_list が構築され (2 つの文字列の最初の文字列を指している必要がありますが、gdb でそれをデバッグする方法がわかりません)、制御が vsnprintf に渡され、curl が内部的に定義します。 curl_mvsnprintf として。

フレーム 1 では、すべてのパラメータはまだ良好に見えます。4k バッファにはランダム データが含まれていますが、それは問題ないはずです。上書きされます。4096 の最大長は正しく、フォーマット文字列はまだフォーマットのままです。nsprintf 構造体が作成され、制御が dprintf_formatf に渡されます。dprintf_formatf に渡されるすべてのパラメーターを出力しましょう。

(gdb) print info
$6 = {buffer = 0x73860540 "\b\025\206sa", length = 0, max = 4096}
(gdb) print &info
$7 = (struct nsprintf *) 0x73860510
(gdb) print addbyter
$8 = {int (int, FILE *)} 0x7356e580 <addbyter>
(gdb) print format
$9 = 0x737a1528 "%s; boundary=%s\r\n"
(gdb) print ap_save
$10 = {__ap = 0x73861554}

ご覧のとおり、info 構造体は正しく設定されており、addbyter 関数への関数ポインターと va_args ap_save については何も言う必要はありませんが、少なくとも書式文字列は問題ありません。

ここで、フレーム 0 を見ると、すべてが破損しています。

#0  0x7356d700 in dprintf_formatf (data=0x7393ba34, stream=0x65, format=0x739458a4 "l>\016", ap_save=...) at .../curl/./lib/mprintf.c:518
(gdb) print ap_save
$11 = {__ap = 0x739fae60}

情報ポインターは 0x73860510 から 0x7393ba34 に、関数ポインターは 0x7356e580 から 0x65 に、フォーマット文字列は 0x737a1528 から 0x739458a4 に、va_list 内の __ap は 0x73861554 から 0x739fae60 に変更されました。

これらの (無効な) ポインターは、プログラムを実行するたびに一定に保たれているようです。プログラムを実行するたびに、データ、ストリーム、および形式のアドレスがまったく同じになり、形式 ("l>\016") の内容も同じになります。

いくつかの質問を聞きたいんです:

  • fork()すると、親のメモリは新しい子プロセスにコピーされますが、forkの後、親はクライアントのメモリを変更できませんよね? 私の親プロセスがマルチスレッドであるため、私は尋ねています(ええ、私は知っています、マルチスレッドとフォーク、良い考えではありませんが、とにかくすでにクラッシュしているときにのみそうしています...)
  • 関数へのすべての引数が失われ、無効なものに置き換えられるのはどうしてですか? ここでの唯一の答えは、何かがスタックを破損したということだと思いますよね?
  • 子プロセスのスタックが破損する可能性があるのは何ですか (これは、親から破損した可能性のあるメモリを大量に所有する完全に健全なプロセスである必要があります)。
  • libCurl のコンパイルで何か間違ったことをしたのでしょうか? (子プロセスがシグナル ハンドラで作成されるこの 1 つのケースを除いて、どこからでも (フォークされた子プロセスを含む) 呼び出された場合、アップロード全体が正常に動作します)
  • これらの無効なポインターはどこを指しているのですか?なぜいつも同じ文字列がフォーマットで表示されるのですか? ここにプログラムコードを見ていると思いますか?

あなたの助けを前もってありがとう、私はこれを修正する方法が本当にありません:)

4

0 に答える 0