Skip to content

Commit 3b7a335

Browse files
executor: sys/linux: SYZOS: add support for AMD Nested Event Injection
Implement SYZOS_API_NESTED_AMD_INJECT_EVENT to allow the L1 guest to inject events (Interrupts, NMIs, Exceptions) into L2 via the VMCB EVENTINJ field. This primitive abstracts the VMCB bit-packing logic (Vector, Type, Valid, Error Code) into a high-level API, enabling the fuzzer to semantically mutate event injection parameters. This targets KVM's nested event merging logic, specifically where L0 must reconcile L1-injected events with Host-pending events.
1 parent 0a1338c commit 3b7a335

File tree

3 files changed

+79
-0
lines changed

3 files changed

+79
-0
lines changed

executor/common_kvm_amd64_syzos.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ typedef enum {
3636
SYZOS_API_NESTED_AMD_INVLPGA = 381,
3737
SYZOS_API_NESTED_AMD_STGI = 382,
3838
SYZOS_API_NESTED_AMD_CLGI = 383,
39+
SYZOS_API_NESTED_AMD_INJECT_EVENT = 384,
3940
SYZOS_API_STOP, // Must be the last one
4041
} syzos_api_id;
4142

@@ -119,6 +120,7 @@ GUEST_CODE static void guest_handle_nested_amd_vmcb_write_mask(struct api_call_5
119120
GUEST_CODE static void guest_handle_nested_amd_invlpga(struct api_call_2* cmd, uint64 cpu_id);
120121
GUEST_CODE static void guest_handle_nested_amd_stgi();
121122
GUEST_CODE static void guest_handle_nested_amd_clgi();
123+
GUEST_CODE static void guest_handle_nested_amd_inject_event(struct api_call_5* cmd, uint64 cpu_id);
122124

123125
typedef enum {
124126
UEXIT_END = (uint64)-1,
@@ -243,6 +245,9 @@ guest_main(uint64 size, uint64 cpu)
243245
} else if (call == SYZOS_API_NESTED_AMD_CLGI) {
244246
// Clear Global Interrupt Flag (Disable Interrupts, including NMI).
245247
guest_handle_nested_amd_clgi();
248+
} else if (call == SYZOS_API_NESTED_AMD_INJECT_EVENT) {
249+
// Inject an event (IRQ/Exception) into the L2 guest via VMCB.
250+
guest_handle_nested_amd_inject_event((struct api_call_5*)cmd, cpu);
246251
}
247252
addr += cmd->size;
248253
size -= cmd->size;
@@ -1326,4 +1331,32 @@ guest_handle_nested_amd_clgi()
13261331
asm volatile("clgi" ::: "memory");
13271332
}
13281333

1334+
GUEST_CODE static noinline void
1335+
guest_handle_nested_amd_inject_event(struct api_call_5* cmd, uint64 cpu_id)
1336+
{
1337+
if (get_cpu_vendor() != CPU_VENDOR_AMD)
1338+
return;
1339+
1340+
uint64 vm_id = cmd->args[0];
1341+
uint64 vmcb_addr = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
1342+
1343+
uint64 vector = cmd->args[1] & 0xFF;
1344+
uint64 type = cmd->args[2] & 0x7;
1345+
uint64 error_code = cmd->args[3] & 0xFFFFFFFF;
1346+
uint64 flags = cmd->args[4];
1347+
1348+
// Flags bit 0: Valid (V)
1349+
// Flags bit 1: Error Code Valid (EV)
1350+
uint64 event_inj = vector;
1351+
event_inj |= (type << 8);
1352+
if (flags & 2)
1353+
event_inj |= (1ULL << 11); // EV bit
1354+
if (flags & 1)
1355+
event_inj |= (1ULL << 31); // V bit
1356+
event_inj |= (error_code << 32);
1357+
1358+
// Write to VMCB Offset 0x60 (EVENTINJ)
1359+
vmcb_write64(vmcb_addr, 0x60, event_inj);
1360+
}
1361+
13291362
#endif // EXECUTOR_COMMON_KVM_AMD64_SYZOS_H

sys/linux/dev_kvm_amd64.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,14 @@ syzos_api_nested_amd_invlpga {
152152
asid int64[0:65535]
153153
}
154154

155+
syzos_api_nested_amd_inject_event {
156+
vm_id syzos_api_vm_id
157+
vector int64[0:255]
158+
type int64[0:7]
159+
error_code int64
160+
flags int64[0:3]
161+
}
162+
155163
# IDs here must match those in executor/common_kvm_amd64_syzos.h.
156164
syzos_api_call$x86 [
157165
uexit syzos_api$x86[0, intptr]
@@ -174,6 +182,7 @@ syzos_api_call$x86 [
174182
nested_amd_invlpga syzos_api$x86[381, syzos_api_nested_amd_invlpga]
175183
nested_amd_stgi syzos_api$x86[382, void]
176184
nested_amd_clgi syzos_api$x86[383, void]
185+
nested_amd_inject_event syzos_api$x86[384, syzos_api_nested_amd_inject_event]
177186
] [varlen]
178187

179188
kvm_text_x86 [
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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 AMD Nested Event Injection.
9+
#
10+
# 1. Setup nested environment (L1) and Create VM (L2).
11+
# 2. Inject an NMI (Vector 2, Type 2, Valid) into L2 via VMCB EVENTINJ.
12+
# 3. Launch L2.
13+
# - This forces KVM L0 to parse EVENTINJ and handle the injection.
14+
# - We expect L0 to succeed without crashing.
15+
#
16+
# Arguments for INJECT_EVENT:
17+
# vm_id=0
18+
# vector=2 (NMI)
19+
# type=2 (NMI)
20+
# error_code=0
21+
# flags=1 (Valid=1, EV=0)
22+
#
23+
r3 = syz_kvm_add_vcpu$x86(r2, &AUTO={0x0, &AUTO=[@enable_nested={AUTO, AUTO, 0x0}, @nested_create_vm={AUTO, AUTO, 0x0}, @nested_amd_inject_event={AUTO, AUTO, {0x0, 0x2, 0x2, 0x0, 0x1}}, @nested_vmlaunch={AUTO, AUTO, 0x0}, @uexit={AUTO, AUTO, 0xface}], AUTO})
24+
r4 = ioctl$KVM_GET_VCPU_MMAP_SIZE(r0, AUTO)
25+
r5 = mmap$KVM_VCPU(&(0x7f0000009000/0x1000)=nil, r4, 0x3, 0x1, r3, 0x0)
26+
27+
# Run the VCPU.
28+
# The guest executes the injection and launch.
29+
# If KVM L0 processes the EVENTINJ correctly, the guest should eventually exit back to us.
30+
#
31+
ioctl$KVM_RUN(r3, AUTO, 0x0)
32+
syz_kvm_assert_syzos_uexit$x86(r5, 0xface)
33+
34+
# Cleanup
35+
#
36+
ioctl$KVM_RUN(r3, AUTO, 0x0)
37+
syz_kvm_assert_syzos_uexit$x86(r5, 0xffffffff)

0 commit comments

Comments
 (0)