Version 18 (modified by David, 5 years ago) (diff)


Safe Haskell



The proposal addresses security in the following scenario.

  • The user constructing A must trust:
    • GHC, its supporting tools, and
    • Any Haskell modules of A compiled without -XSafe.
  • The user does not trust M, which is why it compiles M with -XSafe.

More specifically, there are two parts to the proposed extension:

  1. A new GHC option (-XSafe) enabling a "Safe" dialect of Haskell in which GHC rejects any source code that might produce unsafe effects or otherwise subvert the type system.
  1. Another new GHC option (-XTrustworthy) indicating that, though a module may invoke unsafe functions internally, the set of exported symbols cannot be used in an unsafe way. The -XTrustworthy option makes a small extension to the syntax of import statements, adding a safe keyword:


Roughly speaking, import declarations are either safe or unsafe, while modules are classified as either trusted or untrusted. A safe import declaration causes compilation to fail if the imported module is not trusted. In the Safe dialect, all import declarations are implicitly safe. When compiling with -XTrustworthy, import declarations with the safe keyword are safe, while those without the keyword are unsafe. A module can be trusted only if it was compiled with -XSafe or -XTrustworthy, but there are additional restrictions as discussed below under module trust.

Safety Goal

As long as no module compiled with -XTrustworthy contains a vulnerability, the Safe dialect's goal is to guarantee the following properties:


The Safe dialect is intended to be of use for both normal (trusted) and untrusted code. Authors of trusted modules may wish to include {-# LANGUAGE Safe #-} pragmas to ensure they do not accidentally invoke unsafe actions (directly or indirectly), or to allow other Safe code to import their modules.

The safe dialect does not prevent use of the symbol IO. If an untrusted module exports an IO action, that action may have arbitrary side effects, regardless of the -XSafe option. Hence, while an application A importing a safe but possibly malicious module M may safely invoke pure functions from M, it must avoid executing IO actions exported by M unless some other mechanism ensures those actions conform to A's security goal. (See -ultrasafe below for one such mechanism.)

Module trust

Recall that a safe import of a module M fails unless M is trusted. M is trusted when two conditions hold:

  1. M was compiled with -XSafe or -XTrustWorthy, in which case we say M is trustable, and
  1. The modules on which M depends (transitively) for safety all reside in packages that the application trusts.

Determining trust requires two modifications to the way GHC manages modules. First, the interface file format must change to record whether each module is trustable, and if so what its trust dependencies are. Second, we need compiler options to specify which packages are trusted by an application.

We therefore extend the interface file format to record for each module M whether or not M is trustable. If M is trustable, the interface file contains a list of trust dependencies, which are (package, module) pairs on which the current application depends for safety. The trust dependencies of a module are determined as follows:

  • If a module is compiled with -XTrustworthy, then its trust dependencies always include itself, plus the union of trust dependencies of all modules imported with the safe keyword.
  • If a module is compiled with -XSafe but not -XTrustworthy, then its trust dependencies are the union of trust dependencies of all modules it imports.
  • If a module was compiled with neither -XSafe nor -XTrustworthy, or, regardless of these options, if a module contains a {-# LANGUAGE Untrustworthy #-} pragma, then the interface file marks the module as not trustable, so there are no trust dependencies.



  • A convenience option -ultrasafe is equivalent to -distrust-all-packages -XNoForeignFunctionInterface -XNoImplicitPrelude -XSafe.

None of these options can be specified or overwritten by OPTIONS_GHC pragmas in the Safe dialect.



  • A number of functions can be used to violate safety. Many of these have names prefixed with unsafe (e.g., unsafePerformIO, unsafeIterleaveIO, unsafeCoerce unsafeSTToIO, ...). However, not all unsafe functions fit this pattern. For instance, inlinePerformIO and fromForeignPtr from the bytestring package are unsafe.

Implementation details

  • The -XSafe option must be processed last after all other options. If previous options conflict with -XSafe, they must be overwritten or compilation must fail. Alternatively, maybe -XSafe and {-# LANGUAGE Safe #-) should always be processed before all other pragmas, especially LANGUAGE and OPTIONS_GHC, and any subsequent dangerous pragmas should cause compilation to fail.
  • When -XSafe and -XTrustworthy are used together, the language is restricted to the Safe dialect. The effect of -XTrustworthy is to change the trust dependency set. Specifically, the trust dependency set will include the module itself. However, rather than include the union of trust dependency sets of all imported modules, only dependencies of modules imported with the safe keyword are added to the current module's set. A plausible use for both pragmas simultaneously is to prune the list of trusted modules--for instance, if a module imports a bunch of trusted modules but does not use any of their trusted features, or only uses those features in a very limited way. If the code happens also to be safe, the programmer may want to add -XSafe to catch accidental unsafe actions.
  • The option {-# LANGUAGE Untrustworthy -#} is also not incompatible with {-# LANGUAGE Safe -#}. The former causes the interface file to be marked not trustable, while the latter causes the source code to be confined to the Safe dialect. Untrustworthy should be used in seemingly safe modules that export constructors that would allow other modules to do unsafe things. (The PS constructor discussed above is an example of a dangerous constructor that could potentially be defined in a module that happily compiles with -XSafe.)

Intended uses


Enforcing good programming style


Restricted IO monads



-- Either or both of the following pragmas would do
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE Safe #-}



newtype RIO a = UnsafeRIO { runRIO :: IO a }

instance Monad RIO where
    return = UnsafeRIO . return

-- Returns True iff access is allowed to file name
pathOK :: FilePath -> IO Bool

rioReadFile :: FilePath -> RIO String
rioReadFile file = UnsafeRIO $ do
  ok <- pathOK file
  if ok then readFile file else return ""

rioWriteFile :: FilePath -> String -> RIO ()
rioWriteFile file contents = UnsafeRIO $ do
  ok <- pathOK file
  if ok then writeFile file contents else return ()




Restricted IO imports


{-# LANGUAGE Trustworthy #-}

module SafeIO (rioReadFile, rioWriteFile
              , module RestrictedPrelude) where

import RestrictedPrelude -- Subset of Prelude without IO actions

-- Returns True iff access is allowed to file name
pathOK :: FilePath -> IO Bool

rioReadFile :: FilePath -> IO String
rioReadFile file = do
  ok <- pathOK file
  if ok then readFile file else return ""

rioWriteFile :: FilePath -> String -> IO ()
rioWriteFile file contents = do
  ok <- pathOK file
  if ok then writeFile file contents else return ()



The following links are to discussions of similar topics: