DF-4829 / fix.diff
--- a/sys/dev/disk/md/md.c +++ b/sys/dev/disk/md/md.c @@ -32,6 +32,9 @@ #define MD_NSECT (10000 * 2) #endif +/* Sanity cap for the MD_MALLOC sector pointer array (2^24 sectors = 8 GB). */ +#define MD_MAX_NSECT (1U << 24) + MALLOC_DEFINE(M_MD, "MD disk", "Memory Disk"); MALLOC_DEFINE(M_MDSECT, "MD sectors", "Memory Disk Sectors"); @@ -204,7 +207,8 @@ cdev_t dev = ap->a_head.a_dev; struct bio *bio = ap->a_bio; struct buf *bp = bio->bio_buf; - unsigned secno, nsec, secval, uc; + uint64_t secno; + unsigned nsec, secval, uc; u_char *secp, **secpp, *dst; struct md_s *sc; int i; @@ -250,8 +254,27 @@ } nsec = bp->b_bcount >> DEV_BSHIFT; - secno = (unsigned)(bio->bio_offset >> DEV_BSHIFT); + /* + * Keep the full 64-bit sector number. Truncating to + * unsigned made (secno + nsec + 1) below wrap at 2^32, + * turning the array growth kmalloc() into kmalloc(0) (which + * returns ZERO_LENGTH_PTR) or a wild undersized allocation + * followed by an out-of-bounds bcopy() and a wild write via + * &sc->secp[secno]. + */ + secno = (uint64_t)bio->bio_offset >> DEV_BSHIFT; dst = bp->b_data; + + if (bio->bio_offset < 0 || secno + nsec > MD_MAX_NSECT) { + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + bp->b_resid = bp->b_bcount; + devstat_end_transaction_buf(&sc->stats, bp); + biodone(bio); + crit_enter(); + continue; + } + while (nsec--) { if (secno < sc->nsecp) { secpp = &sc->secp[secno]; @@ -300,13 +323,14 @@ *secpp = (u_char *)(uintptr_t)uc; } else { if (!secpp) { - secpp = kmalloc((secno + nsec + 1) * sizeof(u_char *), + secpp = kmalloc((size_t)(secno + nsec + 1) * + sizeof(u_char *), M_MD, M_WAITOK | M_ZERO); bcopy(sc->secp, secpp, sc->nsecp * sizeof(u_char *)); kfree(sc->secp, M_MD); sc->secp = secpp; - sc->nsecp = secno + nsec + 1; + sc->nsecp = (unsigned)(secno + nsec + 1); secpp = &sc->secp[secno]; } if (i == DEV_BSIZE) { |