summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Klebinger <klebinger.andreas@gmx.at>2019-12-17 16:29:54 +0100
committerAndreas Klebinger <klebinger.andreas@gmx.at>2019-12-17 16:47:13 +0100
commit59b265f4dd6104f518538c7cefa0dee574fb8ca9 (patch)
treee6b489b75851cf27f385552250b1b711e67f29e6
parent62fa1f51e0bf6737520f65fac3057dc541b92632 (diff)
downloadhaskell-wip/winio-changes.tar.gz
Fix input truncation when reading from handle.wip/winio-changes
This was caused by not upholding the read buffer invariant that bufR == bufL == 0 for empty read buffers.
-rw-r--r--libraries/base/GHC/IO/Buffer.hs3
-rw-r--r--libraries/base/GHC/IO/Handle/Text.hs37
2 files changed, 26 insertions, 14 deletions
diff --git a/libraries/base/GHC/IO/Buffer.hs b/libraries/base/GHC/IO/Buffer.hs
index 5098e087d1..f169ae6f5f 100644
--- a/libraries/base/GHC/IO/Buffer.hs
+++ b/libraries/base/GHC/IO/Buffer.hs
@@ -305,7 +305,8 @@ summaryBuffer !buf -- Strict => slightly better code
in if null p then "0x0" else '0':'x':p
ppr x = x
--- INVARIANTS on Buffers:
+-- Note [INVARIANTS on Buffers]
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- * r <= w
-- * if r == w, and the buffer is for reading, then r == 0 && w == 0
-- * a write buffer is never full. If an operation
diff --git a/libraries/base/GHC/IO/Handle/Text.hs b/libraries/base/GHC/IO/Handle/Text.hs
index 7af1b450f4..f7a1e73f8c 100644
--- a/libraries/base/GHC/IO/Handle/Text.hs
+++ b/libraries/base/GHC/IO/Handle/Text.hs
@@ -826,12 +826,16 @@ hGetBuf h !ptr count
bufReadNonEmpty :: Handle__ -> Buffer Word8 -> Ptr Word8 -> Int -> Int -> IO Int
bufReadNonEmpty h_@Handle__{..}
+ -- w for width, r for ... read ptr?
buf@Buffer{ bufRaw=raw, bufR=w, bufL=r, bufSize=sz }
ptr !so_far !count
= do
debugIO ":: bufReadNonEmpty"
+ -- We use < instead of <= because for count == avail
+ -- we need to reset bufL and bufR to zero.
+ -- See also: INVARIANTS on Buffers
let avail = w - r
- if (count <= avail)
+ if (count < avail)
then do
copyFromRawBuffer ptr raw r count
writeIORef haByteBuffer buf{ bufL = r + count }
@@ -850,24 +854,28 @@ bufReadNonEmpty h_@Handle__{..}
then return so_far'
else bufReadEmpty h_ buf' ptr' so_far' remaining
-
+-- We want to read more data, but the buffer is empty. (buffL == buffR == 0)
+-- See also Note [INVARIANTS on Buffers] in Buffer.hs
bufReadEmpty :: Handle__ -> Buffer Word8 -> Ptr Word8 -> Int -> Int -> IO Int
bufReadEmpty h_@Handle__{..}
buf@Buffer{ bufRaw=raw, bufR=w, bufL=r, bufSize=sz, bufOffset=bff }
ptr so_far count
- | count > sz = do count <- loop haDevice 0 bff count
- let buf1 = bufferAddOffset (fromIntegral count) buf
- -- let buf2 = buf1 { bufR = w + count }
- writeIORef haByteBuffer buf1
- debugIO ("bufReadEmpty: " ++ summaryBuffer buf1)
- return count
+ | count > sz
+ = do
+ count <- loop haDevice 0 bff count
+ let buf1 = bufferAddOffset (fromIntegral count) buf
+ writeIORef haByteBuffer buf1
+ debugIO ("bufReadEmpty: " ++ summaryBuffer buf1)
+ return count
| otherwise = do
- (r,buf') <- Buffered.fillReadBuffer haDevice buf
- if r == 0
- then return so_far
- else do writeIORef haByteBuffer buf'
- bufReadNonEmpty h_ buf' ptr so_far count
+ (r,buf') <- Buffered.fillReadBuffer haDevice buf
+ if r == 0
+ then return so_far
+ else do writeIORef haByteBuffer buf'
+ bufReadNonEmpty h_ buf' ptr so_far count
where
+ -- Read @bytes@ byte into ptr. Repeating the read until either zero
+ -- bytes where read, or we are done reading.
loop :: RawIO.RawIO dev => dev -> Int -> Word64 -> Int -> IO Int
loop dev delta off bytes | bytes <= 0 = return (so_far + delta)
loop dev delta off bytes = do
@@ -985,6 +993,9 @@ bufReadNBNonEmpty h_@Handle__{..}
ptr so_far count
= do
let avail = w - r
+ -- We use < instead of <= because for count == avail
+ -- we need to reset bufL and bufR to zero.
+ -- See also [INVARIANTS on Buffers] in Buffer.hs
if (count < avail)
then do
copyFromRawBuffer ptr raw r count