diff options
| author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2011-06-26 10:20:55 -0400 |
|---|---|---|
| committer | James Morris <jmorris@namei.org> | 2011-06-28 19:31:21 -0400 |
| commit | 2e503bbb435ae418aebbe4aeede1c6f2a33d6f74 (patch) | |
| tree | c6b783c245716cf87b337b2a855e742133afb7ac /security | |
| parent | 5625f2e3266319fd29fe4f1c76ccd3f550c79ac4 (diff) | |
TOMOYO: Fix lockdep warning.
Currently TOMOYO holds SRCU lock upon open() and releases it upon close()
because list elements stored in the "struct tomoyo_io_buffer" instances are
accessed until close() is called. However, such SRCU usage causes lockdep to
complain about leaving the kernel with SRCU lock held.
This patch solves the warning by holding/releasing SRCU upon each
read()/write(). This patch is doing something similar to calling kfree()
without calling synchronize_srcu(), by selectively deferring kfree() by keeping
track of the "struct tomoyo_io_buffer" instances.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security')
| -rw-r--r-- | security/tomoyo/common.c | 41 | ||||
| -rw-r--r-- | security/tomoyo/common.h | 8 | ||||
| -rw-r--r-- | security/tomoyo/gc.c | 278 |
3 files changed, 276 insertions, 51 deletions
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 50481d2cf970..691c34025a4a 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c | |||
| @@ -1820,9 +1820,7 @@ static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head) | |||
| 1820 | * @type: Type of interface. | 1820 | * @type: Type of interface. |
| 1821 | * @file: Pointer to "struct file". | 1821 | * @file: Pointer to "struct file". |
| 1822 | * | 1822 | * |
| 1823 | * Associates policy handler and returns 0 on success, -ENOMEM otherwise. | 1823 | * Returns 0 on success, negative value otherwise. |
| 1824 | * | ||
| 1825 | * Caller acquires tomoyo_read_lock(). | ||
| 1826 | */ | 1824 | */ |
| 1827 | int tomoyo_open_control(const u8 type, struct file *file) | 1825 | int tomoyo_open_control(const u8 type, struct file *file) |
| 1828 | { | 1826 | { |
| @@ -1921,9 +1919,6 @@ int tomoyo_open_control(const u8 type, struct file *file) | |||
| 1921 | return -ENOMEM; | 1919 | return -ENOMEM; |
| 1922 | } | 1920 | } |
| 1923 | } | 1921 | } |
| 1924 | if (type != TOMOYO_QUERY && type != TOMOYO_AUDIT) | ||
| 1925 | head->reader_idx = tomoyo_read_lock(); | ||
| 1926 | file->private_data = head; | ||
| 1927 | /* | 1922 | /* |
| 1928 | * If the file is /sys/kernel/security/tomoyo/query , increment the | 1923 | * If the file is /sys/kernel/security/tomoyo/query , increment the |
| 1929 | * observer counter. | 1924 | * observer counter. |
| @@ -1932,6 +1927,8 @@ int tomoyo_open_control(const u8 type, struct file *file) | |||
| 1932 | */ | 1927 | */ |
| 1933 | if (type == TOMOYO_QUERY) | 1928 | if (type == TOMOYO_QUERY) |
| 1934 | atomic_inc(&tomoyo_query_observers); | 1929 | atomic_inc(&tomoyo_query_observers); |
| 1930 | file->private_data = head; | ||
| 1931 | tomoyo_notify_gc(head, true); | ||
| 1935 | return 0; | 1932 | return 0; |
| 1936 | } | 1933 | } |
| 1937 | 1934 | ||
| @@ -2000,13 +1997,12 @@ static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head) | |||
| 2000 | * @buffer_len: Size of @buffer. | 1997 | * @buffer_len: Size of @buffer. |
| 2001 | * | 1998 | * |
| 2002 | * Returns bytes read on success, negative value otherwise. | 1999 | * Returns bytes read on success, negative value otherwise. |
| 2003 | * | ||
| 2004 | * Caller holds tomoyo_read_lock(). | ||
| 2005 | */ | 2000 | */ |
| 2006 | int tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, | 2001 | int tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, |
| 2007 | const int buffer_len) | 2002 | const int buffer_len) |
| 2008 | { | 2003 | { |
| 2009 | int len; | 2004 | int len; |
| 2005 | int idx; | ||
| 2010 | 2006 | ||
| 2011 | if (!head->read) | 2007 | if (!head->read) |
| 2012 | return -ENOSYS; | 2008 | return -ENOSYS; |
| @@ -2014,6 +2010,7 @@ int tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, | |||
| 2014 | return -EINTR; | 2010 | return -EINTR; |
| 2015 | head->read_user_buf = buffer; | 2011 | head->read_user_buf = buffer; |
| 2016 | head->read_user_buf_avail = buffer_len; | 2012 | head->read_user_buf_avail = buffer_len; |
| 2013 | idx = tomoyo_read_lock(); | ||
| 2017 | if (tomoyo_flush(head)) | 2014 | if (tomoyo_flush(head)) |
| 2018 | /* Call the policy handler. */ | 2015 | /* Call the policy handler. */ |
| 2019 | do { | 2016 | do { |
| @@ -2021,6 +2018,7 @@ int tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, | |||
| 2021 | head->read(head); | 2018 | head->read(head); |
| 2022 | } while (tomoyo_flush(head) && | 2019 | } while (tomoyo_flush(head) && |
| 2023 | tomoyo_has_more_namespace(head)); | 2020 | tomoyo_has_more_namespace(head)); |
| 2021 | tomoyo_read_unlock(idx); | ||
| 2024 | len = head->read_user_buf - buffer; | 2022 | len = head->read_user_buf - buffer; |
| 2025 | mutex_unlock(&head->io_sem); | 2023 | mutex_unlock(&head->io_sem); |
| 2026 | return len; | 2024 | return len; |
| @@ -2071,8 +2069,6 @@ static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line) | |||
| 2071 | * @buffer_len: Size of @buffer. | 2069 | * @buffer_len: Size of @buffer. |
| 2072 | * | 2070 | * |
| 2073 | * Returns @buffer_len on success, negative value otherwise. | 2071 | * Returns @buffer_len on success, negative value otherwise. |
| 2074 | * | ||
| 2075 | * Caller holds tomoyo_read_lock(). | ||
| 2076 | */ | 2072 | */ |
| 2077 | int tomoyo_write_control(struct tomoyo_io_buffer *head, | 2073 | int tomoyo_write_control(struct tomoyo_io_buffer *head, |
| 2078 | const char __user *buffer, const int buffer_len) | 2074 | const char __user *buffer, const int buffer_len) |
| @@ -2080,12 +2076,14 @@ int tomoyo_write_control(struct tomoyo_io_buffer *head, | |||
| 2080 | int error = buffer_len; | 2076 | int error = buffer_len; |
| 2081 | size_t avail_len = buffer_len; | 2077 | size_t avail_len = buffer_len; |
| 2082 | char *cp0 = head->write_buf; | 2078 | char *cp0 = head->write_buf; |
| 2079 | int idx; | ||
| 2083 | if (!head->write) | 2080 | if (!head->write) |
| 2084 | return -ENOSYS; | 2081 | return -ENOSYS; |
| 2085 | if (!access_ok(VERIFY_READ, buffer, buffer_len)) | 2082 | if (!access_ok(VERIFY_READ, buffer, buffer_len)) |
| 2086 | return -EFAULT; | 2083 | return -EFAULT; |
| 2087 | if (mutex_lock_interruptible(&head->io_sem)) | 2084 | if (mutex_lock_interruptible(&head->io_sem)) |
| 2088 | return -EINTR; | 2085 | return -EINTR; |
| 2086 | idx = tomoyo_read_lock(); | ||
| 2089 | /* Read a line and dispatch it to the policy handler. */ | 2087 | /* Read a line and dispatch it to the policy handler. */ |
| 2090 | while (avail_len > 0) { | 2088 | while (avail_len > 0) { |
| 2091 | char c; | 2089 | char c; |
| @@ -2148,6 +2146,7 @@ int tomoyo_write_control(struct tomoyo_io_buffer *head, | |||
| 2148 | } | 2146 | } |
| 2149 | } | 2147 | } |
| 2150 | out: | 2148 | out: |
| 2149 | tomoyo_read_unlock(idx); | ||
| 2151 | mutex_unlock(&head->io_sem); | 2150 | mutex_unlock(&head->io_sem); |
| 2152 | return error; | 2151 | return error; |
| 2153 | } | 2152 | } |
| @@ -2157,30 +2156,18 @@ out: | |||
| 2157 | * | 2156 | * |
| 2158 | * @head: Pointer to "struct tomoyo_io_buffer". | 2157 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 2159 | * | 2158 | * |
| 2160 | * Releases memory and returns 0. | 2159 | * Returns 0. |
| 2161 | * | ||
| 2162 | * Caller looses tomoyo_read_lock(). | ||
| 2163 | */ | 2160 | */ |
| 2164 | int tomoyo_close_control(struct tomoyo_io_buffer *head) | 2161 | int tomoyo_close_control(struct tomoyo_io_buffer *head) |
| 2165 | { | 2162 | { |
| 2166 | const bool is_write = !!head->write_buf; | ||
| 2167 | |||
| 2168 | /* | 2163 | /* |
| 2169 | * If the file is /sys/kernel/security/tomoyo/query , decrement the | 2164 | * If the file is /sys/kernel/security/tomoyo/query , decrement the |
| 2170 | * observer counter. | 2165 | * observer counter. |
| 2171 | */ | 2166 | */ |
| 2172 | if (head->type == TOMOYO_QUERY) | 2167 | if (head->type == TOMOYO_QUERY && |
| 2173 | atomic_dec(&tomoyo_query_observers); | 2168 | atomic_dec_and_test(&tomoyo_query_observers)) |
| 2174 | else if (head->type != TOMOYO_AUDIT) | 2169 | wake_up_all(&tomoyo_answer_wait); |
| 2175 | tomoyo_read_unlock(head->reader_idx); | 2170 | tomoyo_notify_gc(head, false); |
| 2176 | /* Release memory used for policy I/O. */ | ||
| 2177 | kfree(head->read_buf); | ||
| 2178 | head->read_buf = NULL; | ||
| 2179 | kfree(head->write_buf); | ||
| 2180 | head->write_buf = NULL; | ||
| 2181 | kfree(head); | ||
| 2182 | if (is_write) | ||
| 2183 | tomoyo_run_gc(); | ||
| 2184 | return 0; | 2171 | return 0; |
| 2185 | } | 2172 | } |
| 2186 | 2173 | ||
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 53c8798e38b7..a5eeabcc0738 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h | |||
| @@ -441,8 +441,6 @@ struct tomoyo_io_buffer { | |||
| 441 | int (*poll) (struct file *file, poll_table *wait); | 441 | int (*poll) (struct file *file, poll_table *wait); |
| 442 | /* Exclusive lock for this structure. */ | 442 | /* Exclusive lock for this structure. */ |
| 443 | struct mutex io_sem; | 443 | struct mutex io_sem; |
| 444 | /* Index returned by tomoyo_read_lock(). */ | ||
| 445 | int reader_idx; | ||
| 446 | char __user *read_user_buf; | 444 | char __user *read_user_buf; |
| 447 | int read_user_buf_avail; | 445 | int read_user_buf_avail; |
| 448 | struct { | 446 | struct { |
| @@ -480,6 +478,10 @@ struct tomoyo_io_buffer { | |||
| 480 | int writebuf_size; | 478 | int writebuf_size; |
| 481 | /* Type of this interface. */ | 479 | /* Type of this interface. */ |
| 482 | u8 type; | 480 | u8 type; |
| 481 | /* Users counter protected by tomoyo_io_buffer_list_lock. */ | ||
| 482 | u8 users; | ||
| 483 | /* List for telling GC not to kfree() elements. */ | ||
| 484 | struct list_head list; | ||
| 483 | }; | 485 | }; |
| 484 | 486 | ||
| 485 | /* | 487 | /* |
| @@ -651,7 +653,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm); | |||
| 651 | void tomoyo_print_ulong(char *buffer, const int buffer_len, | 653 | void tomoyo_print_ulong(char *buffer, const int buffer_len, |
| 652 | const unsigned long value, const u8 type); | 654 | const unsigned long value, const u8 type); |
| 653 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr); | 655 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr); |
| 654 | void tomoyo_run_gc(void); | 656 | void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register); |
| 655 | void tomoyo_memory_free(void *ptr); | 657 | void tomoyo_memory_free(void *ptr); |
| 656 | int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, | 658 | int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, |
| 657 | struct tomoyo_acl_param *param, | 659 | struct tomoyo_acl_param *param, |
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 782e844dca7f..1e1a6c8c832c 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c | |||
| @@ -11,13 +11,123 @@ | |||
| 11 | #include <linux/kthread.h> | 11 | #include <linux/kthread.h> |
| 12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
| 13 | 13 | ||
| 14 | /* The list for "struct tomoyo_io_buffer". */ | ||
| 15 | static LIST_HEAD(tomoyo_io_buffer_list); | ||
| 16 | /* Lock for protecting tomoyo_io_buffer_list. */ | ||
| 17 | static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock); | ||
| 18 | |||
| 19 | /* Size of an element. */ | ||
| 20 | static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = { | ||
| 21 | [TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group), | ||
| 22 | [TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group), | ||
| 23 | [TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group), | ||
| 24 | [TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator), | ||
| 25 | [TOMOYO_ID_TRANSITION_CONTROL] = | ||
| 26 | sizeof(struct tomoyo_transition_control), | ||
| 27 | [TOMOYO_ID_MANAGER] = sizeof(struct tomoyo_manager), | ||
| 28 | /* [TOMOYO_ID_NAME] = "struct tomoyo_name"->size, */ | ||
| 29 | /* [TOMOYO_ID_ACL] = | ||
| 30 | tomoyo_acl_size["struct tomoyo_acl_info"->type], */ | ||
| 31 | [TOMOYO_ID_DOMAIN] = sizeof(struct tomoyo_domain_info), | ||
| 32 | }; | ||
| 33 | |||
| 34 | /* Size of a domain ACL element. */ | ||
| 35 | static const u8 tomoyo_acl_size[] = { | ||
| 36 | [TOMOYO_TYPE_PATH_ACL] = sizeof(struct tomoyo_path_acl), | ||
| 37 | [TOMOYO_TYPE_PATH2_ACL] = sizeof(struct tomoyo_path2_acl), | ||
| 38 | [TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl), | ||
| 39 | [TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl), | ||
| 40 | [TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl), | ||
| 41 | }; | ||
| 42 | |||
| 43 | /** | ||
| 44 | * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not. | ||
| 45 | * | ||
| 46 | * @element: Pointer to "struct list_head". | ||
| 47 | * | ||
| 48 | * Returns true if @element is used by /sys/kernel/security/tomoyo/ users, | ||
| 49 | * false otherwise. | ||
| 50 | */ | ||
| 51 | static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element) | ||
| 52 | { | ||
| 53 | struct tomoyo_io_buffer *head; | ||
| 54 | bool in_use = false; | ||
| 55 | |||
| 56 | spin_lock(&tomoyo_io_buffer_list_lock); | ||
| 57 | list_for_each_entry(head, &tomoyo_io_buffer_list, list) { | ||
| 58 | head->users++; | ||
| 59 | spin_unlock(&tomoyo_io_buffer_list_lock); | ||
| 60 | if (mutex_lock_interruptible(&head->io_sem)) { | ||
| 61 | in_use = true; | ||
| 62 | goto out; | ||
| 63 | } | ||
| 64 | if (head->r.domain == element || head->r.group == element || | ||
| 65 | head->r.acl == element || &head->w.domain->list == element) | ||
| 66 | in_use = true; | ||
| 67 | mutex_unlock(&head->io_sem); | ||
| 68 | out: | ||
| 69 | spin_lock(&tomoyo_io_buffer_list_lock); | ||
| 70 | head->users--; | ||
| 71 | if (in_use) | ||
| 72 | break; | ||
| 73 | } | ||
| 74 | spin_unlock(&tomoyo_io_buffer_list_lock); | ||
| 75 | return in_use; | ||
| 76 | } | ||
| 77 | |||
| 78 | /** | ||
| 79 | * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not. | ||
| 80 | * | ||
| 81 | * @string: String to check. | ||
| 82 | * @size: Memory allocated for @string . | ||
| 83 | * | ||
| 84 | * Returns true if @string is used by /sys/kernel/security/tomoyo/ users, | ||
| 85 | * false otherwise. | ||
| 86 | */ | ||
| 87 | static bool tomoyo_name_used_by_io_buffer(const char *string, | ||
| 88 | const size_t size) | ||
| 89 | { | ||
| 90 | struct tomoyo_io_buffer *head; | ||
| 91 | bool in_use = false; | ||
| 92 | |||
| 93 | spin_lock(&tomoyo_io_buffer_list_lock); | ||
| 94 | list_for_each_entry(head, &tomoyo_io_buffer_list, list) { | ||
| 95 | int i; | ||
| 96 | head->users++; | ||
| 97 | spin_unlock(&tomoyo_io_buffer_list_lock); | ||
| 98 | if (mutex_lock_interruptible(&head->io_sem)) { | ||
| 99 | in_use = true; | ||
| 100 | goto out; | ||
| 101 | } | ||
| 102 | for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) { | ||
| 103 | const char *w = head->r.w[i]; | ||
| 104 | if (w < string || w > string + size) | ||
| 105 | continue; | ||
| 106 | in_use = true; | ||
| 107 | break; | ||
| 108 | } | ||
| 109 | mutex_unlock(&head->io_sem); | ||
| 110 | out: | ||
| 111 | spin_lock(&tomoyo_io_buffer_list_lock); | ||
| 112 | head->users--; | ||
| 113 | if (in_use) | ||
| 114 | break; | ||
| 115 | } | ||
| 116 | spin_unlock(&tomoyo_io_buffer_list_lock); | ||
| 117 | return in_use; | ||
| 118 | } | ||
| 119 | |||
| 120 | /* Structure for garbage collection. */ | ||
| 14 | struct tomoyo_gc { | 121 | struct tomoyo_gc { |
| 15 | struct list_head list; | 122 | struct list_head list; |
| 16 | enum tomoyo_policy_id type; | 123 | enum tomoyo_policy_id type; |
| 124 | size_t size; | ||
| 17 | struct list_head *element; | 125 | struct list_head *element; |
| 18 | }; | 126 | }; |
| 19 | static LIST_HEAD(tomoyo_gc_queue); | 127 | /* List of entries to be deleted. */ |
| 20 | static DEFINE_MUTEX(tomoyo_gc_mutex); | 128 | static LIST_HEAD(tomoyo_gc_list); |
| 129 | /* Length of tomoyo_gc_list. */ | ||
| 130 | static int tomoyo_gc_list_len; | ||
| 21 | 131 | ||
| 22 | /** | 132 | /** |
| 23 | * tomoyo_add_to_gc - Add an entry to to be deleted list. | 133 | * tomoyo_add_to_gc - Add an entry to to be deleted list. |
| @@ -43,10 +153,42 @@ static bool tomoyo_add_to_gc(const int type, struct list_head *element) | |||
| 43 | if (!entry) | 153 | if (!entry) |
| 44 | return false; | 154 | return false; |
| 45 | entry->type = type; | 155 | entry->type = type; |
| 156 | if (type == TOMOYO_ID_ACL) | ||
| 157 | entry->size = tomoyo_acl_size[ | ||
| 158 | container_of(element, | ||
| 159 | typeof(struct tomoyo_acl_info), | ||
| 160 | list)->type]; | ||
| 161 | else if (type == TOMOYO_ID_NAME) | ||
| 162 | entry->size = strlen(container_of(element, | ||
| 163 | typeof(struct tomoyo_name), | ||
| 164 | head.list)->entry.name) + 1; | ||
| 165 | else | ||
| 166 | entry->size = tomoyo_element_size[type]; | ||
| 46 | entry->element = element; | 167 | entry->element = element; |
| 47 | list_add(&entry->list, &tomoyo_gc_queue); | 168 | list_add(&entry->list, &tomoyo_gc_list); |
| 48 | list_del_rcu(element); | 169 | list_del_rcu(element); |
| 49 | return true; | 170 | return tomoyo_gc_list_len++ < 128; |
| 171 | } | ||
| 172 | |||
| 173 | /** | ||
| 174 | * tomoyo_element_linked_by_gc - Validate next element of an entry. | ||
| 175 | * | ||
| 176 | * @element: Pointer to an element. | ||
| 177 | * @size: Size of @element in byte. | ||
| 178 | * | ||
| 179 | * Returns true if @element is linked by other elements in the garbage | ||
| 180 | * collector's queue, false otherwise. | ||
| 181 | */ | ||
| 182 | static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size) | ||
| 183 | { | ||
| 184 | struct tomoyo_gc *p; | ||
| 185 | list_for_each_entry(p, &tomoyo_gc_list, list) { | ||
| 186 | const u8 *ptr = (const u8 *) p->element->next; | ||
| 187 | if (ptr < element || element + size < ptr) | ||
| 188 | continue; | ||
| 189 | return true; | ||
| 190 | } | ||
| 191 | return false; | ||
| 50 | } | 192 | } |
| 51 | 193 | ||
| 52 | /** | 194 | /** |
| @@ -151,6 +293,13 @@ static void tomoyo_del_acl(struct list_head *element) | |||
| 151 | } | 293 | } |
| 152 | } | 294 | } |
| 153 | 295 | ||
| 296 | /** | ||
| 297 | * tomoyo_del_domain - Delete members in "struct tomoyo_domain_info". | ||
| 298 | * | ||
| 299 | * @element: Pointer to "struct list_head". | ||
| 300 | * | ||
| 301 | * Returns true if deleted, false otherwise. | ||
| 302 | */ | ||
| 154 | static bool tomoyo_del_domain(struct list_head *element) | 303 | static bool tomoyo_del_domain(struct list_head *element) |
| 155 | { | 304 | { |
| 156 | struct tomoyo_domain_info *domain = | 305 | struct tomoyo_domain_info *domain = |
| @@ -360,13 +509,44 @@ unlock: | |||
| 360 | mutex_unlock(&tomoyo_policy_lock); | 509 | mutex_unlock(&tomoyo_policy_lock); |
| 361 | } | 510 | } |
| 362 | 511 | ||
| 363 | static void tomoyo_kfree_entry(void) | 512 | /** |
| 513 | * tomoyo_kfree_entry - Delete entries in tomoyo_gc_list. | ||
| 514 | * | ||
| 515 | * Returns true if some entries were kfree()d, false otherwise. | ||
| 516 | */ | ||
| 517 | static bool tomoyo_kfree_entry(void) | ||
| 364 | { | 518 | { |
| 365 | struct tomoyo_gc *p; | 519 | struct tomoyo_gc *p; |
| 366 | struct tomoyo_gc *tmp; | 520 | struct tomoyo_gc *tmp; |
| 521 | bool result = false; | ||
| 367 | 522 | ||
| 368 | list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { | 523 | list_for_each_entry_safe(p, tmp, &tomoyo_gc_list, list) { |
| 369 | struct list_head *element = p->element; | 524 | struct list_head *element = p->element; |
| 525 | |||
| 526 | /* | ||
| 527 | * list_del_rcu() in tomoyo_add_to_gc() guarantees that the | ||
| 528 | * list element became no longer reachable from the list which | ||
| 529 | * the element was originally on (e.g. tomoyo_domain_list). | ||
| 530 | * Also, synchronize_srcu() in tomoyo_gc_thread() guarantees | ||
| 531 | * that the list element became no longer referenced by syscall | ||
| 532 | * users. | ||
| 533 | * | ||
| 534 | * However, there are three users which may still be using the | ||
| 535 | * list element. We need to defer until all of these users | ||
| 536 | * forget the list element. | ||
| 537 | * | ||
| 538 | * Firstly, defer until "struct tomoyo_io_buffer"->r.{domain, | ||
| 539 | * group,acl} and "struct tomoyo_io_buffer"->w.domain forget | ||
| 540 | * the list element. | ||
| 541 | */ | ||
| 542 | if (tomoyo_struct_used_by_io_buffer(element)) | ||
| 543 | continue; | ||
| 544 | /* | ||
| 545 | * Secondly, defer until all other elements in the | ||
| 546 | * tomoyo_gc_list list forget the list element. | ||
| 547 | */ | ||
| 548 | if (tomoyo_element_linked_by_gc((const u8 *) element, p->size)) | ||
| 549 | continue; | ||
| 370 | switch (p->type) { | 550 | switch (p->type) { |
| 371 | case TOMOYO_ID_TRANSITION_CONTROL: | 551 | case TOMOYO_ID_TRANSITION_CONTROL: |
| 372 | tomoyo_del_transition_control(element); | 552 | tomoyo_del_transition_control(element); |
| @@ -378,6 +558,14 @@ static void tomoyo_kfree_entry(void) | |||
| 378 | tomoyo_del_manager(element); | 558 | tomoyo_del_manager(element); |
| 379 | break; | 559 | break; |
| 380 | case TOMOYO_ID_NAME: | 560 | case TOMOYO_ID_NAME: |
| 561 | /* | ||
| 562 | * Thirdly, defer until all "struct tomoyo_io_buffer" | ||
| 563 | * ->r.w[] forget the list element. | ||
| 564 | */ | ||
| 565 | if (tomoyo_name_used_by_io_buffer( | ||
| 566 | container_of(element, typeof(struct tomoyo_name), | ||
| 567 | head.list)->entry.name, p->size)) | ||
| 568 | continue; | ||
| 381 | tomoyo_del_name(element); | 569 | tomoyo_del_name(element); |
| 382 | break; | 570 | break; |
| 383 | case TOMOYO_ID_ACL: | 571 | case TOMOYO_ID_ACL: |
| @@ -402,7 +590,10 @@ static void tomoyo_kfree_entry(void) | |||
| 402 | tomoyo_memory_free(element); | 590 | tomoyo_memory_free(element); |
| 403 | list_del(&p->list); | 591 | list_del(&p->list); |
| 404 | kfree(p); | 592 | kfree(p); |
| 593 | tomoyo_gc_list_len--; | ||
| 594 | result = true; | ||
| 405 | } | 595 | } |
| 596 | return result; | ||
| 406 | } | 597 | } |
| 407 | 598 | ||
| 408 | /** | 599 | /** |
| @@ -418,25 +609,70 @@ static void tomoyo_kfree_entry(void) | |||
| 418 | */ | 609 | */ |
| 419 | static int tomoyo_gc_thread(void *unused) | 610 | static int tomoyo_gc_thread(void *unused) |
| 420 | { | 611 | { |
| 612 | /* Garbage collector thread is exclusive. */ | ||
| 613 | static DEFINE_MUTEX(tomoyo_gc_mutex); | ||
| 614 | if (!mutex_trylock(&tomoyo_gc_mutex)) | ||
| 615 | goto out; | ||
| 421 | daemonize("GC for TOMOYO"); | 616 | daemonize("GC for TOMOYO"); |
| 422 | if (mutex_trylock(&tomoyo_gc_mutex)) { | 617 | do { |
| 423 | int i; | 618 | tomoyo_collect_entry(); |
| 424 | for (i = 0; i < 10; i++) { | 619 | if (list_empty(&tomoyo_gc_list)) |
| 425 | tomoyo_collect_entry(); | 620 | break; |
| 426 | if (list_empty(&tomoyo_gc_queue)) | 621 | synchronize_srcu(&tomoyo_ss); |
| 427 | break; | 622 | } while (tomoyo_kfree_entry()); |
| 428 | synchronize_srcu(&tomoyo_ss); | 623 | { |
| 429 | tomoyo_kfree_entry(); | 624 | struct tomoyo_io_buffer *head; |
| 625 | struct tomoyo_io_buffer *tmp; | ||
| 626 | |||
| 627 | spin_lock(&tomoyo_io_buffer_list_lock); | ||
| 628 | list_for_each_entry_safe(head, tmp, &tomoyo_io_buffer_list, | ||
| 629 | list) { | ||
| 630 | if (head->users) | ||
| 631 | continue; | ||
| 632 | list_del(&head->list); | ||
| 633 | kfree(head->read_buf); | ||
| 634 | kfree(head->write_buf); | ||
| 635 | kfree(head); | ||
| 430 | } | 636 | } |
| 431 | mutex_unlock(&tomoyo_gc_mutex); | 637 | spin_unlock(&tomoyo_io_buffer_list_lock); |
| 432 | } | 638 | } |
| 433 | do_exit(0); | 639 | mutex_unlock(&tomoyo_gc_mutex); |
| 640 | out: | ||
| 641 | /* This acts as do_exit(0). */ | ||
| 642 | return 0; | ||
| 434 | } | 643 | } |
| 435 | 644 | ||
| 436 | void tomoyo_run_gc(void) | 645 | /** |
| 646 | * tomoyo_notify_gc - Register/unregister /sys/kernel/security/tomoyo/ users. | ||
| 647 | * | ||
| 648 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 649 | * @is_register: True if register, false if unregister. | ||
| 650 | * | ||
| 651 | * Returns nothing. | ||
| 652 | */ | ||
| 653 | void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register) | ||
| 437 | { | 654 | { |
| 438 | struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL, | 655 | bool is_write = false; |
| 439 | "GC for TOMOYO"); | 656 | |
| 440 | if (!IS_ERR(task)) | 657 | spin_lock(&tomoyo_io_buffer_list_lock); |
| 441 | wake_up_process(task); | 658 | if (is_register) { |
| 659 | head->users = 1; | ||
| 660 | list_add(&head->list, &tomoyo_io_buffer_list); | ||
| 661 | } else { | ||
| 662 | is_write = head->write_buf != NULL; | ||
| 663 | if (!--head->users) { | ||
| 664 | list_del(&head->list); | ||
| 665 | kfree(head->read_buf); | ||
| 666 | kfree(head->write_buf); | ||
| 667 | kfree(head); | ||
| 668 | } | ||
| 669 | } | ||
| 670 | spin_unlock(&tomoyo_io_buffer_list_lock); | ||
| 671 | if (is_write) { | ||
| 672 | struct task_struct *task = kthread_create(tomoyo_gc_thread, | ||
| 673 | NULL, | ||
| 674 | "GC for TOMOYO"); | ||
| 675 | if (!IS_ERR(task)) | ||
| 676 | wake_up_process(task); | ||
| 677 | } | ||
| 442 | } | 678 | } |
