From: Linux kernel audit A BPF_PROG_TYPE_SOCKET_FILTER program can be loaded with expected_attach_type set to BPF_SK_REUSEPORT_SELECT_OR_MIGRATE and attached to a SO_REUSEPORT TCP group; both steps are allowed without CAP_BPF. Closing a listener that still has queued children then runs that socket filter with a struct sk_reuseport_kern as its context, while the program was verified against struct sk_buff. It reads and writes past the 56-byte object on the stack, which KASAN reports as a stack-out-of-bounds in ___bpf_prog_run(). The cause is that reuseport_migrate_sock() decides from prog->expected_attach_type alone. That field is supplied by user space and does not identify the program type, and bpf_prog_load_check_attach() does not reject the value for SOCKET_FILTER programs. Fix this by requiring BPF_PROG_TYPE_SK_REUSEPORT before running the program, and use the same check in reuseport_stop_listen_sock(). Socket filters can still be attached and used as selectors; the selection path is unchanged. Fixes: d5e4ddaeb6ab ("bpf: Support socket migration by eBPF.") Signed-off-by: Linux kernel audit --- net/core/sock_reuseport.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) --- a/net/core/sock_reuseport.c +++ b/net/core/sock_reuseport.c @@ -460,6 +460,18 @@ } EXPORT_SYMBOL(reuseport_detach_sock); +/* + * Only a BPF_PROG_TYPE_SK_REUSEPORT program is verified against + * struct sk_reuseport_kern, which is the context bpf_run_sk_reuseport() + * passes to it. expected_attach_type is set by user space and does not + * identify the program type. + */ +static bool reuseport_is_migration_prog(const struct bpf_prog *prog) +{ + return prog && prog->type == BPF_PROG_TYPE_SK_REUSEPORT && + prog->expected_attach_type == BPF_SK_REUSEPORT_SELECT_OR_MIGRATE; +} + void reuseport_stop_listen_sock(struct sock *sk) { if (sk->sk_protocol == IPPROTO_TCP) { @@ -474,7 +486,7 @@ lockdep_is_held(&reuseport_lock)); if (READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_migrate_req) || - (prog && prog->expected_attach_type == BPF_SK_REUSEPORT_SELECT_OR_MIGRATE)) { + reuseport_is_migration_prog(prog)) { /* Migration capable, move sk from the listening section * to the closed section. */ @@ -644,7 +656,7 @@ hash = migrating_sk->sk_hash; prog = rcu_dereference(reuse->prog); - if (!prog || prog->expected_attach_type != BPF_SK_REUSEPORT_SELECT_OR_MIGRATE) { + if (!reuseport_is_migration_prog(prog)) { if (READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_migrate_req)) goto select_by_hash; goto failure;