aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Momjian <bruce@momjian.us>2015-02-02 10:00:45 -0500
committerBruce Momjian <bruce@momjian.us>2015-02-02 10:00:52 -0500
commit9e05c5063eb01ce52618f29fe975696dc4495e6f (patch)
tree8d07e5833109d998e50b705e0dbe7c3448eaf11c
parent56b970f2e98853bee7205022df4c5d38bafacdf5 (diff)
downloadpostgresql-9e05c5063eb01ce52618f29fe975696dc4495e6f.tar.gz
postgresql-9e05c5063eb01ce52618f29fe975696dc4495e6f.zip
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
-rw-r--r--src/port/snprintf.c69
1 files changed, 62 insertions, 7 deletions
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index 4b64ca74d39..a07c7c9e024 100644
--- a/src/port/snprintf.c
+++ b/src/port/snprintf.c
@@ -32,7 +32,9 @@
#include "c.h"
+#include <ctype.h>
#include <limits.h>
+#include <math.h>
#ifndef WIN32
#include <sys/ioctl.h>
#endif
@@ -906,27 +908,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);
}