Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: sr: block BH in seg6_output_core() and seg6_input_core()

As explained in commit 1378817486d6 ("tipc: block BH
before using dst_cache"), net/core/dst_cache.c
helpers need to be called with BH disabled.

Disabling preemption in seg6_output_core() is not good enough,
because seg6_output_core() is called from process context,
lwtunnel_output() only uses rcu_read_lock().

We might be interrupted by a softirq, re-enter seg6_output_core()
and corrupt dst_cache data structures.

Fix the race by using local_bh_disable() instead of
preempt_disable().

Apply a similar change in seg6_input_core().

Fixes: fa79581ea66c ("ipv6: sr: fix several BUGs when preemption is enabled")
Fixes: 6c8702c60b88 ("ipv6: sr: add support for SRH encapsulation and injection with lwtunnels")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: David Lebrun <dlebrun@google.com>
Acked-by: Paolo Abeni <pabeni@redhat.com>
Link: https://lore.kernel.org/r/20240531132636.2637995-4-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Eric Dumazet and committed by
Jakub Kicinski
c0b98ac1 db0090c6

+6 -8
+6 -8
net/ipv6/seg6_iptunnel.c
··· 464 464 465 465 slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); 466 466 467 - preempt_disable(); 467 + local_bh_disable(); 468 468 dst = dst_cache_get(&slwt->cache); 469 - preempt_enable(); 470 469 471 470 if (!dst) { 472 471 ip6_route_input(skb); 473 472 dst = skb_dst(skb); 474 473 if (!dst->error) { 475 - preempt_disable(); 476 474 dst_cache_set_ip6(&slwt->cache, dst, 477 475 &ipv6_hdr(skb)->saddr); 478 - preempt_enable(); 479 476 } 480 477 } else { 481 478 skb_dst_drop(skb); 482 479 skb_dst_set(skb, dst); 483 480 } 481 + local_bh_enable(); 484 482 485 483 err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); 486 484 if (unlikely(err)) ··· 534 536 535 537 slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); 536 538 537 - preempt_disable(); 539 + local_bh_disable(); 538 540 dst = dst_cache_get(&slwt->cache); 539 - preempt_enable(); 541 + local_bh_enable(); 540 542 541 543 if (unlikely(!dst)) { 542 544 struct ipv6hdr *hdr = ipv6_hdr(skb); ··· 556 558 goto drop; 557 559 } 558 560 559 - preempt_disable(); 561 + local_bh_disable(); 560 562 dst_cache_set_ip6(&slwt->cache, dst, &fl6.saddr); 561 - preempt_enable(); 563 + local_bh_enable(); 562 564 } 563 565 564 566 skb_dst_drop(skb);