Skip to content

[ciqlts8_6] bpf: Fix ringbuf memory type confusion when passing to helpers #371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 30, 2025

Conversation

thefossguy-ciq
Copy link

  • Commit Message Requirements
  • Built against Vault/LTS Environment
  • kABI Check Passed, where Valid (Pre 9.4 RT does not have kABI stability)
  • Boot Test
  • Kernel SelfTest results
  • Additional Tests as determined relevant

Commit message

jira VULN-68
cve CVE-2021-4204
commit-author Daniel Borkmann <daniel@iogearbox.net> commit a672b2e36a648afb04ad3bda93b6bda947a479a5

The bpf_ringbuf_submit() and bpf_ringbuf_discard() have ARG_PTR_TO_ALLOC_MEM in their bpf_func_proto definition as their first argument, and thus both expect the result from a prior bpf_ringbuf_reserve() call which has a return type of RET_PTR_TO_ALLOC_MEM_OR_NULL.

While the non-NULL memory from bpf_ringbuf_reserve() can be passed to other helpers, the two sinks (bpf_ringbuf_submit(), bpf_ringbuf_discard()) right now only enforce a register type of PTR_TO_MEM.

This can lead to potential type confusion since it would allow other PTR_TO_MEM memory to be passed into the two sinks which did not come from bpf_ringbuf_reserve().

Add a new MEM_ALLOC composable type attribute for PTR_TO_MEM, and enforce that:

 - bpf_ringbuf_reserve() returns NULL or PTR_TO_MEM | MEM_ALLOC
 - bpf_ringbuf_submit() and bpf_ringbuf_discard() only take PTR_TO_MEM | MEM_ALLOC but not plain PTR_TO_MEM arguments via ARG_PTR_TO_ALLOC_MEM
 - however, other helpers might treat PTR_TO_MEM | MEM_ALLOC as plain PTR_TO_MEM to populate the memory area when they use ARG_PTR_TO_{UNINIT_,}MEM in their func proto description

Fixes: 457f44363a88 ("bpf: Implement BPF ring buffer and verifier support for it")
	Reported-by: Alexei Starovoitov <ast@kernel.org>
	Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
	Acked-by: John Fastabend <john.fastabend@gmail.com>
	Acked-by: Alexei Starovoitov <ast@kernel.org>
(cherry picked from commit a672b2e36a648afb04ad3bda93b6bda947a479a5)
	Signed-off-by: Pratham Patel <ppatel@ciq.com>

Kernel build logs

/home/ciq/kernel/kernel-src-tree
  CLEAN   .
  CLEAN   arch/x86/entry/vdso
  CLEAN   arch/x86/kernel/cpu
  CLEAN   arch/x86/kernel
  CLEAN   arch/x86/purgatory
  CLEAN   arch/x86/realmode/rm
  CLEAN   arch/x86/lib
  CLEAN   certs
  CLEAN   drivers/firmware/efi/libstub
  CLEAN   drivers/gpu/drm/radeon
  CLEAN   drivers/scsi
  CLEAN   drivers/tty/vt
  CLEAN   drivers/video/logo
  CLEAN   kernel/debug/kdb
  CLEAN   kernel
  CLEAN   lib/raid6
  CLEAN   lib
  CLEAN   net/wireless
  CLEAN   security/selinux
  CLEAN   usr
  CLEAN   arch/x86/boot/compressed
  CLEAN   arch/x86/boot
  CLEAN   arch/x86/tools
  CLEAN    resolve_btfids
  CLEAN   .tmp_versions
  CLEAN   scripts/basic
  CLEAN   scripts/genksyms
  CLEAN   scripts/kconfig
  CLEAN   scripts/mod
  CLEAN   scripts/selinux/genheaders
  CLEAN   scripts/selinux/mdp
  CLEAN   scripts
  CLEAN   include/config usr/include include/generated arch/x86/include/generated
  CLEAN   .config .config.old .version Module.symvers
[TIMER]{MRPROPER}: 13s
x86_64 architecture detected, copying config
'configs/kernel-x86_64.config' -> '.config'
Setting Local Version for build
CONFIG_LOCALVERSION="-ciqlts8_6-CVE-2021-4204-613e08bfafa7"
Making olddefconfig
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
  YACC    scripts/kconfig/zconf.tab.c
  LEX     scripts/kconfig/zconf.lex.c
  HOSTCC  scripts/kconfig/zconf.tab.o
  HOSTLD  scripts/kconfig/conf
scripts/kconfig/conf  --olddefconfig Kconfig
#
# configuration written to .config
#
Starting Build
scripts/kconfig/conf  --syncconfig Kconfig
  SYSTBL  arch/x86/include/generated/asm/syscalls_32.h
  SYSHDR  arch/x86/include/generated/asm/unistd_32_ia32.h
  SYSHDR  arch/x86/include/generated/asm/unistd_64_x32.h
  SYSTBL  arch/x86/include/generated/asm/syscalls_64.h
  HYPERCALLS arch/x86/include/generated/asm/xen-hypercalls.h
  SYSHDR  arch/x86/include/generated/uapi/asm/unistd_32.h
  SYSHDR  arch/x86/include/generated/uapi/asm/unistd_64.h
  SYSHDR  arch/x86/include/generated/uapi/asm/unistd_x32.h
  HOSTCC  scripts/basic/bin2c
  WRAP    arch/x86/include/generated/uapi/asm/bpf_perf_event.h
  WRAP    arch/x86/include/generated/uapi/asm/socket.h
  WRAP    arch/x86/include/generated/uapi/asm/poll.h
  UPD     include/generated/uapi/linux/version.h
  UPD     include/config/kernel.release
  DESCEND objtool
  DESCEND bpf/resolve_btfids
[---snip---]
  INSTALL sound/usb/misc/snd-ua101.ko
  INSTALL sound/usb/snd-usb-audio.ko
  INSTALL sound/usb/snd-usbmidi-lib.ko
  INSTALL sound/usb/usx2y/snd-usb-us122l.ko
  INSTALL sound/usb/usx2y/snd-usb-usx2y.ko
  INSTALL sound/virtio/virtio_snd.ko
  INSTALL sound/x86/snd-hdmi-lpe-audio.ko
  INSTALL sound/xen/snd_xen_front.ko
  INSTALL virt/lib/irqbypass.ko
  DEPMOD  4.18.0-ciqlts8_6-CVE-2021-4204-613e08bfafa7+
[TIMER]{MODULES}: 7s
Making Install
sh ./arch/x86/boot/install.sh 4.18.0-ciqlts8_6-CVE-2021-4204-613e08bfafa7+ arch/x86/boot/bzImage \
	System.map "/boot"
[TIMER]{INSTALL}: 12s
Checking kABI
Checking kABI
kABI check passed
Setting Default Kernel to /boot/vmlinuz-4.18.0-ciqlts8_6-CVE-2021-4204-613e08bfafa7+ and Index to 0
The default is /boot/loader/entries/7f3f66b2da04463d952241da9616a58a-4.18.0-ciqlts8_6-CVE-2021-4204-613e08bfafa7+.conf with index 0 and kernel /boot/vmlinuz-4.18.0-ciqlts8_6-CVE-2021-4204-613e08bfafa7+
The default is /boot/loader/entries/7f3f66b2da04463d952241da9616a58a-4.18.0-ciqlts8_6-CVE-2021-4204-613e08bfafa7+.conf with index 0 and kernel /boot/vmlinuz-4.18.0-ciqlts8_6-CVE-2021-4204-613e08bfafa7+
Generating grub configuration file ...
Adding boot menu entry for EFI firmware configuration
done
Hopefully Grub2.0 took everything ... rebooting after time metrices
[TIMER]{MRPROPER}: 13s
[TIMER]{BUILD}: 1372s
[TIMER]{MODULES}: 7s
[TIMER]{INSTALL}: 12s
[TIMER]{TOTAL} 1408s
Rebooting in 10 seconds

build.log

Kselftests

It appears that the vmx_preemption_timer_test kselftest doesn't fail anymore.

$ grep '^ok ' ../logs/kselftest-before.log | wc -l && grep '^ok ' ../logs/kselftest-after.log | wc -l
210
211

$ grep '^not ok ' ../logs/kselftest-before.log | wc -l && grep '^not ok ' ../logs/kselftest-after.log | wc -l
56
55

kselftest-before.log
kselftest-after.log

jira VULN-68
cve CVE-2021-4204
commit-author Daniel Borkmann <daniel@iogearbox.net>
commit a672b2e

The bpf_ringbuf_submit() and bpf_ringbuf_discard() have ARG_PTR_TO_ALLOC_MEM
in their bpf_func_proto definition as their first argument, and thus both expect
the result from a prior bpf_ringbuf_reserve() call which has a return type of
RET_PTR_TO_ALLOC_MEM_OR_NULL.

While the non-NULL memory from bpf_ringbuf_reserve() can be passed to other
helpers, the two sinks (bpf_ringbuf_submit(), bpf_ringbuf_discard()) right now
only enforce a register type of PTR_TO_MEM.

This can lead to potential type confusion since it would allow other PTR_TO_MEM
memory to be passed into the two sinks which did not come from bpf_ringbuf_reserve().

Add a new MEM_ALLOC composable type attribute for PTR_TO_MEM, and enforce that:

 - bpf_ringbuf_reserve() returns NULL or PTR_TO_MEM | MEM_ALLOC
 - bpf_ringbuf_submit() and bpf_ringbuf_discard() only take PTR_TO_MEM | MEM_ALLOC
   but not plain PTR_TO_MEM arguments via ARG_PTR_TO_ALLOC_MEM
 - however, other helpers might treat PTR_TO_MEM | MEM_ALLOC as plain PTR_TO_MEM
   to populate the memory area when they use ARG_PTR_TO_{UNINIT_,}MEM in their
   func proto description

Fixes: 457f443 ("bpf: Implement BPF ring buffer and verifier support for it")
	Reported-by: Alexei Starovoitov <ast@kernel.org>
	Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
	Acked-by: John Fastabend <john.fastabend@gmail.com>
	Acked-by: Alexei Starovoitov <ast@kernel.org>
(cherry picked from commit a672b2e)
	Signed-off-by: Pratham Patel <ppatel@ciq.com>
@thefossguy-ciq
Copy link
Author

The LOCALVERSION and the current commit SHA are different because I rebased before a push and also that the branch was called ciqlts8_6-CVE-2021-4204 but I couldn't push because of branch protection rules and now have renamed to {ppatel}_ciqlts8_6-CVE-2021-4204.

Copy link
Collaborator

@bmastbergen bmastbergen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥌 Nice work tracking down the fix.

@PlaidCat
Copy link
Collaborator

The LOCALVERSION and the current commit SHA are different because I rebased before a push and also that the branch was called ciqlts8_6-CVE-2021-4204 but I couldn't push because of branch protection rules and now have renamed to {ppatel}_ciqlts8_6-CVE-2021-4204.

Sorry I am trying to keep this so its easy to identify the user branches and their age so they can be deleted when needed.

@thefossguy-ciq
Copy link
Author

The LOCALVERSION and the current commit SHA are different because I rebased before a push and also that the branch was called ciqlts8_6-CVE-2021-4204 but I couldn't push because of branch protection rules and now have renamed to {ppatel}_ciqlts8_6-CVE-2021-4204.

Sorry I am trying to keep this so its easy to identify the user branches and their age so they can be deleted when needed.

That's totally fine by me. I was stating the reason for the commit hash mismatch between the commit pushed and the one in the build log, nothing more. :)

@thefossguy-ciq
Copy link
Author

Sorry, Maple's comment on the ticket reminded me that I forgot to share the result of running the test from LTP. The test wouldn't run for two reasons.

The first reason was that the test would be skipped if the kernel version was lower than 5.8 (the one actually affected in upstream). That was an easy fix.

diff --git a/testcases/kernel/syscalls/bpf/bpf_prog06.c b/testcases/kernel/syscalls/bpf/bpf_prog06.c
index f701e9599..6d2ca3a81 100644
--- a/testcases/kernel/syscalls/bpf/bpf_prog06.c
+++ b/testcases/kernel/syscalls/bpf/bpf_prog06.c
@@ -132,7 +132,7 @@ static struct tst_test test = {
        .timeout = 20,
        .setup = setup,
        .test_all = run,
-       .min_kver = "5.8",
+       .min_kver = "4.10",
        .taint_check = TST_TAINT_W | TST_TAINT_D,
        .caps = (struct tst_cap []) {
                TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN),

But then, the binary kept dropping the capabilities. It would do that even when executed as root (not sudo, logged in as root). That was an easy fix too.

diff --git a/lib/tst_capability.c b/lib/tst_capability.c
index cb0502e2e..bb3947f99 100644
--- a/lib/tst_capability.c
+++ b/lib/tst_capability.c
@@ -63,7 +63,6 @@ void tst_cap_action(struct tst_cap *cap)

        switch (act) {
        case TST_CAP_DROP:
-               do_cap_drop(pE, mask, cap);
                break;
        case TST_CAP_REQ:
                do_cap_req(pP, pE, mask, cap);

And finally, we have binary execution.

# ./bpf_prog06
tst_buffers.c:57: TINFO: Test is using guarded buffers
tst_test.c:1994: TINFO: LTP version: 20250530-50-g0c99c7915
tst_test.c:1997: TINFO: Tested kernel: 4.18.0-ciqlts8_6-CVE-2021-4204-613e08bfafa7+ #1 SMP Wed Jun 25 19:23:09 IST 2025 x86_64
tst_kconfig.c:88: TINFO: Parsing kernel config '/lib/modules/4.18.0-ciqlts8_6-CVE-2021-4204-613e08bfafa7+/build/.config'
tst_test.c:1817: TINFO: Overall timeout per run is 0h 00m 50s
bpf_common.c:17: TINFO: Raising RLIMIT_MEMLOCK to 2162688
bpf_prog06.c:116: TINFO: Trying to load eBPF with OOB write
0: (18) r1 = 0xffff91a00fdbca00
2: (b7) r2 = 4080
3: (b7) r3 = 0
4: (85) call bpf_ringbuf_reserve#131
5: (55) if r0 != 0x0 goto pc+2

from 5 to 8: R0=alloc_mem(id=0,ref_obj_id=2,off=0,imm=0) R10=fp0 refs=2
8: (17) r0 -= 12240
9: (bf) r1 = r0
10: (b7) r2 = 1
11: (85) call bpf_ringbuf_submit#132
dereference of modified alloc_mem ptr R1 off=-12240 disallowed
processed 11 insns (limit 1000000) max_states_per_insn 0 total_states 1 peak_states 1 mark_read 1

bpf_prog06.c:119: TPASS: Failed verification

Summary:
passed   1
failed   0
broken   0
skipped  0
warnings 0

Since the BPF program exited with -1 (failed verification), it is evident that the verifier is fixed and the CVE is patched.

@thefossguy-ciq thefossguy-ciq changed the title [lts-86] bpf: Fix ringbuf memory type confusion when passing to helpers [ciqlts8_6] bpf: Fix ringbuf memory type confusion when passing to helpers Jun 30, 2025
Copy link
Collaborator

@PlaidCat PlaidCat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@thefossguy-ciq thefossguy-ciq merged commit ab8e5bf into ciqlts8_6 Jun 30, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants