Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 09 Jun 2026 19:17:36 +0000
From:      Mark Johnston <markj@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Cc:        Gordon Tetlow <gordon@FreeBSD.org>
Subject:   git: 865c8ff56693 - stable/15 - openssl: Fix multiple vulnerabilities
Message-ID:  <6a2866d0.3e82d.354576fe@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch stable/15 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=865c8ff56693db508513599cf1e03e9c612cbce2

commit 865c8ff56693db508513599cf1e03e9c612cbce2
Author:     Gordon Tetlow <gordon@FreeBSD.org>
AuthorDate: 2026-04-29 08:23:24 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-06-09 19:15:25 +0000

    openssl: Fix multiple vulnerabilities
    
    This is a rollup commit from upstream to fix:
      Reject oversized inputs in ASN1_mbstring_ncopy()
      cms: kek_unwrap_key: Fix out-of-bounds read in check-byte validation
      cms: kek_unwrap_key: test for fix out-of-bounds read in check-byte validation
      Avoid length truncation in ASN1_STRING_set
      pkcs12: verify that the pbmac1 key length is safe
      Reject potentially forged encrypted CMS AuthEnvelopedData messages
      QUIC stack must limit the number of PATH_CHALLENGE frames processed in RX
      Fix NULL dereference in QUIC address validation
      Fix potential NULL dereference processing CMS PasswordRecipientInfo
      Fix potential NULL dereference in OSSL_CRMF_ENCRYPTEDVALUE_decrypt()
      Enforce implicit rejection for CMS/PKCS#7 decryption
      Use the correct issuer when validating rootCAKeyUpdate
      Match the local q DHX parameter against the peer's q
      Apply the buffered IV on the AES-OCB EVP_Cipher() path
      Fix handling of empty-ciphertext messages in AES-GCM-SIV and AES-SIV
      Fix possible use-after-free in OpenSSL PKCS7_verify()
    
    Approved by:    so
    Obtained from:  OpenSSL
    Security:       FreeBSD-SA-26:35.openssl
    Security:       CVE-2026-7383
    Security:       CVE-2026-9076
    Security:       CVE-2026-34180
    Security:       CVE-2026-34181
    Security:       CVE-2026-34182
    Security:       CVE-2026-34183
    Security:       CVE-2026-42764
    Security:       CVE-2026-42766
    Security:       CVE-2026-42767
    Security:       CVE-2026-42768
    Security:       CVE-2026-42769
    Security:       CVE-2026-42770
    Security:       CVE-2026-45445
    Security:       CVE-2026-45446
    Security:       CVE-2026-45447
---
 crypto/openssl/crypto/asn1/a_mbstr.c               |  31 ++++-
 crypto/openssl/crypto/asn1/tasn_dec.c              |  24 ++--
 crypto/openssl/crypto/cmp/cmp_genm.c               |   6 +-
 crypto/openssl/crypto/cms/cms_enc.c                |  10 +-
 crypto/openssl/crypto/cms/cms_env.c                |   7 --
 crypto/openssl/crypto/cms/cms_pwri.c               |  13 +-
 crypto/openssl/crypto/crmf/crmf_lib.c              |  10 +-
 crypto/openssl/crypto/pkcs12/p12_mutl.c            |   8 +-
 crypto/openssl/crypto/pkcs7/pk7_doit.c             |   7 --
 crypto/openssl/crypto/pkcs7/pk7_smime.c            |   9 +-
 crypto/openssl/doc/man3/CMS_decrypt.pod            |   4 +-
 crypto/openssl/doc/man3/PKCS7_decrypt.pod          |  10 +-
 crypto/openssl/include/internal/quic_cfq.h         |   1 +
 crypto/openssl/include/internal/quic_channel.h     |   1 +
 crypto/openssl/include/internal/quic_fifd.h        |   1 +
 .../ciphers/cipher_aes_gcm_siv_hw.c                |  27 ++--
 .../implementations/ciphers/cipher_aes_ocb.c       |  13 ++
 .../implementations/ciphers/cipher_aes_siv.c       |   3 +
 .../providers/implementations/exchange/dh_exch.c   |   5 +-
 crypto/openssl/ssl/quic/quic_cfq.c                 |  15 +++
 crypto/openssl/ssl/quic/quic_channel.c             |   6 +
 crypto/openssl/ssl/quic/quic_channel_local.h       |  39 ++++++
 crypto/openssl/ssl/quic/quic_fifd.c                |  43 +++++++
 crypto/openssl/ssl/quic/quic_port.c                |   6 +-
 crypto/openssl/ssl/quic/quic_rx_depack.c           |  60 +++++----
 crypto/openssl/ssl/quic/quic_txp.c                 |   2 +
 crypto/openssl/test/cmsapitest.c                   |  48 ++++++-
 crypto/openssl/test/evp_extra_test.c               | 140 +++++++++++++++++++++
 crypto/openssl/test/recipes/80-test_cmsapi.t       |   3 +-
 .../80-test_cmsapi_data/cms_pwri_kek_oob.der       | Bin 0 -> 193 bytes
 crypto/openssl/test/recipes/80-test_pkcs12.t       |  13 +-
 .../pbmac1_256_256.bad-key-len.p12                 | Bin 0 -> 2803 bytes
 .../pbmac1_256_256.good-shorter-key-len.p12        | Bin 0 -> 2803 bytes
 33 files changed, 472 insertions(+), 93 deletions(-)

diff --git a/crypto/openssl/crypto/asn1/a_mbstr.c b/crypto/openssl/crypto/asn1/a_mbstr.c
index 2270e63d51d4..962e19b2ceaa 100644
--- a/crypto/openssl/crypto/asn1/a_mbstr.c
+++ b/crypto/openssl/crypto/asn1/a_mbstr.c
@@ -174,11 +174,27 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
         break;
 
     case MBSTRING_BMP:
+        if (nchar > INT_MAX / 2) {
+            ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
+            if (free_out) {
+                ASN1_STRING_free(dest);
+                *out = NULL;
+            }
+            return -1;
+        }
         outlen = nchar << 1;
         cpyfunc = cpy_bmp;
         break;
 
     case MBSTRING_UNIV:
+        if (nchar > INT_MAX / 4) {
+            ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
+            if (free_out) {
+                ASN1_STRING_free(dest);
+                *out = NULL;
+            }
+            return -1;
+        }
         outlen = nchar << 2;
         cpyfunc = cpy_univ;
         break;
@@ -186,8 +202,11 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
     case MBSTRING_UTF8:
         outlen = 0;
         ret = traverse_string(in, len, inform, out_utf8, &outlen);
-        if (ret < 0) {
-            ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
+        if (ret < 0) { /* error already raised in out_utf8() */
+            if (free_out) {
+                ASN1_STRING_free(dest);
+                *out = NULL;
+            }
             return -1;
         }
         cpyfunc = cpy_utf8;
@@ -270,9 +289,15 @@ static int out_utf8(unsigned long value, void *arg)
     int *outlen, len;
 
     len = UTF8_putc(NULL, -1, value);
-    if (len <= 0)
+    if (len <= 0) {
+        ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
         return len;
+    }
     outlen = arg;
+    if (*outlen > INT_MAX - len) {
+        ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
+        return -1;
+    }
     *outlen += len;
     return 1;
 }
diff --git a/crypto/openssl/crypto/asn1/tasn_dec.c b/crypto/openssl/crypto/asn1/tasn_dec.c
index 91c2e524f55b..e9532b9f48f7 100644
--- a/crypto/openssl/crypto/asn1/tasn_dec.c
+++ b/crypto/openssl/crypto/asn1/tasn_dec.c
@@ -54,7 +54,7 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval,
     const ASN1_ITEM *it,
     int tag, int aclass, char opt,
     ASN1_TLC *ctx);
-static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, long len,
     int utype, char *free_cont, const ASN1_ITEM *it);
 
 /* Table to convert tags to bit values, used for MSTRING type */
@@ -855,19 +855,24 @@ err:
 
 /* Translate ASN1 content octets into a structure */
 
-static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, long len,
     int utype, char *free_cont, const ASN1_ITEM *it)
 {
     ASN1_VALUE **opval = NULL;
     ASN1_STRING *stmp;
     ASN1_TYPE *typ = NULL;
     int ret = 0;
+    int ilen = (int)len;
     const ASN1_PRIMITIVE_FUNCS *pf;
     ASN1_INTEGER **tint;
     pf = it->funcs;
 
-    if (pf && pf->prim_c2i)
-        return pf->prim_c2i(pval, cont, len, utype, free_cont, it);
+    if (pf && pf->prim_c2i) {
+        if (len == (long)ilen)
+            return pf->prim_c2i(pval, cont, ilen, utype, free_cont, it);
+        ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LONG);
+        return 0;
+    }
     /* If ANY type clear type and set pointer to internal value */
     if (it->utype == V_ASN1_ANY) {
         if (*pval == NULL) {
@@ -885,7 +890,8 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
     }
     switch (utype) {
     case V_ASN1_OBJECT:
-        if (!ossl_c2i_ASN1_OBJECT((ASN1_OBJECT **)pval, &cont, len))
+        if (len != (long)ilen
+            || !ossl_c2i_ASN1_OBJECT((ASN1_OBJECT **)pval, &cont, ilen))
             goto err;
         break;
 
@@ -940,6 +946,10 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
     case V_ASN1_SET:
     case V_ASN1_SEQUENCE:
     default:
+        if (len != (long)ilen) {
+            ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LONG);
+            goto err;
+        }
         if (utype == V_ASN1_BMPSTRING && (len & 1)) {
             ERR_raise(ERR_LIB_ASN1, ASN1_R_BMPSTRING_IS_WRONG_LENGTH);
             goto err;
@@ -970,10 +980,10 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
         }
         /* If we've already allocated a buffer use it */
         if (*free_cont) {
-            ASN1_STRING_set0(stmp, (unsigned char *)cont /* UGLY CAST! */, len);
+            ASN1_STRING_set0(stmp, (unsigned char *)cont /* UGLY CAST! */, ilen);
             *free_cont = 0;
         } else {
-            if (!ASN1_STRING_set(stmp, cont, len)) {
+            if (!ASN1_STRING_set(stmp, cont, ilen)) {
                 ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB);
                 ASN1_STRING_free(stmp);
                 *pval = NULL;
diff --git a/crypto/openssl/crypto/cmp/cmp_genm.c b/crypto/openssl/crypto/cmp/cmp_genm.c
index bcc121f14695..ec1f03d20c1a 100644
--- a/crypto/openssl/crypto/cmp/cmp_genm.c
+++ b/crypto/openssl/crypto/cmp/cmp_genm.c
@@ -202,7 +202,7 @@ static int selfsigned_verify_cb(int ok, X509_STORE_CTX *store_ctx)
         for (i = 0; i < sk_X509_num(trust); i++) {
             issuer = sk_X509_value(trust, i);
             if ((*check_issued)(store_ctx, cert, issuer)) {
-                if (X509_add_cert(chain, cert, X509_ADD_FLAG_UP_REF))
+                if (X509_add_cert(chain, issuer, X509_ADD_FLAG_UP_REF))
                     ok = 1;
                 break;
             }
@@ -235,6 +235,7 @@ static int verify_ss_cert(OSSL_LIB_CTX *libctx, const char *propq,
     if ((csc = X509_STORE_CTX_new_ex(libctx, propq)) == NULL
         || !X509_STORE_CTX_init(csc, ts, target, untrusted))
         goto err;
+    X509_STORE_CTX_set_flags(csc, X509_V_FLAG_CHECK_SS_SIGNATURE);
     X509_STORE_CTX_set_verify_cb(csc, selfsigned_verify_cb);
     ok = X509_verify_cert(csc) > 0;
 
@@ -253,7 +254,8 @@ verify_ss_cert_trans(OSSL_CMP_CTX *ctx, X509 *trusted /* may be NULL */,
     int res = 0;
 
     if (trusted != NULL) {
-        X509_VERIFY_PARAM *vpm = X509_STORE_get0_param(ts);
+        X509_VERIFY_PARAM *vpm = (ts == NULL) ? NULL
+                                              : X509_STORE_get0_param(ts);
 
         if ((ts = X509_STORE_new()) == NULL)
             return 0;
diff --git a/crypto/openssl/crypto/cms/cms_enc.c b/crypto/openssl/crypto/cms/cms_enc.c
index 08afb5ab114b..ba7082cebd72 100644
--- a/crypto/openssl/crypto/cms/cms_enc.c
+++ b/crypto/openssl/crypto/cms/cms_enc.c
@@ -109,13 +109,15 @@ BIO *ossl_cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec,
                 goto err;
             }
             piv = aparams.iv;
-            if (ec->taglen > 0
-                && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
-                       ec->taglen, ec->tag)
-                    <= 0) {
+
+            if (ec->taglen < 4 || ec->taglen > 16
+                || EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, (int)ec->taglen, ec->tag) <= 0) {
                 ERR_raise(ERR_LIB_CMS, CMS_R_CIPHER_AEAD_SET_TAG_ERROR);
                 goto err;
             }
+        } else if (auth) {
+            ERR_raise(ERR_LIB_CMS, CMS_R_UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM);
+            goto err;
         }
     }
     len = EVP_CIPHER_CTX_get_key_length(ctx);
diff --git a/crypto/openssl/crypto/cms/cms_env.c b/crypto/openssl/crypto/cms/cms_env.c
index 0828d157fad6..70dd59c06169 100644
--- a/crypto/openssl/crypto/cms/cms_env.c
+++ b/crypto/openssl/crypto/cms/cms_env.c
@@ -619,13 +619,6 @@ static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms,
     if (!ossl_cms_env_asn1_ctrl(ri, 1))
         goto err;
 
-    if (EVP_PKEY_is_a(pkey, "RSA"))
-        /* upper layer CMS code incorrectly assumes that a successful RSA
-         * decryption means that the key matches ciphertext (which never
-         * was the case, implicit rejection or not), so to make it work
-         * disable implicit rejection for RSA keys */
-        EVP_PKEY_CTX_ctrl_str(ktri->pctx, "rsa_pkcs1_implicit_rejection", "0");
-
     if (evp_pkey_decrypt_alloc(ktri->pctx, &ek, &eklen, fixlen,
             ktri->encryptedKey->data,
             ktri->encryptedKey->length)
diff --git a/crypto/openssl/crypto/cms/cms_pwri.c b/crypto/openssl/crypto/cms/cms_pwri.c
index d62dbbde881b..faf6a164669b 100644
--- a/crypto/openssl/crypto/cms/cms_pwri.c
+++ b/crypto/openssl/crypto/cms/cms_pwri.c
@@ -200,18 +200,18 @@ static int kek_unwrap_key(unsigned char *out, size_t *outlen,
     const unsigned char *in, size_t inlen,
     EVP_CIPHER_CTX *ctx)
 {
-    size_t blocklen = EVP_CIPHER_CTX_get_block_size(ctx);
+    int blocklen = EVP_CIPHER_CTX_get_block_size(ctx);
     unsigned char *tmp;
     int outl, rv = 0;
 
-    if (blocklen == 0)
+    if (blocklen < 4)
         return 0;
 
-    if (inlen < 2 * blocklen) {
+    if (inlen < 2 * (size_t)blocklen) {
         /* too small */
         return 0;
     }
-    if (inlen % blocklen) {
+    if (inlen > INT_MAX || inlen % blocklen) {
         /* Invalid size */
         return 0;
     }
@@ -367,6 +367,11 @@ int ossl_cms_RecipientInfo_pwri_crypt(const CMS_ContentInfo *cms,
 
     /* Finish password based key derivation to setup key in "ctx" */
 
+    if (algtmp == NULL) {
+        ERR_raise_data(ERR_LIB_CMS, CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER,
+            "Missing KeyDerivationAlgorithm");
+        goto err;
+    }
     if (!EVP_PBE_CipherInit_ex(algtmp->algorithm,
             (char *)pwri->pass, (int)pwri->passlen,
             algtmp->parameter, kekctx, en_de,
diff --git a/crypto/openssl/crypto/crmf/crmf_lib.c b/crypto/openssl/crypto/crmf/crmf_lib.c
index d5c8983b2fd4..34477d52662d 100644
--- a/crypto/openssl/crypto/crmf/crmf_lib.c
+++ b/crypto/openssl/crypto/crmf/crmf_lib.c
@@ -766,6 +766,7 @@ unsigned char *OSSL_CRMF_ENCRYPTEDVALUE_decrypt(const OSSL_CRMF_ENCRYPTEDVALUE *
     EVP_CIPHER *cipher = NULL; /* used cipher */
     int cikeysize = 0; /* key size from cipher */
     unsigned char *iv = NULL; /* initial vector for symmetric encryption */
+    int iv_len; /* iv length */
     unsigned char *out = NULL; /* decryption output buffer */
     int n, ret = 0;
     EVP_PKEY_CTX *pkctx = NULL; /* private key context */
@@ -820,11 +821,12 @@ unsigned char *OSSL_CRMF_ENCRYPTEDVALUE_decrypt(const OSSL_CRMF_ENCRYPTEDVALUE *
     } else {
         goto end;
     }
-    if ((iv = OPENSSL_malloc(EVP_CIPHER_get_iv_length(cipher))) == NULL)
+    iv_len = EVP_CIPHER_get_iv_length(cipher);
+    if ((iv = OPENSSL_malloc(iv_len)) == NULL)
         goto end;
-    if (ASN1_TYPE_get_octetstring(enc->symmAlg->parameter, iv,
-            EVP_CIPHER_get_iv_length(cipher))
-        != EVP_CIPHER_get_iv_length(cipher)) {
+    if (enc->symmAlg->parameter == NULL
+        || ASN1_TYPE_get_octetstring(enc->symmAlg->parameter, iv, iv_len)
+            != iv_len) {
         ERR_raise(ERR_LIB_CRMF, CRMF_R_MALFORMED_IV);
         goto end;
     }
diff --git a/crypto/openssl/crypto/pkcs12/p12_mutl.c b/crypto/openssl/crypto/pkcs12/p12_mutl.c
index 01956252df76..15072e12f26b 100644
--- a/crypto/openssl/crypto/pkcs12/p12_mutl.c
+++ b/crypto/openssl/crypto/pkcs12/p12_mutl.c
@@ -144,11 +144,13 @@ static int PBMAC1_PBKDF2_HMAC(OSSL_LIB_CTX *ctx, const char *propq,
     }
     pbkdf2_salt = pbkdf2_param->salt->value.octet_string;
 
-    /* RFC 9579 specifies missing key length as invalid */
+    /* RFC 9879 specifies missing key length as invalid */
     if (pbkdf2_param->keylength != NULL)
         keylen = ASN1_INTEGER_get(pbkdf2_param->keylength);
-    if (keylen <= 0 || keylen > EVP_MAX_MD_SIZE) {
-        ERR_raise(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR);
+    /* RFC 9879 specifies too short key length as untrustworthy too */
+    if (keylen < 20 || keylen > EVP_MAX_MD_SIZE) {
+        ERR_raise_data(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR,
+            "Invalid Key length (%d is not in the range 20..64)", keylen);
         goto err;
     }
 
diff --git a/crypto/openssl/crypto/pkcs7/pk7_doit.c b/crypto/openssl/crypto/pkcs7/pk7_doit.c
index d6513cf3a379..1ec7895fc197 100644
--- a/crypto/openssl/crypto/pkcs7/pk7_doit.c
+++ b/crypto/openssl/crypto/pkcs7/pk7_doit.c
@@ -203,13 +203,6 @@ static int pkcs7_decrypt_rinfo(unsigned char **pek, int *peklen,
     if (EVP_PKEY_decrypt_init(pctx) <= 0)
         goto err;
 
-    if (EVP_PKEY_is_a(pkey, "RSA"))
-        /* upper layer pkcs7 code incorrectly assumes that a successful RSA
-         * decryption means that the key matches ciphertext (which never
-         * was the case, implicit rejection or not), so to make it work
-         * disable implicit rejection for RSA keys */
-        EVP_PKEY_CTX_ctrl_str(pctx, "rsa_pkcs1_implicit_rejection", "0");
-
     ret = evp_pkey_decrypt_alloc(pctx, &ek, &eklen, fixlen,
         ri->enc_key->data, ri->enc_key->length);
     if (ret <= 0)
diff --git a/crypto/openssl/crypto/pkcs7/pk7_smime.c b/crypto/openssl/crypto/pkcs7/pk7_smime.c
index 97f20058979f..dc003ee2affd 100644
--- a/crypto/openssl/crypto/pkcs7/pk7_smime.c
+++ b/crypto/openssl/crypto/pkcs7/pk7_smime.c
@@ -222,6 +222,7 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store,
     int i, j = 0, k, ret = 0;
     BIO *p7bio = NULL;
     BIO *tmpout = NULL;
+    BIO *next = NULL;
     const PKCS7_CTX *p7_ctx;
 
     if (p7 == NULL) {
@@ -352,9 +353,11 @@ err:
         BIO_free(tmpout);
     X509_STORE_CTX_free(cert_ctx);
     OPENSSL_free(buf);
-    if (indata != NULL)
-        BIO_pop(p7bio);
-    BIO_free_all(p7bio);
+    while (p7bio != NULL && p7bio != indata) {
+        next = BIO_pop(p7bio);
+        BIO_free(p7bio);
+        p7bio = next;
+    }
     sk_X509_free(signers);
     sk_X509_free(untrusted);
     return ret;
diff --git a/crypto/openssl/doc/man3/CMS_decrypt.pod b/crypto/openssl/doc/man3/CMS_decrypt.pod
index 121b74a30a10..66a94287b6f5 100644
--- a/crypto/openssl/doc/man3/CMS_decrypt.pod
+++ b/crypto/openssl/doc/man3/CMS_decrypt.pod
@@ -68,7 +68,7 @@ then the above behaviour is modified and an error B<is> returned if no
 recipient encrypted key can be decrypted B<without> generating a random
 content encryption key. Applications should use this flag with
 B<extreme caution> especially in automated gateways as it can leave them
-open to attack.
+open to attack. See L<EVP_PKEY_decrypt(3)> for more details.
 
 It is possible to determine the correct recipient key by other means (for
 example looking them up in a database) and setting them in the CMS structure
@@ -103,7 +103,7 @@ mentioned in CMS_verify() also applies to CMS_decrypt().
 
 =head1 SEE ALSO
 
-L<ERR_get_error(3)>, L<CMS_encrypt(3)>
+L<ERR_get_error(3)>, L<CMS_encrypt(3)>, L<EVP_PKEY_decrypt(3)>
 
 =head1 HISTORY
 
diff --git a/crypto/openssl/doc/man3/PKCS7_decrypt.pod b/crypto/openssl/doc/man3/PKCS7_decrypt.pod
index aea15937ab86..cfb5b3f87376 100644
--- a/crypto/openssl/doc/man3/PKCS7_decrypt.pod
+++ b/crypto/openssl/doc/man3/PKCS7_decrypt.pod
@@ -22,6 +22,14 @@ B<flags> is an optional set of flags.
 Although the recipients certificate is not needed to decrypt the data it is needed
 to locate the appropriate (of possible several) recipients in the PKCS#7 structure.
 
+When RSA PKCS#1 v1.5 Key Transport is in use, the invoked EVP_PKEY_decrypt()
+will use implicit rejection mechanism. It always returns the result of RSA
+decryption of the symmetric key to avoid Marvin attack. This result is
+deterministic and can happen to match the symmetric cipher used for the content
+encryption. In case when the certificate is not provided, the last
+RecipientInfo producing the key looking valid will be used. It may cause
+getting garbage content on decryption.
+
 The following flags can be passed in the B<flags> parameter.
 
 If the B<PKCS7_TEXT> flag is set MIME headers for type B<text/plain> are deleted
@@ -43,7 +51,7 @@ mentioned in PKCS7_sign() also applies to PKCS7_verify().
 
 =head1 SEE ALSO
 
-L<ERR_get_error(3)>, L<PKCS7_encrypt(3)>
+L<ERR_get_error(3)>, L<PKCS7_encrypt(3)>, L<EVP_PKEY_decrypt(3)>
 
 =head1 COPYRIGHT
 
diff --git a/crypto/openssl/include/internal/quic_cfq.h b/crypto/openssl/include/internal/quic_cfq.h
index 0b2a3a4cb2d6..96c8d89eb600 100644
--- a/crypto/openssl/include/internal/quic_cfq.h
+++ b/crypto/openssl/include/internal/quic_cfq.h
@@ -149,6 +149,7 @@ QUIC_CFQ_ITEM *ossl_quic_cfq_get_priority_head(const QUIC_CFQ *cfq,
 QUIC_CFQ_ITEM *ossl_quic_cfq_item_get_priority_next(const QUIC_CFQ_ITEM *item,
     uint32_t pn_space);
 
+int ossl_quic_cfq_discard_unreliable(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item);
 #endif
 
 #endif
diff --git a/crypto/openssl/include/internal/quic_channel.h b/crypto/openssl/include/internal/quic_channel.h
index b917b966abeb..cfaeab728178 100644
--- a/crypto/openssl/include/internal/quic_channel.h
+++ b/crypto/openssl/include/internal/quic_channel.h
@@ -468,6 +468,7 @@ int ossl_quic_bind_channel(QUIC_CHANNEL *ch, const BIO_ADDR *peer,
     const QUIC_CONN_ID *scid, const QUIC_CONN_ID *dcid,
     const QUIC_CONN_ID *odcid);
 
+void ossl_ch_reset_rx_state(QUIC_CHANNEL *ch);
 #endif
 
 #endif
diff --git a/crypto/openssl/include/internal/quic_fifd.h b/crypto/openssl/include/internal/quic_fifd.h
index 4ea7a2e0d226..afa330cbc4a2 100644
--- a/crypto/openssl/include/internal/quic_fifd.h
+++ b/crypto/openssl/include/internal/quic_fifd.h
@@ -83,6 +83,7 @@ int ossl_quic_fifd_pkt_commit(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *pkt);
 void ossl_quic_fifd_set_qlog_cb(QUIC_FIFD *fifd, QLOG *(*get_qlog_cb)(void *arg),
     void *arg);
 
+void ossl_quic_fifd_pkt_discard_unreliable(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *tpkt);
 #endif
 
 #endif
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
index d0b6ae4b070d..5bdc567b4bb1 100644
--- a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
@@ -58,6 +58,9 @@ static int aes_gcm_siv_initkey(void *vctx)
     memset(&data, 0, sizeof(data));
     memcpy(&data.block[sizeof(data.counter)], ctx->nonce, NONCE_SIZE);
 
+    ctx->generated_tag = 0;
+    memset(ctx->tag, 0, TAG_SIZE);
+
     /* msg_auth_key is always 16 bytes in size, regardless of AES128/AES256 */
     /* counter is stored little-endian */
     for (i = 0; i < BLOCK_SIZE; i += 8) {
@@ -134,17 +137,6 @@ static int aes_gcm_siv_aad(PROV_AES_GCM_SIV_CTX *ctx,
     return 1;
 }
 
-static int aes_gcm_siv_finish(PROV_AES_GCM_SIV_CTX *ctx)
-{
-    int ret = 0;
-
-    if (ctx->enc)
-        return ctx->generated_tag;
-    ret = !CRYPTO_memcmp(ctx->tag, ctx->user_tag, sizeof(ctx->tag));
-    ret &= ctx->have_user_tag;
-    return ret;
-}
-
 static int aes_gcm_siv_encrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *in,
     unsigned char *out, size_t len)
 {
@@ -271,6 +263,19 @@ static int aes_gcm_siv_decrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *i
     return !error;
 }
 
+static int aes_gcm_siv_finish(PROV_AES_GCM_SIV_CTX *ctx)
+{
+    int ret = 0;
+
+    if (ctx->enc)
+        return ctx->generated_tag;
+    if (!ctx->generated_tag)
+        aes_gcm_siv_decrypt(ctx, NULL, NULL, 0);
+    ret = !CRYPTO_memcmp(ctx->tag, ctx->user_tag, sizeof(ctx->tag));
+    ret &= ctx->have_user_tag;
+    return ret;
+}
+
 static int aes_gcm_siv_cipher(void *vctx, unsigned char *out,
     const unsigned char *in, size_t len)
 {
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c
index b724c425e392..99254cb49a88 100644
--- a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c
@@ -514,6 +514,19 @@ static int aes_ocb_cipher(void *vctx, unsigned char *out, size_t *outl,
         return 0;
     }
 
+    /*
+     * Mirror the streaming handler: refuse if the key has not been set,
+     * and push the buffered IV into the OCB context before any data is
+     * processed.  Without this, CRYPTO_ocb128_encrypt/decrypt runs with
+     * Offset_0 = 0 regardless of the caller's IV -- catastrophic
+     * (key, nonce) reuse, and a subsequent EVP_*Final_ex() emits a tag
+     * that is a function of (key, iv) only.
+     */
+    if (!ctx->key_set || !update_iv(ctx)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+        return 0;
+    }
+
     if (!aes_generic_ocb_cipher(ctx, in, out, inl)) {
         ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
         return 0;
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c
index 96f26757abe2..754e0757cda3 100644
--- a/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c
@@ -192,6 +192,7 @@ static int aes_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[])
     PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx;
     const OSSL_PARAM *p;
     unsigned int speed = 0;
+    SIV128_CONTEXT *sctx = &ctx->siv;
 
     if (ossl_param_is_empty(params))
         return 1;
@@ -226,6 +227,8 @@ static int aes_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[])
         if (keylen != ctx->keylen)
             return 0;
     }
+    sctx->final_ret = -1;
+
     return 1;
 }
 
diff --git a/crypto/openssl/providers/implementations/exchange/dh_exch.c b/crypto/openssl/providers/implementations/exchange/dh_exch.c
index 94d4254ed5d2..2bfefc0aedf4 100644
--- a/crypto/openssl/providers/implementations/exchange/dh_exch.c
+++ b/crypto/openssl/providers/implementations/exchange/dh_exch.c
@@ -146,12 +146,15 @@ static int dh_init(void *vpdhctx, void *vdh, const OSSL_PARAM params[])
 static int dh_match_params(DH *priv, DH *peer)
 {
     int ret;
+    int ignore_q = 1;
     FFC_PARAMS *dhparams_priv = ossl_dh_get0_params(priv);
     FFC_PARAMS *dhparams_peer = ossl_dh_get0_params(peer);
 
+    if (dhparams_priv != NULL && dhparams_priv->q != NULL)
+        ignore_q = 0;
     ret = dhparams_priv != NULL
         && dhparams_peer != NULL
-        && ossl_ffc_params_cmp(dhparams_priv, dhparams_peer, 1);
+        && ossl_ffc_params_cmp(dhparams_priv, dhparams_peer, ignore_q);
     if (!ret)
         ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
     return ret;
diff --git a/crypto/openssl/ssl/quic/quic_cfq.c b/crypto/openssl/ssl/quic/quic_cfq.c
index 3c59234ff0ff..16818e55f57d 100644
--- a/crypto/openssl/ssl/quic/quic_cfq.c
+++ b/crypto/openssl/ssl/quic/quic_cfq.c
@@ -7,6 +7,7 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include "internal/quic_channel.h"
 #include "internal/quic_cfq.h"
 #include "internal/numbers.h"
 
@@ -307,6 +308,20 @@ void ossl_quic_cfq_mark_lost(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item,
     }
 }
 
+int ossl_quic_cfq_discard_unreliable(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item)
+{
+    int discarded;
+
+    if (ossl_quic_cfq_item_is_unreliable(item)) {
+        ossl_quic_cfq_release(cfq, item);
+        discarded = 1;
+    } else {
+        discarded = 0;
+    }
+
+    return discarded;
+}
+
 /*
  * Releases a CFQ item. The item may be in either state (NEW or TX) prior to the
  * call. The QUIC_CFQ_ITEM pointer must not be used following this call.
diff --git a/crypto/openssl/ssl/quic/quic_channel.c b/crypto/openssl/ssl/quic/quic_channel.c
index 13692e5bd09e..5f81a8560d5f 100644
--- a/crypto/openssl/ssl/quic/quic_channel.c
+++ b/crypto/openssl/ssl/quic/quic_channel.c
@@ -2213,6 +2213,12 @@ static void ch_rx_check_forged_pkt_limit(QUIC_CHANNEL *ch)
         "forgery limit");
 }
 
+void ossl_ch_reset_rx_state(QUIC_CHANNEL *ch)
+{
+    ch->did_crypto_frame = 0;
+    ch->seen_path_challenge = 0;
+}
+
 /* Process queued incoming packets and handle frames, if any. */
 static int ch_rx(QUIC_CHANNEL *ch, int channel_only, int *notify_other_threads)
 {
diff --git a/crypto/openssl/ssl/quic/quic_channel_local.h b/crypto/openssl/ssl/quic/quic_channel_local.h
index ae443fccca1e..e40b4901cbc7 100644
--- a/crypto/openssl/ssl/quic/quic_channel_local.h
+++ b/crypto/openssl/ssl/quic/quic_channel_local.h
@@ -12,6 +12,28 @@
 #include "internal/quic_stream_map.h"
 #include "internal/quic_tls.h"
 
+/*
+ * This is a part of PATH_CHALLENGE flood [1] mitigation. This limits the
+ * number of PATH_CHALLENGE frames  QUIC stack is willing to process for
+ * connection. Local QUIC stack creates PATH_RESPONSE frame for PATH_CHALLENGE
+ * frame it receives from remote peer. The response frame is put Control Frame
+ * Queue waiting to be dispatched. The PATH_RESPONSE frame is removed from CFQ
+ * after it is dispatched. The QUIC_PATH_RESPONSE_QLEN limits the number of
+ * PATH_RESPONSE frames waiting to be dispatched. No new PATH_RESPONSE frames
+ * are inserted into CFQ if queue limit is exceeded.
+ *
+ * QUIC implementations use different limits for PATH_RESPONSE queue lengths:
+ *    quic-go defines maxPathResponses as 256
+ *    quiche from cloadflare sets DEFAULT_MAX_PATH_CHALLENGE_RX_QUEUE_LEN to 3
+ *    t-quic from tencent chooses MAX_PATH_CHALS_RECV to be 8
+ *
+ * OpenSSL here introduces QUIC_PATH_RESPONSE_QLEN as 32.
+ *
+ * [1] https://www.ietf.org/archive/id/draft-chen-quic-logical-vuln-mitigations-00.txt
+ *     (section 4.2)
+ */
+#define QUIC_PATH_RESPONSE_QLEN 32
+
 /*
  * QUIC Channel Structure
  * ======================
@@ -457,6 +479,18 @@ struct quic_channel_st {
 
     /* Has qlog been requested? */
     unsigned int is_tserver_ch : 1;
+    /*
+     * RFC 9000 Section 9.2.1 says:
+     *      However, an endpoint SHOULD NOT send multiple
+     *      PATH_CHALLENGE frames in a single packet.
+     * The counter here allows us to detect multiple presence
+     * of PATH_CHALLENGE frame in packet. We process only the
+     * first PATH_CHALLENGE frame found in packet. Remaining PATH_CHALLENGE
+     * frames are ignored.
+     * seen_path_challenge flag is always reset before
+     * ossl_quic_handle_frames() gets called.
+     */
+    unsigned int seen_path_challenge : 1;
 
     /* Saved error stack in case permanent error was encountered */
     ERR_STATE *err_state;
@@ -467,6 +501,11 @@ struct quic_channel_st {
 
     /* Title for qlog purposes. We own this copy. */
     char *qlog_title;
+    /*
+     * number of path responses waiting to be dispatched
+     * from control frame queue (CFQ)
+     */
+    unsigned int path_response_limit;
 };
 
 #endif
diff --git a/crypto/openssl/ssl/quic/quic_fifd.c b/crypto/openssl/ssl/quic/quic_fifd.c
index 03b8cebd3057..e80483b501d7 100644
--- a/crypto/openssl/ssl/quic/quic_fifd.c
+++ b/crypto/openssl/ssl/quic/quic_fifd.c
@@ -310,3 +310,46 @@ void ossl_quic_fifd_set_qlog_cb(QUIC_FIFD *fifd, QLOG *(*get_qlog_cb)(void *arg)
     fifd->get_qlog_cb = get_qlog_cb;
     fifd->get_qlog_cb_arg = get_qlog_cb_arg;
 }
+
+static void txpim_pkt_remove_cfq_item(QUIC_TXPIM_PKT *pkt, QUIC_CFQ_ITEM *cfq_item)
+{
+    QUIC_CFQ_ITEM *prev = cfq_item->pkt_prev;
+
+    if (prev != NULL) {
+        prev->pkt_next = cfq_item->pkt_next;
+    } else {
+        pkt->retx_head = cfq_item->pkt_next;
+    }
+
+    if (cfq_item->pkt_next != NULL)
+        cfq_item->pkt_next->pkt_prev = prev;
+
+    cfq_item->pkt_prev = NULL;
+    cfq_item->pkt_next = NULL;
+}
+
+void ossl_quic_fifd_pkt_discard_unreliable(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *pkt)
+{
+    QUIC_CFQ_ITEM *cfq_item, *cfq_next;
+
+    /*
+     * The packet has been written to network. We can discard frames we don't
+     * retransmit when loss is detected.
+     */
+    cfq_item = pkt->retx_head;
+    while (cfq_item != NULL) {
+        /*
+         * Discarded items are moved to free list. If item
+         * got moved to free list we must also remove it from
+         * cfq list kept in pkt, so ACKM does not find it when
+         * receives an ACK for pkt.
+         */
+        if (ossl_quic_cfq_discard_unreliable(fifd->cfq, cfq_item)) {
+            cfq_next = cfq_item->pkt_next;
+            txpim_pkt_remove_cfq_item(pkt, cfq_item);
+            cfq_item = cfq_next;
+        } else {
+            cfq_item = cfq_item->pkt_next;
+        }
+    }
+}
diff --git a/crypto/openssl/ssl/quic/quic_port.c b/crypto/openssl/ssl/quic/quic_port.c
index 1e247e1ec624..dc79485b96a5 100644
--- a/crypto/openssl/ssl/quic/quic_port.c
+++ b/crypto/openssl/ssl/quic/quic_port.c
@@ -1666,8 +1666,10 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg,
          * forget qrx so channel can create a new one
          * with valid initial encryption level keys.
          */
-        qrx_src = qrx;
-        qrx = NULL;
+        if (qrx != NULL) {
+            qrx_src = qrx;
+            qrx = NULL;
+        }
     }
 
     port_bind_channel(port, &e->peer, &scid, &hdr.dst_conn_id,
diff --git a/crypto/openssl/ssl/quic/quic_rx_depack.c b/crypto/openssl/ssl/quic/quic_rx_depack.c
index 786af9b4c221..1bdb43b7d639 100644
--- a/crypto/openssl/ssl/quic/quic_rx_depack.c
+++ b/crypto/openssl/ssl/quic/quic_rx_depack.c
@@ -931,6 +931,12 @@ static int depack_do_frame_retire_conn_id(PACKET *pkt,
 
 static void free_path_response(unsigned char *buf, size_t buf_len, void *arg)
 {
+    QUIC_CHANNEL *ch = (QUIC_CHANNEL *)arg;
+
+    assert(ch->path_response_limit > 0);
+
+    ch->path_response_limit--;
+
     OPENSSL_free(buf);
 }
 
@@ -951,33 +957,39 @@ static int depack_do_frame_path_challenge(PACKET *pkt,
         return 0;
     }
 
-    /*
-     * RFC 9000 s. 8.2.2: On receiving a PATH_CHALLENGE frame, an endpoint MUST
-     * respond by echoing the data contained in the PATH_CHALLENGE frame in a
-     * PATH_RESPONSE frame.
-     *
-     * TODO(QUIC FUTURE): We should try to avoid allocation here in the future.
-     */
-    encoded_len = sizeof(uint64_t) + 1;
-    if ((encoded = OPENSSL_malloc(encoded_len)) == NULL)
-        goto err;
+    if (ch->seen_path_challenge == 0
+        && ch->path_response_limit < QUIC_PATH_RESPONSE_QLEN) {
+        /*
+         * RFC 9000 s. 8.2.2: On receiving a PATH_CHALLENGE frame, an endpoint
+         * MUST respond by echoing the data contained in the PATH_CHALLENGE
+         * frame in a PATH_RESPONSE frame.
+         *
+         * TODO(QUIC FUTURE): We should try to avoid allocation here in the
+         * future.
+         */
+        encoded_len = sizeof(uint64_t) + 1;
+        if ((encoded = OPENSSL_malloc(encoded_len)) == NULL)
+            goto err;
 
-    if (!WPACKET_init_static_len(&wpkt, encoded, encoded_len, 0))
-        goto err;
+        if (!WPACKET_init_static_len(&wpkt, encoded, encoded_len, 0))
+            goto err;
 
-    if (!ossl_quic_wire_encode_frame_path_response(&wpkt, frame_data)) {
-        WPACKET_cleanup(&wpkt);
-        goto err;
-    }
+        if (!ossl_quic_wire_encode_frame_path_response(&wpkt, frame_data)) {
+            WPACKET_cleanup(&wpkt);
+            goto err;
+        }
 
-    WPACKET_finish(&wpkt);
+        WPACKET_finish(&wpkt);
 
-    if (!ossl_quic_cfq_add_frame(ch->cfq, 0, QUIC_PN_SPACE_APP,
-            OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE,
-            QUIC_CFQ_ITEM_FLAG_UNRELIABLE,
-            encoded, encoded_len,
-            free_path_response, NULL))
-        goto err;
+        if (!ossl_quic_cfq_add_frame(ch->cfq, 0, QUIC_PN_SPACE_APP,
+                OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE,
+                QUIC_CFQ_ITEM_FLAG_UNRELIABLE,
+                encoded, encoded_len,
+                free_path_response, ch))
+            goto err;
+        ch->seen_path_challenge = 1;
+        ch->path_response_limit++;
+    }
 
     return 1;
 
@@ -1432,7 +1444,7 @@ int ossl_quic_handle_frames(QUIC_CHANNEL *ch, OSSL_QRX_PKT *qpacket)
     if (ch == NULL)
         return 0;
 
-    ch->did_crypto_frame = 0;
+    ossl_ch_reset_rx_state(ch);
 
     /* Initialize |ackm_data| (and reinitialize |ok|)*/
     memset(&ackm_data, 0, sizeof(ackm_data));
diff --git a/crypto/openssl/ssl/quic/quic_txp.c b/crypto/openssl/ssl/quic/quic_txp.c
index 44aaad868d2f..b2565c1a9fee 100644
--- a/crypto/openssl/ssl/quic/quic_txp.c
+++ b/crypto/openssl/ssl/quic/quic_txp.c
@@ -3133,6 +3133,8 @@ static int txp_pkt_commit(OSSL_QUIC_TX_PACKETISER *txp,
             --probe_info->pto[pn_space];
     }
 
+    ossl_quic_fifd_pkt_discard_unreliable(&txp->fifd, tpkt);
+
     return rc;
 }
 
diff --git a/crypto/openssl/test/cmsapitest.c b/crypto/openssl/test/cmsapitest.c
index 0752d14df09c..d908bc6fc4c4 100644
--- a/crypto/openssl/test/cmsapitest.c
+++ b/crypto/openssl/test/cmsapitest.c
@@ -21,6 +21,7 @@ static X509 *cert = NULL;
 static EVP_PKEY *privkey = NULL;
 static char *derin = NULL;
 static char *too_long_iv_cms_in = NULL;
+static char *pwri_kek_oob_der_in = NULL;
 
 static int test_encrypt_decrypt(const EVP_CIPHER *cipher)
 {
@@ -512,7 +513,48 @@ end:
     return ret;
 }
 
-OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile\n")
+/*
+ * CMS EnvelopedData with a single PasswordRecipientInfo using
+ * id-alg-PWRI-KEK and an AES-128-CFB key encryption cipher
+ * (1-byte effective block size).  The encryptedKey OCTET STRING is
+ * only two bytes long, so the wrapped key buffer is shorter than
+ * the seven octets read by the check-byte test in kek_unwrap_key().
+ * Prior to CVE-2026-9076 this triggered an out-of-bounds heap read;
+ * CMS_decrypt() must now fail cleanly.
+ */
+static int test_pwri_kek_unwrap_short_encrypted_key(void)
+{
+    BIO *in = NULL;
+    CMS_ContentInfo *cms = NULL;
+    unsigned long err = 0;
+    int ret = 0;
+
+    if (!TEST_ptr(in = BIO_new_file(pwri_kek_oob_der_in, "rb"))
+        || !TEST_ptr(cms = d2i_CMS_bio(in, NULL)))
+        goto end;
+
+    /*
+     * The unwrap is attempted eagerly inside CMS_decrypt_set1_password().
+     * It must fail cleanly (no OOB read) and report CMS_R_UNWRAP_FAILURE.
+     */
+    if (!TEST_false(CMS_decrypt_set1_password(cms,
+            (unsigned char *)"password", -1)))
+        goto end;
+
+    err = ERR_peek_last_error();
+    if (!TEST_int_eq(ERR_GET_LIB(err), ERR_LIB_CMS)
+        || !TEST_int_eq(ERR_GET_REASON(err), CMS_R_UNWRAP_FAILURE))
+        goto end;
+
+    ERR_clear_error();
+    ret = 1;
+end:
+    CMS_ContentInfo_free(cms);
+    BIO_free(in);
+    return ret;
+}
+
+OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile tooLongIVpem pwriKekOobDer\n")
 
 int setup_tests(void)
 {
@@ -527,7 +569,8 @@ int setup_tests(void)
     if (!TEST_ptr(certin = test_get_argument(0))
         || !TEST_ptr(privkeyin = test_get_argument(1))
         || !TEST_ptr(derin = test_get_argument(2))
-        || !TEST_ptr(too_long_iv_cms_in = test_get_argument(3)))
+        || !TEST_ptr(too_long_iv_cms_in = test_get_argument(3))
+        || !TEST_ptr(pwri_kek_oob_der_in = test_get_argument(4)))
*** 235 LINES SKIPPED ***


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a2866d0.3e82d.354576fe>