lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20090618160521.GC6794@linux.vnet.ibm.com>
Date:	Thu, 18 Jun 2009 09:05:21 -0700
From:	"Paul E. McKenney" <paulmck@...ux.vnet.ibm.com>
To:	Tetsuo Handa <penguin-kernel@...ove.sakura.ne.jp>
Cc:	linux-security-module@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH 3/3] TOMOYO: Add SRCU based garbage collector.

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 <penguin-kernel@...ove.SAKURA.ne.jp>
> ---
>  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 <linux/uaccess.h>
>  #include <linux/security.h>
>  #include <linux/hardirq.h>
> +#include <linux/kthread.h>
>  #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 "<kernel> /usr/sbin/httpd" domain
>   * unless executed from "<kernel> /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
>   * "<kernel> /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 <linux/fs_struct.h>
>  #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;
>  }
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ