On Mon, Aug 10, 2026 at 1:28 PM Bradley Morgan <[email protected]> wrote:
>
> On 10 August 2026 16:19:10 BST, Ricardo Robaina <[email protected]>
> wrote:
> >On Mon, Aug 10, 2026 at 10:32 AM Bradley Morgan <[email protected]> wrote:
> >>
> >> Hi Ricardo,
> >>
> >> > -     nlh->nlmsg_len = skb->len;
> >> > +     nlh->nlmsg_len = copy->len;
> >>
> >> Fine. skb_copy() does skb_put(n, skb->len) and nothing touches
> >> copy->len in between, so the two are always identical.
> >> Which is also why "safer" is a bit of a stretch, it prevents nothing.
> >> Feel free to bikeshed the changelog wording on that.
> >>
> >
> >Thanks for reviewing this patch, Bradley. I agree with you on the wording.
> >
>
> np.
>
> >> If you ever want a more interesting cleanup here, the real quirk is
> >> the line above: the unicast path sets nlmsg_len to skb->len minus
> >> NLMSG_HDRLEN, this one uses the full length.
> >
> >That's interesting, maybe moving the nlmsg_len fixup out of
> >__audit_log_end() would be better. I'll look into it.
> >
>
> Hmmm.. perhaps it will. I will suggest a fix:
>
> From 776375d625cd9cb138f7a6f876861bf428ae1e6c Mon Sep 17 00:00:00 2001
> From: Bradley Morgan <[email protected]>
> Date: Mon, 10 Aug 2026 16:26:06 +0000
> Subject: [PATCH] audit: move the nlmsg_len fixup from __audit_log_end() to
>  send time
>
> The auditd breakage (nlmsg_len set to the payload length instead of
> the full message length) is applied in __audit_log_end() when the
> record is queued. That forces kauditd_send_multicast_skb() to deep
> copy every record and undo the length on the copy, so the multicast
> group still sees a standard netlink message.
>
> Instead, finalize the header with the standard full length at queue
> time and apply the auditd length right before the unicast send in
> kauditd_send_queue(). Records stay standard netlink messages while
> they sit in the queues, and the multicast copy no longer needs its
> own fixup. The deep copy itself stays, since the auditd length
> rewrite lands in the shared data region after the copy is already
> handed to the listeners.
>
> auditd sees the same bytes as before. The fixup is computed from
> skb->len, which does not change between queueing and sending, so
> records that come back through the retry and hold queues get the same
> value again. Reply and rule list skbs are built with nlmsg_put() and
> sent on other paths, none of those are touched.
>
> This came out of the "use copied skb length" thread, where moving the
> fixup was suggested as the more interesting cleanup.
>
> Signed-off-by: Bradley Morgan <[email protected]>
> ---
>  kernel/audit.c | 38 ++++++++++++++++++++++----------------
>  1 file changed, 22 insertions(+), 16 deletions(-)
>
> diff --git a/kernel/audit.c b/kernel/audit.c
> index 9412af9144bc..5b6528fc6eb5 100644
> --- a/kernel/audit.c
> +++ b/kernel/audit.c
> @@ -802,6 +802,15 @@ static int kauditd_send_queue(struct sock *sk, u32 
> portid,
>                 if (skb_hook)
>                         (*skb_hook)(skb);
>
> +               /*
> +                * auditd expects nlmsg_len to be the payload length rather
> +                * than the full message length.  Apply the breakage here at
> +                * send time so the record stays a standard netlink message
> +                * while queued and while it is copied for the multicast
> +                * group above.
> +                */
> +               nlmsg_hdr(skb)->nlmsg_len = skb->len - NLMSG_HDRLEN;
> +
>                 /* can we send to anyone via unicast? */
>                 if (!sk) {
>                         if (err_hook)
> @@ -849,7 +858,6 @@ static void kauditd_send_multicast_skb(struct sk_buff 
> *skb)
>  {
>         struct sk_buff *copy;
>         struct sock *sock = audit_get_sk(&init_net);
> -       struct nlmsghdr *nlh;
>
>         /* NOTE: we are not taking an additional reference for init_net since
>          *       we don't have to worry about it going away */
> @@ -859,19 +867,15 @@ static void kauditd_send_multicast_skb(struct sk_buff 
> *skb)
>
>         /*
>          * The seemingly wasteful skb_copy() rather than bumping the refcount
> -        * using skb_get() is necessary because non-standard mods are made to
> -        * the skb by the original kaudit unicast socket send routine.  The
> -        * existing auditd daemon assumes this breakage.  Fixing this would
> -        * require co-ordinating a change in the established protocol between
> -        * the kaudit kernel subsystem and the auditd userspace code.  There 
> is
> -        * no reason for new multicast clients to continue with this
> -        * non-compliance.
> +        * using skb_get() is necessary because the unicast send in
> +        * kauditd_send_queue() rewrites nlmsg_len to the payload only length
> +        * that auditd expects.  The copy shields the multicast listeners from
> +        * that historical breakage, there is no reason for them to continue
> +        * with this non compliance.
>          */
>         copy = skb_copy(skb, GFP_KERNEL);
>         if (!copy)
>                 return;
> -       nlh = nlmsg_hdr(copy);
> -       nlh->nlmsg_len = skb->len;
>
>         nlmsg_multicast(sock, copy, 0, AUDIT_NLGRP_READLOG, GFP_KERNEL);
>  }
> @@ -2785,13 +2789,15 @@ int audit_signal_info(int sig, struct task_struct *t)
>   */
>  static void __audit_log_end(struct sk_buff *skb)
>  {
> -       struct nlmsghdr *nlh;
> -
>         if (audit_rate_check()) {
> -               /* setup the netlink header, see the comments in
> -                * kauditd_send_multicast_skb() for length quirks */
> -               nlh = nlmsg_hdr(skb);
> -               nlh->nlmsg_len = skb->len - NLMSG_HDRLEN;
> +               /*
> +                * The record was built by appending data after nlmsg_put()
> +                * without keeping nlmsg_len up to date, so finalize the
> +                * header here with the standard full message length.  The
> +                * payload only length that auditd expects is applied at
> +                * send time in kauditd_send_queue().
> +                */
> +               nlmsg_end(skb, nlmsg_hdr(skb));
>
>                 /* queue the netlink packet */
>                 skb_queue_tail(&audit_queue, skb);
> --
> 2.47.3
>
>
>
>
> >>
> >> Well, why not, please add:
> >>
> >> Reviewed-by: Bradley Morgan <[email protected]>
> >> Thanks!
> >>
> >
> >-Ricardo
> >
> >
>
> Thanks!
>

It looks good to me. I've built a kernel to test it and verified that
it passes the audit testsuite.

# uname -r
7.2.0-rc6+

# make test
make -C tests test
chmod +x */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.
Files=28, Tests=303, 46 wallclock secs ( 0.08 usr  0.03 sys + 14.50
cusr  1.56 csys = 16.17 CPU)
Result: PASS

Reviewed-by: Ricardo Robaina <[email protected]>
Tested-by: Ricardo Robaina <[email protected]>

-Ricardo


Reply via email to