1

我在 ART 上测试Dexposed,它在 art::ReferenceMapVisitor::VisitQuickFrame() 处随机崩溃。我认为 dexposed 可能会错误地处理堆栈帧:

     .extern artQuickDexposedInvokeHandler
ENTRY art_quick_dexposed_invoke_handler
    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
    str     r0, [sp, #0]           @ place proxy method at bottom of frame
    mov     r2, r9                 @ pass Thread::Current
    mov     r3, sp                 @ pass SP
    blx     artQuickDexposedInvokeHandler  @ (Method* proxy method, receiver, Thread*, SP)
    ldr     r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
    add     sp, #16                @ skip r1-r3, 4 bytes padding.
    .cfi_adjust_cfa_offset -16
    cbnz    r2, 1f                 @ success if no exception is pending
    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
    bx      lr                     @ return on success
1:
    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
    DELIVER_PENDING_EXCEPTION
END art_quick_dexposed_invoke_handler

在 hookMethodNative 中使用:

static void com_taobao_android_dexposed_DexposedBridge_hookMethodNative(JNIEnv *env, jclass, jobject java_method......) {
    ArtMethod *art_method = ArtMethod::FromReflectedMethod(soa, java_method);
    ......
    art_method->SetEntryPointFromQuickCompiledCode((void *) art_quick_dexposed_invoke_handler);
    art_method->SetAccessFlags((art_method->GetAccessFlags() & ~kAccNative));
}

函数 VisitQuickFrame 在 中art/runtime/thread.cc,在访问map.data_[1]map.RegWidth() 时崩溃,从ArtMethod::GetEntryPointFromQuickCompiledCode

private:
void VisitQuickFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
  StackReference<mirror::ArtMethod>* cur_quick_frame = GetCurrentQuickFrame();
  mirror::ArtMethod* m = cur_quick_frame->AsMirrorPtr();
  mirror::ArtMethod* old_method = m;
  visitor_(reinterpret_cast<mirror::Object**>(&m), 0 /*ignored*/, this);
  if (m != old_method) {
    cur_quick_frame->Assign(m);
  }

  // Process register map (which native and runtime methods don't have)
  if (!m->IsNative() && !m->IsRuntimeMethod() && !m->IsProxyMethod()) {
    const uint8_t* native_gc_map = m->GetNativeGcMap(sizeof(void*));
    CHECK(native_gc_map != nullptr) << PrettyMethod(m);
    const DexFile::CodeItem* code_item = m->GetCodeItem();
    DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be nullptr or how would we compile its instructions?
    NativePcOffsetToReferenceMap map(native_gc_map);
    size_t num_regs = std::min(map.RegWidth() * 8,
                               static_cast<size_t>(code_item->registers_size_));
    if (num_regs > 0) {
  ......

上面的汇编代码复制自art/runtime/arch/arm/quick_entrypoints_arm.S

    /*
     * Called by managed code that is attempting to call a method on a proxy class. On entry
     * r0 holds the proxy method and r1 holds the receiver; r2 and r3 may contain arguments. The
     * frame size of the invoked proxy method agrees with a ref and args callee save frame.
     */
     .extern artQuickProxyInvokeHandler
ENTRY art_quick_proxy_invoke_handler
    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
    str     r0, [sp, #0]           @ place proxy method at bottom of frame
    mov     r2, r9                 @ pass Thread::Current
    mov     r3, sp                 @ pass SP
    blx     artQuickProxyInvokeHandler  @ (Method* proxy method, receiver, Thread*, SP)
    ldr     r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
    add     sp, #16                @ skip r1-r3, 4 bytes padding.
    .cfi_adjust_cfa_offset -16
    cbnz    r2, 1f                 @ success if no exception is pending
    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
    bx      lr                     @ return on success
1:
    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
    DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler

它是否正确保存/恢复堆栈帧?

崩溃回溯:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/hammerhead/hammerhead:5.1.1/LMY48I/2074855:user/release-keys'
Revision: '11'
ABI: 'arm'
pid: 16711, tid: 16727, name: Binder_1  >>> joker.li.dexposeltest <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xd101755d
    r0 b487a600  r1 71db5d14  r2 71db5d14  r3 711e23a8
    r4 b3749ba8  r5 d101755c  r6 b47faa70  r7 001c1448
    r8 b3749a00  r9 c52d968f  sl b47f83e8  fp b4623ad1
    ip fffffaa8  sp b37499b8  lr 0000fff8  pc b4732696  cpsr a0070030
    d0  0000000000000000  d1  0000000000000000
    d2  0000000000000000  d3  0000000000000000
    d4  b4a23400b4a24800  d5  b4a25800b4a24c00
    d6  b4a27400aec3b400  d7  b4a2a800b4a28800
    d8  0000000000001a50  d9  0000000000000000
    d10 0000000000000000  d11 0000000000000000
    d12 0000000000000000  d13 0000000000000000
    d14 0000000000000000  d15 0000000000000000
    d16 be9b62b000000000  d17 0000000000004000
    d18 0000000000004000  d19 0000000000000000
    d20 00000000c8baf094  d21 00000000003d3dcb
    d22 70f7634070f76340  d23 0000000004352858
    d24 0000000000014d1e  d25 000000000000039d
    d26 00000000000150bb  d27 00000000000d0e49
    d28 00000000044236a1  d29 0000000000004000
    d30 0000000000000001  d31 0000000000000000
    scr 80000011

backtrace:
    #00 pc 0023d696  /system/lib/libart.so (art::ReferenceMapVisitor<art::RootCallbackVisitor>::VisitQuickFrame()+333)
    #01 pc 0023dcb9  /system/lib/libart.so (art::ReferenceMapVisitor<art::RootCallbackVisitor>::VisitFrame()+224)
    #02 pc 00231959  /system/lib/libart.so (art::StackVisitor::WalkStack(bool)+276)
    #03 pc 002336b3  /system/lib/libart.so (art::Thread::VisitRoots(void (*)(art::mirror::Object**, void*, art::RootInfo const&), void*)+994)
    #04 pc 0012db67  /system/lib/libart.so (art::gc::collector::CheckpointMarkThreadRoots::Run(art::Thread*)+126)
    #05 pc 00240455  /system/lib/libart.so (art::ThreadList::RunCheckpoint(art::Closure*)+296)
    #06 pc 0012c7b1  /system/lib/libart.so (art::gc::collector::MarkSweep::MarkRootsCheckpoint(art::Thread*, bool)+96)
    #07 pc 0013021d  /system/lib/libart.so (art::gc::collector::MarkSweep::PreCleanCards()+172)
    #08 pc 00130393  /system/lib/libart.so (art::gc::collector::MarkSweep::MarkingPhase()+126)
    #09 pc 00130479  /system/lib/libart.so (art::gc::collector::MarkSweep::RunPhases()+176)
    #10 pc 00127067  /system/lib/libart.so (art::gc::collector::GarbageCollector::Run(art::gc::GcCause, bool)+246)
    #11 pc 001460af  /system/lib/libart.so (art::gc::Heap::CollectGarbageInternal(art::gc::collector::GcType, art::gc::GcCause, bool)+1406)
    #12 pc 00201357  /system/lib/libart.so (art::VMDebug_countInstancesOfClass(_JNIEnv*, _jclass*, _jclass*, unsigned char)+294)
    #13 pc 0001a3ed  /data/dalvik-cache/arm/system@framework@boot.oat
4

1 回答 1

0

1、艺术钩子打破艺术的堆栈布局:

class ArtMethod{

  ...

  // Total size in bytes of the frame

  size_t frame_size_in_bytes_;

  ...

}

"frame_size_in_bytes 是帧中对应 Quick 帧的部分。也就是说,它是从 ArtMethod* 存储到下一个 Quick 帧的帧的大小。在内部,JNI 帧使用该地址注册,并且帧大小用于堆栈行走。” --- https://code.google.com/p/android/issues/detail?id=79204

因此,运行时在堆栈中获得了错误的 ArtMethod 指针。

2、StackVisitor::WalkStack()检查编译后的代码在dex中是否有对应的opcode。

Art hook 应该修复这些或避免调用 WalkStack()。

于 2016-05-23T05:19:36.923 回答