diff options
author | John Fastabend <john.fastabend@gmail.com> | 2018-08-31 00:25:02 -0400 |
---|---|---|
committer | Daniel Borkmann <daniel@iogearbox.net> | 2018-09-02 16:31:10 -0400 |
commit | 597222f72a94118f593e4f32bf58ae7e049a0df1 (patch) | |
tree | 846e0e8c6a168acc8c4e6e7fe570b95e7e702699 /kernel | |
parent | 97911e0ccb54c7478b9dac77e6c54c01b449e3a7 (diff) |
bpf: avoid misuse of psock when TCP_ULP_BPF collides with another ULP
Currently we check sk_user_data is non NULL to determine if the sk
exists in a map. However, this is not sufficient to ensure the psock
or the ULP ops are not in use by another user, such as kcm or TLS. To
avoid this when adding a sock to a map also verify it is of the
correct ULP type. Additionally, when releasing a psock verify that
it is the TCP_ULP_BPF type before releasing the ULP. The error case
where we abort an update due to ULP collision can cause this error
path.
For example,
__sock_map_ctx_update_elem()
[...]
err = tcp_set_ulp_id(sock, TCP_ULP_BPF) <- collides with TLS
if (err) <- so err out here
goto out_free
[...]
out_free:
smap_release_sock() <- calling tcp_cleanup_ulp releases the
TLS ULP incorrectly.
Fixes: 2f857d04601a ("bpf: sockmap, remove STRPARSER map_flags and add multi-map support")
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/bpf/sockmap.c | 12 |
1 files changed, 11 insertions, 1 deletions
diff --git a/kernel/bpf/sockmap.c b/kernel/bpf/sockmap.c index ce63e5801746..488ef9663c01 100644 --- a/kernel/bpf/sockmap.c +++ b/kernel/bpf/sockmap.c | |||
@@ -1462,10 +1462,16 @@ static void smap_destroy_psock(struct rcu_head *rcu) | |||
1462 | schedule_work(&psock->gc_work); | 1462 | schedule_work(&psock->gc_work); |
1463 | } | 1463 | } |
1464 | 1464 | ||
1465 | static bool psock_is_smap_sk(struct sock *sk) | ||
1466 | { | ||
1467 | return inet_csk(sk)->icsk_ulp_ops == &bpf_tcp_ulp_ops; | ||
1468 | } | ||
1469 | |||
1465 | static void smap_release_sock(struct smap_psock *psock, struct sock *sock) | 1470 | static void smap_release_sock(struct smap_psock *psock, struct sock *sock) |
1466 | { | 1471 | { |
1467 | if (refcount_dec_and_test(&psock->refcnt)) { | 1472 | if (refcount_dec_and_test(&psock->refcnt)) { |
1468 | tcp_cleanup_ulp(sock); | 1473 | if (psock_is_smap_sk(sock)) |
1474 | tcp_cleanup_ulp(sock); | ||
1469 | write_lock_bh(&sock->sk_callback_lock); | 1475 | write_lock_bh(&sock->sk_callback_lock); |
1470 | smap_stop_sock(psock, sock); | 1476 | smap_stop_sock(psock, sock); |
1471 | write_unlock_bh(&sock->sk_callback_lock); | 1477 | write_unlock_bh(&sock->sk_callback_lock); |
@@ -1892,6 +1898,10 @@ static int __sock_map_ctx_update_elem(struct bpf_map *map, | |||
1892 | * doesn't update user data. | 1898 | * doesn't update user data. |
1893 | */ | 1899 | */ |
1894 | if (psock) { | 1900 | if (psock) { |
1901 | if (!psock_is_smap_sk(sock)) { | ||
1902 | err = -EBUSY; | ||
1903 | goto out_progs; | ||
1904 | } | ||
1895 | if (READ_ONCE(psock->bpf_parse) && parse) { | 1905 | if (READ_ONCE(psock->bpf_parse) && parse) { |
1896 | err = -EBUSY; | 1906 | err = -EBUSY; |
1897 | goto out_progs; | 1907 | goto out_progs; |