Commit d8ea426e09 for openssl.org
commit d8ea426e09314416d88f1cef9a25acb4caafcf80
Author: Daniel Cuthbert <daniel@seventysix.io>
Date: Tue Mar 17 18:58:33 2026 +0000
ech: fix off-by-one in hpke_decrypt_encch extensions length bounds check
The bounds check before reading the two-byte extensions length field uses
extsoffset + 1 instead of extsoffset + 2:
if ((extsoffset + 1) > clearlen) { goto paderr; }
extslen = clear[extsoffset] * 256 + clear[extsoffset + 1];
When extsoffset == clearlen - 1 the check passes, but the second read
clear[extsoffset + 1] is clear[clearlen], which is one byte beyond
the decrypted plaintext. The allocation is OPENSSL_malloc(cipherlen)
where cipherlen = clearlen + AEAD_overhead, so the address is valid,
but the byte is uninitialised after OSSL_HPKE_open returns.
Using Valgrind confirmed an uninitialised-value read at this location
via the full server handshake path:
hpke_decrypt_encch (ech_internal.c)
ossl_ech_early_decrypt
tls_process_client_hello
state_machine
SSL_do_handshake
The subsequent ch_len > clearlen check (line 1875) acts as a safety net
and prevents the stale byte from being used further, so the practical
impact is a forced decode error rather than memory disclosure.
Nevertheless, the read itself is incorrect and should be fixed.
Fix: change the guard to extsoffset + 2 so that both bytes
of the extensions length field are confirmed to be within the decrypted
buffer before either is read.
This issue was identified through AI-assisted structural analysis
(RAPTOR) using CodeQL database tooling (AST analysis, control flow
verification, dominator tree analysis) against the OpenSSL master
branch. The off-by-one was confirmed via AST inspection showing
GT(Add(extsoffset, 1), clearlen) instead of the expected
GT(Add(extsoffset, 2), clearlen).
Found by myself @danielcuthbert and validated
by Benjamin Rodes - Microsoft @bdrodes.
CLA: trivial
Fixes: 6c3edd4f3a8a "Add server-side handling of Encrypted Client Hello"
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Matt Caswell <matt@openssl.foundation>
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
MergeDate: Fri Mar 20 18:07:01 2026
(Merged from https://github.com/openssl/openssl/pull/30472)
diff --git a/ssl/ech/ech_internal.c b/ssl/ech/ech_internal.c
index dd0e40fc1b..338c111bfd 100644
--- a/ssl/ech/ech_internal.c
+++ b/ssl/ech/ech_internal.c
@@ -1864,7 +1864,7 @@ end:
goto paderr;
}
/* odd form of check below just for emphasis */
- if ((extsoffset + 1) > clearlen) {
+ if ((extsoffset + 2) > clearlen) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
goto paderr;
}