summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tclBasic.c34
-rw-r--r--generic/tclDecls.h14
-rw-r--r--generic/tclExecute.c12
-rw-r--r--generic/tclInt.h6
-rw-r--r--generic/tclParse.c15
-rw-r--r--generic/tclUtil.c40
-rw-r--r--tests/bigdata.test20
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