aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGordon Tetlow <gordon@FreeBSD.org>2020-09-02 16:24:32 +0000
committerGordon Tetlow <gordon@FreeBSD.org>2020-09-02 16:24:32 +0000
commitca789358b9ffc57ad56f7f0b0a50746f45581d4d (patch)
tree461fa8be28a1e9ccc3364f6415dba01673cba3f5
parent6bf1f3a72341aec2f7bf4b89c46e0734c6564ffa (diff)
downloadsrc-ca789358b9ffc57ad56f7f0b0a50746f45581d4d.tar.gz
src-ca789358b9ffc57ad56f7f0b0a50746f45581d4d.zip
Fix SCTP socket use-after-free.
Approved by: so Security: FreeBSD-SA-20:25.sctp Security: CVE-2020-7463
Notes
Notes: svn path=/releng/11.4/; revision=365256
-rw-r--r--sys/netinet/sctp_input.c3
-rw-r--r--sys/netinet/sctp_output.c49
-rw-r--r--sys/netinet/sctp_pcb.c9
-rw-r--r--sys/netinet/sctp_structs.h1
-rw-r--r--sys/netinet/sctputil.c17
-rw-r--r--sys/netinet/sctputil.h2
6 files changed, 56 insertions, 25 deletions
diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c
index c8facafa42b3..dac771d2ba6e 100644
--- a/sys/netinet/sctp_input.c
+++ b/sys/netinet/sctp_input.c
@@ -846,7 +846,6 @@ sctp_handle_abort(struct sctp_abort_chunk *abort,
SCTP_TCB_LOCK(stcb);
atomic_subtract_int(&stcb->asoc.refcnt, 1);
#endif
- SCTP_ADD_SUBSTATE(stcb, SCTP_STATE_WAS_ABORTED);
(void)sctp_free_assoc(stcb->sctp_ep, stcb, SCTP_NORMAL_PROC,
SCTP_FROM_SCTP_INPUT + SCTP_LOC_8);
#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
@@ -1995,7 +1994,7 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
/* send up all the data */
SCTP_TCB_SEND_LOCK(stcb);
- sctp_report_all_outbound(stcb, 0, 1, SCTP_SO_LOCKED);
+ sctp_report_all_outbound(stcb, 0, SCTP_SO_LOCKED);
for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
stcb->asoc.strmout[i].chunks_on_queues = 0;
#if defined(SCTP_DETAILED_STR_STATS)
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index e91141c90193..78ea61699987 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -13202,11 +13202,10 @@ skip_preblock:
error = EINVAL;
goto out;
}
- SCTP_TCB_SEND_UNLOCK(stcb);
-
strm = &stcb->asoc.strmout[srcv->sinfo_stream];
if (strm->last_msg_incomplete == 0) {
do_a_copy_in:
+ SCTP_TCB_SEND_UNLOCK(stcb);
sp = sctp_copy_it_in(stcb, asoc, srcv, uio, net, max_len, user_marks_eor, &error);
if (error) {
goto out;
@@ -13232,13 +13231,11 @@ skip_preblock:
if (sinfo_flags & SCTP_UNORDERED) {
SCTP_STAT_INCR(sctps_sends_with_unord);
}
+ sp->processing = 1;
TAILQ_INSERT_TAIL(&strm->outqueue, sp, next);
stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, asoc, strm, sp, 1);
- SCTP_TCB_SEND_UNLOCK(stcb);
} else {
- SCTP_TCB_SEND_LOCK(stcb);
sp = TAILQ_LAST(&strm->outqueue, sctp_streamhead);
- SCTP_TCB_SEND_UNLOCK(stcb);
if (sp == NULL) {
/* ???? Huh ??? last msg is gone */
#ifdef INVARIANTS
@@ -13250,7 +13247,16 @@ skip_preblock:
goto do_a_copy_in;
}
+ if (sp->processing) {
+ SCTP_TCB_SEND_UNLOCK(stcb);
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
+ error = EINVAL;
+ goto out;
+ } else {
+ sp->processing = 1;
+ }
}
+ SCTP_TCB_SEND_UNLOCK(stcb);
while (uio->uio_resid > 0) {
/* How much room do we have? */
struct mbuf *new_tail, *mm;
@@ -13275,20 +13281,29 @@ skip_preblock:
if (mm) {
sctp_m_freem(mm);
}
+ SCTP_TCB_SEND_LOCK(stcb);
+ if (sp != NULL) {
+ sp->processing = 0;
+ }
+ SCTP_TCB_SEND_UNLOCK(stcb);
goto out;
}
/* Update the mbuf and count */
SCTP_TCB_SEND_LOCK(stcb);
- if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
+ if ((stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) ||
+ (stcb->asoc.state & SCTP_STATE_WAS_ABORTED)) {
/*
* we need to get out. Peer probably
* aborted.
*/
sctp_m_freem(mm);
- if (stcb->asoc.state & SCTP_PCB_FLAGS_WAS_ABORTED) {
+ if (stcb->asoc.state & SCTP_STATE_WAS_ABORTED) {
SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ECONNRESET);
error = ECONNRESET;
}
+ if (sp != NULL) {
+ sp->processing = 0;
+ }
SCTP_TCB_SEND_UNLOCK(stcb);
goto out;
}
@@ -13348,6 +13363,11 @@ skip_preblock:
/* wait for space now */
if (non_blocking) {
/* Non-blocking io in place out */
+ SCTP_TCB_SEND_LOCK(stcb);
+ if (sp != NULL) {
+ sp->processing = 0;
+ }
+ SCTP_TCB_SEND_UNLOCK(stcb);
goto skip_out_eof;
}
/* What about the INIT, send it maybe */
@@ -13471,6 +13491,11 @@ skip_preblock:
}
}
SOCKBUF_UNLOCK(&so->so_snd);
+ SCTP_TCB_SEND_LOCK(stcb);
+ if (sp != NULL) {
+ sp->processing = 0;
+ }
+ SCTP_TCB_SEND_UNLOCK(stcb);
goto out_unlocked;
}
@@ -13480,12 +13505,19 @@ skip_preblock:
}
}
SOCKBUF_UNLOCK(&so->so_snd);
+ SCTP_TCB_SEND_LOCK(stcb);
if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
+ if (sp != NULL) {
+ sp->processing = 0;
+ }
+ SCTP_TCB_SEND_UNLOCK(stcb);
goto out_unlocked;
}
+ SCTP_TCB_SEND_UNLOCK(stcb);
}
SCTP_TCB_SEND_LOCK(stcb);
- if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
+ if ((stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) ||
+ (stcb->asoc.state & SCTP_STATE_WAS_ABORTED)) {
SCTP_TCB_SEND_UNLOCK(stcb);
goto out_unlocked;
}
@@ -13501,6 +13533,7 @@ skip_preblock:
strm->last_msg_incomplete = 0;
asoc->stream_locked = 0;
}
+ sp->processing = 0;
} else {
SCTP_PRINTF("Huh no sp TSNH?\n");
strm->last_msg_incomplete = 0;
diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c
index b82ebedcffe1..48f5c6f3e004 100644
--- a/sys/netinet/sctp_pcb.c
+++ b/sys/netinet/sctp_pcb.c
@@ -4725,6 +4725,7 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
/* there is no asoc, really TSNH :-0 */
return (1);
}
+ SCTP_TCB_SEND_LOCK(stcb);
if (stcb->asoc.alternate) {
sctp_free_remote_addr(stcb->asoc.alternate);
stcb->asoc.alternate = NULL;
@@ -4759,6 +4760,7 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
/* nope, reader or writer in the way */
sctp_timer_start(SCTP_TIMER_TYPE_ASOCKILL, inp, stcb, NULL);
/* no asoc destroyed */
+ SCTP_TCB_SEND_UNLOCK(stcb);
SCTP_TCB_UNLOCK(stcb);
#ifdef SCTP_LOG_CLOSING
sctp_log_closing(inp, stcb, 8);
@@ -4827,6 +4829,7 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
SCTP_CLEAR_SUBSTATE(stcb, SCTP_STATE_IN_ACCEPT_QUEUE);
sctp_timer_start(SCTP_TIMER_TYPE_ASOCKILL, inp, stcb, NULL);
}
+ SCTP_TCB_SEND_UNLOCK(stcb);
SCTP_TCB_UNLOCK(stcb);
if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) ||
(inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE))
@@ -4860,10 +4863,12 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
if (from_inpcbfree == SCTP_NORMAL_PROC) {
atomic_add_int(&stcb->asoc.refcnt, 1);
+ SCTP_TCB_SEND_UNLOCK(stcb);
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_INFO_WLOCK();
SCTP_INP_WLOCK(inp);
SCTP_TCB_LOCK(stcb);
+ SCTP_TCB_SEND_LOCK(stcb);
}
/* Double check the GONE flag */
if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) ||
@@ -4911,6 +4916,7 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
SCTP_INP_INFO_WUNLOCK();
SCTP_INP_WUNLOCK(inp);
}
+ SCTP_TCB_SEND_UNLOCK(stcb);
SCTP_TCB_UNLOCK(stcb);
return (0);
}
@@ -4942,7 +4948,6 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
* in case.
*/
/* anything on the wheel needs to be removed */
- SCTP_TCB_SEND_LOCK(stcb);
for (i = 0; i < asoc->streamoutcnt; i++) {
struct sctp_stream_out *outs;
@@ -4973,7 +4978,6 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
sctp_free_a_strmoq(stcb, sp, SCTP_SO_LOCKED);
}
}
- SCTP_TCB_SEND_UNLOCK(stcb);
/* sa_ignore FREED_MEMORY */
TAILQ_FOREACH_SAFE(strrst, &asoc->resetHead, next_resp, nstrrst) {
TAILQ_REMOVE(&asoc->resetHead, strrst, next_resp);
@@ -5175,6 +5179,7 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
/* Insert new items here :> */
/* Get rid of LOCK */
+ SCTP_TCB_SEND_UNLOCK(stcb);
SCTP_TCB_UNLOCK(stcb);
SCTP_TCB_LOCK_DESTROY(stcb);
SCTP_TCB_SEND_LOCK_DESTROY(stcb);
diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h
index 1c0c3f739f72..0b527db4f4d0 100644
--- a/sys/netinet/sctp_structs.h
+++ b/sys/netinet/sctp_structs.h
@@ -535,6 +535,7 @@ struct sctp_stream_queue_pending {
uint8_t sender_all_done;
uint8_t put_last_out;
uint8_t discard_rest;
+ uint8_t processing;
};
/*
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index de84d3bbe3c6..2f6263e35988 100644
--- a/sys/netinet/sctputil.c
+++ b/sys/netinet/sctputil.c
@@ -3921,7 +3921,7 @@ sctp_ulp_notify(uint32_t notification, struct sctp_tcb *stcb,
}
void
-sctp_report_all_outbound(struct sctp_tcb *stcb, uint16_t error, int holds_lock, int so_locked
+sctp_report_all_outbound(struct sctp_tcb *stcb, uint16_t error, int so_locked
#if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING)
SCTP_UNUSED
#endif
@@ -3947,9 +3947,6 @@ sctp_report_all_outbound(struct sctp_tcb *stcb, uint16_t error, int holds_lock,
return;
}
/* now through all the gunk freeing chunks */
- if (holds_lock == 0) {
- SCTP_TCB_SEND_LOCK(stcb);
- }
/* sent queue SHOULD be empty */
TAILQ_FOREACH_SAFE(chk, &asoc->sent_queue, sctp_next, nchk) {
TAILQ_REMOVE(&asoc->sent_queue, chk, sctp_next);
@@ -4026,10 +4023,6 @@ sctp_report_all_outbound(struct sctp_tcb *stcb, uint16_t error, int holds_lock,
/* sa_ignore FREED_MEMORY */
}
}
-
- if (holds_lock == 0) {
- SCTP_TCB_SEND_UNLOCK(stcb);
- }
}
void
@@ -4053,8 +4046,11 @@ sctp_abort_notification(struct sctp_tcb *stcb, uint8_t from_peer, uint16_t error
(stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET)) {
return;
}
+ SCTP_TCB_SEND_LOCK(stcb);
+ SCTP_ADD_SUBSTATE(stcb, SCTP_STATE_WAS_ABORTED);
/* Tell them we lost the asoc */
- sctp_report_all_outbound(stcb, error, 0, so_locked);
+ sctp_report_all_outbound(stcb, error, so_locked);
+ SCTP_TCB_SEND_UNLOCK(stcb);
if (from_peer) {
sctp_ulp_notify(SCTP_NOTIFY_ASSOC_REM_ABORTED, stcb, error, abort, so_locked);
} else {
@@ -4086,7 +4082,6 @@ sctp_abort_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
if (stcb != NULL) {
/* We have a TCB to abort, send notification too */
sctp_abort_notification(stcb, 0, 0, NULL, SCTP_SO_NOT_LOCKED);
- SCTP_ADD_SUBSTATE(stcb, SCTP_STATE_WAS_ABORTED);
/* Ok, now lets free it */
#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
so = SCTP_INP_SO(inp);
@@ -4196,8 +4191,6 @@ sctp_abort_an_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
}
}
return;
- } else {
- SCTP_ADD_SUBSTATE(stcb, SCTP_STATE_WAS_ABORTED);
}
/* notify the peer */
sctp_send_abort_tcb(stcb, op_err, so_locked);
diff --git a/sys/netinet/sctputil.h b/sys/netinet/sctputil.h
index fc5512caefbc..318fbb143aec 100644
--- a/sys/netinet/sctputil.h
+++ b/sys/netinet/sctputil.h
@@ -166,7 +166,7 @@ void sctp_stop_timers_for_shutdown(struct sctp_tcb *);
void sctp_stop_association_timers(struct sctp_tcb *, bool);
void
-sctp_report_all_outbound(struct sctp_tcb *, uint16_t, int, int
+sctp_report_all_outbound(struct sctp_tcb *, uint16_t, int
#if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING)
SCTP_UNUSED
#endif