Commit 5dbd6501ee for openssl.org

commit 5dbd6501ee6c07c8a397df5c979743c05d775603
Author: Yasuaki Torimaru <yasuakitorimaru@gmail.com>
Date:   Sun Oct 11 16:18:03 2026 +0900

    Fix missing bounds check in GCM setivinv()

    setivinv() computes the destination of the copy as ctx->iv + ctx->ivlen - inl
    without checking that inl is at most ctx->ivlen.  Both operands are size_t,
    so a larger inl underflows and memcpy() writes before ctx->iv.

    The length is caller-supplied, through EVP_CTRL_GCM_SET_IV_INV or the
    "tlsivinv" cipher parameter, and neither path bounds it against the IV
    length.  Reject inl == 0 and inl > ctx->ivlen before the copy.

    Add a test covering the rejected lengths and the accepted full-length case.

    Fixes: 11b443598626 ("Add GCM support for EVP_CTRL_GCM_IV_GEN and EVP_CTRL_GCM_SET_IV_INV to providers")
    Assisted-by: Claude:claude-opus-4-8
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    Reviewed-by: Richard Levitte <levitte@openssl.foundation>
    Merge-date: Wed Nov  4 17:57:07 2026
    Merged-from: https://github.com/openssl/openssl/pull/30680

diff --git a/providers/implementations/ciphers/ciphercommon_gcm.c b/providers/implementations/ciphers/ciphercommon_gcm.c
index 805fa6d9d4..c40ff92d2d 100644
--- a/providers/implementations/ciphers/ciphercommon_gcm.c
+++ b/providers/implementations/ciphers/ciphercommon_gcm.c
@@ -146,6 +146,9 @@ static int setivinv(PROV_GCM_CTX *ctx, unsigned char *in, size_t inl)
         || ctx->enc)
         return 0;

+    if (inl == 0 || inl > ctx->ivlen)
+        return 0;
+
     memcpy(ctx->iv + ctx->ivlen - inl, in, inl);
     if (!ctx->hw->setiv(ctx, ctx->iv, ctx->ivlen))
         return 0;
diff --git a/test/aesgcmtest.c b/test/aesgcmtest.c
index 737c0af696..5a117b32db 100644
--- a/test/aesgcmtest.c
+++ b/test/aesgcmtest.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2019-2026 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
@@ -8,6 +8,8 @@
  */

 #include <openssl/evp.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
 #include "testutil.h"

 static const unsigned char gcm_key[] = {
@@ -136,10 +138,70 @@ static int ivgen_test(void)
         && do_decrypt(iv_gen, ct, ctlen, tag, taglen);
 }

+/*
+ * inl > ctx->ivlen makes setivinv() compute ctx->iv + ctx->ivlen - inl,
+ * which wraps, and inl == 0 points past the IV.  Both are rejected.
+ */
+static int setivinv_bounds_test(void)
+{
+    int ret = 0;
+    EVP_CIPHER_CTX *ctx = NULL;
+    EVP_CIPHER *cipher = NULL;
+    unsigned char key[32] = { 0 };
+    unsigned char fixed_iv[4] = { 0x01, 0x02, 0x03, 0x04 };
+    unsigned char big_ivinv[64];
+    unsigned char exact_ivinv[12];
+    OSSL_PARAM params[2];
+
+    memset(big_ivinv, 'A', sizeof(big_ivinv));
+    memset(exact_ivinv, 'B', sizeof(exact_ivinv));
+
+    if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())
+        || !TEST_ptr(cipher = EVP_CIPHER_fetch(NULL, "AES-256-GCM", NULL)))
+        goto err;
+
+    if (!TEST_true(EVP_DecryptInit_ex2(ctx, cipher, key, NULL, NULL)))
+        goto err;
+
+    if (!TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IV_FIXED,
+            (int)sizeof(fixed_iv), fixed_iv)))
+        goto err;
+
+    /* --- Negative: inl == 0 must be rejected --- */
+    params[0] = OSSL_PARAM_construct_octet_string(
+        OSSL_CIPHER_PARAM_AEAD_TLS1_SET_IV_INV, big_ivinv, 0);
+    params[1] = OSSL_PARAM_construct_end();
+    if (!TEST_false(EVP_CIPHER_CTX_set_params(ctx, params)))
+        goto err;
+
+    /* --- Negative: inl > ivlen (64 > 12) must be rejected --- */
+    params[0] = OSSL_PARAM_construct_octet_string(
+        OSSL_CIPHER_PARAM_AEAD_TLS1_SET_IV_INV,
+        big_ivinv, sizeof(big_ivinv));
+    params[1] = OSSL_PARAM_construct_end();
+    if (!TEST_false(EVP_CIPHER_CTX_set_params(ctx, params)))
+        goto err;
+
+    /* --- Positive: inl == ivlen (12 == 12) must be accepted --- */
+    params[0] = OSSL_PARAM_construct_octet_string(
+        OSSL_CIPHER_PARAM_AEAD_TLS1_SET_IV_INV,
+        exact_ivinv, sizeof(exact_ivinv));
+    params[1] = OSSL_PARAM_construct_end();
+    if (!TEST_true(EVP_CIPHER_CTX_set_params(ctx, params)))
+        goto err;
+
+    ret = 1;
+err:
+    EVP_CIPHER_CTX_free(ctx);
+    EVP_CIPHER_free(cipher);
+    return ret;
+}
+
 int setup_tests(void)
 {
     ADD_TEST(kat_test);
     ADD_TEST(badkeylen_test);
     ADD_TEST(ivgen_test);
+    ADD_TEST(setivinv_bounds_test);
     return 1;
 }