Changes between Version 32 and Version 33 of SafeHaskell

Show
Ignore:
Timestamp:
01/20/11 16:21:44 (2 years ago)
Author:
dterei
Comment:

Big update, tried to reword to sepperate implementation out also includes recent ideas from discussion between me and David M.

Legend:

Unmodified
Added
Removed
Modified
  • SafeHaskell

    v32 v33  
    22 
    33This is a proposal for a Haskell extension through which people can safely execute untrusted Haskell code, much the way web browsers currently run untrusted Java and !JavaScript, or the way the Spin and Singularity operating systems ran untrusted Modula-3 and C#/Sing#. 
     4 
    45 
    56== Setup == 
     
    1516 * The user does not trust M, which is why he or she compiles M with `-XSafe`. 
    1617 
    17 == Safety Goal == 
    18  
    19 As long as no module compiled with `-XTrustworthy` contains a vulnerability, the goal of the Safe dialect (i.e., code compiled with `-XSafe`) is to guarantee the following properties: 
     18 
     19== Design == 
     20 
     21The design of Safe Haskell involves the following aspects: 
     22 
     23 * A safe language dialect of Haskell (as an extension) that provides certain guarantees about the code. Mainly it allows the types to be trusted. 
     24 * A new "safe import" extension to Haskell that specifies the module being imported must be trusted. 
     25 * A definition of "trust" and how it operates, as well as ways of defining and changing the trust of modules and packages. 
     26 
     27 
     28== Safe Language == 
     29 
     30The goal of the Safe dialect is to guarantee the following properties: 
    2031 
    2132 * '''Referential transparency.'''  Functions in the Safe dialect must be deterministic.  Moreover, evaluating them should have no side effects, and should not halt the program (except by throwing uncaught exceptions or looping forever). 
     
    2536 * '''Semantic consistency.'''  Any expression that compiles both with and without the import of a Safe module must have the same meaning in both cases.  (E.g., {{{1 + 1 == 3}}} must remain `False` when you add the import of a Safe module.) 
    2637 
    27 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. 
    28  
    29  
    30 == Language extension == 
    31  
    32 There are two parts to the proposed extension: 
    33  
    34  1. Two new GHC LANGUAGE options, `-XSafe` and `-XTrustworthy`.  Intuitively 
    35     * `-XSafe` enables a "Safe" dialect of Haskell in which GHC rejects any source code that might produce unsafe effects or otherwise subvert the type system. 
    36     * `-XTrustworthy` means that, though a module may invoke unsafe functions internally, the module's author claims that the set of exported symbols cannot be used in an unsafe way.  (There is a corresponding `-XUntrustworthy` option to enable the language extension but negate `-XTrustworthy`.  '''SLPJ: don't understand''') 
    37  
    38  2. A small extension to the syntax of import statements (enabled by `-XSafe` or `-XTrustworhty`), adding a `safe` keyword: 
    39    
    40   impdecl -> `import` [`safe`] [`qualified`] modid [`as` modid] [impspec] 
    41  
    42 The LANGUAGE extensions have the following effect.  When a client C compiles a module M: 
    43    * Under `-XSafe` several potentially-unsafe language features, listed under "Threats" below, are disabled. 
    44    * Under `-XSafe`, all M's `imports` must be trusted by C 
    45    * Under `-XTrustworthy` or `-XUntrustworthy` (but not `-XSafe`) all M's `safe imports` must be trusted by C 
    46  
    47 What does it mean for a module to be "trusted by C"?  Here is the definition: 
     38The Safe dialect is intended to be of use for both trusted and untrusted code. It can be used for trusted code as a way to enforce good programming style. It is also useful on untrusted code to allow that code to be trusted. Please keep in mind though that the issue of trust is at a higher level than the safe dialect. Using the safe dialect doesn't automatically imply trust, trust is defined separately below. 
     39 
     40The safe dialect basically disallows some dangerous features in Haskell to guarantee the above property, as well as checking that the direct dependencies of a module are trusted. 
     41 
     42 
     43== Safe Imports == 
     44 
     45A small extension to the syntax of import statements, adding a `safe` keyword: 
     46 
     47impdecl -> `import` [`safe`] [`qualified`] modid [`as` modid] [impspec] 
     48 
     49When enabled, a module imported with the safe keyword must be a trusted module, otherwise a compilation error will result. Safe imports can be enabled by themselves but are automatically enabled as part of the safe language dialect where all imports are considered safe imports. 
     50 
     51 
     52== Trust == 
     53 
     54Trust can be thought of simply as a boolean property that applies both to packages and to modules, it is defined as: 
    4855 
    4956 * A '''client''' is someone running GHC, typically the person compiling the application. 
    5057 
    51  * A '''package P is trusted by a client C''' iff one of these conditions holds 
    52      * C's package database records that P is trusted (and command-line arguments do not override the database) 
    53      * C's command-line flags say to trust it regardless of the database (see `-trust`, `-distrust` below)[[BR]] 
    54  It is up to C to decide what packages to trust; it is not a property of P. 
    55  
    56  * A '''module M from package P is trusted by a client C''' iff  
    57    * Both of these hold: 
    58      * The module was compiled with `-XSafe` and without `-XUntrustworthy` 
    59      * All of M's direct `imports` are trusted by C 
    60    * OR all of these hold: 
    61      * The module was compiled with `-XTrustworthy` 
    62      * All of M's direct `safe imports` are trusted by C 
    63      * Package P is trusted by C 
    64  
    65  
    66  
    67 The intuition is this.  The '''author''' of a package undertakes the following obligations: 
    68    * When the author of code compiles it with `-XSafe`, he asks the compiler to check that it is indeed safe.  He takes on no responsibility himself.  Although he must trust imported packages in order to compile his package, he takes not responsibility for them. 
    69    * When the author of code compiles it with `-XTrustworthy` he takes on responsibility for the stafety of that code, ''under the assumption'' that `safe imports` are indeed safe. 
    70  
    71 When a '''client''' C trusts package P, he expresses trust in the author of that code.  But since the author makes no guarantees about `safe imports`, C may need to chase dependencies to decide which modules in P should be trusted by C. 
    72  
    73 For example, suppose we have this setup: 
    74 {{{ 
    75 Package Wuggle: 
    76    {-# LANGUAGE Safe #-} 
    77    module Buggle where 
    78      import Prelude 
    79      f x = ...blah... 
    80       
    81 Package P: 
    82    {-# LANGUAGE Trustworthy #-} 
    83    module M where 
    84      import System.IO.Unsafe 
    85      import safe Buggle 
    86 }}} 
    87 Suppose client C decides to trust package P.  Then does C trust module M?  To decide, C must check M's imports: 
    88  * 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. 
    89  * 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.  Ah, but `Prelude` comes from `base`, which C trusts, and is (let's say) compiled with `-XTrustworthy`. 
    90  
    91 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. 
    92  
    93 === Command line options === 
     58 * A '''package P is trusted by the client C''' if decided so by the C. 
     59 
     60 * A '''module M is trusted by the client C''' if either: 
     61   1. M is guaranteed by the compiler (ghc) to be `safe`. 
     62      * (This is done by using the safe language extension for M without any compromises). 
     63      * All of M's direct dependencies must be trusted (all imports are safe imports). 
     64      * M can reside in any package P, regardless of if P is trusted or not. 
     65   2. '''OR''': M is specified by the client C to be `safe`. 
     66      * M can use all of Haskell. 
     67      * Only M's direct dependencies imported with the safe keyword need to be trusted. 
     68      * M must reside in a trusted package P 
     69 
     70The difference is basically for trust type 1, the trust is provided by ghc while for trust type 2 the trust is provided by the client C. Trust 1 should be used for code that C doesn't trust (e.g provided by unknown 3rd party) while type 2 should only be used for code C does trust (his/her own code or code provided by known, trusted 3rd party). 
     71 
     72 
     73== User Interface for Safe Haskell == 
     74 
     75Now that the high level goals and definitions have been established we discuss how these are exposed to the Haskell user. 
     76 
     77 
     78=== Safe Language & Imports (Module Trust) === 
     79 
     80We propose two new GHC Options that can be set in the usual way either as a `{-# LANGUAGE #-}` pragma or on the command line. 
     81 
     82 * `-XSafe` enables the safe dialect of Haskell and ask ghc to guarantee safety. If a module M using `-XSafe` compiles successfully then it is trustable. This corresponds to trust type 1. 
     83 * `-XTrustworthy` enables only the safe import extension and requires the client C guarantee safety. If a module using '-XTrustworthy' compiles successfully then it is trustable. This corresponds to trust type 2. 
     84 
     85We also want to be able to enable the safe dialect and safe import extensions without any corresponding trust assertion for the code: 
     86 
     87 * `-XSafeImports` ('''previously''' `-XUntrustworthy`) enables the safe import extension. Module M is left untrusted though. 
     88 * `-XSafeLanguage` ('''previously''' `-XUntrustworthy` `-XSafe`) enables the safe language (and therefore safe imports). Module M is left untrusted though. 
     89 
     90We see these being used both for good coding style and more flexibility during development of trusted code. We have this relation between the flags: 
     91 
     92 * `-XSafeLanguage` implies `-XSafeImports`. 
     93 * `-XTrustworthy` implies `-XSafeImports` and establishes Trust guaranteed by client C. 
     94 * `-XSafe` implies `-XSafeLanguage` and establishes Trust guaranteed by GHC. 
     95 
     96 
     97=== Specifying Package Trust  === 
    9498 
    9599On the command line, several new options control which packages are trusted: 
     
    103107 * A convenience option `-ultrasafe` which is equivalent to specifying {{{-distrust-all-packages -XNoForeignFunctionInterface -XNoImplicitPrelude}}} at the point of the `-ultrasafe` option, and `-XSafe` at the end of the command line. 
    104108 
     109 
    105110=== Order of options === 
    106111 
    107 Safety-critical options must not be specified or overwritten by `LANGUAGE` and `OPTIONS_GHC` pragmas in the Safe dialect.  To avoid such surprises, certain options and pragmas are '''restricted''', meaning they can only be supplied before the safe dialect is enabled. The order of options is considered to be:  first, all command-line options in the order they appear on the command line, second, all `LANGUAGE` and `OPTIONS_GHC` pragmas, in the order they appear in the module source.  Thus, the `-XSafe` command-line option disallows all restricted pragmas, but, in the absence of `-XSafe` on the command line, `{-# LANGUAGE Safe #-}` may appear below restricted pragmas in the source, just not above them. 
    108  
    109 At least the following options (and their pragma equivalents) are restricted: 
    110  
    111  - `-XTrustworthy` - Rationale:  Trusted packages may wish to include untrusted code compiled with `-XSafe`.  Yet once `-XSafe` is applied, the module must not be able to prune its trust dependency set, which it could with `{-# Trustworthy #-}`. 
    112  - `-XUntrustworthy` - Rationale: Unless -XUntrustworthy is applied first, if compilation does not fail, then `-XSafe` should produce code that can be trusted with the specified set of trusted packages. 
    113  - `-XForeignFunctionInterface` - Rationale: Trustworthy code in the Safe dialect may wish to have foreign import declarations, but modules from untrusted sources do not need this feature.  Thus, `-XSafe` on the command line should disable `{-# ForeignFunctionInterface #-}` pragmas. 
    114  - `-trust` - it is not safe to increase the set of trusted packages. 
    115  - `-package`, `-package-id`, `-package-conf`, `-no-user-package-conf` - untrusted code should not have access to explicitly hidden packages. 
    116  - `-package-name` - package name should be set only by trusted user 
    117  - `-F`, `-cpp`, `-XCPP` - these options allow access to the local file system. 
    118  - All of the linking options should be restricted (`-main-is`, `-l`__lib__, `-L`__dir__, `-framework`, etc.) 
    119  - Several other options discussed below: `-XTemplateHaskell`, `-XStandaloneDeriving`, `-XGeneralizedNewtypeDeriving`. 
    120  - The `RULES` and `SPECIALIZE` pragmas are also restricted and cannot appear below `{-# LANGUAGE Safe #-}` or when the `-XSafe` option has been specified on the command line. 
    121  
    122 Note that `-ultrasafe` only enables Safe mode at the end of the command-line.  Thus, one can supply one or more `-trust` options after `-ultrasafe` to allow ultrasafe code to do I/O. 
    123  
    124 == Threats == 
     112We must decide on if and how the order of various `LANGUAGE` and `OPTIONS_GHC` pragmas interact with `-XSafe`, `-XTrustworthy`, `-XSafeImports` and `-XSafeLanguage`. The order of options is considered to be:  first, all command-line options in the order they appear on the command line, second, all `LANGUAGE` and `OPTIONS_GHC` pragmas, in the order they appear in the module source. We are only really concerned with dynamic options here as static options are under the complete control of the client C. 
     113 
     114 * '''`-XSafe`''' disables some options and extensions completely while for others it restricts them to appearing before `-XSafe` does. ('''Note:''' Incomplete) 
     115   * '''Must appear before''': `TemplateHaskell`, `-cpp`, `-pgm{L,P,lo,lc,m,s,a,l,dll,F,windres}`, `-opt{L,P,lo,lc,m,s,a,l,dll,F,windres}`, `-F`, `-l''lib''`, `-framework`, `-L''dir''`, `-framework-path''dir''`, `-main-is`, `-package-name` 
     116   * '''Can't appear''': `StandaloneDeriving`, `GeneralizedNewtypeDeriving`, {{{RULES}}}, {{{SPECIALIZE}}}, `-fglasgow-exts` (or should we allow but just not have it enabled restricted options?), `OverlappingInstances` (restricted anyway, see threats below), ` 
     117   * '''Doesn't matter''': `-v`, `-vn`, `-fasm`, `-fllvm`, `-fvia-C`, `-fno-code`, `-fobject-code`, `-fbyte-code`, `-c`, `-split-objs`, `-shared`, `-hcsuf`, `-hidir`, `-o`, `-odir`, `-ohi`, `-osuf`, `-stubdir`, `-outputdir`, `-keep-*`, `-tmpdir`, `-ddump-*`, `-fforce-recomp`, `-no-auto-link-packages` 
     118   * '''Not Sure''': `-D''symbol''`, `-U''symbol''`, `-I''dir''`,  
     119 
     120 * '''`-XTrustworthy`''' is incompatible with `-XSafe` since both are about different trust types. If both appear then it is considered an error. Otherwise `-XTrustworthy` has no special interactions. 
     121   * If `-XSafeImports` appears nothing changes since `-XTrustworthy` implies `-XSafeImports`. 
     122   * If `-XSafeLanguage` appears then the module is restricted to the safe language dialect but trust is still guaranteed by the client C and all imports aren't required to be safe. 
     123 
     124 * '''`-XSafeImports`''' has no special interactions. `-XSafe`, `-XTrustworthy` and `-XSafeLanguage` are all compatible with it as they all imply `-XSafeImports` anyway. 
     125 
     126 * '''`-XSafeLanguage`''' not sure yet. Do we want `-XSafeLanguage` to be as restrictive as `-XSafe` or should it allow {{{RULES}}} and `StandaloneDeriving`? I would lean towards it being as restrictive as `-XSafe` to limit the complexity of the overall !SafeHaskell proposal. 
     127 
     128 
     129== Safe Language Threats == 
    125130 
    126131'''SLPJ note''': we should enumerate precisely what is and is not allowed with `-XSafe`.  '''End of note''' 
     
    150155 * Similarly, {{{GeneralizedNewtypeDeriving}}} can violate constructor access control, by allowing untrusted code to manipulate protected data types in ways the data type author did not intend. 
    151156 
     157 
    152158== Implementation details == 
    153159 
    154 -------------- 
    155 '''SLPJ note''' I am uncertain whether these implementation notes are correct. We need to revisit them in the light of our new definitions. 
    156  
    157 Determining trust requires two modifications to the way GHC manages modules.  First, the interface file format must change to record each module's trust dependency set.  Second, we need compiler options to specify which packages are trusted by an application. 
    158  
    159 We therefore extend the interface file format to record the trust dependency set of each module.  The set is represented as a list of ''trust dependencies'', each of which is a (package, module) pair. 
     160Determining trust requires two modifications to the way GHC manages modules.  First, the interface file format must change to record each module's trust type (Safe, Trustworthy, Untrusted).  Second, we need compiler options to specify which packages are trusted by an application. 
    160161 
    161162Currently, in any given run of the compiler, GHC classifies each package as either exposed or hidden.  To incorporate trust, we add a second bit specifying whether each package is trusted or untrusted. This bit will be controllable by two new options to `ghc-pkg`, `trust` and `distrust`, which are analogous to `expose` and `hide`. 
    162163 
    163 -------------- 
     164 * We want to be able to change a package P from trusted to untrusted and then have compilation of code that directly or transversely depends on it to fail accordingly if it relies on that package being trusted. That is trust should be checked recursively at link time and not just for code being compiled. Having the interface file format record each modules trust type should be enough for this. 
     165   * If a module M is Untrusted then no further processing needs to be done. 
     166   * If a module M is Safe then we know all imports must be safe or trustworthy so we must check them. 
     167   * If a module M is Trustworthy then we handle it differently when linking than compiling: 
     168     * At both link time and compile time M itself must be in a trusted package. 
     169     * At compile time we check each of M's safe imports are indeed safe or trustworthy. 
     170     * At link time we don't check that M's safe imports are still considered safe or trustworthy. The reasoning behind this is that at compile time we had a guarantee that any modules marked Trustworthy did indeed reside in a package P that was trusted. If at link time some of M's safe imports that are marked Trustworthy now reside in a package marked untrusted this is because the client C changed the package trust. Since C is the one guaranteeing trustworthy modules we believe its fine to not fail. 
     171     * Guaranteeing trustworthy at link time wouldn't be too hard, it would just require we also record in the interface file format for modules marked as trustworthy, which of their dependencies were safe imports. 
    164172 
    165173 * {{{GHC.Prim}}} will need to be made (or just kept) unsafe. 
    166174 
    167  * {{{-XSafe}}} should disallow the {{{TemplateHaskell}}}, {{{StandaloneDeriving}}}, {{{GeneralizedNewtypeDeriving}}}, and {{{CPP}}} language extensions, as well as the {{{RULES}}} and {{{SPECIALIZE}}} pragmas. 
     175 * {{{-XSafe}}} should disallow the {{{TemplateHaskell}}}, {{{StandaloneDeriving}}}, {{{GeneralizedNewtypeDeriving}}}, and {{{CPP}}} language extensions, as well as the {{{RULES}}} and {{{SPECIALIZE}}} pragmas. (See [#Order Of Options] above for details). 
    168176 
    169177 * Overlapping instance declarations must either all reside in modules compiled without `-XSafe`, or else all reside in the same module.  It violates semantic consistency to allow Safe code to change the instance definition associated with a particular type. 
     
    173181 * Libraries will progressively need to be updated to export trustable interfaces, which may require moving unsafe functions into separate modules, or adding new `{-# LANGUAGE Trustworthy #-}` modules that re-export a safe subset of symbols.  Ideally, most modules in widely-used libraries will eventually contain either `{-# LANGUAGE Safe -#}` or `{-# LANGUAGE Trustworthy -#}` pragmas, except for internal modules or a few modules exporting unsafe symbols.  Maybe haddock can add some indicator to make it obvious which modules are trustable and show the trust dependencies. 
    174182 
    175  * When `-XTrustworthy` and `-XSafe` 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. 
    176  
    177  * 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`.) 
     183 * When `-XTrustworthy` and `-XSafeLanguage` are used together, the language is restricted to the Safe dialect and the module is marked as trusted.  Unlike `-XSafe` though all imports aren't forced to be safe imports and so trust is provided by the client C, not ghc.  A plausible use for this 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 `-XSafeLanguage` to catch accidental unsafe actions. 
     184 
    178185 
    179186== Intended uses == 
     
    181188We anticipate the Safe dialect and corresponding options being used in several ways. 
    182189 
     190 
    183191=== Enforcing good programming style === 
    184192 
    185193Over-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. 
     194 
    186195 
    187196=== Restricted IO monads === 
     
    227236[Note that as shown, RIO could fall victim to TOCTTOU bugs or symbolic links, but the same approach applies to more secure monads.] 
    228237 
     238 
    229239=== Restricted `IO` imports === 
    230240 
     
    256266In this case, the type of `Danger.runMe` will be `IO ()`.  However, because `-ultrasafe` implies `-distrust-all-packages`, the only modules `Danger` can import are trustable modules whose entire trust dependency set lies in the current package.  Let's say that `SafeIO` and `Danger` are the only two such modules.  We then know that the only IO actions `Danger.runMe` can directly execute are `rioReadFile` and `rioWriteFile`. 
    257267 
     268 
    258269== Ultra-safety == 
    259270 
     
    261272 
    262273The safe dialect does not prevent use of the symbol `IO`. Nor does it prevent use of `foreign import`.  So this module is OK: 
     274 
    263275{{{ 
    264276{-# LANGUAGE Safe #-} 
     
    266278  foreign import "deleteAllFiles" :: IO () 
    267279}}} 
     280 
    268281Hence, 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 construted inside M unless some other mechanism ensures those actions conform to A's security goal.  Such actions may be hidden inside data structures: 
     282 
    269283{{{ 
    270284{-# LANGUAGE Safe #-} 
     
    275289  rm = RM deleteAllFiles 
    276290}}} 
    277 The flag (and LANGUAGE pragma) `UltraSafe` is just like `Safe` except that it also disables `foreign import`.  This strengtens the safety guarantee, by esuring that an `UltraSafe` module can construct IO actions only by composing together IO actions that it imports from trusted modules.  Note that `UltraSafe` does not disable the use of IO itself. For example this is fine: 
     291 
     292The flag (and LANGUAGE pragma) `UltraSafe` is just like `Safe` except that it also disables `foreign import`.  This strengthens the safety guarantee, by ensuring that an `UltraSafe` module can construct IO actions only by composing together IO actions that it imports from trusted modules.  Note that `UltraSafe` does not disable the use of IO itself. For example this is fine: 
     293 
    278294{{{ 
    279295{-# LANGUAGE UltraSafe #-} 
     
    285301 
    286302Do we really want ultra-safety.  As shown above, we can get some of the benefit by sandboxing with a RIO-like mechanism.  But there is no machine check that you've done it right. What I'd like is a machine check: 
    287  * when I compile untrusted module `Ba`d with `-XUltraSafe` I get the guarantee that any I/O actions accessible through U's exports are obtained by composing I/O actions from modules that I trust 
    288          
    289 I think that's a valuable guarantee.  Simon M points out that if I want to freely call I/O actions exported by an untrusted `-XUltraSafe` module, then I must be careful to trust only packages whose I/O actions are pretty restricted.  In practice, I'll make a sandbox library, and trust only that; now the untrusted module can only to those restricted IO actions. And now we are back to something RIO like.   
    290  
    291 Well, yes, but I want a stronger static guarantee.  As things stand the untrusted module U might export `removeFiles`, and I might accidentally call it. (After all, I have to call some IO actions!)  I want a static check that I'm not calling IO actions contructed by a bad guy. 
     303 * when I compile untrusted module `Bad` with `-XUltraSafe` I get the guarantee that any I/O actions accessible through U's exports are obtained by composing I/O actions from modules that I trust 
     304 
     305I think that's a valuable guarantee.  Simon M points out that if I want to freely call I/O actions exported by an untrusted `-XUltraSafe` module, then I must be careful to trust only packages whose I/O actions are pretty restricted.  In practice, I'll make a sandbox library, and trust only that; now the untrusted module can only call to those restricted IO actions. And now we are back to something RIO like.   
     306 
     307Well, yes, but I want a stronger static guarantee.  As things stand the untrusted module U might export `removeFiles`, and I might accidentally call it. (After all, I have to call some IO actions!)  I want a static check that I'm not calling IO actions constructed by a bad guy. 
    292308 
    293309An alternative way to achieve this would be to have a machine check that none of `Bad`'s exports mention IO, even hidden inside a data type, but I don't really know how to do that.  For example, if the RIO sandbox accidentally exposed the IO-to-RIO constructor, we'd be dead, and that's nothing to do with U's exports. 
     
    303319 
    304320 * [http://hackage.haskell.org/trac/ghc/ticket/1338#comment:29] 
     321 
     322