DF-3284 / df3284.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 | /* * DF-3284 harness -- ieee80211_output raw-injection path admits frames * of sizeof(struct ieee80211_frame_ack) = 10 bytes and then runs * ieee80211_classify() which parses the mbuf as an 802.3 frame * ("XXX assumes an 802.3 frame", output.c:570-577/703-705). * * VERBATIM ieee80211_output.c raw path: * :715 if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack)) * senderr(EIO); (10 bytes ARE admitted) * :723-736 dir TODS/DSTODS only gets sizeof(ieee80211_frame) (24) * :757 if (ieee80211_classify(ni, m)) <-- 802.3 parse of a 10-byte * 802.11 raw frame * VERBATIM ieee80211_classify (output.c:1180+): * const struct ether_header *eh = mtod(m, struct ether_header *); * if (eh->ether_type == htons(ETHERTYPE_PAE)) ... reads bytes 12..13 * if (eh->ether_type == htons(ETHERTYPE_IP)) * m_copydata(m, 14 + offsetof(struct ip, ip_tos), 1, &tos); * * and m_copydata() (uipc_mbuf.c) KASSERTs the chain is long enough: * KASSERT(m != NULL, ("m_copydata: length > size of mbuf chain")) * -> INVARIANTS kernel panics; without INVARIANTS bcopy reads garbage. * Both demonstrated here with the verbatim control flow and a faithful * m_copydata over a guard-placed "mbuf". */ #include "guard.h" struct eh { uint8_t d[6], s[6]; uint16_t type; } __packed; /* faithful m_copydata for a single-mbuf chain */ static void m_copydata(const unsigned char *mdata, int mlen, int off, int len, void *cp) { printf(" m_copydata(off=%d, len=%d) on chain of %d bytes\n", off, len, mlen); if (off >= mlen) { printf(" KASSERT \"m_copydata: offset > size of mbuf chain\"" " FIRES (INVARIANTS kernel: panic)\n"); /* kernel would stop here with a panic; emulate the read the * non-INVARIANTS kernel would perform: */ memcpy(cp, mdata + off, len); /* reads past the chain */ return; } memcpy(cp, mdata + off, len); } static int /* returns 1 = would have matched IP */ classify(const unsigned char *mdata, int mlen) { const struct eh *eh = (const struct eh *)mdata; printf(" classify: reading eh->ether_type at bytes 12..13 of a" " %d-byte frame\n", mlen); if (eh->type == 0x0806) /* any non-PAE/IP value; the READ is the bug */ return 1; if (eh->type == 0x0800) { unsigned char tos; m_copydata(mdata, mlen, 14 + 1, 1, &tos); return 1; } return 0; } static unsigned char *m; static int mlen, which; static unsigned char *m; static int mlen, which; int main(int argc, char **argv) { which = (argc > 1) ? atoi(argv[1]) : 0; printf("DF-3284 (raw 10-byte frames parsed as 802.3 by classify)\n"); if (which == 2) { mlen = 14; m = guard_buf(mlen); memset(m, 0, mlen); m[12] = 0x00; m[13] = 0x08; /* LE read of 0x0800 */ printf(" 14-byte frame, ether_type==IP: m_copydata(off=15,1)\n"); } else if (which == 1) { mlen = 10; m = guard_buf(mlen); memset(m, 0, mlen); printf(" 10-byte frame: classify reads eh->ether_type (12..13)\n"); } else { mlen = 40; m = guard_buf(mlen); memset(m, 0, mlen); printf(" control: 40-byte frame (well-formed 802.3)\n"); } fflush(stdout); #ifdef FIXED /* DF-3284 fix: raw 802.3 classification needs an 802.3 header */ if (mlen < sizeof(struct eh) + 1) { /* IP frames need TOS@15 */ printf("FIXED: raw frame %d bytes < 14 (802.3 header)," " rejected before classify\n", mlen); return 0; } #endif { int r = classify(m, mlen); printf(" ran (in bounds), r=%d\n", r); } return 0; } |