Skip to content

Commit 40e0ee6

Browse files
dmatlacksean-jc
authored andcommitted
KVM: selftests: Add coverage of EPT-disabled to vmx_dirty_log_test
Extend vmx_dirty_log_test to include accesses made by L2 when EPT is disabled. This commit adds explicit coverage of a bug caught by syzkaller, where the TDP MMU would clear D-bits instead of write-protecting SPTEs being used to map an L2, which only happens when L1 does not enable EPT, causing writes made by L2 to not be reflected in the dirty log when PML is enabled: $ ./vmx_dirty_log_test Nested EPT: disabled ==== Test Assertion Failure ==== x86_64/vmx_dirty_log_test.c:151: test_bit(0, bmap) pid=72052 tid=72052 errno=4 - Interrupted system call (stack trace empty) Page 0 incorrectly reported clean Opportunistically replace the volatile casts with {READ,WRITE}_ONCE(). Link: https://lore.kernel.org/kvm/000000000000c6526f06137f18cc@google.com/ Signed-off-by: David Matlack <dmatlack@google.com> Link: https://lore.kernel.org/r/20240315230541.1635322-5-dmatlack@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent b1a8d2b commit 40e0ee6

File tree

1 file changed

+46
-14
lines changed

1 file changed

+46
-14
lines changed

tools/testing/selftests/kvm/x86_64/vmx_dirty_log_test.c

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,34 +28,50 @@
2828
#define NESTED_TEST_MEM1 0xc0001000
2929
#define NESTED_TEST_MEM2 0xc0002000
3030

31-
static void l2_guest_code(void)
31+
static void l2_guest_code(u64 *a, u64 *b)
3232
{
33-
*(volatile uint64_t *)NESTED_TEST_MEM1;
34-
*(volatile uint64_t *)NESTED_TEST_MEM1 = 1;
33+
READ_ONCE(*a);
34+
WRITE_ONCE(*a, 1);
3535
GUEST_SYNC(true);
3636
GUEST_SYNC(false);
3737

38-
*(volatile uint64_t *)NESTED_TEST_MEM2 = 1;
38+
WRITE_ONCE(*b, 1);
3939
GUEST_SYNC(true);
40-
*(volatile uint64_t *)NESTED_TEST_MEM2 = 1;
40+
WRITE_ONCE(*b, 1);
4141
GUEST_SYNC(true);
4242
GUEST_SYNC(false);
4343

4444
/* Exit to L1 and never come back. */
4545
vmcall();
4646
}
4747

48+
static void l2_guest_code_ept_enabled(void)
49+
{
50+
l2_guest_code((u64 *)NESTED_TEST_MEM1, (u64 *)NESTED_TEST_MEM2);
51+
}
52+
53+
static void l2_guest_code_ept_disabled(void)
54+
{
55+
/* Access the same L1 GPAs as l2_guest_code_ept_enabled() */
56+
l2_guest_code((u64 *)GUEST_TEST_MEM, (u64 *)GUEST_TEST_MEM);
57+
}
58+
4859
void l1_guest_code(struct vmx_pages *vmx)
4960
{
5061
#define L2_GUEST_STACK_SIZE 64
5162
unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
63+
void *l2_rip;
5264

5365
GUEST_ASSERT(vmx->vmcs_gpa);
5466
GUEST_ASSERT(prepare_for_vmx_operation(vmx));
5567
GUEST_ASSERT(load_vmcs(vmx));
5668

57-
prepare_vmcs(vmx, l2_guest_code,
58-
&l2_guest_stack[L2_GUEST_STACK_SIZE]);
69+
if (vmx->eptp_gpa)
70+
l2_rip = l2_guest_code_ept_enabled;
71+
else
72+
l2_rip = l2_guest_code_ept_disabled;
73+
74+
prepare_vmcs(vmx, l2_rip, &l2_guest_stack[L2_GUEST_STACK_SIZE]);
5975

6076
GUEST_SYNC(false);
6177
GUEST_ASSERT(!vmlaunch());
@@ -64,7 +80,7 @@ void l1_guest_code(struct vmx_pages *vmx)
6480
GUEST_DONE();
6581
}
6682

67-
int main(int argc, char *argv[])
83+
static void test_vmx_dirty_log(bool enable_ept)
6884
{
6985
vm_vaddr_t vmx_pages_gva = 0;
7086
struct vmx_pages *vmx;
@@ -76,8 +92,7 @@ int main(int argc, char *argv[])
7692
struct ucall uc;
7793
bool done = false;
7894

79-
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX));
80-
TEST_REQUIRE(kvm_cpu_has_ept());
95+
pr_info("Nested EPT: %s\n", enable_ept ? "enabled" : "disabled");
8196

8297
/* Create VM */
8398
vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
@@ -103,11 +118,16 @@ int main(int argc, char *argv[])
103118
*
104119
* Note that prepare_eptp should be called only L1's GPA map is done,
105120
* meaning after the last call to virt_map.
121+
*
122+
* When EPT is disabled, the L2 guest code will still access the same L1
123+
* GPAs as the EPT enabled case.
106124
*/
107-
prepare_eptp(vmx, vm, 0);
108-
nested_map_memslot(vmx, vm, 0);
109-
nested_map(vmx, vm, NESTED_TEST_MEM1, GUEST_TEST_MEM, 4096);
110-
nested_map(vmx, vm, NESTED_TEST_MEM2, GUEST_TEST_MEM, 4096);
125+
if (enable_ept) {
126+
prepare_eptp(vmx, vm, 0);
127+
nested_map_memslot(vmx, vm, 0);
128+
nested_map(vmx, vm, NESTED_TEST_MEM1, GUEST_TEST_MEM, 4096);
129+
nested_map(vmx, vm, NESTED_TEST_MEM2, GUEST_TEST_MEM, 4096);
130+
}
111131

112132
bmap = bitmap_zalloc(TEST_MEM_PAGES);
113133
host_test_mem = addr_gpa2hva(vm, GUEST_TEST_MEM);
@@ -148,3 +168,15 @@ int main(int argc, char *argv[])
148168
}
149169
}
150170
}
171+
172+
int main(int argc, char *argv[])
173+
{
174+
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX));
175+
176+
test_vmx_dirty_log(/*enable_ept=*/false);
177+
178+
if (kvm_cpu_has_ept())
179+
test_vmx_dirty_log(/*enable_ept=*/true);
180+
181+
return 0;
182+
}

0 commit comments

Comments
 (0)