DF-1499 / 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 | /* * DF-1499 harness — musycc NGM_TEXT_STATUS ksprintf overflow * * sys/dev/misc/musycc/musycc.c:1000-1001 NG_MKRESPONSE(...,sizeof(ng_mesg)+ * NG_TEXTRESPONSE=1024, ...) -> data area is 1024 bytes * :1006-1008 s = data; status_8370(sc,s); status_chans(sc,s); * :1009 (*resp)->header.arglen = strlen(s)+1; * * status_chans (lines 443-474) iterates NHDLC=32 channels, writing up to 7 * ksprintf calls per non-NULL channel. Each channel prints: * "cXX:" " ts %08x" " RX %lus/%lus" " TX %lus/%lus/%lus" * " TXdrop %lu Pend %lu" * " CRC %lu Dribble %lu Long %lu Short %lu Abort %lu" * "\n TX: %lu RX: %lu\n" * Counters are unsigned long (max 20 digits). With counters all large and * NHDLC=32 channels open, each channel is ~280-450 bytes; 32 channels * therefore emit ~9-14 KiB into a 1 KiB response buffer. * * The guest has no Siemens/Conexant 83791 framer + LMC musycc card so the * node cannot be instantiated in-kernel; harness reproduces the genuine * ksprintf count and confirms the overflow magnitude. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define NG_TEXTRESPONSE 1024 /* ng_message.h:59 */ #define NHDLC 32 /* musycc.c:156 */ struct schan { uint32_t ts; unsigned long last_recv, last_rxerr; unsigned long last_xmit, last_txerr, last_txdrop; unsigned long tx_drop, tx_pending; unsigned long crc_error, dribble_error, long_error, short_error, abort_error; unsigned long txn, rxn; }; /* Replicate status_chans exactly from musycc.c:443-474, returning bytes used. * We point ksprintf at a tail buffer of "cap" bytes to detect overflow. */ static size_t emit_chan(char *s, size_t cap, struct schan *scp, int i, unsigned long time_uptime) { size_t used = strlen(s); char *p = s + used; size_t room = (cap > used) ? cap - used : 0; int n = snprintf(p, room, "c%2d: ts %08x RX %lus/%lus TX %lus/%lus/%lus " "TXdrop %lu Pend %lu " "CRC %lu Dribble %lu Long %lu Short %lu Abort %lu " "\n TX: %lu RX: %lu\n", i, scp->ts, time_uptime - scp->last_recv, time_uptime - scp->last_rxerr, time_uptime - scp->last_xmit, time_uptime - scp->last_txerr, time_uptime - scp->last_txdrop, scp->tx_drop, scp->tx_pending, scp->crc_error, scp->dribble_error, scp->long_error, scp->short_error, scp->abort_error, scp->txn, scp->rxn); /* snprintf returns what would have been written */ return (size_t)n; /* not counting the existing strlen(s) */ } int main(void) { /* status_8370 emits ~100-200 bytes; we conservatively assume 200. */ const size_t header_bytes = 200; /* Two scenarios: fresh counters (0) vs maximally-stressed counters. */ struct schan fresh, stressed; memset(&fresh, 0, sizeof(fresh)); memset(&stressed, 0x7f, sizeof(stressed)); /* max ulong-ish */ stressed.ts = 0xDEADBEEF; unsigned long tu = 1000000; /* Per-channel byte count using snprintf-equivalent format */ char tmp[1024]; tmp[0] = 0; size_t per_fresh = emit_chan(tmp, sizeof(tmp), &fresh, 31, tu); size_t per_stressed = emit_chan(tmp, sizeof(tmp), &stressed, 31, tu); printf("Per-channel bytes (fresh counters) : %zu\n", per_fresh); printf("Per-channel bytes (stressed counters): %zu\n", per_stressed); printf("status_8370 header bytes (approx) : %zu\n", header_bytes); printf("NG_TEXTRESPONSE buffer : %d\n", NG_TEXTRESPONSE); /* Worst-case: all NHDLC channels non-NULL */ size_t worst_used = header_bytes + per_stressed * NHDLC; long worst_oob = (long)worst_used - NG_TEXTRESPONSE; size_t fresh_used = header_bytes + per_fresh * NHDLC; long fresh_oob = (long)fresh_used - NG_TEXTRESPONSE; printf("\nAll %d channels open (worst case):\n", NHDLC); printf(" fresh counters used=%zu OOB=%ld bytes\n", fresh_used, fresh_oob); printf(" stressed counters used=%zu OOB=%ld bytes\n", worst_used, worst_oob); /* Realistic trigger threshold (number of open hooks for overflow): */ int thresh = (int)((NG_TEXTRESPONSE - header_bytes) / per_fresh) + 1; printf(" overflow starts at >= %d open channels (fresh)\n", thresh); if (fresh_oob > 0) { printf("\nCONFIRMED: status_chans writes ~%zu bytes into a %d-byte " "kmalloc(M_NETGRAPH) response buffer -> %ld-byte heap overflow " "with all %d channels open. Content is partly attacker-influenced " "(counter magnitude via traffic pattern, channel open/close via " "ng_socket mkpeer). Reliable panic + heap grooming -> kernel " "code execution on systems where the musycc node can be created.\n", fresh_used, NG_TEXTRESPONSE, fresh_oob, NHDLC); return 0; } fprintf(stderr,"NOT CONFIRMED\n"); return 1; } |