Skip to content

binder_buffer管理

追踪 binder_buffer 从事务分配、用户释放校验到对象清理、oneway 推进和地址区间合并的完整责任链。

基于android-17.0.0_r1
AndroidBinderbinder_buffer内存管理源码阅读

binder_buffer管理 ​

binder_buffer 不是“申请一段内存然后 free”的薄包装。它同时连接三种状态:allocator 的虚拟地址区间、驱动中的 binder_transaction、以及 Parcel offsets 中已经翻译过的 Binder 对象。接收方写回 BC_FREE_BUFFER(data_ptr) 后,驱动必须先确认这个地址确实属于该进程且已经允许用户释放,再解除 transaction 关联、释放 node/ref/fd 对象,推进同一 node 的下一笔 oneway work,最后才把区间交回 binder_alloc 合并。

本文面向已经读过 binder_alloc内存分配器、Binder事务发送 和 BR事务返回 的读者。BD024 解释 free tree、页和 LRU,BD017 解释 buffer 如何被写入和挂上 transaction,BD022 解释用户态何时通过 BC_FREE_BUFFER 发起回收;本篇把这些入口接成一个单 buffer 的生命周期。

本文不重新讲 best-fit 的地址选择和页 shrinker 算法,也不把 binder_transaction_buffer_release() 当作完整对象翻译教程。重点是回答:谁拥有 buffer、何时允许用户释放、失败事务和正常事务释放有什么不同、offsets 中的对象如何反向减引用,以及 oneway 队列为什么要在 buffer 真正回收后推进。

1. 三层责任 ​

1.1 Buffer对象 ​

源码文件:kernel/common/drivers/android/binder_alloc.h

相关类型:binder_buffer

c
struct binder_buffer {
    struct list_head entry;
    struct rb_node rb_node;
    unsigned free:1;
    unsigned clear_on_free:1;
    unsigned allow_user_free:1;
    unsigned async_transaction:1;
    unsigned oneway_spam_suspect:1;
    unsigned debug_id:27;
    struct binder_transaction *transaction;
    struct binder_node *target_node;
    size_t data_size;
    size_t offsets_size;
    size_t extra_buffers_size;
    unsigned long user_data;
    int pid;
};

字段属于三个消费者:user_data、free、allow_user_free 由 allocator 负责地址和释放权,transaction、target_node 由驱动事务路径负责关联和 oneway 调度,data_size、offsets_size、extra_buffers_size 由对象释放路径确定需要扫描的范围。一个字段被清零或移动,不能推断另外两层已经完成。

1.2 生命周期图 ​

正常用户释放、事务翻译失败和 FD fixup 失败会进入对象释放并最终交回 allocator。descriptor 写入用户 read buffer 的 -EFAULT 是例外:当前分支只清理 transaction,buffer 未获得 allow_user_free,也没有立即进入 allocator free,最终由目标 proc 的 deferred release 收束。

1.3 Owner矩阵 ​

阶段主要 owner主要消费者生效时机
地址区间binder_allocfree/allocated tree分配或合并时
事务关联binder_transaction 与 buffer 互指binder_free_buf()交付、失败或用户释放时
对象引用offsets 中的对象binder_transaction_buffer_release()buffer 结束时
用户访问权allow_user_freeBC_FREE_BUFFER 校验binder_thread_read() 写完 BR 后
oneway 顺序target_node->async_todobinder_free_buf()当前 async buffer 释放后

2. 分配挂接 ​

2.1 外层入口 ​

源码文件:kernel/common/drivers/android/binder_alloc.c

相关函数:binder_alloc_new_buf()

c
if (!binder_alloc_is_mapped(alloc))
    return ERR_PTR(-ESRCH);

size = sanitized_size(data_size,
        offsets_size, extra_buffers_size);
if (unlikely(!size))
    return ERR_PTR(-EINVAL);

next = kzalloc(sizeof(*next), GFP_KERNEL);
if (!next)
    return ERR_PTR(-ENOMEM);

mutex_lock(&alloc->mutex);
buffer = binder_alloc_new_buf_locked(
        alloc, next, size, is_async);

allocator 先检查 VMA 状态和长度,再预分配一个可能用于拆分剩余区间的 binder_buffer 元数据。这里的 next 不是事务 buffer;它只有在大 free 区间被拆分时才成为剩余 free 节点,否则在锁内尾部被释放。

2.2 元数据写入 ​

源码文件:kernel/common/drivers/android/binder_alloc.c

相关函数:binder_alloc_new_buf()

c
buffer->data_size = data_size;
buffer->offsets_size = offsets_size;
buffer->extra_buffers_size = extra_buffers_size;
buffer->pid = current->tgid;
mutex_unlock(&alloc->mutex);

ret = binder_install_buffer_pages(
        alloc, buffer, size);
if (ret) {
    binder_alloc_free_buf(alloc, buffer);
    buffer = ERR_PTR(ret);
}

树状态先变成 allocated,页安装在 mutex 解锁后进行。如果页安装失败,调用 allocator free 回滚区间;因此调用者看到失败时不能假设“已经返回的 buffer 仍在 allocated tree”。

2.3 事务互指 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_transaction()

c
t->buffer = binder_alloc_new_buf(
        &target_proc->alloc,
        tr->data_size,
        tr->offsets_size,
        extra_buffers_size,
        !reply && (tr->flags & TF_ONE_WAY));
if (IS_ERR(t->buffer)) {
    t->buffer = NULL;
    return_error = BR_FAILED_REPLY;
    goto err_binder_alloc_buf_failed;
}

t->buffer->transaction = t;
t->buffer->target_node = target_node;

buffer->transaction = t 建立的是驱动内部反向索引:释放 buffer 时可以把仍指向它的 transaction 置空;t->buffer = buffer 则让失败标签和交付路径找到 allocator 对象。 target_node 不只是调试字段,异步 buffer 释放时要用它推进该 node 的 async_todo。

3. 交付门槛 ​

3.1 未交付状态 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_thread_read()

c
if (put_user(cmd,
        (uint32_t __user *)ptr)) {
    binder_cleanup_transaction(
            t, "put_user failed",
            BR_FAILED_REPLY);
    return -EFAULT;
}
ptr += sizeof(uint32_t);
if (copy_to_user(ptr, &tr, trsize)) {
    binder_cleanup_transaction(
            t, "copy_to_user failed",
            BR_FAILED_REPLY);
    return -EFAULT;
}

descriptor 写入失败时,work 已从队列取出但用户尚未获得 buffer 地址。驱动调用 binder_cleanup_transaction(),最终由 binder_free_transaction() 清空 buffer->transaction;这个分支没有调用 binder_free_buf(),也不会设置 allow_user_free。因此用户态无法回收该地址,buffer 最终由目标 proc 的 deferred release 清理,而不是在 -EFAULT 返回点立即回到 free tree。

3.2 交付后状态 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_thread_read()

c
t->buffer->allow_user_free = 1;
if (cmd != BR_REPLY &&
        !(t->flags & TF_ONE_WAY)) {
    binder_inner_proc_lock(thread->proc);
    t->to_parent = thread->transaction_stack;
    t->to_thread = thread;
    thread->transaction_stack = t;
    binder_inner_proc_unlock(thread->proc);
} else {
    binder_free_transaction(t);
}

BR 写入成功后,buffer 才允许用户用地址回收。同步请求的 transaction 对象继续留在接收线程 stack,等待服务端发送 BC_REPLY;BR_REPLY 和 oneway 的 transaction 对象可以先释放,但 buffer 仍由用户 Parcel 持有,直到 BC_FREE_BUFFER。

4. 释放校验 ​

4.1 BC入口 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_thread_write() 中 BC_FREE_BUFFER

c
case BC_FREE_BUFFER: {
    binder_uintptr_t data_ptr;
    struct binder_buffer *buffer;

    if (get_user(data_ptr,
            (binder_uintptr_t __user *)ptr))
        return -EFAULT;
    ptr += sizeof(binder_uintptr_t);

    buffer = binder_alloc_prepare_to_free(
            &proc->alloc, data_ptr);
    if (IS_ERR_OR_NULL(buffer)) {
        if (PTR_ERR(buffer) == -EPERM)
            binder_user_error(
                    "BC_FREE_BUFFER matched unreturned buffer");
        else
            binder_user_error(
                    "BC_FREE_BUFFER no match");
        break;
    }
    binder_free_buf(proc, thread, buffer, false);
    break;
}

BC 命令只携带一个用户指针。驱动不信任这个指针对应的内容,而是把它交给 allocator 的 allocated tree 做精确地址查找;找不到和“找到了但尚未允许用户释放”是两个不同错误。

4.2 一次性权限 ​

源码文件:kernel/common/drivers/android/binder_alloc.c

相关函数:binder_alloc_prepare_to_free_locked()

c
if (user_ptr < buffer->user_data)
    n = n->rb_left;
else if (user_ptr > buffer->user_data)
    n = n->rb_right;
else {
    if (!buffer->allow_user_free)
        return ERR_PTR(-EPERM);
    buffer->allow_user_free = 0;
    return buffer;
}

成功匹配会立即把 allow_user_free 清零,再返回 buffer。这个顺序防止同一进程的两个线程同时提交相同地址时都通过校验:第一个线程拿到释放权,第二个线程看到 -EPERM。真正的区间回收稍后才发生,但权限已经被消费。

4.3 错误不变量 ​

BC_FREE_BUFFER 的地址必须是 buffer->user_data 的起始地址,而不是区间中间任意地址;必须来自当前接收进程的 Binder VMA,而不是发送方地址;必须已经由 binder_thread_read() 交付。错误地址不会触发对象扫描和 allocator 合并。

5. 驱动回收 ​

5.1 断开事务 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_free_buf()

c
binder_inner_proc_lock(proc);
if (buffer->transaction) {
    buffer->transaction->buffer = NULL;
    buffer->transaction = NULL;
    has_transaction = true;
}
binder_inner_proc_unlock(proc);

释放入口先断开 buffer 与 transaction 的互指。 has_transaction 会传给 trace 和后续释放逻辑,说明这次回收是否还发现一个活动 transaction;这一步不等于释放 transaction 对象本身,transaction 可能已经由其他路径处理。

5.2 Oneway推进 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_free_buf()

c
if (buffer->async_transaction &&
        buffer->target_node) {
    struct binder_node *buf_node =
            buffer->target_node;
    struct binder_work *w;

    binder_node_inner_lock(buf_node);
    BUG_ON(!buf_node->has_async_transaction);
    BUG_ON(buf_node->proc != proc);
    w = binder_dequeue_work_head_ilocked(
            &buf_node->async_todo);
    if (!w) {
        buf_node->has_async_transaction = false;
    } else {
        binder_enqueue_work_ilocked(
                w, &proc->todo);
        binder_wakeup_proc_ilocked(proc);
    }
    binder_node_inner_unlock(buf_node);
}

同一 node 的 async buffer 释放后,驱动才从 async_todo 取下一笔 work。没有下一笔时清除 has_async_transaction;有下一笔时把它放入目标 proc 的 todo 并唤醒消费者。这个推进发生在 allocator 合并前,保证“当前 buffer 释放”是 oneway 串行化的生效点。

5.3 释放总入口 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_free_buf()

c
trace_android_vh_binder_buffer_release(
        proc, thread, buffer, has_transaction);
trace_binder_transaction_buffer_release(buffer);
binder_release_entire_buffer(
        proc, thread, buffer, is_failure);
binder_alloc_free_buf(&proc->alloc, buffer);

在这段尾部之前,函数已经断开 transaction 并推进 async work;随后固定为 tracing hook → 扫描 offsets 中的 Binder 对象 → allocator 回收区间。若把 allocator free 提前,后续 binder_alloc_copy_from_buffer() 将可能读取已经被重新分配或回收的地址。

6. 对象释放 ​

6.1 扫描范围 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_release_entire_buffer()、binder_transaction_buffer_release()

c
off_end_offset = ALIGN(
        buffer->data_size, sizeof(void *));
off_end_offset += buffer->offsets_size;

binder_transaction_buffer_release(
        proc, thread, buffer,
        off_end_offset, is_failure);

offsets 数组位于 data 对齐后的尾部,off_end_offset 只覆盖对象偏移数组,不扫描 extra_buffers_size。额外空间可能保存 SG 或安全上下文等数据,它们由各自的释放路径处理;不能把整个 allocator 区间长度当作对象偏移表长度。

6.2 目标节点 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_transaction_buffer_release()

c
if (buffer->target_node)
    binder_dec_node(
            buffer->target_node, 1, 0);

for (buffer_offset = off_start_offset;
     buffer_offset < off_end_offset;
     buffer_offset += sizeof(binder_size_t)) {
    binder_size_t object_offset;
    struct binder_object object;

    if (!binder_alloc_copy_from_buffer(
            &proc->alloc, &object_offset,
            buffer, buffer_offset,
            sizeof(object_offset)))
        object_size = binder_get_object(
            proc, NULL, buffer,
            object_offset, &object);

target_node 是 buffer 目标 node 的临时强引用,先于 offsets 对象扫描递减。随后驱动从接收进程自己的 Binder buffer 读取每个 offset,再验证对象头;对象偏移损坏时记录错误并跳过该对象,不会盲目按用户提供的长度解引用。

6.3 Binder对象 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_transaction_buffer_release()

c
switch (hdr->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
    struct flat_binder_object *fp =
            to_flat_binder_object(hdr);
    struct binder_node *node =
            binder_get_node(proc, fp->binder);
    if (node) {
        binder_dec_node(node,
                hdr->type == BINDER_TYPE_BINDER,
                0);
        binder_put_node(node);
    }
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
    struct flat_binder_object *fp =
            to_flat_binder_object(hdr);
    binder_dec_ref_for_handle(
            proc, fp->handle,
            hdr->type == BINDER_TYPE_HANDLE,
            &rdata);
} break;
}

本地 Binder 对象通过 node 指针减引用,远程 handle 对象通过当前 proc 的 descriptor 减引用。释放 buffer 不会“释放一个全局 Binder 对象”:它只撤销这次 Parcel 在目标 proc 上增加的 node/ref 边沿,最终 node/ref 是否销毁还取决于其它引用和附件。

6.4 FD数组 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_transaction_buffer_release()

c
case BINDER_TYPE_FD:
    /* 成功交付的 fd 由用户态关闭。 */
    break;

case BINDER_TYPE_FDA:
    if (is_failure)
        continue;
    /* 校验 parent 和 fd 数量后读取目标 buffer 中的 fd。 */
    for (fd_index = 0;
         fd_index < fda->num_fds;
         fd_index++) {
        binder_alloc_copy_from_buffer(
                &proc->alloc, &fd, buffer,
                offset, sizeof(fd));
        binder_deferred_fd_close(fd);
    }
    break;

成功交付后,用户态 Parcel 获得目标进程的 fd,驱动释放 buffer 时不替用户关闭这些 fd。未交付失败路径则可能没有安装目标 fd;is_failure 为真时跳过 FDA 关闭扫描,避免把尚未真正交给用户的编号当作已安装 fd。

7. 地址合并 ​

7.1 进入allocator ​

源码文件:kernel/common/drivers/android/binder_alloc.c

相关函数:binder_alloc_free_buf()、binder_free_buf_locked()

c
if (buffer->clear_on_free) {
    binder_alloc_clear_buf(alloc, buffer);
    buffer->clear_on_free = false;
}
mutex_lock(&alloc->mutex);
binder_free_buf_locked(alloc, buffer);
mutex_unlock(&alloc->mutex);

清零在 allocator mutex 外进行,随后锁内才改变树和链表。 binder_free_buf_locked() 要求 buffer 已不再关联 transaction,并把它从 allocated tree 移入 free tree;如果上层漏掉 transaction 清理,allocator 会通过 BUG_ON(buffer->transaction != NULL) 暴露不变量破坏。

7.2 相邻判断 ​

源码文件:kernel/common/drivers/android/binder_alloc.c

相关函数:binder_free_buf_locked()、binder_buffer_next()、binder_buffer_prev()

c
if (!list_is_last(&buffer->entry,
        &alloc->buffers)) {
    struct binder_buffer *next =
            binder_buffer_next(buffer);
    if (next->free) {
        rb_erase(&next->rb_node,
                &alloc->free_buffers);
        binder_delete_free_buffer(
                alloc, next);
    }
}
if (alloc->buffers.next != &buffer->entry) {
    struct binder_buffer *prev =
            binder_buffer_prev(buffer);
    if (prev->free) {
        binder_delete_free_buffer(
                alloc, buffer);
        rb_erase(&prev->rb_node,
                &alloc->free_buffers);
        buffer = prev;
    }
}

free tree 按大小排序,不能用它判断虚拟地址相邻。驱动必须通过 buffers 链表找前后邻居,先吸收 next,再吸收 prev,最后把合并后的一个节点重新插回 free tree。合并的是元数据和连续虚拟区间,不是立即释放其中所有 page。

7.3 页状态 ​

源码文件:kernel/common/drivers/android/binder_alloc.c

相关函数:binder_lru_freelist_add()、binder_delete_free_buffer()

c
binder_lru_freelist_add(
        alloc, PAGE_ALIGN(buffer->user_data),
        (buffer->user_data + buffer_size)
            & PAGE_MASK);

只有不再被活动邻居使用的页才加入 LRU。一个未对齐 buffer 的首尾页可能与相邻活动 buffer 共享,所以区间 free、free tree 合并和 page LRU 入队不能简单按同一整段地址处理。

8. 失败与取消 ​

8.1 页安装失败 ​

相关源码:

  • kernel/common/drivers/android/binder.c
  • kernel/common/drivers/android/binder_alloc.c

相关函数:binder_transaction()、binder_alloc_new_buf()

c
ret = binder_install_buffer_pages(
        alloc, buffer, size);
if (ret) {
    binder_alloc_free_buf(alloc, buffer);
    buffer = ERR_PTR(ret);
}

页安装失败发生在 buffer 已进入 allocated tree 之后,因此回滚必须走完整 allocator free;不能只 kfree(buffer),否则 free tree、buffers 链表、async 配额和已进入 LRU 的页都会失真。

8.2 事务翻译失败 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_transaction() 的 err_translate_failed 路径

c
binder_cleanup_deferred_txn_lists(
        &sgc_head, &pf_head);
binder_free_txn_fixups(t);
binder_transaction_buffer_release(
        target_proc, NULL, t->buffer,
        buffer_offset, true);
t->buffer->transaction = NULL;
binder_alloc_free_buf(
        &target_proc->alloc, t->buffer);

对象、父子 pointer、FD 或安全上下文翻译失败时,驱动只释放已经处理到 buffer_offset 的对象范围,并把 is_failure 传给 buffer release。这样未完成的 FD fixup 不会被当成已安装 fd 关闭,已经增加的 node/ref 则会被反向减回。

8.3 写入失败 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_cleanup_transaction()、binder_free_transaction()、binder_alloc_deferred_release()

c
if (t->buffer)
    t->buffer->transaction = NULL;
binder_free_txn_fixups(t);
kfree(t);

descriptor put_user()/copy_to_user() 失败会走这段 transaction 清理,但 buffer 区间仍在 allocated_buffers。目标进程释放 Binder fd 后,allocator deferred release 遍历所有残留 allocated buffer,断言 transaction 已清空,再执行对象和区间清理。这个边界说明 ioctl -EFAULT 不是一个可以重试同一 BR descriptor 的正常协议结果。

8.4 用户取消 ​

Binder buffer 没有一个独立的“用户取消 buffer”命令。同步调用被取消、目标进程死亡或 descriptor 写失败,最终都通过驱动错误路径找到 buffer 并释放;已经交付给用户态的 buffer 则必须由 Parcel 的 freeBuffer() 写 BC_FREE_BUFFER。因此“取消 transact”不等于“任意时刻都能直接释放目标 buffer”。

9. 测试边界 ​

9.1 地址和页 ​

源码文件:kernel/common/drivers/android/tests/binder_alloc_kunit.c

相关测试:binder_alloc_exhaustive_test()

c
failures = binder_alloc_test_alloc_buf(
        test, alloc, buffers,
        tc->buffer_sizes,
        tc->free_sequence);
KUNIT_EXPECT_EQ_MSG(test, failures, 0,
        "Initial allocation failed");

failures = binder_alloc_test_free_buf(
        test, alloc, buffers,
        tc->buffer_sizes,
        tc->free_sequence, end);
KUNIT_EXPECT_EQ_MSG(test, failures, 0,
        "buffers not freed correctly");

failures = binder_alloc_test_free_page(
        test, alloc);
KUNIT_EXPECT_EQ_MSG(test, failures, 0,
        "pages still installed");

测试改变 buffer 的对齐布局和释放排列,验证 allocator 层的区间、邻居、页 LRU 和最终 page free;它不验证 binder_transaction_buffer_release() 中的 Binder object、FDA 或 transaction stack 语义。

9.2 用户释放 ​

源码文件:frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp

相关测试:BinderDriverInterfaceTest.Transaction

cpp
EXPECT_EQ(BR_REPLY, br.cmd2);

bc2.cmd1 = BC_FREE_BUFFER;
bc2.arg1 = br.arg2.data.ptr.buffer;
bwr.write_buffer = (uintptr_t)&bc2;
bwr.write_size = sizeof(bc2);
binderTestIoctl(BINDER_WRITE_READ, &bwr);
EXPECT_EQ(sizeof(bc2), bwr.write_consumed);

测试先取到 reply descriptor 中的 data.ptr.buffer,再把同一个地址作为 BC_FREE_BUFFER 输入。关键断言是命令被完整消费并且后续 read 为空;它验证用户地址回收入口,不证明非法地址、重复释放和包含 Binder 对象的 buffer 都会以同样日志表现。

9.3 Oneway顺序 ​

源码文件:frameworks/native/libs/binder/tests/binderLibTest.cpp

相关测试:BinderLibTest.OnewayQueueing

测试让第一笔 oneway callback 延迟,再发送第二笔 oneway,使第二笔进入单线程 server 的 async 队列;两个 callback 都必须按预期完成。它间接覆盖 target_node->async_todo 和释放后推进的可观察结果,但不把测试结果扩大成所有线程数、所有 node 或所有 vendor hook 下的公平性保证。

10. 源码导航 ​

排查一个 buffer “无法释放”或“释放后空间没有恢复”时,可以按下面顺序阅读:

  1. 从 binder_thread_write() 确认收到的地址是否是 BC_FREE_BUFFER,以及 binder_alloc_prepare_to_free() 返回 NULL 还是 -EPERM;
  2. 看 buffer->allow_user_free 和 buffer->transaction,区分尚未交付、已交付未回收、正在释放三种状态;
  3. 进入 binder_free_buf(),确认 transaction 互指是否断开、async node 是否推进;
  4. 进入 binder_release_entire_buffer(),检查 offsets 范围和 is_failure,再检查 node/ref/FDA 反向清理;
  5. 最后看 binder_alloc_free_buf() 的 free tree 合并和页 LRU,而不是只看 kfree(buffer);
  6. 若是重复释放,回到一次性 allow_user_free 校验;若是空间不连续,检查 buffers 链表邻居是否已经合并。

源码搜索:

bash
rg -n "BC_FREE_BUFFER|binder_alloc_prepare_to_free|binder_free_buf\\(" \
  kernel/common/drivers/android/binder.c \
  kernel/common/drivers/android/binder_alloc.c

rg -n "binder_transaction_buffer_release|binder_release_entire_buffer|BINDER_TYPE_FDA" \
  kernel/common/drivers/android/binder.c

rg -n "binder_free_buf_locked|binder_delete_free_buffer|allow_user_free" \
  kernel/common/drivers/android/binder_alloc.c \
  kernel/common/drivers/android/binder_alloc.h

rg -n "Transaction|OnewayQueueing|BC_FREE_BUFFER" \
  frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp \
  frameworks/native/libs/binder/tests/binderLibTest.cpp

如果能解释“为什么 BC_FREE_BUFFER 先做一次性权限消费”“为什么对象释放必须发生在 allocator 合并之前”“为什么 oneway 下一笔 work 要等当前 buffer 释放”“为什么失败翻译只扫描到 buffer_offset”,就已经掌握了 binder_buffer 作为地址、事务和对象引用交汇点的真实生命周期。