问题现象
- 复现步骤
- Android 7.0平台
- 安装手机百度apk(v8.6.5)
- 启动App后必现native crash
分析定位
初步分析
-
tombstone文件如下
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** Native Crash TIME: 959207 *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** Revision: '0' ABI: 'arm' pid: 3329, tid: 3719, name: bner >>> com.baidu.searchbox <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'stack corruption detected' r0 00000000 r1 00000e87 r2 00000006 r3 00000008 r4 84cff978 r5 00000006 r6 84cff920 r7 0000010c r8 84cfe708 r9 923fc200 sl 00000000 fp 84cfe694 ip 0000000d sp 84cfe530 lr ae08c597 pc ae08edf4 cpsr 000b0010 d0 0000000000000000 d1 0000000000000000 d2 0000000000000000 d3 0000000000000000 d4 0000000000000000 d5 0000000000000000 d6 0000000000000000 d7 0000000000000000 d8 0000000000000000 d9 0000000000000000 d10 0000000000000000 d11 0000000000000000 d12 0000000000000000 d13 0000000000000000 d14 0000000000000000 d15 0000000000000000 d16 005f006b006e0075 d17 003000310033006b d18 0000000000052da0 d19 000000000004a938 d20 000000002000166a d21 00013093a834e468 d22 0000000000000000 d23 0000000000000000 d24 0000004100000052 d25 ffffff79ffffffbb d26 0000000000000000 d27 0000000000000000 d28 0000000000000000 d29 0000000000000000 d30 0000000000000000 d31 00000000000e8800 scr 88000093 backtrace: #00 pc 00049df4 /system/lib/libc.so (tgkill+12) #01 pc 00047593 /system/lib/libc.so (pthread_kill+34) #02 pc 0001d855 /system/lib/libc.so (raise+10) #03 pc 000193a1 /system/lib/libc.so (__libc_android_abort+34) #04 pc 00017014 /system/lib/libc.so (abort+4) #05 pc 0001b84f /system/lib/libc.so (__libc_fatal+22) #06 pc 000482ab /system/lib/libc.so (__stack_chk_fail+6) #07 pc 00002c29 /data/app/com.baidu.searchbox-1/lib/arm/libbner.so (getSec+96) #08 pc 0000142f /data/app/com.baidu.searchbox-1/lib/arm/libbner.so (Java_com_bnerclient_InitSo_getSec+2) #09 pc 01623e41 /data/app/com.baidu.searchbox-1/oat/arm/base.odex (offset 0x153f000)
-
相关log
libc打印出"stack corruption detected", 即检测到栈内存被破坏。
--------- beginning of crash 06-16 10:02:27.009 3223 3565 F libc : stack corruption detected 06-16 10:02:28.126 3223 3565 E jnicrash: finish 06-16 10:02:28.128 3223 3565 E chromium: [ERROR:zw_microdump_crash_reporter.cc(296)] BREAKPAD ************************************************ 06-16 10:02:28.128 3223 3565 E chromium: [ERROR:zw_microdump_crash_reporter.cc(297)] BREAKPAD Build fingerprint 6, time 2017-06-16 02:02:28.127 UTC 06-16 10:02:29.862 3223 3565 W BREAKPAD: Output crash dump file: 06-16 10:02:29.862 3223 3565 W BREAKPAD: /data/user/0/com.baidu.searchbox/files/zeuslogs//1afdc4cc-7cc6-8598-45991107-0e5e400e.dmp 06-16 10:02:29.863 3223 3565 W BREAKPAD: /data/user/0/com.baidu.searchbox/files/zeuslogs/8.6.5-25580288-com.baidu.searchbox-8.4.2.345-SP9832A-neon-7.0-0-1497578548127.bdmp 06-16 10:02:33.370 3223 3565 W google-breakpad: ### ### ### ### ### ### ### ### ### ### ### ### ###
对比实验
- Nexus 5x/6p(Android 7.0)上无法复现
- 我司多个产品均能复现
- 有一台机器(烧录的本地pc编译版本)无法复现
侧面排查
另一同事, 通过对比Jenkins编译版本和我本地编译版本的多个文件,发现/system/build.prop中的ro.build.host属性值存在差异,修改该属性值后问题不再复现。
正面分析
-
原理
stack check failed的根本原因在于栈内存被覆盖,编译器会在函数进入和退出前后插入一个stack check的东西用于检测内存越界这种错误。
-
汇编分析
-
进入函数时ldr/str
00002bc8 <getSec>: 2bc8: b5f0 push {r4, r5, r6, r7, lr} 2bca: 4d19 ldr r5, [pc, #100] ; (2c30 <getSec+0x68>) 2bcc: b091 sub sp, #68 ; 0x44 2bce: 1c04 adds r4, r0, #0 2bd0: 447d add r5, pc 2bd2: 682d ldr r5, [r5, #0] 2bd4: ae02 add r6, sp, #8 2bd6: 9201 str r2, [sp, #4] 2bd8: 682b ldr r3, [r5, #0] 2bda: 2100 movs r1, #0 2bdc: 2232 movs r2, #50 ; 0x32 2bde: 1c30 adds r0, r6, #0 2be0: 930f str r3, [sp, #60] ; 0x3c ==> store到[sp + 60]
-
退出函数前检查栈内存是否相同
... ... 2c20: 9a0f ldr r2, [sp, #60] ; 0x3c ==>从[sp + 60] load 2c22: 682b ldr r3, [r5, #0] 2c24: 429a cmp r2, r3 2c26: d001 beq.n 2c2c <getSec+0x64> 相等就正常退出 2c28: f000 f94a bl 2ec0 <getDSec+0x114> 不相等就跳到__stack_chk_fail执行abort 2c2c: b011 add sp, #68 ; 0x44 2c2e: bdf0 pop {r4, r5, r6, r7, pc}
所以,问题出现后,我们可以查看[sp, #60]附近的内存数据, 就能知道大概什么类型的数据发生内存越界。
-
gdb堆栈和相关寄存器
(gdb) bt #0 tgkill () at bionic/libc/arch-arm/syscalls/tgkill.S:10 #1 0xae08c596 in pthread_kill (t=<optimized out>, sig=6) at bionic/libc/bionic/pthread_kill.cpp:45 #2 0xae062858 in raise (sig=3719) at bionic/libc/bionic/raise.cpp:34 #3 0xae05e3ce in __libc_android_abort () at bionic/libc/bionic/abort.cpp:57 #4 0xae05c018 in abort () at bionic/libc/arch-arm/bionic/abort_arm.S:43 #5 0xae060852 in __libc_fatal (format=0x0) at bionic/libc/bionic/libc_logging.cpp:678 #6 0xae08d2ae in __stack_chk_fail () at bionic/libc/bionic/__stack_chk_fail.cpp:35 #7 0x9249ec2c in ?? () Backtrace stopped: previous frame identical to this frame (corrupt stack?) (gdb) f 7 (gdb) i r r0 0x0 0 r1 0xe87 3719 r2 0x6 6 r3 0x8 8 r4 0x84cfe564 2228217188 r5 0x84cfe554 2228217172 r6 0x84cfe5a8 2228217256 r7 0x84839db0 2223218096 r8 0x84cfe708 2228217608 r9 0x923fc200 2453651968 r10 0x0 0 r11 0x84cfe694 2228217492 r12 0xd 13 sp 0x84cfe550 0x84cfe550 lr 0xae05e3cf -1375345713 pc 0xae05e3ce 0xae05e3ce <__libc_android_abort()+80> cpsr 0xb0030 720944
-
查看sp+60附近内存
(gdb) x /32xw 0x84cfe5a0+60 0x84cfe5dc: 0x3031336b 0x3230362f 0x8e3a002f 0x84cfe968 0x84cfe5ec: 0x84cfe708 0x84cfe6c8 0x9249d433 0x74129b00 0x84cfe5fc: 0x975abe43 0x8e3a7cdc 0x00000000 0x00000002 0x84cfe60c: 0x12f9d940 0x740fad60 0x00000000 0x923fc28c 0x84cfe61c: 0x72ce9c55 0x6ff3d9f8 0x00000000 0x00000000 0x84cfe62c: 0x00000000 0x00000000 0x00000000 0x00000000 0x84cfe63c: 0x00000000 0x00000000 0x00000000 0x00000000 0x84cfe64c: 0x00000000 0x00000000 0x00000000 0x00000000
这里很明显是ASCII字符
(gdb) p (const char *)0x84cfe5dc $1 = 0x84cfe5dc "k310/602/"
再往前查看整个字符串,就能看到这是我司特有的一个URL
(gdb) p (const char *)0x84cfe5a0 $2 = 0x84cfe5a0 "}\315\377\377\020\346τhttp://10.0.1.97:8080/jenkins/job/sprdroid7.0_trunk_k310/602/"
-
结合汇编code, 猜测这里应是获取某个属性。
-
现在通过加log, 已确认是App在访问ro.build.host属性导致的。
06-16 10:08:10.869 1956 2288 E libc : __system_property_get: property name = "ro.build.host" --------- beginning of crash 06-16 10:08:10.869 1956 2288 F libc : stack corruption detected
-
再查看下出问题手机上的ro.build.host的值, 正好对应上
$adb shell getprop ro.build.host http://10.0.1.97:8080/jenkins/job/sprdroid7.0_trunk_k310/602/
长度为62
-
Nexus 6p上该属性的值为
$ adb shell getprop ro.build.host wphr7.hot.corp.google.com
长度为26
-
本地编译的版本该属性的值为
$ adb shell getprop ro.build.host tj03456pcu1
长度为12
这基本就确定是获取属性值出现越界。
-
-
Root Cause
- Android定义的属性值最大长度为92。
#define PROP_VALUE_MAX 92
- 而手机百度App在栈上用于存储property的局部数组size太小,大概为50+左右。所以遇到超过50+字符串,就容易出现栈内存越界了。
解决方案
此问题在我司平台不解决
反馈此问题至百度客服,希望百度解决App栈内存越界问题。