bpf: Fix global subprog context argument resolution logic

Bugzilla: https://bugzilla.redhat.com/2178930

commit d384dce281ed1b504fae2e279507827638d56fa3
Author: Andrii Nakryiko <andrii@kernel.org>
Date:   Wed Feb 15 20:59:52 2023 -0800

    bpf: Fix global subprog context argument resolution logic
    
    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: 91cc1a9974 ("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: Viktor Malik <vmalik@redhat.com>
This commit is contained in:
Viktor Malik 2023-05-10 07:19:38 +02:00 committed by Felix Maurer
parent 8dab6796dc
commit b9ffc5c79f
1 changed files with 11 additions and 2 deletions

View File

@ -5669,6 +5669,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 */
@ -5682,8 +5683,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;
}