[kvm-unit-tests RFC PATCH] x86/emulator: Test indirect CALL (gpa_available)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



It seems em_call_near_abs() is missing a TwoMemOp flag.

If there's a match between ~PAGE_MASK (lower 12) bits of
	- address of memory storing CALL's target, and
	- stack pointer at the time of pushing the return address,
emulator, while pushing the return address, will skip GVA->GPA walk and use GPA
provided by nPF. See arch/x86/kvm/x86.c:emulator_read_write_onepage().

Problem is that nPF came from reading CALL's SrcMem (pointer to CALL's target),
so the "push" will overwrite CALL's SrcMem, not touching the stack at all. Then
RET comes along and attempts to pop the return address, which was never written.

I guess it's hight time I admit I have no idea if such indirect CALL + nPF setup
was meant to be supported and/or this has any real world consequences :) That
said, here's a KUT testcase where the bamboozled emulator makes RET twist the
execution flow.

Please let me know if this is worth submitting a TwoMemOp patch. em_call_far()
appears to have the same "problem".

Signed-off-by: Michal Luczaj <mhal@xxxxxxx>
---
 x86/emulator.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/x86/emulator.c b/x86/emulator.c
index ad94374..425f838 100644
--- a/x86/emulator.c
+++ b/x86/emulator.c
@@ -787,6 +787,49 @@ static void test_mov_pop_ss_code_db(void)
 	handle_exception(DB_VECTOR, old_db_handler);
 }
 
+static void test_indirect_call(void)
+{
+	long page1, page2, old_sp, sp, dest, tmp;
+	int status;
+
+	page1 = (long)vmap(IORAM_BASE_PHYS, PAGE_SIZE * 2);
+	page2 = page1 + PAGE_SIZE;
+
+	sp = page1 + PAGE_SIZE;
+	dest = page2 + PAGE_SIZE - sizeof(dest);
+
+	assert(((sp - sizeof(sp)) & ~PAGE_MASK) == (dest & ~PAGE_MASK));
+
+	asm volatile("mov %%"R "sp, %[old_sp]\n\t"
+		     "mov %[sp], %%"R "sp\n\t"
+
+		     "lea Lret_diverted, %0\n\t"
+		     "mov %0, -"S"(%%"R "sp)\n\t"
+
+		     "lea Lcall_target, %0\n\t"
+		     "mov %0, (%[dest])\n\t"
+
+		     "mov $-1, %[status]\n\t"
+		     "call *(%[dest])\n\t"
+		     "jmp Lout\n\t"
+
+		     "Lcall_target:\n\t"
+		     "endbr" xstr(BITS_PER_LONG)"\n\t"
+		     "mov $0, %[status]\n\t"
+		     "ret\n\t"
+
+		     "Lret_diverted:\n\t"
+		     "mov $1, %[status]\n\t"
+
+		     "Lout:\n\t"
+		     "mov %[old_sp], %%"R "sp\n\t"
+		     : "=&r"(tmp), [old_sp]"=&r"(old_sp), [status]"=&r"(status)
+		     : [sp]"r"(sp), [dest]"r"(dest)
+		     : "memory");
+
+	report(!status, "indirect call (gpa_val), status = %d", status);
+}
+
 int main(void)
 {
 	void *mem;
@@ -834,6 +877,7 @@ int main(void)
 	test_string_io_mmio(mem);
 	test_illegal_movbe();
 	test_mov_pop_ss_code_db();
+	test_indirect_call();
 
 #ifdef __x86_64__
 	test_emulator_64(mem);
-- 
2.41.0




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux