]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 4 Aug 2023 10:27:44 +0000 (12:27 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 4 Aug 2023 10:27:44 +0000 (12:27 +0200)
added patches:
mm-lock_vma_under_rcu-must-check-vma-anon_vma-under-vma-lock.patch

queue-6.4/mm-lock_vma_under_rcu-must-check-vma-anon_vma-under-vma-lock.patch [new file with mode: 0644]
queue-6.4/series

diff --git a/queue-6.4/mm-lock_vma_under_rcu-must-check-vma-anon_vma-under-vma-lock.patch b/queue-6.4/mm-lock_vma_under_rcu-must-check-vma-anon_vma-under-vma-lock.patch
new file mode 100644 (file)
index 0000000..fd531bc
--- /dev/null
@@ -0,0 +1,116 @@
+From 657b5146955eba331e01b9a6ae89ce2e716ba306 Mon Sep 17 00:00:00 2001
+From: Jann Horn <jannh@google.com>
+Date: Wed, 26 Jul 2023 23:41:03 +0200
+Subject: mm: lock_vma_under_rcu() must check vma->anon_vma under vma lock
+
+From: Jann Horn <jannh@google.com>
+
+commit 657b5146955eba331e01b9a6ae89ce2e716ba306 upstream.
+
+lock_vma_under_rcu() tries to guarantee that __anon_vma_prepare() can't
+be called in the VMA-locked page fault path by ensuring that
+vma->anon_vma is set.
+
+However, this check happens before the VMA is locked, which means a
+concurrent move_vma() can concurrently call unlink_anon_vmas(), which
+disassociates the VMA's anon_vma.
+
+This means we can get UAF in the following scenario:
+
+  THREAD 1                   THREAD 2
+  ========                   ========
+  <page fault>
+    lock_vma_under_rcu()
+      rcu_read_lock()
+      mas_walk()
+      check vma->anon_vma
+
+                             mremap() syscall
+                               move_vma()
+                                vma_start_write()
+                                 unlink_anon_vmas()
+                             <syscall end>
+
+    handle_mm_fault()
+      __handle_mm_fault()
+        handle_pte_fault()
+          do_pte_missing()
+            do_anonymous_page()
+              anon_vma_prepare()
+                __anon_vma_prepare()
+                  find_mergeable_anon_vma()
+                    mas_walk() [looks up VMA X]
+
+                             munmap() syscall (deletes VMA X)
+
+                    reusable_anon_vma() [called on freed VMA X]
+
+This is a security bug if you can hit it, although an attacker would
+have to win two races at once where the first race window is only a few
+instructions wide.
+
+This patch is based on some previous discussion with Linus Torvalds on
+the security list.
+
+Cc: stable@vger.kernel.org
+Fixes: 5e31275cc997 ("mm: add per-VMA lock and helper functions to control it")
+Signed-off-by: Jann Horn <jannh@google.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/memory.c |   28 ++++++++++++++++------------
+ 1 file changed, 16 insertions(+), 12 deletions(-)
+
+--- a/mm/memory.c
++++ b/mm/memory.c
+@@ -5410,27 +5410,28 @@ retry:
+       if (!vma_is_anonymous(vma))
+               goto inval;
+-      /* find_mergeable_anon_vma uses adjacent vmas which are not locked */
+-      if (!vma->anon_vma)
+-              goto inval;
+-
+       if (!vma_start_read(vma))
+               goto inval;
+       /*
++       * find_mergeable_anon_vma uses adjacent vmas which are not locked.
++       * This check must happen after vma_start_read(); otherwise, a
++       * concurrent mremap() with MREMAP_DONTUNMAP could dissociate the VMA
++       * from its anon_vma.
++       */
++      if (unlikely(!vma->anon_vma))
++              goto inval_end_read;
++
++      /*
+        * Due to the possibility of userfault handler dropping mmap_lock, avoid
+        * it for now and fall back to page fault handling under mmap_lock.
+        */
+-      if (userfaultfd_armed(vma)) {
+-              vma_end_read(vma);
+-              goto inval;
+-      }
++      if (userfaultfd_armed(vma))
++              goto inval_end_read;
+       /* Check since vm_start/vm_end might change before we lock the VMA */
+-      if (unlikely(address < vma->vm_start || address >= vma->vm_end)) {
+-              vma_end_read(vma);
+-              goto inval;
+-      }
++      if (unlikely(address < vma->vm_start || address >= vma->vm_end))
++              goto inval_end_read;
+       /* Check if the VMA got isolated after we found it */
+       if (vma->detached) {
+@@ -5442,6 +5443,9 @@ retry:
+       rcu_read_unlock();
+       return vma;
++
++inval_end_read:
++      vma_end_read(vma);
+ inval:
+       rcu_read_unlock();
+       count_vm_vma_lock_event(VMA_LOCK_ABORT);
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4d1f0c015e4719054b97446a87ed4728237385e0 100644 (file)
@@ -0,0 +1 @@
+mm-lock_vma_under_rcu-must-check-vma-anon_vma-under-vma-lock.patch