Some basic tests of the prctl interface of the DEXCR.

Signed-off-by: Benjamin Gray <bg...@linux.ibm.com>
---
 .../selftests/powerpc/dexcr/.gitignore        |   1 +
 .../testing/selftests/powerpc/dexcr/Makefile  |   4 +-
 tools/testing/selftests/powerpc/dexcr/dexcr.c |  40 ++++
 tools/testing/selftests/powerpc/dexcr/dexcr.h |  10 +
 .../selftests/powerpc/dexcr/dexcr_test.c      | 213 ++++++++++++++++++
 5 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/powerpc/dexcr/dexcr_test.c

diff --git a/tools/testing/selftests/powerpc/dexcr/.gitignore 
b/tools/testing/selftests/powerpc/dexcr/.gitignore
index b82f45dd46b9..5d526613cd26 100644
--- a/tools/testing/selftests/powerpc/dexcr/.gitignore
+++ b/tools/testing/selftests/powerpc/dexcr/.gitignore
@@ -1,2 +1,3 @@
+dexcr_test
 hashchk_test
 lsdexcr
diff --git a/tools/testing/selftests/powerpc/dexcr/Makefile 
b/tools/testing/selftests/powerpc/dexcr/Makefile
index 829ad075b4a4..076943193c07 100644
--- a/tools/testing/selftests/powerpc/dexcr/Makefile
+++ b/tools/testing/selftests/powerpc/dexcr/Makefile
@@ -1,8 +1,10 @@
-TEST_GEN_PROGS := hashchk_test
+TEST_GEN_PROGS := dexcr_test hashchk_test
 TEST_GEN_FILES := lsdexcr
 
 include ../../lib.mk
 
+CFLAGS += $(KHDR_INCLUDES)
+
 $(OUTPUT)/hashchk_test: CFLAGS += -fno-pie -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
index 65ec5347de98..468fd0dc9912 100644
--- a/tools/testing/selftests/powerpc/dexcr/dexcr.c
+++ b/tools/testing/selftests/powerpc/dexcr/dexcr.c
@@ -3,6 +3,7 @@
 #include <errno.h>
 #include <setjmp.h>
 #include <signal.h>
+#include <sys/prctl.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 
@@ -43,6 +44,45 @@ bool dexcr_exists(void)
        return exists;
 }
 
+unsigned int pr_which_to_aspect(unsigned long which)
+{
+       switch (which) {
+       case PR_PPC_DEXCR_SBHE:
+               return DEXCR_PR_SBHE;
+       case PR_PPC_DEXCR_IBRTPD:
+               return DEXCR_PR_IBRTPD;
+       case PR_PPC_DEXCR_SRAPD:
+               return DEXCR_PR_SRAPD;
+       case PR_PPC_DEXCR_NPHIE:
+               return DEXCR_PR_NPHIE;
+       default:
+               FAIL_IF_EXIT_MSG(true, "unknown PR aspect");
+       }
+}
+
+int pr_get_dexcr(unsigned long which)
+{
+       return prctl(PR_PPC_GET_DEXCR, which, 0UL, 0UL, 0UL);
+}
+
+int pr_set_dexcr(unsigned long which, unsigned long ctrl)
+{
+       return prctl(PR_PPC_SET_DEXCR, which, ctrl, 0UL, 0UL);
+}
+
+bool pr_dexcr_aspect_supported(unsigned long which)
+{
+       if (pr_get_dexcr(which) == -1)
+               return errno == ENODEV;
+
+       return true;
+}
+
+bool pr_dexcr_aspect_editable(unsigned long which)
+{
+       return pr_get_dexcr(which) & PR_PPC_DEXCR_CTRL_EDITABLE;
+}
+
 /*
  * Just test if a bad hashchk triggers a signal, without checking
  * for support or if the NPHIE aspect is enabled.
diff --git a/tools/testing/selftests/powerpc/dexcr/dexcr.h 
b/tools/testing/selftests/powerpc/dexcr/dexcr.h
index f55cbbc8643b..a6aa7eac11da 100644
--- a/tools/testing/selftests/powerpc/dexcr/dexcr.h
+++ b/tools/testing/selftests/powerpc/dexcr/dexcr.h
@@ -28,6 +28,16 @@
 
 bool dexcr_exists(void);
 
+bool pr_dexcr_aspect_supported(unsigned long which);
+
+bool pr_dexcr_aspect_editable(unsigned long which);
+
+int pr_get_dexcr(unsigned long pr_aspect);
+
+int pr_set_dexcr(unsigned long pr_aspect, unsigned long ctrl);
+
+unsigned int pr_which_to_aspect(unsigned long which);
+
 bool hashchk_triggers(void);
 
 enum dexcr_source {
diff --git a/tools/testing/selftests/powerpc/dexcr/dexcr_test.c 
b/tools/testing/selftests/powerpc/dexcr/dexcr_test.c
new file mode 100644
index 000000000000..4662c823a4f1
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/dexcr_test.c
@@ -0,0 +1,213 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "dexcr.h"
+#include "utils.h"
+
+/*
+ * Helper function for testing the behaviour of a newly exec-ed process
+ */
+static int dexcr_prctl_onexec_test_child(unsigned long which, const char 
*status)
+{
+       unsigned long dexcr = mfspr(SPRN_DEXCR_RO);
+       unsigned long aspect = pr_which_to_aspect(which);
+       int ctrl = pr_get_dexcr(which);
+
+       if (!strcmp(status, "set")) {
+               FAIL_IF_EXIT_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_SET),
+                                "setting aspect across exec not applied");
+
+               FAIL_IF_EXIT_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_SET_ONEXEC),
+                                "setting aspect across exec not inherited");
+
+               FAIL_IF_EXIT_MSG(!(aspect & dexcr), "setting aspect across exec 
did not take effect");
+       } else if (!strcmp(status, "clear")) {
+               FAIL_IF_EXIT_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_CLEAR),
+                                "clearing aspect across exec not applied");
+
+               FAIL_IF_EXIT_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC),
+                                "clearing aspect across exec not inherited");
+
+               FAIL_IF_EXIT_MSG(aspect & dexcr, "clearing aspect across exec 
did not take effect");
+       } else {
+               FAIL_IF_EXIT_MSG(true, "unknown expected status");
+       }
+
+       return 0;
+}
+
+/*
+ * Test that the given prctl value can be manipulated freely
+ */
+static int dexcr_prctl_aspect_test(unsigned long which)
+{
+       unsigned long aspect = pr_which_to_aspect(which);
+       pid_t pid;
+       int ctrl;
+       int err;
+       int errno_save;
+
+       SKIP_IF_MSG(!dexcr_exists(), "DEXCR not supported");
+       SKIP_IF_MSG(!pr_dexcr_aspect_supported(which), "DEXCR aspect not 
supported");
+       SKIP_IF_MSG(!pr_dexcr_aspect_editable(which), "DEXCR aspect not 
editable with prctl");
+
+       /* We reject invalid combinations of arguments */
+       err = pr_set_dexcr(which, PR_PPC_DEXCR_CTRL_SET | 
PR_PPC_DEXCR_CTRL_CLEAR);
+       errno_save = errno;
+       FAIL_IF_MSG(err != -1, "simultaneous set and clear should be rejected");
+       FAIL_IF_MSG(errno_save != EINVAL, "simultaneous set and clear should be 
rejected with EINVAL");
+
+       err = pr_set_dexcr(which, PR_PPC_DEXCR_CTRL_SET_ONEXEC | 
PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC);
+       errno_save = errno;
+       FAIL_IF_MSG(err != -1, "simultaneous set and clear on exec should be 
rejected");
+       FAIL_IF_MSG(errno_save != EINVAL, "simultaneous set and clear on exec 
should be rejected with EINVAL");
+
+       /* We set the aspect */
+       err = pr_set_dexcr(which, PR_PPC_DEXCR_CTRL_SET);
+       FAIL_IF_MSG(err, "PR_PPC_DEXCR_CTRL_SET failed");
+
+       ctrl = pr_get_dexcr(which);
+       FAIL_IF_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_SET), "config value not 
PR_PPC_DEXCR_CTRL_SET");
+       FAIL_IF_MSG(ctrl & PR_PPC_DEXCR_CTRL_CLEAR, "config value unexpected 
clear flag");
+       FAIL_IF_MSG(!(aspect & mfspr(SPRN_DEXCR_RO)), "setting aspect did not 
take effect");
+
+       /* We clear the aspect */
+       err = pr_set_dexcr(which, PR_PPC_DEXCR_CTRL_CLEAR);
+       FAIL_IF_MSG(err, "PR_PPC_DEXCR_CTRL_CLEAR failed");
+
+       ctrl = pr_get_dexcr(which);
+       FAIL_IF_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_CLEAR), "config value not 
PR_PPC_DEXCR_CTRL_CLEAR");
+       FAIL_IF_MSG(ctrl & PR_PPC_DEXCR_CTRL_SET, "config value unexpected set 
flag");
+       FAIL_IF_MSG(aspect & mfspr(SPRN_DEXCR_RO), "clearing aspect did not 
take effect");
+
+       /* We make it set on exec (doesn't change our current value) */
+       err = pr_set_dexcr(which, PR_PPC_DEXCR_CTRL_SET_ONEXEC);
+       FAIL_IF_MSG(err, "PR_PPC_DEXCR_CTRL_SET_ONEXEC failed");
+
+       ctrl = pr_get_dexcr(which);
+       FAIL_IF_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_CLEAR), "process aspect should 
still be cleared");
+       FAIL_IF_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_SET_ONEXEC), "config value not 
PR_PPC_DEXCR_CTRL_SET_ONEXEC");
+       FAIL_IF_MSG(ctrl & PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC, "config value 
unexpected clear on exec flag");
+       FAIL_IF_MSG(aspect & mfspr(SPRN_DEXCR_RO), "scheduling aspect to set on 
exec should not change it now");
+
+       /* We make it clear on exec (doesn't change our current value) */
+       err = pr_set_dexcr(which, PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC);
+       FAIL_IF_MSG(err, "PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC failed");
+
+       ctrl = pr_get_dexcr(which);
+       FAIL_IF_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_CLEAR), "process aspect config 
should still be cleared");
+       FAIL_IF_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC), "config value not 
PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC");
+       FAIL_IF_MSG(ctrl & PR_PPC_DEXCR_CTRL_SET_ONEXEC, "config value 
unexpected set on exec flag");
+       FAIL_IF_MSG(aspect & mfspr(SPRN_DEXCR_RO), "process aspect should still 
be cleared");
+
+       /* We allow setting the current and on-exec value in a single call */
+       err = pr_set_dexcr(which, PR_PPC_DEXCR_CTRL_SET | 
PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC);
+       FAIL_IF_MSG(err, "PR_PPC_DEXCR_CTRL_SET | 
PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC failed");
+
+       ctrl = pr_get_dexcr(which);
+       FAIL_IF_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_SET), "config value not 
PR_PPC_DEXCR_CTRL_SET");
+       FAIL_IF_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC), "config value not 
PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC");
+       FAIL_IF_MSG(!(aspect & mfspr(SPRN_DEXCR_RO)), "process aspect should be 
set");
+
+       err = pr_set_dexcr(which, PR_PPC_DEXCR_CTRL_CLEAR | 
PR_PPC_DEXCR_CTRL_SET_ONEXEC);
+       FAIL_IF_MSG(err, "PR_PPC_DEXCR_CTRL_CLEAR | 
PR_PPC_DEXCR_CTRL_SET_ONEXEC failed");
+
+       ctrl = pr_get_dexcr(which);
+       FAIL_IF_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_CLEAR), "config value not 
PR_PPC_DEXCR_CTRL_CLEAR");
+       FAIL_IF_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_SET_ONEXEC), "config value not 
PR_PPC_DEXCR_CTRL_SET_ONEXEC");
+       FAIL_IF_MSG(aspect & mfspr(SPRN_DEXCR_RO), "process aspect should be 
clear");
+
+       /* Verify the onexec value is applied across exec */
+       pid = fork();
+       if (!pid) {
+               char which_str[32] = {};
+               char *args[] = { "dexcr_prctl_onexec_test_child", which_str, 
"set", NULL };
+               unsigned int ctrl = pr_get_dexcr(which);
+
+               sprintf(which_str, "%lu", which);
+
+               FAIL_IF_EXIT_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_SET_ONEXEC),
+                                "setting aspect on exec not copied across 
fork");
+
+               FAIL_IF_EXIT_MSG(mfspr(SPRN_DEXCR_RO) & aspect,
+                                "setting aspect on exec wrongly applied to 
fork");
+
+               execve("/proc/self/exe", args, NULL);
+               _exit(errno);
+       }
+       await_child_success(pid);
+
+       err = pr_set_dexcr(which, PR_PPC_DEXCR_CTRL_SET | 
PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC);
+       FAIL_IF_MSG(err, "PR_PPC_DEXCR_CTRL_SET | 
PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC failed");
+
+       pid = fork();
+       if (!pid) {
+               char which_str[32] = {};
+               char *args[] = { "dexcr_prctl_onexec_test_child", which_str, 
"clear", NULL };
+               unsigned int ctrl = pr_get_dexcr(which);
+
+               sprintf(which_str, "%lu", which);
+
+               FAIL_IF_EXIT_MSG(!(ctrl & PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC),
+                                "clearing aspect on exec not copied across 
fork");
+
+               FAIL_IF_EXIT_MSG(!(mfspr(SPRN_DEXCR_RO) & aspect),
+                                "clearing aspect on exec wrongly applied to 
fork");
+
+               execve("/proc/self/exe", args, NULL);
+               _exit(errno);
+       }
+       await_child_success(pid);
+
+       return 0;
+}
+
+static int dexcr_prctl_ibrtpd_test(void)
+{
+       return dexcr_prctl_aspect_test(PR_PPC_DEXCR_IBRTPD);
+}
+
+static int dexcr_prctl_srapd_test(void)
+{
+       return dexcr_prctl_aspect_test(PR_PPC_DEXCR_SRAPD);
+}
+
+static int dexcr_prctl_nphie_test(void)
+{
+       return dexcr_prctl_aspect_test(PR_PPC_DEXCR_NPHIE);
+}
+
+int main(int argc, char *argv[])
+{
+       int err = 0;
+
+       /*
+        * Some tests require checking what happens across exec, so we may be
+        * invoked as the child of a particular test
+        */
+       if (argc > 1) {
+               if (argc == 3 && !strcmp(argv[0], 
"dexcr_prctl_onexec_test_child")) {
+                       unsigned long which;
+
+                       err = parse_ulong(argv[1], strlen(argv[1]), &which, 10);
+                       FAIL_IF_MSG(err, "failed to parse which value for 
child");
+
+                       return dexcr_prctl_onexec_test_child(which, argv[2]);
+               }
+
+               FAIL_IF_MSG(true, "unknown test case");
+       }
+
+       /*
+        * Otherwise we are the main test invocation and run the full suite
+        */
+       err |= test_harness(dexcr_prctl_ibrtpd_test, "dexcr_prctl_ibrtpd");
+       err |= test_harness(dexcr_prctl_srapd_test, "dexcr_prctl_srapd");
+       err |= test_harness(dexcr_prctl_nphie_test, "dexcr_prctl_nphie");
+
+       return err;
+}
-- 
2.44.0

Reply via email to