aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Fastabend <john.fastabend@gmail.com>2018-07-05 11:50:04 -0400
committerAlexei Starovoitov <ast@kernel.org>2018-07-07 18:19:30 -0400
commit99ba2b5aba24e022683a7db63204f9e306fe7ab9 (patch)
treee174736f65a9dd001a67e2f007f93b5fd6265992
parent0c6bc6e531a6db36f49622f1f115770160f7afb0 (diff)
bpf: sockhash, disallow bpf_tcp_close and update in parallel
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>
-rw-r--r--kernel/bpf/sockmap.c15
-rw-r--r--kernel/bpf/syscall.c4
2 files changed, 18 insertions, 1 deletions
diff --git a/kernel/bpf/sockmap.c b/kernel/bpf/sockmap.c
index 00fb2e328d1b..9c67e96fe336 100644
--- a/kernel/bpf/sockmap.c
+++ b/kernel/bpf/sockmap.c
@@ -312,10 +312,12 @@ static void bpf_tcp_close(struct sock *sk, long timeout)
312 struct smap_psock *psock; 312 struct smap_psock *psock;
313 struct sock *osk; 313 struct sock *osk;
314 314
315 lock_sock(sk);
315 rcu_read_lock(); 316 rcu_read_lock();
316 psock = smap_psock_sk(sk); 317 psock = smap_psock_sk(sk);
317 if (unlikely(!psock)) { 318 if (unlikely(!psock)) {
318 rcu_read_unlock(); 319 rcu_read_unlock();
320 release_sock(sk);
319 return sk->sk_prot->close(sk, timeout); 321 return sk->sk_prot->close(sk, timeout);
320 } 322 }
321 323
@@ -371,6 +373,7 @@ static void bpf_tcp_close(struct sock *sk, long timeout)
371 e = psock_map_pop(sk, psock); 373 e = psock_map_pop(sk, psock);
372 } 374 }
373 rcu_read_unlock(); 375 rcu_read_unlock();
376 release_sock(sk);
374 close_fun(sk, timeout); 377 close_fun(sk, timeout);
375} 378}
376 379
@@ -2069,7 +2072,13 @@ static int sock_map_update_elem(struct bpf_map *map,
2069 return -EOPNOTSUPP; 2072 return -EOPNOTSUPP;
2070 } 2073 }
2071 2074
2075 lock_sock(skops.sk);
2076 preempt_disable();
2077 rcu_read_lock();
2072 err = sock_map_ctx_update_elem(&skops, map, key, flags); 2078 err = sock_map_ctx_update_elem(&skops, map, key, flags);
2079 rcu_read_unlock();
2080 preempt_enable();
2081 release_sock(skops.sk);
2073 fput(socket->file); 2082 fput(socket->file);
2074 return err; 2083 return err;
2075} 2084}
@@ -2410,7 +2419,13 @@ static int sock_hash_update_elem(struct bpf_map *map,
2410 return -EINVAL; 2419 return -EINVAL;
2411 } 2420 }
2412 2421
2422 lock_sock(skops.sk);
2423 preempt_disable();
2424 rcu_read_lock();
2413 err = sock_hash_ctx_update_elem(&skops, map, key, flags); 2425 err = sock_hash_ctx_update_elem(&skops, map, key, flags);
2426 rcu_read_unlock();
2427 preempt_enable();
2428 release_sock(skops.sk);
2414 fput(socket->file); 2429 fput(socket->file);
2415 return err; 2430 return err;
2416} 2431}
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index d10ecd78105f..a31a1ba0f8ea 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -735,7 +735,9 @@ static int map_update_elem(union bpf_attr *attr)
735 if (bpf_map_is_dev_bound(map)) { 735 if (bpf_map_is_dev_bound(map)) {
736 err = bpf_map_offload_update_elem(map, key, value, attr->flags); 736 err = bpf_map_offload_update_elem(map, key, value, attr->flags);
737 goto out; 737 goto out;
738 } else if (map->map_type == BPF_MAP_TYPE_CPUMAP) { 738 } else if (map->map_type == BPF_MAP_TYPE_CPUMAP ||
739 map->map_type == BPF_MAP_TYPE_SOCKHASH ||
740 map->map_type == BPF_MAP_TYPE_SOCKMAP) {
739 err = map->ops->map_update_elem(map, key, value, attr->flags); 741 err = map->ops->map_update_elem(map, key, value, attr->flags);
740 goto out; 742 goto out;
741 } 743 }