DF-4546 / lm_detach_nullwrite.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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | /* * DF-4546 — lm78.c lm_detach on a zero-sensor attach (W83791SD) * ================================================================= * Lifted-copy harness (macro-level; the in-kernel path needs an LM78-class * chip on the ISA bus, absent on the audit guest). * * Faithful replica of: * - struct ksensordev (sys/sys/sensors.h:156-166) in a zeroed softc * - the DragonFly TAILQ macros verbatim from sys/sys/queue.h:646-662 * - sensordev_install / sensordev_deinstall list logic * (sys/kern/kern_sensors.c:77-109 / 153-176 -- sysctl parts dropped) * - the driver flow: wb_match() W83791SD case (lm78.c:590-592) which sets * NO sensor table -> numsensors == 0 -> lm_attach() early return * (lm78.c:427-429, sensordev never installed) -> lm_detach() * (lm78.c:445-458) which UNCONDITIONALLY calls sensordev_deinstall() * * TAILQ_REMOVE(&sensordev_list, &zeroed->sensordev, list) on a never * inserted (zeroed) node executes, per sys/sys/queue.h:646-662: * tqe_next == NULL -> head->tqh_last = NULL (global list corrupted) * *(tqe_prev == NULL) = tqe_next (write to address 0) * i.e. a NULL-pointer WRITE plus corruption of the global sensordev_list * head -- in the kernel this is an immediate panic (and any later * TAILQ_INSERT_TAIL dereferences the NULL tqh_last). * * Control: a normal chip (numsensors > 0) installs the device and detaches * cleanly. * * Build: cc -O2 -Wall -o lm_detach_nullwrite lm_detach_nullwrite.c * Run: ./lm_detach_nullwrite */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <signal.h> #include <unistd.h> #include <sys/wait.h> #include <string.h> #define SENSOR_MAX_TYPES 18 /* sys/sys/sensors.h enum */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* sys/sys/queue.h:646-662 -- verbatim (QMD_* debug hooks are no-ops) */ #define TAILQ_REMOVE(head, elm, field) do { \ if ((TAILQ_NEXT((elm), field)) != NULL) \ TAILQ_NEXT((elm), field)->field.tqe_prev = \ (elm)->field.tqe_prev; \ else { \ (head)->tqh_last = (elm)->field.tqe_prev; \ } \ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) /* sys/sys/sensors.h:156-166 */ struct ksensordev { TAILQ_ENTRY(ksensordev) list; int num; char xname[16]; int maxnumt[SENSOR_MAX_TYPES]; int sensors_count; struct { void *slh_first; } sensors_list; void *oid; void *clist; }; TAILQ_HEAD(sensordev_list, ksensordev); /* the global from kern_sensors.c:39-40 */ static struct sensordev_list sensordev_list = { NULL, (struct ksensordev **)&sensordev_list.tqh_first }; static volatile unsigned long fault_addr; static void sigsegv(int sig, siginfo_t *si, void *uc) { char msg[96]; int len; (void)sig; (void)uc; fault_addr = (unsigned long)si->si_addr; len = snprintf(msg, sizeof(msg), " >>> SIGSEGV: kernel-equivalent NULL-pointer WRITE at 0x%lx\n", fault_addr); write(1, msg, len); _exit(1); } /* kern_sensors.c:77-109 (sysctl parts elided) */ static void sensordev_install(struct ksensordev *sensdev) { sensdev->num = 0; TAILQ_INSERT_TAIL(&sensordev_list, sensdev, list); } /* kern_sensors.c:153-176 (sysctl parts elided) */ static void sensordev_deinstall(struct ksensordev *sensdev) { TAILQ_REMOVE(&sensordev_list, sensdev, list); } /* lm_softc (lm78var.h:139-156) -- only the fields that matter */ struct lm_softc { struct ksensordev sensordev; unsigned numsensors; }; /* lm78.c:445-458 (+ fix.diff guard) */ static int lm_detach_patched; static int lm_detach(struct lm_softc *sc) { int i; if (lm_detach_patched && sc->numsensors == 0) return (0); /* fix.diff: never installed */ /* Remove sensors */ sensordev_deinstall(&sc->sensordev); /* unconditional! */ for (i = 0; i < (int)sc->numsensors; i++) ; /* ... */ return 0; } /* lm78.c:422-443 */ static void lm_attach(struct lm_softc *sc) { unsigned i; if (sc->numsensors == 0) /* W83791SD path: bail here */ return; /* sensordev NEVER installed */ for (i = 0; i < sc->numsensors; ++i) ; sensordev_install(&sc->sensordev); } static struct sigaction sa; static int run_case(const char *name, unsigned numsensors, int expect_fault) { struct lm_softc *sc; int pid, status, ret; printf("=== %s (numsensors=%u) ===\n", name, numsensors); if ((pid = fork()) < 0) { perror("fork"); exit(2); } if (pid == 0) { sigaction(SIGSEGV, &sa, NULL); lm_detach_patched = (strstr(name, "PATCHED") != NULL); sc = calloc(1, sizeof(*sc)); /* newbus zeroes the softc */ sc->numsensors = numsensors; lm_attach(sc); /* probe picked the chip */ printf(" attach done (sensordev %sinstalled), detaching...\n", numsensors ? "" : "never "); lm_detach(sc); printf(" detach returned cleanly\n"); _exit(0); } waitpid(pid, &status, 0); ret = WEXITSTATUS(status); printf(" %s\n\n", ret == 1 ? "NULL-WRITE FAULT REPRODUCED" : (expect_fault ? "NOT REPRODUCED" : "OK (clean, as expected)")); return ret == 1; } int main(void) { memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = sigsegv; sa.sa_flags = SA_SIGINFO; sigfillset(&sa.sa_mask); printf("DF-4546 lifted-copy harness: lm_detach -> sensordev_deinstall " "of never-installed ksensordev (lm78.c:445-458 + " "sys/sys/queue.h:646)\n\n"); /* control: real chip with sensors (e.g. W83792D, 19 sensors) */ run_case("CONTROL normal chip", 19, 0); /* bug: wb_match() W83791SD sets no sensor table (lm78.c:590-592) */ int f = run_case("BUG W83791SD zero-sensor attach", 0, 1); /* patched: same zero-sensor attach, guarded detach -> clean */ run_case("PATCHED W83791SD guarded detach", 0, 0); printf("%s: zero-sensor attach + detach writes through NULL and " "corrupts sensordev_list.tqh_last\n", f ? "REPRODUCED" : "NOT REPRODUCED"); return f ? 0 : 1; } |