From: Greg Kroah-Hartman Date: Tue, 22 Apr 2025 12:51:25 +0000 (+0200) Subject: 6.12-stable patches X-Git-Tag: v6.1.135~38 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=71812f8647bb574b1ae6255942ccbe808fbde575;p=thirdparty%2Fkernel%2Fstable-queue.git 6.12-stable patches added patches: mm-vma-add-give_up_on_oom-option-on-modify-merge-use-in-uffd-release.patch --- 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 index 0000000000..cb0088109e --- /dev/null +++ b/queue-6.12/mm-vma-add-give_up_on_oom-option-on-modify-merge-use-in-uffd-release.patch @@ -0,0 +1,199 @@ +From 41e6ddcaa0f18dda4c3fadf22533775a30d6f72f Mon Sep 17 00:00:00 2001 +From: Lorenzo Stoakes +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 + +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 +Suggested-by: Pedro Falcato +Suggested-by: Jann Horn +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + 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); + diff --git a/queue-6.12/series b/queue-6.12/series index c223fe2bab..e32a3a9e4d 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -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