首页 > 编程语言 >记一次dlopen使用问题导致Framework重启,tombstones、pmap与反汇编分析(上)

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

时间:2024-04-05 22:44:54浏览次数:22  
标签:... ext binder tombstones Framework x8 00000079 反汇编 so

关键词: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

标签:...,ext,binder,tombstones,Framework,x8,00000079,反汇编,so
From: https://www.cnblogs.com/kryo/p/18116327

相关文章

  • Quasar framework build if not a root path
    build:{target:{browser:['es2019','edge88','firefox78','chrome87','safari13.1'],node:'node16'},vueRouterMode:'hash',//availablevalues:......
  • Metasploit Framework 6.4 (macOS, Linux, Windows) - 开源渗透测试框架
    MetasploitFramework6.4(macOS,Linux,Windows)-开源渗透测试框架Rapid7Penetrationtesting请访问原文链接:https://sysin.org/blog/metasploit-framework-6/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org世界上最广泛使用的渗透测试框架知识就是力量,尤......
  • EntityFramework Core Scaffolding
    EntityFrameworkCodeFirst是从代码生成数据库,叫做数据迁移。EntityFrameworkDatabaseFirst是从数据库生成代码,叫做脚手架(Scaffold)。本文介绍脚手架入门。用数据库图形界面(如SQLiteStudio)生成数据库模式,插入数据等,已经发展成熟,标准化了,非常直观,即使是生手也很容易掌握。......
  • Microservice - Solution Selection for Distributed Transaction Framework
      ......
  • Radio Frameworks 启动流程
    RadioFrameworks收音机服务进程在Android中在frameworks/base/services/java/com/android/server/SystemServer.java/startOtherServices中被调用启动一、加载Radio权限配置文件SystemConfig在构造函数中加载权限配置文件时,会在多个文件目录下找权限配置文件,比如环......
  • 低版本的.netFramework项目调用高版本的.net framework的dll的解决方法
    如果你的项目引用了一个针对较高版本的.NETFramework的程序集,而你的项目当前的目标框架版本较低,你可以采取以下措施来解决这个问题:更改目标框架版本:在VisualStudio中,打开你的项目。在“解决方案资源管理器”中,右键单击项目并选择“属性”。在“应用程序”选项卡中,选......
  • Android Framework system_server进程启动
    哈喽大家我是Zzz.今天分享的blog是system_server进程的启动,环境基于aosp13_r6,话不多说直接上干货前言:system_server是系统核心服务的一个进程,这个进程包含了系统中核心Service的运行,给系统提供如:Window的管理、Activity的管理,系统的核心功能都在System_server中;在ZygoteIni......
  • 《Android Framework源码解析》全网最详尽的Android系统框架层的指南,不容错过!!
    前言在当今数字化时代,移动应用已成为我们日常生活中不可或缺的一部分。随着技术的不断进步,Android作为全球领先的移动操作系统,其市场份额和影响力持续扩大。开发者们面临着一个充满活力且竞争激烈的市场环境,用户对应用的体验和性能要求日益提高。在这样的背景下,深入了解And......
  • Django_Restful_Framework视图与路由
    视图与路由drf除了在数据序列化部分简写代码以外,还在视图中提供了简写操作。所以在django原有的django.views.View类基础上,drf封装了多个子类出来提供给我们使用。**DjangoRESTframwork**提供的视图的主要作用:控制序列化器的执行(检验、保存、转换数据)控制数据库查询的执......
  • Django_Restful_Framework
    1.Web应用模式在开发Web应用中,有两种应用模式:前后端不分离前后端分离2.api接口为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们需要找到一种大家都觉得很好的接口实现规范,而且这种规范能够让后端写的接口,用途一目了然,减少双方之间的合作成本。目前市面上大部......