Tcl Source Code

View Ticket
Login
Ticket UUID: 1999176
Title: glob can cause crash on windows Tcl8.5.2
Type: Bug Version: obsolete: 8.5.2
Submitter: nobody Created on: 2008-06-20 22:22:55
Subsystem: 36. Pathname Management Assigned To: dgp
Priority: 9 Immediate Severity:
Status: Closed Last Modified: 2008-06-30 02:39:44
Resolution: Fixed Closed By: dgp
    Closed on: 2008-06-29 19:39:44
Description:
This glob has crashed my windows on
with Tcl8.5.2

glob -nocomplain -directory {} anything

Artur
[email protected]
User Comments: dgp added on 2008-06-30 02:09:51:
Logged In: YES 
user_id=80530
Originator: NO


leak plugged for 8.5.3 and 8.6a2.

dgp added on 2008-06-30 02:01:02:
Logged In: YES 
user_id=80530
Originator: NO


The trouble is the if () {} else {}
on lines 1970 - 1994.  With the recent
changes, the empty string path actually
needs to take both branches, so the logic
needs a revision.

dgp added on 2008-06-30 01:36:05:
Logged In: YES 
user_id=80530
Originator: NO


pointsman reports that commenting
out line 1910 of Revision 1.66.2.3
of tclPathObj.c plugs the leak.

This indicates that somehow refCounting
is broken, but I'm not yet able to
sort it out.  I think it has something
to do with not accounting for the strange
Tcl_FSGetCwd() interface that increments
refcount for the caller.

pointsman added on 2008-06-29 23:37:51:
Logged In: YES 
user_id=13222
Originator: NO

Can't attach (not my bug), here's the valgrind output:

==2077== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 105 from 3)
==2077== malloc/free: in use at exit: 218 bytes in 8 blocks.
==2077== malloc/free: 35,978 allocs, 35,970 frees, 2,582,181 bytes allocated.
==2077== For counts of detected errors, rerun with: -v
==2077== searching for pointers to 8 not-freed blocks.
==2077== checked 110,236 bytes.
==2077== 
==2077== 218 (24 direct, 194 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
==2077==    at 0x4019925: malloc (m_replacemalloc/vg_replace_malloc.c:207)
==2077==    by 0x404038D: TclpAlloc (/home/rolf/tcltk/tcl8.5.3/generic/tclAlloc.c:706)
==2077==    by 0x404A87D: Tcl_Alloc (/home/rolf/tcltk/tcl8.5.3/generic/tclCkalloc.c:1019)
==2077==    by 0x40EB07C: Tcl_NewStringObj (/home/rolf/tcltk/tcl8.5.3/generic/tclStringObj.c:210)
==2077==    by 0x40D9CE6: TclSubstTokens (/home/rolf/tcltk/tcl8.5.3/generic/tclParse.c:2252)
==2077==    by 0x4044BDC: TclEvalEx (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:4191)
==2077==    by 0x404479F: Tcl_EvalEx (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:4002)
==2077==    by 0x40456F9: TclEvalObjEx (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:4674)
==2077==    by 0x404542A: Tcl_EvalObjEx (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:4555)
==2077==    by 0x40E428C: Tcl_UplevelObjCmd (/home/rolf/tcltk/tcl8.5.3/generic/tclProc.c:911)
==2077==    by 0x4044126: TclEvalObjvInternal (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:3649)
==2077==    by 0x4094C10: TclExecuteByteCode (/home/rolf/tcltk/tcl8.5.3/generic/tclExecute.c:2327)
==2077==    by 0x40E5062: TclObjInterpProcCore (/home/rolf/tcltk/tcl8.5.3/generic/tclProc.c:1721)
==2077==    by 0x40E4FBC: TclObjInterpProc (/home/rolf/tcltk/tcl8.5.3/generic/tclProc.c:1615)
==2077==    by 0x4044126: TclEvalObjvInternal (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:3649)
==2077==    by 0x4044618: Tcl_EvalObjv (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:3844)
==2077==    by 0x4045641: TclEvalObjEx (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:4643)
==2077==    by 0x404542A: Tcl_EvalObjEx (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:4555)
==2077==    by 0x40E428C: Tcl_UplevelObjCmd (/home/rolf/tcltk/tcl8.5.3/generic/tclProc.c:911)
==2077==    by 0x4044126: TclEvalObjvInternal (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:3649)
==2077==    by 0x4094C10: TclExecuteByteCode (/home/rolf/tcltk/tcl8.5.3/generic/tclExecute.c:2327)
==2077==    by 0x40E5062: TclObjInterpProcCore (/home/rolf/tcltk/tcl8.5.3/generic/tclProc.c:1721)
==2077==    by 0x40E4FBC: TclObjInterpProc (/home/rolf/tcltk/tcl8.5.3/generic/tclProc.c:1615)
==2077==    by 0x4044126: TclEvalObjvInternal (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:3649)
==2077==    by 0x4044618: Tcl_EvalObjv (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:3844)
==2077==    by 0x4045641: TclEvalObjEx (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:4643)
==2077==    by 0x404542A: Tcl_EvalObjEx (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:4555)
==2077==    by 0x40E428C: Tcl_UplevelObjCmd (/home/rolf/tcltk/tcl8.5.3/generic/tclProc.c:911)
==2077==    by 0x4044126: TclEvalObjvInternal (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:3649)
==2077==    by 0x4094C10: TclExecuteByteCode (/home/rolf/tcltk/tcl8.5.3/generic/tclExecute.c:2327)
==2077==    by 0x40E5062: TclObjInterpProcCore (/home/rolf/tcltk/tcl8.5.3/generic/tclProc.c:1721)
==2077==    by 0x40E4FBC: TclObjInterpProc (/home/rolf/tcltk/tcl8.5.3/generic/tclProc.c:1615)
==2077==    by 0x40CC576: InvokeImportedCmd (/home/rolf/tcltk/tcl8.5.3/generic/tclNamesp.c:1889)
==2077==    by 0x4044126: TclEvalObjvInternal (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:3649)
==2077==    by 0x4094C10: TclExecuteByteCode (/home/rolf/tcltk/tcl8.5.3/generic/tclExecute.c:2327)
==2077==    by 0x40931A3: TclCompEvalObj (/home/rolf/tcltk/tcl8.5.3/generic/tclExecute.c:1473)
==2077==    by 0x404589D: TclEvalObjEx (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:4763)
==2077==    by 0x40AD119: SlaveEval (/home/rolf/tcltk/tcl8.5.3/generic/tclInterp.c:2479)
==2077==    by 0x40ACC31: SlaveObjCmd (/home/rolf/tcltk/tcl8.5.3/generic/tclInterp.c:2288)
==2077==    by 0x4044126: TclEvalObjvInternal (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:3649)
==2077==    by 0x4045021: TclEvalEx (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:4296)
==2077==    by 0x404479F: Tcl_EvalEx (/home/rolf/tcltk/tcl8.5.3/generic/tclBasic.c:4002)
==2077==    by 0x40C1D43: Tcl_FSEvalFileEx (/home/rolf/tcltk/tcl8.5.3/generic/tclIOUtil.c:1820)
==2077==    by 0x40C9CD7: Tcl_Main (/home/rolf/tcltk/tcl8.5.3/generic/tclMain.c:441)
==2077==    by 0x804C975: main (/home/rolf/tcltk/tcl8.5.3/unix/tclAppInit.c:87)
==2077== 
==2077== LEAK SUMMARY:
==2077==    definitely lost: 24 bytes in 1 blocks.
==2077==    indirectly lost: 194 bytes in 7 blocks.
==2077==      possibly lost: 0 bytes in 0 blocks.
==2077==    still reachable: 0 bytes in 0 blocks.
==2077==         suppressed: 0 bytes in 0 blocks.

pointsman added on 2008-06-29 23:36:20:
Logged In: YES 
user_id=13222
Originator: NO

The fix seems to leak.

With 8.5.3rc0 w/ 2004654 patch the following script shows the leak:

while 1 {
    interp create moo
    moo eval {
        package require tcltest
        namespace import tcltest::*

        proc createfile {file {string a}} {
            set f [open $file w]
            puts -nonewline $f $string
            close $f
            return $string
        }

        proc cleanup {args} {
            set wd [list .]
            foreach p [concat $wd $args] {
                set x ""
                catch {
                    set x [glob -directory $p tf* td*]
                }
                foreach file $x {
                    if {
                        [catch {file delete -force -- $file}]
                        && [testConstraint testchmod]
                    } then {
                        catch {openup $file}
                        catch {file delete -force -- $file}
                    }
                }
            }
        }

        for {set i 0} {$i < 1000} {incr i} {
            test fCmd-10.11 {file copy: copy to empty file name} -setup {
                cleanup
            } -returnCodes error -body {
                createfile tf1
                file copy tf1 ""
            } -result {error copying "tf1" to "": no such file or directory}
        }
    }
    puts moo
    interp delete moo
}

The script leaks slowly enough, to be watched with top without danger.

The tests fCmd-10.11 and fCmd-10.12 out of the test suite trigger the leak. Attached is the relevant part of a valgrind output (-DPURIFY build).

dgp added on 2008-06-25 03:06:47:
Logged In: YES 
user_id=80530
Originator: NO


patch committed for 8.6a1 and 8.5.3.

No backport to 8.4.* since the bug
hasn't been demo'd there.

dgp added on 2008-06-25 03:00:00:

File Added - 282525: 1999716.patch

Logged In: YES 
user_id=80530
Originator: NO


here's a patch to stop the crash
without redesigning the world

File Added: 1999716.patch

dgp added on 2008-06-25 00:34:09:
Logged In: YES 
user_id=80530
Originator: NO


Lines 1773-1774 are more reasonable than
I thought.  When they run, we have already
called Tcl_FSGetNormalizedPath() on
fsPathPtr->cwdPtr, so for any normal value,
that call should have established
cwdPtr!=NULL.  That's only false because
the empty string value as a pathname is
so weird (see 2001389).

dgp added on 2008-06-24 04:30:15:
Logged In: YES 
user_id=80530
Originator: NO


From the comments about 'struct FsPath'
on lines 57-64 of tclPathObj.c:

 * (i) flags == 0, => Ordinary path.
 *
 * translatedPathPtr contains the translated path (which may be a circular
 * reference to the object itself). If it is NULL then the path is pure
 * normalized (and the normPathPtr will be a circular reference). cwdPtr is
 * null for an absolute path, and non-null for a relative path (unless the cwd
 * has never been set, in which case the cwdPtr may also be null for a
 * relative path).

Take care to note that cwdPtr==NULL is possible for a
relative path when the global cwd has never been set.
The code at lines 1773-1174 (in Tcl_FSGetNormalizedPath())
fails to account for this possibility.

dgp added on 2008-06-24 01:20:23:
Logged In: YES 
user_id=80530
Originator: NO


Add the command

cd [pwd]

before the [glob] and the crash
doesn't happen.

Suggests that at least one contibutor
to the error is that Tcl's internal
record of what is the current working
directory is not initialized at a time
when other parts of Tcl's filesystem
code may be assuming that it is initialized.

dgp added on 2008-06-23 21:49:20:
Logged In: YES 
user_id=80530
Originator: NO

% glob -nocomplain -directory {} anything

Program received signal SIGSEGV, Segmentation fault.
0x0807c3db in Tcl_DbIncrRefCount (objPtr=0x0,
    file=0x8172768 "/home/dgp/cvs/tcl/unix/../generic/tclPathObj.c", line=1772)
    at /home/dgp/cvs/tcl/unix/../generic/tclObj.c:3088
3088        if (objPtr->refCount == 0x61616161) {
(gdb) bt
#0  0x0807c3db in Tcl_DbIncrRefCount (objPtr=0x0,
    file=0x8172768 "/home/dgp/cvs/tcl/unix/../generic/tclPathObj.c", line=1772)
    at /home/dgp/cvs/tcl/unix/../generic/tclObj.c:3088
#1  0x080827a8 in Tcl_FSGetNormalizedPath (interp=0x0, pathPtr=0x9d1e850)
    at /home/dgp/cvs/tcl/unix/../generic/tclPathObj.c:1772
#2  0x080ae733 in TclNativeCreateNativeRep (pathPtr=0x9d1e850)
    at /home/dgp/cvs/tcl/unix/../unix/tclUnixFile.c:1104
#3  0x08082d79 in Tcl_FSGetInternalRep (pathPtr=0x9d1e850, fsPtr=0x8188f40)
    at /home/dgp/cvs/tcl/unix/../generic/tclPathObj.c:2069
#4  0x0806bb9b in Tcl_FSGetNativePath (pathPtr=0x9d1e850)
    at /home/dgp/cvs/tcl/unix/../generic/tclIOUtil.c:4290
#5  0x080ad679 in TclpMatchInDirectory (interp=0x9cd18c8, resultPtr=0x9d13200,
    pathPtr=0x9d1e850, pattern=0x0, types=0x0)
    at /home/dgp/cvs/tcl/unix/../unix/tclUnixFile.c:235
#6  0x08068974 in Tcl_FSMatchInDirectory (interp=0x9cd18c8,
    resultPtr=0x9d13200, pathPtr=0x9d1e850, pattern=0x0, types=0x0)
    at /home/dgp/cvs/tcl/unix/../generic/tclIOUtil.c:1056
#7  0x081473d7 in DoGlob (interp=0x9cd18c8, matchesObj=0x9d13200,
    separators=0x8181bfe "/", pathPtr=0x9d06d18, flags=4,
    pattern=0x9d18080 "anything", types=0x0)
    at /home/dgp/cvs/tcl/unix/../generic/tclFileName.c:2457

dgp added on 2008-06-23 21:43:14:
Logged In: YES 
user_id=80530
Originator: NO


crashes on unix too.

Attachments: