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