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: <200906180645.n5I6jxQn096226@www262.sakura.ne.jp>
Date:	Thu, 18 Jun 2009 15:45:59 +0900
From:	Tetsuo Handa <penguin-kernel@...ove.sakura.ne.jp>
To:	paulmck@...ux.vnet.ibm.com
Cc:	linux-security-module@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: [PATCH 3/3] TOMOYO: Add SRCU based garbage collector.

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. 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.

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