kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [kvm-unit-tests PATCH 0/3] x86 hypercall spring^W summer cleanup
@ 2025-07-24 19:10 Mathias Krause
  2025-07-24 19:10 ` [kvm-unit-tests PATCH 1/3] x86: Don't rely on KVM's hypercall patching Mathias Krause
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Mathias Krause @ 2025-07-24 19:10 UTC (permalink / raw)
  To: Paolo Bonzini, kvm; +Cc: Mathias Krause

This little series cleans up the x86 hypercall tests by making them no
longer rely on KVM's hypercall patching to change a non-native
instruction to the native one.

There are attempts[1] to disable KVM's default behaviour regarding
hypercall patching, actually requiring executing the native hypercall
instruction.

The last patch is also a general cleanup of the x86/hypercall test.

Please apply!

Thanks,
Mathias

[1] https://lore.kernel.org/kvm/20250722204316.1186096-1-minipli@grsecurity.net/

Mathias Krause (3):
  x86: Don't rely on KVM's hypercall patching
  x86: Provide a macro for extable handling
  x86/hypercall: Simplify and increase coverage

 lib/x86/desc.h  |   9 ++--
 x86/apic.c      |   5 +-
 x86/hypercall.c | 131 +++++++++++++++++++++++-------------------------
 x86/vmexit.c    |   5 +-
 4 files changed, 77 insertions(+), 73 deletions(-)

-- 
2.30.2


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [kvm-unit-tests PATCH 1/3] x86: Don't rely on KVM's hypercall patching
  2025-07-24 19:10 [kvm-unit-tests PATCH 0/3] x86 hypercall spring^W summer cleanup Mathias Krause
@ 2025-07-24 19:10 ` Mathias Krause
  2025-07-24 19:10 ` [kvm-unit-tests PATCH 2/3] x86: Provide a macro for extable handling Mathias Krause
  2025-07-24 19:10 ` [kvm-unit-tests PATCH 3/3] x86/hypercall: Simplify and increase coverage Mathias Krause
  2 siblings, 0 replies; 4+ messages in thread
From: Mathias Krause @ 2025-07-24 19:10 UTC (permalink / raw)
  To: Paolo Bonzini, kvm; +Cc: Mathias Krause

Instead of relying on KVM to patch VMCALL into VMMCALL on non-Intel
systems, use the native instruction directly.

Signed-off-by: Mathias Krause <minipli@grsecurity.net>
---
 x86/apic.c   | 5 ++++-
 x86/vmexit.c | 5 ++++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/x86/apic.c b/x86/apic.c
index b45fc9c1b72f..0a52e9a45f1c 100644
--- a/x86/apic.c
+++ b/x86/apic.c
@@ -641,7 +641,10 @@ static void test_pv_ipi(void)
 	if (!test_device_enabled())
 		return;
 
-	asm volatile("vmcall" : "=a"(ret) :"a"(KVM_HC_SEND_IPI), "b"(a0), "c"(a1), "d"(a2), "S"(a3));
+	if (is_intel())
+		asm volatile("vmcall"  : "=a"(ret) :"a"(KVM_HC_SEND_IPI), "b"(a0), "c"(a1), "d"(a2), "S"(a3));
+	else
+		asm volatile("vmmcall" : "=a"(ret) :"a"(KVM_HC_SEND_IPI), "b"(a0), "c"(a1), "d"(a2), "S"(a3));
 	report(!ret, "PV IPIs testing");
 }
 
diff --git a/x86/vmexit.c b/x86/vmexit.c
index 48a38f60f6d6..56c37f6215ca 100644
--- a/x86/vmexit.c
+++ b/x86/vmexit.c
@@ -33,7 +33,10 @@ static void vmcall(void)
 {
 	unsigned long a = 0, b, c, d;
 
-	asm volatile ("vmcall" : "+a"(a), "=b"(b), "=c"(c), "=d"(d));
+	if (is_intel())
+		asm volatile ("vmcall"  : "+a"(a), "=b"(b), "=c"(c), "=d"(d));
+	else
+		asm volatile ("vmmcall" : "+a"(a), "=b"(b), "=c"(c), "=d"(d));
 }
 
 #define MSR_EFER 0xc0000080
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [kvm-unit-tests PATCH 2/3] x86: Provide a macro for extable handling
  2025-07-24 19:10 [kvm-unit-tests PATCH 0/3] x86 hypercall spring^W summer cleanup Mathias Krause
  2025-07-24 19:10 ` [kvm-unit-tests PATCH 1/3] x86: Don't rely on KVM's hypercall patching Mathias Krause
@ 2025-07-24 19:10 ` Mathias Krause
  2025-07-24 19:10 ` [kvm-unit-tests PATCH 3/3] x86/hypercall: Simplify and increase coverage Mathias Krause
  2 siblings, 0 replies; 4+ messages in thread
From: Mathias Krause @ 2025-07-24 19:10 UTC (permalink / raw)
  To: Paolo Bonzini, kvm; +Cc: Mathias Krause

Provide a macro to emit exception table entries that can be used in
asm() statements.

Signed-off-by: Mathias Krause <minipli@grsecurity.net>
---
 lib/x86/desc.h | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/lib/x86/desc.h b/lib/x86/desc.h
index 68f38f3d7533..21e0ae8dd028 100644
--- a/lib/x86/desc.h
+++ b/lib/x86/desc.h
@@ -150,11 +150,14 @@ typedef struct  __attribute__((packed)) {
 	u16 iomap_base;
 } tss64_t;
 
+#define ASM_EX_ENTRY(src, dst) \
+	".pushsection .data.ex\n\t"			\
+	__ASM_SEL(.long, .quad) src ", " dst "\n\t"	\
+	".popsection \n\t"
+
 #define __ASM_TRY(prefix, catch)				\
 	"movl $0, %%gs:4\n\t"					\
-	".pushsection .data.ex\n\t"				\
-	__ASM_SEL(.long, .quad) " 1111f,  " catch "\n\t"	\
-	".popsection \n\t"					\
+	ASM_EX_ENTRY("1111f", catch)				\
 	prefix "\n\t"						\
 	"1111:"
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [kvm-unit-tests PATCH 3/3] x86/hypercall: Simplify and increase coverage
  2025-07-24 19:10 [kvm-unit-tests PATCH 0/3] x86 hypercall spring^W summer cleanup Mathias Krause
  2025-07-24 19:10 ` [kvm-unit-tests PATCH 1/3] x86: Don't rely on KVM's hypercall patching Mathias Krause
  2025-07-24 19:10 ` [kvm-unit-tests PATCH 2/3] x86: Provide a macro for extable handling Mathias Krause
@ 2025-07-24 19:10 ` Mathias Krause
  2 siblings, 0 replies; 4+ messages in thread
From: Mathias Krause @ 2025-07-24 19:10 UTC (permalink / raw)
  To: Paolo Bonzini, kvm; +Cc: Mathias Krause

Simplify the hypercall tests by always executing both variants and
handle exceptions appropriately.

If the test runs on bare metal, using the wrong instruction, the
exception will be caught and the test still passes.

If the test runs under KVM and a non-native instruction was patched to
the native one, a fitting message will be printed.

The x86-64-specific test for crossing into non-canonical space can also
greatly be simplified by making use of exception table entries.

Signed-off-by: Mathias Krause <minipli@grsecurity.net>
---
 x86/hypercall.c | 131 +++++++++++++++++++++++-------------------------
 1 file changed, 63 insertions(+), 68 deletions(-)

diff --git a/x86/hypercall.c b/x86/hypercall.c
index a02ee33627a3..2d343fee3632 100644
--- a/x86/hypercall.c
+++ b/x86/hypercall.c
@@ -4,93 +4,88 @@
 #include "alloc_page.h"
 #include "fwcfg.h"
 
-#define KVM_HYPERCALL_INTEL ".byte 0x0f,0x01,0xc1"
-#define KVM_HYPERCALL_AMD ".byte 0x0f,0x01,0xd9"
+#define KVM_HYPERCALL_VMCALL	0x0f,0x01,0xc1	/* Intel */
+#define KVM_HYPERCALL_VMMCALL	0x0f,0x01,0xd9	/* AMD */
 
-static inline long kvm_hypercall0_intel(unsigned int nr)
-{
-	long ret;
-	asm volatile(KVM_HYPERCALL_INTEL
-		     : "=a"(ret)
-		     : "a"(nr));
-	return ret;
-}
+#define test_hypercall(type, may_fail) \
+	do {								\
+		const char ref_insn[] = { KVM_HYPERCALL_##type };	\
+		bool extra;						\
+		extern const char hc_##type[];				\
+									\
+		asm volatile goto(ASM_TRY("%l["xstr(fault_##type)"]")	\
+				  xstr(hc_##type) ":\n\t"		\
+				  ".byte " xstr(KVM_HYPERCALL_##type)	\
+				  : /* no outputs allowed */		\
+				  : "a"(-1)				\
+				  : "memory"				\
+				  : fault_##type);			\
+		extra = memcmp(hc_##type, ref_insn, sizeof(ref_insn));	\
+		report(true, "Hypercall via " #type ": OK%s",		\
+		       extra ? " (patched)" : "");			\
+		break;							\
+									\
+	fault_##type:							\
+		extra = exception_vector() != PF_VECTOR &&		\
+			exception_vector() != UD_VECTOR;		\
+		report((may_fail) && !extra,				\
+			"Hypercall via " #type ": %s%s(%u)",		\
+			extra ? "unexpected " : "",			\
+			exception_mnemonic(exception_vector()),		\
+			exception_error_code());			\
+	} while (0)
 
-static inline long kvm_hypercall0_amd(unsigned int nr)
-{
-	long ret;
-	asm volatile(KVM_HYPERCALL_AMD
-		     : "=a"(ret)
-		     : "a"(nr));
-	return ret;
-}
-
-
-volatile unsigned long test_rip;
 #ifdef __x86_64__
-extern void gp_tss(void);
-asm ("gp_tss: \n\t"
-	"add $8, %rsp\n\t"            // discard error code
-	"popq test_rip(%rip)\n\t"     // pop return address
-	"pushq %rsi\n\t"              // new return address
-	"iretq\n\t"
-	"jmp gp_tss\n\t"
-    );
+#define NON_CANON_START	(1UL << 47)
 
-static inline int
-test_edge(void)
+static bool test_edge(bool may_fail)
 {
-	test_rip = 0;
-	asm volatile ("movq $-1, %%rax\n\t"			// prepare for vmcall
-		      "leaq 1f(%%rip), %%rsi\n\t"		// save return address for gp_tss
-		      "movabsq $0x7ffffffffffd, %%rbx\n\t"
-		      "jmp *%%rbx; 1:" : : : "rax", "rbx", "rsi");
-	printf("Return from int 13, test_rip = %lx\n", test_rip);
-	return test_rip == (1ul << 47);
+	const char *addr = (void *)(NON_CANON_START - 3);
+	char insn[3] = { addr[0], addr[1], addr[2] };
+
+	static_assert(NON_CANON_START == 0x800000000000UL);
+	asm volatile goto("jmpq *%[addr]; 1:"
+			  ASM_EX_ENTRY("0x7ffffffffffd", "%l[insn_failed]")
+			  ASM_EX_ENTRY("0x800000000000", "1b")
+			  : /* no outputs allowed */
+			  : "a"(-1), [addr]"r"(addr)
+			  : "memory"
+			  : insn_failed);
+	printf("Return from %s(%u) with RIP = %lx%s\n",
+	       exception_mnemonic(exception_vector()), exception_error_code(),
+	       NON_CANON_START, memcmp(addr, insn, 3) ? ", patched" : "");
+	return true;
+
+insn_failed:
+	printf("KVM hypercall failed%s\n",
+	       may_fail ? ", as expected" : " unexpectedly!");
+	return may_fail;
 }
 #endif
 
 int main(int ac, char **av)
 {
-	bool test_vmcall = !no_test_device || is_intel();
-	bool test_vmmcall = !no_test_device || !is_intel();
+	/* VMCALL may be patched by KVM on AMD or fail with #UD on bare metal */
+	test_hypercall(VMCALL, !is_intel());
 
-	if (test_vmcall) {
-		kvm_hypercall0_intel(-1u);
-		printf("Hypercall via VMCALL: OK\n");
-	}
-
-	if (test_vmmcall) {
-		kvm_hypercall0_amd(-1u);
-		printf("Hypercall via VMMCALL: OK\n");
-	}
+	/* VMMCALL may be patched on Intel or fail with #UD in bare metal */
+	test_hypercall(VMMCALL, is_intel());
 
 #ifdef __x86_64__
 	setup_vm();
-	setup_alt_stack();
-	set_intr_alt_stack(13, gp_tss);
 
-	u8 *data1 = alloc_page();
-	u8 *topmost = (void *) ((1ul << 47) - PAGE_SIZE);
+	u8 *topmost = (void *) (NON_CANON_START - PAGE_SIZE);
 
-	install_pte(phys_to_virt(read_cr3()), 1, topmost,
-		    virt_to_phys(data1) | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0);
+	install_page(current_page_table(), virt_to_phys(alloc_page()), topmost);
 	memset(topmost, 0xcc, PAGE_SIZE);
-	topmost[4093] = 0x0f;
-	topmost[4094] = 0x01;
-	topmost[4095] = 0xc1;
 
-	if (test_vmcall) {
-		report(test_edge(),
-		       "VMCALL on edge of canonical address space (intel)");
-	}
+	memcpy(topmost + PAGE_SIZE - 3, (char []){ KVM_HYPERCALL_VMCALL }, 3);
+	report(test_edge(!is_intel()),
+	       "VMCALL on edge of canonical address space (Intel)");
 
-	topmost[4095] = 0xd9;
-
-	if (test_vmmcall) {
-		report(test_edge(),
-		       "VMMCALL on edge of canonical address space (AMD)");
-	}
+	memcpy(topmost + PAGE_SIZE - 3, (char []){ KVM_HYPERCALL_VMMCALL }, 3);
+	report(test_edge(is_intel()),
+	       "VMMCALL on edge of canonical address space (AMD)");
 #endif
 
 	return report_summary();
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2025-07-24 19:10 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-24 19:10 [kvm-unit-tests PATCH 0/3] x86 hypercall spring^W summer cleanup Mathias Krause
2025-07-24 19:10 ` [kvm-unit-tests PATCH 1/3] x86: Don't rely on KVM's hypercall patching Mathias Krause
2025-07-24 19:10 ` [kvm-unit-tests PATCH 2/3] x86: Provide a macro for extable handling Mathias Krause
2025-07-24 19:10 ` [kvm-unit-tests PATCH 3/3] x86/hypercall: Simplify and increase coverage Mathias Krause

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).