DF-3274 / df3274.c
/* * DF-3274 harness -- sppp TX accounting reads m->m_pkthdr.len AFTER * m_freem(m) on the control-queue-full branch. * * VERBATIM if_spppsubr.c (all three sites, e.g. sppp_cp_send :1429-1438): * if (IF_QFULL (&sp->pp_cpq)) { * IF_DROP (&sp->pp_fastq); * m_freem (m); * IFNET_STAT_INC(ifp, oerrors, 1); * } else * IF_ENQUEUE (&sp->pp_cpq, m); * ifsq = ...; (*ifp->if_start) (ifp, ifsq); * IFNET_STAT_INC(ifp, obytes, m->m_pkthdr.len + 3); <-- read after free * * A peer fills pp_cpq (IFQ_MAXLEN=50 deep) and any further control * transmit takes the free branch; the obytes accounting then reads the * freed mbuf header (stale-value read feeding a userspace-visible * counter: netstat -i). Guard-page demonstration of the post-free read. */ #include "guard.h" struct pkthdr_h { int len; }; struct mbufh { struct pkthdr_h m_pkthdr; }; static struct mbufh *m; static long obytes; static int qfull = 1; static void IFNET_STAT_INC_OBYTES(int v); static void cp_send_tail(void); static void m_freem_(void *p) { /* emulate: chunk returned to the slab free list; guard it so the * post-free read is caught (in-kernel: stale/recycled memory) */ mprotect((void *)((unsigned long)p & ~4095UL), 4096, PROT_NONE); } /* verbatim tail of sppp_cp_send (state/logging dropped) */ static void cp_send_tail(void) { if (qfull) { /* IF_DROP */ m_freem_(m); /* m_freem (m); */ /* IFNET_STAT_INC(ifp, oerrors, 1); */ } /* else IF_ENQUEUE */ /* (*ifp->if_start) (ifp, ifsq); */ IFNET_STAT_INC_OBYTES(m->m_pkthdr.len + 3); } static void IFNET_STAT_INC_OBYTES(int v) { obytes += v; } static void qfull_path(int len_) /* FIXED: length captured pre-free */ { m_freem_(m); IFNET_STAT_INC_OBYTES(len_ + 3); } int main(int argc, char **argv) { int uaf = (argc > 1 && argv[1][0] == 'u'); printf("DF-3274 (obytes accounting reads freed mbuf, if_spppsubr.c" " :1372-1380/:1429-1438/:4672-4681)\n"); m = (struct mbufh *)guard_buf(sizeof(*m)); m->m_pkthdr.len = 26; printf(" control queue full: m_freem(m), then obytes +=" " m->m_pkthdr.len + 3\n"); if (!uaf) { /* control: read BEFORE the free (as the fix would) */ int len = m->m_pkthdr.len; m_freem_(m); IFNET_STAT_INC_OBYTES(len + 3); printf(" ran (fixed order), obytes=%ld\n", obytes); return 0; } fflush(stdout); #ifdef FIXED /* DF-3274 fix: capture the length before the free */ { int len_ = m->m_pkthdr.len; printf("FIXED: length captured before free (len=%d)\n", len_); qfull_path(len_); return 0; } #endif cp_send_tail(); /* verbatim order: free, then read */ printf(" ran?!\n"); return 0; } |