VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-46078

CVE-2026-46078

Description

In the Linux kernel, the following vulnerability has been resolved:

erofs: fix the out-of-bounds nameoff handling for trailing dirents

Currently we already have boundary-checks for nameoffs, but the trailing dirents are special since the namelens are calculated with strnlen() with unchecked nameoffs.

If a crafted EROFS has a trailing dirent with nameoff >= maxsize, maxsize - nameoff can underflow, causing strnlen() to read past the directory block.

nameoff0 should also be verified to be a multiple of sizeof(struct erofs_dirent) as well [1].

[1] https://sashiko.dev/#/patchset/20260416063511.3173774-1-hsiangkao%40linux.alibaba.com

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

In the Linux kernel, an out-of-bounds read in EROFS directory handling due to insufficient bounds checking on nameoff for trailing dirents could lead to information disclosure.

Vulnerability

The EROFS filesystem implementation in the Linux kernel has an out-of-bounds read vulnerability in directory entry (dirent) handling. When processing trailing dirents, the namelen is computed using strnlen() with an unchecked nameoff. If a crafted EROFS image has a trailing dirent with nameoff >= maxsize, the subtraction maxsize - nameoff can underflow, causing strnlen() to read past the directory block. Additionally, nameoff0 should be verified to be a multiple of sizeof(struct erofs_dirent). Affected are kernel versions prior to the commit fix [1].

Exploitation

An attacker can exploit this vulnerability by mounting a maliciously crafted EROFS filesystem. This requires the ability to mount a filesystem, which may be possible for unprivileged users if configured (e.g., via user mount option). No authentication is needed beyond mount access.

Impact

Successful exploitation results in an out-of-bounds read from kernel memory, potentially leading to information disclosure of sensitive data. The read may also cause a kernel panic (denial of service). The specific impact depends on the memory layout and what data is leaked.

Mitigation

The vulnerability is fixed in commit 8ebb951a284b7446e025afc7dc5e9516ef9a7214 in the stable Linux kernel tree [1]. Users should apply this patch or update to a kernel version containing the fix. No known workarounds exist.

AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2

Patches

10
d18a3b5d337f

erofs: fix the out-of-bounds nameoff handling for trailing dirents

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitGao XiangApr 21, 2026Fixed in 7.1-rc1via kernel-cna
1 file changed · +15 14
  • fs/erofs/dir.c+15 14 modified
    diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c
    index e5132575b9d3ef..4aa52a5f204a0f 100644
    --- a/fs/erofs/dir.c
    +++ b/fs/erofs/dir.c
    @@ -19,20 +19,18 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		const char *de_name = (char *)dentry_blk + nameoff;
     		unsigned int de_namelen;
     
    -		/* the last dirent in the block? */
    -		if (de + 1 >= end)
    -			de_namelen = strnlen(de_name, maxsize - nameoff);
    -		else
    +		/* non-trailing dirent in the directory block? */
    +		if (de + 1 < end)
     			de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
    +		else if (maxsize <= nameoff)
    +			goto err_bogus;
    +		else
    +			de_namelen = strnlen(de_name, maxsize - nameoff);
     
    -		/* a corrupted entry is found */
    -		if (nameoff + de_namelen > maxsize ||
    -		    de_namelen > EROFS_NAME_LEN) {
    -			erofs_err(dir->i_sb, "bogus dirent @ nid %llu",
    -				  EROFS_I(dir)->nid);
    -			DBG_BUGON(1);
    -			return -EFSCORRUPTED;
    -		}
    +		/* a corrupted entry is found (including negative namelen) */
    +		if (!in_range32(de_namelen, 1, EROFS_NAME_LEN) ||
    +		    nameoff + de_namelen > maxsize)
    +			goto err_bogus;
     
     		if (!dir_emit(ctx, de_name, de_namelen,
     			      erofs_nid_to_ino64(EROFS_SB(dir->i_sb),
    @@ -42,6 +40,10 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		ctx->pos += sizeof(struct erofs_dirent);
     	}
     	return 0;
    +err_bogus:
    +	erofs_err(dir->i_sb, "bogus dirent @ nid %llu", EROFS_I(dir)->nid);
    +	DBG_BUGON(1);
    +	return -EFSCORRUPTED;
     }
     
     static int erofs_readdir(struct file *f, struct dir_context *ctx)
    @@ -88,7 +90,7 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx)
     		}
     
     		nameoff = le16_to_cpu(de->nameoff);
    -		if (nameoff < sizeof(struct erofs_dirent) || nameoff >= bsz) {
    +		if (!nameoff || nameoff >= bsz || (nameoff % sizeof(*de))) {
     			erofs_err(sb, "invalid de[0].nameoff %u @ nid %llu",
     				  nameoff, EROFS_I(dir)->nid);
     			err = -EFSCORRUPTED;
    -- 
    cgit 1.3-korg
    
    
    
222055e6b406

erofs: fix the out-of-bounds nameoff handling for trailing dirents

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitGao XiangApr 21, 2026Fixed in 6.6.140via kernel-cna
1 file changed · +15 14
  • fs/erofs/dir.c+15 14 modified
    diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c
    index b80abec0531aad..c5a965c5c8f850 100644
    --- a/fs/erofs/dir.c
    +++ b/fs/erofs/dir.c
    @@ -22,20 +22,18 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		nameoff = le16_to_cpu(de->nameoff);
     		de_name = (char *)dentry_blk + nameoff;
     
    -		/* the last dirent in the block? */
    -		if (de + 1 >= end)
    -			de_namelen = strnlen(de_name, maxsize - nameoff);
    -		else
    +		/* non-trailing dirent in the directory block? */
    +		if (de + 1 < end)
     			de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
    +		else if (maxsize <= nameoff)
    +			goto err_bogus;
    +		else
    +			de_namelen = strnlen(de_name, maxsize - nameoff);
     
    -		/* a corrupted entry is found */
    -		if (nameoff + de_namelen > maxsize ||
    -		    de_namelen > EROFS_NAME_LEN) {
    -			erofs_err(dir->i_sb, "bogus dirent @ nid %llu",
    -				  EROFS_I(dir)->nid);
    -			DBG_BUGON(1);
    -			return -EFSCORRUPTED;
    -		}
    +		/* a corrupted entry is found (including negative namelen) */
    +		if (!in_range32(de_namelen, 1, EROFS_NAME_LEN) ||
    +		    nameoff + de_namelen > maxsize)
    +			goto err_bogus;
     
     		if (!dir_emit(ctx, de_name, de_namelen,
     			      le64_to_cpu(de->nid), d_type))
    @@ -44,6 +42,10 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		ctx->pos += sizeof(struct erofs_dirent);
     	}
     	return 0;
    +err_bogus:
    +	erofs_err(dir->i_sb, "bogus dirent @ nid %llu", EROFS_I(dir)->nid);
    +	DBG_BUGON(1);
    +	return -EFSCORRUPTED;
     }
     
     static int erofs_readdir(struct file *f, struct dir_context *ctx)
    @@ -72,7 +74,7 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx)
     		}
     
     		nameoff = le16_to_cpu(de->nameoff);
    -		if (nameoff < sizeof(struct erofs_dirent) || nameoff >= bsz) {
    +		if (!nameoff || nameoff >= bsz || (nameoff % sizeof(*de))) {
     			erofs_err(sb, "invalid de[0].nameoff %u @ nid %llu",
     				  nameoff, EROFS_I(dir)->nid);
     			err = -EFSCORRUPTED;
    -- 
    cgit 1.3-korg
    
    
    
48b27a955d22

erofs: fix the out-of-bounds nameoff handling for trailing dirents

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitGao XiangApr 21, 2026Fixed in 6.12.86via kernel-cna
1 file changed · +15 14
  • fs/erofs/dir.c+15 14 modified
    diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c
    index c3b90abdee37af..19efbff53048b8 100644
    --- a/fs/erofs/dir.c
    +++ b/fs/erofs/dir.c
    @@ -18,20 +18,18 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		const char *de_name = (char *)dentry_blk + nameoff;
     		unsigned int de_namelen;
     
    -		/* the last dirent in the block? */
    -		if (de + 1 >= end)
    -			de_namelen = strnlen(de_name, maxsize - nameoff);
    -		else
    +		/* non-trailing dirent in the directory block? */
    +		if (de + 1 < end)
     			de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
    +		else if (maxsize <= nameoff)
    +			goto err_bogus;
    +		else
    +			de_namelen = strnlen(de_name, maxsize - nameoff);
     
    -		/* a corrupted entry is found */
    -		if (nameoff + de_namelen > maxsize ||
    -		    de_namelen > EROFS_NAME_LEN) {
    -			erofs_err(dir->i_sb, "bogus dirent @ nid %llu",
    -				  EROFS_I(dir)->nid);
    -			DBG_BUGON(1);
    -			return -EFSCORRUPTED;
    -		}
    +		/* a corrupted entry is found (including negative namelen) */
    +		if (!in_range32(de_namelen, 1, EROFS_NAME_LEN) ||
    +		    nameoff + de_namelen > maxsize)
    +			goto err_bogus;
     
     		if (!dir_emit(ctx, de_name, de_namelen,
     			      le64_to_cpu(de->nid), d_type))
    @@ -40,6 +38,10 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		ctx->pos += sizeof(struct erofs_dirent);
     	}
     	return 0;
    +err_bogus:
    +	erofs_err(dir->i_sb, "bogus dirent @ nid %llu", EROFS_I(dir)->nid);
    +	DBG_BUGON(1);
    +	return -EFSCORRUPTED;
     }
     
     static int erofs_readdir(struct file *f, struct dir_context *ctx)
    @@ -67,7 +69,7 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx)
     		}
     
     		nameoff = le16_to_cpu(de->nameoff);
    -		if (nameoff < sizeof(struct erofs_dirent) || nameoff >= bsz) {
    +		if (!nameoff || nameoff >= bsz || (nameoff % sizeof(*de))) {
     			erofs_err(sb, "invalid de[0].nameoff %u @ nid %llu",
     				  nameoff, EROFS_I(dir)->nid);
     			err = -EFSCORRUPTED;
    -- 
    cgit 1.3-korg
    
    
    
8ebb951a284b

erofs: fix the out-of-bounds nameoff handling for trailing dirents

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitGao XiangApr 21, 2026Fixed in 6.18.27via kernel-cna
1 file changed · +15 14
  • fs/erofs/dir.c+15 14 modified
    diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c
    index 32b4f5aa60c986..2b388775be1677 100644
    --- a/fs/erofs/dir.c
    +++ b/fs/erofs/dir.c
    @@ -18,20 +18,18 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		const char *de_name = (char *)dentry_blk + nameoff;
     		unsigned int de_namelen;
     
    -		/* the last dirent in the block? */
    -		if (de + 1 >= end)
    -			de_namelen = strnlen(de_name, maxsize - nameoff);
    -		else
    +		/* non-trailing dirent in the directory block? */
    +		if (de + 1 < end)
     			de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
    +		else if (maxsize <= nameoff)
    +			goto err_bogus;
    +		else
    +			de_namelen = strnlen(de_name, maxsize - nameoff);
     
    -		/* a corrupted entry is found */
    -		if (nameoff + de_namelen > maxsize ||
    -		    de_namelen > EROFS_NAME_LEN) {
    -			erofs_err(dir->i_sb, "bogus dirent @ nid %llu",
    -				  EROFS_I(dir)->nid);
    -			DBG_BUGON(1);
    -			return -EFSCORRUPTED;
    -		}
    +		/* a corrupted entry is found (including negative namelen) */
    +		if (!in_range32(de_namelen, 1, EROFS_NAME_LEN) ||
    +		    nameoff + de_namelen > maxsize)
    +			goto err_bogus;
     
     		if (!dir_emit(ctx, de_name, de_namelen,
     			      erofs_nid_to_ino64(EROFS_SB(dir->i_sb),
    @@ -41,6 +39,10 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		ctx->pos += sizeof(struct erofs_dirent);
     	}
     	return 0;
    +err_bogus:
    +	erofs_err(dir->i_sb, "bogus dirent @ nid %llu", EROFS_I(dir)->nid);
    +	DBG_BUGON(1);
    +	return -EFSCORRUPTED;
     }
     
     static int erofs_readdir(struct file *f, struct dir_context *ctx)
    @@ -87,7 +89,7 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx)
     		}
     
     		nameoff = le16_to_cpu(de->nameoff);
    -		if (nameoff < sizeof(struct erofs_dirent) || nameoff >= bsz) {
    +		if (!nameoff || nameoff >= bsz || (nameoff % sizeof(*de))) {
     			erofs_err(sb, "invalid de[0].nameoff %u @ nid %llu",
     				  nameoff, EROFS_I(dir)->nid);
     			err = -EFSCORRUPTED;
    -- 
    cgit 1.3-korg
    
    
    
1d55445226c7

erofs: fix the out-of-bounds nameoff handling for trailing dirents

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitGao XiangApr 21, 2026Fixed in 7.0.4via kernel-cna
1 file changed · +15 14
  • fs/erofs/dir.c+15 14 modified
    diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c
    index e5132575b9d3ef..4aa52a5f204a0f 100644
    --- a/fs/erofs/dir.c
    +++ b/fs/erofs/dir.c
    @@ -19,20 +19,18 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		const char *de_name = (char *)dentry_blk + nameoff;
     		unsigned int de_namelen;
     
    -		/* the last dirent in the block? */
    -		if (de + 1 >= end)
    -			de_namelen = strnlen(de_name, maxsize - nameoff);
    -		else
    +		/* non-trailing dirent in the directory block? */
    +		if (de + 1 < end)
     			de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
    +		else if (maxsize <= nameoff)
    +			goto err_bogus;
    +		else
    +			de_namelen = strnlen(de_name, maxsize - nameoff);
     
    -		/* a corrupted entry is found */
    -		if (nameoff + de_namelen > maxsize ||
    -		    de_namelen > EROFS_NAME_LEN) {
    -			erofs_err(dir->i_sb, "bogus dirent @ nid %llu",
    -				  EROFS_I(dir)->nid);
    -			DBG_BUGON(1);
    -			return -EFSCORRUPTED;
    -		}
    +		/* a corrupted entry is found (including negative namelen) */
    +		if (!in_range32(de_namelen, 1, EROFS_NAME_LEN) ||
    +		    nameoff + de_namelen > maxsize)
    +			goto err_bogus;
     
     		if (!dir_emit(ctx, de_name, de_namelen,
     			      erofs_nid_to_ino64(EROFS_SB(dir->i_sb),
    @@ -42,6 +40,10 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		ctx->pos += sizeof(struct erofs_dirent);
     	}
     	return 0;
    +err_bogus:
    +	erofs_err(dir->i_sb, "bogus dirent @ nid %llu", EROFS_I(dir)->nid);
    +	DBG_BUGON(1);
    +	return -EFSCORRUPTED;
     }
     
     static int erofs_readdir(struct file *f, struct dir_context *ctx)
    @@ -88,7 +90,7 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx)
     		}
     
     		nameoff = le16_to_cpu(de->nameoff);
    -		if (nameoff < sizeof(struct erofs_dirent) || nameoff >= bsz) {
    +		if (!nameoff || nameoff >= bsz || (nameoff % sizeof(*de))) {
     			erofs_err(sb, "invalid de[0].nameoff %u @ nid %llu",
     				  nameoff, EROFS_I(dir)->nid);
     			err = -EFSCORRUPTED;
    -- 
    cgit 1.3-korg
    
    
    
48b27a955d22

erofs: fix the out-of-bounds nameoff handling for trailing dirents

1 file changed · +15 14
  • fs/erofs/dir.c+15 14 modified
    diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c
    index c3b90abdee37af..19efbff53048b8 100644
    --- a/fs/erofs/dir.c
    +++ b/fs/erofs/dir.c
    @@ -18,20 +18,18 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		const char *de_name = (char *)dentry_blk + nameoff;
     		unsigned int de_namelen;
     
    -		/* the last dirent in the block? */
    -		if (de + 1 >= end)
    -			de_namelen = strnlen(de_name, maxsize - nameoff);
    -		else
    +		/* non-trailing dirent in the directory block? */
    +		if (de + 1 < end)
     			de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
    +		else if (maxsize <= nameoff)
    +			goto err_bogus;
    +		else
    +			de_namelen = strnlen(de_name, maxsize - nameoff);
     
    -		/* a corrupted entry is found */
    -		if (nameoff + de_namelen > maxsize ||
    -		    de_namelen > EROFS_NAME_LEN) {
    -			erofs_err(dir->i_sb, "bogus dirent @ nid %llu",
    -				  EROFS_I(dir)->nid);
    -			DBG_BUGON(1);
    -			return -EFSCORRUPTED;
    -		}
    +		/* a corrupted entry is found (including negative namelen) */
    +		if (!in_range32(de_namelen, 1, EROFS_NAME_LEN) ||
    +		    nameoff + de_namelen > maxsize)
    +			goto err_bogus;
     
     		if (!dir_emit(ctx, de_name, de_namelen,
     			      le64_to_cpu(de->nid), d_type))
    @@ -40,6 +38,10 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		ctx->pos += sizeof(struct erofs_dirent);
     	}
     	return 0;
    +err_bogus:
    +	erofs_err(dir->i_sb, "bogus dirent @ nid %llu", EROFS_I(dir)->nid);
    +	DBG_BUGON(1);
    +	return -EFSCORRUPTED;
     }
     
     static int erofs_readdir(struct file *f, struct dir_context *ctx)
    @@ -67,7 +69,7 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx)
     		}
     
     		nameoff = le16_to_cpu(de->nameoff);
    -		if (nameoff < sizeof(struct erofs_dirent) || nameoff >= bsz) {
    +		if (!nameoff || nameoff >= bsz || (nameoff % sizeof(*de))) {
     			erofs_err(sb, "invalid de[0].nameoff %u @ nid %llu",
     				  nameoff, EROFS_I(dir)->nid);
     			err = -EFSCORRUPTED;
    -- 
    cgit 1.3-korg
    
    
    
222055e6b406

erofs: fix the out-of-bounds nameoff handling for trailing dirents

1 file changed · +15 14
  • fs/erofs/dir.c+15 14 modified
    diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c
    index b80abec0531aad..c5a965c5c8f850 100644
    --- a/fs/erofs/dir.c
    +++ b/fs/erofs/dir.c
    @@ -22,20 +22,18 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		nameoff = le16_to_cpu(de->nameoff);
     		de_name = (char *)dentry_blk + nameoff;
     
    -		/* the last dirent in the block? */
    -		if (de + 1 >= end)
    -			de_namelen = strnlen(de_name, maxsize - nameoff);
    -		else
    +		/* non-trailing dirent in the directory block? */
    +		if (de + 1 < end)
     			de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
    +		else if (maxsize <= nameoff)
    +			goto err_bogus;
    +		else
    +			de_namelen = strnlen(de_name, maxsize - nameoff);
     
    -		/* a corrupted entry is found */
    -		if (nameoff + de_namelen > maxsize ||
    -		    de_namelen > EROFS_NAME_LEN) {
    -			erofs_err(dir->i_sb, "bogus dirent @ nid %llu",
    -				  EROFS_I(dir)->nid);
    -			DBG_BUGON(1);
    -			return -EFSCORRUPTED;
    -		}
    +		/* a corrupted entry is found (including negative namelen) */
    +		if (!in_range32(de_namelen, 1, EROFS_NAME_LEN) ||
    +		    nameoff + de_namelen > maxsize)
    +			goto err_bogus;
     
     		if (!dir_emit(ctx, de_name, de_namelen,
     			      le64_to_cpu(de->nid), d_type))
    @@ -44,6 +42,10 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		ctx->pos += sizeof(struct erofs_dirent);
     	}
     	return 0;
    +err_bogus:
    +	erofs_err(dir->i_sb, "bogus dirent @ nid %llu", EROFS_I(dir)->nid);
    +	DBG_BUGON(1);
    +	return -EFSCORRUPTED;
     }
     
     static int erofs_readdir(struct file *f, struct dir_context *ctx)
    @@ -72,7 +74,7 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx)
     		}
     
     		nameoff = le16_to_cpu(de->nameoff);
    -		if (nameoff < sizeof(struct erofs_dirent) || nameoff >= bsz) {
    +		if (!nameoff || nameoff >= bsz || (nameoff % sizeof(*de))) {
     			erofs_err(sb, "invalid de[0].nameoff %u @ nid %llu",
     				  nameoff, EROFS_I(dir)->nid);
     			err = -EFSCORRUPTED;
    -- 
    cgit 1.3-korg
    
    
    
8ebb951a284b

erofs: fix the out-of-bounds nameoff handling for trailing dirents

1 file changed · +15 14
  • fs/erofs/dir.c+15 14 modified
    diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c
    index 32b4f5aa60c986..2b388775be1677 100644
    --- a/fs/erofs/dir.c
    +++ b/fs/erofs/dir.c
    @@ -18,20 +18,18 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		const char *de_name = (char *)dentry_blk + nameoff;
     		unsigned int de_namelen;
     
    -		/* the last dirent in the block? */
    -		if (de + 1 >= end)
    -			de_namelen = strnlen(de_name, maxsize - nameoff);
    -		else
    +		/* non-trailing dirent in the directory block? */
    +		if (de + 1 < end)
     			de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
    +		else if (maxsize <= nameoff)
    +			goto err_bogus;
    +		else
    +			de_namelen = strnlen(de_name, maxsize - nameoff);
     
    -		/* a corrupted entry is found */
    -		if (nameoff + de_namelen > maxsize ||
    -		    de_namelen > EROFS_NAME_LEN) {
    -			erofs_err(dir->i_sb, "bogus dirent @ nid %llu",
    -				  EROFS_I(dir)->nid);
    -			DBG_BUGON(1);
    -			return -EFSCORRUPTED;
    -		}
    +		/* a corrupted entry is found (including negative namelen) */
    +		if (!in_range32(de_namelen, 1, EROFS_NAME_LEN) ||
    +		    nameoff + de_namelen > maxsize)
    +			goto err_bogus;
     
     		if (!dir_emit(ctx, de_name, de_namelen,
     			      erofs_nid_to_ino64(EROFS_SB(dir->i_sb),
    @@ -41,6 +39,10 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		ctx->pos += sizeof(struct erofs_dirent);
     	}
     	return 0;
    +err_bogus:
    +	erofs_err(dir->i_sb, "bogus dirent @ nid %llu", EROFS_I(dir)->nid);
    +	DBG_BUGON(1);
    +	return -EFSCORRUPTED;
     }
     
     static int erofs_readdir(struct file *f, struct dir_context *ctx)
    @@ -87,7 +89,7 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx)
     		}
     
     		nameoff = le16_to_cpu(de->nameoff);
    -		if (nameoff < sizeof(struct erofs_dirent) || nameoff >= bsz) {
    +		if (!nameoff || nameoff >= bsz || (nameoff % sizeof(*de))) {
     			erofs_err(sb, "invalid de[0].nameoff %u @ nid %llu",
     				  nameoff, EROFS_I(dir)->nid);
     			err = -EFSCORRUPTED;
    -- 
    cgit 1.3-korg
    
    
    
d18a3b5d337f

erofs: fix the out-of-bounds nameoff handling for trailing dirents

1 file changed · +15 14
  • fs/erofs/dir.c+15 14 modified
    diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c
    index e5132575b9d3ef..4aa52a5f204a0f 100644
    --- a/fs/erofs/dir.c
    +++ b/fs/erofs/dir.c
    @@ -19,20 +19,18 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		const char *de_name = (char *)dentry_blk + nameoff;
     		unsigned int de_namelen;
     
    -		/* the last dirent in the block? */
    -		if (de + 1 >= end)
    -			de_namelen = strnlen(de_name, maxsize - nameoff);
    -		else
    +		/* non-trailing dirent in the directory block? */
    +		if (de + 1 < end)
     			de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
    +		else if (maxsize <= nameoff)
    +			goto err_bogus;
    +		else
    +			de_namelen = strnlen(de_name, maxsize - nameoff);
     
    -		/* a corrupted entry is found */
    -		if (nameoff + de_namelen > maxsize ||
    -		    de_namelen > EROFS_NAME_LEN) {
    -			erofs_err(dir->i_sb, "bogus dirent @ nid %llu",
    -				  EROFS_I(dir)->nid);
    -			DBG_BUGON(1);
    -			return -EFSCORRUPTED;
    -		}
    +		/* a corrupted entry is found (including negative namelen) */
    +		if (!in_range32(de_namelen, 1, EROFS_NAME_LEN) ||
    +		    nameoff + de_namelen > maxsize)
    +			goto err_bogus;
     
     		if (!dir_emit(ctx, de_name, de_namelen,
     			      erofs_nid_to_ino64(EROFS_SB(dir->i_sb),
    @@ -42,6 +40,10 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		ctx->pos += sizeof(struct erofs_dirent);
     	}
     	return 0;
    +err_bogus:
    +	erofs_err(dir->i_sb, "bogus dirent @ nid %llu", EROFS_I(dir)->nid);
    +	DBG_BUGON(1);
    +	return -EFSCORRUPTED;
     }
     
     static int erofs_readdir(struct file *f, struct dir_context *ctx)
    @@ -88,7 +90,7 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx)
     		}
     
     		nameoff = le16_to_cpu(de->nameoff);
    -		if (nameoff < sizeof(struct erofs_dirent) || nameoff >= bsz) {
    +		if (!nameoff || nameoff >= bsz || (nameoff % sizeof(*de))) {
     			erofs_err(sb, "invalid de[0].nameoff %u @ nid %llu",
     				  nameoff, EROFS_I(dir)->nid);
     			err = -EFSCORRUPTED;
    -- 
    cgit 1.3-korg
    
    
    
1d55445226c7

erofs: fix the out-of-bounds nameoff handling for trailing dirents

1 file changed · +15 14
  • fs/erofs/dir.c+15 14 modified
    diff --git a/fs/erofs/dir.c b/fs/erofs/dir.c
    index e5132575b9d3ef..4aa52a5f204a0f 100644
    --- a/fs/erofs/dir.c
    +++ b/fs/erofs/dir.c
    @@ -19,20 +19,18 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		const char *de_name = (char *)dentry_blk + nameoff;
     		unsigned int de_namelen;
     
    -		/* the last dirent in the block? */
    -		if (de + 1 >= end)
    -			de_namelen = strnlen(de_name, maxsize - nameoff);
    -		else
    +		/* non-trailing dirent in the directory block? */
    +		if (de + 1 < end)
     			de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
    +		else if (maxsize <= nameoff)
    +			goto err_bogus;
    +		else
    +			de_namelen = strnlen(de_name, maxsize - nameoff);
     
    -		/* a corrupted entry is found */
    -		if (nameoff + de_namelen > maxsize ||
    -		    de_namelen > EROFS_NAME_LEN) {
    -			erofs_err(dir->i_sb, "bogus dirent @ nid %llu",
    -				  EROFS_I(dir)->nid);
    -			DBG_BUGON(1);
    -			return -EFSCORRUPTED;
    -		}
    +		/* a corrupted entry is found (including negative namelen) */
    +		if (!in_range32(de_namelen, 1, EROFS_NAME_LEN) ||
    +		    nameoff + de_namelen > maxsize)
    +			goto err_bogus;
     
     		if (!dir_emit(ctx, de_name, de_namelen,
     			      erofs_nid_to_ino64(EROFS_SB(dir->i_sb),
    @@ -42,6 +40,10 @@ static int erofs_fill_dentries(struct inode *dir, struct dir_context *ctx,
     		ctx->pos += sizeof(struct erofs_dirent);
     	}
     	return 0;
    +err_bogus:
    +	erofs_err(dir->i_sb, "bogus dirent @ nid %llu", EROFS_I(dir)->nid);
    +	DBG_BUGON(1);
    +	return -EFSCORRUPTED;
     }
     
     static int erofs_readdir(struct file *f, struct dir_context *ctx)
    @@ -88,7 +90,7 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx)
     		}
     
     		nameoff = le16_to_cpu(de->nameoff);
    -		if (nameoff < sizeof(struct erofs_dirent) || nameoff >= bsz) {
    +		if (!nameoff || nameoff >= bsz || (nameoff % sizeof(*de))) {
     			erofs_err(sb, "invalid de[0].nameoff %u @ nid %llu",
     				  nameoff, EROFS_I(dir)->nid);
     			err = -EFSCORRUPTED;
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing bounds check on nameoff for the trailing dirent allows maxsize - nameoff to underflow, causing strnlen() to read out-of-bounds memory."

Attack vector

An attacker who can mount a crafted EROFS filesystem image can trigger an out-of-bounds read. When `erofs_fill_dentries()` processes the last dirent in a directory block, it computes the name length via `strnlen(de_name, maxsize - nameoff)`. If a malicious image sets `nameoff >= maxsize`, the subtraction `maxsize - nameoff` underflows (wrapping to a very large `size_t`), causing `strnlen()` to read past the directory block boundary [patch_id=2659890]. Additionally, a `nameoff0` that is not a multiple of `sizeof(struct erofs_dirent)` could bypass the existing lower-bound check (`nameoff < sizeof(struct erofs_dirent)`) and lead to misaligned access [patch_id=2659890].

Affected code

The vulnerability resides in `fs/erofs/dir.c` in the `erofs_fill_dentries()` function, specifically in the handling of the trailing (last) directory entry in a block. The `erofs_readdir()` function's validation of `de[0].nameoff` is also affected. All patches apply the same diff to this file [patch_id=2659890].

What the fix does

The patch adds an explicit check `else if (maxsize <= nameoff) goto err_bogus;` before calling `strnlen()` for the trailing dirent, preventing the underflow [patch_id=2659890]. It also replaces the old post-computation bounds check with a unified `in_range32(de_namelen, 1, EROFS_NAME_LEN)` check that catches negative (wrapped) namelen values, and consolidates error handling into a single `err_bogus` label. In `erofs_readdir()`, the `nameoff0` validation is strengthened from `nameoff < sizeof(struct erofs_dirent)` to `!nameoff || nameoff >= bsz || (nameoff % sizeof(*de))`, ensuring nameoff0 is non-zero, within bounds, and properly aligned [patch_id=2659890].

Preconditions

  • inputAttacker must be able to mount a crafted EROFS filesystem image
  • inputThe crafted image must contain a directory block with a trailing dirent whose nameoff >= maxsize

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.