]> git.itanic.dy.fi Git - linux-stable/commitdiff
mm/userfaultfd: allow hugetlb change protection upon poison entry
authorPeter Xu <peterx@redhat.com>
Fri, 5 Apr 2024 23:19:20 +0000 (19:19 -0400)
committerAndrew Morton <akpm@linux-foundation.org>
Tue, 16 Apr 2024 22:39:50 +0000 (15:39 -0700)
After UFFDIO_POISON, there can be two kinds of hugetlb pte markers, either
the POISON one or UFFD_WP one.

Allow change protection to run on a poisoned marker just like !hugetlb
cases, ignoring the marker irrelevant of the permission.

Here the two bits are mutual exclusive.  For example, when install a
poisoned entry it must not be UFFD_WP already (by checking pte_none()
before such install).  And it also means if UFFD_WP is set there must have
no POISON bit set.  It makes sense because UFFD_WP is a bit to reflect
permission, and permissions do not apply if the pte is poisoned and
destined to sigbus.

So here we simply check uffd_wp bit set first, do nothing otherwise.

Attach the Fixes to UFFDIO_POISON work, as before that it should not be
possible to have poison entry for hugetlb (e.g., hugetlb doesn't do swap,
so no chance of swapin errors).

Link: https://lkml.kernel.org/r/20240405231920.1772199-1-peterx@redhat.com
Link: https://lore.kernel.org/r/000000000000920d5e0615602dd1@google.com
Fixes: fc71884a5f59 ("mm: userfaultfd: add new UFFDIO_POISON ioctl")
Signed-off-by: Peter Xu <peterx@redhat.com>
Reported-by: syzbot+b07c8ac8eee3d4d8440f@syzkaller.appspotmail.com
Reviewed-by: David Hildenbrand <david@redhat.com>
Reviewed-by: Axel Rasmussen <axelrasmussen@google.com>
Cc: <stable@vger.kernel.org> [6.6+]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
mm/hugetlb.c

index 23ef240ba48a60a77102f7bf1beb2e76a987486d..31d00eee028f1179b99405f0fdd3461b16e17d2e 100644 (file)
@@ -7044,9 +7044,13 @@ long hugetlb_change_protection(struct vm_area_struct *vma,
                        if (!pte_same(pte, newpte))
                                set_huge_pte_at(mm, address, ptep, newpte, psize);
                } else if (unlikely(is_pte_marker(pte))) {
-                       /* No other markers apply for now. */
-                       WARN_ON_ONCE(!pte_marker_uffd_wp(pte));
-                       if (uffd_wp_resolve)
+                       /*
+                        * Do nothing on a poison marker; page is
+                        * corrupted, permissons do not apply.  Here
+                        * pte_marker_uffd_wp()==true implies !poison
+                        * because they're mutual exclusive.
+                        */
+                       if (pte_marker_uffd_wp(pte) && uffd_wp_resolve)
                                /* Safe to modify directly (non-present->none). */
                                huge_pte_clear(mm, address, ptep, psize);
                } else if (!huge_pte_none(pte)) {