- priority: 5 --> 2
- status: open --> closed-fixed
OriginalBugID: 3737 Bug
Version: 8.2.2
SubmitDate: '1999-12-01'
LastModified: '1999-12-02'
Severity: CRIT
Status: Released
Submitter: techsupp
ChangedBy: hobbs
OS: All
FixedDate: '1999-12-02'
FixedInVersion: 8.2.3
ClosedDate: '2000-10-25'
Name:
Don Porter
CVS:
$Id: tclCompile.c,v 1.17 1999/10/21 02:16:22 hobbs Exp $
CustomShell:
Compiled with -DTCL_COMPILE_DEBUG
Comments:
Thanks to Johannes Schacht <Johannes.Schacht@cgk.siemens.de> for
demonstrating the bug.
The problem is in file generic/tclCompile.c in the routine
TclCompileScript(interp, script, numBytes, nested, envPtr).
As TclCompileScript is written, it assumes that script is
a substring of envPtr->source. This assumption is used in
line 839 when computing the srcOffset for a command as
(parse.commandStart - envPtr->source). parse.commandStart
points into script. The subtraction only makes sense if
script is a substring of envPtr->source. This is almost
always the case.
However, when compiling [expr + {[set a 0]}], the routine
TclCompileExprWords() tries to compile the substituted,
concatenated arguments of [expr]. It forms the new string
"+ [set a 0]" and passes it as the script argument to
TclCompileExpr(interp, script, numBytes, envPtr). However
it passes in the same envPtr as for the whole command. The
new string is not a substring of envPtr->source. It's in an
entirely different section of memory, so the pointer subtraction
results in garbage for the srcOffset values. The garbage values
can be seen in the compile debugging information.
This error only causes a crash if a routine tries to use the bad
srcOffset values. That happens if the nested command returns an
error. While trying to construct an appropriate ::errorInfo string,
the routine Tcl_LogCommandInfo(). That routine tries to use the
bad srcOffset values and is led off into memory not belonging to
the process, causing the crash. The second example demonstrates this.
It's not immediately clear to me what the best fix is. One might try
to remove the assumption in TclCompileScript that script is a subset
of envPtr->source. Or, one might try to detect this case in
TclCompileExprWords() and not try the "optimistic" compile of the
arguments of [expr] in this circumstance.
I hope that with these examples and analysis that someone with more
familiarity with the design of Tcl's bytecode compiler machinery can
determine the most appropriate fix.
ReproducibleScript:
# FILE: demo.tcl
catch {foo} ;# Get all auto-loading, initialization, etc. done
;# before turning on verbose compile messages
set tcl_traceCompile 2
catch {expr + {[set a 0]}}
catch {expr + {[error foo]}}
ObservedBehavior:
$ tclsh demo.tcl
Compiling: "expr + {[set a 0]}"
ByteCode 0x201142a0, refCt 1, epoch 0, interp 0x20102f40 (epoch 0)
Source "expr + {[set a 0]}"
Cmds 2, src 18, inst 25, litObjs 5, aux 0, stkDepth 2, code/src 15.33
Code 276 = header 160+inst 25+litObj 40+exc 28+aux 0+cmdMap 12
Exception ranges 1, depth 1:
0: level 1, catch, pc 5-10, catch 14
Commands 2:
1: pc 0-23, src 0-17 2: pc 5-9, src -1129533--1129527
Command 1: "expr + {[set a 0]}"
(0) beginCatch4 0
Command 2: "set a 0"
(5) push1 0 # "a"
(7) push1 1 # "0"
(9) storeScalarStk
(10) uplus
(11) endCatch
(12) jump1 12 # pc 24
(14) endCatch
(15) push1 2 # "+"
(17) push1 3 # " "
(19) push1 4 # "[set a 0]"
(21) concat1 3
(23) exprStk
(24) done
Compiling: "expr + {[error foo]}"
ByteCode 0x201142a0, refCt 1, epoch 0, interp 0x20102f40 (epoch 0)
Source "expr + {[error foo]}"
Cmds 2, src 20, inst 26, litObjs 5, aux 0, stkDepth 2, code/src 13.80
Code 276 = header 160+inst 26+litObj 40+exc 28+aux 0+cmdMap 12
Exception ranges 1, depth 1:
0: level 1, catch, pc 5-11, catch 15
Commands 2:
1: pc 0-24, src 0-19 2: pc 5-10, src -1113933--1113925
Command 1: "expr + {[error foo]}"
(0) beginCatch4 0
Command 2: "error foo"
(5) push1 0 # "error"
(7) push1 1 # "foo"
(9) invokeStk1 2
(11) uplus
(12) endCatch
(13) jump1 12 # pc 25
(15) endCatch
(16) push1 2 # "+"
(18) push1 3 # " "
(20) push1 4 # "[error foo]"
(22) concat1 3
(24) exprStk
(25) done
Segmentation fault
DesiredBehavior:
No crash.
This was essentially due to an aggressive optimization done by
the 'expr' compiling command. In essence, it was checking to
see if all the words that came in were literals, and making the
optimization that you could compile these by creating a single
string out of the literals:
expr 1 + {[string length abc]}
became in essence the same as
expr {1 + [string length abc]}
However, the error case would cause the invalid checking back
into the source string, and thus a crash, as mentioned in the
description above.
Since this all occurs in degenerate cases (the user should
really have used the latter 'expr'), it was decided to remove
the problematic optimization. This will cause the former to
not be compiled and thus be slower, but still correct. The
latter will still be compiled, and is the recommended usage.
However, for any case, it no longer seg faults.
This fix is in 8.2.3.
-- 12/02/1999 hobbs