mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	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:
		@@ -32,7 +32,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "c.h"
 | 
					#include "c.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <ctype.h>
 | 
				
			||||||
#include <limits.h>
 | 
					#include <limits.h>
 | 
				
			||||||
 | 
					#include <math.h>
 | 
				
			||||||
#ifndef WIN32
 | 
					#ifndef WIN32
 | 
				
			||||||
#include <sys/ioctl.h>
 | 
					#include <sys/ioctl.h>
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -906,27 +908,80 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
 | 
				
			|||||||
		 PrintfTarget *target)
 | 
							 PrintfTarget *target)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int			signvalue = 0;
 | 
						int			signvalue = 0;
 | 
				
			||||||
 | 
						int			prec;
 | 
				
			||||||
	int			vallen;
 | 
						int			vallen;
 | 
				
			||||||
	char		fmt[32];
 | 
						char		fmt[32];
 | 
				
			||||||
	char		convert[512];
 | 
						char		convert[1024];
 | 
				
			||||||
	int			padlen = 0;		/* amount to pad */
 | 
						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)
 | 
						if (pointflag)
 | 
				
			||||||
		sprintf(fmt, "%%.%d%c", precision, type);
 | 
						{
 | 
				
			||||||
 | 
							sprintf(fmt, "%%.%d%c", prec, type);
 | 
				
			||||||
 | 
							zeropadlen = precision - prec;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		sprintf(fmt, "%%%c", type);
 | 
							sprintf(fmt, "%%%c", type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (adjust_sign((value < 0), forcesign, &signvalue))
 | 
						if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue))
 | 
				
			||||||
		value = -value;
 | 
							value = -value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	vallen = sprintf(convert, fmt, 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);
 | 
						leading_pad(zpad, &signvalue, &padlen, 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);
 | 
								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);
 | 
						trailing_pad(&padlen, target);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user