Commit 50194c3f authored by Xin Long's avatar Xin Long Committed by Greg Kroah-Hartman

sctp: return error if the asoc has been peeled off in sctp_wait_for_sndbuf


[ Upstream commit a0ff6600 ]

After commit cea0cc80 ("sctp: use the right sk after waking up from
wait_buf sleep"), it may change to lock another sk if the asoc has been
peeled off in sctp_wait_for_sndbuf.

However, the asoc's new sk could be already closed elsewhere, as it's in
the sendmsg context of the old sk that can't avoid the new sk's closing.
If the sk's last one refcnt is held by this asoc, later on after putting
this asoc, the new sk will be freed, while under it's own lock.

This patch is to revert that commit, but fix the old issue by returning
error under the old sk's lock.

Fixes: cea0cc80 ("sctp: use the right sk after waking up from wait_buf sleep")
Reported-by: syzbot+ac6ea7baa4432811eb50@syzkaller.appspotmail.com
Signed-off-by: default avatarXin Long <lucien.xin@gmail.com>
Acked-by: default avatarNeil Horman <nhorman@tuxdriver.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 23f521bc
...@@ -83,7 +83,7 @@ ...@@ -83,7 +83,7 @@
static int sctp_writeable(struct sock *sk); static int sctp_writeable(struct sock *sk);
static void sctp_wfree(struct sk_buff *skb); static void sctp_wfree(struct sk_buff *skb);
static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
size_t msg_len, struct sock **orig_sk); size_t msg_len);
static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p); static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p);
static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p); static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);
static int sctp_wait_for_accept(struct sock *sk, long timeo); static int sctp_wait_for_accept(struct sock *sk, long timeo);
...@@ -1952,7 +1952,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) ...@@ -1952,7 +1952,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
if (!sctp_wspace(asoc)) { if (!sctp_wspace(asoc)) {
/* sk can be changed by peel off when waiting for buf. */ /* sk can be changed by peel off when waiting for buf. */
err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len, &sk); err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
if (err) { if (err) {
if (err == -ESRCH) { if (err == -ESRCH) {
/* asoc is already dead. */ /* asoc is already dead. */
...@@ -6974,12 +6974,12 @@ void sctp_sock_rfree(struct sk_buff *skb) ...@@ -6974,12 +6974,12 @@ void sctp_sock_rfree(struct sk_buff *skb)
/* Helper function to wait for space in the sndbuf. */ /* Helper function to wait for space in the sndbuf. */
static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
size_t msg_len, struct sock **orig_sk) size_t msg_len)
{ {
struct sock *sk = asoc->base.sk; struct sock *sk = asoc->base.sk;
int err = 0;
long current_timeo = *timeo_p; long current_timeo = *timeo_p;
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
int err = 0;
pr_debug("%s: asoc:%p, timeo:%ld, msg_len:%zu\n", __func__, asoc, pr_debug("%s: asoc:%p, timeo:%ld, msg_len:%zu\n", __func__, asoc,
*timeo_p, msg_len); *timeo_p, msg_len);
...@@ -7008,17 +7008,13 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, ...@@ -7008,17 +7008,13 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
release_sock(sk); release_sock(sk);
current_timeo = schedule_timeout(current_timeo); current_timeo = schedule_timeout(current_timeo);
lock_sock(sk); lock_sock(sk);
if (sk != asoc->base.sk) { if (sk != asoc->base.sk)
release_sock(sk); goto do_error;
sk = asoc->base.sk;
lock_sock(sk);
}
*timeo_p = current_timeo; *timeo_p = current_timeo;
} }
out: out:
*orig_sk = sk;
finish_wait(&asoc->wait, &wait); finish_wait(&asoc->wait, &wait);
/* Release the association's refcnt. */ /* Release the association's refcnt. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment