? LOG
Index: ChangeLog
===================================================================
RCS file: /cvsroot/tcl/tcl/ChangeLog,v
retrieving revision 1.4916
diff -w -u -r1.4916 ChangeLog
--- ChangeLog	18 Jan 2010 22:19:11 -0000	1.4916
+++ ChangeLog	19 Jan 2010 19:19:05 -0000
@@ -1,5 +1,16 @@
 2010-01-18  Andreas Kupries  <andreask@activestate.com>
 
+	* generic/tclIORTrans.c (ReflectInput, et al): [Bug 2921116]:
+	Added missing TclEventuallyFree calls for preserved
+	ReflectedTransform* structures. Reworked ReflectInput to preserve
+	the structure for its whole life, not only in InvokeTclMethod.
+	* generic/tclIO.c (Tcl_GetsObj): [Bug 2921116]: Regenerate
+	topChan, may have been changed by a self-modifying transformation.
+
+	Note: This is incomplete, all channel driver functions should
+	preserve the structure or we can crash them in an equivalent
+	manner.
+
 	* generic/tclIO.c (CreateScriptRecord): [Bug 2918110]: Initialize
 	the EventScriptRecord (esPtr) fully before handing it to
 	Tcl_CreateChannelHandler for registration. Otherwise a reflected
Index: generic/tclIO.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclIO.c,v
retrieving revision 1.171
diff -w -u -r1.171 tclIO.c
--- generic/tclIO.c	18 Jan 2010 22:19:11 -0000	1.171
+++ generic/tclIO.c	19 Jan 2010 19:19:12 -0000
@@ -4765,6 +4765,13 @@
      */
 
   done:
+    /*
+     * Regenerate the top channel, in case it was changed due to
+     * self-modifying reflected transforms.
+     */
+
+    chanPtr = statePtr->topChanPtr;
+
     UpdateInterest(chanPtr);
     return copiedTotal;
 }
Index: generic/tclIORTrans.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclIORTrans.c,v
retrieving revision 1.11
diff -w -u -r1.11 tclIORTrans.c
--- generic/tclIORTrans.c	10 Jan 2010 22:58:40 -0000	1.11
+++ generic/tclIORTrans.c	19 Jan 2010 19:19:13 -0000
@@ -719,7 +719,7 @@
      * structure.
      */
 
-    FreeReflectedTransform(rtPtr);
+    Tcl_EventuallyFree (rtPtr, FreeReflectedTransform);
     return TCL_ERROR;
 
 #undef CHAN
@@ -929,7 +929,7 @@
 	}
 #endif
 
-	FreeReflectedTransform(rtPtr);
+	Tcl_EventuallyFree (rtPtr, FreeReflectedTransform);
 	return EOK;
     }
 
@@ -1028,7 +1028,7 @@
     }
 #endif
 
-    FreeReflectedTransform(rtPtr);
+    Tcl_EventuallyFree (rtPtr, FreeReflectedTransform);
     return (result == TCL_OK) ? EOK : EINVAL;
 }
 
@@ -1070,8 +1070,9 @@
 	return -1;
     }
 
-    gotBytes = 0;
+    Tcl_Preserve(rtPtr);
 
+    gotBytes = 0;
     while (toRead > 0) {
 	/*
 	 * Loop until the request is satisfied (or no data available from
@@ -1084,7 +1085,7 @@
 	gotBytes += copied;
 
 	if (toRead == 0) {
-	    return gotBytes;
+	    goto stop;
 	}
 
 	/*
@@ -1107,10 +1108,10 @@
 	    int maxRead = -1;
 
 	    if (!TransformLimit(rtPtr, errorCodePtr, &maxRead)) {
-		return -1;
+		goto error;
 	    }
 	    if (maxRead == 0) {
-		return gotBytes;
+		goto stop;
 	    } else if (maxRead > 0) {
 		if (maxRead < toRead) {
 		    toRead = maxRead;
@@ -1119,7 +1120,7 @@
 	}
 
 	if (toRead <= 0) {
-	    return gotBytes;
+	    goto stop;
 	}
 
 	read = Tcl_ReadRaw(rtPtr->parent, buf, toRead);
@@ -1135,11 +1136,11 @@
 		 * we report that instead of the request to re-try.
 		 */
 
-		return gotBytes;
+		goto stop;
 	    }
 
 	    *errorCodePtr = Tcl_GetErrno();
-	    return -1;
+	    goto error;
 	}
 
 	if (read == 0) {
@@ -1160,16 +1161,16 @@
 
 		if ((gotBytes == 0) && rtPtr->nonblocking) {
 		    *errorCodePtr = EWOULDBLOCK;
-		    return -1;
+		    goto error;
 		}
-		return gotBytes;
+		goto stop;
 	    } else {
 		/*
 		 * Eof in parent.
 		 */
 
 		if (rtPtr->readIsDrained) {
-		    return gotBytes;
+		    goto stop;
 		}
 
 		/*
@@ -1179,7 +1180,7 @@
 
 		if (HAS(rtPtr->methods, METH_DRAIN)) {
 		    if (!TransformDrain(rtPtr, errorCodePtr)) {
-			return -1;
+			goto error;
 		    }
 		}
 
@@ -1188,7 +1189,7 @@
 		     * The drain delivered nothing.
 		     */
 
-		    return gotBytes;
+		    goto stop;
 		}
 
 		/*
@@ -1207,11 +1208,17 @@
 	 */
 
 	if (!TransformRead(rtPtr, errorCodePtr, UCHARP(buf), read)) {
-	    return -1;
+	    goto error;
 	}
     } /* while toRead > 0 */
 
+ stop:
+    Tcl_Release(rtPtr);
     return gotBytes;
+
+ error:
+    gotBytes = -1;
+    goto stop;
 }
 
 /*
@@ -1264,6 +1271,8 @@
      * we do when explicitly seeking as well.
      */
 
+    Tcl_Preserve(rtPtr);
+
     if ((rtPtr->methods & FLAG(METH_CLEAR))) {
 	TransformClear(rtPtr);
     }
@@ -1275,10 +1284,12 @@
      */
 
     if (!TransformWrite(rtPtr, errorCodePtr, UCHARP(buf), toWrite)) {
+	Tcl_Release(rtPtr);
 	return -1;
     }
 
     *errorCodePtr = EOK;
+    Tcl_Release(rtPtr);
     return toWrite;
 }
 
@@ -1329,6 +1340,8 @@
      * request down and the result back up unchanged.
      */
 
+    Tcl_Preserve(rtPtr);
+
     if (((seekMode != SEEK_CUR) || (offset != 0)) &&
 	  (HAS(rtPtr->methods,METH_CLEAR) || HAS(rtPtr->methods,METH_FLUSH))){
 	/*
@@ -1350,6 +1363,7 @@
 
 	if (HAS(rtPtr->methods, METH_FLUSH)) {
 	    if (!TransformFlush(rtPtr, errorCodePtr, FLUSH_DISCARD)) {
+		Tcl_Release(rtPtr);
 		return -1;
 	    }
 	}
@@ -1379,6 +1393,7 @@
     }
 
     *errorCodePtr = EOK;
+    Tcl_Release(rtPtr);
     return curPos;
 }
 
@@ -1968,7 +1983,7 @@
      */
 
     sr = Tcl_SaveInterpState(rtPtr->interp, 0 /* Dummy */);
-    Tcl_Preserve(rtPtr->interp);
+    Tcl_Preserve(rtPtr);
     result = Tcl_EvalObjv(rtPtr->interp, cmdc, rtPtr->argv, TCL_EVAL_GLOBAL);
 
     /*
@@ -2013,7 +2028,7 @@
 	Tcl_IncrRefCount(resObj);
     }
     Tcl_RestoreInterpState(rtPtr->interp, sr);
-    Tcl_Release(rtPtr->interp);
+    Tcl_Release(rtPtr);
 
     /*
      * Cleanup of the dynamic parts of the command.
@@ -2528,7 +2543,8 @@
 	rtmPtr = GetThreadReflectedTransformMap();
 	hPtr = Tcl_FindHashEntry(&rtmPtr->map, Tcl_GetString(rtPtr->handle));
 	Tcl_DeleteHashEntry(hPtr);
-	FreeReflectedTransform(rtPtr);
+
+	Tcl_EventuallyFree (rtPtr, FreeReflectedTransform);
 	break;
 
     case ForwardedInput: {
