]>
Commit | Line | Data |
---|---|---|
321d628a FG |
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 | |
633c5ed1 | 4 | Subject: [PATCH 206/242] x86/pti: Map the vsyscall page if needed |
321d628a FG |
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 |