All of lore.kernel.org
 help / color / mirror / Atom feed
From: Michal Luczaj <mhal@rbox.co>
To: kvm@vger.kernel.org
Cc: linux-kselftest@vger.kernel.org, seanjc@google.com,
	pbonzini@redhat.com, shuah@kernel.org,
	Michal Luczaj <mhal@rbox.co>
Subject: [PATCH 2/2] KVM: selftests: x86: Test illegal LEA handling
Date: Fri, 29 Jul 2022 15:48:03 +0200	[thread overview]
Message-ID: <20220729134801.1120-2-mhal@rbox.co> (raw)
In-Reply-To: <20220729134801.1120-1-mhal@rbox.co>

Check if the emulator reveals host's kernel memory address.

Signed-off-by: Michal Luczaj <mhal@rbox.co>
---
Although checkpatch complains about the use of `extern`, I am sending this
as it is. Let me know if you need it in some better form.

[unpatched]$ ./tools/testing/selftests/kvm/x86_64/emulator_illegal_lea
==== Test Assertion Failure ====
  x86_64/emulator_illegal_lea.c:129: uc->args[0] == 0
  pid=957 tid=957 errno=4 - Interrupted system call
     1	: check_state at emulator_illegal_lea.c:129
     2	: main at emulator_illegal_lea.c:169 (discriminator 1)
     3	: ?? ??:0
     4	: ?? ??:0
     5	: _start at ??:?
  Host's address leaked: 0xffff88811a5d3620 (2)

[patched]$ ./tools/testing/selftests/kvm/x86_64/emulator_illegal_lea
[patched]$

 tools/testing/selftests/kvm/.gitignore        |   1 +
 tools/testing/selftests/kvm/Makefile          |   1 +
 .../kvm/x86_64/emulator_illegal_lea.c         | 180 ++++++++++++++++++
 3 files changed, 182 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/x86_64/emulator_illegal_lea.c

diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index 4509a3a7eeae..8977458080b9 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -17,6 +17,7 @@
 /x86_64/debug_regs
 /x86_64/evmcs_test
 /x86_64/emulator_error_test
+/x86_64/emulator_illegal_lea
 /x86_64/fix_hypercall_test
 /x86_64/get_msr_index_features
 /x86_64/kvm_clock_test
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 22423c871ed6..453cd34e1fde 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -75,6 +75,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/cr4_cpuid_sync_test
 TEST_GEN_PROGS_x86_64 += x86_64/get_msr_index_features
 TEST_GEN_PROGS_x86_64 += x86_64/evmcs_test
 TEST_GEN_PROGS_x86_64 += x86_64/emulator_error_test
+TEST_GEN_PROGS_x86_64 += x86_64/emulator_illegal_lea
 TEST_GEN_PROGS_x86_64 += x86_64/fix_hypercall_test
 TEST_GEN_PROGS_x86_64 += x86_64/hyperv_clock
 TEST_GEN_PROGS_x86_64 += x86_64/hyperv_cpuid
diff --git a/tools/testing/selftests/kvm/x86_64/emulator_illegal_lea.c b/tools/testing/selftests/kvm/x86_64/emulator_illegal_lea.c
new file mode 100644
index 000000000000..5f0704e04fac
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86_64/emulator_illegal_lea.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test for handling of illegal (ModRM mod = 3) LEA.
+ *
+ * To trigger the emulator and feed it with LEA, we VM-exit on IO (with a
+ * single OUTS), then race decoder's instruction fetch - hoping to replace the
+ * initial IO op with an illegal LEA.
+ */
+
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+
+#include <string.h>
+#include <pthread.h>
+#include <sys/mman.h>
+
+/* defined in tools/testing/selftests/kvm/lib/x86_64/ucall.c */
+#define UCALL_PIO_PORT ((uint16_t)0x1000)
+
+#define VCPU_ID 0
+#define NOT_UCALL_PORT ((uint16_t)~UCALL_PIO_PORT)
+#define MAX_ATTEMPTS 0x10000
+#define MAYBE_KERNEL(addr) ((int64_t)(addr) < 0)
+
+extern uint32_t race_op;
+
+static void guest_ud_handler(struct ex_regs *regs)
+{
+	GUEST_ASSERT(regs->rip == (uint64_t)&race_op);
+	regs->rip += 4;
+}
+
+__aligned(0x1000) __attribute__((naked)) static void guest_code(void)
+{
+	uint32_t n = 1;
+	void *readable;
+	uint64_t rax;
+
+	do {
+		rax = 0;
+		readable = &guest_code;
+		/*
+		 * This is where
+		 *   3e 3e 3e 6f    outsl (%rsi), %dx
+		 * will be turned into
+		 *   3e 48 8d c0    lea rax (!), rax
+		 * Both instructions padded to 32 bits with DS prefixes.
+		 */
+		asm volatile(".align 8\n\t"
+			     "race_op:\n\t"
+			     "ds\n\t"
+			     "ds\n\t"
+			     "ds\n\t"
+			     "outsl (%[readable]), %[port]\n\t"
+			     ".fill 8, 1, 0x90"
+			     : [rax] "=a" (rax)
+			     : [readable] "S" (readable),
+			       [port] "d" (NOT_UCALL_PORT));
+	} while (!MAYBE_KERNEL(rax) && n++ < MAX_ATTEMPTS);
+
+	ucall(UCALL_DONE, 2, rax, n);
+}
+
+__attribute__((naked)) static void asm_lea(void)
+{
+	asm volatile("ds\n\t"
+		     "lea (%rax), %rax");
+}
+
+__attribute__((naked)) static void asm_outs(void)
+{
+	asm volatile("ds\n\t"
+		     "ds\n\t"
+		     "ds\n\t"
+		     "outsl (%rsi), %dx");
+}
+
+void *racer(void *arg)
+{
+	uint32_t *ptr = arg;
+	uint32_t lea, outs;
+
+	memcpy(&lea, &asm_lea, sizeof(lea));
+	/* set modrm mod = 3 */
+	lea |= 0xc0000000;
+
+	memcpy(&outs, &asm_outs, sizeof(outs));
+
+	while (true) {
+		*ptr = outs;
+		sched_yield();
+		*ptr = lea;
+		sched_yield();
+		pthread_testcancel();
+	};
+}
+
+int check_exit_reason(struct kvm_run *run)
+{
+	switch (run->exit_reason) {
+	case KVM_EXIT_IO:
+		/* race lost: OUTS was executed; ignore and retry */
+		TEST_ASSERT(run->io.port == NOT_UCALL_PORT,
+			    "Unexpected IO port: %u",
+			    run->io.port);
+		return 1;
+	case KVM_EXIT_INTERNAL_ERROR:
+		/* emulator hit bad LEA and bailed out; well done */
+		ASSERT_EQ(*(uint32_t *)&run->emulation_failure.insn_bytes,
+			  *(uint32_t *)&asm_lea | 0xc0000000);
+		TEST_ASSERT(run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION,
+			    "Unexpected suberror: %u",
+			    run->internal.suberror);
+		return 0;
+	default:
+		TEST_FAIL("Unexpected VM exit: %s",
+			  exit_reason_str(run->exit_reason));
+	}
+
+	__builtin_unreachable();
+}
+
+int check_state(struct kvm_run *run, struct ucall *uc)
+{
+	switch (uc->cmd) {
+	case UCALL_NONE:
+		return check_exit_reason(run);
+	case UCALL_DONE:
+		TEST_ASSERT(uc->args[0] == 0,
+			    "Host's address leaked: %#lx (%lu)",
+			    uc->args[0], uc->args[1]);
+		return 0;
+	case UCALL_ABORT:
+		TEST_FAIL("UCALL_ABORT: %s, line %ld\n",
+			  (char *)uc->args[0], uc->args[1]);
+	case UCALL_UNHANDLED:
+		TEST_FAIL("UCALL_UNHANDLED: #%ld\n", uc->args[0]);
+	default:
+		TEST_FAIL("Unexpected ucall");
+	}
+
+	__builtin_unreachable();
+}
+
+int main(int argc, char *argv[])
+{
+	struct kvm_run *run;
+	struct kvm_vm *vm;
+	pthread_t thread;
+	struct ucall uc;
+	int ret;
+
+	ret = mprotect(&guest_code, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC);
+	TEST_ASSERT(ret == 0, "mprotect() failed: %s", strerror(errno));
+
+	vm = vm_create_default(VCPU_ID, 0, (void *)guest_code);
+	run = vcpu_state(vm, VCPU_ID);
+
+	vm_init_descriptor_tables(vm);
+	vcpu_init_descriptor_tables(vm, VCPU_ID);
+	vm_install_exception_handler(vm, UD_VECTOR, guest_ud_handler);
+
+	ret = pthread_create(&thread, 0, racer, addr_gva2hva(vm, (vm_vaddr_t)&race_op));
+	TEST_ASSERT(ret == 0, "pthread_create() failed: %s", strerror(ret));
+
+	do {
+		vcpu_run(vm, VCPU_ID);
+		get_ucall(vm, VCPU_ID, &uc);
+	} while (check_state(run, &uc));
+
+	ret = pthread_cancel(thread);
+	TEST_ASSERT(ret == 0, "pthread_cancel() failed: %s", strerror(ret));
+
+	ret = pthread_join(thread, 0);
+	TEST_ASSERT(ret == 0, "pthread_join() failed: %s", strerror(ret));
+
+	kvm_vm_free(vm);
+	return 0;
+}
-- 
2.32.0


  reply	other threads:[~2022-07-29 14:15 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-07-29 13:48 [PATCH 1/2] KVM: x86: emulator: Fix illegal LEA handling Michal Luczaj
2022-07-29 13:48 ` Michal Luczaj [this message]
2022-07-29 16:53   ` [PATCH 2/2] KVM: selftests: x86: Test " Sean Christopherson
2022-07-31 20:43     ` Michal Luczaj
2022-07-31 20:46     ` [kvm-unit-tests PATCH v2] " Michal Luczaj
2022-08-01 16:44       ` Sean Christopherson
2022-08-02 23:07         ` Michal Luczaj
2022-08-02 23:41           ` Sean Christopherson
2022-08-03 17:21             ` Michal Luczaj
2022-08-03 17:25             ` [kvm-unit-tests PATCH 1/4] x86: emulator.c cleanup: Save and restore exception handlers Michal Luczaj
2022-08-03 17:25               ` [kvm-unit-tests PATCH 2/4] x86: emulator.c cleanup: Use ASM_TRY for the UD_VECTOR cases Michal Luczaj
2022-08-03 18:21                 ` Sean Christopherson
2022-08-05 11:42                   ` Paolo Bonzini
2022-08-05 18:55                     ` Michal Luczaj
2022-08-05 19:59                       ` Sean Christopherson
2022-08-06  2:00                         ` Nadav Amit
2022-08-06 11:08                           ` Michal Luczaj
2022-08-03 17:25               ` [kvm-unit-tests PATCH 3/4] x86: Test emulator's handling of LEA with /reg Michal Luczaj
2022-08-03 17:25               ` [kvm-unit-tests PATCH 4/4] x86: Extend ASM_TRY to handle #UD thrown by FEP-triggered emulator Michal Luczaj
2022-08-03 18:16                 ` Sean Christopherson
2022-08-05 11:50                   ` Paolo Bonzini
2022-07-29 16:41 ` [PATCH 1/2] KVM: x86: emulator: Fix illegal LEA handling Sean Christopherson

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=20220729134801.1120-2-mhal@rbox.co \
    --to=mhal@rbox.co \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=seanjc@google.com \
    --cc=shuah@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.