1 /*
2 * Copyright (c) 2021-2022 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 "appspawn_server.h"
17
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <signal.h>
23 #include <malloc.h>
24 #undef _GNU_SOURCE
25 #define _GNU_SOURCE
26 #include <sched.h>
27 #include <time.h>
28 #ifdef SECURITY_COMPONENT_ENABLE
29 #include "sec_comp_enhance_kit_c.h"
30 #endif
31
32 #define DEFAULT_UMASK 0002
33
DiffTime(struct timespec * startTime)34 long long DiffTime(struct timespec *startTime)
35 {
36 struct timespec tmEnd = {0};
37 clock_gettime(CLOCK_REALTIME, &tmEnd);
38 long long diff = (long long)((tmEnd.tv_sec - startTime->tv_sec) * 1000000); // 1000000 1000ms
39 if (tmEnd.tv_nsec > startTime->tv_nsec) {
40 diff += (tmEnd.tv_nsec - startTime->tv_nsec) / 1000; // 1000 ms
41 } else {
42 diff -= (startTime->tv_nsec - tmEnd.tv_nsec) / 1000; // 1000 ms
43 }
44 return diff;
45 }
46
NotifyResToParent(struct AppSpawnContent_ * content,AppSpawnClient * client,int result)47 static void NotifyResToParent(struct AppSpawnContent_ *content, AppSpawnClient *client, int result)
48 {
49 if (content->notifyResToParent != NULL) {
50 content->notifyResToParent(content, client, result);
51 }
52 }
53
ProcessExit(int code)54 static void ProcessExit(int code)
55 {
56 APPSPAWN_LOGI("App exit code: %{public}d", code);
57 #ifdef OHOS_LITE
58 _exit(0x7f); // 0x7f user exit
59 #else
60 #ifndef APPSPAWN_TEST
61 quick_exit(0);
62 #endif
63 #endif
64 }
65
66 #ifdef APPSPAWN_HELPER
67 __attribute__((visibility("default")))
68 _Noreturn
exit(int code)69 void exit(int code)
70 {
71 char *checkExit = getenv(APPSPAWN_CHECK_EXIT);
72 if (checkExit && atoi(checkExit) == getpid()) {
73 APPSPAWN_LOGF("Unexpected call: exit(%{public}d)", code);
74 abort();
75 }
76 // hook `exit` to `ProcessExit` to ensure app exit in a clean way
77 ProcessExit(code);
78 // should not come here
79 abort();
80 }
81 #endif
82
DoStartApp(struct AppSpawnContent_ * content,AppSpawnClient * client,char * longProcName,uint32_t longProcNameLen)83 int DoStartApp(struct AppSpawnContent_ *content, AppSpawnClient *client, char *longProcName, uint32_t longProcNameLen)
84 {
85 int32_t ret = 0;
86 APPSPAWN_LOGV("DoStartApp id %{public}d longProcNameLen %{public}u", client->id, longProcNameLen);
87 if (content->handleInternetPermission != NULL) {
88 content->handleInternetPermission(client);
89 }
90
91 if (content->setAppSandbox) {
92 ret = content->setAppSandbox(content, client);
93 APPSPAWN_CHECK(ret == 0, NotifyResToParent(content, client, ret);
94 return ret, "Failed to set app sandbox");
95 }
96
97 (void)umask(DEFAULT_UMASK);
98 if (content->setKeepCapabilities) {
99 ret = content->setKeepCapabilities(content, client);
100 APPSPAWN_CHECK(ret == 0, NotifyResToParent(content, client, ret);
101 return ret, "Failed to set KeepCapabilities");
102 }
103
104 if (content->setXpmConfig) {
105 ret = content->setXpmConfig(content, client);
106 APPSPAWN_CHECK(ret == 0, NotifyResToParent(content, client, ret);
107 return ret, "Failed to set setXpmConfig");
108 }
109
110 if (content->setProcessName) {
111 ret = content->setProcessName(content, client, content->longProcName, content->longProcNameLen);
112 APPSPAWN_CHECK(ret == 0, NotifyResToParent(content, client, ret);
113 return ret, "Failed to set setProcessName");
114 }
115
116 if (content->setUidGid) {
117 ret = content->setUidGid(content, client);
118 APPSPAWN_CHECK(ret == 0, NotifyResToParent(content, client, ret);
119 return ret, "Failed to setUidGid");
120 }
121
122 if (content->setFileDescriptors) {
123 ret = content->setFileDescriptors(content, client);
124 APPSPAWN_CHECK(ret == 0, NotifyResToParent(content, client, ret);
125 return ret, "Failed to setFileDescriptors");
126 }
127
128 if (content->setCapabilities) {
129 ret = content->setCapabilities(content, client);
130 APPSPAWN_CHECK(ret == 0, NotifyResToParent(content, client, ret);
131 return ret, "Failed to setCapabilities");
132 }
133
134 if (content->waitForDebugger) {
135 ret = content->waitForDebugger(client);
136 APPSPAWN_CHECK(ret == 0, NotifyResToParent(content, client, ret);
137 return ret, "Failed to waitForDebugger");
138 }
139
140 #ifdef SECURITY_COMPONENT_ENABLE
141 InitSecCompClientEnhance();
142 #endif
143
144 // notify success to father process and start app process
145 NotifyResToParent(content, client, 0);
146 return 0;
147 }
148
AppSpawnChild(void * arg)149 static int AppSpawnChild(void *arg)
150 {
151 APPSPAWN_CHECK(arg != NULL, return -1, "Invalid arg for appspawn child");
152 AppSandboxArg *sandbox = (AppSandboxArg *)arg;
153 struct AppSpawnContent_ *content = sandbox->content;
154 AppSpawnClient *client = sandbox->client;
155 int ret = -1;
156
157 if (content->setProcessName) {
158 ret = content->setProcessName(content, client, content->longProcName, content->longProcNameLen);
159 APPSPAWN_CHECK(ret == 0, NotifyResToParent(content, client, ret);
160 return ret, "Failed to set setProcessName");
161 }
162
163 #ifdef OHOS_DEBUG
164 struct timespec tmStart = {0};
165 clock_gettime(CLOCK_REALTIME, &tmStart);
166 #endif
167 // close socket id and signal for child
168 if (content->clearEnvironment != NULL) {
169 content->clearEnvironment(content, client);
170 }
171
172 if (content->setAppAccessToken != NULL) {
173 ret = content->setAppAccessToken(content, client);
174 if (ret != 0) {
175 APPSPAWN_LOGE("AppSpawnChild, set app token id failed");
176 return -1;
177 }
178 }
179
180 if ((content->getWrapBundleNameValue != NULL && content->getWrapBundleNameValue(content, client) == 0) ||
181 ((client->flags & APP_COLD_START) != 0)) {
182 // cold start fail, to start normal
183 if (content->coldStartApp != NULL && content->coldStartApp(content, client) == 0) {
184 return 0;
185 }
186 }
187 #ifndef OHOS_LITE
188 // enable cache for app process
189 mallopt(M_OHOS_CONFIG, M_TCACHE_PERFORMANCE_MODE);
190 mallopt(M_OHOS_CONFIG, M_ENABLE_OPT_TCACHE);
191 mallopt(M_SET_THREAD_CACHE, M_THREAD_CACHE_ENABLE);
192 mallopt(M_DELAYED_FREE, M_DELAYED_FREE_ENABLE);
193 #endif
194 ret = DoStartApp(content, client, content->longProcName, content->longProcNameLen);
195 if (content->initDebugParams != NULL) {
196 content->initDebugParams(content, client);
197 }
198 #ifdef OHOS_DEBUG
199 long long diff = DiffTime(&tmStart);
200 APPSPAWN_LOGI("App timeused %{public}d %lld ns.", getpid(), diff);
201 #endif
202 if (ret == 0 && content->runChildProcessor != NULL) {
203 content->runChildProcessor(content, client);
204 }
205 return 0;
206 }
207
CloneAppSpawn(void * arg)208 static int CloneAppSpawn(void *arg)
209 {
210 ProcessExit(AppSpawnChild(arg));
211 return 0;
212 }
213
AppSpawnProcessMsg(AppSandboxArg * sandbox,pid_t * childPid)214 int AppSpawnProcessMsg(AppSandboxArg *sandbox, pid_t *childPid)
215 {
216 APPSPAWN_CHECK(sandbox != NULL && sandbox->content != NULL, return -1, "Invalid content for appspawn");
217 APPSPAWN_CHECK(sandbox->client != NULL && childPid != NULL, return -1, "Invalid client for appspawn");
218 APPSPAWN_LOGI("AppSpawnProcessMsg id %{public}d 0x%{public}x", sandbox->client->id, sandbox->client->flags);
219
220 pid_t pid = 0;
221 if (sandbox->content->isNweb) {
222 pid = clone(CloneAppSpawn, NULL, sandbox->client->cloneFlags | SIGCHLD, (void *)sandbox);
223 } else {
224 pid = fork();
225 if (pid == 0) {
226 ProcessExit(AppSpawnChild((void *)sandbox));
227 }
228 }
229 APPSPAWN_CHECK(pid >= 0, return -errno, "fork child process error: %{public}d", -errno);
230 *childPid = pid;
231 return 0;
232 }
233
234