diff --git a/sys/vfs/hammer/hammer_ondisk.c b/sys/vfs/hammer/hammer_ondisk.c --- a/sys/vfs/hammer/hammer_ondisk.c +++ b/sys/vfs/hammer/hammer_ondisk.c @@ -1323,6 +1323,31 @@ node->flags |= HAMMER_NODE_CRCGOOD; } } + + /* + * Validate B-Tree node type and element count. These fields + * are read from disk and used as loop/bcopy bounds against the + * fixed-size elms[HAMMER_BTREE_LEAF_ELMS] array; without + * validation a forged node (CRC32C is not crypto-secure) can + * supply an out-of-range count, causing OOB reads/writes. + * Reject the node the same way a bad CRC is rejected. + */ + if (isnew == 0 && + (node->flags & HAMMER_NODE_CRCBAD) == 0) { + int max_elms; + + max_elms = hammer_node_max_elements(node->ondisk->type); + if (max_elms < 0 || + node->ondisk->count < 0 || + node->ondisk->count > max_elms) { + hdkprintf("B-TREE NODE @ %016jx BAD " + "type=%d count=%d\n", + (intmax_t)node->node_offset, + node->ondisk->type, + node->ondisk->count); + node->flags |= HAMMER_NODE_CRCBAD; + } + } } if (node->flags & HAMMER_NODE_CRCBAD) { if (trans->flags & HAMMER_TRANSF_CRCDOM) diff --git a/sys/vfs/hammer/hammer_cursor.c b/sys/vfs/hammer/hammer_cursor.c --- a/sys/vfs/hammer/hammer_cursor.c +++ b/sys/vfs/hammer/hammer_cursor.c @@ -199,8 +199,8 @@ cursor->node = node; if (error == 0) error = hammer_load_cursor_parent(cursor, 0); - KKASSERT(error == 0); - /* if (error) hammer_done_cursor(cursor); */ + if (error) + hammer_done_cursor(cursor); return(error); } diff --git a/sys/vfs/hammer/hammer_inode.c b/sys/vfs/hammer/hammer_inode.c --- a/sys/vfs/hammer/hammer_inode.c +++ b/sys/vfs/hammer/hammer_inode.c @@ -496,7 +496,11 @@ else cachep = &dip->cache[0]; } - hammer_init_cursor(trans, &cursor, cachep, NULL); + *errorp = hammer_init_cursor(trans, &cursor, cachep, NULL); + if (*errorp) { + hammer_free_inode(ip); + return NULL; + } cursor.key_beg.localization = localization | HAMMER_LOCALIZE_INODE; cursor.key_beg.obj_id = ip->obj_id; cursor.key_beg.key = 0;