7.27. Safe Haskell

Safe Haskellは、バージョン7.2でGHCに実装された言語拡張である。これは、コードが使うことが許されるGHC Haskellの機能を限定することによって、unsafeなコードを信頼されたコードベースに安全な形で含めることができるようにする。単純化して言うと、これはプログラム内の型を信頼に足るものにする。Safe Haskellは最小限のものであることを目標としていて、その上により高度な安全システムを構築するのに必要十分な強さの保証を提供する。

これがSafe Haskellの動機となる使用例であるものの、Safe Haskellが追跡し強制するものが、通常のHaskellで保証されるよりも厳格な型安全性であるということを理解するのは重要である。これの一環として、Safe HaskellはGHCによるコンパイルのたびに実行され、明示的にSafe Haskellを使っていないモジュールについてさえ安全性を追跡し推論する。これについてのさらなる詳細は7.27.5. Safe Haskellの推論を参照して欲しい。これはまた、セキュリティの観点からは奇妙に見えるが型安全性を追跡するという角度からは論理的な設計判断があることも意味する。現在の設計について、またこのセキュリティと型安全性の間の緊張関係について、フィードバックを歓迎する。

Safe Haskellの設計は以下の面にわたっている。

一方で、Safe Haskellは、コンパイル時安全性を提供しない。コンパイル時には、例えばプリプロセッサを変更するフラグによって任意のプロセスが起動され得る。これを操作することで、コンパイル時にユーザのシステムを危険にさらしたり、コンパイルの直前にソースコードを改変することで設定されたSafe Haskellのフラグに変更を加えることができる。これについては、節7.27.7. 安全なコンパイルにさらなる議論がある。

7.27.1. Safe Haskellの用途

Safe Haskellは以下の二つの利用例を念頭に置いて設計されている。

  • 厳格な型安全性をコンパイル時に強制する
  • 信頼されていないコードをコンパイルし、実行する

7.27.1.1. 厳格な型安全性(良いスタイル)

Haskellは、強力な型システムと、IOモナドを通した純粋な関数と作用のある関数の分離を提供している。しかし、この型システムには抜け穴がいくつかある。最も分かり易いのは関数unsafePerformIO :: IO a -> aである。Safe Haskellのsafe language方言はこのような関数の利用を禁止する。これによってHaskellコードを分析したり論証したりし易くなるので、色々の目的のために便利である。また、このようなunsafeな関数はどうしても必要でない限り使うべきでないという既存のHaskellコミュニティの文化を成文化するものでもある。こういう訳で、(-XSafeを使った)safe languageの利用は、-Wallの機能と似て、良いスタイルを強制する手段だととらえることができる。

7.27.1.2. セキュアなシステムを構築する(制約付きIOモナド)

情報フロー制御、capability-basedセキュリティ、暗号化されたデータを扱うためのDSL、といったシステムは、単に一つのライブラリとしてHaskell言語に組み込むことができる。しかしこれらのシステムは、unsafePerformIOのような関数が許される一般の場合には成り立たないようなHaskell言語の性質が保証されることを要求する。Safe Haskellは、コンパイルされたコードの性質に関して、このようなシステムを構築するのに十分なだけの保証を与えるように設計されている。

例として、プラグイン作者が信用されず、悪意のある第三者かもしれないようなプラグインシステムのためのインタフェースを定義してみよう。このために、プラグインのインタフェースを純粋な関数にするか、IO動作の安全なサブセットしか実行することを許されないような制限されたIOモナドにする。ここではプラグインのインタフェースを以下のようにする。すなわち、プラグインモジュールDangerは、型RIO ()を持つ単一の計算Danger.runMeを定義することを要求される。ただしRIOは以下のように定義された新しいモナドである。

-- 以下のSafe Haskellのプラグマはどちらでも良い
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE Safe #-}

module RIO (RIO(), runRIO, rioReadFile, rioWriteFile) where

-- UnsafeRIOシンボルがこのモジュールからエクスポートされていないのに注意!
newtype RIO a = UnsafeRIO { runRIO :: IO a }

instance Monad RIO where
    return = UnsafeRIO . return
    (UnsafeRIO m) >>= k = UnsafeRIO $ m >>= runRIO . k

-- ファイル名にアクセスが許可されている場合、かつその場合に限りTrue
pathOK :: FilePath -> IO Bool
pathOK file = {- ファイル名についての何らかのポリシーの実装 -}

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 ()
Dangerを、新しいSafe Haskellのフラグである-XSafe付きでコンパイルする
{-# LANGUAGE Safe #-}
module Danger ( runMe ) where

runMe :: RIO ()
runMe = ...

Safe Haskellの詳細に入る前に、この設計がSafe Haskellなしではうまく行かない理由の一部を指摘しておこう。

  • この設計は、Dangerが実行することのできる操作を型によって、具体的にはIOのラッパであるRIOによって制限している。Dangerの作者は、単にunsafePerformIO :: IO a -> aを使って任意のIOアクションを純粋関数として実行することでこの制限を覆すことができる。
  • またこの設計はDangerモジュールがUnsafeRIO構築子にアクセスできないことを前提としている。残念なことにTemplate Haskellを使うとモジュール境界を破壊することができ、この構築子へのアクセスを得ることもできるだろう。
  • 信用されていないDangerモジュールがインポートできるモジュールに制限を掛ける方法がない。これによって非常に広いattack surface(実質的に、システムにインストールされている全てのパッケージ)をDangerの作者に与えることになる。仮にこれらのパッケージのいずれかに脆弱性があれば、Dangerモジュールはそれを利用できる。これを防ぐ唯一の方法は、既知の脆弱性のあるモジュールにパッチを当てるか削除することである。それらがRIOモジュールなどの信頼されたモジュールからのみ使われるべきものであったとしても。

これらの攻撃を止めるためにSafe Haskellを使うことができる。これには、RIOモジュールを-XTrustworthyフラグ付きでコンパイルし、Dangerモジュールを-XSafeフラグ付きでコンパイルする。

-XSafeフラグを使うことで、利用可能なHaskellの機能を安全なサブセットに限定してDangerモジュールをコンパイルすることができる。これには、unsafePerfromIO、Template Haskell、純粋なFFI関数、一般化newtype deriving、RULESを禁止し、重複インスタンスの操作に制限を加えることを含む。さらに、-XSafeは、Dangerがインポートできるモジュールを信頼されているとみなされるもののみに制限する。信頼されるモジュールは、-XSafe付きでコンパイルされたもの(この場合、コードが安全であることをGHCが機械的に保証する)または、-XTrustworthy付きでコンパイルされたもの(この場合、モジュール作者がこのモジュールが安全だと主張する)である。

これが、RIOモジュールがDangerモジュールからインポートできるように-XTrustworthy付きでコンパイルされている理由である。-XTrustworthyは、-XSafeと異なり、モジュールについていかなる制限も課さない。代わりに、コードが内部的にunsafeな機能を使っていても、安全に使えるAPIしか露出していないということをモジュール作者が主張する。-XTrustworthy自体が、そのモジュールを信頼されたものにするのである。ここで、問題が一つある。あらゆるモジュールのあらゆる作者が-XTrustworthyを使えるのである。trustworthyモジュールの利用を制御するため、-fpackage-trustフラグを使うことが推奨される。このフラグは、trustworthyモジュールの信頼検査へ条件を追加する。その場合、Trustworthyとされたモジュールが信頼され、-XSafeでコンパイルされるコードから使うことを許されるためには、コードをコンパイルしているクライアントCが、このTrustworthyとされたモジュールが所属するパッケージを信頼する旨をGHCに伝えなければならない。これは実質的に、「このパッケージは-XSafeでコンパイルされる信頼されないモジュールによって使えるTrustworthyモジュールを含んでいるが、私はこのパッケージの作者を信頼し、このモジュールが安全なAPIしか露出しないことを信頼する」と言う手段をCに与える。パッケージへの信頼はいつでも変えられるので、もしパッケージに脆弱性が見付かったら、Cはそのパッケージが信頼されないと宣言し、そのパッケージに対する将来のいかなるコンパイルも失敗するようにできる。この機構についてのより詳細な概観は7.27.4. 信頼とSafe Haskellのモードを見よ。

例では、RIOはTrustworthyと標示されているのでDangerはRIOをインポートできる。よって、DangerはrioReadFileとrioWriteFileの各関数を使って許可されたファイル名にアクセスできる。次に主アプリケーションがRIOとDangerの両方をインポートし、RIO.runRIO Danger.runMeをIOモナド中で呼ぶことでプラグインを走らせる。pathOKの検査によって認められたパスを持つファイルにしかIOできないと分かっているので、このアプリケーションは安全である。

7.27.2. safe language

Safe Haskellのsafe languageは以下の性質を保証する。
  • 参照透明性 — safe languageの関数は決定的であり、それらを評価することはいかなる副作用も引き起こさない。IOモナドの関数は通常通りに振る舞うことが許される。しかし、純粋な関数は全て、その型に従って実際に純粋であることが保証される。この性質によって、safe languageの利用者は型を信頼することが可能になる。これは、例えばunsafePerformIO :: IO a -> aがsafe languageにおいて禁止されることを意味している。
  • モジュール境界の制御 — safe languageを使ってコンパイルされたHaskellコードは、他のモジュールのエクスポートリストから公的に利用可能なシンボルにしかアクセスできないことが保証される。このうち重要な点として、safeでコンパイルされたコードは、自分がインポートできない構築子を使ってデータ値を検査したり作ったりすることができない。モジュールMが、エクスポートリストを注意深く使うことである不変条件を確立しているなら、safe languageを使ってコンパイルされたコードでMをインポートするものは、これらの不変条件を守ることが保証される。このために、Template Haskellはsafe languageにおいては無効にされる。これらを使ってこの性質に違反することができるからである。
  • 意味的な整合性 — safe languageはHaskellのGHC実装の厳密なサブセットである。safe languageでコンパイル可能なあらゆる式は、通常のHaskellでコンパイルされた場合と全く同じ意味を持つ。さらに、safe languageモジュールをインポートするあらゆるモジュールにおいて、このsafe importを付けても付けなくてもコンパイル可能なあらゆる式は、どちらの場合でも同じ意味を持つ。つまり、safe languageを使ったモジュールをインポートすることで、そのモジュールに依存していない既存コードの意味を変えることはできない。よって例えば、重複インスタンス拡張はこの性質に違反できるので、いくらかの制限が課される。

これらの三つの性質は、safe languageにおいて、型を信頼できること、コードがモジュールのエクスポートリストに従うこと、コンパイルに成功したコードが通常持つべき意味と同じ意味を持つこと、を保証する。

ではsafe languageの詳細を見ていこう。safe lauguage方言(-XSafeによって有効になる)においては、以下の機能は完全に無効になる。
  • TemplateHaskell — これは、コンパイル時に副作用を起こすことを可能にし、抽象データ型の構築子にアクセスするために使えるので特に危険である。
safe language方言では以下の機能に制限が加えられる。
  • ForeignFunctionInterface — これは大部分において安全であるが、関数をIOでない型でインポートするforeign import宣言は禁止される。全てのFFIインポートはIOモナドに生息しなければならない。
  • RULES — 信頼されたコードの振る舞いを予測不可能な形で変更し、意味上の整合性に違反することができるので、機能が制限される。具体的には、-XSafeでコンパイルされたモジュールMで定義されたRULESは全て捨てられる。MがインポートするTrustworthyモジュールで定義されたRULESはなお有効であり、通常通り発動する。
  • OverlappingInstances — この拡張は意味上の整合性に違反するために使うことができる。悪意のあるコードは、この信頼されていないモジュールをインポートしているコードの振る舞いを変えるようなインスタンスを再定義できるかもしれない(より特殊性の高いインスタンス宣言を持つことで)からである。-XSafeでコンンパイルされたモジュールMにおいて、この拡張は無効化されないが制限を受ける。Mは重複インスタンス宣言を定義できるが、それらはMで定義された別のインスタンス宣言としか重複できない。MをインポートするモジュールNの中、型クラス関数を使う呼び出し地点で、どのインスタンスを使うかの選択肢(つまり重複)があり、最も特殊性の高いインスタンスがM由来の場合、他の全ての選択肢もM由来でなければならない。これについての簡単な考え方は、Safeコンパイルされたモジュールで定義された重複インスタンスに関する同一生成元ポリシー(same origin policy)だと思うことである。
  • Data.Typeable — Typeableインスタンスを自動導出(GHCによって-XDeriveDataTypeable拡張を介して提供されている)されたもののみに制限している。Typeable型クラスの手書きインスタンスは、unasfeに型変換を行うように簡単に悪用できるので、Safe Haskellでは許されていない。

7.27.3. safeインポート

Safe Haskellは、Haskellの通常のインポート構文に対する小さな拡張を有効にする。次のようにsafeキーワードを加える。
impdecl -> import [safe] [qualified] modid [as modid] [impspec]
使われると、safeキーワード付きでインポートされるモジュールは信頼されたものでなければならず、そうでなければコンパイルエラーが発生する。このsafeインポート拡張は-XSafe-XTrustworthy-XUnsafeのいずれか、あるいは対応するプラグマによって有効になる。-XSafeフラグが使われている場合、このsafeキーワードは許されるが無意味である。いずれにせよ全てのインポートが安全でなければならない。

7.27.4. 信頼とSafe Haskellのモード

Safe Haskell拡張は以下の三つの新しい言語フラグを導入する。
  • -XSafe — safe language方言を有効にし、GHCに信頼を保証するように頼む。safe language方言は全てのインポートが信頼されていることを要求し、そうでなければコンパイルエラーが発生する。
  • -XTrustworthy — このモジュールは内部的にunsafeな関数を呼びかもしれないが、unsafeな形で使うことのできないAPIしかエクスポートしないとモジュールの作者が主張していることを意味する。これはsafe languageを有効にせず、どんなHaskellコードが許されるかについて制限を加えることもない。信頼の保証はGHCによってではなくモジュール作者によって与えられる。safeキーワードの付いたインポート文は、インポート先モジュールが信頼されていない場合にコンパイルエラーになる。このキーワードの付いていないインポート文は通常通りに振る舞い、信頼されているかどうかにかかわらず、あらゆるモジュールをインポートできる。
  • -XUnsafe — コンパイルされているモジュールが安全でなく、したがって-XSafeを使ってコンパイルされているモジュールからインポートできないことを標示する。

あるモジュールが信頼されるかどうかの検査の手続きは、-fpackage-trustフラグが与えられているかどうかに依存する。どちらの場合の検査も良く似ており、-fpackage-trustの存在は、trustworthyモジュールが信頼されているとみなされるための要件を一つ追加するのみである。

7.27.4.1. 信頼検査(-fpackage-trustが無効の場合)

パッケージPのモジュールMがクライアントCに信頼されるのは、以下の条件が満されるとき、かつそのときに限る。
  • 以下が両方とも成り立つ
    • このモジュールが-XSafe付きでコンパイルされている
    • Mが直接インポートしているモジュールが全てCに信頼されている
  • あるいは以下が全て成り立つ
    • このモジュールが-XTrustworthy付きでコンパイルされている
    • Mが直接safeインポートしているモジュールが全てCに信頼されている

信頼を上のように定義することには問題がある。どんなモジュールでも-XTrustworthy付きでコンパイルすることができ、そうするとそれが何をするかにかかわらず信頼されるのである。これを制御するために、パッケージへの信頼についての追加的定義がある(-fpackage-trustで有効になる)。パッケージへの信頼の要点は、どのパッケージがtrustworthyモジュールを含めるかをクライアントCが明示的に言うことである。つまり、Cは、パッケージPとその作者を信頼するのでP内の-XTrustworthyを使ったモジュールを信頼する、と決める。パッケージへの信頼が有効の場合、trustworthyだとみなされるものの信頼されていないパッケージにあるモジュールは信頼されていないとみなされる。より形式的な定義は次の節で与えられる。

7.27.4.2. 信頼検査(-fpackage-trustが有効の場合)

-fpackage-trustが有効なら、あるモジュールが信頼されるかどうかは、パッケージへの信頼という概念に依存する。これはGHCを起動するクライアントC(つまり、あなたである)によって決定される。パッケージPが信頼されるのは、以下のどれかが成立する場合である。

  • CのパッケージデータベースのレコードがPが信頼されていると記録している(しかも、コマンド行引数によって塗り替えられていない)場合。
  • Cのコマンド行フラグが、パケージデータベースの記録に関係なく信頼しろと言っている場合。

どちらの場合でも、パッケージの信頼に関して決定権はCにしかない。どのパッケージを信頼するかはクライアントによる。

-fpackage-trustフラグが使われているとき、パッケージPのモジュールMがクライアントCに信頼されるのは、以下の条件が満されるとき、かつそのときに限る。

  • 以下が両方とも成り立つ
    • このモジュールが-XSafe付きでコンパイルされている
    • Mが直接インポートしているモジュールが全てCに信頼されている
  • あるいは以下が全て成り立つ
    • このモジュールが-XTrustworthy付きでコンパイルされている
    • Mが直接safeインポートしているモジュールが全てCに信頼されている
    • パッケージPがCに信頼されている

一番目の信頼の定義については、信頼の保証は、safe languageによって課される制約を通してGHCによって与えられる。二番目の定義については、この保証はまずモジュール作者によって与えられる。次にクライアントCが、このモジュールを含むパッケージを信頼することを示すことによって、モジュール作者への信頼を明示する。この信頼の連鎖が必要なのは、-XTrustworthyでコンパイルされたモジュールに関してGHCはいかなる保証も提供しないからである。

信頼検査に二つのモードがある理由は、-fpackage-trustによって有効になる追加の要件が、Safe Haskellの設計を侵襲的なものにするからである。このフラグが有効な場合、Safe Haskellを使っているパッケージは、ユーザの機械上のパッケージの信頼状態によってコンパイルが通ったり通らなかったりする。パッケージfooのメンテナが、セキュリティ意識のあるHaskellerがfooを使えるようにSafe Haskellを使うと、Safe Haskellを知りも構いもしないユーザに、fooが必要とするパッケージbarが彼らの機械上で信頼されていないために、fooがコンパイルできないと文句を言われることになる。この意味で、-fpackage-trustはSafe Haskellを正式に有効にするフラグだと思うことができる。これなしでは、Safe Haskellはこっそりとしたやりかたで動作する。

Having the -fpackage-trust flag also nicely unifies the semantics of how Safe Haskell works when used explicitly and how modules are inferred as safe.(訳注: 訳せません。助けて)

7.27.4.3. 例

パッケージWuggle:
   {-# LANGUAGE Safe #-}
   module Buggle where
     import Prelude
     f x = ...blah...

パッケージP:
   {-# LANGUAGE Trustworthy #-}
   module M where
     import System.IO.Unsafe
     import safe Buggle

クライアントCがパッケージPを信頼することを決めたとしよう。ではCはモジュールMを信用するか?これを決めるために、GHCはMのインポートを検査しなければいけない。MはSystem.IO.Unsafeをインポートしている。Mは-XTrustworthy付きでコンパイルされているから、このインポートについてはMの作者が責任を負う。CはPの作者を信頼しているので、CはMがunsafeなインポートを安全かつMが露出するAPIに整合的な形でしか使わないということを信頼している。Mには、BuggleへのSafeインポートもある。safeインポートなのでPの作者はこれの安全性について責任を負わないため、BuggleがCに信頼されているかをGHCが検査しないといけない。どうか?うーん、これは-XSafe付きでコンパイルされているので、Buggle自体のコードは問題ないことが機械的に検査されている。ただしこれもBuggleのインポートが全てCに信頼されていることが条件である。Preludeはbase由来であり、Cはこれを信頼しており、-XTrustworthy付きでコンパイルされている。(Preludeは典型的には暗黙にインポートされるが、その場合でもここで述べた規則に沿う)。よってBuggleは信頼されているとみなされる。

CはパッケージWuggleを信頼する必要がなかったことに注意。機械による検査だけで十分である。Cは、-XTrustworthyなモジュールを含むパッケージだけを信頼すれば良い。

7.27.4.4. Trustworthyの要件

言語拡張-XTrustworthyを使うモジュールMの作者は、Mの公開API(エクスポートリストによって露出されているシンボル群)が安全でない方法で使えないことを保証するべきである。これは、エクスポートされているシンボルが型安全性と参照透明性に従うべきだということである。

7.27.4.5. パッケージへの信頼

Safe Haskellは各パッケージに新しい真偽値のプロパティを与える。信頼プロパティである。GHCのコマンド行でパッケージの信頼プロパティを指定するためのオプションがいくつか新たに利用可能である。
  • -trust P — パッケージPを隠されているなら露出し、パッケージデータベースに関係なく、信頼されているとみなす。
  • -distrust P — パッケージPを隠されているなら露出し、パッケージデータベースに関係なく、信頼されていないとみなす。
  • -distrust-all-packages — 後続のコマンド行オプションで明示的に信頼されていると指定されていない限り、全てのパッケージを信頼されていないとみなす。
パッケージの信頼プロパティをパッケージデータベース中で設定するには、4.9. パッケージ を参照してほしい。

7.27.5. Safe Haskellの推論

-XSafe-XTrustworthy-XUnsafeのどれもが使われずにモジュールがコンパイルされる場合、GHCはそのモジュールが安全だとみなせるかを自分で見出そうとする。この安全性の推論はモジュールを決してtrustworthyだとは標示せず、unsafeかsafeだとする。これをモジュールMについて決定するために、GHCは以下の単純な方法を使う。すなわち、Mが-XSafeフラグの下でもエラーなしでコンパイルできるなら、Mはsafeであると標示する。Mが-XSafeフラグの下ではコンパイルに失敗するなら、unsafeであると標示する。

Safe Haskellの推論を使うべきなのは何時で、明示的な-XSafeフラグを使うべきなのは何時か?そのモジュールが安全でなければならないという動かせない要求があるなら、後者を使うべきである。つまり、概観した利用例や、Safe Haskellが意図している目的、すなわち信頼されていないコードをコンパイルする場合である。安全性の推論はふつうのHaskellプログラマに使われることを意図している。通常Safe Haskellを気にかけないユーザである。

Haskellライブラリを書いているとしよう。その場合、あなたは単に安全性推論を使いたいと思うだろう。安全でない言語機能を避けるなら、あなたのモジュールはsafeと標示される。これは良いことである。なぜなら、あなたのライブラリを信頼されないコードに露出されるAPIの一部として使いたいユーザが、ライブラリを変更なしで使えるからである。安全性の推論がなかったら、ライブラリの書き手が明示的にSafe Haskellを使わなければならないだろうが、これをHaskellコミュニティ全体に期待するのは合理的でない。そうでなければ、ライブラリのユーザがあなたのAPIをtrustworthyモジュールから再エクスポートするだけのラッパを書かなければならないが、これは鬱陶しい作業である。

7.27.6. Safe Haskellのフラグまとめ

まとめると、Safe Haskellは以下の三つの言語フラグと、
-XSafe
信頼されるためには全ての直接のインポート先が信頼されていなければならないが、モジュール自体は信頼済みパッケージに存在している必要はない。これが信頼に足るものであることはコンパイラが保証するからである。インポート文において"safe"キーワードは許されるが無意味であり、いずれにせよ全てのインポートが安全でなければならない。
  • モジュールが信頼されるか — される
  • Haskell言語 — safe languageに制限される
  • インポートされるモジュール — 全て強制的にsafeインポートになり、全てが信頼されていなければならない。
-XTrustworthy
これはモジュールが信頼されることを定めるが、その保証はモジュール作者によって与えられる。このモジュールのクライアントは次に、モジュールが含まれるパッケージを信頼すると指定することにより、このモジュール作者への信頼を指定する。-XTrustworthyはどのようなHaskellプログラムが受理されるかについて、およびそれらの意味について影響を与えない。例外は、safeインポートキーワードを許すことである。
  • モジュールが信頼されるか — される
  • モジュールが信頼されるか(-fpackage-trustが有効の場合) — されるが、モジュールのあるパッケージが信頼されている場合に限る。
  • Haskell言語 — 制限されない
  • インポートされるモジュール — どれが信頼されていなければいけないかは、モジュール作者に制御される。
-XUnsafe
モジュールが安全でないことを標示し、-XSafeでコンパイルされたモジュールからインポートできないようにする。またsafeインポート拡張を有効にし、依存関係が信頼されていることをモジュールが要求できるようにする。
  • モジュールが信頼されるか — されない
  • Haskell言語 — 制限されない
  • インポートされるモジュール — どれが信頼されていなければいけないかは、モジュール作者に制御される。
一つの一般フラグと、
-fpackage-trust
有効にされると、trustworthyなモジュールMに追加の検査を行う。Mが信頼されているとみなされるには、Mが所属するパッケージが信頼されているとみなされることが要求される。
二つの警告フラグからなる。
-fwarn-unsafe
コンパイル中のモジュールがunsafeであるとみなされる場合に警告する。安全性推論を使っている場合にモジュールの安全性状態を確かめるために使うべきである。
-fwarn-safe
コンパイル中のモジュールがsafeであるとみなされる場合に警告する。安全性推論を使っている場合にモジュールの安全性状態を確かめるために使うべきである。

7.27.7. 安全なコンパイル

GHCには、コンパイル時に任意のプロセスを走らせることを許すフラグが豊富にある。一つの例はプリプロセッサの変更フラグである。また、IOアクションを含む任意のHaskellコードをコンパイル時に走らせる能力のあるTemplate Haskellもそうである。Safe Haskellはこの危険性に対処しない(Template Haskellは禁止される機能だが)。

よって、信頼できないコードを、人力での検査なしでコンパイルする場合、以下の予防策を取ることが提案される。

  • chrootや類似のコンテナ技術などのサンドボックス内でコンパイルする。あるいは、単にシステムへのアクセスが非常に制限されたユーザとしてコンパイルする。
  • 信頼できないコードは-XSafeをコマンド行で指定してコンパイルする。コマンド行のフラグはソースレベルのプラグマよりも優先されるので、これによって、コンパイル対象のソースファイルの改変によってSafe Languageを無効にすることができないようになる。
  • 信頼できないコードが全てsafe importでインポートされ、なおかつ-fpackage-trustを使い、信頼できない供給元のパッケージが信頼されていないとみなされているようにする。

GHC Wikiには、コンパイル時安全性に関する問題点と、その解決策の可能性についてのさらに詳細な議論がある。