VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-46099

CVE-2026-46099

Description

In the Linux kernel, the following vulnerability has been resolved:

net: ipv6: fix NOREF dst use in seg6 and rpl lwtunnels

seg6_input_core() and rpl_input() call ip6_route_input() which sets a NOREF dst on the skb, then pass it to dst_cache_set_ip6() invoking dst_hold() unconditionally. On PREEMPT_RT, ksoftirqd is preemptible and a higher-priority task can release the underlying pcpu_rt between the lookup and the caching through a concurrent FIB lookup on a shared nexthop. Simplified race sequence:

ksoftirqd/X higher-prio task (same CPU X) ----------- -------------------------------- seg6_input_core(,skb)/rpl_input(skb) dst_cache_get() -> miss ip6_route_input(skb) -> ip6_pol_route(,skb,flags) [RT6_LOOKUP_F_DST_NOREF in flags] -> FIB lookup resolves fib6_nh [nhid=N route] -> rt6_make_pcpu_route() [creates pcpu_rt, refcount=1] pcpu_rt->sernum = fib6_sernum [fib6_sernum=W] -> cmpxchg(fib6_nh.rt6i_pcpu, NULL, pcpu_rt) [slot was empty, store succeeds] -> skb_dst_set_noref(skb, dst) [dst is pcpu_rt, refcount still 1]

rt_genid_bump_ipv6() -> bumps fib6_sernum [fib6_sernum from W to Z] ip6_route_output() -> ip6_pol_route() -> FIB lookup resolves fib6_nh [nhid=N] -> rt6_get_pcpu_route() pcpu_rt->sernum != fib6_sernum [W <> Z, stale] -> prev = xchg(rt6i_pcpu, NULL) -> dst_release(prev) [prev is pcpu_rt, refcount 1->0, dead]

dst = skb_dst(skb) [dst is the dead pcpu_rt] dst_cache_set_ip6(dst) -> dst_hold() on dead dst -> WARN / use-after-free

For the race to occur, ksoftirqd must be preemptible (PREEMPT_RT without PREEMPT_RT_NEEDS_BH_LOCK) and a concurrent task must be able to release the pcpu_rt. Shared nexthop objects provide such a path, as two routes pointing to the same nhid share the same fib6_nh and its rt6i_pcpu entry.

Fix seg6_input_core() and rpl_input() by calling skb_dst_force() after ip6_route_input() to force the NOREF dst into a refcounted one before caching. The output path is not affected as ip6_route_output() already returns a refcounted dst.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Race condition in seg6_input_core() and rpl_input() in Linux kernel leads to use-after-free due to incorrect NOREF dst caching on PREEMPT_RT.

Vulnerability

In the Linux kernel, the IPv6 segment routing (seg6) and RPL (rpl) lightweight tunnels are vulnerable to a race condition. Functions seg6_input_core() and rpl_input() call ip6_route_input() which sets a NOREF dst on the skb, then pass it to dst_cache_set_ip6() invoking dst_hold() unconditionally. On PREEMPT_RT, where ksoftirqd is preemptible, a higher-priority task can release the underlying pcpu_rt between the lookup and the caching through a concurrent FIB lookup on a shared nexthop. This results in a use-after-free when the freed dst is later held. Affected versions include kernels that implement the seg6 and rpl lwtunnels [1].

Exploitation

An attacker does not require authentication but needs to send crafted IPv6 packets that traverse the vulnerable seg6 or rpl lwtunnels. The race window is tight and exploitation depends on timing: a concurrent FIB lookup on the same CPU (from a higher-priority task) must occur between the route lookup and the dst caching. On standard kernels without PREEMPT_RT, ksoftirqd is not preemptible, so the race is not exploitable. On PREEMPT_RT, an attacker can increase the chance of winning the race by generating high network traffic or triggering FIB updates [1].

Impact

Successful exploitation of the race condition leads to a use-after-free of the pcpu_rt structure. This can result in a kernel crash (denial of service) or potentially arbitrary code execution with kernel privileges, allowing an attacker to escalate privileges or disrupt system operations [1].

Mitigation

The vulnerability is fixed in the Linux kernel by commit b778b6d095421619c331fd2d7751143cd5387103, which is included in stable kernel updates. Users should apply the latest kernel patches from their distribution. If patching is not possible, consider disabling the seg6 and rpl lwtunnels (e.g., by unloading the seg6 and rpl kernel modules) or avoid running PREEMPT_RT kernels on systems where the race could be triggered. No known workaround is available for PREEMPT_RT systems without the fix [1].

AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

10
f9c52a6ba978

net: ipv6: fix NOREF dst use in seg6 and rpl lwtunnels

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitAndrea MayerApr 21, 2026Fixed in 7.1-rc2via kernel-cna
2 files changed · +18 1
  • net/ipv6/rpl_iptunnel.c+9 0 modified
    diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c
    index c7942cf655671c..4e10adcd70e89d 100644
    --- a/net/ipv6/rpl_iptunnel.c
    +++ b/net/ipv6/rpl_iptunnel.c
    @@ -287,7 +287,16 @@ static int rpl_input(struct sk_buff *skb)
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    
  • net/ipv6/seg6_iptunnel.c+9 1 modified
    diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
    index 9b64343ebad686..4c45c0a77d75d0 100644
    --- a/net/ipv6/seg6_iptunnel.c
    +++ b/net/ipv6/seg6_iptunnel.c
    @@ -515,7 +515,16 @@ static int seg6_input_core(struct net *net, struct sock *sk,
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    -- 
    cgit 1.3-korg
    
    
    
b778b6d09542

net: ipv6: fix NOREF dst use in seg6 and rpl lwtunnels

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitAndrea MayerApr 21, 2026Fixed in 6.18.27via kernel-cna
2 files changed · +18 1
  • net/ipv6/rpl_iptunnel.c+9 0 modified
    diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c
    index c7942cf655671c..4e10adcd70e89d 100644
    --- a/net/ipv6/rpl_iptunnel.c
    +++ b/net/ipv6/rpl_iptunnel.c
    @@ -287,7 +287,16 @@ static int rpl_input(struct sk_buff *skb)
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    
  • net/ipv6/seg6_iptunnel.c+9 1 modified
    diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
    index 351a0ed7441cc2..ead677bca49015 100644
    --- a/net/ipv6/seg6_iptunnel.c
    +++ b/net/ipv6/seg6_iptunnel.c
    @@ -500,7 +500,16 @@ static int seg6_input_core(struct net *net, struct sock *sk,
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    -- 
    cgit 1.3-korg
    
    
    
52f9db67f8f3

net: ipv6: fix NOREF dst use in seg6 and rpl lwtunnels

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitAndrea MayerApr 21, 2026Fixed in 6.12.86via kernel-cna
2 files changed · +18 1
  • net/ipv6/rpl_iptunnel.c+9 0 modified
    diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c
    index c7942cf655671c..4e10adcd70e89d 100644
    --- a/net/ipv6/rpl_iptunnel.c
    +++ b/net/ipv6/rpl_iptunnel.c
    @@ -287,7 +287,16 @@ static int rpl_input(struct sk_buff *skb)
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    
  • net/ipv6/seg6_iptunnel.c+9 1 modified
    diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
    index 777069ab605052..fb5a701f8d255d 100644
    --- a/net/ipv6/seg6_iptunnel.c
    +++ b/net/ipv6/seg6_iptunnel.c
    @@ -500,7 +500,16 @@ static int seg6_input_core(struct net *net, struct sock *sk,
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    -- 
    cgit 1.3-korg
    
    
    
9dd5481f960e

net: ipv6: fix NOREF dst use in seg6 and rpl lwtunnels

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitAndrea MayerApr 21, 2026Fixed in 7.0.4via kernel-cna
2 files changed · +18 1
  • net/ipv6/rpl_iptunnel.c+9 0 modified
    diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c
    index c7942cf655671c..4e10adcd70e89d 100644
    --- a/net/ipv6/rpl_iptunnel.c
    +++ b/net/ipv6/rpl_iptunnel.c
    @@ -287,7 +287,16 @@ static int rpl_input(struct sk_buff *skb)
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    
  • net/ipv6/seg6_iptunnel.c+9 1 modified
    diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
    index 351a0ed7441cc2..ead677bca49015 100644
    --- a/net/ipv6/seg6_iptunnel.c
    +++ b/net/ipv6/seg6_iptunnel.c
    @@ -500,7 +500,16 @@ static int seg6_input_core(struct net *net, struct sock *sk,
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    -- 
    cgit 1.3-korg
    
    
    
6bd17925bd68

net: ipv6: fix NOREF dst use in seg6 and rpl lwtunnels

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitAndrea MayerApr 21, 2026Fixed in 6.6.140via kernel-cna
2 files changed · +18 1
  • net/ipv6/rpl_iptunnel.c+9 0 modified
    diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c
    index 523aa8c9b382fe..702509dffd7c2a 100644
    --- a/net/ipv6/rpl_iptunnel.c
    +++ b/net/ipv6/rpl_iptunnel.c
    @@ -282,7 +282,16 @@ static int rpl_input(struct sk_buff *skb)
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    
  • net/ipv6/seg6_iptunnel.c+9 1 modified
    diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
    index 1ae3530dd44bde..6fb1f99c541224 100644
    --- a/net/ipv6/seg6_iptunnel.c
    +++ b/net/ipv6/seg6_iptunnel.c
    @@ -496,7 +496,16 @@ static int seg6_input_core(struct net *net, struct sock *sk,
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    -- 
    cgit 1.3-korg
    
    
    
f9c52a6ba978

net: ipv6: fix NOREF dst use in seg6 and rpl lwtunnels

2 files changed · +18 1
  • net/ipv6/rpl_iptunnel.c+9 0 modified
    diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c
    index c7942cf655671c..4e10adcd70e89d 100644
    --- a/net/ipv6/rpl_iptunnel.c
    +++ b/net/ipv6/rpl_iptunnel.c
    @@ -287,7 +287,16 @@ static int rpl_input(struct sk_buff *skb)
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    
  • net/ipv6/seg6_iptunnel.c+9 1 modified
    diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
    index 9b64343ebad686..4c45c0a77d75d0 100644
    --- a/net/ipv6/seg6_iptunnel.c
    +++ b/net/ipv6/seg6_iptunnel.c
    @@ -515,7 +515,16 @@ static int seg6_input_core(struct net *net, struct sock *sk,
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    -- 
    cgit 1.3-korg
    
    
    
52f9db67f8f3

net: ipv6: fix NOREF dst use in seg6 and rpl lwtunnels

2 files changed · +18 1
  • net/ipv6/rpl_iptunnel.c+9 0 modified
    diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c
    index c7942cf655671c..4e10adcd70e89d 100644
    --- a/net/ipv6/rpl_iptunnel.c
    +++ b/net/ipv6/rpl_iptunnel.c
    @@ -287,7 +287,16 @@ static int rpl_input(struct sk_buff *skb)
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    
  • net/ipv6/seg6_iptunnel.c+9 1 modified
    diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
    index 777069ab605052..fb5a701f8d255d 100644
    --- a/net/ipv6/seg6_iptunnel.c
    +++ b/net/ipv6/seg6_iptunnel.c
    @@ -500,7 +500,16 @@ static int seg6_input_core(struct net *net, struct sock *sk,
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    -- 
    cgit 1.3-korg
    
    
    
6bd17925bd68

net: ipv6: fix NOREF dst use in seg6 and rpl lwtunnels

2 files changed · +18 1
  • net/ipv6/rpl_iptunnel.c+9 0 modified
    diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c
    index 523aa8c9b382fe..702509dffd7c2a 100644
    --- a/net/ipv6/rpl_iptunnel.c
    +++ b/net/ipv6/rpl_iptunnel.c
    @@ -282,7 +282,16 @@ static int rpl_input(struct sk_buff *skb)
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    
  • net/ipv6/seg6_iptunnel.c+9 1 modified
    diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
    index 1ae3530dd44bde..6fb1f99c541224 100644
    --- a/net/ipv6/seg6_iptunnel.c
    +++ b/net/ipv6/seg6_iptunnel.c
    @@ -496,7 +496,16 @@ static int seg6_input_core(struct net *net, struct sock *sk,
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    -- 
    cgit 1.3-korg
    
    
    
9dd5481f960e

net: ipv6: fix NOREF dst use in seg6 and rpl lwtunnels

2 files changed · +18 1
  • net/ipv6/rpl_iptunnel.c+9 0 modified
    diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c
    index c7942cf655671c..4e10adcd70e89d 100644
    --- a/net/ipv6/rpl_iptunnel.c
    +++ b/net/ipv6/rpl_iptunnel.c
    @@ -287,7 +287,16 @@ static int rpl_input(struct sk_buff *skb)
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    
  • net/ipv6/seg6_iptunnel.c+9 1 modified
    diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
    index 351a0ed7441cc2..ead677bca49015 100644
    --- a/net/ipv6/seg6_iptunnel.c
    +++ b/net/ipv6/seg6_iptunnel.c
    @@ -500,7 +500,16 @@ static int seg6_input_core(struct net *net, struct sock *sk,
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    -- 
    cgit 1.3-korg
    
    
    
b778b6d09542

net: ipv6: fix NOREF dst use in seg6 and rpl lwtunnels

2 files changed · +18 1
  • net/ipv6/rpl_iptunnel.c+9 0 modified
    diff --git a/net/ipv6/rpl_iptunnel.c b/net/ipv6/rpl_iptunnel.c
    index c7942cf655671c..4e10adcd70e89d 100644
    --- a/net/ipv6/rpl_iptunnel.c
    +++ b/net/ipv6/rpl_iptunnel.c
    @@ -287,7 +287,16 @@ static int rpl_input(struct sk_buff *skb)
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    
  • net/ipv6/seg6_iptunnel.c+9 1 modified
    diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
    index 351a0ed7441cc2..ead677bca49015 100644
    --- a/net/ipv6/seg6_iptunnel.c
    +++ b/net/ipv6/seg6_iptunnel.c
    @@ -500,7 +500,16 @@ static int seg6_input_core(struct net *net, struct sock *sk,
     
     	if (!dst) {
     		ip6_route_input(skb);
    +
    +		/* ip6_route_input() sets a NOREF dst; force a refcount on it
    +		 * before caching or further use.
    +		 */
    +		skb_dst_force(skb);
     		dst = skb_dst(skb);
    +		if (unlikely(!dst)) {
    +			err = -ENETUNREACH;
    +			goto drop;
    +		}
     
     		/* cache only if we don't create a dst reference loop */
     		if (!dst->error && lwtst != dst->lwtstate) {
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing skb_dst_force() after ip6_route_input() in seg6 and rpl lwtunnel input handlers allows a NOREF dst to be cached and then dst_hold() called on a freed pcpu_rt."

Attack vector

An attacker can trigger a use-after-free by exploiting a race condition on PREEMPT_RT systems where ksoftirqd is preemptible. The attacker sends IPv6 packets that reach the seg6 or rpl lwtunnel input path. A concurrent higher-priority task on the same CPU performs a FIB lookup that shares a nexthop object (nhid=N), causing the pcpu_rt to be released between the NOREF dst lookup and the subsequent `dst_cache_set_ip6()` call that does `dst_hold()` on the now-dead dst [patch_id=2659719].

Affected code

The vulnerable functions are `seg6_input_core()` in `net/ipv6/seg6_iptunnel.c` and `rpl_input()` in `net/ipv6/rpl_iptunnel.c`. Both call `ip6_route_input()` which sets a NOREF dst on the skb, then pass that dst to `dst_cache_set_ip6()` which calls `dst_hold()` unconditionally on a potentially stale reference [patch_id=2659719].

What the fix does

The patch inserts a call to `skb_dst_force(skb)` immediately after `ip6_route_input(skb)` in both `seg6_input_core()` and `rpl_input()`. This converts the NOREF dst into a refcounted one before it is cached or used further. An additional `unlikely(!dst)` check returns `-ENETUNREACH` if the force fails, preventing a null-pointer dereference [patch_id=2659719].

Preconditions

  • configSystem must run PREEMPT_RT (ksoftirqd preemptible)
  • inputConcurrent higher-priority task must perform a FIB lookup on a shared nexthop (nhid=N)
  • networkAttacker must send IPv6 packets that reach the seg6 or rpl lwtunnel input path

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.