Improve handling of PKTDROP chunks. This includes the input validation
to address two issues found by ossfuzz testing the userland stack: * https://oss-fuzz.com/testcase-detail/5387560242380800 * https://oss-fuzz.com/testcase-detail/4887954068865024 and adding support for I-DATA chunks in addition to DATA chunks.
This commit is contained in:
parent
de402d6322
commit
32df1c9ebb
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=363008
@ -3046,7 +3046,8 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc,
|
|||||||
{
|
{
|
||||||
switch (desc->chunk_type) {
|
switch (desc->chunk_type) {
|
||||||
case SCTP_DATA:
|
case SCTP_DATA:
|
||||||
/* find the tsn to resend (possibly */
|
case SCTP_IDATA:
|
||||||
|
/* find the tsn to resend (possibly) */
|
||||||
{
|
{
|
||||||
uint32_t tsn;
|
uint32_t tsn;
|
||||||
struct sctp_tmit_chunk *tp1;
|
struct sctp_tmit_chunk *tp1;
|
||||||
@ -3080,8 +3081,6 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc,
|
|||||||
SCTP_STAT_INCR(sctps_pdrptsnnf);
|
SCTP_STAT_INCR(sctps_pdrptsnnf);
|
||||||
}
|
}
|
||||||
if ((tp1) && (tp1->sent < SCTP_DATAGRAM_ACKED)) {
|
if ((tp1) && (tp1->sent < SCTP_DATAGRAM_ACKED)) {
|
||||||
uint8_t *ddp;
|
|
||||||
|
|
||||||
if (((flg & SCTP_BADCRC) == 0) &&
|
if (((flg & SCTP_BADCRC) == 0) &&
|
||||||
((flg & SCTP_FROM_MIDDLE_BOX) == 0)) {
|
((flg & SCTP_FROM_MIDDLE_BOX) == 0)) {
|
||||||
return (0);
|
return (0);
|
||||||
@ -3096,20 +3095,18 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc,
|
|||||||
SCTP_STAT_INCR(sctps_pdrpdizrw);
|
SCTP_STAT_INCR(sctps_pdrpdizrw);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
ddp = (uint8_t *)(mtod(tp1->data, caddr_t)+
|
if ((uint32_t)SCTP_BUF_LEN(tp1->data) <
|
||||||
sizeof(struct sctp_data_chunk));
|
SCTP_DATA_CHUNK_OVERHEAD(stcb) + SCTP_NUM_DB_TO_VERIFY) {
|
||||||
{
|
/* Payload not matching. */
|
||||||
unsigned int iii;
|
SCTP_STAT_INCR(sctps_pdrpbadd);
|
||||||
|
return (-1);
|
||||||
for (iii = 0; iii < sizeof(desc->data_bytes);
|
}
|
||||||
iii++) {
|
if (memcmp(mtod(tp1->data, caddr_t)+SCTP_DATA_CHUNK_OVERHEAD(stcb),
|
||||||
if (ddp[iii] != desc->data_bytes[iii]) {
|
desc->data_bytes, SCTP_NUM_DB_TO_VERIFY) != 0) {
|
||||||
SCTP_STAT_INCR(sctps_pdrpbadd);
|
/* Payload not matching. */
|
||||||
return (-1);
|
SCTP_STAT_INCR(sctps_pdrpbadd);
|
||||||
}
|
return (-1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tp1->do_rtt) {
|
if (tp1->do_rtt) {
|
||||||
/*
|
/*
|
||||||
* this guy had a RTO calculation
|
* this guy had a RTO calculation
|
||||||
@ -4135,104 +4132,126 @@ static void
|
|||||||
sctp_handle_packet_dropped(struct sctp_pktdrop_chunk *cp,
|
sctp_handle_packet_dropped(struct sctp_pktdrop_chunk *cp,
|
||||||
struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t limit)
|
struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t limit)
|
||||||
{
|
{
|
||||||
uint32_t bottle_bw, on_queue;
|
|
||||||
uint16_t trunc_len;
|
|
||||||
unsigned int chlen;
|
|
||||||
unsigned int at;
|
|
||||||
struct sctp_chunk_desc desc;
|
struct sctp_chunk_desc desc;
|
||||||
struct sctp_chunkhdr *ch;
|
struct sctp_chunkhdr *chk_hdr;
|
||||||
|
struct sctp_data_chunk *data_chunk;
|
||||||
|
struct sctp_idata_chunk *idata_chunk;
|
||||||
|
uint32_t bottle_bw, on_queue;
|
||||||
|
uint32_t offset, chk_len;
|
||||||
|
uint16_t trunc_len;
|
||||||
|
uint16_t pktdrp_len;
|
||||||
|
uint8_t pktdrp_flags;
|
||||||
|
|
||||||
chlen = ntohs(cp->ch.chunk_length);
|
KASSERT(sizeof(struct sctp_pktdrop_chunk) <= limit,
|
||||||
chlen -= sizeof(struct sctp_pktdrop_chunk);
|
("PKTDROP chunk too small"));
|
||||||
/* XXX possible chlen underflow */
|
pktdrp_flags = cp->ch.chunk_flags;
|
||||||
if (chlen == 0) {
|
pktdrp_len = ntohs(cp->ch.chunk_length);
|
||||||
ch = NULL;
|
KASSERT(limit <= pktdrp_len, ("Inconsistent limit"));
|
||||||
if (cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX)
|
if (pktdrp_flags & SCTP_PACKET_TRUNCATED) {
|
||||||
SCTP_STAT_INCR(sctps_pdrpbwrpt);
|
trunc_len = ntohs(cp->trunc_len);
|
||||||
|
if (trunc_len <= pktdrp_len - sizeof(struct sctp_pktdrop_chunk)) {
|
||||||
|
/* The peer plays games with us. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ch = (struct sctp_chunkhdr *)(cp->data + sizeof(struct sctphdr));
|
trunc_len = 0;
|
||||||
chlen -= sizeof(struct sctphdr);
|
|
||||||
/* XXX possible chlen underflow */
|
|
||||||
memset(&desc, 0, sizeof(desc));
|
|
||||||
}
|
}
|
||||||
trunc_len = (uint16_t)ntohs(cp->trunc_len);
|
limit -= sizeof(struct sctp_pktdrop_chunk);
|
||||||
if (trunc_len > limit) {
|
offset = 0;
|
||||||
trunc_len = limit;
|
if (offset == limit) {
|
||||||
|
if (pktdrp_flags & SCTP_FROM_MIDDLE_BOX) {
|
||||||
|
SCTP_STAT_INCR(sctps_pdrpbwrpt);
|
||||||
|
}
|
||||||
|
} else if (offset + sizeof(struct sctphdr) > limit) {
|
||||||
|
/* Only a partial SCTP common header. */
|
||||||
|
SCTP_STAT_INCR(sctps_pdrpcrupt);
|
||||||
|
offset = limit;
|
||||||
|
} else {
|
||||||
|
/* XXX: Check embedded SCTP common header. */
|
||||||
|
offset += sizeof(struct sctphdr);
|
||||||
}
|
}
|
||||||
|
/* Now parse through the chunks themselves. */
|
||||||
/* now the chunks themselves */
|
while (offset < limit) {
|
||||||
while ((ch != NULL) && (chlen >= sizeof(struct sctp_chunkhdr))) {
|
if (offset + sizeof(struct sctp_chunkhdr) > limit) {
|
||||||
desc.chunk_type = ch->chunk_type;
|
|
||||||
/* get amount we need to move */
|
|
||||||
at = ntohs(ch->chunk_length);
|
|
||||||
if (at < sizeof(struct sctp_chunkhdr)) {
|
|
||||||
/* corrupt chunk, maybe at the end? */
|
|
||||||
SCTP_STAT_INCR(sctps_pdrpcrupt);
|
SCTP_STAT_INCR(sctps_pdrpcrupt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (trunc_len == 0) {
|
chk_hdr = (struct sctp_chunkhdr *)(cp->data + offset);
|
||||||
/* we are supposed to have all of it */
|
desc.chunk_type = chk_hdr->chunk_type;
|
||||||
if (at > chlen) {
|
/* get amount we need to move */
|
||||||
/* corrupt skip it */
|
chk_len = (uint32_t)ntohs(chk_hdr->chunk_length);
|
||||||
SCTP_STAT_INCR(sctps_pdrpcrupt);
|
if (chk_len < sizeof(struct sctp_chunkhdr)) {
|
||||||
break;
|
/* Someone is lying... */
|
||||||
}
|
break;
|
||||||
} else {
|
|
||||||
/* is there enough of it left ? */
|
|
||||||
if (desc.chunk_type == SCTP_DATA) {
|
|
||||||
if (chlen < (sizeof(struct sctp_data_chunk) +
|
|
||||||
sizeof(desc.data_bytes))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (chlen < sizeof(struct sctp_chunkhdr)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (desc.chunk_type == SCTP_DATA) {
|
if (desc.chunk_type == SCTP_DATA) {
|
||||||
/* can we get out the tsn? */
|
if (stcb->asoc.idata_supported) {
|
||||||
if ((cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX))
|
/* Some is playing games with us. */
|
||||||
SCTP_STAT_INCR(sctps_pdrpmbda);
|
|
||||||
|
|
||||||
if (chlen >= (sizeof(struct sctp_data_chunk) + sizeof(uint32_t))) {
|
|
||||||
/* yep */
|
|
||||||
struct sctp_data_chunk *dcp;
|
|
||||||
uint8_t *ddp;
|
|
||||||
unsigned int iii;
|
|
||||||
|
|
||||||
dcp = (struct sctp_data_chunk *)ch;
|
|
||||||
ddp = (uint8_t *)(dcp + 1);
|
|
||||||
for (iii = 0; iii < sizeof(desc.data_bytes); iii++) {
|
|
||||||
desc.data_bytes[iii] = ddp[iii];
|
|
||||||
}
|
|
||||||
desc.tsn_ifany = dcp->dp.tsn;
|
|
||||||
} else {
|
|
||||||
/* nope we are done. */
|
|
||||||
SCTP_STAT_INCR(sctps_pdrpnedat);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (chk_len <= sizeof(struct sctp_data_chunk)) {
|
||||||
|
/* Some is playing games with us. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (chk_len < sizeof(struct sctp_data_chunk) + SCTP_NUM_DB_TO_VERIFY) {
|
||||||
|
/*
|
||||||
|
* Not enough data bytes available in the
|
||||||
|
* chunk.
|
||||||
|
*/
|
||||||
|
SCTP_STAT_INCR(sctps_pdrpnedat);
|
||||||
|
goto next_chunk;
|
||||||
|
}
|
||||||
|
if (offset + sizeof(struct sctp_data_chunk) + SCTP_NUM_DB_TO_VERIFY > limit) {
|
||||||
|
/* Not enough data in buffer. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data_chunk = (struct sctp_data_chunk *)(cp->data + offset);
|
||||||
|
memcpy(desc.data_bytes, data_chunk + 1, SCTP_NUM_DB_TO_VERIFY);
|
||||||
|
desc.tsn_ifany = data_chunk->dp.tsn;
|
||||||
|
if (pktdrp_flags & SCTP_FROM_MIDDLE_BOX) {
|
||||||
|
SCTP_STAT_INCR(sctps_pdrpmbda);
|
||||||
|
}
|
||||||
|
} else if (desc.chunk_type == SCTP_IDATA) {
|
||||||
|
if (!stcb->asoc.idata_supported) {
|
||||||
|
/* Some is playing games with us. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (chk_len <= sizeof(struct sctp_idata_chunk)) {
|
||||||
|
/* Some is playing games with us. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (chk_len < sizeof(struct sctp_idata_chunk) + SCTP_NUM_DB_TO_VERIFY) {
|
||||||
|
/*
|
||||||
|
* Not enough data bytes available in the
|
||||||
|
* chunk.
|
||||||
|
*/
|
||||||
|
SCTP_STAT_INCR(sctps_pdrpnedat);
|
||||||
|
goto next_chunk;
|
||||||
|
}
|
||||||
|
if (offset + sizeof(struct sctp_idata_chunk) + SCTP_NUM_DB_TO_VERIFY > limit) {
|
||||||
|
/* Not enough data in buffer. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
idata_chunk = (struct sctp_idata_chunk *)(cp->data + offset);
|
||||||
|
memcpy(desc.data_bytes, idata_chunk + 1, SCTP_NUM_DB_TO_VERIFY);
|
||||||
|
desc.tsn_ifany = idata_chunk->dp.tsn;
|
||||||
|
if (pktdrp_flags & SCTP_FROM_MIDDLE_BOX) {
|
||||||
|
SCTP_STAT_INCR(sctps_pdrpmbda);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX))
|
if (pktdrp_flags & SCTP_FROM_MIDDLE_BOX) {
|
||||||
SCTP_STAT_INCR(sctps_pdrpmbct);
|
SCTP_STAT_INCR(sctps_pdrpmbct);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (process_chunk_drop(stcb, &desc, net, pktdrp_flags)) {
|
||||||
if (process_chunk_drop(stcb, &desc, net, cp->ch.chunk_flags)) {
|
|
||||||
SCTP_STAT_INCR(sctps_pdrppdbrk);
|
SCTP_STAT_INCR(sctps_pdrppdbrk);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (SCTP_SIZE32(at) > chlen) {
|
next_chunk:
|
||||||
break;
|
offset += SCTP_SIZE32(chk_len);
|
||||||
}
|
|
||||||
chlen -= SCTP_SIZE32(at);
|
|
||||||
if (chlen < sizeof(struct sctp_chunkhdr)) {
|
|
||||||
/* done, none left */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ch = (struct sctp_chunkhdr *)((caddr_t)ch + SCTP_SIZE32(at));
|
|
||||||
}
|
}
|
||||||
/* Now update any rwnd --- possibly */
|
/* Now update any rwnd --- possibly */
|
||||||
if ((cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX) == 0) {
|
if ((pktdrp_flags & SCTP_FROM_MIDDLE_BOX) == 0) {
|
||||||
/* From a peer, we get a rwnd report */
|
/* From a peer, we get a rwnd report */
|
||||||
uint32_t a_rwnd;
|
uint32_t a_rwnd;
|
||||||
|
|
||||||
@ -4268,7 +4287,7 @@ sctp_handle_packet_dropped(struct sctp_pktdrop_chunk *cp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* now middle boxes in sat networks get a cwnd bump */
|
/* now middle boxes in sat networks get a cwnd bump */
|
||||||
if ((cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX) &&
|
if ((pktdrp_flags & SCTP_FROM_MIDDLE_BOX) &&
|
||||||
(stcb->asoc.sat_t3_loss_recovery == 0) &&
|
(stcb->asoc.sat_t3_loss_recovery == 0) &&
|
||||||
(stcb->asoc.sat_network)) {
|
(stcb->asoc.sat_network)) {
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user