* [PATCH bpf] bpf: fix classic bpf reads from negative offset outside of linear skb portion
@ 2025-01-22 20:04 Maciej Żenczykowski
2025-01-22 20:16 ` Maciej Żenczykowski
2025-01-23 22:36 ` Daniel Borkmann
0 siblings, 2 replies; 7+ messages in thread
From: Maciej Żenczykowski @ 2025-01-22 20:04 UTC (permalink / raw)
To: Maciej Żenczykowski, Alexei Starovoitov, Daniel Borkmann
Cc: Linux Network Development Mailing List, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Linux Kernel Mailing List, BPF Mailing List,
Maciej Żenczykowski, Stanislav Fomichev, Willem de Bruijn,
Matt Moeller
We're received reports of cBPF code failing to accept DHCP packets.
"BPF filter for DHCP not working (android14-6.1-lts + android-14.0.0_r74)"
The relevant Android code is at:
https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/NetworkStack/jni/network_stack_utils_jni.cpp;l=95;drc=9df50aef1fd163215dcba759045706253a5624f5
which uses a lot of macros from:
https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/bpf/headers/include/bpf/BpfClassic.h;drc=c58cfb7c7da257010346bd2d6dcca1c0acdc8321
This is widely used and does work on the vast majority of drivers,
but is exposing a core kernel cBPF bug related to driver skb layout.
Root cause is iwlwifi driver, specifically on (at least):
Dell 7212: Intel Dual Band Wireless AC 8265
Dell 7220: Intel Wireless AC 9560
Dell 7230: Intel Wi-Fi 6E AX211
delivers frames where the UDP destination port is not in the skb linear
portion, while the cBPF code is using SKF_NET_OFF relative addressing.
simplified from above, effectively:
BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, SKF_NET_OFF)
BPF_STMT(BPF_LD | BPF_H | BPF_IND, SKF_NET_OFF + 2)
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 68, 1, 0)
BPF_STMT(BPF_RET | BPF_K, 0)
BPF_STMT(BPF_RET | BPF_K, 0xFFFFFFFF)
fails to match udp dport=68 packets.
Specifically the 3rd cBPF instruction fails to match the condition:
if (ptr >= skb->head && ptr + size <= skb_tail_pointer(skb))
within bpf_internal_load_pointer_neg_helper() and thus returns NULL,
which results in reading -EFAULT.
This is because bpf_skb_load_helper_{8,16,32} don't include the
"data past headlen do skb_copy_bits()" logic from the non-negative
offset branch in the negative offset branch.
Note: I don't know sparc assembly, so this doesn't fix sparc...
ideally we should just delete bpf_internal_load_pointer_neg_helper()
This seems to have always been broken (but not pre-git era, since
obviously there was no eBPF helpers back then), but stuff older
than 5.4 is no longer LTS supported anyway, so using 5.4 as fixes tag.
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Stanislav Fomichev <sdf@fomichev.me>
Cc: Willem de Bruijn <willemb@google.com>
Reported-by: Matt Moeller <moeller.matt@gmail.com>
Closes: https://issuetracker.google.com/384636719 [Treble - GKI partner internal]
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Fixes: 219d54332a09 ("Linux 5.4")
---
include/linux/filter.h | 2 ++
kernel/bpf/core.c | 14 +++++++++
net/core/filter.c | 69 +++++++++++++++++-------------------------
3 files changed, 43 insertions(+), 42 deletions(-)
diff --git a/include/linux/filter.h b/include/linux/filter.h
index a3ea46281595..c24d8e338ce4 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -1479,6 +1479,8 @@ static inline u16 bpf_anc_helper(const struct sock_filter *ftest)
void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb,
int k, unsigned int size);
+int bpf_internal_neg_helper(const struct sk_buff *skb, int k);
+
static inline int bpf_tell_extensions(void)
{
return SKF_AD_MAX;
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index da729cbbaeb9..994988dabb97 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -89,6 +89,20 @@ void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, int k, uns
return NULL;
}
+int bpf_internal_neg_helper(const struct sk_buff *skb, int k)
+{
+ if (k >= 0)
+ return k;
+ if (k >= SKF_NET_OFF)
+ return skb->network_header + k - SKF_NET_OFF;
+ if (k >= SKF_LL_OFF) {
+ if (unlikely(!skb_mac_header_was_set(skb)))
+ return -1;
+ return skb->mac_header + k - SKF_LL_OFF;
+ }
+ return -1;
+}
+
/* tell bpf programs that include vmlinux.h kernel's PAGE_SIZE */
enum page_size_enum {
__PAGE_SIZE = PAGE_SIZE
diff --git a/net/core/filter.c b/net/core/filter.c
index e56a0be31678..609ef7df71ce 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -221,21 +221,16 @@ BPF_CALL_3(bpf_skb_get_nlattr_nest, struct sk_buff *, skb, u32, a, u32, x)
BPF_CALL_4(bpf_skb_load_helper_8, const struct sk_buff *, skb, const void *,
data, int, headlen, int, offset)
{
- u8 tmp, *ptr;
- const int len = sizeof(tmp);
-
- if (offset >= 0) {
- if (headlen - offset >= len)
- return *(u8 *)(data + offset);
- if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp)))
- return tmp;
- } else {
- ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len);
- if (likely(ptr))
- return *(u8 *)ptr;
- }
+ u8 tmp;
- return -EFAULT;
+ offset = bpf_internal_neg_helper(skb, offset);
+ if (unlikely(offset < 0))
+ return -EFAULT;
+ if (headlen - offset >= sizeof(u8))
+ return *(u8 *)(data + offset);
+ if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp)))
+ return -EFAULT;
+ return tmp;
}
BPF_CALL_2(bpf_skb_load_helper_8_no_cache, const struct sk_buff *, skb,
@@ -248,21 +243,16 @@ BPF_CALL_2(bpf_skb_load_helper_8_no_cache, const struct sk_buff *, skb,
BPF_CALL_4(bpf_skb_load_helper_16, const struct sk_buff *, skb, const void *,
data, int, headlen, int, offset)
{
- __be16 tmp, *ptr;
- const int len = sizeof(tmp);
+ __be16 tmp;
- if (offset >= 0) {
- if (headlen - offset >= len)
- return get_unaligned_be16(data + offset);
- if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp)))
- return be16_to_cpu(tmp);
- } else {
- ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len);
- if (likely(ptr))
- return get_unaligned_be16(ptr);
- }
-
- return -EFAULT;
+ offset = bpf_internal_neg_helper(skb, offset);
+ if (unlikely(offset < 0))
+ return -EFAULT;
+ if (headlen - offset >= sizeof(__be16))
+ return get_unaligned_be16(data + offset);
+ if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp)))
+ return -EFAULT;
+ return be16_to_cpu(tmp);
}
BPF_CALL_2(bpf_skb_load_helper_16_no_cache, const struct sk_buff *, skb,
@@ -275,21 +265,16 @@ BPF_CALL_2(bpf_skb_load_helper_16_no_cache, const struct sk_buff *, skb,
BPF_CALL_4(bpf_skb_load_helper_32, const struct sk_buff *, skb, const void *,
data, int, headlen, int, offset)
{
- __be32 tmp, *ptr;
- const int len = sizeof(tmp);
-
- if (likely(offset >= 0)) {
- if (headlen - offset >= len)
- return get_unaligned_be32(data + offset);
- if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp)))
- return be32_to_cpu(tmp);
- } else {
- ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len);
- if (likely(ptr))
- return get_unaligned_be32(ptr);
- }
+ __be32 tmp;
- return -EFAULT;
+ offset = bpf_internal_neg_helper(skb, offset);
+ if (unlikely(offset < 0))
+ return -EFAULT;
+ if (headlen - offset >= sizeof(__be32))
+ return get_unaligned_be32(data + offset);
+ if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp)))
+ return -EFAULT;
+ return be32_to_cpu(tmp);
}
BPF_CALL_2(bpf_skb_load_helper_32_no_cache, const struct sk_buff *, skb,
--
2.48.1.262.g85cc9f2d1e-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH bpf] bpf: fix classic bpf reads from negative offset outside of linear skb portion 2025-01-22 20:04 [PATCH bpf] bpf: fix classic bpf reads from negative offset outside of linear skb portion Maciej Żenczykowski @ 2025-01-22 20:16 ` Maciej Żenczykowski 2025-01-22 20:28 ` Maciej Żenczykowski 2025-01-23 22:36 ` Daniel Borkmann 1 sibling, 1 reply; 7+ messages in thread From: Maciej Żenczykowski @ 2025-01-22 20:16 UTC (permalink / raw) To: Maciej Żenczykowski, Alexei Starovoitov, Daniel Borkmann Cc: Linux Network Development Mailing List, David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linux Kernel Mailing List, BPF Mailing List, Stanislav Fomichev, Willem de Bruijn, Matt Moeller On Wed, Jan 22, 2025 at 12:04 PM Maciej Żenczykowski <maze@google.com> wrote: > > We're received reports of cBPF code failing to accept DHCP packets. > "BPF filter for DHCP not working (android14-6.1-lts + android-14.0.0_r74)" > > The relevant Android code is at: > https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/NetworkStack/jni/network_stack_utils_jni.cpp;l=95;drc=9df50aef1fd163215dcba759045706253a5624f5 > which uses a lot of macros from: > https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/bpf/headers/include/bpf/BpfClassic.h;drc=c58cfb7c7da257010346bd2d6dcca1c0acdc8321 > > This is widely used and does work on the vast majority of drivers, > but is exposing a core kernel cBPF bug related to driver skb layout. > > Root cause is iwlwifi driver, specifically on (at least): > Dell 7212: Intel Dual Band Wireless AC 8265 > Dell 7220: Intel Wireless AC 9560 > Dell 7230: Intel Wi-Fi 6E AX211 > delivers frames where the UDP destination port is not in the skb linear > portion, while the cBPF code is using SKF_NET_OFF relative addressing. > > simplified from above, effectively: > BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, SKF_NET_OFF) > BPF_STMT(BPF_LD | BPF_H | BPF_IND, SKF_NET_OFF + 2) > BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 68, 1, 0) > BPF_STMT(BPF_RET | BPF_K, 0) > BPF_STMT(BPF_RET | BPF_K, 0xFFFFFFFF) > fails to match udp dport=68 packets. > > Specifically the 3rd cBPF instruction fails to match the condition: 2nd of course > if (ptr >= skb->head && ptr + size <= skb_tail_pointer(skb)) > within bpf_internal_load_pointer_neg_helper() and thus returns NULL, > which results in reading -EFAULT. > > This is because bpf_skb_load_helper_{8,16,32} don't include the > "data past headlen do skb_copy_bits()" logic from the non-negative > offset branch in the negative offset branch. > > Note: I don't know sparc assembly, so this doesn't fix sparc... > ideally we should just delete bpf_internal_load_pointer_neg_helper() > This seems to have always been broken (but not pre-git era, since > obviously there was no eBPF helpers back then), but stuff older > than 5.4 is no longer LTS supported anyway, so using 5.4 as fixes tag. > > Cc: Alexei Starovoitov <ast@kernel.org> > Cc: Daniel Borkmann <daniel@iogearbox.net> > Cc: Stanislav Fomichev <sdf@fomichev.me> > Cc: Willem de Bruijn <willemb@google.com> > Reported-by: Matt Moeller <moeller.matt@gmail.com> > Closes: https://issuetracker.google.com/384636719 [Treble - GKI partner internal] > Signed-off-by: Maciej Żenczykowski <maze@google.com> > Fixes: 219d54332a09 ("Linux 5.4") > --- > include/linux/filter.h | 2 ++ > kernel/bpf/core.c | 14 +++++++++ > net/core/filter.c | 69 +++++++++++++++++------------------------- > 3 files changed, 43 insertions(+), 42 deletions(-) > > diff --git a/include/linux/filter.h b/include/linux/filter.h > index a3ea46281595..c24d8e338ce4 100644 > --- a/include/linux/filter.h > +++ b/include/linux/filter.h > @@ -1479,6 +1479,8 @@ static inline u16 bpf_anc_helper(const struct sock_filter *ftest) > void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, > int k, unsigned int size); > > +int bpf_internal_neg_helper(const struct sk_buff *skb, int k); > + > static inline int bpf_tell_extensions(void) > { > return SKF_AD_MAX; > diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c > index da729cbbaeb9..994988dabb97 100644 > --- a/kernel/bpf/core.c > +++ b/kernel/bpf/core.c > @@ -89,6 +89,20 @@ void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, int k, uns > return NULL; > } > > +int bpf_internal_neg_helper(const struct sk_buff *skb, int k) > +{ > + if (k >= 0) > + return k; > + if (k >= SKF_NET_OFF) > + return skb->network_header + k - SKF_NET_OFF; > + if (k >= SKF_LL_OFF) { > + if (unlikely(!skb_mac_header_was_set(skb))) > + return -1; > + return skb->mac_header + k - SKF_LL_OFF; > + } > + return -1; > +} > + > /* tell bpf programs that include vmlinux.h kernel's PAGE_SIZE */ > enum page_size_enum { > __PAGE_SIZE = PAGE_SIZE > diff --git a/net/core/filter.c b/net/core/filter.c > index e56a0be31678..609ef7df71ce 100644 > --- a/net/core/filter.c > +++ b/net/core/filter.c > @@ -221,21 +221,16 @@ BPF_CALL_3(bpf_skb_get_nlattr_nest, struct sk_buff *, skb, u32, a, u32, x) > BPF_CALL_4(bpf_skb_load_helper_8, const struct sk_buff *, skb, const void *, > data, int, headlen, int, offset) > { > - u8 tmp, *ptr; > - const int len = sizeof(tmp); > - > - if (offset >= 0) { > - if (headlen - offset >= len) > - return *(u8 *)(data + offset); > - if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > - return tmp; > - } else { > - ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); > - if (likely(ptr)) > - return *(u8 *)ptr; > - } > + u8 tmp; > > - return -EFAULT; > + offset = bpf_internal_neg_helper(skb, offset); > + if (unlikely(offset < 0)) > + return -EFAULT; > + if (headlen - offset >= sizeof(u8)) > + return *(u8 *)(data + offset); > + if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > + return -EFAULT; > + return tmp; > } > > BPF_CALL_2(bpf_skb_load_helper_8_no_cache, const struct sk_buff *, skb, > @@ -248,21 +243,16 @@ BPF_CALL_2(bpf_skb_load_helper_8_no_cache, const struct sk_buff *, skb, > BPF_CALL_4(bpf_skb_load_helper_16, const struct sk_buff *, skb, const void *, > data, int, headlen, int, offset) > { > - __be16 tmp, *ptr; > - const int len = sizeof(tmp); > + __be16 tmp; > > - if (offset >= 0) { > - if (headlen - offset >= len) > - return get_unaligned_be16(data + offset); > - if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > - return be16_to_cpu(tmp); > - } else { > - ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); > - if (likely(ptr)) > - return get_unaligned_be16(ptr); > - } > - > - return -EFAULT; > + offset = bpf_internal_neg_helper(skb, offset); > + if (unlikely(offset < 0)) > + return -EFAULT; > + if (headlen - offset >= sizeof(__be16)) > + return get_unaligned_be16(data + offset); > + if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > + return -EFAULT; > + return be16_to_cpu(tmp); > } > > BPF_CALL_2(bpf_skb_load_helper_16_no_cache, const struct sk_buff *, skb, > @@ -275,21 +265,16 @@ BPF_CALL_2(bpf_skb_load_helper_16_no_cache, const struct sk_buff *, skb, > BPF_CALL_4(bpf_skb_load_helper_32, const struct sk_buff *, skb, const void *, > data, int, headlen, int, offset) > { > - __be32 tmp, *ptr; > - const int len = sizeof(tmp); > - > - if (likely(offset >= 0)) { > - if (headlen - offset >= len) > - return get_unaligned_be32(data + offset); > - if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > - return be32_to_cpu(tmp); > - } else { > - ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); > - if (likely(ptr)) > - return get_unaligned_be32(ptr); > - } > + __be32 tmp; > > - return -EFAULT; > + offset = bpf_internal_neg_helper(skb, offset); > + if (unlikely(offset < 0)) > + return -EFAULT; > + if (headlen - offset >= sizeof(__be32)) > + return get_unaligned_be32(data + offset); > + if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > + return -EFAULT; > + return be32_to_cpu(tmp); > } > > BPF_CALL_2(bpf_skb_load_helper_32_no_cache, const struct sk_buff *, skb, > -- > 2.48.1.262.g85cc9f2d1e-goog > Note: this is currently only compile and boot tested. Which doesn't prove all that much ;-) - Maciej ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf] bpf: fix classic bpf reads from negative offset outside of linear skb portion 2025-01-22 20:16 ` Maciej Żenczykowski @ 2025-01-22 20:28 ` Maciej Żenczykowski 2025-01-22 20:58 ` Maciej Żenczykowski 0 siblings, 1 reply; 7+ messages in thread From: Maciej Żenczykowski @ 2025-01-22 20:28 UTC (permalink / raw) To: Maciej Żenczykowski, Alexei Starovoitov, Daniel Borkmann Cc: Linux Network Development Mailing List, David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linux Kernel Mailing List, BPF Mailing List, Stanislav Fomichev, Willem de Bruijn, Matt Moeller On Wed, Jan 22, 2025 at 12:16 PM Maciej Żenczykowski <zenczykowski@gmail.com> wrote: > > On Wed, Jan 22, 2025 at 12:04 PM Maciej Żenczykowski <maze@google.com> wrote: > > > > We're received reports of cBPF code failing to accept DHCP packets. > > "BPF filter for DHCP not working (android14-6.1-lts + android-14.0.0_r74)" > > > > The relevant Android code is at: > > https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/NetworkStack/jni/network_stack_utils_jni.cpp;l=95;drc=9df50aef1fd163215dcba759045706253a5624f5 > > which uses a lot of macros from: > > https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/bpf/headers/include/bpf/BpfClassic.h;drc=c58cfb7c7da257010346bd2d6dcca1c0acdc8321 > > > > This is widely used and does work on the vast majority of drivers, > > but is exposing a core kernel cBPF bug related to driver skb layout. > > > > Root cause is iwlwifi driver, specifically on (at least): > > Dell 7212: Intel Dual Band Wireless AC 8265 > > Dell 7220: Intel Wireless AC 9560 > > Dell 7230: Intel Wi-Fi 6E AX211 > > delivers frames where the UDP destination port is not in the skb linear > > portion, while the cBPF code is using SKF_NET_OFF relative addressing. > > > > simplified from above, effectively: > > BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, SKF_NET_OFF) > > BPF_STMT(BPF_LD | BPF_H | BPF_IND, SKF_NET_OFF + 2) > > BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 68, 1, 0) > > BPF_STMT(BPF_RET | BPF_K, 0) > > BPF_STMT(BPF_RET | BPF_K, 0xFFFFFFFF) > > fails to match udp dport=68 packets. > > > > Specifically the 3rd cBPF instruction fails to match the condition: > > 2nd of course > > > if (ptr >= skb->head && ptr + size <= skb_tail_pointer(skb)) > > within bpf_internal_load_pointer_neg_helper() and thus returns NULL, > > which results in reading -EFAULT. > > > > This is because bpf_skb_load_helper_{8,16,32} don't include the > > "data past headlen do skb_copy_bits()" logic from the non-negative > > offset branch in the negative offset branch. > > > > Note: I don't know sparc assembly, so this doesn't fix sparc... > > ideally we should just delete bpf_internal_load_pointer_neg_helper() > > This seems to have always been broken (but not pre-git era, since > > obviously there was no eBPF helpers back then), but stuff older > > than 5.4 is no longer LTS supported anyway, so using 5.4 as fixes tag. > > > > Cc: Alexei Starovoitov <ast@kernel.org> > > Cc: Daniel Borkmann <daniel@iogearbox.net> > > Cc: Stanislav Fomichev <sdf@fomichev.me> > > Cc: Willem de Bruijn <willemb@google.com> > > Reported-by: Matt Moeller <moeller.matt@gmail.com> > > Closes: https://issuetracker.google.com/384636719 [Treble - GKI partner internal] > > Signed-off-by: Maciej Żenczykowski <maze@google.com> > > Fixes: 219d54332a09 ("Linux 5.4") > > --- > > include/linux/filter.h | 2 ++ > > kernel/bpf/core.c | 14 +++++++++ > > net/core/filter.c | 69 +++++++++++++++++------------------------- > > 3 files changed, 43 insertions(+), 42 deletions(-) > > > > diff --git a/include/linux/filter.h b/include/linux/filter.h > > index a3ea46281595..c24d8e338ce4 100644 > > --- a/include/linux/filter.h > > +++ b/include/linux/filter.h > > @@ -1479,6 +1479,8 @@ static inline u16 bpf_anc_helper(const struct sock_filter *ftest) > > void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, > > int k, unsigned int size); > > > > +int bpf_internal_neg_helper(const struct sk_buff *skb, int k); > > + > > static inline int bpf_tell_extensions(void) > > { > > return SKF_AD_MAX; > > diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c > > index da729cbbaeb9..994988dabb97 100644 > > --- a/kernel/bpf/core.c > > +++ b/kernel/bpf/core.c > > @@ -89,6 +89,20 @@ void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, int k, uns > > return NULL; > > } > > > > +int bpf_internal_neg_helper(const struct sk_buff *skb, int k) > > +{ > > + if (k >= 0) > > + return k; > > + if (k >= SKF_NET_OFF) > > + return skb->network_header + k - SKF_NET_OFF; > > + if (k >= SKF_LL_OFF) { > > + if (unlikely(!skb_mac_header_was_set(skb))) > > + return -1; > > + return skb->mac_header + k - SKF_LL_OFF; > > + } > > + return -1; > > +} > > + > > /* tell bpf programs that include vmlinux.h kernel's PAGE_SIZE */ > > enum page_size_enum { > > __PAGE_SIZE = PAGE_SIZE > > diff --git a/net/core/filter.c b/net/core/filter.c > > index e56a0be31678..609ef7df71ce 100644 > > --- a/net/core/filter.c > > +++ b/net/core/filter.c > > @@ -221,21 +221,16 @@ BPF_CALL_3(bpf_skb_get_nlattr_nest, struct sk_buff *, skb, u32, a, u32, x) > > BPF_CALL_4(bpf_skb_load_helper_8, const struct sk_buff *, skb, const void *, > > data, int, headlen, int, offset) > > { > > - u8 tmp, *ptr; > > - const int len = sizeof(tmp); > > - > > - if (offset >= 0) { > > - if (headlen - offset >= len) > > - return *(u8 *)(data + offset); > > - if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > - return tmp; > > - } else { > > - ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); > > - if (likely(ptr)) > > - return *(u8 *)ptr; > > - } > > + u8 tmp; > > > > - return -EFAULT; > > + offset = bpf_internal_neg_helper(skb, offset); > > + if (unlikely(offset < 0)) > > + return -EFAULT; > > + if (headlen - offset >= sizeof(u8)) > > + return *(u8 *)(data + offset); > > + if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > + return -EFAULT; > > + return tmp; > > } > > > > BPF_CALL_2(bpf_skb_load_helper_8_no_cache, const struct sk_buff *, skb, > > @@ -248,21 +243,16 @@ BPF_CALL_2(bpf_skb_load_helper_8_no_cache, const struct sk_buff *, skb, > > BPF_CALL_4(bpf_skb_load_helper_16, const struct sk_buff *, skb, const void *, > > data, int, headlen, int, offset) > > { > > - __be16 tmp, *ptr; > > - const int len = sizeof(tmp); > > + __be16 tmp; > > > > - if (offset >= 0) { > > - if (headlen - offset >= len) > > - return get_unaligned_be16(data + offset); > > - if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > - return be16_to_cpu(tmp); > > - } else { > > - ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); > > - if (likely(ptr)) > > - return get_unaligned_be16(ptr); > > - } > > - > > - return -EFAULT; > > + offset = bpf_internal_neg_helper(skb, offset); > > + if (unlikely(offset < 0)) > > + return -EFAULT; > > + if (headlen - offset >= sizeof(__be16)) > > + return get_unaligned_be16(data + offset); > > + if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > + return -EFAULT; > > + return be16_to_cpu(tmp); > > } > > > > BPF_CALL_2(bpf_skb_load_helper_16_no_cache, const struct sk_buff *, skb, > > @@ -275,21 +265,16 @@ BPF_CALL_2(bpf_skb_load_helper_16_no_cache, const struct sk_buff *, skb, > > BPF_CALL_4(bpf_skb_load_helper_32, const struct sk_buff *, skb, const void *, > > data, int, headlen, int, offset) > > { > > - __be32 tmp, *ptr; > > - const int len = sizeof(tmp); > > - > > - if (likely(offset >= 0)) { > > - if (headlen - offset >= len) > > - return get_unaligned_be32(data + offset); > > - if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > - return be32_to_cpu(tmp); > > - } else { > > - ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); > > - if (likely(ptr)) > > - return get_unaligned_be32(ptr); > > - } > > + __be32 tmp; > > > > - return -EFAULT; > > + offset = bpf_internal_neg_helper(skb, offset); > > + if (unlikely(offset < 0)) > > + return -EFAULT; > > + if (headlen - offset >= sizeof(__be32)) > > + return get_unaligned_be32(data + offset); > > + if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > + return -EFAULT; > > + return be32_to_cpu(tmp); > > } > > > > BPF_CALL_2(bpf_skb_load_helper_32_no_cache, const struct sk_buff *, skb, > > -- > > 2.48.1.262.g85cc9f2d1e-goog > > > > Note: this is currently only compile and boot tested. > Which doesn't prove all that much ;-) Furthermore even after cherrypicking this (or a similar style fix) into older LTS: - sparc jit is (presumably, maybe only 32-bit) still broken, as the assembly uses the old function - same for mips jit on 5.4/5.10/5.15 - same for powerpc jit on 5.4/5.10 (but I would guess we don't care) ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf] bpf: fix classic bpf reads from negative offset outside of linear skb portion 2025-01-22 20:28 ` Maciej Żenczykowski @ 2025-01-22 20:58 ` Maciej Żenczykowski 2025-01-23 20:37 ` Stanislav Fomichev 0 siblings, 1 reply; 7+ messages in thread From: Maciej Żenczykowski @ 2025-01-22 20:58 UTC (permalink / raw) To: Maciej Żenczykowski, Alexei Starovoitov, Daniel Borkmann Cc: Linux Network Development Mailing List, David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linux Kernel Mailing List, BPF Mailing List, Stanislav Fomichev, Willem de Bruijn, Matt Moeller On Wed, Jan 22, 2025 at 12:28 PM Maciej Żenczykowski <zenczykowski@gmail.com> wrote: > > On Wed, Jan 22, 2025 at 12:16 PM Maciej Żenczykowski > <zenczykowski@gmail.com> wrote: > > > > On Wed, Jan 22, 2025 at 12:04 PM Maciej Żenczykowski <maze@google.com> wrote: > > > > > > We're received reports of cBPF code failing to accept DHCP packets. > > > "BPF filter for DHCP not working (android14-6.1-lts + android-14.0.0_r74)" > > > > > > The relevant Android code is at: > > > https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/NetworkStack/jni/network_stack_utils_jni.cpp;l=95;drc=9df50aef1fd163215dcba759045706253a5624f5 > > > which uses a lot of macros from: > > > https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/bpf/headers/include/bpf/BpfClassic.h;drc=c58cfb7c7da257010346bd2d6dcca1c0acdc8321 > > > > > > This is widely used and does work on the vast majority of drivers, > > > but is exposing a core kernel cBPF bug related to driver skb layout. > > > > > > Root cause is iwlwifi driver, specifically on (at least): > > > Dell 7212: Intel Dual Band Wireless AC 8265 > > > Dell 7220: Intel Wireless AC 9560 > > > Dell 7230: Intel Wi-Fi 6E AX211 > > > delivers frames where the UDP destination port is not in the skb linear > > > portion, while the cBPF code is using SKF_NET_OFF relative addressing. > > > > > > simplified from above, effectively: > > > BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, SKF_NET_OFF) > > > BPF_STMT(BPF_LD | BPF_H | BPF_IND, SKF_NET_OFF + 2) > > > BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 68, 1, 0) > > > BPF_STMT(BPF_RET | BPF_K, 0) > > > BPF_STMT(BPF_RET | BPF_K, 0xFFFFFFFF) > > > fails to match udp dport=68 packets. > > > > > > Specifically the 3rd cBPF instruction fails to match the condition: > > > > 2nd of course > > > > > if (ptr >= skb->head && ptr + size <= skb_tail_pointer(skb)) > > > within bpf_internal_load_pointer_neg_helper() and thus returns NULL, > > > which results in reading -EFAULT. > > > > > > This is because bpf_skb_load_helper_{8,16,32} don't include the > > > "data past headlen do skb_copy_bits()" logic from the non-negative > > > offset branch in the negative offset branch. > > > > > > Note: I don't know sparc assembly, so this doesn't fix sparc... > > > ideally we should just delete bpf_internal_load_pointer_neg_helper() > > > This seems to have always been broken (but not pre-git era, since > > > obviously there was no eBPF helpers back then), but stuff older > > > than 5.4 is no longer LTS supported anyway, so using 5.4 as fixes tag. > > > > > > Cc: Alexei Starovoitov <ast@kernel.org> > > > Cc: Daniel Borkmann <daniel@iogearbox.net> > > > Cc: Stanislav Fomichev <sdf@fomichev.me> > > > Cc: Willem de Bruijn <willemb@google.com> > > > Reported-by: Matt Moeller <moeller.matt@gmail.com> > > > Closes: https://issuetracker.google.com/384636719 [Treble - GKI partner internal] > > > Signed-off-by: Maciej Żenczykowski <maze@google.com> > > > Fixes: 219d54332a09 ("Linux 5.4") > > > --- > > > include/linux/filter.h | 2 ++ > > > kernel/bpf/core.c | 14 +++++++++ > > > net/core/filter.c | 69 +++++++++++++++++------------------------- > > > 3 files changed, 43 insertions(+), 42 deletions(-) > > > > > > diff --git a/include/linux/filter.h b/include/linux/filter.h > > > index a3ea46281595..c24d8e338ce4 100644 > > > --- a/include/linux/filter.h > > > +++ b/include/linux/filter.h > > > @@ -1479,6 +1479,8 @@ static inline u16 bpf_anc_helper(const struct sock_filter *ftest) > > > void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, > > > int k, unsigned int size); > > > > > > +int bpf_internal_neg_helper(const struct sk_buff *skb, int k); > > > + > > > static inline int bpf_tell_extensions(void) > > > { > > > return SKF_AD_MAX; > > > diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c > > > index da729cbbaeb9..994988dabb97 100644 > > > --- a/kernel/bpf/core.c > > > +++ b/kernel/bpf/core.c > > > @@ -89,6 +89,20 @@ void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, int k, uns > > > return NULL; > > > } > > > > > > +int bpf_internal_neg_helper(const struct sk_buff *skb, int k) > > > +{ > > > + if (k >= 0) > > > + return k; > > > + if (k >= SKF_NET_OFF) > > > + return skb->network_header + k - SKF_NET_OFF; > > > + if (k >= SKF_LL_OFF) { > > > + if (unlikely(!skb_mac_header_was_set(skb))) > > > + return -1; > > > + return skb->mac_header + k - SKF_LL_OFF; > > > + } > > > + return -1; > > > +} > > > + > > > /* tell bpf programs that include vmlinux.h kernel's PAGE_SIZE */ > > > enum page_size_enum { > > > __PAGE_SIZE = PAGE_SIZE > > > diff --git a/net/core/filter.c b/net/core/filter.c > > > index e56a0be31678..609ef7df71ce 100644 > > > --- a/net/core/filter.c > > > +++ b/net/core/filter.c > > > @@ -221,21 +221,16 @@ BPF_CALL_3(bpf_skb_get_nlattr_nest, struct sk_buff *, skb, u32, a, u32, x) > > > BPF_CALL_4(bpf_skb_load_helper_8, const struct sk_buff *, skb, const void *, > > > data, int, headlen, int, offset) > > > { > > > - u8 tmp, *ptr; > > > - const int len = sizeof(tmp); > > > - > > > - if (offset >= 0) { > > > - if (headlen - offset >= len) > > > - return *(u8 *)(data + offset); > > > - if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > > - return tmp; > > > - } else { > > > - ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); > > > - if (likely(ptr)) > > > - return *(u8 *)ptr; > > > - } > > > + u8 tmp; > > > > > > - return -EFAULT; > > > + offset = bpf_internal_neg_helper(skb, offset); > > > + if (unlikely(offset < 0)) > > > + return -EFAULT; > > > + if (headlen - offset >= sizeof(u8)) > > > + return *(u8 *)(data + offset); > > > + if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > > + return -EFAULT; > > > + return tmp; > > > } > > > > > > BPF_CALL_2(bpf_skb_load_helper_8_no_cache, const struct sk_buff *, skb, > > > @@ -248,21 +243,16 @@ BPF_CALL_2(bpf_skb_load_helper_8_no_cache, const struct sk_buff *, skb, > > > BPF_CALL_4(bpf_skb_load_helper_16, const struct sk_buff *, skb, const void *, > > > data, int, headlen, int, offset) > > > { > > > - __be16 tmp, *ptr; > > > - const int len = sizeof(tmp); > > > + __be16 tmp; > > > > > > - if (offset >= 0) { > > > - if (headlen - offset >= len) > > > - return get_unaligned_be16(data + offset); > > > - if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > > - return be16_to_cpu(tmp); > > > - } else { > > > - ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); > > > - if (likely(ptr)) > > > - return get_unaligned_be16(ptr); > > > - } > > > - > > > - return -EFAULT; > > > + offset = bpf_internal_neg_helper(skb, offset); > > > + if (unlikely(offset < 0)) > > > + return -EFAULT; > > > + if (headlen - offset >= sizeof(__be16)) > > > + return get_unaligned_be16(data + offset); > > > + if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > > + return -EFAULT; > > > + return be16_to_cpu(tmp); > > > } > > > > > > BPF_CALL_2(bpf_skb_load_helper_16_no_cache, const struct sk_buff *, skb, > > > @@ -275,21 +265,16 @@ BPF_CALL_2(bpf_skb_load_helper_16_no_cache, const struct sk_buff *, skb, > > > BPF_CALL_4(bpf_skb_load_helper_32, const struct sk_buff *, skb, const void *, > > > data, int, headlen, int, offset) > > > { > > > - __be32 tmp, *ptr; > > > - const int len = sizeof(tmp); > > > - > > > - if (likely(offset >= 0)) { > > > - if (headlen - offset >= len) > > > - return get_unaligned_be32(data + offset); > > > - if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > > - return be32_to_cpu(tmp); > > > - } else { > > > - ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); > > > - if (likely(ptr)) > > > - return get_unaligned_be32(ptr); > > > - } > > > + __be32 tmp; > > > > > > - return -EFAULT; > > > + offset = bpf_internal_neg_helper(skb, offset); > > > + if (unlikely(offset < 0)) > > > + return -EFAULT; > > > + if (headlen - offset >= sizeof(__be32)) > > > + return get_unaligned_be32(data + offset); > > > + if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > > + return -EFAULT; > > > + return be32_to_cpu(tmp); > > > } > > > > > > BPF_CALL_2(bpf_skb_load_helper_32_no_cache, const struct sk_buff *, skb, > > > -- > > > 2.48.1.262.g85cc9f2d1e-goog > > > > > > > Note: this is currently only compile and boot tested. > > Which doesn't prove all that much ;-) > > Furthermore even after cherrypicking this (or a similar style fix) > into older LTS: > - sparc jit is (presumably, maybe only 32-bit) still broken, as the > assembly uses the old function > - same for mips jit on 5.4/5.10/5.15 > - same for powerpc jit on 5.4/5.10 > > (but I would guess we don't care) and Willem points out that: "skb->network_header is an offset against head, while the get_unaligned_be16 and skb_copy_bits take an offset relative to data. I did check yesterday that skb_copy_bits can take negative offsets, in case a protocol header (e.g., SKF_LL_OFF) is smaller than skb->data." which makes me think this approach is possibly (likely?) incorrect? skb offsets always leave me confused... ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf] bpf: fix classic bpf reads from negative offset outside of linear skb portion 2025-01-22 20:58 ` Maciej Żenczykowski @ 2025-01-23 20:37 ` Stanislav Fomichev 0 siblings, 0 replies; 7+ messages in thread From: Stanislav Fomichev @ 2025-01-23 20:37 UTC (permalink / raw) To: Maciej Żenczykowski Cc: Alexei Starovoitov, Daniel Borkmann, Linux Network Development Mailing List, David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linux Kernel Mailing List, BPF Mailing List, Stanislav Fomichev, Willem de Bruijn, Matt Moeller On 01/22, Maciej Żenczykowski wrote: > On Wed, Jan 22, 2025 at 12:28 PM Maciej Żenczykowski > <zenczykowski@gmail.com> wrote: > > > > On Wed, Jan 22, 2025 at 12:16 PM Maciej Żenczykowski > > <zenczykowski@gmail.com> wrote: > > > > > > On Wed, Jan 22, 2025 at 12:04 PM Maciej Żenczykowski <maze@google.com> wrote: > > > > > > > > We're received reports of cBPF code failing to accept DHCP packets. > > > > "BPF filter for DHCP not working (android14-6.1-lts + android-14.0.0_r74)" > > > > > > > > The relevant Android code is at: > > > > https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/NetworkStack/jni/network_stack_utils_jni.cpp;l=95;drc=9df50aef1fd163215dcba759045706253a5624f5 > > > > which uses a lot of macros from: > > > > https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/bpf/headers/include/bpf/BpfClassic.h;drc=c58cfb7c7da257010346bd2d6dcca1c0acdc8321 > > > > > > > > This is widely used and does work on the vast majority of drivers, > > > > but is exposing a core kernel cBPF bug related to driver skb layout. > > > > > > > > Root cause is iwlwifi driver, specifically on (at least): > > > > Dell 7212: Intel Dual Band Wireless AC 8265 > > > > Dell 7220: Intel Wireless AC 9560 > > > > Dell 7230: Intel Wi-Fi 6E AX211 > > > > delivers frames where the UDP destination port is not in the skb linear > > > > portion, while the cBPF code is using SKF_NET_OFF relative addressing. > > > > > > > > simplified from above, effectively: > > > > BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, SKF_NET_OFF) > > > > BPF_STMT(BPF_LD | BPF_H | BPF_IND, SKF_NET_OFF + 2) > > > > BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 68, 1, 0) > > > > BPF_STMT(BPF_RET | BPF_K, 0) > > > > BPF_STMT(BPF_RET | BPF_K, 0xFFFFFFFF) > > > > fails to match udp dport=68 packets. > > > > > > > > Specifically the 3rd cBPF instruction fails to match the condition: > > > > > > 2nd of course > > > > > > > if (ptr >= skb->head && ptr + size <= skb_tail_pointer(skb)) > > > > within bpf_internal_load_pointer_neg_helper() and thus returns NULL, > > > > which results in reading -EFAULT. > > > > > > > > This is because bpf_skb_load_helper_{8,16,32} don't include the > > > > "data past headlen do skb_copy_bits()" logic from the non-negative > > > > offset branch in the negative offset branch. > > > > > > > > Note: I don't know sparc assembly, so this doesn't fix sparc... > > > > ideally we should just delete bpf_internal_load_pointer_neg_helper() > > > > This seems to have always been broken (but not pre-git era, since > > > > obviously there was no eBPF helpers back then), but stuff older > > > > than 5.4 is no longer LTS supported anyway, so using 5.4 as fixes tag. > > > > > > > > Cc: Alexei Starovoitov <ast@kernel.org> > > > > Cc: Daniel Borkmann <daniel@iogearbox.net> > > > > Cc: Stanislav Fomichev <sdf@fomichev.me> > > > > Cc: Willem de Bruijn <willemb@google.com> > > > > Reported-by: Matt Moeller <moeller.matt@gmail.com> > > > > Closes: https://issuetracker.google.com/384636719 [Treble - GKI partner internal] > > > > Signed-off-by: Maciej Żenczykowski <maze@google.com> > > > > Fixes: 219d54332a09 ("Linux 5.4") > > > > --- > > > > include/linux/filter.h | 2 ++ > > > > kernel/bpf/core.c | 14 +++++++++ > > > > net/core/filter.c | 69 +++++++++++++++++------------------------- > > > > 3 files changed, 43 insertions(+), 42 deletions(-) > > > > > > > > diff --git a/include/linux/filter.h b/include/linux/filter.h > > > > index a3ea46281595..c24d8e338ce4 100644 > > > > --- a/include/linux/filter.h > > > > +++ b/include/linux/filter.h > > > > @@ -1479,6 +1479,8 @@ static inline u16 bpf_anc_helper(const struct sock_filter *ftest) > > > > void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, > > > > int k, unsigned int size); > > > > > > > > +int bpf_internal_neg_helper(const struct sk_buff *skb, int k); > > > > + > > > > static inline int bpf_tell_extensions(void) > > > > { > > > > return SKF_AD_MAX; > > > > diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c > > > > index da729cbbaeb9..994988dabb97 100644 > > > > --- a/kernel/bpf/core.c > > > > +++ b/kernel/bpf/core.c > > > > @@ -89,6 +89,20 @@ void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, int k, uns > > > > return NULL; > > > > } > > > > > > > > +int bpf_internal_neg_helper(const struct sk_buff *skb, int k) > > > > +{ > > > > + if (k >= 0) > > > > + return k; > > > > + if (k >= SKF_NET_OFF) > > > > + return skb->network_header + k - SKF_NET_OFF; > > > > + if (k >= SKF_LL_OFF) { > > > > + if (unlikely(!skb_mac_header_was_set(skb))) > > > > + return -1; > > > > + return skb->mac_header + k - SKF_LL_OFF; > > > > + } > > > > + return -1; > > > > +} > > > > + > > > > /* tell bpf programs that include vmlinux.h kernel's PAGE_SIZE */ > > > > enum page_size_enum { > > > > __PAGE_SIZE = PAGE_SIZE > > > > diff --git a/net/core/filter.c b/net/core/filter.c > > > > index e56a0be31678..609ef7df71ce 100644 > > > > --- a/net/core/filter.c > > > > +++ b/net/core/filter.c > > > > @@ -221,21 +221,16 @@ BPF_CALL_3(bpf_skb_get_nlattr_nest, struct sk_buff *, skb, u32, a, u32, x) > > > > BPF_CALL_4(bpf_skb_load_helper_8, const struct sk_buff *, skb, const void *, > > > > data, int, headlen, int, offset) > > > > { > > > > - u8 tmp, *ptr; > > > > - const int len = sizeof(tmp); > > > > - > > > > - if (offset >= 0) { > > > > - if (headlen - offset >= len) > > > > - return *(u8 *)(data + offset); > > > > - if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > > > - return tmp; > > > > - } else { > > > > - ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); > > > > - if (likely(ptr)) > > > > - return *(u8 *)ptr; > > > > - } > > > > + u8 tmp; > > > > > > > > - return -EFAULT; > > > > + offset = bpf_internal_neg_helper(skb, offset); > > > > + if (unlikely(offset < 0)) > > > > + return -EFAULT; > > > > + if (headlen - offset >= sizeof(u8)) > > > > + return *(u8 *)(data + offset); > > > > + if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > > > + return -EFAULT; > > > > + return tmp; > > > > } > > > > > > > > BPF_CALL_2(bpf_skb_load_helper_8_no_cache, const struct sk_buff *, skb, > > > > @@ -248,21 +243,16 @@ BPF_CALL_2(bpf_skb_load_helper_8_no_cache, const struct sk_buff *, skb, > > > > BPF_CALL_4(bpf_skb_load_helper_16, const struct sk_buff *, skb, const void *, > > > > data, int, headlen, int, offset) > > > > { > > > > - __be16 tmp, *ptr; > > > > - const int len = sizeof(tmp); > > > > + __be16 tmp; > > > > > > > > - if (offset >= 0) { > > > > - if (headlen - offset >= len) > > > > - return get_unaligned_be16(data + offset); > > > > - if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > > > - return be16_to_cpu(tmp); > > > > - } else { > > > > - ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); > > > > - if (likely(ptr)) > > > > - return get_unaligned_be16(ptr); > > > > - } > > > > - > > > > - return -EFAULT; > > > > + offset = bpf_internal_neg_helper(skb, offset); > > > > + if (unlikely(offset < 0)) > > > > + return -EFAULT; > > > > + if (headlen - offset >= sizeof(__be16)) > > > > + return get_unaligned_be16(data + offset); > > > > + if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > > > + return -EFAULT; > > > > + return be16_to_cpu(tmp); > > > > } > > > > > > > > BPF_CALL_2(bpf_skb_load_helper_16_no_cache, const struct sk_buff *, skb, > > > > @@ -275,21 +265,16 @@ BPF_CALL_2(bpf_skb_load_helper_16_no_cache, const struct sk_buff *, skb, > > > > BPF_CALL_4(bpf_skb_load_helper_32, const struct sk_buff *, skb, const void *, > > > > data, int, headlen, int, offset) > > > > { > > > > - __be32 tmp, *ptr; > > > > - const int len = sizeof(tmp); > > > > - > > > > - if (likely(offset >= 0)) { > > > > - if (headlen - offset >= len) > > > > - return get_unaligned_be32(data + offset); > > > > - if (!skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > > > - return be32_to_cpu(tmp); > > > > - } else { > > > > - ptr = bpf_internal_load_pointer_neg_helper(skb, offset, len); > > > > - if (likely(ptr)) > > > > - return get_unaligned_be32(ptr); > > > > - } > > > > + __be32 tmp; > > > > > > > > - return -EFAULT; > > > > + offset = bpf_internal_neg_helper(skb, offset); > > > > + if (unlikely(offset < 0)) > > > > + return -EFAULT; > > > > + if (headlen - offset >= sizeof(__be32)) > > > > + return get_unaligned_be32(data + offset); > > > > + if (skb_copy_bits(skb, offset, &tmp, sizeof(tmp))) > > > > + return -EFAULT; > > > > + return be32_to_cpu(tmp); > > > > } > > > > > > > > BPF_CALL_2(bpf_skb_load_helper_32_no_cache, const struct sk_buff *, skb, > > > > -- > > > > 2.48.1.262.g85cc9f2d1e-goog > > > > > > > > > > Note: this is currently only compile and boot tested. > > > Which doesn't prove all that much ;-) > > > > Furthermore even after cherrypicking this (or a similar style fix) > > into older LTS: > > - sparc jit is (presumably, maybe only 32-bit) still broken, as the > > assembly uses the old function > > - same for mips jit on 5.4/5.10/5.15 > > - same for powerpc jit on 5.4/5.10 > > > > (but I would guess we don't care) > > and Willem points out that: > > "skb->network_header is an offset against head, while the > get_unaligned_be16 and skb_copy_bits take an offset relative to data. > > I did check yesterday that skb_copy_bits can take negative offsets, in > case a protocol header (e.g., SKF_LL_OFF) is smaller than skb->data." > > which makes me think this approach is possibly (likely?) incorrect? > skb offsets always leave me confused... Same for me WRT skb offsets... However, it should be easy to write a minimal reproducer to trigger the issues in a local vm: create tap device, attach egress bpf (classic) classifier, use tap's new zerocopy api (tap_get_user SOCK_ZEROCOPY) to create fragmented skb. And attach it as a selftest O:-) ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf] bpf: fix classic bpf reads from negative offset outside of linear skb portion 2025-01-22 20:04 [PATCH bpf] bpf: fix classic bpf reads from negative offset outside of linear skb portion Maciej Żenczykowski 2025-01-22 20:16 ` Maciej Żenczykowski @ 2025-01-23 22:36 ` Daniel Borkmann 2025-01-24 18:51 ` Maciej Żenczykowski 1 sibling, 1 reply; 7+ messages in thread From: Daniel Borkmann @ 2025-01-23 22:36 UTC (permalink / raw) To: Maciej Żenczykowski, Maciej Żenczykowski, Alexei Starovoitov Cc: Linux Network Development Mailing List, David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linux Kernel Mailing List, BPF Mailing List, Stanislav Fomichev, Willem de Bruijn, Matt Moeller On 1/22/25 9:04 PM, Maciej Żenczykowski wrote: > We're received reports of cBPF code failing to accept DHCP packets. > "BPF filter for DHCP not working (android14-6.1-lts + android-14.0.0_r74)" I presume this is cBPF on AF_PACKET, right? > The relevant Android code is at: > https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/NetworkStack/jni/network_stack_utils_jni.cpp;l=95;drc=9df50aef1fd163215dcba759045706253a5624f5 > which uses a lot of macros from: > https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/bpf/headers/include/bpf/BpfClassic.h;drc=c58cfb7c7da257010346bd2d6dcca1c0acdc8321 > > This is widely used and does work on the vast majority of drivers, > but is exposing a core kernel cBPF bug related to driver skb layout. > > Root cause is iwlwifi driver, specifically on (at least): > Dell 7212: Intel Dual Band Wireless AC 8265 > Dell 7220: Intel Wireless AC 9560 > Dell 7230: Intel Wi-Fi 6E AX211 > delivers frames where the UDP destination port is not in the skb linear > portion, while the cBPF code is using SKF_NET_OFF relative addressing. > > simplified from above, effectively: > BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, SKF_NET_OFF) > BPF_STMT(BPF_LD | BPF_H | BPF_IND, SKF_NET_OFF + 2) > BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 68, 1, 0) > BPF_STMT(BPF_RET | BPF_K, 0) > BPF_STMT(BPF_RET | BPF_K, 0xFFFFFFFF) > fails to match udp dport=68 packets. > > Specifically the 3rd cBPF instruction fails to match the condition: > if (ptr >= skb->head && ptr + size <= skb_tail_pointer(skb)) > within bpf_internal_load_pointer_neg_helper() and thus returns NULL, > which results in reading -EFAULT. > > This is because bpf_skb_load_helper_{8,16,32} don't include the > "data past headlen do skb_copy_bits()" logic from the non-negative > offset branch in the negative offset branch. > > Note: I don't know sparc assembly, so this doesn't fix sparc... > ideally we should just delete bpf_internal_load_pointer_neg_helper() > This seems to have always been broken (but not pre-git era, since > obviously there was no eBPF helpers back then), but stuff older > than 5.4 is no longer LTS supported anyway, so using 5.4 as fixes tag. > > Cc: Alexei Starovoitov <ast@kernel.org> > Cc: Daniel Borkmann <daniel@iogearbox.net> > Cc: Stanislav Fomichev <sdf@fomichev.me> > Cc: Willem de Bruijn <willemb@google.com> > Reported-by: Matt Moeller <moeller.matt@gmail.com> > Closes: https://issuetracker.google.com/384636719 [Treble - GKI partner internal] > Signed-off-by: Maciej Żenczykowski <maze@google.com> > Fixes: 219d54332a09 ("Linux 5.4") Hm, so not strictly broken, just that using relative SKF_NET_OFF offset is limited in that it requires the data to be in linear section. Some potential workarounds that come to mind: 1) When you say vast majority of drivers, have you checked how much they typically pull into linear section and whether it would make sense also for iwlwifi to do the same? (It sounds like start of network header is already in non-linear part for iwlwifi?) 2) Perhaps rework the filter to avoid relying on SKF_NET_OFF.. tradeoff are more instructions, e.g., # tcpdump -dd udp dst port 68 { 0x28, 0, 0, 0x0000000c }, { 0x15, 0, 4, 0x000086dd }, { 0x30, 0, 0, 0x00000014 }, { 0x15, 0, 11, 0x00000011 }, { 0x28, 0, 0, 0x00000038 }, { 0x15, 8, 9, 0x00000044 }, { 0x15, 0, 8, 0x00000800 }, { 0x30, 0, 0, 0x00000017 }, { 0x15, 0, 6, 0x00000011 }, { 0x28, 0, 0, 0x00000014 }, { 0x45, 4, 0, 0x00001fff }, { 0xb1, 0, 0, 0x0000000e }, { 0x48, 0, 0, 0x00000010 }, { 0x15, 0, 1, 0x00000044 }, { 0x6, 0, 0, 0x00040000 }, { 0x6, 0, 0, 0x00000000 }, 3) Use eBPF asm, e.g. you can pull in data into linear section via helper bpf_skb_pull_data() if needed, or use bpf_skb_load_bytes() which works for linear & non-linear data. 4) What about pulling in linear data in AF_PACKET code right before the cBPF filter is run (perhaps usage of SKF_NET_OFF could be detected and then only done if really needed by the filter)? Thanks, Daniel ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf] bpf: fix classic bpf reads from negative offset outside of linear skb portion 2025-01-23 22:36 ` Daniel Borkmann @ 2025-01-24 18:51 ` Maciej Żenczykowski 0 siblings, 0 replies; 7+ messages in thread From: Maciej Żenczykowski @ 2025-01-24 18:51 UTC (permalink / raw) To: Daniel Borkmann Cc: Alexei Starovoitov, Linux Network Development Mailing List, David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linux Kernel Mailing List, BPF Mailing List, Stanislav Fomichev, Willem de Bruijn, Matt Moeller On Thu, Jan 23, 2025 at 2:36 PM Daniel Borkmann <daniel@iogearbox.net> wrote: > > On 1/22/25 9:04 PM, Maciej Żenczykowski wrote: > > We're received reports of cBPF code failing to accept DHCP packets. > > "BPF filter for DHCP not working (android14-6.1-lts + android-14.0.0_r74)" > > I presume this is cBPF on AF_PACKET, right? > > > The relevant Android code is at: > > https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/NetworkStack/jni/network_stack_utils_jni.cpp;l=95;drc=9df50aef1fd163215dcba759045706253a5624f5 > > which uses a lot of macros from: > > https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/bpf/headers/include/bpf/BpfClassic.h;drc=c58cfb7c7da257010346bd2d6dcca1c0acdc8321 > > > > This is widely used and does work on the vast majority of drivers, > > but is exposing a core kernel cBPF bug related to driver skb layout. > > > > Root cause is iwlwifi driver, specifically on (at least): > > Dell 7212: Intel Dual Band Wireless AC 8265 > > Dell 7220: Intel Wireless AC 9560 > > Dell 7230: Intel Wi-Fi 6E AX211 > > delivers frames where the UDP destination port is not in the skb linear > > portion, while the cBPF code is using SKF_NET_OFF relative addressing. > > > > simplified from above, effectively: > > BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, SKF_NET_OFF) > > BPF_STMT(BPF_LD | BPF_H | BPF_IND, SKF_NET_OFF + 2) > > BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 68, 1, 0) > > BPF_STMT(BPF_RET | BPF_K, 0) > > BPF_STMT(BPF_RET | BPF_K, 0xFFFFFFFF) > > fails to match udp dport=68 packets. > > > > Specifically the 3rd cBPF instruction fails to match the condition: > > if (ptr >= skb->head && ptr + size <= skb_tail_pointer(skb)) > > within bpf_internal_load_pointer_neg_helper() and thus returns NULL, > > which results in reading -EFAULT. > > > > This is because bpf_skb_load_helper_{8,16,32} don't include the > > "data past headlen do skb_copy_bits()" logic from the non-negative > > offset branch in the negative offset branch. > > > > Note: I don't know sparc assembly, so this doesn't fix sparc... > > ideally we should just delete bpf_internal_load_pointer_neg_helper() > > This seems to have always been broken (but not pre-git era, since > > obviously there was no eBPF helpers back then), but stuff older > > than 5.4 is no longer LTS supported anyway, so using 5.4 as fixes tag. > > > > Cc: Alexei Starovoitov <ast@kernel.org> > > Cc: Daniel Borkmann <daniel@iogearbox.net> > > Cc: Stanislav Fomichev <sdf@fomichev.me> > > Cc: Willem de Bruijn <willemb@google.com> > > Reported-by: Matt Moeller <moeller.matt@gmail.com> > > Closes: https://issuetracker.google.com/384636719 [Treble - GKI partner internal] > > Signed-off-by: Maciej Żenczykowski <maze@google.com> > > Fixes: 219d54332a09 ("Linux 5.4") > > Hm, so not strictly broken, just that using relative SKF_NET_OFF offset > is limited in that it requires the data to be in linear section. Some > potential workarounds that come to mind: I'd consider that to be broken... this is *ancient* functionality of classic bpf, and it far predates both eBPF and drivers creating weird split packet layouts. The breakage is very much userspace visible. > 1) When you say vast majority of drivers, have you checked how much they > typically pull into linear section and whether it would make sense also > for iwlwifi to do the same? (It sounds like start of network header is > already in non-linear part for iwlwifi?) Well... when I say 'vast majority' what I mean is: this is rolled out to *all* Android mainline capable devices (and has been for a while), and this is the first bugreport we've got. I would imagine if IPv4 DHCP discovery failed to work (and this appears to be a 100% failure on affected drivers) we'd be hearing a *lot* more screaming... Of course this doesn't mean this is the first time this has failed, since this is certainly not something easy to debug... But it has to be mostly working on the ecosystem as a whole. Of course most deployed devices tend to have old kernels and or old drivers, so probably most of them simply don't do packet split (and even if they do, they'd presumably be doing more normal header split, and be splitting after the udp header) > 2) Perhaps rework the filter to avoid relying on SKF_NET_OFF.. tradeoff > are more instructions, e.g., > > # tcpdump -dd udp dst port 68 > { 0x28, 0, 0, 0x0000000c }, > { 0x15, 0, 4, 0x000086dd }, > { 0x30, 0, 0, 0x00000014 }, > { 0x15, 0, 11, 0x00000011 }, > { 0x28, 0, 0, 0x00000038 }, > { 0x15, 8, 9, 0x00000044 }, > { 0x15, 0, 8, 0x00000800 }, > { 0x30, 0, 0, 0x00000017 }, > { 0x15, 0, 6, 0x00000011 }, > { 0x28, 0, 0, 0x00000014 }, > { 0x45, 4, 0, 0x00001fff }, > { 0xb1, 0, 0, 0x0000000e }, > { 0x48, 0, 0, 0x00000010 }, > { 0x15, 0, 1, 0x00000044 }, > { 0x6, 0, 0, 0x00040000 }, > { 0x6, 0, 0, 0x00000000 }, I don't think this works as is. When I run this with -dd I get the (at first glance) same output as you, but with a warning, here's the same thing with -d: $ tcpdump -d udp dst port 68 Warning: assuming Ethernet (000) ldh [12] <-- assumes ethernet header, loads ethertype (001) jeq #0x86dd jt 2 jf 6 (002) ldb [20] (003) jeq #0x11 jt 4 jf 15 (004) ldh [56] (005) jeq #0x44 jt 14 jf 15 (006) jeq #0x800 jt 7 jf 15 (007) ldb [23] (008) jeq #0x11 jt 9 jf 15 (009) ldh [20] (010) jset #0x1fff jt 15 jf 11 (011) ldxb 4*([14]&0xf) (012) ldh [x + 16] (013) jeq #0x44 jt 14 jf 15 (014) ret #262144 (015) ret #0 Not assuming ethernet is the entire point of using SKF_NET_OFF. (and SKF_AD_OFF + SKF_AD_PROTOCOL to check ethertype) > 3) Use eBPF asm, e.g. you can pull in data into linear section via helper > bpf_skb_pull_data() if needed, or use bpf_skb_load_bytes() which works > for linear & non-linear data. Sure, we could (and possibly should) replace our existing cBPF filters with eBPF. I've been considering it, BUT... we shouldn't have to. [The reason why I've been considering switching all our cBPF to eBPF is related to the bpf jit hardening thread from a few months back] Furthermore some devices running this code are quite likely still on 3.18 and 4.4 kernels (I would have to check, but 4.9 is guaranteed)... which makes any such transition painful. This is *clearly* a kernel bug/regression vs something like Linux 2.0 (or 1.0), and should thus be fixed in the kernel. > 4) What about pulling in linear data in AF_PACKET code right before the > cBPF filter is run (perhaps usage of SKF_NET_OFF could be detected and > then only done if really needed by the filter)? I'm not sure how to do this cleanly without a kernel change. And for a kernel change fixing the bpf helpers simply seems superior. I guess I could 'hack' this by still doing a relative load, but then considering -EFAULT to match... But with the kernel bug present, it's hard to say in how many other places our cBPF code can run into this same problem (just in other filters and/or at different offsets). Btw. we did get a working patch tested yesterday [it basically does an extra -= skb_headroom(skb) ], I'll try to clean it up (add comments) and send in a v2. ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-01-24 18:51 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-01-22 20:04 [PATCH bpf] bpf: fix classic bpf reads from negative offset outside of linear skb portion Maciej Żenczykowski 2025-01-22 20:16 ` Maciej Żenczykowski 2025-01-22 20:28 ` Maciej Żenczykowski 2025-01-22 20:58 ` Maciej Żenczykowski 2025-01-23 20:37 ` Stanislav Fomichev 2025-01-23 22:36 ` Daniel Borkmann 2025-01-24 18:51 ` Maciej Żenczykowski
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).