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