已开启
appspawn: PATH环境变量调整优先级及弱沙箱应用保持原有顺序 #2947
soulrequiem创建于 3 天前
appspawn: PATH环境变量调整优先级及弱沙箱应用保持原有顺序 #2947
已开启
共 8 个文件变更+247-14
| @@ -88,7 +88,7 @@ hnp安装包路径 | |||
| 88 | | 公有 | `/data/app/el1/bundle/<uid>/hnppublic/<name>.org/<name>_<ver>` | `/data/service/hnp/bin` | `HNP_PUBLIC_HOME=/data/service/hnp` | | 88 | | 公有 | `/data/app/el1/bundle/<uid>/hnppublic/<name>.org/<name>_<ver>` | `/data/service/hnp/bin` | `HNP_PUBLIC_HOME=/data/service/hnp` | |
| 89 | | 私有 | `/data/app/el1/bundle/<uid>/hnp/<hapPkg>/<name>.org/<name>_<ver>` | `/data/app/<bundleName>/...` | `HNP_PRIVATE_HOME=/data/app` | | 89 | | 私有 | `/data/app/el1/bundle/<uid>/hnp/<hapPkg>/<name>.org/<name>_<ver>` | `/data/app/<bundleName>/...` | `HNP_PRIVATE_HOME=/data/app` | |
| 90 | 90 | ||
| 91 | -- `HNP_PRIVATE_HOME` 排在 `HNP_PUBLIC_HOME` **之前**,同名二进制优先执行私有路径下的。 | 91 | +- `HNP_PRIVATE_HOME` 排在 `HNP_PUBLIC_HOME` **之前**,且系统 PATH 居中,优先级为:私有 hnp > 系统自带命令 > 公有 hnp。同名二进制优先执行私有路径下的,其次为系统自带命令,最后为公有 hnp 路径下的。 |
| 92 | - 公有 hnp 可被所有应用访问;私有 hnp 仅安装它的 HAP 可访问。 | 92 | - 公有 hnp 可被所有应用访问;私有 hnp 仅安装它的 HAP 可访问。 |
| 93 | - 公有 hnp 安装信息记录在 `hnp_info_<uid>.json`(旧路径 `hnp_info.json`),私有 hnp 不记入此文件。 | 93 | - 公有 hnp 安装信息记录在 `hnp_info_<uid>.json`(旧路径 `hnp_info.json`),私有 hnp 不记入此文件。 |
| 94 | 94 | ||
| @@ -402,11 +402,11 @@ appspawn 为每个应用进程注入环境变量(`util/src/appspawn_utils.c:40 | |||
| 402 | ```c | 402 | ```c |
| 403 | {"HNP_PRIVATE_HOME", "/data/app", false}, | 403 | {"HNP_PRIVATE_HOME", "/data/app", false}, |
| 404 | {"HNP_PUBLIC_HOME", "/data/service/hnp", false}, | 404 | {"HNP_PUBLIC_HOME", "/data/service/hnp", false}, |
| 405 | -{"PATH", "${HNP_PRIVATE_HOME}/bin:${HNP_PUBLIC_HOME}/bin:${PATH}", false}, | 405 | +{"PATH", "${HNP_PRIVATE_HOME}/bin:${PATH}:${HNP_PUBLIC_HOME}/bin", false}, |
| 406 | ``` | 406 | ``` |
| 407 | 407 | ||
| 408 | - `HNP_PRIVATE_HOME` 和 `HNP_PUBLIC_HOME` 指向沙箱内挂载点(与 11.2 的 sandbox-path 对应)。 | 408 | - `HNP_PRIVATE_HOME` 和 `HNP_PUBLIC_HOME` 指向沙箱内挂载点(与 11.2 的 sandbox-path 对应)。 |
| 409 | -- `PATH` 中 `HNP_PRIVATE_HOME/bin` 排在 `HNP_PUBLIC_HOME/bin` **前面**,同名二进制优先执行私有路径下的。 | 409 | +- `PATH` 中 `HNP_PRIVATE_HOME/bin` 排在 `${PATH}` **前面**,系统 PATH 居中,`HNP_PUBLIC_HOME/bin` 排在最后。优先级为:私有 hnp > 系统自带命令 > 公有 hnp。弱沙箱应用(custom_sandbox)通过 `SetLegacyHnpPathOrder` 恢复原有顺序(私有 hnp > 公有 hnp > 系统)。 |
| 410 | - 环境变量中的 `${HNP_PRIVATE_HOME}` 等占位符在注入前由 `ConvertEnvValue`(`appspawn_utils.c:50`)展开。 | 410 | - 环境变量中的 `${HNP_PRIVATE_HOME}` 等占位符在注入前由 `ConvertEnvValue`(`appspawn_utils.c:50`)展开。 |
| 411 | 411 | ||
| 412 | ### 11.5 运行时视角 | 412 | ### 11.5 运行时视角 |
| @@ -128,6 +128,19 @@ APPSPAWN_STATIC int32_t LoadCustomSandboxEnv(const char* envFilePath) | |||
| 128 | return APPSPAWN_OK; | 128 | return APPSPAWN_OK; |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | +// Restore legacy HNP PATH order before SpawnSetAppEnv (AppEnv JSON) so that | ||
| 132 | +// app-declared env in AppEnv JSON and /etc/environment can still override it. | ||
| 133 | +APPSPAWN_STATIC int SpawnSetLegacyHnpPath(AppSpawnMgr *content, AppSpawningCtx *property) | ||
| 134 | +{ | ||
| 135 | + APPSPAWN_LOGV("Spawning: set SpawnSetLegacyHnpPath."); | ||
| 136 | + if (CheckAppMsgFlagsSet(property, APP_FLAGS_CUSTOM_SANDBOX)) { | ||
| 137 | + // custom sandbox (weak sandbox) apps keep legacy PATH order: | ||
| 138 | + // HNP_PRIVATE > HNP_PUBLIC > system | ||
| 139 | + SetLegacyHnpPathOrder(); | ||
| 140 | + } | ||
| 141 | + return APPSPAWN_OK; | ||
| 142 | +} | ||
| 143 | + | ||
| 131 | APPSPAWN_STATIC int SpawnSetCustomSandboxEnv(AppSpawnMgr *content, AppSpawningCtx *property) | 144 | APPSPAWN_STATIC int SpawnSetCustomSandboxEnv(AppSpawnMgr *content, AppSpawningCtx *property) |
| 132 | { | 145 | { |
| 133 | APPSPAWN_LOGV("Spawning: set SpawnSetCustomSandboxEnv."); | 146 | APPSPAWN_LOGV("Spawning: set SpawnSetCustomSandboxEnv."); |
| @@ -221,6 +234,8 @@ int GetUserId(uint32_t *userId) | |||
| 221 | 234 | ||
| 222 | MODULE_CONSTRUCTOR(void) | 235 | MODULE_CONSTRUCTOR(void) |
| 223 | { | 236 | { |
| 224 | - const int32_t priority = HOOK_PRIO_COMMON + 2; // after SpawnSetAppEnv | 237 | + // legacy PATH order runs before SpawnSetAppEnv (HOOK_PRIO_COMMON + 1) |
| 225 | - AddAppSpawnHook(STAGE_CHILD_PRE_COLDBOOT, priority, SpawnSetCustomSandboxEnv); | 238 | + // so AppEnv JSON and /etc/environment can override it |
| 239 | + AddAppSpawnHook(STAGE_CHILD_PRE_COLDBOOT, HOOK_PRIO_HIGHEST + 1, SpawnSetLegacyHnpPath); | ||
| 240 | + AddAppSpawnHook(STAGE_CHILD_PRE_COLDBOOT, HOOK_PRIO_COMMON + 2, SpawnSetCustomSandboxEnv); | ||
| 226 | } | 241 | } |
| @@ -57,7 +57,7 @@ hnp目录准备完成后,参考第4步在hap打包命令中用--hnp-path指定 | |||
| 57 | **规格:** | 57 | **规格:** |
| 58 | 58 | ||
| 59 | 1. 公有hnp包安装后应用进程沙箱路径为/data/service/hnp/xxx.org/xxx_yyy,私有hnp包安装后应用进程沙箱路径为/data/app/<bundleName>/xxx.org/xxx_yyy,其中xxx值hnp包hnp.json文件"name"字段,yyy值为hnp包中"version"字段。 | 59 | 1. 公有hnp包安装后应用进程沙箱路径为/data/service/hnp/xxx.org/xxx_yyy,私有hnp包安装后应用进程沙箱路径为/data/app/<bundleName>/xxx.org/xxx_yyy,其中xxx值hnp包hnp.json文件"name"字段,yyy值为hnp包中"version"字段。 |
| 60 | -2. 公有hnp包根路径的环境变量HNP_PUBLIC_HOME=/data/service/hnp,私有hnp包根路径的环境变量HNP_PRIVATE_HOME=/data/app。HNP_PRIVATE_HOME环境变量排序在HNP_PUBLIC_HOME前面,意味着如果存在同名二进制分别在公有hnp路径下和私有hnp路径下,优先执行私有hnp路径下二进制。 | 60 | +2. 公有hnp包根路径的环境变量HNP_PUBLIC_HOME=/data/service/hnp,私有hnp包根路径的环境变量HNP_PRIVATE_HOME=/data/app。PATH环境变量排序为HNP_PRIVATE_HOME/bin:${PATH}:HNP_PUBLIC_HOME/bin,即私有hnp路径优先级最高,其次为系统自带命令路径,最后为公有hnp路径。意味着如果存在同名二进制分别在私有hnp路径下、系统路径下和公有hnp路径下,优先执行私有hnp路径下二进制,其次为系统自带命令,最后为公有hnp路径下二进制。 |
| 61 | 3. 公有hnp包可以被所有应用访问,私有hnp包只允许被安装该hnp包的hap应用访问。 | 61 | 3. 公有hnp包可以被所有应用访问,私有hnp包只允许被安装该hnp包的hap应用访问。 |
| 62 | 4. 卸载hap应用会同步卸载该hap应用安装的所有hnp包,如果该hnp包中二进制正在被其他应用使用,则会导致hap应用卸载失败。 | 62 | 4. 卸载hap应用会同步卸载该hap应用安装的所有hnp包,如果该hnp包中二进制正在被其他应用使用,则会导致hap应用卸载失败。 |
| 63 | 5. Hap应用A和B安装先后安装同名公有hnp包(hnp.json文件中"name")。后安装的应用B会无法安装,需要卸载hap应用A或者将应用B中的公有hnp修改为私有hnp方可继续安装。 | 63 | 5. Hap应用A和B安装先后安装同名公有hnp包(hnp.json文件中"name")。后安装的应用B会无法安装,需要卸载hap应用A或者将应用B中的公有hnp修改为私有hnp方可继续安装。 |
| @@ -108,8 +108,8 @@ int main(int argc, char *const argv[]) | |||
| 108 | if (end == 0) { | 108 | if (end == 0) { |
| 109 | return 0; | 109 | return 0; |
| 110 | } | 110 | } |
| 111 | - InitCommonEnv(); | ||
| 112 | CheckPreload(argv); | 111 | CheckPreload(argv); |
| 112 | + InitCommonEnv(); | ||
| 113 | (void)signal(SIGPIPE, SIG_IGN); | 113 | (void)signal(SIGPIPE, SIG_IGN); |
| 114 | uint32_t argvSize = end - start; | 114 | uint32_t argvSize = end - start; |
| 115 | AppSpawnStartArg *arg; | 115 | AppSpawnStartArg *arg; |
Mtest/unittest/app_spawn_standard_test/app_spawn_custom_config_test/app_spawn_custom_config_test.cpp+60-1
| @@ -36,6 +36,7 @@ APPSPAWN_STATIC std::string_view TrimWhitespaceView(std::string_view str); | |||
| 36 | APPSPAWN_STATIC int32_t ParseEnvLine(const std::string &line, std::string &envName, std::string &envValue); | 36 | APPSPAWN_STATIC int32_t ParseEnvLine(const std::string &line, std::string &envName, std::string &envValue); |
| 37 | APPSPAWN_STATIC int32_t LoadCustomSandboxEnv(const char* envFilePath); | 37 | APPSPAWN_STATIC int32_t LoadCustomSandboxEnv(const char* envFilePath); |
| 38 | APPSPAWN_STATIC int SpawnSetCustomSandboxEnv(AppSpawnMgr *content, AppSpawningCtx *property); | 38 | APPSPAWN_STATIC int SpawnSetCustomSandboxEnv(AppSpawnMgr *content, AppSpawningCtx *property); |
| 39 | +APPSPAWN_STATIC int SpawnSetLegacyHnpPath(AppSpawnMgr *content, AppSpawningCtx *property); | ||
| 39 | APPSPAWN_STATIC int SetUidGid(const AppSpawnMgr *content, const AppSpawningCtx *property); | 40 | APPSPAWN_STATIC int SetUidGid(const AppSpawnMgr *content, const AppSpawningCtx *property); |
| 40 | 41 | ||
| 41 | extern "C" { | 42 | extern "C" { |
| @@ -105,9 +106,67 @@ HWTEST_F(AppSpawnCustomConfigTest, SpawnSetCustomSandboxEnv_01, TestSize.Level0) | |||
| 105 | ASSERT_EQ(ret, 0); | 106 | ASSERT_EQ(ret, 0); |
| 106 | } | 107 | } |
| 107 | 108 | ||
| 109 | +/** | ||
| 110 | + * @brief SpawnSetLegacyHnpPath_01 - custom_sandbox flag set, triggers SetLegacyHnpPathOrder | ||
| 111 | + */ | ||
| 112 | +HWTEST_F(AppSpawnCustomConfigTest, SpawnSetLegacyHnpPath_01, TestSize.Level0) | ||
| 113 | +{ | ||
| 114 | + int ret = -1; | ||
| 115 | + AppSpawnClientHandle clientHandle = nullptr; | ||
| 116 | + AppSpawningCtx *property = nullptr; | ||
| 117 | + do { | ||
| 118 | + ret = AppSpawnClientInit(APPSPAWN_SERVER_NAME, &clientHandle); | ||
| 119 | + APPSPAWN_CHECK(ret == 0, break, "Failed to create client %{public}s", APPSPAWN_SERVER_NAME); | ||
| 120 | + AppSpawnReqMsgHandle reqHandle = g_testHelper.CreateMsg(clientHandle, MSG_APP_SPAWN, 0); | ||
| 121 | + APPSPAWN_CHECK(reqHandle != nullptr, break, "Failed to create msg"); | ||
| 122 | + AppSpawnReqMsgSetAppFlag(reqHandle, APP_FLAGS_CUSTOM_SANDBOX); | ||
| 123 | + property = g_testHelper.GetAppProperty(clientHandle, reqHandle); | ||
| 124 | + APPSPAWN_CHECK(property != nullptr, break, "Failed to get app property"); | ||
| 125 | + | ||
| 126 | + InitCommonEnv(); | ||
| 127 | + setenv("PATH", "/data/app/bin:/system/bin:/data/service/hnp/bin", 1); | ||
| 128 | + ret = SpawnSetLegacyHnpPath(nullptr, property); | ||
| 129 | + ASSERT_EQ(ret, 0); | ||
| 130 | + const char *env = getenv("PATH"); | ||
| 131 | + ASSERT_NE(env, nullptr); | ||
| 132 | + EXPECT_STREQ(env, "/data/app/bin:/data/service/hnp/bin:/system/bin"); | ||
| 133 | + } while (0); | ||
| 134 | + DeleteAppSpawningCtx(property); | ||
| 135 | + AppSpawnClientDestroy(clientHandle); | ||
| 136 | + ASSERT_EQ(ret, 0); | ||
| 137 | +} | ||
| 138 | + | ||
| 139 | +/** | ||
| 140 | + * @brief SpawnSetLegacyHnpPath_02 - no custom_sandbox flag, skip reorder | ||
| 141 | + */ | ||
| 142 | +HWTEST_F(AppSpawnCustomConfigTest, SpawnSetLegacyHnpPath_02, TestSize.Level0) | ||
| 143 | +{ | ||
| 144 | + int ret = -1; | ||
| 145 | + AppSpawnClientHandle clientHandle = nullptr; | ||
| 146 | + AppSpawningCtx *property = nullptr; | ||
| 147 | + do { | ||
| 148 | + ret = AppSpawnClientInit(APPSPAWN_SERVER_NAME, &clientHandle); | ||
| 149 | + APPSPAWN_CHECK(ret == 0, break, "Failed to create client %{public}s", APPSPAWN_SERVER_NAME); | ||
| 150 | + AppSpawnReqMsgHandle reqHandle = g_testHelper.CreateMsg(clientHandle, MSG_APP_SPAWN, 0); | ||
| 151 | + APPSPAWN_CHECK(reqHandle != nullptr, break, "Failed to create msg"); | ||
| 152 | + // no APP_FLAGS_CUSTOM_SANDBOX set | ||
| 153 | + property = g_testHelper.GetAppProperty(clientHandle, reqHandle); | ||
| 154 | + APPSPAWN_CHECK(property != nullptr, break, "Failed to get app property"); | ||
| 155 | + | ||
| 156 | + setenv("PATH", "/data/app/bin:/system/bin:/data/service/hnp/bin", 1); | ||
| 157 | + ret = SpawnSetLegacyHnpPath(nullptr, property); | ||
| 158 | + ASSERT_EQ(ret, 0); | ||
| 159 | + const char *env = getenv("PATH"); | ||
| 160 | + ASSERT_NE(env, nullptr); | ||
| 161 | + EXPECT_STREQ(env, "/data/app/bin:/system/bin:/data/service/hnp/bin"); | ||
| 162 | + } while (0); | ||
| 163 | + DeleteAppSpawningCtx(property); | ||
| 164 | + AppSpawnClientDestroy(clientHandle); | ||
| 165 | + ASSERT_EQ(ret, 0); | ||
| 166 | +} | ||
| 167 | + | ||
| 108 | /** | 168 | /** |
| 109 | * @brief App_Spawn_TrimWhitespace_01 | 169 | * @brief App_Spawn_TrimWhitespace_01 |
| 110 | - * | ||
| 111 | */ | 170 | */ |
| 112 | HWTEST_F(AppSpawnCustomConfigTest, App_Spawn_TrimWhitespace_01, TestSize.Level0) | 171 | HWTEST_F(AppSpawnCustomConfigTest, App_Spawn_TrimWhitespace_01, TestSize.Level0) |
| 113 | { | 172 | { |
| @@ -1110,10 +1110,15 @@ HWTEST_F(AppSpawnServiceTest, App_Spawn_InitCommonEnv_001, TestSize.Level0) | |||
| 1110 | EXPECT_EQ(strcmp(env, "/data/service/hnp"), 0); | 1110 | EXPECT_EQ(strcmp(env, "/data/service/hnp"), 0); |
| 1111 | } | 1111 | } |
| 1112 | env = getenv("PATH"); | 1112 | env = getenv("PATH"); |
| 1113 | - EXPECT_NE(env, nullptr); | 1113 | + ASSERT_NE(env, nullptr); |
| 1114 | - if (env != nullptr) { | 1114 | + // default order: HNP_PRIVATE > system > HNP_PUBLIC |
| 1115 | - EXPECT_NE(strstr(env, "/data/app/bin:/data/service/hnp/bin"), nullptr); | 1115 | + EXPECT_NE(strstr(env, "/data/app/bin:"), nullptr); |
| 1116 | - } | 1116 | + EXPECT_NE(strstr(env, ":/data/service/hnp/bin"), nullptr); |
| 1117 | + const char *publicPos = strstr(env, "/data/service/hnp/bin"); | ||
| 1118 | + const char *privatePos = strstr(env, "/data/app/bin"); | ||
| 1119 | + ASSERT_NE(publicPos, nullptr); | ||
| 1120 | + ASSERT_NE(privatePos, nullptr); | ||
| 1121 | + EXPECT_TRUE(publicPos > privatePos); | ||
| 1117 | } | 1122 | } |
| 1118 | env = getenv("HOME"); | 1123 | env = getenv("HOME"); |
| 1119 | EXPECT_NE(env, nullptr); | 1124 | EXPECT_NE(env, nullptr); |
| @@ -1137,6 +1142,110 @@ HWTEST_F(AppSpawnServiceTest, App_Spawn_InitCommonEnv_001, TestSize.Level0) | |||
| 1137 | } | 1142 | } |
| 1138 | } | 1143 | } |
| 1139 | 1144 | ||
| 1145 | +/** | ||
| 1146 | + * @brief SetLegacyHnpPathOrder: HNP segments in middle/tail positions | ||
| 1147 | + */ | ||
| 1148 | +HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_MiddleTail_001, TestSize.Level0) | ||
| 1149 | +{ | ||
| 1150 | + // PATH with hnp segments in middle and tail: system:hnp_private:system:hnp_public | ||
| 1151 | + setenv("PATH", "/system/bin:/data/app/bin:/vendor/bin:/data/service/hnp/bin", 1); | ||
| 1152 | + SetLegacyHnpPathOrder(); | ||
| 1153 | + const char *env = getenv("PATH"); | ||
| 1154 | + ASSERT_NE(env, nullptr); | ||
| 1155 | + // legacy order: hnp_private:hnp_public:system(remainder in original order) | ||
| 1156 | + EXPECT_STREQ(env, "/data/app/bin:/data/service/hnp/bin:/system/bin:/vendor/bin"); | ||
| 1157 | +} | ||
| 1158 | + | ||
| 1159 | +/** | ||
| 1160 | + * @brief SetLegacyHnpPathOrder: only hnp segments, no system path | ||
| 1161 | + */ | ||
| 1162 | +HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_HnpOnly_001, TestSize.Level0) | ||
| 1163 | +{ | ||
| 1164 | + // PATH with only hnp segments, no system path -> skip reorder | ||
| 1165 | + setenv("PATH", "/data/app/bin:/data/service/hnp/bin", 1); | ||
| 1166 | + SetLegacyHnpPathOrder(); | ||
| 1167 | + const char *env = getenv("PATH"); | ||
| 1168 | + ASSERT_NE(env, nullptr); | ||
| 1169 | + // unchanged: no system segment to reorder | ||
| 1170 | + EXPECT_STREQ(env, "/data/app/bin:/data/service/hnp/bin"); | ||
| 1171 | +} | ||
| 1172 | + | ||
| 1173 | +/** | ||
| 1174 | + * @brief SetLegacyHnpPathOrder: PATH is empty string | ||
| 1175 | + */ | ||
| 1176 | +HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_EmptyPath_001, TestSize.Level0) | ||
| 1177 | +{ | ||
| 1178 | + setenv("PATH", "", 1); | ||
| 1179 | + SetLegacyHnpPathOrder(); | ||
| 1180 | + const char *env = getenv("PATH"); | ||
| 1181 | + ASSERT_NE(env, nullptr); | ||
| 1182 | + // empty PATH: no reorder, unchanged | ||
| 1183 | + EXPECT_STREQ(env, ""); | ||
| 1184 | +} | ||
| 1185 | + | ||
| 1186 | +/** | ||
| 1187 | + * @brief SetLegacyHnpPathOrder: PATH with duplicate hnp segments | ||
| 1188 | + */ | ||
| 1189 | +HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_DuplicateHnp_001, TestSize.Level0) | ||
| 1190 | +{ | ||
| 1191 | + // duplicate hnp segments should be deduplicated in legacy output | ||
| 1192 | + setenv("PATH", "/data/app/bin:/system/bin:/data/app/bin:/data/service/hnp/bin:/vendor/bin", 1); | ||
| 1193 | + SetLegacyHnpPathOrder(); | ||
| 1194 | + const char *env = getenv("PATH"); | ||
| 1195 | + ASSERT_NE(env, nullptr); | ||
| 1196 | + // hnp duplicates dropped, system remainder preserved in order | ||
| 1197 | + EXPECT_STREQ(env, "/data/app/bin:/data/service/hnp/bin:/system/bin:/vendor/bin"); | ||
| 1198 | +} | ||
| 1199 | + | ||
| 1200 | +/** | ||
| 1201 | + * @brief SetLegacyHnpPathOrder: PATH with only one hnp segment (missing public) | ||
| 1202 | + */ | ||
| 1203 | +HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_OneHnpOnly_001, TestSize.Level0) | ||
| 1204 | +{ | ||
| 1205 | + // only private hnp, no public hnp -> skip reorder | ||
| 1206 | + setenv("PATH", "/data/app/bin:/system/bin:/vendor/bin", 1); | ||
| 1207 | + SetLegacyHnpPathOrder(); | ||
| 1208 | + const char *env = getenv("PATH"); | ||
| 1209 | + ASSERT_NE(env, nullptr); | ||
| 1210 | + // unchanged: both hnp segments must exist to trigger reorder | ||
| 1211 | + EXPECT_STREQ(env, "/data/app/bin:/system/bin:/vendor/bin"); | ||
| 1212 | +} | ||
| 1213 | + | ||
| 1214 | +/** | ||
| 1215 | + * @brief SetLegacyHnpPathOrder: system path overflow (sysSeg exceeds MAX_ENV_VALUE_LEN) | ||
| 1216 | + */ | ||
| 1217 | +HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_Overflow_001, TestSize.Level0) | ||
| 1218 | +{ | ||
| 1219 | + // build a PATH with hnp segments + very long system paths to overflow sysSeg | ||
| 1220 | + std::string longPath = "/data/app/bin:"; | ||
| 1221 | + // each segment ~200 chars, need > 5 segments to overflow MAX_ENV_VALUE_LEN(1024) | ||
| 1222 | + // after dropping hnp segments, sysSeg must still exceed 1024 | ||
| 1223 | + std::string longSeg(200, 'x'); | ||
| 1224 | + for (int i = 0; i < 8; i++) { | ||
| 1225 | + longPath += "/" + longSeg + std::to_string(i) + ":"; | ||
| 1226 | + } | ||
| 1227 | + longPath += "/data/service/hnp/bin"; | ||
| 1228 | + setenv("PATH", longPath.c_str(), 1); | ||
| 1229 | + SetLegacyHnpPathOrder(); | ||
| 1230 | + const char *env = getenv("PATH"); | ||
| 1231 | + ASSERT_NE(env, nullptr); | ||
| 1232 | + // overflow: keep original PATH unchanged | ||
| 1233 | + EXPECT_STREQ(env, longPath.c_str()); | ||
| 1234 | +} | ||
| 1235 | + | ||
| 1236 | +/** | ||
| 1237 | + * @brief SetLegacyHnpPathOrder: hnp segments at head (default InitCommonEnv order) | ||
| 1238 | + */ | ||
| 1239 | +HWTEST_F(AppSpawnServiceTest, App_Spawn_SetLegacyHnpPathOrder_HeadPosition_001, TestSize.Level0) | ||
| 1240 | +{ | ||
| 1241 | + // default InitCommonEnv order: private:system:public | ||
| 1242 | + setenv("PATH", "/data/app/bin:/system/bin:/vendor/bin:/data/service/hnp/bin", 1); | ||
| 1243 | + SetLegacyHnpPathOrder(); | ||
| 1244 | + const char *env = getenv("PATH"); | ||
| 1245 | + ASSERT_NE(env, nullptr); | ||
| 1246 | + EXPECT_STREQ(env, "/data/app/bin:/data/service/hnp/bin:/system/bin:/vendor/bin"); | ||
| 1247 | +} | ||
| 1248 | + | ||
| 1140 | /** | 1249 | /** |
| 1141 | * @brief 测试环境变量转换功能是否正常 | 1250 | * @brief 测试环境变量转换功能是否正常 |
| 1142 | * | 1251 | * |
| @@ -163,6 +163,7 @@ void DumpCurrentDir(char *buffer, uint32_t bufferLen, const char *dirPath); | |||
| 163 | int CheckEnabled(const char *param, const char *value); | 163 | int CheckEnabled(const char *param, const char *value); |
| 164 | int IsDeveloperModeOpen(); | 164 | int IsDeveloperModeOpen(); |
| 165 | void InitCommonEnv(void); | 165 | void InitCommonEnv(void); |
| 166 | +void SetLegacyHnpPathOrder(void); | ||
| 166 | int ConvertEnvValue(const char *srcEnv, char *dstEnv, int len); | 167 | int ConvertEnvValue(const char *srcEnv, char *dstEnv, int len); |
| 167 | void SetNoShareFsEnable(bool enable); | 168 | void SetNoShareFsEnable(bool enable); |
| 168 | bool IsNoShareFsEnable(void); | 169 | bool IsNoShareFsEnable(void); |
| @@ -40,7 +40,7 @@ | |||
| 40 | static const AppSpawnCommonEnv COMMON_ENV[] = { | 40 | static const AppSpawnCommonEnv COMMON_ENV[] = { |
| 41 | {"HNP_PRIVATE_HOME", "/data/app", false}, | 41 | {"HNP_PRIVATE_HOME", "/data/app", false}, |
| 42 | {"HNP_PUBLIC_HOME", "/data/service/hnp", false}, | 42 | {"HNP_PUBLIC_HOME", "/data/service/hnp", false}, |
| 43 | - {"PATH", "${HNP_PRIVATE_HOME}/bin:${HNP_PUBLIC_HOME}/bin:${PATH}", false}, | 43 | + {"PATH", "${HNP_PRIVATE_HOME}/bin:${PATH}:${HNP_PUBLIC_HOME}/bin", false}, |
| 44 | {"HOME", "/storage/Users/currentUser", false}, | 44 | {"HOME", "/storage/Users/currentUser", false}, |
| 45 | {"TMPDIR", "/data/storage/el2/base/cache", false}, | 45 | {"TMPDIR", "/data/storage/el2/base/cache", false}, |
| 46 | {"SHELL", "/bin/sh", false}, | 46 | {"SHELL", "/bin/sh", false}, |
| @@ -117,10 +117,59 @@ void InitCommonEnv(void) | |||
| 117 | COMMON_ENV[i].envName, COMMON_ENV[i].envValue); | 117 | COMMON_ENV[i].envName, COMMON_ENV[i].envValue); |
| 118 | ret = setenv(COMMON_ENV[i].envName, envValue, true); | 118 | ret = setenv(COMMON_ENV[i].envName, envValue, true); |
| 119 | APPSPAWN_CHECK(ret == 0, return, "Set env fail name=%{public}s, value=%{public}s", | 119 | APPSPAWN_CHECK(ret == 0, return, "Set env fail name=%{public}s, value=%{public}s", |
| 120 | - COMMON_ENV[i].envName, envValue); | 120 | + COMMON_ENV[i].envName, COMMON_ENV[i].envValue); |
| 121 | } | 121 | } |
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | +// Collect non-hnp segments from path into sysSeg, return false on overflow | ||
| 125 | +static bool CollectSysSeg(const char *path, const char *privateSeg, const char *publicSeg, | ||
| 126 | + char *sysSeg, uint32_t sysSegLen) | ||
| 127 | +{ | ||
| 128 | + char *tmpPath = strdup(path); | ||
| 129 | + APPSPAWN_CHECK(tmpPath != NULL, return false, "Failed to strdup path"); | ||
| 130 | + bool collectSuccess = true; | ||
| 131 | + char *savePtr = NULL; | ||
| 132 | + for (char *seg = strtok_r(tmpPath, ":", &savePtr); seg != NULL; | ||
| 133 | + seg = strtok_r(NULL, ":", &savePtr)) { | ||
| 134 | + APPSPAWN_ONLY_EXPER(strcmp(seg, privateSeg) == 0 || strcmp(seg, publicSeg) == 0, continue); | ||
| 135 | + if (sysSeg[0] != '\0') { | ||
| 136 | + errno_t rc = strcat_s(sysSeg, sysSegLen, ":"); | ||
| 137 | + APPSPAWN_CHECK(rc == EOK, collectSuccess = false; break, "Failed to append colon"); | ||
| 138 | + } | ||
| 139 | + errno_t rc = strcat_s(sysSeg, sysSegLen, seg); | ||
| 140 | + APPSPAWN_CHECK(rc == EOK, collectSuccess = false; break, "Failed to append seg"); | ||
| 141 | + } | ||
| 142 | + free(tmpPath); | ||
| 143 | + return collectSuccess; | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +// Reorder current PATH (HNP_PRIVATE:system:HNP_PUBLIC) back to legacy order | ||
| 147 | +// (HNP_PRIVATE:HNP_PUBLIC:system) for custom sandbox apps. | ||
| 148 | +void SetLegacyHnpPathOrder(void) | ||
| 149 | +{ | ||
| 150 | + static const char *privateSeg = "/data/app/bin"; | ||
| 151 | + static const char *publicSeg = "/data/service/hnp/bin"; | ||
| 152 | + const char *path = getenv("PATH"); | ||
| 153 | + APPSPAWN_CHECK(path != NULL && path[0] != '\0', return, "Invalid env for legacy path order"); | ||
| 154 | + | ||
| 155 | + APPSPAWN_CHECK(strstr(path, privateSeg) != NULL && strstr(path, publicSeg) != NULL, return, | ||
| 156 | + "PATH has no hnp segments, keep current order"); | ||
| 157 | + | ||
| 158 | + char sysSeg[MAX_ENV_VALUE_LEN] = {0}; | ||
| 159 | + APPSPAWN_CHECK(CollectSysSeg(path, privateSeg, publicSeg, sysSeg, sizeof(sysSeg)), return, | ||
| 160 | + "system path overflow, keep current order"); | ||
| 161 | + // no system segment means PATH is already hnp-only, no reorder needed | ||
| 162 | + APPSPAWN_CHECK(sysSeg[0] != '\0', return, "PATH has only hnp segments, keep current order"); | ||
| 163 | + | ||
| 164 | + char newPath[MAX_ENV_VALUE_LEN] = {0}; | ||
| 165 | + int len = snprintf_s(newPath, sizeof(newPath), sizeof(newPath) - 1, | ||
| 166 | + "%s:%s:%s", privateSeg, publicSeg, sysSeg); | ||
| 167 | + APPSPAWN_CHECK(len > 0, return, "Failed to build new path"); | ||
| 168 | + APPSPAWN_LOGV("SetLegacyHnpPathOrder: %{public}s", newPath); | ||
| 169 | + int ret = setenv("PATH", newPath, true); | ||
| 170 | + APPSPAWN_CHECK(ret == 0, return, "Failed to set legacy path order"); | ||
| 171 | +} | ||
| 172 | + | ||
| 124 | uint64_t DiffTime(const struct timespec *startTime, const struct timespec *endTime) | 173 | uint64_t DiffTime(const struct timespec *startTime, const struct timespec *endTime) |
| 125 | { | 174 | { |
| 126 | APPSPAWN_CHECK_ONLY_EXPER(startTime != NULL, return 0); | 175 | APPSPAWN_CHECK_ONLY_EXPER(startTime != NULL, return 0); |