diff options
-rw-r--r-- | security/selinux/avc.c | 63 |
1 files changed, 44 insertions, 19 deletions
diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 326aa78bd421..9dd5c506a826 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c | |||
@@ -92,12 +92,12 @@ struct avc_entry { | |||
92 | 92 | ||
93 | struct avc_node { | 93 | struct avc_node { |
94 | struct avc_entry ae; | 94 | struct avc_entry ae; |
95 | struct list_head list; | 95 | struct list_head list; /* anchored in avc_cache->slots[i] */ |
96 | struct rcu_head rhead; | 96 | struct rcu_head rhead; |
97 | }; | 97 | }; |
98 | 98 | ||
99 | struct avc_cache { | 99 | struct avc_cache { |
100 | struct list_head slots[AVC_CACHE_SLOTS]; | 100 | struct list_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */ |
101 | spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */ | 101 | spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */ |
102 | atomic_t lru_hint; /* LRU hint for reclaim scan */ | 102 | atomic_t lru_hint; /* LRU hint for reclaim scan */ |
103 | atomic_t active_nodes; | 103 | atomic_t active_nodes; |
@@ -249,16 +249,18 @@ int avc_get_hash_stats(char *page) | |||
249 | { | 249 | { |
250 | int i, chain_len, max_chain_len, slots_used; | 250 | int i, chain_len, max_chain_len, slots_used; |
251 | struct avc_node *node; | 251 | struct avc_node *node; |
252 | struct list_head *head; | ||
252 | 253 | ||
253 | rcu_read_lock(); | 254 | rcu_read_lock(); |
254 | 255 | ||
255 | slots_used = 0; | 256 | slots_used = 0; |
256 | max_chain_len = 0; | 257 | max_chain_len = 0; |
257 | for (i = 0; i < AVC_CACHE_SLOTS; i++) { | 258 | for (i = 0; i < AVC_CACHE_SLOTS; i++) { |
258 | if (!list_empty(&avc_cache.slots[i])) { | 259 | head = &avc_cache.slots[i]; |
260 | if (!list_empty(head)) { | ||
259 | slots_used++; | 261 | slots_used++; |
260 | chain_len = 0; | 262 | chain_len = 0; |
261 | list_for_each_entry_rcu(node, &avc_cache.slots[i], list) | 263 | list_for_each_entry_rcu(node, head, list) |
262 | chain_len++; | 264 | chain_len++; |
263 | if (chain_len > max_chain_len) | 265 | if (chain_len > max_chain_len) |
264 | max_chain_len = chain_len; | 266 | max_chain_len = chain_len; |
@@ -306,26 +308,30 @@ static inline int avc_reclaim_node(void) | |||
306 | struct avc_node *node; | 308 | struct avc_node *node; |
307 | int hvalue, try, ecx; | 309 | int hvalue, try, ecx; |
308 | unsigned long flags; | 310 | unsigned long flags; |
311 | struct list_head *head; | ||
312 | spinlock_t *lock; | ||
309 | 313 | ||
310 | for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++) { | 314 | for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++) { |
311 | hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1); | 315 | hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1); |
316 | head = &avc_cache.slots[hvalue]; | ||
317 | lock = &avc_cache.slots_lock[hvalue]; | ||
312 | 318 | ||
313 | if (!spin_trylock_irqsave(&avc_cache.slots_lock[hvalue], flags)) | 319 | if (!spin_trylock_irqsave(lock, flags)) |
314 | continue; | 320 | continue; |
315 | 321 | ||
316 | rcu_read_lock(); | 322 | rcu_read_lock(); |
317 | list_for_each_entry(node, &avc_cache.slots[hvalue], list) { | 323 | list_for_each_entry(node, head, list) { |
318 | avc_node_delete(node); | 324 | avc_node_delete(node); |
319 | avc_cache_stats_incr(reclaims); | 325 | avc_cache_stats_incr(reclaims); |
320 | ecx++; | 326 | ecx++; |
321 | if (ecx >= AVC_CACHE_RECLAIM) { | 327 | if (ecx >= AVC_CACHE_RECLAIM) { |
322 | rcu_read_unlock(); | 328 | rcu_read_unlock(); |
323 | spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags); | 329 | spin_unlock_irqrestore(lock, flags); |
324 | goto out; | 330 | goto out; |
325 | } | 331 | } |
326 | } | 332 | } |
327 | rcu_read_unlock(); | 333 | rcu_read_unlock(); |
328 | spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags); | 334 | spin_unlock_irqrestore(lock, flags); |
329 | } | 335 | } |
330 | out: | 336 | out: |
331 | return ecx; | 337 | return ecx; |
@@ -362,9 +368,11 @@ static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass) | |||
362 | { | 368 | { |
363 | struct avc_node *node, *ret = NULL; | 369 | struct avc_node *node, *ret = NULL; |
364 | int hvalue; | 370 | int hvalue; |
371 | struct list_head *head; | ||
365 | 372 | ||
366 | hvalue = avc_hash(ssid, tsid, tclass); | 373 | hvalue = avc_hash(ssid, tsid, tclass); |
367 | list_for_each_entry_rcu(node, &avc_cache.slots[hvalue], list) { | 374 | head = &avc_cache.slots[hvalue]; |
375 | list_for_each_entry_rcu(node, head, list) { | ||
368 | if (ssid == node->ae.ssid && | 376 | if (ssid == node->ae.ssid && |
369 | tclass == node->ae.tclass && | 377 | tclass == node->ae.tclass && |
370 | tsid == node->ae.tsid) { | 378 | tsid == node->ae.tsid) { |
@@ -453,11 +461,17 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_dec | |||
453 | 461 | ||
454 | node = avc_alloc_node(); | 462 | node = avc_alloc_node(); |
455 | if (node) { | 463 | if (node) { |
464 | struct list_head *head; | ||
465 | spinlock_t *lock; | ||
466 | |||
456 | hvalue = avc_hash(ssid, tsid, tclass); | 467 | hvalue = avc_hash(ssid, tsid, tclass); |
457 | avc_node_populate(node, ssid, tsid, tclass, avd); | 468 | avc_node_populate(node, ssid, tsid, tclass, avd); |
458 | 469 | ||
459 | spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag); | 470 | head = &avc_cache.slots[hvalue]; |
460 | list_for_each_entry(pos, &avc_cache.slots[hvalue], list) { | 471 | lock = &avc_cache.slots_lock[hvalue]; |
472 | |||
473 | spin_lock_irqsave(lock, flag); | ||
474 | list_for_each_entry(pos, head, list) { | ||
461 | if (pos->ae.ssid == ssid && | 475 | if (pos->ae.ssid == ssid && |
462 | pos->ae.tsid == tsid && | 476 | pos->ae.tsid == tsid && |
463 | pos->ae.tclass == tclass) { | 477 | pos->ae.tclass == tclass) { |
@@ -465,9 +479,9 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_dec | |||
465 | goto found; | 479 | goto found; |
466 | } | 480 | } |
467 | } | 481 | } |
468 | list_add_rcu(&node->list, &avc_cache.slots[hvalue]); | 482 | list_add_rcu(&node->list, head); |
469 | found: | 483 | found: |
470 | spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag); | 484 | spin_unlock_irqrestore(lock, flag); |
471 | } | 485 | } |
472 | out: | 486 | out: |
473 | return node; | 487 | return node; |
@@ -736,6 +750,8 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, | |||
736 | int hvalue, rc = 0; | 750 | int hvalue, rc = 0; |
737 | unsigned long flag; | 751 | unsigned long flag; |
738 | struct avc_node *pos, *node, *orig = NULL; | 752 | struct avc_node *pos, *node, *orig = NULL; |
753 | struct list_head *head; | ||
754 | spinlock_t *lock; | ||
739 | 755 | ||
740 | node = avc_alloc_node(); | 756 | node = avc_alloc_node(); |
741 | if (!node) { | 757 | if (!node) { |
@@ -745,9 +761,13 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, | |||
745 | 761 | ||
746 | /* Lock the target slot */ | 762 | /* Lock the target slot */ |
747 | hvalue = avc_hash(ssid, tsid, tclass); | 763 | hvalue = avc_hash(ssid, tsid, tclass); |
748 | spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag); | ||
749 | 764 | ||
750 | list_for_each_entry(pos, &avc_cache.slots[hvalue], list) { | 765 | head = &avc_cache.slots[hvalue]; |
766 | lock = &avc_cache.slots_lock[hvalue]; | ||
767 | |||
768 | spin_lock_irqsave(lock, flag); | ||
769 | |||
770 | list_for_each_entry(pos, head, list) { | ||
751 | if (ssid == pos->ae.ssid && | 771 | if (ssid == pos->ae.ssid && |
752 | tsid == pos->ae.tsid && | 772 | tsid == pos->ae.tsid && |
753 | tclass == pos->ae.tclass && | 773 | tclass == pos->ae.tclass && |
@@ -792,7 +812,7 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, | |||
792 | } | 812 | } |
793 | avc_node_replace(node, orig); | 813 | avc_node_replace(node, orig); |
794 | out_unlock: | 814 | out_unlock: |
795 | spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag); | 815 | spin_unlock_irqrestore(lock, flag); |
796 | out: | 816 | out: |
797 | return rc; | 817 | return rc; |
798 | } | 818 | } |
@@ -807,18 +827,23 @@ int avc_ss_reset(u32 seqno) | |||
807 | int i, rc = 0, tmprc; | 827 | int i, rc = 0, tmprc; |
808 | unsigned long flag; | 828 | unsigned long flag; |
809 | struct avc_node *node; | 829 | struct avc_node *node; |
830 | struct list_head *head; | ||
831 | spinlock_t *lock; | ||
810 | 832 | ||
811 | for (i = 0; i < AVC_CACHE_SLOTS; i++) { | 833 | for (i = 0; i < AVC_CACHE_SLOTS; i++) { |
812 | spin_lock_irqsave(&avc_cache.slots_lock[i], flag); | 834 | head = &avc_cache.slots[i]; |
835 | lock = &avc_cache.slots_lock[i]; | ||
836 | |||
837 | spin_lock_irqsave(lock, flag); | ||
813 | /* | 838 | /* |
814 | * With preemptable RCU, the outer spinlock does not | 839 | * With preemptable RCU, the outer spinlock does not |
815 | * prevent RCU grace periods from ending. | 840 | * prevent RCU grace periods from ending. |
816 | */ | 841 | */ |
817 | rcu_read_lock(); | 842 | rcu_read_lock(); |
818 | list_for_each_entry(node, &avc_cache.slots[i], list) | 843 | list_for_each_entry(node, head, list) |
819 | avc_node_delete(node); | 844 | avc_node_delete(node); |
820 | rcu_read_unlock(); | 845 | rcu_read_unlock(); |
821 | spin_unlock_irqrestore(&avc_cache.slots_lock[i], flag); | 846 | spin_unlock_irqrestore(lock, flag); |
822 | } | 847 | } |
823 | 848 | ||
824 | for (c = avc_callbacks; c; c = c->next) { | 849 | for (c = avc_callbacks; c; c = c->next) { |