• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1From a9dc703806031c8535c84c9905979f86dfaec1d3 Mon Sep 17 00:00:00 2001
2From: Quentin Perret <qperret@google.com>
3Date: Thu, 17 Feb 2022 19:34:27 +0000
4Subject: [PATCH] ANDROID: kvm: Test that pVM memory is wiped during teardown
5
6In protected KVM mode, we expect the hypervisor to protect guest secrets
7when they are torn down. Add a test checking this property by running a
8minimal guest, and checking that the content of memory has been wiped by
9the hypervisor after teardown.
10
11Note: although some of the pKVM code has already landed upstream, the
12functionality tested here hasn't at the time of writing. Once it does,
13this test should be sent upstream for review to replace this ANDROID
14patch.
15
16Bug: 218934075
17Signed-off-by: Quentin Perret <qperret@google.com>
18Change-Id: I4a347908d189f2c4835fd576b26fae7c10ec9b22
19---
20 Android.bp                                    |  15 ++
21 android/kselftest_test_list.mk                |   1 +
22 .../selftests/kvm/aarch64/pvm_wipe_mem.c      | 174 ++++++++++++++++++
23 3 files changed, 190 insertions(+)
24 create mode 100644 tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c
25
26diff --git a/Android.bp b/Android.bp
27index 0b9166c56315..08805a161450 100644
28--- a/Android.bp
29+++ b/Android.bp
30@@ -340,6 +340,21 @@ cc_test {
31     defaults: ["kselftest_defaults"],
32 }
33
34+// KVM test
35+cc_test {
36+    name: "kselftest_kvm_arm64_tests",
37+    relative_install_path: "linux-kselftest/kvm/aarch64",
38+    local_include_dirs: [ "tools/testing/selftests"],
39+    enabled: false,
40+    arch: {
41+        arm64: {
42+            enabled: true,
43+            srcs: ["tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c"],
44+        },
45+    },
46+    defaults: ["kselftest_defaults"],
47+}
48+
49 // Lib test
50 sh_test {
51     name: "kselftest_lib_printf",
52diff --git a/android/kselftest_test_list.mk b/android/kselftest_test_list.mk
53index 07df6fc5d758..46b9b92c58fb 100644
54--- a/android/kselftest_test_list.mk
55+++ b/android/kselftest_test_list.mk
56@@ -31,6 +31,7 @@ kselftest_modules += \
57   kselftest_intel_pstate_tests_aperf \
58   kselftest_intel_pstate_tests_msr \
59   kselftest_kcmp_tests_kcmp_test \
60+  kselftest_kvm_arm64_tests_pvm_wipe_mem \
61   kselftest_net_tests_psock_tpacket \
62   kselftest_net_tests_socket \
63   kselftest_net_tests_reuseaddr_conflict \
64diff --git a/tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c b/tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c
65new file mode 100644
66index 000000000000..4af8ca3c4bad
67--- /dev/null
68+++ b/tools/testing/selftests/kvm/aarch64/pvm_wipe_mem.c
69@@ -0,0 +1,174 @@
70+// SPDX-License-Identifier: GPL-2.0-only
71+/*
72+ * Test checking that memory of protected guests is wiped after teardown.
73+ *
74+ * Copyright (C) 2022, Google LLC.
75+ */
76+
77+#define _GNU_SOURCE
78+
79+#include <err.h>
80+#include <errno.h>
81+#include <fcntl.h>
82+#include <stdio.h>
83+#include <stdint.h>
84+#include <stdlib.h>
85+#include <string.h>
86+#include <unistd.h>
87+
88+#include <linux/kvm.h>
89+#include <sys/ioctl.h>
90+#include <sys/mman.h>
91+
92+#include "kselftest.h"
93+
94+#define KVM_VM_TYPE_ARM_PROTECTED	(1UL << 31)
95+
96+#define REG_X(number)	(0x6030000000100000ULL + (number) * 2UL)
97+#define REG_PC		0x6030000000100040ULL
98+
99+static void set_one_reg(int vcpufd, uint64_t reg_id, uint64_t val)
100+{
101+	uint64_t reg_data;
102+	struct kvm_one_reg reg;
103+	int ret;
104+
105+	reg.addr = (__u64) &reg_data;
106+	reg_data = val;
107+	reg.id = reg_id;
108+
109+	ret = ioctl(vcpufd, KVM_SET_ONE_REG, &reg);
110+	if (ret < 0)
111+		ksft_exit_fail_msg("Failed to set reg: %d\n", ret);
112+}
113+
114+static int get_kvm(void)
115+{
116+	size_t run_size;
117+	int kvm, ret;
118+
119+	kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC);
120+	if (kvm < 0)
121+		ksft_exit_skip("KVM not supported\n");
122+
123+	ret = ioctl(kvm, KVM_GET_API_VERSION, NULL);
124+	if (ret != 12)
125+		ksft_exit_fail_msg("KVM_GET_API_VERSION %d, expected 12", ret);
126+
127+	run_size = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL);
128+	if (run_size < sizeof(struct kvm_run))
129+		ksft_exit_fail_msg("KVM_GET_VCPU_MMAP_SIZE unexpectedly small\n");
130+
131+	return kvm;
132+}
133+
134+static int create_protected_vm(int kvm)
135+{
136+	int vmfd = ioctl(kvm, KVM_CREATE_VM, KVM_VM_TYPE_ARM_PROTECTED);
137+
138+	if (vmfd < 0)
139+		ksft_exit_skip("Protected guests not supported: %d\n", vmfd);
140+
141+	return vmfd;
142+}
143+
144+static int create_vcpu(int vmfd, struct kvm_run **run)
145+{
146+	struct kvm_vcpu_init vcpu_init;
147+	int vcpufd, ret;
148+
149+	ret = ioctl(vmfd, KVM_ARM_PREFERRED_TARGET, &vcpu_init);
150+	if (ret)
151+		ksft_exit_fail_msg("Failed to set kvm_vcpu_init %d\n", ret);
152+
153+	vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long)0);
154+	if (vcpufd < 0)
155+		ksft_exit_fail_msg("Failed to create VCPU: %d\n", vcpufd);
156+
157+	*run = mmap(NULL, sizeof(**run), PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0);
158+	if (!run)
159+		ksft_exit_fail_msg("Failed to mmap vcpu_run struct\n");
160+
161+	ret = ioctl(vcpufd, KVM_ARM_VCPU_INIT, &vcpu_init);
162+	if (ret)
163+		ksft_exit_fail_msg("Failed to initialize VCPU %d\n", ret);
164+
165+	return vcpufd;
166+}
167+
168+static void teardown(int kvm, int vmfd, int vcpufd, struct kvm_run *run)
169+{
170+	int ret = munmap(run, sizeof(*run));
171+
172+	if (ret)
173+		ksft_exit_fail_msg("Failed to unmap vCPU run: %d\n", ret);
174+
175+	ret = close(vcpufd);
176+	if (ret)
177+		ksft_exit_fail_msg("Failed to destroy VCPU: %d\n", ret);
178+
179+	ret = close(vmfd);
180+	if (ret)
181+		ksft_exit_fail_msg("Failed to destroy VM: %d\n", ret);
182+
183+	ret = close(kvm);
184+	if (ret)
185+		ksft_exit_fail_msg("Failed to close KVM fd: %d\n", ret);
186+}
187+
188+int main(void)
189+{
190+	struct kvm_userspace_memory_region region;
191+	long page_size = sysconf(_SC_PAGESIZE);
192+	int ret, kvm, vmfd, vcpufd;
193+	uint32_t guest_code[2];
194+	struct kvm_run *run;
195+	uint8_t *guest_mem;
196+	size_t run_size;
197+
198+	kvm = get_kvm();
199+	vmfd = create_protected_vm(kvm);
200+	vcpufd = create_vcpu(vmfd, &run);
201+
202+	/* Create a one-page memslot for the guest */
203+	guest_mem = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
204+			MAP_SHARED | MAP_ANONYMOUS, -1, 0);
205+	if (guest_mem == MAP_FAILED)
206+		ksft_exit_fail_msg("Failed to mmap guest memory\n");
207+	region = (struct kvm_userspace_memory_region) {
208+		.slot = 0,
209+		.guest_phys_addr = 1UL << 30,
210+		.memory_size = page_size,
211+		.userspace_addr = (uint64_t)guest_mem,
212+	};
213+
214+	/* Copy some code in guest memory. */
215+	guest_code[0] = 0xf9400001;	/* 1:  ldr	x1, [x0]  */
216+	guest_code[1] = 0x17ffffff;	/*     b	1b	  */
217+	memcpy(guest_mem, guest_code, sizeof(guest_code));
218+	ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region);
219+	if (ret)
220+		ksft_exit_fail_msg("Failed to set memory region: %d\n", ret);
221+
222+	/*
223+	 * Get the VCPU to run one instruction, to be sure the page containing
224+	 * the code has been faulted in.
225+	 */
226+	set_one_reg(vcpufd, REG_PC, region.guest_phys_addr);
227+	set_one_reg(vcpufd, REG_X(0), region.guest_phys_addr + region.memory_size);
228+	ret = ioctl(vcpufd, KVM_RUN, NULL);
229+	if (ret)
230+		ksft_exit_fail_msg("Failed to run vcpu: %d\n", ret);
231+	if (run->exit_reason != KVM_EXIT_MMIO)
232+		ksft_exit_fail_msg("Unexpected KVM exit reason: %u\n", run->exit_reason);
233+
234+	/*
235+	 * Tear the guest down, and check that the donated memory has been
236+	 * wiped by the hypervisor.
237+	 */
238+	teardown(kvm, vmfd, vcpufd, run);
239+	if (!memcmp(guest_mem, guest_code, sizeof(guest_code)))
240+		ksft_exit_fail_msg("Protected guest memory has not been poisoned\n");
241+
242+	ksft_exit_pass();
243+}
244--
2452.35.1.473.g83b2b277ed-goog
246
247