aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXin Long <lucien.xin@gmail.com>2018-01-15 04:01:36 -0500
committerDavid S. Miller <davem@davemloft.net>2018-01-16 14:22:51 -0500
commita0ff660058b88d12625a783ce9e5c1371c87951f (patch)
tree2a4b0dc2da7c65711221ad99347f969bcf0edcdd
parent625637bf4afa45204bd87e4218645182a919485a (diff)
sctp: return error if the asoc has been peeled off in sctp_wait_for_sndbuf
After commit cea0cc80a677 ("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: cea0cc80a677 ("sctp: use the right sk after waking up from wait_buf sleep") Reported-by: syzbot+ac6ea7baa4432811eb50@syzkaller.appspotmail.com Signed-off-by: Xin Long <lucien.xin@gmail.com> Acked-by: Neil Horman <nhorman@tuxdriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/sctp/socket.c16
1 files changed, 6 insertions, 10 deletions
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 15ae018b386f..feb2ca69827a 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -85,7 +85,7 @@
85static int sctp_writeable(struct sock *sk); 85static int sctp_writeable(struct sock *sk);
86static void sctp_wfree(struct sk_buff *skb); 86static void sctp_wfree(struct sk_buff *skb);
87static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, 87static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
88 size_t msg_len, struct sock **orig_sk); 88 size_t msg_len);
89static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p); 89static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p);
90static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p); 90static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);
91static int sctp_wait_for_accept(struct sock *sk, long timeo); 91static int sctp_wait_for_accept(struct sock *sk, long timeo);
@@ -1977,7 +1977,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
1977 timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); 1977 timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
1978 if (!sctp_wspace(asoc)) { 1978 if (!sctp_wspace(asoc)) {
1979 /* sk can be changed by peel off when waiting for buf. */ 1979 /* sk can be changed by peel off when waiting for buf. */
1980 err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len, &sk); 1980 err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
1981 if (err) { 1981 if (err) {
1982 if (err == -ESRCH) { 1982 if (err == -ESRCH) {
1983 /* asoc is already dead. */ 1983 /* asoc is already dead. */
@@ -8022,12 +8022,12 @@ void sctp_sock_rfree(struct sk_buff *skb)
8022 8022
8023/* Helper function to wait for space in the sndbuf. */ 8023/* Helper function to wait for space in the sndbuf. */
8024static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, 8024static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
8025 size_t msg_len, struct sock **orig_sk) 8025 size_t msg_len)
8026{ 8026{
8027 struct sock *sk = asoc->base.sk; 8027 struct sock *sk = asoc->base.sk;
8028 int err = 0;
8029 long current_timeo = *timeo_p; 8028 long current_timeo = *timeo_p;
8030 DEFINE_WAIT(wait); 8029 DEFINE_WAIT(wait);
8030 int err = 0;
8031 8031
8032 pr_debug("%s: asoc:%p, timeo:%ld, msg_len:%zu\n", __func__, asoc, 8032 pr_debug("%s: asoc:%p, timeo:%ld, msg_len:%zu\n", __func__, asoc,
8033 *timeo_p, msg_len); 8033 *timeo_p, msg_len);
@@ -8056,17 +8056,13 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
8056 release_sock(sk); 8056 release_sock(sk);
8057 current_timeo = schedule_timeout(current_timeo); 8057 current_timeo = schedule_timeout(current_timeo);
8058 lock_sock(sk); 8058 lock_sock(sk);
8059 if (sk != asoc->base.sk) { 8059 if (sk != asoc->base.sk)
8060 release_sock(sk); 8060 goto do_error;
8061 sk = asoc->base.sk;
8062 lock_sock(sk);
8063 }
8064 8061
8065 *timeo_p = current_timeo; 8062 *timeo_p = current_timeo;
8066 } 8063 }
8067 8064
8068out: 8065out:
8069 *orig_sk = sk;
8070 finish_wait(&asoc->wait, &wait); 8066 finish_wait(&asoc->wait, &wait);
8071 8067
8072 /* Release the association's refcnt. */ 8068 /* Release the association's refcnt. */