af_unix: Annotate data-race of sk->sk_state in unix_inq_len().
authorKuniyuki Iwashima <kuniyu@amazon.com>
Tue, 4 Jun 2024 16:52:29 +0000 (09:52 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 6 Jun 2024 10:57:14 +0000 (12:57 +0200)
ioctl(SIOCINQ) calls unix_inq_len() that checks sk->sk_state first
and returns -EINVAL if it's TCP_LISTEN.

Then, for SOCK_STREAM sockets, unix_inq_len() returns the number of
bytes in recvq.

However, unix_inq_len() does not hold unix_state_lock(), and the
concurrent listen() might change the state after checking sk->sk_state.

If the race occurs, 0 is returned for the listener, instead of -EINVAL,
because the length of skb with embryo is 0.

We could hold unix_state_lock() in unix_inq_len(), but it's overkill
given the result is true for pre-listen() TCP_CLOSE state.

So, let's use READ_ONCE() for sk->sk_state in unix_inq_len().

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/unix/af_unix.c

index 424d021a4d7d76e811b9ac4068f4bd15751d53fa..b37b53767b29b6954db6a51ee3c35378a01c303f 100644 (file)
@@ -3017,7 +3017,7 @@ long unix_inq_len(struct sock *sk)
        struct sk_buff *skb;
        long amount = 0;
 
-       if (sk->sk_state == TCP_LISTEN)
+       if (READ_ONCE(sk->sk_state) == TCP_LISTEN)
                return -EINVAL;
 
        spin_lock(&sk->sk_receive_queue.lock);