diff mbox series

[SRU,Trusty,7/7] UBUNTU: SAUCE: x86/fremap: Invert the offset when converting to/from a PTE

Message ID 20180822064021.17216-8-juergh@canonical.com
State New
Headers show
Series Follow-up fixes for CVE-2018-3620/CVE-2018-3646 | expand

Commit Message

Juerg Haefliger Aug. 22, 2018, 6:40 a.m. UTC
The 3.13 kernel still uses non-emulated code for the remap_file_pages
syscall which makes use of macros to convert between page offsets and
PTEs. These macros need to invert the offset for L1TF protection.

Without this, the page table entries of a remapped and swapped out
file page look like this:

[28865.660359]   virtual user addr: 00007fe49ea9b000
[28865.660360]   page: ffffeddf927aa6c0
[28865.660361]   pgd:  ffff8802605267f8 (8000000260229067) | USR RW                     NX | pgd
[28865.660365]   pud:  ffff880260229c90 (000000025f9d1067) | USR RW             PAT     x  | pud 1G
[28865.660368]   pmd:  ffff88025f9d17a8 (00000002602d8067) | USR RW                     x  | pmd 2M
[28865.660371]   pte:  ffff8802602d84d8 (00000000001f4040) |     ro                     x  | pte 4K
                                            ^^^^^^ non-inverted offset

With this commit, they look like:

[ 2564.508511]   virtual user addr: 00007f728c787000
[ 2564.508514]   page: ffffedddca31e1c0
[ 2564.508518]   pgd:  ffff8802603207f0 (800000026036b067) | USR RW                     NX | pgd
[ 2564.508531]   pud:  ffff88026036be50 (0000000260ee6067) | USR RW                     x  | pud 1G
[ 2564.508543]   pmd:  ffff880260ee6318 (0000000260360067) | USR RW                     x  | pmd 2M
[ 2564.508554]   pte:  ffff880260360c38 (00003fffffe0b040) |     ro                     x  | pte 4K
                                            ^^^^^^ inverted offset

Also make sure that the number of bits for the maximum offset for a remap
is limited to 1 bit less than the number of actual physical bits so that
the highest bit can be inverted by the conversion macros.

CVE-2018-3620
CVE-2018-3646

Signed-off-by: Juerg Haefliger <juergh@canonical.com>
---
 arch/x86/include/asm/pgtable_64.h | 22 ++++++++++++++++++----
 mm/fremap.c                       |  6 ++++++
 2 files changed, 24 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/arch/x86/include/asm/pgtable_64.h b/arch/x86/include/asm/pgtable_64.h
index a9b88fe94bfa..7342c233e9ca 100644
--- a/arch/x86/include/asm/pgtable_64.h
+++ b/arch/x86/include/asm/pgtable_64.h
@@ -154,10 +154,24 @@  static inline int pgd_large(pgd_t pgd) { return 0; }
 /* PUD - Level3 access */
 
 /* PMD  - Level 2 access */
-#define pte_to_pgoff(pte) ((pte_val((pte)) & PHYSICAL_PAGE_MASK) >> PAGE_SHIFT)
-#define pgoff_to_pte(off) ((pte_t) { .pte = ((off) << PAGE_SHIFT) |	\
-					    _PAGE_FILE })
-#define PTE_FILE_MAX_BITS __PHYSICAL_MASK_SHIFT
+#define pte_to_pgoff(pte) ((~pte_val((pte)) & PHYSICAL_PAGE_MASK) >> PAGE_SHIFT)
+#define pgoff_to_pte(off) ((pte_t) { .pte =				\
+			((~(off) & (PHYSICAL_PAGE_MASK >> PAGE_SHIFT))	\
+			 << PAGE_SHIFT) | _PAGE_FILE })
+/*
+ * Set the highest allowed nonlinear pgoff to 1 bit less than
+ * x86_phys_bits to guarantee the inversion of the highest bit
+ * in the pgoff_to_pte conversion. The lowest x86_phys_bits is
+ * 36 so x86 implementations with 36 bits will find themselves
+ * unable to keep using remap_file_pages() with file offsets
+ * above 128TiB (calculated as 1<<(36-1+PAGE_SHIFT)). More
+ * recent CPUs will retain much higher max file offset limits.
+ */
+#ifdef PTE_FILE_MAX_BITS
+#error "Huh? PTE_FILE_MAX_BITS shouldn't be defined here"
+#endif
+#define L1TF_PTE_FILE_MAX_BITS min(__PHYSICAL_MASK_SHIFT,		\
+				   boot_cpu_data.x86_phys_bits - 1)
 
 /* PTE - Level 1 access. */
 
diff --git a/mm/fremap.c b/mm/fremap.c
index fd94a867cda0..9959bad4ec55 100644
--- a/mm/fremap.c
+++ b/mm/fremap.c
@@ -153,10 +153,16 @@  SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
 		return err;
 
 	/* Can we represent this offset inside this architecture's pte's? */
+#ifdef L1TF_PTE_FILE_MAX_BITS
+	if (L1TF_PTE_FILE_MAX_BITS < BITS_PER_LONG &&
+	    (pgoff + (size >> PAGE_SHIFT) >= (1UL << L1TF_PTE_FILE_MAX_BITS)))
+		return err;
+#else
 #if PTE_FILE_MAX_BITS < BITS_PER_LONG
 	if (pgoff + (size >> PAGE_SHIFT) >= (1UL << PTE_FILE_MAX_BITS))
 		return err;
 #endif
+#endif /* L1TF_PTE_FILE_MAX_BITS */
 
 	/* We need down_write() to change vma->vm_flags. */
 	down_read(&mm->mmap_sem);