Fix SCTP socket use-after-free.
Approved by: so Security: FreeBSD-SA-20:25.sctp Security: CVE-2020-7463
This commit is contained in:
parent
db60b3ee85
commit
47b536fd85
|
@ -841,7 +841,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)
|
||||
|
@ -1987,7 +1986,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)
|
||||
|
|
|
@ -13207,11 +13207,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;
|
||||
|
@ -13237,13 +13236,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
|
||||
|
@ -13255,7 +13252,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;
|
||||
|
@ -13280,20 +13286,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;
|
||||
}
|
||||
|
@ -13353,6 +13368,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 */
|
||||
|
@ -13476,6 +13496,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;
|
||||
}
|
||||
|
||||
|
@ -13485,12 +13510,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;
|
||||
}
|
||||
|
@ -13506,6 +13538,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;
|
||||
|
|
|
@ -4726,6 +4726,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;
|
||||
|
@ -4760,6 +4761,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);
|
||||
|
@ -4857,6 +4859,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))
|
||||
|
@ -4890,10 +4893,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) ||
|
||||
|
@ -4941,6 +4946,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);
|
||||
}
|
||||
|
@ -4984,7 +4990,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;
|
||||
|
||||
|
@ -5015,7 +5020,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);
|
||||
|
@ -5217,6 +5221,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);
|
||||
|
|
|
@ -537,6 +537,7 @@ struct sctp_stream_queue_pending {
|
|||
uint8_t sender_all_done;
|
||||
uint8_t put_last_out;
|
||||
uint8_t discard_rest;
|
||||
uint8_t processing;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -3870,7 +3870,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
|
||||
|
@ -3896,9 +3896,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);
|
||||
|
@ -3975,10 +3972,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
|
||||
|
@ -4002,8 +3995,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 {
|
||||
|
@ -4035,7 +4031,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);
|
||||
|
@ -4145,8 +4140,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);
|
||||
|
|
|
@ -165,7 +165,7 @@ sctp_pull_off_control_to_new_inp(struct sctp_inpcb *old_inp,
|
|||
void sctp_stop_timers_for_shutdown(struct sctp_tcb *);
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue