SELinuxの速度チューニング

昨日宣言した、「衝撃のデータ」話。

NSAのMLに投げたネタ
http://marc.info/?l=selinux&m=118845327521551&w=2

色々とSELinuxのパフォーマンスを
測ってみたが、組込み向けアーキテクチャだと
Pen4と比較してオーバーヘッドが大きいことに気付いてしまった。。

lmbenchの結果の比較

x86, ARM, SHでベンチマークをとった。
SELinux無効と比べてどれくらいオーバーヘッドがあるか測定。
simple read/writeの結果を抜粋
simple read/writeってのは、/dev/zero,/dev/nullに
1バイト読み込み/書き込みする時間を測っている。
openの時間は含めていない。純粋にread/writeの時間のみ。

1) Result for x86(Pentium 4 2.6Ghz), kernel 2.6.22
Base SELinux Overhead(%)
Simple read 1.1034 1.2387 12.3
Simple write 0.9989 1.139 14.0
Base: kernel compiled without SELinux support

2) Result for ARM(Intel XScale (PXA270 416MHz)), kernel 2.6.17
Base SELinux Overhead(%)
Simple read 1.7945 3.1279 74.3
Simple write 1.4706 2.9228 98.7

3) Result for SH(SH4, SH7751R), kernel 2.6.22
Base SELinux Overhead(%)
Simple read 2.6781 6.4671 141.5
Simple write 2.0781 5.3181 155.9

Pen4だと、オーバーヘッドは10%ちょっと。
しかし、
SHだと、なんと150%ぐらい(2.5倍遅い)。
これはさすがに心理的に微妙だと思う。

どこかで、「組込みSELinuxのオーバーヘッドはたいしたことない」
と言った気がするのですが、超勘違いしてましたorz

チューニング

ハッシュのチューニングとか色々と試行錯誤したのだが、
結局、「関数呼び出し」が利いているような気がした。
余計な関数呼び出しを削減する単純なパッチを作った。

チューニング後のパフォーマンス

1) Result for x86
Base SELinux Overhead(%)
Simple read 1.1034 1.1939 8.2(before 12.3)
Simple write 0.9989 1.1039 10.5(before 14.0)

2) Result for ARM
Base SELinux Overhead(%)
Simple read 1.7945 2.7555 53.6(before 74.3)
Simple write 1.4706 2.5347 72.4(before 98.7)

3) Result for SH
Base SELinux Overhead(%)
Simple read 2.6781 3.6538 36.43(before 141.5)
Simple write 2.0781 3.321 59.80(before 155.9)

ちょっとしたことだが、だいぶ改善されている。
ARMよりSHのほうが効果が大きい。不思議だ。。
SHの関数呼び出しは重いのだろうか。
詳しい人教えてください。

さらに改善するには、
TOMOYO Linuxばりに、openだけチェックして、
read/writeのチェックを無くすという荒業もあるが、
コミュニティに受け入れられる気がしない。

反応

おっと、今メール見たら早速反応が。
この遅さは意外で、どうも信じられないようだ。
私も信じられなかったが、今週、何回測ってもほぼ同じ結果が得られた。
私がとんでもない思い違いをしているのかもしれないので、
他の人の追試を希望なのだけど。

ロスコンパイルの際に、とんでもない誤りをしているのかもしれないし。

read/writeのチェックは無駄か?

Stephenが、色々とアイデアを出してくれたが、その中には、
なんと、read/writeのチェック無くす可能性まで示唆された。
ΩΩ<な、なんだってー! 
聞いてみるものだ。

1) Make selinux_file_permission optional, e.g. depend on separate config
option, so people can build kernels without it. Revalidation on
read/write is nice for revocation on file relabels and policy changes,
but it is known to be incomplete (e.g. mmap'd files), it isn't required
for evaluation/certification, and it carries a high cost.

open以外にread/writeのチェックをしてる理由が分からなかったのだけど、
open後に,relabel・reloadが生じた場合に意味があるそうだ。
ただ,それでも完璧ではないんだとか。

反応をまとめると、以下のようだ

  • 手作業で、関数を展開するのは、たとえ速くなるとしてもいいことではない
  • 遅いのはなんとかせねばなるまい
  • 別の方法を探るべき(read/writeのチェック可能性を限りなく無くすなど)

なので、Stephenが教えてくれたアイデアを検討して、よさそうなやつを
採用してみよう。

relabel,reloadが生じた場合のみ,
read/writeのチェックとかするのが完璧っぽいけど、
なんか、SMPの場合とかに今度は問題が出てきそうな予感。よく分からんけど。
KaiGaiさんのやったRCU関係の仕事をパァにしないように気を使う必要があるっぽい。
色々と勉強になりそうだ。

また反応が。

On second thought, making selinux_file_permission optional may lead to
insecurity for policies and/or applications that depend on it, and there
is presently no mechanism for a policy or application to check what set
of hooks/permissions are applied by a given kernel.

やっぱり、read/write時のチェックを消しちゃうのは危ないということか。

So let's focus for now on improving selinux_file_permission. I'm not
opposed to inlining if it truly helps that much, but I think we should
also look at the recheck-only-if-something-changed approach.

ということで、
relabel,reloadが生じた時だけ,
read/writeをチェックするアプローチを考えることになった。
うーむ、どうすりゃいいのか、全然分からんぞ(汗
勉強ですな。

対応策

さて、どうすりゃいいんだ。対応策をメモ。
カーネル内のデータ構造もよく分かってないが。。
Stephenのメールを見ると、
open後に、read/writeのチェックが必要なのは以下か。
1) プロセスのsidがopen後に変わった場合
2) ファイルのsidがopen後に変わった場合
3) ポリシがリロードされた場合
これらの変化を検出すればいいのか。
1)については、

  • プロセスがオープンしてるファイル識別子の構造体に、フラグを追加する。
  • sidを変えるシステムコールを呼んだ場合(execもしくはsetcon)
  • 上記フラグを一斉に立てる。
  • selinux_file_permission(read/writeで呼ばれる)で、フラグをチェック。フラグが立ってる時だけ呼ぶ
  • read/writeをチェックし、アクセス許可ならフラグをクリア

でいいのか?
このとき、何か排他制御しないと、問題が生じるか??
2)については、、

  • グローバル変数で通し番号を用意する(sid_serial)
  • ファイルのsidを変える可能性があるシステムコール(setxattr等)を呼んだら、sid_serialをインクリメント
  • open時に,その時のsid_serialを覚えておく(struct fileのf_security構造体か何かで)
  • read/write時に、sid_serialが変わってたら、パーミッションチェック
  • 許可されたら、open時に覚えたsid_serialをアップデート

排他制御とか、通し番号が一周したらどうなるかは知らない(汗
ファイルのsidを変える可能性があるシステムコールはそんなに頻繁に呼ばれないハズなので、パーミッションチェックが発生する場合も少ないと期待。

3)も、load_policyするごとに、2)の通し番号を増やせば、同じロジックでいける。

1)も、プロセスのsidを変えるシステムコールがそんなに頻繁に呼ばれなければ、同じロジックでいける? 

通し番号が一周した場合は重要そうだ。