Commit message: Fix memory issues in BIO_*printf functions

The internal |fmtstr| function used in processing a "%s" format string
in the BIO_*printf functions could overflow while calculating the length
of a string and cause an OOB read when printing very long strings.

Additionally the internal |doapr_outch| function can attempt to write to
an OOB memory location (at an offset from the NULL pointer) in the event of
a memory allocation failure. In 1.0.2 and below this could be caused where
the size of a buffer to be allocated is greater than INT_MAX. E.g. this
could be in processing a very long "%s" format string. Memory leaks can also
occur.

These issues will only occur on certain platforms where sizeof(size_t) >
sizeof(int). E.g. many 64 bit systems. The first issue may mask the second
issue dependent on compiler behaviour.

These problems could enable attacks where large amounts of untrusted data
is passed to the BIO_*printf functions. If applications use these functions
in this way then they could be vulnerable. OpenSSL itself uses these
functions when printing out human-readable dumps of ASN.1 data. Therefore
applications that print this data could be vulnerable if the data is from
untrusted sources. OpenSSL command line applications could also be
vulnerable where they print out ASN.1 data, or if untrusted data is passed
as command line arguments.

Libssl is not considered directly vulnerable. Additionally certificates etc
received via remote connections via libssl are also unlikely to be able to
trigger these issues because of message size limits enforced within libssl.

CVE-2016-0799

Issue reported by Guido Vranken.

Reviewed-by: Andy Polyakov <appro@openssl.org>
CVE: CVE-2016-2842
CWE: CWE-119
Vulnerability description: The doapr_outch function in crypto/bio/b_print.c in OpenSSL 1.0.1 before 1.0.1s and 1.0.2 before 1.0.2g does not verify that a certain memory allocation succeeds, which allows remote attackers to cause a denial of service (out-of-bounds write or memory consumption) or possibly have unspecified other impact via a long string, as demonstrated by a large amount of ASN.1 data, a different vulnerability than CVE-2016-0799.

Project: openssl

 _dopr(char **sbuffer,
       char **buffer,
       size_t *maxlen,
      size_t *retlen, int *truncated, const char *format, va_list args)
{
    char ch;
    LLONG value;
    LDOUBLE fvalue;
    char *strvalue;
    int min;
    int max;
    int state;
    int flags;
    int cflags;
    size_t currlen;

    state = DP_S_DEFAULT;
    flags = currlen = cflags = min = 0;
    max = -1;
    ch = *format++;

    while (state != DP_S_DONE) {
        if (ch == '\0' || (buffer == NULL && currlen >= *maxlen))
            state = DP_S_DONE;

        switch (state) {
        case DP_S_DEFAULT:
             if (ch == '%')
                 state = DP_S_FLAGS;
             else
                doapr_outch(sbuffer, buffer, &currlen, maxlen, ch);
             ch = *format++;
             break;
         case DP_S_FLAGS:
            case '-':
                flags |= DP_F_MINUS;
                ch = *format++;
                break;
            case '+':
                flags |= DP_F_PLUS;
                ch = *format++;
                break;
            case ' ':
                flags |= DP_F_SPACE;
                ch = *format++;
                break;
            case '#':
                flags |= DP_F_NUM;
                ch = *format++;
                break;
            case '0':
                flags |= DP_F_ZERO;
                ch = *format++;
                break;
            default:
                state = DP_S_MIN;
                break;
            }
            break;
        case DP_S_MIN:
            if (isdigit((unsigned char)ch)) {
                min = 10 * min + char_to_int(ch);
                ch = *format++;
            } else if (ch == '*') {
                min = va_arg(args, int);
                ch = *format++;
                state = DP_S_DOT;
            } else
                state = DP_S_DOT;
            break;
        case DP_S_DOT:
            if (ch == '.') {
                state = DP_S_MAX;
                ch = *format++;
            } else
                state = DP_S_MOD;
            break;
        case DP_S_MAX:
            if (isdigit((unsigned char)ch)) {
                if (max < 0)
                    max = 0;
                max = 10 * max + char_to_int(ch);
                ch = *format++;
            } else if (ch == '*') {
                max = va_arg(args, int);
                ch = *format++;
                state = DP_S_MOD;
            } else
                state = DP_S_MOD;
            break;
        case DP_S_MOD:
            switch (ch) {
            case 'h':
                cflags = DP_C_SHORT;
                ch = *format++;
                break;
            case 'l':
                if (*format == 'l') {
                    cflags = DP_C_LLONG;
                    format++;
                } else
                    cflags = DP_C_LONG;
                ch = *format++;
                break;
            case 'q':
                cflags = DP_C_LLONG;
                ch = *format++;
                break;
            case 'L':
                cflags = DP_C_LDOUBLE;
                ch = *format++;
                break;
            default:
                break;
            }
            state = DP_S_CONV;
            break;
        case DP_S_CONV:
            switch (ch) {
            case 'd':
            case 'i':
                switch (cflags) {
                case DP_C_SHORT:
                    value = (short int)va_arg(args, int);
                    break;
                case DP_C_LONG:
                    value = va_arg(args, long int);
                    break;
                case DP_C_LLONG:
                    value = va_arg(args, LLONG);
                    break;
                default:
                    value = va_arg(args, int);
                     value = va_arg(args, int);
                     break;
                 }
                fmtint(sbuffer, buffer, &currlen, maxlen,
                       value, 10, min, max, flags);
                 break;
             case 'X':
                 flags |= DP_F_UP;
            case 'o':
            case 'u':
                flags |= DP_F_UNSIGNED;
                switch (cflags) {
                case DP_C_SHORT:
                    value = (unsigned short int)va_arg(args, unsigned int);
                    break;
                case DP_C_LONG:
                    value = (LLONG) va_arg(args, unsigned long int);
                    break;
                case DP_C_LLONG:
                    value = va_arg(args, unsigned LLONG);
                    break;
                default:
                    value = (LLONG) va_arg(args, unsigned int);
                    break;
                     value = (LLONG) va_arg(args, unsigned int);
                     break;
                 }
                fmtint(sbuffer, buffer, &currlen, maxlen, value,
                       ch == 'o' ? 8 : (ch == 'u' ? 10 : 16),
                       min, max, flags);
                 break;
             case 'f':
                 if (cflags == DP_C_LDOUBLE)
                     fvalue = va_arg(args, LDOUBLE);
                 else
                     fvalue = va_arg(args, double);
                fmtfp(sbuffer, buffer, &currlen, maxlen,
                      fvalue, min, max, flags);
                 break;
             case 'E':
                 flags |= DP_F_UP;
                    fvalue = va_arg(args, double);
                break;
            case 'G':
                flags |= DP_F_UP;
            case 'g':
                if (cflags == DP_C_LDOUBLE)
                    fvalue = va_arg(args, LDOUBLE);
                else
                    fvalue = va_arg(args, double);
                break;
            case 'c':
                doapr_outch(sbuffer, buffer, &currlen, maxlen,
                     fvalue = va_arg(args, double);
                 break;
             case 'c':
                doapr_outch(sbuffer, buffer, &currlen, maxlen,
                            va_arg(args, int));
                 break;
             case 's':
                 strvalue = va_arg(args, char *);
                }
                fmtstr(sbuffer, buffer, &currlen, maxlen, strvalue,
                       flags, min, max);
                     else
                         max = *maxlen;
                 }
                fmtstr(sbuffer, buffer, &currlen, maxlen, strvalue,
                       flags, min, max);
                 break;
             case 'p':
                 value = (long)va_arg(args, void *);
                fmtint(sbuffer, buffer, &currlen, maxlen,
                       value, 16, min, max, flags | DP_F_NUM);
                 break;
             case 'n':          /* XXX */
                 if (cflags == DP_C_SHORT) {
                } else if (cflags == DP_C_LLONG) { /* XXX */
                    LLONG *num;
                    num = va_arg(args, LLONG *);
                    *num = (LLONG) currlen;
                } else {
                    int *num;
                    num = va_arg(args, int *);
                    *num = currlen;
                }
                break;
            case '%':
                doapr_outch(sbuffer, buffer, &currlen, maxlen, ch);
                break;
            case 'w':
                /* not supported yet, treat as next char */
                 }