[PATCH v4] ntfs: validate error codes from untrusted disk data

From: Hongling Zeng

Date: Wed Jul 01 2026 - 03:43:04 EST


ntfs_lookup_ino_by_name() returns MFT references read directly from
disk, which are untrusted data. The current code extracts error codes
via MREF_ERR() without proper validation, allowing maliciously crafted
NTFS images to trigger kernel panic.

The MFT reference encoding uses bit 47 as an error indicator, but the
lower 32 bits can contain arbitrary values. If a malicious image sets
the error bit with a positive integer (e.g., 1), MREF_ERR() returns
that positive value. Returning ERR_PTR(1) causes VFS to treat it as
a valid dentry pointer since IS_ERR() only recognizes values in
[-MAX_ERRNO, -1] as errors.

This leads to kernel panic when walk_component() → step_into()
dereferences the bogus pointer at:
struct inode *inode = dentry->d_inode;

Fix by strictly validating error codes: only accept negative values
in the valid errno range [-MAX_ERRNO, -1]. Convert all other values
(positive, zero, or out-of-range) to -EIO to indicate disk corruption.

This prevents potential security issues and ensures proper error handling
for corrupted or malicious NTFS filesystems.

Fixes: 1e9ea7e04472d ("Revert \"fs: Remove NTFS classic\"")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Hongling Zeng <zenghongling@xxxxxxxxxx>
Suggested-by: Namjae Jeon <linkinjeon@xxxxxxxxxx>

---
Change in v2:
- Use else branch to replace original error handling, avoiding duplicate
logging and ensuring single consistent error messag
---
Change in v3:
- move the assignment long err = MREF_ERR(mref); to the top of the block
and reuse err instead of using MREF_ERR(mref).
---
Change in v4
- Declaring variables at the top.
---
fs/ntfs/namei.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
index a19626a135bd..3d8b7e557b08 100644
--- a/fs/ntfs/namei.c
+++ b/fs/ntfs/namei.c
@@ -177,6 +177,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
u64 mref;
unsigned long dent_ino;
int uname_len;
+ long err;

ntfs_debug("Looking up %pd in directory inode 0x%llx.",
dent, NTFS_I(dir_ino)->mft_no);
@@ -233,9 +234,18 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
ntfs_debug("Done.");
return d_splice_alias(NULL, dent);
}
- ntfs_error(vol->sb, "ntfs_lookup_ino_by_name() failed with error code %i.",
- -MREF_ERR(mref));
- return ERR_PTR(MREF_ERR(mref));
+
+ err = MREF_ERR(mref);
+
+ if (err < 0 && err >= -MAX_ERRNO) {
+ ntfs_error(vol->sb, "ntfs_lookup_ino_by_name() failed with error code %li.",
+ err);
+ return ERR_PTR(err);
+ }
+ ntfs_error(vol->sb,
+ "ntfs_lookup_ino_by_name() returned invalid error code %li, treating as disk corruption.",
+ err);
+ return ERR_PTR(-EIO);
handle_name:
{
struct mft_record *m;
--
2.25.1