Skip to content

Commit 0a1338c

Browse files
executor: sys/linux: SYZOS: add support for AMD STGI and CLGI instructions
Implement the SYZOS_API_NESTED_AMD_STGI and SYZOS_API_NESTED_AMD_CLGI primitives to toggle the Global Interrupt Flag (GIF). These commands execute the stgi and clgi instructions respectively and require no arguments. Also add a test checking that CLGI correctly masks NMI injection from L0.
1 parent 8d22bd8 commit 0a1338c

File tree

3 files changed

+75
-0
lines changed

3 files changed

+75
-0
lines changed

executor/common_kvm_amd64_syzos.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ typedef enum {
3434
SYZOS_API_NESTED_INTEL_VMWRITE_MASK = 340,
3535
SYZOS_API_NESTED_AMD_VMCB_WRITE_MASK = 380,
3636
SYZOS_API_NESTED_AMD_INVLPGA = 381,
37+
SYZOS_API_NESTED_AMD_STGI = 382,
38+
SYZOS_API_NESTED_AMD_CLGI = 383,
3739
SYZOS_API_STOP, // Must be the last one
3840
} syzos_api_id;
3941

@@ -115,6 +117,8 @@ GUEST_CODE static void guest_handle_nested_vmresume(struct api_call_1* cmd, uint
115117
GUEST_CODE static void guest_handle_nested_intel_vmwrite_mask(struct api_call_5* cmd, uint64 cpu_id);
116118
GUEST_CODE static void guest_handle_nested_amd_vmcb_write_mask(struct api_call_5* cmd, uint64 cpu_id);
117119
GUEST_CODE static void guest_handle_nested_amd_invlpga(struct api_call_2* cmd, uint64 cpu_id);
120+
GUEST_CODE static void guest_handle_nested_amd_stgi();
121+
GUEST_CODE static void guest_handle_nested_amd_clgi();
118122

119123
typedef enum {
120124
UEXIT_END = (uint64)-1,
@@ -233,6 +237,12 @@ guest_main(uint64 size, uint64 cpu)
233237
} else if (call == SYZOS_API_NESTED_AMD_INVLPGA) {
234238
// Invalidate TLB mappings for the specified address/ASID.
235239
guest_handle_nested_amd_invlpga((struct api_call_2*)cmd, cpu);
240+
} else if (call == SYZOS_API_NESTED_AMD_STGI) {
241+
// Set Global Interrupt Flag (Enable Interrupts).
242+
guest_handle_nested_amd_stgi();
243+
} else if (call == SYZOS_API_NESTED_AMD_CLGI) {
244+
// Clear Global Interrupt Flag (Disable Interrupts, including NMI).
245+
guest_handle_nested_amd_clgi();
236246
}
237247
addr += cmd->size;
238248
size -= cmd->size;
@@ -1300,4 +1310,20 @@ guest_handle_nested_amd_invlpga(struct api_call_2* cmd, uint64 cpu_id)
13001310
asm volatile("invlpga" : : "a"(linear_addr), "c"(asid) : "memory");
13011311
}
13021312

1313+
GUEST_CODE static noinline void
1314+
guest_handle_nested_amd_stgi()
1315+
{
1316+
if (get_cpu_vendor() != CPU_VENDOR_AMD)
1317+
return;
1318+
asm volatile("stgi" ::: "memory");
1319+
}
1320+
1321+
GUEST_CODE static noinline void
1322+
guest_handle_nested_amd_clgi()
1323+
{
1324+
if (get_cpu_vendor() != CPU_VENDOR_AMD)
1325+
return;
1326+
asm volatile("clgi" ::: "memory");
1327+
}
1328+
13031329
#endif // EXECUTOR_COMMON_KVM_AMD64_SYZOS_H

sys/linux/dev_kvm_amd64.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ syzos_api_call$x86 [
172172
nested_intel_vmwrite_mask syzos_api$x86[340, syzos_api_nested_intel_vmwrite_mask]
173173
nested_amd_vmcb_write_mask syzos_api$x86[380, syzos_api_nested_amd_vmcb_write_mask]
174174
nested_amd_invlpga syzos_api$x86[381, syzos_api_nested_amd_invlpga]
175+
nested_amd_stgi syzos_api$x86[382, void]
176+
nested_amd_clgi syzos_api$x86[383, void]
175177
] [varlen]
176178

177179
kvm_text_x86 [
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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 SVM STGI/CLGI interaction with Host NMI Injection.
9+
#
10+
# 1. Guest executes CLGI (disabling interrupts).
11+
# 2. Guest executes UEXIT (0x1337) to yield to Host.
12+
# 3. Host injects an NMI via KVM_NMI.
13+
# - Because GIF=0, this NMI must remain PENDING and NOT be delivered yet.
14+
# 4. Guest resumes and executes STGI.
15+
# - NMI should be delivered immediately after STGI.
16+
#
17+
r3 = syz_kvm_add_vcpu$x86(r2, &AUTO={0x0, &AUTO=[@enable_nested={AUTO, AUTO, 0x0}, @nested_create_vm={AUTO, AUTO, 0x0}, @nested_amd_clgi={AUTO, AUTO, ""}, @uexit={AUTO, AUTO, 0x1337}, @nested_amd_stgi={AUTO, AUTO, ""}, @uexit={AUTO, AUTO, 0xface}], AUTO})
18+
r4 = ioctl$KVM_GET_VCPU_MMAP_SIZE(r0, AUTO)
19+
r5 = mmap$KVM_VCPU(&(0x7f0000009000/0x1000)=nil, r4, 0x3, 0x1, r3, 0x0)
20+
21+
# Run 1: Execute CLGI -> UEXIT(0x1337)
22+
#
23+
ioctl$KVM_RUN(r3, AUTO, 0x0)
24+
syz_kvm_assert_syzos_uexit$x86(r5, 0x1337)
25+
26+
# Inject NMI into the vCPU.
27+
# Since GIF=0 in the guest, this NMI should be queued by L0.
28+
#
29+
ioctl$KVM_NMI(r3, 0x0)
30+
31+
# Run 2: Resume -> STGI -> NMI Delivery -> UEXIT(0xface)
32+
# We verify that the guest survives the NMI delivery and reaches the final exit.
33+
# (If KVM fails to queue the NMI and delivers it early, or corrupts state, this may crash).
34+
#
35+
ioctl$KVM_RUN(r3, AUTO, 0x0)
36+
37+
# We check for successful completion.
38+
# Note: If NMI is delivered, it might cause a standard KVM exit depending on interception settings.
39+
# If the guest handles it transparently, we see 0xface.
40+
# For this regression test, ensuring we don't crash L0 is the primary goal.
41+
#
42+
syz_kvm_assert_syzos_uexit$x86(r5, 0xface)
43+
44+
# Cleanup.
45+
#
46+
ioctl$KVM_RUN(r3, AUTO, 0x0)
47+
syz_kvm_assert_syzos_uexit$x86(r5, 0xffffffff)

0 commit comments

Comments
 (0)