Binder ioctl权限
Binder ioctl 的安全边界分为设备打开、UAPI 参数拷贝、命令分派和具体操作权限。能够打开 /dev/binder 不表示能够成为 context manager、冻结进程或让任意用户指针通过校验。
本文面向已读过 SELinux与Binder 和 Binder设备打开 的读者。Binder ioctl 的“权限检查”有多层含义:设备文件能否 open、命令是否受驱动支持、用户指针能否读写、命令是否要求 SELinux/caller 条件,以及目标 proc 是否有效。本文沿 binder_ioctl 的真实 switch 逐层拆开,不把所有失败都归因于 Unix mode 或 capability。
1. 打开设备
源码文件:kernel/common/drivers/android/binder.c
binder_open 为每次文件打开创建 binder_proc,保存 filp->f_cred、进程 leader、outer/inner 锁和 todo/冻结状态。open 成功只说明获得了 Binder file context,不代表已经 mmap、加入 looper 或拥有 context manager 权限。
2. ioctl总入口
源码文件:kernel/common/drivers/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg) {
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread = binder_get_thread(proc);
switch (cmd) {
case BINDER_WRITE_READ: ...
case BINDER_SET_MAX_THREADS: ...
case BINDER_SET_CONTEXT_MGR: ...
case BINDER_FREEZE: ...
default:
ret = -EINVAL;
}
}入口先等待 user-error stop 状态、取得当前 proc/thread,再按命令分派。未知命令统一 -EINVAL;命令参数错误则在各分支中返回 -EFAULT、-EINVAL 或具体权限错误。
3. WRITE_READ
源码文件:kernel/common/drivers/android/binder.c
binder_ioctl_write_read 先 copy_from_user 读取 binder_write_read,再处理用户写 buffer 和读 buffer,最后 copy_to_user 写回 consumed 字段。任一用户指针不可访问都会在 ioctl 层失败;即使结构体地址合法,内部 transaction 的 data/object 指针仍会在后续 copy_from_user 和 offset 校验中再次检查。
4. 线程上限
BINDER_SET_MAX_THREADS 只复制一个 u32 并在 inner lock 下更新 proc->max_threads,不需要 SELinux Binder call 权限,也不会立即创建线程。空指针会触发用户复制错误;该命令影响后续 BR_SPAWN_LOOPER 条件,而不是授予进程访问其他服务的能力。
5. Context manager
BINDER_SET_CONTEXT_MGR(_EXT) 进入 binder_ioctl_set_ctx_mgr 后检查 manager 是否已存在、调用 security_binder_set_context_mgr,并比较当前 euid 与 context 记录的 manager UID。SELinux 允许也不等于 UID 条件通过;第二个 manager 返回 -EBUSY,UID 不匹配返回 -EPERM。
6. 诊断命令
BINDER_VERSION 通过 put_user 写协议版本;BINDER_GET_NODE_INFO_FOR_REF、BINDER_GET_NODE_DEBUG_INFO 都先复制输入结构,再输出结果。输出指针同样需要 copy_to_user 成功。能读 debug 信息不代表能修改节点状态。
7. 冻结控制
BINDER_FREEZE 先复制 pid/flags,锁住全局 proc 链找到目标并增加临时引用,再对目标 proc 执行冻结;目标不存在返回 -EINVAL。BINDER_GET_FROZEN_INFO 读取状态并复制回用户,权限和生命周期与普通 transaction 不同,不能用 caller UID 替代目标 proc 引用检查。
8. Error返回
驱动在返回前清除 looper_need_return,记录非中断错误日志并发出 trace_binder_ioctl_done。用户态 ioctl 的 errno 是第一层错误信息;Native Binder API 可能把它转换为 FAILED_TRANSACTION、DEAD_OBJECT 或其他 status,排查需保留原始 ioctl 日志。
9. 测试边界
源码文件:frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp
测试对 BINDER_VERSION、BINDER_WRITE_READ、BINDER_SET_MAX_THREADS 传入 null,验证 EFAULT/EINVAL 边界;对合法结构体验证成功,对未实现命令验证 unimplemented 行为。它测试的是驱动 UAPI 参数契约,不证明 SELinux policy 已允许业务服务调用。
10. SELinux边界
设备节点 open、ioctl 参数复制和 Binder transaction LSM hook 是不同层次。普通 transaction 的域权限由 security_binder_transaction 检查;context manager 有独立 set_context_mgr;ioctl 本身若被文件/LSM 拒绝,可能在进入 Binder switch 前就失败。必须结合 AVC class、ioctl cmd 和驱动日志定位。
11. 阅读检查
给定四个错误:open 失败、WRITE_READ 用户指针坏、第二个 context manager、未知 ioctl,分别指出发生层次、源码分支和 errno。再解释为什么 BINDER_SET_MAX_THREADS 成功不代表能向任意服务发起 Binder 调用。
