summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJhonata Poma-Hansen <jhonata.poma@gmail.com>2026-10-27 15:04:01 +0100
committerJakub Kicinski <kuba@kernel.org>2026-10-29 09:15:16 -0700
commitb45d37740cfb4bdfccef3fbd9fd970fd3224438f (patch)
tree6116bfef438b49b863dd85a9a923357eecd022b4
parentc9897252a66b43a17d04c19bc7303fd6539a56ba (diff)
downloadlinux-b45d37740cfb4bdfccef3fbd9fd970fd3224438f.tar.gz
linux-b45d37740cfb4bdfccef3fbd9fd970fd3224438f.zip
netlink: check sk_rcvbuf before charging the dump skb
Since commit ae8f160e7eb2 ("netlink: Fix wraparounds of sk->sk_rmem_alloc."), netlink_dump() charges the dump skb first and queues it only if sk_rmem_alloc is still below sk_rcvbuf. Commit a215b5723922 ("netlink: make sure we allow at least one dump skb") added an exception for an empty socket. A dump that starts while something else is still charged to a socket with a small receive buffer fails with ENOBUFS and stays running. Every recvmsg() retries it, fails again and sets sk_err, so a reader that peeks before reading, as libnl does, gets ENOBUFS on the read after each peek, never consumes the message at the head of the queue and spins forever. I hit this with "iw dev wlan0 scan" hanging at 100% CPU on an arm64 board with a 6.6 based kernel (iw 5.19 sets SO_RCVBUF to 8KiB). A kprobe trace showed the scan dump starting while the NEW_SCAN_RESULTS notification iw had just read was still charged to its socket, because iw had preempted the sender before netlink_broadcast() returned. Check the limit before charging, as before ae8f160e7eb2, which also covers the empty socket. netlink_recvmsg() continues a dump only at or below half of sk_rcvbuf, so the skb is then always queued, and a dump goes over the limit by at most one skb. Refuse a skb that would take sk_rmem_alloc past INT_MAX, so the wraparound stays fixed. Reproduced on net in a KVM guest with a userspace reproducer and the selftest in the next patch; both pass with this fix. Fixes: ae8f160e7eb2 ("netlink: Fix wraparounds of sk->sk_rmem_alloc.") Signed-off-by: Jhonata Poma-Hansen <jhonata.poma@gmail.com> Reviewed-by: Eric Dumazet <edumazet@kernel.org> Link: https://patch.msgid.link/20261027-netlink-dump-livelock-v2-1-c4bb05352a33@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--net/netlink/af_netlink.c6
1 files changed, 5 insertions, 1 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index fec2879c497d..1281c42a3456 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -2322,9 +2322,13 @@ static int netlink_dump(struct sock *sk, bool lock_taken)
if (!skb)
goto errout_skb;
+ /* Check the limit before charging the skb: netlink_recvmsg() continues
+ * the dump at or below half of sk_rcvbuf, and the skb must be queued
+ * then. Keep sk_rmem_alloc at or below INT_MAX.
+ */
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 || rmem > INT_MAX) {
atomic_sub(skb->truesize, &sk->sk_rmem_alloc);
goto errout_skb;
}