2

我正在尝试让 android 上的本机崩溃报告正常工作。我使用 breakpad 创建一个转储文件,然后使用 curl 上传它(与 breakpad 附带的 minidump_upload 工具非常相似)。

为了测试,我通过尝试写入取消引用的 NULL 指针来引发段错误。

在我的转储回调函数中,在 minidump 成功创建后从 breakpad 的信号处理程序中调用,我 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 帧开始变得有趣。这里调用 AddFormDataf

  • 指向表单的 NULL 指针的指针(预期,表单仍为空)
  • 大小指针为 NULL
  • 一个有效的格式字符串“%s;边界=%s\r\n”
  • 并将两个字符串参数放入可变参数部分:“Content-Type: multipart/form-data”和“------------------------a7584d0e40b10c4a”

在第 2 帧中,一切都很好,构造了 va_list(它应该指向两个字符串中的第一个,但我不知道如何在 gdb 中调试它),并将控制权交给 curl 内部定义的 vsnprintf作为 curl_mvsnprintf。

在第一帧,所有参数看起来都还不错,4k Buffer里面有随机数据,不过应该没问题,会被覆盖,maxlength 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 struct 已正确填充,关于指向 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}

info 指针从 0x73860510 更改为 0x7393ba34,函数指针从 0x7356e580 更改为 0x65,格式字符串从 0x737a1528 更改为 0x739458a4,va_list 中的 __ap 从 0x73861554 更改为 0x739fae60。

这些(无效的)指针似乎在程序的每次运行中都保持不变——每当我运行程序时,我得到的数据、流和格式的地址完全相同,甚至格式的内容相同(“l>\016”)。

我有几个问题:

  • 当我 fork() 时,父进程的内存被复制到新的子进程中,但是在 fork 之后父进程不能修改客户端的内存,对吗?我问是因为我的父进程是多线程的(是的,我知道,多线程和分叉,不是一个好主意,但我只是在我已经崩溃时才这样做......)
  • 一个函数的所有参数都丢失并被无效的东西取代怎么会发生?我认为这里唯一的答案是某些东西破坏了堆栈,对吗?
  • 什么可能会破坏我在子进程中的堆栈(这应该是一个完全健康的进程,它只拥有来自父进程的大量潜在损坏的内存)?
  • 我在编译 libCurl 时可能做错了什么吗?(当从任何地方(包括分叉的子进程)调用时,整个上传工作正常,除非在信号处理程序中创建子进程的这种情况下)
  • 那些无效的指针指向哪里,为什么我总是看到格式相同的字符串?我假设我在这里看到程序代码?

提前感谢您的帮助,我真的不知道如何解决这个问题:)

4

0 回答 0