Crash问题(Java与Native)
1.问题介绍
Crash问题,无论是java侧还是native侧,在日常测试中经常会遇到。
Crash问题一般我们可以分为两类,一类是java侧的crash问题也就是抛出exception问题,这一类相对简单。
另一类是native侧的问题,由于是c++或者是c,所以相对较为复杂。
另外,kernel部分的crash暂且不提。
2.分析过程
分析过程主要分两部分,按照不同的问题来做分析。
(1).java侧问题
java侧出现exception抛出但是并没有栈上任何一层都没有一个地方catch住的话,最终将会使程序崩溃。
这种exception一般最终将会在slog中体现,一般如下所示
12-28 23:32:27.143: D/AndroidRuntime(337): Shutting down VM
12-28 23:32:27.143: W/dalvikvm(337): threadid=1: thread exiting with uncaught exception (group=0x40015560)
12-28 23:32:27.173: E/AndroidRuntime(337): FATAL EXCEPTION: main
12-28 23:32:27.173: E/AndroidRuntime(337): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.wtaylorjr2001.practice/com.wtaylorjr2001.practice.PracticeActivity}: java.lang.NullPointerException
12-28 23:32:27.173: E/AndroidRuntime(337): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647)
12-28 23:32:27.173: E/AndroidRuntime(337): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
12-28 23:32:27.173: E/AndroidRuntime(337): at android.app.ActivityThread.access$1500(ActivityThread.java:117)
12-28 23:32:27.173: E/AndroidRuntime(337): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
12-28 23:32:27.173: E/AndroidRuntime(337): at android.os.Handler.dispatchMessage(Handler.java:99)
12-28 23:32:27.173: E/AndroidRuntime(337): at android.os.Looper.loop(Looper.java:123)
12-28 23:32:27.173: E/AndroidRuntime(337): at android.app.ActivityThread.main(ActivityThread.java:3683)
12-28 23:32:27.173: E/AndroidRuntime(337): at java.lang.reflect.Method.invokeNative(Native Method)
12-28 23:32:27.173: E/AndroidRuntime(337): at java.lang.reflect.Method.invoke(Method.java:507)
12-28 23:32:27.173: E/AndroidRuntime(337): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
12-28 23:32:27.173: E/AndroidRuntime(337): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
12-28 23:32:27.173: E/AndroidRuntime(337): at dalvik.system.NativeStart.main(Native Method)
12-28 23:32:27.173: E/AndroidRuntime(337): Caused by: java.lang.NullPointerException
12-28 23:32:27.173: E/AndroidRuntime(337): at com.wtaylorjr2001.practice.PracticeActivity.onCreate(PracticeActivity.java:24)
12-28 23:32:27.173: E/AndroidRuntime(337): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
12-28 23:32:27.173: E/AndroidRuntime(337): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
12-28 23:32:27.173: E/AndroidRuntime(337): ... 11 more
log中会将该exception的调用栈打出,我们可以跟踪该调用栈进行一步步分析。
此外,由于在一般代码中通常会多次迭代打包exception,比如
1071 try {
1072 implementation = Class.forName(className, true, cl);
1073 lastClassName = className;
1074 } catch (Exception e) {
1075 throw new NoSuchAlgorithmException(type + " " + algorithm + " implementation not found: " + e);
1076 }
如果我们需要查看这个e的调用栈,我们可以在catch block中添加e.printStackTrace()
即可打印Exception e的调用栈。
(2).natvie侧crash问题
native侧crash问题较为复杂。我们需要用到以下工具
32bit:
arm-eabi-addr2line
arm-eabi-gdb
64bit:
aarch64-linux-android-addr2line
aarch64-linux-android-gdb
尽管32bit与64bit工具不同,实际上操作方法一致,我们以64bit为例。
我们举个实际例子来解释
Bug 542978 - [TJ][Telephony][SC9850S][full]点击信息中的网址超链接,偶见“浏览器停止运行”:
现在出现一native crash问题,log中有如下错误报告:
Build fingerprint: 'SPRD/sp9850s_2h10_native/sp9850s_2h10:6.0/MRA58K/W16.12.1N15:userdebug/test-keys'
Revision: '0'
ABI: 'arm64'
pid: 6695, tid: 6758, name: Chrome_InProcGp >>> com.android.browser <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), <font color="#FF0000">fault addr 0x170cbeee0</font>
x0 0000000170cbeec0 x1 0000007f7cebecec x2 0000007f7cebecec x3 0000007f8d601054
x4 0000000000000001 x5 0000000000000000 x6 0000014e0000058d x7 0000000200000000
x8 0000000000000000 x9 0000000000182012 x10 0000000000001eee x11 0000000039ca6b46
x12 0000000000000018 x13 ffffffffa91703af x14 0032c69c8c000000 x15 003b9aca00000000
x16 0000007f939018b0 x17 0000007fa5d27854 x18 0000000000000008 x19 0000000170cbeec0
x20 0000000170cbeec0 x21 0000007f7cebecec x22 0000000000000000 x23 0000007f7cebee90
x24 0000000000000001 x25 0000007f93940000 x26 0000007f92851328 x27 0000007f8ce03670
x28 0000000000001000 x29 0000007f7cebeca0 x30 0000007f90ae66f8
sp 0000007f7cebeca0 pc 0000007f90548cd8 pstate 0000000020000000
v0 00000000000000000000000000000000 v1 00000000000000000000000000000000
v2 00000000000000000000000000000000 v3 00000000000000000000000000000000
v4 00000000000031d100000000000021d1 v5 00000000000000040000000000000004
v6 00000000000000020000000000000002 v7 00000000000001d100000000000001d1
v8 00000000000000000000007f8ce03f90 v9 00000000000000000000007fcdb200c8
v10 00000000000000000000007f939681a8 v11 00000000000000000000007f9295f000
v12 00000000000000000000007fa61ec2b8 v13 00000000000000000000007fcdb21c28
v14 00000000000000000000000000000004 v15 00000000000000000000000000000000
v16 00000000000031d100000000000021d1 v17 00000000000000000000000000000000
v18 00000000000031d100000000000021d1 v19 00000000000000004024000000000000
v20 00000000000000004014000000000000 v21 00000000000000004014000000000000
v22 00000000000000000000000000000000 v23 00000000000000003d16800000000000
v24 00000000000000003fe0000000000000 v25 00000000000000003fe00000000000b4
v26 00000000000000003fdffffffffffe98 v27 0000000000000000401c000000000000
v28 00000000000000004022000000000000 v29 00000000000000003fc555555555553e
v30 00000000000000003fc553ad8b79a5cc v31 00000000000000003fc0e0b02a48f03b
fpsr 00000010 fpcr 00000000
backtrace:
#00 pc 0000000000708cd8 /system/app/webview/webview.apk (offset 0x7fd000)
#01 pc 0000000000ca66f4 /system/app/webview/webview.apk (offset 0x7fd000)
#02 pc 0000000000ca7240 /system/app/webview/webview.apk (offset 0x7fd000)
#03 pc 00000000010fc178 /system/app/webview/webview.apk (offset 0x7fd000)
#04 pc 00000000010ff33c /system/app/webview/webview.apk (offset 0x7fd000)
#05 pc 00000000011103fc /system/app/webview/webview.apk (offset 0x7fd000)
#06 pc 00000000010f934c /system/app/webview/webview.apk (offset 0x7fd000)
#07 pc 00000000010f879c /system/app/webview/webview.apk (offset 0x7fd000)
#08 pc 00000000006e21b0 /system/app/webview/webview.apk (offset 0x7fd000)
#09 pc 00000000006fd00c /system/app/webview/webview.apk (offset 0x7fd000)
#10 pc 00000000006fd91c /system/app/webview/webview.apk (offset 0x7fd000)
#11 pc 00000000006fdeb8 /system/app/webview/webview.apk (offset 0x7fd000)
#12 pc 00000000006fef1c /system/app/webview/webview.apk (offset 0x7fd000)
#13 pc 00000000006fd528 /system/app/webview/webview.apk (offset 0x7fd000)
#14 pc 000000000070de38 /system/app/webview/webview.apk (offset 0x7fd000)
#15 pc 00000000006fc380 /system/app/webview/webview.apk (offset 0x7fd000)
#16 pc 000000000072a0c0 /system/app/webview/webview.apk (offset 0x7fd000)
#17 pc 0000000000726230 /system/app/webview/webview.apk (offset 0x7fd000)
#18 pc 0000000000066ca4 /system/lib64/libc.so (__pthread_start(void*)+52)
#19 pc 000000000001eb84 /system/lib64/libc.so (__start_thread+16)
主要我们首先看该app是以什么abi运行的,这里显示是arm64,所以我们会使用64bit的一套工具。
然后我们看到错误的地址为0x170cbeee0。这里还不是空指针问题,所以我们还需要通过推调用栈分析具体错误位置。
我们找到这个版本对应的带符号表的libwebviewchromium.so(这里说明下,系统级apk如果其中含有so lib,是不会主动将其解压到某一个目录的,故在最终的调用栈中显示的还是webview.apk,但是实际指的即是webview.apk中的libwebviewchromium.so。)
然后执行如下名令:
aarch64-linux-android-addr2line -i -f -e libwebviewchromium.so 0x0000000000708cd8 0x0000000000ca66f4 0x0000000000ca7240 0x00000000010fc178 0x00000000010ff33c 0x00000000011103fc 0x00000000010f934c 0x00000000010f879c 0x00000000006e21b0 0x00000000006fd00c 0x00000000006fd91c 0x00000000006fdeb8
命令后面的各个地址即是各层调用栈的地址,其中以空格分割。
过了一会儿,命令行即会显示:
_ZN4base6Pickle16WriteBytesCommonEPKvm
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../base/pickle.cc:397
_ZN4base6Pickle16WriteBytesStaticILm4EEEvPKv
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../base/pickle.cc:380
ZN4base6Pickle8WritePODIiEEbRKT
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../base/pickle.h:306
_ZN4base6Pickle8WriteIntEi
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../base/pickle.h:172
_ZN3IPC11ParamTraitsIiE5WriteEPNS_7MessageERKi
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../ipc/ipc_message_utils.h:157
WriteParam<int>
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../ipc/ipc_message_utils.h:103
ZN3IPC11ParamTraitsIN3gpu13CommandBuffer5StateEE5WriteEPNS_7MessageERKS3
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../gpu/ipc/gpu_command_buffer_traits.cc:32
WriteParam<gpu::CommandBuffer::State>
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../ipc/ipc_message_utils.h:103
ZN3IPC11ParamTraitsIN4base5TupleIJRN3gpu13CommandBuffer5StateEEEEE5WriteEPNS_7MessageERKS7
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../ipc/ipc_message_utils.h:607
WriteParam<base::Tuple<gpu::CommandBuffer::State&> >
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../ipc/ipc_message_utils.h:103
ZN3IPC17SyncMessageSchemaIN4base5TupleIJiiEEENS2_IJRN3gpu13CommandBuffer5StateEEEEE16WriteReplyParamsIJS6_EEEvPNS_7MessageEDpT
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../ipc/ipc_message_utils.h:1061
ZN43GpuCommandBufferMsg_WaitForGetOffsetInRange16WriteReplyParamsIN3gpu13CommandBuffer5StateEEEvPN3IPC7MessageET
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../content/common/gpu/gpu_messages.h:531
_ZN7content20GpuCommandBufferStub18CheckCompleteWaitsEv
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../content/common/gpu/gpu_command_buffer_stub.cc:809
_ZN7content20GpuCommandBufferStub17OnMessageReceivedERKN3IPC7MessageE
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../content/common/gpu/gpu_command_buffer_stub.cc:324
_ZN7content13MessageRouter12RouteMessageERKN3IPC7MessageE
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../content/common/message_router.cc:54
_ZN7content10GpuChannel13HandleMessageEv
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../content/common/gpu/gpu_channel.cc:847
ZN4base8internal15RunnableAdapterIMN7content10GpuChannelEFvvEE3RunEPS3
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../base/bind_internal.h:176 (discriminator 4)
ZN4base8internal12InvokeHelperILb1EvNS0_15RunnableAdapterIMN7content10GpuChannelEFvvEEENS0_8TypeListIJRKNS_7WeakPtrIS4_EEEEEE8MakeItSoES7_SC
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../base/bind_internal.h:303 (discriminator 4)
_ZN4base8internal7InvokerINS_13IndexSequenceIJLm0EEEENS0_9BindStateINS0_15RunnableAdapterIMN7content10GpuChannelEFvvEEEFvPS7_ENS0_8TypeListIJNS_7WeakPtrIS7_EEEEEEENSD_IJNS0_12UnwrapTraitsISF_EEEEENS0_12InvokeHelperILb1EvSA_NSD_IJRKSF_EEEEEFvvEE3RunEPNS0_13BindStateBaseE
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../base/bind_internal.h:346 (discriminator 4)
_ZNK4base8CallbackIFvvEE3RunEv
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../base/callback.h:396 (discriminator 1)
_ZN4base5debug13TaskAnnotator7RunTaskEPKcRKNS_11PendingTaskE
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../base/debug/task_annotator.cc:51 (discriminator 1)
_ZN4base11MessageLoop7RunTaskERKNS_11PendingTaskE
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../base/message_loop/message_loop.cc:481
_ZN4base11MessageLoop21DeferOrRunPendingTaskERKNS_11PendingTaskE
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../base/message_loop/message_loop.cc:490
_ZN4base11MessageLoop6DoWorkEv
/home8/tianqichen/chromium_release_arm64/chromium/src/out/Release/../../base/message_loop/message_loop.cc:602
我们就可以知道大致错误的位置是哪里。
/base/pickle.cc:397
一般来说基本的native crash问题在使用了addr2line工具之后都能够大致分别错误几何。
然而实际上我们去查看该段代码
inline void Pickle::WriteBytesCommon(const void* data, size_t length) {
DCHECK_NE(kCapacityReadOnly, capacity_after_header_)
<< "oops: pickle is readonly";
MSAN_CHECK_MEM_IS_INITIALIZED(data, length);
size_t data_len = bits::Align(length, sizeof(uint32));
DCHECK_GE(data_len, length);
#ifdef ARCH_CPU_64_BITS
DCHECK_LE(data_len, kuint32max);
#endif
DCHECK_LE(write_offset_, kuint32max - data_len);
size_t new_size = write_offset_ + data_len; // <----------------------------------- This line.
if (new_size > capacity_after_header_) {
size_t new_capacity = capacity_after_header_ * 2;
const size_t kPickleHeapAlign = 4096;
if (new_capacity > kPickleHeapAlign)
new_capacity = bits::Align(new_capacity, kPickleHeapAlign) - kPayloadUnit;
Resize(std::max(new_capacity, new_size));
}
char* write = mutable_payload() + write_offset_;
memcpy(write, data, length);
memset(write + length, 0, data_len - length);
header_->payload_size = static_cast<uint32>(new_size);
write_offset_ = new_size;
}
size_t new_size = write_offset_ + data_len;
这一段代码看上去并不涉及指针操作,所以一般非代码逻辑问题。因此我们需要进一步通过corefile,查看错误当时具体汇编代码在干什么,当时保存的寄存器以及内存现场是怎么样。
gdb调试corefile
首先如果手机上出现native crash问题,一般都会在手机中生成corefile来dump当时的内存信息。一般会在/data/corefile目录下生成。
分析corefile我们需要用到GDB工具,由于刚才已经从log信息中看到该apk运行在64bit下,故我们将使用aarch64-linux-android-gdb进行分析。
在此之前我们需要准备以下内容。
- 相应版本的symbols压缩包,并且解压
- slog中找到corefile
在将symbols解压后(假设我们解压到了当前目录),使用aarch64-linux-android-gdb ./symbols/system/bin/app_process64 命令进入GDB。
进入之后我们可以先设置so的搜索路径,首先由于我们的webview.apk是已经打包好放入源码目录中参加编译的,故该symbols里的lib64目录下并不存在我们的libwebviewchromium.so。再者,由于GDB搜索加载的符号库时是通过名字严格判别,所以我们需要重命名我们的libwebviewchromium.so为webview.apk,然后放入./symbols/system/lib64/中。
之后我们在GDB命令行下执行
set solib-search-path ./symbols/system/lib64/
即指明了so lib的搜索路径。
之后我们可以加载corefile,执行
core-file ./core-file core-Chrome_InProcGp-6695
即可。执行之后需要等一段时间。
待执行完毕后我们即可分析当时crash的现场。
GDB的基本名令有
- bt 查看当前调用栈
- f 后续带数字,跳到指定栈以查看寄存器或者汇编代码
- disassemble 不带内存地址即反汇编当前栈代码,带内存地址即查看该内存地址所在代码段的汇编代码。
- info registers 查看当前栈的寄存器信息
该问题,我们需要先查看f 0上的汇编代码
(gdb) disassemble
Dump of assembler code for function base::Pickle::WriteBytesStatic<4ul>(void const*):
0x0000007f90548cc0 <+0>: stp x29, x30, [sp,#-48]!
0x0000007f90548cc4 <+4>: mov x29, sp
0x0000007f90548cc8 <+8>: stp x19, x20, [sp,#16]
0x0000007f90548ccc <+12>: str x21, [sp,#32]
0x0000007f90548cd0 <+16>: mov x19, x0
0x0000007f90548cd4 <+20>: mov x21, x1
=> 0x0000007f90548cd8 <+24>: ldr x3, [x0,#32]
0x0000007f90548cdc <+28>: ldr x2, [x0,#24]
0x0000007f90548ce0 <+32>: add x20, x3, #0x4
0x0000007f90548ce4 <+36>: cmp x20, x2
0x0000007f90548ce8 <+40>: b.ls 0x7f90548d0c <base::Pickle::WriteBytesStatic<4ul>(void const)+76>
0x0000007f90548cec <+44>: lsl x1, x2, #1
0x0000007f90548cf0 <+48>: cmp x1, #0x1, lsl #12
0x0000007f90548cf4 <+52>: b.hi 0x7f90548d3c <base::Pickle::WriteBytesStatic<4ul>(void const)+124>
0x0000007f90548cf8 <+56>: cmp x1, x20
0x0000007f90548cfc <+60>: mov x0, x19
所以错误的位置即为
=> 0x0000007f90548cd8 <+24>: ldr x3, [x0,#32]
即读取x0 + 32这个地址的内容然后放到x 3里去
这时候我们通过info registers 查看x0地址为
(gdb) info registers
x0 0x170cbeec0 6187380416
目测或者严格来说查看map表可以发现这个值作为内存地址来使用应该是不正确的,接下来就是倒推汇编来查看到底这个值是怎么来的。这部分这个问题没有直接证据证明
具体arm汇编指令可以点击这里查看
依然我们先从log中找到crash的调用栈
00 pc 007a52b2 /system/lib/libwebviewchromium.so
#01 pc 00ac631b /system/lib/libwebviewchromium.so
#02 pc 00949239 /system/lib/libwebviewchromium.so
#03 pc 007f2fdf /system/lib/libwebviewchromium.so
#04 pc 007f603d /system/lib/libwebviewchromium.so
#05 pc 00809999 /system/lib/libwebviewchromium.so
#06 pc 008099cd /system/lib/libwebviewchromium.so
#07 pc 007f8043 /system/lib/libwebviewchromium.so
#08 pc 007de197 /system/lib/libwebviewchromium.so
#09 pc 007f8091 /system/lib/libwebviewchromium.so
#10 pc 007de197 /system/lib/libwebviewchromium.so
#11 pc 007f8091 /system/lib/libwebviewchromium.so
#12 pc 007de197 /system/lib/libwebviewchromium.so
#13 pc 007f8091 /system/lib/libwebviewchromium.so
#14 pc 007de197 /system/lib/libwebviewchromium.so
#15 pc 007f8091 /system/lib/libwebviewchromium.so
#16 pc 007de197 /system/lib/libwebviewchromium.so
#17 pc 007f8091 /system/lib/libwebviewchromium.so
#18 pc 007de197 /system/lib/libwebviewchromium.so
#19 pc 007f8091 /system/lib/libwebviewchromium.so
#20 pc 007de197 /system/lib/libwebviewchromium.so
#21 pc 007f8091 /system/lib/libwebviewchromium.so
#22 pc 007fe33b /system/lib/libwebviewchromium.so
#23 pc 007f610b /system/lib/libwebviewchromium.so
#24 pc 007f81db /system/lib/libwebviewchromium.so
#25 pc 007df2e5 /system/lib/libwebviewchromium.so
#26 pc 007f824d /system/lib/libwebviewchromium.so
#27 pc 007df2e5 /system/lib/libwebviewchromium.so
#28 pc 007f824d /system/lib/libwebviewchromium.so
#29 pc 007df2e5 /system/lib/libwebviewchromium.so
#30 pc 007f824d /system/lib/libwebviewchromium.so
#31 pc 007eb4ed /system/lib/libwebviewchromium.so
#32 pc 007ee833 /system/lib/libwebviewchromium.so
#33 pc 007eea03 /system/lib/libwebviewchromium.so
#34 pc 007ef77f /system/lib/libwebviewchromium.so
#35 pc 00987ceb /system/lib/libwebviewchromium.so
#36 pc 00bc058d /system/lib/libwebviewchromium.so
#37 pc 01288e3b /system/lib/libwebviewchromium.so
#38 pc 010d9e51 /system/lib/libwebviewchromium.so
#39 pc 00001110 <unknown>
首先我们将其调用栈打印下
bswap32
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/wtf/ByteSwap.h:56
bswapuintptrt
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/wtf/ByteSwap.h:72
partitionFreelistMask
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/wtf/PartitionAlloc.h:329
partitionBucketAlloc
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/wtf/PartitionAlloc.h:449
partitionAllocGenericFlags
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/wtf/PartitionAlloc.h:546
_ZN3WTF21partitionAllocGenericEPNS_20PartitionRootGenericEj
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/wtf/PartitionAlloc.h:554
_ZN3WTF10fastMallocEj
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/wtf/FastMalloc.cpp:74
_ZN3WTF10RefCountedIN5blink11RenderStyleEEnwEj
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/wtf/RefCounted.h:166
_ZN5blink11RenderStyle6createEv
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/core/rendering/style/RenderStyle.cpp:78
_ZN5blink13StyleResolver15styleForElementEPNS_7ElementEPNS_11RenderStyleENS_20StyleSharingBehaviorENS_20RuleMatchingBehaviorE
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/core/css/resolver/StyleResolver.cpp:582
_ZN5blink7Element24originalStyleForRendererEv
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/core/dom/Element.cpp:1466
_ZN5blink7Element16styleForRendererEv
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/core/dom/Element.cpp:1445
_ZNK5blink17RenderTreeBuilder5styleEv
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/core/dom/RenderTreeBuilder.cpp:99
_ZN5blink17RenderTreeBuilder32createRendererForElementIfNeededEv
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/core/dom/RenderTreeBuilder.cpp:111
_ZN5blink7Element6attachERKNS_4Node13AttachContextE
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/core/dom/Element.cpp:1332
_ZN5blink13ContainerNode14attachChildrenERKNS_4Node13AttachContextE
/space/builder/repo/sprdroid5.1_trunk_volte/external/chromium_org/third_party/WebKit/Source/core/dom/ContainerNode.h:291
最后一栈来看
ALWAYS_INLINE uint32_t bswap32(uint32_t x) { return __builtin_bswap32(x); }
看不太出问题所在,总而言之和x有关
这时候需要查看下汇编代码
和之前同样方法进行corefile加载
局部汇编如下:
0xa85bf286 <+146>: movs r0, #1
0xa85bf288 <+148>: add r12, pc
0xa85bf28a <+150>: add.w r2, r12, #112 ; 0x70
0xa85bf28e <+154>: ldrex r1, [r2]
0xa85bf292 <+158>: strex r5, r0, [r2]
0xa85bf296 <+162>: cmp r5, #0
0xa85bf298 <+164>: bne.n 0xa85bf28e <WTF::fastMalloc(unsigned int)+154>
0xa85bf29a <+166>: dmb sy
0xa85bf29e <+170>: cbz r1, 0xa85bf2ac <WTF::fastMalloc(unsigned int)+184>
0xa85bf2a0 <+172>: ldr r1, [pc, #92] ; (0xa85bf300 <WTF::fastMalloc(unsigned int)+268>)
0xa85bf2a2 <+174>: add r1, pc
0xa85bf2a4 <+176>: ldr r0, [r1, #112] ; 0x70
0xa85bf2a6 <+178>: cmp r0, #0
0xa85bf2a8 <+180>: bne.n 0xa85bf2a0 <WTF::fastMalloc(unsigned int)+172>
0xa85bf2aa <+182>: b.n 0xa85bf282 <WTF::fastMalloc(unsigned int)+142>
0xa85bf2ac <+184>: ldr r2, [r3, #0]
0xa85bf2ae <+186>: ldr r1, [r2, #0]
0xa85bf2b0 <+188>: cbz r1, 0xa85bf2c4 <WTF::fastMalloc(unsigned int)+208>
=> 0xa85bf2b2 <+190>: ldr r4, [r1, #0]
此时寄存器信息:
(gdb) info registers
r0 0x1 1
r1 0x4a6f8ef8 1248825080
r2 0x4b6017a0 1264588704
r3 0xa98fbefc 2844770044
r4 0x38 56
r5 0x0 0
r6 0xa56c5da0 2775342496
r7 0x57209fe4 1461755876
r8 0xa56c6034 2775343156
r9 0xa98dd62c 2844644908
r10 0x4b7180b4 1265729716
r11 0x0 0
r12 0xa98fb7fc 2844768252
sp 0xa56c5ce8 0xa56c5ce8
lr 0xa88e031f -1467088097
pc 0xa85bf2b2 0xa85bf2b2 <WTF::fastMalloc(unsigned int)+190>
cpsr 0x680d0030 1745682480
所以r1 0x4a6f8ef8这个值没办法读取出来
从corefile对应的map来看,0x4a6f8ef8也确实不在可访问区域中
但是我们看看,其他的内存地址都是0x4b什么什么的,所以可以猜测是否出现读取内存或者写入内存时出现问题
我们假设实际上存着的应该是0x4b6f8ef8
此时我们读取该内存地址的内存来看
(gdb) x /1xw 0x4b6f8ef8
0x4b6f8ef8: 0xc08e6f4b
此时需要结合代码来看,这个x究竟是干嘛用的
结果我们可以发现这边是在做大小端转换工作__builtin_bswap32
也就是说我们内存中读到的值必然是一个经过反转过的内存地址
0xc08e6f4b经过反转为0x4b6f8ec0,按字节倒序,可以访问。
我们再看代码,看下这个传入的x代表什么,我们看到frame 2处
ALWAYS_INLINE PartitionFreelistEntry* partitionFreelistMask(PartitionFreelistEntry* ptr)
{
// We use bswap on little endian as a fast mask for two reasons:
// 1) If an object is freed and its vtable used where the attacker doesn't
// get the chance to run allocations between the free and use, the vtable
// dereference is likely to fault.
// 2) If the attacker has a linear buffer overflow and elects to try and
// corrupt a freelist pointer, partial pointer overwrite attacks are
// thwarted.
// For big endian, similar guarantees are arrived at with a negation.
#if CPU(BIG_ENDIAN)
uintptr_t masked = ~reinterpret_cast<uintptr_t>(ptr);
#else
uintptr_t masked = bswapuintptrt(reinterpret_cast<uintptr_t>(ptr));
#endif
return reinterpret_cast<PartitionFreelistEntry*>(masked);
}
可以看到ptr实际是PartitionFreelistEntry*,也就是一个list的入口。
本身PartitionFreelistEntry结构为
struct PartitionFreelistEntry {
PartitionFreelistEntry* next;
};
那就太简单了,我们只需要看下0x4b6f8ef8是否能够按照next一直下去
由于我们知道是PartitionFreelistEntry*,所以可以使用p /x \*(PartitionFreelistEntry\*)[address]
指令来强转查看
于是
(gdb) p /x (PartitionFreelistEntry)0x4b6f8ef8
$1 = {next = 0xc08e6f4b}
(gdb) p /x (PartitionFreelistEntry)0x4b6f8ec0
$2 = {next = 0x636f4b}
(gdb) p /x (PartitionFreelistEntry)0x4b6f6300
$3 = {next = 0x58626f4b}
(gdb) p /x (PartitionFreelistEntry)0x4b6f6258
$4 = {next = 0xa8636f4b}
(gdb) p /x (PartitionFreelistEntry)0x4b6f63a8
$5 = {next = 0x70636f4b}
(gdb) p /x (PartitionFreelistEntry)0x4b6f6370
$6 = {next = 0x38636f4b}
(gdb) p /x (PartitionFreelistEntry)0x4b6f6338
$7 = {next = 0xc8626f4b}
(gdb) p /x (PartitionFreelistEntry)0x4b6f62c8
$8 = {next = 0xa08f6f4b}
(gdb) p /x (PartitionFreelistEntry)0x4b6f8fa0
$9 = {next = 0xd88f6f4b}
...
证明了9个node应该足够证明推论是正确的
3.调试技巧
相对来说调试技巧几乎没有,一般都是循序渐进循规蹈矩地按照步骤来查找问题。
Java侧:一般多使用printStackTrace来查看具体exception的throw位置。
C++侧:多使用GDB来熟练技巧。
4.解决思路及解决方案
Java侧:
Java侧一般就两种解决办法:
- 把这个throw的exception catch住。
- 查看为何抛出这个exception然后解决。
C++侧:
- 查看是否存在代码逻辑漏洞导致访问空指针或类似错误,然后修改。
- 如果并不存在代码逻辑漏洞,可能存在内存读写问题,需要找到具体位置然后转交系统稳定性同事进行分析。