1 /*
2 * Copyright (c) 2025 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 <cerrno>
17 #include <cstring>
18 #include <dlfcn.h>
19 #include <set>
20 #include <string>
21 #include <unistd.h>
22 #include <utility>
23 #include <vector>
24
25 #include "appspawn_hook.h"
26 #include "appspawn_server.h"
27 #include "appspawn_service.h"
28 #include "appspawn_manager.h"
29 #include "appspawn_utils.h"
30 #ifdef ARKWEB_UTILS_ENABLE
31 #include "arkweb_utils.h"
32 #endif
33 #include "command_lexer.h"
34 #include "config_policy_utils.h"
35 #include "hitrace_meter.h"
36 #include "js_runtime.h"
37 #include "json_utils.h"
38 #include "parameter.h"
39 #include "parameters.h"
40 #include "resource_manager.h"
41 #ifndef APPSPAWN_TEST
42 #include "ace_forward_compatibility.h"
43 #include "main_thread.h"
44 #include "runtime.h"
45 #endif
46
47 using namespace OHOS::AppSpawn;
48 using namespace OHOS::Global;
49
50 #define SYSTEMLIB_JSON "/appspawn_systemLib.json"
51 #ifdef ASAN_DETECTOR
52 static const bool DEFAULT_PRELOAD_VALUE = false;
53 #else
54 static const bool DEFAULT_PRELOAD_VALUE = true;
55 #endif
56 static const bool DEFAULT_PRELOAD_ETS_VALUE = true;
57 static const std::string PRELOAD_JSON_CONFIG("/appspawn_preload.json");
58 static const std::string PRELOAD_ETS_JSON_CONFIG("/appspawn_preload_ets.json");
59
60 typedef struct TagParseJsonContext {
61 std::set<std::string> names;
62 std::string key;
63 } ParseJsonContext;
64
GetNames(const cJSON * root,std::set<std::string> & names,std::string key)65 static void GetNames(const cJSON *root, std::set<std::string> &names, std::string key)
66 {
67 cJSON *namesJson = cJSON_GetObjectItemCaseSensitive(root, key.c_str());
68 APPSPAWN_CHECK_ONLY_EXPER(namesJson != nullptr, return);
69
70 uint32_t count = (uint32_t)cJSON_GetArraySize(namesJson);
71 for (uint32_t i = 0; i < count; ++i) {
72 const char *name = cJSON_GetStringValue(cJSON_GetArrayItem(namesJson, i));
73 APPSPAWN_CHECK_ONLY_EXPER(name != nullptr, continue);
74 APPSPAWN_LOGV("name %{public}s", name);
75 if (!names.count(name)) {
76 names.insert(name);
77 }
78 }
79 }
80
GetNameSet(const cJSON * root,ParseJsonContext * context)81 static int GetNameSet(const cJSON *root, ParseJsonContext *context)
82 {
83 GetNames(root, context->names, context->key);
84 return 0;
85 }
86
PreloadModule(bool isHybrid)87 static void PreloadModule(bool isHybrid)
88 {
89 bool preloadEts = OHOS::system::GetBoolParameter("persist.appspawn.preloadets", DEFAULT_PRELOAD_ETS_VALUE);
90 APPSPAWN_LOGI("LoadExtendLib: preloadets param value is %{public}s, isHybrid is %{public}s",
91 preloadEts ? "true" : "false", isHybrid ? "true" : "false");
92
93 OHOS::AbilityRuntime::Runtime::Options options;
94 options.loadAce = true;
95 options.preload = true;
96 options.lang = OHOS::AbilityRuntime::Runtime::Language::JS;
97 if (isHybrid && preloadEts) {
98 options.lang = OHOS::AbilityRuntime::Runtime::Language::ETS;
99 }
100 auto runtime = OHOS::AbilityRuntime::Runtime::Create(options);
101 if (!runtime) {
102 APPSPAWN_LOGE("LoadExtendLib: Failed to create runtime");
103 return;
104 }
105
106 ParseJsonContext jsContext = {{}, "napi"};
107 (void)ParseJsonConfig("etc/appspawn", PRELOAD_JSON_CONFIG.c_str(), GetNameSet, &jsContext);
108 for (std::string moduleName : jsContext.names) {
109 APPSPAWN_LOGI("moduleName %{public}s", moduleName.c_str());
110 runtime->PreloadSystemModule(moduleName);
111 }
112
113 ParseJsonContext etsContext = {{}, "class"};
114 (void)ParseJsonConfig("etc/appspawn", PRELOAD_ETS_JSON_CONFIG.c_str(), GetNameSet, &etsContext);
115 for (std::string className : etsContext.names) {
116 APPSPAWN_LOGI("className %{public}s", className.c_str());
117 runtime->PreloadSystemClass(className.c_str());
118 }
119
120 OHOS::AbilityRuntime::Runtime::SavePreloaded(std::move(runtime));
121 }
122
LoadExtendLib(bool isHybrid)123 static void LoadExtendLib(bool isHybrid)
124 {
125 const char *acelibdir = OHOS::Ace::AceForwardCompatibility::GetAceLibName();
126 APPSPAWN_LOGI("LoadExtendLib: Start calling dlopen acelibdir");
127 void *aceAbilityLib = dlopen(acelibdir, RTLD_NOW | RTLD_LOCAL);
128 APPSPAWN_CHECK(aceAbilityLib != nullptr, return, "Fail to dlopen %{public}s, [%{public}s]", acelibdir, dlerror());
129 APPSPAWN_LOGI("LoadExtendLib: Success to dlopen %{public}s", acelibdir);
130
131 OHOS::AppExecFwk::MainThread::PreloadExtensionPlugin();
132 bool preload = OHOS::system::GetBoolParameter("persist.appspawn.preload", DEFAULT_PRELOAD_VALUE);
133 if (!preload) {
134 APPSPAWN_LOGI("LoadExtendLib: Do not preload VM");
135 return;
136 }
137
138 APPSPAWN_LOGI("LoadExtendLib: Start preload VM");
139 SetTraceDisabled(true);
140 PreloadModule(isHybrid);
141 SetTraceDisabled(false);
142
143 Resource::ResourceManager *systemResMgr = Resource::GetSystemResourceManagerNoSandBox();
144 APPSPAWN_CHECK(systemResMgr != nullptr, return, "Fail to get system resource manager");
145 APPSPAWN_LOGI("LoadExtendLib: End preload VM");
146 }
147
PreloadCJLibs(void)148 static void PreloadCJLibs(void)
149 {
150 const char* cjEnvLibName = "libcj_environment.z.so";
151 const char* cjEnvInitName = "OHOS_InitSpawnEnv";
152 void* cjEnvLib = dlopen(cjEnvLibName, RTLD_NOW | RTLD_LOCAL);
153 APPSPAWN_CHECK(cjEnvLib != nullptr, return, "Failed to dlopen %{public}s, [%{public}s]", cjEnvLibName, dlerror());
154 auto symbol = dlsym(cjEnvLib, cjEnvInitName);
155 if (!symbol) {
156 dlclose(cjEnvLib);
157 APPSPAWN_LOGE("Failed to dlsym %{public}s, [%{public}s]", cjEnvInitName, dlerror());
158 return;
159 }
160 auto initSpawnEnv = reinterpret_cast<void (*)()>(symbol);
161 initSpawnEnv();
162 }
163
LoadExtendCJLib(void)164 static void LoadExtendCJLib(void)
165 {
166 const char *acelibdir = OHOS::Ace::AceForwardCompatibility::GetAceLibName();
167 APPSPAWN_LOGI("LoadExtendLib: Start calling dlopen acelibdir.");
168 void *aceAbilityLib = dlopen(acelibdir, RTLD_NOW | RTLD_LOCAL);
169 APPSPAWN_CHECK(aceAbilityLib != nullptr, return, "Fail to dlopen %{public}s, [%{public}s]", acelibdir, dlerror());
170 APPSPAWN_LOGI("LoadExtendLib: Success to dlopen %{public}s", acelibdir);
171
172 OHOS::AppExecFwk::MainThread::PreloadExtensionPlugin();
173
174 PreloadCJLibs();
175 }
176
BuildFdInfoMap(const AppSpawnMsgNode * message,std::map<std::string,int> & fdMap,int isColdRun)177 static int BuildFdInfoMap(const AppSpawnMsgNode *message, std::map<std::string, int> &fdMap, int isColdRun)
178 {
179 APPSPAWN_CHECK_ONLY_EXPER(message != NULL && message->buffer != NULL, return -1);
180 APPSPAWN_CHECK_ONLY_EXPER(message->tlvOffset != NULL, return -1);
181 int findFdIndex = 0;
182 AppSpawnMsgReceiverCtx recvCtx;
183 if (!isColdRun) {
184 APPSPAWN_CHECK_ONLY_EXPER(message->connection != NULL, return -1);
185 recvCtx = message->connection->receiverCtx;
186 if (recvCtx.fdCount <= 0) {
187 APPSPAWN_LOGI("no need to build fd info %{public}d, %{public}d", recvCtx.fds != NULL, recvCtx.fdCount);
188 return 0;
189 }
190 }
191 for (uint32_t index = TLV_MAX; index < (TLV_MAX + message->tlvCount); index++) {
192 if (message->tlvOffset[index] == INVALID_OFFSET) {
193 return -1;
194 }
195 uint8_t *data = message->buffer + message->tlvOffset[index];
196 if (((AppSpawnTlv *)data)->tlvType != TLV_MAX) {
197 continue;
198 }
199 AppSpawnTlvExt *tlv = (AppSpawnTlvExt *)data;
200 if (strcmp(tlv->tlvName, MSG_EXT_NAME_APP_FD) != 0) {
201 continue;
202 }
203 std::string key((char *)data + sizeof(AppSpawnTlvExt));
204 if (isColdRun) {
205 std::string envKey = std::string(APP_FDENV_PREFIX) + key;
206 char *fdChar = getenv(envKey.c_str());
207 APPSPAWN_CHECK(fdChar != NULL, continue, "getfd from env failed %{public}s", envKey.c_str());
208 int fd = atoi(fdChar);
209 APPSPAWN_CHECK(fd > 0, continue, "getfd from env atoi errno %{public}s,%{public}d", envKey.c_str(), fd);
210 fdMap[key] = fd;
211 } else {
212 APPSPAWN_CHECK(findFdIndex < recvCtx.fdCount && recvCtx.fds[findFdIndex] > 0,
213 return -1, "invalid fd info %{public}d %{public}d", findFdIndex, recvCtx.fds[findFdIndex]);
214 fdMap[key] = recvCtx.fds[findFdIndex++];
215 if (findFdIndex >= recvCtx.fdCount) {
216 break;
217 }
218 }
219 }
220 return 0;
221 }
222
RunChildThread(const AppSpawnMgr * content,const AppSpawningCtx * property)223 APPSPAWN_STATIC int RunChildThread(const AppSpawnMgr *content, const AppSpawningCtx *property)
224 {
225 std::string checkExit;
226 if (OHOS::system::GetBoolParameter("persist.init.debug.checkexit", true)) {
227 checkExit = std::to_string(getpid());
228 }
229 setenv(APPSPAWN_CHECK_EXIT, checkExit.c_str(), true);
230 if (CheckAppMsgFlagsSet(property, APP_FLAGS_CHILDPROCESS)) {
231 std::map<std::string, int> fdMap;
232 BuildFdInfoMap(property->message, fdMap, IsColdRunMode(content));
233 AppSpawnEnvClear((AppSpawnContent *)&content->content, (AppSpawnClient *)&property->client);
234 OHOS::AppExecFwk::MainThread::StartChild(fdMap);
235 } else {
236 AppSpawnEnvClear((AppSpawnContent *)&content->content, (AppSpawnClient *)&property->client);
237 OHOS::AppExecFwk::MainThread::Start();
238 }
239 unsetenv(APPSPAWN_CHECK_EXIT);
240 return 0;
241 }
242
RunChildByRenderCmd(const AppSpawnMgr * content,const AppSpawningCtx * property)243 APPSPAWN_STATIC int RunChildByRenderCmd(const AppSpawnMgr *content, const AppSpawningCtx *property)
244 {
245 uint32_t len = 0;
246 char *renderCmd = reinterpret_cast<char *>(GetAppPropertyExt(property, MSG_EXT_NAME_RENDER_CMD, &len));
247 if (renderCmd == NULL || !IsDeveloperModeOn(property)) {
248 APPSPAWN_LOGE("Denied launching a native process: not in developer mode");
249 return -1;
250 }
251 APPSPAWN_LOGI("renderCmd %{public}s", renderCmd);
252 std::vector<std::string> args;
253 std::string command(renderCmd);
254 CommandLexer lexer(command);
255 if (!lexer.GetAllArguments(args)) {
256 return -1;
257 }
258 if (args.empty()) {
259 APPSPAWN_LOGE("Failed to run a native process: empty command %{public}s", renderCmd);
260 return -1;
261 }
262 std::vector<char *> options;
263 for (const auto &arg : args) {
264 options.push_back(const_cast<char *>(arg.c_str()));
265 }
266 options.push_back(nullptr);
267 // clear appspawn env, do not user any content and property
268 AppSpawnEnvClear((AppSpawnContent *)&content->content, (AppSpawnClient *)&property->client);
269 execvp(args[0].c_str(), options.data());
270 // If it succeeds calling execvp, it never returns.
271 int err = errno;
272 APPSPAWN_LOGE("Failed to launch a native process with execvp: %{public}s", strerror(err));
273 return 0;
274 }
275
RunChildProcessor(AppSpawnContent * content,AppSpawnClient * client)276 static int RunChildProcessor(AppSpawnContent *content, AppSpawnClient *client)
277 {
278 APPSPAWN_CHECK(client != NULL && content != NULL, return -1, "Invalid client");
279 AppSpawningCtx *property = reinterpret_cast<AppSpawningCtx *>(client);
280 int ret = 0;
281 if (GetAppSpawnMsgType(property) == MSG_SPAWN_NATIVE_PROCESS) {
282 ret = RunChildByRenderCmd(reinterpret_cast<AppSpawnMgr *>(content), property);
283 } else {
284 ret = RunChildThread(reinterpret_cast<AppSpawnMgr *>(content), property);
285 }
286 return ret;
287 }
288
PreLoadAppSpawn(AppSpawnMgr * content)289 APPSPAWN_STATIC int PreLoadAppSpawn(AppSpawnMgr *content)
290 {
291 if (IsNWebSpawnMode(content)) {
292 return 0;
293 }
294 // register
295 RegChildLooper(&content->content, RunChildProcessor);
296 if (strcmp(content->content.longProcName, CJAPPSPAWN_SERVER_NAME) == 0) {
297 LoadExtendCJLib();
298 return 0;
299 }
300
301 bool isHybrid = IsHybridSpawnMode(content) ? true : false;
302 APPSPAWN_LOGI("PreLoadAppSpawn: mode %{public}d, isHybrid is %{public}d", content->content.mode, isHybrid);
303 if (content->content.mode != MODE_FOR_APP_COLD_RUN) {
304 LoadExtendLib(isHybrid);
305 }
306 return 0;
307 }
308
DoDlopenLibs(const cJSON * root,ParseJsonContext * context)309 APPSPAWN_STATIC int DoDlopenLibs(const cJSON *root, ParseJsonContext *context)
310 {
311 cJSON *systemLibs = cJSON_GetObjectItemCaseSensitive(root, "systemLib");
312 if (systemLibs == nullptr) {
313 return 0;
314 }
315
316 uint32_t libsCount = (uint32_t)cJSON_GetArraySize(systemLibs);
317 for (uint32_t i = 0; i < libsCount; ++i) {
318 const char *libName = cJSON_GetStringValue(cJSON_GetArrayItem(systemLibs, i));
319 char reaLibPath[PATH_MAX] = {0};
320 if (libName == nullptr || realpath(libName, reaLibPath) == nullptr) {
321 continue;
322 }
323 APPSPAWN_LOGV("reaLibPath %{public}s", reaLibPath);
324 void *lib = dlopen(reaLibPath, RTLD_LAZY);
325 if (lib == nullptr) {
326 APPSPAWN_LOGE("FAILED to dlopen %{public}s %{public}s", libName, dlerror());
327 }
328 }
329 return 0;
330 }
331
DlopenAppSpawn(AppSpawnMgr * content)332 APPSPAWN_STATIC int DlopenAppSpawn(AppSpawnMgr *content)
333 {
334 if (!(IsAppSpawnMode(content) || IsHybridSpawnMode(content))) {
335 return 0;
336 }
337
338 (void)ParseJsonConfig("etc/appspawn", SYSTEMLIB_JSON, DoDlopenLibs, nullptr);
339
340 APPSPAWN_LOGI("DlopenAppSpawn: Start reclaim file cache");
341 OHOS::Ace::AceForwardCompatibility::ReclaimFileCache(getpid());
342 #ifdef ARKWEB_UTILS_ENABLE
343 OHOS::ArkWeb::PreloadArkWebLibForBrowser();
344 #endif
345 return 0;
346 }
347
MODULE_CONSTRUCTOR(void)348 MODULE_CONSTRUCTOR(void)
349 {
350 APPSPAWN_LOGV("Load ace module ...");
351 AddPreloadHook(HOOK_PRIO_HIGHEST, PreLoadAppSpawn);
352 AddPreloadHook(HOOK_PRIO_HIGHEST, DlopenAppSpawn);
353 }
354