Skip to content

Commit dbc3c49

Browse files
Delyan KratunovNobody
Delyan Kratunov
authored and
Nobody
committed
bpftool: bpf skeletons assert type sizes
When emitting type declarations in skeletons, bpftool will now also emit static assertions on the size of the data/bss/rodata/etc fields. This ensures that in situations where userspace and kernel types have the same name but differ in size we do not silently produce incorrect results but instead break the build. This was reported in [1] and as expected the repro in [2] fails to build on the new size assert after this change. [1]: Closes: libbpf/libbpf#433 [2]: https://github.com/fuweid/iovisor-bcc-pr-3777 Signed-off-by: Delyan Kratunov <[email protected]> Acked-by: Hengqi Chen <[email protected]> Tested-by: Hengqi Chen <[email protected]>
1 parent 5632278 commit dbc3c49

File tree

1 file changed

+111
-22
lines changed

1 file changed

+111
-22
lines changed

tools/bpf/bpftool/gen.c

Lines changed: 111 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,38 @@ static int codegen_datasec_def(struct bpf_object *obj,
209209
return 0;
210210
}
211211

212+
static const struct btf_type *find_type_for_map(struct bpf_object *obj,
213+
const char *map_ident)
214+
{
215+
struct btf *btf = bpf_object__btf(obj);
216+
int n = btf__type_cnt(btf), i;
217+
char sec_ident[256];
218+
219+
for (i = 1; i < n; i++) {
220+
const struct btf_type *t = btf__type_by_id(btf, i);
221+
const char *name;
222+
223+
if (!btf_is_datasec(t))
224+
continue;
225+
226+
name = btf__str_by_offset(btf, t->name_off);
227+
if (!get_datasec_ident(name, sec_ident, sizeof(sec_ident)))
228+
continue;
229+
230+
if (strcmp(sec_ident, map_ident) == 0)
231+
return t;
232+
}
233+
return NULL;
234+
}
235+
212236
static int codegen_datasecs(struct bpf_object *obj, const char *obj_name)
213237
{
214238
struct btf *btf = bpf_object__btf(obj);
215-
int n = btf__type_cnt(btf);
216239
struct btf_dump *d;
217240
struct bpf_map *map;
218241
const struct btf_type *sec;
219-
char sec_ident[256], map_ident[256];
220-
int i, err = 0;
242+
char map_ident[256];
243+
int err = 0;
221244

222245
d = btf_dump__new(btf, codegen_btf_dump_printf, NULL, NULL);
223246
err = libbpf_get_error(d);
@@ -234,23 +257,7 @@ static int codegen_datasecs(struct bpf_object *obj, const char *obj_name)
234257
if (!get_map_ident(map, map_ident, sizeof(map_ident)))
235258
continue;
236259

237-
sec = NULL;
238-
for (i = 1; i < n; i++) {
239-
const struct btf_type *t = btf__type_by_id(btf, i);
240-
const char *name;
241-
242-
if (!btf_is_datasec(t))
243-
continue;
244-
245-
name = btf__str_by_offset(btf, t->name_off);
246-
if (!get_datasec_ident(name, sec_ident, sizeof(sec_ident)))
247-
continue;
248-
249-
if (strcmp(sec_ident, map_ident) == 0) {
250-
sec = t;
251-
break;
252-
}
253-
}
260+
sec = find_type_for_map(obj, map_ident);
254261

255262
/* In some cases (e.g., sections like .rodata.cst16 containing
256263
* compiler allocated string constants only) there will be
@@ -363,6 +370,78 @@ static size_t bpf_map_mmap_sz(const struct bpf_map *map)
363370
return map_sz;
364371
}
365372

373+
/* Emit type size asserts for all top-level fields in memory-mapped internal maps.
374+
*/
375+
static void codegen_asserts(struct bpf_object *obj, const char *obj_name)
376+
{
377+
struct btf *btf = bpf_object__btf(obj);
378+
struct bpf_map *map;
379+
struct btf_var_secinfo *sec_var;
380+
int i, vlen;
381+
const struct btf_type *sec;
382+
char map_ident[256], var_ident[256];
383+
384+
codegen("\
385+
\n\
386+
\n\
387+
#ifdef __cplusplus \n\
388+
#define _Static_assert static_assert \n\
389+
#endif \n\
390+
\n\
391+
__attribute__((unused)) static void \n\
392+
%1$s__type_asserts(struct %1$s *s) \n\
393+
{ \n\
394+
", obj_name);
395+
396+
bpf_object__for_each_map(map, obj) {
397+
if (!bpf_map__is_internal(map))
398+
continue;
399+
if (!(bpf_map__map_flags(map) & BPF_F_MMAPABLE))
400+
continue;
401+
if (!get_map_ident(map, map_ident, sizeof(map_ident)))
402+
continue;
403+
404+
sec = find_type_for_map(obj, map_ident);
405+
if (!sec) {
406+
/* best effort, couldn't find the type for this map */
407+
continue;
408+
}
409+
410+
sec_var = btf_var_secinfos(sec);
411+
vlen = btf_vlen(sec);
412+
413+
for (i = 0; i < vlen; i++, sec_var++) {
414+
const struct btf_type *var = btf__type_by_id(btf, sec_var->type);
415+
const char *var_name = btf__name_by_offset(btf, var->name_off);
416+
__u32 var_type_id = var->type;
417+
__s64 var_size = btf__resolve_size(btf, var_type_id);
418+
419+
if (var_size < 0)
420+
continue;
421+
422+
/* static variables are not exposed through BPF skeleton */
423+
if (btf_var(var)->linkage == BTF_VAR_STATIC)
424+
continue;
425+
426+
var_ident[0] = '\0';
427+
strncat(var_ident, var_name, sizeof(var_ident) - 1);
428+
sanitize_identifier(var_ident);
429+
430+
printf("\t_Static_assert(sizeof(s->%1$s->%2$s) == %3$lld, \"unexpected size of '%2$s'\");\n",
431+
map_ident, var_ident, var_size);
432+
}
433+
}
434+
codegen("\
435+
\n\
436+
} \n\
437+
\n\
438+
#ifdef __cplusplus \n\
439+
#undef _Static_assert \n\
440+
#endif \n\
441+
");
442+
}
443+
444+
366445
static void codegen_attach_detach(struct bpf_object *obj, const char *obj_name)
367446
{
368447
struct bpf_program *prog;
@@ -641,6 +720,8 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
641720
} \n\
642721
", obj_name);
643722

723+
codegen_asserts(obj, obj_name);
724+
644725
codegen("\
645726
\n\
646727
\n\
@@ -1046,9 +1127,17 @@ static int do_skeleton(int argc, char **argv)
10461127
const void *%1$s::elf_bytes(size_t *sz) { return %1$s__elf_bytes(sz); } \n\
10471128
#endif /* __cplusplus */ \n\
10481129
\n\
1049-
#endif /* %2$s */ \n\
10501130
",
1051-
obj_name, header_guard);
1131+
obj_name);
1132+
1133+
codegen_asserts(obj, obj_name);
1134+
1135+
codegen("\
1136+
\n\
1137+
\n\
1138+
#endif /* %1$s */ \n\
1139+
",
1140+
header_guard);
10521141
err = 0;
10531142
out:
10541143
bpf_object__close(obj);

0 commit comments

Comments
 (0)