diff options
| author | sebres <sebres@users.sourceforge.net> | 2024-05-16 20:28:04 (GMT) |
|---|---|---|
| committer | sebres <sebres@users.sourceforge.net> | 2024-05-16 20:28:04 (GMT) |
| commit | a7ec3ac2ad4ef2890f4229c9d9b3c079e94d188b (patch) | |
| tree | 758ef95e5f6585f4cb5143a745f4751e8b515152 | |
| parent | dfa67061deda621491e1623f06e61b93da516195 (diff) | |
| download | tcl-a7ec3ac2ad4ef2890f4229c9d9b3c079e94d188b.zip tcl-a7ec3ac2ad4ef2890f4229c9d9b3c079e94d188b.tar.gz tcl-a7ec3ac2ad4ef2890f4229c9d9b3c079e94d188b.tar.bz2 | |
fix mem-leak originating by cyclic reference `rcPtr->name (type "channel", its refCount may be larger than 1) => statPtr => chanPtr => chanPtr->instanceData => refChan`:
this would avoid that object rcPtr->name (name of channel that gets deleted or dead) still holds the reference to statPtr, see 2nd part of bug [79474c58800cdf94].
| -rw-r--r-- | generic/tclIORChan.c | 41 | ||||
| -rw-r--r-- | tests/ioCmd.test | 2 |
2 files changed, 23 insertions, 20 deletions
diff --git a/generic/tclIORChan.c b/generic/tclIORChan.c index 727239b..f2bb186 100644 --- a/generic/tclIORChan.c +++ b/generic/tclIORChan.c @@ -2211,23 +2211,37 @@ NextHandle(void) return resObj; } -static void -FreeReflectedChannel( - char *blockPtr) +static inline void +CleanRefChannelInstance( + ReflectedChannel *rcPtr) { - ReflectedChannel *rcPtr = (ReflectedChannel *) blockPtr; - Channel *chanPtr = (Channel *) rcPtr->chan; - - TclChannelRelease((Tcl_Channel)chanPtr); if (rcPtr->name) { + /* + * Reset obj-type (channel is deleted or dead anyway) to avoid leakage + * by cyclic references (see bug [79474c58800cdf94]). + */ + TclFreeIntRep(rcPtr->name); Tcl_DecrRefCount(rcPtr->name); + rcPtr->name = NULL; } if (rcPtr->methods) { Tcl_DecrRefCount(rcPtr->methods); + rcPtr->methods = NULL; } if (rcPtr->cmd) { Tcl_DecrRefCount(rcPtr->cmd); + rcPtr->cmd = NULL; } +} +static void +FreeReflectedChannel( + char *blockPtr) +{ + ReflectedChannel *rcPtr = (ReflectedChannel *) blockPtr; + Channel *chanPtr = (Channel *) rcPtr->chan; + + TclChannelRelease((Tcl_Channel)chanPtr); + CleanRefChannelInstance(rcPtr); ckfree(rcPtr); } @@ -2497,18 +2511,7 @@ MarkDead( if (rcPtr->dead) { return; } - if (rcPtr->name) { - Tcl_DecrRefCount(rcPtr->name); - rcPtr->name = NULL; - } - if (rcPtr->methods) { - Tcl_DecrRefCount(rcPtr->methods); - rcPtr->methods = NULL; - } - if (rcPtr->cmd) { - Tcl_DecrRefCount(rcPtr->cmd); - rcPtr->cmd = NULL; - } + CleanRefChannelInstance(rcPtr); rcPtr->dead = 1; } diff --git a/tests/ioCmd.test b/tests/ioCmd.test index b341aa8..3dbb3cc 100644 --- a/tests/ioCmd.test +++ b/tests/ioCmd.test @@ -2177,7 +2177,7 @@ test iocmd-32.3 {prevent copy-state against segfault by finalize, bug [79474c588 catch {close $ch} } if {$toev ne ""} { after cancel $toev } - unset -nocomplain ::done ::ret ch in1 in2 in3 out1 out2 out3 toev + unset -nocomplain ::done ::ret ch in1 in2 in3 out1 out2 out3 toev clchlst } -result {initialize read write finalize done} # ### ### ### ######### ######### ######### |
