skb_do_redirect returns various of values: error code (negative), 0 (success), and some positive status code, e.g. NET_XMIT_CN, NET_RX_DROP. Commit 3a0af8fd61f9 ("bpf: BPF for lightweight tunnel infrastructure") didn't check the return code correctly, so positive values are propagated back along call chain:
ip_finish_output2 -> bpf_xmit -> run_lwt_bpf -> skb_do_redirect
Inside ip_finish_output2, redirected skb will continue to neighbor subsystem as if LWTUNNEL_XMIT_CONTINUE is returned, despite that this skb could have been freed. The bug can trigger use-after-free warning and crashes kernel afterwards:
https://gist.github.com/zhaiyan920/8fbac245b261fe316a7ef04c9b1eba48
Convert positive statuses from skb_do_redirect eliminates this issue.
Fixes: 3a0af8fd61f9 ("bpf: BPF for lightweight tunnel infrastructure") Tested-by: Jakub Sitnicki jakub@cloudflare.com Suggested-by: Markus Elfring Markus.Elfring@web.de Suggested-by: Stanislav Fomichev sdf@google.com Reported-by: Jordan Griege jgriege@cloudflare.com Signed-off-by: Yan Zhai yan@cloudflare.com --- include/linux/netdevice.h | 2 ++ net/core/filter.c | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b828c7a75be2..520d808eec15 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -87,6 +87,8 @@ void netdev_sw_irq_coalesce_default_on(struct net_device *dev); #define NET_RX_SUCCESS 0 /* keep 'em coming, baby */ #define NET_RX_DROP 1 /* packet dropped */
+#define net_rx_errno(e) ((e) == NET_RX_DROP ? -ENOBUFS : (e)) + #define MAX_NEST_DEV 8
/* diff --git a/net/core/filter.c b/net/core/filter.c index 06ba0e56e369..564104543737 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2095,7 +2095,9 @@ static const struct bpf_func_proto bpf_csum_level_proto = {
static inline int __bpf_rx_skb(struct net_device *dev, struct sk_buff *skb) { - return dev_forward_skb_nomtu(dev, skb); + int ret = dev_forward_skb_nomtu(dev, skb); + + return net_rx_errno(ret); }
static inline int __bpf_rx_skb_no_mac(struct net_device *dev, @@ -2108,7 +2110,7 @@ static inline int __bpf_rx_skb_no_mac(struct net_device *dev, ret = netif_rx(skb); }
- return ret; + return net_rx_errno(ret); }
static inline int __bpf_tx_skb(struct net_device *dev, struct sk_buff *skb) @@ -2129,6 +2131,9 @@ static inline int __bpf_tx_skb(struct net_device *dev, struct sk_buff *skb) ret = dev_queue_xmit(skb); dev_xmit_recursion_dec();
+ if (unlikely(ret > 0)) + ret = net_xmit_errno(ret); + return ret; }