From ddb327ab19b29c64c1752cef26c460ca0b7064ec Mon Sep 17 00:00:00 2001
From: Ken Zalewski <ken.zalewski@gmail.com>
Date: Tue, 16 Jun 2026 01:33:01 -0400
Subject: [PATCH] Patch to openssl-1.1.1zh.  This version addresses five
 vulnerabilities:  CVE-2026-34180, CVE-2026-42766, CVE-2026-45447,
 CVE-2026-7383, and CVE-2026-9076

---
 CHANGES                    | 86 ++++++++++++++++++++++++++++++++++++++
 NEWS                       | 11 +++++
 README                     |  2 +-
 crypto/asn1/a_mbstr.c      | 39 +++++++++++++++--
 crypto/asn1/tasn_dec.c     | 25 +++++++----
 crypto/cms/cms_pwri.c      | 15 +++++--
 crypto/pkcs7/pk7_smime.c   |  9 ++--
 include/openssl/opensslv.h |  4 +-
 8 files changed, 171 insertions(+), 20 deletions(-)

diff --git a/CHANGES b/CHANGES
index f7e81b3..e2da990 100644
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,92 @@
  https://github.com/openssl/openssl/commits/ and pick the appropriate
  release branch.
 
+ Changes between 1.1.1zg and 1.1.1zh [9 Jun 2026]
+
+ *) Fixed heap use-after-free in `PKCS7_verify()`.
+    Severity: High
+
+    Issue summary: A specially crafted PKCS#7 or S/MIME signed message could
+    trigger a use-after-free during PKCS#7 signature verification.
+
+    Impact summary: A use-after-free may result in process crashes, heap
+    corruption, or, potentially, remote code execution.
+
+    Reported by: Thai Duong (Calif.io in collaboration with Claude
+    and Anthropic Research).
+
+    (CVE-2026-45447)
+    [Igor Ustinov]
+
+ *) Fixed possible heap buffer overflow in ASN.1 multibyte string conversion.
+
+    Severity: Low
+
+    Issue summary: A signed integer overflow when sizing the destination
+    buffer for Unicode output in `ASN1_mbstring_ncopy()` can lead to a heap
+    buffer overflow.
+
+    Impact summary: A heap buffer overflow may lead to a crash or possibly
+    attacker controlled code execution or other undefined behaviour.
+
+    Reported by: Zehua Qiao and Jinwen He.
+
+    (CVE-2026-7383)
+    [Viktor Dukhovni]
+
+ *) Fixed out-of-bounds read in CMS password-based decryption.
+
+    Severity: Low
+
+    Issue summary: When CMS password-based decryption ([RFC 3211]/PWRI key
+    unwrap) processes attacker-supplied CMS data, an attacker-chosen stream-mode
+    KEK cipher can trigger a heap out-of-bounds read in `kek_unwrap_key()`.
+
+    Impact summary: A heap buffer over-read may trigger a crash, which leads
+    to Denial of Service for an application if the input buffer ends at a memory
+    page boundary and the following page is unmapped.  There is no information
+    disclosure, as the over-read bytes are not revealed to the attacker.
+
+    Reported by: Bhabani Sankar Das and Haruki Oyama (Waseda University).
+
+    (CVE-2026-9076)
+    [Nikola Pajkovsky]
+
+ *) Fixed heap buffer over-read in ASN.1 content parsing.
+
+    Severity: Low
+
+    Issue summary: Parsing a crafted DER-encoded ASN.1 structure with a
+    primitive element whose content exceeds 2 gigabytes in length may cause a
+    heap buffer over-read on 64-bit Unix and Unix-like platforms.
+
+    Impact summary: The heap buffer over-read may crash the application (Denial
+    of Service) or to load into the decoded ASN.1 object contents of memory
+    beyond the end of the input buffer.  More typically, such ASN.1 elements
+    would instead be truncated.
+
+    Reported by: Frank Buss.
+
+    (CVE-2026-34180)
+    [Viktor Dukhovni]
+
+ *) Fixed possible NULL dereference in password-dased CMS decryption.
+
+    Severity: Low
+
+    Issue summary: A specially crafted password-encrypted CMS message
+    could trigger a NULL pointer dereference during CMS decryption.
+
+    Impact summary: This NULL pointer dereference could lead to an application
+    crash and a Denial of Service.
+
+    Reported by: Mayank Jangid, Kushal Khemka, Hari Priandana,
+    Bhabani Sankar Das, and Qifan Zhang (Palo Alto Networks).
+
+    (CVE-2026-42766)
+    [Igor Ustinov]
+
+
  Changes between 1.1.1ze and 1.1.1zg [7 Apr 2026]
 
  *) Fixed potential use-after-free in DANE client code.
diff --git a/NEWS b/NEWS
index d01ce10..dd08b3f 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,17 @@
   This file gives a brief overview of the major changes between each OpenSSL
   release. For more details please read the CHANGES file.
 
+  Major changes between 1.1.1zg and 1.1.1zh [9 Jun 2026]
+
+      o Fixed heap use-after-free in `PKCS7_verify()`. (CVE-2026-45447)
+      o Fixed possible heap buffer overflow in ASN.1 multibyte string
+        conversion. (CVE-2026-7383)
+      o Fixed out-of-bounds read in CMS password-based decryption.
+        (CVE-2026-9076)
+      o Fixed heap buffer over-read in ASN.1 content parsing. (CVE-2026-34180)
+      o Fixed possible NULL dereference in password-dased CMS decryption.
+        (CVE-2026-42766)
+
   Major changes between 1.1.1ze and 1.1.1zg [7 Apr 2026]
 
       o Fixed potential use-after-free in DANE client code. (CVE-2026-28387)
diff --git a/README b/README
index 2bd7fdf..61509fa 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
 
- OpenSSL 1.1.1zg 7 Apr 2026
+ OpenSSL 1.1.1zh 9 Jun 2026
 
  Copyright (c) 1998-2026 The OpenSSL Project
  Copyright (c) 1995-1998 Eric A. Young, Tim J. Hudson
diff --git a/crypto/asn1/a_mbstr.c b/crypto/asn1/a_mbstr.c
index bdb697a..3a1a1bf 100644
--- a/crypto/asn1/a_mbstr.c
+++ b/crypto/asn1/a_mbstr.c
@@ -171,18 +171,41 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
         break;
 
     case MBSTRING_BMP:
+        if (nchar > INT_MAX / 2) {
+            ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, 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) {
+            ASN1err(ASN1_F_ASN1_MBSTRING_NCOPY, ASN1_R_STRING_TOO_LONG);
+            if (free_out) {
+                ASN1_STRING_free(dest);
+                *out = NULL;
+            }
+            return -1;
+        }
         outlen = nchar << 2;
         cpyfunc = cpy_univ;
         break;
 
     case MBSTRING_UTF8:
         outlen = 0;
-        traverse_string(in, len, inform, out_utf8, &outlen);
+        ret = traverse_string(in, len, inform, out_utf8, &outlen);
+        if (ret < 0) {
+            if (free_out) {
+                ASN1_STRING_free(dest);
+                *out = NULL;
+            }
+            return -1;
+        }
         cpyfunc = cpy_utf8;
         break;
     }
@@ -256,9 +279,19 @@ static int in_utf8(unsigned long value, void *arg)
 
 static int out_utf8(unsigned long value, void *arg)
 {
-    int *outlen;
+    int *outlen, len;
+
+    len = UTF8_putc(NULL, -1, value);
+    if (len <= 0) {
+        /* ASN1err(ASN1_F_OUT_UTF8, ASN1_R_INVALID_UTF8STRING); */
+        return len;
+    }
     outlen = arg;
-    *outlen += UTF8_putc(NULL, -1, value);
+    if (*outlen > INT_MAX - len) {
+        /* ASN1err(ASN1_F_OUT_UTF8, ASN1_R_STRING_TOO_LONG); */
+        return -1;
+    }
+    *outlen += len;
     return 1;
 }
 
diff --git a/crypto/asn1/tasn_dec.c b/crypto/asn1/tasn_dec.c
index 82577b1..764de38 100644
--- a/crypto/asn1/tasn_dec.c
+++ b/crypto/asn1/tasn_dec.c
@@ -57,7 +57,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 */
@@ -790,19 +790,25 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval,
 
 /* 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);
+        ASN1err(ASN1_F_ASN1_EX_C2I, 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) {
@@ -820,7 +826,8 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
     }
     switch (utype) {
     case V_ASN1_OBJECT:
-        if (!c2i_ASN1_OBJECT((ASN1_OBJECT **)pval, &cont, len))
+        if (len != (long)ilen
+            || !c2i_ASN1_OBJECT((ASN1_OBJECT **)pval, &cont, len))
             goto err;
         break;
 
@@ -875,6 +882,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) {
+            ASN1err(ASN1_F_ASN1_EX_C2I, ASN1_R_TOO_LONG);
+            goto err;
+        }
         if (utype == V_ASN1_BMPSTRING && (len & 1)) {
             ASN1err(ASN1_F_ASN1_EX_C2I, ASN1_R_BMPSTRING_IS_WRONG_LENGTH);
             goto err;
@@ -900,10 +911,10 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
         if (*free_cont) {
             OPENSSL_free(stmp->data);
             stmp->data = (unsigned char *)cont; /* UGLY CAST! RL */
-            stmp->length = len;
+            stmp->length = ilen;
             *free_cont = 0;
         } else {
-            if (!ASN1_STRING_set(stmp, cont, len)) {
+            if (!ASN1_STRING_set(stmp, cont, ilen)) {
                 ASN1err(ASN1_F_ASN1_EX_C2I, ERR_R_MALLOC_FAILURE);
                 ASN1_STRING_free(stmp);
                 *pval = NULL;
diff --git a/crypto/cms/cms_pwri.c b/crypto/cms/cms_pwri.c
index 9f98840..0275117 100644
--- a/crypto/cms/cms_pwri.c
+++ b/crypto/cms/cms_pwri.c
@@ -177,14 +177,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_block_size(ctx);
+    int blocklen = EVP_CIPHER_CTX_block_size(ctx);
     unsigned char *tmp;
     int outl, rv = 0;
-    if (inlen < 2 * blocklen) {
+
+    if (blocklen < 4)
+        return 0;
+
+    if (inlen < 2 * (size_t)blocklen) {
         /* too small */
         return 0;
     }
-    if (inlen % blocklen) {
+    if (inlen > INT_MAX || inlen % blocklen) {
         /* Invalid size */
         return 0;
     }
@@ -336,6 +340,11 @@ int cms_RecipientInfo_pwri_crypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri,
 
     /* Finish password based key derivation to setup key in "ctx" */
 
+    if (algtmp == NULL) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT,
+               CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER);
+        goto err;
+    }
     if (EVP_PBE_CipherInit(algtmp->algorithm,
                            (char *)pwri->pass, pwri->passlen,
                            algtmp->parameter, kekctx, en_de) < 0) {
diff --git a/crypto/pkcs7/pk7_smime.c b/crypto/pkcs7/pk7_smime.c
index a95db62..8a8449a 100644
--- a/crypto/pkcs7/pk7_smime.c
+++ b/crypto/pkcs7/pk7_smime.c
@@ -213,6 +213,7 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store,
     int i, j = 0, k, ret = 0;
     BIO *p7bio = NULL;
     BIO *tmpin = NULL, *tmpout = NULL;
+    BIO *next = NULL;
 
     if (!p7) {
         PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_INVALID_NULL_POINTER);
@@ -360,11 +361,11 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store,
  err:
     X509_STORE_CTX_free(cert_ctx);
     OPENSSL_free(buf);
-    if (tmpin == indata) {
-        if (indata)
-            BIO_pop(p7bio);
+    while (p7bio != NULL && p7bio != indata) {
+        next = BIO_pop(p7bio);
+        BIO_free(p7bio);
+        p7bio = next;
     }
-    BIO_free_all(p7bio);
     sk_X509_free(signers);
     return ret;
 }
diff --git a/include/openssl/opensslv.h b/include/openssl/opensslv.h
index 0dcdd92..0759061 100644
--- a/include/openssl/opensslv.h
+++ b/include/openssl/opensslv.h
@@ -39,8 +39,8 @@ extern "C" {
  * (Prior to 0.9.5a beta1, a different scheme was used: MMNNFFRBB for
  *  major minor fix final patch/beta)
  */
-# define OPENSSL_VERSION_NUMBER  0x1010120fL
-# define OPENSSL_VERSION_TEXT    "OpenSSL 1.1.1zg  7 Apr 2026"
+# define OPENSSL_VERSION_NUMBER  0x1010121fL
+# define OPENSSL_VERSION_TEXT    "OpenSSL 1.1.1zh  9 Jun 2026"
 
 /*-
  * The macros below are to be used for shared library (.so, .dll, ...)
