Skip to content

Commit 276dcdd

Browse files
author
Alexei Starovoitov
committed
Merge branch 'Provide bpf_for() and bpf_for_each() by libbpf'
Andrii Nakryiko says: ==================== This patch set moves bpf_for(), bpf_for_each(), and bpf_repeat() macros from selftests-internal bpf_misc.h header to libbpf-provided bpf_helpers.h header. To do this in a way to allow users to feature-detect and guard such bpf_for()/bpf_for_each() uses on old kernels we also extend libbpf to improve unresolved kfunc calls handling and reporting. This lets us mark bpf_iter_num_{new,next,destroy}() declarations as __weak, and thus not fail program loading outright if such kfuncs are missing on the host kernel. Patches #1 and #2 do some simple clean ups and logging improvements. Patch #3 adds kfunc call poisoning and log fixup logic and is the hear of this patch set, effectively. Patch #4 adds selftest for this logic. Patches #4 and #5 move bpf_for()/bpf_for_each()/bpf_repeat() into bpf_helpers.h header and mark kfuncs as __weak to allow users to feature-detect and guard their uses. ==================== Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2 parents 49859de + 94dccba commit 276dcdd

File tree

5 files changed

+232
-122
lines changed

5 files changed

+232
-122
lines changed

tools/lib/bpf/bpf_helpers.h

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,4 +291,107 @@ enum libbpf_tristate {
291291
/* Helper macro to print out debug messages */
292292
#define bpf_printk(fmt, args...) ___bpf_pick_printk(args)(fmt, ##args)
293293

294+
struct bpf_iter_num;
295+
296+
extern int bpf_iter_num_new(struct bpf_iter_num *it, int start, int end) __weak __ksym;
297+
extern int *bpf_iter_num_next(struct bpf_iter_num *it) __weak __ksym;
298+
extern void bpf_iter_num_destroy(struct bpf_iter_num *it) __weak __ksym;
299+
300+
#ifndef bpf_for_each
301+
/* bpf_for_each(iter_type, cur_elem, args...) provides generic construct for
302+
* using BPF open-coded iterators without having to write mundane explicit
303+
* low-level loop logic. Instead, it provides for()-like generic construct
304+
* that can be used pretty naturally. E.g., for some hypothetical cgroup
305+
* iterator, you'd write:
306+
*
307+
* struct cgroup *cg, *parent_cg = <...>;
308+
*
309+
* bpf_for_each(cgroup, cg, parent_cg, CG_ITER_CHILDREN) {
310+
* bpf_printk("Child cgroup id = %d", cg->cgroup_id);
311+
* if (cg->cgroup_id == 123)
312+
* break;
313+
* }
314+
*
315+
* I.e., it looks almost like high-level for each loop in other languages,
316+
* supports continue/break, and is verifiable by BPF verifier.
317+
*
318+
* For iterating integers, the difference betwen bpf_for_each(num, i, N, M)
319+
* and bpf_for(i, N, M) is in that bpf_for() provides additional proof to
320+
* verifier that i is in [N, M) range, and in bpf_for_each() case i is `int
321+
* *`, not just `int`. So for integers bpf_for() is more convenient.
322+
*
323+
* Note: this macro relies on C99 feature of allowing to declare variables
324+
* inside for() loop, bound to for() loop lifetime. It also utilizes GCC
325+
* extension: __attribute__((cleanup(<func>))), supported by both GCC and
326+
* Clang.
327+
*/
328+
#define bpf_for_each(type, cur, args...) for ( \
329+
/* initialize and define destructor */ \
330+
struct bpf_iter_##type ___it __attribute__((aligned(8), /* enforce, just in case */, \
331+
cleanup(bpf_iter_##type##_destroy))), \
332+
/* ___p pointer is just to call bpf_iter_##type##_new() *once* to init ___it */ \
333+
*___p __attribute__((unused)) = ( \
334+
bpf_iter_##type##_new(&___it, ##args), \
335+
/* this is a workaround for Clang bug: it currently doesn't emit BTF */ \
336+
/* for bpf_iter_##type##_destroy() when used from cleanup() attribute */ \
337+
(void)bpf_iter_##type##_destroy, (void *)0); \
338+
/* iteration and termination check */ \
339+
(((cur) = bpf_iter_##type##_next(&___it))); \
340+
)
341+
#endif /* bpf_for_each */
342+
343+
#ifndef bpf_for
344+
/* bpf_for(i, start, end) implements a for()-like looping construct that sets
345+
* provided integer variable *i* to values starting from *start* through,
346+
* but not including, *end*. It also proves to BPF verifier that *i* belongs
347+
* to range [start, end), so this can be used for accessing arrays without
348+
* extra checks.
349+
*
350+
* Note: *start* and *end* are assumed to be expressions with no side effects
351+
* and whose values do not change throughout bpf_for() loop execution. They do
352+
* not have to be statically known or constant, though.
353+
*
354+
* Note: similarly to bpf_for_each(), it relies on C99 feature of declaring for()
355+
* loop bound variables and cleanup attribute, supported by GCC and Clang.
356+
*/
357+
#define bpf_for(i, start, end) for ( \
358+
/* initialize and define destructor */ \
359+
struct bpf_iter_num ___it __attribute__((aligned(8), /* enforce, just in case */ \
360+
cleanup(bpf_iter_num_destroy))), \
361+
/* ___p pointer is necessary to call bpf_iter_num_new() *once* to init ___it */ \
362+
*___p __attribute__((unused)) = ( \
363+
bpf_iter_num_new(&___it, (start), (end)), \
364+
/* this is a workaround for Clang bug: it currently doesn't emit BTF */ \
365+
/* for bpf_iter_num_destroy() when used from cleanup() attribute */ \
366+
(void)bpf_iter_num_destroy, (void *)0); \
367+
({ \
368+
/* iteration step */ \
369+
int *___t = bpf_iter_num_next(&___it); \
370+
/* termination and bounds check */ \
371+
(___t && ((i) = *___t, (i) >= (start) && (i) < (end))); \
372+
}); \
373+
)
374+
#endif /* bpf_for */
375+
376+
#ifndef bpf_repeat
377+
/* bpf_repeat(N) performs N iterations without exposing iteration number
378+
*
379+
* Note: similarly to bpf_for_each(), it relies on C99 feature of declaring for()
380+
* loop bound variables and cleanup attribute, supported by GCC and Clang.
381+
*/
382+
#define bpf_repeat(N) for ( \
383+
/* initialize and define destructor */ \
384+
struct bpf_iter_num ___it __attribute__((aligned(8), /* enforce, just in case */ \
385+
cleanup(bpf_iter_num_destroy))), \
386+
/* ___p pointer is necessary to call bpf_iter_num_new() *once* to init ___it */ \
387+
*___p __attribute__((unused)) = ( \
388+
bpf_iter_num_new(&___it, 0, (N)), \
389+
/* this is a workaround for Clang bug: it currently doesn't emit BTF */ \
390+
/* for bpf_iter_num_destroy() when used from cleanup() attribute */ \
391+
(void)bpf_iter_num_destroy, (void *)0); \
392+
bpf_iter_num_next(&___it); \
393+
/* nothing here */ \
394+
)
395+
#endif /* bpf_repeat */
396+
294397
#endif

tools/lib/bpf/libbpf.c

Lines changed: 88 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ struct reloc_desc {
333333
struct {
334334
int map_idx;
335335
int sym_off;
336+
int ext_idx;
336337
};
337338
};
338339
};
@@ -4042,7 +4043,7 @@ static int bpf_program__record_reloc(struct bpf_program *prog,
40424043
else
40434044
reloc_desc->type = RELO_EXTERN_LD64;
40444045
reloc_desc->insn_idx = insn_idx;
4045-
reloc_desc->sym_off = i; /* sym_off stores extern index */
4046+
reloc_desc->ext_idx = i;
40464047
return 0;
40474048
}
40484049

@@ -5811,8 +5812,8 @@ bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path)
58115812
}
58125813

58135814
/* base map load ldimm64 special constant, used also for log fixup logic */
5814-
#define MAP_LDIMM64_POISON_BASE 2001000000
5815-
#define MAP_LDIMM64_POISON_PFX "200100"
5815+
#define POISON_LDIMM64_MAP_BASE 2001000000
5816+
#define POISON_LDIMM64_MAP_PFX "200100"
58165817

58175818
static void poison_map_ldimm64(struct bpf_program *prog, int relo_idx,
58185819
int insn_idx, struct bpf_insn *insn,
@@ -5834,12 +5835,36 @@ static void poison_map_ldimm64(struct bpf_program *prog, int relo_idx,
58345835
* invalid func unknown#2001000123
58355836
* where lower 123 is map index into obj->maps[] array
58365837
*/
5837-
insn->imm = MAP_LDIMM64_POISON_BASE + map_idx;
5838+
insn->imm = POISON_LDIMM64_MAP_BASE + map_idx;
58385839

58395840
insn++;
58405841
}
58415842
}
58425843

5844+
/* unresolved kfunc call special constant, used also for log fixup logic */
5845+
#define POISON_CALL_KFUNC_BASE 2002000000
5846+
#define POISON_CALL_KFUNC_PFX "2002"
5847+
5848+
static void poison_kfunc_call(struct bpf_program *prog, int relo_idx,
5849+
int insn_idx, struct bpf_insn *insn,
5850+
int ext_idx, const struct extern_desc *ext)
5851+
{
5852+
pr_debug("prog '%s': relo #%d: poisoning insn #%d that calls kfunc '%s'\n",
5853+
prog->name, relo_idx, insn_idx, ext->name);
5854+
5855+
/* we turn kfunc call into invalid helper call with identifiable constant */
5856+
insn->code = BPF_JMP | BPF_CALL;
5857+
insn->dst_reg = 0;
5858+
insn->src_reg = 0;
5859+
insn->off = 0;
5860+
/* if this instruction is reachable (not a dead code),
5861+
* verifier will complain with something like:
5862+
* invalid func unknown#2001000123
5863+
* where lower 123 is extern index into obj->externs[] array
5864+
*/
5865+
insn->imm = POISON_CALL_KFUNC_BASE + ext_idx;
5866+
}
5867+
58435868
/* Relocate data references within program code:
58445869
* - map references;
58455870
* - global variable references;
@@ -5885,7 +5910,7 @@ bpf_object__relocate_data(struct bpf_object *obj, struct bpf_program *prog)
58855910
}
58865911
break;
58875912
case RELO_EXTERN_LD64:
5888-
ext = &obj->externs[relo->sym_off];
5913+
ext = &obj->externs[relo->ext_idx];
58895914
if (ext->type == EXT_KCFG) {
58905915
if (obj->gen_loader) {
58915916
insn[0].src_reg = BPF_PSEUDO_MAP_IDX_VALUE;
@@ -5907,14 +5932,14 @@ bpf_object__relocate_data(struct bpf_object *obj, struct bpf_program *prog)
59075932
}
59085933
break;
59095934
case RELO_EXTERN_CALL:
5910-
ext = &obj->externs[relo->sym_off];
5935+
ext = &obj->externs[relo->ext_idx];
59115936
insn[0].src_reg = BPF_PSEUDO_KFUNC_CALL;
59125937
if (ext->is_set) {
59135938
insn[0].imm = ext->ksym.kernel_btf_id;
59145939
insn[0].off = ext->ksym.btf_fd_idx;
5915-
} else { /* unresolved weak kfunc */
5916-
insn[0].imm = 0;
5917-
insn[0].off = 0;
5940+
} else { /* unresolved weak kfunc call */
5941+
poison_kfunc_call(prog, i, relo->insn_idx, insn,
5942+
relo->ext_idx, ext);
59185943
}
59195944
break;
59205945
case RELO_SUBPROG_ADDR:
@@ -7022,13 +7047,13 @@ static void fixup_log_missing_map_load(struct bpf_program *prog,
70227047
char *buf, size_t buf_sz, size_t log_sz,
70237048
char *line1, char *line2, char *line3)
70247049
{
7025-
/* Expected log for failed and not properly guarded CO-RE relocation:
7050+
/* Expected log for failed and not properly guarded map reference:
70267051
* line1 -> 123: (85) call unknown#2001000345
70277052
* line2 -> invalid func unknown#2001000345
70287053
* line3 -> <anything else or end of buffer>
70297054
*
70307055
* "123" is the index of the instruction that was poisoned.
7031-
* "345" in "2001000345" are map index in obj->maps to fetch map name.
7056+
* "345" in "2001000345" is a map index in obj->maps to fetch map name.
70327057
*/
70337058
struct bpf_object *obj = prog->obj;
70347059
const struct bpf_map *map;
@@ -7038,7 +7063,7 @@ static void fixup_log_missing_map_load(struct bpf_program *prog,
70387063
if (sscanf(line1, "%d: (%*d) call unknown#%d\n", &insn_idx, &map_idx) != 2)
70397064
return;
70407065

7041-
map_idx -= MAP_LDIMM64_POISON_BASE;
7066+
map_idx -= POISON_LDIMM64_MAP_BASE;
70427067
if (map_idx < 0 || map_idx >= obj->nr_maps)
70437068
return;
70447069
map = &obj->maps[map_idx];
@@ -7051,6 +7076,39 @@ static void fixup_log_missing_map_load(struct bpf_program *prog,
70517076
patch_log(buf, buf_sz, log_sz, line1, line3 - line1, patch);
70527077
}
70537078

7079+
static void fixup_log_missing_kfunc_call(struct bpf_program *prog,
7080+
char *buf, size_t buf_sz, size_t log_sz,
7081+
char *line1, char *line2, char *line3)
7082+
{
7083+
/* Expected log for failed and not properly guarded kfunc call:
7084+
* line1 -> 123: (85) call unknown#2002000345
7085+
* line2 -> invalid func unknown#2002000345
7086+
* line3 -> <anything else or end of buffer>
7087+
*
7088+
* "123" is the index of the instruction that was poisoned.
7089+
* "345" in "2002000345" is an extern index in obj->externs to fetch kfunc name.
7090+
*/
7091+
struct bpf_object *obj = prog->obj;
7092+
const struct extern_desc *ext;
7093+
int insn_idx, ext_idx;
7094+
char patch[128];
7095+
7096+
if (sscanf(line1, "%d: (%*d) call unknown#%d\n", &insn_idx, &ext_idx) != 2)
7097+
return;
7098+
7099+
ext_idx -= POISON_CALL_KFUNC_BASE;
7100+
if (ext_idx < 0 || ext_idx >= obj->nr_extern)
7101+
return;
7102+
ext = &obj->externs[ext_idx];
7103+
7104+
snprintf(patch, sizeof(patch),
7105+
"%d: <invalid kfunc call>\n"
7106+
"kfunc '%s' is referenced but wasn't resolved\n",
7107+
insn_idx, ext->name);
7108+
7109+
patch_log(buf, buf_sz, log_sz, line1, line3 - line1, patch);
7110+
}
7111+
70547112
static void fixup_verifier_log(struct bpf_program *prog, char *buf, size_t buf_sz)
70557113
{
70567114
/* look for familiar error patterns in last N lines of the log */
@@ -7070,23 +7128,33 @@ static void fixup_verifier_log(struct bpf_program *prog, char *buf, size_t buf_s
70707128
if (!cur_line)
70717129
return;
70727130

7073-
/* failed CO-RE relocation case */
70747131
if (str_has_pfx(cur_line, "invalid func unknown#195896080\n")) {
70757132
prev_line = find_prev_line(buf, cur_line);
70767133
if (!prev_line)
70777134
continue;
70787135

7136+
/* failed CO-RE relocation case */
70797137
fixup_log_failed_core_relo(prog, buf, buf_sz, log_sz,
70807138
prev_line, cur_line, next_line);
70817139
return;
7082-
} else if (str_has_pfx(cur_line, "invalid func unknown#"MAP_LDIMM64_POISON_PFX)) {
7140+
} else if (str_has_pfx(cur_line, "invalid func unknown#"POISON_LDIMM64_MAP_PFX)) {
70837141
prev_line = find_prev_line(buf, cur_line);
70847142
if (!prev_line)
70857143
continue;
70867144

7145+
/* reference to uncreated BPF map */
70877146
fixup_log_missing_map_load(prog, buf, buf_sz, log_sz,
70887147
prev_line, cur_line, next_line);
70897148
return;
7149+
} else if (str_has_pfx(cur_line, "invalid func unknown#"POISON_CALL_KFUNC_PFX)) {
7150+
prev_line = find_prev_line(buf, cur_line);
7151+
if (!prev_line)
7152+
continue;
7153+
7154+
/* reference to unresolved kfunc */
7155+
fixup_log_missing_kfunc_call(prog, buf, buf_sz, log_sz,
7156+
prev_line, cur_line, next_line);
7157+
return;
70907158
}
70917159
}
70927160
}
@@ -7098,7 +7166,7 @@ static int bpf_program_record_relos(struct bpf_program *prog)
70987166

70997167
for (i = 0; i < prog->nr_reloc; i++) {
71007168
struct reloc_desc *relo = &prog->reloc_desc[i];
7101-
struct extern_desc *ext = &obj->externs[relo->sym_off];
7169+
struct extern_desc *ext = &obj->externs[relo->ext_idx];
71027170
int kind;
71037171

71047172
switch (relo->type) {
@@ -7536,8 +7604,9 @@ static int bpf_object__resolve_ksym_func_btf_id(struct bpf_object *obj,
75367604
ret = bpf_core_types_are_compat(obj->btf, local_func_proto_id,
75377605
kern_btf, kfunc_proto_id);
75387606
if (ret <= 0) {
7539-
pr_warn("extern (func ksym) '%s': func_proto [%d] incompatible with kernel [%d]\n",
7540-
ext->name, local_func_proto_id, kfunc_proto_id);
7607+
pr_warn("extern (func ksym) '%s': func_proto [%d] incompatible with %s [%d]\n",
7608+
ext->name, local_func_proto_id,
7609+
mod_btf ? mod_btf->name : "vmlinux", kfunc_proto_id);
75417610
return -EINVAL;
75427611
}
75437612

@@ -7571,8 +7640,8 @@ static int bpf_object__resolve_ksym_func_btf_id(struct bpf_object *obj,
75717640
* {kernel_btf_id, kernel_btf_obj_fd} -> fixup ld_imm64.
75727641
*/
75737642
ext->ksym.kernel_btf_obj_fd = mod_btf ? mod_btf->fd : 0;
7574-
pr_debug("extern (func ksym) '%s': resolved to kernel [%d]\n",
7575-
ext->name, kfunc_id);
7643+
pr_debug("extern (func ksym) '%s': resolved to %s [%d]\n",
7644+
ext->name, mod_btf ? mod_btf->name : "vmlinux", kfunc_id);
75767645

75777646
return 0;
75787647
}

tools/testing/selftests/bpf/prog_tests/log_fixup.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,35 @@ static void missing_map(void)
135135
test_log_fixup__destroy(skel);
136136
}
137137

138+
static void missing_kfunc(void)
139+
{
140+
char log_buf[8 * 1024];
141+
struct test_log_fixup* skel;
142+
int err;
143+
144+
skel = test_log_fixup__open();
145+
if (!ASSERT_OK_PTR(skel, "skel_open"))
146+
return;
147+
148+
bpf_program__set_autoload(skel->progs.use_missing_kfunc, true);
149+
bpf_program__set_log_buf(skel->progs.use_missing_kfunc, log_buf, sizeof(log_buf));
150+
151+
err = test_log_fixup__load(skel);
152+
if (!ASSERT_ERR(err, "load_fail"))
153+
goto cleanup;
154+
155+
ASSERT_HAS_SUBSTR(log_buf,
156+
"0: <invalid kfunc call>\n"
157+
"kfunc 'bpf_nonexistent_kfunc' is referenced but wasn't resolved\n",
158+
"log_buf");
159+
160+
if (env.verbosity > VERBOSE_NONE)
161+
printf("LOG: \n=================\n%s=================\n", log_buf);
162+
163+
cleanup:
164+
test_log_fixup__destroy(skel);
165+
}
166+
138167
void test_log_fixup(void)
139168
{
140169
if (test__start_subtest("bad_core_relo_trunc_none"))
@@ -147,4 +176,6 @@ void test_log_fixup(void)
147176
bad_core_relo_subprog();
148177
if (test__start_subtest("missing_map"))
149178
missing_map();
179+
if (test__start_subtest("missing_kfunc"))
180+
missing_kfunc();
150181
}

0 commit comments

Comments
 (0)