diff options
author | Dr. Stephen Henson <steve@openssl.org> | 2016-04-11 13:57:20 +0100 |
---|---|---|
committer | Dr. Stephen Henson <steve@openssl.org> | 2016-04-23 00:27:42 +0100 |
commit | f32774087f7b3db1f789688368d16d917757421e (patch) | |
tree | 57637e4918ff392b4607b762981ddeb05cef0906 | |
parent | 9676402c3a6657781a65836c716066d3d39ee54f (diff) | |
download | openssl-new-f32774087f7b3db1f789688368d16d917757421e.tar.gz |
Harden ASN.1 BIO handling of large amounts of data.
If the ASN.1 BIO is presented with a large length field read it in
chunks of increasing size checking for EOF on each read. This prevents
small files allocating excessive amounts of data.
CVE-2016-2109
Thanks to Brian Carpenter for reporting this issue.
Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
(cherry picked from commit c62981390d6cf9e3d612c489b8b77c2913b25807)
-rw-r--r-- | crypto/asn1/a_d2i_fp.c | 36 |
1 files changed, 26 insertions, 10 deletions
diff --git a/crypto/asn1/a_d2i_fp.c b/crypto/asn1/a_d2i_fp.c index a1864b42c9..51b6f245ab 100644 --- a/crypto/asn1/a_d2i_fp.c +++ b/crypto/asn1/a_d2i_fp.c @@ -141,6 +141,7 @@ void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x) #endif #define HEADER_SIZE 8 +#define ASN1_CHUNK_INITIAL_SIZE (16 * 1024) static int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb) { BUF_MEM *b; @@ -217,29 +218,44 @@ static int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb) /* suck in c.slen bytes of data */ want = c.slen; if (want > (len - off)) { + size_t chunk_max = ASN1_CHUNK_INITIAL_SIZE; + want -= (len - off); if (want > INT_MAX /* BIO_read takes an int length */ || len + want < len) { ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ASN1_R_TOO_LONG); goto err; } - if (!BUF_MEM_grow_clean(b, len + want)) { - ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ERR_R_MALLOC_FAILURE); - goto err; - } while (want > 0) { - i = BIO_read(in, &(b->data[len]), want); - if (i <= 0) { - ASN1err(ASN1_F_ASN1_D2I_READ_BIO, - ASN1_R_NOT_ENOUGH_DATA); + /* + * Read content in chunks of increasing size + * so we can return an error for EOF without + * having to allocate the entire content length + * in one go. + */ + size_t chunk = want > chunk_max ? chunk_max : want; + + if (!BUF_MEM_grow_clean(b, len + chunk)) { + ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ERR_R_MALLOC_FAILURE); goto err; } + want -= chunk; + while (chunk > 0) { + i = BIO_read(in, &(b->data[len]), chunk); + if (i <= 0) { + ASN1err(ASN1_F_ASN1_D2I_READ_BIO, + ASN1_R_NOT_ENOUGH_DATA); + goto err; + } /* * This can't overflow because |len+want| didn't * overflow. */ - len += i; - want -= i; + len += i; + chunk -= i; + } + if (chunk_max < INT_MAX/2) + chunk_max *= 2; } } if (off + c.slen < off) { |