diff --git a/sys/bus/u4b/wlan/if_run.c b/sys/bus/u4b/wlan/if_run.c --- a/sys/bus/u4b/wlan/if_run.c +++ b/sys/bus/u4b/wlan/if_run.c @@ -2998,6 +2998,25 @@ dmalen + 8, xferlen); break; } + /* + * The per-frame DMA length is device-controlled and is only bounded + * above by the bulk RX URB length (RUN_MAX_RXSZ == MJUMPAGESIZE == + * 4096). When aggregated frames are copied out below, the destination + * is an mbuf cluster; reject any frame whose DMA region plus the + * trailing rt2870_rxd would not fit in a cluster (MCLBYTES == 2048), + * so a malicious/buggy device cannot drive an out-of-bounds heap write + * via m_copydata(). + */ + if (dmalen + sizeof(struct rt2870_rxd) > MCLBYTES) { + DPRINTF("oversized DMA length %u > %d\n", + dmalen + (int)sizeof(struct rt2870_rxd), MCLBYTES); +#if defined(__DragonFly__) + ++ic->ic_ierrors; +#else + counter_u64_add(ic->ic_ierrors, 1); +#endif + break; + } /* If it is the last one or a single frame, we won't copy. */ if ((xferlen -= dmalen + 8) <= 8) { @@ -3009,8 +3028,14 @@ break; } - /* copy aggregated frames to another mbuf */ - m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + /* + * Copy aggregated frames to another mbuf. The per-frame DMA length + * can be up to RUN_MAX_RXSZ, so the destination cluster must be at + * least MJUMPAGESIZE bytes (matching sc->rx_m above); a plain + * m_getcl() cluster is only MCLBYTES (2048) and would be overflowed + * by m_copydata() for any dmalen > ~2044. + */ + m0 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); if (__predict_false(m0 == NULL)) { DPRINTF("could not allocate mbuf\n"); #if defined(__DragonFly__)