[PATCH v6 3/6] syscall.h: introduce syscall_set_nr()
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 Tested-by: Charlie Jenkins Reviewed-by: Charlie Jenkins Acked-by: Helge Deller # parisc --- arch/arc/include/asm/syscall.h| 11 +++ arch/arm/include/asm/syscall.h| 24 arch/arm64/include/asm/syscall.h | 16 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 | 14 ++ 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| 10 ++ arch/riscv/include/asm/syscall.h | 7 +++ arch/s390/include/asm/syscall.h | 12 arch/sh/include/asm/syscall_32.h | 12 arch/sparc/include/asm/syscall.h | 12 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, 197 insertions(+) diff --git a/arch/arc/include/asm/syscall.h b/arch/arc/include/asm/syscall.h index 89c1e1736356..728d625a10f1 100644 --- a/arch/arc/include/asm/syscall.h +++ b/arch/arc/include/asm/syscall.h @@ -23,6 +23,17 @@ 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) +{ + /* +* Unlike syscall_get_nr(), syscall_set_nr() can be called only when +* the target task is stopped for tracing on entering syscall, so +* there is no need to have the same check syscall_get_nr() has. +*/ + 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..18b102a30741 100644 --- a/arch/arm/include/asm/syscall.h +++ b/arch/arm/include/asm/syscall.h @@ -68,6 +68,30 @@ static inline void syscall_set_return_value(struct task_struct *task, regs->ARM_r0 = (long) error ? error : val; } +static inline void syscall_set_nr(struct task_struct *task, + struct pt_regs *regs, + int nr) +{ + if (nr == -1) { + task_thread_info(task)->abi_syscall = -1; + /* +* When the syscall number is set to -1, the syscall will be +* skipped. In this case the syscall return value has to be +* set explicitly, otherwise the first syscall argument is +* returned as the syscall return value. +*/ + syscall_set_return_value(task, regs, -ENOSYS, 0); + return; + } + if ((IS_ENABLED(CONFIG_AEABI) && !IS_ENABLED(CONFIG_OABI_COMPAT))) { + task_thread_info(task)->abi_syscall = nr; + return; + } + task_thread_info(task)->abi_syscall = + (task_thread_info(task)->abi_syscall & ~__NR_SYSCALL_MASK) | + (nr & __NR_SYSCALL_MASK); +} + #define SYSCALL_MAX_ARGS 7 static inline void syscall_get_arguments(struct task_struct *task, diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 76020b66286b..712daa90e643 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -61,6 +61,22 @@ static inline void syscall_set_return_value(struct task_struct *task, regs->regs[0] = val; } +static inline void syscall_set_nr(struct task_struct *task, + struct pt_regs *regs, + int nr) +{ + regs->syscallno = nr; + if (nr == -1) { + /* +* When the syscall number is set to -1, the syscall will be +* skipped. In this case the syscall return value has to be +* set explicitly, otherwise the first syscall argument is +* returned as the syscall return value. +*/ + syscall_set_return_value(task, regs, -ENOSYS, 0); + } +} + #define SYSCALL_MAX_ARGS 6 static inline void syscall_get_arguments(struct task_struct *task, diff --git a/arch/hexagon/include/asm/syscall.h b/arch/hexagon/include/asm/syscall.h index 1024a6548d78..70637261817a 100644 --- a/arch/hexagon/include/asm/syscall.h +++ b/arch/hexagon/in
[PATCH v6 0/6] ptrace: introduce PTRACE_SET_SYSCALL_INFO 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, providing a consistent way of manipulating the system call number and arguments across architectures. As in case of PTRACE_GET_SYSCALL_INFO, PTRACE_SET_SYSCALL_INFO also does not aim to address numerous architecture-specific system call ABI peculiarities, like differences in the number of system call arguments for such system calls as pread64 and preadv. The current implementation supports changing only those bits of system call information that are used by strace system call tampering, 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 using struct ptrace_syscall_info.flags to specify the additional details that should be set. Currently, "flags" and "reserved" fields of struct ptrace_syscall_info must be initialized with zeroes; "arch", "instruction_pointer", and "stack_pointer" fields are currently 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-d4b2055ee...@gmail.com/ Notes: v6: * mips: Submit mips_get_syscall_arg() o32 fix via mips tree to get it merged into v6.14-rc3 * Rebase to v6.14-rc3 * v5: https://lore.kernel.org/all/20250210113336.ga...@strace.io/ v5: * ptrace: Extend the commit message to say that the new API does not aim to address numerous architecture-specific syscall ABI peculiarities * selftests: Add a workaround for s390 16-bit syscall numbers * Add more Acked-by * v4: https://lore.kernel.org/all/20250203065849.ga14...@strace.io/ v4: * Split out syscall_set_return_value() for hexagon into a separate patch * s390: Change the style of syscall_set_arguments() implementation as requested * Add more Reviewed-by * v3: https://lore.kernel.org/all/20250128091445.ga8...@strace.io/ v3: * powerpc: Submit syscall_set_return_value() fix for "sc" case separately * mips: Do not introduce erroneous argument truncation on mips n32, add a detailed description to the commit message of the mips_get_syscall_arg() change * ptrace: Add explicit padding to the end of struct ptrace_syscall_info, simplify obtaining of user ptrace_syscall_info, do not introduce PTRACE_SYSCALL_INFO_SIZE_VER0 * ptrace: Change the return type of ptrace_set_syscall_info_* functions from "unsigned long" to "int" * ptrace: Add -ERANGE check to ptrace_set_syscall_info_exit(), add comments to -ERANGE checks * ptrace: Update comments about supported syscall stops * selftests: Extend set_syscall_info test, fix for mips n32 * Add Tested-by and Reviewed-by v2: * Add patch to fix syscall_set_return_value() on powerpc * Add patch to fix mips_get_syscall_arg() on mips * Add syscall_set_return_value() implementation on hexagon * Add syscall_set_return_value() invocation to syscall_set_nr() on arm and arm64. * Fix syscall_set_nr() and mips_set_syscall_arg() on mips * Add a comment to syscall_set_nr() on arc, powerpc, s390, sh, and sparc * Remove redundant ptrace_syscall_info.op assignments in ptrace_get_syscall_info_* * Minor style tweaks in ptrace_get_syscall_info_op() * Remove syscall_set_return_value() invocation from ptrace_set_syscall_info_entry() * Skip syscall_set_arguments() invocation in case of syscall number -1 in ptrace_set_syscall_info_entry() * Split ptrace_syscall_info.reserved into ptrace_syscall_info.reserved and ptrace_syscall_info.flags * Use __kernel_ulong_t instead of unsigned long in set_syscall_info test Dmitry V. Levin (6): hexagon: add syscall_set_return_value() syscall.h: add syscall_set_arguments() syscall.h: intro
[PATCH v6 2/6] syscall.h: add syscall_set_arguments()
This function is going to be needed on all HAVE_ARCH_TRACEHOOK architectures to implement PTRACE_SET_SYSCALL_INFO API. This partially reverts commit 7962c2eddbfe ("arch: remove unused function syscall_set_arguments()") by reusing some of old syscall_set_arguments() implementations. Signed-off-by: Dmitry V. Levin Tested-by: Charlie Jenkins Reviewed-by: Charlie Jenkins Acked-by: Helge Deller # parisc --- 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 | 32 arch/nios2/include/asm/syscall.h | 11 arch/openrisc/include/asm/syscall.h | 7 ++ arch/parisc/include/asm/syscall.h | 12 + arch/powerpc/include/asm/syscall.h| 10 arch/riscv/include/asm/syscall.h | 9 +++ arch/s390/include/asm/syscall.h | 9 +++ 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 19 files changed, 257 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 = ®s->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, ®s->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(®s->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, ®s->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(®s->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, ®s->a1, 5 * sizeof(args[0])); } +static inline void +syscall_set_arguments(struct task_struct *task, struct pt_regs *regs, + const unsigned long *args) +{ + memcpy(®s->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/inc