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

This commit is contained in:
goldsimon 2009-11-29 13:23:21 +00:00
parent c5d2e536cf
commit 59a5fb7ce8
2 changed files with 64 additions and 43 deletions

View File

@ -46,6 +46,10 @@ HISTORY
++ Bugfixes: ++ 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 2009-11-29: Simon Goldschmidt
* pbuf.c: Fixed bug #28064: pbuf_alloc(PBUF_POOL) is not thread-safe by * 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 queueing a call into tcpip_thread to free ooseq-bufs if the pool is empty

View File

@ -735,6 +735,40 @@ tcp_process(struct tcp_pcb *pcb)
return ERR_OK; 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 * 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 * 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) { while (pcb->ooseq != NULL) {
struct tcp_seg *old_ooseq = pcb->ooseq; struct tcp_seg *old_ooseq = pcb->ooseq;
pcb->ooseq = pcb->ooseq->next; 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)) { } else if (TCP_SEQ_LEQ(pcb->ooseq->tcphdr->seqno, seqno + tcplen)) {
if (pcb->ooseq->len > 0) { if (pcb->ooseq->len > 0) {
@ -1157,7 +1191,7 @@ tcp_receive(struct tcp_pcb *pcb)
(TCPH_FLAGS(pcb->ooseq->tcphdr) & (TCP_FIN|TCP_SYN))) { (TCPH_FLAGS(pcb->ooseq->tcphdr) & (TCP_FIN|TCP_SYN))) {
struct tcp_seg *old_ooseq = pcb->ooseq; struct tcp_seg *old_ooseq = pcb->ooseq;
pcb->ooseq = pcb->ooseq->next; 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; pcb->ooseq = cseg->next;
tcp_seg_free(cseg); tcp_seg_free(cseg);
} }
@ -1266,25 +1299,16 @@ tcp_receive(struct tcp_pcb *pcb)
discard. */ discard. */
if (inseg.len > next->len) { if (inseg.len > next->len) {
/* The incoming segment is larger than the old /* 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. */ one. */
cseg = tcp_seg_copy(&inseg); cseg = tcp_seg_copy(&inseg);
if (cseg != NULL) { if (cseg != NULL) {
cseg->next = next->next;
if (prev != NULL) { if (prev != NULL) {
prev->next = cseg; prev->next = cseg;
} else { } else {
pcb->ooseq = cseg; pcb->ooseq = cseg;
} }
tcp_seg_free(next); tcp_oos_insert_segment(cseg, 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);
}
}
} }
break; break;
} else { } else {
@ -1300,51 +1324,44 @@ tcp_receive(struct tcp_pcb *pcb)
than the sequence number of the first segment on the than the sequence number of the first segment on the
queue. We put the incoming segment first on the queue. We put the incoming segment first on the
queue. */ 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); cseg = tcp_seg_copy(&inseg);
if (cseg != NULL) { if (cseg != NULL) {
cseg->next = next;
pcb->ooseq = cseg; pcb->ooseq = cseg;
tcp_oos_insert_segment(cseg, next);
} }
break; break;
} }
} else } else {
/*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) && /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/ TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/
if(TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)){ if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) {
/* The sequence number of the incoming segment is in /* The sequence number of the incoming segment is in
between the sequence numbers of the previous and between the sequence numbers of the previous and
the next segment on ->ooseq. We trim and insert the the next segment on ->ooseq. We trim trim the previous
incoming segment and trim the previous segment, if segment, delete next segments that included in received segment
needed. */ and trim received, if needed. */
if (TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) { cseg = tcp_seg_copy(&inseg);
/* We need to trim the incoming segment. */ if (cseg != NULL) {
inseg.len = (u16_t)(next->tcphdr->seqno - seqno); if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
pbuf_realloc(inseg.p, inseg.len); /* We need to trim the prev segment. */
} prev->len = (u16_t)(seqno - prev->tcphdr->seqno);
pbuf_realloc(prev->p, prev->len);
cseg = tcp_seg_copy(&inseg); }
if (cseg != NULL) { prev->next = cseg;
cseg->next = next; tcp_oos_insert_segment(cseg, 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);
} }
break;
} }
break;
} }
/* If the "next" segment is the last segment on the /* If the "next" segment is the last segment on the
ooseq queue, we add the incoming segment to the end ooseq queue, we add the incoming segment to the end
of the list. */ of the list. */
if (next->next == NULL && if (next->next == NULL &&
TCP_SEQ_GT(seqno, next->tcphdr->seqno)) { 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); next->next = tcp_seg_copy(&inseg);
if (next->next != NULL) { if (next->next != NULL) {
if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) { if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {