Skip to content

Commit 76ec90a

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
libbpf: warn on unexpected __arg_ctx type when rewriting BTF
On kernel that don't support arg:ctx tag, before adjusting global subprog BTF information to match kernel's expected canonical type names, make sure that types used by user are meaningful, and if not, warn and don't do BTF adjustments. This is similar to checks that kernel performs, but narrower in scope, as only a small subset of BPF program types can be accommodated by libbpf using canonical type names. Libbpf unconditionally allows `struct pt_regs *` for perf_event program types, unlike kernel, which supports that conditionally on architecture. This is done to keep things simple and not cause unnecessary false positives. This seems like a minor and harmless deviation, which in real-world programs will be caught by kernels with arg:ctx tag support anyways. So KISS principle. This logic is hard to test (especially on latest kernels), so manual testing was performed instead. Libbpf emitted the following warning for perf_event program with wrong context argument type: libbpf: prog 'arg_tag_ctx_perf': subprog 'subprog_ctx_tag' arg#0 is expected to be of `struct bpf_perf_event_data *` type Signed-off-by: Andrii Nakryiko <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 989410c commit 76ec90a

File tree

1 file changed

+66
-9
lines changed

1 file changed

+66
-9
lines changed

tools/lib/bpf/libbpf.c

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6695,6 +6695,67 @@ static struct {
66956695
/* all other program types don't have "named" context structs */
66966696
};
66976697

6698+
static bool need_func_arg_type_fixup(const struct btf *btf, const struct bpf_program *prog,
6699+
const char *subprog_name, int arg_idx,
6700+
int arg_type_id, const char *ctx_name)
6701+
{
6702+
const struct btf_type *t;
6703+
const char *tname;
6704+
6705+
/* check if existing parameter already matches verifier expectations */
6706+
t = skip_mods_and_typedefs(btf, arg_type_id, NULL);
6707+
if (!btf_is_ptr(t))
6708+
goto out_warn;
6709+
6710+
/* typedef bpf_user_pt_regs_t is a special PITA case, valid for kprobe
6711+
* and perf_event programs, so check this case early on and forget
6712+
* about it for subsequent checks
6713+
*/
6714+
while (btf_is_mod(t))
6715+
t = btf__type_by_id(btf, t->type);
6716+
if (btf_is_typedef(t) &&
6717+
(prog->type == BPF_PROG_TYPE_KPROBE || prog->type == BPF_PROG_TYPE_PERF_EVENT)) {
6718+
tname = btf__str_by_offset(btf, t->name_off) ?: "<anon>";
6719+
if (strcmp(tname, "bpf_user_pt_regs_t") == 0)
6720+
return false; /* canonical type for kprobe/perf_event */
6721+
}
6722+
6723+
/* now we can ignore typedefs moving forward */
6724+
t = skip_mods_and_typedefs(btf, t->type, NULL);
6725+
6726+
/* if it's `void *`, definitely fix up BTF info */
6727+
if (btf_is_void(t))
6728+
return true;
6729+
6730+
/* if it's already proper canonical type, no need to fix up */
6731+
tname = btf__str_by_offset(btf, t->name_off) ?: "<anon>";
6732+
if (btf_is_struct(t) && strcmp(tname, ctx_name) == 0)
6733+
return false;
6734+
6735+
/* special cases */
6736+
switch (prog->type) {
6737+
case BPF_PROG_TYPE_KPROBE:
6738+
case BPF_PROG_TYPE_PERF_EVENT:
6739+
/* `struct pt_regs *` is expected, but we need to fix up */
6740+
if (btf_is_struct(t) && strcmp(tname, "pt_regs") == 0)
6741+
return true;
6742+
break;
6743+
case BPF_PROG_TYPE_RAW_TRACEPOINT:
6744+
case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
6745+
/* allow u64* as ctx */
6746+
if (btf_is_int(t) && t->size == 8)
6747+
return true;
6748+
break;
6749+
default:
6750+
break;
6751+
}
6752+
6753+
out_warn:
6754+
pr_warn("prog '%s': subprog '%s' arg#%d is expected to be of `struct %s *` type\n",
6755+
prog->name, subprog_name, arg_idx, ctx_name);
6756+
return false;
6757+
}
6758+
66986759
static int clone_func_btf_info(struct btf *btf, int orig_fn_id, struct bpf_program *prog)
66996760
{
67006761
int fn_id, fn_proto_id, ret_type_id, orig_proto_id;
@@ -6829,7 +6890,7 @@ static int probe_kern_arg_ctx_tag(void)
68296890
*/
68306891
static int bpf_program_fixup_func_info(struct bpf_object *obj, struct bpf_program *prog)
68316892
{
6832-
const char *ctx_name = NULL, *ctx_tag = "arg:ctx";
6893+
const char *ctx_name = NULL, *ctx_tag = "arg:ctx", *fn_name;
68336894
struct bpf_func_info_min *func_rec;
68346895
struct btf_type *fn_t, *fn_proto_t;
68356896
struct btf *btf = obj->btf;
@@ -6909,15 +6970,11 @@ static int bpf_program_fixup_func_info(struct bpf_object *obj, struct bpf_progra
69096970
if (arg_idx < 0 || arg_idx >= arg_cnt)
69106971
continue;
69116972

6912-
/* check if existing parameter already matches verifier expectations */
6973+
/* check if we should fix up argument type */
69136974
p = &btf_params(fn_proto_t)[arg_idx];
6914-
t = skip_mods_and_typedefs(btf, p->type, NULL);
6915-
if (btf_is_ptr(t) &&
6916-
(t = skip_mods_and_typedefs(btf, t->type, NULL)) &&
6917-
btf_is_struct(t) &&
6918-
strcmp(btf__str_by_offset(btf, t->name_off), ctx_name) == 0) {
6919-
continue; /* no need for fix up */
6920-
}
6975+
fn_name = btf__str_by_offset(btf, fn_t->name_off) ?: "<anon>";
6976+
if (!need_func_arg_type_fixup(btf, prog, fn_name, arg_idx, p->type, ctx_name))
6977+
continue;
69216978

69226979
/* clone fn/fn_proto, unless we already did it for another arg */
69236980
if (func_rec->type_id == orig_fn_id) {

0 commit comments

Comments
 (0)