summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorapnadkarni <apnmbx-wits@yahoo.com>2023-03-01 13:18:17 (GMT)
committerapnadkarni <apnmbx-wits@yahoo.com>2023-03-01 13:18:17 (GMT)
commit30a9b333ba194339f5e8f68575626df0701b2a50 (patch)
tree4c5588b358d57274bcb77c78979fde884c77bd73
parenta6b4ef3d29565b68fb8c7104b11b03a99e0b153e (diff)
downloadtcl-30a9b333ba194339f5e8f68575626df0701b2a50.zip
tcl-30a9b333ba194339f5e8f68575626df0701b2a50.tar.gz
tcl-30a9b333ba194339f5e8f68575626df0701b2a50.tar.bz2
Bug [9a978f8323]: crash reading large files
-rw-r--r--generic/tclIO.c26
-rw-r--r--tests/io.test104
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