diff options
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | generic/tclStringObj.c | 77 |
2 files changed, 75 insertions, 8 deletions
@@ -1,5 +1,11 @@ 2009-06-10 Don Porter <dgp@users.sourceforge.net> + * 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] + * generic/tclStringObj.c: Corrected failures to deal with the "pure unicode" representation of an empty string. Thanks to Julian Noble for reporting the problem. [Bug 2803109] diff --git a/generic/tclStringObj.c b/generic/tclStringObj.c index ded77e9..ebc15b3 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.123 2009/06/10 14:44:29 dgp Exp $ */ + * RCS: @(#) $Id: tclStringObj.c,v 1.124 2009/06/10 21:39:49 dgp Exp $ */ #include "tclInt.h" #include "tommath.h" @@ -1659,18 +1659,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 *const 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. @@ -1680,7 +1682,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); @@ -1691,7 +1693,12 @@ Tcl_AppendFormatToObj( continue; } if (numBytes) { + if (numBytes > limit) { + msg = overflow; + goto errorMsg; + } Tcl_AppendToObj(appendObj, span, numBytes); + limit -= numBytes; numBytes = 0; } @@ -1798,6 +1805,10 @@ Tcl_AppendFormatToObj( format += step; step = Tcl_UtfToUniChar(format, &ch); } + if (width > limit) { + msg = overflow; + goto errorMsg; + } /* * Step 4. Precision. @@ -1911,7 +1922,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) { @@ -1962,25 +1973,30 @@ 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; case 'b': Tcl_AppendToObj(segment, "0b", 2); + segmentLimit -= 2; break; } } @@ -2011,6 +2027,7 @@ Tcl_AppendFormatToObj( length--; bytes++; } + toAppend = length; /* * Canonical decimal string reps for integers are composed @@ -2019,6 +2036,9 @@ Tcl_AppendFormatToObj( */ if (gotPrecision) { + if (length < precision) { + segmentLimit -= (precision - length); + } while (length < precision) { Tcl_AppendToObj(segment, "0", 1); length++; @@ -2027,12 +2047,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; } @@ -2043,7 +2070,8 @@ Tcl_AppendFormatToObj( case 'X': case 'b': { 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; @@ -2077,11 +2105,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; @@ -2102,7 +2135,7 @@ Tcl_AppendFormatToObj( pure = Tcl_NewObj(); Tcl_SetObjLength(pure, numDigits); bytes = TclGetString(pure); - length = numDigits; + toAppend = length = numDigits; while (numDigits--) { int digitOffset; @@ -2126,6 +2159,9 @@ Tcl_AppendFormatToObj( mp_clear(&big); } if (gotPrecision) { + if (length < precision) { + segmentLimit -= (precision - length); + } while (length < precision) { Tcl_AppendToObj(segment, "0", 1); length++; @@ -2134,11 +2170,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; @@ -2222,15 +2265,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++; @@ -2239,7 +2295,12 @@ Tcl_AppendFormatToObj( objIndex += gotSequential; } if (numBytes) { + if (numBytes > limit) { + msg = overflow; + goto errorMsg; + } Tcl_AppendToObj(appendObj, span, numBytes); + limit -= numBytes; numBytes = 0; } |