Tcl Source Code

View Ticket
Login
Ticket UUID: 824752f10e0cd14625d2d476ee1ebbf8e7f5fd5c
Title: Crash in Tcl_ListObjReplace
Type: Bug Version: 8.6.6
Submitter: gahr Created on: 2016-11-04 13:47:32
Subsystem: 14. List Object Assigned To: nobody
Priority: 5 Medium Severity: Minor
Status: Closed Last Modified: 2016-11-04 15:44:33
Resolution: Fixed Closed By: jan.nijtmans
    Closed on: 2016-11-04 15:44:33
Description:
I am seeing a crash in Tcl_ListObjReplace, caused by a combination of invalid arguments and a NULL interp.

This is the stacktrace:

ore file '/home/gahr/tcl/unix/../../tclsh.core' (aarch64) was loaded.                                                                                            
(lldb) bt                                                                                                                                                         
* thread #1: tid = 101137, 0x0000000040547678 libtcl8.7.so`Tcl_SetObjResult + 12, name = 'tclsh', stop reason = signal SIGSEGV                                    
  * frame #0: 0x0000000040547678 libtcl8.7.so`Tcl_SetObjResult + 12                                                                                               
    frame #1: 0x000000004052ce1c libtcl8.7.so`Tcl_ListObjReplace + 252                                                                                            
    frame #2: 0x00000000404f8520 libtcl8.7.so`TclCompileEnsemble + 1312                                                                                           
    frame #3: 0x00000000404f93c0 libtcl8.7.so`TclAttemptCompileProc + 212                                                                                         
    frame #4: 0x00000000404e20ec libtcl8.7.so`TclCompileScript + 1588                                                                                             
    frame #5: 0x00000000404e155c libtcl8.7.so`TclSetByteCodeFromAny + 164                                                                                         
    frame #6: 0x00000000404fcb30 libtcl8.7.so`TclCompileObj + 500                                                                                                 
    frame #7: 0x000000004047e7f4 libtcl8.7.so`TclNREvalObjEx + 476                                                                                                
    frame #8: 0x0000000040529e84 libtcl8.7.so`TclNREvalFile + 896                                                                                                 
    frame #9: 0x0000000040492a14 libtcl8.7.so`TclNRSourceObjCmd + 188                                                                                             
    frame #10: 0x000000004047c75c libtcl8.7.so`Tcl_EvalObjv + 104                                                                                                 
    frame #11: 0x000000004047db90 libtcl8.7.so`TclEvalEx + 2032                                                                                                   
    frame #12: 0x00000000004009b4 tclsh`Tcl_AppInit + 20                                                                                                          
    frame #13: 0x0000000040530540 libtcl8.7.so`Tcl_MainEx + 440                                                                                                   
    frame #14: 0x0000000000400990 tclsh`main + 48                                                                                                                 
    frame #15: 0x00000000004008c0 tclsh`__start + 360                                                                                                             
    frame #16: 0x0000000040412658 ld-elf.so.1 at rtld_start.S:41


The fix is in two parts.

1) Make sure Tcl_SetObjResult is not called if interp is NULL

Index: generic/tclListObj.c
==================================================================
--- generic/tclListObj.c
+++ generic/tclListObj.c
@@ -905,12 +905,14 @@

        count = numElems - first;
     }

     if (objc > LIST_MAX - (numElems - count)) {
-       Tcl_SetObjResult(interp, Tcl_ObjPrintf(
-               "max length of a Tcl list (%d elements) exceeded", LIST_MAX));
+       if (interp != NULL) {
+           Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+                   "max length of a Tcl list (%d elements) exceeded", LIST_MAX));
+       }
        return TCL_ERROR;
     }
     isShared = (listRepPtr->refCount > 1);
     numRequired = numElems - count + objc; /* Known <= LIST_MAX */
     needGrow = numRequired > listRepPtr->maxElemCount;


2) Avoid calling Tcl_ListObjReplace with an invalid combination of arguments

Index: generic/tclEnsemble.c
==================================================================
--- generic/tclEnsemble.c
+++ generic/tclEnsemble.c
@@ -3133,11 +3133,11 @@
        /*
         * The length of the "replaced" list must be depth-1.  Trim back
         * any extra elements that might have been appended by failing
         * pathways above.
         */
-       (void) Tcl_ListObjReplace(NULL, replaced, depth-1, INT_MAX, 0, NULL);
+       (void) Tcl_ListObjReplace(NULL, replaced, depth-1, LIST_MAX, 0, NULL);

        /*
         * TODO: Reconsider whether we ought to call CompileToInvokedCommand()
         * when depth==1.  In that case we are choosing to emit the
         * INST_INVOKE_REPLACE bytecode when there is in fact no replacing


I am happy to commit this myself, but I would rather seek opinion on part 2) first.
User Comments: gahr (claiming to be [email protected]) added on 2016-11-04 13:55:39:
I should mention that this is happening only on FreeBSD 12-CURRENT, arm64 (ARMv8).