Message ID | 20220615011540.813025-1-jmaxwell37@gmail.com (mailing list archive) |
---|---|
State | Accepted |
Commit | 3046a827316c0e55fc563b4fb78c93b9ca5c7c37 |
Delegated to: | BPF |
Headers | show |
Series | [v2] net: bpf: fix request_sock leak in filter.c | expand |
Hello: This patch was applied to bpf/bpf.git (master) by Daniel Borkmann <daniel@iogearbox.net>: On Wed, 15 Jun 2022 11:15:40 +1000 you wrote: > v2 of this patch contains, refactor as per Daniel Borkmann's suggestions to > validate RCU flags on the listen socket so that it balances with > bpf_sk_release() and update comments as per Martin KaFai Lau's suggestion. > One small change to Daniels suggestion, put "sk = sk2" under "if (sk2 != sk)" > to avoid an extra instruction. > > A customer reported a request_socket leak in a Calico cloud environment. We > found that a BPF program was doing a socket lookup with takes a refcnt on > the socket and that it was finding the request_socket but returning the parent > LISTEN socket via sk_to_full_sk() without decrementing the child request socket > 1st, resulting in request_sock slab object leak. This patch retains the > existing behaviour of returning full socks to the caller but it also decrements > the child request_socket if one is present before doing so to prevent the leak. > > [...] Here is the summary with links: - [v2] net: bpf: fix request_sock leak in filter.c https://git.kernel.org/bpf/bpf/c/3046a827316c You are awesome, thank you!
diff --git a/net/core/filter.c b/net/core/filter.c index 2e32cee2c469..ec2a1e68af12 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -6204,10 +6204,21 @@ __bpf_sk_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len, ifindex, proto, netns_id, flags); if (sk) { - sk = sk_to_full_sk(sk); - if (!sk_fullsock(sk)) { + struct sock *sk2 = sk_to_full_sk(sk); + + /* sk_to_full_sk() may return (sk)->rsk_listener, so make sure the original sk + * sock refcnt is decremented to prevent a request_sock leak. + */ + if (!sk_fullsock(sk2)) + sk2 = NULL; + if (sk2 != sk) { sock_gen_put(sk); - return NULL; + /* Ensure there is no need to bump sk2 refcnt */ + if (unlikely(sk2 && !sock_flag(sk2, SOCK_RCU_FREE))) { + WARN_ONCE(1, "Found non-RCU, unreferenced socket!"); + return NULL; + } + sk = sk2; } } @@ -6241,10 +6252,21 @@ bpf_sk_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len, flags); if (sk) { - sk = sk_to_full_sk(sk); - if (!sk_fullsock(sk)) { + struct sock *sk2 = sk_to_full_sk(sk); + + /* sk_to_full_sk() may return (sk)->rsk_listener, so make sure the original sk + * sock refcnt is decremented to prevent a request_sock leak. + */ + if (!sk_fullsock(sk2)) + sk2 = NULL; + if (sk2 != sk) { sock_gen_put(sk); - return NULL; + /* Ensure there is no need to bump sk2 refcnt */ + if (unlikely(sk2 && !sock_flag(sk2, SOCK_RCU_FREE))) { + WARN_ONCE(1, "Found non-RCU, unreferenced socket!"); + return NULL; + } + sk = sk2; } }