Menu

#4078 NRE crash calling tailcall from [dict with]

obsolete: 8.6a1
closed-fixed
9
2008-07-18
2008-07-14
Neil Madden
No

I'm seeing a crash testing NRE tailcall on Windows XP, using a tclsh86sg built from current CVS head (VS.NET 2003, static, symbols). Currently trying to track down the problem, possibly a funny interaction between [uplevel] and [tailcall]. I'll try and create a minimal example. In the meantime, the code that causes the crash is http://wiki.tcl.tk/16695. Copy that code to an editor, and then add the following interp alias at the top:

interp alias {} tailcall {} ::tcl::unsupported::tailcall

Then change the term::fold procedure to use [tailcall]:

proc term::fold {f z t} {
match $t {
{Lambda var body} -> { tailcall $f lam $z $var $body }
{Apply a b} -> { tailcall $f app $z [fold $f $z $a] [fold $f $z $b] }
{Var name} -> { tailcall $f var $z $name }
{IntLit i} -> { tailcall $f int $z $i }
{Add a b} -> { tailcall $f add $z [fold $f $z $a] [fold $f $z $b] }
{Sub a b} -> { tailcall $f sub $z [fold $f $z $a] [fold $f $z $b] }
{Mul a b} -> { tailcall $f mul $z [fold $f $z $a] [fold $f $z $b] }
{Div a b} -> { tailcall $f div $z [fold $f $z $a] [fold $f $z $b] }
}
}

This is an interesting case, as [match] uses both [uplevel] and [dict with], so plenty of tricksiness going on here. Possibly might not be ok to call [tailcall] here, but should produce an error rather than a crash. Crash seems to be an invalid pointer deref (cannot access location 0x00000014). Stack dump:

> tclsh86sg.exe!TclExecuteByteCode(Tcl_Interp * interp=0x00afa8b8, ByteCode * codePtr=0x00b86ae0) Line 2741 + 0xc C
tclsh86sg.exe!NRPostProcess(Tcl_Interp * interp=0x00afa8b8, int result=0, int objc=0, Tcl_Obj * const * objv=0x00000000) Line 7419 + 0x10 C
tclsh86sg.exe!TclEvalObjEx(Tcl_Interp * interp=0x00afa8b8, Tcl_Obj * objPtr=0x00b5e5e8, int flags=0, const CmdFrame * invoker=0x00afb314, int word=1) Line 5327 + 0x11 C
tclsh86sg.exe!Tcl_CatchObjCmd(void * dummy=0x00000000, Tcl_Interp * interp=0x00afa8b8, int objc=3, Tcl_Obj * const * objv=0x00afb33c) Line 253 + 0x1e C
tclsh86sg.exe!Tcl_EvalObjv(Tcl_Interp * interp=0x00afa8b8, int objc=3, Tcl_Obj * const * objv=0x00afb33c, int flags=2097152) Line 4079 + 0x15 C
tclsh86sg.exe!TclExecuteByteCode(Tcl_Interp * interp=0x00afa8b8, ByteCode * codePtr=0x00b67840) Line 2696 + 0x1c C
tclsh86sg.exe!NRPostProcess(Tcl_Interp * interp=0x00afa8b8, int result=0, int objc=0, Tcl_Obj * const * objv=0x00000000) Line 7419 + 0x10 C
tclsh86sg.exe!TclEvalObjEx(Tcl_Interp * interp=0x00afa8b8, Tcl_Obj * objPtr=0x00b862d0, int flags=0, const CmdFrame * invoker=0x00afb1c0, int word=2) Line 5327 + 0x11 C
tclsh86sg.exe!Tcl_WhileObjCmd(void * dummy=0x00000000, Tcl_Interp * interp=0x00afa8b8, int objc=3, Tcl_Obj * const * objv=0x00afb1f0) Line 4029 + 0x1e C
tclsh86sg.exe!Tcl_EvalObjv(Tcl_Interp * interp=0x00afa8b8, int objc=3, Tcl_Obj * const * objv=0x00afb1f0, int flags=2097152) Line 4079 + 0x15 C
tclsh86sg.exe!TclEvalEx(Tcl_Interp * interp=0x00afa8b8, const char * script=0x00b81ec8, int numBytes=6872, int flags=0, int line=219) Line 5044 + 0x16 C
tclsh86sg.exe!Tcl_EvalEx(Tcl_Interp * interp=0x00afa8b8, const char * script=0x00b81ec8, int numBytes=6872, int flags=0) Line 4751 + 0x17 C
tclsh86sg.exe!Tcl_FSEvalFileEx(Tcl_Interp * interp=0x00afa8b8, Tcl_Obj * pathPtr=0x00b69f28, const char * encodingName=0x00000000) Line 1776 + 0x13 C
tclsh86sg.exe!Tcl_Main(int argc=-1, char * * argv=0x00af38e8, int (Tcl_Interp *)* appInitProc=0x00401070) Line 443 + 0x11 C
tclsh86sg.exe!main(int argc=2, char * * argv=0x00af38e0) Line 102 + 0x12 C
tclsh86sg.exe!mainCRTStartup() Line 259 + 0x19 C
kernel32.dll!7c816fd7()

Offending line in tclExecute.c:
iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr;

I can't dig into that structure to see what those fields are set to as iPtr is just a #define, and the interp pointer shows nothing interesting. Looks like iPtr->cmdFramePtr is invalid at this point.

Discussion

  • Donal K. Fellows

    Logged In: YES
    user_id=79902
    Originator: NO

    Fact points to help chasing this down: [dict with] is not bytecoded (I didn't have the time to figure it out) and has a substantial amount of code to execute after the body. Some of said code *must* execute, even in error cases; disrupting that will cause trouble, memory leaks, nasal demons, the end of the world, cats and dogs living together, etc.

     
  • Neil Madden

    Neil Madden - 2008-07-14
    • priority: 5 --> 9
     
  • Neil Madden

    Neil Madden - 2008-07-14

    Logged In: YES
    user_id=102050
    Originator: YES

    Definitely an interaction with [dict with] rather than [uplevel]. Minimal script to reproduce bug:

    package require Tcl 8.6a1
    proc test env {
    dict with env { tcl::unsupported::tailcall test {} }
    }
    test ""

    (Note: if/when fixed this proc will result in an infinite loop).

     
  • Neil Madden

    Neil Madden - 2008-07-14
    • summary: NRE crash calling tailcall from uplevel context --> NRE crash calling tailcall from [dict with]
     
  • Donal K. Fellows

    Logged In: YES
    user_id=79902
    Originator: NO

    sounds like that means that [dict with] needs NRE-ing

     
  • Donal K. Fellows

    Logged In: YES
    user_id=79902
    Originator: NO

    done, but leaving open as I ran across a problem

    The problem was that while TclNREvalObjEx works just fine if the thing being evaluated is a general script, it crashes if it is the same command twice (see test dict-21.10 for an example). I've worked around it with TclNR_EvalObj but that breaks TIP#280 promises, so this is a short-term solution only.

    Two places to fix in tclDictObj.c, one at end of DictUpdateCmd, one at end of DictWithCmd.

     
  • Andreas Kupries

    Andreas Kupries - 2008-07-17

    Logged In: YES
    user_id=75003
    Originator: NO

    Oh, let me note something here. The cmdFrame structure for the info frame stack is kept on the C-stack, see for example TEBC, bcFrame. Then there are eoFrame and eeFrame (EvalObj, and EvalEx). The bcFrame being mismanaged because of the trampolining, that might be the cause of the nested dict crash.

     
  • miguel sofer

    miguel sofer - 2008-07-18

    Logged In: YES
    user_id=148712
    Originator: NO

    Crash fixed (refcounting bug); cmdFrame management was ok (all on the Tcl stack).

     
  • miguel sofer

    miguel sofer - 2008-07-18
    • status: open --> closed-fixed