DF-0789 / fix.diff
diff --git a/sys/vfs/ntfs/ntfs_subr.h b/sys/vfs/ntfs/ntfs_subr.h --- a/sys/vfs/ntfs/ntfs_subr.h +++ b/sys/vfs/ntfs/ntfs_subr.h @@ -84,7 +84,7 @@ int ntfs_times ( struct ntfsmount *, struct ntnode *, ntfs_times_t *); struct timespec ntfs_nttimetounix ( u_int64_t ); int ntfs_ntreaddir ( struct ntfsmount *, struct fnode *, u_int32_t, struct attr_indexentry **); -int ntfs_runtovrun ( cn_t **, cn_t **, u_long *, u_int8_t *); +int ntfs_runtovrun ( cn_t **, cn_t **, u_long *, u_int8_t *, size_t ); int ntfs_attrtontvattr ( struct ntfsmount *, struct ntvattr **, struct attr * ); void ntfs_freentvattr ( struct ntvattr * ); int ntfs_loadntvattrs ( struct ntfsmount *, struct vnode *, caddr_t, struct ntvattr **); diff --git a/sys/vfs/ntfs/ntfs_subr.c b/sys/vfs/ntfs/ntfs_subr.c --- a/sys/vfs/ntfs/ntfs_subr.c +++ b/sys/vfs/ntfs/ntfs_subr.c @@ -548,9 +548,17 @@ vap->va_vcnstart = rap->a_nr.a_vcnstart; vap->va_vcnend = rap->a_nr.a_vcnend; vap->va_compressalg = rap->a_nr.a_compressalg; - error = ntfs_runtovrun(&(vap->va_vruncn), &(vap->va_vruncl), + /* DF-0789: pass the run-list extent length so ntfs_runtovrun + * can bound its walk. reclen is the attribute's total size; + * a_dataoff is where the run list starts within it. */ + if (rap->a_hdr.reclen >= rap->a_nr.a_dataoff) { + size_t runlen = rap->a_hdr.reclen - rap->a_nr.a_dataoff; + error = ntfs_runtovrun(&(vap->va_vruncn), &(vap->va_vruncl), &(vap->va_vruncnt), - (caddr_t) rap + rap->a_nr.a_dataoff); + (caddr_t) rap + rap->a_nr.a_dataoff, runlen); + } else { + error = EINVAL; + } } else { vap->va_compressalg = 0; ddprintf((", res.")); @@ -579,10 +587,11 @@ * Expand run into more utilizable and more memory eating format. */ int -ntfs_runtovrun(cn_t **rcnp, cn_t **rclp, u_long *rcntp, u_int8_t *run) +ntfs_runtovrun(cn_t **rcnp, cn_t **rclp, u_long *rcntp, u_int8_t *run, + size_t runlen) { u_int32_t off; - u_int32_t sz, i; + u_int32_t sz, i, adv; cn_t *cn; cn_t *cl; u_long cnt; @@ -592,18 +601,31 @@ off = 0; cnt = 0; i = 0; - while (run[off]) { - off += (run[off] & 0xF) + ((run[off] >> 4) & 0xF) + 1; + /* DF-0789: bound both loops by the run-list extent length so a + * crafted run list without a zero terminator cannot walk past the + * attribute's data extent into adjacent kernel heap. Mirrors the + * bounds checks in the disabled ntfs_parserun() (:1760/:1770). */ + while (off < runlen && run[off]) { + adv = (run[off] & 0xF) + ((run[off] >> 4) & 0xF) + 1; + if (off + adv > runlen) { + kprintf("ntfs_runtovrun: malformed run list at " + "offset %u\n", off); + return (EINVAL); + } + off += adv; cnt++; } + if (cnt == 0) { + kprintf("ntfs_runtovrun: empty run list\n"); + return (EINVAL); + } cn = kmalloc(cnt * sizeof(cn_t), M_NTFSRUN, M_WAITOK); cl = kmalloc(cnt * sizeof(cn_t), M_NTFSRUN, M_WAITOK); off = 0; cnt = 0; prev = 0; - while (run[off]) { - + while (off < runlen && run[off]) { sz = run[off++]; cl[cnt] = 0; @@ -611,6 +633,13 @@ cl[cnt] += (u_int32_t) run[off++] << (i << 3); sz >>= 4; + if (sz == 0 || off + sz > runlen) { + kfree(cn, M_NTFSRUN); + kfree(cl, M_NTFSRUN); + kprintf("ntfs_runtovrun: malformed run list at " + "offset %u\n", off); + return (EINVAL); + } if (run[off + sz - 1] & 0x80) { tmp = ((u_int64_t) - 1) << (sz << 3); for (i = 0; i < sz; i++) |