diff options
| author | David Terei <code@davidterei.com> | 2015-05-12 15:30:36 -0700 |
|---|---|---|
| committer | David Terei <code@davidterei.com> | 2015-05-12 15:38:23 -0700 |
| commit | a171cc133cc1111e91cf892fdbaa7ca476129b07 (patch) | |
| tree | 3c1430ab814c7d46357040f9471388e55c2172aa /docs/users_guide/safe_haskell.xml | |
| parent | 44328639fb9044049be27fdb02a79f0e381c592c (diff) | |
| download | haskell-a171cc133cc1111e91cf892fdbaa7ca476129b07.tar.gz | |
Update Safe Haskell documentation.
Biggest change is to document new overlapping instances behavior. We
also add back in the explanation of GND being restricted, and improve
the docs across the board.
Diffstat (limited to 'docs/users_guide/safe_haskell.xml')
| -rw-r--r-- | docs/users_guide/safe_haskell.xml | 662 |
1 files changed, 398 insertions, 264 deletions
diff --git a/docs/users_guide/safe_haskell.xml b/docs/users_guide/safe_haskell.xml index f0d3bded9e..75093dce46 100644 --- a/docs/users_guide/safe_haskell.xml +++ b/docs/users_guide/safe_haskell.xml @@ -15,15 +15,16 @@ Haskell doesn't provide this directly. Instead, Safe Haskell provides strict type safety. Without Safe Haskell, GHC allows many exceptions to the type system which can subvert any abstractions. By providing strict type safety, - Safe Haskell enables developers to build from their own library level sandbox + Safe Haskell enables developers to build their own library level sandbox mechanisms to run untrusted code. </para> <para> While Safe Haskell is an extension, it actually runs in the background for - every compilation with GHC. It does this to infer the type violations of - modules, even when they aren't explicitly using Safe Haskell. Please refer to - section <xref linkend="safe-inference"/> for more details of this. + every compilation with GHC. It does this to track the type violations of + modules to infer their safety, even when they aren't explicitly using Safe + Haskell. Please refer to section <xref linkend="safe-inference"/> for more + details of this. </para> <para> @@ -31,8 +32,8 @@ <itemizedlist> <listitem>A <link linkend="safe-language">safe language</link> dialect of - Haskell that provides guarantees about the code. It allows types and - module boundaries to be trusted. + Haskell that provides stricter guarantees about the code. It allows types + and module boundaries to be trusted. </listitem> <listitem>A <emphasis>safe import</emphasis> extension that specifies that the module being imported must be trusted. @@ -48,10 +49,10 @@ Safe Haskell, however, <emphasis>does not offer</emphasis> compilation safety. During compilation time it is possible for arbitrary processes to be launched, using for example the <link linkend="pre-processor">custom - pre-processor</link> flag. This can be manipulated to either compromise a + pre-processor</link> flag. This can be manipulated to either compromise a users system at compilation time, or to modify the source code just before - compilation to try to alter set Safe Haskell flags. This is discussed further - in section <xref linkend="safe-compilation"/>. + compilation to try to alter Safe Haskell flags. This is discussed further in + section <xref linkend="safe-compilation"/>. </para> <sect2 id="safe-use-cases"> @@ -72,16 +73,15 @@ Haskell offers a powerful type system and separation of pure and effectual functions through the <literal>IO</literal> monad. However, - there are several loop holes in the type system, the most obvious - offender being the <literal>unsafePerformIO :: IO a -> a</literal> - function. The safe language dialect of Safe Haskell disallows the use of - such functions. This can be useful for a variety of purposes as it makes - Haskell code easier to analyse and reason about. It also codifies an - existing culture in the Haskell community of trying to avoid using such - unsafe functions unless absolutely necessary. As such using the safe - language (through the <option>-XSafe</option> flag) can be thought of as - a way of enforcing good style, similar to the function of - <option>-Wall</option>. + there are several loop holes in the type system, the most obvious being + the <literal>unsafePerformIO :: IO a -> a</literal> function. The safe + language dialect of Safe Haskell disallows the use of such functions. + This can be useful restriction as it makes Haskell code easier to analyse + and reason about. It also codifies the existing culture in the Haskell + community of trying to avoid unsafe functions unless absolutely + necessary. As such, using the safe language (through the + <option>-XSafe</option> flag) can be thought of as a way of enforcing + good style, similar to the function of <option>-Wall</option>. </sect3> <sect3> @@ -95,7 +95,7 @@ guarantees about the properties of Haskell that aren't true in general due to the presence of functions like <literal>unsafePerformIO </literal>. Safe Haskell gives users enough guarantees about the type - safety of compiled code so that they can build such secure systems. + system to allow them to build such secure systems. </para> <para> @@ -108,7 +108,7 @@ interface so that it requires the plugin module, <literal>Danger</literal>, to export a single computation, <literal>Danger.runMe</literal>, of type <literal>RIO ()</literal>, where - <literal>RIO</literal> is a new monad defined as follows: + <literal>RIO</literal> is a monad defined as follows: </para> <programlisting> @@ -144,8 +144,8 @@ if ok then writeFile file contents else return () </programlisting> - We compile <literal>Danger</literal> using the new Safe Haskell - <option>-XSafe</option> flag: + We then compile the <literal>Danger</literal> plugin using the new Safe + Haskell <option>-XSafe</option> flag: <programlisting> {-# LANGUAGE Safe #-} @@ -161,87 +161,99 @@ </para> <itemizedlist> - <listitem>The design attempts to restrict the operations that Danger - can perform by using types, specifically the <literal>RIO</literal> - type wrapper around <literal>IO</literal>. The author of Danger can - subvert this though by simply writing arbitrary - <literal>IO</literal> actions and using <literal>unsafePerformIO :: - IO a -> a</literal> to execute them as pure functions. + <listitem>The design attempts to restrict the operations that + <literal>Danger</literal> can perform by using types, specifically + the <literal>RIO</literal> type wrapper around <literal>IO</literal>. + The author of <literal>Danger</literal> can subvert this though by + simply writing arbitrary <literal>IO</literal> actions and using + <literal>unsafePerformIO :: IO a -> a</literal> to execute them as + pure functions. </listitem> - <listitem>The design also relies on the Danger module not being able - to access the <literal>UnsafeRIO</literal> constructor. + <listitem>The design also relies on <literal>Danger</literal> not being + able to access the <literal>UnsafeRIO</literal> constructor. Unfortunately Template Haskell can be used to subvert module boundaries and so could be used to gain access to this constructor. </listitem> - <listitem>There is no way to place restrictions on the modules that the - untrusted Danger module can import. This gives the author of Danger a - very large attack surface, essentially any package currently - installed on the system. Should any of these packages have a - vulnerability then the Danger module can exploit this. + <listitem>There is no way to place restrictions on the modules that + <literal>Danger</literal> can import. This gives the author of + <literal>Danger</literal> a very large attack surface, essentially + any package currently installed on the system. Should any of these + packages have a vulnerability, then the <literal>Danger</literal> + module can exploit it. </listitem> </itemizedlist> <para> - To stop these attacks Safe Haskell can be used. This is done by compiling - the RIO module with the <option>-XSafe</option> or - <option>-XTrustworthy</option> flag and compiling the Danger module with - the <option>-XSafe</option> flag. + Safe Haskell prevents all these attacks. This is done by compiling the + RIO module with the <option>-XSafe</option> or + <option>-XTrustworthy</option> flag and compiling + <literal>Danger</literal> with the <option>-XSafe</option> flag. We + explain each below. </para> <para> - The use of the <option>-XSafe</option> flag to compile the - Danger module restricts the features of Haskell that can be used + The use of <option>-XSafe</option> to compile + <literal>Danger</literal> restricts the features of Haskell that can be used to a <link linkend="safe-language">safe subset</link>. This includes disallowing <literal>unsafePerformIO</literal>, Template Haskell, pure FFI functions, RULES and restricting the operation of Overlapping Instances. The <option>-XSafe</option> - flag also restricts the modules can be imported by Danger to - only those that are considered trusted. Trusted modules are - those compiled with <option>-XSafe</option>, where GHC provides - a mechanical guarantee that the code is safe. Or those modules - compiled with <option>-XTrustworthy</option>, where the module + flag also restricts the modules can be imported by + <literal>Danger</literal> to only those that are considered trusted. + Trusted modules are those compiled with <option>-XSafe</option>, where + GHC provides a mechanical guarantee that the code is safe. Or those + modules compiled with <option>-XTrustworthy</option>, where the module author claims that the module is Safe. </para> <para> This is why the RIO module is compiled with <option>-XSafe</option> or - <option>-XTrustworthy</option>>, to allow the Danger module to import it. - The <option>-XTrustworthy</option> flag doesn't place any restrictions on - the module like <option>-XSafe</option> does. Instead the module author - claims that while code may use unsafe features internally, it only - exposes an API that can used in a safe manner. The use of - <option>-XTrustworthy</option> by itself marks the module as trusted. + <option>-XTrustworthy</option>>, to allow the <literal>Danger</literal> + module to import it. The <option>-XTrustworthy</option> flag doesn't + place any restrictions on the module like <option>-XSafe</option> does + (expect to restrict overlapping instances to + <link linkend="safe-overlapping-instances">safe overlapping + instances</link>). Instead the module author claims that while code + may use unsafe features internally, it only exposes an API that can used + in a safe manner. </para> <para> - There is an issue here as <option>-XTrustworthy</option> may be used by - an arbitrary module and module author. To control the use of trustworthy - modules it is recommended to use the <option>-fpackage-trust</option> - flag. This flag adds an extra requirement to the trust check for - trustworthy modules, such that for trustworthy modules to be considered - trusted, and allowed to be used in <option>-XSafe</option> compiled code, - the client C compiling the code must tell GHC that they trust the package - the trustworthy module resides in. This is essentially a way of for C to - say, while this package contains trustworthy modules that can be used by - untrusted modules compiled with <option>-XSafe</option>, I trust the - author(s) of this package and trust the modules only expose a safe API. - The trust of a package can be changed at any time, so if a vulnerability - found in a package, C can declare that package untrusted so that any - future compilation against that package would fail. For a more detailed - overview of this mechanism see <xref linkend="safe-trust"/>. + However, the unrestricted use of <option>-XTrustworthy</option> is a + problem as an arbitrary module can use it to mark themselves as + trusted, yet <option>-XTrustworthy</option> doesn't offer any + guarantees about the module, unlike <option>-XSafe</option>. + + To control the use of trustworthy modules it is recommended to use the + <option>-fpackage-trust</option> flag. This flag adds an extra + requirement to the trust check for trustworthy modules. + + It requires that for a trustworthy modules to be considered trusted, and + allowed to be used in <option>-XSafe</option> compiled code, the client C + compiling the code must tell GHC that they trust the package the + trustworthy module resides in. + + This is essentially a way of for C to say, while this package contains + trustworthy modules that can be used by untrusted modules compiled with + <option>-XSafe</option>, I trust the author(s) of this package and trust + the modules only expose a safe API. The trust of a package can be + changed at any time, so if a vulnerability found in a package, C can + declare that package untrusted so that any future compilation against + that package would fail. For a more detailed overview of this mechanism + see <xref linkend="safe-trust"/>. </para> <para> In the example, <literal>Danger</literal> can import module - <literal>RIO</literal>because <literal>RIO</literal> is marked - trustworthy. Thus, <literal>Danger</literal> can make use of the - <literal>rioReadFile</literal> and <literal>rioWriteFile</literal> + <literal>RIO</literal> because <literal>RIO</literal> is compiled with + <option>-XSafe</option>. Thus, <literal>Danger</literal> can make use of + the <literal>rioReadFile</literal> and <literal>rioWriteFile</literal> functions to access permitted file names. The main application then imports both <literal>RIO</literal> and <literal>Danger</literal>. To run the plugin, it calls <literal>RIO.runRIO Danger.runMe</literal> within the <literal>IO</literal> monad. The application is safe in the knowledge - that the only IO to ensue will be to files whose paths were approved by - the <literal>pathOK</literal> test. + that the only <literal>IO</literal> to ensue will be to files whose paths + were approved by the <literal>pathOK</literal> test. </para> </sect3> </sect2> @@ -250,98 +262,205 @@ <title>Safe Language</title> <indexterm><primary>safe language</primary></indexterm> - The Safe Haskell <emphasis>safe language</emphasis> guarantees the - following properties: + The Safe Haskell <emphasis>safe language</emphasis> (enabled by + <option>-XSafe</option>) guarantees the following properties: <itemizedlist> - <listitem><emphasis>Referential transparency</emphasis> — Functions - in the safe language are deterministic, evaluating them will not - cause any side effects. Functions in the <literal>IO</literal> monad - are still allowed and behave as usual. Any pure function though, as - according to its type, is guaranteed to indeed be pure. This property - allows a user of the safe language to trust the types. This means, - for example, that the <literal>unsafePerformIO :: IO a -> a</literal> - function is disallowed in the safe language. + <listitem><emphasis>Referential transparency</emphasis> — The types + can be trusted. Any pure function, is guaranteed to be pure. Evaluating + them is deterministic and won't cause any side effects. Functions in + the <literal>IO</literal> monad are still allowed and behave as usual. + So, for example, the <literal>unsafePerformIO :: IO a -> a</literal> + function is disallowed in the safe language to enforce this property. </listitem> - <listitem><emphasis>Module boundary control</emphasis> — 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 important part of this is that safe compiled code is not - able to examine or create data values using data constructors - that it 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. - Because of this, <emphasis><link linkend="template-haskell">Template - Haskell</link></emphasis> is disabled in the safe language as it can be - used to violate this property. + <listitem><emphasis>Module boundary control</emphasis> — Only + symbols that are publicly available through other module export lists + can be accessed in the safe language. Values using data constructors + not exported by the defining module, cannot be examined or created. As + such, if a module M establishes some invariants through careful use of + its export list, then code written in the safe language that imports M + is guaranteed to respect those invariants. </listitem> - <listitem><emphasis>Semantic consistency</emphasis> — 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. So for - example, there are some restrictions placed on the <emphasis> - <link linkend="instance-overlap">Overlapping Instances</link> - </emphasis> extension as it can violate this property. + + <listitem><emphasis>Semantic consistency</emphasis> — For any + module that imports a module written in the safe language, expressions + that compile both with and without the safe import have the same + meaning in both cases. That is, importing a module written in the safe + language cannot change the meaning of existing code that isn't + dependent on that module. So, for example, there are some restrictions + placed on the use of <emphasis><link linkend="instance-overlap"> + Overlapping Instances</link></emphasis>, as these can violate this + property. + </listitem> + + <listitem><emphasis>Strict subset</emphasis> — 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. </listitem> </itemizedlist> <para> - These three properties guarantee that in the safe language you can trust - the types, can trust that module export lists are respected and can trust - that code that successfully compiles has the same meaning as it normally - would. + These four properties guarantee that in the safe language you can trust the + types, can trust that module export lists are respected, and can trust that + code that successfully compiles has the same meaning as it normally would. </para> - Lets now look at the details of the safe language. In the safe language - dialect (enabled by <option>-XSafe</option>) we disable completely the - following features: - + <para> + To achieve these properties, in the safe language dialect we disable + completely the following features: <itemizedlist> <listitem><emphasis>TemplateHaskell</emphasis> — Can be used to gain access to constructors and abstract data types that weren't - exported by a module, subverting module boundaries.</listitem> - </itemizedlist> + exported by a module, subverting module boundaries. + </listitem> + </itemizedlist> + </para> - In the safe language dialect we restrict the following features: + <para> + Furthermore, we restrict the following features: <itemizedlist> - <listitem><emphasis>ForeignFunctionInterface</emphasis> — 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.</listitem> - <listitem><emphasis>RULES</emphasis> — 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 <option>-XSafe</option> are - dropped. RULES defined in trustworthy modules that M imports are still - valid and will fire as usual.</listitem> - <listitem><emphasis>OverlappingInstances</emphasis> — 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 <option>-XSafe</option> but restricted. While M - can define overlapping instance declarations, they can only overlap - other instance declaration defined 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. an overlap) and the most specific instances - is from M, then all the other choices must also be from M. If not, a - compilation error will occur. A simple way to think of this is a - <emphasis>same origin policy</emphasis> for overlapping instances - defined in Safe compiled modules.</listitem> - <listitem><emphasis>Data.Typeable</emphasis> — We restrict Typeable - instances to only derived ones (offered by GHC through the - <link linkend="deriving-typeable"><option>-XDeriveDataTypeable</option> - </link> extension). Hand crafted instances of the Typeable type class - are not allowed in Safe Haskell as this can easily be abused to - unsafely coerce between types. Since GHC 7.8.1, this became a rule in - regular GHC Haskell as well, so going forward, Typeable can only be - derived both in and outside of the Safe language.</listitem> + <listitem><emphasis>ForeignFunctionInterface</emphasis> — Foreign + import declarations that import a function with a non-IO type are + disallowed. + </listitem> + + <listitem><emphasis>RULES</emphasis> — RULES defined in a module M + compiled with <option>-XSafe</option> are dropped. RULES defined in + trustworthy modules that M imports are still valid and will fire as + usual. + </listitem> + + <listitem><emphasis>OverlappingInstances</emphasis> — There is no + restriction on the creation of overlapping instances, but we do + restrict their use at a particular call site. This is a detailed + restriction, please refer to + <link linkend="safe-overlapping-instances">Safe Overlapping Instances + </link> for details. + </listitem> + + <listitem><emphasis>GeneralisedNewtypeDeriving</emphasis> — GND is + not allowed in the safe language. This is due to the ability of it to + violate module boundaries when module authors forget to put nominal + role annotations on their types as appropriate. For this reason, the + <literal>Data.Coerce</literal> module is also considered unsafe. We are + hoping to find a better solution here in the future. + </listitem> + + <listitem><emphasis>Data.Typeable</emphasis> — Hand crafted + instances of the Typeable type class are not allowed in Safe Haskell as + this can easily be abused to unsafely coerce between types. Derived + instances (through the <link linkend="deriving-typeable"><option> + -XDeriveDataTypeable</option></link> extension) are still allowed. + </listitem> </itemizedlist> + </para> + + <sect3 id="safe-overlapping-instances"> + <title>Safe Overlapping Instances</title> + + <para> + Due to the semantic consistency guarantee of Safe Haskell, we must + restrict the function of overlapping instances. We don't restrict their + ability to be defined, as this is a global property and not something we + can determine by looking at a single module. Instead, when a module calls + a function belonging to a type-class, we check that the instance + resolution done is considered 'safe'. This check is enforced for modules + compiled with both <option>-XSafe</option> and + <option>-XTrustworthy</option>. + </para> + + <para> + More specifically, consider the following modules: + + <programlisting> + {-# LANGUAGE Safe #-} + module Class (TC(..)) where + class TC a where { op :: a -> String } + + {-# LANGUAGE Safe #-} + module Dangerous (TC(..)) where + import Class + + instance + {-# OVERLAPS #-} + TC [Int] where { op _ = "[Int]" } + + {-# LANGUAGE Safe #-} + module TCB_Runner where + import Class + import Dangerous + + instance + TC [a] where { op _ = "[a]" } + + f :: String + f = op ([1,2,3,4] :: [Int]) + </programlisting> + + Both module Class and module Dangerous will compile under + <option>-XSafe</option> without issue. However, in module TCB_Runner, we + must check if the call to <literal>op</literal> in function + <literal>f</literal> is safe. + </para> + + <para> + What does it mean to be Safe? That importing a module compiled with + <option>-XSafe</option> shouldn't change the meaning of code that + compiles fine without importing the module. This is the Safe Haskell + property known as <emphasis>semantic consistency</emphasis>. + </para> + + <para> + In our situation, module TCB_Runner compiles fine without importing + module Dangerous. So when deciding which instance to use for the call to + <literal>op</literal>, if we determine the instance <literal>TC + [Int]</literal> from module Dangerous is the most specific, this is + unsafe. This prevents code written by third-parties we don't trust (which + is compiled using <option>-XSafe</option> in Safe Haskell) from changing + the behaviour of our existing code. + </para> + + <para> + Specifically, we apply the following rule to determine if a type-class + method call is <emphasis>unsafe</emphasis> when overlapping instances are + involved: + + <itemizedlist> + <listitem>Most specific instance, <literal>Ix</literal>, defined in an + `-XSafe` compiled module. + </listitem> + + <listitem><literal>Ix</literal> is an orphan instance or a + multi-parameter-type-class. + </listitem> + + <listitem>At least one overlapped instance, <literal>Iy</literal>, is both: + <itemizedlist> + <listitem>From a different module than <literal>Ix</literal></listitem> + <listitem><literal>Iy</literal> is not marked + <option>OVERLAPPABLE</option></listitem> + </itemizedlist> + </listitem> + </itemizedlist> + + This is a slightly involved heuristic, but captures the situation of an + imported module N changing the behaviour of existing code. For example, if + the second condition isn't violated, then the module author M must depend + either on a type-class or type defined in N. + </para> + + <para> + When an particular type-class method call is considered unsafe due to + overlapping instances, and the module being compiled is using + <option>-XSafe</option> or <option>-XTrustworthy</option>, then + compilation will fail. For <option>-XUnsafe</option>, no restriction is + applied, and for modules using safe inference, they will be inferred + unsafe. + </para> + </sect3> + </sect2> <sect2 id="safe-imports"> @@ -350,6 +469,7 @@ Safe Haskell enables a small extension to the usual import syntax of Haskell, adding a <emphasis>safe</emphasis> keyword: + <programlisting> impdecl -> import [safe] [qualified] modid [as modid] [impspec] </programlisting> @@ -357,10 +477,9 @@ 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 <option>-XSafe</option>, - <option>-XTrustworthy</option>, or <option>-XUnsafe</option> - flags and corresponding PRAGMA's. When the <option>-XSafe</option> flag - is used, the safe keyword is allowed but meaningless, every import - is required to be safe regardless. + <option>-XTrustworthy</option>, or <option>-XUnsafe</option> flags. When + the <option>-XSafe</option> flag is used, the safe keyword is allowed but + meaningless, as every import is treated as a safe import. </sect2> <sect2 id="safe-trust"> @@ -368,28 +487,35 @@ <indexterm><primary>safe haskell trust</primary></indexterm> <indexterm><primary>trust</primary></indexterm> - The Safe Haskell extension introduces the following three language flags: + Safe Haskell introduces the following three language flags: <itemizedlist> <listitem><emphasis>-XSafe</emphasis> — Enables the safe language dialect, asking GHC to guarantee trust. The safe language dialect - requires that all imports be trusted or a compilation error will - occur. Safe Haskell will also infer this safety type for modules - automatically when possible. Please refer to section - <xref linkend="safe-inference"/> for more details of this. - </listitem> + requires that all imports be trusted or a compilation error will occur. + Safe Haskell will also infer this safety type for modules automatically + when possible. Please refer to section <xref linkend="safe-inference"/> + for more details of this. + </listitem> + <listitem><emphasis>-XTrustworthy</emphasis> — 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 + doesn't enable the safe language. It does however restrict the + resolution of overlapping instances to only allow + <link linkend="safe-overlapping-instances">safe overlapping + instances</link>. The trust guarantee is provided by the module author, not GHC. An import statement with the safe keyword results in a - compilation error if the imported module is not trusted. An import + compilation error if the imported module is not trusted. An import statement without the keyword behaves as usual and can import any - module whether trusted or not.</listitem> + module whether trusted or not. + </listitem> + <listitem><emphasis>-XUnsafe</emphasis> — Marks the module being compiled as unsafe so that modules compiled using - <option>-XSafe</option> can't import it. + <option>-XSafe</option> can't import it. You may want to explicitly + mark a module unsafe when it exports internal constructors that can be + used to violate invariants. </listitem> </itemizedlist> @@ -397,17 +523,16 @@ While these are flags, they also correspond to Safe Haskell module types that a module can have. You can think of using these as declaring an explicit contract (or type) that a module must have. If it is invalid, then - compilation will fail. Just like with regular Haskell types, GHC will also - infer the correct type for Safe Haskell. Please refer to section - <xref linkend="safe-inference"/> for more details of this. + compilation will fail. GHC will also infer the correct type for Safe + Haskell, please refer to section <xref linkend="safe-inference"/> for more + details. </para> <para> The procedure to check if a module is trusted or not depends on if the - <option>-fpackage-trust</option> flag is present. The check is very similar - in both cases with the presence of the <option>-fpackage-trust</option> - flag simply enabling an extra requirement for trustworthy modules to be - regarded as trusted. + <option>-fpackage-trust</option> flag is present. The check is similar in + both cases with the <option>-fpackage-trust</option> flag enabling an extra + requirement for trustworthy modules to be regarded as trusted. </para> <sect3> @@ -439,16 +564,12 @@ <para> The above definition of trust has an issue. Any module can be compiled - with -XTrustworthy and it will be trusted regardless of what it does. To - control this there is an additional definition of package trust (enabled - with the <option>-fpackage-trust</option> flag). The point of package - trusts is to require that the client C explicitly say which packages are - allowed to contain trustworthy modules. That is, C establishes that it - trusts a package P and its author and so trust the modules in P that use - <option>-XTrustworthy</option>. When package trust is enabled, any - modules that are considered trustworthy but reside in a package that - isn't trusted are not considered trusted. A more formal definition is - given in the next section. + with <option>-XTrustworthy</option> and it will be trusted. To control + this, there is an additional definition of package trust (enabled with the + <option>-fpackage-trust</option> flag). The point of package trust is to + require that the client C explicitly say which packages are allowed to + contain trustworthy modules. Trustworthy packages are only trusted if + they reside in a package trusted by C. </para> </sect3> @@ -459,9 +580,13 @@ <para> When the <option>-fpackage-trust</option> flag is enabled, whether or not - a module is trusted depends on a notion of trust for packages, which is - determined by the client C invoking GHC (i.e. you). A package <emphasis>P - is trusted</emphasis> when one of these hold: + a module is trusted depends on if certain packages are trusted. Package + trust is determined by the client C invoking GHC (i.e. you). + </para> + + <para> + Specifically, a package <emphasis>P is trusted</emphasis> when one of + these hold: <itemizedlist> <listitem>C's package database records that P is trusted (and no command-line arguments override this)</listitem> @@ -514,16 +639,11 @@ requirement enabled by <option>-fpackage-trust</option> causes the design of Safe Haskell to be invasive. Packages using Safe Haskell when the flag is enabled may or may not compile depending on the state of trusted - packages on a users machine. A maintainer of a package - <literal>foo</literal> that uses Safe Haskell so that security conscious - Haskellers can use <literal>foo</literal> now may have other users of - <literal>foo</literal> who don't know or care about Safe Haskell - complaining about compilation problems they are having with - <literal>foo</literal>because a package <literal>bar</literal>that foo - requires, isn't trusted on their machine. In this sense, the - <option>-fpackage-trust</option> flag can be thought of as a flag to - properly turn on Safe Haskell while without it, it's operating in a - covert fashion. + packages on a users machine. This is both fragile, and causes compilation + failures for everyone, even if they aren't trying to use any of the + guarantees provided by Safe Haskell. Disabling + <option>-fpackage-trust</option> by default and turning it into a flag + makes Safe Haskell an opt-in extension rather than an always on feature. </para> </sect3> @@ -545,20 +665,38 @@ </programlisting> <para> - 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 <option>-XTrustworthy</option>, so - P's author takes responsibility for that import. C trusts P's author, so - C trusts M to only use its unsafe imports in a safe and consistent - manner with respect to the API M exposes. M also has a safe import of - Buggle, so for this import P's author takes no responsibility for the - safety, so GHC must check whether Buggle is trusted by C. Is it? Well, - it is compiled with <option>-XSafe</option>, so the code in Buggle - itself is machine-checked to be OK, but again under the assumption that - all of Buggle's imports are trusted by C. Prelude comes from base, which - C trusts, and is compiled with <option>-XTrustworthy</option> (While - Prelude is typically imported implicitly, it still obeys the same rules - outlined here). So Buggle is considered trusted. + Suppose a client C decides to trust package P and package base. Then does + C trust module M? Well M is marked <option>-XTrustworthy</option>, so we + don't restrict the language. However, we still must check M's imports: + <itemizedlist> + <listitem>First, M imports System.IO.Unsafe. This is an unsafe module, + however M was compiled with <option>-XTrustworthy</option>, so P's + author takes responsibility for that import. C trusts P's author, so + this import is fine. + </listitem> + + <listitem> + Second, M safe imports Buggle. For this import P's author takes no + responsibility for the safety, instead asking GHC to check whether + Buggle is trusted by C. Is it? + </listitem> + + <listitem> + Buggle, is compiled with <option>-XSafe</option>, so the code is + machine-checked to be OK, but again under the assumption that all of + Buggle's imports are trusted by C. We must recursively check all + imports! + </listitem> + + <liteitem> + Buggle only imports Prelude, which is compiled with + <option>-XTrustworthy</option>. Prelude resides in the base package, + which C trusts, and (we'll assume) all of Prelude's imports are + trusted. So C trusts Prelude, and so C also trusts Buggle. (While + Prelude is typically imported implicitly, it still obeys the same + rules outlined here). + </liteitem> + </itemizedlist> </para> <para> @@ -588,15 +726,15 @@ trust property of packages: <itemizedlist> - <listitem><emphasis>-trust P</emphasis> — Exposes package P if it - was hidden and considers it a trusted package regardless of the + <listitem><emphasis>-trust P</emphasis> — Exposes package P if it was + hidden and considers it a trusted package regardless of the package + database.</listitem> + <listitem><emphasis>-distrust P</emphasis> — Exposes package P if it + was hidden and considers it an untrusted package regardless of the package database.</listitem> - <listitem><emphasis>-distrust P</emphasis> — Exposes package P if - it was hidden and considers it an untrusted package regardless of the - package database.</listitem> - <listitem><emphasis>-distrust-all-packages</emphasis> — Considers - all packages distrusted unless they are explicitly set to be trusted - by subsequent command-line options.</listitem> + <listitem><emphasis>-distrust-all-packages</emphasis> — Considers all + packages distrusted unless they are explicitly set to be trusted by + subsequent command-line options.</listitem> </itemizedlist> To set a package's trust property in the package database please refer to @@ -616,19 +754,18 @@ the module can be considered safe. This safety inference will never mark a module as trustworthy, only as either unsafe or as safe. GHC uses a simple method to determine this for a module M: If M would compile without error - under the <option>-XSafe</option> flag, then M is marked as safe. If M - would fail to compile under the <option>-XSafe</option> flag, then it is - marked as unsafe. + under the <option>-XSafe</option> flag, then M is marked as safe. + Otherwise, it is marked as unsafe. </para> <para> When should you use Safe Haskell inference and when should you use an explicit <option>-XSafe</option> flag? The later case should be used when you have a hard requirement that the module be safe. This is most useful - for the security <xref linkend="safe-use-cases">use case</xref> of Safe + for the <xref linkend="safe-use-cases">security use case</xref> of Safe Haskell: running untrusted code. Safe inference is meant to be used by ordinary Haskell programmers. Users who probably don't care about Safe - Haskell. + Haskell. </para> <para> @@ -638,28 +775,18 @@ </para> <itemizedlist> - <listitem><emphasis>Inferred</emphasis> — This may work well and - adds no dependencies on the Safe Haskell type of any modules in other - packages. It does mean that the Safe Haskell type of your own modules - could change without warning if a dependency changes. One way to deal - with this is through the use of <xref linkend="safe-flag-summary">Safe + <listitem><emphasis>Inferred</emphasis> — This works well and adds no + dependencies on the Safe Haskell type of any modules in other packages. + It does mean that the Safe Haskell type of your own modules could + change without warning if a dependency changes. One way to deal with + this is through the use of <xref linkend="safe-flag-summary">Safe Haskell warning flags</xref> that will warn if GHC infers a Safe Haskell type different from expected.</listitem> <listitem><emphasis>Explicit</emphasis> — This gives your library a - stable Safe Haskell type that others can depend on. However, it may - increase the rate of compilation failure when your package dependencies - change.</listitem> + stable Safe Haskell type that others can depend on. However, it will + increase the chance of compilation failure when your package + dependencies change.</listitem> </itemizedlist> - - <para> - This is a benefit as now a user of your library who may want to use it as - part of an API exposed to untrusted code can use the library without - change. If there wasn't safety inference then either the writer of the - library would have to explicitly use Safe Haskell, which is an unreasonable - expectation of the whole Haskell community. Or the user of the library - would have to wrap it in a shim that simply re-exported your API through a - trustworthy module, an annoying practice. - </para> </sect2> <sect2 id="safe-flag-summary"> @@ -672,15 +799,16 @@ <varlistentry> <term>-XSafe</term> <indexterm><primary>-XSafe</primary></indexterm> - <listitem>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, - every import is required to be safe regardless. + <listitem>Restricts the module to the safe language. 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, as regardless, every import is required to be + safe. <itemizedlist> <listitem><emphasis>Module Trusted</emphasis> — Yes</listitem> - <listitem><emphasis>Haskell Language</emphasis> — Restricted to Safe - Language</listitem> + <listitem><emphasis>Haskell Language</emphasis> — Restricted to + Safe Language</listitem> <listitem><emphasis>Imported Modules</emphasis> — All forced to be safe imports, all must be trusted.</listitem> </itemizedlist> @@ -691,18 +819,24 @@ <term>-XTrustworthy</term> <indexterm><primary>-XTrustworthy</primary></indexterm> <listitem>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. - <option>-XTrustworthy</option> has no effect on the accepted range - of Haskell programs or their semantics, except that they allow the - safe import keyword. + 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. + <option>-XTrustworthy</option> doesn't restrict the module to the + safe language. It does however restrict the resolution of overlapping + instances to only allow <link linkend="safe-overlapping-instances"> + safe overlapping instances</link>. It also allows the use of the safe + import keyword. + <itemizedlist> <listitem><emphasis>Module Trusted</emphasis> — Yes.</listitem> - <listitem><emphasis>Module Trusted (<option>-fpackage-trust</option> - enabled)</emphasis> — Yes but only if the package the module - resides in is also trusted.</listitem> - <listitem><emphasis>Haskell Language</emphasis> — Unrestricted + <listitem><emphasis>Module Trusted + (<option>-fpackage-trust</option> enabled)</emphasis> — Yes but + only if the package the module resides in is also + trusted.</listitem> + <listitem><emphasis>Haskell Language</emphasis> — Unrestricted, + except only <link linkend="safe-overlapping-instances">safe + overlapping instances</link> allowed. </listitem> <listitem><emphasis>Imported Modules</emphasis> — Under control of module author which ones must be trusted.</listitem> @@ -734,9 +868,9 @@ <varlistentry> <term>-fpackage-trust</term> <indexterm><primary>-fpackage-trust</primary></indexterm> - <listitem>When enabled turn on an extra check for a trustworthy module - M, requiring that the package M resides in is considered trusted for - the M to be considered trusted. + <listitem>When enabled, turn on an extra check for a trustworthy module + M, requiring the package that M resides in be considered trusted, for + M to be considered trusted. </listitem> </varlistentry> </variablelist> @@ -747,17 +881,17 @@ <varlistentry> <term>-fwarn-unsafe</term> <indexterm><primary>-fwarn-unsafe</primary></indexterm> - <listitem>Issue a warning if the module being compiled is regarded - to be unsafe. Should be used to check the safety type of modules - when using safe inference. + <listitem>Issue a warning if the module being compiled is regarded to + be unsafe. Should be used to check the safety type of modules when + using safe inference. </listitem> </varlistentry> <varlistentry> <term>-fwarn-safe</term> <indexterm><primary>-fwarn-safe</primary></indexterm> - <listitem>Issue a warning if the module being compiled is regarded - to be safe. Should be used to check the safety type of modules - when using safe inference. + <listitem>Issue a warning if the module being compiled is regarded to + be safe. Should be used to check the safety type of modules when + using safe inference. </listitem> </varlistentry> <varlistentry> |
