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