你的App是否有出现过幽灵调用?

前言

在前段时间遇到一个挺有意思的问题,高概率集中在某个调用堆栈过程中,或是在 GC 过程中 verify 失败发现存在坏根。经过几天的调试后才锁定编译优化的问题,以Nterp解释运行缓存了字节码,出现幽灵调用函数。

错误堆栈

场景一

yaml 复制代码
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000018
Cause: null pointer dereference
    x0  0000000000000000  x1  0000000002e62ec8  x2  0000000000000000  x3  0000000072223650
    x4  0000007c3ec13000  x5  3b7463656a624f2f  x6  3b7463656a624f2f  x7  0000007bbdababac
    x8  0000000000000002  x9  2542ebd30d0dfceb  x10 0000000000000000  x11 0000000000000002
    x12 00000000af950a08  x13 b400007d15e5fa50  x14 0000007f1598f880  x15 0000007bbd9446e8
    x16 0000007fea726e40  x17 0000000000000020  x18 0000007f15ca0000  x19 b400007d55e10be0
    x20 0000000000000000  x21 b400007d55e10ca0  x22 0000000002d51610  x23 0000000002e61b08
    x24 0000000000000005  x25 0000000000000002  x26 0000000002e62ec8  x27 0000000000000002
    x28 00000000031b1f38  x29 00000000ffffffff
    lr  00000000721b63c8  sp  0000007fea728e90  pc  00000000721b63d4  pst 0000000080001000101 total frames
backtrace:
      #00 pc 00000000008123d4  /system/framework/arm64/boot-framework.oat (android.view.ViewGroup.jumpDrawablesToCurrentState+132)
      #01 pc 00000000007ca6c8  /system/framework/arm64/boot-framework.oat (android.view.View.onDetachedFromWindowInternal+472)
      #02 pc 00000000007bb8d0  /system/framework/arm64/boot-framework.oat (android.view.View.dispatchDetachedFromWindow+288)
      #03 pc 000000000080c444  /system/framework/arm64/boot-framework.oat (android.view.ViewGroup.dispatchDetachedFromWindow+484)
      #04 pc 000000000080c34c  /system/framework/arm64/boot-framework.oat (android.view.ViewGroup.dispatchDetachedFromWindow+236)
      #05 pc 000000000080c34c  /system/framework/arm64/boot-framework.oat (android.view.ViewGroup.dispatchDetachedFromWindow+236)
...

场景二

ini 复制代码
verification.cc:124] GC tried to mark invalid reference 0x20052a8
verification.cc:124] ref=0x20052a8 klass=0x0 <invalid address> space=main space (region space) 0x2000000-0x42000000 card=0 adjacent_ram=0000000000000000 0000000000000000 0000000000000000 0000000000000000 |0000000000000000 0000000000000000 0000000000000000 0000000000000000
verification.cc:124] holder=0x3ac8698 klass=0x20052a8 <invalid address> space=main space (region space) 0x2000000-0x42000000 card=0 adjacent_ram=9d73f26e02aea3e0 0000000002a33738 1000000002a38c48 0000000002a33738 |bd4f9b6d020052a8 0000000000000000 0000000000000000 0000000000000000
verification.cc:124]  reference addr adjacent_ram=9d73f26e02aea3e0 0000000002a33738 1000000002a38c48 0000000002a33738 |bd4f9b6d020052a8 0000000000000000 0000000000000000 0000000000000000 0xb400007a5760fdb0 main space (region space) 0x2000000-0x42000000

Core分析

复现抓取了对应错误 core 文件进行分析,该类型问题利用 core-parser 解析能力是最佳的。

ini 复制代码
core-parser> bt
Switch oat version(259) env.
"main" sysTid=7943 Runnable
  | group="main" daemon=0 prio=5 target=0x0 uncaught_exception=0x0
  | tid=1 sCount=0 flags=0 obj=0x7370c3e0 self=0xb400007a09207010 env=0xb400007a4920dd50
  | stack=0x7fc3918000-0x7fc391a000 stackSize=0x7ff000 handle=0x7c428fa098
  | mutexes=0xb400007a092077b0 held="mutator lock"(shared held) 
  x0  0x0000000000000000  x1  0x00000000027e6d40  x2  0x0000000000000000  x3  0x0000000072fc3650  
  x4  0x0000007963213000  x5  0x000000006576696c  x6  0x000000006576696c  x7  0x000000794915fad4  
  x8  0x0000000000000002  x9  0x2e157e9e8ba7cb87  x10 0x0000000000000000  x11 0x0000000000000001  
  x12 0x00000000b06119e0  x13 0x000000000000005a  x14 0x0000007c4156f880  x15 0x0000007948fa193c  
  x16 0x0000007fc410ff90  x17 0x0000000000000020  x18 0x0000007c41ae8000  x19 0xb400007a09207010  
  x20 0x0000000000000000  x21 0xb400007a092070d0  x22 0x00000000026cefa0  x23 0x00000000027e5980  
  x24 0x0000000000000005  x25 0x0000000000000002  x26 0x00000000027e6d40  x27 0x00000000027e2478  
  x28 0x00000000026cc620  fp  0x00000000027df8f0  
  lr  0x0000000072f563c8  sp  0x0000007fc4111fe0  pc  0x0000000072f563d4  pst 0x0000000080001000  
  Native: #0  0000000072f563d4  
  Native: #1  0000000072f563c4  
  ManagedStack* 0xb400007a092070b8 maybe invalid.
  JavaKt: #0  0000000000000000  android.view.View[].
  JavaKt: #1  0000000000000000  
  JavaKt: #2  0000000000000000  java.lang.reflect.Method.invoke
  JavaKt: #3  000000795f4796fe  com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run
  JavaKt: #4  000000795f47e568  com.android.internal.os.ZygoteInit.main

该堆栈输出失败是因为在 oat 上报错,ManagedStack 还未保存这一帧,因此需要进行假帧处理。

css 复制代码
core-parser> fake stack --sp 0x0000007fc4111fe0 --pc 0x0000000072f563d4
yaml 复制代码
core-parser> bt
"main" sysTid=7943 Runnable
  | group="main" daemon=0 prio=5 target=0x0 uncaught_exception=0x0
  | tid=1 sCount=0 flags=0 obj=0x7370c3e0 self=0xb400007a09207010 env=0xb400007a4920dd50
  | stack=0x7fc3918000-0x7fc391a000 stackSize=0x7ff000 handle=0x7c428fa098
  | mutexes=0xb400007a092077b0 held="mutator lock"(shared held) 
  x0  0x0000000000000000  x1  0x00000000027e6d40  x2  0x0000000000000000  x3  0x0000000072fc3650  
  x4  0x0000007963213000  x5  0x000000006576696c  x6  0x000000006576696c  x7  0x000000794915fad4  
  x8  0x0000000000000002  x9  0x2e157e9e8ba7cb87  x10 0x0000000000000000  x11 0x0000000000000001  
  x12 0x00000000b06119e0  x13 0x000000000000005a  x14 0x0000007c4156f880  x15 0x0000007948fa193c  
  x16 0x0000007fc410ff90  x17 0x0000000000000020  x18 0x0000007c41ae8000  x19 0xb400007a09207010  
  x20 0x0000000000000000  x21 0xb400007a092070d0  x22 0x00000000026cefa0  x23 0x00000000027e5980  
  x24 0x0000000000000005  x25 0x0000000000000002  x26 0x00000000027e6d40  x27 0x00000000027e2478  
  x28 0x00000000026cc620  fp  0x00000000027df8f0  
  lr  0x0000000072f563c8  sp  0x0000007fc4111fe0  pc  0x0000000072f563d4  pst 0x0000000080001000  
  Native: #0  0000000072f563d4  
  Native: #1  0000000072f563c4  
  JavaKt: #00  000000795fe114ac  android.view.ViewGroup.jumpDrawablesToCurrentState
  JavaKt: #01  000000795fe4a60a  android.view.View.onDetachedFromWindowInternal
  JavaKt: #02  000000795fe461ea  android.view.View.dispatchDetachedFromWindow
  JavaKt: #03  000000795fe0f92c  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #04  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #05  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #06  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #07  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #08  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #09  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #10  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #11  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #12  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #13  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #14  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #15  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #16  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #17  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #18  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #19  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #20  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #21  000000795fe0f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #22  000000795fe25f4a  android.view.ViewRootImpl.dispatchDetachedFromWindow
  JavaKt: #23  000000795fe26b48  android.view.ViewRootImpl.doDie
  JavaKt: #24  000000795fe1e94c  android.view.ViewRootImpl.die
  JavaKt: #25  000000795fe5fe62  android.view.WindowManagerGlobal.removeViewLocked
  JavaKt: #26  000000795fe5fdb4  android.view.WindowManagerGlobal.removeView
  JavaKt: #27  000000795fe6105e  android.view.WindowManagerImpl.removeViewImmediate
  JavaKt: #28  0000007961796140  android.app.ActivityThread.handleDestroyActivity
  JavaKt: #29  00000079618de728  android.app.servertransaction.DestroyActivityItem.execute
  JavaKt: #30  00000079618e1784  android.app.servertransaction.TransactionExecutor.executeLifecycleItem
  JavaKt: #31  00000079618e18c0  android.app.servertransaction.TransactionExecutor.executeTransactionItems
  JavaKt: #32  00000079618e169a  android.app.servertransaction.TransactionExecutor.execute
  JavaKt: #33  000000796178bc92  android.app.ActivityThread$H.handleMessage
  JavaKt: #34  000000796065a7f2  android.os.Handler.dispatchMessage
  JavaKt: #35  0000007960683a0e  android.os.Looper.loopOnce
  JavaKt: #36  0000007960684330  android.os.Looper.loop
  JavaKt: #37  0000007961799df0  android.app.ActivityThread.main
  JavaKt: #38  0000000000000000  java.lang.reflect.Method.invoke
  JavaKt: #39  000000795f4796fe  com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run
  JavaKt: #40  000000795f47e568  com.android.internal.os.ZygoteInit.main
core-parser>

内存分析

yaml 复制代码
core-parser> f 0
  JavaKt: #00  000000795fe114ac  android.view.ViewGroup.jumpDrawablesToCurrentState()
  {
      Location: /system/framework/framework.jar!classes4.dex
      art::ArtMethod: 0x71a2c858
      dex_pc_ptr: 0x795fe114ac
      quick_frame: 0x7fc4111fe0
      frame_pc: 0x72f563d4
      method_header: 0x72f5634c

      DEX CODE:
      0x795fe114a2: 0212                     | const/4 v2, #+0
      0x795fe114a4: 1235 000a                | if-ge v2, v1, 0x795fe114b8 //+10
      0x795fe114a8: 0346 0200                | aget-object v3, v0, v2
      0x795fe114ac: 106e 80d3 0003           | invoke-virtual {v3}, void android.view.View.jumpDrawablesToCurrentState() // method@32979

      OAT CODE:
      0x72f563b0: 6b18033f | cmp w25, w24
      0x72f563b4: 540001aa | b.ge 0x72f563e8
      0x72f563b8: 110032e0 | add w0, w23, #0xc
      0x72f563bc: 1000007e | adr x30, 0x72f563c8
      0x72f563c0: b59da314 | cbnz x20, 0x72e91820
      0x72f563c4: b8797801 | ldr w1, [x0, x25, lsl #2]
      0x72f563c8: aa0103fa | mov x26, x1
      0x72f563cc: b9400020 | ldr w0, [x1]
      0x72f563d0: f949c000 | ldr x0, [x0, #0x1380]
      0x72f563d4: f9400c1e | ldr x30, [x0, #0x18]
      0x72f563d8: d63f03c0 | blr x30
      0x72f563dc: 11000739 | add w25, w25, #1
  }

可以看到当前错误发生在 invoke-virtual jumpDrawablesToCurrentState 的过程当中。

yaml 复制代码
      0x72f563cc: b9400020 | ldr w0, [x1]           从 View 对象中取 klass_ 地址
      0x72f563d0: f949c000 | ldr x0, [x0, #0x1380]  在 klass_ 中找到 ArtMethod 地址
      0x72f563d4: f9400c1e | ldr x30, [x0, #0x18]   在 ArtMethod 中找到代码地址
      0x72f563d8: d63f03c0 | blr x30                跳转到函数 jumpDrawablesToCurrentState
markdown 复制代码
core-parser> vtor 0x00000000027e6d40
  * VIRTUAL: 0x27e6d40
  * OFFSET: 0x7e6d40
  * OR: 0x78f506c6bd40
  * MMAP: 0x0
  * OVERLAY: 0x0
[2000000, 42000000)  rw-  0040000000  0040000000  [anon:dalvik-main space (region space)] [*]

可见地址范围是落在 Java 堆上,p 指令输出下该对象信息。

css 复制代码
core-parser> p 0x00000000027e6d40
ERROR: Size: 0x0
makefile 复制代码
core-parser> rd 0x00000000027e6d40 -e 0x00000000027e6e40
27e6d40: 8adbc7e1b0000888  0000000000000000  ................
27e6d50: 0000000000000000  0000000000000000  ................
27e6d60: 0000000000000000  0000000000000000  ................
27e6d70: 0000000000000000  0000000000000000  ................
27e6d80: 0000000000000000  0000000000000000  ................
27e6d90: 028c86b000000000  0000000000000000  ................
27e6da0: 00000000026ccfd8  0000000000000000  ..l.............
27e6db0: 0000000071199090  028c86c800000000  ...q............
27e6dc0: 0000000000000000  0000000000000000  ................
27e6dd0: 028c8718028c86e8  028c8760028c8730  ........0...`...
27e6de0: 028c8798028c8778  0000000000000000  x...............
27e6df0: 0000000000000000  028c87e8028c87b0  ................
27e6e00: 00000000028c8860  028c887800000000  `...........x...
27e6e10: 0000000000000000  711a35f800000000  .............5.q
27e6e20: 026cefa000000000  0000000000000000  ......l.........
27e6e30: 0000000000000000  028c889000000000  ................

可见该地址是个坏根,非Java对象地址,由于 b0000888 落在有效内存段上。

markdown 复制代码
core-parser> vtor b0000888
  * VIRTUAL: 0xb0000888
  * OFFSET: 0x1ef4888
  * OR: 0x75b21f265888
  * MMAP: 0x0
  * OVERLAY: 0x0
[ae10c000, b010c000)  r-x  0002000000  0002000000  /memfd:jit-cache (deleted) [*]
makefile 复制代码
core-parser> rd 0xb0001c08
b0001c08: 0000000000000000  .......

因此在 0x72f563d4: f9400c1e | ldr x30, [x0, #0x18] 上才出现错误。

scala 复制代码
core-parser> class android.view.View -m 
[0x710274a8]
public class android.view.View extends java.lang.Object {
  // Methods:
  ...
    [0x71a15d60] public void android.view.View.jumpDrawablesToCurrentState()
  ...
ini 复制代码
0x710274a8 + 0x1380 = 0x71028828
core-parser> rd  0x71028828
71028828: 0000000071a15d60  `].q....

当能正确获取 view klass 地址,程序是正确的。

堆坏根检测

vbnet 复制代码
core-parser> space -c
ERROR: Region:[0x27e6d40, 0x27e7100) main space (region space) has bad object!!
makefile 复制代码
core-parser> rd 0x27e6c40 -e 0x27e6d40
27e6c40: 1800001000000000  0000000000000001  ................
27e6c50: 4000000000000000  0000000000000000  .......@........
27e6c60: 40a00000bf800000  bf8000003f800000  .......@...?....
27e6c70: 0000000000000000  0000000000000000  ................
27e6c80: 7fc000007fc00000  7fc0000000000000  ................
27e6c90: 000000003f800000  0000010100000001  ...?............
27e6ca0: 0000000000000000  0000000000000000  ................
27e6cb0: 0000000100000000  0100000000000000  ................
27e6cc0: 0000000000000000  028c8de0027e69a8  .........i~.....
27e6cd0: 028c8e00028c8df0  0000000000000000  ................
27e6ce0: 028c8eb000000000  0000000005aefcf5  ................
27e6cf0: 0000000000000011  0000000000000000  ................
27e6d00: 0000000100000000  028c8ec000000000  ................
27e6d10: 028c8f5000000000  70c4b320028c8f90  ....P..........p
27e6d20: 800a3035b02fc458  00000000026cf448  X./.50..H.l.....
27e6d30: 00000000b02fbed0  00000000026cf448  ../.....H.l.....
ini 复制代码
core-parser> p 27e6d30 -b
Size: 0x10
Padding: 0x4
Object Name: a.b.c.d.e.f.g$h
    [0x8] final a.b.c.d.e.f.g k = 0x26cf448
  // extends java.lang.Object
    [0x4] private transient int shadow$_monitor_ = 0
    [0x0] private transient java.lang.Class shadow$_klass_ = 0xb02fbed0
Binary:
27e6d30: 00000000b02fbed0  00000000026cf448  ../.....H.l.....
ini 复制代码
core-parser> p 0x27e7100 -b
Size: 0x10
Object Name: a.b.c.d.u.s.t
    [0x0c] private java.io.File toq = 0x0
    [0x08] private final android.content.Context k = 0x26dd860
  // extends java.lang.Object
    [0x04] private transient int shadow$_monitor_ = 0
    [0x00] private transient java.lang.Class shadow$_klass_ = 0xb0208330
Binary:
27e7100: 00000000b0208330  00000000026dd860  0.......`.m.....
a.b.c.d.e.f.g$h 坏根大小 a.b.c.d.u.s.t
0x27e6d30 0x27e6d40 0x27e7100

0x27e6d40 ~ 0x27e7100 中间存在至少一个坏根,总大小未 0x3c0。

坏根内存分析

makefile 复制代码
core-parser> rd 0x27e6d40 -e 0x27e7100
27e6d40: 8adbc7e1b0000888  0000000000000000  ................
27e6d50: 0000000000000000  0000000000000000  ................
27e6d60: 0000000000000000  0000000000000000  ................
27e6d70: 0000000000000000  0000000000000000  ................
27e6d80: 0000000000000000  0000000000000000  ................
27e6d90: 028c86b000000000  0000000000000000  ................
27e6da0: 00000000026ccfd8  0000000000000000  ..l.............
27e6db0: 0000000071199090  028c86c800000000  ...q............
27e6dc0: 0000000000000000  0000000000000000  ................
27e6dd0: 028c8718028c86e8  028c8760028c8730  ........0...`...
27e6de0: 028c8798028c8778  0000000000000000  x...............
27e6df0: 0000000000000000  028c87e8028c87b0  ................
27e6e00: 00000000028c8860  028c887800000000  `...........x...
27e6e10: 0000000000000000  711a35f800000000  .............5.q
27e6e20: 026cefa000000000  0000000000000000  ......l.........
27e6e30: 0000000000000000  028c889000000000  ................
27e6e40: 00000000027def48  027767f800000000  H.}..........gw.
27e6e50: 00000000027dc398  028c88b000000000  ..}.............
27e6e60: 0000000000000000  0000000000000000  ................
27e6e70: 0000000000000000  0000000000000000  ................
27e6e80: 0000000000000000  0000000000000000  ................
27e6e90: 0000000000000000  0000000000000000  ................
27e6ea0: 0000000000000000  ffffffff00000000  ................
27e6eb0: ffffffffffffffff  ffffffff00000090  ................
27e6ec0: 00000000ffffffff  0000000000000000  ................
27e6ed0: 0000000000000000  2dffffff00000a60  ........`......-
27e6ee0: 0000000000000000  0000000000000000  ................
27e6ef0: 000000012d000000  ffffffff00000000  ...-............
27e6f00: ffffffff00000002  0300000300000000  ................
27e6f10: 0000000000000000  00000a6000000000  ............`...
27e6f20: 00000000000004c4  0000000000000000  ................
27e6f30: 0000000000000000  ffffffff00000000  ................
27e6f40: ffffffffffffffff  ffffffffffffffff  ................
27e6f50: 40000a60ffffffff  00000001400004c4  ....`..@...@....
27e6f60: 0000000000000000  0000000000000000  ................
27e6f70: 81280810fffff448  0010001060032628  H.....(.(&.`....
27e6f80: 000004c428000001  0000000000000000  ...(............
27e6f90: 0000000000000000  0000000303000003  ................
27e6fa0: 0000000000000000  0000001900000000  ................
27e6fb0: 0000000000000000  8000000000000000  ................
27e6fc0: 0000000000000000  0000000000000000  ................
27e6fd0: 0000000080000000  1800021000000000  ................
27e6fe0: 0000000000000001  4000000000000000  ...............@
27e6ff0: 0000000000000000  40a00000bf800000  ...............@
27e7000: bf8000003f800000  0000000000000000  ...?............
27e7010: 0000000000000000  7fc000007fc00000  ................
27e7020: 7fc0000000000000  000000003f800000  ...........?....
27e7030: 0000010100000001  0000000000000000  ................
27e7040: 0000000000000000  0000000100000000  ................
27e7050: 0000000000000000  028c88c800000000  ................
27e7060: 00000000028c88d8  0000000000000000  ................
27e7070: 0000000000000000  0000000000000000  ................
27e7080: 028c8958028c8928  028c8980028c8970  (...X...p.......
27e7090: 028c88c8028c89a0  7114440000000000  .............D.q
27e70a0: 028c89c000000000  00000000028c89d8  ................
27e70b0: 000000ff00000000  000697bbffffffff  ................
27e70c0: 0000000000030890  7fffffff7fffffff  ................
27e70d0: 0000010000000000  0000000000000000  ................
27e70e0: 0000000000010000  028c8a10028c89f0  ................
27e70f0: 0000000000000000  0000000000000000  ................

通过数据特征分析,确定为某个Java对象的数据结构。这里就定义为 A 类对象。

scala 复制代码
core-parser> class A -f
[0xb01f0888]
public final class A extends androidx.appcompat.widget.AppCompatImageView {
  // Object instance fields:
    [0x03b8] private boolean n
    [0x03b4] private a.b.c.d.u.o q
    [0x03b0] private volatile a.b.c.d.u.H k

  // extends androidx.appcompat.widget.AppCompatImageView
    [0x03ac] private final androidx.appcompat.widget.ld6 mImageHelper
    [0x03a8] private final androidx.appcompat.widget.q mBackgroundTintHelper
    [0x03a6] private boolean mHasLevel
...
}

该对象大小正好 0x3c0,满足堆内存前后关系。

数据恢复

shell 复制代码
core-parser> wd 0x27e6d40 -v 8adbc7e1b01f0888
ini 复制代码
core-parser> p 0x27e6d40
Size: 0x3c0
Padding: 0x7
Object Name: A
    [0x3b8] private boolean n = false
    [0x3b4] private a.b.c.d.u.o q = 0x0
    [0x3b0] private volatile a.b.c.d.u.H k = 0x0
  // extends androidx.appcompat.widget.AppCompatImageView
    [0x3ac] private final androidx.appcompat.widget.ld6 mImageHelper = 0x28c8a10
    [0x3a8] private final androidx.appcompat.widget.q mBackgroundTintHelper = 0x28c89f0
    [0x3a6] private boolean mHasLevel = false
  // extends android.widget.ImageView
    [0x3a5] private boolean mMergeState = false
...
    [0x004] private transient int shadow$_monitor_ = -1965307935
    [0x000] private transient java.lang.Class shadow$_klass_ = 0xb01f0888
core-parser> 

采样校验

ini 复制代码
[0x3ac] private final androidx.appcompat.widget.ld6 mImageHelper = 0x28c8a10
core-parser> p 0x28c8a10
Size: 0x20
Padding: 0x4
Object Name: androidx.appcompat.widget.ld6
    [0x18] private int n = 0
    [0x14] private androidx.appcompat.widget.d3 zy = 0x0
    [0x10] private androidx.appcompat.widget.d3 toq = 0x0
    [0x0c] private androidx.appcompat.widget.d3 q = 0x0
    [0x08] private final android.widget.ImageView k = 0x27e6d40
  // extends java.lang.Object
    [0x04] private transient int shadow$_monitor_ = 0
    [0x00] private transient java.lang.Class shadow$_klass_ = 0xb0299708
    
[0x3a8] private final androidx.appcompat.widget.q mBackgroundTintHelper = 0x28c89f0
core-parser> p 0x28c89f0
Size: 0x20
Object Name: androidx.appcompat.widget.q
    [0x1c] private int zy = -1
    [0x18] private final androidx.appcompat.widget.f7l8 toq = 0x2766330
    [0x14] private androidx.appcompat.widget.d3 q = 0x0
    [0x10] private androidx.appcompat.widget.d3 n = 0x0
    [0x0c] private final android.view.View k = 0x27e6d40
    [0x08] private androidx.appcompat.widget.d3 g = 0x0
  // extends java.lang.Object
    [0x04] private transient int shadow$_monitor_ = 0
    [0x00] private transient java.lang.Class shadow$_klass_ = 0xb0299bf0
core-parser> 

[0x0dc] android.view.ViewOutlineProvider mOutlineProvider = 0x711a35f8
core-parser> p 0x711a35f8
Size: 0x8
Object Name: android.view.ViewOutlineProvider$1
  // extends android.view.ViewOutlineProvider
  // extends java.lang.Object
    [0x4] private transient int shadow$_monitor_ = 536870912
    [0x0] private transient java.lang.Class shadow$_klass_ = 0x71392bc8

可见内存中的其它成员对象是正确有效的。也就是 klass_ 内容中 1f 被擦掉。

makefile 复制代码
core-parser> rd 0x27e6d40 --ori
27e6d40: 8adbc7e1b0000888  ........

core-parser> rd 0x27e6d40
27e6d40: 8adbc7e1b01f0888  ........
ini 复制代码
0xb01f0888 + 0x1380 = 0xb01f1c08
 
core-parser> rd 0xb01f1c08
b01f1c08: 0000000071a76ea8  .n.q....

core-parser> method 0000000071a76ea8
public void android.widget.ImageView.jumpDrawablesToCurrentState() [dex_method_idx=51663]

错误内存

地址 正确值 错误值 对象名
27e6d40 8adbc7e1b01f0888 8adbc7e1b0000888 A

内存检测

本地复现出现各类场景堆栈,基本都是klass_坏根错误。统计错误特征如下:

错误值 正确值 类名
0x70004a00 0x70504a00 java.lang.String
0xaf005a30 0xafa55a30 A
0xaf004db0 0xafb54db0 a.b.c.u.m$t
0xaf0000a8 0xafb600a8 a.b.c.d.e.b.M
0xaf00d4d8 0xafb2d4d8 a.a.t.b.i.T
0x7000b170 0x708fb170 android.view.View$ScrollabilityCache
0x7000ff90 0x704eff90 java.lang.Object[]
0x70008970 0x70bf8970 android.view.View$ListenerInfo
0x70003418 0x704f3418 java.util.HashMap
main space 的类
0x030026f8 0x032326f8 android.graphics.RenderNodeStubImpl

由于没有条件设置硬件观察点,并且观察Java对象地址,变化过大不可取,于是通过 core-parser 注入一个观察线程,通过异步抓取最近节点的堆栈。

arduino 复制代码
// Monitor::CheckVirtualMemory((void *)0x2000000, 0x1000000);

void Monitor::CheckVirtualMemory(void *vaddr, uint64_t size) {
    while (1) {
        uint64_t *current = (uint64_t *)vaddr;
        uint64_t *end = (uint64_t *)((uint64_t)vaddr + size);
        while (current < end) {
            volatile uint32_t value = (uint32_t)(*current);
            if (value == 0x70004a00 || value == 0xaf005a30
                    || value == 0xaf004db0 || value == 0xaf0000a8)
                Report();
            current++;
        }
    }
}

void Monitor::Report() {
    raise(SIGSEGV);
}

观察范围设置在 0x2000000 ~ 0x3000000,这范围是在前面本地复现坏根对象地址统计的基础确定。

css 复制代码
core-parser> space
TYPE   REGION                  ADDRESS             NAME
  5  [0x2000000, 0x42000000)  0xb400007a6920aef0  main space (region space)...
yaml 复制代码
"main" sysTid=28886 Waiting
  | group="main" daemon=0 prio=5 target=0x0 uncaught_exception=0x0
  | tid=1 sCount=0 flags=0 obj=0x72f744c0 self=0xb40000726060b380 env=0xb4000072f0615050
  | stack=0x7fe9d3d000-0x7fe9d3f000 stackSize=0x7ff000 handle=0x7478bee098
  | mutexes=0xb40000726060bb20 held=
  x0  0xb40000735061ad80  x1  0x0000000000000080  x2  0x00000000000005c3  x3  0x0000000000000000
  x4  0x0000000000000000  x5  0x0000000000000000  x6  0x0000000000000000  x7  0x000000717e9b28af
  x8  0x0000000000000062  x9  0xa1a11ca7b7440108  x10 0x0000000000000000  x11 0x0000000000000044
  x12 0x00000000afda7ad0  x13 0x0000000000000056  x14 0x0000007477865880  x15 0x000000717e7e3226
  x16 0x00000071994340e8  x17 0x000000745f360a80  x18 0x000000747885c000  x19 0xb40000735061ad70
  x20 0xb40000726060b380  x21 0x00000000000005c3  x22 0x0000000000000000  x23 0x0000007477865880
  x24 0x0000000000000001  x25 0xb40000722084c770  x26 0x0000000000000047  x27 0x000000719ab449c8
  x28 0x0000000000000000  fp  0x0000007fea536ed0
  lr  0x0000007198e137ec  sp  0x0000007fea536ec0  pc  0x000000745f360aa0  pst 0x0000000060001000
  Native: #0  000000745f360aa0  syscall+0x20
  Native: #1  0000007198e137e8  art::ConditionVariable::WaitHoldingLocks(art::Thread*)+0x8c
  Native: #2  000fee7198ca2d48  art::Monitor::Wait(art::Thread*, art::ObjPtr<art::mirror::Object>, long, int, bool, art::ThreadState)+0x18cc
  Native: #3  00549b8071e96228  art_jni_trampoline+0x58
  JavaKt: #00  0000000000000000  java.lang.Object.wait
  - waiting on <0x26cc968> (a a.b.c.e.e.w.G$x2)
  JavaKt: #01  0000007198508d94  java.lang.Object.wait
  JavaKt: #02  000000717e7e3226  a.b.c.e.e.w.G$ld6.x2
  JavaKt: #03  000000717e7e4044  a.b.c.e.e.w.G.onDetachedFromWindow
  JavaKt: #04  00000071962461e4  android.view.View.dispatchDetachedFromWindow
  JavaKt: #05  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #06  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #07  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #08  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #09  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #10  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #11  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #12  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #13  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #14  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #15  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #16  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #17  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #18  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #19  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #20  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #21  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #22  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #23  000000719620f8e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #24  0000007196225f4a  android.view.ViewRootImpl.dispatchDetachedFromWindow
  JavaKt: #25  0000007196226b48  android.view.ViewRootImpl.doDie
  JavaKt: #26  000000719621e94c  android.view.ViewRootImpl.die
  JavaKt: #27  000000719625fe62  android.view.WindowManagerGlobal.removeViewLocked
  JavaKt: #28  000000719625fdb4  android.view.WindowManagerGlobal.removeView
  JavaKt: #29  000000719626105e  android.view.WindowManagerImpl.removeViewImmediate
  JavaKt: #30  0000007197b96140  android.app.ActivityThread.handleDestroyActivity
  JavaKt: #31  0000007197cde728  android.app.servertransaction.DestroyActivityItem.execute
  JavaKt: #32  0000007197ce1784  android.app.servertransaction.TransactionExecutor.executeLifecycleItem
  JavaKt: #33  0000007197ce18c0  android.app.servertransaction.TransactionExecutor.executeTransactionItems
  JavaKt: #34  0000007197ce169a  android.app.servertransaction.TransactionExecutor.execute
  JavaKt: #35  0000007197b8bc92  android.app.ActivityThread$H.handleMessage
  JavaKt: #36  0000007196a5a7f2  android.os.Handler.dispatchMessage
  JavaKt: #37  0000007196a83a0e  android.os.Looper.loopOnce
  JavaKt: #38  0000007196a84330  android.os.Looper.loop
  JavaKt: #39  0000007197b99df0  android.app.ActivityThread.main
  JavaKt: #40  0000000000000000  java.lang.reflect.Method.invoke
  JavaKt: #41  00000071958796fe  com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run
  JavaKt: #42  000000719587e568  com.android.internal.os.ZygoteInit.main
makefile 复制代码
core-parser> rd 0x00000000026e6f98
26e6f98: 0000000070004a00  .J.p....

core-parser> space -c
ERROR: Region:[0x26e6f98, 0x26e6fc0) main space (region space) has bad object!!

可见 View 的过程中,android.view.View.dispatchDetachedFromWindow 中发生变化。

内存越界踩踏

接下来就要去锁定为什么会踩到 Java 堆内存。由于破坏的内存比较特殊,Java堆内存属于 32 位,无法使用传统的内存检测,如HWASAN、MTE等方式,并且中间含GC迁移等特殊情况,调试内存难度相当大。中间调试涉及到 BPF 采集,core-parser 注入 so 库观察配合 mprotect 后准确命中踩内存的机器码。

内存越界原因

yaml 复制代码
core-parser> bt
"main" sysTid=28703 Runnable
  | group="main" daemon=0 prio=5 target=0x0 uncaught_exception=0x0
  | tid=1 sCount=0 flags=0 obj=0x735c4d08 self=0xb400006ffd004010 env=0xb400006f3d00dd50
  | stack=0x7ff182b000-0x7ff182d000 stackSize=0x7ff000 handle=0x70d0225098
  | mutexes=0xb400006ffd0047b0 held="mutator lock"(shared held) 
  x0  0x0000000000000000  x1  0x0000000070af83b0  x2  0x00000000008f8b89  x3  0x00000000022f7008  
  x4  0x0000000000000000  x5  0x0000000000000000  x6  0x0000000000000001  x7  0x0000000027d57d8b  
  x8  0x0000000073d3317c  x9  0x0000000000000001  x10 0x0000000000000000  x11 0x0000000020000000  
  x12 0x0000000000000004  x13 0x000000000000005a  x14 0x0000007ff20255a0  x15 0x000000007361499c  
  x16 0x0000007ff20234e0  x17 0x0000000070dc60f0  x18 0x00000070cf728000  x19 0xb400006ffd004010  
  x20 0x0000000000000000  x21 0xb400006ffd0040d0  x22 0x00000000021c07f0  x23 0x00000000022f7008  
  x24 0x0000000002b33b40  x25 0x0000007ff20255c0  x26 0x0000000018300004  x27 0x0000000000000004  
  x28 0x0000007ff20255d0  fp  0x0000007ff20255c8  
  lr  0x0000000072ef6cf4  sp  0x0000007ff2025560  pc  0x0000000072ef6cf4  pst 0x0000000080001000  
  Native: #0  0000000072ef6cf4  /system/framework/arm64/boot-framework.oat+0x8facf4
  Native: #1  0000000072ef6cf0  /system/framework/arm64/boot-framework.oat+0x8facf0
  JavaKt: #00  0000006dfec2ae3e  android.widget.ImageView.onDetachedFromWindow
  JavaKt: #01  0000006d7d08ec04  B.onDetachedFromWindow
  JavaKt: #02  0000006dfeb491e4  android.view.View.dispatchDetachedFromWindow
  JavaKt: #03  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #04  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #05  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #06  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #07  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #08  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #09  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #10  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #11  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #12  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #13  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #14  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #15  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #16  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #17  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #18  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #19  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #20  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #21  0000006dfeb128e2  android.view.ViewGroup.dispatchDetachedFromWindow
  JavaKt: #22  0000006dfeb28f4a  android.view.ViewRootImpl.dispatchDetachedFromWindow
  JavaKt: #23  0000006dfeb29b48  android.view.ViewRootImpl.doDie
  JavaKt: #24  0000006dfeb2194c  android.view.ViewRootImpl.die
  JavaKt: #25  0000006dfeb62e62  android.view.WindowManagerGlobal.removeViewLocked
  JavaKt: #26  0000006dfeb62db4  android.view.WindowManagerGlobal.removeView
  JavaKt: #27  0000006dfeb6405e  android.view.WindowManagerImpl.removeViewImmediate
  JavaKt: #28  0000006e00499140  android.app.ActivityThread.handleDestroyActivity
  JavaKt: #29  0000006e005e1728  android.app.servertransaction.DestroyActivityItem.execute
  JavaKt: #30  0000006e005e4784  android.app.servertransaction.TransactionExecutor.executeLifecycleItem
  JavaKt: #31  0000006e005e48c0  android.app.servertransaction.TransactionExecutor.executeTransactionItems
  JavaKt: #32  0000006e005e469a  android.app.servertransaction.TransactionExecutor.execute
  JavaKt: #33  0000006e0048ec92  android.app.ActivityThread$H.handleMessage
  JavaKt: #34  0000006dff35d7f2  android.os.Handler.dispatchMessage
  JavaKt: #35  0000006dff386a0e  android.os.Looper.loopOnce
  JavaKt: #36  0000006dff387330  android.os.Looper.loop
  JavaKt: #37  0000006e0049cdf0  android.app.ActivityThread.main
  JavaKt: #38  0000000000000000  java.lang.reflect.Method.invoke
  JavaKt: #39  0000006dfe17c6fe  com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run
  JavaKt: #40  0000006dfe181568  com.android.internal.os.ZygoteInit.main
core-parser>
css 复制代码
core-parser> search A -o
[1] 0x22f73a0 A

core-parser> search B -o
[1] 0x22f7008 B
yaml 复制代码
core-parser> f 0
  JavaKt: #00  0000006dfec2ae3e  android.widget.ImageView.onDetachedFromWindow()
  {
      Location: /system/framework/framework.jar!classes4.dex
      art::ArtMethod: 0x7192ef08
      dex_pc_ptr: 0x6dfec2ae3e
      quick_frame: 0x7ff2025560
      frame_pc: 0x72ef6cf4
      method_header: 0x72ef6c3c

      DEX CODE:
      0x6dfec2ae38: 000c                     | move-result-object v0
      0x6dfec2ae3a: 0038 0007                | if-eqz v0, 0x6dfec2ae48 //+7
      0x6dfec2ae3e: 206e 8ada 0020           | invoke-virtual {v0, v2}, void android.view.ViewRootImpl.removeForceHdrImageView(android.view.View) // method@35546

      OAT CODE:
      0x72ef6cd0: b5224794 | cbnz x20, 0x72f3b5c0
      0x72ef6cd4: b940a401 | ldr w1, [x0, #0xa4]
      0x72ef6cd8: aa1703e2 | mov x2, x23
      0x72ef6cdc: aa0003f6 | mov x22, x0
      0x72ef6ce0: aa0103f8 | mov x24, x1
      0x72ef6ce4: b9400020 | ldr w0, [x1]
      0x72ef6ce8: f9408800 | ldr x0, [x0, #0x110]
      0x72ef6cec: f9400c1e | ldr x30, [x0, #0x18]
      0x72ef6cf0: d63f03c0 | blr x30
      0x72ef6cf4: 390e6aff | strb wzr, [x23, #0x39a]
      0x72ef6cf8: a9425ff6 | ldp x22, x23, [sp, #0x20]
      0x72ef6cfc: a9437bf8 | ldp x24, x30, [sp, #0x30]
  }
core-parser> 
ini 复制代码
x23 = 0x00000000022f7008
0x00000000022f7008 + 0x39a = 0x22f73a2

当运行 0x72ef6cf4: 390e6aff | strb wzr, [x23, #0x39a] 后,
正好擦除了 A klass_ 地址

core-parser> rd 0x22f73a0
22f73a0: 89103a34b02b4e20  .N+.4:..

也就是 89103a34b02b4e20  >>>  89103a34b0004e20
scala 复制代码
core-parser> class B -f
[0xb00f0dd8]
public class B extends android.view.TextureView {

  // Object instance fields:
    [0x0374] private java.lang.Boolean y
    [0x0370] java.util.HashSet s
...
scala 复制代码
core-parser> class android.widget.ImageView -f
[0x70ed9e18]
public class android.widget.ImageView extends android.view.View {
  // Object instance fields:
    [0x03a5] private boolean mX1
    ...
    [0x039a] private boolean mXxx

由于把 B 类对象被错误的处理成 ImageView ,而且中间的错误调用刚好也满足了内存,于是在新增的代码中出现内存越界踩踏现像。

踩踏分析

css 复制代码
core-parser> method 0xb0476378 --dex --oat
protected void B.onDetachedFromWindow() [dex_method_idx=15974]
DEX CODE:
  0x6d7d08ec04: 106f 0525 0001           | invoke-super {v1}, void android.view.View.onDetachedFromWindow() // method@1317
...
  0x6d7d08ec18: 000e                     | return-void
core-parser>
java 复制代码
core-parser> method 0xb0404368 --dex --oat
protected void C.onDetachedFromWindow() [dex_method_idx=16079]
DEX CODE:
  0x6d7d08ec04: 106f 0525 0001           | invoke-super {v1}, void android.view.View.onDetachedFromWindow() // method@1317
...
  0x6d7d08ec18: 000e                     | return-void
core-parser> 
函数名
B.onDetachedFromWindow()
C.onDetachedFromWindow() 这两个函数共用一段字节码内存。
scala 复制代码
core-parser> class C -i -f
[0xb0017e60]
public final class C extends androidx.appcompat.widget.AppCompatImageView {
...

而 C 类是继承 android.widget.ImageView 的子类的。

yaml 复制代码
core-parser> method 0xb0476378 --dex --oat -v --pc 0x6e01aa71a0
protected void B.onDetachedFromWindow() [dex_method_idx=15974]
Location      : xxx.apk!classes3.dex
CodeItem      : 0x6d7d08ebf4
Registers     : 2
Ins           : 1
Outs          : 2
Insns size    : 0xb
DEX CODE:
  0x6d7d08ec04: 106f 0525 0001           | invoke-super {v1}, void android.view.View.onDetachedFromWindow() // method@1317
...
  0x6d7d08ec18: 000e                     | return-void
OatQuickMethodHeader(0x6e01a9cf3c)
  code_offset: 0x6e01a9cf40
  code_size: 0xef00
  NterpFrameInfo
    frame_size_in_bytes: 0xd0
    core_spill_mask: 0x7ff80000 (x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, fp, lr)
    fp_spill_mask: 0xff00 (x8, x9, x10, x11, x12, x13, x14, x15)
OAT CODE:
  [0x6e01a9cf40, 0x6e01aabe40]
core-parser> 

当前该函数 B.onDetachedFromWindow 以 Nterp 方式解释运行。

Nterp解释运行

这个业务过程中 C.onDetachedFromWindow 比 B.onDetachedFromWindow 发生的更早,于是 UpdateCache(self, dex_pc_ptr, resolved_method); 该字节码 resolved_method 被缓存。

yaml 复制代码
core-parser> cs 6d7d08ec04 -w
6ffd004a80: 0000006d7d08ec04  000000007192ef08  ...}m......q....
7ff2025580: 0000006d7d08ec04  000000000000106f  ...}m...o.......
7ff20255b0: 0000006d7d08ec04  0000007ff20255d0  ...}m....U......

// self=0xb400006ffd004010

core-parser> vtor 6ffd004a80
  * VIRTUAL: 0x6ffd004a80
  * PHYSICAL: 0xb78f4a80
  * OFFSET: 0xa80
  * OR: 0x79c77faf4a80
  * MMAP: 0x0
  * OVERLAY: 0x0
[6ffd004000, 6ffd044000)  rw-  0000040000  0000040000  [anon:scudo:primary] [*]

core-parser> method 000000007192ef08
protected void android.widget.ImageView.onDetachedFromWindow() [dex_method_idx=51670]
core-parser>

C 类对象调用 onDetachedFromWindow 也是以 Nterp 方式运行的,可看到该字节码已经被缓存成 ImageView.onDetachedFromWindow 函数,于是 B 类对象在后来以 Nterp 方式运行该字节码就会进入 ImageView 的函数。

yaml 复制代码
core-parser> method 0xb0404368 --dex --oat -b -v --pc 0000006e01a9cf40
protected void C.onDetachedFromWindow() [dex_method_idx=16079]
Location      : xxx.apk!classes3.dex
CodeItem      : 0x6d7d08ebf4
Registers     : 2
Ins           : 1
Outs          : 2
Insns size    : 0xb
DEX CODE:
  0x6d7d08ec04: 106f 0525 0001           | invoke-super {v1}, void android.view.View.onDetachedFromWindow() // method@1317
...
  0x6d7d08ec18: 000e                     | return-void
OatQuickMethodHeader(0x6e01a9cf3c)
  code_offset: 0x6e01a9cf40
  code_size: 0xef00
  NterpFrameInfo
    frame_size_in_bytes: 0xd0
    core_spill_mask: 0x7ff80000 (x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, fp, lr)
    fp_spill_mask: 0xff00 (x8, x9, x10, x11, x12, x13, x14, x15)
OAT CODE:
  [0x6e01a9cf40, 0x6e01aabe40]
Binary:
b0404368: 10300004b0017e60  ffa3027f00003ecf  `~....0..>......
b0404378: 0000006d7d08ebf4  0000006e01a9cf40  ...}m...@...n...
core-parser> 

总结

B.onDetachedFromWindow 与 C.onDetachedFromWindow 函数编译优化共用了同一段字节码,触发虚拟机 Nterp 解释运行的字节码缓存机制,出现幽灵调用,因此发生内存踩踏写入脏数据问题。 BTW: 当出现软件造成的坏根问题,往往后是相关的 Java 函数带来的,因为 Java 堆内存地址比较小,基本上处于整个虚拟内存的最前边,很难是其它动态库踩踏的,因此更多是各类优化、混淆的问题,或是某特性不支持等。

如何检测风险

在开源项目 core-parser 中增加通用风险检测:(实现中...),后面大家可以检测下是否存在以下风险。

css 复制代码
core-parser> space --full-check
vbnet 复制代码
core-parser> space --full-check
ERROR: verify array: [0x21a4888]
Array Name: java.lang.Object[]
    [0] 0x27e6d40

ERROR: verify array: [0x21a5b80]
Array Name: java.lang.Object[]
    [0] 0x27e6d40

ERROR: verify array: [0x21a6880]
Array Name: java.lang.Object[]
    [0] 0x27e6d40

ERROR: verify instance: [0x21aff30]
Object Name: java.util.LinkedHashMap$Entry
    [0x08] final java.lang.Object key = 0x27e6d40
...
ERROR: Region:[0x27e6d40, 0x27e7100) main space (region space) has bad object!!
...

ERROR: verify class reuse dex_pc_ptr method
[0xb06179f0] protected void B.onDetachedFromWindow()
[0xb054e978] protected void C.onDetachedFromWindow()
相关推荐
没有了遇见2 小时前
Android ViewPager2 嵌套 RecyclerView 滑动冲突解决方案
android
咖啡の猫2 小时前
Android开发-选择按钮
android·gitee
火柴就是我3 小时前
android 以maven的方式 引入本地的aar
android
过-眼-云-烟3 小时前
新版Android Studio能打包但无法run ‘app‘,编译通过后手机中没有安装,顶部一直转圈
android·ide·android studio
hedalei4 小时前
android14 硬键盘ESC改BACK按键返回无效问题
android·android14·esc·back按键
hcgeng4 小时前
android 如何判定底部导航栏显示时 不是键盘显示
android·底部导航·导航高度
和煦的春风4 小时前
性能案例分析 | Waiting for GPU completion
android·linux
用户2018792831674 小时前
ConcurrentHashMap:用 “社区超市” 故事讲透并发的设计哲学
android
4Forsee4 小时前
【Android】View 交互的事件处理机制
android·交互