From 947e3f840513df07d5f854ebe9fcf7e348668878 Mon Sep 17 00:00:00 2001 From: apnadkarni Date: Sat, 29 Apr 2023 02:11:48 +0000 Subject: Limit scripts to INT_MAX-1 bytes --- generic/tclCompile.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/generic/tclCompile.c b/generic/tclCompile.c index 9448241..69a8383 100644 --- a/generic/tclCompile.c +++ b/generic/tclCompile.c @@ -2136,7 +2136,7 @@ TclCompileScript( * serves as context for finding and compiling * commands. May not be NULL. */ const char *script, /* The source script to compile. */ - Tcl_Size numBytes, /* Number of bytes in script. If -1, the + Tcl_Size numBytes, /* Number of bytes in script. If < 0, the * script consists of all bytes up to the * first null character. */ CompileEnv *envPtr) /* Holds resulting instructions. */ @@ -2167,9 +2167,26 @@ TclCompileScript( return; } + if (numBytes < 0) { + numBytes = strlen(script); + } + /* Each iteration compiles one command from the script. */ if (numBytes > 0) { + if (numBytes >= INT_MAX) { + /* + * Note this gets -errorline as 1. Not worth figuring out which line + * crosses the limit to get -errorline for this error case. + */ + Tcl_SetObjResult(interp, + Tcl_ObjPrintf("Script length %" TCL_SIZE_MODIFIER + "d exceeds max permitted length %d.", + numBytes, (int)INT_MAX-1)); + Tcl_SetErrorCode(interp, "TCL", "LIMIT", "SCRIPTLENGTH", NULL); + TclCompileSyntaxError(interp, envPtr); + return; + } /* * Don't use system stack (size of Tcl_Parse is ca. 400 bytes), so * many nested compilations (body enclosed in body) can cause abnormal -- cgit v0.12 From 6c973cf9a204f96f3350a7908067f2707e130afb Mon Sep 17 00:00:00 2001 From: apnadkarni Date: Sat, 29 Apr 2023 16:16:58 +0000 Subject: Add tests. Panic on bytecode length limit --- generic/tclCompile.c | 12 ++++++++++++ tests/bigdata.test | 21 ++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/generic/tclCompile.c b/generic/tclCompile.c index 69a8383..926c492 100644 --- a/generic/tclCompile.c +++ b/generic/tclCompile.c @@ -870,6 +870,18 @@ TclSetByteCodeFromAny( } /* + * After optimization is all done, check that byte code length limits + * are not exceeded. Bug [27b3ce2997]. + */ + if ((compEnv.codeNext - compEnv.codeStart) > INT_MAX) { + /* + * Cannot just return TCL_ERROR as callers ignore return value. + * TODO - May be use TclCompileSyntaxError here? + */ + Tcl_Panic("Maximum byte code length %d exceeded.", INT_MAX); + } + + /* * Change the object into a ByteCode object. Ownership of the literal * objects and aux data items passes to the ByteCode object. */ diff --git a/tests/bigdata.test b/tests/bigdata.test index a1ccd32..286f930 100644 --- a/tests/bigdata.test +++ b/tests/bigdata.test @@ -116,6 +116,26 @@ set ::bigLengths(patlenmultiple) [bigPatlenMultiple $::bigLengths(intmax)] set ::bigLengths(upatlenmultiple) [bigPatlenMultiple $::bigLengths(uintmax)] # +# script limits +bigtestRO script-length-bigdata-1 {Test script length limit} b -body { + try [string cat [string repeat " " 0x7ffffff7] "set a b"] +} +# TODO - different behaviour between compiled and uncompiled +test script-length-bigdata-2.compiled {Test script length limit} -body { + try [string cat [string repeat " " 0x7ffffff8] "set a b"] +} -result {Script length 2147483647 exceeds max permitted length 2147483646.} -returnCodes error +test script-length-bigdata-2.uncompiled {Test script length limit} -body { + testevalex [string cat [string repeat " " 0x7ffffff8] "set a b"] +} -result b +test script-bytecode-length-bigdata-1 {Test bytecode length limit} -body { + # Note we need to exceed bytecode limit without exceeding script char limit + set s [string repeat {{*}$x;} [expr 0x7fffffff/6]] + catch $s r e +} -cleanup { + bigClean +} -constraints panic-in-EnterCmdStartData + +# # string cat bigtest string-cat-bigdata-1 "string cat large small result > INT_MAX" 1 -body { string equal \ @@ -159,7 +179,6 @@ bigtestRO string-equal/compare-bigdata-2 "string compare/equal -length unequal s } -cleanup { bigClean } -# -constraints bug-a814ee5bbd # # string first -- cgit v0.12