Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : : */
5 : :
6 : : #include "bpfilter/btf.h"
7 : :
8 : : #include <linux/btf.h>
9 : :
10 : : #include <bpf/btf.h>
11 : : #include <errno.h>
12 : : #include <limits.h>
13 : : #include <stddef.h>
14 : : #include <stdlib.h>
15 : :
16 : : #include "bpfilter/helper.h"
17 : : #include "bpfilter/logger.h"
18 : :
19 : : static struct btf *_bf_btf = NULL;
20 : :
21 : 26 : int bf_btf_setup(void)
22 : : {
23 : 26 : _bf_btf = btf__load_vmlinux_btf();
24 [ + + ]: 26 : if (!_bf_btf)
25 [ + - ]: 1 : return bf_err_r(errno, "failed to load vmlinux BTF");
26 : :
27 : : return 0;
28 : : }
29 : :
30 : 24 : void bf_btf_teardown(void)
31 : : {
32 : 24 : btf__free(_bf_btf);
33 : 24 : _bf_btf = NULL;
34 : 24 : }
35 : :
36 : 265 : int bf_btf_get_id(const char *name)
37 : : {
38 : : int id;
39 : :
40 : : bf_assert(name);
41 : :
42 : 265 : id = btf__find_by_name(_bf_btf, name);
43 [ + + ]: 265 : if (id < 0)
44 [ + - ]: 1 : return bf_err_r(errno, "failed to find BTF type for \"%s\"", name);
45 : :
46 : : return id;
47 : : }
48 : :
49 : 3 : const char *bf_btf_get_name(int id)
50 : : {
51 : : const struct btf_type *type;
52 : :
53 : 3 : type = btf__type_by_id(_bf_btf, id);
54 [ + + ]: 3 : if (!type) {
55 [ + - ]: 2 : bf_warn("can't find BTF type ID %d", id);
56 : 2 : return NULL;
57 : : }
58 : :
59 : 1 : return btf__name_by_offset(_bf_btf, type->name_off);
60 : : }
61 : :
62 : 21 : int bf_btf_kernel_has_token(void)
63 : : {
64 : : int bpf_attr_id;
65 : : const struct btf_type *bpf_attr_type;
66 : : const struct btf_member *bpf_attr_members;
67 : :
68 : 21 : bpf_attr_id = btf__find_by_name_kind(_bf_btf, "bpf_attr", BTF_KIND_UNION);
69 [ - + ]: 21 : if (bpf_attr_id < 0) {
70 [ # # ]: 0 : return bf_err_r(bpf_attr_id,
71 : : "can't find structure 'bpf_attr' in kernel BTF");
72 : : }
73 : :
74 : 21 : bpf_attr_type = btf__type_by_id(_bf_btf, bpf_attr_id);
75 [ - + ]: 21 : if (!bpf_attr_type)
76 [ # # ]: 0 : return bf_err_r(-EINVAL, "failed to request 'bpf_attr' BTF type");
77 : :
78 : : bpf_attr_members = btf_members(bpf_attr_type);
79 : : // Iterate through union members
80 [ + - ]: 84 : for (unsigned short i = 0; i < btf_vlen(bpf_attr_type); i++) {
81 : : const struct btf_type *member_type =
82 : 84 : btf__type_by_id(_bf_btf, bpf_attr_members[i].type);
83 : : const struct btf_member *m_members = btf_members(member_type);
84 : :
85 : : // We are looking for an anonymous structure
86 [ + - + + ]: 84 : if (!btf_is_struct(member_type) || bpf_attr_members[i].name_off != 0)
87 : 21 : continue;
88 : :
89 [ + + ]: 1029 : for (int j = 0; j < btf_vlen(member_type); j++) {
90 : : const char *member_name =
91 : 987 : btf__name_by_offset(_bf_btf, m_members[j].name_off);
92 : :
93 [ + - + + ]: 987 : if (member_name && bf_streq(member_name, "prog_token_fd"))
94 : : return 0;
95 : : }
96 : : }
97 : :
98 : : return -ENOENT;
99 : : }
100 : :
101 : : #define _bf_btf_type_is_compound(type) \
102 : : (BTF_INFO_KIND((type)->info) == BTF_KIND_UNION || \
103 : : BTF_INFO_KIND((type)->info) == BTF_KIND_STRUCT)
104 : :
105 : : /**
106 : : * @brief Find the offset of `field_name` in BTF type ID `compound_id`.
107 : : *
108 : : * @param compound_id Compound BTF type id.
109 : : * @param field_name Name of the field to find the offset of. Can't be NULL.
110 : : * @return Offset (in bits) of `field_name` in `compound_id` BTF type.
111 : : */
112 : 379 : static ssize_t _bf_btf_offset_in_compound(uint32_t compound_id,
113 : : const char *field_name)
114 : : {
115 : : ssize_t offset;
116 : : const struct btf_type *compound_type, *member_type;
117 : : const struct btf_member *member;
118 : :
119 : : bf_assert(field_name);
120 : :
121 : 379 : compound_type = btf__type_by_id(_bf_btf, compound_id);
122 [ - + ]: 379 : if (!compound_type)
123 : 0 : return -errno;
124 : :
125 [ - + ]: 379 : if (!_bf_btf_type_is_compound(compound_type))
126 : : return -ENOTSUP;
127 : :
128 : 379 : member = (const struct btf_member *)(compound_type + 1);
129 [ + + ]: 2378 : for (size_t i = 0; i < BTF_INFO_VLEN(compound_type->info); ++i, ++member) {
130 : 2158 : const char *name = btf__name_by_offset(_bf_btf, member->name_off);
131 : :
132 : 2158 : member_type = btf__type_by_id(_bf_btf, member->type);
133 [ - + ]: 2158 : if (!member_type)
134 : 0 : return -errno;
135 : :
136 : : // Member is an anonymous compound type? Check its members
137 [ + + + - ]: 2158 : if (*name == '\0' && _bf_btf_type_is_compound(member_type)) {
138 : 256 : offset = _bf_btf_offset_in_compound(member->type, field_name);
139 [ + + ]: 256 : if (offset < 0)
140 : 219 : continue;
141 : :
142 [ - + ]: 37 : if (BTF_MEMBER_BIT_OFFSET(member->offset) % 8) {
143 : : // Assuming this is not possible, but who knows
144 [ # # ]: 0 : return bf_err_r(-ENOTSUP,
145 : : "anonymous compound parent is a bitfield");
146 : : }
147 : :
148 [ + + ]: 37 : if (BTF_INFO_KFLAG(compound_type->info))
149 : 23 : offset += BTF_MEMBER_BIT_OFFSET(member->offset);
150 : : else
151 : 14 : offset += member->offset;
152 : :
153 : 37 : return offset;
154 : : }
155 : :
156 [ + + ]: 1902 : if (!bf_streq(name, field_name))
157 : 1780 : continue;
158 : :
159 : 122 : offset = (ssize_t)member->offset;
160 [ + + ]: 122 : if (BTF_INFO_KFLAG(compound_type->info))
161 : 28 : offset = BTF_MEMBER_BIT_OFFSET(member->offset);
162 : :
163 : : return offset;
164 : : }
165 : :
166 : : return -EINVAL;
167 : : }
168 : :
169 : 124 : static int _bf_btf_get_compound_type_id(const char *name)
170 : : {
171 : : int compound_id;
172 : :
173 : 124 : compound_id = btf__find_by_name_kind(_bf_btf, name, BTF_KIND_STRUCT);
174 [ + - ]: 124 : if (compound_id < 0 && compound_id != -ENOENT)
175 : : return compound_id;
176 [ + + ]: 124 : if (compound_id >= 0)
177 : : return compound_id;
178 : :
179 : 1 : compound_id = btf__find_by_name_kind(_bf_btf, name, BTF_KIND_UNION);
180 : : if (compound_id < 0 && compound_id != -ENOENT)
181 : : return compound_id;
182 : :
183 : : return compound_id;
184 : : }
185 : :
186 : 124 : int bf_btf_get_field_off(const char *struct_name, const char *field_name)
187 : : {
188 : : int id;
189 : : ssize_t offset;
190 : :
191 : : bf_assert(struct_name);
192 : : bf_assert(field_name);
193 : :
194 : 124 : id = _bf_btf_get_compound_type_id(struct_name);
195 [ + + ]: 124 : if (id < 0)
196 [ + - ]: 1 : return bf_err_r(id, "failed to find BTF type ID for '%s'", struct_name);
197 : :
198 : 123 : offset = _bf_btf_offset_in_compound(id, field_name);
199 [ + + ]: 123 : if (offset < 0) {
200 [ + - ]: 1 : return bf_err_r((int)offset, "failed to find offset of %s.%s",
201 : : struct_name, field_name);
202 : : }
203 [ + + ]: 122 : if (offset % 8) {
204 [ + - ]: 1 : return bf_err_r(-EINVAL, "%s.%s has a bit offset", struct_name,
205 : : field_name);
206 : : }
207 [ - + ]: 121 : if (offset / 8 > INT_MAX) {
208 [ # # ]: 0 : return bf_err_r(-E2BIG, "%s.%s has an offset bigger than %d",
209 : : struct_name, field_name, INT_MAX);
210 : : }
211 : :
212 : 121 : return (int)(offset / 8);
213 : : }
|