summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
authorsebres <sebres@users.sourceforge.net>2024-05-15 12:39:53 (GMT)
committersebres <sebres@users.sourceforge.net>2024-05-15 12:39:53 (GMT)
commitb6b6d5e98a0a5c26e2eed280657ee45b2baae4c9 (patch)
tree8feb387dd89bb61417c184c18f40f69fdaabfa23 /generic
parent05fad8df8972def0af73c6f528308220660ba67b (diff)
downloadtcl-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
Diffstat (limited to 'generic')
-rw-r--r--generic/tclIO.c67
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);
}