Binder 代理对象泄露问题分析

Android Framework 系列教程:yuandaimaahao.github.io/AndroidFram...

视频教程、源码、答疑服务与进入 Framework 技术交流群请联系微信 zzh0838

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

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

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

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

cpp 复制代码
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);
}

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

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

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

java 复制代码
            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);

AMS 的做法简单粗暴有效:

  • 打印错误信息
  • 如果客户端不是系统进程,直接杀掉
相关推荐
Doro再努力2 小时前
【Linux操作系统10】Makefile深度解析:从依赖推导到有效编译
android·linux·运维·服务器·编辑器·vim
Daniel李华2 小时前
echarts使用案例
android·javascript·echarts
做人不要太理性3 小时前
CANN Runtime 运行时组件深度解析:任务调度机制、存储管理策略与维测体系构建逻辑
android·运维·魔珐星云
我命由我123453 小时前
Android 广播 - 静态注册与动态注册对广播接收器实例创建的影响
android·java·开发语言·java-ee·android studio·android-studio·android runtime
朗迹 - 张伟4 小时前
Tauri2 导出 Android 详细教程
android
lpruoyu4 小时前
【Android第一行代码学习笔记】Android架构_四大组件_权限_持久化_通知_异步_服务
android·笔记·学习
独自破碎E5 小时前
【BISHI15】小红的夹吃棋
android·java·开发语言
李堇8 小时前
android滚动列表VerticalRollingTextView
android·java
lxysbly10 小时前
n64模拟器安卓版带金手指2026
android
游戏开发爱好者813 小时前
日常开发与测试的 App 测试方法、查看设备状态、实时日志、应用数据
android·ios·小程序·https·uni-app·iphone·webview