aboutsummaryrefslogtreecommitdiffstats
path: root/security/tomoyo/gc.c
diff options
context:
space:
mode:
authorTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>2011-06-26 10:20:55 -0400
committerJames Morris <jmorris@namei.org>2011-06-28 19:31:21 -0400
commit2e503bbb435ae418aebbe4aeede1c6f2a33d6f74 (patch)
treec6b783c245716cf87b337b2a855e742133afb7ac /security/tomoyo/gc.c
parent5625f2e3266319fd29fe4f1c76ccd3f550c79ac4 (diff)
TOMOYO: Fix lockdep warning.
Currently TOMOYO holds SRCU lock upon open() and releases it upon close() because list elements stored in the "struct tomoyo_io_buffer" instances are accessed until close() is called. However, such SRCU usage causes lockdep to complain about leaving the kernel with SRCU lock held. This patch solves the warning by holding/releasing SRCU upon each read()/write(). This patch is doing something similar to calling kfree() without calling synchronize_srcu(), by selectively deferring kfree() by keeping track of the "struct tomoyo_io_buffer" instances. Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/tomoyo/gc.c')
-rw-r--r--security/tomoyo/gc.c278
1 files changed, 257 insertions, 21 deletions
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c
index 782e844dca7..1e1a6c8c832 100644
--- a/security/tomoyo/gc.c
+++ b/security/tomoyo/gc.c
@@ -11,13 +11,123 @@
11#include <linux/kthread.h> 11#include <linux/kthread.h>
12#include <linux/slab.h> 12#include <linux/slab.h>
13 13
14/* The list for "struct tomoyo_io_buffer". */
15static LIST_HEAD(tomoyo_io_buffer_list);
16/* Lock for protecting tomoyo_io_buffer_list. */
17static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock);
18
19/* Size of an element. */
20static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = {
21 [TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group),
22 [TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group),
23 [TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group),
24 [TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator),
25 [TOMOYO_ID_TRANSITION_CONTROL] =
26 sizeof(struct tomoyo_transition_control),
27 [TOMOYO_ID_MANAGER] = sizeof(struct tomoyo_manager),
28 /* [TOMOYO_ID_NAME] = "struct tomoyo_name"->size, */
29 /* [TOMOYO_ID_ACL] =
30 tomoyo_acl_size["struct tomoyo_acl_info"->type], */
31 [TOMOYO_ID_DOMAIN] = sizeof(struct tomoyo_domain_info),
32};
33
34/* Size of a domain ACL element. */
35static const u8 tomoyo_acl_size[] = {
36 [TOMOYO_TYPE_PATH_ACL] = sizeof(struct tomoyo_path_acl),
37 [TOMOYO_TYPE_PATH2_ACL] = sizeof(struct tomoyo_path2_acl),
38 [TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl),
39 [TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl),
40 [TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl),
41};
42
43/**
44 * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not.
45 *
46 * @element: Pointer to "struct list_head".
47 *
48 * Returns true if @element is used by /sys/kernel/security/tomoyo/ users,
49 * false otherwise.
50 */
51static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element)
52{
53 struct tomoyo_io_buffer *head;
54 bool in_use = false;
55
56 spin_lock(&tomoyo_io_buffer_list_lock);
57 list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
58 head->users++;
59 spin_unlock(&tomoyo_io_buffer_list_lock);
60 if (mutex_lock_interruptible(&head->io_sem)) {
61 in_use = true;
62 goto out;
63 }
64 if (head->r.domain == element || head->r.group == element ||
65 head->r.acl == element || &head->w.domain->list == element)
66 in_use = true;
67 mutex_unlock(&head->io_sem);
68out:
69 spin_lock(&tomoyo_io_buffer_list_lock);
70 head->users--;
71 if (in_use)
72 break;
73 }
74 spin_unlock(&tomoyo_io_buffer_list_lock);
75 return in_use;
76}
77
78/**
79 * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not.
80 *
81 * @string: String to check.
82 * @size: Memory allocated for @string .
83 *
84 * Returns true if @string is used by /sys/kernel/security/tomoyo/ users,
85 * false otherwise.
86 */
87static bool tomoyo_name_used_by_io_buffer(const char *string,
88 const size_t size)
89{
90 struct tomoyo_io_buffer *head;
91 bool in_use = false;
92
93 spin_lock(&tomoyo_io_buffer_list_lock);
94 list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
95 int i;
96 head->users++;
97 spin_unlock(&tomoyo_io_buffer_list_lock);
98 if (mutex_lock_interruptible(&head->io_sem)) {
99 in_use = true;
100 goto out;
101 }
102 for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) {
103 const char *w = head->r.w[i];
104 if (w < string || w > string + size)
105 continue;
106 in_use = true;
107 break;
108 }
109 mutex_unlock(&head->io_sem);
110out:
111 spin_lock(&tomoyo_io_buffer_list_lock);
112 head->users--;
113 if (in_use)
114 break;
115 }
116 spin_unlock(&tomoyo_io_buffer_list_lock);
117 return in_use;
118}
119
120/* Structure for garbage collection. */
14struct tomoyo_gc { 121struct tomoyo_gc {
15 struct list_head list; 122 struct list_head list;
16 enum tomoyo_policy_id type; 123 enum tomoyo_policy_id type;
124 size_t size;
17 struct list_head *element; 125 struct list_head *element;
18}; 126};
19static LIST_HEAD(tomoyo_gc_queue); 127/* List of entries to be deleted. */
20static DEFINE_MUTEX(tomoyo_gc_mutex); 128static LIST_HEAD(tomoyo_gc_list);
129/* Length of tomoyo_gc_list. */
130static int tomoyo_gc_list_len;
21 131
22/** 132/**
23 * tomoyo_add_to_gc - Add an entry to to be deleted list. 133 * tomoyo_add_to_gc - Add an entry to to be deleted list.
@@ -43,10 +153,42 @@ static bool tomoyo_add_to_gc(const int type, struct list_head *element)
43 if (!entry) 153 if (!entry)
44 return false; 154 return false;
45 entry->type = type; 155 entry->type = type;
156 if (type == TOMOYO_ID_ACL)
157 entry->size = tomoyo_acl_size[
158 container_of(element,
159 typeof(struct tomoyo_acl_info),
160 list)->type];
161 else if (type == TOMOYO_ID_NAME)
162 entry->size = strlen(container_of(element,
163 typeof(struct tomoyo_name),
164 head.list)->entry.name) + 1;
165 else
166 entry->size = tomoyo_element_size[type];
46 entry->element = element; 167 entry->element = element;
47 list_add(&entry->list, &tomoyo_gc_queue); 168 list_add(&entry->list, &tomoyo_gc_list);
48 list_del_rcu(element); 169 list_del_rcu(element);
49 return true; 170 return tomoyo_gc_list_len++ < 128;
171}
172
173/**
174 * tomoyo_element_linked_by_gc - Validate next element of an entry.
175 *
176 * @element: Pointer to an element.
177 * @size: Size of @element in byte.
178 *
179 * Returns true if @element is linked by other elements in the garbage
180 * collector's queue, false otherwise.
181 */
182static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size)
183{
184 struct tomoyo_gc *p;
185 list_for_each_entry(p, &tomoyo_gc_list, list) {
186 const u8 *ptr = (const u8 *) p->element->next;
187 if (ptr < element || element + size < ptr)
188 continue;
189 return true;
190 }
191 return false;
50} 192}
51 193
52/** 194/**
@@ -151,6 +293,13 @@ static void tomoyo_del_acl(struct list_head *element)
151 } 293 }
152} 294}
153 295
296/**
297 * tomoyo_del_domain - Delete members in "struct tomoyo_domain_info".
298 *
299 * @element: Pointer to "struct list_head".
300 *
301 * Returns true if deleted, false otherwise.
302 */
154static bool tomoyo_del_domain(struct list_head *element) 303static bool tomoyo_del_domain(struct list_head *element)
155{ 304{
156 struct tomoyo_domain_info *domain = 305 struct tomoyo_domain_info *domain =
@@ -360,13 +509,44 @@ unlock:
360 mutex_unlock(&tomoyo_policy_lock); 509 mutex_unlock(&tomoyo_policy_lock);
361} 510}
362 511
363static void tomoyo_kfree_entry(void) 512/**
513 * tomoyo_kfree_entry - Delete entries in tomoyo_gc_list.
514 *
515 * Returns true if some entries were kfree()d, false otherwise.
516 */
517static bool tomoyo_kfree_entry(void)
364{ 518{
365 struct tomoyo_gc *p; 519 struct tomoyo_gc *p;
366 struct tomoyo_gc *tmp; 520 struct tomoyo_gc *tmp;
521 bool result = false;
367 522
368 list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { 523 list_for_each_entry_safe(p, tmp, &tomoyo_gc_list, list) {
369 struct list_head *element = p->element; 524 struct list_head *element = p->element;
525
526 /*
527 * list_del_rcu() in tomoyo_add_to_gc() guarantees that the
528 * list element became no longer reachable from the list which
529 * the element was originally on (e.g. tomoyo_domain_list).
530 * Also, synchronize_srcu() in tomoyo_gc_thread() guarantees
531 * that the list element became no longer referenced by syscall
532 * users.
533 *
534 * However, there are three users which may still be using the
535 * list element. We need to defer until all of these users
536 * forget the list element.
537 *
538 * Firstly, defer until "struct tomoyo_io_buffer"->r.{domain,
539 * group,acl} and "struct tomoyo_io_buffer"->w.domain forget
540 * the list element.
541 */
542 if (tomoyo_struct_used_by_io_buffer(element))
543 continue;
544 /*
545 * Secondly, defer until all other elements in the
546 * tomoyo_gc_list list forget the list element.
547 */
548 if (tomoyo_element_linked_by_gc((const u8 *) element, p->size))
549 continue;
370 switch (p->type) { 550 switch (p->type) {
371 case TOMOYO_ID_TRANSITION_CONTROL: 551 case TOMOYO_ID_TRANSITION_CONTROL:
372 tomoyo_del_transition_control(element); 552 tomoyo_del_transition_control(element);
@@ -378,6 +558,14 @@ static void tomoyo_kfree_entry(void)
378 tomoyo_del_manager(element); 558 tomoyo_del_manager(element);
379 break; 559 break;
380 case TOMOYO_ID_NAME: 560 case TOMOYO_ID_NAME:
561 /*
562 * Thirdly, defer until all "struct tomoyo_io_buffer"
563 * ->r.w[] forget the list element.
564 */
565 if (tomoyo_name_used_by_io_buffer(
566 container_of(element, typeof(struct tomoyo_name),
567 head.list)->entry.name, p->size))
568 continue;
381 tomoyo_del_name(element); 569 tomoyo_del_name(element);
382 break; 570 break;
383 case TOMOYO_ID_ACL: 571 case TOMOYO_ID_ACL:
@@ -402,7 +590,10 @@ static void tomoyo_kfree_entry(void)
402 tomoyo_memory_free(element); 590 tomoyo_memory_free(element);
403 list_del(&p->list); 591 list_del(&p->list);
404 kfree(p); 592 kfree(p);
593 tomoyo_gc_list_len--;
594 result = true;
405 } 595 }
596 return result;
406} 597}
407 598
408/** 599/**
@@ -418,25 +609,70 @@ static void tomoyo_kfree_entry(void)
418 */ 609 */
419static int tomoyo_gc_thread(void *unused) 610static int tomoyo_gc_thread(void *unused)
420{ 611{
612 /* Garbage collector thread is exclusive. */
613 static DEFINE_MUTEX(tomoyo_gc_mutex);
614 if (!mutex_trylock(&tomoyo_gc_mutex))
615 goto out;
421 daemonize("GC for TOMOYO"); 616 daemonize("GC for TOMOYO");
422 if (mutex_trylock(&tomoyo_gc_mutex)) { 617 do {
423 int i; 618 tomoyo_collect_entry();
424 for (i = 0; i < 10; i++) { 619 if (list_empty(&tomoyo_gc_list))
425 tomoyo_collect_entry(); 620 break;
426 if (list_empty(&tomoyo_gc_queue)) 621 synchronize_srcu(&tomoyo_ss);
427 break; 622 } while (tomoyo_kfree_entry());
428 synchronize_srcu(&tomoyo_ss); 623 {
429 tomoyo_kfree_entry(); 624 struct tomoyo_io_buffer *head;
625 struct tomoyo_io_buffer *tmp;
626
627 spin_lock(&tomoyo_io_buffer_list_lock);
628 list_for_each_entry_safe(head, tmp, &tomoyo_io_buffer_list,
629 list) {
630 if (head->users)
631 continue;
632 list_del(&head->list);
633 kfree(head->read_buf);
634 kfree(head->write_buf);
635 kfree(head);
430 } 636 }
431 mutex_unlock(&tomoyo_gc_mutex); 637 spin_unlock(&tomoyo_io_buffer_list_lock);
432 } 638 }
433 do_exit(0); 639 mutex_unlock(&tomoyo_gc_mutex);
640out:
641 /* This acts as do_exit(0). */
642 return 0;
434} 643}
435 644
436void tomoyo_run_gc(void) 645/**
646 * tomoyo_notify_gc - Register/unregister /sys/kernel/security/tomoyo/ users.
647 *
648 * @head: Pointer to "struct tomoyo_io_buffer".
649 * @is_register: True if register, false if unregister.
650 *
651 * Returns nothing.
652 */
653void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register)
437{ 654{
438 struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL, 655 bool is_write = false;
439 "GC for TOMOYO"); 656
440 if (!IS_ERR(task)) 657 spin_lock(&tomoyo_io_buffer_list_lock);
441 wake_up_process(task); 658 if (is_register) {
659 head->users = 1;
660 list_add(&head->list, &tomoyo_io_buffer_list);
661 } else {
662 is_write = head->write_buf != NULL;
663 if (!--head->users) {
664 list_del(&head->list);
665 kfree(head->read_buf);
666 kfree(head->write_buf);
667 kfree(head);
668 }
669 }
670 spin_unlock(&tomoyo_io_buffer_list_lock);
671 if (is_write) {
672 struct task_struct *task = kthread_create(tomoyo_gc_thread,
673 NULL,
674 "GC for TOMOYO");
675 if (!IS_ERR(task))
676 wake_up_process(task);
677 }
442} 678}