Skip to content

Commit 44392eb

Browse files
executor: sys/linux: SYZOS: add support for AMD INVLPGA instruction
Implement the SYZOS_API_NESTED_AMD_INVLPGA primitive to execute the INVLPGA instruction in the L1 guest. This allows the fuzzer to target KVM's Shadow MMU and Nested Paging (NPT) logic by invalidating TLB entries for specific ASIDs. Also add a simple syzlang seed/regression test.
1 parent ce94b10 commit 44392eb

File tree

3 files changed

+59
-0
lines changed

3 files changed

+59
-0
lines changed

executor/common_kvm_amd64_syzos.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ typedef enum {
3333
SYZOS_API_NESTED_VMRESUME = 304,
3434
SYZOS_API_NESTED_INTEL_VMWRITE_MASK = 340,
3535
SYZOS_API_NESTED_AMD_VMCB_WRITE_MASK = 380,
36+
SYZOS_API_NESTED_AMD_INVLPGA = 381,
3637
SYZOS_API_STOP, // Must be the last one
3738
} syzos_api_id;
3839

@@ -113,6 +114,7 @@ GUEST_CODE static void guest_handle_nested_vmlaunch(struct api_call_1* cmd, uint
113114
GUEST_CODE static void guest_handle_nested_vmresume(struct api_call_1* cmd, uint64 cpu_id);
114115
GUEST_CODE static void guest_handle_nested_intel_vmwrite_mask(struct api_call_5* cmd, uint64 cpu_id);
115116
GUEST_CODE static void guest_handle_nested_amd_vmcb_write_mask(struct api_call_5* cmd, uint64 cpu_id);
117+
GUEST_CODE static void guest_handle_nested_amd_invlpga(struct api_call_2* cmd, uint64 cpu_id);
116118

117119
typedef enum {
118120
UEXIT_END = (uint64)-1,
@@ -228,6 +230,9 @@ guest_main(uint64 size, uint64 cpu)
228230
} else if (call == SYZOS_API_NESTED_AMD_VMCB_WRITE_MASK) {
229231
// Write to a VMCB field using masks.
230232
guest_handle_nested_amd_vmcb_write_mask((struct api_call_5*)cmd, cpu);
233+
} else if (call == SYZOS_API_NESTED_AMD_INVLPGA) {
234+
// Invalidate TLB mappings for the specified address/ASID.
235+
guest_handle_nested_amd_invlpga((struct api_call_2*)cmd, cpu);
231236
}
232237
addr += cmd->size;
233238
size -= cmd->size;
@@ -1282,4 +1287,17 @@ guest_handle_nested_amd_vmcb_write_mask(struct api_call_5* cmd, uint64 cpu_id)
12821287
vmcb_write64(vmcb_addr, offset, new_value);
12831288
}
12841289

1290+
GUEST_CODE static noinline void
1291+
guest_handle_nested_amd_invlpga(struct api_call_2* cmd, uint64 cpu_id)
1292+
{
1293+
if (get_cpu_vendor() != CPU_VENDOR_AMD)
1294+
return;
1295+
1296+
uint64 linear_addr = cmd->args[0];
1297+
// ASID (Address Space ID) - only lower 16 bits matter usually, but register is 32-bit.
1298+
uint32 asid = (uint32)cmd->args[1];
1299+
1300+
asm volatile("invlpga" : : "a"(linear_addr), "c"(asid) : "memory");
1301+
}
1302+
12851303
#endif // EXECUTOR_COMMON_KVM_AMD64_SYZOS_H

sys/linux/dev_kvm_amd64.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ syzos_api_nested_amd_vmcb_write_mask {
147147
flip_mask int64
148148
}
149149

150+
syzos_api_nested_amd_invlpga {
151+
addr flags[kvm_guest_addrs, int64]
152+
asid int64[0:65535]
153+
}
154+
150155
# IDs here must match those in executor/common_kvm_amd64_syzos.h.
151156
syzos_api_call$x86 [
152157
uexit syzos_api$x86[0, intptr]
@@ -166,6 +171,7 @@ syzos_api_call$x86 [
166171
nested_vmresume syzos_api$x86[304, syzos_api_vm_id]
167172
nested_intel_vmwrite_mask syzos_api$x86[340, syzos_api_nested_intel_vmwrite_mask]
168173
nested_amd_vmcb_write_mask syzos_api$x86[380, syzos_api_nested_amd_vmcb_write_mask]
174+
nested_amd_invlpga syzos_api$x86[381, syzos_api_nested_amd_invlpga]
169175
] [varlen]
170176

171177
kvm_text_x86 [
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#
2+
# requires: arch=amd64 -threaded
3+
#
4+
r0 = openat$kvm(0, &AUTO='/dev/kvm\x00', 0x0, 0x0)
5+
r1 = ioctl$KVM_CREATE_VM(r0, AUTO, 0x0)
6+
r2 = syz_kvm_setup_syzos_vm$x86(r1, &(0x7f0000c00000/0x400000)=nil)
7+
8+
# Test L0 emulation of the AMD SVM INVLPGA instruction.
9+
#
10+
# 1. Setup nested environment (L1).
11+
# 2. L1 executes INVLPGA with specific ASID and Linear Address.
12+
# - This triggers a VMEXIT to L0.
13+
# - L0 must emulate the invalidation in its Shadow MMU / NPT.
14+
# - L0 resumes L1.
15+
# 3. L1 executes UEXIT(0xface) to signal survival.
16+
#
17+
# Arguments for INVLPGA:
18+
# Arg 0: Linear Address (e.g., 0x400000)
19+
# Arg 1: ASID (e.g., 1)
20+
#
21+
r3 = syz_kvm_add_vcpu$x86(r2, &AUTO={0x0, &AUTO=[@enable_nested={AUTO, AUTO, 0x0}, @nested_create_vm={AUTO, AUTO, 0x0}, @nested_amd_invlpga={AUTO, AUTO, {0x400000, 0x1}}, @uexit={AUTO, AUTO, 0xface}], AUTO})
22+
r4 = ioctl$KVM_GET_VCPU_MMAP_SIZE(r0, AUTO)
23+
r5 = mmap$KVM_VCPU(&(0x7f0000009000/0x1000)=nil, r4, 0x3, 0x1, r3, 0x0)
24+
25+
# Run the VCPU.
26+
# We expect the guest to execute INVLPGA (handled transparently by L0)
27+
# and then immediately yield with UEXIT code 0xface.
28+
#
29+
ioctl$KVM_RUN(r3, AUTO, 0x0)
30+
syz_kvm_assert_syzos_uexit$x86(r5, 0xface)
31+
32+
# guest_main should finish with guest_uexit(-1).
33+
#
34+
ioctl$KVM_RUN(r3, AUTO, 0x0)
35+
syz_kvm_assert_syzos_uexit$x86(r5, 0xffffffff)

0 commit comments

Comments
 (0)