From 8677c615752235194f1a01925c9a591cdf8da821 Mon Sep 17 00:00:00 2001 From: Date: Wed, 8 May 2019 03:43:25 -0400 Subject: [PATCH] fix pstree coredump due pid reuse From: huangkaibin sign_off_by: XiaShuang pstree scans /proc directory to construct tree of all processes. In scanning, if a pid or tid is already added to ps tree, and then exists, and it is reused by another process or thread, there will be a loop in ps tree, and will cause coredump in tree_equal function. An example step is as below. 1. process A added to ps tree 2. process B added to ps tree as a child of Author 3. B exists and its pid is reused by process C(with same pid as B) 4. A exists and its pid is reused by process D(with same pid as A) as a child of process C 5. hence, A(pid1)->B(pid2)->C(pid2)->D(pid1)->B(pid)->... loop is constructed This patch fix this problem by not adding processes with pid reused to ps tree. --- src/pstree.c | 108 ++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 38 deletions(-) diff --git a/src/pstree.c b/src/pstree.c index aaf3df7..b4880f8 100644 --- a/src/pstree.c +++ b/src/pstree.c @@ -119,6 +119,8 @@ typedef struct _proc { #define PFLAG_HILIGHT 0x01 #define PFLAG_THREAD 0x02 +#define ISTHREAD(proc) ((proc)->flags & ~PFLAG_THREAD) + typedef struct _child { PROC *child; struct _child *next; @@ -574,15 +576,44 @@ rename_proc(PROC *this, const char *comm, uid_t uid) } } -static void +static int has_proc_loop(const PROC *parent, const PROC *child) { + int ret = 0; + const PROC *raw_parent = parent; + if(child != NULL) { + while(parent != NULL) { + if(parent == child) { + ret = 1; + break ; + } + parent = parent->parent; + if(parent == raw_parent) + break ; + } + } + return ret; +} + + +static int add_proc(const char *comm, pid_t pid, pid_t ppid, pid_t pgid, uid_t uid, const char *args, int size, char isthread, security_context_t scontext) { PROC *this, *parent; + if (!(parent = find_proc(ppid))) { + parent = new_proc("?", ppid, 0, scontext); + } if (!(this = find_proc(pid))) this = new_proc(comm, pid, uid, scontext); else { + if(strcmp(this->comm, "?") != 0 || has_proc_loop(parent, this)) { + /*when comm is not ?, then it is not a placeholder process, and there + * comes the scenario of pid/tid reuse. + * or if it is a placeholder, but there contains a proc loop, + * it will not be added to ps tree. + */ + return -1; + } rename_proc(this, comm, uid); } if (args) @@ -592,13 +623,12 @@ add_proc(const char *comm, pid_t pid, pid_t ppid, pid_t pgid, uid_t uid, this->pgid = pgid; if (isthread) this->flags |= PFLAG_THREAD; - if (!(parent = find_proc(ppid))) { - parent = new_proc("?", ppid, 0, scontext); - } - if (pid != 0) { + if (pid != 0 && !ISTHREAD(parent)) { + /*thread can not have any children. this can happen when a tid is reused as a pid*/ add_child(parent, this); this->parent = parent; } + return 0; } @@ -918,7 +948,7 @@ static void read_proc(void) char readbuf[BUFSIZ + 1]; char *tmpptr, *endptr; pid_t pid, ppid, pgid; - int fd, size; + int fd, size, ret; int empty; security_context_t scontext = NULL; #ifdef WITH_SELINUX @@ -978,41 +1008,14 @@ static void read_proc(void) struct dirent *dt; char *taskpath; int thread; - - /* handle process threads */ - if (! hide_threads) { - if (! (taskpath = malloc(strlen(path) + 10))) - exit(2); - sprintf(taskpath, "%s/task", path); - - if ((taskdir = opendir(taskpath)) != 0) { - /* if we have this dir, we're on 2.6 */ - while ((dt = readdir(taskdir)) != NULL) { - if ((thread = atoi(dt->d_name)) != 0) { - if (thread != pid) { - char *threadname; - threadname = get_threadname(pid, thread, comm); - if (print_args) - add_proc(threadname, thread, pid, pgid, st.st_uid, - threadname, strlen (threadname) + 1, 1,scontext); - else - add_proc(threadname, thread, pid, pgid, st.st_uid, - NULL, 0, 1, scontext); - free(threadname); - } - } - } - (void) closedir(taskdir); - } - free(taskpath); - } + char cmdline[128]; /* handle process */ if (!print_args) - add_proc(comm, pid, ppid, pgid, st.st_uid, NULL, 0, 0, scontext); + ret = add_proc(comm, pid, ppid, pgid, st.st_uid, NULL, 0, 0, scontext); else { - sprintf(path, "%s/%d/cmdline", PROC_BASE, pid); - if ((fd = open(path, O_RDONLY)) < 0) { + sprintf(cmdline, "%s/%d/cmdline", PROC_BASE, pid); + if ((fd = open(cmdline, O_RDONLY)) < 0) { /* If this fails then the process is gone. If a PID * was specified on the command-line then we might * not even be interested in the current process. @@ -1037,9 +1040,38 @@ static void read_proc(void) size--; if (size) buffer[size++] = 0; - add_proc(comm, pid, ppid, pgid, st.st_uid, + ret = add_proc(comm, pid, ppid, pgid, st.st_uid, buffer, size, 0, scontext); } + if (ret >= 0) {/*do not add tids to ps tree when its parent process reuses pid/tid*/ + /* handle process threads */ + if (!hide_threads) { + if (! (taskpath = malloc(strlen(path) + 10))) + exit(2); + sprintf(taskpath, "%s/task", path); + + if ((taskdir = opendir(taskpath)) != 0) { + /* if we have this dir, we're on 2.6 */ + while ((dt = readdir(taskdir)) != NULL) { + if ((thread = atoi(dt->d_name)) != 0) { + if (thread != pid) { + char *threadname; + threadname = get_threadname(pid, thread, comm); + if (print_args) + add_proc(threadname, thread, pid, pgid, st.st_uid, + threadname, strlen (threadname) + 1, 1,scontext); + else + add_proc(threadname, thread, pid, pgid, st.st_uid, + NULL, 0, 1, scontext); + free(threadname); + } + } + } + (void) closedir(taskdir); + } + free(taskpath); + } + } } } } -- 1.8.3.1