Skip to content

Commit e2d1759

Browse files
executor: sys/linux: SYZOS: add AMD SET_INTERCEPT primitive
This patch introduces SYZOS_API_NESTED_AMD_SET_INTERCEPT to SYZOS. This primitive enables the fuzzer to surgically modify intercept vectors in the AMD VMCB (Virtual Machine Control Block) Control Area. It implements a read-modify-write operation on 32-bit VMCB offsets, allowing the L1 hypervisor (SYZOS) to deterministically set or clear specific intercept bits (e.g., for RDTSC, HLT, or exceptions) for the L2 guest. This capability allows syzkaller to systematically explore KVM's nested SVM emulation logic by toggling intercepts on and off, rather than relying on static defaults or random memory corruption.
1 parent 8a9c6fb commit e2d1759

File tree

3 files changed

+80
-0
lines changed

3 files changed

+80
-0
lines changed

executor/common_kvm_amd64_syzos.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ typedef enum {
3737
SYZOS_API_NESTED_AMD_STGI = 382,
3838
SYZOS_API_NESTED_AMD_CLGI = 383,
3939
SYZOS_API_NESTED_AMD_INJECT_EVENT = 384,
40+
SYZOS_API_NESTED_AMD_SET_INTERCEPT = 385,
4041
SYZOS_API_STOP, // Must be the last one
4142
} syzos_api_id;
4243

@@ -121,6 +122,7 @@ GUEST_CODE static void guest_handle_nested_amd_invlpga(struct api_call_2* cmd, u
121122
GUEST_CODE static void guest_handle_nested_amd_stgi();
122123
GUEST_CODE static void guest_handle_nested_amd_clgi();
123124
GUEST_CODE static void guest_handle_nested_amd_inject_event(struct api_call_5* cmd, uint64 cpu_id);
125+
GUEST_CODE static void guest_handle_nested_amd_set_intercept(struct api_call_5* cmd, uint64 cpu_id);
124126

125127
typedef enum {
126128
UEXIT_END = (uint64)-1,
@@ -248,6 +250,9 @@ guest_main(uint64 size, uint64 cpu)
248250
} else if (call == SYZOS_API_NESTED_AMD_INJECT_EVENT) {
249251
// Inject an event (IRQ/Exception) into the L2 guest via VMCB.
250252
guest_handle_nested_amd_inject_event((struct api_call_5*)cmd, cpu);
253+
} else if (call == SYZOS_API_NESTED_AMD_SET_INTERCEPT) {
254+
// Set/Clear specific intercept bits in the VMCB.
255+
guest_handle_nested_amd_set_intercept((struct api_call_5*)cmd, cpu);
251256
}
252257
addr += cmd->size;
253258
size -= cmd->size;
@@ -570,6 +575,11 @@ GUEST_CODE static noinline void vmcb_write32(uint64 vmcb, uint16 offset, uint32
570575
*((volatile uint32*)(vmcb + offset)) = val;
571576
}
572577

578+
GUEST_CODE static noinline uint32 vmcb_read32(uint64 vmcb, uint16 offset)
579+
{
580+
return *((volatile uint32*)(vmcb + offset));
581+
}
582+
573583
GUEST_CODE static noinline void vmcb_write64(uint64 vmcb, uint16 offset, uint64 val)
574584
{
575585
*((volatile uint64*)(vmcb + offset)) = val;
@@ -1359,4 +1369,27 @@ guest_handle_nested_amd_inject_event(struct api_call_5* cmd, uint64 cpu_id)
13591369
vmcb_write64(vmcb_addr, 0x60, event_inj);
13601370
}
13611371

1372+
GUEST_CODE static noinline void
1373+
guest_handle_nested_amd_set_intercept(struct api_call_5* cmd, uint64 cpu_id)
1374+
{
1375+
if (get_cpu_vendor() != CPU_VENDOR_AMD)
1376+
return;
1377+
1378+
uint64 vm_id = cmd->args[0];
1379+
uint64 vmcb_addr = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
1380+
uint64 offset = cmd->args[1];
1381+
uint64 bit_mask = cmd->args[2];
1382+
uint64 action = cmd->args[3]; // 1 = Set, 0 = Clear
1383+
1384+
// Read 32-bit intercept field (Offsets 0x00 - 0x14 are all 32-bit vectors).
1385+
uint32 current = vmcb_read32(vmcb_addr, (uint16)offset);
1386+
1387+
if (action == 1)
1388+
current |= (uint32)bit_mask;
1389+
else
1390+
current &= ~((uint32)bit_mask);
1391+
1392+
vmcb_write32(vmcb_addr, (uint16)offset, current);
1393+
}
1394+
13621395
#endif // EXECUTOR_COMMON_KVM_AMD64_SYZOS_H

sys/linux/dev_kvm_amd64.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,13 @@ syzos_api_nested_amd_inject_event {
160160
flags int64[0:3]
161161
}
162162

163+
syzos_api_nested_amd_set_intercept {
164+
vm_id syzos_api_vm_id
165+
offset int64
166+
bit_mask int64
167+
action int64[0:1]
168+
}
169+
163170
# IDs here must match those in executor/common_kvm_amd64_syzos.h.
164171
syzos_api_call$x86 [
165172
uexit syzos_api$x86[0, intptr]
@@ -183,6 +190,7 @@ syzos_api_call$x86 [
183190
nested_amd_stgi syzos_api$x86[382, void]
184191
nested_amd_clgi syzos_api$x86[383, void]
185192
nested_amd_inject_event syzos_api$x86[384, syzos_api_nested_amd_inject_event]
193+
nested_amd_set_intercept syzos_api$x86[385, syzos_api_nested_amd_set_intercept]
186194
] [varlen]
187195

188196
kvm_text_x86 [
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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 Intercepts: The RDTSC Liberation
9+
#
10+
# 1. L1 Setup:
11+
# - SYZOS initializes VMCB with ALL intercepts enabled.
12+
# - We call SET_INTERCEPT to *CLEAR* the RDTSC intercept.
13+
# - Offset: 0xC (Control Vector 1)
14+
# - Bit: 14 (0x4000) = RDTSC
15+
# - Action: 0 (Clear/Disable)
16+
#
17+
# 2. L2 Payload:
18+
# - "rdtsc" (0x0f 0x31)
19+
# - "hlt" (0xf4)
20+
#
21+
# 3. Execution Flow:
22+
# - If RDTSC is intercepted: Exit Reason = RDTSC (0x4).
23+
# - If RDTSC is native: executes RDTSC -> executes HLT -> Exit Reason = HLT (0x1).
24+
#
25+
r3 = syz_kvm_add_vcpu$x86(r2, &AUTO={0x0, &AUTO=[@enable_nested={AUTO, AUTO, 0x0}, @nested_create_vm={AUTO, AUTO, 0x0}, @nested_amd_set_intercept={AUTO, AUTO, {0x0, 0xC, 0x4000, 0x0}}, @nested_load_code={AUTO, AUTO, {0x0, "0f31f4"}}, @nested_vmlaunch={AUTO, AUTO, 0x0}], AUTO})
26+
r4 = ioctl$KVM_GET_VCPU_MMAP_SIZE(r0, AUTO)
27+
r5 = mmap$KVM_VCPU(&(0x7f0000009000/0x1000)=nil, r4, 0x3, 0x1, r3, 0x0)
28+
29+
# Run the VCPU.
30+
# Expectation: Success (Native Execution) -> HLT Exit.
31+
# Code: 0xe2e20001 (SYZOS_NESTED_EXIT_REASON_HLT)
32+
#
33+
ioctl$KVM_RUN(r3, AUTO, 0x0)
34+
syz_kvm_assert_syzos_uexit$x86(r3, r5, 0xe2e20001)
35+
36+
# Cleanup
37+
#
38+
ioctl$KVM_RUN(r3, AUTO, 0x0)
39+
syz_kvm_assert_syzos_uexit$x86(r3, r5, 0xffffffffffffffff)

0 commit comments

Comments
 (0)