diff mbox series

[net] Drop packets with invalid headers to prevent KMSAN infoleak

Message ID 20241019071149.81696-1-danielyangkang@gmail.com (mailing list archive)
State Changes Requested
Delegated to: BPF
Headers show
Series [net] Drop packets with invalid headers to prevent KMSAN infoleak | expand

Checks

Context Check Description
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for net
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag present in non-next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 5 this patch: 5
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers fail 17 maintainers not CCed: andrii@kernel.org pabeni@redhat.com kuba@kernel.org john.fastabend@gmail.com daniel@iogearbox.net bpf@vger.kernel.org yonghong.song@linux.dev jolsa@kernel.org song@kernel.org haoluo@google.com ast@kernel.org netdev@vger.kernel.org edumazet@google.com sdf@fomichev.me martin.lau@linux.dev kpsingh@kernel.org eddyz87@gmail.com
netdev/build_clang success Errors and warnings before: 3 this patch: 3
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
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: 30 this patch: 30
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 13 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 6 this patch: 6
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2024-10-19--09-00 (tests: 765)
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-37 fail Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17-O2
bpf/vmtest-bpf-next-VM_Test-30 fail Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-18 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-31 fail Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18-O2
bpf/vmtest-bpf-next-VM_Test-17 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-15 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 fail Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-7 fail Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-38 fail Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-21 fail Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-39 fail Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-22 fail Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-14 fail Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-PR fail PR summary
bpf/vmtest-bpf-next-VM_Test-13 fail Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc

Commit Message

Daniel Yang Oct. 19, 2024, 7:11 a.m. UTC
KMSAN detects uninitialized memory stored to memory by
bpf_clone_redirect(). Adding a check to the transmission path to find
malformed headers prevents this issue. Specifically, we check if the length
of the data stored in skb is less than the minimum device header length.
If so, drop the packet since the skb cannot contain a valid device header.
Also check if mac_header_len(skb) is outside the range provided of valid
device header lengths.

Testing this patch with syzbot removes the bug.

Fixes: 88264981f208 ("Merge tag 'sched_ext-for-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext")
Reported-by: syzbot+346474e3bf0b26bd3090@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=346474e3bf0b26bd3090
Signed-off-by: Daniel Yang <danielyangkang@gmail.com>
---
 net/core/filter.c | 7 +++++++
 1 file changed, 7 insertions(+)

Comments

Martin KaFai Lau Oct. 21, 2024, 10:25 p.m. UTC | #1
On 10/19/24 12:11 AM, Daniel Yang wrote:
> KMSAN detects uninitialized memory stored to memory by
> bpf_clone_redirect(). Adding a check to the transmission path to find
> malformed headers prevents this issue. Specifically, we check if the length
> of the data stored in skb is less than the minimum device header length.
> If so, drop the packet since the skb cannot contain a valid device header.
> Also check if mac_header_len(skb) is outside the range provided of valid
> device header lengths.
> 
> Testing this patch with syzbot removes the bug.
> 
> Fixes: 88264981f208 ("Merge tag 'sched_ext-for-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext")

I am pretty sure this is the wrong tag.

A test in selftests/bpf is needed to reproduce and better understand this. Only 
bpf_clone_redirect() is needed to reproduce or other bpf_skb_*() helpers calls 
are needed to reproduce?


> Reported-by: syzbot+346474e3bf0b26bd3090@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=346474e3bf0b26bd3090
> Signed-off-by: Daniel Yang <danielyangkang@gmail.com>
> ---
>   net/core/filter.c | 7 +++++++
>   1 file changed, 7 insertions(+)
> 
> diff --git a/net/core/filter.c b/net/core/filter.c
> index cd3524cb3..92d8f2098 100644
> --- a/net/core/filter.c
> +++ b/net/core/filter.c
> @@ -2191,6 +2191,13 @@ static int __bpf_redirect_common(struct sk_buff *skb, struct net_device *dev,
>   		return -ERANGE;
>   	}
>   
> +	if (unlikely(skb->len < dev->min_header_len ||
> +		     skb_mac_header_len(skb) < dev->min_header_len ||
> +		     skb_mac_header_len(skb) > dev->hard_header_len)) {
> +		kfree_skb(skb);
> +		return -ERANGE;
> +	}
> +
>   	bpf_push_mac_rcsum(skb);
>   	return flags & BPF_F_INGRESS ?
>   	       __bpf_rx_skb(dev, skb) : __bpf_tx_skb(dev, skb);
Daniel Yang Oct. 22, 2024, 1:37 a.m. UTC | #2
> A test in selftests/bpf is needed to reproduce and better understand this.
I don't know much about self tests but I've just been using the syzbot
repro and #syz test at the link in the patch:
https://syzkaller.appspot.com/bug?extid=346474e3bf0b26bd3090. Testing
the patch showed that the uninitialized memory was not getting written
to memory.

> Only bpf_clone_redirect() is needed to reproduce or other bpf_skb_*() helpers calls
> are needed to reproduce?

From what I can see in the crash report here:
https://syzkaller.appspot.com/text?tag=CrashReport&x=10ba3ca9980000,
only bpf_clone_redirect() is needed to trigger this issue. The issue
seems to be that bpf_try_make_head_writable clones the skb and creates
uninitialized memory but __bpf_tx_skb() gets called and the ethernet
header never got written, resulting in the skb having a data section
without a proper mac header. Current check:

if (unlikely(skb->mac_header >= skb->network_header || skb->len == 0))
{
**drop packet**
}

in __bpf_redirect_common() is insufficient since it only checks if the
mac header is misordered or if the data length is 0. So, any packet
with a malformed MAC header that is not 14 bytes but is not 0 doesn't
get dropped. Adding bounds checks for mac header size should fix this.
And from what I see in the syz test of this patch, it does.

Are there any possible unexpected issues that can be caused by this?
Paolo Abeni Oct. 22, 2024, 3:30 p.m. UTC | #3
On 10/22/24 03:37, Daniel Yang wrote:
> Are there any possible unexpected issues that can be caused by this?

This patch is apparently the cause of BPF self-tests failures:

test_empty_skb:FAIL:ret: veth ETH_HLEN+1 packet ingress
[redirect_ingress] unexpected ret: veth ETH_HLEN+1 packet ingress
[redirect_ingress]: actual -34 != expected 0
test_empty_skb:PASS:err: veth ETH_HLEN+1 packet ingress
[redirect_egress] 0 nsec
test_empty_skb:FAIL:ret: veth ETH_HLEN+1 packet ingress
[redirect_egress] unexpected ret: veth ETH_HLEN+1 packet ingress
[redirect_egress]: actual -34 != expected 1

Before submitting an eventual next revision, please very that BPF
self-tests are happy.

Thanks,

Paolo
Martin KaFai Lau Oct. 22, 2024, 6:14 p.m. UTC | #4
On 10/21/24 6:37 PM, Daniel Yang wrote:
>> A test in selftests/bpf is needed to reproduce and better understand this.
> I don't know much about self tests but I've just been using the syzbot
> repro and #syz test at the link in the patch:
> https://syzkaller.appspot.com/bug?extid=346474e3bf0b26bd3090. Testing
> the patch showed that the uninitialized memory was not getting written
> to memory.
> 
>> Only bpf_clone_redirect() is needed to reproduce or other bpf_skb_*() helpers calls
>> are needed to reproduce?

If only bpf_clone_redirect() is needed, it should be simple to write a selftest 
to reproduce it. It also helps to catch future regression.

Please tag the next respin as "bpf" also.

> 
>  From what I can see in the crash report here:
> https://syzkaller.appspot.com/text?tag=CrashReport&x=10ba3ca9980000,
> only bpf_clone_redirect() is needed to trigger this issue. The issue
> seems to be that bpf_try_make_head_writable clones the skb and creates
> uninitialized memory but __bpf_tx_skb() gets called and the ethernet
> header never got written, resulting in the skb having a data section
> without a proper mac header. Current check:
> 
> if (unlikely(skb->mac_header >= skb->network_header || skb->len == 0))
> {
> **drop packet**
> }
> 
> in __bpf_redirect_common() is insufficient since it only checks if the
> mac header is misordered or if the data length is 0. So, any packet
> with a malformed MAC header that is not 14 bytes but is not 0 doesn't
> get dropped. Adding bounds checks for mac header size should fix this.
> And from what I see in the syz test of this patch, it does.
> 
> Are there any possible unexpected issues that can be caused by this?
Daniel Yang Oct. 27, 2024, 8:49 a.m. UTC | #5
On Tue, Oct 22, 2024 at 11:14 AM Martin KaFai Lau <martin.lau@linux.dev> wrote:
>
> On 10/21/24 6:37 PM, Daniel Yang wrote:
> >> A test in selftests/bpf is needed to reproduce and better understand this.
> > I don't know much about self tests but I've just been using the syzbot
> > repro and #syz test at the link in the patch:
> > https://syzkaller.appspot.com/bug?extid=346474e3bf0b26bd3090. Testing
> > the patch showed that the uninitialized memory was not getting written
> > to memory.
> >
> >> Only bpf_clone_redirect() is needed to reproduce or other bpf_skb_*() helpers calls
> >> are needed to reproduce?
>
> If only bpf_clone_redirect() is needed, it should be simple to write a selftest
> to reproduce it. It also helps to catch future regression.
>
> Please tag the next respin as "bpf" also.

I have a problem. I can't seem to build the bpf kselftests for some
reason. There is always a struct definition error:
In file included from progs/profiler1.c:5:
progs/profiler.inc.h:599:49: error: declaration of 'struct
syscall_trace_enter' will not be visible outside of t]
  599 | int tracepoint__syscalls__sys_enter_kill(struct
syscall_trace_enter* ctx)
      |                                                 ^
progs/profiler.inc.h:604:15: error: incomplete definition of type
'struct syscall_trace_enter'
  604 |         int pid = ctx->args[0];
      |                   ~~~^
progs/profiler.inc.h:599:49: note: forward declaration of 'struct
syscall_trace_enter'
  599 | int tracepoint__syscalls__sys_enter_kill(struct
syscall_trace_enter* ctx)
      |                                                 ^
progs/profiler.inc.h:605:15: error: incomplete definition of type
'struct syscall_trace_enter'
  605 |         int sig = ctx->args[1];
      |                   ~~~^
progs/profiler.inc.h:599:49: note: forward declaration of 'struct
syscall_trace_enter'
  599 | int tracepoint__syscalls__sys_enter_kill(struct
syscall_trace_enter* ctx)

I just run the following to build:
$ cd tools/testing/selftests/bpf/
$ make

I can't find anyone else encountering the same error.
Yonghong Song Oct. 28, 2024, 5:42 a.m. UTC | #6
On 10/27/24 1:49 AM, Daniel Yang wrote:
> On Tue, Oct 22, 2024 at 11:14 AM Martin KaFai Lau <martin.lau@linux.dev> wrote:
>> On 10/21/24 6:37 PM, Daniel Yang wrote:
>>>> A test in selftests/bpf is needed to reproduce and better understand this.
>>> I don't know much about self tests but I've just been using the syzbot
>>> repro and #syz test at the link in the patch:
>>> https://syzkaller.appspot.com/bug?extid=346474e3bf0b26bd3090. Testing
>>> the patch showed that the uninitialized memory was not getting written
>>> to memory.
>>>
>>>> Only bpf_clone_redirect() is needed to reproduce or other bpf_skb_*() helpers calls
>>>> are needed to reproduce?
>> If only bpf_clone_redirect() is needed, it should be simple to write a selftest
>> to reproduce it. It also helps to catch future regression.
>>
>> Please tag the next respin as "bpf" also.
> I have a problem. I can't seem to build the bpf kselftests for some
> reason. There is always a struct definition error:
> In file included from progs/profiler1.c:5:
> progs/profiler.inc.h:599:49: error: declaration of 'struct
> syscall_trace_enter' will not be visible outside of t]
>    599 | int tracepoint__syscalls__sys_enter_kill(struct
> syscall_trace_enter* ctx)
>        |                                                 ^
> progs/profiler.inc.h:604:15: error: incomplete definition of type
> 'struct syscall_trace_enter'
>    604 |         int pid = ctx->args[0];
>        |                   ~~~^
> progs/profiler.inc.h:599:49: note: forward declaration of 'struct
> syscall_trace_enter'
>    599 | int tracepoint__syscalls__sys_enter_kill(struct
> syscall_trace_enter* ctx)
>        |                                                 ^
> progs/profiler.inc.h:605:15: error: incomplete definition of type
> 'struct syscall_trace_enter'
>    605 |         int sig = ctx->args[1];
>        |                   ~~~^
> progs/profiler.inc.h:599:49: note: forward declaration of 'struct
> syscall_trace_enter'
>    599 | int tracepoint__syscalls__sys_enter_kill(struct
> syscall_trace_enter* ctx)
>
> I just run the following to build:
> $ cd tools/testing/selftests/bpf/
> $ make

It might be due to your .config file.
The 'struct syscall_trace_enter' is defined in kernel/trace/trace.h,
which is used in kernel/trace/trace_syscalls.c. Maybe your config
does not have CONFIG_FTRACE_SYSCALLS?

>
> I can't find anyone else encountering the same error.
Alexander Lobakin Oct. 29, 2024, 4:40 p.m. UTC | #7
From: Daniel Yang <danielyangkang@gmail.com>
Date: Sat, 19 Oct 2024 00:11:39 -0700

> KMSAN detects uninitialized memory stored to memory by
> bpf_clone_redirect(). Adding a check to the transmission path to find
> malformed headers prevents this issue. Specifically, we check if the length
> of the data stored in skb is less than the minimum device header length.
> If so, drop the packet since the skb cannot contain a valid device header.
> Also check if mac_header_len(skb) is outside the range provided of valid
> device header lengths.
> 
> Testing this patch with syzbot removes the bug.
> 
> Fixes: 88264981f208 ("Merge tag 'sched_ext-for-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext")
> Reported-by: syzbot+346474e3bf0b26bd3090@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=346474e3bf0b26bd3090
> Signed-off-by: Daniel Yang <danielyangkang@gmail.com>
> ---
>  net/core/filter.c | 7 +++++++
>  1 file changed, 7 insertions(+)
> 
> diff --git a/net/core/filter.c b/net/core/filter.c
> index cd3524cb3..92d8f2098 100644
> --- a/net/core/filter.c
> +++ b/net/core/filter.c
> @@ -2191,6 +2191,13 @@ static int __bpf_redirect_common(struct sk_buff *skb, struct net_device *dev,
>  		return -ERANGE;
>  	}
>  
> +	if (unlikely(skb->len < dev->min_header_len ||
> +		     skb_mac_header_len(skb) < dev->min_header_len ||
> +		     skb_mac_header_len(skb) > dev->hard_header_len)) {
> +		kfree_skb(skb);
> +		return -ERANGE;
> +	}

I believe this should go under IS_ENABLED(CONFIG_KMSAN) or
CONFIG_DEBUG_NET or so to not affect the regular configurations.
Or does this fix some real bug?

> +
>  	bpf_push_mac_rcsum(skb);
>  	return flags & BPF_F_INGRESS ?
>  	       __bpf_rx_skb(dev, skb) : __bpf_tx_skb(dev, skb);

Thanks,
Olek
Daniel Yang Oct. 29, 2024, 9:23 p.m. UTC | #8
On Sun, Oct 27, 2024 at 10:42 PM Yonghong Song <yonghong.song@linux.dev> wrote:
> It might be due to your .config file.
> The 'struct syscall_trace_enter' is defined in kernel/trace/trace.h,
> which is used in kernel/trace/trace_syscalls.c. Maybe your config
> does not have CONFIG_FTRACE_SYSCALLS?
I did add it to my config but another error popped up after I tried to
rerun the tests. I just ended up using the vmtest.sh script and got
the tests working. Seems like there should be a template config file
or something to make building locally more convenient. Anyways, thanks
for the help.

- Daniel
Daniel Yang Oct. 29, 2024, 9:34 p.m. UTC | #9
On Tue, Oct 29, 2024 at 9:41 AM Alexander Lobakin
<aleksander.lobakin@intel.com> wrote:
> > +     if (unlikely(skb->len < dev->min_header_len ||
> > +                  skb_mac_header_len(skb) < dev->min_header_len ||
> > +                  skb_mac_header_len(skb) > dev->hard_header_len)) {
> > +             kfree_skb(skb);
> > +             return -ERANGE;
> > +     }
>
> I believe this should go under IS_ENABLED(CONFIG_KMSAN) or
> CONFIG_DEBUG_NET or so to not affect the regular configurations.
> Or does this fix some real bug?

Well in my opinion, an infoleak is still an infoleak. But, this would
likely not get triggered as long as an skb with a properly initialized
eth header is passed into the bpf_clone_redirect function. We could
initialize the memory to 0 but the performance hit would be too much.
If the bpf_clone_redirect() function cannot be called from user space
with user-crafted skbs as input, I don't think this is really an issue
and we can just put it under the macros to get rid of the syzbot
error.

- Daniel
diff mbox series

Patch

diff --git a/net/core/filter.c b/net/core/filter.c
index cd3524cb3..92d8f2098 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2191,6 +2191,13 @@  static int __bpf_redirect_common(struct sk_buff *skb, struct net_device *dev,
 		return -ERANGE;
 	}
 
+	if (unlikely(skb->len < dev->min_header_len ||
+		     skb_mac_header_len(skb) < dev->min_header_len ||
+		     skb_mac_header_len(skb) > dev->hard_header_len)) {
+		kfree_skb(skb);
+		return -ERANGE;
+	}
+
 	bpf_push_mac_rcsum(skb);
 	return flags & BPF_F_INGRESS ?
 	       __bpf_rx_skb(dev, skb) : __bpf_tx_skb(dev, skb);