-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathsafe_haskell.xml
More file actions
411 lines (321 loc) · 42.9 KB
/
safe_haskell.xml
File metadata and controls
411 lines (321 loc) · 42.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
<?xml version="1.0" encoding="UTF-8"?>
<sect1 id="safe-haskell">
<title>Safe Haskell</title>
<indexterm><primary>safe haskell</primary></indexterm>
<para>Safe Haskellは、バージョン7.2でGHCに実装された言語拡張である。これは、コードが使うことが許されるGHC Haskellの機能を限定することによって、unsafeなコードを信頼されたコードベースに安全な形で含めることができるようにする。単純化して言うと、これはプログラム内の型を信頼に足るものにする。Safe Haskellは最小限のものであることを目標としていて、その上により高度な安全システムを構築するのに必要十分な強さの保証を提供する。</para>
<para>これがSafe Haskellの動機となる使用例であるものの、Safe Haskellが追跡し強制するものが、通常のHaskellで保証されるよりも厳格な型安全性であるということを理解するのは重要である。これの一環として、Safe HaskellはGHCによるコンパイルのたびに実行され、明示的にSafe Haskellを使っていないモジュールについてさえ安全性を追跡し推論する。これについてのさらなる詳細は<xref linkend="safe-inference"/>を参照して欲しい。これはまた、セキュリティの観点からは奇妙に見えるが型安全性を追跡するという角度からは論理的な設計判断があることも意味する。現在の設計について、またこのセキュリティと型安全性の間の緊張関係について、フィードバックを歓迎する。
</para>
<para>
Safe Haskellの設計は以下の面にわたっている。
<itemizedlist>
<listitem>コードに関する保証を提供するHaskellの<link linkend="safe-language">safe language</link>方言。これは、型およびモジュール境界を信頼することを可能にする。</listitem>
<listitem><emphasis>safe import</emphasis>拡張。これは、インポートされるモジュールが信頼されていなければならないことを指定する。</listitem>
<listitem><emphasis>信頼</emphasis>(あるいは安全性)と、それがどのように操作されるかの定義、さらにモジュールやパッケージの信頼を定義したり変更したりする方法。</listitem>
</itemizedlist>
</para>
<para>一方で、Safe Haskellは、コンパイル時安全性を<emphasis>提供しない</emphasis>。コンパイル時には、例えば<link linkend="pre-processor">プリプロセッサを変更する</link>フラグによって任意のプロセスが起動され得る。これを操作することで、コンパイル時にユーザのシステムを危険にさらしたり、コンパイルの直前にソースコードを改変することで設定されたSafe Haskellのフラグに変更を加えることができる。これについては、節<xref linkend="safe-compilation"/>にさらなる議論がある。
</para>
<sect2 id="safe-use-cases">
<title>Safe Haskellの用途</title>
<indexterm><primary>safe haskell uses</primary></indexterm>
<para>
Safe Haskellは以下の二つの利用例を念頭に置いて設計されている。
<itemizedlist>
<listitem>厳格な型安全性をコンパイル時に強制する</listitem>
<listitem>信頼されていないコードをコンパイルし、実行する</listitem>
</itemizedlist>
</para>
<sect3>
<title>厳格な型安全性(良いスタイル)</title>
Haskellは、強力な型システムと、<literal>IO</literal>モナドを通した純粋な関数と作用のある関数の分離を提供している。しかし、この型システムには抜け穴がいくつかある。最も分かり易いのは関数<literal>unsafePerformIO :: IO a -> a</literal>である。Safe Haskellのsafe language方言はこのような関数の利用を禁止する。これによってHaskellコードを分析したり論証したりし易くなるので、色々の目的のために便利である。また、このようなunsafeな関数はどうしても必要でない限り使うべきでないという既存のHaskellコミュニティの文化を成文化するものでもある。こういう訳で、(<option>-XSafe</option>を使った)safe languageの利用は、<option>-Wall</option>の機能と似て、良いスタイルを強制する手段だととらえることができる。
</sect3>
<sect3>
<title>セキュアなシステムを構築する(制約付きIOモナド)</title>
<indexterm><primary>secure haskell</primary></indexterm>
<para>情報フロー制御、capability-basedセキュリティ、暗号化されたデータを扱うためのDSL、といったシステムは、単に一つのライブラリとしてHaskell言語に組み込むことができる。しかしこれらのシステムは、<literal>unsafePerformIO</literal>のような関数が許される一般の場合には成り立たないようなHaskell言語の性質が保証されることを要求する。Safe Haskellは、コンパイルされたコードの性質に関して、このようなシステムを構築するのに十分なだけの保証を与えるように設計されている。</para>
<para>例として、プラグイン作者が信用されず、悪意のある第三者かもしれないようなプラグインシステムのためのインタフェースを定義してみよう。このために、プラグインのインタフェースを純粋な関数にするか、<literal>IO</literal>動作の安全なサブセットしか実行することを許されないような制限された<literal>IO</literal>モナドにする。ここではプラグインのインタフェースを以下のようにする。すなわち、プラグインモジュール<literal>Danger</literal>は、型<literal>RIO ()</literal>を持つ単一の計算<literal>Danger.runMe</literal>を定義することを要求される。ただし<literal>RIO</literal>は以下のように定義された新しいモナドである。
</para>
<programlisting>
-- 以下の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 ()
</programlisting>
Dangerを、新しいSafe Haskellのフラグである<option>-XSafe</option>付きでコンパイルする
<programlisting>
{-# LANGUAGE Safe #-}
module Danger ( runMe ) where
runMe :: RIO ()
runMe = ...
</programlisting>
<para>Safe Haskellの詳細に入る前に、この設計がSafe Haskellなしではうまく行かない理由の一部を指摘しておこう。</para>
<itemizedlist>
<listitem>この設計は、Dangerが実行することのできる操作を型によって、具体的には<literal>IO</literal>のラッパである<literal>RIO</literal>によって制限している。Dangerの作者は、単に<literal>unsafePerformIO :: IO a -> a</literal>を使って任意の<literal>IO</literal>アクションを純粋関数として実行することでこの制限を覆すことができる。
</listitem>
<listitem>またこの設計はDangerモジュールが<literal>UnsafeRIO</literal>構築子にアクセスできないことを前提としている。残念なことにTemplate Haskellを使うとモジュール境界を破壊することができ、この構築子へのアクセスを得ることもできるだろう。
</listitem>
<listitem>信用されていないDangerモジュールがインポートできるモジュールに制限を掛ける方法がない。これによって非常に広いattack surface(実質的に、システムにインストールされている全てのパッケージ)をDangerの作者に与えることになる。仮にこれらのパッケージのいずれかに脆弱性があれば、Dangerモジュールはそれを利用できる。これを防ぐ唯一の方法は、既知の脆弱性のあるモジュールにパッチを当てるか削除することである。それらがRIOモジュールなどの信頼されたモジュールからのみ使われるべきものであったとしても。
</listitem>
</itemizedlist>
<para>これらの攻撃を止めるためにSafe Haskellを使うことができる。これには、RIOモジュールを<option>-XTrustworthy</option>フラグ付きでコンパイルし、Dangerモジュールを<option>-XSafe</option>フラグ付きでコンパイルする。</para>
<para><option>-XSafe</option>フラグを使うことで、利用可能なHaskellの機能を<link linkend="safe-language">安全なサブセット</link>に限定してDangerモジュールをコンパイルすることができる。これには、<literal>unsafePerfromIO</literal>、Template Haskell、純粋なFFI関数、一般化newtype deriving、RULESを禁止し、重複インスタンスの操作に制限を加えることを含む。さらに、<option>-XSafe</option>は、Dangerがインポートできるモジュールを信頼されているとみなされるもののみに制限する。信頼されるモジュールは、<option>-XSafe</option>付きでコンパイルされたもの(この場合、コードが安全であることをGHCが機械的に保証する)または、<option>-XTrustworthy</option>付きでコンパイルされたもの(この場合、モジュール作者がこのモジュールが安全だと主張する)である。</para>
<para>これが、RIOモジュールがDangerモジュールからインポートできるように<option>-XTrustworthy</option>付きでコンパイルされている理由である。<option>-XTrustworthy</option>は、<option>-XSafe</option>と異なり、モジュールについていかなる制限も課さない。代わりに、コードが内部的にunsafeな機能を使っていても、安全に使えるAPIしか露出していないということをモジュール作者が主張する。<option>-XTrustworthy</option>自体が、そのモジュールを信頼されたものにするのである。ここで、問題が一つある。あらゆるモジュールのあらゆる作者が<option>-XTrustworthy</option>を使えるのである。trustworthyモジュールの利用を制御するため、<option>-fpackage-trust</option>フラグを使うことが推奨される。このフラグは、trustworthyモジュールの信頼検査へ条件を追加する。その場合、Trustworthyとされたモジュールが信頼され、<option>-XSafe</option>でコンパイルされるコードから使うことを許されるためには、コードをコンパイルしているクライアントCが、このTrustworthyとされたモジュールが所属するパッケージを信頼する旨をGHCに伝えなければならない。これは実質的に、「このパッケージは<option>-XSafe</option>でコンパイルされる信頼されないモジュールによって使えるTrustworthyモジュールを含んでいるが、私はこのパッケージの作者を信頼し、このモジュールが安全なAPIしか露出しないことを信頼する」と言う手段をCに与える。パッケージへの信頼はいつでも変えられるので、もしパッケージに脆弱性が見付かったら、Cはそのパッケージが信頼されないと宣言し、そのパッケージに対する将来のいかなるコンパイルも失敗するようにできる。この機構についてのより詳細な概観は<xref linkend="safe-trust"/>を見よ。
</para>
<para>例では、RIOはTrustworthyと標示されているのでDangerはRIOをインポートできる。よって、DangerはrioReadFileとrioWriteFileの各関数を使って許可されたファイル名にアクセスできる。次に主アプリケーションがRIOとDangerの両方をインポートし、RIO.runRIO Danger.runMeをIOモナド中で呼ぶことでプラグインを走らせる。pathOKの検査によって認められたパスを持つファイルにしかIOできないと分かっているので、このアプリケーションは安全である。</para>
</sect3>
</sect2>
<sect2 id="safe-language">
<title>safe language</title>
<indexterm><primary>safe language</primary></indexterm>
Safe Haskellの<emphasis>safe language</emphasis>は以下の性質を保証する。
<itemizedlist>
<listitem><emphasis>参照透明性</emphasis> — safe languageの関数は決定的であり、それらを評価することはいかなる副作用も引き起こさない。<literal>IO</literal>モナドの関数は通常通りに振る舞うことが許される。しかし、純粋な関数は全て、その型に従って実際に純粋であることが保証される。この性質によって、safe languageの利用者は型を信頼することが可能になる。これは、例えば<literal>unsafePerformIO :: IO a -> a</literal>がsafe languageにおいて禁止されることを意味している。
</listitem>
<listitem><emphasis>モジュール境界の制御</emphasis> — safe languageを使ってコンパイルされたHaskellコードは、他のモジュールのエクスポートリストから公的に利用可能なシンボルにしかアクセスできないことが保証される。このうち重要な点として、safeでコンパイルされたコードは、自分がインポートできない構築子を使ってデータ値を検査したり作ったりすることができない。モジュールMが、エクスポートリストを注意深く使うことである不変条件を確立しているなら、safe languageを使ってコンパイルされたコードでMをインポートするものは、これらの不変条件を守ることが保証される。このために、<emphasis><link linkend="template-haskell">Template Haskell</link></emphasis>はsafe languageにおいては無効にされる。これらを使ってこの性質に違反することができるからである。
</listitem>
<listitem><emphasis>意味的な整合性</emphasis> — safe languageはHaskellのGHC実装の厳密なサブセットである。safe languageでコンパイル可能なあらゆる式は、通常のHaskellでコンパイルされた場合と全く同じ意味を持つ。さらに、safe languageモジュールをインポートするあらゆるモジュールにおいて、このsafe importを付けても付けなくてもコンパイル可能なあらゆる式は、どちらの場合でも同じ意味を持つ。つまり、safe languageを使ったモジュールをインポートすることで、そのモジュールに依存していない既存コードの意味を変えることはできない。よって例えば、<emphasis><link linkend="instance-overlap">重複インスタンス</link></emphasis>拡張はこの性質に違反できるので、いくらかの制限が課される。
</listitem>
</itemizedlist>
<para>これらの三つの性質は、safe languageにおいて、型を信頼できること、コードがモジュールのエクスポートリストに従うこと、コンパイルに成功したコードが通常持つべき意味と同じ意味を持つこと、を保証する。</para>
ではsafe languageの詳細を見ていこう。safe lauguage方言(<option>-XSafe</option>によって有効になる)においては、以下の機能は完全に無効になる。
<itemizedlist>
<listitem><emphasis>TemplateHaskell</emphasis> — これは、コンパイル時に副作用を起こすことを可能にし、抽象データ型の構築子にアクセスするために使えるので特に危険である。</listitem>
</itemizedlist>
safe language方言では以下の機能に制限が加えられる。
<itemizedlist>
<listitem><emphasis>ForeignFunctionInterface</emphasis> — これは大部分において安全であるが、関数をIOでない型でインポートするforeign import宣言は禁止される。全てのFFIインポートはIOモナドに生息しなければならない。</listitem>
<listitem><emphasis>RULES</emphasis> — 信頼されたコードの振る舞いを予測不可能な形で変更し、意味上の整合性に違反することができるので、機能が制限される。具体的には、<option>-XSafe</option>でコンパイルされたモジュールMで定義されたRULESは全て捨てられる。MがインポートするTrustworthyモジュールで定義されたRULESはなお有効であり、通常通り発動する。</listitem>
<listitem><emphasis>OverlappingInstances</emphasis> — この拡張は意味上の整合性に違反するために使うことができる。悪意のあるコードは、この信頼されていないモジュールをインポートしているコードの振る舞いを変えるようなインスタンスを再定義できるかもしれない(より特殊性の高いインスタンス宣言を持つことで)からである。<option>-XSafe</option>でコンンパイルされたモジュールMにおいて、この拡張は無効化されないが制限を受ける。Mは重複インスタンス宣言を定義できるが、それらはMで定義された別のインスタンス宣言としか重複できない。MをインポートするモジュールNの中、型クラス関数を使う呼び出し地点で、どのインスタンスを使うかの選択肢(つまり重複)があり、最も特殊性の高いインスタンスがM由来の場合、他の全ての選択肢もM由来でなければならない。これについての簡単な考え方は、Safeコンパイルされたモジュールで定義された重複インスタンスに関する<emphasis>同一生成元ポリシー</emphasis>(same origin policy)だと思うことである。</listitem>
<listitem><emphasis>Data.Typeable</emphasis> — Typeableインスタンスを自動導出(GHCによって<link linkend="deriving-typeable"><option>-XDeriveDataTypeable</option></link>拡張を介して提供されている)されたもののみに制限している。Typeable型クラスの手書きインスタンスは、unasfeに型変換を行うように簡単に悪用できるので、Safe Haskellでは許されていない。</listitem>
</itemizedlist>
</sect2>
<sect2 id="safe-imports">
<title>safeインポート</title>
<indexterm><primary>safe imports</primary></indexterm>
Safe Haskellは、Haskellの通常のインポート構文に対する小さな拡張を有効にする。次のように<emphasis>safe</emphasis>キーワードを加える。
<programlisting>
impdecl -> import [safe] [qualified] modid [as modid] [impspec]
</programlisting>
使われると、safeキーワード付きでインポートされるモジュールは信頼されたものでなければならず、そうでなければコンパイルエラーが発生する。このsafeインポート拡張は<option>-XSafe</option>、<option>-XTrustworthy</option>、<option>-XUnsafe</option>のいずれか、あるいは対応するプラグマによって有効になる。<option>-XSafe</option>フラグが使われている場合、このsafeキーワードは許されるが無意味である。いずれにせよ全てのインポートが安全でなければならない。
</sect2>
<sect2 id="safe-trust">
<title>信頼とSafe Haskellのモード</title>
<indexterm><primary>safe haskell trust</primary></indexterm>
<indexterm><primary>trust</primary></indexterm>
Safe Haskell拡張は以下の三つの新しい言語フラグを導入する。
<itemizedlist>
<listitem><emphasis>-XSafe</emphasis> — safe language方言を有効にし、GHCに信頼を保証するように頼む。safe language方言は全てのインポートが信頼されていることを要求し、そうでなければコンパイルエラーが発生する。</listitem>
<listitem><emphasis>-XTrustworthy</emphasis> — このモジュールは内部的にunsafeな関数を呼びかもしれないが、unsafeな形で使うことのできないAPIしかエクスポートしないとモジュールの作者が主張していることを意味する。これはsafe languageを有効にせず、どんなHaskellコードが許されるかについて制限を加えることもない。信頼の保証はGHCによってではなくモジュール作者によって与えられる。safeキーワードの付いたインポート文は、インポート先モジュールが信頼されていない場合にコンパイルエラーになる。このキーワードの付いていないインポート文は通常通りに振る舞い、信頼されているかどうかにかかわらず、あらゆるモジュールをインポートできる。</listitem>
<listitem><emphasis>-XUnsafe</emphasis> — コンパイルされているモジュールが安全でなく、したがって<option>-XSafe</option>を使ってコンパイルされているモジュールからインポートできないことを標示する。
</listitem>
</itemizedlist>
<para>あるモジュールが信頼されるかどうかの検査の手続きは、<option>-fpackage-trust</option>フラグが与えられているかどうかに依存する。どちらの場合の検査も良く似ており、<option>-fpackage-trust</option>の存在は、trustworthyモジュールが信頼されているとみなされるための要件を一つ追加するのみである。
</para>
<sect3>
<title>信頼検査(<option>-fpackage-trust</option>が無効の場合)</title>
<indexterm><primary>trust check</primary></indexterm>
<emphasis>パッケージPのモジュールMがクライアントCに信頼される</emphasis>のは、以下の条件が満されるとき、かつそのときに限る。
<itemizedlist>
<listitem>以下が両方とも成り立つ
<itemizedlist>
<listitem>このモジュールが<option>-XSafe</option>付きでコンパイルされている
</listitem>
<listitem>Mが直接インポートしているモジュールが全てCに信頼されている</listitem>
</itemizedlist>
</listitem>
<listitem><emphasis>あるいは</emphasis>以下が全て成り立つ
<itemizedlist>
<listitem>このモジュールが<option>-XTrustworthy</option>付きでコンパイルされている
</listitem>
<listitem>Mが直接safeインポートしているモジュールが全てCに信頼されている</listitem>
</itemizedlist>
</listitem>
</itemizedlist>
<para>信頼を上のように定義することには問題がある。どんなモジュールでも-XTrustworthy付きでコンパイルすることができ、そうするとそれが何をするかにかかわらず信頼されるのである。これを制御するために、パッケージへの信頼についての追加的定義がある(<option>-fpackage-trust</option>で有効になる)。パッケージへの信頼の要点は、どのパッケージがtrustworthyモジュールを含めるかをクライアントCが明示的に言うことである。つまり、Cは、パッケージPとその作者を信頼するのでP内の<option>-XTrustworthy</option>を使ったモジュールを信頼する、と決める。パッケージへの信頼が有効の場合、trustworthyだとみなされるものの信頼されていないパッケージにあるモジュールは信頼されていないとみなされる。より形式的な定義は次の節で与えられる。
</para>
</sect3>
<sect3>
<title>信頼検査(<option>-fpackage-trust</option>が有効の場合)</title>
<indexterm><primary>trust check</primary></indexterm>
<indexterm><primary>-fpackage-trust</primary></indexterm>
<para><option>-fpackage-trust</option>が有効なら、あるモジュールが信頼されるかどうかは、パッケージへの信頼という概念に依存する。これはGHCを起動するクライアントC(つまり、あなたである)によって決定される。パッケージ<emphasis>P</emphasis>が信頼されるのは、以下のどれかが成立する場合である。
<itemizedlist>
<listitem>Cのパッケージデータベースのレコードが<emphasis>P</emphasis>が信頼されていると記録している(しかも、コマンド行引数によって塗り替えられていない)場合。</listitem>
<listitem>Cのコマンド行フラグが、パケージデータベースの記録に関係なく信頼しろと言っている場合。</listitem>
</itemizedlist>
<para>どちらの場合でも、パッケージの信頼に関して決定権はCにしかない。どの<link linkend="safe-package-trust">パッケージを信頼する</link>かはクライアントによる。</para>
<option>-fpackage-trust</option>フラグが使われているとき、<emphasis>パッケージPのモジュールMがクライアントCに信頼される</emphasis>のは、以下の条件が満されるとき、かつそのときに限る。
<itemizedlist>
<listitem>以下が両方とも成り立つ
<itemizedlist>
<listitem>このモジュールが<option>-XSafe</option>付きでコンパイルされている
</listitem>
<listitem>Mが直接インポートしているモジュールが全てCに信頼されている</listitem>
</itemizedlist>
</listitem>
<listitem><emphasis>あるいは</emphasis>以下が全て成り立つ
<itemizedlist>
<listitem>このモジュールが<option>-XTrustworthy</option>付きでコンパイルされている
</listitem>
<listitem>Mが直接safeインポートしているモジュールが全てCに信頼されている</listitem>
<listitem>パッケージPがCに信頼されている</listitem>
</itemizedlist>
</listitem>
</itemizedlist>
</para>
<para>
一番目の信頼の定義については、信頼の保証は、safe languageによって課される制約を通してGHCによって与えられる。二番目の定義については、この保証はまずモジュール作者によって与えられる。次にクライアントCが、このモジュールを含むパッケージを信頼することを示すことによって、モジュール作者への信頼を明示する。この信頼の連鎖が必要なのは、<literal>-XTrustworthy</literal>でコンパイルされたモジュールに関してGHCはいかなる保証も提供しないからである。
</para>
<para>信頼検査に二つのモードがある理由は、<option>-fpackage-trust</option>によって有効になる追加の要件が、Safe Haskellの設計を侵襲的なものにするからである。このフラグが有効な場合、Safe Haskellを使っているパッケージは、ユーザの機械上のパッケージの信頼状態によってコンパイルが通ったり通らなかったりする。パッケージ<literal>foo</literal>のメンテナが、セキュリティ意識のあるHaskellerが<literal>foo</literal>を使えるようにSafe Haskellを使うと、Safe Haskellを知りも構いもしないユーザに、fooが必要とするパッケージ<literal>bar</literal>が彼らの機械上で信頼されていないために、<literal>foo</literal>がコンパイルできないと文句を言われることになる。この意味で、<option>-fpackage-trust</option>はSafe Haskellを正式に有効にするフラグだと思うことができる。これなしでは、Safe Haskellはこっそりとしたやりかたで動作する。
</para>
<para>
Having the <option>-fpackage-trust</option> flag also nicely unifies the
semantics of how Safe Haskell works when used explicitly and how modules
are <ulink linkend="safe-inference">inferred as safe</ulink>.(訳注: 訳せません。助けて)
</para>
</sect3>
<sect3 id="safe-trust-example">
<title>例</title>
<programlisting>
パッケージWuggle:
{-# LANGUAGE Safe #-}
module Buggle where
import Prelude
f x = ...blah...
パッケージP:
{-# LANGUAGE Trustworthy #-}
module M where
import System.IO.Unsafe
import safe Buggle
</programlisting>
<para>クライアントCがパッケージPを信頼することを決めたとしよう。ではCはモジュールMを信用するか?これを決めるために、GHCはMのインポートを検査しなければいけない。MはSystem.IO.Unsafeをインポートしている。Mは<option>-XTrustworthy</option>付きでコンパイルされているから、このインポートについてはMの作者が責任を負う。CはPの作者を信頼しているので、CはMがunsafeなインポートを安全かつMが露出するAPIに整合的な形でしか使わないということを信頼している。Mには、BuggleへのSafeインポートもある。safeインポートなのでPの作者はこれの安全性について責任を負わないため、BuggleがCに信頼されているかをGHCが検査しないといけない。どうか?うーん、これは<option>-XSafe</option>付きでコンパイルされているので、Buggle自体のコードは問題ないことが機械的に検査されている。ただしこれもBuggleのインポートが全てCに信頼されていることが条件である。Preludeはbase由来であり、Cはこれを信頼しており、<option>-XTrustworthy</option>付きでコンパイルされている。(Preludeは典型的には暗黙にインポートされるが、その場合でもここで述べた規則に沿う)。よってBuggleは信頼されているとみなされる。</para>
<para>CはパッケージWuggleを信頼する必要がなかったことに注意。機械による検査だけで十分である。Cは、<option>-XTrustworthy</option>なモジュールを含むパッケージだけを信頼すれば良い。</para>
</sect3>
<sect3 id="trustworthy-guarantees">
<title>Trustworthyの要件</title>
<indexterm><primary>trustworthy</primary></indexterm>
言語拡張<option>-XTrustworthy</option>を使うモジュールMの作者は、Mの公開API(エクスポートリストによって露出されているシンボル群)が安全でない方法で使えないことを保証するべきである。これは、エクスポートされているシンボルが型安全性と参照透明性に従うべきだということである。
</sect3>
<sect3 id="safe-package-trust">
<title>パッケージへの信頼</title>
<indexterm><primary>package trust</primary></indexterm>
Safe Haskellは各パッケージに新しい真偽値のプロパティを与える。信頼プロパティである。GHCのコマンド行でパッケージの信頼プロパティを指定するためのオプションがいくつか新たに利用可能である。
<itemizedlist>
<listitem><emphasis>-trust P</emphasis> — パッケージPを隠されているなら露出し、パッケージデータベースに関係なく、信頼されているとみなす。</listitem>
<listitem><emphasis>-distrust P</emphasis> — パッケージPを隠されているなら露出し、パッケージデータベースに関係なく、信頼されていないとみなす。</listitem>
<listitem><emphasis>-distrust-all-packages</emphasis> — 後続のコマンド行オプションで明示的に信頼されていると指定されていない限り、全てのパッケージを信頼されていないとみなす。</listitem>
</itemizedlist>
パッケージの信頼プロパティをパッケージデータベース中で設定するには、<xref linkend="packages"/>を参照してほしい。
</sect3>
</sect2>
<sect2 id="safe-inference">
<title>Safe Haskellの推論</title>
<indexterm><primary>safe inference</primary></indexterm>
<para><option>-XSafe</option>、<option>-XTrustworthy</option>、<option>-XUnsafe</option>のどれもが使われずにモジュールがコンパイルされる場合、GHCはそのモジュールが安全だとみなせるかを自分で見出そうとする。この安全性の推論はモジュールを決してtrustworthyだとは標示せず、unsafeかsafeだとする。これをモジュールMについて決定するために、GHCは以下の単純な方法を使う。すなわち、Mが<option>-XSafe</option>フラグの下でもエラーなしでコンパイルできるなら、Mはsafeであると標示する。Mが<option>-XSafe</option>フラグの下ではコンパイルに失敗するなら、unsafeであると標示する。
</para>
<para>Safe Haskellの推論を使うべきなのは何時で、明示的な<option>-XSafe</option>フラグを使うべきなのは何時か?そのモジュールが安全でなければならないという動かせない要求があるなら、後者を使うべきである。つまり、概観した<ulink linkend="safe-use-cases">利用例</ulink>や、Safe Haskellが意図している目的、すなわち信頼されていないコードをコンパイルする場合である。安全性の推論はふつうのHaskellプログラマに使われることを意図している。通常Safe Haskellを気にかけないユーザである。
</para>
<para>Haskellライブラリを書いているとしよう。その場合、あなたは単に安全性推論を使いたいと思うだろう。安全でない言語機能を避けるなら、あなたのモジュールはsafeと標示される。これは良いことである。なぜなら、あなたのライブラリを信頼されないコードに露出されるAPIの一部として使いたいユーザが、ライブラリを変更なしで使えるからである。安全性の推論がなかったら、ライブラリの書き手が明示的にSafe Haskellを使わなければならないだろうが、これをHaskellコミュニティ全体に期待するのは合理的でない。そうでなければ、ライブラリのユーザがあなたのAPIをtrustworthyモジュールから再エクスポートするだけのラッパを書かなければならないが、これは鬱陶しい作業である。
</para>
</sect2>
<sect2 id="safe-flag-summary">
<title>Safe Haskellのフラグまとめ</title>
<indexterm><primary>safe haskell flags</primary></indexterm>
まとめると、Safe Haskellは以下の三つの言語フラグと、
<variablelist>
<varlistentry>
<term>-XSafe</term>
<indexterm><primary>-XSafe</primary></indexterm>
<listitem>信頼されるためには全ての直接のインポート先が信頼されていなければならないが、モジュール自体は信頼済みパッケージに存在している必要はない。これが信頼に足るものであることはコンパイラが保証するからである。インポート文において"safe"キーワードは許されるが無意味であり、いずれにせよ全てのインポートが安全でなければならない。
<itemizedlist>
<listitem><emphasis>モジュールが信頼されるか</emphasis> — される</listitem>
<listitem><emphasis>Haskell言語</emphasis> — safe languageに制限される</listitem>
<listitem><emphasis>インポートされるモジュール</emphasis> — 全て強制的にsafeインポートになり、全てが信頼されていなければならない。</listitem>
</itemizedlist>
</listitem>
</varlistentry>
<varlistentry>
<term>-XTrustworthy</term>
<indexterm><primary>-XTrustworthy</primary></indexterm>
<listitem>これはモジュールが信頼されることを定めるが、その保証はモジュール作者によって与えられる。このモジュールのクライアントは次に、モジュールが含まれるパッケージを信頼すると指定することにより、このモジュール作者への信頼を指定する。<option>-XTrustworthy</option>はどのようなHaskellプログラムが受理されるかについて、およびそれらの意味について影響を与えない。例外は、safeインポートキーワードを許すことである。
<itemizedlist>
<listitem><emphasis>モジュールが信頼されるか</emphasis> — される</listitem>
<listitem><emphasis>モジュールが信頼されるか(<option>-fpackage-trust</option>が有効の場合)</emphasis> — されるが、モジュールのあるパッケージが信頼されている場合に限る。</listitem>
<listitem><emphasis>Haskell言語</emphasis> — 制限されない</listitem>
<listitem><emphasis>インポートされるモジュール</emphasis> — どれが信頼されていなければいけないかは、モジュール作者に制御される。</listitem>
</itemizedlist>
</listitem>
</varlistentry>
<varlistentry>
<term>-XUnsafe</term>
<indexterm><primary>-XUnsafe</primary></indexterm>
<listitem>モジュールが安全でないことを標示し、<option>-XSafe</option>でコンパイルされたモジュールからインポートできないようにする。またsafeインポート拡張を有効にし、依存関係が信頼されていることをモジュールが要求できるようにする。
<itemizedlist>
<listitem><emphasis>モジュールが信頼されるか</emphasis> — されない</listitem>
<listitem><emphasis>Haskell言語</emphasis> — 制限されない</listitem>
<listitem><emphasis>インポートされるモジュール</emphasis> — どれが信頼されていなければいけないかは、モジュール作者に制御される。</listitem>
</itemizedlist>
</listitem>
</varlistentry>
</variablelist>
一つの一般フラグと、
<variablelist>
<varlistentry>
<term>-fpackage-trust</term>
<indexterm><primary>-fpackage-trust</primary></indexterm>
<listitem>有効にされると、trustworthyなモジュールMに追加の検査を行う。Mが信頼されているとみなされるには、Mが所属するパッケージが信頼されているとみなされることが要求される。
</listitem>
</varlistentry>
</variablelist>
二つの警告フラグからなる。
<variablelist>
<varlistentry>
<term>-fwarn-unsafe</term>
<indexterm><primary>-fwarn-unsafe</primary></indexterm>
<listitem>コンパイル中のモジュールがunsafeであるとみなされる場合に警告する。安全性推論を使っている場合にモジュールの安全性状態を確かめるために使うべきである。
</listitem>
</varlistentry>
<varlistentry>
<term>-fwarn-safe</term>
<indexterm><primary>-fwarn-safe</primary></indexterm>
<listitem>コンパイル中のモジュールがsafeであるとみなされる場合に警告する。安全性推論を使っている場合にモジュールの安全性状態を確かめるために使うべきである。
</listitem>
</varlistentry>
</variablelist>
</sect2>
<sect2 id="safe-compilation">
<title>安全なコンパイル</title>
<indexterm><primary>safe compilation</primary></indexterm>
<para>GHCには、コンパイル時に任意のプロセスを走らせることを許すフラグが豊富にある。一つの例は<link linkend="pre-processor">プリプロセッサの変更</link>フラグである。また、IOアクションを含む任意のHaskellコードをコンパイル時に走らせる能力のあるTemplate Haskellもそうである。Safe Haskellは<emphasis>この危険性に対処しない</emphasis>(Template Haskellは禁止される機能だが)。
</para>
<para>よって、信頼できないコードを、人力での検査なしでコンパイルする場合、以下の予防策を取ることが提案される。
<itemizedlist>
<listitem>chrootや類似のコンテナ技術などのサンドボックス内でコンパイルする。あるいは、単にシステムへのアクセスが非常に制限されたユーザとしてコンパイルする。</listitem>
<listitem>信頼できないコードは<option>-XSafe</option>をコマンド行で指定してコンパイルする。コマンド行のフラグはソースレベルのプラグマよりも優先されるので、これによって、コンパイル対象のソースファイルの改変によってSafe Languageを無効にすることができないようになる。</listitem>
<listitem>信頼できないコードが全て<link linkend="safe-imports">safe import</link>でインポートされ、<emphasis>なおかつ</emphasis><link linkend="safe-package-trust"><option>-fpackage-trust</option></link>を使い、信頼できない供給元のパッケージが信頼されていないとみなされているようにする。</listitem>
</itemizedlist>
</para>
<para><ulink url="http://ghc.haskell.org/trac/ghc/wiki/SafeHaskell/SafeCompilation">GHC Wiki</ulink>には、コンパイル時安全性に関する問題点と、その解決策の可能性についてのさらに詳細な議論がある。</para>
</sect2>
</sect1>
<!-- Emacs stuff:
;;; Local Variables: ***
;;; sgml-parent-document: ("users_guide.xml" "book" "chapter" "sect1") ***
;;; End: ***
-->