DragonFlyBSD Kernel Audit
DF-0788 / fix.diff
← back to finding ↓ download raw
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