The trampoline could be corrupted by the blindly 'tr->flags = BPF_TRAMP_F_TAIL_CALL_CTX' in verifier. 1. A fexit attached to a tail_call_reachable prog. 'tr->flags' became 'BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_TAIL_CALL_CTX'. And, the trampoline would poke the target prog's nop insn using jmp insn instead of call insn. 2. Another fexit loaded with the same tail_call_reachable prog target. 'tr->flags' became 'BPF_TRAMP_F_TAIL_CALL_CTX'. 3. Close the first fexit link. Due to no BPF_TRAMP_F_CALL_ORIG in 'tr->flags', the trampoline will fail to restore the prog's nop insn using call insn. [ 3.410719] WARNING: kernel/bpf/syscall.c:3551 at bpf_tracing_link_release+0x53/0x60, CPU#1: test_progs/98 ... [ 3.428793] bpf_link_free+0x58/0x130 [ 3.429293] bpf_link_release+0x23/0x30 Fix the warning by updating 'tr->flags' with '|=' and lock.
Unified · Split
| @@ -1523,6 +1523,7 @@ int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids, · 1522 unmodified lines | |||
| 1523 | struct bpf_tracing_multi_link *link); | 1523 | struct bpf_tracing_multi_link *link); |
| 1524 | int bpf_trampoline_multi_detach(struct bpf_prog *prog, | 1524 | int bpf_trampoline_multi_detach(struct bpf_prog *prog, |
| 1525 | struct bpf_tracing_multi_link *link); | 1525 | struct bpf_tracing_multi_link *link); |
| 1526 | void bpf_trampoline_set_flags(struct bpf_trampoline *tr, u32 flags); | ||
| 1526 | 1527 | ||
| 1527 | /* | 1528 | /* |
| 1528 | * When the architecture supports STATIC_CALL replace the bpf_dispatcher_fn | 1529 | * When the architecture supports STATIC_CALL replace the bpf_dispatcher_fn |
| @@ -1646,6 +1647,7 @@ static inline int bpf_trampoline_multi_detach(struct bpf_prog *prog, · 117 unmodified lines | |||
| 1646 | { | 1647 | { |
| 1647 | return -ENOTSUPP; | 1648 | return -ENOTSUPP; |
| 1648 | } | 1649 | } |
| 1650 | static inline void bpf_trampoline_set_flags(struct bpf_trampoline *tr, u32 flags) {} | ||
| 1649 | #endif | 1651 | #endif |
| 1650 | 1652 | ||
| 1651 | struct bpf_func_info_aux { | 1653 | struct bpf_func_info_aux { |
| @@ -670,6 +670,13 @@ out: · 669 unmodified lines | |||
| 670 | return ERR_PTR(err); | 670 | return ERR_PTR(err); |
| 671 | } | 671 | } |
| 672 | 672 | ||
| 673 | void bpf_trampoline_set_flags(struct bpf_trampoline *tr, u32 flags) | ||
| 674 | { | ||
| 675 | trampoline_lock(tr); | ||
| 676 | tr->flags |= flags; | ||
| 677 | trampoline_unlock(tr); | ||
| 678 | } | ||
| 679 | |||
| 673 | static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mutex, | 680 | static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mutex, |
| 674 | const struct bpf_trampoline_ops *ops, void *data) | 681 | const struct bpf_trampoline_ops *ops, void *data) |
| 675 | { | 682 | { |
| @@ -19523,7 +19523,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) · 19522 unmodified lines | |||
| 19523 | return -ENOMEM; | 19523 | return -ENOMEM; |
| 19524 | 19524 | ||
| 19525 | if (tgt_prog && tgt_prog->aux->tail_call_reachable) | 19525 | if (tgt_prog && tgt_prog->aux->tail_call_reachable) |
| 19526 | tr->flags = BPF_TRAMP_F_TAIL_CALL_CTX; | 19526 | bpf_trampoline_set_flags(tr, BPF_TRAMP_F_TAIL_CALL_CTX); |
| 19527 | 19527 | ||
| 19528 | prog->aux->dst_trampoline = tr; | 19528 | prog->aux->dst_trampoline = tr; |
| 19529 | return 0; | 19529 | return 0; |