Messages in this thread Patch in this message | | | From | Benjamin Gray <> | Subject | [RFC PATCH 11/13] selftests/powerpc: Add DEXCR prctl, sysctl interface test | Date | Mon, 28 Nov 2022 13:44:56 +1100 |
| |
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 <bgray@linux.ibm.com> --- .../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
| |