From de6bf5ab97755a0b16c57506ae41b74e35d58cc3 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Thu, 25 Feb 2016 13:09:46 +0000 Subject: [PATCH 3/3] 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 (cherry picked from commit 578b956fe741bf8e84055547b1e83c28dd902c73) --- crypto/bio/b_print.c | 164 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 107 insertions(+), 57 deletions(-) diff --git a/crypto/bio/b_print.c b/crypto/bio/b_print.c index 143a7cf..160c046 100644 --- a/crypto/bio/b_print.c +++ b/crypto/bio/b_print.c @@ -125,14 +125,14 @@ #define LLONG long #endif -static void fmtstr (char **, char **, size_t *, size_t *, +static int fmtstr (char **, char **, size_t *, size_t *, const char *, int, int, int); -static void fmtint (char **, char **, size_t *, size_t *, +static int fmtint (char **, char **, size_t *, size_t *, LLONG, int, int, int, int); -static void fmtfp (char **, char **, size_t *, size_t *, +static int fmtfp (char **, char **, size_t *, size_t *, LDOUBLE, int, int, int); -static void doapr_outch (char **, char **, size_t *, size_t *, int); -static void _dopr(char **sbuffer, char **buffer, +static int doapr_outch (char **, char **, size_t *, size_t *, int); +static int _dopr(char **sbuffer, char **buffer, size_t *maxlen, size_t *retlen, int *truncated, const char *format, va_list args); @@ -165,7 +165,7 @@ static void _dopr(char **sbuffer, char **buffer, #define char_to_int(p) (p - '0') #define OSSL_MAX(p,q) ((p >= q) ? p : q) -static void +static int _dopr( char **sbuffer, char **buffer, @@ -200,7 +200,8 @@ _dopr( if (ch == '%') state = DP_S_FLAGS; else - doapr_outch(sbuffer,buffer, &currlen, maxlen, ch); + if(!doapr_outch(sbuffer,buffer, &currlen, maxlen, ch)) + return 0; ch = *format++; break; case DP_S_FLAGS: @@ -306,8 +307,9 @@ _dopr( value = va_arg(args, int); break; } - fmtint(sbuffer, buffer, &currlen, maxlen, - value, 10, min, max, flags); + if (!fmtint(sbuffer, buffer, &currlen, maxlen, value, 10, min, + max, flags)) + return 0; break; case 'X': flags |= DP_F_UP; @@ -332,17 +334,19 @@ _dopr( unsigned int); break; } - fmtint(sbuffer, buffer, &currlen, maxlen, value, - ch == 'o' ? 8 : (ch == 'u' ? 10 : 16), - min, max, flags); + if (!fmtint(sbuffer, buffer, &currlen, maxlen, value, + ch == 'o' ? 8 : (ch == 'u' ? 10 : 16), + min, max, flags)) + return 0; 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); + if (!fmtfp(sbuffer, buffer, &currlen, maxlen, fvalue, min, max, + flags)) + return 0; break; case 'E': flags |= DP_F_UP; @@ -361,8 +365,9 @@ _dopr( fvalue = va_arg(args, double); break; case 'c': - doapr_outch(sbuffer, buffer, &currlen, maxlen, - va_arg(args, int)); + if(!doapr_outch(sbuffer, buffer, &currlen, maxlen, + va_arg(args, int))) + return 0; break; case 's': strvalue = va_arg(args, char *); @@ -372,13 +377,15 @@ _dopr( else max = *maxlen; } - fmtstr(sbuffer, buffer, &currlen, maxlen, strvalue, - flags, min, max); + if(!fmtstr(sbuffer, buffer, &currlen, maxlen, strvalue, + flags, min, max)) + return 0; break; case 'p': value = (long)va_arg(args, void *); - fmtint(sbuffer, buffer, &currlen, maxlen, - value, 16, min, max, flags|DP_F_NUM); + if(!fmtint(sbuffer, buffer, &currlen, maxlen, + value, 16, min, max, flags|DP_F_NUM)) + return 0; break; case 'n': /* XXX */ if (cflags == DP_C_SHORT) { @@ -400,7 +407,8 @@ _dopr( } break; case '%': - doapr_outch(sbuffer, buffer, &currlen, maxlen, ch); + if(!doapr_outch(sbuffer, buffer, &currlen, maxlen, ch)) + return 0; break; case 'w': /* not supported yet, treat as next char */ @@ -424,12 +432,13 @@ _dopr( *truncated = (currlen > *maxlen - 1); if (*truncated) currlen = *maxlen - 1; - doapr_outch(sbuffer, buffer, &currlen, maxlen, '\0'); + if(!doapr_outch(sbuffer, buffer, &currlen, maxlen, '\0')) + return 0; *retlen = currlen - 1; - return; + return 1; } -static void +static int fmtstr( char **sbuffer, char **buffer, @@ -440,36 +449,44 @@ fmtstr( int min, int max) { - int padlen, strln; + int padlen; + size_t strln; int cnt = 0; if (value == 0) value = ""; - for (strln = 0; value[strln]; ++strln) - ; + + strln = strlen(value); + if (strln > INT_MAX) + strln = INT_MAX; + padlen = min - strln; - if (padlen < 0) + if (min < 0 || padlen < 0) padlen = 0; if (flags & DP_F_MINUS) padlen = -padlen; while ((padlen > 0) && (cnt < max)) { - doapr_outch(sbuffer, buffer, currlen, maxlen, ' '); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, ' ')) + return 0; --padlen; ++cnt; } while (*value && (cnt < max)) { - doapr_outch(sbuffer, buffer, currlen, maxlen, *value++); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, *value++)) + return 0; ++cnt; } while ((padlen < 0) && (cnt < max)) { - doapr_outch(sbuffer, buffer, currlen, maxlen, ' '); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, ' ')) + return 0; ++padlen; ++cnt; } + return 1; } -static void +static int fmtint( char **sbuffer, char **buffer, @@ -533,37 +550,43 @@ fmtint( /* spaces */ while (spadlen > 0) { - doapr_outch(sbuffer, buffer, currlen, maxlen, ' '); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, ' ')) + return 0; --spadlen; } /* sign */ if (signvalue) - doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue)) + return 0; /* prefix */ while (*prefix) { - doapr_outch(sbuffer, buffer, currlen, maxlen, *prefix); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, *prefix)) + return 0; prefix++; } /* zeros */ if (zpadlen > 0) { while (zpadlen > 0) { - doapr_outch(sbuffer, buffer, currlen, maxlen, '0'); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, '0')) + return 0; --zpadlen; } } /* digits */ while (place > 0) - doapr_outch(sbuffer, buffer, currlen, maxlen, convert[--place]); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, convert[--place])) + return 0; /* left justified spaces */ while (spadlen < 0) { - doapr_outch(sbuffer, buffer, currlen, maxlen, ' '); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, ' ')) + return 0; ++spadlen; } - return; + return 1; } static LDOUBLE @@ -597,7 +620,7 @@ roundv(LDOUBLE value) return intpart; } -static void +static int fmtfp( char **sbuffer, char **buffer, @@ -682,47 +705,61 @@ fmtfp( if ((flags & DP_F_ZERO) && (padlen > 0)) { if (signvalue) { - doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue)) + return 0; --padlen; signvalue = 0; } while (padlen > 0) { - doapr_outch(sbuffer, buffer, currlen, maxlen, '0'); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, '0')) + return 0; --padlen; } } while (padlen > 0) { - doapr_outch(sbuffer, buffer, currlen, maxlen, ' '); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, ' ')) + return 0; --padlen; } if (signvalue) - doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue)) + return 0; - while (iplace > 0) - doapr_outch(sbuffer, buffer, currlen, maxlen, iconvert[--iplace]); + while (iplace > 0) { + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, iconvert[--iplace])) + return 0; + } /* * Decimal point. This should probably use locale to find the correct * char to print out. */ if (max > 0 || (flags & DP_F_NUM)) { - doapr_outch(sbuffer, buffer, currlen, maxlen, '.'); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, '.')) + return 0; - while (fplace > 0) - doapr_outch(sbuffer, buffer, currlen, maxlen, fconvert[--fplace]); + while (fplace > 0) { + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, fconvert[--fplace])) + return 0; + } } while (zpadlen > 0) { - doapr_outch(sbuffer, buffer, currlen, maxlen, '0'); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, '0')) + return 0; --zpadlen; } while (padlen < 0) { - doapr_outch(sbuffer, buffer, currlen, maxlen, ' '); + if(!doapr_outch(sbuffer, buffer, currlen, maxlen, ' ')) + return 0; ++padlen; } + return 1; } -static void +#define BUFFER_INC 1024 + +static int doapr_outch( char **sbuffer, char **buffer, @@ -739,14 +776,23 @@ doapr_outch( if (*maxlen == 0) *maxlen = 1024; *buffer = OPENSSL_malloc(*maxlen); + if (*buffer == NULL) + return 0; if (*currlen > 0) { assert(*sbuffer != NULL); memcpy(*buffer, *sbuffer, *currlen); } *sbuffer = NULL; } else { + char *tmpbuf; + + if (*maxlen > INT_MAX - BUFFER_INC) + return 0; *maxlen += 1024; - *buffer = OPENSSL_realloc(*buffer, *maxlen); + tmpbuf = OPENSSL_realloc(*buffer, *maxlen); + if (tmpbuf == NULL) + return 0; + *buffer = tmpbuf; } } /* What to do if *buffer is NULL? */ @@ -760,7 +806,7 @@ doapr_outch( (*buffer)[(*currlen)++] = (char)c; } - return; + return 1; } /***************************************************************************/ @@ -792,8 +838,11 @@ int BIO_vprintf (BIO *bio, const char *format, va_list args) dynbuf = NULL; CRYPTO_push_info("doapr()"); - _dopr(&hugebufp, &dynbuf, &hugebufsize, - &retlen, &ignored, format, args); + if(!_dopr(&hugebufp, &dynbuf, &hugebufsize, + &retlen, &ignored, format, args)) { + OPENSSL_free(dynbuf); + return -1; + } if (dynbuf) { ret=BIO_write(bio, dynbuf, (int)retlen); @@ -829,7 +878,8 @@ int BIO_vsnprintf(char *buf, size_t n, const char *format, va_list args) size_t retlen; int truncated; - _dopr(&buf, NULL, &n, &retlen, &truncated, format, args); + if(!_dopr(&buf, NULL, &n, &retlen, &truncated, format, args)) + return -1; if (truncated) /* In case of truncation, return -1 like traditional snprintf. -- 1.9.1