Skip to content

Commit 728dc14

Browse files
executor: sys/linux: implement SYZOS_API_NESTED_AMD_VMCB_WRITE_MASK
The new command allows mutation of AMD VMCB block with plain 64-bit writes. In addition to VM ID and VMCB offset, @nested_amd_vmcb_write_mask takes three 64-bit numbers: the set mask, the unset mask, and the flip mask. This allows to make bitwise modifications to VMCB without disturbing the execution too much. Also add sys/linux/test/amd64-syz_kvm_nested_amd_vmcb_write_mask to test the new command behavior.
1 parent accb0c3 commit 728dc14

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

executor/common_kvm_amd64_syzos.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ typedef enum {
3232
SYZOS_API_NESTED_VMLAUNCH = 303,
3333
SYZOS_API_NESTED_VMRESUME = 304,
3434
SYZOS_API_NESTED_INTEL_VMWRITE_MASK = 340,
35+
SYZOS_API_NESTED_AMD_VMCB_WRITE_MASK = 380,
3536
SYZOS_API_STOP, // Must be the last one
3637
} syzos_api_id;
3738

@@ -111,6 +112,7 @@ GUEST_CODE static void guest_handle_nested_load_code(struct api_call_nested_load
111112
GUEST_CODE static void guest_handle_nested_vmlaunch(struct api_call_1* cmd, uint64 cpu_id);
112113
GUEST_CODE static void guest_handle_nested_vmresume(struct api_call_1* cmd, uint64 cpu_id);
113114
GUEST_CODE static void guest_handle_nested_intel_vmwrite_mask(struct api_call_5* cmd, uint64 cpu_id);
115+
GUEST_CODE static void guest_handle_nested_amd_vmcb_write_mask(struct api_call_5* cmd, uint64 cpu_id);
114116

115117
typedef enum {
116118
UEXIT_END = (uint64)-1,
@@ -223,6 +225,9 @@ guest_main(uint64 size, uint64 cpu)
223225
} else if (call == SYZOS_API_NESTED_INTEL_VMWRITE_MASK) {
224226
// Write to a VMCS field using masks.
225227
guest_handle_nested_intel_vmwrite_mask((struct api_call_5*)cmd, cpu);
228+
} else if (call == SYZOS_API_NESTED_AMD_VMCB_WRITE_MASK) {
229+
// Write to a VMCB field using masks.
230+
guest_handle_nested_amd_vmcb_write_mask((struct api_call_5*)cmd, cpu);
226231
}
227232
addr += cmd->size;
228233
size -= cmd->size;
@@ -1256,4 +1261,22 @@ guest_handle_nested_intel_vmwrite_mask(struct api_call_5* cmd, uint64 cpu_id)
12561261
vmwrite(field, new_value);
12571262
}
12581263

1264+
GUEST_CODE static noinline void
1265+
guest_handle_nested_amd_vmcb_write_mask(struct api_call_5* cmd, uint64 cpu_id)
1266+
{
1267+
if (get_cpu_vendor() != CPU_VENDOR_AMD)
1268+
return;
1269+
uint64 vm_id = cmd->args[0];
1270+
uint64 vmcb_addr = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
1271+
uint64 offset = cmd->args[1];
1272+
uint64 set_mask = cmd->args[2];
1273+
uint64 unset_mask = cmd->args[3];
1274+
uint64 flip_mask = cmd->args[4];
1275+
1276+
uint64 current_value = vmcb_read64((volatile uint8*)vmcb_addr, offset);
1277+
uint64 new_value = (current_value & ~unset_mask) | set_mask;
1278+
new_value ^= flip_mask;
1279+
vmcb_write64(vmcb_addr, offset, new_value);
1280+
}
1281+
12591282
#endif // EXECUTOR_COMMON_KVM_AMD64_SYZOS_H

sys/linux/dev_kvm_amd64.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,21 @@ syzos_api_nested_intel_vmwrite_mask {
131131
flip_mask int64
132132
}
133133

134+
# See AMD Secure Virtual Machine Architecture Reference Manual,
135+
# Appendix C: Layout of VMCB.
136+
vmcb_offset [
137+
control_area int64[0x0:0xc0]
138+
save_area int64[0x400:0x698]
139+
]
140+
141+
syzos_api_nested_amd_vmcb_write_mask {
142+
vm_id syzos_api_vm_id
143+
offset vmcb_offset
144+
set_mask int64
145+
unset_mask int64
146+
flip_mask int64
147+
}
148+
134149
# IDs here must match those in executor/common_kvm_amd64_syzos.h.
135150
syzos_api_call$x86 [
136151
uexit syzos_api$x86[0, intptr]
@@ -149,6 +164,7 @@ syzos_api_call$x86 [
149164
nested_vmlaunch syzos_api$x86[303, syzos_api_vm_id]
150165
nested_vmresume syzos_api$x86[304, syzos_api_vm_id]
151166
nested_intel_vmwrite_mask syzos_api$x86[340, syzos_api_nested_intel_vmwrite_mask]
167+
nested_amd_vmcb_write_mask syzos_api$x86[380, syzos_api_nested_amd_vmcb_write_mask]
152168
] [varlen]
153169

154170
kvm_text_x86 [
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
# Create a nested AMD VM that performs HLT and INVD to test vmcb_write_mask.
9+
# This is the AMD equivalent of the amd64-syz_kvm_nested_vmwrite_mask test.
10+
# 1. L2 executes HLT -> exit to L1 (HLT is intercepted by default).
11+
# 2. L1 disables HLT intercept via vmcb_write_mask.
12+
# 3. L1 resumes L2.
13+
# 4. L2 executes HLT (no L2->L1 exit), causing an L1->L0 KVM_EXIT_HLT.
14+
# 5. L0 resumes L1, which resumes L2.
15+
# 6. L2 executes INVD -> exit to L1.
16+
#
17+
# The HLT intercept bit is bit 24 in the VMCB's intercept vector 3 (offset 0xC).
18+
# We unset this bit (0x1000000).
19+
#
20+
r3 = syz_kvm_add_vcpu$x86(r2, &AUTO={0x0, &AUTO=[@enable_nested={AUTO, AUTO, 0x0}, @nested_create_vm={AUTO, AUTO, 0x0}, @nested_load_code={AUTO, AUTO, {0x0, "f40f08"}}, @nested_vmlaunch={AUTO, AUTO, 0x0}, @nested_amd_vmcb_write_mask={AUTO, AUTO, {0x0, @control_area=0xc, 0x0, 0x1000000, 0x0}}, @nested_vmresume={AUTO, AUTO, 0x0}], AUTO})
21+
r4 = ioctl$KVM_GET_VCPU_MMAP_SIZE(r0, AUTO)
22+
r5 = mmap$KVM_VCPU(&(0x7f0000009000/0x1000)=nil, r4, 0x3, 0x1, r3, 0x0)
23+
24+
# L2 VM executes HLT. Exit reason is mapped to 0xe2e20001.
25+
#
26+
ioctl$KVM_RUN(r3, AUTO, 0x0)
27+
syz_kvm_assert_syzos_uexit$x86(r5, 0xe2e20001)
28+
29+
# L1 disables HLT intercept and resumes L2. L2 executes HLT, causing KVM_EXIT_HLT (0x5) from L1 to L0.
30+
# This confirms that the vmcb_write_mask command was successful.
31+
#
32+
ioctl$KVM_RUN(r3, AUTO, 0x0)
33+
syz_kvm_assert_syzos_kvm_exit$x86(r5, 0x5)
34+
35+
# After resuming, L2 executes INVD. Exit reason is mapped to 0xe2e20002.
36+
#
37+
ioctl$KVM_RUN(r3, AUTO, 0x0)
38+
syz_kvm_assert_syzos_uexit$x86(r5, 0xe2e20002)
39+
40+
# guest_main should finish with guest_uexit(-1).
41+
#
42+
ioctl$KVM_RUN(r3, AUTO, 0x0)
43+
syz_kvm_assert_syzos_uexit$x86(r5, 0xffffffff)

0 commit comments

Comments
 (0)