All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jim Mattson <jmattson@google.com>
To: kvm@vger.kernel.org
Cc: Jim Mattson <jmattson@google.com>
Subject: [kvm-unit-tests PATCH] Test 32-bit ljmp emulation
Date: Mon, 28 Nov 2016 11:18:01 -0800	[thread overview]
Message-ID: <1480360681-7826-1-git-send-email-jmattson@google.com> (raw)

em_jmp_far and em_ret_far assumed that setting IP can only fail in 64
bit mode, but syzkaller proved otherwise (and SDM agrees).  This test
exercises the bug.
---
 lib/x86/desc.h      |   1 +
 lib/x86/processor.h |   5 +++
 x86/cstart64.S      |   1 +
 x86/emulator.c      | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 115 insertions(+)

diff --git a/lib/x86/desc.h b/lib/x86/desc.h
index be52fd4..17c7960 100644
--- a/lib/x86/desc.h
+++ b/lib/x86/desc.h
@@ -142,6 +142,7 @@ void set_intr_task_gate(int vec, void *fn);
 void setup_tss32(void);
 #else
 extern tss64_t tss;
+extern gdt_entry_t gdt64[];
 #endif
 
 unsigned exception_vector(void);
diff --git a/lib/x86/processor.h b/lib/x86/processor.h
index ee7f180..6cb7937 100644
--- a/lib/x86/processor.h
+++ b/lib/x86/processor.h
@@ -46,6 +46,11 @@ struct descriptor_table_ptr {
     ulong base;
 } __attribute__((packed));
 
+struct far_pointer32 {
+    u32 offset;
+    u16 selector;
+} __attribute__((packed));
+
 static inline void barrier(void)
 {
     asm volatile ("" : : : "memory");
diff --git a/x86/cstart64.S b/x86/cstart64.S
index e947888..8bb7af3 100644
--- a/x86/cstart64.S
+++ b/x86/cstart64.S
@@ -7,6 +7,7 @@ boot_idt = 0
 .globl idt_descr
 .globl tss_descr
 .globl gdt64_desc
+.globl gdt64
 
 ipi_vector = 0x20
 
diff --git a/x86/emulator.c b/x86/emulator.c
index 8d262d8..240362a 100644
--- a/x86/emulator.c
+++ b/x86/emulator.c
@@ -768,6 +768,32 @@ asm(
 	".align 4096\n\t"
 );
 
+asm(
+	".align 4096\n\t"
+	"compat_insn_page:\n\t"
+	"ret\n\t"
+	"pushf\n\t"
+	"push 136+save\n\t"
+	"popf\n\t"
+	INSN_XCHG_ALL
+	"ljmp *(%rax)\n\t"
+	".code32\n\t"
+	"compat_test_insn:\n\t"
+	"in (%dx), %al\n\t"
+	".skip 31, 0x90\n\t"
+	"compat_test_insn_end:\n\t"
+	"ljmp $" xstr(KERNEL_CS) ", $64f\n\t"
+	".code64\n\t"
+	"64:\n\t"
+	INSN_XCHG_ALL
+	"pushf\n\t"
+	"pop 136+save\n\t"
+	"popf\n\t"
+	"ret\n\t"
+	"compat_insn_page_end:\n\t"
+	".align 4096\n\t"
+);
+
 #define MK_INSN(name, str)				\
     asm (						\
 	 ".pushsection .data.insn  \n\t"		\
@@ -810,6 +836,46 @@ static void trap_emulator(uint64_t *mem, void *alt_insn_page,
 	outregs = save;
 }
 
+/*
+ * Trigger emulation of a compatibility mode instruction. Note that this
+ * function clobbers inregs.rax.
+ */
+static void trap_emulator32(uint64_t *mem, void *alt_insn_page,
+			    struct insn_desc *alt_insn)
+{
+	extern u8 compat_insn_page[], compat_test_insn[];
+	ulong *cr3 = (ulong *)read_cr3();
+	struct far_pointer32 compat_fp = {
+		.offset = (uintptr_t)compat_test_insn,
+		.selector = KERNEL_CS32,
+	};
+	void *insn_ram;
+
+	insn_ram = compat_insn_page;
+	memcpy(alt_insn_page, compat_insn_page, 4096);
+	memcpy(alt_insn_page + (compat_test_insn - compat_insn_page),
+	       (void *)(alt_insn->ptr), alt_insn->len);
+	save = inregs;
+	save.rax = (uintptr_t)&compat_fp;
+
+	/*
+	 * Load the code TLB with compat_insn_page, but point the page
+	 * tables at alt_insn_page (and keep the data TLB clear, for
+	 * AMD decode assist).  This will make the CPU trap on the
+	 * compat_insn_page instruction but the hypervisor will see
+	 * alt_insn_page.
+	 */
+	install_page(cr3, virt_to_phys(compat_insn_page), insn_ram);
+	invlpg(insn_ram);
+	/* Load code TLB */
+	asm volatile("call *%0" : : "r" (insn_ram));
+	install_page(cr3, virt_to_phys(alt_insn_page), insn_ram);
+	/* Trap, let hypervisor emulate at alt_insn_page */
+	asm volatile("call *%0" : : "r" (insn_ram + 1));
+
+	outregs = save;
+}
+
 static unsigned long rip_advance;
 
 static void advance_rip_and_note_exception(struct ex_regs *regs)
@@ -855,6 +921,47 @@ static void test_jmp_noncanonical(uint64_t *mem)
 	handle_exception(GP_VECTOR, 0);
 }
 
+u16 exception_cs;
+
+static void record_cs_and_advance_rip(struct ex_regs *regs)
+{
+	exception_cs = regs->cs;
+	regs->rip += rip_advance;
+}
+
+/*
+ * This test ensures that emulation of 32-bit far jmp works properly
+ * when the far jmp raises #GP because the offset in the target operand
+ * is beyond the limit of the code segment in the target operand.
+ */
+static void test_jmpfar_32bitgp(uint64_t *mem, uint8_t *insn_page,
+				uint8_t *alt_insn_page, void *insn_ram)
+{
+	void *stack = alloc_page();
+	gdt_entry_t *spare = gdt64 + (FIRST_SPARE_SEL >> 3);
+	struct far_pointer32 spare_fp = {
+		.offset = 0x1000,
+		.selector = FIRST_SPARE_SEL,
+	};
+	/*
+	 * Set up a 32-bit code segment with a single-page limit.
+	 */
+	*spare = gdt64[KERNEL_CS32 >> 3];
+	spare->limit_low = 0;
+	spare->granularity &= ~0xf;
+
+	handle_exception(GP_VECTOR, record_cs_and_advance_rip);
+	MK_INSN(jmp_far32, ".code32; ljmp *(%edx); .code64");
+	exception_cs = -1;
+	rip_advance = insn_jmp_far32.len;
+	inregs = (struct regs){ .rdx = (uintptr_t)&spare_fp,
+				.rsp = (uintptr_t)stack + sizeof(stack),
+			      };
+	trap_emulator32(mem, alt_insn_page, &insn_jmp_far32);
+	report("32-bit far jmp #GP preserves CS", exception_cs == KERNEL_CS32);
+	handle_exception(GP_VECTOR, 0);
+}
+
 static void test_movabs(uint64_t *mem, uint8_t *insn_page,
 		       uint8_t *alt_insn_page, void *insn_ram)
 {
@@ -1162,6 +1269,7 @@ int main()
 	test_string_io_mmio(mem);
 
 	test_jmp_noncanonical(mem);
+	test_jmpfar_32bitgp(mem, insn_page, alt_insn_page, insn_ram);
 	test_illegal_movbe();
 
 	return report_summary();
-- 
2.8.0.rc3.226.g39d4020


             reply	other threads:[~2016-11-28 19:18 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-11-28 19:18 Jim Mattson [this message]
2016-11-30 16:36 ` [kvm-unit-tests PATCH v2] Test 32-bit ljmp emulation Jim Mattson

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=1480360681-7826-1-git-send-email-jmattson@google.com \
    --to=jmattson@google.com \
    --cc=kvm@vger.kernel.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.