> diff --git a/tools/lib/bpf/features.c b/tools/lib/bpf/features.c
> index b7e388f99d0b..f008ee50e246 100644
> --- a/tools/lib/bpf/features.c
> +++ b/tools/lib/bpf/features.c
> @@ -19,6 +19,57 @@ int probe_fd(int fd)
>       return fd >= 0;
>  }
>
> +/* Fetch the first program type from the token that can be used for
> + * feature probes.  Only a few common types are checked that are known
> + * to accept a trivial "return 0" program.  Returns the type on success,
> + * -EINVAL if the token has no probeable type.
> + */
> +static int token_probeable_prog_type(int token_fd)
> +{
> +     /* Types that accept a simple "return 0" probe program */
> +     /* Types that accept a trivial "return 0" program without
> +      * specific expected_attach_type.  Types requiring expected_attach_type
> +      * (CGROUP_SOCK, CGROUP_SOCK_ADDR, LWT_*, NETFILTER) are excluded.
> +      */

There seem to be two comment blocks describing the probeable[] array
back-to-back here.  The first single-line comment looks like a leftover
from an earlier revision; should it be removed?

> +     static const int probeable[] = {
> +             BPF_PROG_TYPE_SOCKET_FILTER,
> +             BPF_PROG_TYPE_KPROBE,
> +             BPF_PROG_TYPE_SCHED_CLS,
> +             BPF_PROG_TYPE_SCHED_ACT,
> +             BPF_PROG_TYPE_TRACEPOINT,
> +             BPF_PROG_TYPE_XDP,
> +             BPF_PROG_TYPE_PERF_EVENT,
> +             BPF_PROG_TYPE_CGROUP_SKB,
> +             BPF_PROG_TYPE_SOCK_OPS,
> +             BPF_PROG_TYPE_SK_SKB,
> +             BPF_PROG_TYPE_CGROUP_DEVICE,
> +             BPF_PROG_TYPE_SK_MSG,
> +             BPF_PROG_TYPE_RAW_TRACEPOINT,
> +             BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
> +             BPF_PROG_TYPE_CGROUP_SOCKOPT,
> +             BPF_PROG_TYPE_CGROUP_SYSCTL,
> +             BPF_PROG_TYPE_FLOW_DISSECTOR,
> +             BPF_PROG_TYPE_SK_LOOKUP,
> +     };

The list places BPF_PROG_TYPE_SOCKET_FILTER first, and the no-token case
also returns SOCKET_FILTER.  This matters for the two probes below that
previously used TRACEPOINT.

> +     struct bpf_token_info info = {};
> +     __u32 info_len = sizeof(info);
> +     int i, err;
> +
> +     if (!token_fd)
> +             return BPF_PROG_TYPE_SOCKET_FILTER;
> +
> +     err = bpf_obj_get_info_by_fd(token_fd, &info, &info_len);
> +     if (err)
> +             return -errno;
> +
> +     for (i = 0; i < ARRAY_SIZE(probeable); i++) {
> +             if (info.allowed_progs & (1ULL << probeable[i]))
> +                     return probeable[i];
> +     }
> +
> +     return -EINVAL;
> +}
> +

[ ... ]

> @@ -257,8 +318,13 @@ static int probe_kern_probe_read_kernel(int token_fd)
>               BPF_EXIT_INSN(),
>       };
>       int fd, insn_cnt = ARRAY_SIZE(insns);
> +     int prog_type;
>
> -     fd = bpf_prog_load(BPF_PROG_TYPE_TRACEPOINT, NULL, "GPL", insns, 
> insn_cnt, &opts);
> +     prog_type = token_probeable_prog_type(token_fd);
> +     if (prog_type < 0)
> +             return 1;
> +
> +     fd = bpf_prog_load(prog_type, NULL, "GPL", insns, insn_cnt, &opts);

This probe tests for BPF_FUNC_probe_read_kernel and previously hardcoded
BPF_PROG_TYPE_TRACEPOINT.  In the no-token case token_probeable_prog_type()
now returns BPF_PROG_TYPE_SOCKET_FILTER instead.

For a TRACEPOINT program, probe_read_kernel is reachable through:

    tp_prog_func_proto() -> bpf_tracing_func_proto()

For a SOCKET_FILTER program the helper is only reachable through
bpf_base_func_proto(), where it is gated behind
bpf_token_capable(CAP_PERFMON) and
security_locked_down(LOCKDOWN_BPF_READ_KERNEL), and is not exposed via
sk_filter_func_proto().

Does this narrow the conditions under which the probe succeeds, so that
FEAT_PROBE_READ_KERNEL can be misreported in the no-token case that
previously used TRACEPOINT?

> +     return probe_fd(fd);
>  }
>

[ ... ]

> @@ -277,6 +343,7 @@ static int probe_prog_bind_map(int token_fd)
>               .prog_flags = token_fd ? BPF_F_TOKEN_FD : 0,
>       );
>       int ret, map, prog, insn_cnt = ARRAY_SIZE(insns);
> +     int prog_type;
>
>       map = bpf_map_create(BPF_MAP_TYPE_ARRAY, "libbpf_det_bind", 
> sizeof(int), 32, 1, &map_opts);
>       if (map < 0) {
> @@ -286,7 +353,12 @@ static int probe_prog_bind_map(int token_fd)
>               return ret;
>       }
>
> -     prog = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", insns, 
> insn_cnt, &prog_opts);
> +     prog = prog_type = token_probeable_prog_type(token_fd);

This assigns the result to both prog and prog_type, but prog is
unconditionally overwritten by the bpf_prog_load() call just below before
it is ever read.  The assignment to prog looks like dead code; should this
just be:

        prog_type = token_probeable_prog_type(token_fd);

> +     if (prog_type < 0) {
> +             close(map);
> +             return 1;
> +     }
> +     prog = bpf_prog_load(prog_type, NULL, "GPL", insns, insn_cnt, 
> &prog_opts);
>       if (prog < 0) {
>               close(map);
>               return 0;

[ ... ]

> @@ -435,8 +507,13 @@ static int probe_kern_bpf_cookie(int token_fd)
>               .prog_flags = token_fd ? BPF_F_TOKEN_FD : 0,
>       );
>       int ret, insn_cnt = ARRAY_SIZE(insns);
> +     int prog_type;
>
> -     ret = bpf_prog_load(BPF_PROG_TYPE_TRACEPOINT, NULL, "GPL", insns, 
> insn_cnt, &opts);
> +     prog_type = token_probeable_prog_type(token_fd);
> +     if (prog_type < 0)
> +             return 1;
> +
> +     ret = bpf_prog_load(prog_type, NULL, "GPL", insns, insn_cnt, &opts);

This probe tests for BPF_FUNC_get_attach_cookie and previously hardcoded
BPF_PROG_TYPE_TRACEPOINT.  In the no-token case token_probeable_prog_type()
now returns BPF_PROG_TYPE_SOCKET_FILTER.

The get_attach_cookie func_proto is only returned for tracing program
types (kprobe via kprobe_prog_func_proto, tracepoint via tp_prog_func_proto,
perf_event via pe_prog_func_proto, raw_tp/tracing).

For a SOCKET_FILTER program, helper resolution goes:

    sk_filter_func_proto() -> bpf_sk_base_func_proto() -> bpf_base_func_proto()

none of which expose BPF_FUNC_get_attach_cookie.

Does this mean the verifier rejects the trivial program, bpf_prog_load()
fails, probe_fd() returns 0, and feat_supported() caches FEAT_MISSING, so
FEAT_BPF_COOKIE is reported as unsupported on kernels that do support it,
even with no token in use?

> +     return probe_fd(ret);
>  }
>


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/27285576775

Reply via email to