gdtoa-gethex.c.patch   [plain text]


--- gdtoa-gethex.c.orig	2010-02-24 20:50:10.000000000 -0800
+++ gdtoa-gethex.c	2010-02-24 21:26:32.000000000 -0800
@@ -29,34 +29,40 @@ THIS SOFTWARE.
 /* Please send bug reports to David M. Gay (dmg at acm dot org,
  * with " at " changed at "@" and " dot " changed to ".").	*/
 
+#include "xlocale_private.h"
+
 #include "gdtoaimp.h"
 
+#include <sys/types.h>
+
 #ifdef USE_LOCALE
 #include "locale.h"
 #endif
 
  int
 #ifdef KR_headers
-gethex(sp, fpi, exp, bp, sign)
-	CONST char **sp; FPI *fpi; Long *exp; Bigint **bp; int sign;
+gethex(sp, fpi, exp, bp, sign, loc)
+	CONST char **sp; FPI *fpi; Long *exp; Bigint **bp; int sign; locale_t loc;
 #else
-gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
+gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign, locale_t loc)
 #endif
 {
 	Bigint *b;
 	CONST unsigned char *decpt, *s0, *s, *s1;
+	unsigned char *strunc;
 	int big, esign, havedig, irv, j, k, n, n0, nbits, up, zret;
 	ULong L, lostbits, *x;
 	Long e, e1;
 #ifdef USE_LOCALE
 	int i;
+	NORMALIZE_LOCALE(loc);
 #ifdef NO_LOCALE_CACHE
-	const unsigned char *decimalpoint = (unsigned char*)localeconv()->decimal_point;
+	const unsigned char *decimalpoint = (unsigned char*)localeconv_l(loc)->decimal_point;
 #else
 	const unsigned char *decimalpoint;
 	static unsigned char *decimalpoint_cache;
 	if (!(s0 = decimalpoint_cache)) {
-		s0 = (unsigned char*)localeconv()->decimal_point;
+		s0 = (unsigned char*)localeconv_l(loc)->decimal_point;
 		if ((decimalpoint_cache = (char*)MALLOC(strlen(s0) + 1))) {
 			strcpy(decimalpoint_cache, s0);
 			s0 = decimalpoint_cache;
@@ -198,6 +204,57 @@ gethex( CONST char **sp, FPI *fpi, Long 
 		*exp = fpi->emin;
 		return STRTOG_Normal | STRTOG_Inexlo;
 		}
+	/*
+	 * Truncate the hex string if it is longer than the precision needed,
+	 * to avoid denial-of-service issues with very large strings.  Use
+	 * additional digits to insure precision.  Scan to-be-truncated digits
+	 * and replace with either '1' or '0' to ensure proper rounding.
+	 */
+	{
+		int maxdigits = ((fpi->nbits + 3) >> 2) + 2;
+		size_t nd = s1 - s0;
+#ifdef USE_LOCALE
+		int dplen = strlen((const char *)decimalpoint);
+#else
+		int dplen = 1;
+#endif
+
+		if (decpt && s0 < decpt)
+			nd -= dplen;
+		if (nd > maxdigits && (strunc = alloca(maxdigits + dplen + 2)) != NULL) {
+			ssize_t nd0 = decpt ? decpt - s0 - dplen : nd;
+			unsigned char *tp = strunc + maxdigits;
+			int found = 0;
+			if ((nd0 -= maxdigits) >= 0 || s0 >= decpt)
+				memcpy(strunc, s0, maxdigits);
+			else {
+				memcpy(strunc, s0, maxdigits + dplen);
+				tp += dplen;
+				}
+			s0 += maxdigits;
+			e += (nd - (maxdigits + 1)) << 2;
+			if (nd0 > 0) {
+				while(nd0-- > 0)
+					if (*s0++ != '0') {
+						found++;
+						break;
+						}
+				s0 += dplen;
+				}
+			if (!found && decpt) {
+				while(s0 < s1)
+					if(*s0++ != '0') {
+						found++;
+						break;
+						}
+				}
+			*tp++ = found ? '1' : '0';
+			*tp = 0;
+			s0 = strunc;
+			s1 = tp;
+			}
+		}
+
 	n = s1 - s0 - 1;
 	for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1)
 		k++;