]> git.proxmox.com Git - pve-kernel.git/blob - patches/kernel/0206-x86-pti-Map-the-vsyscall-page-if-needed.patch
ba8bfdf235c33190f350dfab632c650e04d284de
[pve-kernel.git] / patches / kernel / 0206-x86-pti-Map-the-vsyscall-page-if-needed.patch
1 From 7505dd405211a42c3abf52ef33b97eea470aaf60 Mon Sep 17 00:00:00 2001
2 From: Andy Lutomirski <luto@kernel.org>
3 Date: Tue, 12 Dec 2017 07:56:42 -0800
4 Subject: [PATCH 206/233] x86/pti: Map the vsyscall page if needed
5 MIME-Version: 1.0
6 Content-Type: text/plain; charset=UTF-8
7 Content-Transfer-Encoding: 8bit
8
9 CVE-2017-5754
10
11 Make VSYSCALLs work fully in PTI mode by mapping them properly to the user
12 space visible page tables.
13
14 [ tglx: Hide unused functions (Patch by Arnd Bergmann) ]
15
16 Signed-off-by: Andy Lutomirski <luto@kernel.org>
17 Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
18 Cc: Borislav Petkov <bp@alien8.de>
19 Cc: Brian Gerst <brgerst@gmail.com>
20 Cc: Dave Hansen <dave.hansen@linux.intel.com>
21 Cc: David Laight <David.Laight@aculab.com>
22 Cc: H. Peter Anvin <hpa@zytor.com>
23 Cc: Josh Poimboeuf <jpoimboe@redhat.com>
24 Cc: Juergen Gross <jgross@suse.com>
25 Cc: Kees Cook <keescook@chromium.org>
26 Cc: Linus Torvalds <torvalds@linux-foundation.org>
27 Cc: Peter Zijlstra <peterz@infradead.org>
28 Signed-off-by: Ingo Molnar <mingo@kernel.org>
29 (cherry picked from commit 85900ea51577e31b186e523c8f4e068c79ecc7d3)
30 Signed-off-by: Andy Whitcroft <apw@canonical.com>
31 Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
32 (cherry picked from commit 7a2ba0ea0a18cfc1f18c3f1389ef85f2a0d3227d)
33 Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
34 ---
35 arch/x86/include/asm/vsyscall.h | 1 +
36 arch/x86/entry/vsyscall/vsyscall_64.c | 6 ++--
37 arch/x86/mm/pti.c | 65 +++++++++++++++++++++++++++++++++++
38 3 files changed, 69 insertions(+), 3 deletions(-)
39
40 diff --git a/arch/x86/include/asm/vsyscall.h b/arch/x86/include/asm/vsyscall.h
41 index 6ba66ee79710..0eaeb223d692 100644
42 --- a/arch/x86/include/asm/vsyscall.h
43 +++ b/arch/x86/include/asm/vsyscall.h
44 @@ -6,6 +6,7 @@
45
46 #ifdef CONFIG_X86_VSYSCALL_EMULATION
47 extern void map_vsyscall(void);
48 +extern void set_vsyscall_pgtable_user_bits(pgd_t *root);
49
50 /*
51 * Called on instruction fetch fault in vsyscall page.
52 diff --git a/arch/x86/entry/vsyscall/vsyscall_64.c b/arch/x86/entry/vsyscall/vsyscall_64.c
53 index 5e56a4ced848..238b4bcd3c47 100644
54 --- a/arch/x86/entry/vsyscall/vsyscall_64.c
55 +++ b/arch/x86/entry/vsyscall/vsyscall_64.c
56 @@ -343,14 +343,14 @@ int in_gate_area_no_mm(unsigned long addr)
57 * vsyscalls but leave the page not present. If so, we skip calling
58 * this.
59 */
60 -static void __init set_vsyscall_pgtable_user_bits(void)
61 +void __init set_vsyscall_pgtable_user_bits(pgd_t *root)
62 {
63 pgd_t *pgd;
64 p4d_t *p4d;
65 pud_t *pud;
66 pmd_t *pmd;
67
68 - pgd = pgd_offset_k(VSYSCALL_ADDR);
69 + pgd = pgd_offset_pgd(root, VSYSCALL_ADDR);
70 set_pgd(pgd, __pgd(pgd_val(*pgd) | _PAGE_USER));
71 p4d = p4d_offset(pgd, VSYSCALL_ADDR);
72 #if CONFIG_PGTABLE_LEVELS >= 5
73 @@ -372,7 +372,7 @@ void __init map_vsyscall(void)
74 vsyscall_mode == NATIVE
75 ? PAGE_KERNEL_VSYSCALL
76 : PAGE_KERNEL_VVAR);
77 - set_vsyscall_pgtable_user_bits();
78 + set_vsyscall_pgtable_user_bits(swapper_pg_dir);
79 }
80
81 BUILD_BUG_ON((unsigned long)__fix_to_virt(VSYSCALL_PAGE) !=
82 diff --git a/arch/x86/mm/pti.c b/arch/x86/mm/pti.c
83 index b1c38ef9fbbb..bce8aea65606 100644
84 --- a/arch/x86/mm/pti.c
85 +++ b/arch/x86/mm/pti.c
86 @@ -38,6 +38,7 @@
87
88 #include <asm/cpufeature.h>
89 #include <asm/hypervisor.h>
90 +#include <asm/vsyscall.h>
91 #include <asm/cmdline.h>
92 #include <asm/pti.h>
93 #include <asm/pgtable.h>
94 @@ -223,6 +224,69 @@ static pmd_t *pti_user_pagetable_walk_pmd(unsigned long address)
95 return pmd_offset(pud, address);
96 }
97
98 +#ifdef CONFIG_X86_VSYSCALL_EMULATION
99 +/*
100 + * Walk the shadow copy of the page tables (optionally) trying to allocate
101 + * page table pages on the way down. Does not support large pages.
102 + *
103 + * Note: this is only used when mapping *new* kernel data into the
104 + * user/shadow page tables. It is never used for userspace data.
105 + *
106 + * Returns a pointer to a PTE on success, or NULL on failure.
107 + */
108 +static __init pte_t *pti_user_pagetable_walk_pte(unsigned long address)
109 +{
110 + gfp_t gfp = (GFP_KERNEL | __GFP_NOTRACK | __GFP_ZERO);
111 + pmd_t *pmd = pti_user_pagetable_walk_pmd(address);
112 + pte_t *pte;
113 +
114 + /* We can't do anything sensible if we hit a large mapping. */
115 + if (pmd_large(*pmd)) {
116 + WARN_ON(1);
117 + return NULL;
118 + }
119 +
120 + if (pmd_none(*pmd)) {
121 + unsigned long new_pte_page = __get_free_page(gfp);
122 + if (!new_pte_page)
123 + return NULL;
124 +
125 + if (pmd_none(*pmd)) {
126 + set_pmd(pmd, __pmd(_KERNPG_TABLE | __pa(new_pte_page)));
127 + new_pte_page = 0;
128 + }
129 + if (new_pte_page)
130 + free_page(new_pte_page);
131 + }
132 +
133 + pte = pte_offset_kernel(pmd, address);
134 + if (pte_flags(*pte) & _PAGE_USER) {
135 + WARN_ONCE(1, "attempt to walk to user pte\n");
136 + return NULL;
137 + }
138 + return pte;
139 +}
140 +
141 +static void __init pti_setup_vsyscall(void)
142 +{
143 + pte_t *pte, *target_pte;
144 + unsigned int level;
145 +
146 + pte = lookup_address(VSYSCALL_ADDR, &level);
147 + if (!pte || WARN_ON(level != PG_LEVEL_4K) || pte_none(*pte))
148 + return;
149 +
150 + target_pte = pti_user_pagetable_walk_pte(VSYSCALL_ADDR);
151 + if (WARN_ON(!target_pte))
152 + return;
153 +
154 + *target_pte = *pte;
155 + set_vsyscall_pgtable_user_bits(kernel_to_user_pgdp(swapper_pg_dir));
156 +}
157 +#else
158 +static void __init pti_setup_vsyscall(void) { }
159 +#endif
160 +
161 static void __init
162 pti_clone_pmds(unsigned long start, unsigned long end, pmdval_t clear)
163 {
164 @@ -319,4 +383,5 @@ void __init pti_init(void)
165 pti_clone_user_shared();
166 pti_clone_entry_text();
167 pti_setup_espfix64();
168 + pti_setup_vsyscall();
169 }
170 --
171 2.14.2
172