1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * security/tomoyo/gc.c |
4 | * |
5 | * Copyright (C) 2005-2011 NTT DATA CORPORATION |
6 | */ |
7 | |
8 | #include "common.h" |
9 | #include <linux/kthread.h> |
10 | #include <linux/slab.h> |
11 | |
12 | /** |
13 | * tomoyo_memory_free - Free memory for elements. |
14 | * |
15 | * @ptr: Pointer to allocated memory. |
16 | * |
17 | * Returns nothing. |
18 | * |
19 | * Caller holds tomoyo_policy_lock mutex. |
20 | */ |
21 | static inline void tomoyo_memory_free(void *ptr) |
22 | { |
23 | tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= ksize(objp: ptr); |
24 | kfree(objp: ptr); |
25 | } |
26 | |
27 | /* The list for "struct tomoyo_io_buffer". */ |
28 | static LIST_HEAD(tomoyo_io_buffer_list); |
29 | /* Lock for protecting tomoyo_io_buffer_list. */ |
30 | static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock); |
31 | |
32 | /** |
33 | * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not. |
34 | * |
35 | * @element: Pointer to "struct list_head". |
36 | * |
37 | * Returns true if @element is used by /sys/kernel/security/tomoyo/ users, |
38 | * false otherwise. |
39 | */ |
40 | static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element) |
41 | { |
42 | struct tomoyo_io_buffer *head; |
43 | bool in_use = false; |
44 | |
45 | spin_lock(lock: &tomoyo_io_buffer_list_lock); |
46 | list_for_each_entry(head, &tomoyo_io_buffer_list, list) { |
47 | head->users++; |
48 | spin_unlock(lock: &tomoyo_io_buffer_list_lock); |
49 | mutex_lock(&head->io_sem); |
50 | if (head->r.domain == element || head->r.group == element || |
51 | head->r.acl == element || &head->w.domain->list == element) |
52 | in_use = true; |
53 | mutex_unlock(lock: &head->io_sem); |
54 | spin_lock(lock: &tomoyo_io_buffer_list_lock); |
55 | head->users--; |
56 | if (in_use) |
57 | break; |
58 | } |
59 | spin_unlock(lock: &tomoyo_io_buffer_list_lock); |
60 | return in_use; |
61 | } |
62 | |
63 | /** |
64 | * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not. |
65 | * |
66 | * @string: String to check. |
67 | * |
68 | * Returns true if @string is used by /sys/kernel/security/tomoyo/ users, |
69 | * false otherwise. |
70 | */ |
71 | static bool tomoyo_name_used_by_io_buffer(const char *string) |
72 | { |
73 | struct tomoyo_io_buffer *head; |
74 | const size_t size = strlen(string) + 1; |
75 | bool in_use = false; |
76 | |
77 | spin_lock(lock: &tomoyo_io_buffer_list_lock); |
78 | list_for_each_entry(head, &tomoyo_io_buffer_list, list) { |
79 | int i; |
80 | |
81 | head->users++; |
82 | spin_unlock(lock: &tomoyo_io_buffer_list_lock); |
83 | mutex_lock(&head->io_sem); |
84 | for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) { |
85 | const char *w = head->r.w[i]; |
86 | |
87 | if (w < string || w > string + size) |
88 | continue; |
89 | in_use = true; |
90 | break; |
91 | } |
92 | mutex_unlock(lock: &head->io_sem); |
93 | spin_lock(lock: &tomoyo_io_buffer_list_lock); |
94 | head->users--; |
95 | if (in_use) |
96 | break; |
97 | } |
98 | spin_unlock(lock: &tomoyo_io_buffer_list_lock); |
99 | return in_use; |
100 | } |
101 | |
102 | /** |
103 | * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control". |
104 | * |
105 | * @element: Pointer to "struct list_head". |
106 | * |
107 | * Returns nothing. |
108 | */ |
109 | static inline void tomoyo_del_transition_control(struct list_head *element) |
110 | { |
111 | struct tomoyo_transition_control *ptr = |
112 | container_of(element, typeof(*ptr), head.list); |
113 | |
114 | tomoyo_put_name(name: ptr->domainname); |
115 | tomoyo_put_name(name: ptr->program); |
116 | } |
117 | |
118 | /** |
119 | * tomoyo_del_aggregator - Delete members in "struct tomoyo_aggregator". |
120 | * |
121 | * @element: Pointer to "struct list_head". |
122 | * |
123 | * Returns nothing. |
124 | */ |
125 | static inline void tomoyo_del_aggregator(struct list_head *element) |
126 | { |
127 | struct tomoyo_aggregator *ptr = |
128 | container_of(element, typeof(*ptr), head.list); |
129 | |
130 | tomoyo_put_name(name: ptr->original_name); |
131 | tomoyo_put_name(name: ptr->aggregated_name); |
132 | } |
133 | |
134 | /** |
135 | * tomoyo_del_manager - Delete members in "struct tomoyo_manager". |
136 | * |
137 | * @element: Pointer to "struct list_head". |
138 | * |
139 | * Returns nothing. |
140 | */ |
141 | static inline void tomoyo_del_manager(struct list_head *element) |
142 | { |
143 | struct tomoyo_manager *ptr = |
144 | container_of(element, typeof(*ptr), head.list); |
145 | |
146 | tomoyo_put_name(name: ptr->manager); |
147 | } |
148 | |
149 | /** |
150 | * tomoyo_del_acl - Delete members in "struct tomoyo_acl_info". |
151 | * |
152 | * @element: Pointer to "struct list_head". |
153 | * |
154 | * Returns nothing. |
155 | */ |
156 | static void tomoyo_del_acl(struct list_head *element) |
157 | { |
158 | struct tomoyo_acl_info *acl = |
159 | container_of(element, typeof(*acl), list); |
160 | |
161 | tomoyo_put_condition(cond: acl->cond); |
162 | switch (acl->type) { |
163 | case TOMOYO_TYPE_PATH_ACL: |
164 | { |
165 | struct tomoyo_path_acl *entry |
166 | = container_of(acl, typeof(*entry), head); |
167 | tomoyo_put_name_union(ptr: &entry->name); |
168 | } |
169 | break; |
170 | case TOMOYO_TYPE_PATH2_ACL: |
171 | { |
172 | struct tomoyo_path2_acl *entry |
173 | = container_of(acl, typeof(*entry), head); |
174 | tomoyo_put_name_union(ptr: &entry->name1); |
175 | tomoyo_put_name_union(ptr: &entry->name2); |
176 | } |
177 | break; |
178 | case TOMOYO_TYPE_PATH_NUMBER_ACL: |
179 | { |
180 | struct tomoyo_path_number_acl *entry |
181 | = container_of(acl, typeof(*entry), head); |
182 | tomoyo_put_name_union(ptr: &entry->name); |
183 | tomoyo_put_number_union(ptr: &entry->number); |
184 | } |
185 | break; |
186 | case TOMOYO_TYPE_MKDEV_ACL: |
187 | { |
188 | struct tomoyo_mkdev_acl *entry |
189 | = container_of(acl, typeof(*entry), head); |
190 | tomoyo_put_name_union(ptr: &entry->name); |
191 | tomoyo_put_number_union(ptr: &entry->mode); |
192 | tomoyo_put_number_union(ptr: &entry->major); |
193 | tomoyo_put_number_union(ptr: &entry->minor); |
194 | } |
195 | break; |
196 | case TOMOYO_TYPE_MOUNT_ACL: |
197 | { |
198 | struct tomoyo_mount_acl *entry |
199 | = container_of(acl, typeof(*entry), head); |
200 | tomoyo_put_name_union(ptr: &entry->dev_name); |
201 | tomoyo_put_name_union(ptr: &entry->dir_name); |
202 | tomoyo_put_name_union(ptr: &entry->fs_type); |
203 | tomoyo_put_number_union(ptr: &entry->flags); |
204 | } |
205 | break; |
206 | case TOMOYO_TYPE_ENV_ACL: |
207 | { |
208 | struct tomoyo_env_acl *entry = |
209 | container_of(acl, typeof(*entry), head); |
210 | |
211 | tomoyo_put_name(name: entry->env); |
212 | } |
213 | break; |
214 | case TOMOYO_TYPE_INET_ACL: |
215 | { |
216 | struct tomoyo_inet_acl *entry = |
217 | container_of(acl, typeof(*entry), head); |
218 | |
219 | tomoyo_put_group(group: entry->address.group); |
220 | tomoyo_put_number_union(ptr: &entry->port); |
221 | } |
222 | break; |
223 | case TOMOYO_TYPE_UNIX_ACL: |
224 | { |
225 | struct tomoyo_unix_acl *entry = |
226 | container_of(acl, typeof(*entry), head); |
227 | |
228 | tomoyo_put_name_union(ptr: &entry->name); |
229 | } |
230 | break; |
231 | case TOMOYO_TYPE_MANUAL_TASK_ACL: |
232 | { |
233 | struct tomoyo_task_acl *entry = |
234 | container_of(acl, typeof(*entry), head); |
235 | |
236 | tomoyo_put_name(name: entry->domainname); |
237 | } |
238 | break; |
239 | } |
240 | } |
241 | |
242 | /** |
243 | * tomoyo_del_domain - Delete members in "struct tomoyo_domain_info". |
244 | * |
245 | * @element: Pointer to "struct list_head". |
246 | * |
247 | * Returns nothing. |
248 | * |
249 | * Caller holds tomoyo_policy_lock mutex. |
250 | */ |
251 | static inline void tomoyo_del_domain(struct list_head *element) |
252 | { |
253 | struct tomoyo_domain_info *domain = |
254 | container_of(element, typeof(*domain), list); |
255 | struct tomoyo_acl_info *acl; |
256 | struct tomoyo_acl_info *tmp; |
257 | |
258 | /* |
259 | * Since this domain is referenced from neither |
260 | * "struct tomoyo_io_buffer" nor "struct cred"->security, we can delete |
261 | * elements without checking for is_deleted flag. |
262 | */ |
263 | list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { |
264 | tomoyo_del_acl(element: &acl->list); |
265 | tomoyo_memory_free(ptr: acl); |
266 | } |
267 | tomoyo_put_name(name: domain->domainname); |
268 | } |
269 | |
270 | /** |
271 | * tomoyo_del_condition - Delete members in "struct tomoyo_condition". |
272 | * |
273 | * @element: Pointer to "struct list_head". |
274 | * |
275 | * Returns nothing. |
276 | */ |
277 | void tomoyo_del_condition(struct list_head *element) |
278 | { |
279 | struct tomoyo_condition *cond = container_of(element, typeof(*cond), |
280 | head.list); |
281 | const u16 condc = cond->condc; |
282 | const u16 numbers_count = cond->numbers_count; |
283 | const u16 names_count = cond->names_count; |
284 | const u16 argc = cond->argc; |
285 | const u16 envc = cond->envc; |
286 | unsigned int i; |
287 | const struct tomoyo_condition_element *condp |
288 | = (const struct tomoyo_condition_element *) (cond + 1); |
289 | struct tomoyo_number_union *numbers_p |
290 | = (struct tomoyo_number_union *) (condp + condc); |
291 | struct tomoyo_name_union *names_p |
292 | = (struct tomoyo_name_union *) (numbers_p + numbers_count); |
293 | const struct tomoyo_argv *argv |
294 | = (const struct tomoyo_argv *) (names_p + names_count); |
295 | const struct tomoyo_envp *envp |
296 | = (const struct tomoyo_envp *) (argv + argc); |
297 | |
298 | for (i = 0; i < numbers_count; i++) |
299 | tomoyo_put_number_union(ptr: numbers_p++); |
300 | for (i = 0; i < names_count; i++) |
301 | tomoyo_put_name_union(ptr: names_p++); |
302 | for (i = 0; i < argc; argv++, i++) |
303 | tomoyo_put_name(name: argv->value); |
304 | for (i = 0; i < envc; envp++, i++) { |
305 | tomoyo_put_name(name: envp->name); |
306 | tomoyo_put_name(name: envp->value); |
307 | } |
308 | } |
309 | |
310 | /** |
311 | * tomoyo_del_name - Delete members in "struct tomoyo_name". |
312 | * |
313 | * @element: Pointer to "struct list_head". |
314 | * |
315 | * Returns nothing. |
316 | */ |
317 | static inline void tomoyo_del_name(struct list_head *element) |
318 | { |
319 | /* Nothing to do. */ |
320 | } |
321 | |
322 | /** |
323 | * tomoyo_del_path_group - Delete members in "struct tomoyo_path_group". |
324 | * |
325 | * @element: Pointer to "struct list_head". |
326 | * |
327 | * Returns nothing. |
328 | */ |
329 | static inline void tomoyo_del_path_group(struct list_head *element) |
330 | { |
331 | struct tomoyo_path_group *member = |
332 | container_of(element, typeof(*member), head.list); |
333 | |
334 | tomoyo_put_name(name: member->member_name); |
335 | } |
336 | |
337 | /** |
338 | * tomoyo_del_group - Delete "struct tomoyo_group". |
339 | * |
340 | * @element: Pointer to "struct list_head". |
341 | * |
342 | * Returns nothing. |
343 | */ |
344 | static inline void tomoyo_del_group(struct list_head *element) |
345 | { |
346 | struct tomoyo_group *group = |
347 | container_of(element, typeof(*group), head.list); |
348 | |
349 | tomoyo_put_name(name: group->group_name); |
350 | } |
351 | |
352 | /** |
353 | * tomoyo_del_address_group - Delete members in "struct tomoyo_address_group". |
354 | * |
355 | * @element: Pointer to "struct list_head". |
356 | * |
357 | * Returns nothing. |
358 | */ |
359 | static inline void tomoyo_del_address_group(struct list_head *element) |
360 | { |
361 | /* Nothing to do. */ |
362 | } |
363 | |
364 | /** |
365 | * tomoyo_del_number_group - Delete members in "struct tomoyo_number_group". |
366 | * |
367 | * @element: Pointer to "struct list_head". |
368 | * |
369 | * Returns nothing. |
370 | */ |
371 | static inline void tomoyo_del_number_group(struct list_head *element) |
372 | { |
373 | /* Nothing to do. */ |
374 | } |
375 | |
376 | /** |
377 | * tomoyo_try_to_gc - Try to kfree() an entry. |
378 | * |
379 | * @type: One of values in "enum tomoyo_policy_id". |
380 | * @element: Pointer to "struct list_head". |
381 | * |
382 | * Returns nothing. |
383 | * |
384 | * Caller holds tomoyo_policy_lock mutex. |
385 | */ |
386 | static void tomoyo_try_to_gc(const enum tomoyo_policy_id type, |
387 | struct list_head *element) |
388 | { |
389 | /* |
390 | * __list_del_entry() guarantees that the list element became no longer |
391 | * reachable from the list which the element was originally on (e.g. |
392 | * tomoyo_domain_list). Also, synchronize_srcu() guarantees that the |
393 | * list element became no longer referenced by syscall users. |
394 | */ |
395 | __list_del_entry(entry: element); |
396 | mutex_unlock(lock: &tomoyo_policy_lock); |
397 | synchronize_srcu(ssp: &tomoyo_ss); |
398 | /* |
399 | * However, there are two users which may still be using the list |
400 | * element. We need to defer until both users forget this element. |
401 | * |
402 | * Don't kfree() until "struct tomoyo_io_buffer"->r.{domain,group,acl} |
403 | * and "struct tomoyo_io_buffer"->w.domain forget this element. |
404 | */ |
405 | if (tomoyo_struct_used_by_io_buffer(element)) |
406 | goto reinject; |
407 | switch (type) { |
408 | case TOMOYO_ID_TRANSITION_CONTROL: |
409 | tomoyo_del_transition_control(element); |
410 | break; |
411 | case TOMOYO_ID_MANAGER: |
412 | tomoyo_del_manager(element); |
413 | break; |
414 | case TOMOYO_ID_AGGREGATOR: |
415 | tomoyo_del_aggregator(element); |
416 | break; |
417 | case TOMOYO_ID_GROUP: |
418 | tomoyo_del_group(element); |
419 | break; |
420 | case TOMOYO_ID_PATH_GROUP: |
421 | tomoyo_del_path_group(element); |
422 | break; |
423 | case TOMOYO_ID_ADDRESS_GROUP: |
424 | tomoyo_del_address_group(element); |
425 | break; |
426 | case TOMOYO_ID_NUMBER_GROUP: |
427 | tomoyo_del_number_group(element); |
428 | break; |
429 | case TOMOYO_ID_CONDITION: |
430 | tomoyo_del_condition(element); |
431 | break; |
432 | case TOMOYO_ID_NAME: |
433 | /* |
434 | * Don't kfree() until all "struct tomoyo_io_buffer"->r.w[] |
435 | * forget this element. |
436 | */ |
437 | if (tomoyo_name_used_by_io_buffer |
438 | (container_of(element, typeof(struct tomoyo_name), |
439 | head.list)->entry.name)) |
440 | goto reinject; |
441 | tomoyo_del_name(element); |
442 | break; |
443 | case TOMOYO_ID_ACL: |
444 | tomoyo_del_acl(element); |
445 | break; |
446 | case TOMOYO_ID_DOMAIN: |
447 | /* |
448 | * Don't kfree() until all "struct cred"->security forget this |
449 | * element. |
450 | */ |
451 | if (atomic_read(v: &container_of |
452 | (element, typeof(struct tomoyo_domain_info), |
453 | list)->users)) |
454 | goto reinject; |
455 | break; |
456 | case TOMOYO_MAX_POLICY: |
457 | break; |
458 | } |
459 | mutex_lock(&tomoyo_policy_lock); |
460 | if (type == TOMOYO_ID_DOMAIN) |
461 | tomoyo_del_domain(element); |
462 | tomoyo_memory_free(ptr: element); |
463 | return; |
464 | reinject: |
465 | /* |
466 | * We can safely reinject this element here because |
467 | * (1) Appending list elements and removing list elements are protected |
468 | * by tomoyo_policy_lock mutex. |
469 | * (2) Only this function removes list elements and this function is |
470 | * exclusively executed by tomoyo_gc_mutex mutex. |
471 | * are true. |
472 | */ |
473 | mutex_lock(&tomoyo_policy_lock); |
474 | list_add_rcu(new: element, head: element->prev); |
475 | } |
476 | |
477 | /** |
478 | * tomoyo_collect_member - Delete elements with "struct tomoyo_acl_head". |
479 | * |
480 | * @id: One of values in "enum tomoyo_policy_id". |
481 | * @member_list: Pointer to "struct list_head". |
482 | * |
483 | * Returns nothing. |
484 | */ |
485 | static void tomoyo_collect_member(const enum tomoyo_policy_id id, |
486 | struct list_head *member_list) |
487 | { |
488 | struct tomoyo_acl_head *member; |
489 | struct tomoyo_acl_head *tmp; |
490 | |
491 | list_for_each_entry_safe(member, tmp, member_list, list) { |
492 | if (!member->is_deleted) |
493 | continue; |
494 | member->is_deleted = TOMOYO_GC_IN_PROGRESS; |
495 | tomoyo_try_to_gc(type: id, element: &member->list); |
496 | } |
497 | } |
498 | |
499 | /** |
500 | * tomoyo_collect_acl - Delete elements in "struct tomoyo_domain_info". |
501 | * |
502 | * @list: Pointer to "struct list_head". |
503 | * |
504 | * Returns nothing. |
505 | */ |
506 | static void tomoyo_collect_acl(struct list_head *list) |
507 | { |
508 | struct tomoyo_acl_info *acl; |
509 | struct tomoyo_acl_info *tmp; |
510 | |
511 | list_for_each_entry_safe(acl, tmp, list, list) { |
512 | if (!acl->is_deleted) |
513 | continue; |
514 | acl->is_deleted = TOMOYO_GC_IN_PROGRESS; |
515 | tomoyo_try_to_gc(type: TOMOYO_ID_ACL, element: &acl->list); |
516 | } |
517 | } |
518 | |
519 | /** |
520 | * tomoyo_collect_entry - Try to kfree() deleted elements. |
521 | * |
522 | * Returns nothing. |
523 | */ |
524 | static void tomoyo_collect_entry(void) |
525 | { |
526 | int i; |
527 | enum tomoyo_policy_id id; |
528 | struct tomoyo_policy_namespace *ns; |
529 | |
530 | mutex_lock(&tomoyo_policy_lock); |
531 | { |
532 | struct tomoyo_domain_info *domain; |
533 | struct tomoyo_domain_info *tmp; |
534 | |
535 | list_for_each_entry_safe(domain, tmp, &tomoyo_domain_list, |
536 | list) { |
537 | tomoyo_collect_acl(list: &domain->acl_info_list); |
538 | if (!domain->is_deleted || atomic_read(v: &domain->users)) |
539 | continue; |
540 | tomoyo_try_to_gc(type: TOMOYO_ID_DOMAIN, element: &domain->list); |
541 | } |
542 | } |
543 | list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { |
544 | for (id = 0; id < TOMOYO_MAX_POLICY; id++) |
545 | tomoyo_collect_member(id, member_list: &ns->policy_list[id]); |
546 | for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++) |
547 | tomoyo_collect_acl(list: &ns->acl_group[i]); |
548 | } |
549 | { |
550 | struct tomoyo_shared_acl_head *ptr; |
551 | struct tomoyo_shared_acl_head *tmp; |
552 | |
553 | list_for_each_entry_safe(ptr, tmp, &tomoyo_condition_list, |
554 | list) { |
555 | if (atomic_read(v: &ptr->users) > 0) |
556 | continue; |
557 | atomic_set(v: &ptr->users, TOMOYO_GC_IN_PROGRESS); |
558 | tomoyo_try_to_gc(type: TOMOYO_ID_CONDITION, element: &ptr->list); |
559 | } |
560 | } |
561 | list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { |
562 | for (i = 0; i < TOMOYO_MAX_GROUP; i++) { |
563 | struct list_head *list = &ns->group_list[i]; |
564 | struct tomoyo_group *group; |
565 | struct tomoyo_group *tmp; |
566 | |
567 | switch (i) { |
568 | case 0: |
569 | id = TOMOYO_ID_PATH_GROUP; |
570 | break; |
571 | case 1: |
572 | id = TOMOYO_ID_NUMBER_GROUP; |
573 | break; |
574 | default: |
575 | id = TOMOYO_ID_ADDRESS_GROUP; |
576 | break; |
577 | } |
578 | list_for_each_entry_safe(group, tmp, list, head.list) { |
579 | tomoyo_collect_member(id, member_list: &group->member_list); |
580 | if (!list_empty(head: &group->member_list) || |
581 | atomic_read(v: &group->head.users) > 0) |
582 | continue; |
583 | atomic_set(v: &group->head.users, |
584 | TOMOYO_GC_IN_PROGRESS); |
585 | tomoyo_try_to_gc(type: TOMOYO_ID_GROUP, |
586 | element: &group->head.list); |
587 | } |
588 | } |
589 | } |
590 | for (i = 0; i < TOMOYO_MAX_HASH; i++) { |
591 | struct list_head *list = &tomoyo_name_list[i]; |
592 | struct tomoyo_shared_acl_head *ptr; |
593 | struct tomoyo_shared_acl_head *tmp; |
594 | |
595 | list_for_each_entry_safe(ptr, tmp, list, list) { |
596 | if (atomic_read(v: &ptr->users) > 0) |
597 | continue; |
598 | atomic_set(v: &ptr->users, TOMOYO_GC_IN_PROGRESS); |
599 | tomoyo_try_to_gc(type: TOMOYO_ID_NAME, element: &ptr->list); |
600 | } |
601 | } |
602 | mutex_unlock(lock: &tomoyo_policy_lock); |
603 | } |
604 | |
605 | /** |
606 | * tomoyo_gc_thread - Garbage collector thread function. |
607 | * |
608 | * @unused: Unused. |
609 | * |
610 | * Returns 0. |
611 | */ |
612 | static int tomoyo_gc_thread(void *unused) |
613 | { |
614 | /* Garbage collector thread is exclusive. */ |
615 | static DEFINE_MUTEX(tomoyo_gc_mutex); |
616 | |
617 | if (!mutex_trylock(lock: &tomoyo_gc_mutex)) |
618 | goto out; |
619 | tomoyo_collect_entry(); |
620 | { |
621 | struct tomoyo_io_buffer *head; |
622 | struct tomoyo_io_buffer *tmp; |
623 | |
624 | spin_lock(lock: &tomoyo_io_buffer_list_lock); |
625 | list_for_each_entry_safe(head, tmp, &tomoyo_io_buffer_list, |
626 | list) { |
627 | if (head->users) |
628 | continue; |
629 | list_del(entry: &head->list); |
630 | kfree(objp: head->read_buf); |
631 | kfree(objp: head->write_buf); |
632 | kfree(objp: head); |
633 | } |
634 | spin_unlock(lock: &tomoyo_io_buffer_list_lock); |
635 | } |
636 | mutex_unlock(lock: &tomoyo_gc_mutex); |
637 | out: |
638 | /* This acts as do_exit(0). */ |
639 | return 0; |
640 | } |
641 | |
642 | /** |
643 | * tomoyo_notify_gc - Register/unregister /sys/kernel/security/tomoyo/ users. |
644 | * |
645 | * @head: Pointer to "struct tomoyo_io_buffer". |
646 | * @is_register: True if register, false if unregister. |
647 | * |
648 | * Returns nothing. |
649 | */ |
650 | void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register) |
651 | { |
652 | bool is_write = false; |
653 | |
654 | spin_lock(lock: &tomoyo_io_buffer_list_lock); |
655 | if (is_register) { |
656 | head->users = 1; |
657 | list_add(new: &head->list, head: &tomoyo_io_buffer_list); |
658 | } else { |
659 | is_write = head->write_buf != NULL; |
660 | if (!--head->users) { |
661 | list_del(entry: &head->list); |
662 | kfree(objp: head->read_buf); |
663 | kfree(objp: head->write_buf); |
664 | kfree(objp: head); |
665 | } |
666 | } |
667 | spin_unlock(lock: &tomoyo_io_buffer_list_lock); |
668 | if (is_write) |
669 | kthread_run(tomoyo_gc_thread, NULL, "GC for TOMOYO" ); |
670 | } |
671 | |