From b6b6d5e98a0a5c26e2eed280657ee45b2baae4c9 Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 15 May 2024 12:39:53 +0000 Subject: ultimate fix for [79474c58800cdf94]: avoid segfault on copy-state structure freed to early, protected by refCount and by preserving its r/w channels now; back-ported (squashed rewrite) from tclSE to 8.5 base --- generic/tclIO.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/generic/tclIO.c b/generic/tclIO.c index 807fce1..4260cea 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -107,6 +107,7 @@ typedef struct GetsState { typedef struct CopyState { struct Channel *readPtr; /* Pointer to input channel. */ struct Channel *writePtr; /* Pointer to output channel. */ + int refCount; /* Reference counter. */ int readFlags; /* Original read channel flags. */ int writeFlags; /* Original write channel flags. */ int toRead; /* Number of bytes to copy, or -1. */ @@ -217,6 +218,7 @@ static int StackSetBlockMode(Channel *chanPtr, int mode); static int SetBlockMode(Tcl_Interp *interp, Channel *chanPtr, int mode); static void StopCopy(CopyState *csPtr); +static void CopyDecrRefCount(CopyState *csPtr); static void TranslateInputEOL(ChannelState *statePtr, char *dst, const char *src, int *dstLenPtr, int *srcLenPtr); static void UpdateInterest(Channel *chanPtr); @@ -1973,7 +1975,7 @@ Tcl_UnstackChannel( return TCL_ERROR; } - statePtr->csPtrR = csPtrR; + statePtr->csPtrR = csPtrR; statePtr->csPtrW = csPtrW; } @@ -3483,8 +3485,14 @@ Tcl_ClearChannelHandlers( * Cancel any pending copy operation. */ - StopCopy(statePtr->csPtrR); - StopCopy(statePtr->csPtrW); + if (statePtr->csPtrR) { + StopCopy(statePtr->csPtrR); + statePtr->csPtrR = NULL; + } + if (statePtr->csPtrW) { + StopCopy(statePtr->csPtrW); + statePtr->csPtrW = NULL; + } /* * Must set the interest mask now to 0, otherwise infinite loops @@ -8630,6 +8638,9 @@ TclCopyChannel( CopyState *csPtr; int nonBlocking = (cmdPtr) ? CHANNEL_NONBLOCKING : 0; + TclChannelPreserve(inChan); + TclChannelPreserve(outChan); + inStatePtr = inPtr->state; outStatePtr = outPtr->state; @@ -8689,6 +8700,7 @@ TclCopyChannel( csPtr->bufSize = inStatePtr->bufSize; csPtr->readPtr = inPtr; csPtr->writePtr = outPtr; + csPtr->refCount = 2; /* two references below (inStatePtr, outStatePtr) */ csPtr->readFlags = readFlags; csPtr->writeFlags = writeFlags; csPtr->toRead = toRead; @@ -8699,7 +8711,7 @@ TclCopyChannel( } csPtr->cmdPtr = cmdPtr; - inStatePtr->csPtrR = csPtr; + inStatePtr->csPtrR = csPtr; outStatePtr->csPtrW = csPtr; /* @@ -8709,7 +8721,7 @@ TclCopyChannel( if ((nonBlocking == CHANNEL_NONBLOCKING) && (toRead == 0)) { Tcl_CreateTimerHandler(0, ZeroTransferTimerProc, csPtr); - return 0; + return TCL_OK; } /* @@ -8752,6 +8764,8 @@ CopyData( /* Encoding control */ int underflow; /* Input underflow */ + csPtr->refCount++; /* avoid freeing during handling */ + inChan = (Tcl_Channel) csPtr->readPtr; outChan = (Tcl_Channel) csPtr->writePtr; inStatePtr = csPtr->readPtr->state; @@ -8863,7 +8877,8 @@ CopyData( TclDecrRefCount(bufObj); bufObj = NULL; } - return TCL_OK; + result = TCL_OK; + goto done; } } @@ -8954,7 +8969,8 @@ CopyData( TclDecrRefCount(bufObj); bufObj = NULL; } - return TCL_OK; + result = TCL_OK; + goto done; } /* @@ -8976,7 +8992,8 @@ CopyData( TclDecrRefCount(bufObj); bufObj = NULL; } - return TCL_OK; + result = TCL_OK; + goto done; } } /* while */ @@ -9027,6 +9044,9 @@ CopyData( } } } + +done: + CopyDecrRefCount(csPtr); return result; } @@ -9130,14 +9150,12 @@ DoRead( code = GetInput(chanPtr); bufPtr = statePtr->inQueueHead; - assert (bufPtr != NULL); - if (GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED)) { /* Further reads cannot do any more */ break; } - if (code) { + if (code || !bufPtr) { /* Read error */ UpdateInterest(chanPtr); TclChannelRelease((Tcl_Channel)chanPtr); @@ -9340,9 +9358,32 @@ StopCopy( CopyEventProc, csPtr); } TclDecrRefCount(csPtr->cmdPtr); + csPtr->cmdPtr = NULL; + } + + if (inStatePtr->csPtrR) { + assert(inStatePtr->csPtrR == csPtr); + inStatePtr->csPtrR = NULL; + CopyDecrRefCount(csPtr); + } + if (outStatePtr->csPtrW) { + assert(outStatePtr->csPtrW == csPtr); + outStatePtr->csPtrW = NULL; + CopyDecrRefCount(csPtr); + } +} + +static void +CopyDecrRefCount( + CopyState *csPtr +) { + if (csPtr->refCount-- > 1) { + return; } - inStatePtr->csPtrR = NULL; - outStatePtr->csPtrW = NULL; + + TclChannelRelease((Tcl_Channel)csPtr->readPtr); + TclChannelRelease((Tcl_Channel)csPtr->writePtr); + ckfree((char *) csPtr); } -- cgit v0.12 From bbd85b6ee0f879ae6f0c3bbc4b1d189739efd59f Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 15 May 2024 12:46:39 +0000 Subject: test illustrating bug [79474c58800cdf94] (segfaults at end of iocmd-32.3) --- tests/ioCmd.test | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/tests/ioCmd.test b/tests/ioCmd.test index b8cf52b..2f13904 100644 --- a/tests/ioCmd.test +++ b/tests/ioCmd.test @@ -2117,6 +2117,67 @@ test iocmd-32.2 {delete interp of reflected chan} { interp delete child } {} +test iocmd-32.3 {prevent copy-state against segfault by finalize, bug [79474c58800cdf94]} -setup { + proc test_chan {args} { + set rest [lassign $args mode chan] + lappend ::ret $mode + switch -exact $mode { + read {puts $chan "Test" ; close $chan} + finalize {after 20 {set ::done done}} + initialize {return "initialize watch finalize read write"} + } + } + set clchlst {} +} -body { + set ::ret {} + set ch [chan create "read write" test_chan] + lappend clchlst $ch + + lassign [chan pipe] in1 out1 + lappend clchlst $in1 $out1 + lassign [chan pipe] in2 out2 + lappend clchlst $in2 $out2 + lassign [chan pipe] in3 out3 + lappend clchlst $in3 $out3 + + # simulate exec: echo test >@ $out2 2>@ $out3 <@ $in1 &: + fileevent $out2 writable [list apply {{cho che} {puts $cho test; close $cho; close $che}} $out2 $out3] + # recopy to given chans in handler + fileevent $in2 readable [list apply {{in out} { + if {[catch { + chan copy $in $out + } msg]} { + #puts err:$msg + fileevent $in readable {} + } + }} $in2 $ch] + fileevent $in3 readable [list apply {{in out} { + if {[catch { + chan copy $in $out + } msg]} { + #puts err:$msg + fileevent $in readable {} + } + }} $in3 $ch] + fileevent $out1 writable [list apply {{in out} { + if {[catch { + chan copy $in $out + } msg]} { + #puts err:$msg + fileevent $out writable {} + } + }} $ch $out1] + + after 5000 {set ::done tout} + vwait ::done + list {*}$::ret $::done +} -cleanup { + foreach ch $clchlst { + catch {close $ch} + } + unset -nocomplain ::done ::ret ch in1 in2 in3 out1 out2 out3 +} -result {initialize read write finalize done} + # ### ### ### ######### ######### ######### ## Same tests as above, but exercising the code forwarding and ## receiving driver operations to the originator thread. -- cgit v0.12 From 3144d02e6d0c5999a1f603095254dea140f24964 Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 15 May 2024 15:06:44 +0000 Subject: small code review: removal of unneeded - result is initially TCL_OK --- generic/tclIO.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/generic/tclIO.c b/generic/tclIO.c index 4260cea..31cb48f 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -8877,7 +8877,6 @@ CopyData( TclDecrRefCount(bufObj); bufObj = NULL; } - result = TCL_OK; goto done; } } @@ -8969,7 +8968,6 @@ CopyData( TclDecrRefCount(bufObj); bufObj = NULL; } - result = TCL_OK; goto done; } @@ -8992,7 +8990,6 @@ CopyData( TclDecrRefCount(bufObj); bufObj = NULL; } - result = TCL_OK; goto done; } } /* while */ -- cgit v0.12 From 51dc3f20b034af043d0a14d6d79a60c6431baa0d Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 16 May 2024 08:49:27 +0000 Subject: test: clean timeout timer --- tests/ioCmd.test | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/ioCmd.test b/tests/ioCmd.test index 2f13904..b341aa8 100644 --- a/tests/ioCmd.test +++ b/tests/ioCmd.test @@ -2128,6 +2128,7 @@ test iocmd-32.3 {prevent copy-state against segfault by finalize, bug [79474c588 } } set clchlst {} + set toev {} } -body { set ::ret {} set ch [chan create "read write" test_chan] @@ -2168,14 +2169,15 @@ test iocmd-32.3 {prevent copy-state against segfault by finalize, bug [79474c588 } }} $ch $out1] - after 5000 {set ::done tout} + set toev [after 5000 {set ::done tout}] vwait ::done list {*}$::ret $::done } -cleanup { foreach ch $clchlst { catch {close $ch} } - unset -nocomplain ::done ::ret ch in1 in2 in3 out1 out2 out3 + if {$toev ne ""} { after cancel $toev } + unset -nocomplain ::done ::ret ch in1 in2 in3 out1 out2 out3 toev } -result {initialize read write finalize done} # ### ### ### ######### ######### ######### -- cgit v0.12 From 03cf1c69bbf2cbf952abeef3db7bf2129756e53b Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 16 May 2024 08:56:01 +0000 Subject: code review: preserving r/w channels after all possible failures (don't need to release in error case) --- generic/tclIO.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/generic/tclIO.c b/generic/tclIO.c index 31cb48f..80b646b 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -8638,9 +8638,6 @@ TclCopyChannel( CopyState *csPtr; int nonBlocking = (cmdPtr) ? CHANNEL_NONBLOCKING : 0; - TclChannelPreserve(inChan); - TclChannelPreserve(outChan); - inStatePtr = inPtr->state; outStatePtr = outPtr->state; @@ -8711,6 +8708,9 @@ TclCopyChannel( } csPtr->cmdPtr = cmdPtr; + TclChannelPreserve(inChan); + TclChannelPreserve(outChan); + inStatePtr->csPtrR = csPtr; outStatePtr->csPtrW = csPtr; -- cgit v0.12 From c51319579f93b95668629f8bf2830cb2ba2fba00 Mon Sep 17 00:00:00 2001 From: dkf Date: Thu, 16 May 2024 12:43:52 +0000 Subject: A couple of tiny code style improvements --- generic/tclIO.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/generic/tclIO.c b/generic/tclIO.c index d4c52a7..2df8696 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -9559,7 +9559,7 @@ CopyData( /* Encoding control */ int underflow; /* Input underflow */ - csPtr->refCount++; /* avoid freeing during handling */ + csPtr->refCount++; /* avoid freeing during handling */ inChan = (Tcl_Channel) csPtr->readPtr; outChan = (Tcl_Channel) csPtr->writePtr; @@ -9847,7 +9847,7 @@ CopyData( } } -done: + done: CopyDecrRefCount(csPtr); return result; } @@ -10202,8 +10202,8 @@ StopCopy( static void CopyDecrRefCount( - CopyState *csPtr -) { + CopyState *csPtr) +{ if (csPtr->refCount-- > 1) { return; } -- cgit v0.12 From d882c39f4b96a7a2f39035b4dc27817d70c5fd29 Mon Sep 17 00:00:00 2001 From: sebres Date: Thu, 16 May 2024 20:28:04 +0000 Subject: 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]. --- generic/tclIORChan.c | 41 ++++++++++++++++++++++------------------- 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} # ### ### ### ######### ######### ######### -- cgit v0.12 From 3d0973aa69124e07b27724807538e4bf504d299a Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 17 May 2024 09:51:41 +0000 Subject: fixes yet another segfault (if write handler doesn't generate an error, but returns normally with length of written bytes) --- generic/tclIO.c | 17 +++++++++-------- tests/ioCmd.test | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/generic/tclIO.c b/generic/tclIO.c index 2df8696..c3844af 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -9964,19 +9964,20 @@ DoRead( } if (code || !bufPtr) { - /* - * Read error - */ - - UpdateInterest(chanPtr); - TclChannelRelease((Tcl_Channel)chanPtr); - return -1; + /* Read error (or channel dead/closed) */ + goto readErr; } assert(IsBufferFull(bufPtr)); } - assert(bufPtr != NULL); + if (!bufPtr) { + readErr: + + UpdateInterest(chanPtr); + TclChannelRelease((Tcl_Channel)chanPtr); + return -1; + } bytesRead = BytesLeft(bufPtr); bytesWritten = bytesToRead; diff --git a/tests/ioCmd.test b/tests/ioCmd.test index 3dbb3cc..e56260b 100644 --- a/tests/ioCmd.test +++ b/tests/ioCmd.test @@ -2123,6 +2123,7 @@ test iocmd-32.3 {prevent copy-state against segfault by finalize, bug [79474c588 lappend ::ret $mode switch -exact $mode { read {puts $chan "Test" ; close $chan} + write {set data [lindex $rest 0]; string length $data} finalize {after 20 {set ::done done}} initialize {return "initialize watch finalize read write"} } -- cgit v0.12 From 4fb3e71bda90cd40070e96dc5cbf200ed957cc41 Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 17 May 2024 10:14:01 +0000 Subject: extend test to cover both variants (without and with error in reflected write handler) --- tests/ioCmd.test | 95 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/tests/ioCmd.test b/tests/ioCmd.test index e56260b..a2061fa 100644 --- a/tests/ioCmd.test +++ b/tests/ioCmd.test @@ -2118,68 +2118,73 @@ test iocmd-32.2 {delete interp of reflected chan} { } {} test iocmd-32.3 {prevent copy-state against segfault by finalize, bug [79474c58800cdf94]} -setup { - proc test_chan {args} { + proc test_chan {writeErr args} { set rest [lassign $args mode chan] lappend ::ret $mode switch -exact $mode { read {puts $chan "Test" ; close $chan} - write {set data [lindex $rest 0]; string length $data} + write {if {$writeErr} {return}; set data [lindex $rest 0]; string length $data} finalize {after 20 {set ::done done}} initialize {return "initialize watch finalize read write"} } } set clchlst {} - set toev {} + set toev [after 5000 {set ::done tout}] } -body { set ::ret {} - set ch [chan create "read write" test_chan] - lappend clchlst $ch - - lassign [chan pipe] in1 out1 - lappend clchlst $in1 $out1 - lassign [chan pipe] in2 out2 - lappend clchlst $in2 $out2 - lassign [chan pipe] in3 out3 - lappend clchlst $in3 $out3 - - # simulate exec: echo test >@ $out2 2>@ $out3 <@ $in1 &: - fileevent $out2 writable [list apply {{cho che} {puts $cho test; close $cho; close $che}} $out2 $out3] - # recopy to given chans in handler - fileevent $in2 readable [list apply {{in out} { - if {[catch { - chan copy $in $out - } msg]} { - #puts err:$msg - fileevent $in readable {} - } - }} $in2 $ch] - fileevent $in3 readable [list apply {{in out} { - if {[catch { - chan copy $in $out - } msg]} { - #puts err:$msg - fileevent $in readable {} - } - }} $in3 $ch] - fileevent $out1 writable [list apply {{in out} { - if {[catch { - chan copy $in $out - } msg]} { - #puts err:$msg - fileevent $out writable {} - } - }} $ch $out1] + # 1st attempt without error in write, another with error in write: + foreach i {0 1} { + set ch [chan create "read write" [list test_chan $i]] + lappend clchlst $ch + + lassign [chan pipe] in1 out1 + lappend clchlst $in1 $out1 + lassign [chan pipe] in2 out2 + lappend clchlst $in2 $out2 + lassign [chan pipe] in3 out3 + lappend clchlst $in3 $out3 + + # simulate exec: echo test >@ $out2 2>@ $out3 <@ $in1 &: + fileevent $out2 writable [list apply {{cho che} { + puts $cho test; close $cho; close $che + }} $out2 $out3] + # recopy to given chans in handler + fileevent $in2 readable [list apply {{in out} { + if {[catch { + chan copy $in $out + } msg]} { + #puts err:$msg + fileevent $in readable {} + } + }} $in2 $ch] + fileevent $in3 readable [list apply {{in out} { + if {[catch { + chan copy $in $out + } msg]} { + #puts err:$msg + fileevent $in readable {} + } + }} $in3 $ch] + fileevent $out1 writable [list apply {{in out} { + if {[catch { + chan copy $in $out + } msg]} { + #puts err:$msg + fileevent $out writable {} + } + }} $ch $out1] - set toev [after 5000 {set ::done tout}] - vwait ::done - list {*}$::ret $::done + vwait ::done + lappend ::ret $::done + } + set ::ret } -cleanup { foreach ch $clchlst { catch {close $ch} } - if {$toev ne ""} { after cancel $toev } + after cancel $toev unset -nocomplain ::done ::ret ch in1 in2 in3 out1 out2 out3 toev clchlst -} -result {initialize read write finalize done} +} -result [lrepeat 2 initialize read write finalize done] # ### ### ### ######### ######### ######### ## Same tests as above, but exercising the code forwarding and -- cgit v0.12 From 0c8986dc65b0034df10a77db626ef663285574a3 Mon Sep 17 00:00:00 2001 From: apnadkarni Date: Fri, 17 May 2024 11:48:53 +0000 Subject: Free ChannelState.chanMsg .unreportedMsg --- generic/tclIO.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/generic/tclIO.c b/generic/tclIO.c index 2df8696..adcf513 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -157,7 +157,8 @@ typedef struct CloseCallback { static ChannelBuffer * AllocChannelBuffer(int length); static void PreserveChannelBuffer(ChannelBuffer *bufPtr); static void ReleaseChannelBuffer(ChannelBuffer *bufPtr); -static int IsShared(ChannelBuffer *bufPtr); +static void FreeChannelState(char *blockPtr); +static int IsShared(ChannelBuffer *bufPtr); static void ChannelFree(Channel *chanPtr); static void ChannelTimerProc(void *clientData); static int ChanRead(Channel *chanPtr, char *dst, int dstSize); @@ -2949,6 +2950,23 @@ FlushChannel( return errorCode; } +static void FreeChannelState(char *blockPtr) +{ + ChannelState *statePtr = (ChannelState *)blockPtr; + /* + * Bug [79474c588] leak. Possible other fields need freeing but + * not clear if they are already freed and if the fields are set to NULL + * when they are. Test suite shows no other leaks at the moment. + */ + if (statePtr->chanMsg) { + Tcl_DecrRefCount(statePtr->chanMsg); + } + if (statePtr->unreportedMsg) { + Tcl_DecrRefCount(statePtr->unreportedMsg); + } + ckfree(statePtr); +} + /* *---------------------------------------------------------------------- * @@ -3125,7 +3143,7 @@ CloseChannel( ChannelFree(chanPtr); - Tcl_EventuallyFree(statePtr, TCL_DYNAMIC); + Tcl_EventuallyFree(statePtr, FreeChannelState); return errorCode; } -- cgit v0.12 From 15fd9ef834884fbc1b67a491dead6fa004a3fd45 Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 17 May 2024 13:21:47 +0000 Subject: split iocmd-32.3 in two tests (move cycle outside of the test) --- tests/ioCmd.test | 97 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/tests/ioCmd.test b/tests/ioCmd.test index a2061fa..74fabe7 100644 --- a/tests/ioCmd.test +++ b/tests/ioCmd.test @@ -2117,13 +2117,15 @@ test iocmd-32.2 {delete interp of reflected chan} { interp delete child } {} -test iocmd-32.3 {prevent copy-state against segfault by finalize, bug [79474c58800cdf94]} -setup { - proc test_chan {writeErr args} { +# 1st attempt without error in write, another with error in write: +foreach ::writeErr {0 1} { +test iocmd-32.3.$::writeErr {prevent copy-state against segfault by finalize, bug [79474c58800cdf94]} -setup { + proc test_chan {args} { set rest [lassign $args mode chan] lappend ::ret $mode switch -exact $mode { read {puts $chan "Test" ; close $chan} - write {if {$writeErr} {return}; set data [lindex $rest 0]; string length $data} + write {if {$::writeErr} {return "boom"}; set data [lindex $rest 0]; string length $data} finalize {after 20 {set ::done done}} initialize {return "initialize watch finalize read write"} } @@ -2132,59 +2134,56 @@ test iocmd-32.3 {prevent copy-state against segfault by finalize, bug [79474c588 set toev [after 5000 {set ::done tout}] } -body { set ::ret {} - # 1st attempt without error in write, another with error in write: - foreach i {0 1} { - set ch [chan create "read write" [list test_chan $i]] - lappend clchlst $ch - - lassign [chan pipe] in1 out1 - lappend clchlst $in1 $out1 - lassign [chan pipe] in2 out2 - lappend clchlst $in2 $out2 - lassign [chan pipe] in3 out3 - lappend clchlst $in3 $out3 - - # simulate exec: echo test >@ $out2 2>@ $out3 <@ $in1 &: - fileevent $out2 writable [list apply {{cho che} { - puts $cho test; close $cho; close $che - }} $out2 $out3] - # recopy to given chans in handler - fileevent $in2 readable [list apply {{in out} { - if {[catch { - chan copy $in $out - } msg]} { - #puts err:$msg - fileevent $in readable {} - } - }} $in2 $ch] - fileevent $in3 readable [list apply {{in out} { - if {[catch { - chan copy $in $out - } msg]} { - #puts err:$msg - fileevent $in readable {} - } - }} $in3 $ch] - fileevent $out1 writable [list apply {{in out} { - if {[catch { - chan copy $in $out - } msg]} { - #puts err:$msg - fileevent $out writable {} - } - }} $ch $out1] + set ch [chan create "read write" test_chan] + lappend clchlst $ch + + lassign [chan pipe] in1 out1 + lappend clchlst $in1 $out1 + lassign [chan pipe] in2 out2 + lappend clchlst $in2 $out2 + lassign [chan pipe] in3 out3 + lappend clchlst $in3 $out3 + + # simulate exec: echo test >@ $out2 2>@ $out3 <@ $in1 &: + fileevent $out2 writable [list apply {{cho che} { + puts $cho test; close $cho; close $che + }} $out2 $out3] + # recopy to given chans in handler + fileevent $in2 readable [list apply {{in out} { + if {[catch { + chan copy $in $out + } msg]} { + #puts err:$msg + fileevent $in readable {} + } + }} $in2 $ch] + fileevent $in3 readable [list apply {{in out} { + if {[catch { + chan copy $in $out + } msg]} { + #puts err:$msg + fileevent $in readable {} + } + }} $in3 $ch] + fileevent $out1 writable [list apply {{in out} { + if {[catch { + chan copy $in $out + } msg]} { + #puts err:$msg + fileevent $out writable {} + } + }} $ch $out1] - vwait ::done - lappend ::ret $::done - } - set ::ret + vwait ::done + lappend ::ret $::done } -cleanup { foreach ch $clchlst { catch {close $ch} } after cancel $toev unset -nocomplain ::done ::ret ch in1 in2 in3 out1 out2 out3 toev clchlst -} -result [lrepeat 2 initialize read write finalize done] +} -result {initialize read write finalize done} +}; unset ::writeErr # ### ### ### ######### ######### ######### ## Same tests as above, but exercising the code forwarding and -- cgit v0.12