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