Netdev List
 help / color / mirror / Atom feed
From: netdev-bot+sashiko@kernel.org
To: sigefriedhyy@gmail.com
Cc: davem@davemloft.net, dsahern@kernel.org, donald.hunter@gmail.com,
	edumazet@google.com, idosch@nvidia.com, kuba@kernel.org,
	kuniyu@google.com, nickgarlis@gmail.com, pabeni@redhat.com,
	sd@queasysnail.net, shuah@kernel.org, horms@kernel.org,
	sdf.kernel@gmail.com, linux-kernel@vger.kernel.org,
	linux-kselftest@vger.kernel.org, netdev@vger.kernel.org
Subject: Re: [PATCH net-next 1/3] rtnetlink: add AF_PACKET multicast dumps
Date: Thu, 10 Sep 2026 03:41:33 +0000	[thread overview]
Message-ID: <178901169340.219967.5720246601361596190@kernel.org> (raw)
In-Reply-To: <20260905093932.43726-2-sigefriedhyy@gmail.com>

Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider:

Critical: 0 · High: 0 · Medium: 2 · Low: 2

- [Medium] rtnl_dump_mcaddr()/rtnl_dump_mcaddr_dev()
  (net/core/rtnetlink.c) resume a truncated dump purely positionally —…
- [Medium] rtnl_fill_mcaddr() (net/core/rtnetlink.c) copies dev->addr_len
  bytes out of ha->addr[MAX_ADDR_LEN] into the IFA_MULTICAST attribute:…
- [Low] Claimed uAPI/YAML divergence: Documentation/netlink/specs/rt-
  addr.yaml documents op `getmulticast` as 'Get / dump IPv4/IPv6…
- [Low] Claimed spec/implementation mismatch: rt-addr.yaml lists
  `cacheinfo` in the getmulticast reply attribute set (mcaddr-attrs) and…

--

rtnetlink: add AF_PACKET multicast dumps

This adds a PF_PACKET RTM_GETMULTICAST dump handler that walks dev->mc for
every device in the netns and reports each netdev_hw_addr in the existing
ifaddrmsg format with IFA_MULTICAST and IFA_MC_USERS.  The dump runs under
RCU plus netif_addr_lock_bh() and is registered with
RTNL_FLAG_DUMP_UNLOCKED.

A few questions below.

> diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
> index 81c5a6104dea1..5e83232c15048 100644
> --- a/net/core/rtnetlink.c
> +++ b/net/core/rtnetlink.c
> @@ -4566,6 +4566,136 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
>  	return skb->len ? : ret;
>  }
>  
> +static int rtnl_fill_mcaddr(struct sk_buff *skb, const struct net_device *dev,
> +			    const struct netdev_hw_addr *ha, u32 portid,
> +			    u32 seq, unsigned int flags)
> +{

[ ... ]

> +	if (nla_put(skb, IFA_MULTICAST, dev->addr_len, ha->addr) ||
> +	    nla_put_u32(skb, IFA_MC_USERS, ha->refcount)) {

[Severity: Medium]

Can this nla_put() copy uninitialized slab bytes into IFA_MULTICAST when
dev->addr_len grew after the entry was created?

netdev_hw_addr entries are allocated with plain kmalloc() and only
addr_len bytes are initialized, and the length used at creation time is
not recorded anywhere:

net/core/dev_addr_lists.c:__hw_addr_create() {
	alloc_size = sizeof(*ha);
	if (alloc_size < L1_CACHE_BYTES)
		alloc_size = L1_CACHE_BYTES;
	ha = kmalloc(alloc_size, GFP_ATOMIC);
	if (!ha)
		return NULL;
	memcpy(ha->addr, addr, addr_len);
	...
}

__dev_mc_add() passes dev->addr_len as it is at add time:

net/core/dev_addr_lists.c:__dev_mc_add() {
	err = __hw_addr_add_ex(&dev->mc, addr, dev->addr_len,
			       NETDEV_HW_ADDR_T_MULTICAST, global, false,
			       0, false);
}

while rtnl_fill_mcaddr() emits the current dev->addr_len.  One sequence
that produces a mismatch:

  1. create a tap device (ARPHRD_ETHER, addr_len 6)
  2. SIOCADDMULTI via dev_ifsioc() -> dev_mc_add_global(), which only
     requires netif_device_present(), so the device may be down; 6 bytes
     of ha->addr are initialized
  3. TUNSETLINK with ARPHRD_TUNNEL6 in drivers/net/tun.c:

	tun->dev->type = (int) arg;
	tun->dev->addr_len = tun_get_addr_len(tun->dev->type);

     which raises addr_len to 16 and does not purge dev->mc
  4. run the new RTM_GETMULTICAST/AF_PACKET dump

Would step 4 then emit 16 bytes out of a 6-byte-initialized ha->addr,
handing 10 bytes of never-written slab memory to user space?  The read
stays inside ha->addr[MAX_ADDR_LEN], so this is a disclosure of
uninitialized memory rather than an out-of-bounds access.

The same bytes are already visible through /proc/net/dev_mcast, which
prints "%*phN" with dev->addr_len, so the underlying unzeroed tail is
older than this patch, but this adds a second path to it.  Would it make
sense to either bound the emitted length or zero the address tail in
__hw_addr_create()?

> +		nlmsg_cancel(skb, nlh);
> +		return -EMSGSIZE;
> +	}
> +
> +	nlmsg_end(skb, nlh);
> +	return 0;
> +}
> +
> +static int rtnl_dump_mcaddr_dev(struct net_device *dev, struct sk_buff *skb,
> +				struct netlink_callback *cb, int *s_addr_idx,
> +				unsigned int flags)
> +{
> +	struct netdev_hw_addr *ha;
> +	int addr_idx = 0;
> +	int err = 0;
> +
> +	netif_addr_lock_bh(dev);
> +	netdev_for_each_mc_addr(ha, dev) {
> +		if (addr_idx < *s_addr_idx) {
> +			addr_idx++;
> +			continue;
> +		}
> +		err = rtnl_fill_mcaddr(skb, dev, ha, NETLINK_CB(cb->skb).portid,
> +				       cb->nlh->nlmsg_seq, flags);
> +		if (err < 0)
> +			break;
> +		addr_idx++;
> +	}
> +	netif_addr_unlock_bh(dev);
> +
> +	*s_addr_idx = err < 0 ? addr_idx : 0;

[Severity: Medium]

Should this dump also maintain a generation counter and call
nl_dump_check_consistent() so interrupted dumps get NLM_F_DUMP_INTR?

Resume state here is purely positional (ctx->ifindex plus ctx->addr_idx)
and neither RTNL nor netif_addr_lock is held between two invocations of
the callback.  netlink_dump() turns the -EMSGSIZE into a continued dump:

net/netlink/af_netlink.c:netlink_dump() {
	if (nlk->dump_done_errno == -EMSGSIZE && skb->len)
		nlk->dump_done_errno = skb->len;
}

so entries added to or removed from dev->mc in that window shift the
index and entries can be silently repeated or skipped.

cb->seq is never assigned in rtnl_dump_mcaddr(), so the check in
nl_dump_check_consistent():

	if (cb->prev_seq && cb->seq != cb->prev_seq)
		nlh->nlmsg_flags |= NLM_F_DUMP_INTR;

can never fire and user space has no way to notice the loss.  The AF_INET
sibling of the same message type does both halves: inet_dump_addr() sets

	cb->seq = inet_base_seq(tgt_net);

and in_dev_dump_ifmcaddr() calls nl_dump_check_consistent(cb,
nlmsg_hdr(skb)) for every emitted message.

> +
> +	return err;
> +}

[ ... ]

> +static int rtnl_dump_mcaddr(struct sk_buff *skb, struct netlink_callback *cb)
> +{

[ ... ]

> +	for_each_netdev_dump(net, dev, ctx->ifindex) {
> +		err = rtnl_dump_mcaddr_dev(dev, skb, cb, &ctx->addr_idx,
> +					   flags);
> +		if (err < 0)
> +			break;
> +	}

[Severity: Medium]

Related to the above, can a stale ctx->addr_idx be applied to the wrong
device here?

On -EMSGSIZE, rtnl_dump_mcaddr_dev() keeps the offset in ctx->addr_idx and
the break skips the ifindex++ of for_each_netdev_dump(), so ctx->ifindex
still names the interrupted device.  If that device is unregistered before
the dump resumes:

include/linux/netdevice.h:
	#define for_each_netdev_dump(net, d, ifindex)				\
		for (; (d = xa_find(&(net)->dev_by_index, &ifindex,		\
				    ULONG_MAX, XA_PRESENT)); ifindex++)

xa_find() with XA_PRESENT returns the next device that does exist, and the
non-zero ctx->addr_idx is passed unchanged into rtnl_dump_mcaddr_dev(), so

		if (addr_idx < *s_addr_idx) {
			addr_idx++;
			continue;
		}

would skip the first ctx->addr_idx multicast entries of an unrelated
device.  Should ctx->addr_idx be reset when the resumed ifindex does not
match the recorded one?

> +out:
> +	rcu_read_unlock();
> +	return err;
> +}
> +

[ ... ]

> @@ -7251,6 +7381,8 @@ static const struct rtnl_msg_handler rtnetlink_rtnl_msg_handlers[] __initconst =
>  	{.msgtype = RTM_SETSTATS, .doit = rtnl_stats_set},
>  	{.msgtype = RTM_NEWLINKPROP, .doit = rtnl_newlinkprop},
>  	{.msgtype = RTM_DELLINKPROP, .doit = rtnl_dellinkprop},
> +	{.protocol = PF_PACKET, .msgtype = RTM_GETMULTICAST,
> +	 .dumpit = rtnl_dump_mcaddr, .flags = RTNL_FLAG_DUMP_UNLOCKED},

[Severity: Low]

This isn't a bug, but as of this commit alone the getmulticast op in
Documentation/netlink/specs/rt-addr.yaml still reads "Get / dump
IPv4/IPv6 multicast addresses." while the same op now also serves
AF_PACKET requests with link-layer address semantics.

The follow-up patch in the series, "netlink: specs: rt-addr: document
AF_PACKET multicast dumps", rewrites that doc to cover ifa-family
selecting AF_INET, AF_INET6 or AF_PACKET and the permanent-flag meaning,
so the spec and the implementation do agree at the end of the series.
Only noting it in case the two patches are ever applied separately.

>  	{.protocol = PF_BRIDGE, .msgtype = RTM_GETLINK,
>  	 .dumpit = rtnl_bridge_getlink},
>  	{.protocol = PF_BRIDGE, .msgtype = RTM_DELLINK,

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260905093932.43726-1-sigefriedhyy%40gmail.com

  parent reply	other threads:[~2026-09-10  3:41 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-05  9:39 [PATCH net-next 0/3] rtnetlink: dump link-layer multicast addresses Yuyang Huang
2026-09-05  9:39 ` [PATCH net-next 1/3] rtnetlink: add AF_PACKET multicast dumps Yuyang Huang
2026-09-08 15:56   ` David Ahern
2026-09-09  1:15     ` Yuyang Huang
2026-09-10  3:41   ` netdev-bot+sashiko [this message]
2026-09-10  4:04     ` Yuyang Huang
2026-09-05  9:39 ` [PATCH net-next 2/3] netlink: specs: rt-addr: document " Yuyang Huang
2026-09-05  9:39 ` [PATCH net-next 3/3] selftests: net: test " Yuyang Huang
2026-09-10  3:41   ` netdev-bot+sashiko
2026-09-10  4:05     ` Yuyang Huang
2026-09-07 12:51 ` [PATCH net-next 0/3] rtnetlink: dump link-layer multicast addresses Nicolas Dichtel
2026-09-08  2:56   ` Yuyang Huang
2026-09-08  9:23     ` Nicolas Dichtel

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=178901169340.219967.5720246601361596190@kernel.org \
    --to=netdev-bot+sashiko@kernel.org \
    --cc=davem@davemloft.net \
    --cc=donald.hunter@gmail.com \
    --cc=dsahern@kernel.org \
    --cc=edumazet@google.com \
    --cc=horms@kernel.org \
    --cc=idosch@nvidia.com \
    --cc=kuba@kernel.org \
    --cc=kuniyu@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=nickgarlis@gmail.com \
    --cc=pabeni@redhat.com \
    --cc=sd@queasysnail.net \
    --cc=sdf.kernel@gmail.com \
    --cc=shuah@kernel.org \
    --cc=sigefriedhyy@gmail.com \
    /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