diff --git a/[refs] b/[refs] index ee2bb7cd4a79..7070268f6495 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: a427fd14d3edf6396c4b9638dbc8e2972afaa05b +refs/heads/master: b78049831ffed65f0b4e61f69df14f3ab17922cb diff --git a/trunk/include/linux/kernel.h b/trunk/include/linux/kernel.h index 46ac9a50528d..8eefcf7e95eb 100644 --- a/trunk/include/linux/kernel.h +++ b/trunk/include/linux/kernel.h @@ -382,7 +382,7 @@ static inline char *pack_hex_byte(char *buf, u8 byte) } extern int hex_to_bin(char ch); -extern void hex2bin(u8 *dst, const char *src, size_t count); +extern int __must_check hex2bin(u8 *dst, const char *src, size_t count); /* * General tracing related utility functions - trace_printk(), diff --git a/trunk/lib/hexdump.c b/trunk/lib/hexdump.c index f5fe6ba7a3ab..51d5ae210244 100644 --- a/trunk/lib/hexdump.c +++ b/trunk/lib/hexdump.c @@ -38,14 +38,21 @@ EXPORT_SYMBOL(hex_to_bin); * @dst: binary result * @src: ascii hexadecimal string * @count: result length + * + * Return 0 on success, -1 in case of bad input. */ -void hex2bin(u8 *dst, const char *src, size_t count) +int hex2bin(u8 *dst, const char *src, size_t count) { while (count--) { - *dst = hex_to_bin(*src++) << 4; - *dst += hex_to_bin(*src++); - dst++; + int hi = hex_to_bin(*src++); + int lo = hex_to_bin(*src++); + + if ((hi < 0) || (lo < 0)) + return -1; + + *dst++ = (hi << 4) | lo; } + return 0; } EXPORT_SYMBOL(hex2bin); diff --git a/trunk/security/tomoyo/common.c b/trunk/security/tomoyo/common.c index 2e2802060eef..0994948f3edc 100644 --- a/trunk/security/tomoyo/common.c +++ b/trunk/security/tomoyo/common.c @@ -262,17 +262,13 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string) WARN_ON(1); } -static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, - ...) __printf(2, 3); - /** * tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure. * * @head: Pointer to "struct tomoyo_io_buffer". * @fmt: The printf()'s format string, followed by parameters. */ -static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, - ...) +void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) { va_list args; size_t len; diff --git a/trunk/security/tomoyo/common.h b/trunk/security/tomoyo/common.h index ed311d7a8ce0..a2bc33fc60b6 100644 --- a/trunk/security/tomoyo/common.h +++ b/trunk/security/tomoyo/common.h @@ -52,9 +52,6 @@ #define TOMOYO_EXEC_TMPSIZE 4096 -/* Garbage collector is trying to kfree() this element. */ -#define TOMOYO_GC_IN_PROGRESS -1 - /* Profile number is an integer between 0 and 255. */ #define TOMOYO_MAX_PROFILES 256 @@ -401,7 +398,7 @@ enum tomoyo_pref_index { /* Common header for holding ACL entries. */ struct tomoyo_acl_head { struct list_head list; - s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */ + bool is_deleted; } __packed; /* Common header for shared entries. */ @@ -668,7 +665,7 @@ struct tomoyo_condition { struct tomoyo_acl_info { struct list_head list; struct tomoyo_condition *cond; /* Maybe NULL. */ - s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */ + bool is_deleted; u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */ } __packed; @@ -981,6 +978,8 @@ int tomoyo_path_number_perm(const u8 operation, struct path *path, unsigned long number); int tomoyo_path_perm(const u8 operation, struct path *path, const char *target); +int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, + const struct tomoyo_path_info *filename); int tomoyo_poll_control(struct file *file, poll_table *wait); int tomoyo_poll_log(struct file *file, poll_table *wait); int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr, @@ -1042,7 +1041,10 @@ void tomoyo_del_condition(struct list_head *element); void tomoyo_fill_path_info(struct tomoyo_path_info *ptr); void tomoyo_get_attributes(struct tomoyo_obj_info *obj); void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns); +void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) + __printf(2, 3); void tomoyo_load_policy(const char *filename); +void tomoyo_memory_free(void *ptr); void tomoyo_normalize_line(unsigned char *buffer); void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register); void tomoyo_print_ip(char *buf, const unsigned int size, diff --git a/trunk/security/tomoyo/condition.c b/trunk/security/tomoyo/condition.c index 986330b8c73e..b854959c0fd4 100644 --- a/trunk/security/tomoyo/condition.c +++ b/trunk/security/tomoyo/condition.c @@ -400,9 +400,8 @@ static struct tomoyo_condition *tomoyo_commit_condition found = true; goto out; } - list_for_each_entry(ptr, &tomoyo_condition_list, head.list) { - if (!tomoyo_same_condition(ptr, entry) || - atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS) + list_for_each_entry_rcu(ptr, &tomoyo_condition_list, head.list) { + if (!tomoyo_same_condition(ptr, entry)) continue; /* Same entry found. Share this entry. */ atomic_inc(&ptr->head.users); @@ -412,7 +411,8 @@ static struct tomoyo_condition *tomoyo_commit_condition if (!found) { if (tomoyo_memory_ok(entry)) { atomic_set(&entry->head.users, 1); - list_add(&entry->head.list, &tomoyo_condition_list); + list_add_rcu(&entry->head.list, + &tomoyo_condition_list); } else { found = true; ptr = NULL; diff --git a/trunk/security/tomoyo/domain.c b/trunk/security/tomoyo/domain.c index da16dfeed728..860390ee1fbe 100644 --- a/trunk/security/tomoyo/domain.c +++ b/trunk/security/tomoyo/domain.c @@ -39,8 +39,6 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, if (mutex_lock_interruptible(&tomoyo_policy_lock)) return -ENOMEM; list_for_each_entry_rcu(entry, list, list) { - if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS) - continue; if (!check_duplicate(entry, new_entry)) continue; entry->is_deleted = param->is_delete; @@ -117,8 +115,6 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; list_for_each_entry_rcu(entry, list, list) { - if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS) - continue; if (!tomoyo_same_acl_head(entry, new_entry) || !check_duplicate(entry, new_entry)) continue; @@ -571,7 +567,6 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, tomoyo_write_log(&r, "use_profile %u\n", entry->profile); tomoyo_write_log(&r, "use_group %u\n", entry->group); - tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES); } } return entry; diff --git a/trunk/security/tomoyo/file.c b/trunk/security/tomoyo/file.c index 400390790745..b280c1bd652d 100644 --- a/trunk/security/tomoyo/file.c +++ b/trunk/security/tomoyo/file.c @@ -555,8 +555,8 @@ static int tomoyo_update_path2_acl(const u8 perm, * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, - const struct tomoyo_path_info *filename) +int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, + const struct tomoyo_path_info *filename) { int error; diff --git a/trunk/security/tomoyo/gc.c b/trunk/security/tomoyo/gc.c index c3214b32dbfb..7747ceb9a221 100644 --- a/trunk/security/tomoyo/gc.c +++ b/trunk/security/tomoyo/gc.c @@ -8,26 +8,40 @@ #include #include -/** - * tomoyo_memory_free - Free memory for elements. - * - * @ptr: Pointer to allocated memory. - * - * Returns nothing. - * - * Caller holds tomoyo_policy_lock mutex. - */ -static inline void tomoyo_memory_free(void *ptr) -{ - tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= ksize(ptr); - kfree(ptr); -} - /* The list for "struct tomoyo_io_buffer". */ static LIST_HEAD(tomoyo_io_buffer_list); /* Lock for protecting tomoyo_io_buffer_list. */ static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock); +/* Size of an element. */ +static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = { + [TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group), + [TOMOYO_ID_ADDRESS_GROUP] = sizeof(struct tomoyo_address_group), + [TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group), + [TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group), + [TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator), + [TOMOYO_ID_TRANSITION_CONTROL] = + sizeof(struct tomoyo_transition_control), + [TOMOYO_ID_MANAGER] = sizeof(struct tomoyo_manager), + /* [TOMOYO_ID_CONDITION] = "struct tomoyo_condition"->size, */ + /* [TOMOYO_ID_NAME] = "struct tomoyo_name"->size, */ + /* [TOMOYO_ID_ACL] = + tomoyo_acl_size["struct tomoyo_acl_info"->type], */ + [TOMOYO_ID_DOMAIN] = sizeof(struct tomoyo_domain_info), +}; + +/* Size of a domain ACL element. */ +static const u8 tomoyo_acl_size[] = { + [TOMOYO_TYPE_PATH_ACL] = sizeof(struct tomoyo_path_acl), + [TOMOYO_TYPE_PATH2_ACL] = sizeof(struct tomoyo_path2_acl), + [TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl), + [TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl), + [TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl), + [TOMOYO_TYPE_INET_ACL] = sizeof(struct tomoyo_inet_acl), + [TOMOYO_TYPE_UNIX_ACL] = sizeof(struct tomoyo_unix_acl), + [TOMOYO_TYPE_ENV_ACL] = sizeof(struct tomoyo_env_acl), +}; + /** * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not. * @@ -45,11 +59,15 @@ static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element) list_for_each_entry(head, &tomoyo_io_buffer_list, list) { head->users++; spin_unlock(&tomoyo_io_buffer_list_lock); - mutex_lock(&head->io_sem); + if (mutex_lock_interruptible(&head->io_sem)) { + in_use = true; + goto out; + } if (head->r.domain == element || head->r.group == element || head->r.acl == element || &head->w.domain->list == element) in_use = true; mutex_unlock(&head->io_sem); +out: spin_lock(&tomoyo_io_buffer_list_lock); head->users--; if (in_use) @@ -63,14 +81,15 @@ static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element) * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not. * * @string: String to check. + * @size: Memory allocated for @string . * * Returns true if @string is used by /sys/kernel/security/tomoyo/ users, * false otherwise. */ -static bool tomoyo_name_used_by_io_buffer(const char *string) +static bool tomoyo_name_used_by_io_buffer(const char *string, + const size_t size) { struct tomoyo_io_buffer *head; - const size_t size = strlen(string) + 1; bool in_use = false; spin_lock(&tomoyo_io_buffer_list_lock); @@ -78,7 +97,10 @@ static bool tomoyo_name_used_by_io_buffer(const char *string) int i; head->users++; spin_unlock(&tomoyo_io_buffer_list_lock); - mutex_lock(&head->io_sem); + if (mutex_lock_interruptible(&head->io_sem)) { + in_use = true; + goto out; + } for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) { const char *w = head->r.w[i]; if (w < string || w > string + size) @@ -87,6 +109,7 @@ static bool tomoyo_name_used_by_io_buffer(const char *string) break; } mutex_unlock(&head->io_sem); +out: spin_lock(&tomoyo_io_buffer_list_lock); head->users--; if (in_use) @@ -96,6 +119,84 @@ static bool tomoyo_name_used_by_io_buffer(const char *string) return in_use; } +/* Structure for garbage collection. */ +struct tomoyo_gc { + struct list_head list; + enum tomoyo_policy_id type; + size_t size; + struct list_head *element; +}; +/* List of entries to be deleted. */ +static LIST_HEAD(tomoyo_gc_list); +/* Length of tomoyo_gc_list. */ +static int tomoyo_gc_list_len; + +/** + * tomoyo_add_to_gc - Add an entry to to be deleted list. + * + * @type: One of values in "enum tomoyo_policy_id". + * @element: Pointer to "struct list_head". + * + * Returns true on success, false otherwise. + * + * Caller holds tomoyo_policy_lock mutex. + * + * Adding an entry needs kmalloc(). Thus, if we try to add thousands of + * entries at once, it will take too long time. Thus, do not add more than 128 + * entries per a scan. But to be able to handle worst case where all entries + * are in-use, we accept one more entry per a scan. + * + * If we use singly linked list using "struct list_head"->prev (which is + * LIST_POISON2), we can avoid kmalloc(). + */ +static bool tomoyo_add_to_gc(const int type, struct list_head *element) +{ + struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return false; + entry->type = type; + if (type == TOMOYO_ID_ACL) + entry->size = tomoyo_acl_size[ + container_of(element, + typeof(struct tomoyo_acl_info), + list)->type]; + else if (type == TOMOYO_ID_NAME) + entry->size = strlen(container_of(element, + typeof(struct tomoyo_name), + head.list)->entry.name) + 1; + else if (type == TOMOYO_ID_CONDITION) + entry->size = + container_of(element, typeof(struct tomoyo_condition), + head.list)->size; + else + entry->size = tomoyo_element_size[type]; + entry->element = element; + list_add(&entry->list, &tomoyo_gc_list); + list_del_rcu(element); + return tomoyo_gc_list_len++ < 128; +} + +/** + * tomoyo_element_linked_by_gc - Validate next element of an entry. + * + * @element: Pointer to an element. + * @size: Size of @element in byte. + * + * Returns true if @element is linked by other elements in the garbage + * collector's queue, false otherwise. + */ +static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size) +{ + struct tomoyo_gc *p; + list_for_each_entry(p, &tomoyo_gc_list, list) { + const u8 *ptr = (const u8 *) p->element->next; + if (ptr < element || element + size < ptr) + continue; + return true; + } + return false; +} + /** * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control". * @@ -103,7 +204,7 @@ static bool tomoyo_name_used_by_io_buffer(const char *string) * * Returns nothing. */ -static inline void tomoyo_del_transition_control(struct list_head *element) +static void tomoyo_del_transition_control(struct list_head *element) { struct tomoyo_transition_control *ptr = container_of(element, typeof(*ptr), head.list); @@ -118,7 +219,7 @@ static inline void tomoyo_del_transition_control(struct list_head *element) * * Returns nothing. */ -static inline void tomoyo_del_aggregator(struct list_head *element) +static void tomoyo_del_aggregator(struct list_head *element) { struct tomoyo_aggregator *ptr = container_of(element, typeof(*ptr), head.list); @@ -133,7 +234,7 @@ static inline void tomoyo_del_aggregator(struct list_head *element) * * Returns nothing. */ -static inline void tomoyo_del_manager(struct list_head *element) +static void tomoyo_del_manager(struct list_head *element) { struct tomoyo_manager *ptr = container_of(element, typeof(*ptr), head.list); @@ -229,26 +330,44 @@ static void tomoyo_del_acl(struct list_head *element) * * @element: Pointer to "struct list_head". * - * Returns nothing. - * - * Caller holds tomoyo_policy_lock mutex. + * Returns true if deleted, false otherwise. */ -static inline void tomoyo_del_domain(struct list_head *element) +static bool tomoyo_del_domain(struct list_head *element) { struct tomoyo_domain_info *domain = container_of(element, typeof(*domain), list); struct tomoyo_acl_info *acl; struct tomoyo_acl_info *tmp; /* - * Since this domain is referenced from neither - * "struct tomoyo_io_buffer" nor "struct cred"->security, we can delete - * elements without checking for is_deleted flag. + * Since we don't protect whole execve() operation using SRCU, + * we need to recheck domain->users at this point. + * + * (1) Reader starts SRCU section upon execve(). + * (2) Reader traverses tomoyo_domain_list and finds this domain. + * (3) Writer marks this domain as deleted. + * (4) Garbage collector removes this domain from tomoyo_domain_list + * because this domain is marked as deleted and used by nobody. + * (5) Reader saves reference to this domain into + * "struct linux_binprm"->cred->security . + * (6) Reader finishes SRCU section, although execve() operation has + * not finished yet. + * (7) Garbage collector waits for SRCU synchronization. + * (8) Garbage collector kfree() this domain because this domain is + * used by nobody. + * (9) Reader finishes execve() operation and restores this domain from + * "struct linux_binprm"->cred->security. + * + * By updating domain->users at (5), we can solve this race problem + * by rechecking domain->users at (8). */ + if (atomic_read(&domain->users)) + return false; list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { tomoyo_del_acl(&acl->list); tomoyo_memory_free(acl); } tomoyo_put_name(domain->domainname); + return true; } /** @@ -297,9 +416,10 @@ void tomoyo_del_condition(struct list_head *element) * * Returns nothing. */ -static inline void tomoyo_del_name(struct list_head *element) +static void tomoyo_del_name(struct list_head *element) { - /* Nothing to do. */ + const struct tomoyo_name *ptr = + container_of(element, typeof(*ptr), head.list); } /** @@ -309,7 +429,7 @@ static inline void tomoyo_del_name(struct list_head *element) * * Returns nothing. */ -static inline void tomoyo_del_path_group(struct list_head *element) +static void tomoyo_del_path_group(struct list_head *element) { struct tomoyo_path_group *member = container_of(element, typeof(*member), head.list); @@ -323,7 +443,7 @@ static inline void tomoyo_del_path_group(struct list_head *element) * * Returns nothing. */ -static inline void tomoyo_del_group(struct list_head *element) +static void tomoyo_del_group(struct list_head *element) { struct tomoyo_group *group = container_of(element, typeof(*group), head.list); @@ -349,110 +469,10 @@ static inline void tomoyo_del_address_group(struct list_head *element) * * Returns nothing. */ -static inline void tomoyo_del_number_group(struct list_head *element) +static void tomoyo_del_number_group(struct list_head *element) { - /* Nothing to do. */ -} - -/** - * tomoyo_try_to_gc - Try to kfree() an entry. - * - * @type: One of values in "enum tomoyo_policy_id". - * @element: Pointer to "struct list_head". - * - * Returns nothing. - * - * Caller holds tomoyo_policy_lock mutex. - */ -static void tomoyo_try_to_gc(const enum tomoyo_policy_id type, - struct list_head *element) -{ - /* - * __list_del_entry() guarantees that the list element became no longer - * reachable from the list which the element was originally on (e.g. - * tomoyo_domain_list). Also, synchronize_srcu() guarantees that the - * list element became no longer referenced by syscall users. - */ - __list_del_entry(element); - mutex_unlock(&tomoyo_policy_lock); - synchronize_srcu(&tomoyo_ss); - /* - * However, there are two users which may still be using the list - * element. We need to defer until both users forget this element. - * - * Don't kfree() until "struct tomoyo_io_buffer"->r.{domain,group,acl} - * and "struct tomoyo_io_buffer"->w.domain forget this element. - */ - if (tomoyo_struct_used_by_io_buffer(element)) - goto reinject; - switch (type) { - case TOMOYO_ID_TRANSITION_CONTROL: - tomoyo_del_transition_control(element); - break; - case TOMOYO_ID_MANAGER: - tomoyo_del_manager(element); - break; - case TOMOYO_ID_AGGREGATOR: - tomoyo_del_aggregator(element); - break; - case TOMOYO_ID_GROUP: - tomoyo_del_group(element); - break; - case TOMOYO_ID_PATH_GROUP: - tomoyo_del_path_group(element); - break; - case TOMOYO_ID_ADDRESS_GROUP: - tomoyo_del_address_group(element); - break; - case TOMOYO_ID_NUMBER_GROUP: - tomoyo_del_number_group(element); - break; - case TOMOYO_ID_CONDITION: - tomoyo_del_condition(element); - break; - case TOMOYO_ID_NAME: - /* - * Don't kfree() until all "struct tomoyo_io_buffer"->r.w[] - * forget this element. - */ - if (tomoyo_name_used_by_io_buffer - (container_of(element, typeof(struct tomoyo_name), - head.list)->entry.name)) - goto reinject; - tomoyo_del_name(element); - break; - case TOMOYO_ID_ACL: - tomoyo_del_acl(element); - break; - case TOMOYO_ID_DOMAIN: - /* - * Don't kfree() until all "struct cred"->security forget this - * element. - */ - if (atomic_read(&container_of - (element, typeof(struct tomoyo_domain_info), - list)->users)) - goto reinject; - break; - case TOMOYO_MAX_POLICY: - break; - } - mutex_lock(&tomoyo_policy_lock); - if (type == TOMOYO_ID_DOMAIN) - tomoyo_del_domain(element); - tomoyo_memory_free(element); - return; -reinject: - /* - * We can safely reinject this element here bacause - * (1) Appending list elements and removing list elements are protected - * by tomoyo_policy_lock mutex. - * (2) Only this function removes list elements and this function is - * exclusively executed by tomoyo_gc_mutex mutex. - * are true. - */ - mutex_lock(&tomoyo_policy_lock); - list_add_rcu(element, element->prev); + struct tomoyo_number_group *member = + container_of(element, typeof(*member), head.list); } /** @@ -461,19 +481,19 @@ static void tomoyo_try_to_gc(const enum tomoyo_policy_id type, * @id: One of values in "enum tomoyo_policy_id". * @member_list: Pointer to "struct list_head". * - * Returns nothing. + * Returns true if some elements are deleted, false otherwise. */ -static void tomoyo_collect_member(const enum tomoyo_policy_id id, +static bool tomoyo_collect_member(const enum tomoyo_policy_id id, struct list_head *member_list) { struct tomoyo_acl_head *member; - struct tomoyo_acl_head *tmp; - list_for_each_entry_safe(member, tmp, member_list, list) { + list_for_each_entry(member, member_list, list) { if (!member->is_deleted) continue; - member->is_deleted = TOMOYO_GC_IN_PROGRESS; - tomoyo_try_to_gc(id, &member->list); + if (!tomoyo_add_to_gc(id, &member->list)) + return false; } + return true; } /** @@ -481,22 +501,22 @@ static void tomoyo_collect_member(const enum tomoyo_policy_id id, * * @list: Pointer to "struct list_head". * - * Returns nothing. + * Returns true if some elements are deleted, false otherwise. */ -static void tomoyo_collect_acl(struct list_head *list) +static bool tomoyo_collect_acl(struct list_head *list) { struct tomoyo_acl_info *acl; - struct tomoyo_acl_info *tmp; - list_for_each_entry_safe(acl, tmp, list, list) { + list_for_each_entry(acl, list, list) { if (!acl->is_deleted) continue; - acl->is_deleted = TOMOYO_GC_IN_PROGRESS; - tomoyo_try_to_gc(TOMOYO_ID_ACL, &acl->list); + if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list)) + return false; } + return true; } /** - * tomoyo_collect_entry - Try to kfree() deleted elements. + * tomoyo_collect_entry - Scan lists for deleted elements. * * Returns nothing. */ @@ -505,40 +525,36 @@ static void tomoyo_collect_entry(void) int i; enum tomoyo_policy_id id; struct tomoyo_policy_namespace *ns; - mutex_lock(&tomoyo_policy_lock); + int idx; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + return; + idx = tomoyo_read_lock(); { struct tomoyo_domain_info *domain; - struct tomoyo_domain_info *tmp; - list_for_each_entry_safe(domain, tmp, &tomoyo_domain_list, - list) { - tomoyo_collect_acl(&domain->acl_info_list); + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { + if (!tomoyo_collect_acl(&domain->acl_info_list)) + goto unlock; if (!domain->is_deleted || atomic_read(&domain->users)) continue; - tomoyo_try_to_gc(TOMOYO_ID_DOMAIN, &domain->list); + /* + * Nobody is referring this domain. But somebody may + * refer this domain after successful execve(). + * We recheck domain->users after SRCU synchronization. + */ + if (!tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, &domain->list)) + goto unlock; } } - list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { + list_for_each_entry_rcu(ns, &tomoyo_namespace_list, namespace_list) { for (id = 0; id < TOMOYO_MAX_POLICY; id++) - tomoyo_collect_member(id, &ns->policy_list[id]); + if (!tomoyo_collect_member(id, &ns->policy_list[id])) + goto unlock; for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++) - tomoyo_collect_acl(&ns->acl_group[i]); - } - { - struct tomoyo_shared_acl_head *ptr; - struct tomoyo_shared_acl_head *tmp; - list_for_each_entry_safe(ptr, tmp, &tomoyo_condition_list, - list) { - if (atomic_read(&ptr->users) > 0) - continue; - atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS); - tomoyo_try_to_gc(TOMOYO_ID_CONDITION, &ptr->list); - } - } - list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { + if (!tomoyo_collect_acl(&ns->acl_group[i])) + goto unlock; for (i = 0; i < TOMOYO_MAX_GROUP; i++) { struct list_head *list = &ns->group_list[i]; struct tomoyo_group *group; - struct tomoyo_group *tmp; switch (i) { case 0: id = TOMOYO_ID_PATH_GROUP; @@ -550,37 +566,139 @@ static void tomoyo_collect_entry(void) id = TOMOYO_ID_ADDRESS_GROUP; break; } - list_for_each_entry_safe(group, tmp, list, head.list) { - tomoyo_collect_member(id, &group->member_list); + list_for_each_entry(group, list, head.list) { + if (!tomoyo_collect_member + (id, &group->member_list)) + goto unlock; if (!list_empty(&group->member_list) || - atomic_read(&group->head.users) > 0) + atomic_read(&group->head.users)) continue; - atomic_set(&group->head.users, - TOMOYO_GC_IN_PROGRESS); - tomoyo_try_to_gc(TOMOYO_ID_GROUP, - &group->head.list); + if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, + &group->head.list)) + goto unlock; } } } - for (i = 0; i < TOMOYO_MAX_HASH; i++) { - struct list_head *list = &tomoyo_name_list[i]; + id = TOMOYO_ID_CONDITION; + for (i = 0; i < TOMOYO_MAX_HASH + 1; i++) { + struct list_head *list = !i ? + &tomoyo_condition_list : &tomoyo_name_list[i - 1]; struct tomoyo_shared_acl_head *ptr; - struct tomoyo_shared_acl_head *tmp; - list_for_each_entry_safe(ptr, tmp, list, list) { - if (atomic_read(&ptr->users) > 0) + list_for_each_entry(ptr, list, list) { + if (atomic_read(&ptr->users)) continue; - atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS); - tomoyo_try_to_gc(TOMOYO_ID_NAME, &ptr->list); + if (!tomoyo_add_to_gc(id, &ptr->list)) + goto unlock; } + id = TOMOYO_ID_NAME; } +unlock: + tomoyo_read_unlock(idx); mutex_unlock(&tomoyo_policy_lock); } +/** + * tomoyo_kfree_entry - Delete entries in tomoyo_gc_list. + * + * Returns true if some entries were kfree()d, false otherwise. + */ +static bool tomoyo_kfree_entry(void) +{ + struct tomoyo_gc *p; + struct tomoyo_gc *tmp; + bool result = false; + + list_for_each_entry_safe(p, tmp, &tomoyo_gc_list, list) { + struct list_head *element = p->element; + + /* + * list_del_rcu() in tomoyo_add_to_gc() guarantees that the + * list element became no longer reachable from the list which + * the element was originally on (e.g. tomoyo_domain_list). + * Also, synchronize_srcu() in tomoyo_gc_thread() guarantees + * that the list element became no longer referenced by syscall + * users. + * + * However, there are three users which may still be using the + * list element. We need to defer until all of these users + * forget the list element. + * + * Firstly, defer until "struct tomoyo_io_buffer"->r.{domain, + * group,acl} and "struct tomoyo_io_buffer"->w.domain forget + * the list element. + */ + if (tomoyo_struct_used_by_io_buffer(element)) + continue; + /* + * Secondly, defer until all other elements in the + * tomoyo_gc_list list forget the list element. + */ + if (tomoyo_element_linked_by_gc((const u8 *) element, p->size)) + continue; + switch (p->type) { + case TOMOYO_ID_TRANSITION_CONTROL: + tomoyo_del_transition_control(element); + break; + case TOMOYO_ID_AGGREGATOR: + tomoyo_del_aggregator(element); + break; + case TOMOYO_ID_MANAGER: + tomoyo_del_manager(element); + break; + case TOMOYO_ID_CONDITION: + tomoyo_del_condition(element); + break; + case TOMOYO_ID_NAME: + /* + * Thirdly, defer until all "struct tomoyo_io_buffer" + * ->r.w[] forget the list element. + */ + if (tomoyo_name_used_by_io_buffer( + container_of(element, typeof(struct tomoyo_name), + head.list)->entry.name, p->size)) + continue; + tomoyo_del_name(element); + break; + case TOMOYO_ID_ACL: + tomoyo_del_acl(element); + break; + case TOMOYO_ID_DOMAIN: + if (!tomoyo_del_domain(element)) + continue; + break; + case TOMOYO_ID_PATH_GROUP: + tomoyo_del_path_group(element); + break; + case TOMOYO_ID_ADDRESS_GROUP: + tomoyo_del_address_group(element); + break; + case TOMOYO_ID_GROUP: + tomoyo_del_group(element); + break; + case TOMOYO_ID_NUMBER_GROUP: + tomoyo_del_number_group(element); + break; + case TOMOYO_MAX_POLICY: + break; + } + tomoyo_memory_free(element); + list_del(&p->list); + kfree(p); + tomoyo_gc_list_len--; + result = true; + } + return result; +} + /** * tomoyo_gc_thread - Garbage collector thread function. * * @unused: Unused. * + * In case OOM-killer choose this thread for termination, we create this thread + * as a short live thread whenever /sys/kernel/security/tomoyo/ interface was + * close()d. + * * Returns 0. */ static int tomoyo_gc_thread(void *unused) @@ -589,7 +707,13 @@ static int tomoyo_gc_thread(void *unused) static DEFINE_MUTEX(tomoyo_gc_mutex); if (!mutex_trylock(&tomoyo_gc_mutex)) goto out; - tomoyo_collect_entry(); + + do { + tomoyo_collect_entry(); + if (list_empty(&tomoyo_gc_list)) + break; + synchronize_srcu(&tomoyo_ss); + } while (tomoyo_kfree_entry()); { struct tomoyo_io_buffer *head; struct tomoyo_io_buffer *tmp; diff --git a/trunk/security/tomoyo/memory.c b/trunk/security/tomoyo/memory.c index 0e995716cc25..7a56051146c2 100644 --- a/trunk/security/tomoyo/memory.c +++ b/trunk/security/tomoyo/memory.c @@ -27,6 +27,8 @@ void tomoyo_warn_oom(const char *function) panic("MAC Initialization failed.\n"); } +/* Lock for protecting tomoyo_memory_used. */ +static DEFINE_SPINLOCK(tomoyo_policy_memory_lock); /* Memoy currently used by policy/audit log/query. */ unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT]; /* Memory quota for "policy"/"audit log"/"query". */ @@ -40,19 +42,22 @@ unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT]; * Returns true on success, false otherwise. * * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. - * - * Caller holds tomoyo_policy_lock mutex. */ bool tomoyo_memory_ok(void *ptr) { if (ptr) { const size_t s = ksize(ptr); + bool result; + spin_lock(&tomoyo_policy_memory_lock); tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s; - if (!tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] || - tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <= - tomoyo_memory_quota[TOMOYO_MEMORY_POLICY]) + result = !tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] || + tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <= + tomoyo_memory_quota[TOMOYO_MEMORY_POLICY]; + if (!result) + tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s; + spin_unlock(&tomoyo_policy_memory_lock); + if (result) return true; - tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s; } tomoyo_warn_oom(__func__); return false; @@ -66,8 +71,6 @@ bool tomoyo_memory_ok(void *ptr) * * Returns pointer to allocated memory on success, NULL otherwise. * @data is zero-cleared on success. - * - * Caller holds tomoyo_policy_lock mutex. */ void *tomoyo_commit_ok(void *data, const unsigned int size) { @@ -81,6 +84,20 @@ void *tomoyo_commit_ok(void *data, const unsigned int size) return NULL; } +/** + * tomoyo_memory_free - Free memory for elements. + * + * @ptr: Pointer to allocated memory. + */ +void tomoyo_memory_free(void *ptr) +{ + size_t s = ksize(ptr); + spin_lock(&tomoyo_policy_memory_lock); + tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s; + spin_unlock(&tomoyo_policy_memory_lock); + kfree(ptr); +} + /** * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group". * @@ -106,8 +123,7 @@ struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param, goto out; list = ¶m->ns->group_list[idx]; list_for_each_entry(group, list, head.list) { - if (e.group_name != group->group_name || - atomic_read(&group->head.users) == TOMOYO_GC_IN_PROGRESS) + if (e.group_name != group->group_name) continue; atomic_inc(&group->head.users); found = true; @@ -159,8 +175,7 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name) if (mutex_lock_interruptible(&tomoyo_policy_lock)) return NULL; list_for_each_entry(ptr, head, head.list) { - if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) || - atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS) + if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) continue; atomic_inc(&ptr->head.users); goto out; diff --git a/trunk/security/tomoyo/securityfs_if.c b/trunk/security/tomoyo/securityfs_if.c index 2672ac4f3beb..d08296a4882b 100644 --- a/trunk/security/tomoyo/securityfs_if.c +++ b/trunk/security/tomoyo/securityfs_if.c @@ -265,7 +265,6 @@ static int __init tomoyo_initerface_init(void) TOMOYO_VERSION); securityfs_create_file("self_domain", 0666, tomoyo_dir, NULL, &tomoyo_self_operations); - tomoyo_load_builtin_policy(); return 0; }