From: Jhonata Poma-Hansen <redacted>
netlink_dump() sizes the skb after the largest buffer the reader passed
to recvmsg(), up to 32KiB. Since commit ae8f160e7eb2 ("netlink: Fix
wraparounds of sk->sk_rmem_alloc.") the skb is only queued if it fits
in sk_rcvbuf, or if nothing else is charged to the socket.
A reader with a small receive buffer that still has data charged to
its socket when a dump starts gets ENOBUFS, even when a minimum size
skb would fit. The dump stays marked as running and every recvmsg()
retries it, so a reader that peeks before reading, as libnl does,
never consumes the data at the head of the queue and spins forever.
"iw dev wlan0 scan" hits this: its socket has an 8KiB buffer, it joins
the nl80211 multicast groups, and it dumps the scan results as soon as
it has read the NEW_SCAN_RESULTS notification. When it preempts the
sender, the notification is still charged to its socket until
netlink_broadcast() returns, the dump fails to start and iw spins at
100% CPU.
Retry with alloc_min_size before failing the dump.
Fixes: ae8f160e7eb2 ("netlink: Fix wraparounds of sk->sk_rmem_alloc.")
Signed-off-by: Jhonata Poma-Hansen <redacted>
---
net/netlink/af_netlink.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index a904dc3bc..b750e79fe 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -2318,10 +2318,21 @@ static int netlink_dump(struct sock *sk, bool lock_taken)
if (!skb)
goto errout_skb;
+retry:
rcvbuf = READ_ONCE(sk->sk_rcvbuf);
rmem = atomic_add_return(skb->truesize, &sk->sk_rmem_alloc);
if (rmem != skb->truesize && rmem >= rcvbuf) {
atomic_sub(skb->truesize, &sk->sk_rmem_alloc);
+ /* A skb sized after max_recvmsg_len may not fit next to data
+ * still charged to the socket, while a minimum size one does.
+ */
+ if (alloc_size > alloc_min_size) {
+ kfree_skb(skb);
+ alloc_size = alloc_min_size;
+ skb = alloc_skb(alloc_size, GFP_KERNEL);
+ if (skb)
+ goto retry;
+ }
goto errout_skb;
}
--
2.54.0