diff --git a/sys/vfs/hammer2/hammer2_vnops.c b/sys/vfs/hammer2/hammer2_vnops.c --- a/sys/vfs/hammer2/hammer2_vnops.c +++ b/sys/vfs/hammer2/hammer2_vnops.c @@ -700,14 +700,30 @@ hammer2_cluster_bref(&xop->head.cluster, &bref); if (bref.type == HAMMER2_BREF_TYPE_INODE) { + uint16_t name_len; + ripdata = &hammer2_xop_gdata(&xop->head)->ipdata; dtype = hammer2_get_dtype(ripdata->meta.type); saveoff = bref.key & HAMMER2_DIRHASH_USERMSK; + /* + * Validate on-disk name_len against the actual + * filename field size to prevent an OOB read in + * vop_write_dirent's bcopy. A crafted image can + * set name_len up to 65535; the filename field is + * only HAMMER2_INODE_MAXNAME (256) bytes. + */ + name_len = ripdata->meta.name_len; + if (name_len >= HAMMER2_INODE_MAXNAME) { + kprintf("hammer2_readdir: ignoring inode " + "with bogus name_len %u\n", name_len); + hammer2_xop_pdata(&xop->head); + goto next; + } r = vop_write_dirent(&error, uio, ripdata->meta.inum & HAMMER2_DIRHASH_USERMSK, dtype, - ripdata->meta.name_len, + name_len, ripdata->filename); hammer2_xop_pdata(&xop->head); if (r) @@ -721,6 +737,23 @@ dtype = hammer2_get_dtype(bref.embed.dirent.type); saveoff = bref.key & HAMMER2_DIRHASH_USERMSK; namlen = bref.embed.dirent.namlen; + /* + * Validate on-disk namlen. For inline names + * (namlen <= sizeof(check.buf) == 64) the source + * buffer is check.buf (64 bytes), so cap there. + * For out-of-line names the data block is at + * least HAMMER2_ALLOC_MIN (1024) bytes, but we + * cap at HAMMER2_INODE_MAXNAME (256) which is the + * maximum legal filename length — anything larger + * is a crafted image. + */ + if (namlen >= HAMMER2_INODE_MAXNAME) { + kprintf("hammer2_readdir: ignoring dirent " + "with bogus namlen %u\n", namlen); + if (namlen > sizeof(bref.check.buf)) + hammer2_xop_pdata(&xop->head); + goto next; + } if (namlen <= sizeof(bref.check.buf)) { dname = bref.check.buf; } else { @@ -740,6 +773,8 @@ /* XXX chain error */ kprintf("bad chain type readdir %d\n", bref.type); } +next: + continue; } hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); if (error == ENOENT) {