Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [SECURITY] mm: MADV_GUARD_INSTALL drops uffd-wp/rwp markers silently
@ 2026-11-10 12:09 n3rd
  2026-11-10 12:12 ` Lorenzo Stoakes (ARM)
  0 siblings, 1 reply; 2+ messages in thread
From: n3rd @ 2026-11-10 12:09 UTC (permalink / raw)
  To: linux-mm@kvack.org, linux-kernel@vger.kernel.org
  Cc: akpm@linux-foundation.org, ljs@kernel.org, Liam R. Howlett,
	security@kernel.org

[-- Attachment #1: Type: text/plain, Size: 5880 bytes --]



Hello,


I am reporting a security bug in the Linux kernel.


1. Affected kernel versions
Linux v7.3-rc7 (mainline, built and tested as 7.3.0-rc7) is affected and was reproduced on two fresh boots. The same code is byte-identical in v7.2.9 and v7.3-rc6 according to the static comparison records of the workspace that produced this report, so both are affected as well; earlier releases were not compared one by one. There is no upstream fix as of v7.3-rc7 and no CVE has been assigned.


2. Problem description
madvise_guard_install() in mm/madvise.c is the only behaviour in that file that drops pages without notifying the userfaultfd manager. When the target VMA is registered and armed with UFFDIO_REGISTER_MODE_WP plus UFFDIO_WRITEPROTECT, or with the v7.3 UFFDIO_REGISTER_MODE_RWP plus UFFDIO_RWPROTECT, is_valid_guard_vma() only tests VM_SPECIAL, VM_HUGETLB and VM_LOCKED, so the call is not refused. The page table walk then finds the armed uffd PTE, returns that a zap is needed, and the code calls zap_vma_range() for the whole range. That zap clears the PTE level uffd marker, _PAGE_UFFD in the WP case and the PAGE_NONE plus uffd combination in the RWP case, and it never calls userfaultfd_remove(). The sibling paths in the same file do: madvise_dontneed_free() for MADV_DONTNEED, MADV_DONTNEED_LOCKED and MADV_FREE, and madvise_remove() for MADV_REMOVE, both call userfaultfd_remove() first, and that function is the only delivery point for UFFD_EVENT_REMOVE. After a following MADV_GUARD_REMOVE the PTE becomes none, the next write takes do_anonymous_page(), where vmf_orig_pte_uffd_wp() is false because orig_pte is not a uffd marker, and the new page is installed writable with no uffd event at all. The monitor therefore loses write protection enforcement for that page permanently and cannot tell that apart from no write happening. A reproducer shows the whole chain: with the VMA armed, a plain write is blocked and produces a PAGEFAULT; after MADV_GUARD_INSTALL the event count is zero while touching the page raises SIGSEGV, which proves the guard marker was really installed and the pages were really zapped; after MADV_GUARD_REMOVE the same write completes in a few milliseconds with zero events; the same VMA shape changed to MADV_DONTNEED does deliver UFFD_EVENT_REMOVE. kprobes on madvise_guard_install, madvise_guard_remove, zap_vma_range and userfaultfd_remove, filtered to the probe VMA, give userfaultfd_remove equal to zero for the guard phase and one for the DONTNEED phase. KASAN, Oops, general protection and WARNING counts are zero in both runs: this is a lost enforcement signal, not memory corruption.


3. Reproducer
Three statically linked C programs are used, mm04 for the full six check sequence in both WP and RWP mode, mm04phase for the control, guard and dontneed phases as separate processes, and mm04probe for the DONTNEED remove event. They carry their own UAPI constants, run as root inside a QEMU guest, and print explicit verdict lines such as PHASE VERDICT guard_notify_missing=1 and MM04 [C] write completed in 2.5ms with ZERO uffd events. The complete reproducer and the raw console logs are available on request; I am not posting them publicly.


4. Conditions
The target VMA must already be registered and armed with UFFDIO_REGISTER_MODE_WP plus UFFDIO_WRITEPROTECT, or with UFFDIO_REGISTER_MODE_RWP plus UFFDIO_RWPROTECT, and UFFD_FEATURE_EVENT_REMOVE must be negotiated. Triggering needs only MADV_GUARD_INSTALL and MADV_GUARD_REMOVE, so the sequence is deterministic and has no race window. It does not depend on THP, SLUB layout or kernel randomisation. No capability is required: madvise(2) only acts on the caller's own mm, and userfaultfd(2) is always available to root or CAP_SYS_PTRACE, while unprivileged users need vm.unprivileged_userfaultfd=1. The attacker is the monitored process itself and the victim is the uffd manager, which can be another thread of the same process or a separate process. Tested on x86_64 with QEMU 6.2.0 in TCG mode, two vCPUs, 4096 MB of RAM and an initramfs, with CONFIG_USERFAULTFD, CONFIG_USERFAULTFD_RWP and CONFIG_PTE_MARKER_UFFD_WP enabled and KASAN on as a control.


5. Suspected location
mm/madvise.c: madvise_guard_install(), the zap_vma_range() call in it; is_valid_guard_vma() in the same file lacks a userfaultfd_armed() test. The two notifying call sites are madvise_dontneed_free() and madvise_remove() in the same file, and userfaultfd_remove() itself is in mm/userfaultfd.c.


6. Suggested fix
Either call userfaultfd_remove() for the range before zap_vma_range() when userfaultfd_armed() is true, mirroring madvise_dontneed_free() and re-validating the VMA after the mmap read lock is dropped, or make is_valid_guard_vma() refuse a uffd armed VMA with -EINVAL, matching the existing uffd exclusion in the VMA lock path. A more complete option is to add mm/madvise.c to the RWP marker preservation family that the series mm: preserve RWP marker across PTE rewrites already covers.


7. Mitigation
Monitors should not rely on events alone: re-arm the range with UFFDIO_WRITEPROTECT or UFFDIO_RWPROTECT periodically and cross-check with pagemap soft-dirty or accessed bits. Applications should avoid MADV_GUARD_INSTALL on a range that is registered with userfaultfd. Administrators can keep vm.unprivileged_userfaultfd at zero, although a root monitor still has to re-arm periodically.


8. Disclosure
Public. This report is sent to the public linux-mm list and LKML and no embargo is requested. The reproducer is not posted publicly; it is available on request.


9. AI-assistance note
This report was prepared with the help of an AI coding assistant, which was used for the source review, for building and running the test kernel, and for drafting this text. Per the kernel security process I treat the issue as public and I am not posting the reproducer publicly.

[-- Attachment #2: Type: text/html, Size: 6798 bytes --]

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

end of thread, other threads:[~2026-11-10 12:12 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-10 12:09 [SECURITY] mm: MADV_GUARD_INSTALL drops uffd-wp/rwp markers silently n3rd
2026-11-10 12:12 ` Lorenzo Stoakes (ARM)

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