GHC issueshttps://gitlab.haskell.org/ghc/ghc/-/issues2023-06-20T15:56:29Zhttps://gitlab.haskell.org/ghc/ghc/-/issues/5688instance Read Integer/Rational/Double readsPrec out of memory and crash due t...2023-06-20T15:56:29Zgracjaninstance Read Integer/Rational/Double readsPrec out of memory and crash due to exponential notation```
GHCi, version 6.12.3: http://www.haskell.org/ghc/
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ....```
GHCi, version 6.12.3: http://www.haskell.org/ghc/
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
120000000000
Prelude> read "12e1000000000000" :: Integer
Segmentation fault
```
Sometimes it fails with Bus error.
According to Haskell'98 and Haskell'00 Reports Integers should not parse exponential notation at all.
http://www.haskell.org/onlinereport/haskell2010/haskellch2.html\#x7-190002.5
This is security issue in web frameworks as parsing HTTP headers, URLs, JSON and other may involve parsing integers.7.4.2Ian Lynagh <igloo@earth.li>Ian Lynagh <igloo@earth.li>https://gitlab.haskell.org/ghc/ghc/-/issues/3897reading a large String as Double takes too long2019-07-07T19:01:38ZChristian Maederreading a large String as Double takes too long```
Prelude> :set +s
Prelude> read "1e1000000" :: Double
Infinity
(0.46 secs, 8977756 bytes)
Prelude> read "1e1000000000" :: Double
```
The final call takes up all memory and does not terminate
<details><summary>Trac metadata</summary>...```
Prelude> :set +s
Prelude> read "1e1000000" :: Double
Infinity
(0.46 secs, 8977756 bytes)
Prelude> read "1e1000000000" :: Double
```
The final call takes up all memory and does not terminate
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------------ |
| Version | 6.12.1 |
| Type | Bug |
| TypeOfFailure | RuntimePerformance |
| Priority | normal |
| Resolution | Unresolved |
| Component | Prelude |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | Linux |
| Architecture | x86 |
</details>
<!-- {"blocked_by":[],"summary":"reading a large String as Double takes too long","status":"New","operating_system":"Linux","component":"Prelude","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"6.12.1","keywords":[],"differentials":[],"test_case":"","architecture":"x86","cc":[""],"type":"Bug","description":"{{{\r\nPrelude> :set +s\r\nPrelude> read \"1e1000000\" :: Double\r\nInfinity\r\n(0.46 secs, 8977756 bytes)\r\nPrelude> read \"1e1000000000\" :: Double\r\n}}}\r\n\r\nThe final call takes up all memory and does not terminate","type_of_failure":"RuntimePerformance","blocking":[]} -->7.4.2https://gitlab.haskell.org/ghc/ghc/-/issues/4968openTempFile fails on Windows if a directory exists with the file name it tries2019-07-07T18:57:34ZganeshopenTempFile fails on Windows if a directory exists with the file name it triesThe program below works on Linux but fails on Windows with "permission denied" from the second openTempFile.
I think the reason is that findTempName in the openTempFile source doesn't get eEXIST when a directory with the same name exist...The program below works on Linux but fails on Windows with "permission denied" from the second openTempFile.
I think the reason is that findTempName in the openTempFile source doesn't get eEXIST when a directory with the same name exists, so it fails instead of trying again.
The real use case behind this code is making temp directories on multiple threads simultaneously.
```
import Prelude
import System.IO
import System.Directory
main :: IO ()
main = do
dir <- getTemporaryDirectory
(npath1, handle1) <- openTempFile dir "tmp"
hClose handle1
removeFile npath1
createDirectory npath1
(npath2, handle2) <- openTempFile dir "tmp"
return ()
```
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | --------------- |
| Version | 7.0.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | libraries/base |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | ganesh@earth.li |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"openTempFile fails on Windows if a directory exists with the file name it tries","status":"New","operating_system":"","component":"libraries/base","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"7.0.1","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":["ganesh@earth.li"],"type":"Bug","description":"The program below works on Linux but fails on Windows with \"permission denied\" from the second openTempFile.\r\n\r\nI think the reason is that findTempName in the openTempFile source doesn't get eEXIST when a directory with the same name exists, so it fails instead of trying again.\r\n\r\nThe real use case behind this code is making temp directories on multiple threads simultaneously.\r\n\r\n\r\n{{{\r\nimport Prelude\r\nimport System.IO\r\nimport System.Directory\r\n\r\nmain :: IO ()\r\nmain = do\r\n dir <- getTemporaryDirectory\r\n (npath1, handle1) <- openTempFile dir \"tmp\"\r\n hClose handle1\r\n removeFile npath1\r\n createDirectory npath1\r\n (npath2, handle2) <- openTempFile dir \"tmp\"\r\n return ()\r\n\r\n}}}\r\n","type_of_failure":"OtherFailure","blocking":[]} -->7.4.2pcapriottipcapriottihttps://gitlab.haskell.org/ghc/ghc/-/issues/5568Add Show and Binary instances for TypeRep2019-07-07T18:54:41ZSimon MarlowAdd Show and Binary instances for TypeRep(was a re-opening of #3480; copied into a new ticket so we can leave #3480 dead and buried)
Unfortunately the new !TypeRepKey type is abstract and provides no operations apart from Eq and Ord instances. Hence, there is still no way to w...(was a re-opening of #3480; copied into a new ticket so we can leave #3480 dead and buried)
Unfortunately the new !TypeRepKey type is abstract and provides no operations apart from Eq and Ord instances. Hence, there is still no way to write a type representation into a file or across the wire. Jush showing a !TypeRep won't cut it, since that loses the distinctions between similarly named types in different modules.
So, I think we need a Show instance for !TypeRepKey, or better yet, a Binary instance. Moreover, a non-deprecated pure function `TypeRep -> TypeRepKey` is in place, since the keys have an actual use as a compact, efficient, and serializable injection of the typereps.
Alternatively, the Show instance of !TypeRep could be made injective by printing out the package and module.
### Commentary from #3480
simonmar says:
> I've exposed the representation via the Data.Typeable.Internal module. Of course that's not guaranteed to be a stable API, but I'm happy to add a stable API if we can agree on what it should be. Lennart Augustsson and Neil Mitchell are doing exactly this (serialising !TypeRep) - I think they're using the Data.Typeable.Internal API right now.
> So what shall we do?
Lennart says:
> For older versions of ghc I just used (show . typeOf). When ghc 7.2 broke this I had to start using Internal, so now I use tyConModule and tyConName to emulate the behaviour of the old show.
lealanko says:
> I'm sorry for messing up the ticket. I had assumed that the missing Show instance had been a simple oversight. If so, this would suffice:
```
instance Show TypeRepKey where
show (TypeRepKey (Fingerprint hi lo)) = printf "%016x%016x" hi lo
```
> A Binary instance is more difficult: where would it be put? It cannot be in Typeable.hs (as base cannot depend on binary), but it cannot be in Binary.hs either (as !TypeRepKey is opaque and not exposed from Typeable).
> I don't think it's a good idea to make TypeReps themselves publicly deserializable, as that would allow the creation of fake typereps. A separate one-way injection to a serializable !TypeRepKey seems much more secure.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | -------------- |
| Version | 7.2.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | libraries/base |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Add Show and Binary instances for TypeRep","status":"New","operating_system":"","component":"libraries/base","related":[],"milestone":"7.4.1","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"7.2.1","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"(was a re-opening of #3480; copied into a new ticket so we can leave #3480 dead and buried)\r\n\r\nUnfortunately the new !TypeRepKey type is abstract and provides no operations apart from Eq and Ord instances. Hence, there is still no way to write a type representation into a file or across the wire. Jush showing a !TypeRep won't cut it, since that loses the distinctions between similarly named types in different modules.\r\n\r\nSo, I think we need a Show instance for !TypeRepKey, or better yet, a Binary instance. Moreover, a non-deprecated pure function `TypeRep -> TypeRepKey` is in place, since the keys have an actual use as a compact, efficient, and serializable injection of the typereps.\r\n\r\nAlternatively, the Show instance of !TypeRep could be made injective by printing out the package and module.\r\n\r\n=== Commentary from #3480 ===\r\n\r\nsimonmar says:\r\n\r\n I've exposed the representation via the Data.Typeable.Internal module. Of course that's not guaranteed to be a stable API, but I'm happy to add a stable API if we can agree on what it should be. Lennart Augustsson and Neil Mitchell are doing exactly this (serialising !TypeRep) - I think they're using the Data.Typeable.Internal API right now.\r\n\r\n So what shall we do? \r\n\r\nLennart says:\r\n\r\n For older versions of ghc I just used (show . typeOf). When ghc 7.2 broke this I had to start using Internal, so now I use tyConModule and tyConName to emulate the behaviour of the old show. \r\n\r\nlealanko says:\r\n\r\n I'm sorry for messing up the ticket. I had assumed that the missing Show instance had been a simple oversight. If so, this would suffice:\r\n\r\n{{{\r\ninstance Show TypeRepKey where\r\n show (TypeRepKey (Fingerprint hi lo)) = printf \"%016x%016x\" hi lo\r\n}}}\r\n\r\n A Binary instance is more difficult: where would it be put? It cannot be in Typeable.hs (as base cannot depend on binary), but it cannot be in Binary.hs either (as !TypeRepKey is opaque and not exposed from Typeable).\r\n\r\n I don't think it's a good idea to make TypeReps themselves publicly deserializable, as that would allow the creation of fake typereps. A separate one-way injection to a serializable !TypeRepKey seems much more secure.\r\n","type_of_failure":"OtherFailure","blocking":[]} -->7.4.2https://gitlab.haskell.org/ghc/ghc/-/issues/5843hGetBufSome blocks when all available input is buffered (on Windows only)2019-07-07T18:53:21ZjoeyadamshGetBufSome blocks when all available input is buffered (on Windows only)See the attached program. On Windows, it blocks on hGetSome.
If I take out the hGetLine and hWaitForInput, hGetSome does not block. If I add \<code\>hSetBuffering h NoBuffering\</code\>, hGetBufSome still blocks after hWaitForInput beca...See the attached program. On Windows, it blocks on hGetSome.
If I take out the hGetLine and hWaitForInput, hGetSome does not block. If I add \<code\>hSetBuffering h NoBuffering\</code\>, hGetBufSome still blocks after hWaitForInput because it does some buffering when it calls hLookAhead_.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | -------------- |
| Version | 7.2.2 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | libraries/base |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"hGetBufSome blocks when all available input is buffered (on Windows only)","status":"New","operating_system":"","component":"libraries/base","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"7.2.2","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"See the attached program. On Windows, it blocks on hGetSome.\r\n\r\nIf I take out the hGetLine and hWaitForInput, hGetSome does not block. If I add <code>hSetBuffering h NoBuffering</code>, hGetBufSome still blocks after hWaitForInput because it does some buffering when it calls hLookAhead_.","type_of_failure":"OtherFailure","blocking":[]} -->7.4.2pcapriottipcapriottihttps://gitlab.haskell.org/ghc/ghc/-/issues/5870writeChan is not 100% exception-safe2019-07-07T18:53:11ZjoeyadamswriteChan is not 100% exception-safeControl.Concurrent.Chan.writeChan is currently implemented like this:
```
writeChan :: Chan a -> a -> IO ()
writeChan (Chan _ writeVar) val = do
new_hole <- newEmptyMVar
modifyMVar_ writeVar $ \old_hole -> do
putMVar old_hole (C...Control.Concurrent.Chan.writeChan is currently implemented like this:
```
writeChan :: Chan a -> a -> IO ()
writeChan (Chan _ writeVar) val = do
new_hole <- newEmptyMVar
modifyMVar_ writeVar $ \old_hole -> do
putMVar old_hole (ChItem val new_hole)
return new_hole
```
The problem is that modifyMVar_ restores the exception mask when it calls the callback. If an asynchronous exception is delivered immediately after the putMVar, the Chan's write end will be set to a filled hole, violating a key invariant.
I think it should be implemented like this:
```
writeChan :: Chan a -> a -> IO ()
writeChan (Chan _ writeVar) val = do
new_hole <- newEmptyMVar
mask_ $ do
old_hole <- takeMVar writeVar -- (1)
putMVar old_hole (ChItem val new_hole) -- (2)
putMVar writeVar new_hole -- (3)
```
This looks a bit naïve, but it should be atomic, at least in theory:
- (1) is interruptible. However, if it is interrupted, the MVar will remain intact.
- (2) is not interruptible, because old_hole is guaranteed to be empty. No other thread can fill it because we have taken exclusive access to writeVar.
- (3) is not interruptible, because we have taken exclusive access to writeVar.
This implementation has the added benefit of removing an exception handler, which should make it slightly faster.
readChan is okay, though:
```
readChan :: Chan a -> IO a
readChan (Chan readVar _) = do
modifyMVar readVar $ \read_end -> do
(ChItem val new_read_end) <- readMVar read_end
return (new_read_end, val)
```
If an exception is received immediately after the readMVar, then the read cursor will not be advanced, and the next call to readChan will simply read from that position again. Unfortunately, we can't get rid of the exception handler like we did with writeChan, because `readMVar read_end` is interruptible.
The attached patch changes the implementation of writeChan to the one shown above, and adds a comment to the definition of Chan about the write end's empty-hole invariant. I believe all of the Chan operations preserve this invariant, even the deprecated unGetChan and isEmptyChan.
I have not been able to create a test case where writeChan is interrupted between filling the hole and updating the write cursor. Perhaps it doesn't actually happen in practice, or perhaps it is extremely rare. But by my understanding of the semantics:
```
mask $ \restore -> do
thing1
restore $ putMVar ...
thing2
```
It is possible for an exception to arrive after the putMVar and before thing2.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | -------------- |
| Version | 7.4.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | libraries/base |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"writeChan is not 100% exception-safe","status":"New","operating_system":"","component":"libraries/base","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"7.4.1","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"Control.Concurrent.Chan.writeChan is currently implemented like this:\r\n\r\n{{{\r\nwriteChan :: Chan a -> a -> IO ()\r\nwriteChan (Chan _ writeVar) val = do\r\n new_hole <- newEmptyMVar\r\n modifyMVar_ writeVar $ \\old_hole -> do\r\n putMVar old_hole (ChItem val new_hole)\r\n return new_hole\r\n}}}\r\n\r\nThe problem is that modifyMVar_ restores the exception mask when it calls the callback. If an asynchronous exception is delivered immediately after the putMVar, the Chan's write end will be set to a filled hole, violating a key invariant.\r\n\r\nI think it should be implemented like this:\r\n\r\n{{{\r\nwriteChan :: Chan a -> a -> IO ()\r\nwriteChan (Chan _ writeVar) val = do\r\n new_hole <- newEmptyMVar\r\n mask_ $ do\r\n old_hole <- takeMVar writeVar -- (1)\r\n putMVar old_hole (ChItem val new_hole) -- (2)\r\n putMVar writeVar new_hole -- (3)\r\n}}}\r\n\r\nThis looks a bit naïve, but it should be atomic, at least in theory:\r\n\r\n * (1) is interruptible. However, if it is interrupted, the MVar will remain intact.\r\n\r\n * (2) is not interruptible, because old_hole is guaranteed to be empty. No other thread can fill it because we have taken exclusive access to writeVar.\r\n\r\n * (3) is not interruptible, because we have taken exclusive access to writeVar.\r\n\r\nThis implementation has the added benefit of removing an exception handler, which should make it slightly faster.\r\n\r\nreadChan is okay, though:\r\n\r\n{{{\r\nreadChan :: Chan a -> IO a\r\nreadChan (Chan readVar _) = do\r\n modifyMVar readVar $ \\read_end -> do\r\n (ChItem val new_read_end) <- readMVar read_end\r\n return (new_read_end, val)\r\n}}}\r\n\r\nIf an exception is received immediately after the readMVar, then the read cursor will not be advanced, and the next call to readChan will simply read from that position again. Unfortunately, we can't get rid of the exception handler like we did with writeChan, because {{{readMVar read_end}}} is interruptible.\r\n\r\nThe attached patch changes the implementation of writeChan to the one shown above, and adds a comment to the definition of Chan about the write end's empty-hole invariant. I believe all of the Chan operations preserve this invariant, even the deprecated unGetChan and isEmptyChan.\r\n\r\nI have not been able to create a test case where writeChan is interrupted between filling the hole and updating the write cursor. Perhaps it doesn't actually happen in practice, or perhaps it is extremely rare. But by my understanding of the semantics:\r\n\r\n{{{\r\nmask $ \\restore -> do\r\n thing1\r\n restore $ putMVar ...\r\n thing2\r\n}}}\r\n\r\nIt is possible for an exception to arrive after the putMVar and before thing2.","type_of_failure":"OtherFailure","blocking":[]} -->7.4.2Simon MarlowSimon Marlowhttps://gitlab.haskell.org/ghc/ghc/-/issues/5872bug in default implementation of popCount2019-07-07T18:53:11ZLevent Erkökbug in default implementation of popCountbase 4.5.0.0 (http://hackage.haskell.org/packages/archive/base/4.5.0.0/doc/html/src/Data-Bits.html\#popCount) gives the default implementation of popCount of the Bits class like this:
```
popCount :: a -> Int
popCount = go 0
...base 4.5.0.0 (http://hackage.haskell.org/packages/archive/base/4.5.0.0/doc/html/src/Data-Bits.html\#popCount) gives the default implementation of popCount of the Bits class like this:
```
popCount :: a -> Int
popCount = go 0
where
go !c 0 = c
go c w = go (c+1) (w .&. w - 1)
```
Alas, .&. binds more tightly than -, thus the last expression parses as `((w .&. w) - 1)` not as `(w .&. (w-1))`, as it was intended.
At the least, this causes `popCount :: Integer -> Int` to behave erratically:
```
Prelude Data.Bits> map popCount [2::Integer .. 4]
[2, 3, 4]
```
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | -------------- |
| Version | 7.4.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | libraries/base |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"bug in default implementation of popCount","status":"New","operating_system":"","component":"libraries/base","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"7.4.1","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"base 4.5.0.0 (http://hackage.haskell.org/packages/archive/base/4.5.0.0/doc/html/src/Data-Bits.html#popCount) gives the default implementation of popCount of the Bits class like this:\r\n\r\n{{{\r\npopCount :: a -> Int\r\npopCount = go 0\r\n where\r\n go !c 0 = c\r\n go c w = go (c+1) (w .&. w - 1)\r\n}}}\r\n\r\nAlas, .&. binds more tightly than -, thus the last expression parses as `((w .&. w) - 1)` not as `(w .&. (w-1))`, as it was intended. \r\n\r\nAt the least, this causes `popCount :: Integer -> Int` to behave erratically:\r\n\r\n{{{\r\nPrelude Data.Bits> map popCount [2::Integer .. 4]\r\n[2, 3, 4]\r\n}}}","type_of_failure":"OtherFailure","blocking":[]} -->7.4.2tibbetibbehttps://gitlab.haskell.org/ghc/ghc/-/issues/5947Small typo in Data.List.\\ documentation2019-07-07T18:52:53ZJoachim Breitnermail@joachim-breitner.deSmall typo in Data.List.\\ documentationReally just cosmetic, but still...
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | -------------- |
| Version | 7.4.1 |
| Type |...Really just cosmetic, but still...
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | -------------- |
| Version | 7.4.1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | libraries/base |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Small typo in Data.List.\\\\ documentation","status":"New","operating_system":"","component":"libraries/base","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"7.4.1","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"Really just cosmetic, but still...","type_of_failure":"OtherFailure","blocking":[]} -->7.4.2