Develop and Download Open Source Software

Browse Subversion Repository

Contents of /branches/for-mainline/security/caitsith/gc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 267 - (show annotations) (download) (as text)
Mon Aug 27 10:15:55 2018 UTC (5 years, 7 months ago) by kumaneko
File MIME type: text/x-csrc
File size: 9217 byte(s)


1 /*
2 * security/caitsith/gc.c
3 *
4 * Copyright (C) 2005-2012 NTT DATA CORPORATION
5 */
6
7 #include "caitsith.h"
8
9 /* The list for "struct cs_io_buffer". */
10 static LIST_HEAD(cs_io_buffer_list);
11 /* Lock for protecting cs_io_buffer_list. */
12 static DEFINE_SPINLOCK(cs_io_buffer_list_lock);
13
14 static bool cs_name_used_by_io_buffer(const char *string, const size_t size);
15 static bool cs_struct_used_by_io_buffer(const struct list_head *element);
16 static int cs_gc_thread(void *unused);
17 static void cs_collect_acl(struct list_head *list);
18 static void cs_collect_entry(void);
19 static void cs_try_to_gc(const enum cs_policy_id type,
20 struct list_head *element);
21
22 /*
23 * Lock for syscall users.
24 *
25 * This lock is held for only protecting single SRCU section.
26 */
27 struct srcu_struct cs_ss;
28
29 /**
30 * cs_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/caitsith/ users or not.
31 *
32 * @element: Pointer to "struct list_head".
33 *
34 * Returns true if @element is used by /sys/kernel/security/caitsith/ users,
35 * false otherwise.
36 */
37 static bool cs_struct_used_by_io_buffer(const struct list_head *element)
38 {
39 struct cs_io_buffer *head;
40 bool in_use = false;
41
42 spin_lock(&cs_io_buffer_list_lock);
43 list_for_each_entry(head, &cs_io_buffer_list, list) {
44 head->users++;
45 spin_unlock(&cs_io_buffer_list_lock);
46 mutex_lock(&head->io_sem);
47 if (head->r.acl == element || head->r.subacl == element ||
48 &head->w.acl->list == element)
49 in_use = true;
50 mutex_unlock(&head->io_sem);
51 spin_lock(&cs_io_buffer_list_lock);
52 head->users--;
53 if (in_use)
54 break;
55 }
56 spin_unlock(&cs_io_buffer_list_lock);
57 return in_use;
58 }
59
60 /**
61 * cs_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/caitsith/ users or not.
62 *
63 * @string: String to check.
64 * @size: Memory allocated for @string .
65 *
66 * Returns true if @string is used by /sys/kernel/security/caitsith/ users,
67 * false otherwise.
68 */
69 static bool cs_name_used_by_io_buffer(const char *string, const size_t size)
70 {
71 struct cs_io_buffer *head;
72 bool in_use = false;
73
74 spin_lock(&cs_io_buffer_list_lock);
75 list_for_each_entry(head, &cs_io_buffer_list, list) {
76 int i;
77
78 head->users++;
79 spin_unlock(&cs_io_buffer_list_lock);
80 mutex_lock(&head->io_sem);
81 for (i = 0; i < CS_MAX_IO_READ_QUEUE; i++) {
82 const char *w = head->r.w[i];
83
84 if (w < string || w > string + size)
85 continue;
86 in_use = true;
87 break;
88 }
89 mutex_unlock(&head->io_sem);
90 spin_lock(&cs_io_buffer_list_lock);
91 head->users--;
92 if (in_use)
93 break;
94 }
95 spin_unlock(&cs_io_buffer_list_lock);
96 return in_use;
97 }
98
99 /**
100 * cs_acl_info_has_sub_acl - Clear "struct cs_acl_info"->acl_info.
101 *
102 * @list: Pointer to "struct list_head".
103 *
104 * Returns true if @list is not empty, false otherwise.
105 */
106 static bool cs_acl_info_has_sub_acl(struct list_head *list)
107 {
108 struct cs_acl_info *acl;
109 struct cs_acl_info *tmp;
110
111 if (list_empty(list))
112 return false;
113 mutex_lock(&cs_policy_lock);
114 list_for_each_entry_safe(acl, tmp, list, list) {
115 cs_try_to_gc(CS_ID_ACL, &acl->list);
116 }
117 mutex_unlock(&cs_policy_lock);
118 return !list_empty(list);
119 }
120
121 /**
122 * cs_del_acl - Delete members in "struct cs_acl_info".
123 *
124 * @element: Pointer to "struct list_head".
125 *
126 * Returns nothing.
127 */
128 static inline void cs_del_acl(struct list_head *element)
129 {
130 struct cs_acl_info *acl = container_of(element, typeof(*acl), list);
131
132 cs_put_condition(acl->cond);
133 }
134
135 /**
136 * cs_del_condition - Delete members in "struct cs_condition".
137 *
138 * @element: Pointer to "struct list_head".
139 *
140 * Returns nothing.
141 */
142 void cs_del_condition(struct list_head *element)
143 {
144 struct cs_condition *cond = container_of(element, typeof(*cond),
145 head.list);
146 const union cs_condition_element *condp = (typeof(condp)) (cond + 1);
147
148 while ((void *) condp < (void *) ((u8 *) cond) + cond->size) {
149 const enum cs_conditions_index right = condp->right;
150
151 condp++;
152 if (right == CS_IMM_NAME_ENTRY) {
153 if (condp->path != &cs_null_name)
154 cs_put_name(condp->path);
155 condp++;
156 }
157 }
158 }
159
160 /**
161 * cs_try_to_gc - Try to kfree() an entry.
162 *
163 * @type: One of values in "enum cs_policy_id".
164 * @element: Pointer to "struct list_head".
165 *
166 * Returns nothing.
167 *
168 * Caller holds cs_policy_lock mutex.
169 */
170 static void cs_try_to_gc(const enum cs_policy_id type,
171 struct list_head *element)
172 {
173 /*
174 * __list_del_entry() guarantees that the list element became no longer
175 * reachable from the list which the element was originally on (e.g.
176 * cs_domain_list). Also, synchronize_srcu() guarantees that the list
177 * element became no longer referenced by syscall users.
178 */
179 __list_del_entry(element);
180 mutex_unlock(&cs_policy_lock);
181 synchronize_srcu(&cs_ss);
182 /*
183 * However, there are two users which may still be using the list
184 * element. We need to defer until both users forget this element.
185 *
186 * Don't kfree() until "struct cs_io_buffer"->r.{acl,subacl} and
187 * "struct cs_io_buffer"->w.acl forget this element.
188 */
189 if (cs_struct_used_by_io_buffer(element))
190 goto reinject;
191 switch (type) {
192 case CS_ID_CONDITION:
193 cs_del_condition(element);
194 break;
195 case CS_ID_NAME:
196 /*
197 * Don't kfree() until all "struct cs_io_buffer"->r.w[] forget
198 * this element.
199 */
200 if (cs_name_used_by_io_buffer
201 (container_of(element, typeof(struct cs_name),
202 head.list)->entry.name,
203 container_of(element, typeof(struct cs_name),
204 head.list)->size))
205 goto reinject;
206 break;
207 case CS_ID_ACL:
208 /*
209 * Don't kfree() until "struct cs_acl_info"->acl_info_list
210 * becomes empty.
211 */
212 if (cs_acl_info_has_sub_acl
213 (&container_of(element, typeof(struct cs_acl_info),
214 list)->acl_info_list))
215 goto reinject;
216 cs_del_acl(element);
217 break;
218 default:
219 break;
220 }
221 mutex_lock(&cs_policy_lock);
222 cs_memory_used[CS_MEMORY_POLICY] -= ksize(element);
223 kfree(element);
224 return;
225 reinject:
226 /*
227 * We can safely reinject this element here bacause
228 * (1) Appending list elements and removing list elements are protected
229 * by cs_policy_lock mutex.
230 * (2) Only this function removes list elements and this function is
231 * exclusively executed by cs_gc_mutex mutex.
232 * are true.
233 */
234 mutex_lock(&cs_policy_lock);
235 list_add_rcu(element, element->prev);
236 }
237
238 /**
239 * cs_collect_acl - Delete elements in "struct cs_acl_info".
240 *
241 * @list: Pointer to "struct list_head".
242 *
243 * Returns nothing.
244 *
245 * Caller holds cs_policy_lock mutex.
246 */
247 static void cs_collect_acl(struct list_head *list)
248 {
249 struct cs_acl_info *acl;
250 struct cs_acl_info *tmp;
251
252 list_for_each_entry_safe(acl, tmp, list, list) {
253 if (!acl->is_deleted)
254 continue;
255 cs_try_to_gc(CS_ID_ACL, &acl->list);
256 }
257 }
258
259 /**
260 * cs_collect_entry - Try to kfree() deleted elements.
261 *
262 * Returns nothing.
263 */
264 static void cs_collect_entry(void)
265 {
266 int i;
267
268 mutex_lock(&cs_policy_lock);
269 for (i = 0; i < CS_MAX_MAC_INDEX; i++) {
270 struct cs_acl_info *ptr;
271 struct cs_acl_info *tmp;
272 struct list_head * const list = &cs_acl_list[i];
273
274 list_for_each_entry_safe(ptr, tmp, list, list) {
275 cs_collect_acl(&ptr->acl_info_list);
276 if (!ptr->is_deleted)
277 continue;
278 cs_try_to_gc(CS_ID_ACL, &ptr->list);
279 }
280 }
281 {
282 struct cs_shared_acl_head *ptr;
283 struct cs_shared_acl_head *tmp;
284
285 list_for_each_entry_safe(ptr, tmp, &cs_condition_list, list) {
286 if (atomic_read(&ptr->users) > 0)
287 continue;
288 atomic_set(&ptr->users, CS_GC_IN_PROGRESS);
289 cs_try_to_gc(CS_ID_CONDITION, &ptr->list);
290 }
291 }
292 for (i = 0; i < CS_MAX_HASH; i++) {
293 struct list_head *list = &cs_name_list[i];
294 struct cs_shared_acl_head *ptr;
295 struct cs_shared_acl_head *tmp;
296
297 list_for_each_entry_safe(ptr, tmp, list, list) {
298 if (atomic_read(&ptr->users) > 0)
299 continue;
300 atomic_set(&ptr->users, CS_GC_IN_PROGRESS);
301 cs_try_to_gc(CS_ID_NAME, &ptr->list);
302 }
303 }
304 mutex_unlock(&cs_policy_lock);
305 }
306
307 /**
308 * cs_gc_thread - Garbage collector thread function.
309 *
310 * @unused: Unused.
311 *
312 * Returns 0.
313 */
314 static int cs_gc_thread(void *unused)
315 {
316 /* Garbage collector thread is exclusive. */
317 static DEFINE_MUTEX(cs_gc_mutex);
318
319 if (!mutex_trylock(&cs_gc_mutex))
320 goto out;
321 cs_collect_entry();
322 {
323 struct cs_io_buffer *head;
324 struct cs_io_buffer *tmp;
325
326 spin_lock(&cs_io_buffer_list_lock);
327 list_for_each_entry_safe(head, tmp, &cs_io_buffer_list,
328 list) {
329 if (head->users)
330 continue;
331 list_del(&head->list);
332 kfree(head->read_buf);
333 kfree(head->write_buf);
334 kfree(head);
335 }
336 spin_unlock(&cs_io_buffer_list_lock);
337 }
338 mutex_unlock(&cs_gc_mutex);
339 out:
340 /* This acts as do_exit(0). */
341 return 0;
342 }
343
344 /**
345 * cs_notify_gc - Register/unregister /sys/kernel/security/caitsith/ users.
346 *
347 * @head: Pointer to "struct cs_io_buffer".
348 * @is_register: True if register, false if unregister.
349 *
350 * Returns nothing.
351 */
352 void cs_notify_gc(struct cs_io_buffer *head, const bool is_register)
353 {
354 bool is_write = false;
355
356 spin_lock(&cs_io_buffer_list_lock);
357 if (is_register) {
358 head->users = 1;
359 list_add(&head->list, &cs_io_buffer_list);
360 } else {
361 is_write = head->write_buf != NULL;
362 if (!--head->users) {
363 list_del(&head->list);
364 kfree(head->read_buf);
365 kfree(head->write_buf);
366 kfree(head);
367 }
368 }
369 spin_unlock(&cs_io_buffer_list_lock);
370 if (is_write)
371 kthread_run(cs_gc_thread, NULL, "CaitSith's GC");
372 }

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26