2007年12月 9日

[selinux-users:02085] Re: セキュアOSの性能評価機能の公開

松田さん

海外です。

CELFでの発表、楽しみにしています。
# 若い人がコードを出してくれるのは嬉しい限り :)

MATSUDA Naoto wrote:

> 岡山大学の松田と申します.
>
> 現在,IBMの宗藤さんと共同で,LSMのオーバーヘッド測定機能LSMPMONを開発
> し,以下のページで公開しています.
> http://www.swlab.it.okayama-u.ac.jp/lab/tabata/lsmpmon/
>
> 有用なツールだと思いますので,ぜひとも皆様に使用していただき,何らか
> のフィードバックを頂ければ,と考えています.
>
> よろしくお願いいたします.

まず、測定方法について

LSM上にモジュールが乗った際のオーバーヘッドは、フック呼び出しにかかるもの
だけではありません。
最も顕著なのがファイルを作成した際の xattr の初期化です。
これは、security_inode_init_security() フックで初期化された値を xattr に書き込む
ためのフックですが、LSMモジュールが空の場合や、xattrを使わない人の場合は、
こういったI/O操作は必要ありません。
この辺を見るためには、実は security_inode_init_security() が成功を返した後の、
xattrの書込み操作まで見ないと実際の差分が計測漏れになることになります。

続いて、パッチに関して

+/*
+ * 時刻の取得用関数
+ */
+long long gettime(void)
+{
+ long long time;
+ rdtscll(time);
+ return time;
+}

酒井さんの言うように、rdtsc()は x86 固有なので使うべきではありません。
また、最近の CPU は動作周波数を動的に変更できるので、経過クロック数というのは
あまりアテにならないという事情もあります。

ただ、LSMのフックの中にはほとんど一瞬で戻ってくるものもあるので、
タイムソースとして他のモノがどの程度アテになるかというのはあります。
試しに、High Resolution Timer で置き換えてみたら、結果はどれくらい
変わってくるでしょうか?

たしか、ktime_get() を使うことで、字面上は nsec 単位での情報を取得できると
いうことになっています。

+/*
+ * データを比較し,保存
+ * 最大値,最小値,呼び出し回数,合計処理時間をフック関数ごとに保存
+ * time: 今回の処理時間
+ * offset: 対応するフック関数
+ */
+void lsmpmon_store(long long time, int offset)
+{
+ if (time > lsmpmon_max[offset])
+ lsmpmon_max[offset] = time;
+ if (time < lsmpmon_min[offset])
+ lsmpmon_min[offset] = time;
+ lsmpmon_count[offset]++;
+ lsmpmon_sum[offset] += time;
+}

これは、シングルプロセッサを前提とした結果の格納ですね。
SMP環境で同じ変数に対するアクセスが同時に発生した場合、
変数の値が破壊されてしまう可能性があります。

こういった場合の対処はいくつか方法があります。
(1) ロックを利用する
spinlockを利用して、lsmpmon_sumやlsmpmon_countへの加算を同時に 1CPU しか
実行できないようにします。単純な方法で、適用できるケースも多いのですが、
ロックで『待ち』が発生した場合には、パフォーマンス測定には良くない影響を
与えます。

(2) atomic_t 型を利用する
atomic_t 型は 32bit の幅を持つ整数のデータを格納することができ、
atomic_add() や atomic_dec() マクロで加算/減算、atomic_read() で読出しを
行うことができます。
この操作には、他のCPUが干渉できないことが保証されています。
(なので、kernel内では参照カウンタの実装に使われています)

ただ、今回は lsmpmon_count と lsmpmon_sum という2つの変数を別々に操作する
必要があり、タイミングによっては、lsmpmon_count を加算し、lsmpmon_sumの操作を
行う前にユーザがこの変数を read するという状況が発生する可能性があるので、
今回、これを利用することはできません。
(また、機械語レベルで多少重い命令を使うため、パフォーマンスへの影響も出ます)

(3) per-cpu データを利用する
per-cpuデータというのは、CPU0〜CPUn 専用の領域を作るというものです。
イメージとしては、CPU番号を引数に取る配列変数を考えてください。
各CPUは自分専用の領域を加減算するため、排他処理を考えずにアクセスする事が可能です。

サンプルとしては、security/selinux/avc.c で定義されている avc_cache_stats が
良い例です。

DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 };
#define avc_cache_stats_incr(field) \
do { \
per_cpu(avc_cache_stats, get_cpu()).field++; \
put_cpu(); \
} while (0)

get_cpu()でCPU番号を取得して、それをキーに per-cpu 領域にアクセスするという
形になっていますね。この統計情報は /selinux/avc 以下のエントリを参照することで
ユーザが読むことができます。

以上、簡単ではありますが。
--
KaiGai Kohei <kaigai@xxxxx>

投稿者 xml-rpc : 2007年12月 9日 22:19
役に立ちました?:
過去のフィードバック 平均:(0) 総合:(0) 投票回数:(0)
本記事へのTrackback: http://hoop.euqset.org/blog/mt-tb2006.cgi/67443
トラックバック
コメント
コメントする




画像の中に見える文字を入力してください。