• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright 2014 Google, Inc
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "libprocessgroup"
19 
20 #include <assert.h>
21 #include <dirent.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <inttypes.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 
31 #include <chrono>
32 #include <memory>
33 #include <mutex>
34 #include <thread>
35 
36 #include <android-base/logging.h>
37 #include <private/android_filesystem_config.h>
38 
39 #include <processgroup/processgroup.h>
40 
41 using namespace std::chrono_literals;
42 
43 // Uncomment line below use memory cgroups for keeping track of (forked) PIDs
44 // #define USE_MEMCG 1
45 
46 #define MEM_CGROUP_PATH "/dev/memcg/apps"
47 #define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
48 #define ACCT_CGROUP_PATH "/acct"
49 
50 #define PROCESSGROUP_UID_PREFIX "uid_"
51 #define PROCESSGROUP_PID_PREFIX "pid_"
52 #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
53 #define PROCESSGROUP_MAX_UID_LEN 11
54 #define PROCESSGROUP_MAX_PID_LEN 11
55 #define PROCESSGROUP_MAX_PATH_LEN \
56         ((sizeof(MEM_CGROUP_PATH) > sizeof(ACCT_CGROUP_PATH) ? \
57           sizeof(MEM_CGROUP_PATH) : sizeof(ACCT_CGROUP_PATH)) + \
58          sizeof(PROCESSGROUP_UID_PREFIX) + 1 + \
59          PROCESSGROUP_MAX_UID_LEN + \
60          sizeof(PROCESSGROUP_PID_PREFIX) + 1 + \
61          PROCESSGROUP_MAX_PID_LEN + \
62          sizeof(PROCESSGROUP_CGROUP_PROCS_FILE) + \
63          1)
64 
65 std::once_flag init_path_flag;
66 
67 struct ctx {
68     bool initialized;
69     int fd;
70     char buf[128];
71     char *buf_ptr;
72     size_t buf_len;
73 };
74 
getCgroupRootPath()75 static const char* getCgroupRootPath() {
76 #ifdef USE_MEMCG
77     static const char* cgroup_root_path = NULL;
78     std::call_once(init_path_flag, [&]() {
79             // Check if mem cgroup is mounted, only then check for write-access to avoid
80             // SELinux denials
81             cgroup_root_path = access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
82                     ACCT_CGROUP_PATH : MEM_CGROUP_PATH;
83             });
84     return cgroup_root_path;
85 #else
86     return ACCT_CGROUP_PATH;
87 #endif
88 }
89 
convertUidToPath(char * path,size_t size,uid_t uid)90 static int convertUidToPath(char *path, size_t size, uid_t uid)
91 {
92     return snprintf(path, size, "%s/%s%d",
93             getCgroupRootPath(),
94             PROCESSGROUP_UID_PREFIX,
95             uid);
96 }
97 
convertUidPidToPath(char * path,size_t size,uid_t uid,int pid)98 static int convertUidPidToPath(char *path, size_t size, uid_t uid, int pid)
99 {
100     return snprintf(path, size, "%s/%s%d/%s%d",
101             getCgroupRootPath(),
102             PROCESSGROUP_UID_PREFIX,
103             uid,
104             PROCESSGROUP_PID_PREFIX,
105             pid);
106 }
107 
initCtx(uid_t uid,int pid,struct ctx * ctx)108 static int initCtx(uid_t uid, int pid, struct ctx *ctx)
109 {
110     int ret;
111     char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
112     convertUidPidToPath(path, sizeof(path), uid, pid);
113     strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
114 
115     int fd = open(path, O_RDONLY);
116     if (fd < 0) {
117         ret = -errno;
118         PLOG(WARNING) << "failed to open " << path;
119         return ret;
120     }
121 
122     ctx->fd = fd;
123     ctx->buf_ptr = ctx->buf;
124     ctx->buf_len = 0;
125     ctx->initialized = true;
126 
127     LOG(VERBOSE) << "Initialized context for " << path;
128 
129     return 0;
130 }
131 
refillBuffer(struct ctx * ctx)132 static int refillBuffer(struct ctx *ctx)
133 {
134     memmove(ctx->buf, ctx->buf_ptr, ctx->buf_len);
135     ctx->buf_ptr = ctx->buf;
136 
137     ssize_t ret = read(ctx->fd, ctx->buf_ptr + ctx->buf_len,
138                 sizeof(ctx->buf) - ctx->buf_len - 1);
139     if (ret < 0) {
140         return -errno;
141     } else if (ret == 0) {
142         return 0;
143     }
144 
145     ctx->buf_len += ret;
146     ctx->buf[ctx->buf_len] = 0;
147     LOG(VERBOSE) << "Read " << ret << " to buffer: " << ctx->buf;
148 
149     assert(ctx->buf_len <= sizeof(ctx->buf));
150 
151     return ret;
152 }
153 
getOneAppProcess(uid_t uid,int appProcessPid,struct ctx * ctx)154 static pid_t getOneAppProcess(uid_t uid, int appProcessPid, struct ctx *ctx)
155 {
156     if (!ctx->initialized) {
157         int ret = initCtx(uid, appProcessPid, ctx);
158         if (ret < 0) {
159             return ret;
160         }
161     }
162 
163     char *eptr;
164     while ((eptr = (char *)memchr(ctx->buf_ptr, '\n', ctx->buf_len)) == NULL) {
165         int ret = refillBuffer(ctx);
166         if (ret == 0) {
167             return -ERANGE;
168         }
169         if (ret < 0) {
170             return ret;
171         }
172     }
173 
174     *eptr = '\0';
175     char *pid_eptr = NULL;
176     errno = 0;
177     long pid = strtol(ctx->buf_ptr, &pid_eptr, 10);
178     if (errno != 0) {
179         return -errno;
180     }
181     if (pid_eptr != eptr) {
182         return -EINVAL;
183     }
184 
185     ctx->buf_len -= (eptr - ctx->buf_ptr) + 1;
186     ctx->buf_ptr = eptr + 1;
187 
188     return (pid_t)pid;
189 }
190 
removeProcessGroup(uid_t uid,int pid)191 static int removeProcessGroup(uid_t uid, int pid)
192 {
193     int ret;
194     char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
195 
196     convertUidPidToPath(path, sizeof(path), uid, pid);
197     ret = rmdir(path);
198 
199     convertUidToPath(path, sizeof(path), uid);
200     rmdir(path);
201 
202     return ret;
203 }
204 
removeUidProcessGroups(const char * uid_path)205 static void removeUidProcessGroups(const char *uid_path)
206 {
207     std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path), closedir);
208     if (uid != NULL) {
209         dirent* dir;
210         while ((dir = readdir(uid.get())) != nullptr) {
211             char path[PROCESSGROUP_MAX_PATH_LEN];
212 
213             if (dir->d_type != DT_DIR) {
214                 continue;
215             }
216 
217             if (strncmp(dir->d_name, PROCESSGROUP_PID_PREFIX, strlen(PROCESSGROUP_PID_PREFIX))) {
218                 continue;
219             }
220 
221             snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
222             LOG(VERBOSE) << "removing " << path;
223             if (rmdir(path) == -1) PLOG(WARNING) << "failed to remove " << path;
224         }
225     }
226 }
227 
removeAllProcessGroups()228 void removeAllProcessGroups()
229 {
230     LOG(VERBOSE) << "removeAllProcessGroups()";
231     const char* cgroup_root_path = getCgroupRootPath();
232     std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
233     if (root == NULL) {
234         PLOG(ERROR) << "failed to open " << cgroup_root_path;
235     } else {
236         dirent* dir;
237         while ((dir = readdir(root.get())) != nullptr) {
238             char path[PROCESSGROUP_MAX_PATH_LEN];
239 
240             if (dir->d_type != DT_DIR) {
241                 continue;
242             }
243             if (strncmp(dir->d_name, PROCESSGROUP_UID_PREFIX, strlen(PROCESSGROUP_UID_PREFIX))) {
244                 continue;
245             }
246 
247             snprintf(path, sizeof(path), "%s/%s", cgroup_root_path, dir->d_name);
248             removeUidProcessGroups(path);
249             LOG(VERBOSE) << "removing " << path;
250             if (rmdir(path) == -1) PLOG(WARNING) << "failed to remove " << path;
251         }
252     }
253 }
254 
doKillProcessGroupOnce(uid_t uid,int initialPid,int signal)255 static int doKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
256     int processes = 0;
257     struct ctx ctx;
258     pid_t pid;
259 
260     ctx.initialized = false;
261 
262     while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) {
263         processes++;
264         if (pid == 0) {
265             // Should never happen...  but if it does, trying to kill this
266             // will boomerang right back and kill us!  Let's not let that happen.
267             LOG(WARNING) << "Yikes, we've been told to kill pid 0!  How about we don't do that?";
268             continue;
269         }
270         LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid
271                      << " as part of process group " << initialPid;
272         if (kill(pid, signal) == -1) {
273             PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
274         }
275     }
276 
277     if (ctx.initialized) {
278         close(ctx.fd);
279     }
280 
281     return processes;
282 }
283 
killProcessGroup(uid_t uid,int initialPid,int signal,int retry)284 static int killProcessGroup(uid_t uid, int initialPid, int signal, int retry) {
285     std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
286 
287     int processes;
288     while ((processes = doKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
289         LOG(VERBOSE) << "killed " << processes << " processes for processgroup " << initialPid;
290         if (retry > 0) {
291             std::this_thread::sleep_for(5ms);
292             --retry;
293         } else {
294             LOG(ERROR) << "failed to kill " << processes << " processes for processgroup "
295                        << initialPid;
296             break;
297         }
298     }
299 
300     std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
301 
302     auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
303     LOG(VERBOSE) << "Killed process group uid " << uid << " pid " << initialPid << " in "
304                  << static_cast<int>(ms) << "ms, " << processes << " procs remain";
305 
306     if (processes == 0) {
307         return removeProcessGroup(uid, initialPid);
308     } else {
309         return -1;
310     }
311 }
312 
killProcessGroup(uid_t uid,int initialPid,int signal)313 int killProcessGroup(uid_t uid, int initialPid, int signal) {
314     return killProcessGroup(uid, initialPid, signal, 40 /*maxRetry*/);
315 }
316 
killProcessGroupOnce(uid_t uid,int initialPid,int signal)317 int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
318     return killProcessGroup(uid, initialPid, signal, 0 /*maxRetry*/);
319 }
320 
mkdirAndChown(const char * path,mode_t mode,uid_t uid,gid_t gid)321 static bool mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
322 {
323     if (mkdir(path, mode) == -1 && errno != EEXIST) {
324         return false;
325     }
326 
327     if (chown(path, uid, gid) == -1) {
328         int saved_errno = errno;
329         rmdir(path);
330         errno = saved_errno;
331         return false;
332     }
333 
334     return true;
335 }
336 
createProcessGroup(uid_t uid,int initialPid)337 int createProcessGroup(uid_t uid, int initialPid)
338 {
339     char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
340 
341     convertUidToPath(path, sizeof(path), uid);
342 
343     if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
344         PLOG(ERROR) << "failed to make and chown " << path;
345         return -errno;
346     }
347 
348     convertUidPidToPath(path, sizeof(path), uid, initialPid);
349 
350     if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
351         PLOG(ERROR) << "failed to make and chown " << path;
352         return -errno;
353     }
354 
355     strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
356 
357     int fd = open(path, O_WRONLY);
358     if (fd == -1) {
359         int ret = -errno;
360         PLOG(ERROR) << "failed to open " << path;
361         return ret;
362     }
363 
364     char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0};
365     int len = snprintf(pid, sizeof(pid), "%d", initialPid);
366 
367     int ret = 0;
368     if (write(fd, pid, len) < 0) {
369         ret = -errno;
370         PLOG(ERROR) << "failed to write '" << pid << "' to " << path;
371     }
372 
373     close(fd);
374     return ret;
375 }
376