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

这篇具有很好参考价值的文章主要介绍了记一次dlopen使用问题导致Framework重启,tombstones、pmap与反汇编分析(上)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

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

  • Linux 动态链接过程中的【重定位】底层原理

  • 《程序员的自我修养:链接、装载与库》文章来源地址https://www.toymoban.com/news/detail-844287.html

到了这里,关于记一次dlopen使用问题导致Framework重启,tombstones、pmap与反汇编分析(上)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 记一次javaMetaspace导致CPU200%的排查

    insertMotionDataByWxCallBack方法并发多(其实也没多少,可能就3个?)就导致CPU200%了,本地没法复现。 看报错是:java.lang.OutOfMemoryError: Metaspace,刚开始的时候眼挫,忽略了后面的Metaspace,只看到了OutOfMemoryError,就各种找代码问题。 https://arthas.aliyun.com/doc/install-detail.html 然后发现

    2023年04月24日
    浏览(51)
  • 记一次swoole连接数太多导致的错误

    原先就有点担心这个项目正式上线会出现各种问题,所以刚上线就赶紧查看日志 果然,频繁出现错误: WARNING Server::accept_connection(): accept() failed, Error: Too many open files[24] 这个错误通常是由于操作系统限制了进程能够打开的文件句柄数量,导致当前进程无法打开更多的文件,从

    2024年02月02日
    浏览(48)
  • 记一次 .NET某工控 宇宙射线 导致程序崩溃分析

    为什么要提 宇宙射线 , 太阳耀斑 导致的程序崩溃呢?主要是昨天在知乎上看了这篇文章:莫非我遇到了传说中的bug? ,由于 rip 中的0x41变成了0x61出现了bit位翻转导致程序崩溃,截图如下: 下面的评论大多是说由于 宇宙射线 ,这个太玄乎了,说实话看到这个 传说bug 的提法

    2024年02月04日
    浏览(44)
  • 记一次 Mockito.mockStatic 泄漏导致的单元测试偶发报错排查过程

    相信用 Java 写过单元测试的读者们对 Mockito 不会陌生。至于 Mockito 是什么,为什么要用 Mockito,本文不再赘述。本文记录了一次在 Apache ShardingSphere 项目中,由 Mockito.mockStatic 使用不当导致的单元测试偶发报错排查过程。 Mockito 自 3.4.0 起新增了一个方法 Mockito.mockStatic ,支持对

    2024年02月10日
    浏览(56)
  • 记一次使用NetworkManager管理Ubuntu网络无效问题分析

    我们都知道CentOS、Redhat系列网络配置比较连贯,要么在 /etc/sysconfig/network-scripts/ifcfg-网络设备名 ,文件中编辑后,重启网络服务;要么使用 nmtui 或者 nmcli 进行配置。但是, Ubuntu变动就比较大: 早期 版本的Ubuntu,配置网络在 /etc/network/interfaces 下,后面这个文件就被遗弃了,

    2024年02月09日
    浏览(46)
  • 记一次Git未Commit直接Pull导致本地代码丢失后的挽救过程

    第一次遇到这种问题,有点紧张... 好吧,废话不多说,IDEA或者AndroidStudio进入Git Uncommiteed Changes - Unstash Changes: 在弹出的Unstash Changes对话框点View查看代码,如果代码是本地丢失的代码,那么恭喜你,又可以继续愉快的玩耍了。 不过千万要注意不用随便点到Drop,Clear按钮。 这

    2024年02月06日
    浏览(55)
  • 记一次MySQL从节点服务器宕机重启后,从节点出现主键冲突异常的处理

    MySQL 5.7 非GTID模式多线程复制。 某MySQL数据库从节点因故障宕机(因故障直接宕机,非正常关闭),重启之后发现复制状态异常,show slave的结果中Slave_SQL_Running为No,错误代码为1062 error code,从系统表performance_schema.replication_applier_status_by_worker以及error log中显示某条数据因为已

    2024年02月19日
    浏览(45)
  • 记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码

           基于公司的业务需求,在SpringCloud Gateway组件的基础上,写了一个转发服务,测试开发阶段运行正常,并实现初步使用。但三个月后,PostMan请求接口,返回异常,经排查,从日志中获取到转发响应的结果为乱码:        跟踪日志: 转发到目标接口,响应结果已乱码

    2024年02月04日
    浏览(51)
  • 记一次SPI机制导致的BUG定位【不支持:http://javax.xml.XMLConstants/property/accessExternalDTD】

    今天在生产环境启用了某个功能,结果发现有个文件上传华为云OBS失败了,报错如下: 首先看抛异常的第一条信息,org.apache.xalan.processor.TransformerFactoryImpl,这个类首先看名称,后面带了Impl,一般来说应该是某个接口的实现类,因为这个是引用的jar包里报的错,还是apache的

    2024年01月25日
    浏览(42)
  • 记一次线上mysql出错:由于docker自动拉取最新mysql镜像导致mysql容器无法启动

    我随便写写,你们随便看看 环境背景:在docker中部署mysql镜像,通过portainer管理docker容器 简单说下过程:docker里mysql的时区没有设置,导致相差8小时,通过增加TZ=Asiz/Shanghai环境变量,然后重启容器来生效。结果重启的时候始终无法启动起来,后来发现是自动升级了mysql镜像版

    2024年02月07日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包