Commit 0c0c96c2a3 for openssl.org
commit 0c0c96c2a37f57c92129a8aa7fc46b7a4703983a
Author: Mohammad Hossein Abedini <mhmd.abedinii@gmail.com>
Date: Mon Jul 20 10:30:18 2026 +0330
EVP_CTRL_AEAD_SET_IV_FIXED: reject arg values below -1
EVP_CIPHER_CTX_ctrl() casts the signed int arg to size_t before
dispatching. For EVP_CTRL_AEAD_SET_IV_FIXED the value -1 is an
intentional sentinel ("copy full IV"), so it must be preserved, but
any value below -1 wraps to a near-SIZE_MAX size_t. The two guards
inside gcm_tls_iv_set_fixed() both fail to catch this:
* the sentinel check tests for SIZE_MAX (i.e. (size_t)-1), not
SIZE_MAX-1 or lower;
* the bounds check back-casts len to int, undoing the wrap and
making the comparison evaluate to false.
The result is memcpy(ctx->iv, iv, SIZE_MAX-1) — a heap overflow.
Fix the root cause in EVP_CIPHER_CTX_ctrl() by rejecting any arg
below -1 for EVP_CTRL_AEAD_SET_IV_FIXED, consistent with the guards
already present for EVP_CTRL_SET_KEY_LENGTH, EVP_CTRL_AEAD_SET_IVLEN,
EVP_CTRL_GCM_SET_IV_INV and others.
As a defense-in-depth measure, also replace the (int) back-cast in
gcm_tls_iv_set_fixed() with a safe size_t upper-bound check so that
the provider itself rejects any out-of-range length independently of
the caller.
Co-authored-by: Tomáš Mráz <tm@t8m.info> (comment fix)
Reviewed-by: Jakub Zelenka <jakub.zelenka@openssl.foundation>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Richard Levitte <levitte@openssl.org>
Merge-date: Tue Aug 18 07:41:57 2026
Merged-from: https://github.com/openssl/openssl/pull/31879
diff --git a/crypto/evp/evp_enc.c b/crypto/evp/evp_enc.c
index a5c513db0d..d20ee204f9 100644
--- a/crypto/evp/evp_enc.c
+++ b/crypto/evp/evp_enc.c
@@ -986,6 +986,12 @@ int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
ctx->iv_len = -1;
break;
case EVP_CTRL_AEAD_SET_IV_FIXED:
+ /*
+ * arg == -1 is a valid sentinel meaning "use full ivlen"; anything
+ * below that would wrap to a huge size_t and overflow on memcpy.
+ */
+ if (arg < -1)
+ return 0;
params[0] = OSSL_PARAM_construct_octet_string(
OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED, ptr, sz);
break;
diff --git a/providers/implementations/ciphers/ciphercommon_gcm.c b/providers/implementations/ciphers/ciphercommon_gcm.c
index c93b0767b0..5104875a12 100644
--- a/providers/implementations/ciphers/ciphercommon_gcm.c
+++ b/providers/implementations/ciphers/ciphercommon_gcm.c
@@ -525,8 +525,9 @@ static int gcm_tls_iv_set_fixed(PROV_GCM_CTX *ctx, unsigned char *iv,
return 1;
}
/* Fixed field must be at least 4 bytes and invocation field at least 8 */
- if ((len < EVP_GCM_TLS_FIXED_IV_LEN)
- || (ctx->ivlen - (int)len) < EVP_GCM_TLS_EXPLICIT_IV_LEN)
+ if (len < EVP_GCM_TLS_FIXED_IV_LEN
+ || len > ctx->ivlen
+ || (ctx->ivlen - len) < EVP_GCM_TLS_EXPLICIT_IV_LEN)
return 0;
if (len > 0)
memcpy(ctx->iv, iv, len);