aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Potapenko <glider@google.com>2026-01-20 11:24:50 +0100
committerAlexander Potapenko <glider@google.com>2026-01-20 11:17:07 +0000
commit88c380012ee818101167d1400550823398a15845 (patch)
treef4888efd28872940c3454db27fa43163449689bb
parent06648d9ccf5ec6f9453ef09b0fdbdebf020ee0f8 (diff)
executor: sys/linux: SYZOS: add AMD VMLOAD and VMSAVE primitives
This patch introduces SYZOS_API_NESTED_AMD_VMLOAD and SYZOS_API_NESTED_AMD_VMSAVE. These primitives allow the L1 guest to execute the VMLOAD and VMSAVE instructions, which load/store additional guest state (FS, GS, TR, LDTR, etc.) to/from the VMCB specified by the 'vm_id' argument. This stresses the KVM L0 instruction emulator, which must validate the L1-provided physical address in RAX and perform the state transfer.
-rw-r--r--executor/common_kvm_amd64_syzos.h32
-rw-r--r--sys/linux/dev_kvm_amd64.txt2
-rw-r--r--sys/linux/test/amd64-syz_kvm_nested_vmload_vmsave11
3 files changed, 45 insertions, 0 deletions
diff --git a/executor/common_kvm_amd64_syzos.h b/executor/common_kvm_amd64_syzos.h
index 31f62a79c..5584a62b9 100644
--- a/executor/common_kvm_amd64_syzos.h
+++ b/executor/common_kvm_amd64_syzos.h
@@ -38,6 +38,8 @@ typedef enum {
SYZOS_API_NESTED_AMD_CLGI = 383,
SYZOS_API_NESTED_AMD_INJECT_EVENT = 384,
SYZOS_API_NESTED_AMD_SET_INTERCEPT = 385,
+ SYZOS_API_NESTED_AMD_VMLOAD = 386,
+ SYZOS_API_NESTED_AMD_VMSAVE = 387,
SYZOS_API_STOP, // Must be the last one
} syzos_api_id;
@@ -123,6 +125,8 @@ GUEST_CODE static void guest_handle_nested_amd_stgi();
GUEST_CODE static void guest_handle_nested_amd_clgi();
GUEST_CODE static void guest_handle_nested_amd_inject_event(struct api_call_5* cmd, uint64 cpu_id);
GUEST_CODE static void guest_handle_nested_amd_set_intercept(struct api_call_5* cmd, uint64 cpu_id);
+GUEST_CODE static void guest_handle_nested_amd_vmload(struct api_call_1* cmd, uint64 cpu_id);
+GUEST_CODE static void guest_handle_nested_amd_vmsave(struct api_call_1* cmd, uint64 cpu_id);
typedef enum {
UEXIT_END = (uint64)-1,
@@ -253,6 +257,12 @@ guest_main(uint64 size, uint64 cpu)
} else if (call == SYZOS_API_NESTED_AMD_SET_INTERCEPT) {
// Set/Clear specific intercept bits in the VMCB.
guest_handle_nested_amd_set_intercept((struct api_call_5*)cmd, cpu);
+ } else if (call == SYZOS_API_NESTED_AMD_VMLOAD) {
+ // Execute VMLOAD to load state from VMCB.
+ guest_handle_nested_amd_vmload((struct api_call_1*)cmd, cpu);
+ } else if (call == SYZOS_API_NESTED_AMD_VMSAVE) {
+ // Execute VMSAVE to save state to VMCB.
+ guest_handle_nested_amd_vmsave((struct api_call_1*)cmd, cpu);
}
addr += cmd->size;
size -= cmd->size;
@@ -1392,4 +1402,26 @@ guest_handle_nested_amd_set_intercept(struct api_call_5* cmd, uint64 cpu_id)
vmcb_write32(vmcb_addr, (uint16)offset, current);
}
+GUEST_CODE static noinline void
+guest_handle_nested_amd_vmload(struct api_call_1* cmd, uint64 cpu_id)
+{
+ if (get_cpu_vendor() != CPU_VENDOR_AMD)
+ return;
+ uint64 vm_id = cmd->arg;
+ uint64 vmcb_pa = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
+
+ asm volatile("vmload %%rax" ::"a"(vmcb_pa) : "memory");
+}
+
+GUEST_CODE static noinline void
+guest_handle_nested_amd_vmsave(struct api_call_1* cmd, uint64 cpu_id)
+{
+ if (get_cpu_vendor() != CPU_VENDOR_AMD)
+ return;
+ uint64 vm_id = cmd->arg;
+ uint64 vmcb_pa = X86_SYZOS_ADDR_VMCS_VMCB(cpu_id, vm_id);
+
+ asm volatile("vmsave %%rax" ::"a"(vmcb_pa) : "memory");
+}
+
#endif // EXECUTOR_COMMON_KVM_AMD64_SYZOS_H
diff --git a/sys/linux/dev_kvm_amd64.txt b/sys/linux/dev_kvm_amd64.txt
index 0d90ceeab..4694f93cc 100644
--- a/sys/linux/dev_kvm_amd64.txt
+++ b/sys/linux/dev_kvm_amd64.txt
@@ -191,6 +191,8 @@ syzos_api_call$x86 [
nested_amd_clgi syzos_api$x86[383, void]
nested_amd_inject_event syzos_api$x86[384, syzos_api_nested_amd_inject_event]
nested_amd_set_intercept syzos_api$x86[385, syzos_api_nested_amd_set_intercept]
+ nested_amd_vmload syzos_api$x86[386, syzos_api_vm_id]
+ nested_amd_vmsave syzos_api$x86[387, syzos_api_vm_id]
] [varlen]
kvm_text_x86 [
diff --git a/sys/linux/test/amd64-syz_kvm_nested_vmload_vmsave b/sys/linux/test/amd64-syz_kvm_nested_vmload_vmsave
new file mode 100644
index 000000000..c8e169f61
--- /dev/null
+++ b/sys/linux/test/amd64-syz_kvm_nested_vmload_vmsave
@@ -0,0 +1,11 @@
+#
+# requires: arch=amd64 -threaded
+#
+
+# VMLOAD/VMSAVE Reproducer
+#
+r0 = openat$kvm(0xffffffffffffff9c, &AUTO='/dev/kvm\x00', 0x0, 0x0)
+r1 = ioctl$KVM_CREATE_VM(r0, 0x0, 0x0)
+r2 = syz_kvm_setup_syzos_vm$x86(r1, &(0x7f0000000000/0x400000)=nil)
+r3 = syz_kvm_add_vcpu$x86(r2, &AUTO={0x0, &AUTO=[@enable_nested={0x12c, 0x18, 0x0}, @nested_create_vm={0x12d, 0x18, 0x1}, @nested_amd_vmsave={0x183, 0x18, 0x1}, @nested_amd_vmload={0x182, 0x18, 0x1}], 0x0})
+ioctl$KVM_RUN(r3, 0x0, 0x0)