* OSECPUのセキュリティ -(by [[K]], 2013.07.08) ** (1) 一般的なセキュリティ -OSECPUはASLRがいらない(そんなことしなくても守りきれる) --だからアドレス空間が少ししかなくても問題ない --ページングをサポートしていないCPUでも平気 -OSECPUはNX-bitとかセグメンテーションによるハードウェア保護がいらない --これらの機能を持たない幅広いCPUに対応可能 --LMEM/SMEM命令のポインタに対する自動チェックが、これらを完全に代用しているとも言える -OSECPUには特権モードがない --それでもシステムはアプリに対してデータを隠せる、隠したまま渡すこともできる ---隠したまま渡す = ハンドルとしての使い道(システムからはいつでも中身が見える) --逆にアプリはシステムに対してデータを隠すことができる、でもこれがメリットなのかどうかは不明 --この特権モードがないという設計によって、rootを取りに行くという攻撃手法が存在しない -OSECPUではuse-after-free脆弱性が発生しない --指している先のメモリがfreeされると、自動的にポインタが使えなくなるから --同じ仕組みによって、double-free脆弱性も発生しない -OSECPUの無限ループ対策 --ウォッチドッグタイマーなどは不要 -関連:[[page0018]] ** (2) (川合の考える)拡張されたセキュリティ -たとえばバッファオーバーラン脆弱性があったとして、プログラマにとっては、これは意図しないバグだと思います(まさか、わざとやっていたわけじゃないし、有効な対応法がないというわけでもない)。 -こういうのを早期に(=開発中に)誰かが教えてくれたら、こんな脆弱性を作りこまずに済んだはずです。 -となれば、もしもそういう便利機能があれば、それは十分に立派なセキュリティ対策と言えると、僕は考えます。 -これをさらに発展させて、ユーザやプログラマが不便な思いをしないように手助けしてくれるすべての機能が、僕にとってはすべてセキュリティ機能です。たとえば一般的なデバッグ支援だって、セキュリティ機能だと言ってもいいと(僕は)思うのです。誤って削除しちゃったファイルの復元ツールとかも。 --というか、そういうふうにしていかないと、セキュアなOSなんてみんな似たり寄ったりでおもしろくないのではないかと・・・。 ** (3) ガーベージコレクション方式 vs free方式 -これをここで少し議論しようと思う。 -まずアプリのサイズを考えると、実はGC方式の方が有利である。なぜならfreeの分だけ記述が少なくて済むから。それは百も承知の上で、しかも[[page0040]]なほどサイズに執着しているにもかかわらず、OSECPUはGC方式を採用しなかった。 -なぜか?GC方式には欠点があるからだ。定期的にポインタを精査してfreeできるオブジェクトがあるかどうかを検査しなければいけない。このCPU負荷は気にしなくていいのだろうか?オブジェクトが少ないうちは大したことないだろうが、多くなったらどうするのだろうか?そもそもオブジェクトが少ないうちは、free方式での管理もそんなに大変ではないので、GC方式のメリットはあまり大きくない。 -僕は以前Javaで大量のオブジェクトを作っては捨て、作っては捨ての作業をすることになったのだが、このコードの処理速度があるスピードを超えた時に、ついにGCスレッドが追いつかなくなって、メインスレッドが停止されられて、一定期間応答しなくなってしまった。なんということだ。 -僕はどのオブジェクトが必要で、どのオブジェクトが不要なのかということくらい、全部わかっていた。しかしJavaでは「今すぐこれを捨てろ、その代わり、GCスレッドはおとなしくしていろ」ということができない。僕はこれが嫌だ。自分でメモリ管理ができない(orしたくない)プログラマにとってはGCは夢の技術かもしれないが、僕にとってはただのおせっかいで足かせにしかなってない。 -ということで、OSECPUではGC方式を採用しないことにした。自分が使いたくないOSなんか作ってもしょうがないから。 --註:僕はJavaのGCに楽をさせるためのテクニックなども知っている。使い終わったポインタにnullを代入してやるとか、そもそもnewなどせずに自前でリソース管理して使いまわすとか。しかし、オブジェクトをnewして使っているのは、僕が呼び出したライブラリの中であって、僕がおいそれとは手出しできない場所にあった。だいいちnullを代入させるって、それは手間においてfreeと大差ない気がする。リソース管理を自前でやるというのは、それはもはやfree方式よりも面倒なのではないか?? -OSECPUではfree方式を採用したものの、もちろんそれにともなうバグがあることは僕にも十分にわかっていたので、そのバグをできるだけ容易に見つけられるようにしようと思い、use-after-freeとdouble-freeの検出機能を付けた。しかもこれはただそういうバグがあったと報告するにとどまらず、問題の原因になったfreeはこれだと思いますという情報までくれる。したがって、早すぎたfreeについての対策はほぼ完全と言えると思う。残った問題はfreeし忘れ、つまりリークのほうである。ここで開放するべきだったのでは?と示唆できるようになればいいのだけど・・・。 -しかしまあ、リークしてしまったメモリに対して、このメモリをmallocしたのはここでのmalloc呼び出しですよ、くらいの報告はできる。 ** (4) ユーザはどこまでOSに頼っていいのか。どこからは自分が注意しなければいけないのか。 -ということを考えてみたメモ: -アプリ開発者の視点: --(1-1) OSECPUは幅広いCPUとOSに対応可能なので、一度作ったアプリを移植する手間はおそらく発生しない。だからそのときにバグを作りこんでしまう心配はない。 ---つまり移植に関してはOS任せにしてしまっていい。 --(1-2) ガーベージコレクト方式を採用していないので、メモリ解放を自分でしなければいけないが、しかしどのタイミングでメモリを開放するかを十分に制御できる。 ---解放タイミングを間違えたことによるバグについては、use-after-freeとdouble-freeに関しては、OSが検出して教えてくれる。 ---解放タイミングを間違えたことによるバグについて、use-after-freeとdouble-freeに関しては、OSが検出して教えてくれる。 --(1-3) あるアプリが他のアプリのワークエリアなどをバグで破壊してしまう心配はない。 ---x86ではこんなことはもはや当たり前で、何をいまさらと思うかもしれないが、ページングがないCPUでもこれを保証してもらえるのは間違いなくうれしい。 --(1-4) たとえば32bitのintで利用しているメモリに対して、16bitや8bitのアクセスをしてしまうことはない。これはエンディアンが異なるアーキテクチャへ移行しても移植上の問題が発生しないことを意味する。 ---もっと一般的に言えば、異なる型でアクセスする行為がすべて禁止されているので、この手のバグを心配する必要がない。 --(1-5) たとえばT_UINT8なメモリに対して、-1を書き込むことは現状では禁止されていない。 ---そのような場合、おそらくこのメモリを読みだせば255が返ってくると思うが、これは厳密に考えればアーキテクチャ依存である。 ---速度低減と引き換えにしてでも、これのチェックを入れて、禁止するべきだろうか・・・悩ましい。 ---とにかく現状では、この手のバグについては人間が気にする必要がある。 --(1-6) コードの一部を自らのバグによって改変してしまっている可能性を心配する必要はない。 ---一部でも実行されはじめたコードはその後どの部分も変更されない。 -アプリ利用者の視点: --(2-1) ユーザは信用できるアプリと信用できないアプリを区別する必要がある。 ---自分が作ったアプリは基本的に信用できるだろう。ソースコードが公開されて、自分でチェックしたアプリも信用できるだろう。自分が信用している人がチェックしてくれたものも信用できるだろう。 ---しかしそれ以外の物はすべて信用するべきではない。 ---この区別を何度もやるのはきっと大変なので、何か設定ファイルみたいなものを作って、それに書いてあるものはわざわざ問い合わせない。 --(2-2) 信用できないアプリには、壊されては困るようなデータファイルをいじらせてはいけないし、明示されていない範囲へのデータの出力も許さない。インターネット接続なんてもってのほかだ。 --(a) 悪意あるソフトウェアはユーザの大事なデータを壊したい。 --(b) 悪意あるソフトウェアはユーザのシステムを破壊したい。 --(c) 悪意あるソフトウェアはユーザの大事なデータを盗みたい。 --この3点への対策が重要となりそう。 --(b)に関しては、アプリがシステムや他のアプリへの干渉や設定変更を許さないことで可能になる。これはたぶんそれほど難しくはない。 --(a)に関しては、アプリがファイルへのライトアクセスをするときに、それを許さないか、もしくは復元する手段を用意しておくことで実現可能かなと思う。 * こめんと欄 #comment