diff options
author | apnadkarni <apnmbx-wits@yahoo.com> | 2023-04-29 03:15:11 (GMT) |
---|---|---|
committer | apnadkarni <apnmbx-wits@yahoo.com> | 2023-04-29 03:15:11 (GMT) |
commit | 85a2a9172920e13d3a1aa6ff861073ad690ccb12 (patch) | |
tree | a36d9351e7580de003fd8b57e031bdfedce03de8 | |
parent | a6d654980e6e0e35e1cb74dc978f654c216ce7e4 (diff) | |
parent | 175931c905a640bfd9ee76f0896c142b8549e391 (diff) | |
download | tcl-85a2a9172920e13d3a1aa6ff861073ad690ccb12.zip tcl-85a2a9172920e13d3a1aa6ff861073ad690ccb12.tar.gz tcl-85a2a9172920e13d3a1aa6ff861073ad690ccb12.tar.bz2 |
Fix [7cddd2845c] - crash with >= INT_MAX operands
-rw-r--r-- | generic/tclBasic.c | 28 | ||||
-rw-r--r-- | generic/tclExecute.c | 12 | ||||
-rw-r--r-- | generic/tclInt.h | 6 | ||||
-rw-r--r-- | generic/tclParse.c | 15 | ||||
-rw-r--r-- | generic/tclUtil.c | 40 | ||||
-rw-r--r-- | tests/bigdata.test | 20 |
6 files changed, 97 insertions, 24 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 480b72e..686cf62 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -5164,17 +5164,17 @@ TclEvalEx( { Interp *iPtr = (Interp *) interp; const char *p, *next; - const unsigned int minObjs = 20; + const int minObjs = 20; Tcl_Obj **objv, **objvSpace; int *expand, *lines, *lineSpace; Tcl_Token *tokenPtr; - int bytesLeft, expandRequested, code = TCL_OK; - Tcl_Size commandLength; + int expandRequested, code = TCL_OK; + Tcl_Size bytesLeft, commandLength; CallFrame *savedVarFramePtr;/* Saves old copy of iPtr->varFramePtr in case * TCL_EVAL_GLOBAL was set. */ int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS); int gotParse = 0; - TCL_HASH_TYPE i, objectsUsed = 0; + Tcl_Size i, objectsUsed = 0; /* These variables keep track of how much * state has been allocated while evaluating * the script, so that it can be freed @@ -5312,8 +5312,8 @@ TclEvalEx( Tcl_Size wordLine = line; const char *wordStart = parsePtr->commandStart; int *wordCLNext = clNext; - unsigned int objectsNeeded = 0; - unsigned int numWords = parsePtr->numWords; + Tcl_Size objectsNeeded = 0; + Tcl_Size numWords = parsePtr->numWords; /* * Generate an array of objects for the words of the command. @@ -5332,6 +5332,8 @@ TclEvalEx( for (objectsUsed = 0, tokenPtr = parsePtr->tokenPtr; objectsUsed < numWords; objectsUsed++, tokenPtr += tokenPtr->numComponents+1) { + Tcl_Size additionalObjsCount; + /* * TIP #280. Track lines to current word. Save the information * on a per-word basis, signaling dynamic words as needed. @@ -5381,11 +5383,21 @@ TclEvalEx( expandRequested = 1; expand[objectsUsed] = 1; - objectsNeeded += (numElements ? numElements : 1); + additionalObjsCount = (numElements ? numElements : 1); + } else { expand[objectsUsed] = 0; - objectsNeeded++; + additionalObjsCount = 1; + } + + /* Currently max command words in INT_MAX */ + if (additionalObjsCount > INT_MAX || + objectsNeeded > (INT_MAX - additionalObjsCount)) { + code = TclCommandWordLimitError(interp, -1); + Tcl_DecrRefCount(objv[objectsUsed]); + break; } + objectsNeeded += additionalObjsCount; if (wordCLNext) { TclContinuationsEnterDerived(objv[objectsUsed], diff --git a/generic/tclExecute.c b/generic/tclExecute.c index 789c139..56c8207 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -981,10 +981,10 @@ GrowEvaluationStack( { ExecStack *esPtr = eePtr->execStackPtr, *oldPtr = NULL; size_t newBytes; - int growth = growth1; - int newElems, currElems, needed = growth - (esPtr->endPtr - esPtr->tosPtr); + Tcl_Size growth = growth1; + Tcl_Size newElems, currElems, needed = growth - (esPtr->endPtr - esPtr->tosPtr); Tcl_Obj **markerPtr = esPtr->markerPtr, **memStart; - int moveWords = 0; + Tcl_Size moveWords = 0; if (move) { if (!markerPtr) { @@ -2825,8 +2825,12 @@ TEBCresume( pc += pcAdjustment; TEBC_YIELD(); - return TclNREvalObjv(interp, objc, objv, + if (objc > INT_MAX) { + return TclCommandWordLimitError(interp, objc); + } else { + return TclNREvalObjv(interp, objc, objv, TCL_EVAL_NOERR | TCL_EVAL_SOURCE_IN_FRAME, NULL); + } case INST_INVOKE_REPLACE: objc = TclGetUInt4AtPtr(pc+1); diff --git a/generic/tclInt.h b/generic/tclInt.h index d777e38..df708b4 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -4173,6 +4173,12 @@ MODULE_SCOPE int TclIndexEncode(Tcl_Interp *interp, Tcl_Obj *objPtr, MODULE_SCOPE Tcl_Size TclIndexDecode(int encoded, Tcl_Size endValue); MODULE_SCOPE int TclIndexInvalidError(Tcl_Interp *interp, const char *idxType, Tcl_Size idx); + +/* + * Error message utility functions + */ +MODULE_SCOPE int TclCommandWordLimitError(Tcl_Interp *interp, Tcl_Size count); + #endif /* TCL_MAJOR_VERSION > 8 */ /* Constants used in index value encoding routines. */ diff --git a/generic/tclParse.c b/generic/tclParse.c index 96f8a8e..d8b40e4 100644 --- a/generic/tclParse.c +++ b/generic/tclParse.c @@ -214,7 +214,7 @@ Tcl_ParseCommand( * command. */ char type; /* Result returned by CHAR_TYPE(*src). */ Tcl_Token *tokenPtr; /* Pointer to token being filled in. */ - int wordIndex; /* Index of word token for current word. */ + Tcl_Size wordIndex; /* Index of word token for current word. */ int terminators; /* CHAR_TYPE bits that indicate the end of a * command. */ const char *termPtr; /* Set by Tcl_ParseBraces/QuotedString to @@ -327,7 +327,7 @@ Tcl_ParseCommand( src = termPtr; numBytes = parsePtr->end - src; } else if (*src == '{') { - int expIdx = wordIndex + 1; + Tcl_Size expIdx = wordIndex + 1; Tcl_Token *expPtr; if (Tcl_ParseBraces(interp, src, numBytes, parsePtr, 1, @@ -345,7 +345,7 @@ Tcl_ParseCommand( expPtr = &parsePtr->tokenPtr[expIdx]; if ((0 == expandWord) /* Haven't seen prefix already */ - && (expIdx + 1 == (int)parsePtr->numTokens) + && (expIdx + 1 == parsePtr->numTokens) /* Only one token */ && (((1 == expPtr->size) /* Same length as prefix */ @@ -380,7 +380,7 @@ Tcl_ParseCommand( tokenPtr = &parsePtr->tokenPtr[wordIndex]; tokenPtr->size = src - tokenPtr->start; - tokenPtr->numComponents = (int)parsePtr->numTokens - (wordIndex + 1); + tokenPtr->numComponents = parsePtr->numTokens - (wordIndex + 1); if (expandWord) { Tcl_Size i; int isLiteral = 1; @@ -407,7 +407,8 @@ Tcl_ParseCommand( } if (isLiteral) { - int elemCount = 0, code = TCL_OK, literal = 1; + Tcl_Size elemCount = 0; + int code = TCL_OK, literal = 1; const char *nextElem, *listEnd, *elemStart; /* @@ -471,8 +472,8 @@ Tcl_ParseCommand( */ const char *listStart; - int growthNeeded = wordIndex + 2*elemCount - - (int)parsePtr->numTokens; + Tcl_Size growthNeeded = wordIndex + 2*elemCount + - parsePtr->numTokens; parsePtr->numWords += elemCount - 1; if (growthNeeded > 0) { diff --git a/generic/tclUtil.c b/generic/tclUtil.c index b765a0f..07b497b 100644 --- a/generic/tclUtil.c +++ b/generic/tclUtil.c @@ -3977,6 +3977,46 @@ TclIndexInvalidError ( } /* + *------------------------------------------------------------------------ + * + * TclCommandWordLimitErrpr -- + * + * Generates an error message limit on number of command words exceeded. + * + * Results: + * Always return TCL_ERROR. + * + * Side effects: + * If interp is not-NULL, an error message is stored in it. + * + *------------------------------------------------------------------------ + */ +int +TclCommandWordLimitError ( + Tcl_Interp *interp, /* May be NULL */ + Tcl_Size count) /* If <= 0, "unknown" */ +{ + if (interp) { + if (count > 0) { + Tcl_SetObjResult( + interp, + Tcl_ObjPrintf("Number of words (%" TCL_SIZE_MODIFIER + "d) in command exceeds limit %" TCL_SIZE_MODIFIER + "d.", + count, + (Tcl_Size)INT_MAX)); + } + else { + Tcl_SetObjResult(interp, + Tcl_ObjPrintf("Number of words in command exceeds " + "limit %" TCL_SIZE_MODIFIER "d.", + (Tcl_Size)INT_MAX)); + } + } + return TCL_ERROR; /* Always */ +} + +/* *---------------------------------------------------------------------- * * ClearHash -- diff --git a/tests/bigdata.test b/tests/bigdata.test index a1ccd32..ccd34af 100644 --- a/tests/bigdata.test +++ b/tests/bigdata.test @@ -879,15 +879,25 @@ bigtest linsert-bigdata-1 "linsert" {4294967330 1} -body { # # list and {*} -bigtestRO list-bigdata-1 {list {*} } {4294967296 0 4294967295} -body { - unset -nocomplain l2 +# TODO - compiled and uncompiled behave differently so tested separately +test list-bigdata-1.compiled {list {*}} -body { + set l [bigList 0x100000000] set l2 [list {*}$l] + unset l list [llength $l2] [lindex $l2 0] [lindex $l2 end] -} -setup { - set l [bigList 0x100000000] } -cleanup { bigClean -} -constraints bug-7cddd2845c +} -constraints { + bigdata +} -result {4294967296 0 5} +test list-bigdata-1.uncompiled {list {*}} -body { + set l [bigList 0x7fffffff] + testevalex {set l2 [list {*}$l]} +} -cleanup { + bigClean +} -constraints { + bigdata +} -result {Number of words in command exceeds limit 2147483647.} -returnCodes error # # llength |