mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs.git
synced 2026-11-01 06:46:35 +01:00
bpf, sockmap: Don't let sock_map_{close,destroy,unhash} call itself
sock_map proto callbacks should never call themselves by design. Protect against bugs like [1] and break out of the recursive loop to avoid a stack overflow in favor of a resource leak. [1] https://lore.kernel.org/all/[email protected]/ Suggested-by: Eric Dumazet <[email protected]> Signed-off-by: Jakub Sitnicki <[email protected]> Acked-by: John Fastabend <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
This commit is contained in:
1 parent
74bc3a5acc
commit
5b4a79ba65
1 file changed
+34
-27
+34
-27
@@ -1569,15 +1569,16 @@ void sock_map_unhash(struct sock *sk)
|
||||
psock = sk_psock(sk);
|
||||
if (unlikely(!psock)) {
|
||||
rcu_read_unlock();
|
||||
if (sk->sk_prot->unhash)
|
||||
sk->sk_prot->unhash(sk);
|
||||
return;
|
||||
saved_unhash = READ_ONCE(sk->sk_prot)->unhash;
|
||||
} else {
|
||||
saved_unhash = psock->saved_unhash;
|
||||
sock_map_remove_links(sk, psock);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
saved_unhash = psock->saved_unhash;
|
||||
sock_map_remove_links(sk, psock);
|
||||
rcu_read_unlock();
|
||||
saved_unhash(sk);
|
||||
if (WARN_ON_ONCE(saved_unhash == sock_map_unhash))
|
||||
return;
|
||||
if (saved_unhash)
|
||||
saved_unhash(sk);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sock_map_unhash);
|
||||
|
||||
@@ -1590,17 +1591,18 @@ void sock_map_destroy(struct sock *sk)
|
||||
psock = sk_psock_get(sk);
|
||||
if (unlikely(!psock)) {
|
||||
rcu_read_unlock();
|
||||
if (sk->sk_prot->destroy)
|
||||
sk->sk_prot->destroy(sk);
|
||||
return;
|
||||
saved_destroy = READ_ONCE(sk->sk_prot)->destroy;
|
||||
} else {
|
||||
saved_destroy = psock->saved_destroy;
|
||||
sock_map_remove_links(sk, psock);
|
||||
rcu_read_unlock();
|
||||
sk_psock_stop(psock);
|
||||
sk_psock_put(sk, psock);
|
||||
}
|
||||
|
||||
saved_destroy = psock->saved_destroy;
|
||||
sock_map_remove_links(sk, psock);
|
||||
rcu_read_unlock();
|
||||
sk_psock_stop(psock);
|
||||
sk_psock_put(sk, psock);
|
||||
saved_destroy(sk);
|
||||
if (WARN_ON_ONCE(saved_destroy == sock_map_destroy))
|
||||
return;
|
||||
if (saved_destroy)
|
||||
saved_destroy(sk);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sock_map_destroy);
|
||||
|
||||
@@ -1615,16 +1617,21 @@ void sock_map_close(struct sock *sk, long timeout)
|
||||
if (unlikely(!psock)) {
|
||||
rcu_read_unlock();
|
||||
release_sock(sk);
|
||||
return sk->sk_prot->close(sk, timeout);
|
||||
saved_close = READ_ONCE(sk->sk_prot)->close;
|
||||
} else {
|
||||
saved_close = psock->saved_close;
|
||||
sock_map_remove_links(sk, psock);
|
||||
rcu_read_unlock();
|
||||
sk_psock_stop(psock);
|
||||
release_sock(sk);
|
||||
cancel_work_sync(&psock->work);
|
||||
sk_psock_put(sk, psock);
|
||||
}
|
||||
|
||||
saved_close = psock->saved_close;
|
||||
sock_map_remove_links(sk, psock);
|
||||
rcu_read_unlock();
|
||||
sk_psock_stop(psock);
|
||||
release_sock(sk);
|
||||
cancel_work_sync(&psock->work);
|
||||
sk_psock_put(sk, psock);
|
||||
/* Make sure we do not recurse. This is a bug.
|
||||
* Leak the socket instead of crashing on a stack overflow.
|
||||
*/
|
||||
if (WARN_ON_ONCE(saved_close == sock_map_close))
|
||||
return;
|
||||
saved_close(sk, timeout);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sock_map_close);
|
||||
|
||||
Reference in new issue
Block a user