mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
selftests/bpf: Add BTF fixup for __naked subprog parameter names
When __naked subprogs are used in verifier tests, clang drops parameter names from their BTF FUNC_PROTO entries. This prevents the verifier from resolving stack argument slots by name. Add a __btf_func_path(path) annotation that points to a separate BTF file containing properly-named FUNC entries. The test_loader matches FUNC entries by name, detects anonymous parameters, and replaces the FUNC_PROTO with a new one that carries parameter names from the custom file while preserving the original type IDs. The custom BTF file also serves as btf_custom_path for kfunc resolution when no separate btf_custom_path is specified. Signed-off-by: Yonghong Song <yonghong.song@linux.dev> Link: https://lore.kernel.org/r/20260513045138.2398886-1-yonghong.song@linux.dev Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
9f42204c62
commit
5b31de8892
|
|
@ -152,6 +152,7 @@
|
|||
#define __auxiliary __test_tag("test_auxiliary")
|
||||
#define __auxiliary_unpriv __test_tag("test_auxiliary_unpriv")
|
||||
#define __btf_path(path) __test_tag("test_btf_path=" path)
|
||||
#define __btf_func_path(path) __test_tag("test_btf_func_path=" path)
|
||||
#define __arch(arch) __test_tag("test_arch=" arch)
|
||||
#define __arch_x86_64 __arch("X86_64")
|
||||
#define __arch_arm64 __arch("ARM64")
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ struct test_spec {
|
|||
struct test_subspec priv;
|
||||
struct test_subspec unpriv;
|
||||
const char *btf_custom_path;
|
||||
const char *btf_custom_func_path;
|
||||
int log_level;
|
||||
int prog_flags;
|
||||
int mode_mask;
|
||||
|
|
@ -590,6 +591,8 @@ static int parse_test_spec(struct test_loader *tester,
|
|||
jit_on_next_line = true;
|
||||
} else if ((val = str_has_pfx(s, "test_btf_path="))) {
|
||||
spec->btf_custom_path = val;
|
||||
} else if ((val = str_has_pfx(s, "test_btf_func_path="))) {
|
||||
spec->btf_custom_func_path = val;
|
||||
} else if ((val = str_has_pfx(s, "test_caps_unpriv="))) {
|
||||
err = parse_caps(val, &spec->unpriv.caps, "test caps");
|
||||
if (err)
|
||||
|
|
@ -1175,6 +1178,123 @@ static int get_stream(int stream_id, int prog_fd, char *text, size_t text_sz)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix up the program's BTF using BTF from a separate file.
|
||||
*
|
||||
* For __naked subprogs, clang drops parameter names from BTF. Find FUNC
|
||||
* entries with anonymous parameters and replace their FUNC_PROTO with the
|
||||
* properly-named version from the custom file.
|
||||
*/
|
||||
static int fixup_btf_from_path(struct bpf_object *obj, const char *path)
|
||||
{
|
||||
struct btf *prog_btf, *custom_btf;
|
||||
__u32 i, j, cnt, custom_cnt;
|
||||
int err = 0;
|
||||
|
||||
prog_btf = bpf_object__btf(obj);
|
||||
if (!prog_btf)
|
||||
return 0;
|
||||
|
||||
custom_btf = btf__parse(path, NULL);
|
||||
if (!ASSERT_OK_PTR(custom_btf, "parse_custom_btf"))
|
||||
return -EINVAL;
|
||||
|
||||
cnt = btf__type_cnt(prog_btf);
|
||||
custom_cnt = btf__type_cnt(custom_btf);
|
||||
|
||||
/* Fix up FUNC entries with anonymous params.
|
||||
* Save all data from prog_btf BEFORE calling btf__add_*,
|
||||
* since those calls may reallocate the BTF data buffer
|
||||
* and invalidate any pointers obtained from btf__type_by_id.
|
||||
*/
|
||||
for (i = 1; i < cnt; i++) {
|
||||
const struct btf_type *t = btf__type_by_id(prog_btf, i);
|
||||
const struct btf_type *fp, *custom_t, *custom_fp;
|
||||
const struct btf_param *params, *custom_params;
|
||||
__u32 ret_type_id, vlen;
|
||||
__u32 *prog_param_types = NULL;
|
||||
const char *name;
|
||||
int new_proto_id;
|
||||
|
||||
if (!btf_is_func(t))
|
||||
continue;
|
||||
|
||||
fp = btf__type_by_id(prog_btf, t->type);
|
||||
if (!fp || !btf_is_func_proto(fp) || btf_vlen(fp) == 0)
|
||||
continue;
|
||||
|
||||
/* Check if any param is anonymous */
|
||||
params = btf_params(fp);
|
||||
if (params[0].name_off != 0)
|
||||
continue;
|
||||
|
||||
/* Find matching FUNC by name in custom BTF */
|
||||
name = btf__name_by_offset(prog_btf, t->name_off);
|
||||
if (!name)
|
||||
continue;
|
||||
|
||||
for (j = 1; j < custom_cnt; j++) {
|
||||
const char *cname;
|
||||
|
||||
custom_t = btf__type_by_id(custom_btf, j);
|
||||
if (!btf_is_func(custom_t))
|
||||
continue;
|
||||
cname = btf__name_by_offset(custom_btf, custom_t->name_off);
|
||||
if (cname && strcmp(name, cname) == 0)
|
||||
break;
|
||||
}
|
||||
if (j >= custom_cnt)
|
||||
continue;
|
||||
|
||||
custom_fp = btf__type_by_id(custom_btf, custom_t->type);
|
||||
if (!custom_fp || !btf_is_func_proto(custom_fp))
|
||||
continue;
|
||||
|
||||
vlen = btf_vlen(fp);
|
||||
if (vlen != btf_vlen(custom_fp))
|
||||
continue;
|
||||
|
||||
/* Save data before btf__add_* calls invalidate pointers */
|
||||
ret_type_id = fp->type;
|
||||
prog_param_types = malloc(vlen * sizeof(*prog_param_types));
|
||||
if (!prog_param_types) {
|
||||
err = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
for (j = 0; j < vlen; j++)
|
||||
prog_param_types[j] = params[j].type;
|
||||
|
||||
/* Add a new FUNC_PROTO: param names from custom, types from prog */
|
||||
new_proto_id = btf__add_func_proto(prog_btf, ret_type_id);
|
||||
if (new_proto_id < 0) {
|
||||
err = new_proto_id;
|
||||
free(prog_param_types);
|
||||
break;
|
||||
}
|
||||
|
||||
custom_params = btf_params(custom_fp);
|
||||
for (j = 0; j < vlen; j++) {
|
||||
const char *pname;
|
||||
|
||||
pname = btf__name_by_offset(custom_btf, custom_params[j].name_off);
|
||||
err = btf__add_func_param(prog_btf, pname ?: "", prog_param_types[j]);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
free(prog_param_types);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
/* Update the FUNC to point to the new FUNC_PROTO (re-fetch
|
||||
* since btf__add_* may have reallocated the data buffer).
|
||||
*/
|
||||
((struct btf_type *)btf__type_by_id(prog_btf, i))->type = new_proto_id;
|
||||
}
|
||||
|
||||
btf__free(custom_btf);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* this function is forced noinline and has short generic name to look better
|
||||
* in test_progs output (in case of a failure)
|
||||
*/
|
||||
|
|
@ -1231,13 +1351,27 @@ void run_subtest(struct test_loader *tester,
|
|||
}
|
||||
}
|
||||
|
||||
/* Implicitly reset to NULL if next test case doesn't specify */
|
||||
/* Implicitly reset to NULL if next test case doesn't specify.
|
||||
* btf_custom_func_path also serves as btf_custom_path for kfunc resolution.
|
||||
*/
|
||||
open_opts->btf_custom_path = spec->btf_custom_path;
|
||||
if (!open_opts->btf_custom_path)
|
||||
open_opts->btf_custom_path = spec->btf_custom_func_path;
|
||||
|
||||
tobj = bpf_object__open_mem(obj_bytes, obj_byte_cnt, open_opts);
|
||||
if (!ASSERT_OK_PTR(tobj, "obj_open_mem")) /* shouldn't happen */
|
||||
goto subtest_cleanup;
|
||||
|
||||
/* Fix up __naked subprog BTF using a separate file with named params */
|
||||
if (spec->btf_custom_func_path) {
|
||||
err = fixup_btf_from_path(tobj, spec->btf_custom_func_path);
|
||||
if (err) {
|
||||
PRINT_FAIL("failed to fixup BTF from %s: %d\n",
|
||||
spec->btf_custom_func_path, err);
|
||||
goto tobj_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
bpf_object__for_each_program(tprog_iter, tobj) {
|
||||
spec_iter = &specs[i++];
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user