DF-0787 / fix.diff
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 @@ -302,21 +302,49 @@ } dprintf(("ntfs_loadntnode: load attrs for ino: %"PRId64"\n",ip->i_number)); - off = mfrp->fr_attroff; - ap = (struct attr *) ((caddr_t)mfrp + off); + { + size_t recsz = ntfs_bntob(ntmp->ntm_bpmftrec); - LIST_INIT(&ip->i_valist); + off = mfrp->fr_attroff; + /* DF-0787: bound the attribute walk by the MFT record size and + * reject malformed entries (zero/huge reclen, overflowed offset) + * before dereferencing the next attribute header. Without this, + * a crafted MFT record causes ntfs_loadntnode() to loop forever, + * leaking struct ntvattr allocations until the slab allocator + * panics with "malloc limit exceeded". */ + if (off < sizeof(struct filerec) || off >= (int)recsz) { + error = EINVAL; + kprintf("ntfs_loadntnode: bad attribute offset %d for " + "ino %"PRId64"\n", off, ip->i_number); + goto out; + } + ap = (struct attr *) ((caddr_t)mfrp + off); - while (ap->a_hdr.a_type != -1) { - error = ntfs_attrtontvattr(ntmp, &nvap, ap); - if (error) - break; - nvap->va_ip = ip; + LIST_INIT(&ip->i_valist); - LIST_INSERT_HEAD(&ip->i_valist, nvap, va_list); + while (ap->a_hdr.a_type != -1) { + /* The attribute header must fit in the remaining record bytes. */ + if ((size_t)off + sizeof(struct attrhdr) > recsz) { + error = EINVAL; + break; + } + /* reclen must be sane: at least the header, and not past the end. */ + if (ap->a_hdr.reclen < sizeof(struct attrhdr) || + ap->a_hdr.reclen > recsz - off) { + error = EINVAL; + break; + } - off += ap->a_hdr.reclen; - ap = (struct attr *) ((caddr_t)mfrp + off); + error = ntfs_attrtontvattr(ntmp, &nvap, ap); + if (error) + break; + nvap->va_ip = ip; + + LIST_INSERT_HEAD(&ip->i_valist, nvap, va_list); + + off += ap->a_hdr.reclen; + ap = (struct attr *) ((caddr_t)mfrp + off); + } } if (error) { kprintf("ntfs_loadntnode: failed to load attr ino: %"PRId64"\n", |