From 04b983b4f376697f04980b8d547fa4d03aa10600 Mon Sep 17 00:00:00 2001 From: Joel Cunningham Date: Tue, 19 Dec 2017 09:29:24 -0600 Subject: [PATCH] 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() --- src/core/tcp_out.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/tcp_out.c b/src/core/tcp_out.c index f35720fa..6fdf1975 100644 --- a/src/core/tcp_out.c +++ b/src/core/tcp_out.c @@ -1900,6 +1900,9 @@ tcp_split_unsent_seg(struct tcp_pcb *pcb, u16_t split) seg->flags |= TF_SEG_DATA_CHECKSUMMED; #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 successfully because we are modifying the original segment */ 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; #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 /* The checksum on the split segment is now incorrect. We need to re-run it over the split */ 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 * 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 */ - pcb->snd_queuelen++; + pcb->snd_queuelen += pbuf_clen(seg->p); /* Finally insert remainder into queue after split (which stays head) */ seg->next = useg->next;