DragonFlyBSD Kernel Audit
DF-2635 / fix.diff
← back to finding ↓ download raw
--- a/sys/vfs/hammer2/hammer2_inode.c
+++ b/sys/vfs/hammer2/hammer2_inode.c
@@ -621,13 +621,32 @@
 				KKASSERT(hammer2_mtx_refs(&ip->lock) == 0);
 				if (ip->flags & HAMMER2_INODE_ONHASH) {
 					xipp = &hash->base;
-					while (*xipp != ip)
+					/*
+					 * DF-2635: meta.inum can be
+					 * overwritten from media after the
+					 * inode was hashed (iroot in
+					 * hammer2_pfsalloc/hammer2_vfs_root).
+					 * Never walk off the end of the
+					 * (possibly wrong) bucket.
+					 */
+					while (*xipp && *xipp != ip)
 						xipp = &(*xipp)->next;
-					*xipp = ip->next;
-					ip->next = NULL;
-					atomic_add_long(&pmp->inum_count, -1);
-					atomic_clear_int(&ip->flags,
+					if (*xipp == ip) {
+						*xipp = ip->next;
+						ip->next = NULL;
+						atomic_add_long(
+							&pmp->inum_count, -1);
+						atomic_clear_int(&ip->flags,
 						     HAMMER2_INODE_ONHASH);
+					} else {
+						kprintf("hammer2: inode %p "
+							"inum %ld not on "
+							"inum hash\n",
+							ip,
+							(long)ip->meta.inum);
+						atomic_clear_int(&ip->flags,
+						     HAMMER2_INODE_ONHASH);
+					}
 				}
 				hammer2_spin_unex(&hash->spin);
 
--- a/sys/vfs/hammer2/hammer2_vfsops.c
+++ b/sys/vfs/hammer2/hammer2_vfsops.c
@@ -452,8 +452,11 @@
 	 */
 	if ((iroot = pmp->iroot) == NULL) {
 		iroot = hammer2_inode_get(pmp, NULL, 1, -1);
-		if (ripdata)
+		if (ripdata) {
 			iroot->meta = ripdata->meta;
+			/* DF-2635: iroot is hashed under inum 1; keep it */
+			iroot->meta.inum = 1;
+		}
 		pmp->iroot = iroot;
 		hammer2_inode_ref(iroot);
 		hammer2_inode_unlock(iroot);
@@ -1974,6 +1977,8 @@
 		if (error == 0) {
 			meta = &hammer2_xop_gdata(&xop->head)->ipdata.meta;
 			pmp->iroot->meta = *meta;
+			/* DF-2635: iroot is hashed under inum 1; keep it */
+			pmp->iroot->meta.inum = 1;
 			pmp->inode_tid = meta->pfs_inum + 1;
 			hammer2_xop_pdata(&xop->head);
 			/* meta invalid */