Re: [PATCH] audit: add CLONE3 auxiliary record to log process cloning

Ricardo Robaina <[email protected]>
Newsgroups org.kernel.vger.audit,org.kernel.vger.linux-kernel,org.kvack.linux-mm
Message-ID <CAABTaaA=FupPfFhOdMPi7nPy5wSh7znEdnBquYbTXCN=YbtKiA@mail.gmail.com>
On Wed, Jul 29, 2026 at 7:03 PM Paul Moore <[email protected]> wrote:
>
> On Fri, Jul 24, 2026 at 11:03 AM Ricardo Robaina <[email protected]> wrote:
> >
> > The clone3(2) syscall moves most parameters to struct clone_args.
> > For this reason, the generic SYSCALL audit record does not capture
> > the structured arguments.
> >
> > Add a CLONE3 auxiliary record that logs: flags, exit_signal, cgroup,
> > and pidfd fields from struct clone_args. When CLONE_PIDFD is set and
> > the syscall succeeds, the resolved pidfd is logged; otherwise
> > pidfd=(null).
> >
> >  ----
> >  type=SYSCALL : syscall=clone3 a0=0x7ffe7f1ec640 a1=0x58 a2=0x0 ...
> >  type=CLONE3 : cl3_flags=0x1000 exit_signal=17 cgroup=0 pidfd=3
> >
> > Link: https://github.com/linux-audit/audit-kernel/issues/151
> > Signed-off-by: Ricardo Robaina <[email protected]>
> > ---
> >  include/linux/audit.h      | 10 ++++++++++
> >  include/uapi/linux/audit.h |  1 +
> >  kernel/auditsc.c           | 20 ++++++++++++++++++++
> >  kernel/fork.c              |  4 +++-
> >  4 files changed, 34 insertions(+), 1 deletion(-)
> >
> > diff --git a/include/linux/audit.h b/include/linux/audit.h
> > index 45abb3722d30..833f349bf415 100644
> > --- a/include/linux/audit.h
> > +++ b/include/linux/audit.h
> > @@ -449,6 +449,7 @@ extern void __audit_tk_injoffset(struct timespec64 offset);
> >  extern void __audit_ntp_log(const struct audit_ntp_data *ad);
> >  extern void __audit_log_nfcfg(const char *name, u8 af, unsigned int nentries,
> >                               enum audit_nfcfgop op, gfp_t gfp);
> > +extern void __audit_log_clone3(struct kernel_clone_args *kargs, int ret);
> >
> >  static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp)
> >  {
> > @@ -598,6 +599,12 @@ static inline void audit_log_nfcfg(const char *name, u8 af,
> >                 __audit_log_nfcfg(name, af, nentries, op, gfp);
> >  }
> >
> > +static inline void audit_log_clone3(struct kernel_clone_args *kargs, int ret)
> > +{
> > +       if (!audit_dummy_context())
> > +               __audit_log_clone3(kargs, ret);
> > +}
> > +
> >  extern int audit_n_rules;
> >  extern int audit_signals;
> >  #else /* CONFIG_AUDITSYSCALL */
> > @@ -730,6 +737,9 @@ static inline void audit_log_nfcfg(const char *name, u8 af,
> >                                    enum audit_nfcfgop op, gfp_t gfp)
> >  { }
> >
> > +static inline void audit_log_clone3(struct kernel_clone_args *kargs, int ret)
> > +{ }
> > +
> >  #define audit_n_rules 0
> >  #define audit_signals 0
> >  #endif /* CONFIG_AUDITSYSCALL */
> > diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
> > index e8f5ce677df7..37357e17adbf 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_CLONE3           1343    /* Record showing clone3 args */
> >
> >  #define AUDIT_AVC              1400    /* SE Linux avc denial or grant */
> >  #define AUDIT_SELINUX_ERR      1401    /* Internal SE Linux Errors */
> > diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> > index 6610e667c728..c0106bb71c19 100644
> > --- a/kernel/auditsc.c
> > +++ b/kernel/auditsc.c
> > @@ -2882,6 +2882,26 @@ void __audit_log_nfcfg(const char *name, u8 af, unsigned int nentries,
> >  }
> >  EXPORT_SYMBOL_GPL(__audit_log_nfcfg);
> >
> > +void __audit_log_clone3(struct kernel_clone_args *kargs, int ret)
> > +{
> > +       struct audit_buffer *ab;
> > +       int pidfd;
> > +
> > +       ab = audit_log_start(audit_context(), GFP_KERNEL,
> > +                            AUDIT_CLONE3);
> > +       if (!ab)
> > +               return;
> > +
> > +       audit_log_format(ab, "cl3_flags=0x%llx exit_signal=%d cgroup=%d",
> > +                        kargs->flags, kargs->exit_signal, kargs->cgroup);
> > +       if ((kargs->flags & CLONE_PIDFD) && ret >= 0 &&
> > +           !get_user(pidfd, kargs->pidfd))
> > +               audit_log_format(ab, " pidfd=%d", pidfd);
>
> Do we care about distinguishing between the combination of CLONE_PIDFD
> and CLONE_THREAD vs CLONE_PIDFD alone?  In other words, do we care if
> the "pidfd" field sometimes represents a pidfd of the child process vs
> a thread in the current process?
>

Hi Paul,

Thanks for reviewing this patch.

The flags field can be use to check if CLONE_THREAD is set, so it's
possible to determine whether the pidfd refers to a child process or a
thread by inspecting the flags. Do you think we should include a
dedicated field for that?

> > +       else
> > +               audit_log_format(ab, " pidfd=(null)");
> > +       audit_log_end(ab);
> > +}
> > +
> >  static void audit_log_task(struct audit_buffer *ab)
> >  {
> >         kuid_t auid, uid;
> > diff --git a/kernel/fork.c b/kernel/fork.c
> > index f0e2e131a9a5..0f52e8c0e900 100644
> > --- a/kernel/fork.c
> > +++ b/kernel/fork.c
> > @@ -3047,7 +3047,9 @@ SYSCALL_DEFINE2(clone3, struct clone_args __user *, uargs, size_t, size)
> >         if (!clone3_args_valid(&kargs))
> >                 return -EINVAL;
> >
> > -       return kernel_clone(&kargs);
> > +       err = kernel_clone(&kargs);
> > +       audit_log_clone3(&kargs, err);
> > +       return err;
> >  }
> >
> >  void walk_process_tree(struct task_struct *top, proc_visitor visitor, void *data)
> > --
> > 2.53.0
>
> --
> paul-moore.com
>

-Ricardo
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.