Clever programs violate GHC assumptions about representation
Consider this program:
$ cat Blargh.hs
{-# LANGUAGE KindSignatures, RankNTypes, GADTs, MagicHash #-}
module Main where
import GHC.Exts
import GHC.IO
{-# NOINLINE foo #-}
foo :: forall (f :: * -> !). f RealWorld -> Int -> Int
foo x y = case x of _ -> y + 10
main :: IO ()
main = IO $ \s -> case print (foo s 10) of IO f -> f s
When compiled run it produces this result:
$ ./Blargh
-5116089176676103435
It can easily be modified to provoke a segfault instead.
The problem here is that when generating the code for foo, Type.typePrimRep
assumes that any TyApp
has Ptr
representation and so expects that argument to be a word wide.
Unfortunately, the State# TyCon
has kind (* -> #)
and is represented by VoidRep
, so we can make the call site of foo believe that the argument to foo is of width 0... havoc ensues.
One way to fix this is to encode representation in the kind hierarchy, so we have WordRep
, IntRep
, VoidRep
all subkinds of #. Then we can know the representation of types without relying on deconstructing them.