Fix the handling of buffered messages in stream reset deferred handling.

Thanks to Eugen-Andrei Gavriloaie for reporting the issue and providing
substantial help in nailing down the issue.

MFC after:	1 week
This commit is contained in:
Michael Tuexen 2016-12-17 22:31:30 +00:00
parent 660c1ea0b4
commit 3d6fe5d84c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=310193

View File

@ -472,7 +472,6 @@ sctp_clean_up_control(struct sctp_tcb *stcb, struct sctp_queued_to_read *control
*/
static void
sctp_queue_data_to_stream(struct sctp_tcb *stcb,
struct sctp_stream_in *strm,
struct sctp_association *asoc,
struct sctp_queued_to_read *control, int *abort_flag, int *need_reasm)
{
@ -498,16 +497,17 @@ sctp_queue_data_to_stream(struct sctp_tcb *stcb,
int queue_needed;
uint32_t nxt_todel;
struct mbuf *op_err;
struct sctp_stream_in *strm;
char msg[SCTP_DIAG_INFO_LEN];
strm = &asoc->strmin[control->sinfo_stream];
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_STR_LOGGING_ENABLE) {
sctp_log_strm_del(control, NULL, SCTP_STR_LOG_FROM_INTO_STRD);
}
if (SCTP_MID_GT((asoc->idata_supported), strm->last_mid_delivered, control->mid)) {
/* The incoming sseq is behind where we last delivered? */
SCTPDBG(SCTP_DEBUG_INDATA1, "Duplicate S-SEQ: %u delivered: %u from peer, Abort association\n",
control->mid, strm->last_mid_delivered);
protocol_error:
strm->last_mid_delivered, control->mid);
/*
* throw it in the stream so it gets cleaned up in
* association destruction
@ -531,9 +531,6 @@ sctp_queue_data_to_stream(struct sctp_tcb *stcb,
return;
}
if ((SCTP_TSN_GE(asoc->cumulative_tsn, control->sinfo_tsn)) && (asoc->idata_supported == 0)) {
goto protocol_error;
}
queue_needed = 1;
asoc->size_on_all_streams += control->length;
sctp_ucount_incr(asoc->cnt_on_all_streams);
@ -1041,7 +1038,7 @@ sctp_deliver_reasm_check(struct sctp_tcb *stcb, struct sctp_association *asoc,
}
control = TAILQ_FIRST(&strm->uno_inqueue);
if ((control) &&
if ((control != NULL) &&
(asoc->idata_supported == 0)) {
/* Special handling needed for "old" data format */
if (sctp_handle_old_unordered_data(stcb, asoc, strm, control, pd_point, inp_read_lock_held)) {
@ -1280,7 +1277,6 @@ sctp_add_chk_to_control(struct sctp_queued_to_read *control,
*/
static void
sctp_queue_data_for_reasm(struct sctp_tcb *stcb, struct sctp_association *asoc,
struct sctp_stream_in *strm,
struct sctp_queued_to_read *control,
struct sctp_tmit_chunk *chk,
int created_control,
@ -1288,8 +1284,10 @@ sctp_queue_data_for_reasm(struct sctp_tcb *stcb, struct sctp_association *asoc,
{
uint32_t next_fsn;
struct sctp_tmit_chunk *at, *nat;
struct sctp_stream_in *strm;
int do_wakeup, unordered;
strm = &asoc->strmin[control->sinfo_stream];
/*
* For old un-ordered data chunks.
*/
@ -1582,7 +1580,6 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
uint32_t ppid;
uint8_t chk_flags;
struct sctp_stream_reset_list *liste;
struct sctp_stream_in *strm;
int ordered;
size_t clen;
int created_control = 0;
@ -1733,7 +1730,6 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
}
return (0);
}
strm = &asoc->strmin[sid];
/*
* If its a fragmented message, lets see if we can find the control
* on the reassembly queues.
@ -1750,7 +1746,7 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
mid, chk_flags);
goto err_out;
}
control = sctp_find_reasm_entry(strm, mid, ordered, asoc->idata_supported);
control = sctp_find_reasm_entry(&asoc->strmin[sid], mid, ordered, asoc->idata_supported);
SCTPDBG(SCTP_DEBUG_XXX, "chunk_flags:0x%x look for control on queues %p\n",
chk_flags, control);
if ((chk_flags & SCTP_DATA_NOT_FRAG) != SCTP_DATA_NOT_FRAG) {
@ -2020,7 +2016,7 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
if ((chk_flags & SCTP_DATA_UNORDERED) == 0) {
/* for ordered, bump what we delivered */
strm->last_mid_delivered++;
asoc->strmin[sid].last_mid_delivered++;
}
SCTP_STAT_INCR(sctps_recvexpress);
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_STR_LOGGING_ENABLE) {
@ -2130,7 +2126,7 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
} else {
SCTPDBG(SCTP_DEBUG_XXX, "Queue control: %p for reordering MID: %u\n", control,
mid);
sctp_queue_data_to_stream(stcb, strm, asoc, control, abort_flag, &need_reasm_check);
sctp_queue_data_to_stream(stcb, asoc, control, abort_flag, &need_reasm_check);
if (*abort_flag) {
if (last_chunk) {
*m = NULL;
@ -2145,7 +2141,7 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
SCTPDBG(SCTP_DEBUG_XXX,
"Queue data to stream for reasm control: %p MID: %u\n",
control, mid);
sctp_queue_data_for_reasm(stcb, asoc, strm, control, chk, created_control, abort_flag, tsn);
sctp_queue_data_for_reasm(stcb, asoc, control, chk, created_control, abort_flag, tsn);
if (*abort_flag) {
/*
* the assoc is now gone and chk was put onto the reasm
@ -2179,6 +2175,10 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
sctp_log_map(asoc->mapping_array_base_tsn, asoc->cumulative_tsn,
asoc->highest_tsn_inside_map, SCTP_MAP_PREPARE_SLIDE);
}
if (need_reasm_check) {
(void)sctp_deliver_reasm_check(stcb, asoc, &asoc->strmin[sid], SCTP_READ_LOCK_NOT_HELD);
need_reasm_check = 0;
}
/* check the special flag for stream resets */
if (((liste = TAILQ_FIRST(&asoc->resetHead)) != NULL) &&
SCTP_TSN_GE(asoc->cumulative_tsn, liste->tsn)) {
@ -2200,10 +2200,14 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
/* All can be removed */
TAILQ_FOREACH_SAFE(ctl, &asoc->pending_reply_queue, next, nctl) {
TAILQ_REMOVE(&asoc->pending_reply_queue, ctl, next);
sctp_queue_data_to_stream(stcb, strm, asoc, ctl, abort_flag, &need_reasm_check);
sctp_queue_data_to_stream(stcb, asoc, ctl, abort_flag, &need_reasm_check);
if (*abort_flag) {
return (0);
}
if (need_reasm_check) {
(void)sctp_deliver_reasm_check(stcb, asoc, &asoc->strmin[ctl->sinfo_stream], SCTP_READ_LOCK_NOT_HELD);
need_reasm_check = 0;
}
}
} else {
TAILQ_FOREACH_SAFE(ctl, &asoc->pending_reply_queue, next, nctl) {
@ -2216,22 +2220,16 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
* ctl->sinfo_tsn > liste->tsn
*/
TAILQ_REMOVE(&asoc->pending_reply_queue, ctl, next);
sctp_queue_data_to_stream(stcb, strm, asoc, ctl, abort_flag, &need_reasm_check);
sctp_queue_data_to_stream(stcb, asoc, ctl, abort_flag, &need_reasm_check);
if (*abort_flag) {
return (0);
}
if (need_reasm_check) {
(void)sctp_deliver_reasm_check(stcb, asoc, &asoc->strmin[ctl->sinfo_stream], SCTP_READ_LOCK_NOT_HELD);
need_reasm_check = 0;
}
}
}
/*
* Now service re-assembly to pick up anything that has been
* held on reassembly queue?
*/
(void)sctp_deliver_reasm_check(stcb, asoc, strm, SCTP_READ_LOCK_NOT_HELD);
need_reasm_check = 0;
}
if (need_reasm_check) {
/* Another one waits ? */
(void)sctp_deliver_reasm_check(stcb, asoc, strm, SCTP_READ_LOCK_NOT_HELD);
}
return (1);
}