Commit f6b9f6d5 authored by Andrii Nakryiko's avatar Andrii Nakryiko

Merge branch 'Introduce type match support'

Daniel Müller says:

====================

This patch set proposes the addition of a new way for performing type queries to
BPF. It introduces the "type matches" relation, similar to what is already
present with "type exists" (in the form of bpf_core_type_exists).

"type exists" performs fairly superficial checking, mostly concerned with
whether a type exists in the kernel and is of the same kind (enum/struct/...).
Notably, compatibility checks for members of composite types is lacking.

The newly introduced "type matches" (bpf_core_type_matches) fills this gap in
that it performs stricter checks: compatibility of members and existence of
similarly named enum variants is checked as well. E.g., given these definitions:

	struct task_struct___og { int pid; int tgid; };

	struct task_struct___foo { int foo; }

'task_struct___og' would "match" the kernel type 'task_struct', because the
members match up, while 'task_struct___foo' would not match, because the
kernel's 'task_struct' has no member named 'foo'.

More precisely, the "type match" relation is defined as follows (copied from
source):
- modifiers and typedefs are stripped (and, hence, effectively ignored)
- generally speaking types need to be of same kind (struct vs. struct, union
  vs. union, etc.)
  - exceptions are struct/union behind a pointer which could also match a
    forward declaration of a struct or union, respectively, and enum vs.
    enum64 (see below)
Then, depending on type:
- integers:
  - match if size and signedness match
- arrays & pointers:
  - target types are recursively matched
- structs & unions:
  - local members need to exist in target with the same name
  - for each member we recursively check match unless it is already behind a
    pointer, in which case we only check matching names and compatible kind
- enums:
  - local variants have to have a match in target by symbolic name (but not
    numeric value)
  - size has to match (but enum may match enum64 and vice versa)
- function pointers:
  - number and position of arguments in local type has to match target
  - for each argument and the return value we recursively check match

Enabling this feature requires a new relocation to be made known to the
compiler. This is being taken care of for LLVM as part of
https://reviews.llvm.org/D126838.

If applied, among other things, usage of this functionality could have helped
flag issues such as the one discussed here
https://lore.kernel.org/all/93a20759600c05b6d9e4359a1517c88e06b44834.camel@fb.com/
earlier.
Suggested-by: default avatarAndrii Nakryiko <andrii@kernel.org>
---
Changelog:
v2 -> v3:
- renamed btfgen_mark_types_match
- covered BTF_KIND_RESTRICT in type match marking logic
- used bpf_core_names_match in more places
- reworked "behind pointer" logic
- added test using live task_struct

v1 -> v2:
- deduplicated and moved core algorithm into relo_core.c
- adjusted bpf_core_names_match to get btf_type passed in
- removed some length equality checks before strncmp usage
- correctly use kflag from targ_t instead of local_t
- added comment for meaning of kflag w/ FWD kind
- __u32 -> u32
- handle BTF_KIND_FWD properly in bpftool marking logic
- rebased
====================
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
parents cfb5a2db 950b3477
...@@ -6786,6 +6786,7 @@ enum bpf_core_relo_kind { ...@@ -6786,6 +6786,7 @@ enum bpf_core_relo_kind {
BPF_CORE_TYPE_SIZE = 9, /* type size in bytes */ BPF_CORE_TYPE_SIZE = 9, /* type size in bytes */
BPF_CORE_ENUMVAL_EXISTS = 10, /* enum value existence in target kernel */ BPF_CORE_ENUMVAL_EXISTS = 10, /* enum value existence in target kernel */
BPF_CORE_ENUMVAL_VALUE = 11, /* enum value integer value */ BPF_CORE_ENUMVAL_VALUE = 11, /* enum value integer value */
BPF_CORE_TYPE_MATCHES = 12, /* type match in target kernel */
}; };
/* /*
......
...@@ -7443,6 +7443,15 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id, ...@@ -7443,6 +7443,15 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
MAX_TYPES_ARE_COMPAT_DEPTH); MAX_TYPES_ARE_COMPAT_DEPTH);
} }
#define MAX_TYPES_MATCH_DEPTH 2
int bpf_core_types_match(const struct btf *local_btf, u32 local_id,
const struct btf *targ_btf, u32 targ_id)
{
return __bpf_core_types_match(local_btf, local_id, targ_btf, targ_id, false,
MAX_TYPES_MATCH_DEPTH);
}
static bool bpf_core_is_flavor_sep(const char *s) static bool bpf_core_is_flavor_sep(const char *s)
{ {
/* check X___Y name pattern, where X and Y are not underscores */ /* check X___Y name pattern, where X and Y are not underscores */
......
...@@ -1856,6 +1856,112 @@ static int btfgen_record_field_relo(struct btfgen_info *info, struct bpf_core_sp ...@@ -1856,6 +1856,112 @@ static int btfgen_record_field_relo(struct btfgen_info *info, struct bpf_core_sp
return 0; return 0;
} }
/* Mark types, members, and member types. Compared to btfgen_record_field_relo,
* this function does not rely on the target spec for inferring members, but
* uses the associated BTF.
*
* The `behind_ptr` argument is used to stop marking of composite types reached
* through a pointer. This way, we can keep BTF size in check while providing
* reasonable match semantics.
*/
static int btfgen_mark_type_match(struct btfgen_info *info, __u32 type_id, bool behind_ptr)
{
const struct btf_type *btf_type;
struct btf *btf = info->src_btf;
struct btf_type *cloned_type;
int i, err;
if (type_id == 0)
return 0;
btf_type = btf__type_by_id(btf, type_id);
/* mark type on cloned BTF as used */
cloned_type = (struct btf_type *)btf__type_by_id(info->marked_btf, type_id);
cloned_type->name_off = MARKED;
switch (btf_kind(btf_type)) {
case BTF_KIND_UNKN:
case BTF_KIND_INT:
case BTF_KIND_FLOAT:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
break;
case BTF_KIND_STRUCT:
case BTF_KIND_UNION: {
struct btf_member *m = btf_members(btf_type);
__u16 vlen = btf_vlen(btf_type);
if (behind_ptr)
break;
for (i = 0; i < vlen; i++, m++) {
/* mark member */
btfgen_mark_member(info, type_id, i);
/* mark member's type */
err = btfgen_mark_type_match(info, m->type, false);
if (err)
return err;
}
break;
}
case BTF_KIND_CONST:
case BTF_KIND_FWD:
case BTF_KIND_RESTRICT:
case BTF_KIND_TYPEDEF:
case BTF_KIND_VOLATILE:
return btfgen_mark_type_match(info, btf_type->type, behind_ptr);
case BTF_KIND_PTR:
return btfgen_mark_type_match(info, btf_type->type, true);
case BTF_KIND_ARRAY: {
struct btf_array *array;
array = btf_array(btf_type);
/* mark array type */
err = btfgen_mark_type_match(info, array->type, false);
/* mark array's index type */
err = err ? : btfgen_mark_type_match(info, array->index_type, false);
if (err)
return err;
break;
}
case BTF_KIND_FUNC_PROTO: {
__u16 vlen = btf_vlen(btf_type);
struct btf_param *param;
/* mark ret type */
err = btfgen_mark_type_match(info, btf_type->type, false);
if (err)
return err;
/* mark parameters types */
param = btf_params(btf_type);
for (i = 0; i < vlen; i++) {
err = btfgen_mark_type_match(info, param->type, false);
if (err)
return err;
param++;
}
break;
}
/* tells if some other type needs to be handled */
default:
p_err("unsupported kind: %s (%d)", btf_kind_str(btf_type), type_id);
return -EINVAL;
}
return 0;
}
/* Mark types, members, and member types. Compared to btfgen_record_field_relo,
* this function does not rely on the target spec for inferring members, but
* uses the associated BTF.
*/
static int btfgen_record_type_match_relo(struct btfgen_info *info, struct bpf_core_spec *targ_spec)
{
return btfgen_mark_type_match(info, targ_spec->root_type_id, false);
}
static int btfgen_record_type_relo(struct btfgen_info *info, struct bpf_core_spec *targ_spec) static int btfgen_record_type_relo(struct btfgen_info *info, struct bpf_core_spec *targ_spec)
{ {
return btfgen_mark_type(info, targ_spec->root_type_id, true); return btfgen_mark_type(info, targ_spec->root_type_id, true);
...@@ -1882,6 +1988,8 @@ static int btfgen_record_reloc(struct btfgen_info *info, struct bpf_core_spec *r ...@@ -1882,6 +1988,8 @@ static int btfgen_record_reloc(struct btfgen_info *info, struct bpf_core_spec *r
case BPF_CORE_TYPE_EXISTS: case BPF_CORE_TYPE_EXISTS:
case BPF_CORE_TYPE_SIZE: case BPF_CORE_TYPE_SIZE:
return btfgen_record_type_relo(info, res); return btfgen_record_type_relo(info, res);
case BPF_CORE_TYPE_MATCHES:
return btfgen_record_type_match_relo(info, res);
case BPF_CORE_ENUMVAL_EXISTS: case BPF_CORE_ENUMVAL_EXISTS:
case BPF_CORE_ENUMVAL_VALUE: case BPF_CORE_ENUMVAL_VALUE:
return btfgen_record_enumval_relo(info, res); return btfgen_record_enumval_relo(info, res);
......
...@@ -6786,6 +6786,7 @@ enum bpf_core_relo_kind { ...@@ -6786,6 +6786,7 @@ enum bpf_core_relo_kind {
BPF_CORE_TYPE_SIZE = 9, /* type size in bytes */ BPF_CORE_TYPE_SIZE = 9, /* type size in bytes */
BPF_CORE_ENUMVAL_EXISTS = 10, /* enum value existence in target kernel */ BPF_CORE_ENUMVAL_EXISTS = 10, /* enum value existence in target kernel */
BPF_CORE_ENUMVAL_VALUE = 11, /* enum value integer value */ BPF_CORE_ENUMVAL_VALUE = 11, /* enum value integer value */
BPF_CORE_TYPE_MATCHES = 12, /* type match in target kernel */
}; };
/* /*
......
...@@ -29,6 +29,7 @@ enum bpf_type_id_kind { ...@@ -29,6 +29,7 @@ enum bpf_type_id_kind {
enum bpf_type_info_kind { enum bpf_type_info_kind {
BPF_TYPE_EXISTS = 0, /* type existence in target kernel */ BPF_TYPE_EXISTS = 0, /* type existence in target kernel */
BPF_TYPE_SIZE = 1, /* type size in target kernel */ BPF_TYPE_SIZE = 1, /* type size in target kernel */
BPF_TYPE_MATCHES = 2, /* type match in target kernel */
}; };
/* second argument to __builtin_preserve_enum_value() built-in */ /* second argument to __builtin_preserve_enum_value() built-in */
...@@ -183,6 +184,16 @@ enum bpf_enum_value_kind { ...@@ -183,6 +184,16 @@ enum bpf_enum_value_kind {
#define bpf_core_type_exists(type) \ #define bpf_core_type_exists(type) \
__builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_EXISTS) __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_EXISTS)
/*
* Convenience macro to check that provided named type
* (struct/union/enum/typedef) "matches" that in a target kernel.
* Returns:
* 1, if the type matches in the target kernel's BTF;
* 0, if the type does not match any in the target kernel
*/
#define bpf_core_type_matches(type) \
__builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_MATCHES)
/* /*
* Convenience macro to get the byte size of a provided named type * Convenience macro to get the byte size of a provided named type
* (struct/union/enum/typedef) in a target kernel. * (struct/union/enum/typedef) in a target kernel.
......
...@@ -5470,6 +5470,12 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id, ...@@ -5470,6 +5470,12 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
return __bpf_core_types_are_compat(local_btf, local_id, targ_btf, targ_id, 32); return __bpf_core_types_are_compat(local_btf, local_id, targ_btf, targ_id, 32);
} }
int bpf_core_types_match(const struct btf *local_btf, __u32 local_id,
const struct btf *targ_btf, __u32 targ_id)
{
return __bpf_core_types_match(local_btf, local_id, targ_btf, targ_id, false, 32);
}
static size_t bpf_core_hash_fn(const void *key, void *ctx) static size_t bpf_core_hash_fn(const void *key, void *ctx)
{ {
return (size_t)key; return (size_t)key;
......
...@@ -95,6 +95,7 @@ static const char *core_relo_kind_str(enum bpf_core_relo_kind kind) ...@@ -95,6 +95,7 @@ static const char *core_relo_kind_str(enum bpf_core_relo_kind kind)
case BPF_CORE_TYPE_ID_LOCAL: return "local_type_id"; case BPF_CORE_TYPE_ID_LOCAL: return "local_type_id";
case BPF_CORE_TYPE_ID_TARGET: return "target_type_id"; case BPF_CORE_TYPE_ID_TARGET: return "target_type_id";
case BPF_CORE_TYPE_EXISTS: return "type_exists"; case BPF_CORE_TYPE_EXISTS: return "type_exists";
case BPF_CORE_TYPE_MATCHES: return "type_matches";
case BPF_CORE_TYPE_SIZE: return "type_size"; case BPF_CORE_TYPE_SIZE: return "type_size";
case BPF_CORE_ENUMVAL_EXISTS: return "enumval_exists"; case BPF_CORE_ENUMVAL_EXISTS: return "enumval_exists";
case BPF_CORE_ENUMVAL_VALUE: return "enumval_value"; case BPF_CORE_ENUMVAL_VALUE: return "enumval_value";
...@@ -123,6 +124,7 @@ static bool core_relo_is_type_based(enum bpf_core_relo_kind kind) ...@@ -123,6 +124,7 @@ static bool core_relo_is_type_based(enum bpf_core_relo_kind kind)
case BPF_CORE_TYPE_ID_LOCAL: case BPF_CORE_TYPE_ID_LOCAL:
case BPF_CORE_TYPE_ID_TARGET: case BPF_CORE_TYPE_ID_TARGET:
case BPF_CORE_TYPE_EXISTS: case BPF_CORE_TYPE_EXISTS:
case BPF_CORE_TYPE_MATCHES:
case BPF_CORE_TYPE_SIZE: case BPF_CORE_TYPE_SIZE:
return true; return true;
default: default:
...@@ -251,7 +253,7 @@ int __bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id, ...@@ -251,7 +253,7 @@ int __bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
* - field 'a' access (corresponds to '2' in low-level spec); * - field 'a' access (corresponds to '2' in low-level spec);
* - array element #3 access (corresponds to '3' in low-level spec). * - array element #3 access (corresponds to '3' in low-level spec).
* *
* Type-based relocations (TYPE_EXISTS/TYPE_SIZE, * Type-based relocations (TYPE_EXISTS/TYPE_MATCHES/TYPE_SIZE,
* TYPE_ID_LOCAL/TYPE_ID_TARGET) don't capture any field information. Their * TYPE_ID_LOCAL/TYPE_ID_TARGET) don't capture any field information. Their
* spec and raw_spec are kept empty. * spec and raw_spec are kept empty.
* *
...@@ -568,6 +570,11 @@ static int bpf_core_spec_match(struct bpf_core_spec *local_spec, ...@@ -568,6 +570,11 @@ static int bpf_core_spec_match(struct bpf_core_spec *local_spec,
targ_spec->relo_kind = local_spec->relo_kind; targ_spec->relo_kind = local_spec->relo_kind;
if (core_relo_is_type_based(local_spec->relo_kind)) { if (core_relo_is_type_based(local_spec->relo_kind)) {
if (local_spec->relo_kind == BPF_CORE_TYPE_MATCHES)
return bpf_core_types_match(local_spec->btf,
local_spec->root_type_id,
targ_btf, targ_id);
else
return bpf_core_types_are_compat(local_spec->btf, return bpf_core_types_are_compat(local_spec->btf,
local_spec->root_type_id, local_spec->root_type_id,
targ_btf, targ_id); targ_btf, targ_id);
...@@ -819,6 +826,7 @@ static int bpf_core_calc_type_relo(const struct bpf_core_relo *relo, ...@@ -819,6 +826,7 @@ static int bpf_core_calc_type_relo(const struct bpf_core_relo *relo,
*validate = false; *validate = false;
break; break;
case BPF_CORE_TYPE_EXISTS: case BPF_CORE_TYPE_EXISTS:
case BPF_CORE_TYPE_MATCHES:
*val = 1; *val = 1;
break; break;
case BPF_CORE_TYPE_SIZE: case BPF_CORE_TYPE_SIZE:
...@@ -1410,3 +1418,266 @@ int bpf_core_calc_relo_insn(const char *prog_name, ...@@ -1410,3 +1418,266 @@ int bpf_core_calc_relo_insn(const char *prog_name,
return 0; return 0;
} }
static bool bpf_core_names_match(const struct btf *local_btf, size_t local_name_off,
const struct btf *targ_btf, size_t targ_name_off)
{
const char *local_n, *targ_n;
size_t local_len, targ_len;
local_n = btf__name_by_offset(local_btf, local_name_off);
targ_n = btf__name_by_offset(targ_btf, targ_name_off);
if (str_is_empty(targ_n))
return str_is_empty(local_n);
targ_len = bpf_core_essential_name_len(targ_n);
local_len = bpf_core_essential_name_len(local_n);
return targ_len == local_len && strncmp(local_n, targ_n, local_len) == 0;
}
static int bpf_core_enums_match(const struct btf *local_btf, const struct btf_type *local_t,
const struct btf *targ_btf, const struct btf_type *targ_t)
{
__u16 local_vlen = btf_vlen(local_t);
__u16 targ_vlen = btf_vlen(targ_t);
int i, j;
if (local_t->size != targ_t->size)
return 0;
if (local_vlen > targ_vlen)
return 0;
/* iterate over the local enum's variants and make sure each has
* a symbolic name correspondent in the target
*/
for (i = 0; i < local_vlen; i++) {
bool matched = false;
__u32 local_n_off, targ_n_off;
local_n_off = btf_is_enum(local_t) ? btf_enum(local_t)[i].name_off :
btf_enum64(local_t)[i].name_off;
for (j = 0; j < targ_vlen; j++) {
targ_n_off = btf_is_enum(targ_t) ? btf_enum(targ_t)[j].name_off :
btf_enum64(targ_t)[j].name_off;
if (bpf_core_names_match(local_btf, local_n_off, targ_btf, targ_n_off)) {
matched = true;
break;
}
}
if (!matched)
return 0;
}
return 1;
}
static int bpf_core_composites_match(const struct btf *local_btf, const struct btf_type *local_t,
const struct btf *targ_btf, const struct btf_type *targ_t,
bool behind_ptr, int level)
{
const struct btf_member *local_m = btf_members(local_t);
__u16 local_vlen = btf_vlen(local_t);
__u16 targ_vlen = btf_vlen(targ_t);
int i, j, err;
if (local_vlen > targ_vlen)
return 0;
/* check that all local members have a match in the target */
for (i = 0; i < local_vlen; i++, local_m++) {
const struct btf_member *targ_m = btf_members(targ_t);
bool matched = false;
for (j = 0; j < targ_vlen; j++, targ_m++) {
if (!bpf_core_names_match(local_btf, local_m->name_off,
targ_btf, targ_m->name_off))
continue;
err = __bpf_core_types_match(local_btf, local_m->type, targ_btf,
targ_m->type, behind_ptr, level - 1);
if (err > 0) {
matched = true;
break;
}
}
if (!matched)
return 0;
}
return 1;
}
/* Check that two types "match".
*
* The matching relation is defined as follows:
* - modifiers and typedefs are stripped (and, hence, effectively ignored)
* - generally speaking types need to be of same kind (struct vs. struct, union
* vs. union, etc.)
* - exceptions are struct/union behind a pointer which could also match a
* forward declaration of a struct or union, respectively, and enum vs.
* enum64 (see below)
* Then, depending on type:
* - integers:
* - match if size and signedness match
* - arrays & pointers:
* - target types are recursively matched
* - structs & unions:
* - local members need to exist in target with the same name
* - for each member we recursively check match unless it is already behind a
* pointer, in which case we only check matching names and compatible kind
* - enums:
* - local variants have to have a match in target by symbolic name (but not
* numeric value)
* - size has to match (but enum may match enum64 and vice versa)
* - function pointers:
* - number and position of arguments in local type has to match target
* - for each argument and the return value we recursively check match
*/
int __bpf_core_types_match(const struct btf *local_btf, __u32 local_id, const struct btf *targ_btf,
__u32 targ_id, bool behind_ptr, int level)
{
const struct btf_type *local_t, *targ_t;
int depth = 32; /* max recursion depth */
__u16 local_k, targ_k;
if (level <= 0)
return -EINVAL;
local_t = btf_type_by_id(local_btf, local_id);
targ_t = btf_type_by_id(targ_btf, targ_id);
recur:
depth--;
if (depth < 0)
return -EINVAL;
local_t = skip_mods_and_typedefs(local_btf, local_id, &local_id);
targ_t = skip_mods_and_typedefs(targ_btf, targ_id, &targ_id);
if (!local_t || !targ_t)
return -EINVAL;
if (!bpf_core_names_match(local_btf, local_t->name_off, targ_btf, targ_t->name_off))
return 0;
local_k = btf_kind(local_t);
targ_k = btf_kind(targ_t);
switch (local_k) {
case BTF_KIND_UNKN:
return local_k == targ_k;
case BTF_KIND_FWD: {
bool local_f = BTF_INFO_KFLAG(local_t->info);
if (behind_ptr) {
if (local_k == targ_k)
return local_f == BTF_INFO_KFLAG(targ_t->info);
/* for forward declarations kflag dictates whether the
* target is a struct (0) or union (1)
*/
return (targ_k == BTF_KIND_STRUCT && !local_f) ||
(targ_k == BTF_KIND_UNION && local_f);
} else {
if (local_k != targ_k)
return 0;
/* match if the forward declaration is for the same kind */
return local_f == BTF_INFO_KFLAG(targ_t->info);
}
}
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
if (!btf_is_any_enum(targ_t))
return 0;
return bpf_core_enums_match(local_btf, local_t, targ_btf, targ_t);
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
if (behind_ptr) {
bool targ_f = BTF_INFO_KFLAG(targ_t->info);
if (local_k == targ_k)
return 1;
if (targ_k != BTF_KIND_FWD)
return 0;
return (local_k == BTF_KIND_UNION) == targ_f;
} else {
if (local_k != targ_k)
return 0;
return bpf_core_composites_match(local_btf, local_t, targ_btf, targ_t,
behind_ptr, level);
}
case BTF_KIND_INT: {
__u8 local_sgn;
__u8 targ_sgn;
if (local_k != targ_k)
return 0;
local_sgn = btf_int_encoding(local_t) & BTF_INT_SIGNED;
targ_sgn = btf_int_encoding(targ_t) & BTF_INT_SIGNED;
return local_t->size == targ_t->size && local_sgn == targ_sgn;
}
case BTF_KIND_PTR:
if (local_k != targ_k)
return 0;
behind_ptr = true;
local_id = local_t->type;
targ_id = targ_t->type;
goto recur;
case BTF_KIND_ARRAY: {
const struct btf_array *local_array = btf_array(local_t);
const struct btf_array *targ_array = btf_array(targ_t);
if (local_k != targ_k)
return 0;
if (local_array->nelems != targ_array->nelems)
return 0;
local_id = local_array->type;
targ_id = targ_array->type;
goto recur;
}
case BTF_KIND_FUNC_PROTO: {
struct btf_param *local_p = btf_params(local_t);
struct btf_param *targ_p = btf_params(targ_t);
__u16 local_vlen = btf_vlen(local_t);
__u16 targ_vlen = btf_vlen(targ_t);
int i, err;
if (local_k != targ_k)
return 0;
if (local_vlen != targ_vlen)
return 0;
for (i = 0; i < local_vlen; i++, local_p++, targ_p++) {
err = __bpf_core_types_match(local_btf, local_p->type, targ_btf,
targ_p->type, behind_ptr, level - 1);
if (err <= 0)
return err;
}
/* tail recurse for return type check */
local_id = local_t->type;
targ_id = targ_t->type;
goto recur;
}
default:
pr_warn("unexpected kind %s relocated, local [%d], target [%d]\n",
btf_kind_str(local_t), local_id, targ_id);
return 0;
}
}
...@@ -72,6 +72,10 @@ int __bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id, ...@@ -72,6 +72,10 @@ int __bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
const struct btf *targ_btf, __u32 targ_id, int level); const struct btf *targ_btf, __u32 targ_id, int level);
int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id, int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
const struct btf *targ_btf, __u32 targ_id); const struct btf *targ_btf, __u32 targ_id);
int __bpf_core_types_match(const struct btf *local_btf, __u32 local_id, const struct btf *targ_btf,
__u32 targ_id, bool behind_ptr, int level);
int bpf_core_types_match(const struct btf *local_btf, __u32 local_id, const struct btf *targ_btf,
__u32 targ_id);
size_t bpf_core_essential_name_len(const char *name); size_t bpf_core_essential_name_len(const char *name);
......
...@@ -543,7 +543,6 @@ static int __trigger_module_test_read(const struct core_reloc_test_case *test) ...@@ -543,7 +543,6 @@ static int __trigger_module_test_read(const struct core_reloc_test_case *test)
return 0; return 0;
} }
static const struct core_reloc_test_case test_cases[] = { static const struct core_reloc_test_case test_cases[] = {
/* validate we can find kernel image and use its BTF for relocs */ /* validate we can find kernel image and use its BTF for relocs */
{ {
...@@ -556,6 +555,7 @@ static const struct core_reloc_test_case test_cases[] = { ...@@ -556,6 +555,7 @@ static const struct core_reloc_test_case test_cases[] = {
.valid = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, .valid = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, },
.comm = "test_progs", .comm = "test_progs",
.comm_len = sizeof("test_progs"), .comm_len = sizeof("test_progs"),
.local_task_struct_matches = true,
}, },
.output_len = sizeof(struct core_reloc_kernel_output), .output_len = sizeof(struct core_reloc_kernel_output),
.raw_tp_name = "sys_enter", .raw_tp_name = "sys_enter",
...@@ -752,9 +752,10 @@ static const struct core_reloc_test_case test_cases[] = { ...@@ -752,9 +752,10 @@ static const struct core_reloc_test_case test_cases[] = {
SIZE_CASE(size___diff_offs), SIZE_CASE(size___diff_offs),
SIZE_ERR_CASE(size___err_ambiguous), SIZE_ERR_CASE(size___err_ambiguous),
/* validate type existence and size relocations */ /* validate type existence, match, and size relocations */
TYPE_BASED_CASE(type_based, { TYPE_BASED_CASE(type_based, {
.struct_exists = 1, .struct_exists = 1,
.complex_struct_exists = 1,
.union_exists = 1, .union_exists = 1,
.enum_exists = 1, .enum_exists = 1,
.typedef_named_struct_exists = 1, .typedef_named_struct_exists = 1,
...@@ -765,6 +766,20 @@ static const struct core_reloc_test_case test_cases[] = { ...@@ -765,6 +766,20 @@ static const struct core_reloc_test_case test_cases[] = {
.typedef_void_ptr_exists = 1, .typedef_void_ptr_exists = 1,
.typedef_func_proto_exists = 1, .typedef_func_proto_exists = 1,
.typedef_arr_exists = 1, .typedef_arr_exists = 1,
.struct_matches = 1,
.complex_struct_matches = 1,
.union_matches = 1,
.enum_matches = 1,
.typedef_named_struct_matches = 1,
.typedef_anon_struct_matches = 1,
.typedef_struct_ptr_matches = 1,
.typedef_int_matches = 1,
.typedef_enum_matches = 1,
.typedef_void_ptr_matches = 1,
.typedef_func_proto_matches = 1,
.typedef_arr_matches = 1,
.struct_sz = sizeof(struct a_struct), .struct_sz = sizeof(struct a_struct),
.union_sz = sizeof(union a_union), .union_sz = sizeof(union a_union),
.enum_sz = sizeof(enum an_enum), .enum_sz = sizeof(enum an_enum),
...@@ -780,6 +795,45 @@ static const struct core_reloc_test_case test_cases[] = { ...@@ -780,6 +795,45 @@ static const struct core_reloc_test_case test_cases[] = {
TYPE_BASED_CASE(type_based___all_missing, { TYPE_BASED_CASE(type_based___all_missing, {
/* all zeros */ /* all zeros */
}), }),
TYPE_BASED_CASE(type_based___diff, {
.struct_exists = 1,
.complex_struct_exists = 1,
.union_exists = 1,
.enum_exists = 1,
.typedef_named_struct_exists = 1,
.typedef_anon_struct_exists = 1,
.typedef_struct_ptr_exists = 1,
.typedef_int_exists = 1,
.typedef_enum_exists = 1,
.typedef_void_ptr_exists = 1,
.typedef_func_proto_exists = 1,
.typedef_arr_exists = 1,
.struct_matches = 1,
.complex_struct_matches = 1,
.union_matches = 1,
.enum_matches = 1,
.typedef_named_struct_matches = 1,
.typedef_anon_struct_matches = 1,
.typedef_struct_ptr_matches = 1,
.typedef_int_matches = 0,
.typedef_enum_matches = 1,
.typedef_void_ptr_matches = 1,
.typedef_func_proto_matches = 0,
.typedef_arr_matches = 0,
.struct_sz = sizeof(struct a_struct___diff),
.union_sz = sizeof(union a_union___diff),
.enum_sz = sizeof(enum an_enum___diff),
.typedef_named_struct_sz = sizeof(named_struct_typedef___diff),
.typedef_anon_struct_sz = sizeof(anon_struct_typedef___diff),
.typedef_struct_ptr_sz = sizeof(struct_ptr_typedef___diff),
.typedef_int_sz = sizeof(int_typedef___diff),
.typedef_enum_sz = sizeof(enum_typedef___diff),
.typedef_void_ptr_sz = sizeof(void_ptr_typedef___diff),
.typedef_func_proto_sz = sizeof(func_proto_typedef___diff),
.typedef_arr_sz = sizeof(arr_typedef___diff),
}),
TYPE_BASED_CASE(type_based___diff_sz, { TYPE_BASED_CASE(type_based___diff_sz, {
.struct_exists = 1, .struct_exists = 1,
.union_exists = 1, .union_exists = 1,
...@@ -792,6 +846,19 @@ static const struct core_reloc_test_case test_cases[] = { ...@@ -792,6 +846,19 @@ static const struct core_reloc_test_case test_cases[] = {
.typedef_void_ptr_exists = 1, .typedef_void_ptr_exists = 1,
.typedef_func_proto_exists = 1, .typedef_func_proto_exists = 1,
.typedef_arr_exists = 1, .typedef_arr_exists = 1,
.struct_matches = 0,
.union_matches = 0,
.enum_matches = 0,
.typedef_named_struct_matches = 0,
.typedef_anon_struct_matches = 0,
.typedef_struct_ptr_matches = 1,
.typedef_int_matches = 0,
.typedef_enum_matches = 0,
.typedef_void_ptr_matches = 1,
.typedef_func_proto_matches = 0,
.typedef_arr_matches = 0,
.struct_sz = sizeof(struct a_struct___diff_sz), .struct_sz = sizeof(struct a_struct___diff_sz),
.union_sz = sizeof(union a_union___diff_sz), .union_sz = sizeof(union a_union___diff_sz),
.enum_sz = sizeof(enum an_enum___diff_sz), .enum_sz = sizeof(enum an_enum___diff_sz),
...@@ -806,10 +873,12 @@ static const struct core_reloc_test_case test_cases[] = { ...@@ -806,10 +873,12 @@ static const struct core_reloc_test_case test_cases[] = {
}), }),
TYPE_BASED_CASE(type_based___incompat, { TYPE_BASED_CASE(type_based___incompat, {
.enum_exists = 1, .enum_exists = 1,
.enum_matches = 1,
.enum_sz = sizeof(enum an_enum), .enum_sz = sizeof(enum an_enum),
}), }),
TYPE_BASED_CASE(type_based___fn_wrong_args, { TYPE_BASED_CASE(type_based___fn_wrong_args, {
.struct_exists = 1, .struct_exists = 1,
.struct_matches = 1,
.struct_sz = sizeof(struct a_struct), .struct_sz = sizeof(struct a_struct),
}), }),
......
#include "core_reloc_types.h"
void f(struct core_reloc_type_based___diff x) {}
...@@ -13,6 +13,7 @@ struct core_reloc_kernel_output { ...@@ -13,6 +13,7 @@ struct core_reloc_kernel_output {
int valid[10]; int valid[10];
char comm[sizeof("test_progs")]; char comm[sizeof("test_progs")];
int comm_len; int comm_len;
bool local_task_struct_matches;
}; };
/* /*
...@@ -860,10 +861,11 @@ struct core_reloc_size___err_ambiguous2 { ...@@ -860,10 +861,11 @@ struct core_reloc_size___err_ambiguous2 {
}; };
/* /*
* TYPE EXISTENCE & SIZE * TYPE EXISTENCE, MATCH & SIZE
*/ */
struct core_reloc_type_based_output { struct core_reloc_type_based_output {
bool struct_exists; bool struct_exists;
bool complex_struct_exists;
bool union_exists; bool union_exists;
bool enum_exists; bool enum_exists;
bool typedef_named_struct_exists; bool typedef_named_struct_exists;
...@@ -875,6 +877,19 @@ struct core_reloc_type_based_output { ...@@ -875,6 +877,19 @@ struct core_reloc_type_based_output {
bool typedef_func_proto_exists; bool typedef_func_proto_exists;
bool typedef_arr_exists; bool typedef_arr_exists;
bool struct_matches;
bool complex_struct_matches;
bool union_matches;
bool enum_matches;
bool typedef_named_struct_matches;
bool typedef_anon_struct_matches;
bool typedef_struct_ptr_matches;
bool typedef_int_matches;
bool typedef_enum_matches;
bool typedef_void_ptr_matches;
bool typedef_func_proto_matches;
bool typedef_arr_matches;
int struct_sz; int struct_sz;
int union_sz; int union_sz;
int enum_sz; int enum_sz;
...@@ -892,6 +907,14 @@ struct a_struct { ...@@ -892,6 +907,14 @@ struct a_struct {
int x; int x;
}; };
struct a_complex_struct {
union {
struct a_struct * restrict a;
void *b;
} x;
volatile long y;
};
union a_union { union a_union {
int y; int y;
int z; int z;
...@@ -923,22 +946,85 @@ typedef char arr_typedef[20]; ...@@ -923,22 +946,85 @@ typedef char arr_typedef[20];
struct core_reloc_type_based { struct core_reloc_type_based {
struct a_struct f1; struct a_struct f1;
union a_union f2; struct a_complex_struct f2;
enum an_enum f3; union a_union f3;
named_struct_typedef f4; enum an_enum f4;
anon_struct_typedef f5; named_struct_typedef f5;
struct_ptr_typedef f6; anon_struct_typedef f6;
int_typedef f7; struct_ptr_typedef f7;
enum_typedef f8; int_typedef f8;
void_ptr_typedef f9; enum_typedef f9;
func_proto_typedef f10; void_ptr_typedef f10;
arr_typedef f11; func_proto_typedef f11;
arr_typedef f12;
}; };
/* no types in target */ /* no types in target */
struct core_reloc_type_based___all_missing { struct core_reloc_type_based___all_missing {
}; };
/* different member orders, enum variant values, signedness, etc */
struct a_struct___diff {
int x;
int a;
};
struct a_struct___forward;
struct a_complex_struct___diff {
union {
struct a_struct___forward *a;
void *b;
} x;
volatile long y;
};
union a_union___diff {
int z;
int y;
};
typedef struct a_struct___diff named_struct_typedef___diff;
typedef struct { int z, x, y; } anon_struct_typedef___diff;
typedef struct {
int c;
int b;
int a;
} *struct_ptr_typedef___diff;
enum an_enum___diff {
AN_ENUM_VAL2___diff = 0,
AN_ENUM_VAL1___diff = 42,
AN_ENUM_VAL3___diff = 1,
};
typedef unsigned int int_typedef___diff;
typedef enum { TYPEDEF_ENUM_VAL2___diff, TYPEDEF_ENUM_VAL1___diff = 50 } enum_typedef___diff;
typedef const void *void_ptr_typedef___diff;
typedef int_typedef___diff (*func_proto_typedef___diff)(long);
typedef char arr_typedef___diff[3];
struct core_reloc_type_based___diff {
struct a_struct___diff f1;
struct a_complex_struct___diff f2;
union a_union___diff f3;
enum an_enum___diff f4;
named_struct_typedef___diff f5;
anon_struct_typedef___diff f6;
struct_ptr_typedef___diff f7;
int_typedef___diff f8;
enum_typedef___diff f9;
void_ptr_typedef___diff f10;
func_proto_typedef___diff f11;
arr_typedef___diff f12;
};
/* different type sizes, extra modifiers, anon vs named enums, etc */ /* different type sizes, extra modifiers, anon vs named enums, etc */
struct a_struct___diff_sz { struct a_struct___diff_sz {
long x; long x;
......
...@@ -21,6 +21,7 @@ struct core_reloc_kernel_output { ...@@ -21,6 +21,7 @@ struct core_reloc_kernel_output {
/* we have test_progs[-flavor], so cut flavor part */ /* we have test_progs[-flavor], so cut flavor part */
char comm[sizeof("test_progs")]; char comm[sizeof("test_progs")];
int comm_len; int comm_len;
bool local_task_struct_matches;
}; };
struct task_struct { struct task_struct {
...@@ -30,11 +31,25 @@ struct task_struct { ...@@ -30,11 +31,25 @@ struct task_struct {
struct task_struct *group_leader; struct task_struct *group_leader;
}; };
struct mm_struct___wrong {
int abc_whatever_should_not_exist;
};
struct task_struct___local {
int pid;
struct mm_struct___wrong *mm;
};
#define CORE_READ(dst, src) bpf_core_read(dst, sizeof(*(dst)), src) #define CORE_READ(dst, src) bpf_core_read(dst, sizeof(*(dst)), src)
SEC("raw_tracepoint/sys_enter") SEC("raw_tracepoint/sys_enter")
int test_core_kernel(void *ctx) int test_core_kernel(void *ctx)
{ {
/* Support for the BPF_TYPE_MATCHES argument to the
* __builtin_preserve_type_info builtin was added at some point during
* development of clang 15 and it's what we require for this test.
*/
#if __has_builtin(__builtin_preserve_type_info) && __clang_major__ >= 15
struct task_struct *task = (void *)bpf_get_current_task(); struct task_struct *task = (void *)bpf_get_current_task();
struct core_reloc_kernel_output *out = (void *)&data.out; struct core_reloc_kernel_output *out = (void *)&data.out;
uint64_t pid_tgid = bpf_get_current_pid_tgid(); uint64_t pid_tgid = bpf_get_current_pid_tgid();
...@@ -93,6 +108,10 @@ int test_core_kernel(void *ctx) ...@@ -93,6 +108,10 @@ int test_core_kernel(void *ctx)
group_leader, group_leader, group_leader, group_leader, group_leader, group_leader, group_leader, group_leader,
comm); comm);
out->local_task_struct_matches = bpf_core_type_matches(struct task_struct___local);
#else
data.skip = true;
#endif
return 0; return 0;
} }
...@@ -19,6 +19,14 @@ struct a_struct { ...@@ -19,6 +19,14 @@ struct a_struct {
int x; int x;
}; };
struct a_complex_struct {
union {
struct a_struct *a;
void *b;
} x;
volatile long y;
};
union a_union { union a_union {
int y; int y;
int z; int z;
...@@ -50,6 +58,7 @@ typedef char arr_typedef[20]; ...@@ -50,6 +58,7 @@ typedef char arr_typedef[20];
struct core_reloc_type_based_output { struct core_reloc_type_based_output {
bool struct_exists; bool struct_exists;
bool complex_struct_exists;
bool union_exists; bool union_exists;
bool enum_exists; bool enum_exists;
bool typedef_named_struct_exists; bool typedef_named_struct_exists;
...@@ -61,6 +70,19 @@ struct core_reloc_type_based_output { ...@@ -61,6 +70,19 @@ struct core_reloc_type_based_output {
bool typedef_func_proto_exists; bool typedef_func_proto_exists;
bool typedef_arr_exists; bool typedef_arr_exists;
bool struct_matches;
bool complex_struct_matches;
bool union_matches;
bool enum_matches;
bool typedef_named_struct_matches;
bool typedef_anon_struct_matches;
bool typedef_struct_ptr_matches;
bool typedef_int_matches;
bool typedef_enum_matches;
bool typedef_void_ptr_matches;
bool typedef_func_proto_matches;
bool typedef_arr_matches;
int struct_sz; int struct_sz;
int union_sz; int union_sz;
int enum_sz; int enum_sz;
...@@ -77,10 +99,17 @@ struct core_reloc_type_based_output { ...@@ -77,10 +99,17 @@ struct core_reloc_type_based_output {
SEC("raw_tracepoint/sys_enter") SEC("raw_tracepoint/sys_enter")
int test_core_type_based(void *ctx) int test_core_type_based(void *ctx)
{ {
#if __has_builtin(__builtin_preserve_type_info) /* Support for the BPF_TYPE_MATCHES argument to the
* __builtin_preserve_type_info builtin was added at some point during
* development of clang 15 and it's what we require for this test. Part of it
* could run with merely __builtin_preserve_type_info (which could be checked
* separately), but we have to find an upper bound.
*/
#if __has_builtin(__builtin_preserve_type_info) && __clang_major__ >= 15
struct core_reloc_type_based_output *out = (void *)&data.out; struct core_reloc_type_based_output *out = (void *)&data.out;
out->struct_exists = bpf_core_type_exists(struct a_struct); out->struct_exists = bpf_core_type_exists(struct a_struct);
out->complex_struct_exists = bpf_core_type_exists(struct a_complex_struct);
out->union_exists = bpf_core_type_exists(union a_union); out->union_exists = bpf_core_type_exists(union a_union);
out->enum_exists = bpf_core_type_exists(enum an_enum); out->enum_exists = bpf_core_type_exists(enum an_enum);
out->typedef_named_struct_exists = bpf_core_type_exists(named_struct_typedef); out->typedef_named_struct_exists = bpf_core_type_exists(named_struct_typedef);
...@@ -92,6 +121,19 @@ int test_core_type_based(void *ctx) ...@@ -92,6 +121,19 @@ int test_core_type_based(void *ctx)
out->typedef_func_proto_exists = bpf_core_type_exists(func_proto_typedef); out->typedef_func_proto_exists = bpf_core_type_exists(func_proto_typedef);
out->typedef_arr_exists = bpf_core_type_exists(arr_typedef); out->typedef_arr_exists = bpf_core_type_exists(arr_typedef);
out->struct_matches = bpf_core_type_matches(struct a_struct);
out->complex_struct_matches = bpf_core_type_matches(struct a_complex_struct);
out->union_matches = bpf_core_type_matches(union a_union);
out->enum_matches = bpf_core_type_matches(enum an_enum);
out->typedef_named_struct_matches = bpf_core_type_matches(named_struct_typedef);
out->typedef_anon_struct_matches = bpf_core_type_matches(anon_struct_typedef);
out->typedef_struct_ptr_matches = bpf_core_type_matches(struct_ptr_typedef);
out->typedef_int_matches = bpf_core_type_matches(int_typedef);
out->typedef_enum_matches = bpf_core_type_matches(enum_typedef);
out->typedef_void_ptr_matches = bpf_core_type_matches(void_ptr_typedef);
out->typedef_func_proto_matches = bpf_core_type_matches(func_proto_typedef);
out->typedef_arr_matches = bpf_core_type_matches(arr_typedef);
out->struct_sz = bpf_core_type_size(struct a_struct); out->struct_sz = bpf_core_type_size(struct a_struct);
out->union_sz = bpf_core_type_size(union a_union); out->union_sz = bpf_core_type_size(union a_union);
out->enum_sz = bpf_core_type_size(enum an_enum); out->enum_sz = bpf_core_type_size(enum an_enum);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment