diff options
author | Ted Unangst <tedu@cvs.openbsd.org> | 2016-05-04 14:58:10 +0000 |
---|---|---|
committer | Ted Unangst <tedu@cvs.openbsd.org> | 2016-05-04 14:58:10 +0000 |
commit | 19970904842c2052b64517ef1cc8e69d8b2ba226 (patch) | |
tree | 81fff7c58d1f5b337381356c6e6696b7296b0739 /lib/libcrypto | |
parent | 01287bdff69bd2efe31124a00e6664d6814a5baf (diff) |
be careful about consuming excessive memory by reading in chunks.
CVE-2016-2109 from openssl.
Diffstat (limited to 'lib/libcrypto')
-rw-r--r-- | lib/libcrypto/asn1/a_d2i_fp.c | 51 |
1 files changed, 37 insertions, 14 deletions
diff --git a/lib/libcrypto/asn1/a_d2i_fp.c b/lib/libcrypto/asn1/a_d2i_fp.c index d85be565685..96416540c41 100644 --- a/lib/libcrypto/asn1/a_d2i_fp.c +++ b/lib/libcrypto/asn1/a_d2i_fp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: a_d2i_fp.c,v 1.13 2016/05/04 14:53:29 tedu Exp $ */ +/* $OpenBSD: a_d2i_fp.c,v 1.14 2016/05/04 14:58:09 tedu Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -144,6 +144,7 @@ ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x) } #define HEADER_SIZE 8 +#define ASN1_CHUNK_INITIAL_SIZE (16 * 1024) static int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb) { @@ -167,18 +168,22 @@ asn1_d2i_read_bio(BIO *in, BUF_MEM **pb) if (want >= (len - off)) { want -= (len - off); - if (len + want < len || !BUF_MEM_grow_clean(b, len + want)) { - ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ERR_R_MALLOC_FAILURE); + if (len + want < len || + !BUF_MEM_grow_clean(b, len + want)) { + ASN1err(ASN1_F_ASN1_D2I_READ_BIO, + ERR_R_MALLOC_FAILURE); goto err; } i = BIO_read(in, &(b->data[len]), want); if ((i < 0) && ((len - off) == 0)) { - ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ASN1_R_NOT_ENOUGH_DATA); + ASN1err(ASN1_F_ASN1_D2I_READ_BIO, + ASN1_R_NOT_ENOUGH_DATA); goto err; } if (i > 0) { if (len + i < len) { - ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ASN1_R_TOO_LONG); + ASN1err(ASN1_F_ASN1_D2I_READ_BIO, + ASN1_R_TOO_LONG); goto err; } len += i; @@ -206,7 +211,8 @@ asn1_d2i_read_bio(BIO *in, BUF_MEM **pb) /* no data body so go round again */ eos++; if (eos < 0) { - ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ASN1_R_HEADER_TOO_LONG); + ASN1err(ASN1_F_ASN1_D2I_READ_BIO, + ASN1_R_HEADER_TOO_LONG); goto err; } want = HEADER_SIZE; @@ -221,28 +227,45 @@ 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); + 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); + /* + * 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; } - while (want > 0) { - i = BIO_read(in, &(b->data[len]), want); + 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. */ + /* + * This can't overflow because |len+want| + * didn't overflow. + */ len += i; - want -= i; + chunk -= i; } + if (chunk_max < INT_MAX/2) + chunk_max *= 2; } if (off + c.slen < off) { ASN1err(ASN1_F_ASN1_D2I_READ_BIO, ASN1_R_TOO_LONG); |