[PATCH] mm: fix mapping_seek_hole_data() overflow on last page

From: Zhen Yan

Date: Mon Jun 29 2026 - 23:14:22 EST


A local unprivileged process can create a shmem/tmpfs file with
i_size == LLONG_MAX using memfd_create() and fallocate(). If the last
page is present in the page cache, lseek(SEEK_HOLE) on that page
returns 0x8000000000000000 as a successful offset, which is LLONG_MIN
when stored in loff_t.

The same file has readable data at the last byte, but SEEK_DATA from
that offset returns ENXIO.

The overflow is in mapping_seek_hole_data():

pos = round_up((u64)pos + 1, seek_size);

For the final page below LLONG_MAX, the next page boundary is
0x8000000000000000, which is then used as a signed file offset.

This violates the lseek() ABI for shmem/tmpfs: a successful seek
returns a negative offset, and SEEK_DATA can miss readable data at EOF.
Hole-aware copy, backup, and sparse-file scanners may skip the last
cached byte range.

Keep mapping_seek_hole_data() inside its documented [start, end)
search range by clamping the rounded-up position to end before passing
it to folio_seek_hole_data().

Signed-off-by: Zhen Yan <yanzhen20011121@xxxxxxx>
---
mm/filemap.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/mm/filemap.c b/mm/filemap.c
index 5af62e6abca5..22a1534ec8b3 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -3234,6 +3234,8 @@ loff_t mapping_seek_hole_data(struct address_space *mapping, loff_t start,

seek_size = seek_folio_size(&xas, folio);
pos = round_up((u64)pos + 1, seek_size);
+ if (pos > end)
+ pos = end;
start = folio_seek_hole_data(&xas, mapping, folio, start, pos,
seek_data);
if (start < pos)
--
2.25.1

Sorry, my previous email may have been lost due to network
filtering issues. Resending the revised patch with the
requested changes (tabs preserved, CCs added, signoff added).