diff mbox series

[PATCHv2,net] sctp: do not free asoc when it is already dead in sctp_sendmsg

Message ID a374f3840d50382fa15a3e8909b38855aa133959.1510736154.git.lucien.xin@gmail.com
State Accepted, archived
Delegated to: David Miller
Headers show
Series [PATCHv2,net] sctp: do not free asoc when it is already dead in sctp_sendmsg | expand

Commit Message

Xin Long Nov. 15, 2017, 8:55 a.m. UTC
Now in sctp_sendmsg sctp_wait_for_sndbuf could schedule out without
holding sock sk. It means the current asoc can be freed elsewhere,
like when receiving an abort packet.

If the asoc is just created in sctp_sendmsg and sctp_wait_for_sndbuf
returns err, the asoc will be freed again due to new_asoc is not nil.
An use-after-free issue would be triggered by this.

This patch is to fix it by setting new_asoc with nil if the asoc is
already dead when cpu schedules back, so that it will not be freed
again in sctp_sendmsg.

v1->v2:
  set new_asoc as nil in sctp_sendmsg instead of sctp_wait_for_sndbuf.

Suggested-by: Neil Horman <nhorman@tuxdriver.com>
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
 net/sctp/socket.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

Comments

Neil Horman Nov. 15, 2017, 12:42 p.m. UTC | #1
On Wed, Nov 15, 2017 at 04:55:54PM +0800, Xin Long wrote:
> Now in sctp_sendmsg sctp_wait_for_sndbuf could schedule out without
> holding sock sk. It means the current asoc can be freed elsewhere,
> like when receiving an abort packet.
> 
> If the asoc is just created in sctp_sendmsg and sctp_wait_for_sndbuf
> returns err, the asoc will be freed again due to new_asoc is not nil.
> An use-after-free issue would be triggered by this.
> 
> This patch is to fix it by setting new_asoc with nil if the asoc is
> already dead when cpu schedules back, so that it will not be freed
> again in sctp_sendmsg.
> 
> v1->v2:
>   set new_asoc as nil in sctp_sendmsg instead of sctp_wait_for_sndbuf.
> 
> Suggested-by: Neil Horman <nhorman@tuxdriver.com>
> Reported-by: Dmitry Vyukov <dvyukov@google.com>
> Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
David Miller Nov. 16, 2017, 1:49 a.m. UTC | #2
From: Xin Long <lucien.xin@gmail.com>
Date: Wed, 15 Nov 2017 16:55:54 +0800

> Now in sctp_sendmsg sctp_wait_for_sndbuf could schedule out without
> holding sock sk. It means the current asoc can be freed elsewhere,
> like when receiving an abort packet.
> 
> If the asoc is just created in sctp_sendmsg and sctp_wait_for_sndbuf
> returns err, the asoc will be freed again due to new_asoc is not nil.
> An use-after-free issue would be triggered by this.
> 
> This patch is to fix it by setting new_asoc with nil if the asoc is
> already dead when cpu schedules back, so that it will not be freed
> again in sctp_sendmsg.
> 
> v1->v2:
>   set new_asoc as nil in sctp_sendmsg instead of sctp_wait_for_sndbuf.
> 
> Suggested-by: Neil Horman <nhorman@tuxdriver.com>
> Reported-by: Dmitry Vyukov <dvyukov@google.com>
> Signed-off-by: Xin Long <lucien.xin@gmail.com>

Applied.
diff mbox series

Patch

diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 6f45d17..e62251f 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -1963,8 +1963,14 @@  static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
 	timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
 	if (!sctp_wspace(asoc)) {
 		err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
-		if (err)
+		if (err) {
+			if (err == -ESRCH) {
+				/* asoc is already dead. */
+				new_asoc = NULL;
+				err = -EPIPE;
+			}
 			goto out_free;
+		}
 	}
 
 	/* If an address is passed with the sendto/sendmsg call, it is used
@@ -7839,10 +7845,11 @@  static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
 	for (;;) {
 		prepare_to_wait_exclusive(&asoc->wait, &wait,
 					  TASK_INTERRUPTIBLE);
+		if (asoc->base.dead)
+			goto do_dead;
 		if (!*timeo_p)
 			goto do_nonblock;
-		if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING ||
-		    asoc->base.dead)
+		if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING)
 			goto do_error;
 		if (signal_pending(current))
 			goto do_interrupted;
@@ -7867,6 +7874,10 @@  static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
 
 	return err;
 
+do_dead:
+	err = -ESRCH;
+	goto out;
+
 do_error:
 	err = -EPIPE;
 	goto out;