DF-1093 / df1093_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 122 123 124 125 126 127 128 129 130 | /* * DF-1093 โ userspace harness for ppb_pnp_detect NULL+1 deref. * * The kernel path (sys/bus/ppbus/ppbconf.c:ppb_pnp_detect) is reachable * only when an IEEE 1284 peripheral is electrically attached to a parallel * port and supplies a PnP ID string at attach time. The audit QEMU guest * has no parallel port (no lpt/ppbus device in dmesg), so this harness * mirrors the algorithm to demonstrate the NULL+1 โ deref crash pattern. * * A malicious peripheral sends "MFG\0" (keyword found, no ':' before the * next NUL). search_token() returns NULL for the ':' lookup. ppb_pnp_detect * unconditionally computes NULL + 1 = (char*)0x1 and passes it to * kprintf("%s", ...) which dereferences address 1 โ SIGSEGV / kernel * page fault / panic. * * The harness installs a SIGSEGV handler that prints "BUG: NULL+1 * dereferenced" so the crash is observable without taking down the host. * * Build: cc -O0 -o df1093_harness df1093_harness.c * or: cc -O0 -DFIX -o df1093_harness_fix df1093_harness.c * Run: ./df1093_harness */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <signal.h> #include <setjmp.h> #define UNKNOWN_LENGTH -1 static sigjmp_buf jmp_env; static volatile sig_atomic_t got_segfault; static void sigsegv_handler(int sig) { (void)sig; got_segfault = 1; siglongjmp(jmp_env, 1); } /* Mirror search_token at ppbconf.c:177-206. Returns pointer to match, or NULL. */ static char * search_token(char *str, int slen, char *token) { char *p; int tlen, i, j; if (slen == UNKNOWN_LENGTH) for (slen = 0, p = str; *p != '\0'; p++) slen++; for (tlen = 0, p = token; *p != '\0'; p++) tlen++; if (tlen == 0) return (str); for (i = 0; i <= slen - tlen; i++) { for (j = 0; j < tlen; j++) if (str[i + j] != token[j]) break; if (j == tlen) return (&str[i]); } return (NULL); } /* Mirror ppb_pnp_detect's MFG-handling block โ the bug. * 'str' is the IEEE 1284 PnP string. */ static void detect_mfg(char *str, int len) { char *token, *val; if ((token = search_token(str, len, "MFG")) != NULL) { /* keyword found; look for ':' after it */ val = search_token(token, UNKNOWN_LENGTH, ":"); #ifndef FIX /* UNPATCHED โ NULL + 1 unconditionally, then deref via %s */ char *p = val + 1; /* val may be NULL โ p = (char*)0x1 */ /* trigger the deref (this is what kprintf("%s", p) does) */ (void)*(volatile char *)p; printf(" MFG value: '%s' (would print)\n", p); #else /* PATCHED โ check val for NULL before deref */ if (val != NULL) { char *p = val + 1; (void)*(volatile char *)p; printf(" MFG value: '%s'\n", p); } else { printf(" MFG keyword found but no ':' โ skip safely\n"); } #endif } else { printf(" no MFG keyword\n"); } } int main(void) { struct sigaction sa; char str_mfg_only[] = "MFG"; /* keyword, no ':' */ printf("=== DF-1093 ppb_pnp_detect NULL+1 harness (%s) ===\n", #ifdef FIX "PATCHED" #else "UNPATCHED" #endif ); memset(&sa, 0, sizeof(sa)); sa.sa_handler = sigsegv_handler; sigemptyset(&sa.sa_mask); sigaction(SIGSEGV, &sa, NULL); got_segfault = 0; if (sigsetjmp(jmp_env, 1) == 0) { detect_mfg(str_mfg_only, strlen(str_mfg_only)); printf(" returned normally\n"); } else { printf(" BUG: NULL+1 deref trapped (SIGSEGV at addr 1)\n"); printf(" ===== In the kernel: page fault, kernel panic at boot =====\n"); } return (got_segfault ? 0 : 0); } |