Re: [PATCH net 1/1] ipmr: validate VIF indexes before offload lookup
flat view
From: Naman Gulati <hidden>
Date: 2026-10-16 23:08:07
Subsystem:
networking [general], networking [ipv4/ipv6], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Linus Torvalds
On Tue, Oct 13, 2026 at 8:35 AM Zhiling Zou [off-list ref] wrote:
quoted hunk ↗ jump to hunk
ipmr_find_vif() can return -1 when a packet races with VIF publication. ipmr_forward_offloaded() forms pointers into mrt->vif_table before checking the packet's offload mark, so a negative index is out of bounds. Validate both VIF indexes before forming pointers into vif_table. Invalid indexes fall back to the regular software forwarding path. Fixes: 4eadb88244d1 ("ipmr: do not acquire mrt_lock while calling ip_mr_forward()") Cc: stable@vger.kernel.org Reported-by: Vega <redacted> Assisted-by: LLM Signed-off-by: Zhiling Zou <redacted> --- 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..bc3263d15a44 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c@@ -1879,11 +1879,16 @@ static inline int ipmr_forward_finish(struct net *net, struct sock *sk, static bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt, int in_vifi, int out_vifi) { - struct vif_device *out_vif = &mrt->vif_table[out_vifi]; - struct vif_device *in_vif = &mrt->vif_table[in_vifi]; + struct vif_device *out_vif, *in_vif; if (!skb->offload_l3_fwd_mark) return false; + if (in_vifi < 0 || in_vifi >= MAXVIFS || + out_vifi < 0 || out_vifi >= MAXVIFS) + return false;
It might be cleaner to move this check higher into the stack at
ip_mr_forward() where the ipmr_find_vif lookup is done (and
ip6_mr_forward(), which has the same race but no OOB access since
commit 9b1c21d898fd ("ip6mr: do not acquire mrt_lock while calling
ip6_mr_forward()")).
Before mrt_lock was removed, true_vifi < 0 in ip_mr_forward() always
implied mrt->vif_table[vif].dev != dev, so the packet entered the
wrong-interface branch and was dropped. Without this early check in
ip_mr_forward(), the outgoing-interface loop check (ct != true_vifi)
for (*,G) entries will also fail to prevent hairpining the packet back
out the ingress VIF when true_vifi = -1. Something like this maybe:
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index b9c544d48c45..de869a6ac011 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c@@ -2064,7 +2064,8 @@ static void ip_mr_forward(struct net *net,struct mr_table *mrt,
}
/* Wrong interface: drop packet and (maybe) send PIM assert. */
- if (rcu_access_pointer(mrt->vif_table[vif].dev) != dev) {
+ if (true_vifi < 0 ||
+ rcu_access_pointer(mrt->vif_table[vif].dev) != dev) {
if (rt_is_output_route(skb_rtable(skb))) {
/* It is our own packet, looped back.
* Very complicated situation...diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 9d8116b5edb1..faaf04f5c4f3 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c@@ -2218,7 +2218,8 @@ static void ip6_mr_forward(struct net *net,struct mr_table *mrt,
/*
* Wrong interface: drop packet and (maybe) send PIM assert.
*/
- if (rcu_access_pointer(mrt->vif_table[vif].dev) != dev) {
+ if (true_vifi < 0 ||
+ rcu_access_pointer(mrt->vif_table[vif].dev) != dev) {
atomic_long_inc(&c->_c.mfc_un.res.wrong_if);
if (true_vifi >= 0 && READ_ONCE(mrt->mroute_do_assert) &&
+
+ out_vif = &mrt->vif_table[out_vifi];
+ in_vif = &mrt->vif_table[in_vifi];
if (!out_vif->dev_parent_id.id_len || !in_vif->dev_parent_id.id_len)
return false;
return netdev_phys_item_id_same(&out_vif->dev_parent_id,
--
2.55.0