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