bpf: limit the number of levels of a nested struct type.
authorKui-Feng Lee <thinker.li@gmail.com>
Thu, 23 May 2024 17:41:59 +0000 (10:41 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 4 Jun 2024 03:52:42 +0000 (20:52 -0700)
Limit the number of levels looking into struct types to avoid running out
of stack space.

Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com>
Link: https://lore.kernel.org/r/20240523174202.461236-7-thinker.li@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/btf.c

index 5e2b231..7928d92 100644 (file)
@@ -3536,7 +3536,8 @@ static int btf_repeat_fields(struct btf_field_info *info,
 
 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);
+                                struct btf_field_info *info, int info_cnt,
+                                u32 level);
 
 /* Find special fields in the struct type of a field.
  *
@@ -3547,11 +3548,15 @@ 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)
+                                 int info_cnt, u32 level)
 {
        int ret, err, i;
 
-       ret = btf_find_struct_field(btf, t, field_mask, info, info_cnt);
+       level++;
+       if (level >= MAX_RESOLVE_DEPTH)
+               return -E2BIG;
+
+       ret = btf_find_struct_field(btf, t, field_mask, info, info_cnt, level);
 
        if (ret <= 0)
                return ret;
@@ -3579,7 +3584,8 @@ static int btf_find_field_one(const struct btf *btf,
                              int var_idx,
                              u32 off, u32 expected_size,
                              u32 field_mask, u32 *seen_mask,
-                             struct btf_field_info *info, int info_cnt)
+                             struct btf_field_info *info, int info_cnt,
+                             u32 level)
 {
        int ret, align, sz, field_type;
        struct btf_field_info tmp;
@@ -3607,7 +3613,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);
+                                            &info[0], info_cnt, level);
                return ret;
        }
 
@@ -3668,7 +3674,8 @@ 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)
+                                struct btf_field_info *info, int info_cnt,
+                                u32 level)
 {
        int ret, idx = 0;
        const struct btf_member *member;
@@ -3687,7 +3694,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,
-                                        &info[idx], info_cnt - idx);
+                                        &info[idx], info_cnt - idx, level);
                if (ret < 0)
                        return ret;
                idx += ret;
@@ -3697,7 +3704,7 @@ 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)
+                               int info_cnt, u32 level)
 {
        int ret, idx = 0;
        const struct btf_var_secinfo *vsi;
@@ -3710,7 +3717,8 @@ 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,
-                                        &info[idx], info_cnt - idx);
+                                        &info[idx], info_cnt - idx,
+                                        level);
                if (ret < 0)
                        return ret;
                idx += ret;
@@ -3723,9 +3731,9 @@ static int btf_find_field(const struct btf *btf, const struct btf_type *t,
                          int info_cnt)
 {
        if (__btf_type_is_struct(t))
-               return btf_find_struct_field(btf, t, field_mask, info, info_cnt);
+               return btf_find_struct_field(btf, t, field_mask, info, info_cnt, 0);
        else if (btf_type_is_datasec(t))
-               return btf_find_datasec_var(btf, t, field_mask, info, info_cnt);
+               return btf_find_datasec_var(btf, t, field_mask, info, info_cnt, 0);
        return -EINVAL;
 }