port/snprintf(): fix overflow and do padding

Prevent port/snprintf() from overflowing its local fixed-size
buffer and pad to the desired number of digits with zeros, even
if the precision is beyond the ability of the native sprintf().
port/snprintf() is only used on systems that lack a native
snprintf().

Reported by Bruce Momjian. Patch by Tom Lane.	Backpatch to all
supported versions.

Security: CVE-2015-0242
This commit is contained in:
Bruce Momjian 2015-02-02 10:00:45 -05:00
parent 9241c84cbc
commit 29725b3db6
1 changed files with 62 additions and 7 deletions

View File

@ -32,7 +32,9 @@
#include "c.h"
#include <ctype.h>
#include <limits.h>
#include <math.h>
#ifndef WIN32
#include <sys/ioctl.h>
#endif
@ -932,27 +934,80 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
PrintfTarget *target)
{
int signvalue = 0;
int prec;
int vallen;
char fmt[32];
char convert[512];
int padlen = 0; /* amount to pad */
char convert[1024];
int zeropadlen = 0; /* amount to pad with zeroes */
int padlen = 0; /* amount to pad with spaces */
/*
* We rely on the regular C library's sprintf to do the basic conversion,
* then handle padding considerations here.
*
* The dynamic range of "double" is about 1E+-308 for IEEE math, and not
* too wildly more than that with other hardware. In "f" format, sprintf
* could therefore generate at most 308 characters to the left of the
* decimal point; while we need to allow the precision to get as high as
* 308+17 to ensure that we don't truncate significant digits from very
* small values. To handle both these extremes, we use a buffer of 1024
* bytes and limit requested precision to 350 digits; this should prevent
* buffer overrun even with non-IEEE math. If the original precision
* request was more than 350, separately pad with zeroes.
*/
if (precision < 0) /* cover possible overflow of "accum" */
precision = 0;
prec = Min(precision, 350);
/* we rely on regular C library's sprintf to do the basic conversion */
if (pointflag)
sprintf(fmt, "%%.%d%c", precision, type);
{
sprintf(fmt, "%%.%d%c", prec, type);
zeropadlen = precision - prec;
}
else
sprintf(fmt, "%%%c", type);
if (adjust_sign((value < 0), forcesign, &signvalue))
if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue))
value = -value;
vallen = sprintf(convert, fmt, value);
adjust_padlen(minlen, vallen, leftjust, &padlen);
/* If it's infinity or NaN, forget about doing any zero-padding */
if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1]))
zeropadlen = 0;
adjust_padlen(minlen, vallen + zeropadlen, leftjust, &padlen);
leading_pad(zpad, &signvalue, &padlen, target);
dostr(convert, vallen, target);
if (zeropadlen > 0)
{
/* If 'e' or 'E' format, inject zeroes before the exponent */
char *epos = strrchr(convert, 'e');
if (!epos)
epos = strrchr(convert, 'E');
if (epos)
{
/* pad after exponent */
dostr(convert, epos - convert, target);
while (zeropadlen-- > 0)
dopr_outch('0', target);
dostr(epos, vallen - (epos - convert), target);
}
else
{
/* no exponent, pad after the digits */
dostr(convert, vallen, target);
while (zeropadlen-- > 0)
dopr_outch('0', target);
}
}
else
{
/* no zero padding, just emit the number as-is */
dostr(convert, vallen, target);
}
trailing_pad(&padlen, target);
}