diff options
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | generic/tclIORChan.c | 133 |
2 files changed, 95 insertions, 47 deletions
@@ -1,3 +1,12 @@ +2010-03-30 Andreas Kupries <andreask@activestate.com> + + * generic/tclIORChan.c (ReflectClose, ReflectInput, ReflectOutput, + ReflectSeekWide, ReflectWatch, ReflectBlock, ReflectSetOption, + ReflectGetOption, ForwardProc): [Bug 2978773]: Preserve + ReflectedChannel* structures across handler invokations, to avoid + crashes when the handler implementation induces nested callbacks + and destruction of the channel deep inside such a nesting. + 2010-03-30 Don Porter <dgp@users.sourceforge.net> * generic/tclObj.c (Tcl_GetCommandFromObj): [Bug 2979402]: Reorder diff --git a/generic/tclIORChan.c b/generic/tclIORChan.c index f483870..03ddc16 100644 --- a/generic/tclIORChan.c +++ b/generic/tclIORChan.c @@ -15,7 +15,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclIORChan.c,v 1.28.2.9 2010/03/09 21:13:13 andreas_kupries Exp $ + * RCS: @(#) $Id: tclIORChan.c,v 1.28.2.10 2010/03/30 21:17:30 andreas_kupries Exp $ */ #include <tclInt.h> @@ -1082,7 +1082,7 @@ ReflectClose( } #endif - FreeReflectedChannel(rcPtr); + Tcl_EventuallyFree (rcPtr, (Tcl_FreeProc *) FreeReflectedChannel); return EOK; } @@ -1094,7 +1094,7 @@ ReflectClose( */ if (rcPtr->methods == 0) { - FreeReflectedChannel(rcPtr); + Tcl_EventuallyFree (rcPtr, (Tcl_FreeProc *) FreeReflectedChannel); return EOK; } @@ -1157,7 +1157,7 @@ ReflectClose( } #endif - FreeReflectedChannel(rcPtr); + Tcl_EventuallyFree (rcPtr, (Tcl_FreeProc *) FreeReflectedChannel); #ifdef TCL_THREADS } #endif @@ -1238,29 +1238,26 @@ ReflectInput( /* ASSERT: rcPtr->method & FLAG(METH_READ) */ /* ASSERT: rcPtr->mode & TCL_READABLE */ + Tcl_Preserve(rcPtr); + toReadObj = Tcl_NewIntObj(toRead); if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj)!=TCL_OK) { int code = ErrnoReturn (rcPtr, resObj); if (code < 0) { - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ *errorCodePtr = -code; - return -1; + goto error; } Tcl_SetChannelError(rcPtr->chan, resObj); - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ - *errorCodePtr = EINVAL; - return -1; + goto invalid; } bytev = Tcl_GetByteArrayFromObj(resObj, &bytec); if (toRead < bytec) { - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ SetChannelErrorStr(rcPtr->chan, msg_read_toomuch); - *errorCodePtr = EINVAL; - return -1; + goto invalid; } *errorCodePtr = EOK; @@ -1269,8 +1266,15 @@ ReflectInput( memcpy(buf, bytev, (size_t)bytec); } + stop: Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ + Tcl_Release(rcPtr); return bytec; + invalid: + *errorCodePtr = EINVAL; + error: + bytec = -1; + goto stop; } /* @@ -1346,31 +1350,26 @@ ReflectOutput( /* ASSERT: rcPtr->method & FLAG(METH_WRITE) */ /* ASSERT: rcPtr->mode & TCL_WRITABLE */ + Tcl_Preserve(rcPtr); + bufObj = Tcl_NewByteArrayObj((unsigned char *) buf, toWrite); if (InvokeTclMethod(rcPtr, "write", bufObj, NULL, &resObj) != TCL_OK) { int code = ErrnoReturn(rcPtr, resObj); if (code < 0) { - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ *errorCodePtr = -code; - return -1; + goto error; } Tcl_SetChannelError(rcPtr->chan, resObj); - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ - *errorCodePtr = EINVAL; - return -1; + goto invalid; } if (Tcl_GetIntFromObj(rcPtr->interp, resObj, &written) != TCL_OK) { - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp)); - *errorCodePtr = EINVAL; - return -1; + goto invalid; } - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ - if ((written == 0) && (toWrite > 0)) { /* * The handler claims to have written nothing of what it was @@ -1378,8 +1377,7 @@ ReflectOutput( */ SetChannelErrorStr(rcPtr->chan, msg_write_nothing); - *errorCodePtr = EINVAL; - return -1; + goto invalid; } if (toWrite < written) { /* @@ -1389,12 +1387,19 @@ ReflectOutput( */ SetChannelErrorStr(rcPtr->chan, msg_write_toomuch); - *errorCodePtr = EINVAL; - return -1; + goto invalid; } *errorCodePtr = EOK; + stop: + Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ + Tcl_Release(rcPtr); return written; + invalid: + *errorCodePtr = EINVAL; + error: + written = -1; + goto stop; } /* @@ -1452,33 +1457,35 @@ ReflectSeekWide( /* ASSERT: rcPtr->method & FLAG(METH_SEEK) */ + Tcl_Preserve(rcPtr); + offObj = Tcl_NewWideIntObj(offset); baseObj = Tcl_NewStringObj((seekMode == SEEK_SET) ? "start" : ((seekMode == SEEK_CUR) ? "current" : "end"), -1); if (InvokeTclMethod(rcPtr, "seek", offObj, baseObj, &resObj)!=TCL_OK) { Tcl_SetChannelError(rcPtr->chan, resObj); - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ - *errorCodePtr = EINVAL; - return -1; + goto invalid; } if (Tcl_GetWideIntFromObj(rcPtr->interp, resObj, &newLoc) != TCL_OK) { - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp)); - *errorCodePtr = EINVAL; - return -1; + goto invalid; } - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ - if (newLoc < Tcl_LongAsWide(0)) { SetChannelErrorStr(rcPtr->chan, msg_seek_beforestart); - *errorCodePtr = EINVAL; - return -1; + goto invalid; } *errorCodePtr = EOK; + stop: + Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ + Tcl_Release(rcPtr); return newLoc; + invalid: + *errorCodePtr = EINVAL; + newLoc = -1; + goto stop; } static int @@ -1564,9 +1571,13 @@ ReflectWatch( } #endif + Tcl_Preserve(rcPtr); + maskObj = DecodeEventMask(mask); (void) InvokeTclMethod(rcPtr, "watch", maskObj, NULL, NULL); Tcl_DecrRefCount(maskObj); + + Tcl_Release(rcPtr); } /* @@ -1619,6 +1630,8 @@ ReflectBlock( blockObj = Tcl_NewBooleanObj(!nonblocking); + Tcl_Preserve(rcPtr); + if (InvokeTclMethod(rcPtr, "blocking", blockObj, NULL, &resObj) != TCL_OK) { Tcl_SetChannelError(rcPtr->chan, resObj); errorNum = EINVAL; @@ -1627,6 +1640,8 @@ ReflectBlock( } Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ + + Tcl_Release(rcPtr); return errorNum; } @@ -1682,6 +1697,7 @@ ReflectSetOption( return p.base.code; } #endif + Tcl_Preserve(rcPtr); optionObj = Tcl_NewStringObj(optionName, -1); valueObj = Tcl_NewStringObj(newValue, -1); @@ -1691,6 +1707,7 @@ ReflectSetOption( } Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ + Tcl_Release(rcPtr); return result; } @@ -1777,10 +1794,11 @@ ReflectGetOption( optionObj = Tcl_NewStringObj(optionName, -1); } + Tcl_Preserve(rcPtr); + if (InvokeTclMethod(rcPtr, method, optionObj, NULL, &resObj)!=TCL_OK) { UnmarshallErrorResult(interp, resObj); - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ - return TCL_ERROR; + goto error; } /* @@ -1790,8 +1808,7 @@ ReflectGetOption( if (optionObj != NULL) { Tcl_DStringAppend(dsPtr, TclGetString(resObj), -1); - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ - return TCL_OK; + goto ok; } /* @@ -1806,8 +1823,7 @@ ReflectGetOption( */ if (Tcl_ListObjGetElements(interp, resObj, &listc, &listv) != TCL_OK) { - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ - return TCL_ERROR; + goto error; } if ((listc % 2) == 1) { @@ -1820,8 +1836,7 @@ ReflectGetOption( "Expected list with even number of " "elements, got %d element%s instead", listc, (listc == 1 ? "" : "s"))); - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ - return TCL_ERROR; + goto error; } else { int len; char *str = Tcl_GetStringFromObj(resObj, &len); @@ -1830,9 +1845,17 @@ ReflectGetOption( Tcl_DStringAppend(dsPtr, " ", 1); Tcl_DStringAppend(dsPtr, str, len); } - Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ - return TCL_OK; + goto ok; } + + ok: + Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ + Tcl_Release(rcPtr); + return TCL_OK; + error: + Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ + Tcl_Release(rcPtr); + return TCL_ERROR; } /* @@ -2816,12 +2839,13 @@ ForwardProc( Tcl_GetChannelName (rcPtr->chan)); Tcl_DeleteHashEntry (hPtr); - FreeReflectedChannel(rcPtr); + Tcl_EventuallyFree (rcPtr, (Tcl_FreeProc *) FreeReflectedChannel); break; case ForwardedInput: { Tcl_Obj *toReadObj = Tcl_NewIntObj(paramPtr->input.toRead); + Tcl_Preserve(rcPtr); if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj)!=TCL_OK){ int code = ErrnoReturn (rcPtr, resObj); @@ -2851,6 +2875,7 @@ ForwardProc( paramPtr->input.toRead = bytec; } } + Tcl_Release(rcPtr); break; } @@ -2858,6 +2883,7 @@ ForwardProc( Tcl_Obj *bufObj = Tcl_NewByteArrayObj((unsigned char *) paramPtr->output.buf, paramPtr->output.toWrite); + Tcl_Preserve(rcPtr); if (InvokeTclMethod(rcPtr, "write", bufObj, NULL, &resObj) != TCL_OK) { int code = ErrnoReturn(rcPtr, resObj); @@ -2884,6 +2910,7 @@ ForwardProc( paramPtr->output.toWrite = written; } } + Tcl_Release(rcPtr); break; } @@ -2893,6 +2920,7 @@ ForwardProc( (paramPtr->seek.seekMode==SEEK_SET) ? "start" : (paramPtr->seek.seekMode==SEEK_CUR) ? "current" : "end", -1); + Tcl_Preserve(rcPtr); if (InvokeTclMethod(rcPtr, "seek", offObj, baseObj, &resObj)!=TCL_OK){ ForwardSetObjError(paramPtr, resObj); paramPtr->seek.offset = -1; @@ -2916,24 +2944,29 @@ ForwardProc( paramPtr->seek.offset = -1; } } + Tcl_Release(rcPtr); break; } case ForwardedWatch: { Tcl_Obj *maskObj = DecodeEventMask(paramPtr->watch.mask); + Tcl_Preserve(rcPtr); (void) InvokeTclMethod(rcPtr, "watch", maskObj, NULL, NULL); Tcl_DecrRefCount(maskObj); + Tcl_Release(rcPtr); break; } case ForwardedBlock: { Tcl_Obj *blockObj = Tcl_NewBooleanObj(!paramPtr->block.nonblocking); + Tcl_Preserve(rcPtr); if (InvokeTclMethod(rcPtr, "blocking", blockObj, NULL, &resObj) != TCL_OK) { ForwardSetObjError(paramPtr, resObj); } + Tcl_Release(rcPtr); break; } @@ -2941,10 +2974,12 @@ ForwardProc( Tcl_Obj *optionObj = Tcl_NewStringObj(paramPtr->setOpt.name, -1); Tcl_Obj *valueObj = Tcl_NewStringObj(paramPtr->setOpt.value, -1); + Tcl_Preserve(rcPtr); if (InvokeTclMethod(rcPtr, "configure", optionObj, valueObj, &resObj) != TCL_OK) { ForwardSetObjError(paramPtr, resObj); } + Tcl_Release(rcPtr); break; } @@ -2955,12 +2990,14 @@ ForwardProc( Tcl_Obj *optionObj = Tcl_NewStringObj(paramPtr->getOpt.name, -1); + Tcl_Preserve(rcPtr); if (InvokeTclMethod(rcPtr, "cget", optionObj, NULL, &resObj)!=TCL_OK){ ForwardSetObjError(paramPtr, resObj); } else { Tcl_DStringAppend(paramPtr->getOpt.value, TclGetString(resObj), -1); } + Tcl_Release(rcPtr); break; } @@ -2969,6 +3006,7 @@ ForwardProc( * Retrieve all options. */ + Tcl_Preserve(rcPtr); if (InvokeTclMethod(rcPtr, "cgetall", NULL, NULL, &resObj) != TCL_OK){ ForwardSetObjError(paramPtr, resObj); } else { @@ -3004,6 +3042,7 @@ ForwardProc( } } } + Tcl_Release(rcPtr); break; default: |