tcp: handle pcb->snd_queuelen and chained pbufs during segment split (bug #52692)

This fixes a bug in tcp_split_unsent_seg() where a chained pbuf was
not correctly updating pcb->snd_queuelen during trimming and snd_queuelen
would desynchronize if pbuf_realloc() freed some of the chain

Also, use pbuf_clen() for adding the new remaining segment rather than ++.
The new remaining segment should always be one pbuf due to the semantics
of PBUF_RAM, but this follows the best practice of using pbuf_clen()
This commit is contained in:
Joel Cunningham 2017-12-19 09:29:24 -06:00
parent f334ac68b6
commit 04b983b4f3

View File

@ -1900,6 +1900,9 @@ tcp_split_unsent_seg(struct tcp_pcb *pcb, u16_t split)
seg->flags |= TF_SEG_DATA_CHECKSUMMED; seg->flags |= TF_SEG_DATA_CHECKSUMMED;
#endif /* TCP_CHECKSUM_ON_COPY */ #endif /* TCP_CHECKSUM_ON_COPY */
/* Remove this segment from the queue since trimming it may free pbufs */
pcb->snd_queuelen -= pbuf_clen(useg->p);
/* Trim the original pbuf into our split size. At this point our remainder segment must be setup /* Trim the original pbuf into our split size. At this point our remainder segment must be setup
successfully because we are modifying the original segment */ successfully because we are modifying the original segment */
pbuf_realloc(useg->p, useg->p->tot_len - remainder); pbuf_realloc(useg->p, useg->p->tot_len - remainder);
@ -1910,6 +1913,9 @@ tcp_split_unsent_seg(struct tcp_pcb *pcb, u16_t split)
useg->oversize_left = 0; useg->oversize_left = 0;
#endif /* TCP_OVERSIZE_DBGCHECK */ #endif /* TCP_OVERSIZE_DBGCHECK */
/* Add back to the queue with new trimmed pbuf */
pcb->snd_queuelen += pbuf_clen(useg->p);
#if TCP_CHECKSUM_ON_COPY #if TCP_CHECKSUM_ON_COPY
/* The checksum on the split segment is now incorrect. We need to re-run it over the split */ /* The checksum on the split segment is now incorrect. We need to re-run it over the split */
useg->chksum = 0; useg->chksum = 0;
@ -1933,7 +1939,7 @@ tcp_split_unsent_seg(struct tcp_pcb *pcb, u16_t split)
/* Update number of segments on the queues. Note that length now may /* Update number of segments on the queues. Note that length now may
* exceed TCP_SND_QUEUELEN! We don't have to touch pcb->snd_buf * exceed TCP_SND_QUEUELEN! We don't have to touch pcb->snd_buf
* because the total amount of data is constant when packet is split */ * because the total amount of data is constant when packet is split */
pcb->snd_queuelen++; pcb->snd_queuelen += pbuf_clen(seg->p);
/* Finally insert remainder into queue after split (which stays head) */ /* Finally insert remainder into queue after split (which stays head) */
seg->next = useg->next; seg->next = useg->next;