[SRU][R][PATCH v2 1/1] ntfs3: use page allocation for resident attribute inline data

Viktor Pashaiev w.paszajew at gmail.com
Thu Sep 17 19:24:44 UTC 2026


From: Namjae Jeon <linkinjeon at kernel.org>

BugLink: https://bugs.launchpad.net/bugs/2165844

SRU Justification:

[ Impact ]

Any buffered write() to an ntfs3-mounted volume can trigger a kernel BUG in
the generic iomap write path and panic the kernel:
  kernel BUG at fs/iomap/buffered-io.c:1061!
  RIP: 0010:iomap_write_end+0x...

This crash is reachable from unprivileged userspace with a plain write(2)
syscall (e.g. file copy, rsync, or appending data). Small files with resident
attribute data allocate inline_data using kmemdup() from slab. Because slab
allocations rarely start at the beginning of a physical page (i.e.
offset_in_page(inline_data) > 0), when the file size plus that offset exceeds
PAGE_SIZE (4096 bytes), iomap_inline_data_valid() fails and hits the BUG_ON
assertion, immediately crashing the system.

[ Fix ]

Backport upstream commit 36ee1313199b7f16bf963c6ac0241861585125d9:
"fs/ntfs3: Use page allocation for resident attribute inline data".

Replace kmemdup() with alloc_page(GFP_NOFS | __GFP_ZERO) so that
page_address() is always strictly page-aligned (offset_in_page == 0).
Update the cleanup path to put_page() accordingly.
For Ubuntu 7.0 (resolute), paths are adjusted from fs/ntfs/ (upstream 7.1+)
to fs/ntfs3/ (Ubuntu 7.0 kernel driver location).

[ Test Plan ]

1. Create and mount an NTFS filesystem:
   mkfs.ntfs -Q -F /dev/loopX
   mount -t ntfs3 /dev/loopX /mnt
2. Perform buffered writes on small resident files near the 4KB boundary
   from an unprivileged account.
   Without the fix: triggers immediate kernel BUG_ON at fs/iomap/buffered-io.c:1061.
   With the fix: write completes successfully without panic, and data integrity
   is verified upon unmount/remount.

[ Where problems could occur ]

The change is strictly isolated to resident attribute handling in fs/ntfs3.
Instead of allocating arbitrary slab chunks, a single dedicated page is
allocated and immediately released via put_page() upon completion of the
iomap operation. The memory overhead is negligible (4KB transient per active
resident write), and non-resident attributes or other filesystems are
completely unaffected.

[ Other Info ]

This fix was requested by Benjamin Wheeler in LP: #2165844.
The backport was verified with independent clean builds on linux-source-7.0.0
against both 7.0.0-31-generic and 7.0.0-30-generic headers, and git am
applies cleanly without offsets or warnings.

(backported from commit 36ee1313199b7f16bf963c6ac0241861585125d9)
[Viktor Pashaiev: adjusted file paths from fs/ntfs/ to fs/ntfs3/ for 7.0]
Signed-off-by: Namjae Jeon <linkinjeon at kernel.org>
Signed-off-by: Viktor Pashaiev <w.paszajew at gmail.com>
---
 fs/ntfs3/attrib.c | 10 +++++++---
 fs/ntfs3/inode.c  |  6 +++---
 2 files changed, 10 insertions(+), 6 deletions(-)

--- a/fs/ntfs3/attrib.c
+++ b/fs/ntfs3/attrib.c
@@ -1042,10 +1042,14 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen,
 		*lcn = RESIDENT_LCN;
 		*len = data_size;
 		if (res && data_size) {
-			*res = kmemdup(resident_data(attr_b), data_size,
-				       GFP_KERNEL);
-			if (!*res)
+			struct page *page = alloc_page(GFP_NOFS | __GFP_ZERO);
+			if (page) {
+				memcpy(page_address(page), resident_data(attr_b),
+				       data_size);
+				*res = page;
+			} else {
 				err = -ENOMEM;
+			}
 		}
 		goto out;
 	}
--- a/fs/ntfs3/inode.c
+++ b/fs/ntfs3/inode.c
@@ -845,7 +845,7 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
 
 	if (lcn == RESIDENT_LCN) {
 		if (offset >= clen) {
-			kfree(res);
+			put_page((struct page *)res);
 			if (flags & IOMAP_REPORT) {
 				/* special code for report. */
 				return -ENOENT;
@@ -853,7 +853,8 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
 			return -EFAULT;
 		}
 
-		iomap->private = iomap->inline_data = res;
+		iomap->private = res;
+		iomap->inline_data = page_address((struct page *)res);
 		iomap->type = IOMAP_INLINE;
 		iomap->offset = 0;
 		iomap->length = clen; /* resident size in bytes. */
@@ -965,7 +966,7 @@ static int ntfs_iomap_end(struct inode *inode, loff_t pos, loff_t length,
 
 out:
 	if (iomap->type == IOMAP_INLINE) {
-		kfree(iomap->private);
+		put_page((struct page *)iomap->private);
 		iomap->private = NULL;
 	}
 
-- 
2.43.0



More information about the kernel-team mailing list