| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/26 16:57 | flow | patch-triage |
3mArgs:null Results: {
"EnableConfigs": [
"BPF_SYSCALL"
],
"FocusSymbols": [
"btf_loc_param_check_meta",
"btf_loc_proto_check_meta",
"btf_loc_proto_resolve",
"btf_locsec_check_meta",
"btf_locsec_resolve",
"bpf_btf_load"
],
"KMSANReasoning": "The patch series introduces support for three new BTF kinds (BTF_KIND_LOC_PARAM, BTF_KIND_LOC_PROTO, and BTF_KIND_LOCSEC) representing parameter and site location debug information.\n\nThe changes in the kernel proper are confined to BTF validation and resolution logic in `kernel/bpf/btf.c` and related header definitions:\n1. `btf_loc_param_check_meta()`, `btf_loc_proto_check_meta()`, and `btf_locsec_check_meta()` validate metadata size, flags, and names.\n2. `btf_loc_proto_resolve()` and `btf_locsec_resolve()` validate relationships and references between types.\n3. The rest of the patch updates userspace tooling (`bpftool`, `libbpf`, and selftests).\n\nAny potential bugs in the verifier parsing logic (e.g., buffer indexing or malformed metadata length calculations) would manifest as out-of-bounds reads or invalid pointer dereferences, which are detected by KASAN. The patch does not allocate or copy uninitialized kernel memory, does not modify kernel-to-user copy paths (no info-leaks), and does not introduce conditional branches on uninitialized variables. Therefore, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false,
"Reasoning": "The patch introduces three new BTF kinds (BTF_KIND_LOC_PARAM, BTF_KIND_LOC_PROTO, and BTF_KIND_LOCSEC) along with their metadata validation and resolution logic in kernel/bpf/btf.c. This code executes in the core BPF subsystem and is directly reachable from userspace via the BPF_BTF_LOAD command of the bpf() system call. Fuzzing is warranted to test bounds checking, type resolution, validation loops, and error handling for malformed BTF payloads.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/26 16:57 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 006d1ad744f5dd16dd22415c5cef649e66ef7f74\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sat Sep 26 16:57:26 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst\nindex 004aa1058d857..29de1222c3e73 100644\n--- a/Documentation/bpf/btf.rst\n+++ b/Documentation/bpf/btf.rst\n@@ -88,6 +88,9 @@ sequentially and type id is assigned to each recognized type starting from id\n #define BTF_KIND_DECL_TAG 17 /* Decl Tag */\n #define BTF_KIND_TYPE_TAG 18 /* Type Tag */\n #define BTF_KIND_ENUM64 19 /* Enumeration up to 64-bit values */\n+ #define BTF_KIND_LOC_PARAM 20 /* Location description (register, const etc) */\n+ #define BTF_KIND_LOC_PROTO 21 /* Set of location parameters for site */\n+ #define BTF_KIND_LOCSEC 22 /* Section with site descriptions */\n \n Note that the type section encodes debug info, not just pure types.\n ``BTF_KIND_FUNC`` is not a type, and it represents a defined subprogram.\n@@ -104,11 +107,13 @@ Each type contains the following common data::\n * decl_tag and type_tag\n */\n __u32 info;\n- /* \"size\" is used by INT, ENUM, STRUCT, UNION and ENUM64.\n+ /* \"size\" is used by INT, ENUM, STRUCT, UNION, ENUM64 and\n+ * LOC_PARAM.\n * \"size\" tells the size of the type it is describing.\n *\n * \"type\" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,\n- * FUNC, FUNC_PROTO, DECL_TAG and TYPE_TAG.\n+ * FUNC, FUNC_PROTO, DECL_TAG and TYPE_TAG. It is unused by\n+ * LOC_PROTO and LOCSEC.\n * \"type\" is a type_id referring to another type.\n */\n union {\n@@ -563,6 +568,80 @@ The ``btf_enum64`` encoding:\n If the original enum value is signed and the size is less than 8,\n that value will be sign extended into 8 bytes.\n \n+2.2.20 BTF_KIND_LOC_PARAM\n+~~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+``struct btf_type`` encoding requirement:\n+ * ``name_off``: 0\n+ * ``info.kind_flag``: 0\n+ * ``info.kind``: BTF_KIND_LOC_PARAM\n+ * ``info.vlen``: number of 32-bit location value words\n+ * ``size``: size in bytes of the represented parameter: 1 through 16\n+\n+``btf_type`` is followed by a ``struct btf_loc_param`` and ``info.vlen``\n+number of 32-bit value words.::\n+\n+ struct btf_loc_param {\n+ __u32 flags;\n+ __u32 values[];\n+ };\n+\n+The ``flags`` field describes how to interpret ``values``:\n+\n+ * ``BTF_LOC_PARAM_CONST`` describes a constant; the value is stored in\n+ low-word, high-word order when it requires 64 bits.\n+ * ``BTF_LOC_PARAM_ADDR | BTF_LOC_PARAM_CONST`` describes an address offset\n+ relative to the runtime base address of the kernel or module image.\n+ * ``BTF_LOC_PARAM_REG`` with one word describes a register number; with two\n+ words it describes a multi-register parameter.\n+ * ``BTF_LOC_PARAM_REG | BTF_LOC_PARAM_OFFSET`` describes an address held in\n+ a register plus an offset. Adding ``BTF_LOC_PARAM_DEREF`` dereferences\n+ that address. ``BTF_LOC_PARAM_REG | BTF_LOC_PARAM_DEREF`` with one word\n+ dereferences the value held in the register.\n+ * ``BTF_LOC_PARAM_SIGNED`` makes a constant or offset signed. A constant's\n+ signed width is ``size``. For a register-relative offset, its signed\n+ width is the number of offset value words times 32 bits.\n+\n+2.2.21 BTF_KIND_LOC_PROTO\n+~~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+``struct btf_type`` encoding requirement:\n+ * ``name_off``: 0\n+ * ``info.kind_flag``: 0\n+ * ``info.kind``: BTF_KIND_LOC_PROTO\n+ * ``info.vlen``: number of function parameter locations\n+ * ``type``: 0\n+\n+``btf_type`` is followed by ``info.vlen`` number of ``__u32`` BTF type IDs.\n+Each entry corresponds to a function parameter at an inline site. An entry is\n+either 0, meaning that no location information is available, or the type ID\n+of a ``BTF_KIND_LOC_PARAM``.\n+\n+2.2.22 BTF_KIND_LOCSEC\n+~~~~~~~~~~~~~~~~~~~~~~\n+\n+``struct btf_type`` encoding requirement:\n+ * ``name_off``: offset to a valid ELF section name\n+ * ``info.kind_flag``: 0\n+ * ``info.kind``: BTF_KIND_LOCSEC\n+ * ``info.vlen``: number of inline sites in the section\n+ * ``type``: 0\n+\n+``btf_type`` is followed by ``info.vlen`` number of ``struct btf_loc``.::\n+\n+ struct btf_loc {\n+ __u32 func;\n+ __u32 loc_proto;\n+ __u32 offset;\n+ };\n+\n+The ``func`` field is the non-zero type ID of the ``BTF_KIND_FUNC`` being\n+described. ``loc_proto`` is the non-zero type ID of the associated\n+``BTF_KIND_LOC_PROTO``. ``offset`` is the location address offset relative to\n+the runtime base address of the ELF section associated with the LOCSEC.\n+For example, a LOCSEC named ``inline.text`` contains records for ``.text``\n+whose offsets are relative to the runtime base of that section.\n+\n 2.3 Constant Values\n -------------------\n \ndiff --git a/include/linux/btf.h b/include/linux/btf.h\nindex ddd0f4f32d24b..f29ed358b7a8c 100644\n--- a/include/linux/btf.h\n+++ b/include/linux/btf.h\n@@ -261,6 +261,11 @@ const char *btf_type_str(const struct btf_type *t);\n \t i \u003c btf_type_vlen(datasec_type);\t\t\t\\\n \t i++, member++)\n \n+#define for_each_loc(i, locsec_type, member)\t\t\t\\\n+\tfor (i = 0, member = btf_type_loc_secinfo(locsec_type);\t\\\n+\t i \u003c btf_type_vlen(locsec_type);\t\t\t\\\n+\t i++, member++)\n+\n static inline bool btf_type_is_ptr(const struct btf_type *t)\n {\n \treturn BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_PTR;\n@@ -329,6 +334,21 @@ static inline u64 btf_enum64_value(const struct btf_enum64 *e)\n \treturn ((u64)e-\u003eval_hi32 \u003c\u003c 32) | e-\u003eval_lo32;\n }\n \n+static inline struct btf_loc_param *btf_loc_param(const struct btf_type *t)\n+{\n+\treturn (struct btf_loc_param *)(t + 1);\n+}\n+\n+static inline __u32 *btf_loc_proto_params(const struct btf_type *t)\n+{\n+\treturn (__u32 *)(t + 1);\n+}\n+\n+static inline struct btf_loc *btf_type_loc_secinfo(const struct btf_type *t)\n+{\n+\treturn (struct btf_loc *)(t + 1);\n+}\n+\n static inline bool btf_is_composite(const struct btf_type *t)\n {\n \tu16 kind = btf_kind(t);\n@@ -559,7 +579,7 @@ struct btf_field_desc {\n \t/* member struct size, or zero, if no members */\n \tint m_sz;\n \t/* repeated per-member offsets */\n-\tint m_off_cnt, m_offs[1];\n+\tint m_off_cnt, m_offs[2];\n };\n \n struct btf_field_iter {\ndiff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h\nindex 618167cab4e61..11d65871d163e 100644\n--- a/include/uapi/linux/btf.h\n+++ b/include/uapi/linux/btf.h\n@@ -54,8 +54,8 @@ struct btf_type {\n \t * decl_tag and type_tag\n \t */\n \t__u32 info;\n-\t/* \"size\" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.\n-\t * \"size\" tells the size of the type it is describing.\n+\t/* \"size\" is used by INT, ENUM, STRUCT, UNION, DATASEC, ENUM64\n+\t * and LOC_PARAM. \"size\" tells the size of the type it is describing.\n \t *\n \t * \"type\" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,\n \t * FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG.\n@@ -92,7 +92,9 @@ enum {\n \tBTF_KIND_DECL_TAG\t= 17,\t/* Decl Tag */\n \tBTF_KIND_TYPE_TAG\t= 18,\t/* Type Tag */\n \tBTF_KIND_ENUM64\t\t= 19,\t/* Enumeration up to 64-bit values */\n-\n+\tBTF_KIND_LOC_PARAM\t= 20,\t/* Location parameter information */\n+\tBTF_KIND_LOC_PROTO\t= 21,\t/* Location prototype for site */\n+\tBTF_KIND_LOCSEC\t\t= 22,\t/* Location section */\n \tNR_BTF_KINDS,\n \tBTF_KIND_MAX\t\t= NR_BTF_KINDS - 1,\n };\n@@ -212,4 +214,79 @@ struct btf_enum64 {\n \t__u32\tval_hi32;\n };\n \n+/*\n+ * BTF_KIND_LOC_PARAM is followed by a single \"struct btf_loc_param\"\n+ * that contains flags specifying the contents of the vlen-specified\n+ * number of 4-byte values that follow.\n+ */\n+struct btf_loc_param {\n+\t__u32 flags;\n+\t__u32 values[];\n+};\n+\n+/*\n+ * The combination of size, vlen and flags gives us the means to interpret\n+ * the following vlen-specified set of 4-byte values:\n+ *\n+ * - a BTF_LOC_PARAM_CONST is a constant value; combination\n+ * of size, vlen and _SIGNED flag determines it. If the value requires\n+ * 64 bits it is stored in {lo,hi} order.\n+ * - a BTF_LOC_PARAM_ADDR|BTF_LOC_PARAM_CONST is an address that should be\n+ * normalized with respect to kernel/module base address.\n+ * - a BTF_LOC_PARAM_REG with vlen 1 is a simple register number;\n+ * with vlen 2 it is a multi-register parameter. Register numbers are\n+ * numbers derived from DW_OP_reg values, i.e. 0 is DW_OP_reg0; since\n+ * DW_OP_fbreg has its own special DW_OP_value and DW_OP_regx can refer\n+ * to an arbitrary register number, we reserve BTF_LOC_PARAM_FBREG for\n+ * the frame base register to avoid collisions.\n+ * - a _REG | OFFSET describes an address without dereferencing it.\n+ * - a _REG | DEREF with vlen 1 dereferences the value in the register\n+ * number specified.\n+ * - a REG | DEREF | OFFSET with vlen \u003e 1 specifies the register number\n+ * in the first 4-byte value and the offset in the remainder.\n+ * In the case of REG and OFFSET combinations, the OFFSET has the width\n+ * of the value words while the type size describes the represented parameter,\n+ * so for example a REG | DEREF | OFFSET with size 8 and vlen 2 would be\n+ * an 8-byte register dereference with signed 4-byte offset, since the vlen 2\n+ * values consist of a register value and the signed value.\n+ */\n+enum btf_loc_param_flags {\n+\tBTF_LOC_PARAM_SIGNED\t\t=\t0x1,\n+\tBTF_LOC_PARAM_CONST\t\t=\t0x2,\n+\tBTF_LOC_PARAM_ADDR\t\t=\t0x4,\n+\tBTF_LOC_PARAM_REG\t\t=\t0x8,\n+\tBTF_LOC_PARAM_DEREF\t\t=\t0x10,\n+\tBTF_LOC_PARAM_OFFSET\t\t=\t0x20,\n+};\n+\n+enum {\n+\tBTF_LOC_PARAM_FBREG\t\t=\t0xffffffff\n+};\n+\n+/*\n+ * BTF_KIND_LOC_PROTO specifies location prototypes; i.e. how locations relate\n+ * to parameters; a struct btf_type of BTF_KIND_LOC_PROTO is followed by a\n+ * vlen-specified number of __u32 BTF type ids which specify the associated\n+ * BTF_KIND_LOC_PARAM for each function parameter associated with the\n+ * location. The type should either be 0 (no location info) or point at\n+ * a BTF_KIND_LOC_PARAM.\n+ */\n+\n+/*\n+ * BTF_KIND_LOCSEC consists of vlen-specified number of \"struct btf_loc\"\n+ * containing location site-specific information for a specific ELF section;\n+ * for example locations in \".text\" are in a LOCSEC named \"inline.text\".\n+ *\n+ * - function (func)\n+ * - location prototype type id (loc_proto)\n+ * - address offset (offset) relative to the runtime base address of the\n+ * ELF section associated with the LOCSEC\n+ */\n+\n+struct btf_loc {\n+\t__u32 func;\n+\t__u32 loc_proto;\n+\t__u32 offset;\n+};\n+\n #endif /* _UAPI__LINUX_BTF_H__ */\ndiff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c\nindex 314ecb0e593b0..2ef28c6f14f93 100644\n--- a/kernel/bpf/btf.c\n+++ b/kernel/bpf/btf.c\n@@ -346,6 +346,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {\n \t[BTF_KIND_DECL_TAG]\t= \"DECL_TAG\",\n \t[BTF_KIND_TYPE_TAG]\t= \"TYPE_TAG\",\n \t[BTF_KIND_ENUM64]\t= \"ENUM64\",\n+\t[BTF_KIND_LOC_PARAM]\t= \"LOC_PARAM\",\n+\t[BTF_KIND_LOC_PROTO]\t= \"LOC_PROTO\",\n+\t[BTF_KIND_LOCSEC]\t= \"LOCSEC\",\n };\n \n const char *btf_type_str(const struct btf_type *t)\n@@ -518,11 +521,27 @@ static bool btf_type_is_decl_tag(const struct btf_type *t)\n \treturn BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_DECL_TAG;\n }\n \n+static bool btf_type_is_loc_param(const struct btf_type *t)\n+{\n+\treturn BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_LOC_PARAM;\n+}\n+\n+static bool btf_type_is_loc_proto(const struct btf_type *t)\n+{\n+\treturn BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_LOC_PROTO;\n+}\n+\n+static bool btf_type_is_locsec(const struct btf_type *t)\n+{\n+\treturn BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_LOCSEC;\n+}\n+\n static bool btf_type_nosize(const struct btf_type *t)\n {\n \treturn btf_type_is_void(t) || btf_type_is_fwd(t) ||\n \t btf_type_is_func(t) || btf_type_is_func_proto(t) ||\n-\t btf_type_is_decl_tag(t);\n+\t btf_type_is_decl_tag(t) || btf_type_is_loc_param(t) ||\n+\t btf_type_is_loc_proto(t) || btf_type_is_locsec(t);\n }\n \n static bool btf_type_nosize_or_null(const struct btf_type *t)\n@@ -769,7 +788,9 @@ static bool btf_type_is_resolve_source_only(const struct btf_type *t)\n {\n \treturn btf_type_is_var(t) ||\n \t btf_type_is_decl_tag(t) ||\n-\t btf_type_is_datasec(t);\n+\t btf_type_is_datasec(t) ||\n+\t btf_type_is_loc_proto(t) ||\n+\t btf_type_is_locsec(t);\n }\n \n /* What types need to be resolved?\n@@ -797,7 +818,9 @@ static bool btf_type_needs_resolve(const struct btf_type *t)\n \t btf_type_is_var(t) ||\n \t btf_type_is_func(t) ||\n \t btf_type_is_decl_tag(t) ||\n-\t btf_type_is_datasec(t);\n+\t btf_type_is_datasec(t) ||\n+\t btf_type_is_loc_proto(t) ||\n+\t btf_type_is_locsec(t);\n }\n \n /* t-\u003esize can be used */\n@@ -4730,6 +4753,293 @@ static const struct btf_kind_operations enum64_ops = {\n \t.show = btf_enum64_show,\n };\n \n+static s32 btf_loc_param_check_meta(struct btf_verifier_env *env,\n+\t\t\t\t const struct btf_type *t,\n+\t\t\t\t u32 meta_left)\n+{\n+\tconst struct btf_loc_param *p = btf_loc_param(t);\n+\tu32 size, meta_needed, vlen = btf_vlen(t);\n+\n+\tmeta_needed = sizeof(*p) + sizeof(__u32) * vlen;\n+\tif (meta_left \u003c meta_needed) {\n+\t\tbtf_verifier_log_basic(env, t,\n+\t\t\t\t \"meta_left:%u meta_needed:%u\",\n+\t\t\t\t meta_left, meta_needed);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (t-\u003ename_off) {\n+\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\n+\t\treturn -EINVAL;\n+\t}\n+\tsize = t-\u003esize;\n+\tif (!size || size \u003e 16) {\n+\t\tbtf_verifier_log_type(env, t, \"Unexpected size\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (btf_type_kflag(t)) {\n+\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tbtf_verifier_log_type(env, t, NULL);\n+\n+\treturn meta_needed;\n+}\n+\n+static void btf_loc_param_log(struct btf_verifier_env *env,\n+\t\t\t const struct btf_type *t)\n+{\n+\tconst struct btf_loc_param *p = btf_loc_param(t);\n+\tu32 i, vlen = btf_vlen(t);\n+\n+\tbtf_verifier_log(env, \"size=%u vlen=%u flags=0x%x\", t-\u003esize, vlen, p-\u003eflags);\n+\tfor (i = 0; i \u003c vlen; i++)\n+\t\tbtf_verifier_log(env, \", %u\", p-\u003evalues[i]);\n+}\n+\n+static const struct btf_kind_operations loc_param_ops = {\n+\t.check_meta = btf_loc_param_check_meta,\n+\t.resolve = btf_df_resolve,\n+\t.check_member = btf_df_check_member,\n+\t.check_kflag_member = btf_df_check_kflag_member,\n+\t.log_details = btf_loc_param_log,\n+\t.show = btf_df_show,\n+};\n+\n+static s32 btf_loc_proto_check_meta(struct btf_verifier_env *env,\n+\t\t\t\t const struct btf_type *t,\n+\t\t\t\t u32 meta_left)\n+{\n+\tu32 meta_needed;\n+\n+\tmeta_needed = sizeof(__u32) * btf_type_vlen(t);\n+\n+\tif (meta_left \u003c meta_needed) {\n+\t\tbtf_verifier_log_basic(env, t,\n+\t\t\t\t \"meta_left:%u meta_needed:%u\",\n+\t\t\t\t meta_left, meta_needed);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (t-\u003ename_off) {\n+\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (btf_type_kflag(t)) {\n+\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tbtf_verifier_log_type(env, t, NULL);\n+\n+\treturn meta_needed;\n+}\n+\n+static void btf_loc_proto_log(struct btf_verifier_env *env,\n+\t\t\t const struct btf_type *t)\n+{\n+\tconst __u32 *params = btf_loc_proto_params(t);\n+\tu32 i, nr_params = btf_type_vlen(t);\n+\n+\tbtf_verifier_log(env, \"vlen=%u\", nr_params);\n+\tfor (i = 0; i \u003c nr_params; i++, params++)\n+\t\tbtf_verifier_log(env, \", %u\", *params);\n+}\n+\n+static int btf_loc_proto_resolve(struct btf_verifier_env *env,\n+\t\t\t\t const struct resolve_vertex *v)\n+{\n+\tconst struct btf_type *t = v-\u003et;\n+\tconst __u32 *params = btf_loc_proto_params(t);\n+\tu32 i, nr_params = btf_type_vlen(t);\n+\tstruct btf *btf = env-\u003ebtf;\n+\n+\tif (t-\u003etype) {\n+\t\tbtf_verifier_log_type(env, t, \"Invalid loc_proto type\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tfor (i = 0; i \u003c nr_params; i++) {\n+\t\tconst struct btf_type *param_type;\n+\t\tu32 param_type_id = params[i];\n+\n+\t\tif (!param_type_id)\n+\t\t\tcontinue;\n+\n+\t\tparam_type = btf_type_by_id(btf, param_type_id);\n+\t\tif (!param_type || !btf_type_is_loc_param(param_type)) {\n+\t\t\tbtf_verifier_log_type(env, t,\n+\t\t\t\t\t \"Invalid loc_param#%u\", i + 1);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t}\n+\n+\tenv_stack_pop_resolved(env, 0, 0);\n+\treturn 0;\n+}\n+\n+static const struct btf_kind_operations loc_proto_ops = {\n+\t.check_meta = btf_loc_proto_check_meta,\n+\t.resolve = btf_loc_proto_resolve,\n+\t.check_member = btf_df_check_member,\n+\t.check_kflag_member = btf_df_check_kflag_member,\n+\t.log_details = btf_loc_proto_log,\n+\t.show = btf_df_show,\n+};\n+\n+__printf(4, 5)\n+static void btf_verifier_log_loc(struct btf_verifier_env *env,\n+\t\t\t\t const struct btf_type *locsec_type,\n+\t\t\t\t const struct btf_loc *loc,\n+\t\t\t\t const char *fmt, ...)\n+{\n+\tstruct bpf_verifier_log *log = \u0026env-\u003elog;\n+\tva_list args;\n+\n+\tif (!bpf_verifier_log_needed(log))\n+\t\treturn;\n+\tif (log-\u003elevel == BPF_LOG_KERNEL \u0026\u0026 !fmt)\n+\t\treturn;\n+\tif (env-\u003ephase != CHECK_META)\n+\t\tbtf_verifier_log_type(env, locsec_type, NULL);\n+\n+\t__btf_verifier_log(log, \"\\t func=%u loc_proto=%u offset=%u\",\n+\t\t\t loc-\u003efunc, loc-\u003eloc_proto, loc-\u003eoffset);\n+\tif (fmt \u0026\u0026 *fmt) {\n+\t\t__btf_verifier_log(log, \" \");\n+\t\tva_start(args, fmt);\n+\t\tbpf_verifier_vlog(log, fmt, args);\n+\t\tva_end(args);\n+\t}\n+\n+\t__btf_verifier_log(log, \"\\n\");\n+}\n+\n+static s32 btf_locsec_check_meta(struct btf_verifier_env *env,\n+\t\t\t\t const struct btf_type *t,\n+\t\t\t\t u32 meta_left)\n+{\n+\tu32 i, meta_needed, vlen = btf_type_vlen(t);\n+\tconst struct btf_loc *loc;\n+\n+\tmeta_needed = sizeof(struct btf_loc) * vlen;\n+\n+\tif (meta_left \u003c meta_needed) {\n+\t\tbtf_verifier_log_basic(env, t,\n+\t\t\t\t \"meta_left:%u meta_needed:%u\",\n+\t\t\t\t meta_left, meta_needed);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (btf_type_kflag(t)) {\n+\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (!t-\u003ename_off ||\n+\t !btf_name_valid_section(env-\u003ebtf, t-\u003ename_off)) {\n+\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tfor_each_loc(i, t, loc) {\n+\t\t/* A loc func, loc proto cannot be in type void */\n+\t\tif (!loc-\u003efunc || !BTF_TYPE_ID_VALID(loc-\u003efunc)) {\n+\t\t\tbtf_verifier_log_loc(env, t, loc, \"Invalid func\");\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\tif (!loc-\u003eloc_proto || !BTF_TYPE_ID_VALID(loc-\u003eloc_proto)) {\n+\t\t\tbtf_verifier_log_loc(env, t, loc, \"Invalid loc_proto\");\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\tbtf_verifier_log_loc(env, t, loc, NULL);\n+\t}\n+\n+\treturn meta_needed;\n+}\n+\n+static void btf_locsec_log(struct btf_verifier_env *env,\n+\t\t\t const struct btf_type *t)\n+{\n+\tbtf_verifier_log(env, \"vlen=%u\", btf_type_vlen(t));\n+}\n+\n+static int btf_locsec_resolve(struct btf_verifier_env *env,\n+\t\t\t const struct resolve_vertex *v)\n+{\n+\tconst struct btf_type *t = v-\u003et;\n+\tconst struct btf_loc *loc;\n+\tstruct btf *btf = env-\u003ebtf;\n+\tu32 i;\n+\n+\tif (t-\u003etype) {\n+\t\tbtf_verifier_log_type(env, t, \"Invalid locsec type\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tenv-\u003eresolve_mode = RESOLVE_TBD;\n+\tfor (i = v-\u003enext_member, loc = btf_type_loc_secinfo(t) + i;\n+\t i \u003c btf_type_vlen(t); i++, loc++) {\n+\t\tconst struct btf_type *func_type, *func_proto_type;\n+\t\tconst struct btf_type *loc_proto_type;\n+\t\tu32 func_type_id = loc-\u003efunc;\n+\t\tu32 loc_proto_type_id = loc-\u003eloc_proto;\n+\t\tu32 proto_vlen;\n+\n+\t\tfunc_type = btf_type_by_id(btf, func_type_id);\n+\t\tif (!func_type || !btf_type_is_func(func_type)) {\n+\t\t\tbtf_verifier_log_type(env, t,\n+\t\t\t\t\t \"Invalid func#%u\", i + 1);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\tfunc_proto_type = btf_type_by_id(btf, func_type-\u003etype);\n+\t\tif (!func_proto_type || !btf_type_is_func_proto(func_proto_type)) {\n+\t\t\tbtf_verifier_log_type(env, t,\n+\t\t\t\t\t \"Invalid func#%u\", i + 1);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\tproto_vlen = btf_vlen(func_proto_type);\n+\n+\t\tif (!env_type_is_resolved(env, func_type_id)) {\n+\t\t\tenv_stack_set_next_member(env, i);\n+\t\t\treturn env_stack_push(env, func_type, func_type_id);\n+\t\t}\n+\n+\t\tloc_proto_type = btf_type_by_id(btf, loc_proto_type_id);\n+\t\tif (!loc_proto_type || !btf_type_is_loc_proto(loc_proto_type)) {\n+\t\t\tbtf_verifier_log_type(env, t,\n+\t\t\t\t\t \"Invalid loc_proto#%u\", i + 1);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\tif (proto_vlen != btf_vlen(loc_proto_type)) {\n+\t\t\tbtf_verifier_log_type(env, t,\n+\t\t\t\t\t \"Mismatched vlen for loc_proto#%u\", i + 1);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\n+\t\tif (!env_type_is_resolved(env, loc_proto_type_id)) {\n+\t\t\tenv_stack_set_next_member(env, i + 1);\n+\t\t\treturn env_stack_push(env, loc_proto_type,\n+\t\t\t\t\t loc_proto_type_id);\n+\t\t}\n+\t}\n+\n+\tenv_stack_pop_resolved(env, 0, 0);\n+\treturn 0;\n+}\n+\n+static const struct btf_kind_operations locsec_ops = {\n+\t.check_meta = btf_locsec_check_meta,\n+\t.resolve = btf_locsec_resolve,\n+\t.check_member = btf_df_check_member,\n+\t.check_kflag_member = btf_df_check_kflag_member,\n+\t.log_details = btf_locsec_log,\n+\t.show = btf_df_show,\n+};\n+\n static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,\n \t\t\t\t const struct btf_type *t,\n \t\t\t\t u32 meta_left)\n@@ -5399,6 +5709,9 @@ static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {\n \t[BTF_KIND_DECL_TAG] = \u0026decl_tag_ops,\n \t[BTF_KIND_TYPE_TAG] = \u0026modifier_ops,\n \t[BTF_KIND_ENUM64] = \u0026enum64_ops,\n+\t[BTF_KIND_LOC_PARAM] = \u0026loc_param_ops,\n+\t[BTF_KIND_LOC_PROTO] = \u0026loc_proto_ops,\n+\t[BTF_KIND_LOCSEC] = \u0026locsec_ops,\n };\n \n static s32 btf_check_meta(struct btf_verifier_env *env,\n@@ -5473,7 +5786,8 @@ static bool btf_resolve_valid(struct btf_verifier_env *env,\n \tif (!env_type_is_resolved(env, type_id))\n \t\treturn false;\n \n-\tif (btf_type_is_struct(t) || btf_type_is_datasec(t))\n+\tif (btf_type_is_struct(t) || btf_type_is_datasec(t) ||\n+\t btf_type_is_loc_proto(t) || btf_type_is_locsec(t))\n \t\treturn !btf_resolved_type_id(btf, type_id) \u0026\u0026\n \t\t !btf_resolved_type_size(btf, type_id);\n \ndiff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst\nindex cf75a7fa2d6bc..c6dc445cd4a19 100644\n--- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst\n+++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst\n@@ -16,7 +16,7 @@ SYNOPSIS\n \n **bpftool** [*OPTIONS*] **btf** *COMMAND*\n \n-*OPTIONS* := { |COMMON_OPTIONS| | { **-B** | **--base-btf** } }\n+*OPTIONS* := { |COMMON_OPTIONS| | [{ **-B** | **--base-btf**} *FILE*]... }\n \n *COMMANDS* := { **dump** | **help** }\n \n@@ -87,7 +87,10 @@ OPTIONS\n objects for kernel modules. To avoid duplicating all kernel symbols\n required by modules, BTF objects for modules are \"split\", they are\n built incrementally on top of the kernel (vmlinux) BTF object. So the\n- base BTF reference should usually point to the kernel BTF.\n+ base BTF reference should usually point to the kernel BTF. Multiple\n+ base BTF objects can be passed, where the first is assumed to be the\n+ root BTF, followed by split BTF based upon it, followed by split\n+ BTF based upon the first split BTF and so on.\n \n When the main BTF object to process (for example, the module BTF to\n dump) is passed as a *FILE*, bpftool attempts to autodetect the path\ndiff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c\nindex bca0a3982f09b..e29c8a84e224c 100644\n--- a/tools/bpf/bpftool/btf.c\n+++ b/tools/bpf/bpftool/btf.c\n@@ -29,6 +29,7 @@\n \n #define MAX_ROOT_IDS\t\t16\n #define MAX_BTF_FILES\t\t64\n+#define MAX_LOC_PARAM_WORDS\t8\n \n static const char * const btf_kind_str[NR_BTF_KINDS] = {\n \t[BTF_KIND_UNKN]\t\t= \"UNKNOWN\",\n@@ -51,6 +52,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {\n \t[BTF_KIND_DECL_TAG]\t= \"DECL_TAG\",\n \t[BTF_KIND_TYPE_TAG]\t= \"TYPE_TAG\",\n \t[BTF_KIND_ENUM64]\t= \"ENUM64\",\n+\t[BTF_KIND_LOC_PARAM]\t= \"LOC_PARAM\",\n+\t[BTF_KIND_LOC_PROTO]\t= \"LOC_PROTO\",\n+\t[BTF_KIND_LOCSEC]\t= \"LOCSEC\",\n };\n \n struct sort_datum {\n@@ -117,6 +121,137 @@ static int btf_kind_safe(int kind)\n \treturn kind \u003c= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;\n }\n \n+static void btf_loc_param_reg_str(__u32 reg, char *str, size_t sz)\n+{\n+\tif (reg == BTF_LOC_PARAM_FBREG)\n+\t\tsnprintf(str, sz, \"fbreg\");\n+\telse\n+\t\tsnprintf(str, sz, \"reg%u\", reg);\n+}\n+\n+static void btf_loc_param_raw_str(const struct btf_loc_param *p, __u32 vlen,\n+\t\t\t\t char *str, size_t sz)\n+{\n+\t__u32 i, nr_words = min(vlen, (__u32)MAX_LOC_PARAM_WORDS);\n+\tsize_t off = 0;\n+\n+\tif (!sz)\n+\t\treturn;\n+\n+\toff += snprintf(str + off, sz - off, \"raw=[\");\n+\tfor (i = 0; i \u003c nr_words \u0026\u0026 off \u003c sz; i++)\n+\t\toff += snprintf(str + off, sz - off, \"%s0x%08x\",\n+\t\t\t\ti ? \", \" : \"\", p-\u003evalues[i]);\n+\tif (vlen \u003e nr_words \u0026\u0026 off \u003c sz)\n+\t\toff += snprintf(str + off, sz - off, \", ...\");\n+\tif (off \u003c sz)\n+\t\tsnprintf(str + off, sz - off, \"]\");\n+}\n+\n+static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz)\n+{\n+\tconst struct btf_loc_param *p;\n+\t__u32 i = 0, vlen;\n+\t__u64 value;\n+\t__u32 value_size;\n+\tbool negative = false;\n+\tchar regs[32] = {};\n+\tchar num[32] = {};\n+\tconst char *op = \"\";\n+\n+\tif (!t || !btf_is_loc_param(t)) {\n+\t\tsnprintf(str, sz, \"\u003cinvalid\u003e\");\n+\t\treturn;\n+\t}\n+\n+\tp = btf_loc_param(t);\n+\tvlen = btf_vlen(t);\n+\n+\tif (p-\u003eflags \u0026 BTF_LOC_PARAM_REG) {\n+\t\t__u32 nregs = (p-\u003eflags == BTF_LOC_PARAM_REG) ? vlen : 1;\n+\n+\t\tif (nregs \u003e vlen) {\n+\t\t\tbtf_loc_param_raw_str(p, vlen, str, sz);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tswitch (nregs) {\n+\t\tcase 2:\n+\t\t\tbtf_loc_param_reg_str(p-\u003evalues[0], regs, sizeof(regs));\n+\t\t\tsnprintf(regs + strlen(regs), sizeof(regs) - strlen(regs),\n+\t\t\t\t \", \");\n+\t\t\tbtf_loc_param_reg_str(p-\u003evalues[1],\n+\t\t\t\t\t regs + strlen(regs),\n+\t\t\t\t\t sizeof(regs) - strlen(regs));\n+\t\t\tbreak;\n+\t\tcase 1:\n+\t\t\tbtf_loc_param_reg_str(p-\u003evalues[0], regs, sizeof(regs));\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tbtf_loc_param_raw_str(p, vlen, str, sz);\n+\t\t\treturn;\n+\t\t}\n+\t\ti += nregs;\n+\t}\n+\tif (p-\u003eflags \u0026 (BTF_LOC_PARAM_CONST|BTF_LOC_PARAM_OFFSET)) {\n+\t\tswitch (vlen - i) {\n+\t\tcase 1:\n+\t\t\tvalue_size = sizeof(p-\u003evalues[0]);\n+\t\t\tvalue = p-\u003evalues[i];\n+\t\t\tbreak;\n+\t\tcase 2:\n+\t\t\tvalue_size = 2 * sizeof(p-\u003evalues[0]);\n+\t\t\tvalue = ((__u64)p-\u003evalues[i + 1] \u003c\u003c 32) | p-\u003evalues[i];\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tbtf_loc_param_raw_str(p, vlen, str, sz);\n+\t\t\treturn;\n+\t\t}\n+\t\ti = vlen;\n+\t\tif (p-\u003eflags \u0026 BTF_LOC_PARAM_SIGNED) {\n+\t\t\t/*\n+\t\t\t * size describes the represented parameter, so it\n+\t\t\t * describes a constant's signed width. An offset\n+\t\t\t * is determined by its value words after the register\n+\t\t\t * number.\n+\t\t\t */\n+\t\t\t__u32 size = p-\u003eflags \u0026 BTF_LOC_PARAM_OFFSET ?\n+\t\t\t\t value_size : t-\u003esize;\n+\t\t\t__u32 bits = size * 8;\n+\n+\t\t\t/*\n+\t\t\t * Since we represent constant values in hex, we\n+\t\t\t * need to determine if the value is negative so\n+\t\t\t * we can prepend a \"-\", and also fix the value\n+\t\t\t * to be positive so we can have - 0x\u003cvalue\u003e.\n+\t\t\t */\n+\t\t\tif (size \u0026\u0026 size \u003c= sizeof(value)) {\n+\t\t\t\tif (size \u003c sizeof(value))\n+\t\t\t\t\tvalue \u0026= (1ULL \u003c\u003c bits) - 1;\n+\t\t\t\tnegative = value \u0026 (1ULL \u003c\u003c (bits - 1));\n+\t\t\t\tif (negative)\n+\t\t\t\t\tvalue = size == sizeof(value) ? -value :\n+\t\t\t\t\t\t(1ULL \u003c\u003c bits) - value;\n+\t\t\t}\n+\t\t}\n+\t\tsnprintf(num, sizeof(num), \"0x%llx%s\", (unsigned long long)value,\n+\t\t\t p-\u003eflags \u0026 BTF_LOC_PARAM_ADDR ? \" (addr)\" : \"\");\n+\t}\n+\tif (i != vlen) {\n+\t\tbtf_loc_param_raw_str(p, vlen, str, sz);\n+\t\treturn;\n+\t}\n+\tif (num[0])\n+\t\top = regs[0] ? (negative ? \" - \" : \" + \") : negative ? \"-\" : \"\";\n+\n+\tsnprintf(str, sz, \"%s%s%s%s%s\",\n+\t\t p-\u003eflags \u0026 BTF_LOC_PARAM_DEREF ? \"*(\" : \"\",\n+\t\t regs,\n+\t\t op,\n+\t\t num,\n+\t\t p-\u003eflags \u0026 BTF_LOC_PARAM_DEREF ? \")\" : \"\");\n+}\n+\n static int dump_btf_type(const struct btf *btf, __u32 id,\n \t\t\t const struct btf_type *t)\n {\n@@ -415,6 +550,95 @@ static int dump_btf_type(const struct btf *btf, __u32 id,\n \t\t}\n \t\tbreak;\n \t}\n+\tcase BTF_KIND_LOC_PARAM: {\n+\t\tconst struct btf_loc_param *p = btf_loc_param(t);\n+\t\t__u32 vlen = btf_vlen(t);\n+\t\tchar param_str[256] = {};\n+\n+\t\tbtf_loc_param_str(t, param_str, sizeof(param_str));\n+\n+\t\tif (json_output) {\n+\t\t\tjsonw_uint_field(w, \"size\", t-\u003esize);\n+\t\t\tjsonw_uint_field(w, \"flags\", p-\u003eflags);\n+\t\t\tjsonw_uint_field(w, \"vlen\", vlen);\n+\t\t\tjsonw_string_field(w, \"values\", param_str);\n+\t\t} else {\n+\t\t\tprintf(\" size=%u flags=0x%x vlen=%u values='%s'\",\n+\t\t\t t-\u003esize, p-\u003eflags, vlen, param_str);\n+\t\t}\n+\t\tbreak;\n+\t}\n+\tcase BTF_KIND_LOC_PROTO: {\n+\t\t__u32 *params = btf_loc_proto_params(t);\n+\t\t__u32 i, vlen = btf_vlen(t);\n+\n+\t\tif (json_output) {\n+\t\t\tjsonw_uint_field(w, \"vlen\", vlen);\n+\t\t\tjsonw_name(w, \"params\");\n+\t\t\tjsonw_start_array(w);\n+\t\t} else {\n+\t\t\tprintf(\" vlen=%u\", vlen);\n+\t\t}\n+\n+\t\tfor (i = 0; i \u003c vlen; i++, params++) {\n+\t\t\tconst struct btf_type *p;\n+\t\t\tchar param_str[256] = {};\n+\n+\t\t\tif (*params) {\n+\t\t\t\tp = btf__type_by_id(btf, *params);\n+\t\t\t\tbtf_loc_param_str(p, param_str, sizeof(param_str));\n+\t\t\t} else {\n+\t\t\t\tsnprintf(param_str, sizeof(param_str), \"\u003cunavailable\u003e\");\n+\t\t\t}\n+\t\t\tif (json_output) {\n+\t\t\t\tjsonw_start_object(w);\n+\t\t\t\tjsonw_uint_field(w, \"type_id\", *params);\n+\t\t\t\tjsonw_string_field(w, \"value\", param_str);\n+\t\t\t\tjsonw_end_object(w);\n+\t\t\t} else {\n+\t\t\t\tprintf(\"\\n\\ttype_id=%u value='%s'\", *params, param_str);\n+\t\t\t}\n+\t\t}\n+\t\tif (json_output)\n+\t\t\tjsonw_end_array(w);\n+\t\tbreak;\n+\t}\n+\n+\tcase BTF_KIND_LOCSEC: {\n+\t\tstruct btf_loc *locs = btf_locsec_locs(t);\n+\t\t__u32 i, vlen = btf_vlen(t);\n+\n+\t\tif (json_output) {\n+\t\t\tjsonw_uint_field(w, \"vlen\", vlen);\n+\t\t\tjsonw_name(w, \"locs\");\n+\t\t\tjsonw_start_array(w);\n+\t\t} else {\n+\t\t\tprintf(\" vlen=%u\", vlen);\n+\t\t}\n+\n+\t\tfor (i = 0; i \u003c vlen; i++, locs++) {\n+\t\t\tconst struct btf_type *f = btf__type_by_id(btf, locs-\u003efunc);\n+\t\t\tconst char *name = \"\u003cinvalid\u003e\";\n+\n+\t\t\tif (f \u0026\u0026 btf_is_func(f))\n+\t\t\t\tname = btf_str(btf, f-\u003ename_off);\n+\n+\t\t\tif (json_output) {\n+\t\t\t\tjsonw_start_object(w);\n+\t\t\t\tjsonw_uint_field(w, \"func_type_id\", locs-\u003efunc);\n+\t\t\t\tjsonw_string_field(w, \"name\", name);\n+\t\t\t\tjsonw_uint_field(w, \"loc_proto_type_id\", locs-\u003eloc_proto);\n+\t\t\t\tjsonw_uint_field(w, \"offset\", locs-\u003eoffset);\n+\t\t\t\tjsonw_end_object(w);\n+\t\t\t} else {\n+\t\t\t\tprintf(\"\\n\\tname='%s' func_type_id=%u loc_proto_type_id=%u offset=%u\",\n+\t\t\t\t name, locs-\u003efunc, locs-\u003eloc_proto, locs-\u003eoffset);\n+\t\t\t}\n+\t\t}\n+\t\tif (json_output)\n+\t\t\tjsonw_end_array(w);\n+\t\tbreak;\n+\t}\n \tdefault:\n \t\tbreak;\n \t}\n@@ -1545,7 +1769,7 @@ static int do_help(int argc, char **argv)\n \t\t\" \" HELP_SPEC_MAP \"\\n\"\n \t\t\" \" HELP_SPEC_PROGRAM \"\\n\"\n \t\t\" \" HELP_SPEC_OPTIONS \" |\\n\"\n-\t\t\" {-B|--base-btf} }\\n\"\n+\t\t\" {[{-B|--base-btf} FILE]... }}\\n\"\n \t\t\"\",\n \t\tbin_name, \"btf\");\n \ndiff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c\nindex 5ababd8f7d0a0..83884b21e7081 100644\n--- a/tools/bpf/bpftool/main.c\n+++ b/tools/bpf/bpftool/main.c\n@@ -466,6 +466,7 @@ int main(int argc, char **argv)\n \t\t{ \"base-btf\",\trequired_argument, NULL, 'B' },\n \t\t{ 0 }\n \t};\n+\tstruct btf *new_base_btf = NULL, *root_base_btf = NULL;\n \tbool version_requested = false;\n \tint opt, ret;\n \n@@ -515,12 +516,16 @@ int main(int argc, char **argv)\n \t\t\tverifier_logs = true;\n \t\t\tbreak;\n \t\tcase 'B':\n-\t\t\tbase_btf = btf__parse(optarg, NULL);\n-\t\t\tif (!base_btf) {\n+\t\t\t/* handle multi-split BTF */\n+\t\t\tnew_base_btf = btf__parse_split(optarg, base_btf);\n+\t\t\tif (!new_base_btf) {\n \t\t\t\tp_err(\"failed to parse base BTF at '%s': %d\\n\",\n \t\t\t\t optarg, -errno);\n \t\t\t\treturn -1;\n \t\t\t}\n+\t\t\tbase_btf = new_base_btf;\n+\t\t\tif (!root_base_btf)\n+\t\t\t\troot_base_btf = base_btf;\n \t\t\tbreak;\n \t\tcase 'L':\n \t\t\tuse_loader = true;\n@@ -567,7 +572,20 @@ int main(int argc, char **argv)\n \tif (json_output)\n \t\tjsonw_destroy(\u0026json_wtr);\n \n-\tbtf__free(base_btf);\n+\twhile (base_btf) {\n+\t\tbool is_root = base_btf == root_base_btf;\n+\n+\t\tnew_base_btf = (struct btf *)btf__base_btf(base_btf);\n+\t\tbtf__free(base_btf);\n+\t\t/*\n+\t\t * Do not free base BTF that is an owned .BTF.base ; leads\n+\t\t * to a double-free, so only free as far as the root base\n+\t\t * we explicitly read with -B above.\n+\t\t */\n+\t\tif (is_root)\n+\t\t\tbreak;\n+\t\tbase_btf = new_base_btf;\n+\t}\n \n \treturn ret;\n }\ndiff --git a/tools/include/uapi/linux/btf.h b/tools/include/uapi/linux/btf.h\nindex 618167cab4e61..11d65871d163e 100644\n--- a/tools/include/uapi/linux/btf.h\n+++ b/tools/include/uapi/linux/btf.h\n@@ -54,8 +54,8 @@ struct btf_type {\n \t * decl_tag and type_tag\n \t */\n \t__u32 info;\n-\t/* \"size\" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.\n-\t * \"size\" tells the size of the type it is describing.\n+\t/* \"size\" is used by INT, ENUM, STRUCT, UNION, DATASEC, ENUM64\n+\t * and LOC_PARAM. \"size\" tells the size of the type it is describing.\n \t *\n \t * \"type\" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,\n \t * FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG.\n@@ -92,7 +92,9 @@ enum {\n \tBTF_KIND_DECL_TAG\t= 17,\t/* Decl Tag */\n \tBTF_KIND_TYPE_TAG\t= 18,\t/* Type Tag */\n \tBTF_KIND_ENUM64\t\t= 19,\t/* Enumeration up to 64-bit values */\n-\n+\tBTF_KIND_LOC_PARAM\t= 20,\t/* Location parameter information */\n+\tBTF_KIND_LOC_PROTO\t= 21,\t/* Location prototype for site */\n+\tBTF_KIND_LOCSEC\t\t= 22,\t/* Location section */\n \tNR_BTF_KINDS,\n \tBTF_KIND_MAX\t\t= NR_BTF_KINDS - 1,\n };\n@@ -212,4 +214,79 @@ struct btf_enum64 {\n \t__u32\tval_hi32;\n };\n \n+/*\n+ * BTF_KIND_LOC_PARAM is followed by a single \"struct btf_loc_param\"\n+ * that contains flags specifying the contents of the vlen-specified\n+ * number of 4-byte values that follow.\n+ */\n+struct btf_loc_param {\n+\t__u32 flags;\n+\t__u32 values[];\n+};\n+\n+/*\n+ * The combination of size, vlen and flags gives us the means to interpret\n+ * the following vlen-specified set of 4-byte values:\n+ *\n+ * - a BTF_LOC_PARAM_CONST is a constant value; combination\n+ * of size, vlen and _SIGNED flag determines it. If the value requires\n+ * 64 bits it is stored in {lo,hi} order.\n+ * - a BTF_LOC_PARAM_ADDR|BTF_LOC_PARAM_CONST is an address that should be\n+ * normalized with respect to kernel/module base address.\n+ * - a BTF_LOC_PARAM_REG with vlen 1 is a simple register number;\n+ * with vlen 2 it is a multi-register parameter. Register numbers are\n+ * numbers derived from DW_OP_reg values, i.e. 0 is DW_OP_reg0; since\n+ * DW_OP_fbreg has its own special DW_OP_value and DW_OP_regx can refer\n+ * to an arbitrary register number, we reserve BTF_LOC_PARAM_FBREG for\n+ * the frame base register to avoid collisions.\n+ * - a _REG | OFFSET describes an address without dereferencing it.\n+ * - a _REG | DEREF with vlen 1 dereferences the value in the register\n+ * number specified.\n+ * - a REG | DEREF | OFFSET with vlen \u003e 1 specifies the register number\n+ * in the first 4-byte value and the offset in the remainder.\n+ * In the case of REG and OFFSET combinations, the OFFSET has the width\n+ * of the value words while the type size describes the represented parameter,\n+ * so for example a REG | DEREF | OFFSET with size 8 and vlen 2 would be\n+ * an 8-byte register dereference with signed 4-byte offset, since the vlen 2\n+ * values consist of a register value and the signed value.\n+ */\n+enum btf_loc_param_flags {\n+\tBTF_LOC_PARAM_SIGNED\t\t=\t0x1,\n+\tBTF_LOC_PARAM_CONST\t\t=\t0x2,\n+\tBTF_LOC_PARAM_ADDR\t\t=\t0x4,\n+\tBTF_LOC_PARAM_REG\t\t=\t0x8,\n+\tBTF_LOC_PARAM_DEREF\t\t=\t0x10,\n+\tBTF_LOC_PARAM_OFFSET\t\t=\t0x20,\n+};\n+\n+enum {\n+\tBTF_LOC_PARAM_FBREG\t\t=\t0xffffffff\n+};\n+\n+/*\n+ * BTF_KIND_LOC_PROTO specifies location prototypes; i.e. how locations relate\n+ * to parameters; a struct btf_type of BTF_KIND_LOC_PROTO is followed by a\n+ * vlen-specified number of __u32 BTF type ids which specify the associated\n+ * BTF_KIND_LOC_PARAM for each function parameter associated with the\n+ * location. The type should either be 0 (no location info) or point at\n+ * a BTF_KIND_LOC_PARAM.\n+ */\n+\n+/*\n+ * BTF_KIND_LOCSEC consists of vlen-specified number of \"struct btf_loc\"\n+ * containing location site-specific information for a specific ELF section;\n+ * for example locations in \".text\" are in a LOCSEC named \"inline.text\".\n+ *\n+ * - function (func)\n+ * - location prototype type id (loc_proto)\n+ * - address offset (offset) relative to the runtime base address of the\n+ * ELF section associated with the LOCSEC\n+ */\n+\n+struct btf_loc {\n+\t__u32 func;\n+\t__u32 loc_proto;\n+\t__u32 offset;\n+};\n+\n #endif /* _UAPI__LINUX_BTF_H__ */\ndiff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c\nindex 908bd344229d5..512b3962f75f8 100644\n--- a/tools/lib/bpf/btf.c\n+++ b/tools/lib/bpf/btf.c\n@@ -57,6 +57,9 @@ static struct btf_layout layouts[NR_BTF_KINDS] = {\n [BTF_KIND_DECL_TAG] =\t{\tsizeof(struct btf_decl_tag),\t0,\t\t\t\t0 },\n [BTF_KIND_TYPE_TAG] =\t{\t0,\t\t\t\t0,\t\t\t\t0 },\n [BTF_KIND_ENUM64] =\t{\t0,\t\t\t\tsizeof(struct btf_enum64),\t0 },\n+[BTF_KIND_LOC_PARAM] =\t{\tsizeof(struct btf_loc_param),\tsizeof(__u32),\t\t\t0 },\n+[BTF_KIND_LOC_PROTO] =\t{\t0,\t\t\t\tsizeof(__u32),\t\t\t0 },\n+[BTF_KIND_LOCSEC] =\t{\t0,\t\t\t\tsizeof(struct btf_loc),\t\t0 },\n };\n \n struct btf {\n@@ -486,6 +489,12 @@ static int btf_type_size(const struct btf *btf, const struct btf_type *t)\n \t\treturn base_size + vlen * sizeof(struct btf_var_secinfo);\n \tcase BTF_KIND_DECL_TAG:\n \t\treturn base_size + sizeof(struct btf_decl_tag);\n+\tcase BTF_KIND_LOC_PARAM:\n+\t\treturn base_size + sizeof(struct btf_loc_param) + vlen * sizeof(__u32);\n+\tcase BTF_KIND_LOC_PROTO:\n+\t\treturn base_size + vlen * sizeof(__u32);\n+\tcase BTF_KIND_LOCSEC:\n+\t\treturn base_size + vlen * sizeof(struct btf_loc);\n \tdefault:\n \t\treturn btf_type_size_unknown(btf, t);\n \t}\n@@ -501,12 +510,15 @@ static void btf_bswap_type_base(struct btf_type *t)\n static int btf_bswap_type_rest(struct btf_type *t)\n {\n \tstruct btf_var_secinfo *v;\n+\tstruct btf_loc_param *lp;\n \tstruct btf_enum64 *e64;\n \tstruct btf_member *m;\n \tstruct btf_array *a;\n \tstruct btf_param *p;\n \tstruct btf_enum *e;\n+\tstruct btf_loc *l;\n \t__u32 vlen = btf_vlen(t);\n+\t__u32 *d;\n \tint i;\n \n \tswitch (btf_kind(t)) {\n@@ -569,6 +581,23 @@ static int btf_bswap_type_rest(struct btf_type *t)\n \tcase BTF_KIND_DECL_TAG:\n \t\tbtf_decl_tag(t)-\u003ecomponent_idx = bswap_32(btf_decl_tag(t)-\u003ecomponent_idx);\n \t\treturn 0;\n+\tcase BTF_KIND_LOC_PARAM:\n+\t\tlp = btf_loc_param(t);\n+\t\tlp-\u003eflags = bswap_32(lp-\u003eflags);\n+\t\tfor (i = 0, d = (__u32 *)(lp + 1); i \u003c vlen; i++, d++)\n+\t\t\t*d = bswap_32(*d);\n+\t\treturn 0;\n+\tcase BTF_KIND_LOC_PROTO:\n+\t\tfor (i = 0, d = btf_loc_proto_params(t); i \u003c vlen; i++, d++)\n+\t\t\t*d = bswap_32(*d);\n+\t\treturn 0;\n+\tcase BTF_KIND_LOCSEC:\n+\t\tfor (i = 0, l = btf_locsec_locs(t); i \u003c vlen; i++, l++) {\n+\t\t\tl-\u003efunc = bswap_32(l-\u003efunc);\n+\t\t\tl-\u003eloc_proto = bswap_32(l-\u003eloc_proto);\n+\t\t\tl-\u003eoffset = bswap_32(l-\u003eoffset);\n+\t\t}\n+\t\treturn 0;\n \tdefault:\n \t\tpr_debug(\"Unsupported BTF_KIND:%u\\n\", btf_kind(t));\n \t\treturn -EINVAL;\n@@ -745,6 +774,33 @@ static int btf_validate_type(const struct btf *btf, const struct btf_type *t, __\n \t\t}\n \t\tbreak;\n \t}\n+\tcase BTF_KIND_LOC_PARAM:\n+\t\tbreak;\n+\tcase BTF_KIND_LOC_PROTO: {\n+\t\t__u32 *p = btf_loc_proto_params(t);\n+\n+\t\tn = btf_vlen(t);\n+\t\tfor (i = 0; i \u003c n; i++, p++) {\n+\t\t\terr = btf_validate_id(btf, *p, id);\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n+\t\tbreak;\n+\t}\n+\tcase BTF_KIND_LOCSEC: {\n+\t\tconst struct btf_loc *l = btf_locsec_locs(t);\n+\n+\t\tn = btf_vlen(t);\n+\t\tfor (i = 0; i \u003c n; i++, l++) {\n+\t\t\tif (!err)\n+\t\t\t\terr = btf_validate_id(btf, l-\u003efunc, id);\n+\t\t\tif (!err)\n+\t\t\t\terr = btf_validate_id(btf, l-\u003eloc_proto, id);\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n+\t\tbreak;\n+\t}\n \tdefault:\n \t\t/* Kind may be represented in kind layout information. */\n \t\tif (btf_type_size_unknown(btf, t) \u003c 0) {\n@@ -3344,6 +3400,243 @@ int btf__add_decl_attr(struct btf *btf, const char *value, int ref_type_id,\n \treturn btf_add_decl_tag(btf, value, ref_type_id, component_idx, 1);\n }\n \n+/*\n+ * Append new BTF_KIND_LOC_PARAM with specified size and flags. Values are\n+ * added via btf__add_loc_param_value().\n+ *\n+ * - *byte_sz* - size of the type, in bytes (1 through 16);\n+ * - *flags* - combination of enum btf_loc_param_flags values\n+ *\n+ * Returns:\n+ * - \u003e0, type ID of newly added BTF type;\n+ * - \u003c0, on error.\n+ */\n+int btf__add_loc_param(struct btf *btf, __u32 byte_sz, __u32 flags)\n+{\n+\tstruct btf_loc_param *p;\n+\tstruct btf_type *t;\n+\tint sz, err;\n+\n+\tif (!byte_sz || byte_sz \u003e 16)\n+\t\treturn libbpf_err(-EINVAL);\n+\n+\terr = btf_ensure_modifiable(btf);\n+\tif (err)\n+\t\treturn libbpf_err(err);\n+\n+\tsz = sizeof(struct btf_type) + sizeof(*p);\n+\tt = btf_add_type_mem(btf, sz);\n+\tif (!t)\n+\t\treturn libbpf_err(-ENOMEM);\n+\n+\tt-\u003ename_off = 0;\n+\tt-\u003einfo = btf_type_info(BTF_KIND_LOC_PARAM, 0, 0);\n+\tt-\u003esize = byte_sz;\n+\n+\tp = btf_loc_param(t);\n+\tp-\u003eflags = flags;\n+\n+\treturn btf_commit_type(btf, sz);\n+}\n+\n+/*\n+ * Append *value* to existing BTF_KIND_LOC_PARAM.\n+ *\n+ * Returns:\n+ * - 0 on success\n+ * - \u003c0, on error.\n+ */\n+int btf__add_loc_param_value(struct btf *btf, __u32 value)\n+{\n+\tstruct btf_type *t;\n+\tint sz, err;\n+\t__u32 *v;\n+\n+\t/* last type should be BTF_KIND_LOC_PARAM */\n+\tif (btf-\u003enr_types == 0)\n+\t\treturn libbpf_err(-EINVAL);\n+\tt = btf_last_type(btf);\n+\tif (!btf_is_loc_param(t))\n+\t\treturn libbpf_err(-EINVAL);\n+\n+\t/* decompose and invalidate raw data */\n+\terr = btf_ensure_modifiable(btf);\n+\tif (err)\n+\t\treturn libbpf_err(err);\n+\n+\tsz = sizeof(value);\n+\tv = btf_add_type_mem(btf, sz);\n+\tif (!v)\n+\t\treturn libbpf_err(-ENOMEM);\n+\t*v = value;\n+\n+\t/* update parent type's vlen */\n+\tt = btf_last_type(btf);\n+\terr = btf_type_inc_vlen(t);\n+\tif (err)\n+\t\treturn libbpf_err(err);\n+\n+\tbtf_hdr_update_type_len(btf, btf-\u003ehdr.type_len + sz);\n+\treturn 0;\n+}\n+\n+/*\n+ * Append new BTF_KIND_LOC_PROTO\n+ *\n+ * The prototype is then populated with 0 or more BTF_KIND_LOC_PARAMs via\n+ * btf__add_loc_proto_param(); similar to how btf__add_func_param() adds\n+ * parameters to a FUNC_PROTO.\n+ *\n+ * Returns:\n+ * - \u003e0, type ID of newly added BTF type;\n+ * - \u003c0, on error.\n+ */\n+int btf__add_loc_proto(struct btf *btf)\n+{\n+\tstruct btf_type *t;\n+\tint err;\n+\n+\terr = btf_ensure_modifiable(btf);\n+\tif (err)\n+\t\treturn libbpf_err(err);\n+\n+\tt = btf_add_type_mem(btf, sizeof(struct btf_type));\n+\tif (!t)\n+\t\treturn libbpf_err(-ENOMEM);\n+\n+\tt-\u003ename_off = 0;\n+\tt-\u003einfo = btf_type_info(BTF_KIND_LOC_PROTO, 0, 0);\n+\tt-\u003esize = 0;\n+\n+\treturn btf_commit_type(btf, sizeof(struct btf_type));\n+}\n+\n+/*\n+ * Append a BTF_KIND_LOC_PARAM id/0 to BTF_KIND_LOC_PROTO.\n+ *\n+ * Returns:\n+ * - 0 on success;\n+ * - \u003c0, on error.\n+\n+ */\n+int btf__add_loc_proto_param(struct btf *btf, __u32 id)\n+{\n+\tstruct btf_type *t;\n+\tint sz, err;\n+\t__u32 *p;\n+\n+\tif (validate_type_id(id))\n+\t\treturn libbpf_err(-EINVAL);\n+\n+\t/* last type should be BTF_KIND_LOC_PROTO */\n+\tif (btf-\u003enr_types == 0)\n+\t\treturn libbpf_err(-EINVAL);\n+\tt = btf_last_type(btf);\n+\tif (!btf_is_loc_proto(t))\n+\t\treturn libbpf_err(-EINVAL);\n+\n+\t/* decompose and invalidate raw data */\n+\terr = btf_ensure_modifiable(btf);\n+\tif (err)\n+\t\treturn libbpf_err(err);\n+\n+\tsz = sizeof(__u32);\n+\tp = btf_add_type_mem(btf, sz);\n+\tif (!p)\n+\t\treturn libbpf_err(-ENOMEM);\n+\t*p = id;\n+\n+\t/* update parent type's vlen */\n+\tt = btf_last_type(btf);\n+\terr = btf_type_inc_vlen(t);\n+\tif (err)\n+\t\treturn libbpf_err(err);\n+\n+\tbtf_hdr_update_type_len(btf, btf-\u003ehdr.type_len + sz);\n+\treturn 0;\n+}\n+\n+/*\n+ * Append new BTF_KIND_LOCSEC type with:\n+ * - *name* - non-empty/non-NULL name;\n+ *\n+ * Location section is initially empty. Location info can be added with\n+ * btf__add_locsec_loc() calls, after btf__add_locsec() succeeds.\n+ *\n+ * Returns:\n+ * - \u003e0, type ID of newly added BTF type;\n+ * - \u003c0, on error.\n+ */\n+\n+int btf__add_locsec(struct btf *btf, const char *name)\n+{\n+\tstruct btf_type *t;\n+\tint name_off = 0;\n+\tint err;\n+\n+\t/* non-empty name */\n+\tif (str_is_empty(name))\n+\t\treturn libbpf_err(-EINVAL);\n+\n+\terr = btf_ensure_modifiable(btf);\n+\tif (err)\n+\t\treturn libbpf_err(err);\n+\n+\tt = btf_add_type_mem(btf, sizeof(struct btf_type));\n+\tif (!t)\n+\t\treturn libbpf_err(-ENOMEM);\n+\n+\tname_off = btf__add_str(btf, name);\n+\tif (name_off \u003c 0)\n+\t\treturn libbpf_err(name_off);\n+\tt-\u003ename_off = name_off;\n+\tt-\u003einfo = btf_type_info(BTF_KIND_LOCSEC, 0, 0);\n+\tt-\u003esize = 0;\n+\n+\treturn btf_commit_type(btf, sizeof(struct btf_type));\n+}\n+\n+int btf__add_locsec_loc(struct btf *btf, __u32 func, __u32 loc_proto,\n+\t\t\t__u32 offset)\n+{\n+\tstruct btf_type *t;\n+\tstruct btf_loc *l;\n+\tint sz, err;\n+\n+\tif (validate_type_id(func) || validate_type_id(loc_proto))\n+\t\treturn libbpf_err(-EINVAL);\n+\n+\t/* last type should be BTF_KIND_LOCSEC */\n+\tif (btf-\u003enr_types == 0)\n+\t\treturn libbpf_err(-EINVAL);\n+\tt = btf_last_type(btf);\n+\tif (!btf_is_locsec(t))\n+\t\treturn libbpf_err(-EINVAL);\n+\n+\t/* decompose and invalidate raw data */\n+\terr = btf_ensure_modifiable(btf);\n+\tif (err)\n+\t\treturn libbpf_err(err);\n+\n+\tsz = sizeof(*l);\n+\tl = btf_add_type_mem(btf, sz);\n+\tif (!l)\n+\t\treturn libbpf_err(-ENOMEM);\n+\n+\tl-\u003efunc = func;\n+\tl-\u003eloc_proto = loc_proto;\n+\tl-\u003eoffset = offset;\n+\n+\t/* update parent type's vlen */\n+\tt = btf_last_type(btf);\n+\terr = btf_type_inc_vlen(t);\n+\tif (err)\n+\t\treturn libbpf_err(err);\n+\n+\tbtf_hdr_update_type_len(btf, btf-\u003ehdr.type_len + sz);\n+\treturn 0;\n+}\n+\n struct btf_ext_sec_info_param {\n \t__u32 off;\n \t__u32 len;\n@@ -4114,8 +4407,8 @@ static struct btf_dedup *btf_dedup_new(struct btf *btf, const struct btf_dedup_o\n \tfor (i = 1; i \u003c type_cnt; i++) {\n \t\tstruct btf_type *t = btf_type_by_id(d-\u003ebtf, i);\n \n-\t\t/* VAR and DATASEC are never deduped and are self-canonical */\n-\t\tif (btf_is_var(t) || btf_is_datasec(t))\n+\t\t/* VAR, DATASEC and LOCSEC are never deduped and are self-canonical */\n+\t\tif (btf_is_var(t) || btf_is_datasec(t) || btf_is_locsec(t))\n \t\t\td-\u003emap[i] = i;\n \t\telse\n \t\t\td-\u003emap[i] = BTF_UNPROCESSED_ID;\n@@ -4395,6 +4688,47 @@ static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2)\n \t btf_is_any_enum(t1) \u0026\u0026 btf_is_any_enum(t2);\n }\n \n+static long btf_hash_loc_param(struct btf_type *t)\n+{\n+\tstruct btf_loc_param *p = btf_loc_param(t);\n+\tlong h = btf_hash_common(t);\n+\tint i, vlen = btf_vlen(t);\n+\n+\th = hash_combine(h, p-\u003eflags);\n+\n+\tfor (i = 0; i \u003c vlen; i++)\n+\t\th = hash_combine(h, p-\u003evalues[i]);\n+\treturn h;\n+}\n+\n+static long btf_hash_loc_proto(struct btf_type *t)\n+{\n+\t__u32 *p = btf_loc_proto_params(t);\n+\tlong h = btf_hash_common(t);\n+\tint i, vlen = btf_vlen(t);\n+\n+\tfor (i = 0; i \u003c vlen; i++, p++)\n+\t\th = hash_combine(h, *p);\n+\treturn h;\n+}\n+\n+static bool btf_equal_loc_param(struct btf_type *t1, struct btf_type *t2)\n+{\n+\tstruct btf_loc_param *p1 = btf_loc_param(t1);\n+\tstruct btf_loc_param *p2 = btf_loc_param(t2);\n+\tint i, vlen = btf_vlen(t1);\n+\n+\tif (!btf_equal_common(t1, t2))\n+\t\treturn false;\n+\tif (p1-\u003eflags != p2-\u003eflags)\n+\t\treturn false;\n+\tfor (i = 0; i \u003c vlen; i++) {\n+\t\tif (p1-\u003evalues[i] != p2-\u003evalues[i])\n+\t\t\treturn false;\n+\t}\n+\treturn true;\n+}\n+\n /*\n * Calculate type signature hash of STRUCT/UNION, ignoring referenced type IDs,\n * as referenced type IDs equivalence is established separately during type\n@@ -4589,7 +4923,8 @@ static int btf_dedup_prep(struct btf_dedup *d)\n \t\tswitch (btf_kind(t)) {\n \t\tcase BTF_KIND_VAR:\n \t\tcase BTF_KIND_DATASEC:\n-\t\t\t/* VAR and DATASEC are never hash/deduplicated */\n+\t\tcase BTF_KIND_LOCSEC:\n+\t\t\t/* VAR DATASEC and LOCSEC are never hash/deduplicated */\n \t\t\tcontinue;\n \t\tcase BTF_KIND_CONST:\n \t\tcase BTF_KIND_VOLATILE:\n@@ -4622,6 +4957,12 @@ static int btf_dedup_prep(struct btf_dedup *d)\n \t\tcase BTF_KIND_FUNC_PROTO:\n \t\t\th = btf_hash_fnproto(t);\n \t\t\tbreak;\n+\t\tcase BTF_KIND_LOC_PARAM:\n+\t\t\th = btf_hash_loc_param(t);\n+\t\t\tbreak;\n+\t\tcase BTF_KIND_LOC_PROTO:\n+\t\t\th = btf_hash_loc_proto(t);\n+\t\t\tbreak;\n \t\tdefault:\n \t\t\tpr_debug(\"unknown kind %d for type [%d]\\n\", btf_kind(t), type_id);\n \t\t\treturn -EINVAL;\n@@ -4664,6 +5005,8 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)\n \tcase BTF_KIND_DATASEC:\n \tcase BTF_KIND_DECL_TAG:\n \tcase BTF_KIND_TYPE_TAG:\n+\tcase BTF_KIND_LOC_PROTO:\n+\tcase BTF_KIND_LOCSEC:\n \t\treturn 0;\n \n \tcase BTF_KIND_INT:\n@@ -4713,6 +5056,18 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)\n \t\t}\n \t\tbreak;\n \n+\tcase BTF_KIND_LOC_PARAM:\n+\t\th = btf_hash_loc_param(t);\n+\t\tfor_each_dedup_cand(d, hash_entry, h) {\n+\t\t\tcand_id = hash_entry-\u003evalue;\n+\t\t\tcand = btf_type_by_id(d-\u003ebtf, cand_id);\n+\t\t\tif (btf_equal_loc_param(t, cand)) {\n+\t\t\t\tnew_id = cand_id;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tbreak;\n+\n \tdefault:\n \t\treturn -EINVAL;\n \t}\n@@ -5145,6 +5500,13 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,\n \t\treturn 1;\n \t}\n \n+\tcase BTF_KIND_LOC_PARAM:\n+\t\treturn btf_equal_loc_param(cand_type, canon_type);\n+\n+\tcase BTF_KIND_LOC_PROTO:\n+\tcase BTF_KIND_LOCSEC:\n+\t\treturn 0;\n+\n \tdefault:\n \t\treturn -EINVAL;\n \t}\n@@ -5489,6 +5851,37 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)\n \t\tbreak;\n \t}\n \n+\tcase BTF_KIND_LOC_PROTO: {\n+\t\t__u32 *p1, *p2;\n+\t\t__u32 i, vlen;\n+\n+\t\tp1 = btf_loc_proto_params(t);\n+\t\tvlen = btf_vlen(t);\n+\n+\t\tfor (i = 0; i \u003c vlen; i++, p1++) {\n+\t\t\tref_type_id = btf_dedup_ref_type(d, *p1);\n+\t\t\tif (ref_type_id \u003c 0)\n+\t\t\t\treturn ref_type_id;\n+\t\t\t*p1 = ref_type_id;\n+\t\t}\n+\n+\t\th = btf_hash_loc_proto(t);\n+\t\tfor_each_dedup_cand(d, hash_entry, h) {\n+\t\t\tcand_id = hash_entry-\u003evalue;\n+\t\t\tcand = btf_type_by_id(d-\u003ebtf, cand_id);\n+\t\t\tif (!btf_equal_common(t, cand))\n+\t\t\t\tcontinue;\n+\t\t\tvlen = btf_vlen(cand);\n+\t\t\tp1 = btf_loc_proto_params(t);\n+\t\t\tp2 = btf_loc_proto_params(cand);\n+\t\t\tif (memcmp(p1, p2, vlen * sizeof(__u32)) == 0) {\n+\t\t\t\tnew_id = cand_id;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tbreak;\n+\t}\n+\n \tdefault:\n \t\treturn -EINVAL;\n \t}\n@@ -5970,8 +6363,11 @@ static int btf_add_distilled_type_ids(struct btf_distill *dist, __u32 i)\n \t\tcase BTF_KIND_CONST:\n \t\tcase BTF_KIND_RESTRICT:\n \t\tcase BTF_KIND_VOLATILE:\n+\t\tcase BTF_KIND_FUNC:\n \t\tcase BTF_KIND_FUNC_PROTO:\n \t\tcase BTF_KIND_TYPE_TAG:\n+\t\tcase BTF_KIND_LOC_PARAM:\n+\t\tcase BTF_KIND_LOC_PROTO:\n \t\t\tdist-\u003eid_map[*id] = *id;\n \t\t\tbreak;\n \t\tdefault:\n@@ -5997,7 +6393,7 @@ static int btf_add_distilled_type_ids(struct btf_distill *dist, __u32 i)\n \n static int btf_add_distilled_types(struct btf_distill *dist)\n {\n-\tbool adding_to_base = dist-\u003epipe.dst-\u003estart_id == 1;\n+\tbool adding_to_base = dist-\u003epipe.dst-\u003ebase_btf == NULL;\n \tint id = btf__type_cnt(dist-\u003epipe.dst);\n \tstruct btf_type *t;\n \tint i, err = 0;\n@@ -6065,8 +6461,12 @@ static int btf_add_distilled_types(struct btf_distill *dist)\n \t\tcase BTF_KIND_CONST:\n \t\tcase BTF_KIND_RESTRICT:\n \t\tcase BTF_KIND_VOLATILE:\n+\t\tcase BTF_KIND_FUNC:\n \t\tcase BTF_KIND_FUNC_PROTO:\n \t\tcase BTF_KIND_TYPE_TAG:\n+\t\tcase BTF_KIND_LOC_PARAM:\n+\t\tcase BTF_KIND_LOC_PROTO:\n+\t\tcase BTF_KIND_LOCSEC:\n \t\t\t/* All other types are added to split BTF. */\n \t\t\tif (adding_to_base)\n \t\t\t\tcontinue;\ndiff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h\nindex 587172c0de089..57f12630c5d15 100644\n--- a/tools/lib/bpf/btf.h\n+++ b/tools/lib/bpf/btf.h\n@@ -274,6 +274,20 @@ LIBBPF_API int btf__add_decl_tag(struct btf *btf, const char *value, int ref_typ\n LIBBPF_API int btf__add_decl_attr(struct btf *btf, const char *value, int ref_type_id,\n \t\t\t\t int component_idx);\n \n+/* location construction APIs */\n+LIBBPF_API int btf__add_loc_param(struct btf *btf, __u32 size, __u32 flags);\n+\n+LIBBPF_API int btf__add_loc_param_value(struct btf *btf, __u32 value);\n+\n+LIBBPF_API int btf__add_loc_proto(struct btf *btf);\n+\n+LIBBPF_API int btf__add_loc_proto_param(struct btf *btf, __u32 id);\n+\n+LIBBPF_API int btf__add_locsec(struct btf *btf, const char *name);\n+\n+LIBBPF_API int btf__add_locsec_loc(struct btf *btf, __u32 func, __u32 loc_proto,\n+\t\t\t\t __u32 offset);\n+\n struct btf_dedup_opts {\n \tsize_t sz;\n \t/* optional .BTF.ext info to dedup along the main BTF info */\n@@ -431,6 +445,12 @@ btf_dump__dump_type_data(struct btf_dump *d, __u32 id,\n #define BTF_KIND_DECL_TAG\t17\t/* Decl Tag */\n #define BTF_KIND_TYPE_TAG\t18\t/* Type Tag */\n #define BTF_KIND_ENUM64\t\t19\t/* Enum for up-to 64bit values */\n+#define BTF_KIND_LOC_PARAM\t20\t/* Parameter at location */\n+#define BTF_KIND_LOC_PROTO\t21\t/* Parameter set at location */\n+#define BTF_KIND_LOCSEC\t\t22\t/* Section containing location info */\n+\n+struct btf_loc_param;\n+struct btf_loc;\n \n static inline __u16 btf_kind(const struct btf_type *t)\n {\n@@ -569,6 +589,21 @@ static inline bool btf_is_any_enum(const struct btf_type *t)\n \treturn btf_is_enum(t) || btf_is_enum64(t);\n }\n \n+static inline bool btf_is_loc_param(const struct btf_type *t)\n+{\n+\treturn btf_kind(t) == BTF_KIND_LOC_PARAM;\n+}\n+\n+static inline bool btf_is_loc_proto(const struct btf_type *t)\n+{\n+\treturn btf_kind(t) == BTF_KIND_LOC_PROTO;\n+}\n+\n+static inline bool btf_is_locsec(const struct btf_type *t)\n+{\n+\treturn btf_kind(t) == BTF_KIND_LOCSEC;\n+}\n+\n static inline bool btf_kind_core_compat(const struct btf_type *t1,\n \t\t\t\t\tconst struct btf_type *t2)\n {\n@@ -683,6 +718,21 @@ static inline struct btf_decl_tag *btf_decl_tag(const struct btf_type *t)\n \treturn (struct btf_decl_tag *)(t + 1);\n }\n \n+static inline struct btf_loc_param *btf_loc_param(const struct btf_type *t)\n+{\n+\treturn (struct btf_loc_param *)(t + 1);\n+}\n+\n+static inline __u32 *btf_loc_proto_params(const struct btf_type *t)\n+{\n+\treturn (__u32 *)(t + 1);\n+}\n+\n+static inline struct btf_loc *btf_locsec_locs(const struct btf_type *t)\n+{\n+\treturn (struct btf_loc *)(t + 1);\n+}\n+\n #ifdef __cplusplus\n } /* extern \"C\" */\n #endif\ndiff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c\nindex 123c448f20c74..fa995c02a1705 100644\n--- a/tools/lib/bpf/btf_dump.c\n+++ b/tools/lib/bpf/btf_dump.c\n@@ -328,6 +328,9 @@ static int btf_dump_mark_referenced(struct btf_dump *d)\n \t\tcase BTF_KIND_ENUM64:\n \t\tcase BTF_KIND_FWD:\n \t\tcase BTF_KIND_FLOAT:\n+\t\tcase BTF_KIND_LOC_PARAM:\n+\t\tcase BTF_KIND_LOC_PROTO:\n+\t\tcase BTF_KIND_LOCSEC:\n \t\t\tbreak;\n \n \t\tcase BTF_KIND_VOLATILE:\n@@ -609,6 +612,9 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)\n \tcase BTF_KIND_VAR:\n \tcase BTF_KIND_DATASEC:\n \tcase BTF_KIND_DECL_TAG:\n+\tcase BTF_KIND_LOC_PARAM:\n+\tcase BTF_KIND_LOC_PROTO:\n+\tcase BTF_KIND_LOCSEC:\n \t\td-\u003etype_states[id].order_state = ORDERED;\n \t\treturn 0;\n \n@@ -2525,6 +2531,9 @@ static int btf_dump_dump_type_data(struct btf_dump *d,\n \tcase BTF_KIND_FUNC:\n \tcase BTF_KIND_FUNC_PROTO:\n \tcase BTF_KIND_DECL_TAG:\n+\tcase BTF_KIND_LOC_PARAM:\n+\tcase BTF_KIND_LOC_PROTO:\n+\tcase BTF_KIND_LOCSEC:\n \t\terr = btf_dump_unsupported_data(d, t, id);\n \t\tbreak;\n \tcase BTF_KIND_INT:\ndiff --git a/tools/lib/bpf/btf_iter.c b/tools/lib/bpf/btf_iter.c\nindex 9a6c822c22944..199063b55ebe6 100644\n--- a/tools/lib/bpf/btf_iter.c\n+++ b/tools/lib/bpf/btf_iter.c\n@@ -29,6 +29,7 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t,\n \t\tcase BTF_KIND_FLOAT:\n \t\tcase BTF_KIND_ENUM:\n \t\tcase BTF_KIND_ENUM64:\n+\t\tcase BTF_KIND_LOC_PARAM:\n \t\t\tit-\u003edesc = (struct btf_field_desc) {};\n \t\t\tbreak;\n \t\tcase BTF_KIND_FWD:\n@@ -71,6 +72,20 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t,\n \t\t\t\t1, {offsetof(struct btf_var_secinfo, type)}\n \t\t\t};\n \t\t\tbreak;\n+\t\tcase BTF_KIND_LOC_PROTO:\n+\t\t\tit-\u003edesc = (struct btf_field_desc) {\n+\t\t\t\t0, {},\n+\t\t\t\tsizeof(__u32),\n+\t\t\t\t1, {0}};\n+\t\t\tbreak;\n+\t\tcase BTF_KIND_LOCSEC:\n+\t\t\tit-\u003edesc = (struct btf_field_desc) {\n+\t\t\t\t0, {},\n+\t\t\t\tsizeof(struct btf_loc),\n+\t\t\t\t2, {offsetof(struct btf_loc, func),\n+\t\t\t\t offsetof(struct btf_loc, loc_proto)}};\n+\t\t\tbreak;\n+\n \t\tdefault:\n \t\t\treturn -EINVAL;\n \t\t}\n@@ -94,6 +109,9 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t,\n \t\tcase BTF_KIND_DECL_TAG:\n \t\tcase BTF_KIND_TYPE_TAG:\n \t\tcase BTF_KIND_DATASEC:\n+\t\tcase BTF_KIND_LOC_PARAM:\n+\t\tcase BTF_KIND_LOC_PROTO:\n+\t\tcase BTF_KIND_LOCSEC:\n \t\t\tit-\u003edesc = (struct btf_field_desc) {\n \t\t\t\t1, {offsetof(struct btf_type, name_off)}\n \t\t\t};\ndiff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c\nindex 2f53afc985cfb..930588950ab39 100644\n--- a/tools/lib/bpf/libbpf.c\n+++ b/tools/lib/bpf/libbpf.c\n@@ -2483,6 +2483,9 @@ static const char *__btf_kind_str(__u16 kind)\n \tcase BTF_KIND_DECL_TAG: return \"decl_tag\";\n \tcase BTF_KIND_TYPE_TAG: return \"type_tag\";\n \tcase BTF_KIND_ENUM64: return \"enum64\";\n+\tcase BTF_KIND_LOC_PARAM: return \"loc_param\";\n+\tcase BTF_KIND_LOC_PROTO: return \"loc_proto\";\n+\tcase BTF_KIND_LOCSEC: return \"locsec\";\n \tdefault: return \"unknown\";\n \t}\n }\ndiff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map\nindex 7a84dd00ce95f..18d27d20102ec 100644\n--- a/tools/lib/bpf/libbpf.map\n+++ b/tools/lib/bpf/libbpf.map\n@@ -463,6 +463,12 @@ LIBBPF_1.8.0 {\n \t\tbpf_program__attach_tracing_multi;\n \t\tbpf_program__clear_flags;\n \t\tbpf_program__clone;\n+\t\tbtf__add_loc_param;\n+\t\tbtf__add_loc_param_value;\n+\t\tbtf__add_loc_proto;\n+\t\tbtf__add_loc_proto_param;\n+\t\tbtf__add_locsec;\n+\t\tbtf__add_locsec_loc;\n \t\tbtf__find_by_name_kind_own;\n \t\tbtf__new_empty_opts;\n } LIBBPF_1.7.0;\ndiff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h\nindex cb4d962338441..546f65b95cf4d 100644\n--- a/tools/lib/bpf/libbpf_internal.h\n+++ b/tools/lib/bpf/libbpf_internal.h\n@@ -580,7 +580,7 @@ struct btf_field_desc {\n \t/* member struct size, or zero, if no members */\n \tint m_sz;\n \t/* repeated per-member offsets */\n-\tint m_off_cnt, m_offs[1];\n+\tint m_off_cnt, m_offs[2];\n };\n \n struct btf_field_iter {\ndiff --git a/tools/testing/selftests/bpf/btf_helpers.c b/tools/testing/selftests/bpf/btf_helpers.c\nindex 1c1c2c26690a7..85a4d15d1dd1c 100644\n--- a/tools/testing/selftests/bpf/btf_helpers.c\n+++ b/tools/testing/selftests/bpf/btf_helpers.c\n@@ -27,11 +27,14 @@ static const char * const btf_kind_str_mapping[] = {\n \t[BTF_KIND_DECL_TAG]\t= \"DECL_TAG\",\n \t[BTF_KIND_TYPE_TAG]\t= \"TYPE_TAG\",\n \t[BTF_KIND_ENUM64]\t= \"ENUM64\",\n+\t[BTF_KIND_LOC_PARAM]\t= \"LOC_PARAM\",\n+\t[BTF_KIND_LOC_PROTO]\t= \"LOC_PROTO\",\n+\t[BTF_KIND_LOCSEC]\t= \"LOCSEC\",\n };\n \n static const char *btf_kind_str(__u16 kind)\n {\n-\tif (kind \u003e BTF_KIND_ENUM64)\n+\tif (kind \u003e BTF_KIND_LOCSEC)\n \t\treturn \"UNKNOWN\";\n \treturn btf_kind_str_mapping[kind];\n }\n@@ -203,6 +206,36 @@ int fprintf_btf_type_raw(FILE *out, const struct btf *btf, __u32 id)\n \t\tfprintf(out, \" type_id=%u component_idx=%d\",\n \t\t\tt-\u003etype, btf_decl_tag(t)-\u003ecomponent_idx);\n \t\tbreak;\n+\tcase BTF_KIND_LOC_PARAM: {\n+\t\tstruct btf_loc_param *p = btf_loc_param(t);\n+\n+\t\tfprintf(out, \" size=%u flags=0x%x vlen=%u\", t-\u003esize, p-\u003eflags, vlen);\n+\t\tfor (i = 0; i \u003c vlen; i++) {\n+\t\t\tif (p-\u003eflags \u0026 BTF_LOC_PARAM_SIGNED)\n+\t\t\t\tfprintf(out, \"\\n\\tvalue=%d\", (__s32)p-\u003evalues[i]);\n+\t\t\telse\n+\t\t\t\tfprintf(out, \"\\n\\tvalue=%u\", p-\u003evalues[i]);\n+\t\t}\n+\t\tbreak;\n+\t}\n+\tcase BTF_KIND_LOC_PROTO: {\n+\t\tconst __u32 *p = btf_loc_proto_params(t);\n+\n+\t\tfprintf(out, \" vlen=%u\", vlen);\n+\t\tfor (i = 0; i \u003c vlen; i++, p++)\n+\t\t\tfprintf(out, \"\\n\\ttype_id=%u\", *p);\n+\t\tbreak;\n+\t}\n+\tcase BTF_KIND_LOCSEC: {\n+\t\tconst struct btf_loc *l = btf_locsec_locs(t);\n+\n+\t\tfprintf(out, \" vlen=%u\", vlen);\n+\t\tfor (i = 0; i \u003c vlen; i++, l++) {\n+\t\t\tfprintf(out, \"\\n\\tfunc_type_id=%u loc_proto_type_id=%u offset=%u\",\n+\t\t\t\tl-\u003efunc, l-\u003eloc_proto, l-\u003eoffset);\n+\t\t}\n+\t\tbreak;\n+\t}\n \tdefault:\n \t\tbreak;\n \t}\ndiff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c\nindex d5b25302b0c89..bed506badc755 100644\n--- a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c\n+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c\n@@ -57,6 +57,57 @@ static struct btf *mk_btf(void)\n \treturn btf;\n }\n \n+static struct btf *mk_loc_btf(void)\n+{\n+\tstruct btf *btf;\n+\n+\tbtf = btf__new_empty();\n+\tif (!ASSERT_OK_PTR(btf, \"new_empty\"))\n+\t\treturn NULL;\n+\n+\tbtf__add_int(btf, \"int\", 4, BTF_INT_SIGNED);\n+\tbtf__add_func_proto(btf, 1);\n+\tbtf__add_func_param(btf, \"arg1\", 1);\n+\tbtf__add_func_param(btf, \"arg2\", 1);\n+\tbtf__add_func_param(btf, \"arg3\", 1);\n+\tbtf__add_func_param(btf, \"arg4\", 1);\n+\tbtf__add_func_param(btf, \"arg5\", 1);\n+\tbtf__add_func(btf, \"foo\", BTF_FUNC_STATIC, 2);\n+\n+\tbtf__add_loc_param(btf, 4, BTF_LOC_PARAM_REG);\n+\tbtf__add_loc_param_value(btf, 1);\n+\tbtf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG |\n+\t\t\t BTF_LOC_PARAM_DEREF | BTF_LOC_PARAM_OFFSET);\n+\tbtf__add_loc_param_value(btf, 2);\n+\tbtf__add_loc_param_value(btf, 0x10);\n+\tbtf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG |\n+\t\t\t BTF_LOC_PARAM_OFFSET | BTF_LOC_PARAM_SIGNED);\n+\tbtf__add_loc_param_value(btf, BTF_LOC_PARAM_FBREG);\n+\tbtf__add_loc_param_value(btf, -0x10);\n+\tbtf__add_loc_param(btf, 8, BTF_LOC_PARAM_ADDR | BTF_LOC_PARAM_CONST);\n+\tbtf__add_loc_param_value(btf, 0x9abcdef0);\n+\tbtf__add_loc_param_value(btf, 0x12345678);\n+\tbtf__add_loc_param(btf, 8, BTF_LOC_PARAM_CONST);\n+\tbtf__add_loc_param_value(btf, 0xfeedface);\n+\tbtf__add_loc_param_value(btf, 0xdeadbeef);\n+\tbtf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG |\n+\t\t\t BTF_LOC_PARAM_DEREF | BTF_LOC_PARAM_OFFSET |\n+\t\t\t BTF_LOC_PARAM_SIGNED);\n+\tbtf__add_loc_param_value(btf, BTF_LOC_PARAM_FBREG);\n+\tbtf__add_loc_param_value(btf, -0x20);\n+\tbtf__add_loc_proto(btf);\n+\tbtf__add_loc_proto_param(btf, 4);\n+\tbtf__add_loc_proto_param(btf, 5);\n+\tbtf__add_loc_proto_param(btf, 6);\n+\tbtf__add_loc_proto_param(btf, 7);\n+\tbtf__add_loc_proto_param(btf, 8);\n+\tbtf__add_loc_proto_param(btf, 9);\n+\tbtf__add_locsec(btf, \"inline.text\");\n+\tbtf__add_locsec_loc(btf, 3, 10, 64);\n+\n+\treturn btf;\n+}\n+\n static int btf_to_tmpfile(const struct btf *btf, char *path)\n {\n \tssize_t written;\n@@ -105,6 +156,26 @@ static char *dump_c(const char *btf_path, bool sorted)\n \treturn buf;\n }\n \n+static char *dump_raw(const char *btf_path)\n+{\n+\tchar args[MAX_BPFTOOL_CMD_LEN];\n+\tchar *buf;\n+\tint err;\n+\n+\tbuf = malloc(DUMP_BUF_SZ);\n+\tif (!ASSERT_OK_PTR(buf, \"alloc_dump\"))\n+\t\treturn NULL;\n+\n+\tsnprintf(args, sizeof(args), \"btf dump file %s\", btf_path);\n+\terr = get_bpftool_command_output(args, buf, DUMP_BUF_SZ);\n+\tif (!ASSERT_OK(err, \"btf_dump_raw\")) {\n+\t\tfree(buf);\n+\t\treturn NULL;\n+\t}\n+\n+\treturn buf;\n+}\n+\n static char *read_expected(const char *path)\n {\n \tchar *buf = NULL;\n@@ -155,10 +226,46 @@ static void test_dump(const char *btf_path, bool sorted)\n \tfree(dump);\n }\n \n+static void test_loc_dump(const char *btf_path)\n+{\n+\tconst char expected[] =\n+\t\t\"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED\\n\"\n+\t\t\"[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=5\\n\"\n+\t\t\"\\t'arg1' type_id=1\\n\"\n+\t\t\"\\t'arg2' type_id=1\\n\"\n+\t\t\"\\t'arg3' type_id=1\\n\"\n+\t\t\"\\t'arg4' type_id=1\\n\"\n+\t\t\"\\t'arg5' type_id=1\\n\"\n+\t\t\"[3] FUNC 'foo' type_id=2 linkage=static\\n\"\n+\t\t\"[4] LOC_PARAM '(anon)' size=4 flags=0x8 vlen=1 values='reg1'\\n\"\n+\t\t\"[5] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(reg2 + 0x10)'\\n\"\n+\t\t\"[6] LOC_PARAM '(anon)' size=8 flags=0x29 vlen=2 values='fbreg - 0x10'\\n\"\n+\t\t\"[7] LOC_PARAM '(anon)' size=8 flags=0x6 vlen=2 values='0x123456789abcdef0 (addr)'\\n\"\n+\t\t\"[8] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2 values='0xdeadbeeffeedface'\\n\"\n+\t\t\"[9] LOC_PARAM '(anon)' size=8 flags=0x39 vlen=2 values='*(fbreg - 0x20)'\\n\"\n+\t\t\"[10] LOC_PROTO '(anon)' vlen=6\\n\"\n+\t\t\"\\ttype_id=4 value='reg1'\\n\"\n+\t\t\"\\ttype_id=5 value='*(reg2 + 0x10)'\\n\"\n+\t\t\"\\ttype_id=6 value='fbreg - 0x10'\\n\"\n+\t\t\"\\ttype_id=7 value='0x123456789abcdef0 (addr)'\\n\"\n+\t\t\"\\ttype_id=8 value='0xdeadbeeffeedface'\\n\"\n+\t\t\"\\ttype_id=9 value='*(fbreg - 0x20)'\\n\"\n+\t\t\"[11] LOCSEC 'inline.text' vlen=1\\n\"\n+\t\t\"\\tname='foo' func_type_id=3 loc_proto_type_id=10 offset=64\\n\";\n+\tchar *dump;\n+\n+\tdump = dump_raw(btf_path);\n+\tif (!dump)\n+\t\treturn;\n+\n+\tASSERT_OK(compare_text_to_expected(dump, expected), \"cmp_loc_dump\");\n+\tfree(dump);\n+}\n+\n void test_bpftool_btf_dump(void)\n {\n \tchar path[PATH_MAX];\n-\tstruct btf *btf;\n+\tstruct btf *btf = NULL;\n \n \tbtf = mk_btf();\n \tif (!btf)\n@@ -171,7 +278,19 @@ void test_bpftool_btf_dump(void)\n \t\ttest_dump(path, true);\n \tif (test__start_subtest(\"c_unsorted\"))\n \t\ttest_dump(path, false);\n+\tunlink(path);\n+\n+\tbtf__free(btf);\n+\n+\tbtf = mk_loc_btf();\n+\tif (!btf)\n+\t\treturn;\n+\n+\tif (btf_to_tmpfile(btf, path))\n+\t\tgoto out_btf;\n \n+\tif (test__start_subtest(\"loc_dump\"))\n+\t\ttest_loc_dump(path);\n \tunlink(path);\n out_btf:\n \tbtf__free(btf);\ndiff --git a/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c b/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c\nindex 9d61611515938..5c15a6b7f4fb9 100644\n--- a/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c\n+++ b/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c\n@@ -554,6 +554,120 @@ static void test_split_module(void)\n \tbtf__free(vmlinux_btf);\n }\n \n+static void test_split_loc(void)\n+{\n+\tstruct btf *btf1, *btf2;\n+\tint err;\n+\n+\tbtf1 = btf__new_empty();\n+\tif (!ASSERT_OK_PTR(btf1, \"empty_main_btf\"))\n+\t\treturn;\n+\n+\tbtf__set_pointer_size(btf1, 8); /* enforce 64-bit arch */\n+\n+\n+\tbtf__add_int(btf1, \"long\", 8, BTF_INT_SIGNED); /* [1] long */\n+\tbtf__add_ptr(btf1, 1); /* [2] ptr to long */\n+\tbtf__add_func_proto(btf1, 1);\t\t\t/* [3] long (*)(long, long *); */\n+\tbtf__add_func_param(btf1, \"p1\", 1);\n+\tbtf__add_func_param(btf1, \"p2\", 2);\n+\tbtf__add_func(btf1, \"foo\", BTF_FUNC_STATIC, 3);\t/* [4] long foo(long, long *); */\n+\tbtf__add_loc_param(btf1, 8, BTF_LOC_PARAM_CONST);\n+\tbtf__add_loc_param_value(btf1, 3735928559);\n+\tbtf__add_loc_param_value(btf1, 4277009102);\t/* [5] loc value */\n+\tbtf__add_loc_param(btf1, 8, BTF_LOC_PARAM_REG);\t/* [6] loc reg 1 */\n+\tbtf__add_loc_param_value(btf1, 1);\n+\tbtf__add_loc_proto(btf1);\t\t\t/* [7] loc proto */\n+\tbtf__add_loc_proto_param(btf1, 5);\t\t/* param value */\n+\tbtf__add_loc_proto_param(btf1, 6);\t\t/* param reg 1 */\n+\n+\tVALIDATE_RAW_BTF(\n+\t\tbtf1,\n+\t\t\"[1] INT 'long' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED\",\n+\t\t\"[2] PTR '(anon)' type_id=1\",\n+\t\t\"[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\\n\"\n+\t\t\"\\t'p1' type_id=1\\n\"\n+\t\t\"\\t'p2' type_id=2\",\n+\t\t\"[4] FUNC 'foo' type_id=3 linkage=static\",\n+\t\t\"[5] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2\\n\"\n+\t\t\"\\tvalue=3735928559\\n\"\n+\t\t\"\\tvalue=4277009102\",\n+\t\t\"[6] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\\n\"\n+\t\t\"\\tvalue=1\",\n+\t\t\"[7] LOC_PROTO '(anon)' vlen=2\\n\"\n+\t\t\"\\ttype_id=5\\n\"\n+\t\t\"\\ttype_id=6\");\n+\n+\tbtf2 = btf__new_empty_split(btf1);\n+\tif (!ASSERT_OK_PTR(btf2, \"empty_split_btf\"))\n+\t\tgoto cleanup;\n+\tbtf__add_loc_param(btf2, 8, BTF_LOC_PARAM_REG);\n+\tbtf__add_loc_param_value(btf2, 1);\t\t/* [8] loc reg 1 */\n+\tbtf__add_loc_proto(btf2);\t\t\t/* [9] loc proto */\n+\tbtf__add_loc_proto_param(btf2, 5);\t\t/* param value */\n+\tbtf__add_loc_proto_param(btf2, 8);\t\t/* param reg 1 */\n+\tbtf__add_locsec(btf2, \"inline.text\");\t\t/* [10] locsec \"inline.text\" */\n+\t/* add duplicate locsec */\n+\tbtf__add_locsec_loc(btf2, 4, 9, 128);\n+\tbtf__add_locsec(btf2, \"inline.text\");\t\t/* [11] locsec \"inline.text\" */\n+\tbtf__add_locsec_loc(btf2, 4, 9, 128);\n+\n+\tVALIDATE_RAW_BTF(\n+\t\tbtf2,\n+\t\t\"[1] INT 'long' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED\",\n+\t\t\"[2] PTR '(anon)' type_id=1\",\n+\t\t\"[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\\n\"\n+\t\t\"\\t'p1' type_id=1\\n\"\n+\t\t\"\\t'p2' type_id=2\",\n+\t\t\"[4] FUNC 'foo' type_id=3 linkage=static\",\n+\t\t\"[5] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2\\n\"\n+\t\t\"\\tvalue=3735928559\\n\"\n+\t\t\"\\tvalue=4277009102\",\n+\t\t\"[6] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\\n\"\n+\t\t\"\\tvalue=1\",\n+\t\t\"[7] LOC_PROTO '(anon)' vlen=2\\n\"\n+\t\t\"\\ttype_id=5\\n\"\n+\t\t\"\\ttype_id=6\",\n+\t\t\"[8] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\\n\"\n+\t\t\"\\tvalue=1\",\n+\t\t\"[9] LOC_PROTO '(anon)' vlen=2\\n\"\n+\t\t\"\\ttype_id=5\\n\"\n+\t\t\"\\ttype_id=8\",\n+\t\t\"[10] LOCSEC 'inline.text' vlen=1\\n\"\n+\t\t\"\\tfunc_type_id=4 loc_proto_type_id=9 offset=128\",\n+\t\t\"[11] LOCSEC 'inline.text' vlen=1\\n\"\n+\t\t\"\\tfunc_type_id=4 loc_proto_type_id=9 offset=128\");\n+\n+\terr = btf__dedup(btf2, NULL);\n+\tif (!ASSERT_OK(err, \"btf_dedup\"))\n+\t\tgoto cleanup;\n+\n+\tVALIDATE_RAW_BTF(\n+\t\tbtf2,\n+\t\t\"[1] INT 'long' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED\",\n+\t\t\"[2] PTR '(anon)' type_id=1\",\n+\t\t\"[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\\n\"\n+\t\t\"\\t'p1' type_id=1\\n\"\n+\t\t\"\\t'p2' type_id=2\",\n+\t\t\"[4] FUNC 'foo' type_id=3 linkage=static\",\n+\t\t\"[5] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2\\n\"\n+\t\t\"\\tvalue=3735928559\\n\"\n+\t\t\"\\tvalue=4277009102\",\n+\t\t\"[6] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\\n\"\n+\t\t\"\\tvalue=1\",\n+\t\t\"[7] LOC_PROTO '(anon)' vlen=2\\n\"\n+\t\t\"\\ttype_id=5\\n\"\n+\t\t\"\\ttype_id=6\",\n+\t\t\"[8] LOCSEC 'inline.text' vlen=1\\n\"\n+\t\t\"\\tfunc_type_id=4 loc_proto_type_id=7 offset=128\",\n+\t\t\"[9] LOCSEC 'inline.text' vlen=1\\n\"\n+\t\t\"\\tfunc_type_id=4 loc_proto_type_id=7 offset=128\");\n+\n+cleanup:\n+\tbtf__free(btf2);\n+\tbtf__free(btf1);\n+}\n+\n void test_btf_dedup_split()\n {\n \tif (test__start_subtest(\"split_simple\"))\n@@ -566,4 +680,6 @@ void test_btf_dedup_split()\n \t\ttest_split_dup_struct_in_cu();\n \tif (test__start_subtest(\"split_module\"))\n \t\ttest_split_module();\n+\tif (test__start_subtest(\"split_loc\"))\n+\t\ttest_split_loc();\n }\ndiff --git a/tools/testing/selftests/bpf/prog_tests/btf_distill.c b/tools/testing/selftests/bpf/prog_tests/btf_distill.c\nindex fb67ae195a738..f0900b47fd487 100644\n--- a/tools/testing/selftests/bpf/prog_tests/btf_distill.c\n+++ b/tools/testing/selftests/bpf/prog_tests/btf_distill.c\n@@ -671,6 +671,110 @@ static void test_distilled_base_embedded_err(void)\n \tbtf__free(btf1);\n }\n \n+/* LOC_PARAM, LOC_PROTO should be added to split BTF. */\n+static void test_distilled_loc(void)\n+{\n+\tstruct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL;\n+\tstruct btf *btf4 = NULL, *btf5 = NULL;\n+\n+\tbtf1 = btf__new_empty();\n+\tif (!ASSERT_OK_PTR(btf1, \"empty_main_btf\"))\n+\t\treturn;\n+\n+\tbtf__add_int(btf1, \"int\", 4, BTF_INT_SIGNED);\t/* [1] int */\n+\tbtf__add_func_proto(btf1, 1); /* [2] int (*)(int); */\n+\tbtf__add_func_param(btf1, \"p1\", 1);\n+\tbtf__add_func(btf1, \"foo\", BTF_FUNC_STATIC, 2);\t/* [3] int foo(int); */\n+\tbtf__add_loc_param(btf1, 4, BTF_LOC_PARAM_SIGNED | BTF_LOC_PARAM_CONST);\n+\tbtf__add_loc_param_value(btf1, -1);\t\t/* [4] loc value */\n+\tbtf__add_loc_proto(btf1);\t\t\t/* [5] loc proto */\n+\tbtf__add_loc_proto_param(btf1, 4);\t\t/* param value */\n+\tbtf__add_int(btf1, \"unsigned int\", 4, 0);\t/* [6] unsigned int */\n+\n+\tVALIDATE_RAW_BTF(\n+\t\tbtf1,\n+\t\t\"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED\",\n+\t\t\"[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\\n\"\n+\t\t\"\\t'p1' type_id=1\",\n+\t\t\"[3] FUNC 'foo' type_id=2 linkage=static\",\n+\t\t\"[4] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\\n\"\n+\t\t\"\\tvalue=-1\",\n+\t\t\"[5] LOC_PROTO '(anon)' vlen=1\\n\"\n+\t\t\"\\ttype_id=4\",\n+\t\t\"[6] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)\");\n+\n+\tbtf2 = btf__new_empty_split(btf1);\n+\tif (!ASSERT_OK_PTR(btf2, \"empty_split_btf\"))\n+\t\tgoto cleanup;\n+\n+\tbtf__add_locsec(btf2, \"inline.text\");\t\t/* [6] locsec */\n+\tbtf__add_locsec_loc(btf2, 3, 5, 256);\t\t/* \"foo\" offset 256 */\n+\tVALIDATE_RAW_BTF(\n+\t\tbtf2,\n+\t\t\"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED\",\n+\t\t\"[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\\n\"\n+\t\t\"\\t'p1' type_id=1\",\n+\t\t\"[3] FUNC 'foo' type_id=2 linkage=static\",\n+\t\t\"[4] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\\n\"\n+\t\t\"\\tvalue=-1\",\n+\t\t\"[5] LOC_PROTO '(anon)' vlen=1\\n\"\n+\t\t\"\\ttype_id=4\",\n+\t\t\"[6] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)\",\n+\t\t\"[7] LOCSEC 'inline.text' vlen=1\\n\"\n+\t\t\"\\tfunc_type_id=3 loc_proto_type_id=5 offset=256\");\n+\n+\tif (!ASSERT_EQ(0, btf__distill_base(btf2, \u0026btf3, \u0026btf4),\n+\t\t \"distilled_base\") ||\n+\t !ASSERT_OK_PTR(btf3, \"distilled_base\") ||\n+\t !ASSERT_OK_PTR(btf4, \"distilled_split\") ||\n+\t !ASSERT_EQ(2, btf__type_cnt(btf3), \"distilled_base_type_cnt\"))\n+\t\tgoto cleanup;\n+\n+\tVALIDATE_RAW_BTF(\n+\t\tbtf4,\n+\t\t\"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED\",\n+\t\t/* remainder is split BTF */\n+\t\t\"[2] LOCSEC 'inline.text' vlen=1\\n\"\n+\t\t\"\\tfunc_type_id=4 loc_proto_type_id=6 offset=256\",\n+\t\t\"[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\\n\"\n+\t\t\"\\t'p1' type_id=1\",\n+\t\t\"[4] FUNC 'foo' type_id=3 linkage=static\",\n+\t\t\"[5] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\\n\"\n+\t\t\"\\tvalue=-1\",\n+\t\t\"[6] LOC_PROTO '(anon)' vlen=1\\n\"\n+\t\t\"\\ttype_id=5\");\n+\n+\tbtf5 = btf__new_empty();\n+\tif (!ASSERT_OK_PTR(btf5, \"empty_reloc_btf\"))\n+\t\tgoto cleanup;\n+\tbtf__add_int(btf5, \"int\", 4, BTF_INT_SIGNED);\t/* [1] int */\n+\tbtf__add_int(btf5, \"char\", 1, 0);\t\t/* [2] char */\n+\n+\tif (!ASSERT_EQ(btf__relocate(btf4, btf5), 0, \"relocate_split\"))\n+\t\tgoto cleanup;\n+\tVALIDATE_RAW_BTF(\n+\t\tbtf4,\n+\t\t\"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED\",\n+\t\t\"[2] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=(none)\",\n+\t\t/* remainder is split BTF */\n+\t\t\"[3] LOCSEC 'inline.text' vlen=1\\n\"\n+\t\t\"\\tfunc_type_id=5 loc_proto_type_id=7 offset=256\",\n+\t\t\"[4] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\\n\"\n+\t\t\"\\t'p1' type_id=1\",\n+\t\t\"[5] FUNC 'foo' type_id=4 linkage=static\",\n+\t\t\"[6] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\\n\"\n+\t\t\"\\tvalue=-1\",\n+\t\t\"[7] LOC_PROTO '(anon)' vlen=1\\n\"\n+\t\t\"\\ttype_id=6\");\n+\n+cleanup:\n+\tbtf__free(btf5);\n+\tbtf__free(btf4);\n+\tbtf__free(btf3);\n+\tbtf__free(btf2);\n+\tbtf__free(btf1);\n+}\n+\n void test_btf_distill(void)\n {\n \tif (test__start_subtest(\"distilled_base\"))\n@@ -689,4 +793,6 @@ void test_btf_distill(void)\n \t\ttest_distilled_base_vmlinux();\n \tif (test__start_subtest(\"distilled_endianness\"))\n \t\ttest_distilled_endianness();\n+\tif (test__start_subtest(\"distilled_loc\"))\n+\t\ttest_distilled_loc();\n }\ndiff --git a/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c b/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c\nindex 32159d3eb281b..dcb5429d141df 100644\n--- a/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c\n+++ b/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c\n@@ -31,8 +31,11 @@ struct field_data {\n \t{ .ids = { 11 },\t.strs = { \"decltag\" } },\n \t{ .ids = { 6 },\t\t.strs = { \"typetag\" } },\n \t{ .ids = {},\t\t.strs = { \"e64\", \"eval1\", \"eval2\", \"eval3\" } },\n-\t{ .ids = { 15, 16 },\t.strs = { \"datasec1\" } }\n-\n+\t{ .ids = { 15, 16 },\t.strs = { \"datasec1\" } },\n+\t{ .ids = {},\t\t.strs = { \"\" } },\n+\t{ .ids = {},\t\t.strs = { \"\" } },\n+\t{ .ids = { 22, 23 },\t.strs = { \"\" } },\n+\t{ .ids = { 14, 24 },\t.strs = { \".loc\" } }\n };\n \n /* Fabricate BTF with various types and check BTF field iteration finds types,\n@@ -88,6 +91,19 @@ void test_btf_field_iter(void)\n \tbtf__add_datasec_var_info(btf, 15, 0, 4);\n \tbtf__add_datasec_var_info(btf, 16, 4, 8);\n \n+\tbtf__add_loc_param(btf, 4, BTF_LOC_PARAM_CONST | BTF_LOC_PARAM_SIGNED);\n+\t\t\t\t\t\t\t/* [22] loc value -1 */\n+\tbtf__add_loc_param_value(btf, -1);\n+\tbtf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG);\t/* [23] loc reg 1 */\n+\tbtf__add_loc_param_value(btf, 1);\n+\n+\tbtf__add_loc_proto(btf);\t\t\t/* [24] loc proto */\n+\tbtf__add_loc_proto_param(btf, 22);\t\t/* param value -1, */\n+\tbtf__add_loc_proto_param(btf, 23);\t\t/* param reg 1 */\n+\n+\tbtf__add_locsec(btf, \".loc\");\t\t\t/* [25] locsec \".loc\" */\n+\tbtf__add_locsec_loc(btf, 14, 24, 128);\t\t/* \"func\" */\n+\n \tVALIDATE_RAW_BTF(\n \t\tbtf,\n \t\t\"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED\",\n@@ -123,7 +139,16 @@ void test_btf_field_iter(void)\n \t\t\"\\t'eval3' val=3000\",\n \t\t\"[21] DATASEC 'datasec1' size=12 vlen=2\\n\"\n \t\t\"\\ttype_id=15 offset=0 size=4\\n\"\n-\t\t\"\\ttype_id=16 offset=4 size=8\");\n+\t\t\"\\ttype_id=16 offset=4 size=8\",\n+\t\t\"[22] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\\n\"\n+\t\t\"\\tvalue=-1\",\n+\t\t\"[23] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\\n\"\n+\t\t\"\\tvalue=1\",\n+\t\t\"[24] LOC_PROTO '(anon)' vlen=2\\n\"\n+\t\t\"\\ttype_id=22\\n\"\n+\t\t\"\\ttype_id=23\",\n+\t\t\"[25] LOCSEC '.loc' vlen=1\\n\"\n+\t\t\"\\tfunc_type_id=14 loc_proto_type_id=24 offset=128\");\n \n \tfor (id = 1; id \u003c btf__type_cnt(btf); id++) {\n \t\tstruct btf_type *t = btf_type_by_id(btf, id);\n"
} |
| 2/1 | 2026/09/26 16:57 | agent | patch-evaluator |
1mModel:core Args: null Results: {
"EnableConfigs": [
"BPF_SYSCALL"
],
"FocusSymbols": [
"btf_loc_param_check_meta",
"btf_loc_proto_check_meta",
"btf_loc_proto_resolve",
"btf_locsec_check_meta",
"btf_locsec_resolve",
"bpf_btf_load"
],
"Reasoning": "The patch introduces three new BTF kinds (BTF_KIND_LOC_PARAM, BTF_KIND_LOC_PROTO, and BTF_KIND_LOCSEC) along with their metadata validation and resolution logic in kernel/bpf/btf.c. This code executes in the core BPF subsystem and is directly reachable from userspace via the BPF_BTF_LOAD command of the bpf() system call. Fuzzing is warranted to test bounds checking, type resolution, validation loops, and error handling for malformed BTF payloads.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 006d1ad744f5dd16dd22415c5cef649e66ef7f74
Author: syz-cluster <triage@syzkaller.com>
Date: Sat Sep 26 16:57:26 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst
index 004aa1058d857..29de1222c3e73 100644
--- a/Documentation/bpf/btf.rst
+++ b/Documentation/bpf/btf.rst
@@ -88,6 +88,9 @@ sequentially and type id is assigned to each recognized type starting from id
#define BTF_KIND_DECL_TAG 17 /* Decl Tag */
#define BTF_KIND_TYPE_TAG 18 /* Type Tag */
#define BTF_KIND_ENUM64 19 /* Enumeration up to 64-bit values */
+ #define BTF_KIND_LOC_PARAM 20 /* Location description (register, const etc) */
+ #define BTF_KIND_LOC_PROTO 21 /* Set of location parameters for site */
+ #define BTF_KIND_LOCSEC 22 /* Section with site descriptions */
Note that the type section encodes debug info, not just pure types.
``BTF_KIND_FUNC`` is not a type, and it represents a defined subprogram.
@@ -104,11 +107,13 @@ Each type contains the following common data::
* decl_tag and type_tag
*/
__u32 info;
- /* "size" is used by INT, ENUM, STRUCT, UNION and ENUM64.
+ /* "size" is used by INT, ENUM, STRUCT, UNION, ENUM64 and
+ * LOC_PARAM.
* "size" tells the size of the type it is describing.
*
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
- * FUNC, FUNC_PROTO, DECL_TAG and TYPE_TAG.
+ * FUNC, FUNC_PROTO, DECL_TAG and TYPE_TAG. It is unused by
+ * LOC_PROTO and LOCSEC.
* "type" is a type_id referring to another type.
*/
union {
@@ -563,6 +568,80 @@ The ``btf_enum64`` encoding:
If the original enum value is signed and the size is less than 8,
that value will be sign extended into 8 bytes.
+2.2.20 BTF_KIND_LOC_PARAM
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``struct btf_type`` encoding requirement:
+ * ``name_off``: 0
+ * ``info.kind_flag``: 0
+ * ``info.kind``: BTF_KIND_LOC_PARAM
+ * ``info.vlen``: number of 32-bit location value words
+ * ``size``: size in bytes of the represented parameter: 1 through 16
+
+``btf_type`` is followed by a ``struct btf_loc_param`` and ``info.vlen``
+number of 32-bit value words.::
+
+ struct btf_loc_param {
+ __u32 flags;
+ __u32 values[];
+ };
+
+The ``flags`` field describes how to interpret ``values``:
+
+ * ``BTF_LOC_PARAM_CONST`` describes a constant; the value is stored in
+ low-word, high-word order when it requires 64 bits.
+ * ``BTF_LOC_PARAM_ADDR | BTF_LOC_PARAM_CONST`` describes an address offset
+ relative to the runtime base address of the kernel or module image.
+ * ``BTF_LOC_PARAM_REG`` with one word describes a register number; with two
+ words it describes a multi-register parameter.
+ * ``BTF_LOC_PARAM_REG | BTF_LOC_PARAM_OFFSET`` describes an address held in
+ a register plus an offset. Adding ``BTF_LOC_PARAM_DEREF`` dereferences
+ that address. ``BTF_LOC_PARAM_REG | BTF_LOC_PARAM_DEREF`` with one word
+ dereferences the value held in the register.
+ * ``BTF_LOC_PARAM_SIGNED`` makes a constant or offset signed. A constant's
+ signed width is ``size``. For a register-relative offset, its signed
+ width is the number of offset value words times 32 bits.
+
+2.2.21 BTF_KIND_LOC_PROTO
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``struct btf_type`` encoding requirement:
+ * ``name_off``: 0
+ * ``info.kind_flag``: 0
+ * ``info.kind``: BTF_KIND_LOC_PROTO
+ * ``info.vlen``: number of function parameter locations
+ * ``type``: 0
+
+``btf_type`` is followed by ``info.vlen`` number of ``__u32`` BTF type IDs.
+Each entry corresponds to a function parameter at an inline site. An entry is
+either 0, meaning that no location information is available, or the type ID
+of a ``BTF_KIND_LOC_PARAM``.
+
+2.2.22 BTF_KIND_LOCSEC
+~~~~~~~~~~~~~~~~~~~~~~
+
+``struct btf_type`` encoding requirement:
+ * ``name_off``: offset to a valid ELF section name
+ * ``info.kind_flag``: 0
+ * ``info.kind``: BTF_KIND_LOCSEC
+ * ``info.vlen``: number of inline sites in the section
+ * ``type``: 0
+
+``btf_type`` is followed by ``info.vlen`` number of ``struct btf_loc``.::
+
+ struct btf_loc {
+ __u32 func;
+ __u32 loc_proto;
+ __u32 offset;
+ };
+
+The ``func`` field is the non-zero type ID of the ``BTF_KIND_FUNC`` being
+described. ``loc_proto`` is the non-zero type ID of the associated
+``BTF_KIND_LOC_PROTO``. ``offset`` is the location address offset relative to
+the runtime base address of the ELF section associated with the LOCSEC.
+For example, a LOCSEC named ``inline.text`` contains records for ``.text``
+whose offsets are relative to the runtime base of that section.
+
2.3 Constant Values
-------------------
diff --git a/include/linux/btf.h b/include/linux/btf.h
index ddd0f4f32d24b..f29ed358b7a8c 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -261,6 +261,11 @@ const char *btf_type_str(const struct btf_type *t);
i < btf_type_vlen(datasec_type); \
i++, member++)
+#define for_each_loc(i, locsec_type, member) \
+ for (i = 0, member = btf_type_loc_secinfo(locsec_type); \
+ i < btf_type_vlen(locsec_type); \
+ i++, member++)
+
static inline bool btf_type_is_ptr(const struct btf_type *t)
{
return BTF_INFO_KIND(t->info) == BTF_KIND_PTR;
@@ -329,6 +334,21 @@ static inline u64 btf_enum64_value(const struct btf_enum64 *e)
return ((u64)e->val_hi32 << 32) | e->val_lo32;
}
+static inline struct btf_loc_param *btf_loc_param(const struct btf_type *t)
+{
+ return (struct btf_loc_param *)(t + 1);
+}
+
+static inline __u32 *btf_loc_proto_params(const struct btf_type *t)
+{
+ return (__u32 *)(t + 1);
+}
+
+static inline struct btf_loc *btf_type_loc_secinfo(const struct btf_type *t)
+{
+ return (struct btf_loc *)(t + 1);
+}
+
static inline bool btf_is_composite(const struct btf_type *t)
{
u16 kind = btf_kind(t);
@@ -559,7 +579,7 @@ struct btf_field_desc {
/* member struct size, or zero, if no members */
int m_sz;
/* repeated per-member offsets */
- int m_off_cnt, m_offs[1];
+ int m_off_cnt, m_offs[2];
};
struct btf_field_iter {
diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
index 618167cab4e61..11d65871d163e 100644
--- a/include/uapi/linux/btf.h
+++ b/include/uapi/linux/btf.h
@@ -54,8 +54,8 @@ struct btf_type {
* decl_tag and type_tag
*/
__u32 info;
- /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
- * "size" tells the size of the type it is describing.
+ /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC, ENUM64
+ * and LOC_PARAM. "size" tells the size of the type it is describing.
*
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
* FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG.
@@ -92,7 +92,9 @@ enum {
BTF_KIND_DECL_TAG = 17, /* Decl Tag */
BTF_KIND_TYPE_TAG = 18, /* Type Tag */
BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */
-
+ BTF_KIND_LOC_PARAM = 20, /* Location parameter information */
+ BTF_KIND_LOC_PROTO = 21, /* Location prototype for site */
+ BTF_KIND_LOCSEC = 22, /* Location section */
NR_BTF_KINDS,
BTF_KIND_MAX = NR_BTF_KINDS - 1,
};
@@ -212,4 +214,79 @@ struct btf_enum64 {
__u32 val_hi32;
};
+/*
+ * BTF_KIND_LOC_PARAM is followed by a single "struct btf_loc_param"
+ * that contains flags specifying the contents of the vlen-specified
+ * number of 4-byte values that follow.
+ */
+struct btf_loc_param {
+ __u32 flags;
+ __u32 values[];
+};
+
+/*
+ * The combination of size, vlen and flags gives us the means to interpret
+ * the following vlen-specified set of 4-byte values:
+ *
+ * - a BTF_LOC_PARAM_CONST is a constant value; combination
+ * of size, vlen and _SIGNED flag determines it. If the value requires
+ * 64 bits it is stored in {lo,hi} order.
+ * - a BTF_LOC_PARAM_ADDR|BTF_LOC_PARAM_CONST is an address that should be
+ * normalized with respect to kernel/module base address.
+ * - a BTF_LOC_PARAM_REG with vlen 1 is a simple register number;
+ * with vlen 2 it is a multi-register parameter. Register numbers are
+ * numbers derived from DW_OP_reg values, i.e. 0 is DW_OP_reg0; since
+ * DW_OP_fbreg has its own special DW_OP_value and DW_OP_regx can refer
+ * to an arbitrary register number, we reserve BTF_LOC_PARAM_FBREG for
+ * the frame base register to avoid collisions.
+ * - a _REG | OFFSET describes an address without dereferencing it.
+ * - a _REG | DEREF with vlen 1 dereferences the value in the register
+ * number specified.
+ * - a REG | DEREF | OFFSET with vlen > 1 specifies the register number
+ * in the first 4-byte value and the offset in the remainder.
+ * In the case of REG and OFFSET combinations, the OFFSET has the width
+ * of the value words while the type size describes the represented parameter,
+ * so for example a REG | DEREF | OFFSET with size 8 and vlen 2 would be
+ * an 8-byte register dereference with signed 4-byte offset, since the vlen 2
+ * values consist of a register value and the signed value.
+ */
+enum btf_loc_param_flags {
+ BTF_LOC_PARAM_SIGNED = 0x1,
+ BTF_LOC_PARAM_CONST = 0x2,
+ BTF_LOC_PARAM_ADDR = 0x4,
+ BTF_LOC_PARAM_REG = 0x8,
+ BTF_LOC_PARAM_DEREF = 0x10,
+ BTF_LOC_PARAM_OFFSET = 0x20,
+};
+
+enum {
+ BTF_LOC_PARAM_FBREG = 0xffffffff
+};
+
+/*
+ * BTF_KIND_LOC_PROTO specifies location prototypes; i.e. how locations relate
+ * to parameters; a struct btf_type of BTF_KIND_LOC_PROTO is followed by a
+ * vlen-specified number of __u32 BTF type ids which specify the associated
+ * BTF_KIND_LOC_PARAM for each function parameter associated with the
+ * location. The type should either be 0 (no location info) or point at
+ * a BTF_KIND_LOC_PARAM.
+ */
+
+/*
+ * BTF_KIND_LOCSEC consists of vlen-specified number of "struct btf_loc"
+ * containing location site-specific information for a specific ELF section;
+ * for example locations in ".text" are in a LOCSEC named "inline.text".
+ *
+ * - function (func)
+ * - location prototype type id (loc_proto)
+ * - address offset (offset) relative to the runtime base address of the
+ * ELF section associated with the LOCSEC
+ */
+
+struct btf_loc {
+ __u32 func;
+ __u32 loc_proto;
+ __u32 offset;
+};
+
#endif /* _UAPI__LINUX_BTF_H__ */
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 314ecb0e593b0..2ef28c6f14f93 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -346,6 +346,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
+ [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
+ [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
+ [BTF_KIND_LOCSEC] = "LOCSEC",
};
const char *btf_type_str(const struct btf_type *t)
@@ -518,11 +521,27 @@ static bool btf_type_is_decl_tag(const struct btf_type *t)
return BTF_INFO_KIND(t->info) == BTF_KIND_DECL_TAG;
}
+static bool btf_type_is_loc_param(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info) == BTF_KIND_LOC_PARAM;
+}
+
+static bool btf_type_is_loc_proto(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info) == BTF_KIND_LOC_PROTO;
+}
+
+static bool btf_type_is_locsec(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info) == BTF_KIND_LOCSEC;
+}
+
static bool btf_type_nosize(const struct btf_type *t)
{
return btf_type_is_void(t) || btf_type_is_fwd(t) ||
btf_type_is_func(t) || btf_type_is_func_proto(t) ||
- btf_type_is_decl_tag(t);
+ btf_type_is_decl_tag(t) || btf_type_is_loc_param(t) ||
+ btf_type_is_loc_proto(t) || btf_type_is_locsec(t);
}
static bool btf_type_nosize_or_null(const struct btf_type *t)
@@ -769,7 +788,9 @@ static bool btf_type_is_resolve_source_only(const struct btf_type *t)
{
return btf_type_is_var(t) ||
btf_type_is_decl_tag(t) ||
- btf_type_is_datasec(t);
+ btf_type_is_datasec(t) ||
+ btf_type_is_loc_proto(t) ||
+ btf_type_is_locsec(t);
}
/* What types need to be resolved?
@@ -797,7 +818,9 @@ static bool btf_type_needs_resolve(const struct btf_type *t)
btf_type_is_var(t) ||
btf_type_is_func(t) ||
btf_type_is_decl_tag(t) ||
- btf_type_is_datasec(t);
+ btf_type_is_datasec(t) ||
+ btf_type_is_loc_proto(t) ||
+ btf_type_is_locsec(t);
}
/* t->size can be used */
@@ -4730,6 +4753,293 @@ static const struct btf_kind_operations enum64_ops = {
.show = btf_enum64_show,
};
+static s32 btf_loc_param_check_meta(struct btf_verifier_env *env,
+ const struct btf_type *t,
+ u32 meta_left)
+{
+ const struct btf_loc_param *p = btf_loc_param(t);
+ u32 size, meta_needed, vlen = btf_vlen(t);
+
+ meta_needed = sizeof(*p) + sizeof(__u32) * vlen;
+ if (meta_left < meta_needed) {
+ btf_verifier_log_basic(env, t,
+ "meta_left:%u meta_needed:%u",
+ meta_left, meta_needed);
+ return -EINVAL;
+ }
+
+ if (t->name_off) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+ size = t->size;
+ if (!size || size > 16) {
+ btf_verifier_log_type(env, t, "Unexpected size");
+ return -EINVAL;
+ }
+
+ if (btf_type_kflag(t)) {
+ btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+ return -EINVAL;
+ }
+
+ btf_verifier_log_type(env, t, NULL);
+
+ return meta_needed;
+}
+
+static void btf_loc_param_log(struct btf_verifier_env *env,
+ const struct btf_type *t)
+{
+ const struct btf_loc_param *p = btf_loc_param(t);
+ u32 i, vlen = btf_vlen(t);
+
+ btf_verifier_log(env, "size=%u vlen=%u flags=0x%x", t->size, vlen, p->flags);
+ for (i = 0; i < vlen; i++)
+ btf_verifier_log(env, ", %u", p->values[i]);
+}
+
+static const struct btf_kind_operations loc_param_ops = {
+ .check_meta = btf_loc_param_check_meta,
+ .resolve = btf_df_resolve,
+ .check_member = btf_df_check_member,
+ .check_kflag_member = btf_df_check_kflag_member,
+ .log_details = btf_loc_param_log,
+ .show = btf_df_show,
+};
+
+static s32 btf_loc_proto_check_meta(struct btf_verifier_env *env,
+ const struct btf_type *t,
+ u32 meta_left)
+{
+ u32 meta_needed;
+
+ meta_needed = sizeof(__u32) * btf_type_vlen(t);
+
+ if (meta_left < meta_needed) {
+ btf_verifier_log_basic(env, t,
+ "meta_left:%u meta_needed:%u",
+ meta_left, meta_needed);
+ return -EINVAL;
+ }
+
+ if (t->name_off) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+
+ if (btf_type_kflag(t)) {
+ btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+ return -EINVAL;
+ }
+
+ btf_verifier_log_type(env, t, NULL);
+
+ return meta_needed;
+}
+
+static void btf_loc_proto_log(struct btf_verifier_env *env,
+ const struct btf_type *t)
+{
+ const __u32 *params = btf_loc_proto_params(t);
+ u32 i, nr_params = btf_type_vlen(t);
+
+ btf_verifier_log(env, "vlen=%u", nr_params);
+ for (i = 0; i < nr_params; i++, params++)
+ btf_verifier_log(env, ", %u", *params);
+}
+
+static int btf_loc_proto_resolve(struct btf_verifier_env *env,
+ const struct resolve_vertex *v)
+{
+ const struct btf_type *t = v->t;
+ const __u32 *params = btf_loc_proto_params(t);
+ u32 i, nr_params = btf_type_vlen(t);
+ struct btf *btf = env->btf;
+
+ if (t->type) {
+ btf_verifier_log_type(env, t, "Invalid loc_proto type");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nr_params; i++) {
+ const struct btf_type *param_type;
+ u32 param_type_id = params[i];
+
+ if (!param_type_id)
+ continue;
+
+ param_type = btf_type_by_id(btf, param_type_id);
+ if (!param_type || !btf_type_is_loc_param(param_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid loc_param#%u", i + 1);
+ return -EINVAL;
+ }
+ }
+
+ env_stack_pop_resolved(env, 0, 0);
+ return 0;
+}
+
+static const struct btf_kind_operations loc_proto_ops = {
+ .check_meta = btf_loc_proto_check_meta,
+ .resolve = btf_loc_proto_resolve,
+ .check_member = btf_df_check_member,
+ .check_kflag_member = btf_df_check_kflag_member,
+ .log_details = btf_loc_proto_log,
+ .show = btf_df_show,
+};
+
+__printf(4, 5)
+static void btf_verifier_log_loc(struct btf_verifier_env *env,
+ const struct btf_type *locsec_type,
+ const struct btf_loc *loc,
+ const char *fmt, ...)
+{
+ struct bpf_verifier_log *log = &env->log;
+ va_list args;
+
+ if (!bpf_verifier_log_needed(log))
+ return;
+ if (log->level == BPF_LOG_KERNEL && !fmt)
+ return;
+ if (env->phase != CHECK_META)
+ btf_verifier_log_type(env, locsec_type, NULL);
+
+ __btf_verifier_log(log, "\t func=%u loc_proto=%u offset=%u",
+ loc->func, loc->loc_proto, loc->offset);
+ if (fmt && *fmt) {
+ __btf_verifier_log(log, " ");
+ va_start(args, fmt);
+ bpf_verifier_vlog(log, fmt, args);
+ va_end(args);
+ }
+
+ __btf_verifier_log(log, "\n");
+}
+
+static s32 btf_locsec_check_meta(struct btf_verifier_env *env,
+ const struct btf_type *t,
+ u32 meta_left)
+{
+ u32 i, meta_needed, vlen = btf_type_vlen(t);
+ const struct btf_loc *loc;
+
+ meta_needed = sizeof(struct btf_loc) * vlen;
+
+ if (meta_left < meta_needed) {
+ btf_verifier_log_basic(env, t,
+ "meta_left:%u meta_needed:%u",
+ meta_left, meta_needed);
+ return -EINVAL;
+ }
+
+ if (btf_type_kflag(t)) {
+ btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+ return -EINVAL;
+ }
+
+ if (!t->name_off ||
+ !btf_name_valid_section(env->btf, t->name_off)) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+
+ for_each_loc(i, t, loc) {
+ /* A loc func, loc proto cannot be in type void */
+ if (!loc->func || !BTF_TYPE_ID_VALID(loc->func)) {
+ btf_verifier_log_loc(env, t, loc, "Invalid func");
+ return -EINVAL;
+ }
+ if (!loc->loc_proto || !BTF_TYPE_ID_VALID(loc->loc_proto)) {
+ btf_verifier_log_loc(env, t, loc, "Invalid loc_proto");
+ return -EINVAL;
+ }
+ btf_verifier_log_loc(env, t, loc, NULL);
+ }
+
+ return meta_needed;
+}
+
+static void btf_locsec_log(struct btf_verifier_env *env,
+ const struct btf_type *t)
+{
+ btf_verifier_log(env, "vlen=%u", btf_type_vlen(t));
+}
+
+static int btf_locsec_resolve(struct btf_verifier_env *env,
+ const struct resolve_vertex *v)
+{
+ const struct btf_type *t = v->t;
+ const struct btf_loc *loc;
+ struct btf *btf = env->btf;
+ u32 i;
+
+ if (t->type) {
+ btf_verifier_log_type(env, t, "Invalid locsec type");
+ return -EINVAL;
+ }
+
+ env->resolve_mode = RESOLVE_TBD;
+ for (i = v->next_member, loc = btf_type_loc_secinfo(t) + i;
+ i < btf_type_vlen(t); i++, loc++) {
+ const struct btf_type *func_type, *func_proto_type;
+ const struct btf_type *loc_proto_type;
+ u32 func_type_id = loc->func;
+ u32 loc_proto_type_id = loc->loc_proto;
+ u32 proto_vlen;
+
+ func_type = btf_type_by_id(btf, func_type_id);
+ if (!func_type || !btf_type_is_func(func_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid func#%u", i + 1);
+ return -EINVAL;
+ }
+ func_proto_type = btf_type_by_id(btf, func_type->type);
+ if (!func_proto_type || !btf_type_is_func_proto(func_proto_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid func#%u", i + 1);
+ return -EINVAL;
+ }
+ proto_vlen = btf_vlen(func_proto_type);
+
+ if (!env_type_is_resolved(env, func_type_id)) {
+ env_stack_set_next_member(env, i);
+ return env_stack_push(env, func_type, func_type_id);
+ }
+
+ loc_proto_type = btf_type_by_id(btf, loc_proto_type_id);
+ if (!loc_proto_type || !btf_type_is_loc_proto(loc_proto_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid loc_proto#%u", i + 1);
+ return -EINVAL;
+ }
+ if (proto_vlen != btf_vlen(loc_proto_type)) {
+ btf_verifier_log_type(env, t,
+ "Mismatched vlen for loc_proto#%u", i + 1);
+ return -EINVAL;
+ }
+
+ if (!env_type_is_resolved(env, loc_proto_type_id)) {
+ env_stack_set_next_member(env, i + 1);
+ return env_stack_push(env, loc_proto_type,
+ loc_proto_type_id);
+ }
+ }
+
+ env_stack_pop_resolved(env, 0, 0);
+ return 0;
+}
+
+static const struct btf_kind_operations locsec_ops = {
+ .check_meta = btf_locsec_check_meta,
+ .resolve = btf_locsec_resolve,
+ .check_member = btf_df_check_member,
+ .check_kflag_member = btf_df_check_kflag_member,
+ .log_details = btf_locsec_log,
+ .show = btf_df_show,
+};
+
static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,
const struct btf_type *t,
u32 meta_left)
@@ -5399,6 +5709,9 @@ static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = &decl_tag_ops,
[BTF_KIND_TYPE_TAG] = &modifier_ops,
[BTF_KIND_ENUM64] = &enum64_ops,
+ [BTF_KIND_LOC_PARAM] = &loc_param_ops,
+ [BTF_KIND_LOC_PROTO] = &loc_proto_ops,
+ [BTF_KIND_LOCSEC] = &locsec_ops,
};
static s32 btf_check_meta(struct btf_verifier_env *env,
@@ -5473,7 +5786,8 @@ static bool btf_resolve_valid(struct btf_verifier_env *env,
if (!env_type_is_resolved(env, type_id))
return false;
- if (btf_type_is_struct(t) || btf_type_is_datasec(t))
+ if (btf_type_is_struct(t) || btf_type_is_datasec(t) ||
+ btf_type_is_loc_proto(t) || btf_type_is_locsec(t))
return !btf_resolved_type_id(btf, type_id) &&
!btf_resolved_type_size(btf, type_id);
diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
index cf75a7fa2d6bc..c6dc445cd4a19 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
@@ -16,7 +16,7 @@ SYNOPSIS
**bpftool** [*OPTIONS*] **btf** *COMMAND*
-*OPTIONS* := { |COMMON_OPTIONS| | { **-B** | **--base-btf** } }
+*OPTIONS* := { |COMMON_OPTIONS| | [{ **-B** | **--base-btf**} *FILE*]... }
*COMMANDS* := { **dump** | **help** }
@@ -87,7 +87,10 @@ OPTIONS
objects for kernel modules. To avoid duplicating all kernel symbols
required by modules, BTF objects for modules are "split", they are
built incrementally on top of the kernel (vmlinux) BTF object. So the
- base BTF reference should usually point to the kernel BTF.
+ base BTF reference should usually point to the kernel BTF. Multiple
+ base BTF objects can be passed, where the first is assumed to be the
+ root BTF, followed by split BTF based upon it, followed by split
+ BTF based upon the first split BTF and so on.
When the main BTF object to process (for example, the module BTF to
dump) is passed as a *FILE*, bpftool attempts to autodetect the path
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index bca0a3982f09b..e29c8a84e224c 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -29,6 +29,7 @@
#define MAX_ROOT_IDS 16
#define MAX_BTF_FILES 64
+#define MAX_LOC_PARAM_WORDS 8
static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_UNKN] = "UNKNOWN",
@@ -51,6 +52,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
+ [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
+ [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
+ [BTF_KIND_LOCSEC] = "LOCSEC",
};
struct sort_datum {
@@ -117,6 +121,137 @@ static int btf_kind_safe(int kind)
return kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;
}
+static void btf_loc_param_reg_str(__u32 reg, char *str, size_t sz)
+{
+ if (reg == BTF_LOC_PARAM_FBREG)
+ snprintf(str, sz, "fbreg");
+ else
+ snprintf(str, sz, "reg%u", reg);
+}
+
+static void btf_loc_param_raw_str(const struct btf_loc_param *p, __u32 vlen,
+ char *str, size_t sz)
+{
+ __u32 i, nr_words = min(vlen, (__u32)MAX_LOC_PARAM_WORDS);
+ size_t off = 0;
+
+ if (!sz)
+ return;
+
+ off += snprintf(str + off, sz - off, "raw=[");
+ for (i = 0; i < nr_words && off < sz; i++)
+ off += snprintf(str + off, sz - off, "%s0x%08x",
+ i ? ", " : "", p->values[i]);
+ if (vlen > nr_words && off < sz)
+ off += snprintf(str + off, sz - off, ", ...");
+ if (off < sz)
+ snprintf(str + off, sz - off, "]");
+}
+
+static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz)
+{
+ const struct btf_loc_param *p;
+ __u32 i = 0, vlen;
+ __u64 value;
+ __u32 value_size;
+ bool negative = false;
+ char regs[32] = {};
+ char num[32] = {};
+ const char *op = "";
+
+ if (!t || !btf_is_loc_param(t)) {
+ snprintf(str, sz, "<invalid>");
+ return;
+ }
+
+ p = btf_loc_param(t);
+ vlen = btf_vlen(t);
+
+ if (p->flags & BTF_LOC_PARAM_REG) {
+ __u32 nregs = (p->flags == BTF_LOC_PARAM_REG) ? vlen : 1;
+
+ if (nregs > vlen) {
+ btf_loc_param_raw_str(p, vlen, str, sz);
+ return;
+ }
+
+ switch (nregs) {
+ case 2:
+ btf_loc_param_reg_str(p->values[0], regs, sizeof(regs));
+ snprintf(regs + strlen(regs), sizeof(regs) - strlen(regs),
+ ", ");
+ btf_loc_param_reg_str(p->values[1],
+ regs + strlen(regs),
+ sizeof(regs) - strlen(regs));
+ break;
+ case 1:
+ btf_loc_param_reg_str(p->values[0], regs, sizeof(regs));
+ break;
+ default:
+ btf_loc_param_raw_str(p, vlen, str, sz);
+ return;
+ }
+ i += nregs;
+ }
+ if (p->flags & (BTF_LOC_PARAM_CONST|BTF_LOC_PARAM_OFFSET)) {
+ switch (vlen - i) {
+ case 1:
+ value_size = sizeof(p->values[0]);
+ value = p->values[i];
+ break;
+ case 2:
+ value_size = 2 * sizeof(p->values[0]);
+ value = ((__u64)p->values[i + 1] << 32) | p->values[i];
+ break;
+ default:
+ btf_loc_param_raw_str(p, vlen, str, sz);
+ return;
+ }
+ i = vlen;
+ if (p->flags & BTF_LOC_PARAM_SIGNED) {
+ /*
+ * size describes the represented parameter, so it
+ * describes a constant's signed width. An offset
+ * is determined by its value words after the register
+ * number.
+ */
+ __u32 size = p->flags & BTF_LOC_PARAM_OFFSET ?
+ value_size : t->size;
+ __u32 bits = size * 8;
+
+ /*
+ * Since we represent constant values in hex, we
+ * need to determine if the value is negative so
+ * we can prepend a "-", and also fix the value
+ * to be positive so we can have - 0x<value>.
+ */
+ if (size && size <= sizeof(value)) {
+ if (size < sizeof(value))
+ value &= (1ULL << bits) - 1;
+ negative = value & (1ULL << (bits - 1));
+ if (negative)
+ value = size == sizeof(value) ? -value :
+ (1ULL << bits) - value;
+ }
+ }
+ snprintf(num, sizeof(num), "0x%llx%s", (unsigned long long)value,
+ p->flags & BTF_LOC_PARAM_ADDR ? " (addr)" : "");
+ }
+ if (i != vlen) {
+ btf_loc_param_raw_str(p, vlen, str, sz);
+ return;
+ }
+ if (num[0])
+ op = regs[0] ? (negative ? " - " : " + ") : negative ? "-" : "";
+
+ snprintf(str, sz, "%s%s%s%s%s",
+ p->flags & BTF_LOC_PARAM_DEREF ? "*(" : "",
+ regs,
+ op,
+ num,
+ p->flags & BTF_LOC_PARAM_DEREF ? ")" : "");
+}
+
static int dump_btf_type(const struct btf *btf, __u32 id,
const struct btf_type *t)
{
@@ -415,6 +550,95 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
}
break;
}
+ case BTF_KIND_LOC_PARAM: {
+ const struct btf_loc_param *p = btf_loc_param(t);
+ __u32 vlen = btf_vlen(t);
+ char param_str[256] = {};
+
+ btf_loc_param_str(t, param_str, sizeof(param_str));
+
+ if (json_output) {
+ jsonw_uint_field(w, "size", t->size);
+ jsonw_uint_field(w, "flags", p->flags);
+ jsonw_uint_field(w, "vlen", vlen);
+ jsonw_string_field(w, "values", param_str);
+ } else {
+ printf(" size=%u flags=0x%x vlen=%u values='%s'",
+ t->size, p->flags, vlen, param_str);
+ }
+ break;
+ }
+ case BTF_KIND_LOC_PROTO: {
+ __u32 *params = btf_loc_proto_params(t);
+ __u32 i, vlen = btf_vlen(t);
+
+ if (json_output) {
+ jsonw_uint_field(w, "vlen", vlen);
+ jsonw_name(w, "params");
+ jsonw_start_array(w);
+ } else {
+ printf(" vlen=%u", vlen);
+ }
+
+ for (i = 0; i < vlen; i++, params++) {
+ const struct btf_type *p;
+ char param_str[256] = {};
+
+ if (*params) {
+ p = btf__type_by_id(btf, *params);
+ btf_loc_param_str(p, param_str, sizeof(param_str));
+ } else {
+ snprintf(param_str, sizeof(param_str), "<unavailable>");
+ }
+ if (json_output) {
+ jsonw_start_object(w);
+ jsonw_uint_field(w, "type_id", *params);
+ jsonw_string_field(w, "value", param_str);
+ jsonw_end_object(w);
+ } else {
+ printf("\n\ttype_id=%u value='%s'", *params, param_str);
+ }
+ }
+ if (json_output)
+ jsonw_end_array(w);
+ break;
+ }
+
+ case BTF_KIND_LOCSEC: {
+ struct btf_loc *locs = btf_locsec_locs(t);
+ __u32 i, vlen = btf_vlen(t);
+
+ if (json_output) {
+ jsonw_uint_field(w, "vlen", vlen);
+ jsonw_name(w, "locs");
+ jsonw_start_array(w);
+ } else {
+ printf(" vlen=%u", vlen);
+ }
+
+ for (i = 0; i < vlen; i++, locs++) {
+ const struct btf_type *f = btf__type_by_id(btf, locs->func);
+ const char *name = "<invalid>";
+
+ if (f && btf_is_func(f))
+ name = btf_str(btf, f->name_off);
+
+ if (json_output) {
+ jsonw_start_object(w);
+ jsonw_uint_field(w, "func_type_id", locs->func);
+ jsonw_string_field(w, "name", name);
+ jsonw_uint_field(w, "loc_proto_type_id", locs->loc_proto);
+ jsonw_uint_field(w, "offset", locs->offset);
+ jsonw_end_object(w);
+ } else {
+ printf("\n\tname='%s' func_type_id=%u loc_proto_type_id=%u offset=%u",
+ name, locs->func, locs->loc_proto, locs->offset);
+ }
+ }
+ if (json_output)
+ jsonw_end_array(w);
+ break;
+ }
default:
break;
}
@@ -1545,7 +1769,7 @@ static int do_help(int argc, char **argv)
" " HELP_SPEC_MAP "\n"
" " HELP_SPEC_PROGRAM "\n"
" " HELP_SPEC_OPTIONS " |\n"
- " {-B|--base-btf} }\n"
+ " {[{-B|--base-btf} FILE]... }}\n"
"",
bin_name, "btf");
diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
index 5ababd8f7d0a0..83884b21e7081 100644
--- a/tools/bpf/bpftool/main.c
+++ b/tools/bpf/bpftool/main.c
@@ -466,6 +466,7 @@ int main(int argc, char **argv)
{ "base-btf", required_argument, NULL, 'B' },
{ 0 }
};
+ struct btf *new_base_btf = NULL, *root_base_btf = NULL;
bool version_requested = false;
int opt, ret;
@@ -515,12 +516,16 @@ int main(int argc, char **argv)
verifier_logs = true;
break;
case 'B':
- base_btf = btf__parse(optarg, NULL);
- if (!base_btf) {
+ /* handle multi-split BTF */
+ new_base_btf = btf__parse_split(optarg, base_btf);
+ if (!new_base_btf) {
p_err("failed to parse base BTF at '%s': %d\n",
optarg, -errno);
return -1;
}
+ base_btf = new_base_btf;
+ if (!root_base_btf)
+ root_base_btf = base_btf;
break;
case 'L':
use_loader = true;
@@ -567,7 +572,20 @@ int main(int argc, char **argv)
if (json_output)
jsonw_destroy(&json_wtr);
- btf__free(base_btf);
+ while (base_btf) {
+ bool is_root = base_btf == root_base_btf;
+
+ new_base_btf = (struct btf *)btf__base_btf(base_btf);
+ btf__free(base_btf);
+ /*
+ * Do not free base BTF that is an owned .BTF.base ; leads
+ * to a double-free, so only free as far as the root base
+ * we explicitly read with -B above.
+ */
+ if (is_root)
+ break;
+ base_btf = new_base_btf;
+ }
return ret;
}
diff --git a/tools/include/uapi/linux/btf.h b/tools/include/uapi/linux/btf.h
index 618167cab4e61..11d65871d163e 100644
--- a/tools/include/uapi/linux/btf.h
+++ b/tools/include/uapi/linux/btf.h
@@ -54,8 +54,8 @@ struct btf_type {
* decl_tag and type_tag
*/
__u32 info;
- /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
- * "size" tells the size of the type it is describing.
+ /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC, ENUM64
+ * and LOC_PARAM. "size" tells the size of the type it is describing.
*
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
* FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG.
@@ -92,7 +92,9 @@ enum {
BTF_KIND_DECL_TAG = 17, /* Decl Tag */
BTF_KIND_TYPE_TAG = 18, /* Type Tag */
BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */
-
+ BTF_KIND_LOC_PARAM = 20, /* Location parameter information */
+ BTF_KIND_LOC_PROTO = 21, /* Location prototype for site */
+ BTF_KIND_LOCSEC = 22, /* Location section */
NR_BTF_KINDS,
BTF_KIND_MAX = NR_BTF_KINDS - 1,
};
@@ -212,4 +214,79 @@ struct btf_enum64 {
__u32 val_hi32;
};
+/*
+ * BTF_KIND_LOC_PARAM is followed by a single "struct btf_loc_param"
+ * that contains flags specifying the contents of the vlen-specified
+ * number of 4-byte values that follow.
+ */
+struct btf_loc_param {
+ __u32 flags;
+ __u32 values[];
+};
+
+/*
+ * The combination of size, vlen and flags gives us the means to interpret
+ * the following vlen-specified set of 4-byte values:
+ *
+ * - a BTF_LOC_PARAM_CONST is a constant value; combination
+ * of size, vlen and _SIGNED flag determines it. If the value requires
+ * 64 bits it is stored in {lo,hi} order.
+ * - a BTF_LOC_PARAM_ADDR|BTF_LOC_PARAM_CONST is an address that should be
+ * normalized with respect to kernel/module base address.
+ * - a BTF_LOC_PARAM_REG with vlen 1 is a simple register number;
+ * with vlen 2 it is a multi-register parameter. Register numbers are
+ * numbers derived from DW_OP_reg values, i.e. 0 is DW_OP_reg0; since
+ * DW_OP_fbreg has its own special DW_OP_value and DW_OP_regx can refer
+ * to an arbitrary register number, we reserve BTF_LOC_PARAM_FBREG for
+ * the frame base register to avoid collisions.
+ * - a _REG | OFFSET describes an address without dereferencing it.
+ * - a _REG | DEREF with vlen 1 dereferences the value in the register
+ * number specified.
+ * - a REG | DEREF | OFFSET with vlen > 1 specifies the register number
+ * in the first 4-byte value and the offset in the remainder.
+ * In the case of REG and OFFSET combinations, the OFFSET has the width
+ * of the value words while the type size describes the represented parameter,
+ * so for example a REG | DEREF | OFFSET with size 8 and vlen 2 would be
+ * an 8-byte register dereference with signed 4-byte offset, since the vlen 2
+ * values consist of a register value and the signed value.
+ */
+enum btf_loc_param_flags {
+ BTF_LOC_PARAM_SIGNED = 0x1,
+ BTF_LOC_PARAM_CONST = 0x2,
+ BTF_LOC_PARAM_ADDR = 0x4,
+ BTF_LOC_PARAM_REG = 0x8,
+ BTF_LOC_PARAM_DEREF = 0x10,
+ BTF_LOC_PARAM_OFFSET = 0x20,
+};
+
+enum {
+ BTF_LOC_PARAM_FBREG = 0xffffffff
+};
+
+/*
+ * BTF_KIND_LOC_PROTO specifies location prototypes; i.e. how locations relate
+ * to parameters; a struct btf_type of BTF_KIND_LOC_PROTO is followed by a
+ * vlen-specified number of __u32 BTF type ids which specify the associated
+ * BTF_KIND_LOC_PARAM for each function parameter associated with the
+ * location. The type should either be 0 (no location info) or point at
+ * a BTF_KIND_LOC_PARAM.
+ */
+
+/*
+ * BTF_KIND_LOCSEC consists of vlen-specified number of "struct btf_loc"
+ * containing location site-specific information for a specific ELF section;
+ * for example locations in ".text" are in a LOCSEC named "inline.text".
+ *
+ * - function (func)
+ * - location prototype type id (loc_proto)
+ * - address offset (offset) relative to the runtime base address of the
+ * ELF section associated with the LOCSEC
+ */
+
+struct btf_loc {
+ __u32 func;
+ __u32 loc_proto;
+ __u32 offset;
+};
+
#endif /* _UAPI__LINUX_BTF_H__ */
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 908bd344229d5..512b3962f75f8 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -57,6 +57,9 @@ static struct btf_layout layouts[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = { sizeof(struct btf_decl_tag), 0, 0 },
[BTF_KIND_TYPE_TAG] = { 0, 0, 0 },
[BTF_KIND_ENUM64] = { 0, sizeof(struct btf_enum64), 0 },
+[BTF_KIND_LOC_PARAM] = { sizeof(struct btf_loc_param), sizeof(__u32), 0 },
+[BTF_KIND_LOC_PROTO] = { 0, sizeof(__u32), 0 },
+[BTF_KIND_LOCSEC] = { 0, sizeof(struct btf_loc), 0 },
};
struct btf {
@@ -486,6 +489,12 @@ static int btf_type_size(const struct btf *btf, const struct btf_type *t)
return base_size + vlen * sizeof(struct btf_var_secinfo);
case BTF_KIND_DECL_TAG:
return base_size + sizeof(struct btf_decl_tag);
+ case BTF_KIND_LOC_PARAM:
+ return base_size + sizeof(struct btf_loc_param) + vlen * sizeof(__u32);
+ case BTF_KIND_LOC_PROTO:
+ return base_size + vlen * sizeof(__u32);
+ case BTF_KIND_LOCSEC:
+ return base_size + vlen * sizeof(struct btf_loc);
default:
return btf_type_size_unknown(btf, t);
}
@@ -501,12 +510,15 @@ static void btf_bswap_type_base(struct btf_type *t)
static int btf_bswap_type_rest(struct btf_type *t)
{
struct btf_var_secinfo *v;
+ struct btf_loc_param *lp;
struct btf_enum64 *e64;
struct btf_member *m;
struct btf_array *a;
struct btf_param *p;
struct btf_enum *e;
+ struct btf_loc *l;
__u32 vlen = btf_vlen(t);
+ __u32 *d;
int i;
switch (btf_kind(t)) {
@@ -569,6 +581,23 @@ static int btf_bswap_type_rest(struct btf_type *t)
case BTF_KIND_DECL_TAG:
btf_decl_tag(t)->component_idx = bswap_32(btf_decl_tag(t)->component_idx);
return 0;
+ case BTF_KIND_LOC_PARAM:
+ lp = btf_loc_param(t);
+ lp->flags = bswap_32(lp->flags);
+ for (i = 0, d = (__u32 *)(lp + 1); i < vlen; i++, d++)
+ *d = bswap_32(*d);
+ return 0;
+ case BTF_KIND_LOC_PROTO:
+ for (i = 0, d = btf_loc_proto_params(t); i < vlen; i++, d++)
+ *d = bswap_32(*d);
+ return 0;
+ case BTF_KIND_LOCSEC:
+ for (i = 0, l = btf_locsec_locs(t); i < vlen; i++, l++) {
+ l->func = bswap_32(l->func);
+ l->loc_proto = bswap_32(l->loc_proto);
+ l->offset = bswap_32(l->offset);
+ }
+ return 0;
default:
pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t));
return -EINVAL;
@@ -745,6 +774,33 @@ static int btf_validate_type(const struct btf *btf, const struct btf_type *t, __
}
break;
}
+ case BTF_KIND_LOC_PARAM:
+ break;
+ case BTF_KIND_LOC_PROTO: {
+ __u32 *p = btf_loc_proto_params(t);
+
+ n = btf_vlen(t);
+ for (i = 0; i < n; i++, p++) {
+ err = btf_validate_id(btf, *p, id);
+ if (err)
+ return err;
+ }
+ break;
+ }
+ case BTF_KIND_LOCSEC: {
+ const struct btf_loc *l = btf_locsec_locs(t);
+
+ n = btf_vlen(t);
+ for (i = 0; i < n; i++, l++) {
+ if (!err)
+ err = btf_validate_id(btf, l->func, id);
+ if (!err)
+ err = btf_validate_id(btf, l->loc_proto, id);
+ if (err)
+ return err;
+ }
+ break;
+ }
default:
/* Kind may be represented in kind layout information. */
if (btf_type_size_unknown(btf, t) < 0) {
@@ -3344,6 +3400,243 @@ int btf__add_decl_attr(struct btf *btf, const char *value, int ref_type_id,
return btf_add_decl_tag(btf, value, ref_type_id, component_idx, 1);
}
+/*
+ * Append new BTF_KIND_LOC_PARAM with specified size and flags. Values are
+ * added via btf__add_loc_param_value().
+ *
+ * - *byte_sz* - size of the type, in bytes (1 through 16);
+ * - *flags* - combination of enum btf_loc_param_flags values
+ *
+ * Returns:
+ * - >0, type ID of newly added BTF type;
+ * - <0, on error.
+ */
+int btf__add_loc_param(struct btf *btf, __u32 byte_sz, __u32 flags)
+{
+ struct btf_loc_param *p;
+ struct btf_type *t;
+ int sz, err;
+
+ if (!byte_sz || byte_sz > 16)
+ return libbpf_err(-EINVAL);
+
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ sz = sizeof(struct btf_type) + sizeof(*p);
+ t = btf_add_type_mem(btf, sz);
+ if (!t)
+ return libbpf_err(-ENOMEM);
+
+ t->name_off = 0;
+ t->info = btf_type_info(BTF_KIND_LOC_PARAM, 0, 0);
+ t->size = byte_sz;
+
+ p = btf_loc_param(t);
+ p->flags = flags;
+
+ return btf_commit_type(btf, sz);
+}
+
+/*
+ * Append *value* to existing BTF_KIND_LOC_PARAM.
+ *
+ * Returns:
+ * - 0 on success
+ * - <0, on error.
+ */
+int btf__add_loc_param_value(struct btf *btf, __u32 value)
+{
+ struct btf_type *t;
+ int sz, err;
+ __u32 *v;
+
+ /* last type should be BTF_KIND_LOC_PARAM */
+ if (btf->nr_types == 0)
+ return libbpf_err(-EINVAL);
+ t = btf_last_type(btf);
+ if (!btf_is_loc_param(t))
+ return libbpf_err(-EINVAL);
+
+ /* decompose and invalidate raw data */
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ sz = sizeof(value);
+ v = btf_add_type_mem(btf, sz);
+ if (!v)
+ return libbpf_err(-ENOMEM);
+ *v = value;
+
+ /* update parent type's vlen */
+ t = btf_last_type(btf);
+ err = btf_type_inc_vlen(t);
+ if (err)
+ return libbpf_err(err);
+
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
+ return 0;
+}
+
+/*
+ * Append new BTF_KIND_LOC_PROTO
+ *
+ * The prototype is then populated with 0 or more BTF_KIND_LOC_PARAMs via
+ * btf__add_loc_proto_param(); similar to how btf__add_func_param() adds
+ * parameters to a FUNC_PROTO.
+ *
+ * Returns:
+ * - >0, type ID of newly added BTF type;
+ * - <0, on error.
+ */
+int btf__add_loc_proto(struct btf *btf)
+{
+ struct btf_type *t;
+ int err;
+
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ t = btf_add_type_mem(btf, sizeof(struct btf_type));
+ if (!t)
+ return libbpf_err(-ENOMEM);
+
+ t->name_off = 0;
+ t->info = btf_type_info(BTF_KIND_LOC_PROTO, 0, 0);
+ t->size = 0;
+
+ return btf_commit_type(btf, sizeof(struct btf_type));
+}
+
+/*
+ * Append a BTF_KIND_LOC_PARAM id/0 to BTF_KIND_LOC_PROTO.
+ *
+ * Returns:
+ * - 0 on success;
+ * - <0, on error.
+
+ */
+int btf__add_loc_proto_param(struct btf *btf, __u32 id)
+{
+ struct btf_type *t;
+ int sz, err;
+ __u32 *p;
+
+ if (validate_type_id(id))
+ return libbpf_err(-EINVAL);
+
+ /* last type should be BTF_KIND_LOC_PROTO */
+ if (btf->nr_types == 0)
+ return libbpf_err(-EINVAL);
+ t = btf_last_type(btf);
+ if (!btf_is_loc_proto(t))
+ return libbpf_err(-EINVAL);
+
+ /* decompose and invalidate raw data */
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ sz = sizeof(__u32);
+ p = btf_add_type_mem(btf, sz);
+ if (!p)
+ return libbpf_err(-ENOMEM);
+ *p = id;
+
+ /* update parent type's vlen */
+ t = btf_last_type(btf);
+ err = btf_type_inc_vlen(t);
+ if (err)
+ return libbpf_err(err);
+
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
+ return 0;
+}
+
+/*
+ * Append new BTF_KIND_LOCSEC type with:
+ * - *name* - non-empty/non-NULL name;
+ *
+ * Location section is initially empty. Location info can be added with
+ * btf__add_locsec_loc() calls, after btf__add_locsec() succeeds.
+ *
+ * Returns:
+ * - >0, type ID of newly added BTF type;
+ * - <0, on error.
+ */
+
+int btf__add_locsec(struct btf *btf, const char *name)
+{
+ struct btf_type *t;
+ int name_off = 0;
+ int err;
+
+ /* non-empty name */
+ if (str_is_empty(name))
+ return libbpf_err(-EINVAL);
+
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ t = btf_add_type_mem(btf, sizeof(struct btf_type));
+ if (!t)
+ return libbpf_err(-ENOMEM);
+
+ name_off = btf__add_str(btf, name);
+ if (name_off < 0)
+ return libbpf_err(name_off);
+ t->name_off = name_off;
+ t->info = btf_type_info(BTF_KIND_LOCSEC, 0, 0);
+ t->size = 0;
+
+ return btf_commit_type(btf, sizeof(struct btf_type));
+}
+
+int btf__add_locsec_loc(struct btf *btf, __u32 func, __u32 loc_proto,
+ __u32 offset)
+{
+ struct btf_type *t;
+ struct btf_loc *l;
+ int sz, err;
+
+ if (validate_type_id(func) || validate_type_id(loc_proto))
+ return libbpf_err(-EINVAL);
+
+ /* last type should be BTF_KIND_LOCSEC */
+ if (btf->nr_types == 0)
+ return libbpf_err(-EINVAL);
+ t = btf_last_type(btf);
+ if (!btf_is_locsec(t))
+ return libbpf_err(-EINVAL);
+
+ /* decompose and invalidate raw data */
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ sz = sizeof(*l);
+ l = btf_add_type_mem(btf, sz);
+ if (!l)
+ return libbpf_err(-ENOMEM);
+
+ l->func = func;
+ l->loc_proto = loc_proto;
+ l->offset = offset;
+
+ /* update parent type's vlen */
+ t = btf_last_type(btf);
+ err = btf_type_inc_vlen(t);
+ if (err)
+ return libbpf_err(err);
+
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
+ return 0;
+}
+
struct btf_ext_sec_info_param {
__u32 off;
__u32 len;
@@ -4114,8 +4407,8 @@ static struct btf_dedup *btf_dedup_new(struct btf *btf, const struct btf_dedup_o
for (i = 1; i < type_cnt; i++) {
struct btf_type *t = btf_type_by_id(d->btf, i);
- /* VAR and DATASEC are never deduped and are self-canonical */
- if (btf_is_var(t) || btf_is_datasec(t))
+ /* VAR, DATASEC and LOCSEC are never deduped and are self-canonical */
+ if (btf_is_var(t) || btf_is_datasec(t) || btf_is_locsec(t))
d->map[i] = i;
else
d->map[i] = BTF_UNPROCESSED_ID;
@@ -4395,6 +4688,47 @@ static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2)
btf_is_any_enum(t1) && btf_is_any_enum(t2);
}
+static long btf_hash_loc_param(struct btf_type *t)
+{
+ struct btf_loc_param *p = btf_loc_param(t);
+ long h = btf_hash_common(t);
+ int i, vlen = btf_vlen(t);
+
+ h = hash_combine(h, p->flags);
+
+ for (i = 0; i < vlen; i++)
+ h = hash_combine(h, p->values[i]);
+ return h;
+}
+
+static long btf_hash_loc_proto(struct btf_type *t)
+{
+ __u32 *p = btf_loc_proto_params(t);
+ long h = btf_hash_common(t);
+ int i, vlen = btf_vlen(t);
+
+ for (i = 0; i < vlen; i++, p++)
+ h = hash_combine(h, *p);
+ return h;
+}
+
+static bool btf_equal_loc_param(struct btf_type *t1, struct btf_type *t2)
+{
+ struct btf_loc_param *p1 = btf_loc_param(t1);
+ struct btf_loc_param *p2 = btf_loc_param(t2);
+ int i, vlen = btf_vlen(t1);
+
+ if (!btf_equal_common(t1, t2))
+ return false;
+ if (p1->flags != p2->flags)
+ return false;
+ for (i = 0; i < vlen; i++) {
+ if (p1->values[i] != p2->values[i])
+ return false;
+ }
+ return true;
+}
+
/*
* Calculate type signature hash of STRUCT/UNION, ignoring referenced type IDs,
* as referenced type IDs equivalence is established separately during type
@@ -4589,7 +4923,8 @@ static int btf_dedup_prep(struct btf_dedup *d)
switch (btf_kind(t)) {
case BTF_KIND_VAR:
case BTF_KIND_DATASEC:
- /* VAR and DATASEC are never hash/deduplicated */
+ case BTF_KIND_LOCSEC:
+ /* VAR DATASEC and LOCSEC are never hash/deduplicated */
continue;
case BTF_KIND_CONST:
case BTF_KIND_VOLATILE:
@@ -4622,6 +4957,12 @@ static int btf_dedup_prep(struct btf_dedup *d)
case BTF_KIND_FUNC_PROTO:
h = btf_hash_fnproto(t);
break;
+ case BTF_KIND_LOC_PARAM:
+ h = btf_hash_loc_param(t);
+ break;
+ case BTF_KIND_LOC_PROTO:
+ h = btf_hash_loc_proto(t);
+ break;
default:
pr_debug("unknown kind %d for type [%d]\n", btf_kind(t), type_id);
return -EINVAL;
@@ -4664,6 +5005,8 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
case BTF_KIND_DATASEC:
case BTF_KIND_DECL_TAG:
case BTF_KIND_TYPE_TAG:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
return 0;
case BTF_KIND_INT:
@@ -4713,6 +5056,18 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
}
break;
+ case BTF_KIND_LOC_PARAM:
+ h = btf_hash_loc_param(t);
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = hash_entry->value;
+ cand = btf_type_by_id(d->btf, cand_id);
+ if (btf_equal_loc_param(t, cand)) {
+ new_id = cand_id;
+ break;
+ }
+ }
+ break;
+
default:
return -EINVAL;
}
@@ -5145,6 +5500,13 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
return 1;
}
+ case BTF_KIND_LOC_PARAM:
+ return btf_equal_loc_param(cand_type, canon_type);
+
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
+ return 0;
+
default:
return -EINVAL;
}
@@ -5489,6 +5851,37 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
break;
}
+ case BTF_KIND_LOC_PROTO: {
+ __u32 *p1, *p2;
+ __u32 i, vlen;
+
+ p1 = btf_loc_proto_params(t);
+ vlen = btf_vlen(t);
+
+ for (i = 0; i < vlen; i++, p1++) {
+ ref_type_id = btf_dedup_ref_type(d, *p1);
+ if (ref_type_id < 0)
+ return ref_type_id;
+ *p1 = ref_type_id;
+ }
+
+ h = btf_hash_loc_proto(t);
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = hash_entry->value;
+ cand = btf_type_by_id(d->btf, cand_id);
+ if (!btf_equal_common(t, cand))
+ continue;
+ vlen = btf_vlen(cand);
+ p1 = btf_loc_proto_params(t);
+ p2 = btf_loc_proto_params(cand);
+ if (memcmp(p1, p2, vlen * sizeof(__u32)) == 0) {
+ new_id = cand_id;
+ break;
+ }
+ }
+ break;
+ }
+
default:
return -EINVAL;
}
@@ -5970,8 +6363,11 @@ static int btf_add_distilled_type_ids(struct btf_distill *dist, __u32 i)
case BTF_KIND_CONST:
case BTF_KIND_RESTRICT:
case BTF_KIND_VOLATILE:
+ case BTF_KIND_FUNC:
case BTF_KIND_FUNC_PROTO:
case BTF_KIND_TYPE_TAG:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
dist->id_map[*id] = *id;
break;
default:
@@ -5997,7 +6393,7 @@ static int btf_add_distilled_type_ids(struct btf_distill *dist, __u32 i)
static int btf_add_distilled_types(struct btf_distill *dist)
{
- bool adding_to_base = dist->pipe.dst->start_id == 1;
+ bool adding_to_base = dist->pipe.dst->base_btf == NULL;
int id = btf__type_cnt(dist->pipe.dst);
struct btf_type *t;
int i, err = 0;
@@ -6065,8 +6461,12 @@ static int btf_add_distilled_types(struct btf_distill *dist)
case BTF_KIND_CONST:
case BTF_KIND_RESTRICT:
case BTF_KIND_VOLATILE:
+ case BTF_KIND_FUNC:
case BTF_KIND_FUNC_PROTO:
case BTF_KIND_TYPE_TAG:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
/* All other types are added to split BTF. */
if (adding_to_base)
continue;
diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
index 587172c0de089..57f12630c5d15 100644
--- a/tools/lib/bpf/btf.h
+++ b/tools/lib/bpf/btf.h
@@ -274,6 +274,20 @@ LIBBPF_API int btf__add_decl_tag(struct btf *btf, const char *value, int ref_typ
LIBBPF_API int btf__add_decl_attr(struct btf *btf, const char *value, int ref_type_id,
int component_idx);
+/* location construction APIs */
+LIBBPF_API int btf__add_loc_param(struct btf *btf, __u32 size, __u32 flags);
+
+LIBBPF_API int btf__add_loc_param_value(struct btf *btf, __u32 value);
+
+LIBBPF_API int btf__add_loc_proto(struct btf *btf);
+
+LIBBPF_API int btf__add_loc_proto_param(struct btf *btf, __u32 id);
+
+LIBBPF_API int btf__add_locsec(struct btf *btf, const char *name);
+
+LIBBPF_API int btf__add_locsec_loc(struct btf *btf, __u32 func, __u32 loc_proto,
+ __u32 offset);
+
struct btf_dedup_opts {
size_t sz;
/* optional .BTF.ext info to dedup along the main BTF info */
@@ -431,6 +445,12 @@ btf_dump__dump_type_data(struct btf_dump *d, __u32 id,
#define BTF_KIND_DECL_TAG 17 /* Decl Tag */
#define BTF_KIND_TYPE_TAG 18 /* Type Tag */
#define BTF_KIND_ENUM64 19 /* Enum for up-to 64bit values */
+#define BTF_KIND_LOC_PARAM 20 /* Parameter at location */
+#define BTF_KIND_LOC_PROTO 21 /* Parameter set at location */
+#define BTF_KIND_LOCSEC 22 /* Section containing location info */
+
+struct btf_loc_param;
+struct btf_loc;
static inline __u16 btf_kind(const struct btf_type *t)
{
@@ -569,6 +589,21 @@ static inline bool btf_is_any_enum(const struct btf_type *t)
return btf_is_enum(t) || btf_is_enum64(t);
}
+static inline bool btf_is_loc_param(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_LOC_PARAM;
+}
+
+static inline bool btf_is_loc_proto(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_LOC_PROTO;
+}
+
+static inline bool btf_is_locsec(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_LOCSEC;
+}
+
static inline bool btf_kind_core_compat(const struct btf_type *t1,
const struct btf_type *t2)
{
@@ -683,6 +718,21 @@ static inline struct btf_decl_tag *btf_decl_tag(const struct btf_type *t)
return (struct btf_decl_tag *)(t + 1);
}
+static inline struct btf_loc_param *btf_loc_param(const struct btf_type *t)
+{
+ return (struct btf_loc_param *)(t + 1);
+}
+
+static inline __u32 *btf_loc_proto_params(const struct btf_type *t)
+{
+ return (__u32 *)(t + 1);
+}
+
+static inline struct btf_loc *btf_locsec_locs(const struct btf_type *t)
+{
+ return (struct btf_loc *)(t + 1);
+}
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
index 123c448f20c74..fa995c02a1705 100644
--- a/tools/lib/bpf/btf_dump.c
+++ b/tools/lib/bpf/btf_dump.c
@@ -328,6 +328,9 @@ static int btf_dump_mark_referenced(struct btf_dump *d)
case BTF_KIND_ENUM64:
case BTF_KIND_FWD:
case BTF_KIND_FLOAT:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
break;
case BTF_KIND_VOLATILE:
@@ -609,6 +612,9 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)
case BTF_KIND_VAR:
case BTF_KIND_DATASEC:
case BTF_KIND_DECL_TAG:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
d->type_states[id].order_state = ORDERED;
return 0;
@@ -2525,6 +2531,9 @@ static int btf_dump_dump_type_data(struct btf_dump *d,
case BTF_KIND_FUNC:
case BTF_KIND_FUNC_PROTO:
case BTF_KIND_DECL_TAG:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
err = btf_dump_unsupported_data(d, t, id);
break;
case BTF_KIND_INT:
diff --git a/tools/lib/bpf/btf_iter.c b/tools/lib/bpf/btf_iter.c
index 9a6c822c22944..199063b55ebe6 100644
--- a/tools/lib/bpf/btf_iter.c
+++ b/tools/lib/bpf/btf_iter.c
@@ -29,6 +29,7 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t,
case BTF_KIND_FLOAT:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
+ case BTF_KIND_LOC_PARAM:
it->desc = (struct btf_field_desc) {};
break;
case BTF_KIND_FWD:
@@ -71,6 +72,20 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t,
1, {offsetof(struct btf_var_secinfo, type)}
};
break;
+ case BTF_KIND_LOC_PROTO:
+ it->desc = (struct btf_field_desc) {
+ 0, {},
+ sizeof(__u32),
+ 1, {0}};
+ break;
+ case BTF_KIND_LOCSEC:
+ it->desc = (struct btf_field_desc) {
+ 0, {},
+ sizeof(struct btf_loc),
+ 2, {offsetof(struct btf_loc, func),
+ offsetof(struct btf_loc, loc_proto)}};
+ break;
+
default:
return -EINVAL;
}
@@ -94,6 +109,9 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t,
case BTF_KIND_DECL_TAG:
case BTF_KIND_TYPE_TAG:
case BTF_KIND_DATASEC:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
it->desc = (struct btf_field_desc) {
1, {offsetof(struct btf_type, name_off)}
};
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 2f53afc985cfb..930588950ab39 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -2483,6 +2483,9 @@ static const char *__btf_kind_str(__u16 kind)
case BTF_KIND_DECL_TAG: return "decl_tag";
case BTF_KIND_TYPE_TAG: return "type_tag";
case BTF_KIND_ENUM64: return "enum64";
+ case BTF_KIND_LOC_PARAM: return "loc_param";
+ case BTF_KIND_LOC_PROTO: return "loc_proto";
+ case BTF_KIND_LOCSEC: return "locsec";
default: return "unknown";
}
}
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 7a84dd00ce95f..18d27d20102ec 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -463,6 +463,12 @@ LIBBPF_1.8.0 {
bpf_program__attach_tracing_multi;
bpf_program__clear_flags;
bpf_program__clone;
+ btf__add_loc_param;
+ btf__add_loc_param_value;
+ btf__add_loc_proto;
+ btf__add_loc_proto_param;
+ btf__add_locsec;
+ btf__add_locsec_loc;
btf__find_by_name_kind_own;
btf__new_empty_opts;
} LIBBPF_1.7.0;
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index cb4d962338441..546f65b95cf4d 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -580,7 +580,7 @@ struct btf_field_desc {
/* member struct size, or zero, if no members */
int m_sz;
/* repeated per-member offsets */
- int m_off_cnt, m_offs[1];
+ int m_off_cnt, m_offs[2];
};
struct btf_field_iter {
diff --git a/tools/testing/selftests/bpf/btf_helpers.c b/tools/testing/selftests/bpf/btf_helpers.c
index 1c1c2c26690a7..85a4d15d1dd1c 100644
--- a/tools/testing/selftests/bpf/btf_helpers.c
+++ b/tools/testing/selftests/bpf/btf_helpers.c
@@ -27,11 +27,14 @@ static const char * const btf_kind_str_mapping[] = {
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
+ [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
+ [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
+ [BTF_KIND_LOCSEC] = "LOCSEC",
};
static const char *btf_kind_str(__u16 kind)
{
- if (kind > BTF_KIND_ENUM64)
+ if (kind > BTF_KIND_LOCSEC)
return "UNKNOWN";
return btf_kind_str_mapping[kind];
}
@@ -203,6 +206,36 @@ int fprintf_btf_type_raw(FILE *out, const struct btf *btf, __u32 id)
fprintf(out, " type_id=%u component_idx=%d",
t->type, btf_decl_tag(t)->component_idx);
break;
+ case BTF_KIND_LOC_PARAM: {
+ struct btf_loc_param *p = btf_loc_param(t);
+
+ fprintf(out, " size=%u flags=0x%x vlen=%u", t->size, p->flags, vlen);
+ for (i = 0; i < vlen; i++) {
+ if (p->flags & BTF_LOC_PARAM_SIGNED)
+ fprintf(out, "\n\tvalue=%d", (__s32)p->values[i]);
+ else
+ fprintf(out, "\n\tvalue=%u", p->values[i]);
+ }
+ break;
+ }
+ case BTF_KIND_LOC_PROTO: {
+ const __u32 *p = btf_loc_proto_params(t);
+
+ fprintf(out, " vlen=%u", vlen);
+ for (i = 0; i < vlen; i++, p++)
+ fprintf(out, "\n\ttype_id=%u", *p);
+ break;
+ }
+ case BTF_KIND_LOCSEC: {
+ const struct btf_loc *l = btf_locsec_locs(t);
+
+ fprintf(out, " vlen=%u", vlen);
+ for (i = 0; i < vlen; i++, l++) {
+ fprintf(out, "\n\tfunc_type_id=%u loc_proto_type_id=%u offset=%u",
+ l->func, l->loc_proto, l->offset);
+ }
+ break;
+ }
default:
break;
}
diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
index d5b25302b0c89..bed506badc755 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
@@ -57,6 +57,57 @@ static struct btf *mk_btf(void)
return btf;
}
+static struct btf *mk_loc_btf(void)
+{
+ struct btf *btf;
+
+ btf = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf, "new_empty"))
+ return NULL;
+
+ btf__add_int(btf, "int", 4, BTF_INT_SIGNED);
+ btf__add_func_proto(btf, 1);
+ btf__add_func_param(btf, "arg1", 1);
+ btf__add_func_param(btf, "arg2", 1);
+ btf__add_func_param(btf, "arg3", 1);
+ btf__add_func_param(btf, "arg4", 1);
+ btf__add_func_param(btf, "arg5", 1);
+ btf__add_func(btf, "foo", BTF_FUNC_STATIC, 2);
+
+ btf__add_loc_param(btf, 4, BTF_LOC_PARAM_REG);
+ btf__add_loc_param_value(btf, 1);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG |
+ BTF_LOC_PARAM_DEREF | BTF_LOC_PARAM_OFFSET);
+ btf__add_loc_param_value(btf, 2);
+ btf__add_loc_param_value(btf, 0x10);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG |
+ BTF_LOC_PARAM_OFFSET | BTF_LOC_PARAM_SIGNED);
+ btf__add_loc_param_value(btf, BTF_LOC_PARAM_FBREG);
+ btf__add_loc_param_value(btf, -0x10);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_ADDR | BTF_LOC_PARAM_CONST);
+ btf__add_loc_param_value(btf, 0x9abcdef0);
+ btf__add_loc_param_value(btf, 0x12345678);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_CONST);
+ btf__add_loc_param_value(btf, 0xfeedface);
+ btf__add_loc_param_value(btf, 0xdeadbeef);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG |
+ BTF_LOC_PARAM_DEREF | BTF_LOC_PARAM_OFFSET |
+ BTF_LOC_PARAM_SIGNED);
+ btf__add_loc_param_value(btf, BTF_LOC_PARAM_FBREG);
+ btf__add_loc_param_value(btf, -0x20);
+ btf__add_loc_proto(btf);
+ btf__add_loc_proto_param(btf, 4);
+ btf__add_loc_proto_param(btf, 5);
+ btf__add_loc_proto_param(btf, 6);
+ btf__add_loc_proto_param(btf, 7);
+ btf__add_loc_proto_param(btf, 8);
+ btf__add_loc_proto_param(btf, 9);
+ btf__add_locsec(btf, "inline.text");
+ btf__add_locsec_loc(btf, 3, 10, 64);
+
+ return btf;
+}
+
static int btf_to_tmpfile(const struct btf *btf, char *path)
{
ssize_t written;
@@ -105,6 +156,26 @@ static char *dump_c(const char *btf_path, bool sorted)
return buf;
}
+static char *dump_raw(const char *btf_path)
+{
+ char args[MAX_BPFTOOL_CMD_LEN];
+ char *buf;
+ int err;
+
+ buf = malloc(DUMP_BUF_SZ);
+ if (!ASSERT_OK_PTR(buf, "alloc_dump"))
+ return NULL;
+
+ snprintf(args, sizeof(args), "btf dump file %s", btf_path);
+ err = get_bpftool_command_output(args, buf, DUMP_BUF_SZ);
+ if (!ASSERT_OK(err, "btf_dump_raw")) {
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
static char *read_expected(const char *path)
{
char *buf = NULL;
@@ -155,10 +226,46 @@ static void test_dump(const char *btf_path, bool sorted)
free(dump);
}
+static void test_loc_dump(const char *btf_path)
+{
+ const char expected[] =
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED\n"
+ "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=5\n"
+ "\t'arg1' type_id=1\n"
+ "\t'arg2' type_id=1\n"
+ "\t'arg3' type_id=1\n"
+ "\t'arg4' type_id=1\n"
+ "\t'arg5' type_id=1\n"
+ "[3] FUNC 'foo' type_id=2 linkage=static\n"
+ "[4] LOC_PARAM '(anon)' size=4 flags=0x8 vlen=1 values='reg1'\n"
+ "[5] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(reg2 + 0x10)'\n"
+ "[6] LOC_PARAM '(anon)' size=8 flags=0x29 vlen=2 values='fbreg - 0x10'\n"
+ "[7] LOC_PARAM '(anon)' size=8 flags=0x6 vlen=2 values='0x123456789abcdef0 (addr)'\n"
+ "[8] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2 values='0xdeadbeeffeedface'\n"
+ "[9] LOC_PARAM '(anon)' size=8 flags=0x39 vlen=2 values='*(fbreg - 0x20)'\n"
+ "[10] LOC_PROTO '(anon)' vlen=6\n"
+ "\ttype_id=4 value='reg1'\n"
+ "\ttype_id=5 value='*(reg2 + 0x10)'\n"
+ "\ttype_id=6 value='fbreg - 0x10'\n"
+ "\ttype_id=7 value='0x123456789abcdef0 (addr)'\n"
+ "\ttype_id=8 value='0xdeadbeeffeedface'\n"
+ "\ttype_id=9 value='*(fbreg - 0x20)'\n"
+ "[11] LOCSEC 'inline.text' vlen=1\n"
+ "\tname='foo' func_type_id=3 loc_proto_type_id=10 offset=64\n";
+ char *dump;
+
+ dump = dump_raw(btf_path);
+ if (!dump)
+ return;
+
+ ASSERT_OK(compare_text_to_expected(dump, expected), "cmp_loc_dump");
+ free(dump);
+}
+
void test_bpftool_btf_dump(void)
{
char path[PATH_MAX];
- struct btf *btf;
+ struct btf *btf = NULL;
btf = mk_btf();
if (!btf)
@@ -171,7 +278,19 @@ void test_bpftool_btf_dump(void)
test_dump(path, true);
if (test__start_subtest("c_unsorted"))
test_dump(path, false);
+ unlink(path);
+
+ btf__free(btf);
+
+ btf = mk_loc_btf();
+ if (!btf)
+ return;
+
+ if (btf_to_tmpfile(btf, path))
+ goto out_btf;
+ if (test__start_subtest("loc_dump"))
+ test_loc_dump(path);
unlink(path);
out_btf:
btf__free(btf);
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c b/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c
index 9d61611515938..5c15a6b7f4fb9 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c
@@ -554,6 +554,120 @@ static void test_split_module(void)
btf__free(vmlinux_btf);
}
+static void test_split_loc(void)
+{
+ struct btf *btf1, *btf2;
+ int err;
+
+ btf1 = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
+ return;
+
+ btf__set_pointer_size(btf1, 8); /* enforce 64-bit arch */
+
+
+ btf__add_int(btf1, "long", 8, BTF_INT_SIGNED); /* [1] long */
+ btf__add_ptr(btf1, 1); /* [2] ptr to long */
+ btf__add_func_proto(btf1, 1); /* [3] long (*)(long, long *); */
+ btf__add_func_param(btf1, "p1", 1);
+ btf__add_func_param(btf1, "p2", 2);
+ btf__add_func(btf1, "foo", BTF_FUNC_STATIC, 3); /* [4] long foo(long, long *); */
+ btf__add_loc_param(btf1, 8, BTF_LOC_PARAM_CONST);
+ btf__add_loc_param_value(btf1, 3735928559);
+ btf__add_loc_param_value(btf1, 4277009102); /* [5] loc value */
+ btf__add_loc_param(btf1, 8, BTF_LOC_PARAM_REG); /* [6] loc reg 1 */
+ btf__add_loc_param_value(btf1, 1);
+ btf__add_loc_proto(btf1); /* [7] loc proto */
+ btf__add_loc_proto_param(btf1, 5); /* param value */
+ btf__add_loc_proto_param(btf1, 6); /* param reg 1 */
+
+ VALIDATE_RAW_BTF(
+ btf1,
+ "[1] INT 'long' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED",
+ "[2] PTR '(anon)' type_id=1",
+ "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n"
+ "\t'p1' type_id=1\n"
+ "\t'p2' type_id=2",
+ "[4] FUNC 'foo' type_id=3 linkage=static",
+ "[5] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2\n"
+ "\tvalue=3735928559\n"
+ "\tvalue=4277009102",
+ "[6] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[7] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=5\n"
+ "\ttype_id=6");
+
+ btf2 = btf__new_empty_split(btf1);
+ if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
+ goto cleanup;
+ btf__add_loc_param(btf2, 8, BTF_LOC_PARAM_REG);
+ btf__add_loc_param_value(btf2, 1); /* [8] loc reg 1 */
+ btf__add_loc_proto(btf2); /* [9] loc proto */
+ btf__add_loc_proto_param(btf2, 5); /* param value */
+ btf__add_loc_proto_param(btf2, 8); /* param reg 1 */
+ btf__add_locsec(btf2, "inline.text"); /* [10] locsec "inline.text" */
+ /* add duplicate locsec */
+ btf__add_locsec_loc(btf2, 4, 9, 128);
+ btf__add_locsec(btf2, "inline.text"); /* [11] locsec "inline.text" */
+ btf__add_locsec_loc(btf2, 4, 9, 128);
+
+ VALIDATE_RAW_BTF(
+ btf2,
+ "[1] INT 'long' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED",
+ "[2] PTR '(anon)' type_id=1",
+ "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n"
+ "\t'p1' type_id=1\n"
+ "\t'p2' type_id=2",
+ "[4] FUNC 'foo' type_id=3 linkage=static",
+ "[5] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2\n"
+ "\tvalue=3735928559\n"
+ "\tvalue=4277009102",
+ "[6] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[7] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=5\n"
+ "\ttype_id=6",
+ "[8] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[9] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=5\n"
+ "\ttype_id=8",
+ "[10] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=9 offset=128",
+ "[11] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=9 offset=128");
+
+ err = btf__dedup(btf2, NULL);
+ if (!ASSERT_OK(err, "btf_dedup"))
+ goto cleanup;
+
+ VALIDATE_RAW_BTF(
+ btf2,
+ "[1] INT 'long' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED",
+ "[2] PTR '(anon)' type_id=1",
+ "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n"
+ "\t'p1' type_id=1\n"
+ "\t'p2' type_id=2",
+ "[4] FUNC 'foo' type_id=3 linkage=static",
+ "[5] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2\n"
+ "\tvalue=3735928559\n"
+ "\tvalue=4277009102",
+ "[6] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[7] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=5\n"
+ "\ttype_id=6",
+ "[8] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=7 offset=128",
+ "[9] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=7 offset=128");
+
+cleanup:
+ btf__free(btf2);
+ btf__free(btf1);
+}
+
void test_btf_dedup_split()
{
if (test__start_subtest("split_simple"))
@@ -566,4 +680,6 @@ void test_btf_dedup_split()
test_split_dup_struct_in_cu();
if (test__start_subtest("split_module"))
test_split_module();
+ if (test__start_subtest("split_loc"))
+ test_split_loc();
}
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_distill.c b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
index fb67ae195a738..f0900b47fd487 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_distill.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
@@ -671,6 +671,110 @@ static void test_distilled_base_embedded_err(void)
btf__free(btf1);
}
+/* LOC_PARAM, LOC_PROTO should be added to split BTF. */
+static void test_distilled_loc(void)
+{
+ struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL;
+ struct btf *btf4 = NULL, *btf5 = NULL;
+
+ btf1 = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
+ return;
+
+ btf__add_int(btf1, "int", 4, BTF_INT_SIGNED); /* [1] int */
+ btf__add_func_proto(btf1, 1); /* [2] int (*)(int); */
+ btf__add_func_param(btf1, "p1", 1);
+ btf__add_func(btf1, "foo", BTF_FUNC_STATIC, 2); /* [3] int foo(int); */
+ btf__add_loc_param(btf1, 4, BTF_LOC_PARAM_SIGNED | BTF_LOC_PARAM_CONST);
+ btf__add_loc_param_value(btf1, -1); /* [4] loc value */
+ btf__add_loc_proto(btf1); /* [5] loc proto */
+ btf__add_loc_proto_param(btf1, 4); /* param value */
+ btf__add_int(btf1, "unsigned int", 4, 0); /* [6] unsigned int */
+
+ VALIDATE_RAW_BTF(
+ btf1,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+ "\t'p1' type_id=1",
+ "[3] FUNC 'foo' type_id=2 linkage=static",
+ "[4] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[5] LOC_PROTO '(anon)' vlen=1\n"
+ "\ttype_id=4",
+ "[6] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)");
+
+ btf2 = btf__new_empty_split(btf1);
+ if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
+ goto cleanup;
+
+ btf__add_locsec(btf2, "inline.text"); /* [6] locsec */
+ btf__add_locsec_loc(btf2, 3, 5, 256); /* "foo" offset 256 */
+ VALIDATE_RAW_BTF(
+ btf2,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+ "\t'p1' type_id=1",
+ "[3] FUNC 'foo' type_id=2 linkage=static",
+ "[4] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[5] LOC_PROTO '(anon)' vlen=1\n"
+ "\ttype_id=4",
+ "[6] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)",
+ "[7] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=3 loc_proto_type_id=5 offset=256");
+
+ if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4),
+ "distilled_base") ||
+ !ASSERT_OK_PTR(btf3, "distilled_base") ||
+ !ASSERT_OK_PTR(btf4, "distilled_split") ||
+ !ASSERT_EQ(2, btf__type_cnt(btf3), "distilled_base_type_cnt"))
+ goto cleanup;
+
+ VALIDATE_RAW_BTF(
+ btf4,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ /* remainder is split BTF */
+ "[2] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=6 offset=256",
+ "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+ "\t'p1' type_id=1",
+ "[4] FUNC 'foo' type_id=3 linkage=static",
+ "[5] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[6] LOC_PROTO '(anon)' vlen=1\n"
+ "\ttype_id=5");
+
+ btf5 = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf5, "empty_reloc_btf"))
+ goto cleanup;
+ btf__add_int(btf5, "int", 4, BTF_INT_SIGNED); /* [1] int */
+ btf__add_int(btf5, "char", 1, 0); /* [2] char */
+
+ if (!ASSERT_EQ(btf__relocate(btf4, btf5), 0, "relocate_split"))
+ goto cleanup;
+ VALIDATE_RAW_BTF(
+ btf4,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[2] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=(none)",
+ /* remainder is split BTF */
+ "[3] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=5 loc_proto_type_id=7 offset=256",
+ "[4] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+ "\t'p1' type_id=1",
+ "[5] FUNC 'foo' type_id=4 linkage=static",
+ "[6] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[7] LOC_PROTO '(anon)' vlen=1\n"
+ "\ttype_id=6");
+
+cleanup:
+ btf__free(btf5);
+ btf__free(btf4);
+ btf__free(btf3);
+ btf__free(btf2);
+ btf__free(btf1);
+}
+
void test_btf_distill(void)
{
if (test__start_subtest("distilled_base"))
@@ -689,4 +793,6 @@ void test_btf_distill(void)
test_distilled_base_vmlinux();
if (test__start_subtest("distilled_endianness"))
test_distilled_endianness();
+ if (test__start_subtest("distilled_loc"))
+ test_distilled_loc();
}
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c b/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c
index 32159d3eb281b..dcb5429d141df 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c
@@ -31,8 +31,11 @@ struct field_data {
{ .ids = { 11 }, .strs = { "decltag" } },
{ .ids = { 6 }, .strs = { "typetag" } },
{ .ids = {}, .strs = { "e64", "eval1", "eval2", "eval3" } },
- { .ids = { 15, 16 }, .strs = { "datasec1" } }
-
+ { .ids = { 15, 16 }, .strs = { "datasec1" } },
+ { .ids = {}, .strs = { "" } },
+ { .ids = {}, .strs = { "" } },
+ { .ids = { 22, 23 }, .strs = { "" } },
+ { .ids = { 14, 24 }, .strs = { ".loc" } }
};
/* Fabricate BTF with various types and check BTF field iteration finds types,
@@ -88,6 +91,19 @@ void test_btf_field_iter(void)
btf__add_datasec_var_info(btf, 15, 0, 4);
btf__add_datasec_var_info(btf, 16, 4, 8);
+ btf__add_loc_param(btf, 4, BTF_LOC_PARAM_CONST | BTF_LOC_PARAM_SIGNED);
+ /* [22] loc value -1 */
+ btf__add_loc_param_value(btf, -1);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG); /* [23] loc reg 1 */
+ btf__add_loc_param_value(btf, 1);
+
+ btf__add_loc_proto(btf); /* [24] loc proto */
+ btf__add_loc_proto_param(btf, 22); /* param value -1, */
+ btf__add_loc_proto_param(btf, 23); /* param reg 1 */
+
+ btf__add_locsec(btf, ".loc"); /* [25] locsec ".loc" */
+ btf__add_locsec_loc(btf, 14, 24, 128); /* "func" */
+
VALIDATE_RAW_BTF(
btf,
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
@@ -123,7 +139,16 @@ void test_btf_field_iter(void)
"\t'eval3' val=3000",
"[21] DATASEC 'datasec1' size=12 vlen=2\n"
"\ttype_id=15 offset=0 size=4\n"
- "\ttype_id=16 offset=4 size=8");
+ "\ttype_id=16 offset=4 size=8",
+ "[22] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[23] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[24] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=22\n"
+ "\ttype_id=23",
+ "[25] LOCSEC '.loc' vlen=1\n"
+ "\tfunc_type_id=14 loc_proto_type_id=24 offset=128");
for (id = 1; id < btf__type_cnt(btf); id++) {
struct btf_type *t = btf_type_by_id(btf, id);
|
| 3/2 | 2026/09/26 16:57 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 36242 output: 28 thoughts: 1013 Thoughts: **Patch Evaluation: BTF Location Support** Okay, so the user wants me to evaluate this patch series targeting amd64, which is applied as the head commit. Seems like it's adding support for `BTF_KIND_LOC_PARAM`, `BTF_KIND_LOC_PROTO`, and `BTF_KIND_LOCSEC` within the BTF (BPF Type Format) infrastructure. I need to dig into this thoroughly. My initial assessment is that this touches several key files: `kernel/bpf/btf.c`, `include/linux/btf.h`, `include/uapi/linux/btf.h`, and, importantly, some tooling (`tools/...`). Let's break down the changes: Looking at `kernel/bpf/btf.c`, there are new functions being added: `btf_type_is_loc_param`, `btf_type_is_loc_proto`, and `btf_type_is_locsec`. It looks like some existing functions are being modified (`btf_type_nosize`, `btf_type_is_resolve_source_only`, `btf_type_needs_resolve`) and there's a good chunk of new functions for metadata checks and resolution logic: `btf_loc_param_check_meta`, `btf_loc_param_log`, `loc_param_ops`, `btf_loc_proto_check_meta`, `btf_loc_proto_log`, `btf_loc_proto_resolve`, `loc_proto_ops`, `btf_verifier_log_loc`, `btf_locsec_check_meta`, `btf_locsec_log`, `btf_locsec_resolve`, `locsec_ops`. The `kind_ops` table is getting updated, and `btf_resolve_valid` is modified too. In `include/linux/btf.h`, there's a new `for_each_loc`, and new structs like `btf_loc_param`, `btf_loc_proto_params`, `btf_type_loc_secinfo`. The `struct btf_field_desc` is also being changed. This suggests deeper integration. `include/uapi/linux/btf.h` defines the user-space interface. This means there's an `enum` for the new `BTF_KIND` values and structs `btf_loc_param`, `btf_loc_param_flags`, and `btf_loc`. And, as expected, we have changes in the `tools` directory, including `bpftool`, `lib/bpf`, and self-tests. This is expected as these tools need to be updated to handle the new BTF kinds. Now, the crucial question: is this code reachable and worth fuzzing? Section 3 of our evaluation criteria says "Return WorthFuzzing=true whenever the patch touches reachable executable code". Based on the description, and my knowledge of the kernel, I can confirm this is absolutely a candidate for fuzzing. Specifically, BTF parsing happens within `kernel/bpf/btf.c`, which is directly exposed to userspace through the `BPF_BTF_LOAD` command in the `bpf()` system call! Users, *anyone*, can invoke `sys_bpf(BPF_BTF_LOAD, ...)` or load BPF programs with BTF, and they can even load BTF objects directly. This is a primary target for fuzzing, and I know Syzkaller hits this hard. This patch is adding new BTF kinds and associated validation and resolution logic. That's a huge attack surface. This is code is 100% reachable executable kernel code within the BPF subsystem, which is accessible through either unprivileged or privileged `bpf()` calls. Now, let me just double check. I need to verify the Kconfig options required for `kernel/bpf/btf.c` and BTF support. Just to be sure. I'll need to run `grepper` or check the Makefile in `kernel/bpf/`. |
| 4/2 | 2026/09/26 16:57 | tool | grepper |
0mArgs:{
"Expression": "btf\\.o",
"PathPrefix": "kernel/bpf"
}Results: {
"Output": "kernel/bpf/Makefile=13=obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o bpf_task_storage.o\nkernel/bpf/Makefile:14:obj-$(CONFIG_BPF_SYSCALL) += fixups.o cfg.o states.o backtrack.o check_btf.o\nkernel/bpf/Makefile-15-obj-${CONFIG_BPF_LSM}\t += bpf_inode_storage.o\n--\nkernel/bpf/Makefile=17=obj-$(CONFIG_BPF_JIT) += trampoline.o\nkernel/bpf/Makefile:18:obj-$(CONFIG_BPF_SYSCALL) += btf.o memalloc.o rqspinlock.o stream.o\nkernel/bpf/Makefile-19-ifeq ($(CONFIG_MMU)$(CONFIG_64BIT),yy)\n--\nkernel/bpf/Makefile=43=ifeq ($(CONFIG_SYSFS),y)\nkernel/bpf/Makefile:44:obj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o\nkernel/bpf/Makefile-45-endif\n"
} |
| 5/2 | 2026/09/26 16:57 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 38479 output: 31 thoughts: 35 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in the weeds of kernel development and understand the context: **Adding a BPF Syscall Object to the Build Process** Alright, so I'm seeing this line: `obj-$(CONFIG_BPF_SYSCALL) += btf.o`. My immediate thought is, "Okay, we're building a `btf.o` object file, but it's conditional." The key here is that `obj-$(CONFIG_BPF_SYSCALL)` part. This implies that the inclusion of `btf.o` in the final build depends on whether a Kconfig option, specifically `CONFIG_BPF_SYSCALL`, is enabled. My next step is definitely to head over to the Kconfig files. I need to understand what `BPF_SYSCALL` actually *is*. What are the implications of enabling it? What does it relate to? Is it dependent on anything else? Is it enabled by default in any of our common configurations? I need to understand the configuration's impact on this module's inclusion. And, I need to know where I can find the actual kernel code for `BPF_SYSCALL`. I'm guessing that this relates to BPF (Berkeley Packet Filter) and system calls. But without checking Kconfig, I can't be sure of the exact details. |
| 6/2 | 2026/09/26 16:57 | tool | grepper |
0mError:no matches Args: {
"Expression": "config BPF_SYSCALL",
"PathPrefix": "init/Kconfig"
}Results: {
"error": "no matches"
} |
| 7/2 | 2026/09/26 16:57 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 38858 output: 21 thoughts: 17 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Investigating the Configuration for BPF_SYSCALL** Alright, let's get down to brass tacks. I need to pinpoint the definition of `config BPF_SYSCALL` in the kernel configuration. I'm thinking I'll need to trace its origins, understand its dependencies, and see exactly *where* this crucial setting is being managed. Given my experience, I'm expecting this will involve spelunking through the Kconfig files, likely diving through the various architecture-specific or feature-specific directories. I’ll be looking for cross-references, conditional statements, and any hints about what other options control its visibility or state. Finding the definitive source of truth is key to understanding its implications within the BPF ecosystem and the kernel as a whole. Time to get digging. |
| 8/2 | 2026/09/26 16:57 | tool | grepper |
0mArgs:{
"Expression": "config BPF_SYSCALL"
}Results: {
"Output": "kernel/bpf/Kconfig=29=menu \"BPF subsystem\"\nkernel/bpf/Kconfig-30-\nkernel/bpf/Kconfig:31:config BPF_SYSCALL\nkernel/bpf/Kconfig-32-\tbool \"Enable bpf() system call\"\n"
} |
| 9/2 | 2026/09/26 16:57 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39164 output: 34 thoughts: 18 Thoughts: Okay, here's my interpretation of what's going on, framed as a first-person thought process, keeping in mind the context of a fellow expert: **Investigating a Kconfig Line** Alright, so the instruction is to examine the lines around `kernel/bpf/Kconfig:31`. That's the key. This means we're dealing with a Kconfig file, likely related to the Berkeley Packet Filter (BPF) subsystem within the kernel. Line 31 itself is what's important, but to understand its meaning and implications, we need context. We need to see what's *around* it. I'll be looking for a few specific things: * **Dependencies:** What other features or options is this line dependent on? Is it behind any `if` statements, or does it require other features to be enabled? This is crucial for understanding how this particular BPF config option fits into the broader picture. * **Default Values:** Does the line define a default value? Knowing the default is really useful, as it helps determine the base configuration. * **Type of Option:** Is it a boolean, tristate, integer, or string option? This dictates how it's handled in the kernel config and what values it can take. * **Help Text:** The help text will (hopefully) tell me precisely what the option controls. I'll read it carefully, looking for a concise explanation of the feature, any performance or security implications, and any necessary usage guidelines. * **Relationship to other BPF Options:** How does this line relate to the broader set of BPF options? Is it part of a larger group of related functionality? Are there any obvious conflicts or synergies? * **Recent Changes:** I'll mentally check my knowledge of recent kernel changes related to BPF. Has there been any recent work in the area, or potential regressions? A quick review of the change log could be worthwhile. Basically, I want to build a complete mental model of this Kconfig line: its purpose, its place in the kernel configuration, and its potential impact on the system. Then I will determine the overall implications of the configuration. |
| 10/2 | 2026/09/26 16:57 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/Kconfig",
"FirstLine": 29,
"LineCount": 40
}Results: {
"Contents": " 29:\tmenu \"BPF subsystem\"\n 30:\t\n 31:\tconfig BPF_SYSCALL\n 32:\t\tbool \"Enable bpf() system call\"\n 33:\t\tselect BPF\n 34:\t\tselect IRQ_WORK\n 35:\t\tselect NEED_TASKS_RCU\n 36:\t\tselect TASKS_TRACE_RCU\n 37:\t\tselect BINARY_PRINTF\n 38:\t\tselect NET_SOCK_MSG if NET\n 39:\t\tselect NET_XGRESS if NET\n 40:\t\tselect PAGE_POOL if NET\n 41:\t\tdefault n\n 42:\t\thelp\n 43:\t\t Enable the bpf() system call that allows to manipulate BPF programs\n 44:\t\t and maps via file descriptors.\n 45:\t\n 46:\tconfig BPF_JIT\n 47:\t\tbool \"Enable BPF Just In Time compiler\"\n 48:\t\tdepends on BPF\n 49:\t\tdepends on HAVE_CBPF_JIT || HAVE_EBPF_JIT\n 50:\t\tselect EXECMEM\n 51:\t\thelp\n 52:\t\t BPF programs are normally handled by a BPF interpreter. This option\n 53:\t\t allows the kernel to generate native code when a program is loaded\n 54:\t\t into the kernel. This will significantly speed-up processing of BPF\n 55:\t\t programs.\n 56:\t\n 57:\t\t Note, an admin should enable this feature changing:\n 58:\t\t /proc/sys/net/core/bpf_jit_enable\n 59:\t\t /proc/sys/net/core/bpf_jit_harden (optional)\n 60:\t\t /proc/sys/net/core/bpf_jit_kallsyms (optional)\n 61:\t\n 62:\tconfig BPF_JIT_ALWAYS_ON\n 63:\t\tbool \"Permanently enable BPF JIT and remove BPF interpreter\"\n 64:\t\tdepends on BPF_SYSCALL \u0026\u0026 HAVE_EBPF_JIT \u0026\u0026 BPF_JIT\n 65:\t\thelp\n 66:\t\t Enables BPF JIT and removes BPF interpreter to avoid speculative\n 67:\t\t execution of BPF instructions by the interpreter.\n 68:\t\n"
} |
| 11/2 | 2026/09/26 16:57 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 40260 output: 31 thoughts: 48 Thoughts: Okay, let's see. My first thought is: **Assessing Configuration Dependency** I need to confirm whether `BPF_SYSCALL` is the only configuration option governing this modified code, or if there are other, possibly more granular, configurations in play. I need to be absolutely sure I understand the conditional compilation context. Then, I'll dive into `kernel/bpf/btf.c`. I need to meticulously scrutinize that file for any `#ifdef` blocks or other preprocessor directives surrounding the code I'm dealing with. That file is a key one for BTF related actions, and it's almost certain to hold the secrets I seek. I'm looking for any conditional compilation paths that might affect this code's behavior or availability. |
| 12/2 | 2026/09/26 16:57 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_",
"PathPrefix": "kernel/bpf/btf.c"
}Results: {
"Output": "kernel/bpf/btf.c=1584=__printf(4, 5) static void __btf_verifier_log_type(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-1604-\t\t/* Skip logging when loading module BTF with mismatches permitted */\nkernel/bpf/btf.c:1605:\t\tif (env-\u003ebtf-\u003ebase_btf \u0026\u0026 IS_ENABLED(CONFIG_MODULE_ALLOW_BTF_MISMATCH))\nkernel/bpf/btf.c-1606-\t\t\treturn;\n--\nkernel/bpf/btf.c=1634=static void btf_verifier_log_member(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-1650-\t\t/* Skip logging when loading module BTF with mismatches permitted */\nkernel/bpf/btf.c:1651:\t\tif (env-\u003ebtf-\u003ebase_btf \u0026\u0026 IS_ENABLED(CONFIG_MODULE_ALLOW_BTF_MISMATCH))\nkernel/bpf/btf.c-1652-\t\t\treturn;\n--\nkernel/bpf/btf.c=6731=static struct btf *btf_parse_base(struct btf_verifier_env *env, const char *name,\n--\nkernel/bpf/btf.c-6736-\nkernel/bpf/btf.c:6737:\tif (!IS_ENABLED(CONFIG_DEBUG_INFO_BTF))\nkernel/bpf/btf.c-6738-\t\treturn ERR_PTR(-ENOENT);\n--\nkernel/bpf/btf.c=6817=__u32 btf_relocate_id(const struct btf *btf, __u32 id)\n--\nkernel/bpf/btf.c-6823-\nkernel/bpf/btf.c:6824:#ifdef CONFIG_DEBUG_INFO_BTF_MODULES\nkernel/bpf/btf.c-6825-\nkernel/bpf/btf.c=6826=static struct btf *btf_parse_module(const char *module_name, const void *data,\n--\nkernel/bpf/btf.c-6921-\nkernel/bpf/btf.c:6922:#endif /* CONFIG_DEBUG_INFO_BTF_MODULES */\nkernel/bpf/btf.c-6923-\n--\nkernel/bpf/btf.c=8729=int btf_type_name_to_buf(const struct btf *btf, u32 type_id, char *buf, int len)\n--\nkernel/bpf/btf.c-8738-\nkernel/bpf/btf.c:8739:#ifdef CONFIG_PROC_FS\nkernel/bpf/btf.c-8740-static void bpf_btf_show_fdinfo(struct seq_file *m, struct file *filp)\n--\nkernel/bpf/btf.c=8754=const struct file_operations btf_fops = {\nkernel/bpf/btf.c:8755:#ifdef CONFIG_PROC_FS\nkernel/bpf/btf.c-8756-\t.show_fdinfo\t= bpf_btf_show_fdinfo,\n--\nkernel/bpf/btf.c=8902=enum {\n--\nkernel/bpf/btf.c-8905-\nkernel/bpf/btf.c:8906:#ifdef CONFIG_DEBUG_INFO_BTF_MODULES\nkernel/bpf/btf.c-8907-struct btf_module {\n--\nkernel/bpf/btf.c=8920=static int btf_module_notify(struct notifier_block *nb, unsigned long op,\n--\nkernel/bpf/btf.c-8943-\t\t\tkfree(btf_mod);\nkernel/bpf/btf.c:8944:\t\t\tif (!IS_ENABLED(CONFIG_MODULE_ALLOW_BTF_MISMATCH)) {\nkernel/bpf/btf.c-8945-\t\t\t\tpr_warn(\"failed to validate module [%s] BTF: %ld\\n\",\n--\nkernel/bpf/btf.c-8966-\nkernel/bpf/btf.c:8967:\t\tif (IS_ENABLED(CONFIG_SYSFS)) {\nkernel/bpf/btf.c-8968-\t\t\tstruct bin_attribute *attr;\n--\nkernel/bpf/btf.c=9044=fs_initcall(btf_module_init);\nkernel/bpf/btf.c:9045:#endif /* CONFIG_DEBUG_INFO_BTF_MODULES */\nkernel/bpf/btf.c-9046-\nkernel/bpf/btf.c=9047=struct module *btf_try_get_module(const struct btf *btf)\n--\nkernel/bpf/btf.c-9049-\tstruct module *res = NULL;\nkernel/bpf/btf.c:9050:#ifdef CONFIG_DEBUG_INFO_BTF_MODULES\nkernel/bpf/btf.c-9051-\tstruct btf_module *btf_mod, *tmp;\n--\nkernel/bpf/btf.c=9077=struct btf *btf_get_module_btf(const struct module *module)\nkernel/bpf/btf.c-9078-{\nkernel/bpf/btf.c:9079:#ifdef CONFIG_DEBUG_INFO_BTF_MODULES\nkernel/bpf/btf.c-9080-\tstruct btf_module *btf_mod, *tmp;\n--\nkernel/bpf/btf.c-9090-\nkernel/bpf/btf.c:9091:#ifdef CONFIG_DEBUG_INFO_BTF_MODULES\nkernel/bpf/btf.c-9092-\tmutex_lock(\u0026btf_module_mutex);\n--\nkernel/bpf/btf.c=9107=static int check_btf_kconfigs(const struct module *module, const char *feature)\nkernel/bpf/btf.c-9108-{\nkernel/bpf/btf.c:9109:\tif (!module \u0026\u0026 IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) {\nkernel/bpf/btf.c-9110-\t\tpr_err(\"missing vmlinux BTF, cannot register %s\\n\", feature);\n--\nkernel/bpf/btf.c-9112-\t}\nkernel/bpf/btf.c:9113:\tif (module \u0026\u0026 IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES))\nkernel/bpf/btf.c-9114-\t\tpr_warn(\"missing module BTF, cannot register %s\\n\", feature);\n--\nkernel/bpf/btf.c=9255=static int btf_check_kfunc_name(struct btf *btf, const char *func_name, u32 kind)\nkernel/bpf/btf.c-9256-{\nkernel/bpf/btf.c:9257:#ifdef CONFIG_DEBUG_INFO_BTF_MODULES\nkernel/bpf/btf.c-9258-\tstruct btf_module *btf_mod, *tmp;\n--\nkernel/bpf/btf.c-9271-\nkernel/bpf/btf.c:9272:#ifdef CONFIG_DEBUG_INFO_BTF_MODULES\nkernel/bpf/btf.c-9273-\tguard(mutex)(\u0026btf_module_mutex);\n--\nkernel/bpf/btf.c=9669=static int btf_check_dtor_kfuncs(struct btf *btf, const struct btf_id_dtor_kfunc *dtors, u32 cnt)\n--\nkernel/bpf/btf.c-9702-\nkernel/bpf/btf.c:9703:\t\tif (IS_ENABLED(CONFIG_CFI)) {\nkernel/bpf/btf.c-9704-\t\t\t/* Ensure the destructor kfunc type matches btf_dtor_kfunc_t */\n--\nkernel/bpf/btf.c=9920=static struct bpf_cand_cache *populate_cand_cache(struct bpf_cand_cache *cands,\n--\nkernel/bpf/btf.c-9947-\nkernel/bpf/btf.c:9948:#ifdef CONFIG_DEBUG_INFO_BTF_MODULES\nkernel/bpf/btf.c-9949-static void __purge_cand_cache(struct btf *btf, struct bpf_cand_cache **cache,\n--\nkernel/bpf/btf.c=10252=bool btf_type_ids_nocast_alias(struct bpf_verifier_log *log,\n--\nkernel/bpf/btf.c-10306-\nkernel/bpf/btf.c:10307:#ifdef CONFIG_BPF_JIT\nkernel/bpf/btf.c-10308-static int\n"
} |
| 13/2 | 2026/09/26 16:57 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42817 output: 31 thoughts: 203 Thoughts: Okay, let me lay out my understanding of this. **Analyzing BTF Code Execution Paths** Right, so I'm looking at a set of functions: `btf_loc_param_check_meta`, `btf_loc_param_log`, `btf_loc_proto_check_meta`, `btf_loc_proto_log`, `btf_loc_proto_resolve`, `btf_locsec_check_meta`, `btf_locsec_log`, and `btf_locsec_resolve`. These are all clearly related to BTF (BPF Type Format), and my initial observation is that none of them seem to be guarded by any conditional compilation (`#ifdef`) *within* the `btf.c` file itself. The only condition that influences their inclusion is the overall `CONFIG_BPF_SYSCALL` flag, which means if that's enabled, then these functions *will* be compiled. Now, my focus shifts to understanding how BTF data actually gets *loaded* from userspace into the kernel. The obvious starting point for this is the BPF syscall interface, so I'm thinking the logical entry point would be in `kernel/bpf/syscall.c`. Specifically, I'm thinking about the `bpf_btf_load` function. From there, it would call either `btf_parse` or `btf_new_fd`, which I will search for, as these seem to be the core loading mechanisms. |
| 14/2 | 2026/09/26 16:58 | tool | grepper |
0mArgs:{
"Expression": "bpf_btf_load",
"PathPrefix": "kernel/bpf"
}Results: {
"Output": "kernel/bpf/syscall.c=5539=static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,\n--\nkernel/bpf/syscall.c-5567-\nkernel/bpf/syscall.c:5568:static int bpf_btf_load(const union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_attr *attr_log)\nkernel/bpf/syscall.c-5569-{\n--\nkernel/bpf/syscall.c=6307=static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size,\n--\nkernel/bpf/syscall.c-6420-\t\t\t\t\t\u0026attr_common, uattr_common, size_common);\nkernel/bpf/syscall.c:6421:\t\terr = err ?: bpf_btf_load(\u0026attr, uattr, \u0026attr_log);\nkernel/bpf/syscall.c-6422-\t\tbreak;\n"
} |
| 15/2 | 2026/09/26 16:58 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 43642 output: 37 thoughts: 48 Thoughts: Okay, here's my take on summarizing the thought process, keeping in mind I'm speaking to a fellow expert: **Diving into the bpf_btf_load Function** Right, so I see we're dealing with `bpf_btf_load`. My brain immediately clicks – that's called through `__sys_bpf` when the command is `BPF_BTF_LOAD`. We need to understand what this function is *actually* doing. Let's dig in and figure out the exact flow of execution and the details. What are its inputs? What is it loading? What resources is it consuming and how is that handled? I need to get a clear picture of its behavior. |
| 16/2 | 2026/09/26 16:58 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/syscall.c",
"FirstLine": 5568,
"LineCount": 45
}Results: {
"Contents": "5568:\tstatic int bpf_btf_load(const union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_attr *attr_log)\n5569:\t{\n5570:\t\tstruct bpf_token *token = NULL;\n5571:\t\n5572:\t\tif (CHECK_ATTR(BPF_BTF_LOAD))\n5573:\t\t\treturn -EINVAL;\n5574:\t\n5575:\t\tif (attr-\u003ebtf_flags \u0026 ~BPF_F_TOKEN_FD)\n5576:\t\t\treturn -EINVAL;\n5577:\t\n5578:\t\tif (attr-\u003ebtf_flags \u0026 BPF_F_TOKEN_FD) {\n5579:\t\t\ttoken = bpf_token_get_from_fd(attr-\u003ebtf_token_fd);\n5580:\t\t\tif (IS_ERR(token))\n5581:\t\t\t\treturn PTR_ERR(token);\n5582:\t\t\tif (!bpf_token_allow_cmd(token, BPF_BTF_LOAD)) {\n5583:\t\t\t\tbpf_token_put(token);\n5584:\t\t\t\ttoken = NULL;\n5585:\t\t\t}\n5586:\t\t}\n5587:\t\n5588:\t\tif (!bpf_token_capable(token, CAP_BPF)) {\n5589:\t\t\tbpf_token_put(token);\n5590:\t\t\treturn -EPERM;\n5591:\t\t}\n5592:\t\n5593:\t\tbpf_token_put(token);\n5594:\t\n5595:\t\treturn btf_new_fd(attr, uattr, attr_log);\n5596:\t}\n5597:\t\n5598:\t#define BPF_BTF_GET_FD_BY_ID_LAST_FIELD fd_by_id_token_fd\n5599:\t\n5600:\tstatic int bpf_btf_get_fd_by_id(const union bpf_attr *attr)\n5601:\t{\n5602:\t\tstruct bpf_token *token = NULL;\n5603:\t\n5604:\t\tif (CHECK_ATTR(BPF_BTF_GET_FD_BY_ID))\n5605:\t\t\treturn -EINVAL;\n5606:\t\n5607:\t\tif (attr-\u003eopen_flags \u0026 ~BPF_F_TOKEN_FD)\n5608:\t\t\treturn -EINVAL;\n5609:\t\n5610:\t\tif (attr-\u003eopen_flags \u0026 BPF_F_TOKEN_FD) {\n5611:\t\t\ttoken = bpf_token_get_from_fd(attr-\u003efd_by_id_token_fd);\n5612:\t\t\tif (IS_ERR(token))\n"
} |
| 17/2 | 2026/09/26 16:58 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44617 output: 31 thoughts: 33 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and presented as if they were my own mental process: **Diving into btf_new_fd** Right, let's take a look at `btf_new_fd`. I'm going to start by getting a handle on its purpose. I'm expecting this function to be involved in creating file descriptors related to BTF (BPF Type Format) data, likely the mechanism for managing and interacting with BTF objects within the kernel. I'm immediately thinking about things like how it handles memory allocation, error conditions, reference counting (if any), and how it might interface with the broader BPF subsystem. I want to understand the lifecycle of these file descriptors and what kind of data they represent – is it the BTF data itself, a handle to some kernel-internal structure, or both? I need to quickly grasp its role in the larger picture. I wonder about its interaction with `bpf()` syscall and other BPF-related system calls. Is it used only internally or exposed to the user space? |
| 18/2 | 2026/09/26 16:58 | tool | grepper |
0mArgs:{
"Expression": "int btf_new_fd",
"PathPrefix": "kernel/bpf"
}Results: {
"Output": "kernel/bpf/btf.c=8761=static int __btf_new_fd(struct btf *btf)\n--\nkernel/bpf/btf.c-8765-\nkernel/bpf/btf.c:8766:int btf_new_fd(const union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_attr *attr_log)\nkernel/bpf/btf.c-8767-{\n"
} |
| 19/2 | 2026/09/26 16:58 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 45043 output: 22 thoughts: 2208 Thoughts: **BTF Analysis for Fuzzing: A Deep Dive** Okay, so the initial thought process is centered around fuzzing the BTF (BPF Type Format) loading logic, specifically focusing on the new location kinds. I'm starting by tracing the execution path that's involved, and it appears `btf_new_fd` is the entry point, calling `btf_load` and then `btf_parse`, which in turn calls a series of check and resolve functions. My focus right now is on understanding how those check/resolve functions, like `btf_loc_param_check_meta`, `btf_loc_proto_check_meta`, `btf_loc_proto_resolve`, `btf_locsec_check_meta`, and `btf_locsec_resolve` get exercised during the BTF loading process. Given that I have a mandate to identify good fuzzing targets, I'm checking which functions in `kernel/bpf/btf.c` are added or modified. The list confirmed is what I mentioned previously. The critical question here is whether these functions are static or exported. Turns out they're static functions, and they're called via function pointers within structures like `loc_param_ops`, `loc_proto_ops`, and `locsec_ops`. I need to ascertain if syzkaller can target static functions accessed through function pointers, since this is a common anti-fuzzing pattern. The good news is, static functions in .c files are *still* symbols in `vmlinux`, so they *should* be accessible, as long as they aren't inlined (and given their size and the function pointer usage, they almost certainly won't be inlined). So, the next step is to nail down the direct API entrypoints for BTF loading – `btf_new_fd` and `bpf_btf_load`. Based on the guidelines, I'm looking for the specific feature logic and entrypoints. `btf_new_fd` definitely seems like a strong candidate, especially since the static functions mentioned above are called from within the context of that function. Thus, `btf_loc_param_check_meta`, `btf_loc_proto_check_meta`, `btf_loc_proto_resolve`, `btf_locsec_check_meta`, and `btf_locsec_resolve` and `btf_new_fd` are all solid focus symbols. Now, because I am keen on finding subtle bugs and edge cases, I’m digging deeper into how those resolve functions work. Specifically, I'm now looking at `btf_locsec_resolve` and its logic. The crucial part that stands out is how the `next_member` is handled when resolving dependencies. The logic pushes `func_type` onto a resolve stack, and sets `next_member` to `i`, but when dealing with `loc_proto_type_id`, it sets `next_member` to `i + 1`. This subtle difference warrants close attention. Are there situations where this could lead to unintended consequences, especially in the presence of cyclic dependencies within the BTF data? I'll need to carefully craft test cases to exercise these conditional branches to flush out any potential issues. I'm also analyzing `btf_locsec_check_meta` with the suspicion of potential integer overflows. Specifically, I'm looking at whether `sizeof(struct btf_loc) * vlen` could overflow. Given that `struct btf_loc` is 12 bytes and `vlen` is derived from the `t->info`, a potential overflow must be explored. Therefore, I need to check how `btf_type_vlen` is defined to assess the size of `vlen`. |
| 20/2 | 2026/09/26 16:58 | tool | grepper |
0mError:no matches Args: {
"Expression": "define btf_type_vlen"
}Results: {
"error": "no matches"
} |
| 21/2 | 2026/09/26 16:58 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 48102 output: 33 thoughts: 21 Thoughts: Here's my thought process, as if I were considering this myself: **Digging into BTF Length Information** Okay, so I need to get at the length information within a BTF type. I'm pretty sure I've worked with this before, and the core concept revolves around knowing the size of a particular BTF type. I think the key variables I'm looking for are `btf_type_vlen` or maybe just `btf_vlen`. It's possible the naming convention uses a prefix to denote the type of length information, or it may just be the variable `btf_vlen`. I'll need to figure out which one is the relevant one, and what structure holds it. Time to look deeper. |
| 22/2 | 2026/09/26 16:58 | tool | grepper |
0mArgs:{
"Expression": "btf_vlen",
"PathPrefix": "include/linux/btf.h"
}Results: {
"Output": "include/linux/btf.h=444=static inline u32 btf_type_vlen(const struct btf_type *t)\n--\ninclude/linux/btf.h-448-\ninclude/linux/btf.h:449:static inline u32 btf_vlen(const struct btf_type *t)\ninclude/linux/btf.h-450-{\n"
} |
| 23/2 | 2026/09/26 16:58 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 48428 output: 36 thoughts: 21 Thoughts: Here's a first-person summary, tailored for an expert audience, based on the provided "thoughts": **Diving into BTF Header** Okay, time to get my hands dirty. Let's navigate directly to `include/linux/btf.h`. I'm particularly interested in what's happening around line 444. Given my understanding of BTF and the kernel's use of it, I'm expecting to find something crucial there – probably related to a critical data structure definition, a core function, or maybe a key macro that defines the behaviour of some BTF related function. This usually is the start of a deep dive. Line 444… let's see what gems are there. |
| 24/2 | 2026/09/26 16:58 | tool | read-file |
0mArgs:{
"File": "include/linux/btf.h",
"FirstLine": 440,
"LineCount": 20
}Results: {
"Contents": " 440:\t{\n 441:\t\treturn BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_ARRAY;\n 442:\t}\n 443:\t\n 444:\tstatic inline u32 btf_type_vlen(const struct btf_type *t)\n 445:\t{\n 446:\t\treturn BTF_INFO_VLEN(t-\u003einfo);\n 447:\t}\n 448:\t\n 449:\tstatic inline u32 btf_vlen(const struct btf_type *t)\n 450:\t{\n 451:\t\treturn btf_type_vlen(t);\n 452:\t}\n 453:\t\n 454:\tstatic inline u16 btf_func_linkage(const struct btf_type *t)\n 455:\t{\n 456:\t\treturn BTF_INFO_VLEN(t-\u003einfo);\n 457:\t}\n 458:\t\n 459:\tstatic inline bool btf_type_kflag(const struct btf_type *t)\n"
} |
| 25/2 | 2026/09/26 16:58 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 48943 output: 38 thoughts: 30 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Diving into BTF_INFO_VLEN** Alright, so I'm thinking about this `BTF_INFO_VLEN` thing. It's a key element in the BTF (BPF Type Format) structure, and I need to solidify my understanding of it. My first instinct is always to go directly to the source, to the authoritative definition. So, I need to check the definitive header file for BTF definitions. I'll open up `include/uapi/linux/btf.h` and dig into the details. I expect to find the precise definition and documentation there, which should clarify how this field factors into the broader BTF schema. Knowing the underlying representation is crucial for any low-level interaction, so I need to check how the BPF subsystem interprets this value in its overall type metadata processing. |
| 26/2 | 2026/09/26 16:58 | tool | grepper |
0mArgs:{
"Expression": "BTF_INFO_VLEN",
"PathPrefix": "include/uapi/linux/btf.h"
}Results: {
"Output": "include/uapi/linux/btf.h=47=struct btf_type {\n--\ninclude/uapi/linux/btf.h-70-#define BTF_INFO_KIND(info)\t(((info) \u003e\u003e 24) \u0026 0x7f)\ninclude/uapi/linux/btf.h:71:#define BTF_INFO_VLEN(info)\t((info) \u0026 0xffffff)\ninclude/uapi/linux/btf.h-72-#define BTF_INFO_KFLAG(info)\t((info) \u003e\u003e 31)\n"
} |
| 27/2 | 2026/09/26 16:58 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 49357 output: 36 thoughts: 1998 Thoughts: **Deep Dive into BTF Locsec Verification** Okay, so I'm looking at this BTF verification code, specifically the `btf_locsec` handling. The core of it seems to be in `btf_locsec_resolve` and `btf_locsec_check_meta`. First, the code's using `vlen`, and it's a 24-bit value, which is fine, up to 16M. I'm checking the calculations of `meta_needed` and it seems that calculations stay within the 32 bit range. That part checks out. My initial thought was if a huge `vlen` would cause an issue with the `s32` return type of `btf_locsec_check_meta`. However, even with the maximum `vlen`, the calculations remain within the 32-bit range, so that is not a concern. That's good. Now, let's zoom in on `btf_locsec_resolve`. This is where it gets interesting. It looks like it's trying to resolve dependencies, specifically the `func` and `loc_proto` fields within the `btf_loc` structures. The code is using `btf_type_by_id`, which returns `NULL` for invalid type IDs. Okay, so that handles some invalid cases. The key is this `env_type_is_resolved` check and the stack operations. It looks like we push `func_type` onto a stack if it's not yet resolved, then attempt to resolve it later, which seems like a cycle or dependency resolution approach. Okay, so if it's not resolved, we push it onto the stack and it is revisited, but it checks again. So, when `func_type` is resolved and we revisit, then the `loc_proto_type` comes into play. It appears that it pushes `loc_proto_type` onto the stack as well if not resolved, and sets `next_member` appropriately. Now, this is where it gets dicey. The code looks like it's designed to handle nested dependencies, but there is potential for issues. If `loc_proto_type` resolution fails, it would leave the stack and what if there is a cycle here? I see now. It looks like the resolution process happens within `btf_loc_proto_resolve`, which, in turn, doesn't resolve the `param_type` immediately. It merely *checks* their validity. So, the stack's logic relies on whether those checks pass, which, in turn, relies on `env_type_is_resolved`, which, I hope, prevents infinite loops. The code uses `env_stack_pop_resolved`, which removes the current entry. Let's check `btf_loc_proto_resolve` more closely. It goes through the `loc_param` types, and does a check on each of them. However, it seems that there's not an actual *resolution* step here. It just checks and then calls `env_stack_pop_resolved` regardless. This could mean some potential issues with dependencies not being resolved. So, I need to check how `env_stack_pop_resolved` works, as it seems to be the critical control path for resolving these BTF dependencies in a safe way. |
| 28/2 | 2026/09/26 16:58 | tool | grepper |
0mArgs:{
"Expression": "env_stack_pop_resolved",
"PathPrefix": "kernel/bpf/btf.c"
}Results: {
"Output": "kernel/bpf/btf.c=2057=static void env_stack_set_next_member(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2062-\nkernel/bpf/btf.c:2063:static void env_stack_pop_resolved(struct btf_verifier_env *env,\nkernel/bpf/btf.c-2064-\t\t\t\t u32 resolved_type_id,\n--\nkernel/bpf/btf.c=2783=static int btf_modifier_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2819-\nkernel/bpf/btf.c:2820:\tenv_stack_pop_resolved(env, next_type_id, 0);\nkernel/bpf/btf.c-2821-\n--\nkernel/bpf/btf.c=2825=static int btf_var_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2865-\nkernel/bpf/btf.c:2866:\tenv_stack_pop_resolved(env, next_type_id, 0);\nkernel/bpf/btf.c-2867-\n--\nkernel/bpf/btf.c=2871=static int btf_ptr_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2922-\nkernel/bpf/btf.c:2923:\tenv_stack_pop_resolved(env, next_type_id, 0);\nkernel/bpf/btf.c-2924-\n--\nkernel/bpf/btf.c=3130=static int btf_array_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-3189-\nkernel/bpf/btf.c:3190:\tenv_stack_pop_resolved(env, elem_type_id, elem_size * array-\u003enelems);\nkernel/bpf/btf.c-3191-\n--\nkernel/bpf/btf.c=3405=static int btf_struct_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-3470-\nkernel/bpf/btf.c:3471:\tenv_stack_pop_resolved(env, 0, 0);\nkernel/bpf/btf.c-3472-\n--\nkernel/bpf/btf.c=4852=static int btf_loc_proto_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-4879-\nkernel/bpf/btf.c:4880:\tenv_stack_pop_resolved(env, 0, 0);\nkernel/bpf/btf.c-4881-\treturn 0;\n--\nkernel/bpf/btf.c=4970=static int btf_locsec_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5029-\nkernel/bpf/btf.c:5030:\tenv_stack_pop_resolved(env, 0, 0);\nkernel/bpf/btf.c-5031-\treturn 0;\n--\nkernel/bpf/btf.c=5155=static int btf_func_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5165-\nkernel/bpf/btf.c:5166:\tenv_stack_pop_resolved(env, next_type_id, 0);\nkernel/bpf/btf.c-5167-\treturn 0;\n--\nkernel/bpf/btf.c=5316=static int btf_datasec_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5351-\nkernel/bpf/btf.c:5352:\tenv_stack_pop_resolved(env, 0, 0);\nkernel/bpf/btf.c-5353-\treturn 0;\n--\nkernel/bpf/btf.c=5508=static int btf_decl_tag_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5548-\nkernel/bpf/btf.c:5549:\tenv_stack_pop_resolved(env, next_type_id, 0);\nkernel/bpf/btf.c-5550-\n"
} |
| 29/2 | 2026/09/26 16:58 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 53330 output: 41 thoughts: 1761 Thoughts: **Deep Dive into BTF Locsec Resolution** Okay, let's break this down. First, I'm starting with a hunch related to how we validate and resolve BTF types, specifically those tricky `locsec` types. I need to understand the potential for forward references and potential loops. I'm thinking about `btf_resolve_valid`. This seems to be the entry point to check if types should be resolved. My initial question is: does `btf_resolve_valid` adequately handle unresolved types, or does it miss something specific to structures like `locsec`? Next, I'm drilling into `btf_locsec_resolve`. I immediately wonder: does the function type, `func_type`, fetched inside the loop, get verified early enough? Or is there a race condition where an invalid `func_type` slips through? That's when `btf_locsec_check_meta` comes to mind. Yes, it does check, but the crucial point is *when* the check happens and if the checks cover every possible scenario. Then I get fixated on the index `i` of the `loc` in the loop, I wonder if the value is being guarded correctly, and if not, how. The logic seems straightforward, but I need to make sure I don't overlook any edge cases, like what if `loc->func` goes out of bounds. Now, looking deeper at the `btf_locsec_resolve` function, the code checks if `func_type->type` (which is supposed to be a function prototype) is valid. If it's zero or invalid, we're in trouble. `btf_type_by_id(btf, 0)` returns `&btf_void`, which isn't a function prototype. I need to make sure this doesn't lead to issues. And I'm pondering whether the order of parsing can introduce issues: what happens if a type depends on a *yet unparsed* type? This is the core issue of forward references. The `env_type_is_resolved` calls in `btf_locsec_resolve` are the most important part! I'm considering that the `loc_proto_type_id` might be a forward reference. The code *does* push the type onto the stack to resolve it later, which implies that the infrastructure is set up to handle this. But what if it isn't? What if it's a *loop*? I have to consider the risk of pushing and resolving the types on the stack recursively. Then it hits me, I have to go through `btf_locsec_resolve` step by step. I zoom in on how the code pushes `loc_proto_type` onto the stack if it isn't resolved yet. That's the key. But if pushing causes an error, what happens? I need to review how those stack operations work. The code sets `next_member` to `i + 1` *after* pushing. So, if the stack push *succeeds*, the loop continues with the *next* location. This seems reasonable. But if a push *fails*, it returns an error. The crucial question is: will the next loc still be processed correctly? It does not seem like that `next_member` is updated on failure. And then, if `loc_proto_type` is already resolved, the push is skipped. I can see the potential for a race condition or an unexpected path if a push is skipped but something unexpected happens. This is an important distinction. Then, there's another subtle but important point that I have to think about: what if `locsec` *itself* is referenced by another type? Can it happen, and if so, how does the resolution handle this recursive definition? That's what `btf_type_is_resolve_source_only` is for, I have to check. The function has all the problematic types: `var`, `decl_tag`, `datasec`, `loc_proto`, and `locsec`. This means they can only be *source* types. So, `btf_type_is_resolve_source_only` is the answer. I have to read the uses of this function. That is the next step. |
| 30/2 | 2026/09/26 16:58 | tool | grepper |
0mArgs:{
"Expression": "btf_type_is_resolve_source_only",
"PathPrefix": "kernel/bpf/btf.c"
}Results: {
"Output": "kernel/bpf/btf.c=772=const struct btf_type *btf_type_resolve_func_ptr(const struct btf *btf,\n--\nkernel/bpf/btf.c-786- */\nkernel/bpf/btf.c:787:static bool btf_type_is_resolve_source_only(const struct btf_type *t)\nkernel/bpf/btf.c-788-{\n--\nkernel/bpf/btf.c=2783=static int btf_modifier_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2791-\tnext_type = btf_type_by_id(btf, next_type_id);\nkernel/bpf/btf.c:2792:\tif (!next_type || btf_type_is_resolve_source_only(next_type)) {\nkernel/bpf/btf.c-2793-\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid type_id\");\n--\nkernel/bpf/btf.c=2825=static int btf_var_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2833-\tnext_type = btf_type_by_id(btf, next_type_id);\nkernel/bpf/btf.c:2834:\tif (!next_type || btf_type_is_resolve_source_only(next_type)) {\nkernel/bpf/btf.c-2835-\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid type_id\");\n--\nkernel/bpf/btf.c=2871=static int btf_ptr_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2879-\tnext_type = btf_type_by_id(btf, next_type_id);\nkernel/bpf/btf.c:2880:\tif (!next_type || btf_type_is_resolve_source_only(next_type)) {\nkernel/bpf/btf.c-2881-\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid type_id\");\n--\nkernel/bpf/btf.c=3130=static int btf_array_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-3142-\tif (btf_type_nosize_or_null(index_type) ||\nkernel/bpf/btf.c:3143:\t btf_type_is_resolve_source_only(index_type)) {\nkernel/bpf/btf.c-3144-\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid index\");\n--\nkernel/bpf/btf.c-3162-\tif (btf_type_nosize_or_null(elem_type) ||\nkernel/bpf/btf.c:3163:\t btf_type_is_resolve_source_only(elem_type)) {\nkernel/bpf/btf.c-3164-\t\tbtf_verifier_log_type(env, v-\u003et,\n--\nkernel/bpf/btf.c=3405=static int btf_struct_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-3446-\t\tif (btf_type_nosize_or_null(member_type) ||\nkernel/bpf/btf.c:3447:\t\t btf_type_is_resolve_source_only(member_type)) {\nkernel/bpf/btf.c-3448-\t\t\tbtf_verifier_log_member(env, v-\u003et, member,\n--\nkernel/bpf/btf.c=5569=static int btf_func_proto_check(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5591-\nkernel/bpf/btf.c:5592:\t\tif (btf_type_is_resolve_source_only(ret_type)) {\nkernel/bpf/btf.c-5593-\t\t\tbtf_verifier_log_type(env, t, \"Invalid return type\");\n--\nkernel/bpf/btf.c-5634-\nkernel/bpf/btf.c:5635:\t\tif (btf_type_is_resolve_source_only(arg_type)) {\nkernel/bpf/btf.c-5636-\t\t\tbtf_verifier_log_type(env, t, \"Invalid arg#%u\", i + 1);\n"
} |
| 31/2 | 2026/09/26 16:58 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 57272 output: 37 thoughts: 301 Thoughts: **My Current Understanding of BTF Type Resolution** Okay, so I'm digging into this `btf_type_is_resolve_source_only` macro. It seems like it's meant to identify BTF types that *only* serve as sources for resolving other types, and nothing else should be pointing *to* them. Essentially, they're the origin points for type resolution, the headwaters of the data flow. The addition of `btf_type_is_loc_proto` and `btf_type_is_locsec` to this set makes intuitive sense in this context. Wait a minute, though... I need to clarify something. Can `BTF_KIND_LOC_PROTO` actually be *resolved* by `btf_locsec_resolve`? I need to double-check my assumptions. Let me pull up `btf_locsec_resolve`... Ah, yes, there it is: `loc_proto_type_id = loc->loc_proto;`. It grabs the `loc_proto` and checks if it is resolved. Then `env_stack_set_next_member` is called and `env_stack_push` is called to add it to the stack. So, the code *does* explicitly attempt to resolve a `BTF_KIND_LOC_PROTO`! In fact, the code pushes `loc_proto_type` onto the resolve stack. Okay, so that's clear. Now I should probably zoom out a bit to see the bigger picture. Where does this stack fit into the overall BTF validation/resolution process? What does `btf_check_all_types` do, and what's its role in all this? I'll head over to `kernel/bpf/btf.c` and take a look at its implementation. |
| 32/2 | 2026/09/26 16:58 | tool | grepper |
0mArgs:{
"Expression": "btf_check_all_types",
"PathPrefix": "kernel/bpf/btf.c"
}Results: {
"Output": "kernel/bpf/btf.c=5822=static int btf_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5854-\nkernel/bpf/btf.c:5855:static int btf_check_all_types(struct btf_verifier_env *env)\nkernel/bpf/btf.c-5856-{\n--\nkernel/bpf/btf.c=5889=static int btf_parse_type_sec(struct btf_verifier_env *env)\n--\nkernel/bpf/btf.c-5908-\nkernel/bpf/btf.c:5909:\treturn btf_check_all_types(env);\nkernel/bpf/btf.c-5910-}\n"
} |
| 33/2 | 2026/09/26 16:58 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 58220 output: 38 thoughts: 33 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm already quite familiar with the kernel and BPF: **Diving into BTF Type Checking** Right, let's get down to brass tacks. I need to take a look at how the kernel's BTF verification is implemented, specifically. My initial thought is to navigate to the `btf_check_all_types` function. I think that's the right place to begin, since it sounds like the central point for checking the validity of the BTF (BPF Type Format) data that has been loaded. I'm going to jump to line 5855 in `kernel/bpf/btf.c`. That should give me a good overview of the checks performed on the entire BTF type information. I expect to be digging into how the verifier ensures that struct layouts are sound, that pointers are used correctly, and that any dependencies between types are properly resolved. I'll need to understand the logic for any type checking loops or recursive validation. Time to get my hands dirty! |
| 34/2 | 2026/09/26 16:58 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/btf.c",
"FirstLine": 5845,
"LineCount": 45
}Results: {
"Contents": "5845:\t\t/* Final sanity check */\n5846:\t\tif (!err \u0026\u0026 !btf_resolve_valid(env, t, type_id)) {\n5847:\t\t\tbtf_verifier_log_type(env, t, \"Invalid resolve state\");\n5848:\t\t\terr = -EINVAL;\n5849:\t\t}\n5850:\t\n5851:\t\tenv-\u003elog_type_id = save_log_type_id;\n5852:\t\treturn err;\n5853:\t}\n5854:\t\n5855:\tstatic int btf_check_all_types(struct btf_verifier_env *env)\n5856:\t{\n5857:\t\tstruct btf *btf = env-\u003ebtf;\n5858:\t\tconst struct btf_type *t;\n5859:\t\tu32 type_id, i;\n5860:\t\tint err;\n5861:\t\n5862:\t\terr = env_resolve_init(env);\n5863:\t\tif (err)\n5864:\t\t\treturn err;\n5865:\t\n5866:\t\tenv-\u003ephase++;\n5867:\t\tfor (i = btf-\u003ebase_btf ? 0 : 1; i \u003c btf-\u003enr_types; i++) {\n5868:\t\t\ttype_id = btf-\u003estart_id + i;\n5869:\t\t\tt = btf_type_by_id(btf, type_id);\n5870:\t\n5871:\t\t\tenv-\u003elog_type_id = type_id;\n5872:\t\t\tif (btf_type_needs_resolve(t) \u0026\u0026\n5873:\t\t\t !env_type_is_resolved(env, type_id)) {\n5874:\t\t\t\terr = btf_resolve(env, t, type_id);\n5875:\t\t\t\tif (err)\n5876:\t\t\t\t\treturn err;\n5877:\t\t\t}\n5878:\t\n5879:\t\t\tif (btf_type_is_func_proto(t)) {\n5880:\t\t\t\terr = btf_func_proto_check(env, t);\n5881:\t\t\t\tif (err)\n5882:\t\t\t\t\treturn err;\n5883:\t\t\t}\n5884:\t\t}\n5885:\t\n5886:\t\treturn 0;\n5887:\t}\n5888:\t\n5889:\tstatic int btf_parse_type_sec(struct btf_verifier_env *env)\n"
} |
| 35/2 | 2026/09/26 16:58 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 59188 output: 225 thoughts: 1154 Thoughts: **Analysis of BTF Location Type Resolution for Fuzzing** Alright, let's break this down. I've been looking at this BTF code, specifically around the introduction of the `loc_proto` and `locsec` types. I see that within `btf_check_all_types`, the new logic now ensures that these location types are resolved via `btf_resolve`, which in turn calls `btf_loc_proto_resolve` and `btf_locsec_resolve`. This integration seems pretty clean and tightly coupled with the core BTF verifier. It's clear this is all wired into the kernel, specifically in `kernel/bpf/btf.c`, `include/linux/btf.h`, and `include/uapi/linux/btf.h`. This is prime BPF verifier/parser territory and accessed through the `bpf(BPF_BTF_LOAD, ...)` syscall. Given that `CONFIG_BPF_SYSCALL=y` is likely the standard, this is very accessible both in QEMU and other standard environments. Now, let's assess the fuzzing criteria. Reachability? Check. We're talking about the `sys_bpf(BPF_BTF_LOAD, ...)` syscall. Syzkaller's already got a description for it. Core Subsystem? BPF, right in `kernel/bpf/btf.c`. Complexity? Absolutely. We're talking about new BTF kinds, parsing, size checks, validation loops, resolution stack operations, and pointer math. All of the above lead me to believe that this subsystem is **worth fuzzing**. Let's confirm the necessary configurations. It's looking like `CONFIG_BPF_SYSCALL` is the only requirement, as `BPF_SYSCALL` directly selects BPF without further dependencies. The code added to `kernel/bpf/btf.c` isn't guarded by any other configurations either - so we're good there. It builds `kernel/bpf/btf.o`, no conditional compilation based on other config options. Regarding focus symbols, I'm keying in on these newly added functions: `btf_loc_param_check_meta`, `btf_loc_proto_check_meta`, `btf_loc_proto_resolve`, `btf_locsec_check_meta`, and `btf_locsec_resolve`, along with `btf_new_fd` (or `bpf_btf_load`). These symbols are unique to this new BTF parsing logic and are not likely ubiquitous. I took a peek at the other modified files. Looks like kernel-side modifications are all in `kernel/bpf/btf.c`, `include/linux/btf.h`, and `include/uapi/linux/btf.h`, so we're staying on track here. Alright, double-checking, we've established: * **WorthFuzzing**: True * **FocusSymbols**: `btf_loc_param_check_meta`, `btf_loc_proto_check_meta`, `btf_loc_proto_resolve`, `btf_locsec_check_meta`, `btf_locsec_resolve`, `bpf_btf_load` * **EnableConfigs**: `BPF_SYSCALL` * **Reasoning**: Reachability via a syscall, core BPF subsystem, newly added complexity (location type parsing and resolution) justifies this as a fuzzing target. I'm confident we have a solid case here. I'll call `set-results`. |
| 36/2 | 2026/09/26 16:59 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"BPF_SYSCALL"
],
"FocusSymbols": [
"btf_loc_param_check_meta",
"btf_loc_proto_check_meta",
"btf_loc_proto_resolve",
"btf_locsec_check_meta",
"btf_locsec_resolve",
"bpf_btf_load"
],
"Reasoning": "The patch introduces three new BTF kinds (BTF_KIND_LOC_PARAM, BTF_KIND_LOC_PROTO, and BTF_KIND_LOCSEC) along with their metadata validation and resolution logic in kernel/bpf/btf.c. This code executes in the core BPF subsystem and is directly reachable from userspace via the BPF_BTF_LOAD command of the bpf() system call. Fuzzing is warranted to test bounds checking, type resolution, validation loops, and error handling for malformed BTF payloads.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"BPF_SYSCALL"
],
"FocusSymbols": [
"btf_loc_param_check_meta",
"btf_loc_proto_check_meta",
"btf_loc_proto_resolve",
"btf_locsec_check_meta",
"btf_locsec_resolve",
"bpf_btf_load"
],
"Reasoning": "The patch introduces three new BTF kinds (BTF_KIND_LOC_PARAM, BTF_KIND_LOC_PROTO, and BTF_KIND_LOCSEC) along with their metadata validation and resolution logic in kernel/bpf/btf.c. This code executes in the core BPF subsystem and is directly reachable from userspace via the BPF_BTF_LOAD command of the bpf() system call. Fuzzing is warranted to test bounds checking, type resolution, validation loops, and error handling for malformed BTF payloads.",
"WorthFuzzing": true
} |
| 37/1 | 2026/09/26 16:59 | action | If |
1mArgs:{
"WorthFuzzing": true
}Results: null |
| 38/2 | 2026/09/26 16:59 | agent | kmsan-evaluator |
1mModel:core Args: null Results: {
"KMSANReasoning": "The patch series introduces support for three new BTF kinds (BTF_KIND_LOC_PARAM, BTF_KIND_LOC_PROTO, and BTF_KIND_LOCSEC) representing parameter and site location debug information.\n\nThe changes in the kernel proper are confined to BTF validation and resolution logic in `kernel/bpf/btf.c` and related header definitions:\n1. `btf_loc_param_check_meta()`, `btf_loc_proto_check_meta()`, and `btf_locsec_check_meta()` validate metadata size, flags, and names.\n2. `btf_loc_proto_resolve()` and `btf_locsec_resolve()` validate relationships and references between types.\n3. The rest of the patch updates userspace tooling (`bpftool`, `libbpf`, and selftests).\n\nAny potential bugs in the verifier parsing logic (e.g., buffer indexing or malformed metadata length calculations) would manifest as out-of-bounds reads or invalid pointer dereferences, which are detected by KASAN. The patch does not allocate or copy uninitialized kernel memory, does not modify kernel-to-user copy paths (no info-leaks), and does not introduce conditional branches on uninitialized variables. Therefore, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 006d1ad744f5dd16dd22415c5cef649e66ef7f74
Author: syz-cluster <triage@syzkaller.com>
Date: Sat Sep 26 16:57:26 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst
index 004aa1058d857..29de1222c3e73 100644
--- a/Documentation/bpf/btf.rst
+++ b/Documentation/bpf/btf.rst
@@ -88,6 +88,9 @@ sequentially and type id is assigned to each recognized type starting from id
#define BTF_KIND_DECL_TAG 17 /* Decl Tag */
#define BTF_KIND_TYPE_TAG 18 /* Type Tag */
#define BTF_KIND_ENUM64 19 /* Enumeration up to 64-bit values */
+ #define BTF_KIND_LOC_PARAM 20 /* Location description (register, const etc) */
+ #define BTF_KIND_LOC_PROTO 21 /* Set of location parameters for site */
+ #define BTF_KIND_LOCSEC 22 /* Section with site descriptions */
Note that the type section encodes debug info, not just pure types.
``BTF_KIND_FUNC`` is not a type, and it represents a defined subprogram.
@@ -104,11 +107,13 @@ Each type contains the following common data::
* decl_tag and type_tag
*/
__u32 info;
- /* "size" is used by INT, ENUM, STRUCT, UNION and ENUM64.
+ /* "size" is used by INT, ENUM, STRUCT, UNION, ENUM64 and
+ * LOC_PARAM.
* "size" tells the size of the type it is describing.
*
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
- * FUNC, FUNC_PROTO, DECL_TAG and TYPE_TAG.
+ * FUNC, FUNC_PROTO, DECL_TAG and TYPE_TAG. It is unused by
+ * LOC_PROTO and LOCSEC.
* "type" is a type_id referring to another type.
*/
union {
@@ -563,6 +568,80 @@ The ``btf_enum64`` encoding:
If the original enum value is signed and the size is less than 8,
that value will be sign extended into 8 bytes.
+2.2.20 BTF_KIND_LOC_PARAM
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``struct btf_type`` encoding requirement:
+ * ``name_off``: 0
+ * ``info.kind_flag``: 0
+ * ``info.kind``: BTF_KIND_LOC_PARAM
+ * ``info.vlen``: number of 32-bit location value words
+ * ``size``: size in bytes of the represented parameter: 1 through 16
+
+``btf_type`` is followed by a ``struct btf_loc_param`` and ``info.vlen``
+number of 32-bit value words.::
+
+ struct btf_loc_param {
+ __u32 flags;
+ __u32 values[];
+ };
+
+The ``flags`` field describes how to interpret ``values``:
+
+ * ``BTF_LOC_PARAM_CONST`` describes a constant; the value is stored in
+ low-word, high-word order when it requires 64 bits.
+ * ``BTF_LOC_PARAM_ADDR | BTF_LOC_PARAM_CONST`` describes an address offset
+ relative to the runtime base address of the kernel or module image.
+ * ``BTF_LOC_PARAM_REG`` with one word describes a register number; with two
+ words it describes a multi-register parameter.
+ * ``BTF_LOC_PARAM_REG | BTF_LOC_PARAM_OFFSET`` describes an address held in
+ a register plus an offset. Adding ``BTF_LOC_PARAM_DEREF`` dereferences
+ that address. ``BTF_LOC_PARAM_REG | BTF_LOC_PARAM_DEREF`` with one word
+ dereferences the value held in the register.
+ * ``BTF_LOC_PARAM_SIGNED`` makes a constant or offset signed. A constant's
+ signed width is ``size``. For a register-relative offset, its signed
+ width is the number of offset value words times 32 bits.
+
+2.2.21 BTF_KIND_LOC_PROTO
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``struct btf_type`` encoding requirement:
+ * ``name_off``: 0
+ * ``info.kind_flag``: 0
+ * ``info.kind``: BTF_KIND_LOC_PROTO
+ * ``info.vlen``: number of function parameter locations
+ * ``type``: 0
+
+``btf_type`` is followed by ``info.vlen`` number of ``__u32`` BTF type IDs.
+Each entry corresponds to a function parameter at an inline site. An entry is
+either 0, meaning that no location information is available, or the type ID
+of a ``BTF_KIND_LOC_PARAM``.
+
+2.2.22 BTF_KIND_LOCSEC
+~~~~~~~~~~~~~~~~~~~~~~
+
+``struct btf_type`` encoding requirement:
+ * ``name_off``: offset to a valid ELF section name
+ * ``info.kind_flag``: 0
+ * ``info.kind``: BTF_KIND_LOCSEC
+ * ``info.vlen``: number of inline sites in the section
+ * ``type``: 0
+
+``btf_type`` is followed by ``info.vlen`` number of ``struct btf_loc``.::
+
+ struct btf_loc {
+ __u32 func;
+ __u32 loc_proto;
+ __u32 offset;
+ };
+
+The ``func`` field is the non-zero type ID of the ``BTF_KIND_FUNC`` being
+described. ``loc_proto`` is the non-zero type ID of the associated
+``BTF_KIND_LOC_PROTO``. ``offset`` is the location address offset relative to
+the runtime base address of the ELF section associated with the LOCSEC.
+For example, a LOCSEC named ``inline.text`` contains records for ``.text``
+whose offsets are relative to the runtime base of that section.
+
2.3 Constant Values
-------------------
diff --git a/include/linux/btf.h b/include/linux/btf.h
index ddd0f4f32d24b..f29ed358b7a8c 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -261,6 +261,11 @@ const char *btf_type_str(const struct btf_type *t);
i < btf_type_vlen(datasec_type); \
i++, member++)
+#define for_each_loc(i, locsec_type, member) \
+ for (i = 0, member = btf_type_loc_secinfo(locsec_type); \
+ i < btf_type_vlen(locsec_type); \
+ i++, member++)
+
static inline bool btf_type_is_ptr(const struct btf_type *t)
{
return BTF_INFO_KIND(t->info) == BTF_KIND_PTR;
@@ -329,6 +334,21 @@ static inline u64 btf_enum64_value(const struct btf_enum64 *e)
return ((u64)e->val_hi32 << 32) | e->val_lo32;
}
+static inline struct btf_loc_param *btf_loc_param(const struct btf_type *t)
+{
+ return (struct btf_loc_param *)(t + 1);
+}
+
+static inline __u32 *btf_loc_proto_params(const struct btf_type *t)
+{
+ return (__u32 *)(t + 1);
+}
+
+static inline struct btf_loc *btf_type_loc_secinfo(const struct btf_type *t)
+{
+ return (struct btf_loc *)(t + 1);
+}
+
static inline bool btf_is_composite(const struct btf_type *t)
{
u16 kind = btf_kind(t);
@@ -559,7 +579,7 @@ struct btf_field_desc {
/* member struct size, or zero, if no members */
int m_sz;
/* repeated per-member offsets */
- int m_off_cnt, m_offs[1];
+ int m_off_cnt, m_offs[2];
};
struct btf_field_iter {
diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
index 618167cab4e61..11d65871d163e 100644
--- a/include/uapi/linux/btf.h
+++ b/include/uapi/linux/btf.h
@@ -54,8 +54,8 @@ struct btf_type {
* decl_tag and type_tag
*/
__u32 info;
- /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
- * "size" tells the size of the type it is describing.
+ /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC, ENUM64
+ * and LOC_PARAM. "size" tells the size of the type it is describing.
*
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
* FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG.
@@ -92,7 +92,9 @@ enum {
BTF_KIND_DECL_TAG = 17, /* Decl Tag */
BTF_KIND_TYPE_TAG = 18, /* Type Tag */
BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */
-
+ BTF_KIND_LOC_PARAM = 20, /* Location parameter information */
+ BTF_KIND_LOC_PROTO = 21, /* Location prototype for site */
+ BTF_KIND_LOCSEC = 22, /* Location section */
NR_BTF_KINDS,
BTF_KIND_MAX = NR_BTF_KINDS - 1,
};
@@ -212,4 +214,79 @@ struct btf_enum64 {
__u32 val_hi32;
};
+/*
+ * BTF_KIND_LOC_PARAM is followed by a single "struct btf_loc_param"
+ * that contains flags specifying the contents of the vlen-specified
+ * number of 4-byte values that follow.
+ */
+struct btf_loc_param {
+ __u32 flags;
+ __u32 values[];
+};
+
+/*
+ * The combination of size, vlen and flags gives us the means to interpret
+ * the following vlen-specified set of 4-byte values:
+ *
+ * - a BTF_LOC_PARAM_CONST is a constant value; combination
+ * of size, vlen and _SIGNED flag determines it. If the value requires
+ * 64 bits it is stored in {lo,hi} order.
+ * - a BTF_LOC_PARAM_ADDR|BTF_LOC_PARAM_CONST is an address that should be
+ * normalized with respect to kernel/module base address.
+ * - a BTF_LOC_PARAM_REG with vlen 1 is a simple register number;
+ * with vlen 2 it is a multi-register parameter. Register numbers are
+ * numbers derived from DW_OP_reg values, i.e. 0 is DW_OP_reg0; since
+ * DW_OP_fbreg has its own special DW_OP_value and DW_OP_regx can refer
+ * to an arbitrary register number, we reserve BTF_LOC_PARAM_FBREG for
+ * the frame base register to avoid collisions.
+ * - a _REG | OFFSET describes an address without dereferencing it.
+ * - a _REG | DEREF with vlen 1 dereferences the value in the register
+ * number specified.
+ * - a REG | DEREF | OFFSET with vlen > 1 specifies the register number
+ * in the first 4-byte value and the offset in the remainder.
+ * In the case of REG and OFFSET combinations, the OFFSET has the width
+ * of the value words while the type size describes the represented parameter,
+ * so for example a REG | DEREF | OFFSET with size 8 and vlen 2 would be
+ * an 8-byte register dereference with signed 4-byte offset, since the vlen 2
+ * values consist of a register value and the signed value.
+ */
+enum btf_loc_param_flags {
+ BTF_LOC_PARAM_SIGNED = 0x1,
+ BTF_LOC_PARAM_CONST = 0x2,
+ BTF_LOC_PARAM_ADDR = 0x4,
+ BTF_LOC_PARAM_REG = 0x8,
+ BTF_LOC_PARAM_DEREF = 0x10,
+ BTF_LOC_PARAM_OFFSET = 0x20,
+};
+
+enum {
+ BTF_LOC_PARAM_FBREG = 0xffffffff
+};
+
+/*
+ * BTF_KIND_LOC_PROTO specifies location prototypes; i.e. how locations relate
+ * to parameters; a struct btf_type of BTF_KIND_LOC_PROTO is followed by a
+ * vlen-specified number of __u32 BTF type ids which specify the associated
+ * BTF_KIND_LOC_PARAM for each function parameter associated with the
+ * location. The type should either be 0 (no location info) or point at
+ * a BTF_KIND_LOC_PARAM.
+ */
+
+/*
+ * BTF_KIND_LOCSEC consists of vlen-specified number of "struct btf_loc"
+ * containing location site-specific information for a specific ELF section;
+ * for example locations in ".text" are in a LOCSEC named "inline.text".
+ *
+ * - function (func)
+ * - location prototype type id (loc_proto)
+ * - address offset (offset) relative to the runtime base address of the
+ * ELF section associated with the LOCSEC
+ */
+
+struct btf_loc {
+ __u32 func;
+ __u32 loc_proto;
+ __u32 offset;
+};
+
#endif /* _UAPI__LINUX_BTF_H__ */
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 314ecb0e593b0..2ef28c6f14f93 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -346,6 +346,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
+ [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
+ [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
+ [BTF_KIND_LOCSEC] = "LOCSEC",
};
const char *btf_type_str(const struct btf_type *t)
@@ -518,11 +521,27 @@ static bool btf_type_is_decl_tag(const struct btf_type *t)
return BTF_INFO_KIND(t->info) == BTF_KIND_DECL_TAG;
}
+static bool btf_type_is_loc_param(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info) == BTF_KIND_LOC_PARAM;
+}
+
+static bool btf_type_is_loc_proto(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info) == BTF_KIND_LOC_PROTO;
+}
+
+static bool btf_type_is_locsec(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info) == BTF_KIND_LOCSEC;
+}
+
static bool btf_type_nosize(const struct btf_type *t)
{
return btf_type_is_void(t) || btf_type_is_fwd(t) ||
btf_type_is_func(t) || btf_type_is_func_proto(t) ||
- btf_type_is_decl_tag(t);
+ btf_type_is_decl_tag(t) || btf_type_is_loc_param(t) ||
+ btf_type_is_loc_proto(t) || btf_type_is_locsec(t);
}
static bool btf_type_nosize_or_null(const struct btf_type *t)
@@ -769,7 +788,9 @@ static bool btf_type_is_resolve_source_only(const struct btf_type *t)
{
return btf_type_is_var(t) ||
btf_type_is_decl_tag(t) ||
- btf_type_is_datasec(t);
+ btf_type_is_datasec(t) ||
+ btf_type_is_loc_proto(t) ||
+ btf_type_is_locsec(t);
}
/* What types need to be resolved?
@@ -797,7 +818,9 @@ static bool btf_type_needs_resolve(const struct btf_type *t)
btf_type_is_var(t) ||
btf_type_is_func(t) ||
btf_type_is_decl_tag(t) ||
- btf_type_is_datasec(t);
+ btf_type_is_datasec(t) ||
+ btf_type_is_loc_proto(t) ||
+ btf_type_is_locsec(t);
}
/* t->size can be used */
@@ -4730,6 +4753,293 @@ static const struct btf_kind_operations enum64_ops = {
.show = btf_enum64_show,
};
+static s32 btf_loc_param_check_meta(struct btf_verifier_env *env,
+ const struct btf_type *t,
+ u32 meta_left)
+{
+ const struct btf_loc_param *p = btf_loc_param(t);
+ u32 size, meta_needed, vlen = btf_vlen(t);
+
+ meta_needed = sizeof(*p) + sizeof(__u32) * vlen;
+ if (meta_left < meta_needed) {
+ btf_verifier_log_basic(env, t,
+ "meta_left:%u meta_needed:%u",
+ meta_left, meta_needed);
+ return -EINVAL;
+ }
+
+ if (t->name_off) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+ size = t->size;
+ if (!size || size > 16) {
+ btf_verifier_log_type(env, t, "Unexpected size");
+ return -EINVAL;
+ }
+
+ if (btf_type_kflag(t)) {
+ btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+ return -EINVAL;
+ }
+
+ btf_verifier_log_type(env, t, NULL);
+
+ return meta_needed;
+}
+
+static void btf_loc_param_log(struct btf_verifier_env *env,
+ const struct btf_type *t)
+{
+ const struct btf_loc_param *p = btf_loc_param(t);
+ u32 i, vlen = btf_vlen(t);
+
+ btf_verifier_log(env, "size=%u vlen=%u flags=0x%x", t->size, vlen, p->flags);
+ for (i = 0; i < vlen; i++)
+ btf_verifier_log(env, ", %u", p->values[i]);
+}
+
+static const struct btf_kind_operations loc_param_ops = {
+ .check_meta = btf_loc_param_check_meta,
+ .resolve = btf_df_resolve,
+ .check_member = btf_df_check_member,
+ .check_kflag_member = btf_df_check_kflag_member,
+ .log_details = btf_loc_param_log,
+ .show = btf_df_show,
+};
+
+static s32 btf_loc_proto_check_meta(struct btf_verifier_env *env,
+ const struct btf_type *t,
+ u32 meta_left)
+{
+ u32 meta_needed;
+
+ meta_needed = sizeof(__u32) * btf_type_vlen(t);
+
+ if (meta_left < meta_needed) {
+ btf_verifier_log_basic(env, t,
+ "meta_left:%u meta_needed:%u",
+ meta_left, meta_needed);
+ return -EINVAL;
+ }
+
+ if (t->name_off) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+
+ if (btf_type_kflag(t)) {
+ btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+ return -EINVAL;
+ }
+
+ btf_verifier_log_type(env, t, NULL);
+
+ return meta_needed;
+}
+
+static void btf_loc_proto_log(struct btf_verifier_env *env,
+ const struct btf_type *t)
+{
+ const __u32 *params = btf_loc_proto_params(t);
+ u32 i, nr_params = btf_type_vlen(t);
+
+ btf_verifier_log(env, "vlen=%u", nr_params);
+ for (i = 0; i < nr_params; i++, params++)
+ btf_verifier_log(env, ", %u", *params);
+}
+
+static int btf_loc_proto_resolve(struct btf_verifier_env *env,
+ const struct resolve_vertex *v)
+{
+ const struct btf_type *t = v->t;
+ const __u32 *params = btf_loc_proto_params(t);
+ u32 i, nr_params = btf_type_vlen(t);
+ struct btf *btf = env->btf;
+
+ if (t->type) {
+ btf_verifier_log_type(env, t, "Invalid loc_proto type");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nr_params; i++) {
+ const struct btf_type *param_type;
+ u32 param_type_id = params[i];
+
+ if (!param_type_id)
+ continue;
+
+ param_type = btf_type_by_id(btf, param_type_id);
+ if (!param_type || !btf_type_is_loc_param(param_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid loc_param#%u", i + 1);
+ return -EINVAL;
+ }
+ }
+
+ env_stack_pop_resolved(env, 0, 0);
+ return 0;
+}
+
+static const struct btf_kind_operations loc_proto_ops = {
+ .check_meta = btf_loc_proto_check_meta,
+ .resolve = btf_loc_proto_resolve,
+ .check_member = btf_df_check_member,
+ .check_kflag_member = btf_df_check_kflag_member,
+ .log_details = btf_loc_proto_log,
+ .show = btf_df_show,
+};
+
+__printf(4, 5)
+static void btf_verifier_log_loc(struct btf_verifier_env *env,
+ const struct btf_type *locsec_type,
+ const struct btf_loc *loc,
+ const char *fmt, ...)
+{
+ struct bpf_verifier_log *log = &env->log;
+ va_list args;
+
+ if (!bpf_verifier_log_needed(log))
+ return;
+ if (log->level == BPF_LOG_KERNEL && !fmt)
+ return;
+ if (env->phase != CHECK_META)
+ btf_verifier_log_type(env, locsec_type, NULL);
+
+ __btf_verifier_log(log, "\t func=%u loc_proto=%u offset=%u",
+ loc->func, loc->loc_proto, loc->offset);
+ if (fmt && *fmt) {
+ __btf_verifier_log(log, " ");
+ va_start(args, fmt);
+ bpf_verifier_vlog(log, fmt, args);
+ va_end(args);
+ }
+
+ __btf_verifier_log(log, "\n");
+}
+
+static s32 btf_locsec_check_meta(struct btf_verifier_env *env,
+ const struct btf_type *t,
+ u32 meta_left)
+{
+ u32 i, meta_needed, vlen = btf_type_vlen(t);
+ const struct btf_loc *loc;
+
+ meta_needed = sizeof(struct btf_loc) * vlen;
+
+ if (meta_left < meta_needed) {
+ btf_verifier_log_basic(env, t,
+ "meta_left:%u meta_needed:%u",
+ meta_left, meta_needed);
+ return -EINVAL;
+ }
+
+ if (btf_type_kflag(t)) {
+ btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+ return -EINVAL;
+ }
+
+ if (!t->name_off ||
+ !btf_name_valid_section(env->btf, t->name_off)) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+
+ for_each_loc(i, t, loc) {
+ /* A loc func, loc proto cannot be in type void */
+ if (!loc->func || !BTF_TYPE_ID_VALID(loc->func)) {
+ btf_verifier_log_loc(env, t, loc, "Invalid func");
+ return -EINVAL;
+ }
+ if (!loc->loc_proto || !BTF_TYPE_ID_VALID(loc->loc_proto)) {
+ btf_verifier_log_loc(env, t, loc, "Invalid loc_proto");
+ return -EINVAL;
+ }
+ btf_verifier_log_loc(env, t, loc, NULL);
+ }
+
+ return meta_needed;
+}
+
+static void btf_locsec_log(struct btf_verifier_env *env,
+ const struct btf_type *t)
+{
+ btf_verifier_log(env, "vlen=%u", btf_type_vlen(t));
+}
+
+static int btf_locsec_resolve(struct btf_verifier_env *env,
+ const struct resolve_vertex *v)
+{
+ const struct btf_type *t = v->t;
+ const struct btf_loc *loc;
+ struct btf *btf = env->btf;
+ u32 i;
+
+ if (t->type) {
+ btf_verifier_log_type(env, t, "Invalid locsec type");
+ return -EINVAL;
+ }
+
+ env->resolve_mode = RESOLVE_TBD;
+ for (i = v->next_member, loc = btf_type_loc_secinfo(t) + i;
+ i < btf_type_vlen(t); i++, loc++) {
+ const struct btf_type *func_type, *func_proto_type;
+ const struct btf_type *loc_proto_type;
+ u32 func_type_id = loc->func;
+ u32 loc_proto_type_id = loc->loc_proto;
+ u32 proto_vlen;
+
+ func_type = btf_type_by_id(btf, func_type_id);
+ if (!func_type || !btf_type_is_func(func_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid func#%u", i + 1);
+ return -EINVAL;
+ }
+ func_proto_type = btf_type_by_id(btf, func_type->type);
+ if (!func_proto_type || !btf_type_is_func_proto(func_proto_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid func#%u", i + 1);
+ return -EINVAL;
+ }
+ proto_vlen = btf_vlen(func_proto_type);
+
+ if (!env_type_is_resolved(env, func_type_id)) {
+ env_stack_set_next_member(env, i);
+ return env_stack_push(env, func_type, func_type_id);
+ }
+
+ loc_proto_type = btf_type_by_id(btf, loc_proto_type_id);
+ if (!loc_proto_type || !btf_type_is_loc_proto(loc_proto_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid loc_proto#%u", i + 1);
+ return -EINVAL;
+ }
+ if (proto_vlen != btf_vlen(loc_proto_type)) {
+ btf_verifier_log_type(env, t,
+ "Mismatched vlen for loc_proto#%u", i + 1);
+ return -EINVAL;
+ }
+
+ if (!env_type_is_resolved(env, loc_proto_type_id)) {
+ env_stack_set_next_member(env, i + 1);
+ return env_stack_push(env, loc_proto_type,
+ loc_proto_type_id);
+ }
+ }
+
+ env_stack_pop_resolved(env, 0, 0);
+ return 0;
+}
+
+static const struct btf_kind_operations locsec_ops = {
+ .check_meta = btf_locsec_check_meta,
+ .resolve = btf_locsec_resolve,
+ .check_member = btf_df_check_member,
+ .check_kflag_member = btf_df_check_kflag_member,
+ .log_details = btf_locsec_log,
+ .show = btf_df_show,
+};
+
static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,
const struct btf_type *t,
u32 meta_left)
@@ -5399,6 +5709,9 @@ static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = &decl_tag_ops,
[BTF_KIND_TYPE_TAG] = &modifier_ops,
[BTF_KIND_ENUM64] = &enum64_ops,
+ [BTF_KIND_LOC_PARAM] = &loc_param_ops,
+ [BTF_KIND_LOC_PROTO] = &loc_proto_ops,
+ [BTF_KIND_LOCSEC] = &locsec_ops,
};
static s32 btf_check_meta(struct btf_verifier_env *env,
@@ -5473,7 +5786,8 @@ static bool btf_resolve_valid(struct btf_verifier_env *env,
if (!env_type_is_resolved(env, type_id))
return false;
- if (btf_type_is_struct(t) || btf_type_is_datasec(t))
+ if (btf_type_is_struct(t) || btf_type_is_datasec(t) ||
+ btf_type_is_loc_proto(t) || btf_type_is_locsec(t))
return !btf_resolved_type_id(btf, type_id) &&
!btf_resolved_type_size(btf, type_id);
diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
index cf75a7fa2d6bc..c6dc445cd4a19 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
@@ -16,7 +16,7 @@ SYNOPSIS
**bpftool** [*OPTIONS*] **btf** *COMMAND*
-*OPTIONS* := { |COMMON_OPTIONS| | { **-B** | **--base-btf** } }
+*OPTIONS* := { |COMMON_OPTIONS| | [{ **-B** | **--base-btf**} *FILE*]... }
*COMMANDS* := { **dump** | **help** }
@@ -87,7 +87,10 @@ OPTIONS
objects for kernel modules. To avoid duplicating all kernel symbols
required by modules, BTF objects for modules are "split", they are
built incrementally on top of the kernel (vmlinux) BTF object. So the
- base BTF reference should usually point to the kernel BTF.
+ base BTF reference should usually point to the kernel BTF. Multiple
+ base BTF objects can be passed, where the first is assumed to be the
+ root BTF, followed by split BTF based upon it, followed by split
+ BTF based upon the first split BTF and so on.
When the main BTF object to process (for example, the module BTF to
dump) is passed as a *FILE*, bpftool attempts to autodetect the path
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index bca0a3982f09b..e29c8a84e224c 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -29,6 +29,7 @@
#define MAX_ROOT_IDS 16
#define MAX_BTF_FILES 64
+#define MAX_LOC_PARAM_WORDS 8
static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_UNKN] = "UNKNOWN",
@@ -51,6 +52,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
+ [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
+ [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
+ [BTF_KIND_LOCSEC] = "LOCSEC",
};
struct sort_datum {
@@ -117,6 +121,137 @@ static int btf_kind_safe(int kind)
return kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;
}
+static void btf_loc_param_reg_str(__u32 reg, char *str, size_t sz)
+{
+ if (reg == BTF_LOC_PARAM_FBREG)
+ snprintf(str, sz, "fbreg");
+ else
+ snprintf(str, sz, "reg%u", reg);
+}
+
+static void btf_loc_param_raw_str(const struct btf_loc_param *p, __u32 vlen,
+ char *str, size_t sz)
+{
+ __u32 i, nr_words = min(vlen, (__u32)MAX_LOC_PARAM_WORDS);
+ size_t off = 0;
+
+ if (!sz)
+ return;
+
+ off += snprintf(str + off, sz - off, "raw=[");
+ for (i = 0; i < nr_words && off < sz; i++)
+ off += snprintf(str + off, sz - off, "%s0x%08x",
+ i ? ", " : "", p->values[i]);
+ if (vlen > nr_words && off < sz)
+ off += snprintf(str + off, sz - off, ", ...");
+ if (off < sz)
+ snprintf(str + off, sz - off, "]");
+}
+
+static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz)
+{
+ const struct btf_loc_param *p;
+ __u32 i = 0, vlen;
+ __u64 value;
+ __u32 value_size;
+ bool negative = false;
+ char regs[32] = {};
+ char num[32] = {};
+ const char *op = "";
+
+ if (!t || !btf_is_loc_param(t)) {
+ snprintf(str, sz, "<invalid>");
+ return;
+ }
+
+ p = btf_loc_param(t);
+ vlen = btf_vlen(t);
+
+ if (p->flags & BTF_LOC_PARAM_REG) {
+ __u32 nregs = (p->flags == BTF_LOC_PARAM_REG) ? vlen : 1;
+
+ if (nregs > vlen) {
+ btf_loc_param_raw_str(p, vlen, str, sz);
+ return;
+ }
+
+ switch (nregs) {
+ case 2:
+ btf_loc_param_reg_str(p->values[0], regs, sizeof(regs));
+ snprintf(regs + strlen(regs), sizeof(regs) - strlen(regs),
+ ", ");
+ btf_loc_param_reg_str(p->values[1],
+ regs + strlen(regs),
+ sizeof(regs) - strlen(regs));
+ break;
+ case 1:
+ btf_loc_param_reg_str(p->values[0], regs, sizeof(regs));
+ break;
+ default:
+ btf_loc_param_raw_str(p, vlen, str, sz);
+ return;
+ }
+ i += nregs;
+ }
+ if (p->flags & (BTF_LOC_PARAM_CONST|BTF_LOC_PARAM_OFFSET)) {
+ switch (vlen - i) {
+ case 1:
+ value_size = sizeof(p->values[0]);
+ value = p->values[i];
+ break;
+ case 2:
+ value_size = 2 * sizeof(p->values[0]);
+ value = ((__u64)p->values[i + 1] << 32) | p->values[i];
+ break;
+ default:
+ btf_loc_param_raw_str(p, vlen, str, sz);
+ return;
+ }
+ i = vlen;
+ if (p->flags & BTF_LOC_PARAM_SIGNED) {
+ /*
+ * size describes the represented parameter, so it
+ * describes a constant's signed width. An offset
+ * is determined by its value words after the register
+ * number.
+ */
+ __u32 size = p->flags & BTF_LOC_PARAM_OFFSET ?
+ value_size : t->size;
+ __u32 bits = size * 8;
+
+ /*
+ * Since we represent constant values in hex, we
+ * need to determine if the value is negative so
+ * we can prepend a "-", and also fix the value
+ * to be positive so we can have - 0x<value>.
+ */
+ if (size && size <= sizeof(value)) {
+ if (size < sizeof(value))
+ value &= (1ULL << bits) - 1;
+ negative = value & (1ULL << (bits - 1));
+ if (negative)
+ value = size == sizeof(value) ? -value :
+ (1ULL << bits) - value;
+ }
+ }
+ snprintf(num, sizeof(num), "0x%llx%s", (unsigned long long)value,
+ p->flags & BTF_LOC_PARAM_ADDR ? " (addr)" : "");
+ }
+ if (i != vlen) {
+ btf_loc_param_raw_str(p, vlen, str, sz);
+ return;
+ }
+ if (num[0])
+ op = regs[0] ? (negative ? " - " : " + ") : negative ? "-" : "";
+
+ snprintf(str, sz, "%s%s%s%s%s",
+ p->flags & BTF_LOC_PARAM_DEREF ? "*(" : "",
+ regs,
+ op,
+ num,
+ p->flags & BTF_LOC_PARAM_DEREF ? ")" : "");
+}
+
static int dump_btf_type(const struct btf *btf, __u32 id,
const struct btf_type *t)
{
@@ -415,6 +550,95 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
}
break;
}
+ case BTF_KIND_LOC_PARAM: {
+ const struct btf_loc_param *p = btf_loc_param(t);
+ __u32 vlen = btf_vlen(t);
+ char param_str[256] = {};
+
+ btf_loc_param_str(t, param_str, sizeof(param_str));
+
+ if (json_output) {
+ jsonw_uint_field(w, "size", t->size);
+ jsonw_uint_field(w, "flags", p->flags);
+ jsonw_uint_field(w, "vlen", vlen);
+ jsonw_string_field(w, "values", param_str);
+ } else {
+ printf(" size=%u flags=0x%x vlen=%u values='%s'",
+ t->size, p->flags, vlen, param_str);
+ }
+ break;
+ }
+ case BTF_KIND_LOC_PROTO: {
+ __u32 *params = btf_loc_proto_params(t);
+ __u32 i, vlen = btf_vlen(t);
+
+ if (json_output) {
+ jsonw_uint_field(w, "vlen", vlen);
+ jsonw_name(w, "params");
+ jsonw_start_array(w);
+ } else {
+ printf(" vlen=%u", vlen);
+ }
+
+ for (i = 0; i < vlen; i++, params++) {
+ const struct btf_type *p;
+ char param_str[256] = {};
+
+ if (*params) {
+ p = btf__type_by_id(btf, *params);
+ btf_loc_param_str(p, param_str, sizeof(param_str));
+ } else {
+ snprintf(param_str, sizeof(param_str), "<unavailable>");
+ }
+ if (json_output) {
+ jsonw_start_object(w);
+ jsonw_uint_field(w, "type_id", *params);
+ jsonw_string_field(w, "value", param_str);
+ jsonw_end_object(w);
+ } else {
+ printf("\n\ttype_id=%u value='%s'", *params, param_str);
+ }
+ }
+ if (json_output)
+ jsonw_end_array(w);
+ break;
+ }
+
+ case BTF_KIND_LOCSEC: {
+ struct btf_loc *locs = btf_locsec_locs(t);
+ __u32 i, vlen = btf_vlen(t);
+
+ if (json_output) {
+ jsonw_uint_field(w, "vlen", vlen);
+ jsonw_name(w, "locs");
+ jsonw_start_array(w);
+ } else {
+ printf(" vlen=%u", vlen);
+ }
+
+ for (i = 0; i < vlen; i++, locs++) {
+ const struct btf_type *f = btf__type_by_id(btf, locs->func);
+ const char *name = "<invalid>";
+
+ if (f && btf_is_func(f))
+ name = btf_str(btf, f->name_off);
+
+ if (json_output) {
+ jsonw_start_object(w);
+ jsonw_uint_field(w, "func_type_id", locs->func);
+ jsonw_string_field(w, "name", name);
+ jsonw_uint_field(w, "loc_proto_type_id", locs->loc_proto);
+ jsonw_uint_field(w, "offset", locs->offset);
+ jsonw_end_object(w);
+ } else {
+ printf("\n\tname='%s' func_type_id=%u loc_proto_type_id=%u offset=%u",
+ name, locs->func, locs->loc_proto, locs->offset);
+ }
+ }
+ if (json_output)
+ jsonw_end_array(w);
+ break;
+ }
default:
break;
}
@@ -1545,7 +1769,7 @@ static int do_help(int argc, char **argv)
" " HELP_SPEC_MAP "\n"
" " HELP_SPEC_PROGRAM "\n"
" " HELP_SPEC_OPTIONS " |\n"
- " {-B|--base-btf} }\n"
+ " {[{-B|--base-btf} FILE]... }}\n"
"",
bin_name, "btf");
diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
index 5ababd8f7d0a0..83884b21e7081 100644
--- a/tools/bpf/bpftool/main.c
+++ b/tools/bpf/bpftool/main.c
@@ -466,6 +466,7 @@ int main(int argc, char **argv)
{ "base-btf", required_argument, NULL, 'B' },
{ 0 }
};
+ struct btf *new_base_btf = NULL, *root_base_btf = NULL;
bool version_requested = false;
int opt, ret;
@@ -515,12 +516,16 @@ int main(int argc, char **argv)
verifier_logs = true;
break;
case 'B':
- base_btf = btf__parse(optarg, NULL);
- if (!base_btf) {
+ /* handle multi-split BTF */
+ new_base_btf = btf__parse_split(optarg, base_btf);
+ if (!new_base_btf) {
p_err("failed to parse base BTF at '%s': %d\n",
optarg, -errno);
return -1;
}
+ base_btf = new_base_btf;
+ if (!root_base_btf)
+ root_base_btf = base_btf;
break;
case 'L':
use_loader = true;
@@ -567,7 +572,20 @@ int main(int argc, char **argv)
if (json_output)
jsonw_destroy(&json_wtr);
- btf__free(base_btf);
+ while (base_btf) {
+ bool is_root = base_btf == root_base_btf;
+
+ new_base_btf = (struct btf *)btf__base_btf(base_btf);
+ btf__free(base_btf);
+ /*
+ * Do not free base BTF that is an owned .BTF.base ; leads
+ * to a double-free, so only free as far as the root base
+ * we explicitly read with -B above.
+ */
+ if (is_root)
+ break;
+ base_btf = new_base_btf;
+ }
return ret;
}
diff --git a/tools/include/uapi/linux/btf.h b/tools/include/uapi/linux/btf.h
index 618167cab4e61..11d65871d163e 100644
--- a/tools/include/uapi/linux/btf.h
+++ b/tools/include/uapi/linux/btf.h
@@ -54,8 +54,8 @@ struct btf_type {
* decl_tag and type_tag
*/
__u32 info;
- /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
- * "size" tells the size of the type it is describing.
+ /* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC, ENUM64
+ * and LOC_PARAM. "size" tells the size of the type it is describing.
*
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
* FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG.
@@ -92,7 +92,9 @@ enum {
BTF_KIND_DECL_TAG = 17, /* Decl Tag */
BTF_KIND_TYPE_TAG = 18, /* Type Tag */
BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */
-
+ BTF_KIND_LOC_PARAM = 20, /* Location parameter information */
+ BTF_KIND_LOC_PROTO = 21, /* Location prototype for site */
+ BTF_KIND_LOCSEC = 22, /* Location section */
NR_BTF_KINDS,
BTF_KIND_MAX = NR_BTF_KINDS - 1,
};
@@ -212,4 +214,79 @@ struct btf_enum64 {
__u32 val_hi32;
};
+/*
+ * BTF_KIND_LOC_PARAM is followed by a single "struct btf_loc_param"
+ * that contains flags specifying the contents of the vlen-specified
+ * number of 4-byte values that follow.
+ */
+struct btf_loc_param {
+ __u32 flags;
+ __u32 values[];
+};
+
+/*
+ * The combination of size, vlen and flags gives us the means to interpret
+ * the following vlen-specified set of 4-byte values:
+ *
+ * - a BTF_LOC_PARAM_CONST is a constant value; combination
+ * of size, vlen and _SIGNED flag determines it. If the value requires
+ * 64 bits it is stored in {lo,hi} order.
+ * - a BTF_LOC_PARAM_ADDR|BTF_LOC_PARAM_CONST is an address that should be
+ * normalized with respect to kernel/module base address.
+ * - a BTF_LOC_PARAM_REG with vlen 1 is a simple register number;
+ * with vlen 2 it is a multi-register parameter. Register numbers are
+ * numbers derived from DW_OP_reg values, i.e. 0 is DW_OP_reg0; since
+ * DW_OP_fbreg has its own special DW_OP_value and DW_OP_regx can refer
+ * to an arbitrary register number, we reserve BTF_LOC_PARAM_FBREG for
+ * the frame base register to avoid collisions.
+ * - a _REG | OFFSET describes an address without dereferencing it.
+ * - a _REG | DEREF with vlen 1 dereferences the value in the register
+ * number specified.
+ * - a REG | DEREF | OFFSET with vlen > 1 specifies the register number
+ * in the first 4-byte value and the offset in the remainder.
+ * In the case of REG and OFFSET combinations, the OFFSET has the width
+ * of the value words while the type size describes the represented parameter,
+ * so for example a REG | DEREF | OFFSET with size 8 and vlen 2 would be
+ * an 8-byte register dereference with signed 4-byte offset, since the vlen 2
+ * values consist of a register value and the signed value.
+ */
+enum btf_loc_param_flags {
+ BTF_LOC_PARAM_SIGNED = 0x1,
+ BTF_LOC_PARAM_CONST = 0x2,
+ BTF_LOC_PARAM_ADDR = 0x4,
+ BTF_LOC_PARAM_REG = 0x8,
+ BTF_LOC_PARAM_DEREF = 0x10,
+ BTF_LOC_PARAM_OFFSET = 0x20,
+};
+
+enum {
+ BTF_LOC_PARAM_FBREG = 0xffffffff
+};
+
+/*
+ * BTF_KIND_LOC_PROTO specifies location prototypes; i.e. how locations relate
+ * to parameters; a struct btf_type of BTF_KIND_LOC_PROTO is followed by a
+ * vlen-specified number of __u32 BTF type ids which specify the associated
+ * BTF_KIND_LOC_PARAM for each function parameter associated with the
+ * location. The type should either be 0 (no location info) or point at
+ * a BTF_KIND_LOC_PARAM.
+ */
+
+/*
+ * BTF_KIND_LOCSEC consists of vlen-specified number of "struct btf_loc"
+ * containing location site-specific information for a specific ELF section;
+ * for example locations in ".text" are in a LOCSEC named "inline.text".
+ *
+ * - function (func)
+ * - location prototype type id (loc_proto)
+ * - address offset (offset) relative to the runtime base address of the
+ * ELF section associated with the LOCSEC
+ */
+
+struct btf_loc {
+ __u32 func;
+ __u32 loc_proto;
+ __u32 offset;
+};
+
#endif /* _UAPI__LINUX_BTF_H__ */
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 908bd344229d5..512b3962f75f8 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -57,6 +57,9 @@ static struct btf_layout layouts[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = { sizeof(struct btf_decl_tag), 0, 0 },
[BTF_KIND_TYPE_TAG] = { 0, 0, 0 },
[BTF_KIND_ENUM64] = { 0, sizeof(struct btf_enum64), 0 },
+[BTF_KIND_LOC_PARAM] = { sizeof(struct btf_loc_param), sizeof(__u32), 0 },
+[BTF_KIND_LOC_PROTO] = { 0, sizeof(__u32), 0 },
+[BTF_KIND_LOCSEC] = { 0, sizeof(struct btf_loc), 0 },
};
struct btf {
@@ -486,6 +489,12 @@ static int btf_type_size(const struct btf *btf, const struct btf_type *t)
return base_size + vlen * sizeof(struct btf_var_secinfo);
case BTF_KIND_DECL_TAG:
return base_size + sizeof(struct btf_decl_tag);
+ case BTF_KIND_LOC_PARAM:
+ return base_size + sizeof(struct btf_loc_param) + vlen * sizeof(__u32);
+ case BTF_KIND_LOC_PROTO:
+ return base_size + vlen * sizeof(__u32);
+ case BTF_KIND_LOCSEC:
+ return base_size + vlen * sizeof(struct btf_loc);
default:
return btf_type_size_unknown(btf, t);
}
@@ -501,12 +510,15 @@ static void btf_bswap_type_base(struct btf_type *t)
static int btf_bswap_type_rest(struct btf_type *t)
{
struct btf_var_secinfo *v;
+ struct btf_loc_param *lp;
struct btf_enum64 *e64;
struct btf_member *m;
struct btf_array *a;
struct btf_param *p;
struct btf_enum *e;
+ struct btf_loc *l;
__u32 vlen = btf_vlen(t);
+ __u32 *d;
int i;
switch (btf_kind(t)) {
@@ -569,6 +581,23 @@ static int btf_bswap_type_rest(struct btf_type *t)
case BTF_KIND_DECL_TAG:
btf_decl_tag(t)->component_idx = bswap_32(btf_decl_tag(t)->component_idx);
return 0;
+ case BTF_KIND_LOC_PARAM:
+ lp = btf_loc_param(t);
+ lp->flags = bswap_32(lp->flags);
+ for (i = 0, d = (__u32 *)(lp + 1); i < vlen; i++, d++)
+ *d = bswap_32(*d);
+ return 0;
+ case BTF_KIND_LOC_PROTO:
+ for (i = 0, d = btf_loc_proto_params(t); i < vlen; i++, d++)
+ *d = bswap_32(*d);
+ return 0;
+ case BTF_KIND_LOCSEC:
+ for (i = 0, l = btf_locsec_locs(t); i < vlen; i++, l++) {
+ l->func = bswap_32(l->func);
+ l->loc_proto = bswap_32(l->loc_proto);
+ l->offset = bswap_32(l->offset);
+ }
+ return 0;
default:
pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t));
return -EINVAL;
@@ -745,6 +774,33 @@ static int btf_validate_type(const struct btf *btf, const struct btf_type *t, __
}
break;
}
+ case BTF_KIND_LOC_PARAM:
+ break;
+ case BTF_KIND_LOC_PROTO: {
+ __u32 *p = btf_loc_proto_params(t);
+
+ n = btf_vlen(t);
+ for (i = 0; i < n; i++, p++) {
+ err = btf_validate_id(btf, *p, id);
+ if (err)
+ return err;
+ }
+ break;
+ }
+ case BTF_KIND_LOCSEC: {
+ const struct btf_loc *l = btf_locsec_locs(t);
+
+ n = btf_vlen(t);
+ for (i = 0; i < n; i++, l++) {
+ if (!err)
+ err = btf_validate_id(btf, l->func, id);
+ if (!err)
+ err = btf_validate_id(btf, l->loc_proto, id);
+ if (err)
+ return err;
+ }
+ break;
+ }
default:
/* Kind may be represented in kind layout information. */
if (btf_type_size_unknown(btf, t) < 0) {
@@ -3344,6 +3400,243 @@ int btf__add_decl_attr(struct btf *btf, const char *value, int ref_type_id,
return btf_add_decl_tag(btf, value, ref_type_id, component_idx, 1);
}
+/*
+ * Append new BTF_KIND_LOC_PARAM with specified size and flags. Values are
+ * added via btf__add_loc_param_value().
+ *
+ * - *byte_sz* - size of the type, in bytes (1 through 16);
+ * - *flags* - combination of enum btf_loc_param_flags values
+ *
+ * Returns:
+ * - >0, type ID of newly added BTF type;
+ * - <0, on error.
+ */
+int btf__add_loc_param(struct btf *btf, __u32 byte_sz, __u32 flags)
+{
+ struct btf_loc_param *p;
+ struct btf_type *t;
+ int sz, err;
+
+ if (!byte_sz || byte_sz > 16)
+ return libbpf_err(-EINVAL);
+
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ sz = sizeof(struct btf_type) + sizeof(*p);
+ t = btf_add_type_mem(btf, sz);
+ if (!t)
+ return libbpf_err(-ENOMEM);
+
+ t->name_off = 0;
+ t->info = btf_type_info(BTF_KIND_LOC_PARAM, 0, 0);
+ t->size = byte_sz;
+
+ p = btf_loc_param(t);
+ p->flags = flags;
+
+ return btf_commit_type(btf, sz);
+}
+
+/*
+ * Append *value* to existing BTF_KIND_LOC_PARAM.
+ *
+ * Returns:
+ * - 0 on success
+ * - <0, on error.
+ */
+int btf__add_loc_param_value(struct btf *btf, __u32 value)
+{
+ struct btf_type *t;
+ int sz, err;
+ __u32 *v;
+
+ /* last type should be BTF_KIND_LOC_PARAM */
+ if (btf->nr_types == 0)
+ return libbpf_err(-EINVAL);
+ t = btf_last_type(btf);
+ if (!btf_is_loc_param(t))
+ return libbpf_err(-EINVAL);
+
+ /* decompose and invalidate raw data */
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ sz = sizeof(value);
+ v = btf_add_type_mem(btf, sz);
+ if (!v)
+ return libbpf_err(-ENOMEM);
+ *v = value;
+
+ /* update parent type's vlen */
+ t = btf_last_type(btf);
+ err = btf_type_inc_vlen(t);
+ if (err)
+ return libbpf_err(err);
+
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
+ return 0;
+}
+
+/*
+ * Append new BTF_KIND_LOC_PROTO
+ *
+ * The prototype is then populated with 0 or more BTF_KIND_LOC_PARAMs via
+ * btf__add_loc_proto_param(); similar to how btf__add_func_param() adds
+ * parameters to a FUNC_PROTO.
+ *
+ * Returns:
+ * - >0, type ID of newly added BTF type;
+ * - <0, on error.
+ */
+int btf__add_loc_proto(struct btf *btf)
+{
+ struct btf_type *t;
+ int err;
+
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ t = btf_add_type_mem(btf, sizeof(struct btf_type));
+ if (!t)
+ return libbpf_err(-ENOMEM);
+
+ t->name_off = 0;
+ t->info = btf_type_info(BTF_KIND_LOC_PROTO, 0, 0);
+ t->size = 0;
+
+ return btf_commit_type(btf, sizeof(struct btf_type));
+}
+
+/*
+ * Append a BTF_KIND_LOC_PARAM id/0 to BTF_KIND_LOC_PROTO.
+ *
+ * Returns:
+ * - 0 on success;
+ * - <0, on error.
+
+ */
+int btf__add_loc_proto_param(struct btf *btf, __u32 id)
+{
+ struct btf_type *t;
+ int sz, err;
+ __u32 *p;
+
+ if (validate_type_id(id))
+ return libbpf_err(-EINVAL);
+
+ /* last type should be BTF_KIND_LOC_PROTO */
+ if (btf->nr_types == 0)
+ return libbpf_err(-EINVAL);
+ t = btf_last_type(btf);
+ if (!btf_is_loc_proto(t))
+ return libbpf_err(-EINVAL);
+
+ /* decompose and invalidate raw data */
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ sz = sizeof(__u32);
+ p = btf_add_type_mem(btf, sz);
+ if (!p)
+ return libbpf_err(-ENOMEM);
+ *p = id;
+
+ /* update parent type's vlen */
+ t = btf_last_type(btf);
+ err = btf_type_inc_vlen(t);
+ if (err)
+ return libbpf_err(err);
+
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
+ return 0;
+}
+
+/*
+ * Append new BTF_KIND_LOCSEC type with:
+ * - *name* - non-empty/non-NULL name;
+ *
+ * Location section is initially empty. Location info can be added with
+ * btf__add_locsec_loc() calls, after btf__add_locsec() succeeds.
+ *
+ * Returns:
+ * - >0, type ID of newly added BTF type;
+ * - <0, on error.
+ */
+
+int btf__add_locsec(struct btf *btf, const char *name)
+{
+ struct btf_type *t;
+ int name_off = 0;
+ int err;
+
+ /* non-empty name */
+ if (str_is_empty(name))
+ return libbpf_err(-EINVAL);
+
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ t = btf_add_type_mem(btf, sizeof(struct btf_type));
+ if (!t)
+ return libbpf_err(-ENOMEM);
+
+ name_off = btf__add_str(btf, name);
+ if (name_off < 0)
+ return libbpf_err(name_off);
+ t->name_off = name_off;
+ t->info = btf_type_info(BTF_KIND_LOCSEC, 0, 0);
+ t->size = 0;
+
+ return btf_commit_type(btf, sizeof(struct btf_type));
+}
+
+int btf__add_locsec_loc(struct btf *btf, __u32 func, __u32 loc_proto,
+ __u32 offset)
+{
+ struct btf_type *t;
+ struct btf_loc *l;
+ int sz, err;
+
+ if (validate_type_id(func) || validate_type_id(loc_proto))
+ return libbpf_err(-EINVAL);
+
+ /* last type should be BTF_KIND_LOCSEC */
+ if (btf->nr_types == 0)
+ return libbpf_err(-EINVAL);
+ t = btf_last_type(btf);
+ if (!btf_is_locsec(t))
+ return libbpf_err(-EINVAL);
+
+ /* decompose and invalidate raw data */
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ sz = sizeof(*l);
+ l = btf_add_type_mem(btf, sz);
+ if (!l)
+ return libbpf_err(-ENOMEM);
+
+ l->func = func;
+ l->loc_proto = loc_proto;
+ l->offset = offset;
+
+ /* update parent type's vlen */
+ t = btf_last_type(btf);
+ err = btf_type_inc_vlen(t);
+ if (err)
+ return libbpf_err(err);
+
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
+ return 0;
+}
+
struct btf_ext_sec_info_param {
__u32 off;
__u32 len;
@@ -4114,8 +4407,8 @@ static struct btf_dedup *btf_dedup_new(struct btf *btf, const struct btf_dedup_o
for (i = 1; i < type_cnt; i++) {
struct btf_type *t = btf_type_by_id(d->btf, i);
- /* VAR and DATASEC are never deduped and are self-canonical */
- if (btf_is_var(t) || btf_is_datasec(t))
+ /* VAR, DATASEC and LOCSEC are never deduped and are self-canonical */
+ if (btf_is_var(t) || btf_is_datasec(t) || btf_is_locsec(t))
d->map[i] = i;
else
d->map[i] = BTF_UNPROCESSED_ID;
@@ -4395,6 +4688,47 @@ static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2)
btf_is_any_enum(t1) && btf_is_any_enum(t2);
}
+static long btf_hash_loc_param(struct btf_type *t)
+{
+ struct btf_loc_param *p = btf_loc_param(t);
+ long h = btf_hash_common(t);
+ int i, vlen = btf_vlen(t);
+
+ h = hash_combine(h, p->flags);
+
+ for (i = 0; i < vlen; i++)
+ h = hash_combine(h, p->values[i]);
+ return h;
+}
+
+static long btf_hash_loc_proto(struct btf_type *t)
+{
+ __u32 *p = btf_loc_proto_params(t);
+ long h = btf_hash_common(t);
+ int i, vlen = btf_vlen(t);
+
+ for (i = 0; i < vlen; i++, p++)
+ h = hash_combine(h, *p);
+ return h;
+}
+
+static bool btf_equal_loc_param(struct btf_type *t1, struct btf_type *t2)
+{
+ struct btf_loc_param *p1 = btf_loc_param(t1);
+ struct btf_loc_param *p2 = btf_loc_param(t2);
+ int i, vlen = btf_vlen(t1);
+
+ if (!btf_equal_common(t1, t2))
+ return false;
+ if (p1->flags != p2->flags)
+ return false;
+ for (i = 0; i < vlen; i++) {
+ if (p1->values[i] != p2->values[i])
+ return false;
+ }
+ return true;
+}
+
/*
* Calculate type signature hash of STRUCT/UNION, ignoring referenced type IDs,
* as referenced type IDs equivalence is established separately during type
@@ -4589,7 +4923,8 @@ static int btf_dedup_prep(struct btf_dedup *d)
switch (btf_kind(t)) {
case BTF_KIND_VAR:
case BTF_KIND_DATASEC:
- /* VAR and DATASEC are never hash/deduplicated */
+ case BTF_KIND_LOCSEC:
+ /* VAR DATASEC and LOCSEC are never hash/deduplicated */
continue;
case BTF_KIND_CONST:
case BTF_KIND_VOLATILE:
@@ -4622,6 +4957,12 @@ static int btf_dedup_prep(struct btf_dedup *d)
case BTF_KIND_FUNC_PROTO:
h = btf_hash_fnproto(t);
break;
+ case BTF_KIND_LOC_PARAM:
+ h = btf_hash_loc_param(t);
+ break;
+ case BTF_KIND_LOC_PROTO:
+ h = btf_hash_loc_proto(t);
+ break;
default:
pr_debug("unknown kind %d for type [%d]\n", btf_kind(t), type_id);
return -EINVAL;
@@ -4664,6 +5005,8 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
case BTF_KIND_DATASEC:
case BTF_KIND_DECL_TAG:
case BTF_KIND_TYPE_TAG:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
return 0;
case BTF_KIND_INT:
@@ -4713,6 +5056,18 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
}
break;
+ case BTF_KIND_LOC_PARAM:
+ h = btf_hash_loc_param(t);
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = hash_entry->value;
+ cand = btf_type_by_id(d->btf, cand_id);
+ if (btf_equal_loc_param(t, cand)) {
+ new_id = cand_id;
+ break;
+ }
+ }
+ break;
+
default:
return -EINVAL;
}
@@ -5145,6 +5500,13 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
return 1;
}
+ case BTF_KIND_LOC_PARAM:
+ return btf_equal_loc_param(cand_type, canon_type);
+
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
+ return 0;
+
default:
return -EINVAL;
}
@@ -5489,6 +5851,37 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
break;
}
+ case BTF_KIND_LOC_PROTO: {
+ __u32 *p1, *p2;
+ __u32 i, vlen;
+
+ p1 = btf_loc_proto_params(t);
+ vlen = btf_vlen(t);
+
+ for (i = 0; i < vlen; i++, p1++) {
+ ref_type_id = btf_dedup_ref_type(d, *p1);
+ if (ref_type_id < 0)
+ return ref_type_id;
+ *p1 = ref_type_id;
+ }
+
+ h = btf_hash_loc_proto(t);
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = hash_entry->value;
+ cand = btf_type_by_id(d->btf, cand_id);
+ if (!btf_equal_common(t, cand))
+ continue;
+ vlen = btf_vlen(cand);
+ p1 = btf_loc_proto_params(t);
+ p2 = btf_loc_proto_params(cand);
+ if (memcmp(p1, p2, vlen * sizeof(__u32)) == 0) {
+ new_id = cand_id;
+ break;
+ }
+ }
+ break;
+ }
+
default:
return -EINVAL;
}
@@ -5970,8 +6363,11 @@ static int btf_add_distilled_type_ids(struct btf_distill *dist, __u32 i)
case BTF_KIND_CONST:
case BTF_KIND_RESTRICT:
case BTF_KIND_VOLATILE:
+ case BTF_KIND_FUNC:
case BTF_KIND_FUNC_PROTO:
case BTF_KIND_TYPE_TAG:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
dist->id_map[*id] = *id;
break;
default:
@@ -5997,7 +6393,7 @@ static int btf_add_distilled_type_ids(struct btf_distill *dist, __u32 i)
static int btf_add_distilled_types(struct btf_distill *dist)
{
- bool adding_to_base = dist->pipe.dst->start_id == 1;
+ bool adding_to_base = dist->pipe.dst->base_btf == NULL;
int id = btf__type_cnt(dist->pipe.dst);
struct btf_type *t;
int i, err = 0;
@@ -6065,8 +6461,12 @@ static int btf_add_distilled_types(struct btf_distill *dist)
case BTF_KIND_CONST:
case BTF_KIND_RESTRICT:
case BTF_KIND_VOLATILE:
+ case BTF_KIND_FUNC:
case BTF_KIND_FUNC_PROTO:
case BTF_KIND_TYPE_TAG:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
/* All other types are added to split BTF. */
if (adding_to_base)
continue;
diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
index 587172c0de089..57f12630c5d15 100644
--- a/tools/lib/bpf/btf.h
+++ b/tools/lib/bpf/btf.h
@@ -274,6 +274,20 @@ LIBBPF_API int btf__add_decl_tag(struct btf *btf, const char *value, int ref_typ
LIBBPF_API int btf__add_decl_attr(struct btf *btf, const char *value, int ref_type_id,
int component_idx);
+/* location construction APIs */
+LIBBPF_API int btf__add_loc_param(struct btf *btf, __u32 size, __u32 flags);
+
+LIBBPF_API int btf__add_loc_param_value(struct btf *btf, __u32 value);
+
+LIBBPF_API int btf__add_loc_proto(struct btf *btf);
+
+LIBBPF_API int btf__add_loc_proto_param(struct btf *btf, __u32 id);
+
+LIBBPF_API int btf__add_locsec(struct btf *btf, const char *name);
+
+LIBBPF_API int btf__add_locsec_loc(struct btf *btf, __u32 func, __u32 loc_proto,
+ __u32 offset);
+
struct btf_dedup_opts {
size_t sz;
/* optional .BTF.ext info to dedup along the main BTF info */
@@ -431,6 +445,12 @@ btf_dump__dump_type_data(struct btf_dump *d, __u32 id,
#define BTF_KIND_DECL_TAG 17 /* Decl Tag */
#define BTF_KIND_TYPE_TAG 18 /* Type Tag */
#define BTF_KIND_ENUM64 19 /* Enum for up-to 64bit values */
+#define BTF_KIND_LOC_PARAM 20 /* Parameter at location */
+#define BTF_KIND_LOC_PROTO 21 /* Parameter set at location */
+#define BTF_KIND_LOCSEC 22 /* Section containing location info */
+
+struct btf_loc_param;
+struct btf_loc;
static inline __u16 btf_kind(const struct btf_type *t)
{
@@ -569,6 +589,21 @@ static inline bool btf_is_any_enum(const struct btf_type *t)
return btf_is_enum(t) || btf_is_enum64(t);
}
+static inline bool btf_is_loc_param(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_LOC_PARAM;
+}
+
+static inline bool btf_is_loc_proto(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_LOC_PROTO;
+}
+
+static inline bool btf_is_locsec(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_LOCSEC;
+}
+
static inline bool btf_kind_core_compat(const struct btf_type *t1,
const struct btf_type *t2)
{
@@ -683,6 +718,21 @@ static inline struct btf_decl_tag *btf_decl_tag(const struct btf_type *t)
return (struct btf_decl_tag *)(t + 1);
}
+static inline struct btf_loc_param *btf_loc_param(const struct btf_type *t)
+{
+ return (struct btf_loc_param *)(t + 1);
+}
+
+static inline __u32 *btf_loc_proto_params(const struct btf_type *t)
+{
+ return (__u32 *)(t + 1);
+}
+
+static inline struct btf_loc *btf_locsec_locs(const struct btf_type *t)
+{
+ return (struct btf_loc *)(t + 1);
+}
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
index 123c448f20c74..fa995c02a1705 100644
--- a/tools/lib/bpf/btf_dump.c
+++ b/tools/lib/bpf/btf_dump.c
@@ -328,6 +328,9 @@ static int btf_dump_mark_referenced(struct btf_dump *d)
case BTF_KIND_ENUM64:
case BTF_KIND_FWD:
case BTF_KIND_FLOAT:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
break;
case BTF_KIND_VOLATILE:
@@ -609,6 +612,9 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)
case BTF_KIND_VAR:
case BTF_KIND_DATASEC:
case BTF_KIND_DECL_TAG:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
d->type_states[id].order_state = ORDERED;
return 0;
@@ -2525,6 +2531,9 @@ static int btf_dump_dump_type_data(struct btf_dump *d,
case BTF_KIND_FUNC:
case BTF_KIND_FUNC_PROTO:
case BTF_KIND_DECL_TAG:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
err = btf_dump_unsupported_data(d, t, id);
break;
case BTF_KIND_INT:
diff --git a/tools/lib/bpf/btf_iter.c b/tools/lib/bpf/btf_iter.c
index 9a6c822c22944..199063b55ebe6 100644
--- a/tools/lib/bpf/btf_iter.c
+++ b/tools/lib/bpf/btf_iter.c
@@ -29,6 +29,7 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t,
case BTF_KIND_FLOAT:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
+ case BTF_KIND_LOC_PARAM:
it->desc = (struct btf_field_desc) {};
break;
case BTF_KIND_FWD:
@@ -71,6 +72,20 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t,
1, {offsetof(struct btf_var_secinfo, type)}
};
break;
+ case BTF_KIND_LOC_PROTO:
+ it->desc = (struct btf_field_desc) {
+ 0, {},
+ sizeof(__u32),
+ 1, {0}};
+ break;
+ case BTF_KIND_LOCSEC:
+ it->desc = (struct btf_field_desc) {
+ 0, {},
+ sizeof(struct btf_loc),
+ 2, {offsetof(struct btf_loc, func),
+ offsetof(struct btf_loc, loc_proto)}};
+ break;
+
default:
return -EINVAL;
}
@@ -94,6 +109,9 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t,
case BTF_KIND_DECL_TAG:
case BTF_KIND_TYPE_TAG:
case BTF_KIND_DATASEC:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
it->desc = (struct btf_field_desc) {
1, {offsetof(struct btf_type, name_off)}
};
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 2f53afc985cfb..930588950ab39 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -2483,6 +2483,9 @@ static const char *__btf_kind_str(__u16 kind)
case BTF_KIND_DECL_TAG: return "decl_tag";
case BTF_KIND_TYPE_TAG: return "type_tag";
case BTF_KIND_ENUM64: return "enum64";
+ case BTF_KIND_LOC_PARAM: return "loc_param";
+ case BTF_KIND_LOC_PROTO: return "loc_proto";
+ case BTF_KIND_LOCSEC: return "locsec";
default: return "unknown";
}
}
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 7a84dd00ce95f..18d27d20102ec 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -463,6 +463,12 @@ LIBBPF_1.8.0 {
bpf_program__attach_tracing_multi;
bpf_program__clear_flags;
bpf_program__clone;
+ btf__add_loc_param;
+ btf__add_loc_param_value;
+ btf__add_loc_proto;
+ btf__add_loc_proto_param;
+ btf__add_locsec;
+ btf__add_locsec_loc;
btf__find_by_name_kind_own;
btf__new_empty_opts;
} LIBBPF_1.7.0;
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index cb4d962338441..546f65b95cf4d 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -580,7 +580,7 @@ struct btf_field_desc {
/* member struct size, or zero, if no members */
int m_sz;
/* repeated per-member offsets */
- int m_off_cnt, m_offs[1];
+ int m_off_cnt, m_offs[2];
};
struct btf_field_iter {
diff --git a/tools/testing/selftests/bpf/btf_helpers.c b/tools/testing/selftests/bpf/btf_helpers.c
index 1c1c2c26690a7..85a4d15d1dd1c 100644
--- a/tools/testing/selftests/bpf/btf_helpers.c
+++ b/tools/testing/selftests/bpf/btf_helpers.c
@@ -27,11 +27,14 @@ static const char * const btf_kind_str_mapping[] = {
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
+ [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
+ [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
+ [BTF_KIND_LOCSEC] = "LOCSEC",
};
static const char *btf_kind_str(__u16 kind)
{
- if (kind > BTF_KIND_ENUM64)
+ if (kind > BTF_KIND_LOCSEC)
return "UNKNOWN";
return btf_kind_str_mapping[kind];
}
@@ -203,6 +206,36 @@ int fprintf_btf_type_raw(FILE *out, const struct btf *btf, __u32 id)
fprintf(out, " type_id=%u component_idx=%d",
t->type, btf_decl_tag(t)->component_idx);
break;
+ case BTF_KIND_LOC_PARAM: {
+ struct btf_loc_param *p = btf_loc_param(t);
+
+ fprintf(out, " size=%u flags=0x%x vlen=%u", t->size, p->flags, vlen);
+ for (i = 0; i < vlen; i++) {
+ if (p->flags & BTF_LOC_PARAM_SIGNED)
+ fprintf(out, "\n\tvalue=%d", (__s32)p->values[i]);
+ else
+ fprintf(out, "\n\tvalue=%u", p->values[i]);
+ }
+ break;
+ }
+ case BTF_KIND_LOC_PROTO: {
+ const __u32 *p = btf_loc_proto_params(t);
+
+ fprintf(out, " vlen=%u", vlen);
+ for (i = 0; i < vlen; i++, p++)
+ fprintf(out, "\n\ttype_id=%u", *p);
+ break;
+ }
+ case BTF_KIND_LOCSEC: {
+ const struct btf_loc *l = btf_locsec_locs(t);
+
+ fprintf(out, " vlen=%u", vlen);
+ for (i = 0; i < vlen; i++, l++) {
+ fprintf(out, "\n\tfunc_type_id=%u loc_proto_type_id=%u offset=%u",
+ l->func, l->loc_proto, l->offset);
+ }
+ break;
+ }
default:
break;
}
diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
index d5b25302b0c89..bed506badc755 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
@@ -57,6 +57,57 @@ static struct btf *mk_btf(void)
return btf;
}
+static struct btf *mk_loc_btf(void)
+{
+ struct btf *btf;
+
+ btf = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf, "new_empty"))
+ return NULL;
+
+ btf__add_int(btf, "int", 4, BTF_INT_SIGNED);
+ btf__add_func_proto(btf, 1);
+ btf__add_func_param(btf, "arg1", 1);
+ btf__add_func_param(btf, "arg2", 1);
+ btf__add_func_param(btf, "arg3", 1);
+ btf__add_func_param(btf, "arg4", 1);
+ btf__add_func_param(btf, "arg5", 1);
+ btf__add_func(btf, "foo", BTF_FUNC_STATIC, 2);
+
+ btf__add_loc_param(btf, 4, BTF_LOC_PARAM_REG);
+ btf__add_loc_param_value(btf, 1);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG |
+ BTF_LOC_PARAM_DEREF | BTF_LOC_PARAM_OFFSET);
+ btf__add_loc_param_value(btf, 2);
+ btf__add_loc_param_value(btf, 0x10);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG |
+ BTF_LOC_PARAM_OFFSET | BTF_LOC_PARAM_SIGNED);
+ btf__add_loc_param_value(btf, BTF_LOC_PARAM_FBREG);
+ btf__add_loc_param_value(btf, -0x10);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_ADDR | BTF_LOC_PARAM_CONST);
+ btf__add_loc_param_value(btf, 0x9abcdef0);
+ btf__add_loc_param_value(btf, 0x12345678);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_CONST);
+ btf__add_loc_param_value(btf, 0xfeedface);
+ btf__add_loc_param_value(btf, 0xdeadbeef);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG |
+ BTF_LOC_PARAM_DEREF | BTF_LOC_PARAM_OFFSET |
+ BTF_LOC_PARAM_SIGNED);
+ btf__add_loc_param_value(btf, BTF_LOC_PARAM_FBREG);
+ btf__add_loc_param_value(btf, -0x20);
+ btf__add_loc_proto(btf);
+ btf__add_loc_proto_param(btf, 4);
+ btf__add_loc_proto_param(btf, 5);
+ btf__add_loc_proto_param(btf, 6);
+ btf__add_loc_proto_param(btf, 7);
+ btf__add_loc_proto_param(btf, 8);
+ btf__add_loc_proto_param(btf, 9);
+ btf__add_locsec(btf, "inline.text");
+ btf__add_locsec_loc(btf, 3, 10, 64);
+
+ return btf;
+}
+
static int btf_to_tmpfile(const struct btf *btf, char *path)
{
ssize_t written;
@@ -105,6 +156,26 @@ static char *dump_c(const char *btf_path, bool sorted)
return buf;
}
+static char *dump_raw(const char *btf_path)
+{
+ char args[MAX_BPFTOOL_CMD_LEN];
+ char *buf;
+ int err;
+
+ buf = malloc(DUMP_BUF_SZ);
+ if (!ASSERT_OK_PTR(buf, "alloc_dump"))
+ return NULL;
+
+ snprintf(args, sizeof(args), "btf dump file %s", btf_path);
+ err = get_bpftool_command_output(args, buf, DUMP_BUF_SZ);
+ if (!ASSERT_OK(err, "btf_dump_raw")) {
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
static char *read_expected(const char *path)
{
char *buf = NULL;
@@ -155,10 +226,46 @@ static void test_dump(const char *btf_path, bool sorted)
free(dump);
}
+static void test_loc_dump(const char *btf_path)
+{
+ const char expected[] =
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED\n"
+ "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=5\n"
+ "\t'arg1' type_id=1\n"
+ "\t'arg2' type_id=1\n"
+ "\t'arg3' type_id=1\n"
+ "\t'arg4' type_id=1\n"
+ "\t'arg5' type_id=1\n"
+ "[3] FUNC 'foo' type_id=2 linkage=static\n"
+ "[4] LOC_PARAM '(anon)' size=4 flags=0x8 vlen=1 values='reg1'\n"
+ "[5] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(reg2 + 0x10)'\n"
+ "[6] LOC_PARAM '(anon)' size=8 flags=0x29 vlen=2 values='fbreg - 0x10'\n"
+ "[7] LOC_PARAM '(anon)' size=8 flags=0x6 vlen=2 values='0x123456789abcdef0 (addr)'\n"
+ "[8] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2 values='0xdeadbeeffeedface'\n"
+ "[9] LOC_PARAM '(anon)' size=8 flags=0x39 vlen=2 values='*(fbreg - 0x20)'\n"
+ "[10] LOC_PROTO '(anon)' vlen=6\n"
+ "\ttype_id=4 value='reg1'\n"
+ "\ttype_id=5 value='*(reg2 + 0x10)'\n"
+ "\ttype_id=6 value='fbreg - 0x10'\n"
+ "\ttype_id=7 value='0x123456789abcdef0 (addr)'\n"
+ "\ttype_id=8 value='0xdeadbeeffeedface'\n"
+ "\ttype_id=9 value='*(fbreg - 0x20)'\n"
+ "[11] LOCSEC 'inline.text' vlen=1\n"
+ "\tname='foo' func_type_id=3 loc_proto_type_id=10 offset=64\n";
+ char *dump;
+
+ dump = dump_raw(btf_path);
+ if (!dump)
+ return;
+
+ ASSERT_OK(compare_text_to_expected(dump, expected), "cmp_loc_dump");
+ free(dump);
+}
+
void test_bpftool_btf_dump(void)
{
char path[PATH_MAX];
- struct btf *btf;
+ struct btf *btf = NULL;
btf = mk_btf();
if (!btf)
@@ -171,7 +278,19 @@ void test_bpftool_btf_dump(void)
test_dump(path, true);
if (test__start_subtest("c_unsorted"))
test_dump(path, false);
+ unlink(path);
+
+ btf__free(btf);
+
+ btf = mk_loc_btf();
+ if (!btf)
+ return;
+
+ if (btf_to_tmpfile(btf, path))
+ goto out_btf;
+ if (test__start_subtest("loc_dump"))
+ test_loc_dump(path);
unlink(path);
out_btf:
btf__free(btf);
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c b/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c
index 9d61611515938..5c15a6b7f4fb9 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c
@@ -554,6 +554,120 @@ static void test_split_module(void)
btf__free(vmlinux_btf);
}
+static void test_split_loc(void)
+{
+ struct btf *btf1, *btf2;
+ int err;
+
+ btf1 = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
+ return;
+
+ btf__set_pointer_size(btf1, 8); /* enforce 64-bit arch */
+
+
+ btf__add_int(btf1, "long", 8, BTF_INT_SIGNED); /* [1] long */
+ btf__add_ptr(btf1, 1); /* [2] ptr to long */
+ btf__add_func_proto(btf1, 1); /* [3] long (*)(long, long *); */
+ btf__add_func_param(btf1, "p1", 1);
+ btf__add_func_param(btf1, "p2", 2);
+ btf__add_func(btf1, "foo", BTF_FUNC_STATIC, 3); /* [4] long foo(long, long *); */
+ btf__add_loc_param(btf1, 8, BTF_LOC_PARAM_CONST);
+ btf__add_loc_param_value(btf1, 3735928559);
+ btf__add_loc_param_value(btf1, 4277009102); /* [5] loc value */
+ btf__add_loc_param(btf1, 8, BTF_LOC_PARAM_REG); /* [6] loc reg 1 */
+ btf__add_loc_param_value(btf1, 1);
+ btf__add_loc_proto(btf1); /* [7] loc proto */
+ btf__add_loc_proto_param(btf1, 5); /* param value */
+ btf__add_loc_proto_param(btf1, 6); /* param reg 1 */
+
+ VALIDATE_RAW_BTF(
+ btf1,
+ "[1] INT 'long' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED",
+ "[2] PTR '(anon)' type_id=1",
+ "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n"
+ "\t'p1' type_id=1\n"
+ "\t'p2' type_id=2",
+ "[4] FUNC 'foo' type_id=3 linkage=static",
+ "[5] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2\n"
+ "\tvalue=3735928559\n"
+ "\tvalue=4277009102",
+ "[6] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[7] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=5\n"
+ "\ttype_id=6");
+
+ btf2 = btf__new_empty_split(btf1);
+ if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
+ goto cleanup;
+ btf__add_loc_param(btf2, 8, BTF_LOC_PARAM_REG);
+ btf__add_loc_param_value(btf2, 1); /* [8] loc reg 1 */
+ btf__add_loc_proto(btf2); /* [9] loc proto */
+ btf__add_loc_proto_param(btf2, 5); /* param value */
+ btf__add_loc_proto_param(btf2, 8); /* param reg 1 */
+ btf__add_locsec(btf2, "inline.text"); /* [10] locsec "inline.text" */
+ /* add duplicate locsec */
+ btf__add_locsec_loc(btf2, 4, 9, 128);
+ btf__add_locsec(btf2, "inline.text"); /* [11] locsec "inline.text" */
+ btf__add_locsec_loc(btf2, 4, 9, 128);
+
+ VALIDATE_RAW_BTF(
+ btf2,
+ "[1] INT 'long' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED",
+ "[2] PTR '(anon)' type_id=1",
+ "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n"
+ "\t'p1' type_id=1\n"
+ "\t'p2' type_id=2",
+ "[4] FUNC 'foo' type_id=3 linkage=static",
+ "[5] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2\n"
+ "\tvalue=3735928559\n"
+ "\tvalue=4277009102",
+ "[6] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[7] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=5\n"
+ "\ttype_id=6",
+ "[8] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[9] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=5\n"
+ "\ttype_id=8",
+ "[10] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=9 offset=128",
+ "[11] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=9 offset=128");
+
+ err = btf__dedup(btf2, NULL);
+ if (!ASSERT_OK(err, "btf_dedup"))
+ goto cleanup;
+
+ VALIDATE_RAW_BTF(
+ btf2,
+ "[1] INT 'long' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED",
+ "[2] PTR '(anon)' type_id=1",
+ "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n"
+ "\t'p1' type_id=1\n"
+ "\t'p2' type_id=2",
+ "[4] FUNC 'foo' type_id=3 linkage=static",
+ "[5] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2\n"
+ "\tvalue=3735928559\n"
+ "\tvalue=4277009102",
+ "[6] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[7] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=5\n"
+ "\ttype_id=6",
+ "[8] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=7 offset=128",
+ "[9] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=7 offset=128");
+
+cleanup:
+ btf__free(btf2);
+ btf__free(btf1);
+}
+
void test_btf_dedup_split()
{
if (test__start_subtest("split_simple"))
@@ -566,4 +680,6 @@ void test_btf_dedup_split()
test_split_dup_struct_in_cu();
if (test__start_subtest("split_module"))
test_split_module();
+ if (test__start_subtest("split_loc"))
+ test_split_loc();
}
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_distill.c b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
index fb67ae195a738..f0900b47fd487 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_distill.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
@@ -671,6 +671,110 @@ static void test_distilled_base_embedded_err(void)
btf__free(btf1);
}
+/* LOC_PARAM, LOC_PROTO should be added to split BTF. */
+static void test_distilled_loc(void)
+{
+ struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL;
+ struct btf *btf4 = NULL, *btf5 = NULL;
+
+ btf1 = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
+ return;
+
+ btf__add_int(btf1, "int", 4, BTF_INT_SIGNED); /* [1] int */
+ btf__add_func_proto(btf1, 1); /* [2] int (*)(int); */
+ btf__add_func_param(btf1, "p1", 1);
+ btf__add_func(btf1, "foo", BTF_FUNC_STATIC, 2); /* [3] int foo(int); */
+ btf__add_loc_param(btf1, 4, BTF_LOC_PARAM_SIGNED | BTF_LOC_PARAM_CONST);
+ btf__add_loc_param_value(btf1, -1); /* [4] loc value */
+ btf__add_loc_proto(btf1); /* [5] loc proto */
+ btf__add_loc_proto_param(btf1, 4); /* param value */
+ btf__add_int(btf1, "unsigned int", 4, 0); /* [6] unsigned int */
+
+ VALIDATE_RAW_BTF(
+ btf1,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+ "\t'p1' type_id=1",
+ "[3] FUNC 'foo' type_id=2 linkage=static",
+ "[4] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[5] LOC_PROTO '(anon)' vlen=1\n"
+ "\ttype_id=4",
+ "[6] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)");
+
+ btf2 = btf__new_empty_split(btf1);
+ if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
+ goto cleanup;
+
+ btf__add_locsec(btf2, "inline.text"); /* [6] locsec */
+ btf__add_locsec_loc(btf2, 3, 5, 256); /* "foo" offset 256 */
+ VALIDATE_RAW_BTF(
+ btf2,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+ "\t'p1' type_id=1",
+ "[3] FUNC 'foo' type_id=2 linkage=static",
+ "[4] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[5] LOC_PROTO '(anon)' vlen=1\n"
+ "\ttype_id=4",
+ "[6] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)",
+ "[7] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=3 loc_proto_type_id=5 offset=256");
+
+ if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4),
+ "distilled_base") ||
+ !ASSERT_OK_PTR(btf3, "distilled_base") ||
+ !ASSERT_OK_PTR(btf4, "distilled_split") ||
+ !ASSERT_EQ(2, btf__type_cnt(btf3), "distilled_base_type_cnt"))
+ goto cleanup;
+
+ VALIDATE_RAW_BTF(
+ btf4,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ /* remainder is split BTF */
+ "[2] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=6 offset=256",
+ "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+ "\t'p1' type_id=1",
+ "[4] FUNC 'foo' type_id=3 linkage=static",
+ "[5] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[6] LOC_PROTO '(anon)' vlen=1\n"
+ "\ttype_id=5");
+
+ btf5 = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf5, "empty_reloc_btf"))
+ goto cleanup;
+ btf__add_int(btf5, "int", 4, BTF_INT_SIGNED); /* [1] int */
+ btf__add_int(btf5, "char", 1, 0); /* [2] char */
+
+ if (!ASSERT_EQ(btf__relocate(btf4, btf5), 0, "relocate_split"))
+ goto cleanup;
+ VALIDATE_RAW_BTF(
+ btf4,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[2] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=(none)",
+ /* remainder is split BTF */
+ "[3] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=5 loc_proto_type_id=7 offset=256",
+ "[4] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+ "\t'p1' type_id=1",
+ "[5] FUNC 'foo' type_id=4 linkage=static",
+ "[6] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[7] LOC_PROTO '(anon)' vlen=1\n"
+ "\ttype_id=6");
+
+cleanup:
+ btf__free(btf5);
+ btf__free(btf4);
+ btf__free(btf3);
+ btf__free(btf2);
+ btf__free(btf1);
+}
+
void test_btf_distill(void)
{
if (test__start_subtest("distilled_base"))
@@ -689,4 +793,6 @@ void test_btf_distill(void)
test_distilled_base_vmlinux();
if (test__start_subtest("distilled_endianness"))
test_distilled_endianness();
+ if (test__start_subtest("distilled_loc"))
+ test_distilled_loc();
}
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c b/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c
index 32159d3eb281b..dcb5429d141df 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c
@@ -31,8 +31,11 @@ struct field_data {
{ .ids = { 11 }, .strs = { "decltag" } },
{ .ids = { 6 }, .strs = { "typetag" } },
{ .ids = {}, .strs = { "e64", "eval1", "eval2", "eval3" } },
- { .ids = { 15, 16 }, .strs = { "datasec1" } }
-
+ { .ids = { 15, 16 }, .strs = { "datasec1" } },
+ { .ids = {}, .strs = { "" } },
+ { .ids = {}, .strs = { "" } },
+ { .ids = { 22, 23 }, .strs = { "" } },
+ { .ids = { 14, 24 }, .strs = { ".loc" } }
};
/* Fabricate BTF with various types and check BTF field iteration finds types,
@@ -88,6 +91,19 @@ void test_btf_field_iter(void)
btf__add_datasec_var_info(btf, 15, 0, 4);
btf__add_datasec_var_info(btf, 16, 4, 8);
+ btf__add_loc_param(btf, 4, BTF_LOC_PARAM_CONST | BTF_LOC_PARAM_SIGNED);
+ /* [22] loc value -1 */
+ btf__add_loc_param_value(btf, -1);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG); /* [23] loc reg 1 */
+ btf__add_loc_param_value(btf, 1);
+
+ btf__add_loc_proto(btf); /* [24] loc proto */
+ btf__add_loc_proto_param(btf, 22); /* param value -1, */
+ btf__add_loc_proto_param(btf, 23); /* param reg 1 */
+
+ btf__add_locsec(btf, ".loc"); /* [25] locsec ".loc" */
+ btf__add_locsec_loc(btf, 14, 24, 128); /* "func" */
+
VALIDATE_RAW_BTF(
btf,
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
@@ -123,7 +139,16 @@ void test_btf_field_iter(void)
"\t'eval3' val=3000",
"[21] DATASEC 'datasec1' size=12 vlen=2\n"
"\ttype_id=15 offset=0 size=4\n"
- "\ttype_id=16 offset=4 size=8");
+ "\ttype_id=16 offset=4 size=8",
+ "[22] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[23] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[24] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=22\n"
+ "\ttype_id=23",
+ "[25] LOCSEC '.loc' vlen=1\n"
+ "\tfunc_type_id=14 loc_proto_type_id=24 offset=128");
for (id = 1; id < btf__type_cnt(btf); id++) {
struct btf_type *t = btf_type_by_id(btf, id);
|
| 39/3 | 2026/09/26 16:59 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35454 output: 35 thoughts: 549 Thoughts: **Analyzing the BTF Patch: Kernel-Level and Tooling Implications** Okay, so this patch introduces a few new BTF kinds: `BTF_KIND_LOC_PARAM`, `BTF_KIND_LOC_PROTO`, and `BTF_KIND_LOCSEC`. My initial thought is to break down the changes across the kernel and associated tooling to understand the impact. First, I'll examine `kernel/bpf/btf.c`. I see the usual suspects: `btf_kind_str` being updated for human-readable output, and a series of new functions to check the type (`btf_type_is_loc_param`, etc.). There are also a few functions to check if a type needs resolving and some new checks and logging. Crucially, I see new operations structures like `loc_param_ops`, `loc_proto_ops`, and `locsec_ops`. The `kind_ops` table being modified is significant; it governs the behavior for the BTF kinds, so any changes there are key. The `btf_resolve_valid` function has likely changed to handle the new kinds correctly. Next, I need to look at `include/linux/btf.h`. The `struct btf_field_desc` change, specifically the increase in `m_offs` array size from 1 to 2, caught my eye. This is important to understand because a size change could introduce compatibility issues. I also see new helper functions related to the new kinds (`btf_loc_param`, `btf_loc_proto_params`, `btf_type_loc_secinfo`), and the `for_each_loc` macro. Now, my more immediate concern centers on potential security implications. I'm going to look at how types are handled, loaded, copied, verified, and returned to userspace within the kernel space. I'm especially interested in whether the kernel actually *returns* BTF types to userspace directly. I need to specifically look for any points where uninitialized memory could become an issue, or where potentially sensitive kernel information could leak to user-space through these new types. I will use `grepper` or dive into `kernel/bpf/btf.c` to search for where these types are handled to verify their safety. |
| 40/3 | 2026/09/26 16:59 | tool | grepper |
0mArgs:{
"Expression": "btf_get_info_by_fd",
"PathPrefix": "kernel/bpf/"
}Results: {
"Output": "kernel/bpf/btf.c=8794=struct btf *btf_get_by_fd(int fd)\n--\nkernel/bpf/btf.c-8805-\nkernel/bpf/btf.c:8806:int btf_get_info_by_fd(const struct btf *btf,\nkernel/bpf/btf.c-8807-\t\t const union bpf_attr *attr,\n--\nkernel/bpf/syscall.c=5393=static int bpf_map_get_info_by_fd(struct file *file,\n--\nkernel/bpf/syscall.c-5463-\nkernel/bpf/syscall.c:5464:static int bpf_btf_get_info_by_fd(struct file *file,\nkernel/bpf/syscall.c-5465-\t\t\t\t struct btf *btf,\n--\nkernel/bpf/syscall.c-5476-\nkernel/bpf/syscall.c:5477:\treturn btf_get_info_by_fd(btf, attr, uattr);\nkernel/bpf/syscall.c-5478-}\n--\nkernel/bpf/syscall.c=5539=static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,\n--\nkernel/bpf/syscall.c-5555-\telse if (fd_file(f)-\u003ef_op == \u0026btf_fops)\nkernel/bpf/syscall.c:5556:\t\treturn bpf_btf_get_info_by_fd(fd_file(f), fd_file(f)-\u003eprivate_data, attr, uattr);\nkernel/bpf/syscall.c-5557-\telse if (fd_file(f)-\u003ef_op == \u0026bpf_link_fops || fd_file(f)-\u003ef_op == \u0026bpf_link_fops_poll)\n"
} |
| 41/3 | 2026/09/26 16:59 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37028 output: 38 thoughts: 34 Thoughts: Here's my analysis of the provided thought process, tailored for an expert audience: **Analyzing BTF Copying to Userspace with New Kinds in Mind** Okay, my immediate thought is to dive into `btf_get_info_by_fd`. This function is the critical link between the kernel's BTF data and the userspace applications that need to consume it. I need to understand the mechanism here – how BTF data is actually copied out. Is it a simple memcpy, or something more sophisticated that might involve marshaling or format conversion? Understanding this will be crucial. Specifically, I'm concerned about the potential impact of *new* BTF kinds. If we've introduced new data structures, type representations, or metadata within BTF, I need to know if this copying process is designed to handle those new additions gracefully. Does it have robust checks for version compatibility or forward compatibility? Are there any potential vulnerabilities or subtle bugs that could arise when encountering these novel kinds? I'll be looking for any signs of hardcoded offsets, implicit assumptions about the structure of BTF data, or any lack of extensibility in the current implementation. The new BTF kinds could easily cause issues, so I'm thinking about edge cases and potential problems at this stage. I want to know, is the copy robust? |
| 42/3 | 2026/09/26 16:59 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/btf.c",
"FirstLine": 8805,
"LineCount": 40
}Results: {
"Contents": "8805:\t\n8806:\tint btf_get_info_by_fd(const struct btf *btf,\n8807:\t\t\t const union bpf_attr *attr,\n8808:\t\t\t union bpf_attr __user *uattr)\n8809:\t{\n8810:\t\tstruct bpf_btf_info __user *uinfo;\n8811:\t\tstruct bpf_btf_info info;\n8812:\t\tu32 info_copy, btf_copy;\n8813:\t\tvoid __user *ubtf;\n8814:\t\tchar __user *uname;\n8815:\t\tu32 uinfo_len, uname_len, name_len;\n8816:\t\tint ret = 0;\n8817:\t\n8818:\t\tuinfo = u64_to_user_ptr(attr-\u003einfo.info);\n8819:\t\tuinfo_len = attr-\u003einfo.info_len;\n8820:\t\n8821:\t\tinfo_copy = min_t(u32, uinfo_len, sizeof(info));\n8822:\t\tmemset(\u0026info, 0, sizeof(info));\n8823:\t\tif (copy_from_user(\u0026info, uinfo, info_copy))\n8824:\t\t\treturn -EFAULT;\n8825:\t\n8826:\t\tinfo.id = READ_ONCE(btf-\u003eid);\n8827:\t\tubtf = u64_to_user_ptr(info.btf);\n8828:\t\tbtf_copy = min_t(u32, btf-\u003edata_size, info.btf_size);\n8829:\t\tif (copy_to_user(ubtf, btf-\u003edata, btf_copy))\n8830:\t\t\treturn -EFAULT;\n8831:\t\tinfo.btf_size = btf-\u003edata_size;\n8832:\t\n8833:\t\tinfo.kernel_btf = btf-\u003ekernel_btf;\n8834:\t\n8835:\t\tuname = u64_to_user_ptr(info.name);\n8836:\t\tuname_len = info.name_len;\n8837:\t\tif (!uname ^ !uname_len)\n8838:\t\t\treturn -EINVAL;\n8839:\t\n8840:\t\tname_len = strlen(btf-\u003ename);\n8841:\t\tinfo.name_len = name_len;\n8842:\t\n8843:\t\tif (uname) {\n8844:\t\t\tif (uname_len \u003e= name_len + 1) {\n"
} |
| 43/3 | 2026/09/26 16:59 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 38063 output: 39 thoughts: 203 Thoughts: **Diving into BTF Verification in the Kernel** Okay, so I've got this `btf->data`, which is the raw BTF information, either loaded from user space or directly from the kernel image itself. Now, I need to understand the verification process and how this BTF data is handled within `kernel/bpf/btf.c`. Let's dig in. I need to get a handle on how these new functions in `kernel/bpf/btf.c` fit into the verification and processing: I see: * `btf_loc_param_check_meta` * `btf_loc_param_log` * `btf_loc_proto_check_meta` * `btf_loc_proto_log` * `btf_loc_proto_resolve` * `btf_verifier_log_loc` * `btf_locsec_check_meta` * `btf_locsec_log` * `btf_locsec_resolve` I'll start by reading the code around line 4753 in `kernel/bpf/btf.c` to understand the flow and context of those functions, I'm assuming that the functions are going to be verifying some metadata of local parameter and functions, as the names suggest. |
| 44/3 | 2026/09/26 16:59 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/btf.c",
"FirstLine": 4750,
"LineCount": 100
}Results: {
"Contents": "4750:\t\t.check_member = btf_enum_check_member,\n4751:\t\t.check_kflag_member = btf_enum_check_kflag_member,\n4752:\t\t.log_details = btf_enum_log,\n4753:\t\t.show = btf_enum64_show,\n4754:\t};\n4755:\t\n4756:\tstatic s32 btf_loc_param_check_meta(struct btf_verifier_env *env,\n4757:\t\t\t\t\t const struct btf_type *t,\n4758:\t\t\t\t\t u32 meta_left)\n4759:\t{\n4760:\t\tconst struct btf_loc_param *p = btf_loc_param(t);\n4761:\t\tu32 size, meta_needed, vlen = btf_vlen(t);\n4762:\t\n4763:\t\tmeta_needed = sizeof(*p) + sizeof(__u32) * vlen;\n4764:\t\tif (meta_left \u003c meta_needed) {\n4765:\t\t\tbtf_verifier_log_basic(env, t,\n4766:\t\t\t\t\t \"meta_left:%u meta_needed:%u\",\n4767:\t\t\t\t\t meta_left, meta_needed);\n4768:\t\t\treturn -EINVAL;\n4769:\t\t}\n4770:\t\n4771:\t\tif (t-\u003ename_off) {\n4772:\t\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\n4773:\t\t\treturn -EINVAL;\n4774:\t\t}\n4775:\t\tsize = t-\u003esize;\n4776:\t\tif (!size || size \u003e 16) {\n4777:\t\t\tbtf_verifier_log_type(env, t, \"Unexpected size\");\n4778:\t\t\treturn -EINVAL;\n4779:\t\t}\n4780:\t\n4781:\t\tif (btf_type_kflag(t)) {\n4782:\t\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\n4783:\t\t\treturn -EINVAL;\n4784:\t\t}\n4785:\t\n4786:\t\tbtf_verifier_log_type(env, t, NULL);\n4787:\t\n4788:\t\treturn meta_needed;\n4789:\t}\n4790:\t\n4791:\tstatic void btf_loc_param_log(struct btf_verifier_env *env,\n4792:\t\t\t\t const struct btf_type *t)\n4793:\t{\n4794:\t\tconst struct btf_loc_param *p = btf_loc_param(t);\n4795:\t\tu32 i, vlen = btf_vlen(t);\n4796:\t\n4797:\t\tbtf_verifier_log(env, \"size=%u vlen=%u flags=0x%x\", t-\u003esize, vlen, p-\u003eflags);\n4798:\t\tfor (i = 0; i \u003c vlen; i++)\n4799:\t\t\tbtf_verifier_log(env, \", %u\", p-\u003evalues[i]);\n4800:\t}\n4801:\t\n4802:\tstatic const struct btf_kind_operations loc_param_ops = {\n4803:\t\t.check_meta = btf_loc_param_check_meta,\n4804:\t\t.resolve = btf_df_resolve,\n4805:\t\t.check_member = btf_df_check_member,\n4806:\t\t.check_kflag_member = btf_df_check_kflag_member,\n4807:\t\t.log_details = btf_loc_param_log,\n4808:\t\t.show = btf_df_show,\n4809:\t};\n4810:\t\n4811:\tstatic s32 btf_loc_proto_check_meta(struct btf_verifier_env *env,\n4812:\t\t\t\t\t const struct btf_type *t,\n4813:\t\t\t\t\t u32 meta_left)\n4814:\t{\n4815:\t\tu32 meta_needed;\n4816:\t\n4817:\t\tmeta_needed = sizeof(__u32) * btf_type_vlen(t);\n4818:\t\n4819:\t\tif (meta_left \u003c meta_needed) {\n4820:\t\t\tbtf_verifier_log_basic(env, t,\n4821:\t\t\t\t\t \"meta_left:%u meta_needed:%u\",\n4822:\t\t\t\t\t meta_left, meta_needed);\n4823:\t\t\treturn -EINVAL;\n4824:\t\t}\n4825:\t\n4826:\t\tif (t-\u003ename_off) {\n4827:\t\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\n4828:\t\t\treturn -EINVAL;\n4829:\t\t}\n4830:\t\n4831:\t\tif (btf_type_kflag(t)) {\n4832:\t\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\n4833:\t\t\treturn -EINVAL;\n4834:\t\t}\n4835:\t\n4836:\t\tbtf_verifier_log_type(env, t, NULL);\n4837:\t\n4838:\t\treturn meta_needed;\n4839:\t}\n4840:\t\n4841:\tstatic void btf_loc_proto_log(struct btf_verifier_env *env,\n4842:\t\t\t\t const struct btf_type *t)\n4843:\t{\n4844:\t\tconst __u32 *params = btf_loc_proto_params(t);\n4845:\t\tu32 i, nr_params = btf_type_vlen(t);\n4846:\t\n4847:\t\tbtf_verifier_log(env, \"vlen=%u\", nr_params);\n4848:\t\tfor (i = 0; i \u003c nr_params; i++, params++)\n4849:\t\t\tbtf_verifier_log(env, \", %u\", *params);\n"
} |
| 45/3 | 2026/09/26 16:59 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 40244 output: 39 thoughts: 35 Thoughts: Okay, let's dive into this code. **Diving Deep into kernel/bpf/btf.c: Lines 4850-5050** Alright, I'm going to take a look at the latest additions within the `btf.c` file, specifically from line 4850 to 5050. This likely holds some crucial advancements in how we're handling BTF (BPF Type Format) data within the kernel. Given the line range, I expect to see the implementations of a few key functions or significant modifications to existing ones. My initial hypothesis is that this section focuses on either parsing, processing, or possibly utilizing BTF information for more advanced BPF features. I need to understand what new functionalities have been incorporated to enhance BPF's capabilities. Are we working with complex data structures, advanced type handling, or perhaps improving the interaction between BTF data and specific BPF helper functions? My expertise allows me to quickly navigate the code and understand any novel BPF related techniques implemented. Let's dig in and see what's what. |
| 46/3 | 2026/09/26 16:59 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/btf.c",
"FirstLine": 4850,
"LineCount": 100
}Results: {
"Contents": "4850:\t}\n4851:\t\n4852:\tstatic int btf_loc_proto_resolve(struct btf_verifier_env *env,\n4853:\t\t\t\t\t const struct resolve_vertex *v)\n4854:\t{\n4855:\t\tconst struct btf_type *t = v-\u003et;\n4856:\t\tconst __u32 *params = btf_loc_proto_params(t);\n4857:\t\tu32 i, nr_params = btf_type_vlen(t);\n4858:\t\tstruct btf *btf = env-\u003ebtf;\n4859:\t\n4860:\t\tif (t-\u003etype) {\n4861:\t\t\tbtf_verifier_log_type(env, t, \"Invalid loc_proto type\");\n4862:\t\t\treturn -EINVAL;\n4863:\t\t}\n4864:\t\n4865:\t\tfor (i = 0; i \u003c nr_params; i++) {\n4866:\t\t\tconst struct btf_type *param_type;\n4867:\t\t\tu32 param_type_id = params[i];\n4868:\t\n4869:\t\t\tif (!param_type_id)\n4870:\t\t\t\tcontinue;\n4871:\t\n4872:\t\t\tparam_type = btf_type_by_id(btf, param_type_id);\n4873:\t\t\tif (!param_type || !btf_type_is_loc_param(param_type)) {\n4874:\t\t\t\tbtf_verifier_log_type(env, t,\n4875:\t\t\t\t\t\t \"Invalid loc_param#%u\", i + 1);\n4876:\t\t\t\treturn -EINVAL;\n4877:\t\t\t}\n4878:\t\t}\n4879:\t\n4880:\t\tenv_stack_pop_resolved(env, 0, 0);\n4881:\t\treturn 0;\n4882:\t}\n4883:\t\n4884:\tstatic const struct btf_kind_operations loc_proto_ops = {\n4885:\t\t.check_meta = btf_loc_proto_check_meta,\n4886:\t\t.resolve = btf_loc_proto_resolve,\n4887:\t\t.check_member = btf_df_check_member,\n4888:\t\t.check_kflag_member = btf_df_check_kflag_member,\n4889:\t\t.log_details = btf_loc_proto_log,\n4890:\t\t.show = btf_df_show,\n4891:\t};\n4892:\t\n4893:\t__printf(4, 5)\n4894:\tstatic void btf_verifier_log_loc(struct btf_verifier_env *env,\n4895:\t\t\t\t\t const struct btf_type *locsec_type,\n4896:\t\t\t\t\t const struct btf_loc *loc,\n4897:\t\t\t\t\t const char *fmt, ...)\n4898:\t{\n4899:\t\tstruct bpf_verifier_log *log = \u0026env-\u003elog;\n4900:\t\tva_list args;\n4901:\t\n4902:\t\tif (!bpf_verifier_log_needed(log))\n4903:\t\t\treturn;\n4904:\t\tif (log-\u003elevel == BPF_LOG_KERNEL \u0026\u0026 !fmt)\n4905:\t\t\treturn;\n4906:\t\tif (env-\u003ephase != CHECK_META)\n4907:\t\t\tbtf_verifier_log_type(env, locsec_type, NULL);\n4908:\t\n4909:\t\t__btf_verifier_log(log, \"\\t func=%u loc_proto=%u offset=%u\",\n4910:\t\t\t\t loc-\u003efunc, loc-\u003eloc_proto, loc-\u003eoffset);\n4911:\t\tif (fmt \u0026\u0026 *fmt) {\n4912:\t\t\t__btf_verifier_log(log, \" \");\n4913:\t\t\tva_start(args, fmt);\n4914:\t\t\tbpf_verifier_vlog(log, fmt, args);\n4915:\t\t\tva_end(args);\n4916:\t\t}\n4917:\t\n4918:\t\t__btf_verifier_log(log, \"\\n\");\n4919:\t}\n4920:\t\n4921:\tstatic s32 btf_locsec_check_meta(struct btf_verifier_env *env,\n4922:\t\t\t\t\t const struct btf_type *t,\n4923:\t\t\t\t\t u32 meta_left)\n4924:\t{\n4925:\t\tu32 i, meta_needed, vlen = btf_type_vlen(t);\n4926:\t\tconst struct btf_loc *loc;\n4927:\t\n4928:\t\tmeta_needed = sizeof(struct btf_loc) * vlen;\n4929:\t\n4930:\t\tif (meta_left \u003c meta_needed) {\n4931:\t\t\tbtf_verifier_log_basic(env, t,\n4932:\t\t\t\t\t \"meta_left:%u meta_needed:%u\",\n4933:\t\t\t\t\t meta_left, meta_needed);\n4934:\t\t\treturn -EINVAL;\n4935:\t\t}\n4936:\t\n4937:\t\tif (btf_type_kflag(t)) {\n4938:\t\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\n4939:\t\t\treturn -EINVAL;\n4940:\t\t}\n4941:\t\n4942:\t\tif (!t-\u003ename_off ||\n4943:\t\t !btf_name_valid_section(env-\u003ebtf, t-\u003ename_off)) {\n4944:\t\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\n4945:\t\t\treturn -EINVAL;\n4946:\t\t}\n4947:\t\n4948:\t\tfor_each_loc(i, t, loc) {\n4949:\t\t\t/* A loc func, loc proto cannot be in type void */\n"
} |
| 47/3 | 2026/09/26 16:59 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42145 output: 39 thoughts: 19 Thoughts: Okay, I'm ready to dive into that text! Here's my thought process summarized, assuming I'm an expert in the field: **Initial Assessment and Targeted Reading** Alright, I see the task involves summarizing some text, presumably within a specific domain I'm familiar with. Immediately, my mind zeroes in on efficiency. Before getting bogged down, I'm going to start with a strategically targeted read, not a general overview. I'm noting the instructions specify lines 4950 to 5050. That range is critical. I'm focusing on that *specific* section. I need to understand the nuances of *that* particular segment, and *then* extrapolate appropriately, if necessary. Let's see what that range holds; my understanding will hinge upon what's presented within those boundaries. |
| 48/3 | 2026/09/26 16:59 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/btf.c",
"FirstLine": 4950,
"LineCount": 100
}Results: {
"Contents": "4950:\t\t\tif (!loc-\u003efunc || !BTF_TYPE_ID_VALID(loc-\u003efunc)) {\n4951:\t\t\t\tbtf_verifier_log_loc(env, t, loc, \"Invalid func\");\n4952:\t\t\t\treturn -EINVAL;\n4953:\t\t\t}\n4954:\t\t\tif (!loc-\u003eloc_proto || !BTF_TYPE_ID_VALID(loc-\u003eloc_proto)) {\n4955:\t\t\t\tbtf_verifier_log_loc(env, t, loc, \"Invalid loc_proto\");\n4956:\t\t\t\treturn -EINVAL;\n4957:\t\t\t}\n4958:\t\t\tbtf_verifier_log_loc(env, t, loc, NULL);\n4959:\t\t}\n4960:\t\n4961:\t\treturn meta_needed;\n4962:\t}\n4963:\t\n4964:\tstatic void btf_locsec_log(struct btf_verifier_env *env,\n4965:\t\t\t\t const struct btf_type *t)\n4966:\t{\n4967:\t\tbtf_verifier_log(env, \"vlen=%u\", btf_type_vlen(t));\n4968:\t}\n4969:\t\n4970:\tstatic int btf_locsec_resolve(struct btf_verifier_env *env,\n4971:\t\t\t\t const struct resolve_vertex *v)\n4972:\t{\n4973:\t\tconst struct btf_type *t = v-\u003et;\n4974:\t\tconst struct btf_loc *loc;\n4975:\t\tstruct btf *btf = env-\u003ebtf;\n4976:\t\tu32 i;\n4977:\t\n4978:\t\tif (t-\u003etype) {\n4979:\t\t\tbtf_verifier_log_type(env, t, \"Invalid locsec type\");\n4980:\t\t\treturn -EINVAL;\n4981:\t\t}\n4982:\t\n4983:\t\tenv-\u003eresolve_mode = RESOLVE_TBD;\n4984:\t\tfor (i = v-\u003enext_member, loc = btf_type_loc_secinfo(t) + i;\n4985:\t\t i \u003c btf_type_vlen(t); i++, loc++) {\n4986:\t\t\tconst struct btf_type *func_type, *func_proto_type;\n4987:\t\t\tconst struct btf_type *loc_proto_type;\n4988:\t\t\tu32 func_type_id = loc-\u003efunc;\n4989:\t\t\tu32 loc_proto_type_id = loc-\u003eloc_proto;\n4990:\t\t\tu32 proto_vlen;\n4991:\t\n4992:\t\t\tfunc_type = btf_type_by_id(btf, func_type_id);\n4993:\t\t\tif (!func_type || !btf_type_is_func(func_type)) {\n4994:\t\t\t\tbtf_verifier_log_type(env, t,\n4995:\t\t\t\t\t\t \"Invalid func#%u\", i + 1);\n4996:\t\t\t\treturn -EINVAL;\n4997:\t\t\t}\n4998:\t\t\tfunc_proto_type = btf_type_by_id(btf, func_type-\u003etype);\n4999:\t\t\tif (!func_proto_type || !btf_type_is_func_proto(func_proto_type)) {\n5000:\t\t\t\tbtf_verifier_log_type(env, t,\n5001:\t\t\t\t\t\t \"Invalid func#%u\", i + 1);\n5002:\t\t\t\treturn -EINVAL;\n5003:\t\t\t}\n5004:\t\t\tproto_vlen = btf_vlen(func_proto_type);\n5005:\t\n5006:\t\t\tif (!env_type_is_resolved(env, func_type_id)) {\n5007:\t\t\t\tenv_stack_set_next_member(env, i);\n5008:\t\t\t\treturn env_stack_push(env, func_type, func_type_id);\n5009:\t\t\t}\n5010:\t\n5011:\t\t\tloc_proto_type = btf_type_by_id(btf, loc_proto_type_id);\n5012:\t\t\tif (!loc_proto_type || !btf_type_is_loc_proto(loc_proto_type)) {\n5013:\t\t\t\tbtf_verifier_log_type(env, t,\n5014:\t\t\t\t\t\t \"Invalid loc_proto#%u\", i + 1);\n5015:\t\t\t\treturn -EINVAL;\n5016:\t\t\t}\n5017:\t\t\tif (proto_vlen != btf_vlen(loc_proto_type)) {\n5018:\t\t\t\tbtf_verifier_log_type(env, t,\n5019:\t\t\t\t\t\t \"Mismatched vlen for loc_proto#%u\", i + 1);\n5020:\t\t\t\treturn -EINVAL;\n5021:\t\t\t}\n5022:\t\n5023:\t\t\tif (!env_type_is_resolved(env, loc_proto_type_id)) {\n5024:\t\t\t\tenv_stack_set_next_member(env, i + 1);\n5025:\t\t\t\treturn env_stack_push(env, loc_proto_type,\n5026:\t\t\t\t\t\t loc_proto_type_id);\n5027:\t\t\t}\n5028:\t\t}\n5029:\t\n5030:\t\tenv_stack_pop_resolved(env, 0, 0);\n5031:\t\treturn 0;\n5032:\t}\n5033:\t\n5034:\tstatic const struct btf_kind_operations locsec_ops = {\n5035:\t\t.check_meta = btf_locsec_check_meta,\n5036:\t\t.resolve = btf_locsec_resolve,\n5037:\t\t.check_member = btf_df_check_member,\n5038:\t\t.check_kflag_member = btf_df_check_kflag_member,\n5039:\t\t.log_details = btf_locsec_log,\n5040:\t\t.show = btf_df_show,\n5041:\t};\n5042:\t\n5043:\tstatic s32 btf_func_proto_check_meta(struct btf_verifier_env *env,\n5044:\t\t\t\t\t const struct btf_type *t,\n5045:\t\t\t\t\t u32 meta_left)\n5046:\t{\n5047:\t\tu32 meta_needed = btf_type_vlen(t) * sizeof(struct btf_param);\n5048:\t\n5049:\t\tif (meta_left \u003c meta_needed) {\n"
} |
| 49/3 | 2026/09/26 16:59 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44135 output: 28 thoughts: 119 Thoughts: **Investigating a BTF Structure Change** Okay, hold on a second! I need to take a closer look at something in the kernel. Specifically, I was reviewing `include/linux/btf.h` and immediately spotted a potential issue. There's a curious change in the `struct btf_field_desc` definition. It looks like the size of the `m_offs` array has been bumped up from `[1]` to `[2]`. Now, I need to understand the implications of this. Why was this modification made? What's the context? Where is this structure *actually* used within the kernel? Clearly, I need to trace the usage of `struct btf_field_desc` throughout the codebase. The quickest and most efficient way to do that is, of course, to fire up `grepper` and see what it turns up. I'll need to see where this structure is defined and referenced to determine the impact of this seemingly small, yet potentially significant, change. Time to dig in and understand this properly. |
| 50/3 | 2026/09/26 16:59 | tool | grepper |
0mError:no matches Args: {
"Expression": "btf_field_desc",
"PathPrefix": "kernel/"
}Results: {
"error": "no matches"
} |
| 51/3 | 2026/09/26 16:59 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44519 output: 20 thoughts: 17 Thoughts: Okay, here's my summary, presented as a stream of thought: **Initial Strategy for Code Analysis** Alright, so the task at hand is to locate every instance of `btf_field_desc` within the code's hierarchical structure. The obvious first move is to implement a comprehensive search that traverses the entire tree-like organization of the codebase. This brute-force approach is essential to ensure that no relevant occurrence is missed. It's a fundamental step for gaining a full understanding of the object in question. Now, let's execute the search and see what the results tell us. |
| 52/3 | 2026/09/26 16:59 | tool | grepper |
0mArgs:{
"Expression": "btf_field_desc"
}Results: {
"Output": "include/linux/btf.h=571=enum btf_field_iter_kind {\n--\ninclude/linux/btf.h-575-\ninclude/linux/btf.h:576:struct btf_field_desc {\ninclude/linux/btf.h-577-\t/* once-per-type offsets */\n--\ninclude/linux/btf.h=585=struct btf_field_iter {\ninclude/linux/btf.h:586:\tstruct btf_field_desc desc;\ninclude/linux/btf.h-587-\tvoid *p;\n--\ntools/lib/bpf/btf_iter.c=16=int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t,\n--\ntools/lib/bpf/btf_iter.c-32-\t\tcase BTF_KIND_LOC_PARAM:\ntools/lib/bpf/btf_iter.c:33:\t\t\tit-\u003edesc = (struct btf_field_desc) {};\ntools/lib/bpf/btf_iter.c-34-\t\t\tbreak;\n--\ntools/lib/bpf/btf_iter.c-44-\t\tcase BTF_KIND_TYPE_TAG:\ntools/lib/bpf/btf_iter.c:45:\t\t\tit-\u003edesc = (struct btf_field_desc) { 1, {offsetof(struct btf_type, type)} };\ntools/lib/bpf/btf_iter.c-46-\t\t\tbreak;\ntools/lib/bpf/btf_iter.c-47-\t\tcase BTF_KIND_ARRAY:\ntools/lib/bpf/btf_iter.c:48:\t\t\tit-\u003edesc = (struct btf_field_desc) {\ntools/lib/bpf/btf_iter.c-49-\t\t\t\t2, {sizeof(struct btf_type) + offsetof(struct btf_array, type),\n--\ntools/lib/bpf/btf_iter.c-54-\t\tcase BTF_KIND_UNION:\ntools/lib/bpf/btf_iter.c:55:\t\t\tit-\u003edesc = (struct btf_field_desc) {\ntools/lib/bpf/btf_iter.c-56-\t\t\t\t0, {},\n--\ntools/lib/bpf/btf_iter.c-61-\t\tcase BTF_KIND_FUNC_PROTO:\ntools/lib/bpf/btf_iter.c:62:\t\t\tit-\u003edesc = (struct btf_field_desc) {\ntools/lib/bpf/btf_iter.c-63-\t\t\t\t1, {offsetof(struct btf_type, type)},\n--\ntools/lib/bpf/btf_iter.c-68-\t\tcase BTF_KIND_DATASEC:\ntools/lib/bpf/btf_iter.c:69:\t\t\tit-\u003edesc = (struct btf_field_desc) {\ntools/lib/bpf/btf_iter.c-70-\t\t\t\t0, {},\n--\ntools/lib/bpf/btf_iter.c-75-\t\tcase BTF_KIND_LOC_PROTO:\ntools/lib/bpf/btf_iter.c:76:\t\t\tit-\u003edesc = (struct btf_field_desc) {\ntools/lib/bpf/btf_iter.c-77-\t\t\t\t0, {},\n--\ntools/lib/bpf/btf_iter.c-81-\t\tcase BTF_KIND_LOCSEC:\ntools/lib/bpf/btf_iter.c:82:\t\t\tit-\u003edesc = (struct btf_field_desc) {\ntools/lib/bpf/btf_iter.c-83-\t\t\t\t0, {},\n--\ntools/lib/bpf/btf_iter.c-95-\t\tcase BTF_KIND_UNKN:\ntools/lib/bpf/btf_iter.c:96:\t\t\tit-\u003edesc = (struct btf_field_desc) {};\ntools/lib/bpf/btf_iter.c-97-\t\t\tbreak;\n--\ntools/lib/bpf/btf_iter.c-114-\t\tcase BTF_KIND_LOCSEC:\ntools/lib/bpf/btf_iter.c:115:\t\t\tit-\u003edesc = (struct btf_field_desc) {\ntools/lib/bpf/btf_iter.c-116-\t\t\t\t1, {offsetof(struct btf_type, name_off)}\n--\ntools/lib/bpf/btf_iter.c-119-\t\tcase BTF_KIND_ENUM:\ntools/lib/bpf/btf_iter.c:120:\t\t\tit-\u003edesc = (struct btf_field_desc) {\ntools/lib/bpf/btf_iter.c-121-\t\t\t\t1, {offsetof(struct btf_type, name_off)},\n--\ntools/lib/bpf/btf_iter.c-126-\t\tcase BTF_KIND_ENUM64:\ntools/lib/bpf/btf_iter.c:127:\t\t\tit-\u003edesc = (struct btf_field_desc) {\ntools/lib/bpf/btf_iter.c-128-\t\t\t\t1, {offsetof(struct btf_type, name_off)},\n--\ntools/lib/bpf/btf_iter.c-134-\t\tcase BTF_KIND_UNION:\ntools/lib/bpf/btf_iter.c:135:\t\t\tit-\u003edesc = (struct btf_field_desc) {\ntools/lib/bpf/btf_iter.c-136-\t\t\t\t1, {offsetof(struct btf_type, name_off)},\n--\ntools/lib/bpf/btf_iter.c-141-\t\tcase BTF_KIND_FUNC_PROTO:\ntools/lib/bpf/btf_iter.c:142:\t\t\tit-\u003edesc = (struct btf_field_desc) {\ntools/lib/bpf/btf_iter.c-143-\t\t\t\t1, {offsetof(struct btf_type, name_off)},\n--\ntools/lib/bpf/libbpf_internal.h=572=enum btf_field_iter_kind {\n--\ntools/lib/bpf/libbpf_internal.h-576-\ntools/lib/bpf/libbpf_internal.h:577:struct btf_field_desc {\ntools/lib/bpf/libbpf_internal.h-578-\t/* once-per-type offsets */\n--\ntools/lib/bpf/libbpf_internal.h=586=struct btf_field_iter {\ntools/lib/bpf/libbpf_internal.h:587:\tstruct btf_field_desc desc;\ntools/lib/bpf/libbpf_internal.h-588-\tvoid *p;\n"
} |
| 53/3 | 2026/09/26 16:59 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 46410 output: 28 thoughts: 82 Thoughts: Here's my take on the thought process, as requested: **Navigating BTF Field Descriptions in the Kernel** Okay, so I'm looking at `btf_field_desc` in `include/linux/btf.h`. I need to ensure the kernel version aligns with the user-space tools. I remember seeing this synchronized with `tools/lib/bpf/libbpf_internal.h`. That's the baseline. Now, I need to understand how the kernel is *actually* using this structure, specifically the iteration. My first thought is around the iterator `btf_field_iter`. I need to confirm: Is `btf_field_iter` even *used* within the kernel space? It's crucial to understand the context of the field descriptions used in this module. Time to dive in. I'm going to quickly search within the kernel source in `kernel/`. I'll use grep or a similar tool to pinpoint every instance of `btf_field_iter` and see what's happening. The usage patterns should reveal the practical use cases and potential implications I need to consider for this structure. |
| 54/3 | 2026/09/26 16:59 | tool | grepper |
0mError:no matches Args: {
"Expression": "btf_field_iter",
"PathPrefix": "kernel/"
}Results: {
"error": "no matches"
} |
| 55/3 | 2026/09/26 16:59 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 46779 output: 35 thoughts: 121 Thoughts: **Analyzing the BTF Kind Implementation** Okay, so I see this new thing isn't actually *used* anywhere in the kernel itself, just defined in `include/linux/btf.h`. That's a starting point. Now, let's dive into `kernel/bpf/btf.c`. My primary concern right now is ensuring the new BTF kinds are handled correctly. I need to meticulously audit the code to ensure there aren't any gaps in how these kinds are checked or processed, especially where `btf_kind` is evaluated. I'm thinking about potential memory initialization issues. Uninitialized memory in BPF is a huge no-no. Right, so I'm going to systematically search `kernel/bpf/btf.c` for any instance of `BTF_KIND_`. That's the best way to make sure I catch every place where the code makes assumptions or decisions based on BTF kind, including the new one, and verify the control flow. I need to be absolutely certain there are no overlooked code paths that could lead to uninitialized memory being used when dealing with these new kind definitions. |
| 56/3 | 2026/09/26 16:59 | tool | grepper |
0mArgs:{
"Expression": "BTF_KIND_",
"PathPrefix": "kernel/bpf/btf.c"
}Results: {
"Output": "kernel/bpf/btf.c-92- * - [?] is the type_id of the btf_type object.\nkernel/bpf/btf.c:93: * - CONST/PTR is the BTF_KIND_XXX\nkernel/bpf/btf.c-94- * - \"(anon)\" is the name of the type. It just\n--\nkernel/bpf/btf.c-159- *\nkernel/bpf/btf.c:160: * When resolving a BTF_KIND_STRUCT, we need to resolve all its\nkernel/bpf/btf.c-161- * members because a member is always referring to another\nkernel/bpf/btf.c-162- * type. A struct's member can be treated as \"RESOLVED\" if\nkernel/bpf/btf.c:163: * it is referring to a BTF_KIND_PTR. Otherwise, the\nkernel/bpf/btf.c-164- * following valid C struct would be rejected:\n--\nkernel/bpf/btf.c-170- *\nkernel/bpf/btf.c:171: * When resolving a BTF_KIND_PTR, it needs to keep resolving if\nkernel/bpf/btf.c:172: * it is referring to another BTF_KIND_PTR. Otherwise, we cannot\nkernel/bpf/btf.c-173- * detect a pointer loop, e.g.:\nkernel/bpf/btf.c:174: * BTF_KIND_CONST -\u003e BTF_KIND_PTR -\u003e BTF_KIND_CONST -\u003e BTF_KIND_PTR +\nkernel/bpf/btf.c-175- * ^ |\n--\nkernel/bpf/btf.c=328=static const char * const btf_kind_str[NR_BTF_KINDS] = {\nkernel/bpf/btf.c:329:\t[BTF_KIND_UNKN]\t\t= \"UNKNOWN\",\nkernel/bpf/btf.c:330:\t[BTF_KIND_INT]\t\t= \"INT\",\nkernel/bpf/btf.c:331:\t[BTF_KIND_PTR]\t\t= \"PTR\",\nkernel/bpf/btf.c:332:\t[BTF_KIND_ARRAY]\t= \"ARRAY\",\nkernel/bpf/btf.c:333:\t[BTF_KIND_STRUCT]\t= \"STRUCT\",\nkernel/bpf/btf.c:334:\t[BTF_KIND_UNION]\t= \"UNION\",\nkernel/bpf/btf.c:335:\t[BTF_KIND_ENUM]\t\t= \"ENUM\",\nkernel/bpf/btf.c:336:\t[BTF_KIND_FWD]\t\t= \"FWD\",\nkernel/bpf/btf.c:337:\t[BTF_KIND_TYPEDEF]\t= \"TYPEDEF\",\nkernel/bpf/btf.c:338:\t[BTF_KIND_VOLATILE]\t= \"VOLATILE\",\nkernel/bpf/btf.c:339:\t[BTF_KIND_CONST]\t= \"CONST\",\nkernel/bpf/btf.c:340:\t[BTF_KIND_RESTRICT]\t= \"RESTRICT\",\nkernel/bpf/btf.c:341:\t[BTF_KIND_FUNC]\t\t= \"FUNC\",\nkernel/bpf/btf.c:342:\t[BTF_KIND_FUNC_PROTO]\t= \"FUNC_PROTO\",\nkernel/bpf/btf.c:343:\t[BTF_KIND_VAR]\t\t= \"VAR\",\nkernel/bpf/btf.c:344:\t[BTF_KIND_DATASEC]\t= \"DATASEC\",\nkernel/bpf/btf.c:345:\t[BTF_KIND_FLOAT]\t= \"FLOAT\",\nkernel/bpf/btf.c:346:\t[BTF_KIND_DECL_TAG]\t= \"DECL_TAG\",\nkernel/bpf/btf.c:347:\t[BTF_KIND_TYPE_TAG]\t= \"TYPE_TAG\",\nkernel/bpf/btf.c:348:\t[BTF_KIND_ENUM64]\t= \"ENUM64\",\nkernel/bpf/btf.c:349:\t[BTF_KIND_LOC_PARAM]\t= \"LOC_PARAM\",\nkernel/bpf/btf.c:350:\t[BTF_KIND_LOC_PROTO]\t= \"LOC_PROTO\",\nkernel/bpf/btf.c:351:\t[BTF_KIND_LOCSEC]\t= \"LOCSEC\",\nkernel/bpf/btf.c-352-};\n--\nkernel/bpf/btf.c=480=static bool btf_type_is_modifier(const struct btf_type *t)\n--\nkernel/bpf/btf.c-492-\tswitch (BTF_INFO_KIND(t-\u003einfo)) {\nkernel/bpf/btf.c:493:\tcase BTF_KIND_TYPEDEF:\nkernel/bpf/btf.c:494:\tcase BTF_KIND_VOLATILE:\nkernel/bpf/btf.c:495:\tcase BTF_KIND_CONST:\nkernel/bpf/btf.c:496:\tcase BTF_KIND_RESTRICT:\nkernel/bpf/btf.c:497:\tcase BTF_KIND_TYPE_TAG:\nkernel/bpf/btf.c-498-\t\treturn true;\n--\nkernel/bpf/btf.c=514=static bool btf_type_is_datasec(const struct btf_type *t)\nkernel/bpf/btf.c-515-{\nkernel/bpf/btf.c:516:\treturn BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_DATASEC;\nkernel/bpf/btf.c-517-}\n--\nkernel/bpf/btf.c=519=static bool btf_type_is_decl_tag(const struct btf_type *t)\nkernel/bpf/btf.c-520-{\nkernel/bpf/btf.c:521:\treturn BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_DECL_TAG;\nkernel/bpf/btf.c-522-}\n--\nkernel/bpf/btf.c=524=static bool btf_type_is_loc_param(const struct btf_type *t)\nkernel/bpf/btf.c-525-{\nkernel/bpf/btf.c:526:\treturn BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_LOC_PARAM;\nkernel/bpf/btf.c-527-}\n--\nkernel/bpf/btf.c=529=static bool btf_type_is_loc_proto(const struct btf_type *t)\nkernel/bpf/btf.c-530-{\nkernel/bpf/btf.c:531:\treturn BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_LOC_PROTO;\nkernel/bpf/btf.c-532-}\n--\nkernel/bpf/btf.c=534=static bool btf_type_is_locsec(const struct btf_type *t)\nkernel/bpf/btf.c-535-{\nkernel/bpf/btf.c:536:\treturn BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_LOCSEC;\nkernel/bpf/btf.c-537-}\n--\nkernel/bpf/btf.c=827=static bool btf_type_has_size(const struct btf_type *t)\n--\nkernel/bpf/btf.c-829-\tswitch (BTF_INFO_KIND(t-\u003einfo)) {\nkernel/bpf/btf.c:830:\tcase BTF_KIND_INT:\nkernel/bpf/btf.c:831:\tcase BTF_KIND_STRUCT:\nkernel/bpf/btf.c:832:\tcase BTF_KIND_UNION:\nkernel/bpf/btf.c:833:\tcase BTF_KIND_ENUM:\nkernel/bpf/btf.c:834:\tcase BTF_KIND_DATASEC:\nkernel/bpf/btf.c:835:\tcase BTF_KIND_FLOAT:\nkernel/bpf/btf.c:836:\tcase BTF_KIND_ENUM64:\nkernel/bpf/btf.c-837-\t\treturn true;\n--\nkernel/bpf/btf.c=1078=static const struct btf_type *btf_type_skip_qualifiers(const struct btf *btf,\n--\nkernel/bpf/btf.c-1083-\twhile (btf_type_is_modifier(t) \u0026\u0026\nkernel/bpf/btf.c:1084:\t BTF_INFO_KIND(t-\u003einfo) != BTF_KIND_TYPEDEF) {\nkernel/bpf/btf.c-1085-\t\tt = btf_type_by_id(btf, t-\u003etype);\n--\nkernel/bpf/btf.c-1092-\nkernel/bpf/btf.c:1093:#define BTF_KIND_BIT(kind)\t(1ULL \u003c\u003c kind)\nkernel/bpf/btf.c-1094-\n--\nkernel/bpf/btf.c=1101=static const char *btf_show_name(struct btf_show *show)\n--\nkernel/bpf/btf.c-1171-\t\tswitch (BTF_INFO_KIND(t-\u003einfo)) {\nkernel/bpf/btf.c:1172:\t\tcase BTF_KIND_TYPEDEF:\nkernel/bpf/btf.c-1173-\t\t\tif (!name)\n--\nkernel/bpf/btf.c-1175-\t\t\t\t\t\t\t t-\u003ename_off);\nkernel/bpf/btf.c:1176:\t\t\tkinds |= BTF_KIND_BIT(BTF_KIND_TYPEDEF);\nkernel/bpf/btf.c-1177-\t\t\tid = t-\u003etype;\nkernel/bpf/btf.c-1178-\t\t\tbreak;\nkernel/bpf/btf.c:1179:\t\tcase BTF_KIND_ARRAY:\nkernel/bpf/btf.c:1180:\t\t\tkinds |= BTF_KIND_BIT(BTF_KIND_ARRAY);\nkernel/bpf/btf.c-1181-\t\t\tparens = \"[\";\n--\nkernel/bpf/btf.c-1188-\t\t\tbreak;\nkernel/bpf/btf.c:1189:\t\tcase BTF_KIND_PTR:\nkernel/bpf/btf.c:1190:\t\t\tkinds |= BTF_KIND_BIT(BTF_KIND_PTR);\nkernel/bpf/btf.c-1191-\t\t\tif (ptr_suffix \u003e ptr_suffixes)\n--\nkernel/bpf/btf.c-1210-\tswitch (BTF_INFO_KIND(t-\u003einfo)) {\nkernel/bpf/btf.c:1211:\tcase BTF_KIND_STRUCT:\nkernel/bpf/btf.c:1212:\tcase BTF_KIND_UNION:\nkernel/bpf/btf.c:1213:\t\tprefix = BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_STRUCT ?\nkernel/bpf/btf.c-1214-\t\t\t \"struct\" : \"union\";\nkernel/bpf/btf.c-1215-\t\t/* if it's an array of struct/union, parens is already set */\nkernel/bpf/btf.c:1216:\t\tif (!(kinds \u0026 (BTF_KIND_BIT(BTF_KIND_ARRAY))))\nkernel/bpf/btf.c-1217-\t\t\tparens = \"{\";\nkernel/bpf/btf.c-1218-\t\tbreak;\nkernel/bpf/btf.c:1219:\tcase BTF_KIND_ENUM:\nkernel/bpf/btf.c:1220:\tcase BTF_KIND_ENUM64:\nkernel/bpf/btf.c-1221-\t\tprefix = \"enum\";\n--\nkernel/bpf/btf.c-1227-\t/* pointer does not require parens */\nkernel/bpf/btf.c:1228:\tif (kinds \u0026 BTF_KIND_BIT(BTF_KIND_PTR))\nkernel/bpf/btf.c-1229-\t\tparens = \"\";\nkernel/bpf/btf.c-1230-\t/* typedef does not require struct/union/enum prefix */\nkernel/bpf/btf.c:1231:\tif (kinds \u0026 BTF_KIND_BIT(BTF_KIND_TYPEDEF))\nkernel/bpf/btf.c-1232-\t\tprefix = \"\";\n--\nkernel/bpf/btf.c=1280=static const char *btf_show_delim(struct btf_show *show)\n--\nkernel/bpf/btf.c-1285-\tif ((show-\u003eflags \u0026 BTF_SHOW_COMPACT) \u0026\u0026 show-\u003estate.type \u0026\u0026\nkernel/bpf/btf.c:1286:\t\tBTF_INFO_KIND(show-\u003estate.type-\u003einfo) == BTF_KIND_UNION)\nkernel/bpf/btf.c-1287-\t\treturn \"|\";\n--\nkernel/bpf/btf.c=2076=static const struct resolve_vertex *env_stack_peak(struct btf_verifier_env *env)\n--\nkernel/bpf/btf.c-2083- * type: is an array (e.g. u32 array[x][y])\nkernel/bpf/btf.c:2084: * return type: type \"u32[x][y]\", i.e. BTF_KIND_ARRAY,\nkernel/bpf/btf.c-2085- * *type_size: (x * y * sizeof(u32)). Hence, *type_size always\n--\nkernel/bpf/btf.c=2102=__btf_resolve_size(const struct btf *btf, const struct btf_type *type,\n--\nkernel/bpf/btf.c-2112-\t\t/* type-\u003esize can be used */\nkernel/bpf/btf.c:2113:\t\tcase BTF_KIND_INT:\nkernel/bpf/btf.c:2114:\t\tcase BTF_KIND_STRUCT:\nkernel/bpf/btf.c:2115:\t\tcase BTF_KIND_UNION:\nkernel/bpf/btf.c:2116:\t\tcase BTF_KIND_ENUM:\nkernel/bpf/btf.c:2117:\t\tcase BTF_KIND_FLOAT:\nkernel/bpf/btf.c:2118:\t\tcase BTF_KIND_ENUM64:\nkernel/bpf/btf.c-2119-\t\t\tsize = type-\u003esize;\n--\nkernel/bpf/btf.c-2121-\nkernel/bpf/btf.c:2122:\t\tcase BTF_KIND_PTR:\nkernel/bpf/btf.c-2123-\t\t\tsize = sizeof(void *);\n--\nkernel/bpf/btf.c-2126-\t\t/* Modifiers */\nkernel/bpf/btf.c:2127:\t\tcase BTF_KIND_TYPEDEF:\nkernel/bpf/btf.c:2128:\t\tcase BTF_KIND_VOLATILE:\nkernel/bpf/btf.c:2129:\t\tcase BTF_KIND_CONST:\nkernel/bpf/btf.c:2130:\t\tcase BTF_KIND_RESTRICT:\nkernel/bpf/btf.c:2131:\t\tcase BTF_KIND_TYPE_TAG:\nkernel/bpf/btf.c-2132-\t\t\tid = type-\u003etype;\n--\nkernel/bpf/btf.c-2135-\nkernel/bpf/btf.c:2136:\t\tcase BTF_KIND_ARRAY:\nkernel/bpf/btf.c-2137-\t\t\tif (!array_type)\n--\nkernel/bpf/btf.c=2735=static int btf_ref_type_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2758-\t */\nkernel/bpf/btf.c:2759:\tif (BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_TYPEDEF) {\nkernel/bpf/btf.c-2760-\t\tif (!t-\u003ename_off ||\n--\nkernel/bpf/btf.c-2764-\t\t}\nkernel/bpf/btf.c:2765:\t} else if (BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_TYPE_TAG) {\nkernel/bpf/btf.c-2766-\t\tvalue = btf_name_by_offset(env-\u003ebtf, t-\u003ename_off);\n--\nkernel/bpf/btf.c=3324=static s32 btf_struct_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-3327-{\nkernel/bpf/btf.c:3328:\tbool is_union = BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_UNION;\nkernel/bpf/btf.c-3329-\tconst struct btf_member *member;\n--\nkernel/bpf/btf.c=3672=btf_find_graph_root(const struct btf *btf, const struct btf_type *pt,\n--\nkernel/bpf/btf.c-3694-\t\treturn -ENOMEM;\nkernel/bpf/btf.c:3695:\tid = btf_find_by_name_kind(btf, value_type, BTF_KIND_STRUCT);\nkernel/bpf/btf.c-3696-\tkfree(value_type);\n--\nkernel/bpf/btf.c=5112=static const struct btf_kind_operations func_proto_ops = {\n--\nkernel/bpf/btf.c-5115-\t/*\nkernel/bpf/btf.c:5116:\t * BTF_KIND_FUNC_PROTO cannot be directly referred by\nkernel/bpf/btf.c-5117-\t * a struct's member.\n--\nkernel/bpf/btf.c-5119-\t * It should be a function pointer instead.\nkernel/bpf/btf.c:5120:\t * (i.e. struct's member -\u003e BTF_KIND_PTR -\u003e BTF_KIND_FUNC_PROTO)\nkernel/bpf/btf.c-5121-\t *\n--\nkernel/bpf/btf.c=5692=static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {\nkernel/bpf/btf.c:5693:\t[BTF_KIND_INT] = \u0026int_ops,\nkernel/bpf/btf.c:5694:\t[BTF_KIND_PTR] = \u0026ptr_ops,\nkernel/bpf/btf.c:5695:\t[BTF_KIND_ARRAY] = \u0026array_ops,\nkernel/bpf/btf.c:5696:\t[BTF_KIND_STRUCT] = \u0026struct_ops,\nkernel/bpf/btf.c:5697:\t[BTF_KIND_UNION] = \u0026struct_ops,\nkernel/bpf/btf.c:5698:\t[BTF_KIND_ENUM] = \u0026enum_ops,\nkernel/bpf/btf.c:5699:\t[BTF_KIND_FWD] = \u0026fwd_ops,\nkernel/bpf/btf.c:5700:\t[BTF_KIND_TYPEDEF] = \u0026modifier_ops,\nkernel/bpf/btf.c:5701:\t[BTF_KIND_VOLATILE] = \u0026modifier_ops,\nkernel/bpf/btf.c:5702:\t[BTF_KIND_CONST] = \u0026modifier_ops,\nkernel/bpf/btf.c:5703:\t[BTF_KIND_RESTRICT] = \u0026modifier_ops,\nkernel/bpf/btf.c:5704:\t[BTF_KIND_FUNC] = \u0026func_ops,\nkernel/bpf/btf.c:5705:\t[BTF_KIND_FUNC_PROTO] = \u0026func_proto_ops,\nkernel/bpf/btf.c:5706:\t[BTF_KIND_VAR] = \u0026var_ops,\nkernel/bpf/btf.c:5707:\t[BTF_KIND_DATASEC] = \u0026datasec_ops,\nkernel/bpf/btf.c:5708:\t[BTF_KIND_FLOAT] = \u0026float_ops,\nkernel/bpf/btf.c:5709:\t[BTF_KIND_DECL_TAG] = \u0026decl_tag_ops,\nkernel/bpf/btf.c:5710:\t[BTF_KIND_TYPE_TAG] = \u0026modifier_ops,\nkernel/bpf/btf.c:5711:\t[BTF_KIND_ENUM64] = \u0026enum64_ops,\nkernel/bpf/btf.c:5712:\t[BTF_KIND_LOC_PARAM] = \u0026loc_param_ops,\nkernel/bpf/btf.c:5713:\t[BTF_KIND_LOC_PROTO] = \u0026loc_proto_ops,\nkernel/bpf/btf.c:5714:\t[BTF_KIND_LOCSEC] = \u0026locsec_ops,\nkernel/bpf/btf.c-5715-};\n--\nkernel/bpf/btf.c=5717=static s32 btf_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5730-\nkernel/bpf/btf.c:5731:\tif (BTF_INFO_KIND(t-\u003einfo) \u003e BTF_KIND_MAX ||\nkernel/bpf/btf.c:5732:\t BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_UNKN) {\nkernel/bpf/btf.c-5733-\t\tbtf_verifier_log(env, \"[%u] Invalid kind:%u\",\n--\nkernel/bpf/btf.c=6125=btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf)\n--\nkernel/bpf/btf.c-6145-\nkernel/bpf/btf.c:6146:\t\tid = btf_find_by_name_kind(btf, alloc_obj_fields[i], BTF_KIND_STRUCT);\nkernel/bpf/btf.c-6147-\t\tif (id \u003c 0)\n--\nkernel/bpf/btf.c=7462=static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,\n--\nkernel/bpf/btf.c-7482-\tvlen = btf_type_vlen(t);\nkernel/bpf/btf.c:7483:\tif (BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_UNION \u0026\u0026 vlen != 1 \u0026\u0026 !(*flag \u0026 PTR_UNTRUSTED))\nkernel/bpf/btf.c-7484-\t\t/*\n--\nkernel/bpf/btf.c=7913=int btf_distill_func_proto(struct bpf_verifier_log *log,\n--\nkernel/bpf/btf.c-7984-/* Compare BTFs of two functions assuming only scalars and pointers to context.\nkernel/bpf/btf.c:7985: * t1 points to BTF_KIND_FUNC in btf1\nkernel/bpf/btf.c:7986: * t2 points to BTF_KIND_FUNC in btf2\nkernel/bpf/btf.c-7987- * Returns:\n"
} |
| 57/3 | 2026/09/26 16:59 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 53577 output: 38 thoughts: 490 Thoughts: **Analyzing BTF Meta Data Checks**
Okay, let's dive deep into `btf_loc_param_check_meta`, `btf_loc_proto_check_meta`, and `btf_locsec_check_meta`. I need to ensure there are no subtle bugs here. Specifically, I'm focusing on the potential for integer overflows in the calculations.
Let's start with `btf_loc_param_check_meta`. I'm immediately scrutinizing this line: `meta_needed = sizeof(*p) + sizeof(__u32) * vlen;`. My primary concern is if `meta_needed` could overflow.
Okay, let's break it down: `sizeof(*p)` is 4 bytes, as `struct btf_loc_param` only contains a single `__u32 flags` field. Now, `vlen` comes from `btf_vlen(t)`, which expands to `BTF_INFO_VLEN(t->info)`. Ah, right, that macro: `#define BTF_INFO_VLEN(info) ((info) & 0xffff)`. So, `vlen` is a 16-bit value, meaning its maximum value is 65535. Therefore, `sizeof(__u32) * vlen` is at most `4 * 65535 = 262140`. Thus, `meta_needed` cannot overflow a 32-bit integer. It seems safe.
Now, wait a moment; I have to keep the entire context in mind. `meta_left` is also a `u32`. This makes me think.
My previous concern is cleared, but I have a related concern, how does `btf_loc_param(t)` work?
Let's see: `static inline struct btf_loc_param *btf_loc_param(const struct btf_type *t) { return (struct btf_loc_param *)(t + 1); }`
Ah, it is using pointer arithmetic.
Hold on, I also need to make sure that the `btf_check_meta` function verifies that `meta_left` is at least the size of `struct btf_type` before calling the specific `check_meta` function to prevent reading past the end of the `btf_type` structure.
Time to check `btf_check_meta`...
|
| 58/3 | 2026/09/26 16:59 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/btf.c",
"FirstLine": 5715,
"LineCount": 45
}Results: {
"Contents": "5715:\t};\n5716:\t\n5717:\tstatic s32 btf_check_meta(struct btf_verifier_env *env,\n5718:\t\t\t\t const struct btf_type *t,\n5719:\t\t\t\t u32 meta_left)\n5720:\t{\n5721:\t\tu32 saved_meta_left = meta_left;\n5722:\t\ts32 var_meta_size;\n5723:\t\n5724:\t\tif (meta_left \u003c sizeof(*t)) {\n5725:\t\t\tbtf_verifier_log(env, \"[%u] meta_left:%u meta_needed:%zu\",\n5726:\t\t\t\t\t env-\u003elog_type_id, meta_left, sizeof(*t));\n5727:\t\t\treturn -EINVAL;\n5728:\t\t}\n5729:\t\tmeta_left -= sizeof(*t);\n5730:\t\n5731:\t\tif (BTF_INFO_KIND(t-\u003einfo) \u003e BTF_KIND_MAX ||\n5732:\t\t BTF_INFO_KIND(t-\u003einfo) == BTF_KIND_UNKN) {\n5733:\t\t\tbtf_verifier_log(env, \"[%u] Invalid kind:%u\",\n5734:\t\t\t\t\t env-\u003elog_type_id, BTF_INFO_KIND(t-\u003einfo));\n5735:\t\t\treturn -EINVAL;\n5736:\t\t}\n5737:\t\n5738:\t\tif (!btf_name_offset_valid(env-\u003ebtf, t-\u003ename_off)) {\n5739:\t\t\tbtf_verifier_log(env, \"[%u] Invalid name_offset:%u\",\n5740:\t\t\t\t\t env-\u003elog_type_id, t-\u003ename_off);\n5741:\t\t\treturn -EINVAL;\n5742:\t\t}\n5743:\t\n5744:\t\tvar_meta_size = btf_type_ops(t)-\u003echeck_meta(env, t, meta_left);\n5745:\t\tif (var_meta_size \u003c 0)\n5746:\t\t\treturn var_meta_size;\n5747:\t\n5748:\t\tmeta_left -= var_meta_size;\n5749:\t\n5750:\t\treturn saved_meta_left - meta_left;\n5751:\t}\n5752:\t\n5753:\tstatic int btf_check_all_metas(struct btf_verifier_env *env)\n5754:\t{\n5755:\t\tstruct btf *btf = env-\u003ebtf;\n5756:\t\tstruct btf_header *hdr;\n5757:\t\tvoid *cur, *end;\n5758:\t\n5759:\t\thdr = \u0026btf-\u003ehdr;\n"
} |
| 59/3 | 2026/09/26 16:59 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 55372 output: 37 thoughts: 330 Thoughts: **Analyzing the Metadata Check in BTF Verification** Okay, so `btf_check_meta` is subtracting `sizeof(*t)` and passing `meta_left` to `ops->check_meta`. Now I'm in `btf_loc_param_check_meta`. I see it's trying to validate the metadata associated with a local parameter, specifically calculating `meta_needed` based on the size of `btf_loc_param` and the `vlen` of the type. Hold on, `p = btf_loc_param(t)` – that's just pointer arithmetic, effectively `(t + 1)` cast to `btf_loc_param *`. It's not actually dereferencing the pointer right away, it's just setting up an address. But when is `p` actually dereferenced? I need to check the usages to be sure of the state of the pointer. Now I'm remembering that `btf_loc_param_log` and `btf_verifier_log_type` might be where it's actually accessed. Wait a second, is `p` dereferenced *in* `btf_loc_param_check_meta` itself? Let me look more closely. Aha! I see `btf_verifier_log_type(env, t, NULL);`. So, *that's* what's going on. I need to figure out what `btf_verifier_log_type` does. Let's dig into that function's implementation and see if it dereferences `p`. I'll trace what it does with `t`. |
| 60/3 | 2026/09/26 16:59 | tool | grepper |
0mArgs:{
"Expression": "btf_verifier_log_type",
"PathPrefix": "kernel/bpf/btf.c"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 502 lines.\nUse more precise expression if possible.\n\nkernel/bpf/btf.c=1570=__printf(2, 3) static void btf_verifier_log(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-1583-\nkernel/bpf/btf.c:1584:__printf(4, 5) static void __btf_verifier_log_type(struct btf_verifier_env *env,\nkernel/bpf/btf.c-1585-\t\t\t\t\t\t const struct btf_type *t,\n--\nkernel/bpf/btf.c-1597-\t\t/* btf verifier prints all types it is processing via\nkernel/bpf/btf.c:1598:\t\t * btf_verifier_log_type(..., fmt = NULL).\nkernel/bpf/btf.c-1599-\t\t * Skip those prints for in-kernel BTF verification.\n--\nkernel/bpf/btf.c-1627-\nkernel/bpf/btf.c:1628:#define btf_verifier_log_type(env, t, ...) \\\nkernel/bpf/btf.c:1629:\t__btf_verifier_log_type((env), (t), true, __VA_ARGS__)\nkernel/bpf/btf.c-1630-#define btf_verifier_log_basic(env, t, ...) \\\nkernel/bpf/btf.c:1631:\t__btf_verifier_log_type((env), (t), false, __VA_ARGS__)\nkernel/bpf/btf.c-1632-\n--\nkernel/bpf/btf.c=1634=static void btf_verifier_log_member(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-1661-\tif (env-\u003ephase != CHECK_META)\nkernel/bpf/btf.c:1662:\t\tbtf_verifier_log_type(env, struct_type, NULL);\nkernel/bpf/btf.c-1663-\n--\nkernel/bpf/btf.c=1687=static void btf_verifier_log_vsi(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-1699-\tif (env-\u003ephase != CHECK_META)\nkernel/bpf/btf.c:1700:\t\tbtf_verifier_log_type(env, datasec_type, NULL);\nkernel/bpf/btf.c-1701-\n--\nkernel/bpf/btf.c=2395=static s32 btf_int_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2409-\tif (btf_type_vlen(t)) {\nkernel/bpf/btf.c:2410:\t\tbtf_verifier_log_type(env, t, \"vlen != 0\");\nkernel/bpf/btf.c-2411-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-2414-\tif (btf_type_kflag(t)) {\nkernel/bpf/btf.c:2415:\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\nkernel/bpf/btf.c-2416-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-2428-\tif (nr_bits \u003e BITS_PER_U128) {\nkernel/bpf/btf.c:2429:\t\tbtf_verifier_log_type(env, t, \"nr_bits exceeds %zu\",\nkernel/bpf/btf.c-2430-\t\t\t\t BITS_PER_U128);\n--\nkernel/bpf/btf.c-2434-\tif (BITS_ROUNDUP_BYTES(nr_bits) \u003e t-\u003esize) {\nkernel/bpf/btf.c:2435:\t\tbtf_verifier_log_type(env, t, \"nr_bits exceeds type_size\");\nkernel/bpf/btf.c-2436-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-2449-\t encoding != BTF_INT_BOOL) {\nkernel/bpf/btf.c:2450:\t\tbtf_verifier_log_type(env, t, \"Unsupported encoding\");\nkernel/bpf/btf.c-2451-\t\treturn -ENOTSUPP;\n--\nkernel/bpf/btf.c-2453-\nkernel/bpf/btf.c:2454:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-2455-\n--\nkernel/bpf/btf.c=2735=static int btf_ref_type_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2741-\tif (btf_type_vlen(t)) {\nkernel/bpf/btf.c:2742:\t\tbtf_verifier_log_type(env, t, \"vlen != 0\");\nkernel/bpf/btf.c-2743-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-2746-\tif (btf_type_kflag(t) \u0026\u0026 !btf_type_is_type_tag(t)) {\nkernel/bpf/btf.c:2747:\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\nkernel/bpf/btf.c-2748-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-2751-\tif (!BTF_TYPE_ID_VALID(t-\u003etype)) {\nkernel/bpf/btf.c:2752:\t\tbtf_verifier_log_type(env, t, \"Invalid type_id\");\nkernel/bpf/btf.c-2753-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-2761-\t\t !btf_name_valid_identifier(env-\u003ebtf, t-\u003ename_off)) {\nkernel/bpf/btf.c:2762:\t\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-2763-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-2767-\t\tif (!value || !value[0]) {\nkernel/bpf/btf.c:2768:\t\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-2769-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-2772-\t\tif (t-\u003ename_off) {\nkernel/bpf/btf.c:2773:\t\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-2774-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-2777-\nkernel/bpf/btf.c:2778:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-2779-\n--\nkernel/bpf/btf.c=2783=static int btf_modifier_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2792-\tif (!next_type || btf_type_is_resolve_source_only(next_type)) {\nkernel/bpf/btf.c:2793:\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid type_id\");\nkernel/bpf/btf.c-2794-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-2814-\t\t !btf_type_is_func_proto(next_type)) {\nkernel/bpf/btf.c:2815:\t\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid type_id\");\nkernel/bpf/btf.c-2816-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c=2825=static int btf_var_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2834-\tif (!next_type || btf_type_is_resolve_source_only(next_type)) {\nkernel/bpf/btf.c:2835:\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid type_id\");\nkernel/bpf/btf.c-2836-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-2861-\tif (!btf_type_id_size(btf, \u0026next_type_id, NULL)) {\nkernel/bpf/btf.c:2862:\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid type_id\");\nkernel/bpf/btf.c-2863-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c=2871=static int btf_ptr_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2880-\tif (!next_type || btf_type_is_resolve_source_only(next_type)) {\nkernel/bpf/btf.c:2881:\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid type_id\");\nkernel/bpf/btf.c-2882-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-2917-\t\t !btf_type_is_func_proto(next_type)) {\nkernel/bpf/btf.c:2918:\t\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid type_id\");\nkernel/bpf/btf.c-2919-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c=3007=static s32 btf_fwd_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-3011-\tif (btf_type_vlen(t)) {\nkernel/bpf/btf.c:3012:\t\tbtf_verifier_log_type(env, t, \"vlen != 0\");\nkernel/bpf/btf.c-3013-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3016-\tif (t-\u003etype) {\nkernel/bpf/btf.c:3017:\t\tbtf_verifier_log_type(env, t, \"type != 0\");\nkernel/bpf/btf.c-3018-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3023-\t !btf_name_valid_identifier(env-\u003ebtf, t-\u003ename_off)) {\nkernel/bpf/btf.c:3024:\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-3025-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3027-\nkernel/bpf/btf.c:3028:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-3029-\n--\nkernel/bpf/btf.c=3077=static s32 btf_array_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-3092-\tif (t-\u003ename_off) {\nkernel/bpf/btf.c:3093:\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-3094-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3097-\tif (btf_type_vlen(t)) {\nkernel/bpf/btf.c:3098:\t\tbtf_verifier_log_type(env, t, \"vlen != 0\");\nkernel/bpf/btf.c-3099-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3102-\tif (btf_type_kflag(t)) {\nkernel/bpf/btf.c:3103:\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\nkernel/bpf/btf.c-3104-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3107-\tif (t-\u003esize) {\nkernel/bpf/btf.c:3108:\t\tbtf_verifier_log_type(env, t, \"size != 0\");\nkernel/bpf/btf.c-3109-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3115-\tif (!array-\u003etype || !BTF_TYPE_ID_VALID(array-\u003etype)) {\nkernel/bpf/btf.c:3116:\t\tbtf_verifier_log_type(env, t, \"Invalid elem\");\nkernel/bpf/btf.c-3117-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3120-\tif (!array-\u003eindex_type || !BTF_TYPE_ID_VALID(array-\u003eindex_type)) {\nkernel/bpf/btf.c:3121:\t\tbtf_verifier_log_type(env, t, \"Invalid index\");\nkernel/bpf/btf.c-3122-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3124-\nkernel/bpf/btf.c:3125:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-3126-\n--\nkernel/bpf/btf.c=3130=static int btf_array_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-3143-\t btf_type_is_resolve_source_only(index_type)) {\nkernel/bpf/btf.c:3144:\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid index\");\nkernel/bpf/btf.c-3145-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3154-\t !btf_type_int_is_regular(index_type)) {\nkernel/bpf/btf.c:3155:\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid index\");\nkernel/bpf/btf.c-3156-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3163-\t btf_type_is_resolve_source_only(elem_type)) {\nkernel/bpf/btf.c:3164:\t\tbtf_verifier_log_type(env, v-\u003et,\nkernel/bpf/btf.c-3165-\t\t\t\t \"Invalid elem\");\n--\nkernel/bpf/btf.c-3174-\tif (!elem_type) {\nkernel/bpf/btf.c:3175:\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid elem\");\nkernel/bpf/btf.c-3176-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3179-\tif (btf_type_is_int(elem_type) \u0026\u0026 !btf_type_int_is_regular(elem_type)) {\nkernel/bpf/btf.c:3180:\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid array of int\");\nkernel/bpf/btf.c-3181-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3184-\tif (array-\u003enelems \u0026\u0026 elem_size \u003e U32_MAX / array-\u003enelems) {\nkernel/bpf/btf.c:3185:\t\tbtf_verifier_log_type(env, v-\u003et,\nkernel/bpf/btf.c-3186-\t\t\t\t \"Array size overflows U32_MAX\");\n--\nkernel/bpf/btf.c=3324=static s32 btf_struct_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-3346-\t !btf_name_valid_identifier(env-\u003ebtf, t-\u003ename_off)) {\nkernel/bpf/btf.c:3347:\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-3348-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-3350-\nkernel/bpf/btf.c:3351:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-3352-\n--\nkernel/bpf/btf.c=4545=static s32 btf_enum_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-4565-\tif (t-\u003esize \u003e 8 || !is_power_of_2(t-\u003esize)) {\nkernel/bpf/btf.c:4566:\t\tbtf_verifier_log_type(env, t, \"Unexpected size\");\nkernel/bpf/btf.c-4567-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-4572-\t !btf_name_valid_identifier(env-\u003ebtf, t-\u003ename_off)) {\nkernel/bpf/btf.c:4573:\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-4574-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-4576-\nkernel/bpf/btf.c:4577:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-4578-\n--\nkernel/bpf/btf.c-4588-\t\t !btf_name_valid_identifier(btf, enums[i].name_off)) {\nkernel/bpf/btf.c:4589:\t\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-4590-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c=4653=static s32 btf_enum64_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-4673-\tif (t-\u003esize \u003e 8 || !is_power_of_2(t-\u003esize)) {\nkernel/bpf/btf.c:4674:\t\tbtf_verifier_log_type(env, t, \"Unexpected size\");\nkernel/bpf/btf.c-4675-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-4680-\t !btf_name_valid_identifier(env-\u003ebtf, t-\u003ename_off)) {\nkernel/bpf/btf.c:4681:\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-4682-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-4684-\nkernel/bpf/btf.c:4685:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-4686-\n--\nkernel/bpf/btf.c-4696-\t\t !btf_name_valid_identifier(btf, enums[i].name_off)) {\nkernel/bpf/btf.c:4697:\t\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-4698-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c=4756=static s32 btf_loc_param_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-4771-\tif (t-\u003ename_off) {\nkernel/bpf/btf.c:4772:\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-4773-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-4776-\tif (!size || size \u003e 16) {\nkernel/bpf/btf.c:4777:\t\tbtf_verifier_log_type(env, t, \"Unexpected size\");\nkernel/bpf/btf.c-4778-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-4781-\tif (btf_type_kflag(t)) {\nkernel/bpf/btf.c:4782:\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\nkernel/bpf/btf.c-4783-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-4785-\nkernel/bpf/btf.c:4786:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-4787-\n--\nkernel/bpf/btf.c=4811=static s32 btf_loc_proto_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-4826-\tif (t-\u003ename_off) {\nkernel/bpf/btf.c:4827:\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-4828-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-4831-\tif (btf_type_kflag(t)) {\nkernel/bpf/btf.c:4832:\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\nkernel/bpf/btf.c-4833-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-4835-\nkernel/bpf/btf.c:4836:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-4837-\n--\nkernel/bpf/btf.c=4852=static int btf_loc_proto_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-4860-\tif (t-\u003etype) {\nkernel/bpf/btf.c:4861:\t\tbtf_verifier_log_type(env, t, \"Invalid loc_proto type\");\nkernel/bpf/btf.c-4862-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-4873-\t\tif (!param_type || !btf_type_is_loc_param(param_type)) {\nkernel/bpf/btf.c:4874:\t\t\tbtf_verifier_log_type(env, t,\nkernel/bpf/btf.c-4875-\t\t\t\t\t \"Invalid loc_param#%u\", i + 1);\n--\nkernel/bpf/btf.c=4894=static void btf_verifier_log_loc(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-4906-\tif (env-\u003ephase != CHECK_META)\nkernel/bpf/btf.c:4907:\t\tbtf_verifier_log_type(env, locsec_type, NULL);\nkernel/bpf/btf.c-4908-\n--\nkernel/bpf/btf.c=4921=static s32 btf_locsec_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-4937-\tif (btf_type_kflag(t)) {\nkernel/bpf/btf.c:4938:\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\nkernel/bpf/btf.c-4939-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-4943-\t !btf_name_valid_section(env-\u003ebtf, t-\u003ename_off)) {\nkernel/bpf/btf.c:4944:\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-4945-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c=4970=static int btf_locsec_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-4978-\tif (t-\u003etype) {\nkernel/bpf/btf.c:4979:\t\tbtf_verifier_log_type(env, t, \"Invalid locsec type\");\nkernel/bpf/btf.c-4980-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-4993-\t\tif (!func_type || !btf_type_is_func(func_type)) {\nkernel/bpf/btf.c:4994:\t\t\tbtf_verifier_log_type(env, t,\nkernel/bpf/btf.c-4995-\t\t\t\t\t \"Invalid func#%u\", i + 1);\n--\nkernel/bpf/btf.c-4999-\t\tif (!func_proto_type || !btf_type_is_func_proto(func_proto_type)) {\nkernel/bpf/btf.c:5000:\t\t\tbtf_verifier_log_type(env, t,\nkernel/bpf/btf.c-5001-\t\t\t\t\t \"Invalid func#%u\", i + 1);\n--\nkernel/bpf/btf.c-5012-\t\tif (!loc_proto_type || !btf_type_is_loc_proto(loc_proto_type)) {\nkernel/bpf/btf.c:5013:\t\t\tbtf_verifier_log_type(env, t,\nkernel/bpf/btf.c-5014-\t\t\t\t\t \"Invalid loc_proto#%u\", i + 1);\n--\nkernel/bpf/btf.c-5017-\t\tif (proto_vlen != btf_vlen(loc_proto_type)) {\nkernel/bpf/btf.c:5018:\t\t\tbtf_verifier_log_type(env, t,\nkernel/bpf/btf.c-5019-\t\t\t\t\t \"Mismatched vlen for loc_proto#%u\", i + 1);\n--\nkernel/bpf/btf.c=5043=static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5056-\tif (t-\u003ename_off) {\nkernel/bpf/btf.c:5057:\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-5058-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5061-\tif (btf_type_kflag(t)) {\nkernel/bpf/btf.c:5062:\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\nkernel/bpf/btf.c-5063-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5065-\nkernel/bpf/btf.c:5066:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-5067-\n--\nkernel/bpf/btf.c=5130=static s32 btf_func_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5135-\t !btf_name_valid_identifier(env-\u003ebtf, t-\u003ename_off)) {\nkernel/bpf/btf.c:5136:\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-5137-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5140-\tif (btf_type_vlen(t) \u003e BTF_FUNC_GLOBAL) {\nkernel/bpf/btf.c:5141:\t\tbtf_verifier_log_type(env, t, \"Invalid func linkage\");\nkernel/bpf/btf.c-5142-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5145-\tif (btf_type_kflag(t)) {\nkernel/bpf/btf.c:5146:\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\nkernel/bpf/btf.c-5147-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5149-\nkernel/bpf/btf.c:5150:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-5151-\n--\nkernel/bpf/btf.c=5179=static s32 btf_var_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5193-\tif (btf_type_vlen(t)) {\nkernel/bpf/btf.c:5194:\t\tbtf_verifier_log_type(env, t, \"vlen != 0\");\nkernel/bpf/btf.c-5195-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5198-\tif (btf_type_kflag(t)) {\nkernel/bpf/btf.c:5199:\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\nkernel/bpf/btf.c-5200-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5204-\t !btf_name_valid_identifier(env-\u003ebtf, t-\u003ename_off)) {\nkernel/bpf/btf.c:5205:\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-5206-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5210-\tif (!t-\u003etype || !BTF_TYPE_ID_VALID(t-\u003etype)) {\nkernel/bpf/btf.c:5211:\t\tbtf_verifier_log_type(env, t, \"Invalid type_id\");\nkernel/bpf/btf.c-5212-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5217-\t var-\u003elinkage != BTF_VAR_GLOBAL_ALLOCATED) {\nkernel/bpf/btf.c:5218:\t\tbtf_verifier_log_type(env, t, \"Linkage not supported\");\nkernel/bpf/btf.c-5219-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5221-\nkernel/bpf/btf.c:5222:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-5223-\n--\nkernel/bpf/btf.c=5243=static s32 btf_datasec_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5259-\tif (!t-\u003esize) {\nkernel/bpf/btf.c:5260:\t\tbtf_verifier_log_type(env, t, \"size == 0\");\nkernel/bpf/btf.c-5261-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5264-\tif (btf_type_kflag(t)) {\nkernel/bpf/btf.c:5265:\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\nkernel/bpf/btf.c-5266-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5270-\t !btf_name_valid_section(env-\u003ebtf, t-\u003ename_off)) {\nkernel/bpf/btf.c:5271:\t\tbtf_verifier_log_type(env, t, \"Invalid name\");\nkernel/bpf/btf.c-5272-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5274-\nkernel/bpf/btf.c:5275:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-5276-\n--\nkernel/bpf/btf.c-5308-\tif (t-\u003esize \u003c sum) {\nkernel/bpf/btf.c:5309:\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info size\");\nkernel/bpf/btf.c-5310-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c=5395=static s32 btf_float_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5399-\tif (btf_type_vlen(t)) {\nkernel/bpf/btf.c:5400:\t\tbtf_verifier_log_type(env, t, \"vlen != 0\");\nkernel/bpf/btf.c-5401-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5404-\tif (btf_type_kflag(t)) {\nkernel/bpf/btf.c:5405:\t\tbtf_verifier_log_type(env, t, \"Invalid btf_info kind_flag\");\nkernel/bpf/btf.c-5406-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5410-\t t-\u003esize != 16) {\nkernel/bpf/btf.c:5411:\t\tbtf_verifier_log_type(env, t, \"Invalid type_size\");\nkernel/bpf/btf.c-5412-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5414-\nkernel/bpf/btf.c:5415:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-5416-\n--\nkernel/bpf/btf.c=5470=static s32 btf_decl_tag_check_meta(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5487-\tif (!value || !value[0]) {\nkernel/bpf/btf.c:5488:\t\tbtf_verifier_log_type(env, t, \"Invalid value\");\nkernel/bpf/btf.c-5489-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5492-\tif (btf_type_vlen(t)) {\nkernel/bpf/btf.c:5493:\t\tbtf_verifier_log_type(env, t, \"vlen != 0\");\nkernel/bpf/btf.c-5494-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5498-\tif (component_idx \u003c -1) {\nkernel/bpf/btf.c:5499:\t\tbtf_verifier_log_type(env, t, \"Invalid component_idx\");\nkernel/bpf/btf.c-5500-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5502-\nkernel/bpf/btf.c:5503:\tbtf_verifier_log_type(env, t, NULL);\nkernel/bpf/btf.c-5504-\n--\nkernel/bpf/btf.c=5508=static int btf_decl_tag_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5519-\tif (!next_type || !btf_type_is_decl_tag_target(next_type)) {\nkernel/bpf/btf.c:5520:\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid type_id\");\nkernel/bpf/btf.c-5521-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5530-\t\tif (btf_type_is_var(next_type) || btf_type_is_typedef(next_type)) {\nkernel/bpf/btf.c:5531:\t\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid component_idx\");\nkernel/bpf/btf.c-5532-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5543-\t\tif ((u32)component_idx \u003e= vlen) {\nkernel/bpf/btf.c:5544:\t\t\tbtf_verifier_log_type(env, v-\u003et, \"Invalid component_idx\");\nkernel/bpf/btf.c-5545-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c=5569=static int btf_func_proto_check(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5587-\t\tif (!ret_type) {\nkernel/bpf/btf.c:5588:\t\t\tbtf_verifier_log_type(env, t, \"Invalid return type\");\nkernel/bpf/btf.c-5589-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5592-\t\tif (btf_type_is_resolve_source_only(ret_type)) {\nkernel/bpf/btf.c:5593:\t\t\tbtf_verifier_log_type(env, t, \"Invalid return type\");\nkernel/bpf/btf.c-5594-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5605-\t\tif (!btf_type_id_size(btf, \u0026ret_type_id, NULL)) {\nkernel/bpf/btf.c:5606:\t\t\tbtf_verifier_log_type(env, t, \"Invalid return type\");\nkernel/bpf/btf.c-5607-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5616-\t\tif (args[nr_args - 1].name_off) {\nkernel/bpf/btf.c:5617:\t\t\tbtf_verifier_log_type(env, t, \"Invalid arg#%u\",\nkernel/bpf/btf.c-5618-\t\t\t\t\t nr_args);\n--\nkernel/bpf/btf.c-5630-\t\tif (!arg_type) {\nkernel/bpf/btf.c:5631:\t\t\tbtf_verifier_log_type(env, t, \"Invalid arg#%u\", i + 1);\nkernel/bpf/btf.c-5632-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5635-\t\tif (btf_type_is_resolve_source_only(arg_type)) {\nkernel/bpf/btf.c:5636:\t\t\tbtf_verifier_log_type(env, t, \"Invalid arg#%u\", i + 1);\nkernel/bpf/btf.c-5637-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5642-\t\t !btf_name_valid_identifier(btf, args[i].name_off))) {\nkernel/bpf/btf.c:5643:\t\t\tbtf_verifier_log_type(env, t,\nkernel/bpf/btf.c-5644-\t\t\t\t\t \"Invalid arg#%u\", i + 1);\n--\nkernel/bpf/btf.c-5655-\t\tif (!btf_type_id_size(btf, \u0026arg_type_id, NULL)) {\nkernel/bpf/btf.c:5656:\t\t\tbtf_verifier_log_type(env, t, \"Invalid arg#%u\", i + 1);\nkernel/bpf/btf.c-5657-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c=5664=static int btf_func_check(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5675-\tif (!proto_type || !btf_type_is_func_proto(proto_type)) {\nkernel/bpf/btf.c:5676:\t\tbtf_verifier_log_type(env, t, \"Invalid type_id\");\nkernel/bpf/btf.c-5677-\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c-5683-\t\tif (!args[i].name_off \u0026\u0026 args[i].type) {\nkernel/bpf/btf.c:5684:\t\t\tbtf_verifier_log_type(env, t, \"Invalid arg#%u\", i + 1);\nkernel/bpf/btf.c-5685-\t\t\treturn -EINVAL;\n--\nkernel/bpf/btf.c=5822=static int btf_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5837-\tif (err == -E2BIG) {\nkernel/bpf/btf.c:5838:\t\tbtf_verifier_log_type(env, t,\nkernel/bpf/btf.c-5839-\t\t\t\t \"Exceeded max resolving depth:%u\",\n--\nkernel/bpf/btf.c-5841-\t} else if (err == -EEXIST) {\nkernel/bpf/btf.c:5842:\t\tbtf_verifier_log_type(env, t, \"Loop detected\");\nkernel/bpf/btf.c-5843-\t}\n--\nkernel/bpf/btf.c-5846-\tif (!err \u0026\u0026 !btf_resolve_valid(env, t, type_id)) {\n\n"
} |
| 61/3 | 2026/09/26 16:59 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 67316 output: 38 thoughts: 38 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience and presented in the first person: **Analyzing the Behavior of `__btf_verifier_log_type` with a NULL Format String** Right, let's dive into the guts of `__btf_verifier_log_type`. My primary interest here is understanding its edge-case behavior, specifically what happens when the `fmt` argument is passed as `NULL`. We all know BTF verification can be tricky, and understanding these null-pointer scenarios is crucial for robustness. I want to see if it handles a missing format string gracefully, perhaps with a default, or if it triggers some kind of error, crash, or undefined behavior. I need to know how resilient this function is in the face of what should be a straightforward check, and how the program handles the situation. Is this a code path that's tested or is this a place where security flaws might arise? Let's trace it and find out. |
| 62/3 | 2026/09/26 16:59 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/btf.c",
"FirstLine": 1584,
"LineCount": 40
}Results: {
"Contents": "1584:\t__printf(4, 5) static void __btf_verifier_log_type(struct btf_verifier_env *env,\n1585:\t\t\t\t\t\t\t const struct btf_type *t,\n1586:\t\t\t\t\t\t\t bool log_details,\n1587:\t\t\t\t\t\t\t const char *fmt, ...)\n1588:\t{\n1589:\t\tstruct bpf_verifier_log *log = \u0026env-\u003elog;\n1590:\t\tstruct btf *btf = env-\u003ebtf;\n1591:\t\tva_list args;\n1592:\t\n1593:\t\tif (!bpf_verifier_log_needed(log))\n1594:\t\t\treturn;\n1595:\t\n1596:\t\tif (log-\u003elevel == BPF_LOG_KERNEL) {\n1597:\t\t\t/* btf verifier prints all types it is processing via\n1598:\t\t\t * btf_verifier_log_type(..., fmt = NULL).\n1599:\t\t\t * Skip those prints for in-kernel BTF verification.\n1600:\t\t\t */\n1601:\t\t\tif (!fmt)\n1602:\t\t\t\treturn;\n1603:\t\n1604:\t\t\t/* Skip logging when loading module BTF with mismatches permitted */\n1605:\t\t\tif (env-\u003ebtf-\u003ebase_btf \u0026\u0026 IS_ENABLED(CONFIG_MODULE_ALLOW_BTF_MISMATCH))\n1606:\t\t\t\treturn;\n1607:\t\t}\n1608:\t\n1609:\t\t__btf_verifier_log(log, \"[%u] %s %s%s\",\n1610:\t\t\t\t env-\u003elog_type_id,\n1611:\t\t\t\t btf_type_str(t),\n1612:\t\t\t\t __btf_name_by_offset(btf, t-\u003ename_off),\n1613:\t\t\t\t log_details ? \" \" : \"\");\n1614:\t\n1615:\t\tif (log_details)\n1616:\t\t\tbtf_type_ops(t)-\u003elog_details(env, t);\n1617:\t\n1618:\t\tif (fmt \u0026\u0026 *fmt) {\n1619:\t\t\t__btf_verifier_log(log, \" \");\n1620:\t\t\tva_start(args, fmt);\n1621:\t\t\tbpf_verifier_vlog(log, fmt, args);\n1622:\t\t\tva_end(args);\n1623:\t\t}\n"
} |
| 63/3 | 2026/09/26 16:59 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 68239 output: 33 thoughts: 1082 Thoughts: **Analyzing BTF Metadata Validation** Okay, let's break this down. My initial focus is on how metadata is validated within the BTF (BPF Type Format) verifier. The core idea is to ensure that enough memory is available for the metadata associated with different BTF types. I'm starting with `btf_loc_param_check_meta`. Here, I see a check to ensure that `meta_left` (the remaining available metadata size) is sufficient to hold the size of a `btf_loc_param` struct, which is `sizeof(*p)` plus an array of `__u32` values `vlen` long. If there isn't enough memory (`meta_left < meta_needed`), a basic log message is generated, and the function returns an error. This log uses `btf_verifier_log_basic`, which I note doesn't involve detailed logging. However, if enough memory *is* available, then `btf_verifier_log_type` is called. This, in turn, calls `btf_loc_param_log`. This logs the size, vlen, and flags of the struct, then the `vlen` values. This is all within `meta_needed`. Now, things get interesting. I'm comparing this with `btf_loc_proto_check_meta`, and a pattern is emerging. Here, it checks if `meta_left` is enough for an array of `__u32` values, `btf_type_vlen(t)` long. If not, it logs a basic message and returns. If there is enough memory, then `btf_verifier_log_type` is called, which handles logging of the parameters. Again, this is all within `meta_needed`. I then investigate `btf_locsec_check_meta`. Here the code checks if `meta_left` is enough to hold an array of `struct btf_loc` elements, `vlen` long. If `meta_left` is smaller than `meta_needed`, then an error is returned. If there's sufficient space, the code loops through the `btf_loc` array. Each loc must have valid function and proto IDs, otherwise a verifier log is created, and an error is returned. If everything checks out, then `btf_verifier_log_loc` is called. This is all within `meta_needed`. Okay, the memory checks are consistent. The general structure seems to be: calculate the needed metadata size, check if it fits, log a basic error if not, and then proceed with further processing if it does. This seems to be consistent across various BTF type checks. But, a bigger question looms: Where does this memory come from in the first place? I need to track down the source of the BTF data that these checks are validating. This means I need to dig into the system calls responsible for loading BTF information. My next step will be to explore `btf_new_fd` and `btf_parse` to understand how BTF data is loaded from userspace, specifically via `bpf(BPF_BTF_LOAD, ...)` calls. |
| 64/3 | 2026/09/26 17:00 | tool | grepper |
0mArgs:{
"Expression": "btf_parse",
"PathPrefix": "kernel/bpf/btf.c"
}Results: {
"Output": "kernel/bpf/btf.c=4000=static int btf_find_field(const struct btf *btf, const struct btf_type *t,\n--\nkernel/bpf/btf.c-4013-/* Callers have to ensure the life cycle of btf if it is program BTF */\nkernel/bpf/btf.c:4014:static int btf_parse_kptr(const struct btf *btf, struct btf_field *field,\nkernel/bpf/btf.c-4015-\t\t\t struct btf_field_info *info)\n--\nkernel/bpf/btf.c-4032-\tif (id == -ENOENT) {\nkernel/bpf/btf.c:4033:\t\t/* btf_parse_kptr should only be called w/ btf = program BTF */\nkernel/bpf/btf.c-4034-\t\tWARN_ON_ONCE(btf_is_kernel(btf));\n--\nkernel/bpf/btf.c-4103-\nkernel/bpf/btf.c:4104:static int btf_parse_graph_root(const struct btf *btf,\nkernel/bpf/btf.c-4105-\t\t\t\tstruct btf_field *field,\n--\nkernel/bpf/btf.c-4147-\nkernel/bpf/btf.c:4148:static int btf_parse_list_head(const struct btf *btf, struct btf_field *field,\nkernel/bpf/btf.c-4149-\t\t\t struct btf_field_info *info)\nkernel/bpf/btf.c-4150-{\nkernel/bpf/btf.c:4151:\treturn btf_parse_graph_root(btf, field, info, \"bpf_list_node\",\nkernel/bpf/btf.c-4152-\t\t\t\t\t __alignof__(struct bpf_list_node));\n--\nkernel/bpf/btf.c-4154-\nkernel/bpf/btf.c:4155:static int btf_parse_rb_root(const struct btf *btf, struct btf_field *field,\nkernel/bpf/btf.c-4156-\t\t\t struct btf_field_info *info)\nkernel/bpf/btf.c-4157-{\nkernel/bpf/btf.c:4158:\treturn btf_parse_graph_root(btf, field, info, \"bpf_rb_node\",\nkernel/bpf/btf.c-4159-\t\t\t\t\t __alignof__(struct bpf_rb_node));\n--\nkernel/bpf/btf.c=4162=static int btf_field_cmp(const void *_a, const void *_b, const void *priv)\n--\nkernel/bpf/btf.c-4173-\nkernel/bpf/btf.c:4174:struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type *t,\nkernel/bpf/btf.c-4175-\t\t\t\t u32 field_mask, u32 value_size)\n--\nkernel/bpf/btf.c-4253-\t\tcase BPF_UPTR:\nkernel/bpf/btf.c:4254:\t\t\tret = btf_parse_kptr(btf, \u0026rec-\u003efields[i], \u0026info_arr[i]);\nkernel/bpf/btf.c-4255-\t\t\tif (ret \u003c 0)\n--\nkernel/bpf/btf.c-4258-\t\tcase BPF_LIST_HEAD:\nkernel/bpf/btf.c:4259:\t\t\tret = btf_parse_list_head(btf, \u0026rec-\u003efields[i], \u0026info_arr[i]);\nkernel/bpf/btf.c-4260-\t\t\tif (ret \u003c 0)\n--\nkernel/bpf/btf.c-4263-\t\tcase BPF_RB_ROOT:\nkernel/bpf/btf.c:4264:\t\t\tret = btf_parse_rb_root(btf, \u0026rec-\u003efields[i], \u0026info_arr[i]);\nkernel/bpf/btf.c-4265-\t\t\tif (ret \u003c 0)\n--\nkernel/bpf/btf.c=5855=static int btf_check_all_types(struct btf_verifier_env *env)\n--\nkernel/bpf/btf.c-5888-\nkernel/bpf/btf.c:5889:static int btf_parse_type_sec(struct btf_verifier_env *env)\nkernel/bpf/btf.c-5890-{\n--\nkernel/bpf/btf.c-5911-\nkernel/bpf/btf.c:5912:static int btf_parse_str_sec(struct btf_verifier_env *env)\nkernel/bpf/btf.c-5913-{\n--\nkernel/bpf/btf.c-5943-\nkernel/bpf/btf.c:5944:static int btf_parse_layout_sec(struct btf_verifier_env *env)\nkernel/bpf/btf.c-5945-{\n--\nkernel/bpf/btf.c=5994=static int btf_check_sec_info(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-6050-\nkernel/bpf/btf.c:6051:static int btf_parse_hdr(struct btf_verifier_env *env)\nkernel/bpf/btf.c-6052-{\n--\nkernel/bpf/btf.c=6124=static struct btf_struct_metas *\nkernel/bpf/btf.c:6125:btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf)\nkernel/bpf/btf.c-6126-{\n--\nkernel/bpf/btf.c-6225-\t\ttype-\u003ebtf_id = i;\nkernel/bpf/btf.c:6226:\t\trecord = btf_parse_fields(btf, t, BPF_SPIN_LOCK | BPF_RES_SPIN_LOCK | BPF_LIST_HEAD | BPF_LIST_NODE |\nkernel/bpf/btf.c-6227-\t\t\t\t\t\t BPF_RB_ROOT | BPF_RB_NODE | BPF_REFCOUNT |\n--\nkernel/bpf/btf.c=6257=static int btf_check_modifier_chain_length(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-6294-\nkernel/bpf/btf.c:6295:static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr,\nkernel/bpf/btf.c-6296-\t\t\t struct bpf_log_attr *attr_log)\n--\nkernel/bpf/btf.c-6340-\nkernel/bpf/btf.c:6341:\terr = btf_parse_hdr(env);\nkernel/bpf/btf.c-6342-\tif (err)\n--\nkernel/bpf/btf.c-6346-\nkernel/bpf/btf.c:6347:\terr = btf_parse_str_sec(env);\nkernel/bpf/btf.c-6348-\tif (err)\n--\nkernel/bpf/btf.c-6350-\nkernel/bpf/btf.c:6351:\terr = btf_parse_layout_sec(env);\nkernel/bpf/btf.c-6352-\tif (err)\n--\nkernel/bpf/btf.c-6354-\nkernel/bpf/btf.c:6355:\terr = btf_parse_type_sec(env);\nkernel/bpf/btf.c-6356-\tif (err)\n--\nkernel/bpf/btf.c-6362-\nkernel/bpf/btf.c:6363:\tstruct_meta_tab = btf_parse_struct_metas(\u0026env-\u003elog, btf);\nkernel/bpf/btf.c-6364-\tif (IS_ERR(struct_meta_tab)) {\n--\nkernel/bpf/btf.c=6729=BTF_ID_LIST_SINGLE(bpf_ctx_convert_btf_id, struct, bpf_ctx_convert)\nkernel/bpf/btf.c-6730-\nkernel/bpf/btf.c:6731:static struct btf *btf_parse_base(struct btf_verifier_env *env, const char *name,\nkernel/bpf/btf.c-6732-\t\t\t\t void *data, unsigned int data_size)\n--\nkernel/bpf/btf.c-6752-\nkernel/bpf/btf.c:6753:\terr = btf_parse_hdr(env);\nkernel/bpf/btf.c-6754-\tif (err)\n--\nkernel/bpf/btf.c-6758-\nkernel/bpf/btf.c:6759:\terr = btf_parse_str_sec(env);\nkernel/bpf/btf.c-6760-\tif (err)\n--\nkernel/bpf/btf.c-6783-\nkernel/bpf/btf.c:6784:struct btf *btf_parse_vmlinux(void)\nkernel/bpf/btf.c-6785-{\n--\nkernel/bpf/btf.c-6796-\tlog-\u003elevel = BPF_LOG_KERNEL;\nkernel/bpf/btf.c:6797:\tbtf = btf_parse_base(env, \"vmlinux\", __start_BTF, __stop_BTF - __start_BTF);\nkernel/bpf/btf.c-6798-\tif (IS_ERR(btf))\n--\nkernel/bpf/btf.c-6800-\nkernel/bpf/btf.c:6801:\t/* btf_parse_vmlinux() runs under btf_vmlinux_lock */\nkernel/bpf/btf.c-6802-\tbpf_ctx_convert.t = btf_type_by_id(btf, bpf_ctx_convert_btf_id[0]);\n--\nkernel/bpf/btf.c=6817=__u32 btf_relocate_id(const struct btf *btf, __u32 id)\n--\nkernel/bpf/btf.c-6825-\nkernel/bpf/btf.c:6826:static struct btf *btf_parse_module(const char *module_name, const void *data,\nkernel/bpf/btf.c-6827-\t\t\t\t unsigned int data_size, void *base_data,\n--\nkernel/bpf/btf.c-6848-\tif (base_data) {\nkernel/bpf/btf.c:6849:\t\tbase_btf = btf_parse_base(env, \".BTF.base\", base_data, base_data_size);\nkernel/bpf/btf.c-6850-\t\tif (IS_ERR(base_btf)) {\n--\nkernel/bpf/btf.c-6878-\nkernel/bpf/btf.c:6879:\terr = btf_parse_hdr(env);\nkernel/bpf/btf.c-6880-\tif (err)\n--\nkernel/bpf/btf.c-6884-\nkernel/bpf/btf.c:6885:\terr = btf_parse_str_sec(env);\nkernel/bpf/btf.c-6886-\tif (err)\n--\nkernel/bpf/btf.c=8766=int btf_new_fd(const union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_attr *attr_log)\n--\nkernel/bpf/btf.c-8770-\nkernel/bpf/btf.c:8771:\tbtf = btf_parse(attr, uattr, attr_log);\nkernel/bpf/btf.c-8772-\tif (IS_ERR(btf))\n--\nkernel/bpf/btf.c=8920=static int btf_module_notify(struct notifier_block *nb, unsigned long op,\n--\nkernel/bpf/btf.c-8939-\t\t}\nkernel/bpf/btf.c:8940:\t\tbtf = btf_parse_module(mod-\u003ename, mod-\u003ebtf_data, mod-\u003ebtf_data_size,\nkernel/bpf/btf.c-8941-\t\t\t\t mod-\u003ebtf_base_data, mod-\u003ebtf_base_data_size);\n"
} |
| 65/3 | 2026/09/26 17:00 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 73423 output: 38 thoughts: 41 Thoughts: Okay, let's dive into this `btf_parse` allocation. I need to understand how the `btf->data` field gets its memory. I'll focus on the code around line 6300 in `kernel/bpf/btf.c`. I expect to see the core memory allocation strategy there, likely using something like `kmalloc` or `vmalloc`, perhaps even a custom allocator depending on the complexity of the BTF data structures. I'll be looking for any hints on sizing calculations - is it a static size determined upfront, or is it dynamically computed based on the input BTF data? And I should pay close attention to the error handling within this allocation code – what are the failure paths, and what mechanisms are in place to clean up any partially allocated resources? This allocation is critical for the BTF parser to function correctly. I'll also check if the allocation is page aligned as it has performance implications. |
| 66/3 | 2026/09/26 17:00 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/btf.c",
"FirstLine": 6300,
"LineCount": 45
}Results: {
"Contents": "6300:\t\tstruct btf_verifier_env *env = NULL;\n6301:\t\tstruct btf *btf = NULL;\n6302:\t\tu8 *data;\n6303:\t\tint err, ret;\n6304:\t\n6305:\t\tif (attr-\u003ebtf_size \u003e BTF_MAX_SIZE)\n6306:\t\t\treturn ERR_PTR(-E2BIG);\n6307:\t\n6308:\t\tenv = kzalloc_obj(*env, GFP_KERNEL | __GFP_NOWARN);\n6309:\t\tif (!env)\n6310:\t\t\treturn ERR_PTR(-ENOMEM);\n6311:\t\n6312:\t\t/* user could have requested verbose verifier output\n6313:\t\t * and supplied buffer to store the verification trace\n6314:\t\t */\n6315:\t\terr = bpf_vlog_init(\u0026env-\u003elog, attr_log-\u003elevel, attr_log-\u003eubuf, attr_log-\u003esize);\n6316:\t\tif (err)\n6317:\t\t\tgoto errout_free;\n6318:\t\n6319:\t\tbtf = kzalloc_obj(*btf, GFP_KERNEL | __GFP_NOWARN);\n6320:\t\tif (!btf) {\n6321:\t\t\terr = -ENOMEM;\n6322:\t\t\tgoto errout;\n6323:\t\t}\n6324:\t\tenv-\u003ebtf = btf;\n6325:\t\tbtf-\u003enamed_start_id = 0;\n6326:\t\n6327:\t\tdata = kvmalloc(attr-\u003ebtf_size, GFP_KERNEL | __GFP_NOWARN);\n6328:\t\tif (!data) {\n6329:\t\t\terr = -ENOMEM;\n6330:\t\t\tgoto errout;\n6331:\t\t}\n6332:\t\n6333:\t\tbtf-\u003edata = data;\n6334:\t\tbtf-\u003edata_size = attr-\u003ebtf_size;\n6335:\t\n6336:\t\tif (copy_from_bpfptr(data, btf_data, attr-\u003ebtf_size)) {\n6337:\t\t\terr = -EFAULT;\n6338:\t\t\tgoto errout;\n6339:\t\t}\n6340:\t\n6341:\t\terr = btf_parse_hdr(env);\n6342:\t\tif (err)\n6343:\t\t\tgoto errout;\n6344:\t\n"
} |
| 67/3 | 2026/09/26 17:00 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 74352 output: 34 thoughts: 919 Thoughts: **Diving into BTF Locsec Resolution** Okay, so I'm looking at how BTF data gets loaded, whether it's from user space or pulled in from a module or vmlinux. Now, I want to understand `btf_locsec_resolve`. This seems like a critical piece. I see that it's handling `struct btf_loc`, which appears to be within the context of a `btf_type` and specifically a "locsec" type. The function iterates through the members of a locsec, each potentially representing a function location. The first thing it does is ensure the type is valid. After that I see the loop that goes through each location and does some checks. Within this loop, it grabs the `func` and `loc_proto` IDs from the `btf_loc` struct. Then, it uses those IDs to get the corresponding `btf_type` entries for both the function type and the location prototype. It validates that the function type is actually a function and the prototype is actually a prototype. A crucial check involves whether these types have already been "resolved" within the environment. If the function type is *not* resolved, the function pushes it onto a stack, essentially deferring resolution until later. Similarly, it checks the location prototype and does the same. This is where I start thinking. Hold on... I need to be more precise about the initialization flow. What is the initial value of `v->next_member` when this whole process starts? That's key to how the first elements are handled. I need to understand how the resolution process is set up to fully grasp this. I'm thinking I should look at how the stack is initialized and how things get pushed and popped and how this is applied to other types, like datasec or struct, to gain a clearer picture. |
| 68/3 | 2026/09/26 17:00 | tool | grepper |
0mArgs:{
"Expression": "env_stack_push",
"PathPrefix": "kernel/bpf/btf.c"
}Results: {
"Output": "kernel/bpf/btf.c=2017=static bool env_type_is_resolved(const struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2026-\nkernel/bpf/btf.c:2027:static int env_stack_push(struct btf_verifier_env *env,\nkernel/bpf/btf.c-2028-\t\t\t const struct btf_type *t, u32 type_id)\n--\nkernel/bpf/btf.c=2783=static int btf_modifier_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2798-\t !env_type_is_resolved(env, next_type_id))\nkernel/bpf/btf.c:2799:\t\treturn env_stack_push(env, next_type, next_type_id);\nkernel/bpf/btf.c-2800-\n--\nkernel/bpf/btf.c=2825=static int btf_var_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2840-\t !env_type_is_resolved(env, next_type_id))\nkernel/bpf/btf.c:2841:\t\treturn env_stack_push(env, next_type, next_type_id);\nkernel/bpf/btf.c-2842-\n--\nkernel/bpf/btf.c-2852-\t\t !env_type_is_resolved(env, resolved_type_id))\nkernel/bpf/btf.c:2853:\t\t\treturn env_stack_push(env, resolved_type,\nkernel/bpf/btf.c-2854-\t\t\t\t\t resolved_type_id);\n--\nkernel/bpf/btf.c=2871=static int btf_ptr_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-2886-\t !env_type_is_resolved(env, next_type_id))\nkernel/bpf/btf.c:2887:\t\treturn env_stack_push(env, next_type, next_type_id);\nkernel/bpf/btf.c-2888-\n--\nkernel/bpf/btf.c-2906-\t\t !env_type_is_resolved(env, resolved_type_id))\nkernel/bpf/btf.c:2907:\t\t\treturn env_stack_push(env, resolved_type,\nkernel/bpf/btf.c-2908-\t\t\t\t\t resolved_type_id);\n--\nkernel/bpf/btf.c=3130=static int btf_array_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-3149-\t !env_type_is_resolved(env, index_type_id))\nkernel/bpf/btf.c:3150:\t\treturn env_stack_push(env, index_type, index_type_id);\nkernel/bpf/btf.c-3151-\n--\nkernel/bpf/btf.c-3170-\t !env_type_is_resolved(env, elem_type_id))\nkernel/bpf/btf.c:3171:\t\treturn env_stack_push(env, elem_type, elem_type_id);\nkernel/bpf/btf.c-3172-\n--\nkernel/bpf/btf.c=3405=static int btf_struct_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-3455-\t\t\tenv_stack_set_next_member(env, i + 1);\nkernel/bpf/btf.c:3456:\t\t\treturn env_stack_push(env, member_type, member_type_id);\nkernel/bpf/btf.c-3457-\t\t}\n--\nkernel/bpf/btf.c=4970=static int btf_locsec_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5007-\t\t\tenv_stack_set_next_member(env, i);\nkernel/bpf/btf.c:5008:\t\t\treturn env_stack_push(env, func_type, func_type_id);\nkernel/bpf/btf.c-5009-\t\t}\n--\nkernel/bpf/btf.c-5024-\t\t\tenv_stack_set_next_member(env, i + 1);\nkernel/bpf/btf.c:5025:\t\t\treturn env_stack_push(env, loc_proto_type,\nkernel/bpf/btf.c-5026-\t\t\t\t\t loc_proto_type_id);\n--\nkernel/bpf/btf.c=5316=static int btf_datasec_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5336-\t\t\tenv_stack_set_next_member(env, i + 1);\nkernel/bpf/btf.c:5337:\t\t\treturn env_stack_push(env, var_type, var_type_id);\nkernel/bpf/btf.c-5338-\t\t}\n--\nkernel/bpf/btf.c=5508=static int btf_decl_tag_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5525-\t !env_type_is_resolved(env, next_type_id))\nkernel/bpf/btf.c:5526:\t\treturn env_stack_push(env, next_type, next_type_id);\nkernel/bpf/btf.c-5527-\n--\nkernel/bpf/btf.c=5822=static int btf_resolve(struct btf_verifier_env *env,\n--\nkernel/bpf/btf.c-5829-\tenv-\u003eresolve_mode = RESOLVE_TBD;\nkernel/bpf/btf.c:5830:\tenv_stack_push(env, t, type_id);\nkernel/bpf/btf.c-5831-\twhile (!err \u0026\u0026 (v = env_stack_peak(env))) {\n"
} |
| 69/3 | 2026/09/26 17:00 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 77341 output: 38 thoughts: 11 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person: **Deep Dive into `env_stack_push`** Alright, so let's get right into it, shall we? My first instinct is to zero in on `env_stack_push`. That's where it all begins, the cornerstone, really. I need to refresh my mental model of its behavior – how it interacts with the current environment, what data structures it utilizes, and most importantly, what assumptions it makes about the state of things *before* it gets called. Is it thread-safe? Does it handle re-entrancy gracefully, or is there a chance of corruption if multiple threads are vying for the stack simultaneously? I need to quickly assess the possible error conditions. What checks are in place to ensure a clean push? Are we talking about memory allocation failures, stack overflow, or invalid environment pointers? And what happens on those failures? Does it propagate an error code or throw an exception? The devil is in the details here. I'm thinking about the implications for debugging – how easy is it to trace a problem back to a specific `env_stack_push` call if something goes awry later? Then, the performance implications. What's the computational complexity? Is it O(1) or is there more involved? Any locking overhead if it's thread-safe? Could this function be a bottleneck in a high-concurrency system? If so, are there any potential optimizations – perhaps using a lock-free data structure or some sort of pre-allocation strategy? This demands some careful consideration, as I'm thinking this function might be the heart of the system. |
| 70/3 | 2026/09/26 17:00 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/btf.c",
"FirstLine": 2025,
"LineCount": 25
}Results: {
"Contents": "2025:\t}\n2026:\t\n2027:\tstatic int env_stack_push(struct btf_verifier_env *env,\n2028:\t\t\t\t const struct btf_type *t, u32 type_id)\n2029:\t{\n2030:\t\tconst struct btf *btf = env-\u003ebtf;\n2031:\t\tstruct resolve_vertex *v;\n2032:\t\n2033:\t\tif (env-\u003etop_stack == MAX_RESOLVE_DEPTH)\n2034:\t\t\treturn -E2BIG;\n2035:\t\n2036:\t\tif (type_id \u003c btf-\u003estart_id\n2037:\t\t || env-\u003evisit_states[type_id - btf-\u003estart_id] != NOT_VISITED)\n2038:\t\t\treturn -EEXIST;\n2039:\t\n2040:\t\tenv-\u003evisit_states[type_id - btf-\u003estart_id] = VISITED;\n2041:\t\n2042:\t\tv = \u0026env-\u003estack[env-\u003etop_stack++];\n2043:\t\tv-\u003et = t;\n2044:\t\tv-\u003etype_id = type_id;\n2045:\t\tv-\u003enext_member = 0;\n2046:\t\n2047:\t\tif (env-\u003eresolve_mode == RESOLVE_TBD) {\n2048:\t\t\tif (btf_type_is_ptr(t))\n2049:\t\t\t\tenv-\u003eresolve_mode = RESOLVE_PTR;\n"
} |
| 71/3 | 2026/09/26 17:00 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 78152 output: 316 thoughts: 3785 Thoughts: **Summary of the BTF Verifier Analysis** Okay, so I've been diving deep into this BTF verifier code, trying to see if we need to flag this patch for KMSAN. The core of this patch seems to be adding validation and resolution logic for new BTF types: `LOC_PARAM`, `LOC_PROTO`, and `LOCSEC`. My thought process has been: First, I'm tracing the data flow through the `btf_locsec_resolve` function and how it resolves type dependencies, noting how we set `v->next_member` and use `env_type_is_resolved` to track the state of these dependent types. The verifier uses an environment stack to handle recursive type dependencies. I'm focusing on how `func_type` and `loc_proto_type` are handled in this resolution process. Next, I jumped into `btf_loc_proto_resolve` to understand its role. This function checks the validity of local protocol parameters. I noted that it calls `env_stack_pop_resolved`, which means that it immediately pops entries off the stack if the `loc_param` types are valid. I then did a deeper look to find out what requires the `btf_loc_param` resolution. The `btf_type_is_resolve_source_only` check revealed that it does NOT need to be resolved. This is because its resolve operation is to just immediately pop it. I'm trying to see where these types are used and how their resolution affects the overall BTF validation process. I also looked at `btf_resolve_valid` to see how the verifier determines if a type needs resolution. Then, I looked at `btf_locsec_check_meta`. Here, I'm checking for potential issues with uninitialized memory. I noted that it checks `func` and `loc_proto` fields within the `loc` structure, and it logs errors if these are invalid. I was worried that `btf_verifier_log_loc` was called with uninitialized memory, but this function does not use uninitialized memory because the `loc` struct is properly initialized within verified bounds. I then briefly looked at the `btf_field_desc` struct. It doesn't seem to be used anywhere which is irrelevant in this context. Now, let's talk about the KMSAN stuff. KMSAN is here to help detect uninitialized memory. I'm focusing on whether this patch introduces any new risks in that area. I'm evaluating: 1. Allocation: Are we allocating any kernel structures that aren't initialized? No. 2. Info Leaks: Does the patch modify any paths that could leak uninitialized kernel memory to userspace? No. 3. Stack/Heap usage: Are there any stack or heap buffers allocated and used? All buffers are cleanly initialized before use. 4. User-provided data: The BTF data comes from userspace, so how is it verified? `copy_from_bpfptr` is used, so the data read is considered initialized by KMSAN. Invalid input leads to error return. Finally, I checked the diff, specifically which files were modified. The kernel-space changes are limited to `kernel/bpf/btf.c`, `include/linux/btf.h`, and `include/uapi/linux/btf.h`. The `tools/` directory is also modified, but those are userspace tools, and selftests, irrelevant in this KMSAN evaluation. The core of the patch is about verifying the new BTF types. Any memory safety bugs that could happen here will be due to out-of-bounds accesses which will be detected by KASAN. No kernel structures are being sent to userspace uninitialized. There's no risk here that KMSAN will detect that wouldn't already be caught by KASAN or another standard bug detector. Based on this analysis, I'm going to set `NeedsKMSAN` to `false`. I've looked at all the key concerns and am confident this patch doesn't introduce any new KMSAN risks. |
| 72/3 | 2026/09/26 17:00 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series introduces support for three new BTF kinds (BTF_KIND_LOC_PARAM, BTF_KIND_LOC_PROTO, and BTF_KIND_LOCSEC) representing parameter and site location debug information.\n\nThe changes in the kernel proper are confined to BTF validation and resolution logic in `kernel/bpf/btf.c` and related header definitions:\n1. `btf_loc_param_check_meta()`, `btf_loc_proto_check_meta()`, and `btf_locsec_check_meta()` validate metadata size, flags, and names.\n2. `btf_loc_proto_resolve()` and `btf_locsec_resolve()` validate relationships and references between types.\n3. The rest of the patch updates userspace tooling (`bpftool`, `libbpf`, and selftests).\n\nAny potential bugs in the verifier parsing logic (e.g., buffer indexing or malformed metadata length calculations) would manifest as out-of-bounds reads or invalid pointer dereferences, which are detected by KASAN. The patch does not allocate or copy uninitialized kernel memory, does not modify kernel-to-user copy paths (no info-leaks), and does not introduce conditional branches on uninitialized variables. Therefore, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series introduces support for three new BTF kinds (BTF_KIND_LOC_PARAM, BTF_KIND_LOC_PROTO, and BTF_KIND_LOCSEC) representing parameter and site location debug information.\n\nThe changes in the kernel proper are confined to BTF validation and resolution logic in `kernel/bpf/btf.c` and related header definitions:\n1. `btf_loc_param_check_meta()`, `btf_loc_proto_check_meta()`, and `btf_locsec_check_meta()` validate metadata size, flags, and names.\n2. `btf_loc_proto_resolve()` and `btf_locsec_resolve()` validate relationships and references between types.\n3. The rest of the patch updates userspace tooling (`bpftool`, `libbpf`, and selftests).\n\nAny potential bugs in the verifier parsing logic (e.g., buffer indexing or malformed metadata length calculations) would manifest as out-of-bounds reads or invalid pointer dereferences, which are detected by KASAN. The patch does not allocate or copy uninitialized kernel memory, does not modify kernel-to-user copy paths (no info-leaks), and does not introduce conditional branches on uninitialized variables. Therefore, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|