乾貨 | JAVA代碼引發的NATIVE野指針問題(下)

實施hook
咱們有了hook,但目前還不知道是哪一個so中釋放了functor。
若是沒法肯定是哪一個so,能夠多hook幾個so就好了。
固然對於特定的例子,也有技巧來肯定so,好比咱們這個例子:
被析構的對象是Functor類的對象,因爲它的vtbl地址咱們可以從log中獲取到,
而vtbl通常指向定義了該類的so中,因此用vtbl值(0×73648de0)去map表中找,就能肯定是哪一個so了。html

73635000-73646000 rw-p 00000000 00:00 0

73646000-73648000 r-xp 00000000 b3:18 1287       /system/lib/libwebviewchromium_plat_support.so

=>73648000-73649000 r--p 00001000 b3:18 1287       /system/lib/libwebviewchromium_plat_support.so

73649000-7364a000 rw-p 00002000 b3:18 1287       /system/lib/libwebviewchromium_plat_support.so

7364a000-73684000 rw-p 00000000 00:00 0

73684000-73696000 r-xp 00000000 b3:18 1034       /system/lib/libjavacrypto.so

73696000-73697000 r--p 00011000 b3:18 1034       /system/lib/libjavacrypto.so

73697000-73698000 rw-p 00012000 b3:18 1034       /system/lib/libjavacrypto.so

而須要注意的是,C++對象的釋放是delete函數,
libwebviewchromium_plat_support.so不會直接調用libc的free函數,而是調用libc++.so中的delete函數,再由delete函數調用free函數,
因此咱們得hook libc++.so的free函數,但打印調用棧的模塊也依賴libc++.so,因此若是在hook函數中打印調用棧,也會遇到死循環問題。
因此咱們得hook libwebviewchromium_plat_support.so中的delete函數,這樣既減小log量,也能避免死循環。

先確認libwebviewchromium_plat_support.so是否依賴了delete函數:java

$ readelf -s libwebviewchromium_plat_support.so |grep UND

     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND

     1: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_finalize

     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit

     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_unwind_cpp_pr0

     5: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_unwind_cpp_pr1

     6: 00000000     0 FUNC    GLOBAL DEFAULT  UND getrlimit

     7: 00000000     0 FUNC    GLOBAL DEFAULT  UND setrlimit

     8: 00000000     0 FUNC    GLOBAL DEFAULT  UND __errno

     9: 00000000     0 FUNC    GLOBAL DEFAULT  UND strerror

    10: 00000000     0 FUNC    GLOBAL DEFAULT  UND __android_log_print

=>  11: 00000000     0 FUNC    GLOBAL DEFAULT  UND _Znwj

=>  12: 00000000     0 FUNC    GLOBAL DEFAULT  UND _ZdlPv

    14: 00000000     0 FUNC    GLOBAL DEFAULT  UND __android_log_assert

    ...

    51: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_llsr

    52: 00000000     0 OBJECT  GLOBAL DEFAULT  UND __popcount_tab

其中11項_Znwj是new的符號,_ZdlPv是delete的符號。
接下來就用工具hook libwebviewchromium_plat_support.so的delete函數:android

extern void _ZdlPv(void *);

void inject__ZdlPv(void* ptr) {

    LOGD("delete %p",ptr);

    dumpNativeStack();

    dumpJavaStack();

    _ZdlPv(ptr);

}

hook後復現問題,抓到的log以下:c++

10-27 21:19:52.961  8027  8027 D ObserverLayout: onStop: clz=com.miui.player.display.view.DisplayFragmentLayout{45665838 V.E..... ........ 0,0-1080,1920 #7f080039 app:id/content}

10-27 21:19:52.965  8027  8027 I MusicBaseFragment: onDestroyView  the view is still attached, delay destroy

10-27 21:19:52.966  8027  8027 D INJECT  : delete 0x7a7b8530

10-27 21:19:52.986  8027  8027 D INJECT  : #00  pc 000015f6  /system/lib/libinject.so (inject__ZdlPv+21)

10-27 21:19:52.986  8027  8027 D INJECT  : #01  pc 00001134  /system/lib/libwebviewchromium_plat_supp

10-27 21:19:52.986  8027  8027 D INJECT  : #02  pc 00001088  /system/lib/libwebviewchromium_plat_supp

10-27 21:19:52.987  8027  8027 D INJECT  : #03  pc 0001d30c  /system/lib/libdvm.so (dvmPlatformInvoke+112)

10-27 21:19:52.987  8027  8027 D INJECT  : #04  pc 0004d8da  /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, JV+397)

10-27 21:19:52.987  8027  8027 D INJECT  : #05  pc 00026720  /system/lib/libdvm.so

10-27 21:19:52.987  8027  8027 D INJECT  : #06  pc 0002d790  /system/lib/libdvm.so (dvmMterpStd(Thread*)+76)

10-27 21:19:52.987  8027  8027 D INJECT  : #07  pc 0002adf4  /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JVa+184)

10-27 21:19:52.988  8027  8027 D INJECT  : #08  pc 00060058  /system/lib/libdvm.so (dvmInvokeMethod(Object*, Method const*, +391)

10-27 21:19:52.988  8027  8027 D INJECT  : #09  pc 00067ff6  /system/lib/libdvm.so

10-27 21:19:52.988  8027  8027 D INJECT  : #10  pc 00026720  /system/lib/libdvm.so

10-27 21:19:52.988  8027  8027 D INJECT  : #11  pc 0002d790  /system/lib/libdvm.so (dvmMterpStd(Thread*)+76)

10-27 21:19:52.988  8027  8027 D INJECT  : #12  pc 0002adf4  /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JVa+184)

10-27 21:19:52.988  8027  8027 D INJECT  : #13  pc 0005fd74  /system/lib/libdvm.so (dvmCallMethodV(Thread*, Method const*, O+335)

10-27 21:19:52.988  8027  8027 D INJECT  : #14  pc 000494c2  /system/lib/libdvm.so

10-27 21:19:52.989  8027  8027 D INJECT  :   at com.android.webview.chromium.DrawGLFunctor.nativeDestroyGLFunctor(Native Method)

10-27 21:19:52.989  8027  8027 D INJECT  :   at com.android.webview.chromium.DrawGLFunctor.access$000(DrawGLFunctor.java:31)

10-27 21:19:52.989  8027  8027 D INJECT  :   at com.android.webview.chromium.DrawGLFunctor$DestroyRunnable.run(DrawGLFunctor.java:91)

10-27 21:19:52.989  8027  8027 D INJECT  :   at com.android.org.chromium.content.common.CleanupReference.runCleanupTaskInternal(CleanupReference.java:159)

10-27 21:19:52.989  8027  8027 D INJECT  :   at com.android.org.chromium.content.common.CleanupReference.access$300(CleanupReference.java:32)

10-27 21:19:52.989  8027  8027 D INJECT  :   at com.android.org.chromium.content.common.CleanupReference$LazyHolder$1.handleMessage(CleanupReference.java:93)

10-27 21:19:52.990  8027  8027 D INJECT  :   at com.android.org.chromium.content.common.CleanupReference.handleOnUiThread(CleanupReference.java:147)

10-27 21:19:52.990  8027  8027 D INJECT  :   at com.android.org.chromium.content.common.CleanupReference.cleanupNow(CleanupReference.java:141)

10-27 21:19:52.990  8027  8027 D INJECT  :   at com.android.webview.chromium.DrawGLFunctor.destroy(DrawGLFunctor.java:46)

10-27 21:19:52.990  8027  8027 D INJECT  :   at com.android.webview.chromium.WebViewChromium.destroy(WebViewChromium.java:430)

10-27 21:19:52.990  8027  8027 D INJECT  :   at android.webkit.WebView.destroy(WebView.java:667)

10-27 21:19:52.990  8027  8027 D INJECT  :   at com.xiaomi.music.hybrid.HybridFragment.destroyHybridView(HybridFragment.java:64)

10-27 21:19:52.990  8027  8027 D INJECT  :   at com.xiaomi.music.hybrid.HybridFragment.onDestroyView(HybridFragment.java:115)

10-27 21:19:52.990  8027  8027 D INJECT  :   at com.miui.player.component.MusicBaseFragment.onDestroyView(MusicBaseFragment.java:216)

10-27 21:19:52.991  8027  8027 D INJECT  :   at android.app.Fragment.performDestroyView(Fragment.java:1898)

10-27 21:19:52.991  8027  8027 D INJECT  :   at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:954)

10-27 21:19:52.991  8027  8027 D INJECT  :   at android.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1167)

10-27 21:19:52.991  8027  8027 D INJECT  :   at android.app.BackStackRecord.popFromBackStack(BackStackRecord.java:715)

10-27 21:19:52.991  8027  8027 D INJECT  :   at android.app.FragmentManagerImpl.popBackStackState(FragmentManager.java:1544)

10-27 21:19:52.992  8027  8027 D INJECT  :   at android.app.FragmentManagerImpl$3.run(FragmentManager.java:502)

10-27 21:19:52.992  8027  8027 D INJECT  :   at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1449)

10-27 21:19:52.992  8027  8027 D INJECT  :   at android.app.FragmentManagerImpl$1.run(FragmentManager.java:443)

10-27 21:19:52.992  8027  8027 D INJECT  :   at android.os.Handler.handleCallback(Handler.java:733)

10-27 21:19:52.992  8027  8027 D INJECT  :   at android.os.Handler.dispatchMessage(Handler.java:95)

10-27 21:19:52.992  8027  8027 D INJECT  :   at android.os.Looper.loop(Looper.java:136)

10-27 21:19:52.993  8027  8027 D INJECT  :   at android.app.ActivityThread.main(ActivityThread.java:5016)

10-27 21:19:52.993  8027  8027 D INJECT  :   at java.lang.reflect.Method.invokeNative(Native Method)

10-27 21:19:52.993  8027  8027 D INJECT  :   at java.lang.reflect.Method.invoke(Method.java:515)

10-27 21:19:52.993  8027  8027 D INJECT  :   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)

10-27 21:19:52.993  8027  8027 D INJECT  :   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)

10-27 21:19:52.993  8027  8027 D INJECT  :   at dalvik.system.NativeStart.main(Native Method)

10-27 21:19:53.020  8027  8027 I OpenGLRenderer: functor=0x7a7b8530,vtbl=0x400fc1b8

從log中能夠看到,確實是在distroy view的時候釋放了Functor,而隨後再Renderer中又使用了這個Functor。web

打印崩潰時的java調用棧以下:app

10-27 21:19:53.274  8027  8027 I dalvikvm: "main" prio=5 tid=1 TIMED_WAIT10-27 21:19:53.279  8027  8027 I dalvikvm:   | group="main" sCount=0 dsCount=0 obj=0x41716ca8 self=0x415344f8

10-27 21:19:53.279  8027  8027 I dalvikvm:   | sysTid=6895 nice=-6 sched=0/0 cgrp=apps handle=1074409812

10-27 21:19:53.280  8027  8027 I dalvikvm:   | state=R schedstat=( 0 0 0 ) utm=184 stm=61 core=3

10-27 21:19:53.280  8027  8027 I dalvikvm:   at android.view.GLES20Canvas.nDrawDisplayList(Native Method)

10-27 21:19:53.281  8027  8027 I dalvikvm:   at android.view.GLES20Canvas.drawDisplayList(GLES20Canvas.java:420)

10-27 21:19:53.281  8027  8027 I dalvikvm:   at android.view.HardwareRenderer$GlRenderer.drawDisplayList(HardwareRenderer.java:1709)

10-27 21:19:53.281  8027  8027 I dalvikvm:   at android.view.HardwareRenderer$GlRenderer.draw(HardwareRenderer.java:1525)

10-27 21:19:53.282  8027  8027 I dalvikvm:   at android.view.ViewRootImpl.draw(ViewRootImpl.java:2475)

10-27 21:19:53.282  8027  8027 I dalvikvm:   at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2347)

10-27 21:19:53.283  8027  8027 I dalvikvm:   at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1977)

10-27 21:19:53.284  8027  8027 I dalvikvm:   at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1094)

10-27 21:19:53.285  8027  8027 I dalvikvm:   at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5703)

10-27 21:19:53.285  8027  8027 I dalvikvm:   at android.view.Choreographer$CallbackRecord.run(Choreographer.java:764)

10-27 21:19:53.286  8027  8027 I dalvikvm:   at android.view.Choreographer.doCallbacks(Choreographer.java:577)

10-27 21:19:53.287  8027  8027 I dalvikvm:   at android.view.Choreographer.doFrame(Choreographer.java:547)

10-27 21:19:53.288  8027  8027 I dalvikvm:   at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:750)

10-27 21:19:53.289  8027  8027 I dalvikvm:   at android.os.Handler.handleCallback(Handler.java:733)

10-27 21:19:53.289  8027  8027 I dalvikvm:   at android.os.Handler.dispatchMessage(Handler.java:95)

10-27 21:19:53.290  8027  8027 I dalvikvm:   at android.os.Looper.loop(Looper.java:136)

10-27 21:19:53.291  8027  8027 I dalvikvm:   at android.app.ActivityThread.main(ActivityThread.java:5016)

10-27 21:19:53.291  8027  8027 I dalvikvm:   at java.lang.reflect.Method.invokeNative(Native Method)

10-27 21:19:53.292  8027  8027 I dalvikvm:   at java.lang.reflect.Method.invoke(Method.java:515)

10-27 21:19:53.293  8027  8027 I dalvikvm:   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)

10-27 21:19:53.293  8027  8027 I dalvikvm:   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)

10-27 21:19:53.293  8027  8027 I dalvikvm:   at dalvik.system.NativeStart.main(Native Method)

正常狀況下,view在被destroy後不該該再被繪製,經過跟孫念溝通,得知這種狀況多是view在destroy前沒有remove致使的。

分析代碼:
上面delete時的調用棧中有特別的兩行:函數

10-27 21:19:52.990  8027  8027 D INJECT  :   at com.xiaomi.music.hybrid.HybridFragment.destroyHybridView(HybridFragment.java:64)

10-27 21:19:52.990  8027  8027 D INJECT  :   at com.xiaomi.music.hybrid.HybridFragment.onDestroyView(HybridFragment.java:115)

這個是應用的代碼,而這個問題只有在這個應用上出現過,因此極可能是應用的代碼引發的,
因此查了下opengrok中的代碼,發現有兩處destroyHybridView()的實現:工具

@v8-kk-pisces-alpha/packages/apps/MiuiMusic/common/music_sdk/hybrid/src/com/xiaomi/music/hybrid/HybridFragment.java

    private void destroyHybridView() {

        for (HybridView view : mHybridViews) {

            if (view != null) {

                view.destroy();

            }

        }

        mHybridViews.clear();

    }



@v8-kk-pisces-alpha/packages/apps/MiuiSdk/library/src/java/miui/hybrid/HybridFragment.java

    private void destroyHybridView() {

        for (HybridView view : mHybridViews) {

            if (view != null) {

=>              if (view.getParent() != null) {

=>                  ((ViewGroup) view.getParent()).removeView(view);

=>              }

                view.destroy();

            }

        }

        mHybridViews.clear();

    }

跟應用的同事溝通後得知,音樂應用是用上面的代碼,也就是沒有removeView的代碼。
將上面代碼中添加removeView的邏輯後再也不復現問題。

雖然問題獲得解決,但還不清楚爲何沒有removeView會致使野指針。
爲了找到根源仔細閱讀了相關代碼,發現代碼中Render中有detachFunctor的代碼:oop

class GLES20Canvas extends HardwareCanvas {

    ...

    public void detachFunctor(int functor) {

        nDetachFunctor(mRenderer, functor);

    }

用studio在這個代碼中設置斷點,獲得以下調用棧:ui

java.lang.Thread.State: RUNNABLE

      at android.view.GLES20Canvas.detachFunctor(GLES20Canvas.java:321)

      at android.view.HardwareRenderer$GlRenderer.detachFunctor(HardwareRenderer.java:1791)

      at android.view.ViewRootImpl.detachFunctor(ViewRootImpl.java:744)

      at com.android.webview.chromium.DrawGLFunctor$DestroyRunnable.detachNativeFunctor(DrawGLFunctor.java:97)

      at com.android.webview.chromium.DrawGLFunctor.detach(DrawGLFunctor.java:53)

      at com.android.webview.chromium.WebViewChromium.onDetachedFromWindow(WebViewChromium.java:1718)

      at android.webkit.WebView.onDetachedFromWindow(WebView.java:2108)

      at android.view.View.dispatchDetachedFromWindow(View.java:12631)

      at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2587)

      at android.view.ViewGroup.removeViewInternal(ViewGroup.java:3845)

      at android.view.ViewGroup.removeViewInternal(ViewGroup.java:3818)

      at android.view.ViewGroup.removeView(ViewGroup.java:3750)

      at com.xiaomi.music.hybrid.HybridFragment.destroyHybridView(HybridFragment.java:66)

      at com.xiaomi.music.hybrid.HybridFragment.onDestroyView(HybridFragment.java:119)

      at com.miui.player.component.MusicBaseFragment.onDestroyView(MusicBaseFragment.java:216)

      at android.app.Fragment.performDestroyView(Fragment.java:1898)

      at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:954)

      at android.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1167)

      at android.app.BackStackRecord.popFromBackStack(BackStackRecord.java:715)

      at android.app.FragmentManagerImpl.popBackStackState(FragmentManager.java:1544)

      at android.app.FragmentManagerImpl$3.run(FragmentManager.java:502)

      at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1449)

      at android.app.FragmentManagerImpl$1.run(FragmentManager.java:443)

      at android.os.Handler.handleCallback(Handler.java:733)

      at android.os.Handler.dispatchMessage(Handler.java:95)

      at android.os.Looper.loop(Looper.java:136)

      at android.app.ActivityThread.main(ActivityThread.java:5016)

      at java.lang.reflect.Method.invokeNative(Method.java:-1)

      at java.lang.reflect.Method.invoke(Method.java:515)

      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)

      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)

      at dalvik.system.NativeStart.main(NativeStart.java:-1)

加了removeView後,會從Render中刪除Functor,這樣Render在繪製時,再也不調用這個Functor。

這個問題只會在KK上有,L之後對Render作的很大改動,即便不作removeView,也不會存在野指針問題。

 

小米開放平臺重磅推出小米賬號接入有禮活動自今日起至2016年12月31日前成功接入小米賬號便可得到小米開放平臺免費提供的平臺資源(小米應用商店、小米卡包、小米推送vip、小米賬號聯盟等資源),機會不容錯過,咱們期待您的加入!
活動報名地址:http://dev.xiaomi.com/console/hd/account.html?hmsr=%E5%BC%80%E6%BA%90%E4%B8%AD%E5%9B%BD%E5%8D%9A%E5%AE%A2%E6%B8%A0%E9%81%93&hmpl=&hmcu=&hmkw=&hmci=
官方QQ交流羣:398616987

想了解更多?

那就關注咱們吧!

小米開放平臺公衆號二維碼

相關文章
相關標籤/搜索