Commit 35852da1d9 for openssl.org

commit 35852da1d9e24cb74034b2f418cef3a58203b127
Author: Daniel Sands <dnsands@sandia.gov>
Date:   Thu Mar 12 11:59:13 2026 -0600

    Add intelligence to asn1_d2i_read_bio for reading entire header without blocking for extra data

    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
    (Merged from https://github.com/openssl/openssl/pull/30401)

diff --git a/crypto/asn1/a_d2i_fp.c b/crypto/asn1/a_d2i_fp.c
index 4204743cd2..629d517028 100644
--- a/crypto/asn1/a_d2i_fp.c
+++ b/crypto/asn1/a_d2i_fp.c
@@ -104,7 +104,7 @@ void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x)
 }
 #endif

-#define HEADER_SIZE 8
+#define HEADER_SIZE 2
 #define ASN1_CHUNK_INITIAL_SIZE (16 * 1024)
 int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
 {
@@ -140,7 +140,7 @@ int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
             }
             i = BIO_read(in, &(b->data[len]), (int)want);

-            if (i <= 0 && diff == 0) {
+            if (i <= 0) {
                 ERR_raise(ERR_LIB_ASN1, ASN1_R_NOT_ENOUGH_DATA);
                 goto err;
             }
@@ -157,11 +157,57 @@ int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
         }
         /* else data already loaded */

+        /* make sure there is enough data for a complete header */
         p = (unsigned char *)&(b->data[off]);
         q = p;
         diff = len - off;
-        if (diff == 0)
+        if (diff < 2) {
+            /* Failed sanity check */
+            ERR_raise(ERR_LIB_ASN1, ASN1_R_NOT_ENOUGH_DATA);
             goto err;
+        }
+
+        diff--;
+        if ((*(q++) & V_ASN1_PRIMITIVE_TAG) == V_ASN1_PRIMITIVE_TAG) {
+            /* Multi-byte tag.  See if we have the whole thing yet */
+            do {
+                diff--;
+            } while (diff > 0 && *(q++) & 0x80);
+
+            if (diff == 0) {
+                /*
+                 * End of current data, will need at least 1 more byte for
+                 * length.  2 if the tag is still incomplete
+                 */
+                want = q - p + 2;
+                if (*q & 0x80) {
+                    want++;
+                }
+                continue;
+            }
+        }
+
+        /* Check the length.  This should also work for indefinite length */
+        diff--;
+        if (*q & 0x80) {
+            unsigned int i = *q & 0x7f;
+
+            if (i > sizeof(long)) {
+                ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LONG);
+                goto err;
+            }
+            if (i > diff) {
+                want = q - p + i + 1;
+                continue;
+            }
+        }
+
+        /*
+         * We have a complete header now, assuming we didn't hit EOF. Parse the
+         * tag and length
+         */
+        q = p;
+        diff = len - off;
         inf = ASN1_get_object(&q, &slen, &tag, &xclass, (int)diff);
         if (inf & 0x80) {
             unsigned long e;
diff --git a/crypto/asn1/asn1_lib.c b/crypto/asn1/asn1_lib.c
index e122afd92d..4d61dfca54 100644
--- a/crypto/asn1/asn1_lib.c
+++ b/crypto/asn1/asn1_lib.c
@@ -129,7 +129,7 @@ static int asn1_get_length(const unsigned char **pp, int *inf, long *rl,
         *inf = 0;
         i = *p & 0x7f;
         if (*p++ & 0x80) {
-            if (max < i + 1)
+            if (max < i)
                 return 0;
             /* Skip leading zeroes */
             while (i > 0 && *p == 0) {