History

mm: add VM_UFFD_RWP VMA flag

Preparatory patch for userfaultfd read-write protection (RWP). RWP extends userfaultfd protection from plain write-protection (WP) to full read-write protection: accesses to an RWP-protected range -- reads as well as writes -- trap through userfaultfd. Reserve VM_UFFD_RWP, add the userfaultfd_rwp() and userfaultfd_protected() helpers, and wire up the smaps "ur" entry and the trace-flag table the rest of the series will use. The flag is gated on CONFIG_USERFAULTFD_RWP, which is introduced together with the UAPI in a later patch; until then VM_UFFD_RWP aliases VM_NONE and every downstream check folds to dead code. Nothing sets or queries the flag yet.

Kiryl Shutsemau (Meta) committed 4 months ago

Commit 134c1d01 parent 2730ee25 files · +75 −24Browse files

Unified · Split

5 files
  1. Documentation/filesystems/proc.rst +1 −0
  2. fs/proc/task_mmu.c +3 −0
  3. include/linux/mm.h +28 −15
  4. include/linux/userfaultfd_k.h +36 −9
  5. include/trace/events/mmflags.h +7 −0
Documentation/filesystems/proc.rst+1−0
@@ -608,6 +608,7 @@ encoded manner. The codes are the following: · 607 unmodified lines
608 um userfaultfd missing tracking608 um userfaultfd missing tracking
609 uw userfaultfd wr-protect tracking609 uw userfaultfd wr-protect tracking
610 ui userfaultfd minor fault610 ui userfaultfd minor fault
611 ur userfaultfd read-write-protect tracking
611 ss shadow/guarded control stack page612 ss shadow/guarded control stack page
612 sl sealed613 sl sealed
613 lf lock on fault pages614 lf lock on fault pages
fs/proc/task_mmu.c+3−0
@@ -1237,6 +1237,9 @@ static void show_smap_vma_flags(struct seq_file *m, struct vm_area_struct *vma) · 1236 unmodified lines
1237#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR1237#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
1238 [ilog2(VM_UFFD_MINOR)] = "ui",1238 [ilog2(VM_UFFD_MINOR)] = "ui",
1239#endif /* CONFIG_HAVE_ARCH_USERFAULTFD_MINOR */1239#endif /* CONFIG_HAVE_ARCH_USERFAULTFD_MINOR */
1240#ifdef CONFIG_USERFAULTFD_RWP
1241 [ilog2(VM_UFFD_RWP)] = "ur",
1242#endif
1240#ifdef CONFIG_ARCH_HAS_USER_SHADOW_STACK1243#ifdef CONFIG_ARCH_HAS_USER_SHADOW_STACK
1241 [ilog2(VM_SHADOW_STACK)] = "ss",1244 [ilog2(VM_SHADOW_STACK)] = "ss",
1242#endif1245#endif
include/linux/mm.h+28−15
@@ -354,6 +354,7 @@ enum { · 353 unmodified lines
354#endif354#endif
355 DECLARE_VMA_BIT(UFFD_MINOR, 41),355 DECLARE_VMA_BIT(UFFD_MINOR, 41),
356 DECLARE_VMA_BIT(SEALED, 42),356 DECLARE_VMA_BIT(SEALED, 42),
357 DECLARE_VMA_BIT(UFFD_RWP, 43),
357 /* Flags that reuse flags above. */358 /* Flags that reuse flags above. */
358 DECLARE_VMA_BIT_ALIAS(PKEY_BIT0, HIGH_ARCH_0),359 DECLARE_VMA_BIT_ALIAS(PKEY_BIT0, HIGH_ARCH_0),
359 DECLARE_VMA_BIT_ALIAS(PKEY_BIT1, HIGH_ARCH_1),360 DECLARE_VMA_BIT_ALIAS(PKEY_BIT1, HIGH_ARCH_1),
@@ -497,12 +498,17 @@ enum { · 137 unmodified lines
497#else498#else
498#define VM_UFFD_MINOR VM_NONE499#define VM_UFFD_MINOR VM_NONE
499#endif500#endif
501#ifdef CONFIG_USERFAULTFD_RWP
502#define VM_UFFD_RWP INIT_VM_FLAG(UFFD_RWP)
503#else
504#define VM_UFFD_RWP VM_NONE
505#endif
500 506
501/*507/*
502 * vma_flags_t masks for the userfaultfd VMA flags. VMA_UFFD_MINOR is gated on508 * vma_flags_t masks for the userfaultfd VMA flags. The two high-bit modes are
503 * the same config as VM_UFFD_MINOR -- which implies 64BIT, where the bit fits509 * gated on the same configs as their VM_* flags above -- both of which imply
504 * -- so an out-of-range bit is never fed to mk_vma_flags() on a build whose510 * 64BIT -- so an out-of-range bit is never fed to mk_vma_flags() on a build
505 * bitmap cannot hold it.511 * whose bitmap cannot hold it.
506 */512 */
507#define VMA_UFFD_MISSING mk_vma_flags(VMA_UFFD_MISSING_BIT)513#define VMA_UFFD_MISSING mk_vma_flags(VMA_UFFD_MISSING_BIT)
508#define VMA_UFFD_WP mk_vma_flags(VMA_UFFD_WP_BIT)514#define VMA_UFFD_WP mk_vma_flags(VMA_UFFD_WP_BIT)
@@ -511,6 +517,11 @@ enum { · 2 unmodified lines
511#else517#else
512#define VMA_UFFD_MINOR EMPTY_VMA_FLAGS518#define VMA_UFFD_MINOR EMPTY_VMA_FLAGS
513#endif519#endif
520#ifdef CONFIG_USERFAULTFD_RWP
521#define VMA_UFFD_RWP mk_vma_flags(VMA_UFFD_RWP_BIT)
522#else
523#define VMA_UFFD_RWP EMPTY_VMA_FLAGS
524#endif
514 525
515#ifdef CONFIG_64BIT526#ifdef CONFIG_64BIT
516#define VM_ALLOW_ANY_UNCACHED INIT_VM_FLAG(ALLOW_ANY_UNCACHED)527#define VM_ALLOW_ANY_UNCACHED INIT_VM_FLAG(ALLOW_ANY_UNCACHED)
@@ -649,22 +660,24 @@ enum { · 132 unmodified lines
649 * reconsistuted upon page fault, so necessitate page table copying upon fork.660 * reconsistuted upon page fault, so necessitate page table copying upon fork.
650 *661 *
651 * Note that these flags should be compared with the DESTINATION VMA not the662 * Note that these flags should be compared with the DESTINATION VMA not the
652 * source, as VM_UFFD_WP may not be propagated to destination, while all other663 * source: VM_UFFD_WP and VM_UFFD_RWP may be cleared on the destination
653 * flags will be.664 * (dup_userfaultfd() -> userfaultfd_reset_ctx() when the parent context did
665 * not negotiate UFFD_FEATURE_EVENT_FORK), while all other flags propagate.
654 *666 *
655 * VM_PFNMAP / VM_MIXEDMAP - These contain kernel-mapped data which cannot be667 * VM_PFNMAP / VM_MIXEDMAP - These contain kernel-mapped data which cannot be
656 * reasonably reconstructed on page fault.668 * reasonably reconstructed on page fault.
657 *669 *
658 * VM_UFFD_WP - Encodes metadata about an installed uffd670 * VM_UFFD_WP - Encodes metadata about an installed uffd
659 * write protect handler, which cannot be671 * VM_UFFD_RWP write- or read-write-protect handler, which
660 * reconstructed on page fault.672 * cannot be reconstructed on page fault.
661 *673 *
662 * We always copy pgtables when dst_vma has uffd-wp674 * We always copy pgtables when dst_vma has the
663 * enabled even if it's file-backed675 * uffd PTE bit in use even if it's file-backed
664 * (e.g. shmem). Because when uffd-wp is enabled,676 * (e.g. shmem). Because when the uffd bit is
665 * pgtable contains uffd-wp protection information,677 * in use, the pgtable contains the protection
666 * that's something we can't retrieve from page cache,678 * information, that's something we can't
667 * and skip copying will lose those info.679 * retrieve from page cache, and skip copying
680 * will lose those info.
668 *681 *
669 * VM_MAYBE_GUARD - Could contain page guard region markers which682 * VM_MAYBE_GUARD - Could contain page guard region markers which
670 * by design are a property of the page tables683 * by design are a property of the page tables
include/linux/userfaultfd_k.h+36−9
@@ -21,10 +21,11 @@ · 20 unmodified lines
21#include <linux/hugetlb_inline.h>21#include <linux/hugetlb_inline.h>
22 22
23/* The set of all possible UFFD-related VM flags. */23/* The set of all possible UFFD-related VM flags. */
24#define __VM_UFFD_FLAGS (VM_UFFD_MISSING | VM_UFFD_WP | VM_UFFD_MINOR)24#define __VM_UFFD_FLAGS (VM_UFFD_MISSING | VM_UFFD_MINOR | \
25 VM_UFFD_WP | VM_UFFD_RWP)
25 26
26#define __VMA_UFFD_FLAGS mk_vma_flags_from_masks(VMA_UFFD_MISSING, VMA_UFFD_WP, \27#define __VMA_UFFD_FLAGS mk_vma_flags_from_masks(VMA_UFFD_MISSING, VMA_UFFD_WP, \
27 VMA_UFFD_MINOR)28 VMA_UFFD_MINOR, VMA_UFFD_RWP)
28 29
29/*30/*
30 * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining31 * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining
@@ -168,7 +169,8 @@ static inline bool is_mergeable_vm_userfaultfd_ctx(struct vm_area_struct *vma, · 137 unmodified lines
168/*169/*
169 * Never enable huge pmd sharing on some uffd registered vmas:170 * Never enable huge pmd sharing on some uffd registered vmas:
170 *171 *
171 * - VM_UFFD_WP VMAs, because write protect information is per pgtable entry.172 * - VM_UFFD_WP and VM_UFFD_RWP VMAs, because the write protect / access
173 * tracking information is per pgtable entry.
172 *174 *
173 * - VM_UFFD_MINOR VMAs, because otherwise we would never get minor faults for175 * - VM_UFFD_MINOR VMAs, because otherwise we would never get minor faults for
174 * VMAs which share huge pmds. (If you have two mappings to the same176 * VMAs which share huge pmds. (If you have two mappings to the same
@@ -179,20 +181,25 @@ static inline bool is_mergeable_vm_userfaultfd_ctx(struct vm_area_struct *vma, · 4 unmodified lines
179static inline bool uffd_disable_huge_pmd_share(struct vm_area_struct *vma)181static inline bool uffd_disable_huge_pmd_share(struct vm_area_struct *vma)
180{182{
181 return vma_test_any_mask(vma,183 return vma_test_any_mask(vma,
182 mk_vma_flags_from_masks(VMA_UFFD_WP, VMA_UFFD_MINOR));184 mk_vma_flags_from_masks(VMA_UFFD_WP, VMA_UFFD_RWP,
185 VMA_UFFD_MINOR));
183}186}
184 187
185/*188/*
186 * Don't do fault around for either WP or MINOR registered uffd range. For189 * Don't do fault around for WP, RWP or MINOR registered uffd range. For
187 * MINOR registered range, fault around will be a total disaster and ptes can190 * MINOR registered range, fault around will be a total disaster and ptes can
188 * be installed without notifications; for WP it should mostly be fine as long191 * be installed without notifications; for WP it should mostly be fine as long
189 * as the fault around checks for pte_none() before the installation, however192 * as the fault around checks for pte_none() before the installation, however
190 * to be super safe we just forbid it.193 * to be super safe we just forbid it; for RWP, pre-faulted neighbours would
194 * be indistinguishable from accessed pages in PAGEMAP_SCAN (PAGE_IS_ACCESSED)
195 * and pollute the tracked working set, so each page must be populated by its
196 * own fault.
191 */197 */
192static inline bool uffd_disable_fault_around(struct vm_area_struct *vma)198static inline bool uffd_disable_fault_around(struct vm_area_struct *vma)
193{199{
194 return vma_test_any_mask(vma,200 return vma_test_any_mask(vma,
195 mk_vma_flags_from_masks(VMA_UFFD_WP, VMA_UFFD_MINOR));201 mk_vma_flags_from_masks(VMA_UFFD_WP, VMA_UFFD_RWP,
202 VMA_UFFD_MINOR));
196}203}
197 204
198static inline bool userfaultfd_missing(struct vm_area_struct *vma)205static inline bool userfaultfd_missing(struct vm_area_struct *vma)
@@ -210,6 +217,16 @@ static inline bool userfaultfd_minor(struct vm_area_struct *vma) · 11 unmodified lines
210 return vma_test_any_mask(vma, VMA_UFFD_MINOR);217 return vma_test_any_mask(vma, VMA_UFFD_MINOR);
211}218}
212 219
220static inline bool userfaultfd_rwp(struct vm_area_struct *vma)
221{
222 return vma_test_single_mask(vma, VMA_UFFD_RWP);
223}
224
225static inline bool userfaultfd_protected(struct vm_area_struct *vma)
226{
227 return userfaultfd_wp(vma) || userfaultfd_rwp(vma);
228}
229
213static inline bool userfaultfd_pte_wp(struct vm_area_struct *vma,230static inline bool userfaultfd_pte_wp(struct vm_area_struct *vma,
214 pte_t pte)231 pte_t pte)
215{232{
@@ -330,6 +347,16 @@ static inline bool userfaultfd_minor(struct vm_area_struct *vma) · 114 unmodified lines
330 return false;347 return false;
331}348}
332 349
350static inline bool userfaultfd_rwp(struct vm_area_struct *vma)
351{
352 return false;
353}
354
355static inline bool userfaultfd_protected(struct vm_area_struct *vma)
356{
357 return false;
358}
359
333static inline bool userfaultfd_pte_wp(struct vm_area_struct *vma,360static inline bool userfaultfd_pte_wp(struct vm_area_struct *vma,
334 pte_t pte)361 pte_t pte)
335{362{
@@ -423,8 +450,8 @@ static inline bool userfaultfd_wp_use_markers(struct vm_area_struct *vma) · 87 unmodified lines
423}450}
424 451
425/*452/*
426 * Returns true if this is a swap pte and was uffd-wp wr-protected in either453 * Returns true if this swap pte carries uffd-tracked state in either
427 * forms (pte marker or a normal swap pte), false otherwise.454 * form (pte marker or a normal swap pte), false otherwise.
428 */455 */
429static inline bool pte_swp_uffd_any(pte_t pte)456static inline bool pte_swp_uffd_any(pte_t pte)
430{457{
include/trace/events/mmflags.h+7−0
@@ -186,6 +186,12 @@ IF_HAVE_PG_ARCH_3(arch_3) · 185 unmodified lines
186# define IF_HAVE_UFFD_MINOR(flag, name)186# define IF_HAVE_UFFD_MINOR(flag, name)
187#endif187#endif
188 188
189#ifdef CONFIG_USERFAULTFD_RWP
190# define IF_HAVE_UFFD_RWP(flag, name) {flag, name},
191#else
192# define IF_HAVE_UFFD_RWP(flag, name)
193#endif
194
189#if defined(CONFIG_64BIT) || defined(CONFIG_PPC32)195#if defined(CONFIG_64BIT) || defined(CONFIG_PPC32)
190# define IF_HAVE_VM_DROPPABLE(flag, name) {flag, name},196# define IF_HAVE_VM_DROPPABLE(flag, name) {flag, name},
191#else197#else
@@ -207,6 +213,7 @@ IF_HAVE_UFFD_MINOR(VM_UFFD_MINOR, "uffd_minor" ) \ · 15 unmodified lines
207 {VM_PFNMAP, "pfnmap" }, \213 {VM_PFNMAP, "pfnmap" }, \
208 {VM_MAYBE_GUARD, "maybe_guard" }, \214 {VM_MAYBE_GUARD, "maybe_guard" }, \
209 {VM_UFFD_WP, "uffd_wp" }, \215 {VM_UFFD_WP, "uffd_wp" }, \
216IF_HAVE_UFFD_RWP(VM_UFFD_RWP, "uffd_rwp" ) \
210 {VM_LOCKED, "locked" }, \217 {VM_LOCKED, "locked" }, \
211 {VM_IO, "io" }, \218 {VM_IO, "io" }, \
212 {VM_SEQ_READ, "seqread" }, \219 {VM_SEQ_READ, "seqread" }, \