Merge branch 'btf-inline-functionality-followups'

Alan Maguire says:

====================
BTF inline functionality followups

Include additional vlen/flags verification for BTF_KIND_LOC_PARAM
(patch 1), and add function signatures to LOCSEC entries (patch 2),
adjusting test to cover these (patch 3).

Changes since v2 [1]

- Improved flag/vlen verification to check combinations (Alexei,
  patch 1)
- Simplified overrun ellipsis printing (Quentin, patch 2)

Changes since v1 [2]

- Fixed sign to appear after const (bots, patch 2)
- Simplified loc printing to remove vsnprintf() logic, updated
  to retain name in json (Quentin, patch 2)
- For oversized signature strings, append "..." (Quentin, patch 2)

[1] https://lore.kernel.org/bpf/20260925170854.1240637-1-alan.maguire@oracle.com/
[2] https://lore.kernel.org/bpf/20260925095231.879708-1-alan.maguire@oracle.com/
====================

Acked-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Quentin Monnet <qmo@kernel.org>
Link: https://patch.msgid.link/20260926175113.2368566-1-alan.maguire@oracle.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Alexei Starovoitov 2026-09-29 10:41:17 +00:00
commit cc6010e6e5
No known key found for this signature in database
3 changed files with 165 additions and 15 deletions

View File

@ -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,

View File

@ -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, "<invalid>");
@ -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), "<unavailable>");
}
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, "<invalid>");
}
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 = "<invalid>";
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)

View File

@ -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);