diff options
author | sebres <sebres@users.sourceforge.net> | 2024-05-15 12:39:53 (GMT) |
---|---|---|
committer | sebres <sebres@users.sourceforge.net> | 2024-05-15 12:39:53 (GMT) |
commit | b6b6d5e98a0a5c26e2eed280657ee45b2baae4c9 (patch) | |
tree | 8feb387dd89bb61417c184c18f40f69fdaabfa23 | |
parent | 05fad8df8972def0af73c6f528308220660ba67b (diff) | |
download | tcl-b6b6d5e98a0a5c26e2eed280657ee45b2baae4c9.zip tcl-b6b6d5e98a0a5c26e2eed280657ee45b2baae4c9.tar.gz tcl-b6b6d5e98a0a5c26e2eed280657ee45b2baae4c9.tar.bz2 |
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
-rw-r--r-- | generic/tclIO.c | 67 |
1 files 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); } |