bpf: Allow a variable in DATASEC that is smaller than its type

LLVM splits a static of a Rust program into pieces. Every piece is a VAR
with the type of the whole static:

  [223] STRUCT 'BpfCell<core::option::Option<...>>' size=32 vlen=1
  [236] VAR '..scx_cosmos9TASK_CTXS.0' type_id=223, linkage=static
  [248] DATASEC '.bss' size=0 vlen=9
          type_id=236 offset=24648 size=1 (VAR '..TASK_CTXS.0')

and the kernel rejects such BTF with "Invalid size".

Allow it. The size of a variable in DATASEC is used in two places:

- btf_find_datasec_var() looks for timers, spin locks, kptrs and other
  special fields. btf_find_field_one() skips a variable when its size is
  not the size of its type, so there are no special fields in a piece.

- btf_datasec_show() prints variables by their types. It would read
  past the piece and past the end of the map value when the piece is the
  last one. Don't print the pieces.

"global data test #8" and "#10" in prog_tests/btf.c expect "Invalid
size". BTF of both is loaded now. The map of #10 is not created, since
its value is larger than the section.

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20261002124714.180012-10-alexei.starovoitov@gmail.com
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
This commit is contained in:
Alexei Starovoitov 2026-10-02 12:47:08 +00:00 committed by Kumar Kartikeya Dwivedi
parent 3dc3a91ec9
commit 9094a158e8
No known key found for this signature in database
GPG Key ID: 472D377B63542F83
2 changed files with 26 additions and 14 deletions

View File

@ -5397,7 +5397,7 @@ static int btf_datasec_resolve(struct btf_verifier_env *env,
env->resolve_mode = RESOLVE_TBD;
for_each_vsi_from(i, v->next_member, v->t, vsi) {
u32 var_type_id = vsi->type, type_id, type_size = 0;
u32 var_type_id = vsi->type, type_id;
const struct btf_type *var_type = btf_type_by_id(env->btf,
var_type_id);
if (!var_type || !btf_type_is_var(var_type)) {
@ -5412,16 +5412,16 @@ static int btf_datasec_resolve(struct btf_verifier_env *env,
return env_stack_push(env, var_type, var_type_id);
}
/*
* The variable can be smaller than its type. It's a piece of
* a variable that the compiler split then, with the type of
* the whole variable.
*/
type_id = var_type->type;
if (!btf_type_id_size(btf, &type_id, &type_size)) {
if (!btf_type_id_size(btf, &type_id, NULL)) {
btf_verifier_log_vsi(env, v->t, vsi, "Invalid type");
return -EINVAL;
}
if (vsi->size < type_size) {
btf_verifier_log_vsi(env, v->t, vsi, "Invalid size");
return -EINVAL;
}
}
env_stack_pop_resolved(env, 0, 0);
@ -5434,6 +5434,16 @@ static void btf_datasec_log(struct btf_verifier_env *env,
btf_verifier_log(env, "size=%u vlen=%u", t->size, btf_type_vlen(t));
}
/* A piece of a variable is smaller than its type, which is the one of the whole variable */
static bool btf_var_is_piece(const struct btf *btf, const struct btf_type *var,
const struct btf_var_secinfo *vsi)
{
u32 size;
return IS_ERR(btf_resolve_size(btf, btf_type_by_id(btf, var->type), &size)) ||
vsi->size < size;
}
static void btf_datasec_show(const struct btf *btf,
const struct btf_type *t, u32 type_id,
void *data, u8 bits_offset,
@ -5441,6 +5451,7 @@ static void btf_datasec_show(const struct btf *btf,
{
const struct btf_var_secinfo *vsi;
const struct btf_type *var;
bool comma = false;
u32 i;
if (!btf_show_start_type(show, t, type_id, data))
@ -5450,8 +5461,12 @@ static void btf_datasec_show(const struct btf *btf,
__btf_name_by_offset(btf, t->name_off));
for_each_vsi(i, t, vsi) {
var = btf_type_by_id(btf, vsi->type);
if (i)
/* there is less data than the type takes */
if (btf_var_is_piece(btf, var, vsi))
continue;
if (comma)
btf_show(show, ",");
comma = true;
btf_type_ops(var)->show(btf, var, vsi->type,
data + vsi->offset, bits_offset, show);
}

View File

@ -424,7 +424,7 @@ static struct btf_raw_test raw_tests[] = {
.err_str = "Invalid type",
},
{
.descr = "global data test #8, invalid var size",
.descr = "global data test #8, var is smaller than its type",
.raw_types = {
/* int */
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
@ -457,8 +457,6 @@ static struct btf_raw_test raw_tests[] = {
.key_type_id = 0,
.value_type_id = 7,
.max_entries = 1,
.btf_load_err = true,
.err_str = "Invalid size",
},
{
.descr = "global data test #9, invalid var size",
@ -498,7 +496,7 @@ static struct btf_raw_test raw_tests[] = {
.err_str = "Invalid size",
},
{
.descr = "global data test #10, invalid var size",
.descr = "global data test #10, section is smaller than map value",
.raw_types = {
/* int */
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
@ -531,8 +529,7 @@ static struct btf_raw_test raw_tests[] = {
.key_type_id = 0,
.value_type_id = 7,
.max_entries = 1,
.btf_load_err = true,
.err_str = "Invalid size",
.map_create_err = true,
},
{
.descr = "global data test #11, multiple section members",