]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
mm: Add unsafe_follow_pfn
authorDaniel Vetter <daniel.vetter@ffwll.ch>
Tue, 16 Mar 2021 15:33:01 +0000 (16:33 +0100)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 8 Apr 2021 14:54:28 +0000 (16:54 +0200)
Way back it was a reasonable assumptions that iomem mappings never
change the pfn range they point at. But this has changed:

- gpu drivers dynamically manage their memory nowadays, invalidating
ptes with unmap_mapping_range when buffers get moved

- contiguous dma allocations have moved from dedicated carvetouts to
cma regions. This means if we miss the unmap the pfn might contain
pagecache or anon memory (well anything allocated with GFP_MOVEABLE)

- even /dev/mem now invalidates mappings when the kernel requests that
iomem region when CONFIG_IO_STRICT_DEVMEM is set, see 3234ac664a87
("/dev/mem: Revoke mappings when a driver claims the region")

Accessing pfns obtained from ptes without holding all the locks is
therefore no longer a good idea.

Unfortunately there's some users where this is not fixable (like v4l
userptr of iomem mappings) or involves a pile of work (vfio type1
iommu). For now annotate these as unsafe and splat appropriately.

This patch adds an unsafe_follow_pfn, which later patches will then
roll out to all appropriate places.

Also mark up follow_pfn as EXPORT_SYMBOL_GPL. The only safe way to use
that by drivers/modules is together with an mmu_notifier, and that's
all _GPL stuff.

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Kees Cook <keescook@chromium.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Jérôme Glisse <jglisse@redhat.com>
Cc: Jan Kara <jack@suse.cz>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: linux-mm@kvack.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-samsung-soc@vger.kernel.org
Cc: linux-media@vger.kernel.org
Cc: kvm@vger.kernel.org
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20210316153303.3216674-2-daniel.vetter@ffwll.ch
include/linux/mm.h
mm/memory.c
mm/nommu.c
security/Kconfig

index 64a71bf2053674c32322555b5ad32821acdbc6ca..caec8b25d66f18aa91af60d81675b8aa13c4217e 100644 (file)
@@ -1695,6 +1695,8 @@ int follow_pte(struct mm_struct *mm, unsigned long address,
               pte_t **ptepp, spinlock_t **ptlp);
 int follow_pfn(struct vm_area_struct *vma, unsigned long address,
        unsigned long *pfn);
+int unsafe_follow_pfn(struct vm_area_struct *vma, unsigned long address,
+                     unsigned long *pfn);
 int follow_phys(struct vm_area_struct *vma, unsigned long address,
                unsigned int flags, unsigned long *prot, resource_size_t *phys);
 int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
index 5efa07fb6cdc18c9189fc94a995716744218454a..e8a145505b69360c08a03b28bb3768837f6a73cf 100644 (file)
@@ -4741,7 +4741,12 @@ EXPORT_SYMBOL_GPL(follow_pte);
  * @address: user virtual address
  * @pfn: location to store found PFN
  *
- * Only IO mappings and raw PFN mappings are allowed.
+ * Only IO mappings and raw PFN mappings are allowed. Note that callers must
+ * ensure coherency with pte updates by using a &mmu_notifier to follow updates.
+ * If this is not feasible, or the access to the @pfn is only very short term,
+ * use follow_pte_pmd() instead and hold the pagetable lock for the duration of
+ * the access instead. Any caller not following these requirements must use
+ * unsafe_follow_pfn() instead.
  *
  * This function does not allow the caller to read the permissions
  * of the PTE.  Do not use it.
@@ -4765,7 +4770,32 @@ int follow_pfn(struct vm_area_struct *vma, unsigned long address,
        pte_unmap_unlock(ptep, ptl);
        return 0;
 }
-EXPORT_SYMBOL(follow_pfn);
+EXPORT_SYMBOL_GPL(follow_pfn);
+
+/**
+ * unsafe_follow_pfn - look up PFN at a user virtual address
+ * @vma: memory mapping
+ * @address: user virtual address
+ * @pfn: location to store found PFN
+ *
+ * Only IO mappings and raw PFN mappings are allowed.
+ *
+ * Returns zero and the pfn at @pfn on success, -ve otherwise.
+ */
+int unsafe_follow_pfn(struct vm_area_struct *vma, unsigned long address,
+                     unsigned long *pfn)
+{
+       if (IS_ENABLED(CONFIG_STRICT_FOLLOW_PFN)) {
+               pr_info("unsafe follow_pfn usage rejected, see CONFIG_STRICT_FOLLOW_PFN\n");
+               return -EINVAL;
+       }
+
+       WARN_ONCE(1, "unsafe follow_pfn usage\n");
+       add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+
+       return follow_pfn(vma, address, pfn);
+}
+EXPORT_SYMBOL(unsafe_follow_pfn);
 
 #ifdef CONFIG_HAVE_IOREMAP_PROT
 int follow_phys(struct vm_area_struct *vma,
index 5c9ab799c0e6395877cb781d8eb88493641f81d2..1dc983f50e2cdd1047fac7d7dd7ededf6e2eb72c 100644 (file)
@@ -130,7 +130,32 @@ int follow_pfn(struct vm_area_struct *vma, unsigned long address,
        *pfn = address >> PAGE_SHIFT;
        return 0;
 }
-EXPORT_SYMBOL(follow_pfn);
+EXPORT_SYMBOL_GPL(follow_pfn);
+
+/**
+ * unsafe_follow_pfn - look up PFN at a user virtual address
+ * @vma: memory mapping
+ * @address: user virtual address
+ * @pfn: location to store found PFN
+ *
+ * Only IO mappings and raw PFN mappings are allowed.
+ *
+ * Returns zero and the pfn at @pfn on success, -ve otherwise.
+ */
+int unsafe_follow_pfn(struct vm_area_struct *vma, unsigned long address,
+                     unsigned long *pfn)
+{
+       if (IS_ENABLED(CONFIG_STRICT_FOLLOW_PFN)) {
+               pr_info("unsafe follow_pfn usage rejected, see CONFIG_STRICT_FOLLOW_PFN\n");
+               return -EINVAL;
+       }
+
+       WARN_ONCE(1, "unsafe follow_pfn usage\n");
+       add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+
+       return follow_pfn(vma, address, pfn);
+}
+EXPORT_SYMBOL(unsafe_follow_pfn);
 
 LIST_HEAD(vmap_area_list);
 
index 7561f6f99f1d294d354f6c58cd46f58006e5f29b..322071cebaa11162d37b30b31a36f1c739c8dcda 100644 (file)
@@ -230,6 +230,19 @@ config STATIC_USERMODEHELPER_PATH
          If you wish for all usermode helper programs to be disabled,
          specify an empty string here (i.e. "").
 
+config STRICT_FOLLOW_PFN
+       bool "Disable unsafe use of follow_pfn"
+       depends on MMU
+       help
+         Some functionality in the kernel follows userspace mappings to iomem
+         ranges in an unsafe matter. Examples include v4l userptr for zero-copy
+         buffers sharing.
+
+         If this option is switched on, such access is rejected. Only disable
+         this option when you must run userspace which requires this.
+
+         If in doubt, say Y.
+
 source "security/selinux/Kconfig"
 source "security/smack/Kconfig"
 source "security/tomoyo/Kconfig"