mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-11 03:45:21 +02:00
af_unix: Annotate data-races around sk->sk_state in unix_write_space() and poll().
[ Upstream commiteb0718fb3e
] unix_poll() and unix_dgram_poll() read sk->sk_state locklessly and calls unix_writable() which also reads sk->sk_state without holding unix_state_lock(). Let's use READ_ONCE() in unix_poll() and unix_dgram_poll() and pass it to unix_writable(). While at it, we remove TCP_SYN_SENT check in unix_dgram_poll() as that state does not exist for AF_UNIX socket since the code was added. Fixes:1586a5877d
("af_unix: do not report POLLOUT on listeners") Fixes:3c73419c09
("af_unix: fix 'poll for write'/ connected DGRAM sockets") Fixes:1da177e4c3
("Linux-2.6.12-rc2") Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
4e38d6c049
commit
484e036e1a
|
@ -531,9 +531,9 @@ static int unix_dgram_peer_wake_me(struct sock *sk, struct sock *other)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unix_writable(const struct sock *sk)
|
static int unix_writable(const struct sock *sk, unsigned char state)
|
||||||
{
|
{
|
||||||
return sk->sk_state != TCP_LISTEN &&
|
return state != TCP_LISTEN &&
|
||||||
(refcount_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf;
|
(refcount_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,7 +542,7 @@ static void unix_write_space(struct sock *sk)
|
||||||
struct socket_wq *wq;
|
struct socket_wq *wq;
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
if (unix_writable(sk)) {
|
if (unix_writable(sk, READ_ONCE(sk->sk_state))) {
|
||||||
wq = rcu_dereference(sk->sk_wq);
|
wq = rcu_dereference(sk->sk_wq);
|
||||||
if (skwq_has_sleeper(wq))
|
if (skwq_has_sleeper(wq))
|
||||||
wake_up_interruptible_sync_poll(&wq->wait,
|
wake_up_interruptible_sync_poll(&wq->wait,
|
||||||
|
@ -3095,12 +3095,14 @@ static int unix_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned lon
|
||||||
static __poll_t unix_poll(struct file *file, struct socket *sock, poll_table *wait)
|
static __poll_t unix_poll(struct file *file, struct socket *sock, poll_table *wait)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
|
unsigned char state;
|
||||||
__poll_t mask;
|
__poll_t mask;
|
||||||
u8 shutdown;
|
u8 shutdown;
|
||||||
|
|
||||||
sock_poll_wait(file, sock, wait);
|
sock_poll_wait(file, sock, wait);
|
||||||
mask = 0;
|
mask = 0;
|
||||||
shutdown = READ_ONCE(sk->sk_shutdown);
|
shutdown = READ_ONCE(sk->sk_shutdown);
|
||||||
|
state = READ_ONCE(sk->sk_state);
|
||||||
|
|
||||||
/* exceptional events? */
|
/* exceptional events? */
|
||||||
if (READ_ONCE(sk->sk_err))
|
if (READ_ONCE(sk->sk_err))
|
||||||
|
@ -3122,14 +3124,14 @@ static __poll_t unix_poll(struct file *file, struct socket *sock, poll_table *wa
|
||||||
|
|
||||||
/* Connection-based need to check for termination and startup */
|
/* Connection-based need to check for termination and startup */
|
||||||
if ((sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) &&
|
if ((sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) &&
|
||||||
sk->sk_state == TCP_CLOSE)
|
state == TCP_CLOSE)
|
||||||
mask |= EPOLLHUP;
|
mask |= EPOLLHUP;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* we set writable also when the other side has shut down the
|
* we set writable also when the other side has shut down the
|
||||||
* connection. This prevents stuck sockets.
|
* connection. This prevents stuck sockets.
|
||||||
*/
|
*/
|
||||||
if (unix_writable(sk))
|
if (unix_writable(sk, state))
|
||||||
mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
|
mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
|
||||||
|
|
||||||
return mask;
|
return mask;
|
||||||
|
@ -3140,12 +3142,14 @@ static __poll_t unix_dgram_poll(struct file *file, struct socket *sock,
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk, *other;
|
struct sock *sk = sock->sk, *other;
|
||||||
unsigned int writable;
|
unsigned int writable;
|
||||||
|
unsigned char state;
|
||||||
__poll_t mask;
|
__poll_t mask;
|
||||||
u8 shutdown;
|
u8 shutdown;
|
||||||
|
|
||||||
sock_poll_wait(file, sock, wait);
|
sock_poll_wait(file, sock, wait);
|
||||||
mask = 0;
|
mask = 0;
|
||||||
shutdown = READ_ONCE(sk->sk_shutdown);
|
shutdown = READ_ONCE(sk->sk_shutdown);
|
||||||
|
state = READ_ONCE(sk->sk_state);
|
||||||
|
|
||||||
/* exceptional events? */
|
/* exceptional events? */
|
||||||
if (READ_ONCE(sk->sk_err) ||
|
if (READ_ONCE(sk->sk_err) ||
|
||||||
|
@ -3165,19 +3169,14 @@ static __poll_t unix_dgram_poll(struct file *file, struct socket *sock,
|
||||||
mask |= EPOLLIN | EPOLLRDNORM;
|
mask |= EPOLLIN | EPOLLRDNORM;
|
||||||
|
|
||||||
/* Connection-based need to check for termination and startup */
|
/* Connection-based need to check for termination and startup */
|
||||||
if (sk->sk_type == SOCK_SEQPACKET) {
|
if (sk->sk_type == SOCK_SEQPACKET && state == TCP_CLOSE)
|
||||||
if (sk->sk_state == TCP_CLOSE)
|
|
||||||
mask |= EPOLLHUP;
|
mask |= EPOLLHUP;
|
||||||
/* connection hasn't started yet? */
|
|
||||||
if (sk->sk_state == TCP_SYN_SENT)
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No write status requested, avoid expensive OUT tests. */
|
/* No write status requested, avoid expensive OUT tests. */
|
||||||
if (!(poll_requested_events(wait) & (EPOLLWRBAND|EPOLLWRNORM|EPOLLOUT)))
|
if (!(poll_requested_events(wait) & (EPOLLWRBAND|EPOLLWRNORM|EPOLLOUT)))
|
||||||
return mask;
|
return mask;
|
||||||
|
|
||||||
writable = unix_writable(sk);
|
writable = unix_writable(sk, state);
|
||||||
if (writable) {
|
if (writable) {
|
||||||
unix_state_lock(sk);
|
unix_state_lock(sk);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user