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) {