1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2020 SUSE LLC
4 * Author: Nicolai Stange <[email protected]>
5 * LTP port: Martin Doucha <[email protected]>
6 */
7
8 /*\
9 * CVE 2021-38198
10 *
11 * Check that x86_64 KVM correctly enforces (lack of) write permissions
12 * in 4-level and 5-level memory page table mode. Missing page faults fixed in:
13 *
14 * commit b1bd5cba3306691c771d558e94baa73e8b0b96b7
15 * Author: Lai Jiangshan <[email protected]>
16 * Date: Thu Jun 3 13:24:55 2021 +0800
17 *
18 * KVM: X86: MMU: Use the correct inherited permissions to get shadow page
19 */
20
21 #include "kvm_test.h"
22
23 #ifdef COMPILE_PAYLOAD
24 #ifdef __x86_64__
25
26 #include "kvm_x86.h"
27
28 #define PTE_BITMASK 0x1ff
29 #define PAGESIZE 0x1000
30
handle_page_fault(void * userdata,struct kvm_interrupt_frame * ifrm,unsigned long errcode)31 int handle_page_fault(void *userdata, struct kvm_interrupt_frame *ifrm,
32 unsigned long errcode)
33 {
34 struct kvm_cregs buf;
35
36 kvm_read_cregs(&buf);
37
38 /* Check that the page fault was caused by write to *readonly below */
39 if (buf.cr2 == (uintptr_t)userdata) {
40 tst_res(TPASS, "KVM enforces memory write permissions");
41 kvm_exit();
42 }
43
44 /* Unexpected page fault, fall back to default handler */
45 return 0;
46 }
47
main(void)48 void main(void)
49 {
50 uintptr_t tmp;
51 struct page_table_entry_pae *subpte, *pte = kvm_pagetable;
52 int val, *writable, *readonly, *cacher1, *cacher2;
53
54 if (!(kvm_rdmsr(MSR_EFER) & EFER_LMA))
55 tst_brk(TBROK, "Bootstrap did not enable 64bit paging");
56
57 /*
58 * Find the first page table entry which branches. This entry was
59 * configured by bootstrap as follows:
60 * 0x00000000 - 0x3fffffff in pte[0] (identity mapped)
61 * 0x40000000 - 0x7fffffff in pte[1] (identity mapped)
62 * 0x80000000 - 0xbfffffff in pte[2] (unmapped)
63 * 0xc0000000 - 0xffffffff in pte[3] (only last page identity mapped)
64 */
65 while (!pte[1].present) {
66 tmp = kvm_get_page_address_pae(pte);
67 pte = (struct page_table_entry_pae *)tmp;
68 }
69
70 /*
71 * Setup mapping above the 32bit address space. The test needs two
72 * different unused 1GB chunks of address space. Remapping part of
73 * the lower 4GB address space would make it harder to reproduce
74 * the bug because any memory access in the same 1GB chunk (even
75 * fetching instructions by the CPU) could evict entries from page
76 * table cache and force the bypassable write permission check
77 * to happen even on buggy kernels.
78 *
79 * Allocate 3 pages for page table + 2 pages for data
80 */
81 writable = tst_heap_alloc_aligned(5 * PAGESIZE, PAGESIZE);
82 memset(writable, 0, 5 * PAGESIZE);
83 tmp = (uintptr_t)writable;
84 pte[4].address = tmp >> 12;
85 pte[4].user_access = 1;
86 pte[4].writable = 1;
87 pte[4].present = 1;
88 pte[5] = pte[4];
89 pte[5].writable = 0;
90
91 subpte = (struct page_table_entry_pae *)tmp;
92 subpte[0].address = (tmp + PAGESIZE) >> 12;
93 subpte[0].user_access = 1;
94 subpte[0].writable = 0;
95 subpte[0].present = 1;
96 subpte[1].address = (tmp + 2 * PAGESIZE) >> 12;
97 subpte[1].user_access = 1;
98 subpte[1].writable = 1;
99 subpte[1].present = 1;
100
101 subpte = (struct page_table_entry_pae *)(tmp + PAGESIZE);
102 subpte[0].address = (tmp + 3 * PAGESIZE) >> 12;
103 subpte[0].user_access = 1;
104 subpte[0].writable = 1;
105 subpte[0].present = 1;
106
107 subpte = (struct page_table_entry_pae *)(tmp + 2 * PAGESIZE);
108 subpte[0].address = (tmp + 4 * PAGESIZE) >> 12;
109 subpte[0].user_access = 1;
110 subpte[0].writable = 1;
111 subpte[0].present = 1;
112
113 /* Create pointers into the new mapping */
114 cacher1 = (int *)0x100000000ULL;
115 writable = (int *)0x100200000ULL;
116 cacher2 = (int *)0x140000000ULL;
117 readonly = (int *)0x140200000ULL;
118 tst_set_interrupt_callback(INTR_PAGE_FAULT, handle_page_fault,
119 readonly);
120
121 /* Fill page table cache */
122 val = *cacher1;
123 *writable = val;
124 val = *cacher2;
125
126 /* Trigger page fault (unless the kernel is vulnerable) */
127 *readonly = val;
128
129 /* This line should be unreachable */
130 tst_res(TFAIL, "Write to read-only address did not page fault");
131 }
132
133 #else /* __x86_64__ */
134 TST_TEST_TCONF("Test supported only on x86_64");
135 #endif /* __x86_64__ */
136
137 #else /* COMPILE_PAYLOAD */
138
139 #include <ctype.h>
140 #include <stdio.h>
141 #include <unistd.h>
142 #include "tst_module.h"
143
144 #define TDP_MMU_SYSFILE "/sys/module/kvm/parameters/tdp_mmu"
145 #define TDP_AMD_SYSFILE "/sys/module/kvm_amd/parameters/npt"
146 #define TDP_INTEL_SYSFILE "/sys/module/kvm_intel/parameters/ept"
147
148 #define BUF_SIZE 64
149
read_bool_sys_param(const char * filename)150 static int read_bool_sys_param(const char *filename)
151 {
152 char buf[BUF_SIZE];
153 int i, fd, ret;
154
155 fd = open(filename, O_RDONLY);
156
157 if (fd < 0)
158 return -1;
159
160 ret = read(fd, buf, BUF_SIZE - 1);
161 SAFE_CLOSE(fd);
162
163 if (ret < 1)
164 return -1;
165
166 buf[ret] = '\0';
167
168 for (i = 0; buf[i] && !isspace(buf[i]); i++)
169 ;
170
171 buf[i] = '\0';
172
173 if (isdigit(buf[0])) {
174 tst_parse_int(buf, &ret, INT_MIN, INT_MAX);
175 return ret;
176 }
177
178 if (!strcasecmp(buf, "N"))
179 return 0;
180
181 /* Assume that any other value than 0 or N means the param is enabled */
182 return 1;
183 }
184
reload_module(const char * module,char * arg)185 static void reload_module(const char *module, char *arg)
186 {
187 const char *const argv[] = {"modprobe", module, arg, NULL};
188
189 tst_res(TINFO, "Reloading module %s with parameter %s", module, arg);
190 tst_module_unload(module);
191 tst_cmd(argv, NULL, NULL, 0);
192 }
193
disable_tdp(void)194 static void disable_tdp(void)
195 {
196 if (!access(TDP_MMU_SYSFILE, F_OK)) {
197 /* FIXME: Is setting tdp_mmu=0 sufficient to disable TDP? */
198 return;
199 }
200
201 if (read_bool_sys_param(TDP_AMD_SYSFILE) > 0)
202 reload_module("kvm_amd", "npt=0");
203
204 if (read_bool_sys_param(TDP_INTEL_SYSFILE) > 0)
205 reload_module("kvm_intel", "ept=0");
206 }
207
setup(void)208 static void setup(void)
209 {
210 disable_tdp();
211 tst_kvm_setup();
212 }
213
214 static struct tst_test test = {
215 .test_all = tst_kvm_run,
216 .setup = setup,
217 .cleanup = tst_kvm_cleanup,
218 .needs_root = 1,
219 .save_restore = (const struct tst_path_val[]) {
220 {"/sys/module/kvm/parameters/tdp_mmu", "0",
221 TST_SR_SKIP_MISSING | TST_SR_TCONF_RO},
222 {}
223 },
224 .supported_archs = (const char *const []) {
225 "x86_64",
226 NULL
227 },
228 .tags = (struct tst_tag[]){
229 {"linux-git", "b1bd5cba3306"},
230 {"CVE", "2021-38198"},
231 {}
232 }
233 };
234
235 #endif /* COMPILE_PAYLOAD */
236