DF-0838 / fix.diff
diff --git a/sys/vfs/hammer/hammer.h b/sys/vfs/hammer/hammer.h --- a/sys/vfs/hammer/hammer.h +++ b/sys/vfs/hammer/hammer.h @@ -437,6 +437,7 @@ #define HAMMER_INODE_REDO 0x02000000 /* REDO logging active */ #define HAMMER_INODE_RDIRTY 0x04000000 /* REDO records active in fifo */ #define HAMMER_INODE_SLAVEFLUSH 0x08000000 /* being flushed by slave */ +#define HAMMER_INODE_BADDIRHASH 0x10000000 /* unknown dirhash alg, fall back to ALG0 */ #define HAMMER_INODE_MODMASK (HAMMER_INODE_DDIRTY|HAMMER_INODE_SDIRTY| \ HAMMER_INODE_XDIRTY|HAMMER_INODE_BUFS| \ diff --git a/sys/vfs/hammer/hammer_subs.c b/sys/vfs/hammer/hammer_subs.c --- a/sys/vfs/hammer/hammer_subs.c +++ b/sys/vfs/hammer/hammer_subs.c @@ -1026,9 +1026,25 @@ case HAMMER_INODE_CAP_DIRHASH_ALG2: case HAMMER_INODE_CAP_DIRHASH_ALG3: default: - key = 0; /* compiler warning */ - *max_iterationsp = 1; /* sanity */ - hpanic("bad algorithm %p", dip); + /* + * DF-0838: cap_flags is read verbatim from disk and an + * attacker-crafted (or simply corrupt) image can select + * an unimplemented dirhash algorithm. Panicking gives a + * trivial mount-time / directory-listing DoS, so fall + * back to ALG0 instead and log once per directory inode. + */ + if ((dip->flags & HAMMER_INODE_BADDIRHASH) == 0) { + dip->flags |= HAMMER_INODE_BADDIRHASH; + hdkprintf("dir ino %016jx has unknown dirhash alg %d, " + "falling back to ALG0\n", + (intmax_t)dip->obj_id, + dip->ino_data.cap_flags & + HAMMER_INODE_CAP_DIRHASH_MASK); + } + key = (int64_t)(crc32(aname, len) & 0x7FFFFFFF) << 32; + if (key == 0) + key |= 0x100000000LL; + *max_iterationsp = 0xFFFFFFFFU; break; } return(key); |