Commit c6298139 authored by Dr. Stephen Henson's avatar Dr. Stephen Henson
Browse files

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: default avatarViktor Dukhovni <viktor@openssl.org>
parent ddc606c9
Loading
Loading
Loading
Loading
+26 −10
Original line number Diff line number Diff line
@@ -138,6 +138,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;
@@ -216,18 +217,30 @@ static int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
            /* suck in slen bytes of data */
            want = 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)) {
                while (want > 0) {
                    /*
                     * 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);
@@ -238,7 +251,10 @@ static int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
                     * overflow.
                     */
                        len += i;
                    want -= i;
                        chunk -= i;
                    }
                    if (chunk_max < INT_MAX/2)
                        chunk_max *= 2;
                }
            }
            if (off + slen < off) {