summaryrefslogtreecommitdiffstats
path: root/tools/lib
diff options
context:
space:
mode:
authorEric Leblond <eric@regit.org>2016-11-14 23:05:47 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2016-11-25 09:27:33 -0500
commit4708bbda5cb2f6cdc331744597527143f46394d5 (patch)
tree809f1d14defbc82fc6a957597b8b0bcb6600faed /tools/lib
parentd6be16719e0b65f586ae4a301f02407422e6b5dd (diff)
tools lib bpf: Fix maps resolution
It is not correct to assimilate the elf data of the maps section to an array of map definition. In fact the sizes differ. The offset provided in the symbol section has to be used instead. This patch fixes a bug causing a elf with two maps not to load correctly. Wang Nan added: This patch requires a name for each BPF map, so array of BPF maps is not allowed. This restriction is reasonable, because kernel verifier forbid indexing BPF map from such array unless the index is a fixed value, but if the index is fixed why not merging it into name? For example: Program like this: ... unsigned long cpu = get_smp_processor_id(); int *pval = map_lookup_elem(&map_array[cpu], &key); ... Generates bytecode like this: 0: (b7) r1 = 0 1: (63) *(u32 *)(r10 -4) = r1 2: (b7) r1 = 680997 3: (63) *(u32 *)(r10 -8) = r1 4: (85) call 8 5: (67) r0 <<= 4 6: (18) r1 = 0x112dd000 8: (0f) r0 += r1 9: (bf) r2 = r10 10: (07) r2 += -4 11: (bf) r1 = r0 12: (85) call 1 Where instruction 8 is the computation, 8 and 11 render r1 to an invalid value for function map_lookup_elem, causes verifier report error. Signed-off-by: Eric Leblond <eric@regit.org> Cc: Alexei Starovoitov <ast@fb.com> Cc: He Kuang <hekuang@huawei.com> Cc: Wang Nan <wangnan0@huawei.com> [ Merge bpf_object__init_maps_name into bpf_object__init_maps. Fix segfault for buggy BPF script Validate obj->maps ] Cc: Zefan Li <lizefan@huawei.com> Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/20161115040617.69788-5-wangnan0@huawei.com Signed-off-by: Wang Nan <wangnan0@huawei.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/lib')
-rw-r--r--tools/lib/bpf/libbpf.c142
1 files changed, 98 insertions, 44 deletions
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index b699aea9a025..96a2b2ff1212 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -185,6 +185,7 @@ struct bpf_program {
185struct bpf_map { 185struct bpf_map {
186 int fd; 186 int fd;
187 char *name; 187 char *name;
188 size_t offset;
188 struct bpf_map_def def; 189 struct bpf_map_def def;
189 void *priv; 190 void *priv;
190 bpf_map_clear_priv_t clear_priv; 191 bpf_map_clear_priv_t clear_priv;
@@ -513,57 +514,106 @@ bpf_object__init_kversion(struct bpf_object *obj,
513} 514}
514 515
515static int 516static int
516bpf_object__init_maps(struct bpf_object *obj, void *data, 517bpf_object__validate_maps(struct bpf_object *obj)
517 size_t size)
518{ 518{
519 size_t nr_maps;
520 int i; 519 int i;
521 520
522 nr_maps = size / sizeof(struct bpf_map_def); 521 /*
523 if (!data || !nr_maps) { 522 * If there's only 1 map, the only error case should have been
524 pr_debug("%s doesn't need map definition\n", 523 * catched in bpf_object__init_maps().
525 obj->path); 524 */
525 if (!obj->maps || !obj->nr_maps || (obj->nr_maps == 1))
526 return 0; 526 return 0;
527 }
528 527
529 pr_debug("maps in %s: %zd bytes\n", obj->path, size); 528 for (i = 1; i < obj->nr_maps; i++) {
529 const struct bpf_map *a = &obj->maps[i - 1];
530 const struct bpf_map *b = &obj->maps[i];
530 531
531 obj->maps = calloc(nr_maps, sizeof(obj->maps[0])); 532 if (b->offset - a->offset < sizeof(struct bpf_map_def)) {
532 if (!obj->maps) { 533 pr_warning("corrupted map section in %s: map \"%s\" too small\n",
533 pr_warning("alloc maps for object failed\n"); 534 obj->path, a->name);
534 return -ENOMEM; 535 return -EINVAL;
536 }
535 } 537 }
536 obj->nr_maps = nr_maps; 538 return 0;
537 539}
538 for (i = 0; i < nr_maps; i++) {
539 struct bpf_map_def *def = &obj->maps[i].def;
540 540
541 /* 541static int compare_bpf_map(const void *_a, const void *_b)
542 * fill all fd with -1 so won't close incorrect 542{
543 * fd (fd=0 is stdin) when failure (zclose won't close 543 const struct bpf_map *a = _a;
544 * negative fd)). 544 const struct bpf_map *b = _b;
545 */
546 obj->maps[i].fd = -1;
547 545
548 /* Save map definition into obj->maps */ 546 return a->offset - b->offset;
549 *def = ((struct bpf_map_def *)data)[i];
550 }
551 return 0;
552} 547}
553 548
554static int 549static int
555bpf_object__init_maps_name(struct bpf_object *obj) 550bpf_object__init_maps(struct bpf_object *obj)
556{ 551{
557 int i; 552 int i, map_idx, nr_maps = 0;
553 Elf_Scn *scn;
554 Elf_Data *data;
558 Elf_Data *symbols = obj->efile.symbols; 555 Elf_Data *symbols = obj->efile.symbols;
559 556
560 if (!symbols || obj->efile.maps_shndx < 0) 557 if (obj->efile.maps_shndx < 0)
558 return -EINVAL;
559 if (!symbols)
560 return -EINVAL;
561
562 scn = elf_getscn(obj->efile.elf, obj->efile.maps_shndx);
563 if (scn)
564 data = elf_getdata(scn, NULL);
565 if (!scn || !data) {
566 pr_warning("failed to get Elf_Data from map section %d\n",
567 obj->efile.maps_shndx);
561 return -EINVAL; 568 return -EINVAL;
569 }
562 570
571 /*
572 * Count number of maps. Each map has a name.
573 * Array of maps is not supported: only the first element is
574 * considered.
575 *
576 * TODO: Detect array of map and report error.
577 */
563 for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) { 578 for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
564 GElf_Sym sym; 579 GElf_Sym sym;
565 size_t map_idx; 580
581 if (!gelf_getsym(symbols, i, &sym))
582 continue;
583 if (sym.st_shndx != obj->efile.maps_shndx)
584 continue;
585 nr_maps++;
586 }
587
588 /* Alloc obj->maps and fill nr_maps. */
589 pr_debug("maps in %s: %d maps in %zd bytes\n", obj->path,
590 nr_maps, data->d_size);
591
592 if (!nr_maps)
593 return 0;
594
595 obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
596 if (!obj->maps) {
597 pr_warning("alloc maps for object failed\n");
598 return -ENOMEM;
599 }
600 obj->nr_maps = nr_maps;
601
602 /*
603 * fill all fd with -1 so won't close incorrect
604 * fd (fd=0 is stdin) when failure (zclose won't close
605 * negative fd)).
606 */
607 for (i = 0; i < nr_maps; i++)
608 obj->maps[i].fd = -1;
609
610 /*
611 * Fill obj->maps using data in "maps" section.
612 */
613 for (i = 0, map_idx = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
614 GElf_Sym sym;
566 const char *map_name; 615 const char *map_name;
616 struct bpf_map_def *def;
567 617
568 if (!gelf_getsym(symbols, i, &sym)) 618 if (!gelf_getsym(symbols, i, &sym))
569 continue; 619 continue;
@@ -573,21 +623,27 @@ bpf_object__init_maps_name(struct bpf_object *obj)
573 map_name = elf_strptr(obj->efile.elf, 623 map_name = elf_strptr(obj->efile.elf,
574 obj->efile.strtabidx, 624 obj->efile.strtabidx,
575 sym.st_name); 625 sym.st_name);
576 map_idx = sym.st_value / sizeof(struct bpf_map_def); 626 obj->maps[map_idx].offset = sym.st_value;
577 if (map_idx >= obj->nr_maps) { 627 if (sym.st_value + sizeof(struct bpf_map_def) > data->d_size) {
578 pr_warning("index of map \"%s\" is buggy: %zu > %zu\n", 628 pr_warning("corrupted maps section in %s: last map \"%s\" too small\n",
579 map_name, map_idx, obj->nr_maps); 629 obj->path, map_name);
580 continue; 630 return -EINVAL;
581 } 631 }
632
582 obj->maps[map_idx].name = strdup(map_name); 633 obj->maps[map_idx].name = strdup(map_name);
583 if (!obj->maps[map_idx].name) { 634 if (!obj->maps[map_idx].name) {
584 pr_warning("failed to alloc map name\n"); 635 pr_warning("failed to alloc map name\n");
585 return -ENOMEM; 636 return -ENOMEM;
586 } 637 }
587 pr_debug("map %zu is \"%s\"\n", map_idx, 638 pr_debug("map %d is \"%s\"\n", map_idx,
588 obj->maps[map_idx].name); 639 obj->maps[map_idx].name);
640 def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
641 obj->maps[map_idx].def = *def;
642 map_idx++;
589 } 643 }
590 return 0; 644
645 qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]), compare_bpf_map);
646 return bpf_object__validate_maps(obj);
591} 647}
592 648
593static int bpf_object__elf_collect(struct bpf_object *obj) 649static int bpf_object__elf_collect(struct bpf_object *obj)
@@ -645,11 +701,9 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
645 err = bpf_object__init_kversion(obj, 701 err = bpf_object__init_kversion(obj,
646 data->d_buf, 702 data->d_buf,
647 data->d_size); 703 data->d_size);
648 else if (strcmp(name, "maps") == 0) { 704 else if (strcmp(name, "maps") == 0)
649 err = bpf_object__init_maps(obj, data->d_buf,
650 data->d_size);
651 obj->efile.maps_shndx = idx; 705 obj->efile.maps_shndx = idx;
652 } else if (sh.sh_type == SHT_SYMTAB) { 706 else if (sh.sh_type == SHT_SYMTAB) {
653 if (obj->efile.symbols) { 707 if (obj->efile.symbols) {
654 pr_warning("bpf: multiple SYMTAB in %s\n", 708 pr_warning("bpf: multiple SYMTAB in %s\n",
655 obj->path); 709 obj->path);
@@ -698,7 +752,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
698 return LIBBPF_ERRNO__FORMAT; 752 return LIBBPF_ERRNO__FORMAT;
699 } 753 }
700 if (obj->efile.maps_shndx >= 0) 754 if (obj->efile.maps_shndx >= 0)
701 err = bpf_object__init_maps_name(obj); 755 err = bpf_object__init_maps(obj);
702out: 756out:
703 return err; 757 return err;
704} 758}
@@ -807,7 +861,7 @@ bpf_object__create_maps(struct bpf_object *obj)
807 zclose(obj->maps[j].fd); 861 zclose(obj->maps[j].fd);
808 return err; 862 return err;
809 } 863 }
810 pr_debug("create map: fd=%d\n", *pfd); 864 pr_debug("create map %s: fd=%d\n", obj->maps[i].name, *pfd);
811 } 865 }
812 866
813 return 0; 867 return 0;