tcp: ensure proper barriers in lockless contexts
Some functions access TCP sockets without holding a lock and might output non consistent data, depending on compiler and or architecture. tcp_diag_get_info(), tcp_get_info(), tcp_poll(), get_tcp4_sock() ... Introduce sk_state_load() and sk_state_store() to fix the issues, and more clearly document where this lack of locking is happening. Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
5883d9c6d7
commit
00fd38d938
@@ -451,11 +451,14 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
|
||||
unsigned int mask;
|
||||
struct sock *sk = sock->sk;
|
||||
const struct tcp_sock *tp = tcp_sk(sk);
|
||||
int state;
|
||||
|
||||
sock_rps_record_flow(sk);
|
||||
|
||||
sock_poll_wait(file, sk_sleep(sk), wait);
|
||||
if (sk->sk_state == TCP_LISTEN)
|
||||
|
||||
state = sk_state_load(sk);
|
||||
if (state == TCP_LISTEN)
|
||||
return inet_csk_listen_poll(sk);
|
||||
|
||||
/* Socket is not locked. We are protected from async events
|
||||
@@ -492,14 +495,14 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
|
||||
* NOTE. Check for TCP_CLOSE is added. The goal is to prevent
|
||||
* blocking on fresh not-connected or disconnected socket. --ANK
|
||||
*/
|
||||
if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == TCP_CLOSE)
|
||||
if (sk->sk_shutdown == SHUTDOWN_MASK || state == TCP_CLOSE)
|
||||
mask |= POLLHUP;
|
||||
if (sk->sk_shutdown & RCV_SHUTDOWN)
|
||||
mask |= POLLIN | POLLRDNORM | POLLRDHUP;
|
||||
|
||||
/* Connected or passive Fast Open socket? */
|
||||
if (sk->sk_state != TCP_SYN_SENT &&
|
||||
(sk->sk_state != TCP_SYN_RECV || tp->fastopen_rsk)) {
|
||||
if (state != TCP_SYN_SENT &&
|
||||
(state != TCP_SYN_RECV || tp->fastopen_rsk)) {
|
||||
int target = sock_rcvlowat(sk, 0, INT_MAX);
|
||||
|
||||
if (tp->urg_seq == tp->copied_seq &&
|
||||
@@ -507,9 +510,6 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
|
||||
tp->urg_data)
|
||||
target++;
|
||||
|
||||
/* Potential race condition. If read of tp below will
|
||||
* escape above sk->sk_state, we can be illegally awaken
|
||||
* in SYN_* states. */
|
||||
if (tp->rcv_nxt - tp->copied_seq >= target)
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
|
||||
@@ -1934,7 +1934,7 @@ void tcp_set_state(struct sock *sk, int state)
|
||||
/* Change state AFTER socket is unhashed to avoid closed
|
||||
* socket sitting in hash tables.
|
||||
*/
|
||||
sk->sk_state = state;
|
||||
sk_state_store(sk, state);
|
||||
|
||||
#ifdef STATE_TRACE
|
||||
SOCK_DEBUG(sk, "TCP sk=%p, State %s -> %s\n", sk, statename[oldstate], statename[state]);
|
||||
@@ -2644,7 +2644,8 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info)
|
||||
if (sk->sk_type != SOCK_STREAM)
|
||||
return;
|
||||
|
||||
info->tcpi_state = sk->sk_state;
|
||||
info->tcpi_state = sk_state_load(sk);
|
||||
|
||||
info->tcpi_ca_state = icsk->icsk_ca_state;
|
||||
info->tcpi_retransmits = icsk->icsk_retransmits;
|
||||
info->tcpi_probes = icsk->icsk_probes_out;
|
||||
@@ -2672,7 +2673,7 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info)
|
||||
info->tcpi_snd_mss = tp->mss_cache;
|
||||
info->tcpi_rcv_mss = icsk->icsk_ack.rcv_mss;
|
||||
|
||||
if (sk->sk_state == TCP_LISTEN) {
|
||||
if (info->tcpi_state == TCP_LISTEN) {
|
||||
info->tcpi_unacked = sk->sk_ack_backlog;
|
||||
info->tcpi_sacked = sk->sk_max_ack_backlog;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user