Merge branch 'fix-unique-field-logic-in-btf'

Kumar Kartikeya Dwivedi says:

====================
Fix unique field logic in BTF

Fix constraint of certain fields that have to be unique when nested
structs are present, BPF_REFCOUNT which should be marked as unique.
See commit logs for details. While at it, fix improper offset check.

Changelog:
----------
v1 -> v2
v1: https://lore.kernel.org/bpf/20260719142401.2420111-1-memxor@gmail.com

 * Add fix for BPF_REFCOUNT not being unique. (Sashiko)
 * Roll improper offset warning check into series. (Sashiko)
====================

Link: https://patch.msgid.link/20260719153634.2908692-1-memxor@gmail.com
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
This commit is contained in:
Eduard Zingerman 2026-07-21 16:26:42 -07:00
commit a23a718233
2 changed files with 64 additions and 14 deletions

View File

@ -3669,7 +3669,7 @@ static int btf_get_field_type(const struct btf *btf, const struct btf_type *var_
{ BPF_LIST_NODE, "bpf_list_node", false },
{ BPF_RB_ROOT, "bpf_rb_root", false },
{ BPF_RB_NODE, "bpf_rb_node", false },
{ BPF_REFCOUNT, "bpf_refcount", false },
{ BPF_REFCOUNT, "bpf_refcount", true },
};
int type = 0, i;
const char *name = __btf_name_by_offset(btf, var_type->name_off);
@ -3751,7 +3751,7 @@ static int btf_repeat_fields(struct btf_field_info *info, int info_cnt,
static int btf_find_struct_field(const struct btf *btf,
const struct btf_type *t, u32 field_mask,
struct btf_field_info *info, int info_cnt,
u32 level);
u32 level, u32 *seen_mask);
/* Find special fields in the struct type of a field.
*
@ -3762,7 +3762,7 @@ static int btf_find_struct_field(const struct btf *btf,
static int btf_find_nested_struct(const struct btf *btf, const struct btf_type *t,
u32 off, u32 nelems,
u32 field_mask, struct btf_field_info *info,
int info_cnt, u32 level)
int info_cnt, u32 level, u32 *seen_mask)
{
int ret, err, i;
@ -3770,7 +3770,7 @@ static int btf_find_nested_struct(const struct btf *btf, const struct btf_type *
if (level >= MAX_RESOLVE_DEPTH)
return -E2BIG;
ret = btf_find_struct_field(btf, t, field_mask, info, info_cnt, level);
ret = btf_find_struct_field(btf, t, field_mask, info, info_cnt, level, seen_mask);
if (ret <= 0)
return ret;
@ -3827,7 +3827,7 @@ static int btf_find_field_one(const struct btf *btf,
if (expected_size && expected_size != sz * nelems)
return 0;
ret = btf_find_nested_struct(btf, var_type, off, nelems, field_mask,
&info[0], info_cnt, level);
&info[0], info_cnt, level, seen_mask);
return ret;
}
@ -3892,11 +3892,11 @@ static int btf_find_field_one(const struct btf *btf,
static int btf_find_struct_field(const struct btf *btf,
const struct btf_type *t, u32 field_mask,
struct btf_field_info *info, int info_cnt,
u32 level)
u32 level, u32 *seen_mask)
{
int ret, idx = 0;
const struct btf_member *member;
u32 i, off, seen_mask = 0;
u32 i, off;
for_each_member(i, t, member) {
const struct btf_type *member_type = btf_type_by_id(btf,
@ -3910,7 +3910,7 @@ static int btf_find_struct_field(const struct btf *btf,
ret = btf_find_field_one(btf, t, member_type, i,
off, 0,
field_mask, &seen_mask,
field_mask, seen_mask,
&info[idx], info_cnt - idx, level);
if (ret < 0)
return ret;
@ -3921,11 +3921,11 @@ static int btf_find_struct_field(const struct btf *btf,
static int btf_find_datasec_var(const struct btf *btf, const struct btf_type *t,
u32 field_mask, struct btf_field_info *info,
int info_cnt, u32 level)
int info_cnt, u32 level, u32 *seen_mask)
{
int ret, idx = 0;
const struct btf_var_secinfo *vsi;
u32 i, off, seen_mask = 0;
u32 i, off;
for_each_vsi(i, t, vsi) {
const struct btf_type *var = btf_type_by_id(btf, vsi->type);
@ -3933,7 +3933,7 @@ static int btf_find_datasec_var(const struct btf *btf, const struct btf_type *t,
off = vsi->offset;
ret = btf_find_field_one(btf, var, var_type, -1, off, vsi->size,
field_mask, &seen_mask,
field_mask, seen_mask,
&info[idx], info_cnt - idx,
level);
if (ret < 0)
@ -3947,10 +3947,12 @@ static int btf_find_field(const struct btf *btf, const struct btf_type *t,
u32 field_mask, struct btf_field_info *info,
int info_cnt)
{
u32 seen_mask = 0;
if (__btf_type_is_struct(t))
return btf_find_struct_field(btf, t, field_mask, info, info_cnt, 0);
return btf_find_struct_field(btf, t, field_mask, info, info_cnt, 0, &seen_mask);
else if (btf_type_is_datasec(t))
return btf_find_datasec_var(btf, t, field_mask, info, info_cnt, 0);
return btf_find_datasec_var(btf, t, field_mask, info, info_cnt, 0, &seen_mask);
return -EINVAL;
}
@ -4168,7 +4170,7 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type
rec->spin_lock_off = rec->fields[i].offset;
break;
case BPF_RES_SPIN_LOCK:
WARN_ON_ONCE(rec->spin_lock_off >= 0);
WARN_ON_ONCE(rec->res_spin_lock_off >= 0);
/* Cache offset for faster lookup at runtime */
rec->res_spin_lock_off = rec->fields[i].offset;
break;

View File

@ -4250,6 +4250,54 @@ static struct btf_raw_test raw_tests[] = {
.max_entries = 1,
},
/*
* struct inner {
* struct bpf_spin_lock lock;
* };
*
* struct value {
* struct bpf_spin_lock lock;
* struct inner nested;
* };
*/
{
.descr = "struct test duplicate nested unique fields",
.raw_types = {
BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
BTF_STRUCT_ENC(NAME_TBD, 1, 4), /* [2] */
BTF_MEMBER_ENC(NAME_TBD, 1, 0),
BTF_STRUCT_ENC(NAME_TBD, 1, 4), /* [3] */
BTF_MEMBER_ENC(NAME_TBD, 2, 0),
BTF_STRUCT_ENC(NAME_TBD, 2, 8), /* [4] */
BTF_MEMBER_ENC(NAME_TBD, 2, 0),
BTF_MEMBER_ENC(NAME_TBD, 3, 32),
BTF_END_RAW,
},
BTF_STR_SEC("\0int\0bpf_spin_lock\0val\0inner\0lock\0value\0lock\0nested"),
.btf_load_err = true,
},
/*
* struct value {
* struct bpf_refcount a;
* struct bpf_refcount b;
* };
*/
{
.descr = "struct test duplicate bpf_refcount fields",
.raw_types = {
BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
BTF_STRUCT_ENC(NAME_TBD, 1, 4), /* [2] */
BTF_MEMBER_ENC(NAME_TBD, 1, 0),
BTF_STRUCT_ENC(NAME_TBD, 2, 8), /* [3] */
BTF_MEMBER_ENC(NAME_TBD, 2, 0),
BTF_MEMBER_ENC(NAME_TBD, 2, 32),
BTF_END_RAW,
},
BTF_STR_SEC("\0int\0bpf_refcount\0refs\0value\0a\0b"),
.btf_load_err = true,
},
{
.descr = "struct test repeated fields count overflow",
.raw_types = {