From a3df11403e8c8c3b613ee4595c1da0e3b9636971 Mon Sep 17 00:00:00 2001 From: Alan Maguire Date: Sat, 26 Sep 2026 18:51:11 +0100 Subject: [PATCH 1/3] bpf: Verify BTF_KIND_LOC_PARAM vlen, flags Ensure that vlen and flags combinations are consistent for BTF_KIND_LOC_PARAMs. Fixes: 33c5a3278bdb ("btf: Extend UAPI to support BTF location (inline site) info") Suggested-by: Alexei Starovoitov Signed-off-by: Alan Maguire Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20260926175113.2368566-2-alan.maguire@oracle.com --- kernel/bpf/btf.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 9bcfefdfb734..8cc17a1cd25c 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -4823,10 +4823,53 @@ static s32 btf_loc_param_check_meta(struct btf_verifier_env *env, btf_verifier_log_type(env, t, "Invalid btf_info kind_flag"); return -EINVAL; } + /* All LOC_PARAMs have vlen of at least 1 */ + if (vlen < 1) + goto invalid_vlen; + + switch (p->flags) { + case BTF_LOC_PARAM_CONST: + case BTF_LOC_PARAM_CONST | BTF_LOC_PARAM_SIGNED: + case BTF_LOC_PARAM_CONST | BTF_LOC_PARAM_ADDR: + case BTF_LOC_PARAM_CONST | BTF_LOC_PARAM_ADDR | BTF_LOC_PARAM_SIGNED: + if (vlen > 2) + goto invalid_vlen; + /* Ensure size/vlen are consistent */ + if (size > sizeof(__u32) && vlen != 2) + goto invalid_vlen; + if (size > sizeof(__u64)) + goto invalid_vlen; + break; + case BTF_LOC_PARAM_REG: + /* Reg or reg/reg pair */ + if (vlen > 2) + goto invalid_vlen; + break; + case BTF_LOC_PARAM_REG | BTF_LOC_PARAM_DEREF: + /* A reg deref without offset can only have vlen 1 */ + if (vlen != 1) + goto invalid_vlen; + break; + case BTF_LOC_PARAM_REG | BTF_LOC_PARAM_OFFSET: + case BTF_LOC_PARAM_REG | BTF_LOC_PARAM_OFFSET | BTF_LOC_PARAM_SIGNED: + case BTF_LOC_PARAM_REG | BTF_LOC_PARAM_DEREF | BTF_LOC_PARAM_OFFSET: + case BTF_LOC_PARAM_REG | BTF_LOC_PARAM_DEREF | BTF_LOC_PARAM_OFFSET | + BTF_LOC_PARAM_SIGNED: + if (vlen < 2 || vlen > 3) + goto invalid_vlen; + break; + default: + btf_verifier_log_type(env, t, "Invalid flags"); + return -EINVAL; + } btf_verifier_log_type(env, t, NULL); return meta_needed; + +invalid_vlen: + btf_verifier_log_type(env, t, "Invalid vlen"); + return -EINVAL; } static void btf_loc_param_log(struct btf_verifier_env *env, From 29f6453491f3b47893e0fc676adbff2e552b27f5 Mon Sep 17 00:00:00 2001 From: Alan Maguire Date: Sat, 26 Sep 2026 18:51:12 +0100 Subject: [PATCH 2/3] bpftool: Update func representation to include function signature Augment func= output for LOCSEC entries to include a mapping from function signature to where parameters are stored; for example: [290179] LOCSEC 'inline.text' vlen=524941 func='task_pid_nr(tsk [reg0])' func_type_id=136691 loc_proto_type_id=136693 offset=2097226 func='get_current()' func_type_id=136694 loc_proto_type_id=136695 offset=2097247 func='arch_static_branch(key [address 0x2e275e8], branch [const 0x0])' func_type_id=136697 loc_proto_type_id=136700 offset=2097296 Fixes: 321562c34d5b ("bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC") Suggested-by: Alexei Starovoitov Signed-off-by: Alan Maguire Signed-off-by: Alexei Starovoitov Acked-by: Quentin Monnet Link: https://patch.msgid.link/20260926175113.2368566-3-alan.maguire@oracle.com --- tools/bpf/bpftool/btf.c | 123 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 9 deletions(-) diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c index e29c8a84e224..abd661db91e6 100644 --- a/tools/bpf/bpftool/btf.c +++ b/tools/bpf/bpftool/btf.c @@ -150,6 +150,7 @@ static void btf_loc_param_raw_str(const struct btf_loc_param *p, __u32 vlen, static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz) { + const char *op = "", *prefix = ""; const struct btf_loc_param *p; __u32 i = 0, vlen; __u64 value; @@ -157,7 +158,6 @@ static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz) bool negative = false; char regs[32] = {}; char num[32] = {}; - const char *op = ""; if (!t || !btf_is_loc_param(t)) { snprintf(str, sz, ""); @@ -234,8 +234,12 @@ static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz) (1ULL << bits) - value; } } - snprintf(num, sizeof(num), "0x%llx%s", (unsigned long long)value, - p->flags & BTF_LOC_PARAM_ADDR ? " (addr)" : ""); + if (p->flags & BTF_LOC_PARAM_ADDR) + prefix = "address "; + else if (p->flags & BTF_LOC_PARAM_CONST) + prefix = "const "; + snprintf(num, sizeof(num), "0x%llx", + (unsigned long long)value); } if (i != vlen) { btf_loc_param_raw_str(p, vlen, str, sz); @@ -244,14 +248,111 @@ static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz) if (num[0]) op = regs[0] ? (negative ? " - " : " + ") : negative ? "-" : ""; - snprintf(str, sz, "%s%s%s%s%s", + snprintf(str, sz, "%s%s%s%s%s%s", p->flags & BTF_LOC_PARAM_DEREF ? "*(" : "", + prefix, regs, op, num, p->flags & BTF_LOC_PARAM_DEREF ? ")" : ""); } +static bool btf_locsec_append(char *str, size_t sz, size_t *off, + const char *suffix) +{ + static const char nospace_suffix[] = "..."; + bool space_left = true; + size_t left = 0, len; + + if (*off >= sz || sz < sizeof(nospace_suffix)) + return false; + left = sz - *off; + + /* + * Copy as much of the string as we can in remaining space, and + * append "..." if we would have overrun. + */ + len = strlen(suffix) + 1; + if (len > left) { + len = left; + space_left = false; + } + + memcpy(str + *off, suffix, len); + *off += len - 1; + + if (!space_left) + memcpy(str + sz - sizeof(nospace_suffix), nospace_suffix, sizeof(nospace_suffix)); + + return space_left; +} + +static void btf_locsec_func_str(const struct btf *btf, + const struct btf_loc *loc, char *str, size_t sz) +{ + const struct btf_type *func, *func_proto, *loc_proto; + const struct btf_param *params; + const __u32 *loc_params; + const char *name; + __u32 i, vlen; + size_t off = 0; + + if (!sz) + return; + + str[0] = '\0'; + func = btf__type_by_id(btf, loc->func); + if (!func || !btf_is_func(func)) + goto invalid; + + name = btf_str(btf, func->name_off); + func_proto = btf__type_by_id(btf, func->type); + loc_proto = btf__type_by_id(btf, loc->loc_proto); + if (!func_proto || !btf_is_func_proto(func_proto) || + !loc_proto || !btf_is_loc_proto(loc_proto) || + btf_vlen(func_proto) != btf_vlen(loc_proto)) + goto invalid; + + params = (const void *)(func_proto + 1); + loc_params = btf_loc_proto_params(loc_proto); + vlen = btf_vlen(func_proto); + if (!btf_locsec_append(str, sz, &off, name) || + !btf_locsec_append(str, sz, &off, "(")) + return; + for (i = 0; i < vlen; i++) { + const struct btf_type *param_loc; + char param_str[256] = {}; + + if (!params[i].type) { + /* Handle varargs func proto, must be last parameter */ + if (i != vlen - 1) + goto invalid; + if (!btf_locsec_append(str, sz, &off, i ? ", " : "") || + !btf_locsec_append(str, sz, &off, "...")) + return; + break; + } else if (loc_params[i]) { + param_loc = btf__type_by_id(btf, loc_params[i]); + btf_loc_param_str(param_loc, param_str, sizeof(param_str)); + } else { + snprintf(param_str, sizeof(param_str), ""); + } + + if (!btf_locsec_append(str, sz, &off, i ? ", " : "") || + !btf_locsec_append(str, sz, &off, + btf_str(btf, params[i].name_off)) || + !btf_locsec_append(str, sz, &off, " [") || + !btf_locsec_append(str, sz, &off, param_str) || + !btf_locsec_append(str, sz, &off, "]")) + return; + } + (void) btf_locsec_append(str, sz, &off, ")"); + return; + +invalid: + snprintf(str, sz, ""); +} + static int dump_btf_type(const struct btf *btf, __u32 id, const struct btf_type *t) { @@ -617,22 +718,26 @@ static int dump_btf_type(const struct btf *btf, __u32 id, } for (i = 0; i < vlen; i++, locs++) { - const struct btf_type *f = btf__type_by_id(btf, locs->func); + const struct btf_type *func; const char *name = ""; + char func_str[1024] = {}; - if (f && btf_is_func(f)) - name = btf_str(btf, f->name_off); + func = btf__type_by_id(btf, locs->func); + if (func && btf_is_func(func)) + name = btf_str(btf, func->name_off); + btf_locsec_func_str(btf, locs, func_str, sizeof(func_str)); if (json_output) { jsonw_start_object(w); jsonw_uint_field(w, "func_type_id", locs->func); jsonw_string_field(w, "name", name); + jsonw_string_field(w, "func", func_str); jsonw_uint_field(w, "loc_proto_type_id", locs->loc_proto); jsonw_uint_field(w, "offset", locs->offset); jsonw_end_object(w); } else { - printf("\n\tname='%s' func_type_id=%u loc_proto_type_id=%u offset=%u", - name, locs->func, locs->loc_proto, locs->offset); + printf("\n\tfunc='%s' func_type_id=%u loc_proto_type_id=%u offset=%u", + func_str, locs->func, locs->loc_proto, locs->offset); } } if (json_output) From 3cc62f77b9749bb90bf32a85361a6bcde18ff44b Mon Sep 17 00:00:00 2001 From: Alan Maguire Date: Sat, 26 Sep 2026 18:51:13 +0100 Subject: [PATCH 3/3] selftests/bpf: Fix up bpftool btf dump test for signatures Now we show a function signature, fix up the test to expect it. Also fix the fact that we did not add the right number of parameters to the FUNC_PROTO, and ensure consts/addresses are prefixed appropriately. Fixes: 321562c34d5b ("bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC") Signed-off-by: Alan Maguire Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20260926175113.2368566-4-alan.maguire@oracle.com --- .../selftests/bpf/prog_tests/bpftool_btf_dump.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c index bed506badc75..abc62958b6ed 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c +++ b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c @@ -72,6 +72,7 @@ static struct btf *mk_loc_btf(void) btf__add_func_param(btf, "arg3", 1); btf__add_func_param(btf, "arg4", 1); btf__add_func_param(btf, "arg5", 1); + btf__add_func_param(btf, "arg6", 1); btf__add_func(btf, "foo", BTF_FUNC_STATIC, 2); btf__add_loc_param(btf, 4, BTF_LOC_PARAM_REG); @@ -230,28 +231,29 @@ static void test_loc_dump(const char *btf_path) { const char expected[] = "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED\n" - "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=5\n" + "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=6\n" "\t'arg1' type_id=1\n" "\t'arg2' type_id=1\n" "\t'arg3' type_id=1\n" "\t'arg4' type_id=1\n" "\t'arg5' type_id=1\n" + "\t'arg6' type_id=1\n" "[3] FUNC 'foo' type_id=2 linkage=static\n" "[4] LOC_PARAM '(anon)' size=4 flags=0x8 vlen=1 values='reg1'\n" "[5] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(reg2 + 0x10)'\n" "[6] LOC_PARAM '(anon)' size=8 flags=0x29 vlen=2 values='fbreg - 0x10'\n" - "[7] LOC_PARAM '(anon)' size=8 flags=0x6 vlen=2 values='0x123456789abcdef0 (addr)'\n" - "[8] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2 values='0xdeadbeeffeedface'\n" + "[7] LOC_PARAM '(anon)' size=8 flags=0x6 vlen=2 values='address 0x123456789abcdef0'\n" + "[8] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2 values='const 0xdeadbeeffeedface'\n" "[9] LOC_PARAM '(anon)' size=8 flags=0x39 vlen=2 values='*(fbreg - 0x20)'\n" "[10] LOC_PROTO '(anon)' vlen=6\n" "\ttype_id=4 value='reg1'\n" "\ttype_id=5 value='*(reg2 + 0x10)'\n" "\ttype_id=6 value='fbreg - 0x10'\n" - "\ttype_id=7 value='0x123456789abcdef0 (addr)'\n" - "\ttype_id=8 value='0xdeadbeeffeedface'\n" + "\ttype_id=7 value='address 0x123456789abcdef0'\n" + "\ttype_id=8 value='const 0xdeadbeeffeedface'\n" "\ttype_id=9 value='*(fbreg - 0x20)'\n" "[11] LOCSEC 'inline.text' vlen=1\n" - "\tname='foo' func_type_id=3 loc_proto_type_id=10 offset=64\n"; + "\tfunc='foo(arg1 [reg1], arg2 [*(reg2 + 0x10)], arg3 [fbreg - 0x10], arg4 [address 0x123456789abcdef0], arg5 [const 0xdeadbeeffeedface], arg6 [*(fbreg - 0x20)])' func_type_id=3 loc_proto_type_id=10 offset=64\n"; char *dump; dump = dump_raw(btf_path);