]> granicus.if.org Git - procps-ng/commitdiff
0084-proc/readproc.c: Work around a design flaw in readeither().
authorQualys Security Advisory <qsa@qualys.com>
Thu, 1 Jan 1970 00:00:00 +0000 (00:00 +0000)
committerCraig Small <csmall@enc.com.au>
Sat, 9 Jun 2018 11:35:19 +0000 (21:35 +1000)
readeither() caches (in new_p) a pointer to the proc_t of a task-group
leader, but readeither()'s callers can do pretty much anything with the
proc_t structure passed to and/or returned by this function. For
example, they can 1/ free it or 2/ recycle it (by passing it to
readeither() as x).

1/ leads to a use-after-free, and 2/ leads to unexpected behavior when
taskreader()/simple_readtask() is called with new_p equal to x (this is
not a theoretical flaw: 2/ happens in readproctab3() when want_task()
returns false and p is a group leader).

As a workaround, we keep a copy of new_p's first member (tid) in static
storage, and the next times we enter readeither() we check this "canary"
against the tid in new_p: if they differ, we reset new_p to NULL, which
forces the allocation of a new proc_t (the new "leader", or reference).

This always detects 2/ (because free_acquired(x,1) memsets x and hence
new_p); always detects 1/ if freed via free_acquired() and/or freeproc()
(very likely, otherwise memory may be leaked); probably detects 1/ even
if freed directly via free() (because the canary is the first member of
proc_t, likely to be overwritten by free()); but can not detect 1/ if
free() does not write to new_p's chunk at all.

Moreover, accessing new_p->tid to check the canary in case 1/ is itself
a use-after-free, so a better long-term solution should be implemented
at some point (we wanted to avoid intrusive and backward-incompatible
changes in this library function, hence this imperfect workaround).

---------------------------- adapted for newlib branch
. adapted via 'patch' (rejected due to 'xcalloc' ref)
. with loss of both readproctab functions, most no longer true

Signed-off-by: Jim Warner <james.warner@comcast.net>
proc/readproc.c

index 18c16d17288728f74c8648a530bc762900a963cd..bd87bb4435f2f0a08484d339fa6fcaca1e18ac8e 100644 (file)
@@ -1328,6 +1328,7 @@ out:
 proc_t* readeither (PROCTAB *restrict const PT, proc_t *restrict x) {
     static proc_t skel_p;    // skeleton proc_t, only uses tid + tgid
     static proc_t *new_p;    // for process/task transitions
+    static int canary;
     char path[PROCPATHLEN];
     proc_t *saved_x, *ret;
 
@@ -1337,7 +1338,10 @@ proc_t* readeither (PROCTAB *restrict const PT, proc_t *restrict x) {
         x = calloc(1, sizeof(*x));
         if (!x) goto end_procs;
     }
-    if (new_p) goto next_task;
+    if (new_p) {
+        if (new_p->tid != canary) new_p = NULL;
+        goto next_task;
+    }
 
 next_proc:
     new_p = NULL;
@@ -1355,7 +1359,10 @@ next_task:
     || (!(ret = PT->taskreader(PT,new_p,x,path)))) {       // simple_readtask
         goto next_proc;
     }
-    if (!new_p) new_p = ret;
+    if (!new_p) {
+        new_p = ret;
+        canary = new_p->tid;
+    }
     return ret;
 
 end_procs: