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 43using namespace OHOS::AppSpawn; 44using namespace OHOS::Global; 45 46#ifdef ASAN_DETECTOR 47static const bool DEFAULT_PRELOAD_VALUE = false; 48#else 49static const bool DEFAULT_PRELOAD_VALUE = true; 50#endif 51static const std::string PRELOAD_JSON_CONFIG("/appspawn_preload.json"); 52 53typedef struct TagParseJsonContext { 54 std::set<std::string> modules; 55} ParseJsonContext; 56 57static 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 78static int GetModuleSet(const cJSON *root, ParseJsonContext *context) 79{ 80 GetModules(root, context->modules); 81 return 0; 82} 83 84static 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 107static 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 133APPSPAWN_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 142APPSPAWN_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 188static 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 208APPSPAWN_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 241static int RunChildProcessor(AppSpawnContent *content, AppSpawnClient *client) 242{ 243 EnableCache(); 244 APPSPAWN_CHECK(client != NULL && content != NULL, return -1, "Invalid client"); 245 AppSpawningCtx *property = reinterpret_cast<AppSpawningCtx *>(client); 246 int ret = 0; 247 if (GetAppSpawnMsgType(property) == MSG_SPAWN_NATIVE_PROCESS) { 248 ret = RunChildByRenderCmd(reinterpret_cast<AppSpawnMgr *>(content), property); 249 } else { 250 ret = RunChildThread(reinterpret_cast<AppSpawnMgr *>(content), property); 251 } 252 return ret; 253} 254 255APPSPAWN_STATIC int PreLoadAppSpawn(AppSpawnMgr *content) 256{ 257 if (IsNWebSpawnMode(content)) { 258 return 0; 259 } 260 // register 261 RegChildLooper(&content->content, RunChildProcessor); 262 if (strcmp(content->content.longProcName, CJAPPSPAWN_SERVER_NAME) == 0) { 263 LoadExtendCJLib(); 264 return 0; 265 } 266 LoadExtendLib(); 267 return 0; 268} 269 270MODULE_CONSTRUCTOR(void) 271{ 272 APPSPAWN_LOGV("Load ace module ..."); 273 AddPreloadHook(HOOK_PRIO_HIGHEST, PreLoadAppSpawn); 274} 275