From f30fc8e1b6059df94d54b2a905cbb818c9d494e1 Mon Sep 17 00:00:00 2001 From: SeungJong Ha Date: Tue, 25 Aug 2026 17:08:33 +0900 Subject: [PATCH 1/2] bpf, rex: call security_bpf_prog_load() from the Rex load paths bpf_prog_load_rex() and bpf_prog_load_rex_base() free the LSM blob on their error path but never allocate it. Neither calls security_bpf_prog_load(), while upstream's bpf_prog_load() calls both: bpf_prog_load() load hook + free hook bpf_prog_load_rex() free hook only bpf_prog_load_rex_base() free hook only With CONFIG_SECURITY_SELINUX=y, prog->aux->security therefore stays NULL and selinux_bpf_prog() dereferences it. The first Rex program load takes the machine down: BUG: kernel NULL pointer dereference, address: 0000000000000000 Oops: Oops: 0000 [#1] SMP RIP: 0010:selinux_bpf_prog+0x1e/0x50 Call Trace: bpf_prog_load_rex_base+0xb1c/0xe50 Kernel panic - not syncing: Fatal exception scripts/q-script/.config has CONFIG_SECURITY_SELINUX off, which is why this has not been hit here. On a kernel that enables it, no Rex program can be loaded at all. Add the call where bpf_prog_load() makes it: right after bpf_obj_name_cpy(), branching to the same free_prog_sec label. token is NULL because neither Rex path takes one. Reproduced on this branch, on 905dbe3ad37b. CONFIG_SECURITY is already set and CONFIG_LSM already lists selinux, so enabling CONFIG_SECURITY_SELINUX in scripts/q-script/.config is the only change needed -- it adds no field to struct bpf_prog_aux, so the same librex and the same samples/bmc binary are used on both sides. Booted under virtme-ng, loading samples/bmc via BPF_PROG_LOAD_REX_BASE: before panic as above, on the first load after loader stays up, program attaches to XDP, xdp_tx_filter pins under /sys/fs/bpf Fixes: fea5e301f0df ("BPF_PROG_LOAD_DJW") Fixes: 94373bb5f42d ("implement subprog-loading functions to support multiple programs in the same file") Assisted-by: Claude:claude-opus-5-1m Signed-off-by: SeungJong Ha --- kernel/bpf/syscall.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 0563db3b8ebfc7..d8aa810e03cfd4 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -3285,6 +3285,10 @@ static int bpf_prog_load_rex(union bpf_attr *attr, bpfptr_t uattr) if (err < 0) goto free_prog_sec; + err = security_bpf_prog_load(prog, attr, NULL, uattr.is_kernel); + if (err) + goto free_prog_sec; + prog->no_bpf = 1; /* This gets the refcnt */ @@ -3811,6 +3815,10 @@ static int bpf_prog_load_rex_base(union bpf_attr *attr, bpfptr_t uattr) if (err < 0) goto free_prog_sec; + err = security_bpf_prog_load(prog, attr, NULL, uattr.is_kernel); + if (err) + goto free_prog_sec; + bpf_get_trace_printk_proto(); filp = fget(attr->rustfd); From ea571867f056baf0e688d215e8f65fce4dcf91ea Mon Sep 17 00:00:00 2001 From: SeungJong Ha Date: Tue, 25 Aug 2026 17:08:47 +0900 Subject: [PATCH 2/2] bpf, rex: sync union bpf_attr into the tools/ uapi header Rex puts a 64-byte union into union bpf_attr's prog_load struct -- map_offs, dyn_relas, the symbol tables, rustfd and the base-prog pair -- in front of fd_array, and adds BPF_PROG_TYPE_REX_BASE to enum bpf_prog_type. Both went into include/uapi/linux/bpf.h only. The tools/ copy did get the new bpf_cmd values (81d257d1fabc), so libbpf can issue the Rex commands while laying bpf_attr out the old way. Every field from fd_array on is then at a different offset in libbpf than in the kernel: kernel tools (before) fd_array 200 136 core_relos 208 144 log_true_size 220 156 sizeof(bpf_attr) 248 184 libbpf writes fd_array at 136, the verifier reads 0 at 200, and any program calling a kfunc from a module is rejected before its first instruction: kfunc offset > 0 without fd_array is invalid failed to find BTF for kernel function The module itself is fine -- loaded, kfuncs registered, BTF present in /sys/kernel/btf -- which makes that message misleading. This affects every module kfunc on a Rex kernel; programs that use no module kfunc are unaffected, which is why it can hide for a long time. Copy both into tools/, which is what the kernel does with tools/ copies anyway. After this the two headers agree on all four offsets above. struct rex_rela_dyn, rex_dyn_sym and rex_text_sym stay out: they carry __user annotations that the tools/ copy uses nowhere, and they are separate types rather than part of union bpf_attr, so they do not affect its layout. Syncing them would need the annotations stripped, which is a separate decision. The trailing whitespace on the map_cnt line is dropped on both sides so the copies are identical rather than identical-modulo-whitespace. Fixes: 0fad382d08c7 ("map support") Assisted-by: Claude:claude-opus-5-1m Signed-off-by: SeungJong Ha --- include/uapi/linux/bpf.h | 2 +- tools/include/uapi/linux/bpf.h | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 05d480bd0b5d62..d4bab005e2dcac 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -1647,7 +1647,7 @@ union bpf_attr { __aligned_u64 text_syms; /* ptr to text sym info entries */ __aligned_u64 nr_text_syms; /* nr of text sym info entries */ __u32 rustfd; /* file descriptor of Rust Program */ - __u32 map_cnt; /* length map reloc array */ + __u32 map_cnt; /* length map reloc array */ }; struct { __aligned_u64 prog_offset; /* offset of prog in base */ diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 385852749758b6..fe04c94fb76520 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -1094,6 +1094,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_SK_LOOKUP, BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */ BPF_PROG_TYPE_NETFILTER, + BPF_PROG_TYPE_REX_BASE, __MAX_BPF_PROG_TYPE }; @@ -1619,6 +1620,23 @@ union bpf_attr { __u32 attach_btf_obj_fd; }; __u32 core_relo_cnt; /* number of bpf_core_relo */ + union { + struct { + __aligned_u64 map_offs; /* offsets of map relocs */ + __aligned_u64 dyn_relas; /* ptr to dynamic rela info */ + __aligned_u64 nr_dyn_relas; /* nr of dyn rela entries */ + __aligned_u64 dyn_syms; /* ptr to dyn sym entries */ + __aligned_u64 nr_dyn_syms; /* nr of dyn sym entries */ + __aligned_u64 text_syms; /* ptr to text sym info entries */ + __aligned_u64 nr_text_syms; /* nr of text sym info entries */ + __u32 rustfd; /* file descriptor of Rust Program */ + __u32 map_cnt; /* length map reloc array */ + }; + struct { + __aligned_u64 prog_offset; /* offset of prog in base */ + __u32 base_prog_fd; /* fd of the base prog */ + }; + }; __aligned_u64 fd_array; /* array of FDs */ __aligned_u64 core_relos; __u32 core_relo_rec_size; /* sizeof(struct bpf_core_relo) */