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]>
---
 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);
                }
        }
 
-- 
2.54.0


Reply via email to