Commit f33bb236fc for openssl.org
commit f33bb236fc44afe40a8b4787e14913f82d501dca
Author: Eugene Syromiatnikov <esyr@openssl.org>
Date: Sun Apr 12 14:33:08 2026 +0200
ssl/quic/quic_record_tx.c: refactor qtx->cons obtaining
As currently implemented, the only txe passed to qtx_reserve_txe()
(and, subsequently, to qtx_resize_txe()) is qtx->cons one, so the check
"if (qtx->cons == txe)" is superfluous, and, more so, would lead
to a memory leak if it weren't the case, as was spotted by Coverity.
Moreover, the set of qtx_alloc_txe(), qtx_ensure_free_txe(),
qtx_ensure_cons(), qtx_resize_txe(), and qtx_reserve_txe() functions,
while being written in a relatively generic way, is actually called
from a single call site in ossl_qtx_write_pkt(), and contains several
duplicating checks and unnecessary logic (like, adding a newly allocated
TXE to the free list, only to remove it from there right away
in qtx_ensure_cons(), the only its user), so just merge the whole
aforementioned set of functions (except qtx_alloc_txe()) in a single
function, qtx_get_cons_txe().
Resolves: https://scan5.scan.coverity.com/#/project-view/63999/10222?selectedIssue=1691460
Complements: 16892155e153 "quic: fix NULL txl dereference in qtx_resize_txe"
Signed-off-by: Eugene Syromiatnikov <esyr@openssl.org>
Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
MergeDate: Wed Apr 29 15:12:14 2026
(Merged from https://github.com/openssl/openssl/pull/30783)
diff --git a/ssl/quic/quic_record_tx.c b/ssl/quic/quic_record_tx.c
index 0dbdb1491a..f5bd5b2f83 100644
--- a/ssl/quic/quic_record_tx.c
+++ b/ssl/quic/quic_record_tx.c
@@ -242,77 +242,49 @@ static TXE *qtx_alloc_txe(size_t alloc_len)
}
/*
- * Ensures there is at least one TXE in the free list, allocating a new entry
- * if necessary. The returned TXE is in the free list; it is not popped.
- *
- * alloc_len is a hint which may be used to determine the TXE size if allocation
- * is necessary. Returns NULL on allocation failure.
- */
-static TXE *qtx_ensure_free_txe(OSSL_QTX *qtx, size_t alloc_len)
-{
- TXE *txe;
-
- txe = ossl_list_txe_head(&qtx->free);
- if (txe != NULL)
- return txe;
-
- txe = qtx_alloc_txe(alloc_len);
- if (txe == NULL)
- return NULL;
-
- ossl_list_txe_insert_tail(&qtx->free, txe);
- return txe;
-}
-
-/*
- * Resize the data buffer attached to an TXE to be n bytes in size. The address
- * of the TXE might change; the new address is returned, or NULL on failure, in
- * which case the original TXE remains valid.
+ * Ensure that qtx->cons has a TXE attached with allocated size of at least
+ * min_size. Returns pointer to the TXE on success or NULL on failure.
*/
-static TXE *qtx_resize_txe(OSSL_QTX *qtx, TXE *txe, size_t n)
+static TXE *qtx_get_cons_txe(OSSL_QTX *qtx, size_t min_size)
{
- TXE *txe2;
-
- /* Should never happen. */
- if (txe == NULL)
- return NULL;
-
- if (n >= SIZE_MAX - sizeof(TXE))
+ if (min_size >= SIZE_MAX - sizeof(TXE))
return NULL;
/*
- * to resize txe, the caller must detach it from the list first,
- * fail if txe is still attached.
+ * If there is no coalescing in progress, try to get a TXE
+ * from the free list (and remove it from there), or allocate a new one.
*/
- if (!ossl_assert(ossl_list_txe_prev(txe) == NULL
- && ossl_list_txe_next(txe) == NULL))
- return NULL;
+ if (qtx->cons == NULL) {
+ TXE *txe = ossl_list_txe_head(&qtx->free);
- /*
- * NOTE: We do not clear old memory, although it does contain decrypted
- * data.
- */
- txe2 = OPENSSL_realloc(txe, sizeof(TXE) + n);
- if (txe2 == NULL)
- return NULL;
+ if (txe != NULL) {
+ ossl_list_txe_remove(&qtx->free, txe);
+ } else {
+ if ((txe = qtx_alloc_txe(min_size)) == NULL)
+ return NULL;
+ }
- if (qtx->cons == txe)
- qtx->cons = txe2;
+ txe->data_len = 0;
+ qtx->cons = txe;
+ qtx->cons_count = 0;
+ }
- txe2->alloc_len = n;
- return txe2;
-}
+ /* Resize TXE if it's too small. */
+ if (qtx->cons->alloc_len < min_size) {
+ /*
+ * NOTE: We do not clear old memory, although it does contain decrypted
+ * data.
+ */
+ TXE *realloc_txe = OPENSSL_realloc(qtx->cons, sizeof(TXE) + min_size);
-/*
- * Ensure the data buffer attached to an TXE is at least n bytes in size.
- * Returns NULL on failure.
- */
-static TXE *qtx_reserve_txe(OSSL_QTX *qtx, TXE *txe, size_t n)
-{
- if (txe->alloc_len >= n)
- return txe;
+ if (realloc_txe == NULL)
+ return NULL;
+
+ realloc_txe->alloc_len = min_size;
+ qtx->cons = realloc_txe;
+ }
- return qtx_resize_txe(qtx, txe, n);
+ return qtx->cons;
}
/* Move a TXE from pending to free. */
@@ -730,24 +702,6 @@ err:
return ret;
}
-static TXE *qtx_ensure_cons(OSSL_QTX *qtx)
-{
- TXE *txe = qtx->cons;
-
- if (txe != NULL)
- return txe;
-
- txe = qtx_ensure_free_txe(qtx, qtx->mdpl);
- if (txe == NULL)
- return NULL;
-
- ossl_list_txe_remove(&qtx->free, txe);
- qtx->cons = txe;
- qtx->cons_count = 0;
- txe->data_len = 0;
- return txe;
-}
-
static QLOG *qtx_get_qlog(OSSL_QTX *qtx)
{
if (qtx->get_qlog_cb == NULL)
@@ -840,22 +794,14 @@ int ossl_qtx_write_pkt(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt)
* - detached.
*
* if txe is detached (not member of free/tx list), then it is kept
- * in qtx->cons. The qtx_ensure_cons() here either returns the txe
- * from free list or existing ->cons txe. The txe we obtain here
- * is detached.
- */
- txe = qtx_ensure_cons(qtx);
- if (txe == NULL)
- return 0; /* allocation failure */
-
- /*
- * Ensure TXE has at least MDPL bytes allocated. This should only be
- * possible if the MDPL has increased.
+ * in qtx->cons. The qtx_get_cons_txe() here makes sure that qtx->cons
+ * points at a (new or existing) detached txe and has at least MDPL
+ * bytes allocated.
*/
- txe = qtx_reserve_txe(qtx, txe, qtx->mdpl);
+ txe = qtx_get_cons_txe(qtx, qtx->mdpl);
if (txe == NULL) {
/*
- * realloc of txe failed. however it is still kept in ->cons,
+ * If realloc of txe has failed. it is still kept in ->cons,
* no memory leak.
* The question is what we should do here to handle error,
* is doing `return 0` enough? or shall we discard ->cons and