Skip to contentSkip to conversation

lwt: fix return values of BPF ops

PATCH In review Yan Zhai 2 participants 9 messages Aug 18, 2023

Subsystems (6)
lore
Share & tools
lore

Yan Zhai

author
#0 lore raw

v6 cover lwt: fix return values of BPF ops

lwt xmit hook does not expect positive return values in function ip_finish_output2 and ip6_finish_output. However, BPF programs can directly return positive statuses such like NET_XMIT_DROP, NET_RX_DROP, and etc to the caller. Such return values would make the kernel continue processing already freed skbs and eventually panic.

This set fixes the return values from BPF ops to unexpected continue processing, checks strictly on the correct continue condition for future proof. In addition, add missing selftests for BPF redirect and reroute cases for BPF-CI.

v5: https://lore.kernel.org/bpf/[email protected]/
v4: https://lore.kernel.org/bpf/[email protected]/T/
v3: https://lore.kernel.org/bpf/[email protected]/
v2: https://lore.kernel.org/netdev/[email protected]/
v1: https://lore.kernel.org/bpf/[email protected]/

changes since v5:
* fix BPF-CI failures due to missing config and busybox ping issue

changes since v4:
* fixed same error on BPF_REROUTE path
* re-implemented selftests under BPF-CI requirement

changes since v3:
* minor change in commit message and changelogs
* tested by Jakub Sitnicki

changes since v2:
* subject name changed
* also covered redirect to ingress case
* added selftests

changes since v1:
* minor code style changes

Yan Zhai (4):
lwt: fix return values of BPF xmit ops
lwt: check LWTUNNEL_XMIT_CONTINUE strictly selftests/bpf: add lwt_xmit tests for BPF_REDIRECT selftests/bpf: add lwt_xmit tests for BPF_REROUTE

 include/net/lwtunnel.h                        |   5 +-
 net/core/lwt_bpf.c                            |   7 +-
 net/ipv4/ip_output.c                          |   2 +-
 net/ipv6/ip6_output.c                         |   2 +-
 tools/testing/selftests/bpf/config            |   2 +
 .../selftests/bpf/prog_tests/lwt_helpers.h    | 139 ++++++++
 .../selftests/bpf/prog_tests/lwt_redirect.c   | 330 ++++++++++++++++++
 .../selftests/bpf/prog_tests/lwt_reroute.c    | 262 ++++++++++++++
 .../selftests/bpf/progs/test_lwt_redirect.c   |  90 +++++
 .../selftests/bpf/progs/test_lwt_reroute.c    |  36 ++
 10 files changed, 868 insertions(+), 7 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/lwt_helpers.h
 create mode 100644 tools/testing/selftests/bpf/prog_tests/lwt_redirect.c
 create mode 100644 tools/testing/selftests/bpf/prog_tests/lwt_reroute.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_lwt_redirect.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_lwt_reroute.c
Signature · 1 line
2.30.2
author
#1 lore raw

PATCH v6 1/4 lwt: fix return values of BPF xmit ops

BPF encap ops can return different types of positive values, such like NET_RX_DROP, NET_XMIT_CN, NETDEV_TX_BUSY, and so on, from function skb_do_redirect and bpf_lwt_xmit_reroute. At the xmit hook, such return values would be treated implicitly as LWTUNNEL_XMIT_CONTINUE in ip(6)_finish_output2. When this happens, skbs that have been freed would continue to the neighbor subsystem, causing use-after-free bug and kernel crashes.

To fix the incorrect behavior, skb_do_redirect return values can be simply discarded, the same as tc-egress behavior. On the other hand, bpf_lwt_xmit_reroute returns useful errors to local senders, e.g. PMTU information. Thus convert its return values to avoid the conflict with LWTUNNEL_XMIT_CONTINUE.

Fixes 3a0af8fd61f9 ("bpf: BPF for lightweight tunnel infrastructure")

---
* v5: discards skb_do_redirect return instead; convert

      bpf_lwt_xmit_reroute return;
* v4: minor commit message changes
* v3: converts skb_do_redirect statuses from both ingress and egress
* v2: code style amend
---
 net/core/lwt_bpf.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
net/core/lwt_bpf.c +3 -4
index 8b6b5e72b217..4a0797f0a154 100644
@@ -60,9 +60,8 @@ static int run_lwt_bpf(struct sk_buff *skb, struct bpf_lwt_prog *lwt,
 			ret = BPF_OK;
 		} else {
 			skb_reset_mac_header(skb);
-			ret = skb_do_redirect(skb);
-			if (ret == 0)
-				ret = BPF_REDIRECT;
+			skb_do_redirect(skb);
+			ret = BPF_REDIRECT;
 		}
 		break;
 
@@ -255,7 +254,7 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb)
 
 	err = dst_output(dev_net(skb_dst(skb)->dev), skb->sk, skb);
 	if (unlikely(err))
-		return err;
+		return net_xmit_errno(err);
 
 	/* ip[6]_finish_output2 understand LWTUNNEL_XMIT_DONE */
 	return LWTUNNEL_XMIT_DONE;
Signature · 1 line
2.30.2
author
#2 lore raw

PATCH v6 2/4 lwt: check LWTUNNEL_XMIT_CONTINUE strictly

In reply to Yan Zhai

LWTUNNEL_XMIT_CONTINUE is implicitly assumed in ip(6)_finish_output2, such that any positive return value from a xmit hook could cause unexpected continue behavior, despite that related skb may have been freed. This could be error-prone for future xmit hook ops. One of the possible errors is to return statuses of dst_output directly.

To make the code safer, redefine LWTUNNEL_XMIT_CONTINUE value to distinguish from dst_output statuses and check the continue condition explicitly.

Fixes 3a0af8fd61f9 ("bpf: BPF for lightweight tunnel infrastructure")
---
 include/net/lwtunnel.h | 5 ++++-
 net/ipv4/ip_output.c   | 2 +-
 net/ipv6/ip6_output.c  | 2 +-
 3 files changed, 6 insertions(+), 3 deletions(-)
include/net/lwtunnel.h +4 -1
index 6f15e6fa154e..53bd2d02a4f0 100644
@@ -16,9 +16,12 @@
 #define LWTUNNEL_STATE_INPUT_REDIRECT	BIT(1)
 #define LWTUNNEL_STATE_XMIT_REDIRECT	BIT(2)
 
+/* LWTUNNEL_XMIT_CONTINUE should be distinguishable from dst_output return
+ * values (NET_XMIT_xxx and NETDEV_TX_xxx in linux/netdevice.h) for safety.
+ */
 enum {
 	LWTUNNEL_XMIT_DONE,
-	LWTUNNEL_XMIT_CONTINUE,
+	LWTUNNEL_XMIT_CONTINUE = 0x100,
 };
 
 
net/ipv4/ip_output.c +1 -1
index 6ba1a0fafbaa..a6e4c82615d7 100644
@@ -216,7 +216,7 @@ static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *s
 	if (lwtunnel_xmit_redirect(dst->lwtstate)) {
 		int res = lwtunnel_xmit(skb);
 
-		if (res < 0 || res == LWTUNNEL_XMIT_DONE)
+		if (res != LWTUNNEL_XMIT_CONTINUE)
 			return res;
 	}
 
net/ipv6/ip6_output.c +1 -1
index 1e8c90e97608..016b0a513259 100644
@@ -113,7 +113,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
 	if (lwtunnel_xmit_redirect(dst->lwtstate)) {
 		int res = lwtunnel_xmit(skb);
 
-		if (res < 0 || res == LWTUNNEL_XMIT_DONE)
+		if (res != LWTUNNEL_XMIT_CONTINUE)
 			return res;
 	}
 
Signature · 1 line
2.30.2
author
#3 lore raw

PATCH v6 3/4 selftests/bpf: add lwt_xmit tests for BPF_REDIRECT

In reply to Yan Zhai

There is no lwt_xmit test case for BPF_REDIRECT yet. Add test cases for both normal and abnormal situations. For abnormal test cases, devices are set down or have its carrier set down. Without proper fixes, BPF_REDIRECT to either ingress or egress of such device would panic the kernel.

---
v5->v6: busybox does not support ping mark, redirect via IP address
instead. Add missing kernel test config.

---
 tools/testing/selftests/bpf/config            |   1 +
 .../selftests/bpf/prog_tests/lwt_helpers.h    | 139 ++++++++
 .../selftests/bpf/prog_tests/lwt_redirect.c   | 330 ++++++++++++++++++
 .../selftests/bpf/progs/test_lwt_redirect.c   |  90 +++++
 4 files changed, 560 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/lwt_helpers.h
 create mode 100644 tools/testing/selftests/bpf/prog_tests/lwt_redirect.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_lwt_redirect.c
tools/testing/selftests/bpf/config +1 -0
index 3b350bc31343..fa083f1e5b34 100644
@@ -16,6 +16,7 @@ CONFIG_CRYPTO_USER_API_HASH=y
 CONFIG_DEBUG_INFO=y
 CONFIG_DEBUG_INFO_BTF=y
 CONFIG_DEBUG_INFO_DWARF4=y
+CONFIG_DUMMY=y
 CONFIG_DYNAMIC_FTRACE=y
 CONFIG_FPROBE=y
 CONFIG_FTRACE_SYSCALLS=y
tools/testing/selftests/bpf/prog_tests/lwt_helpers.h new file +139 -0

139 lines not on this page · raw message

tools/testing/selftests/bpf/prog_tests/lwt_redirect.c new file +330 -0

330 lines not on this page · raw message

tools/testing/selftests/bpf/progs/test_lwt_redirect.c new file +90 -0

90 lines not on this page · raw message

Signature · 1 line
2.30.2
author
#4 lore raw

PATCH v6 4/4 selftests/bpf: add lwt_xmit tests for BPF_REROUTE

In reply to Yan Zhai

There is no lwt test case for BPF_REROUTE yet. Add test cases for both normal and abnormal situations. The abnormal situation is set up with an fq qdisc on the reroute target device. Without proper fixes, overflow this qdisc queue limit (to trigger a drop) would panic the kernel.

---
 tools/testing/selftests/bpf/config            |   1 +
 .../selftests/bpf/prog_tests/lwt_reroute.c    | 262 ++++++++++++++++++
 .../selftests/bpf/progs/test_lwt_reroute.c    |  36 +++
 3 files changed, 299 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/lwt_reroute.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_lwt_reroute.c
tools/testing/selftests/bpf/config +1 -0
index fa083f1e5b34..1c7584e8dd9e 100644
@@ -60,6 +60,7 @@ CONFIG_NET_IPGRE=y
 CONFIG_NET_IPGRE_DEMUX=y
 CONFIG_NET_IPIP=y
 CONFIG_NET_MPLS_GSO=y
+CONFIG_NET_SCH_FQ=y
 CONFIG_NET_SCH_INGRESS=y
 CONFIG_NET_SCHED=y
 CONFIG_NETDEVSIM=y
tools/testing/selftests/bpf/prog_tests/lwt_reroute.c new file +262 -0

262 lines not on this page · raw message

tools/testing/selftests/bpf/progs/test_lwt_reroute.c new file +36 -0
new file mode 100644
index 000000000000..1dc64351929c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <inttypes.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_endian.h>
+#include <bpf/bpf_helpers.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+
+/* This function extracts the last byte of the daddr, and uses it
+ * as output dev index.
+ */
+SEC("lwt_xmit")
+int test_lwt_reroute(struct __sk_buff *skb)
+{
+	struct iphdr *iph = NULL;
+	void *start = (void *)(long)skb->data;
+	void *end = (void *)(long)skb->data_end;
+
+	/* set mark at most once */
+	if (skb->mark != 0)
+		return BPF_OK;
+
+	if (start + sizeof(*iph) > end)
+		return BPF_DROP;
+
+	iph = (struct iphdr *)start;
+	skb->mark = bpf_ntohl(iph->daddr) & 0xff;
+
+	/* do not reroute x.x.x.0 packets */
+	if (skb->mark == 0)
+		return BPF_OK;
+
+	return BPF_LWT_REROUTE;
+}
+
+char _license[] SEC("license") = "GPL";
Signature · 1 line
2.30.2
maintainer
#5 lore raw
In reply to Yan Zhai
Quoted 11 lines · Yan Zhai

lwt xmit hook does not expect positive return values in function ip_finish_output2 and ip6_finish_output. However, BPF programs can directly return positive statuses such like NET_XMIT_DROP, NET_RX_DROP, and etc to the caller. Such return values would make the kernel continue processing already freed skbs and eventually panic.

This set fixes the return values from BPF ops to unexpected continue processing, checks strictly on the correct continue condition for future proof. In addition, add missing selftests for BPF redirect and reroute cases for BPF-CI.

Quoted 8 lines

v5: https://lore.kernel.org/bpf/[email protected]/
v4: https://lore.kernel.org/bpf/[email protected]/T/
v3: https://lore.kernel.org/bpf/[email protected]/
v2: https://lore.kernel.org/netdev/[email protected]/
v1: https://lore.kernel.org/bpf/[email protected]/

changes since v5:
* fix BPF-CI failures due to missing config and busybox ping issue

Series looks good, thanks! Given we're fairly close to merge window and this has been broken for quite some time, I took this into bpf-next.

Thanks,
Daniel

author
#6 lore raw
Quoted 19 lines · Daniel Borkmann
Quoted 17 lines · Yan Zhai

lwt xmit hook does not expect positive return values in function ip_finish_output2 and ip6_finish_output. However, BPF programs can directly return positive statuses such like NET_XMIT_DROP, NET_RX_DROP, and etc to the caller. Such return values would make the kernel continue processing already freed skbs and eventually panic.

This set fixes the return values from BPF ops to unexpected continue processing, checks strictly on the correct continue condition for future proof. In addition, add missing selftests for BPF redirect and reroute cases for BPF-CI.

v5: https://lore.kernel.org/bpf/[email protected]/
v4: https://lore.kernel.org/bpf/[email protected]/T/
v3: https://lore.kernel.org/bpf/[email protected]/
v2: https://lore.kernel.org/netdev/[email protected]/
v1: https://lore.kernel.org/bpf/[email protected]/

Quoted 6 lines
Quoted 2 lines

changes since v5:
* fix BPF-CI failures due to missing config and busybox ping issue

Series looks good, thanks! Given we're fairly close to merge window and this has been broken for quite some time, I took this into bpf-next.

Thanks Daniel! Can you also queue this up for stable (or guide how I can do it)?

Yan

Quoted 2 lines

Thanks,
Daniel

maintainer
#7 lore raw
Quoted 20 lines · Yan Zhai
Quoted 19 lines · Daniel Borkmann
Quoted 17 lines · Yan Zhai

lwt xmit hook does not expect positive return values in function ip_finish_output2 and ip6_finish_output. However, BPF programs can directly return positive statuses such like NET_XMIT_DROP, NET_RX_DROP, and etc to the caller. Such return values would make the kernel continue processing already freed skbs and eventually panic.

This set fixes the return values from BPF ops to unexpected continue processing, checks strictly on the correct continue condition for future proof. In addition, add missing selftests for BPF redirect and reroute cases for BPF-CI.

v5: https://lore.kernel.org/bpf/[email protected]/
v4: https://lore.kernel.org/bpf/[email protected]/T/
v3: https://lore.kernel.org/bpf/[email protected]/
v2: https://lore.kernel.org/netdev/[email protected]/
v1: https://lore.kernel.org/bpf/[email protected]/

Quoted 7 lines
Quoted 6 lines
Quoted 2 lines

changes since v5:
* fix BPF-CI failures due to missing config and busybox ping issue

Series looks good, thanks! Given we're fairly close to merge window and this has been broken for quite some time, I took this into bpf-next.

Thanks Daniel! Can you also queue this up for stable (or guide how I can do it)?

Given the Fixes tags, it will be picked up automatically once it lands in Linus' tree.

Thanks,
Daniel

author
#8 lore raw
Quoted 25 lines · Daniel Borkmann
Quoted 23 lines · Yan Zhai
Quoted 22 lines · Daniel Borkmann
Quoted 19 lines · Yan Zhai

lwt xmit hook does not expect positive return values in function ip_finish_output2 and ip6_finish_output. However, BPF programs can directly return positive statuses such like NET_XMIT_DROP, NET_RX_DROP, and etc to the caller. Such return values would make the kernel continue processing already freed skbs and eventually panic.

This set fixes the return values from BPF ops to unexpected continue processing, checks strictly on the correct continue condition for future proof. In addition, add missing selftests for BPF redirect and reroute cases for BPF-CI.

v5: https://lore.kernel.org/bpf/[email protected]/
v4: https://lore.kernel.org/bpf/[email protected]/T/
v3: https://lore.kernel.org/bpf/[email protected]/
v2: https://lore.kernel.org/netdev/[email protected]/
v1: https://lore.kernel.org/bpf/[email protected]/

changes since v5:
* fix BPF-CI failures due to missing config and busybox ping issue

Quoted 8 lines
Quoted 4 lines
Quoted 3 lines

Series looks good, thanks! Given we're fairly close to merge window and this has been broken for quite some time, I took this into bpf-next.

Thanks Daniel! Can you also queue this up for stable (or guide how I can do it)?

Given the Fixes tags, it will be picked up automatically once it lands in Linus' tree.

Wonderful. Thank you!

Quoted 2 lines

Thanks,
Daniel