โฌข DragonFlyBSD Kernel Audit
DF-0594 / fix_check.c
โ† back to finding โ†“ download raw
/*
 * DF-0594 โ€” fix-validation harness.
 *
 * This replicates the GUARD added by fix.diff at the top of tkip_decap() and
 * inside tkip_demic(), and demonstrates that the too-short trigger frame (the
 * same 32-byte frame the reproduction harness uses) is now REJECTED before the
 * vulnerable line-994 / line-357 arithmetic can run. It also confirms a
 * well-formed (long-enough) frame still passes the guard, proving the fix is
 * not over-restrictive.
 *
 * Build:
 *   cc -O2 -Wall -o fix_check fix_check.c
 *
 * Run:
 *   ./fix_check
 *
 * Expected (fix present): the 32-byte trigger frame is rejected at the
 * tkip_decap guard (returns 0, "too short for decrypt"); a 36-byte frame and a
 * 33-byte demic frame both pass their respective guards. Compare with the
 * unpatched reproduction harness, where the 32-byte frame reaches the
 * line-994 underflow.
 */

#include <stdio.h>
#include <stdint.h>

typedef unsigned int u_int;
typedef uint8_t u8;

#define IEEE80211_WEP_IVLEN     3
#define IEEE80211_WEP_KIDLEN    1
#define IEEE80211_WEP_EXTIVLEN  4
#define IEEE80211_WEP_CRCLEN    4
#define IEEE80211_WEP_MICLEN    8

static const struct { u_int ic_header, ic_trailer, ic_miclen; } tkip = {
    IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN,
    IEEE80211_WEP_CRCLEN, IEEE80211_WEP_MICLEN,
};

/*
 * Returns 1 if the frame passes the patched tkip_decap guard (long enough to
 * proceed to decrypt), 0 if rejected by the new length check. This is the
 * verbatim condition from fix.diff:
 *   if (m->m_pkthdr.len < hdrlen + (int)(tkip.ic_header + tkip.ic_trailer))
 *       return 0;
 */
static int
tkip_decap_guard_passes(int m_len, int hdrlen)
{
    if (m_len < hdrlen + (int)(tkip.ic_header + tkip.ic_trailer))
        return 0;   /* rejected by fix.diff guard */
    return 1;       /* proceeds to tkip_decrypt (line 994 arithmetic) */
}

/*
 * Returns 1 if the frame passes the patched tkip_demic guard (long enough for
 * MIC verification), 0 if rejected.
 */
static int
tkip_demic_guard_passes(int m_len, int hdrlen)
{
    if (m_len < hdrlen + (int)tkip.ic_miclen)
        return 0;   /* rejected by fix.diff guard */
    return 1;       /* proceeds to michael_mic (line 357 arithmetic) */
}

int
main(void)
{
    const int hdrlen = 24;
    const int decrypt_need = hdrlen + (int)(tkip.ic_header + tkip.ic_trailer); /* 36 */
    const int demic_need   = hdrlen + (int)tkip.ic_miclen;                     /* 32 */
    int fails = 0;

    fprintf(stderr, "=== DF-0594 fix-validation (patched tkip_decap/tkip_demic guards) ===\n");
    fprintf(stderr, "decrypt floor = hdrlen + ic_header + ic_trailer = %d\n", decrypt_need);
    fprintf(stderr, "demic   floor = hdrlen + ic_miclen             = %d\n\n", demic_need);

    /* Case 1: the 32-byte trigger frame from the reproduction harness.
     * Unpatched: reaches line 994 underflow -> panic/OOB read.
     * Patched:   rejected by the new tkip_decap guard. */
    {
        int m_len = 32;
        int ok = tkip_decap_guard_passes(m_len, hdrlen);
        fprintf(stderr, "[1] 32-byte trigger frame  tkip_decap: %s\n",
                ok ? "PASSES (BUG STILL REACHABLE)" : "REJECTED by guard (fix works)");
        if (ok) fails++;
    }
    /* Case 2: 35-byte frame (still < 36 decrypt floor). Patched: rejected. */
    {
        int m_len = 35;
        int ok = tkip_decap_guard_passes(m_len, hdrlen);
        fprintf(stderr, "[2] 35-byte frame           tkip_decap: %s\n",
                ok ? "PASSES (BUG STILL REACHABLE)" : "REJECTED by guard (fix works)");
        if (ok) fails++;
    }
    /* Case 3: 36-byte frame (exactly meets decrypt floor). Patched: passes,
     * and the line-994 arithmetic yields data_len = 0 (no underflow). */
    {
        int m_len = 36;
        int ok = tkip_decap_guard_passes(m_len, hdrlen);
        size_t dl = (size_t)((u_int)m_len - (u_int)(hdrlen + tkip.ic_header + tkip.ic_trailer));
        fprintf(stderr, "[3] 36-byte frame           tkip_decap: %s; line994 data_len=%zu %s\n",
                ok ? "passes (legitimate)" : "REJECTED (over-restrictive!)",
                dl, dl == 0 ? "(no underflow)" : "(UNDERFLOW!)");
        if (!ok) fails++;
        if (dl != 0) fails++;
    }
    /* Case 4: 31-byte frame at the demic path (HW-decrypt + SW-MIC). Patched:
     * rejected by the new tkip_demic guard. */
    {
        int m_len = 31;
        int ok = tkip_demic_guard_passes(m_len, hdrlen);
        fprintf(stderr, "[4] 31-byte frame           tkip_demic: %s\n",
                ok ? "PASSES (BUG STILL REACHABLE)" : "REJECTED by guard (fix works)");
        if (ok) fails++;
    }
    /* Case 5: 32-byte demic frame (meets demic floor exactly). Patched:
     * passes; the line-357 arithmetic yields data_len = 0 (no underflow). */
    {
        int m_len = 32;
        int ok = tkip_demic_guard_passes(m_len, hdrlen);
        size_t dl = (size_t)((u_int)m_len - (u_int)(hdrlen + tkip.ic_miclen));
        fprintf(stderr, "[5] 32-byte frame           tkip_demic: %s; line357 data_len=%zu %s\n",
                ok ? "passes (legitimate)" : "REJECTED (over-restrictive!)",
                dl, dl == 0 ? "(no underflow)" : "(UNDERFLOW!)");
        if (!ok) fails++;
        if (dl != 0) fails++;
    }

    fprintf(stderr, "\n");
    if (fails == 0) {
        fprintf(stderr, "RESULT: fix.diff GUARDS the vulnerable path โ€” too-short frames "
                "rejected, legitimate frames pass with no underflow. FIX VALIDATED.\n");
        return 0;
    }
    fprintf(stderr, "RESULT: %d check(s) failed โ€” fix does not fully close the path.\n", fails);
    return 2;
}