linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
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 11/13] selftests/powerpc: Add DEXCR prctl, sysctl interface test
Date: Mon, 28 Nov 2022 13:44:56 +1100	[thread overview]
Message-ID: <20221128024458.46121-12-bgray@linux.ibm.com> (raw)
In-Reply-To: <20221128024458.46121-1-bgray@linux.ibm.com>

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


  parent reply	other threads:[~2022-11-28  2:53 UTC|newest]

Thread overview: 29+ 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 ` [RFC PATCH 01/13] powerpc/book3s: Add missing <linux/sched.h> include Benjamin Gray
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
2023-03-07  4:45   ` Nicholas Piggin
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-29 10:39   ` Nicholas Piggin
2022-11-29 22:04     ` Benjamin Gray
2022-11-28  2:44 ` [RFC PATCH 04/13] powerpc/dexcr: Support userspace ROP protection Benjamin Gray
2023-03-07  5:05   ` Nicholas Piggin
2023-03-07  5:37     ` Benjamin Gray
2023-03-21  4:51       ` Nicholas Piggin
2022-11-28  2:44 ` [RFC PATCH 05/13] prctl: Define PowerPC DEXCR interface Benjamin Gray
2023-03-07  5:07   ` Nicholas Piggin
2022-11-28  2:44 ` [RFC PATCH 06/13] powerpc/dexcr: Add prctl implementation Benjamin Gray
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
2023-03-07  5:30   ` Nicholas Piggin
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 ` [RFC PATCH 09/13] selftests/powerpc: Add more utility macros Benjamin Gray
2022-11-28  2:44 ` [RFC PATCH 10/13] selftests/powerpc: Add hashst/hashchk test Benjamin Gray
2022-11-28  2:44 ` Benjamin Gray [this message]
2022-11-28  2:44 ` [RFC PATCH 12/13] selftests/powerpc: Add DEXCR status utility lsdexcr Benjamin Gray
2022-11-28  2:44 ` [RFC PATCH 13/13] Documentation: Document PowerPC kernel DEXCR interface Benjamin Gray
2023-03-07  5:40   ` Nicholas Piggin
2023-03-07  5:52     ` Benjamin Gray
2022-11-28  4:05 ` [RFC PATCH 00/13] Add DEXCR support 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-12-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 \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).