Module Name: src Committed By: drochner Date: Sun Apr 3 10:04:32 UTC 2011
Modified Files: src/sys/net80211: ieee80211_crypto_tkip.c Log Message: make michael_mic() robust against degenerate mbuf layouts like odd sizes in the middle of a chain To generate a diff of this commit: cvs rdiff -u -r1.10 -r1.11 src/sys/net80211/ieee80211_crypto_tkip.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net80211/ieee80211_crypto_tkip.c diff -u src/sys/net80211/ieee80211_crypto_tkip.c:1.10 src/sys/net80211/ieee80211_crypto_tkip.c:1.11 --- src/sys/net80211/ieee80211_crypto_tkip.c:1.10 Wed Dec 17 20:51:37 2008 +++ src/sys/net80211/ieee80211_crypto_tkip.c Sun Apr 3 10:04:32 2011 @@ -34,7 +34,7 @@ __FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_tkip.c,v 1.10 2005/08/08 18:46:35 sam Exp $"); #endif #ifdef __NetBSD__ -__KERNEL_RCSID(0, "$NetBSD: ieee80211_crypto_tkip.c,v 1.10 2008/12/17 20:51:37 cegger Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ieee80211_crypto_tkip.c,v 1.11 2011/04/03 10:04:32 drochner Exp $"); #endif /* @@ -802,6 +802,8 @@ u32 l, r; const uint8_t *data; u_int space; + uint8_t spill[4]; + int nspill = 0; michael_mic_hdr(mtod(m, struct ieee80211_frame *), hdr); @@ -824,6 +826,20 @@ for (;;) { if (space > data_len) space = data_len; + if (nspill) { + int n = min(4 - nspill, space); + memcpy(spill + nspill, data, n); + nspill += n; + data += n; + space -= n; + data_len -= n; + if (nspill == 4) { + l ^= get_le32(spill); + michael_block(l, r); + nspill = 0; + } else + goto next; + } /* collect 32-bit blocks from current buffer */ while (space >= sizeof(uint32_t)) { l ^= get_le32(data); @@ -832,84 +848,27 @@ space -= sizeof(uint32_t); data_len -= sizeof(uint32_t); } - /* - * NB: when space is zero we make one more trip around - * the loop to advance to the next mbuf where there is - * data. This handles the case where there are 4*n - * bytes in an mbuf followed by <4 bytes in a later mbuf. - * By making an extra trip we'll drop out of the loop - * with m pointing at the mbuf with 3 bytes and space - * set as required by the remainder handling below. - */ - if (!data_len || (data_len < sizeof(uint32_t) && space != 0)) + if (space) { + memcpy(spill, data, space); + nspill = space; + data_len -= space; + } +next: + if (!data_len) break; m = m->m_next; - if (m == NULL) { - IASSERT(0, ("out of data, data_len %zu\n", data_len)); - break; - } - if (space != 0) { - const uint8_t *data_next; - /* - * Block straddles buffers, split references. - */ - data_next = mtod(m, const uint8_t *); - IASSERT(m->m_len >= sizeof(uint32_t) - space, - ("not enough data in following buffer, " - "m_len %u need %zu\n", m->m_len, - sizeof(uint32_t) - space)); - switch (space) { - case 1: - l ^= get_le32_split(data[0], data_next[0], - data_next[1], data_next[2]); - data = data_next + 3; - space = m->m_len - 3; - break; - case 2: - l ^= get_le32_split(data[0], data[1], - data_next[0], data_next[1]); - data = data_next + 2; - space = m->m_len - 2; - break; - case 3: - l ^= get_le32_split(data[0], data[1], - data[2], data_next[0]); - data = data_next + 1; - space = m->m_len - 1; - break; - } - michael_block(l, r); - data_len -= sizeof(uint32_t); - } else { - /* - * Setup for next buffer. - */ - data = mtod(m, const uint8_t *); - space = m->m_len; - } + KASSERT(m); + /* + * Setup for next buffer. + */ + data = mtod(m, const uint8_t *); + space = m->m_len; } - /* - * Catch degenerate cases like mbuf[4*n+1 bytes] followed by - * mbuf[2 bytes]. I don't believe these should happen; if they - * do then we'll need more involved logic. - */ - KASSERT(data_len <= space); - /* Last block and padding (0x5a, 4..7 x 0) */ - switch (data_len) { - case 0: - l ^= get_le32_split(0x5a, 0, 0, 0); - break; - case 1: - l ^= get_le32_split(data[0], 0x5a, 0, 0); - break; - case 2: - l ^= get_le32_split(data[0], data[1], 0x5a, 0); - break; - case 3: - l ^= get_le32_split(data[0], data[1], data[2], 0x5a); - break; - } + spill[nspill++] = 0x5a; + for (; nspill < 4; nspill++) + spill[nspill] = 0; + l ^= get_le32(spill); michael_block(l, r); /* l ^= 0; */ michael_block(l, r);