[PATCH v3 04/12] libbpf: Support exclusive map creation

KP Singh kpsingh at kernel.org
Fri Sep 12 18:22:11 UTC 2025


On Thu, Aug 14, 2025 at 8:46 PM Andrii Nakryiko
<andrii.nakryiko at gmail.com> wrote:
>
> On Wed, Aug 13, 2025 at 1:55 PM KP Singh <kpsingh at kernel.org> wrote:
> >
> > Implement setters and getters that allow map to be registers as
>
> typo: registered
>
> > exclusive to the specified program. The registration should be done
> > before the exclusive program is loaded.
> >
> > Signed-off-by: KP Singh <kpsingh at kernel.org>
> > ---
> >  tools/lib/bpf/bpf.c      |  4 ++-
> >  tools/lib/bpf/bpf.h      |  4 ++-
> >  tools/lib/bpf/libbpf.c   | 66 ++++++++++++++++++++++++++++++++++++++++
> >  tools/lib/bpf/libbpf.h   | 18 +++++++++++
> >  tools/lib/bpf/libbpf.map |  2 ++
> >  5 files changed, 92 insertions(+), 2 deletions(-)
> >
> > diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
> > index ab40dbf9f020..6a08a1559237 100644
> > --- a/tools/lib/bpf/bpf.c
> > +++ b/tools/lib/bpf/bpf.c
> > @@ -172,7 +172,7 @@ int bpf_map_create(enum bpf_map_type map_type,
> >                    __u32 max_entries,
> >                    const struct bpf_map_create_opts *opts)
> >  {
> > -       const size_t attr_sz = offsetofend(union bpf_attr, map_token_fd);
> > +       const size_t attr_sz = offsetofend(union bpf_attr, excl_prog_hash);
> >         union bpf_attr attr;
> >         int fd;
> >
> > @@ -203,6 +203,8 @@ int bpf_map_create(enum bpf_map_type map_type,
> >         attr.map_ifindex = OPTS_GET(opts, map_ifindex, 0);
> >
> >         attr.map_token_fd = OPTS_GET(opts, token_fd, 0);
> > +       attr.excl_prog_hash = ptr_to_u64(OPTS_GET(opts, excl_prog_hash, NULL));
> > +       attr.excl_prog_hash_size = OPTS_GET(opts, excl_prog_hash_size, 0);
> >
> >         fd = sys_bpf_fd(BPF_MAP_CREATE, &attr, attr_sz);
> >         return libbpf_err_errno(fd);
> > diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
> > index 7252150e7ad3..675a09bb7d2f 100644
> > --- a/tools/lib/bpf/bpf.h
> > +++ b/tools/lib/bpf/bpf.h
> > @@ -54,9 +54,11 @@ struct bpf_map_create_opts {
> >         __s32 value_type_btf_obj_fd;
> >
> >         __u32 token_fd;
> > +       __u32 excl_prog_hash_size;
>
> leaving a gap here, can you please reorder and have hash first,
> followed by size?

done

>
> > +       const void *excl_prog_hash;
> >         size_t :0;
> >  };
> > -#define bpf_map_create_opts__last_field token_fd
> > +#define bpf_map_create_opts__last_field excl_prog_hash
> >
> >  LIBBPF_API int bpf_map_create(enum bpf_map_type map_type,
> >                               const char *map_name,
> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index 0bb3d71dcd9f..ed3294f69271 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
> > @@ -499,6 +499,7 @@ struct bpf_program {
> >         __u32 line_info_rec_size;
> >         __u32 line_info_cnt;
> >         __u32 prog_flags;
> > +       __u8  hash[SHA256_DIGEST_LENGTH];
> >  };
> >
> >  struct bpf_struct_ops {
> > @@ -578,6 +579,7 @@ struct bpf_map {
> >         bool autocreate;
> >         bool autoattach;
> >         __u64 map_extra;
> > +       struct bpf_program *excl_prog;
> >  };
> >
> >  enum extern_type {
> > @@ -4488,6 +4490,43 @@ bpf_object__section_to_libbpf_map_type(const struct bpf_object *obj, int shndx)
> >         }
> >  }
> >
> > +static int bpf_program__compute_hash(struct bpf_program *prog)
>
> nit: this is not an API, so please don't use double underscores.
> Something like bpf_prog_compute_hash() should do.

ack.

>
> > +{
> > +       struct bpf_insn *purged;
> > +       int i, err;
> > +
> > +       purged = calloc(1, BPF_INSN_SZ * prog->insns_cnt);
>
> we had some patch fixing similar argument misuse issue, so I'd rather
> have calloc(prog->insns_cnt, BPF_INSN_SZ), if you don't mind
>

I don't mind :)

> > +       if (!purged)
> > +               return -ENOMEM;
> > +
> > +       /* If relocations have been done, the map_fd needs to be
> > +        * discarded for the digest calculation.
> > +        */
> > +       for (i = 0; i < prog->insns_cnt; i++) {
> > +               purged[i] = prog->insns[i];
> > +               if (purged[i].code == (BPF_LD | BPF_IMM | BPF_DW) &&
> > +                   (purged[i].src_reg == BPF_PSEUDO_MAP_FD ||
> > +                    purged[i].src_reg == BPF_PSEUDO_MAP_VALUE)) {
> > +                       purged[i].imm = 0;
> > +                       i++;
> > +                       if (i >= prog->insns_cnt ||
> > +                           prog->insns[i].code != 0 ||
> > +                           prog->insns[i].dst_reg != 0 ||
> > +                           prog->insns[i].src_reg != 0 ||
> > +                           prog->insns[i].off != 0) {
> > +                               err = -EINVAL;
> > +                               goto out;
> > +                       }
> > +                       purged[i] = prog->insns[i];
> > +                       purged[i].imm = 0;
> > +               }
> > +       }
> > +       err = libbpf_sha256(purged, prog->insns_cnt * sizeof(struct bpf_insn), prog->hash, SHA256_DIGEST_LENGTH);
>
> too long, wrap before prog->hash?

done

>
> > +out:
> > +       free(purged);
> > +       return err;
> > +}
> > +
> >  static int bpf_program__record_reloc(struct bpf_program *prog,
> >                                      struct reloc_desc *reloc_desc,
> >                                      __u32 insn_idx, const char *sym_name,
> > @@ -5227,6 +5266,18 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b
> >         create_attr.token_fd = obj->token_fd;
> >         if (obj->token_fd)
> >                 create_attr.map_flags |= BPF_F_TOKEN_FD;
> > +       if (map->excl_prog) {
> > +               if (map->excl_prog->obj->state == OBJ_LOADED) {
> > +                       pr_warn("exclusive program already loaded\n");
> > +                       return libbpf_err(-EINVAL);
> > +               }
>
> unnecessary check, maps are always created before programs, so if
> map->excl_prog belongs to the same bpf_object (and it should), then we
> implicitly have a guarantee it's not yet created. So please drop.

Removed.

>
> > +               err = bpf_program__compute_hash(map->excl_prog);
> > +               if (err)
> > +                       return err;
> > +
> > +               create_attr.excl_prog_hash = map->excl_prog->hash;
> > +               create_attr.excl_prog_hash_size = SHA256_DIGEST_LENGTH;
> > +       }
> >
> >         if (bpf_map__is_struct_ops(map)) {
> >                 create_attr.btf_vmlinux_value_type_id = map->btf_vmlinux_value_type_id;
> > @@ -10517,6 +10568,21 @@ int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd)
> >         return 0;
> >  }
> >
> > +int bpf_map__set_exclusive_program(struct bpf_map *map, struct bpf_program *prog)
> > +{
> > +       if (map_is_created(map)) {
> > +               pr_warn("exclusive programs must be set before map creation\n");
> > +               return libbpf_err(-EINVAL);
> > +       }
>
> should we worry about someone providing a bpf_program that doesn't
> belong to the same bpf_object that map belongs to? it's easy to check,
> just compare map->obj and prog->obj

Seems logical, added the check.

> > +       map->excl_prog = prog;
> > +       return 0;
> > +}
> > +
> > +struct bpf_program *bpf_map__get_exclusive_program(struct bpf_map *map)
>
> libbpf getters don't have "get_" prefix, so just bpf_map__exclusive_program()

updated.

>
> > +{
> > +       return map->excl_prog;
> > +}
> > +
> >  static struct bpf_map *
> >  __bpf_map__iter(const struct bpf_map *m, const struct bpf_object *obj, int i)
> >  {
> > diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> > index 455a957cb702..ddaf58c8a298 100644
> > --- a/tools/lib/bpf/libbpf.h
> > +++ b/tools/lib/bpf/libbpf.h
> > @@ -1266,7 +1266,25 @@ LIBBPF_API int bpf_map__lookup_and_delete_elem(const struct bpf_map *map,
> >   */
> >  LIBBPF_API int bpf_map__get_next_key(const struct bpf_map *map,
> >                                      const void *cur_key, void *next_key, size_t key_sz);
> > +/**
> > + * @brief **bpf_map__set_exclusive_program()** sets map to be exclusive to the
> > + * to the specified program. The program must not be loaded yet.
>
> typo: "to the" duplicated

fixed.

>
> Also, I think the more important restriction is that the map should
> not have been created yet (so this has to be called between opening
> and prepare/load steps, just like setting read-only global variables).
> This by implication will mean that the program is not loaded either,
> as we'll restrict bpf_program to be from the same bpf_object (which
> you can mention as well for clarity).
>

How about?

/**
* @brief **bpf_map__set_exclusive_program()** sets a map to be exclusive to the
* specified program. This must be called *before* the map is created.
*
* @param map BPF map to make exclusive.
* @param prog BPF program to be the exclusive user of the map. Must belong
* to the same bpf_object as the map.
* @return 0 on success; a negative error code otherwise.
*
* This function must be called after the BPF object is opened but before
* it is loaded. Once the object is loaded, only the specified program
* will be able to access the map's contents.
*/

> > + * @param map BPF map to make exclusive.
> > + * @param prog BPF program to be the exclusive user of the map.
> > + * @return 0 on success; a negative error code otherwise.
> > + *
> > + * Once a map is made exclusive, only the specified program can access its
> > + * contents.
> > + */
> > +LIBBPF_API int bpf_map__set_exclusive_program(struct bpf_map *map, struct bpf_program *prog);
> >
> > +/**
> > + * @brief **bpf_map__get_exclusive_program()** returns the exclusive program
> > + * that is registered with the map (if any).
> > + * @param map BPF map to which the exclusive program is registered.
> > + * @return the registered exclusive program.
> > + */
> > +LIBBPF_API struct bpf_program *bpf_map__get_exclusive_program(struct bpf_map *map);
> >  struct bpf_xdp_set_link_opts {
> >         size_t sz;
> >         int old_fd;
> > diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> > index d7bd463e7017..a5c5d0f2db5c 100644
> > --- a/tools/lib/bpf/libbpf.map
> > +++ b/tools/lib/bpf/libbpf.map
> > @@ -436,6 +436,8 @@ LIBBPF_1.6.0 {
> >                 bpf_linker__add_buf;
> >                 bpf_linker__add_fd;
> >                 bpf_linker__new_fd;
> > +               bpf_map__set_exclusive_program;
> > +               bpf_map__get_exclusive_program;
>
> we are in LIBBPF_1.7.0 now, so please move

done

>
> pw-bot: cr
>
>
> >                 bpf_object__prepare;
> >                 bpf_prog_stream_read;
> >                 bpf_program__attach_cgroup_opts;
> > --
> > 2.43.0
> >



More information about the Linux-security-module-archive mailing list