diff options
-rw-r--r-- | generic/tclBasic.c | 34 | ||||
-rw-r--r-- | generic/tclDecls.h | 14 | ||||
-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 |
7 files changed, 109 insertions, 32 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 480b72e..1b4bca1 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -3298,7 +3298,11 @@ invokeObj2Command( Command *cmdPtr = (Command *) clientData; if (objc > INT_MAX) { - objc = TCL_INDEX_NONE; /* TODO - why? Should error, not truncate */ + /* Since TCL_INDEX_NONE is an invalid value for objc, + * calling cmdPtr->objProc or cmdPtr->nreProc will + * eventually result in a Tcl_WrongNumArgs() call. + * That's exactly what we want to happen. */ + objc = TCL_INDEX_NONE; } if (cmdPtr->objProc != NULL) { result = cmdPtr->objProc(cmdPtr->objClientData, interp, objc, objv); @@ -5164,17 +5168,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 +5316,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 +5336,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 +5387,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/tclDecls.h b/generic/tclDecls.h index db8f957..feb7a64 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -4172,31 +4172,31 @@ extern const TclStubs *tclStubsPtr; #if TCL_MAJOR_VERSION < 9 || !defined(TCL_NO_DEPRECATED) # undef Tcl_ListObjGetElements # define Tcl_ListObjGetElements(interp, listPtr, objcPtr, objvPtr) (sizeof(*(objcPtr)) == sizeof(int) \ - ? tclStubsPtr->tclListObjGetElements((interp), (listPtr), (int *)(void *)(objcPtr), (objvPtr)) \ + ? tclStubsPtr->tclListObjGetElements((interp), (listPtr), (objcPtr), (objvPtr)) \ : tclStubsPtr->tcl_ListObjGetElements((interp), (listPtr), (Tcl_Size *)(void *)(objcPtr), (objvPtr))) # undef Tcl_ListObjLength # define Tcl_ListObjLength(interp, listPtr, lengthPtr) (sizeof(*(lengthPtr)) == sizeof(int) \ - ? tclStubsPtr->tclListObjLength((interp), (listPtr), (int *)(void *)(lengthPtr)) \ + ? tclStubsPtr->tclListObjLength((interp), (listPtr), (lengthPtr)) \ : tclStubsPtr->tcl_ListObjLength((interp), (listPtr), (Tcl_Size *)(void *)(lengthPtr))) # undef Tcl_DictObjSize # define Tcl_DictObjSize(interp, dictPtr, sizePtr) (sizeof(*(sizePtr)) == sizeof(int) \ - ? tclStubsPtr->tclDictObjSize((interp), (dictPtr), (int *)(void *)(sizePtr)) \ + ? tclStubsPtr->tclDictObjSize((interp), (dictPtr), (sizePtr)) \ : tclStubsPtr->tcl_DictObjSize((interp), (dictPtr), (Tcl_Size *)(void *)(sizePtr))) # undef Tcl_SplitList # define Tcl_SplitList(interp, listStr, argcPtr, argvPtr) (sizeof(*(argcPtr)) == sizeof(int) \ - ? tclStubsPtr->tclSplitList((interp), (listStr), (int *)(void *)(argcPtr), (argvPtr)) \ + ? tclStubsPtr->tclSplitList((interp), (listStr), (argcPtr), (argvPtr)) \ : tclStubsPtr->tcl_SplitList((interp), (listStr), (Tcl_Size *)(void *)(argcPtr), (argvPtr))) # undef Tcl_SplitPath # define Tcl_SplitPath(path, argcPtr, argvPtr) (sizeof(*(argcPtr)) == sizeof(int) \ - ? tclStubsPtr->tclSplitPath((path), (int *)(void *)(argcPtr), (argvPtr)) \ + ? tclStubsPtr->tclSplitPath((path), (argcPtr), (argvPtr)) \ : tclStubsPtr->tcl_SplitPath((path), (Tcl_Size *)(void *)(argcPtr), (argvPtr))) # undef Tcl_FSSplitPath # define Tcl_FSSplitPath(pathPtr, lenPtr) (sizeof(*(lenPtr)) == sizeof(int) \ - ? tclStubsPtr->tclFSSplitPath((pathPtr), (int *)(void *)(lenPtr)) \ + ? tclStubsPtr->tclFSSplitPath((pathPtr), (lenPtr)) \ : tclStubsPtr->tcl_FSSplitPath((pathPtr), (Tcl_Size *)(void *)(lenPtr))) # undef Tcl_ParseArgsObjv # define Tcl_ParseArgsObjv(interp, argTable, objcPtr, objv, remObjv) (sizeof(*(objcPtr)) == sizeof(int) \ - ? tclStubsPtr->tclParseArgsObjv((interp), (argTable), (int *)(void *)(objcPtr), (objv), (remObjv)) \ + ? tclStubsPtr->tclParseArgsObjv((interp), (argTable), (objcPtr), (objv), (remObjv)) \ : tclStubsPtr->tcl_ParseArgsObjv((interp), (argTable), (Tcl_Size *)(void *)(objcPtr), (objv), (remObjv))) #endif /* TCL_MAJOR_VERSION < 9 || !defined(TCL_NO_DEPRECATED) */ #else 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 286f930..6ecf4a0 100644 --- a/tests/bigdata.test +++ b/tests/bigdata.test @@ -898,15 +898,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 |