This patch ensures the member->offset of a struct
is in the correct order (i.e the later member's offset cannot
go backward).

The current "pahole -J" BTF encoder does not generate something
like this.  However, checking this can ensure future encoder
will not violate this.

Fixes: 69b693f0aefa ("bpf: btf: Introduce BPF Type Format (BTF)")
Signed-off-by: Martin KaFai Lau <ka...@fb.com>
---
 kernel/bpf/btf.c                       | 14 ++++++++++++-
 tools/testing/selftests/bpf/test_btf.c | 28 ++++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 9704934252b3..2590700237c1 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -1519,9 +1519,9 @@ static s32 btf_struct_check_meta(struct btf_verifier_env 
*env,
 {
        bool is_union = BTF_INFO_KIND(t->info) == BTF_KIND_UNION;
        const struct btf_member *member;
+       u32 meta_needed, last_offset;
        struct btf *btf = env->btf;
        u32 struct_size = t->size;
-       u32 meta_needed;
        u16 i;
 
        meta_needed = btf_type_vlen(t) * sizeof(*member);
@@ -1534,6 +1534,7 @@ static s32 btf_struct_check_meta(struct btf_verifier_env 
*env,
 
        btf_verifier_log_type(env, t, NULL);
 
+       last_offset = 0;
        for_each_member(i, t, member) {
                if (!btf_name_offset_valid(btf, member->name_off)) {
                        btf_verifier_log_member(env, t, member,
@@ -1555,6 +1556,16 @@ static s32 btf_struct_check_meta(struct btf_verifier_env 
*env,
                        return -EINVAL;
                }
 
+               /*
+                * ">" instead of ">=" because the last member could be
+                * "char a[0];"
+                */
+               if (last_offset > member->offset) {
+                       btf_verifier_log_member(env, t, member,
+                                               "Invalid member bits_offset");
+                       return -EINVAL;
+               }
+
                if (BITS_ROUNDUP_BYTES(member->offset) > struct_size) {
                        btf_verifier_log_member(env, t, member,
                                                "Memmber bits_offset exceeds 
its struct size");
@@ -1562,6 +1573,7 @@ static s32 btf_struct_check_meta(struct btf_verifier_env 
*env,
                }
 
                btf_verifier_log_member(env, t, member, NULL);
+               last_offset = member->offset;
        }
 
        return meta_needed;
diff --git a/tools/testing/selftests/bpf/test_btf.c 
b/tools/testing/selftests/bpf/test_btf.c
index 3619f3023088..402c0f7cc418 100644
--- a/tools/testing/selftests/bpf/test_btf.c
+++ b/tools/testing/selftests/bpf/test_btf.c
@@ -247,6 +247,34 @@ static struct btf_raw_test raw_tests[] = {
        .max_entries = 4,
 },
 
+{
+       .descr = "struct test #3 Invalid member offset",
+       .raw_types = {
+               /* int */                                       /* [1] */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),
+               /* int64 */                                     /* [2] */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 64, 8),
+
+               /* struct A { */                                /* [3] */
+               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 2), 16),
+               BTF_MEMBER_ENC(NAME_TBD, 1, 64),        /* int m;               
*/
+               BTF_MEMBER_ENC(NAME_TBD, 2, 0),         /* int64 n; */
+               /* } */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0A\0m\0n\0",
+       .str_sec_size = sizeof("\0A\0m\0n\0"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = "struct_test3_map",
+       .key_size = sizeof(int),
+       .value_size = 16,
+       .key_type_id = 1,
+       .value_type_id = 3,
+       .max_entries = 4,
+       .btf_load_err = true,
+       .err_str = "Invalid member bits_offset",
+},
+
 /* Test member exceeds the size of struct.
  *
  * struct A {
-- 
2.17.1

Reply via email to