Thread (26 messages) 26 messages, 2 authors, 1d ago
WARM1d

[RFC PATCH 16/24] audit: add pidfd spawn child contexts

From: Li Chen <hidden>
Date: 2026-07-16 14:41:42
Also in: linux-arch, linux-doc, linux-fsdevel, linux-kselftest, linux-mm, linux-security-module, lkml
Subsystem: audit subsystem, the rest · Maintainers: Paul Moore, Eric Paris, Linus Torvalds

Kernel-mediated spawn executes file setup and exec from initial child task
work, not from a second syscall entry. Representing that work as another
AUDIT_SYSCALL record duplicates the source pidfd_spawn_run transaction and
invents a syscall that the child never entered.

Add a dedicated audit context and AUDIT_PIDFD_SPAWN record. Carry the
originating syscall number and arguments. Existing syscall exit filters can
then select the child transaction. CWD, PATH, and EXECVE records remain
correlated with it. Let the child close the transaction with an explicit
result instead of rewriting an architecture syscall-return frame.

Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Li Chen <redacted>
---
 include/linux/audit.h      |  31 +++++++++++
 include/uapi/linux/audit.h |   1 +
 kernel/audit.h             |   1 +
 kernel/auditsc.c           | 105 ++++++++++++++++++++++++++++++++++++-
 4 files changed, 136 insertions(+), 2 deletions(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 45abb3722d304..872624a9fba08 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -323,6 +323,9 @@ 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);
+void __audit_pidfd_spawn_entry(int major, unsigned long a0, unsigned long a1,
+			       unsigned long a2, unsigned long a3);
+void __audit_pidfd_spawn_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_exit(int ret_success, long ret_value);
@@ -373,6 +376,22 @@ static inline void audit_uring_exit(int success, long code)
 	if (unlikely(audit_context()))
 		__audit_uring_exit(success, code);
 }
+
+static inline void audit_pidfd_spawn_entry(int major, unsigned long a0,
+					   unsigned long a1,
+					   unsigned long a2,
+					   unsigned long a3)
+{
+	if (unlikely(audit_context()))
+		__audit_pidfd_spawn_entry(major, a0, a1, a2, a3);
+}
+
+static inline void audit_pidfd_spawn_exit(int success, long code)
+{
+	if (unlikely(audit_context()))
+		__audit_pidfd_spawn_exit(success, code);
+}
+
 static inline void audit_syscall_entry(int major, unsigned long a0,
 				       unsigned long a1, unsigned long a2,
 				       unsigned long a3)
@@ -380,6 +399,7 @@ static inline void audit_syscall_entry(int major, unsigned long a0,
 	if (unlikely(audit_context()))
 		__audit_syscall_entry(major, a0, a1, a2, a3);
 }
+
 static inline void audit_syscall_exit(void *pt_regs)
 {
 	if (unlikely(audit_context())) {
@@ -611,10 +631,21 @@ static inline void audit_uring_entry(u8 op)
 { }
 static inline void audit_uring_exit(int success, long code)
 { }
+
+static inline void audit_pidfd_spawn_entry(int major, unsigned long a0,
+					   unsigned long a1,
+					   unsigned long a2,
+					   unsigned long a3)
+{ }
+
+static inline void audit_pidfd_spawn_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_exit(void *pt_regs)
 { }
 static inline bool audit_dummy_context(void)
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index e8f5ce677df73..e09fb5222293a 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -122,6 +122,7 @@
 #define AUDIT_OPENAT2		1337	/* Record showing openat2 how args */
 #define AUDIT_DM_CTRL		1338	/* Device Mapper target control */
 #define AUDIT_DM_EVENT		1339	/* Device Mapper events */
+#define AUDIT_PIDFD_SPAWN	1340	/* pidfd spawn child operation */
 
 #define AUDIT_AVC		1400	/* SE Linux avc denial or grant */
 #define AUDIT_SELINUX_ERR	1401	/* Internal SE Linux Errors */
diff --git a/kernel/audit.h b/kernel/audit.h
index 92d5e723d570b..2f740df7beecf 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -112,6 +112,7 @@ struct audit_context {
 		AUDIT_CTX_UNUSED,	/* audit_context is currently unused */
 		AUDIT_CTX_SYSCALL,	/* in use by syscall */
 		AUDIT_CTX_URING,	/* in use by io_uring */
+		AUDIT_CTX_PIDFD_SPAWN,	/* pidfd spawn child operation */
 	} context;
 	enum audit_state    state, current_state;
 	struct audit_stamp  stamp;	/* event identifier */
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 6610e667c728a..8f6d84a902239 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -1649,6 +1649,30 @@ static void audit_log_uring(struct audit_context *ctx)
 	audit_log_end(ab);
 }
 
+/**
+ * audit_log_pidfd_spawn - generate an AUDIT_PIDFD_SPAWN record
+ * @ctx: the audit context
+ */
+static void audit_log_pidfd_spawn(struct audit_context *ctx)
+{
+	struct audit_buffer *ab;
+
+	ab = audit_log_start(ctx, GFP_KERNEL, AUDIT_PIDFD_SPAWN);
+	if (!ab)
+		return;
+	audit_log_format(ab, "arch=%x syscall=%d", ctx->arch, ctx->major);
+	if (ctx->return_valid != AUDITSC_INVALID)
+		audit_log_format(ab, " success=%s exit=%ld",
+				 str_yes_no(ctx->return_valid == AUDITSC_SUCCESS),
+				 ctx->return_code);
+	audit_log_format(ab, " a0=%lx a1=%lx a2=%lx a3=%lx items=%d",
+			 ctx->argv[0], ctx->argv[1], ctx->argv[2],
+			 ctx->argv[3], ctx->name_count);
+	audit_log_task_info(ab);
+	audit_log_key(ab, ctx->filterkey);
+	audit_log_end(ab);
+}
+
 static void audit_log_exit(void)
 {
 	int i, call_panic = 0;
@@ -1687,6 +1711,9 @@ static void audit_log_exit(void)
 	case AUDIT_CTX_URING:
 		audit_log_uring(context);
 		break;
+	case AUDIT_CTX_PIDFD_SPAWN:
+		audit_log_pidfd_spawn(context);
+		break;
 	default:
 		BUG();
 		break;
@@ -1782,7 +1809,8 @@ static void audit_log_exit(void)
 		audit_log_name(context, n, NULL, i++, &call_panic);
 	}
 
-	if (context->context == AUDIT_CTX_SYSCALL)
+	if (context->context == AUDIT_CTX_SYSCALL ||
+	    context->context == AUDIT_CTX_PIDFD_SPAWN)
 		audit_log_proctitle();
 
 	/* Send end of event record to help user space know we are finished */
@@ -1818,7 +1846,8 @@ void __audit_free(struct task_struct *tsk)
 	if (tsk == current && !context->dummy) {
 		context->return_valid = AUDITSC_INVALID;
 		context->return_code = 0;
-		if (context->context == AUDIT_CTX_SYSCALL) {
+		if (context->context == AUDIT_CTX_SYSCALL ||
+		    context->context == AUDIT_CTX_PIDFD_SPAWN) {
 			audit_filter_syscall(tsk, context);
 			audit_filter_inodes(tsk, context);
 			if (context->current_state == AUDIT_STATE_RECORD)
@@ -1967,6 +1996,78 @@ void __audit_uring_exit(int success, long code)
 	audit_reset_context(ctx);
 }
 
+/**
+ * __audit_pidfd_spawn_entry - start a pidfd spawn child audit context
+ * @major: originating pidfd_spawn_run syscall number
+ * @a0: originating syscall argument 0
+ * @a1: originating syscall argument 1
+ * @a2: originating syscall argument 2
+ * @a3: originating syscall argument 3
+ */
+void __audit_pidfd_spawn_entry(int major, unsigned long a0, unsigned long a1,
+			       unsigned long a2, unsigned long a3)
+{
+	struct audit_context *context = audit_context();
+	enum audit_state state;
+
+	if (!audit_enabled || !context)
+		return;
+
+	WARN_ON(context->context != AUDIT_CTX_UNUSED);
+	WARN_ON(context->name_count);
+	if (context->context != AUDIT_CTX_UNUSED || context->name_count) {
+		audit_panic("unrecoverable error in audit_pidfd_spawn_entry()");
+		return;
+	}
+
+	state = context->state;
+	if (state == AUDIT_STATE_DISABLED)
+		return;
+
+	context->dummy = !audit_n_rules;
+	if (!context->dummy && state == AUDIT_STATE_BUILD) {
+		context->prio = 0;
+		if (auditd_test_task(current))
+			return;
+	}
+
+	context->arch = syscall_get_arch(current);
+	context->major = major;
+	context->argv[0] = a0;
+	context->argv[1] = a1;
+	context->argv[2] = a2;
+	context->argv[3] = a3;
+	context->context = AUDIT_CTX_PIDFD_SPAWN;
+	context->current_state = state;
+	ktime_get_coarse_real_ts64(&context->stamp.ctime);
+}
+
+/**
+ * __audit_pidfd_spawn_exit - finish a pidfd spawn child audit context
+ * @success: whether child-side setup succeeded
+ * @return_code: child-side setup result
+ */
+void __audit_pidfd_spawn_exit(int success, long return_code)
+{
+	struct audit_context *context = audit_context();
+
+	if (!context || context->dummy ||
+	    context->context != AUDIT_CTX_PIDFD_SPAWN)
+		goto out;
+
+	if (!list_empty(&context->killed_trees))
+		audit_kill_trees(context);
+
+	audit_return_fixup(context, success, return_code);
+	audit_filter_syscall(current, context);
+	audit_filter_inodes(current, context);
+	if (context->current_state == AUDIT_STATE_RECORD)
+		audit_log_exit();
+
+out:
+	audit_reset_context(context);
+}
+
 /**
  * __audit_syscall_entry - fill in an audit record at syscall entry
  * @major: major syscall type (function)
-- 
2.52.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help