diff options
| author | John Fastabend <john.fastabend@gmail.com> | 2018-07-05 11:50:04 -0400 |
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2018-07-07 18:19:30 -0400 |
| commit | 99ba2b5aba24e022683a7db63204f9e306fe7ab9 (patch) | |
| tree | e174736f65a9dd001a67e2f007f93b5fd6265992 /kernel | |
| parent | 0c6bc6e531a6db36f49622f1f115770160f7afb0 (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>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/bpf/sockmap.c | 15 | ||||
| -rw-r--r-- | kernel/bpf/syscall.c | 4 |
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 | } |
