Netdev List
 help / color / mirror / Atom feed
From: Stephen Hemminger <stephen@networkplumber.org>
To: netdev@vger.kernel.org
Cc: dsahern@kernel.org, Stephen Hemminger <stephen@networkplumber.org>
Subject: [PATCH iproute2 2/5] netlink: ignore messages not sent by the kernel
Date: Sun,  8 Nov 2026 17:43:59 -0800	[thread overview]
Message-ID: <20261109014434.115922-3-stephen@networkplumber.org> (raw)
In-Reply-To: <20261109014434.115922-1-stephen@networkplumber.org>

A process with CAP_NET_ADMIN over the namespace can send unicast or
multicast netlink datagrams to iproute2 tools. rtnl_listen() and
mnlu_socket_recv_run() never checked the sender, so monitors print
forged events and exit or overflow on malformed ones. The dump and talk
loops checked nl_pid only after framing checks and after ss -D had
recorded the datagram.

Drop any datagram whose sender is not port id 0 right after recvmsg(),
in rtnl_recvmsg(), rtnl_listen() and a new mnlu_socket_recvfrom().

Reported-by: Anthropic OSS scanner (ANT-2026-ISEFARJF)
Reported-by: Anthropic OSS scanner (ANT-2026-GYQAKSVI)
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 include/mnl_utils.h |  1 +
 lib/libnetlink.c    | 36 ++++++++++++++++++++----------------
 lib/mnl_utils.c     | 35 ++++++++++++++++++++++++++++++++++-
 rdma/stat.c         |  2 +-
 4 files changed, 56 insertions(+), 18 deletions(-)

diff --git a/include/mnl_utils.h b/include/mnl_utils.h
index d809383c..19a49de6 100644
--- a/include/mnl_utils.h
+++ b/include/mnl_utils.h
@@ -28,6 +28,7 @@ int mnl_add_nl_group(struct mnl_socket *nl, unsigned int group);
 uint32_t mnlu_next_seq(void);
 struct nlmsghdr *mnlu_msg_prepare(void *buf, uint32_t nlmsg_type, uint16_t flags,
 				  void *extra_header, size_t extra_header_size);
+ssize_t mnlu_socket_recvfrom(const struct mnl_socket *nl, void *buf, size_t buf_size);
 int mnlu_socket_recv_run(struct mnl_socket *nl, unsigned int seq, void *buf, size_t buf_size,
 			 mnl_cb_t cb, void *data);
 int mnlu_gen_socket_recv_run(struct mnlu_gen_socket *nlg, mnl_cb_t cb,
diff --git a/lib/libnetlink.c b/lib/libnetlink.c
index 6ff9cace..b923ba02 100644
--- a/lib/libnetlink.c
+++ b/lib/libnetlink.c
@@ -843,12 +843,22 @@ static int __rtnl_recvmsg(int fd, struct msghdr *msg, int flags)
 	return len;
 }
 
+/* Netlink datagrams from other processes are discarded */
+static bool rtnl_from_kernel(const struct msghdr *msg)
+{
+	const struct sockaddr_nl *nladdr = msg->msg_name;
+
+	return msg->msg_namelen == sizeof(*nladdr) && nladdr->nl_pid == 0;
+}
+
 static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer)
 {
 	struct iovec *iov = msg->msg_iov;
 	char *buf;
 	int len;
 
+again:
+	msg->msg_namelen = sizeof(struct sockaddr_nl);
 	iov->iov_base = NULL;
 	iov->iov_len = 0;
 
@@ -873,6 +883,11 @@ static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer)
 		return len;
 	}
 
+	if (!rtnl_from_kernel(msg)) {
+		free(buf);
+		goto again;
+	}
+
 	if (answer)
 		*answer = buf;
 	else
@@ -918,8 +933,7 @@ static int rtnl_dump_filter_l(struct rtnl_handle *rth,
 
 				h->nlmsg_flags &= ~a->nc_flags;
 
-				if (nladdr.nl_pid != 0 ||
-				    h->nlmsg_pid != rth->local.nl_pid ||
+				if (h->nlmsg_pid != rth->local.nl_pid ||
 				    h->nlmsg_seq != rth->dump)
 					goto skip_it;
 
@@ -1066,12 +1080,6 @@ next:
 		if (status < 0)
 			return status;
 
-		if (msg.msg_namelen != sizeof(nladdr)) {
-			fprintf(stderr,
-				"sender address length == %d\n",
-				msg.msg_namelen);
-			exit(1);
-		}
 		for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) {
 			int len = h->nlmsg_len;
 			int l = len - sizeof(*h);
@@ -1088,8 +1096,7 @@ next:
 				exit(1);
 			}
 
-			if (nladdr.nl_pid != 0 ||
-			    h->nlmsg_pid != rtnl->local.nl_pid ||
+			if (h->nlmsg_pid != rtnl->local.nl_pid ||
 			    (unsigned int)(seq - h->nlmsg_seq) >= iovlen) {
 				/* Don't forget to skip that message. */
 				status -= NLMSG_ALIGN(len);
@@ -1246,6 +1253,7 @@ int rtnl_listen(struct rtnl_handle *rtnl,
 		}
 
 		iov.iov_len = sizeof(buf);
+		msg.msg_namelen = sizeof(nladdr);
 		status = recvmsg(rtnl->fd, &msg, 0);
 
 		if (status < 0) {
@@ -1261,12 +1269,8 @@ int rtnl_listen(struct rtnl_handle *rtnl,
 			fprintf(stderr, "EOF on netlink\n");
 			return -1;
 		}
-		if (msg.msg_namelen != sizeof(nladdr)) {
-			fprintf(stderr,
-				"Sender address length == %d\n",
-				msg.msg_namelen);
-			exit(1);
-		}
+		if (!rtnl_from_kernel(&msg))
+			continue;
 
 		if (rtnl->flags & RTNL_HANDLE_F_LISTEN_ALL_NSID) {
 			memset(&ctrl, 0, sizeof(ctrl));
diff --git a/lib/mnl_utils.c b/lib/mnl_utils.c
index 44e54aa8..8ddf2b90 100644
--- a/lib/mnl_utils.c
+++ b/lib/mnl_utils.c
@@ -5,6 +5,7 @@
 
 #include <errno.h>
 #include <string.h>
+#include <sys/socket.h>
 #include <libmnl/libmnl.h>
 #include <linux/genetlink.h>
 
@@ -111,6 +112,38 @@ static mnl_cb_t mnlu_cb_array[NLMSG_MIN_TYPE] = {
 	[NLMSG_OVERRUN]	= mnlu_cb_noop,
 };
 
+/*
+ * Like mnl_socket_recvfrom(), but discards datagrams that were
+ * not sent by the kernel.
+ */
+ssize_t mnlu_socket_recvfrom(const struct mnl_socket *nl, void *buf, size_t buf_size)
+{
+	struct sockaddr_nl addr;
+	struct iovec iov = {
+		.iov_base = buf,
+		.iov_len = buf_size,
+	};
+	struct msghdr msg = {
+		.msg_name = &addr,
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
+	};
+	ssize_t ret;
+
+	do {
+		msg.msg_namelen = sizeof(addr);
+		ret = recvmsg(mnl_socket_get_fd(nl), &msg, 0);
+		if (ret < 0)
+			return ret;
+	} while (msg.msg_namelen != sizeof(addr) || addr.nl_pid != 0);
+
+	if (msg.msg_flags & MSG_TRUNC) {
+		errno = ENOSPC;
+		return -1;
+	}
+	return ret;
+}
+
 int mnlu_socket_recv_run(struct mnl_socket *nl, unsigned int seq, void *buf, size_t buf_size,
 			 mnl_cb_t cb, void *data)
 {
@@ -118,7 +151,7 @@ int mnlu_socket_recv_run(struct mnl_socket *nl, unsigned int seq, void *buf, siz
 	int err;
 
 	do {
-		err = mnl_socket_recvfrom(nl, buf, buf_size);
+		err = mnlu_socket_recvfrom(nl, buf, buf_size);
 		if (err <= 0)
 			break;
 		err = mnl_cb_run2(buf, err, seq, portid,
diff --git a/rdma/stat.c b/rdma/stat.c
index f2c2d173..01cc171b 100644
--- a/rdma/stat.c
+++ b/rdma/stat.c
@@ -735,7 +735,7 @@ static int stat_one_qp_unbind(struct rd *rd)
 	 * then rd_recv_msg() always return -1 here
 	 */
 	portid = mnl_socket_get_portid(rd->nl);
-	ret = mnl_socket_recvfrom(rd->nl, buf, sizeof(buf));
+	ret = mnlu_socket_recvfrom(rd->nl, buf, sizeof(buf));
 	if (ret <= 0)
 		return ret;
 
-- 
2.53.0


  parent reply	other threads:[~2026-11-09  1:44 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-09  1:43 [PATCH iproute2 0/5] netlink receive path hardening Stephen Hemminger
2026-11-09  1:43 ` [PATCH iproute2 1/5] netlink: use a counter for sequence numbers Stephen Hemminger
2026-11-09  1:43 ` Stephen Hemminger [this message]
2026-11-09  1:44 ` [PATCH iproute2 3/5] netlink: connect sockets to the kernel Stephen Hemminger
2026-11-09  1:44 ` [PATCH iproute2 4/5] netlink: handle unpadded last message in receive loops Stephen Hemminger
2026-11-09  1:44 ` [PATCH iproute2 5/5] libnetlink: do not read past datagram when recording dump Stephen Hemminger
2026-11-11 17:30 ` [PATCH iproute2 0/5] netlink receive path hardening patchwork-bot+netdevbpf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261109014434.115922-3-stephen@networkplumber.org \
    --to=stephen@networkplumber.org \
    --cc=dsahern@kernel.org \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox