]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
syscall.h: introduce syscall_set_nr()
authorDmitry V. Levin <ldv@strace.io>
Mon, 3 Mar 2025 11:20:20 +0000 (13:20 +0200)
committerAndrew Morton <akpm@linux-foundation.org>
Mon, 12 May 2025 00:48:15 +0000 (17:48 -0700)
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.

Link: https://lkml.kernel.org/r/20250303112020.GD24170@strace.io
Signed-off-by: Dmitry V. Levin <ldv@strace.io>
Tested-by: Charlie Jenkins <charlie@rivosinc.com>
Reviewed-by: Charlie Jenkins <charlie@rivosinc.com>
Acked-by: Helge Deller <deller@gmx.de> # parisc
Reviewed-by: Maciej W. Rozycki <macro@orcam.me.uk> # mips
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Alexey Gladkov (Intel) <legion@kernel.org>
Cc: Andreas Larsson <andreas@gaisler.com>
Cc: anton ivanov <anton.ivanov@cambridgegreys.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Borislav Betkov <bp@alien8.de>
Cc: Brian Cain <bcain@quicinc.com>
Cc: Christian Borntraeger <borntraeger@linux.ibm.com>
Cc: Christian Zankel <chris@zankel.net>
Cc: Christophe Leroy <christophe.leroy@csgroup.eu>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Davide Berardi <berardi.dav@gmail.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Dinh Nguyen <dinguyen@kernel.org>
Cc: Eugene Syromiatnikov <esyr@redhat.com>
Cc: Eugene Syromyatnikov <evgsyr@gmail.com>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Guo Ren <guoren@kernel.org>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Huacai Chen <chenhuacai@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Johannes Berg <johannes@sipsolutions.net>
Cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
Cc: Jonas Bonn <jonas@southpole.se>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Max Filippov <jcmvbkbc@gmail.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Michal Simek <monstr@monstr.eu>
Cc: Mike Frysinger <vapier@gentoo.org>
Cc: Naveen N Rao <naveen@kernel.org>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Renzo Davoi <renzo@cs.unibo.it>
Cc: Richard Weinberger <richard@nod.at>
Cc: Rich Felker <dalias@libc.org>
Cc: Russel King <linux@armlinux.org.uk>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Stafford Horne <shorne@gmail.com>
Cc: Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
Cc: Sven Schnelle <svens@linux.ibm.com>
Cc: Thomas Gleinxer <tglx@linutronix.de>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: Vineet Gupta <vgupta@kernel.org>
Cc: WANG Xuerui <kernel@xen0n.name>
Cc: Will Deacon <will@kernel.org>
Cc: Yoshinori Sato <ysato@users.sourceforge.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
20 files changed:
arch/arc/include/asm/syscall.h
arch/arm/include/asm/syscall.h
arch/arm64/include/asm/syscall.h
arch/hexagon/include/asm/syscall.h
arch/loongarch/include/asm/syscall.h
arch/m68k/include/asm/syscall.h
arch/microblaze/include/asm/syscall.h
arch/mips/include/asm/syscall.h
arch/nios2/include/asm/syscall.h
arch/openrisc/include/asm/syscall.h
arch/parisc/include/asm/syscall.h
arch/powerpc/include/asm/syscall.h
arch/riscv/include/asm/syscall.h
arch/s390/include/asm/syscall.h
arch/sh/include/asm/syscall_32.h
arch/sparc/include/asm/syscall.h
arch/um/include/asm/syscall-generic.h
arch/x86/include/asm/syscall.h
arch/xtensa/include/asm/syscall.h
include/asm-generic/syscall.h

index 89c1e1736356f41472cc3bca6216446e8c725581..728d625a10f1ebb0bd81b94ac93cc640a98276b5 100644 (file)
@@ -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)
 {
index 21927fa0ae2b046af747e003cf73412f11ade6b9..18b102a3074199fa4e79d066fc02a6eec31246cf 100644 (file)
@@ -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,
index 76020b66286b8eac12e7c11e92f8f7aa5fd63da2..712daa90e6431df303911e38dfc845edd74e43df 100644 (file)
@@ -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,
index 1024a6548d78e439fb10b4d7a9e580a9b7ed11b3..70637261817afc8850a98f87118031ef255e7955 100644 (file)
@@ -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)
index ff415b3c0a8ef159497a541656e4f18912181054..81d2733f7b949af90f1f0d0b3a0fb61625bae685 100644 (file)
@@ -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)
 {
index d1453e850cddb03128456ee898f9348c58f0c7f0..bf84b160c2eb3a96438d8ff61a248a08f1a0f8b7 100644 (file)
@@ -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)
 {
index 5eb3f624cc590a8d293da9c29a7b237f95487a36..b5b6b91fae3e970bc3dd5451c7856418c1d06813 100644 (file)
@@ -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)
 {
index f1926ce30d4bc6a2d929c1e21d6625a3c928dd78..d19e67e2aa6a118316f59c950fc9fb855abd2768 100644 (file)
@@ -41,6 +41,21 @@ 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)
+{
+       /*
+        * New syscall number has to be assigned to regs[2] because
+        * it is loaded from there unconditionally after return from
+        * syscall_trace_enter() invocation.
+        *
+        * Consequently, if the syscall was indirect and nr != __NR_syscall,
+        * then after this assignment the syscall will cease to be indirect.
+        */
+       task_thread_info(task)->syscall = regs->regs[2] = nr;
+}
+
 static inline void mips_syscall_update_nr(struct task_struct *task,
                                          struct pt_regs *regs)
 {
index 526449edd768c01b6f0a218807e768bd0e4686f8..8e3eb1d689bbee5c142e0635f4572ad8ef99f1aa 100644 (file)
@@ -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)
 {
index e6383be2a19522cf9f2d2830f2a73668a7dec113..5e037d9659c5bcf0a34a5fc1757f26ad11c89a00 100644 (file)
@@ -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)
 {
index b146d0ae4c77aa2697794964485fc1a5b7497dd0..c11222798ab2f4ec20f33a76e387919f3ee0c312 100644 (file)
@@ -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)
index b2715448a660700ecb6d19a63cf859e8697e5590..4b3c52ed6e9d2b099ad30cefc858ffcda76438b3 100644 (file)
@@ -39,6 +39,16 @@ 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)
+{
+       /*
+        * 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->gpr[0] = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
                                    struct pt_regs *regs)
 {
index da56417b6705ba3ecdfb71349f11fc944e193624..34313387f9774c43009ad0a24ed5b3520fadd34e 100644 (file)
@@ -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)
 {
index b87d8bb2cbaa929e215872edf890a2bc57275508..bd4cb00ccd5e3a1f2f7b6fc54679376240f87dd4 100644 (file)
@@ -24,6 +24,18 @@ 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)
+{
+       /*
+        * 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->int_code = (regs->int_code & ~0xffff) | (nr & 0xffff);
+}
+
 static inline void syscall_rollback(struct task_struct *task,
                                    struct pt_regs *regs)
 {
index cb51a752838425e85b8735143e3b73fdd32700de..7027d87d901df7c48ee2cbbd9cbc91570e187b80 100644 (file)
@@ -15,6 +15,18 @@ 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)
+{
+       /*
+        * 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->regs[3] = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
                                    struct pt_regs *regs)
 {
index 62a5a78804c46d76f5155d337117f8c7fee0baf1..b0233924d323252a198c6456a9095d0bb63d5e14 100644 (file)
@@ -25,6 +25,18 @@ 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)
+{
+       /*
+        * 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->u_regs[UREG_G1] = nr;
+}
+
 static inline void syscall_rollback(struct task_struct *task,
                                    struct pt_regs *regs)
 {
index 2984feb9d5762f224d2d9de987e26b4fcf77a556..bcd73bcfe5771e955ea0082493808bf3361ff844 100644 (file)
@@ -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)
 {
index b9c249dd9e3d76f3406cd3a1602337e8f15121f2..c10dbb74cd001e2c5920150c239d38fde3d4b5a6 100644 (file)
@@ -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)
 {
index f9a671cbf9338003704de9c2daf006219b1b8eb9..7db3b489c8ad08189fb5842e3f22eaeeaa1cbe15 100644 (file)
@@ -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)
 {
index 292b412f4e9a56b9bb0339340b6a473622bd5541..c5a3ad53beec68825e2036b071bc50b840cc3d11 100644 (file)
@@ -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