From 855a348eeae253e6765ea3edf689e41d79a7c081 Mon Sep 17 00:00:00 2001 From: dgp Date: Wed, 10 Jun 2009 21:38:13 +0000 Subject: * generic/tclStringObj.c: Revised [format] to not overflow the integer calculations computing the length of the %ll formats of really big integers. Also added protections so that [format]s that would produce results overflowing the maximum string length of Tcl values throw a normal Tcl error instead of a panic. [Bug 2801413] --- ChangeLog | 10 ++++++- generic/tclStringObj.c | 76 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index fb2f2b3..de8d594 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2009-06-10 Don Porter + + * generic/tclStringObj.c: Revised [format] to not overflow the + integer calculations computing the length of the %ll formats of + really big integers. Also added protections so that [format]s that + would produce results overflowing the maximum string length of Tcl + values throw a normal Tcl error instead of a panic. [Bug 2801413] + 2006-06-09 Kevin B. Kenny * generic/tclGetDate.y: Fixed a thread safety bug in the generated @@ -13,7 +21,7 @@ * library/tzdata/Asia/Dhaka: New DST rule for Bangladesh. (Olson's tzdata2009i.) - + 2009-06-02 Don Porter * generic/tclExecute.c: Replace dynamically-initialized table with diff --git a/generic/tclStringObj.c b/generic/tclStringObj.c index a0992aa..e844e14 100644 --- a/generic/tclStringObj.c +++ b/generic/tclStringObj.c @@ -33,7 +33,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclStringObj.c,v 1.70.2.12 2009/04/15 19:07:04 dgp Exp $ */ + * RCS: @(#) $Id: tclStringObj.c,v 1.70.2.13 2009/06/10 21:38:13 dgp Exp $ */ #include "tclInt.h" #include "tommath.h" @@ -1800,18 +1800,20 @@ Tcl_AppendFormatToObj( { const char *span = format, *msg; int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0; - int originalLength; + int originalLength, limit; static const char *mixedXPG = "cannot mix \"%\" and \"%n$\" conversion specifiers"; static const char *badIndex[2] = { "not enough arguments for all format specifiers", "\"%n$\" argument index out of range" }; + static const char *overflow = "max size for a Tcl value exceeded"; if (Tcl_IsShared(appendObj)) { Tcl_Panic("%s called with shared object", "Tcl_AppendFormatToObj"); } TclGetStringFromObj(appendObj, &originalLength); + limit = INT_MAX - originalLength; /* * Format string is NUL-terminated. @@ -1821,7 +1823,7 @@ Tcl_AppendFormatToObj( char *end; int gotMinus, gotHash, gotZero, gotSpace, gotPlus, sawFlag; int width, gotPrecision, precision, useShort, useWide, useBig; - int newXpg, numChars, allocSegment = 0; + int newXpg, numChars, allocSegment = 0, segmentLimit, segmentNumBytes; Tcl_Obj *segment; Tcl_UniChar ch; int step = Tcl_UtfToUniChar(format, &ch); @@ -1832,7 +1834,12 @@ Tcl_AppendFormatToObj( continue; } if (numBytes) { + if (numBytes > limit) { + msg = overflow; + goto errorMsg; + } Tcl_AppendToObj(appendObj, span, numBytes); + limit -= numBytes; numBytes = 0; } @@ -1939,6 +1946,10 @@ Tcl_AppendFormatToObj( format += step; step = Tcl_UtfToUniChar(format, &ch); } + if (width > limit) { + msg = overflow; + goto errorMsg; + } /* * Step 4. Precision. @@ -2051,7 +2062,7 @@ Tcl_AppendFormatToObj( long l; Tcl_WideInt w; mp_int big; - int isNegative = 0; + int toAppend, isNegative = 0; if (useBig) { if (Tcl_GetBignumFromObj(interp, segment, &big) != TCL_OK) { @@ -2102,21 +2113,25 @@ Tcl_AppendFormatToObj( segment = Tcl_NewObj(); allocSegment = 1; + segmentLimit = INT_MAX; Tcl_IncrRefCount(segment); if ((isNegative || gotPlus || gotSpace) && (useBig || (ch == 'd'))) { Tcl_AppendToObj(segment, (isNegative ? "-" : gotPlus ? "+" : " "), 1); + segmentLimit -= 1; } if (gotHash) { switch (ch) { case 'o': Tcl_AppendToObj(segment, "0", 1); + segmentLimit -= 1; precision--; break; case 'x': case 'X': Tcl_AppendToObj(segment, "0x", 2); + segmentLimit -= 2; break; } } @@ -2147,6 +2162,7 @@ Tcl_AppendFormatToObj( length--; bytes++; } + toAppend = length; /* * Canonical decimal string reps for integers are composed @@ -2155,6 +2171,9 @@ Tcl_AppendFormatToObj( */ if (gotPrecision) { + if (length < precision) { + segmentLimit -= (precision - length); + } while (length < precision) { Tcl_AppendToObj(segment, "0", 1); length++; @@ -2163,12 +2182,19 @@ Tcl_AppendFormatToObj( } if (gotZero) { length += Tcl_GetCharLength(segment); + if (length < width) { + segmentLimit -= (width - length); + } while (length < width) { Tcl_AppendToObj(segment, "0", 1); length++; } } - Tcl_AppendToObj(segment, bytes, -1); + if (toAppend > segmentLimit) { + msg = overflow; + goto errorMsg; + } + Tcl_AppendToObj(segment, bytes, toAppend); Tcl_DecrRefCount(pure); break; } @@ -2178,7 +2204,8 @@ Tcl_AppendFormatToObj( case 'x': case 'X': { Tcl_WideUInt bits = (Tcl_WideUInt)0; - int length, numBits = 4, numDigits = 0, base = 16; + Tcl_WideInt numDigits = (Tcl_WideInt)0; + int length, numBits = 4, base = 16; int index = 0, shift = 0; Tcl_Obj *pure; char *bytes; @@ -2210,11 +2237,16 @@ Tcl_AppendFormatToObj( int leftover = (big.used * DIGIT_BIT) % numBits; mp_digit mask = (~(mp_digit)0) << (DIGIT_BIT-leftover); - numDigits = 1 + ((big.used * DIGIT_BIT) / numBits); + numDigits = 1 + + (((Tcl_WideInt)big.used * DIGIT_BIT) / numBits); while ((mask & big.dp[big.used-1]) == 0) { numDigits--; mask >>= numBits; } + if (numDigits > INT_MAX) { + msg = overflow; + goto errorMsg; + } } else if (!useBig) { unsigned long int ul = (unsigned long int) l; @@ -2235,7 +2267,7 @@ Tcl_AppendFormatToObj( pure = Tcl_NewObj(); Tcl_SetObjLength(pure, numDigits); bytes = TclGetString(pure); - length = numDigits; + toAppend = length = numDigits; while (numDigits--) { int digitOffset; @@ -2259,6 +2291,9 @@ Tcl_AppendFormatToObj( mp_clear(&big); } if (gotPrecision) { + if (length < precision) { + segmentLimit -= (precision - length); + } while (length < precision) { Tcl_AppendToObj(segment, "0", 1); length++; @@ -2267,11 +2302,18 @@ Tcl_AppendFormatToObj( } if (gotZero) { length += Tcl_GetCharLength(segment); + if (length < width) { + segmentLimit -= (width - length); + } while (length < width) { Tcl_AppendToObj(segment, "0", 1); length++; } } + if (toAppend > segmentLimit) { + msg = overflow; + goto errorMsg; + } Tcl_AppendObjToObj(segment, pure); Tcl_DecrRefCount(pure); break; @@ -2355,15 +2397,28 @@ Tcl_AppendFormatToObj( numChars = Tcl_GetCharLength(segment); if (!gotMinus) { + if (numChars < width) { + limit -= (width - numChars); + } while (numChars < width) { Tcl_AppendToObj(appendObj, (gotZero ? "0" : " "), 1); numChars++; } } + + Tcl_GetStringFromObj(segment, &segmentNumBytes); + if (segmentNumBytes > limit) { + msg = overflow; + goto errorMsg; + } Tcl_AppendObjToObj(appendObj, segment); + limit -= segmentNumBytes; if (allocSegment) { Tcl_DecrRefCount(segment); } + if (numChars < width) { + limit -= (width - numChars); + } while (numChars < width) { Tcl_AppendToObj(appendObj, (gotZero ? "0" : " "), 1); numChars++; @@ -2372,7 +2427,12 @@ Tcl_AppendFormatToObj( objIndex += gotSequential; } if (numBytes) { + if (numBytes > limit) { + msg = overflow; + goto errorMsg; + } Tcl_AppendToObj(appendObj, span, numBytes); + limit -= numBytes; numBytes = 0; } -- cgit v0.12