[PATCH v4 bpf 1/2] bpf: fix skb_do_redirect return values

From: Yan Zhai
Date: Tue Jul 25 2023 - 21:08:33 EST


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@xxxxxxxxxxxxxx>
Suggested-by: Markus Elfring <Markus.Elfring@xxxxxx>
Suggested-by: Stanislav Fomichev <sdf@xxxxxxxxxx>
Reported-by: Jordan Griege <jgriege@xxxxxxxxxxxxxx>
Signed-off-by: Yan Zhai <yan@xxxxxxxxxxxxxx>
---
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;
}

--
2.30.2