diff options
author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2011-06-26 10:20:55 -0400 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2011-06-28 19:31:21 -0400 |
commit | 2e503bbb435ae418aebbe4aeede1c6f2a33d6f74 (patch) | |
tree | c6b783c245716cf87b337b2a855e742133afb7ac /security/tomoyo | |
parent | 5625f2e3266319fd29fe4f1c76ccd3f550c79ac4 (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')
-rw-r--r-- | security/tomoyo/common.c | 41 | ||||
-rw-r--r-- | security/tomoyo/common.h | 8 | ||||
-rw-r--r-- | security/tomoyo/gc.c | 278 |
3 files changed, 276 insertions, 51 deletions
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 50481d2cf970..691c34025a4a 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c | |||
@@ -1820,9 +1820,7 @@ static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head) | |||
1820 | * @type: Type of interface. | 1820 | * @type: Type of interface. |
1821 | * @file: Pointer to "struct file". | 1821 | * @file: Pointer to "struct file". |
1822 | * | 1822 | * |
1823 | * Associates policy handler and returns 0 on success, -ENOMEM otherwise. | 1823 | * Returns 0 on success, negative value otherwise. |
1824 | * | ||
1825 | * Caller acquires tomoyo_read_lock(). | ||
1826 | */ | 1824 | */ |
1827 | int tomoyo_open_control(const u8 type, struct file *file) | 1825 | int tomoyo_open_control(const u8 type, struct file *file) |
1828 | { | 1826 | { |
@@ -1921,9 +1919,6 @@ int tomoyo_open_control(const u8 type, struct file *file) | |||
1921 | return -ENOMEM; | 1919 | return -ENOMEM; |
1922 | } | 1920 | } |
1923 | } | 1921 | } |
1924 | if (type != TOMOYO_QUERY && type != TOMOYO_AUDIT) | ||
1925 | head->reader_idx = tomoyo_read_lock(); | ||
1926 | file->private_data = head; | ||
1927 | /* | 1922 | /* |
1928 | * If the file is /sys/kernel/security/tomoyo/query , increment the | 1923 | * If the file is /sys/kernel/security/tomoyo/query , increment the |
1929 | * observer counter. | 1924 | * observer counter. |
@@ -1932,6 +1927,8 @@ int tomoyo_open_control(const u8 type, struct file *file) | |||
1932 | */ | 1927 | */ |
1933 | if (type == TOMOYO_QUERY) | 1928 | if (type == TOMOYO_QUERY) |
1934 | atomic_inc(&tomoyo_query_observers); | 1929 | atomic_inc(&tomoyo_query_observers); |
1930 | file->private_data = head; | ||
1931 | tomoyo_notify_gc(head, true); | ||
1935 | return 0; | 1932 | return 0; |
1936 | } | 1933 | } |
1937 | 1934 | ||
@@ -2000,13 +1997,12 @@ static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head) | |||
2000 | * @buffer_len: Size of @buffer. | 1997 | * @buffer_len: Size of @buffer. |
2001 | * | 1998 | * |
2002 | * Returns bytes read on success, negative value otherwise. | 1999 | * Returns bytes read on success, negative value otherwise. |
2003 | * | ||
2004 | * Caller holds tomoyo_read_lock(). | ||
2005 | */ | 2000 | */ |
2006 | int tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, | 2001 | int tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, |
2007 | const int buffer_len) | 2002 | const int buffer_len) |
2008 | { | 2003 | { |
2009 | int len; | 2004 | int len; |
2005 | int idx; | ||
2010 | 2006 | ||
2011 | if (!head->read) | 2007 | if (!head->read) |
2012 | return -ENOSYS; | 2008 | return -ENOSYS; |
@@ -2014,6 +2010,7 @@ int tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, | |||
2014 | return -EINTR; | 2010 | return -EINTR; |
2015 | head->read_user_buf = buffer; | 2011 | head->read_user_buf = buffer; |
2016 | head->read_user_buf_avail = buffer_len; | 2012 | head->read_user_buf_avail = buffer_len; |
2013 | idx = tomoyo_read_lock(); | ||
2017 | if (tomoyo_flush(head)) | 2014 | if (tomoyo_flush(head)) |
2018 | /* Call the policy handler. */ | 2015 | /* Call the policy handler. */ |
2019 | do { | 2016 | do { |
@@ -2021,6 +2018,7 @@ int tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, | |||
2021 | head->read(head); | 2018 | head->read(head); |
2022 | } while (tomoyo_flush(head) && | 2019 | } while (tomoyo_flush(head) && |
2023 | tomoyo_has_more_namespace(head)); | 2020 | tomoyo_has_more_namespace(head)); |
2021 | tomoyo_read_unlock(idx); | ||
2024 | len = head->read_user_buf - buffer; | 2022 | len = head->read_user_buf - buffer; |
2025 | mutex_unlock(&head->io_sem); | 2023 | mutex_unlock(&head->io_sem); |
2026 | return len; | 2024 | return len; |
@@ -2071,8 +2069,6 @@ static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line) | |||
2071 | * @buffer_len: Size of @buffer. | 2069 | * @buffer_len: Size of @buffer. |
2072 | * | 2070 | * |
2073 | * Returns @buffer_len on success, negative value otherwise. | 2071 | * Returns @buffer_len on success, negative value otherwise. |
2074 | * | ||
2075 | * Caller holds tomoyo_read_lock(). | ||
2076 | */ | 2072 | */ |
2077 | int tomoyo_write_control(struct tomoyo_io_buffer *head, | 2073 | int tomoyo_write_control(struct tomoyo_io_buffer *head, |
2078 | const char __user *buffer, const int buffer_len) | 2074 | const char __user *buffer, const int buffer_len) |
@@ -2080,12 +2076,14 @@ int tomoyo_write_control(struct tomoyo_io_buffer *head, | |||
2080 | int error = buffer_len; | 2076 | int error = buffer_len; |
2081 | size_t avail_len = buffer_len; | 2077 | size_t avail_len = buffer_len; |
2082 | char *cp0 = head->write_buf; | 2078 | char *cp0 = head->write_buf; |
2079 | int idx; | ||
2083 | if (!head->write) | 2080 | if (!head->write) |
2084 | return -ENOSYS; | 2081 | return -ENOSYS; |
2085 | if (!access_ok(VERIFY_READ, buffer, buffer_len)) | 2082 | if (!access_ok(VERIFY_READ, buffer, buffer_len)) |
2086 | return -EFAULT; | 2083 | return -EFAULT; |
2087 | if (mutex_lock_interruptible(&head->io_sem)) | 2084 | if (mutex_lock_interruptible(&head->io_sem)) |
2088 | return -EINTR; | 2085 | return -EINTR; |
2086 | idx = tomoyo_read_lock(); | ||
2089 | /* Read a line and dispatch it to the policy handler. */ | 2087 | /* Read a line and dispatch it to the policy handler. */ |
2090 | while (avail_len > 0) { | 2088 | while (avail_len > 0) { |
2091 | char c; | 2089 | char c; |
@@ -2148,6 +2146,7 @@ int tomoyo_write_control(struct tomoyo_io_buffer *head, | |||
2148 | } | 2146 | } |
2149 | } | 2147 | } |
2150 | out: | 2148 | out: |
2149 | tomoyo_read_unlock(idx); | ||
2151 | mutex_unlock(&head->io_sem); | 2150 | mutex_unlock(&head->io_sem); |
2152 | return error; | 2151 | return error; |
2153 | } | 2152 | } |
@@ -2157,30 +2156,18 @@ out: | |||
2157 | * | 2156 | * |
2158 | * @head: Pointer to "struct tomoyo_io_buffer". | 2157 | * @head: Pointer to "struct tomoyo_io_buffer". |
2159 | * | 2158 | * |
2160 | * Releases memory and returns 0. | 2159 | * Returns 0. |
2161 | * | ||
2162 | * Caller looses tomoyo_read_lock(). | ||
2163 | */ | 2160 | */ |
2164 | int tomoyo_close_control(struct tomoyo_io_buffer *head) | 2161 | int tomoyo_close_control(struct tomoyo_io_buffer *head) |
2165 | { | 2162 | { |
2166 | const bool is_write = !!head->write_buf; | ||
2167 | |||
2168 | /* | 2163 | /* |
2169 | * If the file is /sys/kernel/security/tomoyo/query , decrement the | 2164 | * If the file is /sys/kernel/security/tomoyo/query , decrement the |
2170 | * observer counter. | 2165 | * observer counter. |
2171 | */ | 2166 | */ |
2172 | if (head->type == TOMOYO_QUERY) | 2167 | if (head->type == TOMOYO_QUERY && |
2173 | atomic_dec(&tomoyo_query_observers); | 2168 | atomic_dec_and_test(&tomoyo_query_observers)) |
2174 | else if (head->type != TOMOYO_AUDIT) | 2169 | wake_up_all(&tomoyo_answer_wait); |
2175 | tomoyo_read_unlock(head->reader_idx); | 2170 | tomoyo_notify_gc(head, false); |
2176 | /* Release memory used for policy I/O. */ | ||
2177 | kfree(head->read_buf); | ||
2178 | head->read_buf = NULL; | ||
2179 | kfree(head->write_buf); | ||
2180 | head->write_buf = NULL; | ||
2181 | kfree(head); | ||
2182 | if (is_write) | ||
2183 | tomoyo_run_gc(); | ||
2184 | return 0; | 2171 | return 0; |
2185 | } | 2172 | } |
2186 | 2173 | ||
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 53c8798e38b7..a5eeabcc0738 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h | |||
@@ -441,8 +441,6 @@ struct tomoyo_io_buffer { | |||
441 | int (*poll) (struct file *file, poll_table *wait); | 441 | int (*poll) (struct file *file, poll_table *wait); |
442 | /* Exclusive lock for this structure. */ | 442 | /* Exclusive lock for this structure. */ |
443 | struct mutex io_sem; | 443 | struct mutex io_sem; |
444 | /* Index returned by tomoyo_read_lock(). */ | ||
445 | int reader_idx; | ||
446 | char __user *read_user_buf; | 444 | char __user *read_user_buf; |
447 | int read_user_buf_avail; | 445 | int read_user_buf_avail; |
448 | struct { | 446 | struct { |
@@ -480,6 +478,10 @@ struct tomoyo_io_buffer { | |||
480 | int writebuf_size; | 478 | int writebuf_size; |
481 | /* Type of this interface. */ | 479 | /* Type of this interface. */ |
482 | u8 type; | 480 | u8 type; |
481 | /* Users counter protected by tomoyo_io_buffer_list_lock. */ | ||
482 | u8 users; | ||
483 | /* List for telling GC not to kfree() elements. */ | ||
484 | struct list_head list; | ||
483 | }; | 485 | }; |
484 | 486 | ||
485 | /* | 487 | /* |
@@ -651,7 +653,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm); | |||
651 | void tomoyo_print_ulong(char *buffer, const int buffer_len, | 653 | void tomoyo_print_ulong(char *buffer, const int buffer_len, |
652 | const unsigned long value, const u8 type); | 654 | const unsigned long value, const u8 type); |
653 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr); | 655 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr); |
654 | void tomoyo_run_gc(void); | 656 | void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register); |
655 | void tomoyo_memory_free(void *ptr); | 657 | void tomoyo_memory_free(void *ptr); |
656 | int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, | 658 | int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, |
657 | struct tomoyo_acl_param *param, | 659 | struct tomoyo_acl_param *param, |
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 782e844dca7f..1e1a6c8c832c 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". */ | ||
15 | static LIST_HEAD(tomoyo_io_buffer_list); | ||
16 | /* Lock for protecting tomoyo_io_buffer_list. */ | ||
17 | static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock); | ||
18 | |||
19 | /* Size of an element. */ | ||
20 | static 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. */ | ||
35 | static 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 | */ | ||
51 | static 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); | ||
68 | out: | ||
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 | */ | ||
87 | static 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); | ||
110 | out: | ||
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. */ | ||
14 | struct tomoyo_gc { | 121 | struct 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 | }; |
19 | static LIST_HEAD(tomoyo_gc_queue); | 127 | /* List of entries to be deleted. */ |
20 | static DEFINE_MUTEX(tomoyo_gc_mutex); | 128 | static LIST_HEAD(tomoyo_gc_list); |
129 | /* Length of tomoyo_gc_list. */ | ||
130 | static 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 | */ | ||
182 | static 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 | */ | ||
154 | static bool tomoyo_del_domain(struct list_head *element) | 303 | static 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 | ||
363 | static 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 | */ | ||
517 | static 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 | */ |
419 | static int tomoyo_gc_thread(void *unused) | 610 | static 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); |
640 | out: | ||
641 | /* This acts as do_exit(0). */ | ||
642 | return 0; | ||
434 | } | 643 | } |
435 | 644 | ||
436 | void 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 | */ | ||
653 | void 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 | } |