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);