Skip to contentSkip to conversation

audit: log all six syscall arguments in the SYSCALL record

PATCH Superseded Ricardo Robaina 4 participants 33 messages Sep 2 – Sep 17

Subsystems (16)
Arm (32-bit) MIPS BPF arm64 +12 more subsystems

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 cover audit: log all six syscall arguments in the SYSCALL record

The SYSCALL record currently logs only four of the six syscall arguments (a0-a3), silently discarding the remaining two. This leads to the need for auxiliary records when audit-relevant data lands in the 5th or 6th argument of a syscall.

This series extends the SYSCALL record to log all six arguments, by adding arguments a4 and a5 inline within the existing record.

The audit testsuite runs successfully:

 # make test
 make -C tests test
 Running as   user    root
        with context unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
        on   system  Fedora

amcast_joinpart/test ................. ok
backlog_wait_time_actual_reset/test .. ok
bpf/test ............................. ok
coredump/test ........................ ok
exec_execve/test ..................... ok
exec_name/test ....................... ok
fanotify/test ........................ ok
field_compare/test ................... ok
file_create/test ..................... ok
file_delete/test ..................... ok
file_permission/test ................. ok
file_rename/test ..................... ok
filter_device/test ................... ok
filter_exclude/test .................. ok
filter_exit/test ..................... ok
filter_inode/test .................... ok
filter_saddr_fam/test ................ ok
filter_sessionid/test ................ ok
io_uring/test ........................ ok
login_tty/test ....................... ok
lost_reset/test ...................... ok
netfilter_pkt/test ................... ok
signal/test .......................... ok
syscalls_file/test ................... ok
syscall_module/test .................. ok
syscall_socketcall/test .............. ok
time_change/test ..................... ok
user_msg/test ........................ ok
All tests successful.
Result: PASS

Changes in v2:
- Rework the core change per Will Deacon's suggestion: instead of plumbing all six arguments through every architecture's syscall entry path, __audit_syscall_entry() now takes a struct pt_regs * and retrieves the arguments itself via syscall_get_arguments().
- Per-arch patches now simply pass regs instead of the individual argument registers.
- Fetch the arguments directly into context->argv, dropping the temporary array.

Ricardo Robaina (13):
audit: log all six syscall arguments in the SYSCALL record
alpha: pass pt_regs to audit_syscall_entry()
arm: pass pt_regs to audit_syscall_entry()
arm64: pass pt_regs to audit_syscall_entry()
csky: pass pt_regs to audit_syscall_entry()
microblaze: pass pt_regs to audit_syscall_entry()
mips: pass pt_regs to audit_syscall_entry()
openrisc: pass pt_regs to audit_syscall_entry()
parisc: pass pt_regs to audit_syscall_entry()
sh: pass pt_regs to audit_syscall_entry()
sparc64: pass pt_regs to audit_syscall_entry()
um: pass pt_regs to audit_syscall_entry()
xtensa: pass pt_regs to audit_syscall_entry()

 arch/alpha/kernel/ptrace.c      |  3 +--
 arch/arm/kernel/ptrace.c        |  3 +--
 arch/arm64/kernel/ptrace.c      |  3 +--
 arch/csky/kernel/ptrace.c       |  2 +-
 arch/microblaze/kernel/ptrace.c |  2 +-
 arch/mips/kernel/ptrace.c       |  4 +---
 arch/openrisc/kernel/ptrace.c   |  3 +--
 arch/parisc/kernel/ptrace.c     |  9 ++-------
 arch/sh/kernel/ptrace_32.c      |  3 +--
 arch/sparc/kernel/ptrace_64.c   |  4 +---
 arch/um/kernel/ptrace.c         |  6 +-----
 arch/xtensa/kernel/ptrace.c     |  4 +---
 include/linux/audit.h           | 13 ++++---------
 include/uapi/linux/audit.h      |  2 ++
 kernel/audit.h                  |  2 +-
 kernel/auditfilter.c            |  2 ++
 kernel/auditsc.c                | 19 ++++++++-----------
 kernel/entry/syscall-common.c   |  4 +---
 18 files changed, 31 insertions(+), 57 deletions(-)
Signature · 1 line
2.55.0
#1 lore raw

PATCH v2 1/13 audit: log all six syscall arguments in the SYSCALL record

The SYSCALL record currently logs only four of the six syscall arguments (a0-a3). The remaining two are captured but silently discarded before reaching the audit context. This leads to the need for auxiliary records when audit-relevant data lands in the 5th or 6th argument of a syscall.

Extend the SYSCALL record to log all six arguments, by adding arguments a4 and a5 (5th and 6th syscall arguments respectively) inline within the existing record. Also add the two new args to the audit rules switch case, so audit rules can filter on them.

Rather than plumbing two more register arguments through every architecture's syscall entry path, change __audit_syscall_entry() to take a pointer to pt_regs and use syscall_get_arguments() to retrieve all six arguments.

audit-testsuite# make test
audit-testsuite# ausearch -i -m SYSCALL
...
type=SYSCALL ... syscall=sendto success=yes exit=1088 a0=0x4
a1=0x7ffe43518b60 a2=0x440 a3=0x0 a4=7ffe43518b4c a5=c
type=SYSCALL ... syscall=openat2 success=yes exit=4 a0=0x3
a1=0x7fffc74692c8 a2=0x7fffc7467390 a3=0x18 a4=0 a5=7fffc74674f8
type=SYSCALL ... syscall=openat success=yes exit=3 a0=AT_FDCWD
a1=0x557ced4141a2 a2=O_RDWR|O_NONBLOCK a3=0x0 a4=0 a5=0
...

---
 include/linux/audit.h         | 13 ++++---------
 include/uapi/linux/audit.h    |  2 ++
 kernel/audit.h                |  2 +-
 kernel/auditfilter.c          |  2 ++
 kernel/auditsc.c              | 19 ++++++++-----------
 kernel/entry/syscall-common.c |  4 +---
 6 files changed, 18 insertions(+), 24 deletions(-)
include/linux/audit.h +4 -9
index 45abb3722d30..9ce5962bc537 100644
@@ -323,8 +323,7 @@ extern int  audit_alloc(struct task_struct *task);
 extern void __audit_free(struct task_struct *task);
 extern void __audit_uring_entry(u8 op);
 extern void __audit_uring_exit(int success, long code);
-extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1,
-				  unsigned long a2, unsigned long a3);
+extern void __audit_syscall_entry(int major, struct pt_regs *regs);
 extern void __audit_syscall_exit(int ret_success, long ret_value);
 extern void __audit_getname(struct filename *name);
 extern void __audit_inode(struct filename *name, const struct dentry *dentry,
@@ -373,12 +372,10 @@ static inline void audit_uring_exit(int success, long code)
 	if (unlikely(audit_context()))
 		__audit_uring_exit(success, code);
 }
-static inline void audit_syscall_entry(int major, unsigned long a0,
-				       unsigned long a1, unsigned long a2,
-				       unsigned long a3)
+static inline void audit_syscall_entry(int major, struct pt_regs *regs)
 {
 	if (unlikely(audit_context()))
-		__audit_syscall_entry(major, a0, a1, a2, a3);
+		__audit_syscall_entry(major, regs);
 }
 static inline void audit_syscall_exit(void *pt_regs)
 {
@@ -611,9 +608,7 @@ static inline void audit_uring_entry(u8 op)
 { }
 static inline void audit_uring_exit(int success, long code)
 { }
-static inline void audit_syscall_entry(int major, unsigned long a0,
-				       unsigned long a1, unsigned long a2,
-				       unsigned long a3)
+static inline void audit_syscall_entry(int major, struct pt_regs *regs)
 { }
 static inline void audit_syscall_exit(void *pt_regs)
 { }
include/uapi/linux/audit.h +2 -0
index e8f5ce677df7..6726059d6df1 100644
@@ -303,6 +303,8 @@
 #define AUDIT_ARG1      (AUDIT_ARG0+1)
 #define AUDIT_ARG2      (AUDIT_ARG0+2)
 #define AUDIT_ARG3      (AUDIT_ARG0+3)
+#define AUDIT_ARG4      (AUDIT_ARG0+4)
+#define AUDIT_ARG5      (AUDIT_ARG0+5)
 
 #define AUDIT_FILTERKEY	210
 
kernel/audit.h +1 -1
index 92d5e723d570..83011b14af18 100644
@@ -117,7 +117,7 @@ struct audit_context {
 	struct audit_stamp  stamp;	/* event identifier */
 	int		    major;      /* syscall number */
 	int		    uring_op;   /* uring operation */
-	unsigned long	    argv[4];    /* syscall arguments */
+	unsigned long	    argv[6];    /* syscall arguments */
 	long		    return_code;/* syscall return code */
 	u64		    prio;
 	int		    return_valid; /* return code is valid */
kernel/auditfilter.c +2 -0
index f52645625214..cded3696e3c5 100644
@@ -358,6 +358,8 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f)
 	case AUDIT_ARG1:
 	case AUDIT_ARG2:
 	case AUDIT_ARG3:
+	case AUDIT_ARG4:
+	case AUDIT_ARG5:
 	case AUDIT_PERS: /* <uapi/linux/personality.h> */
 	case AUDIT_DEVMINOR:
 		/* all ops are valid */
kernel/auditsc.c +8 -11
index 2b9ce0b52511..cf45c782b03d 100644
@@ -728,6 +728,8 @@ static int audit_filter_rules(struct task_struct *tsk,
 		case AUDIT_ARG1:
 		case AUDIT_ARG2:
 		case AUDIT_ARG3:
+		case AUDIT_ARG4:
+		case AUDIT_ARG5:
 			if (ctx)
 				result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val);
 			break;
@@ -1674,11 +1676,13 @@ static void audit_log_exit(void)
 						    AUDITSC_SUCCESS),
 					 context->return_code);
 		audit_log_format(ab,
-				 " a0=%lx a1=%lx a2=%lx a3=%lx items=%d",
+				 " a0=%lx a1=%lx a2=%lx a3=%lx a4=%lx a5=%lx items=%d",
 				 context->argv[0],
 				 context->argv[1],
 				 context->argv[2],
 				 context->argv[3],
+				 context->argv[4],
+				 context->argv[5],
 				 context->name_count);
 		audit_log_task_info(ab);
 		audit_log_key(ab, context->filterkey);
@@ -1970,10 +1974,7 @@ void __audit_uring_exit(int success, long code)
 /**
  * __audit_syscall_entry - fill in an audit record at syscall entry
  * @major: major syscall type (function)
- * @a1: additional syscall register 1
- * @a2: additional syscall register 2
- * @a3: additional syscall register 3
- * @a4: additional syscall register 4
+ * @regs: the task's register state at syscall entry
  *
  * Fill in audit context at syscall entry.  This only happens if the
  * audit context was created when the task was created and the state or
@@ -1983,8 +1984,7 @@ void __audit_uring_exit(int success, long code)
  * will only be written if another part of the kernel requests that it
  * be written).
  */
-void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2,
-			   unsigned long a3, unsigned long a4)
+void __audit_syscall_entry(int major, struct pt_regs *regs)
 {
 	struct audit_context *context = audit_context();
 	enum audit_state     state;
@@ -2012,10 +2012,7 @@ void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2,
 
 	context->arch	    = syscall_get_arch(current);
 	context->major      = major;
-	context->argv[0]    = a1;
-	context->argv[1]    = a2;
-	context->argv[2]    = a3;
-	context->argv[3]    = a4;
+	syscall_get_arguments(current, regs, context->argv);
 	context->context = AUDIT_CTX_SYSCALL;
 	context->current_state  = state;
 	ktime_get_coarse_real_ts64(&context->stamp.ctime);
kernel/entry/syscall-common.c +1 -3
index b8eac9efb6fd..471db2a8e09d 100644
@@ -22,9 +22,7 @@ void trace_syscall_exit(struct pt_regs *regs, long ret)
 void syscall_enter_audit(struct pt_regs *regs)
 {
 	long syscall = syscall_get_nr(current, regs);
-	unsigned long args[6];
 
-	syscall_get_arguments(current, regs, args);
-	__audit_syscall_entry(syscall, args[0], args[1], args[2], args[3]);
+	__audit_syscall_entry(syscall, regs);
 }
 #endif
Signature · 1 line
2.55.0
#2 lore raw
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 27 lines

The SYSCALL record currently logs only four of the six syscall arguments (a0-a3). The remaining two are captured but silently discarded before reaching the audit context. This leads to the need for auxiliary records when audit-relevant data lands in the 5th or 6th argument of a syscall.

Extend the SYSCALL record to log all six arguments, by adding arguments a4 and a5 (5th and 6th syscall arguments respectively) inline within the existing record. Also add the two new args to the audit rules switch case, so audit rules can filter on them.

Rather than plumbing two more register arguments through every architecture's syscall entry path, change __audit_syscall_entry() to take a pointer to pt_regs and use syscall_get_arguments() to retrieve all six arguments.

audit-testsuite# make test
audit-testsuite# ausearch -i -m SYSCALL
...
type=SYSCALL ... syscall=sendto success=yes exit=1088 a0=0x4
a1=0x7ffe43518b60 a2=0x440 a3=0x0 a4=7ffe43518b4c a5=c
type=SYSCALL ... syscall=openat2 success=yes exit=4 a0=0x3
a1=0x7fffc74692c8 a2=0x7fffc7467390 a3=0x18 a4=0 a5=7fffc74674f8
type=SYSCALL ... syscall=openat success=yes exit=3 a0=AT_FDCWD
a1=0x557ced4141a2 a2=O_RDWR|O_NONBLOCK a3=0x0 a4=0 a5=0
...

Quoted 3 lines

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
maintainer
#3 lore raw
In reply to Ricardo Robaina
Quoted 56 lines · Ricardo Robaina

The SYSCALL record currently logs only four of the six syscall arguments (a0-a3). The remaining two are captured but silently discarded before reaching the audit context. This leads to the need for auxiliary records when audit-relevant data lands in the 5th or 6th argument of a syscall.

Extend the SYSCALL record to log all six arguments, by adding arguments a4 and a5 (5th and 6th syscall arguments respectively) inline within the existing record. Also add the two new args to the audit rules switch case, so audit rules can filter on them.

Rather than plumbing two more register arguments through every architecture's syscall entry path, change __audit_syscall_entry() to take a pointer to pt_regs and use syscall_get_arguments() to retrieve all six arguments.

audit-testsuite# make test
audit-testsuite# ausearch -i -m SYSCALL
...
type=SYSCALL ... syscall=sendto success=yes exit=1088 a0=0x4
a1=0x7ffe43518b60 a2=0x440 a3=0x0 a4=7ffe43518b4c a5=c
type=SYSCALL ... syscall=openat2 success=yes exit=4 a0=0x3
a1=0x7fffc74692c8 a2=0x7fffc7467390 a3=0x18 a4=0 a5=7fffc74674f8
type=SYSCALL ... syscall=openat success=yes exit=3 a0=AT_FDCWD
a1=0x557ced4141a2 a2=O_RDWR|O_NONBLOCK a3=0x0 a4=0 a5=0
...

---
 include/linux/audit.h         | 13 ++++---------
 include/uapi/linux/audit.h    |  2 ++
 kernel/audit.h                |  2 +-
 kernel/auditfilter.c          |  2 ++
 kernel/auditsc.c              | 19 ++++++++-----------
 kernel/entry/syscall-common.c |  4 +---
 6 files changed, 18 insertions(+), 24 deletions(-)
include/linux/audit.h +1 -2
index 45abb3722d30..9ce5962bc537 100644
@@ -323,8 +323,7 @@ extern int  audit_alloc(struct task_struct *task);
 extern void __audit_free(struct task_struct *task);
 extern void __audit_uring_entry(u8 op);
 extern void __audit_uring_exit(int success, long code);
-extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1,
-				  unsigned long a2, unsigned long a3);
+extern void __audit_syscall_entry(int major, struct pt_regs *regs);
 extern void __audit_syscall_exit(int ret_success, long ret_value);
 extern void __audit_getname(struct filename *name);
 extern void __audit_inode(struct filename *name, const struct dentry *dentry,
@@ -373,12 +372,10 @@ static inline void audit_uring_exit(int success, long code)
 	if (unlikely(audit_context()))
 		__audit_uring_exit(success, code);

include/linux/audit.h static inline void audit_uring_exit(int success, long code) · line 379

Quoted 10 lines
include/linux/audit.h +2 -4
 }
-static inline void audit_syscall_entry(int major, unsigned long a0,
-				       unsigned long a1, unsigned long a2,
-				       unsigned long a3)
+static inline void audit_syscall_entry(int major, struct pt_regs *regs)
 {
 	if (unlikely(audit_context()))
-		__audit_syscall_entry(major, a0, a1, a2, a3);
+		__audit_syscall_entry(major, regs);
 }

Won't this (temporarily) break the build? Maybe you need to introduce a new helper e.g. audit_syscall_entry_regs(), then convert everybody over to that, then rename it to audit_syscall_entry() at the end to keep the series bisectable.

Will

#4 lore raw
Quoted 74 lines · Will Deacon
Quoted 56 lines · Ricardo Robaina

The SYSCALL record currently logs only four of the six syscall arguments (a0-a3). The remaining two are captured but silently discarded before reaching the audit context. This leads to the need for auxiliary records when audit-relevant data lands in the 5th or 6th argument of a syscall.

Extend the SYSCALL record to log all six arguments, by adding arguments a4 and a5 (5th and 6th syscall arguments respectively) inline within the existing record. Also add the two new args to the audit rules switch case, so audit rules can filter on them.

Rather than plumbing two more register arguments through every architecture's syscall entry path, change __audit_syscall_entry() to take a pointer to pt_regs and use syscall_get_arguments() to retrieve all six arguments.

audit-testsuite# make test
audit-testsuite# ausearch -i -m SYSCALL
...
type=SYSCALL ... syscall=sendto success=yes exit=1088 a0=0x4
a1=0x7ffe43518b60 a2=0x440 a3=0x0 a4=7ffe43518b4c a5=c
type=SYSCALL ... syscall=openat2 success=yes exit=4 a0=0x3
a1=0x7fffc74692c8 a2=0x7fffc7467390 a3=0x18 a4=0 a5=7fffc74674f8
type=SYSCALL ... syscall=openat success=yes exit=3 a0=AT_FDCWD
a1=0x557ced4141a2 a2=O_RDWR|O_NONBLOCK a3=0x0 a4=0 a5=0
...

---
 include/linux/audit.h         | 13 ++++---------
 include/uapi/linux/audit.h    |  2 ++
 kernel/audit.h                |  2 +-
 kernel/auditfilter.c          |  2 ++
 kernel/auditsc.c              | 19 ++++++++-----------
 kernel/entry/syscall-common.c |  4 +---
 6 files changed, 18 insertions(+), 24 deletions(-)
include/linux/audit.h +1 -2
index 45abb3722d30..9ce5962bc537 100644
@@ -323,8 +323,7 @@ extern int  audit_alloc(struct task_struct *task);
 extern void __audit_free(struct task_struct *task);
 extern void __audit_uring_entry(u8 op);
 extern void __audit_uring_exit(int success, long code);
-extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1,
-                               unsigned long a2, unsigned long a3);
+extern void __audit_syscall_entry(int major, struct pt_regs *regs);
 extern void __audit_syscall_exit(int ret_success, long ret_value);
 extern void __audit_getname(struct filename *name);
 extern void __audit_inode(struct filename *name, const struct dentry *dentry,
@@ -373,12 +372,10 @@ static inline void audit_uring_exit(int success, long code)
      if (unlikely(audit_context()))
              __audit_uring_exit(success, code);

include/linux/audit.h static inline void audit_uring_exit(int success, long code) · line 379

Quoted 10 lines
include/linux/audit.h +2 -4
 }
-static inline void audit_syscall_entry(int major, unsigned long a0,
-                                    unsigned long a1, unsigned long a2,
-                                    unsigned long a3)
+static inline void audit_syscall_entry(int major, struct pt_regs *regs)
 {
      if (unlikely(audit_context()))
-             __audit_syscall_entry(major, a0, a1, a2, a3);
+             __audit_syscall_entry(major, regs);
 }

Won't this (temporarily) break the build? Maybe you need to introduce a new helper e.g. audit_syscall_entry_regs(), then convert everybody over to that, then rename it to audit_syscall_entry() at the end to keep the series bisectable.

Quoted 2 lines

Will

Yes, you're right Will. Thanks for your suggestion I'll do that in the next version.

Thanks!
-Ricardo

#5 lore raw

PATCH v2 2/13 alpha: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/alpha/kernel/ptrace.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
arch/alpha/kernel/ptrace.c +1 -2
index fc8f6cedbb28..8a39c0473297 100644
@@ -496,8 +496,7 @@ asmlinkage unsigned long syscall_trace_enter(void)
 	}
 
 #ifdef CONFIG_AUDITSYSCALL
-	audit_syscall_entry(syscall_get_nr(current, regs),
-		regs->r16, regs->r17, regs->r18, regs->r19);
+	audit_syscall_entry(syscall_get_nr(current, regs), regs);
 #endif
 	return syscall_get_nr(current, regs);
 }
Signature · 1 line
2.55.0
#6 lore raw

Re: [PATCH v2 02/13] alpha: pass pt_regs to audit_syscall_entry()

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 5 lines

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
maintainer
#7 lore raw

Re: [PATCH v2 02/13] alpha: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina

Hi Ricardo,

Quoted 25 lines · Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/alpha/kernel/ptrace.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
arch/alpha/kernel/ptrace.c +1 -3
index fc8f6cedbb28..8a39c0473297 100644
@@ -496,8 +496,7 @@ asmlinkage unsigned long syscall_trace_enter(void)
        }
 
 #ifdef CONFIG_AUDITSYSCALL
-       audit_syscall_entry(syscall_get_nr(current, regs),
-               regs->r16, regs->r17, regs->r18, regs->r19);
+       audit_syscall_entry(syscall_get_nr(current, regs), regs);
 #endif
        return syscall_get_nr(current, regs);
 }
--
Quoted 2 lines

2.55.0

This doesn't apply on top of my "alpha: enable GENERIC_ENTRY" series

https://lore.kernel.org/linux-alpha/[email protected]/T/#t

That series moves alpha's syscall entry onto the generic entry framework and removes syscall_trace_enter() and its direct audit_syscall_entry() call entirely - verified with git am against GENERIC_ENTRY applied, it fails at exactly this hunk.
You don't need a replacement alpha patch, though. Once GENERIC_ENTRY lands, alpha's syscall entry goes through syscall_enter_from_user_mode_randomize_stack(), which already calls syscall_enter_audit() as part of the shared entry helpers (include/linux/entry-common.h). I applied patch 1/13 alone on top of GENERIC_ENTRY and confirmed alpha's entry path already routes through the updated __audit_syscall_entry() - no alpha-specific code needed. So for alpha, you can drop this patch in the next revision rather than rebase it.

Regards
Magnus

#8 lore raw

Re: [PATCH v2 02/13] alpha: pass pt_regs to audit_syscall_entry()

Quoted 49 lines · Magnus Lindholm

Hi Ricardo,

Quoted 25 lines · Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/alpha/kernel/ptrace.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
arch/alpha/kernel/ptrace.c +1 -3
index fc8f6cedbb28..8a39c0473297 100644
@@ -496,8 +496,7 @@ asmlinkage unsigned long syscall_trace_enter(void)
        }
 
 #ifdef CONFIG_AUDITSYSCALL
-       audit_syscall_entry(syscall_get_nr(current, regs),
-               regs->r16, regs->r17, regs->r18, regs->r19);
+       audit_syscall_entry(syscall_get_nr(current, regs), regs);
 #endif
        return syscall_get_nr(current, regs);
 }
--
Quoted 2 lines

2.55.0

This doesn't apply on top of my "alpha: enable GENERIC_ENTRY" series

https://lore.kernel.org/linux-alpha/[email protected]/T/#t

That series moves alpha's syscall entry onto the generic entry framework and removes syscall_trace_enter() and its direct audit_syscall_entry() call entirely - verified with git am against GENERIC_ENTRY applied, it fails at exactly this hunk.
You don't need a replacement alpha patch, though. Once GENERIC_ENTRY lands, alpha's syscall entry goes through syscall_enter_from_user_mode_randomize_stack(), which already calls syscall_enter_audit() as part of the shared entry helpers (include/linux/entry-common.h). I applied patch 1/13 alone on top of GENERIC_ENTRY and confirmed alpha's entry path already routes through the updated __audit_syscall_entry() - no alpha-specific code needed. So for alpha, you can drop this patch in the next revision rather than rebase it.

Quoted 3 lines

Regards
Magnus

Hi Magnus,

Thanks for reviewing this patch! Ok, I'll drop this patch from the series.

Thanks!
-Ricardo

#9 lore raw

PATCH v2 3/13 arm: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/arm/kernel/ptrace.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
arch/arm/kernel/ptrace.c +1 -2
index ed7a2a87a670..e103ec2661cd 100644
@@ -868,8 +868,7 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
 		trace_sys_enter(regs, scno);
 
-	audit_syscall_entry(scno, regs->ARM_r0, regs->ARM_r1, regs->ARM_r2,
-			    regs->ARM_r3);
+	audit_syscall_entry(scno, regs);
 
 	return scno;
 }
Signature · 1 line
2.55.0
#10 lore raw

Re: [PATCH v2 03/13] arm: pass pt_regs to audit_syscall_entry()

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] The transition to using `syscall_get_arguments()` for audit logging causes the first syscall…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The transition to using syscall_get_arguments() for audit logging causes the first syscall argument to be read from regs->ARM_ORIG_r0 instead of regs->ARM_r0. This allows tracers to bypass audit logging by spoofing arguments, as the audit subsystem logs the unmodified argument while the kernel executes the modified one. --

commit 2a794057248ff5b9ad0444e2a1b1392a156db536
Author: Ricardo Robaina <[email protected]>

arm: pass pt_regs to audit_syscall_entry()

This commit updates syscall_trace_enter() on ARM to pass the pt_regs structure directly to audit_syscall_entry() instead of individual arguments. The audit logging subsystem now extracts the arguments itself via syscall_get_arguments().

arch/arm/kernel/ptrace.c asmlinkage int syscall_trace_enter(struct pt_regs *regs) · line 871

Quoted 11 lines
arch/arm/kernel/ptrace.c +1 -2
index ed7a2a87a6707..e103ec2661cd4 100644
@@ -868,8 +868,7 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
 		trace_sys_enter(regs, scno);
 
-	audit_syscall_entry(scno, regs->ARM_r0, regs->ARM_r1, regs->ARM_r2,
-			    regs->ARM_r3);
+	audit_syscall_entry(scno, regs);

[Severity: High]
Does this change allow tracers to bypass audit logging by spoofing syscall arguments?

By delegating argument extraction to the helper, the first syscall argument is now extracted via syscall_get_arguments(), which reads from regs->ARM_ORIG_r0. The previous code directly passed regs->ARM_r0.

If a user-space process intercepts the syscall at PTRACE_SYSCALL_ENTER and modifies the first argument via PTRACE_POKEUSR to write to regs->ARM_r0, the audit subsystem will log the unmodified argument from regs->ARM_ORIG_r0. However, the syscall dispatcher will subsequently reload r0 from regs->ARM_r0, causing the kernel to execute the modified argument while the benign one is logged.

Can we ensure the audit subsystem logs the active argument that will actually be executed?

Quoted 3 lines
arch/arm/kernel/ptrace.c +0 -0
 	return scno;
 }
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
#11 lore raw

PATCH v2 4/13 arm64: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/arm64/kernel/ptrace.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
arch/arm64/kernel/ptrace.c +1 -2
index f743cbec1c3a..65532b2a8cd4 100644
@@ -2476,8 +2476,7 @@ int syscall_trace_enter(struct pt_regs *regs)
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
 		trace_sys_enter(regs, regs->syscallno);
 
-	audit_syscall_entry(regs->syscallno, regs->orig_x0, regs->regs[1],
-			    regs->regs[2], regs->regs[3]);
+	audit_syscall_entry(regs->syscallno, regs);
 
 	return regs->syscallno;
 }
Signature · 1 line
2.55.0
#12 lore raw

Re: [PATCH v2 04/13] arm64: pass pt_regs to audit_syscall_entry()

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Syscall argument filtering truncates 64-bit arguments to 32 bits, allowing audit filter bypass. --

commit 32fa904a92560e78e9593b4944ee636ddfb88ea9
Author: Ricardo Robaina <[email protected]>

arm64: pass pt_regs to audit_syscall_entry()

This commit updates arm64 to pass a pointer to pt_regs directly to audit_syscall_entry(), allowing the audit subsystem to extract the syscall arguments itself.

Quoted 3 lines
arch/arm64/kernel/ptrace.c +0 -0

[ ... ]

arch/arm64/kernel/ptrace.c int syscall_trace_enter(struct pt_regs *regs) · line 2479

Quoted 7 lines
arch/arm64/kernel/ptrace.c +1 -2
@@ -2476,8 +2476,7 @@ int syscall_trace_enter(struct pt_regs *regs)
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
 		trace_sys_enter(regs, regs->syscallno);
 
-	audit_syscall_entry(regs->syscallno, regs->orig_x0, regs->regs[1],
-			    regs->regs[2], regs->regs[3]);
+	audit_syscall_entry(regs->syscallno, regs);

[Severity: High]
This is a pre-existing issue, but does the underlying audit framework truncate these 64-bit syscall arguments to 32 bits during filtering?

When the extracted 64-bit arguments are later evaluated in kernel/auditsc.c:audit_filter_rules(), they are passed to audit_comparator():

    result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val);

However, looking at kernel/auditfilter.c, the signature of audit_comparator() implicitly truncates the 64-bit argument to a u32:

int audit_comparator(u32 left, u32 op, u32 right)

Can this allow an unprivileged user-space process to bypass audit filtering rules (such as != or <) targeting 64-bit syscall arguments? By crafting 64-bit values whose lower 32 bits evade the 32-bit rule comparison, the full 64-bit value processed by the kernel could still execute normally while evading audit logging.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
maintainer Acked-by on v2 4/13
#13 lore raw

Re: [PATCH v2 04/13] arm64: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina
Quoted 14 lines · Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/arm64/kernel/ptrace.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
arch/arm64/kernel/ptrace.c +0 -0
index f743cbec1c3a..65532b2a8cd4 100644

arch/arm64/kernel/ptrace.c int syscall_trace_enter(struct pt_regs *regs) · line 2479

Quoted 6 lines
arch/arm64/kernel/ptrace.c +1 -2
@@ -2476,8 +2476,7 @@ int syscall_trace_enter(struct pt_regs *regs)
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
 		trace_sys_enter(regs, regs->syscallno);
 
-	audit_syscall_entry(regs->syscallno, regs->orig_x0, regs->regs[1],
-			    regs->regs[2], regs->regs[3]);
+	audit_syscall_entry(regs->syscallno, regs);

Thanks for doing this.

Will

#14 lore raw

Re: [PATCH v2 04/13] arm64: pass pt_regs to audit_syscall_entry()

Quoted 25 lines · Will Deacon
Quoted 14 lines · Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/arm64/kernel/ptrace.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
arch/arm64/kernel/ptrace.c +0 -0
index f743cbec1c3a..65532b2a8cd4 100644

arch/arm64/kernel/ptrace.c int syscall_trace_enter(struct pt_regs *regs) · line 2479

Quoted 6 lines
arch/arm64/kernel/ptrace.c +1 -2
@@ -2476,8 +2476,7 @@ int syscall_trace_enter(struct pt_regs *regs)
      if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
              trace_sys_enter(regs, regs->syscallno);
 
-     audit_syscall_entry(regs->syscallno, regs->orig_x0, regs->regs[1],
-                         regs->regs[2], regs->regs[3]);
+     audit_syscall_entry(regs->syscallno, regs);
Quoted 4 lines

Thanks for doing this.

Will

Thanks, Will!

-Ricardo

#15 lore raw

PATCH v2 5/13 csky: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/csky/kernel/ptrace.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/csky/kernel/ptrace.c +1 -1
index ee2867a1576e..5c3051bcd78c 100644
@@ -329,7 +329,7 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
 		trace_sys_enter(regs, syscall_get_nr(current, regs));
 
-	audit_syscall_entry(regs_syscallid(regs), regs->a0, regs->a1, regs->a2, regs->a3);
+	audit_syscall_entry(regs_syscallid(regs), regs);
 	return 0;
 }
 
Signature · 1 line
2.55.0
#16 lore raw

Re: [PATCH v2 05/13] csky: pass pt_regs to audit_syscall_entry()

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 5 lines

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
#17 lore raw

PATCH v2 6/13 microblaze: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/microblaze/kernel/ptrace.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/microblaze/kernel/ptrace.c +1 -1
index 236264e932d6..5fe9e3a150f4 100644
@@ -147,7 +147,7 @@ asmlinkage unsigned long do_syscall_trace_enter(struct pt_regs *regs)
 		 */
 		ret = -1L;
 
-	audit_syscall_entry(regs->r12, regs->r5, regs->r6, regs->r7, regs->r8);
+	audit_syscall_entry(regs->r12, regs);
 
 	return ret ?: regs->r12;
 }
Signature · 1 line
2.55.0
#18 lore raw

Re: [PATCH v2 06/13] microblaze: pass pt_regs to audit_syscall_entry()

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 5 lines

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6
#19 lore raw

PATCH v2 7/13 mips: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/mips/kernel/ptrace.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
arch/mips/kernel/ptrace.c +1 -3
index 17dc7249b8ca..7a09b4671a54 100644
@@ -1338,9 +1338,7 @@ asmlinkage long syscall_trace_enter(struct pt_regs *regs)
 	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 		trace_sys_enter(regs, regs->regs[2]);
 
-	audit_syscall_entry(current_thread_info()->syscall,
-			    regs->regs[4], regs->regs[5],
-			    regs->regs[6], regs->regs[7]);
+	audit_syscall_entry(current_thread_info()->syscall, regs);
 
 	/*
 	 * Negative syscall numbers are mistaken for rejected syscalls, but
Signature · 1 line
2.55.0
#20 lore raw

Re: [PATCH v2 07/13] mips: pass pt_regs to audit_syscall_entry()

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 5 lines

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
#21 lore raw

PATCH v2 8/13 openrisc: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/openrisc/kernel/ptrace.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
arch/openrisc/kernel/ptrace.c +1 -2
index 287a9718f0c6..eca613b84ff8 100644
@@ -301,8 +301,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 		 */
 		ret = -1L;
 
-	audit_syscall_entry(regs->gpr[11], regs->gpr[3], regs->gpr[4],
-			    regs->gpr[5], regs->gpr[6]);
+	audit_syscall_entry(regs->gpr[11], regs);
 
 	return ret ? : regs->gpr[11];
 }
Signature · 1 line
2.55.0
#22 lore raw

Re: [PATCH v2 08/13] openrisc: pass pt_regs to audit_syscall_entry()

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Syscall abortion via ptrace is ignored by the assembly fastpath, causing aborted syscalls to execute anyway.
- [High] Tracer-directed syscall number mutations are ignored by both the audit subsystem and the execution path. --

commit b9f4a1989ccdc62e1dafc2931825c9e52d439b17
Author: Ricardo Robaina <[email protected]>

openrisc: pass pt_regs to audit_syscall_entry()

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

arch/openrisc/kernel/ptrace.c asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) · line 304

Quoted 11 lines
arch/openrisc/kernel/ptrace.c +1 -2
index 287a9718f0c60..eca613b84ff87 100644
@@ -301,8 +301,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 		 */
 		ret = -1L;
 
-	audit_syscall_entry(regs->gpr[11], regs->gpr[3], regs->gpr[4],
-			    regs->gpr[5], regs->gpr[6]);
+	audit_syscall_entry(regs->gpr[11], regs);

[Severity: High]
This is a pre-existing issue, but does hardcoding regs->gpr[11] here ignore tracer-directed syscall number mutations?

The generic framework's syscall_set_nr() updates regs->orig_gpr11 with the new syscall number. However, this code directly passes regs->gpr[11] to audit_syscall_entry() instead of using syscall_get_nr(current, regs).

Additionally, the _syscall_call assembly fastpath appears to use the original syscall number reloaded from PT_GPR11(r1). Since neither layer reads orig_gpr11, standard PTRACE_SET_SYSCALL_INFO mutations seem to silently fail to influence the kernel on OpenRISC.

arch/openrisc/kernel/ptrace.c asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) · line 306

Quoted 2 lines
arch/openrisc/kernel/ptrace.c +0 -0
 	return ret ? : regs->gpr[11];

[Severity: High]
This is also a pre-existing issue, but does this function successfully abort syscalls when a tracer denies them?

When a tracer denies a syscall, ptrace_report_syscall_permit_entry() causes ret to be set to -1L, which is then returned. However, looking at the assembly fastpath in arch/openrisc/kernel/entry.S:_syscall_trace_enter:

	l.jal	do_syscall_trace_enter
	 l.addi	r3,r1,0

	/* Restore arguments ... */
	l.lwz	r11,PT_GPR11(r1)

Doesn't this instruction blindly reload the original unmodified syscall number from the stack into r11?

Since do_syscall_trace_enter() never writes -1L back to regs->gpr[11] in memory, it seems the abort signal is permanently lost when the assembly restores registers, allowing the aborted syscall to execute anyway.

Quoted 1 line
arch/openrisc/kernel/ptrace.c +0 -0
 }
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=8
#23 lore raw

PATCH v2 9/13 parisc: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/parisc/kernel/ptrace.c | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)
arch/parisc/kernel/ptrace.c +2 -7
index 1d9e210702e1..948e0cf29ba4 100644
@@ -359,15 +359,10 @@ long do_syscall_trace_enter(struct pt_regs *regs)
 
 #ifdef CONFIG_64BIT
 	if (!is_compat_task())
-		audit_syscall_entry(regs->gr[20], regs->gr[26], regs->gr[25],
-				    regs->gr[24], regs->gr[23]);
+		audit_syscall_entry(regs->gr[20], regs);
 	else
 #endif
-		audit_syscall_entry(regs->gr[20] & 0xffffffff,
-			regs->gr[26] & 0xffffffff,
-			regs->gr[25] & 0xffffffff,
-			regs->gr[24] & 0xffffffff,
-			regs->gr[23] & 0xffffffff);
+		audit_syscall_entry(regs->gr[20] & 0xffffffff, regs);
 
 	/*
 	 * Sign extend the syscall number to 64bit since it may have been
Signature · 1 line
2.55.0
#24 lore raw

Re: [PATCH v2 09/13] parisc: pass pt_regs to audit_syscall_entry()

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [High] Removal of explicit 32-bit masking for compat syscall arguments allows a ptracer to bypass audit filters and leaks upper 32 bits into audit logs.

Pre-existing issues:
- [High] Seccomp evaluates unmasked 64-bit register values for 32-bit compat tasks, allowing bypass of reject filters. --

commit 78cafbbd76943b4c2f06e1817363e637bd633afe
Author: Ricardo Robaina <[email protected]>

parisc: pass pt_regs to audit_syscall_entry()

This commit changes audit_syscall_entry() to take a pointer to pt_regs and extract the syscall arguments itself via syscall_get_arguments(). The individual argument registers are dropped from the call.

Quoted 4 lines
arch/parisc/kernel/ptrace.c +0 -0
index 1d9e210702e1f..948e0cf29ba47 100644

[ ... ]

Quoted 5 lines
arch/parisc/kernel/ptrace.c +0 -1
@@ -359,15 +359,10 @@ long do_syscall_trace_enter(struct pt_regs *regs)
 
 #ifdef CONFIG_64BIT
 	if (!is_compat_task())
-		audit_syscall_entry(regs->gr[20], regs->gr[26], regs->gr[25],

arch/parisc/kernel/ptrace.c long do_syscall_trace_enter(struct pt_regs *regs) · line 365

Quoted 10 lines
arch/parisc/kernel/ptrace.c +2 -6
-				    regs->gr[24], regs->gr[23]);
+		audit_syscall_entry(regs->gr[20], regs);
 	else
 #endif
-		audit_syscall_entry(regs->gr[20] & 0xffffffff,
-			regs->gr[26] & 0xffffffff,
-			regs->gr[25] & 0xffffffff,
-			regs->gr[24] & 0xffffffff,
-			regs->gr[23] & 0xffffffff);
+		audit_syscall_entry(regs->gr[20] & 0xffffffff, regs);

[Severity: High]
Does removing the explicit 32-bit masking for compat syscall arguments allow a ptracer to bypass audit filters?

Since audit_syscall_entry() now uses syscall_get_arguments() internally to extract the registers, and syscall_get_arguments() does not truncate the arguments to 32 bits on PARISC:

arch/parisc/include/asm/syscall.h:syscall_get_arguments() {
	args[5] = regs->gr[21];
	args[4] = regs->gr[22];
	args[3] = regs->gr[23];
	args[2] = regs->gr[24];
	args[1] = regs->gr[25];
	args[0] = regs->gr[26];
}

An attacker in a 32-bit compat task could manipulate the upper 32 bits of syscall argument registers via ptrace. Audit would evaluate the unmasked 64-bit values, while the syscall wrapper would truncate them to 32 bits, potentially leading to an evasion of security audit logging.

[Severity: High]
This is a pre-existing issue, but does Seccomp also evaluate unmasked 64-bit register values for 32-bit compat tasks on PARISC?

Seccomp relies on syscall_get_arguments() in populate_seccomp_data():

kernel/seccomp.c:populate_seccomp_data() {
	...
	syscall_get_arguments(task, regs, args);
	sd->args[0] = args[0];
	sd->args[1] = args[1];
	...
}

Because syscall_get_arguments() does not mask the upper 32 bits for compat tasks, could an attacker bypass Seccomp reject filters by injecting 64-bit values with garbage in the upper 32 bits before Seccomp evaluates them?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=9
#25 lore raw

PATCH v2 10/13 sh: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/sh/kernel/ptrace_32.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
arch/sh/kernel/ptrace_32.c +1 -2
index 8794081483fb..e138f529ee7e 100644
@@ -466,8 +466,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 		trace_sys_enter(regs, regs->regs[0]);
 
-	audit_syscall_entry(regs->regs[3], regs->regs[4], regs->regs[5],
-			    regs->regs[6], regs->regs[7]);
+	audit_syscall_entry(regs->regs[3], regs);
 
 	return 0;
 }
Signature · 1 line
2.55.0
#26 lore raw

Re: [PATCH v2 10/13] sh: pass pt_regs to audit_syscall_entry()

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Syscall spoofing vulnerability: trace_sys_enter() logs the user-controlled 5th argument instead of the true syscall ID. --

commit 54980eef37cc5d7ecba9111c6a7139474010c424
Author: Ricardo Robaina <[email protected]>

sh: pass pt_regs to audit_syscall_entry()

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). This commit drops the individual argument registers from the call and passes regs instead.

arch/sh/kernel/ptrace_32.c asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) · line 467

Quoted 7 lines
arch/sh/kernel/ptrace_32.c +0 -0
index 8794081483fbd..e138f529ee7ec 100644
@@ -466,8 +466,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 		trace_sys_enter(regs, regs->regs[0]);

[Severity: Critical]
This isn't a bug introduced by this patch, but it appears there is a critical pre-existing issue here in do_syscall_trace_enter().

Could trace_sys_enter() be logging the wrong syscall ID?

On SH32, the actual syscall number is in regs->regs[3], while regs->regs[0] contains the 5th argument, which is a user-controlled value.

Because do_syscall_trace_enter() passes regs->regs[0] to trace_sys_enter(), a user-space application might be able to invoke a malicious syscall (like execve) while populating the 5th argument register with a benign syscall ID.

Does this mismatch allow attackers to execute one syscall while security monitors log another, potentially bypassing eBPF and ftrace monitors?

Quoted 6 lines
arch/sh/kernel/ptrace_32.c +1 -2
-	audit_syscall_entry(regs->regs[3], regs->regs[4], regs->regs[5],
-			    regs->regs[6], regs->regs[7]);
+	audit_syscall_entry(regs->regs[3], regs);
 
 	return 0;
 }
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=10
#27 lore raw

PATCH v2 11/13 sparc64: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/sparc/kernel/ptrace_64.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
arch/sparc/kernel/ptrace_64.c +1 -3
index 825ddf55fece..b162585b4a74 100644
@@ -1098,9 +1098,7 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 		trace_sys_enter(regs, regs->u_regs[UREG_G1]);
 
-	audit_syscall_entry(regs->u_regs[UREG_G1], regs->u_regs[UREG_I0],
-			    regs->u_regs[UREG_I1], regs->u_regs[UREG_I2],
-			    regs->u_regs[UREG_I3]);
+	audit_syscall_entry(regs->u_regs[UREG_G1], regs);
 
 	return ret;
 }
Signature · 1 line
2.55.0
#28 lore raw

Re: [PATCH v2 11/13] sparc64: pass pt_regs to audit_syscall_entry()

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 5 lines

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=11
#29 lore raw

PATCH v2 12/13 um: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/um/kernel/ptrace.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)
arch/um/kernel/ptrace.c +1 -5
index 7da0a5223aa6..bdc1b18bb342 100644
@@ -123,11 +123,7 @@ static void send_sigtrap(struct uml_pt_regs *regs, int error_code)
  */
 int syscall_trace_enter(struct pt_regs *regs)
 {
-	audit_syscall_entry(UPT_SYSCALL_NR(&regs->regs),
-			    UPT_SYSCALL_ARG1(&regs->regs),
-			    UPT_SYSCALL_ARG2(&regs->regs),
-			    UPT_SYSCALL_ARG3(&regs->regs),
-			    UPT_SYSCALL_ARG4(&regs->regs));
+	audit_syscall_entry(UPT_SYSCALL_NR(&regs->regs), regs);
 
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
 		trace_sys_enter(regs, UPT_SYSCALL_NR(&regs->regs));
Signature · 1 line
2.55.0
#30 lore raw

Re: [PATCH v2 12/13] um: pass pt_regs to audit_syscall_entry()

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 5 lines

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=12
#31 lore raw

PATCH v2 13/13 xtensa: pass pt_regs to audit_syscall_entry()

In reply to Ricardo Robaina

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

---
 arch/xtensa/kernel/ptrace.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
arch/xtensa/kernel/ptrace.c +1 -3
index 364e4fdabb00..327e00c2ebde 100644
@@ -561,9 +561,7 @@ int do_syscall_trace_enter(struct pt_regs *regs)
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
 		trace_sys_enter(regs, syscall_get_nr(current, regs));
 
-	audit_syscall_entry(regs->syscall, regs->areg[6],
-			    regs->areg[3], regs->areg[4],
-			    regs->areg[5]);
+	audit_syscall_entry(regs->syscall, regs);
 	return 1;
 }
 
Signature · 1 line
2.55.0
#32 lore raw

Re: [PATCH v2 13/13] xtensa: pass pt_regs to audit_syscall_entry()

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 5 lines

audit_syscall_entry() now takes a pointer to pt_regs and extracts the syscall arguments itself via syscall_get_arguments(). Drop the individual argument registers from the call and pass regs instead.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=13