From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758817AbZFRQFi (ORCPT ); Thu, 18 Jun 2009 12:05:38 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1754633AbZFRQF1 (ORCPT ); Thu, 18 Jun 2009 12:05:27 -0400 Received: from e8.ny.us.ibm.com ([32.97.182.138]:34090 "EHLO e8.ny.us.ibm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754359AbZFRQFW (ORCPT ); Thu, 18 Jun 2009 12:05:22 -0400 Date: Thu, 18 Jun 2009 09:05:21 -0700 From: "Paul E. McKenney" To: Tetsuo Handa Cc: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Subject: Re: [PATCH 3/3] TOMOYO: Add SRCU based garbage collector. Message-ID: <20090618160521.GC6794@linux.vnet.ibm.com> Reply-To: paulmck@linux.vnet.ibm.com References: <200906172019.EAE00032.OLOJFQVMtFOFSH@I-love.SAKURA.ne.jp> <20090617163122.GC6767@linux.vnet.ibm.com> <200906180534.n5I5YgMY080614@www262.sakura.ne.jp> <200906180645.n5I6jxQn096226@www262.sakura.ne.jp> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <200906180645.n5I6jxQn096226@www262.sakura.ne.jp> User-Agent: Mutt/1.5.15+20070412 (2007-04-11) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Thu, Jun 18, 2009 at 03:45:59PM +0900, Tetsuo Handa wrote: > Tetsuo Handa wrote: > > I have one worry regarding SRCU. > > Inside synchronize_srcu(), there is a loop > > > > while (srcu_readers_active_idx(sp, idx)) > > schedule_timeout_interruptible(1); > > > > but the reader's sleeping duration varies from less than one second to > > more than hours. > > > > Checking for counters for every jiffies sounds too much waste of CPU. > > Delaying kfree() for seconds or minutes won't cause troubles for TOMOYO. > > It would be nice if checking interval is configurable like > > "schedule_timeout_interruptible(sp->timeout);". > > > Well, GC thread's schedule_timeout_interruptible(1); loop does not > appear on /usr/bin/top , thus I don't need to worry about checking interval. OK, that does make things easier. ;-) I won't bother with set_srcu_timeout(), then. > OK. Here is SRCU version. > ------------------------------ > Subject: [PATCH 3/3] TOMOYO: Add SRCU based garbage collector. > > As of now, TOMOYO cannot release memory used by marked-as-deleted list elements > because TOMOYO does not know how many readers are there. > > This patch adds SRCU based garbage collector. I was able to make it through about the first 16% before reality intruded. Assuming I can trust the various "Caller holds srcu_read_lock(&tomoyo_ss)" comments, that part of the code looks good. Thanx, Paul > Signed-off-by: Tetsuo Handa > --- > security/tomoyo/common.c | 124 ++++++-------- > security/tomoyo/common.h | 180 ++++++++++++++++++++- > security/tomoyo/domain.c | 191 ++++------------------- > security/tomoyo/file.c | 174 ++++++++------------ > security/tomoyo/realpath.c | 373 +++++++++++++++++++++++++++++++++++++++++++-- > security/tomoyo/realpath.h | 5 > security/tomoyo/tomoyo.c | 18 ++ > 7 files changed, 707 insertions(+), 358 deletions(-) > > --- security-testing-2.6.git.orig/security/tomoyo/common.c > +++ security-testing-2.6.git/security/tomoyo/common.c > @@ -12,6 +12,7 @@ > #include > #include > #include > +#include > #include "realpath.h" > #include "common.h" > #include "tomoyo.h" > @@ -340,10 +341,9 @@ bool tomoyo_is_domain_def(const unsigned > * > * @domainname: The domainname to find. > * > - * Caller must call down_read(&tomoyo_domain_list_lock); or > - * down_write(&tomoyo_domain_list_lock); . > - * > * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. > + * > + * Caller holds srcu_read_lock(&tomoyo_ss). > */ > struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) > { > @@ -352,7 +352,7 @@ struct tomoyo_domain_info *tomoyo_find_d > > name.name = domainname; > tomoyo_fill_path_info(&name); > - list_for_each_entry(domain, &tomoyo_domain_list, list) { > + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { > if (!domain->is_deleted && > !tomoyo_pathcmp(&name, domain->domainname)) > return domain; > @@ -788,6 +788,8 @@ bool tomoyo_verbose_mode(const struct to > * @domain: Pointer to "struct tomoyo_domain_info". > * > * Returns true if the domain is not exceeded quota, false otherwise. > + * > + * Caller holds srcu_read_lock(&tomoyo_ss). > */ > bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain) > { > @@ -796,8 +798,7 @@ bool tomoyo_domain_quota_is_ok(struct to > > if (!domain) > return true; > - down_read(&tomoyo_domain_acl_info_list_lock); > - list_for_each_entry(ptr, &domain->acl_info_list, list) { > + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { > if (ptr->type & TOMOYO_ACL_DELETED) > continue; > switch (tomoyo_acl_type2(ptr)) { > @@ -850,7 +851,6 @@ bool tomoyo_domain_quota_is_ok(struct to > break; > } > } > - up_read(&tomoyo_domain_acl_info_list_lock); > if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) > return true; > if (!domain->quota_warned) { > @@ -1029,27 +1029,6 @@ static int tomoyo_read_profile(struct to > } > > /* > - * tomoyo_policy_manager_entry is a structure which is used for holding list of > - * domainnames or programs which are permitted to modify configuration via > - * /sys/kernel/security/tomoyo/ interface. > - * It has following fields. > - * > - * (1) "list" which is linked to tomoyo_policy_manager_list . > - * (2) "manager" is a domainname or a program's pathname. > - * (3) "is_domain" is a bool which is true if "manager" is a domainname, false > - * otherwise. > - * (4) "is_deleted" is a bool which is true if marked as deleted, false > - * otherwise. > - */ > -struct tomoyo_policy_manager_entry { > - struct list_head list; > - /* A path to program or a domainname. */ > - const struct tomoyo_path_info *manager; > - bool is_domain; /* True if manager is a domainname. */ > - bool is_deleted; /* True if this entry is deleted. */ > -}; > - > -/* > * tomoyo_policy_manager_list is used for holding list of domainnames or > * programs which are permitted to modify configuration via > * /sys/kernel/security/tomoyo/ interface. > @@ -1079,8 +1058,7 @@ struct tomoyo_policy_manager_entry { > * > * # cat /sys/kernel/security/tomoyo/manager > */ > -static LIST_HEAD(tomoyo_policy_manager_list); > -static DECLARE_RWSEM(tomoyo_policy_manager_list_lock); > +LIST_HEAD(tomoyo_policy_manager_list); > > /** > * tomoyo_update_manager_entry - Add a manager entry. > @@ -1112,8 +1090,8 @@ static int tomoyo_update_manager_entry(c > return -ENOMEM; > if (!is_delete) > new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); > - down_write(&tomoyo_policy_manager_list_lock); > - list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) { > + mutex_lock(&tomoyo_policy_lock); > + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { > if (ptr->manager != saved_manager) > continue; > ptr->is_deleted = is_delete; > @@ -1124,11 +1102,12 @@ static int tomoyo_update_manager_entry(c > new_entry->manager = saved_manager; > saved_manager = NULL; > new_entry->is_domain = is_domain; > - list_add_tail(&new_entry->list, &tomoyo_policy_manager_list); > + list_add_tail_rcu(&new_entry->list, > + &tomoyo_policy_manager_list); > new_entry = NULL; > error = 0; > } > - up_write(&tomoyo_policy_manager_list_lock); > + mutex_unlock(&tomoyo_policy_lock); > tomoyo_put_name(saved_manager); > kfree(new_entry); > return error; > @@ -1167,9 +1146,8 @@ static int tomoyo_read_manager_policy(st > > if (head->read_eof) > return 0; > - down_read(&tomoyo_policy_manager_list_lock); > - list_for_each_cookie(pos, head->read_var2, > - &tomoyo_policy_manager_list) { > + list_for_each_cookie_rcu(pos, head->read_var2, > + &tomoyo_policy_manager_list) { > struct tomoyo_policy_manager_entry *ptr; > ptr = list_entry(pos, struct tomoyo_policy_manager_entry, > list); > @@ -1179,7 +1157,6 @@ static int tomoyo_read_manager_policy(st > if (!done) > break; > } > - up_read(&tomoyo_policy_manager_list_lock); > head->read_eof = done; > return 0; > } > @@ -1189,6 +1166,8 @@ static int tomoyo_read_manager_policy(st > * > * Returns true if the current process is permitted to modify policy > * via /sys/kernel/security/tomoyo/ interface. > + * > + * Caller holds srcu_read_lock(&tomoyo_ss). > */ > static bool tomoyo_is_policy_manager(void) > { > @@ -1202,29 +1181,25 @@ static bool tomoyo_is_policy_manager(voi > return true; > if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid)) > return false; > - down_read(&tomoyo_policy_manager_list_lock); > - list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) { > + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { > if (!ptr->is_deleted && ptr->is_domain > && !tomoyo_pathcmp(domainname, ptr->manager)) { > found = true; > break; > } > } > - up_read(&tomoyo_policy_manager_list_lock); > if (found) > return true; > exe = tomoyo_get_exe(); > if (!exe) > return false; > - down_read(&tomoyo_policy_manager_list_lock); > - list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) { > + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { > if (!ptr->is_deleted && !ptr->is_domain > && !strcmp(exe, ptr->manager->name)) { > found = true; > break; > } > } > - up_read(&tomoyo_policy_manager_list_lock); > if (!found) { /* Reduce error messages. */ > static pid_t last_pid; > const pid_t pid = current->pid; > @@ -1245,6 +1220,8 @@ static bool tomoyo_is_policy_manager(voi > * @data: String to parse. > * > * Returns true on success, false otherwise. > + * > + * Caller holds srcu_read_lock(&tomoyo_ss). > */ > static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, > const char *data) > @@ -1260,11 +1237,8 @@ static bool tomoyo_is_select_one(struct > domain = tomoyo_real_domain(p); > read_unlock(&tasklist_lock); > } else if (!strncmp(data, "domain=", 7)) { > - if (tomoyo_is_domain_def(data + 7)) { > - down_read(&tomoyo_domain_list_lock); > + if (tomoyo_is_domain_def(data + 7)) > domain = tomoyo_find_domain(data + 7); > - up_read(&tomoyo_domain_list_lock); > - } > } else > return false; > head->write_var1 = domain; > @@ -1278,13 +1252,11 @@ static bool tomoyo_is_select_one(struct > if (domain) { > struct tomoyo_domain_info *d; > head->read_var1 = NULL; > - down_read(&tomoyo_domain_list_lock); > - list_for_each_entry(d, &tomoyo_domain_list, list) { > + list_for_each_entry_rcu(d, &tomoyo_domain_list, list) { > if (d == domain) > break; > head->read_var1 = &d->list; > } > - up_read(&tomoyo_domain_list_lock); > head->read_var2 = NULL; > head->read_bit = 0; > head->read_step = 0; > @@ -1300,6 +1272,8 @@ static bool tomoyo_is_select_one(struct > * @domainname: The name of domain. > * > * Returns 0. > + * > + * Caller holds srcu_read_lock(&tomoyo_ss). > */ > static int tomoyo_delete_domain(char *domainname) > { > @@ -1308,9 +1282,9 @@ static int tomoyo_delete_domain(char *do > > name.name = domainname; > tomoyo_fill_path_info(&name); > - down_write(&tomoyo_domain_list_lock); > + mutex_lock(&tomoyo_policy_lock); > /* Is there an active domain? */ > - list_for_each_entry(domain, &tomoyo_domain_list, list) { > + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { > /* Never delete tomoyo_kernel_domain */ > if (domain == &tomoyo_kernel_domain) > continue; > @@ -1320,7 +1294,7 @@ static int tomoyo_delete_domain(char *do > domain->is_deleted = true; > break; > } > - up_write(&tomoyo_domain_list_lock); > + mutex_unlock(&tomoyo_policy_lock); > return 0; > } > > @@ -1330,6 +1304,8 @@ static int tomoyo_delete_domain(char *do > * @head: Pointer to "struct tomoyo_io_buffer". > * > * Returns 0 on success, negative value otherwise. > + * > + * Caller holds srcu_read_lock(&tomoyo_ss). > */ > static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) > { > @@ -1352,11 +1328,9 @@ static int tomoyo_write_domain_policy(st > domain = NULL; > if (is_delete) > tomoyo_delete_domain(data); > - else if (is_select) { > - down_read(&tomoyo_domain_list_lock); > + else if (is_select) > domain = tomoyo_find_domain(data); > - up_read(&tomoyo_domain_list_lock); > - } else > + else > domain = tomoyo_find_or_assign_new_domain(data, 0); > head->write_var1 = domain; > return 0; > @@ -1511,8 +1485,7 @@ static int tomoyo_read_domain_policy(str > return 0; > if (head->read_step == 0) > head->read_step = 1; > - down_read(&tomoyo_domain_list_lock); > - list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) { > + list_for_each_cookie_rcu(dpos, head->read_var1, &tomoyo_domain_list) { > struct tomoyo_domain_info *domain; > const char *quota_exceeded = ""; > const char *transition_failed = ""; > @@ -1543,9 +1516,8 @@ acl_loop: > if (head->read_step == 3) > goto tail_mark; > /* Print ACL entries in the domain. */ > - down_read(&tomoyo_domain_acl_info_list_lock); > - list_for_each_cookie(apos, head->read_var2, > - &domain->acl_info_list) { > + list_for_each_cookie_rcu(apos, head->read_var2, > + &domain->acl_info_list) { > struct tomoyo_acl_info *ptr > = list_entry(apos, struct tomoyo_acl_info, > list); > @@ -1553,7 +1525,6 @@ acl_loop: > if (!done) > break; > } > - up_read(&tomoyo_domain_acl_info_list_lock); > if (!done) > break; > head->read_step = 3; > @@ -1565,7 +1536,6 @@ tail_mark: > if (head->read_single_domain) > break; > } > - up_read(&tomoyo_domain_list_lock); > head->read_eof = done; > return 0; > } > @@ -1581,6 +1551,8 @@ tail_mark: > * > * ( echo "select " $domainname; echo "use_profile " $profile ) | > * /usr/lib/ccs/loadpolicy -d > + * > + * Caller holds srcu_read_lock(&tomoyo_ss). > */ > static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) > { > @@ -1592,9 +1564,7 @@ static int tomoyo_write_domain_profile(s > if (!cp) > return -EINVAL; > *cp = '\0'; > - down_read(&tomoyo_domain_list_lock); > domain = tomoyo_find_domain(cp + 1); > - up_read(&tomoyo_domain_list_lock); > if (strict_strtoul(data, 10, &profile)) > return -EINVAL; > if (domain && profile < TOMOYO_MAX_PROFILES > @@ -1624,8 +1594,7 @@ static int tomoyo_read_domain_profile(st > > if (head->read_eof) > return 0; > - down_read(&tomoyo_domain_list_lock); > - list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) { > + list_for_each_cookie_rcu(pos, head->read_var1, &tomoyo_domain_list) { > struct tomoyo_domain_info *domain; > domain = list_entry(pos, struct tomoyo_domain_info, list); > if (domain->is_deleted) > @@ -1635,7 +1604,6 @@ static int tomoyo_read_domain_profile(st > if (!done) > break; > } > - up_read(&tomoyo_domain_list_lock); > head->read_eof = done; > return 0; > } > @@ -1854,16 +1822,24 @@ void tomoyo_load_policy(const char *file > printk(KERN_INFO "Mandatory Access Control activated.\n"); > tomoyo_policy_loaded = true; > { /* Check all profiles currently assigned to domains are defined. */ > + const int idx = srcu_read_lock(&tomoyo_ss); > struct tomoyo_domain_info *domain; > - down_read(&tomoyo_domain_list_lock); > - list_for_each_entry(domain, &tomoyo_domain_list, list) { > + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { > const u8 profile = domain->profile; > if (tomoyo_profile_ptr[profile]) > continue; > panic("Profile %u (used by '%s') not defined.\n", > profile, domain->domainname->name); > } > - up_read(&tomoyo_domain_list_lock); > + srcu_read_unlock(&tomoyo_ss, idx); > + } > + { > + struct task_struct *task = > + kthread_create(tomoyo_gc_thread, NULL, "GC for TOMOYO"); > + if (IS_ERR(task)) > + printk(KERN_ERR "GC thread not available.\n"); > + else > + wake_up_process(task); > } > } > > @@ -1997,6 +1973,7 @@ static int tomoyo_open_control(const u8 > } > } > file->private_data = head; > + head->tomoyo_srcu_index = srcu_read_lock(&tomoyo_ss); > /* > * Call the handler now if the file is > * /sys/kernel/security/tomoyo/self_domain > @@ -2114,6 +2091,7 @@ static int tomoyo_write_control(struct f > static int tomoyo_close_control(struct file *file) > { > struct tomoyo_io_buffer *head = file->private_data; > + srcu_read_unlock(&tomoyo_ss, head->tomoyo_srcu_index); > > /* Release memory used for policy I/O. */ > tomoyo_free(head->read_buf); > --- security-testing-2.6.git.orig/security/tomoyo/common.h > +++ security-testing-2.6.git/security/tomoyo/common.h > @@ -156,6 +156,7 @@ struct tomoyo_domain_info { > struct list_head acl_info_list; > /* Name of this domain. Never NULL. */ > const struct tomoyo_path_info *domainname; > + atomic_t users; > u8 profile; /* Profile number to use. */ > bool is_deleted; /* Delete flag. */ > bool quota_warned; /* Quota warnning flag. */ > @@ -266,6 +267,8 @@ struct tomoyo_io_buffer { > int (*write) (struct tomoyo_io_buffer *); > /* Exclusive lock for this structure. */ > struct mutex io_sem; > + /* counter which this structure locked. */ > + int tomoyo_srcu_index; > /* The position currently reading from. */ > struct list_head *read_var1; > /* Extra variables for reading. */ > @@ -421,10 +424,9 @@ static inline bool tomoyo_is_invalid(con > > /* The list for "struct tomoyo_domain_info". */ > extern struct list_head tomoyo_domain_list; > -extern struct rw_semaphore tomoyo_domain_list_lock; > > -/* Lock for domain->acl_info_list. */ > -extern struct rw_semaphore tomoyo_domain_acl_info_list_lock; > +/* Lock for modifying policy. */ > +extern struct mutex tomoyo_policy_lock; > > /* Has /sbin/init started? */ > extern bool tomoyo_policy_loaded; > @@ -433,21 +435,181 @@ extern bool tomoyo_policy_loaded; > extern struct tomoyo_domain_info tomoyo_kernel_domain; > > /** > - * list_for_each_cookie - iterate over a list with cookie. > + * list_for_each_cookie_rcu - iterate over a list with cookie. > * @pos: the &struct list_head to use as a loop cursor. > * @cookie: the &struct list_head to use as a cookie. > * @head: the head for your list. > * > - * Same with list_for_each() except that this primitive uses @cookie > + * Same with __list_for_each_rcu() except that this primitive uses @cookie > * so that we can continue iteration. > * @cookie must be NULL when iteration starts, and @cookie will become > * NULL when iteration finishes. > */ > -#define list_for_each_cookie(pos, cookie, head) \ > +#define list_for_each_cookie_rcu(pos, cookie, head) \ > for (({ if (!cookie) \ > - cookie = head; }), \ > - pos = (cookie)->next; \ > + cookie = head; }), \ > + pos = rcu_dereference((cookie)->next); \ > prefetch(pos->next), pos != (head) || ((cookie) = NULL); \ > - (cookie) = pos, pos = pos->next) > + (cookie) = pos, pos = rcu_dereference(pos->next)) > + > +/* SRCU structure for GC */ > +extern struct srcu_struct tomoyo_ss; > + > +/* > + * tomoyo_policy_manager_entry is a structure which is used for holding list of > + * domainnames or programs which are permitted to modify configuration via > + * /sys/kernel/security/tomoyo/ interface. > + * It has following fields. > + * > + * (1) "list" which is linked to tomoyo_policy_manager_list . > + * (2) "manager" is a domainname or a program's pathname. > + * (3) "is_domain" is a bool which is true if "manager" is a domainname, false > + * otherwise. > + * (4) "is_deleted" is a bool which is true if marked as deleted, false > + * otherwise. > + */ > +struct tomoyo_policy_manager_entry { > + struct list_head list; > + /* A path to program or a domainname. */ > + const struct tomoyo_path_info *manager; > + bool is_domain; /* True if manager is a domainname. */ > + bool is_deleted; /* True if this entry is deleted. */ > +}; > + > +extern struct list_head tomoyo_policy_manager_list; > + > +/* > + * tomoyo_globally_readable_file_entry is a structure which is used for holding > + * "allow_read" entries. > + * It has following fields. > + * > + * (1) "list" which is linked to tomoyo_globally_readable_list . > + * (2) "filename" is a pathname which is allowed to open(O_RDONLY). > + * (3) "is_deleted" is a bool which is true if marked as deleted, false > + * otherwise. > + */ > +struct tomoyo_globally_readable_file_entry { > + struct list_head list; > + const struct tomoyo_path_info *filename; > + bool is_deleted; > +}; > + > +extern struct list_head tomoyo_globally_readable_list; > + > +/* > + * tomoyo_pattern_entry is a structure which is used for holding > + * "tomoyo_pattern_list" entries. > + * It has following fields. > + * > + * (1) "list" which is linked to tomoyo_pattern_list . > + * (2) "pattern" is a pathname pattern which is used for converting pathnames > + * to pathname patterns during learning mode. > + * (3) "is_deleted" is a bool which is true if marked as deleted, false > + * otherwise. > + */ > +struct tomoyo_pattern_entry { > + struct list_head list; > + const struct tomoyo_path_info *pattern; > + bool is_deleted; > +}; > + > +extern struct list_head tomoyo_pattern_list; > + > +/* > + * tomoyo_no_rewrite_entry is a structure which is used for holding > + * "deny_rewrite" entries. > + * It has following fields. > + * > + * (1) "list" which is linked to tomoyo_no_rewrite_list . > + * (2) "pattern" is a pathname which is by default not permitted to modify > + * already existing content. > + * (3) "is_deleted" is a bool which is true if marked as deleted, false > + * otherwise. > + */ > +struct tomoyo_no_rewrite_entry { > + struct list_head list; > + const struct tomoyo_path_info *pattern; > + bool is_deleted; > +}; > + > +extern struct list_head tomoyo_no_rewrite_list; > + > +/* > + * tomoyo_domain_initializer_entry is a structure which is used for holding > + * "initialize_domain" and "no_initialize_domain" entries. > + * It has following fields. > + * > + * (1) "list" which is linked to tomoyo_domain_initializer_list . > + * (2) "domainname" which is "a domainname" or "the last component of a > + * domainname". This field is NULL if "from" clause is not specified. > + * (3) "program" which is a program's pathname. > + * (4) "is_deleted" is a bool which is true if marked as deleted, false > + * otherwise. > + * (5) "is_not" is a bool which is true if "no_initialize_domain", false > + * otherwise. > + * (6) "is_last_name" is a bool which is true if "domainname" is "the last > + * component of a domainname", false otherwise. > + */ > +struct tomoyo_domain_initializer_entry { > + struct list_head list; > + const struct tomoyo_path_info *domainname; /* This may be NULL */ > + const struct tomoyo_path_info *program; > + bool is_deleted; > + bool is_not; /* True if this entry is "no_initialize_domain". */ > + /* True if the domainname is tomoyo_get_last_name(). */ > + bool is_last_name; > +}; > + > +extern struct list_head tomoyo_domain_initializer_list; > + > +/* > + * tomoyo_domain_keeper_entry is a structure which is used for holding > + * "keep_domain" and "no_keep_domain" entries. > + * It has following fields. > + * > + * (1) "list" which is linked to tomoyo_domain_keeper_list . > + * (2) "domainname" which is "a domainname" or "the last component of a > + * domainname". > + * (3) "program" which is a program's pathname. > + * This field is NULL if "from" clause is not specified. > + * (4) "is_deleted" is a bool which is true if marked as deleted, false > + * otherwise. > + * (5) "is_not" is a bool which is true if "no_initialize_domain", false > + * otherwise. > + * (6) "is_last_name" is a bool which is true if "domainname" is "the last > + * component of a domainname", false otherwise. > + */ > +struct tomoyo_domain_keeper_entry { > + struct list_head list; > + const struct tomoyo_path_info *domainname; > + const struct tomoyo_path_info *program; /* This may be NULL */ > + bool is_deleted; > + bool is_not; /* True if this entry is "no_keep_domain". */ > + /* True if the domainname is tomoyo_get_last_name(). */ > + bool is_last_name; > +}; > + > +extern struct list_head tomoyo_domain_keeper_list; > + > +/* > + * tomoyo_alias_entry is a structure which is used for holding "alias" entries. > + * It has following fields. > + * > + * (1) "list" which is linked to tomoyo_alias_list . > + * (2) "original_name" which is a dereferenced pathname. > + * (3) "aliased_name" which is a symlink's pathname. > + * (4) "is_deleted" is a bool which is true if marked as deleted, false > + * otherwise. > + */ > +struct tomoyo_alias_entry { > + struct list_head list; > + const struct tomoyo_path_info *original_name; > + const struct tomoyo_path_info *aliased_name; > + bool is_deleted; > +}; > + > +extern struct list_head tomoyo_alias_list; > + > +int tomoyo_gc_thread(void *unused); > > #endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */ > --- security-testing-2.6.git.orig/security/tomoyo/domain.c > +++ security-testing-2.6.git/security/tomoyo/domain.c > @@ -58,77 +58,6 @@ struct tomoyo_domain_info tomoyo_kernel_ > * exceptions. > */ > LIST_HEAD(tomoyo_domain_list); > -DECLARE_RWSEM(tomoyo_domain_list_lock); > - > -/* > - * tomoyo_domain_initializer_entry is a structure which is used for holding > - * "initialize_domain" and "no_initialize_domain" entries. > - * It has following fields. > - * > - * (1) "list" which is linked to tomoyo_domain_initializer_list . > - * (2) "domainname" which is "a domainname" or "the last component of a > - * domainname". This field is NULL if "from" clause is not specified. > - * (3) "program" which is a program's pathname. > - * (4) "is_deleted" is a bool which is true if marked as deleted, false > - * otherwise. > - * (5) "is_not" is a bool which is true if "no_initialize_domain", false > - * otherwise. > - * (6) "is_last_name" is a bool which is true if "domainname" is "the last > - * component of a domainname", false otherwise. > - */ > -struct tomoyo_domain_initializer_entry { > - struct list_head list; > - const struct tomoyo_path_info *domainname; /* This may be NULL */ > - const struct tomoyo_path_info *program; > - bool is_deleted; > - bool is_not; /* True if this entry is "no_initialize_domain". */ > - /* True if the domainname is tomoyo_get_last_name(). */ > - bool is_last_name; > -}; > - > -/* > - * tomoyo_domain_keeper_entry is a structure which is used for holding > - * "keep_domain" and "no_keep_domain" entries. > - * It has following fields. > - * > - * (1) "list" which is linked to tomoyo_domain_keeper_list . > - * (2) "domainname" which is "a domainname" or "the last component of a > - * domainname". > - * (3) "program" which is a program's pathname. > - * This field is NULL if "from" clause is not specified. > - * (4) "is_deleted" is a bool which is true if marked as deleted, false > - * otherwise. > - * (5) "is_not" is a bool which is true if "no_initialize_domain", false > - * otherwise. > - * (6) "is_last_name" is a bool which is true if "domainname" is "the last > - * component of a domainname", false otherwise. > - */ > -struct tomoyo_domain_keeper_entry { > - struct list_head list; > - const struct tomoyo_path_info *domainname; > - const struct tomoyo_path_info *program; /* This may be NULL */ > - bool is_deleted; > - bool is_not; /* True if this entry is "no_keep_domain". */ > - /* True if the domainname is tomoyo_get_last_name(). */ > - bool is_last_name; > -}; > - > -/* > - * tomoyo_alias_entry is a structure which is used for holding "alias" entries. > - * It has following fields. > - * > - * (1) "list" which is linked to tomoyo_alias_list . > - * (2) "original_name" which is a dereferenced pathname. > - * (3) "aliased_name" which is a symlink's pathname. > - * (4) "is_deleted" is a bool which is true if marked as deleted, false > - * otherwise. > - */ > -struct tomoyo_alias_entry { > - struct list_head list; > - const struct tomoyo_path_info *original_name; > - const struct tomoyo_path_info *aliased_name; > - bool is_deleted; > -}; > > /** > * tomoyo_get_last_name - Get last component of a domainname. > @@ -183,8 +112,7 @@ const char *tomoyo_get_last_name(const s > * will cause "/usr/sbin/httpd" to belong to " /usr/sbin/httpd" domain > * unless executed from " /etc/rc.d/init.d/httpd" domain. > */ > -static LIST_HEAD(tomoyo_domain_initializer_list); > -static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock); > +LIST_HEAD(tomoyo_domain_initializer_list); > > /** > * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list. > @@ -227,8 +155,8 @@ static int tomoyo_update_domain_initiali > } > if (!is_delete) > new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); > - down_write(&tomoyo_domain_initializer_list_lock); > - list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) { > + mutex_lock(&tomoyo_policy_lock); > + list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { > if (ptr->is_not != is_not || > ptr->domainname != saved_domainname || > ptr->program != saved_program) > @@ -244,12 +172,12 @@ static int tomoyo_update_domain_initiali > saved_program = NULL; > new_entry->is_not = is_not; > new_entry->is_last_name = is_last_name; > - list_add_tail(&new_entry->list, > - &tomoyo_domain_initializer_list); > + list_add_tail_rcu(&new_entry->list, > + &tomoyo_domain_initializer_list); > new_entry = NULL; > error = 0; > } > - up_write(&tomoyo_domain_initializer_list_lock); > + mutex_unlock(&tomoyo_policy_lock); > tomoyo_put_name(saved_domainname); > tomoyo_put_name(saved_program); > kfree(new_entry); > @@ -268,15 +196,14 @@ bool tomoyo_read_domain_initializer_poli > struct list_head *pos; > bool done = true; > > - down_read(&tomoyo_domain_initializer_list_lock); > - list_for_each_cookie(pos, head->read_var2, > - &tomoyo_domain_initializer_list) { > + list_for_each_cookie_rcu(pos, head->read_var2, > + &tomoyo_domain_initializer_list) { > const char *no; > const char *from = ""; > const char *domain = ""; > struct tomoyo_domain_initializer_entry *ptr; > ptr = list_entry(pos, struct tomoyo_domain_initializer_entry, > - list); > + list); > if (ptr->is_deleted) > continue; > no = ptr->is_not ? "no_" : ""; > @@ -291,7 +218,6 @@ bool tomoyo_read_domain_initializer_poli > if (!done) > break; > } > - up_read(&tomoyo_domain_initializer_list_lock); > return done; > } > > @@ -328,6 +254,8 @@ int tomoyo_write_domain_initializer_poli > * > * Returns true if executing @program reinitializes domain transition, > * false otherwise. > + * > + * Caller holds srcu_read_lock(&tomoyo_ss). > */ > static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info * > domainname, > @@ -338,8 +266,7 @@ static bool tomoyo_is_domain_initializer > struct tomoyo_domain_initializer_entry *ptr; > bool flag = false; > > - down_read(&tomoyo_domain_initializer_list_lock); > - list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) { > + list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { > if (ptr->is_deleted) > continue; > if (ptr->domainname) { > @@ -359,7 +286,6 @@ static bool tomoyo_is_domain_initializer > } > flag = true; > } > - up_read(&tomoyo_domain_initializer_list_lock); > return flag; > } > > @@ -401,8 +327,7 @@ static bool tomoyo_is_domain_initializer > * " /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless > * explicitly specified by "initialize_domain". > */ > -static LIST_HEAD(tomoyo_domain_keeper_list); > -static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock); > +LIST_HEAD(tomoyo_domain_keeper_list); > > /** > * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list. > @@ -445,8 +370,8 @@ static int tomoyo_update_domain_keeper_e > } > if (!is_delete) > new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); > - down_write(&tomoyo_domain_keeper_list_lock); > - list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) { > + mutex_lock(&tomoyo_policy_lock); > + list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { > if (ptr->is_not != is_not || > ptr->domainname != saved_domainname || > ptr->program != saved_program) > @@ -462,11 +387,12 @@ static int tomoyo_update_domain_keeper_e > saved_program = NULL; > new_entry->is_not = is_not; > new_entry->is_last_name = is_last_name; > - list_add_tail(&new_entry->list, &tomoyo_domain_keeper_list); > + list_add_tail_rcu(&new_entry->list, > + &tomoyo_domain_keeper_list); > new_entry = NULL; > error = 0; > } > - up_write(&tomoyo_domain_keeper_list_lock); > + mutex_unlock(&tomoyo_policy_lock); > tomoyo_put_name(saved_domainname); > tomoyo_put_name(saved_program); > kfree(new_entry); > @@ -506,9 +432,8 @@ bool tomoyo_read_domain_keeper_policy(st > struct list_head *pos; > bool done = true; > > - down_read(&tomoyo_domain_keeper_list_lock); > - list_for_each_cookie(pos, head->read_var2, > - &tomoyo_domain_keeper_list) { > + list_for_each_cookie_rcu(pos, head->read_var2, > + &tomoyo_domain_keeper_list) { > struct tomoyo_domain_keeper_entry *ptr; > const char *no; > const char *from = ""; > @@ -529,7 +454,6 @@ bool tomoyo_read_domain_keeper_policy(st > if (!done) > break; > } > - up_read(&tomoyo_domain_keeper_list_lock); > return done; > } > > @@ -542,6 +466,8 @@ bool tomoyo_read_domain_keeper_policy(st > * > * Returns true if executing @program supresses domain transition, > * false otherwise. > + * > + * Caller holds srcu_read_lock(&tomoyo_ss). > */ > static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname, > const struct tomoyo_path_info *program, > @@ -550,8 +476,7 @@ static bool tomoyo_is_domain_keeper(cons > struct tomoyo_domain_keeper_entry *ptr; > bool flag = false; > > - down_read(&tomoyo_domain_keeper_list_lock); > - list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) { > + list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { > if (ptr->is_deleted) > continue; > if (!ptr->is_last_name) { > @@ -569,7 +494,6 @@ static bool tomoyo_is_domain_keeper(cons > } > flag = true; > } > - up_read(&tomoyo_domain_keeper_list_lock); > return flag; > } > > @@ -603,8 +527,7 @@ static bool tomoyo_is_domain_keeper(cons > * /bin/busybox and domainname which the current process will belong to after > * execve() succeeds is calculated using /bin/cat rather than /bin/busybox . > */ > -static LIST_HEAD(tomoyo_alias_list); > -static DECLARE_RWSEM(tomoyo_alias_list_lock); > +LIST_HEAD(tomoyo_alias_list); > > /** > * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list. > @@ -637,8 +560,8 @@ static int tomoyo_update_alias_entry(con > } > if (!is_delete) > new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); > - down_write(&tomoyo_alias_list_lock); > - list_for_each_entry(ptr, &tomoyo_alias_list, list) { > + mutex_lock(&tomoyo_policy_lock); > + list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { > if (ptr->original_name != saved_original_name || > ptr->aliased_name != saved_aliased_name) > continue; > @@ -651,11 +574,11 @@ static int tomoyo_update_alias_entry(con > saved_original_name = NULL; > new_entry->aliased_name = saved_aliased_name; > saved_aliased_name = NULL; > - list_add_tail(&new_entry->list, &tomoyo_alias_list); > + list_add_tail_rcu(&new_entry->list, &tomoyo_alias_list); > new_entry = NULL; > error = 0; > } > - up_write(&tomoyo_alias_list_lock); > + mutex_unlock(&tomoyo_policy_lock); > tomoyo_put_name(saved_original_name); > tomoyo_put_name(saved_aliased_name); > kfree(new_entry); > @@ -674,8 +597,7 @@ bool tomoyo_read_alias_policy(struct tom > struct list_head *pos; > bool done = true; > > - down_read(&tomoyo_alias_list_lock); > - list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) { > + list_for_each_cookie_rcu(pos, head->read_var2, &tomoyo_alias_list) { > struct tomoyo_alias_entry *ptr; > > ptr = list_entry(pos, struct tomoyo_alias_entry, list); > @@ -687,7 +609,6 @@ bool tomoyo_read_alias_policy(struct tom > if (!done) > break; > } > - up_read(&tomoyo_alias_list_lock); > return done; > } > > @@ -731,52 +652,18 @@ struct tomoyo_domain_info *tomoyo_find_o > if (!saved_domainname) > return NULL; > new_domain = kmalloc(sizeof(*new_domain), GFP_KERNEL); > - down_write(&tomoyo_domain_list_lock); > + mutex_lock(&tomoyo_policy_lock); > domain = tomoyo_find_domain(domainname); > - if (domain) > - goto out; > - /* Can I reuse memory of deleted domain? */ > - list_for_each_entry(domain, &tomoyo_domain_list, list) { > - struct task_struct *p; > - struct tomoyo_acl_info *ptr; > - bool flag; > - if (!domain->is_deleted || > - domain->domainname != saved_domainname) > - continue; > - flag = false; > - read_lock(&tasklist_lock); > - for_each_process(p) { > - if (tomoyo_real_domain(p) != domain) > - continue; > - flag = true; > - break; > - } > - read_unlock(&tasklist_lock); > - if (flag) > - continue; > - list_for_each_entry(ptr, &domain->acl_info_list, list) { > - ptr->type |= TOMOYO_ACL_DELETED; > - } > - domain->ignore_global_allow_read = false; > - domain->domain_transition_failed = false; > - domain->profile = profile; > - domain->quota_warned = false; > - mb(); /* Avoid out-of-order execution. */ > - domain->is_deleted = false; > - goto out; > - } > - /* No memory reusable. Create using new memory. */ > - if (tomoyo_memory_ok(new_domain)) { > + if (!domain && tomoyo_memory_ok(new_domain)) { > domain = new_domain; > new_domain = NULL; > INIT_LIST_HEAD(&domain->acl_info_list); > domain->domainname = saved_domainname; > saved_domainname = NULL; > domain->profile = profile; > - list_add_tail(&domain->list, &tomoyo_domain_list); > + list_add_tail_rcu(&domain->list, &tomoyo_domain_list); > } > - out: > - up_write(&tomoyo_domain_list_lock); > + mutex_unlock(&tomoyo_policy_lock); > tomoyo_put_name(saved_domainname); > kfree(new_domain); > return domain; > @@ -788,6 +675,8 @@ struct tomoyo_domain_info *tomoyo_find_o > * @bprm: Pointer to "struct linux_binprm". > * > * Returns 0 on success, negative value otherwise. > + * > + * Caller holds srcu_read_lock(&tomoyo_ss). > */ > int tomoyo_find_next_domain(struct linux_binprm *bprm) > { > @@ -810,6 +699,7 @@ int tomoyo_find_next_domain(struct linux > struct tomoyo_path_info s; /* symlink name */ > struct tomoyo_path_info l; /* last name */ > static bool initialized; > + const int idx = srcu_read_lock(&tomoyo_ss); > > if (!tmp) > goto out; > @@ -848,8 +738,7 @@ int tomoyo_find_next_domain(struct linux > if (tomoyo_pathcmp(&r, &s)) { > struct tomoyo_alias_entry *ptr; > /* Is this program allowed to be called via symbolic links? */ > - down_read(&tomoyo_alias_list_lock); > - list_for_each_entry(ptr, &tomoyo_alias_list, list) { > + list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { > if (ptr->is_deleted || > tomoyo_pathcmp(&r, ptr->original_name) || > tomoyo_pathcmp(&s, ptr->aliased_name)) > @@ -860,7 +749,6 @@ int tomoyo_find_next_domain(struct linux > tomoyo_fill_path_info(&r); > break; > } > - up_read(&tomoyo_alias_list_lock); > } > > /* Check execute permission. */ > @@ -891,9 +779,7 @@ int tomoyo_find_next_domain(struct linux > } > if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN) > goto done; > - down_read(&tomoyo_domain_list_lock); > domain = tomoyo_find_domain(new_domain_name); > - up_read(&tomoyo_domain_list_lock); > if (domain) > goto done; > if (is_enforce) > @@ -910,9 +796,12 @@ int tomoyo_find_next_domain(struct linux > else > old_domain->domain_transition_failed = true; > out: > + BUG_ON(bprm->cred->security); > if (!domain) > domain = old_domain; > + atomic_inc(&domain->users); > bprm->cred->security = domain; > + srcu_read_unlock(&tomoyo_ss, idx); > tomoyo_free(real_program_name); > tomoyo_free(symlink_program_name); > tomoyo_free(tmp); > --- security-testing-2.6.git.orig/security/tomoyo/file.c > +++ security-testing-2.6.git/security/tomoyo/file.c > @@ -14,56 +14,6 @@ > #include "realpath.h" > #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) > > -/* > - * tomoyo_globally_readable_file_entry is a structure which is used for holding > - * "allow_read" entries. > - * It has following fields. > - * > - * (1) "list" which is linked to tomoyo_globally_readable_list . > - * (2) "filename" is a pathname which is allowed to open(O_RDONLY). > - * (3) "is_deleted" is a bool which is true if marked as deleted, false > - * otherwise. > - */ > -struct tomoyo_globally_readable_file_entry { > - struct list_head list; > - const struct tomoyo_path_info *filename; > - bool is_deleted; > -}; > - > -/* > - * tomoyo_pattern_entry is a structure which is used for holding > - * "tomoyo_pattern_list" entries. > - * It has following fields. > - * > - * (1) "list" which is linked to tomoyo_pattern_list . > - * (2) "pattern" is a pathname pattern which is used for converting pathnames > - * to pathname patterns during learning mode. > - * (3) "is_deleted" is a bool which is true if marked as deleted, false > - * otherwise. > - */ > -struct tomoyo_pattern_entry { > - struct list_head list; > - const struct tomoyo_path_info *pattern; > - bool is_deleted; > -}; > - > -/* > - * tomoyo_no_rewrite_entry is a structure which is used for holding > - * "deny_rewrite" entries. > - * It has following fields. > - * > - * (1) "list" which is linked to tomoyo_no_rewrite_list . > - * (2) "pattern" is a pathname which is by default not permitted to modify > - * already existing content. > - * (3) "is_deleted" is a bool which is true if marked as deleted, false > - * otherwise. > - */ > -struct tomoyo_no_rewrite_entry { > - struct list_head list; > - const struct tomoyo_path_info *pattern; > - bool is_deleted; > -}; > - > /* Keyword array for single path operations. */ > static const char *tomoyo_sp_keyword[TOMOYO_MAX_SINGLE_PATH_OPERATION] = { > [TOMOYO_TYPE_READ_WRITE_ACL] = "read/write", > @@ -159,8 +109,8 @@ static struct tomoyo_path_info *tomoyo_g > return NULL; > } > > -/* Lock for domain->acl_info_list. */ > -DECLARE_RWSEM(tomoyo_domain_acl_info_list_lock); > +/* Lock for modifying TOMOYO's policy. */ > +DEFINE_MUTEX(tomoyo_policy_lock); > > static int tomoyo_update_double_path_acl(const u8 type, const char *filename1, > const char *filename2, > @@ -195,8 +145,7 @@ static int tomoyo_update_single_path_acl > * given "allow_read /lib/libc-2.5.so" to the domain which current process > * belongs to. > */ > -static LIST_HEAD(tomoyo_globally_readable_list); > -static DECLARE_RWSEM(tomoyo_globally_readable_list_lock); > +LIST_HEAD(tomoyo_globally_readable_list); > > /** > * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list. > @@ -221,8 +170,8 @@ static int tomoyo_update_globally_readab > return -ENOMEM; > if (!is_delete) > new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); > - down_write(&tomoyo_globally_readable_list_lock); > - list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) { > + mutex_lock(&tomoyo_policy_lock); > + list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) { > if (ptr->filename != saved_filename) > continue; > ptr->is_deleted = is_delete; > @@ -232,11 +181,12 @@ static int tomoyo_update_globally_readab > if (!is_delete && error && tomoyo_memory_ok(new_entry)) { > new_entry->filename = saved_filename; > saved_filename = NULL; > - list_add_tail(&new_entry->list, &tomoyo_globally_readable_list); > + list_add_tail_rcu(&new_entry->list, > + &tomoyo_globally_readable_list); > new_entry = NULL; > error = 0; > } > - up_write(&tomoyo_globally_readable_list_lock); > + mutex_unlock(&tomoyo_policy_lock); > tomoyo_put_name(saved_filename); > kfree(new_entry); > return error; > @@ -248,21 +198,21 @@ static int tomoyo_update_globally_readab > * @filename: The filename to check. > * > * Returns true if any domain can open @filename for reading, false otherwise. > + * > + * Caller holds srcu_read_lock(&tomoyo_ss). > */ > static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info * > filename) > { > struct tomoyo_globally_readable_file_entry *ptr; > bool found = false; > - down_read(&tomoyo_globally_readable_list_lock); > - list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) { > + list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) { > if (!ptr->is_deleted && > tomoyo_path_matches_pattern(filename, ptr->filename)) { > found = true; > break; > } > } > - up_read(&tomoyo_globally_readable_list_lock); > return found; > } > > @@ -291,9 +241,8 @@ bool tomoyo_read_globally_readable_polic > struct list_head *pos; > bool done = true; > > - down_read(&tomoyo_globally_readable_list_lock); > - list_for_each_cookie(pos, head->read_var2, > - &tomoyo_globally_readable_list) { > + list_for_each_cookie_rcu(pos, head->read_var2, > + &tomoyo_globally_readable_list) { > struct tomoyo_globally_readable_file_entry *ptr; > ptr = list_entry(pos, > struct tomoyo_globally_readable_file_entry, > @@ -305,7 +254,6 @@ bool tomoyo_read_globally_readable_polic > if (!done) > break; > } > - up_read(&tomoyo_globally_readable_list_lock); > return done; > } > > @@ -338,8 +286,7 @@ bool tomoyo_read_globally_readable_polic > * which pretends as if /proc/self/ is not a symlink; so that we can forbid > * current process from accessing other process's information. > */ > -static LIST_HEAD(tomoyo_pattern_list); > -static DECLARE_RWSEM(tomoyo_pattern_list_lock); > +LIST_HEAD(tomoyo_pattern_list); > > /** > * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list. > @@ -364,8 +311,8 @@ static int tomoyo_update_file_pattern_en > return -ENOMEM; > if (!is_delete) > new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); > - down_write(&tomoyo_pattern_list_lock); > - list_for_each_entry(ptr, &tomoyo_pattern_list, list) { > + mutex_lock(&tomoyo_policy_lock); > + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { > if (saved_pattern != ptr->pattern) > continue; > ptr->is_deleted = is_delete; > @@ -375,11 +322,11 @@ static int tomoyo_update_file_pattern_en > if (!is_delete && error && tomoyo_memory_ok(new_entry)) { > new_entry->pattern = saved_pattern; > saved_pattern = NULL; > - list_add_tail(&new_entry->list, &tomoyo_pattern_list); > + list_add_tail_rcu(&new_entry->list, &tomoyo_pattern_list); > new_entry = NULL; > error = 0; > } > - up_write(&tomoyo_pattern_list_lock); > + mutex_unlock(&tomoyo_policy_lock); > tomoyo_put_name(saved_pattern); > kfree(new_entry); > return error; > @@ -391,6 +338,8 @@ static int tomoyo_update_file_pattern_en > * @filename: The filename to find patterned pathname. > * > * Returns pointer to pathname pattern if matched, @filename otherwise. > + * > + * Caller holds srcu_read_lock(&tomoyo_ss). > */ > static const struct tomoyo_path_info * > tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) > @@ -398,8 +347,7 @@ tomoyo_get_file_pattern(const struct tom > struct tomoyo_pattern_entry *ptr; > const struct tomoyo_path_info *pattern = NULL; > > - down_read(&tomoyo_pattern_list_lock); > - list_for_each_entry(ptr, &tomoyo_pattern_list, list) { > + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { > if (ptr->is_deleted) > continue; > if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) > @@ -412,7 +360,6 @@ tomoyo_get_file_pattern(const struct tom > break; > } > } > - up_read(&tomoyo_pattern_list_lock); > if (pattern) > filename = pattern; > return filename; > @@ -443,8 +390,7 @@ bool tomoyo_read_file_pattern(struct tom > struct list_head *pos; > bool done = true; > > - down_read(&tomoyo_pattern_list_lock); > - list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) { > + list_for_each_cookie_rcu(pos, head->read_var2, &tomoyo_pattern_list) { > struct tomoyo_pattern_entry *ptr; > ptr = list_entry(pos, struct tomoyo_pattern_entry, list); > if (ptr->is_deleted) > @@ -454,7 +400,6 @@ bool tomoyo_read_file_pattern(struct tom > if (!done) > break; > } > - up_read(&tomoyo_pattern_list_lock); > return done; > } > > @@ -487,8 +432,7 @@ bool tomoyo_read_file_pattern(struct tom > * " (deleted)" suffix if the file is already unlink()ed; so that we don't > * need to worry whether the file is already unlink()ed or not. > */ > -static LIST_HEAD(tomoyo_no_rewrite_list); > -static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock); > +LIST_HEAD(tomoyo_no_rewrite_list); > > /** > * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list. > @@ -513,8 +457,8 @@ static int tomoyo_update_no_rewrite_entr > return -ENOMEM; > if (!is_delete) > new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); > - down_write(&tomoyo_no_rewrite_list_lock); > - list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) { > + mutex_lock(&tomoyo_policy_lock); > + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { > if (ptr->pattern != saved_pattern) > continue; > ptr->is_deleted = is_delete; > @@ -524,11 +468,11 @@ static int tomoyo_update_no_rewrite_entr > if (!is_delete && error && tomoyo_memory_ok(new_entry)) { > new_entry->pattern = saved_pattern; > saved_pattern = NULL; > - list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list); > + list_add_tail_rcu(&new_entry->list, &tomoyo_no_rewrite_list); > new_entry = NULL; > error = 0; > } > - up_write(&tomoyo_no_rewrite_list_lock); > + mutex_unlock(&tomoyo_policy_lock); > tomoyo_put_name(saved_pattern); > return error; > } > @@ -540,14 +484,15 @@ static int tomoyo_update_no_rewrite_entr > * > * Returns true if @filename is specified by "deny_rewrite" directive, > * false otherwise. > + * > + * Caller holds srcu_read_lock(&tomoyo_ss). > */ > static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename) > { > struct tomoyo_no_rewrite_entry *ptr; > bool found = false; > > - down_read(&tomoyo_no_rewrite_list_lock); > - list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) { > + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { > if (ptr->is_deleted) > continue; > if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) > @@ -555,7 +500,6 @@ static bool tomoyo_is_no_rewrite_file(co > found = true; > break; > } > - up_read(&tomoyo_no_rewrite_list_lock); > return found; > } > > @@ -584,8 +528,8 @@ bool tomoyo_read_no_rewrite_policy(struc > struct list_head *pos; > bool done = true; > > - down_read(&tomoyo_no_rewrite_list_lock); > - list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) { > + list_for_each_cookie_rcu(pos, head->read_var2, > + &tomoyo_no_rewrite_list) { > struct tomoyo_no_rewrite_entry *ptr; > ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list); > if (ptr->is_deleted) > @@ -595,7 +539,6 @@ bool tomoyo_read_no_rewrite_policy(struc > if (!done) > break; > } > - up_read(&tomoyo_no_rewrite_list_lock); > return done; > } > > @@ -660,9 +603,9 @@ static int tomoyo_check_single_path_acl2 > { > struct tomoyo_acl_info *ptr; > int error = -EPERM; > + const int idx = srcu_read_lock(&tomoyo_ss); > > - down_read(&tomoyo_domain_acl_info_list_lock); > - list_for_each_entry(ptr, &domain->acl_info_list, list) { > + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { > struct tomoyo_single_path_acl_record *acl; > if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL) > continue; > @@ -680,7 +623,7 @@ static int tomoyo_check_single_path_acl2 > error = 0; > break; > } > - up_read(&tomoyo_domain_acl_info_list_lock); > + srcu_read_unlock(&tomoyo_ss, idx); > return error; > } > > @@ -846,10 +789,10 @@ static int tomoyo_update_single_path_acl > return -ENOMEM; > if (!is_delete) > new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); > - down_write(&tomoyo_domain_acl_info_list_lock); > + mutex_lock(&tomoyo_policy_lock); > if (is_delete) > goto delete; > - list_for_each_entry(ptr, &domain->acl_info_list, list) { > + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { > struct tomoyo_single_path_acl_record *acl; > if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL) > continue; > @@ -877,13 +820,14 @@ static int tomoyo_update_single_path_acl > new_entry->perm |= rw_mask; > new_entry->filename = saved_filename; > saved_filename = NULL; > - list_add_tail(&new_entry->head.list, &domain->acl_info_list); > + list_add_tail_rcu(&new_entry->head.list, > + &domain->acl_info_list); > new_entry = NULL; > error = 0; > } > goto out; > delete: > - list_for_each_entry(ptr, &domain->acl_info_list, list) { > + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { > struct tomoyo_single_path_acl_record *acl; > if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL) > continue; > @@ -902,7 +846,7 @@ static int tomoyo_update_single_path_acl > break; > } > out: > - up_write(&tomoyo_domain_acl_info_list_lock); > + mutex_unlock(&tomoyo_policy_lock); > tomoyo_put_name(saved_filename); > kfree(new_entry); > return error; > @@ -945,10 +889,10 @@ static int tomoyo_update_double_path_acl > } > if (!is_delete) > new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); > - down_write(&tomoyo_domain_acl_info_list_lock); > + mutex_lock(&tomoyo_policy_lock); > if (is_delete) > goto delete; > - list_for_each_entry(ptr, &domain->acl_info_list, list) { > + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { > struct tomoyo_double_path_acl_record *acl; > if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL) > continue; > @@ -973,13 +917,14 @@ static int tomoyo_update_double_path_acl > saved_filename1 = NULL; > new_entry->filename2 = saved_filename2; > saved_filename2 = NULL; > - list_add_tail(&new_entry->head.list, &domain->acl_info_list); > + list_add_tail_rcu(&new_entry->head.list, > + &domain->acl_info_list); > new_entry = NULL; > error = 0; > } > goto out; > delete: > - list_for_each_entry(ptr, &domain->acl_info_list, list) { > + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { > struct tomoyo_double_path_acl_record *acl; > if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL) > continue; > @@ -995,7 +940,7 @@ static int tomoyo_update_double_path_acl > break; > } > out: > - up_write(&tomoyo_domain_acl_info_list_lock); > + mutex_unlock(&tomoyo_policy_lock); > tomoyo_put_name(saved_filename1); > tomoyo_put_name(saved_filename2); > kfree(new_entry); > @@ -1040,11 +985,12 @@ static int tomoyo_check_double_path_acl( > struct tomoyo_acl_info *ptr; > const u8 perm = 1 << type; > int error = -EPERM; > + int idx; > > if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE)) > return 0; > - down_read(&tomoyo_domain_acl_info_list_lock); > - list_for_each_entry(ptr, &domain->acl_info_list, list) { > + idx = srcu_read_lock(&tomoyo_ss); > + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { > struct tomoyo_double_path_acl_record *acl; > if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL) > continue; > @@ -1059,7 +1005,7 @@ static int tomoyo_check_double_path_acl( > error = 0; > break; > } > - up_read(&tomoyo_domain_acl_info_list_lock); > + srcu_read_unlock(&tomoyo_ss, idx); > return error; > } > > @@ -1169,6 +1115,7 @@ int tomoyo_check_open_permission(struct > struct tomoyo_path_info *buf; > const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); > const bool is_enforce = (mode == 3); > + int idx; > > if (!mode || !path->mnt) > return 0; > @@ -1184,6 +1131,7 @@ int tomoyo_check_open_permission(struct > if (!buf) > goto out; > error = 0; > + idx = srcu_read_lock(&tomoyo_ss); > /* > * If the filename is specified by "deny_rewrite" keyword, > * we need to check "allow_rewrite" permission when the filename is not > @@ -1203,6 +1151,7 @@ int tomoyo_check_open_permission(struct > error = tomoyo_check_single_path_permission2(domain, > TOMOYO_TYPE_TRUNCATE_ACL, > buf, mode); > + srcu_read_unlock(&tomoyo_ss, idx); > out: > tomoyo_free(buf); > if (!is_enforce) > @@ -1226,6 +1175,7 @@ int tomoyo_check_1path_perm(struct tomoy > struct tomoyo_path_info *buf; > const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); > const bool is_enforce = (mode == 3); > + int idx; > > if (!mode || !path->mnt) > return 0; > @@ -1243,8 +1193,10 @@ int tomoyo_check_1path_perm(struct tomoy > tomoyo_fill_path_info(buf); > } > } > + idx = srcu_read_lock(&tomoyo_ss); > error = tomoyo_check_single_path_permission2(domain, operation, buf, > mode); > + srcu_read_unlock(&tomoyo_ss, idx); > out: > tomoyo_free(buf); > if (!is_enforce) > @@ -1267,19 +1219,23 @@ int tomoyo_check_rewrite_permission(stru > const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); > const bool is_enforce = (mode == 3); > struct tomoyo_path_info *buf; > + int idx; > > if (!mode || !filp->f_path.mnt) > return 0; > buf = tomoyo_get_path(&filp->f_path); > if (!buf) > goto out; > + idx = srcu_read_lock(&tomoyo_ss); > if (!tomoyo_is_no_rewrite_file(buf)) { > error = 0; > - goto out; > + goto ok; > } > error = tomoyo_check_single_path_permission2(domain, > TOMOYO_TYPE_REWRITE_ACL, > buf, mode); > + ok: > + srcu_read_unlock(&tomoyo_ss, idx); > out: > tomoyo_free(buf); > if (!is_enforce) > @@ -1306,6 +1262,7 @@ int tomoyo_check_2path_perm(struct tomoy > const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); > const bool is_enforce = (mode == 3); > const char *msg; > + int idx; > > if (!mode || !path1->mnt || !path2->mnt) > return 0; > @@ -1329,10 +1286,11 @@ int tomoyo_check_2path_perm(struct tomoy > } > } > } > + idx = srcu_read_lock(&tomoyo_ss); > error = tomoyo_check_double_path_acl(domain, operation, buf1, buf2); > msg = tomoyo_dp2keyword(operation); > if (!error) > - goto out; > + goto ok; > if (tomoyo_verbose_mode(domain)) > printk(KERN_WARNING "TOMOYO-%s: Access '%s %s %s' " > "denied for %s\n", tomoyo_get_msg(is_enforce), > @@ -1344,6 +1302,8 @@ int tomoyo_check_2path_perm(struct tomoy > tomoyo_update_double_path_acl(operation, name1, name2, domain, > false); > } > + ok: > + srcu_read_unlock(&tomoyo_ss, idx); > out: > tomoyo_free(buf1); > tomoyo_free(buf2); > --- security-testing-2.6.git.orig/security/tomoyo/realpath.c > +++ security-testing-2.6.git/security/tomoyo/realpath.c > @@ -1,3 +1,4 @@ > + > /* > * security/tomoyo/realpath.c > * > @@ -15,6 +16,9 @@ > #include > #include "common.h" > #include "realpath.h" > +#include "tomoyo.h" > + > +struct srcu_struct tomoyo_ss; > > /** > * tomoyo_encode: Convert binary string to ascii string. > @@ -223,6 +227,17 @@ bool tomoyo_memory_ok(void *ptr) > return false; > } > > +/** > + * tomoyo_free_element - Free memory for elements. > + * > + * @ptr: Pointer to allocated memory. > + */ > +static void tomoyo_free_element(void *ptr) > +{ > + atomic_sub(ksize(ptr), &tomoyo_allocated_memory_for_elements); > + kfree(ptr); > +} > + > /* Memory allocated for string data in bytes. */ > static atomic_t tomoyo_allocated_memory_for_savename; > /* Quota for holding string data in bytes. */ > @@ -238,15 +253,10 @@ static unsigned int tomoyo_quota_for_sav > /* > * tomoyo_name_entry is a structure which is used for linking > * "struct tomoyo_path_info" into tomoyo_name_list . > - * > - * Since tomoyo_name_list manages a list of strings which are shared by > - * multiple processes (whereas "struct tomoyo_path_info" inside > - * "struct tomoyo_path_info_with_data" is not shared), a reference counter will > - * be added to "struct tomoyo_name_entry" rather than "struct tomoyo_path_info" > - * when TOMOYO starts supporting garbage collector. > */ > struct tomoyo_name_entry { > struct list_head list; > + atomic_t users; > struct tomoyo_path_info entry; > }; > > @@ -287,10 +297,11 @@ const struct tomoyo_path_info *tomoyo_ge > entry = kmalloc(sizeof(*entry) + len, GFP_KERNEL); > allocated_len = entry ? ksize(entry) : 0; > mutex_lock(&tomoyo_name_list_lock); > - list_for_each_entry(ptr, &tomoyo_name_list[hash % TOMOYO_MAX_HASH], > - list) { > + list_for_each_entry_rcu(ptr, &tomoyo_name_list[hash % TOMOYO_MAX_HASH], > + list) { > if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) > continue; > + atomic_inc(&ptr->users); > error = 0; > break; > } > @@ -305,8 +316,9 @@ const struct tomoyo_path_info *tomoyo_ge > ptr->entry.name = ((char *) ptr) + sizeof(*ptr); > memmove((char *) ptr->entry.name, name, len); > tomoyo_fill_path_info(&ptr->entry); > - list_add_tail(&ptr->list, > - &tomoyo_name_list[hash % TOMOYO_MAX_HASH]); > + atomic_set(&ptr->users, 1); > + list_add_tail_rcu(&ptr->list, > + &tomoyo_name_list[hash % TOMOYO_MAX_HASH]); > entry = NULL; > error = 0; > } > @@ -321,6 +333,31 @@ const struct tomoyo_path_info *tomoyo_ge > } > > /** > + * tomoyo_put_name - Delete shared memory for string data. > + * > + * @ptr: Pointer to "struct tomoyo_path_info". > + */ > +void tomoyo_put_name(const struct tomoyo_path_info *name) > +{ > + struct tomoyo_name_entry *ptr; > + bool can_delete = false; > + > + if (!name) > + return; > + ptr = container_of(name, struct tomoyo_name_entry, entry); > + mutex_lock(&tomoyo_name_list_lock); > + if (atomic_dec_and_test(&ptr->users)) { > + list_del(&ptr->list); > + can_delete = true; > + } > + mutex_unlock(&tomoyo_name_list_lock); > + if (can_delete) { > + atomic_sub(ksize(ptr), &tomoyo_allocated_memory_for_savename); > + kfree(ptr); > + } > +} > + > +/** > * tomoyo_realpath_init - Initialize realpath related code. > */ > void __init tomoyo_realpath_init(void) > @@ -331,12 +368,14 @@ void __init tomoyo_realpath_init(void) > for (i = 0; i < TOMOYO_MAX_HASH; i++) > INIT_LIST_HEAD(&tomoyo_name_list[i]); > INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); > + if (init_srcu_struct(&tomoyo_ss)) > + panic("Can't initialize tomoyo_ss"); > tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME); > - list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list); > - down_read(&tomoyo_domain_list_lock); > + list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list); > + i = srcu_read_lock(&tomoyo_ss); > if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) > panic("Can't register tomoyo_kernel_domain"); > - up_read(&tomoyo_domain_list_lock); > + srcu_read_unlock(&tomoyo_ss, i); > } > > /* Memory allocated for temporary purpose. */ > @@ -431,3 +470,311 @@ int tomoyo_write_memory_quota(struct tom > tomoyo_quota_for_elements = size; > return 0; > } > + > +/* Garbage collecter functions */ > + > +static inline void tomoyo_gc_del_domain_initializer > +(struct tomoyo_domain_initializer_entry *ptr) > +{ > + tomoyo_put_name(ptr->domainname); > + tomoyo_put_name(ptr->program); > +} > + > +static inline void tomoyo_gc_del_domain_keeper > +(struct tomoyo_domain_keeper_entry *ptr) > +{ > + tomoyo_put_name(ptr->domainname); > + tomoyo_put_name(ptr->program); > +} > + > +static inline void tomoyo_gc_del_alias(struct tomoyo_alias_entry *ptr) > +{ > + tomoyo_put_name(ptr->original_name); > + tomoyo_put_name(ptr->aliased_name); > +} > + > +static inline void tomoyo_gc_del_readable > +(struct tomoyo_globally_readable_file_entry *ptr) > +{ > + tomoyo_put_name(ptr->filename); > +} > + > +static inline void tomoyo_gc_del_pattern(struct tomoyo_pattern_entry *ptr) > +{ > + tomoyo_put_name(ptr->pattern); > +} > + > +static inline void tomoyo_gc_del_no_rewrite > +(struct tomoyo_no_rewrite_entry *ptr) > +{ > + tomoyo_put_name(ptr->pattern); > +} > + > +static inline void tomoyo_gc_del_manager > +(struct tomoyo_policy_manager_entry *ptr) > +{ > + tomoyo_put_name(ptr->manager); > +} > + > +static void tomoyo_gc_del_acl(struct tomoyo_acl_info *acl) > +{ > + switch (tomoyo_acl_type1(acl)) { > + struct tomoyo_single_path_acl_record *acl1; > + struct tomoyo_double_path_acl_record *acl2; > + case TOMOYO_TYPE_SINGLE_PATH_ACL: > + acl1 = container_of(acl, struct tomoyo_single_path_acl_record, > + head); > + tomoyo_put_name(acl1->filename); > + break; > + case TOMOYO_TYPE_DOUBLE_PATH_ACL: > + acl2 = container_of(acl, struct tomoyo_double_path_acl_record, > + head); > + tomoyo_put_name(acl2->filename1); > + tomoyo_put_name(acl2->filename2); > + break; > + } > +} > + > +static bool tomoyo_gc_del_domain(struct tomoyo_domain_info *domain) > +{ > + struct tomoyo_acl_info *acl; > + struct tomoyo_acl_info *tmp; > + /* > + * We need to recheck domain->users because > + * tomoyo_find_next_domain() increments it. > + */ > + if (atomic_read(&domain->users)) > + return false; > + /* Delete all entries in this domain. */ > + list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { > + list_del_rcu(&acl->list); > + tomoyo_gc_del_acl(acl); > + tomoyo_free_element(acl); > + } > + tomoyo_put_name(domain->domainname); > + return true; > +} > + > +enum tomoyo_gc_id { > + TOMOYO_ID_DOMAIN_INITIALIZER, > + TOMOYO_ID_DOMAIN_KEEPER, > + TOMOYO_ID_ALIAS, > + TOMOYO_ID_GLOBALLY_READABLE, > + TOMOYO_ID_PATTERN, > + TOMOYO_ID_NO_REWRITE, > + TOMOYO_ID_MANAGER, > + TOMOYO_ID_ACL, > + TOMOYO_ID_DOMAIN > +}; > + > +struct tomoyo_gc_entry { > + struct list_head list; > + int type; > + void *element; > +}; > + > + > +/* Caller holds tomoyo_policy_lock mutex. */ > +static bool tomoyo_add_to_gc(const int type, void *element, > + struct list_head *head) > +{ > + struct tomoyo_gc_entry *entry = kmalloc(sizeof(*entry), GFP_ATOMIC); > + if (!entry) > + return false; > + entry->type = type; > + entry->element = element; > + list_add(&entry->list, head); > + return true; > +} > + > +/** > + * tomoyo_gc_thread_main - Garbage collector thread for TOMOYO. > + * > + * @unused: Not used. > + * > + * This function is exclusively executed. > + */ > +static int tomoyo_gc_thread_main(void *unused) > +{ > + static DEFINE_MUTEX(tomoyo_gc_mutex); > + static LIST_HEAD(tomoyo_gc_queue); > + if (!mutex_trylock(&tomoyo_gc_mutex)) > + return 0; > + > + mutex_lock(&tomoyo_policy_lock); > + { > + struct tomoyo_globally_readable_file_entry *ptr; > + list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, > + list) { > + if (!ptr->is_deleted) > + continue; > + if (tomoyo_add_to_gc(TOMOYO_ID_GLOBALLY_READABLE, ptr, > + &tomoyo_gc_queue)) > + list_del_rcu(&ptr->list); > + else > + break; > + } > + } > + { > + struct tomoyo_pattern_entry *ptr; > + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { > + if (!ptr->is_deleted) > + continue; > + if (tomoyo_add_to_gc(TOMOYO_ID_PATTERN, ptr, > + &tomoyo_gc_queue)) > + list_del_rcu(&ptr->list); > + else > + break; > + } > + } > + { > + struct tomoyo_no_rewrite_entry *ptr; > + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { > + if (!ptr->is_deleted) > + continue; > + if (tomoyo_add_to_gc(TOMOYO_ID_NO_REWRITE, ptr, > + &tomoyo_gc_queue)) > + list_del_rcu(&ptr->list); > + else > + break; > + } > + } > + { > + struct tomoyo_domain_initializer_entry *ptr; > + list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, > + list) { > + if (!ptr->is_deleted) > + continue; > + if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_INITIALIZER, > + ptr, &tomoyo_gc_queue)) > + list_del_rcu(&ptr->list); > + else > + break; > + } > + } > + { > + struct tomoyo_domain_keeper_entry *ptr; > + list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, > + list) { > + if (!ptr->is_deleted) > + continue; > + if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_KEEPER, ptr, > + &tomoyo_gc_queue)) > + list_del_rcu(&ptr->list); > + else > + break; > + } > + } > + { > + struct tomoyo_alias_entry *ptr; > + list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { > + if (!ptr->is_deleted) > + continue; > + if (tomoyo_add_to_gc(TOMOYO_ID_ALIAS, ptr, > + &tomoyo_gc_queue)) > + list_del_rcu(&ptr->list); > + else > + break; > + } > + } > + { > + struct tomoyo_policy_manager_entry *ptr; > + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, > + list) { > + if (!ptr->is_deleted) > + continue; > + if (tomoyo_add_to_gc(TOMOYO_ID_MANAGER, ptr, > + &tomoyo_gc_queue)) > + list_del_rcu(&ptr->list); > + else > + break; > + } > + } > + { > + struct tomoyo_domain_info *domain; > + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { > + struct tomoyo_acl_info *acl; > + list_for_each_entry_rcu(acl, &domain->acl_info_list, > + list) { > + if (!(acl->type & TOMOYO_ACL_DELETED)) > + continue; > + if (tomoyo_add_to_gc(TOMOYO_ID_ACL, acl, > + &tomoyo_gc_queue)) > + list_del_rcu(&acl->list); > + else > + break; > + } > + if (domain->is_deleted && > + !atomic_read(&domain->users)) { > + if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, domain, > + &tomoyo_gc_queue)) > + list_del_rcu(&domain->list); > + else > + break; > + } > + } > + } > + mutex_unlock(&tomoyo_policy_lock); > + if (list_empty(&tomoyo_gc_queue)) > + goto done; > + synchronize_srcu(&tomoyo_ss); > + { > + struct tomoyo_gc_entry *p; > + struct tomoyo_gc_entry *tmp; > + list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { > + switch (p->type) { > + case TOMOYO_ID_DOMAIN_INITIALIZER: > + tomoyo_gc_del_domain_initializer(p->element); > + break; > + case TOMOYO_ID_DOMAIN_KEEPER: > + tomoyo_gc_del_domain_keeper(p->element); > + break; > + case TOMOYO_ID_ALIAS: > + tomoyo_gc_del_alias(p->element); > + break; > + case TOMOYO_ID_GLOBALLY_READABLE: > + tomoyo_gc_del_readable(p->element); > + break; > + case TOMOYO_ID_PATTERN: > + tomoyo_gc_del_pattern(p->element); > + break; > + case TOMOYO_ID_NO_REWRITE: > + tomoyo_gc_del_no_rewrite(p->element); > + break; > + case TOMOYO_ID_MANAGER: > + tomoyo_gc_del_manager(p->element); > + break; > + case TOMOYO_ID_ACL: > + tomoyo_gc_del_acl(p->element); > + break; > + case TOMOYO_ID_DOMAIN: > + if (!tomoyo_gc_del_domain(p->element)) > + continue; > + break; > + } > + tomoyo_free_element(p->element); > + list_del(&p->list); > + kfree(p); > + } > + } > + done: > + mutex_unlock(&tomoyo_gc_mutex); > + return 0; > +} > + > +/** > + * tomoyo_gc_thread - Garbage collector thread for TOMOYO. > + * > + * @unused: Not used. > + */ > +int tomoyo_gc_thread(void *unused) > +{ > + /* > + * Maybe this thread should be created and terminated as needed > + * rather than created upon boot and living forever... > + */ > + while (1) { > + msleep(30000); > + tomoyo_gc_thread_main(unused); > + } > +} > --- security-testing-2.6.git.orig/security/tomoyo/realpath.h > +++ security-testing-2.6.git/security/tomoyo/realpath.h > @@ -44,10 +44,7 @@ bool tomoyo_memory_ok(void *ptr); > * The RAM is shared, so NEVER try to modify or kfree() the returned name. > */ > const struct tomoyo_path_info *tomoyo_get_name(const char *name); > -static inline void tomoyo_put_name(const struct tomoyo_path_info *name) > -{ > - /* It's a dummy so far. */ > -} > +void tomoyo_put_name(const struct tomoyo_path_info *name); > > /* Allocate memory for temporary use (e.g. permission checks). */ > void *tomoyo_alloc(const size_t size); > --- security-testing-2.6.git.orig/security/tomoyo/tomoyo.c > +++ security-testing-2.6.git/security/tomoyo/tomoyo.c > @@ -22,9 +22,19 @@ static int tomoyo_cred_prepare(struct cr > * we don't need to duplicate. > */ > new->security = old->security; > + if (new->security) > + atomic_inc(&((struct tomoyo_domain_info *) > + new->security)->users); > return 0; > } > > +static void tomoyo_cred_free(struct cred *cred) > +{ > + struct tomoyo_domain_info *domain = cred->security; > + if (domain) > + atomic_dec(&domain->users); > +} > + > static int tomoyo_bprm_set_creds(struct linux_binprm *bprm) > { > int rc; > @@ -49,7 +59,11 @@ static int tomoyo_bprm_set_creds(struct > * Tell tomoyo_bprm_check_security() is called for the first time of an > * execve operation. > */ > - bprm->cred->security = NULL; > + if (bprm->cred->security) { > + atomic_dec(&((struct tomoyo_domain_info *) > + bprm->cred->security)->users); > + bprm->cred->security = NULL; > + } > return 0; > } > > @@ -263,6 +277,7 @@ static int tomoyo_dentry_open(struct fil > static struct security_operations tomoyo_security_ops = { > .name = "tomoyo", > .cred_prepare = tomoyo_cred_prepare, > + .cred_free = tomoyo_cred_free, > .bprm_set_creds = tomoyo_bprm_set_creds, > .bprm_check_security = tomoyo_bprm_check_security, > #ifdef CONFIG_SYSCTL > @@ -291,6 +306,7 @@ static int __init tomoyo_init(void) > panic("Failure registering TOMOYO Linux"); > printk(KERN_INFO "TOMOYO Linux initialized\n"); > cred->security = &tomoyo_kernel_domain; > + atomic_inc(&tomoyo_kernel_domain.users); > tomoyo_realpath_init(); > return 0; > }