v2: - replaced preempt_{disable|enable}() by local_bh_{disable|enable}() - not in lwtunnel_input() since BHs are already disabled on that path v1: - https://lore.kernel.org/netdev/20250403083956.13946-1-justin.iurman@xxxxxxxxx/ In lwtunnel_{output|xmit}(), dev_xmit_recursion() may be called in preemptible scope for PREEMPT kernels. This patch disables BHs before calling dev_xmit_recursion(). BHs are re-enabled only at the end, since we must ensure the same CPU is used for both dev_xmit_recursion_inc() and dev_xmit_recursion_dec() (and any other recursion levels in some cases) in order to maintain valid per-cpu counters. Reported-by: Alexei Starovoitov <alexei.starovoitov@xxxxxxxxx> Closes: https://lore.kernel.org/netdev/CAADnVQJFWn3dBFJtY+ci6oN1pDFL=TzCmNbRgey7MdYxt_AP2g@xxxxxxxxxxxxxx/ Reported-by: Eduard Zingerman <eddyz87@xxxxxxxxx> Closes: https://lore.kernel.org/netdev/m2h62qwf34.fsf@xxxxxxxxx/ Fixes: 986ffb3a57c5 ("net: lwtunnel: fix recursion loops") Signed-off-by: Justin Iurman <justin.iurman@xxxxxxxxx> --- Cc: bpf <bpf@xxxxxxxxxxxxxxx> Cc: Alexei Starovoitov <alexei.starovoitov@xxxxxxxxx> Cc: Stanislav Fomichev <stfomichev@xxxxxxxxx> Cc: Sebastian Sewior <bigeasy@xxxxxxxxxxxxx> Cc: Andrea Mayer <andrea.mayer@xxxxxxxxxxx> Cc: Stefano Salsano <stefano.salsano@xxxxxxxxxxx> Cc: Paolo Lungaroni <paolo.lungaroni@xxxxxxxxxxx> --- net/core/lwtunnel.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index e39a459540ec..cc1b78616a94 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -333,6 +333,8 @@ int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb) struct dst_entry *dst; int ret; + local_bh_disable(); + if (dev_xmit_recursion()) { net_crit_ratelimited("%s(): recursion limit reached on datapath\n", __func__); @@ -345,11 +347,13 @@ int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb) ret = -EINVAL; goto drop; } - lwtstate = dst->lwtstate; + lwtstate = dst->lwtstate; if (lwtstate->type == LWTUNNEL_ENCAP_NONE || - lwtstate->type > LWTUNNEL_ENCAP_MAX) - return 0; + lwtstate->type > LWTUNNEL_ENCAP_MAX) { + ret = 0; + goto out; + } ret = -EOPNOTSUPP; rcu_read_lock(); @@ -364,11 +368,11 @@ int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb) if (ret == -EOPNOTSUPP) goto drop; - return ret; - + goto out; drop: kfree_skb(skb); - +out: + local_bh_enable(); return ret; } EXPORT_SYMBOL_GPL(lwtunnel_output); @@ -380,6 +384,8 @@ int lwtunnel_xmit(struct sk_buff *skb) struct dst_entry *dst; int ret; + local_bh_disable(); + if (dev_xmit_recursion()) { net_crit_ratelimited("%s(): recursion limit reached on datapath\n", __func__); @@ -394,10 +400,11 @@ int lwtunnel_xmit(struct sk_buff *skb) } lwtstate = dst->lwtstate; - if (lwtstate->type == LWTUNNEL_ENCAP_NONE || - lwtstate->type > LWTUNNEL_ENCAP_MAX) - return 0; + lwtstate->type > LWTUNNEL_ENCAP_MAX) { + ret = 0; + goto out; + } ret = -EOPNOTSUPP; rcu_read_lock(); @@ -412,11 +419,11 @@ int lwtunnel_xmit(struct sk_buff *skb) if (ret == -EOPNOTSUPP) goto drop; - return ret; - + goto out; drop: kfree_skb(skb); - +out: + local_bh_enable(); return ret; } EXPORT_SYMBOL_GPL(lwtunnel_xmit); @@ -428,6 +435,8 @@ int lwtunnel_input(struct sk_buff *skb) struct dst_entry *dst; int ret; + DEBUG_NET_WARN_ON_ONCE(!in_softirq()); + if (dev_xmit_recursion()) { net_crit_ratelimited("%s(): recursion limit reached on datapath\n", __func__); @@ -440,8 +449,8 @@ int lwtunnel_input(struct sk_buff *skb) ret = -EINVAL; goto drop; } - lwtstate = dst->lwtstate; + lwtstate = dst->lwtstate; if (lwtstate->type == LWTUNNEL_ENCAP_NONE || lwtstate->type > LWTUNNEL_ENCAP_MAX) return 0; @@ -460,10 +469,8 @@ int lwtunnel_input(struct sk_buff *skb) goto drop; return ret; - drop: kfree_skb(skb); - return ret; } EXPORT_SYMBOL_GPL(lwtunnel_input); -- 2.34.1