Skip to content

策略语法工作台

以 Android 17 的 media.tuner、文件创建、Binder、Property 和 HAL 策略为案例,建立从需求到规则、消费者和验证的源码工作台。

基于android-17.0.0_r1
AndroidSELinuxPolicyLanguageTESourceNavigationDebugging源码阅读

策略语法工作台 ​

本文是策略语言组的收束篇,面向已经读过 TE规则语法、Type与Attribute、Type Transition、TE宏库、Global Macros、M4预处理、Neverallow、Constrain约束 和 Boolean条件策略 的读者。

这不是九篇文章的缩略目录。本文把知识重新按开发任务组织:需求改变的是 type membership、operation allow、对象转换、名称映射、构建输入还是运行时 Context;一行宏展开后由哪个内核 hook 或用户空间 object manager 消费;为什么规则存在仍可能被 label、neverallow、MLS、xperm 或条件构建挡住;最后如何用源码搜索、m4、binary policy 与设备标签建立闭环。

1. 先定问题层 ​

1.1 六类变化 ​

写策略前先确定要改变哪类状态。不同语句解决的是不同问题,不能用更多 allow 代替缺失的 type、transition 或 Context 映射。

需求状态 owner首选构造运行消费者
定义新主体/客体policy symbol tabletype、attribute、typeattributepolicydb type/attribute bitmap
允许已有对象操作access vectorallow、permission setAVC、LSM hook、object manager
选择新进程/对象标签SID transitiontype_transition、transition macroexec/create hook、security_compute_sid
把名字映射到typecontexts 数据file/service/property/hwservice contextslabel lookup、ServiceManager、Property Service
按产品裁剪规则build inputm4/Soong flagm4 输出的 policy.conf
对候选allow再限缩构建或运行约束neverallow、mlsconstrain、xpermcheckpolicy/secilc、security server

1.2 选择入口 ​

1.3 生效顺序 ​

.te 文件中规则的前后顺序通常不表示运行时先后。真正顺序由 operation 决定:exec hook 先算 new SID 再检查 transition/entrypoint;文件创建先查父目录、再算标签、再查 create/associate;ServiceManager 先按名字找 target Context,再查 add/find;Property Service 先解析 property Context,再查 set 和 value type。

2. 规则生命周期 ​

2.1 构建输入 ​

Soong 将宏、class、attributes 与 .te 按固定顺序交给 m4。宏调用在 policy.conf 中变成普通规则。

源码文件:system/sepolicy/build/soong/policy.go

go
var policyConfOrder = []string{
	"flagging_macros",
	"security_classes",
	"initial_sids",
	"access_vectors",
	"global_macros",
	"neverallow_macros",
	"mls_macros",
	"mls_decl",
	"mls",
	"policy_capabilities",
	"te_macros",
	"ioctl_defines",
	"ioctl_macros",
	"nlmsg_defines",
	"nlmsg_macros",
	"attributes|*.te",
	"roles_decl",
	"roles",
	"users",
	"initial_sid_contexts",
	"fs_use",
	"genfs_contexts",
	"port_contexts",
}

2.2 编译与加载 ​

M4 输出交给 checkpolicy 生成 CIL,再由 secilc 合并为 binary policy。Kernel load 后建立 type maps、avtabs、constraints、sidtab 和 class mapping。

2.3 Access决策 ​

运行时 security server 先查询普通和 conditional TE rules,再清除不满足 constraint、role 或 type bounds 的 permission。

源码文件:kernel/common/security/selinux/ss/services.c

c
/* Attribute expansion and avkey setup omitted. */
for (node = avtab_search_node(&policydb->te_avtab, &avkey);
     node;
     node = avtab_search_node_next(node, avkey.specified)) {
	if (node->key.specified == AVTAB_ALLOWED)
		avd->allowed |= node->datum.u.data;
	else if (node->key.specified == AVTAB_AUDITALLOW)
		avd->auditallow |= node->datum.u.data;
	else if (node->key.specified == AVTAB_AUDITDENY)
		avd->auditdeny &= node->datum.u.data;
}

cond_compute_av(&policydb->te_cond_avtab, &avkey, avd, xperms);

/* Unrelated decision stages omitted. */
constraint = tclass_datum->constraints;
while (constraint) {
	if ((constraint->permissions & avd->allowed) &&
	    !constraint_expr_eval(policydb, scontext, tcontext, NULL,
				  constraint->expr))
		avd->allowed &= ~constraint->permissions;
	constraint = constraint->next;
}

Android 当前没有 policy boolean,但 conditional compatibility 仍在内核;MLS constraints 则真实参与每次相关 AV 计算。

3. MediaTuner闭环 ​

media.tuner 是理解策略语言分层的好案例:业务层通过 property 启用 service,SELinux policy 始终静态存在;init 执行带 _exec 标签的 binary 触发 domain transition;daemon 注册 Binder 服务、查找其他服务、使用 HAL 和读取 properties;neverallow 再限制执行与 socket ioctl 边界。

3.1 业务启用 ​

Service 默认 disabled,tuner.server.enable=true 时 init 只改变服务生命周期,不修改 SELinux policy。

源码文件:frameworks/av/services/tuner/mediatuner.rc

bash
# media.tuner service is not started by default unless tuner.server.enable is set
service media.tuner /system/bin/mediatuner
    class main
    group media
    user root
    ioprio rt 4
    task_profiles ProcessCapacityHigh HighPerformance
    interface aidl media.tuner
    oneshot
    disabled

on property:tuner.server.enable=true
    enable media.tuner

Property trigger 是 init 业务条件。它不会像 SELinux boolean 那样启停 conditional avtab,也不会生成 allow。

3.2 Executable标签 ​

文件路径被映射到 mediatuner_exec,为 exec transition 提供 target type。

源码文件:system/sepolicy/private/file_contexts

text
/system/bin/mediatuner	        u:object_r:mediatuner_exec:s0

源码文件:system/sepolicy/private/mediatuner.te

text
type mediatuner, domain;
type mediatuner_exec, system_file_type, exec_type, file_type;

typeattribute mediatuner coredomain;

init_daemon_domain(mediatuner)

domain 表示进程 domain,exec_type/file_type/system_file_type 描述 executable。缺少 file contexts 时 binary 可能落到普通 system file type,宏存在也不会命中 transition key。

3.3 Transition展开 ​

init_daemon_domain(mediatuner) 递归展开为执行权限、process transition、entrypoint 和自动 type transition。

源码文件:system/sepolicy/public/te_macros

text
define(`init_daemon_domain', `
domain_auto_trans(init, $1_exec, $1)
')

define(`domain_auto_trans', `
domain_trans($1,$2,$3)
type_transition $1 $2:process $3;
')

对应的 exec hook 先计算 new SID,SID 变化时再检查 source→new process transition 与 new domain→executable entrypoint。

源码文件:kernel/common/security/selinux/hooks.c

c
rc = security_transition_sid(old_crsec->sid,
			     isec->sid, SECCLASS_PROCESS, NULL,
			     &new_crsec->sid);
if (rc)
	return rc;

/* NNP/nosuid fallback and audit-data setup omitted. */
if (new_crsec->sid == old_crsec->sid) {
	rc = avc_has_perm(old_crsec->sid, isec->sid, isec->sclass,
			  FILE__EXECUTE_NO_TRANS, &ad);
} else {
	rc = avc_has_perm(old_crsec->sid, new_crsec->sid,
			  SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
	if (!rc)
		rc = avc_has_perm(new_crsec->sid, isec->sid,
				  isec->sclass, FILE__ENTRYPOINT, &ad);
}

3.4 Service名称 ​

Binder service name media.tuner 映射到 mediatuner_service。这是 ServiceManager add/find 的 target type,不是 daemon process type。

源码文件:system/sepolicy/private/service_contexts

text
media.tuner                               u:object_r:mediatuner_service:s0

源码文件:system/sepolicy/private/mediatuner.te

text
binder_use(mediatuner)
binder_call(mediatuner, appdomain)
binder_service(mediatuner)

add_service(mediatuner, mediatuner_service)
allow mediatuner tv_tuner_resource_mgr_service:service_manager find;
allow mediatuner package_native_service:service_manager find;
binder_call(mediatuner, system_server)

add_service 决定谁能注册该 service type,binder_call 决定 transaction/transfer/fd,二者不能替代。

3.5 名称检查 ​

ServiceManager 用调用 SID 与 service name 查 target Context,再执行 service_manager add/find。

源码文件:frameworks/native/cmds/servicemanager/Access.cpp

cpp
bool Access::canFind(const CallingContext& ctx,const std::string& name) {
    return actionAllowedFromLookup(ctx, name, "find");
}

bool Access::canAdd(const CallingContext& ctx, const std::string& name) {
    return actionAllowedFromLookup(ctx, name, "add");
}

bool Access::actionAllowedFromLookup(const CallingContext& sctx,
                                     const std::string& name,
                                     const char *perm) {
#ifdef __ANDROID__
    char *tctx = nullptr;
    if (selabel_lookup(getSehandle(), &tctx, name.c_str(),
                       SELABEL_CTX_ANDROID_SERVICE) != 0) {
        LOG(ERROR) << "SELinux: No match for " << name
                   << " in service_contexts.\n";
        return false;
    }

    bool allowed = actionAllowed(sctx, tctx, perm, name);
    freecon(tctx);
    return allowed;
#else
    (void)sctx;
    (void)name;
    (void)perm;
    (void)kIsVendor;

    return true;
#endif
}

名字没有映射时,新增 add allow 也不会让注册成功。

3.6 Property读取 ​

Daemon 读取两类 property,宏只展开 property file 的 getattr/open/read/map。

源码文件:system/sepolicy/private/mediatuner.te

text
get_prop(mediatuner, tuner_config_prop)
get_prop(mediatuner, tuner_server_ctl_prop)

源码文件:system/sepolicy/public/te_macros

text
define(`get_prop', `
allow $1 $2:file { getattr open read map };
')

读取 property 与设置 tuner.server.enable 是不同主体和 operation;这里没有 property_service set。

3.7 HAL角色 ​

hal_client_domain(mediatuner, hal_tv_tuner) 只建立 client attribute 与 memfd 规则,具体 service find 来自 HAL 专属策略。

源码文件:system/sepolicy/private/mediatuner.te

text
hal_client_domain(mediatuner, hal_tv_tuner)

不能从宏名推导“自动获得所有 tuner HAL 权限”;应继续搜索 hal_tv_tuner_client 的规则消费者。

3.8 负向边界 ​

Daemon 禁止执行任何 executable 而不发生 transition,并禁止 privileged socket ioctl commands。

源码文件:system/sepolicy/private/mediatuner.te

text
neverallow mediatuner { file_type fs_type }:file execute_no_trans;
neverallowxperm mediatuner domain:{ rawip_socket tcp_socket udp_socket }
    ioctl priv_sock_ioctls;

这些规则说明“能启动 daemon”并不等于 daemon 可以继续 exec 任意工具或使用任意 socket ioctl。

4. 文件操作表 ​

4.1 读取已有文件 ​

最少需要沿路径目录的 search,以及目标 file 的实际 operation。r_file_perms 是常用集合,但包含 ioctl、lock、map、watch,并不总是最小授权。

源码文件:system/sepolicy/public/global_macros

text
define(`r_file_perms', `{ getattr open read ioctl lock map watch watch_reads }')
define(`r_dir_perms', `{ open getattr read search ioctl lock watch watch_reads }')
真实需求常见规则还要检查
已知路径读取parent dir search + file open/read/getattr每级目录 label
mmap 配置file map/readshared write/execute protection
使用别人传入的 fdfile object permission + fd use不一定需要 path open
设备 ioctlioctl + allowxpermcommand allowlist

4.2 创建文件 ​

创建需要父目录、new object 和 filesystem 三层检查。create_file_perms 只针对 new object,不能替代父目录 search/add_name。

源码文件:kernel/common/security/selinux/hooks.c

c
rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR,
			  DIR__ADD_NAME | DIR__SEARCH, &ad);
if (rc)
	return rc;

rc = selinux_determine_inode_label(crsec, dir, &dentry->d_name,
					   tclass, &newsid);
if (rc)
	return rc;

rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad);
if (rc)
	return rc;

return avc_has_perm(newsid, sbsec->sid,
			    SECCLASS_FILESYSTEM,
			    FILESYSTEM__ASSOCIATE, &ad);

Android 策略通常把目录和文件规则成对书写。

源码文件:system/sepolicy/private/system_server.te

text
allow system_server system_data_file:dir create_dir_perms;
allow system_server system_data_file:notdevfile_class_set create_file_perms;
allow system_server packages_list_file:file create_file_perms;

4.3 创建后的标签 ​

如果结果 type 与预期不符,先检查 type_transition source parent:class new_type [name],再检查父目录实际 SID 与 object class。仅添加 create allow 可能让错误标签对象创建成功。

5. 进程启动表 ​

5.1 三个对象 ​

进程启动至少涉及 old domain、executable file type 和 new domain。domain_auto_trans(old, exec_type, new) 同时补齐规则,但 file context 仍负责把路径标成 exec type。

输入示例Owner
old domaininit当前 task credentials
executable typemediatuner_execinode security/xattr
new domainmediatunertransition SID 结果

5.2 两种执行结果 ​

SID 未变化时需要 execute_no_trans;SID 变化时需要 process transition 与 executable entrypoint。不要同时随意授予两条路径。

5.3 启动失败顺序 ​

  1. 路径不存在或 init service 未启用;
  2. Executable label 错误,transition key 不命中;
  3. Source 无 execute;
  4. New domain 缺 process transition;
  5. New domain 对 executable 缺 entrypoint;
  6. MLS/role/NNP/nosuid 等后续条件拒绝。

6. Binder与服务 ​

6.1 三个权限层 ​

层次规则消费者
名字发现/注册service_manager find/addServiceManager Access
Binder transactionbinder call/transferSELinux Binder hooks
FD传递fd use + inode operationBinder file-transfer hook

binder_call(client, server) 只生成后两层,不包含 service find。

源码文件:system/sepolicy/public/te_macros

text
define(`binder_call', `
allow $1 $2:binder { call transfer };
allow $2 $1:binder transfer;
allow $1 $2:fd use;
')

6.2 Binder Hook ​

源码文件:kernel/common/security/selinux/hooks.c

c
static int selinux_binder_transaction(const struct cred *from,
				      const struct cred *to)
{
	u32 mysid = current_sid();
	u32 fromsid = cred_sid(from);
	u32 tosid = cred_sid(to);
	int rc;

	if (mysid != fromsid) {
		rc = avc_has_perm(mysid, fromsid, SECCLASS_BINDER,
				  BINDER__IMPERSONATE, NULL);
		if (rc)
			return rc;
	}

	return avc_has_perm(fromsid, tosid,
			    SECCLASS_BINDER, BINDER__CALL, NULL);
}

6.3 服务独占 ​

add_service 同时生成 allow 与 owner neverallow。换 owner 不是增加第二条 add allow,而是重新设计 service type 所有权。

7. Property路径 ​

7.1 Read与Set ​

get_prop 读取共享 property area 对应 file;set_prop 额外连接 Property Service 并请求 property_service set。

源码文件:system/sepolicy/public/te_macros

text
define(`set_prop', `
unix_socket_connect($1, property, init)
allow $1 $2:property_service set;
get_prop($1, $2)
')

define(`get_prop', `
allow $1 $2:file { getattr open read map };
')

7.2 用户空间消费者 ​

Property Service 根据 property name lookup target Context,再检查 property_service set;MAC 通过后还要验证 value type。

源码文件:system/core/init/property_service.cpp

cpp
const char* target_context = nullptr;
const char* type = nullptr;
property_info_area->GetPropertyInfo(name.c_str(),
                                    &target_context, &type);

if (!CheckMacPerms(name, target_context,
                   source_context.c_str(), cr)) {
    *error = StringPrintf(
            "SELinux permission check failed "
            "(source_context=%s, target_context=%s)",
            source_context.c_str(), target_context ?: "(null)");
    return PROP_ERROR_PERMISSION_DENIED;
}

if (!CheckType(type, value)) {
    *error = StringPrintf(
            "Property type check failed, value doesn't match expected type '%s'",
            (type ?: "(null)"));
    return PROP_ERROR_INVALID_VALUE;
}

7.3 真实组合 ​

Fingerprint HAL 的 property type 在 public policy 中声明,vendor daemon 调用 set macro。

源码文件:system/sepolicy/public/property.te

text
system_public_prop(virtual_fingerprint_prop)

源码文件:system/sepolicy/vendor/hal_fingerprint_default.te

text
set_prop(hal_fingerprint_default, virtual_fingerprint_prop)

缺 property contexts name mapping、value schema 不匹配或 socket 路径失败,都不能靠重复 property_service set 修复。

8. HAL组合 ​

8.1 三种身份 ​

HAL policy 通常分为基础 attribute、client attribute 与 server attribute。具体 daemon 通过 hal_server_domain 加入 server,调用方通过 hal_client_domain 加入 client。

源码文件:system/sepolicy/vendor/hal_audio_default.te

text
type hal_audio_default, domain;
hal_server_domain(hal_audio_default, hal_audio)

type hal_audio_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_audio_default)

hal_client_domain(hal_audio_default, hal_allocator)
hal_client_domain(hal_audio_default, hal_bluetooth)

8.2 IPC与名称 ​

Audio HAL 公共策略分别建立 Binder direction、HwService/AIDL service ownership 和 ServiceManager transaction。

源码文件:system/sepolicy/private/hal_audio.te

text
binder_call(hal_audio_client, hal_audio_server)
binder_call(hal_audio_server, hal_audio_client)

hal_attribute_hwservice(hal_audio, hal_audio_hwservice)
hal_attribute_service(hal_audio, hal_audio_service)

binder_call(hal_audio_server, servicemanager)

8.3 名称映射 ​

服务实例名还必须映射到 HAL service type。

源码文件:system/sepolicy/private/service_contexts

text
android.hardware.audio.core.IConfig/default                          u:object_r:hal_audio_service:s0
android.hardware.audio.core.IModule/default                          u:object_r:hal_audio_service:s0
android.hardware.audio.effect.IFactory/default                       u:object_r:hal_audio_service:s0

8.4 硬件边界 ​

HAL client/server 标记不自动授予设备节点。Audio HAL 单独获得 audio device 权限,并通过 neverallow 保证其他 HAL server 不能直接访问。

源码文件:system/sepolicy/private/hal_audio.te

text
allow hal_audio_server audio_device:dir r_dir_perms;
allow hal_audio_server audio_device:chr_file rw_file_perms;

neverallow { halserverdomain -hal_audio_server -hal_omx_server }
    audio_device:chr_file *;

9. 不能互换 ​

9.1 语义矩阵 ​

误用为什么错误正确方向
用 allow 修复错误 labelRule target 与运行对象不匹配修 contexts/transition
用 type_transition 当授权只计算 new type补父目录/create/associate 等 allow
用 binder_call 修 find denial不含 service_manager find查 service type 与 find rule
用 get_prop 修 set denial不含 socket 与 property_service set使用 set_prop 并检查 mapping/schema
用 dontaudit 修功能失败只抑制 audit,不授予找真正缺失 permission 或标签
用 permissive 绕 neverallowNeverallow 在构建期修 allow/attribute/架构边界
用 trusted attribute 修 MLS豁免范围过大修 level 或使用 fd 路径
用业务 property 当policy booleanProperty 不改 loaded AV table构建条件或固定策略设计

9.2 条件层次 ​

10. Denial定位 ​

10.1 四个字段 ​

从 AVC 先提取 scontext、tcontext、tclass 与 denied permission。它们直接对应 TE query key,不要先运行 audit2allow 猜宏。

10.2 按Class分流 ​

tclass/permission第一检查点第二检查点
file open/read/map文件实际 label、目录 searchpermission set是否过宽/过窄
dir add_name/search父目录 label创建流程中的 new type
process transitionold/new domainexecutable type与entrypoint
binder callclient/server process SIDservice find 是否先通过
service_manager find/addservice name mappingclient/owner attribute
property_service setproperty name mappingset_prop、value schema
unix_stream_socket connecttoserver socket/process SIDsock_file write
filesystem associatenew object typefilesystem SID

10.3 约束判断 ​

Binary policy 已有 allow 但仍 denied 时,再看:

  • source/target MLS level 是否满足 private/mls;
  • permission 是否被 type bounds 或 role transition 清除;
  • ioctl command 是否还需 allowxperm;
  • 规则是否只存在于另一 build variant;
  • source/target 是否通过 attribute 展开落入 neverallow。

11. 验证阶梯 ​

11.1 跨文件搜索 ​

先用真实对象名建立 owner map。MediaTuner 的搜索应同时命中 rc、file context、service context 与 TE policy。

bash
# 从AOSP根目录执行。
rg -n 'mediatuner|media\.tuner' \
  system/sepolicy/private/mediatuner.te \
  system/sepolicy/private/file_contexts \
  system/sepolicy/private/service_contexts \
  frameworks/av/services/tuner/mediatuner.rc

输入是四个已知 ownership 文件。断言是能找到 service lifecycle、executable label、Binder service label 和 domain policy。它不能证明宏展开和最终 product policy。

11.2 M4展开 ​

bash
# 展开MediaTuner策略中的高层宏。
case "$(uname -s)-$(uname -m)" in
  Darwin-*) M4=prebuilts/build-tools/darwin-x86/bin/m4 ;;
  Linux-x86_64) M4=prebuilts/build-tools/linux-x86/bin/m4 ;;
  Linux-aarch64) M4=prebuilts/build-tools/linux-arm64/bin/m4 ;;
  *) printf '%s\n' 'unsupported host'; exit 1 ;;
esac

"$M4" --fatal-warnings \
  -D target_build_variant=user \
  -D target_full_treble=true \
  -D target_exclude_build_test=false \
  system/sepolicy/public/global_macros \
  system/sepolicy/public/neverallow_macros \
  system/sepolicy/public/te_macros \
  system/sepolicy/private/mediatuner.te | \
  sed '/^#/d;/^[[:space:]]*$/d'

关键断言是输出包含 type_transition init mediatuner_exec:process mediatuner、Binder call/transfer/fd、service add/find、property file read 和 owner neverallow。M4 成功仍不验证 type/class symbols 与 neverallow 能通过完整编译。

11.3 Binary查询 ​

bash
# 将PRODUCT替换为实际产品名。
ANDROID_SEPOLICY='out/target/product/PRODUCT/root/sepolicy'

# Process transition与entrypoint。
sesearch -T -s init -t mediatuner_exec -c process \
  "$ANDROID_SEPOLICY"
sesearch -A -s mediatuner -t mediatuner_exec -c file -p entrypoint \
  "$ANDROID_SEPOLICY"

# Service注册、Binder与property读取。
sesearch -A -s mediatuner -t mediatuner_service -c service_manager \
  "$ANDROID_SEPOLICY"
sesearch -A -s mediatuner -t system_server -c binder -p call \
  "$ANDROID_SEPOLICY"
sesearch -A -s mediatuner -t tuner_config_prop -c file -p read \
  "$ANDROID_SEPOLICY"

11.4 设备标签 ​

bash
# 服务启用后查看进程domain与executable label。
adb shell ps -AZ | grep mediatuner
adb shell ls -Z /system/bin/mediatuner

# 查看服务是否注册;命令可用性取决于产品工具集。
adb shell service list | grep 'media.tuner'

进程不存在可能是业务 property 未启用,不等于 SELinux transition 失败。应同时检查 init 状态和日志。

11.5 测试消费者 ​

Android policy tests 同时遍历普通与 conditional avtab,说明最终查询应针对 binary rules,而不是假设所有 rule 都来自单个 .te。

源码文件:system/sepolicy/tests/policy.py

python
def __InitTERules(self):
    avtabIterP = self.__libsepolwrap.init_avtab(self.__policydbP)
    if (avtabIterP == None):
        sys.exit("Failed to initialize avtab")
    self.__GetTERules(self.__policydbP, avtabIterP, self.__Rules)
    self.__libsepolwrap.destroy_avtab(avtabIterP)

    avtabIterP = self.__libsepolwrap.init_cond_avtab(self.__policydbP)
    if (avtabIterP == None):
        sys.exit("Failed to initialize conditional avtab")
    self.__GetTERules(self.__policydbP, avtabIterP, self.__Rules)
    self.__libsepolwrap.destroy_avtab(avtabIterP)

测试输入是 binary policy,断言范围是普通和 conditional allow 都进入查询集合;它不检查运行对象是否带正确 label。

12. 提交审视 ​

12.1 权限面积 ​

提交前展开每个新增宏,确认它是否附带 ioctl、map、watch、fd use、callback、neverallow 或非 Treble 分支。宏名表达意图,但审查对象是展开结果。

12.2 Attribute传播 ​

给 type 增加 attribute 前搜索所有消费者:

bash
# 同时查正向allow、neverallow、transition和宏参数。
rg -n '\b目标attribute\b' system/sepolicy \
  -g '*.te' -g 'attributes' -g 'te_macros'

Attribute membership 可能一次改变数百条规则,并让 type 进入新的 neverallow source/target set。

12.3 结束路径 ​

策略资源没有传统的“释放函数”,但修改仍有结束路径:

  • 构建失败:修语法、symbol、neverallow 或条件输入;
  • 服务未启动:回到 init/property/lifecycle;
  • 运行 denial:回到 label、AV、constraint;
  • 功能成功但权限过宽:用 neverallow、xperm 或更窄 type 收紧;
  • OTA/policy reload:确认 mapping、public API 与旧 vendor compatibility。

12.4 五个反例 ​

  1. 为了消除 denial 给 daemon 加 mlstrustedsubject;
  2. 对设备节点使用 rw_file_perms 却不审查 ioctl commands;
  3. 服务 find 失败时只增加 binder_call;
  4. 文件创建失败时只增加 new file create,忽略父目录和 associate;
  5. 源码里看到 userdebug rule,就假定 user 产品也包含。

13. 源码导航 ​

要解决的问题先看哪里再追哪里
TE规则语义system/sepolicy/**/*.tekernel/common/security/selinux/ss/services.c
Type/attribute成员public/attributes、type声明policydb type maps、neverallow消费者
Exec transitionexecutable contexts、transition macroselinux_bprm_creds_for_exec
File create/labelparent type、type_transitionmay_create、security_compute_sid
Binder transactionbinder_callBinder LSM hooks
Service add/findservice_contexts、service typeServiceManager Access.cpp
Property set/getproperty type、macroproperty_service.cpp
HAL client/serverHAL attributes、service/hwservice typeBinder/HwBinder service mapping
构建条件te_macros、flagging macrosSoong policy.go
Neverallow冲突展开后的 conf、attribute memberscheckpolicy、sepolicy-analyze
MLS denialsource/target levels、private/mlsconstraint evaluator
Boolean疑问final bool tableselinuxfs/conditional kernel code

面对一个新的 Android SELinux 修改,最短可靠路径是:先找到运行入口和对象 owner,确认实际 Context;再选择对应的声明、mapping、transition 或 allow;展开宏并构建最终策略;用 binary query 验证 rule;最后由设备上的进程、文件、服务或 property 行为确认消费者。任何一步缺失,策略都可能“源码看起来正确,运行仍然失败”。

media.tuner 的完整链正好覆盖这套方法:Property 只启用 init service;file contexts 提供 executable type;transition macro 产生 mediatuner domain;service contexts 提供 Binder service type;add_service、binder_call、HAL client 和 get_prop 分别服务不同 operation;neverallow/xperm 定义不可越过的边界。能够独立从这条链替换成另一个真实 daemon,并逐层找到相同角色的源码文件,就已经具备继续进入 Android contexts 与具体 domain 专题的基础。