]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
bpf: Fix bpf_sk_select_reuseport() memory leak
authorMichal Luczaj <mhal@rbox.co>
Fri, 10 Jan 2025 13:21:55 +0000 (14:21 +0100)
committerJakub Kicinski <kuba@kernel.org>
Sat, 11 Jan 2025 01:59:29 +0000 (17:59 -0800)
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: 64d85290d79c ("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>
net/core/filter.c

index 834614071727ab92cee759dc788ec2ee6f92284b..2fb45a86f3ddbffa9fd55885d4c4c0d8c3a6c381 100644 (file)
@@ -11251,6 +11251,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)
@@ -11258,10 +11259,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.
@@ -11269,24 +11266,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 = {