DF-1795 / harness.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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | /* * DF-1795 source-confirmation harness (PLIP SIOCSIFMTU race). * * The buggy kernel code is HW-gated (needs PLIP parallel-port hardware) * and cannot run on this audit guest. This harness reproduces the LOGIC * of the race it documents: an interrupt handler reads a length bound * (if_mtu) and a buffer pointer (sc_ifbuf) that are updated in three * SEPARATE unlocked steps by SIOCSIFMTU. We model the two thread roles * and show that the ithread can observe the OLD mtu with the NEW * (smaller) buffer, producing a heap overflow of (OLD_MTU - NEW_MTU) * bytes. The model uses the exact field-update sequence from * sys/dev/netif/plip/if_plip.c:348-354 and the length check at :470. * * Build: cc -O2 -o harness harness.c -lpthread * Run: ./harness */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <unistd.h> /* Model of the lp_data softc โ only the fields involved in the race. */ struct lp_model { unsigned char *sc_ifbuf; /* lp_data.sc_ifbuf */ size_t if_mtu; /* lp_data.sc_if.if_mtu */ size_t buf_cap; /* actual allocation (for harness) */ }; static struct lp_model state; static volatile int stop; #define MLPIPHDRLEN 14 /* from if_plip.c:#define MLPIPHDRLEN */ #define CLPIPHDRLEN 14 #define LPIPHDRLEN 14 /* SIOCSIFMTU handler: lines 348-354 of if_plip.c. THREE unlocked steps: * ptr = sc->sc_ifbuf; * sc->sc_ifbuf = kmalloc(ifr_mtu + MLPIPHDRLEN, ...); // step 1: new buf * if (ptr) kfree(ptr); // step 2: old freed * sc->sc_if.if_mtu = ifr_mtu; // step 3: new mtu * * Critically: step 1 publishes a NEW (smaller) buffer BEFORE step 3 * publishes the matching NEW mtu. An interrupt sampling in between * sees (old mtu, new small buf) -> overflow. */ static void siocsifmtu(struct lp_model *s, size_t new_mtu) { unsigned char *ptr = s->sc_ifbuf; size_t cap = new_mtu + MLPIPHDRLEN; s->sc_ifbuf = calloc(1, cap); /* step 1 */ s->buf_cap = cap; if (ptr) free(ptr); /* step 2 (UAF window in kernel) */ s->if_mtu = new_mtu; /* step 3 */ } /* lp_intr: lines 444-503. It reads sc->sc_if.if_mtu as the length bound * for writing into sc->sc_ifbuf. In the kernel this runs in an ithread * with ONLY crit_enter() (line 454) โ NOT the ifnet serializer that * wraps lpioctl (if.c:2276). crit_enter does not block the ioctl thread * on another CPU. */ static volatile int overflow_observed; static volatile size_t overflow_bytes; static void * lp_intr_thread(void *arg) { struct lp_model *s = arg; while (!stop) { /* intr samples mtu as the bound, writes into sc_ifbuf */ size_t len = s->if_mtu + MLPIPHDRLEN; /* line 470 */ if (s->sc_ifbuf && len > s->buf_cap) { /* In the kernel: *bp++ = j; for len iterations -> heap overflow. * The overflow depth is exactly len - buf_cap bytes. */ overflow_observed++; overflow_bytes = len - s->buf_cap; stop = 1; break; } } return NULL; } static void * siocsifmtu_thread(void *arg) { struct lp_model *s = arg; size_t mtu = 1500; while (!stop) { /* Toggle MTU between large (1500) and tiny (64) to widen the window */ siocsifmtu(s, mtu); mtu = (mtu == 1500) ? 64 : 1500; usleep(1); } return NULL; } int main(void) { state.sc_ifbuf = calloc(1, 1500 + MLPIPHDRLEN); state.buf_cap = 1500 + MLPIPHDRLEN; state.if_mtu = 1500; pthread_t intr, ioctl_t; pthread_create(&intr, NULL, lp_intr_thread, &state); pthread_create(&ioctl_t,NULL, siocsifmtu_thread, &state); pthread_join(intr, NULL); stop = 1; pthread_join(ioctl_t, NULL); if (overflow_observed) { printf("DF-1795: race confirmed โ lp_intr observed mtu+14=%zu-byte bound " "with %zu-byte buffer -> %zu-byte heap overflow\n", state.if_mtu + MLPIPHDRLEN, state.buf_cap, overflow_bytes); printf("This matches sys/dev/netif/plip/if_plip.c:470 (len bound) vs :350 " "(new smaller buf) vs :353 (new mtu published LAST).\n"); return 0; } printf("DF-1795: race not observed in this short run (kernel has a wider window).\n"); return 0; } |