There are places in code which can be simplified by using the btf_type_is_regular_int() helper (slightly patched to add an additional, optional, argument to check the exact size). So patch the helper, export it, and simplify code in a few files. (Suggested by Eduard in a bit different form in [1].) [1] https://lore.kernel.org/bpf/7edb47e73baa46705119a23c6bf4af26517a640f.camel@xxxxxxxxx/ Suggested-by: Eduard Zingerman <eddyz87@xxxxxxxxx> Signed-off-by: Anton Protopopov <a.s.protopopov@xxxxxxxxx> --- include/linux/btf.h | 1 + kernel/bpf/arraymap.c | 11 +++------ kernel/bpf/bpf_local_storage.c | 8 +------ kernel/bpf/btf.c | 43 +++++++++++++++++----------------- kernel/bpf/local_storage.c | 9 +------ 5 files changed, 27 insertions(+), 45 deletions(-) diff --git a/include/linux/btf.h b/include/linux/btf.h index b2983706292f..dbd52be2dbc4 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -221,6 +221,7 @@ bool btf_is_vmlinux(const struct btf *btf); struct module *btf_try_get_module(const struct btf *btf); u32 btf_nr_types(const struct btf *btf); struct btf *btf_base_btf(const struct btf *btf); +bool btf_type_is_regular_int(const struct btf_type *t, size_t expected_size); bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s, const struct btf_member *m, u32 expected_offset, u32 expected_size); diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index eb28c0f219ee..f7f84800c1dc 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -530,8 +530,6 @@ static int array_map_check_btf(const struct bpf_map *map, const struct btf_type *key_type, const struct btf_type *value_type) { - u32 int_data; - /* One exception for keyless BTF: .bss/.data/.rodata map */ if (btf_type_is_void(key_type)) { if (map->map_type != BPF_MAP_TYPE_ARRAY || @@ -544,14 +542,11 @@ static int array_map_check_btf(const struct bpf_map *map, return 0; } - if (BTF_INFO_KIND(key_type->info) != BTF_KIND_INT) - return -EINVAL; - - int_data = *(u32 *)(key_type + 1); - /* bpf array can only take a u32 key. This check makes sure + /* + * Bpf array can only take a u32 key. This check makes sure * that the btf matches the attr used during map_create. */ - if (BTF_INT_BITS(int_data) != 32 || BTF_INT_OFFSET(int_data)) + if (!btf_type_is_regular_int(key_type, sizeof(u32))) return -EINVAL; return 0; diff --git a/kernel/bpf/bpf_local_storage.c b/kernel/bpf/bpf_local_storage.c index fa56c30833ff..1ea8fb93d55e 100644 --- a/kernel/bpf/bpf_local_storage.c +++ b/kernel/bpf/bpf_local_storage.c @@ -722,13 +722,7 @@ int bpf_local_storage_map_check_btf(const struct bpf_map *map, const struct btf_type *key_type, const struct btf_type *value_type) { - u32 int_data; - - if (BTF_INFO_KIND(key_type->info) != BTF_KIND_INT) - return -EINVAL; - - int_data = *(u32 *)(key_type + 1); - if (BTF_INT_BITS(int_data) != 32 || BTF_INT_OFFSET(int_data)) + if (!btf_type_is_regular_int(key_type, sizeof(u32))) return -EINVAL; return 0; diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 682acb1ed234..ea21b74d5027 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -857,27 +857,27 @@ const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id) } EXPORT_SYMBOL_GPL(btf_type_by_id); -/* - * Regular int is not a bit field and it must be either - * u8/u16/u32/u64 or __int128. - */ -static bool btf_type_int_is_regular(const struct btf_type *t) +static bool btf_type_int_is_regular(const struct btf_type *t, size_t expected_size) { - u8 nr_bits, nr_bytes; - u32 int_data; + u32 int_data = btf_type_int(t); + u8 nr_bits = BTF_INT_BITS(int_data); + u8 nr_bytes = BITS_ROUNDUP_BYTES(nr_bits); - int_data = btf_type_int(t); - nr_bits = BTF_INT_BITS(int_data); - nr_bytes = BITS_ROUNDUP_BYTES(nr_bits); - if (BITS_PER_BYTE_MASKED(nr_bits) || - BTF_INT_OFFSET(int_data) || - (nr_bytes != sizeof(u8) && nr_bytes != sizeof(u16) && - nr_bytes != sizeof(u32) && nr_bytes != sizeof(u64) && - nr_bytes != (2 * sizeof(u64)))) { - return false; - } + return BITS_PER_BYTE_MASKED(nr_bits) == 0 && + BTF_INT_OFFSET(int_data) == 0 && + (nr_bytes <= 16 && is_power_of_2(nr_bytes)) && + (expected_size == 0 || nr_bytes == expected_size); +} - return true; +/* + * Check that the type @t is a regular int. This means that @t is not + * a bit field and it has the same size as either of u8/u16/u32/u64 + * or __int128. If @expected_size is not zero, then size of @t should + * be the same. + */ +bool btf_type_is_regular_int(const struct btf_type *t, size_t expected_size) +{ + return btf_type_is_int(t) && btf_type_int_is_regular(t, expected_size); } /* @@ -2180,7 +2180,7 @@ static int btf_int_check_kflag_member(struct btf_verifier_env *env, u32 nr_copy_bits; /* a regular int type is required for the kflag int member */ - if (!btf_type_int_is_regular(member_type)) { + if (!btf_type_int_is_regular(member_type, 0)) { btf_verifier_log_member(env, struct_type, member, "Invalid member base type"); return -EINVAL; @@ -2969,8 +2969,7 @@ static int btf_array_resolve(struct btf_verifier_env *env, return env_stack_push(env, index_type, index_type_id); index_type = btf_type_id_size(btf, &index_type_id, NULL); - if (!index_type || !btf_type_is_int(index_type) || - !btf_type_int_is_regular(index_type)) { + if (!index_type || !btf_type_is_regular_int(index_type, 0)) { btf_verifier_log_type(env, v->t, "Invalid index"); return -EINVAL; } @@ -2995,7 +2994,7 @@ static int btf_array_resolve(struct btf_verifier_env *env, return -EINVAL; } - if (btf_type_is_int(elem_type) && !btf_type_int_is_regular(elem_type)) { + if (btf_type_is_int(elem_type) && !btf_type_int_is_regular(elem_type, 0)) { btf_verifier_log_type(env, v->t, "Invalid array of int"); return -EINVAL; } diff --git a/kernel/bpf/local_storage.c b/kernel/bpf/local_storage.c index 3969eb0382af..8645a39e8dd1 100644 --- a/kernel/bpf/local_storage.c +++ b/kernel/bpf/local_storage.c @@ -394,17 +394,10 @@ static int cgroup_storage_check_btf(const struct bpf_map *map, if (!btf_member_is_reg_int(btf, key_type, m, offset, size)) return -EINVAL; } else { - u32 int_data; - /* * Key is expected to be u64, which stores the cgroup_inode_id */ - - if (BTF_INFO_KIND(key_type->info) != BTF_KIND_INT) - return -EINVAL; - - int_data = *(u32 *)(key_type + 1); - if (BTF_INT_BITS(int_data) != 64 || BTF_INT_OFFSET(int_data)) + if (!btf_type_is_regular_int(key_type, sizeof(u64))) return -EINVAL; } -- 2.34.1