[PATCH] fs/ntfs3: bound asize by SIZEOF_NONRESIDENT in check_attr()

Zhan Xusheng posted 1 patch 3 weeks ago
fs/ntfs3/fslog.c | 3 +++
1 file changed, 3 insertions(+)
[PATCH] fs/ntfs3: bound asize by SIZEOF_NONRESIDENT in check_attr()
Posted by Zhan Xusheng 3 weeks ago
check_attr() validates a file record attribute during log replay.  Its
non-resident branch reads through attr->nres.valid_size, which ends
SIZEOF_NONRESIDENT bytes into the attribute, but only checks that the
attribute fits the record, not that it is long enough to hold those
fields.  check_file_record() admits an attribute starting as late as
record_size - SIZEOF_RESIDENT, so one placed there with non_res set reads
up to 40 bytes past mi->mrec, a kmalloc(record_size) buffer.  The later
"run_off >= asize" rejection is too late: data_size, svcn, evcn and
valid_size have already been read.  Nothing earlier covers it, mi_read()
checks only rec->total, and do_action() gets here for the ordinary
record-modifying operations, so a crafted volume reaches it through
normal replay.

Reject an attribute that leaves fewer than SIZEOF_NONRESIDENT bytes in the
record, as mi_enum_attr() already does outside the log path.  A real
non-resident attribute cannot be smaller than its own header, so nothing
that mounts today is rejected.

Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
Cc: stable@vger.kernel.org
Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com>
---
Reproduced with ASAN in a userspace replica of the layout and of
check_attr()'s bounds: an attribute at record_size - SIZEOF_RESIDENT
gives "READ of size 8 ... located 24 bytes after 1024-byte region",
and is rejected once the guard is added.

I did not craft a volume, so there is no mount-level reproducer and no
KASAN splat from a real replay; the reachability above comes from
reading do_action(), mi_read() and ntfs_fix_post_read().

 fs/ntfs3/fslog.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c
index ed50c1d0c23e..034d72929ac0 100644
--- a/fs/ntfs3/fslog.c
+++ b/fs/ntfs3/fslog.c
@@ -2772,6 +2772,9 @@ static inline bool check_attr(const struct MFT_REC *rec,
 		break;
 
 	case 1:
+		if (asize < SIZEOF_NONRESIDENT)
+			return false;
+
 		dsize = le64_to_cpu(attr->nres.data_size);
 		svcn = le64_to_cpu(attr->nres.svcn);
 		evcn = le64_to_cpu(attr->nres.evcn);

base-commit: a500db7819c50db59e55f1b4fa1c3baa5a2616f3
-- 
2.43.0