Re: iw scan hangs in netlink ENOBUFS loop with kernel 6.6.99+

Peter Astrand <[email protected]> Sun, 26 Jul 2026 20:39:06 +0200 (CEST)
Newsgroups dev.linux.lists.regressions,org.kernel.vger.linux-wireless,org.kernel.vger.stable
Message-ID <[email protected]>
On Wed, 22 Jul 2026, Peter Astrand wrote:
> 
> On Tue, 21 Jul 2026, Thorsten Leemhuis wrote:
> 
> > On 7/14/26 00:37, Peter Astrand wrote:
> > > 
> > > kernel 6.6.y
> > > iw 6.7
> > > libnl-genl-3-200 3.9.0
> > > mac80211_hwsim
> > > 
> > > With kernel 6.6.99 and later, "iw dev <device> scan" sometimes hangs in 
> > > busy loop:
> > 
> > Thanks for the report. We need to rule out if this is a 6.6.y specific
> > problem (like a broken or incomplete backport) or something general.
> > Could you please check if 7.2-rc is affected as well? Testing 6.18.y
> > might help, too, if it's easy for you to try. Maybe switching to that
> > might be the easies way to resolve the problem and a good idea in
> > general, as explained here:
> > http://www.kroah.com/log/blog/2018/08/24/what-stable-kernel-should-i-use/
> 
> Platform is NXP imx6ull. Forgot to mention that we are actually running 
> https://github.com/Freescale/linux-fslc/tree/6.6-2.2.x-imx rather than 
> upstream kernel. No changes to net/netlink though.
> 
> We cannot easily test with 7.X, but I have tested 
> https://github.com/Freescale/linux-fslc/tree/6.18-2.0.x-imx and can 
> reproduce the same problem; this is 6.18.20. strace output is similar:
> 
> recvmsg(3, {msg_namelen=12}, 0)         = -1 ENOBUFS (No buffer space available)
> recvmsg(3, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=12, msg_iov=[{iov_base=[{nlmsg_len=48, nlmsg_type=NLMSG_ERROR, nlmsg_flags=0, nlmsg_seq=2510255223, nlmsg_pid=1468011032}, {error=-ENOBUFS, msg=[{nlmsg_len=28, nlmsg_type=0x1b /* NLMSG_??? */, nlmsg_flags=NLM_F_REQUEST|NLM_F_ACK|0x300, nlmsg_seq=2510255223, nlmsg_pid=1468011032}, "\x20\x00\x00\x00\x08\x00\x03\x00\x0d\x00\x00\x00"]}], iov_len=16384}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, MSG_PEEK|MSG_TRUNC) = 48

Claude created a patch (against 6.6.y, but applies to master as well):

--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1219,7 +1219,7 @@ int netlink_attachskb(struct sock *sk, struct 
sk_buff *skb,
        nlk = nlk_sk(sk);
        rmem = atomic_add_return(skb->truesize, &sk->sk_rmem_alloc);
 
-       if ((rmem == skb->truesize || rmem <= READ_ONCE(sk->sk_rcvbuf)) &&
+       if (rmem - skb->truesize <= READ_ONCE(sk->sk_rcvbuf) &&
            !test_bit(NETLINK_S_CONGESTED, &nlk->state)) {
                netlink_skb_set_owner_r(skb, sk);
                return 0;
@@ -1395,7 +1395,7 @@ static int netlink_broadcast_deliver(struct sock 
*sk, struct sk_buff *skb)
        rmem = atomic_add_return(skb->truesize, &sk->sk_rmem_alloc);
        rcvbuf = READ_ONCE(sk->sk_rcvbuf);
 
-       if ((rmem == skb->truesize || rmem <= rcvbuf) &&
+       if (rmem - skb->truesize <= rcvbuf &&
            !test_bit(NETLINK_S_CONGESTED, &nlk->state)) {
                netlink_skb_set_owner_r(skb, sk);
                __netlink_sendskb(sk, skb);
@@ -2232,7 +2232,7 @@ static int netlink_dump(struct sock *sk, bool 
lock_taken)
 
        rcvbuf = READ_ONCE(sk->sk_rcvbuf);
        rmem = atomic_add_return(skb->truesize, &sk->sk_rmem_alloc);
-       if (rmem != skb->truesize && rmem >= rcvbuf) {
+       if (rmem - skb->truesize >= rcvbuf) {
                atomic_sub(skb->truesize, &sk->sk_rmem_alloc);
                goto errout_skb;
        }


It also created this commit message:

    netlink: restore pre-add admission-control boundary
    
    Commit 55baecb9eb90 ("netlink: Fix wraparounds of sk->sk_rmem_alloc.")
    moved the sk_rmem_alloc admission check in netlink_attachskb(),
    netlink_broadcast_deliver() and netlink_dump() from "queue was already
    under budget before this skb" (checked BEFORE adding truesize) to
    "queue is still under budget after this skb" (checked AFTER adding
    truesize). That's a strictly stricter boundary: a message that would
    push the queue from just-under to just-over sk_rcvbuf is now rejected
    outright, where before it was admitted.
    
    For slow-draining or MSG_PEEK-based readers (e.g. iw/libnl doing
    netlink dumps), a message can hit this new, stricter ceiling every
    time it's retried, turning what used to be a transient ENOBUFS into
    a permanent one for that message size -- causing indefinite retry
    loops in userspace (iw scan hanging).
    
    Fix by keeping atomic_add_return()-based accounting (needed for the
    actual wraparound fix), but comparing against the pre-add value:
    rmem is guaranteed >= skb->truesize since it's the value just
    returned by adding truesize, so `rmem - skb->truesize` can't
    underflow and stays unsigned throughout, preserving the wraparound
    fix while restoring the original admission boundary.


The patch seems to solve my issues. However, since I don't understand this 
code well, I cannot really say if the patch is correct. 

Br,
Peter