chromium作爲瀏覽器架構,除提供給瀏覽器做渲染引擎之外,還作爲android平臺上webview的主要構成部分,提供給包括瀏覽器等諸多web app作爲數據加載及渲染只用;
經常遇到某第三方軟件調用webview過程中出現crash,如下一例做參考,調試定位native webview 的crash。
遇到一個crash問題,第三方應用com.whty.wicity.china, monkey過程中出現了crash,可復現,crash log如下:
ABI: 'arm'
pid: 1043, tid: 1531, name: WorkerPool/1531 >>> com.whty.wicity.china <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
r0 00000000 r1 00000000 r2 80000000 r3 00000000
r4 ffffffff r5 a0113220 r6 aa120ae0 r7 a0113220
r8 00000000 r9 00000001 sl aa13c380 fp 00000000
ip ac649d88 sp 8497f3f8 lr ac611c09 pc ac5dab1c cpsr 60070030
d0 6974747568732073 d1 3a6e776f6420676e
d2 0000000000000000 d3 0000000000000000
d4 e4dc8b4d3b1d2cb7 d5 551baf3358041d64
d6 e4740430bf0cabe7 d7 044b8d39302f0ef3
d8 0000000000000000 d9 0000000000000000
d10 0000000000000000 d11 0000000000000000
d12 0000000000000000 d13 0000000000000000
d14 0000000000000000 d15 0000000000000000
d16 0000000000000000 d17 0000000000000000
d18 0000000000000000 d19 0000000000000000
d20 400140e308f51835 d21 4000000000000000
d22 3fd999999997fa04 d23 3fe5555555555593
d24 8f97f8788f97f7e0 d25 8f97f9a88f97f910
d26 0000000200000002 d27 0000000200000002
d28 0000000200000002 d29 0000000200000002
d30 0000000600000006 d31 0000000003424c7c
scr 28000093
backtrace:
#00 pc 00018b1c /system/lib/libc.so (strlen+71)
#01 pc 0004fc05 /system/lib/libc.so (__strlen_chk+4)
#02 pc 0032cf4d /system/lib/libart.so (_ZN3art6Thread6AttachEPKcbP8_jobjectb+136)
#03 pc 00318217 /system/lib/libart.so (_ZN3art7Runtime19AttachCurrentThreadEPKcbP8_jobjectb+66)
#04 pc 0023bf9b /system/lib/libart.so (_ZN3art3JII27AttachCurrentThreadInternalEP7_JavaVMPP7_JNIEnvPvb+330)
#05 pc 01a6f073 /system/app/webview/webview.apk (offset 0x80d000)
#06 pc 00704767 /system/app/webview/webview.apk (offset 0x80d000)
#07 pc 0070f0c1 /system/app/webview/webview.apk (offset 0x80d000)
#08 pc 0070f581 /system/app/webview/webview.apk (offset 0x80d000)
#09 pc 0070ec23 /system/app/webview/webview.apk (offset 0x80d000)
#10 pc 0070f999 /system/app/webview/webview.apk (offset 0x80d000)
#11 pc 0070f7bd /system/app/webview/webview.apk (offset 0x80d000)
#12 pc 01a9e64b /system/app/webview/webview.apk (offset 0x80d000)
#13 pc 01aa17af /system/app/webview/webview.apk (offset 0x80d000)
#14 pc 01a9e493 /system/app/webview/webview.apk (offset 0x80d000)
#15 pc 00047133 /system/lib/libc.so (_ZL15__pthread_startPv+22)
#16 pc 00019e5d /system/lib/libc.so (__start_thread+6)
調試如下:
1. 請測試同事提供corefile及symbol文件
2. 進入prebuild的gcc下的gdb,加載
set solib-search-path /symbols/system/lib/
file symbols/system/bin/app_process32
core-file corefile/core-1043****
加載模塊代碼 directory + code
打出調用棧如下:
(gdb) bt
#0 strlen () at bionic/libc/arch-arm/cortex-a15/bionic/strlen.S:100
#1 0xac611c08 in __strlen_chk (s=0x0, s_len=4294967295) at bionic/libc/bionic/__strlen_chk.cpp:59
#2 0xa9f41f50 in strlen (s=0x0) at bionic/libc/include/string.h:324
#3 length (__s=<optimized out>) at external/libcxx/include/string:640
#4 operator<<<std::__1::char_traits<char> > (__os=..., __str=<optimized out>) at external/libcxx/include/ostream:894
#5 art::Thread::Attach (thread_name=0x0, as_daemon=<optimized out>, thread_group=<optimized out>, create_peer=<optimized out>) at art/runtime/thread.cc:756
#6 0xa9f2d21a in art::Runtime::AttachCurrentThread (this=<optimized out>, thread_name=0x0, as_daemon=false, thread_group=0x0, create_peer=true) at art/runtime/runtime.cc:1499
#7 0xa9e50f9e in art::JII::AttachCurrentThreadInternal (vm=<optimized out>, p_env=0x8497f4fc, raw_args=<optimized out>, as_daemon=false) at art/runtime/java_vm_ext.cc:401
#8 0x98a66074 in AttachCurrentThread (thr_args=0x0, p_env=0x8497f4fc, this=<optimized out>) at ../../third_party/android_tools/ndk/platforms/android-16/arch-arm/usr/include/jni.h:1091
#9 base::android::AttachCurrentThread () at ../../base/android/jni_android.cc:46
#10 0x976fb76a in net::android::VerifyX509CertChain (cert_chain=..., auth_type=..., host=..., status=status@entry=0x8497f584, is_issued_by_known_root=is_issued_by_known_root@entry=0x8dc64978,
verified_chain=verified_chain@entry=0x8497f590) at ../../net/android/network_library.cc:32
#11 0x977060c4 in net::(anonymous namespace)::VerifyFromAndroidTrustManager (cert_bytes=..., hostname=..., verify_result=verify_result@entry=0x8dc6495c) at ../../net/cert/cert_verify_proc_android.cc:39
#12 0x97706584 in net::CertVerifyProcAndroid::VerifyInternal (this=<optimized out>, cert=<optimized out>, hostname=..., ocsp_response=..., flags=14, crl_set=0x0, additional_trust_anchors=...,
verify_result=0x8dc6495c) at ../../net/cert/cert_verify_proc_android.cc:180
#13 0x97705c24 in net::CertVerifyProc::Verify (this=0xa2bac420, cert=0x84b37780, hostname=..., ocsp_response=..., flags=14, flags@entry=6, crl_set=0x0, additional_trust_anchors=...,
verify_result=verify_result@entry=0x8dc6495c) at ../../net/cert/cert_verify_proc.cc:252
#14 0x9770699c in net::DoVerifyOnWorkerThread (verify_proc=..., cert=..., hostname=..., ocsp_response=..., flags=6, crl_set=..., additional_trust_anchors=..., error=0x8dc64958, result=0x8dc6495c)
at ../../net/cert/multi_threaded_cert_verifier.cc:242
#15 0x977067be in Run<scoped_refptr<net::CertVerifyProc> const&, scoped_refptr<net::X509Certificate> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int const&, scoped_refptr<net::CRLSet> const&, std::__1::vector<scoped_refptr<net::X509Certificate>, std::__1::allocator<scoped_refptr<net::X509Certificate> > > const&, int* const&, net::CertVerifyResult* const&> (this=<synthetic pointer>) at ../../base/bind_internal.h:159
#16 MakeItSo<scoped_refptr<net::CertVerifyProc> const&, scoped_refptr<net::X509Certificate> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int const&, scoped_refptr<net::CRLSet> const&, std::__1::vector<scoped_refptr<net::X509Certificate>, std::__1::allocator<scoped_refptr<net::X509Certificate> > > const&, int* const&, net::CertVerifyResult* const&> (runnable=...) at ../../base/bind_internal.h:311
#17 base::internal::Invoker<base::IndexSequence<0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u>, base::internal::BindState<base::internal::RunnableAdapter<void (*)(scoped_refptr<net::CertVerifyProc> const&, scoped_refptr<net::X509Certificate> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, scoped_refptr<net::CRLSet> const&, std::__1::vector<scoped_refptr<net::X509Certificate>, std::__1::allocator<scoped_refptr<net::X509Certificate> > > const&, int*, net::CertVerifyResult*)>, void (scoped_refptr<net::CertVerifyProc> const&, scoped_refptr<net::X509Certificate> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, scoped_refptr<net::CRLSet> const&, std::__1::vector<scoped_refptr<net::X509Certificate>, std::__1::allocator<scoped_refptr<net::X509Certificate> > > const&, int*, net::CertVerifyResult*), scoped_refptr<net::CertVerifyProc> const&, scoped_refptr<net::X509Certificate> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int&, scoped_refptr<net::CRLSet> const&, std::__1::vector<scoped_refptr<net::X509Certificate>, std::__1::allocator<scoped_refptr<net::X509Certificate> > > const&, int*, net::CertVerifyResult*>, base::internal::InvokeHelper<false, void, base::internal::RunnableAdapter<void (*)(scoped_refptr<net::CertVerifyProc> const&, scoped_refptr<net::X509Certificate> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, scoped_refptr<net::CRLSet> const&, std::__1::vector<scoped_refptr<net::X509Certificate>, std::__1::allocator<scoped_refptr<net::X509Certificate> > > const&, int*, net::CertVerifyResult*)> >, void ()>::Run(base::internal::BindStateBase*) (base=<optimized out>) at ../../base/bind_internal.h:364
#18 0x98a9564c in Run (this=0x8dc648d4) at ../../base/callback.h:397
#19 base::(anonymous namespace)::PostTaskAndReplyRelay::Run (this=0x8dc648b8) at ../../base/threading/post_task_and_reply_impl.cc:43
#20 0x98a987b0 in Run (this=0x8497f8a0) at ../../base/callback.h:397
#21 base::(anonymous namespace)::WorkerThread::ThreadMain (this=0x86d57d18) at ../../base/threading/worker_pool_posix.cc:93
#22 0x98a95494 in base::(anonymous namespace)::ThreadFunc (params=<optimized out>) at ../../base/threading/platform_thread_posix.cc:70
#23 0xac609134 in __pthread_start (arg=0x8497f920) at bionic/libc/bionic/pthread_create.cpp:198
#24 0xac5dbe5e in __start_thread (fn=0x80000000, arg=0x0) at bionic/libc/bionic/clone.cpp:41
分析:
1. 在當前f 0幀,list顯示源碼,找到100行:
(gdb) list
95 ands ip, ip, #0x80808080
96 bne .L_zero_in_second_register
97
98 .p2align 2
99 .L_mainloop:
100 ldrd r2, r3, [r1], #8
101
102 pld [r1, #64]
103
104 sub ip, r2, #0x01010101
2. disassemble
Dump of assembler code for function strlen:
0xac5daad4 <+0>: pld [r0]
0xac5daad8 <+4>: mov r1, r0
0xac5daada <+6>: ands.w r3, r0, #7
0xac5daade <+10>: beq.n 0xac5dab1c <strlen+72>
0xac5daae0 <+12>: rsb r3, r3, #8
0xac5daae4 <+16>: movs.w r12, r3, lsl #31
0xac5daae8 <+20>: beq.n 0xac5daaf0 <strlen+28>
0xac5daaea <+22>: ldrb.w r2, [r1], #1
0xac5daaee <+26>: cbz r2, 0xac5dab42 <strlen+110>
0xac5daaf0 <+28>: bcc.n 0xac5dab04 <strlen+48>
0xac5daaf2 <+30>: ands.w r12, r3, #2
0xac5daaf6 <+34>: beq.n 0xac5dab04 <strlen+48>
0xac5daaf8 <+36>: ldrb.w r2, [r1], #1
0xac5daafc <+40>: cbz r2, 0xac5dab42 <strlen+110>
0xac5daafe <+42>: ldrb.w r2, [r1], #1
0xac5dab02 <+46>: cbz r2, 0xac5dab42 <strlen+110>
0xac5dab04 <+48>: tst.w r3, #4
0xac5dab08 <+52>: beq.n 0xac5dab1c <strlen+72>
0xac5dab0a <+54>: ldr.w r3, [r1], #4
0xac5dab0e <+58>: sub.w r12, r3, #16843009 ; 0x1010101
0xac5dab12 <+62>: bic.w r12, r12, r3
0xac5dab16 <+66>: ands.w r12, r12, #2155905152 ; 0x80808080
0xac5dab1a <+70>: bne.n 0xac5dab76 <strlen+162>
=> 0xac5dab1c <+72>: ldrd r2, r3, [r1], #8
0xac5dab20 <+76>: pld [r1, #64] ; 0x40
0xac5dab24 <+80>: sub.w r12, r2, #16843009 ; 0x1010101
3. info register 顯示寄存器信息
r0 0x0 0
r1 0x0 0
r2 0x80000000 2147483648
r3 0x0 0
r4 0xffffffff 4294967295
r5 0xa0113220 2685481504
r6 0xaa120ae0 2853309152
r7 0xa0113220 2685481504
r8 0x0 0
r9 0x1 1
r10 0xaa13c380 2853421952
r11 0x0 0
r12 0xac649d88 2892275080
sp 0x8497f3f8 0x8497f3f8
lr 0xac611c09 -1402921975
pc 0xac5dab1c 0xac5dab1c <strlen+72>
cpsr 0x60070030 1611071536
4. 追蹤上一幀變量傳入 f 1:
54 * }
55 *
56 * or anytime strlen reads beyond an object boundary.
57 */
58 extern "C" size_t __strlen_chk(const char* s, size_t s_len) {
59 size_t ret = strlen(s);
60
61 if (__predict_false(ret >= s_len)) {
62 __fortify_chk_fail("strlen: prevented read past end of buffer", 0);
63 }
即59行,計算strlen(s),p 一下s,顯示
(gdb) p s
$1 = 0x0
s爲空地址,導致的後邊strlen的crash
5. 繼續追蹤上一層 f 2,string.h文件
08__BIONIC_FORTIFY_INLINE
309size_t strlen(const char *s) {
310 size_t bos = __bos(s);
311
312#if !defined(__clang__)
313 // Compiler doesn't know destination size. Don't call __strlen_chk
314 if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
315 return __builtin_strlen(s);
316 }
317
318 size_t slen = __builtin_strlen(s);
319 if (__builtin_constant_p(slen)) {
320 return slen;
321 }
322#endif /* !defined(__clang__) */
323
324 return __strlen_chk(s, bos);
chromium走的clang編譯器,所以直接調到__strlen_chk(s, bos), s=0x0,爲傳入參數,繼續向上追蹤s的來源
6. 進入f3
static inline size_t length(const char_type* __s) {return strlen(__s);}
入參爲內聯函數length的入參,繼續向上;
7. 進入f4
892 operator<<(basic_ostream<char, _Traits>& __os, const char* __str)
893 {
894 return _VSTD::__put_character_sequence(__os, __str, _Traits::length(__str));
895 }
s來自const char* __str,也就是最後一個參數,還要繼續上行
8. 進入f5
745Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_group,
746 bool create_peer) {
747 Runtime* runtime = Runtime::Current();
748 if (runtime == nullptr) {
749 LOG(ERROR) << "Thread attaching to non-existent runtime: " << thread_name;
750 return nullptr;
751 }
752 Thread* self;
753 {
754 MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_);
755 if (runtime->IsShuttingDownLocked()) {
756 LOG(WARNING) << "Thread attaching while runtime is shutting down: " << thread_name;
757 return nullptr;
758 } else {
759 Runtime::Current()->StartThreadBirth();
760 self = new Thread(as_daemon);
761 bool init_success = self->Init(runtime->GetThreadList(), runtime->GetJavaVM());
762 Runtime::Current()->EndThreadBirth();
763 if (!init_success) {
764 delete self;
765 return nullptr;
766 }
767 }
768 }
756行, LOG打印, thread_name就是後面的s, 打印p thread_name
(gdb) p thread_name
$3 = 0x0
下面要檢查爲什麼thread_name = 0x0;我們看到thread_name就是attach的入參,中間沒有賦值;因此繼續向上追溯
9. 進入f6 ,沒有變量修改,直接透傳,進入f7
f 6
1496 bool Runtime::AttachCurrentThread(const char* thread_name, bool as_daemon, jobject thread_group,
1497 bool create_peer) {
1498 ScopedTrace trace(__FUNCTION__);
1499 return Thread::Attach(thread_name, as_daemon, thread_group, create_peer) != nullptr;
1500 }
f7
367 static jint AttachCurrentThreadInternal(JavaVM* vm, JNIEnv** p_env, void* raw_args, bool as_daemon) {
368 if (vm == nullptr || p_env == nullptr) {
369 return JNI_ERR;
370 }
371
372 // Return immediately if we're already attached.
373 Thread* self = Thread::Current();
374 if (self != nullptr) {
375 *p_env = self->GetJniEnv();
376 return JNI_OK;
377 }
378
379 Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->GetRuntime();
380
381 // No threads allowed in zygote mode.
382 if (runtime->IsZygote()) {
383 LOG(ERROR) << "Attempt to attach a thread in the zygote";
384 return JNI_ERR;
385 }
386
387 JavaVMAttachArgs* args = static_cast<JavaVMAttachArgs*>(raw_args);
388 const char* thread_name = nullptr;
389 jobject thread_group = nullptr;
390 if (args != nullptr) {
391 if (IsBadJniVersion(args->version)) {
392 LOG(ERROR) << "Bad JNI version passed to "
393 << (as_daemon ? "AttachCurrentThreadAsDaemon" : "AttachCurrentThread") << ": "
394 << args->version;
395 return JNI_EVERSION;
396 }
397 thread_name = args->name;
398 thread_group = args->group;
399 }
400
401 if (!runtime->AttachCurrentThread(thread_name, as_daemon, thread_group,
402 !runtime->IsAotCompiler())) {
403 *p_env = nullptr;
404 return JNI_ERR;
405 } else {
406 *p_env = Thread::Current()->GetJniEnv();
407 return JNI_OK;
408 }
409 }
410};
文件java_vm_ext.cc中401行傳入的thread_name, 爲null,從代碼上看thread_name爲空有2種情況:
A. thread_name = args->name 爲空
B. args 爲空導致const char* thread_name = nullptr 未進入後面的賦值語句。
因此繼續向上追溯;檢查args 也就是raw_args的值;
由於raw_args也就是AttachCurrentThreadInternal(JavaVM* vm, JNIEnv** p_env, void* raw_args, bool as_daemon) 入參的void* raw_args
因此向上追溯
328 static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
329 return AttachCurrentThreadInternal(vm, p_env, thr_args, false);
330 }
AttachCurrentThread封裝了AttachCurrentThreadInternal,thr_args透傳;
10. 進入f 8
90 jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
1091 { return functions->AttachCurrentThread(this, p_env, thr_args); }
這裏是個jni映射,繼續追
11. 進入f 9
#9 base::android::AttachCurrentThread () at ../../base/android/jni_android.cc:46
46 jint ret = g_jvm->AttachCurrentThread(&env, NULL);
可以看到AttachCurrentThread入參直接傳入的NULL,也就是f7幀,args爲空;因此thread_name賦值而爲空,且使用了clang編譯器,進行 __strlen_chk,導致後面log打印的時候的crash;