]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
hugetlb: fix copy_huge_page_from_user contig page struct assumption
authorMike Kravetz <mike.kravetz@oracle.com>
Wed, 24 Feb 2021 20:07:54 +0000 (12:07 -0800)
committerKleber Sacilotto de Souza <kleber.souza@canonical.com>
Wed, 14 Apr 2021 16:31:40 +0000 (18:31 +0200)
BugLink: https://bugs.launchpad.net/bugs/1918974
commit 3272cfc2525b3a2810a59312d7a1e6f04a0ca3ef upstream.

page structs are not guaranteed to be contiguous for gigantic pages.  The
routine copy_huge_page_from_user can encounter gigantic pages, yet it
assumes page structs are contiguous when copying pages from user space.

Since page structs for the target gigantic page are not contiguous, the
data copied from user space could overwrite other pages not associated
with the gigantic page and cause data corruption.

Non-contiguous page structs are generally not an issue.  However, they can
exist with a specific kernel configuration and hotplug operations.  For
example: Configure the kernel with CONFIG_SPARSEMEM and
!CONFIG_SPARSEMEM_VMEMMAP.  Then, hotplug add memory for the area where
the gigantic page will be allocated.

Link: https://lkml.kernel.org/r/20210217184926.33567-2-mike.kravetz@oracle.com
Fixes: 8fb5debc5fcd ("userfaultfd: hugetlbfs: add hugetlb_mcopy_atomic_pte for userfaultfd support")
Signed-off-by: Mike Kravetz <mike.kravetz@oracle.com>
Cc: Zi Yan <ziy@nvidia.com>
Cc: Davidlohr Bueso <dbueso@suse.de>
Cc: "Kirill A . Shutemov" <kirill.shutemov@linux.intel.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Joao Martins <joao.m.martins@oracle.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Kelsey Skunberg <kelsey.skunberg@canonical.com>
mm/memory.c

index 84afd3d08c055f4bcfdc775215d49e0d6d8b9112..0e7cc626933d663760cabb520957ca666f0629c9 100644 (file)
@@ -4719,17 +4719,19 @@ long copy_huge_page_from_user(struct page *dst_page,
        void *page_kaddr;
        unsigned long i, rc = 0;
        unsigned long ret_val = pages_per_huge_page * PAGE_SIZE;
+       struct page *subpage = dst_page;
 
-       for (i = 0; i < pages_per_huge_page; i++) {
+       for (i = 0; i < pages_per_huge_page;
+            i++, subpage = mem_map_next(subpage, dst_page, i)) {
                if (allow_pagefault)
-                       page_kaddr = kmap(dst_page + i);
+                       page_kaddr = kmap(subpage);
                else
-                       page_kaddr = kmap_atomic(dst_page + i);
+                       page_kaddr = kmap_atomic(subpage);
                rc = copy_from_user(page_kaddr,
                                (const void __user *)(src + i * PAGE_SIZE),
                                PAGE_SIZE);
                if (allow_pagefault)
-                       kunmap(dst_page + i);
+                       kunmap(subpage);
                else
                        kunmap_atomic(page_kaddr);