Skip to content

Binder对象编码

解析 Binder 对象公共 header、flat_binder_object 的 union 与 flags,并追踪本地实体、远程句柄和文件描述符的驱动转换与校验。

基于android-17.0.0_r1
AndroidBinderflat_binder_objectParcel源码阅读

Binder对象编码 ​

flat_binder_object 不是任意 Binder 元数据的总称。UAPI 还定义 binder_fd_object、 binder_buffer_object 和 binder_fd_array_object:它们共享 binder_object_header,但字段布局、 对象大小和驱动 translator 都不同。

本文面向已经读过 Binder数据转换链 的读者,专门回答 type 如何 决定 union 字段、Parcel 如何记录对象 offset、驱动如何改写 Binder/handle/fd,以及接收端如何 恢复本地对象或代理。本文不展开 transaction_data、allocator 页算法和 AIDL 编码。

1. 对象类型 ​

公共header ​

源码文件:kernel/common/include/uapi/linux/android/binder.h

c
struct binder_object_header {
    __u32 type;
};

#define B_PACK_CHARS(c1, c2, c3, c4) \
    ((((c1) << 24)) | (((c2) << 16)) | \
     (((c3) << 8)) | (c4))
#define B_TYPE_LARGE 0x85

enum {
    BINDER_TYPE_BINDER =
            B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
    BINDER_TYPE_WEAK_BINDER =
            B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
    BINDER_TYPE_HANDLE =
            B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
    BINDER_TYPE_WEAK_HANDLE =
            B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
    BINDER_TYPE_FD =
            B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
    BINDER_TYPE_FDA =
            B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE),
    BINDER_TYPE_PTR =
            B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE),
};

type 是协议标签,驱动用它选择实际结构大小和转换函数。strong/weak、binder/handle 等语义编码 在常量中,但不能只靠名字决定对象 owner,还要结合当前进程和 transaction 方向。

结构分流 ​

type结构主要字段驱动处理
BINDER/WEAK_BINDERflat_binder_objectbinder、cookietranslate_binder
HANDLE/WEAK_HANDLEflat_binder_objecthandletranslate_handle
FDbinder_fd_objectfd、cookietranslate_fd
PTRbinder_buffer_objectbuffer、length、parentbuffer fixup
FDAbinder_fd_array_objectnum_fds、parenttranslate_fd_array

2. flat布局 ​

union解释 ​

源码文件:kernel/common/include/uapi/linux/android/binder.h

c
struct flat_binder_object {
    struct binder_object_header hdr;
    __u32 flags;

    union {
        binder_uintptr_t binder;
        __u32 handle;
    };

    binder_uintptr_t cookie;
};
typeunion 有效字段cookie
BINDER/WEAK_BINDER本地 weak-ref 指针本地 BBinder 指针
HANDLE/WEAK_HANDLE当前进程 handle0

cookie 不能作为跨进程地址使用。只有 node 所属进程会收到驱动恢复后的 ptr/cookie;其他进程 看到的是各自 binder_ref 的 handle。

扩展结构 ​

源码文件:kernel/common/include/uapi/linux/android/binder.h

c
struct binder_fd_object {
    struct binder_object_header hdr;
    __u32 pad_flags;
    union {
        binder_uintptr_t pad_binder;
        __u32 fd;
    };
    binder_uintptr_t cookie;
};

struct binder_buffer_object {
    struct binder_object_header hdr;
    __u32 flags;
    binder_uintptr_t buffer;
    binder_size_t length;
    binder_size_t parent;
    binder_size_t parent_offset;
};

struct binder_fd_array_object {
    struct binder_object_header hdr;
    __u32 pad;
    binder_size_t num_fds;
    binder_size_t parent;
    binder_size_t parent_offset;
};

FD 布局为兼容旧用户空间保留 padding;PTR/FDA 描述 scatter-gather 关系。它们不是 flat object union 的额外分支。

3. flags字段 ​

节点属性 ​

源码文件:kernel/common/include/uapi/linux/android/binder.h

c
enum flat_binder_object_flags {
    FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff,
    FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100,
    FLAT_BINDER_FLAG_SCHED_POLICY_MASK =
            3U << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT,
    FLAT_BINDER_FLAG_INHERIT_RT = 0x800,
    FLAT_BINDER_FLAG_TXN_SECURITY_CTX = 0x1000,
};

flat flags 描述 node 的 fd、调度和安全上下文属性,不是 transaction flags。TF_ONE_WAY、 TF_STATUS_CODE 等属于 binder_transaction_data.flags。

Node生效 ​

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

相关函数:binder_init_node_ilocked()

c
priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
node->sched_policy =
        (flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK)
        >> FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT;
node->min_priority =
        to_kernel_prio(node->sched_policy, priority);
node->accept_fds =
        !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
node->inherit_rt =
        !!(flags & FLAT_BINDER_FLAG_INHERIT_RT);
node->txn_security_ctx =
        !!(flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX);

这些位在 node 创建时成为内核状态,影响后续事务;不是服务方法每次动态读取的业务参数。

4. Parcel编码 ​

本地与远程 ​

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

相关函数:flattenBinder()

cpp
if (binder != nullptr) {
    if (BBinder* local = binder->localBinder()) {
        obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
        if (local->isRequestingSid())
            obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
        if (local->isInheritRt())
            obj.flags |= FLAT_BINDER_FLAG_INHERIT_RT;
        obj.hdr.type = BINDER_TYPE_BINDER;
        obj.binder = reinterpret_cast<uintptr_t>(
                local->getWeakRefs());
        obj.cookie = reinterpret_cast<uintptr_t>(local);
    } else {
        BpBinder* proxy = binder->remoteBinder();
        obj.hdr.type = BINDER_TYPE_HANDLE;
        obj.binder = 0;
        obj.handle =
                proxy->getPrivateAccessor().binderHandle();
        obj.cookie = 0;
    }
}

本地实体编码 ptr/cookie,远程代理编码 handle。Parcel 不复制 C++ 对象,只写驱动可转换的引用。

fd编码 ​

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

cpp
flat_binder_object object;
object.hdr.type = BINDER_TYPE_FD;
object.flags = 0;
object.binder = 0;
object.handle = fd;
object.cookie = takeOwnership ? 1 : 0;
return writeObject(object, true);

Parcel 为兼容布局使用 flat_binder_object 临时变量写 fd object;驱动仍按 binder_fd_object 解释。 cookie 在用户态表示 fd ownership,不是服务端对象指针。

offsets索引 ​

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

相关函数:writeObject()

cpp
*reinterpret_cast<flat_binder_object*>(
        mData + mDataPos) = value;

if (value.hdr.type == BINDER_TYPE_FD) {
    if (!mAllowFds)
        return FDS_NOT_ALLOWED;
    fields->mHasFds = fields->mFdsKnown = true;
}

if (nullMetaData || value.binder != 0) {
    fields->mObjects[fields->mObjectsSize] =
            mDataPos;
    acquire_object(ProcessState::self(),
            value, this, true);
    fields->mObjectsSize++;
}

对象本体进入 data,起始位置进入 objects offsets。没有 offset 的同型字节不会被驱动识别为对象。

5. Parcel持有 ​

Acquire ​

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

相关函数:acquire_object()

cpp
case BINDER_TYPE_BINDER:
    if (obj.binder)
        reinterpret_cast<IBinder*>(
                obj.cookie)->incStrong(who);
    return;

case BINDER_TYPE_HANDLE: {
    const sp<IBinder> binder =
            proc->getStrongProxyForHandle(obj.handle);
    if (binder != nullptr)
        binder->incStrong(who);
    return;
}

case BINDER_TYPE_FD:
    if (tagFds && obj.cookie != 0)
        FdTag(obj.handle, nullptr, who);
    return;

Parcel 对本地对象、远程代理和 owned fd 建立不同的持有关系。release_object 反向递减引用或 关闭 owned fd。对象元数据不仅影响传输,也影响发送方 Parcel 自身生命周期。

6. 驱动识别 ​

大小校验 ​

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

相关函数:binder_get_object()

c
read_size = min_t(size_t,
        sizeof(*object),
        buffer->data_size - offset);

if (offset > buffer->data_size ||
        read_size < sizeof(*hdr) ||
        !IS_ALIGNED(offset, sizeof(u32)))
    return 0;

switch (object->hdr.type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER:
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE:
    object_size = sizeof(struct flat_binder_object);
    break;
case BINDER_TYPE_FD:
    object_size = sizeof(struct binder_fd_object);
    break;
case BINDER_TYPE_PTR:
    object_size = sizeof(struct binder_buffer_object);
    break;
case BINDER_TYPE_FDA:
    object_size = sizeof(struct binder_fd_array_object);
    break;
default:
    return 0;
}

驱动先验证 offset、对齐、header 和完整结构大小,再执行 translator。未知 type 或截断对象在 业务服务执行前失败。

Binder转handle ​

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

相关函数:binder_translate_binder()

c
node = binder_get_node(proc, fp->binder);
if (!node)
    node = binder_new_node(proc, fp);

if (fp->cookie != node->cookie)
    return -EINVAL;

ret = binder_inc_ref_for_node(
        target_proc, node,
        fp->hdr.type == BINDER_TYPE_BINDER,
        &thread->todo, &rdata);

fp->hdr.type = fp->hdr.type == BINDER_TYPE_BINDER
        ? BINDER_TYPE_HANDLE
        : BINDER_TYPE_WEAK_HANDLE;
fp->binder = 0;
fp->handle = rdata.desc;
fp->cookie = 0;

驱动用 ptr 查 node,并校验 cookie;随后在目标 proc 创建或增加 ref,把对象改写成目标进程 handle。

Handle转对象 ​

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

相关函数:binder_translate_handle()

c
node = binder_get_node_from_ref(
        proc, fp->handle,
        fp->hdr.type == BINDER_TYPE_HANDLE,
        &src_rdata);
if (!node)
    return -EINVAL;

if (node->proc == target_proc) {
    fp->hdr.type =
            fp->hdr.type == BINDER_TYPE_HANDLE
            ? BINDER_TYPE_BINDER
            : BINDER_TYPE_WEAK_BINDER;
    fp->binder = node->ptr;
    fp->cookie = node->cookie;
} else {
    binder_inc_ref_for_node(
            target_proc, node,
            fp->hdr.type == BINDER_TYPE_HANDLE,
            NULL, &dest_rdata);
    fp->binder = 0;
    fp->handle = dest_rdata.desc;
    fp->cookie = 0;
}

目标正是 node owner 时恢复 ptr/cookie;否则生成目标 proc 自己的 handle。相同 node 在不同进程 中的 handle 不要求相同。

7. 其他对象 ​

fd转换 ​

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

c
case BINDER_TYPE_FD: {
    struct binder_fd_object* fdObject =
            to_binder_fd_object(hdr);
    binder_size_t fdOffset =
            object_offset +
            (uintptr_t)&fdObject->fd -
            (uintptr_t)fdObject;

    ret = binder_translate_fd(
            fdObject->fd, fdOffset,
            transaction, thread,
            in_reply_to);
    fdObject->pad_binder = 0;
    break;
}

fd 整数不能原样跨进程。驱动保存 struct file,并在目标 task 上下文安装新 fd;目标 fd 数字可能 不同。

PTR与FDA ​

PTR 表示额外 buffer,驱动复制其内容并把 buffer 地址修正为目标用户地址;HAS_PARENT 要求修正 父 buffer 内部的指针。FDA 指向 parent buffer 中的 fd 数组,驱动验证 parent、offset 和长度后 逐个安装 fd。两者的失败回滚比 flat object 更复杂。

8. 接收恢复 ​

unflatten ​

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

相关函数:unflattenBinder()

cpp
const flat_binder_object* flat =
        readObject(false);
if (!flat)
    return BAD_TYPE;

switch (flat->hdr.type) {
case BINDER_TYPE_BINDER:
    return finishUnflattenBinder(
            sp<IBinder>::fromExisting(
                    reinterpret_cast<IBinder*>(
                            flat->cookie)), out);
case BINDER_TYPE_HANDLE:
    return finishUnflattenBinder(
            ProcessState::self()
                    ->getStrongProxyForHandle(
                            flat->handle), out);
default:
    return BAD_TYPE;
}

接收端读取的是驱动转换后的 type。本地 ptr/cookie 恢复原对象;handle 进入 ProcessState 代理缓存。

外部视图 ​

ipcSetDataReference 还会验证 offsets 单调递增,并只接受用户态支持的 BINDER、HANDLE 和 FD 类型。 PTR/FDA 由驱动处理其嵌套数据,但普通 libbinder Parcel 不把它们暴露成可直接 readObject 的对象。

9. 失败回滚 ​

Cookie与权限 ​

cookie 不匹配返回 EINVAL;security_binder_transfer_binder 拒绝时返回 EPERM;无效 handle 也会 失败。三者都发生在服务实现执行前。

已处理范围 ​

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

相关函数:binder_transaction_buffer_release()

c
for (buffer_offset = off_start_offset;
        buffer_offset < off_end_offset;
        buffer_offset += sizeof(binder_size_t)) {
    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);

    switch (object.hdr.type) {
    case BINDER_TYPE_BINDER:
    case BINDER_TYPE_WEAK_BINDER:
        binder_dec_node(node,
                object.hdr.type ==
                BINDER_TYPE_BINDER, 0);
        break;
    case BINDER_TYPE_HANDLE:
    case BINDER_TYPE_WEAK_HANDLE:
        binder_dec_ref_for_handle(
                proc, object.fbo.handle,
                object.hdr.type ==
                BINDER_TYPE_HANDLE,
                &rdata);
        break;
    }
}

驱动只回滚已成功处理的 offsets 范围,避免释放尚未取得所有权的对象。

10. 测试输入 ​

offsets边界 ​

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

相关测试:AppendWithBinderPartial()、AppendFromPartialObjectList()、 WriteObjectUnsortedThenValidate()、AppendOverObject()

测试覆盖四种输入:只复制 Binder 对象的一部分、只复制对象列表中的一个完整对象、故意写出失序 offsets,以及尝试覆盖 fd object 区域。断言分别检查 object count、读回对象和 PERMISSION_DENIED。 它们证明 data 与 offsets 必须一起维护。

非法PTR ​

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

相关测试:BinderLibTest.BufRejected()

cpp
binder_buffer_object object {
    .hdr = { .type = BINDER_TYPE_PTR },
    .flags = 0,
    .buffer = reinterpret_cast<
            binder_uintptr_t>(&buffer),
    .length = 4,
};

data.writeFileDescriptor(0);
memcpy(const_cast<uint8_t*>(
        data.data()), &object,
        sizeof(object));
data.setDataSize(sizeof(object));

EXPECT_THAT(server->transact(
        BINDER_LIB_TEST_REJECT_OBJECTS,
        data, &reply),
        Not(StatusEq(NO_ERROR)));

测试借 fd object 建立合法 offset,再把 data 覆盖成未经合法 parent/SG 构造的 PTR object,断言 事务不能成功。type 正确并不足以证明对象合法。

11. 阅读边界 ​

类型分流图 ​

flat_binder_object.hdr.type 决定驱动后续进入哪条转换函数;flags 只补充 node 行为,不能把一种对象伪装成另一种对象。

读者应先从 offsets 找对象,再按 type 选择转换函数,最后检查目标资源和失败回滚;不能只看 Parcel 中的原始 handle 或 cookie 数值。

本文证明了 header、type、flat union、flags、Parcel offsets、node/ref/handle/fd/PTR/FDA 转换、 接收恢复和失败回滚。没有展开 transaction_data 全字段、allocator、AIDL 序列化、死亡通知、 线程池、RPC Binder 和 SELinux。

下一篇 BD008 将深入 binder_transaction_data。复查对象问题时,沿 offset、type、object size、translator、target rewrite、cleanup 的顺序走源码。