Skip to content

Type与Attribute

从 Android 类型声明、attribute 关联、kernel type_attr_map 和 public API freeze 解释类型集合的编译与运行语义。

基于android-17.0.0_r1
AndroidSELinuxTypeAttributeCILTreble源码阅读

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;声明时加入多个 attributecompiler/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

text
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_typeTreble/分区测试
数据用途app_data_file_type、core_data_file_typeapp 隔离与 neverallow
服务APIservice_manager_type、system_server_serviceservicemanager 与客户端规则

同一 type 的所有 attribute 都会进入策略查询,所以把 vendor file type 错误加入 system file attribute 不只是分类错误,还可能获得 platform 范围规则并触发 Treble 测试。

2.3 expandattribute ​

部分 attribute 显式声明是否展开:

text
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

text
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

text
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

text
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 已提前声明。

text
type traced_perf_exec,
    system_file_type,
    exec_type,
    file_type;

4.2 延迟关联 ​

typeattribute 适合职责在另一个策略层追加,或由宏统一生成。它可以跨文件扩展同一个 type,因此只读声明文件无法获得完整成员列表。

text
typeattribute system_server coredomain;
typeattribute system_server bpfdomain;

两种写法进入最终成员图后的访问语义相同;差异在代码组织、可见性和 public/private 接口所有权。

4.3 宏生成 ​

宏可以同时生成 attribute 声明、typeattribute 和规则。阅读宏调用必须回到宏定义。

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

text
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

c
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

c
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

c
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

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

python
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 失败
加入 domainrole r 可关联该 type,通用 domain 规则生效获得大量基础权限与 neverallow
加入 appdomain应用通用规则生效继承 Binder、文件、进程规则及限制
加入 file_type可用于 labeled filesystem contexts进入所有 file_type 集合规则
加入 system_file_type被视为 system 分区文件Treble/coredomain 边界改变
修改 expandattributeCIL 与分析工具可见结构变化neverallow、mapping 或工具查询差异
新增 public attributevendor 可引用新接口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

python
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 的有无。

python
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 符号。

bash
# 在完整AOSP构建环境中运行public policy freeze检查。
m se_freeze_test

# 运行全局策略与attribute/file_contexts关系测试。
m sepolicy_test

如果平台 API 尚未冻结或产品配置跳过 freeze,目标行为可能不同;测试结果必须结合 board API level 和 release 配置解释。

9. 源码导航 ​

先从 type 和 attribute 声明位置建立成员图:

bash
# 搜索直接声明、延迟关联和展开控制。
rg -n '^(type |attribute |typeattribute |expandattribute |typealias )' \
  system/sepolicy/public system/sepolicy/private

找到 attribute 后,必须搜索它在 source、target、neverallow 和宏中的全部消费者:

bash
# 以coredomain/appdomain为例检查关联和规则引用。
rg -n '\b(coredomain|appdomain)\b' \
  system/sepolicy/public system/sepolicy/private

运行期关系位于 kernel policydb 和 access vector 计算:

bash
# 查看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.c

Public API 与 mapping 问题要进入版本化和 freeze 工具:

bash
# 查看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

生成策略后,用查询工具验证成员而不是只读源码:

bash
# 示例:查询编译策略中的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_sepolicy

10. 闭环复述 ​

  1. 对比 type foo, domain, coredomain; 与先声明 type foo; 再写两条 typeattribute,说明最终成员语义和代码组织差异。
  2. 从 system_server 的 attribute 列表中任选一个,搜索它作为 source/target 的 allow 和 neverallow,说明新增成员可能同时获得权限和约束。
  3. 从 type_attr_map_array 复述具体 source/target type 怎样扩展为 attribute 组合,并说明 AVC 为什么不重新解析 typeattribute 文本。
  4. 解释 expandattribute false 不等于 attribute 不生效,并给出查看 CIL/编译策略验证最终结构的方法。
  5. 给定一个 vendor daemon,判断 domain、coredomain、vendor_file_type 等 attribute 应放在哪里,并说明错误 coredomain 关联如何被测试发现。
  6. 说明 public type/attribute 与 private type 的区别,以及 freeze test 为什么关注符号增删而不等价于行为兼容测试。

Type 是具体安全身份,attribute 是会被规则、neverallow、mapping 和测试共同消费的命名集合。声明一个 type 只创建符号;把它加入 attribute 才会把它接入既有策略网络。安全地修改成员关系,需要同时阅读源策略、生成 CIL、kernel type map 和 public API 兼容边界。