Thread (6 messages) 6 messages, 4 authors, 4d ago

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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help