bpf: Fix bpf_sk_select_reuseport() memory leak
[ Upstream commit b3af60928ab9129befa65e6df0310d27300942bf ]
As pointed out in the original comment, lookup in sockmap can return a TCP
ESTABLISHED socket. Such TCP socket may have had SO_ATTACH_REUSEPORT_EBPF
set before it was ESTABLISHED. In other words, a non-NULL sk_reuseport_cb
does not imply a non-refcounted socket.
Drop sk's reference in both error paths.
unreferenced object 0xffff888101911800 (size 2048):
comm "test_progs", pid 44109, jiffies 4297131437
hex dump (first 32 bytes):
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
80 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
backtrace (crc 9336483b):
__kmalloc_noprof+0x3bf/0x560
__reuseport_alloc+0x1d/0x40
reuseport_alloc+0xca/0x150
reuseport_attach_prog+0x87/0x140
sk_reuseport_attach_bpf+0xc8/0x100
sk_setsockopt+0x1181/0x1990
do_sock_setsockopt+0x12b/0x160
__sys_setsockopt+0x7b/0xc0
__x64_sys_setsockopt+0x1b/0x30
do_syscall_64+0x93/0x180
entry_SYSCALL_64_after_hwframe+0x76/0x7e
Fixes: 64d85290d7 ("bpf: Allow bpf_map_lookup_elem for SOCKMAP and SOCKHASH")
Signed-off-by: Michal Luczaj <mhal@rbox.co>
Reviewed-by: Martin KaFai Lau <martin.lau@kernel.org>
Link: https://patch.msgid.link/20250110-reuseport-memleak-v1-1-fa1ddab0adfe@rbox.co
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
9e08229256
commit
cccd51dd22
@@ -11263,6 +11263,7 @@ BPF_CALL_4(sk_select_reuseport, struct sk_reuseport_kern *, reuse_kern,
|
||||
bool is_sockarray = map->map_type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY;
|
||||
struct sock_reuseport *reuse;
|
||||
struct sock *selected_sk;
|
||||
int err;
|
||||
|
||||
selected_sk = map->ops->map_lookup_elem(map, key);
|
||||
if (!selected_sk)
|
||||
@@ -11270,10 +11271,6 @@ BPF_CALL_4(sk_select_reuseport, struct sk_reuseport_kern *, reuse_kern,
|
||||
|
||||
reuse = rcu_dereference(selected_sk->sk_reuseport_cb);
|
||||
if (!reuse) {
|
||||
/* Lookup in sock_map can return TCP ESTABLISHED sockets. */
|
||||
if (sk_is_refcounted(selected_sk))
|
||||
sock_put(selected_sk);
|
||||
|
||||
/* reuseport_array has only sk with non NULL sk_reuseport_cb.
|
||||
* The only (!reuse) case here is - the sk has already been
|
||||
* unhashed (e.g. by close()), so treat it as -ENOENT.
|
||||
@@ -11281,24 +11278,33 @@ BPF_CALL_4(sk_select_reuseport, struct sk_reuseport_kern *, reuse_kern,
|
||||
* Other maps (e.g. sock_map) do not provide this guarantee and
|
||||
* the sk may never be in the reuseport group to begin with.
|
||||
*/
|
||||
return is_sockarray ? -ENOENT : -EINVAL;
|
||||
err = is_sockarray ? -ENOENT : -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (unlikely(reuse->reuseport_id != reuse_kern->reuseport_id)) {
|
||||
struct sock *sk = reuse_kern->sk;
|
||||
|
||||
if (sk->sk_protocol != selected_sk->sk_protocol)
|
||||
return -EPROTOTYPE;
|
||||
else if (sk->sk_family != selected_sk->sk_family)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
/* Catch all. Likely bound to a different sockaddr. */
|
||||
return -EBADFD;
|
||||
if (sk->sk_protocol != selected_sk->sk_protocol) {
|
||||
err = -EPROTOTYPE;
|
||||
} else if (sk->sk_family != selected_sk->sk_family) {
|
||||
err = -EAFNOSUPPORT;
|
||||
} else {
|
||||
/* Catch all. Likely bound to a different sockaddr. */
|
||||
err = -EBADFD;
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
|
||||
reuse_kern->selected_sk = selected_sk;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
/* Lookup in sock_map can return TCP ESTABLISHED sockets. */
|
||||
if (sk_is_refcounted(selected_sk))
|
||||
sock_put(selected_sk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct bpf_func_proto sk_select_reuseport_proto = {
|
||||
|
||||
Reference in New Issue
Block a user