From: Benjamin Gray <bgray@linux.ibm.com>
To: linuxppc-dev@lists.ozlabs.org
Cc: ajd@linux.ibm.com, ruscur@russell.cc,
linux-kernel@vger.kernel.org, linux-hardening@vger.kernel.org,
cmr@bluescreens.de, Benjamin Gray <bgray@linux.ibm.com>
Subject: [RFC PATCH 10/13] selftests/powerpc: Add hashst/hashchk test
Date: Mon, 28 Nov 2022 13:44:55 +1100 [thread overview]
Message-ID: <20221128024458.46121-11-bgray@linux.ibm.com> (raw)
In-Reply-To: <20221128024458.46121-1-bgray@linux.ibm.com>
Test the kernel DEXCR[NPHIE] interface and hashchk exception handling.
Introduces with it a DEXCR utils library for common DEXCR operations.
Signed-off-by: Benjamin Gray <bgray@linux.ibm.com>
---
tools/testing/selftests/powerpc/Makefile | 1 +
.../selftests/powerpc/dexcr/.gitignore | 1 +
.../testing/selftests/powerpc/dexcr/Makefile | 9 +
tools/testing/selftests/powerpc/dexcr/dexcr.c | 118 +++++++++
tools/testing/selftests/powerpc/dexcr/dexcr.h | 52 ++++
.../selftests/powerpc/dexcr/hashchk_test.c | 229 ++++++++++++++++++
tools/testing/selftests/powerpc/include/reg.h | 4 +
7 files changed, 414 insertions(+)
create mode 100644 tools/testing/selftests/powerpc/dexcr/.gitignore
create mode 100644 tools/testing/selftests/powerpc/dexcr/Makefile
create mode 100644 tools/testing/selftests/powerpc/dexcr/dexcr.c
create mode 100644 tools/testing/selftests/powerpc/dexcr/dexcr.h
create mode 100644 tools/testing/selftests/powerpc/dexcr/hashchk_test.c
diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile
index 6ba95cd19e42..00dbd000ee01 100644
--- a/tools/testing/selftests/powerpc/Makefile
+++ b/tools/testing/selftests/powerpc/Makefile
@@ -17,6 +17,7 @@ SUB_DIRS = alignment \
benchmarks \
cache_shape \
copyloops \
+ dexcr \
dscr \
mm \
nx-gzip \
diff --git a/tools/testing/selftests/powerpc/dexcr/.gitignore b/tools/testing/selftests/powerpc/dexcr/.gitignore
new file mode 100644
index 000000000000..37adb7f47832
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/.gitignore
@@ -0,0 +1 @@
+hashchk_user
diff --git a/tools/testing/selftests/powerpc/dexcr/Makefile b/tools/testing/selftests/powerpc/dexcr/Makefile
new file mode 100644
index 000000000000..4b4380d4d986
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/Makefile
@@ -0,0 +1,9 @@
+TEST_GEN_PROGS := hashchk_test
+
+TEST_FILES := settings
+top_srcdir = ../../../../..
+include ../../lib.mk
+
+HASHCHK_TEST_CFLAGS = -no-pie $(call cc-option,-mno-rop-protect)
+
+$(TEST_GEN_PROGS): ../harness.c ../utils.c ./dexcr.c
diff --git a/tools/testing/selftests/powerpc/dexcr/dexcr.c b/tools/testing/selftests/powerpc/dexcr/dexcr.c
new file mode 100644
index 000000000000..3e7cb581d4a2
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/dexcr.c
@@ -0,0 +1,118 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+
+#include "dexcr.h"
+#include "reg.h"
+#include "utils.h"
+
+long sysctl_get_sbhe(void)
+{
+ long value;
+
+ FAIL_IF_EXIT_MSG(read_long(SYSCTL_DEXCR_SBHE, &value, 10),
+ "failed to read " SYSCTL_DEXCR_SBHE);
+
+ return value;
+}
+
+void sysctl_set_sbhe(long value)
+{
+ FAIL_IF_EXIT_MSG(write_long(SYSCTL_DEXCR_SBHE, value, 10),
+ "failed to write to " SYSCTL_DEXCR_SBHE);
+}
+
+unsigned int pr_aspect_to_dexcr_mask(unsigned long which)
+{
+ switch (which) {
+ case PR_PPC_DEXCR_SBHE:
+ return DEXCR_PRO_SBHE;
+ case PR_PPC_DEXCR_IBRTPD:
+ return DEXCR_PRO_IBRTPD;
+ case PR_PPC_DEXCR_SRAPD:
+ return DEXCR_PRO_SRAPD;
+ case PR_PPC_DEXCR_NPHIE:
+ return DEXCR_PRO_NPHIE;
+ default:
+ FAIL_IF_EXIT_MSG(true, "unknown PR aspect");
+ }
+}
+
+static inline unsigned int get_dexcr_pro(void)
+{
+ return mfspr(SPRN_DEXCR);
+}
+
+static inline unsigned int get_dexcr_enf(void)
+{
+ return mfspr(SPRN_HDEXCR);
+}
+
+static inline unsigned int get_dexcr_eff(void)
+{
+ return get_dexcr_pro() | get_dexcr_enf();
+}
+
+unsigned int get_dexcr(enum DexcrSource source)
+{
+ switch (source) {
+ case UDEXCR:
+ return get_dexcr_pro();
+ case ENFORCED:
+ return get_dexcr_enf();
+ case EFFECTIVE:
+ return get_dexcr_eff();
+ default:
+ FAIL_IF_EXIT_MSG(true, "bad DEXCR source");
+ }
+}
+
+bool pr_aspect_supported(unsigned long which)
+{
+ return prctl(PR_PPC_GET_DEXCR, which, 0, 0, 0) >= 0;
+}
+
+bool pr_aspect_editable(unsigned long which)
+{
+ int ret = prctl(PR_PPC_GET_DEXCR, which, 0, 0, 0);
+ return ret > 0 && (ret & PR_PPC_DEXCR_PRCTL) > 0;
+}
+
+bool pr_aspect_edit(unsigned long which, unsigned long ctrl)
+{
+ return prctl(PR_PPC_SET_DEXCR, which, ctrl, 0, 0) == 0;
+}
+
+bool pr_aspect_check(unsigned long which, enum DexcrSource source)
+{
+ unsigned int dexcr = get_dexcr(source);
+ unsigned int aspect = pr_aspect_to_dexcr_mask(which);
+ return (dexcr & aspect) != 0;
+}
+
+int pr_aspect_get(unsigned long pr_aspect)
+{
+ int ret = prctl(PR_PPC_GET_DEXCR, pr_aspect, 0, 0, 0);
+ FAIL_IF_EXIT_MSG(ret < 0, "prctl failed");
+ return ret;
+}
+
+bool dexcr_pro_check(unsigned int pro, enum DexcrSource source)
+{
+ return (get_dexcr(source) & pro) != 0;
+}
+
+void await_child_success(pid_t pid)
+{
+ int wstatus;
+
+ FAIL_IF_EXIT_MSG(pid == -1, "fork failed");
+ FAIL_IF_EXIT_MSG(waitpid(pid, &wstatus, 0) == -1, "wait failed");
+ FAIL_IF_EXIT_MSG(!WIFEXITED(wstatus), "child did not exit cleanly");
+ FAIL_IF_EXIT_MSG(WEXITSTATUS(wstatus) != 0, "child exit error");
+}
diff --git a/tools/testing/selftests/powerpc/dexcr/dexcr.h b/tools/testing/selftests/powerpc/dexcr/dexcr.h
new file mode 100644
index 000000000000..fb8007bf19f8
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/dexcr.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * POWER Dynamic Execution Control Facility (DEXCR)
+ *
+ * This header file contains helper functions and macros
+ * required for all the DEXCR related test cases.
+ */
+#ifndef _SELFTESTS_POWERPC_DEXCR_DEXCR_H
+#define _SELFTESTS_POWERPC_DEXCR_DEXCR_H
+
+#include <stdbool.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "reg.h"
+#include "utils.h"
+
+#define DEXCR_PRO_MASK(aspect) __MASK(63 - (32 + (aspect)))
+#define DEXCR_PRO_SBHE DEXCR_PRO_MASK(0)
+#define DEXCR_PRO_IBRTPD DEXCR_PRO_MASK(3)
+#define DEXCR_PRO_SRAPD DEXCR_PRO_MASK(4)
+#define DEXCR_PRO_NPHIE DEXCR_PRO_MASK(5)
+
+enum DexcrSource {
+ UDEXCR, /* Userspace DEXCR value */
+ ENFORCED, /* Enforced by hypervisor */
+ EFFECTIVE, /* Bitwise OR of requested and enforced DEXCR bits */
+};
+
+unsigned int get_dexcr(enum DexcrSource source);
+
+bool pr_aspect_supported(unsigned long which);
+
+bool pr_aspect_editable(unsigned long which);
+
+bool pr_aspect_edit(unsigned long which, unsigned long ctrl);
+
+bool pr_aspect_check(unsigned long which, enum DexcrSource source);
+
+int pr_aspect_get(unsigned long which);
+
+unsigned int pr_aspect_to_dexcr_mask(unsigned long which);
+
+bool dexcr_pro_check(unsigned int pro, enum DexcrSource source);
+
+long sysctl_get_sbhe(void);
+
+void sysctl_set_sbhe(long value);
+
+void await_child_success(pid_t pid);
+
+#endif /* _SELFTESTS_POWERPC_DEXCR_DEXCR_H */
diff --git a/tools/testing/selftests/powerpc/dexcr/hashchk_test.c b/tools/testing/selftests/powerpc/dexcr/hashchk_test.c
new file mode 100644
index 000000000000..3351bdbdaf13
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/hashchk_test.c
@@ -0,0 +1,229 @@
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "dexcr.h"
+#include "utils.h"
+
+static int require_nphie(void)
+{
+ SKIP_IF_MSG(!pr_aspect_supported(PR_PPC_DEXCR_NPHIE),
+ "DEXCR[NPHIE] not supported");
+
+ if (dexcr_pro_check(DEXCR_PRO_NPHIE, EFFECTIVE))
+ return 0;
+
+ pr_aspect_edit(PR_PPC_DEXCR_NPHIE, PR_PPC_DEXCR_FORCE_SET_ASPECT);
+ FAIL_IF_EXIT_MSG(!dexcr_pro_check(DEXCR_PRO_NPHIE, EFFECTIVE),
+ "failed to enable DEXCR[NPIHE]");
+
+ return 0;
+}
+
+static void sigill_handler_enabled(int signum, siginfo_t *info, void *context)
+{
+ SIGSAFE_FAIL_IF_EXIT_MSG(signum != SIGILL, "wrong signal received");
+ SIGSAFE_FAIL_IF_EXIT_MSG(info->si_code != ILL_ILLOPN, "wrong signal-code received");
+ exit(0);
+}
+
+static void do_bad_hashchk(void)
+{
+ unsigned long hash = 0;
+ void *hash_p = ((void *)&hash) + 8; /* hash* offset must be at least -8 */
+
+ asm ("li 3, 0;" /* set r3 (pretend LR) to known value */
+ "hashst 3, -8(%1);" /* compute good hash */
+ "addi 3, 3, 1;" /* modify hash */
+ "hashchk 3, -8(%1);" /* check bad hash */
+ : "+m" (hash) : "r" (hash_p) : "r3");
+}
+
+/*
+ * Check that hashchk triggers when DEXCR[NPHIE] is enabled
+ * and is detected as such by the kernel exception handler
+ */
+static int hashchk_enabled_test(void)
+{
+ int err;
+ struct sigaction sa;
+
+ if ((err = require_nphie()))
+ return err;
+
+ sa.sa_sigaction = sigill_handler_enabled;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_SIGINFO;
+ FAIL_IF_MSG(sigaction(SIGILL, &sa, NULL), "cannot install signal handler");
+
+ do_bad_hashchk();
+
+ FAIL_IF_MSG(true, "hashchk failed to trigger");
+}
+
+#define HASH_COUNT 8
+
+static unsigned long hash_values[HASH_COUNT + 1];
+
+static void fill_hash_values(void)
+{
+ for (unsigned long i = 0; i < HASH_COUNT; i++) {
+ void *hash_addr = ((void*)&hash_values[i]) + 8;
+
+ asm volatile ("hashst %2, -8(%1);"
+ : "+m" (hash_values[i]) : "r" (hash_addr), "r" (i));
+ }
+
+ hash_values[HASH_COUNT] = (unsigned long)&hash_values;
+}
+
+static unsigned int count_hash_values_matches(void)
+{
+ unsigned long matches = 0;
+
+ FAIL_IF_EXIT_MSG(hash_values[HASH_COUNT] != (unsigned long)hash_values,
+ "bad address check");
+
+ for (unsigned long i = 0; i < HASH_COUNT; i++) {
+ unsigned long orig_hash = hash_values[i];
+ void *hash_addr = ((void*)&hash_values[i]) + 8;
+
+ asm volatile ("hashst %2, -8(%1);"
+ : "+m" (hash_values[i]) : "r" (hash_addr), "r" (i));
+
+ if (hash_values[i] == orig_hash)
+ matches++;
+ }
+
+ return matches;
+}
+
+static int hashchk_exec_child(void)
+{
+ ssize_t count;
+
+ fill_hash_values();
+
+ count = write(STDOUT_FILENO, hash_values, sizeof(hash_values));
+ return count == sizeof(hash_values) ? 0 : EOVERFLOW;
+}
+
+/*
+ * Check that new programs get different keys so a malicious process
+ * can't recreate a victim's hash values.
+ */
+static int hashchk_exec_random_key_test(void)
+{
+ pid_t pid;
+ int err;
+ int pipefd[2];
+
+ if ((err = require_nphie()))
+ return err;
+
+ FAIL_IF_MSG(pipe(pipefd), "failed to create pipe");
+
+ pid = fork();
+ if (pid == 0) {
+ char *args[] = { "hashchk_exec_child", NULL };
+
+ if (dup2(pipefd[1], STDOUT_FILENO) == -1)
+ _exit(errno);
+
+ execve("/proc/self/exe", args, NULL);
+ _exit(errno);
+ }
+
+ await_child_success(pid);
+ FAIL_IF_MSG(read(pipefd[0], hash_values, sizeof(hash_values)) != sizeof(hash_values),
+ "missing expected child output");
+
+ /* If all hashes are the same it means (most likely) same key */
+ FAIL_IF_MSG(count_hash_values_matches() == HASH_COUNT, "shared key detected");
+
+ return 0;
+}
+
+/*
+ * Check that forks share the same key so that existing hash values
+ * remain valid.
+ */
+static int hashchk_fork_share_key_test(void)
+{
+ pid_t pid;
+ int err;
+
+ if ((err = require_nphie()))
+ return err;
+
+ fill_hash_values();
+
+ pid = fork();
+ if (pid == 0) {
+ if (count_hash_values_matches() != HASH_COUNT)
+ _exit(1);
+ _exit(0);
+ }
+
+ await_child_success(pid);
+ return 0;
+}
+
+#define STACK_SIZE (1024 * 1024)
+
+static int hashchk_clone_child_fn(void *args)
+{
+ fill_hash_values();
+ return 0;
+}
+
+/*
+ * Check that threads share the same key so that existing hash values
+ * remain valid.
+ */
+static int hashchk_clone_share_key_test(void)
+{
+ void *child_stack;
+ pid_t pid;
+ int err;
+
+ if ((err = require_nphie()))
+ return err;
+
+ child_stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
+
+ FAIL_IF_MSG(child_stack == MAP_FAILED, "failed to map child stack");
+
+ pid = clone(hashchk_clone_child_fn, child_stack + STACK_SIZE, CLONE_VM | SIGCHLD, NULL);
+
+ await_child_success(pid);
+ FAIL_IF_MSG(count_hash_values_matches() != HASH_COUNT, "different key detected");
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int err = 0;
+
+ if (argc >= 1 && !strcmp(argv[0], "hashchk_exec_child"))
+ return hashchk_exec_child();
+
+ err |= test_harness(hashchk_enabled_test, "hashchk_enabled");
+ err |= test_harness(hashchk_exec_random_key_test, "hashchk_exec_random_key");
+ err |= test_harness(hashchk_fork_share_key_test, "hashchk_fork_share_key");
+ err |= test_harness(hashchk_clone_share_key_test, "hashchk_clone_share_key");
+
+ return err;
+}
diff --git a/tools/testing/selftests/powerpc/include/reg.h b/tools/testing/selftests/powerpc/include/reg.h
index d5a547f72669..cbb5979cb3e2 100644
--- a/tools/testing/selftests/powerpc/include/reg.h
+++ b/tools/testing/selftests/powerpc/include/reg.h
@@ -19,6 +19,8 @@
#define mb() asm volatile("sync" : : : "memory");
#define barrier() asm volatile("" : : : "memory");
+#define SPRN_HDEXCR 455
+
#define SPRN_MMCR2 769
#define SPRN_MMCRA 770
#define SPRN_MMCR0 779
@@ -47,6 +49,8 @@
#define SPRN_SDAR 781
#define SPRN_SIER 768
+#define SPRN_DEXCR 812
+
#define SPRN_TEXASR 0x82 /* Transaction Exception and Status Register */
#define SPRN_TFIAR 0x81 /* Transaction Failure Inst Addr */
#define SPRN_TFHAR 0x80 /* Transaction Failure Handler Addr */
--
2.38.1
WARNING: multiple messages have this Message-ID (diff)
From: Benjamin Gray <bgray@linux.ibm.com>
To: linuxppc-dev@lists.ozlabs.org
Cc: ajd@linux.ibm.com, linux-kernel@vger.kernel.org,
linux-hardening@vger.kernel.org, cmr@bluescreens.de,
Benjamin Gray <bgray@linux.ibm.com>
Subject: [RFC PATCH 10/13] selftests/powerpc: Add hashst/hashchk test
Date: Mon, 28 Nov 2022 13:44:55 +1100 [thread overview]
Message-ID: <20221128024458.46121-11-bgray@linux.ibm.com> (raw)
In-Reply-To: <20221128024458.46121-1-bgray@linux.ibm.com>
Test the kernel DEXCR[NPHIE] interface and hashchk exception handling.
Introduces with it a DEXCR utils library for common DEXCR operations.
Signed-off-by: Benjamin Gray <bgray@linux.ibm.com>
---
tools/testing/selftests/powerpc/Makefile | 1 +
.../selftests/powerpc/dexcr/.gitignore | 1 +
.../testing/selftests/powerpc/dexcr/Makefile | 9 +
tools/testing/selftests/powerpc/dexcr/dexcr.c | 118 +++++++++
tools/testing/selftests/powerpc/dexcr/dexcr.h | 52 ++++
.../selftests/powerpc/dexcr/hashchk_test.c | 229 ++++++++++++++++++
tools/testing/selftests/powerpc/include/reg.h | 4 +
7 files changed, 414 insertions(+)
create mode 100644 tools/testing/selftests/powerpc/dexcr/.gitignore
create mode 100644 tools/testing/selftests/powerpc/dexcr/Makefile
create mode 100644 tools/testing/selftests/powerpc/dexcr/dexcr.c
create mode 100644 tools/testing/selftests/powerpc/dexcr/dexcr.h
create mode 100644 tools/testing/selftests/powerpc/dexcr/hashchk_test.c
diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile
index 6ba95cd19e42..00dbd000ee01 100644
--- a/tools/testing/selftests/powerpc/Makefile
+++ b/tools/testing/selftests/powerpc/Makefile
@@ -17,6 +17,7 @@ SUB_DIRS = alignment \
benchmarks \
cache_shape \
copyloops \
+ dexcr \
dscr \
mm \
nx-gzip \
diff --git a/tools/testing/selftests/powerpc/dexcr/.gitignore b/tools/testing/selftests/powerpc/dexcr/.gitignore
new file mode 100644
index 000000000000..37adb7f47832
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/.gitignore
@@ -0,0 +1 @@
+hashchk_user
diff --git a/tools/testing/selftests/powerpc/dexcr/Makefile b/tools/testing/selftests/powerpc/dexcr/Makefile
new file mode 100644
index 000000000000..4b4380d4d986
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/Makefile
@@ -0,0 +1,9 @@
+TEST_GEN_PROGS := hashchk_test
+
+TEST_FILES := settings
+top_srcdir = ../../../../..
+include ../../lib.mk
+
+HASHCHK_TEST_CFLAGS = -no-pie $(call cc-option,-mno-rop-protect)
+
+$(TEST_GEN_PROGS): ../harness.c ../utils.c ./dexcr.c
diff --git a/tools/testing/selftests/powerpc/dexcr/dexcr.c b/tools/testing/selftests/powerpc/dexcr/dexcr.c
new file mode 100644
index 000000000000..3e7cb581d4a2
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/dexcr.c
@@ -0,0 +1,118 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+
+#include "dexcr.h"
+#include "reg.h"
+#include "utils.h"
+
+long sysctl_get_sbhe(void)
+{
+ long value;
+
+ FAIL_IF_EXIT_MSG(read_long(SYSCTL_DEXCR_SBHE, &value, 10),
+ "failed to read " SYSCTL_DEXCR_SBHE);
+
+ return value;
+}
+
+void sysctl_set_sbhe(long value)
+{
+ FAIL_IF_EXIT_MSG(write_long(SYSCTL_DEXCR_SBHE, value, 10),
+ "failed to write to " SYSCTL_DEXCR_SBHE);
+}
+
+unsigned int pr_aspect_to_dexcr_mask(unsigned long which)
+{
+ switch (which) {
+ case PR_PPC_DEXCR_SBHE:
+ return DEXCR_PRO_SBHE;
+ case PR_PPC_DEXCR_IBRTPD:
+ return DEXCR_PRO_IBRTPD;
+ case PR_PPC_DEXCR_SRAPD:
+ return DEXCR_PRO_SRAPD;
+ case PR_PPC_DEXCR_NPHIE:
+ return DEXCR_PRO_NPHIE;
+ default:
+ FAIL_IF_EXIT_MSG(true, "unknown PR aspect");
+ }
+}
+
+static inline unsigned int get_dexcr_pro(void)
+{
+ return mfspr(SPRN_DEXCR);
+}
+
+static inline unsigned int get_dexcr_enf(void)
+{
+ return mfspr(SPRN_HDEXCR);
+}
+
+static inline unsigned int get_dexcr_eff(void)
+{
+ return get_dexcr_pro() | get_dexcr_enf();
+}
+
+unsigned int get_dexcr(enum DexcrSource source)
+{
+ switch (source) {
+ case UDEXCR:
+ return get_dexcr_pro();
+ case ENFORCED:
+ return get_dexcr_enf();
+ case EFFECTIVE:
+ return get_dexcr_eff();
+ default:
+ FAIL_IF_EXIT_MSG(true, "bad DEXCR source");
+ }
+}
+
+bool pr_aspect_supported(unsigned long which)
+{
+ return prctl(PR_PPC_GET_DEXCR, which, 0, 0, 0) >= 0;
+}
+
+bool pr_aspect_editable(unsigned long which)
+{
+ int ret = prctl(PR_PPC_GET_DEXCR, which, 0, 0, 0);
+ return ret > 0 && (ret & PR_PPC_DEXCR_PRCTL) > 0;
+}
+
+bool pr_aspect_edit(unsigned long which, unsigned long ctrl)
+{
+ return prctl(PR_PPC_SET_DEXCR, which, ctrl, 0, 0) == 0;
+}
+
+bool pr_aspect_check(unsigned long which, enum DexcrSource source)
+{
+ unsigned int dexcr = get_dexcr(source);
+ unsigned int aspect = pr_aspect_to_dexcr_mask(which);
+ return (dexcr & aspect) != 0;
+}
+
+int pr_aspect_get(unsigned long pr_aspect)
+{
+ int ret = prctl(PR_PPC_GET_DEXCR, pr_aspect, 0, 0, 0);
+ FAIL_IF_EXIT_MSG(ret < 0, "prctl failed");
+ return ret;
+}
+
+bool dexcr_pro_check(unsigned int pro, enum DexcrSource source)
+{
+ return (get_dexcr(source) & pro) != 0;
+}
+
+void await_child_success(pid_t pid)
+{
+ int wstatus;
+
+ FAIL_IF_EXIT_MSG(pid == -1, "fork failed");
+ FAIL_IF_EXIT_MSG(waitpid(pid, &wstatus, 0) == -1, "wait failed");
+ FAIL_IF_EXIT_MSG(!WIFEXITED(wstatus), "child did not exit cleanly");
+ FAIL_IF_EXIT_MSG(WEXITSTATUS(wstatus) != 0, "child exit error");
+}
diff --git a/tools/testing/selftests/powerpc/dexcr/dexcr.h b/tools/testing/selftests/powerpc/dexcr/dexcr.h
new file mode 100644
index 000000000000..fb8007bf19f8
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/dexcr.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * POWER Dynamic Execution Control Facility (DEXCR)
+ *
+ * This header file contains helper functions and macros
+ * required for all the DEXCR related test cases.
+ */
+#ifndef _SELFTESTS_POWERPC_DEXCR_DEXCR_H
+#define _SELFTESTS_POWERPC_DEXCR_DEXCR_H
+
+#include <stdbool.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "reg.h"
+#include "utils.h"
+
+#define DEXCR_PRO_MASK(aspect) __MASK(63 - (32 + (aspect)))
+#define DEXCR_PRO_SBHE DEXCR_PRO_MASK(0)
+#define DEXCR_PRO_IBRTPD DEXCR_PRO_MASK(3)
+#define DEXCR_PRO_SRAPD DEXCR_PRO_MASK(4)
+#define DEXCR_PRO_NPHIE DEXCR_PRO_MASK(5)
+
+enum DexcrSource {
+ UDEXCR, /* Userspace DEXCR value */
+ ENFORCED, /* Enforced by hypervisor */
+ EFFECTIVE, /* Bitwise OR of requested and enforced DEXCR bits */
+};
+
+unsigned int get_dexcr(enum DexcrSource source);
+
+bool pr_aspect_supported(unsigned long which);
+
+bool pr_aspect_editable(unsigned long which);
+
+bool pr_aspect_edit(unsigned long which, unsigned long ctrl);
+
+bool pr_aspect_check(unsigned long which, enum DexcrSource source);
+
+int pr_aspect_get(unsigned long which);
+
+unsigned int pr_aspect_to_dexcr_mask(unsigned long which);
+
+bool dexcr_pro_check(unsigned int pro, enum DexcrSource source);
+
+long sysctl_get_sbhe(void);
+
+void sysctl_set_sbhe(long value);
+
+void await_child_success(pid_t pid);
+
+#endif /* _SELFTESTS_POWERPC_DEXCR_DEXCR_H */
diff --git a/tools/testing/selftests/powerpc/dexcr/hashchk_test.c b/tools/testing/selftests/powerpc/dexcr/hashchk_test.c
new file mode 100644
index 000000000000..3351bdbdaf13
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/hashchk_test.c
@@ -0,0 +1,229 @@
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "dexcr.h"
+#include "utils.h"
+
+static int require_nphie(void)
+{
+ SKIP_IF_MSG(!pr_aspect_supported(PR_PPC_DEXCR_NPHIE),
+ "DEXCR[NPHIE] not supported");
+
+ if (dexcr_pro_check(DEXCR_PRO_NPHIE, EFFECTIVE))
+ return 0;
+
+ pr_aspect_edit(PR_PPC_DEXCR_NPHIE, PR_PPC_DEXCR_FORCE_SET_ASPECT);
+ FAIL_IF_EXIT_MSG(!dexcr_pro_check(DEXCR_PRO_NPHIE, EFFECTIVE),
+ "failed to enable DEXCR[NPIHE]");
+
+ return 0;
+}
+
+static void sigill_handler_enabled(int signum, siginfo_t *info, void *context)
+{
+ SIGSAFE_FAIL_IF_EXIT_MSG(signum != SIGILL, "wrong signal received");
+ SIGSAFE_FAIL_IF_EXIT_MSG(info->si_code != ILL_ILLOPN, "wrong signal-code received");
+ exit(0);
+}
+
+static void do_bad_hashchk(void)
+{
+ unsigned long hash = 0;
+ void *hash_p = ((void *)&hash) + 8; /* hash* offset must be at least -8 */
+
+ asm ("li 3, 0;" /* set r3 (pretend LR) to known value */
+ "hashst 3, -8(%1);" /* compute good hash */
+ "addi 3, 3, 1;" /* modify hash */
+ "hashchk 3, -8(%1);" /* check bad hash */
+ : "+m" (hash) : "r" (hash_p) : "r3");
+}
+
+/*
+ * Check that hashchk triggers when DEXCR[NPHIE] is enabled
+ * and is detected as such by the kernel exception handler
+ */
+static int hashchk_enabled_test(void)
+{
+ int err;
+ struct sigaction sa;
+
+ if ((err = require_nphie()))
+ return err;
+
+ sa.sa_sigaction = sigill_handler_enabled;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_SIGINFO;
+ FAIL_IF_MSG(sigaction(SIGILL, &sa, NULL), "cannot install signal handler");
+
+ do_bad_hashchk();
+
+ FAIL_IF_MSG(true, "hashchk failed to trigger");
+}
+
+#define HASH_COUNT 8
+
+static unsigned long hash_values[HASH_COUNT + 1];
+
+static void fill_hash_values(void)
+{
+ for (unsigned long i = 0; i < HASH_COUNT; i++) {
+ void *hash_addr = ((void*)&hash_values[i]) + 8;
+
+ asm volatile ("hashst %2, -8(%1);"
+ : "+m" (hash_values[i]) : "r" (hash_addr), "r" (i));
+ }
+
+ hash_values[HASH_COUNT] = (unsigned long)&hash_values;
+}
+
+static unsigned int count_hash_values_matches(void)
+{
+ unsigned long matches = 0;
+
+ FAIL_IF_EXIT_MSG(hash_values[HASH_COUNT] != (unsigned long)hash_values,
+ "bad address check");
+
+ for (unsigned long i = 0; i < HASH_COUNT; i++) {
+ unsigned long orig_hash = hash_values[i];
+ void *hash_addr = ((void*)&hash_values[i]) + 8;
+
+ asm volatile ("hashst %2, -8(%1);"
+ : "+m" (hash_values[i]) : "r" (hash_addr), "r" (i));
+
+ if (hash_values[i] == orig_hash)
+ matches++;
+ }
+
+ return matches;
+}
+
+static int hashchk_exec_child(void)
+{
+ ssize_t count;
+
+ fill_hash_values();
+
+ count = write(STDOUT_FILENO, hash_values, sizeof(hash_values));
+ return count == sizeof(hash_values) ? 0 : EOVERFLOW;
+}
+
+/*
+ * Check that new programs get different keys so a malicious process
+ * can't recreate a victim's hash values.
+ */
+static int hashchk_exec_random_key_test(void)
+{
+ pid_t pid;
+ int err;
+ int pipefd[2];
+
+ if ((err = require_nphie()))
+ return err;
+
+ FAIL_IF_MSG(pipe(pipefd), "failed to create pipe");
+
+ pid = fork();
+ if (pid == 0) {
+ char *args[] = { "hashchk_exec_child", NULL };
+
+ if (dup2(pipefd[1], STDOUT_FILENO) == -1)
+ _exit(errno);
+
+ execve("/proc/self/exe", args, NULL);
+ _exit(errno);
+ }
+
+ await_child_success(pid);
+ FAIL_IF_MSG(read(pipefd[0], hash_values, sizeof(hash_values)) != sizeof(hash_values),
+ "missing expected child output");
+
+ /* If all hashes are the same it means (most likely) same key */
+ FAIL_IF_MSG(count_hash_values_matches() == HASH_COUNT, "shared key detected");
+
+ return 0;
+}
+
+/*
+ * Check that forks share the same key so that existing hash values
+ * remain valid.
+ */
+static int hashchk_fork_share_key_test(void)
+{
+ pid_t pid;
+ int err;
+
+ if ((err = require_nphie()))
+ return err;
+
+ fill_hash_values();
+
+ pid = fork();
+ if (pid == 0) {
+ if (count_hash_values_matches() != HASH_COUNT)
+ _exit(1);
+ _exit(0);
+ }
+
+ await_child_success(pid);
+ return 0;
+}
+
+#define STACK_SIZE (1024 * 1024)
+
+static int hashchk_clone_child_fn(void *args)
+{
+ fill_hash_values();
+ return 0;
+}
+
+/*
+ * Check that threads share the same key so that existing hash values
+ * remain valid.
+ */
+static int hashchk_clone_share_key_test(void)
+{
+ void *child_stack;
+ pid_t pid;
+ int err;
+
+ if ((err = require_nphie()))
+ return err;
+
+ child_stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
+
+ FAIL_IF_MSG(child_stack == MAP_FAILED, "failed to map child stack");
+
+ pid = clone(hashchk_clone_child_fn, child_stack + STACK_SIZE, CLONE_VM | SIGCHLD, NULL);
+
+ await_child_success(pid);
+ FAIL_IF_MSG(count_hash_values_matches() != HASH_COUNT, "different key detected");
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int err = 0;
+
+ if (argc >= 1 && !strcmp(argv[0], "hashchk_exec_child"))
+ return hashchk_exec_child();
+
+ err |= test_harness(hashchk_enabled_test, "hashchk_enabled");
+ err |= test_harness(hashchk_exec_random_key_test, "hashchk_exec_random_key");
+ err |= test_harness(hashchk_fork_share_key_test, "hashchk_fork_share_key");
+ err |= test_harness(hashchk_clone_share_key_test, "hashchk_clone_share_key");
+
+ return err;
+}
diff --git a/tools/testing/selftests/powerpc/include/reg.h b/tools/testing/selftests/powerpc/include/reg.h
index d5a547f72669..cbb5979cb3e2 100644
--- a/tools/testing/selftests/powerpc/include/reg.h
+++ b/tools/testing/selftests/powerpc/include/reg.h
@@ -19,6 +19,8 @@
#define mb() asm volatile("sync" : : : "memory");
#define barrier() asm volatile("" : : : "memory");
+#define SPRN_HDEXCR 455
+
#define SPRN_MMCR2 769
#define SPRN_MMCRA 770
#define SPRN_MMCR0 779
@@ -47,6 +49,8 @@
#define SPRN_SDAR 781
#define SPRN_SIER 768
+#define SPRN_DEXCR 812
+
#define SPRN_TEXASR 0x82 /* Transaction Exception and Status Register */
#define SPRN_TFIAR 0x81 /* Transaction Failure Inst Addr */
#define SPRN_TFHAR 0x80 /* Transaction Failure Handler Addr */
--
2.38.1
next prev parent reply other threads:[~2022-11-28 2:46 UTC|newest]
Thread overview: 58+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-11-28 2:44 [RFC PATCH 00/13] Add DEXCR support Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray
2022-11-28 2:44 ` [RFC PATCH 01/13] powerpc/book3s: Add missing <linux/sched.h> include Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray
2023-03-07 4:28 ` Nicholas Piggin
2023-03-07 4:28 ` Nicholas Piggin
2022-11-28 2:44 ` [RFC PATCH 02/13] powerpc: Add initial Dynamic Execution Control Register (DEXCR) support Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray
2023-03-07 4:45 ` Nicholas Piggin
2023-03-07 4:45 ` Nicholas Piggin
2023-03-09 23:46 ` Benjamin Gray
2023-03-09 23:46 ` Benjamin Gray
2022-11-28 2:44 ` [RFC PATCH 03/13] powerpc/dexcr: Handle hashchk exception Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray
2022-11-29 10:39 ` Nicholas Piggin
2022-11-29 10:39 ` Nicholas Piggin
2022-11-29 22:04 ` Benjamin Gray
2022-11-29 22:04 ` Benjamin Gray
2022-11-28 2:44 ` [RFC PATCH 04/13] powerpc/dexcr: Support userspace ROP protection Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray
2023-03-07 5:05 ` Nicholas Piggin
2023-03-07 5:05 ` Nicholas Piggin
2023-03-07 5:37 ` Benjamin Gray
2023-03-07 5:37 ` Benjamin Gray
2023-03-21 4:51 ` Nicholas Piggin
2023-03-21 4:51 ` Nicholas Piggin
2022-11-28 2:44 ` [RFC PATCH 05/13] prctl: Define PowerPC DEXCR interface Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray
2023-03-07 5:07 ` Nicholas Piggin
2023-03-07 5:07 ` Nicholas Piggin
2022-11-28 2:44 ` [RFC PATCH 06/13] powerpc/dexcr: Add prctl implementation Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray
2023-03-07 5:12 ` Nicholas Piggin
2023-03-07 5:12 ` Nicholas Piggin
2022-11-28 2:44 ` [RFC PATCH 07/13] powerpc/dexcr: Add sysctl entry for SBHE system override Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray
2023-03-07 5:30 ` Nicholas Piggin
2023-03-07 5:30 ` Nicholas Piggin
2023-03-07 5:58 ` Benjamin Gray
2023-03-07 5:58 ` Benjamin Gray
2022-11-28 2:44 ` [RFC PATCH 08/13] powerpc/dexcr: Add enforced userspace ROP protection config Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray
2022-11-28 2:44 ` [RFC PATCH 09/13] selftests/powerpc: Add more utility macros Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray [this message]
2022-11-28 2:44 ` [RFC PATCH 10/13] selftests/powerpc: Add hashst/hashchk test Benjamin Gray
2022-11-28 2:44 ` [RFC PATCH 11/13] selftests/powerpc: Add DEXCR prctl, sysctl interface test Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray
2022-11-28 2:44 ` [RFC PATCH 12/13] selftests/powerpc: Add DEXCR status utility lsdexcr Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray
2022-11-28 2:44 ` [RFC PATCH 13/13] Documentation: Document PowerPC kernel DEXCR interface Benjamin Gray
2022-11-28 2:44 ` Benjamin Gray
2023-03-07 5:40 ` Nicholas Piggin
2023-03-07 5:40 ` Nicholas Piggin
2023-03-07 5:52 ` Benjamin Gray
2023-03-07 5:52 ` Benjamin Gray
2022-11-28 4:05 ` [RFC PATCH 00/13] Add DEXCR support Russell Currey
2022-11-28 4:05 ` Russell Currey
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=20221128024458.46121-11-bgray@linux.ibm.com \
--to=bgray@linux.ibm.com \
--cc=ajd@linux.ibm.com \
--cc=cmr@bluescreens.de \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=ruscur@russell.cc \
/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.