1 |
/* |
2 |
* security/ccsecurity/gc.c |
3 |
* |
4 |
* Copyright (C) 2005-2010 NTT DATA CORPORATION |
5 |
* |
6 |
* Version: 1.8.0-pre 2010/08/01 |
7 |
* |
8 |
* This file is applicable to both 2.4.30 and 2.6.11 and later. |
9 |
* See README.ccs for ChangeLog. |
10 |
* |
11 |
*/ |
12 |
|
13 |
#include <linux/version.h> |
14 |
#include "internal.h" |
15 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) |
16 |
#include <linux/kthread.h> |
17 |
#endif |
18 |
|
19 |
/* Structure for garbage collection. */ |
20 |
struct ccs_gc { |
21 |
struct list_head list; |
22 |
int type; /* = one of values in "enum ccs_policy_id" */ |
23 |
struct list_head *element; |
24 |
}; |
25 |
/* List of entries to be deleted. */ |
26 |
static LIST_HEAD(ccs_gc_list); |
27 |
/* Length of list. */ |
28 |
static int ccs_gc_list_len; |
29 |
|
30 |
/** |
31 |
* ccs_add_to_gc - Add an entry to to be deleted list. |
32 |
* |
33 |
* @type: Type of this entry. |
34 |
* @element: Pointer to "struct list_head". |
35 |
* |
36 |
* Returns true on success, false otherwise. |
37 |
* |
38 |
* Caller holds ccs_policy_lock mutex. |
39 |
* |
40 |
* Adding an entry needs kmalloc(). Thus, if we try to add thousands of |
41 |
* entries at once, it will take too long time. Thus, do not add more than 128 |
42 |
* entries per a scan. But to be able to handle worst case where all entries |
43 |
* are in-use, we accept one more entry per a scan. |
44 |
* |
45 |
* If we use singly linked list using "struct list_head"->prev (which is |
46 |
* LIST_POISON2), we can avoid kmalloc(). |
47 |
*/ |
48 |
static bool ccs_add_to_gc(const int type, struct list_head *element) |
49 |
{ |
50 |
struct ccs_gc *entry = kzalloc(sizeof(*entry), CCS_GFP_FLAGS); |
51 |
if (!entry) |
52 |
return false; |
53 |
entry->type = type; |
54 |
entry->element = element; |
55 |
list_add(&entry->list, &ccs_gc_list); |
56 |
list_del_rcu(element); |
57 |
return ccs_gc_list_len++ < 128; |
58 |
} |
59 |
|
60 |
/** |
61 |
* ccs_del_file_pattern - Delete members in "struct ccs_pattern". |
62 |
* |
63 |
* @element: Pointer to "struct list_head". |
64 |
* |
65 |
* Returns size of @element (for later kfree()). |
66 |
*/ |
67 |
static inline size_t ccs_del_file_pattern(struct list_head *element) |
68 |
{ |
69 |
struct ccs_pattern *ptr = |
70 |
container_of(element, typeof(*ptr), head.list); |
71 |
ccs_put_name(ptr->pattern); |
72 |
return sizeof(*ptr); |
73 |
} |
74 |
|
75 |
/** |
76 |
* ccs_del_transition_control - Delete members in "struct ccs_transition_control". |
77 |
* |
78 |
* @element: Pointer to "struct list_head". |
79 |
* |
80 |
* Returns size of @element (for later kfree()). |
81 |
*/ |
82 |
static inline size_t ccs_del_transition_control(struct list_head *element) |
83 |
{ |
84 |
struct ccs_transition_control *ptr = |
85 |
container_of(element, typeof(*ptr), head.list); |
86 |
ccs_put_name(ptr->domainname); |
87 |
ccs_put_name(ptr->program); |
88 |
return sizeof(*ptr); |
89 |
} |
90 |
|
91 |
/** |
92 |
* ccs_del_aggregator - Delete members in "struct ccs_aggregator". |
93 |
* |
94 |
* @element: Pointer to "struct list_head". |
95 |
* |
96 |
* Returns size of @element (for later kfree()). |
97 |
*/ |
98 |
static inline size_t ccs_del_aggregator(struct list_head *element) |
99 |
{ |
100 |
struct ccs_aggregator *ptr = |
101 |
container_of(element, typeof(*ptr), head.list); |
102 |
ccs_put_name(ptr->original_name); |
103 |
ccs_put_name(ptr->aggregated_name); |
104 |
return sizeof(*ptr); |
105 |
} |
106 |
|
107 |
/** |
108 |
* ccs_del_manager - Delete members in "struct ccs_manager". |
109 |
* |
110 |
* @element: Pointer to "struct list_head". |
111 |
* |
112 |
* Returns size of @element (for later kfree()). |
113 |
*/ |
114 |
static inline size_t ccs_del_manager(struct list_head *element) |
115 |
{ |
116 |
struct ccs_manager *ptr = |
117 |
container_of(element, typeof(*ptr), head.list); |
118 |
ccs_put_name(ptr->manager); |
119 |
return sizeof(*ptr); |
120 |
} |
121 |
|
122 |
/* For compatibility with older kernels. */ |
123 |
#ifndef for_each_process |
124 |
#define for_each_process for_each_task |
125 |
#endif |
126 |
|
127 |
/** |
128 |
* ccs_used_by_task - Check whether the given pointer is referenced by a task. |
129 |
* |
130 |
* @domain: Pointer to "struct ccs_domain_info". |
131 |
* |
132 |
* Returns true if @domain is in use, false otherwise. |
133 |
*/ |
134 |
static bool ccs_used_by_task(struct ccs_domain_info *domain) |
135 |
{ |
136 |
bool in_use = false; |
137 |
/* |
138 |
* Don't delete this domain if somebody is doing execve(). |
139 |
* |
140 |
* Since ccs_finish_execve() first reverts ccs_domain_info and then |
141 |
* updates ccs_flags , we need smp_mb() to make sure that GC first |
142 |
* checks ccs_flags and then checks ccs_domain_info . |
143 |
*/ |
144 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) |
145 |
struct task_struct *g; |
146 |
struct task_struct *t; |
147 |
ccs_tasklist_lock(); |
148 |
do_each_thread(g, t) { |
149 |
if (!(t->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { |
150 |
smp_mb(); /* Avoid out of order execution. */ |
151 |
if (t->ccs_domain_info != domain) |
152 |
continue; |
153 |
} |
154 |
in_use = true; |
155 |
goto out; |
156 |
} while_each_thread(g, t); |
157 |
out: |
158 |
ccs_tasklist_unlock(); |
159 |
#else |
160 |
struct task_struct *p; |
161 |
ccs_tasklist_lock(); |
162 |
for_each_process(p) { |
163 |
if (!(p->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { |
164 |
smp_mb(); /* Avoid out of order execution. */ |
165 |
if (p->ccs_domain_info != domain) |
166 |
continue; |
167 |
} |
168 |
in_use = true; |
169 |
break; |
170 |
} |
171 |
ccs_tasklist_unlock(); |
172 |
#endif |
173 |
return in_use; |
174 |
} |
175 |
|
176 |
/** |
177 |
* ccs_del_acl - Delete members in "struct ccs_acl_info". |
178 |
* |
179 |
* @element: Pointer to "struct list_head". |
180 |
* |
181 |
* Returns size of @element (for later kfree()). |
182 |
*/ |
183 |
static size_t ccs_del_acl(struct list_head *element) |
184 |
{ |
185 |
size_t size; |
186 |
struct ccs_acl_info *acl = container_of(element, typeof(*acl), list); |
187 |
ccs_put_condition(acl->cond); |
188 |
switch (acl->type) { |
189 |
case CCS_TYPE_PATH_ACL: |
190 |
{ |
191 |
struct ccs_path_acl *entry; |
192 |
size = sizeof(*entry); |
193 |
entry = container_of(acl, typeof(*entry), head); |
194 |
ccs_put_name_union(&entry->name); |
195 |
} |
196 |
break; |
197 |
case CCS_TYPE_PATH2_ACL: |
198 |
{ |
199 |
struct ccs_path2_acl *entry; |
200 |
size = sizeof(*entry); |
201 |
entry = container_of(acl, typeof(*entry), head); |
202 |
ccs_put_name_union(&entry->name1); |
203 |
ccs_put_name_union(&entry->name2); |
204 |
} |
205 |
break; |
206 |
case CCS_TYPE_PATH_NUMBER_ACL: |
207 |
{ |
208 |
struct ccs_path_number_acl *entry; |
209 |
size = sizeof(*entry); |
210 |
entry = container_of(acl, typeof(*entry), head); |
211 |
ccs_put_name_union(&entry->name); |
212 |
ccs_put_number_union(&entry->number); |
213 |
} |
214 |
break; |
215 |
case CCS_TYPE_MKDEV_ACL: |
216 |
{ |
217 |
struct ccs_mkdev_acl *entry; |
218 |
size = sizeof(*entry); |
219 |
entry = container_of(acl, typeof(*entry), head); |
220 |
ccs_put_name_union(&entry->name); |
221 |
ccs_put_number_union(&entry->mode); |
222 |
ccs_put_number_union(&entry->major); |
223 |
ccs_put_number_union(&entry->minor); |
224 |
} |
225 |
break; |
226 |
case CCS_TYPE_MOUNT_ACL: |
227 |
{ |
228 |
struct ccs_mount_acl *entry; |
229 |
size = sizeof(*entry); |
230 |
entry = container_of(acl, typeof(*entry), head); |
231 |
ccs_put_name_union(&entry->dev_name); |
232 |
ccs_put_name_union(&entry->dir_name); |
233 |
ccs_put_name_union(&entry->fs_type); |
234 |
ccs_put_number_union(&entry->flags); |
235 |
} |
236 |
break; |
237 |
case CCS_TYPE_INET_ACL: |
238 |
{ |
239 |
struct ccs_inet_acl *entry; |
240 |
size = sizeof(*entry); |
241 |
entry = container_of(acl, typeof(*entry), head); |
242 |
switch (entry->address_type) { |
243 |
case CCS_IP_ADDRESS_TYPE_ADDRESS_GROUP: |
244 |
ccs_put_group(entry->address.group); |
245 |
break; |
246 |
case CCS_IP_ADDRESS_TYPE_IPv6: |
247 |
ccs_put_ipv6_address(entry->address.ipv6.min); |
248 |
ccs_put_ipv6_address(entry->address.ipv6.max); |
249 |
break; |
250 |
} |
251 |
ccs_put_number_union(&entry->port); |
252 |
} |
253 |
break; |
254 |
case CCS_TYPE_UNIX_ACL: |
255 |
{ |
256 |
struct ccs_unix_acl *entry; |
257 |
size = sizeof(*entry); |
258 |
entry = container_of(acl, typeof(*entry), head); |
259 |
ccs_put_name_union(&entry->name); |
260 |
} |
261 |
break; |
262 |
case CCS_TYPE_ENV_ACL: |
263 |
{ |
264 |
struct ccs_env_acl *entry; |
265 |
size = sizeof(*entry); |
266 |
entry = container_of(acl, typeof(*entry), head); |
267 |
ccs_put_name(entry->env); |
268 |
} |
269 |
break; |
270 |
case CCS_TYPE_CAPABILITY_ACL: |
271 |
{ |
272 |
struct ccs_capability_acl *entry; |
273 |
size = sizeof(*entry); |
274 |
entry = container_of(acl, typeof(*entry), head); |
275 |
} |
276 |
break; |
277 |
case CCS_TYPE_SIGNAL_ACL: |
278 |
{ |
279 |
struct ccs_signal_acl *entry; |
280 |
size = sizeof(*entry); |
281 |
entry = container_of(acl, typeof(*entry), head); |
282 |
ccs_put_name(entry->domainname); |
283 |
} |
284 |
break; |
285 |
case CCS_TYPE_EXECUTE_HANDLER: |
286 |
case CCS_TYPE_DENIED_EXECUTE_HANDLER: |
287 |
{ |
288 |
struct ccs_execute_handler *entry; |
289 |
size = sizeof(*entry); |
290 |
entry = container_of(acl, typeof(*entry), head); |
291 |
ccs_put_name(entry->handler); |
292 |
} |
293 |
break; |
294 |
default: |
295 |
size = 0; |
296 |
break; |
297 |
} |
298 |
return size; |
299 |
} |
300 |
|
301 |
/** |
302 |
* ccs_del_domain - Delete members in "struct ccs_domain_info". |
303 |
* |
304 |
* @element: Pointer to "struct list_head". |
305 |
* |
306 |
* Returns size of @element (for later kfree()) on success, 0 otherwise. |
307 |
*/ |
308 |
static inline size_t ccs_del_domain(struct list_head *element) |
309 |
{ |
310 |
struct ccs_domain_info *domain = |
311 |
container_of(element, typeof(*domain), list); |
312 |
struct ccs_acl_info *acl; |
313 |
struct ccs_acl_info *tmp; |
314 |
/* |
315 |
* We need to recheck domain at this point. |
316 |
* |
317 |
* (1) Reader starts SRCU section upon execve(). |
318 |
* (2) Reader traverses ccs_domain_list and finds this domain. |
319 |
* (3) Writer marks this domain as deleted. |
320 |
* (4) Garbage collector removes this domain from ccs_domain_list |
321 |
* because this domain is marked as deleted and used by nobody. |
322 |
* (5) Reader saves reference to this domain into |
323 |
* "struct task_struct"->ccs_domain_info . |
324 |
* (6) Reader finishes execve() operation and starts using this domain. |
325 |
* (7) Garbage collector waits for SRCU synchronization. |
326 |
* (8) Garbage collector kfree() this domain. |
327 |
* |
328 |
* By rechecking whether this domain is used by somebody or not at (8), |
329 |
* we can solve this race problem. |
330 |
*/ |
331 |
if (ccs_used_by_task(domain)) |
332 |
return 0; |
333 |
list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { |
334 |
size_t size = ccs_del_acl(&acl->list); |
335 |
ccs_memory_free(acl, size); |
336 |
} |
337 |
ccs_put_name(domain->domainname); |
338 |
return sizeof(*domain); |
339 |
} |
340 |
|
341 |
/** |
342 |
* ccs_del_path_group - Delete members in "struct ccs_path_group". |
343 |
* |
344 |
* @element: Pointer to "struct list_head". |
345 |
* |
346 |
* Returns size of @element (for later kfree()). |
347 |
*/ |
348 |
static inline size_t ccs_del_path_group(struct list_head *element) |
349 |
{ |
350 |
struct ccs_path_group *member = |
351 |
container_of(element, typeof(*member), head.list); |
352 |
ccs_put_name(member->member_name); |
353 |
return sizeof(*member); |
354 |
} |
355 |
|
356 |
/** |
357 |
* ccs_del_group - Delete "struct ccs_group". |
358 |
* |
359 |
* @element: Pointer to "struct list_head". |
360 |
* |
361 |
* Returns size of @element (for later kfree()). |
362 |
*/ |
363 |
static inline size_t ccs_del_group(struct list_head *element) |
364 |
{ |
365 |
struct ccs_group *group = |
366 |
container_of(element, typeof(*group), head.list); |
367 |
ccs_put_name(group->group_name); |
368 |
return sizeof(*group); |
369 |
} |
370 |
|
371 |
/** |
372 |
* ccs_del_address_group - Delete members in "struct ccs_address_group". |
373 |
* |
374 |
* @element: Pointer to "struct list_head". |
375 |
* |
376 |
* Returns size of @element (for later kfree()). |
377 |
*/ |
378 |
static inline size_t ccs_del_address_group(struct list_head *element) |
379 |
{ |
380 |
struct ccs_address_group *member = |
381 |
container_of(element, typeof(*member), head.list); |
382 |
if (member->is_ipv6) { |
383 |
ccs_put_ipv6_address(member->min.ipv6); |
384 |
ccs_put_ipv6_address(member->max.ipv6); |
385 |
} |
386 |
return sizeof(*member); |
387 |
} |
388 |
|
389 |
/** |
390 |
* ccs_del_number_group - Delete members in "struct ccs_number_group". |
391 |
* |
392 |
* @element: Pointer to "struct list_head". |
393 |
* |
394 |
* Returns size of @element (for later kfree()). |
395 |
*/ |
396 |
static inline size_t ccs_del_number_group(struct list_head *element) |
397 |
{ |
398 |
struct ccs_number_group *member = |
399 |
container_of(element, typeof(*member), head.list); |
400 |
return sizeof(*member); |
401 |
} |
402 |
|
403 |
/** |
404 |
* ccs_del_reservedport - Delete members in "struct ccs_reserved". |
405 |
* |
406 |
* @element: Pointer to "struct list_head". |
407 |
* |
408 |
* Returns size of @element (for later kfree()). |
409 |
*/ |
410 |
static inline size_t ccs_del_reservedport(struct list_head *element) |
411 |
{ |
412 |
struct ccs_reserved *ptr = |
413 |
container_of(element, typeof(*ptr), head.list); |
414 |
return sizeof(*ptr); |
415 |
} |
416 |
|
417 |
/** |
418 |
* ccs_del_ipv6_address - Delete members in "struct ccs_ipv6addr". |
419 |
* |
420 |
* @element: Pointer to "struct list_head". |
421 |
* |
422 |
* Returns size of @element (for later kfree()). |
423 |
*/ |
424 |
static inline size_t ccs_del_ipv6_address(struct list_head *element) |
425 |
{ |
426 |
struct ccs_ipv6addr *ptr = |
427 |
container_of(element, typeof(*ptr), head.list); |
428 |
return sizeof(*ptr); |
429 |
} |
430 |
|
431 |
/** |
432 |
* ccs_del_condition - Delete members in "struct ccs_condition". |
433 |
* |
434 |
* @element: Pointer to "struct list_head". |
435 |
* |
436 |
* Returns size of condition (for later kfree()). |
437 |
*/ |
438 |
size_t ccs_del_condition(struct list_head *element) |
439 |
{ |
440 |
struct ccs_condition *cond = container_of(element, typeof(*cond), |
441 |
head.list); |
442 |
const u16 condc = cond->condc; |
443 |
const u16 numbers_count = cond->numbers_count; |
444 |
const u16 names_count = cond->names_count; |
445 |
const u16 argc = cond->argc; |
446 |
const u16 envc = cond->envc; |
447 |
unsigned int i; |
448 |
const struct ccs_condition_element *condp |
449 |
= (const struct ccs_condition_element *) (cond + 1); |
450 |
struct ccs_number_union *numbers_p |
451 |
= (struct ccs_number_union *) (condp + condc); |
452 |
struct ccs_name_union *names_p |
453 |
= (struct ccs_name_union *) (numbers_p + numbers_count); |
454 |
const struct ccs_argv *argv |
455 |
= (const struct ccs_argv *) (names_p + names_count); |
456 |
const struct ccs_envp *envp |
457 |
= (const struct ccs_envp *) (argv + argc); |
458 |
for (i = 0; i < numbers_count; i++) |
459 |
ccs_put_number_union(numbers_p++); |
460 |
for (i = 0; i < names_count; i++) |
461 |
ccs_put_name_union(names_p++); |
462 |
for (i = 0; i < argc; argv++, i++) |
463 |
ccs_put_name(argv->value); |
464 |
for (i = 0; i < envc; envp++, i++) { |
465 |
ccs_put_name(envp->name); |
466 |
ccs_put_name(envp->value); |
467 |
} |
468 |
return cond->size; |
469 |
} |
470 |
|
471 |
/** |
472 |
* ccs_del_name - Delete members in "struct ccs_name". |
473 |
* |
474 |
* @element: Pointer to "struct list_head". |
475 |
* |
476 |
* Returns size of @element (for later kfree()). |
477 |
*/ |
478 |
static inline size_t ccs_del_name(struct list_head *element) |
479 |
{ |
480 |
const struct ccs_name *ptr = |
481 |
container_of(element, typeof(*ptr), head.list); |
482 |
return ptr->size; |
483 |
} |
484 |
|
485 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) |
486 |
/* Lock for syscall users. */ |
487 |
struct srcu_struct ccs_ss; |
488 |
#endif |
489 |
|
490 |
/* |
491 |
* Lock for /proc/ccs/ users. |
492 |
* |
493 |
* Currently, we hold SRCU lock upon open() and release upon close(). |
494 |
* Thus, kernel complains about returning to userspace with SRCU lock held. |
495 |
* Therefore, non-SRCU lock is used for suppressing the kernel's complain |
496 |
* messages. Modifying to hold/release SRCU lock upon each read()/write() is |
497 |
* to-do list. |
498 |
* |
499 |
* Also used for syscall users for 2.6.18 and earlier kernels because |
500 |
* they don't have SRCU support. |
501 |
*/ |
502 |
static struct { |
503 |
int counter_idx; |
504 |
int counter[2]; |
505 |
} ccs_counter; |
506 |
/* Lock for protecting counter. */ |
507 |
static DEFINE_SPINLOCK(ccs_counter_lock); |
508 |
|
509 |
/** |
510 |
* ccs_lock - Hold non-SRCU lock. |
511 |
* |
512 |
* Returns index number which has to be passed to ccs_unlock(). |
513 |
*/ |
514 |
int ccs_lock(void) |
515 |
{ |
516 |
int idx; |
517 |
spin_lock(&ccs_counter_lock); |
518 |
idx = ccs_counter.counter_idx; |
519 |
ccs_counter.counter[idx]++; |
520 |
spin_unlock(&ccs_counter_lock); |
521 |
return idx; |
522 |
} |
523 |
|
524 |
/** |
525 |
* ccs_unlock - Release non-SRCU lock. |
526 |
* |
527 |
* @idx: Index number returned by ccs_lock(). |
528 |
*/ |
529 |
void ccs_unlock(const int idx) |
530 |
{ |
531 |
spin_lock(&ccs_counter_lock); |
532 |
ccs_counter.counter[idx]--; |
533 |
spin_unlock(&ccs_counter_lock); |
534 |
} |
535 |
|
536 |
/** |
537 |
* ccs_synchronize_counter - Wait for SRCU grace period. |
538 |
*/ |
539 |
static void ccs_synchronize_counter(void) |
540 |
{ |
541 |
int idx; |
542 |
int v; |
543 |
spin_lock(&ccs_counter_lock); |
544 |
idx = ccs_counter.counter_idx; |
545 |
ccs_counter.counter_idx ^= 1; |
546 |
v = ccs_counter.counter[idx]; |
547 |
spin_unlock(&ccs_counter_lock); |
548 |
while (v) { |
549 |
ssleep(1); |
550 |
spin_lock(&ccs_counter_lock); |
551 |
v = ccs_counter.counter[idx]; |
552 |
spin_unlock(&ccs_counter_lock); |
553 |
} |
554 |
} |
555 |
|
556 |
static bool ccs_collect_member(struct list_head *member_list, int id) |
557 |
{ |
558 |
struct ccs_acl_head *member; |
559 |
list_for_each_entry(member, member_list, list) { |
560 |
if (!member->is_deleted) |
561 |
continue; |
562 |
if (!ccs_add_to_gc(id, &member->list)) |
563 |
return false; |
564 |
} |
565 |
return true; |
566 |
} |
567 |
|
568 |
static bool ccs_collect_acl(struct ccs_domain_info *domain) |
569 |
{ |
570 |
struct ccs_acl_info *acl; |
571 |
list_for_each_entry(acl, &domain->acl_info_list, list) { |
572 |
if (!acl->is_deleted) |
573 |
continue; |
574 |
if (!ccs_add_to_gc(CCS_ID_ACL, &acl->list)) |
575 |
return false; |
576 |
} |
577 |
return true; |
578 |
} |
579 |
|
580 |
/** |
581 |
* ccs_collect_entry - Scan lists for deleted elements. |
582 |
*/ |
583 |
static void ccs_collect_entry(void) |
584 |
{ |
585 |
int i; |
586 |
int idx; |
587 |
if (mutex_lock_interruptible(&ccs_policy_lock)) |
588 |
return; |
589 |
idx = ccs_read_lock(); |
590 |
for (i = 0; i < CCS_MAX_POLICY; i++) |
591 |
if (!ccs_collect_member(&ccs_policy_list[i], i)) |
592 |
goto unlock; |
593 |
for (i = 0; i < CCS_MAX_ACL_GROUPS; i++) |
594 |
if (!ccs_collect_acl(&ccs_acl_group[i])) |
595 |
goto unlock; |
596 |
{ |
597 |
struct ccs_domain_info *domain; |
598 |
list_for_each_entry(domain, &ccs_domain_list, list) { |
599 |
if (!ccs_collect_acl(domain)) |
600 |
goto unlock; |
601 |
if (!domain->is_deleted || |
602 |
ccs_used_by_task(domain)) |
603 |
continue; |
604 |
if (!ccs_add_to_gc(CCS_ID_DOMAIN, &domain->list)) |
605 |
goto unlock; |
606 |
} |
607 |
} |
608 |
for (i = 0; i < CCS_MAX_GROUP; i++) { |
609 |
struct list_head *list = &ccs_group_list[i]; |
610 |
int id; |
611 |
struct ccs_group *group; |
612 |
switch (i) { |
613 |
case 0: |
614 |
id = CCS_ID_PATH_GROUP; |
615 |
break; |
616 |
case 1: |
617 |
id = CCS_ID_NUMBER_GROUP; |
618 |
break; |
619 |
default: |
620 |
id = CCS_ID_ADDRESS_GROUP; |
621 |
break; |
622 |
} |
623 |
list_for_each_entry(group, list, head.list) { |
624 |
if (!ccs_collect_member(&group->member_list, id)) |
625 |
goto unlock; |
626 |
if (!list_empty(&group->member_list) || |
627 |
atomic_read(&group->head.users)) |
628 |
continue; |
629 |
if (!ccs_add_to_gc(CCS_ID_GROUP, &group->head.list)) |
630 |
goto unlock; |
631 |
} |
632 |
} |
633 |
for (i = 0; i < CCS_MAX_LIST + CCS_MAX_HASH; i++) { |
634 |
struct list_head *list = i < CCS_MAX_LIST ? |
635 |
&ccs_shared_list[i] : &ccs_name_list[i - CCS_MAX_LIST]; |
636 |
int id; |
637 |
struct ccs_shared_acl_head *ptr; |
638 |
switch (i) { |
639 |
case 0: |
640 |
id = CCS_ID_CONDITION; |
641 |
break; |
642 |
case 1: |
643 |
id = CCS_ID_IPV6_ADDRESS; |
644 |
break; |
645 |
default: |
646 |
id = CCS_ID_NAME; |
647 |
break; |
648 |
} |
649 |
list_for_each_entry(ptr, list, list) { |
650 |
if (atomic_read(&ptr->users)) |
651 |
continue; |
652 |
if (!ccs_add_to_gc(id, &ptr->list)) |
653 |
goto unlock; |
654 |
} |
655 |
} |
656 |
unlock: |
657 |
ccs_read_unlock(idx); |
658 |
mutex_unlock(&ccs_policy_lock); |
659 |
} |
660 |
|
661 |
/** |
662 |
* ccs_kfree_entry - Delete entries in ccs_gc_list . |
663 |
* |
664 |
* Returns true if some entries were kfree()d, false otherwise. |
665 |
*/ |
666 |
static bool ccs_kfree_entry(void) |
667 |
{ |
668 |
struct ccs_gc *p; |
669 |
struct ccs_gc *tmp; |
670 |
bool result = false; |
671 |
list_for_each_entry_safe(p, tmp, &ccs_gc_list, list) { |
672 |
size_t size = 0; |
673 |
struct list_head * const element = p->element; |
674 |
switch (p->type) { |
675 |
case CCS_ID_TRANSITION_CONTROL: |
676 |
size = ccs_del_transition_control(element); |
677 |
break; |
678 |
case CCS_ID_PATTERN: |
679 |
size = ccs_del_file_pattern(element); |
680 |
break; |
681 |
case CCS_ID_MANAGER: |
682 |
size = ccs_del_manager(element); |
683 |
break; |
684 |
case CCS_ID_AGGREGATOR: |
685 |
size = ccs_del_aggregator(element); |
686 |
break; |
687 |
case CCS_ID_GROUP: |
688 |
size = ccs_del_group(element); |
689 |
break; |
690 |
case CCS_ID_PATH_GROUP: |
691 |
size = ccs_del_path_group(element); |
692 |
break; |
693 |
case CCS_ID_ADDRESS_GROUP: |
694 |
size = ccs_del_address_group(element); |
695 |
break; |
696 |
case CCS_ID_NUMBER_GROUP: |
697 |
size = ccs_del_number_group(element); |
698 |
break; |
699 |
case CCS_ID_RESERVEDPORT: |
700 |
size = ccs_del_reservedport(element); |
701 |
break; |
702 |
case CCS_ID_IPV6_ADDRESS: |
703 |
size = ccs_del_ipv6_address(element); |
704 |
break; |
705 |
case CCS_ID_CONDITION: |
706 |
size = ccs_del_condition(element); |
707 |
break; |
708 |
case CCS_ID_NAME: |
709 |
size = ccs_del_name(element); |
710 |
break; |
711 |
case CCS_ID_ACL: |
712 |
size = ccs_del_acl(element); |
713 |
break; |
714 |
case CCS_ID_DOMAIN: |
715 |
size = ccs_del_domain(element); |
716 |
if (!size) |
717 |
continue; |
718 |
break; |
719 |
} |
720 |
ccs_memory_free(element, size); |
721 |
list_del(&p->list); |
722 |
kfree(p); |
723 |
ccs_gc_list_len--; |
724 |
result = true; |
725 |
} |
726 |
return result; |
727 |
} |
728 |
|
729 |
/** |
730 |
* ccs_gc_thread - Garbage collector thread function. |
731 |
* |
732 |
* In case OOM-killer choose this thread for termination, we create this thread |
733 |
* as a short live thread whenever /proc/ccs/ interface was close()d. |
734 |
* |
735 |
* Returns 0. |
736 |
*/ |
737 |
static int ccs_gc_thread(void *unused) |
738 |
{ |
739 |
static DEFINE_MUTEX(ccs_gc_mutex); |
740 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) |
741 |
daemonize("GC for CCS"); |
742 |
#else |
743 |
daemonize(); |
744 |
reparent_to_init(); |
745 |
#if defined(TASK_DEAD) |
746 |
{ |
747 |
struct task_struct *task = current; |
748 |
spin_lock_irq(&task->sighand->siglock); |
749 |
siginitsetinv(&task->blocked, 0); |
750 |
recalc_sigpending(); |
751 |
spin_unlock_irq(&task->sighand->siglock); |
752 |
} |
753 |
#else |
754 |
{ |
755 |
struct task_struct *task = current; |
756 |
spin_lock_irq(&task->sigmask_lock); |
757 |
siginitsetinv(&task->blocked, 0); |
758 |
recalc_sigpending(task); |
759 |
spin_unlock_irq(&task->sigmask_lock); |
760 |
} |
761 |
#endif |
762 |
snprintf(current->comm, sizeof(current->comm) - 1, "GC for CCS"); |
763 |
#endif |
764 |
if (mutex_trylock(&ccs_gc_mutex)) { |
765 |
do { |
766 |
ccs_collect_entry(); |
767 |
if (list_empty(&ccs_gc_list)) |
768 |
break; |
769 |
ccs_synchronize_counter(); |
770 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) |
771 |
synchronize_srcu(&ccs_ss); |
772 |
#endif |
773 |
} while (ccs_kfree_entry()); |
774 |
mutex_unlock(&ccs_gc_mutex); |
775 |
} |
776 |
return 0; |
777 |
} |
778 |
|
779 |
/** |
780 |
* ccs_run_gc - Start garbage collector thread. |
781 |
*/ |
782 |
void ccs_run_gc(void) |
783 |
{ |
784 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) |
785 |
struct task_struct *task = kthread_create(ccs_gc_thread, NULL, |
786 |
"GC for CCS"); |
787 |
if (!IS_ERR(task)) |
788 |
wake_up_process(task); |
789 |
#else |
790 |
kernel_thread(ccs_gc_thread, NULL, 0); |
791 |
#endif |
792 |
} |