On Thu, Jun 4, 2026 at 7:40 AM Mykyta Yatsenko
<[email protected]> wrote:
>
> On 6/4/26 4:10 AM, [email protected] wrote:
> > From: Yuan Chen <[email protected]>
> >
> > probe_fd() converts BPF syscall failures to return value 0,
> > which feat_supported() interprets as 'feature absent' (ret==0),
> > even when the probe failed due to a BPF token's restricted
> > program/map type mask.
> >
> > In a user namespace with an implicit BPF token, the sequence is:
> > 1. bpf_object_open() -> no feat_cache yet -> global cache used
> > 2. bpf_object_prepare_token() -> creates token, sets feat_cache->token_fd
> > 3. bpf_object__create_maps() -> feat_supported() runs probe
> > -> bpf_prog_load(SOCKET_FILTER) with token -> -EPERM
> > -> probe_fd(-EPERM) -> returns 0
> > -> feat_supported sees ret==0 -> FEAT_MISSING
> > -> internal maps (global data) skipped -> verifier EINVAL
> >
> > Fix by making probe_fd() return the negative error on failure
> > instead of 0, so feat_supported() enters the ret<0 path.
> >
> > Additionally add a token_fd rescue in feat_supported() for the
> > ret<0 case: when a BPF token is present, a failed feature probe
> > is treated as 'feature supported' because token creation itself
> > proves the kernel BPF subsystem works. Real BPF issues will
> > be caught during actual program/map loading.
> >
> > Signed-off-by: Yuan Chen <[email protected]>
> > ---
>
> It looks wrong to say that any feature is supported just because there is
> token_fd present. In patch 1 we only checked that BPF is available, so
> it was ok skipping probing. This one looks a bit more risky.
Yep, agreed, this is not the right approach. Please double check what
kind of issues you still have with only patch 1 applied, and let's
discuss possible solutions (if there are still problems).
> Is the reason this code fails because of BPF_PROG_TYPE_SOCKET_FILTER not
> enabled
> in the token? I suggest fetch the first supported program type from the token
> and
> pass it down to bpf_prog_load().
>
> > tools/lib/bpf/features.c | 23 +++++++++++++++++++++--
> > 1 file changed, 21 insertions(+), 2 deletions(-)
> >
> > diff --git a/tools/lib/bpf/features.c b/tools/lib/bpf/features.c
> > index b7e388f99d0b..f934452e52f0 100644
> > --- a/tools/lib/bpf/features.c
> > +++ b/tools/lib/bpf/features.c
> > @@ -16,7 +16,14 @@ int probe_fd(int fd)
> > {
> > if (fd >= 0)
> > close(fd);
> > - return fd >= 0;
> > + /* Return 1 on success, negative error on failure, so
> > + * feat_supported() can distinguish probe errors from
> > + * genuine feature absence. When a BPF token is present,
> > + * a negative return triggers the rescue path that marks
> > + * the feature as SUPPORTED (token creation itself proves
> > + * the kernel BPF subsystem works).
> > + */
> > + return fd >= 0 ? 1 : fd;
> > }
> >
> > static int probe_kern_prog_name(int token_fd)
> > @@ -725,9 +732,21 @@ bool feat_supported(struct kern_feature_cache *cache,
> > enum kern_feature_id feat_
> > } else if (ret == 0) {
> > WRITE_ONCE(cache->res[feat_id], FEAT_MISSING);
> > } else {
> > + /*
> > + * A BPF token may restrict which program/map types
> > + * are permitted, causing the probe to fail even
> > + * though the kernel supports the feature. When a
> > + * token is present the probe is best-effort: BPF
> > + * token creation itself proves the kernel has a
> > + * working BPF subsystem. Real BPF issues will be
> > + * caught during actual program/map loading.
> > + */
> > + if (cache->token_fd)
> > + WRITE_ONCE(cache->res[feat_id],
> > FEAT_SUPPORTED);
> > + else
> > + WRITE_ONCE(cache->res[feat_id], FEAT_MISSING);
> > pr_warn("Detection of kernel %s support failed: %s\n",
> > feat->desc, errstr(ret));
> > - WRITE_ONCE(cache->res[feat_id], FEAT_MISSING);
> > }
> > }
> >
>