A crafted EROFS image can contain an out-of-range node ID in directory entries or the superblock root_nid that causes erofs_iloc() to compute an inode offset beyond the image size. This leads to out-of-bounds reads in erofs_read_metabuf(), potentially crashing fsck.erofs, erofsfuse, or dump.erofs.
Add a bounds check at the start of erofs_read_inode_from_disk() to verify that the computed inode offset plus the minimum on-disk inode size (erofs_inode_compact) does not exceed the primary device size. Return -EFSCORRUPTED if the check fails. This also deduplicates the erofs_iloc() computation which was previously evaluated twice. Signed-off-by: Utkal Singh <[email protected]> --- lib/namei.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/namei.c b/lib/namei.c index 896e348..8a83166 100644 --- a/lib/namei.c +++ b/lib/namei.c @@ -25,8 +25,9 @@ static dev_t erofs_new_decode_dev(u32 dev) int erofs_read_inode_from_disk(struct erofs_inode *vi) { struct erofs_sb_info *sbi = vi->sbi; - erofs_blk_t blkaddr = erofs_blknr(sbi, erofs_iloc(vi)); - unsigned int ofs = erofs_blkoff(sbi, erofs_iloc(vi)); + erofs_off_t inode_loc = erofs_iloc(vi); + erofs_blk_t blkaddr; + unsigned int ofs; bool in_mbox = erofs_inode_in_metabox(vi); struct erofs_buf buf = __EROFS_BUF_INITIALIZER; erofs_blk_t addrmask = BIT_ULL(48) - 1; @@ -36,6 +37,18 @@ int erofs_read_inode_from_disk(struct erofs_inode *vi) void *ptr; int err = 0; + if (!in_mbox && sbi->primarydevice_blocks && + inode_loc + sizeof(struct erofs_inode_compact) > + erofs_pos(sbi, sbi->primarydevice_blocks)) { + erofs_err("invalid nid %llu (inode location %llu beyond image size %llu)", + vi->nid | 0ULL, inode_loc | 0ULL, + erofs_pos(sbi, sbi->primarydevice_blocks) | 0ULL); + return -EFSCORRUPTED; + } + + blkaddr = erofs_blknr(sbi, inode_loc); + ofs = erofs_blkoff(sbi, inode_loc); + ptr = erofs_read_metabuf(&buf, sbi, erofs_pos(sbi, blkaddr), in_mbox); if (IS_ERR(ptr)) { err = PTR_ERR(ptr); -- 2.43.0
