diff mbox series

[net] filter: Account for tail adjustment during pull operations

Message ID 1670906381-25161-1-git-send-email-quic_subashab@quicinc.com (mailing list archive)
State Changes Requested
Delegated to: BPF
Headers show
Series [net] filter: Account for tail adjustment during pull operations | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net
netdev/fixes_present success Fixes tag present in non-next series
netdev/subject_prefix success Link
netdev/cover_letter success Single patches do not need cover letters
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 23 this patch: 23
netdev/cc_maintainers success CCed 17 of 17 maintainers
netdev/build_clang success Errors and warnings before: 5 this patch: 5
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 23 this patch: 23
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 20 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-4 success Logs for build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-34 success Logs for test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-35 success Logs for test_verifier on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-37 success Logs for test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-38 success Logs for test_verifier on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-9 success Logs for test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for test_maps on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-12 success Logs for test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 success Logs for test_maps on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-14 success Logs for test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-15 fail Logs for test_progs on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-17 fail Logs for test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 fail Logs for test_progs on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-19 success Logs for test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for test_progs_no_alu32 on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-22 fail Logs for test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 fail Logs for test_progs_no_alu32 on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-24 success Logs for test_progs_no_alu32_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for test_progs_no_alu32_parallel on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-26 success Logs for test_progs_no_alu32_parallel on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-27 success Logs for test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-28 success Logs for test_progs_no_alu32_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-29 success Logs for test_progs_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-30 success Logs for test_progs_parallel on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-32 success Logs for test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-33 success Logs for test_progs_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-36 success Logs for test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-31 success Logs for test_progs_parallel on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for test_maps on s390x with gcc
bpf/vmtest-bpf-next-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-7 success Logs for llvm-toolchain
bpf/vmtest-bpf-next-VM_Test-8 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-2 success Logs for build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-3 success Logs for build for aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-5 success Logs for build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for build for x86_64 with llvm-16

Commit Message

Subash Abhinov Kasiviswanathan (KS) Dec. 13, 2022, 4:39 a.m. UTC
Extending the tail can have some unexpected side effects if a program is
reading the content beyond the head skb headlen and all the skbs in the
gso frag_list are linear with no head_frag -

  kernel BUG at net/core/skbuff.c:4219!
  pc : skb_segment+0xcf4/0xd2c
  lr : skb_segment+0x63c/0xd2c
  Call trace:
   skb_segment+0xcf4/0xd2c
   __udp_gso_segment+0xa4/0x544
   udp4_ufo_fragment+0x184/0x1c0
   inet_gso_segment+0x16c/0x3a4
   skb_mac_gso_segment+0xd4/0x1b0
   __skb_gso_segment+0xcc/0x12c
   udp_rcv_segment+0x54/0x16c
   udp_queue_rcv_skb+0x78/0x144
   udp_unicast_rcv_skb+0x8c/0xa4
   __udp4_lib_rcv+0x490/0x68c
   udp_rcv+0x20/0x30
   ip_protocol_deliver_rcu+0x1b0/0x33c
   ip_local_deliver+0xd8/0x1f0
   ip_rcv+0x98/0x1a4
   deliver_ptype_list_skb+0x98/0x1ec
   __netif_receive_skb_core+0x978/0xc60

Fix this by marking these skbs as GSO_DODGY so segmentation can handle
the tail updates accordingly.

Fixes: 5293efe62df8 ("bpf: add bpf_skb_change_tail helper")
Signed-off-by: Sean Tranchetti <quic_stranche@quicinc.com>
Signed-off-by: Subash Abhinov Kasiviswanathan <quic_subashab@quicinc.com>
---
 net/core/filter.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

Comments

Daniel Borkmann Dec. 13, 2022, 10:42 p.m. UTC | #1
On 12/13/22 5:39 AM, Subash Abhinov Kasiviswanathan wrote:
> Extending the tail can have some unexpected side effects if a program is
> reading the content beyond the head skb headlen and all the skbs in the
> gso frag_list are linear with no head_frag -
> 
>    kernel BUG at net/core/skbuff.c:4219!
>    pc : skb_segment+0xcf4/0xd2c
>    lr : skb_segment+0x63c/0xd2c
>    Call trace:
>     skb_segment+0xcf4/0xd2c
>     __udp_gso_segment+0xa4/0x544
>     udp4_ufo_fragment+0x184/0x1c0
>     inet_gso_segment+0x16c/0x3a4
>     skb_mac_gso_segment+0xd4/0x1b0
>     __skb_gso_segment+0xcc/0x12c
>     udp_rcv_segment+0x54/0x16c
>     udp_queue_rcv_skb+0x78/0x144
>     udp_unicast_rcv_skb+0x8c/0xa4
>     __udp4_lib_rcv+0x490/0x68c
>     udp_rcv+0x20/0x30
>     ip_protocol_deliver_rcu+0x1b0/0x33c
>     ip_local_deliver+0xd8/0x1f0
>     ip_rcv+0x98/0x1a4
>     deliver_ptype_list_skb+0x98/0x1ec
>     __netif_receive_skb_core+0x978/0xc60
> 
> Fix this by marking these skbs as GSO_DODGY so segmentation can handle
> the tail updates accordingly.
> 
> Fixes: 5293efe62df8 ("bpf: add bpf_skb_change_tail helper")
> Signed-off-by: Sean Tranchetti <quic_stranche@quicinc.com>
> Signed-off-by: Subash Abhinov Kasiviswanathan <quic_subashab@quicinc.com>
> ---
>   net/core/filter.c | 14 ++++++++++++++
>   1 file changed, 14 insertions(+)
> 
> diff --git a/net/core/filter.c b/net/core/filter.c
> index bb0136e..d5f7f79 100644
> --- a/net/core/filter.c
> +++ b/net/core/filter.c
> @@ -1654,6 +1654,20 @@ static DEFINE_PER_CPU(struct bpf_scratchpad, bpf_sp);
>   static inline int __bpf_try_make_writable(struct sk_buff *skb,
>   					  unsigned int write_len)
>   {
> +	struct sk_buff *list_skb = skb_shinfo(skb)->frag_list;
> +
> +	if (skb_is_gso(skb) && list_skb && !list_skb->head_frag &&
> +	    skb_headlen(list_skb)) {
> +		int headlen = skb_headlen(skb);
> +		int err = skb_ensure_writable(skb, write_len);
> +
> +		/* pskb_pull_tail() has occurred */
> +		if (!err && headlen != skb_headlen(skb))
> +			skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
> +
> +		return err;
> +	}

__bpf_try_make_writable() does not look like the right location to me
given this is called also from various other places. bpf_skb_change_tail
has skb_gso_reset in there, potentially that or pskb_pull_tail itself
should mark it?

>   	return skb_ensure_writable(skb, write_len);
>   }
>   
>
Subash Abhinov Kasiviswanathan (KS) Dec. 14, 2022, 6:32 a.m. UTC | #2
On 12/13/2022 3:42 PM, Daniel Borkmann wrote:
> On 12/13/22 5:39 AM, Subash Abhinov Kasiviswanathan wrote:
>> Extending the tail can have some unexpected side effects if a program is
>> reading the content beyond the head skb headlen and all the skbs in the
>> gso frag_list are linear with no head_frag -
>>
>> diff --git a/net/core/filter.c b/net/core/filter.c
>> index bb0136e..d5f7f79 100644
>> --- a/net/core/filter.c
>> +++ b/net/core/filter.c
>> @@ -1654,6 +1654,20 @@ static DEFINE_PER_CPU(struct bpf_scratchpad, 
>> bpf_sp);
>>   static inline int __bpf_try_make_writable(struct sk_buff *skb,
>>                         unsigned int write_len)
>>   {
>> +    struct sk_buff *list_skb = skb_shinfo(skb)->frag_list;
>> +
>> +    if (skb_is_gso(skb) && list_skb && !list_skb->head_frag &&
>> +        skb_headlen(list_skb)) {
>> +        int headlen = skb_headlen(skb);
>> +        int err = skb_ensure_writable(skb, write_len);
>> +
>> +        /* pskb_pull_tail() has occurred */
>> +        if (!err && headlen != skb_headlen(skb))
>> +            skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
>> +
>> +        return err;
>> +    }
> 
> __bpf_try_make_writable() does not look like the right location to me
> given this is called also from various other places. bpf_skb_change_tail
> has skb_gso_reset in there, potentially that or pskb_pull_tail itself
> should mark it?

Actually the program we used had BPF_FUNC_skb_pull_data and we put this 
check in __bpf_try_make_writable so that it would help out 
BPF_FUNC_skb_pull_data & other users of __bpf_try_make_writable. Having 
the check in __pskb_pull_tail seems preferable though. Could you tell if 
the following is acceptable as this works for us -

diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index dfc14a7..0f60abb 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2263,6 +2263,9 @@ void *__pskb_pull_tail(struct sk_buff *skb, int delta)
                                 insp = list;
                         } else {
                                 /* Eaten partially. */
+                               if (skb_is_gso(skb) && !list->head_frag &&
+                                   skb_headlen(list))
+                                       skb_shinfo(skb)->gso_type |= 
SKB_GSO_DODGY;

                                 if (skb_shared(list)) {
                                         /* Sucks! We need to fork list. 
:-( */

> 
>>       return skb_ensure_writable(skb, write_len);
>>   }
>>
>
Daniel Borkmann Dec. 14, 2022, 11:23 p.m. UTC | #3
On 12/14/22 7:32 AM, Subash Abhinov Kasiviswanathan (KS) wrote:
> On 12/13/2022 3:42 PM, Daniel Borkmann wrote:
>> On 12/13/22 5:39 AM, Subash Abhinov Kasiviswanathan wrote:
>>> Extending the tail can have some unexpected side effects if a program is
>>> reading the content beyond the head skb headlen and all the skbs in the
>>> gso frag_list are linear with no head_frag -
>>>
>>> diff --git a/net/core/filter.c b/net/core/filter.c
>>> index bb0136e..d5f7f79 100644
>>> --- a/net/core/filter.c
>>> +++ b/net/core/filter.c
>>> @@ -1654,6 +1654,20 @@ static DEFINE_PER_CPU(struct bpf_scratchpad, bpf_sp);
>>>   static inline int __bpf_try_make_writable(struct sk_buff *skb,
>>>                         unsigned int write_len)
>>>   {
>>> +    struct sk_buff *list_skb = skb_shinfo(skb)->frag_list;
>>> +
>>> +    if (skb_is_gso(skb) && list_skb && !list_skb->head_frag &&
>>> +        skb_headlen(list_skb)) {
>>> +        int headlen = skb_headlen(skb);
>>> +        int err = skb_ensure_writable(skb, write_len);
>>> +
>>> +        /* pskb_pull_tail() has occurred */
>>> +        if (!err && headlen != skb_headlen(skb))
>>> +            skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
>>> +
>>> +        return err;
>>> +    }
>>
>> __bpf_try_make_writable() does not look like the right location to me
>> given this is called also from various other places. bpf_skb_change_tail
>> has skb_gso_reset in there, potentially that or pskb_pull_tail itself
>> should mark it?
> 
> Actually the program we used had BPF_FUNC_skb_pull_data and we put this check in __bpf_try_make_writable so that it would help out BPF_FUNC_skb_pull_data & other users of __bpf_try_make_writable. Having the check in __pskb_pull_tail seems preferable though. Could you tell if the following is acceptable as this works for us -

Ah okay, that is good to know. The Fixes tag might have been misleading in that
case. From what you describe it sounds like a generic __pskb_pull_tail() issue
then? If so I'd go with the below for -net tree as a generic fix, yes.

> diff --git a/net/core/skbuff.c b/net/core/skbuff.c
> index dfc14a7..0f60abb 100644
> --- a/net/core/skbuff.c
> +++ b/net/core/skbuff.c
> @@ -2263,6 +2263,9 @@ void *__pskb_pull_tail(struct sk_buff *skb, int delta)
>                                  insp = list;
>                          } else {
>                                  /* Eaten partially. */
> +                               if (skb_is_gso(skb) && !list->head_frag &&
> +                                   skb_headlen(list))
> +                                       skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
> 
>                                  if (skb_shared(list)) {
>                                          /* Sucks! We need to fork list. :-( */
> 
>>
>>>       return skb_ensure_writable(skb, write_len);
>>>   }
>>>
>>
diff mbox series

Patch

diff --git a/net/core/filter.c b/net/core/filter.c
index bb0136e..d5f7f79 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -1654,6 +1654,20 @@  static DEFINE_PER_CPU(struct bpf_scratchpad, bpf_sp);
 static inline int __bpf_try_make_writable(struct sk_buff *skb,
 					  unsigned int write_len)
 {
+	struct sk_buff *list_skb = skb_shinfo(skb)->frag_list;
+
+	if (skb_is_gso(skb) && list_skb && !list_skb->head_frag &&
+	    skb_headlen(list_skb)) {
+		int headlen = skb_headlen(skb);
+		int err = skb_ensure_writable(skb, write_len);
+
+		/* pskb_pull_tail() has occurred */
+		if (!err && headlen != skb_headlen(skb))
+			skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
+
+		return err;
+	}
+
 	return skb_ensure_writable(skb, write_len);
 }