Test the prctl and sysctl interfaces of the DEXCR.

This adds a new capabilities util for getting and setting CAP_SYS_ADMIN.
Adding this avoids depending on an external libcap package. There is a
similar implementation (and reason) in the tools/testing/selftests/bpf
subtree but there's no obvious place to move it for sharing.

Signed-off-by: Benjamin Gray <[email protected]>
---
 .../selftests/powerpc/dexcr/.gitignore        |   1 +
 .../testing/selftests/powerpc/dexcr/Makefile  |   4 +-
 tools/testing/selftests/powerpc/dexcr/cap.c   |  72 ++++++
 tools/testing/selftests/powerpc/dexcr/cap.h   |  18 ++
 tools/testing/selftests/powerpc/dexcr/dexcr.h |   2 +
 .../selftests/powerpc/dexcr/dexcr_test.c      | 241 ++++++++++++++++++
 6 files changed, 336 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/powerpc/dexcr/cap.c
 create mode 100644 tools/testing/selftests/powerpc/dexcr/cap.h
 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 37adb7f47832..035a1fcd8fb3 100644
--- a/tools/testing/selftests/powerpc/dexcr/.gitignore
+++ b/tools/testing/selftests/powerpc/dexcr/.gitignore
@@ -1 +1,2 @@
+dexcr_test
 hashchk_user
diff --git a/tools/testing/selftests/powerpc/dexcr/Makefile 
b/tools/testing/selftests/powerpc/dexcr/Makefile
index 4b4380d4d986..9814e72a4afa 100644
--- a/tools/testing/selftests/powerpc/dexcr/Makefile
+++ b/tools/testing/selftests/powerpc/dexcr/Makefile
@@ -1,4 +1,4 @@
-TEST_GEN_PROGS := hashchk_test
+TEST_GEN_PROGS := dexcr_test hashchk_test
 
 TEST_FILES := settings
 top_srcdir = ../../../../..
@@ -6,4 +6,4 @@ include ../../lib.mk
 
 HASHCHK_TEST_CFLAGS = -no-pie $(call cc-option,-mno-rop-protect)
 
-$(TEST_GEN_PROGS): ../harness.c ../utils.c ./dexcr.c
+$(TEST_GEN_PROGS): ../harness.c ../utils.c ./dexcr.c ./cap.c
diff --git a/tools/testing/selftests/powerpc/dexcr/cap.c 
b/tools/testing/selftests/powerpc/dexcr/cap.c
new file mode 100644
index 000000000000..3c9b1f27345d
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/cap.c
@@ -0,0 +1,72 @@
+#include <linux/capability.h>
+#include <string.h>
+#include <sys/syscall.h>
+
+#include "cap.h"
+#include "utils.h"
+
+struct kernel_capabilities {
+       struct __user_cap_header_struct header;
+
+       struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3];
+};
+
+static void get_caps(struct kernel_capabilities *caps)
+{
+       FAIL_IF_EXIT_MSG(syscall(SYS_capget, &caps->header, &caps->data),
+                        "cannot get capabilities");
+}
+
+static void set_caps(struct kernel_capabilities *caps)
+{
+       FAIL_IF_EXIT_MSG(syscall(SYS_capset, &caps->header, &caps->data),
+                        "cannot set capabilities");
+}
+
+static void init_caps(struct kernel_capabilities *caps, pid_t pid)
+{
+       memset(caps, 0, sizeof(*caps));
+
+       caps->header.version = _LINUX_CAPABILITY_VERSION_3;
+       caps->header.pid = pid;
+
+       get_caps(caps);
+}
+
+static bool has_cap(struct kernel_capabilities *caps, size_t cap)
+{
+       size_t data_index = cap / 32;
+       size_t offset = cap % 32;
+
+       FAIL_IF_EXIT_MSG(data_index >= ARRAY_SIZE(caps->data), "cap out of 
range");
+
+       return caps->data[data_index].effective & (1 << offset);
+}
+
+static void drop_cap(struct kernel_capabilities *caps, size_t cap)
+{
+       size_t data_index = cap / 32;
+       size_t offset = cap % 32;
+
+       FAIL_IF_EXIT_MSG(data_index >= ARRAY_SIZE(caps->data), "cap out of 
range");
+
+       caps->data[data_index].effective &= ~(1 << offset);
+}
+
+bool check_cap_sysadmin(void)
+{
+       struct kernel_capabilities caps;
+
+       init_caps(&caps, 0);
+
+       return has_cap(&caps, CAP_SYS_ADMIN);
+}
+
+void drop_cap_sysadmin(void)
+{
+       struct kernel_capabilities caps;
+
+       init_caps(&caps, 0);
+       drop_cap(&caps, CAP_SYS_ADMIN);
+       set_caps(&caps);
+}
diff --git a/tools/testing/selftests/powerpc/dexcr/cap.h 
b/tools/testing/selftests/powerpc/dexcr/cap.h
new file mode 100644
index 000000000000..41f41dda9862
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/cap.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Simple capabilities getter/setter
+ *
+ * This header file contains helper functions and macros
+ * required to get and set capabilities(7). Introduced so
+ * we aren't the first to rely on libcap.
+ */
+#ifndef _SELFTESTS_POWERPC_DEXCR_CAP_H
+#define _SELFTESTS_POWERPC_DEXCR_CAP_H
+
+#include <stdbool.h>
+
+bool check_cap_sysadmin(void);
+
+void drop_cap_sysadmin(void);
+
+#endif  /* _SELFTESTS_POWERPC_DEXCR_CAP_H */
diff --git a/tools/testing/selftests/powerpc/dexcr/dexcr.h 
b/tools/testing/selftests/powerpc/dexcr/dexcr.h
index fb8007bf19f8..b90633ae49e9 100644
--- a/tools/testing/selftests/powerpc/dexcr/dexcr.h
+++ b/tools/testing/selftests/powerpc/dexcr/dexcr.h
@@ -21,6 +21,8 @@
 #define DEXCR_PRO_SRAPD                DEXCR_PRO_MASK(4)
 #define DEXCR_PRO_NPHIE                DEXCR_PRO_MASK(5)
 
+#define SYSCTL_DEXCR_SBHE      
"/proc/sys/kernel/speculative_branch_hint_enable"
+
 enum DexcrSource {
        UDEXCR,         /* Userspace DEXCR value */
        ENFORCED,       /* Enforced by hypervisor */
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..5446cb350c84
--- /dev/null
+++ b/tools/testing/selftests/powerpc/dexcr/dexcr_test.c
@@ -0,0 +1,241 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "cap.h"
+#include "dexcr.h"
+#include "utils.h"
+
+/*
+ * Test that an editable aspect
+ * - Current prctl state reported by the getter
+ * - Can be toggled on and off when process has CAP_SYS_ADMIN
+ * - Can't be edited if CAP_SYS_ADMIN not present
+ * - Can't be modified after force set
+ */
+static int dexcr_prctl_editable_aspect_test(unsigned long which)
+{
+       pid_t pid;
+
+       SKIP_IF_MSG(!check_cap_sysadmin(), "must have capability 
CAP_SYS_ADMIN");
+       SKIP_IF_MSG(!pr_aspect_supported(which), "aspect not supported");
+
+       FAIL_IF_MSG(!(pr_aspect_get(which) & PR_PPC_DEXCR_PRCTL), "aspect not 
editable");
+
+       FAIL_IF_MSG(!pr_aspect_edit(which, PR_PPC_DEXCR_CLEAR_ASPECT), "prctl 
failed");
+       FAIL_IF_MSG(pr_aspect_check(which, UDEXCR),
+                   "resetting aspect did not take effect");
+
+       FAIL_IF_MSG(pr_aspect_get(which) != (PR_PPC_DEXCR_CLEAR_ASPECT | 
PR_PPC_DEXCR_PRCTL),
+                   "prctl getter not reporting aspect state");
+
+       FAIL_IF_MSG(!pr_aspect_edit(which, PR_PPC_DEXCR_SET_ASPECT), "prctl 
failed");
+       FAIL_IF_MSG(!pr_aspect_check(which, UDEXCR),
+                   "setting aspect did not take effect");
+
+       FAIL_IF_MSG(pr_aspect_get(which) != (PR_PPC_DEXCR_SET_ASPECT | 
PR_PPC_DEXCR_PRCTL),
+                   "prctl getter not reporting aspect state");
+
+       FAIL_IF_MSG(!pr_aspect_edit(which, PR_PPC_DEXCR_CLEAR_ASPECT), "prctl 
failed");
+       FAIL_IF_MSG(pr_aspect_check(which, UDEXCR),
+                   "clearing aspect did not take effect");
+
+       FAIL_IF_MSG(pr_aspect_get(which) != (PR_PPC_DEXCR_CLEAR_ASPECT | 
PR_PPC_DEXCR_PRCTL),
+                   "prctl getter not reporting aspect state");
+
+       pid = fork();
+       if (pid == 0) {
+               drop_cap_sysadmin();
+               FAIL_IF_EXIT_MSG(pr_aspect_edit(which, PR_PPC_DEXCR_SET_ASPECT),
+                                "prctl success when nonprivileged");
+               FAIL_IF_EXIT_MSG(pr_aspect_check(which, UDEXCR),
+                                "edited aspect when nonprivileged");
+               _exit(0);
+       }
+       await_child_success(pid);
+
+       FAIL_IF_MSG(!pr_aspect_edit(which, PR_PPC_DEXCR_FORCE_SET_ASPECT), 
"prctl force set failed");
+       FAIL_IF_MSG(!pr_aspect_check(which, UDEXCR),
+                   "force setting aspect did not take effect");
+
+       FAIL_IF_MSG(pr_aspect_get(which) != (PR_PPC_DEXCR_FORCE_SET_ASPECT | 
PR_PPC_DEXCR_PRCTL),
+                   "prctl getter not reporting aspect state");
+
+       FAIL_IF_MSG(pr_aspect_edit(which, PR_PPC_DEXCR_CLEAR_ASPECT), "prctl 
success when forced");
+       FAIL_IF_MSG(!pr_aspect_check(which, UDEXCR),
+                   "edited aspect when forced");
+
+       return 0;
+}
+
+static int dexcr_prctl_sbhe_test(void)
+{
+       sysctl_set_sbhe(-1);
+       return dexcr_prctl_editable_aspect_test(PR_PPC_DEXCR_SBHE);
+}
+
+static int dexcr_prctl_ibrtpd_test(void)
+{
+       return dexcr_prctl_editable_aspect_test(PR_PPC_DEXCR_IBRTPD);
+}
+
+static int dexcr_prctl_srapd_test(void)
+{
+       return dexcr_prctl_editable_aspect_test(PR_PPC_DEXCR_SRAPD);
+}
+
+static int dexcr_sysctl_sbhe_test(void)
+{
+       SKIP_IF_MSG(!check_cap_sysadmin(), "must have capability 
CAP_SYS_ADMIN");
+       SKIP_IF_MSG(!pr_aspect_supported(PR_PPC_DEXCR_SBHE), "aspect not 
supported");
+
+       sysctl_set_sbhe(0);
+       FAIL_IF_MSG(sysctl_get_sbhe() != 0, "failed to clear sysctl SBHE");
+       FAIL_IF_MSG(pr_aspect_check(PR_PPC_DEXCR_SBHE, UDEXCR),
+                   "SBHE failed to clear");
+
+       sysctl_set_sbhe(1);
+       FAIL_IF_MSG(sysctl_get_sbhe() != 1, "failed to set sysctl SBHE");
+       FAIL_IF_MSG(!pr_aspect_check(PR_PPC_DEXCR_SBHE, UDEXCR),
+                   "SBHE failed to set");
+
+       sysctl_set_sbhe(-1);
+       FAIL_IF_MSG(sysctl_get_sbhe() != -1, "failed to default sysctl SBHE");
+       FAIL_IF_MSG(!pr_aspect_edit(PR_PPC_DEXCR_SBHE, 
PR_PPC_DEXCR_CLEAR_ASPECT), "prctl failed");
+       FAIL_IF_MSG(pr_aspect_check(PR_PPC_DEXCR_SBHE, UDEXCR),
+                   "SBHE failed to default to prctl clear setting");
+
+       FAIL_IF_MSG(!pr_aspect_edit(PR_PPC_DEXCR_SBHE, 
PR_PPC_DEXCR_SET_ASPECT), "prctl failed");
+       FAIL_IF_MSG(!pr_aspect_check(PR_PPC_DEXCR_SBHE, UDEXCR),
+                   "SBHE failed to default to prctl set setting");
+
+       sysctl_set_sbhe(0);
+       FAIL_IF_MSG(sysctl_get_sbhe() != 0, "failed to clear sysctl SBHE");
+       FAIL_IF_MSG(pr_aspect_check(PR_PPC_DEXCR_SBHE, UDEXCR),
+                   "SBHE failed to override prctl setting");
+
+       return 0;
+}
+
+static int dexcr_test_inherit_execve(char expected_dexcr)
+{
+       switch (expected_dexcr) {
+       case '0':
+               FAIL_IF_EXIT_MSG(pr_aspect_get(PR_PPC_DEXCR_IBRTPD) !=
+                                (PR_PPC_DEXCR_CLEAR_ASPECT | 
PR_PPC_DEXCR_PRCTL),
+                                "clearing IBRTPD across exec not inherited");
+
+               FAIL_IF_EXIT_MSG(pr_aspect_check(PR_PPC_DEXCR_IBRTPD, UDEXCR),
+                                "clearing IBRTPD across exec not applied");
+               break;
+       case '1':
+               FAIL_IF_EXIT_MSG(pr_aspect_get(PR_PPC_DEXCR_IBRTPD) !=
+                                (PR_PPC_DEXCR_SET_ASPECT | PR_PPC_DEXCR_PRCTL),
+                                "setting IBRTPD across exec not inherited");
+
+               FAIL_IF_EXIT_MSG(!pr_aspect_check(PR_PPC_DEXCR_IBRTPD, UDEXCR),
+                                "setting IBRTPD across exec not applied");
+               break;
+       case '2':
+               FAIL_IF_EXIT_MSG(pr_aspect_get(PR_PPC_DEXCR_IBRTPD) !=
+                                (PR_PPC_DEXCR_FORCE_SET_ASPECT | 
PR_PPC_DEXCR_PRCTL),
+                                "force setting IBRTPD across exec not 
inherited");
+
+               FAIL_IF_EXIT_MSG(!pr_aspect_check(PR_PPC_DEXCR_IBRTPD, UDEXCR),
+                                "force setting IBRTPD across exec not 
applied");
+               break;
+       }
+
+       return 0;
+}
+
+/*
+ * Check that a child process inherits the DEXCR over fork and execve
+ */
+static int dexcr_inherit_test(void)
+{
+       pid_t pid;
+
+       SKIP_IF_MSG(!check_cap_sysadmin(), "must have capability 
CAP_SYS_ADMIN");
+       SKIP_IF_MSG(!pr_aspect_supported(PR_PPC_DEXCR_IBRTPD), "IBRTPD not 
supported");
+
+       pr_aspect_edit(PR_PPC_DEXCR_IBRTPD, PR_PPC_DEXCR_CLEAR_ASPECT);
+       FAIL_IF_MSG(pr_aspect_check(PR_PPC_DEXCR_IBRTPD, UDEXCR),
+                   "IBRTPD failed to clear");
+
+       pid = fork();
+       if (pid == 0) {
+               char *args[] = { "dexcr_test_inherit_execve", "0", NULL };
+
+               FAIL_IF_EXIT_MSG(pr_aspect_get(PR_PPC_DEXCR_IBRTPD) !=
+                                (PR_PPC_DEXCR_CLEAR_ASPECT | 
PR_PPC_DEXCR_PRCTL),
+                                "clearing IBRTPD not inherited");
+
+               FAIL_IF_EXIT_MSG(pr_aspect_check(PR_PPC_DEXCR_IBRTPD, UDEXCR),
+                                "clearing IBRTPD not applied");
+
+               execve("/proc/self/exe", args, NULL);
+               _exit(errno);
+       }
+       await_child_success(pid);
+
+       pr_aspect_edit(PR_PPC_DEXCR_IBRTPD, PR_PPC_DEXCR_SET_ASPECT);
+       FAIL_IF_MSG(!pr_aspect_check(PR_PPC_DEXCR_IBRTPD, UDEXCR),
+                   "IBRTPD failed to set");
+
+       pid = fork();
+       if (pid == 0) {
+               char *args[] = { "dexcr_test_inherit_execve", "1", NULL };
+
+               FAIL_IF_EXIT_MSG(pr_aspect_get(PR_PPC_DEXCR_IBRTPD) !=
+                                (PR_PPC_DEXCR_SET_ASPECT | PR_PPC_DEXCR_PRCTL),
+                                "setting IBRTPD not inherited");
+
+               FAIL_IF_EXIT_MSG(!pr_aspect_check(PR_PPC_DEXCR_IBRTPD, UDEXCR),
+                                "setting IBRTPD not applied");
+
+               execve("/proc/self/exe", args, NULL);
+               _exit(errno);
+       }
+       await_child_success(pid);
+
+       pr_aspect_edit(PR_PPC_DEXCR_IBRTPD, PR_PPC_DEXCR_FORCE_SET_ASPECT);
+       FAIL_IF_MSG(!pr_aspect_check(PR_PPC_DEXCR_IBRTPD, UDEXCR),
+                   "IBRTPD failed to force set");
+
+       pid = fork();
+       if (pid == 0) {
+               char *args[] = { "dexcr_test_inherit_execve", "2", NULL };
+
+               FAIL_IF_EXIT_MSG(pr_aspect_get(PR_PPC_DEXCR_IBRTPD) !=
+                                (PR_PPC_DEXCR_FORCE_SET_ASPECT | 
PR_PPC_DEXCR_PRCTL),
+                                "force setting IBRTPD not inherited");
+
+               FAIL_IF_EXIT_MSG(!pr_aspect_check(PR_PPC_DEXCR_IBRTPD, UDEXCR),
+                                "force setting IBRTPD not applied");
+
+               execve("/proc/self/exe", args, NULL);
+               _exit(errno);
+       }
+       await_child_success(pid);
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       int err = 0;
+
+       if (argc >= 2 && strcmp(argv[0], "dexcr_test_inherit_execve") == 0)
+               return dexcr_test_inherit_execve(argv[1][0]);
+
+       err |= test_harness(dexcr_prctl_sbhe_test, "dexcr_prctl_sbhe");
+       err |= test_harness(dexcr_prctl_ibrtpd_test, "dexcr_prctl_ibrtpd");
+       err |= test_harness(dexcr_prctl_srapd_test, "dexcr_prctl_srapd");
+       err |= test_harness(dexcr_sysctl_sbhe_test, "dexcr_sysctl_sbhe");
+       err |= test_harness(dexcr_inherit_test, "dexcr_inherit");
+
+       return err;
+}
-- 
2.38.1

Reply via email to