diff mbox series

[2/9] hugetlb/userfaultfd: Forbid huge pmd sharing when uffd enabled

Message ID 20210115190451.3135416-3-axelrasmussen@google.com (mailing list archive)
State New, archived
Headers show
Series userfaultfd: add minor fault handling | expand

Commit Message

Axel Rasmussen Jan. 15, 2021, 7:04 p.m. UTC
From: Peter Xu <peterx@redhat.com>

Huge pmd sharing could bring problem to userfaultfd.  The thing is that
userfaultfd is running its logic based on the special bits on page table
entries, however the huge pmd sharing could potentially share page table
entries for different address ranges.  That could cause issues on either:

  - When sharing huge pmd page tables for an uffd write protected range, the
    newly mapped huge pmd range will also be write protected unexpectedly, or,

  - When we try to write protect a range of huge pmd shared range, we'll first
    do huge_pmd_unshare() in hugetlb_change_protection(), however that also
    means the UFFDIO_WRITEPROTECT could be silently skipped for the shared
    region, which could lead to data loss.

Since at it, a few other things are done altogether:

  - Move want_pmd_share() from mm/hugetlb.c into linux/hugetlb.h, because
    that's definitely something that arch code would like to use too

  - ARM64 currently directly check against CONFIG_ARCH_WANT_HUGE_PMD_SHARE when
    trying to share huge pmd.  Switch to the want_pmd_share() helper.

Signed-off-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Axel Rasmussen <axelrasmussen@google.com>
---
 arch/arm64/mm/hugetlbpage.c   |  3 +--
 include/linux/hugetlb.h       | 12 ++++++++++++
 include/linux/userfaultfd_k.h |  9 +++++++++
 mm/hugetlb.c                  |  5 ++---
 4 files changed, 24 insertions(+), 5 deletions(-)

Comments

kernel test robot Jan. 15, 2021, 11:36 p.m. UTC | #1
Hi Axel,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on arm64/for-next/core]
[also build test ERROR on powerpc/next s390/features tip/perf/core linus/master v5.11-rc3 next-20210115]
[cannot apply to hp-parisc/for-next hnaz-linux-mm/master ia64/next sparc/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Axel-Rasmussen/userfaultfd-add-minor-fault-handling/20210116-030900
base:   https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-next/core
config: i386-randconfig-r013-20210115 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0
reproduce (this is a W=1 build):
        # https://github.com/0day-ci/linux/commit/97d3dcca13a550a56e5afe40abcc8db929bd3916
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Axel-Rasmussen/userfaultfd-add-minor-fault-handling/20210116-030900
        git checkout 97d3dcca13a550a56e5afe40abcc8db929bd3916
        # save the attached .config to linux build tree
        make W=1 ARCH=i386 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   In file included from include/linux/migrate.h:8,
                    from fs/f2fs/data.c:3857:
   include/linux/hugetlb.h: In function 'want_pmd_share':
>> include/linux/hugetlb.h:954:6: error: implicit declaration of function 'uffd_disable_huge_pmd_share' [-Werror=implicit-function-declaration]
     954 |  if (uffd_disable_huge_pmd_share(vma))
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors
--
   In file included from mm/filemap.c:36:
   include/linux/hugetlb.h: In function 'want_pmd_share':
>> include/linux/hugetlb.h:954:6: error: implicit declaration of function 'uffd_disable_huge_pmd_share' [-Werror=implicit-function-declaration]
     954 |  if (uffd_disable_huge_pmd_share(vma))
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   mm/filemap.c: At top level:
   mm/filemap.c:830:14: warning: no previous prototype for '__add_to_page_cache_locked' [-Wmissing-prototypes]
     830 | noinline int __add_to_page_cache_locked(struct page *page,
         |              ^~~~~~~~~~~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors
--
   In file included from mm/util.c:16:
   include/linux/hugetlb.h: In function 'want_pmd_share':
>> include/linux/hugetlb.h:954:6: error: implicit declaration of function 'uffd_disable_huge_pmd_share' [-Werror=implicit-function-declaration]
     954 |  if (uffd_disable_huge_pmd_share(vma))
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   mm/util.c: In function 'page_mapping':
   mm/util.c:700:15: warning: variable 'entry' set but not used [-Wunused-but-set-variable]
     700 |   swp_entry_t entry;
         |               ^~~~~
   cc1: some warnings being treated as errors
--
   In file included from include/linux/migrate.h:8,
                    from mm/page_alloc.c:61:
   include/linux/hugetlb.h: In function 'want_pmd_share':
>> include/linux/hugetlb.h:954:6: error: implicit declaration of function 'uffd_disable_huge_pmd_share' [-Werror=implicit-function-declaration]
     954 |  if (uffd_disable_huge_pmd_share(vma))
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   mm/page_alloc.c: At top level:
   mm/page_alloc.c:3594:15: warning: no previous prototype for 'should_fail_alloc_page' [-Wmissing-prototypes]
    3594 | noinline bool should_fail_alloc_page(gfp_t gfp_mask, unsigned int order)
         |               ^~~~~~~~~~~~~~~~~~~~~~
   mm/page_alloc.c:6255:23: warning: no previous prototype for 'memmap_init' [-Wmissing-prototypes]
    6255 | void __meminit __weak memmap_init(unsigned long size, int nid,
         |                       ^~~~~~~~~~~
   cc1: some warnings being treated as errors
--
   In file included from kernel/events/core.c:31:
   include/linux/hugetlb.h: In function 'want_pmd_share':
>> include/linux/hugetlb.h:954:6: error: implicit declaration of function 'uffd_disable_huge_pmd_share' [-Werror=implicit-function-declaration]
     954 |  if (uffd_disable_huge_pmd_share(vma))
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   kernel/events/core.c: At top level:
   kernel/events/core.c:6535:6: warning: no previous prototype for 'perf_pmu_snapshot_aux' [-Wmissing-prototypes]
    6535 | long perf_pmu_snapshot_aux(struct perf_buffer *rb,
         |      ^~~~~~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors
--
   In file included from include/linux/migrate.h:8,
                    from kernel/sched/sched.h:53,
                    from kernel/sched/core.c:13:
   include/linux/hugetlb.h: In function 'want_pmd_share':
>> include/linux/hugetlb.h:954:6: error: implicit declaration of function 'uffd_disable_huge_pmd_share' [-Werror=implicit-function-declaration]
     954 |  if (uffd_disable_huge_pmd_share(vma))
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   kernel/sched/core.c: In function 'schedule_tail':
   kernel/sched/core.c:4238:13: warning: variable 'rq' set but not used [-Wunused-but-set-variable]
    4238 |  struct rq *rq;
         |             ^~
   cc1: some warnings being treated as errors
--
   In file included from include/linux/migrate.h:8,
                    from kernel/sched/sched.h:53,
                    from kernel/sched/rt.c:6:
   include/linux/hugetlb.h: In function 'want_pmd_share':
>> include/linux/hugetlb.h:954:6: error: implicit declaration of function 'uffd_disable_huge_pmd_share' [-Werror=implicit-function-declaration]
     954 |  if (uffd_disable_huge_pmd_share(vma))
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   kernel/sched/rt.c: At top level:
   kernel/sched/rt.c:669:6: warning: no previous prototype for 'sched_rt_bandwidth_account' [-Wmissing-prototypes]
     669 | bool sched_rt_bandwidth_account(struct rt_rq *rt_rq)
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors


vim +/uffd_disable_huge_pmd_share +954 include/linux/hugetlb.h

   950	
   951	static inline bool want_pmd_share(struct vm_area_struct *vma)
   952	{
   953	#ifdef CONFIG_ARCH_WANT_HUGE_PMD_SHARE
 > 954		if (uffd_disable_huge_pmd_share(vma))
   955			return false;
   956		return true;
   957	#else
   958		return false;
   959	#endif
   960	}
   961	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Peter Xu Jan. 21, 2021, 6:52 p.m. UTC | #2
On Fri, Jan 15, 2021 at 11:04:44AM -0800, Axel Rasmussen wrote:

[...]

> @@ -947,4 +948,15 @@ static inline __init void hugetlb_cma_check(void)
>  }
>  #endif
>  
> +static inline bool want_pmd_share(struct vm_area_struct *vma)
> +{
> +#ifdef CONFIG_ARCH_WANT_HUGE_PMD_SHARE
> +	if (uffd_disable_huge_pmd_share(vma))
> +		return false;
> +	return true;
> +#else
> +	return false;
> +#endif
> +}

I got syzbot complains about this chunk when compile some other archs outside
x86, probably because CONFIG_ARCH_WANT_HUGE_PMD_SHARE can be defined without
CONFIG_USERFAULTFD.  I don't know why it didn't complain here, so just a fyi
that I've pushed some new version to the online repo so that they should have
been fixed.  For example, for this patch:

https://github.com/xzpeter/linux/commit/0fd01db2c9ac3ab8600f3f7df23cf28fddcfee1b

static inline bool want_pmd_share(struct vm_area_struct *vma)
{
#ifdef CONFIG_USERFAULTFD
	if (uffd_disable_huge_pmd_share(vma))
		return false;
#endif

#ifdef CONFIG_ARCH_WANT_HUGE_PMD_SHARE
	return true;
#else
	return false;
#endif
}

Thanks,
diff mbox series

Patch

diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
index 5b32ec888698..1a8ce0facfe8 100644
--- a/arch/arm64/mm/hugetlbpage.c
+++ b/arch/arm64/mm/hugetlbpage.c
@@ -284,8 +284,7 @@  pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
 		 */
 		ptep = pte_alloc_map(mm, pmdp, addr);
 	} else if (sz == PMD_SIZE) {
-		if (IS_ENABLED(CONFIG_ARCH_WANT_HUGE_PMD_SHARE) &&
-		    pud_none(READ_ONCE(*pudp)))
+		if (want_pmd_share(vma) && pud_none(READ_ONCE(*pudp)))
 			ptep = huge_pmd_share(mm, addr, pudp);
 		else
 			ptep = (pte_t *)pmd_alloc(mm, pudp, addr);
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 1e0abb609976..4959e94e78b1 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -11,6 +11,7 @@ 
 #include <linux/kref.h>
 #include <linux/pgtable.h>
 #include <linux/gfp.h>
+#include <linux/userfaultfd_k.h>
 
 struct ctl_table;
 struct user_struct;
@@ -947,4 +948,15 @@  static inline __init void hugetlb_cma_check(void)
 }
 #endif
 
+static inline bool want_pmd_share(struct vm_area_struct *vma)
+{
+#ifdef CONFIG_ARCH_WANT_HUGE_PMD_SHARE
+	if (uffd_disable_huge_pmd_share(vma))
+		return false;
+	return true;
+#else
+	return false;
+#endif
+}
+
 #endif /* _LINUX_HUGETLB_H */
diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h
index a8e5f3ea9bb2..c63ccdae3eab 100644
--- a/include/linux/userfaultfd_k.h
+++ b/include/linux/userfaultfd_k.h
@@ -52,6 +52,15 @@  static inline bool is_mergeable_vm_userfaultfd_ctx(struct vm_area_struct *vma,
 	return vma->vm_userfaultfd_ctx.ctx == vm_ctx.ctx;
 }
 
+/*
+ * Never enable huge pmd sharing on uffd-wp registered vmas, because uffd-wp
+ * protect information is per pgtable entry.
+ */
+static inline bool uffd_disable_huge_pmd_share(struct vm_area_struct *vma)
+{
+	return vma->vm_flags & VM_UFFD_WP;
+}
+
 static inline bool userfaultfd_missing(struct vm_area_struct *vma)
 {
 	return vma->vm_flags & VM_UFFD_MISSING;
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 16a8d5ac68c0..1ad91d94cbe2 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -5371,7 +5371,7 @@  int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma,
 	*addr = ALIGN(*addr, HPAGE_SIZE * PTRS_PER_PTE) - HPAGE_SIZE;
 	return 1;
 }
-#define want_pmd_share()	(1)
+
 #else /* !CONFIG_ARCH_WANT_HUGE_PMD_SHARE */
 pte_t *huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud)
 {
@@ -5388,7 +5388,6 @@  void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma,
 				unsigned long *start, unsigned long *end)
 {
 }
-#define want_pmd_share()	(0)
 #endif /* CONFIG_ARCH_WANT_HUGE_PMD_SHARE */
 
 #ifdef CONFIG_ARCH_WANT_GENERAL_HUGETLB
@@ -5410,7 +5409,7 @@  pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
 			pte = (pte_t *)pud;
 		} else {
 			BUG_ON(sz != PMD_SIZE);
-			if (want_pmd_share() && pud_none(*pud))
+			if (want_pmd_share(vma) && pud_none(*pud))
 				pte = huge_pmd_share(mm, addr, pud);
 			else
 				pte = (pte_t *)pmd_alloc(mm, pud, addr);