• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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