Ahao's Technical Blog Ahao's Technical Blog
首页
  • 001.基础篇
  • 002.玩转AOSP篇
  • 003.学穿Binder篇
  • 004.基础组件篇
  • 005.系统启动过程分析
  • 006.Hal开发入门与实践
  • 007.显示系统
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

阿豪讲Framework

不积跬步无以至千里
首页
  • 001.基础篇
  • 002.玩转AOSP篇
  • 003.学穿Binder篇
  • 004.基础组件篇
  • 005.系统启动过程分析
  • 006.Hal开发入门与实践
  • 007.显示系统
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础篇

  • 玩转AOSP篇

  • 学穿Binder篇

    • 000.Binder 专题导学 —— 如何深入掌握 Binder
    • 001.学习 Binder 的预备知识
    • 002.Binder 基本原理
    • 003.Binder 程序示例之 C 语言篇
    • 004.Binder 服务注册过程情景分析之 C 语言篇
    • 005.Binder 服务获取与使用过程情景分析之C语言篇
    • 006.Android Binder 驱动框架设计与分析
    • 007.Binder 驱动情景分析之 ServiceManager 启动过程
    • 008.Binder 驱动情景分析之服务注册过程
    • 009.Binder 驱动情景分析之服务获取与使用过程
    • 010.Binder 程序示例之 C++ 篇
    • 011.Binder C++ 程序分析之主要类解析
    • 012.Binder 服务注册过程情景分析之 C++ 篇
    • 013.Binder 服务获取与使用过程情景分析之C++篇
    • 014.Binder 程序示例之 aidl-cpp 篇
    • 015.添加 Android Native 系统服务
    • 016.添加 Native 系统服务回调
    • 017.Binder 程序示例之 Java 篇
    • 018.Binder Java 层初始化
    • 019.Binder Java 层服务注册过程分析
    • 020.Binder Java 层服务获取与使用过程分析
    • 021.添加 Java 系统服务
    • 022.Android Java 系统服务框架与第三方 App 使用自定义 Java 系统服务
    • 023.添加 Java 系统服务回调
    • 024.AIDL 数据类型详解之 Java 篇
    • 025.AIDL 数据类型详解之 C++ 篇
    • 026.Java 调用 Native 服务
    • 027.Native 调用 Java Binder 服务
    • 028.AIDL 关键字 in out inout oneway 解析
    • 029.Binder 驱动 Debug 入门指南
    • 030.Binder 匿名服务源码分析
    • 031.Binder 中的 Parcel 数据结构分析(C++)
    • 032.Binder 中的 Parcel 数据结构分析(Java)
    • 033.Binder 多线程情景分析
    • 034.Binder 线程池溢出问题
    • 035.Binder 代理对象泄露问题分析
    • 036.Binder 死亡通知情景分析
    • 037.Binder 异常处理机制
    • 038.Binder 系统源码演进
    • 039.Binder 面试题汇总
    • 补充——LocalService
  • 基础组件篇

  • 系统启动过程分析

  • Hal开发入门与实践

  • 显示系统

  • Framework
  • 学穿Binder篇
阿豪
2023-08-28

035.Binder 代理对象泄露问题分析

在 Android P 以前,一些恶意 App(更多是我们自己写的屎山代码),会短时间频繁向系统进程发起 binder 回调注册。

当客户端向服务端发起回调注册时,服务端会创建 BinderProxy 对象(Java层)与 BpBinder 对象(Native层),短时间过多的对象创建,会使得内存耗尽,导致卡顿死机等问题。

那么解决的办法就是限制对象的创建,前面我们分析过了 BinderProxy 是 native 层 BpBinder 的马甲,BinderProxy 只是一套 Java 层接口,核心功能均通过 BpBinder 提供,所以我们限制 BpBinder 对象的创建即可。

这个 Bug 在 Android P 就解决了,我们使用 Android Q 的代码来看看是怎么解决的:

BpBinder* BpBinder::create(int32_t handle) {
    int32_t trackedUid = -1;
    if (sCountByUidEnabled) { //sCountByUidEnabled 标志变量为 true,才对 BpBinder 的创建过程做限制
        //拿到客户端的 Uid
        trackedUid = IPCThreadState::self()->getCallingUid();
        AutoMutex _l(sTrackingLock);
        //sTrackingMap 类型是 std::unordered_map<int32_t,uint32_t>
        //保存了每个 UID 创建的 BpBinder 数量
        uint32_t trackedValue = sTrackingMap[trackedUid];
        if (CC_UNLIKELY(trackedValue & LIMIT_REACHED_MASK)) { //没有超过 BpBinder 数量限制
            if (sBinderProxyThrottleCreate) { // false ,不进入
                return nullptr;
            }
        } else { //超过 BpBinder 数量限制
            if ((trackedValue & COUNTING_VALUE_MASK) >= sBinderProxyCountHighWatermark) {
                ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
                      getuid(), trackedUid, trackedValue);
                sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK;
                //调用注册的函数指针回调处理过多的 BpBinder 创建
                if (sLimitCallback) sLimitCallback(trackedUid);
                if (sBinderProxyThrottleCreate) {
                    ALOGI("Throttling binder proxy creates from uid %d in uid %d until binder proxy"
                          " count drops below %d",
                          trackedUid, getuid(), sBinderProxyCountLowWatermark);
                    return nullptr;
                }
            }
        }
        //记录加1
        sTrackingMap[trackedUid]++;
    }
    //创建新的
    return new BpBinder(handle, trackedUid);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

可以看出整个流程还是比较清晰简单的:

  • 用一个 map 来保存每个 uid 创建的 BpBinder 数量
  • 每次创建 BpBinder 的时候,从 map 中找出客户端 uid 创建的 BpBinder 数量
  • 如果操作限制了,打印 Log,并调用 sLimitCallback 函数指针回调来处理

BpBinder 把处理的工作通过回调的方式扔给了 Binder 服务所在的进程,我们以 AMS 为例,来看看注册的回调是怎样的:

            BinderInternal.setBinderProxyCountCallback(
                    new BinderInternal.BinderProxyLimitListener() {
                        @Override
                        public void onLimitReached(int uid) {
                            Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
                                    + Process.myUid());
                            BinderProxy.dumpProxyDebugInfo();
                            if (uid == Process.SYSTEM_UID) {
                                Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
                            } else {
                                killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid),
                                        "Too many Binders sent to SYSTEM");
                            }
                        }
                    }, mHandler);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

AMS 的做法简单粗暴有效:

  • 打印错误信息
  • 如果客户端不是系统进程,直接杀掉
034.Binder 线程池溢出问题
036.Binder 死亡通知情景分析

← 034.Binder 线程池溢出问题 036.Binder 死亡通知情景分析→

最近更新
01
如何调试 SurfaceFlinger
10-05
02
SurfaceFlinger 概述
10-05
03
HWC 接口分析
10-05
更多文章>
Theme by Vdoing | Copyright © 2020-2025 AHao Framework | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式