* [PATCH v2 2/2] riscv: selftests: Add a ptrace test to verify syscall parameter modification
@ 2024-12-03 9:30 ` Celeste Liu
0 siblings, 0 replies; 22+ messages in thread
From: Celeste Liu @ 2024-12-03 9:30 UTC (permalink / raw)
To: Oleg Nesterov, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Eric Biederman, Kees Cook, Shuah Khan
Cc: Alexandre Ghiti, Dmitry V. Levin, Andrea Bolognani,
Björn Töpel, Thomas Gleixner, Ron Economos,
Charlie Jenkins, Quan Zhou, Felix Yan, Ruizhe Pan, Shiqi Zhang,
Guo Ren, Yao Zi, Han Gao, linux-riscv, linux-kernel, linux-mm,
stable, linux-kselftest, Celeste Liu
From: Charlie Jenkins <charlie@rivosinc.com>
This test checks that orig_a0 allows a syscall argument to be modified,
and that changing a0 does not change the syscall argument.
Cc: stable@vger.kernel.org
Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
Co-developed-by: Celeste Liu <uwu@coelacanthus.name>
Signed-off-by: Celeste Liu <uwu@coelacanthus.name>
Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
---
tools/testing/selftests/riscv/abi/.gitignore | 1 +
tools/testing/selftests/riscv/abi/Makefile | 5 +-
tools/testing/selftests/riscv/abi/ptrace.c | 134 +++++++++++++++++++++++++++
3 files changed, 139 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/riscv/abi/.gitignore b/tools/testing/selftests/riscv/abi/.gitignore
index b38358f91c4d2240ae64892871d9ca98bda1ae58..378c605919a3b3d58eec2701eb7af430cfe315d6 100644
--- a/tools/testing/selftests/riscv/abi/.gitignore
+++ b/tools/testing/selftests/riscv/abi/.gitignore
@@ -1 +1,2 @@
pointer_masking
+ptrace
diff --git a/tools/testing/selftests/riscv/abi/Makefile b/tools/testing/selftests/riscv/abi/Makefile
index ed82ff9c664e7eb3f760cbab81fb957ff72579c5..3f74d059dfdcbce4d45d8ff618781ccea1419061 100644
--- a/tools/testing/selftests/riscv/abi/Makefile
+++ b/tools/testing/selftests/riscv/abi/Makefile
@@ -2,9 +2,12 @@
CFLAGS += -I$(top_srcdir)/tools/include
-TEST_GEN_PROGS := pointer_masking
+TEST_GEN_PROGS := pointer_masking ptrace
include ../../lib.mk
$(OUTPUT)/pointer_masking: pointer_masking.c
$(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
+
+$(OUTPUT)/ptrace: ptrace.c
+ $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
diff --git a/tools/testing/selftests/riscv/abi/ptrace.c b/tools/testing/selftests/riscv/abi/ptrace.c
new file mode 100644
index 0000000000000000000000000000000000000000..d192764b428d1f1c442f3957c6fedeb01a48d556
--- /dev/null
+++ b/tools/testing/selftests/riscv/abi/ptrace.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <linux/elf.h>
+#include <linux/unistd.h>
+#include <asm/ptrace.h>
+
+#include "../../kselftest_harness.h"
+
+#define ORIG_A0_MODIFY 0x01
+#define A0_MODIFY 0x02
+#define A0_OLD 0x03
+#define A0_NEW 0x04
+
+#define perr_and_exit(fmt, ...) \
+ ({ \
+ char buf[256]; \
+ snprintf(buf, sizeof(buf), "%s:%d:" fmt ": %m\n", \
+ __func__, __LINE__, ##__VA_ARGS__); \
+ perror(buf); \
+ exit(-1); \
+ })
+
+static inline void resume_and_wait_tracee(pid_t pid, int flag)
+{
+ int status;
+
+ if (ptrace(flag, pid, 0, 0))
+ perr_and_exit("failed to resume the tracee %d\n", pid);
+
+ if (waitpid(pid, &status, 0) != pid)
+ perr_and_exit("failed to wait for the tracee %d\n", pid);
+}
+
+static void ptrace_test(int opt, int *result)
+{
+ int status;
+ pid_t pid;
+ struct user_regs_struct regs;
+ struct iovec iov = {
+ .iov_base = ®s,
+ .iov_len = sizeof(regs),
+ };
+
+ unsigned long orig_a0;
+ struct iovec a0_iov = {
+ .iov_base = &orig_a0,
+ .iov_len = sizeof(orig_a0),
+ };
+
+ pid = fork();
+ if (pid == 0) {
+ /* Mark oneself being traced */
+ long val = ptrace(PTRACE_TRACEME, 0, 0, 0);
+
+ if (val)
+ perr_and_exit("failed to request for tracer to trace me: %ld\n", val);
+
+ kill(getpid(), SIGSTOP);
+
+ /* Perform exit syscall that will be intercepted */
+ exit(A0_OLD);
+ }
+
+ if (pid < 0)
+ exit(1);
+
+ if (waitpid(pid, &status, 0) != pid)
+ perr_and_exit("failed to wait for the tracee %d\n", pid);
+
+ /* Stop at the entry point of the syscall */
+ resume_and_wait_tracee(pid, PTRACE_SYSCALL);
+
+ /* Check tracee regs before the syscall */
+ if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))
+ perr_and_exit("failed to get tracee registers\n");
+ if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
+ perr_and_exit("failed to get tracee registers\n");
+ if (orig_a0 != A0_OLD)
+ perr_and_exit("unexpected orig_a0: 0x%lx\n", orig_a0);
+
+ /* Modify a0/orig_a0 for the syscall */
+ switch (opt) {
+ case A0_MODIFY:
+ regs.a0 = A0_NEW;
+ break;
+ case ORIG_A0_MODIFY:
+ orig_a0 = A0_NEW;
+ break;
+ }
+
+ if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
+ perr_and_exit("failed to set tracee registers\n");
+
+ /* Resume the tracee */
+ ptrace(PTRACE_CONT, pid, 0, 0);
+ if (waitpid(pid, &status, 0) != pid)
+ perr_and_exit("failed to wait for the tracee\n");
+
+ *result = WEXITSTATUS(status);
+}
+
+TEST(ptrace_modify_a0)
+{
+ int result;
+
+ ptrace_test(A0_MODIFY, &result);
+
+ /* The modification of a0 cannot affect the first argument of the syscall */
+ EXPECT_EQ(A0_OLD, result);
+}
+
+TEST(ptrace_modify_orig_a0)
+{
+ int result;
+
+ ptrace_test(ORIG_A0_MODIFY, &result);
+
+ /* Only modify orig_a0 to change the first argument of the syscall */
+ EXPECT_EQ(A0_NEW, result);
+}
+
+TEST_HARNESS_MAIN
--
2.47.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v2 2/2] riscv: selftests: Add a ptrace test to verify syscall parameter modification
2024-12-03 9:30 ` Celeste Liu
@ 2024-12-03 11:38 ` Björn Töpel
-1 siblings, 0 replies; 22+ messages in thread
From: Björn Töpel @ 2024-12-03 11:38 UTC (permalink / raw)
To: Celeste Liu, Oleg Nesterov, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Eric Biederman, Kees Cook, Shuah Khan
Cc: Alexandre Ghiti, Dmitry V. Levin, Andrea Bolognani,
Thomas Gleixner, Ron Economos, Charlie Jenkins, Quan Zhou,
Felix Yan, Ruizhe Pan, Shiqi Zhang, Guo Ren, Yao Zi, Han Gao,
linux-riscv, linux-kernel, linux-mm, stable, linux-kselftest,
Celeste Liu
Celeste Liu <uwu@coelacanthus.name> writes:
> From: Charlie Jenkins <charlie@rivosinc.com>
>
> This test checks that orig_a0 allows a syscall argument to be modified,
> and that changing a0 does not change the syscall argument.
>
> Cc: stable@vger.kernel.org
> Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
> Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
> Co-developed-by: Celeste Liu <uwu@coelacanthus.name>
> Signed-off-by: Celeste Liu <uwu@coelacanthus.name>
> Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
Nice with a selftest! (Don't think the Cc: stable applies... ;-))
Reviewed-by: Björn Töpel <bjorn@rivosinc.com>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 2/2] riscv: selftests: Add a ptrace test to verify syscall parameter modification
@ 2024-12-03 11:38 ` Björn Töpel
0 siblings, 0 replies; 22+ messages in thread
From: Björn Töpel @ 2024-12-03 11:38 UTC (permalink / raw)
To: Celeste Liu, Oleg Nesterov, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Eric Biederman, Kees Cook, Shuah Khan
Cc: Alexandre Ghiti, Dmitry V. Levin, Andrea Bolognani,
Thomas Gleixner, Ron Economos, Charlie Jenkins, Quan Zhou,
Felix Yan, Ruizhe Pan, Shiqi Zhang, Guo Ren, Yao Zi, Han Gao,
linux-riscv, linux-kernel, linux-mm, stable, linux-kselftest,
Celeste Liu
Celeste Liu <uwu@coelacanthus.name> writes:
> From: Charlie Jenkins <charlie@rivosinc.com>
>
> This test checks that orig_a0 allows a syscall argument to be modified,
> and that changing a0 does not change the syscall argument.
>
> Cc: stable@vger.kernel.org
> Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
> Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
> Co-developed-by: Celeste Liu <uwu@coelacanthus.name>
> Signed-off-by: Celeste Liu <uwu@coelacanthus.name>
> Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
Nice with a selftest! (Don't think the Cc: stable applies... ;-))
Reviewed-by: Björn Töpel <bjorn@rivosinc.com>
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 2/2] riscv: selftests: Add a ptrace test to verify syscall parameter modification
2024-12-03 9:30 ` Celeste Liu
@ 2024-12-03 12:55 ` Andrew Jones
-1 siblings, 0 replies; 22+ messages in thread
From: Andrew Jones @ 2024-12-03 12:55 UTC (permalink / raw)
To: Celeste Liu
Cc: Oleg Nesterov, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Eric Biederman, Kees Cook, Shuah Khan, Alexandre Ghiti,
Dmitry V. Levin, Andrea Bolognani, Björn Töpel,
Thomas Gleixner, Ron Economos, Charlie Jenkins, Quan Zhou,
Felix Yan, Ruizhe Pan, Shiqi Zhang, Guo Ren, Yao Zi, Han Gao,
linux-riscv, linux-kernel, linux-mm, stable, linux-kselftest
On Tue, Dec 03, 2024 at 05:30:05PM +0800, Celeste Liu wrote:
> From: Charlie Jenkins <charlie@rivosinc.com>
>
> This test checks that orig_a0 allows a syscall argument to be modified,
> and that changing a0 does not change the syscall argument.
>
> Cc: stable@vger.kernel.org
> Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
> Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
> Co-developed-by: Celeste Liu <uwu@coelacanthus.name>
> Signed-off-by: Celeste Liu <uwu@coelacanthus.name>
> Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
> ---
> tools/testing/selftests/riscv/abi/.gitignore | 1 +
> tools/testing/selftests/riscv/abi/Makefile | 5 +-
> tools/testing/selftests/riscv/abi/ptrace.c | 134 +++++++++++++++++++++++++++
> 3 files changed, 139 insertions(+), 1 deletion(-)
>
> diff --git a/tools/testing/selftests/riscv/abi/.gitignore b/tools/testing/selftests/riscv/abi/.gitignore
> index b38358f91c4d2240ae64892871d9ca98bda1ae58..378c605919a3b3d58eec2701eb7af430cfe315d6 100644
> --- a/tools/testing/selftests/riscv/abi/.gitignore
> +++ b/tools/testing/selftests/riscv/abi/.gitignore
> @@ -1 +1,2 @@
> pointer_masking
> +ptrace
> diff --git a/tools/testing/selftests/riscv/abi/Makefile b/tools/testing/selftests/riscv/abi/Makefile
> index ed82ff9c664e7eb3f760cbab81fb957ff72579c5..3f74d059dfdcbce4d45d8ff618781ccea1419061 100644
> --- a/tools/testing/selftests/riscv/abi/Makefile
> +++ b/tools/testing/selftests/riscv/abi/Makefile
> @@ -2,9 +2,12 @@
>
> CFLAGS += -I$(top_srcdir)/tools/include
>
> -TEST_GEN_PROGS := pointer_masking
> +TEST_GEN_PROGS := pointer_masking ptrace
>
> include ../../lib.mk
>
> $(OUTPUT)/pointer_masking: pointer_masking.c
> $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> +
> +$(OUTPUT)/ptrace: ptrace.c
> + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> diff --git a/tools/testing/selftests/riscv/abi/ptrace.c b/tools/testing/selftests/riscv/abi/ptrace.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..d192764b428d1f1c442f3957c6fedeb01a48d556
> --- /dev/null
> +++ b/tools/testing/selftests/riscv/abi/ptrace.c
> @@ -0,0 +1,134 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <signal.h>
> +#include <errno.h>
> +#include <sys/types.h>
> +#include <sys/ptrace.h>
> +#include <sys/stat.h>
> +#include <sys/user.h>
> +#include <sys/wait.h>
> +#include <sys/uio.h>
> +#include <linux/elf.h>
> +#include <linux/unistd.h>
> +#include <asm/ptrace.h>
> +
> +#include "../../kselftest_harness.h"
> +
> +#define ORIG_A0_MODIFY 0x01
> +#define A0_MODIFY 0x02
> +#define A0_OLD 0x03
> +#define A0_NEW 0x04
Shouldn't A0_OLD and A0_NEW set more bits, since 3 and 4 aren't very
unique (we could have those values by accident)? And should we include
setting bits over 31 for 64-bit targets?
> +
> +#define perr_and_exit(fmt, ...) \
> + ({ \
> + char buf[256]; \
> + snprintf(buf, sizeof(buf), "%s:%d:" fmt ": %m\n", \
> + __func__, __LINE__, ##__VA_ARGS__); \
> + perror(buf); \
> + exit(-1); \
> + })
Can we use e.g. ksft_exit_fail_perror() instead? I'd prefer we try to
consolidate testing/selftests/riscv/* tests on a common format for
errors and exit codes and we're already using other kselftest stuff.
> +
> +static inline void resume_and_wait_tracee(pid_t pid, int flag)
> +{
> + int status;
> +
> + if (ptrace(flag, pid, 0, 0))
> + perr_and_exit("failed to resume the tracee %d\n", pid);
> +
> + if (waitpid(pid, &status, 0) != pid)
> + perr_and_exit("failed to wait for the tracee %d\n", pid);
> +}
> +
> +static void ptrace_test(int opt, int *result)
> +{
> + int status;
> + pid_t pid;
> + struct user_regs_struct regs;
> + struct iovec iov = {
> + .iov_base = ®s,
> + .iov_len = sizeof(regs),
> + };
> +
> + unsigned long orig_a0;
> + struct iovec a0_iov = {
> + .iov_base = &orig_a0,
> + .iov_len = sizeof(orig_a0),
> + };
> +
> + pid = fork();
> + if (pid == 0) {
> + /* Mark oneself being traced */
> + long val = ptrace(PTRACE_TRACEME, 0, 0, 0);
> +
> + if (val)
> + perr_and_exit("failed to request for tracer to trace me: %ld\n", val);
> +
> + kill(getpid(), SIGSTOP);
> +
> + /* Perform exit syscall that will be intercepted */
> + exit(A0_OLD);
> + }
> +
> + if (pid < 0)
> + exit(1);
This unexpected error condition deserves a message, so I'd use
ksft_exit_fail_perror() here.
> +
> + if (waitpid(pid, &status, 0) != pid)
> + perr_and_exit("failed to wait for the tracee %d\n", pid);
> +
> + /* Stop at the entry point of the syscall */
> + resume_and_wait_tracee(pid, PTRACE_SYSCALL);
> +
> + /* Check tracee regs before the syscall */
> + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))
> + perr_and_exit("failed to get tracee registers\n");
> + if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
> + perr_and_exit("failed to get tracee registers\n");
> + if (orig_a0 != A0_OLD)
> + perr_and_exit("unexpected orig_a0: 0x%lx\n", orig_a0);
> +
> + /* Modify a0/orig_a0 for the syscall */
> + switch (opt) {
> + case A0_MODIFY:
> + regs.a0 = A0_NEW;
> + break;
> + case ORIG_A0_MODIFY:
> + orig_a0 = A0_NEW;
> + break;
> + }
> +
> + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
> + perr_and_exit("failed to set tracee registers\n");
> +
> + /* Resume the tracee */
> + ptrace(PTRACE_CONT, pid, 0, 0);
> + if (waitpid(pid, &status, 0) != pid)
> + perr_and_exit("failed to wait for the tracee\n");
> +
> + *result = WEXITSTATUS(status);
> +}
> +
> +TEST(ptrace_modify_a0)
> +{
> + int result;
> +
> + ptrace_test(A0_MODIFY, &result);
> +
> + /* The modification of a0 cannot affect the first argument of the syscall */
> + EXPECT_EQ(A0_OLD, result);
What about checking that we actually set regs.a0 to A0_NEW? We'd need
A0_NEW to be more unique than 4, though.
> +}
> +
> +TEST(ptrace_modify_orig_a0)
> +{
> + int result;
> +
> + ptrace_test(ORIG_A0_MODIFY, &result);
> +
> + /* Only modify orig_a0 to change the first argument of the syscall */
If we run ptrace_modify_a0 first then we've already set regs.a0 to A0_NEW
and can't check with this test that we don't set it to A0_NEW. We should
probably have two different test values, one for regs.a0 and one for
orig_a0 and ensure on both tests that we aren't writing both.
> + EXPECT_EQ(A0_NEW, result);
> +}
> +
> +TEST_HARNESS_MAIN
>
> --
> 2.47.0
>
Thanks,
drew
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 2/2] riscv: selftests: Add a ptrace test to verify syscall parameter modification
@ 2024-12-03 12:55 ` Andrew Jones
0 siblings, 0 replies; 22+ messages in thread
From: Andrew Jones @ 2024-12-03 12:55 UTC (permalink / raw)
To: Celeste Liu
Cc: Oleg Nesterov, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Eric Biederman, Kees Cook, Shuah Khan, Alexandre Ghiti,
Dmitry V. Levin, Andrea Bolognani, Björn Töpel,
Thomas Gleixner, Ron Economos, Charlie Jenkins, Quan Zhou,
Felix Yan, Ruizhe Pan, Shiqi Zhang, Guo Ren, Yao Zi, Han Gao,
linux-riscv, linux-kernel, linux-mm, stable, linux-kselftest
On Tue, Dec 03, 2024 at 05:30:05PM +0800, Celeste Liu wrote:
> From: Charlie Jenkins <charlie@rivosinc.com>
>
> This test checks that orig_a0 allows a syscall argument to be modified,
> and that changing a0 does not change the syscall argument.
>
> Cc: stable@vger.kernel.org
> Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
> Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
> Co-developed-by: Celeste Liu <uwu@coelacanthus.name>
> Signed-off-by: Celeste Liu <uwu@coelacanthus.name>
> Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
> ---
> tools/testing/selftests/riscv/abi/.gitignore | 1 +
> tools/testing/selftests/riscv/abi/Makefile | 5 +-
> tools/testing/selftests/riscv/abi/ptrace.c | 134 +++++++++++++++++++++++++++
> 3 files changed, 139 insertions(+), 1 deletion(-)
>
> diff --git a/tools/testing/selftests/riscv/abi/.gitignore b/tools/testing/selftests/riscv/abi/.gitignore
> index b38358f91c4d2240ae64892871d9ca98bda1ae58..378c605919a3b3d58eec2701eb7af430cfe315d6 100644
> --- a/tools/testing/selftests/riscv/abi/.gitignore
> +++ b/tools/testing/selftests/riscv/abi/.gitignore
> @@ -1 +1,2 @@
> pointer_masking
> +ptrace
> diff --git a/tools/testing/selftests/riscv/abi/Makefile b/tools/testing/selftests/riscv/abi/Makefile
> index ed82ff9c664e7eb3f760cbab81fb957ff72579c5..3f74d059dfdcbce4d45d8ff618781ccea1419061 100644
> --- a/tools/testing/selftests/riscv/abi/Makefile
> +++ b/tools/testing/selftests/riscv/abi/Makefile
> @@ -2,9 +2,12 @@
>
> CFLAGS += -I$(top_srcdir)/tools/include
>
> -TEST_GEN_PROGS := pointer_masking
> +TEST_GEN_PROGS := pointer_masking ptrace
>
> include ../../lib.mk
>
> $(OUTPUT)/pointer_masking: pointer_masking.c
> $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> +
> +$(OUTPUT)/ptrace: ptrace.c
> + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> diff --git a/tools/testing/selftests/riscv/abi/ptrace.c b/tools/testing/selftests/riscv/abi/ptrace.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..d192764b428d1f1c442f3957c6fedeb01a48d556
> --- /dev/null
> +++ b/tools/testing/selftests/riscv/abi/ptrace.c
> @@ -0,0 +1,134 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <signal.h>
> +#include <errno.h>
> +#include <sys/types.h>
> +#include <sys/ptrace.h>
> +#include <sys/stat.h>
> +#include <sys/user.h>
> +#include <sys/wait.h>
> +#include <sys/uio.h>
> +#include <linux/elf.h>
> +#include <linux/unistd.h>
> +#include <asm/ptrace.h>
> +
> +#include "../../kselftest_harness.h"
> +
> +#define ORIG_A0_MODIFY 0x01
> +#define A0_MODIFY 0x02
> +#define A0_OLD 0x03
> +#define A0_NEW 0x04
Shouldn't A0_OLD and A0_NEW set more bits, since 3 and 4 aren't very
unique (we could have those values by accident)? And should we include
setting bits over 31 for 64-bit targets?
> +
> +#define perr_and_exit(fmt, ...) \
> + ({ \
> + char buf[256]; \
> + snprintf(buf, sizeof(buf), "%s:%d:" fmt ": %m\n", \
> + __func__, __LINE__, ##__VA_ARGS__); \
> + perror(buf); \
> + exit(-1); \
> + })
Can we use e.g. ksft_exit_fail_perror() instead? I'd prefer we try to
consolidate testing/selftests/riscv/* tests on a common format for
errors and exit codes and we're already using other kselftest stuff.
> +
> +static inline void resume_and_wait_tracee(pid_t pid, int flag)
> +{
> + int status;
> +
> + if (ptrace(flag, pid, 0, 0))
> + perr_and_exit("failed to resume the tracee %d\n", pid);
> +
> + if (waitpid(pid, &status, 0) != pid)
> + perr_and_exit("failed to wait for the tracee %d\n", pid);
> +}
> +
> +static void ptrace_test(int opt, int *result)
> +{
> + int status;
> + pid_t pid;
> + struct user_regs_struct regs;
> + struct iovec iov = {
> + .iov_base = ®s,
> + .iov_len = sizeof(regs),
> + };
> +
> + unsigned long orig_a0;
> + struct iovec a0_iov = {
> + .iov_base = &orig_a0,
> + .iov_len = sizeof(orig_a0),
> + };
> +
> + pid = fork();
> + if (pid == 0) {
> + /* Mark oneself being traced */
> + long val = ptrace(PTRACE_TRACEME, 0, 0, 0);
> +
> + if (val)
> + perr_and_exit("failed to request for tracer to trace me: %ld\n", val);
> +
> + kill(getpid(), SIGSTOP);
> +
> + /* Perform exit syscall that will be intercepted */
> + exit(A0_OLD);
> + }
> +
> + if (pid < 0)
> + exit(1);
This unexpected error condition deserves a message, so I'd use
ksft_exit_fail_perror() here.
> +
> + if (waitpid(pid, &status, 0) != pid)
> + perr_and_exit("failed to wait for the tracee %d\n", pid);
> +
> + /* Stop at the entry point of the syscall */
> + resume_and_wait_tracee(pid, PTRACE_SYSCALL);
> +
> + /* Check tracee regs before the syscall */
> + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))
> + perr_and_exit("failed to get tracee registers\n");
> + if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
> + perr_and_exit("failed to get tracee registers\n");
> + if (orig_a0 != A0_OLD)
> + perr_and_exit("unexpected orig_a0: 0x%lx\n", orig_a0);
> +
> + /* Modify a0/orig_a0 for the syscall */
> + switch (opt) {
> + case A0_MODIFY:
> + regs.a0 = A0_NEW;
> + break;
> + case ORIG_A0_MODIFY:
> + orig_a0 = A0_NEW;
> + break;
> + }
> +
> + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
> + perr_and_exit("failed to set tracee registers\n");
> +
> + /* Resume the tracee */
> + ptrace(PTRACE_CONT, pid, 0, 0);
> + if (waitpid(pid, &status, 0) != pid)
> + perr_and_exit("failed to wait for the tracee\n");
> +
> + *result = WEXITSTATUS(status);
> +}
> +
> +TEST(ptrace_modify_a0)
> +{
> + int result;
> +
> + ptrace_test(A0_MODIFY, &result);
> +
> + /* The modification of a0 cannot affect the first argument of the syscall */
> + EXPECT_EQ(A0_OLD, result);
What about checking that we actually set regs.a0 to A0_NEW? We'd need
A0_NEW to be more unique than 4, though.
> +}
> +
> +TEST(ptrace_modify_orig_a0)
> +{
> + int result;
> +
> + ptrace_test(ORIG_A0_MODIFY, &result);
> +
> + /* Only modify orig_a0 to change the first argument of the syscall */
If we run ptrace_modify_a0 first then we've already set regs.a0 to A0_NEW
and can't check with this test that we don't set it to A0_NEW. We should
probably have two different test values, one for regs.a0 and one for
orig_a0 and ensure on both tests that we aren't writing both.
> + EXPECT_EQ(A0_NEW, result);
> +}
> +
> +TEST_HARNESS_MAIN
>
> --
> 2.47.0
>
Thanks,
drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 2/2] riscv: selftests: Add a ptrace test to verify syscall parameter modification
2024-12-03 12:55 ` Andrew Jones
@ 2024-12-19 18:26 ` Charlie Jenkins
-1 siblings, 0 replies; 22+ messages in thread
From: Charlie Jenkins @ 2024-12-19 18:26 UTC (permalink / raw)
To: Andrew Jones
Cc: Celeste Liu, Oleg Nesterov, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Eric Biederman, Kees Cook, Shuah Khan, Alexandre Ghiti,
Dmitry V. Levin, Andrea Bolognani, Björn Töpel,
Thomas Gleixner, Ron Economos, Quan Zhou, Felix Yan, Ruizhe Pan,
Shiqi Zhang, Guo Ren, Yao Zi, Han Gao, linux-riscv, linux-kernel,
linux-mm, stable, linux-kselftest
On Tue, Dec 03, 2024 at 01:55:07PM +0100, Andrew Jones wrote:
> On Tue, Dec 03, 2024 at 05:30:05PM +0800, Celeste Liu wrote:
> > From: Charlie Jenkins <charlie@rivosinc.com>
> >
> > This test checks that orig_a0 allows a syscall argument to be modified,
> > and that changing a0 does not change the syscall argument.
> >
> > Cc: stable@vger.kernel.org
> > Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
> > Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
> > Co-developed-by: Celeste Liu <uwu@coelacanthus.name>
> > Signed-off-by: Celeste Liu <uwu@coelacanthus.name>
> > Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
> > ---
> > tools/testing/selftests/riscv/abi/.gitignore | 1 +
> > tools/testing/selftests/riscv/abi/Makefile | 5 +-
> > tools/testing/selftests/riscv/abi/ptrace.c | 134 +++++++++++++++++++++++++++
> > 3 files changed, 139 insertions(+), 1 deletion(-)
> >
> > diff --git a/tools/testing/selftests/riscv/abi/.gitignore b/tools/testing/selftests/riscv/abi/.gitignore
> > index b38358f91c4d2240ae64892871d9ca98bda1ae58..378c605919a3b3d58eec2701eb7af430cfe315d6 100644
> > --- a/tools/testing/selftests/riscv/abi/.gitignore
> > +++ b/tools/testing/selftests/riscv/abi/.gitignore
> > @@ -1 +1,2 @@
> > pointer_masking
> > +ptrace
> > diff --git a/tools/testing/selftests/riscv/abi/Makefile b/tools/testing/selftests/riscv/abi/Makefile
> > index ed82ff9c664e7eb3f760cbab81fb957ff72579c5..3f74d059dfdcbce4d45d8ff618781ccea1419061 100644
> > --- a/tools/testing/selftests/riscv/abi/Makefile
> > +++ b/tools/testing/selftests/riscv/abi/Makefile
> > @@ -2,9 +2,12 @@
> >
> > CFLAGS += -I$(top_srcdir)/tools/include
> >
> > -TEST_GEN_PROGS := pointer_masking
> > +TEST_GEN_PROGS := pointer_masking ptrace
> >
> > include ../../lib.mk
> >
> > $(OUTPUT)/pointer_masking: pointer_masking.c
> > $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> > +
> > +$(OUTPUT)/ptrace: ptrace.c
> > + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> > diff --git a/tools/testing/selftests/riscv/abi/ptrace.c b/tools/testing/selftests/riscv/abi/ptrace.c
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..d192764b428d1f1c442f3957c6fedeb01a48d556
> > --- /dev/null
> > +++ b/tools/testing/selftests/riscv/abi/ptrace.c
> > @@ -0,0 +1,134 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <unistd.h>
> > +#include <fcntl.h>
> > +#include <signal.h>
> > +#include <errno.h>
> > +#include <sys/types.h>
> > +#include <sys/ptrace.h>
> > +#include <sys/stat.h>
> > +#include <sys/user.h>
> > +#include <sys/wait.h>
> > +#include <sys/uio.h>
> > +#include <linux/elf.h>
> > +#include <linux/unistd.h>
> > +#include <asm/ptrace.h>
> > +
> > +#include "../../kselftest_harness.h"
> > +
> > +#define ORIG_A0_MODIFY 0x01
> > +#define A0_MODIFY 0x02
> > +#define A0_OLD 0x03
> > +#define A0_NEW 0x04
>
> Shouldn't A0_OLD and A0_NEW set more bits, since 3 and 4 aren't very
> unique (we could have those values by accident)? And should we include
> setting bits over 31 for 64-bit targets?
>
> > +
> > +#define perr_and_exit(fmt, ...) \
> > + ({ \
> > + char buf[256]; \
> > + snprintf(buf, sizeof(buf), "%s:%d:" fmt ": %m\n", \
> > + __func__, __LINE__, ##__VA_ARGS__); \
> > + perror(buf); \
> > + exit(-1); \
> > + })
>
> Can we use e.g. ksft_exit_fail_perror() instead? I'd prefer we try to
> consolidate testing/selftests/riscv/* tests on a common format for
> errors and exit codes and we're already using other kselftest stuff.
>
> > +
> > +static inline void resume_and_wait_tracee(pid_t pid, int flag)
> > +{
> > + int status;
> > +
> > + if (ptrace(flag, pid, 0, 0))
> > + perr_and_exit("failed to resume the tracee %d\n", pid);
> > +
> > + if (waitpid(pid, &status, 0) != pid)
> > + perr_and_exit("failed to wait for the tracee %d\n", pid);
> > +}
> > +
> > +static void ptrace_test(int opt, int *result)
> > +{
> > + int status;
> > + pid_t pid;
> > + struct user_regs_struct regs;
> > + struct iovec iov = {
> > + .iov_base = ®s,
> > + .iov_len = sizeof(regs),
> > + };
> > +
> > + unsigned long orig_a0;
> > + struct iovec a0_iov = {
> > + .iov_base = &orig_a0,
> > + .iov_len = sizeof(orig_a0),
> > + };
> > +
> > + pid = fork();
> > + if (pid == 0) {
> > + /* Mark oneself being traced */
> > + long val = ptrace(PTRACE_TRACEME, 0, 0, 0);
> > +
> > + if (val)
> > + perr_and_exit("failed to request for tracer to trace me: %ld\n", val);
> > +
> > + kill(getpid(), SIGSTOP);
> > +
> > + /* Perform exit syscall that will be intercepted */
> > + exit(A0_OLD);
> > + }
> > +
> > + if (pid < 0)
> > + exit(1);
>
> This unexpected error condition deserves a message, so I'd use
> ksft_exit_fail_perror() here.
>
> > +
> > + if (waitpid(pid, &status, 0) != pid)
> > + perr_and_exit("failed to wait for the tracee %d\n", pid);
> > +
> > + /* Stop at the entry point of the syscall */
> > + resume_and_wait_tracee(pid, PTRACE_SYSCALL);
> > +
> > + /* Check tracee regs before the syscall */
> > + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))
> > + perr_and_exit("failed to get tracee registers\n");
> > + if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
> > + perr_and_exit("failed to get tracee registers\n");
> > + if (orig_a0 != A0_OLD)
> > + perr_and_exit("unexpected orig_a0: 0x%lx\n", orig_a0);
> > +
> > + /* Modify a0/orig_a0 for the syscall */
> > + switch (opt) {
> > + case A0_MODIFY:
> > + regs.a0 = A0_NEW;
> > + break;
> > + case ORIG_A0_MODIFY:
> > + orig_a0 = A0_NEW;
> > + break;
> > + }
> > +
> > + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
> > + perr_and_exit("failed to set tracee registers\n");
> > +
> > + /* Resume the tracee */
> > + ptrace(PTRACE_CONT, pid, 0, 0);
> > + if (waitpid(pid, &status, 0) != pid)
> > + perr_and_exit("failed to wait for the tracee\n");
> > +
> > + *result = WEXITSTATUS(status);
> > +}
> > +
> > +TEST(ptrace_modify_a0)
> > +{
> > + int result;
> > +
> > + ptrace_test(A0_MODIFY, &result);
> > +
> > + /* The modification of a0 cannot affect the first argument of the syscall */
> > + EXPECT_EQ(A0_OLD, result);
>
> What about checking that we actually set regs.a0 to A0_NEW? We'd need
> A0_NEW to be more unique than 4, though.
>
> > +}
> > +
> > +TEST(ptrace_modify_orig_a0)
> > +{
> > + int result;
> > +
> > + ptrace_test(ORIG_A0_MODIFY, &result);
> > +
> > + /* Only modify orig_a0 to change the first argument of the syscall */
>
> If we run ptrace_modify_a0 first then we've already set regs.a0 to A0_NEW
> and can't check with this test that we don't set it to A0_NEW. We should
> probably have two different test values, one for regs.a0 and one for
> orig_a0 and ensure on both tests that we aren't writing both.
>
Celeste, do you want to fix this up or are you waiting for me to?
- Charlie
> > + EXPECT_EQ(A0_NEW, result);
> > +}
> > +
> > +TEST_HARNESS_MAIN
> >
> > --
> > 2.47.0
> >
>
> Thanks,
> drew
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 2/2] riscv: selftests: Add a ptrace test to verify syscall parameter modification
@ 2024-12-19 18:26 ` Charlie Jenkins
0 siblings, 0 replies; 22+ messages in thread
From: Charlie Jenkins @ 2024-12-19 18:26 UTC (permalink / raw)
To: Andrew Jones
Cc: Celeste Liu, Oleg Nesterov, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Eric Biederman, Kees Cook, Shuah Khan, Alexandre Ghiti,
Dmitry V. Levin, Andrea Bolognani, Björn Töpel,
Thomas Gleixner, Ron Economos, Quan Zhou, Felix Yan, Ruizhe Pan,
Shiqi Zhang, Guo Ren, Yao Zi, Han Gao, linux-riscv, linux-kernel,
linux-mm, stable, linux-kselftest
On Tue, Dec 03, 2024 at 01:55:07PM +0100, Andrew Jones wrote:
> On Tue, Dec 03, 2024 at 05:30:05PM +0800, Celeste Liu wrote:
> > From: Charlie Jenkins <charlie@rivosinc.com>
> >
> > This test checks that orig_a0 allows a syscall argument to be modified,
> > and that changing a0 does not change the syscall argument.
> >
> > Cc: stable@vger.kernel.org
> > Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
> > Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
> > Co-developed-by: Celeste Liu <uwu@coelacanthus.name>
> > Signed-off-by: Celeste Liu <uwu@coelacanthus.name>
> > Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
> > ---
> > tools/testing/selftests/riscv/abi/.gitignore | 1 +
> > tools/testing/selftests/riscv/abi/Makefile | 5 +-
> > tools/testing/selftests/riscv/abi/ptrace.c | 134 +++++++++++++++++++++++++++
> > 3 files changed, 139 insertions(+), 1 deletion(-)
> >
> > diff --git a/tools/testing/selftests/riscv/abi/.gitignore b/tools/testing/selftests/riscv/abi/.gitignore
> > index b38358f91c4d2240ae64892871d9ca98bda1ae58..378c605919a3b3d58eec2701eb7af430cfe315d6 100644
> > --- a/tools/testing/selftests/riscv/abi/.gitignore
> > +++ b/tools/testing/selftests/riscv/abi/.gitignore
> > @@ -1 +1,2 @@
> > pointer_masking
> > +ptrace
> > diff --git a/tools/testing/selftests/riscv/abi/Makefile b/tools/testing/selftests/riscv/abi/Makefile
> > index ed82ff9c664e7eb3f760cbab81fb957ff72579c5..3f74d059dfdcbce4d45d8ff618781ccea1419061 100644
> > --- a/tools/testing/selftests/riscv/abi/Makefile
> > +++ b/tools/testing/selftests/riscv/abi/Makefile
> > @@ -2,9 +2,12 @@
> >
> > CFLAGS += -I$(top_srcdir)/tools/include
> >
> > -TEST_GEN_PROGS := pointer_masking
> > +TEST_GEN_PROGS := pointer_masking ptrace
> >
> > include ../../lib.mk
> >
> > $(OUTPUT)/pointer_masking: pointer_masking.c
> > $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> > +
> > +$(OUTPUT)/ptrace: ptrace.c
> > + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> > diff --git a/tools/testing/selftests/riscv/abi/ptrace.c b/tools/testing/selftests/riscv/abi/ptrace.c
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..d192764b428d1f1c442f3957c6fedeb01a48d556
> > --- /dev/null
> > +++ b/tools/testing/selftests/riscv/abi/ptrace.c
> > @@ -0,0 +1,134 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <unistd.h>
> > +#include <fcntl.h>
> > +#include <signal.h>
> > +#include <errno.h>
> > +#include <sys/types.h>
> > +#include <sys/ptrace.h>
> > +#include <sys/stat.h>
> > +#include <sys/user.h>
> > +#include <sys/wait.h>
> > +#include <sys/uio.h>
> > +#include <linux/elf.h>
> > +#include <linux/unistd.h>
> > +#include <asm/ptrace.h>
> > +
> > +#include "../../kselftest_harness.h"
> > +
> > +#define ORIG_A0_MODIFY 0x01
> > +#define A0_MODIFY 0x02
> > +#define A0_OLD 0x03
> > +#define A0_NEW 0x04
>
> Shouldn't A0_OLD and A0_NEW set more bits, since 3 and 4 aren't very
> unique (we could have those values by accident)? And should we include
> setting bits over 31 for 64-bit targets?
>
> > +
> > +#define perr_and_exit(fmt, ...) \
> > + ({ \
> > + char buf[256]; \
> > + snprintf(buf, sizeof(buf), "%s:%d:" fmt ": %m\n", \
> > + __func__, __LINE__, ##__VA_ARGS__); \
> > + perror(buf); \
> > + exit(-1); \
> > + })
>
> Can we use e.g. ksft_exit_fail_perror() instead? I'd prefer we try to
> consolidate testing/selftests/riscv/* tests on a common format for
> errors and exit codes and we're already using other kselftest stuff.
>
> > +
> > +static inline void resume_and_wait_tracee(pid_t pid, int flag)
> > +{
> > + int status;
> > +
> > + if (ptrace(flag, pid, 0, 0))
> > + perr_and_exit("failed to resume the tracee %d\n", pid);
> > +
> > + if (waitpid(pid, &status, 0) != pid)
> > + perr_and_exit("failed to wait for the tracee %d\n", pid);
> > +}
> > +
> > +static void ptrace_test(int opt, int *result)
> > +{
> > + int status;
> > + pid_t pid;
> > + struct user_regs_struct regs;
> > + struct iovec iov = {
> > + .iov_base = ®s,
> > + .iov_len = sizeof(regs),
> > + };
> > +
> > + unsigned long orig_a0;
> > + struct iovec a0_iov = {
> > + .iov_base = &orig_a0,
> > + .iov_len = sizeof(orig_a0),
> > + };
> > +
> > + pid = fork();
> > + if (pid == 0) {
> > + /* Mark oneself being traced */
> > + long val = ptrace(PTRACE_TRACEME, 0, 0, 0);
> > +
> > + if (val)
> > + perr_and_exit("failed to request for tracer to trace me: %ld\n", val);
> > +
> > + kill(getpid(), SIGSTOP);
> > +
> > + /* Perform exit syscall that will be intercepted */
> > + exit(A0_OLD);
> > + }
> > +
> > + if (pid < 0)
> > + exit(1);
>
> This unexpected error condition deserves a message, so I'd use
> ksft_exit_fail_perror() here.
>
> > +
> > + if (waitpid(pid, &status, 0) != pid)
> > + perr_and_exit("failed to wait for the tracee %d\n", pid);
> > +
> > + /* Stop at the entry point of the syscall */
> > + resume_and_wait_tracee(pid, PTRACE_SYSCALL);
> > +
> > + /* Check tracee regs before the syscall */
> > + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))
> > + perr_and_exit("failed to get tracee registers\n");
> > + if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
> > + perr_and_exit("failed to get tracee registers\n");
> > + if (orig_a0 != A0_OLD)
> > + perr_and_exit("unexpected orig_a0: 0x%lx\n", orig_a0);
> > +
> > + /* Modify a0/orig_a0 for the syscall */
> > + switch (opt) {
> > + case A0_MODIFY:
> > + regs.a0 = A0_NEW;
> > + break;
> > + case ORIG_A0_MODIFY:
> > + orig_a0 = A0_NEW;
> > + break;
> > + }
> > +
> > + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
> > + perr_and_exit("failed to set tracee registers\n");
> > +
> > + /* Resume the tracee */
> > + ptrace(PTRACE_CONT, pid, 0, 0);
> > + if (waitpid(pid, &status, 0) != pid)
> > + perr_and_exit("failed to wait for the tracee\n");
> > +
> > + *result = WEXITSTATUS(status);
> > +}
> > +
> > +TEST(ptrace_modify_a0)
> > +{
> > + int result;
> > +
> > + ptrace_test(A0_MODIFY, &result);
> > +
> > + /* The modification of a0 cannot affect the first argument of the syscall */
> > + EXPECT_EQ(A0_OLD, result);
>
> What about checking that we actually set regs.a0 to A0_NEW? We'd need
> A0_NEW to be more unique than 4, though.
>
> > +}
> > +
> > +TEST(ptrace_modify_orig_a0)
> > +{
> > + int result;
> > +
> > + ptrace_test(ORIG_A0_MODIFY, &result);
> > +
> > + /* Only modify orig_a0 to change the first argument of the syscall */
>
> If we run ptrace_modify_a0 first then we've already set regs.a0 to A0_NEW
> and can't check with this test that we don't set it to A0_NEW. We should
> probably have two different test values, one for regs.a0 and one for
> orig_a0 and ensure on both tests that we aren't writing both.
>
Celeste, do you want to fix this up or are you waiting for me to?
- Charlie
> > + EXPECT_EQ(A0_NEW, result);
> > +}
> > +
> > +TEST_HARNESS_MAIN
> >
> > --
> > 2.47.0
> >
>
> Thanks,
> drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 2/2] riscv: selftests: Add a ptrace test to verify syscall parameter modification
2024-12-19 18:26 ` Charlie Jenkins
@ 2024-12-19 21:29 ` Celeste Liu
-1 siblings, 0 replies; 22+ messages in thread
From: Celeste Liu @ 2024-12-19 21:29 UTC (permalink / raw)
To: Charlie Jenkins, Andrew Jones
Cc: Oleg Nesterov, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Eric Biederman, Kees Cook, Shuah Khan, Alexandre Ghiti,
Dmitry V. Levin, Andrea Bolognani, Björn Töpel,
Thomas Gleixner, Ron Economos, Quan Zhou, Guo Ren, linux-riscv,
linux-kernel, linux-mm, stable, linux-kselftest
On 2024-12-20 02:26, Charlie Jenkins wrote:
> On Tue, Dec 03, 2024 at 01:55:07PM +0100, Andrew Jones wrote:
>> On Tue, Dec 03, 2024 at 05:30:05PM +0800, Celeste Liu wrote:
>>> From: Charlie Jenkins <charlie@rivosinc.com>
>>>
>>> This test checks that orig_a0 allows a syscall argument to be modified,
>>> and that changing a0 does not change the syscall argument.
>>>
>>> Cc: stable@vger.kernel.org
>>> Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
>>> Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
>>> Co-developed-by: Celeste Liu <uwu@coelacanthus.name>
>>> Signed-off-by: Celeste Liu <uwu@coelacanthus.name>
>>> Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
>>> ---
>>> tools/testing/selftests/riscv/abi/.gitignore | 1 +
>>> tools/testing/selftests/riscv/abi/Makefile | 5 +-
>>> tools/testing/selftests/riscv/abi/ptrace.c | 134 +++++++++++++++++++++++++++
>>> 3 files changed, 139 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/tools/testing/selftests/riscv/abi/.gitignore b/tools/testing/selftests/riscv/abi/.gitignore
>>> index b38358f91c4d2240ae64892871d9ca98bda1ae58..378c605919a3b3d58eec2701eb7af430cfe315d6 100644
>>> --- a/tools/testing/selftests/riscv/abi/.gitignore
>>> +++ b/tools/testing/selftests/riscv/abi/.gitignore
>>> @@ -1 +1,2 @@
>>> pointer_masking
>>> +ptrace
>>> diff --git a/tools/testing/selftests/riscv/abi/Makefile b/tools/testing/selftests/riscv/abi/Makefile
>>> index ed82ff9c664e7eb3f760cbab81fb957ff72579c5..3f74d059dfdcbce4d45d8ff618781ccea1419061 100644
>>> --- a/tools/testing/selftests/riscv/abi/Makefile
>>> +++ b/tools/testing/selftests/riscv/abi/Makefile
>>> @@ -2,9 +2,12 @@
>>>
>>> CFLAGS += -I$(top_srcdir)/tools/include
>>>
>>> -TEST_GEN_PROGS := pointer_masking
>>> +TEST_GEN_PROGS := pointer_masking ptrace
>>>
>>> include ../../lib.mk
>>>
>>> $(OUTPUT)/pointer_masking: pointer_masking.c
>>> $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
>>> +
>>> +$(OUTPUT)/ptrace: ptrace.c
>>> + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
>>> diff --git a/tools/testing/selftests/riscv/abi/ptrace.c b/tools/testing/selftests/riscv/abi/ptrace.c
>>> new file mode 100644
>>> index 0000000000000000000000000000000000000000..d192764b428d1f1c442f3957c6fedeb01a48d556
>>> --- /dev/null
>>> +++ b/tools/testing/selftests/riscv/abi/ptrace.c
>>> @@ -0,0 +1,134 @@
>>> +// SPDX-License-Identifier: GPL-2.0-only
>>> +#include <stdio.h>
>>> +#include <stdlib.h>
>>> +#include <string.h>
>>> +#include <unistd.h>
>>> +#include <fcntl.h>
>>> +#include <signal.h>
>>> +#include <errno.h>
>>> +#include <sys/types.h>
>>> +#include <sys/ptrace.h>
>>> +#include <sys/stat.h>
>>> +#include <sys/user.h>
>>> +#include <sys/wait.h>
>>> +#include <sys/uio.h>
>>> +#include <linux/elf.h>
>>> +#include <linux/unistd.h>
>>> +#include <asm/ptrace.h>
>>> +
>>> +#include "../../kselftest_harness.h"
>>> +
>>> +#define ORIG_A0_MODIFY 0x01
>>> +#define A0_MODIFY 0x02
>>> +#define A0_OLD 0x03
>>> +#define A0_NEW 0x04
>>
>> Shouldn't A0_OLD and A0_NEW set more bits, since 3 and 4 aren't very
>> unique (we could have those values by accident)? And should we include
>> setting bits over 31 for 64-bit targets?
>>
>>> +
>>> +#define perr_and_exit(fmt, ...) \
>>> + ({ \
>>> + char buf[256]; \
>>> + snprintf(buf, sizeof(buf), "%s:%d:" fmt ": %m\n", \
>>> + __func__, __LINE__, ##__VA_ARGS__); \
>>> + perror(buf); \
>>> + exit(-1); \
>>> + })
>>
>> Can we use e.g. ksft_exit_fail_perror() instead? I'd prefer we try to
>> consolidate testing/selftests/riscv/* tests on a common format for
>> errors and exit codes and we're already using other kselftest stuff.
>>
>>> +
>>> +static inline void resume_and_wait_tracee(pid_t pid, int flag)
>>> +{
>>> + int status;
>>> +
>>> + if (ptrace(flag, pid, 0, 0))
>>> + perr_and_exit("failed to resume the tracee %d\n", pid);
>>> +
>>> + if (waitpid(pid, &status, 0) != pid)
>>> + perr_and_exit("failed to wait for the tracee %d\n", pid);
>>> +}
>>> +
>>> +static void ptrace_test(int opt, int *result)
>>> +{
>>> + int status;
>>> + pid_t pid;
>>> + struct user_regs_struct regs;
>>> + struct iovec iov = {
>>> + .iov_base = ®s,
>>> + .iov_len = sizeof(regs),
>>> + };
>>> +
>>> + unsigned long orig_a0;
>>> + struct iovec a0_iov = {
>>> + .iov_base = &orig_a0,
>>> + .iov_len = sizeof(orig_a0),
>>> + };
>>> +
>>> + pid = fork();
>>> + if (pid == 0) {
>>> + /* Mark oneself being traced */
>>> + long val = ptrace(PTRACE_TRACEME, 0, 0, 0);
>>> +
>>> + if (val)
>>> + perr_and_exit("failed to request for tracer to trace me: %ld\n", val);
>>> +
>>> + kill(getpid(), SIGSTOP);
>>> +
>>> + /* Perform exit syscall that will be intercepted */
>>> + exit(A0_OLD);
>>> + }
>>> +
>>> + if (pid < 0)
>>> + exit(1);
>>
>> This unexpected error condition deserves a message, so I'd use
>> ksft_exit_fail_perror() here.
>>
>>> +
>>> + if (waitpid(pid, &status, 0) != pid)
>>> + perr_and_exit("failed to wait for the tracee %d\n", pid);
>>> +
>>> + /* Stop at the entry point of the syscall */
>>> + resume_and_wait_tracee(pid, PTRACE_SYSCALL);
>>> +
>>> + /* Check tracee regs before the syscall */
>>> + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))
>>> + perr_and_exit("failed to get tracee registers\n");
>>> + if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
>>> + perr_and_exit("failed to get tracee registers\n");
>>> + if (orig_a0 != A0_OLD)
>>> + perr_and_exit("unexpected orig_a0: 0x%lx\n", orig_a0);
>>> +
>>> + /* Modify a0/orig_a0 for the syscall */
>>> + switch (opt) {
>>> + case A0_MODIFY:
>>> + regs.a0 = A0_NEW;
>>> + break;
>>> + case ORIG_A0_MODIFY:
>>> + orig_a0 = A0_NEW;
>>> + break;
>>> + }
>>> +
>>> + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
>>> + perr_and_exit("failed to set tracee registers\n");
>>> +
>>> + /* Resume the tracee */
>>> + ptrace(PTRACE_CONT, pid, 0, 0);
>>> + if (waitpid(pid, &status, 0) != pid)
>>> + perr_and_exit("failed to wait for the tracee\n");
>>> +
>>> + *result = WEXITSTATUS(status);
>>> +}
>>> +
>>> +TEST(ptrace_modify_a0)
>>> +{
>>> + int result;
>>> +
>>> + ptrace_test(A0_MODIFY, &result);
>>> +
>>> + /* The modification of a0 cannot affect the first argument of the syscall */
>>> + EXPECT_EQ(A0_OLD, result);
>>
>> What about checking that we actually set regs.a0 to A0_NEW? We'd need
>> A0_NEW to be more unique than 4, though.
>>
>>> +}
>>> +
>>> +TEST(ptrace_modify_orig_a0)
>>> +{
>>> + int result;
>>> +
>>> + ptrace_test(ORIG_A0_MODIFY, &result);
>>> +
>>> + /* Only modify orig_a0 to change the first argument of the syscall */
>>
>> If we run ptrace_modify_a0 first then we've already set regs.a0 to A0_NEW
>> and can't check with this test that we don't set it to A0_NEW. We should
>> probably have two different test values, one for regs.a0 and one for
>> orig_a0 and ensure on both tests that we aren't writing both.
>>
>
> Celeste, do you want to fix this up or are you waiting for me to?
Sorry for delay. I was busy with household affairs in the past few weeks.
v3 will be sent tomorrow or the day after tomorrow.
I am deeply sorry for this.
>
> - Charlie
>
>>> + EXPECT_EQ(A0_NEW, result);
>>> +}
>>> +
>>> +TEST_HARNESS_MAIN
>>>
>>> --
>>> 2.47.0
>>>
>>
>> Thanks,
>> drew
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 2/2] riscv: selftests: Add a ptrace test to verify syscall parameter modification
@ 2024-12-19 21:29 ` Celeste Liu
0 siblings, 0 replies; 22+ messages in thread
From: Celeste Liu @ 2024-12-19 21:29 UTC (permalink / raw)
To: Charlie Jenkins, Andrew Jones
Cc: Oleg Nesterov, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Eric Biederman, Kees Cook, Shuah Khan, Alexandre Ghiti,
Dmitry V. Levin, Andrea Bolognani, Björn Töpel,
Thomas Gleixner, Ron Economos, Quan Zhou, Guo Ren, linux-riscv,
linux-kernel, linux-mm, stable, linux-kselftest
On 2024-12-20 02:26, Charlie Jenkins wrote:
> On Tue, Dec 03, 2024 at 01:55:07PM +0100, Andrew Jones wrote:
>> On Tue, Dec 03, 2024 at 05:30:05PM +0800, Celeste Liu wrote:
>>> From: Charlie Jenkins <charlie@rivosinc.com>
>>>
>>> This test checks that orig_a0 allows a syscall argument to be modified,
>>> and that changing a0 does not change the syscall argument.
>>>
>>> Cc: stable@vger.kernel.org
>>> Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
>>> Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
>>> Co-developed-by: Celeste Liu <uwu@coelacanthus.name>
>>> Signed-off-by: Celeste Liu <uwu@coelacanthus.name>
>>> Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
>>> ---
>>> tools/testing/selftests/riscv/abi/.gitignore | 1 +
>>> tools/testing/selftests/riscv/abi/Makefile | 5 +-
>>> tools/testing/selftests/riscv/abi/ptrace.c | 134 +++++++++++++++++++++++++++
>>> 3 files changed, 139 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/tools/testing/selftests/riscv/abi/.gitignore b/tools/testing/selftests/riscv/abi/.gitignore
>>> index b38358f91c4d2240ae64892871d9ca98bda1ae58..378c605919a3b3d58eec2701eb7af430cfe315d6 100644
>>> --- a/tools/testing/selftests/riscv/abi/.gitignore
>>> +++ b/tools/testing/selftests/riscv/abi/.gitignore
>>> @@ -1 +1,2 @@
>>> pointer_masking
>>> +ptrace
>>> diff --git a/tools/testing/selftests/riscv/abi/Makefile b/tools/testing/selftests/riscv/abi/Makefile
>>> index ed82ff9c664e7eb3f760cbab81fb957ff72579c5..3f74d059dfdcbce4d45d8ff618781ccea1419061 100644
>>> --- a/tools/testing/selftests/riscv/abi/Makefile
>>> +++ b/tools/testing/selftests/riscv/abi/Makefile
>>> @@ -2,9 +2,12 @@
>>>
>>> CFLAGS += -I$(top_srcdir)/tools/include
>>>
>>> -TEST_GEN_PROGS := pointer_masking
>>> +TEST_GEN_PROGS := pointer_masking ptrace
>>>
>>> include ../../lib.mk
>>>
>>> $(OUTPUT)/pointer_masking: pointer_masking.c
>>> $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
>>> +
>>> +$(OUTPUT)/ptrace: ptrace.c
>>> + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
>>> diff --git a/tools/testing/selftests/riscv/abi/ptrace.c b/tools/testing/selftests/riscv/abi/ptrace.c
>>> new file mode 100644
>>> index 0000000000000000000000000000000000000000..d192764b428d1f1c442f3957c6fedeb01a48d556
>>> --- /dev/null
>>> +++ b/tools/testing/selftests/riscv/abi/ptrace.c
>>> @@ -0,0 +1,134 @@
>>> +// SPDX-License-Identifier: GPL-2.0-only
>>> +#include <stdio.h>
>>> +#include <stdlib.h>
>>> +#include <string.h>
>>> +#include <unistd.h>
>>> +#include <fcntl.h>
>>> +#include <signal.h>
>>> +#include <errno.h>
>>> +#include <sys/types.h>
>>> +#include <sys/ptrace.h>
>>> +#include <sys/stat.h>
>>> +#include <sys/user.h>
>>> +#include <sys/wait.h>
>>> +#include <sys/uio.h>
>>> +#include <linux/elf.h>
>>> +#include <linux/unistd.h>
>>> +#include <asm/ptrace.h>
>>> +
>>> +#include "../../kselftest_harness.h"
>>> +
>>> +#define ORIG_A0_MODIFY 0x01
>>> +#define A0_MODIFY 0x02
>>> +#define A0_OLD 0x03
>>> +#define A0_NEW 0x04
>>
>> Shouldn't A0_OLD and A0_NEW set more bits, since 3 and 4 aren't very
>> unique (we could have those values by accident)? And should we include
>> setting bits over 31 for 64-bit targets?
>>
>>> +
>>> +#define perr_and_exit(fmt, ...) \
>>> + ({ \
>>> + char buf[256]; \
>>> + snprintf(buf, sizeof(buf), "%s:%d:" fmt ": %m\n", \
>>> + __func__, __LINE__, ##__VA_ARGS__); \
>>> + perror(buf); \
>>> + exit(-1); \
>>> + })
>>
>> Can we use e.g. ksft_exit_fail_perror() instead? I'd prefer we try to
>> consolidate testing/selftests/riscv/* tests on a common format for
>> errors and exit codes and we're already using other kselftest stuff.
>>
>>> +
>>> +static inline void resume_and_wait_tracee(pid_t pid, int flag)
>>> +{
>>> + int status;
>>> +
>>> + if (ptrace(flag, pid, 0, 0))
>>> + perr_and_exit("failed to resume the tracee %d\n", pid);
>>> +
>>> + if (waitpid(pid, &status, 0) != pid)
>>> + perr_and_exit("failed to wait for the tracee %d\n", pid);
>>> +}
>>> +
>>> +static void ptrace_test(int opt, int *result)
>>> +{
>>> + int status;
>>> + pid_t pid;
>>> + struct user_regs_struct regs;
>>> + struct iovec iov = {
>>> + .iov_base = ®s,
>>> + .iov_len = sizeof(regs),
>>> + };
>>> +
>>> + unsigned long orig_a0;
>>> + struct iovec a0_iov = {
>>> + .iov_base = &orig_a0,
>>> + .iov_len = sizeof(orig_a0),
>>> + };
>>> +
>>> + pid = fork();
>>> + if (pid == 0) {
>>> + /* Mark oneself being traced */
>>> + long val = ptrace(PTRACE_TRACEME, 0, 0, 0);
>>> +
>>> + if (val)
>>> + perr_and_exit("failed to request for tracer to trace me: %ld\n", val);
>>> +
>>> + kill(getpid(), SIGSTOP);
>>> +
>>> + /* Perform exit syscall that will be intercepted */
>>> + exit(A0_OLD);
>>> + }
>>> +
>>> + if (pid < 0)
>>> + exit(1);
>>
>> This unexpected error condition deserves a message, so I'd use
>> ksft_exit_fail_perror() here.
>>
>>> +
>>> + if (waitpid(pid, &status, 0) != pid)
>>> + perr_and_exit("failed to wait for the tracee %d\n", pid);
>>> +
>>> + /* Stop at the entry point of the syscall */
>>> + resume_and_wait_tracee(pid, PTRACE_SYSCALL);
>>> +
>>> + /* Check tracee regs before the syscall */
>>> + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))
>>> + perr_and_exit("failed to get tracee registers\n");
>>> + if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
>>> + perr_and_exit("failed to get tracee registers\n");
>>> + if (orig_a0 != A0_OLD)
>>> + perr_and_exit("unexpected orig_a0: 0x%lx\n", orig_a0);
>>> +
>>> + /* Modify a0/orig_a0 for the syscall */
>>> + switch (opt) {
>>> + case A0_MODIFY:
>>> + regs.a0 = A0_NEW;
>>> + break;
>>> + case ORIG_A0_MODIFY:
>>> + orig_a0 = A0_NEW;
>>> + break;
>>> + }
>>> +
>>> + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
>>> + perr_and_exit("failed to set tracee registers\n");
>>> +
>>> + /* Resume the tracee */
>>> + ptrace(PTRACE_CONT, pid, 0, 0);
>>> + if (waitpid(pid, &status, 0) != pid)
>>> + perr_and_exit("failed to wait for the tracee\n");
>>> +
>>> + *result = WEXITSTATUS(status);
>>> +}
>>> +
>>> +TEST(ptrace_modify_a0)
>>> +{
>>> + int result;
>>> +
>>> + ptrace_test(A0_MODIFY, &result);
>>> +
>>> + /* The modification of a0 cannot affect the first argument of the syscall */
>>> + EXPECT_EQ(A0_OLD, result);
>>
>> What about checking that we actually set regs.a0 to A0_NEW? We'd need
>> A0_NEW to be more unique than 4, though.
>>
>>> +}
>>> +
>>> +TEST(ptrace_modify_orig_a0)
>>> +{
>>> + int result;
>>> +
>>> + ptrace_test(ORIG_A0_MODIFY, &result);
>>> +
>>> + /* Only modify orig_a0 to change the first argument of the syscall */
>>
>> If we run ptrace_modify_a0 first then we've already set regs.a0 to A0_NEW
>> and can't check with this test that we don't set it to A0_NEW. We should
>> probably have two different test values, one for regs.a0 and one for
>> orig_a0 and ensure on both tests that we aren't writing both.
>>
>
> Celeste, do you want to fix this up or are you waiting for me to?
Sorry for delay. I was busy with household affairs in the past few weeks.
v3 will be sent tomorrow or the day after tomorrow.
I am deeply sorry for this.
>
> - Charlie
>
>>> + EXPECT_EQ(A0_NEW, result);
>>> +}
>>> +
>>> +TEST_HARNESS_MAIN
>>>
>>> --
>>> 2.47.0
>>>
>>
>> Thanks,
>> drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 2/2] riscv: selftests: Add a ptrace test to verify syscall parameter modification
2024-12-19 21:29 ` Celeste Liu
@ 2024-12-19 21:36 ` Charlie Jenkins
-1 siblings, 0 replies; 22+ messages in thread
From: Charlie Jenkins @ 2024-12-19 21:36 UTC (permalink / raw)
To: Celeste Liu
Cc: Andrew Jones, Oleg Nesterov, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Eric Biederman, Kees Cook, Shuah Khan, Alexandre Ghiti,
Dmitry V. Levin, Andrea Bolognani, Björn Töpel,
Thomas Gleixner, Ron Economos, Quan Zhou, Guo Ren, linux-riscv,
linux-kernel, linux-mm, stable, linux-kselftest
On Fri, Dec 20, 2024 at 05:29:45AM +0800, Celeste Liu wrote:
>
> On 2024-12-20 02:26, Charlie Jenkins wrote:
> > On Tue, Dec 03, 2024 at 01:55:07PM +0100, Andrew Jones wrote:
> >> On Tue, Dec 03, 2024 at 05:30:05PM +0800, Celeste Liu wrote:
> >>> From: Charlie Jenkins <charlie@rivosinc.com>
> >>>
> >>> This test checks that orig_a0 allows a syscall argument to be modified,
> >>> and that changing a0 does not change the syscall argument.
> >>>
> >>> Cc: stable@vger.kernel.org
> >>> Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
> >>> Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
> >>> Co-developed-by: Celeste Liu <uwu@coelacanthus.name>
> >>> Signed-off-by: Celeste Liu <uwu@coelacanthus.name>
> >>> Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
> >>> ---
> >>> tools/testing/selftests/riscv/abi/.gitignore | 1 +
> >>> tools/testing/selftests/riscv/abi/Makefile | 5 +-
> >>> tools/testing/selftests/riscv/abi/ptrace.c | 134 +++++++++++++++++++++++++++
> >>> 3 files changed, 139 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/tools/testing/selftests/riscv/abi/.gitignore b/tools/testing/selftests/riscv/abi/.gitignore
> >>> index b38358f91c4d2240ae64892871d9ca98bda1ae58..378c605919a3b3d58eec2701eb7af430cfe315d6 100644
> >>> --- a/tools/testing/selftests/riscv/abi/.gitignore
> >>> +++ b/tools/testing/selftests/riscv/abi/.gitignore
> >>> @@ -1 +1,2 @@
> >>> pointer_masking
> >>> +ptrace
> >>> diff --git a/tools/testing/selftests/riscv/abi/Makefile b/tools/testing/selftests/riscv/abi/Makefile
> >>> index ed82ff9c664e7eb3f760cbab81fb957ff72579c5..3f74d059dfdcbce4d45d8ff618781ccea1419061 100644
> >>> --- a/tools/testing/selftests/riscv/abi/Makefile
> >>> +++ b/tools/testing/selftests/riscv/abi/Makefile
> >>> @@ -2,9 +2,12 @@
> >>>
> >>> CFLAGS += -I$(top_srcdir)/tools/include
> >>>
> >>> -TEST_GEN_PROGS := pointer_masking
> >>> +TEST_GEN_PROGS := pointer_masking ptrace
> >>>
> >>> include ../../lib.mk
> >>>
> >>> $(OUTPUT)/pointer_masking: pointer_masking.c
> >>> $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> >>> +
> >>> +$(OUTPUT)/ptrace: ptrace.c
> >>> + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> >>> diff --git a/tools/testing/selftests/riscv/abi/ptrace.c b/tools/testing/selftests/riscv/abi/ptrace.c
> >>> new file mode 100644
> >>> index 0000000000000000000000000000000000000000..d192764b428d1f1c442f3957c6fedeb01a48d556
> >>> --- /dev/null
> >>> +++ b/tools/testing/selftests/riscv/abi/ptrace.c
> >>> @@ -0,0 +1,134 @@
> >>> +// SPDX-License-Identifier: GPL-2.0-only
> >>> +#include <stdio.h>
> >>> +#include <stdlib.h>
> >>> +#include <string.h>
> >>> +#include <unistd.h>
> >>> +#include <fcntl.h>
> >>> +#include <signal.h>
> >>> +#include <errno.h>
> >>> +#include <sys/types.h>
> >>> +#include <sys/ptrace.h>
> >>> +#include <sys/stat.h>
> >>> +#include <sys/user.h>
> >>> +#include <sys/wait.h>
> >>> +#include <sys/uio.h>
> >>> +#include <linux/elf.h>
> >>> +#include <linux/unistd.h>
> >>> +#include <asm/ptrace.h>
> >>> +
> >>> +#include "../../kselftest_harness.h"
> >>> +
> >>> +#define ORIG_A0_MODIFY 0x01
> >>> +#define A0_MODIFY 0x02
> >>> +#define A0_OLD 0x03
> >>> +#define A0_NEW 0x04
> >>
> >> Shouldn't A0_OLD and A0_NEW set more bits, since 3 and 4 aren't very
> >> unique (we could have those values by accident)? And should we include
> >> setting bits over 31 for 64-bit targets?
> >>
> >>> +
> >>> +#define perr_and_exit(fmt, ...) \
> >>> + ({ \
> >>> + char buf[256]; \
> >>> + snprintf(buf, sizeof(buf), "%s:%d:" fmt ": %m\n", \
> >>> + __func__, __LINE__, ##__VA_ARGS__); \
> >>> + perror(buf); \
> >>> + exit(-1); \
> >>> + })
> >>
> >> Can we use e.g. ksft_exit_fail_perror() instead? I'd prefer we try to
> >> consolidate testing/selftests/riscv/* tests on a common format for
> >> errors and exit codes and we're already using other kselftest stuff.
> >>
> >>> +
> >>> +static inline void resume_and_wait_tracee(pid_t pid, int flag)
> >>> +{
> >>> + int status;
> >>> +
> >>> + if (ptrace(flag, pid, 0, 0))
> >>> + perr_and_exit("failed to resume the tracee %d\n", pid);
> >>> +
> >>> + if (waitpid(pid, &status, 0) != pid)
> >>> + perr_and_exit("failed to wait for the tracee %d\n", pid);
> >>> +}
> >>> +
> >>> +static void ptrace_test(int opt, int *result)
> >>> +{
> >>> + int status;
> >>> + pid_t pid;
> >>> + struct user_regs_struct regs;
> >>> + struct iovec iov = {
> >>> + .iov_base = ®s,
> >>> + .iov_len = sizeof(regs),
> >>> + };
> >>> +
> >>> + unsigned long orig_a0;
> >>> + struct iovec a0_iov = {
> >>> + .iov_base = &orig_a0,
> >>> + .iov_len = sizeof(orig_a0),
> >>> + };
> >>> +
> >>> + pid = fork();
> >>> + if (pid == 0) {
> >>> + /* Mark oneself being traced */
> >>> + long val = ptrace(PTRACE_TRACEME, 0, 0, 0);
> >>> +
> >>> + if (val)
> >>> + perr_and_exit("failed to request for tracer to trace me: %ld\n", val);
> >>> +
> >>> + kill(getpid(), SIGSTOP);
> >>> +
> >>> + /* Perform exit syscall that will be intercepted */
> >>> + exit(A0_OLD);
> >>> + }
> >>> +
> >>> + if (pid < 0)
> >>> + exit(1);
> >>
> >> This unexpected error condition deserves a message, so I'd use
> >> ksft_exit_fail_perror() here.
> >>
> >>> +
> >>> + if (waitpid(pid, &status, 0) != pid)
> >>> + perr_and_exit("failed to wait for the tracee %d\n", pid);
> >>> +
> >>> + /* Stop at the entry point of the syscall */
> >>> + resume_and_wait_tracee(pid, PTRACE_SYSCALL);
> >>> +
> >>> + /* Check tracee regs before the syscall */
> >>> + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))
> >>> + perr_and_exit("failed to get tracee registers\n");
> >>> + if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
> >>> + perr_and_exit("failed to get tracee registers\n");
> >>> + if (orig_a0 != A0_OLD)
> >>> + perr_and_exit("unexpected orig_a0: 0x%lx\n", orig_a0);
> >>> +
> >>> + /* Modify a0/orig_a0 for the syscall */
> >>> + switch (opt) {
> >>> + case A0_MODIFY:
> >>> + regs.a0 = A0_NEW;
> >>> + break;
> >>> + case ORIG_A0_MODIFY:
> >>> + orig_a0 = A0_NEW;
> >>> + break;
> >>> + }
> >>> +
> >>> + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
> >>> + perr_and_exit("failed to set tracee registers\n");
> >>> +
> >>> + /* Resume the tracee */
> >>> + ptrace(PTRACE_CONT, pid, 0, 0);
> >>> + if (waitpid(pid, &status, 0) != pid)
> >>> + perr_and_exit("failed to wait for the tracee\n");
> >>> +
> >>> + *result = WEXITSTATUS(status);
> >>> +}
> >>> +
> >>> +TEST(ptrace_modify_a0)
> >>> +{
> >>> + int result;
> >>> +
> >>> + ptrace_test(A0_MODIFY, &result);
> >>> +
> >>> + /* The modification of a0 cannot affect the first argument of the syscall */
> >>> + EXPECT_EQ(A0_OLD, result);
> >>
> >> What about checking that we actually set regs.a0 to A0_NEW? We'd need
> >> A0_NEW to be more unique than 4, though.
> >>
> >>> +}
> >>> +
> >>> +TEST(ptrace_modify_orig_a0)
> >>> +{
> >>> + int result;
> >>> +
> >>> + ptrace_test(ORIG_A0_MODIFY, &result);
> >>> +
> >>> + /* Only modify orig_a0 to change the first argument of the syscall */
> >>
> >> If we run ptrace_modify_a0 first then we've already set regs.a0 to A0_NEW
> >> and can't check with this test that we don't set it to A0_NEW. We should
> >> probably have two different test values, one for regs.a0 and one for
> >> orig_a0 and ensure on both tests that we aren't writing both.
> >>
> >
> > Celeste, do you want to fix this up or are you waiting for me to?
>
> Sorry for delay. I was busy with household affairs in the past few weeks.
> v3 will be sent tomorrow or the day after tomorrow.
>
> I am deeply sorry for this.
No need to apologize! Just wanted to make sure you weren't expected me
to update the test :)
- Charlie
>
> >
> > - Charlie
> >
> >>> + EXPECT_EQ(A0_NEW, result);
> >>> +}
> >>> +
> >>> +TEST_HARNESS_MAIN
> >>>
> >>> --
> >>> 2.47.0
> >>>
> >>
> >> Thanks,
> >> drew
>
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 2/2] riscv: selftests: Add a ptrace test to verify syscall parameter modification
@ 2024-12-19 21:36 ` Charlie Jenkins
0 siblings, 0 replies; 22+ messages in thread
From: Charlie Jenkins @ 2024-12-19 21:36 UTC (permalink / raw)
To: Celeste Liu
Cc: Andrew Jones, Oleg Nesterov, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Eric Biederman, Kees Cook, Shuah Khan, Alexandre Ghiti,
Dmitry V. Levin, Andrea Bolognani, Björn Töpel,
Thomas Gleixner, Ron Economos, Quan Zhou, Guo Ren, linux-riscv,
linux-kernel, linux-mm, stable, linux-kselftest
On Fri, Dec 20, 2024 at 05:29:45AM +0800, Celeste Liu wrote:
>
> On 2024-12-20 02:26, Charlie Jenkins wrote:
> > On Tue, Dec 03, 2024 at 01:55:07PM +0100, Andrew Jones wrote:
> >> On Tue, Dec 03, 2024 at 05:30:05PM +0800, Celeste Liu wrote:
> >>> From: Charlie Jenkins <charlie@rivosinc.com>
> >>>
> >>> This test checks that orig_a0 allows a syscall argument to be modified,
> >>> and that changing a0 does not change the syscall argument.
> >>>
> >>> Cc: stable@vger.kernel.org
> >>> Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
> >>> Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
> >>> Co-developed-by: Celeste Liu <uwu@coelacanthus.name>
> >>> Signed-off-by: Celeste Liu <uwu@coelacanthus.name>
> >>> Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
> >>> ---
> >>> tools/testing/selftests/riscv/abi/.gitignore | 1 +
> >>> tools/testing/selftests/riscv/abi/Makefile | 5 +-
> >>> tools/testing/selftests/riscv/abi/ptrace.c | 134 +++++++++++++++++++++++++++
> >>> 3 files changed, 139 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/tools/testing/selftests/riscv/abi/.gitignore b/tools/testing/selftests/riscv/abi/.gitignore
> >>> index b38358f91c4d2240ae64892871d9ca98bda1ae58..378c605919a3b3d58eec2701eb7af430cfe315d6 100644
> >>> --- a/tools/testing/selftests/riscv/abi/.gitignore
> >>> +++ b/tools/testing/selftests/riscv/abi/.gitignore
> >>> @@ -1 +1,2 @@
> >>> pointer_masking
> >>> +ptrace
> >>> diff --git a/tools/testing/selftests/riscv/abi/Makefile b/tools/testing/selftests/riscv/abi/Makefile
> >>> index ed82ff9c664e7eb3f760cbab81fb957ff72579c5..3f74d059dfdcbce4d45d8ff618781ccea1419061 100644
> >>> --- a/tools/testing/selftests/riscv/abi/Makefile
> >>> +++ b/tools/testing/selftests/riscv/abi/Makefile
> >>> @@ -2,9 +2,12 @@
> >>>
> >>> CFLAGS += -I$(top_srcdir)/tools/include
> >>>
> >>> -TEST_GEN_PROGS := pointer_masking
> >>> +TEST_GEN_PROGS := pointer_masking ptrace
> >>>
> >>> include ../../lib.mk
> >>>
> >>> $(OUTPUT)/pointer_masking: pointer_masking.c
> >>> $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> >>> +
> >>> +$(OUTPUT)/ptrace: ptrace.c
> >>> + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> >>> diff --git a/tools/testing/selftests/riscv/abi/ptrace.c b/tools/testing/selftests/riscv/abi/ptrace.c
> >>> new file mode 100644
> >>> index 0000000000000000000000000000000000000000..d192764b428d1f1c442f3957c6fedeb01a48d556
> >>> --- /dev/null
> >>> +++ b/tools/testing/selftests/riscv/abi/ptrace.c
> >>> @@ -0,0 +1,134 @@
> >>> +// SPDX-License-Identifier: GPL-2.0-only
> >>> +#include <stdio.h>
> >>> +#include <stdlib.h>
> >>> +#include <string.h>
> >>> +#include <unistd.h>
> >>> +#include <fcntl.h>
> >>> +#include <signal.h>
> >>> +#include <errno.h>
> >>> +#include <sys/types.h>
> >>> +#include <sys/ptrace.h>
> >>> +#include <sys/stat.h>
> >>> +#include <sys/user.h>
> >>> +#include <sys/wait.h>
> >>> +#include <sys/uio.h>
> >>> +#include <linux/elf.h>
> >>> +#include <linux/unistd.h>
> >>> +#include <asm/ptrace.h>
> >>> +
> >>> +#include "../../kselftest_harness.h"
> >>> +
> >>> +#define ORIG_A0_MODIFY 0x01
> >>> +#define A0_MODIFY 0x02
> >>> +#define A0_OLD 0x03
> >>> +#define A0_NEW 0x04
> >>
> >> Shouldn't A0_OLD and A0_NEW set more bits, since 3 and 4 aren't very
> >> unique (we could have those values by accident)? And should we include
> >> setting bits over 31 for 64-bit targets?
> >>
> >>> +
> >>> +#define perr_and_exit(fmt, ...) \
> >>> + ({ \
> >>> + char buf[256]; \
> >>> + snprintf(buf, sizeof(buf), "%s:%d:" fmt ": %m\n", \
> >>> + __func__, __LINE__, ##__VA_ARGS__); \
> >>> + perror(buf); \
> >>> + exit(-1); \
> >>> + })
> >>
> >> Can we use e.g. ksft_exit_fail_perror() instead? I'd prefer we try to
> >> consolidate testing/selftests/riscv/* tests on a common format for
> >> errors and exit codes and we're already using other kselftest stuff.
> >>
> >>> +
> >>> +static inline void resume_and_wait_tracee(pid_t pid, int flag)
> >>> +{
> >>> + int status;
> >>> +
> >>> + if (ptrace(flag, pid, 0, 0))
> >>> + perr_and_exit("failed to resume the tracee %d\n", pid);
> >>> +
> >>> + if (waitpid(pid, &status, 0) != pid)
> >>> + perr_and_exit("failed to wait for the tracee %d\n", pid);
> >>> +}
> >>> +
> >>> +static void ptrace_test(int opt, int *result)
> >>> +{
> >>> + int status;
> >>> + pid_t pid;
> >>> + struct user_regs_struct regs;
> >>> + struct iovec iov = {
> >>> + .iov_base = ®s,
> >>> + .iov_len = sizeof(regs),
> >>> + };
> >>> +
> >>> + unsigned long orig_a0;
> >>> + struct iovec a0_iov = {
> >>> + .iov_base = &orig_a0,
> >>> + .iov_len = sizeof(orig_a0),
> >>> + };
> >>> +
> >>> + pid = fork();
> >>> + if (pid == 0) {
> >>> + /* Mark oneself being traced */
> >>> + long val = ptrace(PTRACE_TRACEME, 0, 0, 0);
> >>> +
> >>> + if (val)
> >>> + perr_and_exit("failed to request for tracer to trace me: %ld\n", val);
> >>> +
> >>> + kill(getpid(), SIGSTOP);
> >>> +
> >>> + /* Perform exit syscall that will be intercepted */
> >>> + exit(A0_OLD);
> >>> + }
> >>> +
> >>> + if (pid < 0)
> >>> + exit(1);
> >>
> >> This unexpected error condition deserves a message, so I'd use
> >> ksft_exit_fail_perror() here.
> >>
> >>> +
> >>> + if (waitpid(pid, &status, 0) != pid)
> >>> + perr_and_exit("failed to wait for the tracee %d\n", pid);
> >>> +
> >>> + /* Stop at the entry point of the syscall */
> >>> + resume_and_wait_tracee(pid, PTRACE_SYSCALL);
> >>> +
> >>> + /* Check tracee regs before the syscall */
> >>> + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))
> >>> + perr_and_exit("failed to get tracee registers\n");
> >>> + if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
> >>> + perr_and_exit("failed to get tracee registers\n");
> >>> + if (orig_a0 != A0_OLD)
> >>> + perr_and_exit("unexpected orig_a0: 0x%lx\n", orig_a0);
> >>> +
> >>> + /* Modify a0/orig_a0 for the syscall */
> >>> + switch (opt) {
> >>> + case A0_MODIFY:
> >>> + regs.a0 = A0_NEW;
> >>> + break;
> >>> + case ORIG_A0_MODIFY:
> >>> + orig_a0 = A0_NEW;
> >>> + break;
> >>> + }
> >>> +
> >>> + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
> >>> + perr_and_exit("failed to set tracee registers\n");
> >>> +
> >>> + /* Resume the tracee */
> >>> + ptrace(PTRACE_CONT, pid, 0, 0);
> >>> + if (waitpid(pid, &status, 0) != pid)
> >>> + perr_and_exit("failed to wait for the tracee\n");
> >>> +
> >>> + *result = WEXITSTATUS(status);
> >>> +}
> >>> +
> >>> +TEST(ptrace_modify_a0)
> >>> +{
> >>> + int result;
> >>> +
> >>> + ptrace_test(A0_MODIFY, &result);
> >>> +
> >>> + /* The modification of a0 cannot affect the first argument of the syscall */
> >>> + EXPECT_EQ(A0_OLD, result);
> >>
> >> What about checking that we actually set regs.a0 to A0_NEW? We'd need
> >> A0_NEW to be more unique than 4, though.
> >>
> >>> +}
> >>> +
> >>> +TEST(ptrace_modify_orig_a0)
> >>> +{
> >>> + int result;
> >>> +
> >>> + ptrace_test(ORIG_A0_MODIFY, &result);
> >>> +
> >>> + /* Only modify orig_a0 to change the first argument of the syscall */
> >>
> >> If we run ptrace_modify_a0 first then we've already set regs.a0 to A0_NEW
> >> and can't check with this test that we don't set it to A0_NEW. We should
> >> probably have two different test values, one for regs.a0 and one for
> >> orig_a0 and ensure on both tests that we aren't writing both.
> >>
> >
> > Celeste, do you want to fix this up or are you waiting for me to?
>
> Sorry for delay. I was busy with household affairs in the past few weeks.
> v3 will be sent tomorrow or the day after tomorrow.
>
> I am deeply sorry for this.
No need to apologize! Just wanted to make sure you weren't expected me
to update the test :)
- Charlie
>
> >
> > - Charlie
> >
> >>> + EXPECT_EQ(A0_NEW, result);
> >>> +}
> >>> +
> >>> +TEST_HARNESS_MAIN
> >>>
> >>> --
> >>> 2.47.0
> >>>
> >>
> >> Thanks,
> >> drew
>
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 2/2] riscv: selftests: Add a ptrace test to verify syscall parameter modification
2024-12-19 21:36 ` Charlie Jenkins
@ 2024-12-26 11:04 ` Celeste Liu
-1 siblings, 0 replies; 22+ messages in thread
From: Celeste Liu @ 2024-12-26 11:04 UTC (permalink / raw)
To: Charlie Jenkins
Cc: Andrew Jones, Oleg Nesterov, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Eric Biederman, Kees Cook, Shuah Khan, Alexandre Ghiti,
Dmitry V. Levin, Andrea Bolognani, Björn Töpel,
Thomas Gleixner, Ron Economos, Quan Zhou, Guo Ren, linux-riscv,
linux-kernel, linux-mm, stable, linux-kselftest
On 2024-12-20 05:36, Charlie Jenkins wrote:
> On Fri, Dec 20, 2024 at 05:29:45AM +0800, Celeste Liu wrote:
>>
>> On 2024-12-20 02:26, Charlie Jenkins wrote:
>>> On Tue, Dec 03, 2024 at 01:55:07PM +0100, Andrew Jones wrote:
>>>> On Tue, Dec 03, 2024 at 05:30:05PM +0800, Celeste Liu wrote:
>>>>> From: Charlie Jenkins <charlie@rivosinc.com>
>>>>>
>>>>> This test checks that orig_a0 allows a syscall argument to be modified,
>>>>> and that changing a0 does not change the syscall argument.
>>>>>
>>>>> Cc: stable@vger.kernel.org
>>>>> Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
>>>>> Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
>>>>> Co-developed-by: Celeste Liu <uwu@coelacanthus.name>
>>>>> Signed-off-by: Celeste Liu <uwu@coelacanthus.name>
>>>>> Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
>>>>> ---
>>>>> tools/testing/selftests/riscv/abi/.gitignore | 1 +
>>>>> tools/testing/selftests/riscv/abi/Makefile | 5 +-
>>>>> tools/testing/selftests/riscv/abi/ptrace.c | 134 +++++++++++++++++++++++++++
>>>>> 3 files changed, 139 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/tools/testing/selftests/riscv/abi/.gitignore b/tools/testing/selftests/riscv/abi/.gitignore
>>>>> index b38358f91c4d2240ae64892871d9ca98bda1ae58..378c605919a3b3d58eec2701eb7af430cfe315d6 100644
>>>>> --- a/tools/testing/selftests/riscv/abi/.gitignore
>>>>> +++ b/tools/testing/selftests/riscv/abi/.gitignore
>>>>> @@ -1 +1,2 @@
>>>>> pointer_masking
>>>>> +ptrace
>>>>> diff --git a/tools/testing/selftests/riscv/abi/Makefile b/tools/testing/selftests/riscv/abi/Makefile
>>>>> index ed82ff9c664e7eb3f760cbab81fb957ff72579c5..3f74d059dfdcbce4d45d8ff618781ccea1419061 100644
>>>>> --- a/tools/testing/selftests/riscv/abi/Makefile
>>>>> +++ b/tools/testing/selftests/riscv/abi/Makefile
>>>>> @@ -2,9 +2,12 @@
>>>>>
>>>>> CFLAGS += -I$(top_srcdir)/tools/include
>>>>>
>>>>> -TEST_GEN_PROGS := pointer_masking
>>>>> +TEST_GEN_PROGS := pointer_masking ptrace
>>>>>
>>>>> include ../../lib.mk
>>>>>
>>>>> $(OUTPUT)/pointer_masking: pointer_masking.c
>>>>> $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
>>>>> +
>>>>> +$(OUTPUT)/ptrace: ptrace.c
>>>>> + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
>>>>> diff --git a/tools/testing/selftests/riscv/abi/ptrace.c b/tools/testing/selftests/riscv/abi/ptrace.c
>>>>> new file mode 100644
>>>>> index 0000000000000000000000000000000000000000..d192764b428d1f1c442f3957c6fedeb01a48d556
>>>>> --- /dev/null
>>>>> +++ b/tools/testing/selftests/riscv/abi/ptrace.c
>>>>> @@ -0,0 +1,134 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0-only
>>>>> +#include <stdio.h>
>>>>> +#include <stdlib.h>
>>>>> +#include <string.h>
>>>>> +#include <unistd.h>
>>>>> +#include <fcntl.h>
>>>>> +#include <signal.h>
>>>>> +#include <errno.h>
>>>>> +#include <sys/types.h>
>>>>> +#include <sys/ptrace.h>
>>>>> +#include <sys/stat.h>
>>>>> +#include <sys/user.h>
>>>>> +#include <sys/wait.h>
>>>>> +#include <sys/uio.h>
>>>>> +#include <linux/elf.h>
>>>>> +#include <linux/unistd.h>
>>>>> +#include <asm/ptrace.h>
>>>>> +
>>>>> +#include "../../kselftest_harness.h"
>>>>> +
>>>>> +#define ORIG_A0_MODIFY 0x01
>>>>> +#define A0_MODIFY 0x02
>>>>> +#define A0_OLD 0x03
>>>>> +#define A0_NEW 0x04
>>>>
>>>> Shouldn't A0_OLD and A0_NEW set more bits, since 3 and 4 aren't very
>>>> unique (we could have those values by accident)? And should we include
>>>> setting bits over 31 for 64-bit targets?
>>>>
>>>>> +
>>>>> +#define perr_and_exit(fmt, ...) \
>>>>> + ({ \
>>>>> + char buf[256]; \
>>>>> + snprintf(buf, sizeof(buf), "%s:%d:" fmt ": %m\n", \
>>>>> + __func__, __LINE__, ##__VA_ARGS__); \
>>>>> + perror(buf); \
>>>>> + exit(-1); \
>>>>> + })
>>>>
>>>> Can we use e.g. ksft_exit_fail_perror() instead? I'd prefer we try to
>>>> consolidate testing/selftests/riscv/* tests on a common format for
>>>> errors and exit codes and we're already using other kselftest stuff.
>>>>
>>>>> +
>>>>> +static inline void resume_and_wait_tracee(pid_t pid, int flag)
>>>>> +{
>>>>> + int status;
>>>>> +
>>>>> + if (ptrace(flag, pid, 0, 0))
>>>>> + perr_and_exit("failed to resume the tracee %d\n", pid);
>>>>> +
>>>>> + if (waitpid(pid, &status, 0) != pid)
>>>>> + perr_and_exit("failed to wait for the tracee %d\n", pid);
>>>>> +}
>>>>> +
>>>>> +static void ptrace_test(int opt, int *result)
>>>>> +{
>>>>> + int status;
>>>>> + pid_t pid;
>>>>> + struct user_regs_struct regs;
>>>>> + struct iovec iov = {
>>>>> + .iov_base = ®s,
>>>>> + .iov_len = sizeof(regs),
>>>>> + };
>>>>> +
>>>>> + unsigned long orig_a0;
>>>>> + struct iovec a0_iov = {
>>>>> + .iov_base = &orig_a0,
>>>>> + .iov_len = sizeof(orig_a0),
>>>>> + };
>>>>> +
>>>>> + pid = fork();
>>>>> + if (pid == 0) {
>>>>> + /* Mark oneself being traced */
>>>>> + long val = ptrace(PTRACE_TRACEME, 0, 0, 0);
>>>>> +
>>>>> + if (val)
>>>>> + perr_and_exit("failed to request for tracer to trace me: %ld\n", val);
>>>>> +
>>>>> + kill(getpid(), SIGSTOP);
>>>>> +
>>>>> + /* Perform exit syscall that will be intercepted */
>>>>> + exit(A0_OLD);
>>>>> + }
>>>>> +
>>>>> + if (pid < 0)
>>>>> + exit(1);
>>>>
>>>> This unexpected error condition deserves a message, so I'd use
>>>> ksft_exit_fail_perror() here.
>>>>
>>>>> +
>>>>> + if (waitpid(pid, &status, 0) != pid)
>>>>> + perr_and_exit("failed to wait for the tracee %d\n", pid);
>>>>> +
>>>>> + /* Stop at the entry point of the syscall */
>>>>> + resume_and_wait_tracee(pid, PTRACE_SYSCALL);
>>>>> +
>>>>> + /* Check tracee regs before the syscall */
>>>>> + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))
>>>>> + perr_and_exit("failed to get tracee registers\n");
>>>>> + if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
>>>>> + perr_and_exit("failed to get tracee registers\n");
>>>>> + if (orig_a0 != A0_OLD)
>>>>> + perr_and_exit("unexpected orig_a0: 0x%lx\n", orig_a0);
>>>>> +
>>>>> + /* Modify a0/orig_a0 for the syscall */
>>>>> + switch (opt) {
>>>>> + case A0_MODIFY:
>>>>> + regs.a0 = A0_NEW;
>>>>> + break;
>>>>> + case ORIG_A0_MODIFY:
>>>>> + orig_a0 = A0_NEW;
>>>>> + break;
>>>>> + }
>>>>> +
>>>>> + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
>>>>> + perr_and_exit("failed to set tracee registers\n");
>>>>> +
>>>>> + /* Resume the tracee */
>>>>> + ptrace(PTRACE_CONT, pid, 0, 0);
>>>>> + if (waitpid(pid, &status, 0) != pid)
>>>>> + perr_and_exit("failed to wait for the tracee\n");
>>>>> +
>>>>> + *result = WEXITSTATUS(status);
>>>>> +}
>>>>> +
>>>>> +TEST(ptrace_modify_a0)
>>>>> +{
>>>>> + int result;
>>>>> +
>>>>> + ptrace_test(A0_MODIFY, &result);
>>>>> +
>>>>> + /* The modification of a0 cannot affect the first argument of the syscall */
>>>>> + EXPECT_EQ(A0_OLD, result);
>>>>
>>>> What about checking that we actually set regs.a0 to A0_NEW? We'd need
>>>> A0_NEW to be more unique than 4, though.
>>>>
>>>>> +}
>>>>> +
>>>>> +TEST(ptrace_modify_orig_a0)
>>>>> +{
>>>>> + int result;
>>>>> +
>>>>> + ptrace_test(ORIG_A0_MODIFY, &result);
>>>>> +
>>>>> + /* Only modify orig_a0 to change the first argument of the syscall */
>>>>
>>>> If we run ptrace_modify_a0 first then we've already set regs.a0 to A0_NEW
>>>> and can't check with this test that we don't set it to A0_NEW. We should
>>>> probably have two different test values, one for regs.a0 and one for
>>>> orig_a0 and ensure on both tests that we aren't writing both.
>>>>
>>>
>>> Celeste, do you want to fix this up or are you waiting for me to?
>>
>> Sorry for delay. I was busy with household affairs in the past few weeks.
>> v3 will be sent tomorrow or the day after tomorrow.
>>
>> I am deeply sorry for this.
>
> No need to apologize! Just wanted to make sure you weren't expected me
> to update the test :)
>
> - Charlie
>
>>
>>>
>>> - Charlie
>>>
>>>>> + EXPECT_EQ(A0_NEW, result);
>>>>> +}
>>>>> +
>>>>> +TEST_HARNESS_MAIN
>>>>>
>>>>> --
>>>>> 2.47.0
>>>>>
>>>>
>>>> Thanks,
>>>> drew
>>
v4 has been sent.
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 2/2] riscv: selftests: Add a ptrace test to verify syscall parameter modification
@ 2024-12-26 11:04 ` Celeste Liu
0 siblings, 0 replies; 22+ messages in thread
From: Celeste Liu @ 2024-12-26 11:04 UTC (permalink / raw)
To: Charlie Jenkins
Cc: Andrew Jones, Oleg Nesterov, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Eric Biederman, Kees Cook, Shuah Khan, Alexandre Ghiti,
Dmitry V. Levin, Andrea Bolognani, Björn Töpel,
Thomas Gleixner, Ron Economos, Quan Zhou, Guo Ren, linux-riscv,
linux-kernel, linux-mm, stable, linux-kselftest
On 2024-12-20 05:36, Charlie Jenkins wrote:
> On Fri, Dec 20, 2024 at 05:29:45AM +0800, Celeste Liu wrote:
>>
>> On 2024-12-20 02:26, Charlie Jenkins wrote:
>>> On Tue, Dec 03, 2024 at 01:55:07PM +0100, Andrew Jones wrote:
>>>> On Tue, Dec 03, 2024 at 05:30:05PM +0800, Celeste Liu wrote:
>>>>> From: Charlie Jenkins <charlie@rivosinc.com>
>>>>>
>>>>> This test checks that orig_a0 allows a syscall argument to be modified,
>>>>> and that changing a0 does not change the syscall argument.
>>>>>
>>>>> Cc: stable@vger.kernel.org
>>>>> Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
>>>>> Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
>>>>> Co-developed-by: Celeste Liu <uwu@coelacanthus.name>
>>>>> Signed-off-by: Celeste Liu <uwu@coelacanthus.name>
>>>>> Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
>>>>> ---
>>>>> tools/testing/selftests/riscv/abi/.gitignore | 1 +
>>>>> tools/testing/selftests/riscv/abi/Makefile | 5 +-
>>>>> tools/testing/selftests/riscv/abi/ptrace.c | 134 +++++++++++++++++++++++++++
>>>>> 3 files changed, 139 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/tools/testing/selftests/riscv/abi/.gitignore b/tools/testing/selftests/riscv/abi/.gitignore
>>>>> index b38358f91c4d2240ae64892871d9ca98bda1ae58..378c605919a3b3d58eec2701eb7af430cfe315d6 100644
>>>>> --- a/tools/testing/selftests/riscv/abi/.gitignore
>>>>> +++ b/tools/testing/selftests/riscv/abi/.gitignore
>>>>> @@ -1 +1,2 @@
>>>>> pointer_masking
>>>>> +ptrace
>>>>> diff --git a/tools/testing/selftests/riscv/abi/Makefile b/tools/testing/selftests/riscv/abi/Makefile
>>>>> index ed82ff9c664e7eb3f760cbab81fb957ff72579c5..3f74d059dfdcbce4d45d8ff618781ccea1419061 100644
>>>>> --- a/tools/testing/selftests/riscv/abi/Makefile
>>>>> +++ b/tools/testing/selftests/riscv/abi/Makefile
>>>>> @@ -2,9 +2,12 @@
>>>>>
>>>>> CFLAGS += -I$(top_srcdir)/tools/include
>>>>>
>>>>> -TEST_GEN_PROGS := pointer_masking
>>>>> +TEST_GEN_PROGS := pointer_masking ptrace
>>>>>
>>>>> include ../../lib.mk
>>>>>
>>>>> $(OUTPUT)/pointer_masking: pointer_masking.c
>>>>> $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
>>>>> +
>>>>> +$(OUTPUT)/ptrace: ptrace.c
>>>>> + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
>>>>> diff --git a/tools/testing/selftests/riscv/abi/ptrace.c b/tools/testing/selftests/riscv/abi/ptrace.c
>>>>> new file mode 100644
>>>>> index 0000000000000000000000000000000000000000..d192764b428d1f1c442f3957c6fedeb01a48d556
>>>>> --- /dev/null
>>>>> +++ b/tools/testing/selftests/riscv/abi/ptrace.c
>>>>> @@ -0,0 +1,134 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0-only
>>>>> +#include <stdio.h>
>>>>> +#include <stdlib.h>
>>>>> +#include <string.h>
>>>>> +#include <unistd.h>
>>>>> +#include <fcntl.h>
>>>>> +#include <signal.h>
>>>>> +#include <errno.h>
>>>>> +#include <sys/types.h>
>>>>> +#include <sys/ptrace.h>
>>>>> +#include <sys/stat.h>
>>>>> +#include <sys/user.h>
>>>>> +#include <sys/wait.h>
>>>>> +#include <sys/uio.h>
>>>>> +#include <linux/elf.h>
>>>>> +#include <linux/unistd.h>
>>>>> +#include <asm/ptrace.h>
>>>>> +
>>>>> +#include "../../kselftest_harness.h"
>>>>> +
>>>>> +#define ORIG_A0_MODIFY 0x01
>>>>> +#define A0_MODIFY 0x02
>>>>> +#define A0_OLD 0x03
>>>>> +#define A0_NEW 0x04
>>>>
>>>> Shouldn't A0_OLD and A0_NEW set more bits, since 3 and 4 aren't very
>>>> unique (we could have those values by accident)? And should we include
>>>> setting bits over 31 for 64-bit targets?
>>>>
>>>>> +
>>>>> +#define perr_and_exit(fmt, ...) \
>>>>> + ({ \
>>>>> + char buf[256]; \
>>>>> + snprintf(buf, sizeof(buf), "%s:%d:" fmt ": %m\n", \
>>>>> + __func__, __LINE__, ##__VA_ARGS__); \
>>>>> + perror(buf); \
>>>>> + exit(-1); \
>>>>> + })
>>>>
>>>> Can we use e.g. ksft_exit_fail_perror() instead? I'd prefer we try to
>>>> consolidate testing/selftests/riscv/* tests on a common format for
>>>> errors and exit codes and we're already using other kselftest stuff.
>>>>
>>>>> +
>>>>> +static inline void resume_and_wait_tracee(pid_t pid, int flag)
>>>>> +{
>>>>> + int status;
>>>>> +
>>>>> + if (ptrace(flag, pid, 0, 0))
>>>>> + perr_and_exit("failed to resume the tracee %d\n", pid);
>>>>> +
>>>>> + if (waitpid(pid, &status, 0) != pid)
>>>>> + perr_and_exit("failed to wait for the tracee %d\n", pid);
>>>>> +}
>>>>> +
>>>>> +static void ptrace_test(int opt, int *result)
>>>>> +{
>>>>> + int status;
>>>>> + pid_t pid;
>>>>> + struct user_regs_struct regs;
>>>>> + struct iovec iov = {
>>>>> + .iov_base = ®s,
>>>>> + .iov_len = sizeof(regs),
>>>>> + };
>>>>> +
>>>>> + unsigned long orig_a0;
>>>>> + struct iovec a0_iov = {
>>>>> + .iov_base = &orig_a0,
>>>>> + .iov_len = sizeof(orig_a0),
>>>>> + };
>>>>> +
>>>>> + pid = fork();
>>>>> + if (pid == 0) {
>>>>> + /* Mark oneself being traced */
>>>>> + long val = ptrace(PTRACE_TRACEME, 0, 0, 0);
>>>>> +
>>>>> + if (val)
>>>>> + perr_and_exit("failed to request for tracer to trace me: %ld\n", val);
>>>>> +
>>>>> + kill(getpid(), SIGSTOP);
>>>>> +
>>>>> + /* Perform exit syscall that will be intercepted */
>>>>> + exit(A0_OLD);
>>>>> + }
>>>>> +
>>>>> + if (pid < 0)
>>>>> + exit(1);
>>>>
>>>> This unexpected error condition deserves a message, so I'd use
>>>> ksft_exit_fail_perror() here.
>>>>
>>>>> +
>>>>> + if (waitpid(pid, &status, 0) != pid)
>>>>> + perr_and_exit("failed to wait for the tracee %d\n", pid);
>>>>> +
>>>>> + /* Stop at the entry point of the syscall */
>>>>> + resume_and_wait_tracee(pid, PTRACE_SYSCALL);
>>>>> +
>>>>> + /* Check tracee regs before the syscall */
>>>>> + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))
>>>>> + perr_and_exit("failed to get tracee registers\n");
>>>>> + if (ptrace(PTRACE_GETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
>>>>> + perr_and_exit("failed to get tracee registers\n");
>>>>> + if (orig_a0 != A0_OLD)
>>>>> + perr_and_exit("unexpected orig_a0: 0x%lx\n", orig_a0);
>>>>> +
>>>>> + /* Modify a0/orig_a0 for the syscall */
>>>>> + switch (opt) {
>>>>> + case A0_MODIFY:
>>>>> + regs.a0 = A0_NEW;
>>>>> + break;
>>>>> + case ORIG_A0_MODIFY:
>>>>> + orig_a0 = A0_NEW;
>>>>> + break;
>>>>> + }
>>>>> +
>>>>> + if (ptrace(PTRACE_SETREGSET, pid, NT_RISCV_ORIG_A0, &a0_iov))
>>>>> + perr_and_exit("failed to set tracee registers\n");
>>>>> +
>>>>> + /* Resume the tracee */
>>>>> + ptrace(PTRACE_CONT, pid, 0, 0);
>>>>> + if (waitpid(pid, &status, 0) != pid)
>>>>> + perr_and_exit("failed to wait for the tracee\n");
>>>>> +
>>>>> + *result = WEXITSTATUS(status);
>>>>> +}
>>>>> +
>>>>> +TEST(ptrace_modify_a0)
>>>>> +{
>>>>> + int result;
>>>>> +
>>>>> + ptrace_test(A0_MODIFY, &result);
>>>>> +
>>>>> + /* The modification of a0 cannot affect the first argument of the syscall */
>>>>> + EXPECT_EQ(A0_OLD, result);
>>>>
>>>> What about checking that we actually set regs.a0 to A0_NEW? We'd need
>>>> A0_NEW to be more unique than 4, though.
>>>>
>>>>> +}
>>>>> +
>>>>> +TEST(ptrace_modify_orig_a0)
>>>>> +{
>>>>> + int result;
>>>>> +
>>>>> + ptrace_test(ORIG_A0_MODIFY, &result);
>>>>> +
>>>>> + /* Only modify orig_a0 to change the first argument of the syscall */
>>>>
>>>> If we run ptrace_modify_a0 first then we've already set regs.a0 to A0_NEW
>>>> and can't check with this test that we don't set it to A0_NEW. We should
>>>> probably have two different test values, one for regs.a0 and one for
>>>> orig_a0 and ensure on both tests that we aren't writing both.
>>>>
>>>
>>> Celeste, do you want to fix this up or are you waiting for me to?
>>
>> Sorry for delay. I was busy with household affairs in the past few weeks.
>> v3 will be sent tomorrow or the day after tomorrow.
>>
>> I am deeply sorry for this.
>
> No need to apologize! Just wanted to make sure you weren't expected me
> to update the test :)
>
> - Charlie
>
>>
>>>
>>> - Charlie
>>>
>>>>> + EXPECT_EQ(A0_NEW, result);
>>>>> +}
>>>>> +
>>>>> +TEST_HARNESS_MAIN
>>>>>
>>>>> --
>>>>> 2.47.0
>>>>>
>>>>
>>>> Thanks,
>>>> drew
>>
v4 has been sent.
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 22+ messages in thread