DragonFlyBSD Kernel Audit
DF-2640 / fix.diff
← back to finding ↓ download raw
--- a/sys/vfs/hammer2/hammer2_chain.c	2026-06-29 12:51:19.000000000 +0000
+++ b/sys/vfs/hammer2/hammer2_chain.c	2026-08-29 13:45:07.280745947 +0000
@@ -5772,6 +5772,14 @@
 	const hammer2_inode_data_t *ripdata;
 
 	if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) {
+		/*
+		 * DF-2640: an INODE chain whose data could not be loaded
+		 * (I/O or check error), or whose media data_off is 0
+		 * (unresolvable, see hammer2_chain_load_data()), must not
+		 * be dereferenced here.
+		 */
+		if (chain->error || chain->data == NULL)
+			return 0;
 		ripdata = &chain->data->ipdata;
 		if (ripdata->meta.name_len == name_len &&
 		    bcmp(ripdata->filename, name, name_len) == 0) {
@@ -5780,9 +5788,18 @@
 	}
 	if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT &&
 	    chain->bref.embed.dirent.namlen == name_len) {
-		if (name_len > sizeof(chain->bref.check.buf) &&
-		    bcmp(chain->data->buf, name, name_len) == 0) {
-			return 1;
+		/*
+		 * DF-2640: long names live in the dirent's data block.
+		 * If the data could not be loaded or data_off is 0 the
+		 * name is unreadable; do not dereference chain->data.
+		 * (Short names are embedded in the bref itself and are
+		 * still matchable below.)
+		 */
+		if (name_len > sizeof(chain->bref.check.buf)) {
+			if (chain->error || chain->data == NULL)
+				return 0;
+			if (bcmp(chain->data->buf, name, name_len) == 0)
+				return 1;
 		}
 		if (name_len <= sizeof(chain->bref.check.buf) &&
 		    bcmp(chain->bref.check.buf, name, name_len) == 0) {
--- a/sys/vfs/hammer2/hammer2_xops.c	2026-07-13 22:27:02.745458170 +0000
+++ b/sys/vfs/hammer2/hammer2_xops.c	2026-08-29 13:45:07.308745592 +0000
@@ -399,8 +399,12 @@
 		if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
 			type = chain->bref.embed.dirent.type;
 			dopermanent |= HAMMER2_DELETE_PERMANENT;
-		} else {
+		} else if (chain->data) {
 			type = chain->data->ipdata.meta.type;
+		} else {
+			error = chain->error ? chain->error :
+				HAMMER2_ERROR_EIO;
+			goto done;
 		}
 
 		/*
@@ -536,6 +540,15 @@
 			parent = NULL;
 			goto done;
 		}
+		if (chain->error || chain->data == NULL) {
+			error = chain->error ? chain->error :
+				HAMMER2_ERROR_EIO;
+			hammer2_chain_unlock(chain);
+			hammer2_chain_drop(chain);
+			chain = NULL;
+			parent = NULL;
+			goto done;
+		}
 		type = chain->data->ipdata.meta.type;
 		if (type == HAMMER2_OBJTYPE_DIRECTORY &&
 		    (error = checkdirempty(NULL, chain, clindex)) != 0)
@@ -666,6 +679,8 @@
 
 			error = hammer2_chain_modify(chain, xop->head.mtid,
 						     0, 0);
+			if (error == 0 && chain->data == NULL)
+				error = HAMMER2_ERROR_EIO;
 			if (error == 0) {
 				wipdata = &chain->data->ipdata;
 
@@ -714,6 +729,8 @@
 						    chain, xop->head.mtid,
 						    0, 0);
 				}
+				if (error == 0 && chain->data == NULL)
+					error = HAMMER2_ERROR_EIO;
 				if (error == 0) {
 					bzero(chain->data->buf, chain->bytes);
 					bcopy(xop->head.name2,
@@ -733,7 +750,9 @@
 	 * authority, but adjust the inode's iparent field too if the inode
 	 * is embedded in the directory.
 	 */
-	if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
+	if (error == 0 &&
+	    chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
+	    chain->data != NULL &&
 	    chain->data->ipdata.meta.iparent != xop->head.ip3->meta.inum) {
 		hammer2_inode_data_t *wipdata;
 
@@ -1614,6 +1633,8 @@
 	 */
 	if (error == 0) {
 		error = hammer2_chain_modify(parent, xop->head.mtid, 0, 0);
+		if (error == 0 && parent->data == NULL)
+			error = HAMMER2_ERROR_EIO;
 		if (error == 0) {
 			parent->data->ipdata.meta = xop->meta;
 			if (xop->clear_directdata) {