DF-3329 / df3329.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 | /* * DF-3329 harness -- HWMP action IE length "verifiers" read fields * beyond the IE before comparing the declared length. * * VERBATIM ieee80211_hwmp.c:317-386: * * verify_mesh_preq_len(): * if (iefrm[2] & IEEE80211_MESHPREQ_FLAGS_AE) { <-- reads byte 2 * ... * ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET_AE]; (33) * } else * ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET]; (27) * alloc_sz += ndest * IEEE80211_MESHPREQ_TRGT_SZ; * if(iefrm[1] != (alloc_sz)) ... <-- len checked LAST * * verify_mesh_perr_len(): * uint8_t ndest = iefrm_t[IEEE80211_MESHPERR_NDEST_OFFSET]; (3) * ... walks ndest destinations flag-walking the buffer ... * (only ndest<=19 is checked; nothing bounds the walk by frm[1]) * * A PREQ/PERR IE whose declared length is tiny but which sits at the * end of the frame buffer drives reads up to offset 33 (PREQ-AE) past * the buffer end. */ #include "guard.h" #define IEEE80211_MESHPREQ_BASE_SZ 26 #define IEEE80211_MESHPREQ_TCNT_OFFSET 27 #define IEEE80211_MESHPREQ_TCNT_OFFSET_AE 33 #define IEEE80211_MESHPREQ_TRGT_SZ 11 #define IEEE80211_MESHPREQ_FLAGS_AE 0x40 #define IEEE80211_MESHPERR_NDEST_OFFSET 3 #define IEEE80211_MESHPERR_MAXDEST 19 #define IEEE80211_MESHPERR_DEST_SZ 13 /* verbatim shape (log/discard calls removed, semantics identical) */ static int verify_mesh_preq_len(const uint8_t *iefrm) { int ndest, alloc_sz; if (iefrm[2] & IEEE80211_MESHPREQ_FLAGS_AE) ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET_AE]; else ndest = iefrm[IEEE80211_MESHPREQ_TCNT_OFFSET]; if (ndest < 0) /* emulate ndest as signed as in-kernel int */ return -1; alloc_sz = ndest * IEEE80211_MESHPREQ_TRGT_SZ; if (iefrm[1] != alloc_sz) return -1; return ndest; } /* verbatim shape */ static int verify_mesh_perr_len(const uint8_t *iefrm) { int i; uint8_t ndest; const uint8_t *iefrm_t = iefrm; ndest = iefrm_t[IEEE80211_MESHPERR_NDEST_OFFSET]; if (ndest > IEEE80211_MESHPERR_MAXDEST) return -1; iefrm_t += IEEE80211_MESHPERR_NDEST_OFFSET + 1; for (i = 0; i < ndest; i++) { if ((*iefrm_t) & 0x40) /* FLAGS_AE */ iefrm_t += 15; /* DEST_SZ_AE */ else iefrm_t += IEEE80211_MESHPERR_DEST_SZ; } return ndest; } static uint8_t *ie; static int r, which; static uint8_t *ie; static int r; int main(int argc, char **argv) { int which = (argc > 1) ? atoi(argv[1]) : 0; printf("DF-3329 (verify_mesh_preq_len / verify_mesh_perr_len," " hwmp.c:317-386)\n"); if (which == 1) { ie = guard_buf(3); ie[0] = 12; ie[1] = 2; ie[2] = 0x40; /* flags AE, len 2 */ printf(" PREQ: reads ndest at iefrm[33] of a 3-byte IE\n"); } else if (which == 2) { ie = guard_buf(2); ie[0] = 13; ie[1] = 1; printf(" PERR: reads ndest at iefrm[3] of a 2-byte IE\n"); } else if (which == 3) { ie = guard_buf(4); ie[0] = 13; ie[1] = 1; ie[3] = 1; printf(" PERR walk: reads dest flag at iefrm[4]\n"); } else { ie = guard_buf(64); /* control: full IE */ ie[0] = 13; ie[1] = 40; memset(ie + 2, 0, 62); printf(" control: well-formed IE\n"); } fflush(stdout); #ifdef FIXED /* DF-3329 fix: bound all IE field reads by the declared length */ { int ielen = ie[1]; int need = (which == 1) ? IEEE80211_MESHPREQ_TCNT_OFFSET_AE + 1 : IEEE80211_MESHPERR_NDEST_OFFSET + 1; if (ielen < need) { printf("FIXED: IE len %d < %d required, rejected\n", ielen, need); return 0; } } #endif if (which == 1) r = verify_mesh_preq_len(ie); else r = verify_mesh_perr_len(ie); printf(" ran (in bounds), r=%d\n", r); return 0; } |