Safe Haskell Safe Haskell is an extension to the Haskell language supported by GHC, that provides certain safety guarantees about Haskell code compiled using this extension. It allows people to build more advance security mechanisms on top of Haskell and for the safe execution of untrusted Haskell code. Its purpose isn't to provide a complete secure execution environment for Haskell code but to give users enough guarantees about the Haskell language to be able to build such systems. Its design is similar to the safe and unsafe module system supported by the Modula-3 language. The design of Safe Haskell covers the following aspects: A safe language dialect of Haskell that provides guarantees about the code. Mainly it allows the types and module boundaries to be trusted. A new safe import extension that specifies the module being imported must be trusted. A definition of trust (or safety) and how it operates, along with ways of defining and changing the trust of modules and packages. Safe Language Overview The Safe Haskell Safe language guarantees the following properties: Referential transparency. Functions in the Safe language are deterministic, evaluating them will not cause any side effects. Functions in the IO monad are still allowed and behave as usual but any pure function as according to the functions type is guaranteed to indeed be pure. This property allows a user of the Safe language to trust the types of functions. Module boundary control. Haskell code compiled using the Safe language is guaranteed to only access symbols that are publicly available to it through other modules export lists. An import part of this is that safe compiled code is not able to examine or create data values using data constructors that the module cannot import. If a module M establishes some invariants through careful use of its export list then code compiled using the Safe language that imports M is guaranteed to respect those invariants. Semantic consistency. The Safe language is strictly a subset of Haskell as implemented by GHC. Any expression that compiles in the safe language has the same meaning as it does when compiled in normal Haskell. In addition, in any module that imports a Safe language module, expressions that compile both with and without the safe import have the same meaning in both cases. That is, importing a module using the Safe language cannot change the meaning of existing code that isn't dependent on that module. Put simply, these three properties guarantee that you can trust the types in the Safe language, can trust module export lists are respected in the Safe language and that code which successfully compiles in the Safe language has the same meaning as it normally would. Please see for a more detailed view of the safe language. Safe Imports Safe Haskell enables a small extension to the usual import syntax of Haskell, adding a safe keyword: impdecl -> import [safe] [qualified] modid [as modid] [impspec] When used, the module being imported with the safe keyword must be a trusted module, otherwise a compilation error will occur. The safe import extension is enabled by either of the -XSafe, -XTrustworthy, -XSafeLanguage or -XSafeImports flags and corresponding PRAGMA's. When either the -XSafe or -XSafeLanguage flag is used, all imports are assumed to be safe imports. Trust The Safe Haskell extension introduces the following two new language flags: -XSafe: Enables the Safe language dialect, asking GHC to guarantee trust. The safe language dialect requires that all imports be trusted or a compile error will occur. -XTrustworthy: Means that while this module may invoke unsafe functions internally, the module's author claims that it exports an API that can't be used in an unsafe way. This doesn't enable the Safe language or place any restrictions on the allowed Haskell code. The trust guarantee is provided by the module author, not GHC. Modules imported with the safe import feature are required to be trusted but otherwise unsafe modules can be imported as usual. The definition of trust for a module M, residing in a package P for a user of GHC, a client C (someone who is compiling a source module with GHC, aka you!), is defined as follows: A package P is trusted by client C if and only if one of these conditions hold: C's package database records that P is trusted (and no command-line arguments override this). C's command-line flags say to trust it regardless of the what is recorded in the package database. It is important to note that C is the only authority on package trust. It is up to the client to decide which packages they trust. A module M is trusted by a client C if and only if: Both of these hold: The module was compiled with -XSafe All of M's direct imports are trusted by C OR all of these hold: The module was compiled with -XTrustworthy All of M's direct safe imports are trusted by C Package P is trusted by C For the first trust definition the trust guarantee is provided by GHC through the restrictions imposed by the Safe language. For the second definition of trust, the guarantee is provided initially by the module author. The client C then establishes that they trust the module author by indicating they trust the package the module resides in. This trust chain is required as GHC provides no guarantee for -XTrustworthy compiled modules. Example Package Wuggle: {-# LANGUAGE Safe #-} module Buggle where import Prelude f x = ...blah... Package P: {-# LANGUAGE Trustworthy #-} module M where import System.IO.Unsafe import safe Buggle Suppose a client C decides to trust package P. Then does C trust module M? To decide, GHC must check M's imports: M imports System.IO.Unsafe. M was compiled with -XTrustworthy, so P's author takes responsibility for that import. C trusts P's author, so C trusts M. M has a safe import of Buggle, so P's author takes no responsibility for the safety or otherwise of Buggle. So C must check whether Buggle is trusted by C. Is it? Well, it is compiled with -XSafe, so the code in Buggle itself is machine-checked to be OK, but again under the assumption that Buggle's imports are trusted by C. Prelude comes from base, which C trusts, and is compiled with -XTrustworthy. Notice that C didn't need to trust package Wuggle; the machine checking is enough. C only needs to trust packages that have -XTrustworthy modules in them. Safe Language & Imports without Trust Safe Haskell also allows the new language extensions -- the Safe language dialect and safe imports -- to be used independtly of any trust assertions for the code. -XSafeImports: enables the safe import extension. The module using this feature is left untrusted though. -XSafeLanguage: enables the safe language extension. The module using this feature is left untrusted though. These are extensions are useful for encouraging good programming style and also for flexibility during development when using Safe Haskell. The Safe language encourages users to avoid liberal use of unsafe Haskell language features. There are also situations where a module may only use the Safe language subset but exposes some internal API's that code using -XSafe shouldn't be allowed to access for security reasons. Please see Safe Haskell use cases for a more detailed explanation. Safe Haskell Flag Summary In summary, Safe Haskell consists of the following language flags: -XSafe To be trusted, all of the module's direct imports must be trusted, but the module itself need not reside in a trusted package, because the compiler vouches for its trustworthiness. The "safe" keyword is allowed but meaningless in import statements -- conceptually every import is safe whether or not so tagged. Module Trusted: Yes Haskell Language: Restricted to Safe Language Imported Modules: All forced to be safe imports, all must be trusted. -XSafeLanguage: The module is never trusted, because the author does not claim it is trustworthy. As long as the module compiles both ways, the result is identical whether or not the -XSafeLanguage flag is supplied. As with -XSafe, the "safe" import keyword is allowed but meaningless -- all imports must be safe. Module Trusted: No Haskell Language: Restricted to Safe Language Imported Modules: All forced to be safe imports, all must be trusted. -XTrustworthy: This establishes that the module is trusted, but the guarantee is provided by the module's author. A client of this module then specifies that they trust the module author by specifying they trust the package containing the module. '-XTrustworthy' has no effect on the accepted range of Haskell programs or their semantics. Module Trusted: Yes but only if Package the module resides in is also trusted. Haskell Language: Unrestricted Imported Modules: Under control of module author which ones must be trusted. -XSafeLanguage -XTrustworthy: For the trust property this has the same effect as '-XTrustworthy' by itself. However unlike -XTrustworthy it also restricts the range of acceptable Haskell programs to the Safe language. The difference from this and using -XSafe is the different trust type and that not all imports are forced to be safe imports, they are instead optionally specified by the module author. Module Trusted: Yes but only if Package the module resides in is also trusted. Haskell Language: Restricted to Safe Language Imported Modules: Under control of module author which ones must be trusted. -XSafeImport: Enable the Safe Import extension so that a module can require a dependency to be trusted without asserting any trust about itself. Module Trusted: No Haskell Language: Unrestricted Imported Modules: Under control of module author which ones must be trusted. Package Trust Safe Haskell gives packages a new boolean property, that of trust. Several new options are available at the GHC command-line to specify the trust property of packages: -trust P: Exposes package P if it was hidden and considers it a trusted package regardless of the package database. -distrust P: Exposes package P if it was hidden and considers it a untrusted package regardless of the package database. -distrust-all-packages: Considers all packages distrusted unless they are explicitly set to be trusted by subsequent command-line options. To set a package's trust property in the package database please refer to . Safe Language Details In the Safe language dialect we disable completely the following Haskell language features: GeneralizedNewtypeDeriving: It can be used to violate constructor access control, by allowing untrusted code to manipulate protected data types in ways the data type author did not intend. For example can be used to break invariants of data structures. TemplateHaskell: Is particularly dangerous, as it can cause side effects even at compilation time and can be used to access abstract data types. It is very easy to break module boundaries with TH. In the Safe language dialect we restrict the following Haskell language features: ForeignFunctionInterface: This is mostly safe, but foreign import declarations that import a function with a non-IO type are disallowed. All FFI imports must reside in the IO Monad. RULES: As they can change the behaviour of trusted code in unanticipated ways, violating semantic consistency they are restricted in function. Specifically any RULES defined in a module M compiled with -XSafe or -XSafeLanguage are dropped. RULES defined in trustworthy modules that M imports are still valid and will fire as usual. OverlappingInstances: This extension can be used to violate semantic consistency, because malicious code could redefine a type instance (by containing a more specific instance definition) in a way that changes the behaviour of code importing the untrusted module. The extension is not disabled for a module M compiled with -XSafe or -XSafeLanguage but restricted. While M can defined overlapping instance declarations, they can only be used in M. If in a module N that imports M, at a call site that uses a type-class function there is a choice of which instance to use (i.e overlapping) and the most specific choice is from M (or any other Safe compiled module), then compilation will fail. It is irrelevant if module N is considered Safe, or Trustworthy or neither. Use Cases Safe Haskell has been designed with the following use cases in mind. Enforcing Good Programming Style Over-reliance on magic functions such as unsafePerformIO or magic symbols such as #realWorld can lead to less elegant Haskell code. The Safe dialect formalizes this notion of magic and prohibits its use. Thus, people may encourage their collaborators to use the Safe dialect, except when truly necessary, so as to promote better programming style. It can be thought of as an addition to using . Building Secure Systems (restricted IO Monads) The original use case that Safe Haskell was designed for was to allow secure systems to be built on top of the Haskell programming language. Many researchers have done great work with Haskell, building such systems as information flow control security systems, capability based security system, languages for working with encrypted data... etc. These systems all rely on properties of the Haskell language that aren't true in the general case where uses of functions like unsafePerformIO are allowed. Safe Haskell however gives enough guarantees about the compiled Haskell code to be able to successfully build secure systems on top of. As an example lets define an interface for a plugin system where the plugin authors are untrusted, possibly malicious third-parties. We do this by restricting the interface to pure functions or to a restricted IO monad that we have defined that only allows a safe subset of IO actions to be executed. We define the plugin interface here so that it requires the plugin module, Danger, to export a single computation, Danger.runMe, of type RIO (), where RIO is a new monad defined as follows: -- Either of the following pragmas would do {-# LANGUAGE Trustworthy #-} {-# LANGUAGE Safe #-} module RIO (RIO(), runRIO, rioReadFile, rioWriteFile) where -- Notice that symbol UnsafeRIO is not exported from this module! newtype RIO a = UnsafeRIO { runRIO :: IO a } instance Monad RIO where return = UnsafeRIO . return (UnsafeRIO m) >>= k = UnsafeRIO $ m >>= runRIO . k -- Returns True iff access is allowed to file name pathOK :: FilePath -> IO Bool pathOK file = {- Implement some policy based on file name -} 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 () We compile Danger using the -XSafe flag. Danger can import module RIO because RIO is marked Trustworthy. Thus, Danger can make use of the rioReadFile and rioWriteFile functions to access permitted file names. The main application then imports both RIO and Danger. To run the plugin, it calls RIO.runRIO Danger.runMe within the IO monad. The application is safe in the knowledge that the only IO to ensue will be to files whose paths were approved by the pathOK test. We are relying on the fact that the type system and constructor privacy prevent RIO computations from executing IO actions directly. Only functions with access to privileged symbol UnsafeRIO can lift IO computations into the RIO monad. Uses of -XSafeImports If you are writing a module and want to import a module from an untrusted author, then you would use the following syntax: import safe Untrusted.Module As the safe import keyword is a feature of Safe Haskell and not Haskell98 this would fail though unless you enabled Safe imports through on the of the Safe Haskell language flags. Three flags enable safe imports, -XSafe, -XTrustworthy and -XSafeImports. However -XSafe and -XTrustworthy do more then just enable the keyword which may be undesirable. Using the -XSafeImports language flag allows you to enable safe imports and nothing more. Uses of -XSafeLanguage The -XSafeLanguage flag has two use cases. Firstly as stated above it can be used to enforce good programming style. Secondly, in the RIO restricted IO monad example above there is no reason that it can't be implemented in the Safe Language as its code isn't reliant on any unsafe features of Haskell. However we may also wish to export the UnsafeRIO action in the defining module or RIO and then define a new module that only exports a safe subset of the original definition of RIO. The defining module can use the -XSafeLanguage flag and be assured that the untrusted Danger module can't import it.