DF-3780 / fix.diff
--- a/sys/dev/virtual/vmware/vmxnet3/if_vmx.c +++ b/sys/dev/virtual/vmware/vmxnet3/if_vmx.c @@ -1919,7 +1919,8 @@ static void vmxnet3_txq_eof(struct vmxnet3_txqueue *txq) { struct vmxnet3_softc *sc; struct ifnet *ifp; + struct vmxnet3_txbuf *txb; struct vmxnet3_txring *txr; struct vmxnet3_comp_ring *txc; struct vmxnet3_txcompdesc *txcd; @@ -1936,7 +1937,9 @@ vmxnet3_txq_eof(struct vmxnet3_txqueue *t txc = &txq->vxtxq_comp_ring; VMXNET3_TXQ_LOCK_ASSERT(txq); + u_int eop_next, dist, outstanding; + for (;;) { txcd = &txc->vxcr_u.txcd[txc->vxcr_next]; if (txcd->gen != txc->vxcr_gen) @@ -1946,17 +1949,35 @@ vmxnet3_txq_eof(struct vmxnet3_txqueue *t txc->vxcr_gen ^= 1; } + /* + * DF-3780: txcd->eop_idx is host-supplied. Only accept + * completions whose end lands inside the window of + * descriptors the guest actually has outstanding. + */ + eop_next = (txcd->eop_idx + 1) % txr->vxtxr_ndesc; + dist = (eop_next + txr->vxtxr_ndesc - txr->vxtxr_next) % + txr->vxtxr_ndesc; + outstanding = (txr->vxtxr_head + txr->vxtxr_ndesc - + txr->vxtxr_next) % txr->vxtxr_ndesc; + if (dist > outstanding) + break; + sop = txr->vxtxr_next; txb = &txr->vxtxr_txbuf[sop]; if ((m = txb->vtxb_m) != NULL) { bus_dmamap_sync(txr->vxtxr_txtag, txb->vtxb_dmamap, @@ -1964,8 +1985,19 @@ vmxnet3_txq_eof(struct vmxnet3_txqueue *t m_freem(m); txb->vtxb_m = NULL; } - txr->vxtxr_next = (txcd->eop_idx + 1) % txr->vxtxr_ndesc; + /* + * Free every mbuf in the traversed window exactly once so + * a forged eop_idx cannot strand packets on skipped SOP + * slots (unbounded mbuf+cluster leak via the slot + * overwrite in vmxnet3_txq_encap). + */ + while (txr->vxtxr_next != eop_next) { + txb = &txr->vxtxr_txbuf[txr->vxtxr_next]; + if (txb->vtxb_m != NULL) { + bus_dmamap_sync(txr->vxtxr_txtag, + txb->vtxb_dmamap, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(txr->vxtxr_txtag, + txb->vtxb_dmamap); + m_freem(txb->vtxb_m); + txb->vtxb_m = NULL; + } + if (++txr->vxtxr_next == txr->vxtxr_ndesc) + txr->vxtxr_next = 0; + } } if (txr->vxtxr_head == txr->vxtxr_next) *** VERDICT NOTE: the window check (`dist > outstanding`) breaks the walk *** on a forged completion; the watchdog (vmxnet3_tick) then re-inits the *** queue, which is the intended recovery for a protocol-violating host. |