bpf: sockhash, disallow bpf_tcp_close and update in parallel
authorJohn Fastabend <john.fastabend@gmail.com>
Thu, 5 Jul 2018 15:50:04 +0000 (08:50 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 7 Jul 2018 22:19:30 +0000 (15:19 -0700)
After latest lock updates there is no longer anything preventing a
close and recvmsg call running in parallel. Additionally, we can
race update with close if we close a socket and simultaneously update
if via the BPF userspace API (note the cgroup ops are already run
with sock_lock held).

To resolve this take sock_lock in close and update paths.

Reported-by: syzbot+b680e42077a0d7c9a0c4@syzkaller.appspotmail.com
Fixes: e9db4ef6bf4c ("bpf: sockhash fix omitted bucket lock in sock_close")
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/sockmap.c
kernel/bpf/syscall.c

index 00fb2e328d1b0c56c2d79e8ab50f4d8aae84b491..9c67e96fe3362407a9ee8fc5aca0f3dc490d1073 100644 (file)
@@ -312,10 +312,12 @@ static void bpf_tcp_close(struct sock *sk, long timeout)
        struct smap_psock *psock;
        struct sock *osk;
 
+       lock_sock(sk);
        rcu_read_lock();
        psock = smap_psock_sk(sk);
        if (unlikely(!psock)) {
                rcu_read_unlock();
+               release_sock(sk);
                return sk->sk_prot->close(sk, timeout);
        }
 
@@ -371,6 +373,7 @@ static void bpf_tcp_close(struct sock *sk, long timeout)
                e = psock_map_pop(sk, psock);
        }
        rcu_read_unlock();
+       release_sock(sk);
        close_fun(sk, timeout);
 }
 
@@ -2069,7 +2072,13 @@ static int sock_map_update_elem(struct bpf_map *map,
                return -EOPNOTSUPP;
        }
 
+       lock_sock(skops.sk);
+       preempt_disable();
+       rcu_read_lock();
        err = sock_map_ctx_update_elem(&skops, map, key, flags);
+       rcu_read_unlock();
+       preempt_enable();
+       release_sock(skops.sk);
        fput(socket->file);
        return err;
 }
@@ -2410,7 +2419,13 @@ static int sock_hash_update_elem(struct bpf_map *map,
                return -EINVAL;
        }
 
+       lock_sock(skops.sk);
+       preempt_disable();
+       rcu_read_lock();
        err = sock_hash_ctx_update_elem(&skops, map, key, flags);
+       rcu_read_unlock();
+       preempt_enable();
+       release_sock(skops.sk);
        fput(socket->file);
        return err;
 }
index d10ecd78105fa66d2ffbd6b27c42fec2a6e6b09c..a31a1ba0f8eada88e03dc4a73ad9c0305cc70198 100644 (file)
@@ -735,7 +735,9 @@ static int map_update_elem(union bpf_attr *attr)
        if (bpf_map_is_dev_bound(map)) {
                err = bpf_map_offload_update_elem(map, key, value, attr->flags);
                goto out;
-       } else if (map->map_type == BPF_MAP_TYPE_CPUMAP) {
+       } else if (map->map_type == BPF_MAP_TYPE_CPUMAP ||
+                  map->map_type == BPF_MAP_TYPE_SOCKHASH ||
+                  map->map_type == BPF_MAP_TYPE_SOCKMAP) {
                err = map->ops->map_update_elem(map, key, value, attr->flags);
                goto out;
        }