Linux filesystem development
 help / color / mirror / Atom feed
* [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
@ 2026-10-26 22:05 Samiullah Khawaja
  2026-10-26 22:05 ` [PATCH 1/2] " Samiullah Khawaja
                   ` (3 more replies)
  0 siblings, 4 replies; 17+ messages in thread
From: Samiullah Khawaja @ 2026-10-26 22:05 UTC (permalink / raw)
  To: Andrew Morton, Hugh Dickins
  Cc: Baolin Wang, David Hildenbrand, Muchun Song, Oscar Salvador,
	Jeff Layton, Chuck Lever, Alexander Aring, Shuah Khan,
	Jason Gunthorpe, Gerd Hoffmann, Vivek Kasireddy, Jeff Xu,
	David Matlack, Pasha Tatashin, linux-mm, linux-api, linux-fsdevel,
	linux-kselftest, linux-kernel, Samiullah Khawaja

This series introduces F_SEAL_PUNCH_HOLE, a new memfd seal that prevents
holes from being punched into a shmem or hugetlbfs memfd while leaving
the file fully writable.

Background
==========

Drivers such as udmabuf and iommufd (via IOMMU_IOAS_MAP_FILE) use
memfd_pin_folios() to pin the folios backing a memfd and hand them to a
device for DMA. Pinning keeps those folios allocated, but it does not
keep them in the file. If userspace later calls
fallocate(FALLOC_FL_PUNCH_HOLE) or madvise(MADV_REMOVE) on the range,
the folios are dropped from the page cache while the device continues
to use them. The pin holder is never notified, and from that point on
the device and the file silently diverge.

The existing seals only partially address this. F_SEAL_SHRINK prevents
pages from being dropped by truncation, which is why udmabuf requires
it. Hole punching, however, is only blocked by F_SEAL_WRITE and
F_SEAL_FUTURE_WRITE, and both of those also forbid writes. That makes
them unusable for memory that must stay writable, such as guest RAM or
DMA buffers, and udmabuf explicitly rejects memfds that carry them.

Proposal
========

F_SEAL_PUNCH_HOLE fills this gap. Once it is set,
fallocate(FALLOC_FL_PUNCH_HOLE) and madvise(MADV_REMOVE) fail with
-EPERM, while reads, writes and resizing are unaffected. Combined with
F_SEAL_SHRINK, it guarantees that userspace cannot remove pages from
the memfd while the memfd remains writable.

Motivation
==========

The immediate user is IOMMU live update. Before iommufd preserves a
memfd-backed mapping across kexec, it must confirm that every pinned
page still belongs to the file. Today this requires walking each mapped
page at preserve time, which is costly for large mappings. With
F_SEAL_PUNCH_HOLE and F_SEAL_SHRINK in place, iommufd can verify the
seals once at IOMMU_IOAS_MAP_FILE time and skip the walk entirely.
udmabuf can benefit in the same way. Neither user is changed in this
series.

The iommufd change that would use the seal is discussed in the IOMMU
live update series:
https://lore.kernel.org/all/arVYa_90j0oN1hnu@google.com/

Testing
=======

Patch 2 extends the memfd selftests to cover the new seal on both shmem
and hugetlbfs. Both "memfd_test" and "memfd_test hugetlbfs" pass in
QEMU.

The hugetlbfs run depends on a separately posted fix. Without it, the
run aborts in the existing SEAL-EXEC test before it reaches the new
tests:
https://lore.kernel.org/all/20261026-fixes_shmem-v1-5-703206d5fa0c@kernel.org/

Samiullah Khawaja (2):
  mm/memfd: add F_SEAL_PUNCH_HOLE
  selftests/memfd: add F_SEAL_PUNCH_HOLE tests

 fs/hugetlbfs/inode.c                       |   3 +-
 include/uapi/linux/fcntl.h                 |   1 +
 mm/memfd.c                                 |   4 +-
 mm/shmem.c                                 |   3 +-
 tools/testing/selftests/memfd/memfd_test.c | 111 ++++++++++++++++++++-
 5 files changed, 118 insertions(+), 4 deletions(-)


base-commit: 7bf01f16a4d05028fbeff4d90bab40df25142a24
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-26 22:05 [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE Samiullah Khawaja
@ 2026-10-26 22:05 ` Samiullah Khawaja
  2026-10-26 22:05 ` [PATCH 2/2] selftests/memfd: add F_SEAL_PUNCH_HOLE tests Samiullah Khawaja
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 17+ messages in thread
From: Samiullah Khawaja @ 2026-10-26 22:05 UTC (permalink / raw)
  To: Andrew Morton, Hugh Dickins
  Cc: Baolin Wang, David Hildenbrand, Muchun Song, Oscar Salvador,
	Jeff Layton, Chuck Lever, Alexander Aring, Shuah Khan,
	Jason Gunthorpe, Gerd Hoffmann, Vivek Kasireddy, Jeff Xu,
	David Matlack, Pasha Tatashin, linux-mm, linux-api, linux-fsdevel,
	linux-kselftest, linux-kernel, Samiullah Khawaja

Users of memfd_pin_folios(), such as udmabuf and iommufd
(IOMMU_IOAS_MAP_FILE), pin the folios backing a memfd range and keep
using them for DMA. A pin keeps a folio alive, but it does not keep the
folio in the file. If userspace later punches a hole with
fallocate(FALLOC_FL_PUNCH_HOLE) or madvise(MADV_REMOVE), the folio is
removed from the page cache while the pin still holds it, and the pin
holder is not told. From then on the device and the file no longer
share memory: data written by the device is not visible through the
file, and data written through the file never reaches the device.

udmabuf already requires F_SEAL_SHRINK so that truncation cannot remove
pages, but there is no seal that prevents punching holes without also
preventing writes. F_SEAL_WRITE and F_SEAL_FUTURE_WRITE do block hole
punching, but they cannot be used for memory that has to stay writable,
and udmabuf rejects memfds that carry them.

Add F_SEAL_PUNCH_HOLE, which makes fallocate(FALLOC_FL_PUNCH_HOLE) fail
with -EPERM on shmem and hugetlbfs memfds. MADV_REMOVE is covered as
well, because it is implemented with vfs_fallocate() and
FALLOC_FL_PUNCH_HOLE.

Signed-off-by: Samiullah Khawaja <skhawaja@google.com>
---
 fs/hugetlbfs/inode.c       | 3 ++-
 include/uapi/linux/fcntl.h | 1 +
 mm/memfd.c                 | 4 +++-
 mm/shmem.c                 | 3 ++-
 4 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index e8a05d3de327..65034b72b98e 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -660,7 +660,8 @@ static long hugetlbfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
 	inode_lock(inode);
 
 	/* protected by i_rwsem */
-	if (info->seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE)) {
+	if (info->seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE |
+			   F_SEAL_PUNCH_HOLE)) {
 		inode_unlock(inode);
 		return -EPERM;
 	}
diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
index e43e3de3e9ee..f9dd929adaf4 100644
--- a/include/uapi/linux/fcntl.h
+++ b/include/uapi/linux/fcntl.h
@@ -50,6 +50,7 @@
 #define F_SEAL_WRITE	0x0008	/* prevent writes */
 #define F_SEAL_FUTURE_WRITE	0x0010  /* prevent future writes while mapped */
 #define F_SEAL_EXEC	0x0020  /* prevent chmod modifying exec bits */
+#define F_SEAL_PUNCH_HOLE	0x0040  /* prevent punching holes */
 /* (1U << 31) is reserved for signed error codes */
 
 /*
diff --git a/mm/memfd.c b/mm/memfd.c
index 0f6fff004f5e..3f0ef72fb301 100644
--- a/mm/memfd.c
+++ b/mm/memfd.c
@@ -227,7 +227,8 @@ static unsigned int *memfd_file_seals_ptr(struct file *file)
 		     F_SEAL_SHRINK | \
 		     F_SEAL_GROW | \
 		     F_SEAL_WRITE | \
-		     F_SEAL_FUTURE_WRITE)
+		     F_SEAL_FUTURE_WRITE | \
+		     F_SEAL_PUNCH_HOLE)
 
 int memfd_add_seals(struct file *file, unsigned int seals)
 {
@@ -253,6 +254,7 @@ int memfd_add_seals(struct file *file, unsigned int seals)
 	 *   SEAL_GROW: Prevent the file from growing
 	 *   SEAL_WRITE: Prevent write access to the file
 	 *   SEAL_EXEC: Prevent modification of the exec bits in the file mode
+	 *   SEAL_PUNCH_HOLE: Prevent punching holes into the file
 	 *
 	 * As we don't require any trust relationship between two parties, we
 	 * must prevent seals from being removed. Therefore, sealing a file
diff --git a/mm/shmem.c b/mm/shmem.c
index fd0953d55fd6..e79a298d6292 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -3853,7 +3853,8 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset,
 		DECLARE_WAIT_QUEUE_HEAD_ONSTACK(shmem_falloc_waitq);
 
 		/* protected by i_rwsem */
-		if (info->seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE)) {
+		if (info->seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE |
+				   F_SEAL_PUNCH_HOLE)) {
 			error = -EPERM;
 			goto out;
 		}
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 2/2] selftests/memfd: add F_SEAL_PUNCH_HOLE tests
  2026-10-26 22:05 [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE Samiullah Khawaja
  2026-10-26 22:05 ` [PATCH 1/2] " Samiullah Khawaja
@ 2026-10-26 22:05 ` Samiullah Khawaja
  2026-10-26 22:46 ` [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE Andy Lutomirski
  2026-10-30 10:20 ` David Hildenbrand (Arm)
  3 siblings, 0 replies; 17+ messages in thread
From: Samiullah Khawaja @ 2026-10-26 22:05 UTC (permalink / raw)
  To: Andrew Morton, Hugh Dickins
  Cc: Baolin Wang, David Hildenbrand, Muchun Song, Oscar Salvador,
	Jeff Layton, Chuck Lever, Alexander Aring, Shuah Khan,
	Jason Gunthorpe, Gerd Hoffmann, Vivek Kasireddy, Jeff Xu,
	David Matlack, Pasha Tatashin, linux-mm, linux-api, linux-fsdevel,
	linux-kselftest, linux-kernel, Samiullah Khawaja

Add tests for the new F_SEAL_PUNCH_HOLE seal. Like the rest of
memfd_test, they run on shmem memfds by default and on hugetlbfs memfds
when memfd_test is invoked with the "hugetlbfs" argument.

mfd_assert_write() checks that the file can be written, and then also
checks that a hole can be punched into it. The second check no longer
holds once F_SEAL_PUNCH_HOLE is set. Move the write checks into
__mfd_assert_write() and the punch check into mfd_assert_punch_hole(),
and have mfd_assert_write() call both, so the existing tests keep doing
exactly what they did before.

Add mfd_fail_punch_hole(), which checks that both
fallocate(FALLOC_FL_PUNCH_HOLE) and madvise(MADV_REMOVE) fail with
EPERM, and use it in two new tests. SEAL-PUNCH-HOLE sets only the new
seal and checks that punching a hole fails, while writing, shrinking and
growing the file still work. SEAL-PUNCH-HOLE-SHRINK adds F_SEAL_SHRINK
as well and checks that the file can neither have holes punched into it
nor be shrunk, while it can still grow.

Signed-off-by: Samiullah Khawaja <skhawaja@google.com>
---
 tools/testing/selftests/memfd/memfd_test.c | 111 ++++++++++++++++++++-
 1 file changed, 110 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c
index cdab3a837624..095c5eb255af 100644
--- a/tools/testing/selftests/memfd/memfd_test.c
+++ b/tools/testing/selftests/memfd/memfd_test.c
@@ -35,6 +35,10 @@
 
 #define F_SEAL_EXEC	0x0020
 
+#ifndef F_SEAL_PUNCH_HOLE
+#define F_SEAL_PUNCH_HOLE	0x0040
+#endif
+
 #define F_WX_SEALS (F_SEAL_SHRINK | \
 		    F_SEAL_GROW | \
 		    F_SEAL_WRITE | \
@@ -455,7 +459,7 @@ static void mfd_assert_fork_private_write(int fd)
 	munmap(p, mfd_def_size);
 }
 
-static void mfd_assert_write(int fd)
+static void __mfd_assert_write(int fd)
 {
 	ssize_t l;
 	void *p;
@@ -523,6 +527,11 @@ static void mfd_assert_write(int fd)
 
 	*(char *)p = 0;
 	munmap(p, mfd_def_size);
+}
+
+static void mfd_assert_punch_hole(int fd)
+{
+	int r;
 
 	/* verify PUNCH_HOLE works */
 	r = fallocate(fd,
@@ -535,6 +544,49 @@ static void mfd_assert_write(int fd)
 	}
 }
 
+static void mfd_fail_punch_hole(int fd)
+{
+	void *p;
+	int r;
+
+	/* verify PUNCH_HOLE fails */
+	r = fallocate(fd,
+		      FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+		      0,
+		      mfd_def_size);
+	if (r >= 0 || errno != EPERM) {
+		printf("expected EPERM on fallocate(PUNCH_HOLE), but got %d: %m\n",
+		       r);
+		abort();
+	}
+
+	/* verify MADV_REMOVE fails, it punches a hole as well */
+	p = mmap(NULL,
+		 mfd_def_size,
+		 PROT_READ | PROT_WRITE,
+		 MAP_SHARED,
+		 fd,
+		 0);
+	if (p == MAP_FAILED) {
+		printf("mmap() failed: %m\n");
+		abort();
+	}
+
+	r = madvise(p, mfd_def_size, MADV_REMOVE);
+	if (r >= 0 || errno != EPERM) {
+		printf("expected EPERM on madvise(MADV_REMOVE), but got %d: %m\n",
+		       r);
+		abort();
+	}
+	munmap(p, mfd_def_size);
+}
+
+static void mfd_assert_write(int fd)
+{
+	__mfd_assert_write(fd);
+	mfd_assert_punch_hole(fd);
+}
+
 static void mfd_fail_write(int fd)
 {
 	ssize_t l;
@@ -1117,6 +1169,61 @@ static void test_seal_resize(void)
 	close(fd);
 }
 
+/*
+ * Test SEAL_PUNCH_HOLE
+ * Test whether SEAL_PUNCH_HOLE actually prevents punching holes, but still
+ * allows writing and resizing
+ */
+static void test_seal_punch_hole(void)
+{
+	int fd;
+
+	printf("%s SEAL-PUNCH-HOLE\n", memfd_str);
+
+	fd = mfd_assert_new("kern_memfd_seal_punch_hole",
+			    mfd_def_size,
+			    MFD_CLOEXEC | MFD_ALLOW_SEALING);
+	mfd_assert_has_seals(fd, 0);
+	mfd_assert_add_seals(fd, F_SEAL_PUNCH_HOLE);
+	mfd_assert_has_seals(fd, F_SEAL_PUNCH_HOLE);
+
+	mfd_assert_read(fd);
+	__mfd_assert_write(fd);
+	mfd_fail_punch_hole(fd);
+	mfd_assert_shrink(fd);
+	mfd_assert_grow(fd);
+	mfd_assert_grow_write(fd);
+
+	close(fd);
+}
+
+/*
+ * Test SEAL_PUNCH_HOLE | SEAL_SHRINK
+ * Test whether SEAL_PUNCH_HOLE | SEAL_SHRINK prevents removing pages from
+ * the file
+ */
+static void test_seal_punch_hole_shrink(void)
+{
+	int fd;
+
+	printf("%s SEAL-PUNCH-HOLE-SHRINK\n", memfd_str);
+
+	fd = mfd_assert_new("kern_memfd_seal_punch_hole_shrink",
+			    mfd_def_size,
+			    MFD_CLOEXEC | MFD_ALLOW_SEALING);
+	mfd_assert_has_seals(fd, 0);
+	mfd_assert_add_seals(fd, F_SEAL_PUNCH_HOLE | F_SEAL_SHRINK);
+	mfd_assert_has_seals(fd, F_SEAL_PUNCH_HOLE | F_SEAL_SHRINK);
+
+	mfd_assert_read(fd);
+	__mfd_assert_write(fd);
+	mfd_fail_punch_hole(fd);
+	mfd_fail_shrink(fd);
+	mfd_assert_grow(fd);
+
+	close(fd);
+}
+
 /*
  * Test SEAL_EXEC
  * Test fd is created with exec and allow sealing.
@@ -1736,6 +1843,8 @@ int main(int argc, char **argv)
 	test_seal_shrink();
 	test_seal_grow();
 	test_seal_resize();
+	test_seal_punch_hole();
+	test_seal_punch_hole_shrink();
 
 	if (pid_ns_supported()) {
 		test_sysctl_simple();
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-26 22:05 [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE Samiullah Khawaja
  2026-10-26 22:05 ` [PATCH 1/2] " Samiullah Khawaja
  2026-10-26 22:05 ` [PATCH 2/2] selftests/memfd: add F_SEAL_PUNCH_HOLE tests Samiullah Khawaja
@ 2026-10-26 22:46 ` Andy Lutomirski
  2026-10-27 22:55   ` Samiullah Khawaja
  2026-10-30 10:20 ` David Hildenbrand (Arm)
  3 siblings, 1 reply; 17+ messages in thread
From: Andy Lutomirski @ 2026-10-26 22:46 UTC (permalink / raw)
  To: Samiullah Khawaja
  Cc: Andrew Morton, Hugh Dickins, Baolin Wang, David Hildenbrand,
	Muchun Song, Oscar Salvador, Jeff Layton, Chuck Lever,
	Alexander Aring, Shuah Khan, Jason Gunthorpe, Gerd Hoffmann,
	Vivek Kasireddy, Jeff Xu, David Matlack, Pasha Tatashin, linux-mm,
	linux-api, linux-fsdevel, linux-kselftest, linux-kernel

On Mon, Oct 26, 2026 at 3:06 PM Samiullah Khawaja <skhawaja@google.com> wrote:
>
> This series introduces F_SEAL_PUNCH_HOLE, a new memfd seal that prevents
> holes from being punched into a shmem or hugetlbfs memfd while leaving
> the file fully writable.
>
> Background
> ==========
>
> Drivers such as udmabuf and iommufd (via IOMMU_IOAS_MAP_FILE) use
> memfd_pin_folios() to pin the folios backing a memfd and hand them to a
> device for DMA. Pinning keeps those folios allocated, but it does not
> keep them in the file. If userspace later calls
> fallocate(FALLOC_FL_PUNCH_HOLE) or madvise(MADV_REMOVE) on the range,
> the folios are dropped from the page cache while the device continues
> to use them. The pin holder is never notified, and from that point on
> the device and the file silently diverge.
>
> The existing seals only partially address this. F_SEAL_SHRINK prevents
> pages from being dropped by truncation, which is why udmabuf requires
> it. Hole punching, however, is only blocked by F_SEAL_WRITE and
> F_SEAL_FUTURE_WRITE, and both of those also forbid writes. That makes
> them unusable for memory that must stay writable, such as guest RAM or
> DMA buffers, and udmabuf explicitly rejects memfds that carry them.
>
> Proposal
> ========
>
> F_SEAL_PUNCH_HOLE fills this gap. Once it is set,
> fallocate(FALLOC_FL_PUNCH_HOLE) and madvise(MADV_REMOVE) fail with
> -EPERM, while reads, writes and resizing are unaffected. Combined with
> F_SEAL_SHRINK, it guarantees that userspace cannot remove pages from
> the memfd while the memfd remains writable.

How does this solve the problem?  F_SEAL is about a userspace
contract, and if there are existing users of the APIs in question, it
makes very little sense to start making those users fail because they
haven't taken the appropriate seal.

Would it make more sense for the drivers in question to set some flag
(refcount?) on the file to block hole punching?  Or perhaps to have
the hole punch code notice that it's trying to punch out a folio that
is pinned or otherwise referenced and to refuse to do it?

To some extent, the issue you're describing is maybe broader than just
the APIs you're contemplating.  If I open a file and then do something
in thread A that GUP's the file and punch a hole in the file in thread
B, I'm doing something weird, and I can imagine plenty of ways it
could go wrong.  (Especially if it's GUP for write.  I hope we already
get that case right.)

-- 
Andy Lutomirski
AMA Capital Management, LLC

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-26 22:46 ` [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE Andy Lutomirski
@ 2026-10-27 22:55   ` Samiullah Khawaja
  2026-10-28  3:57     ` Andy Lutomirski
  0 siblings, 1 reply; 17+ messages in thread
From: Samiullah Khawaja @ 2026-10-27 22:55 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: Andrew Morton, Hugh Dickins, Baolin Wang, David Hildenbrand,
	Muchun Song, Oscar Salvador, Jeff Layton, Chuck Lever,
	Alexander Aring, Shuah Khan, Jason Gunthorpe, Gerd Hoffmann,
	Vivek Kasireddy, Jeff Xu, David Matlack, Pasha Tatashin, linux-mm,
	linux-api, linux-fsdevel, linux-kselftest, linux-kernel

On Mon, Oct 26, 2026 at 03:46:34PM -0700, Andy Lutomirski wrote:
>On Mon, Oct 26, 2026 at 3:06 PM Samiullah Khawaja <skhawaja@google.com> wrote:
>>
>> This series introduces F_SEAL_PUNCH_HOLE, a new memfd seal that prevents
>> holes from being punched into a shmem or hugetlbfs memfd while leaving
>> the file fully writable.
>>
>> Background
>> ==========
>>
>> Drivers such as udmabuf and iommufd (via IOMMU_IOAS_MAP_FILE) use
>> memfd_pin_folios() to pin the folios backing a memfd and hand them to a
>> device for DMA. Pinning keeps those folios allocated, but it does not
>> keep them in the file. If userspace later calls
>> fallocate(FALLOC_FL_PUNCH_HOLE) or madvise(MADV_REMOVE) on the range,
>> the folios are dropped from the page cache while the device continues
>> to use them. The pin holder is never notified, and from that point on
>> the device and the file silently diverge.
>>
>> The existing seals only partially address this. F_SEAL_SHRINK prevents
>> pages from being dropped by truncation, which is why udmabuf requires
>> it. Hole punching, however, is only blocked by F_SEAL_WRITE and
>> F_SEAL_FUTURE_WRITE, and both of those also forbid writes. That makes
>> them unusable for memory that must stay writable, such as guest RAM or
>> DMA buffers, and udmabuf explicitly rejects memfds that carry them.
>>
>> Proposal
>> ========
>>
>> F_SEAL_PUNCH_HOLE fills this gap. Once it is set,
>> fallocate(FALLOC_FL_PUNCH_HOLE) and madvise(MADV_REMOVE) fail with
>> -EPERM, while reads, writes and resizing are unaffected. Combined with
>> F_SEAL_SHRINK, it guarantees that userspace cannot remove pages from
>> the memfd while the memfd remains writable.
>
>How does this solve the problem?  F_SEAL is about a userspace
>contract, and if there are existing users of the APIs in question, it
>makes very little sense to start making those users fail because they
>haven't taken the appropriate seal.

It doesn't change any existing behaviour, nothing starts failing for
users that don't set the seal, and drivers wouldn't require it. It's
opt-in. Userspace that wants the guarantee seals the memfd, and a kernel
user that pins it can check for the seal and rely on the pages staying
in the file. For example, the iommufd live update path could skip
verifying the pinned pages at preserve time when the seal is present,
and do the full check otherwise.

https://lore.kernel.org/all/arVYa_90j0oN1hnu@google.com/
>
>Would it make more sense for the drivers in question to set some flag
>(refcount?) on the file to block hole punching?  Or perhaps to have
>the hole punch code notice that it's trying to punch out a folio that
>is pinned or otherwise referenced and to refuse to do it?

The pins already keep the memory alive while it's used for DMA; the
problem is only that the page silently stops being part of the file.
Refusing to punch pinned folios on every file would change behaviour for
existing users: fallocate() would start failing whenever a page is
pinned, even briefly by O_DIRECT. A flag set on the file by long-term
pinners would avoid the short-term case, but it would still make punches
start failing for existing users of those drivers, which is why I went
for an opt-in seal.

Another option, closer to what you mentioned, but opt-in, is instead of
sealing the whole file against hole punching, a seal that only protects
pinned pages. Punching, shrinking and truncating keep working on a
sealed memfd, except where they would remove a page that is currently
pinned; those would fail with -EBUSY. Files without the seal behave
exactly as today, so existing users are unaffected, and pinned memory is
guaranteed to stay part of the file.
>
>To some extent, the issue you're describing is maybe broader than just
>the APIs you're contemplating.  If I open a file and then do something
>in thread A that GUP's the file and punch a hole in the file in thread
>B, I'm doing something weird, and I can imagine plenty of ways it
>could go wrong.  (Especially if it's GUP for write.  I hope we already
>get that case right.)
>
>-- 
>Andy Lutomirski
>AMA Capital Management, LLC

Thanks,
Sami

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-27 22:55   ` Samiullah Khawaja
@ 2026-10-28  3:57     ` Andy Lutomirski
  2026-10-29 23:16       ` Samiullah Khawaja
  0 siblings, 1 reply; 17+ messages in thread
From: Andy Lutomirski @ 2026-10-28  3:57 UTC (permalink / raw)
  To: Samiullah Khawaja
  Cc: Andrew Morton, Hugh Dickins, Baolin Wang, David Hildenbrand,
	Muchun Song, Oscar Salvador, Jeff Layton, Chuck Lever,
	Alexander Aring, Shuah Khan, Jason Gunthorpe, Gerd Hoffmann,
	Vivek Kasireddy, Jeff Xu, David Matlack, Pasha Tatashin, linux-mm,
	linux-api, linux-fsdevel, linux-kselftest, linux-kernel

On Tue, Oct 27, 2026 at 3:55 PM Samiullah Khawaja <skhawaja@google.com> wrote:
>
> On Mon, Oct 26, 2026 at 03:46:34PM -0700, Andy Lutomirski wrote:
> >On Mon, Oct 26, 2026 at 3:06 PM Samiullah Khawaja <skhawaja@google.com> wrote:
> >>
> >> This series introduces F_SEAL_PUNCH_HOLE, a new memfd seal that prevents
> >> holes from being punched into a shmem or hugetlbfs memfd while leaving
> >> the file fully writable.
> >>
> >> Background
> >> ==========
> >>
> >> Drivers such as udmabuf and iommufd (via IOMMU_IOAS_MAP_FILE) use
> >> memfd_pin_folios() to pin the folios backing a memfd and hand them to a
> >> device for DMA. Pinning keeps those folios allocated, but it does not
> >> keep them in the file. If userspace later calls
> >> fallocate(FALLOC_FL_PUNCH_HOLE) or madvise(MADV_REMOVE) on the range,
> >> the folios are dropped from the page cache while the device continues
> >> to use them. The pin holder is never notified, and from that point on
> >> the device and the file silently diverge.
> >>
> >> The existing seals only partially address this. F_SEAL_SHRINK prevents
> >> pages from being dropped by truncation, which is why udmabuf requires
> >> it. Hole punching, however, is only blocked by F_SEAL_WRITE and
> >> F_SEAL_FUTURE_WRITE, and both of those also forbid writes. That makes
> >> them unusable for memory that must stay writable, such as guest RAM or
> >> DMA buffers, and udmabuf explicitly rejects memfds that carry them.
> >>
> >> Proposal
> >> ========
> >>
> >> F_SEAL_PUNCH_HOLE fills this gap. Once it is set,
> >> fallocate(FALLOC_FL_PUNCH_HOLE) and madvise(MADV_REMOVE) fail with
> >> -EPERM, while reads, writes and resizing are unaffected. Combined with
> >> F_SEAL_SHRINK, it guarantees that userspace cannot remove pages from
> >> the memfd while the memfd remains writable.
> >
> >How does this solve the problem?  F_SEAL is about a userspace
> >contract, and if there are existing users of the APIs in question, it
> >makes very little sense to start making those users fail because they
> >haven't taken the appropriate seal.
>
> It doesn't change any existing behaviour, nothing starts failing for
> users that don't set the seal, and drivers wouldn't require it. It's
> opt-in. Userspace that wants the guarantee seals the memfd, and a kernel
> user that pins it can check for the seal and rely on the pages staying
> in the file. For example, the iommufd live update path could skip
> verifying the pinned pages at preserve time when the seal is present,
> and do the full check otherwise.

Can you clarify the API you're proposing and the issue?  It seems to
me like the user is expected to preserve HWPT state by creating a
memfd, then using some new API to tell the driver to "persist" its
state into the memfd (possibly by binding its existing state as folios
into the memfd)?  And userspace is supposed to seal the memfd against
growing and shrinking but not against writes?

What if the user writes to the file?  What if the user populates
pages/folios into the file before using it to preserve HWPT?  What if
the user mmaps the file?

Have I understood this correctly at all?  Why does memfd make sense?
If the goal is to attach a bunch of pre-existing pages to a file so
that the page preservation machinery can preserve them, wouldn't it
make more sense to have a handoverfd mechanism where the kernel can
make a handoverfd that stores its purpose and logically represents
some pages but does *not* allow read or write or fallocate or any
other user operation that actually acts on the data or its memory
layout?

>
> https://lore.kernel.org/all/arVYa_90j0oN1hnu@google.com/
> >
> >Would it make more sense for the drivers in question to set some flag
> >(refcount?) on the file to block hole punching?  Or perhaps to have
> >the hole punch code notice that it's trying to punch out a folio that
> >is pinned or otherwise referenced and to refuse to do it?
>
> The pins already keep the memory alive while it's used for DMA; the
> problem is only that the page silently stops being part of the file.
> Refusing to punch pinned folios on every file would change behaviour for
> existing users: fallocate() would start failing whenever a page is
> pinned, even briefly by O_DIRECT. A flag set on the file by long-term
> pinners would avoid the short-term case, but it would still make punches
> start failing for existing users of those drivers, which is why I went
> for an opt-in seal.

Wait a moment here.  Given my comments above, I'm not sure this is
relevant to this patchset, but...

As far as I know, right now, a program can open a file, register some
of its pages with io_uring or RDMA, and then use io_uring or RDMA to
write to those pages later.

And another program can open that same file and use fallocate to punch a hole.

And then the first program thinks it's reading into the file, and it
ends up not reading into the file.  Which makes the hole punch
operation rather different from the write-zeros operation that it's
supposed to be a fancy version of.

Now this doesn't sound like a security problem per se, since the
offending hole-puncher needed write access (admittedly that write
access is only needed at hole punch time, not in the indefinite future
when weird behavior happens).  But this whole sequence seems highly
suspect to me.

Are you *sure* it wouldn't make sense for a GUP'd or similar reference
(whether pinned or not) to *actually preserve that folio's status as
part of the file*?  Or maybe for there to be an extra pin bit to do
this?  Because the current behavior is pretty strange.  And I'm not at
all convinced that anyone would really object if the kernel prevents
hole-punching a range with an active GUP, especially if this behavior
is restricted to writable GUPs.

I admit that truncate seems slightly different in the sense that it
seems more likely that someone would be annoyed by an inability to
truncate a file.  But still, truncate does have the same issue.  Maybe
extending a file could notice that there is still a folio covering the
newly extended range and stick that back into the file?  Seems messy.


-- 
Andy Lutomirski
AMA Capital Management, LLC

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-28  3:57     ` Andy Lutomirski
@ 2026-10-29 23:16       ` Samiullah Khawaja
  2026-10-30  0:26         ` Andy Lutomirski
  0 siblings, 1 reply; 17+ messages in thread
From: Samiullah Khawaja @ 2026-10-29 23:16 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: Andrew Morton, Hugh Dickins, Baolin Wang, David Hildenbrand,
	Muchun Song, Oscar Salvador, Jeff Layton, Chuck Lever,
	Alexander Aring, Shuah Khan, Jason Gunthorpe, Gerd Hoffmann,
	Vivek Kasireddy, Jeff Xu, David Matlack, Pasha Tatashin, linux-mm,
	linux-api, linux-fsdevel, linux-kselftest, linux-kernel

On Tue, Oct 27, 2026 at 08:57:29PM -0700, Andy Lutomirski wrote:
>On Tue, Oct 27, 2026 at 3:55 PM Samiullah Khawaja <skhawaja@google.com> wrote:
>>
>> On Mon, Oct 26, 2026 at 03:46:34PM -0700, Andy Lutomirski wrote:
>> >On Mon, Oct 26, 2026 at 3:06 PM Samiullah Khawaja <skhawaja@google.com> wrote:
>> >>
>> >> This series introduces F_SEAL_PUNCH_HOLE, a new memfd seal that prevents
>> >> holes from being punched into a shmem or hugetlbfs memfd while leaving
>> >> the file fully writable.
>> >>
>> >> Background
>> >> ==========
>> >>
>> >> Drivers such as udmabuf and iommufd (via IOMMU_IOAS_MAP_FILE) use
>> >> memfd_pin_folios() to pin the folios backing a memfd and hand them to a
>> >> device for DMA. Pinning keeps those folios allocated, but it does not
>> >> keep them in the file. If userspace later calls
>> >> fallocate(FALLOC_FL_PUNCH_HOLE) or madvise(MADV_REMOVE) on the range,
>> >> the folios are dropped from the page cache while the device continues
>> >> to use them. The pin holder is never notified, and from that point on
>> >> the device and the file silently diverge.
>> >>
>> >> The existing seals only partially address this. F_SEAL_SHRINK prevents
>> >> pages from being dropped by truncation, which is why udmabuf requires
>> >> it. Hole punching, however, is only blocked by F_SEAL_WRITE and
>> >> F_SEAL_FUTURE_WRITE, and both of those also forbid writes. That makes
>> >> them unusable for memory that must stay writable, such as guest RAM or
>> >> DMA buffers, and udmabuf explicitly rejects memfds that carry them.
>> >>
>> >> Proposal
>> >> ========
>> >>
>> >> F_SEAL_PUNCH_HOLE fills this gap. Once it is set,
>> >> fallocate(FALLOC_FL_PUNCH_HOLE) and madvise(MADV_REMOVE) fail with
>> >> -EPERM, while reads, writes and resizing are unaffected. Combined with
>> >> F_SEAL_SHRINK, it guarantees that userspace cannot remove pages from
>> >> the memfd while the memfd remains writable.
>> >
>> >How does this solve the problem?  F_SEAL is about a userspace
>> >contract, and if there are existing users of the APIs in question, it
>> >makes very little sense to start making those users fail because they
>> >haven't taken the appropriate seal.
>>
>> It doesn't change any existing behaviour, nothing starts failing for
>> users that don't set the seal, and drivers wouldn't require it. It's
>> opt-in. Userspace that wants the guarantee seals the memfd, and a
>> kernel user that pins it can check for the seal and rely on the pages
>> staying in the file. For example, the iommufd live update path could
>> skip verifying the pinned pages at preserve time when the seal is
>> present,
>> and do the full check otherwise.
>
>Can you clarify the API you're proposing and the issue?  It seems to
>me like the user is expected to preserve HWPT state by creating a
>memfd, then using some new API to tell the driver to "persist" its
>state into the memfd (possibly by binding its existing state as folios
>into the memfd)?  And userspace is supposed to seal the memfd against
>growing and shrinking but not against writes?
>
>What if the user writes to the file?  What if the user populates
>pages/folios into the file before using it to preserve HWPT?  What if
>the user mmaps the file?
>
>Have I understood this correctly at all?  Why does memfd make sense?
>If the goal is to attach a bunch of pre-existing pages to a file so
>that the page preservation machinery can preserve them, wouldn't it
>make more sense to have a handoverfd mechanism where the kernel can
>make a handoverfd that stores its purpose and logically represents
>some pages but does *not* allow read or write or fallocate or any
>other user operation that actually acts on the data or its memory
>layout?
>

Not quite. Let me elaborate my use case.

For live update we need to preserve HWPT (iommu domain) so the devices
can continue to do DMA during live update. Domains are preserved by
preserving page tables and relevant data structures using KHO.

To make sure that the devices can only DMA to preserved memory, the user
preserves the memfds (into KHO) that are mapped in the preserved hwpts.
During iommufd preservation we need to check that the memory backing the
iommu mappings is preserved. We do this by checking if the mapped memfd
is preserved, but if the memfd had a hole punched into it before its
preservation then the punched pages will not be preserved. For this we
need to do a complete page table walk and test each page separately.

A punch hole seal, before the memfd preservation, allows iommufd to
optimize the validation path and avoid doing full page walks.

The HWPTs are not preserved into memfd; the memfds are mapped into HWPT
and we need to make sure that the mapped memfd has not mutated and the
pages mapped from it are preserved. Also it is ok if the user writes to
the file or mmaps it.

>>
>> https://lore.kernel.org/all/arVYa_90j0oN1hnu@google.com/
>> >
>> >Would it make more sense for the drivers in question to set some flag
>> >(refcount?) on the file to block hole punching?  Or perhaps to have
>> >the hole punch code notice that it's trying to punch out a folio that
>> >is pinned or otherwise referenced and to refuse to do it?
>>
>> The pins already keep the memory alive while it's used for DMA; the
>> problem is only that the page silently stops being part of the file.
>> Refusing to punch pinned folios on every file would change behaviour for
>> existing users: fallocate() would start failing whenever a page is
>> pinned, even briefly by O_DIRECT. A flag set on the file by long-term
>> pinners would avoid the short-term case, but it would still make punches
>> start failing for existing users of those drivers, which is why I went
>> for an opt-in seal.
>
>Wait a moment here.  Given my comments above, I'm not sure this is
>relevant to this patchset, but...
>
>As far as I know, right now, a program can open a file, register some
>of its pages with io_uring or RDMA, and then use io_uring or RDMA to
>write to those pages later.
>
>And another program can open that same file and use fallocate to punch a hole.
>
>And then the first program thinks it's reading into the file, and it
>ends up not reading into the file.  Which makes the hole punch
>operation rather different from the write-zeros operation that it's
>supposed to be a fancy version of.
>
>Now this doesn't sound like a security problem per se, since the
>offending hole-puncher needed write access (admittedly that write
>access is only needed at hole punch time, not in the indefinite future
>when weird behavior happens).  But this whole sequence seems highly
>suspect to me.
>
>Are you *sure* it wouldn't make sense for a GUP'd or similar reference
>(whether pinned or not) to *actually preserve that folio's status as
>part of the file*?  Or maybe for there to be an extra pin bit to do
>this?  Because the current behavior is pretty strange.  And I'm not at
>all convinced that anyone would really object if the kernel prevents
>hole-punching a range with an active GUP, especially if this behavior
>is restricted to writable GUPs.
>
>I admit that truncate seems slightly different in the sense that it
>seems more likely that someone would be annoyed by an inability to
>truncate a file.  But still, truncate does have the same issue.  Maybe
>extending a file could notice that there is still a folio covering the
>newly extended range and stick that back into the file?  Seems messy.

For live update either approach works. If pinned pages cannot be punched
or truncated, iommufd can rely on that directly and the seal is not
needed. I went with an opt-in seal only to avoid changing existing
behaviour. If folks are fine with blocking punch hole and truncate on
pinned pages, I can send patches for that instead.

>
>
>-- 
>Andy Lutomirski
>AMA Capital Management, LLC

Thanks,
Sami

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-29 23:16       ` Samiullah Khawaja
@ 2026-10-30  0:26         ` Andy Lutomirski
  2026-10-30  2:11           ` Andy Lutomirski
  0 siblings, 1 reply; 17+ messages in thread
From: Andy Lutomirski @ 2026-10-30  0:26 UTC (permalink / raw)
  To: Samiullah Khawaja
  Cc: Andrew Morton, Hugh Dickins, Baolin Wang, David Hildenbrand,
	Muchun Song, Oscar Salvador, Jeff Layton, Chuck Lever,
	Alexander Aring, Shuah Khan, Jason Gunthorpe, Gerd Hoffmann,
	Vivek Kasireddy, Jeff Xu, David Matlack, Pasha Tatashin, linux-mm,
	linux-api, linux-fsdevel, linux-kselftest, linux-kernel

On Thu, Oct 29, 2026 at 4:16 PM Samiullah Khawaja <skhawaja@google.com> wrote:
>
> On Tue, Oct 27, 2026 at 08:57:29PM -0700, Andy Lutomirski wrote:
> >On Tue, Oct 27, 2026 at 3:55 PM Samiullah Khawaja <skhawaja@google.com> wrote:
> >>
> >> On Mon, Oct 26, 2026 at 03:46:34PM -0700, Andy Lutomirski wrote:
> >> >On Mon, Oct 26, 2026 at 3:06 PM Samiullah Khawaja <skhawaja@google.com> wrote:
> >> >>
> >> >> This series introduces F_SEAL_PUNCH_HOLE, a new memfd seal that prevents
> >> >> holes from being punched into a shmem or hugetlbfs memfd while leaving
> >> >> the file fully writable.
> >> >>
> >> >> Background
> >> >> ==========
> >> >>
> >> >> Drivers such as udmabuf and iommufd (via IOMMU_IOAS_MAP_FILE) use
> >> >> memfd_pin_folios() to pin the folios backing a memfd and hand them to a
> >> >> device for DMA. Pinning keeps those folios allocated, but it does not
> >> >> keep them in the file. If userspace later calls
> >> >> fallocate(FALLOC_FL_PUNCH_HOLE) or madvise(MADV_REMOVE) on the range,
> >> >> the folios are dropped from the page cache while the device continues
> >> >> to use them. The pin holder is never notified, and from that point on
> >> >> the device and the file silently diverge.
> >> >>
> >> >> The existing seals only partially address this. F_SEAL_SHRINK prevents
> >> >> pages from being dropped by truncation, which is why udmabuf requires
> >> >> it. Hole punching, however, is only blocked by F_SEAL_WRITE and
> >> >> F_SEAL_FUTURE_WRITE, and both of those also forbid writes. That makes
> >> >> them unusable for memory that must stay writable, such as guest RAM or
> >> >> DMA buffers, and udmabuf explicitly rejects memfds that carry them.
> >> >>
> >> >> Proposal
> >> >> ========
> >> >>
> >> >> F_SEAL_PUNCH_HOLE fills this gap. Once it is set,
> >> >> fallocate(FALLOC_FL_PUNCH_HOLE) and madvise(MADV_REMOVE) fail with
> >> >> -EPERM, while reads, writes and resizing are unaffected. Combined with
> >> >> F_SEAL_SHRINK, it guarantees that userspace cannot remove pages from
> >> >> the memfd while the memfd remains writable.
> >> >
> >> >How does this solve the problem?  F_SEAL is about a userspace
> >> >contract, and if there are existing users of the APIs in question, it
> >> >makes very little sense to start making those users fail because they
> >> >haven't taken the appropriate seal.
> >>
> >> It doesn't change any existing behaviour, nothing starts failing for
> >> users that don't set the seal, and drivers wouldn't require it. It's
> >> opt-in. Userspace that wants the guarantee seals the memfd, and a
> >> kernel user that pins it can check for the seal and rely on the pages
> >> staying in the file. For example, the iommufd live update path could
> >> skip verifying the pinned pages at preserve time when the seal is
> >> present,
> >> and do the full check otherwise.
> >
> >Can you clarify the API you're proposing and the issue?  It seems to
> >me like the user is expected to preserve HWPT state by creating a
> >memfd, then using some new API to tell the driver to "persist" its
> >state into the memfd (possibly by binding its existing state as folios
> >into the memfd)?  And userspace is supposed to seal the memfd against
> >growing and shrinking but not against writes?
> >
> >What if the user writes to the file?  What if the user populates
> >pages/folios into the file before using it to preserve HWPT?  What if
> >the user mmaps the file?
> >
> >Have I understood this correctly at all?  Why does memfd make sense?
> >If the goal is to attach a bunch of pre-existing pages to a file so
> >that the page preservation machinery can preserve them, wouldn't it
> >make more sense to have a handoverfd mechanism where the kernel can
> >make a handoverfd that stores its purpose and logically represents
> >some pages but does *not* allow read or write or fallocate or any
> >other user operation that actually acts on the data or its memory
> >layout?
> >
>
> Not quite. Let me elaborate my use case.
>
> For live update we need to preserve HWPT (iommu domain) so the devices
> can continue to do DMA during live update. Domains are preserved by
> preserving page tables and relevant data structures using KHO.
>
> To make sure that the devices can only DMA to preserved memory, the user
> preserves the memfds (into KHO) that are mapped in the preserved hwpts.
> During iommufd preservation we need to check that the memory backing the
> iommu mappings is preserved. We do this by checking if the mapped memfd
> is preserved, but if the memfd had a hole punched into it before its
> preservation then the punched pages will not be preserved. For this we
> need to do a complete page table walk and test each page separately.

Let me see if I understand.  If I'm following, the user makes a memfd
and does something that maps that into HWPT so that devices can DMA
to/from it.  And this is presumably something that works independently
of KHO, and it's preexisting, which is why I had a hard time finding
it in your patch set :)

And the hole punch issue is already present today and will cause
incoherence vs the user's view of the memfd but not actual corruption,
right?  So maybe that should be fixed but maybe it's okay?  (I still
think it's nasty that hole punch while pages/folios are GUPped or
otherwise gotten or pinned causes incoherence.)

But during KHO the system will ensure that the memfd is preserved but
doesn't preserve these detached folios?  If so, maybe the actual issue
is that either it should be impossible for folios like that to exist
(i.e. blocking the hole punch) or perhaps KHO should also preserve
these detached folios?

Certainly the *clean* solution seems to be to disallow punching a
folio out of a file if someone else has a reference to that folio, but
I can definitely see how that's a real can of worms.

>
> A punch hole seal, before the memfd preservation, allows iommufd to
> optimize the validation path and avoid doing full page walks.
>
> The HWPTs are not preserved into memfd; the memfds are mapped into HWPT
> and we need to make sure that the mapped memfd has not mutated and the
> pages mapped from it are preserved. Also it is ok if the user writes to
> the file or mmaps it.
>
> >>
> >> https://lore.kernel.org/all/arVYa_90j0oN1hnu@google.com/
> >> >
> >> >Would it make more sense for the drivers in question to set some flag
> >> >(refcount?) on the file to block hole punching?  Or perhaps to have
> >> >the hole punch code notice that it's trying to punch out a folio that
> >> >is pinned or otherwise referenced and to refuse to do it?
> >>
> >> The pins already keep the memory alive while it's used for DMA; the
> >> problem is only that the page silently stops being part of the file.
> >> Refusing to punch pinned folios on every file would change behaviour for
> >> existing users: fallocate() would start failing whenever a page is
> >> pinned, even briefly by O_DIRECT. A flag set on the file by long-term
> >> pinners would avoid the short-term case, but it would still make punches
> >> start failing for existing users of those drivers, which is why I went
> >> for an opt-in seal.
> >
> >Wait a moment here.  Given my comments above, I'm not sure this is
> >relevant to this patchset, but...
> >
> >As far as I know, right now, a program can open a file, register some
> >of its pages with io_uring or RDMA, and then use io_uring or RDMA to
> >write to those pages later.
> >
> >And another program can open that same file and use fallocate to punch a hole.
> >
> >And then the first program thinks it's reading into the file, and it
> >ends up not reading into the file.  Which makes the hole punch
> >operation rather different from the write-zeros operation that it's
> >supposed to be a fancy version of.
> >
> >Now this doesn't sound like a security problem per se, since the
> >offending hole-puncher needed write access (admittedly that write
> >access is only needed at hole punch time, not in the indefinite future
> >when weird behavior happens).  But this whole sequence seems highly
> >suspect to me.
> >
> >Are you *sure* it wouldn't make sense for a GUP'd or similar reference
> >(whether pinned or not) to *actually preserve that folio's status as
> >part of the file*?  Or maybe for there to be an extra pin bit to do
> >this?  Because the current behavior is pretty strange.  And I'm not at
> >all convinced that anyone would really object if the kernel prevents
> >hole-punching a range with an active GUP, especially if this behavior
> >is restricted to writable GUPs.
> >
> >I admit that truncate seems slightly different in the sense that it
> >seems more likely that someone would be annoyed by an inability to
> >truncate a file.  But still, truncate does have the same issue.  Maybe
> >extending a file could notice that there is still a folio covering the
> >newly extended range and stick that back into the file?  Seems messy.
>
> For live update either approach works. If pinned pages cannot be punched
> or truncated, iommufd can rely on that directly and the seal is not
> needed. I went with an opt-in seal only to avoid changing existing
> behaviour. If folks are fine with blocking punch hole and truncate on
> pinned pages, I can send patches for that instead.

That needs opinions from other people :)

My two cents are that blocking hole punch (or making hole punch fall
back to writing zeros) seems entirely sensible, but I'm less confident
I know what to do about truncate.

--Andy



-- 
Andy Lutomirski
AMA Capital Management, LLC

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-30  0:26         ` Andy Lutomirski
@ 2026-10-30  2:11           ` Andy Lutomirski
  2026-10-30 12:12             ` Jason Gunthorpe
  0 siblings, 1 reply; 17+ messages in thread
From: Andy Lutomirski @ 2026-10-30  2:11 UTC (permalink / raw)
  To: Samiullah Khawaja
  Cc: Andrew Morton, Hugh Dickins, Baolin Wang, David Hildenbrand,
	Muchun Song, Oscar Salvador, Jeff Layton, Chuck Lever,
	Alexander Aring, Shuah Khan, Jason Gunthorpe, Gerd Hoffmann,
	Vivek Kasireddy, Jeff Xu, David Matlack, Pasha Tatashin, linux-mm,
	linux-api, linux-fsdevel, linux-kselftest, linux-kernel

On Thu, Oct 29, 2026 at 5:26 PM Andy Lutomirski <luto@amacapital.net> wrote:
>
> On Thu, Oct 29, 2026 at 4:16 PM Samiullah Khawaja <skhawaja@google.com> wrote:
> >
> > For live update either approach works. If pinned pages cannot be punched
> > or truncated, iommufd can rely on that directly and the seal is not
> > needed. I went with an opt-in seal only to avoid changing existing
> > behaviour. If folks are fine with blocking punch hole and truncate on
> > pinned pages, I can send patches for that instead.
>
> That needs opinions from other people :)
>
> My two cents are that blocking hole punch (or making hole punch fall
> back to writing zeros) seems entirely sensible, but I'm less confident
> I know what to do about truncate.
>

FWIW, I think a demonstration of weird kernel behavior is clearer than
musing based on first principles, so I had a friendly coding agent
write the lovely code at the end of this email. It prints:

Creating and mmapping memfd and registering it
memcpy "Hello, world!" to memfd
MADV_REMOVE the contents
The memfd now contains NULs!
memcpy "memcpy was here" to the memfd
Using io_uring to read the registered buffer
io_uring says: Hello, world!
WRITE_FIXED completed: 15 bytes.

IMO this is utterly disgusting.  It makes logical sense based on how
the kernel works under the hood, but I think it makes no sense at all
as a matter of actual observable behavior.

Unsurprisingly, the behavior is identical if MADV_REMOVE is replaced
with ftruncate(0) + fallocate(size).

I can't shake the feeling that the problem here isn't so much that KHO
+ iommofd doesn't like this but that the behavior in question is
nonsensical.

My system's tmpfs does not appear to support FALLOC_FL_COLLAPSE_RANGE,
which is probably for the best :)

---

#define _GNU_SOURCE 1
#include <linux/io_uring.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/uio.h>
#include <unistd.h>
#include <fcntl.h>
#include <cerrno>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>

[[noreturn]] static void Fail(const char* operation) {
    std::perror(operation);
    std::exit(1);
}

static void* Map(int fd, size_t size, off_t offset) {
    void* p = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, offset);
    if (p == MAP_FAILED) Fail("mmap");
    return p;
}

// Single-threaded, one outstanding request, using the raw Linux io_uring ABI.
struct Ring {
    int fd;
    io_uring_params p{};
    char* sq;
    char* cq;
    io_uring_sqe* sqes;
    size_t sq_size, cq_size;

    Ring() {
        fd = static_cast<int>(syscall(SYS_io_uring_setup, 2, &p));
        if (fd < 0) Fail("io_uring_setup");
        sq_size = p.sq_off.array + p.sq_entries * sizeof(unsigned);
        cq_size = p.cq_off.cqes + p.cq_entries * sizeof(io_uring_cqe);
        if (p.features & IORING_FEAT_SINGLE_MMAP) {
            if (cq_size > sq_size) sq_size = cq_size;
            cq = sq = static_cast<char*>(Map(fd, sq_size, IORING_OFF_SQ_RING));
        } else {
            sq = static_cast<char*>(Map(fd, sq_size, IORING_OFF_SQ_RING));
            cq = static_cast<char*>(Map(fd, cq_size, IORING_OFF_CQ_RING));
        }
        sqes = static_cast<io_uring_sqe*>(Map(fd, p.sq_entries *
sizeof(*sqes), IORING_OFF_SQES));
    }

    ~Ring() {
        munmap(sqes, p.sq_entries * sizeof(*sqes));
        if (cq != sq) munmap(cq, cq_size);
        munmap(sq, sq_size);
        close(fd);
    }

    int WriteFixed(int output, const char* buffer, unsigned length,
uint64_t offset) {
        auto* tail = reinterpret_cast<unsigned*>(sq + p.sq_off.tail);
        const unsigned next = *tail;
        const unsigned slot = next & *reinterpret_cast<unsigned*>(sq +
p.sq_off.ring_mask);
        io_uring_sqe& entry = sqes[slot];
        entry = {};
        entry.opcode = IORING_OP_WRITE_FIXED;
        entry.fd = output;
        entry.off = offset;
        entry.addr = reinterpret_cast<uintptr_t>(buffer);
        entry.len = length;
        entry.buf_index = 0;
        reinterpret_cast<unsigned*>(sq + p.sq_off.array)[slot] = slot;
        __atomic_store_n(tail, next + 1, __ATOMIC_RELEASE);
        long submitted;
        do {
            submitted = syscall(SYS_io_uring_enter, fd, 1, 0, 0, nullptr, 0);
        } while (submitted < 0 && errno == EINTR);
        if (submitted != 1) {
            if (submitted >= 0) errno = EIO;
            Fail("io_uring_enter submit");
        }
        auto* head = reinterpret_cast<unsigned*>(cq + p.cq_off.head);
        auto* cq_tail = reinterpret_cast<unsigned*>(cq + p.cq_off.tail);
        const unsigned current = *head;
        while (__atomic_load_n(cq_tail, __ATOMIC_ACQUIRE) == current) {
            if (syscall(SYS_io_uring_enter, fd, 0, 1,
IORING_ENTER_GETEVENTS, nullptr, 0) < 0
                && errno != EINTR) Fail("io_uring_enter wait");
        }
        auto* completions = reinterpret_cast<io_uring_cqe*>(cq + p.cq_off.cqes);
        const int result = completions[current &
*reinterpret_cast<unsigned*>(cq + p.cq_off.ring_mask)].res;
        __atomic_store_n(head, current + 1, __ATOMIC_RELEASE);
        return result;
    }
};

int main() {
    printf("Creating and mmapping memfd and registering it\n");
    constexpr size_t size = 16 * 1024;
    const int fd = memfd_create("registered-buffer", MFD_CLOEXEC);
    if (fd < 0) Fail("memfd_create");
    if (fallocate(fd, 0, 0, size) < 0) Fail("fallocate");
    auto* buffer = static_cast<char*>(Map(fd, size, 0));
    Ring ring;
    iovec iov{buffer, size};
    if (syscall(SYS_io_uring_register, ring.fd,
IORING_REGISTER_BUFFERS, &iov, 1) < 0)
        Fail("IORING_REGISTER_BUFFERS");

    printf("memcpy \"Hello, world!\" to memfd\n");
    constexpr char hello[] = "Hello, world!";
    std::memcpy(buffer, hello, sizeof(hello));

    printf("MADV_REMOVE the contents\n");
    // MADV_REMOVE operates on whole pages. Remove the entire 16 KiB mapping.
    if (madvise(buffer, size, MADV_REMOVE) < 0) Fail("MADV_REMOVE");
    for (size_t i = 0; i < size; ++i) {
        if (buffer[i] != '\0') {
            std::fprintf(stderr, "MADV_REMOVE verification failed at
byte %zu\n", i);
            return 1;
        }
    }
    std::fprintf(stderr, "The memfd now contains NULs!\n");

    printf("memcpy \"memcpy was here\" to the memfd\n");
    constexpr char message[] = "memcpy was here";
    std::memcpy(buffer, message, sizeof(message));

    printf("Using io_uring to read the registered buffer\nio_uring says: ");
    fflush(stdout);
    const unsigned length = static_cast<unsigned>(std::strlen(message));
    int result = ring.WriteFixed(STDOUT_FILENO, buffer, length, UINT64_MAX);
    if (result < 0) {
        Fail("WRITE_FIXED");
    }
    if (result != static_cast<int>(length)) {
        std::fprintf(stderr, "Short WRITE_FIXED: %d of %u bytes\n",
result, length);
        return 1;
    }
    std::fprintf(stderr, "\nWRITE_FIXED completed: %d bytes.\n", result);
    if (syscall(SYS_io_uring_register, ring.fd,
IORING_UNREGISTER_BUFFERS, nullptr, 0) < 0)
        Fail("IORING_UNREGISTER_BUFFERS");
    munmap(buffer, size);
    close(fd);
}

-- 
Andy Lutomirski
AMA Capital Management, LLC

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-26 22:05 [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE Samiullah Khawaja
                   ` (2 preceding siblings ...)
  2026-10-26 22:46 ` [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE Andy Lutomirski
@ 2026-10-30 10:20 ` David Hildenbrand (Arm)
  2026-10-30 12:11   ` Jason Gunthorpe
  3 siblings, 1 reply; 17+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-30 10:20 UTC (permalink / raw)
  To: Samiullah Khawaja, Andrew Morton, Hugh Dickins
  Cc: Baolin Wang, Muchun Song, Oscar Salvador, Jeff Layton,
	Chuck Lever, Alexander Aring, Shuah Khan, Jason Gunthorpe,
	Gerd Hoffmann, Vivek Kasireddy, Jeff Xu, David Matlack,
	Pasha Tatashin, linux-mm, linux-api, linux-fsdevel,
	linux-kselftest, linux-kernel

On 10/26/26 23:05, Samiullah Khawaja wrote:
> This series introduces F_SEAL_PUNCH_HOLE, a new memfd seal that prevents
> holes from being punched into a shmem or hugetlbfs memfd while leaving
> the file fully writable.
> 
> Background
> ==========
> 
> Drivers such as udmabuf and iommufd (via IOMMU_IOAS_MAP_FILE) use
> memfd_pin_folios() to pin the folios backing a memfd and hand them to a
> device for DMA. Pinning keeps those folios allocated, but it does not
> keep them in the file. If userspace later calls
> fallocate(FALLOC_FL_PUNCH_HOLE) or madvise(MADV_REMOVE) on the range,
> the folios are dropped from the page cache while the device continues
> to use them. The pin holder is never notified, and from that point on
> the device and the file silently diverge.

We have the exact same situation when userspace maps anonymous folios into vfio
and then desides to MADV_DONTNEED the pages. Or any other memory backend that is
used for such purposes.

User space must not zap/punch things that might be pinned elsewhere.

That's not specific to memfd, udmabuf, iommufd or anything else.

I don't see a reason to make userspace (that should not shoot itself into the
foot) a kernel feature it must activate itself to not shoot itself into the foot.

-- 
Cheers,

David

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-30 10:20 ` David Hildenbrand (Arm)
@ 2026-10-30 12:11   ` Jason Gunthorpe
  2026-10-30 14:39     ` David Hildenbrand (Arm)
  0 siblings, 1 reply; 17+ messages in thread
From: Jason Gunthorpe @ 2026-10-30 12:11 UTC (permalink / raw)
  To: David Hildenbrand (Arm)
  Cc: Samiullah Khawaja, Andrew Morton, Hugh Dickins, Baolin Wang,
	Muchun Song, Oscar Salvador, Jeff Layton, Chuck Lever,
	Alexander Aring, Shuah Khan, Gerd Hoffmann, Vivek Kasireddy,
	Jeff Xu, David Matlack, Pasha Tatashin, linux-mm, linux-api,
	linux-fsdevel, linux-kselftest, linux-kernel

On Fri, Oct 30, 2026 at 11:20:30AM +0100, David Hildenbrand (Arm) wrote:
> On 10/26/26 23:05, Samiullah Khawaja wrote:
> > This series introduces F_SEAL_PUNCH_HOLE, a new memfd seal that prevents
> > holes from being punched into a shmem or hugetlbfs memfd while leaving
> > the file fully writable.
> > 
> > Background
> > ==========
> > 
> > Drivers such as udmabuf and iommufd (via IOMMU_IOAS_MAP_FILE) use
> > memfd_pin_folios() to pin the folios backing a memfd and hand them to a
> > device for DMA. Pinning keeps those folios allocated, but it does not
> > keep them in the file. If userspace later calls
> > fallocate(FALLOC_FL_PUNCH_HOLE) or madvise(MADV_REMOVE) on the range,
> > the folios are dropped from the page cache while the device continues
> > to use them. The pin holder is never notified, and from that point on
> > the device and the file silently diverge.
> 
> We have the exact same situation when userspace maps anonymous folios into vfio
> and then desides to MADV_DONTNEED the pages. Or any other memory backend that is
> used for such purposes.
> 
> User space must not zap/punch things that might be pinned elsewhere.
> 
> That's not specific to memfd, udmabuf, iommufd or anything else.
> 
> I don't see a reason to make userspace (that should not shoot itself into the
> foot) a kernel feature it must activate itself to not shoot itself into the foot.

The issue is live update doesn't preserve those background punched
folios and now we have created an UAF attack on the kernel across
kexec if you don't do something about it.

There was some notion here that with the right sealing flags we could
tell if everything was 'live update compatible' and then know that
these attacks are impossible.

Jason

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-30  2:11           ` Andy Lutomirski
@ 2026-10-30 12:12             ` Jason Gunthorpe
  2026-10-30 14:54               ` Andy Lutomirski
  0 siblings, 1 reply; 17+ messages in thread
From: Jason Gunthorpe @ 2026-10-30 12:12 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: Samiullah Khawaja, Andrew Morton, Hugh Dickins, Baolin Wang,
	David Hildenbrand, Muchun Song, Oscar Salvador, Jeff Layton,
	Chuck Lever, Alexander Aring, Shuah Khan, Gerd Hoffmann,
	Vivek Kasireddy, Jeff Xu, David Matlack, Pasha Tatashin, linux-mm,
	linux-api, linux-fsdevel, linux-kselftest, linux-kernel

On Thu, Oct 29, 2026 at 07:11:25PM -0700, Andy Lutomirski wrote:

> I can't shake the feeling that the problem here isn't so much that KHO
> + iommofd doesn't like this but that the behavior in question is
> nonsensical.

It has been this way for ages, like David says if userspace shoot
itself in the foot it should not be surprised by this illogical
behavior..

You can get the same nonsenscial thing with just mmap too, you don't
even need a memfd.

Jason

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-30 12:11   ` Jason Gunthorpe
@ 2026-10-30 14:39     ` David Hildenbrand (Arm)
  2026-10-30 15:11       ` Jason Gunthorpe
  2026-10-30 17:40       ` Samiullah Khawaja
  0 siblings, 2 replies; 17+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-30 14:39 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: Samiullah Khawaja, Andrew Morton, Hugh Dickins, Baolin Wang,
	Muchun Song, Oscar Salvador, Jeff Layton, Chuck Lever,
	Alexander Aring, Shuah Khan, Gerd Hoffmann, Vivek Kasireddy,
	Jeff Xu, David Matlack, Pasha Tatashin, linux-mm, linux-api,
	linux-fsdevel, linux-kselftest, linux-kernel

On 10/30/26 13:11, Jason Gunthorpe wrote:
> On Fri, Oct 30, 2026 at 11:20:30AM +0100, David Hildenbrand (Arm) wrote:
>> On 10/26/26 23:05, Samiullah Khawaja wrote:
>>> This series introduces F_SEAL_PUNCH_HOLE, a new memfd seal that prevents
>>> holes from being punched into a shmem or hugetlbfs memfd while leaving
>>> the file fully writable.
>>>
>>> Background
>>> ==========
>>>
>>> Drivers such as udmabuf and iommufd (via IOMMU_IOAS_MAP_FILE) use
>>> memfd_pin_folios() to pin the folios backing a memfd and hand them to a
>>> device for DMA. Pinning keeps those folios allocated, but it does not
>>> keep them in the file. If userspace later calls
>>> fallocate(FALLOC_FL_PUNCH_HOLE) or madvise(MADV_REMOVE) on the range,
>>> the folios are dropped from the page cache while the device continues
>>> to use them. The pin holder is never notified, and from that point on
>>> the device and the file silently diverge.
>>
>> We have the exact same situation when userspace maps anonymous folios into vfio
>> and then desides to MADV_DONTNEED the pages. Or any other memory backend that is
>> used for such purposes.
>>
>> User space must not zap/punch things that might be pinned elsewhere.
>>
>> That's not specific to memfd, udmabuf, iommufd or anything else.
>>
>> I don't see a reason to make userspace (that should not shoot itself into the
>> foot) a kernel feature it must activate itself to not shoot itself into the foot.
> 
> The issue is live update doesn't preserve those background punched
> folios and now we have created an UAF attack on the kernel across
> kexec if you don't do something about it.
> 
> There was some notion here that with the right sealing flags we could
> tell if everything was 'live update compatible' and then know that
> these attacks are impossible.
Just remember that there was any hole punch and refuse to live update this instance.

We could likely implement "we saw hole punch on a pinned folio" as well, but I
don't think that's required initially.

No need for more shaky seals.

-- 
Cheers,

David

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-30 12:12             ` Jason Gunthorpe
@ 2026-10-30 14:54               ` Andy Lutomirski
  2026-10-30 19:52                 ` Andy Lutomirski
  0 siblings, 1 reply; 17+ messages in thread
From: Andy Lutomirski @ 2026-10-30 14:54 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: Samiullah Khawaja, Andrew Morton, Hugh Dickins, Baolin Wang,
	David Hildenbrand, Muchun Song, Oscar Salvador, Jeff Layton,
	Chuck Lever, Alexander Aring, Shuah Khan, Gerd Hoffmann,
	Vivek Kasireddy, Jeff Xu, David Matlack, Pasha Tatashin, linux-mm,
	linux-api, linux-fsdevel, linux-kselftest, linux-kernel

On Fri, Oct 30, 2026 at 5:13 AM Jason Gunthorpe <jgg@ziepe.ca> wrote:
>
> On Thu, Oct 29, 2026 at 07:11:25PM -0700, Andy Lutomirski wrote:
>
> > I can't shake the feeling that the problem here isn't so much that KHO
> > + iommofd doesn't like this but that the behavior in question is
> > nonsensical.
>
> It has been this way for ages, like David says if userspace shoot
> itself in the foot it should not be surprised by this illogical
> behavior..
>

I dunno, a couple weeks ago I tracked down a 20+-year-old bug, and now
it's fixed :)

> You can get the same nonsenscial thing with just mmap too, you don't
> even need a memfd.

I feel like there are multiple levels of footgun here.

mmap anonymous (or MAP_PRIVATE), pin the pages, munmap: okay, mmap
creates unnamed private memory, and munmapping is maybe like unlinking
a file -- if someone else is still accessing it, it's still there.  In
fact, you can mmap, fork, unmap, then mmap in the same location, and
your child process still sees the original memory.  It's not clear to
me that anything is wrong.

mmap anonymous, pin, MADV_DONTNEED: okay, this one is starting to be a
small footgun.  But at least it's pointed at *your own feet* (unless
you've forked, which in which case maybe it's pointed at a related
process' feet).  I would still argue that the kernel could notice that
it's false that I "DON'T NEED" the memory and fail or ignore the
request.  And the kernel's pages are still anonymous.

mmap a named file or memfd, pin, truncate or punch hole: who says the
footgun is even pointed at my own feet?  If I mmap a named file,
SCM_RIGHTS it to you, and you mmap it too, and then I punch a hole,
the rmap machinery had better invalidate your mapping along with mine
(and get the TLB flush right!), and it would be an unambiguous and
severe bug if this didn't happen correctly.  After all, what you have
done is to assign a *logical range* within the file an address in your
address space, and nothing I do should change that fact (except,
sadly, truncate, which can give you a SIGBUS, but your vma still
logically refers to the file).

So why is the kernel exempt from keeping its own mappings coherent the
way it keeps user mappings coherent?

What if that memfd (or tmpfs file or whatever) is shared between
multiple processes that don't trust each other?  One of them can cause
it to become incoherent from the perspective of the other one's use of
kernel APIs.



P.S. You can access the offending APIs from a VM guest using balloon
in typical implementations.  If the host userspace does not strictly
enforce that the guest doesn't balloon memory that is not ordinary
host memory *and that is absolutely not in use for any sort of
kernel-side pinned-folio IO*, then the guest may be able to cause the
host kernel's or hardware's view of its memory to become incoherent.
Heck, host userspace could literally use a memfd to share memory
between the guest and a separate host process that implements a
device.

P.P.S. It's possible for there to be a legitimate security boundary
between user or guest code and a future version of *the same*
userspace or guest, via DRTM.  Someone should build a guest that sets
up some form of host-kernel-pinned-folio IO (IOMMU or simply a
host-side emulated device that works this way), then
DONTNEED/REMOVE/hole-punch the memory, then do the DRTM dance and
attest to its state.

--Andy


-- 
Andy Lutomirski
AMA Capital Management, LLC

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-30 14:39     ` David Hildenbrand (Arm)
@ 2026-10-30 15:11       ` Jason Gunthorpe
  2026-10-30 17:40       ` Samiullah Khawaja
  1 sibling, 0 replies; 17+ messages in thread
From: Jason Gunthorpe @ 2026-10-30 15:11 UTC (permalink / raw)
  To: David Hildenbrand (Arm)
  Cc: Samiullah Khawaja, Andrew Morton, Hugh Dickins, Baolin Wang,
	Muchun Song, Oscar Salvador, Jeff Layton, Chuck Lever,
	Alexander Aring, Shuah Khan, Gerd Hoffmann, Vivek Kasireddy,
	Jeff Xu, David Matlack, Pasha Tatashin, linux-mm, linux-api,
	linux-fsdevel, linux-kselftest, linux-kernel

On Fri, Oct 30, 2026 at 03:39:40PM +0100, David Hildenbrand (Arm) wrote:

> >> I don't see a reason to make userspace (that should not shoot itself into the
> >> foot) a kernel feature it must activate itself to not shoot itself into the foot.
> > 
> > The issue is live update doesn't preserve those background punched
> > folios and now we have created an UAF attack on the kernel across
> > kexec if you don't do something about it.
> > 
> > There was some notion here that with the right sealing flags we could
> > tell if everything was 'live update compatible' and then know that
> > these attacks are impossible.
> Just remember that there was any hole punch and refuse to live update this instance.
> 
> We could likely implement "we saw hole punch on a pinned folio" as well, but I
> don't think that's required initially.
> 
> No need for more shaky seals.

Ah, yeah that might work out. It has to be checked really late though,
after userspace cannot change the memfd any further.

Jason

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-30 14:39     ` David Hildenbrand (Arm)
  2026-10-30 15:11       ` Jason Gunthorpe
@ 2026-10-30 17:40       ` Samiullah Khawaja
  1 sibling, 0 replies; 17+ messages in thread
From: Samiullah Khawaja @ 2026-10-30 17:40 UTC (permalink / raw)
  To: David Hildenbrand (Arm)
  Cc: Jason Gunthorpe, Andrew Morton, Hugh Dickins, Baolin Wang,
	Muchun Song, Oscar Salvador, Jeff Layton, Chuck Lever,
	Alexander Aring, Shuah Khan, Gerd Hoffmann, Vivek Kasireddy,
	Jeff Xu, David Matlack, Pasha Tatashin, linux-mm, linux-api,
	linux-fsdevel, linux-kselftest, linux-kernel

On Fri, Oct 30, 2026 at 03:39:40PM +0100, David Hildenbrand (Arm) wrote:
>On 10/30/26 13:11, Jason Gunthorpe wrote:
>> On Fri, Oct 30, 2026 at 11:20:30AM +0100, David Hildenbrand (Arm) wrote:
>>> On 10/26/26 23:05, Samiullah Khawaja wrote:
>>>> This series introduces F_SEAL_PUNCH_HOLE, a new memfd seal that prevents
>>>> holes from being punched into a shmem or hugetlbfs memfd while leaving
>>>> the file fully writable.
>>>>
>>>> Background
>>>> ==========
>>>>
>>>> Drivers such as udmabuf and iommufd (via IOMMU_IOAS_MAP_FILE) use
>>>> memfd_pin_folios() to pin the folios backing a memfd and hand them to a
>>>> device for DMA. Pinning keeps those folios allocated, but it does not
>>>> keep them in the file. If userspace later calls
>>>> fallocate(FALLOC_FL_PUNCH_HOLE) or madvise(MADV_REMOVE) on the range,
>>>> the folios are dropped from the page cache while the device continues
>>>> to use them. The pin holder is never notified, and from that point on
>>>> the device and the file silently diverge.
>>>
>>> We have the exact same situation when userspace maps anonymous folios into vfio
>>> and then desides to MADV_DONTNEED the pages. Or any other memory backend that is
>>> used for such purposes.
>>>
>>> User space must not zap/punch things that might be pinned elsewhere.
>>>
>>> That's not specific to memfd, udmabuf, iommufd or anything else.
>>>
>>> I don't see a reason to make userspace (that should not shoot itself into the
>>> foot) a kernel feature it must activate itself to not shoot itself into the foot.

Agreed, for normal use this is userspace's problem. The seal was meant
for the kernel to cheaply verify at preserve time that nothing was
punched.

>>
>> The issue is live update doesn't preserve those background punched
>> folios and now we have created an UAF attack on the kernel across
>> kexec if you don't do something about it.
>>
>> There was some notion here that with the right sealing flags we could
>> tell if everything was 'live update compatible' and then know that
>> these attacks are impossible.
>Just remember that there was any hole punch and refuse to live update this instance.
>
>We could likely implement "we saw hole punch on a pinned folio" as well, but I
>don't think that's required initially.

If I understand correctly, you mean recording on the memfd that a hole
punch happened, and refusing the live update if it did? That would work
and is much cheaper than what we do today, where iommufd walks all the
mapped pages at preserve time. For truncate, the existing F_SEAL_SHRINK
is enough or I can record that also on the memfd. I will send patches
for this.

>
>No need for more shaky seals.
>
>-- 
>Cheers,
>
>David

Thanks for the feedback,
Sami

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE
  2026-10-30 14:54               ` Andy Lutomirski
@ 2026-10-30 19:52                 ` Andy Lutomirski
  0 siblings, 0 replies; 17+ messages in thread
From: Andy Lutomirski @ 2026-10-30 19:52 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: Samiullah Khawaja, Andrew Morton, Hugh Dickins, Baolin Wang,
	David Hildenbrand, Muchun Song, Oscar Salvador, Jeff Layton,
	Chuck Lever, Alexander Aring, Shuah Khan, Gerd Hoffmann,
	Vivek Kasireddy, Jeff Xu, David Matlack, Pasha Tatashin, linux-mm,
	linux-api, linux-fsdevel, linux-kselftest, linux-kernel

On Fri, Oct 30, 2026 at 7:54 AM Andy Lutomirski <luto@amacapital.net> wrote:
>
> On Fri, Oct 30, 2026 at 5:13 AM Jason Gunthorpe <jgg@ziepe.ca> wrote:
> >
> > On Thu, Oct 29, 2026 at 07:11:25PM -0700, Andy Lutomirski wrote:
> >
> > > I can't shake the feeling that the problem here isn't so much that KHO
> > > + iommofd doesn't like this but that the behavior in question is
> > > nonsensical.
> >
> > It has been this way for ages, like David says if userspace shoot
> > itself in the foot it should not be surprised by this illogical
> > behavior..
> >
>
> I dunno, a couple weeks ago I tracked down a 20+-year-old bug, and now
> it's fixed :)
>
> > You can get the same nonsenscial thing with just mmap too, you don't
> > even need a memfd.
>
> I feel like there are multiple levels of footgun here.
>
> mmap anonymous (or MAP_PRIVATE), pin the pages, munmap: okay, mmap
> creates unnamed private memory, and munmapping is maybe like unlinking
> a file -- if someone else is still accessing it, it's still there.  In
> fact, you can mmap, fork, unmap, then mmap in the same location, and
> your child process still sees the original memory.  It's not clear to
> me that anything is wrong.
>
> mmap anonymous, pin, MADV_DONTNEED: okay, this one is starting to be a
> small footgun.  But at least it's pointed at *your own feet* (unless
> you've forked, which in which case maybe it's pointed at a related
> process' feet).  I would still argue that the kernel could notice that
> it's false that I "DON'T NEED" the memory and fail or ignore the
> request.  And the kernel's pages are still anonymous.
>
> mmap a named file or memfd, pin, truncate or punch hole: who says the
> footgun is even pointed at my own feet?  If I mmap a named file,
> SCM_RIGHTS it to you, and you mmap it too, and then I punch a hole,
> the rmap machinery had better invalidate your mapping along with mine
> (and get the TLB flush right!), and it would be an unambiguous and
> severe bug if this didn't happen correctly.  After all, what you have
> done is to assign a *logical range* within the file an address in your
> address space, and nothing I do should change that fact (except,
> sadly, truncate, which can give you a SIGBUS, but your vma still
> logically refers to the file).
>
> So why is the kernel exempt from keeping its own mappings coherent the
> way it keeps user mappings coherent?
>
> What if that memfd (or tmpfs file or whatever) is shared between
> multiple processes that don't trust each other?  One of them can cause
> it to become incoherent from the perspective of the other one's use of
> kernel APIs.
>
>

I'm contemplating what would be involved in fixing this for real.

First, there's a scary code path in shmem_replace_folio.  AFAICT
memfd_pin_folio() can race with swapout, so a pinned folio can be in
swap cache (obviously we won't *free* the folio if we do this).    If
we then decide to replace the folio when we swap it back in,
shmem_replace_folio() will not notice the pin and we become incoherent
and no one needs to hole-punch the file for this to happen.
Fortunately I haven't spotted a reason that shmem_should_replace_folio
would return true in a case where we care, but if someone does what
the comment suggests and makes it start respecting cpuset or NUMA
policy, this could easily change.

Other than that, I think that hole punch could be made to be fully
safe in one of a couple of not-horribly-invasive ways:

Choice A: Make hole punch work like migration.  Unmap from page
tables, do folio_ref_freeze(), and tolerate failure -- either just
abort the hole punch operation or zero the contents instead of
punching it out.  (And contemplate whether this could be made slightly
more permissive by tolerating a swap entry that still has a live folio
if the live folio has the expected number of references.)

Choice B: In addition to tracking references, mappings, and pins of a
folio, also track a new type of reference that keeps the folio from
dissociating with its mapping or changing its address within the
mapping.  Most code that pins a folio would be changed to take one of
these fancy new pins -- the main thing I'm thinking of that wouldn't
need to is writeback or perhaps non-long-term pins that are just
reading the contents.

Choice C: like B but instead of deciding per caller, treat exactly
paths that take long term pins as also pinning the folio into the
mapping.

For B and C, a big open question would be how this interacts with
truncate.  I think the idea behavior is that a mapping-pinned folio
that's past the end of a file would remain and if the file gets
extended then that folio would represent the portion of the file that
it covers again.

-- 
Andy Lutomirski
AMA Capital Management, LLC

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2026-10-30 19:53 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-26 22:05 [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE Samiullah Khawaja
2026-10-26 22:05 ` [PATCH 1/2] " Samiullah Khawaja
2026-10-26 22:05 ` [PATCH 2/2] selftests/memfd: add F_SEAL_PUNCH_HOLE tests Samiullah Khawaja
2026-10-26 22:46 ` [PATCH 0/2] mm/memfd: add F_SEAL_PUNCH_HOLE Andy Lutomirski
2026-10-27 22:55   ` Samiullah Khawaja
2026-10-28  3:57     ` Andy Lutomirski
2026-10-29 23:16       ` Samiullah Khawaja
2026-10-30  0:26         ` Andy Lutomirski
2026-10-30  2:11           ` Andy Lutomirski
2026-10-30 12:12             ` Jason Gunthorpe
2026-10-30 14:54               ` Andy Lutomirski
2026-10-30 19:52                 ` Andy Lutomirski
2026-10-30 10:20 ` David Hildenbrand (Arm)
2026-10-30 12:11   ` Jason Gunthorpe
2026-10-30 14:39     ` David Hildenbrand (Arm)
2026-10-30 15:11       ` Jason Gunthorpe
2026-10-30 17:40       ` Samiullah Khawaja

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox