Type与Attribute
本文面向已经读过 类型强制 和 TE规则语法 的读者。前文说明 allow 可以使用 type 或 attribute,本篇专门回答集合从何而来:type foo, domain, file_type; 与 typeattribute foo domain; 如何建立成员关系,attribute 与普通 type 在 policydb 中怎样区分,expandattribute 控制什么,新增 public type/attribute 为什么可能破坏 vendor policy API。
本文不把 attribute 描述成“面向对象继承”。一个 type 加入 attribute 后,会参与所有以该 attribute 为 source/target 的规则和 neverallow;它不会复制一份规则文本,也不存在单一父类。读完后,你应能从一个具体 type 追出全部 attribute,从一条 attribute allow 找到受影响成员,并判断修改应放在 public、private 还是 device/vendor policy。
1. 四种声明
| 声明 | 作用 | owner | 常见风险 |
|---|---|---|---|
type name; | 创建具体 type 符号 | policy symbol table | 只声明未标注对象、未关联职责 |
attribute name; | 创建 type 集合符号 | policy symbol table | 过宽集合成为隐式权限传播面 |
type name, attr1, attr2; | 声明时加入多个 attribute | compiler/CIL | 新 type 自动获得 attribute 规则 |
typeattribute name attr; | 声明后追加成员关系 | compiler/CIL | 跨文件关联难以从单文件发现 |
expandattribute attr bool; | 控制 CIL/compiler 展开策略 | compiler | 工具看到的最终 attribute 结构变化 |
这几种声明都在构建期处理。运行期 AVC 不解析 typeattribute 文本,而是消费 policydb 中的 type/attribute 编号与成员位图。
2. Attribute体系
2.1 基础集合
Android 平台在 public/attributes 中声明跨模块可见的职责集合。
源码文件:system/sepolicy/public/attributes
attribute dev_type;
attribute bpffs_type;
attribute domain;
attribute fs_type;
attribute contextmount_type;
attribute fusefs_type;
attribute file_type;
attribute exec_type;
attribute data_file_type;
attribute core_data_file_type;
attribute app_data_file_type;
attribute system_file_type;
attribute vendor_file_type;
attribute proc_type;
attribute sysfs_type;
attribute property_type;
attribute service_manager_type;这些不是严格的树。一个 type 可以同时属于 file_type、data_file_type、app_data_file_type,也可以同时属于某个服务专属 attribute。成员关系构成多维集合,而不是唯一父节点。
2.2 职责维度
attribute 常按对象形态、分区归属、服务职责和访问 API 四个维度组织:
| 维度 | 示例 | 消费者 |
|---|---|---|
| 对象形态 | domain、file_type、dev_type | 通用规则、contexts tests |
| 分区归属 | system_file_type、vendor_file_type | Treble/分区测试 |
| 数据用途 | app_data_file_type、core_data_file_type | app 隔离与 neverallow |
| 服务API | service_manager_type、system_server_service | servicemanager 与客户端规则 |
同一 type 的所有 attribute 都会进入策略查询,所以把 vendor file type 错误加入 system file attribute 不只是分类错误,还可能获得 platform 范围规则并触发 Treble 测试。
2.3 expandattribute
部分 attribute 显式声明是否展开:
attribute data_file_type;
expandattribute data_file_type false;
attribute core_data_file_type;
expandattribute core_data_file_type false;
attribute app_data_file_type;
expandattribute app_data_file_type false;
attribute proc_type;
expandattribute proc_type false;
attribute proc_net_type;
expandattribute proc_net_type true;expandattribute 控制 compiler/CIL 如何保留或展开 attribute,并影响 neverallow、mapping 与分析工具能否直接查询集合。它不改变源码成员关系,也不能用来决定“规则是否生效”。要确认最终结果,应查看生成 CIL 或用 policy 查询工具。
3. Type声明
3.1 文件type
声明时可直接附加多个 attribute。例如可执行文件 type 通常同时属于 file_type、分区 type 和 exec_type。
源码文件:system/sepolicy/private/traced_perf.te
type traced_perf_exec,
system_file_type,
exec_type,
file_type;这条声明让 traced_perf_exec 同时参与所有针对 system files、entrypoint files 和 labeled files 的规则/neverallow。它并未声明 traced_perf 进程 domain;进程 domain 和 executable type 是不同 type。
3.2 进程domain
具体 domain 常在公共或私有 type 文件中声明,再在业务 .te 文件中追加职责 attribute。
源码文件:system/sepolicy/private/system_server.te
typeattribute system_server coredomain;
typeattribute system_server mlstrustedsubject;
typeattribute system_server remote_provisioning_service_server;
typeattribute system_server scheduler_service_server;
typeattribute system_server sensor_service_server;
typeattribute system_server stats_service_server;
typeattribute system_server bpfdomain;system_server 的基础 type 已在其他输入中存在,这里只追加职责。任一 attribute 都可能带来多条 allow/neverallow;新增关联前必须搜索 attribute 的全部消费者。
3.3 coredomain案例
servicemanager 只追加 coredomain,其 domain transition、Binder、文件与属性权限由其他宏和规则描述。
源码文件:system/sepolicy/private/servicemanager.te
typeattribute servicemanager coredomain;
init_daemon_domain(servicemanager)
read_runtime_log_tags(servicemanager)
set_prop(servicemanager, ctl_interface_start_prop)
set_prop(servicemanager, servicemanager_prop)attribute 不是“服务的全部能力”。coredomain 只表达 platform/vendor 边界职责,具体 Binder 与 property 权限仍来自宏展开和显式 allow。
4. 关联方式
4.1 声明时关联
声明时关联适合 type 与基础对象形态不可分的场景,例如 executable 必须是 file type。它让定义集中,但要求 attribute 已提前声明。
type traced_perf_exec,
system_file_type,
exec_type,
file_type;4.2 延迟关联
typeattribute 适合职责在另一个策略层追加,或由宏统一生成。它可以跨文件扩展同一个 type,因此只读声明文件无法获得完整成员列表。
typeattribute system_server coredomain;
typeattribute system_server bpfdomain;两种写法进入最终成员图后的访问语义相同;差异在代码组织、可见性和 public/private 接口所有权。
4.3 宏生成
宏可以同时生成 attribute 声明、typeattribute 和规则。阅读宏调用必须回到宏定义。
源码文件:system/sepolicy/public/te_macros
define(`pdx_service_attributes', `
attribute pdx_$1_endpoint_dir_type;
attribute pdx_$1_endpoint_socket_type;
attribute pdx_$1_channel_socket_type;
attribute pdx_$1_server_type;
')
define(`pdx_server', `
typeattribute $1 pdx_$2_server_type;
allow init pdx_$2_endpoint_socket_type:unix_stream_socket {
create bind
};
neverallow { domain -$1 }
pdx_$2_endpoint_socket_type:unix_stream_socket {
listen accept
};
')一个宏调用既改变成员关系又生成 allow/neverallow。只搜索 typeattribute 调用点会漏掉宏间接关联。
5. 内核表示
5.1 type_datum
policydb 用同一个 type_datum 表示具体 type 和 attribute,attribute 标志区分符号类别;value 是内部编号,bounds 用于 type bounds。
源码文件:kernel/common/security/selinux/ss/policydb.h
struct type_datum {
u32 value;
u32 bounds;
unsigned char primary;
unsigned char attribute;
};attribute 不是一组字符串列表直接挂在 type 上。成员关系另存为 ebitmap,符号表只保存类型自身元数据。
5.2 type_attr_map
policydb.type_attr_map_array 按具体 type 编号索引,每个元素是该 type 关联的 type/attribute 位图。
源码文件:kernel/common/security/selinux/ss/policydb.h
struct policydb {
// ... class、role、type和user符号表。
struct avtab te_avtab;
struct avtab te_cond_avtab;
/* type -> attribute reverse mapping */
struct ebitmap *type_attr_map_array;
};reverse mapping 让运行期从具体 source/target type 快速得到“自身以及所属 attributes”,无需扫描所有 attribute 定义。
5.3 访问向量展开
security server 计算 access vector 时,从 source type 与 target type 各取一个成员位图,做双重遍历,并用每种组合查询 avtab。
源码文件:kernel/common/security/selinux/ss/services.c
相关函数:context_struct_compute_av
sattr = &policydb->type_attr_map_array[
scontext->type - 1];
tattr = &policydb->type_attr_map_array[
tcontext->type - 1];
ebitmap_for_each_positive_bit(sattr, snode, i) {
ebitmap_for_each_positive_bit(tattr, tnode, j) {
avkey.source_type = i + 1;
avkey.target_type = j + 1;
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;
}
}
}假设 source type 属于 domain、coredomain,target type 属于 file_type、system_file_type,内核会查询具体 type/attribute 的多种组合并合并 permission。attribute 规则不是在构建时简单复制成一条规则后就消失的唯一模型,最终行为取决于 CIL expansion 和 policydb mapping。
图中新增成员会让该 type 参与 attribute 已有规则和 neverallow,这就是 attribute 关联具有较大安全影响面的原因。
6. Public接口
6.1 public与private
位于 system/sepolicy/public 的 type/attribute 可以成为 vendor policy 编译接口;private 类型是平台内部实现。把只供 system 使用的 type 暴露到 public,会增加长期兼容负担;把 vendor 必须引用的 attribute 放在 private,则 vendor policy 无法稳定使用。
Public 不是“权限更大”,而是“策略 API 可见性更广”。访问权限仍由 allow/neverallow 决定。
6.2 版本化映射
Soong 的 se_versioned_policy 可生成 public policy mapping,或把 target policy 按版本 attribute 化,再和 mapping 合并。
源码文件:system/sepolicy/build/soong/versioned_policy.go
if proptools.Bool(m.properties.Mapping) {
rule.Command().BuiltTool("version_policy").
FlagWithInput("-b ",
android.PathForModuleSrc(
ctx, *m.properties.Base)).
FlagWithArg("-n ", version).
FlagWithOutput("-o ", out).
Flag("-m")
} else if target := proptools.String(
m.properties.Target_policy); target != "" {
mapping := pathForModuleOut(
ctx, stem+".mapping.cil")
attributized := pathForModuleOut(
ctx, stem+".attributized.cil")
// ... 生成mapping并将target policy版本化。
rule.Command().Text("cat").
Input(mapping).
Input(attributized).
Text("> ").Output(out)
}版本化 attribute 让旧 vendor policy 引用的 public type 在新平台 policy 中仍有映射。它不等于给 vendor 自动增加新权限;mapping 解决的是符号兼容。
6.3 Freeze检查
平台 public API 冻结后,freeze test 比较当前与 prebuilt CIL 的 type/attribute 集合。删除或新增 public 符号都会报告。
源码文件:system/sepolicy/tests/sepolicy_freeze_test.py
removed_types = prebuilt_policy.types - current_policy.types
added_types = current_policy.types - prebuilt_policy.types
removed_attributes = (
prebuilt_policy.typeattributes
- current_policy.typeattributes)
added_attributes = (
current_policy.typeattributes
- prebuilt_policy.typeattributes)
if removed_types:
results += "The following public types were removed:\n" \
+ ", ".join(removed_types) + "\n"
if added_types:
results += "The following public types were added:\n" \
+ ", ".join(added_types) + "\n"
if removed_attributes:
results += "The following public attributes were removed:\n" \
+ ", ".join(removed_attributes) + "\n"
if added_attributes:
results += "The following public attributes were added:\n" \
+ ", ".join(added_attributes) + "\n"测试比较符号集合,不比较某个 type 的所有 allow。它证明 public type/attribute 是需要维护的 API,但不能证明新版本行为完全兼容。
7. 风险边界
| 修改 | 直接影响 | 间接风险 |
|---|---|---|
| 新建具体 type | 新 context 可引用符号 | 未加入必要 attribute 导致 context/test 失败 |
加入 domain | role r 可关联该 type,通用 domain 规则生效 | 获得大量基础权限与 neverallow |
加入 appdomain | 应用通用规则生效 | 继承 Binder、文件、进程规则及限制 |
加入 file_type | 可用于 labeled filesystem contexts | 进入所有 file_type 集合规则 |
加入 system_file_type | 被视为 system 分区文件 | Treble/coredomain 边界改变 |
| 修改 expandattribute | CIL 与分析工具可见结构变化 | neverallow、mapping 或工具查询差异 |
| 新增 public attribute | vendor 可引用新接口 | API freeze 与未来兼容负担 |
| 删除 public type | 旧 vendor policy 失去符号 | version mapping/CTS 构建失败 |
新增 attribute 关联前,应搜索 source 和 target 两侧的所有 allow、neverallow、type transition、tests;不能只看 attribute 注释。
8. 反向测试
8.1 成员查询
sepolicy_tests.py 使用 QueryTypeAttribute() 从编译策略取得 attribute 成员,再逐个查询展开后的 TE 权限。
源码文件:system/sepolicy/tests/sepolicy_tests.py
相关测试:TestIsolatedComputeAllowedPropertySubset
allAttributes = test_policy.pol.GetAllTypes(isAttr=True)
if "isolated_compute_allowed_property_type" \
not in allAttributes:
return ret
for typeName in test_policy.pol.QueryTypeAttribute(
Type="isolated_compute_allowed_property_type",
IsAttr=True):
grantedPerms = set()
for rule in test_policy.pol.QueryExpandedTERule(
scontext={"untrusted_app"},
tcontext={typeName},
tclass={"file"}):
grantedPerms.update(rule.perms)
missingPerms = allowedPerms.difference(grantedPerms)
if missingPerms:
violatingTypes.append(typeName)输入是编译策略中的 attribute 和每个成员 type;断言是成员必须拥有规定的 file 权限。它验证 attribute membership 会影响实际规则查询,而不是只验证源码存在 typeattribute 文本。它没有验证对象是否在运行设备上被标成这些 type。
8.2 Coredomain测试
同一测试集结合 file contexts 判断 domain 从 system 还是 vendor 启动,再检查 coredomain attribute 的有无。
for d in test_policy.alldomains:
domain = test_policy.alldomains[d]
if domain.fromSystem and \
"coredomain" not in domain.attributes:
violators.append(d)
for d in test_policy.alldomains:
domain = test_policy.alldomains[d]
if domain.fromVendor and \
"coredomain" in domain.attributes:
violators.append(d)输入同时包含 policy domain/attribute 和 executable file contexts;断言把 attribute 与分区来源绑定。它证明一个错误 typeattribute 可以跨越 Treble 边界,但不覆盖 domain 的所有 Binder/file 权限。
8.3 Freeze测试
freeze test 的输入是当前 public CIL 与冻结 API CIL,断言 type 和 attribute 集合没有未允许的增删。它不关心 private-only 符号。
# 在完整AOSP构建环境中运行public policy freeze检查。
m se_freeze_test
# 运行全局策略与attribute/file_contexts关系测试。
m sepolicy_test如果平台 API 尚未冻结或产品配置跳过 freeze,目标行为可能不同;测试结果必须结合 board API level 和 release 配置解释。
9. 源码导航
先从 type 和 attribute 声明位置建立成员图:
# 搜索直接声明、延迟关联和展开控制。
rg -n '^(type |attribute |typeattribute |expandattribute |typealias )' \
system/sepolicy/public system/sepolicy/private找到 attribute 后,必须搜索它在 source、target、neverallow 和宏中的全部消费者:
# 以coredomain/appdomain为例检查关联和规则引用。
rg -n '\b(coredomain|appdomain)\b' \
system/sepolicy/public system/sepolicy/private运行期关系位于 kernel policydb 和 access vector 计算:
# 查看type_datum、type_attr_map与双重attribute遍历。
rg -n 'struct type_datum|type_attr_map_array|type_attr_map|ebitmap_for_each_positive_bit' \
kernel/common/security/selinux/ss/policydb.h \
kernel/common/security/selinux/ss/policydb.c \
kernel/common/security/selinux/ss/services.cPublic API 与 mapping 问题要进入版本化和 freeze 工具:
# 查看public符号版本化、mapping和freeze比较。
rg -n 'version_policy|Mapping|attributized|removed_types|added_attributes' \
system/sepolicy/build/soong/versioned_policy.go \
system/sepolicy/build/soong/sepolicy_freeze.go \
system/sepolicy/tests/sepolicy_freeze_test.py生成策略后,用查询工具验证成员而不是只读源码:
# 示例:查询编译策略中的attribute成员;工具和参数取决于构建产物。
seinfo -a appdomain -x \
-p out/target/product/<product>/vendor/etc/selinux/precompiled_sepolicy
# 查询某个具体type拥有的attribute。
seinfo -t system_server -x \
-p out/target/product/<product>/vendor/etc/selinux/precompiled_sepolicy10. 闭环复述
- 对比
type foo, domain, coredomain;与先声明type foo;再写两条typeattribute,说明最终成员语义和代码组织差异。 - 从
system_server的 attribute 列表中任选一个,搜索它作为 source/target 的 allow 和 neverallow,说明新增成员可能同时获得权限和约束。 - 从
type_attr_map_array复述具体 source/target type 怎样扩展为 attribute 组合,并说明 AVC 为什么不重新解析typeattribute文本。 - 解释
expandattribute false不等于 attribute 不生效,并给出查看 CIL/编译策略验证最终结构的方法。 - 给定一个 vendor daemon,判断
domain、coredomain、vendor_file_type等 attribute 应放在哪里,并说明错误 coredomain 关联如何被测试发现。 - 说明 public type/attribute 与 private type 的区别,以及 freeze test 为什么关注符号增删而不等价于行为兼容测试。
Type 是具体安全身份,attribute 是会被规则、neverallow、mapping 和测试共同消费的命名集合。声明一个 type 只创建符号;把它加入 attribute 才会把它接入既有策略网络。安全地修改成员关系,需要同时阅读源策略、生成 CIL、kernel type map 和 public API 兼容边界。
