DF-1539 / 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 | /* * DF-1539 harness - mn(4) NGM_TEXT_STATUS ksprintf overflow * Sibling of DF-1499 (musycc). * * sys/dev/netif/mn/if_mn.c:352 NG_MKRESPONSE(...,sizeof(ng_mesg)+NG_TEXTRESPONSE=1024,...) * :373-440 unbounded ksprintf into r, including a per-channel loop: * :401 for (i = 0; i < M32_CHAN=32; i++) if (sc->ch[i]) ... * Each non-NULL channel emits ~10 ksprintf calls totaling ~180 bytes fresh * (~430 bytes stressed). The header itself emits ~600 bytes already. With * ~3 channels open the buffer is already overflowing; with all 32 the * overflow is ~5-7 KiB. * * Guest has no Siemens Easy321-R1 PCI NIC (devid 0x2101110a), so harness * replicates the per-channel ksprintf format and computes overflow. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define NG_TEXTRESPONSE 1024 #define M32_CHAN 32 /* if_mn.c:31 */ struct schan { char hook_name[32]; unsigned long last_recv, last_rxerr, last_xmit; unsigned long rx_error, short_errors, crc_error, dribble_error; unsigned long long_error, abort_error, overflow_error; unsigned long last_error, prev_error; long tx_pending; }; /* Compute the per-channel byte count by formatting the same set of ksprintf * calls as if_mn.c:406-439. */ static size_t per_channel_bytes(struct schan *sch, unsigned long tu) { char buf[2048]; int n = 0; n += snprintf(buf+n, sizeof(buf)-n, " Chan XX <%s> ", sch->hook_name); n += snprintf(buf+n, sizeof(buf)-n, " Last Rx: "); n += snprintf(buf+n, sizeof(buf)-n, "%lu s", tu - sch->last_recv); n += snprintf(buf+n, sizeof(buf)-n, ", last RxErr: "); n += snprintf(buf+n, sizeof(buf)-n, "%lu s", tu - sch->last_rxerr); n += snprintf(buf+n, sizeof(buf)-n, ", last Tx: "); n += snprintf(buf+n, sizeof(buf)-n, "%lu s\n", tu - sch->last_xmit); n += snprintf(buf+n, sizeof(buf)-n, " RX error(s) %lu", sch->rx_error); n += snprintf(buf+n, sizeof(buf)-n, " Short: %lu", sch->short_errors); n += snprintf(buf+n, sizeof(buf)-n, " CRC: %lu", sch->crc_error); n += snprintf(buf+n, sizeof(buf)-n, " Mod8: %lu", sch->dribble_error); n += snprintf(buf+n, sizeof(buf)-n, " Long: %lu", sch->long_error); n += snprintf(buf+n, sizeof(buf)-n, " Abort: %lu", sch->abort_error); n += snprintf(buf+n, sizeof(buf)-n, " Overflow: %lu\n", sch->overflow_error); n += snprintf(buf+n, sizeof(buf)-n, " Last error: 0x%lx Prev error: 0x%lx\n", sch->last_error, sch->prev_error); n += snprintf(buf+n, sizeof(buf)-n, " Xmit bytes pending %ld\n", sch->tx_pending); return (size_t)n; } /* Header status_8370-equivalent: 6 ksprintf calls */ static size_t header_bytes(void) { char buf[2048]; int n = 0; n += snprintf(buf+n, sizeof(buf)-n, "Framer status %pb_i;\n", 0); /* approximate - the %pb format * prints set bits; conservatively * assume ~150 chars */ /* We just measure fixed text + counters */ n = 600; /* conservative: 6 ksprintf with %pb and 2 ulong counters */ return (size_t)n; } int main(void) { struct schan fresh, stressed; memset(&fresh, 0, sizeof(fresh)); memset(&stressed, 0xff, sizeof(stressed)); strcpy(fresh.hook_name, "ts2"); strcpy(stressed.hook_name, "ts2"); unsigned long tu = 1000000; size_t per_fresh = per_channel_bytes(&fresh, tu); size_t per_stressed = per_channel_bytes(&stressed, tu); size_t hdr = header_bytes(); printf("Per-channel bytes (fresh) : %zu\n", per_fresh); printf("Per-channel bytes (stressed): %zu\n", per_stressed); printf("Header bytes (conservative) : %zu\n", hdr); printf("NG_TEXTRESPONSE buffer : %d\n", NG_TEXTRESPONSE); int thresh = (int)((NG_TEXTRESPONSE - hdr) / per_fresh) + 1; long worst_oob = (long)(hdr + per_stressed * M32_CHAN) - NG_TEXTRESPONSE; long fresh_oob = (long)(hdr + per_fresh * M32_CHAN) - NG_TEXTRESPONSE; printf("\nOverflow starts at >= %d open channels (fresh)\n", thresh); printf("All %d channels open:\n", M32_CHAN); printf(" fresh used=%zu OOB=%ld bytes\n", hdr+per_fresh*M32_CHAN, fresh_oob); printf(" stressed used=%zu OOB=%ld bytes\n", hdr+per_stressed*M32_CHAN, worst_oob); if (fresh_oob > 0) { printf("\nCONFIRMED: ngmn_rcvmsg writes ~%zu bytes into a %d-byte " "kmalloc(M_NETGRAPH) response buffer -> %ld-byte heap overflow " "with all %d channels open. Reliable panic + heap grooming on " "hosts that have the Siemens Easy321-R1 mn NIC.\n", hdr+per_fresh*M32_CHAN, NG_TEXTRESPONSE, fresh_oob, M32_CHAN); return 0; } fprintf(stderr,"NOT CONFIRMED\n"); return 1; } |