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>] [day] [month] [year] [list]
Date:	Tue, 02 Jun 2009 10:47:32 +0900
From:	Tetsuo Handa <penguin-kernel@...ove.sakura.ne.jp>
To:	linux-security-module@...r.kernel.org
Cc:	linux-kernel@...r.kernel.org
Subject: [PATCH 5/5] TOMOYO: Add refcounter and garbage collector.

A process who is reading/writing list elements has pointers which point to
(possibly marked-as-deleted) list elements and releases a lock when the process
leaves the kernel.

As of now, TOMOYO cannot release memory used by marked-as-deleted list elements
because TOMOYO does not manage list of pointers.

This patch makes TOMOYO to manage list of pointers and allows an administrator
release memory used by marked-as-deleted list elements.

Approach:

  Define a structure

    struct tomoyo_entry {
        struct list_head list;
        atomic_t users;
    };

  and replace "struct list_head" with "struct tomoyo_entry".

  Get a reference by calling

    void tomoyo_get_ref(struct list_head *item)

  before releasing a lock and drop a reference by calling

    void tomoyo_put_ref(struct list_head *item)

  after obtaining a lock.

Memory Management Rules:

  When /sys/kernel/security/tomoyo/ interface opened for writing is closed,
  garbage collector functions are called. The garbage collector functions take
  appropriate locks and remove elements if that element has is_deleted flag set
  and that element's refcounter is 0.

  Since strings are likely referenced by multiple list elements, this patch
  assigns a refcounter to each string.
  The garbage collector function releases memory of the string if the string's
  refcounter becomes 0.

Signed-off-by: Kentaro Takeda <takedakn@...data.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@...ove.SAKURA.ne.jp>
Signed-off-by: Toshiharu Harada <haradats@...data.co.jp>
---
 security/tomoyo/common.c   |  313 +++++++++++++++++++++++++++++++++++----------
 security/tomoyo/common.h   |   58 +++++---
 security/tomoyo/domain.c   |  277 ++++++++++++++++++++-------------------
 security/tomoyo/file.c     |  199 ++++++++++++++++++++++------
 security/tomoyo/realpath.c |   57 ++++++--
 security/tomoyo/realpath.h |    7 -
 security/tomoyo/tomoyo.c   |   28 ++--
 security/tomoyo/tomoyo.h   |    3 
 8 files changed, 650 insertions(+), 292 deletions(-)

--- security-testing-2.6.git.orig/security/tomoyo/common.c
+++ security-testing-2.6.git/security/tomoyo/common.c
@@ -327,10 +327,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 must call down_read(&tomoyo_domain_list_lock);.
  */
 struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
 {
@@ -339,7 +338,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(domain, &tomoyo_domain_list, entry.list) {
 		if (!domain->is_deleted &&
 		    !tomoyo_pathcmp(&name, domain->domainname))
 			return domain;
@@ -788,7 +787,7 @@ bool tomoyo_domain_quota_is_ok(struct to
 		return true;
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_domain_acl_info_list_lock);
-	list_for_each_entry(ptr, &domain->acl_info_list, list) {
+	list_for_each_entry(ptr, &domain->acl_info_list, entry.list) {
 		if (ptr->type & TOMOYO_ACL_DELETED)
 			continue;
 		switch (tomoyo_acl_type2(ptr)) {
@@ -889,6 +888,9 @@ static struct tomoyo_profile *tomoyo_fin
 	return ptr;
 }
 
+/* Lock for protecting profile->comment. */
+static DEFINE_MUTEX(tomoyo_profile_comment_lock);
+
 /**
  * tomoyo_write_profile - Write to profile table.
  *
@@ -923,8 +925,12 @@ static int tomoyo_write_profile(struct t
 		const struct tomoyo_path_info *new_comment
 			= tomoyo_get_name(cp + 1);
 		const struct tomoyo_path_info *old_comment;
+		/***** EXCLUSIVE SECTION START *****/
+		mutex_lock(&tomoyo_profile_comment_lock);
 		old_comment = profile->comment;
 		profile->comment = new_comment;
+		mutex_unlock(&tomoyo_profile_comment_lock);
+		/***** EXCLUSIVE SECTION END *****/
 		tomoyo_put_name(old_comment);
 		return 0;
 	}
@@ -983,9 +989,15 @@ static int tomoyo_read_profile(struct to
 		if (!profile)
 			continue;
 		if (!type) { /* Print profile' comment tag. */
-			if (!tomoyo_io_printf(head, "%u-COMMENT=%s\n",
-					      index, profile->comment ?
-					      profile->comment->name : ""))
+			bool done;
+			/***** EXCLUSIVE SECTION START *****/
+			mutex_lock(&tomoyo_profile_comment_lock);
+			done = tomoyo_io_printf(head, "%u-COMMENT=%s\n",
+						index, profile->comment ?
+						profile->comment->name : "");
+			mutex_unlock(&tomoyo_profile_comment_lock);
+			/***** EXCLUSIVE SECTION END *****/
+			if (!done)
 				break;
 			continue;
 		}
@@ -1021,7 +1033,7 @@ static int tomoyo_read_profile(struct to
 
 /* Structure for policy manager. */
 struct tomoyo_policy_manager_entry {
-	struct list_head list;
+	struct tomoyo_entry entry;
 	/* A path to program or a domainname. */
 	const struct tomoyo_path_info *manager;
 	bool is_domain;  /* True if manager is a domainname. */
@@ -1064,7 +1076,7 @@ static int tomoyo_update_manager_entry(c
 		new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
 	/***** WRITER SECTION START *****/
 	down_write(&tomoyo_policy_manager_list_lock);
-	list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+	list_for_each_entry(ptr, &tomoyo_policy_manager_list, entry.list) {
 		if (ptr->manager != saved_manager)
 			continue;
 		ptr->is_deleted = is_delete;
@@ -1073,8 +1085,10 @@ static int tomoyo_update_manager_entry(c
 	}
 	if (!is_delete && error && tomoyo_memory_ok(new_entry)) {
 		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(&new_entry->entry.list,
+			      &tomoyo_policy_manager_list);
 		new_entry = NULL;
 		error = 0;
 	}
@@ -1085,6 +1099,32 @@ static int tomoyo_update_manager_entry(c
 }
 
 /**
+ * tomoyo_cleanup_manager - Clean up deleted "struct tomoyo_policy_manager_entry".
+ */
+static void tomoyo_cleanup_manager(void)
+{
+	struct tomoyo_policy_manager_entry *ptr;
+	struct tomoyo_policy_manager_entry *tmp;
+	LIST_HEAD(q);
+	/***** WRITER SECTION START *****/
+	down_write(&tomoyo_policy_manager_list_lock);
+	list_for_each_entry_safe(ptr, tmp, &tomoyo_policy_manager_list,
+				 entry.list) {
+		if (!ptr->is_deleted || atomic_read(&ptr->entry.users))
+			continue;
+		list_del(&ptr->entry.list);
+		list_add(&ptr->entry.list, &q);
+	}
+	up_write(&tomoyo_policy_manager_list_lock);
+	/***** WRITER SECTION END *****/
+	list_for_each_entry_safe(ptr, tmp, &q, entry.list) {
+		tomoyo_put_name(ptr->manager);
+		list_del(&ptr->entry.list);
+		tomoyo_free_element(ptr);
+	}
+}
+
+/**
  * tomoyo_write_manager_policy - Write manager policy.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
@@ -1119,17 +1159,19 @@ static int tomoyo_read_manager_policy(st
 		return 0;
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_policy_manager_list_lock);
+	tomoyo_put_ref(head->read_var2);
 	list_for_each_cookie(pos, head->read_var2,
 			     &tomoyo_policy_manager_list) {
 		struct tomoyo_policy_manager_entry *ptr;
 		ptr = list_entry(pos, struct tomoyo_policy_manager_entry,
-				 list);
+				 entry.list);
 		if (ptr->is_deleted)
 			continue;
 		done = tomoyo_io_printf(head, "%s\n", ptr->manager->name);
 		if (!done)
 			break;
 	}
+	tomoyo_get_ref(head->read_var2);
 	up_read(&tomoyo_policy_manager_list_lock);
 	/***** READER SECTION END *****/
 	head->read_eof = done;
@@ -1156,7 +1198,7 @@ static bool tomoyo_is_policy_manager(voi
 		return false;
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_policy_manager_list_lock);
-	list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+	list_for_each_entry(ptr, &tomoyo_policy_manager_list, entry.list) {
 		if (!ptr->is_deleted && ptr->is_domain
 		    && !tomoyo_pathcmp(domainname, ptr->manager)) {
 			found = true;
@@ -1172,7 +1214,7 @@ static bool tomoyo_is_policy_manager(voi
 		return false;
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_policy_manager_list_lock);
-	list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+	list_for_each_entry(ptr, &tomoyo_policy_manager_list, entry.list) {
 		if (!ptr->is_deleted && !ptr->is_domain
 		    && !strcmp(exe, ptr->manager->name)) {
 			found = true;
@@ -1205,9 +1247,15 @@ static bool tomoyo_is_policy_manager(voi
 static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
 				 const char *data)
 {
+	bool result = false;
 	unsigned int pid;
 	struct tomoyo_domain_info *domain = NULL;
 
+	/***** READER SECTION START *****/
+	down_read(&tomoyo_domain_list_lock);
+	tomoyo_put_ref(head->write_var1);
+	tomoyo_put_ref(head->read_var1);
+	tomoyo_put_ref(head->read_var2);
 	if (sscanf(data, "pid=%u", &pid) == 1) {
 		struct task_struct *p;
 		/***** CRITICAL SECTION START *****/
@@ -1218,19 +1266,15 @@ static bool tomoyo_is_select_one(struct 
 		read_unlock(&tasklist_lock);
 		/***** CRITICAL SECTION END *****/
 	} else if (!strncmp(data, "domain=", 7)) {
-		if (tomoyo_is_domain_def(data + 7)) {
-			/***** READER SECTION START *****/
-			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);
-			/***** READER SECTION END *****/
-		}
 	} else
-		return false;
-	head->write_var1 = domain;
+		goto out;
+	result = true;
+	head->write_var1 = domain ? &domain->entry.list : NULL;
 	/* Accessing read_buf is safe because head->io_sem is held. */
 	if (!head->read_buf)
-		return true; /* Do nothing if open(O_WRONLY). */
+		goto out; /* Do nothing if open(O_WRONLY). */
 	head->read_avail = 0;
 	tomoyo_io_printf(head, "# select %s\n", data);
 	head->read_single_domain = true;
@@ -1238,22 +1282,62 @@ static bool tomoyo_is_select_one(struct 
 	if (domain) {
 		struct tomoyo_domain_info *d;
 		head->read_var1 = NULL;
-		/***** READER SECTION START *****/
-		down_read(&tomoyo_domain_list_lock);
-		list_for_each_entry(d, &tomoyo_domain_list, list) {
+		list_for_each_entry(d, &tomoyo_domain_list, entry.list) {
 			if (d == domain)
 				break;
-			head->read_var1 = &d->list;
+			head->read_var1 = &d->entry.list;
 		}
-		up_read(&tomoyo_domain_list_lock);
-		/***** READER SECTION END *****/
 		head->read_var2 = NULL;
 		head->read_bit = 0;
 		head->read_step = 0;
 		if (domain->is_deleted)
 			tomoyo_io_printf(head, "# This is a deleted domain.\n");
 	}
-	return true;
+ out:
+	tomoyo_get_ref(head->read_var2);
+	tomoyo_get_ref(head->read_var1);
+	tomoyo_get_ref(head->write_var1);
+	up_read(&tomoyo_domain_list_lock);
+	/***** READER SECTION END *****/
+	return result;
+}
+
+/**
+ * tomoyo_delete_domain - Delete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns 0.
+ *
+ * Caller must call down_read(&tomoyo_domain_list_lock);.
+ */
+static int tomoyo_delete_domain(char *domainname)
+{
+	struct tomoyo_domain_info *domain;
+	struct tomoyo_path_info name;
+
+	up_read(&tomoyo_domain_list_lock);
+	/***** READER SECTION END *****/
+	name.name = domainname;
+	tomoyo_fill_path_info(&name);
+	/***** WRITER SECTION START *****/
+	down_write(&tomoyo_domain_list_lock);
+	/* Is there an active domain? */
+	list_for_each_entry(domain, &tomoyo_domain_list, entry.list) {
+		/* Never delete tomoyo_kernel_domain */
+		if (domain == &tomoyo_kernel_domain)
+			continue;
+		if (domain->is_deleted ||
+		    tomoyo_pathcmp(domain->domainname, &name))
+			continue;
+		domain->is_deleted = true;
+		break;
+	}
+	up_write(&tomoyo_domain_list_lock);
+	/***** WRITER SECTION END *****/
+	/***** READER SECTION START *****/
+	down_read(&tomoyo_domain_list_lock);
+	return 0;
 }
 
 /**
@@ -1266,10 +1350,13 @@ static bool tomoyo_is_select_one(struct 
 static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
 {
 	char *data = head->write_buf;
-	struct tomoyo_domain_info *domain = head->write_var1;
+	struct tomoyo_domain_info *domain
+		= container_of(head->write_var1, struct tomoyo_domain_info,
+			       entry.list);
 	bool is_delete = false;
 	bool is_select = false;
 	unsigned int profile;
+	int error = 0;
 
 	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE))
 		is_delete = true;
@@ -1281,35 +1368,31 @@ static int tomoyo_write_domain_policy(st
 	if (!tomoyo_is_policy_manager())
 		return -EPERM;
 	if (tomoyo_is_domain_def(data)) {
+		/***** READER SECTION START *****/
+		down_read(&tomoyo_domain_list_lock);
+		tomoyo_put_ref(head->write_var1);
 		domain = NULL;
 		if (is_delete)
 			tomoyo_delete_domain(data);
-		else if (is_select) {
-			/***** READER SECTION START *****/
-			down_read(&tomoyo_domain_list_lock);
+		else if (is_select)
 			domain = tomoyo_find_domain(data);
-			up_read(&tomoyo_domain_list_lock);
-			/***** READER SECTION END *****/
-		} else
+		else
 			domain = tomoyo_find_or_assign_new_domain(data, 0);
-		head->write_var1 = domain;
-		return 0;
-	}
-	if (!domain)
-		return -EINVAL;
-
-	if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1
-	    && profile < TOMOYO_MAX_PROFILES) {
+		head->write_var1 = domain ? &domain->entry.list : NULL;
+		tomoyo_get_ref(head->write_var1);
+		up_read(&tomoyo_domain_list_lock);
+		/***** READER SECTION END *****/
+	} else if (!domain)
+		error = -EINVAL;
+	else if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1
+		 && profile < TOMOYO_MAX_PROFILES) {
 		if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)
 			domain->profile = (u8) profile;
-		return 0;
-	}
-	if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
-		tomoyo_set_domain_flag(domain, is_delete,
-			       TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ);
-		return 0;
-	}
-	return tomoyo_write_file_policy(data, domain, is_delete);
+	} else if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
+		domain->ignore_global_allow_read = !is_delete;
+	} else
+		error = tomoyo_write_file_policy(data, domain, is_delete);
+	return error;
 }
 
 /**
@@ -1448,12 +1531,16 @@ static int tomoyo_read_domain_policy(str
 		head->read_step = 1;
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_domain_list_lock);
+	down_read(&tomoyo_domain_acl_info_list_lock);
+	tomoyo_put_ref(head->read_var1);
+	tomoyo_put_ref(head->read_var2);
 	list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) {
 		struct tomoyo_domain_info *domain;
 		const char *quota_exceeded = "";
 		const char *transition_failed = "";
 		const char *ignore_global_allow_read = "";
-		domain = list_entry(dpos, struct tomoyo_domain_info, list);
+		domain = list_entry(dpos, struct tomoyo_domain_info,
+				    entry.list);
 		if (head->read_step != 1)
 			goto acl_loop;
 		if (domain->is_deleted && !head->read_single_domain)
@@ -1461,10 +1548,9 @@ static int tomoyo_read_domain_policy(str
 		/* Print domainname and flags. */
 		if (domain->quota_warned)
 			quota_exceeded = "quota_exceeded\n";
-		if (domain->flags & TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED)
+		if (domain->domain_transition_failed)
 			transition_failed = "transition_failed\n";
-		if (domain->flags &
-		    TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ)
+		if (domain->ignore_global_allow_read)
 			ignore_global_allow_read
 				= TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n";
 		done = tomoyo_io_printf(head, "%s\n" TOMOYO_KEYWORD_USE_PROFILE
@@ -1480,19 +1566,15 @@ acl_loop:
 		if (head->read_step == 3)
 			goto tail_mark;
 		/* Print ACL entries in the domain. */
-		/***** READER SECTION START *****/
-		down_read(&tomoyo_domain_acl_info_list_lock);
 		list_for_each_cookie(apos, head->read_var2,
 				     &domain->acl_info_list) {
 			struct tomoyo_acl_info *ptr
 				= list_entry(apos, struct tomoyo_acl_info,
-					     list);
+					     entry.list);
 			done = tomoyo_print_entry(head, ptr);
 			if (!done)
 				break;
 		}
-		up_read(&tomoyo_domain_acl_info_list_lock);
-		/***** READER SECTION END *****/
 		if (!done)
 			break;
 		head->read_step = 3;
@@ -1504,6 +1586,9 @@ tail_mark:
 		if (head->read_single_domain)
 			break;
 	}
+	tomoyo_get_ref(head->read_var2);
+	tomoyo_get_ref(head->read_var1);
+	up_read(&tomoyo_domain_acl_info_list_lock);
 	up_read(&tomoyo_domain_list_lock);
 	/***** READER SECTION END *****/
 	head->read_eof = done;
@@ -1511,6 +1596,71 @@ tail_mark:
 }
 
 /**
+ * tomoyo_cleanup_domain_policy - Clean up deleted domain policy.
+ */
+static void tomoyo_cleanup_domain_policy(void)
+{
+	struct tomoyo_domain_info *domain;
+	struct tomoyo_domain_info *next_domain;
+	struct tomoyo_acl_info *acl;
+	struct tomoyo_acl_info *next_acl;
+	LIST_HEAD(q_domain);
+	LIST_HEAD(q_acl);
+	/***** WRITER SECTION START *****/
+	down_write(&tomoyo_domain_list_lock);
+	list_for_each_entry_safe(domain, next_domain, &tomoyo_domain_list,
+				 entry.list) {
+		const bool can_delete_domain = domain->is_deleted &&
+			!atomic_read(&domain->entry.users);
+		if (can_delete_domain) {
+			list_for_each_entry(acl, &domain->acl_info_list,
+					    entry.list)
+				acl->type |= TOMOYO_ACL_DELETED;
+		}
+		list_for_each_entry_safe(acl, next_acl, &domain->acl_info_list,
+					 entry.list) {
+			if (!(acl->type & TOMOYO_ACL_DELETED)
+			    || atomic_read(&acl->entry.users))
+				continue;
+			list_del(&acl->entry.list);
+			list_add(&acl->entry.list, &q_acl);
+		}
+		if (can_delete_domain && list_empty(&domain->acl_info_list)) {
+			list_del(&domain->entry.list);
+			list_add(&domain->entry.list, &q_domain);
+		}
+	}
+	up_write(&tomoyo_domain_list_lock);
+	/***** WRITER SECTION END *****/
+	list_for_each_entry_safe(acl, next_acl, &q_acl, entry.list) {
+		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;
+		}
+		list_del(&acl->entry.list);
+		tomoyo_free_element(acl);
+	}
+	list_for_each_entry_safe(domain, next_domain, &q_domain, entry.list) {
+		tomoyo_put_name(domain->domainname);
+		list_del(&domain->entry.list);
+		tomoyo_free_element(domain);
+	}
+}
+
+/**
  * tomoyo_write_domain_profile - Assign profile for specified domain.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
@@ -1532,16 +1682,16 @@ static int tomoyo_write_domain_profile(s
 	if (!cp)
 		return -EINVAL;
 	*cp = '\0';
+	if (strict_strtoul(data, 10, &profile) ||
+	    profile >= TOMOYO_MAX_PROFILES)
+		return -EINVAL;
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_domain_list_lock);
 	domain = tomoyo_find_domain(cp + 1);
+	if (domain && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded))
+		domain->profile = (u8) profile;
 	up_read(&tomoyo_domain_list_lock);
 	/***** READER SECTION END *****/
-	if (strict_strtoul(data, 10, &profile))
-		return -EINVAL;
-	if (domain && profile < TOMOYO_MAX_PROFILES
-	    && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded))
-		domain->profile = (u8) profile;
 	return 0;
 }
 
@@ -1568,9 +1718,10 @@ static int tomoyo_read_domain_profile(st
 		return 0;
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_domain_list_lock);
+	tomoyo_put_ref(head->read_var1);
 	list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
 		struct tomoyo_domain_info *domain;
-		domain = list_entry(pos, struct tomoyo_domain_info, list);
+		domain = list_entry(pos, struct tomoyo_domain_info, entry.list);
 		if (domain->is_deleted)
 			continue;
 		done = tomoyo_io_printf(head, "%u %s\n", domain->profile,
@@ -1578,6 +1729,7 @@ static int tomoyo_read_domain_profile(st
 		if (!done)
 			break;
 	}
+	tomoyo_get_ref(head->read_var1);
 	up_read(&tomoyo_domain_list_lock);
 	/***** READER SECTION END *****/
 	head->read_eof = done;
@@ -1617,6 +1769,8 @@ static int tomoyo_read_pid(struct tomoyo
 		const int pid = head->read_step;
 		struct task_struct *p;
 		struct tomoyo_domain_info *domain = NULL;
+		/***** READER SECTION START *****/
+		down_read(&tomoyo_domain_list_lock);
 		/***** CRITICAL SECTION START *****/
 		read_lock(&tasklist_lock);
 		p = find_task_by_vpid(pid);
@@ -1627,6 +1781,8 @@ static int tomoyo_read_pid(struct tomoyo
 		if (domain)
 			tomoyo_io_printf(head, "%d %u %s", pid, domain->profile,
 					 domain->domainname->name);
+		up_read(&tomoyo_domain_list_lock);
+		/***** READER SECTION END *****/
 		head->read_eof = true;
 	}
 	return 0;
@@ -1803,7 +1959,7 @@ void tomoyo_load_policy(const char *file
 		struct tomoyo_domain_info *domain;
 		/***** READER SECTION START *****/
 		down_read(&tomoyo_domain_list_lock);
-		list_for_each_entry(domain, &tomoyo_domain_list, list) {
+		list_for_each_entry(domain, &tomoyo_domain_list, entry.list) {
 			const u8 profile = domain->profile;
 			if (tomoyo_profile_ptr[profile])
 				continue;
@@ -1945,6 +2101,10 @@ static int tomoyo_open_control(const u8 
 		}
 	}
 	file->private_data = head;
+	/* Get refcount. Though nothing happens. */
+	tomoyo_get_ref(head->write_var1);
+	tomoyo_get_ref(head->read_var1);
+	tomoyo_get_ref(head->read_var2);
 	/*
 	 * Call the handler now if the file is
 	 * /sys/kernel/security/tomoyo/self_domain
@@ -2062,15 +2222,30 @@ static int tomoyo_write_control(struct f
 static int tomoyo_close_control(struct file *file)
 {
 	struct tomoyo_io_buffer *head = file->private_data;
+	const bool is_write = head->write_buf != NULL;
 
 	/* Release memory used for policy I/O. */
 	tomoyo_free(head->read_buf);
 	head->read_buf = NULL;
 	tomoyo_free(head->write_buf);
 	head->write_buf = NULL;
+	/* Put refcount if partially read or written. */
+	tomoyo_put_ref(head->read_var2);
+	tomoyo_put_ref(head->read_var1);
+	tomoyo_put_ref(head->write_var1);
 	tomoyo_free(head);
 	head = NULL;
 	file->private_data = NULL;
+	if (is_write) {
+		tomoyo_cleanup_allow_read();
+		tomoyo_cleanup_file_pattern();
+		tomoyo_cleanup_no_rewrite();
+		tomoyo_cleanup_initializer();
+		tomoyo_cleanup_keep_domain();
+		tomoyo_cleanup_alias();
+		tomoyo_cleanup_manager();
+		tomoyo_cleanup_domain_policy();
+	}
 	return 0;
 }
 
--- security-testing-2.6.git.orig/security/tomoyo/common.h
+++ security-testing-2.6.git/security/tomoyo/common.h
@@ -23,6 +23,25 @@
 #include <linux/mount.h>
 #include <linux/list.h>
 
+struct tomoyo_entry {
+	struct list_head list;
+	atomic_t users;
+};
+
+static inline void tomoyo_get_ref(struct list_head *item)
+{
+	if (item)
+		atomic_dec(&(container_of(item, struct tomoyo_entry,
+					  list)->users));
+}
+
+static inline void tomoyo_put_ref(struct list_head *item)
+{
+	if (item)
+		atomic_inc(&(container_of(item, struct tomoyo_entry,
+					  list)->users));
+}
+
 struct dentry;
 struct vfsmount;
 
@@ -69,7 +88,7 @@ struct tomoyo_path_info_with_data {
  * without enlarging their structure size.
  */
 struct tomoyo_acl_info {
-	struct list_head list;
+	struct tomoyo_entry entry;
 	/*
 	 * Type of this ACL entry.
 	 *
@@ -83,30 +102,27 @@ struct tomoyo_acl_info {
 
 /* Structure for domain information. */
 struct tomoyo_domain_info {
-	struct list_head list;
+	struct tomoyo_entry entry;
 	struct list_head acl_info_list;
 	/* Name of this domain. Never NULL.          */
 	const struct tomoyo_path_info *domainname;
 	u8 profile;        /* Profile number to use. */
 	bool is_deleted;   /* Delete flag.           */
 	bool quota_warned; /* Quota warnning flag.   */
-	/* DOMAIN_FLAGS_*. Use tomoyo_set_domain_flag() to modify. */
-	u8 flags;
+	/* Ignore "allow_read" directive in exception policy. */
+	bool ignore_global_allow_read;
+	/*
+	 * This domain was unable to create a new domain at
+	 * tomoyo_find_next_domain() because the name of the domain to be
+	 * created was too long or it could not allocate memory.
+	 * More than one process continued execve() without domain transition.
+	 */
+	bool domain_transition_failed;
 };
 
 /* Profile number is an integer between 0 and 255. */
 #define TOMOYO_MAX_PROFILES 256
 
-/* Ignore "allow_read" directive in exception policy. */
-#define TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ 1
-/*
- * This domain was unable to create a new domain at tomoyo_find_next_domain()
- * because the name of the domain to be created was too long or
- * it could not allocate memory.
- * More than one process continued execve() without domain transition.
- */
-#define TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED        2
-
 /*
  * Structure for "allow_read/write", "allow_execute", "allow_read",
  * "allow_write", "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
@@ -164,7 +180,7 @@ struct tomoyo_io_buffer {
 	/* Extra variables for reading.         */
 	struct list_head *read_var2;
 	/* The position currently writing to.   */
-	struct tomoyo_domain_info *write_var1;
+	struct list_head *write_var1;
 	/* The step for reading.                */
 	int read_step;
 	/* Buffer for reading.                  */
@@ -229,8 +245,6 @@ const char *tomoyo_get_last_name(const s
 const char *tomoyo_get_msg(const bool is_enforce);
 /* Convert single path operation to operation name. */
 const char *tomoyo_sp2keyword(const u8 operation);
-/* Delete a domain. */
-int tomoyo_delete_domain(char *data);
 /* Create "alias" entry in exception policy. */
 int tomoyo_write_alias_policy(char *data, const bool is_delete);
 /*
@@ -270,9 +284,13 @@ unsigned int tomoyo_check_flags(const st
 void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
 /* Run policy loader when /sbin/init starts. */
 void tomoyo_load_policy(const char *filename);
-/* Change "struct tomoyo_domain_info"->flags. */
-void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
-			    const bool is_delete, const u8 flags);
+
+void tomoyo_cleanup_allow_read(void);
+void tomoyo_cleanup_file_pattern(void);
+void tomoyo_cleanup_no_rewrite(void);
+void tomoyo_cleanup_initializer(void);
+void tomoyo_cleanup_keep_domain(void);
+void tomoyo_cleanup_alias(void);
 
 /* strcmp() for "struct tomoyo_path_info" structure. */
 static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
--- security-testing-2.6.git.orig/security/tomoyo/domain.c
+++ security-testing-2.6.git/security/tomoyo/domain.c
@@ -25,7 +25,7 @@ DECLARE_RWSEM(tomoyo_domain_list_lock);
 
 /* Structure for "initialize_domain" and "no_initialize_domain" keyword. */
 struct tomoyo_domain_initializer_entry {
-	struct list_head list;
+	struct tomoyo_entry entry;
 	const struct tomoyo_path_info *domainname;    /* This may be NULL */
 	const struct tomoyo_path_info *program;
 	bool is_deleted;
@@ -36,7 +36,7 @@ struct tomoyo_domain_initializer_entry {
 
 /* Structure for "keep_domain" and "no_keep_domain" keyword. */
 struct tomoyo_domain_keeper_entry {
-	struct list_head list;
+	struct tomoyo_entry entry;
 	const struct tomoyo_path_info *domainname;
 	const struct tomoyo_path_info *program;       /* This may be NULL */
 	bool is_deleted;
@@ -47,37 +47,13 @@ struct tomoyo_domain_keeper_entry {
 
 /* Structure for "alias" keyword. */
 struct tomoyo_alias_entry {
-	struct list_head list;
+	struct tomoyo_entry entry;
 	const struct tomoyo_path_info *original_name;
 	const struct tomoyo_path_info *aliased_name;
 	bool is_deleted;
 };
 
 /**
- * tomoyo_set_domain_flag - Set or clear domain's attribute flags.
- *
- * @domain:    Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
- * @flags:     Flags to set or clear.
- *
- * Returns nothing.
- */
-void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
-			    const bool is_delete, const u8 flags)
-{
-	/* We need to serialize because this is bitfield operation. */
-	static DEFINE_SPINLOCK(lock);
-	/***** CRITICAL SECTION START *****/
-	spin_lock(&lock);
-	if (!is_delete)
-		domain->flags |= flags;
-	else
-		domain->flags &= ~flags;
-	spin_unlock(&lock);
-	/***** CRITICAL SECTION END *****/
-}
-
-/**
  * tomoyo_get_last_name - Get last component of a domainname.
  *
  * @domain: Pointer to "struct tomoyo_domain_info".
@@ -141,7 +117,7 @@ static int tomoyo_update_domain_initiali
 		new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
 	/***** WRITER SECTION START *****/
 	down_write(&tomoyo_domain_initializer_list_lock);
-	list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+	list_for_each_entry(ptr, &tomoyo_domain_initializer_list, entry.list) {
 		if (ptr->is_not != is_not ||
 		    ptr->domainname != saved_domainname ||
 		    ptr->program != saved_program)
@@ -157,7 +133,7 @@ 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,
+		list_add_tail(&new_entry->entry.list,
 			      &tomoyo_domain_initializer_list);
 		new_entry = NULL;
 		error = 0;
@@ -171,6 +147,33 @@ static int tomoyo_update_domain_initiali
 }
 
 /**
+ * tomoyo_cleanup_initializer - Clean up deleted "struct tomoyo_domain_initializer_entry".
+ */
+void tomoyo_cleanup_initializer(void)
+{
+	struct tomoyo_domain_initializer_entry *ptr;
+	struct tomoyo_domain_initializer_entry *tmp;
+	LIST_HEAD(q);
+	/***** WRITER SECTION START *****/
+	down_write(&tomoyo_domain_initializer_list_lock);
+	list_for_each_entry_safe(ptr, tmp, &tomoyo_domain_initializer_list,
+				 entry.list) {
+		if (!ptr->is_deleted || atomic_read(&ptr->entry.users))
+			continue;
+		list_del(&ptr->entry.list);
+		list_add(&ptr->entry.list, &q);
+	}
+	up_write(&tomoyo_domain_initializer_list_lock);
+	/***** WRITER SECTION END *****/
+	list_for_each_entry_safe(ptr, tmp, &q, entry.list) {
+		tomoyo_put_name(ptr->domainname);
+		tomoyo_put_name(ptr->program);
+		list_del(&ptr->entry.list);
+		tomoyo_free_element(ptr);
+	}
+}
+
+/**
  * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
@@ -184,6 +187,7 @@ bool tomoyo_read_domain_initializer_poli
 
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_domain_initializer_list_lock);
+	tomoyo_put_ref(head->read_var2);
 	list_for_each_cookie(pos, head->read_var2,
 			     &tomoyo_domain_initializer_list) {
 		const char *no;
@@ -191,7 +195,7 @@ bool tomoyo_read_domain_initializer_poli
 		const char *domain = "";
 		struct tomoyo_domain_initializer_entry *ptr;
 		ptr = list_entry(pos, struct tomoyo_domain_initializer_entry,
-				  list);
+				 entry.list);
 		if (ptr->is_deleted)
 			continue;
 		no = ptr->is_not ? "no_" : "";
@@ -206,6 +210,7 @@ bool tomoyo_read_domain_initializer_poli
 		if (!done)
 			break;
 	}
+	tomoyo_get_ref(head->read_var2);
 	up_read(&tomoyo_domain_initializer_list_lock);
 	/***** READER SECTION END *****/
 	return done;
@@ -256,7 +261,7 @@ static bool tomoyo_is_domain_initializer
 
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_domain_initializer_list_lock);
-	list_for_each_entry(ptr,  &tomoyo_domain_initializer_list, list) {
+	list_for_each_entry(ptr,  &tomoyo_domain_initializer_list, entry.list) {
 		if (ptr->is_deleted)
 			continue;
 		if (ptr->domainname) {
@@ -329,7 +334,7 @@ static int tomoyo_update_domain_keeper_e
 		new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
 	/***** WRITER SECTION START *****/
 	down_write(&tomoyo_domain_keeper_list_lock);
-	list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+	list_for_each_entry(ptr, &tomoyo_domain_keeper_list, entry.list) {
 		if (ptr->is_not != is_not ||
 		    ptr->domainname != saved_domainname ||
 		    ptr->program != saved_program)
@@ -345,7 +350,8 @@ 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(&new_entry->entry.list,
+			      &tomoyo_domain_keeper_list);
 		new_entry = NULL;
 		error = 0;
 	}
@@ -358,6 +364,33 @@ static int tomoyo_update_domain_keeper_e
 }
 
 /**
+ * tomoyo_cleanup_keep_domain - Clean up deleted "struct tomoyo_domain_keeper_entry".
+ */
+void tomoyo_cleanup_keep_domain(void)
+{
+	struct tomoyo_domain_keeper_entry *ptr;
+	struct tomoyo_domain_keeper_entry *tmp;
+	LIST_HEAD(q);
+	/***** WRITER SECTION START *****/
+	down_write(&tomoyo_domain_keeper_list_lock);
+	list_for_each_entry_safe(ptr, tmp, &tomoyo_domain_keeper_list,
+				 entry.list) {
+		if (!ptr->is_deleted || atomic_read(&ptr->entry.users))
+			continue;
+		list_del(&ptr->entry.list);
+		list_add(&ptr->entry.list, &q);
+	}
+	up_write(&tomoyo_domain_keeper_list_lock);
+	/***** WRITER SECTION END *****/
+	list_for_each_entry_safe(ptr, tmp, &q, entry.list) {
+		tomoyo_put_name(ptr->domainname);
+		tomoyo_put_name(ptr->program);
+		list_del(&ptr->entry.list);
+		tomoyo_free_element(ptr);
+	}
+}
+
+/**
  * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list.
  *
  * @data:      String to parse.
@@ -392,6 +425,7 @@ bool tomoyo_read_domain_keeper_policy(st
 
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_domain_keeper_list_lock);
+	tomoyo_put_ref(head->read_var2);
 	list_for_each_cookie(pos, head->read_var2,
 			     &tomoyo_domain_keeper_list) {
 		struct tomoyo_domain_keeper_entry *ptr;
@@ -399,7 +433,8 @@ bool tomoyo_read_domain_keeper_policy(st
 		const char *from = "";
 		const char *program = "";
 
-		ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, list);
+		ptr = list_entry(pos, struct tomoyo_domain_keeper_entry,
+				 entry.list);
 		if (ptr->is_deleted)
 			continue;
 		no = ptr->is_not ? "no_" : "";
@@ -414,6 +449,7 @@ bool tomoyo_read_domain_keeper_policy(st
 		if (!done)
 			break;
 	}
+	tomoyo_get_ref(head->read_var2);
 	up_read(&tomoyo_domain_keeper_list_lock);
 	/***** READER SECTION END *****/
 	return done;
@@ -438,7 +474,7 @@ static bool tomoyo_is_domain_keeper(cons
 
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_domain_keeper_list_lock);
-	list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+	list_for_each_entry(ptr, &tomoyo_domain_keeper_list, entry.list) {
 		if (ptr->is_deleted)
 			continue;
 		if (!ptr->is_last_name) {
@@ -498,7 +534,7 @@ static int tomoyo_update_alias_entry(con
 		new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
 	/***** WRITER SECTION START *****/
 	down_write(&tomoyo_alias_list_lock);
-	list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+	list_for_each_entry(ptr, &tomoyo_alias_list, entry.list) {
 		if (ptr->original_name != saved_original_name ||
 		    ptr->aliased_name != saved_aliased_name)
 			continue;
@@ -511,7 +547,7 @@ 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(&new_entry->entry.list, &tomoyo_alias_list);
 		new_entry = NULL;
 		error = 0;
 	}
@@ -524,6 +560,32 @@ static int tomoyo_update_alias_entry(con
 }
 
 /**
+ * tomoyo_cleanup_alias - Clean up deleted "struct tomoyo_alias_entry".
+ */
+void tomoyo_cleanup_alias(void)
+{
+	struct tomoyo_alias_entry *ptr;
+	struct tomoyo_alias_entry *tmp;
+	LIST_HEAD(q);
+	/***** WRITER SECTION START *****/
+	down_write(&tomoyo_alias_list_lock);
+	list_for_each_entry_safe(ptr, tmp, &tomoyo_alias_list, entry.list) {
+		if (!ptr->is_deleted || atomic_read(&ptr->entry.users))
+			continue;
+		list_del(&ptr->entry.list);
+		list_add(&ptr->entry.list, &q);
+	}
+	up_write(&tomoyo_alias_list_lock);
+	/***** WRITER SECTION END *****/
+	list_for_each_entry_safe(ptr, tmp, &q, entry.list) {
+		tomoyo_put_name(ptr->original_name);
+		tomoyo_put_name(ptr->aliased_name);
+		list_del(&ptr->entry.list);
+		tomoyo_free_element(ptr);
+	}
+}
+
+/**
  * tomoyo_read_alias_policy - Read "struct tomoyo_alias_entry" list.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
@@ -537,10 +599,11 @@ bool tomoyo_read_alias_policy(struct tom
 
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_alias_list_lock);
+	tomoyo_put_ref(head->read_var2);
 	list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
 		struct tomoyo_alias_entry *ptr;
 
-		ptr = list_entry(pos, struct tomoyo_alias_entry, list);
+		ptr = list_entry(pos, struct tomoyo_alias_entry, entry.list);
 		if (ptr->is_deleted)
 			continue;
 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n",
@@ -549,6 +612,7 @@ bool tomoyo_read_alias_policy(struct tom
 		if (!done)
 			break;
 	}
+	tomoyo_get_ref(head->read_var2);
 	up_read(&tomoyo_alias_list_lock);
 	/***** READER SECTION END *****/
 	return done;
@@ -572,40 +636,6 @@ int tomoyo_write_alias_policy(char *data
 	return tomoyo_update_alias_entry(data, cp, is_delete);
 }
 
-/* Domain create/delete handler. */
-
-/**
- * tomoyo_delete_domain - Delete a domain.
- *
- * @domainname: The name of domain.
- *
- * Returns 0.
- */
-int tomoyo_delete_domain(char *domainname)
-{
-	struct tomoyo_domain_info *domain;
-	struct tomoyo_path_info name;
-
-	name.name = domainname;
-	tomoyo_fill_path_info(&name);
-	/***** WRITER SECTION START *****/
-	down_write(&tomoyo_domain_list_lock);
-	/* Is there an active domain? */
-	list_for_each_entry(domain, &tomoyo_domain_list, list) {
-		/* Never delete tomoyo_kernel_domain */
-		if (domain == &tomoyo_kernel_domain)
-			continue;
-		if (domain->is_deleted ||
-		    tomoyo_pathcmp(domain->domainname, &name))
-			continue;
-		domain->is_deleted = true;
-		break;
-	}
-	up_write(&tomoyo_domain_list_lock);
-	/***** WRITER SECTION END *****/
-	return 0;
-}
-
 /**
  * tomoyo_find_or_assign_new_domain - Create a domain.
  *
@@ -619,79 +649,56 @@ struct tomoyo_domain_info *tomoyo_find_o
 							    const u8 profile)
 {
 	struct tomoyo_domain_info *new_domain = NULL;
-	struct tomoyo_domain_info *domain;
-	const struct tomoyo_path_info *saved_domainname;
+	struct tomoyo_domain_info *domain = NULL;
+	const struct tomoyo_path_info *saved_domainname = NULL;
+	bool found = false;
 
+	up_read(&tomoyo_domain_list_lock);
+	/***** READER SECTION END *****/
 	if (!tomoyo_is_correct_domain(domainname, __func__))
-		return NULL;
+		goto out;
 	saved_domainname = tomoyo_get_name(domainname);
 	if (!saved_domainname)
-		return NULL;
+		goto out;
 	new_domain = kmalloc(sizeof(*new_domain), GFP_KERNEL);
 	/***** WRITER SECTION START *****/
 	down_write(&tomoyo_domain_list_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 ||
+	list_for_each_entry(domain, &tomoyo_domain_list, entry.list) {
+		if (domain->is_deleted ||
 		    domain->domainname != saved_domainname)
 			continue;
-		flag = false;
-		/***** CRITICAL SECTION START *****/
-		read_lock(&tasklist_lock);
-		for_each_process(p) {
-			if (tomoyo_real_domain(p) != domain)
-				continue;
-			flag = true;
-			break;
-		}
-		read_unlock(&tasklist_lock);
-		/***** CRITICAL SECTION END *****/
-		if (flag)
-			continue;
-		list_for_each_entry(ptr, &domain->acl_info_list, list) {
-			ptr->type |= TOMOYO_ACL_DELETED;
-		}
-		tomoyo_set_domain_flag(domain, true, domain->flags);
-		domain->profile = profile;
-		domain->quota_warned = false;
-		mb(); /* Avoid out-of-order execution. */
-		domain->is_deleted = false;
-		goto out;
+		found = true;
+		break;
 	}
-	/* No memory reusable. Create using new memory. */
-	if (tomoyo_memory_ok(new_domain)) {
+	if (!found && 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(&domain->entry.list, &tomoyo_domain_list);
+		found = true;
 	}
- out:
 	up_write(&tomoyo_domain_list_lock);
 	/***** WRITER SECTION END *****/
+ out:
 	tomoyo_put_name(saved_domainname);
 	kfree(new_domain);
+	/***** READER SECTION START *****/
+	down_read(&tomoyo_domain_list_lock);
+	return found ? domain : NULL;
 	return domain;
 }
 
 /**
  * tomoyo_find_next_domain - Find a domain.
  *
- * @bprm:           Pointer to "struct linux_binprm".
- * @next_domain:    Pointer to pointer to "struct tomoyo_domain_info".
+ * @bprm: Pointer to "struct linux_binprm".
  *
  * Returns 0 on success, negative value otherwise.
  */
-int tomoyo_find_next_domain(struct linux_binprm *bprm,
-			    struct tomoyo_domain_info **next_domain)
+int tomoyo_find_next_domain(struct linux_binprm *bprm)
 {
 	/*
 	 * This function assumes that the size of buffer returned by
@@ -752,7 +759,7 @@ int tomoyo_find_next_domain(struct linux
 		/* Is this program allowed to be called via symbolic links? */
 		/***** READER SECTION START *****/
 		down_read(&tomoyo_alias_list_lock);
-		list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+		list_for_each_entry(ptr, &tomoyo_alias_list, entry.list) {
 			if (ptr->is_deleted ||
 			    tomoyo_pathcmp(&r, ptr->original_name) ||
 			    tomoyo_pathcmp(&s, ptr->aliased_name))
@@ -793,33 +800,33 @@ int tomoyo_find_next_domain(struct linux
 		snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
 			 "%s %s", old_domain_name, real_program_name);
 	}
-	if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
-		goto done;
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_domain_list_lock);
+	if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
+		goto done;
 	domain = tomoyo_find_domain(new_domain_name);
+	if (!domain && !is_enforce)
+		domain = tomoyo_find_or_assign_new_domain(new_domain_name,
+							  old_domain->profile);
+ done:
+	if (!domain) {
+		printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",
+		       new_domain_name);
+		if (is_enforce)
+			retval = -EPERM;
+		else
+			old_domain->domain_transition_failed = true;
+	}
+	if (!domain)
+		domain = old_domain;
+	BUG_ON(bprm->cred->security);
+	atomic_inc(&domain->entry.users);
+	bprm->cred->security = domain;
 	up_read(&tomoyo_domain_list_lock);
 	/***** READER SECTION END *****/
-	if (domain)
-		goto done;
-	if (is_enforce)
-		goto done;
-	domain = tomoyo_find_or_assign_new_domain(new_domain_name,
-						  old_domain->profile);
- done:
-	if (domain)
-		goto out;
-	printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",
-	       new_domain_name);
-	if (is_enforce)
-		retval = -EPERM;
-	else
-		tomoyo_set_domain_flag(old_domain, false,
-				       TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED);
  out:
 	tomoyo_free(real_program_name);
 	tomoyo_free(symlink_program_name);
-	*next_domain = domain ? domain : old_domain;
 	tomoyo_free(tmp);
 	return retval;
 }
--- security-testing-2.6.git.orig/security/tomoyo/file.c
+++ security-testing-2.6.git/security/tomoyo/file.c
@@ -16,21 +16,21 @@
 
 /* Structure for "allow_read" keyword. */
 struct tomoyo_globally_readable_file_entry {
-	struct list_head list;
+	struct tomoyo_entry entry;
 	const struct tomoyo_path_info *filename;
 	bool is_deleted;
 };
 
 /* Structure for "file_pattern" keyword. */
 struct tomoyo_pattern_entry {
-	struct list_head list;
+	struct tomoyo_entry entry;
 	const struct tomoyo_path_info *pattern;
 	bool is_deleted;
 };
 
 /* Structure for "deny_rewrite" keyword. */
 struct tomoyo_no_rewrite_entry {
-	struct list_head list;
+	struct tomoyo_entry entry;
 	const struct tomoyo_path_info *pattern;
 	bool is_deleted;
 };
@@ -170,7 +170,7 @@ static int tomoyo_update_globally_readab
 		new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
 	/***** WRITER SECTION START *****/
 	down_write(&tomoyo_globally_readable_list_lock);
-	list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+	list_for_each_entry(ptr, &tomoyo_globally_readable_list, entry.list) {
 		if (ptr->filename != saved_filename)
 			continue;
 		ptr->is_deleted = is_delete;
@@ -180,7 +180,8 @@ 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(&new_entry->entry.list,
+			      &tomoyo_globally_readable_list);
 		new_entry = NULL;
 		error = 0;
 	}
@@ -192,6 +193,32 @@ static int tomoyo_update_globally_readab
 }
 
 /**
+ * tomoyo_cleanup_allow_read - Clean up deleted "struct tomoyo_globally_readable_file_entry".
+ */
+void tomoyo_cleanup_allow_read(void)
+{
+	struct tomoyo_globally_readable_file_entry *ptr;
+	struct tomoyo_globally_readable_file_entry *tmp;
+	LIST_HEAD(q);
+	/***** WRITER SECTION START *****/
+	down_write(&tomoyo_globally_readable_list_lock);
+	list_for_each_entry_safe(ptr, tmp, &tomoyo_globally_readable_list,
+				 entry.list) {
+		if (!ptr->is_deleted || atomic_read(&ptr->entry.users))
+			continue;
+		list_del(&ptr->entry.list);
+		list_add(&ptr->entry.list, &q);
+	}
+	up_write(&tomoyo_globally_readable_list_lock);
+	/***** WRITER SECTION END *****/
+	list_for_each_entry_safe(ptr, tmp, &q, entry.list) {
+		tomoyo_put_name(ptr->filename);
+		list_del(&ptr->entry.list);
+		tomoyo_free_element(ptr);
+	}
+}
+
+/**
  * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
  *
  * @filename: The filename to check.
@@ -205,7 +232,7 @@ static bool tomoyo_is_globally_readable_
 	bool found = false;
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_globally_readable_list_lock);
-	list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+	list_for_each_entry(ptr, &tomoyo_globally_readable_list, entry.list) {
 		if (!ptr->is_deleted &&
 		    tomoyo_path_matches_pattern(filename, ptr->filename)) {
 			found = true;
@@ -244,12 +271,13 @@ bool tomoyo_read_globally_readable_polic
 
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_globally_readable_list_lock);
+	tomoyo_put_ref(head->read_var2);
 	list_for_each_cookie(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,
-				 list);
+				 entry.list);
 		if (ptr->is_deleted)
 			continue;
 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
@@ -257,6 +285,7 @@ bool tomoyo_read_globally_readable_polic
 		if (!done)
 			break;
 	}
+	tomoyo_get_ref(head->read_var2);
 	up_read(&tomoyo_globally_readable_list_lock);
 	/***** READER SECTION END *****/
 	return done;
@@ -291,7 +320,7 @@ static int tomoyo_update_file_pattern_en
 		new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
 	/***** WRITER SECTION START *****/
 	down_write(&tomoyo_pattern_list_lock);
-	list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+	list_for_each_entry(ptr, &tomoyo_pattern_list, entry.list) {
 		if (saved_pattern != ptr->pattern)
 			continue;
 		ptr->is_deleted = is_delete;
@@ -301,7 +330,7 @@ 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(&new_entry->entry.list, &tomoyo_pattern_list);
 		new_entry = NULL;
 		error = 0;
 	}
@@ -313,38 +342,78 @@ static int tomoyo_update_file_pattern_en
 }
 
 /**
+ * tomoyo_cleanup_file_pattern - Clean up deleted "struct tomoyo_pattern_entry".
+ */
+void tomoyo_cleanup_file_pattern(void)
+{
+	struct tomoyo_pattern_entry *ptr;
+	struct tomoyo_pattern_entry *tmp;
+	LIST_HEAD(q);
+	/***** WRITER SECTION START *****/
+	down_write(&tomoyo_pattern_list_lock);
+	list_for_each_entry_safe(ptr, tmp, &tomoyo_pattern_list, entry.list) {
+		if (!ptr->is_deleted || atomic_read(&ptr->entry.users))
+			continue;
+		list_del(&ptr->entry.list);
+		list_add(&ptr->entry.list, &q);
+	}
+	up_write(&tomoyo_pattern_list_lock);
+	/***** WRITER SECTION END *****/
+	list_for_each_entry_safe(ptr, tmp, &q, entry.list) {
+		tomoyo_put_name(ptr->pattern);
+		list_del(&ptr->entry.list);
+		tomoyo_free_element(ptr);
+	}
+}
+
+/**
  * tomoyo_get_file_pattern - Get patterned pathname.
  *
  * @filename: The filename to find patterned pathname.
  *
- * Returns pointer to pathname pattern if matched, @filename otherwise.
+ * Returns pointer to "struct tomoyo_pattern_entry" if matched, NULL otherwis \
+e.
+ *
+ * Caller must call tomoyo_put_file_pattern() if this function didn't return
+ * NULL.
  */
-static const struct tomoyo_path_info *
+static struct tomoyo_pattern_entry *
 tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
 {
 	struct tomoyo_pattern_entry *ptr;
-	const struct tomoyo_path_info *pattern = NULL;
+	struct tomoyo_pattern_entry *pattern = NULL;
 
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_pattern_list_lock);
-	list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+	list_for_each_entry(ptr, &tomoyo_pattern_list, entry.list) {
 		if (ptr->is_deleted)
 			continue;
 		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
 			continue;
-		pattern = ptr->pattern;
-		if (tomoyo_strendswith(pattern->name, "/\\*")) {
+		pattern = ptr;
+		if (tomoyo_strendswith(ptr->pattern->name, "/\\*")) {
 			/* Do nothing. Try to find the better match. */
 		} else {
 			/* This would be the better match. Use this. */
 			break;
 		}
 	}
+	if (pattern)
+		atomic_inc(&pattern->entry.users);
+	up_read(&tomoyo_pattern_list_lock);
+	/***** READER SECTION END *****/
+	return pattern;
+}
+
+static void tomoyo_put_file_pattern(struct tomoyo_pattern_entry *name)
+{
+	if (!name)
+		return;
+	/***** READER SECTION START *****/
+	down_read(&tomoyo_pattern_list_lock);
+	atomic_dec(&name->entry.users);
 	up_read(&tomoyo_pattern_list_lock);
 	/***** READER SECTION END *****/
-	if (pattern)
-		filename = pattern;
-	return filename;
 }
 
 /**
@@ -374,9 +443,10 @@ bool tomoyo_read_file_pattern(struct tom
 
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_pattern_list_lock);
+	tomoyo_put_ref(head->read_var2);
 	list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
 		struct tomoyo_pattern_entry *ptr;
-		ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
+		ptr = list_entry(pos, struct tomoyo_pattern_entry, entry.list);
 		if (ptr->is_deleted)
 			continue;
 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN
@@ -384,6 +454,7 @@ bool tomoyo_read_file_pattern(struct tom
 		if (!done)
 			break;
 	}
+	tomoyo_get_ref(head->read_var2);
 	up_read(&tomoyo_pattern_list_lock);
 	/***** READER SECTION END *****/
 	return done;
@@ -418,7 +489,7 @@ static int tomoyo_update_no_rewrite_entr
 		new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
 	/***** WRITER SECTION START *****/
 	down_write(&tomoyo_no_rewrite_list_lock);
-	list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+	list_for_each_entry(ptr, &tomoyo_no_rewrite_list, entry.list) {
 		if (ptr->pattern != saved_pattern)
 			continue;
 		ptr->is_deleted = is_delete;
@@ -428,7 +499,7 @@ 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(&new_entry->entry.list, &tomoyo_no_rewrite_list);
 		new_entry = NULL;
 		error = 0;
 	}
@@ -440,6 +511,32 @@ static int tomoyo_update_no_rewrite_entr
 }
 
 /**
+ * tomoyo_cleanup_no_rewrite - Clean up deleted "struct tomoyo_no_rewrite_entry".
+ */
+void tomoyo_cleanup_no_rewrite(void)
+{
+	struct tomoyo_no_rewrite_entry *ptr;
+	struct tomoyo_no_rewrite_entry *tmp;
+	LIST_HEAD(q);
+	/***** WRITER SECTION START *****/
+	down_write(&tomoyo_no_rewrite_list_lock);
+	list_for_each_entry_safe(ptr, tmp, &tomoyo_no_rewrite_list,
+				 entry.list) {
+		if (!ptr->is_deleted || atomic_read(&ptr->entry.users))
+			continue;
+		list_del(&ptr->entry.list);
+		list_add(&ptr->entry.list, &q);
+	}
+	up_write(&tomoyo_no_rewrite_list_lock);
+	/***** WRITER SECTION END *****/
+	list_for_each_entry_safe(ptr, tmp, &q, entry.list) {
+		tomoyo_put_name(ptr->pattern);
+		list_del(&ptr->entry.list);
+		tomoyo_free_element(ptr);
+	}
+}
+
+/**
  * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
  *
  * @filename: Filename to check.
@@ -454,7 +551,7 @@ static bool tomoyo_is_no_rewrite_file(co
 
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_no_rewrite_list_lock);
-	list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+	list_for_each_entry(ptr, &tomoyo_no_rewrite_list, entry.list) {
 		if (ptr->is_deleted)
 			continue;
 		if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -494,9 +591,11 @@ bool tomoyo_read_no_rewrite_policy(struc
 
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_no_rewrite_list_lock);
+	tomoyo_put_ref(head->read_var2);
 	list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
 		struct tomoyo_no_rewrite_entry *ptr;
-		ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
+		ptr = list_entry(pos, struct tomoyo_no_rewrite_entry,
+				 entry.list);
 		if (ptr->is_deleted)
 			continue;
 		done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE
@@ -504,6 +603,7 @@ bool tomoyo_read_no_rewrite_policy(struc
 		if (!done)
 			break;
 	}
+	tomoyo_get_ref(head->read_var2);
 	up_read(&tomoyo_no_rewrite_list_lock);
 	/***** READER SECTION END *****/
 	return done;
@@ -573,7 +673,7 @@ static int tomoyo_check_single_path_acl2
 
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_domain_acl_info_list_lock);
-	list_for_each_entry(ptr, &domain->acl_info_list, list) {
+	list_for_each_entry(ptr, &domain->acl_info_list, entry.list) {
 		struct tomoyo_single_path_acl_record *acl;
 		if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
 			continue;
@@ -650,8 +750,7 @@ static int tomoyo_check_file_perm2(struc
 	if (!filename)
 		return 0;
 	error = tomoyo_check_file_acl(domain, filename, perm);
-	if (error && perm == 4 &&
-	    (domain->flags & TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ) == 0
+	if (error && perm == 4 && !domain->ignore_global_allow_read
 	    && tomoyo_is_globally_readable_file(filename))
 		error = 0;
 	if (perm == 6)
@@ -673,11 +772,13 @@ static int tomoyo_check_file_perm2(struc
 	if (is_enforce)
 		return error;
 	if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
+		struct tomoyo_pattern_entry *p = NULL;
 		/* Don't use patterns for execute permission. */
-		const struct tomoyo_path_info *patterned_file = (perm != 1) ?
-			tomoyo_get_file_pattern(filename) : filename;
-		tomoyo_update_file_acl(patterned_file->name, perm,
-				       domain, false);
+		if (perm != 1)
+			p = tomoyo_get_file_pattern(filename);
+		tomoyo_update_file_acl(p ? p->pattern->name : filename->name,
+				       perm, domain, false);
+		tomoyo_put_file_pattern(p);
 	}
 	return 0;
 }
@@ -763,7 +864,7 @@ static int tomoyo_update_single_path_acl
 	down_write(&tomoyo_domain_acl_info_list_lock);
 	if (is_delete)
 		goto delete;
-	list_for_each_entry(ptr, &domain->acl_info_list, list) {
+	list_for_each_entry(ptr, &domain->acl_info_list, entry.list) {
 		struct tomoyo_single_path_acl_record *acl;
 		if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
 			continue;
@@ -791,13 +892,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(&new_entry->head.entry.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(ptr, &domain->acl_info_list, entry.list) {
 		struct tomoyo_single_path_acl_record *acl;
 		if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
 			continue;
@@ -864,7 +966,7 @@ static int tomoyo_update_double_path_acl
 	down_write(&tomoyo_domain_acl_info_list_lock);
 	if (is_delete)
 		goto delete;
-	list_for_each_entry(ptr, &domain->acl_info_list, list) {
+	list_for_each_entry(ptr, &domain->acl_info_list, entry.list) {
 		struct tomoyo_double_path_acl_record *acl;
 		if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
 			continue;
@@ -889,13 +991,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(&new_entry->head.entry.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(ptr, &domain->acl_info_list, entry.list) {
 		struct tomoyo_double_path_acl_record *acl;
 		if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
 			continue;
@@ -962,7 +1065,7 @@ static int tomoyo_check_double_path_acl(
 		return 0;
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_domain_acl_info_list_lock);
-	list_for_each_entry(ptr, &domain->acl_info_list, list) {
+	list_for_each_entry(ptr, &domain->acl_info_list, entry.list) {
 		struct tomoyo_double_path_acl_record *acl;
 		if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
 			continue;
@@ -1013,8 +1116,12 @@ static int tomoyo_check_single_path_perm
 		       tomoyo_get_msg(is_enforce), msg, filename->name,
 		       tomoyo_get_last_name(domain));
 	if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
-		const char *name = tomoyo_get_file_pattern(filename)->name;
-		tomoyo_update_single_path_acl(operation, name, domain, false);
+		struct tomoyo_pattern_entry *p
+			= tomoyo_get_file_pattern(filename);
+		tomoyo_update_single_path_acl(operation,
+					      p ? p->pattern->name :
+					      filename->name, domain, false);
+		tomoyo_put_file_pattern(p);
 	}
 	if (!is_enforce)
 		error = 0;
@@ -1260,10 +1367,18 @@ int tomoyo_check_2path_perm(struct tomoy
 		       msg, buf1->name, buf2->name,
 		       tomoyo_get_last_name(domain));
 	if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
-		const char *name1 = tomoyo_get_file_pattern(buf1)->name;
-		const char *name2 = tomoyo_get_file_pattern(buf2)->name;
-		tomoyo_update_double_path_acl(operation, name1, name2, domain,
+		struct tomoyo_pattern_entry *p1
+			= tomoyo_get_file_pattern(buf1);
+		struct tomoyo_pattern_entry *p2
+			= tomoyo_get_file_pattern(buf2);
+		tomoyo_update_double_path_acl(operation,
+					      p1 ? p1->pattern->name :
+					      buf1->name,
+					      p2 ? p2->pattern->name :
+					      buf2->name, domain,
 					      false);
+		tomoyo_put_file_pattern(p1);
+		tomoyo_put_file_pattern(p2);
 	}
  out:
 	tomoyo_free(buf1);
--- security-testing-2.6.git.orig/security/tomoyo/realpath.c
+++ security-testing-2.6.git/security/tomoyo/realpath.c
@@ -223,6 +223,17 @@ bool tomoyo_memory_ok(void *ptr)
 	return false;
 }
 
+/**
+ * tomoyo_free_element - Free memory for elements.
+ *
+ * @ptr: Pointer to allocated memory.
+ */
+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,16 +249,13 @@ static unsigned int tomoyo_quota_for_sav
 /* Structure for string data. */
 struct tomoyo_name_entry {
 	struct list_head list;
+	atomic_t users;
 	struct tomoyo_path_info entry;
 };
 
-/*
- * The list for "struct tomoyo_name_entry".
- *
- * This list is updated only inside tomoyo_get_name(), thus
- * no global mutex exists.
- */
+/* The list for "struct tomoyo_name_entry". */
 static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+static DEFINE_MUTEX(tomoyo_name_list_lock);
 
 /**
  * tomoyo_get_name - Allocate permanent memory for string data.
@@ -255,8 +263,6 @@ static struct list_head tomoyo_name_list
  * @name: The string to store into the permernent memory.
  *
  * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
- *
- * 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)
 {
@@ -278,11 +284,12 @@ const struct tomoyo_path_info *tomoyo_ge
 	entry = kmalloc(sizeof(*entry) + len, GFP_KERNEL);
 	allocated_len = entry ? ksize(entry) : 0;
 	/***** EXCLUSIVE SECTION START *****/
-	mutex_lock(&lock);
+	mutex_lock(&tomoyo_name_list_lock);
 	list_for_each_entry(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;
 	}
@@ -297,12 +304,13 @@ 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);
+		atomic_set(&ptr->users, 1);
 		list_add_tail(&ptr->list,
 			      &tomoyo_name_list[hash % TOMOYO_MAX_HASH]);
 		entry = NULL;
 		error = 0;
 	}
-	mutex_unlock(&lock);
+	mutex_unlock(&tomoyo_name_list_lock);
 	/***** EXCLUSIVE SECTION END *****/
 	kfree(entry);
 	if (!error)
@@ -314,6 +322,33 @@ 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);
+	/***** EXCLUSIVE SECTION START *****/
+	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);
+	/***** EXCLUSIVE SECTION END *****/
+	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)
@@ -325,7 +360,7 @@ void __init tomoyo_realpath_init(void)
 		INIT_LIST_HEAD(&tomoyo_name_list[i]);
 	INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
 	tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME);
-	list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
+	list_add_tail(&tomoyo_kernel_domain.entry.list, &tomoyo_domain_list);
 	/***** READER SECTION START *****/
 	down_read(&tomoyo_domain_list_lock);
 	if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
--- 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);
@@ -55,6 +52,8 @@ void *tomoyo_alloc(const size_t size);
 /* Free memory allocated by tomoyo_alloc(). */
 void tomoyo_free(const void *p);
 
+void tomoyo_free_element(void *p);
+
 /* Check for memory usage. */
 int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
 
--- 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)->entry.users);
 	return 0;
 }
 
+static void tomoyo_cred_free(struct cred *cred)
+{
+	struct tomoyo_domain_info *domain = cred->security;
+	if (domain)
+		atomic_dec(&domain->entry.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)->entry.users);
+		bprm->cred->security = NULL;
+	}
 	return 0;
 }
 
@@ -61,14 +75,8 @@ static int tomoyo_bprm_check_security(st
 	 * Execute permission is checked against pathname passed to do_execve()
 	 * using current domain.
 	 */
-	if (!domain) {
-		struct tomoyo_domain_info *next_domain = NULL;
-		int retval = tomoyo_find_next_domain(bprm, &next_domain);
-
-		if (!retval)
-			bprm->cred->security = next_domain;
-		return retval;
-	}
+	if (!domain)
+		return tomoyo_find_next_domain(bprm);
 	/*
 	 * Read permission is checked against interpreters using next domain.
 	 * '1' is the result of open_to_namei_flags(O_RDONLY).
@@ -265,6 +273,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
@@ -293,6 +302,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.entry.users);
 	tomoyo_realpath_init();
 	return 0;
 }
--- security-testing-2.6.git.orig/security/tomoyo/tomoyo.h
+++ security-testing-2.6.git/security/tomoyo/tomoyo.h
@@ -33,8 +33,7 @@ int tomoyo_check_2path_perm(struct tomoy
 			    struct path *path2);
 int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
 				    struct file *filp);
-int tomoyo_find_next_domain(struct linux_binprm *bprm,
-			    struct tomoyo_domain_info **next_domain);
+int tomoyo_find_next_domain(struct linux_binprm *bprm);
 
 /* Index numbers for Access Controls. */
 
--
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