Skip to content

Commit 3903802

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
libbpf: Add basic BTF sanity validation
Implement a simple and straightforward BTF sanity check when parsing BTF data. Right now it's very basic and just validates that all the string offsets and type IDs are within valid range. For FUNC we also check that it points to FUNC_PROTO kinds. Even with such simple checks it fixes a bunch of crashes found by OSS fuzzer ([0]-[5]) and will allow fuzzer to make further progress. Some other invariants will be checked in follow up patches (like ensuring there is no infinite type loops), but this seems like a good start already. Adding FUNC -> FUNC_PROTO check revealed that one of selftests has a problem with FUNC pointing to VAR instead, so fix it up in the same commit. [0] libbpf/libbpf#482 [1] libbpf/libbpf#483 [2] libbpf/libbpf#485 [3] libbpf/libbpf#613 [4] libbpf/libbpf#618 [5] libbpf/libbpf#619 Signed-off-by: Andrii Nakryiko <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Reviewed-by: Alan Maguire <[email protected]> Reviewed-by: Song Liu <[email protected]> Closes: libbpf/libbpf#617 Link: https://lore.kernel.org/bpf/[email protected]
1 parent 73be7fb commit 3903802

File tree

2 files changed

+162
-2
lines changed
  • tools

2 files changed

+162
-2
lines changed

tools/lib/bpf/btf.c

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,165 @@ static int btf_parse_type_sec(struct btf *btf)
448448
return 0;
449449
}
450450

451+
static int btf_validate_str(const struct btf *btf, __u32 str_off, const char *what, __u32 type_id)
452+
{
453+
const char *s;
454+
455+
s = btf__str_by_offset(btf, str_off);
456+
if (!s) {
457+
pr_warn("btf: type [%u]: invalid %s (string offset %u)\n", type_id, what, str_off);
458+
return -EINVAL;
459+
}
460+
461+
return 0;
462+
}
463+
464+
static int btf_validate_id(const struct btf *btf, __u32 id, __u32 ctx_id)
465+
{
466+
const struct btf_type *t;
467+
468+
t = btf__type_by_id(btf, id);
469+
if (!t) {
470+
pr_warn("btf: type [%u]: invalid referenced type ID %u\n", ctx_id, id);
471+
return -EINVAL;
472+
}
473+
474+
return 0;
475+
}
476+
477+
static int btf_validate_type(const struct btf *btf, const struct btf_type *t, __u32 id)
478+
{
479+
__u32 kind = btf_kind(t);
480+
int err, i, n;
481+
482+
err = btf_validate_str(btf, t->name_off, "type name", id);
483+
if (err)
484+
return err;
485+
486+
switch (kind) {
487+
case BTF_KIND_UNKN:
488+
case BTF_KIND_INT:
489+
case BTF_KIND_FWD:
490+
case BTF_KIND_FLOAT:
491+
break;
492+
case BTF_KIND_PTR:
493+
case BTF_KIND_TYPEDEF:
494+
case BTF_KIND_VOLATILE:
495+
case BTF_KIND_CONST:
496+
case BTF_KIND_RESTRICT:
497+
case BTF_KIND_VAR:
498+
case BTF_KIND_DECL_TAG:
499+
case BTF_KIND_TYPE_TAG:
500+
err = btf_validate_id(btf, t->type, id);
501+
if (err)
502+
return err;
503+
break;
504+
case BTF_KIND_ARRAY: {
505+
const struct btf_array *a = btf_array(t);
506+
507+
err = btf_validate_id(btf, a->type, id);
508+
err = err ?: btf_validate_id(btf, a->index_type, id);
509+
if (err)
510+
return err;
511+
break;
512+
}
513+
case BTF_KIND_STRUCT:
514+
case BTF_KIND_UNION: {
515+
const struct btf_member *m = btf_members(t);
516+
517+
n = btf_vlen(t);
518+
for (i = 0; i < n; i++, m++) {
519+
err = btf_validate_str(btf, m->name_off, "field name", id);
520+
err = err ?: btf_validate_id(btf, m->type, id);
521+
if (err)
522+
return err;
523+
}
524+
break;
525+
}
526+
case BTF_KIND_ENUM: {
527+
const struct btf_enum *m = btf_enum(t);
528+
529+
n = btf_vlen(t);
530+
for (i = 0; i < n; i++, m++) {
531+
err = btf_validate_str(btf, m->name_off, "enum name", id);
532+
if (err)
533+
return err;
534+
}
535+
break;
536+
}
537+
case BTF_KIND_ENUM64: {
538+
const struct btf_enum64 *m = btf_enum64(t);
539+
540+
n = btf_vlen(t);
541+
for (i = 0; i < n; i++, m++) {
542+
err = btf_validate_str(btf, m->name_off, "enum name", id);
543+
if (err)
544+
return err;
545+
}
546+
break;
547+
}
548+
case BTF_KIND_FUNC: {
549+
const struct btf_type *ft;
550+
551+
err = btf_validate_id(btf, t->type, id);
552+
if (err)
553+
return err;
554+
ft = btf__type_by_id(btf, t->type);
555+
if (btf_kind(ft) != BTF_KIND_FUNC_PROTO) {
556+
pr_warn("btf: type [%u]: referenced type [%u] is not FUNC_PROTO\n", id, t->type);
557+
return -EINVAL;
558+
}
559+
break;
560+
}
561+
case BTF_KIND_FUNC_PROTO: {
562+
const struct btf_param *m = btf_params(t);
563+
564+
n = btf_vlen(t);
565+
for (i = 0; i < n; i++, m++) {
566+
err = btf_validate_str(btf, m->name_off, "param name", id);
567+
err = err ?: btf_validate_id(btf, m->type, id);
568+
if (err)
569+
return err;
570+
}
571+
break;
572+
}
573+
case BTF_KIND_DATASEC: {
574+
const struct btf_var_secinfo *m = btf_var_secinfos(t);
575+
576+
n = btf_vlen(t);
577+
for (i = 0; i < n; i++, m++) {
578+
err = btf_validate_id(btf, m->type, id);
579+
if (err)
580+
return err;
581+
}
582+
break;
583+
}
584+
default:
585+
pr_warn("btf: type [%u]: unrecognized kind %u\n", id, kind);
586+
return -EINVAL;
587+
}
588+
return 0;
589+
}
590+
591+
/* Validate basic sanity of BTF. It's intentionally less thorough than
592+
* kernel's validation and validates only properties of BTF that libbpf relies
593+
* on to be correct (e.g., valid type IDs, valid string offsets, etc)
594+
*/
595+
static int btf_sanity_check(const struct btf *btf)
596+
{
597+
const struct btf_type *t;
598+
__u32 i, n = btf__type_cnt(btf);
599+
int err;
600+
601+
for (i = 1; i < n; i++) {
602+
t = btf_type_by_id(btf, i);
603+
err = btf_validate_type(btf, t, i);
604+
if (err)
605+
return err;
606+
}
607+
return 0;
608+
}
609+
451610
__u32 btf__type_cnt(const struct btf *btf)
452611
{
453612
return btf->start_id + btf->nr_types;
@@ -902,6 +1061,7 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf)
9021061

9031062
err = btf_parse_str_sec(btf);
9041063
err = err ?: btf_parse_type_sec(btf);
1064+
err = err ?: btf_sanity_check(btf);
9051065
if (err)
9061066
goto done;
9071067

tools/testing/selftests/bpf/prog_tests/btf.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7296,7 +7296,7 @@ static struct btf_dedup_test dedup_tests[] = {
72967296
BTF_FUNC_PROTO_ENC(0, 2), /* [3] */
72977297
BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(2), 1),
72987298
BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(3), 1),
7299-
BTF_FUNC_ENC(NAME_NTH(4), 2), /* [4] */
7299+
BTF_FUNC_ENC(NAME_NTH(4), 3), /* [4] */
73007300
/* tag -> t */
73017301
BTF_DECL_TAG_ENC(NAME_NTH(5), 2, -1), /* [5] */
73027302
BTF_DECL_TAG_ENC(NAME_NTH(5), 2, -1), /* [6] */
@@ -7317,7 +7317,7 @@ static struct btf_dedup_test dedup_tests[] = {
73177317
BTF_FUNC_PROTO_ENC(0, 2), /* [3] */
73187318
BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(2), 1),
73197319
BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(3), 1),
7320-
BTF_FUNC_ENC(NAME_NTH(4), 2), /* [4] */
7320+
BTF_FUNC_ENC(NAME_NTH(4), 3), /* [4] */
73217321
BTF_DECL_TAG_ENC(NAME_NTH(5), 2, -1), /* [5] */
73227322
BTF_DECL_TAG_ENC(NAME_NTH(5), 4, -1), /* [6] */
73237323
BTF_DECL_TAG_ENC(NAME_NTH(5), 4, 1), /* [7] */

0 commit comments

Comments
 (0)