bpf: Fix global subprog context argument resolution logic
authorAndrii Nakryiko <andrii@kernel.org>
Thu, 16 Feb 2023 04:59:52 +0000 (20:59 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 10 Mar 2023 08:39:18 +0000 (09:39 +0100)
[ Upstream commit d384dce281ed1b504fae2e279507827638d56fa3 ]

KPROBE program's user-facing context type is defined as typedef
bpf_user_pt_regs_t. This leads to a problem when trying to passing
kprobe/uprobe/usdt context argument into global subprog, as kernel
always strip away mods and typedefs of user-supplied type, but takes
expected type from bpf_ctx_convert as is, which causes mismatch.

Current way to work around this is to define a fake struct with the same
name as expected typedef:

  struct bpf_user_pt_regs_t {};

  __noinline my_global_subprog(struct bpf_user_pt_regs_t *ctx) { ... }

This patch fixes the issue by resolving expected type, if it's not
a struct. It still leaves the above work-around working for backwards
compatibility.

Fixes: 91cc1a99740e ("bpf: Annotate context types")
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Stanislav Fomichev <sdf@google.com>
Link: https://lore.kernel.org/bpf/20230216045954.3002473-2-andrii@kernel.org
Signed-off-by: Sasha Levin <sashal@kernel.org>
kernel/bpf/btf.c

index 0c2fa93bd8d274784cd04e6336da04cb5aa94a77..1f9369b677fe2e3ac05d23e79c1d9fc6c814d290 100644 (file)
@@ -4468,6 +4468,7 @@ btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
        if (!ctx_struct)
                /* should not happen */
                return NULL;
+again:
        ctx_tname = btf_name_by_offset(btf_vmlinux, ctx_struct->name_off);
        if (!ctx_tname) {
                /* should not happen */
@@ -4481,8 +4482,16 @@ btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
         * int socket_filter_bpf_prog(struct __sk_buff *skb)
         * { // no fields of skb are ever used }
         */
-       if (strcmp(ctx_tname, tname))
-               return NULL;
+       if (strcmp(ctx_tname, tname)) {
+               /* bpf_user_pt_regs_t is a typedef, so resolve it to
+                * underlying struct and check name again
+                */
+               if (!btf_type_is_modifier(ctx_struct))
+                       return NULL;
+               while (btf_type_is_modifier(ctx_struct))
+                       ctx_struct = btf_type_by_id(btf_vmlinux, ctx_struct->type);
+               goto again;
+       }
        return ctx_type;
 }