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