[PATCH 05/12] libbpf: Support exclusive map creation

Andrii Nakryiko andrii.nakryiko at gmail.com
Mon Jul 14 21:05:34 UTC 2025


On Mon, Jul 14, 2025 at 5:55 AM KP Singh <kpsingh at kernel.org> wrote:
>
> On Mon, Jul 14, 2025 at 2:29 PM KP Singh <kpsingh at kernel.org> wrote:
> >
> > On Fri, Jun 13, 2025 at 12:56 AM Andrii Nakryiko
> > <andrii.nakryiko at gmail.com> wrote:
> > >
> > > On Fri, Jun 6, 2025 at 4:29 PM KP Singh <kpsingh at kernel.org> wrote:
> > > >
> > > > Implement a convenient method i.e. bpf_map__make_exclusive which
> > > > calculates the hash for the program and registers it with the map for
> > > > creation as an exclusive map when the objects are loaded.
> > > >
> > > > The hash of the program must be computed after all the relocations are
> > > > done.
> > > >
> > > > 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         | 68 +++++++++++++++++++++++++++++++++-
> > > >  tools/lib/bpf/libbpf.h         | 13 +++++++
> > > >  tools/lib/bpf/libbpf.map       |  5 +++
> > > >  tools/lib/bpf/libbpf_version.h |  2 +-
> > > >  6 files changed, 92 insertions(+), 4 deletions(-)
> > > >
> > > > diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
> > > > index a9c3e33d0f8a..11fa2d64ccca 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 777627d33d25..a82b79c0c349 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;
> > > > +       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 475038d04cb4..17de756973f4 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,8 @@ struct bpf_map {
> > > >         bool autocreate;
> > > >         bool autoattach;
> > > >         __u64 map_extra;
> > > > +       const void *excl_prog_sha;
> > > > +       __u32 excl_prog_sha_size;
> > > >  };
> > > >
> > > >  enum extern_type {
> > > > @@ -4485,6 +4488,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)
> > > > +{
> > > > +       struct bpf_insn *purged;
> > > > +       bool was_ld_map;
> > > > +       int i, err;
> > > > +
> > > > +       purged = calloc(1, BPF_INSN_SZ * prog->insns_cnt);
> > > > +       if (!purged)
> > > > +               return -ENOMEM;
> > > > +
> > > > +       /* If relocations have been done, the map_fd needs to be
> > > > +        * discarded for the digest calculation.
> > > > +        */
> > >
> > > all this looks sketchy, let's think about some more robust approach
> > > here rather than randomly clearing some fields of some instructions...
> > >
> > > > +       for (i = 0, was_ld_map = false; i < prog->insns_cnt; i++) {
> > > > +               purged[i] = prog->insns[i];
> > > > +               if (!was_ld_map &&
> > > > +                   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)) {
> > > > +                       was_ld_map = true;
> > > > +                       purged[i].imm = 0;
> > > > +               } else if (was_ld_map && purged[i].code == 0 &&
> > > > +                          purged[i].dst_reg == 0 && purged[i].src_reg == 0 &&
> > > > +                          purged[i].off == 0) {
> > > > +                       was_ld_map = false;
> > > > +                       purged[i].imm = 0;
> > > > +               } else {
> > > > +                       was_ld_map = false;
> > > > +               }
> > > > +       }
> > >
> > > this was_ld_map business is... unnecessary? Just access purged[i + 1]
> > > (checking i + 1 < prog->insns_cnt, of course), and i += 1. This
> > > stateful approach is an unnecessary complication, IMO
> >
> > Does this look better to you, the next instruction has to be the
> > second half of the double word right?
> >
> > for (int 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) {
> >             return -EINVAL;
> >         }
>
> I mean ofcourse
>
> err = -EINVAL;
> goto out;
>
> to free the buffer.


Yes, but I'd probably modify it a bit for conciseness:

struct bpf_insn *purged, *insn;
int i;

purged = calloc(..);
memcpy(purged, prog->insns, ...);

for (i = 0; i < prog->insns_cnt; i++) {
    insn = &purged[i];
    if (insn[0].code == (BPF_LD | BPF_IMM | BPF_DW) &&
        (insn[0].src_reg == BPF_PSEUDO_MAP_FD || ...) {
        insn[0].imm = 0;
        if (i >= prog_insns_cnt) {
            err = -EINVAL;
            goto err;
        }
        insn[1].imm = 0;
        i++;
}


(I'm not sure libbpf needs to check code,dst_reg,src_reg,off for
ldimm64, verifier will do it anyways, so I'd protect against
out-of-bounds access only)

I'd even consider just doing:

if (i + 1 < prog->insns_cnt && insn[0].code == (BPF_LD | BPF_IMM |
BPF_DW) ...) {
    insn[0].imm = 0;
    insn[1].imm = 0;
    i++;
}


i.e., don't even error out, verifier will do that anyway later because
program is malformed, and hash won't even matter at that point

[...]



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