xref: /aosp_15_r20/external/ltp/testcases/kernel/kvm/kvm_pagefault01.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
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