DragonFlyBSD Kernel Audit
DF-1310 / trigger.c
← back to finding ↓ download raw
/* DF-1310 vtnet_rxeof signed RX-length overflow */
#include <stdio.h>
#include <stdint.h>
#include <limits.h>

#define VTNET_RX_HEADER_PAD 2
#define VTNET_HDR_SIZE 10
#define ETHER_HDR_LEN 14
#define VTNET_MAX_RX_SIZE 65550

static int replace_rxbuf_called_vuln, kassert_tripped_vuln, null_deref_vuln;
static void rxeof_vuln(int len){
    if (len < VTNET_HDR_SIZE + ETHER_HDR_LEN) { return; }  /* drop tiny */
    /* non-mergeable path: len += VTNET_RX_HEADER_PAD ; signed wrap if len near INT_MAX */
    len += VTNET_RX_HEADER_PAD;
    replace_rxbuf_called_vuln = 1;
    /* vtnet_replace_rxbuf: while(len > 0) {...} -> skipped if len <= 0 -> m_prev stays NULL */
    int m_prev_set = 0;
    while (len > 0) { m_prev_set = 1; break; }
    if (!m_prev_set) {
        /* KASSERT(m_prev != NULL) at if_vtnet.c:1319 panics (INVARIANTS)
         * or m_prev->m_next NULL deref at :1334 (production) */
        kassert_tripped_vuln = 1;   /* INVARIANTS panic */
    }
}
static void rxeof_fixed(int len){
    if (len < VTNET_HDR_SIZE + ETHER_HDR_LEN) return;
    /* fix: reject oversized lengths before the pad addition */
    if (len > VTNET_MAX_RX_SIZE + VTNET_HDR_SIZE) return;
    len += VTNET_RX_HEADER_PAD;
    int m_prev_set = 0;
    while (len > 0) { m_prev_set = 1; break; }
    if (!m_prev_set) null_deref_vuln = 1;   /* would only trip if len became 0, which it can't post-fix */
}

int main(void){
    int malicious_len = INT_MAX - 1;        /* 0x7FFFFFFE */
    printf("== BEFORE-FIX (vulnerable) ==\n");
    rxeof_vuln(malicious_len);
    printf("BUG: len=%d(0x%x) + pad(2) wraps to %d(0x%x) ; while(len>0) skipped -> m_prev==NULL -> KASSERT/NULL deref\n",
           malicious_len, (unsigned)malicious_len,
           malicious_len + VTNET_RX_HEADER_PAD, (unsigned)(malicious_len + VTNET_RX_HEADER_PAD));
    if (kassert_tripped_vuln) printf("       -> KASSERT(m_prev != NULL) at if_vtnet.c:1319 (INVARIANTS) or NULL deref :1334\n");
    printf("== AFTER-FIX ==\n");
    rxeof_fixed(malicious_len);
    printf("FIX: len > VTNET_MAX_RX_SIZE + hdr_size(%d) -> packet dropped, if_ierrors++\n",
           VTNET_MAX_RX_SIZE + VTNET_HDR_SIZE);
    return 0;
}