First of all, thank you Craig for working on this.  As Alexei says, we
need to improve tools/lib/bpf/libbpf and move towards converting users
of bpf_load.c to this lib instead.

Comments inlined below.

On Mon,  2 Oct 2017 12:41:28 -0400 Craig Gallek <kraigatg...@gmail.com> wrote:

> From: Craig Gallek <kr...@google.com>
> 
> This library previously assumed a fixed-size map options structure.
> Any new options were ignored.  In order to allow the options structure
> to grow and to support parsing older programs, this patch updates
> the maps section parsing to handle varying sizes.
> 
> Object files with maps sections smaller than expected will have the new
> fields initialized to zero.  Object files which have larger than expected
> maps sections will be rejected unless all of the unrecognized data is zero.
> 
> This change still assumes that each map definition in the maps section
> is the same size.
> 
> Signed-off-by: Craig Gallek <kr...@google.com>
> ---
>  tools/lib/bpf/libbpf.c | 54 
> ++++++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 46 insertions(+), 8 deletions(-)
> 
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 4f402dcdf372..28b300868ad7 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -580,7 +580,7 @@ bpf_object__init_kversion(struct bpf_object *obj,
>  }
>  
>  static int
> -bpf_object__validate_maps(struct bpf_object *obj)
> +bpf_object__validate_maps(struct bpf_object *obj, int map_def_sz)
>  {
>       int i;
>  
> @@ -595,9 +595,11 @@ bpf_object__validate_maps(struct bpf_object *obj)
>               const struct bpf_map *a = &obj->maps[i - 1];
>               const struct bpf_map *b = &obj->maps[i];
>  
> -             if (b->offset - a->offset < sizeof(struct bpf_map_def)) {
> -                     pr_warning("corrupted map section in %s: map \"%s\" too 
> small\n",
> -                                obj->path, a->name);
> +             if (b->offset - a->offset < map_def_sz) {
> +                     pr_warning("corrupted map section in %s: map \"%s\" too 
> small "
> +                                "(%zd vs %d)\n",
> +                                obj->path, a->name, b->offset - a->offset,
> +                                map_def_sz);
>                       return -EINVAL;
>               }
>       }
> @@ -615,7 +617,7 @@ static int compare_bpf_map(const void *_a, const void *_b)
>  static int
>  bpf_object__init_maps(struct bpf_object *obj)
>  {
> -     int i, map_idx, nr_maps = 0;
> +     int i, map_idx, map_def_sz, nr_maps = 0;
>       Elf_Scn *scn;
>       Elf_Data *data;
>       Elf_Data *symbols = obj->efile.symbols;
> @@ -658,6 +660,15 @@ bpf_object__init_maps(struct bpf_object *obj)
>       if (!nr_maps)
>               return 0;
>  
> +     /* Assume equally sized map definitions */
> +     map_def_sz = data->d_size / nr_maps;
> +     if (!data->d_size || (data->d_size % nr_maps) != 0) {
> +             pr_warning("unable to determine map definition size "
> +                        "section %s, %d maps in %zd bytes\n",
> +                        obj->path, nr_maps, data->d_size);
> +             return -EINVAL;
> +     }
> +
>       obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
>       if (!obj->maps) {
>               pr_warning("alloc maps for object failed\n");
> @@ -690,7 +701,7 @@ bpf_object__init_maps(struct bpf_object *obj)
>                                     obj->efile.strtabidx,
>                                     sym.st_name);
>               obj->maps[map_idx].offset = sym.st_value;
> -             if (sym.st_value + sizeof(struct bpf_map_def) > data->d_size) {
> +             if (sym.st_value + map_def_sz > data->d_size) {
>                       pr_warning("corrupted maps section in %s: last map 
> \"%s\" too small\n",
>                                  obj->path, map_name);
>                       return -EINVAL;
> @@ -704,12 +715,39 @@ bpf_object__init_maps(struct bpf_object *obj)
>               pr_debug("map %d is \"%s\"\n", map_idx,
>                        obj->maps[map_idx].name);
>               def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
> -             obj->maps[map_idx].def = *def;
> +             /*
> +              * If the definition of the map in the object file fits in
> +              * bpf_map_def, copy it.  Any extra fields in our version
> +              * of bpf_map_def will default to zero as a result of the
> +              * calloc above.
> +              */
> +             if (map_def_sz <= sizeof(struct bpf_map_def)) {
> +                     memcpy(&obj->maps[map_idx].def, def, map_def_sz);
> +             } else {
> +                     /*
> +                      * Here the map structure being read is bigger than what
> +                      * we expect, truncate if the excess bits are all zero.
> +                      * If they are not zero, reject this map as
> +                      * incompatible.
> +                      */
> +                     char *b;
> +                     for (b = ((char *)def) + sizeof(struct bpf_map_def);
> +                          b < ((char *)def) + map_def_sz; b++) {
> +                             if (*b != 0) {
> +                                     pr_warning("maps section in %s: \"%s\" "
> +                                                "has unrecognized, non-zero "
> +                                                "options\n",
> +                                                obj->path, map_name);
> +                                     return -EINVAL;
> +                             }
> +                     }
> +                     obj->maps[map_idx].def = *def;

I'm not too happy/comfortable with this way of copying the memory of
"def" (the type-cased struct bpf_map_def).  I guess it works, and is
part of the C-standard(?).


> +             }
>               map_idx++;
>       }
>  
>       qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]), compare_bpf_map);
> -     return bpf_object__validate_maps(obj);
> +     return bpf_object__validate_maps(obj, map_def_sz);
>  }
>  
>  static int bpf_object__elf_collect(struct bpf_object *obj)

Besides above comment, I think the patch is correct, based on what I
did in commit 156450d9d964 ("samples/bpf: make bpf_load.c code
compatible with ELF maps section changes").

  https://git.kernel.org/torvalds/c/156450d9d964

-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer

Reply via email to