]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
btf: Support kernel parsing of BTF with layout info
authorAlan Maguire <alan.maguire@oracle.com>
Thu, 26 Mar 2026 14:54:42 +0000 (14:54 +0000)
committerAndrii Nakryiko <andrii@kernel.org>
Thu, 26 Mar 2026 20:53:56 +0000 (13:53 -0700)
Validate layout if present, but because the kernel must be
strict in what it accepts, reject BTF with unsupported kinds,
even if they are in the layout information.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20260326145444.2076244-8-alan.maguire@oracle.com
kernel/bpf/btf.c

index 15f4c99a46c0f28263ca5aea5ff6e165766ef07c..a62d78581207670430cd1c88af52fe934956c412 100644 (file)
@@ -270,6 +270,7 @@ struct btf {
        struct btf_id_dtor_kfunc_tab *dtor_kfunc_tab;
        struct btf_struct_metas *struct_meta_tab;
        struct btf_struct_ops_tab *struct_ops_tab;
+       struct btf_layout *layout;
 
        /* split BTF support */
        struct btf *base_btf;
@@ -1707,6 +1708,11 @@ static void btf_verifier_log_hdr(struct btf_verifier_env *env,
        __btf_verifier_log(log, "type_len: %u\n", hdr->type_len);
        __btf_verifier_log(log, "str_off: %u\n", hdr->str_off);
        __btf_verifier_log(log, "str_len: %u\n", hdr->str_len);
+       if (hdr->hdr_len >= sizeof(struct btf_header) &&
+           btf_data_size >= hdr->hdr_len) {
+               __btf_verifier_log(log, "layout_off: %u\n", hdr->layout_off);
+               __btf_verifier_log(log, "layout_len: %u\n", hdr->layout_len);
+       }
        __btf_verifier_log(log, "btf_total_size: %u\n", btf_data_size);
 }
 
@@ -5526,7 +5532,8 @@ static int btf_parse_str_sec(struct btf_verifier_env *env)
        start = btf->nohdr_data + hdr->str_off;
        end = start + hdr->str_len;
 
-       if (end != btf->data + btf->data_size) {
+       if (hdr->hdr_len < sizeof(struct btf_header) &&
+           end != btf->data + btf->data_size) {
                btf_verifier_log(env, "String section is not at the end");
                return -EINVAL;
        }
@@ -5547,9 +5554,46 @@ static int btf_parse_str_sec(struct btf_verifier_env *env)
        return 0;
 }
 
+static int btf_parse_layout_sec(struct btf_verifier_env *env)
+{
+       const struct btf_header *hdr = &env->btf->hdr;
+       struct btf *btf = env->btf;
+       void *start, *end;
+
+       if (hdr->hdr_len < sizeof(struct btf_header) ||
+           hdr->layout_len == 0)
+               return 0;
+
+       /* Layout section must align to 4 bytes */
+       if (hdr->layout_off & (sizeof(u32) - 1)) {
+               btf_verifier_log(env, "Unaligned layout_off");
+               return -EINVAL;
+       }
+       start = btf->nohdr_data + hdr->layout_off;
+       end = start + hdr->layout_len;
+
+       if (hdr->layout_len < sizeof(struct btf_layout)) {
+               btf_verifier_log(env, "Layout section is too small");
+               return -EINVAL;
+       }
+       if (hdr->layout_len % sizeof(struct btf_layout) != 0) {
+               btf_verifier_log(env, "layout_len is not multiple of %zu",
+                                sizeof(struct btf_layout));
+               return -EINVAL;
+       }
+       if (end > btf->data + btf->data_size) {
+               btf_verifier_log(env, "Layout section is too big");
+               return -EINVAL;
+       }
+       btf->layout = start;
+
+       return 0;
+}
+
 static const size_t btf_sec_info_offset[] = {
        offsetof(struct btf_header, type_off),
        offsetof(struct btf_header, str_off),
+       offsetof(struct btf_header, layout_off)
 };
 
 static int btf_sec_info_cmp(const void *a, const void *b)
@@ -5565,24 +5609,28 @@ static int btf_check_sec_info(struct btf_verifier_env *env,
 {
        struct btf_sec_info secs[ARRAY_SIZE(btf_sec_info_offset)];
        u32 total, expected_total, i;
+       u32 nr_secs = ARRAY_SIZE(btf_sec_info_offset);
        const struct btf_header *hdr;
        const struct btf *btf;
 
        btf = env->btf;
        hdr = &btf->hdr;
 
+       if (hdr->hdr_len < sizeof(struct btf_header) || hdr->layout_len == 0)
+               nr_secs--;
+
        /* Populate the secs from hdr */
-       for (i = 0; i < ARRAY_SIZE(btf_sec_info_offset); i++)
+       for (i = 0; i < nr_secs; i++)
                secs[i] = *(struct btf_sec_info *)((void *)hdr +
                                                   btf_sec_info_offset[i]);
 
-       sort(secs, ARRAY_SIZE(btf_sec_info_offset),
+       sort(secs, nr_secs,
             sizeof(struct btf_sec_info), btf_sec_info_cmp, NULL);
 
        /* Check for gaps and overlap among sections */
        total = 0;
        expected_total = btf_data_size - hdr->hdr_len;
-       for (i = 0; i < ARRAY_SIZE(btf_sec_info_offset); i++) {
+       for (i = 0; i < nr_secs; i++) {
                if (expected_total < secs[i].off) {
                        btf_verifier_log(env, "Invalid section offset");
                        return -EINVAL;
@@ -5938,6 +5986,10 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uat
        if (err)
                goto errout;
 
+       err = btf_parse_layout_sec(env);
+       if (err)
+               goto errout;
+
        err = btf_parse_type_sec(env);
        if (err)
                goto errout;