Skip to content

Commit c6c6aa2

Browse files
committed
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
1 parent e09651e commit c6c6aa2

File tree

1 file changed

+62
-7
lines changed

1 file changed

+62
-7
lines changed

src/port/snprintf.c

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232

3333
#include "c.h"
3434

35+
#include <ctype.h>
3536
#include <limits.h>
37+
#include <math.h>
3638
#ifndef WIN32
3739
#include <sys/ioctl.h>
3840
#endif
@@ -906,27 +908,80 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
906908
PrintfTarget *target)
907909
{
908910
int signvalue = 0;
911+
int prec;
909912
int vallen;
910913
char fmt[32];
911-
char convert[512];
912-
int padlen = 0; /* amount to pad */
914+
char convert[1024];
915+
int zeropadlen = 0; /* amount to pad with zeroes */
916+
int padlen = 0; /* amount to pad with spaces */
917+
918+
/*
919+
* We rely on the regular C library's sprintf to do the basic conversion,
920+
* then handle padding considerations here.
921+
*
922+
* The dynamic range of "double" is about 1E+-308 for IEEE math, and not
923+
* too wildly more than that with other hardware. In "f" format, sprintf
924+
* could therefore generate at most 308 characters to the left of the
925+
* decimal point; while we need to allow the precision to get as high as
926+
* 308+17 to ensure that we don't truncate significant digits from very
927+
* small values. To handle both these extremes, we use a buffer of 1024
928+
* bytes and limit requested precision to 350 digits; this should prevent
929+
* buffer overrun even with non-IEEE math. If the original precision
930+
* request was more than 350, separately pad with zeroes.
931+
*/
932+
if (precision < 0) /* cover possible overflow of "accum" */
933+
precision = 0;
934+
prec = Min(precision, 350);
913935

914-
/* we rely on regular C library's sprintf to do the basic conversion */
915936
if (pointflag)
916-
sprintf(fmt, "%%.%d%c", precision, type);
937+
{
938+
sprintf(fmt, "%%.%d%c", prec, type);
939+
zeropadlen = precision - prec;
940+
}
917941
else
918942
sprintf(fmt, "%%%c", type);
919943

920-
if (adjust_sign((value < 0), forcesign, &signvalue))
944+
if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue))
921945
value = -value;
922946

923947
vallen = sprintf(convert, fmt, value);
924948

925-
adjust_padlen(minlen, vallen, leftjust, &padlen);
949+
/* If it's infinity or NaN, forget about doing any zero-padding */
950+
if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1]))
951+
zeropadlen = 0;
952+
953+
adjust_padlen(minlen, vallen + zeropadlen, leftjust, &padlen);
926954

927955
leading_pad(zpad, &signvalue, &padlen, target);
928956

929-
dostr(convert, vallen, target);
957+
if (zeropadlen > 0)
958+
{
959+
/* If 'e' or 'E' format, inject zeroes before the exponent */
960+
char *epos = strrchr(convert, 'e');
961+
962+
if (!epos)
963+
epos = strrchr(convert, 'E');
964+
if (epos)
965+
{
966+
/* pad after exponent */
967+
dostr(convert, epos - convert, target);
968+
while (zeropadlen-- > 0)
969+
dopr_outch('0', target);
970+
dostr(epos, vallen - (epos - convert), target);
971+
}
972+
else
973+
{
974+
/* no exponent, pad after the digits */
975+
dostr(convert, vallen, target);
976+
while (zeropadlen-- > 0)
977+
dopr_outch('0', target);
978+
}
979+
}
980+
else
981+
{
982+
/* no zero padding, just emit the number as-is */
983+
dostr(convert, vallen, target);
984+
}
930985

931986
trailing_pad(&padlen, target);
932987
}

0 commit comments

Comments
 (0)