* [PATCH 1/2] selftests/powerpc: Add ptrace tests for Protection Key registers
@ 2018-02-23 18:33 Thiago Jung Bauermann
2018-02-23 18:33 ` [PATCH 2/2] selftests/powerpc: Add core file test " Thiago Jung Bauermann
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: Thiago Jung Bauermann @ 2018-02-23 18:33 UTC (permalink / raw)
To: linuxppc-dev
Cc: linux-kselftest, linux-kernel, Michael Ellerman, Ram Pai,
Thiago Jung Bauermann
This test exercises read and write access to the AMR, IAMR and UAMOR.
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
---
tools/testing/selftests/powerpc/include/reg.h | 1 +
tools/testing/selftests/powerpc/ptrace/Makefile | 5 +-
tools/testing/selftests/powerpc/ptrace/child.h | 130 ++++++++
.../testing/selftests/powerpc/ptrace/ptrace-pkey.c | 326 +++++++++++++++++++++
tools/testing/selftests/powerpc/ptrace/ptrace.h | 37 +++
5 files changed, 498 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/powerpc/ptrace/child.h
create mode 100644 tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
diff --git a/tools/testing/selftests/powerpc/include/reg.h b/tools/testing/selftests/powerpc/include/reg.h
index 4afdebcce4cd..7f348c059bc2 100644
--- a/tools/testing/selftests/powerpc/include/reg.h
+++ b/tools/testing/selftests/powerpc/include/reg.h
@@ -54,6 +54,7 @@
#define SPRN_DSCR_PRIV 0x11 /* Privilege State DSCR */
#define SPRN_DSCR 0x03 /* Data Stream Control Register */
#define SPRN_PPR 896 /* Program Priority Register */
+#define SPRN_AMR 13 /* Authority Mask Register - problem state */
/* TEXASR register bits */
#define TEXASR_FC 0xFE00000000000000
diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile
index 480305266504..707ba734faf2 100644
--- a/tools/testing/selftests/powerpc/ptrace/Makefile
+++ b/tools/testing/selftests/powerpc/ptrace/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \
ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \
- ptrace-tm-spd-vsx ptrace-tm-spr
+ ptrace-tm-spd-vsx ptrace-tm-spr ptrace-pkey
include ../../lib.mk
@@ -9,6 +9,9 @@ all: $(TEST_PROGS)
CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm -fno-pie
+ptrace-pkey: ../harness.c ../utils.c ../lib/reg.S ptrace.h child.h ptrace-pkey.c
+ $(LINK.c) $^ $(LDLIBS) -pthread -o $@
+
$(TEST_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h
clean:
diff --git a/tools/testing/selftests/powerpc/ptrace/child.h b/tools/testing/selftests/powerpc/ptrace/child.h
new file mode 100644
index 000000000000..40c1a6d92111
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/child.h
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Helper functions to sync execution between parent and child processes.
+ *
+ * Copyright 2018, Thiago Jung Bauermann, IBM Corporation.
+ */
+#include <stdio.h>
+#include <stdbool.h>
+#include <semaphore.h>
+
+/*
+ * Information in a shared memory location for synchronization between child and
+ * parent.
+ */
+struct child_sync {
+ /* The parent waits on this semaphore. */
+ sem_t sem_parent;
+
+ /* If true, the child should give up as well. */
+ bool parent_gave_up;
+
+ /* The child waits on this semaphore. */
+ sem_t sem_child;
+
+ /* If true, the parent should give up as well. */
+ bool child_gave_up;
+};
+
+#define CHILD_FAIL_IF(x, sync) \
+ do { \
+ if (x) { \
+ fprintf(stderr, \
+ "[FAIL] Test FAILED on line %d\n", __LINE__); \
+ (sync)->child_gave_up = true; \
+ prod_parent(sync); \
+ return 1; \
+ } \
+ } while (0)
+
+#define PARENT_FAIL_IF(x, sync) \
+ do { \
+ if (x) { \
+ fprintf(stderr, \
+ "[FAIL] Test FAILED on line %d\n", __LINE__); \
+ (sync)->parent_gave_up = true; \
+ prod_child(sync); \
+ return 1; \
+ } \
+ } while (0)
+
+int init_child_sync(struct child_sync *sync)
+{
+ int ret;
+
+ ret = sem_init(&sync->sem_parent, 1, 0);
+ if (ret) {
+ perror("Semaphore initialization failed");
+ return 1;
+ }
+
+ ret = sem_init(&sync->sem_child, 1, 0);
+ if (ret) {
+ perror("Semaphore initialization failed");
+ return 1;
+ }
+
+ return 0;
+}
+
+void destroy_child_sync(struct child_sync *sync)
+{
+ sem_destroy(&sync->sem_parent);
+ sem_destroy(&sync->sem_child);
+}
+
+int wait_child(struct child_sync *sync)
+{
+ int ret;
+
+ /* Wait until the child prods us. */
+ ret = sem_wait(&sync->sem_parent);
+ if (ret) {
+ perror("Error waiting for child");
+ return 1;
+ }
+
+ return sync->child_gave_up;
+}
+
+int prod_child(struct child_sync *sync)
+{
+ int ret;
+
+ /* Unblock the child now. */
+ ret = sem_post(&sync->sem_child);
+ if (ret) {
+ perror("Error prodding child");
+ return 1;
+ }
+
+ return 0;
+}
+
+int wait_parent(struct child_sync *sync)
+{
+ int ret;
+
+ /* Wait until the parent prods us. */
+ ret = sem_wait(&sync->sem_child);
+ if (ret) {
+ perror("Error waiting for parent");
+ return 1;
+ }
+
+ return sync->parent_gave_up;
+}
+
+int prod_parent(struct child_sync *sync)
+{
+ int ret;
+
+ /* Unblock the parent now. */
+ ret = sem_post(&sync->sem_parent);
+ if (ret) {
+ perror("Error prodding parent");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
new file mode 100644
index 000000000000..8332b9338d39
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Ptrace test for Memory Protection Key registers
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ * Copyright (C) 2018 IBM Corporation.
+ */
+#include "ptrace.h"
+#include "child.h"
+
+#ifndef __NR_pkey_alloc
+#define __NR_pkey_alloc 384
+#endif
+
+#ifndef __NR_pkey_free
+#define __NR_pkey_free 385
+#endif
+
+#ifndef NT_PPC_PKEY
+#define NT_PPC_PKEY 0x110
+#endif
+
+#ifndef PKEY_DISABLE_EXECUTE
+#define PKEY_DISABLE_EXECUTE 0x4
+#endif
+
+#define AMR_BITS_PER_PKEY 2
+#define PKEY_REG_BITS (sizeof(u64) * 8)
+#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY))
+
+static const char user_read[] = "[User Read (Running)]";
+static const char user_write[] = "[User Write (Running)]";
+static const char ptrace_read_running[] = "[Ptrace Read (Running)]";
+static const char ptrace_write_running[] = "[Ptrace Write (Running)]";
+
+/* Information shared between the parent and the child. */
+struct shared_info {
+ struct child_sync child_sync;
+
+ /* AMR value the parent expects to read from the child. */
+ unsigned long amr1;
+
+ /* AMR value the parent is expected to write to the child. */
+ unsigned long amr2;
+
+ /* AMR value that ptrace should refuse to write to the child. */
+ unsigned long amr3;
+
+ /* IAMR value the parent expects to read from the child. */
+ unsigned long expected_iamr;
+
+ /* UAMOR value the parent expects to read from the child. */
+ unsigned long expected_uamor;
+
+ /*
+ * IAMR and UAMOR values that ptrace should refuse to write to the child
+ * (even though they're valid ones) because userspace doesn't have
+ * access to those registers.
+ */
+ unsigned long new_iamr;
+ unsigned long new_uamor;
+};
+
+static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
+{
+ return syscall(__NR_pkey_alloc, flags, init_access_rights);
+}
+
+static int sys_pkey_free(int pkey)
+{
+ return syscall(__NR_pkey_free, pkey);
+}
+
+static int child(struct shared_info *info)
+{
+ unsigned long reg;
+ bool disable_execute = true;
+ int pkey1, pkey2, pkey3;
+ int ret;
+
+ /* Wait until parent fills out the initial register values. */
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ /* Get some pkeys so that we can change their bits in the AMR. */
+ pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
+ if (pkey1 < 0) {
+ pkey1 = sys_pkey_alloc(0, 0);
+ CHILD_FAIL_IF(pkey1 < 0, &info->child_sync);
+
+ disable_execute = false;
+ }
+
+ pkey2 = sys_pkey_alloc(0, 0);
+ CHILD_FAIL_IF(pkey2 < 0, &info->child_sync);
+
+ pkey3 = sys_pkey_alloc(0, 0);
+ CHILD_FAIL_IF(pkey3 < 0, &info->child_sync);
+
+ info->amr1 |= 3ul << pkeyshift(pkey1);
+ info->amr2 |= 3ul << pkeyshift(pkey2);
+ info->amr3 |= info->amr2 | 3ul << pkeyshift(pkey3);
+
+ if (disable_execute)
+ info->expected_iamr |= 1ul << pkeyshift(pkey1);
+
+ info->expected_uamor |= 3ul << pkeyshift(pkey1) |
+ 3ul << pkeyshift(pkey2);
+ info->new_iamr |= 1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2);
+ info->new_uamor |= 3ul << pkeyshift(pkey1);
+
+ /*
+ * We won't use pkey3. We just want a plausible but invalid key to test
+ * whether ptrace will let us write to AMR bits we are not supposed to.
+ *
+ * This also tests whether the kernel restores the UAMOR permissions
+ * after a key is freed.
+ */
+ sys_pkey_free(pkey3);
+
+ printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
+ user_write, info->amr1, pkey1, pkey2, pkey3);
+
+ mtspr(SPRN_AMR, info->amr1);
+
+ /* Wait for parent to read our AMR value and write a new one. */
+ ret = prod_parent(&info->child_sync);
+ CHILD_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ reg = mfspr(SPRN_AMR);
+
+ printf("%-30s AMR: %016lx\n", user_read, reg);
+
+ CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
+
+ /*
+ * Wait for parent to try to write an invalid AMR value.
+ */
+ ret = prod_parent(&info->child_sync);
+ CHILD_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ reg = mfspr(SPRN_AMR);
+
+ printf("%-30s AMR: %016lx\n", user_read, reg);
+
+ CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
+
+ /*
+ * Wait for parent to try to write an IAMR and a UAMOR value. We can't
+ * verify them, but we can verify that the AMR didn't change.
+ */
+ ret = prod_parent(&info->child_sync);
+ CHILD_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ reg = mfspr(SPRN_AMR);
+
+ printf("%-30s AMR: %016lx\n", user_read, reg);
+
+ CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
+
+ /* Now let parent now that we are finished. */
+
+ ret = prod_parent(&info->child_sync);
+ CHILD_FAIL_IF(ret, &info->child_sync);
+
+ return TEST_PASS;
+}
+
+static int parent(struct shared_info *info, pid_t pid)
+{
+ unsigned long regs[3];
+ int ret, status;
+
+ /*
+ * Get the initial values for AMR, IAMR and UAMOR and communicate them
+ * to the child.
+ */
+ ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ info->amr1 = info->amr2 = info->amr3 = regs[0];
+ info->expected_iamr = info->new_iamr = regs[1];
+ info->expected_uamor = info->new_uamor = regs[2];
+
+ /* Wake up child so that it can set itself up. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_child(&info->child_sync);
+ if (ret)
+ return ret;
+
+ /* Verify that we can read the pkey registers from the child. */
+ ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
+ ptrace_read_running, regs[0], regs[1], regs[2]);
+
+ PARENT_FAIL_IF(regs[0] != info->amr1, &info->child_sync);
+ PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);
+ PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);
+
+ /* Write valid AMR value in child. */
+ ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr2, 1);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr2);
+
+ /* Wake up child so that it can verify it changed. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_child(&info->child_sync);
+ if (ret)
+ return ret;
+
+ /* Write invalid AMR value in child. */
+ ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr3, 1);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr3);
+
+ /* Wake up child so that it can verify it didn't change. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_child(&info->child_sync);
+ if (ret)
+ return ret;
+
+ /* Try to write to IAMR. */
+ regs[0] = info->amr1;
+ regs[1] = info->new_iamr;
+ ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 2);
+ PARENT_FAIL_IF(!ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx IAMR: %016lx\n",
+ ptrace_write_running, regs[0], regs[1]);
+
+ /* Try to write to IAMR and UAMOR. */
+ regs[2] = info->new_uamor;
+ ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_FAIL_IF(!ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
+ ptrace_write_running, regs[0], regs[1], regs[2]);
+
+ /* Verify that all registers still have their expected values. */
+ ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
+ ptrace_read_running, regs[0], regs[1], regs[2]);
+
+ PARENT_FAIL_IF(regs[0] != info->amr2, &info->child_sync);
+ PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);
+ PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);
+
+ /* Wake up child so that it can verify AMR didn't change and wrap up. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait(&status);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ ret = TEST_PASS;
+ } else if (!WIFEXITED(status)) {
+ printf("Child exited abnormally\n");
+ ret = TEST_FAIL;
+ } else
+ ret = WEXITSTATUS(status) ? TEST_FAIL : TEST_PASS;
+
+ return ret;
+}
+
+static int ptrace_pkey(void)
+{
+ struct shared_info *info;
+ int shm_id;
+ int ret;
+ pid_t pid;
+
+ shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
+ info = shmat(shm_id, NULL, 0);
+
+ ret = init_child_sync(&info->child_sync);
+ if (ret)
+ return ret;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ ret = TEST_FAIL;
+ } else if (pid == 0)
+ ret = child(info);
+ else
+ ret = parent(info, pid);
+
+ shmdt(info);
+
+ if (pid) {
+ destroy_child_sync(&info->child_sync);
+ shmctl(shm_id, IPC_RMID, NULL);
+ }
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_pkey, "ptrace_pkey");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace.h b/tools/testing/selftests/powerpc/ptrace/ptrace.h
index 19fb825270a1..d2c9c4c2b5ee 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace.h
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace.h
@@ -102,6 +102,43 @@ int cont_trace(pid_t child)
return TEST_PASS;
}
+int ptrace_read_regs(pid_t child, unsigned long type, unsigned long regs[],
+ int n)
+{
+ struct iovec iov;
+ long ret;
+
+ FAIL_IF(start_trace(child));
+
+ iov.iov_base = regs;
+ iov.iov_len = n * sizeof(unsigned long);
+
+ ret = ptrace(PTRACE_GETREGSET, child, type, &iov);
+ FAIL_IF(ret != 0);
+
+ FAIL_IF(stop_trace(child));
+
+ return TEST_PASS;
+}
+
+long ptrace_write_regs(pid_t child, unsigned long type, unsigned long regs[],
+ int n)
+{
+ struct iovec iov;
+ long ret;
+
+ FAIL_IF(start_trace(child));
+
+ iov.iov_base = regs;
+ iov.iov_len = n * sizeof(unsigned long);
+
+ ret = ptrace(PTRACE_SETREGSET, child, type, &iov);
+
+ FAIL_IF(stop_trace(child));
+
+ return ret;
+}
+
/* TAR, PPR, DSCR */
int show_tar_registers(pid_t child, unsigned long *out)
{
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/2] selftests/powerpc: Add core file test for Protection Key registers
2018-02-23 18:33 [PATCH 1/2] selftests/powerpc: Add ptrace tests for Protection Key registers Thiago Jung Bauermann
@ 2018-02-23 18:33 ` Thiago Jung Bauermann
2018-02-23 18:51 ` Ram Pai
2018-05-17 14:03 ` Michael Ellerman
2018-02-23 18:50 ` [PATCH 1/2] selftests/powerpc: Add ptrace tests " Ram Pai
2018-05-17 14:03 ` Michael Ellerman
2 siblings, 2 replies; 10+ messages in thread
From: Thiago Jung Bauermann @ 2018-02-23 18:33 UTC (permalink / raw)
To: linuxppc-dev
Cc: linux-kselftest, linux-kernel, Michael Ellerman, Ram Pai,
Thiago Jung Bauermann
This test verifies that the AMR, IAMR and UAMOR are being written to a
process' core file.
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
---
tools/testing/selftests/powerpc/ptrace/Makefile | 5 +-
tools/testing/selftests/powerpc/ptrace/core-pkey.c | 460 +++++++++++++++++++++
2 files changed, 464 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/powerpc/ptrace/core-pkey.c
diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile
index 707ba734faf2..a10916c3f3e1 100644
--- a/tools/testing/selftests/powerpc/ptrace/Makefile
+++ b/tools/testing/selftests/powerpc/ptrace/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \
ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \
- ptrace-tm-spd-vsx ptrace-tm-spr ptrace-pkey
+ ptrace-tm-spd-vsx ptrace-tm-spr ptrace-pkey core-pkey
include ../../lib.mk
@@ -12,6 +12,9 @@ CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm -fno-pie
ptrace-pkey: ../harness.c ../utils.c ../lib/reg.S ptrace.h child.h ptrace-pkey.c
$(LINK.c) $^ $(LDLIBS) -pthread -o $@
+core-pkey: ../harness.c ../utils.c ../lib/reg.S ptrace.h child.h core-pkey.c
+ $(LINK.c) $^ $(LDLIBS) -pthread -o $@
+
$(TEST_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h
clean:
diff --git a/tools/testing/selftests/powerpc/ptrace/core-pkey.c b/tools/testing/selftests/powerpc/ptrace/core-pkey.c
new file mode 100644
index 000000000000..ddbe0f2caf70
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/core-pkey.c
@@ -0,0 +1,460 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Ptrace test for Memory Protection Key registers
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ * Copyright (C) 2018 IBM Corporation.
+ */
+#include <limits.h>
+#include <linux/kernel.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "ptrace.h"
+#include "child.h"
+
+#ifndef __NR_pkey_alloc
+#define __NR_pkey_alloc 384
+#endif
+
+#ifndef __NR_pkey_free
+#define __NR_pkey_free 385
+#endif
+
+#ifndef NT_PPC_PKEY
+#define NT_PPC_PKEY 0x110
+#endif
+
+#ifndef PKEY_DISABLE_EXECUTE
+#define PKEY_DISABLE_EXECUTE 0x4
+#endif
+
+#define AMR_BITS_PER_PKEY 2
+#define PKEY_REG_BITS (sizeof(u64) * 8)
+#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY))
+
+#define CORE_FILE_LIMIT (5 * 1024 * 1024) /* 5 MB should be enough */
+
+static const char core_pattern_file[] = "/proc/sys/kernel/core_pattern";
+
+static const char user_write[] = "[User Write (Running)]";
+static const char core_read_running[] = "[Core Read (Running)]";
+
+/* Information shared between the parent and the child. */
+struct shared_info {
+ struct child_sync child_sync;
+
+ /* AMR value the parent expects to read in the core file. */
+ unsigned long amr;
+
+ /* IAMR value the parent expects to read in the core file. */
+ unsigned long iamr;
+
+ /* UAMOR value the parent expects to read in the core file. */
+ unsigned long uamor;
+
+ /* When the child crashed. */
+ time_t core_time;
+};
+
+static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
+{
+ return syscall(__NR_pkey_alloc, flags, init_access_rights);
+}
+
+static int sys_pkey_free(int pkey)
+{
+ return syscall(__NR_pkey_free, pkey);
+}
+
+static int increase_core_file_limit(void)
+{
+ struct rlimit rlim;
+ int ret;
+
+ ret = getrlimit(RLIMIT_CORE, &rlim);
+ FAIL_IF(ret);
+
+ if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
+ rlim.rlim_cur = CORE_FILE_LIMIT;
+
+ if (rlim.rlim_max != RLIM_INFINITY &&
+ rlim.rlim_max < CORE_FILE_LIMIT)
+ rlim.rlim_max = CORE_FILE_LIMIT;
+
+ ret = setrlimit(RLIMIT_CORE, &rlim);
+ FAIL_IF(ret);
+ }
+
+ ret = getrlimit(RLIMIT_FSIZE, &rlim);
+ FAIL_IF(ret);
+
+ if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < CORE_FILE_LIMIT) {
+ rlim.rlim_cur = CORE_FILE_LIMIT;
+
+ if (rlim.rlim_max != RLIM_INFINITY &&
+ rlim.rlim_max < CORE_FILE_LIMIT)
+ rlim.rlim_max = CORE_FILE_LIMIT;
+
+ ret = setrlimit(RLIMIT_FSIZE, &rlim);
+ FAIL_IF(ret);
+ }
+
+ return TEST_PASS;
+}
+
+static int child(struct shared_info *info)
+{
+ bool disable_execute = true;
+ int pkey1, pkey2, pkey3;
+ int *ptr, ret;
+
+ /* Wait until parent fills out the initial register values. */
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ ret = increase_core_file_limit();
+ FAIL_IF(ret);
+
+ /* Get some pkeys so that we can change their bits in the AMR. */
+ pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
+ if (pkey1 < 0) {
+ pkey1 = sys_pkey_alloc(0, 0);
+ FAIL_IF(pkey1 < 0);
+
+ disable_execute = false;
+ }
+
+ pkey2 = sys_pkey_alloc(0, 0);
+ FAIL_IF(pkey2 < 0);
+
+ pkey3 = sys_pkey_alloc(0, 0);
+ FAIL_IF(pkey3 < 0);
+
+ info->amr |= 3ul << pkeyshift(pkey1) | 2ul << pkeyshift(pkey2);
+
+ if (disable_execute)
+ info->iamr |= 1ul << pkeyshift(pkey1);
+
+ info->uamor |= 3ul << pkeyshift(pkey1) | 3ul << pkeyshift(pkey2);
+
+ printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
+ user_write, info->amr, pkey1, pkey2, pkey3);
+
+ mtspr(SPRN_AMR, info->amr);
+
+ /*
+ * We won't use pkey3. This tests whether the kernel restores the UAMOR
+ * permissions after a key is freed.
+ */
+ sys_pkey_free(pkey3);
+
+ info->core_time = time(NULL);
+
+ /* Crash. */
+ ptr = 0;
+ *ptr = 1;
+
+ /* Shouldn't get here. */
+ FAIL_IF(true);
+
+ return TEST_FAIL;
+}
+
+/* Return file size if filename exists and pass sanity check, or zero if not. */
+static off_t try_core_file(const char *filename, struct shared_info *info,
+ pid_t pid)
+{
+ struct stat buf;
+ int ret;
+
+ ret = stat(filename, &buf);
+ if (ret == -1)
+ return TEST_FAIL;
+
+ /* Make sure we're not using a stale core file. */
+ return buf.st_mtime >= info->core_time ? buf.st_size : TEST_FAIL;
+}
+
+static Elf64_Nhdr *next_note(Elf64_Nhdr *nhdr)
+{
+ return (void *) nhdr + sizeof(*nhdr) +
+ __ALIGN_KERNEL(nhdr->n_namesz, 4) +
+ __ALIGN_KERNEL(nhdr->n_descsz, 4);
+}
+
+static int check_core_file(struct shared_info *info, Elf64_Ehdr *ehdr,
+ off_t core_size)
+{
+ unsigned long *regs;
+ Elf64_Phdr *phdr;
+ Elf64_Nhdr *nhdr;
+ size_t phdr_size;
+ void *p = ehdr, *note;
+ int ret;
+
+ ret = memcmp(ehdr->e_ident, ELFMAG, SELFMAG);
+ FAIL_IF(ret);
+
+ FAIL_IF(ehdr->e_type != ET_CORE);
+ FAIL_IF(ehdr->e_machine != EM_PPC64);
+ FAIL_IF(ehdr->e_phoff == 0 || ehdr->e_phnum == 0);
+
+ /*
+ * e_phnum is at most 65535 so calculating the size of the
+ * program header cannot overflow.
+ */
+ phdr_size = sizeof(*phdr) * ehdr->e_phnum;
+
+ /* Sanity check the program header table location. */
+ FAIL_IF(ehdr->e_phoff + phdr_size < ehdr->e_phoff);
+ FAIL_IF(ehdr->e_phoff + phdr_size > core_size);
+
+ /* Find the PT_NOTE segment. */
+ for (phdr = p + ehdr->e_phoff;
+ (void *) phdr < p + ehdr->e_phoff + phdr_size;
+ phdr += ehdr->e_phentsize)
+ if (phdr->p_type == PT_NOTE)
+ break;
+
+ FAIL_IF((void *) phdr >= p + ehdr->e_phoff + phdr_size);
+
+ /* Find the NT_PPC_PKEY note. */
+ for (nhdr = p + phdr->p_offset;
+ (void *) nhdr < p + phdr->p_offset + phdr->p_filesz;
+ nhdr = next_note(nhdr))
+ if (nhdr->n_type == NT_PPC_PKEY)
+ break;
+
+ FAIL_IF((void *) nhdr >= p + phdr->p_offset + phdr->p_filesz);
+ FAIL_IF(nhdr->n_descsz == 0);
+
+ p = nhdr;
+ note = p + sizeof(*nhdr) + __ALIGN_KERNEL(nhdr->n_namesz, 4);
+
+ regs = (unsigned long *) note;
+
+ printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
+ core_read_running, regs[0], regs[1], regs[2]);
+
+ FAIL_IF(regs[0] != info->amr);
+ FAIL_IF(regs[1] != info->iamr);
+ FAIL_IF(regs[2] != info->uamor);
+
+ return TEST_PASS;
+}
+
+static int parent(struct shared_info *info, pid_t pid)
+{
+ char *filenames, *filename[3];
+ int fd, i, ret, status;
+ unsigned long regs[3];
+ off_t core_size;
+ void *core;
+
+ /*
+ * Get the initial values for AMR, IAMR and UAMOR and communicate them
+ * to the child.
+ */
+ ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ info->amr = regs[0];
+ info->iamr = regs[1];
+ info->uamor = regs[2];
+
+ /* Wake up child so that it can set itself up. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait(&status);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ return TEST_FAIL;
+ } else if (!WIFSIGNALED(status) || !WCOREDUMP(status)) {
+ printf("Child didn't dump core\n");
+ return TEST_FAIL;
+ }
+
+ /* Construct array of core file names to try. */
+
+ filename[0] = filenames = malloc(PATH_MAX);
+ if (!filenames) {
+ perror("Error allocating memory");
+ return TEST_FAIL;
+ }
+
+ ret = snprintf(filename[0], PATH_MAX, "core-pkey.%d", pid);
+ if (ret < 0 || ret >= PATH_MAX) {
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ filename[1] = filename[0] + ret + 1;
+ ret = snprintf(filename[1], PATH_MAX - ret - 1, "core.%d", pid);
+ if (ret < 0 || ret >= PATH_MAX - ret - 1) {
+ ret = TEST_FAIL;
+ goto out;
+ }
+ filename[2] = "core";
+
+ for (i = 0; i < 3; i++) {
+ core_size = try_core_file(filename[i], info, pid);
+ if (core_size != TEST_FAIL)
+ break;
+ }
+
+ if (i == 3) {
+ printf("Couldn't find core file\n");
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ fd = open(filename[i], O_RDONLY);
+ if (fd == -1) {
+ perror("Error opening core file");
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ core = mmap(NULL, core_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (core == (void *) -1) {
+ perror("Error mmaping core file");
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ ret = check_core_file(info, core, core_size);
+
+ munmap(core, core_size);
+ close(fd);
+ unlink(filename[i]);
+
+ out:
+ free(filenames);
+
+ return ret;
+}
+
+static int write_core_pattern(const char *core_pattern)
+{
+ size_t len = strlen(core_pattern), ret;
+ FILE *f;
+
+ f = fopen(core_pattern_file, "w");
+ if (!f) {
+ perror("Error writing to core_pattern file");
+ return TEST_FAIL;
+ }
+
+ ret = fwrite(core_pattern, 1, len, f);
+ fclose(f);
+ if (ret != len) {
+ perror("Error writing to core_pattern file");
+ return TEST_FAIL;
+ }
+
+ return TEST_PASS;
+}
+
+static int setup_core_pattern(char **core_pattern_, bool *changed_)
+{
+ FILE *f;
+ char *core_pattern;
+ int ret;
+
+ core_pattern = malloc(PATH_MAX);
+ if (!core_pattern) {
+ perror("Error allocating memory");
+ return TEST_FAIL;
+ }
+
+ f = fopen(core_pattern_file, "r");
+ if (!f) {
+ perror("Error opening core_pattern file");
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ ret = fread(core_pattern, 1, PATH_MAX, f);
+ fclose(f);
+ if (!ret) {
+ perror("Error reading core_pattern file");
+ ret = TEST_FAIL;
+ goto out;
+ }
+
+ /* Check whether we can predict the name of the core file. */
+ if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p"))
+ *changed_ = false;
+ else {
+ ret = write_core_pattern("core-pkey.%p");
+ if (ret)
+ goto out;
+
+ *changed_ = true;
+ }
+
+ *core_pattern_ = core_pattern;
+ ret = TEST_PASS;
+
+ out:
+ if (ret)
+ free(core_pattern);
+
+ return ret;
+}
+
+static int core_pkey(void)
+{
+ char *core_pattern;
+ bool changed_core_pattern;
+ struct shared_info *info;
+ int shm_id;
+ int ret;
+ pid_t pid;
+
+ ret = setup_core_pattern(&core_pattern, &changed_core_pattern);
+ if (ret)
+ return ret;
+
+ shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
+ info = shmat(shm_id, NULL, 0);
+
+ ret = init_child_sync(&info->child_sync);
+ if (ret)
+ return ret;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ ret = TEST_FAIL;
+ } else if (pid == 0)
+ ret = child(info);
+ else
+ ret = parent(info, pid);
+
+ shmdt(info);
+
+ if (pid) {
+ destroy_child_sync(&info->child_sync);
+ shmctl(shm_id, IPC_RMID, NULL);
+
+ if (changed_core_pattern)
+ write_core_pattern(core_pattern);
+ }
+
+ free(core_pattern);
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(core_pkey, "core_pkey");
+}
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] selftests/powerpc: Add ptrace tests for Protection Key registers
2018-02-23 18:33 [PATCH 1/2] selftests/powerpc: Add ptrace tests for Protection Key registers Thiago Jung Bauermann
2018-02-23 18:33 ` [PATCH 2/2] selftests/powerpc: Add core file test " Thiago Jung Bauermann
@ 2018-02-23 18:50 ` Ram Pai
2018-03-27 17:35 ` Thiago Jung Bauermann
2018-05-17 14:03 ` Michael Ellerman
2 siblings, 1 reply; 10+ messages in thread
From: Ram Pai @ 2018-02-23 18:50 UTC (permalink / raw)
To: Thiago Jung Bauermann
Cc: linuxppc-dev, linux-kselftest, linux-kernel, Michael Ellerman
On Fri, Feb 23, 2018 at 03:33:43PM -0300, Thiago Jung Bauermann wrote:
> This test exercises read and write access to the AMR, IAMR and UAMOR.
>
Tested-by: Ram Pai <linuxram@us.ibm.com>
Acked-by: Ram Pai <linuxram@us.ibm.com>
> Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
> ---
> tools/testing/selftests/powerpc/include/reg.h | 1 +
> tools/testing/selftests/powerpc/ptrace/Makefile | 5 +-
> tools/testing/selftests/powerpc/ptrace/child.h | 130 ++++++++
> .../testing/selftests/powerpc/ptrace/ptrace-pkey.c | 326 +++++++++++++++++++++
> tools/testing/selftests/powerpc/ptrace/ptrace.h | 37 +++
> 5 files changed, 498 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/powerpc/ptrace/child.h
> create mode 100644 tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
>
> diff --git a/tools/testing/selftests/powerpc/include/reg.h b/tools/testing/selftests/powerpc/include/reg.h
> index 4afdebcce4cd..7f348c059bc2 100644
> --- a/tools/testing/selftests/powerpc/include/reg.h
> +++ b/tools/testing/selftests/powerpc/include/reg.h
> @@ -54,6 +54,7 @@
> #define SPRN_DSCR_PRIV 0x11 /* Privilege State DSCR */
> #define SPRN_DSCR 0x03 /* Data Stream Control Register */
> #define SPRN_PPR 896 /* Program Priority Register */
> +#define SPRN_AMR 13 /* Authority Mask Register - problem state */
>
> /* TEXASR register bits */
> #define TEXASR_FC 0xFE00000000000000
> diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile
> index 480305266504..707ba734faf2 100644
> --- a/tools/testing/selftests/powerpc/ptrace/Makefile
> +++ b/tools/testing/selftests/powerpc/ptrace/Makefile
> @@ -1,7 +1,7 @@
> # SPDX-License-Identifier: GPL-2.0
> TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \
> ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \
> - ptrace-tm-spd-vsx ptrace-tm-spr
> + ptrace-tm-spd-vsx ptrace-tm-spr ptrace-pkey
>
> include ../../lib.mk
>
> @@ -9,6 +9,9 @@ all: $(TEST_PROGS)
>
> CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm -fno-pie
>
> +ptrace-pkey: ../harness.c ../utils.c ../lib/reg.S ptrace.h child.h ptrace-pkey.c
> + $(LINK.c) $^ $(LDLIBS) -pthread -o $@
> +
> $(TEST_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h
>
> clean:
> diff --git a/tools/testing/selftests/powerpc/ptrace/child.h b/tools/testing/selftests/powerpc/ptrace/child.h
> new file mode 100644
> index 000000000000..40c1a6d92111
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/ptrace/child.h
> @@ -0,0 +1,130 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Helper functions to sync execution between parent and child processes.
> + *
> + * Copyright 2018, Thiago Jung Bauermann, IBM Corporation.
> + */
> +#include <stdio.h>
> +#include <stdbool.h>
> +#include <semaphore.h>
> +
> +/*
> + * Information in a shared memory location for synchronization between child and
> + * parent.
> + */
> +struct child_sync {
> + /* The parent waits on this semaphore. */
> + sem_t sem_parent;
> +
> + /* If true, the child should give up as well. */
> + bool parent_gave_up;
> +
> + /* The child waits on this semaphore. */
> + sem_t sem_child;
> +
> + /* If true, the parent should give up as well. */
> + bool child_gave_up;
> +};
> +
> +#define CHILD_FAIL_IF(x, sync) \
> + do { \
> + if (x) { \
> + fprintf(stderr, \
> + "[FAIL] Test FAILED on line %d\n", __LINE__); \
> + (sync)->child_gave_up = true; \
> + prod_parent(sync); \
> + return 1; \
> + } \
> + } while (0)
> +
> +#define PARENT_FAIL_IF(x, sync) \
> + do { \
> + if (x) { \
> + fprintf(stderr, \
> + "[FAIL] Test FAILED on line %d\n", __LINE__); \
> + (sync)->parent_gave_up = true; \
> + prod_child(sync); \
> + return 1; \
> + } \
> + } while (0)
> +
> +int init_child_sync(struct child_sync *sync)
> +{
> + int ret;
> +
> + ret = sem_init(&sync->sem_parent, 1, 0);
> + if (ret) {
> + perror("Semaphore initialization failed");
> + return 1;
> + }
> +
> + ret = sem_init(&sync->sem_child, 1, 0);
> + if (ret) {
> + perror("Semaphore initialization failed");
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +void destroy_child_sync(struct child_sync *sync)
> +{
> + sem_destroy(&sync->sem_parent);
> + sem_destroy(&sync->sem_child);
> +}
> +
> +int wait_child(struct child_sync *sync)
> +{
> + int ret;
> +
> + /* Wait until the child prods us. */
> + ret = sem_wait(&sync->sem_parent);
> + if (ret) {
> + perror("Error waiting for child");
> + return 1;
> + }
> +
> + return sync->child_gave_up;
> +}
> +
> +int prod_child(struct child_sync *sync)
> +{
> + int ret;
> +
> + /* Unblock the child now. */
> + ret = sem_post(&sync->sem_child);
> + if (ret) {
> + perror("Error prodding child");
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +int wait_parent(struct child_sync *sync)
> +{
> + int ret;
> +
> + /* Wait until the parent prods us. */
> + ret = sem_wait(&sync->sem_child);
> + if (ret) {
> + perror("Error waiting for parent");
> + return 1;
> + }
> +
> + return sync->parent_gave_up;
> +}
> +
> +int prod_parent(struct child_sync *sync)
> +{
> + int ret;
> +
> + /* Unblock the parent now. */
> + ret = sem_post(&sync->sem_parent);
> + if (ret) {
> + perror("Error prodding parent");
> + return 1;
> + }
> +
> + return 0;
> +}
> diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
> new file mode 100644
> index 000000000000..8332b9338d39
> --- /dev/null
> +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
> @@ -0,0 +1,326 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Ptrace test for Memory Protection Key registers
> + *
> + * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
> + * Copyright (C) 2018 IBM Corporation.
> + */
> +#include "ptrace.h"
> +#include "child.h"
> +
> +#ifndef __NR_pkey_alloc
> +#define __NR_pkey_alloc 384
> +#endif
> +
> +#ifndef __NR_pkey_free
> +#define __NR_pkey_free 385
> +#endif
> +
> +#ifndef NT_PPC_PKEY
> +#define NT_PPC_PKEY 0x110
> +#endif
> +
> +#ifndef PKEY_DISABLE_EXECUTE
> +#define PKEY_DISABLE_EXECUTE 0x4
> +#endif
> +
> +#define AMR_BITS_PER_PKEY 2
> +#define PKEY_REG_BITS (sizeof(u64) * 8)
> +#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY))
> +
> +static const char user_read[] = "[User Read (Running)]";
> +static const char user_write[] = "[User Write (Running)]";
> +static const char ptrace_read_running[] = "[Ptrace Read (Running)]";
> +static const char ptrace_write_running[] = "[Ptrace Write (Running)]";
> +
> +/* Information shared between the parent and the child. */
> +struct shared_info {
> + struct child_sync child_sync;
> +
> + /* AMR value the parent expects to read from the child. */
> + unsigned long amr1;
> +
> + /* AMR value the parent is expected to write to the child. */
> + unsigned long amr2;
> +
> + /* AMR value that ptrace should refuse to write to the child. */
> + unsigned long amr3;
> +
> + /* IAMR value the parent expects to read from the child. */
> + unsigned long expected_iamr;
> +
> + /* UAMOR value the parent expects to read from the child. */
> + unsigned long expected_uamor;
> +
> + /*
> + * IAMR and UAMOR values that ptrace should refuse to write to the child
> + * (even though they're valid ones) because userspace doesn't have
> + * access to those registers.
> + */
> + unsigned long new_iamr;
> + unsigned long new_uamor;
> +};
> +
> +static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
> +{
> + return syscall(__NR_pkey_alloc, flags, init_access_rights);
> +}
> +
> +static int sys_pkey_free(int pkey)
> +{
> + return syscall(__NR_pkey_free, pkey);
> +}
> +
> +static int child(struct shared_info *info)
> +{
> + unsigned long reg;
> + bool disable_execute = true;
> + int pkey1, pkey2, pkey3;
> + int ret;
> +
> + /* Wait until parent fills out the initial register values. */
> + ret = wait_parent(&info->child_sync);
> + if (ret)
> + return ret;
> +
> + /* Get some pkeys so that we can change their bits in the AMR. */
> + pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
> + if (pkey1 < 0) {
> + pkey1 = sys_pkey_alloc(0, 0);
> + CHILD_FAIL_IF(pkey1 < 0, &info->child_sync);
> +
> + disable_execute = false;
> + }
> +
> + pkey2 = sys_pkey_alloc(0, 0);
> + CHILD_FAIL_IF(pkey2 < 0, &info->child_sync);
> +
> + pkey3 = sys_pkey_alloc(0, 0);
> + CHILD_FAIL_IF(pkey3 < 0, &info->child_sync);
> +
> + info->amr1 |= 3ul << pkeyshift(pkey1);
> + info->amr2 |= 3ul << pkeyshift(pkey2);
> + info->amr3 |= info->amr2 | 3ul << pkeyshift(pkey3);
> +
> + if (disable_execute)
> + info->expected_iamr |= 1ul << pkeyshift(pkey1);
> +
> + info->expected_uamor |= 3ul << pkeyshift(pkey1) |
> + 3ul << pkeyshift(pkey2);
> + info->new_iamr |= 1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2);
> + info->new_uamor |= 3ul << pkeyshift(pkey1);
> +
> + /*
> + * We won't use pkey3. We just want a plausible but invalid key to test
> + * whether ptrace will let us write to AMR bits we are not supposed to.
> + *
> + * This also tests whether the kernel restores the UAMOR permissions
> + * after a key is freed.
> + */
> + sys_pkey_free(pkey3);
> +
> + printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
> + user_write, info->amr1, pkey1, pkey2, pkey3);
> +
> + mtspr(SPRN_AMR, info->amr1);
> +
> + /* Wait for parent to read our AMR value and write a new one. */
> + ret = prod_parent(&info->child_sync);
> + CHILD_FAIL_IF(ret, &info->child_sync);
> +
> + ret = wait_parent(&info->child_sync);
> + if (ret)
> + return ret;
> +
> + reg = mfspr(SPRN_AMR);
> +
> + printf("%-30s AMR: %016lx\n", user_read, reg);
> +
> + CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
> +
> + /*
> + * Wait for parent to try to write an invalid AMR value.
> + */
> + ret = prod_parent(&info->child_sync);
> + CHILD_FAIL_IF(ret, &info->child_sync);
> +
> + ret = wait_parent(&info->child_sync);
> + if (ret)
> + return ret;
> +
> + reg = mfspr(SPRN_AMR);
> +
> + printf("%-30s AMR: %016lx\n", user_read, reg);
> +
> + CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
> +
> + /*
> + * Wait for parent to try to write an IAMR and a UAMOR value. We can't
> + * verify them, but we can verify that the AMR didn't change.
> + */
> + ret = prod_parent(&info->child_sync);
> + CHILD_FAIL_IF(ret, &info->child_sync);
> +
> + ret = wait_parent(&info->child_sync);
> + if (ret)
> + return ret;
> +
> + reg = mfspr(SPRN_AMR);
> +
> + printf("%-30s AMR: %016lx\n", user_read, reg);
> +
> + CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
> +
> + /* Now let parent now that we are finished. */
> +
> + ret = prod_parent(&info->child_sync);
> + CHILD_FAIL_IF(ret, &info->child_sync);
> +
> + return TEST_PASS;
> +}
> +
> +static int parent(struct shared_info *info, pid_t pid)
> +{
> + unsigned long regs[3];
> + int ret, status;
> +
> + /*
> + * Get the initial values for AMR, IAMR and UAMOR and communicate them
> + * to the child.
> + */
> + ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
> + PARENT_FAIL_IF(ret, &info->child_sync);
> +
> + info->amr1 = info->amr2 = info->amr3 = regs[0];
> + info->expected_iamr = info->new_iamr = regs[1];
> + info->expected_uamor = info->new_uamor = regs[2];
> +
> + /* Wake up child so that it can set itself up. */
> + ret = prod_child(&info->child_sync);
> + PARENT_FAIL_IF(ret, &info->child_sync);
> +
> + ret = wait_child(&info->child_sync);
> + if (ret)
> + return ret;
> +
> + /* Verify that we can read the pkey registers from the child. */
> + ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
> + PARENT_FAIL_IF(ret, &info->child_sync);
> +
> + printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
> + ptrace_read_running, regs[0], regs[1], regs[2]);
> +
> + PARENT_FAIL_IF(regs[0] != info->amr1, &info->child_sync);
> + PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);
> + PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);
> +
> + /* Write valid AMR value in child. */
> + ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr2, 1);
> + PARENT_FAIL_IF(ret, &info->child_sync);
> +
> + printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr2);
> +
> + /* Wake up child so that it can verify it changed. */
> + ret = prod_child(&info->child_sync);
> + PARENT_FAIL_IF(ret, &info->child_sync);
> +
> + ret = wait_child(&info->child_sync);
> + if (ret)
> + return ret;
> +
> + /* Write invalid AMR value in child. */
> + ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr3, 1);
> + PARENT_FAIL_IF(ret, &info->child_sync);
> +
> + printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr3);
> +
> + /* Wake up child so that it can verify it didn't change. */
> + ret = prod_child(&info->child_sync);
> + PARENT_FAIL_IF(ret, &info->child_sync);
> +
> + ret = wait_child(&info->child_sync);
> + if (ret)
> + return ret;
> +
> + /* Try to write to IAMR. */
> + regs[0] = info->amr1;
> + regs[1] = info->new_iamr;
> + ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 2);
> + PARENT_FAIL_IF(!ret, &info->child_sync);
> +
> + printf("%-30s AMR: %016lx IAMR: %016lx\n",
> + ptrace_write_running, regs[0], regs[1]);
> +
> + /* Try to write to IAMR and UAMOR. */
> + regs[2] = info->new_uamor;
> + ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 3);
> + PARENT_FAIL_IF(!ret, &info->child_sync);
> +
> + printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
> + ptrace_write_running, regs[0], regs[1], regs[2]);
> +
> + /* Verify that all registers still have their expected values. */
> + ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
> + PARENT_FAIL_IF(ret, &info->child_sync);
> +
> + printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
> + ptrace_read_running, regs[0], regs[1], regs[2]);
> +
> + PARENT_FAIL_IF(regs[0] != info->amr2, &info->child_sync);
> + PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);
> + PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);
> +
> + /* Wake up child so that it can verify AMR didn't change and wrap up. */
> + ret = prod_child(&info->child_sync);
> + PARENT_FAIL_IF(ret, &info->child_sync);
> +
> + ret = wait(&status);
> + if (ret != pid) {
> + printf("Child's exit status not captured\n");
> + ret = TEST_PASS;
> + } else if (!WIFEXITED(status)) {
> + printf("Child exited abnormally\n");
> + ret = TEST_FAIL;
> + } else
> + ret = WEXITSTATUS(status) ? TEST_FAIL : TEST_PASS;
> +
> + return ret;
> +}
> +
> +static int ptrace_pkey(void)
> +{
> + struct shared_info *info;
> + int shm_id;
> + int ret;
> + pid_t pid;
> +
> + shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
> + info = shmat(shm_id, NULL, 0);
> +
> + ret = init_child_sync(&info->child_sync);
> + if (ret)
> + return ret;
> +
> + pid = fork();
> + if (pid < 0) {
> + perror("fork() failed");
> + ret = TEST_FAIL;
> + } else if (pid == 0)
> + ret = child(info);
> + else
> + ret = parent(info, pid);
> +
> + shmdt(info);
> +
> + if (pid) {
> + destroy_child_sync(&info->child_sync);
> + shmctl(shm_id, IPC_RMID, NULL);
> + }
> +
> + return ret;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + return test_harness(ptrace_pkey, "ptrace_pkey");
> +}
> diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace.h b/tools/testing/selftests/powerpc/ptrace/ptrace.h
> index 19fb825270a1..d2c9c4c2b5ee 100644
> --- a/tools/testing/selftests/powerpc/ptrace/ptrace.h
> +++ b/tools/testing/selftests/powerpc/ptrace/ptrace.h
> @@ -102,6 +102,43 @@ int cont_trace(pid_t child)
> return TEST_PASS;
> }
>
> +int ptrace_read_regs(pid_t child, unsigned long type, unsigned long regs[],
> + int n)
> +{
> + struct iovec iov;
> + long ret;
> +
> + FAIL_IF(start_trace(child));
> +
> + iov.iov_base = regs;
> + iov.iov_len = n * sizeof(unsigned long);
> +
> + ret = ptrace(PTRACE_GETREGSET, child, type, &iov);
> + FAIL_IF(ret != 0);
> +
> + FAIL_IF(stop_trace(child));
> +
> + return TEST_PASS;
> +}
> +
> +long ptrace_write_regs(pid_t child, unsigned long type, unsigned long regs[],
> + int n)
> +{
> + struct iovec iov;
> + long ret;
> +
> + FAIL_IF(start_trace(child));
> +
> + iov.iov_base = regs;
> + iov.iov_len = n * sizeof(unsigned long);
> +
> + ret = ptrace(PTRACE_SETREGSET, child, type, &iov);
> +
> + FAIL_IF(stop_trace(child));
> +
> + return ret;
> +}
> +
> /* TAR, PPR, DSCR */
> int show_tar_registers(pid_t child, unsigned long *out)
> {
--
Ram Pai
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] selftests/powerpc: Add core file test for Protection Key registers
2018-02-23 18:33 ` [PATCH 2/2] selftests/powerpc: Add core file test " Thiago Jung Bauermann
@ 2018-02-23 18:51 ` Ram Pai
2018-05-17 14:03 ` Michael Ellerman
1 sibling, 0 replies; 10+ messages in thread
From: Ram Pai @ 2018-02-23 18:51 UTC (permalink / raw)
To: Thiago Jung Bauermann
Cc: linuxppc-dev, linux-kselftest, linux-kernel, Michael Ellerman
On Fri, Feb 23, 2018 at 03:33:44PM -0300, Thiago Jung Bauermann wrote:
> This test verifies that the AMR, IAMR and UAMOR are being written to a
> process' core file.
>
Acked-by: Ram Pai <linuxram@us.ibm.com>
Tested-by: Ram Pai <linuxram@us.ibm.com>
> Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
> ---
> tools/testing/selftests/powerpc/ptrace/Makefile | 5 +-
> tools/testing/selftests/powerpc/ptrace/core-pkey.c | 460 +++++++++++++++++++++
> 2 files changed, 464 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/powerpc/ptrace/core-pkey.c
>
.snip...
RP
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] selftests/powerpc: Add ptrace tests for Protection Key registers
2018-02-23 18:50 ` [PATCH 1/2] selftests/powerpc: Add ptrace tests " Ram Pai
@ 2018-03-27 17:35 ` Thiago Jung Bauermann
0 siblings, 0 replies; 10+ messages in thread
From: Thiago Jung Bauermann @ 2018-03-27 17:35 UTC (permalink / raw)
To: Ram Pai; +Cc: linuxppc-dev, linux-kernel, linux-kselftest
Ram Pai <linuxram@us.ibm.com> writes:
> On Fri, Feb 23, 2018 at 03:33:43PM -0300, Thiago Jung Bauermann wrote:
>> This test exercises read and write access to the AMR, IAMR and UAMOR.
>>
>
> Tested-by: Ram Pai <linuxram@us.ibm.com>
> Acked-by: Ram Pai <linuxram@us.ibm.com>
Ping? Can this and patch 2/2 go in v4.17?
>> Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
>> ---
>> tools/testing/selftests/powerpc/include/reg.h | 1 +
>> tools/testing/selftests/powerpc/ptrace/Makefile | 5 +-
>> tools/testing/selftests/powerpc/ptrace/child.h | 130 ++++++++
>> .../testing/selftests/powerpc/ptrace/ptrace-pkey.c | 326 +++++++++++++++++++++
>> tools/testing/selftests/powerpc/ptrace/ptrace.h | 37 +++
>> 5 files changed, 498 insertions(+), 1 deletion(-)
>> create mode 100644 tools/testing/selftests/powerpc/ptrace/child.h
>> create mode 100644 tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
>>
>> diff --git a/tools/testing/selftests/powerpc/include/reg.h b/tools/testing/selftests/powerpc/include/reg.h
>> index 4afdebcce4cd..7f348c059bc2 100644
>> --- a/tools/testing/selftests/powerpc/include/reg.h
>> +++ b/tools/testing/selftests/powerpc/include/reg.h
>> @@ -54,6 +54,7 @@
>> #define SPRN_DSCR_PRIV 0x11 /* Privilege State DSCR */
>> #define SPRN_DSCR 0x03 /* Data Stream Control Register */
>> #define SPRN_PPR 896 /* Program Priority Register */
>> +#define SPRN_AMR 13 /* Authority Mask Register - problem state */
>>
>> /* TEXASR register bits */
>> #define TEXASR_FC 0xFE00000000000000
>> diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile
>> index 480305266504..707ba734faf2 100644
>> --- a/tools/testing/selftests/powerpc/ptrace/Makefile
>> +++ b/tools/testing/selftests/powerpc/ptrace/Makefile
>> @@ -1,7 +1,7 @@
>> # SPDX-License-Identifier: GPL-2.0
>> TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \
>> ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \
>> - ptrace-tm-spd-vsx ptrace-tm-spr
>> + ptrace-tm-spd-vsx ptrace-tm-spr ptrace-pkey
>>
>> include ../../lib.mk
>>
>> @@ -9,6 +9,9 @@ all: $(TEST_PROGS)
>>
>> CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm -fno-pie
>>
>> +ptrace-pkey: ../harness.c ../utils.c ../lib/reg.S ptrace.h child.h ptrace-pkey.c
>> + $(LINK.c) $^ $(LDLIBS) -pthread -o $@
>> +
>> $(TEST_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h
>>
>> clean:
>> diff --git a/tools/testing/selftests/powerpc/ptrace/child.h b/tools/testing/selftests/powerpc/ptrace/child.h
>> new file mode 100644
>> index 000000000000..40c1a6d92111
>> --- /dev/null
>> +++ b/tools/testing/selftests/powerpc/ptrace/child.h
>> @@ -0,0 +1,130 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Helper functions to sync execution between parent and child processes.
>> + *
>> + * Copyright 2018, Thiago Jung Bauermann, IBM Corporation.
>> + */
>> +#include <stdio.h>
>> +#include <stdbool.h>
>> +#include <semaphore.h>
>> +
>> +/*
>> + * Information in a shared memory location for synchronization between child and
>> + * parent.
>> + */
>> +struct child_sync {
>> + /* The parent waits on this semaphore. */
>> + sem_t sem_parent;
>> +
>> + /* If true, the child should give up as well. */
>> + bool parent_gave_up;
>> +
>> + /* The child waits on this semaphore. */
>> + sem_t sem_child;
>> +
>> + /* If true, the parent should give up as well. */
>> + bool child_gave_up;
>> +};
>> +
>> +#define CHILD_FAIL_IF(x, sync) \
>> + do { \
>> + if (x) { \
>> + fprintf(stderr, \
>> + "[FAIL] Test FAILED on line %d\n", __LINE__); \
>> + (sync)->child_gave_up = true; \
>> + prod_parent(sync); \
>> + return 1; \
>> + } \
>> + } while (0)
>> +
>> +#define PARENT_FAIL_IF(x, sync) \
>> + do { \
>> + if (x) { \
>> + fprintf(stderr, \
>> + "[FAIL] Test FAILED on line %d\n", __LINE__); \
>> + (sync)->parent_gave_up = true; \
>> + prod_child(sync); \
>> + return 1; \
>> + } \
>> + } while (0)
>> +
>> +int init_child_sync(struct child_sync *sync)
>> +{
>> + int ret;
>> +
>> + ret = sem_init(&sync->sem_parent, 1, 0);
>> + if (ret) {
>> + perror("Semaphore initialization failed");
>> + return 1;
>> + }
>> +
>> + ret = sem_init(&sync->sem_child, 1, 0);
>> + if (ret) {
>> + perror("Semaphore initialization failed");
>> + return 1;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +void destroy_child_sync(struct child_sync *sync)
>> +{
>> + sem_destroy(&sync->sem_parent);
>> + sem_destroy(&sync->sem_child);
>> +}
>> +
>> +int wait_child(struct child_sync *sync)
>> +{
>> + int ret;
>> +
>> + /* Wait until the child prods us. */
>> + ret = sem_wait(&sync->sem_parent);
>> + if (ret) {
>> + perror("Error waiting for child");
>> + return 1;
>> + }
>> +
>> + return sync->child_gave_up;
>> +}
>> +
>> +int prod_child(struct child_sync *sync)
>> +{
>> + int ret;
>> +
>> + /* Unblock the child now. */
>> + ret = sem_post(&sync->sem_child);
>> + if (ret) {
>> + perror("Error prodding child");
>> + return 1;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +int wait_parent(struct child_sync *sync)
>> +{
>> + int ret;
>> +
>> + /* Wait until the parent prods us. */
>> + ret = sem_wait(&sync->sem_child);
>> + if (ret) {
>> + perror("Error waiting for parent");
>> + return 1;
>> + }
>> +
>> + return sync->parent_gave_up;
>> +}
>> +
>> +int prod_parent(struct child_sync *sync)
>> +{
>> + int ret;
>> +
>> + /* Unblock the parent now. */
>> + ret = sem_post(&sync->sem_parent);
>> + if (ret) {
>> + perror("Error prodding parent");
>> + return 1;
>> + }
>> +
>> + return 0;
>> +}
>> diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
>> new file mode 100644
>> index 000000000000..8332b9338d39
>> --- /dev/null
>> +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
>> @@ -0,0 +1,326 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Ptrace test for Memory Protection Key registers
>> + *
>> + * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
>> + * Copyright (C) 2018 IBM Corporation.
>> + */
>> +#include "ptrace.h"
>> +#include "child.h"
>> +
>> +#ifndef __NR_pkey_alloc
>> +#define __NR_pkey_alloc 384
>> +#endif
>> +
>> +#ifndef __NR_pkey_free
>> +#define __NR_pkey_free 385
>> +#endif
>> +
>> +#ifndef NT_PPC_PKEY
>> +#define NT_PPC_PKEY 0x110
>> +#endif
>> +
>> +#ifndef PKEY_DISABLE_EXECUTE
>> +#define PKEY_DISABLE_EXECUTE 0x4
>> +#endif
>> +
>> +#define AMR_BITS_PER_PKEY 2
>> +#define PKEY_REG_BITS (sizeof(u64) * 8)
>> +#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY))
>> +
>> +static const char user_read[] = "[User Read (Running)]";
>> +static const char user_write[] = "[User Write (Running)]";
>> +static const char ptrace_read_running[] = "[Ptrace Read (Running)]";
>> +static const char ptrace_write_running[] = "[Ptrace Write (Running)]";
>> +
>> +/* Information shared between the parent and the child. */
>> +struct shared_info {
>> + struct child_sync child_sync;
>> +
>> + /* AMR value the parent expects to read from the child. */
>> + unsigned long amr1;
>> +
>> + /* AMR value the parent is expected to write to the child. */
>> + unsigned long amr2;
>> +
>> + /* AMR value that ptrace should refuse to write to the child. */
>> + unsigned long amr3;
>> +
>> + /* IAMR value the parent expects to read from the child. */
>> + unsigned long expected_iamr;
>> +
>> + /* UAMOR value the parent expects to read from the child. */
>> + unsigned long expected_uamor;
>> +
>> + /*
>> + * IAMR and UAMOR values that ptrace should refuse to write to the child
>> + * (even though they're valid ones) because userspace doesn't have
>> + * access to those registers.
>> + */
>> + unsigned long new_iamr;
>> + unsigned long new_uamor;
>> +};
>> +
>> +static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
>> +{
>> + return syscall(__NR_pkey_alloc, flags, init_access_rights);
>> +}
>> +
>> +static int sys_pkey_free(int pkey)
>> +{
>> + return syscall(__NR_pkey_free, pkey);
>> +}
>> +
>> +static int child(struct shared_info *info)
>> +{
>> + unsigned long reg;
>> + bool disable_execute = true;
>> + int pkey1, pkey2, pkey3;
>> + int ret;
>> +
>> + /* Wait until parent fills out the initial register values. */
>> + ret = wait_parent(&info->child_sync);
>> + if (ret)
>> + return ret;
>> +
>> + /* Get some pkeys so that we can change their bits in the AMR. */
>> + pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
>> + if (pkey1 < 0) {
>> + pkey1 = sys_pkey_alloc(0, 0);
>> + CHILD_FAIL_IF(pkey1 < 0, &info->child_sync);
>> +
>> + disable_execute = false;
>> + }
>> +
>> + pkey2 = sys_pkey_alloc(0, 0);
>> + CHILD_FAIL_IF(pkey2 < 0, &info->child_sync);
>> +
>> + pkey3 = sys_pkey_alloc(0, 0);
>> + CHILD_FAIL_IF(pkey3 < 0, &info->child_sync);
>> +
>> + info->amr1 |= 3ul << pkeyshift(pkey1);
>> + info->amr2 |= 3ul << pkeyshift(pkey2);
>> + info->amr3 |= info->amr2 | 3ul << pkeyshift(pkey3);
>> +
>> + if (disable_execute)
>> + info->expected_iamr |= 1ul << pkeyshift(pkey1);
>> +
>> + info->expected_uamor |= 3ul << pkeyshift(pkey1) |
>> + 3ul << pkeyshift(pkey2);
>> + info->new_iamr |= 1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2);
>> + info->new_uamor |= 3ul << pkeyshift(pkey1);
>> +
>> + /*
>> + * We won't use pkey3. We just want a plausible but invalid key to test
>> + * whether ptrace will let us write to AMR bits we are not supposed to.
>> + *
>> + * This also tests whether the kernel restores the UAMOR permissions
>> + * after a key is freed.
>> + */
>> + sys_pkey_free(pkey3);
>> +
>> + printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
>> + user_write, info->amr1, pkey1, pkey2, pkey3);
>> +
>> + mtspr(SPRN_AMR, info->amr1);
>> +
>> + /* Wait for parent to read our AMR value and write a new one. */
>> + ret = prod_parent(&info->child_sync);
>> + CHILD_FAIL_IF(ret, &info->child_sync);
>> +
>> + ret = wait_parent(&info->child_sync);
>> + if (ret)
>> + return ret;
>> +
>> + reg = mfspr(SPRN_AMR);
>> +
>> + printf("%-30s AMR: %016lx\n", user_read, reg);
>> +
>> + CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
>> +
>> + /*
>> + * Wait for parent to try to write an invalid AMR value.
>> + */
>> + ret = prod_parent(&info->child_sync);
>> + CHILD_FAIL_IF(ret, &info->child_sync);
>> +
>> + ret = wait_parent(&info->child_sync);
>> + if (ret)
>> + return ret;
>> +
>> + reg = mfspr(SPRN_AMR);
>> +
>> + printf("%-30s AMR: %016lx\n", user_read, reg);
>> +
>> + CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
>> +
>> + /*
>> + * Wait for parent to try to write an IAMR and a UAMOR value. We can't
>> + * verify them, but we can verify that the AMR didn't change.
>> + */
>> + ret = prod_parent(&info->child_sync);
>> + CHILD_FAIL_IF(ret, &info->child_sync);
>> +
>> + ret = wait_parent(&info->child_sync);
>> + if (ret)
>> + return ret;
>> +
>> + reg = mfspr(SPRN_AMR);
>> +
>> + printf("%-30s AMR: %016lx\n", user_read, reg);
>> +
>> + CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
>> +
>> + /* Now let parent now that we are finished. */
>> +
>> + ret = prod_parent(&info->child_sync);
>> + CHILD_FAIL_IF(ret, &info->child_sync);
>> +
>> + return TEST_PASS;
>> +}
>> +
>> +static int parent(struct shared_info *info, pid_t pid)
>> +{
>> + unsigned long regs[3];
>> + int ret, status;
>> +
>> + /*
>> + * Get the initial values for AMR, IAMR and UAMOR and communicate them
>> + * to the child.
>> + */
>> + ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
>> + PARENT_FAIL_IF(ret, &info->child_sync);
>> +
>> + info->amr1 = info->amr2 = info->amr3 = regs[0];
>> + info->expected_iamr = info->new_iamr = regs[1];
>> + info->expected_uamor = info->new_uamor = regs[2];
>> +
>> + /* Wake up child so that it can set itself up. */
>> + ret = prod_child(&info->child_sync);
>> + PARENT_FAIL_IF(ret, &info->child_sync);
>> +
>> + ret = wait_child(&info->child_sync);
>> + if (ret)
>> + return ret;
>> +
>> + /* Verify that we can read the pkey registers from the child. */
>> + ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
>> + PARENT_FAIL_IF(ret, &info->child_sync);
>> +
>> + printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
>> + ptrace_read_running, regs[0], regs[1], regs[2]);
>> +
>> + PARENT_FAIL_IF(regs[0] != info->amr1, &info->child_sync);
>> + PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);
>> + PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);
>> +
>> + /* Write valid AMR value in child. */
>> + ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr2, 1);
>> + PARENT_FAIL_IF(ret, &info->child_sync);
>> +
>> + printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr2);
>> +
>> + /* Wake up child so that it can verify it changed. */
>> + ret = prod_child(&info->child_sync);
>> + PARENT_FAIL_IF(ret, &info->child_sync);
>> +
>> + ret = wait_child(&info->child_sync);
>> + if (ret)
>> + return ret;
>> +
>> + /* Write invalid AMR value in child. */
>> + ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr3, 1);
>> + PARENT_FAIL_IF(ret, &info->child_sync);
>> +
>> + printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr3);
>> +
>> + /* Wake up child so that it can verify it didn't change. */
>> + ret = prod_child(&info->child_sync);
>> + PARENT_FAIL_IF(ret, &info->child_sync);
>> +
>> + ret = wait_child(&info->child_sync);
>> + if (ret)
>> + return ret;
>> +
>> + /* Try to write to IAMR. */
>> + regs[0] = info->amr1;
>> + regs[1] = info->new_iamr;
>> + ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 2);
>> + PARENT_FAIL_IF(!ret, &info->child_sync);
>> +
>> + printf("%-30s AMR: %016lx IAMR: %016lx\n",
>> + ptrace_write_running, regs[0], regs[1]);
>> +
>> + /* Try to write to IAMR and UAMOR. */
>> + regs[2] = info->new_uamor;
>> + ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 3);
>> + PARENT_FAIL_IF(!ret, &info->child_sync);
>> +
>> + printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
>> + ptrace_write_running, regs[0], regs[1], regs[2]);
>> +
>> + /* Verify that all registers still have their expected values. */
>> + ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
>> + PARENT_FAIL_IF(ret, &info->child_sync);
>> +
>> + printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
>> + ptrace_read_running, regs[0], regs[1], regs[2]);
>> +
>> + PARENT_FAIL_IF(regs[0] != info->amr2, &info->child_sync);
>> + PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);
>> + PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);
>> +
>> + /* Wake up child so that it can verify AMR didn't change and wrap up. */
>> + ret = prod_child(&info->child_sync);
>> + PARENT_FAIL_IF(ret, &info->child_sync);
>> +
>> + ret = wait(&status);
>> + if (ret != pid) {
>> + printf("Child's exit status not captured\n");
>> + ret = TEST_PASS;
>> + } else if (!WIFEXITED(status)) {
>> + printf("Child exited abnormally\n");
>> + ret = TEST_FAIL;
>> + } else
>> + ret = WEXITSTATUS(status) ? TEST_FAIL : TEST_PASS;
>> +
>> + return ret;
>> +}
>> +
>> +static int ptrace_pkey(void)
>> +{
>> + struct shared_info *info;
>> + int shm_id;
>> + int ret;
>> + pid_t pid;
>> +
>> + shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
>> + info = shmat(shm_id, NULL, 0);
>> +
>> + ret = init_child_sync(&info->child_sync);
>> + if (ret)
>> + return ret;
>> +
>> + pid = fork();
>> + if (pid < 0) {
>> + perror("fork() failed");
>> + ret = TEST_FAIL;
>> + } else if (pid == 0)
>> + ret = child(info);
>> + else
>> + ret = parent(info, pid);
>> +
>> + shmdt(info);
>> +
>> + if (pid) {
>> + destroy_child_sync(&info->child_sync);
>> + shmctl(shm_id, IPC_RMID, NULL);
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +int main(int argc, char *argv[])
>> +{
>> + return test_harness(ptrace_pkey, "ptrace_pkey");
>> +}
>> diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace.h b/tools/testing/selftests/powerpc/ptrace/ptrace.h
>> index 19fb825270a1..d2c9c4c2b5ee 100644
>> --- a/tools/testing/selftests/powerpc/ptrace/ptrace.h
>> +++ b/tools/testing/selftests/powerpc/ptrace/ptrace.h
>> @@ -102,6 +102,43 @@ int cont_trace(pid_t child)
>> return TEST_PASS;
>> }
>>
>> +int ptrace_read_regs(pid_t child, unsigned long type, unsigned long regs[],
>> + int n)
>> +{
>> + struct iovec iov;
>> + long ret;
>> +
>> + FAIL_IF(start_trace(child));
>> +
>> + iov.iov_base = regs;
>> + iov.iov_len = n * sizeof(unsigned long);
>> +
>> + ret = ptrace(PTRACE_GETREGSET, child, type, &iov);
>> + FAIL_IF(ret != 0);
>> +
>> + FAIL_IF(stop_trace(child));
>> +
>> + return TEST_PASS;
>> +}
>> +
>> +long ptrace_write_regs(pid_t child, unsigned long type, unsigned long regs[],
>> + int n)
>> +{
>> + struct iovec iov;
>> + long ret;
>> +
>> + FAIL_IF(start_trace(child));
>> +
>> + iov.iov_base = regs;
>> + iov.iov_len = n * sizeof(unsigned long);
>> +
>> + ret = ptrace(PTRACE_SETREGSET, child, type, &iov);
>> +
>> + FAIL_IF(stop_trace(child));
>> +
>> + return ret;
>> +}
>> +
>> /* TAR, PPR, DSCR */
>> int show_tar_registers(pid_t child, unsigned long *out)
>> {
--
Thiago Jung Bauermann
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] selftests/powerpc: Add ptrace tests for Protection Key registers
2018-02-23 18:33 [PATCH 1/2] selftests/powerpc: Add ptrace tests for Protection Key registers Thiago Jung Bauermann
2018-02-23 18:33 ` [PATCH 2/2] selftests/powerpc: Add core file test " Thiago Jung Bauermann
2018-02-23 18:50 ` [PATCH 1/2] selftests/powerpc: Add ptrace tests " Ram Pai
@ 2018-05-17 14:03 ` Michael Ellerman
2018-05-25 2:14 ` Thiago Jung Bauermann
2 siblings, 1 reply; 10+ messages in thread
From: Michael Ellerman @ 2018-05-17 14:03 UTC (permalink / raw)
To: Thiago Jung Bauermann, linuxppc-dev
Cc: linux-kselftest, linux-kernel, Ram Pai, Thiago Jung Bauermann
Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> writes:
> This test exercises read and write access to the AMR, IAMR and UAMOR.
>
> Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
> ---
> tools/testing/selftests/powerpc/include/reg.h | 1 +
> tools/testing/selftests/powerpc/ptrace/Makefile | 5 +-
> tools/testing/selftests/powerpc/ptrace/child.h | 130 ++++++++
> .../testing/selftests/powerpc/ptrace/ptrace-pkey.c | 326 +++++++++++++++++++++
This is failing on machines without pkeys:
test: ptrace_pkey
tags: git_version:52e7d87
[FAIL] Test FAILED on line 117
[FAIL] Test FAILED on line 191
failure: ptrace_pkey
I think the first fail is in the child here:
int ptrace_read_regs(pid_t child, unsigned long type, unsigned long regs[],
int n)
{
struct iovec iov;
long ret;
FAIL_IF(start_trace(child));
iov.iov_base = regs;
iov.iov_len = n * sizeof(unsigned long);
ret = ptrace(PTRACE_GETREGSET, child, type, &iov);
FAIL_IF(ret != 0);
Which makes sense.
The test needs to skip if pkeys are not available/enabled. Using the
availability of the REGSET might actually be a nice way to detect that,
because it's read-only.
cheers
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] selftests/powerpc: Add core file test for Protection Key registers
2018-02-23 18:33 ` [PATCH 2/2] selftests/powerpc: Add core file test " Thiago Jung Bauermann
2018-02-23 18:51 ` Ram Pai
@ 2018-05-17 14:03 ` Michael Ellerman
1 sibling, 0 replies; 10+ messages in thread
From: Michael Ellerman @ 2018-05-17 14:03 UTC (permalink / raw)
To: Thiago Jung Bauermann, linuxppc-dev
Cc: linux-kselftest, linux-kernel, Ram Pai, Thiago Jung Bauermann
Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> writes:
> This test verifies that the AMR, IAMR and UAMOR are being written to a
> process' core file.
>
> Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
> ---
> tools/testing/selftests/powerpc/ptrace/Makefile | 5 +-
> tools/testing/selftests/powerpc/ptrace/core-pkey.c | 460 +++++++++++++++++++++
Also failing w/out pkeys:
test: core_pkey
tags: git_version:52e7d87
[FAIL] Test FAILED on line 117
[FAIL] Test FAILED on line 265
failure: core_pkey
cheers
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] selftests/powerpc: Add ptrace tests for Protection Key registers
2018-05-17 14:03 ` Michael Ellerman
@ 2018-05-25 2:14 ` Thiago Jung Bauermann
2018-05-25 10:26 ` Michael Ellerman
0 siblings, 1 reply; 10+ messages in thread
From: Thiago Jung Bauermann @ 2018-05-25 2:14 UTC (permalink / raw)
To: Michael Ellerman
Cc: linuxppc-dev, linux-kselftest, linux-kernel, Ram Pai,
Thiago Jung Bauermann
Michael Ellerman <mpe@ellerman.id.au> writes:
> Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> writes:
>
>> This test exercises read and write access to the AMR, IAMR and UAMOR.
>>
>> Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
>> ---
>> tools/testing/selftests/powerpc/include/reg.h | 1 +
>> tools/testing/selftests/powerpc/ptrace/Makefile | 5 +-
>> tools/testing/selftests/powerpc/ptrace/child.h | 130 ++++++++
>> .../testing/selftests/powerpc/ptrace/ptrace-pkey.c | 326 +++++++++++++++++++++
>
> This is failing on machines without pkeys:
>
> test: ptrace_pkey
> tags: git_version:52e7d87
> [FAIL] Test FAILED on line 117
> [FAIL] Test FAILED on line 191
> failure: ptrace_pkey
>
>
> I think the first fail is in the child here:
>
> int ptrace_read_regs(pid_t child, unsigned long type, unsigned long regs[],
> int n)
> {
> struct iovec iov;
> long ret;
>
> FAIL_IF(start_trace(child));
>
> iov.iov_base = regs;
> iov.iov_len = n * sizeof(unsigned long);
>
> ret = ptrace(PTRACE_GETREGSET, child, type, &iov);
> FAIL_IF(ret != 0);
>
>
> Which makes sense.
Yes, that is indeed what is going on.
> The test needs to skip if pkeys are not available/enabled. Using the
> availability of the REGSET might actually be a nice way to detect that,
> because it's read-only.
I forgot to consider the case of pkeys not available or not enabled,
sorry about that.
I just sent a v2 which implements your suggestion above.
--
Thiago Jung Bauermann
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] selftests/powerpc: Add ptrace tests for Protection Key registers
2018-05-25 2:14 ` Thiago Jung Bauermann
@ 2018-05-25 10:26 ` Michael Ellerman
0 siblings, 0 replies; 10+ messages in thread
From: Michael Ellerman @ 2018-05-25 10:26 UTC (permalink / raw)
To: Thiago Jung Bauermann
Cc: linuxppc-dev, linux-kselftest, linux-kernel, Ram Pai,
Thiago Jung Bauermann
Thiago Jung Bauermann <bauerman@linux.ibm.com> writes:
> Michael Ellerman <mpe@ellerman.id.au> writes:
>> Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> writes:
>>> tools/testing/selftests/powerpc/include/reg.h | 1 +
>>> tools/testing/selftests/powerpc/ptrace/Makefile | 5 +-
>>> tools/testing/selftests/powerpc/ptrace/child.h | 130 ++++++++
>>> .../testing/selftests/powerpc/ptrace/ptrace-pkey.c | 326 +++++++++++++++++++++
>>
>> This is failing on machines without pkeys:
>>
>> test: ptrace_pkey
>> tags: git_version:52e7d87
>> [FAIL] Test FAILED on line 117
>> [FAIL] Test FAILED on line 191
>> failure: ptrace_pkey
>>
>>
>> I think the first fail is in the child here:
>>
>> int ptrace_read_regs(pid_t child, unsigned long type, unsigned long regs[],
>> int n)
>> {
>> struct iovec iov;
>> long ret;
>>
>> FAIL_IF(start_trace(child));
>>
>> iov.iov_base = regs;
>> iov.iov_len = n * sizeof(unsigned long);
>>
>> ret = ptrace(PTRACE_GETREGSET, child, type, &iov);
>> FAIL_IF(ret != 0);
>>
>>
>> Which makes sense.
>
> Yes, that is indeed what is going on.
>
>> The test needs to skip if pkeys are not available/enabled. Using the
>> availability of the REGSET might actually be a nice way to detect that,
>> because it's read-only.
>
> I forgot to consider the case of pkeys not available or not enabled,
> sorry about that.
No worries.
> I just sent a v2 which implements your suggestion above.
Thanks.
cheers
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/2] selftests/powerpc: Add ptrace tests for Protection Key registers
2018-01-26 5:55 [PATCH 0/2] Testcases for protection keys feature in powerpc/next Thiago Jung Bauermann
@ 2018-01-26 5:55 ` Thiago Jung Bauermann
0 siblings, 0 replies; 10+ messages in thread
From: Thiago Jung Bauermann @ 2018-01-26 5:55 UTC (permalink / raw)
To: linuxppc-dev
Cc: linux-kselftest, linux-kernel, Michael Ellerman, Ram Pai,
Thiago Jung Bauermann
This test exercises read and write access to the AMR, IAMR and UAMOR.
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
---
tools/testing/selftests/powerpc/include/reg.h | 1 +
tools/testing/selftests/powerpc/ptrace/Makefile | 5 +-
tools/testing/selftests/powerpc/ptrace/child.h | 130 ++++++++
.../testing/selftests/powerpc/ptrace/ptrace-pkey.c | 326 +++++++++++++++++++++
tools/testing/selftests/powerpc/ptrace/ptrace.h | 37 +++
5 files changed, 498 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/powerpc/ptrace/child.h
create mode 100644 tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
diff --git a/tools/testing/selftests/powerpc/include/reg.h b/tools/testing/selftests/powerpc/include/reg.h
index 4afdebcce4cd..7f348c059bc2 100644
--- a/tools/testing/selftests/powerpc/include/reg.h
+++ b/tools/testing/selftests/powerpc/include/reg.h
@@ -54,6 +54,7 @@
#define SPRN_DSCR_PRIV 0x11 /* Privilege State DSCR */
#define SPRN_DSCR 0x03 /* Data Stream Control Register */
#define SPRN_PPR 896 /* Program Priority Register */
+#define SPRN_AMR 13 /* Authority Mask Register - problem state */
/* TEXASR register bits */
#define TEXASR_FC 0xFE00000000000000
diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile
index 480305266504..707ba734faf2 100644
--- a/tools/testing/selftests/powerpc/ptrace/Makefile
+++ b/tools/testing/selftests/powerpc/ptrace/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
TEST_PROGS := ptrace-gpr ptrace-tm-gpr ptrace-tm-spd-gpr \
ptrace-tar ptrace-tm-tar ptrace-tm-spd-tar ptrace-vsx ptrace-tm-vsx \
- ptrace-tm-spd-vsx ptrace-tm-spr
+ ptrace-tm-spd-vsx ptrace-tm-spr ptrace-pkey
include ../../lib.mk
@@ -9,6 +9,9 @@ all: $(TEST_PROGS)
CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm -fno-pie
+ptrace-pkey: ../harness.c ../utils.c ../lib/reg.S ptrace.h child.h ptrace-pkey.c
+ $(LINK.c) $^ $(LDLIBS) -pthread -o $@
+
$(TEST_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h
clean:
diff --git a/tools/testing/selftests/powerpc/ptrace/child.h b/tools/testing/selftests/powerpc/ptrace/child.h
new file mode 100644
index 000000000000..40c1a6d92111
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/child.h
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Helper functions to sync execution between parent and child processes.
+ *
+ * Copyright 2018, Thiago Jung Bauermann, IBM Corporation.
+ */
+#include <stdio.h>
+#include <stdbool.h>
+#include <semaphore.h>
+
+/*
+ * Information in a shared memory location for synchronization between child and
+ * parent.
+ */
+struct child_sync {
+ /* The parent waits on this semaphore. */
+ sem_t sem_parent;
+
+ /* If true, the child should give up as well. */
+ bool parent_gave_up;
+
+ /* The child waits on this semaphore. */
+ sem_t sem_child;
+
+ /* If true, the parent should give up as well. */
+ bool child_gave_up;
+};
+
+#define CHILD_FAIL_IF(x, sync) \
+ do { \
+ if (x) { \
+ fprintf(stderr, \
+ "[FAIL] Test FAILED on line %d\n", __LINE__); \
+ (sync)->child_gave_up = true; \
+ prod_parent(sync); \
+ return 1; \
+ } \
+ } while (0)
+
+#define PARENT_FAIL_IF(x, sync) \
+ do { \
+ if (x) { \
+ fprintf(stderr, \
+ "[FAIL] Test FAILED on line %d\n", __LINE__); \
+ (sync)->parent_gave_up = true; \
+ prod_child(sync); \
+ return 1; \
+ } \
+ } while (0)
+
+int init_child_sync(struct child_sync *sync)
+{
+ int ret;
+
+ ret = sem_init(&sync->sem_parent, 1, 0);
+ if (ret) {
+ perror("Semaphore initialization failed");
+ return 1;
+ }
+
+ ret = sem_init(&sync->sem_child, 1, 0);
+ if (ret) {
+ perror("Semaphore initialization failed");
+ return 1;
+ }
+
+ return 0;
+}
+
+void destroy_child_sync(struct child_sync *sync)
+{
+ sem_destroy(&sync->sem_parent);
+ sem_destroy(&sync->sem_child);
+}
+
+int wait_child(struct child_sync *sync)
+{
+ int ret;
+
+ /* Wait until the child prods us. */
+ ret = sem_wait(&sync->sem_parent);
+ if (ret) {
+ perror("Error waiting for child");
+ return 1;
+ }
+
+ return sync->child_gave_up;
+}
+
+int prod_child(struct child_sync *sync)
+{
+ int ret;
+
+ /* Unblock the child now. */
+ ret = sem_post(&sync->sem_child);
+ if (ret) {
+ perror("Error prodding child");
+ return 1;
+ }
+
+ return 0;
+}
+
+int wait_parent(struct child_sync *sync)
+{
+ int ret;
+
+ /* Wait until the parent prods us. */
+ ret = sem_wait(&sync->sem_child);
+ if (ret) {
+ perror("Error waiting for parent");
+ return 1;
+ }
+
+ return sync->parent_gave_up;
+}
+
+int prod_parent(struct child_sync *sync)
+{
+ int ret;
+
+ /* Unblock the parent now. */
+ ret = sem_post(&sync->sem_parent);
+ if (ret) {
+ perror("Error prodding parent");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
new file mode 100644
index 000000000000..8332b9338d39
--- /dev/null
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Ptrace test for Memory Protection Key registers
+ *
+ * Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
+ * Copyright (C) 2018 IBM Corporation.
+ */
+#include "ptrace.h"
+#include "child.h"
+
+#ifndef __NR_pkey_alloc
+#define __NR_pkey_alloc 384
+#endif
+
+#ifndef __NR_pkey_free
+#define __NR_pkey_free 385
+#endif
+
+#ifndef NT_PPC_PKEY
+#define NT_PPC_PKEY 0x110
+#endif
+
+#ifndef PKEY_DISABLE_EXECUTE
+#define PKEY_DISABLE_EXECUTE 0x4
+#endif
+
+#define AMR_BITS_PER_PKEY 2
+#define PKEY_REG_BITS (sizeof(u64) * 8)
+#define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY))
+
+static const char user_read[] = "[User Read (Running)]";
+static const char user_write[] = "[User Write (Running)]";
+static const char ptrace_read_running[] = "[Ptrace Read (Running)]";
+static const char ptrace_write_running[] = "[Ptrace Write (Running)]";
+
+/* Information shared between the parent and the child. */
+struct shared_info {
+ struct child_sync child_sync;
+
+ /* AMR value the parent expects to read from the child. */
+ unsigned long amr1;
+
+ /* AMR value the parent is expected to write to the child. */
+ unsigned long amr2;
+
+ /* AMR value that ptrace should refuse to write to the child. */
+ unsigned long amr3;
+
+ /* IAMR value the parent expects to read from the child. */
+ unsigned long expected_iamr;
+
+ /* UAMOR value the parent expects to read from the child. */
+ unsigned long expected_uamor;
+
+ /*
+ * IAMR and UAMOR values that ptrace should refuse to write to the child
+ * (even though they're valid ones) because userspace doesn't have
+ * access to those registers.
+ */
+ unsigned long new_iamr;
+ unsigned long new_uamor;
+};
+
+static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights)
+{
+ return syscall(__NR_pkey_alloc, flags, init_access_rights);
+}
+
+static int sys_pkey_free(int pkey)
+{
+ return syscall(__NR_pkey_free, pkey);
+}
+
+static int child(struct shared_info *info)
+{
+ unsigned long reg;
+ bool disable_execute = true;
+ int pkey1, pkey2, pkey3;
+ int ret;
+
+ /* Wait until parent fills out the initial register values. */
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ /* Get some pkeys so that we can change their bits in the AMR. */
+ pkey1 = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
+ if (pkey1 < 0) {
+ pkey1 = sys_pkey_alloc(0, 0);
+ CHILD_FAIL_IF(pkey1 < 0, &info->child_sync);
+
+ disable_execute = false;
+ }
+
+ pkey2 = sys_pkey_alloc(0, 0);
+ CHILD_FAIL_IF(pkey2 < 0, &info->child_sync);
+
+ pkey3 = sys_pkey_alloc(0, 0);
+ CHILD_FAIL_IF(pkey3 < 0, &info->child_sync);
+
+ info->amr1 |= 3ul << pkeyshift(pkey1);
+ info->amr2 |= 3ul << pkeyshift(pkey2);
+ info->amr3 |= info->amr2 | 3ul << pkeyshift(pkey3);
+
+ if (disable_execute)
+ info->expected_iamr |= 1ul << pkeyshift(pkey1);
+
+ info->expected_uamor |= 3ul << pkeyshift(pkey1) |
+ 3ul << pkeyshift(pkey2);
+ info->new_iamr |= 1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2);
+ info->new_uamor |= 3ul << pkeyshift(pkey1);
+
+ /*
+ * We won't use pkey3. We just want a plausible but invalid key to test
+ * whether ptrace will let us write to AMR bits we are not supposed to.
+ *
+ * This also tests whether the kernel restores the UAMOR permissions
+ * after a key is freed.
+ */
+ sys_pkey_free(pkey3);
+
+ printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n",
+ user_write, info->amr1, pkey1, pkey2, pkey3);
+
+ mtspr(SPRN_AMR, info->amr1);
+
+ /* Wait for parent to read our AMR value and write a new one. */
+ ret = prod_parent(&info->child_sync);
+ CHILD_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ reg = mfspr(SPRN_AMR);
+
+ printf("%-30s AMR: %016lx\n", user_read, reg);
+
+ CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
+
+ /*
+ * Wait for parent to try to write an invalid AMR value.
+ */
+ ret = prod_parent(&info->child_sync);
+ CHILD_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ reg = mfspr(SPRN_AMR);
+
+ printf("%-30s AMR: %016lx\n", user_read, reg);
+
+ CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
+
+ /*
+ * Wait for parent to try to write an IAMR and a UAMOR value. We can't
+ * verify them, but we can verify that the AMR didn't change.
+ */
+ ret = prod_parent(&info->child_sync);
+ CHILD_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_parent(&info->child_sync);
+ if (ret)
+ return ret;
+
+ reg = mfspr(SPRN_AMR);
+
+ printf("%-30s AMR: %016lx\n", user_read, reg);
+
+ CHILD_FAIL_IF(reg != info->amr2, &info->child_sync);
+
+ /* Now let parent now that we are finished. */
+
+ ret = prod_parent(&info->child_sync);
+ CHILD_FAIL_IF(ret, &info->child_sync);
+
+ return TEST_PASS;
+}
+
+static int parent(struct shared_info *info, pid_t pid)
+{
+ unsigned long regs[3];
+ int ret, status;
+
+ /*
+ * Get the initial values for AMR, IAMR and UAMOR and communicate them
+ * to the child.
+ */
+ ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ info->amr1 = info->amr2 = info->amr3 = regs[0];
+ info->expected_iamr = info->new_iamr = regs[1];
+ info->expected_uamor = info->new_uamor = regs[2];
+
+ /* Wake up child so that it can set itself up. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_child(&info->child_sync);
+ if (ret)
+ return ret;
+
+ /* Verify that we can read the pkey registers from the child. */
+ ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
+ ptrace_read_running, regs[0], regs[1], regs[2]);
+
+ PARENT_FAIL_IF(regs[0] != info->amr1, &info->child_sync);
+ PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);
+ PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);
+
+ /* Write valid AMR value in child. */
+ ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr2, 1);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr2);
+
+ /* Wake up child so that it can verify it changed. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_child(&info->child_sync);
+ if (ret)
+ return ret;
+
+ /* Write invalid AMR value in child. */
+ ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr3, 1);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr3);
+
+ /* Wake up child so that it can verify it didn't change. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait_child(&info->child_sync);
+ if (ret)
+ return ret;
+
+ /* Try to write to IAMR. */
+ regs[0] = info->amr1;
+ regs[1] = info->new_iamr;
+ ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 2);
+ PARENT_FAIL_IF(!ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx IAMR: %016lx\n",
+ ptrace_write_running, regs[0], regs[1]);
+
+ /* Try to write to IAMR and UAMOR. */
+ regs[2] = info->new_uamor;
+ ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_FAIL_IF(!ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
+ ptrace_write_running, regs[0], regs[1], regs[2]);
+
+ /* Verify that all registers still have their expected values. */
+ ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ printf("%-30s AMR: %016lx IAMR: %016lx UAMOR: %016lx\n",
+ ptrace_read_running, regs[0], regs[1], regs[2]);
+
+ PARENT_FAIL_IF(regs[0] != info->amr2, &info->child_sync);
+ PARENT_FAIL_IF(regs[1] != info->expected_iamr, &info->child_sync);
+ PARENT_FAIL_IF(regs[2] != info->expected_uamor, &info->child_sync);
+
+ /* Wake up child so that it can verify AMR didn't change and wrap up. */
+ ret = prod_child(&info->child_sync);
+ PARENT_FAIL_IF(ret, &info->child_sync);
+
+ ret = wait(&status);
+ if (ret != pid) {
+ printf("Child's exit status not captured\n");
+ ret = TEST_PASS;
+ } else if (!WIFEXITED(status)) {
+ printf("Child exited abnormally\n");
+ ret = TEST_FAIL;
+ } else
+ ret = WEXITSTATUS(status) ? TEST_FAIL : TEST_PASS;
+
+ return ret;
+}
+
+static int ptrace_pkey(void)
+{
+ struct shared_info *info;
+ int shm_id;
+ int ret;
+ pid_t pid;
+
+ shm_id = shmget(IPC_PRIVATE, sizeof(*info), 0777 | IPC_CREAT);
+ info = shmat(shm_id, NULL, 0);
+
+ ret = init_child_sync(&info->child_sync);
+ if (ret)
+ return ret;
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork() failed");
+ ret = TEST_FAIL;
+ } else if (pid == 0)
+ ret = child(info);
+ else
+ ret = parent(info, pid);
+
+ shmdt(info);
+
+ if (pid) {
+ destroy_child_sync(&info->child_sync);
+ shmctl(shm_id, IPC_RMID, NULL);
+ }
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(ptrace_pkey, "ptrace_pkey");
+}
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace.h b/tools/testing/selftests/powerpc/ptrace/ptrace.h
index 19fb825270a1..d2c9c4c2b5ee 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace.h
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace.h
@@ -102,6 +102,43 @@ int cont_trace(pid_t child)
return TEST_PASS;
}
+int ptrace_read_regs(pid_t child, unsigned long type, unsigned long regs[],
+ int n)
+{
+ struct iovec iov;
+ long ret;
+
+ FAIL_IF(start_trace(child));
+
+ iov.iov_base = regs;
+ iov.iov_len = n * sizeof(unsigned long);
+
+ ret = ptrace(PTRACE_GETREGSET, child, type, &iov);
+ FAIL_IF(ret != 0);
+
+ FAIL_IF(stop_trace(child));
+
+ return TEST_PASS;
+}
+
+long ptrace_write_regs(pid_t child, unsigned long type, unsigned long regs[],
+ int n)
+{
+ struct iovec iov;
+ long ret;
+
+ FAIL_IF(start_trace(child));
+
+ iov.iov_base = regs;
+ iov.iov_len = n * sizeof(unsigned long);
+
+ ret = ptrace(PTRACE_SETREGSET, child, type, &iov);
+
+ FAIL_IF(stop_trace(child));
+
+ return ret;
+}
+
/* TAR, PPR, DSCR */
int show_tar_registers(pid_t child, unsigned long *out)
{
^ permalink raw reply related [flat|nested] 10+ messages in thread
end of thread, other threads:[~2018-05-25 10:26 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-02-23 18:33 [PATCH 1/2] selftests/powerpc: Add ptrace tests for Protection Key registers Thiago Jung Bauermann
2018-02-23 18:33 ` [PATCH 2/2] selftests/powerpc: Add core file test " Thiago Jung Bauermann
2018-02-23 18:51 ` Ram Pai
2018-05-17 14:03 ` Michael Ellerman
2018-02-23 18:50 ` [PATCH 1/2] selftests/powerpc: Add ptrace tests " Ram Pai
2018-03-27 17:35 ` Thiago Jung Bauermann
2018-05-17 14:03 ` Michael Ellerman
2018-05-25 2:14 ` Thiago Jung Bauermann
2018-05-25 10:26 ` Michael Ellerman
-- strict thread matches above, loose matches on Subject: below --
2018-01-26 5:55 [PATCH 0/2] Testcases for protection keys feature in powerpc/next Thiago Jung Bauermann
2018-01-26 5:55 ` [PATCH 1/2] selftests/powerpc: Add ptrace tests for Protection Key registers Thiago Jung Bauermann
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).