aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCraig Bergstrom <craigb@google.com>2017-11-15 17:29:51 -0500
committerIngo Molnar <mingo@kernel.org>2017-11-16 06:49:48 -0500
commitbe62a32044061cb4a3b70a10598e093f1319102e (patch)
tree3a14ae25829175d80145b16f30c2b19436fc0d19
parent97f404ad3e53bf9ac598745066ba2f57c1da3039 (diff)
x86/mm: Limit mmap() of /dev/mem to valid physical addresses
One thing /dev/mem access APIs should verify is that there's no way that excessively large pfn's can leak into the high bits of the page table entry. In particular, if people can use "very large physical page addresses" through /dev/mem to set the bits past bit 58 - SOFTW4 and permission key bits and NX bit, that could *really* confuse the kernel. We had an earlier attempt: ce56a86e2ade ("x86/mm: Limit mmap() of /dev/mem to valid physical addresses") ... which turned out to be too restrictive (breaking mem=... bootups for example) and had to be reverted in: 90edaac62729 ("Revert "x86/mm: Limit mmap() of /dev/mem to valid physical addresses"") This v2 attempt modifies the original patch and makes sure that mmap(/dev/mem) limits the pfns so that it at least fits in the actual pteval_t architecturally: - Make sure mmap_mem() actually validates that the offset fits in phys_addr_t ( This may be indirectly true due to some other check, but it's not entirely obvious. ) - Change valid_mmap_phys_addr_range() to just use phys_addr_valid() on the top byte ( Top byte is sufficient, because mmap_mem() has already checked that it cannot wrap. ) - Add a few comments about what the valid_phys_addr_range() vs. valid_mmap_phys_addr_range() difference is. Signed-off-by: Craig Bergstrom <craigb@google.com> [ Fixed the checks and added comments. ] Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> [ Collected the discussion and patches into a commit. ] Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com> Cc: Fengguang Wu <fengguang.wu@intel.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Hans Verkuil <hans.verkuil@cisco.com> Cc: Mauro Carvalho Chehab <mchehab@s-opensource.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Sander Eikelenboom <linux@eikelenboom.it> Cc: Sean Young <sean@mess.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/CA+55aFyEcOMb657vWSmrM13OxmHxC-XxeBmNis=DwVvpJUOogQ@mail.gmail.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/include/asm/io.h4
-rw-r--r--arch/x86/mm/mmap.c16
-rw-r--r--drivers/char/mem.c4
3 files changed, 24 insertions, 0 deletions
diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h
index 93ae8aee1780..95e948627fd0 100644
--- a/arch/x86/include/asm/io.h
+++ b/arch/x86/include/asm/io.h
@@ -111,6 +111,10 @@ build_mmio_write(__writeq, "q", unsigned long, "r", )
111 111
112#endif 112#endif
113 113
114#define ARCH_HAS_VALID_PHYS_ADDR_RANGE
115extern int valid_phys_addr_range(phys_addr_t addr, size_t size);
116extern int valid_mmap_phys_addr_range(unsigned long pfn, size_t size);
117
114/** 118/**
115 * virt_to_phys - map virtual addresses to physical 119 * virt_to_phys - map virtual addresses to physical
116 * @address: address to remap 120 * @address: address to remap
diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c
index 62285fe77b0f..155ecbac9e28 100644
--- a/arch/x86/mm/mmap.c
+++ b/arch/x86/mm/mmap.c
@@ -33,6 +33,8 @@
33#include <linux/compat.h> 33#include <linux/compat.h>
34#include <asm/elf.h> 34#include <asm/elf.h>
35 35
36#include "physaddr.h"
37
36struct va_alignment __read_mostly va_align = { 38struct va_alignment __read_mostly va_align = {
37 .flags = -1, 39 .flags = -1,
38}; 40};
@@ -220,3 +222,17 @@ bool mmap_address_hint_valid(unsigned long addr, unsigned long len)
220 222
221 return (addr > DEFAULT_MAP_WINDOW) == (addr + len > DEFAULT_MAP_WINDOW); 223 return (addr > DEFAULT_MAP_WINDOW) == (addr + len > DEFAULT_MAP_WINDOW);
222} 224}
225
226/* Can we access it for direct reading/writing? Must be RAM: */
227int valid_phys_addr_range(phys_addr_t addr, size_t count)
228{
229 return addr + count <= __pa(high_memory);
230}
231
232/* Can we access it through mmap? Must be a valid physical address: */
233int valid_mmap_phys_addr_range(unsigned long pfn, size_t count)
234{
235 phys_addr_t addr = (phys_addr_t)pfn << PAGE_SHIFT;
236
237 return phys_addr_valid(addr + count - 1);
238}
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 970e1242a282..6aefe5370e5b 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -343,6 +343,10 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma)
343 size_t size = vma->vm_end - vma->vm_start; 343 size_t size = vma->vm_end - vma->vm_start;
344 phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; 344 phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT;
345 345
346 /* Does it even fit in phys_addr_t? */
347 if (offset >> PAGE_SHIFT != vma->vm_pgoff)
348 return -EINVAL;
349
346 /* It's illegal to wrap around the end of the physical address space. */ 350 /* It's illegal to wrap around the end of the physical address space. */
347 if (offset + (phys_addr_t)size - 1 < offset) 351 if (offset + (phys_addr_t)size - 1 < offset)
348 return -EINVAL; 352 return -EINVAL;