| From dfcb9f4f99f1e9a49e43398a7bfbf56927544af1 Mon Sep 17 00:00:00 2001 |
| From: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> |
| Date: Thu, 23 Feb 2017 09:31:18 -0300 |
| Subject: [PATCH] sctp: deny peeloff operation on asocs with threads sleeping |
| on it |
| |
| commit dfcb9f4f99f1e9a49e43398a7bfbf56927544af1 upstream. |
| |
| commit 2dcab5984841 ("sctp: avoid BUG_ON on sctp_wait_for_sndbuf") |
| attempted to avoid a BUG_ON call when the association being used for a |
| sendmsg() is blocked waiting for more sndbuf and another thread did a |
| peeloff operation on such asoc, moving it to another socket. |
| |
| As Ben Hutchings noticed, then in such case it would return without |
| locking back the socket and would cause two unlocks in a row. |
| |
| Further analysis also revealed that it could allow a double free if the |
| application managed to peeloff the asoc that is created during the |
| sendmsg call, because then sctp_sendmsg() would try to free the asoc |
| that was created only for that call. |
| |
| This patch takes another approach. It will deny the peeloff operation |
| if there is a thread sleeping on the asoc, so this situation doesn't |
| exist anymore. This avoids the issues described above and also honors |
| the syscalls that are already being handled (it can be multiple sendmsg |
| calls). |
| |
| Joint work with Xin Long. |
| |
| Fixes: 2dcab5984841 ("sctp: avoid BUG_ON on sctp_wait_for_sndbuf") |
| Cc: Alexander Popov <alex.popov@linux.com> |
| Cc: Ben Hutchings <ben@decadent.org.uk> |
| Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> |
| Signed-off-by: Xin Long <lucien.xin@gmail.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| |
| diff --git a/net/sctp/socket.c b/net/sctp/socket.c |
| index b5321486fbed..465a9c8464f9 100644 |
| --- a/net/sctp/socket.c |
| +++ b/net/sctp/socket.c |
| @@ -4862,6 +4862,12 @@ int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp) |
| if (!asoc) |
| return -EINVAL; |
| |
| + /* If there is a thread waiting on more sndbuf space for |
| + * sending on this asoc, it cannot be peeled. |
| + */ |
| + if (waitqueue_active(&asoc->wait)) |
| + return -EBUSY; |
| + |
| /* An association cannot be branched off from an already peeled-off |
| * socket, nor is this supported for tcp style sockets. |
| */ |
| @@ -7599,8 +7605,6 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, |
| */ |
| release_sock(sk); |
| current_timeo = schedule_timeout(current_timeo); |
| - if (sk != asoc->base.sk) |
| - goto do_error; |
| lock_sock(sk); |
| |
| *timeo_p = current_timeo; |
| -- |
| 2.12.0 |
| |