af_unix: fix holding spinlock in oob handling
authorRao Shoaib <rao.shoaib@oracle.com>
Wed, 11 Aug 2021 22:06:52 +0000 (15:06 -0700)
committerJakub Kicinski <kuba@kernel.org>
Fri, 13 Aug 2021 17:31:22 +0000 (10:31 -0700)
syzkaller found that OOB code was holding spinlock
while calling a function in which it could sleep.

Reported-by: syzbot+8760ca6c1ee783ac4abd@syzkaller.appspotmail.com
Fixes: 314001f0bf92 ("af_unix: Add OOB support")
Signed-off-by: Rao Shoaib <rao.shoaib@oracle.com>
Link: https://lore.kernel.org/r/20210811220652.567434-1-Rao.Shoaib@oracle.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/unix/af_unix.c

index ec02e70a549b42f6c102253508c48426a13f7bc4..1c2224f05b513bda98a9a9f40d5b00b158aea44f 100644 (file)
@@ -2426,19 +2426,37 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state)
        struct sock *sk = sock->sk;
        struct unix_sock *u = unix_sk(sk);
        int chunk = 1;
+       struct sk_buff *oob_skb;
 
-       if (sock_flag(sk, SOCK_URGINLINE) || !u->oob_skb)
+       mutex_lock(&u->iolock);
+       unix_state_lock(sk);
+
+       if (sock_flag(sk, SOCK_URGINLINE) || !u->oob_skb) {
+               unix_state_unlock(sk);
+               mutex_unlock(&u->iolock);
                return -EINVAL;
+       }
 
-       chunk = state->recv_actor(u->oob_skb, 0, chunk, state);
-       if (chunk < 0)
-               return -EFAULT;
+       oob_skb = u->oob_skb;
 
        if (!(state->flags & MSG_PEEK)) {
-               UNIXCB(u->oob_skb).consumed += 1;
-               kfree_skb(u->oob_skb);
                u->oob_skb = NULL;
        }
+
+       unix_state_unlock(sk);
+
+       chunk = state->recv_actor(oob_skb, 0, chunk, state);
+
+       if (!(state->flags & MSG_PEEK)) {
+               UNIXCB(oob_skb).consumed += 1;
+               kfree_skb(oob_skb);
+       }
+
+       mutex_unlock(&u->iolock);
+
+       if (chunk < 0)
+               return -EFAULT;
+
        state->msg->msg_flags |= MSG_OOB;
        return 1;
 }
@@ -2498,13 +2516,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
        if (unlikely(flags & MSG_OOB)) {
                err = -EOPNOTSUPP;
 #if IS_ENABLED(CONFIG_AF_UNIX_OOB)
-               mutex_lock(&u->iolock);
-               unix_state_lock(sk);
-
                err = unix_stream_recv_urg(state);
-
-               unix_state_unlock(sk);
-               mutex_unlock(&u->iolock);
 #endif
                goto out;
        }