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 : : #include <string.h>
16 : :
17 : : #include "bpfilter/core/vector.h"
18 : : #include "bpfilter/helper.h"
19 : : #include "bpfilter/logger.h"
20 : :
21 : : struct bf_btf_cache_entry
22 : : {
23 : : char *name;
24 : : int id;
25 : : };
26 : :
27 : : static struct btf *_bf_btf = NULL;
28 : : static bf_vector _bf_btf_cache;
29 : :
30 : 440 : int bf_btf_setup(void)
31 : : {
32 [ + + ]: 440 : if (_bf_btf)
33 [ + - ]: 1 : return bf_err_r(-EBUSY, "kernel BTF is already initialized");
34 : :
35 : 439 : _bf_btf = btf__load_vmlinux_btf();
36 [ + + ]: 439 : if (!_bf_btf)
37 [ + - ]: 1 : return bf_err_r(errno, "failed to load vmlinux BTF");
38 : :
39 : 438 : bf_vector_init(&_bf_btf_cache, sizeof(struct bf_btf_cache_entry));
40 : :
41 : 438 : return 0;
42 : : }
43 : :
44 : 438 : void bf_btf_teardown(void)
45 : : {
46 [ + + + + ]: 1515 : bf_vector_foreach (&_bf_btf_cache, elem) {
47 : : struct bf_btf_cache_entry *entry = elem;
48 : :
49 : 639 : BF_FREEP(&entry->name);
50 : : }
51 : 438 : bf_vector_clean(&_bf_btf_cache);
52 : :
53 : 438 : btf__free(_bf_btf);
54 : 438 : _bf_btf = NULL;
55 : 438 : }
56 : :
57 : 5516 : int bf_btf_get_id(const char *name)
58 : : {
59 : : _cleanup_free_ char *owned_name = NULL;
60 : : struct bf_btf_cache_entry entry;
61 : : int id;
62 : :
63 : : assert(name);
64 : :
65 [ + + + + ]: 13484 : bf_vector_foreach (&_bf_btf_cache, elem) {
66 : : const struct bf_btf_cache_entry *cached = elem;
67 : :
68 [ + + ]: 7327 : if (bf_streq(cached->name, name))
69 : 4875 : return cached->id;
70 : : }
71 : :
72 : 641 : id = btf__find_by_name(_bf_btf, name);
73 [ + + ]: 641 : if (id < 0)
74 [ + - ]: 2 : return bf_err_r(id, "failed to find BTF type for \"%s\"", name);
75 : :
76 : : // Cache allocation failures must not prevent successful lookups.
77 : 639 : owned_name = strdup(name);
78 [ - + ]: 639 : if (!owned_name)
79 : : return id;
80 : :
81 : 639 : entry = (struct bf_btf_cache_entry) {.name = owned_name, .id = id};
82 [ + - ]: 639 : if (!bf_vector_add(&_bf_btf_cache, &entry))
83 : : TAKE_PTR(owned_name);
84 : :
85 : : return id;
86 : : }
87 : :
88 : 6 : const char *bf_btf_get_name(int id)
89 : : {
90 : : const struct btf_type *type;
91 : :
92 : 6 : type = btf__type_by_id(_bf_btf, id);
93 [ + + ]: 6 : if (!type) {
94 [ + - ]: 2 : bf_warn("can't find BTF type ID %d", id);
95 : : return NULL;
96 : : }
97 : :
98 : 4 : return btf__name_by_offset(_bf_btf, type->name_off);
99 : : }
100 : :
101 : 1 : int bf_btf_kernel_has_token(void)
102 : : {
103 : : int bpf_attr_id;
104 : : const struct btf_type *bpf_attr_type;
105 : : const struct btf_member *bpf_attr_members;
106 : :
107 : 1 : bpf_attr_id = btf__find_by_name_kind(_bf_btf, "bpf_attr", BTF_KIND_UNION);
108 [ - + ]: 1 : if (bpf_attr_id < 0) {
109 [ # # ]: 0 : return bf_err_r(bpf_attr_id,
110 : : "can't find structure 'bpf_attr' in kernel BTF");
111 : : }
112 : :
113 : 1 : bpf_attr_type = btf__type_by_id(_bf_btf, bpf_attr_id);
114 [ - + ]: 1 : if (!bpf_attr_type)
115 [ # # ]: 0 : return bf_err_r(-EINVAL, "failed to request 'bpf_attr' BTF type");
116 : :
117 : : bpf_attr_members = btf_members(bpf_attr_type);
118 : : // Iterate through union members
119 [ + - ]: 4 : for (unsigned short i = 0; i < btf_vlen(bpf_attr_type); i++) {
120 : : const struct btf_type *member_type =
121 : 4 : btf__type_by_id(_bf_btf, bpf_attr_members[i].type);
122 : : const struct btf_member *m_members = btf_members(member_type);
123 : :
124 : : // We are looking for an anonymous structure
125 [ + - + + ]: 4 : if (!btf_is_struct(member_type) || bpf_attr_members[i].name_off != 0)
126 : 1 : continue;
127 : :
128 [ + + ]: 51 : for (int j = 0; j < btf_vlen(member_type); j++) {
129 : : const char *member_name =
130 : 49 : btf__name_by_offset(_bf_btf, m_members[j].name_off);
131 : :
132 [ + - + + ]: 49 : if (member_name && bf_streq(member_name, "prog_token_fd"))
133 : : return 0;
134 : : }
135 : : }
136 : :
137 : : return -ENOENT;
138 : : }
139 : :
140 : : #define _bf_btf_type_is_compound(type) \
141 : : (BTF_INFO_KIND((type)->info) == BTF_KIND_UNION || \
142 : : BTF_INFO_KIND((type)->info) == BTF_KIND_STRUCT)
143 : :
144 : : /**
145 : : * @brief Find the offset of `field_name` in BTF type ID `compound_id`.
146 : : *
147 : : * @param compound_id Compound BTF type id.
148 : : * @param field_name Name of the field to find the offset of. Can't be NULL.
149 : : * @return Offset (in bits) of `field_name` in `compound_id` BTF type.
150 : : */
151 : 10613 : static ssize_t _bf_btf_offset_in_compound(uint32_t compound_id,
152 : : const char *field_name)
153 : : {
154 : : ssize_t offset;
155 : : const struct btf_type *compound_type, *member_type;
156 : : const struct btf_member *member;
157 : :
158 : : assert(field_name);
159 : :
160 : 10613 : compound_type = btf__type_by_id(_bf_btf, compound_id);
161 [ - + ]: 10613 : if (!compound_type)
162 : 0 : return -errno;
163 : :
164 [ + - ]: 10613 : if (!_bf_btf_type_is_compound(compound_type))
165 : : return -ENOTSUP;
166 : :
167 : 10613 : member = (const struct btf_member *)(compound_type + 1);
168 [ + + ]: 60643 : for (size_t i = 0; i < BTF_INFO_VLEN(compound_type->info); ++i, ++member) {
169 : 54728 : const char *name = btf__name_by_offset(_bf_btf, member->name_off);
170 : :
171 : 54728 : member_type = btf__type_by_id(_bf_btf, member->type);
172 [ - + ]: 54728 : if (!member_type)
173 : 0 : return -errno;
174 : :
175 : : // Member is an anonymous compound type? Check its members
176 [ + + + - ]: 54728 : if (*name == '\0' && _bf_btf_type_is_compound(member_type)) {
177 : 6011 : offset = _bf_btf_offset_in_compound(member->type, field_name);
178 [ + + ]: 6011 : if (offset < 0)
179 : 5914 : continue;
180 : :
181 [ - + ]: 97 : if (BTF_MEMBER_BIT_OFFSET(member->offset) % 8) {
182 : : // Assuming this is not possible, but who knows
183 [ # # ]: 0 : return bf_err_r(-ENOTSUP,
184 : : "anonymous compound parent is a bitfield");
185 : : }
186 : :
187 [ + + ]: 97 : if (BTF_INFO_KFLAG(compound_type->info))
188 : 63 : offset += BTF_MEMBER_BIT_OFFSET(member->offset);
189 : : else
190 : 34 : offset += member->offset;
191 : :
192 : : return offset;
193 : : }
194 : :
195 [ + + ]: 48717 : if (!bf_streq(name, field_name))
196 : 44116 : continue;
197 : :
198 : 4601 : offset = (ssize_t)member->offset;
199 [ + + ]: 4601 : if (BTF_INFO_KFLAG(compound_type->info))
200 : 1246 : offset = BTF_MEMBER_BIT_OFFSET(member->offset);
201 : :
202 : : return offset;
203 : : }
204 : :
205 : : return -EINVAL;
206 : : }
207 : :
208 : 4603 : static int _bf_btf_get_compound_type_id(const char *name)
209 : : {
210 : : int compound_id;
211 : :
212 : 4603 : compound_id = btf__find_by_name_kind(_bf_btf, name, BTF_KIND_STRUCT);
213 [ + - ]: 4603 : if (compound_id < 0 && compound_id != -ENOENT)
214 : : return compound_id;
215 [ + + ]: 4603 : if (compound_id >= 0)
216 : : return compound_id;
217 : :
218 : 1 : compound_id = btf__find_by_name_kind(_bf_btf, name, BTF_KIND_UNION);
219 : : if (compound_id < 0 && compound_id != -ENOENT)
220 : : return compound_id;
221 : :
222 : : return compound_id;
223 : : }
224 : :
225 : 4603 : int bf_btf_get_field_off(const char *struct_name, const char *field_name)
226 : : {
227 : : int id;
228 : : ssize_t offset;
229 : :
230 : : assert(struct_name);
231 : : assert(field_name);
232 : :
233 : 4603 : id = _bf_btf_get_compound_type_id(struct_name);
234 [ + + ]: 4603 : if (id < 0)
235 [ + - ]: 1 : return bf_err_r(id, "failed to find BTF type ID for '%s'", struct_name);
236 : :
237 : 4602 : offset = _bf_btf_offset_in_compound(id, field_name);
238 [ + + ]: 4602 : if (offset < 0) {
239 [ + - ]: 1 : return bf_err_r((int)offset, "failed to find offset of %s.%s",
240 : : struct_name, field_name);
241 : : }
242 [ + + ]: 4601 : if (offset % 8) {
243 [ + - ]: 1 : return bf_err_r(-EINVAL, "%s.%s has a bit offset", struct_name,
244 : : field_name);
245 : : }
246 [ - + ]: 4600 : if (offset / 8 > INT_MAX) {
247 [ # # ]: 0 : return bf_err_r(-E2BIG, "%s.%s has an offset bigger than %d",
248 : : struct_name, field_name, INT_MAX);
249 : : }
250 : :
251 : 4600 : return (int)(offset / 8);
252 : : }
253 : :
254 : 9 : int bf_btf_get_type_size(const char *name)
255 : : {
256 : : int id;
257 : : __s64 size;
258 : :
259 : : assert(name);
260 : :
261 : 9 : id = btf__find_by_name_kind(_bf_btf, name, BTF_KIND_STRUCT);
262 [ + + ]: 9 : if (id < 0)
263 [ + - ]: 3 : return bf_err_r(id, "failed to find BTF structure '%s'", name);
264 : :
265 : 6 : size = btf__resolve_size(_bf_btf, id);
266 [ - + ]: 6 : if (size < 0)
267 [ # # ]: 0 : return bf_err_r((int)size, "failed to resolve the size of '%s'", name);
268 [ - + ]: 6 : if (size > INT_MAX)
269 [ # # ]: 0 : return bf_err_r(-E2BIG, "'%s' is bigger than %d bytes", name, INT_MAX);
270 : :
271 : 6 : return (int)size;
272 : : }
|