DF-1487 / 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 131 | /* * DF-1487 harness โ wb_encap OOB write into wb_frag[16] * * sys/dev/netif/wb/if_wb.c:1221-1238 fill loop: stops at frag==WB_MAXFRAGS(16) * :1248 if (m != NULL) coalesce branch โ SKIPPED if we consumed exactly * 16 mbufs and m went NULL via m->m_next * :1274 if (total_len < WB_MIN_FRAMELEN(60)) padding branch * :1275-1280 f = &c->wb_ptr->wb_frag[frag=16] <- one past end of * struct wb_txdesc.wb_frag[16] (if_wbreg.h:291) * writes f->wb_ctl/wb_data/wb_status = 12 bytes OOB * :1284 c->wb_lastdesc = frag-1 = 16 * :1285 WB_TXCTL(c) |= LASTFRAG -> wb_frag[16].wb_ctl |= ... RMW (OOB) * * Trigger: an outgoing mbuf chain with EXACTLY 16 non-empty mbufs whose * total m_len sum is < 60 bytes โ e.g. sendmsg with 16-byte iov using * AF_INET SOCK_RAW IP_HDRINCL with a tiny IP header in the first mbuf. * * Guest has no Winbond W89C840F NIC, so this is a harness proof against * the genuine wb_encap fragment-fill + padding arithmetic. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define WB_MAXFRAGS 16 #define WB_MIN_FRAMELEN 60 struct wb_desc { uint32_t wb_status; uint32_t wb_ctl; uint32_t wb_data; uint32_t wb_next; }; struct wb_txdesc { struct wb_desc wb_frag[WB_MAXFRAGS]; }; /* Track which indices get written. Returns: 0 = in-bounds, 1 = OOB write. */ static int wb_encap_harness(int nmibs, int m_len_each, int *oob_idx) { struct wb_txdesc tx; memset(&tx, 0, sizeof(tx)); /* "canary" past the end โ anything written here is OOB */ uint32_t canary[8] = { 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF }; struct wb_desc *frag_base = tx.wb_frag; int total_len = 0; int frag = 0; int m; for (m = 0; m < nmibs; m++) { if (m_len_each != 0) { if (frag == WB_MAXFRAGS) break; total_len += m_len_each; frag_base[frag].wb_ctl = 0x1000 | m_len_each; frag_base[frag].wb_data = 0xCAFEBABE + frag; frag_base[frag].wb_status= 0; frag++; } } int m_is_null = (m >= nmibs); /* simulating m==NULL after the loop */ /* line 1248: coalesce branch fires only if m != NULL */ if (!m_is_null) { /* would copy to a cluster; we model that as: frag=1, total_len=big */ frag = 1; total_len = WB_MIN_FRAMELEN + 100; } /* line 1274: padding branch */ if (total_len < WB_MIN_FRAMELEN) { struct wb_desc *f = &frag_base[frag]; /* line 1275 */ f->wb_ctl = WB_MIN_FRAMELEN - total_len; /* line 1276 */ f->wb_data = 0xCAFEBABE; /* line 1277 vtophys(&wb_pad) */ f->wb_ctl |= 0x200; /* line 1278 TXCTL_TLINK */ f->wb_status = 0x8000; /* line 1279 TXSTAT_OWN */ frag++; /* line 1280 */ } int lastdesc = frag - 1; /* line 1284 */ /* line 1285: WB_TXCTL(c) |= LASTFRAG */ frag_base[lastdesc].wb_ctl |= 0x40000000; *oob_idx = -1; if (lastdesc >= WB_MAXFRAGS) { *oob_idx = lastdesc; return 1; /* OOB */ } /* check canary: did frag_base[16..] get hit? */ for (int i = WB_MAXFRAGS; i < WB_MAXFRAGS + 4; i++) { /* the harness allocation is exactly WB_MAXFRAGS long; the canary * simulation only makes sense if lastdesc==16, which we caught above. */ } (void)canary; return 0; } int main(void) { int confs[][2] = { /* {n_mibs, m_len_each} */ { 16, 1 }, /* 16 mbufs of 1 byte each: total=16 < 60 -> BUG */ { 16, 2 }, /* 32 < 60 -> BUG */ { 16, 3 }, /* 48 < 60 -> BUG */ { 16, 4 }, /* 64 >= 60 -> no padding branch (in-bounds) */ { 15, 1 }, /* 15 < 60 but frag==15 in-bounds */ { 17, 1 }, /* coalesce branch fires (m!=NULL) */ }; int n = sizeof(confs)/(2*sizeof(int)); int bugs = 0; printf("%-22s %10s %14s %10s\n","config","total_len","coalesce?","result"); for (int i=0;i<n;i++) { int mb = confs[i][0], len = confs[i][1]; int total = mb*len; int oob_idx = -1; /* For the 17-mbuf case, the loop breaks at frag==16 with m!=NULL */ int rc = wb_encap_harness(mb, len, &oob_idx); const char *coalesced = (mb > 16) ? "yes" : "no"; printf("%-22s %10d %14s %10s\n", (i==0?"16x1":i==1?"16x2":i==2?"16x3":i==3?"16x4":i==4?"15x1":"17x1"), total, coalesced, rc? "OOB!" : "in-bounds"); if (rc) bugs++; } printf("\nBuggy configs: %d/%d write past wb_frag[15]\n", bugs, n); if (bugs >= 1) { printf("CONFIRMED: wb_encap writes 12+ bytes (wb_ctl/wb_data/wb_status) " "past the end of struct wb_txdesc (wb_frag[16..]) when the chain " "has exactly 16 non-empty mbufs summing to < 60 bytes, plus a " "4-byte RMW via WB_TXCTL(c). On real hardware this corrupts " "adjacent kernel heap (or the next contiguous wb_txdesc when " "wb_ptr==wb_tx_list[127]).\n"); return 0; } fprintf(stderr,"NOT CONFIRMED\n"); return 1; } |