From 6cf2e3da1eec8b1044a9e4c4df14e3d7243f9e46 Mon Sep 17 00:00:00 2001 From: syed abdul khaliq Date: Thu, 15 Oct 2026 14:45:04 +0530 Subject: [PATCH] kex: free mlkem-nistp hybrid shared secret on NEWKEYS EAGAIN kex_mlkem_nistp() allocates shared_secret on the heap, hashes it into K, zeroes it, and frees it only at the clean_free label. The NEWKEYS send returns EAGAIN with a bare `return rc` that skips clean_free, so in non-blocking mode a stalled write there leaks the buffer once per hybrid handshake; the resumed call re-enters at ssh2_NB_state_sent and never allocates (or frees) it again. Route that EAGAIN return through clean_free, matching the kex_finish EAGAIN path just below it. The buffer is already consumed by that point, and exchange_state is untouched so the resume still works. Only kex_mlkem_nistp has this: the sibling kex_mlkem768x25519_sha256() keeps its shared secret on the stack and the other kex methods hold their buffers in exchange_state, freed by their cleanup. Closes #2670 --- src/kex.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/kex.c b/src/kex.c index 9d7861d8..7b2e9ff1 100644 --- a/src/kex.c +++ b/src/kex.c @@ -2002,8 +2002,10 @@ static int kex_mlkem_nistp(LIBSSH2_SESSION *session, if(exchange_state->state == ssh2_NB_state_sent) { rc = ssh2_transport_send(session, &exchange_state->c, 1, NULL, 0); - if(rc == LIBSSH2_ERROR_EAGAIN) - return rc; + if(rc == LIBSSH2_ERROR_EAGAIN) { + ret = rc; + goto clean_free; + } else if(rc) { ret = ssh2_err(session, rc, "Unable to send NEWKEYS message mlkemnistp");