From: Xin Long <lucien.xin@gmail.com> Date: 2017-11-13 04:44:00
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.
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
net/sctp/socket.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
@@ -7822,7 +7822,7 @@ void sctp_sock_rfree(struct sk_buff *skb)/* Helper function to wait for space in the sndbuf. */staticintsctp_wait_for_sndbuf(structsctp_association*asoc,long*timeo_p,-size_tmsg_len)+size_tmsg_len,structsctp_association**new){structsock*sk=asoc->base.sk;interr=0;
@@ -7839,10 +7839,13 @@ 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){+*new=NULL;+gotodo_error;+}if(!*timeo_p)gotodo_nonblock;-if(sk->sk_err||asoc->state>=SCTP_STATE_SHUTDOWN_PENDING||-asoc->base.dead)+if(sk->sk_err||asoc->state>=SCTP_STATE_SHUTDOWN_PENDING)gotodo_error;if(signal_pending(current))gotodo_interrupted;
From: Neil Horman <nhorman@tuxdriver.com> Date: 2017-11-13 14:47:24
On Mon, Nov 13, 2017 at 12:43:50PM +0800, Xin Long wrote:
quoted hunk
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.
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
net/sctp/socket.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
@@ -7822,7 +7822,7 @@ void sctp_sock_rfree(struct sk_buff *skb)/* Helper function to wait for space in the sndbuf. */staticintsctp_wait_for_sndbuf(structsctp_association*asoc,long*timeo_p,-size_tmsg_len)+size_tmsg_len,structsctp_association**new){structsock*sk=asoc->base.sk;interr=0;
@@ -7839,10 +7839,13 @@ 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){+*new=NULL;+gotodo_error;+}if(!*timeo_p)gotodo_nonblock;-if(sk->sk_err||asoc->state>=SCTP_STATE_SHUTDOWN_PENDING||-asoc->base.dead)+if(sk->sk_err||asoc->state>=SCTP_STATE_SHUTDOWN_PENDING)gotodo_error;if(signal_pending(current))gotodo_interrupted;
--
2.1.0
Why pass a pointer to a pointer into the wait function? It seems like you could
just check the return code for err == -EPIPE and set the association to null in
sctp_sendmsg. That would avoid passing another parameter, and cut down on some
complexity here.
Neil
From: Xin Long <lucien.xin@gmail.com> Date: 2017-11-13 15:29:51
On Mon, Nov 13, 2017 at 10:46 PM, Neil Horman [off-list ref] wrote:
On Mon, Nov 13, 2017 at 12:43:50PM +0800, Xin Long wrote:
quoted
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.
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
net/sctp/socket.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
@@ -7822,7 +7822,7 @@ void sctp_sock_rfree(struct sk_buff *skb)/* Helper function to wait for space in the sndbuf. */staticintsctp_wait_for_sndbuf(structsctp_association*asoc,long*timeo_p,-size_tmsg_len)+size_tmsg_len,structsctp_association**new){structsock*sk=asoc->base.sk;interr=0;
@@ -7839,10 +7839,13 @@ 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){+*new=NULL;+gotodo_error;+}if(!*timeo_p)gotodo_nonblock;-if(sk->sk_err||asoc->state>=SCTP_STATE_SHUTDOWN_PENDING||-asoc->base.dead)+if(sk->sk_err||asoc->state>=SCTP_STATE_SHUTDOWN_PENDING)gotodo_error;if(signal_pending(current))gotodo_interrupted;--
2.1.0
Why pass a pointer to a pointer into the wait function? It seems like you could
just check the return code for err == -EPIPE and set the association to null in
sctp_sendmsg. That would avoid passing another parameter, and cut down on some
complexity here.
"if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING)"
also goes to err == -EPIPE path, with it, the new_asoc are supposed to
be freed in old codes.
do you think it's good to not free it now when sk->sk_err or SHUTDOWN_PENDING ?
or I can add another err path like:
+do_dead:
+ err = -ESRCH;
+ goto out;
From: Neil Horman <nhorman@tuxdriver.com> Date: 2017-11-14 12:43:40
On Mon, Nov 13, 2017 at 11:29:49PM +0800, Xin Long wrote:
On Mon, Nov 13, 2017 at 10:46 PM, Neil Horman [off-list ref] wrote:
quoted
On Mon, Nov 13, 2017 at 12:43:50PM +0800, Xin Long wrote:
quoted
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.
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
net/sctp/socket.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
@@ -7822,7 +7822,7 @@ void sctp_sock_rfree(struct sk_buff *skb)/* Helper function to wait for space in the sndbuf. */staticintsctp_wait_for_sndbuf(structsctp_association*asoc,long*timeo_p,-size_tmsg_len)+size_tmsg_len,structsctp_association**new){structsock*sk=asoc->base.sk;interr=0;
@@ -7839,10 +7839,13 @@ 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){+*new=NULL;+gotodo_error;+}if(!*timeo_p)gotodo_nonblock;-if(sk->sk_err||asoc->state>=SCTP_STATE_SHUTDOWN_PENDING||-asoc->base.dead)+if(sk->sk_err||asoc->state>=SCTP_STATE_SHUTDOWN_PENDING)gotodo_error;if(signal_pending(current))gotodo_interrupted;--
2.1.0
Why pass a pointer to a pointer into the wait function? It seems like you could
just check the return code for err == -EPIPE and set the association to null in
sctp_sendmsg. That would avoid passing another parameter, and cut down on some
complexity here.
"if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING)"
also goes to err == -EPIPE path, with it, the new_asoc are supposed to
be freed in old codes.
do you think it's good to not free it now when sk->sk_err or SHUTDOWN_PENDING ?
or I can add another err path like:
+do_dead:
+ err = -ESRCH;
+ goto out;
That would work, but it also seems just as easy to check in sctp_sendmsg. that
is to say, if sctp_wait_for_sndbuf return -EPIPE sctp_sndmsg jumps to out_free,
where we can check to see if (new_assoc && new_assoc->base.dead) as a gating
factor on free.
Neil
From: Xin Long <lucien.xin@gmail.com> Date: 2017-11-14 12:52:57
On Tue, Nov 14, 2017 at 8:43 PM, Neil Horman [off-list ref] wrote:
On Mon, Nov 13, 2017 at 11:29:49PM +0800, Xin Long wrote:
quoted
On Mon, Nov 13, 2017 at 10:46 PM, Neil Horman [off-list ref] wrote:
quoted
On Mon, Nov 13, 2017 at 12:43:50PM +0800, Xin Long wrote:
quoted
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.
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
net/sctp/socket.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
@@ -7822,7 +7822,7 @@ void sctp_sock_rfree(struct sk_buff *skb)/* Helper function to wait for space in the sndbuf. */staticintsctp_wait_for_sndbuf(structsctp_association*asoc,long*timeo_p,-size_tmsg_len)+size_tmsg_len,structsctp_association**new){structsock*sk=asoc->base.sk;interr=0;
@@ -7839,10 +7839,13 @@ 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){+*new=NULL;+gotodo_error;+}if(!*timeo_p)gotodo_nonblock;-if(sk->sk_err||asoc->state>=SCTP_STATE_SHUTDOWN_PENDING||-asoc->base.dead)+if(sk->sk_err||asoc->state>=SCTP_STATE_SHUTDOWN_PENDING)gotodo_error;if(signal_pending(current))gotodo_interrupted;--
2.1.0
Why pass a pointer to a pointer into the wait function? It seems like you could
just check the return code for err == -EPIPE and set the association to null in
sctp_sendmsg. That would avoid passing another parameter, and cut down on some
complexity here.
"if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING)"
also goes to err == -EPIPE path, with it, the new_asoc are supposed to
be freed in old codes.
do you think it's good to not free it now when sk->sk_err or SHUTDOWN_PENDING ?
or I can add another err path like:
+do_dead:
+ err = -ESRCH;
+ goto out;
That would work, but it also seems just as easy to check in sctp_sendmsg. that
is to say, if sctp_wait_for_sndbuf return -EPIPE sctp_sndmsg jumps to out_free,
where we can check to see if (new_assoc && new_assoc->base.dead) as a gating
factor on free.
we can't do that, as nowhere holds new_assoc, it may be already destroyed.
to dereference it (new_assoc->base.dead) will cause use-after-free panic.
I may do like this:
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;
+ }
agree ?
From: Neil Horman <nhorman@tuxdriver.com> Date: 2017-11-14 18:19:27
On Tue, Nov 14, 2017 at 08:52:56PM +0800, Xin Long wrote:
On Tue, Nov 14, 2017 at 8:43 PM, Neil Horman [off-list ref] wrote:
quoted
On Mon, Nov 13, 2017 at 11:29:49PM +0800, Xin Long wrote:
quoted
On Mon, Nov 13, 2017 at 10:46 PM, Neil Horman [off-list ref] wrote:
quoted
On Mon, Nov 13, 2017 at 12:43:50PM +0800, Xin Long wrote:
quoted
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.
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
net/sctp/socket.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
@@ -7822,7 +7822,7 @@ void sctp_sock_rfree(struct sk_buff *skb)/* Helper function to wait for space in the sndbuf. */staticintsctp_wait_for_sndbuf(structsctp_association*asoc,long*timeo_p,-size_tmsg_len)+size_tmsg_len,structsctp_association**new){structsock*sk=asoc->base.sk;interr=0;
@@ -7839,10 +7839,13 @@ 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){+*new=NULL;+gotodo_error;+}if(!*timeo_p)gotodo_nonblock;-if(sk->sk_err||asoc->state>=SCTP_STATE_SHUTDOWN_PENDING||-asoc->base.dead)+if(sk->sk_err||asoc->state>=SCTP_STATE_SHUTDOWN_PENDING)gotodo_error;if(signal_pending(current))gotodo_interrupted;--
2.1.0
Why pass a pointer to a pointer into the wait function? It seems like you could
just check the return code for err == -EPIPE and set the association to null in
sctp_sendmsg. That would avoid passing another parameter, and cut down on some
complexity here.
"if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING)"
also goes to err == -EPIPE path, with it, the new_asoc are supposed to
be freed in old codes.
do you think it's good to not free it now when sk->sk_err or SHUTDOWN_PENDING ?
or I can add another err path like:
+do_dead:
+ err = -ESRCH;
+ goto out;
That would work, but it also seems just as easy to check in sctp_sendmsg. that
is to say, if sctp_wait_for_sndbuf return -EPIPE sctp_sndmsg jumps to out_free,
where we can check to see if (new_assoc && new_assoc->base.dead) as a gating
factor on free.
we can't do that, as nowhere holds new_assoc, it may be already destroyed.
to dereference it (new_assoc->base.dead) will cause use-after-free panic.
I may do like this:
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;
+ }
agree ?