--- a/sys/vfs/hammer2/hammer2_inode.c +++ b/sys/vfs/hammer2/hammer2_inode.c @@ -1154,9 +1154,31 @@ * inode. */ nip = hammer2_inode_get(dip->pmp, NULL, inum, -1); + /* + * hammer2_inode_get() returns the EXISTING inode when `inum` is + * already present in the pmp inum hash. That must never happen + * here: the inum was just allocated from pmp->inode_tid, whose + * seed (iroot meta.pfs_inum) comes from untrusted on-media data + * and can collide with an existing live inode on a crafted (or + * crash-inconsistent) image. The old KKASSERT compiled out on + * production kernels and create_normal then stomped the live + * inode's meta-data (type/uid/mode/nlinks/DIRECTDATA), flagged a + * fully-linked inode CREATING, and re-inserted a duplicate chain + * into the media topology at the next filesystem sync. + * + * Dispose of the found inode with hammer2_inode_unlock() only: + * its single reference is the one taken by the inum-hash lookup + * inside hammer2_inode_get(), and unlock() drops it. + */ + if ((nip->flags & HAMMER2_INODE_CREATING) != 0 || + nip->cluster.nchains != 0) { + kprintf("hammer2: duplicate inode number %ld detected, " + "create refused\n", (long)inum); + hammer2_inode_unlock(nip); + *errorp = HAMMER2_ERROR_EEXIST; + return NULL; + } nip->comp_heuristic = 0; - KKASSERT((nip->flags & HAMMER2_INODE_CREATING) == 0 && - nip->cluster.nchains == 0); atomic_set_int(&nip->flags, HAMMER2_INODE_CREATING); /*