Netdev List
 help / color / mirror / Atom feed
* [PATCH net v2] ipv6: rpl: add NULL check for idev in ipv6_rpl_srh_rcv()
@ 2026-05-18 14:06 Andrea Mayer
  2026-05-18 14:18 ` David Ahern
  0 siblings, 1 reply; 7+ messages in thread
From: Andrea Mayer @ 2026-05-18 14:06 UTC (permalink / raw)
  To: netdev
  Cc: linux-kernel, dsahern, idosch, davem, edumazet, kuba, pabeni,
	horms, alex.aring, justin.iurman, bestswngs, stefano.salsano,
	Andrea Mayer, stable

ipv6_rpl_srh_rcv() dereferences idev from __in6_dev_get() without a
NULL check when reading idev->cnf.rpl_seg_enabled.

When the device's MTU drops below IPV6_MIN_MTU, addrconf_ifdown()
clears dev->ip6_ptr through RCU_INIT_POINTER(), which is immediately
visible to concurrent readers. A packet that already passed the idev
check in ip6_rcv_core() can race with this and hit a NULL pointer
dereference.

Reproduced by flooding traffic while rapidly flapping the receiving
interface's MTU between 1500 and 1200:

 BUG: KASAN: null-ptr-deref in ipv6_rpl_srh_rcv+0xae/0x1050
 Read of size 4 at addr 00000000000006b4 by task ping6/386

 CPU: 0 UID: 0 PID: 386 Comm: ping6 Not tainted 7.1.0-rc3 #114 PREEMPT(full)
 Call Trace:
  <IRQ>
  kasan_report+0xc6/0x100
  ipv6_rpl_srh_rcv+0xae/0x1050
  ip6_protocol_deliver_rcu+0x754/0x9a0
  ip6_input_finish+0xa3/0x1b0
  ip6_input+0xdc/0x490
  ipv6_rcv+0x338/0x460
  __netif_receive_skb_one_core+0xd1/0x130
  process_backlog+0x2c7/0x9f0
  __napi_poll.constprop.0+0x51/0x270
  net_rx_action+0x322/0x730
  handle_softirqs+0x119/0x640
  do_softirq+0xae/0xe0
  </IRQ>

Add a NULL check for idev after __in6_dev_get(), dropping the skb
with SKB_DROP_REASON_IPV6DISABLED when the device has no IPv6
configuration.

Fixes: 8610c7c6e3bd ("net: ipv6: add support for rpl sr exthdr")
Cc: stable@vger.kernel.org
Signed-off-by: Andrea Mayer <andrea.mayer@uniroma2.it>
---
v2:
  - use SKB_DROP_REASON_IPV6DISABLED as drop reason (Eric Dumazet)
v1: https://lore.kernel.org/netdev/20260428224816.11223-1-andrea.mayer@uniroma2.it/
---
 net/ipv6/exthdrs.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 03cbce842c1a..a4af6e63349c 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -499,6 +499,10 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
 	u32 r;
 
 	idev = __in6_dev_get(skb->dev);
+	if (!idev) {
+		kfree_skb_reason(skb, SKB_DROP_REASON_IPV6DISABLED);
+		return -1;
+	}
 
 	accept_rpl_seg = min(READ_ONCE(net->ipv6.devconf_all->rpl_seg_enabled),
 			     READ_ONCE(idev->cnf.rpl_seg_enabled));
-- 
2.43.0


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

* Re: [PATCH net v2] ipv6: rpl: add NULL check for idev in ipv6_rpl_srh_rcv()
  2026-05-18 14:06 [PATCH net v2] ipv6: rpl: add NULL check for idev in ipv6_rpl_srh_rcv() Andrea Mayer
@ 2026-05-18 14:18 ` David Ahern
  2026-05-21 18:08   ` Andrea Mayer
  0 siblings, 1 reply; 7+ messages in thread
From: David Ahern @ 2026-05-18 14:18 UTC (permalink / raw)
  To: Andrea Mayer, netdev
  Cc: linux-kernel, idosch, davem, edumazet, kuba, pabeni, horms,
	alex.aring, justin.iurman, bestswngs, stefano.salsano, stable

On 5/18/26 8:06 AM, Andrea Mayer wrote:
> diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
> index 03cbce842c1a..a4af6e63349c 100644
> --- a/net/ipv6/exthdrs.c
> +++ b/net/ipv6/exthdrs.c
> @@ -499,6 +499,10 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
>  	u32 r;
>  
>  	idev = __in6_dev_get(skb->dev);
> +	if (!idev) {
> +		kfree_skb_reason(skb, SKB_DROP_REASON_IPV6DISABLED);
> +		return -1;
> +	}
>  
>  	accept_rpl_seg = min(READ_ONCE(net->ipv6.devconf_all->rpl_seg_enabled),
>  			     READ_ONCE(idev->cnf.rpl_seg_enabled));

ipv6_rpl_srh_rcv and ipv6_srh_rcv are both called by ipv6_rthdr_rcv and
both of these functions check idev. Moving the check to ipv6_rthdr_rcv
which already has an idev lookup would simplifying both paths -- and set
the drop code reason the same.

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

* Re: [PATCH net v2] ipv6: rpl: add NULL check for idev in ipv6_rpl_srh_rcv()
  2026-05-18 14:18 ` David Ahern
@ 2026-05-21 18:08   ` Andrea Mayer
  2026-08-14  5:25     ` Xiang Mei
  0 siblings, 1 reply; 7+ messages in thread
From: Andrea Mayer @ 2026-05-21 18:08 UTC (permalink / raw)
  To: David Ahern
  Cc: netdev, linux-kernel, idosch, davem, edumazet, kuba, pabeni,
	horms, alex.aring, justin.iurman, bestswngs, stefano.salsano,
	stable, Andrea Mayer

On Mon, 18 May 2026 08:18:56 -0600
David Ahern <dsahern@kernel.org> wrote:

> On 5/18/26 8:06 AM, Andrea Mayer wrote:
> > diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
> > index 03cbce842c1a..a4af6e63349c 100644
> > --- a/net/ipv6/exthdrs.c
> > +++ b/net/ipv6/exthdrs.c
> > @@ -499,6 +499,10 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
> >  	u32 r;
> >  
> >  	idev = __in6_dev_get(skb->dev);
> > +	if (!idev) {
> > +		kfree_skb_reason(skb, SKB_DROP_REASON_IPV6DISABLED);
> > +		return -1;
> > +	}
> >  
> >  	accept_rpl_seg = min(READ_ONCE(net->ipv6.devconf_all->rpl_seg_enabled),
> >  			     READ_ONCE(idev->cnf.rpl_seg_enabled));
> 
> ipv6_rpl_srh_rcv and ipv6_srh_rcv are both called by ipv6_rthdr_rcv and
> both of these functions check idev. Moving the check to ipv6_rthdr_rcv
> which already has an idev lookup would simplifying both paths -- and set
> the drop code reason the same.

Hi David,

thanks for the review. I went through the code to plan v3, and I'd like to
share a couple of points before sending it, in case I'm missing something.

ipv6_rthdr_rcv() seems to already tolerate idev == NULL. The per-device
accept_source_route read is wrapped in "if (idev)", and all
__IP6_INC_STATS() calls go through _DEVINC(), which is NULL safe.
The code after the switch:

  [...]
  switch (hdr->type) {
  case IPV6_SRCRT_TYPE_4:
      return ipv6_srh_rcv(skb);
  case IPV6_SRCRT_TYPE_3:
      return ipv6_rpl_srh_rcv(skb);
  default:
      break;
  }
  [...]

falls back to the "default: break;" path, which only uses idev through
those macros. For this reason, I think an "if (!idev) drop" at the top
would change the behavior of the default path. So if we want to put the
check on idev in this function, the check would need to go inside the two
switch cases.

Both the callees in the switch need idev to read the per-device sysctl
(idev->cnf.seg6_enabled, idev->cnf.rpl_seg_enabled). To remove their own
__in6_dev_get() they would have to receive idev from the caller.

Two possible shapes for v3, both pass idev to the callees and remove their
own __in6_dev_get() and NULL check:

(a) Check inside the two switch cases with goto to a "disabled:" label at
    the end of the function (same style as the existing "unknown_rh:"):

      switch (hdr->type) {
      case IPV6_SRCRT_TYPE_4:
          if (!idev)
              goto disabled;
          return ipv6_srh_rcv(skb, idev);
      case IPV6_SRCRT_TYPE_3:
          if (!idev)
              goto disabled;
          return ipv6_rpl_srh_rcv(skb, idev);
      default:
          break;
      }
      [...]
      disabled:
          kfree_skb_reason(skb, SKB_DROP_REASON_IPV6DISABLED);
          return -1;

(b) Single check before the switch, only for the two types that need
    idev:

      if (!idev && (hdr->type == IPV6_SRCRT_TYPE_4 ||
                    hdr->type == IPV6_SRCRT_TYPE_3)) {
          kfree_skb_reason(skb, SKB_DROP_REASON_IPV6DISABLED);
          return -1;
      }
      /* switch unchanged, idev passed to the callees */

Both change the signatures of ipv6_srh_rcv() and ipv6_rpl_srh_rcv().

Any preference, or a different approach in mind?

Thanks,
Andrea

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

* Re: [PATCH net v2] ipv6: rpl: add NULL check for idev in ipv6_rpl_srh_rcv()
  2026-05-21 18:08   ` Andrea Mayer
@ 2026-08-14  5:25     ` Xiang Mei
  2026-08-14  5:31       ` Xiang Mei
  0 siblings, 1 reply; 7+ messages in thread
From: Xiang Mei @ 2026-08-14  5:25 UTC (permalink / raw)
  To: andrea.mayer
  Cc: dsahern, edumazet, netdev, bestswngs, justin.iurman, alex.aring,
	davem, horms, idosch, kuba, pabeni, stefano.salsano, stable,
	linux-kernel

Hi Andrea,

We noticed this is still not fixed in net.  We tested your idea and it
works.  This is the patch we tested:

diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 9c677eb1d1a6..51941ad656a3 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -368,23 +368,16 @@ static void seg6_update_csum(struct sk_buff *skb)
 			   (__be32 *)addr);
 }
 
-static int ipv6_srh_rcv(struct sk_buff *skb)
+static int ipv6_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev)
 {
 	struct inet6_skb_parm *opt = IP6CB(skb);
 	struct net *net = dev_net(skb->dev);
 	struct ipv6_sr_hdr *hdr;
-	struct inet6_dev *idev;
 	struct in6_addr *addr;
 	int accept_seg6;
 
 	hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
 
-	idev = __in6_dev_get(skb->dev);
-	if (!idev) {
-		kfree_skb(skb);
-		return -1;
-	}
-
 	accept_seg6 = min(READ_ONCE(net->ipv6.devconf_all->seg6_enabled),
 			  READ_ONCE(idev->cnf.seg6_enabled));
 
@@ -485,12 +478,11 @@ static int ipv6_srh_rcv(struct sk_buff *skb)
 	return -1;
 }
 
-static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
+static int ipv6_rpl_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev)
 {
 	struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr;
 	struct inet6_skb_parm *opt = IP6CB(skb);
 	struct net *net = dev_net(skb->dev);
-	struct inet6_dev *idev;
 	struct ipv6hdr *oldhdr;
 	unsigned int chdr_len;
 	unsigned char *buf;
@@ -499,8 +491,6 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
 	u64 n = 0;
 	u32 r;
 
-	idev = __in6_dev_get(skb->dev);
-
 	accept_rpl_seg = min(READ_ONCE(net->ipv6.devconf_all->rpl_seg_enabled),
 			     READ_ONCE(idev->cnf.rpl_seg_enabled));
 	if (!accept_rpl_seg) {
@@ -689,10 +679,14 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)
 	switch (hdr->type) {
 	case IPV6_SRCRT_TYPE_4:
 		/* segment routing */
-		return ipv6_srh_rcv(skb);
+		if (!idev)
+			goto disabled;
+		return ipv6_srh_rcv(skb, idev);
 	case IPV6_SRCRT_TYPE_3:
 		/* rpl segment routing */
-		return ipv6_rpl_srh_rcv(skb);
+		if (!idev)
+			goto disabled;
+		return ipv6_rpl_srh_rcv(skb, idev);
 	default:
 		break;
 	}
@@ -837,6 +831,10 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)
 	icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
 			  (&hdr->type) - skb_network_header(skb));
 	return -1;
+
+disabled:
+	kfree_skb_reason(skb, SKB_DROP_REASON_IPV6DISABLED);
+	return -1;
 }
 
 static const struct inet6_protocol rthdr_protocol = {

We don't want to take your credit, so if you have time, could you send
this as v3?  If you don't have time to land it, we are happy to send it
for you.

Thanks,
Xiang

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

* Re: [PATCH net v2] ipv6: rpl: add NULL check for idev in ipv6_rpl_srh_rcv()
  2026-08-14  5:25     ` Xiang Mei
@ 2026-08-14  5:31       ` Xiang Mei
  2026-08-14 13:14         ` Andrea Mayer
  0 siblings, 1 reply; 7+ messages in thread
From: Xiang Mei @ 2026-08-14  5:31 UTC (permalink / raw)
  To: andrea.mayer
  Cc: dsahern, edumazet, netdev, bestswngs, justin.iurman, alex.aring,
	davem, horms, idosch, kuba, pabeni, stefano.salsano, stable,
	linux-kernel

On Thu, Aug 13, 2026 at 10:25 PM Xiang Mei <xmei5@asu.edu> wrote:
>
> Hi Andrea,
>
> We noticed this is still not fixed in net.  We tested your idea and it
> works.  This is the patch we tested:
>
> diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
> index 9c677eb1d1a6..51941ad656a3 100644
> --- a/net/ipv6/exthdrs.c
> +++ b/net/ipv6/exthdrs.c
> @@ -368,23 +368,16 @@ static void seg6_update_csum(struct sk_buff *skb)
>                            (__be32 *)addr);
>  }
>
> -static int ipv6_srh_rcv(struct sk_buff *skb)
> +static int ipv6_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev)
>  {
>         struct inet6_skb_parm *opt = IP6CB(skb);
>         struct net *net = dev_net(skb->dev);
>         struct ipv6_sr_hdr *hdr;
> -       struct inet6_dev *idev;
>         struct in6_addr *addr;
>         int accept_seg6;
>
>         hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
>
> -       idev = __in6_dev_get(skb->dev);
> -       if (!idev) {
> -               kfree_skb(skb);
> -               return -1;
> -       }
> -
>         accept_seg6 = min(READ_ONCE(net->ipv6.devconf_all->seg6_enabled),
>                           READ_ONCE(idev->cnf.seg6_enabled));
>
> @@ -485,12 +478,11 @@ static int ipv6_srh_rcv(struct sk_buff *skb)
>         return -1;
>  }
>
> -static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
> +static int ipv6_rpl_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev)
>  {
>         struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr;
>         struct inet6_skb_parm *opt = IP6CB(skb);
>         struct net *net = dev_net(skb->dev);
> -       struct inet6_dev *idev;
>         struct ipv6hdr *oldhdr;
>         unsigned int chdr_len;
>         unsigned char *buf;
> @@ -499,8 +491,6 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
>         u64 n = 0;
>         u32 r;
>
> -       idev = __in6_dev_get(skb->dev);
> -
>         accept_rpl_seg = min(READ_ONCE(net->ipv6.devconf_all->rpl_seg_enabled),
>                              READ_ONCE(idev->cnf.rpl_seg_enabled));
>         if (!accept_rpl_seg) {
> @@ -689,10 +679,14 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)
>         switch (hdr->type) {
>         case IPV6_SRCRT_TYPE_4:
>                 /* segment routing */
> -               return ipv6_srh_rcv(skb);
> +               if (!idev)
> +                       goto disabled;
> +               return ipv6_srh_rcv(skb, idev);
>         case IPV6_SRCRT_TYPE_3:
>                 /* rpl segment routing */
> -               return ipv6_rpl_srh_rcv(skb);
> +               if (!idev)
> +                       goto disabled;
> +               return ipv6_rpl_srh_rcv(skb, idev);
>         default:
>                 break;
>         }
> @@ -837,6 +831,10 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)
>         icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
>                           (&hdr->type) - skb_network_header(skb));
>         return -1;
> +
> +disabled:
> +       kfree_skb_reason(skb, SKB_DROP_REASON_IPV6DISABLED);
> +       return -1;
>  }
>
>  static const struct inet6_protocol rthdr_protocol = {
>
> We don't want to take your credit, so if you have time, could you send
> this as v3?  If you don't have time to land it, we are happy to send it
> for you.
>
Sorry for my English. I mean, if you agree with this patch, you can
take it (it's based on the proposed version you mentioned).
You don't need to follow our version of imp.

Thanks,
Xiang

> Thanks,
> Xiang

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

* Re: [PATCH net v2] ipv6: rpl: add NULL check for idev in ipv6_rpl_srh_rcv()
  2026-08-14  5:31       ` Xiang Mei
@ 2026-08-14 13:14         ` Andrea Mayer
  2026-08-14 17:02           ` Xiang Mei
  0 siblings, 1 reply; 7+ messages in thread
From: Andrea Mayer @ 2026-08-14 13:14 UTC (permalink / raw)
  To: Xiang Mei
  Cc: dsahern, edumazet, netdev, bestswngs, justin.iurman, alex.aring,
	davem, horms, idosch, kuba, pabeni, stefano.salsano, stable,
	linux-kernel, Andrea Mayer

On Thu, 13 Aug 2026 22:31:18 -0700
Xiang Mei <xmei5@asu.edu> wrote:

>> [snip]
>
> Sorry for my English. I mean, if you agree with this patch, you can
> take it (it's based on the proposed version you mentioned).
> You don't need to follow our version of imp.
>
> Thanks,
> Xiang

Hi Xiang,

thank you for testing this and for the kind offer.

I will go with option (a) from my earlier reply [1].

I will send v3. It is identical to the diff you posted, so what you tested
is what I am sending. If you want a Tested-by on it, please tell me and I
will add it before I post. Otherwise you can reply with it once v3 is on
the list.

[1] https://lore.kernel.org/netdev/20260521200859.816b8923b5f27bba6124461e@uniroma2.it/

Ciao,
Andrea

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

* Re: [PATCH net v2] ipv6: rpl: add NULL check for idev in ipv6_rpl_srh_rcv()
  2026-08-14 13:14         ` Andrea Mayer
@ 2026-08-14 17:02           ` Xiang Mei
  0 siblings, 0 replies; 7+ messages in thread
From: Xiang Mei @ 2026-08-14 17:02 UTC (permalink / raw)
  To: Andrea Mayer
  Cc: dsahern, edumazet, netdev, bestswngs, justin.iurman, alex.aring,
	davem, horms, idosch, kuba, pabeni, stefano.salsano, stable,
	linux-kernel

On Fri, Aug 14, 2026 at 6:15 AM Andrea Mayer <andrea.mayer@uniroma2.it> wrote:
>
> On Thu, 13 Aug 2026 22:31:18 -0700
> Xiang Mei <xmei5@asu.edu> wrote:
>
> >> [snip]
> >
> > Sorry for my English. I mean, if you agree with this patch, you can
> > take it (it's based on the proposed version you mentioned).
> > You don't need to follow our version of imp.
> >
> > Thanks,
> > Xiang
>
> Hi Xiang,
>
> thank you for testing this and for the kind offer.
>
> I will go with option (a) from my earlier reply [1].
>
> I will send v3. It is identical to the diff you posted, so what you tested
> is what I am sending. If you want a Tested-by on it, please tell me and I
> will add it before I post. Otherwise you can reply with it once v3 is on
> the list.
>
Feel free to add my Tested-by to the identical diff.

Thanks,
Xiang

> [1] https://lore.kernel.org/netdev/20260521200859.816b8923b5f27bba6124461e@uniroma2.it/
>
> Ciao,
> Andrea

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

end of thread, other threads:[~2026-08-14 17:02 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-18 14:06 [PATCH net v2] ipv6: rpl: add NULL check for idev in ipv6_rpl_srh_rcv() Andrea Mayer
2026-05-18 14:18 ` David Ahern
2026-05-21 18:08   ` Andrea Mayer
2026-08-14  5:25     ` Xiang Mei
2026-08-14  5:31       ` Xiang Mei
2026-08-14 13:14         ` Andrea Mayer
2026-08-14 17:02           ` Xiang Mei

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