From 30a9b333ba194339f5e8f68575626df0701b2a50 Mon Sep 17 00:00:00 2001 From: apnadkarni Date: Wed, 1 Mar 2023 13:18:17 +0000 Subject: Bug [9a978f8323]: crash reading large files --- generic/tclIO.c | 26 ++++++++------ tests/io.test | 104 +++++++++++++++++++++++--------------------------------- 2 files changed, 58 insertions(+), 72 deletions(-) diff --git a/generic/tclIO.c b/generic/tclIO.c index ff74a99..ce0dcc8 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -191,9 +191,9 @@ static int DetachChannel(Tcl_Interp *interp, Tcl_Channel chan); static void DiscardInputQueued(ChannelState *statePtr, int discardSavedBuffers); static void DiscardOutputQueued(ChannelState *chanPtr); -static int DoRead(Channel *chanPtr, char *dst, Tcl_Size bytesToRead, +static Tcl_Size DoRead(Channel *chanPtr, char *dst, Tcl_Size bytesToRead, int allowShortReads); -static int DoReadChars(Channel *chan, Tcl_Obj *objPtr, Tcl_Size toRead, +static Tcl_Size DoReadChars(Channel *chan, Tcl_Obj *objPtr, Tcl_Size toRead, int appendFlag); static int FilterInputBytes(Channel *chanPtr, GetsState *statePtr); @@ -5946,11 +5946,11 @@ Tcl_ReadChars( *--------------------------------------------------------------------------- */ -static int +static Tcl_Size DoReadChars( Channel *chanPtr, /* The channel to read. */ Tcl_Obj *objPtr, /* Input data is stored in this object. */ - Tcl_Size toRead, /* Maximum number of characters to store, or + Tcl_Size toRead, /* Maximum number of characters to store, or * TCL_INDEX_NONE to read all available data (up to EOF or * when channel blocks). */ int appendFlag) /* If non-zero, data read from the channel @@ -5961,7 +5961,8 @@ DoReadChars( ChannelState *statePtr = chanPtr->state; /* State info for channel */ ChannelBuffer *bufPtr; - int copied, copiedNow, result; + Tcl_Size copied; + int result; Tcl_Encoding encoding = statePtr->encoding; int binaryMode; #define UTF_EXPANSION_FACTOR 1024 @@ -6046,8 +6047,8 @@ DoReadChars( } ResetFlag(statePtr, CHANNEL_BLOCKED|CHANNEL_EOF); statePtr->inputEncodingFlags &= ~TCL_ENCODING_END; - for (copied = 0; toRead > 0; ) { - copiedNow = -1; + for (copied = 0; toRead > 0 || toRead == TCL_INDEX_NONE; ) { + int copiedNow = -1; if (statePtr->inQueueHead != NULL) { if (binaryMode) { copiedNow = ReadBytes(statePtr, objPtr, toRead); @@ -6093,7 +6094,9 @@ DoReadChars( } } else { copied += copiedNow; - toRead -= copiedNow; + if (toRead != TCL_INDEX_NONE) { + toRead -= copiedNow; /* Only decr if not reading whole file */ + } } } @@ -6269,7 +6272,7 @@ ReadChars( size_t size; dst = TclGetStringStorage(objPtr, &size) + numBytes; - dstLimit = size - numBytes; + dstLimit = (size - numBytes) > INT_MAX ? INT_MAX : (size - numBytes); } else { dst = TclGetString(objPtr) + numBytes; } @@ -9671,9 +9674,10 @@ CopyData( Tcl_Obj *cmdPtr, *errObj = NULL, *bufObj = NULL, *msg = NULL; Tcl_Channel inChan, outChan; ChannelState *inStatePtr, *outStatePtr; - int result = TCL_OK, size; + int result = TCL_OK; Tcl_Size sizeb; Tcl_WideInt total; + Tcl_WideInt size; /* TODO - be careful if total and size are made unsigned */ const char *buffer; int inBinary, outBinary, sameEncoding; /* Encoding control */ @@ -10011,7 +10015,7 @@ CopyData( *---------------------------------------------------------------------- */ -static int +static Tcl_Size DoRead( Channel *chanPtr, /* The channel from which to read. */ char *dst, /* Where to store input read. */ diff --git a/tests/io.test b/tests/io.test index 065eb4c..5b81dde 100644 --- a/tests/io.test +++ b/tests/io.test @@ -195,46 +195,50 @@ test io-1.9 {Tcl_WriteChars: WriteChars} { set sizes } {19 19 19 19 19} +proc testreadwrite {size {mode ""} args} { + set tmpfile [file join [temporaryDirectory] io-1.10.tmp] + set w [string repeat A $size] + try { + set fd [open $tmpfile w$mode] + try { + if {[llength $args]} { + fconfigure $fd {*}$args + } + puts -nonewline $fd $w + } finally { + close $fd + } + set fd [open $tmpfile r$mode] + try { + if {[llength $args]} { + fconfigure $fd {*}$args + } + set r [read $fd] + } finally { + close $fd + } + } finally { + file delete $tmpfile + } + string equal $w $r +} + test io-1.10 {WriteChars: large file (> INT_MAX). Bug 3d01d51bc4} -constraints { pointerIs64bit perf -} -setup { - set tmpfile [file join [temporaryDirectory] io-1.10.tmp] -} -cleanup { - file delete $tmpfile } -body { - set fd [open $tmpfile w] - puts -nonewline $fd [string repeat A 0x80000000] - close $fd - # TODO - Should really read it back in but large reads are not currently working! - file size $tmpfile -} -result 2147483648 + testreadwrite 0x80000000 +} -result 1 test io-1.11 {WriteChars: large file (> UINT_MAX). Bug 3d01d51bc4} -constraints { pointerIs64bit perf -} -setup { - set tmpfile [file join [temporaryDirectory] io-1.11.tmp] -} -cleanup { - file delete $tmpfile } -body { - set fd [open $tmpfile w] - puts -nonewline $fd [string repeat A 0x100000000] - close $fd - # TODO - Should really read it back in but large reads are not currently working! - file size $tmpfile -} -result 4294967296 + testreadwrite 0x100000000 "" -buffersize 1000000 +} -result 1 test io-1.12 {WriteChars: large file (== UINT_MAX). Bug 90ff9b7f73} -constraints { pointerIs64bit perf -} -setup { - set tmpfile [file join [temporaryDirectory] io-1.12.tmp] -} -cleanup { - file delete $tmpfile } -body { - set fd [open $tmpfile w] # *Exactly* UINT_MAX - separate bug from the general large file tests - puts -nonewline $fd [string repeat A 0xffffffff] - close $fd - # TODO - Should really read it back in but large reads are not currently working! - file size $tmpfile -} -result 4294967295 + testreadwrite 0xffffffff +} -result 1 test io-2.1 {WriteBytes} { # loop until all bytes are written @@ -277,47 +281,25 @@ test io-2.4 {WriteBytes: reset sawLF after each buffer} { close $f lappend x [contents $path(test1)] } [list "abcdefg\nhijklmno" "abcdefg\nhijklmnopqrstuvwxyz"] - test io-2.5 {WriteBytes: large file (> INT_MAX). Bug 3d01d51bc4} -constraints { pointerIs64bit perf -} -setup { - set tmpfile [file join [temporaryDirectory] io-2.5.tmp] -} -cleanup { - file delete $tmpfile } -body { - set fd [open $tmpfile wb] - puts -nonewline $fd [string repeat A 0x80000000] - close $fd - # TODO - Should really read it back in but large reads are not currently working! - file size $tmpfile -} -result 2147483648 + # Binary mode + testreadwrite 0x80000000 b +} -result 1 test io-2.6 {WriteBytes: large file (> UINT_MAX). Bug 3d01d51bc4} -constraints { pointerIs64bit perf -} -setup { - set tmpfile [file join [temporaryDirectory] io-2.6.tmp] -} -cleanup { - file delete $tmpfile } -body { - set fd [open $tmpfile wb] - puts -nonewline $fd [string repeat A 0x100000000] - close $fd - # TODO - Should really read it back in but large reads are not currently working! - file size $tmpfile -} -result 4294967296 + # Binary mode + testreadwrite 0x100000000 b -buffersize 1000000 +} -result 1 test io-2.7 {WriteBytes: large file (== UINT_MAX). Bug 90ff9b7f73} -constraints { pointerIs64bit perf -} -setup { - set tmpfile [file join [temporaryDirectory] io-2.7.tmp] -} -cleanup { - file delete $tmpfile } -body { - set fd [open $tmpfile wb] # *Exactly* UINT_MAX - separate bug from the general large file tests - puts -nonewline $fd [string repeat A 0xffffffff] - close $fd - # TODO - Should really read it back in but large reads are not currently working! - file size $tmpfile -} -result 4294967295 + testreadwrite 0xffffffff b +} -result 1 + test io-3.1 {WriteChars: compatibility with WriteBytes} { # loop until all bytes are written -- cgit v0.12