DF-2760 / getsid_race.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | /* * DF-2760: sys_getsid (kern_prot.c:153-154) reads pt->p_pgrp->pg_session * with NO p_token; sys_setpgid (:383/:404) reads curp->p_session unlocked. * Churners continuously setpgid/setsid (pgrp/session alloc/free churn); * a checker hammers getsid() on them and validates every returned sid * against the registry: {parent sid} U {every churner pid (historical * session leaders)}. Anything else = read through freed memory. */ #include <sys/types.h> #include <sys/mman.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <signal.h> #define NCHURN 24 struct shm_s { pid_t churn[NCHURN]; volatile int cur_sid[NCHURN]; /* churner's current sid */ }; static struct shm_s *shm; static volatile sig_atomic_t stop; static void churner(int idx) { int r; shm->churn[idx] = getpid(); r = setsid(); /* sid = my pid */ shm->cur_sid[idx] = r < 0 ? getsid(0) : (int)getpid(); for (;;) { setpgid(0, 0); if ((idx & 3) == 0 && getsid(0) != getpid()) shm->cur_sid[idx] = setsid() < 0 ? getsid(0) : (int)getpid(); usleep(50 + (idx & 15)); } _exit(0); } static int valid_sid(int sid, int parentsid) { int i; if (sid == parentsid) return 1; if (sid <= 1) return 0; for (i = 0; i < NCHURN; i++) if (shm->churn[i] == sid) /* historical leader pid */ return 1; return 0; } int main(int argc, char **argv) { int secs = argc > 1 ? atoi(argv[1]) : 60; int i; shm = mmap(NULL, sizeof(*shm), PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); memset((void *)shm, 0, sizeof(*shm)); int parentsid = getsid(0); for (i = 0; i < NCHURN; i++) { pid_t p = fork(); if (p == 0) churner(i); } usleep(200000); /* let churners register */ long iters = 0, impossible = 0, stale = 0; time_t t0 = time(NULL); while (time(NULL) - t0 < secs) { for (i = 0; i < NCHURN; i++) { int sid = getsid(shm->churn[i]); iters++; if (sid < 0) continue; if (!valid_sid(sid, parentsid)) { impossible++; if (impossible <= 10) fprintf(stderr, "[!!] getsid(%d)=%d: value never " "was a session id (garbage read " "through freed pgrp/session)\n", shm->churn[i], sid); } else if (sid != shm->cur_sid[i] && sid != shm->churn[i] && sid != parentsid) { stale++; /* benign transition staleness */ } } } fprintf(stderr, "[*] getsid=%ld impossible=%ld stale-transition=%ld " "parentsid=%d\n", iters, impossible, stale, parentsid); fprintf(stderr, "[*] if impossible>0: race reproduced; else window " "not observed this run. Check dmesg for panics.\n"); kill(0, SIGKILL); return 0; } |