已关闭
feat: appspawn 解耦基建 P1+P2(引擎原语抽离 + 策略表框架) #2945
wangfenging创建于 10 天前关闭于 3 天前
feat: appspawn 解耦基建 P1+P2(引擎原语抽离 + 策略表框架) #2945
已关闭
wangfenging创建于 10 天前关闭于 3 天前
12 个文件变更+688-32
@@ -0,0 +1,36 @@
1+{
2+ "policies": [
3+ {
4+ "name": "app",
5+ "uidFrom": "tlv:dac",
6+ "gidFrom": "tlv:dac",
7+ "caps": "tlv:caps",
8+ "selinux": "tlv:selinux",
9+ "sandboxCfg": "appdata-sandbox.json",
10+ "sandboxRootFrom": "tlv:bundle",
11+ "execFrom": "tlv:hap_entry",
12+ "notifyAms": true
13+ },
14+ {
15+ "name": "daemon-owned",
16+ "uid": 0,
17+ "gid": 0,
18+ "caps": "sudo",
19+ "selinux": "sudo",
20+ "sandboxCfg": "appdata-sandbox.json",
21+ "sandboxRootFrom": "ext:owner",
22+ "execFrom": "ext:daemon_bin_path",
23+ "notifyAms": true
24+ },
25+ {
26+ "name": "daemon-system",
27+ "uid": 0,
28+ "gid": 0,
29+ "caps": "sudo",
30+ "selinux": "sudo",
31+ "sandboxCfg": "appdata-sandbox-daemon-system.json",
32+ "execFrom": "ext:daemon_bin_path",
33+ "notifyAms": true
34+ }
35+ ]
36+}
@@ -63,6 +63,12 @@ ohos_prebuilt_etc("appspawn_systemLib.json") {
63 module_install_dir = "etc/appspawn"63 module_install_dir = "etc/appspawn"
64}64}
65 65 
66+ohos_prebuilt_etc("appspawn-spawn-policy.json") {
67+ source = "../appspawn-spawn-policy.json"
68+ part_name = "${part_name}"
69+ module_install_dir = "etc/appspawn"
70+}
71+ 
66ohos_prebuilt_etc("ohos.startup.appspawn.para") {72ohos_prebuilt_etc("ohos.startup.appspawn.para") {
67 source = "ohos.startup.appspawn.para"73 source = "ohos.startup.appspawn.para"
68 part_name = "${part_name}"74 part_name = "${part_name}"
@@ -85,6 +91,7 @@ group("etc_files") {
85 ":appspawn_preload_ets.json",91 ":appspawn_preload_ets.json",
86 ":appspawn_prelink_dso_list",92 ":appspawn_prelink_dso_list",
87 ":appspawn_systemLib.json",93 ":appspawn_systemLib.json",
94+ ":appspawn-spawn-policy.json",
88 ":ohos.startup.appspawn.para",95 ":ohos.startup.appspawn.para",
89 ":ohos.startup.appspawn.para.dac"96 ":ohos.startup.appspawn.para.dac"
90 ]97 ]
@@ -116,10 +116,10 @@ void SetHapDomainInfo(const AppSpawnMgr *content, const AppSpawningCtx *property
116}116}
117#endif117#endif
118 118 
119-int SetSelinuxCon(const AppSpawnMgr *content, const AppSpawningCtx *property)119+int SetAppSelinuxCon(const AppSpawnMgr *content, const AppSpawningCtx *property)
120{120{
121#ifdef WITH_SELINUX121#ifdef WITH_SELINUX
122- APPSPAWN_LOGV("SetSelinuxCon IsDeveloperModeOn %{public}d", IsDeveloperModeOn(property));122+ APPSPAWN_LOGV("SetAppSelinuxCon IsDeveloperModeOn %{public}d", IsDeveloperModeOn(property));
123 if (GetAppSpawnMsgType(property) == MSG_SPAWN_NATIVE_PROCESS) {123 if (GetAppSpawnMsgType(property) == MSG_SPAWN_NATIVE_PROCESS) {
124 if (!IsDeveloperModeOn(property)) {124 if (!IsDeveloperModeOn(property)) {
125 APPSPAWN_LOGE("Denied Launching a native process: not in developer mode");125 APPSPAWN_LOGE("Denied Launching a native process: not in developer mode");
@@ -137,7 +137,7 @@ int SetSelinuxCon(const AppSpawnMgr *content, const AppSpawningCtx *property)
137 int32_t ret = hapContext.HapDomainSetcontext(hapDomainInfo);137 int32_t ret = hapContext.HapDomainSetcontext(hapDomainInfo);
138 APPSPAWN_CHECK(ret == 0, return APPSPAWN_ACCESS_TOKEN_INVALID,138 APPSPAWN_CHECK(ret == 0, return APPSPAWN_ACCESS_TOKEN_INVALID,
139 "Set domain context failed, ret: %{public}d %{public}s", ret, GetProcessName(property));139 "Set domain context failed, ret: %{public}d %{public}s", ret, GetProcessName(property));
140- APPSPAWN_LOGV("SetSelinuxCon success for %{public}s", GetProcessName(property));140+ APPSPAWN_LOGV("SetAppSelinuxCon success for %{public}s", GetProcessName(property));
141#endif141#endif
142 return 0;142 return 0;
143}143}
@@ -23,7 +23,7 @@ extern "C" {
23#endif23#endif
24 24 
25int SetAppAccessToken(const AppSpawnMgr *content, const AppSpawningCtx *property);25int SetAppAccessToken(const AppSpawnMgr *content, const AppSpawningCtx *property);
26-int SetSelinuxCon(const AppSpawnMgr *content, const AppSpawningCtx *property);26+int SetAppSelinuxCon(const AppSpawnMgr *content, const AppSpawningCtx *property);
27 27 
28int SetUidGidFilter(const AppSpawnMgr *content);28int SetUidGidFilter(const AppSpawnMgr *content);
29int SetSeccompFilter(const AppSpawnMgr *content, const AppSpawningCtx *property);29int SetSeccompFilter(const AppSpawnMgr *content, const AppSpawningCtx *property);
@@ -41,6 +41,7 @@
41 41 
42#include "appspawn_adapter.h"42#include "appspawn_adapter.h"
43#include "appspawn_hook.h"43#include "appspawn_hook.h"
44+#include "spawn_primitives.h"
44#include "appspawn_service.h"45#include "appspawn_service.h"
45#include "appspawn_msg.h"46#include "appspawn_msg.h"
46#include "appspawn_manager.h"47#include "appspawn_manager.h"
@@ -214,7 +215,7 @@ APPSPAWN_STATIC int SetExtPermAmbientFromResult(const ExtPermResult *result)
214}215}
215 216 
216 217 
217-APPSPAWN_STATIC int SetAmbientCapabilities(const AppSpawningCtx *property, const ExtPermResult *result)218+APPSPAWN_STATIC int SetAppAmbientCapabilities(const AppSpawningCtx *property, const ExtPermResult *result)
218{219{
219 if (!IsNoShareFsEnable()) {220 if (!IsNoShareFsEnable()) {
220 return 0;221 return 0;
@@ -236,18 +237,8 @@ APPSPAWN_STATIC int SetAmbientCapabilities(const AppSpawningCtx *property, const
236 return ret;237 return ret;
237}238}
238 239 
239-APPSPAWN_STATIC int SetCapabilities(const AppSpawnMgr *content, const AppSpawningCtx *property)240+APPSPAWN_STATIC int SetAppCapabilities(const AppSpawnMgr *content, const AppSpawningCtx *property)
240{241{
241- // init cap
242- struct __user_cap_header_struct capHeader;
243- bool isRet = memset_s(&capHeader, sizeof(capHeader), 0, sizeof(capHeader)) != EOK;
244- APPSPAWN_CHECK(!isRet, return -EINVAL, "Failed to memset cap header");
245- 
246- capHeader.version = _LINUX_CAPABILITY_VERSION_3;
247- capHeader.pid = 0;
248- struct __user_cap_data_struct capData[2];
249- isRet = memset_s(&capData, sizeof(capData), 0, sizeof(capData)) != EOK;
250- APPSPAWN_CHECK(!isRet, return -EINVAL, "Failed to memset cap data");
251 bool needExtPerm = IsNoShareFsEnable() &&242 bool needExtPerm = IsNoShareFsEnable() &&
252 !CheckAppMsgFlagsSet(property, APP_FLAGS_ISOLATED_SANDBOX_TYPE) &&243 !CheckAppMsgFlagsSet(property, APP_FLAGS_ISOLATED_SANDBOX_TYPE) &&
253 (IsAppSpawnMode(content) || IsNativeSpawnMode(content));244 (IsAppSpawnMode(content) || IsNativeSpawnMode(content));
@@ -255,7 +246,7 @@ APPSPAWN_STATIC int SetCapabilities(const AppSpawnMgr *content, const AppSpawnin
255 if (needExtPerm) {246 if (needExtPerm) {
256 GetExtPermResult(property, &extResult);247 GetExtPermResult(property, &extResult);
257 }248 }
258- // init inheritable permitted effective zero249+ // compute capability mask (app policy); mechanism moved to engine primitive SetCapabilities
259#ifdef GRAPHIC_PERMISSION_CHECK250#ifdef GRAPHIC_PERMISSION_CHECK
260 u_int64_t baseCaps = 0;251 u_int64_t baseCaps = 0;
261 if (needExtPerm) {252 if (needExtPerm) {
@@ -272,17 +263,11 @@ APPSPAWN_STATIC int SetCapabilities(const AppSpawnMgr *content, const AppSpawnin
272 const uint64_t permitted = 0x3fffffffff;263 const uint64_t permitted = 0x3fffffffff;
273 const uint64_t effective = 0x3fffffffff;264 const uint64_t effective = 0x3fffffffff;
274#endif265#endif
275- capData[0].inheritable = (__u32)(inheriTable);266+ int ret = SetCapabilities(inheriTable, permitted, effective);
276- capData[1].inheritable = (__u32)(inheriTable >> BITLEN32);267+ APPSPAWN_CHECK(ret == 0, return ret, "Failed to capset via primitive, ret: %{public}d", ret);
277- capData[0].permitted = (__u32)(permitted);
278- capData[1].permitted = (__u32)(permitted >> BITLEN32);
279- capData[0].effective = (__u32)(effective);
280- capData[1].effective = (__u32)(effective >> BITLEN32);
281- isRet = capset(&capHeader, &capData[0]) != 0;
282- APPSPAWN_CHECK(!isRet, return -errno, "Failed to capset errno: %{public}d", errno);
283 268 
284 if (needExtPerm) {269 if (needExtPerm) {
285- isRet = SetAmbientCapabilities(property, &extResult);270+ bool isRet = SetAppAmbientCapabilities(property, &extResult);
286 APPSPAWN_CHECK(!isRet, return -1, "Failed to set ambient");271 APPSPAWN_CHECK(!isRet, return -1, "Failed to set ambient");
287 }272 }
288 return 0;273 return 0;
@@ -401,7 +386,7 @@ APPSPAWN_STATIC int SetXpmConfig(const AppSpawnMgr *content, const AppSpawningCt
401 return 0;386 return 0;
402}387}
403 388 
404-APPSPAWN_STATIC int SetUidGid(const AppSpawnMgr *content, const AppSpawningCtx *property)389+APPSPAWN_STATIC int SetAppUidGid(const AppSpawnMgr *content, const AppSpawningCtx *property)
405{390{
406 AppSpawnMsgDacInfo *dacInfo = (AppSpawnMsgDacInfo *)GetAppProperty(property, TLV_DAC_INFO);391 AppSpawnMsgDacInfo *dacInfo = (AppSpawnMsgDacInfo *)GetAppProperty(property, TLV_DAC_INFO);
407 APPSPAWN_CHECK(dacInfo != NULL, return APPSPAWN_TLV_NONE,392 APPSPAWN_CHECK(dacInfo != NULL, return APPSPAWN_TLV_NONE,
@@ -682,17 +667,17 @@ static int SpawnSetProperties(AppSpawnMgr *content, AppSpawningCtx *property)
682 ret = SetSchedPriority(content, property);667 ret = SetSchedPriority(content, property);
683 APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);668 APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
684 669 
685- ret = SetUidGid(content, property);670+ ret = SetAppUidGid(content, property);
686 APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);671 APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
687 672 
688 ret = SetFileDescriptors(content, property);673 ret = SetFileDescriptors(content, property);
689 APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);674 APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
690 675 
691- ret = SetCapabilities(content, property);676+ ret = SetAppCapabilities(content, property);
692 APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);677 APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
693 678 
694- StartAppspawnTrace("SetSelinuxCon");679+ StartAppspawnTrace("SetAppSelinuxCon");
695- ret = SetSelinuxCon(content, property);680+ ret = SetAppSelinuxCon(content, property);
696 FinishAppspawnTrace();681 FinishAppspawnTrace();
697 APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);682 APPSPAWN_CHECK_ONLY_EXPER(ret == 0, return ret);
698 683 
@@ -0,0 +1,100 @@
1+/*
2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+#ifndef SPAWN_POLICY_H
17+#define SPAWN_POLICY_H
18+ 
19+#include <stdbool.h>
20+#include <stdint.h>
21+#include <sys/types.h>
22+ 
23+#include "appspawn_hook.h" // AppSpawningCtx forward decl
24+ 
25+#ifdef __cplusplus
26+#if __cplusplus
27+extern "C" {
28+#endif
29+#endif
30+ 
31+#define SPAWN_POLICY_NAME_LEN 32
32+#define SPAWN_POLICY_CFG_LEN 64
33+#define SPAWN_POLICY_SELINUX_LEN 64
34+#define SPAWN_POLICY_MAX 8
35+ 
36+// 参数来源标记:把"参数从哪来"数据化,共享 Hook 据此取参,见 daemon_spawn_design §6.4/§6.5
37+typedef enum {
38+ POLICY_SRC_INVALID = 0,
39+ POLICY_SRC_TLV_DAC, // uid/gid 取自 TLV_DAC_INFO
40+ POLICY_SRC_FIXED, // uid/gid 策略表固定值(daemon=0)
41+ POLICY_SRC_SUDO, // caps = sudo 掩码(默认 0x3fffffffff,TBD-R1 待安全 owner 定)
42+ POLICY_SRC_TLV_CAPS, // caps 取自 TLV
43+ POLICY_SRC_SELINUX_SUDO, // selinux = sudo 域(默认 u:r:sudo:s0,TBD-R2 待 SELinux owner 定)
44+ POLICY_SRC_TLV_SELINUX, // selinux 取自 TLV
45+ POLICY_SRC_TLV_BUNDLE, // 沙箱 root 取自 TLV_BUNDLE_INFO
46+ POLICY_SRC_EXT_OWNER, // 沙箱 root 取自属主 ext
47+ POLICY_SRC_TLV_HAP_ENTRY, // exec 目标取自 TLV hap 入口
48+ POLICY_SRC_EXT_BIN_PATH, // exec 目标取自 ext:daemon_bin_path
49+} PolicySrc;
50+ 
51+/**
52+ * @brief 单条孵化策略(策略表条目)。
53+ *
54+ * 对应 appspawn-spawn-policy.json 的一条 policy。共享 Hook 读 ResolvePolicy()
55+ * 得到此结构,据此取参调引擎原语,见 daemon_spawn_design §6.46.5
56+ */
57+typedef struct {
58+ char name[SPAWN_POLICY_NAME_LEN]; // "app" / "daemon-owned" / "daemon-system"
59+ PolicySrc uidFrom;
60+ PolicySrc gidFrom;
61+ uid_t uid; // POLICY_SRC_FIXED 时的固定 uid(daemon=0)
62+ gid_t gid; // POLICY_SRC_FIXED 时的固定 gid
63+ PolicySrc capsFrom; // POLICY_SRC_SUDO / POLICY_SRC_TLV_CAPS
64+ uint64_t capsMask; // POLICY_SRC_SUDO 时的掩码(默认 TBD-R1)
65+ PolicySrc selinuxFrom; // POLICY_SRC_SELINUX_SUDO / POLICY_SRC_TLV_SELINUX
66+ char selinuxCtx[SPAWN_POLICY_SELINUX_LEN]; // POLICY_SRC_SELINUX_SUDO 时的 ctx(默认 TBD-R2)
67+ char sandboxCfg[SPAWN_POLICY_CFG_LEN]; // 沙箱配置 JSON 文件名
68+ PolicySrc sandboxRootFrom; // POLICY_SRC_TLV_BUNDLE / POLICY_SRC_EXT_OWNER
69+ PolicySrc execFrom; // POLICY_SRC_TLV_HAP_ENTRY / POLICY_SRC_EXT_BIN_PATH
70+ bool notifyAms; // 是否回调 ams(取代 isAppspawn 门控,见 §6.6)
71+} SpawnPolicy;
72+ 
73+/**
74+ * @brief 启动期加载策略表 appspawn-spawn-policy.json。
75+ *
76+ * 解析 JSON 填充 g_policy 表;文件缺失/解析失败则回退内置默认 app 策略,
77+ * 保证不裸奔。在 main() InitCommonEnv 后调用,见 daemon_spawn_design §10.2 P2。
78+ *
79+ * @return 0 成功;-1 失败(已回退默认,不致命)
80+ */
81+int SpawnPolicyInit(void);
82+ 
83+/**
84+ * @brief 按请求数据解析孵化策略(零类型分支的关键)。
85+ *
86+ * 粗分类信号 = 消息类型(P4 加 MSG_SPAWN_DAEMON 后启用 daemon 分支),
87+ * 细分类信号 = 请求数据(有无属主 ext)。P2 旁路态:仅返回 app 策略
88+ * (值与原硬编码一致),daemon 分类在 P4 启用,见 daemon_spawn_design §6.4
89+ *
90+ * @param ctx 孵化上下文(含消息类型/TLV/ext)
91+ * @return 命中策略指针(永不为 NULL,未命中回退 app)
92+ */
93+const SpawnPolicy *ResolvePolicy(const AppSpawningCtx *ctx);
94+ 
95+#ifdef __cplusplus
96+#if __cplusplus
97+}
98+#endif
99+#endif
100+#endif // SPAWN_POLICY_H
@@ -0,0 +1,102 @@
1+/*
2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+#ifndef SPAWN_PRIMITIVES_H
17+#define SPAWN_PRIMITIVES_H
18+ 
19+#include <stddef.h>
20+#include <stdint.h>
21+#include <sys/types.h>
22+ 
23+#ifdef __cplusplus
24+#if __cplusplus
25+extern "C" {
26+#endif
27+#endif
28+ 
29+/**
30+ * @brief 设置子进程 uid/gid/补充组(纯机制原语)。
31+ *
32+ * 仅封装 setgroups→setresgid→setresuid 的内核调用序列与顺序不变量
33+ * (setresgid 先于 setresuid)。不读 TLV、不感知孵化类型;
34+ * 参数来源(TLV/策略表)由共享 Hook 决定,见 daemon_spawn_design §6.9
35+ *
36+ * @param uid 目标 uid;daemon 策略传 0,app 由 Hook 从 TLV_DAC 取
37+ * @param gid 目标 gid
38+ * @param gids 补充组表;为 NULL 表示无补充组
39+ * @param gidCount gids 元素数;gids 为 NULL 时忽略
40+ * @return 0 成功;负数失败(-errno)
41+ */
42+int SetUidGid(uid_t uid, gid_t gid, const gid_t *gids, size_t gidCount);
43+ 
44+/**
45+ * @brief 设置进程 capability 集合(纯机制原语)。
46+ *
47+ * 仅封装 capset 系统调用(inheritable/permitted/effective 三集合)。
48+ * 掩码由调用方(共享 Hook 读策略表)提供;app 侧的“flag→mask 计算”、
49+ * ext perm、isolated sandbox 等策略不在此原语,见 daemon_spawn_design §6.9
50+ *
51+ * @param inheritable inheritable 集合位图
52+ * @param permitted permitted 集合位图
53+ * @param effective effective 集合位图
54+ * @return 0 成功;负数失败(-errno)
55+ */
56+int SetCapabilities(uint64_t inheritable, uint64_t permitted, uint64_t effective);
57+ 
58+/**
59+ * @brief 设置 ambient capability 集合(纯机制原语)。
60+ *
61+ * 遍历掩码中置位的每个 cap,逐个 prctl(PR_CAP_AMBIENT, RAISE)。
62+ * 掩码由调用方提供;app 侧的 CAP_DAC_OVERRIDE/CAP_KILL/CAP_FOWNER
63+ * 条件计算不在此原语,见 daemon_spawn_design §6.9
64+ *
65+ * @param mask ambient 能力位图
66+ * @return 0 成功;-1 失败
67+ */
68+int SetAmbientCapabilities(uint64_t mask);
69+ 
70+/**
71+ * @brief 设置子进程 SELinux 上下文(纯机制原语)。
72+ *
73+ * 仅封装 setcon()。上下文串由调用方(共享 Hook 读策略表)提供;
74+ * app 侧的 HapDomain 解析(provision type/APL→域)不在此原语,
75+ * 由 app Hook 算出 context 后调本原语,见 daemon_spawn_design §6.9
76+ * 编译需 WITH_SELINUX;未启用时为空实现返回 0
77+ *
78+ * @param context 目标 SELinux 上下文串,如 "u:r:sudo:s0"
79+ * @return 0 成功;-1 失败(或未启用 SELinux 时 0
80+ */
81+int SetSelinuxCon(const char *context);
82+ 
83+/**
84+ * @brief 执行目标二进制(纯机制原语)。
85+ *
86+ * envp 为 NULL 时走 execv,否则走 execve。路径与参数由调用方
87+ * (共享 Hook 读策略表 execFrom:app→TLV:hap_entry,daemon→ext:daemon_bin_path)
88+ * 提供,见 daemon_spawn_design §6.9。成功不返回。
89+ *
90+ * @param path 目标二进制路径
91+ * @param argv 参数数组(以 NULL 结尾)
92+ * @param envp 环境变量数组;NULL 表示用 execv(继承当前环境)
93+ * @return 失败返回 -errno;成功不返回
94+ */
95+int ExecvTarget(const char *path, char *const argv[], char *const envp[]);
96+ 
97+#ifdef __cplusplus
98+#if __cplusplus
99+}
100+#endif
101+#endif
102+#endif // SPAWN_PRIMITIVES_H
@@ -0,0 +1,222 @@
1+/*
2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+#include "spawn_policy.h"
17+ 
18+#include <errno.h>
19+#include <stdbool.h>
20+#include <stdio.h>
21+#include <string.h>
22+ 
23+#include "appspawn_utils.h"
24+#include "cJSON.h"
25+#include "securec.h"
26+ 
27+#define SPAWN_POLICY_PATH "/system/etc/appspawn/appspawn-spawn-policy.json"
28+// TBD-R1/TBD-R2 默认假设(见 §3.7),安全 owner 确认后改 JSON 即可,零代码
29+#define DEFAULT_SUDO_CAPS_MASK 0x3fffffffffULL
30+#define DEFAULT_SUDO_SELINUX_CTX "u:r:sudo:s0"
31+ 
32+static SpawnPolicy g_policy[SPAWN_POLICY_MAX];
33+static int g_policyCount = 0;
34+static bool g_policyInited = false;
35+ 
36+// 内置默认 app 策略:JSON 缺失/解析失败时回退,保证不裸奔
37+static void LoadDefaultAppPolicy(void)
38+{
39+ SpawnPolicy *p = &g_policy[0];
40+ (void)memset_s(p, sizeof(SpawnPolicy), 0, sizeof(SpawnPolicy));
41+ (void)strcpy_s(p->name, sizeof(p->name), "app");
42+ p->uidFrom = POLICY_SRC_TLV_DAC;
43+ p->gidFrom = POLICY_SRC_TLV_DAC;
44+ p->capsFrom = POLICY_SRC_TLV_CAPS;
45+ p->selinuxFrom = POLICY_SRC_TLV_SELINUX;
46+ (void)strcpy_s(p->sandboxCfg, sizeof(p->sandboxCfg), "appdata-sandbox.json");
47+ p->sandboxRootFrom = POLICY_SRC_TLV_BUNDLE;
48+ p->execFrom = POLICY_SRC_TLV_HAP_ENTRY;
49+ p->notifyAms = true;
50+ g_policyCount = 1;
51+}
52+ 
53+// uid/gid 来源串 -> enum
54+static PolicySrc ParseUidGidSrc(const cJSON *json, const char *field, int64_t *fixedVal)
55+{
56+ const cJSON *src = cJSON_GetObjectItem(json, field);
57+ if (cJSON_IsNumber(src)) {
58+ *fixedVal = (int64_t)src->valuedouble;
59+ return POLICY_SRC_FIXED;
60+ }
61+ if (cJSON_IsString(src) && strcmp(src->valuestring, "tlv:dac") == 0) {
62+ return POLICY_SRC_TLV_DAC;
63+ }
64+ return POLICY_SRC_INVALID;
65+}
66+ 
67+// caps 来源串 -> enum("sudo" 或 "tlv:caps")
68+static PolicySrc ParseCapsSrc(const cJSON *json, uint64_t *mask)
69+{
70+ const cJSON *caps = cJSON_GetObjectItem(json, "caps");
71+ if (cJSON_IsString(caps)) {
72+ if (strcmp(caps->valuestring, "sudo") == 0) {
73+ *mask = DEFAULT_SUDO_CAPS_MASK;
74+ return POLICY_SRC_SUDO;
75+ }
76+ if (strcmp(caps->valuestring, "tlv:caps") == 0) {
77+ return POLICY_SRC_TLV_CAPS;
78+ }
79+ }
80+ return POLICY_SRC_INVALID;
81+}
82+ 
83+// selinux 来源串 -> enum("sudo" 或 "tlv:selinux")
84+static PolicySrc ParseSelinuxSrc(const cJSON *json, char *ctx, size_t ctxLen)
85+{
86+ const cJSON *se = cJSON_GetObjectItem(json, "selinux");
87+ if (cJSON_IsString(se)) {
88+ if (strcmp(se->valuestring, "sudo") == 0) {
89+ (void)strcpy_s(ctx, ctxLen, DEFAULT_SUDO_SELINUX_CTX);
90+ return POLICY_SRC_SELINUX_SUDO;
91+ }
92+ if (strcmp(se->valuestring, "tlv:selinux") == 0) {
93+ return POLICY_SRC_TLV_SELINUX;
94+ }
95+ }
96+ return POLICY_SRC_INVALID;
97+}
98+ 
99+static PolicySrc ParseSrcStr(const cJSON *json, const char *field)
100+{
101+ const cJSON *v = cJSON_GetObjectItem(json, field);
102+ if (!cJSON_IsString(v)) {
103+ return POLICY_SRC_INVALID;
104+ }
105+ const char *s = v->valuestring;
106+ if (strcmp(s, "tlv:bundle") == 0) return POLICY_SRC_TLV_BUNDLE;
107+ if (strcmp(s, "ext:owner") == 0) return POLICY_SRC_EXT_OWNER;
108+ if (strcmp(s, "tlv:hap_entry") == 0) return POLICY_SRC_TLV_HAP_ENTRY;
109+ if (strcmp(s, "ext:daemon_bin_path") == 0) return POLICY_SRC_EXT_BIN_PATH;
110+ return POLICY_SRC_INVALID;
111+}
112+ 
113+static void ParseOnePolicy(const cJSON *item, SpawnPolicy *p)
114+{
115+ (void)memset_s(p, sizeof(SpawnPolicy), 0, sizeof(SpawnPolicy));
116+ const cJSON *name = cJSON_GetObjectItem(item, "name");
117+ if (cJSON_IsString(name)) {
118+ (void)strcpy_s(p->name, sizeof(p->name), name->valuestring);
119+ }
120+ int64_t fixedUid = 0;
121+ int64_t fixedGid = 0;
122+ p->uidFrom = ParseUidGidSrc(item, "uidFrom", &fixedUid);
123+ if (p->uidFrom == POLICY_SRC_INVALID) { // 无 uidFrom,看固定 uid
124+ const cJSON *uid = cJSON_GetObjectItem(item, "uid");
125+ if (cJSON_IsNumber(uid)) {
126+ p->uidFrom = POLICY_SRC_FIXED;
127+ fixedUid = (int64_t)uid->valuedouble;
128+ }
129+ }
130+ p->gidFrom = ParseUidGidSrc(item, "gidFrom", &fixedGid);
131+ if (p->gidFrom == POLICY_SRC_INVALID) {
132+ const cJSON *gid = cJSON_GetObjectItem(item, "gid");
133+ if (cJSON_IsNumber(gid)) {
134+ p->gidFrom = POLICY_SRC_FIXED;
135+ fixedGid = (int64_t)gid->valuedouble;
136+ }
137+ }
138+ p->uid = (uid_t)fixedUid;
139+ p->gid = (gid_t)fixedGid;
140+ p->capsFrom = ParseCapsSrc(item, &p->capsMask);
141+ p->selinuxFrom = ParseSelinuxSrc(item, p->selinuxCtx, sizeof(p->selinuxCtx));
142+ const cJSON *sandboxCfg = cJSON_GetObjectItem(item, "sandboxCfg");
143+ if (cJSON_IsString(sandboxCfg)) {
144+ (void)strcpy_s(p->sandboxCfg, sizeof(p->sandboxCfg), sandboxCfg->valuestring);
145+ }
146+ p->sandboxRootFrom = ParseSrcStr(item, "sandboxRootFrom");
147+ p->execFrom = ParseSrcStr(item, "execFrom");
148+ const cJSON *notify = cJSON_GetObjectItem(item, "notifyAms");
149+ p->notifyAms = cJSON_IsTrue(notify);
150+}
151+ 
152+int SpawnPolicyInit(void)
153+{
154+ if (g_policyInited) {
155+ return 0;
156+ }
157+ g_policyInited = true;
158+ LoadDefaultAppPolicy(); // 先置默认,JSON 失败也有兜底
159+ 
160+ FILE *fp = fopen(SPAWN_POLICY_PATH, "r");
161+ APPSPAWN_CHECK(fp != NULL, return -1,
162+ "SpawnPolicy: open %{public}s failed: %{public}d, use default app policy", SPAWN_POLICY_PATH, errno);
163+ char *content = NULL;
164+ if (fseek(fp, 0, SEEK_END) != 0) {
165+ fclose(fp);
166+ return -1;
167+ }
168+ long fsize = ftell(fp);
169+ if (fsize <= 0) {
170+ fclose(fp);
171+ return -1;
172+ }
173+ (void)fseek(fp, 0, SEEK_SET);
174+ content = (char *)calloc(1, (size_t)fsize + 1);
175+ APPSPAWN_CHECK(content != NULL, fclose(fp); return -1, "SpawnPolicy: alloc failed");
176+ size_t rsize = fread(content, 1, (size_t)fsize, fp);
177+ fclose(fp);
178+ if (rsize == 0) {
179+ free(content);
180+ return -1;
181+ }
182+ 
183+ cJSON *root = cJSON_Parse(content);
184+ free(content);
185+ APPSPAWN_CHECK(root != NULL, return -1, "SpawnPolicy: parse json failed");
186+ cJSON *arr = cJSON_GetObjectItem(root, "policies");
187+ if (cJSON_IsArray(arr)) {
188+ int n = cJSON_GetArraySize(arr);
189+ if (n > SPAWN_POLICY_MAX) {
190+ n = SPAWN_POLICY_MAX;
191+ }
192+ g_policyCount = 0;
193+ for (int i = 0; i < n; i++) {
194+ ParseOnePolicy(cJSON_GetArrayItem(arr, i), &g_policy[g_policyCount]);
195+ if (g_policy[g_policyCount].name[0] != '\0') {
196+ g_policyCount++;
197+ }
198+ }
199+ }
200+ cJSON_Delete(root);
201+ APPSPAWN_LOGI("SpawnPolicy: loaded %{public}d policies from %{public}s", g_policyCount, SPAWN_POLICY_PATH);
202+ return 0;
203+}
204+ 
205+static const SpawnPolicy *FindPolicyByName(const char *name)
206+{
207+ for (int i = 0; i < g_policyCount; i++) {
208+ if (strcmp(g_policy[i].name, name) == 0) {
209+ return &g_policy[i];
210+ }
211+ }
212+ return NULL;
213+}
214+ 
215+const SpawnPolicy *ResolvePolicy(const AppSpawningCtx *ctx)
216+{
217+ // P2 旁路态:返回 app 策略(值与原硬编码一致),不改变现有孵化行为。
218+ // P4:粗分类 msgType==MSG_SPAWN_DAEMON → daemon;细分类 有属主 ext → daemon-owned,无 → daemon-system。
219+ (void)ctx;
220+ const SpawnPolicy *app = FindPolicyByName("app");
221+ return app != NULL ? app : &g_policy[0];
222+}
@@ -0,0 +1,185 @@
1+/*
2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+#include "spawn_primitives.h"
17+ 
18+#include <errno.h>
19+#include <grp.h>
20+#include <stdint.h>
21+#include <sys/capability.h>
22+#include <sys/prctl.h>
23+#include <sys/types.h>
24+#include <unistd.h>
25+ 
26+#include "appspawn_utils.h"
27+#include "securec.h"
28+ 
29+#ifdef WITH_SELINUX
30+#include <selinux/selinux.h>
31+#endif
32+ 
33+#define BITLEN32 32
34+ 
35+/**
36+ * @brief 设置子进程 uid/gid/补充组(纯机制原语)。
37+ *
38+ * 仅封装 setgroups→setresgid→setresuid 的内核调用序列与顺序不变量
39+ * (setresgid 先于 setresuid)。不读 TLV、不感知孵化类型;
40+ * 参数来源(TLV/策略表)由共享 Hook 决定,见 daemon_spawn_design §6.9
41+ *
42+ * @param uid 目标 uid;daemon 策略传 0,app 由 Hook 从 TLV_DAC 取
43+ * @param gid 目标 gid
44+ * @param gids 补充组表;为 NULL 表示无补充组
45+ * @param gidCount gids 元素数;gids 为 NULL 时忽略
46+ * @return 0 成功;负数失败(-errno)
47+ */
48+int SetUidGid(uid_t uid, gid_t gid, const gid_t *gids, size_t gidCount)
49+{
50+ if (gids != NULL && gidCount > 0) {
51+ if (setgroups(gidCount, gids) != 0) {
52+ APPSPAWN_LOGE("setgroups failed: %{public}d, size=%{public}zu", errno, gidCount);
53+ return -errno;
54+ }
55+ }
56+ if (setresgid(gid, gid, gid) != 0) {
57+ APPSPAWN_LOGE("setresgid(%{public}u) failed: %{public}d", gid, errno);
58+ return -errno;
59+ }
60+ // setresgid 必须先于 setresuid:setresuid 从 0→非0 会清空 effective caps
61+ if (setresuid(uid, uid, uid) != 0) {
62+ APPSPAWN_LOGE("setresuid(%{public}u) failed: %{public}d", uid, errno);
63+ return -errno;
64+ }
65+ return 0;
66+}
67+ 
68+/**
69+ * @brief 设置进程 capability 集合(纯机制原语)。
70+ *
71+ * 仅封装 capset 系统调用(inheritable/permitted/effective 三集合)。
72+ * 掩码由调用方(共享 Hook 读策略表)提供;app 侧的“flag→mask 计算”、
73+ * ext perm、isolated sandbox 等策略不在此原语,见 daemon_spawn_design §6.9
74+ *
75+ * @param inheritable inheritable 集合位图
76+ * @param permitted permitted 集合位图
77+ * @param effective effective 集合位图
78+ * @return 0 成功;负数失败(-errno)
79+ */
80+int SetCapabilities(uint64_t inheritable, uint64_t permitted, uint64_t effective)
81+{
82+ struct __user_cap_header_struct capHeader;
83+ if (memset_s(&capHeader, sizeof(capHeader), 0, sizeof(capHeader)) != EOK) {
84+ APPSPAWN_LOGE("Failed to memset cap header");
85+ return -EINVAL;
86+ }
87+ capHeader.version = _LINUX_CAPABILITY_VERSION_3;
88+ capHeader.pid = 0;
89+ struct __user_cap_data_struct capData[2];
90+ if (memset_s(capData, sizeof(capData), 0, sizeof(capData)) != EOK) {
91+ APPSPAWN_LOGE("Failed to memset cap data");
92+ return -EINVAL;
93+ }
94+ capData[0].inheritable = (__u32)(inheritable);
95+ capData[1].inheritable = (__u32)(inheritable >> BITLEN32);
96+ capData[0].permitted = (__u32)(permitted);
97+ capData[1].permitted = (__u32)(permitted >> BITLEN32);
98+ capData[0].effective = (__u32)(effective);
99+ capData[1].effective = (__u32)(effective >> BITLEN32);
100+ if (capset(&capHeader, &capData[0]) != 0) {
101+ APPSPAWN_LOGE("Failed to capset errno: %{public}d", errno);
102+ return -errno;
103+ }
104+ return 0;
105+}
106+ 
107+/**
108+ * @brief 设置 ambient capability 集合(纯机制原语)。
109+ *
110+ * 遍历掩码中置位的每个 cap,逐个 prctl(PR_CAP_AMBIENT, RAISE)。
111+ * 掩码由调用方提供;app 侧的 CAP_DAC_OVERRIDE/CAP_KILL/CAP_FOWNER
112+ * 条件计算不在此原语,见 daemon_spawn_design §6.9
113+ *
114+ * @param mask ambient 能力位图
115+ * @return 0 成功;-1 失败
116+ */
117+int SetAmbientCapabilities(uint64_t mask)
118+{
119+ if (mask == 0) {
120+ return 0;
121+ }
122+ for (int cap = 0; cap < BITLEN32 * (int)(sizeof(uint64_t) / sizeof(uint32_t)); cap++) {
123+ if ((mask & (CAP_TO_MASK(cap))) == 0) {
124+ continue;
125+ }
126+ if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) != 0) {
127+ APPSPAWN_LOGE("prctl PR_CAP_AMBIENT raise cap %{public}d failed: %{public}d", cap, errno);
128+ return -1;
129+ }
130+ }
131+ return 0;
132+}
133+ 
134+/**
135+ * @brief 设置子进程 SELinux 上下文(纯机制原语)。
136+ *
137+ * 仅封装 setcon()。上下文串由调用方(共享 Hook 读策略表)提供;
138+ * app 侧的 HapDomain 解析(provision type/APL→域)不在此原语,
139+ * 由 app Hook 算出 context 后调本原语,见 daemon_spawn_design §6.9
140+ * 编译需 WITH_SELINUX;未启用时为空实现返回 0
141+ *
142+ * @param context 目标 SELinux 上下文串,如 "u:r:sudo:s0"
143+ * @return 0 成功;-1 失败(或未启用 SELinux 时 0
144+ */
145+int SetSelinuxCon(const char *context)
146+{
147+ if (context == NULL) {
148+ return 0;
149+ }
150+#ifdef WITH_SELINUX
151+ if (setcon(context) != 0) {
152+ APPSPAWN_LOGE("setcon(%{public}s) failed: %{public}d", context, errno);
153+ return -1;
154+ }
155+#else
156+ (void)context;
157+#endif
158+ return 0;
159+}
160+ 
161+/**
162+ * @brief 执行目标二进制(纯机制原语)。
163+ *
164+ * envp 为 NULL 时走 execv,否则走 execve。路径与参数由调用方
165+ * (共享 Hook 读策略表 execFrom:app→TLV:hap_entry,daemon→ext:daemon_bin_path)
166+ * 提供,见 daemon_spawn_design §6.9。成功不返回。
167+ *
168+ * @param path 目标二进制路径
169+ * @param argv 参数数组(以 NULL 结尾)
170+ * @param envp 环境变量数组;NULL 表示用 execv(继承当前环境)
171+ * @return 失败返回 -errno;成功不返回
172+ */
173+int ExecvTarget(const char *path, char *const argv[], char *const envp[])
174+{
175+ if (path == NULL || argv == NULL) {
176+ return -EINVAL;
177+ }
178+ if (envp == NULL) {
179+ execv(path, argv);
180+ } else {
181+ execve(path, argv, envp);
182+ }
183+ APPSPAWN_LOGE("exec failed, path: %{public}s errno: %{public}d", path, errno);
184+ return -errno;
185+}
@@ -16,5 +16,12 @@
16 { "name": "DumpAppSpawnMsg" },16 { "name": "DumpAppSpawnMsg" },
17 { "name": "AppSpawnDump" },17 { "name": "AppSpawnDump" },
18 { "name": "RegisterExpandSandboxCfgHandler"},18 { "name": "RegisterExpandSandboxCfgHandler"},
19- { "name": "GetAppSpawnMgr" }19+ { "name": "GetAppSpawnMgr" },
20+ { "name": "SetUidGid" },
21+ { "name": "SetCapabilities" },
22+ { "name": "SetAmbientCapabilities" },
23+ { "name": "SetSelinuxCon" },
24+ { "name": "ExecvTarget" },
25+ { "name": "SpawnPolicyInit" },
26+ { "name": "ResolvePolicy" }
20]27]
@@ -52,6 +52,8 @@ ohos_executable("appspawn") {
52 "${appspawn_path}/common/appspawn_trace.cpp",52 "${appspawn_path}/common/appspawn_trace.cpp",
53 "${appspawn_path}/modules/common/appspawn_dfx_dump.cpp",53 "${appspawn_path}/modules/common/appspawn_dfx_dump.cpp",
54 "${appspawn_path}/modules/modulemgr/appspawn_modulemgr.c",54 "${appspawn_path}/modules/modulemgr/appspawn_modulemgr.c",
55+ "${appspawn_path}/modules/module_engine/spawn_primitives.c",
56+ "${appspawn_path}/modules/module_engine/spawn_policy.c",
55 "${appspawn_path}/standard/appspawn_appmgr.c",57 "${appspawn_path}/standard/appspawn_appmgr.c",
56 "${appspawn_path}/standard/appspawn_fd_manager.c",58 "${appspawn_path}/standard/appspawn_fd_manager.c",
57 "${appspawn_path}/standard/appspawn_kickdog.c",59 "${appspawn_path}/standard/appspawn_kickdog.c",
@@ -191,6 +193,8 @@ ohos_executable("cjappspawn") {
191 "${appspawn_path}/common/appspawn_server.c",193 "${appspawn_path}/common/appspawn_server.c",
192 "${appspawn_path}/common/appspawn_trace.cpp",194 "${appspawn_path}/common/appspawn_trace.cpp",
193 "${appspawn_path}/modules/modulemgr/appspawn_modulemgr.c",195 "${appspawn_path}/modules/modulemgr/appspawn_modulemgr.c",
196+ "${appspawn_path}/modules/module_engine/spawn_primitives.c",
197+ "${appspawn_path}/modules/module_engine/spawn_policy.c",
194 "${appspawn_path}/standard/appspawn_appmgr.c",198 "${appspawn_path}/standard/appspawn_appmgr.c",
195 "${appspawn_path}/standard/appspawn_fd_manager.c",199 "${appspawn_path}/standard/appspawn_fd_manager.c",
196 "${appspawn_path}/standard/appspawn_kickdog.c",200 "${appspawn_path}/standard/appspawn_kickdog.c",
@@ -264,6 +268,8 @@ ohos_executable("nativespawn") {
264 "${appspawn_path}/common/appspawn_server.c",268 "${appspawn_path}/common/appspawn_server.c",
265 "${appspawn_path}/common/appspawn_trace.cpp",269 "${appspawn_path}/common/appspawn_trace.cpp",
266 "${appspawn_path}/modules/modulemgr/appspawn_modulemgr.c",270 "${appspawn_path}/modules/modulemgr/appspawn_modulemgr.c",
271+ "${appspawn_path}/modules/module_engine/spawn_primitives.c",
272+ "${appspawn_path}/modules/module_engine/spawn_policy.c",
267 "${appspawn_path}/standard/appspawn_appmgr.c",273 "${appspawn_path}/standard/appspawn_appmgr.c",
268 "${appspawn_path}/standard/appspawn_fd_manager.c",274 "${appspawn_path}/standard/appspawn_fd_manager.c",
269 "${appspawn_path}/standard/appspawn_kickdog.c",275 "${appspawn_path}/standard/appspawn_kickdog.c",
@@ -352,6 +358,8 @@ ohos_executable("nwebspawn") {
352 "${appspawn_path}/common/appspawn_trace.cpp",358 "${appspawn_path}/common/appspawn_trace.cpp",
353 "${appspawn_path}/modules/common/appspawn_dfx_dump.cpp",359 "${appspawn_path}/modules/common/appspawn_dfx_dump.cpp",
354 "${appspawn_path}/modules/modulemgr/appspawn_modulemgr.c",360 "${appspawn_path}/modules/modulemgr/appspawn_modulemgr.c",
361+ "${appspawn_path}/modules/module_engine/spawn_primitives.c",
362+ "${appspawn_path}/modules/module_engine/spawn_policy.c",
355 "${appspawn_path}/standard/appspawn_appmgr.c",363 "${appspawn_path}/standard/appspawn_appmgr.c",
356 "${appspawn_path}/standard/appspawn_fd_manager.c",364 "${appspawn_path}/standard/appspawn_fd_manager.c",
357 "${appspawn_path}/standard/appspawn_kickdog.c",365 "${appspawn_path}/standard/appspawn_kickdog.c",
@@ -442,6 +450,8 @@ ohos_executable("hybridspawn") {
442 "${appspawn_path}/common/appspawn_trace.cpp",450 "${appspawn_path}/common/appspawn_trace.cpp",
443 "${appspawn_path}/modules/common/appspawn_dfx_dump.cpp",451 "${appspawn_path}/modules/common/appspawn_dfx_dump.cpp",
444 "${appspawn_path}/modules/modulemgr/appspawn_modulemgr.c",452 "${appspawn_path}/modules/modulemgr/appspawn_modulemgr.c",
453+ "${appspawn_path}/modules/module_engine/spawn_primitives.c",
454+ "${appspawn_path}/modules/module_engine/spawn_policy.c",
445 "${appspawn_path}/standard/appspawn_appmgr.c",455 "${appspawn_path}/standard/appspawn_appmgr.c",
446 "${appspawn_path}/standard/appspawn_fd_manager.c",456 "${appspawn_path}/standard/appspawn_fd_manager.c",
447 "${appspawn_path}/standard/appspawn_kickdog.c",457 "${appspawn_path}/standard/appspawn_kickdog.c",
@@ -18,6 +18,7 @@
18#include <stdlib.h>18#include <stdlib.h>
19 19 
20#include "appspawn_hook.h"20#include "appspawn_hook.h"
21+#include "spawn_policy.h"
21#include "appspawn_modulemgr.h"22#include "appspawn_modulemgr.h"
22#include "appspawn_manager.h"23#include "appspawn_manager.h"
23#include "appspawn_service.h"24#include "appspawn_service.h"
@@ -109,6 +110,7 @@ int main(int argc, char *const argv[])
109 return 0;110 return 0;
110 }111 }
111 InitCommonEnv();112 InitCommonEnv();
113+ (void)SpawnPolicyInit(); // 启动期加载策略表(P2 旁路态,仅 app 策略生效,daemon 分类 P4 启用)
112 CheckPreload(argv);114 CheckPreload(argv);
113 (void)signal(SIGPIPE, SIG_IGN);115 (void)signal(SIGPIPE, SIG_IGN);
114 uint32_t argvSize = end - start;116 uint32_t argvSize = end - start;