Original title: fixes clean proctitle in audit context on exec call

Between the actual process startup (fork systemd) and the executable file
replacement (exec), systemd sets a temporary file name (executable file
name in parentheses). If an auditable system call occurs at this point,
the audit context will latch the temporary process name into the cache.
This name will not change again. The patch clears proctitle into the
audit cache when the exec call is made, allowing the new process name to
be latched.

Suggested by Roman Dolgikh https://github.com/rmd4ctf 2025-06-11
Link: https://github.com/linux-audit/audit-kernel/issues/170.
Signed-off-by: Richard Guy Briggs <[email protected]>
---
 fs/exec.c             | 2 ++
 include/linux/audit.h | 9 +++++++++
 kernel/auditsc.c      | 4 ++--
 3 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index b92fe7db176c..bd51489dec23 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1744,6 +1744,8 @@ static int exec_binprm(struct linux_binprm *bprm)
                        fput(exec);
        }
 
+       /* clear proctitle in audit context to allow replacement */
+       audit_proctitle_free(audit_context());
        audit_bprm(bprm);
        trace_sched_process_exec(current, old_pid, bprm);
        ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 45abb3722d30..03ae563ca348 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -320,6 +320,7 @@ static inline void audit_cfg_lsm(const struct lsm_id 
*lsmid, int flags)
 /* These are defined in auditsc.c */
                                /* Public API */
 extern int  audit_alloc(struct task_struct *task);
+extern void __audit_proctitle_free(struct audit_context *context);
 extern void __audit_free(struct task_struct *task);
 extern void __audit_uring_entry(u8 op);
 extern void __audit_uring_exit(int success, long code);
@@ -353,6 +354,11 @@ static inline bool audit_dummy_context(void)
        void *p = audit_context();
        return !p || *(int *)p;
 }
+static inline void audit_proctitle_free(struct audit_context *context)
+{
+       if (unlikely(!audit_dummy_context()))
+               __audit_proctitle_free(context);
+}
 static inline void audit_free(struct task_struct *task)
 {
        if (unlikely(task->audit_context))
@@ -470,6 +476,7 @@ static inline void audit_bprm(struct linux_binprm *bprm)
        if (unlikely(!audit_dummy_context()))
                __audit_bprm(bprm);
 }
+
 static inline int audit_socketcall(int nargs, unsigned long *args)
 {
        if (unlikely(!audit_dummy_context()))
@@ -605,6 +612,8 @@ static inline int audit_alloc(struct task_struct *task)
 {
        return 0;
 }
+static inline void audit_proctitle_free(struct audit_context *context)
+{ }
 static inline void audit_free(struct task_struct *task)
 { }
 static inline void audit_uring_entry(u8 op)
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 6610e667c728..69484ab2c8bc 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -913,7 +913,7 @@ void audit_filter_inodes(struct task_struct *tsk, struct 
audit_context *ctx)
        rcu_read_unlock();
 }
 
-static inline void audit_proctitle_free(struct audit_context *context)
+void __audit_proctitle_free(struct audit_context *context)
 {
        kfree(context->proctitle.value);
        context->proctitle.value = NULL;
@@ -1086,7 +1086,7 @@ static inline void audit_free_context(struct 
audit_context *context)
 {
        /* resetting is extra work, but it is likely just noise */
        audit_reset_context(context);
-       audit_proctitle_free(context);
+       __audit_proctitle_free(context);
        free_tree_refs(context);
        kfree(context->filterkey);
        kfree(context);
-- 
2.43.5


Reply via email to