on master help sign in
Fix a crash reported by Michael Snoyman.
1b30a07Bryan O'Sullivan
Data/Text/Lazy/Encoding/Fusion.hs+5−8
@@ -100,19 +100,17 @@ streamUtf8 onErr bs0 = Stream next (T bs0 S0 0) unknownSize
100unstreamChunks :: Int -> Stream Word8 -> ByteString
100unstreamChunks :: Int -> Stream Word8 -> ByteString
101unstreamChunks chunkSize (Stream next s0 len0) = chunk s0 (upperBound 4 len0)
101unstreamChunks chunkSize (Stream next s0 len0) = chunk s0 (upperBound 4 len0)
102 where chunk s1 len1 = unsafePerformIO $ do
102 where chunk s1 len1 = unsafePerformIO $ do
103− let len = min len1 chunkSize
103+ let len = max 4 (min len1 chunkSize)
104 mallocByteString len >>= loop len 0 s1
104 mallocByteString len >>= loop len 0 s1
105 where
105 where
106 loop !n !off !s fp = case next s of
106 loop !n !off !s fp = case next s of
107 Done | off == 0 -> return Empty
107 Done | off == 0 -> return Empty
108− | otherwise -> do
109− bs <- trimUp fp off
110− return $! Chunk bs Empty
108+ | otherwise -> return $! Chunk (trimUp fp off) Empty
111 Skip s' -> loop n off s' fp
109 Skip s' -> loop n off s' fp
112 Yield x s'
110 Yield x s'
113 | off == chunkSize -> do
111 | off == chunkSize -> do
114− bs <- trimUp fp off
112+ let !newLen = n - off
115− return (Chunk bs (chunk s (n - B.length bs)))
113+ return $! Chunk (trimUp fp off) (chunk s newLen)
116 | off == n -> realloc fp n off s' x
114 | off == n -> realloc fp n off s' x
117 | otherwise -> do
115 | otherwise -> do
118 withForeignPtr fp $ \p -> pokeByteOff p off x
116 withForeignPtr fp $ \p -> pokeByteOff p off x
@@ -123,8 +121,7 @@ unstreamChunks chunkSize (Stream next s0 len0) = chunk s0 (upperBound 4 len0)
123 fp' <- copy0 fp n n'
121 fp' <- copy0 fp n n'
124 withForeignPtr fp' $ \p -> pokeByteOff p off x
122 withForeignPtr fp' $ \p -> pokeByteOff p off x
125 loop n' (off+1) s fp'
123 loop n' (off+1) s fp'
126− {-# NOINLINE trimUp #-}
127− trimUp fp off = return $! B.PS fp 0 off
124+ trimUp fp off = B.PS fp 0 off
128 copy0 :: ForeignPtr Word8 -> Int -> Int -> IO (ForeignPtr Word8)
125 copy0 :: ForeignPtr Word8 -> Int -> Int -> IO (ForeignPtr Word8)
129 copy0 !src !srcLen !destLen = assert (srcLen <= destLen) $ do
126 copy0 !src !srcLen !destLen = assert (srcLen <= destLen) $ do
130 dest <- mallocByteString destLen
127 dest <- mallocByteString destLen
tests/Makefile+6−2
@@ -5,7 +5,8 @@ ghc-base-flags := -funbox-strict-fields -package criterion \
5 -package bytestring -ignore-package text \
5 -package bytestring -ignore-package text \
6 -fno-ignore-asserts
6 -fno-ignore-asserts
7ghc-test-flags := -package QuickCheck -package test-framework \
7ghc-test-flags := -package QuickCheck -package test-framework \
8− -package test-framework-quickcheck
8+ -package test-framework-quickcheck -package test-framework-hunit \
9+ -package HUnit
9ghc-base-flags += -Wall -fno-warn-orphans -fno-warn-missing-signatures
10ghc-base-flags += -Wall -fno-warn-orphans -fno-warn-missing-signatures
10ghc-flags := $(ghc-base-flags) -i../dist/build -package-name text-$(version)
11ghc-flags := $(ghc-base-flags) -i../dist/build -package-name text-$(version)
11ghc-hpc-flags := $(ghc-base-flags) -fhpc -fno-ignore-asserts -odir hpcdir \
12ghc-hpc-flags := $(ghc-base-flags) -fhpc -fno-ignore-asserts -odir hpcdir \
@@ -16,7 +17,7 @@ lib-srcs := $(shell grep '^ *Data' ../text.cabal | \
16
17
17cabal := $(shell which cabal 2>/dev/null)
18cabal := $(shell which cabal 2>/dev/null)
18
19
19−all: bm qc coverage
20+all: bm qc coverage regressions
20
21
21lib: $(lib)
22lib: $(lib)
22
23
@@ -46,6 +47,9 @@ qc-hpc: Properties.hs QuickCheckUtils.hs $(lib-srcs:%=../%)
46 $(ghc) $(ghc-hpc-flags) $(ghc-opt-flags) -ihpcdir \
47 $(ghc) $(ghc-hpc-flags) $(ghc-opt-flags) -ihpcdir \
47 --make -threaded -o $@ $<
48 --make -threaded -o $@ $<
48
49
50+regressions: Regressions.o
51+ $(ghc) $(ghc-test-flags) -o $@ $^ $(lib)
52+
49coverage: qc-hpc-html/hpc_index.html
53coverage: qc-hpc-html/hpc_index.html
50
54
51qc-hpc-html/hpc_index.html: qc-hpc
55qc-hpc-html/hpc_index.html: qc-hpc
tests/Regressions.hsnew file+26
@@ -0,0 +1,26 @@
1+-- Regression tests for specific bugs.
2+
3+import Control.Exception (bracket)
4+import System.Directory (removeFile)
5+import System.IO (hClose, openTempFile)
6+import Test.HUnit (Assertion)
7+import qualified Data.ByteString.Lazy as LB
8+import qualified Data.Text.Lazy as LT
9+import qualified Data.Text.Lazy.Encoding as LE
10+import qualified Test.Framework as F
11+import qualified Test.Framework.Providers.HUnit as F
12+
13+-- Reported by Michael Snoyman: UTF-8 encoding a large lazy bytestring
14+-- caused either a segfault or attempt to allocate a negative number
15+-- of bytes.
16+lazy_encode_crash =
17+ bracket (openTempFile "." "crashy.txt")
18+ (\(path,h) -> hClose h >> removeFile path) $
19+ \(_,h) -> LB.hPut h . LE.encodeUtf8 . LT.pack . replicate 100000 $ 'a'
20+
21+tests :: F.Test
22+tests = F.testGroup "crashers" [
23+ F.testCase "lazy_encode_crash" lazy_encode_crash
24+ ]
25+
26+main = F.defaultMain [tests]