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 36APPSPAWN_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 51APPSPAWN_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 *) 80static 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 122static 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 144static 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 170APPSPAWN_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 196APPSPAWN_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, 0750); // 0750 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 224MODULE_CONSTRUCTOR(void) 225{ 226 AddProcessMgrHook(STAGE_SERVER_APP_ADD, 0, ProcessMgrAddApp); 227 AddProcessMgrHook(STAGE_SERVER_APP_DIED, 0, ProcessMgrRemoveApp); 228} 229