SELinuxは重いのか?
avtab関連のベンチマークを取るついでに、
AVCのベンチマークも取っている。
AVCの効果
その中で分かったのは、AVC(Access Vector Cache)の効果。
SELinuxで、アクセス制御のチェックをするときは、
1) AVCを見に行く
2) AVCにないときは、security_compute_avで、ポリシーファイルを探索
となっている。
測ってみた感じでは、
2)のポリシ探索処理にかかる時間が100だとすると、
1)にかかる時間はわずか1/100!
AVCのヒット率だが、
手元のF7マシンだと、
# cat /selinux/avc/cache_stats
lookups hits misses allocations reclaims frees
905705 901458 4247 4247 3728 3747
なので、
ヒット率:99.53%
ミス率:0.47%
ぐらい。
てことは、
平均時間は、AVCにヒットした場合を1として、
1*0.9953 + 100 * 0.47/100 = 1.465
か。
ミス率をもっと減らせれば、早くできるなぁ。
AVCをチェックする処理
905 int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, 906 u32 requested, struct avc_audit_data *auditdata) 907 { 908 struct av_decision avd; 909 int rc; 910 911 rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, &avd); 912 avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata); 913 return rc; 914 } 915
avc_has_perm_noauditが、アクセスチェック関数。
avc_auditはログを取る関数
847 int avc_has_perm_noaudit(u32 ssid, u32 tsid, 848 u16 tclass, u32 requested, 849 struct av_decision *avd) 850 { 851 struct avc_node *node; 852 struct avc_entry entry, *p_ae; 853 int rc = 0; 854 u32 denied; 855 856 rcu_read_lock(); 857 858 node = avc_lookup(ssid, tsid, tclass, requested); ★↑ここで、AVCを見に行っている★ 859 if (!node) { 860 rcu_read_unlock(); 861 rc = security_compute_av(ssid,tsid,tclass,requested,&entry.avd); ★AVCをミスした場合は、↑でポリシファイルを見に行っている★ 862 if (rc) 863 goto out; 864 rcu_read_lock(); 865 node = avc_insert(ssid,tsid,tclass,&entry); 866 }
avc_lookup関数だが、以下。
401 static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass, u32 requested) 402 { 403 struct avc_node *node; 404 405 avc_cache_stats_incr(lookups); 406 node = avc_search_node(ssid, tsid, tclass); 407 408 if (node && ((node->ae.avd.decided & requested) == requested)) { 409 avc_cache_stats_incr(hits); 410 goto out; 411 } 412 413 node = NULL; 414 avc_cache_stats_incr(misses); 415 out: 416 return node; 417 } 360 static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass) 361 { 362 struct avc_node *node, *ret = NULL; 363 int hvalue; 364 365 hvalue = avc_hash(ssid, tsid, tclass); 366 list_for_each_entry_rcu(node, &avc_cache.slots[hvalue], list) { 367 if (ssid == node->ae.ssid && 368 tclass == node->ae.tclass && 369 tsid == node->ae.tsid) { 370 ret = node; 371 break; 372 } 373 } 374 375 if (ret == NULL) { 376 /* cache miss */ 377 goto out; 378 } 379 380 /* cache hit */ 381 if (atomic_read(&ret->ae.used) != 1) 382 atomic_set(&ret->ae.used, 1); 383 out: 384 return ret; 385 }
処理の内容だが、
hvalue = avc_hash(ssid, tsid, tclass);
のハッシュ関数の計算と、
ハッシュチェーンを探索、単純な比較演算ぐらい。
avc_hashは、
return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1);
という単純なビット演算。
やってることは軽そう。SELinuxのコア部分は、簡単な処理しかしてませんので、
たぶん、重くないです。
さらに軽くするためのポイントを考えてみると。。。
avc_auditの呼び出しは、全部の場合呼ぶ必要ないだろうというのと、
avc_cache_stats_incr(lookups);
が普通はいらないのでselinuxfsで切れるようにすることか。