DF-1310 / trigger.c
/* 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; } |