Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | merge 8.6 |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | core-8-branch |
Files: | files | file ages | folders |
SHA3-256: |
5be1c6c97991798f4fed707d3fd0f749 |
User & Date: | sebres 2024-05-21 09:48:41 |
Context
2024-05-21
| ||
10:49 | circumvent crash [7842f33a5cc5eed0] on null ptr (oPtr->selfCls may be NULL) check-in: 1d16344c8c user: sebres tags: core-8-branch | |
10:33 | merge 8.7 check-in: bc2e652394 user: sebres tags: trunk, main | |
09:48 | merge 8.6 check-in: 5be1c6c979 user: sebres tags: core-8-branch | |
09:45 | closes [79474c58800cdf94]: fixes 2 segfaults and 2 leaks (common IO handlers and reflected channels) check-in: 4342b27a4b user: sebres tags: core-8-6-branch | |
2024-05-20
| ||
20:33 | Fix gcc warning: return should have value check-in: e3cb927560 user: jan.nijtmans tags: core-8-branch | |
Changes
Changes to generic/tclIO.c.
︙ | ︙ | |||
93 94 95 96 97 98 99 100 101 102 103 104 105 106 | * copy. Note that the data buffer for the copy will be appended to this * structure. */ typedef struct CopyState { struct Channel *readPtr; /* Pointer to input channel. */ struct Channel *writePtr; /* Pointer to output channel. */ int readFlags; /* Original read channel flags. */ int writeFlags; /* Original write channel flags. */ Tcl_WideInt toRead; /* Number of bytes to copy, or -1. */ Tcl_WideInt total; /* Total bytes transferred (written). */ Tcl_Interp *interp; /* Interp that started the copy. */ Tcl_Obj *cmdPtr; /* Command to be invoked at completion. */ Tcl_Size bufSize; /* Size of appended buffer. */ | > | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | * copy. Note that the data buffer for the copy will be appended to this * structure. */ 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. */ Tcl_WideInt toRead; /* Number of bytes to copy, or -1. */ Tcl_WideInt total; /* Total bytes transferred (written). */ Tcl_Interp *interp; /* Interp that started the copy. */ Tcl_Obj *cmdPtr; /* Command to be invoked at completion. */ Tcl_Size bufSize; /* Size of appended buffer. */ |
︙ | ︙ | |||
216 217 218 219 220 221 222 223 224 225 226 227 228 229 | int charsLeft, int *factorPtr); static void RecycleBuffer(ChannelState *statePtr, ChannelBuffer *bufPtr, int mustDiscard); static int StackSetBlockMode(Channel *chanPtr, int mode); static int SetBlockMode(Tcl_Interp *interp, Channel *chanPtr, int mode); static void StopCopy(CopyState *csPtr); static void TranslateInputEOL(ChannelState *statePtr, char *dst, const char *src, int *dstLenPtr, int *srcLenPtr); static void UpdateInterest(Channel *chanPtr); static Tcl_Size Write(Channel *chanPtr, const char *src, Tcl_Size srcLen, Tcl_Encoding encoding); static Tcl_Obj * FixLevelCode(Tcl_Obj *msg); static void SpliceChannel(Tcl_Channel chan); | > | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | int charsLeft, int *factorPtr); static void RecycleBuffer(ChannelState *statePtr, ChannelBuffer *bufPtr, int mustDiscard); 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); static Tcl_Size Write(Channel *chanPtr, const char *src, Tcl_Size srcLen, Tcl_Encoding encoding); static Tcl_Obj * FixLevelCode(Tcl_Obj *msg); static void SpliceChannel(Tcl_Channel chan); |
︙ | ︙ | |||
2113 2114 2115 2116 2117 2118 2119 | Tcl_SetObjResult(interp, Tcl_ObjPrintf( "could not flush channel \"%s\"", Tcl_GetChannelName((Tcl_Channel) chanPtr))); } return TCL_ERROR; } | | | 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 | Tcl_SetObjResult(interp, Tcl_ObjPrintf( "could not flush channel \"%s\"", Tcl_GetChannelName((Tcl_Channel) chanPtr))); } return TCL_ERROR; } statePtr->csPtrR = csPtrR; statePtr->csPtrW = csPtrW; } /* * Anything in the input queue and the push-back buffers of the * transformation going away is transformed data, but not yet read. As * unstacking means that the caller does not want to see transformed |
︙ | ︙ | |||
3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 | goto done; } done: TclChannelRelease((Tcl_Channel)chanPtr); return errorCode; } /* *---------------------------------------------------------------------- * * CloseChannel -- * * Utility procedure to close a channel and free associated resources. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 | goto done; } done: TclChannelRelease((Tcl_Channel)chanPtr); return errorCode; } static void FreeChannelState( char *blockPtr) /* Channel state to free. */ { ChannelState *statePtr = (ChannelState *)blockPtr; /* * Even after close some members can be filled again (in events etc). * Test in bug [79474c588] illustrates one leak (on remaining chanMsg). * Possible other fields need freeing on some constellations. */ DiscardInputQueued(statePtr, 1); if (statePtr->curOutPtr != NULL) { ReleaseChannelBuffer(statePtr->curOutPtr); } DiscardOutputQueued(statePtr); DeleteTimerHandler(statePtr); if (statePtr->chanMsg) { Tcl_DecrRefCount(statePtr->chanMsg); } if (statePtr->unreportedMsg) { Tcl_DecrRefCount(statePtr->unreportedMsg); } ckfree(statePtr); } /* *---------------------------------------------------------------------- * * CloseChannel -- * * Utility procedure to close a channel and free associated resources. |
︙ | ︙ | |||
3219 3220 3221 3222 3223 3224 3225 | * There is only the TOP Channel, so we free the remaining pointers we * have and then ourselves. Since this is the last of the channels in the * stack, make sure to free the ChannelState structure associated with it. */ ChannelFree(chanPtr); | | | 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 | * There is only the TOP Channel, so we free the remaining pointers we * have and then ourselves. Since this is the last of the channels in the * stack, make sure to free the ChannelState structure associated with it. */ ChannelFree(chanPtr); Tcl_EventuallyFree(statePtr, FreeChannelState); return errorCode; } /* *---------------------------------------------------------------------- * |
︙ | ︙ | |||
4048 4049 4050 4051 4052 4053 4054 | } statePtr->chPtr = NULL; /* * Cancel any pending copy operation. */ | > | > > > | > > | 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 | } statePtr->chPtr = NULL; /* * Cancel any pending copy operation. */ 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 will * occur if Tcl_DoOneEvent is called before the channel is finally deleted * in FlushChannel. This can happen if the channel has a background flush * active. */ |
︙ | ︙ | |||
9542 9543 9544 9545 9546 9547 9548 9549 9550 9551 9552 9553 9554 9555 9556 9557 9558 | * completed. */ csPtr = (CopyState *)ckalloc(offsetof(CopyState, buffer) + 1U + !moveBytes * inStatePtr->bufSize); csPtr->bufSize = !moveBytes * inStatePtr->bufSize; csPtr->readPtr = inPtr; csPtr->writePtr = outPtr; csPtr->readFlags = readFlags; csPtr->writeFlags = writeFlags; csPtr->toRead = toRead; csPtr->total = (Tcl_WideInt) 0; csPtr->interp = interp; if (cmdPtr) { Tcl_IncrRefCount(cmdPtr); } csPtr->cmdPtr = cmdPtr; | > > > > | | | 9578 9579 9580 9581 9582 9583 9584 9585 9586 9587 9588 9589 9590 9591 9592 9593 9594 9595 9596 9597 9598 9599 9600 9601 9602 9603 9604 9605 9606 9607 9608 9609 9610 9611 9612 9613 9614 9615 9616 9617 9618 9619 9620 | * completed. */ csPtr = (CopyState *)ckalloc(offsetof(CopyState, buffer) + 1U + !moveBytes * inStatePtr->bufSize); csPtr->bufSize = !moveBytes * 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; csPtr->total = (Tcl_WideInt) 0; csPtr->interp = interp; if (cmdPtr) { Tcl_IncrRefCount(cmdPtr); } csPtr->cmdPtr = cmdPtr; TclChannelPreserve(inChan); TclChannelPreserve(outChan); inStatePtr->csPtrR = csPtr; outStatePtr->csPtrW = csPtr; if (moveBytes) { return MoveBytes(csPtr); } /* * Special handling of -size 0 async transfers, so that the -command is * still called asynchronously. */ if ((nonBlocking == CHANNEL_NONBLOCKING) && (toRead == 0)) { Tcl_CreateTimerHandler(0, ZeroTransferTimerProc, csPtr); return TCL_OK; } /* * Start copying data between the channels. */ return CopyData(csPtr, 0); |
︙ | ︙ | |||
9842 9843 9844 9845 9846 9847 9848 9849 9850 9851 9852 9853 9854 9855 | Tcl_WideInt total; int size; const char *buffer; int inBinary, outBinary, sameEncoding; /* Encoding control */ int underflow; /* Input underflow */ inChan = (Tcl_Channel) csPtr->readPtr; outChan = (Tcl_Channel) csPtr->writePtr; inStatePtr = csPtr->readPtr->state; outStatePtr = csPtr->writePtr->state; interp = csPtr->interp; cmdPtr = csPtr->cmdPtr; | > > | 9882 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 | Tcl_WideInt total; int size; const char *buffer; int inBinary, outBinary, sameEncoding; /* 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; outStatePtr = csPtr->writePtr->state; interp = csPtr->interp; cmdPtr = csPtr->cmdPtr; |
︙ | ︙ | |||
9990 9991 9992 9993 9994 9995 9996 | continue; } if (bufObj != NULL) { TclDecrRefCount(bufObj); bufObj = NULL; } | | | 10032 10033 10034 10035 10036 10037 10038 10039 10040 10041 10042 10043 10044 10045 10046 | continue; } if (bufObj != NULL) { TclDecrRefCount(bufObj); bufObj = NULL; } goto done; } } /* * Now write the buffer out. */ |
︙ | ︙ | |||
10081 10082 10083 10084 10085 10086 10087 | Tcl_CreateChannelHandler(outChan, TCL_WRITABLE, CopyEventProc, csPtr); } if (bufObj != NULL) { TclDecrRefCount(bufObj); bufObj = NULL; } | | | 10123 10124 10125 10126 10127 10128 10129 10130 10131 10132 10133 10134 10135 10136 10137 | Tcl_CreateChannelHandler(outChan, TCL_WRITABLE, CopyEventProc, csPtr); } if (bufObj != NULL) { TclDecrRefCount(bufObj); bufObj = NULL; } goto done; } /* * For background copies, we only do one buffer per invocation so we * don't starve the rest of the system. */ |
︙ | ︙ | |||
10103 10104 10105 10106 10107 10108 10109 | Tcl_CreateChannelHandler(outChan, TCL_WRITABLE, CopyEventProc, csPtr); } if (bufObj != NULL) { TclDecrRefCount(bufObj); bufObj = NULL; } | | | 10145 10146 10147 10148 10149 10150 10151 10152 10153 10154 10155 10156 10157 10158 10159 | Tcl_CreateChannelHandler(outChan, TCL_WRITABLE, CopyEventProc, csPtr); } if (bufObj != NULL) { TclDecrRefCount(bufObj); bufObj = NULL; } goto done; } } /* while */ if (bufObj != NULL) { TclDecrRefCount(bufObj); bufObj = NULL; } |
︙ | ︙ | |||
10155 10156 10157 10158 10159 10160 10161 10162 10163 10164 10165 10166 10167 10168 | result = TCL_ERROR; } else { Tcl_ResetResult(interp); Tcl_SetObjResult(interp, Tcl_NewWideIntObj(total)); } } } return result; } /* *---------------------------------------------------------------------- * * DoRead -- | > > > | 10197 10198 10199 10200 10201 10202 10203 10204 10205 10206 10207 10208 10209 10210 10211 10212 10213 | result = TCL_ERROR; } else { Tcl_ResetResult(interp); Tcl_SetObjResult(interp, Tcl_NewWideIntObj(total)); } } } done: CopyDecrRefCount(csPtr); return result; } /* *---------------------------------------------------------------------- * * DoRead -- |
︙ | ︙ | |||
10266 10267 10268 10269 10270 10271 10272 | * to fill the dst */ int code; moreData: code = GetInput(chanPtr); bufPtr = statePtr->inQueueHead; | < < | < | < | < < < | > > > > > > | 10311 10312 10313 10314 10315 10316 10317 10318 10319 10320 10321 10322 10323 10324 10325 10326 10327 10328 10329 10330 10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341 10342 10343 10344 10345 10346 10347 | * to fill the dst */ int code; moreData: code = GetInput(chanPtr); bufPtr = statePtr->inQueueHead; if (GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED)) { /* * Further reads cannot do any more. */ break; } if (code || !bufPtr) { /* Read error (or channel dead/closed) */ goto readErr; } assert(IsBufferFull(bufPtr)); } if (!bufPtr) { readErr: UpdateInterest(chanPtr); TclChannelRelease((Tcl_Channel)chanPtr); return -1; } bytesRead = BytesLeft(bufPtr); bytesWritten = bytesToRead; TranslateInputEOL(statePtr, p, RemovePoint(bufPtr), &bytesWritten, &bytesRead); bufPtr->nextRemoved += bytesRead; |
︙ | ︙ | |||
10498 10499 10500 10501 10502 10503 10504 10505 | Tcl_DeleteChannelHandler(inChan, CopyEventProc, csPtr); if (inChan != outChan) { Tcl_DeleteChannelHandler(outChan, CopyEventProc, csPtr); } Tcl_DeleteChannelHandler(inChan, MBEvent, csPtr); Tcl_DeleteChannelHandler(outChan, MBEvent, csPtr); TclDecrRefCount(csPtr->cmdPtr); } | > > > > | > > > > | > > > > > > > > > > > > > > > | 10542 10543 10544 10545 10546 10547 10548 10549 10550 10551 10552 10553 10554 10555 10556 10557 10558 10559 10560 10561 10562 10563 10564 10565 10566 10567 10568 10569 10570 10571 10572 10573 10574 10575 10576 10577 10578 10579 10580 10581 | Tcl_DeleteChannelHandler(inChan, CopyEventProc, csPtr); if (inChan != outChan) { Tcl_DeleteChannelHandler(outChan, CopyEventProc, csPtr); } Tcl_DeleteChannelHandler(inChan, MBEvent, csPtr); Tcl_DeleteChannelHandler(outChan, MBEvent, 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; } TclChannelRelease((Tcl_Channel)csPtr->readPtr); TclChannelRelease((Tcl_Channel)csPtr->writePtr); ckfree(csPtr); } /* *---------------------------------------------------------------------- * * StackSetBlockMode -- |
︙ | ︙ |
Changes to generic/tclIORChan.c.
︙ | ︙ | |||
2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 | resObj = Tcl_ObjPrintf("rc%lu", rcCounter); rcCounter++; Tcl_MutexUnlock(&rcCounterMutex); return resObj; } static void FreeReflectedChannel( char *blockPtr) { ReflectedChannel *rcPtr = (ReflectedChannel *) blockPtr; Channel *chanPtr = (Channel *) rcPtr->chan; TclChannelRelease((Tcl_Channel)chanPtr); | > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < | 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 | resObj = Tcl_ObjPrintf("rc%lu", rcCounter); rcCounter++; Tcl_MutexUnlock(&rcCounterMutex); return resObj; } static inline void CleanRefChannelInstance( ReflectedChannel *rcPtr) { 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); } /* *---------------------------------------------------------------------- * * InvokeTclMethod -- |
︙ | ︙ | |||
2625 2626 2627 2628 2629 2630 2631 | static void MarkDead( ReflectedChannel *rcPtr) { if (rcPtr->dead) { return; } | | < < < < < < < < < < < | 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 | static void MarkDead( ReflectedChannel *rcPtr) { if (rcPtr->dead) { return; } CleanRefChannelInstance(rcPtr); rcPtr->dead = 1; } static void DeleteReflectedChannelMap( void *clientData, /* The per-interpreter data structure. */ Tcl_Interp *interp) /* The interpreter being deleted. */ |
︙ | ︙ |
Changes to tests/ioCmd.test.
︙ | ︙ | |||
2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 | child eval { proc no-op args {} proc driver {sub args} {return {initialize finalize watch read}} chan event [chan create read driver] readable no-op } interp delete child } {} # ### ### ### ######### ######### ######### ## Same tests as above, but exercising the code forwarding and ## receiving driver operations to the originator thread. # -*- tcl -*- # ### ### ### ######### ######### ######### | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 | child eval { proc no-op args {} proc driver {sub args} {return {initialize finalize watch read}} chan event [chan create read driver] readable no-op } interp delete child } {} # 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 "boom"}; 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 [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] 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 {initialize read write finalize done} }; unset ::writeErr # ### ### ### ######### ######### ######### ## Same tests as above, but exercising the code forwarding and ## receiving driver operations to the originator thread. # -*- tcl -*- # ### ### ### ######### ######### ######### |
︙ | ︙ |