From: Mathias Krause <minipli@grsecurity.net>
To: Sean Christopherson <seanjc@google.com>
Cc: kvm@vger.kernel.org, kvmarm@lists.linux.dev,
Alexandru Elisei <alexandru.elisei@arm.com>,
Andrew Jones <andrew.jones@linux.dev>,
Eric Auger <eric.auger@redhat.com>,
Thomas Huth <thuth@redhat.com>,
Paolo Bonzini <pbonzini@redhat.com>
Subject: Re: [kvm-unit-tests PATCH v2 0/4] Better backtraces for leaf functions
Date: Fri, 21 Nov 2025 17:44:45 +0100 [thread overview]
Message-ID: <3bac29b9-4c49-4e5d-997e-9e4019a2fceb@grsecurity.net> (raw)
In-Reply-To: <0274322e-e28c-4511-a565-6bb85bfade8b@grsecurity.net>
[-- Attachment #1: Type: text/plain, Size: 3674 bytes --]
On 18.11.25 02:47, Mathias Krause wrote:
> On 18.11.25 02:33, Mathias Krause wrote:
> [...]
> Bleh, I just noticed, f01ea38a385a ("x86: Better backtraces for leaf
> functions") broke vmx_sipi_signal_test too :(
>
> Looking into it!
Finally found it. It's register corruption within both host and guest.
It's not related to f01ea38a385a at all but, apparently, it actually
exposes it, likely because of the enforced stack frame setup, making the
code rely on (a corrupted) RBP instead of the properly restored (because
VMCS managed) RSP.
The core issue is, 'regs' being a singleton, used by multiple CPUs, so
all SMP VMX tests concurrently making use of vmx_enter_guest() are
potentially affected.
When the first vCPU calls vmx_enter_guest() to launch a guest, it'll use
'regs' to load the guest registers but also store its host register
state. Now, if while that vCPU is running, another vCPU gets launched
via vmx_enter_guest(), it'll load the previous vCPU's host register
values as guest register state and store its host registers in 'regs'.
Depending on which vCPU returns first, it'll either load the other
vCPU's host registers effectively "switching threads" or, if it's the
vCPU that called vmx_enter_guest() last, it'll resume just fine. Either
way, the next vCPU returning will run with the guest register values.
The latter is what happens with vmx_sipi_signal_test, causing the crash.
I read a lot of vmx.c and vmx_test.c in the last few days and it's
really not meant to be used concurrently by multiple guests. vmx_test.c
has quite some hacks to work around obvious limitations (allocating
dedicated stacks for APs) but state variables like 'launched',
'in_guest', 'guest_finished', 'hypercall_field' and 'regs' are shared
but really meant to be used only by a single thread.
I hacked up something to verify my theory and made 'regs' "per-cpu". It
needs quite some code churn and I'm not all that happy with it. IMHO,
'regs' and lots of the other VMX management state should be part of some
vcpu struct or something. In fact, struct vmx_test already has a
'guest_regs' but using it won't work, as we need offsetable absolute
memory references for the inline ASM in vmx_enter_guest() to work as it
cannot make use of register-based memory references at all. (My hack
uses a global 'scratch_regs' with mutual exclusion on its usage.)
To see the register corruption, one could start the vmx_sipi_signal_test
test with -s -S, attach gdb to it and add a watch for regs.rax. Stepping
through the test will clearly show how 'regs' get overwritten wrongly.
In one shell:
$ ./x86-run x86/vmx.flat -append vmx_sipi_signal_test \
-cpu max,+vmx -smp 2 -s -S
In another:
$ gdb -q -ex 'target remote :1234' x86/vmx.elf
Reading symbols from x86/vmx.elf...
Remote debugging using :1234
0x000000000000fff0 in ?? ()
(gdb) watch regs.rax
Hardware watchpoint 1: regs.rax
(gdb) c
Continuing.
Thread 1 hit Hardware watchpoint 1: regs.rax
Old value = 0
New value = 7589640
0x00000000004006b5 in vmx_enter_guest (result=result@entry=0x73cf08) at
x86/vmx.c:1754
1754 asm volatile (
(gdb)
Continuing.
[Switching to Thread 1.2]
Thread 2 hit Hardware watchpoint 1: regs.rax
Old value = 7589640
New value = 7577392
0x00000000004006b5 in vmx_enter_guest (result=result@entry=0x739f30) at
x86/vmx.c:1754
1754 asm volatile (
(gdb)
Continuing.
Interrupting the test like this even makes it hang for me but above
already shows that vCPU 1 makes use of 'regs' to load and store register
state, followed by vCPU 2, doing the same, destroying vCPU 1's stored
host register state. Bummer!
Now, the question is, how to properly fix that. (No, not my hack! :P)
Thanks,
Mathias
[-- Attachment #2: 0001-x86-hacky-attempt-to-make-regs-per-cpu.patch --]
[-- Type: text/x-patch, Size: 26010 bytes --]
From 3fb07081769ae6680ac732168aab23cf45eaedc6 Mon Sep 17 00:00:00 2001
From: Mathias Krause <minipli@grsecurity.net>
Date: Fri, 21 Nov 2025 17:34:19 +0100
Subject: [kvm-unit-tests PATCH] x86: hacky attempt to make 'regs' per-cpu
The current concurrent use of globals like 'regs' causes register
corruption when multiple CPUs are executing vmx_enter_guest() in
parallel.
Signed-off-by: Mathias Krause <minipli@grsecurity.net>
---
x86/vmx.h | 121 +++++++++++++++++++++++++++++++++--------------
x86/vmx.c | 70 ++++++++++++++++++---------
x86/vmx_tests.c | 122 ++++++++++++++++++++++++------------------------
3 files changed, 196 insertions(+), 117 deletions(-)
diff --git a/x86/vmx.h b/x86/vmx.h
index 33373bd1a2a9..3dfd5d0c2e71 100644
--- a/x86/vmx.h
+++ b/x86/vmx.h
@@ -120,7 +120,7 @@ struct vmx_test {
const char *name;
int (*init)(struct vmcs *vmcs);
void (*guest_main)(void);
- int (*exit_handler)(union exit_reason exit_reason);
+ int (*exit_handler)(struct regs *regs, union exit_reason exit_reason);
void (*syscall_handler)(u64 syscall_no);
struct regs guest_regs;
int (*entry_failure_handler)(struct vmentry_result *result);
@@ -588,43 +588,92 @@ enum vm_entry_failure_code {
ENTRY_FAIL_VMCS_LINK_PTR = 4,
};
-#define SAVE_GPR \
- "xchg %rax, regs\n\t" \
- "xchg %rcx, regs+0x8\n\t" \
- "xchg %rdx, regs+0x10\n\t" \
- "xchg %rbx, regs+0x18\n\t" \
- "xchg %rbp, regs+0x28\n\t" \
- "xchg %rsi, regs+0x30\n\t" \
- "xchg %rdi, regs+0x38\n\t" \
- "xchg %r8, regs+0x40\n\t" \
- "xchg %r9, regs+0x48\n\t" \
- "xchg %r10, regs+0x50\n\t" \
- "xchg %r11, regs+0x58\n\t" \
- "xchg %r12, regs+0x60\n\t" \
- "xchg %r13, regs+0x68\n\t" \
- "xchg %r14, regs+0x70\n\t" \
- "xchg %r15, regs+0x78\n\t"
+#define SAVE_GPR(regs) \
+ "xchg %rax, " regs "\n\t" \
+ "xchg %rcx, " regs "+0x8\n\t" \
+ "xchg %rdx, " regs "+0x10\n\t" \
+ "xchg %rbx, " regs "+0x18\n\t" \
+ "xchg %rbp, " regs "+0x28\n\t" \
+ "xchg %rsi, " regs "+0x30\n\t" \
+ "xchg %rdi, " regs "+0x38\n\t" \
+ "xchg %r8, " regs "+0x40\n\t" \
+ "xchg %r9, " regs "+0x48\n\t" \
+ "xchg %r10, " regs "+0x50\n\t" \
+ "xchg %r11, " regs "+0x58\n\t" \
+ "xchg %r12, " regs "+0x60\n\t" \
+ "xchg %r13, " regs "+0x68\n\t" \
+ "xchg %r14, " regs "+0x70\n\t" \
+ "xchg %r15, " regs "+0x78\n\t"
#define LOAD_GPR SAVE_GPR
-#define SAVE_GPR_C \
- "xchg %%rax, regs\n\t" \
- "xchg %%rcx, regs+0x8\n\t" \
- "xchg %%rdx, regs+0x10\n\t" \
- "xchg %%rbx, regs+0x18\n\t" \
- "xchg %%rbp, regs+0x28\n\t" \
- "xchg %%rsi, regs+0x30\n\t" \
- "xchg %%rdi, regs+0x38\n\t" \
- "xchg %%r8, regs+0x40\n\t" \
- "xchg %%r9, regs+0x48\n\t" \
- "xchg %%r10, regs+0x50\n\t" \
- "xchg %%r11, regs+0x58\n\t" \
- "xchg %%r12, regs+0x60\n\t" \
- "xchg %%r13, regs+0x68\n\t" \
- "xchg %%r14, regs+0x70\n\t" \
- "xchg %%r15, regs+0x78\n\t"
+#define PUSH_GPR_C \
+ "push %%rax\n\t" \
+ "push %%rcx\n\t" \
+ "push %%rdx\n\t" \
+ "push %%rbx\n\t" \
+ "push %%rbp\n\t" \
+ "push %%rsi\n\t" \
+ "push %%rdi\n\t" \
+ "push %%r8\n\t" \
+ "push %%r9\n\t" \
+ "push %%r10\n\t" \
+ "push %%r11\n\t" \
+ "push %%r12\n\t" \
+ "push %%r13\n\t" \
+ "push %%r14\n\t" \
+ "push %%r15\n\t"
-#define LOAD_GPR_C SAVE_GPR_C
+#define LOAD_GPR_C(regs) \
+ "mov " regs ", %%rax\n\t" \
+ "mov " regs "+0x8, %%rcx\n\t" \
+ "mov " regs "+0x10, %%rdx\n\t" \
+ "mov " regs "+0x18, %%rbx\n\t" \
+ "mov " regs "+0x28, %%rbp\n\t" \
+ "mov " regs "+0x30, %%rsi\n\t" \
+ "mov " regs "+0x38, %%rdi\n\t" \
+ "mov " regs "+0x40, %%r8\n\t" \
+ "mov " regs "+0x48, %%r9\n\t" \
+ "mov " regs "+0x50, %%r10\n\t" \
+ "mov " regs "+0x58, %%r11\n\t" \
+ "mov " regs "+0x60, %%r12\n\t" \
+ "mov " regs "+0x68, %%r13\n\t" \
+ "mov " regs "+0x70, %%r14\n\t" \
+ "mov " regs "+0x78, %%r15\n\t"
+
+#define SAFE_GPR_C(regs) \
+ "mov %%rax, " regs "\n\t" \
+ "mov %%rcx, " regs "+0x8\n\t" \
+ "mov %%rdx, " regs "+0x10\n\t" \
+ "mov %%rbx, " regs "+0x18\n\t" \
+ "mov %%rbp, " regs "+0x28\n\t" \
+ "mov %%rsi, " regs "+0x30\n\t" \
+ "mov %%rdi, " regs "+0x38\n\t" \
+ "mov %%r8, " regs "+0x40\n\t" \
+ "mov %%r9, " regs "+0x48\n\t" \
+ "mov %%r10, " regs "+0x50\n\t" \
+ "mov %%r11, " regs "+0x58\n\t" \
+ "mov %%r12, " regs "+0x60\n\t" \
+ "mov %%r13, " regs "+0x68\n\t" \
+ "mov %%r14, " regs "+0x70\n\t" \
+ "mov %%r15, " regs "+0x78\n\t"
+
+#define POP_GPR_C \
+ "pop %%r15\n\t" \
+ "pop %%r14\n\t" \
+ "pop %%r13\n\t" \
+ "pop %%r12\n\t" \
+ "pop %%r11\n\t" \
+ "pop %%r10\n\t" \
+ "pop %%r9\n\t" \
+ "pop %%r8\n\t" \
+ "pop %%rdi\n\t" \
+ "pop %%rsi\n\t" \
+ "pop %%rbp\n\t" \
+ "pop %%rbx\n\t" \
+ "pop %%rdx\n\t" \
+ "pop %%rcx\n\t" \
+ "pop %%rax\n\t"
#define VMX_IO_SIZE_MASK 0x7
#define _VMX_IO_BYTE 0
@@ -755,7 +804,7 @@ enum vm_entry_failure_code {
#define VMCS_FIELD_RESERVED_SHIFT (15)
#define VMCS_FIELD_BIT_SIZE (BITS_PER_LONG)
-extern struct regs regs;
+struct regs *guest_regs(void);
extern union vmx_basic_msr basic_msr;
extern union vmx_ctrl_msr ctrl_pin_rev;
@@ -1017,7 +1066,7 @@ void init_vmx(u64 *vmxon_region);
int init_vmcs(struct vmcs **vmcs);
const char *exit_reason_description(u64 reason);
-void print_vmexit_info(union exit_reason exit_reason);
+void print_vmexit_info(struct regs *regs, union exit_reason exit_reason);
void print_vmentry_failure_info(struct vmentry_result *result);
void install_ept_entry(unsigned long *pml4, int pte_level,
unsigned long guest_addr, unsigned long pte,
diff --git a/x86/vmx.c b/x86/vmx.c
index c803eaa67ac6..2a3d79a4991c 100644
--- a/x86/vmx.c
+++ b/x86/vmx.c
@@ -44,7 +44,14 @@ struct vmcs *vmcs_root;
u32 vpid_cnt;
u64 guest_stack_top, guest_syscall_stack_top;
u32 ctrl_pin, ctrl_enter, ctrl_exit, ctrl_cpu[2];
-struct regs regs;
+static struct regs regs[MAX_TEST_CPUS];
+struct regs sysenter_regs, scratch_regs;
+static volatile bool regs_in_use;
+
+struct regs *guest_regs(void)
+{
+ return ®s[smp_id()];
+}
struct vmx_test *current;
@@ -566,11 +573,11 @@ asm(
".align 4, 0x90\n\t"
".globl entry_sysenter\n\t"
"entry_sysenter:\n\t"
- SAVE_GPR
+ SAVE_GPR("sysenter_regs")
" and $0xf, %rax\n\t"
" mov %rax, %rdi\n\t"
" call syscall_handler\n\t"
- LOAD_GPR
+ LOAD_GPR("sysenter_regs")
" vmresume\n\t"
);
@@ -650,7 +657,7 @@ const char *exit_reason_description(u64 reason)
return exit_reason_descriptions[reason] ? : "(unused)";
}
-void print_vmexit_info(union exit_reason exit_reason)
+void print_vmexit_info(struct regs *regs, union exit_reason exit_reason)
{
u64 guest_rip, guest_rsp;
ulong exit_qual = vmcs_read(EXI_QUALIFICATION);
@@ -662,13 +669,13 @@ void print_vmexit_info(union exit_reason exit_reason)
printf("\texit qualification = %#lx\n", exit_qual);
printf("\tguest_rip = %#lx\n", guest_rip);
printf("\tRAX=%#lx RBX=%#lx RCX=%#lx RDX=%#lx\n",
- regs.rax, regs.rbx, regs.rcx, regs.rdx);
+ regs->rax, regs->rbx, regs->rcx, regs->rdx);
printf("\tRSP=%#lx RBP=%#lx RSI=%#lx RDI=%#lx\n",
- guest_rsp, regs.rbp, regs.rsi, regs.rdi);
+ guest_rsp, regs->rbp, regs->rsi, regs->rdi);
printf("\tR8 =%#lx R9 =%#lx R10=%#lx R11=%#lx\n",
- regs.r8, regs.r9, regs.r10, regs.r11);
+ regs->r8, regs->r9, regs->r10, regs->r11);
printf("\tR12=%#lx R13=%#lx R14=%#lx R15=%#lx\n",
- regs.r12, regs.r13, regs.r14, regs.r15);
+ regs->r12, regs->r13, regs->r14, regs->r15);
}
void print_vmentry_failure_info(struct vmentry_result *result)
@@ -1727,17 +1734,17 @@ void test_skip(const char *msg)
abort();
}
-static int exit_handler(union exit_reason exit_reason)
+static int exit_handler(struct regs *regs, union exit_reason exit_reason)
{
int ret;
current->exits++;
- regs.rflags = vmcs_read(GUEST_RFLAGS);
+ regs->rflags = vmcs_read(GUEST_RFLAGS);
if (is_hypercall(exit_reason))
ret = handle_hypercall();
else
- ret = current->exit_handler(exit_reason);
- vmcs_write(GUEST_RFLAGS, regs.rflags);
+ ret = current->exit_handler(regs, exit_reason);
+ vmcs_write(GUEST_RFLAGS, regs->rflags);
return ret;
}
@@ -1750,11 +1757,22 @@ static noinline void vmx_enter_guest(struct vmentry_result *result)
{
memset(result, 0, sizeof(*result));
+ // XXX: atomic compare-and-set!
+ while (regs_in_use)
+ cpu_relax();
+
+ regs_in_use = 1;
+ barrier();
+
+ scratch_regs = *guest_regs();
in_guest = 1;
asm volatile (
+ PUSH_GPR_C
"mov %[HOST_RSP], %%rdi\n\t"
"vmwrite %%rsp, %%rdi\n\t"
- LOAD_GPR_C
+ LOAD_GPR_C("scratch_regs")
+ "movb $0, regs_in_use\n\t"
+
"cmpb $0, %[launched]\n\t"
"jne 1f\n\t"
"vmlaunch\n\t"
@@ -1762,21 +1780,31 @@ static noinline void vmx_enter_guest(struct vmentry_result *result)
"1: "
"vmresume\n\t"
"2: "
- SAVE_GPR_C
+
+ // XXX: loop until it's 0!
+ "movb $1, regs_in_use\n\t"
+ SAFE_GPR_C("scratch_regs")
"pushf\n\t"
"pop %%rdi\n\t"
"mov %%rdi, %[vm_fail_flags]\n\t"
"movl $1, %[vm_fail]\n\t"
"jmp 3f\n\t"
+
"vmx_return:\n\t"
- SAVE_GPR_C
+ // XXX: loop until it's 0!
+ "movb $1, regs_in_use\n\t"
+ SAFE_GPR_C("scratch_regs")
"3: \n\t"
+ POP_GPR_C
: [vm_fail]"+m"(result->vm_fail),
[vm_fail_flags]"=m"(result->flags)
: [launched]"m"(launched), [HOST_RSP]"i"(HOST_RSP)
- : "rdi", "memory", "cc"
+ : "memory", "cc"
);
in_guest = 0;
+ regs[smp_id()] = scratch_regs;
+ barrier();
+ regs_in_use = 0;
result->vmlaunch = !launched;
result->instr = launched ? "vmresume" : "vmlaunch";
@@ -1799,7 +1827,7 @@ static int vmx_run(void)
* entry failure (early or otherwise).
*/
launched = 1;
- ret = exit_handler(result.exit_reason);
+ ret = exit_handler(guest_regs(), result.exit_reason);
} else if (current->entry_failure_handler) {
ret = current->entry_failure_handler(&result);
} else {
@@ -1822,7 +1850,7 @@ static int vmx_run(void)
}
if (result.entered)
- print_vmexit_info(result.exit_reason);
+ print_vmexit_info(regs, result.exit_reason);
else
print_vmentry_failure_info(&result);
abort();
@@ -1836,6 +1864,7 @@ static void run_teardown_step(struct test_teardown_step *step)
static int test_run(struct vmx_test *test)
{
+ struct regs *vm_regs = guest_regs();
int r;
/* Validate V2 interface. */
@@ -1866,8 +1895,8 @@ static int test_run(struct vmx_test *test)
v2_guest_main = NULL;
test->exits = 0;
current = test;
- regs = test->guest_regs;
- vmcs_write(GUEST_RFLAGS, regs.rflags | X86_EFLAGS_FIXED);
+ *vm_regs = test->guest_regs;
+ vmcs_write(GUEST_RFLAGS, vm_regs->rflags | X86_EFLAGS_FIXED);
launched = 0;
guest_finished = 0;
printf("\nTest suite: %s\n", test->name);
@@ -1878,7 +1907,6 @@ static int test_run(struct vmx_test *test)
goto out;
}
-
if (test->v2)
test->v2();
else
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index 5ffb80a3d866..490dd52fbf80 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -75,10 +75,10 @@ static void basic_guest_main(void)
report_pass("Basic VMX test");
}
-static int basic_exit_handler(union exit_reason exit_reason)
+static int basic_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
report_fail("Basic VMX test");
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
return VMX_TEST_EXIT;
}
@@ -100,22 +100,22 @@ static void vmenter_main(void)
report((rax == 0xFFFF) && (rsp == resume_rsp), "test vmresume");
}
-static int vmenter_exit_handler(union exit_reason exit_reason)
+static int vmenter_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
u64 guest_rip = vmcs_read(GUEST_RIP);
switch (exit_reason.basic) {
case VMX_VMCALL:
- if (regs.rax != 0xABCD) {
+ if (regs->rax != 0xABCD) {
report_fail("test vmresume");
return VMX_TEST_VMEXIT;
}
- regs.rax = 0xFFFF;
+ regs->rax = 0xFFFF;
vmcs_write(GUEST_RIP, guest_rip + 3);
return VMX_TEST_RESUME;
default:
report_fail("test vmresume");
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
}
return VMX_TEST_VMEXIT;
}
@@ -166,7 +166,7 @@ static void preemption_timer_main(void)
vmcall();
}
-static int preemption_timer_exit_handler(union exit_reason exit_reason)
+static int preemption_timer_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
bool guest_halted;
u64 guest_rip;
@@ -204,7 +204,7 @@ static int preemption_timer_exit_handler(union exit_reason exit_reason)
break;
default:
report_fail("Invalid stage.");
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
break;
}
break;
@@ -246,13 +246,13 @@ static int preemption_timer_exit_handler(union exit_reason exit_reason)
// Should not reach here
report_fail("unexpected stage, %d",
vmx_get_test_stage());
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
return VMX_TEST_VMEXIT;
}
break;
default:
report_fail("Unknown exit reason, 0x%x", exit_reason.full);
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
}
vmcs_write(PIN_CONTROLS, vmcs_read(PIN_CONTROLS) & ~PIN_PREEMPT);
return VMX_TEST_VMEXIT;
@@ -346,7 +346,7 @@ static void test_ctrl_pat_main(void)
report(guest_ia32_pat == ia32_pat, "Entry load PAT");
}
-static int test_ctrl_pat_exit_handler(union exit_reason exit_reason)
+static int test_ctrl_pat_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
u64 guest_rip;
u64 guest_pat;
@@ -412,7 +412,7 @@ static void test_ctrl_efer_main(void)
report(guest_ia32_efer == ia32_efer, "Entry load EFER");
}
-static int test_ctrl_efer_exit_handler(union exit_reason exit_reason)
+static int test_ctrl_efer_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
u64 guest_rip;
u64 guest_efer;
@@ -536,7 +536,7 @@ static void cr_shadowing_main(void)
"Write shadowing different X86_CR4_DE");
}
-static int cr_shadowing_exit_handler(union exit_reason exit_reason)
+static int cr_shadowing_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
u64 guest_rip;
u32 insn_len;
@@ -584,7 +584,7 @@ static int cr_shadowing_exit_handler(union exit_reason exit_reason)
// Should not reach here
report_fail("unexpected stage, %d",
vmx_get_test_stage());
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
return VMX_TEST_VMEXIT;
}
vmcs_write(GUEST_RIP, guest_rip + insn_len);
@@ -623,14 +623,14 @@ static int cr_shadowing_exit_handler(union exit_reason exit_reason)
// Should not reach here
report_fail("unexpected stage, %d",
vmx_get_test_stage());
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
return VMX_TEST_VMEXIT;
}
vmcs_write(GUEST_RIP, guest_rip + insn_len);
return VMX_TEST_RESUME;
default:
report_fail("Unknown exit reason, 0x%x", exit_reason.full);
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
}
return VMX_TEST_VMEXIT;
}
@@ -699,7 +699,7 @@ static void iobmp_main(void)
"I/O bitmap - unconditional exiting");
}
-static int iobmp_exit_handler(union exit_reason exit_reason)
+static int iobmp_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
u64 guest_rip;
ulong exit_qual;
@@ -760,7 +760,7 @@ static int iobmp_exit_handler(union exit_reason exit_reason)
// Should not reach here
report_fail("unexpected stage, %d",
vmx_get_test_stage());
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
return VMX_TEST_VMEXIT;
}
vmcs_write(GUEST_RIP, guest_rip + insn_len);
@@ -781,7 +781,7 @@ static int iobmp_exit_handler(union exit_reason exit_reason)
// Should not reach here
report_fail("unexpected stage, %d",
vmx_get_test_stage());
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
return VMX_TEST_VMEXIT;
}
vmcs_write(GUEST_RIP, guest_rip + insn_len);
@@ -985,7 +985,7 @@ static void insn_intercept_main(void)
}
}
-static int insn_intercept_exit_handler(union exit_reason exit_reason)
+static int insn_intercept_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
u64 guest_rip;
ulong exit_qual;
@@ -1280,7 +1280,7 @@ static bool invept_test(int type, u64 eptp)
return true;
}
-static int pml_exit_handler(union exit_reason exit_reason)
+static int pml_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
u16 index, count;
u64 *pmlbuf = pml_log;
@@ -1309,7 +1309,7 @@ static int pml_exit_handler(union exit_reason exit_reason)
default:
report_fail("unexpected stage, %d.",
vmx_get_test_stage());
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
return VMX_TEST_VMEXIT;
}
vmcs_write(GUEST_RIP, guest_rip + insn_len);
@@ -1320,12 +1320,12 @@ static int pml_exit_handler(union exit_reason exit_reason)
return VMX_TEST_RESUME;
default:
report_fail("Unknown exit reason, 0x%x", exit_reason.full);
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
}
return VMX_TEST_VMEXIT;
}
-static int ept_exit_handler_common(union exit_reason exit_reason, bool have_ad)
+static int ept_exit_handler_common(struct regs *regs, union exit_reason exit_reason, bool have_ad)
{
u64 guest_rip;
u64 guest_cr3;
@@ -1406,7 +1406,7 @@ static int ept_exit_handler_common(union exit_reason exit_reason, bool have_ad)
default:
report_fail("ERROR - unexpected stage, %d.",
vmx_get_test_stage());
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
return VMX_TEST_VMEXIT;
}
vmcs_write(GUEST_RIP, guest_rip + insn_len);
@@ -1425,7 +1425,7 @@ static int ept_exit_handler_common(union exit_reason exit_reason, bool have_ad)
default:
report_fail("ERROR - unexpected stage, %d.",
vmx_get_test_stage());
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
return VMX_TEST_VMEXIT;
}
return VMX_TEST_RESUME;
@@ -1481,20 +1481,20 @@ static int ept_exit_handler_common(union exit_reason exit_reason, bool have_ad)
// Should not reach here
report_fail("ERROR : unexpected stage, %d",
vmx_get_test_stage());
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
return VMX_TEST_VMEXIT;
}
return VMX_TEST_RESUME;
default:
report_fail("Unknown exit reason, 0x%x", exit_reason.full);
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
}
return VMX_TEST_VMEXIT;
}
-static int ept_exit_handler(union exit_reason exit_reason)
+static int ept_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
- return ept_exit_handler_common(exit_reason, false);
+ return ept_exit_handler_common(regs, exit_reason, false);
}
static int eptad_init(struct vmcs *vmcs)
@@ -1559,9 +1559,9 @@ static void eptad_main(void)
ept_common();
}
-static int eptad_exit_handler(union exit_reason exit_reason)
+static int eptad_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
- return ept_exit_handler_common(exit_reason, true);
+ return ept_exit_handler_common(regs, exit_reason, true);
}
#define TIMER_VECTOR 222
@@ -1674,7 +1674,7 @@ static void interrupt_main(void)
report(timer_fired, "Inject an event to a halted guest");
}
-static int interrupt_exit_handler(union exit_reason exit_reason)
+static int interrupt_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
u64 guest_rip = vmcs_read(GUEST_RIP);
u32 insn_len = vmcs_read(EXI_INST_LEN);
@@ -1726,7 +1726,7 @@ static int interrupt_exit_handler(union exit_reason exit_reason)
return VMX_TEST_RESUME;
default:
report_fail("Unknown exit reason, 0x%x", exit_reason.full);
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
}
return VMX_TEST_VMEXIT;
@@ -1804,7 +1804,7 @@ static void nmi_hlt_main(void)
vmx_set_test_stage(3);
}
-static int nmi_hlt_exit_handler(union exit_reason exit_reason)
+static int nmi_hlt_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
u64 guest_rip = vmcs_read(GUEST_RIP);
u32 insn_len = vmcs_read(EXI_INST_LEN);
@@ -1814,7 +1814,7 @@ static int nmi_hlt_exit_handler(union exit_reason exit_reason)
if (exit_reason.basic != VMX_VMCALL) {
report_fail("VMEXIT not due to vmcall. Exit reason 0x%x",
exit_reason.full);
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
return VMX_TEST_VMEXIT;
}
@@ -1829,7 +1829,7 @@ static int nmi_hlt_exit_handler(union exit_reason exit_reason)
if (exit_reason.basic != VMX_EXC_NMI) {
report_fail("VMEXIT not due to NMI intercept. Exit reason 0x%x",
exit_reason.full);
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
return VMX_TEST_VMEXIT;
}
report_pass("NMI intercept while running guest");
@@ -1914,7 +1914,7 @@ static void dbgctls_main(void)
report(vmx_get_test_stage() == 4, "Don't save debug controls");
}
-static int dbgctls_exit_handler(union exit_reason exit_reason)
+static int dbgctls_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
u32 insn_len = vmcs_read(EXI_INST_LEN);
u64 guest_rip = vmcs_read(GUEST_RIP);
@@ -1957,7 +1957,7 @@ static int dbgctls_exit_handler(union exit_reason exit_reason)
return VMX_TEST_RESUME;
default:
report_fail("Unknown exit reason, %d", exit_reason.full);
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
}
return VMX_TEST_VMEXIT;
}
@@ -2004,7 +2004,7 @@ static void msr_switch_main(void)
vmcall();
}
-static int msr_switch_exit_handler(union exit_reason exit_reason)
+static int msr_switch_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
if (exit_reason.basic == VMX_VMCALL && vmx_get_test_stage() == 2) {
report(exit_msr_store[0].value == MSR_MAGIC + 1,
@@ -2055,7 +2055,7 @@ static void vmmcall_main(void)
report_fail("VMMCALL");
}
-static int vmmcall_exit_handler(union exit_reason exit_reason)
+static int vmmcall_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
switch (exit_reason.basic) {
case VMX_VMCALL:
@@ -2068,7 +2068,7 @@ static int vmmcall_exit_handler(union exit_reason exit_reason)
break;
default:
report_fail("Unknown exit reason, 0x%x", exit_reason.full);
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
}
return VMX_TEST_VMEXIT;
@@ -2119,7 +2119,7 @@ static void disable_rdtscp_main(void)
vmcall();
}
-static int disable_rdtscp_exit_handler(union exit_reason exit_reason)
+static int disable_rdtscp_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
switch (exit_reason.basic) {
case VMX_VMCALL:
@@ -2140,7 +2140,7 @@ static int disable_rdtscp_exit_handler(union exit_reason exit_reason)
default:
report_fail("Unknown exit reason, 0x%x", exit_reason.full);
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
}
return VMX_TEST_VMEXIT;
}
@@ -2151,7 +2151,7 @@ static void exit_monitor_from_l2_main(void)
exit(0);
}
-static int exit_monitor_from_l2_handler(union exit_reason exit_reason)
+static int exit_monitor_from_l2_handler(struct regs *regs, union exit_reason exit_reason)
{
report_fail("The guest should have killed the VMM");
return VMX_TEST_EXIT;
@@ -9985,11 +9985,11 @@ static void vmx_sipi_signal_test(void)
/* update CR3 on AP */
on_cpu(1, update_cr3, (void *)read_cr3());
+ vmx_set_test_stage(0);
+
/* start AP */
on_cpu_async(1, sipi_test_ap_thread, NULL);
- vmx_set_test_stage(0);
-
/* BSP enter guest */
enter_guest();
}
@@ -10358,10 +10358,11 @@ static unsigned long long host_time_to_guest_time(unsigned long long t)
static unsigned long long rdtsc_vmexit_diff_test_iteration(void)
{
unsigned long long guest_tsc, host_to_guest_tsc;
+ struct regs *regs = guest_regs();
enter_guest();
skip_exit_vmcall();
- guest_tsc = (u32) regs.rax + (regs.rdx << 32);
+ guest_tsc = (u32) regs->rax + (regs->rdx << 32);
host_to_guest_tsc = host_time_to_guest_time(exit_msr_store[0].value);
return host_to_guest_tsc - guest_tsc;
@@ -10431,10 +10432,10 @@ static void invalid_msr_main(void)
report_fail("Invalid MSR load");
}
-static int invalid_msr_exit_handler(union exit_reason exit_reason)
+static int invalid_msr_exit_handler(struct regs *regs, union exit_reason exit_reason)
{
report_fail("Invalid MSR load");
- print_vmexit_info(exit_reason);
+ print_vmexit_info(regs, exit_reason);
return VMX_TEST_EXIT;
}
@@ -10589,6 +10590,7 @@ static void __vmx_pf_exception_test(invalidate_tlb_t inv_fn, void *data,
{
u64 efer;
struct cpuid cpuid;
+ struct regs *regs = guest_regs();
test_set_guest(guest_fn);
@@ -10603,23 +10605,23 @@ static void __vmx_pf_exception_test(invalidate_tlb_t inv_fn, void *data,
while (vmcs_read(EXI_REASON) != VMX_VMCALL) {
switch (vmcs_read(EXI_REASON)) {
case VMX_RDMSR:
- assert(regs.rcx == MSR_EFER);
+ assert(regs->rcx == MSR_EFER);
efer = vmcs_read(GUEST_EFER);
- regs.rdx = efer >> 32;
- regs.rax = efer & 0xffffffff;
+ regs->rdx = efer >> 32;
+ regs->rax = efer & 0xffffffff;
break;
case VMX_WRMSR:
- assert(regs.rcx == MSR_EFER);
- efer = regs.rdx << 32 | (regs.rax & 0xffffffff);
+ assert(regs->rcx == MSR_EFER);
+ efer = regs->rdx << 32 | (regs->rax & 0xffffffff);
vmcs_write(GUEST_EFER, efer);
break;
case VMX_CPUID:
cpuid = (struct cpuid) {0, 0, 0, 0};
- cpuid = raw_cpuid(regs.rax, regs.rcx);
- regs.rax = cpuid.a;
- regs.rbx = cpuid.b;
- regs.rcx = cpuid.c;
- regs.rdx = cpuid.d;
+ cpuid = raw_cpuid(regs->rax, regs->rcx);
+ regs->rax = cpuid.a;
+ regs->rbx = cpuid.b;
+ regs->rcx = cpuid.c;
+ regs->rdx = cpuid.d;
break;
case VMX_INVLPG:
inv_fn(data);
--
2.47.3
next prev parent reply other threads:[~2025-11-21 16:44 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-15 21:54 [kvm-unit-tests PATCH v2 0/4] Better backtraces for leaf functions Mathias Krause
2025-09-15 21:54 ` [kvm-unit-tests PATCH v2 1/4] Makefile: Provide a concept of late CFLAGS Mathias Krause
2025-09-15 21:54 ` [kvm-unit-tests PATCH v2 2/4] x86: Better backtraces for leaf functions Mathias Krause
2025-10-10 6:03 ` Mathias Krause
2025-09-15 21:54 ` [kvm-unit-tests PATCH v2 3/4] arm64: " Mathias Krause
2025-09-15 21:54 ` [kvm-unit-tests PATCH v2 4/4] arm: Fix backtraces involving " Mathias Krause
2025-09-16 13:04 ` [kvm-unit-tests PATCH v2 0/4] Better backtraces for " Andrew Jones
2025-11-14 15:58 ` Mathias Krause
2025-11-14 16:39 ` Sean Christopherson
2025-11-14 18:25 ` Sean Christopherson
2025-11-15 4:56 ` Mathias Krause
2025-11-17 22:19 ` Sean Christopherson
2025-11-18 1:33 ` Mathias Krause
2025-11-18 1:47 ` Mathias Krause
2025-11-18 4:04 ` Mathias Krause
2025-11-18 11:56 ` Mathias Krause
2025-11-18 12:10 ` Mathias Krause
2025-11-21 16:44 ` Mathias Krause [this message]
2025-12-18 1:44 ` Sean Christopherson
2025-12-18 10:07 ` Mathias Krause
2025-12-18 18:26 ` Sean Christopherson
2025-12-19 13:19 ` Mathias Krause
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3bac29b9-4c49-4e5d-997e-9e4019a2fceb@grsecurity.net \
--to=minipli@grsecurity.net \
--cc=alexandru.elisei@arm.com \
--cc=andrew.jones@linux.dev \
--cc=eric.auger@redhat.com \
--cc=kvm@vger.kernel.org \
--cc=kvmarm@lists.linux.dev \
--cc=pbonzini@redhat.com \
--cc=seanjc@google.com \
--cc=thuth@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox