DF-0788 / 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 @@ -531,8 +531,28 @@ ddprintf(("type: 0x%x, index: %d", vap->va_type, vap->va_index)); + /* + * Validate on-disk offsets/lengths against the attribute's declared + * record length (a_hdr.reclen). Without these checks a crafted NTFS + * image can set a_nameoff / a_dataoff past the attribute body, causing + * reads past the MFT record buffer into adjacent kernel heap + * (DF-0788: resident a_dataoff info leak; same class for a_nameoff + * and the non-resident a_dataoff). + */ + if (rap->a_hdr.reclen < sizeof(struct attrhdr)) { + error = EINVAL; + goto out; + } + vap->va_namelen = rap->a_hdr.a_namelen; if (rap->a_hdr.a_namelen) { + if (rap->a_hdr.a_nameoff >= rap->a_hdr.reclen || + (u_int32_t)rap->a_hdr.a_namelen * sizeof(wchar) > + rap->a_hdr.reclen - rap->a_hdr.a_nameoff) { + error = EINVAL; + goto out; + } + { wchar *unp = (wchar *) ((caddr_t) rap + rap->a_hdr.a_nameoff); ddprintf((", name:[")); for (i = 0; i < vap->va_namelen; i++) { @@ -540,6 +560,7 @@ ddprintf(("%c", vap->va_name[i])); } ddprintf(("]")); + } } if (vap->va_flag & NTFS_AF_INRUN) { ddprintf((", nonres.")); @@ -548,6 +569,10 @@ vap->va_vcnstart = rap->a_nr.a_vcnstart; vap->va_vcnend = rap->a_nr.a_vcnend; vap->va_compressalg = rap->a_nr.a_compressalg; + if (rap->a_nr.a_dataoff >= rap->a_hdr.reclen) { + error = EINVAL; + goto out; + } error = ntfs_runtovrun(&(vap->va_vruncn), &(vap->va_vruncl), &(vap->va_vruncnt), (caddr_t) rap + rap->a_nr.a_dataoff); @@ -558,6 +583,12 @@ vap->va_allocated = rap->a_r.a_datalen; vap->va_vcnstart = 0; vap->va_vcnend = ntfs_btocn(vap->va_allocated); + if (rap->a_r.a_dataoff >= rap->a_hdr.reclen || + (u_int32_t)rap->a_r.a_datalen > + rap->a_hdr.reclen - rap->a_r.a_dataoff) { + error = EINVAL; + goto out; + } vap->va_datap = kmalloc(vap->va_datalen, M_NTFSRDATA, M_WAITOK); memcpy(vap->va_datap, (caddr_t) rap + rap->a_r.a_dataoff, @@ -565,6 +596,7 @@ } ddprintf((", len: %d", vap->va_datalen)); +out: if (error) kfree(vap, M_NTFSNTVATTR); else |