Merge tag 'bpf-next-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next

Pull bpf updates from Alexei Starovoitov:

 - Convert selftests/bpf/test_tc_edt and test_tc_tunnel from .sh to
   test_progs runner (Alexis Lothoré)

 - Convert selftests/bpf/test_xsk to test_progs runner (Bastien
   Curutchet)

 - Replace bpf memory allocator with kmalloc_nolock() in
   bpf_local_storage (Amery Hung), and in bpf streams and range tree
   (Puranjay Mohan)

 - Introduce support for indirect jumps in BPF verifier and x86 JIT
   (Anton Protopopov) and arm64 JIT (Puranjay Mohan)

 - Remove runqslower bpf tool (Hoyeon Lee)

 - Fix corner cases in the verifier to close several syzbot reports
   (Eduard Zingerman, KaFai Wan)

 - Several improvements in deadlock detection in rqspinlock (Kumar
   Kartikeya Dwivedi)

 - Implement "jmp" mode for BPF trampoline and corresponding
   DYNAMIC_FTRACE_WITH_JMP. It improves "fexit" program type performance
   from 80 M/s to 136 M/s. With Steven's Ack. (Menglong Dong)

 - Add ability to test non-linear skbs in BPF_PROG_TEST_RUN (Paul
   Chaignon)

 - Do not let BPF_PROG_TEST_RUN emit invalid GSO types to stack (Daniel
   Borkmann)

 - Generalize buildid reader into bpf_dynptr (Mykyta Yatsenko)

 - Optimize bpf_map_update_elem() for map-in-map types (Ritesh
   Oedayrajsingh Varma)

 - Introduce overwrite mode for BPF ring buffer (Xu Kuohai)

* tag 'bpf-next-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (169 commits)
  bpf: optimize bpf_map_update_elem() for map-in-map types
  bpf: make kprobe_multi_link_prog_run always_inline
  selftests/bpf: do not hardcode target rate in test_tc_edt BPF program
  selftests/bpf: remove test_tc_edt.sh
  selftests/bpf: integrate test_tc_edt into test_progs
  selftests/bpf: rename test_tc_edt.bpf.c section to expose program type
  selftests/bpf: Add success stats to rqspinlock stress test
  rqspinlock: Precede non-head waiter queueing with AA check
  rqspinlock: Disable spinning for trylock fallback
  rqspinlock: Use trylock fallback when per-CPU rqnode is busy
  rqspinlock: Perform AA checks immediately
  rqspinlock: Enclose lock/unlock within lock entry acquisitions
  bpf: Remove runqslower tool
  selftests/bpf: Remove usage of lsm/file_alloc_security in selftest
  bpf: Disable file_alloc_security hook
  bpf: check for insn arrays in check_ptr_alignment
  bpf: force BPF_F_RDONLY_PROG on insn array creation
  bpf: Fix exclusive map memory leak
  selftests/bpf: Make CS length configurable for rqspinlock stress test
  selftests/bpf: Add lock wait time stats to rqspinlock stress test
  ...
This commit is contained in:
Linus Torvalds
2025-12-03 16:54:54 -08:00
157 changed files with 10859 additions and 5005 deletions

View File

@@ -190,6 +190,7 @@ static const char * const map_type_name[] = {
[BPF_MAP_TYPE_USER_RINGBUF] = "user_ringbuf",
[BPF_MAP_TYPE_CGRP_STORAGE] = "cgrp_storage",
[BPF_MAP_TYPE_ARENA] = "arena",
[BPF_MAP_TYPE_INSN_ARRAY] = "insn_array",
};
static const char * const prog_type_name[] = {
@@ -369,6 +370,7 @@ enum reloc_type {
RELO_EXTERN_CALL,
RELO_SUBPROG_ADDR,
RELO_CORE,
RELO_INSN_ARRAY,
};
struct reloc_desc {
@@ -379,7 +381,16 @@ struct reloc_desc {
struct {
int map_idx;
int sym_off;
int ext_idx;
/*
* The following two fields can be unionized, as the
* ext_idx field is used for extern symbols, and the
* sym_size is used for jump tables, which are never
* extern
*/
union {
int ext_idx;
int sym_size;
};
};
};
};
@@ -421,6 +432,11 @@ struct bpf_sec_def {
libbpf_prog_attach_fn_t prog_attach_fn;
};
struct bpf_light_subprog {
__u32 sec_insn_off;
__u32 sub_insn_off;
};
/*
* bpf_prog should be a better name but it has been used in
* linux/filter.h.
@@ -494,6 +510,9 @@ struct bpf_program {
__u32 line_info_cnt;
__u32 prog_flags;
__u8 hash[SHA256_DIGEST_LENGTH];
struct bpf_light_subprog *subprogs;
__u32 subprog_cnt;
};
struct bpf_struct_ops {
@@ -667,6 +686,7 @@ struct elf_state {
int symbols_shndx;
bool has_st_ops;
int arena_data_shndx;
int jumptables_data_shndx;
};
struct usdt_manager;
@@ -738,6 +758,16 @@ struct bpf_object {
void *arena_data;
size_t arena_data_sz;
void *jumptables_data;
size_t jumptables_data_sz;
struct {
struct bpf_program *prog;
int sym_off;
int fd;
} *jumptable_maps;
size_t jumptable_map_cnt;
struct kern_feature_cache *feat_cache;
char *token_path;
int token_fd;
@@ -764,6 +794,7 @@ void bpf_program__unload(struct bpf_program *prog)
zfree(&prog->func_info);
zfree(&prog->line_info);
zfree(&prog->subprogs);
}
static void bpf_program__exit(struct bpf_program *prog)
@@ -2996,7 +3027,7 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
scn = elf_sec_by_idx(obj, obj->efile.btf_maps_shndx);
data = elf_sec_data(obj, scn);
if (!scn || !data) {
if (!data) {
pr_warn("elf: failed to get %s map definitions for %s\n",
MAPS_ELF_SEC, obj->path);
return -EINVAL;
@@ -3942,6 +3973,13 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
} else if (strcmp(name, ARENA_SEC) == 0) {
obj->efile.arena_data = data;
obj->efile.arena_data_shndx = idx;
} else if (strcmp(name, JUMPTABLES_SEC) == 0) {
obj->jumptables_data = malloc(data->d_size);
if (!obj->jumptables_data)
return -ENOMEM;
memcpy(obj->jumptables_data, data->d_buf, data->d_size);
obj->jumptables_data_sz = data->d_size;
obj->efile.jumptables_data_shndx = idx;
} else {
pr_info("elf: skipping unrecognized data section(%d) %s\n",
idx, name);
@@ -4634,6 +4672,16 @@ static int bpf_program__record_reloc(struct bpf_program *prog,
return 0;
}
/* jump table data relocation */
if (shdr_idx == obj->efile.jumptables_data_shndx) {
reloc_desc->type = RELO_INSN_ARRAY;
reloc_desc->insn_idx = insn_idx;
reloc_desc->map_idx = -1;
reloc_desc->sym_off = sym->st_value;
reloc_desc->sym_size = sym->st_size;
return 0;
}
/* generic map reference relocation */
if (type == LIBBPF_MAP_UNSPEC) {
if (!bpf_object__shndx_is_maps(obj, shdr_idx)) {
@@ -6144,6 +6192,157 @@ static void poison_kfunc_call(struct bpf_program *prog, int relo_idx,
insn->imm = POISON_CALL_KFUNC_BASE + ext_idx;
}
static int find_jt_map(struct bpf_object *obj, struct bpf_program *prog, int sym_off)
{
size_t i;
for (i = 0; i < obj->jumptable_map_cnt; i++) {
/*
* This might happen that same offset is used for two different
* programs (as jump tables can be the same). However, for
* different programs different maps should be created.
*/
if (obj->jumptable_maps[i].sym_off == sym_off &&
obj->jumptable_maps[i].prog == prog)
return obj->jumptable_maps[i].fd;
}
return -ENOENT;
}
static int add_jt_map(struct bpf_object *obj, struct bpf_program *prog, int sym_off, int map_fd)
{
size_t cnt = obj->jumptable_map_cnt;
size_t size = sizeof(obj->jumptable_maps[0]);
void *tmp;
tmp = libbpf_reallocarray(obj->jumptable_maps, cnt + 1, size);
if (!tmp)
return -ENOMEM;
obj->jumptable_maps = tmp;
obj->jumptable_maps[cnt].prog = prog;
obj->jumptable_maps[cnt].sym_off = sym_off;
obj->jumptable_maps[cnt].fd = map_fd;
obj->jumptable_map_cnt++;
return 0;
}
static int find_subprog_idx(struct bpf_program *prog, int insn_idx)
{
int i;
for (i = prog->subprog_cnt - 1; i >= 0; i--) {
if (insn_idx >= prog->subprogs[i].sub_insn_off)
return i;
}
return -1;
}
static int create_jt_map(struct bpf_object *obj, struct bpf_program *prog, struct reloc_desc *relo)
{
const __u32 jt_entry_size = 8;
int sym_off = relo->sym_off;
int jt_size = relo->sym_size;
__u32 max_entries = jt_size / jt_entry_size;
__u32 value_size = sizeof(struct bpf_insn_array_value);
struct bpf_insn_array_value val = {};
int subprog_idx;
int map_fd, err;
__u64 insn_off;
__u64 *jt;
__u32 i;
map_fd = find_jt_map(obj, prog, sym_off);
if (map_fd >= 0)
return map_fd;
if (sym_off % jt_entry_size) {
pr_warn("map '.jumptables': jumptable start %d should be multiple of %u\n",
sym_off, jt_entry_size);
return -EINVAL;
}
if (jt_size % jt_entry_size) {
pr_warn("map '.jumptables': jumptable size %d should be multiple of %u\n",
jt_size, jt_entry_size);
return -EINVAL;
}
map_fd = bpf_map_create(BPF_MAP_TYPE_INSN_ARRAY, ".jumptables",
4, value_size, max_entries, NULL);
if (map_fd < 0)
return map_fd;
if (!obj->jumptables_data) {
pr_warn("map '.jumptables': ELF file is missing jump table data\n");
err = -EINVAL;
goto err_close;
}
if (sym_off + jt_size > obj->jumptables_data_sz) {
pr_warn("map '.jumptables': jumptables_data size is %zd, trying to access %d\n",
obj->jumptables_data_sz, sym_off + jt_size);
err = -EINVAL;
goto err_close;
}
subprog_idx = -1; /* main program */
if (relo->insn_idx < 0 || relo->insn_idx >= prog->insns_cnt) {
pr_warn("map '.jumptables': invalid instruction index %d\n", relo->insn_idx);
err = -EINVAL;
goto err_close;
}
if (prog->subprogs)
subprog_idx = find_subprog_idx(prog, relo->insn_idx);
jt = (__u64 *)(obj->jumptables_data + sym_off);
for (i = 0; i < max_entries; i++) {
/*
* The offset should be made to be relative to the beginning of
* the main function, not the subfunction.
*/
insn_off = jt[i]/sizeof(struct bpf_insn);
if (subprog_idx >= 0) {
insn_off -= prog->subprogs[subprog_idx].sec_insn_off;
insn_off += prog->subprogs[subprog_idx].sub_insn_off;
} else {
insn_off -= prog->sec_insn_off;
}
/*
* LLVM-generated jump tables contain u64 records, however
* should contain values that fit in u32.
*/
if (insn_off > UINT32_MAX) {
pr_warn("map '.jumptables': invalid jump table value 0x%llx at offset %d\n",
(long long)jt[i], sym_off + i * jt_entry_size);
err = -EINVAL;
goto err_close;
}
val.orig_off = insn_off;
err = bpf_map_update_elem(map_fd, &i, &val, 0);
if (err)
goto err_close;
}
err = bpf_map_freeze(map_fd);
if (err)
goto err_close;
err = add_jt_map(obj, prog, sym_off, map_fd);
if (err)
goto err_close;
return map_fd;
err_close:
close(map_fd);
return err;
}
/* Relocate data references within program code:
* - map references;
* - global variable references;
@@ -6235,6 +6434,20 @@ bpf_object__relocate_data(struct bpf_object *obj, struct bpf_program *prog)
case RELO_CORE:
/* will be handled by bpf_program_record_relos() */
break;
case RELO_INSN_ARRAY: {
int map_fd;
map_fd = create_jt_map(obj, prog, relo);
if (map_fd < 0) {
pr_warn("prog '%s': relo #%d: can't create jump table: sym_off %u\n",
prog->name, i, relo->sym_off);
return map_fd;
}
insn[0].src_reg = BPF_PSEUDO_MAP_VALUE;
insn->imm = map_fd;
insn->off = 0;
}
break;
default:
pr_warn("prog '%s': relo #%d: bad relo type %d\n",
prog->name, i, relo->type);
@@ -6432,36 +6645,62 @@ static int append_subprog_relos(struct bpf_program *main_prog, struct bpf_progra
return 0;
}
static int save_subprog_offsets(struct bpf_program *main_prog, struct bpf_program *subprog)
{
size_t size = sizeof(main_prog->subprogs[0]);
int cnt = main_prog->subprog_cnt;
void *tmp;
tmp = libbpf_reallocarray(main_prog->subprogs, cnt + 1, size);
if (!tmp)
return -ENOMEM;
main_prog->subprogs = tmp;
main_prog->subprogs[cnt].sec_insn_off = subprog->sec_insn_off;
main_prog->subprogs[cnt].sub_insn_off = subprog->sub_insn_off;
main_prog->subprog_cnt++;
return 0;
}
static int
bpf_object__append_subprog_code(struct bpf_object *obj, struct bpf_program *main_prog,
struct bpf_program *subprog)
{
struct bpf_insn *insns;
size_t new_cnt;
int err;
struct bpf_insn *insns;
size_t new_cnt;
int err;
subprog->sub_insn_off = main_prog->insns_cnt;
subprog->sub_insn_off = main_prog->insns_cnt;
new_cnt = main_prog->insns_cnt + subprog->insns_cnt;
insns = libbpf_reallocarray(main_prog->insns, new_cnt, sizeof(*insns));
if (!insns) {
pr_warn("prog '%s': failed to realloc prog code\n", main_prog->name);
return -ENOMEM;
}
main_prog->insns = insns;
main_prog->insns_cnt = new_cnt;
new_cnt = main_prog->insns_cnt + subprog->insns_cnt;
insns = libbpf_reallocarray(main_prog->insns, new_cnt, sizeof(*insns));
if (!insns) {
pr_warn("prog '%s': failed to realloc prog code\n", main_prog->name);
return -ENOMEM;
}
main_prog->insns = insns;
main_prog->insns_cnt = new_cnt;
memcpy(main_prog->insns + subprog->sub_insn_off, subprog->insns,
subprog->insns_cnt * sizeof(*insns));
memcpy(main_prog->insns + subprog->sub_insn_off, subprog->insns,
subprog->insns_cnt * sizeof(*insns));
pr_debug("prog '%s': added %zu insns from sub-prog '%s'\n",
main_prog->name, subprog->insns_cnt, subprog->name);
pr_debug("prog '%s': added %zu insns from sub-prog '%s'\n",
main_prog->name, subprog->insns_cnt, subprog->name);
/* The subprog insns are now appended. Append its relos too. */
err = append_subprog_relos(main_prog, subprog);
if (err)
return err;
return 0;
/* The subprog insns are now appended. Append its relos too. */
err = append_subprog_relos(main_prog, subprog);
if (err)
return err;
err = save_subprog_offsets(main_prog, subprog);
if (err) {
pr_warn("prog '%s': failed to add subprog offsets: %s\n",
main_prog->name, errstr(err));
return err;
}
return 0;
}
static int
@@ -9228,6 +9467,13 @@ void bpf_object__close(struct bpf_object *obj)
zfree(&obj->arena_data);
zfree(&obj->jumptables_data);
obj->jumptables_data_sz = 0;
for (i = 0; i < obj->jumptable_map_cnt; i++)
close(obj->jumptable_maps[i].fd);
zfree(&obj->jumptable_maps);
free(obj);
}
@@ -13854,8 +14100,8 @@ int bpf_program__set_attach_target(struct bpf_program *prog,
return libbpf_err(-EINVAL);
if (attach_prog_fd && !attach_func_name) {
/* remember attach_prog_fd and let bpf_program__load() find
* BTF ID during the program load
/* Store attach_prog_fd. The BTF ID will be resolved later during
* the normal object/program load phase.
*/
prog->attach_prog_fd = attach_prog_fd;
return 0;