* [PATCH net 0/2] netlink: fix a dump livelock with a notification queued
@ 2026-10-23 11:26 Jhonata Poma-Hansen via B4 Relay
2026-10-23 11:26 ` [PATCH net 1/2] netlink: retry a dump skb that does not fit with the minimum size Jhonata Poma-Hansen via B4 Relay
2026-10-23 11:26 ` [PATCH net 2/2] selftests: net: netlink-dumps: start a dump with a notification queued Jhonata Poma-Hansen via B4 Relay
0 siblings, 2 replies; 6+ messages in thread
From: Jhonata Poma-Hansen via B4 Relay @ 2026-10-23 11:26 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Shuah Khan
Cc: netdev, linux-kernel, linux-kselftest, Kuniyuki Iwashima,
Jhonata Poma-Hansen
A netlink dump that starts while data is still charged to the reader's
socket fails with ENOBUFS when the reader's receive buffer is small,
even though a minimum size skb would fit. The dump stays marked as
running, every recvmsg() retries it, and a reader that peeks before
reading, as libnl does, spins forever.
I hit this with "iw dev wlan0 scan" on an arm64 board with a 6.6 based
kernel: iw hung at 100% CPU in 5 to 10 scans out of 15. A kprobe trace
showed the scan dump starting with the NEW_SCAN_RESULTS notification
still charged to iw's 16KiB socket, because iw had preempted the sender
before netlink_broadcast() returned.
Patch 1 retries the dump skb with the minimum size before failing.
Patch 2 adds a selftest which starts a link dump with a link
notification queued and reads it like libnl does.
Tested on net 734bef548a96 in a KVM guest: without patch 1 the new test
fails and a standalone reproducer livelocks in 3 runs out of 3; with
it, netlink-dumps passes 5 of 5 and the reproducer completes. The
existing netlink-dumps tests pass with and without the fix.
---
Jhonata Poma-Hansen (2):
netlink: retry a dump skb that does not fit with the minimum size
selftests: net: netlink-dumps: start a dump with a notification queued
net/netlink/af_netlink.c | 11 ++++
tools/testing/selftests/net/netlink-dumps.c | 93 +++++++++++++++++++++++++++++
2 files changed, 104 insertions(+)
---
base-commit: 734bef548a96e8774d6eef5dfa2026c6edc4e80b
change-id: 20261023-netlink-dump-livelock-e83cf13f977a
Best regards,
--
Jhonata Poma-Hansen <jhonata.poma@gmail.com>
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH net 1/2] netlink: retry a dump skb that does not fit with the minimum size 2026-10-23 11:26 [PATCH net 0/2] netlink: fix a dump livelock with a notification queued Jhonata Poma-Hansen via B4 Relay @ 2026-10-23 11:26 ` Jhonata Poma-Hansen via B4 Relay 2026-10-27 11:49 ` netdev-bot+sashiko 2026-10-23 11:26 ` [PATCH net 2/2] selftests: net: netlink-dumps: start a dump with a notification queued Jhonata Poma-Hansen via B4 Relay 1 sibling, 1 reply; 6+ messages in thread From: Jhonata Poma-Hansen via B4 Relay @ 2026-10-23 11:26 UTC (permalink / raw) To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, Shuah Khan Cc: netdev, linux-kernel, linux-kselftest, Kuniyuki Iwashima, Jhonata Poma-Hansen From: Jhonata Poma-Hansen <jhonata.poma@gmail.com> 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 <jhonata.poma@gmail.com> --- 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 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH net 1/2] netlink: retry a dump skb that does not fit with the minimum size 2026-10-23 11:26 ` [PATCH net 1/2] netlink: retry a dump skb that does not fit with the minimum size Jhonata Poma-Hansen via B4 Relay @ 2026-10-27 11:49 ` netdev-bot+sashiko 2026-10-27 12:44 ` Jhonata Poma-Hansen 0 siblings, 1 reply; 6+ messages in thread From: netdev-bot+sashiko @ 2026-10-27 11:49 UTC (permalink / raw) To: jhonata.poma Cc: davem, edumazet, kuba, pabeni, horms, shuah, netdev, linux-kernel, linux-kselftest, kuniyu Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider. Critical: 0 · High: 1 · Medium: 0 · Low: 0 If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough). - [High] The fix is incomplete: the ENOBUFS livelock it targets is still reachable whenever a minimum-size dump skb also does not fit. -- netlink: retry a dump skb that does not fit with the minimum size When the skb sized after max_recvmsg_len is rejected by the sk_rcvbuf admission check, netlink_dump() now frees it and retries once with an alloc_min_size skb. The goal is to fix a livelock added by commit ae8f160e7eb2, where a reader that peeks before reading, such as libnl, spins on ENOBUFS. > diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c > index a904dc3bcad6b..b750e79fed2ed 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; > } [Severity: High] Does this fully close the livelock described in the commit message? The admission rule is unchanged. The minimum size skb is still rejected when rmem_before != 0 and rmem_before + T_min >= sk_rcvbuf, where T_min is the truesize of an alloc_min_size skb. The comment says a minimum size skb "does" fit, but nothing here guarantees that. On the recvmsg side, the dump is only re-run once rmem drops to half of sk_rcvbuf: net/netlink/af_netlink.c:netlink_recvmsg() { ... if (READ_ONCE(nlk->cb_running) && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) { ret = netlink_dump(sk, false); if (ret) { WRITE_ONCE(sk->sk_err, -ret); sk_error_report(sk); } } ... } When sk_rcvbuf < 2 * T_min, that check does not guarantee rmem + T_min < sk_rcvbuf. A few configurations appear to land in that window: - On 4K pages T_min is roughly 4352 bytes. An unprivileged SO_RCVBUF=4096 gives sk_rcvbuf=8192 via __sock_set_rcvbuf(). Any rmem_before in roughly [3840, 4096] then fails both the large attempt and the retry. - On PAGE_SIZE >= 8K, NLMSG_GOODSIZE is SKB_WITH_OVERHEAD(8192), so T_min is roughly 8448. With iw's 16KiB sk_rcvbuf, rmem in roughly [7936, 8192] fails both attempts. A few notifications that are still charged could reach that range, since do_one_broadcast() holds them via skb_get() until netlink_broadcast() returns. - When cb->min_dump_alloc is at least the capped max_recvmsg_len, alloc_size > alloc_min_size is never true and the retry never runs. smc_diag_handler_dump() is one example: it uses SKB_WITH_OVERHEAD(32768). In those states the original sequence still looks reachable: __netlink_dump_start() netlink_dump() -> -ENOBUFS, cb_running stays true netlink_recvmsg(MSG_PEEK) netlink_dump() -> -ENOBUFS, sk_err = ENOBUFS netlink_recvmsg() __skb_try_recv_datagram() sock_error() -> ENOBUFS returned, nothing dequeued rmem never changes, so the peek/read loop keeps spinning at 100% CPU. Before ae8f160e7eb2, the dump skb was admitted whenever rmem_before < rcvbuf, so a dump triggered from recvmsg could not fail this way. Would it be better to relax the admission rule for dump skbs, instead of adding a second, smaller skb size? -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261023-netlink-dump-livelock-v1-0-478214a635d2%40gmail.com ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net 1/2] netlink: retry a dump skb that does not fit with the minimum size 2026-10-27 11:49 ` netdev-bot+sashiko @ 2026-10-27 12:44 ` Jhonata Poma-Hansen 0 siblings, 0 replies; 6+ messages in thread From: Jhonata Poma-Hansen @ 2026-10-27 12:44 UTC (permalink / raw) To: netdev-bot+sashiko Cc: davem, edumazet, kuba, pabeni, horms, shuah, netdev, linux-kernel, linux-kselftest, kuniyu On Tue, 27 Oct 2026 11:49:11 +0000, netdev-bot+sashiko@kernel.org wrote: > - [High] The fix is incomplete: the ENOBUFS livelock it targets is still > reachable whenever a minimum-size dump skb also does not fit. Agreed. I can reproduce it with a small SO_RCVBUF, where the minimum size skb is rejected as well. v2 will rework the admission check for dump skbs instead of adding a second allocation size, so that a dump continued from recvmsg() always makes progress. It will also address the selftest comments on patch 2. pw-bot: cr ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH net 2/2] selftests: net: netlink-dumps: start a dump with a notification queued 2026-10-23 11:26 [PATCH net 0/2] netlink: fix a dump livelock with a notification queued Jhonata Poma-Hansen via B4 Relay 2026-10-23 11:26 ` [PATCH net 1/2] netlink: retry a dump skb that does not fit with the minimum size Jhonata Poma-Hansen via B4 Relay @ 2026-10-23 11:26 ` Jhonata Poma-Hansen via B4 Relay 2026-10-27 11:49 ` netdev-bot+sashiko 1 sibling, 1 reply; 6+ messages in thread From: Jhonata Poma-Hansen via B4 Relay @ 2026-10-23 11:26 UTC (permalink / raw) To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, Shuah Khan Cc: netdev, linux-kernel, linux-kselftest, Kuniyuki Iwashima, Jhonata Poma-Hansen From: Jhonata Poma-Hansen <jhonata.poma@gmail.com> Add a test which starts a link dump on a socket with a small receive buffer while a link notification is still queued, after a large read made the kernel attempt large dump skbs. The test reads like libnl does, peeking before each read, and expects the dump to complete. Signed-off-by: Jhonata Poma-Hansen <jhonata.poma@gmail.com> --- tools/testing/selftests/net/netlink-dumps.c | 93 +++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/tools/testing/selftests/net/netlink-dumps.c b/tools/testing/selftests/net/netlink-dumps.c index 51129c564..6f66a478d 100644 --- a/tools/testing/selftests/net/netlink-dumps.c +++ b/tools/testing/selftests/net/netlink-dumps.c @@ -3,8 +3,10 @@ #define _GNU_SOURCE #include <fcntl.h> +#include <sched.h> #include <stdio.h> #include <string.h> +#include <sys/ioctl.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/syscall.h> @@ -260,4 +262,95 @@ TEST(close_with_ref) usleep(100 * 1000); } +static int lo_set_up(int sock, bool up) +{ + struct ifreq ifr = {}; + + strcpy(ifr.ifr_name, "lo"); + if (ioctl(sock, SIOCGIFFLAGS, &ifr)) + return -1; + if (up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + return ioctl(sock, SIOCSIFFLAGS, &ifr); +} + +static const struct { + struct nlmsghdr nlhdr; + struct ifinfomsg ifm; +} dump_links = { + .nlhdr = { + .nlmsg_len = sizeof(dump_links), + .nlmsg_type = RTM_GETLINK, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP, + .nlmsg_seq = 1, + }, + .ifm = { + .ifi_family = AF_UNSPEC, + }, +}; + +// A dump started while a notification is still queued must make progress +// for a reader which peeks before reading, like libnl does +TEST(dump_with_queued_ntf) +{ + struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + .nl_groups = RTMGRP_LINK, + }; + int netlink_sock, inet_sock, i; + int rcvbuf = 8192; + bool done = false; + char buf[32768]; + ssize_t n; + + ASSERT_EQ(unshare(CLONE_NEWNET), 0); + + inet_sock = socket(AF_INET, SOCK_DGRAM, 0); + ASSERT_GE(inet_sock, 0); + netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + ASSERT_GE(netlink_sock, 0); + ASSERT_EQ(setsockopt(netlink_sock, SOL_SOCKET, SO_RCVBUF, + &rcvbuf, sizeof(rcvbuf)), 0); + ASSERT_EQ(bind(netlink_sock, (struct sockaddr *)&addr, + sizeof(addr)), 0); + + // A large read makes the kernel attempt large dump skbs + ASSERT_EQ(lo_set_up(inet_sock, true), 0); + n = recv(netlink_sock, buf, sizeof(buf), 0); + ASSERT_GE(n, (ssize_t)sizeof(struct nlmsghdr)); + + // Leave one notification queued, then start the dump + ASSERT_EQ(lo_set_up(inet_sock, false), 0); + n = send(netlink_sock, &dump_links, sizeof(dump_links), 0); + ASSERT_EQ(n, sizeof(dump_links)); + + for (i = 0; i < 100 && !done; i++) { + struct nlmsghdr *nlh; + + n = recv(netlink_sock, buf, sizeof(buf), + MSG_PEEK | MSG_TRUNC | MSG_DONTWAIT); + if (n < 0) + continue; + n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT); + if (n < 0) + continue; + + for (nlh = (struct nlmsghdr *)buf; NLMSG_OK(nlh, n); + nlh = NLMSG_NEXT(nlh, n)) { + if (nlh->nlmsg_seq != dump_links.nlhdr.nlmsg_seq) + continue; + if (nlh->nlmsg_type == NLMSG_ERROR) + EXPECT_EQ(*(int *)NLMSG_DATA(nlh), 0); + if (nlh->nlmsg_type == NLMSG_DONE) + done = true; + } + } + EXPECT_TRUE(done); + + close(netlink_sock); + close(inet_sock); +} + TEST_HARNESS_MAIN -- 2.54.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH net 2/2] selftests: net: netlink-dumps: start a dump with a notification queued 2026-10-23 11:26 ` [PATCH net 2/2] selftests: net: netlink-dumps: start a dump with a notification queued Jhonata Poma-Hansen via B4 Relay @ 2026-10-27 11:49 ` netdev-bot+sashiko 0 siblings, 0 replies; 6+ messages in thread From: netdev-bot+sashiko @ 2026-10-27 11:49 UTC (permalink / raw) To: jhonata.poma Cc: davem, edumazet, kuba, pabeni, horms, shuah, netdev, linux-kernel, linux-kselftest, kuniyu Thank you for your contribution! Sashiko AI review found more severe issue(s) in other patches of this series. This mail is only an FYI, in case you want to address the nit picks below when you respin. Low priority / nit picks for your consideration: - [Low] The new test dump_with_queued_ntf in tools/testing/selftests/net/netlink-dumps.c needs privileges that the… - [Low] dump_with_queued_ntf sets done = true as soon as it sees any NLMSG_DONE with nlmsg_seq == 1. -- selftests: net: netlink-dumps: start a dump with a notification queued This adds a test, dump_with_queued_ntf, to netlink-dumps.c. It starts an RTM_GETLINK dump on a socket with a small receive buffer while a link notification is still queued. The reader peeks before each read, the way libnl does, and the test expects the dump to complete. > diff --git a/tools/testing/selftests/net/netlink-dumps.c b/tools/testing/selftests/net/netlink-dumps.c > index 51129c564d0a6..6f66a478d72f3 100644 > --- a/tools/testing/selftests/net/netlink-dumps.c > +++ b/tools/testing/selftests/net/netlink-dumps.c [ ... ] > +// A dump started while a notification is still queued must make progress > +// for a reader which peeks before reading, like libnl does > +TEST(dump_with_queued_ntf) > +{ > + struct sockaddr_nl addr = { > + .nl_family = AF_NETLINK, > + .nl_groups = RTMGRP_LINK, > + }; > + int netlink_sock, inet_sock, i; > + int rcvbuf = 8192; > + bool done = false; > + char buf[32768]; > + ssize_t n; > + > + ASSERT_EQ(unshare(CLONE_NEWNET), 0); [Severity: Low] Should this skip instead of fail when the test lacks privileges? unshare(CLONE_NEWNET) needs CAP_SYS_ADMIN. lo_set_up() calls ioctl(SIOCSIFFLAGS), which needs CAP_NET_ADMIN. Both results go through ASSERT_EQ, so an unprivileged run reports FAIL rather than SKIP. The other tests in this binary (dump_extack, test_sanity, close_in_progress, close_with_ref) only open netlink sockets, send dump requests and use mq_open/mq_notify. They already run as a normal user, so this is the first test that makes the binary need privileges. Would checking for errno == EPERM and calling SKIP() be enough here? > + > + inet_sock = socket(AF_INET, SOCK_DGRAM, 0); > + ASSERT_GE(inet_sock, 0); [ ... ] > + for (nlh = (struct nlmsghdr *)buf; NLMSG_OK(nlh, n); > + nlh = NLMSG_NEXT(nlh, n)) { > + if (nlh->nlmsg_seq != dump_links.nlhdr.nlmsg_seq) > + continue; > + if (nlh->nlmsg_type == NLMSG_ERROR) > + EXPECT_EQ(*(int *)NLMSG_DATA(nlh), 0); > + if (nlh->nlmsg_type == NLMSG_DONE) > + done = true; [Severity: Low] Can this pass when the dump ends with an error? netlink_dump_done() writes the final dump status into the NLMSG_DONE payload: net/netlink/af_netlink.c:netlink_dump_done() { ... memcpy(nlmsg_data(nlh), &nlk->dump_done_errno, sizeof(nlk->dump_done_errno)); ... } If cb->dump() returns -EMSGSIZE with an empty skb, netlink_dump() keeps the negative value: net/netlink/af_netlink.c:netlink_dump() { ... if (nlk->dump_done_errno == -EMSGSIZE && skb->len) nlk->dump_done_errno = skb->len; ... } The dump then ends with an NLMSG_DONE that carries -EMSGSIZE. Other negative dump errors end the same way. A dump that starts successfully sends no separate NLMSG_ERROR ack. That makes the NLMSG_DONE payload the only place the result is reported. This loop checks the errno only for NLMSG_ERROR, so EXPECT_TRUE(done) still passes. The test also never checks that an RTM_NEWLINK reply for lo arrived. The no-progress case this test targets is still caught, because no DONE arrives there. The gap is a future regression in skb sizing that ends the dump early with an error. The existing nl_get_extack() helper in this file decodes the errno from NLMSG_DONE as well: ea->err = -*(int *)NLMSG_DATA(nlh); Could the new test check the NLMSG_DONE payload in the same way, and also check that at least one RTM_NEWLINK message was received? > + } > + } > + EXPECT_TRUE(done); [ ... ] -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261023-netlink-dump-livelock-v1-0-478214a635d2%40gmail.com ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-27 12:45 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-23 11:26 [PATCH net 0/2] netlink: fix a dump livelock with a notification queued Jhonata Poma-Hansen via B4 Relay 2026-10-23 11:26 ` [PATCH net 1/2] netlink: retry a dump skb that does not fit with the minimum size Jhonata Poma-Hansen via B4 Relay 2026-10-27 11:49 ` netdev-bot+sashiko 2026-10-27 12:44 ` Jhonata Poma-Hansen 2026-10-23 11:26 ` [PATCH net 2/2] selftests: net: netlink-dumps: start a dump with a notification queued Jhonata Poma-Hansen via B4 Relay 2026-10-27 11:49 ` netdev-bot+sashiko
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox