summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorStefan Hajnoczi2022-09-06 13:31:43 +0200
committerStefan Hajnoczi2022-09-06 13:31:43 +0200
commitb34b42f1b6a33c455dccce6ceb49962dddbb7a8a (patch)
tree68143a8c84528fdd3caf8b5206a5df9427ae7263 /tests
parentMerge tag 'pull-or1k-20220904' of https://github.com/stffrdhrn/qemu into staging (diff)
parenttarget/riscv: Make translator stop before the end of a page (diff)
downloadqemu-b34b42f1b6a33c455dccce6ceb49962dddbb7a8a.tar.gz
qemu-b34b42f1b6a33c455dccce6ceb49962dddbb7a8a.tar.xz
qemu-b34b42f1b6a33c455dccce6ceb49962dddbb7a8a.zip
Merge tag 'pull-tcg-20220906' of https://gitlab.com/rth7680/qemu into staging
Respect PROT_EXEC in user-only mode. Fix s390x, i386 and riscv for translations crossing a page. # -----BEGIN PGP SIGNATURE----- # # iQFRBAABCgA7FiEEekgeeIaLTbaoWgXAZN846K9+IV8FAmMW8TcdHHJpY2hhcmQu # aGVuZGVyc29uQGxpbmFyby5vcmcACgkQZN846K9+IV8qfwf9EYjXywES/UYzfeJC # 7irryE3iYddWP+ix3Q4WKaTc61plwP5MMCmeq4PjRo1IBAL5dTtUE1+AFXkEvm4L # EckSiT5D5d/wYOfhWSWxjblmMk7GUXRRgKzkF1ir3soIftQgXdb43PwAswuOca/v # dX7wXBJOoWmGWqXNNlQmGIl7c4uQTkOM6iTTLlm4Qg7SJC4MA6EiSZmXlvAs80lN # TCbBV5P89qseHwzhJUTMZEO+ZMAuTSjFSd/RqBexVa4ty5UJxxgBk21A8JtQPUhr # Y/Ezb0yhOcwrdjJ8REc267BZbdNgbaVNlUd7c9GKbv8bQUh0AoM9gnjGdoID88x9 # q0f+Pw== # =HmJB # -----END PGP SIGNATURE----- # gpg: Signature made Tue 06 Sep 2022 03:05:27 EDT # gpg: using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F # gpg: issuer "richard.henderson@linaro.org" # gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [full] # Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A 05C0 64DF 38E8 AF7E 215F * tag 'pull-tcg-20220906' of https://gitlab.com/rth7680/qemu: target/riscv: Make translator stop before the end of a page target/riscv: Add MAX_INSN_LEN and insn_len target/i386: Make translator stop before the end of a page target/s390x: Make translator stop before the end of a page accel/tcg: Add fast path for translator_ld* accel/tcg: Add pc and host_pc params to gen_intermediate_code accel/tcg: Remove translator_ldsw accel/tcg: Document the faulting lookup in tb_lookup_cmp accel/tcg: Use probe_access_internal for softmmu get_page_addr_code_hostp accel/tcg: Move qemu_ram_addr_from_host_nofail to physmem.c accel/tcg: Make tb_htable_lookup static accel/tcg: Unlock mmap_lock after longjmp accel/tcg: Properly implement get_page_addr_code for user-only accel/tcg: Introduce is_same_page() tests/tcg/i386: Move smc_code2 to an executable section linux-user: Clear translations on mprotect() linux-user: Honor PT_GNU_STACK linux-user/x86_64: Allocate vsyscall page as a commpage linux-user/hppa: Allocate page zero as a commpage linux-user/arm: Mark the commpage executable Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/tcg/i386/test-i386.c2
-rw-r--r--tests/tcg/multiarch/noexec.c.inc139
-rw-r--r--tests/tcg/riscv64/Makefile.target1
-rw-r--r--tests/tcg/riscv64/noexec.c79
-rw-r--r--tests/tcg/s390x/Makefile.target1
-rw-r--r--tests/tcg/s390x/noexec.c106
-rw-r--r--tests/tcg/x86_64/Makefile.target3
-rw-r--r--tests/tcg/x86_64/noexec.c75
8 files changed, 404 insertions, 2 deletions
diff --git a/tests/tcg/i386/test-i386.c b/tests/tcg/i386/test-i386.c
index ac8d5a3c1f..e6b308a2c0 100644
--- a/tests/tcg/i386/test-i386.c
+++ b/tests/tcg/i386/test-i386.c
@@ -1998,7 +1998,7 @@ uint8_t code[] = {
0xc3, /* ret */
};
-asm(".section \".data\"\n"
+asm(".section \".data_x\",\"awx\"\n"
"smc_code2:\n"
"movl 4(%esp), %eax\n"
"movl %eax, smc_patch_addr2 + 1\n"
diff --git a/tests/tcg/multiarch/noexec.c.inc b/tests/tcg/multiarch/noexec.c.inc
new file mode 100644
index 0000000000..2ef539b721
--- /dev/null
+++ b/tests/tcg/multiarch/noexec.c.inc
@@ -0,0 +1,139 @@
+/*
+ * Common code for arch-specific MMU_INST_FETCH fault testing.
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/ucontext.h>
+
+/* Forward declarations. */
+
+static void *arch_mcontext_pc(const mcontext_t *ctx);
+static int arch_mcontext_arg(const mcontext_t *ctx);
+static void arch_flush(void *p, int len);
+
+/* Testing infrastructure. */
+
+struct noexec_test {
+ const char *name;
+ const char *test_code;
+ int test_len;
+ int page_ofs;
+ int entry_ofs;
+ int expected_si_ofs;
+ int expected_pc_ofs;
+ int expected_arg;
+};
+
+static void *page_base;
+static int page_size;
+static const struct noexec_test *current_noexec_test;
+
+static void handle_err(const char *syscall)
+{
+ printf("[ FAILED ] %s: %s\n", syscall, strerror(errno));
+ exit(EXIT_FAILURE);
+}
+
+static void handle_segv(int sig, siginfo_t *info, void *ucontext)
+{
+ const struct noexec_test *test = current_noexec_test;
+ const mcontext_t *mc = &((ucontext_t *)ucontext)->uc_mcontext;
+ void *expected_si;
+ void *expected_pc;
+ void *pc;
+ int arg;
+
+ if (test == NULL) {
+ printf("[ FAILED ] unexpected SEGV\n");
+ exit(EXIT_FAILURE);
+ }
+ current_noexec_test = NULL;
+
+ expected_si = page_base + test->expected_si_ofs;
+ if (info->si_addr != expected_si) {
+ printf("[ FAILED ] wrong si_addr (%p != %p)\n",
+ info->si_addr, expected_si);
+ exit(EXIT_FAILURE);
+ }
+
+ pc = arch_mcontext_pc(mc);
+ expected_pc = page_base + test->expected_pc_ofs;
+ if (pc != expected_pc) {
+ printf("[ FAILED ] wrong pc (%p != %p)\n", pc, expected_pc);
+ exit(EXIT_FAILURE);
+ }
+
+ arg = arch_mcontext_arg(mc);
+ if (arg != test->expected_arg) {
+ printf("[ FAILED ] wrong arg (%d != %d)\n", arg, test->expected_arg);
+ exit(EXIT_FAILURE);
+ }
+
+ if (mprotect(page_base, page_size,
+ PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
+ handle_err("mprotect");
+ }
+}
+
+static void test_noexec_1(const struct noexec_test *test)
+{
+ void *start = page_base + test->page_ofs;
+ void (*fn)(int arg) = page_base + test->entry_ofs;
+
+ memcpy(start, test->test_code, test->test_len);
+ arch_flush(start, test->test_len);
+
+ /* Trigger TB creation in order to test invalidation. */
+ fn(0);
+
+ if (mprotect(page_base, page_size, PROT_NONE) < 0) {
+ handle_err("mprotect");
+ }
+
+ /* Trigger SEGV and check that handle_segv() ran. */
+ current_noexec_test = test;
+ fn(0);
+ assert(current_noexec_test == NULL);
+}
+
+static int test_noexec(struct noexec_test *tests, size_t n_tests)
+{
+ struct sigaction act;
+ size_t i;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = handle_segv;
+ act.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGSEGV, &act, NULL) < 0) {
+ handle_err("sigaction");
+ }
+
+ page_size = getpagesize();
+ page_base = mmap(NULL, 2 * page_size,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (page_base == MAP_FAILED) {
+ handle_err("mmap");
+ }
+ page_base += page_size;
+
+ for (i = 0; i < n_tests; i++) {
+ struct noexec_test *test = &tests[i];
+
+ printf("[ RUN ] %s\n", test->name);
+ test_noexec_1(test);
+ printf("[ OK ]\n");
+ }
+
+ printf("[ PASSED ]\n");
+ return EXIT_SUCCESS;
+}
diff --git a/tests/tcg/riscv64/Makefile.target b/tests/tcg/riscv64/Makefile.target
index d41bf6d60d..b5b89dfb0e 100644
--- a/tests/tcg/riscv64/Makefile.target
+++ b/tests/tcg/riscv64/Makefile.target
@@ -3,3 +3,4 @@
VPATH += $(SRC_PATH)/tests/tcg/riscv64
TESTS += test-div
+TESTS += noexec
diff --git a/tests/tcg/riscv64/noexec.c b/tests/tcg/riscv64/noexec.c
new file mode 100644
index 0000000000..86f64b28db
--- /dev/null
+++ b/tests/tcg/riscv64/noexec.c
@@ -0,0 +1,79 @@
+#include "../multiarch/noexec.c.inc"
+
+static void *arch_mcontext_pc(const mcontext_t *ctx)
+{
+ return (void *)ctx->__gregs[REG_PC];
+}
+
+static int arch_mcontext_arg(const mcontext_t *ctx)
+{
+ return ctx->__gregs[REG_A0];
+}
+
+static void arch_flush(void *p, int len)
+{
+ __builtin___clear_cache(p, p + len);
+}
+
+extern char noexec_1[];
+extern char noexec_2[];
+extern char noexec_end[];
+
+asm(".option push\n"
+ ".option norvc\n"
+ "noexec_1:\n"
+ " li a0,1\n" /* a0 is 0 on entry, set 1. */
+ "noexec_2:\n"
+ " li a0,2\n" /* a0 is 0/1; set 2. */
+ " ret\n"
+ "noexec_end:\n"
+ ".option pop");
+
+int main(void)
+{
+ struct noexec_test noexec_tests[] = {
+ {
+ .name = "fallthrough",
+ .test_code = noexec_1,
+ .test_len = noexec_end - noexec_1,
+ .page_ofs = noexec_1 - noexec_2,
+ .entry_ofs = noexec_1 - noexec_2,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = 0,
+ .expected_arg = 1,
+ },
+ {
+ .name = "jump",
+ .test_code = noexec_1,
+ .test_len = noexec_end - noexec_1,
+ .page_ofs = noexec_1 - noexec_2,
+ .entry_ofs = 0,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = 0,
+ .expected_arg = 0,
+ },
+ {
+ .name = "fallthrough [cross]",
+ .test_code = noexec_1,
+ .test_len = noexec_end - noexec_1,
+ .page_ofs = noexec_1 - noexec_2 - 2,
+ .entry_ofs = noexec_1 - noexec_2 - 2,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = -2,
+ .expected_arg = 1,
+ },
+ {
+ .name = "jump [cross]",
+ .test_code = noexec_1,
+ .test_len = noexec_end - noexec_1,
+ .page_ofs = noexec_1 - noexec_2 - 2,
+ .entry_ofs = -2,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = -2,
+ .expected_arg = 0,
+ },
+ };
+
+ return test_noexec(noexec_tests,
+ sizeof(noexec_tests) / sizeof(noexec_tests[0]));
+}
diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target
index 1a7a4a2f59..5e13a41c3f 100644
--- a/tests/tcg/s390x/Makefile.target
+++ b/tests/tcg/s390x/Makefile.target
@@ -16,6 +16,7 @@ TESTS+=shift
TESTS+=trap
TESTS+=signals-s390x
TESTS+=branch-relative-long
+TESTS+=noexec
Z14_TESTS=vfminmax
vfminmax: LDFLAGS+=-lm
diff --git a/tests/tcg/s390x/noexec.c b/tests/tcg/s390x/noexec.c
new file mode 100644
index 0000000000..15d007d07f
--- /dev/null
+++ b/tests/tcg/s390x/noexec.c
@@ -0,0 +1,106 @@
+#include "../multiarch/noexec.c.inc"
+
+static void *arch_mcontext_pc(const mcontext_t *ctx)
+{
+ return (void *)ctx->psw.addr;
+}
+
+static int arch_mcontext_arg(const mcontext_t *ctx)
+{
+ return ctx->gregs[2];
+}
+
+static void arch_flush(void *p, int len)
+{
+}
+
+extern char noexec_1[];
+extern char noexec_2[];
+extern char noexec_end[];
+
+asm("noexec_1:\n"
+ " lgfi %r2,1\n" /* %r2 is 0 on entry, set 1. */
+ "noexec_2:\n"
+ " lgfi %r2,2\n" /* %r2 is 0/1; set 2. */
+ " br %r14\n" /* return */
+ "noexec_end:");
+
+extern char exrl_1[];
+extern char exrl_2[];
+extern char exrl_end[];
+
+asm("exrl_1:\n"
+ " exrl %r0, exrl_2\n"
+ " br %r14\n"
+ "exrl_2:\n"
+ " lgfi %r2,2\n"
+ "exrl_end:");
+
+int main(void)
+{
+ struct noexec_test noexec_tests[] = {
+ {
+ .name = "fallthrough",
+ .test_code = noexec_1,
+ .test_len = noexec_end - noexec_1,
+ .page_ofs = noexec_1 - noexec_2,
+ .entry_ofs = noexec_1 - noexec_2,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = 0,
+ .expected_arg = 1,
+ },
+ {
+ .name = "jump",
+ .test_code = noexec_1,
+ .test_len = noexec_end - noexec_1,
+ .page_ofs = noexec_1 - noexec_2,
+ .entry_ofs = 0,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = 0,
+ .expected_arg = 0,
+ },
+ {
+ .name = "exrl",
+ .test_code = exrl_1,
+ .test_len = exrl_end - exrl_1,
+ .page_ofs = exrl_1 - exrl_2,
+ .entry_ofs = exrl_1 - exrl_2,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = exrl_1 - exrl_2,
+ .expected_arg = 0,
+ },
+ {
+ .name = "fallthrough [cross]",
+ .test_code = noexec_1,
+ .test_len = noexec_end - noexec_1,
+ .page_ofs = noexec_1 - noexec_2 - 2,
+ .entry_ofs = noexec_1 - noexec_2 - 2,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = -2,
+ .expected_arg = 1,
+ },
+ {
+ .name = "jump [cross]",
+ .test_code = noexec_1,
+ .test_len = noexec_end - noexec_1,
+ .page_ofs = noexec_1 - noexec_2 - 2,
+ .entry_ofs = -2,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = -2,
+ .expected_arg = 0,
+ },
+ {
+ .name = "exrl [cross]",
+ .test_code = exrl_1,
+ .test_len = exrl_end - exrl_1,
+ .page_ofs = exrl_1 - exrl_2 - 2,
+ .entry_ofs = exrl_1 - exrl_2 - 2,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = exrl_1 - exrl_2 - 2,
+ .expected_arg = 0,
+ },
+ };
+
+ return test_noexec(noexec_tests,
+ sizeof(noexec_tests) / sizeof(noexec_tests[0]));
+}
diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target
index 6177fd845a..861a0966f4 100644
--- a/tests/tcg/x86_64/Makefile.target
+++ b/tests/tcg/x86_64/Makefile.target
@@ -10,6 +10,7 @@ include $(SRC_PATH)/tests/tcg/i386/Makefile.target
ifeq ($(filter %-linux-user, $(TARGET)),$(TARGET))
X86_64_TESTS += vsyscall
+X86_64_TESTS += noexec
TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64
else
TESTS=$(MULTIARCH_TESTS)
@@ -23,5 +24,5 @@ test-x86_64: LDFLAGS+=-lm -lc
test-x86_64: test-i386.c test-i386.h test-i386-shift.h test-i386-muldiv.h
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
-vsyscall: $(SRC_PATH)/tests/tcg/x86_64/vsyscall.c
+%: $(SRC_PATH)/tests/tcg/x86_64/%.c
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
diff --git a/tests/tcg/x86_64/noexec.c b/tests/tcg/x86_64/noexec.c
new file mode 100644
index 0000000000..9b124901be
--- /dev/null
+++ b/tests/tcg/x86_64/noexec.c
@@ -0,0 +1,75 @@
+#include "../multiarch/noexec.c.inc"
+
+static void *arch_mcontext_pc(const mcontext_t *ctx)
+{
+ return (void *)ctx->gregs[REG_RIP];
+}
+
+int arch_mcontext_arg(const mcontext_t *ctx)
+{
+ return ctx->gregs[REG_RDI];
+}
+
+static void arch_flush(void *p, int len)
+{
+}
+
+extern char noexec_1[];
+extern char noexec_2[];
+extern char noexec_end[];
+
+asm("noexec_1:\n"
+ " movq $1,%rdi\n" /* %rdi is 0 on entry, set 1. */
+ "noexec_2:\n"
+ " movq $2,%rdi\n" /* %rdi is 0/1; set 2. */
+ " ret\n"
+ "noexec_end:");
+
+int main(void)
+{
+ struct noexec_test noexec_tests[] = {
+ {
+ .name = "fallthrough",
+ .test_code = noexec_1,
+ .test_len = noexec_end - noexec_1,
+ .page_ofs = noexec_1 - noexec_2,
+ .entry_ofs = noexec_1 - noexec_2,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = 0,
+ .expected_arg = 1,
+ },
+ {
+ .name = "jump",
+ .test_code = noexec_1,
+ .test_len = noexec_end - noexec_1,
+ .page_ofs = noexec_1 - noexec_2,
+ .entry_ofs = 0,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = 0,
+ .expected_arg = 0,
+ },
+ {
+ .name = "fallthrough [cross]",
+ .test_code = noexec_1,
+ .test_len = noexec_end - noexec_1,
+ .page_ofs = noexec_1 - noexec_2 - 2,
+ .entry_ofs = noexec_1 - noexec_2 - 2,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = -2,
+ .expected_arg = 1,
+ },
+ {
+ .name = "jump [cross]",
+ .test_code = noexec_1,
+ .test_len = noexec_end - noexec_1,
+ .page_ofs = noexec_1 - noexec_2 - 2,
+ .entry_ofs = -2,
+ .expected_si_ofs = 0,
+ .expected_pc_ofs = -2,
+ .expected_arg = 0,
+ },
+ };
+
+ return test_noexec(noexec_tests,
+ sizeof(noexec_tests) / sizeof(noexec_tests[0]));
+}