public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO API
@ 2025-01-07 23:01 Dmitry V. Levin
  2025-01-07 23:04 ` [PATCH 1/6] Revert "arch: remove unused function syscall_set_arguments()" Dmitry V. Levin
                   ` (6 more replies)
  0 siblings, 7 replies; 22+ messages in thread
From: Dmitry V. Levin @ 2025-01-07 23:01 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, Vineet Gupta, Russell King,
	Catalin Marinas, Will Deacon, Guo Ren, Brian Cain, Huacai Chen,
	WANG Xuerui, Geert Uytterhoeven, Michal Simek,
	Thomas Bogendoerfer, Dinh Nguyen, Jonas Bonn, Stefan Kristiansson,
	Stafford Horne, James E.J. Bottomley, Helge Deller,
	Michael Ellerman, Nicholas Piggin, Christophe Leroy, Naveen N Rao,
	Madhavan Srinivasan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger, Sven Schnelle, Yoshinori Sato, Rich Felker,
	John Paul Adrian Glaubitz, David S. Miller, Andreas Larsson,
	Richard Weinberger, Anton Ivanov, Johannes Berg, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Chris Zankel, Max Filippov, Arnd Bergmann, Shuah Khan,
	linux-snps-arc, linux-kernel, linux-arm-kernel, linux-csky,
	linux-hexagon, loongarch, linux-m68k, linux-mips, linux-openrisc,
	linux-parisc, linuxppc-dev, linux-riscv, linux-s390, linux-sh,
	sparclinux, linux-um, linux-arch, linux-kselftest

PTRACE_SET_SYSCALL_INFO is a generic ptrace API that complements
PTRACE_GET_SYSCALL_INFO by letting the ptracer modify details of
system calls the tracee is blocked in.

This API allows ptracers to obtain and modify system call details
in a straightforward and architecture-agnostic way.

Current implementation supports changing only those bits of system call
information that are used by strace, namely, syscall number, syscall
arguments, and syscall return value.

Support of changing additional details returned by PTRACE_GET_SYSCALL_INFO,
such as instruction pointer and stack pointer, could be added later
if needed, by re-using struct ptrace_syscall_info.reserved to specify
the additional details that should be set.  Currently, the reserved
field of struct ptrace_syscall_info must be initialized with zeroes;
arch, instruction_pointer, and stack_pointer fields are ignored.

PTRACE_SET_SYSCALL_INFO currently supports only PTRACE_SYSCALL_INFO_ENTRY,
PTRACE_SYSCALL_INFO_EXIT, and PTRACE_SYSCALL_INFO_SECCOMP operations.
Other operations could be added later if needed.

Ideally, PTRACE_SET_SYSCALL_INFO should have been introduced along with
PTRACE_GET_SYSCALL_INFO, but it didn't happen.  The last straw that
convinced me to implement PTRACE_SET_SYSCALL_INFO was apparent failure
to provide an API of changing the first system call argument on riscv
architecture [1].

ptrace(2) man page:

long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
...
PTRACE_SET_SYSCALL_INFO
       Modify information about the system call that caused the stop.
       The "data" argument is a pointer to struct ptrace_syscall_info
       that specifies the system call information to be set.
       The "addr" argument should be set to sizeof(struct ptrace_syscall_info)).

[1] https://lore.kernel.org/all/59505464-c84a-403d-972f-d4b2055eeaac@gmail.com/

Dmitry V. Levin (6):
  Revert "arch: remove unused function syscall_set_arguments()"
  syscall.h: add syscall_set_arguments() on remaining
    HAVE_ARCH_TRACEHOOK arches
  syscall.h: introduce syscall_set_nr()
  ptrace_get_syscall_info: factor out ptrace_get_syscall_info_op
  ptrace: introduce PTRACE_SET_SYSCALL_INFO request
  selftests/ptrace: add a test case for PTRACE_SET_SYSCALL_INFO

 arch/arc/include/asm/syscall.h                |  20 +
 arch/arm/include/asm/syscall.h                |  25 +
 arch/arm64/include/asm/syscall.h              |  20 +
 arch/csky/include/asm/syscall.h               |  13 +
 arch/hexagon/include/asm/syscall.h            |  14 +
 arch/loongarch/include/asm/syscall.h          |  15 +
 arch/m68k/include/asm/syscall.h               |   7 +
 arch/microblaze/include/asm/syscall.h         |   7 +
 arch/mips/include/asm/syscall.h               |  53 +++
 arch/nios2/include/asm/syscall.h              |  16 +
 arch/openrisc/include/asm/syscall.h           |  13 +
 arch/parisc/include/asm/syscall.h             |  19 +
 arch/powerpc/include/asm/syscall.h            |  15 +
 arch/riscv/include/asm/syscall.h              |  16 +
 arch/s390/include/asm/syscall.h               |  19 +
 arch/sh/include/asm/syscall_32.h              |  19 +
 arch/sparc/include/asm/syscall.h              |  17 +
 arch/um/include/asm/syscall-generic.h         |  19 +
 arch/x86/include/asm/syscall.h                |  43 ++
 arch/xtensa/include/asm/syscall.h             |  18 +
 include/asm-generic/syscall.h                 |  30 ++
 include/linux/ptrace.h                        |   3 +
 include/uapi/linux/ptrace.h                   |   3 +-
 kernel/ptrace.c                               | 154 ++++++-
 tools/testing/selftests/ptrace/Makefile       |   2 +-
 .../selftests/ptrace/set_syscall_info.c       | 436 ++++++++++++++++++
 26 files changed, 994 insertions(+), 22 deletions(-)
 create mode 100644 tools/testing/selftests/ptrace/set_syscall_info.c

-- 
ldv

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH 1/6] Revert "arch: remove unused function syscall_set_arguments()"
  2025-01-07 23:01 [PATCH 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO API Dmitry V. Levin
@ 2025-01-07 23:04 ` Dmitry V. Levin
  2025-01-07 23:04 ` [PATCH 2/6] syscall.h: add syscall_set_arguments() on remaining HAVE_ARCH_TRACEHOOK arches Dmitry V. Levin
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 22+ messages in thread
From: Dmitry V. Levin @ 2025-01-07 23:04 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, Dinh Nguyen, Jonas Bonn,
	Stefan Kristiansson, Stafford Horne, Michael Ellerman,
	Nicholas Piggin, Christophe Leroy, Naveen N Rao,
	Madhavan Srinivasan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger, Sven Schnelle, Yoshinori Sato, Rich Felker,
	John Paul Adrian Glaubitz, David S. Miller, Andreas Larsson,
	Richard Weinberger, Anton Ivanov, Johannes Berg, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Chris Zankel, Max Filippov, Arnd Bergmann, linux-kernel,
	linux-openrisc, linuxppc-dev, linux-riscv, linux-s390, linux-sh,
	sparclinux, linux-um, linux-arch

syscall_set_arguments() is going to be needed to implement
PTRACE_SET_SYSCALL_INFO API.

This is a partial revert: on a few architectures syscall_set_arguments()
implementations were buggy, so instead of adding them here and applying
fixes on top, cleaner implementations are coming in follow-up commits.

This reverts commit 7962c2eddbfe7cce879acb06f9b4f205789e57b7.

Signed-off-by: Dmitry V. Levin <ldv@strace.io>
---
 arch/nios2/include/asm/syscall.h      | 11 ++++++++
 arch/openrisc/include/asm/syscall.h   |  7 ++++++
 arch/powerpc/include/asm/syscall.h    | 10 ++++++++
 arch/riscv/include/asm/syscall.h      |  9 +++++++
 arch/s390/include/asm/syscall.h       | 12 +++++++++
 arch/sh/include/asm/syscall_32.h      | 12 +++++++++
 arch/sparc/include/asm/syscall.h      | 10 ++++++++
 arch/um/include/asm/syscall-generic.h | 14 +++++++++++
 arch/x86/include/asm/syscall.h        | 36 +++++++++++++++++++++++++++
 arch/xtensa/include/asm/syscall.h     | 11 ++++++++
 include/asm-generic/syscall.h         | 16 ++++++++++++
 11 files changed, 148 insertions(+)

diff --git a/arch/nios2/include/asm/syscall.h b/arch/nios2/include/asm/syscall.h
index fff52205fb65..526449edd768 100644
--- a/arch/nios2/include/asm/syscall.h
+++ b/arch/nios2/include/asm/syscall.h
@@ -58,6 +58,17 @@ static inline void syscall_get_arguments(struct task_struct *task,
 	*args   = regs->r9;
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+	struct pt_regs *regs, const unsigned long *args)
+{
+	regs->r4 = *args++;
+	regs->r5 = *args++;
+	regs->r6 = *args++;
+	regs->r7 = *args++;
+	regs->r8 = *args++;
+	regs->r9 = *args;
+}
+
 static inline int syscall_get_arch(struct task_struct *task)
 {
 	return AUDIT_ARCH_NIOS2;
diff --git a/arch/openrisc/include/asm/syscall.h b/arch/openrisc/include/asm/syscall.h
index 903ed882bdec..e6383be2a195 100644
--- a/arch/openrisc/include/asm/syscall.h
+++ b/arch/openrisc/include/asm/syscall.h
@@ -57,6 +57,13 @@ syscall_get_arguments(struct task_struct *task, struct pt_regs *regs,
 	memcpy(args, &regs->gpr[3], 6 * sizeof(args[0]));
 }
 
+static inline void
+syscall_set_arguments(struct task_struct *task, struct pt_regs *regs,
+		      const unsigned long *args)
+{
+	memcpy(&regs->gpr[3], args, 6 * sizeof(args[0]));
+}
+
 static inline int syscall_get_arch(struct task_struct *task)
 {
 	return AUDIT_ARCH_OPENRISC;
diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h
index 3dd36c5e334a..b2715448a660 100644
--- a/arch/powerpc/include/asm/syscall.h
+++ b/arch/powerpc/include/asm/syscall.h
@@ -110,6 +110,16 @@ static inline void syscall_get_arguments(struct task_struct *task,
 	}
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 const unsigned long *args)
+{
+	memcpy(&regs->gpr[3], args, 6 * sizeof(args[0]));
+
+	/* Also copy the first argument into orig_gpr3 */
+	regs->orig_gpr3 = args[0];
+}
+
 static inline int syscall_get_arch(struct task_struct *task)
 {
 	if (is_tsk_32bit_task(task))
diff --git a/arch/riscv/include/asm/syscall.h b/arch/riscv/include/asm/syscall.h
index 121fff429dce..8d389ba995c8 100644
--- a/arch/riscv/include/asm/syscall.h
+++ b/arch/riscv/include/asm/syscall.h
@@ -66,6 +66,15 @@ static inline void syscall_get_arguments(struct task_struct *task,
 	memcpy(args, &regs->a1, 5 * sizeof(args[0]));
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 const unsigned long *args)
+{
+	regs->orig_a0 = args[0];
+	args++;
+	memcpy(&regs->a1, args, 5 * sizeof(regs->a1));
+}
+
 static inline int syscall_get_arch(struct task_struct *task)
 {
 #ifdef CONFIG_64BIT
diff --git a/arch/s390/include/asm/syscall.h b/arch/s390/include/asm/syscall.h
index 27e3d804b311..b3dd883699e7 100644
--- a/arch/s390/include/asm/syscall.h
+++ b/arch/s390/include/asm/syscall.h
@@ -78,6 +78,18 @@ static inline void syscall_get_arguments(struct task_struct *task,
 	args[0] = regs->orig_gpr2 & mask;
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 const unsigned long *args)
+{
+	unsigned int n = 6;
+
+	while (n-- > 0)
+		if (n > 0)
+			regs->gprs[2 + n] = args[n];
+	regs->orig_gpr2 = args[0];
+}
+
 static inline int syscall_get_arch(struct task_struct *task)
 {
 #ifdef CONFIG_COMPAT
diff --git a/arch/sh/include/asm/syscall_32.h b/arch/sh/include/asm/syscall_32.h
index d87738eebe30..cb51a7528384 100644
--- a/arch/sh/include/asm/syscall_32.h
+++ b/arch/sh/include/asm/syscall_32.h
@@ -57,6 +57,18 @@ static inline void syscall_get_arguments(struct task_struct *task,
 	args[0] = regs->regs[4];
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 const unsigned long *args)
+{
+	regs->regs[1] = args[5];
+	regs->regs[0] = args[4];
+	regs->regs[7] = args[3];
+	regs->regs[6] = args[2];
+	regs->regs[5] = args[1];
+	regs->regs[4] = args[0];
+}
+
 static inline int syscall_get_arch(struct task_struct *task)
 {
 	int arch = AUDIT_ARCH_SH;
diff --git a/arch/sparc/include/asm/syscall.h b/arch/sparc/include/asm/syscall.h
index 20c109ac8cc9..62a5a78804c4 100644
--- a/arch/sparc/include/asm/syscall.h
+++ b/arch/sparc/include/asm/syscall.h
@@ -117,6 +117,16 @@ static inline void syscall_get_arguments(struct task_struct *task,
 	}
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 const unsigned long *args)
+{
+	unsigned int i;
+
+	for (i = 0; i < 6; i++)
+		regs->u_regs[UREG_I0 + i] = args[i];
+}
+
 static inline int syscall_get_arch(struct task_struct *task)
 {
 #if defined(CONFIG_SPARC64) && defined(CONFIG_COMPAT)
diff --git a/arch/um/include/asm/syscall-generic.h b/arch/um/include/asm/syscall-generic.h
index 172b74143c4b..2984feb9d576 100644
--- a/arch/um/include/asm/syscall-generic.h
+++ b/arch/um/include/asm/syscall-generic.h
@@ -62,6 +62,20 @@ static inline void syscall_get_arguments(struct task_struct *task,
 	*args   = UPT_SYSCALL_ARG6(r);
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 const unsigned long *args)
+{
+	struct uml_pt_regs *r = &regs->regs;
+
+	UPT_SYSCALL_ARG1(r) = *args++;
+	UPT_SYSCALL_ARG2(r) = *args++;
+	UPT_SYSCALL_ARG3(r) = *args++;
+	UPT_SYSCALL_ARG4(r) = *args++;
+	UPT_SYSCALL_ARG5(r) = *args++;
+	UPT_SYSCALL_ARG6(r) = *args;
+}
+
 /* See arch/x86/um/asm/syscall.h for syscall_get_arch() definition. */
 
 #endif	/* __UM_SYSCALL_GENERIC_H */
diff --git a/arch/x86/include/asm/syscall.h b/arch/x86/include/asm/syscall.h
index 7c488ff0c764..b9c249dd9e3d 100644
--- a/arch/x86/include/asm/syscall.h
+++ b/arch/x86/include/asm/syscall.h
@@ -90,6 +90,18 @@ static inline void syscall_get_arguments(struct task_struct *task,
 	args[5] = regs->bp;
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 const unsigned long *args)
+{
+	regs->bx = args[0];
+	regs->cx = args[1];
+	regs->dx = args[2];
+	regs->si = args[3];
+	regs->di = args[4];
+	regs->bp = args[5];
+}
+
 static inline int syscall_get_arch(struct task_struct *task)
 {
 	return AUDIT_ARCH_I386;
@@ -121,6 +133,30 @@ static inline void syscall_get_arguments(struct task_struct *task,
 	}
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 const unsigned long *args)
+{
+# ifdef CONFIG_IA32_EMULATION
+	if (task->thread_info.status & TS_COMPAT) {
+		regs->bx = *args++;
+		regs->cx = *args++;
+		regs->dx = *args++;
+		regs->si = *args++;
+		regs->di = *args++;
+		regs->bp = *args;
+	} else
+# endif
+	{
+		regs->di = *args++;
+		regs->si = *args++;
+		regs->dx = *args++;
+		regs->r10 = *args++;
+		regs->r8 = *args++;
+		regs->r9 = *args;
+	}
+}
+
 static inline int syscall_get_arch(struct task_struct *task)
 {
 	/* x32 tasks should be considered AUDIT_ARCH_X86_64. */
diff --git a/arch/xtensa/include/asm/syscall.h b/arch/xtensa/include/asm/syscall.h
index 5ee974bf8330..f9a671cbf933 100644
--- a/arch/xtensa/include/asm/syscall.h
+++ b/arch/xtensa/include/asm/syscall.h
@@ -68,6 +68,17 @@ static inline void syscall_get_arguments(struct task_struct *task,
 		args[i] = regs->areg[reg[i]];
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 const unsigned long *args)
+{
+	static const unsigned int reg[] = XTENSA_SYSCALL_ARGUMENT_REGS;
+	unsigned int i;
+
+	for (i = 0; i < 6; ++i)
+		regs->areg[reg[i]] = args[i];
+}
+
 asmlinkage long xtensa_rt_sigreturn(void);
 asmlinkage long xtensa_shmat(int, char __user *, int);
 asmlinkage long xtensa_fadvise64_64(int, int,
diff --git a/include/asm-generic/syscall.h b/include/asm-generic/syscall.h
index 5a80fe728dc8..0f7b9a493de7 100644
--- a/include/asm-generic/syscall.h
+++ b/include/asm-generic/syscall.h
@@ -117,6 +117,22 @@ void syscall_set_return_value(struct task_struct *task, struct pt_regs *regs,
 void syscall_get_arguments(struct task_struct *task, struct pt_regs *regs,
 			   unsigned long *args);
 
+/**
+ * syscall_set_arguments - change system call parameter value
+ * @task:	task of interest, must be in system call entry tracing
+ * @regs:	task_pt_regs() of @task
+ * @args:	array of argument values to store
+ *
+ * Changes 6 arguments to the system call.
+ * The first argument gets value @args[0], and so on.
+ *
+ * It's only valid to call this when @task is stopped for tracing on
+ * entry to a system call, due to %SYSCALL_WORK_SYSCALL_TRACE or
+ * %SYSCALL_WORK_SYSCALL_AUDIT.
+ */
+void syscall_set_arguments(struct task_struct *task, struct pt_regs *regs,
+			   const unsigned long *args);
+
 /**
  * syscall_get_arch - return the AUDIT_ARCH for the current system call
  * @task:	task of interest, must be blocked

-- 
ldv

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 2/6] syscall.h: add syscall_set_arguments() on remaining HAVE_ARCH_TRACEHOOK arches
  2025-01-07 23:01 [PATCH 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO API Dmitry V. Levin
  2025-01-07 23:04 ` [PATCH 1/6] Revert "arch: remove unused function syscall_set_arguments()" Dmitry V. Levin
@ 2025-01-07 23:04 ` Dmitry V. Levin
  2025-01-09 12:04   ` Oleg Nesterov
  2025-01-07 23:04 ` [PATCH 3/6] syscall.h: introduce syscall_set_nr() Dmitry V. Levin
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 22+ messages in thread
From: Dmitry V. Levin @ 2025-01-07 23:04 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, Vineet Gupta, Russell King,
	Catalin Marinas, Will Deacon, Guo Ren, Brian Cain, Huacai Chen,
	WANG Xuerui, Thomas Bogendoerfer, James E.J. Bottomley,
	Helge Deller, linux-snps-arc, linux-kernel, linux-arm-kernel,
	linux-csky, linux-hexagon, loongarch, linux-mips, linux-parisc

Add syscall_set_arguments() on those few HAVE_ARCH_TRACEHOOK
architectures that haven't provided it yet.

syscall_set_arguments() is going to be needed on all HAVE_ARCH_TRACEHOOK
architectures to implement PTRACE_SET_SYSCALL_INFO API.

Signed-off-by: Dmitry V. Levin <ldv@strace.io>
---
 arch/arc/include/asm/syscall.h       | 14 +++++++++
 arch/arm/include/asm/syscall.h       | 13 ++++++++
 arch/arm64/include/asm/syscall.h     | 13 ++++++++
 arch/csky/include/asm/syscall.h      | 13 ++++++++
 arch/hexagon/include/asm/syscall.h   |  7 +++++
 arch/loongarch/include/asm/syscall.h |  8 +++++
 arch/mips/include/asm/syscall.h      | 46 ++++++++++++++++++++++++++++
 arch/parisc/include/asm/syscall.h    | 12 ++++++++
 8 files changed, 126 insertions(+)

diff --git a/arch/arc/include/asm/syscall.h b/arch/arc/include/asm/syscall.h
index 9709256e31c8..89c1e1736356 100644
--- a/arch/arc/include/asm/syscall.h
+++ b/arch/arc/include/asm/syscall.h
@@ -67,6 +67,20 @@ syscall_get_arguments(struct task_struct *task, struct pt_regs *regs,
 	}
 }
 
+static inline void
+syscall_set_arguments(struct task_struct *task, struct pt_regs *regs,
+		      unsigned long *args)
+{
+	unsigned long *inside_ptregs = &regs->r0;
+	unsigned int n = 6;
+	unsigned int i = 0;
+
+	while (n--) {
+		*inside_ptregs = args[i++];
+		inside_ptregs--;
+	}
+}
+
 static inline int
 syscall_get_arch(struct task_struct *task)
 {
diff --git a/arch/arm/include/asm/syscall.h b/arch/arm/include/asm/syscall.h
index fe4326d938c1..21927fa0ae2b 100644
--- a/arch/arm/include/asm/syscall.h
+++ b/arch/arm/include/asm/syscall.h
@@ -80,6 +80,19 @@ static inline void syscall_get_arguments(struct task_struct *task,
 	memcpy(args, &regs->ARM_r0 + 1, 5 * sizeof(args[0]));
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 const unsigned long *args)
+{
+	memcpy(&regs->ARM_r0, args, 6 * sizeof(args[0]));
+	/*
+	 * Also copy the first argument into ARM_ORIG_r0
+	 * so that syscall_get_arguments() would return it
+	 * instead of the previous value.
+	 */
+	regs->ARM_ORIG_r0 = regs->ARM_r0;
+}
+
 static inline int syscall_get_arch(struct task_struct *task)
 {
 	/* ARM tasks don't change audit architectures on the fly. */
diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h
index ab8e14b96f68..76020b66286b 100644
--- a/arch/arm64/include/asm/syscall.h
+++ b/arch/arm64/include/asm/syscall.h
@@ -73,6 +73,19 @@ static inline void syscall_get_arguments(struct task_struct *task,
 	memcpy(args, &regs->regs[1], 5 * sizeof(args[0]));
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 const unsigned long *args)
+{
+	memcpy(&regs->regs[0], args, 6 * sizeof(args[0]));
+	/*
+	 * Also copy the first argument into orig_x0
+	 * so that syscall_get_arguments() would return it
+	 * instead of the previous value.
+	 */
+	regs->orig_x0 = regs->regs[0];
+}
+
 /*
  * We don't care about endianness (__AUDIT_ARCH_LE bit) here because
  * AArch64 has the same system calls both on little- and big- endian.
diff --git a/arch/csky/include/asm/syscall.h b/arch/csky/include/asm/syscall.h
index 0de5734950bf..30403f7a0487 100644
--- a/arch/csky/include/asm/syscall.h
+++ b/arch/csky/include/asm/syscall.h
@@ -59,6 +59,19 @@ syscall_get_arguments(struct task_struct *task, struct pt_regs *regs,
 	memcpy(args, &regs->a1, 5 * sizeof(args[0]));
 }
 
+static inline void
+syscall_set_arguments(struct task_struct *task, struct pt_regs *regs,
+		      const unsigned long *args)
+{
+	memcpy(&regs->a0, args, 6 * sizeof(regs->a0));
+	/*
+	 * Also copy the first argument into orig_x0
+	 * so that syscall_get_arguments() would return it
+	 * instead of the previous value.
+	 */
+	regs->orig_a0 = regs->a0;
+}
+
 static inline int
 syscall_get_arch(struct task_struct *task)
 {
diff --git a/arch/hexagon/include/asm/syscall.h b/arch/hexagon/include/asm/syscall.h
index f6e454f18038..12d2df6aaa03 100644
--- a/arch/hexagon/include/asm/syscall.h
+++ b/arch/hexagon/include/asm/syscall.h
@@ -33,6 +33,13 @@ static inline void syscall_get_arguments(struct task_struct *task,
 	memcpy(args, &(&regs->r00)[0], 6 * sizeof(args[0]));
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 unsigned long *args)
+{
+	memcpy(&(&regs->r00)[0], args, 6 * sizeof(args[0]));
+}
+
 static inline long syscall_get_error(struct task_struct *task,
 				     struct pt_regs *regs)
 {
diff --git a/arch/loongarch/include/asm/syscall.h b/arch/loongarch/include/asm/syscall.h
index e286dc58476e..ff415b3c0a8e 100644
--- a/arch/loongarch/include/asm/syscall.h
+++ b/arch/loongarch/include/asm/syscall.h
@@ -61,6 +61,14 @@ static inline void syscall_get_arguments(struct task_struct *task,
 	memcpy(&args[1], &regs->regs[5], 5 * sizeof(long));
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 unsigned long *args)
+{
+	regs->orig_a0 = args[0];
+	memcpy(&regs->regs[5], &args[1], 5 * sizeof(long));
+}
+
 static inline int syscall_get_arch(struct task_struct *task)
 {
 	return AUDIT_ARCH_LOONGARCH64;
diff --git a/arch/mips/include/asm/syscall.h b/arch/mips/include/asm/syscall.h
index ebdf4d910af2..b8a19e465bf4 100644
--- a/arch/mips/include/asm/syscall.h
+++ b/arch/mips/include/asm/syscall.h
@@ -90,6 +90,37 @@ static inline void mips_get_syscall_arg(unsigned long *arg,
 	unreachable();
 }
 
+static inline void mips_set_syscall_arg(unsigned long *arg,
+	struct task_struct *task, struct pt_regs *regs, unsigned int n)
+{
+	unsigned long usp __maybe_unused = regs->regs[29];
+
+	switch (n) {
+	case 0: case 1: case 2: case 3:
+		regs->regs[4 + n] = *arg;
+
+		return;
+
+#ifdef CONFIG_32BIT
+	case 4: case 5: case 6: case 7:
+		put_user(*arg, (int *)usp + n);
+		return;
+#endif
+
+#ifdef CONFIG_64BIT
+	case 4: case 5: case 6: case 7:
+#ifdef CONFIG_MIPS32_O32
+		if (test_tsk_thread_flag(task, TIF_32BIT_REGS))
+			put_user(*arg, (int *)usp + n);
+		else
+#endif
+			regs->regs[4 + n] = *arg;
+
+		return;
+#endif
+	}
+}
+
 static inline long syscall_get_error(struct task_struct *task,
 				     struct pt_regs *regs)
 {
@@ -136,6 +167,21 @@ static inline void syscall_get_arguments(struct task_struct *task,
 		mips_get_syscall_arg(args++, task, regs, i++);
 }
 
+static inline void syscall_set_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 unsigned long *args)
+{
+	unsigned int i = 0;
+	unsigned int n = 6;
+
+	/* O32 ABI syscall() */
+	if (mips_syscall_is_indirect(task, regs))
+		i++;
+
+	while (n--)
+		mips_set_syscall_arg(args++, task, regs, i++);
+}
+
 extern const unsigned long sys_call_table[];
 extern const unsigned long sys32_call_table[];
 extern const unsigned long sysn32_call_table[];
diff --git a/arch/parisc/include/asm/syscall.h b/arch/parisc/include/asm/syscall.h
index 00b127a5e09b..b146d0ae4c77 100644
--- a/arch/parisc/include/asm/syscall.h
+++ b/arch/parisc/include/asm/syscall.h
@@ -29,6 +29,18 @@ static inline void syscall_get_arguments(struct task_struct *tsk,
 	args[0] = regs->gr[26];
 }
 
+static inline void syscall_set_arguments(struct task_struct *tsk,
+					 struct pt_regs *regs,
+					 unsigned long *args)
+{
+	regs->gr[21] = args[5];
+	regs->gr[22] = args[4];
+	regs->gr[23] = args[3];
+	regs->gr[24] = args[2];
+	regs->gr[25] = args[1];
+	regs->gr[26] = args[0];
+}
+
 static inline long syscall_get_error(struct task_struct *task,
 				     struct pt_regs *regs)
 {

-- 
ldv

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 3/6] syscall.h: introduce syscall_set_nr()
  2025-01-07 23:01 [PATCH 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO API Dmitry V. Levin
  2025-01-07 23:04 ` [PATCH 1/6] Revert "arch: remove unused function syscall_set_arguments()" Dmitry V. Levin
  2025-01-07 23:04 ` [PATCH 2/6] syscall.h: add syscall_set_arguments() on remaining HAVE_ARCH_TRACEHOOK arches Dmitry V. Levin
@ 2025-01-07 23:04 ` Dmitry V. Levin
  2025-01-10  7:37   ` Sven Schnelle
  2025-01-07 23:04 ` [PATCH 4/6] ptrace_get_syscall_info: factor out ptrace_get_syscall_info_op Dmitry V. Levin
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 22+ messages in thread
From: Dmitry V. Levin @ 2025-01-07 23:04 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, Vineet Gupta, Russell King,
	Catalin Marinas, Will Deacon, Brian Cain, Huacai Chen,
	WANG Xuerui, Geert Uytterhoeven, Michal Simek,
	Thomas Bogendoerfer, Dinh Nguyen, Jonas Bonn, Stefan Kristiansson,
	Stafford Horne, James E.J. Bottomley, Helge Deller,
	Michael Ellerman, Nicholas Piggin, Christophe Leroy, Naveen N Rao,
	Madhavan Srinivasan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger, Sven Schnelle, Yoshinori Sato, Rich Felker,
	John Paul Adrian Glaubitz, David S. Miller, Andreas Larsson,
	Richard Weinberger, Anton Ivanov, Johannes Berg, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Chris Zankel, Max Filippov, Arnd Bergmann, linux-snps-arc,
	linux-kernel, linux-arm-kernel, linux-hexagon, loongarch,
	linux-m68k, linux-mips, linux-openrisc, linux-parisc,
	linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
	linux-um, linux-arch

Similar to syscall_set_arguments() that complements
syscall_get_arguments(), introduce syscall_set_nr()
that complements syscall_get_nr().

syscall_set_nr() is going to be needed along with
syscall_set_arguments() on all HAVE_ARCH_TRACEHOOK
architectures to implement PTRACE_SET_SYSCALL_INFO API.

Signed-off-by: Dmitry V. Levin <ldv@strace.io>
---
 arch/arc/include/asm/syscall.h        |  6 ++++++
 arch/arm/include/asm/syscall.h        | 12 ++++++++++++
 arch/arm64/include/asm/syscall.h      |  7 +++++++
 arch/hexagon/include/asm/syscall.h    |  7 +++++++
 arch/loongarch/include/asm/syscall.h  |  7 +++++++
 arch/m68k/include/asm/syscall.h       |  7 +++++++
 arch/microblaze/include/asm/syscall.h |  7 +++++++
 arch/mips/include/asm/syscall.h       |  7 +++++++
 arch/nios2/include/asm/syscall.h      |  5 +++++
 arch/openrisc/include/asm/syscall.h   |  6 ++++++
 arch/parisc/include/asm/syscall.h     |  7 +++++++
 arch/powerpc/include/asm/syscall.h    |  5 +++++
 arch/riscv/include/asm/syscall.h      |  7 +++++++
 arch/s390/include/asm/syscall.h       |  7 +++++++
 arch/sh/include/asm/syscall_32.h      |  7 +++++++
 arch/sparc/include/asm/syscall.h      |  7 +++++++
 arch/um/include/asm/syscall-generic.h |  5 +++++
 arch/x86/include/asm/syscall.h        |  7 +++++++
 arch/xtensa/include/asm/syscall.h     |  7 +++++++
 include/asm-generic/syscall.h         | 14 ++++++++++++++
 20 files changed, 144 insertions(+)

diff --git a/arch/arc/include/asm/syscall.h b/arch/arc/include/asm/syscall.h
index 89c1e1736356..6095fbfa74ab 100644
--- a/arch/arc/include/asm/syscall.h
+++ b/arch/arc/include/asm/syscall.h
@@ -23,6 +23,12 @@ syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
 		return -1;
 }
 
+static inline void
+syscall_set_nr(struct task_struct *task, struct pt_regs *regs, int nr)
+{
+	regs->r8 = nr;
+}
+
 static inline void
 syscall_rollback(struct task_struct *task, struct pt_regs *regs)
 {
diff --git a/arch/arm/include/asm/syscall.h b/arch/arm/include/asm/syscall.h
index 21927fa0ae2b..cfa61f355675 100644
--- a/arch/arm/include/asm/syscall.h
+++ b/arch/arm/include/asm/syscall.h
@@ -31,6 +31,18 @@ static inline int syscall_get_nr(struct task_struct *task,
 	return task_thread_info(task)->abi_syscall & __NR_SYSCALL_MASK;
 }
 
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	if (!IS_ENABLED(CONFIG_AEABI) || IS_ENABLED(CONFIG_OABI_COMPAT)) {
+		if (nr != -1)
+			nr &= __NR_SYSCALL_MASK;
+	}
+
+	task_thread_info(task)->abi_syscall = nr;
+}
+
 static inline bool __in_oabi_syscall(struct task_struct *task)
 {
 	return IS_ENABLED(CONFIG_OABI_COMPAT) &&
diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h
index 76020b66286b..0e6807618bed 100644
--- a/arch/arm64/include/asm/syscall.h
+++ b/arch/arm64/include/asm/syscall.h
@@ -23,6 +23,13 @@ static inline int syscall_get_nr(struct task_struct *task,
 	return regs->syscallno;
 }
 
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	regs->syscallno = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
 				    struct pt_regs *regs)
 {
diff --git a/arch/hexagon/include/asm/syscall.h b/arch/hexagon/include/asm/syscall.h
index 12d2df6aaa03..72befa719434 100644
--- a/arch/hexagon/include/asm/syscall.h
+++ b/arch/hexagon/include/asm/syscall.h
@@ -26,6 +26,13 @@ static inline long syscall_get_nr(struct task_struct *task,
 	return regs->r06;
 }
 
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	regs->r06 = nr;
+}
+
 static inline void syscall_get_arguments(struct task_struct *task,
 					 struct pt_regs *regs,
 					 unsigned long *args)
diff --git a/arch/loongarch/include/asm/syscall.h b/arch/loongarch/include/asm/syscall.h
index ff415b3c0a8e..81d2733f7b94 100644
--- a/arch/loongarch/include/asm/syscall.h
+++ b/arch/loongarch/include/asm/syscall.h
@@ -26,6 +26,13 @@ static inline long syscall_get_nr(struct task_struct *task,
 	return regs->regs[11];
 }
 
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	regs->regs[11] = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
 				    struct pt_regs *regs)
 {
diff --git a/arch/m68k/include/asm/syscall.h b/arch/m68k/include/asm/syscall.h
index d1453e850cdd..bf84b160c2eb 100644
--- a/arch/m68k/include/asm/syscall.h
+++ b/arch/m68k/include/asm/syscall.h
@@ -14,6 +14,13 @@ static inline int syscall_get_nr(struct task_struct *task,
 	return regs->orig_d0;
 }
 
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	regs->orig_d0 = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
 				    struct pt_regs *regs)
 {
diff --git a/arch/microblaze/include/asm/syscall.h b/arch/microblaze/include/asm/syscall.h
index 5eb3f624cc59..b5b6b91fae3e 100644
--- a/arch/microblaze/include/asm/syscall.h
+++ b/arch/microblaze/include/asm/syscall.h
@@ -14,6 +14,13 @@ static inline long syscall_get_nr(struct task_struct *task,
 	return regs->r12;
 }
 
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	regs->r12 = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
 				    struct pt_regs *regs)
 {
diff --git a/arch/mips/include/asm/syscall.h b/arch/mips/include/asm/syscall.h
index b8a19e465bf4..793026c4c7f0 100644
--- a/arch/mips/include/asm/syscall.h
+++ b/arch/mips/include/asm/syscall.h
@@ -41,6 +41,13 @@ static inline long syscall_get_nr(struct task_struct *task,
 	return task_thread_info(task)->syscall;
 }
 
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	task_thread_info(task)->syscall = nr;
+}
+
 static inline void mips_syscall_update_nr(struct task_struct *task,
 					  struct pt_regs *regs)
 {
diff --git a/arch/nios2/include/asm/syscall.h b/arch/nios2/include/asm/syscall.h
index 526449edd768..8e3eb1d689bb 100644
--- a/arch/nios2/include/asm/syscall.h
+++ b/arch/nios2/include/asm/syscall.h
@@ -15,6 +15,11 @@ static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
 	return regs->r2;
 }
 
+static inline void syscall_set_nr(struct task_struct *task, struct pt_regs *regs, int nr)
+{
+	regs->r2 = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
 				struct pt_regs *regs)
 {
diff --git a/arch/openrisc/include/asm/syscall.h b/arch/openrisc/include/asm/syscall.h
index e6383be2a195..5e037d9659c5 100644
--- a/arch/openrisc/include/asm/syscall.h
+++ b/arch/openrisc/include/asm/syscall.h
@@ -25,6 +25,12 @@ syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
 	return regs->orig_gpr11;
 }
 
+static inline void
+syscall_set_nr(struct task_struct *task, struct pt_regs *regs, int nr)
+{
+	regs->orig_gpr11 = nr;
+}
+
 static inline void
 syscall_rollback(struct task_struct *task, struct pt_regs *regs)
 {
diff --git a/arch/parisc/include/asm/syscall.h b/arch/parisc/include/asm/syscall.h
index b146d0ae4c77..c11222798ab2 100644
--- a/arch/parisc/include/asm/syscall.h
+++ b/arch/parisc/include/asm/syscall.h
@@ -17,6 +17,13 @@ static inline long syscall_get_nr(struct task_struct *tsk,
 	return regs->gr[20];
 }
 
+static inline void syscall_set_nr(struct task_struct *tsk,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	regs->gr[20] = nr;
+}
+
 static inline void syscall_get_arguments(struct task_struct *tsk,
 					 struct pt_regs *regs,
 					 unsigned long *args)
diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h
index b2715448a660..09e34d19c961 100644
--- a/arch/powerpc/include/asm/syscall.h
+++ b/arch/powerpc/include/asm/syscall.h
@@ -39,6 +39,11 @@ static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
 		return -1;
 }
 
+static inline void syscall_set_nr(struct task_struct *task, struct pt_regs *regs, int nr)
+{
+	regs->gpr[0] = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
 				    struct pt_regs *regs)
 {
diff --git a/arch/riscv/include/asm/syscall.h b/arch/riscv/include/asm/syscall.h
index 8d389ba995c8..a5281cdf2b10 100644
--- a/arch/riscv/include/asm/syscall.h
+++ b/arch/riscv/include/asm/syscall.h
@@ -30,6 +30,13 @@ static inline int syscall_get_nr(struct task_struct *task,
 	return regs->a7;
 }
 
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	regs->a7 = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
 				    struct pt_regs *regs)
 {
diff --git a/arch/s390/include/asm/syscall.h b/arch/s390/include/asm/syscall.h
index b3dd883699e7..1c0e349fd5c9 100644
--- a/arch/s390/include/asm/syscall.h
+++ b/arch/s390/include/asm/syscall.h
@@ -24,6 +24,13 @@ static inline long syscall_get_nr(struct task_struct *task,
 		(regs->int_code & 0xffff) : -1;
 }
 
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	regs->int_code = (regs->int_code & ~0xffff) | (nr & 0xffff);
+}
+
 static inline void syscall_rollback(struct task_struct *task,
 				    struct pt_regs *regs)
 {
diff --git a/arch/sh/include/asm/syscall_32.h b/arch/sh/include/asm/syscall_32.h
index cb51a7528384..0e1e7b029457 100644
--- a/arch/sh/include/asm/syscall_32.h
+++ b/arch/sh/include/asm/syscall_32.h
@@ -15,6 +15,13 @@ static inline long syscall_get_nr(struct task_struct *task,
 	return (regs->tra >= 0) ? regs->regs[3] : -1L;
 }
 
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	regs->regs[3] = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
 				    struct pt_regs *regs)
 {
diff --git a/arch/sparc/include/asm/syscall.h b/arch/sparc/include/asm/syscall.h
index 62a5a78804c4..36830a37fda4 100644
--- a/arch/sparc/include/asm/syscall.h
+++ b/arch/sparc/include/asm/syscall.h
@@ -25,6 +25,13 @@ static inline long syscall_get_nr(struct task_struct *task,
 	return (syscall_p ? regs->u_regs[UREG_G1] : -1L);
 }
 
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	regs->u_regs[UREG_G1] = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
 				    struct pt_regs *regs)
 {
diff --git a/arch/um/include/asm/syscall-generic.h b/arch/um/include/asm/syscall-generic.h
index 2984feb9d576..bcd73bcfe577 100644
--- a/arch/um/include/asm/syscall-generic.h
+++ b/arch/um/include/asm/syscall-generic.h
@@ -21,6 +21,11 @@ static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
 	return PT_REGS_SYSCALL_NR(regs);
 }
 
+static inline void syscall_set_nr(struct task_struct *task, struct pt_regs *regs, int nr)
+{
+	PT_REGS_SYSCALL_NR(regs) = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
 				    struct pt_regs *regs)
 {
diff --git a/arch/x86/include/asm/syscall.h b/arch/x86/include/asm/syscall.h
index b9c249dd9e3d..c10dbb74cd00 100644
--- a/arch/x86/include/asm/syscall.h
+++ b/arch/x86/include/asm/syscall.h
@@ -38,6 +38,13 @@ static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
 	return regs->orig_ax;
 }
 
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	regs->orig_ax = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
 				    struct pt_regs *regs)
 {
diff --git a/arch/xtensa/include/asm/syscall.h b/arch/xtensa/include/asm/syscall.h
index f9a671cbf933..7db3b489c8ad 100644
--- a/arch/xtensa/include/asm/syscall.h
+++ b/arch/xtensa/include/asm/syscall.h
@@ -28,6 +28,13 @@ static inline long syscall_get_nr(struct task_struct *task,
 	return regs->syscall;
 }
 
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int nr)
+{
+	regs->syscall = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
 				    struct pt_regs *regs)
 {
diff --git a/include/asm-generic/syscall.h b/include/asm-generic/syscall.h
index 0f7b9a493de7..e33fd4e783c1 100644
--- a/include/asm-generic/syscall.h
+++ b/include/asm-generic/syscall.h
@@ -37,6 +37,20 @@ struct pt_regs;
  */
 int syscall_get_nr(struct task_struct *task, struct pt_regs *regs);
 
+/**
+ * syscall_set_nr - change the system call a task is executing
+ * @task:	task of interest, must be blocked
+ * @regs:	task_pt_regs() of @task
+ * @nr:		system call number
+ *
+ * Changes the system call number @task is about to execute.
+ *
+ * It's only valid to call this when @task is stopped for tracing on
+ * entry to a system call, due to %SYSCALL_WORK_SYSCALL_TRACE or
+ * %SYSCALL_WORK_SYSCALL_AUDIT.
+ */
+void syscall_set_nr(struct task_struct *task, struct pt_regs *regs, int nr);
+
 /**
  * syscall_rollback - roll back registers after an aborted system call
  * @task:	task of interest, must be in system call exit tracing

-- 
ldv

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 4/6] ptrace_get_syscall_info: factor out ptrace_get_syscall_info_op
  2025-01-07 23:01 [PATCH 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO API Dmitry V. Levin
                   ` (2 preceding siblings ...)
  2025-01-07 23:04 ` [PATCH 3/6] syscall.h: introduce syscall_set_nr() Dmitry V. Levin
@ 2025-01-07 23:04 ` Dmitry V. Levin
  2025-01-09 10:09   ` Oleg Nesterov
  2025-01-09 10:41   ` Oleg Nesterov
  2025-01-07 23:04 ` [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request Dmitry V. Levin
                   ` (2 subsequent siblings)
  6 siblings, 2 replies; 22+ messages in thread
From: Dmitry V. Levin @ 2025-01-07 23:04 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, linux-kernel

Move the code that calculates the type of the system call stop
out of ptrace_get_syscall_info() into a separate function
ptrace_get_syscall_info_op() which is going to be used later
to implement PTRACE_SET_SYSCALL_INFO API.

Signed-off-by: Dmitry V. Levin <ldv@strace.io>
---
 kernel/ptrace.c | 52 ++++++++++++++++++++++++++++++-------------------
 1 file changed, 32 insertions(+), 20 deletions(-)

diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index d5f89f9ef29f..e7e0003cc8e0 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -965,19 +965,8 @@ ptrace_get_syscall_info_exit(struct task_struct *child, struct pt_regs *regs,
 }
 
 static int
-ptrace_get_syscall_info(struct task_struct *child, unsigned long user_size,
-			void __user *datavp)
+ptrace_get_syscall_info_op(struct task_struct *child)
 {
-	struct pt_regs *regs = task_pt_regs(child);
-	struct ptrace_syscall_info info = {
-		.op = PTRACE_SYSCALL_INFO_NONE,
-		.arch = syscall_get_arch(child),
-		.instruction_pointer = instruction_pointer(regs),
-		.stack_pointer = user_stack_pointer(regs),
-	};
-	unsigned long actual_size = offsetof(struct ptrace_syscall_info, entry);
-	unsigned long write_size;
-
 	/*
 	 * This does not need lock_task_sighand() to access
 	 * child->last_siginfo because ptrace_freeze_traced()
@@ -988,18 +977,41 @@ ptrace_get_syscall_info(struct task_struct *child, unsigned long user_size,
 	case SIGTRAP | 0x80:
 		switch (child->ptrace_message) {
 		case PTRACE_EVENTMSG_SYSCALL_ENTRY:
-			actual_size = ptrace_get_syscall_info_entry(child, regs,
-								    &info);
-			break;
+			return PTRACE_SYSCALL_INFO_ENTRY;
 		case PTRACE_EVENTMSG_SYSCALL_EXIT:
-			actual_size = ptrace_get_syscall_info_exit(child, regs,
-								   &info);
-			break;
+			return PTRACE_SYSCALL_INFO_EXIT;
 		}
 		break;
 	case SIGTRAP | (PTRACE_EVENT_SECCOMP << 8):
-		actual_size = ptrace_get_syscall_info_seccomp(child, regs,
-							      &info);
+		return PTRACE_SYSCALL_INFO_SECCOMP;
+	}
+
+	return PTRACE_SYSCALL_INFO_NONE;
+}
+
+static int
+ptrace_get_syscall_info(struct task_struct *child, unsigned long user_size,
+			void __user *datavp)
+{
+	struct pt_regs *regs = task_pt_regs(child);
+	struct ptrace_syscall_info info = {
+		.op = ptrace_get_syscall_info_op(child),
+		.arch = syscall_get_arch(child),
+		.instruction_pointer = instruction_pointer(regs),
+		.stack_pointer = user_stack_pointer(regs),
+	};
+	unsigned long actual_size = offsetof(struct ptrace_syscall_info, entry);
+	unsigned long write_size;
+
+	switch (info.op) {
+	case PTRACE_SYSCALL_INFO_ENTRY:
+		actual_size = ptrace_get_syscall_info_entry(child, regs, &info);
+		break;
+	case PTRACE_SYSCALL_INFO_EXIT:
+		actual_size = ptrace_get_syscall_info_exit(child, regs, &info);
+		break;
+	case PTRACE_SYSCALL_INFO_SECCOMP:
+		actual_size = ptrace_get_syscall_info_seccomp(child, regs, &info);
 		break;
 	}
 
-- 
ldv

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request
  2025-01-07 23:01 [PATCH 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO API Dmitry V. Levin
                   ` (3 preceding siblings ...)
  2025-01-07 23:04 ` [PATCH 4/6] ptrace_get_syscall_info: factor out ptrace_get_syscall_info_op Dmitry V. Levin
@ 2025-01-07 23:04 ` Dmitry V. Levin
  2025-01-09  1:37   ` kernel test robot
                     ` (4 more replies)
  2025-01-07 23:05 ` [PATCH 6/6] selftests/ptrace: add a test case for PTRACE_SET_SYSCALL_INFO Dmitry V. Levin
  2025-01-10  3:15 ` [PATCH 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO API H. Peter Anvin
  6 siblings, 5 replies; 22+ messages in thread
From: Dmitry V. Levin @ 2025-01-07 23:04 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, linux-kernel, linux-api

PTRACE_SET_SYSCALL_INFO is a generic ptrace API that complements
PTRACE_GET_SYSCALL_INFO by letting the ptracer modify details of
system calls the tracee is blocked in.

This API allows ptracers to obtain and modify system call details
in a straightforward and architecture-agnostic way.

Current implementation supports changing only those bits of system call
information that are used by strace, namely, syscall number, syscall
arguments, and syscall return value.

Support of changing additional details returned by PTRACE_GET_SYSCALL_INFO,
such as instruction pointer and stack pointer, could be added later
if needed, by re-using struct ptrace_syscall_info.reserved to specify
the additional details that should be set.  Currently, the reserved
field of struct ptrace_syscall_info must be initialized with zeroes;
arch, instruction_pointer, and stack_pointer fields are ignored.

PTRACE_SET_SYSCALL_INFO currently supports only PTRACE_SYSCALL_INFO_ENTRY,
PTRACE_SYSCALL_INFO_EXIT, and PTRACE_SYSCALL_INFO_SECCOMP operations.
Other operations could be added later if needed.

Ideally, PTRACE_SET_SYSCALL_INFO should have been introduced along with
PTRACE_GET_SYSCALL_INFO, but it didn't happen.  The last straw that
convinced me to implement PTRACE_SET_SYSCALL_INFO was apparent failure
to provide an API of changing the first system call argument on riscv
architecture.

ptrace(2) man page:

long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
...
PTRACE_SET_SYSCALL_INFO
       Modify information about the system call that caused the stop.
       The "data" argument is a pointer to struct ptrace_syscall_info
       that specifies the system call information to be set.
       The "addr" argument should be set to sizeof(struct ptrace_syscall_info)).

Link: https://lore.kernel.org/all/59505464-c84a-403d-972f-d4b2055eeaac@gmail.com/
Signed-off-by: Dmitry V. Levin <ldv@strace.io>
---
 include/linux/ptrace.h      |   3 ++
 include/uapi/linux/ptrace.h |   3 +-
 kernel/ptrace.c             | 102 ++++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+), 1 deletion(-)

diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 90507d4afcd6..c8dbf1e498bf 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -17,6 +17,9 @@ struct syscall_info {
 	struct seccomp_data	data;
 };
 
+/* sizeof() the first published struct ptrace_syscall_info */
+#define PTRACE_SYSCALL_INFO_SIZE_VER0	84
+
 extern int ptrace_access_vm(struct task_struct *tsk, unsigned long addr,
 			    void *buf, int len, unsigned int gup_flags);
 
diff --git a/include/uapi/linux/ptrace.h b/include/uapi/linux/ptrace.h
index 72c038fc71d0..231b8bf7eeff 100644
--- a/include/uapi/linux/ptrace.h
+++ b/include/uapi/linux/ptrace.h
@@ -74,6 +74,7 @@ struct seccomp_metadata {
 };
 
 #define PTRACE_GET_SYSCALL_INFO		0x420e
+#define PTRACE_SET_SYSCALL_INFO		0x4212
 #define PTRACE_SYSCALL_INFO_NONE	0
 #define PTRACE_SYSCALL_INFO_ENTRY	1
 #define PTRACE_SYSCALL_INFO_EXIT	2
@@ -81,7 +82,7 @@ struct seccomp_metadata {
 
 struct ptrace_syscall_info {
 	__u8 op;	/* PTRACE_SYSCALL_INFO_* */
-	__u8 pad[3];
+	__u8 reserved[3];
 	__u32 arch;
 	__u64 instruction_pointer;
 	__u64 stack_pointer;
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index e7e0003cc8e0..52377653743d 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -1018,6 +1018,104 @@ ptrace_get_syscall_info(struct task_struct *child, unsigned long user_size,
 	write_size = min(actual_size, user_size);
 	return copy_to_user(datavp, &info, write_size) ? -EFAULT : actual_size;
 }
+
+static unsigned long
+ptrace_set_syscall_info_entry(struct task_struct *child, struct pt_regs *regs,
+			      struct ptrace_syscall_info *info)
+{
+	unsigned long args[ARRAY_SIZE(info->entry.args)];
+	int nr = info->entry.nr;
+	int i;
+
+	if (nr != info->entry.nr)
+		return -ERANGE;
+
+	for (i = 0; i < ARRAY_SIZE(args); i++) {
+		args[i] = info->entry.args[i];
+		if (args[i] != info->entry.args[i])
+			return -ERANGE;
+	}
+
+	syscall_set_nr(child, regs, nr);
+	syscall_set_arguments(child, regs, args);
+	if (nr == -1) {
+		/*
+		 * When the syscall number is set to -1, the syscall will be
+		 * skipped.  In this case also set the syscall return value to
+		 * -ENOSYS, otherwise on some architectures the corresponding
+		 * struct pt_regs field will remain unchanged.
+		 *
+		 * Note that on some architectures syscall_set_return_value()
+		 * modifies one of the struct pt_regs fields also modified by
+		 * syscall_set_arguments(), so the former should be called
+		 * after the latter.
+		 */
+		syscall_set_return_value(child, regs, -ENOSYS, 0);
+	}
+
+	return 0;
+}
+
+static unsigned long
+ptrace_set_syscall_info_seccomp(struct task_struct *child, struct pt_regs *regs,
+				struct ptrace_syscall_info *info)
+{
+	/*
+	 * info->entry is currently a subset of info->seccomp,
+	 * info->seccomp.ret_data is currently ignored.
+	 */
+	return ptrace_set_syscall_info_entry(child, regs, info);
+}
+
+static unsigned long
+ptrace_set_syscall_info_exit(struct task_struct *child, struct pt_regs *regs,
+			     struct ptrace_syscall_info *info)
+{
+	if (info->exit.is_error)
+		syscall_set_return_value(child, regs, info->exit.rval, 0);
+	else
+		syscall_set_return_value(child, regs, 0, info->exit.rval);
+
+	return 0;
+}
+
+static int
+ptrace_set_syscall_info(struct task_struct *child, unsigned long user_size,
+			void __user *datavp)
+{
+	struct pt_regs *regs = task_pt_regs(child);
+	struct ptrace_syscall_info info;
+	int error;
+
+	BUILD_BUG_ON(sizeof(struct ptrace_syscall_info) < PTRACE_SYSCALL_INFO_SIZE_VER0);
+
+	if (user_size < PTRACE_SYSCALL_INFO_SIZE_VER0 || user_size > PAGE_SIZE)
+		return -EINVAL;
+
+	error = copy_struct_from_user(&info, sizeof(info), datavp, user_size);
+	if (error)
+		return error;
+
+	/* Reserved for future use. */
+	if (memchr_inv(info.reserved, 0, sizeof(info.reserved)))
+		return -EINVAL;
+
+	/* Changing the type of the system call stop is not supported. */
+	if (ptrace_get_syscall_info_op(child) != info.op)
+		return -EINVAL;
+
+	switch (info.op) {
+	case PTRACE_SYSCALL_INFO_ENTRY:
+		return ptrace_set_syscall_info_entry(child, regs, &info);
+	case PTRACE_SYSCALL_INFO_EXIT:
+		return ptrace_set_syscall_info_exit(child, regs, &info);
+	case PTRACE_SYSCALL_INFO_SECCOMP:
+		return ptrace_set_syscall_info_seccomp(child, regs, &info);
+	default:
+		/* Other types of system call stops are not supported. */
+		return -EINVAL;
+	}
+}
 #endif /* CONFIG_HAVE_ARCH_TRACEHOOK */
 
 int ptrace_request(struct task_struct *child, long request,
@@ -1236,6 +1334,10 @@ int ptrace_request(struct task_struct *child, long request,
 	case PTRACE_GET_SYSCALL_INFO:
 		ret = ptrace_get_syscall_info(child, addr, datavp);
 		break;
+
+	case PTRACE_SET_SYSCALL_INFO:
+		ret = ptrace_set_syscall_info(child, addr, datavp);
+		break;
 #endif
 
 	case PTRACE_SECCOMP_GET_FILTER:

-- 
ldv

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 6/6] selftests/ptrace: add a test case for PTRACE_SET_SYSCALL_INFO
  2025-01-07 23:01 [PATCH 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO API Dmitry V. Levin
                   ` (4 preceding siblings ...)
  2025-01-07 23:04 ` [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request Dmitry V. Levin
@ 2025-01-07 23:05 ` Dmitry V. Levin
  2025-01-10  3:15 ` [PATCH 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO API H. Peter Anvin
  6 siblings, 0 replies; 22+ messages in thread
From: Dmitry V. Levin @ 2025-01-07 23:05 UTC (permalink / raw)
  To: Oleg Nesterov, Shuah Khan
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, linux-kernel, linux-kselftest

Check whether PTRACE_SET_SYSCALL_INFO semantics implemented in the
kernel matches userspace expectations.

Signed-off-by: Dmitry V. Levin <ldv@strace.io>
---
 tools/testing/selftests/ptrace/Makefile       |   2 +-
 .../selftests/ptrace/set_syscall_info.c       | 436 ++++++++++++++++++
 2 files changed, 437 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/ptrace/set_syscall_info.c

diff --git a/tools/testing/selftests/ptrace/Makefile b/tools/testing/selftests/ptrace/Makefile
index 1c631740a730..c5e0b76ba6ac 100644
--- a/tools/testing/selftests/ptrace/Makefile
+++ b/tools/testing/selftests/ptrace/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 CFLAGS += -std=c99 -pthread -Wall $(KHDR_INCLUDES)
 
-TEST_GEN_PROGS := get_syscall_info peeksiginfo vmaccess get_set_sud
+TEST_GEN_PROGS := get_syscall_info set_syscall_info peeksiginfo vmaccess get_set_sud
 
 include ../lib.mk
diff --git a/tools/testing/selftests/ptrace/set_syscall_info.c b/tools/testing/selftests/ptrace/set_syscall_info.c
new file mode 100644
index 000000000000..f43294e1248e
--- /dev/null
+++ b/tools/testing/selftests/ptrace/set_syscall_info.c
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2018-2025 Dmitry V. Levin <ldv@strace.io>
+ * All rights reserved.
+ *
+ * Check whether PTRACE_SET_SYSCALL_INFO semantics implemented in the kernel
+ * matches userspace expectations.
+ */
+
+#include "../kselftest_harness.h"
+#include <err.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <asm/unistd.h>
+#include "linux/ptrace.h"
+
+static int
+kill_tracee(pid_t pid)
+{
+	if (!pid)
+		return 0;
+
+	int saved_errno = errno;
+
+	int rc = kill(pid, SIGKILL);
+
+	errno = saved_errno;
+	return rc;
+}
+
+static long
+sys_ptrace(int request, pid_t pid, unsigned long addr, unsigned long data)
+{
+	return syscall(__NR_ptrace, request, pid, addr, data);
+}
+
+#define LOG_KILL_TRACEE(fmt, ...)				\
+	do {							\
+		kill_tracee(pid);				\
+		TH_LOG("wait #%d: " fmt,			\
+		       ptrace_stop, ##__VA_ARGS__);		\
+	} while (0)
+
+struct si_entry {
+	int nr;
+	unsigned long args[6];
+};
+struct si_exit {
+	unsigned int is_error;
+	int rval;
+};
+
+TEST(set_syscall_info)
+{
+	const pid_t tracer_pid = getpid();
+	const unsigned long dummy[] = {
+		0xbad0fed0,
+		0xbad1fed1,
+		0xbad2fed2,
+		0xbad3fed3,
+		0xbad4fed4,
+		0xbad5fed5,
+	};
+	int splice_in[2], splice_out[2];
+
+	ASSERT_EQ(0, pipe(splice_in));
+	ASSERT_EQ(0, pipe(splice_out));
+	ASSERT_EQ(sizeof(dummy), write(splice_in[1], dummy, sizeof(dummy)));
+
+	const struct {
+		struct si_entry entry[2];
+		struct si_exit exit[2];
+	} si[] = {
+		/* change scno, keep non-error rval */
+		{
+			{
+				{
+					__NR_gettid,
+					{
+						dummy[0], dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}, {
+					__NR_getppid,
+					{
+						dummy[0], dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}
+			}, {
+				{ 0, tracer_pid }, { 0, tracer_pid }
+			}
+		},
+
+		/* set scno to -1, keep error rval */
+		{
+			{
+				{
+					__NR_chdir,
+					{
+						(unsigned long) ".", dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}, {
+					-1,
+					{
+						(unsigned long) ".", dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}
+			}, {
+				{ 1, -ENOSYS }, { 1, -ENOSYS }
+			}
+		},
+
+		/* keep scno, change non-error rval */
+		{
+			{
+				{
+					__NR_getppid,
+					{
+						dummy[0], dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}, {
+					__NR_getppid,
+					{
+						dummy[0], dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}
+			}, {
+				{ 0, tracer_pid }, { 0, tracer_pid + 1 }
+			}
+		},
+
+		/* change arg1, keep non-error rval */
+		{
+			{
+				{
+					__NR_chdir,
+					{
+						(unsigned long) "", dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}, {
+					__NR_chdir,
+					{
+						(unsigned long) ".", dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}
+			}, {
+				{ 0, 0 }, { 0, 0 }
+			}
+		},
+
+		/* set scno to -1, change error rval to non-error */
+		{
+			{
+				{
+					__NR_gettid,
+					{
+						dummy[0], dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}, {
+					-1,
+					{
+						dummy[0], dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}
+			}, {
+				{ 1, -ENOSYS }, { 0, tracer_pid }
+			}
+		},
+
+		/* change scno, change non-error rval to error */
+		{
+			{
+				{
+					__NR_chdir,
+					{
+						dummy[0], dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}, {
+					__NR_getppid,
+					{
+						dummy[0], dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}
+			}, {
+				{ 0, tracer_pid }, { 1, -EISDIR }
+			}
+		},
+
+		/* change scno and all args, change non-error rval */
+		{
+			{
+				{
+					__NR_gettid,
+					{
+						splice_in[0], dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}, {
+					__NR_splice,
+					{
+						splice_in[0], 0, splice_out[1], 0,
+						sizeof(dummy), SPLICE_F_NONBLOCK
+					}
+				}
+			}, {
+				{ 0, sizeof(dummy) }, { 0, sizeof(dummy) + 1 }
+			}
+		},
+
+		/* change arg1, no exit stop */
+		{
+			{
+				{
+					__NR_exit_group,
+					{
+						dummy[0], dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}, {
+					__NR_exit_group,
+					{
+						0, dummy[1], dummy[2],
+						dummy[3], dummy[4], dummy[5]
+					}
+				}
+			}, {
+				{ 0, 0 }, { 0, 0 }
+			}
+		},
+	};
+
+	long rc;
+	unsigned int i;
+	unsigned int ptrace_stop;
+
+	pid_t pid = fork();
+
+	ASSERT_LE(0, pid) {
+		TH_LOG("fork: %m");
+	}
+
+	if (pid == 0) {
+		/* get the pid before PTRACE_TRACEME */
+		pid = getpid();
+		ASSERT_EQ(0, sys_ptrace(PTRACE_TRACEME, 0, 0, 0)) {
+			TH_LOG("PTRACE_TRACEME: %m");
+		}
+		ASSERT_EQ(0, kill(pid, SIGSTOP)) {
+			/* cannot happen */
+			TH_LOG("kill SIGSTOP: %m");
+		}
+		for (i = 0; i < ARRAY_SIZE(si); ++i) {
+			rc = syscall(si[i].entry[0].nr,
+				     si[i].entry[0].args[0],
+				     si[i].entry[0].args[1],
+				     si[i].entry[0].args[2],
+				     si[i].entry[0].args[3],
+				     si[i].entry[0].args[4],
+				     si[i].entry[0].args[5]);
+			if (si[i].exit[1].is_error) {
+				if (rc != -1 || errno != -si[i].exit[1].rval)
+					break;
+			} else {
+				if (rc != si[i].exit[1].rval)
+					break;
+			}
+		}
+		/*
+		 * Something went wrong, but in this state tracee
+		 * cannot reliably issue syscalls, so just crash.
+		 */
+		*(volatile unsigned char *) (unsigned long) i = 42;
+		/* unreachable */
+		_exit(i + 1);
+	}
+
+	for (ptrace_stop = 0; ; ++ptrace_stop) {
+		struct ptrace_syscall_info info = {
+			.op = 0xff	/* invalid PTRACE_SYSCALL_INFO_* op */
+		};
+		const size_t size = sizeof(info);
+		const int expected_entry_size =
+			(void *) &info.entry.args[6] - (void *) &info;
+		const int expected_exit_size =
+			(void *) (&info.exit.is_error + 1) -
+			(void *) &info;
+		int status;
+
+		ASSERT_EQ(pid, wait(&status)) {
+			/* cannot happen */
+			LOG_KILL_TRACEE("wait: %m");
+		}
+		if (WIFEXITED(status)) {
+			pid = 0;	/* the tracee is no more */
+			ASSERT_EQ(0, WEXITSTATUS(status)) {
+				LOG_KILL_TRACEE("unexpected exit status %u",
+						WEXITSTATUS(status));
+			}
+			break;
+		}
+		ASSERT_FALSE(WIFSIGNALED(status)) {
+			pid = 0;	/* the tracee is no more */
+			LOG_KILL_TRACEE("unexpected signal %u",
+					WTERMSIG(status));
+		}
+		ASSERT_TRUE(WIFSTOPPED(status)) {
+			/* cannot happen */
+			LOG_KILL_TRACEE("unexpected wait status %#x", status);
+		}
+
+		ASSERT_LT(ptrace_stop, ARRAY_SIZE(si) * 2) {
+			LOG_KILL_TRACEE("ptrace stop overflow");
+		}
+
+		switch (WSTOPSIG(status)) {
+		case SIGSTOP:
+			ASSERT_EQ(0, ptrace_stop) {
+				LOG_KILL_TRACEE("unexpected signal stop");
+			}
+			ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, pid, 0,
+						PTRACE_O_TRACESYSGOOD)) {
+				LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m");
+			}
+			break;
+
+		case SIGTRAP | 0x80:
+			ASSERT_LT(0, ptrace_stop) {
+				LOG_KILL_TRACEE("unexpected syscall stop");
+			}
+			ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
+						      pid, size,
+						      (unsigned long) &info))) {
+				LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
+			}
+			if (ptrace_stop & 1) {
+				/* entering syscall */
+				const struct si_entry *exp_entry =
+					&si[ptrace_stop / 2].entry[0];
+				const struct si_entry *set_entry =
+					&si[ptrace_stop / 2].entry[1];
+
+				ASSERT_EQ(expected_entry_size, rc) {
+					LOG_KILL_TRACEE("entry stop mismatch");
+				}
+				ASSERT_EQ(PTRACE_SYSCALL_INFO_ENTRY, info.op) {
+					LOG_KILL_TRACEE("entry stop mismatch");
+				}
+				ASSERT_TRUE(info.arch) {
+					LOG_KILL_TRACEE("entry stop mismatch");
+				}
+				ASSERT_TRUE(info.instruction_pointer) {
+					LOG_KILL_TRACEE("entry stop mismatch");
+				}
+				ASSERT_TRUE(info.stack_pointer) {
+					LOG_KILL_TRACEE("entry stop mismatch");
+				}
+				ASSERT_EQ(exp_entry->nr, info.entry.nr) {
+					LOG_KILL_TRACEE("syscall nr mismatch");
+				}
+				for (i = 0; i < ARRAY_SIZE(exp_entry->args); ++i) {
+					ASSERT_EQ(exp_entry->args[i], info.entry.args[i]) {
+						LOG_KILL_TRACEE("syscall arg #%u mismatch", i);
+					}
+				}
+				info.entry.nr = set_entry->nr;
+				for (i = 0; i < ARRAY_SIZE(set_entry->args); ++i)
+					info.entry.args[i] = set_entry->args[i];
+				ASSERT_EQ(0, sys_ptrace(PTRACE_SET_SYSCALL_INFO,
+							pid, size,
+							(unsigned long) &info)) {
+					LOG_KILL_TRACEE("PTRACE_SET_SYSCALL_INFO: %m");
+				}
+			} else {
+				/* exiting syscall */
+				const struct si_exit *exp_exit =
+					&si[ptrace_stop / 2 - 1].exit[0];
+				const struct si_exit *set_exit =
+					&si[ptrace_stop / 2 - 1].exit[1];
+
+				ASSERT_EQ(expected_exit_size, rc) {
+					LOG_KILL_TRACEE("exit stop mismatch");
+				}
+				ASSERT_EQ(PTRACE_SYSCALL_INFO_EXIT, info.op) {
+					LOG_KILL_TRACEE("exit stop mismatch");
+				}
+				ASSERT_TRUE(info.arch) {
+					LOG_KILL_TRACEE("exit stop mismatch");
+				}
+				ASSERT_TRUE(info.instruction_pointer) {
+					LOG_KILL_TRACEE("exit stop mismatch");
+				}
+				ASSERT_TRUE(info.stack_pointer) {
+					LOG_KILL_TRACEE("exit stop mismatch");
+				}
+				ASSERT_EQ(exp_exit->is_error, info.exit.is_error) {
+					LOG_KILL_TRACEE("exit stop mismatch");
+				}
+				ASSERT_EQ(exp_exit->rval, info.exit.rval) {
+					LOG_KILL_TRACEE("exit stop mismatch");
+				}
+				info.exit.is_error = set_exit->is_error;
+				info.exit.rval = set_exit->rval;
+				ASSERT_EQ(0, sys_ptrace(PTRACE_SET_SYSCALL_INFO,
+							pid, size,
+							(unsigned long) &info)) {
+					LOG_KILL_TRACEE("PTRACE_SET_SYSCALL_INFO: %m");
+				}
+			}
+			break;
+
+		default:
+			LOG_KILL_TRACEE("unexpected stop signal %u",
+					WSTOPSIG(status));
+			abort();
+		}
+
+		ASSERT_EQ(0, sys_ptrace(PTRACE_SYSCALL, pid, 0, 0)) {
+			LOG_KILL_TRACEE("PTRACE_SYSCALL: %m");
+		}
+	}
+
+	ASSERT_EQ(ptrace_stop, ARRAY_SIZE(si) * 2);
+}
+
+TEST_HARNESS_MAIN

-- 
ldv

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request
  2025-01-07 23:04 ` [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request Dmitry V. Levin
@ 2025-01-09  1:37   ` kernel test robot
  2025-01-09  2:21   ` kernel test robot
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 22+ messages in thread
From: kernel test robot @ 2025-01-09  1:37 UTC (permalink / raw)
  To: Dmitry V. Levin, Oleg Nesterov
  Cc: llvm, oe-kbuild-all, Eugene Syromyatnikov, Mike Frysinger,
	Renzo Davoli, Davide Berardi, strace-devel, linux-kernel,
	linux-api

Hi Dmitry,

kernel test robot noticed the following build errors:

[auto build test ERROR on openrisc/for-next]
[also build test ERROR on powerpc/next powerpc/fixes s390/features uml/next jcmvbkbc-xtensa/xtensa-for-next arnd-asm-generic/master vgupta-arc/for-curr arm64/for-next/core linus/master uml/fixes tip/x86/core vgupta-arc/for-next v6.13-rc6 next-20250108]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Dmitry-V-Levin/Revert-arch-remove-unused-function-syscall_set_arguments/20250108-070658
base:   https://github.com/openrisc/linux.git for-next
patch link:    https://lore.kernel.org/r/20250107230456.GE30633%40strace.io
patch subject: [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request
config: hexagon-allnoconfig (https://download.01.org/0day-ci/archive/20250109/202501090919.TiLTOhaq-lkp@intel.com/config)
compiler: clang version 20.0.0git (https://github.com/llvm/llvm-project 096551537b2a747a3387726ca618ceeb3950e9bc)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250109/202501090919.TiLTOhaq-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202501090919.TiLTOhaq-lkp@intel.com/

All errors (new ones prefixed by >>):

>> kernel/ptrace.c:1053:3: error: call to undeclared function 'syscall_set_return_value'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    1053 |                 syscall_set_return_value(child, regs, -ENOSYS, 0);
         |                 ^
   kernel/ptrace.c:1053:3: note: did you mean 'syscall_get_return_value'?
   arch/hexagon/include/asm/syscall.h:56:20: note: 'syscall_get_return_value' declared here
      56 | static inline long syscall_get_return_value(struct task_struct *task,
         |                    ^
   kernel/ptrace.c:1075:3: error: call to undeclared function 'syscall_set_return_value'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    1075 |                 syscall_set_return_value(child, regs, info->exit.rval, 0);
         |                 ^
   2 errors generated.


vim +/syscall_set_return_value +1053 kernel/ptrace.c

  1021	
  1022	static unsigned long
  1023	ptrace_set_syscall_info_entry(struct task_struct *child, struct pt_regs *regs,
  1024				      struct ptrace_syscall_info *info)
  1025	{
  1026		unsigned long args[ARRAY_SIZE(info->entry.args)];
  1027		int nr = info->entry.nr;
  1028		int i;
  1029	
  1030		if (nr != info->entry.nr)
  1031			return -ERANGE;
  1032	
  1033		for (i = 0; i < ARRAY_SIZE(args); i++) {
  1034			args[i] = info->entry.args[i];
  1035			if (args[i] != info->entry.args[i])
  1036				return -ERANGE;
  1037		}
  1038	
  1039		syscall_set_nr(child, regs, nr);
  1040		syscall_set_arguments(child, regs, args);
  1041		if (nr == -1) {
  1042			/*
  1043			 * When the syscall number is set to -1, the syscall will be
  1044			 * skipped.  In this case also set the syscall return value to
  1045			 * -ENOSYS, otherwise on some architectures the corresponding
  1046			 * struct pt_regs field will remain unchanged.
  1047			 *
  1048			 * Note that on some architectures syscall_set_return_value()
  1049			 * modifies one of the struct pt_regs fields also modified by
  1050			 * syscall_set_arguments(), so the former should be called
  1051			 * after the latter.
  1052			 */
> 1053			syscall_set_return_value(child, regs, -ENOSYS, 0);
  1054		}
  1055	
  1056		return 0;
  1057	}
  1058	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request
  2025-01-07 23:04 ` [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request Dmitry V. Levin
  2025-01-09  1:37   ` kernel test robot
@ 2025-01-09  2:21   ` kernel test robot
  2025-01-09 14:03   ` kernel test robot
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 22+ messages in thread
From: kernel test robot @ 2025-01-09  2:21 UTC (permalink / raw)
  To: Dmitry V. Levin, Oleg Nesterov
  Cc: llvm, oe-kbuild-all, Eugene Syromyatnikov, Mike Frysinger,
	Renzo Davoli, Davide Berardi, strace-devel, linux-kernel,
	linux-api

Hi Dmitry,

kernel test robot noticed the following build errors:

[auto build test ERROR on openrisc/for-next]
[also build test ERROR on powerpc/next powerpc/fixes s390/features uml/next jcmvbkbc-xtensa/xtensa-for-next arnd-asm-generic/master vgupta-arc/for-curr arm64/for-next/core linus/master uml/fixes tip/x86/core vgupta-arc/for-next v6.13-rc6 next-20250108]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Dmitry-V-Levin/Revert-arch-remove-unused-function-syscall_set_arguments/20250108-070658
base:   https://github.com/openrisc/linux.git for-next
patch link:    https://lore.kernel.org/r/20250107230456.GE30633%40strace.io
patch subject: [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request
config: hexagon-randconfig-001-20250109 (https://download.01.org/0day-ci/archive/20250109/202501090954.gYTxI9sY-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250109/202501090954.gYTxI9sY-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202501090954.gYTxI9sY-lkp@intel.com/

All errors (new ones prefixed by >>):

>> kernel/ptrace.c:1053:3: error: implicit declaration of function 'syscall_set_return_value' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
                   syscall_set_return_value(child, regs, -ENOSYS, 0);
                   ^
   kernel/ptrace.c:1053:3: note: did you mean 'syscall_get_return_value'?
   arch/hexagon/include/asm/syscall.h:56:20: note: 'syscall_get_return_value' declared here
   static inline long syscall_get_return_value(struct task_struct *task,
                      ^
   kernel/ptrace.c:1075:3: error: implicit declaration of function 'syscall_set_return_value' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
                   syscall_set_return_value(child, regs, info->exit.rval, 0);
                   ^
   2 errors generated.


vim +/syscall_set_return_value +1053 kernel/ptrace.c

  1021	
  1022	static unsigned long
  1023	ptrace_set_syscall_info_entry(struct task_struct *child, struct pt_regs *regs,
  1024				      struct ptrace_syscall_info *info)
  1025	{
  1026		unsigned long args[ARRAY_SIZE(info->entry.args)];
  1027		int nr = info->entry.nr;
  1028		int i;
  1029	
  1030		if (nr != info->entry.nr)
  1031			return -ERANGE;
  1032	
  1033		for (i = 0; i < ARRAY_SIZE(args); i++) {
  1034			args[i] = info->entry.args[i];
  1035			if (args[i] != info->entry.args[i])
  1036				return -ERANGE;
  1037		}
  1038	
  1039		syscall_set_nr(child, regs, nr);
  1040		syscall_set_arguments(child, regs, args);
  1041		if (nr == -1) {
  1042			/*
  1043			 * When the syscall number is set to -1, the syscall will be
  1044			 * skipped.  In this case also set the syscall return value to
  1045			 * -ENOSYS, otherwise on some architectures the corresponding
  1046			 * struct pt_regs field will remain unchanged.
  1047			 *
  1048			 * Note that on some architectures syscall_set_return_value()
  1049			 * modifies one of the struct pt_regs fields also modified by
  1050			 * syscall_set_arguments(), so the former should be called
  1051			 * after the latter.
  1052			 */
> 1053			syscall_set_return_value(child, regs, -ENOSYS, 0);
  1054		}
  1055	
  1056		return 0;
  1057	}
  1058	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 4/6] ptrace_get_syscall_info: factor out ptrace_get_syscall_info_op
  2025-01-07 23:04 ` [PATCH 4/6] ptrace_get_syscall_info: factor out ptrace_get_syscall_info_op Dmitry V. Levin
@ 2025-01-09 10:09   ` Oleg Nesterov
  2025-01-09 10:27     ` Dmitry V. Levin
  2025-01-09 10:41   ` Oleg Nesterov
  1 sibling, 1 reply; 22+ messages in thread
From: Oleg Nesterov @ 2025-01-09 10:09 UTC (permalink / raw)
  To: Dmitry V. Levin
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, linux-kernel

On 01/08, Dmitry V. Levin wrote:
>
> +static int
> +ptrace_get_syscall_info(struct task_struct *child, unsigned long user_size,
> +			void __user *datavp)
> +{
> +	struct pt_regs *regs = task_pt_regs(child);
> +	struct ptrace_syscall_info info = {
> +		.op = ptrace_get_syscall_info_op(child),
> +		.arch = syscall_get_arch(child),
> +		.instruction_pointer = instruction_pointer(regs),
> +		.stack_pointer = user_stack_pointer(regs),
> +	};
> +	unsigned long actual_size = offsetof(struct ptrace_syscall_info, entry);
> +	unsigned long write_size;
> +
> +	switch (info.op) {
> +	case PTRACE_SYSCALL_INFO_ENTRY:
> +		actual_size = ptrace_get_syscall_info_entry(child, regs, &info);
> +		break;
> +	case PTRACE_SYSCALL_INFO_EXIT:
> +		actual_size = ptrace_get_syscall_info_exit(child, regs, &info);
> +		break;
> +	case PTRACE_SYSCALL_INFO_SECCOMP:
> +		actual_size = ptrace_get_syscall_info_seccomp(child, regs, &info);
>  		break;

OK... but unless I misread this patch, all 3 ptrace_get_syscall_info_xxx()
helpers will do the pointless info->op = PTRACE_SYSCALL_INFO_XXX ?

Oleg.


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 4/6] ptrace_get_syscall_info: factor out ptrace_get_syscall_info_op
  2025-01-09 10:09   ` Oleg Nesterov
@ 2025-01-09 10:27     ` Dmitry V. Levin
  0 siblings, 0 replies; 22+ messages in thread
From: Dmitry V. Levin @ 2025-01-09 10:27 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, linux-kernel

On Thu, Jan 09, 2025 at 11:09:44AM +0100, Oleg Nesterov wrote:
> On 01/08, Dmitry V. Levin wrote:
> >
> > +static int
> > +ptrace_get_syscall_info(struct task_struct *child, unsigned long user_size,
> > +			void __user *datavp)
> > +{
> > +	struct pt_regs *regs = task_pt_regs(child);
> > +	struct ptrace_syscall_info info = {
> > +		.op = ptrace_get_syscall_info_op(child),
> > +		.arch = syscall_get_arch(child),
> > +		.instruction_pointer = instruction_pointer(regs),
> > +		.stack_pointer = user_stack_pointer(regs),
> > +	};
> > +	unsigned long actual_size = offsetof(struct ptrace_syscall_info, entry);
> > +	unsigned long write_size;
> > +
> > +	switch (info.op) {
> > +	case PTRACE_SYSCALL_INFO_ENTRY:
> > +		actual_size = ptrace_get_syscall_info_entry(child, regs, &info);
> > +		break;
> > +	case PTRACE_SYSCALL_INFO_EXIT:
> > +		actual_size = ptrace_get_syscall_info_exit(child, regs, &info);
> > +		break;
> > +	case PTRACE_SYSCALL_INFO_SECCOMP:
> > +		actual_size = ptrace_get_syscall_info_seccomp(child, regs, &info);
> >  		break;
> 
> OK... but unless I misread this patch, all 3 ptrace_get_syscall_info_xxx()
> helpers will do the pointless info->op = PTRACE_SYSCALL_INFO_XXX ?

Thanks, your analysis is correct, with this change those assignments
become redundant, I'll remove them in the next iteration of this patch.


-- 
ldv

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 4/6] ptrace_get_syscall_info: factor out ptrace_get_syscall_info_op
  2025-01-07 23:04 ` [PATCH 4/6] ptrace_get_syscall_info: factor out ptrace_get_syscall_info_op Dmitry V. Levin
  2025-01-09 10:09   ` Oleg Nesterov
@ 2025-01-09 10:41   ` Oleg Nesterov
  2025-01-09 11:14     ` Dmitry V. Levin
  1 sibling, 1 reply; 22+ messages in thread
From: Oleg Nesterov @ 2025-01-09 10:41 UTC (permalink / raw)
  To: Dmitry V. Levin
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, linux-kernel

On 01/08, Dmitry V. Levin wrote:
>
> +ptrace_get_syscall_info_op(struct task_struct *child)
>  {
...
>  	case SIGTRAP | 0x80:
>  		switch (child->ptrace_message) {
>  		case PTRACE_EVENTMSG_SYSCALL_ENTRY:
> -			actual_size = ptrace_get_syscall_info_entry(child, regs,
> -								    &info);
> -			break;
> +			return PTRACE_SYSCALL_INFO_ENTRY;
>  		case PTRACE_EVENTMSG_SYSCALL_EXIT:
> -			actual_size = ptrace_get_syscall_info_exit(child, regs,
> -								   &info);
> -			break;
> +			return PTRACE_SYSCALL_INFO_EXIT;
>  		}
>  		break;
>  	case SIGTRAP | (PTRACE_EVENT_SECCOMP << 8):
> -		actual_size = ptrace_get_syscall_info_seccomp(child, regs,
> -							      &info);
> +		return PTRACE_SYSCALL_INFO_SECCOMP;
> +	}
> +
> +	return PTRACE_SYSCALL_INFO_NONE;

Cosmetic, I won't insist, but I'd suggest to do

	default:
		return PTRACE_SYSCALL_INFO_SECCOMP;
	}

to make it more symmetric.

Oleg.


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 4/6] ptrace_get_syscall_info: factor out ptrace_get_syscall_info_op
  2025-01-09 10:41   ` Oleg Nesterov
@ 2025-01-09 11:14     ` Dmitry V. Levin
  0 siblings, 0 replies; 22+ messages in thread
From: Dmitry V. Levin @ 2025-01-09 11:14 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, linux-kernel

On Thu, Jan 09, 2025 at 11:41:47AM +0100, Oleg Nesterov wrote:
> On 01/08, Dmitry V. Levin wrote:
> >
> > +ptrace_get_syscall_info_op(struct task_struct *child)
> >  {
> ...
> >  	case SIGTRAP | 0x80:
> >  		switch (child->ptrace_message) {
> >  		case PTRACE_EVENTMSG_SYSCALL_ENTRY:
> > -			actual_size = ptrace_get_syscall_info_entry(child, regs,
> > -								    &info);
> > -			break;
> > +			return PTRACE_SYSCALL_INFO_ENTRY;
> >  		case PTRACE_EVENTMSG_SYSCALL_EXIT:
> > -			actual_size = ptrace_get_syscall_info_exit(child, regs,
> > -								   &info);
> > -			break;
> > +			return PTRACE_SYSCALL_INFO_EXIT;
> >  		}
> >  		break;
> >  	case SIGTRAP | (PTRACE_EVENT_SECCOMP << 8):
> > -		actual_size = ptrace_get_syscall_info_seccomp(child, regs,
> > -							      &info);
> > +		return PTRACE_SYSCALL_INFO_SECCOMP;
> > +	}
> > +
> > +	return PTRACE_SYSCALL_INFO_NONE;
> 
> Cosmetic, I won't insist, but I'd suggest to do
> 
> 	default:
> 		return PTRACE_SYSCALL_INFO_SECCOMP;
> 	}
> 
> to make it more symmetric.

OK, but I suppose you mean PTRACE_SYSCALL_INFO_NONE here, though.


-- 
ldv

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/6] syscall.h: add syscall_set_arguments() on remaining HAVE_ARCH_TRACEHOOK arches
  2025-01-07 23:04 ` [PATCH 2/6] syscall.h: add syscall_set_arguments() on remaining HAVE_ARCH_TRACEHOOK arches Dmitry V. Levin
@ 2025-01-09 12:04   ` Oleg Nesterov
  2025-01-09 12:11     ` Dmitry V. Levin
  0 siblings, 1 reply; 22+ messages in thread
From: Oleg Nesterov @ 2025-01-09 12:04 UTC (permalink / raw)
  To: Dmitry V. Levin
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, Vineet Gupta, Russell King,
	Catalin Marinas, Will Deacon, Guo Ren, Brian Cain, Huacai Chen,
	WANG Xuerui, Thomas Bogendoerfer, James E.J. Bottomley,
	Helge Deller, linux-snps-arc, linux-kernel, linux-arm-kernel,
	linux-csky, linux-hexagon, loongarch, linux-mips, linux-parisc

On 01/08, Dmitry V. Levin wrote:
>
>  arch/arc/include/asm/syscall.h       | 14 +++++++++
>  arch/arm/include/asm/syscall.h       | 13 ++++++++
>  arch/arm64/include/asm/syscall.h     | 13 ++++++++
>  arch/csky/include/asm/syscall.h      | 13 ++++++++
>  arch/hexagon/include/asm/syscall.h   |  7 +++++
>  arch/loongarch/include/asm/syscall.h |  8 +++++
>  arch/mips/include/asm/syscall.h      | 46 ++++++++++++++++++++++++++++
>  arch/parisc/include/asm/syscall.h    | 12 ++++++++

Confused... arch/x86 needs syscall_set_arguments() too ?

Oleg.


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/6] syscall.h: add syscall_set_arguments() on remaining HAVE_ARCH_TRACEHOOK arches
  2025-01-09 12:04   ` Oleg Nesterov
@ 2025-01-09 12:11     ` Dmitry V. Levin
  0 siblings, 0 replies; 22+ messages in thread
From: Dmitry V. Levin @ 2025-01-09 12:11 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, Vineet Gupta, Russell King,
	Catalin Marinas, Will Deacon, Guo Ren, Brian Cain, Huacai Chen,
	WANG Xuerui, Thomas Bogendoerfer, James E.J. Bottomley,
	Helge Deller, linux-snps-arc, linux-kernel, linux-arm-kernel,
	linux-csky, linux-hexagon, loongarch, linux-mips, linux-parisc

On Thu, Jan 09, 2025 at 01:04:53PM +0100, Oleg Nesterov wrote:
> On 01/08, Dmitry V. Levin wrote:
> >
> >  arch/arc/include/asm/syscall.h       | 14 +++++++++
> >  arch/arm/include/asm/syscall.h       | 13 ++++++++
> >  arch/arm64/include/asm/syscall.h     | 13 ++++++++
> >  arch/csky/include/asm/syscall.h      | 13 ++++++++
> >  arch/hexagon/include/asm/syscall.h   |  7 +++++
> >  arch/loongarch/include/asm/syscall.h |  8 +++++
> >  arch/mips/include/asm/syscall.h      | 46 ++++++++++++++++++++++++++++
> >  arch/parisc/include/asm/syscall.h    | 12 ++++++++
> 
> Confused... arch/x86 needs syscall_set_arguments() too ?

Sure, all HAVE_ARCH_TRACEHOOK architectures need them, but most of these
architectures already provide them since the previous patch in the series:
[PATCH 1/6] Revert "arch: remove unused function syscall_set_arguments()"


-- 
ldv

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request
  2025-01-07 23:04 ` [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request Dmitry V. Levin
  2025-01-09  1:37   ` kernel test robot
  2025-01-09  2:21   ` kernel test robot
@ 2025-01-09 14:03   ` kernel test robot
  2025-01-09 15:17   ` Eugene Syromiatnikov
  2025-01-09 15:21   ` Oleg Nesterov
  4 siblings, 0 replies; 22+ messages in thread
From: kernel test robot @ 2025-01-09 14:03 UTC (permalink / raw)
  To: Dmitry V. Levin, Oleg Nesterov
  Cc: oe-kbuild-all, Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, linux-kernel, linux-api

Hi Dmitry,

kernel test robot noticed the following build warnings:

[auto build test WARNING on openrisc/for-next]
[also build test WARNING on powerpc/next powerpc/fixes s390/features uml/next jcmvbkbc-xtensa/xtensa-for-next arnd-asm-generic/master vgupta-arc/for-curr arm64/for-next/core linus/master uml/fixes tip/x86/core vgupta-arc/for-next v6.13-rc6 next-20250109]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Dmitry-V-Levin/Revert-arch-remove-unused-function-syscall_set_arguments/20250108-070658
base:   https://github.com/openrisc/linux.git for-next
patch link:    https://lore.kernel.org/r/20250107230456.GE30633%40strace.io
patch subject: [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request
config: mips-randconfig-r122-20250109 (https://download.01.org/0day-ci/archive/20250109/202501092150.9BH2s9AO-lkp@intel.com/config)
compiler: mips64-linux-gcc (GCC) 14.2.0
reproduce: (https://download.01.org/0day-ci/archive/20250109/202501092150.9BH2s9AO-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202501092150.9BH2s9AO-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
   kernel/ptrace.c:55:22: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/ptrace.c:55:22: sparse:    struct task_struct *
   kernel/ptrace.c:55:22: sparse:    struct task_struct [noderef] __rcu *
   kernel/ptrace.c:74:23: sparse: sparse: incorrect type in assignment (different address spaces) @@     expected struct task_struct [noderef] __rcu *parent @@     got struct task_struct *new_parent @@
   kernel/ptrace.c:74:23: sparse:     expected struct task_struct [noderef] __rcu *parent
   kernel/ptrace.c:74:23: sparse:     got struct task_struct *new_parent
   kernel/ptrace.c:75:29: sparse: sparse: incorrect type in assignment (different address spaces) @@     expected struct cred const [noderef] __rcu *ptracer_cred @@     got struct cred const * @@
   kernel/ptrace.c:75:29: sparse:     expected struct cred const [noderef] __rcu *ptracer_cred
   kernel/ptrace.c:75:29: sparse:     got struct cred const *
   kernel/ptrace.c:129:18: sparse: sparse: incorrect type in assignment (different address spaces) @@     expected struct cred const *old_cred @@     got struct cred const [noderef] __rcu *ptracer_cred @@
   kernel/ptrace.c:129:18: sparse:     expected struct cred const *old_cred
   kernel/ptrace.c:129:18: sparse:     got struct cred const [noderef] __rcu *ptracer_cred
   kernel/ptrace.c:133:25: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   kernel/ptrace.c:133:25: sparse:     expected struct spinlock [usertype] *lock
   kernel/ptrace.c:133:25: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:160:27: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   kernel/ptrace.c:160:27: sparse:     expected struct spinlock [usertype] *lock
   kernel/ptrace.c:160:27: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:192:28: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   kernel/ptrace.c:192:28: sparse:     expected struct spinlock [usertype] *lock
   kernel/ptrace.c:192:28: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:198:30: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   kernel/ptrace.c:198:30: sparse:     expected struct spinlock [usertype] *lock
   kernel/ptrace.c:198:30: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:251:44: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/ptrace.c:251:44: sparse:    struct task_struct [noderef] __rcu *
   kernel/ptrace.c:251:44: sparse:    struct task_struct *
   kernel/ptrace.c:494:54: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct task_struct *parent @@     got struct task_struct [noderef] __rcu *parent @@
   kernel/ptrace.c:494:54: sparse:     expected struct task_struct *parent
   kernel/ptrace.c:494:54: sparse:     got struct task_struct [noderef] __rcu *parent
   kernel/ptrace.c:502:53: sparse: sparse: incorrect type in argument 2 (different address spaces) @@     expected struct task_struct *new_parent @@     got struct task_struct [noderef] __rcu *real_parent @@
   kernel/ptrace.c:502:53: sparse:     expected struct task_struct *new_parent
   kernel/ptrace.c:502:53: sparse:     got struct task_struct [noderef] __rcu *real_parent
   kernel/ptrace.c:550:41: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct task_struct *p1 @@     got struct task_struct [noderef] __rcu *real_parent @@
   kernel/ptrace.c:550:41: sparse:     expected struct task_struct *p1
   kernel/ptrace.c:550:41: sparse:     got struct task_struct [noderef] __rcu *real_parent
   kernel/ptrace.c:552:50: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct sighand_struct *sigh @@     got struct sighand_struct [noderef] __rcu *sighand @@
   kernel/ptrace.c:552:50: sparse:     expected struct sighand_struct *sigh
   kernel/ptrace.c:552:50: sparse:     got struct sighand_struct [noderef] __rcu *sighand
   kernel/ptrace.c:743:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   kernel/ptrace.c:743:37: sparse:     expected struct spinlock [usertype] *lock
   kernel/ptrace.c:743:37: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:751:39: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   kernel/ptrace.c:751:39: sparse:     expected struct spinlock [usertype] *lock
   kernel/ptrace.c:751:39: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:862:29: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   kernel/ptrace.c:862:29: sparse:     expected struct spinlock [usertype] *lock
   kernel/ptrace.c:862:29: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:866:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   kernel/ptrace.c:866:31: sparse:     expected struct spinlock [usertype] *lock
   kernel/ptrace.c:866:31: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:1206:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   kernel/ptrace.c:1206:37: sparse:     expected struct spinlock [usertype] *lock
   kernel/ptrace.c:1206:37: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:1208:39: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   kernel/ptrace.c:1208:39: sparse:     expected struct spinlock [usertype] *lock
   kernel/ptrace.c:1208:39: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c: note: in included file (through include/linux/fwnode.h, include/linux/logic_pio.h, include/asm-generic/io.h, ...):
   include/linux/list.h:83:21: sparse: sparse: self-comparison always evaluates to true
   kernel/ptrace.c: note: in included file (through include/linux/rcuwait.h, include/linux/percpu-rwsem.h, include/linux/fs.h, ...):
   include/linux/sched/signal.h:751:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   include/linux/sched/signal.h:751:37: sparse:     expected struct spinlock [usertype] *lock
   include/linux/sched/signal.h:751:37: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:380:30: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *l @@     got struct spinlock [noderef] __rcu * @@
   kernel/ptrace.c:380:30: sparse:     expected struct spinlock [usertype] *l
   kernel/ptrace.c:380:30: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:409:12: sparse: sparse: context imbalance in 'ptrace_attach' - different lock contexts for basic block
   kernel/ptrace.c:500:38: sparse: sparse: dereference of noderef expression
   kernel/ptrace.c: note: in included file (through include/linux/fwnode.h, include/linux/logic_pio.h, include/asm-generic/io.h, ...):
   include/linux/list.h:83:21: sparse: sparse: self-comparison always evaluates to true
   kernel/ptrace.c: note: in included file (through include/linux/rcuwait.h, include/linux/percpu-rwsem.h, include/linux/fs.h, ...):
   include/linux/sched/signal.h:751:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   include/linux/sched/signal.h:751:37: sparse:     expected struct spinlock [usertype] *lock
   include/linux/sched/signal.h:751:37: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:690:9: sparse: sparse: context imbalance in 'ptrace_getsiginfo' - different lock contexts for basic block
   include/linux/sched/signal.h:751:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   include/linux/sched/signal.h:751:37: sparse:     expected struct spinlock [usertype] *lock
   include/linux/sched/signal.h:751:37: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:706:9: sparse: sparse: context imbalance in 'ptrace_setsiginfo' - different lock contexts for basic block
   kernel/ptrace.c: note: in included file:
   arch/mips/include/asm/syscall.h:85:25: sparse: sparse: incorrect type in initializer (different address spaces) @@     expected int const [noderef] __user *__p @@     got int * @@
   arch/mips/include/asm/syscall.h:85:25: sparse:     expected int const [noderef] __user *__p
   arch/mips/include/asm/syscall.h:85:25: sparse:     got int *
>> arch/mips/include/asm/syscall.h:121:25: sparse: sparse: incorrect type in initializer (different address spaces) @@     expected int [noderef] __user *__p @@     got int * @@
   arch/mips/include/asm/syscall.h:121:25: sparse:     expected int [noderef] __user *__p
   arch/mips/include/asm/syscall.h:121:25: sparse:     got int *
   kernel/ptrace.c: note: in included file (through include/linux/rcuwait.h, include/linux/percpu-rwsem.h, include/linux/fs.h, ...):
   include/linux/sched/signal.h:751:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   include/linux/sched/signal.h:751:37: sparse:     expected struct spinlock [usertype] *lock
   include/linux/sched/signal.h:751:37: sparse:     got struct spinlock [noderef] __rcu *
   include/linux/sched/signal.h:751:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected struct spinlock [usertype] *lock @@     got struct spinlock [noderef] __rcu * @@
   include/linux/sched/signal.h:751:37: sparse:     expected struct spinlock [usertype] *lock
   include/linux/sched/signal.h:751:37: sparse:     got struct spinlock [noderef] __rcu *
   kernel/ptrace.c:1369:9: sparse: sparse: context imbalance in 'ptrace_request' - different lock contexts for basic block

vim +121 arch/mips/include/asm/syscall.h

307e4cb2957c2f Dmitry V. Levin 2025-01-08  116  
307e4cb2957c2f Dmitry V. Levin 2025-01-08  117  #ifdef CONFIG_64BIT
307e4cb2957c2f Dmitry V. Levin 2025-01-08  118  	case 4: case 5: case 6: case 7:
307e4cb2957c2f Dmitry V. Levin 2025-01-08  119  #ifdef CONFIG_MIPS32_O32
307e4cb2957c2f Dmitry V. Levin 2025-01-08  120  		if (test_tsk_thread_flag(task, TIF_32BIT_REGS))
307e4cb2957c2f Dmitry V. Levin 2025-01-08 @121  			put_user(*arg, (int *)usp + n);
307e4cb2957c2f Dmitry V. Levin 2025-01-08  122  		else
307e4cb2957c2f Dmitry V. Levin 2025-01-08  123  #endif
307e4cb2957c2f Dmitry V. Levin 2025-01-08  124  			regs->regs[4 + n] = *arg;
307e4cb2957c2f Dmitry V. Levin 2025-01-08  125  
307e4cb2957c2f Dmitry V. Levin 2025-01-08  126  		return;
307e4cb2957c2f Dmitry V. Levin 2025-01-08  127  #endif
307e4cb2957c2f Dmitry V. Levin 2025-01-08  128  	}
307e4cb2957c2f Dmitry V. Levin 2025-01-08  129  }
307e4cb2957c2f Dmitry V. Levin 2025-01-08  130  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request
  2025-01-07 23:04 ` [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request Dmitry V. Levin
                     ` (2 preceding siblings ...)
  2025-01-09 14:03   ` kernel test robot
@ 2025-01-09 15:17   ` Eugene Syromiatnikov
  2025-01-09 15:21   ` Oleg Nesterov
  4 siblings, 0 replies; 22+ messages in thread
From: Eugene Syromiatnikov @ 2025-01-09 15:17 UTC (permalink / raw)
  To: Dmitry V. Levin
  Cc: Oleg Nesterov, Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, linux-kernel, linux-api

On Wed, Jan 08, 2025 at 01:04:56AM +0200, Dmitry V. Levin wrote:
> PTRACE_SET_SYSCALL_INFO is a generic ptrace API that complements
> PTRACE_GET_SYSCALL_INFO by letting the ptracer modify details of
> system calls the tracee is blocked in.
> 
> This API allows ptracers to obtain and modify system call details
> in a straightforward and architecture-agnostic way.
> 
> Current implementation supports changing only those bits of system call
> information that are used by strace, namely, syscall number, syscall
> arguments, and syscall return value.
> 
> Support of changing additional details returned by PTRACE_GET_SYSCALL_INFO,
> such as instruction pointer and stack pointer, could be added later
> if needed, by re-using struct ptrace_syscall_info.reserved to specify
> the additional details that should be set.  Currently, the reserved
> field of struct ptrace_syscall_info must be initialized with zeroes;
> arch, instruction_pointer, and stack_pointer fields are ignored.
> 
> PTRACE_SET_SYSCALL_INFO currently supports only PTRACE_SYSCALL_INFO_ENTRY,
> PTRACE_SYSCALL_INFO_EXIT, and PTRACE_SYSCALL_INFO_SECCOMP operations.
> Other operations could be added later if needed.
> 
> Ideally, PTRACE_SET_SYSCALL_INFO should have been introduced along with
> PTRACE_GET_SYSCALL_INFO, but it didn't happen.  The last straw that
> convinced me to implement PTRACE_SET_SYSCALL_INFO was apparent failure
> to provide an API of changing the first system call argument on riscv
> architecture.

> index 72c038fc71d0..231b8bf7eeff 100644
> --- a/include/uapi/linux/ptrace.h
> +++ b/include/uapi/linux/ptrace.h
> @@ -74,6 +74,7 @@ struct seccomp_metadata {
>  };
>  
>  #define PTRACE_GET_SYSCALL_INFO		0x420e
> +#define PTRACE_SET_SYSCALL_INFO		0x4212

It seems prudent to also add a comment about 0x4212 being taken right
after "#define PTRACE_GET_SYSCALL_USER_DISPATCH_CONFIG 0x4211", or the
usage of this ptrace request number may be overlooked on the next update.

>  struct ptrace_syscall_info {
>  	__u8 op;	/* PTRACE_SYSCALL_INFO_* */
> -	__u8 pad[3];
> +	__u8 reserved[3];
>  	__u32 arch;
>  	__u64 instruction_pointer;
>  	__u64 stack_pointer;

I would like to suggest adding flags for changing scno and args right
away;  while it is possibly of limited use and seems like an unnecessary
overcomplication, at least changing arguments only seems natural to me,
to avoid possible interaction with scno-related shenanigans that might
present/appear on some kernels and/or architectures.  Also, it makes
the aforementioned possible extensions of the interface (changing
of ip/sp) more natural (as in those cases users might definitely want
to avoid touching syscall number/arguments).


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request
  2025-01-07 23:04 ` [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request Dmitry V. Levin
                     ` (3 preceding siblings ...)
  2025-01-09 15:17   ` Eugene Syromiatnikov
@ 2025-01-09 15:21   ` Oleg Nesterov
  2025-01-11 11:49     ` Dmitry V. Levin
  4 siblings, 1 reply; 22+ messages in thread
From: Oleg Nesterov @ 2025-01-09 15:21 UTC (permalink / raw)
  To: Dmitry V. Levin
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, linux-kernel, linux-api

On 01/08, Dmitry V. Levin wrote:
>
> +ptrace_set_syscall_info_entry(struct task_struct *child, struct pt_regs *regs,
> +			      struct ptrace_syscall_info *info)
> +{
...
> +	syscall_set_nr(child, regs, nr);
> +	syscall_set_arguments(child, regs, args);
> +	if (nr == -1) {
> +		/*
> +		 * When the syscall number is set to -1, the syscall will be
> +		 * skipped.  In this case also set the syscall return value to
> +		 * -ENOSYS, otherwise on some architectures the corresponding
> +		 * struct pt_regs field will remain unchanged.
> +		 *
> +		 * Note that on some architectures syscall_set_return_value()
> +		 * modifies one of the struct pt_regs fields also modified by
> +		 * syscall_set_arguments(), so the former should be called
> +		 * after the latter.
> +		 */
> +		syscall_set_return_value(child, regs, -ENOSYS, 0);
> +	}

This doesn't look nice to me...

We don't need this syscall_set_return_value(ENOSYS) on x86, right?

So perhaps we should move this "if (nr == -1) code  into
syscall_set_nr/syscall_set_arguments on those "some architectures" which
actually need it ?

Oleg.


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO API
  2025-01-07 23:01 [PATCH 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO API Dmitry V. Levin
                   ` (5 preceding siblings ...)
  2025-01-07 23:05 ` [PATCH 6/6] selftests/ptrace: add a test case for PTRACE_SET_SYSCALL_INFO Dmitry V. Levin
@ 2025-01-10  3:15 ` H. Peter Anvin
  6 siblings, 0 replies; 22+ messages in thread
From: H. Peter Anvin @ 2025-01-10  3:15 UTC (permalink / raw)
  To: Dmitry V. Levin, Oleg Nesterov
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, Vineet Gupta, Russell King,
	Catalin Marinas, Will Deacon, Guo Ren, Brian Cain, Huacai Chen,
	WANG Xuerui, Geert Uytterhoeven, Michal Simek,
	Thomas Bogendoerfer, Dinh Nguyen, Jonas Bonn, Stefan Kristiansson,
	Stafford Horne, James E.J. Bottomley, Helge Deller,
	Michael Ellerman, Nicholas Piggin, Christophe Leroy, Naveen N Rao,
	Madhavan Srinivasan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger, Sven Schnelle, Yoshinori Sato, Rich Felker,
	John Paul Adrian Glaubitz, David S. Miller, Andreas Larsson,
	Richard Weinberger, Anton Ivanov, Johannes Berg, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, Chris Zankel,
	Max Filippov, Arnd Bergmann, Shuah Khan, linux-snps-arc,
	linux-kernel, linux-arm-kernel, linux-csky, linux-hexagon,
	loongarch, linux-m68k, linux-mips, linux-openrisc, linux-parisc,
	linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
	linux-um, linux-arch, linux-kselftest

This would seem like a very good idea. However, it is perhaps important 
to realize that it doesn't fully eliminate the problems with 64-bit 
arguments on 32-bit ABIs being handled differently (never mind 
inconsistencies in system call ABIs etc.) There isn't all that much that 
can be done about that directly, though.

	-hpa


On 1/7/25 15:01, Dmitry V. Levin wrote:
> PTRACE_SET_SYSCALL_INFO is a generic ptrace API that complements
> PTRACE_GET_SYSCALL_INFO by letting the ptracer modify details of
> system calls the tracee is blocked in.
> 
> This API allows ptracers to obtain and modify system call details
> in a straightforward and architecture-agnostic way.
> 
> Current implementation supports changing only those bits of system call
> information that are used by strace, namely, syscall number, syscall
> arguments, and syscall return value.
> 
> Support of changing additional details returned by PTRACE_GET_SYSCALL_INFO,
> such as instruction pointer and stack pointer, could be added later
> if needed, by re-using struct ptrace_syscall_info.reserved to specify
> the additional details that should be set.  Currently, the reserved
> field of struct ptrace_syscall_info must be initialized with zeroes;
> arch, instruction_pointer, and stack_pointer fields are ignored.
> 
> PTRACE_SET_SYSCALL_INFO currently supports only PTRACE_SYSCALL_INFO_ENTRY,
> PTRACE_SYSCALL_INFO_EXIT, and PTRACE_SYSCALL_INFO_SECCOMP operations.
> Other operations could be added later if needed.
> 
> Ideally, PTRACE_SET_SYSCALL_INFO should have been introduced along with
> PTRACE_GET_SYSCALL_INFO, but it didn't happen.  The last straw that
> convinced me to implement PTRACE_SET_SYSCALL_INFO was apparent failure
> to provide an API of changing the first system call argument on riscv
> architecture [1].
> 
> ptrace(2) man page:
> 
> long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
> ...
> PTRACE_SET_SYSCALL_INFO
>         Modify information about the system call that caused the stop.
>         The "data" argument is a pointer to struct ptrace_syscall_info
>         that specifies the system call information to be set.
>         The "addr" argument should be set to sizeof(struct ptrace_syscall_info)).
> 
> [1] https://lore.kernel.org/all/59505464-c84a-403d-972f-d4b2055eeaac@gmail.com/
> 
> Dmitry V. Levin (6):
>    Revert "arch: remove unused function syscall_set_arguments()"
>    syscall.h: add syscall_set_arguments() on remaining
>      HAVE_ARCH_TRACEHOOK arches
>    syscall.h: introduce syscall_set_nr()
>    ptrace_get_syscall_info: factor out ptrace_get_syscall_info_op
>    ptrace: introduce PTRACE_SET_SYSCALL_INFO request
>    selftests/ptrace: add a test case for PTRACE_SET_SYSCALL_INFO
> 
>   arch/arc/include/asm/syscall.h                |  20 +
>   arch/arm/include/asm/syscall.h                |  25 +
>   arch/arm64/include/asm/syscall.h              |  20 +
>   arch/csky/include/asm/syscall.h               |  13 +
>   arch/hexagon/include/asm/syscall.h            |  14 +
>   arch/loongarch/include/asm/syscall.h          |  15 +
>   arch/m68k/include/asm/syscall.h               |   7 +
>   arch/microblaze/include/asm/syscall.h         |   7 +
>   arch/mips/include/asm/syscall.h               |  53 +++
>   arch/nios2/include/asm/syscall.h              |  16 +
>   arch/openrisc/include/asm/syscall.h           |  13 +
>   arch/parisc/include/asm/syscall.h             |  19 +
>   arch/powerpc/include/asm/syscall.h            |  15 +
>   arch/riscv/include/asm/syscall.h              |  16 +
>   arch/s390/include/asm/syscall.h               |  19 +
>   arch/sh/include/asm/syscall_32.h              |  19 +
>   arch/sparc/include/asm/syscall.h              |  17 +
>   arch/um/include/asm/syscall-generic.h         |  19 +
>   arch/x86/include/asm/syscall.h                |  43 ++
>   arch/xtensa/include/asm/syscall.h             |  18 +
>   include/asm-generic/syscall.h                 |  30 ++
>   include/linux/ptrace.h                        |   3 +
>   include/uapi/linux/ptrace.h                   |   3 +-
>   kernel/ptrace.c                               | 154 ++++++-
>   tools/testing/selftests/ptrace/Makefile       |   2 +-
>   .../selftests/ptrace/set_syscall_info.c       | 436 ++++++++++++++++++
>   26 files changed, 994 insertions(+), 22 deletions(-)
>   create mode 100644 tools/testing/selftests/ptrace/set_syscall_info.c
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 3/6] syscall.h: introduce syscall_set_nr()
  2025-01-07 23:04 ` [PATCH 3/6] syscall.h: introduce syscall_set_nr() Dmitry V. Levin
@ 2025-01-10  7:37   ` Sven Schnelle
  2025-01-11  1:16     ` Dmitry V. Levin
  0 siblings, 1 reply; 22+ messages in thread
From: Sven Schnelle @ 2025-01-10  7:37 UTC (permalink / raw)
  To: Dmitry V. Levin
  Cc: Oleg Nesterov, Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, Vineet Gupta, Russell King,
	Catalin Marinas, Will Deacon, Brian Cain, Huacai Chen,
	WANG Xuerui, Geert Uytterhoeven, Michal Simek,
	Thomas Bogendoerfer, Dinh Nguyen, Jonas Bonn, Stefan Kristiansson,
	Stafford Horne, James E.J. Bottomley, Helge Deller,
	Michael Ellerman, Nicholas Piggin, Christophe Leroy, Naveen N Rao,
	Madhavan Srinivasan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger, Yoshinori Sato, Rich Felker,
	John Paul Adrian Glaubitz, David S. Miller, Andreas Larsson,
	Richard Weinberger, Anton Ivanov, Johannes Berg, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Chris Zankel, Max Filippov, Arnd Bergmann, linux-snps-arc,
	linux-kernel, linux-arm-kernel, linux-hexagon, loongarch,
	linux-m68k, linux-mips, linux-openrisc, linux-parisc,
	linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
	linux-um, linux-arch

"Dmitry V. Levin" <ldv@strace.io> writes:

> Similar to syscall_set_arguments() that complements
> syscall_get_arguments(), introduce syscall_set_nr()
> that complements syscall_get_nr().
>
> syscall_set_nr() is going to be needed along with
> syscall_set_arguments() on all HAVE_ARCH_TRACEHOOK
> architectures to implement PTRACE_SET_SYSCALL_INFO API.
>
> Signed-off-by: Dmitry V. Levin <ldv@strace.io>
> ---
>  arch/arc/include/asm/syscall.h        |  6 ++++++
>  arch/arm/include/asm/syscall.h        | 12 ++++++++++++
>  arch/arm64/include/asm/syscall.h      |  7 +++++++
>  arch/hexagon/include/asm/syscall.h    |  7 +++++++
>  arch/loongarch/include/asm/syscall.h  |  7 +++++++
>  arch/m68k/include/asm/syscall.h       |  7 +++++++
>  arch/microblaze/include/asm/syscall.h |  7 +++++++
>  arch/mips/include/asm/syscall.h       |  7 +++++++
>  arch/nios2/include/asm/syscall.h      |  5 +++++
>  arch/openrisc/include/asm/syscall.h   |  6 ++++++
>  arch/parisc/include/asm/syscall.h     |  7 +++++++
>  arch/powerpc/include/asm/syscall.h    |  5 +++++
>  arch/riscv/include/asm/syscall.h      |  7 +++++++
>  arch/s390/include/asm/syscall.h       |  7 +++++++
>  arch/sh/include/asm/syscall_32.h      |  7 +++++++
>  arch/sparc/include/asm/syscall.h      |  7 +++++++
>  arch/um/include/asm/syscall-generic.h |  5 +++++
>  arch/x86/include/asm/syscall.h        |  7 +++++++
>  arch/xtensa/include/asm/syscall.h     |  7 +++++++
>  include/asm-generic/syscall.h         | 14 ++++++++++++++
>  20 files changed, 144 insertions(+)
>
> diff --git a/arch/s390/include/asm/syscall.h b/arch/s390/include/asm/syscall.h
> index b3dd883699e7..1c0e349fd5c9 100644
> --- a/arch/s390/include/asm/syscall.h
> +++ b/arch/s390/include/asm/syscall.h
> @@ -24,6 +24,13 @@ static inline long syscall_get_nr(struct task_struct *task,
>  		(regs->int_code & 0xffff) : -1;
>  }
>  
> +static inline void syscall_set_nr(struct task_struct *task,
> +				  struct pt_regs *regs,
> +				  int nr)
> +{

I think there should be a

	if (!test_pt_regs_flags(regs, PIF_SYSCALL))
		return;

before the modification so a user can't accidentally change int_code
when ptrace stopped in a non-syscall path.

> +	regs->int_code = (regs->int_code & ~0xffff) | (nr & 0xffff);
> +}
> +
>  static inline void syscall_rollback(struct task_struct *task,
>  				    struct pt_regs *regs)
>  {

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 3/6] syscall.h: introduce syscall_set_nr()
  2025-01-10  7:37   ` Sven Schnelle
@ 2025-01-11  1:16     ` Dmitry V. Levin
  0 siblings, 0 replies; 22+ messages in thread
From: Dmitry V. Levin @ 2025-01-11  1:16 UTC (permalink / raw)
  To: Sven Schnelle
  Cc: Oleg Nesterov, Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, Vineet Gupta, Russell King,
	Catalin Marinas, Will Deacon, Brian Cain, Huacai Chen,
	WANG Xuerui, Geert Uytterhoeven, Michal Simek,
	Thomas Bogendoerfer, Dinh Nguyen, Jonas Bonn, Stefan Kristiansson,
	Stafford Horne, James E.J. Bottomley, Helge Deller,
	Michael Ellerman, Nicholas Piggin, Christophe Leroy, Naveen N Rao,
	Madhavan Srinivasan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
	Christian Borntraeger, Yoshinori Sato, Rich Felker,
	John Paul Adrian Glaubitz, David S. Miller, Andreas Larsson,
	Richard Weinberger, Anton Ivanov, Johannes Berg, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Chris Zankel, Max Filippov, Arnd Bergmann, linux-snps-arc,
	linux-kernel, linux-arm-kernel, linux-hexagon, loongarch,
	linux-m68k, linux-mips, linux-openrisc, linux-parisc,
	linuxppc-dev, linux-riscv, linux-s390, linux-sh, sparclinux,
	linux-um, linux-arch

On Fri, Jan 10, 2025 at 08:37:46AM +0100, Sven Schnelle wrote:
> "Dmitry V. Levin" <ldv@strace.io> writes:
> 
> > Similar to syscall_set_arguments() that complements
> > syscall_get_arguments(), introduce syscall_set_nr()
> > that complements syscall_get_nr().
> >
> > syscall_set_nr() is going to be needed along with
> > syscall_set_arguments() on all HAVE_ARCH_TRACEHOOK
> > architectures to implement PTRACE_SET_SYSCALL_INFO API.
[...]
> > diff --git a/arch/s390/include/asm/syscall.h b/arch/s390/include/asm/syscall.h
> > index b3dd883699e7..1c0e349fd5c9 100644
> > --- a/arch/s390/include/asm/syscall.h
> > +++ b/arch/s390/include/asm/syscall.h
> > @@ -24,6 +24,13 @@ static inline long syscall_get_nr(struct task_struct *task,
> >  		(regs->int_code & 0xffff) : -1;
> >  }
> >  
> > +static inline void syscall_set_nr(struct task_struct *task,
> > +				  struct pt_regs *regs,
> > +				  int nr)
> > +{
> 
> I think there should be a
> 
> 	if (!test_pt_regs_flags(regs, PIF_SYSCALL))
> 		return;
> 
> before the modification so a user can't accidentally change int_code
> when ptrace stopped in a non-syscall path.

The reason why syscall_get_nr() has this check on s390 (and similar checks
on arc, powerpc, and sparc) is that syscall_get_nr() can be called while
the target task is not in syscall.

Unlike syscall_get_nr(), syscall_set_nr() can be called only when the
target task is stopped for tracing on entering syscall: the description in
include/asm-generic/syscall.h explicitly states that, and the follow-up
patch that introduces PTRACE_SET_SYSCALL_INFO adds a syscall_set_nr() call
when the tracee is stopped on entering syscall in either
PTRACE_SYSCALL_INFO_ENTRY or PTRACE_SYSCALL_INFO_SECCOMP state.

I don't mind adding a check, but syscall_set_nr() invocation while the
target task is not in syscall wouldn't be a result of user actions but
a kernel programing error, and in that case WARN_ON_ONCE() would be more
appropriate.

If calling syscall_set_nr() while the target task is not in syscall was
legal, then syscall_set_nr() would have been designed to return a value
indicating the status of operation.

Anyway, I'll add an explanatory comment to syscall_set_nr() on all
architectures where syscall_get_nr() has a check.


-- 
ldv

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request
  2025-01-09 15:21   ` Oleg Nesterov
@ 2025-01-11 11:49     ` Dmitry V. Levin
  0 siblings, 0 replies; 22+ messages in thread
From: Dmitry V. Levin @ 2025-01-11 11:49 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Eugene Syromyatnikov, Mike Frysinger, Renzo Davoli,
	Davide Berardi, strace-devel, linux-kernel, linux-api

On Thu, Jan 09, 2025 at 04:21:39PM +0100, Oleg Nesterov wrote:
> On 01/08, Dmitry V. Levin wrote:
> >
> > +ptrace_set_syscall_info_entry(struct task_struct *child, struct pt_regs *regs,
> > +			      struct ptrace_syscall_info *info)
> > +{
> ...
> > +	syscall_set_nr(child, regs, nr);
> > +	syscall_set_arguments(child, regs, args);
> > +	if (nr == -1) {
> > +		/*
> > +		 * When the syscall number is set to -1, the syscall will be
> > +		 * skipped.  In this case also set the syscall return value to
> > +		 * -ENOSYS, otherwise on some architectures the corresponding
> > +		 * struct pt_regs field will remain unchanged.
> > +		 *
> > +		 * Note that on some architectures syscall_set_return_value()
> > +		 * modifies one of the struct pt_regs fields also modified by
> > +		 * syscall_set_arguments(), so the former should be called
> > +		 * after the latter.
> > +		 */
> > +		syscall_set_return_value(child, regs, -ENOSYS, 0);
> > +	}
> 
> This doesn't look nice to me...
> 
> We don't need this syscall_set_return_value(ENOSYS) on x86, right?

No, we don't need this on x86.

> So perhaps we should move this "if (nr == -1) code  into
> syscall_set_nr/syscall_set_arguments on those "some architectures" which
> actually need it ?

Thanks for the suggestion.  I think the best option is to skip
syscall_set_arguments() invocation in case of nr == -1.  It's not just
pointless, but also it would clobber the syscall return value on those
architectures like arm64 that share the same register both for the first
argument of syscall and its return value.

This is what I'm going to implement for the next iteration of the series:

	syscall_set_nr(child, regs, nr);
	/*
	 * If the syscall number is set to -1, setting syscall arguments is not
	 * just pointless, it would also clobber the syscall return value on
	 * those architectures that share the same register both for the first
	 * argument of syscall and its return value.
	 */
	if (nr != -1)
		syscall_set_arguments(child, regs, args);


-- 
ldv

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2025-01-11 11:49 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-07 23:01 [PATCH 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO API Dmitry V. Levin
2025-01-07 23:04 ` [PATCH 1/6] Revert "arch: remove unused function syscall_set_arguments()" Dmitry V. Levin
2025-01-07 23:04 ` [PATCH 2/6] syscall.h: add syscall_set_arguments() on remaining HAVE_ARCH_TRACEHOOK arches Dmitry V. Levin
2025-01-09 12:04   ` Oleg Nesterov
2025-01-09 12:11     ` Dmitry V. Levin
2025-01-07 23:04 ` [PATCH 3/6] syscall.h: introduce syscall_set_nr() Dmitry V. Levin
2025-01-10  7:37   ` Sven Schnelle
2025-01-11  1:16     ` Dmitry V. Levin
2025-01-07 23:04 ` [PATCH 4/6] ptrace_get_syscall_info: factor out ptrace_get_syscall_info_op Dmitry V. Levin
2025-01-09 10:09   ` Oleg Nesterov
2025-01-09 10:27     ` Dmitry V. Levin
2025-01-09 10:41   ` Oleg Nesterov
2025-01-09 11:14     ` Dmitry V. Levin
2025-01-07 23:04 ` [PATCH 5/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO request Dmitry V. Levin
2025-01-09  1:37   ` kernel test robot
2025-01-09  2:21   ` kernel test robot
2025-01-09 14:03   ` kernel test robot
2025-01-09 15:17   ` Eugene Syromiatnikov
2025-01-09 15:21   ` Oleg Nesterov
2025-01-11 11:49     ` Dmitry V. Levin
2025-01-07 23:05 ` [PATCH 6/6] selftests/ptrace: add a test case for PTRACE_SET_SYSCALL_INFO Dmitry V. Levin
2025-01-10  3:15 ` [PATCH 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO API H. Peter Anvin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox