From 59a5fb7ce8e201d1582e8f790dbf38da51788e6f Mon Sep 17 00:00:00 2001 From: goldsimon Date: Sun, 29 Nov 2009 13:23:21 +0000 Subject: [PATCH] Fixed bug #28054: Two segments with FIN flag on the out-of-sequence queue, also fixed PBUF_POOL leak in the out-of-sequence code --- CHANGELOG | 4 ++ src/core/tcp_in.c | 103 +++++++++++++++++++++++++++------------------- 2 files changed, 64 insertions(+), 43 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8022d3a0..858305a2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -46,6 +46,10 @@ HISTORY ++ Bugfixes: + 2009-11-29: Simon Goldschmidt + * tcp_in.c: Fixed bug #28054: Two segments with FIN flag on the out-of- + sequence queue, also fixed PBUF_POOL leak in the out-of-sequence code + 2009-11-29: Simon Goldschmidt * pbuf.c: Fixed bug #28064: pbuf_alloc(PBUF_POOL) is not thread-safe by queueing a call into tcpip_thread to free ooseq-bufs if the pool is empty diff --git a/src/core/tcp_in.c b/src/core/tcp_in.c index 9335c483..80b280d6 100644 --- a/src/core/tcp_in.c +++ b/src/core/tcp_in.c @@ -735,6 +735,40 @@ tcp_process(struct tcp_pcb *pcb) return ERR_OK; } +#if TCP_QUEUE_OOSEQ +/** + * Insert segment into the list (segments covered with new one will be deleted) + * + * Called from tcp_receive() + */ +static void +tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next) +{ + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + /* received segment overlaps all following segments */ + tcp_segs_free(next); + next = NULL; + } + else { + /* delete some following segments */ + while (next && + TCP_SEQ_GT((seqno + cseg->len), + (next->tcphdr->seqno + next->len))) { + struct tcp_seg *old_seg = next; + next = next->next; + tcp_seg_free(old_seg); + } + if (next && + TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + cseg->len = (u16_t)(next->tcphdr->seqno - seqno); + pbuf_realloc(cseg->p, cseg->len); + } + } + cseg->next = next; +} +#endif + /** * Called by tcp_process. Checks if the given segment is an ACK for outstanding * data, and if so frees the memory of the buffered data. Next, is places the @@ -1135,7 +1169,7 @@ tcp_receive(struct tcp_pcb *pcb) while (pcb->ooseq != NULL) { struct tcp_seg *old_ooseq = pcb->ooseq; pcb->ooseq = pcb->ooseq->next; - memp_free(MEMP_TCP_SEG, old_ooseq); + tcp_seg_free(old_ooseq); } } else if (TCP_SEQ_LEQ(pcb->ooseq->tcphdr->seqno, seqno + tcplen)) { if (pcb->ooseq->len > 0) { @@ -1157,7 +1191,7 @@ tcp_receive(struct tcp_pcb *pcb) (TCPH_FLAGS(pcb->ooseq->tcphdr) & (TCP_FIN|TCP_SYN))) { struct tcp_seg *old_ooseq = pcb->ooseq; pcb->ooseq = pcb->ooseq->next; - memp_free(MEMP_TCP_SEG, old_ooseq); + tcp_seg_free(old_ooseq); } } } @@ -1227,7 +1261,6 @@ tcp_receive(struct tcp_pcb *pcb) } } - pcb->ooseq = cseg->next; tcp_seg_free(cseg); } @@ -1266,25 +1299,16 @@ tcp_receive(struct tcp_pcb *pcb) discard. */ if (inseg.len > next->len) { /* The incoming segment is larger than the old - segment. We replace the old segment with the new + segment. We replace some segments with the new one. */ cseg = tcp_seg_copy(&inseg); if (cseg != NULL) { - cseg->next = next->next; if (prev != NULL) { prev->next = cseg; } else { pcb->ooseq = cseg; } - tcp_seg_free(next); - if (cseg->next != NULL) { - next = cseg->next; - if (TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) { - /* We need to trim the incoming segment. */ - cseg->len = (u16_t)(next->tcphdr->seqno - seqno); - pbuf_realloc(cseg->p, cseg->len); - } - } + tcp_oos_insert_segment(cseg, next); } break; } else { @@ -1300,51 +1324,44 @@ tcp_receive(struct tcp_pcb *pcb) than the sequence number of the first segment on the queue. We put the incoming segment first on the queue. */ - - if (TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) { - /* We need to trim the incoming segment. */ - inseg.len = (u16_t)(next->tcphdr->seqno - seqno); - pbuf_realloc(inseg.p, inseg.len); - } cseg = tcp_seg_copy(&inseg); if (cseg != NULL) { - cseg->next = next; pcb->ooseq = cseg; + tcp_oos_insert_segment(cseg, next); } break; } - } else + } else { /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) && TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/ - if(TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)){ - /* The sequence number of the incoming segment is in - between the sequence numbers of the previous and - the next segment on ->ooseq. We trim and insert the - incoming segment and trim the previous segment, if - needed. */ - if (TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) { - /* We need to trim the incoming segment. */ - inseg.len = (u16_t)(next->tcphdr->seqno - seqno); - pbuf_realloc(inseg.p, inseg.len); - } - - cseg = tcp_seg_copy(&inseg); - if (cseg != NULL) { - cseg->next = next; - prev->next = cseg; - if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) { - /* We need to trim the prev segment. */ - prev->len = (u16_t)(seqno - prev->tcphdr->seqno); - pbuf_realloc(prev->p, prev->len); + if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) { + /* The sequence number of the incoming segment is in + between the sequence numbers of the previous and + the next segment on ->ooseq. We trim trim the previous + segment, delete next segments that included in received segment + and trim received, if needed. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) { + /* We need to trim the prev segment. */ + prev->len = (u16_t)(seqno - prev->tcphdr->seqno); + pbuf_realloc(prev->p, prev->len); + } + prev->next = cseg; + tcp_oos_insert_segment(cseg, next); } + break; } - break; } /* If the "next" segment is the last segment on the ooseq queue, we add the incoming segment to the end of the list. */ if (next->next == NULL && TCP_SEQ_GT(seqno, next->tcphdr->seqno)) { + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + /* segment "next" already contains all data */ + break; + } next->next = tcp_seg_copy(&inseg); if (next->next != NULL) { if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {