diff options
author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2011-09-25 04:50:23 -0400 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2011-09-25 20:46:20 -0400 |
commit | f9732ea145886786a6f8b0493bc2239e70cbacdb (patch) | |
tree | e29b2441cc916a174d7cd0b03cd18986ae545250 /security | |
parent | 778c4a4d60d932c1df6d270dcbc88365823c3963 (diff) |
TOMOYO: Simplify garbage collector.
When TOMOYO started using garbage collector at commit 847b173e "TOMOYO: Add
garbage collector.", we waited for close() before kfree(). Thus, elements to be
kfree()d were queued up using tomoyo_gc_list list.
But it turned out that tomoyo_element_linked_by_gc() tends to choke garbage
collector when certain pattern of entries are queued.
Since garbage collector is no longer waiting for close() since commit 2e503bbb
"TOMOYO: Fix lockdep warning.", we can remove tomoyo_gc_list list and
tomoyo_element_linked_by_gc() by doing sequential processing.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security')
-rw-r--r-- | security/tomoyo/common.h | 7 | ||||
-rw-r--r-- | security/tomoyo/condition.c | 8 | ||||
-rw-r--r-- | security/tomoyo/domain.c | 4 | ||||
-rw-r--r-- | security/tomoyo/gc.c | 480 | ||||
-rw-r--r-- | security/tomoyo/memory.c | 6 |
5 files changed, 186 insertions, 319 deletions
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 1a19ad3e67ea..a0212fbf60fb 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h | |||
@@ -52,6 +52,9 @@ | |||
52 | 52 | ||
53 | #define TOMOYO_EXEC_TMPSIZE 4096 | 53 | #define TOMOYO_EXEC_TMPSIZE 4096 |
54 | 54 | ||
55 | /* Garbage collector is trying to kfree() this element. */ | ||
56 | #define TOMOYO_GC_IN_PROGRESS -1 | ||
57 | |||
55 | /* Profile number is an integer between 0 and 255. */ | 58 | /* Profile number is an integer between 0 and 255. */ |
56 | #define TOMOYO_MAX_PROFILES 256 | 59 | #define TOMOYO_MAX_PROFILES 256 |
57 | 60 | ||
@@ -398,7 +401,7 @@ enum tomoyo_pref_index { | |||
398 | /* Common header for holding ACL entries. */ | 401 | /* Common header for holding ACL entries. */ |
399 | struct tomoyo_acl_head { | 402 | struct tomoyo_acl_head { |
400 | struct list_head list; | 403 | struct list_head list; |
401 | bool is_deleted; | 404 | s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */ |
402 | } __packed; | 405 | } __packed; |
403 | 406 | ||
404 | /* Common header for shared entries. */ | 407 | /* Common header for shared entries. */ |
@@ -665,7 +668,7 @@ struct tomoyo_condition { | |||
665 | struct tomoyo_acl_info { | 668 | struct tomoyo_acl_info { |
666 | struct list_head list; | 669 | struct list_head list; |
667 | struct tomoyo_condition *cond; /* Maybe NULL. */ | 670 | struct tomoyo_condition *cond; /* Maybe NULL. */ |
668 | bool is_deleted; | 671 | s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */ |
669 | u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */ | 672 | u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */ |
670 | } __packed; | 673 | } __packed; |
671 | 674 | ||
diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c index b854959c0fd4..986330b8c73e 100644 --- a/security/tomoyo/condition.c +++ b/security/tomoyo/condition.c | |||
@@ -400,8 +400,9 @@ static struct tomoyo_condition *tomoyo_commit_condition | |||
400 | found = true; | 400 | found = true; |
401 | goto out; | 401 | goto out; |
402 | } | 402 | } |
403 | list_for_each_entry_rcu(ptr, &tomoyo_condition_list, head.list) { | 403 | list_for_each_entry(ptr, &tomoyo_condition_list, head.list) { |
404 | if (!tomoyo_same_condition(ptr, entry)) | 404 | if (!tomoyo_same_condition(ptr, entry) || |
405 | atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS) | ||
405 | continue; | 406 | continue; |
406 | /* Same entry found. Share this entry. */ | 407 | /* Same entry found. Share this entry. */ |
407 | atomic_inc(&ptr->head.users); | 408 | atomic_inc(&ptr->head.users); |
@@ -411,8 +412,7 @@ static struct tomoyo_condition *tomoyo_commit_condition | |||
411 | if (!found) { | 412 | if (!found) { |
412 | if (tomoyo_memory_ok(entry)) { | 413 | if (tomoyo_memory_ok(entry)) { |
413 | atomic_set(&entry->head.users, 1); | 414 | atomic_set(&entry->head.users, 1); |
414 | list_add_rcu(&entry->head.list, | 415 | list_add(&entry->head.list, &tomoyo_condition_list); |
415 | &tomoyo_condition_list); | ||
416 | } else { | 416 | } else { |
417 | found = true; | 417 | found = true; |
418 | ptr = NULL; | 418 | ptr = NULL; |
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 70acf7aebbda..da16dfeed728 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c | |||
@@ -39,6 +39,8 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, | |||
39 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 39 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
40 | return -ENOMEM; | 40 | return -ENOMEM; |
41 | list_for_each_entry_rcu(entry, list, list) { | 41 | list_for_each_entry_rcu(entry, list, list) { |
42 | if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS) | ||
43 | continue; | ||
42 | if (!check_duplicate(entry, new_entry)) | 44 | if (!check_duplicate(entry, new_entry)) |
43 | continue; | 45 | continue; |
44 | entry->is_deleted = param->is_delete; | 46 | entry->is_deleted = param->is_delete; |
@@ -115,6 +117,8 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, | |||
115 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 117 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
116 | goto out; | 118 | goto out; |
117 | list_for_each_entry_rcu(entry, list, list) { | 119 | list_for_each_entry_rcu(entry, list, list) { |
120 | if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS) | ||
121 | continue; | ||
118 | if (!tomoyo_same_acl_head(entry, new_entry) || | 122 | if (!tomoyo_same_acl_head(entry, new_entry) || |
119 | !check_duplicate(entry, new_entry)) | 123 | !check_duplicate(entry, new_entry)) |
120 | continue; | 124 | continue; |
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 7747ceb9a221..f2295c65f1e4 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c | |||
@@ -13,35 +13,6 @@ static LIST_HEAD(tomoyo_io_buffer_list); | |||
13 | /* Lock for protecting tomoyo_io_buffer_list. */ | 13 | /* Lock for protecting tomoyo_io_buffer_list. */ |
14 | static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock); | 14 | static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock); |
15 | 15 | ||
16 | /* Size of an element. */ | ||
17 | static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = { | ||
18 | [TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group), | ||
19 | [TOMOYO_ID_ADDRESS_GROUP] = sizeof(struct tomoyo_address_group), | ||
20 | [TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group), | ||
21 | [TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group), | ||
22 | [TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator), | ||
23 | [TOMOYO_ID_TRANSITION_CONTROL] = | ||
24 | sizeof(struct tomoyo_transition_control), | ||
25 | [TOMOYO_ID_MANAGER] = sizeof(struct tomoyo_manager), | ||
26 | /* [TOMOYO_ID_CONDITION] = "struct tomoyo_condition"->size, */ | ||
27 | /* [TOMOYO_ID_NAME] = "struct tomoyo_name"->size, */ | ||
28 | /* [TOMOYO_ID_ACL] = | ||
29 | tomoyo_acl_size["struct tomoyo_acl_info"->type], */ | ||
30 | [TOMOYO_ID_DOMAIN] = sizeof(struct tomoyo_domain_info), | ||
31 | }; | ||
32 | |||
33 | /* Size of a domain ACL element. */ | ||
34 | static const u8 tomoyo_acl_size[] = { | ||
35 | [TOMOYO_TYPE_PATH_ACL] = sizeof(struct tomoyo_path_acl), | ||
36 | [TOMOYO_TYPE_PATH2_ACL] = sizeof(struct tomoyo_path2_acl), | ||
37 | [TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl), | ||
38 | [TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl), | ||
39 | [TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl), | ||
40 | [TOMOYO_TYPE_INET_ACL] = sizeof(struct tomoyo_inet_acl), | ||
41 | [TOMOYO_TYPE_UNIX_ACL] = sizeof(struct tomoyo_unix_acl), | ||
42 | [TOMOYO_TYPE_ENV_ACL] = sizeof(struct tomoyo_env_acl), | ||
43 | }; | ||
44 | |||
45 | /** | 16 | /** |
46 | * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not. | 17 | * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not. |
47 | * | 18 | * |
@@ -59,15 +30,11 @@ static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element) | |||
59 | list_for_each_entry(head, &tomoyo_io_buffer_list, list) { | 30 | list_for_each_entry(head, &tomoyo_io_buffer_list, list) { |
60 | head->users++; | 31 | head->users++; |
61 | spin_unlock(&tomoyo_io_buffer_list_lock); | 32 | spin_unlock(&tomoyo_io_buffer_list_lock); |
62 | if (mutex_lock_interruptible(&head->io_sem)) { | 33 | mutex_lock(&head->io_sem); |
63 | in_use = true; | ||
64 | goto out; | ||
65 | } | ||
66 | if (head->r.domain == element || head->r.group == element || | 34 | if (head->r.domain == element || head->r.group == element || |
67 | head->r.acl == element || &head->w.domain->list == element) | 35 | head->r.acl == element || &head->w.domain->list == element) |
68 | in_use = true; | 36 | in_use = true; |
69 | mutex_unlock(&head->io_sem); | 37 | mutex_unlock(&head->io_sem); |
70 | out: | ||
71 | spin_lock(&tomoyo_io_buffer_list_lock); | 38 | spin_lock(&tomoyo_io_buffer_list_lock); |
72 | head->users--; | 39 | head->users--; |
73 | if (in_use) | 40 | if (in_use) |
@@ -81,15 +48,14 @@ out: | |||
81 | * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not. | 48 | * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not. |
82 | * | 49 | * |
83 | * @string: String to check. | 50 | * @string: String to check. |
84 | * @size: Memory allocated for @string . | ||
85 | * | 51 | * |
86 | * Returns true if @string is used by /sys/kernel/security/tomoyo/ users, | 52 | * Returns true if @string is used by /sys/kernel/security/tomoyo/ users, |
87 | * false otherwise. | 53 | * false otherwise. |
88 | */ | 54 | */ |
89 | static bool tomoyo_name_used_by_io_buffer(const char *string, | 55 | static bool tomoyo_name_used_by_io_buffer(const char *string) |
90 | const size_t size) | ||
91 | { | 56 | { |
92 | struct tomoyo_io_buffer *head; | 57 | struct tomoyo_io_buffer *head; |
58 | const size_t size = strlen(string) + 1; | ||
93 | bool in_use = false; | 59 | bool in_use = false; |
94 | 60 | ||
95 | spin_lock(&tomoyo_io_buffer_list_lock); | 61 | spin_lock(&tomoyo_io_buffer_list_lock); |
@@ -97,10 +63,7 @@ static bool tomoyo_name_used_by_io_buffer(const char *string, | |||
97 | int i; | 63 | int i; |
98 | head->users++; | 64 | head->users++; |
99 | spin_unlock(&tomoyo_io_buffer_list_lock); | 65 | spin_unlock(&tomoyo_io_buffer_list_lock); |
100 | if (mutex_lock_interruptible(&head->io_sem)) { | 66 | mutex_lock(&head->io_sem); |
101 | in_use = true; | ||
102 | goto out; | ||
103 | } | ||
104 | for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) { | 67 | for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) { |
105 | const char *w = head->r.w[i]; | 68 | const char *w = head->r.w[i]; |
106 | if (w < string || w > string + size) | 69 | if (w < string || w > string + size) |
@@ -109,7 +72,6 @@ static bool tomoyo_name_used_by_io_buffer(const char *string, | |||
109 | break; | 72 | break; |
110 | } | 73 | } |
111 | mutex_unlock(&head->io_sem); | 74 | mutex_unlock(&head->io_sem); |
112 | out: | ||
113 | spin_lock(&tomoyo_io_buffer_list_lock); | 75 | spin_lock(&tomoyo_io_buffer_list_lock); |
114 | head->users--; | 76 | head->users--; |
115 | if (in_use) | 77 | if (in_use) |
@@ -119,84 +81,6 @@ out: | |||
119 | return in_use; | 81 | return in_use; |
120 | } | 82 | } |
121 | 83 | ||
122 | /* Structure for garbage collection. */ | ||
123 | struct tomoyo_gc { | ||
124 | struct list_head list; | ||
125 | enum tomoyo_policy_id type; | ||
126 | size_t size; | ||
127 | struct list_head *element; | ||
128 | }; | ||
129 | /* List of entries to be deleted. */ | ||
130 | static LIST_HEAD(tomoyo_gc_list); | ||
131 | /* Length of tomoyo_gc_list. */ | ||
132 | static int tomoyo_gc_list_len; | ||
133 | |||
134 | /** | ||
135 | * tomoyo_add_to_gc - Add an entry to to be deleted list. | ||
136 | * | ||
137 | * @type: One of values in "enum tomoyo_policy_id". | ||
138 | * @element: Pointer to "struct list_head". | ||
139 | * | ||
140 | * Returns true on success, false otherwise. | ||
141 | * | ||
142 | * Caller holds tomoyo_policy_lock mutex. | ||
143 | * | ||
144 | * Adding an entry needs kmalloc(). Thus, if we try to add thousands of | ||
145 | * entries at once, it will take too long time. Thus, do not add more than 128 | ||
146 | * entries per a scan. But to be able to handle worst case where all entries | ||
147 | * are in-use, we accept one more entry per a scan. | ||
148 | * | ||
149 | * If we use singly linked list using "struct list_head"->prev (which is | ||
150 | * LIST_POISON2), we can avoid kmalloc(). | ||
151 | */ | ||
152 | static bool tomoyo_add_to_gc(const int type, struct list_head *element) | ||
153 | { | ||
154 | struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); | ||
155 | if (!entry) | ||
156 | return false; | ||
157 | entry->type = type; | ||
158 | if (type == TOMOYO_ID_ACL) | ||
159 | entry->size = tomoyo_acl_size[ | ||
160 | container_of(element, | ||
161 | typeof(struct tomoyo_acl_info), | ||
162 | list)->type]; | ||
163 | else if (type == TOMOYO_ID_NAME) | ||
164 | entry->size = strlen(container_of(element, | ||
165 | typeof(struct tomoyo_name), | ||
166 | head.list)->entry.name) + 1; | ||
167 | else if (type == TOMOYO_ID_CONDITION) | ||
168 | entry->size = | ||
169 | container_of(element, typeof(struct tomoyo_condition), | ||
170 | head.list)->size; | ||
171 | else | ||
172 | entry->size = tomoyo_element_size[type]; | ||
173 | entry->element = element; | ||
174 | list_add(&entry->list, &tomoyo_gc_list); | ||
175 | list_del_rcu(element); | ||
176 | return tomoyo_gc_list_len++ < 128; | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * tomoyo_element_linked_by_gc - Validate next element of an entry. | ||
181 | * | ||
182 | * @element: Pointer to an element. | ||
183 | * @size: Size of @element in byte. | ||
184 | * | ||
185 | * Returns true if @element is linked by other elements in the garbage | ||
186 | * collector's queue, false otherwise. | ||
187 | */ | ||
188 | static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size) | ||
189 | { | ||
190 | struct tomoyo_gc *p; | ||
191 | list_for_each_entry(p, &tomoyo_gc_list, list) { | ||
192 | const u8 *ptr = (const u8 *) p->element->next; | ||
193 | if (ptr < element || element + size < ptr) | ||
194 | continue; | ||
195 | return true; | ||
196 | } | ||
197 | return false; | ||
198 | } | ||
199 | |||
200 | /** | 84 | /** |
201 | * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control". | 85 | * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control". |
202 | * | 86 | * |
@@ -204,7 +88,7 @@ static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size) | |||
204 | * | 88 | * |
205 | * Returns nothing. | 89 | * Returns nothing. |
206 | */ | 90 | */ |
207 | static void tomoyo_del_transition_control(struct list_head *element) | 91 | static inline void tomoyo_del_transition_control(struct list_head *element) |
208 | { | 92 | { |
209 | struct tomoyo_transition_control *ptr = | 93 | struct tomoyo_transition_control *ptr = |
210 | container_of(element, typeof(*ptr), head.list); | 94 | container_of(element, typeof(*ptr), head.list); |
@@ -219,7 +103,7 @@ static void tomoyo_del_transition_control(struct list_head *element) | |||
219 | * | 103 | * |
220 | * Returns nothing. | 104 | * Returns nothing. |
221 | */ | 105 | */ |
222 | static void tomoyo_del_aggregator(struct list_head *element) | 106 | static inline void tomoyo_del_aggregator(struct list_head *element) |
223 | { | 107 | { |
224 | struct tomoyo_aggregator *ptr = | 108 | struct tomoyo_aggregator *ptr = |
225 | container_of(element, typeof(*ptr), head.list); | 109 | container_of(element, typeof(*ptr), head.list); |
@@ -234,7 +118,7 @@ static void tomoyo_del_aggregator(struct list_head *element) | |||
234 | * | 118 | * |
235 | * Returns nothing. | 119 | * Returns nothing. |
236 | */ | 120 | */ |
237 | static void tomoyo_del_manager(struct list_head *element) | 121 | static inline void tomoyo_del_manager(struct list_head *element) |
238 | { | 122 | { |
239 | struct tomoyo_manager *ptr = | 123 | struct tomoyo_manager *ptr = |
240 | container_of(element, typeof(*ptr), head.list); | 124 | container_of(element, typeof(*ptr), head.list); |
@@ -330,44 +214,24 @@ static void tomoyo_del_acl(struct list_head *element) | |||
330 | * | 214 | * |
331 | * @element: Pointer to "struct list_head". | 215 | * @element: Pointer to "struct list_head". |
332 | * | 216 | * |
333 | * Returns true if deleted, false otherwise. | 217 | * Returns nothing. |
334 | */ | 218 | */ |
335 | static bool tomoyo_del_domain(struct list_head *element) | 219 | static inline void tomoyo_del_domain(struct list_head *element) |
336 | { | 220 | { |
337 | struct tomoyo_domain_info *domain = | 221 | struct tomoyo_domain_info *domain = |
338 | container_of(element, typeof(*domain), list); | 222 | container_of(element, typeof(*domain), list); |
339 | struct tomoyo_acl_info *acl; | 223 | struct tomoyo_acl_info *acl; |
340 | struct tomoyo_acl_info *tmp; | 224 | struct tomoyo_acl_info *tmp; |
341 | /* | 225 | /* |
342 | * Since we don't protect whole execve() operation using SRCU, | 226 | * Since this domain is referenced from neither |
343 | * we need to recheck domain->users at this point. | 227 | * "struct tomoyo_io_buffer" nor "struct cred"->security, we can delete |
344 | * | 228 | * elements without checking for is_deleted flag. |
345 | * (1) Reader starts SRCU section upon execve(). | ||
346 | * (2) Reader traverses tomoyo_domain_list and finds this domain. | ||
347 | * (3) Writer marks this domain as deleted. | ||
348 | * (4) Garbage collector removes this domain from tomoyo_domain_list | ||
349 | * because this domain is marked as deleted and used by nobody. | ||
350 | * (5) Reader saves reference to this domain into | ||
351 | * "struct linux_binprm"->cred->security . | ||
352 | * (6) Reader finishes SRCU section, although execve() operation has | ||
353 | * not finished yet. | ||
354 | * (7) Garbage collector waits for SRCU synchronization. | ||
355 | * (8) Garbage collector kfree() this domain because this domain is | ||
356 | * used by nobody. | ||
357 | * (9) Reader finishes execve() operation and restores this domain from | ||
358 | * "struct linux_binprm"->cred->security. | ||
359 | * | ||
360 | * By updating domain->users at (5), we can solve this race problem | ||
361 | * by rechecking domain->users at (8). | ||
362 | */ | 229 | */ |
363 | if (atomic_read(&domain->users)) | ||
364 | return false; | ||
365 | list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { | 230 | list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { |
366 | tomoyo_del_acl(&acl->list); | 231 | tomoyo_del_acl(&acl->list); |
367 | tomoyo_memory_free(acl); | 232 | tomoyo_memory_free(acl); |
368 | } | 233 | } |
369 | tomoyo_put_name(domain->domainname); | 234 | tomoyo_put_name(domain->domainname); |
370 | return true; | ||
371 | } | 235 | } |
372 | 236 | ||
373 | /** | 237 | /** |
@@ -416,10 +280,9 @@ void tomoyo_del_condition(struct list_head *element) | |||
416 | * | 280 | * |
417 | * Returns nothing. | 281 | * Returns nothing. |
418 | */ | 282 | */ |
419 | static void tomoyo_del_name(struct list_head *element) | 283 | static inline void tomoyo_del_name(struct list_head *element) |
420 | { | 284 | { |
421 | const struct tomoyo_name *ptr = | 285 | /* Nothing to do. */ |
422 | container_of(element, typeof(*ptr), head.list); | ||
423 | } | 286 | } |
424 | 287 | ||
425 | /** | 288 | /** |
@@ -429,7 +292,7 @@ static void tomoyo_del_name(struct list_head *element) | |||
429 | * | 292 | * |
430 | * Returns nothing. | 293 | * Returns nothing. |
431 | */ | 294 | */ |
432 | static void tomoyo_del_path_group(struct list_head *element) | 295 | static inline void tomoyo_del_path_group(struct list_head *element) |
433 | { | 296 | { |
434 | struct tomoyo_path_group *member = | 297 | struct tomoyo_path_group *member = |
435 | container_of(element, typeof(*member), head.list); | 298 | container_of(element, typeof(*member), head.list); |
@@ -443,7 +306,7 @@ static void tomoyo_del_path_group(struct list_head *element) | |||
443 | * | 306 | * |
444 | * Returns nothing. | 307 | * Returns nothing. |
445 | */ | 308 | */ |
446 | static void tomoyo_del_group(struct list_head *element) | 309 | static inline void tomoyo_del_group(struct list_head *element) |
447 | { | 310 | { |
448 | struct tomoyo_group *group = | 311 | struct tomoyo_group *group = |
449 | container_of(element, typeof(*group), head.list); | 312 | container_of(element, typeof(*group), head.list); |
@@ -469,10 +332,109 @@ static inline void tomoyo_del_address_group(struct list_head *element) | |||
469 | * | 332 | * |
470 | * Returns nothing. | 333 | * Returns nothing. |
471 | */ | 334 | */ |
472 | static void tomoyo_del_number_group(struct list_head *element) | 335 | static inline void tomoyo_del_number_group(struct list_head *element) |
473 | { | 336 | { |
474 | struct tomoyo_number_group *member = | 337 | /* Nothing to do. */ |
475 | container_of(element, typeof(*member), head.list); | 338 | } |
339 | |||
340 | /** | ||
341 | * tomoyo_try_to_gc - Try to kfree() an entry. | ||
342 | * | ||
343 | * @type: One of values in "enum tomoyo_policy_id". | ||
344 | * @element: Pointer to "struct list_head". | ||
345 | * | ||
346 | * Returns nothing. | ||
347 | * | ||
348 | * Caller holds tomoyo_policy_lock mutex. | ||
349 | */ | ||
350 | static void tomoyo_try_to_gc(const enum tomoyo_policy_id type, | ||
351 | struct list_head *element) | ||
352 | { | ||
353 | /* | ||
354 | * __list_del_entry() guarantees that the list element became no longer | ||
355 | * reachable from the list which the element was originally on (e.g. | ||
356 | * tomoyo_domain_list). Also, synchronize_srcu() guarantees that the | ||
357 | * list element became no longer referenced by syscall users. | ||
358 | */ | ||
359 | __list_del_entry(element); | ||
360 | mutex_unlock(&tomoyo_policy_lock); | ||
361 | synchronize_srcu(&tomoyo_ss); | ||
362 | /* | ||
363 | * However, there are two users which may still be using the list | ||
364 | * element. We need to defer until both users forget this element. | ||
365 | * | ||
366 | * Don't kfree() until "struct tomoyo_io_buffer"->r.{domain,group,acl} | ||
367 | * and "struct tomoyo_io_buffer"->w.domain forget this element. | ||
368 | */ | ||
369 | if (tomoyo_struct_used_by_io_buffer(element)) | ||
370 | goto reinject; | ||
371 | switch (type) { | ||
372 | case TOMOYO_ID_TRANSITION_CONTROL: | ||
373 | tomoyo_del_transition_control(element); | ||
374 | break; | ||
375 | case TOMOYO_ID_MANAGER: | ||
376 | tomoyo_del_manager(element); | ||
377 | break; | ||
378 | case TOMOYO_ID_AGGREGATOR: | ||
379 | tomoyo_del_aggregator(element); | ||
380 | break; | ||
381 | case TOMOYO_ID_GROUP: | ||
382 | tomoyo_del_group(element); | ||
383 | break; | ||
384 | case TOMOYO_ID_PATH_GROUP: | ||
385 | tomoyo_del_path_group(element); | ||
386 | break; | ||
387 | case TOMOYO_ID_ADDRESS_GROUP: | ||
388 | tomoyo_del_address_group(element); | ||
389 | break; | ||
390 | case TOMOYO_ID_NUMBER_GROUP: | ||
391 | tomoyo_del_number_group(element); | ||
392 | break; | ||
393 | case TOMOYO_ID_CONDITION: | ||
394 | tomoyo_del_condition(element); | ||
395 | break; | ||
396 | case TOMOYO_ID_NAME: | ||
397 | /* | ||
398 | * Don't kfree() until all "struct tomoyo_io_buffer"->r.w[] | ||
399 | * forget this element. | ||
400 | */ | ||
401 | if (tomoyo_name_used_by_io_buffer | ||
402 | (container_of(element, typeof(struct tomoyo_name), | ||
403 | head.list)->entry.name)) | ||
404 | goto reinject; | ||
405 | tomoyo_del_name(element); | ||
406 | break; | ||
407 | case TOMOYO_ID_ACL: | ||
408 | tomoyo_del_acl(element); | ||
409 | break; | ||
410 | case TOMOYO_ID_DOMAIN: | ||
411 | /* | ||
412 | * Don't kfree() until all "struct cred"->security forget this | ||
413 | * element. | ||
414 | */ | ||
415 | if (atomic_read(&container_of | ||
416 | (element, typeof(struct tomoyo_domain_info), | ||
417 | list)->users)) | ||
418 | goto reinject; | ||
419 | tomoyo_del_domain(element); | ||
420 | break; | ||
421 | case TOMOYO_MAX_POLICY: | ||
422 | break; | ||
423 | } | ||
424 | mutex_lock(&tomoyo_policy_lock); | ||
425 | tomoyo_memory_free(element); | ||
426 | return; | ||
427 | reinject: | ||
428 | /* | ||
429 | * We can safely reinject this element here bacause | ||
430 | * (1) Appending list elements and removing list elements are protected | ||
431 | * by tomoyo_policy_lock mutex. | ||
432 | * (2) Only this function removes list elements and this function is | ||
433 | * exclusively executed by tomoyo_gc_mutex mutex. | ||
434 | * are true. | ||
435 | */ | ||
436 | mutex_lock(&tomoyo_policy_lock); | ||
437 | list_add_rcu(element, element->prev); | ||
476 | } | 438 | } |
477 | 439 | ||
478 | /** | 440 | /** |
@@ -481,19 +443,19 @@ static void tomoyo_del_number_group(struct list_head *element) | |||
481 | * @id: One of values in "enum tomoyo_policy_id". | 443 | * @id: One of values in "enum tomoyo_policy_id". |
482 | * @member_list: Pointer to "struct list_head". | 444 | * @member_list: Pointer to "struct list_head". |
483 | * | 445 | * |
484 | * Returns true if some elements are deleted, false otherwise. | 446 | * Returns nothing. |
485 | */ | 447 | */ |
486 | static bool tomoyo_collect_member(const enum tomoyo_policy_id id, | 448 | static void tomoyo_collect_member(const enum tomoyo_policy_id id, |
487 | struct list_head *member_list) | 449 | struct list_head *member_list) |
488 | { | 450 | { |
489 | struct tomoyo_acl_head *member; | 451 | struct tomoyo_acl_head *member; |
490 | list_for_each_entry(member, member_list, list) { | 452 | struct tomoyo_acl_head *tmp; |
453 | list_for_each_entry_safe(member, tmp, member_list, list) { | ||
491 | if (!member->is_deleted) | 454 | if (!member->is_deleted) |
492 | continue; | 455 | continue; |
493 | if (!tomoyo_add_to_gc(id, &member->list)) | 456 | member->is_deleted = TOMOYO_GC_IN_PROGRESS; |
494 | return false; | 457 | tomoyo_try_to_gc(id, &member->list); |
495 | } | 458 | } |
496 | return true; | ||
497 | } | 459 | } |
498 | 460 | ||
499 | /** | 461 | /** |
@@ -501,22 +463,22 @@ static bool tomoyo_collect_member(const enum tomoyo_policy_id id, | |||
501 | * | 463 | * |
502 | * @list: Pointer to "struct list_head". | 464 | * @list: Pointer to "struct list_head". |
503 | * | 465 | * |
504 | * Returns true if some elements are deleted, false otherwise. | 466 | * Returns nothing. |
505 | */ | 467 | */ |
506 | static bool tomoyo_collect_acl(struct list_head *list) | 468 | static void tomoyo_collect_acl(struct list_head *list) |
507 | { | 469 | { |
508 | struct tomoyo_acl_info *acl; | 470 | struct tomoyo_acl_info *acl; |
509 | list_for_each_entry(acl, list, list) { | 471 | struct tomoyo_acl_info *tmp; |
472 | list_for_each_entry_safe(acl, tmp, list, list) { | ||
510 | if (!acl->is_deleted) | 473 | if (!acl->is_deleted) |
511 | continue; | 474 | continue; |
512 | if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list)) | 475 | acl->is_deleted = TOMOYO_GC_IN_PROGRESS; |
513 | return false; | 476 | tomoyo_try_to_gc(TOMOYO_ID_ACL, &acl->list); |
514 | } | 477 | } |
515 | return true; | ||
516 | } | 478 | } |
517 | 479 | ||
518 | /** | 480 | /** |
519 | * tomoyo_collect_entry - Scan lists for deleted elements. | 481 | * tomoyo_collect_entry - Try to kfree() deleted elements. |
520 | * | 482 | * |
521 | * Returns nothing. | 483 | * Returns nothing. |
522 | */ | 484 | */ |
@@ -525,36 +487,40 @@ static void tomoyo_collect_entry(void) | |||
525 | int i; | 487 | int i; |
526 | enum tomoyo_policy_id id; | 488 | enum tomoyo_policy_id id; |
527 | struct tomoyo_policy_namespace *ns; | 489 | struct tomoyo_policy_namespace *ns; |
528 | int idx; | 490 | mutex_lock(&tomoyo_policy_lock); |
529 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
530 | return; | ||
531 | idx = tomoyo_read_lock(); | ||
532 | { | 491 | { |
533 | struct tomoyo_domain_info *domain; | 492 | struct tomoyo_domain_info *domain; |
534 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | 493 | struct tomoyo_domain_info *tmp; |
535 | if (!tomoyo_collect_acl(&domain->acl_info_list)) | 494 | list_for_each_entry_safe(domain, tmp, &tomoyo_domain_list, |
536 | goto unlock; | 495 | list) { |
496 | tomoyo_collect_acl(&domain->acl_info_list); | ||
537 | if (!domain->is_deleted || atomic_read(&domain->users)) | 497 | if (!domain->is_deleted || atomic_read(&domain->users)) |
538 | continue; | 498 | continue; |
539 | /* | 499 | tomoyo_try_to_gc(TOMOYO_ID_DOMAIN, &domain->list); |
540 | * Nobody is referring this domain. But somebody may | ||
541 | * refer this domain after successful execve(). | ||
542 | * We recheck domain->users after SRCU synchronization. | ||
543 | */ | ||
544 | if (!tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, &domain->list)) | ||
545 | goto unlock; | ||
546 | } | 500 | } |
547 | } | 501 | } |
548 | list_for_each_entry_rcu(ns, &tomoyo_namespace_list, namespace_list) { | 502 | list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { |
549 | for (id = 0; id < TOMOYO_MAX_POLICY; id++) | 503 | for (id = 0; id < TOMOYO_MAX_POLICY; id++) |
550 | if (!tomoyo_collect_member(id, &ns->policy_list[id])) | 504 | tomoyo_collect_member(id, &ns->policy_list[id]); |
551 | goto unlock; | ||
552 | for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++) | 505 | for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++) |
553 | if (!tomoyo_collect_acl(&ns->acl_group[i])) | 506 | tomoyo_collect_acl(&ns->acl_group[i]); |
554 | goto unlock; | 507 | } |
508 | { | ||
509 | struct tomoyo_shared_acl_head *ptr; | ||
510 | struct tomoyo_shared_acl_head *tmp; | ||
511 | list_for_each_entry_safe(ptr, tmp, &tomoyo_condition_list, | ||
512 | list) { | ||
513 | if (atomic_read(&ptr->users) > 0) | ||
514 | continue; | ||
515 | atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS); | ||
516 | tomoyo_try_to_gc(TOMOYO_ID_CONDITION, &ptr->list); | ||
517 | } | ||
518 | } | ||
519 | list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { | ||
555 | for (i = 0; i < TOMOYO_MAX_GROUP; i++) { | 520 | for (i = 0; i < TOMOYO_MAX_GROUP; i++) { |
556 | struct list_head *list = &ns->group_list[i]; | 521 | struct list_head *list = &ns->group_list[i]; |
557 | struct tomoyo_group *group; | 522 | struct tomoyo_group *group; |
523 | struct tomoyo_group *tmp; | ||
558 | switch (i) { | 524 | switch (i) { |
559 | case 0: | 525 | case 0: |
560 | id = TOMOYO_ID_PATH_GROUP; | 526 | id = TOMOYO_ID_PATH_GROUP; |
@@ -566,139 +532,37 @@ static void tomoyo_collect_entry(void) | |||
566 | id = TOMOYO_ID_ADDRESS_GROUP; | 532 | id = TOMOYO_ID_ADDRESS_GROUP; |
567 | break; | 533 | break; |
568 | } | 534 | } |
569 | list_for_each_entry(group, list, head.list) { | 535 | list_for_each_entry_safe(group, tmp, list, head.list) { |
570 | if (!tomoyo_collect_member | 536 | tomoyo_collect_member(id, &group->member_list); |
571 | (id, &group->member_list)) | ||
572 | goto unlock; | ||
573 | if (!list_empty(&group->member_list) || | 537 | if (!list_empty(&group->member_list) || |
574 | atomic_read(&group->head.users)) | 538 | atomic_read(&group->head.users) > 0) |
575 | continue; | 539 | continue; |
576 | if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, | 540 | atomic_set(&group->head.users, |
577 | &group->head.list)) | 541 | TOMOYO_GC_IN_PROGRESS); |
578 | goto unlock; | 542 | tomoyo_try_to_gc(TOMOYO_ID_GROUP, |
543 | &group->head.list); | ||
579 | } | 544 | } |
580 | } | 545 | } |
581 | } | 546 | } |
582 | id = TOMOYO_ID_CONDITION; | 547 | for (i = 0; i < TOMOYO_MAX_HASH; i++) { |
583 | for (i = 0; i < TOMOYO_MAX_HASH + 1; i++) { | 548 | struct list_head *list = &tomoyo_name_list[i]; |
584 | struct list_head *list = !i ? | ||
585 | &tomoyo_condition_list : &tomoyo_name_list[i - 1]; | ||
586 | struct tomoyo_shared_acl_head *ptr; | 549 | struct tomoyo_shared_acl_head *ptr; |
587 | list_for_each_entry(ptr, list, list) { | 550 | struct tomoyo_shared_acl_head *tmp; |
588 | if (atomic_read(&ptr->users)) | 551 | list_for_each_entry_safe(ptr, tmp, list, list) { |
552 | if (atomic_read(&ptr->users) > 0) | ||
589 | continue; | 553 | continue; |
590 | if (!tomoyo_add_to_gc(id, &ptr->list)) | 554 | atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS); |
591 | goto unlock; | 555 | tomoyo_try_to_gc(TOMOYO_ID_NAME, &ptr->list); |
592 | } | 556 | } |
593 | id = TOMOYO_ID_NAME; | ||
594 | } | 557 | } |
595 | unlock: | ||
596 | tomoyo_read_unlock(idx); | ||
597 | mutex_unlock(&tomoyo_policy_lock); | 558 | mutex_unlock(&tomoyo_policy_lock); |
598 | } | 559 | } |
599 | 560 | ||
600 | /** | 561 | /** |
601 | * tomoyo_kfree_entry - Delete entries in tomoyo_gc_list. | ||
602 | * | ||
603 | * Returns true if some entries were kfree()d, false otherwise. | ||
604 | */ | ||
605 | static bool tomoyo_kfree_entry(void) | ||
606 | { | ||
607 | struct tomoyo_gc *p; | ||
608 | struct tomoyo_gc *tmp; | ||
609 | bool result = false; | ||
610 | |||
611 | list_for_each_entry_safe(p, tmp, &tomoyo_gc_list, list) { | ||
612 | struct list_head *element = p->element; | ||
613 | |||
614 | /* | ||
615 | * list_del_rcu() in tomoyo_add_to_gc() guarantees that the | ||
616 | * list element became no longer reachable from the list which | ||
617 | * the element was originally on (e.g. tomoyo_domain_list). | ||
618 | * Also, synchronize_srcu() in tomoyo_gc_thread() guarantees | ||
619 | * that the list element became no longer referenced by syscall | ||
620 | * users. | ||
621 | * | ||
622 | * However, there are three users which may still be using the | ||
623 | * list element. We need to defer until all of these users | ||
624 | * forget the list element. | ||
625 | * | ||
626 | * Firstly, defer until "struct tomoyo_io_buffer"->r.{domain, | ||
627 | * group,acl} and "struct tomoyo_io_buffer"->w.domain forget | ||
628 | * the list element. | ||
629 | */ | ||
630 | if (tomoyo_struct_used_by_io_buffer(element)) | ||
631 | continue; | ||
632 | /* | ||
633 | * Secondly, defer until all other elements in the | ||
634 | * tomoyo_gc_list list forget the list element. | ||
635 | */ | ||
636 | if (tomoyo_element_linked_by_gc((const u8 *) element, p->size)) | ||
637 | continue; | ||
638 | switch (p->type) { | ||
639 | case TOMOYO_ID_TRANSITION_CONTROL: | ||
640 | tomoyo_del_transition_control(element); | ||
641 | break; | ||
642 | case TOMOYO_ID_AGGREGATOR: | ||
643 | tomoyo_del_aggregator(element); | ||
644 | break; | ||
645 | case TOMOYO_ID_MANAGER: | ||
646 | tomoyo_del_manager(element); | ||
647 | break; | ||
648 | case TOMOYO_ID_CONDITION: | ||
649 | tomoyo_del_condition(element); | ||
650 | break; | ||
651 | case TOMOYO_ID_NAME: | ||
652 | /* | ||
653 | * Thirdly, defer until all "struct tomoyo_io_buffer" | ||
654 | * ->r.w[] forget the list element. | ||
655 | */ | ||
656 | if (tomoyo_name_used_by_io_buffer( | ||
657 | container_of(element, typeof(struct tomoyo_name), | ||
658 | head.list)->entry.name, p->size)) | ||
659 | continue; | ||
660 | tomoyo_del_name(element); | ||
661 | break; | ||
662 | case TOMOYO_ID_ACL: | ||
663 | tomoyo_del_acl(element); | ||
664 | break; | ||
665 | case TOMOYO_ID_DOMAIN: | ||
666 | if (!tomoyo_del_domain(element)) | ||
667 | continue; | ||
668 | break; | ||
669 | case TOMOYO_ID_PATH_GROUP: | ||
670 | tomoyo_del_path_group(element); | ||
671 | break; | ||
672 | case TOMOYO_ID_ADDRESS_GROUP: | ||
673 | tomoyo_del_address_group(element); | ||
674 | break; | ||
675 | case TOMOYO_ID_GROUP: | ||
676 | tomoyo_del_group(element); | ||
677 | break; | ||
678 | case TOMOYO_ID_NUMBER_GROUP: | ||
679 | tomoyo_del_number_group(element); | ||
680 | break; | ||
681 | case TOMOYO_MAX_POLICY: | ||
682 | break; | ||
683 | } | ||
684 | tomoyo_memory_free(element); | ||
685 | list_del(&p->list); | ||
686 | kfree(p); | ||
687 | tomoyo_gc_list_len--; | ||
688 | result = true; | ||
689 | } | ||
690 | return result; | ||
691 | } | ||
692 | |||
693 | /** | ||
694 | * tomoyo_gc_thread - Garbage collector thread function. | 562 | * tomoyo_gc_thread - Garbage collector thread function. |
695 | * | 563 | * |
696 | * @unused: Unused. | 564 | * @unused: Unused. |
697 | * | 565 | * |
698 | * In case OOM-killer choose this thread for termination, we create this thread | ||
699 | * as a short live thread whenever /sys/kernel/security/tomoyo/ interface was | ||
700 | * close()d. | ||
701 | * | ||
702 | * Returns 0. | 566 | * Returns 0. |
703 | */ | 567 | */ |
704 | static int tomoyo_gc_thread(void *unused) | 568 | static int tomoyo_gc_thread(void *unused) |
@@ -707,13 +571,7 @@ static int tomoyo_gc_thread(void *unused) | |||
707 | static DEFINE_MUTEX(tomoyo_gc_mutex); | 571 | static DEFINE_MUTEX(tomoyo_gc_mutex); |
708 | if (!mutex_trylock(&tomoyo_gc_mutex)) | 572 | if (!mutex_trylock(&tomoyo_gc_mutex)) |
709 | goto out; | 573 | goto out; |
710 | 574 | tomoyo_collect_entry(); | |
711 | do { | ||
712 | tomoyo_collect_entry(); | ||
713 | if (list_empty(&tomoyo_gc_list)) | ||
714 | break; | ||
715 | synchronize_srcu(&tomoyo_ss); | ||
716 | } while (tomoyo_kfree_entry()); | ||
717 | { | 575 | { |
718 | struct tomoyo_io_buffer *head; | 576 | struct tomoyo_io_buffer *head; |
719 | struct tomoyo_io_buffer *tmp; | 577 | struct tomoyo_io_buffer *tmp; |
diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 7a56051146c2..277b9ade4408 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c | |||
@@ -123,7 +123,8 @@ struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param, | |||
123 | goto out; | 123 | goto out; |
124 | list = ¶m->ns->group_list[idx]; | 124 | list = ¶m->ns->group_list[idx]; |
125 | list_for_each_entry(group, list, head.list) { | 125 | list_for_each_entry(group, list, head.list) { |
126 | if (e.group_name != group->group_name) | 126 | if (e.group_name != group->group_name || |
127 | atomic_read(&group->head.users) == TOMOYO_GC_IN_PROGRESS) | ||
127 | continue; | 128 | continue; |
128 | atomic_inc(&group->head.users); | 129 | atomic_inc(&group->head.users); |
129 | found = true; | 130 | found = true; |
@@ -175,7 +176,8 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name) | |||
175 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 176 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
176 | return NULL; | 177 | return NULL; |
177 | list_for_each_entry(ptr, head, head.list) { | 178 | list_for_each_entry(ptr, head, head.list) { |
178 | if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) | 179 | if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) || |
180 | atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS) | ||
179 | continue; | 181 | continue; |
180 | atomic_inc(&ptr->head.users); | 182 | atomic_inc(&ptr->head.users); |
181 | goto out; | 183 | goto out; |