summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandreas_kupries <akupries@shaw.ca>2010-03-30 21:17:30 (GMT)
committerandreas_kupries <akupries@shaw.ca>2010-03-30 21:17:30 (GMT)
commita715f307a38d6b3c3a7bb1df77fbec824879937a (patch)
tree9fe56b0756f3a9351e4c5d35cd0f7ed74df953d7
parent36a7f1eef38266693086f5e322db122fd9469b20 (diff)
downloadtcl-a715f307a38d6b3c3a7bb1df77fbec824879937a.zip
tcl-a715f307a38d6b3c3a7bb1df77fbec824879937a.tar.gz
tcl-a715f307a38d6b3c3a7bb1df77fbec824879937a.tar.bz2
* 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.
-rw-r--r--ChangeLog9
-rw-r--r--generic/tclIORChan.c133
2 files changed, 95 insertions, 47 deletions
diff --git a/ChangeLog b/ChangeLog
index cf30088..b505616 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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: