Skip to contentSkip to conversation

FAILED: patch "[PATCH] ipmr: only copy pktinfo to cache reports" failed to apply to 6.6-stable tree

PATCH New Sasha Levin 1 participant 3 messages Nov 7 – Nov 11

Subsystems (3)
lore
Share & tools
lore

Sasha Levin

author maintainer
#0 lore raw

The patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.

To reproduce the conflict and resubmit, you may use the following commands:

git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x 94a6736bb5abe6b2ef38490d0cab19c1c6a7c71f
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '[email protected]' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'

Thanks,
Sasha

------------------ original commit in Linus's tree ------------------

From 94a6736bb5abe6b2ef38490d0cab19c1c6a7c71f Mon Sep 17 00:00:00 2001
From: Zhiling Zou <[email protected]>
Date: Tue, 20 Oct 2026 14:39:36 +0800
Subject: [PATCH] ipmr: only copy pktinfo to cache reports

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. Only prepare and copy pktinfo when the input skb has an IPv4 header. Synthetic route-query skbs use NETLINK_CB, not IPCB, so leave their report control block zeroed. Restore pkt->cb after preparing pktinfo for regular IPv4 packets.

---
 net/ipv4/ipmr.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)
net/ipv4/ipmr.c +10 -2
index e4c51ca473019..acacaae05cc49 100644
@@ -1063,6 +1063,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;
@@ -1118,8 +1119,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.
 		 */
@@ -1130,6 +1129,15 @@ static int ipmr_cache_report(const struct mr_table *mrt,
 		skb->transport_header = skb->network_header;
 	}
 
+	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));
+	}
+
 	igmpmsg_netlink_event(mrt, skb);
 
 	/* Deliver to mrouted */
Signature · 1 line
2.53.0
author maintainer
#1 lore raw

PATCH 1/2 ipmr: prevent info-leak in pmr_cache_report()

From: Eric Dumazet <[email protected]>

[ Upstream commit 4f34002e2e37639133693c13a2a9977fab86880d ]

Yiming Qian reported:

<quote>
ipmr_cache_report() allocates a report skb with alloc_skb(128, GFP_ATOMIC) and appends a struct igmphdr using skb_put(). In the non-IGMPMSG_WHOLEPKT` path it initializes only:

- igmp->type
- igmp->code

but does not initialize:

- igmp->csum
- igmp->group

Later, igmpmsg_netlink_event() copies the bytes after sizeof(struct igmpmsg) into the IPMRA_CREPORT_PKT netlink attribute and emits RTM_NEWCACHEREPORT on RTNLGRP_IPV4_MROUTE_R.

As a result, 6 bytes of stale heap data from the skb head are disclosed to userspace.
</quote>

Let's use skb_put_zero() instead of skb_put() to fix this bug.

Stable-dep-of: 94a6736bb5ab ("ipmr: only copy pktinfo to cache reports")

---
 net/ipv4/ipmr.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
net/ipv4/ipmr.c +4 -3
index edd2413edff99..391b0152d6c85 100644
@@ -1087,11 +1087,12 @@ static int ipmr_cache_report(const struct mr_table *mrt,
 		msg->im_vif_hi = vifi >> 8;
 		ipv4_pktinfo_prepare(mroute_sk, pkt, false);
 		memcpy(skb->cb, pkt->cb, sizeof(skb->cb));
-		/* Add our header */
-		igmp = skb_put(skb, sizeof(struct igmphdr));
+		/* Add our header.
+		 * Note that code, csum and group fields are cleared.
+		 */
+		igmp = skb_put_zero(skb, sizeof(struct igmphdr));
 		igmp->type = assert;
 		msg->im_msgtype = assert;
-		igmp->code = 0;
 		ip_hdr(skb)->tot_len = htons(skb->len);	/* Fix the length */
 		skb->transport_header = skb->network_header;
 	}
Signature · 1 line
2.53.0
author maintainer
#2 lore raw

PATCH 2/2 ipmr: only copy pktinfo to cache reports

From: Zhiling Zou <[email protected]>

[ Upstream commit 94a6736bb5abe6b2ef38490d0cab19c1c6a7c71f ]

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. Only prepare and copy pktinfo when the input skb has an IPv4 header. Synthetic route-query skbs use NETLINK_CB, not IPCB, so leave their report control block zeroed. Restore pkt->cb after preparing pktinfo for regular IPv4 packets.

---
 net/ipv4/ipmr.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)
net/ipv4/ipmr.c +10 -2
index 391b0152d6c85..04e7688868fde 100644
@@ -1030,6 +1030,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;
@@ -1085,8 +1086,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.
 		 */
@@ -1097,6 +1096,15 @@ static int ipmr_cache_report(const struct mr_table *mrt,
 		skb->transport_header = skb->network_header;
 	}
 
+	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));
+	}
+
 	igmpmsg_netlink_event(mrt, skb);
 
 	/* Deliver to mrouted */
Signature · 1 line
2.53.0