]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.12-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 22 Apr 2025 12:51:25 +0000 (14:51 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 22 Apr 2025 12:51:25 +0000 (14:51 +0200)
added patches:
mm-vma-add-give_up_on_oom-option-on-modify-merge-use-in-uffd-release.patch

queue-6.12/mm-vma-add-give_up_on_oom-option-on-modify-merge-use-in-uffd-release.patch [new file with mode: 0644]
queue-6.12/series

diff --git a/queue-6.12/mm-vma-add-give_up_on_oom-option-on-modify-merge-use-in-uffd-release.patch b/queue-6.12/mm-vma-add-give_up_on_oom-option-on-modify-merge-use-in-uffd-release.patch
new file mode 100644 (file)
index 0000000..cb00881
--- /dev/null
@@ -0,0 +1,199 @@
+From 41e6ddcaa0f18dda4c3fadf22533775a30d6f72f Mon Sep 17 00:00:00 2001
+From: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
+Date: Fri, 21 Mar 2025 10:09:37 +0000
+Subject: mm/vma: add give_up_on_oom option on modify/merge, use in uffd release
+
+From: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
+
+commit 41e6ddcaa0f18dda4c3fadf22533775a30d6f72f upstream.
+
+Currently, if a VMA merge fails due to an OOM condition arising on commit
+merge or a failure to duplicate anon_vma's, we report this so the caller
+can handle it.
+
+However there are cases where the caller is only ostensibly trying a
+merge, and doesn't mind if it fails due to this condition.
+
+Since we do not want to introduce an implicit assumption that we only
+actually modify VMAs after OOM conditions might arise, add a 'give up on
+oom' option and make an explicit contract that, should this flag be set, we
+absolutely will not modify any VMAs should OOM arise and just bail out.
+
+Since it'd be very unusual for a user to try to vma_modify() with this flag
+set but be specifying a range within a VMA which ends up being split (which
+can fail due to rlimit issues, not only OOM), we add a debug warning for
+this condition.
+
+The motivating reason for this is uffd release - syzkaller (and Pedro
+Falcato's VERY astute analysis) found a way in which an injected fault on
+allocation, triggering an OOM condition on commit merge, would result in
+uffd code becoming confused and treating an error value as if it were a VMA
+pointer.
+
+To avoid this, we make use of this new VMG flag to ensure that this never
+occurs, utilising the fact that, should we be clearing entire VMAs, we do
+not wish an OOM event to be reported to us.
+
+Many thanks to Pedro Falcato for his excellent analysis and Jann Horn for
+his insightful and intelligent analysis of the situation, both of whom were
+instrumental in this fix.
+
+Link: https://lkml.kernel.org/r/20250321100937.46634-1-lorenzo.stoakes@oracle.com
+Reported-by: syzbot+20ed41006cf9d842c2b5@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/all/67dc67f0.050a0220.25ae54.001e.GAE@google.com/
+Fixes: 47b16d0462a4 ("mm: abort vma_modify() on merge out of memory failure")
+Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
+Suggested-by: Pedro Falcato <pfalcato@suse.de>
+Suggested-by: Jann Horn <jannh@google.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/userfaultfd.c |   13 +++++++++++--
+ mm/vma.c         |   38 ++++++++++++++++++++++++++++++++++----
+ mm/vma.h         |    9 ++++++++-
+ 3 files changed, 53 insertions(+), 7 deletions(-)
+
+--- a/mm/userfaultfd.c
++++ b/mm/userfaultfd.c
+@@ -1873,6 +1873,14 @@ struct vm_area_struct *userfaultfd_clear
+                                            unsigned long end)
+ {
+       struct vm_area_struct *ret;
++      bool give_up_on_oom = false;
++
++      /*
++       * If we are modifying only and not splitting, just give up on the merge
++       * if OOM prevents us from merging successfully.
++       */
++      if (start == vma->vm_start && end == vma->vm_end)
++              give_up_on_oom = true;
+       /* Reset ptes for the whole vma range if wr-protected */
+       if (userfaultfd_wp(vma))
+@@ -1880,7 +1888,7 @@ struct vm_area_struct *userfaultfd_clear
+       ret = vma_modify_flags_uffd(vmi, prev, vma, start, end,
+                                   vma->vm_flags & ~__VM_UFFD_FLAGS,
+-                                  NULL_VM_UFFD_CTX);
++                                  NULL_VM_UFFD_CTX, give_up_on_oom);
+       /*
+        * In the vma_merge() successful mprotect-like case 8:
+@@ -1931,7 +1939,8 @@ int userfaultfd_register_range(struct us
+               new_flags = (vma->vm_flags & ~__VM_UFFD_FLAGS) | vm_flags;
+               vma = vma_modify_flags_uffd(&vmi, prev, vma, start, vma_end,
+                                           new_flags,
+-                                          (struct vm_userfaultfd_ctx){ctx});
++                                          (struct vm_userfaultfd_ctx){ctx},
++                                          /* give_up_on_oom = */false);
+               if (IS_ERR(vma))
+                       return PTR_ERR(vma);
+--- a/mm/vma.c
++++ b/mm/vma.c
+@@ -846,7 +846,13 @@ static struct vm_area_struct *vma_merge_
+               if (anon_dup)
+                       unlink_anon_vmas(anon_dup);
+-              vmg->state = VMA_MERGE_ERROR_NOMEM;
++              /*
++               * We've cleaned up any cloned anon_vma's, no VMAs have been
++               * modified, no harm no foul if the user requests that we not
++               * report this and just give up, leaving the VMAs unmerged.
++               */
++              if (!vmg->give_up_on_oom)
++                      vmg->state = VMA_MERGE_ERROR_NOMEM;
+               return NULL;
+       }
+@@ -859,7 +865,15 @@ static struct vm_area_struct *vma_merge_
+ abort:
+       vma_iter_set(vmg->vmi, start);
+       vma_iter_load(vmg->vmi);
+-      vmg->state = VMA_MERGE_ERROR_NOMEM;
++
++      /*
++       * This means we have failed to clone anon_vma's correctly, but no
++       * actual changes to VMAs have occurred, so no harm no foul - if the
++       * user doesn't want this reported and instead just wants to give up on
++       * the merge, allow it.
++       */
++      if (!vmg->give_up_on_oom)
++              vmg->state = VMA_MERGE_ERROR_NOMEM;
+       return NULL;
+ }
+@@ -1033,9 +1047,15 @@ int vma_expand(struct vma_merge_struct *
+       return 0;
+ nomem:
+-      vmg->state = VMA_MERGE_ERROR_NOMEM;
+       if (anon_dup)
+               unlink_anon_vmas(anon_dup);
++      /*
++       * If the user requests that we just give upon OOM, we are safe to do so
++       * here, as commit merge provides this contract to us. Nothing has been
++       * changed - no harm no foul, just don't report it.
++       */
++      if (!vmg->give_up_on_oom)
++              vmg->state = VMA_MERGE_ERROR_NOMEM;
+       return -ENOMEM;
+ }
+@@ -1428,6 +1448,13 @@ static struct vm_area_struct *vma_modify
+       if (vmg_nomem(vmg))
+               return ERR_PTR(-ENOMEM);
++      /*
++       * Split can fail for reasons other than OOM, so if the user requests
++       * this it's probably a mistake.
++       */
++      VM_WARN_ON(vmg->give_up_on_oom &&
++                 (vma->vm_start != start || vma->vm_end != end));
++
+       /* Split any preceding portion of the VMA. */
+       if (vma->vm_start < start) {
+               int err = split_vma(vmg->vmi, vma, start, 1);
+@@ -1496,12 +1523,15 @@ struct vm_area_struct
+                      struct vm_area_struct *vma,
+                      unsigned long start, unsigned long end,
+                      unsigned long new_flags,
+-                     struct vm_userfaultfd_ctx new_ctx)
++                     struct vm_userfaultfd_ctx new_ctx,
++                     bool give_up_on_oom)
+ {
+       VMG_VMA_STATE(vmg, vmi, prev, vma, start, end);
+       vmg.flags = new_flags;
+       vmg.uffd_ctx = new_ctx;
++      if (give_up_on_oom)
++              vmg.give_up_on_oom = true;
+       return vma_modify(&vmg);
+ }
+--- a/mm/vma.h
++++ b/mm/vma.h
+@@ -87,6 +87,12 @@ struct vma_merge_struct {
+       struct anon_vma_name *anon_name;
+       enum vma_merge_flags merge_flags;
+       enum vma_merge_state state;
++
++      /*
++       * If a merge is possible, but an OOM error occurs, give up and don't
++       * execute the merge, returning NULL.
++       */
++      bool give_up_on_oom :1;
+ };
+ static inline bool vmg_nomem(struct vma_merge_struct *vmg)
+@@ -303,7 +309,8 @@ struct vm_area_struct
+                      struct vm_area_struct *vma,
+                      unsigned long start, unsigned long end,
+                      unsigned long new_flags,
+-                     struct vm_userfaultfd_ctx new_ctx);
++                     struct vm_userfaultfd_ctx new_ctx,
++                     bool give_up_on_oom);
+ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg);
index c223fe2bab73da3bbd66a95f2c49abdcf3a90110..e32a3a9e4d4507d9924764ecea0b6776e9eb1eda 100644 (file)
@@ -206,3 +206,4 @@ nvmet-fc-remove-unused-functions.patch
 block-remove-rq_list_move.patch
 block-add-a-rq_list-type.patch
 block-don-t-reorder-requests-in-blk_add_rq_to_plug.patch
+mm-vma-add-give_up_on_oom-option-on-modify-merge-use-in-uffd-release.patch