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 <errno.h>
17 #include <inttypes.h>
18 #include <limits.h>
19 #include <fcntl.h>
20 #include <signal.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27
28 #include "appspawn_adapter.h"
29 #include "appspawn_hook.h"
30 #include "appspawn_manager.h"
31 #include "appspawn_utils.h"
32 #include "securec.h"
33 #include "cJSON.h"
34 #include <sys/ioctl.h>
35
GetCgroupPath(const AppSpawnedProcessInfo * appInfo,char * buffer,uint32_t buffLen)36 APPSPAWN_STATIC int GetCgroupPath(const AppSpawnedProcessInfo *appInfo, char *buffer, uint32_t buffLen)
37 {
38 const int userId = appInfo->uid / UID_BASE;
39 #ifdef APPSPAWN_TEST
40 int ret = snprintf_s(buffer, buffLen, buffLen - 1, APPSPAWN_BASE_DIR "/dev/pids/testpids/%d/%s/%d/",
41 userId, appInfo->name, appInfo->pid);
42 #else
43 int ret = snprintf_s(buffer, buffLen, buffLen - 1, "/dev/pids/%d/%s/app_%d/", userId, appInfo->name, appInfo->pid);
44 #endif
45 APPSPAWN_CHECK(ret > 0, return ret, "Failed to snprintf_s errno: %{public}d", errno);
46 APPSPAWN_LOGV("Cgroup path %{public}s ", buffer);
47 return 0;
48 }
49
50
WriteToFile(const char * path,int truncated,pid_t pids[],uint32_t count)51 APPSPAWN_STATIC int WriteToFile(const char *path, int truncated, pid_t pids[], uint32_t count)
52 {
53 char pidName[32] = {0}; // 32 max len
54 int fd = open(path, O_RDWR | (truncated ? O_TRUNC : O_APPEND));
55 APPSPAWN_CHECK(fd >= 0, return APPSPAWN_SYSTEM_ERROR,
56 "Failed to open file errno: %{public}d path: %{public}s", errno, path);
57 int ret = 0;
58 for (uint32_t i = 0; i < count; i++) {
59 APPSPAWN_LOGV(" WriteToFile pid %{public}d ", pids[i]);
60 ret = snprintf_s(pidName, sizeof(pidName), sizeof(pidName) - 1, "%d\n", pids[i]);
61 APPSPAWN_CHECK(ret > 0, break, "Failed to snprintf_s errno: %{public}d", errno);
62 ret = write(fd, pidName, strlen(pidName));
63 APPSPAWN_CHECK(ret > 0, break,
64 "Failed to write file errno: %{public}d path: %{public}s %{public}s", errno, path, pidName);
65 ret = 0;
66 }
67 close(fd);
68 return ret;
69 }
70
71 #define APP_PIDS_MAX_ENCAPS "encaps"
72 #define APP_PIDS_MAX_OHOS_ENCAPS_COUNT_KEY "ohos.encaps.count"
73 #define APP_PIDS_MAX_OHOS_ENCAPS_FORK_KEV "ohos.encaps.fork"
74 #define APP_PIDS_MAX_OHOS_ENCAPS_FORK_DFX_KEY "ohos.encaps.fork.dfx"
75 #define APP_PIDS_MAX_OHOS_ENCAPS_FORK_WEBDFX_KEY "ohos.encaps.fork.webdfx"
76 #define APP_PIDS_MAX_OHOS_ENCAPS_COUNT_VALUE 3
77 #define APP_PIDS_MAX_OHOS_ENCAPS_FORK_DFX_AND_WEBDFX_VALUE 5
78 #define APP_ENABLE_ENCAPS 1
79 #define ASSICN_ENCAPS_CMD _lOW('E', 0x1A, char *)
WritePidMax(const char * path,uint32_t max)80 static int WritePidMax(const char *path, uint32_t max)
81 {
82 #if APP_ENABLE_ENCAPS == 0
83 cJSON *encaps = cJSON_CreateObject();
84 cJSON_AddNumberToObject(encaps, APP_PIDS_MAX_OHOS_ENCAPS_COUNT_KEY,
85 APP_PIDS_MAX_OHOS_ENCAPS_COUNT_VALUE);
86 cJSON_AddNumberToObject(encaps, APP_PIDS_MAX_OHOS_ENCAPS_FORK_KEV, max);
87 cJSON_AddNumberToObject(encaps, APP_PIDS_MAX_OHOS_ENCAPS_FORK_DFX_KEY,
88 APP_PIDS_MAX_OHOS_ENCAPS_FORK_DFX_AND_WEBDFX_VALUE);
89 cJSON_AddNumberToObject(encaps, APP_PIDS_MAX_OHOS_ENCAPS_FORK_WEBDFX_KEY,
90 APP_PIDS_MAX_OHOS_ENCAPS_FORK_DFX_AND_WEBDFX_VALUE);
91 cJSON *addGinseng = cJSON_CreateObject();
92 cJSON_AddItemToObject(addGinseng, APP_PIDS_MAX_ENCAPS, encaps);
93 char *maxPid = cJSON_PrintUnformatted(addGinseng);
94 int ret = 0;
95 int fd = 0;
96 fd = open("dev/encaps", O_RDWR);
97 ret = ioctl(fd, ASSICN_ENCAPS_CMD, maxPid);
98 close(fd);
99 free(maxPid);
100 cJSON_Delete(addGinseng);
101 cJSON_Delete(encaps);
102 return ret;
103 #else
104 char value[32] = {0}; // 32 max len
105 int fd = open(path, O_RDWR | O_TRUNC);
106 APPSPAWN_CHECK(fd >= 0, return -1,
107 "Failed to open file errno: %{public}d path: %{public}s", errno, path);
108 int ret = 0;
109 do {
110 ret = snprintf_s(value, sizeof(value), sizeof(value) - 1, "%u\n", max);
111 APPSPAWN_CHECK(ret > 0, break, "Failed to snprintf_s errno: %{public}d", errno);
112 ret = write(fd, value, strlen(value));
113 APPSPAWN_CHECK(ret > 0, break,
114 "Failed to write file errno: %{public}d path: %{public}s %{public}s %{public}d", errno, path, value, ret);
115 ret = 0;
116 } while (0);
117 close(fd);
118 return ret;
119 #endif
120 }
121
SetForkDenied(const AppSpawnedProcessInfo * appInfo)122 static void SetForkDenied(const AppSpawnedProcessInfo *appInfo)
123 {
124 char pathForkDenied[PATH_MAX] = {};
125 int ret = GetCgroupPath(appInfo, pathForkDenied, sizeof(pathForkDenied));
126 APPSPAWN_CHECK(ret == 0, return, "Failed to get cgroup path errno: %{public}d", errno);
127 ret = strcat_s(pathForkDenied, sizeof(pathForkDenied), "pids.fork_denied");
128 APPSPAWN_CHECK(ret == 0, return, "Failed to strcat_s fork_denied path errno: %{public}d", errno);
129 int fd = open(pathForkDenied, O_RDWR);
130 if (fd < 0) {
131 APPSPAWN_LOGW("SetForkDenied %{public}d open failed ", appInfo->pid);
132 return;
133 }
134 do {
135 ret = write(fd, "1", 1);
136 APPSPAWN_CHECK(ret >= 0, break,
137 "Failed to write file errno: %{public}d path: %{public}s %{public}d", errno, pathForkDenied, ret);
138 fsync(fd);
139 APPSPAWN_LOGI("SetForkDenied success, cgroup's owner:%{public}d", appInfo->pid);
140 } while (0);
141 close(fd);
142 }
143
KillProcessesByCGroup(const char * path,AppSpawnMgr * content,const AppSpawnedProcessInfo * appInfo)144 static void KillProcessesByCGroup(const char *path, AppSpawnMgr *content, const AppSpawnedProcessInfo *appInfo)
145 {
146 SetForkDenied(appInfo);
147 FILE *file = fopen(path, "r");
148 APPSPAWN_CHECK(file != NULL, return, "Open file fail %{public}s errno: %{public}d", path, errno);
149 pid_t pid = 0;
150 while (fscanf_s(file, "%d\n", &pid) == 1 && pid > 0) {
151 APPSPAWN_LOGV(" KillProcessesByCGroup pid %{public}d ", pid);
152 if (pid == appInfo->pid) {
153 continue;
154 }
155 AppSpawnedProcessInfo *tmp = GetSpawnedProcess(pid);
156 if (tmp != NULL) {
157 APPSPAWN_LOGI("Got app %{public}s in same group for pid %{public}d.", tmp->name, pid);
158 continue;
159 }
160 APPSPAWN_LOGI("Kill app pid %{public}d now ...", pid);
161 #ifndef APPSPAWN_TEST
162 if (kill(pid, SIGKILL) != 0) {
163 APPSPAWN_LOGE("unable to kill process, pid: %{public}d ret %{public}d", pid, errno);
164 }
165 #endif
166 }
167 (void)fclose(file);
168 }
169
ProcessMgrRemoveApp(const AppSpawnMgr * content,const AppSpawnedProcessInfo * appInfo)170 static int ProcessMgrRemoveApp(const AppSpawnMgr *content, const AppSpawnedProcessInfo *appInfo)
171 {
172 APPSPAWN_CHECK_ONLY_EXPER(content != NULL, return -1);
173 APPSPAWN_CHECK_ONLY_EXPER(appInfo != NULL, return -1);
174 if (IsNWebSpawnMode(content) || strcmp(appInfo->name, NWEBSPAWN_SERVER_NAME) == 0) {
175 return 0;
176 }
177 char cgroupPath[PATH_MAX] = {};
178 APPSPAWN_LOGV("ProcessMgrRemoveApp %{public}d %{public}d to cgroup ", appInfo->pid, appInfo->uid);
179 int ret = GetCgroupPath(appInfo, cgroupPath, sizeof(cgroupPath));
180 APPSPAWN_CHECK(ret == 0, return -1, "Failed to get real path errno: %{public}d", errno);
181 char procPath[PATH_MAX] = {};
182 ret = memcpy_s(procPath, sizeof(procPath), cgroupPath, sizeof(cgroupPath));
183 if (ret != 0) {
184 return APPSPAWN_ERROR_UTILS_MEM_FAIL;
185 }
186 ret = strcat_s(procPath, sizeof(procPath), "cgroup.procs");
187 APPSPAWN_CHECK(ret == 0, return ret, "Failed to strcat_s errno: %{public}d", errno);
188 KillProcessesByCGroup(procPath, (AppSpawnMgr *)content, appInfo);
189 ret = rmdir(cgroupPath);
190 if (ret != 0) {
191 return APPSPAWN_ERROR_FILE_RMDIR_FAIL;
192 }
193 return ret;
194 }
195
ProcessMgrAddApp(const AppSpawnMgr * content,const AppSpawnedProcessInfo * appInfo)196 static int ProcessMgrAddApp(const AppSpawnMgr *content, const AppSpawnedProcessInfo *appInfo)
197 {
198 APPSPAWN_CHECK_ONLY_EXPER(content != NULL, return -1);
199 APPSPAWN_CHECK_ONLY_EXPER(appInfo != NULL, return -1);
200 if (IsNWebSpawnMode(content)) {
201 return 0;
202 }
203 char path[PATH_MAX] = {};
204 APPSPAWN_LOGV("ProcessMgrAddApp %{public}d %{public}d to cgroup ", appInfo->pid, appInfo->uid);
205 int ret = GetCgroupPath(appInfo, path, sizeof(path));
206 APPSPAWN_CHECK(ret == 0, return -1, "Failed to get real path errno: %{public}d", errno);
207 (void)CreateSandboxDir(path, 0755); // 0755 default mode
208 uint32_t pathLen = strlen(path);
209 ret = strcat_s(path, sizeof(path), "cgroup.procs");
210 APPSPAWN_CHECK(ret == 0, return ret, "Failed to strcat_s errno: %{public}d", errno);
211 ret = WriteToFile(path, 0, (pid_t *)&appInfo->pid, 1);
212 APPSPAWN_CHECK(ret == 0, return ret, "write pid to cgroup.procs fail %{public}s", path);
213 if (appInfo->max != 0) {
214 path[pathLen] = '\0';
215 ret = strcat_s(path, sizeof(path), "pids.max");
216 APPSPAWN_CHECK(ret == 0, return ret, "Failed to strcat_s errno: %{public}d", errno);
217 ret = WritePidMax(path, appInfo->max);
218 APPSPAWN_CHECK(ret == 0, return ret, "write max to pids.max fail %{public}s", path);
219 }
220 APPSPAWN_LOGV("Add app %{public}d to cgroup %{public}s success", appInfo->pid, path);
221 return 0;
222 }
223
MODULE_CONSTRUCTOR(void)224 MODULE_CONSTRUCTOR(void)
225 {
226 AddProcessMgrHook(STAGE_SERVER_APP_ADD, 0, ProcessMgrAddApp);
227 AddProcessMgrHook(STAGE_SERVER_APP_DIED, 0, ProcessMgrRemoveApp);
228 }
229