From 5923fbede8dae47a160d3452fe9cd4e447afbc25 Mon Sep 17 00:00:00 2001 From: dgp Date: Mon, 19 Oct 2009 21:59:17 +0000 Subject: * generic/tclIO.c: Revised ReadChars and FilterInputBytes routines to permit reads to continue up to the string limits of Tcl values. Before revisions, large read attempts could panic when as little as half the limiting value length was reached. [Patch 2107634] Thanks to Sean Morrison and Bob Parker for their roles in the fix. --- ChangeLog | 8 ++++++++ generic/tclIO.c | 44 +++++++++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3b4a3f9..609b273 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2009-10-19 Don Porter + + * generic/tclIO.c: Revised ReadChars and FilterInputBytes routines + to permit reads to continue up to the string limits of Tcl values. + Before revisions, large read attempts could panic when as little as + half the limiting value length was reached. [Patch 2107634] + Thanks to Sean Morrison and Bob Parker for their roles in the fix. + 2009-10-18 Joe Mistachkin * tests/thread.test (thread-4.[345]): [Bug 1565466]: Correct tests to diff --git a/generic/tclIO.c b/generic/tclIO.c index 649e31b..a74cd04 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclIO.c,v 1.137.2.11 2009/07/24 16:51:28 andreas_kupries Exp $ + * RCS: @(#) $Id: tclIO.c,v 1.137.2.12 2009/10/19 21:59:18 dgp Exp $ */ #include "tclInt.h" @@ -4613,7 +4613,7 @@ FilterInputBytes( /* State info for channel */ ChannelBuffer *bufPtr; char *raw, *rawStart, *dst; - int offset, toRead, dstNeeded, spaceLeft, result, rawLen, length; + int offset, toRead, dstNeeded, spaceLeft, result, rawLen; Tcl_Obj *objPtr; #define ENCODING_LINESIZE 20 /* Lower bound on how many bytes to convert at * a time. Since we don't know a priori how @@ -4679,15 +4679,19 @@ FilterInputBytes( if (toRead > rawLen) { toRead = rawLen; } - dstNeeded = toRead * TCL_UTF_MAX + 1; - spaceLeft = objPtr->length - offset - TCL_UTF_MAX - 1; + dstNeeded = toRead * TCL_UTF_MAX; + spaceLeft = objPtr->length - offset; if (dstNeeded > spaceLeft) { - length = offset * 2; - if (offset < dstNeeded) { + int length = offset + ((offset < dstNeeded) ? dstNeeded : offset); + + if (Tcl_AttemptSetObjLength(objPtr, length) == 0) { length = offset + dstNeeded; + if (Tcl_AttemptSetObjLength(objPtr, length) == 0) { + dstNeeded = TCL_UTF_MAX - 1 + toRead; + length = offset + dstNeeded; + Tcl_SetObjLength(objPtr, length); + } } - length += TCL_UTF_MAX + 1; - Tcl_SetObjLength(objPtr, length); spaceLeft = length - offset; dst = objPtr->bytes + offset; *gsPtr->dstPtr = dst; @@ -4695,7 +4699,7 @@ FilterInputBytes( gsPtr->state = statePtr->inputEncodingState; result = Tcl_ExternalToUtf(NULL, gsPtr->encoding, raw, rawLen, statePtr->inputEncodingFlags, &statePtr->inputEncodingState, - dst, spaceLeft, &gsPtr->rawRead, &gsPtr->bytesWrote, + dst, spaceLeft+1, &gsPtr->rawRead, &gsPtr->bytesWrote, &gsPtr->charsWrote); /* @@ -5419,6 +5423,8 @@ ReadBytes( * 'charsToRead' can safely be a very large number because space is only * allocated to hold data read from the channel as needed. * + * 'charsToRead' may *not* be 0. + * * Results: * The return value is the number of characters appended to the object, * *offsetPtr is filled with the number of bytes that were appended, and @@ -5456,7 +5462,7 @@ ReadChars( * UTF-8. On output, contains another guess * based on the data seen so far. */ { - int toRead, factor, offset, spaceLeft, length, srcLen, dstNeeded; + int toRead, factor, offset, spaceLeft, srcLen, dstNeeded; int srcRead, dstWrote, numChars, dstRead; ChannelBuffer *bufPtr; char *src, *dst; @@ -5481,8 +5487,8 @@ ReadChars( * how many characters were produced by the previous pass. */ - dstNeeded = toRead * factor / UTF_EXPANSION_FACTOR; - spaceLeft = objPtr->length - offset - TCL_UTF_MAX - 1; + dstNeeded = TCL_UTF_MAX - 1 + toRead * factor / UTF_EXPANSION_FACTOR; + spaceLeft = objPtr->length - offset; if (dstNeeded > spaceLeft) { /* @@ -5491,13 +5497,17 @@ ReadChars( * larger. */ - length = offset * 2; - if (offset < dstNeeded) { + int length = offset + ((offset < dstNeeded) ? dstNeeded : offset); + + if (Tcl_AttemptSetObjLength(objPtr, length) == 0) { length = offset + dstNeeded; + if (Tcl_AttemptSetObjLength(objPtr, length) == 0) { + dstNeeded = TCL_UTF_MAX - 1 + toRead; + length = offset + dstNeeded; + Tcl_SetObjLength(objPtr, length); + } } spaceLeft = length - offset; - length += TCL_UTF_MAX + 1; - Tcl_SetObjLength(objPtr, length); } if (toRead == srcLen) { /* @@ -5589,7 +5599,7 @@ ReadChars( Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen, statePtr->inputEncodingFlags, &statePtr->inputEncodingState, dst, - dstNeeded + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars); + dstNeeded + 1, &srcRead, &dstWrote, &numChars); if (encEndFlagSuppressed) { statePtr->inputEncodingFlags |= TCL_ENCODING_END; -- cgit v0.12