DragonFlyBSD Kernel Audit
DF-0777 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/hammer2/hammer2_vnops.c b/sys/vfs/hammer2/hammer2_vnops.c
--- a/sys/vfs/hammer2/hammer2_vnops.c
+++ b/sys/vfs/hammer2/hammer2_vnops.c
@@ -700,14 +700,30 @@
 		hammer2_cluster_bref(&xop->head.cluster, &bref);
 
 		if (bref.type == HAMMER2_BREF_TYPE_INODE) {
+			uint16_t name_len;
+
 			ripdata = &hammer2_xop_gdata(&xop->head)->ipdata;
 			dtype = hammer2_get_dtype(ripdata->meta.type);
 			saveoff = bref.key & HAMMER2_DIRHASH_USERMSK;
+			/*
+			 * Validate on-disk name_len against the actual
+			 * filename field size to prevent an OOB read in
+			 * vop_write_dirent's bcopy.  A crafted image can
+			 * set name_len up to 65535; the filename field is
+			 * only HAMMER2_INODE_MAXNAME (256) bytes.
+			 */
+			name_len = ripdata->meta.name_len;
+			if (name_len >= HAMMER2_INODE_MAXNAME) {
+				kprintf("hammer2_readdir: ignoring inode "
+					"with bogus name_len %u\n", name_len);
+				hammer2_xop_pdata(&xop->head);
+				goto next;
+			}
 			r = vop_write_dirent(&error, uio,
 					     ripdata->meta.inum &
 					      HAMMER2_DIRHASH_USERMSK,
 					     dtype,
-					     ripdata->meta.name_len,
+					     name_len,
 					     ripdata->filename);
 			hammer2_xop_pdata(&xop->head);
 			if (r)
@@ -721,6 +737,23 @@
 			dtype = hammer2_get_dtype(bref.embed.dirent.type);
 			saveoff = bref.key & HAMMER2_DIRHASH_USERMSK;
 			namlen = bref.embed.dirent.namlen;
+			/*
+			 * Validate on-disk namlen.  For inline names
+			 * (namlen <= sizeof(check.buf) == 64) the source
+			 * buffer is check.buf (64 bytes), so cap there.
+			 * For out-of-line names the data block is at
+			 * least HAMMER2_ALLOC_MIN (1024) bytes, but we
+			 * cap at HAMMER2_INODE_MAXNAME (256) which is the
+			 * maximum legal filename length — anything larger
+			 * is a crafted image.
+			 */
+			if (namlen >= HAMMER2_INODE_MAXNAME) {
+				kprintf("hammer2_readdir: ignoring dirent "
+					"with bogus namlen %u\n", namlen);
+				if (namlen > sizeof(bref.check.buf))
+					hammer2_xop_pdata(&xop->head);
+				goto next;
+			}
 			if (namlen <= sizeof(bref.check.buf)) {
 				dname = bref.check.buf;
 			} else {
@@ -740,6 +773,8 @@
 			/* XXX chain error */
 			kprintf("bad chain type readdir %d\n", bref.type);
 		}
+next:
+		continue;
 	}
 	hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
 	if (error == ENOENT) {