记一次dlopen使用问题导致Framework重启,tombstones、pmap与反汇编分析(上)
  DIBj01C8HNXr 24天前 25 0

关键词:Android Framework 动态库 动态链接 Binder

1、事件起因

Android Studio一次更新后发现install App,设备就重启了,跑了一遍开机动画但不是从开机第一屏开始重启,tombstones内容查看发现是surfaceflinger挂在libbinder.so,那install app做了什么这个不得而知,理论上有问题应该挂的是PackageManagerService。先不管Android Studio的事情,虽然挂在Binder的库里,还是首先怀疑问题出在surfaceflinger的Binder使用逻辑。

2、原因分析

(以下分析使用RockPi4B还原现场)

tombstone文件如下

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'rockchip/rk3399_ROCKPI4B_Android11/rk3399_ROCKPI4B_Android11:11/RQ3A.210705.001/eng.kryo.20240128.131540:userdebug/release-keys'
Revision: '0'
ABI: 'arm64'
Timestamp: 2024-01-28 23:42:32+0800
pid: 11263, tid: 11289, name: Binder:11263_2  >>> /system/bin/surfaceflinger <<<
uid: 1000
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x791c0e6cd0
    x0  b4000079ce312fd8  x1  000000005f444d50  x2  000000791d20c9c0  x3  000000791d20c940
    x4  0000000000000010  x5  0000000000000018  x6  b400007a7e312e50  x7  b4000079ce312fd8
    x8  000000791c0e6c50  x9  0000000010000000  x10 0000000000000001  x11 0000000000000002
    x12 0000000000000000  x13 0000007baff5e020  x14 0001096dce96e5c4  x15 0000000029aaaaf0
    x16 0000007bafd6c420  x17 0000007bafd29e30  x18 000000791c592000  x19 000000791d20c940
    x20 0000000000000010  x21 000000791d20c9c0  x22 b4000079ce312fd8  x23 000000005f444d50
    x24 000000791d20d000  x25 0000000000000000  x26 ffffffff000003e8  x27 b400007a7e313014
    x28 0000000000000000  x29 000000791d20c8d0
    lr  0000007bafd167bc  sp  000000791d20c8c0  pc  0000007bafd1685c  pst 0000000080000000

backtrace:
      #00 pc 000000000004985c  /system/lib64/libbinder.so (android::BBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+228) (BuildId: d5e42e998e9031430bee87f595521231)
      #01 pc 00000000000524a8  /system/lib64/libbinder.so (android::IPCThreadState::executeCommand(int)+1032) (BuildId: d5e42e998e9031430bee87f595521231)
      #02 pc 0000000000051fec  /system/lib64/libbinder.so (android::IPCThreadState::getAndExecuteCommand()+156) (BuildId: d5e42e998e9031430bee87f595521231)
      #03 pc 000000000005282c  /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+60) (BuildId: d5e42e998e9031430bee87f595521231)
      #04 pc 0000000000078e10  /system/lib64/libbinder.so (android::PoolThread::threadLoop()+24) (BuildId: d5e42e998e9031430bee87f595521231)
      #05 pc 000000000001567c  /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+260) (BuildId: c081ab14bd4aef44c9c459d77d8c9b48)
      #06 pc 0000000000014f14  /system/lib64/libutils.so (thread_data_t::trampoline(thread_data_t const*)+412) (BuildId: c081ab14bd4aef44c9c459d77d8c9b48)
      #07 pc 00000000000b0c08  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+64) (BuildId: 0a481e8df134382e9d3effff2fce8b74)
      #08 pc 00000000000505d0  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 0a481e8df134382e9d3effff2fce8b74)

使用addr2line工具查看代码奔溃处源码,aosp编译时会在out目录下生成symbols目录,会额外保存一份带符号的so文件方便回溯问题,把崩溃处的地址4985c传入

aarch64-linux-android-addr2line -e out/target/product/rk3399_ROCKPI4B_Android11/symbols/system/lib64/libbinder.so 4985c

#frameworks/native/libs/binder/Binder.cpp:188

定位到Binder.cpp 188行处的源码:

status_t BBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    data.setDataPosition(0);

    status_t err = NO_ERROR;
    switch (code) {
        case PING_TRANSACTION:
            err = pingBinder();
            break;
        case EXTENSION_TRANSACTION:
            err = reply->writeStrongBinder(getExtension());
            break;
        case DEBUG_PID_TRANSACTION:
            err = reply->writeInt32(getDebugPid());
            break;
        default:
            err = onTransact(code, data, reply, flags); // 188行处
            break;
    }
    //... ...
}

还是目前很难看出崩溃原因,tombstone给出的是SEGV_MAPERR错误,推测与访存有关,再结合objdump -S 查看libbinder.so的反汇编:

   ... ...

   49840:	1400001a 	b	498a8 <_ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j@@Base+0x130>
   49844:	f94002c8 	ldr	x8, [x22]
   49848:	aa1603e0 	mov	x0, x22
   4984c:	2a1703e1 	mov	w1, w23
   49850:	aa1503e2 	mov	x2, x21
   49854:	aa1303e3 	mov	x3, x19
   49858:	2a1403e4 	mov	w4, w20
   4985c:	f9404108 	ldr	x8, [x8,#128]
   49860:	d63f0100 	blr	x8
   ... ...

可以看到4985c处的汇编指令是通过x8寄存器偏移+128字节进行访存(寄存器相对寻址) x8内容0x791c0e6c50加128正好是出错内存地址0x791c0e6cd0,取得的内存结果再存回x8

x8reg

tombstone也会把进程的pmap信息打印,由于地址空间布局随机化(ASLR)机制,可能每次运行结果的地址都不一样:

memory map (836 entries): (fault address prefixed with --->)
... ...

    00000079'1afe2000-00000079'1bc05fff ---         0    c24000
    00000079'1bc06000-00000079'1bc07fff rw-         0      2000
    00000079'1bc08000-00000079'1bfe1fff ---         0    3da000
    00000079'1bfe2000-00000079'1bfe2fff ---         0      1000
    00000079'1bfe3000-00000079'1c0defff rw-         0     fc000  [anon:stack_and_tls:11290]
    00000079'1c0df000-00000079'1c0dffff ---         0      1000
--->Fault address falls at 00000079'1c0e6cd0 between mapped regions
    00000079'1c113000-00000079'1c591fff ---         0    47f000
    00000079'1c592000-00000079'1c593fff rw-         0      2000
    00000079'1c594000-00000079'1d112fff ---         0    b7f000
    00000079'1d113000-00000079'1d113fff ---         0      1000
... ...

出现错误的地址是个空洞,也就是出现空指针,通过对比汇编代码和C++代码可以得知x0x3(w0w3)正好是传递了4个参数,ldr x8, [x8,#128]准备赋值给x8寄存器onTransact函数类型的函数指针,然后blr x8跳转到x8执行子程序,说明没有找到onTransact这个函数地址。

以上分析也不好判断是前面这个x8寄存器本身出了问题,在运行过程中被意外修改,还是访问这块内存有毛病被修改了,但还是不要怀疑是binder库本身的问题,binder是系统核心功能,有问题早就挂在其他地方了。好消息是这个问题是必现的,我们有足够的试错机会。

通过重新搜寻surfaceflinger相关代码,发现MTK为其定制了一个库libsurface_ext.so,扩展了一些功能,并且实现了一个名为SurfaceExtService的binder服务添加道ServiceManager里:

extern "C" void createSurfaceExtService() {
    const sp<IServiceManager> sm = defaultServiceManager();
    if (sm == nullptr) {
        LOGE("Can't get ServiceManager");
    } else {
        sp<IBinder> binder = sm->checkService(String16(SERVICE_NAME));
        if (binder != nullptr) {
            LOGW("SurfaceExtService added");
        } else {
            sp<SurfaceExtService> sfext = new SurfaceExtService();
            sm->addService(String16(SERVICE_NAME), sfext, false);
            LOGI("SurfaceExtService add");
        }
    }
}

通过宏注释编译代码,去掉SurfaceExtService服务,重新编译push,再次通过Android Studio安装应用,surfaceflinger不出现奔溃。这下问题能够缩小到libsurface_ext.sobinder相关功能。

3、解决问题

反复看了巨久的代码,并不能找到问题,后面突发灵感,还原代码重新编译运行surfaceflinger,通过cat /proc/[pid]/maps |grep libsurface_ext命令查看surfaceflinger的内存映射,发现libsurface_ext.so并不在内存映射中。

搜索代码发现其动态加载so的位置:

SurfaceExtServiceHelper.cpp

//
// Created by kryo on 1/28/24.
//
#include <log/log.h>
#include <dlfcn.h>

void createSurfaceExtServiceProxy() {
    void* soHandle = dlopen("libsurface_ext.so", RTLD_LAZY);
    if (soHandle) {
        void (*createSurfaceExtPtr)();
        createSurfaceExtPtr = (decltype(createSurfaceExtPtr))(dlsym(soHandle, "createSurfaceExtService"));
        if (NULL == createSurfaceExtPtr) {
            dlclose(soHandle);
            soHandle = nullptr;
            ALOGE("createSurfaceExtService not found");
        } else {
            createSurfaceExtPtr();
            dlclose(soHandle); // ?
            ALOGD("createSurfaceExtPtr()");
        }
    } else {
        soHandle = nullptr;
        ALOGE("lib_surface_ext.so not found");
    }
}

代码逻辑dlopen加载libsurface_ext.so库,然后使用dlsym搜索符号createSurfaceExtService拿到函数指针并调用(看起来和Java反射有点类似),日志createSurfaceExtPtr()也成功打印。

乍一看没有问题,但是为啥maps里面并没有libsurface_ext.so,仔细一看createSurfaceExtPtr()调用后又调用了dlclose(),去掉这个调用,重新编译push,问题不再复现,之前绕了一大圈没找到bug,原来被这块给坑了。

回溯下之前的onTransact调用出错的问题,x8寄存器访问的内存所在的区间就是libsurface_ext.so加载的内存映射,后面dlclose又把so库卸载了,回头再看tombstone文件memory map段也没有这个so

这个库使用了binder,有重写BBinder::onTransact方法:

status_t BBinder::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t /*flags*/);

推测进行Binder调用时通过vtable(虚表指针偏移128字节)访问该库Binder对象找被重写的onTransact函数指针,再跳转到函数去执行,结果获取函数指针时访问的不存在,导致进程crash掉!

调用dumpsysservice list等可以触发surfaceflinger的binder调用,均能复现该问题,应该也是Android Studio安装app能复现的原因。

总结

对于代码动态添加binder功能的so库时,我们尽量保存打开的so句柄,并在所有业务结束后才能通过dlclose()关闭句柄,以防止因so库的引用计数为0时被系统从内存中卸载,且对于surfaceflinger这种永不退出的进程来说没有必要调用关闭,这个锅要甩给MTK开发。

Reference

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 24天前 0

暂无评论

推荐阅读
  co6JretT03bl   2024年03月29日   33   0   0 Android开发
  co6JretT03bl   2024年03月29日   24   0   0 Android开发
  co6JretT03bl   2024年03月22日   38   0   0 Android开发
DIBj01C8HNXr