[TOMOYO] Add garbage collector support.

Tetsuo Handa
2009-05-07T11:42:34Z

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.

Memory Management Rules:

When reading list elements, a process takes tomoyo_policy_lock for reading.
When writing list elements, a process takes tomoyo_policy_lock for writing.

When a process finished reading list elements, a garbage collector function
is called after the process released tomoyo_policy_lock.
The garbage collector function takes tomoyo_policy_lock for writing and
traverses the lists and releases elements if that element has is_deleted flag
set.

Since strings are likely referenced by multiple list elements, this patch
assigns a refcounter to each string.
The policy add/del function increments the refcounter only if the element is
added to the list. The garbage collector function decrements the refcounter
only if the element is not referred by a process.
The garbage collector function releases memory of the string if the string's
refcounter becomes 0.

"struct tomoyo_io_buffer" remembers pointer to list elements.
Thus, TOMOYO memorizes the address of "struct tomoyo_io_buffer"->read_var1,
"struct tomoyo_io_buffer"->read_var2, "struct tomoyo_io_buffer"->write_var1
when a file in /sys/kernel/security/tomoyo/ interface is opened and forgets
them when that file is closed.

"struct task_struct"->cred->security also remembers pointer to list elements.
Thus, TOMOYO memorizes the address of cred->security at
security_cred_prepare() and forgets it at security_cred_free().

tomoyo_update_cookie() is called with tomoyo_policy_lock held for reading
when the pointer refers a different list element.

tomoyo_used_by_cookie() is called with tomoyo_policy_lock held for writing
to check whether the pointer is referenced by a cookie or not.

Now, TOMOYO can release memory. :-)

Attached patch is for security-testing-2.6#next.
James, it is possible to apply below commits to security-testing-2.6#next ?
I get a lot of warning messages in my environment.

39826a1e17c1957bd7b5cd7815b83940e5e3a230 tomoyo: version bump to 2.2.0.
17a7b7b39056a82c5012539311850f202e6c3cd4 tomoyo: add Documentation/tomoyo.txt
05fa199d45c54a9bda7aa3ae6537253d6f097aa9 mm: pass correct mm when growing stack
05f54c13cd0c33694eec39a265475c5d6cf223cf Revert "kobject: don't block for each kobject_uevent".
a964e33c5d7c0ea46376d20c2f02edf01c9db251 x86: clean up old gcc warnings

Regards.
--------------------
Subject: [TOMOYO] Add garbage collector support.

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.

Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp>
---
security/tomoyo/common.c | 259 ++++++++++++++++++++++++++++----------------
security/tomoyo/common.h | 37 ++++--
security/tomoyo/domain.c | 237 ++++++++++++++++++++++++----------------
security/tomoyo/file.c | 188 +++++++++++++++++++++++---------
security/tomoyo/realpath.c | 264 ++++++++++++++++++++++++++-------------------
security/tomoyo/realpath.h | 43 +++++--
security/tomoyo/tomoyo.c | 40 ++++--
security/tomoyo/tomoyo.h | 13 --
8 files changed, 689 insertions(+), 392 deletions(-)

--- security-testing-2.6.git.orig/security/tomoyo/common.c
+++ security-testing-2.6.git/security/tomoyo/common.c
@@ -12,8 +12,8 @@
#include <linux/uaccess.h>
#include <linux/security.h>
#include <linux/hardirq.h>
-#include "realpath.h"
#include "common.h"
+#include "realpath.h"
#include "tomoyo.h"

/* Has loading policy done? */
@@ -326,13 +326,13 @@ bool tomoyo_is_domain_def(const unsigned
* tomoyo_find_domain - Find a domain by the given name.
*
* @domainname: The domainname to find.
+ * @cookie: Pointer to "struct tomoyo_cookie".
*
- * Caller must call down_read(&tomoyo_domain_list_lock); or
- * down_write(&tomoyo_domain_list_lock); .
+ * Caller must hold tomoyo_policy_lock.
*
- * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ * Returns true if found, false otherwise.
*/
-struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
+bool tomoyo_find_domain(const char *domainname, struct tomoyo_cookie *cookie)
{
struct tomoyo_domain_info *domain;
struct tomoyo_path_info name;
@@ -340,11 +340,13 @@ struct tomoyo_domain_info *tomoyo_find_d
name.name = domainname;
tomoyo_fill_path_info(&name);
list_for_each_entry(domain, &tomoyo_domain_list, list) {
- if (!domain->is_deleted &&
- !tomoyo_pathcmp(&name, domain->domainname))
- return domain;
+ if (domain->is_deleted ||
+ tomoyo_pathcmp(&name, domain->domainname))
+ continue;
+ tomoyo_update_cookie(cookie, domain);
+ return true;
}
- return NULL;
+ return false;
}

/**
@@ -784,7 +786,7 @@ bool tomoyo_domain_quota_is_ok(struct to

if (!domain)
return true;
- down_read(&tomoyo_domain_acl_info_list_lock);
+ down_read(&tomoyo_policy_lock);
list_for_each_entry(ptr, &domain->acl_info_list, list) {
if (ptr->type & TOMOYO_ACL_DELETED)
continue;
@@ -838,7 +840,7 @@ bool tomoyo_domain_quota_is_ok(struct to
break;
}
}
- up_read(&tomoyo_domain_acl_info_list_lock);
+ up_read(&tomoyo_policy_lock);
if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
return true;
if (!domain->quota_warned) {
@@ -915,7 +917,10 @@ static int tomoyo_write_profile(struct t
return -EINVAL;
*cp = '\0';
if (!strcmp(data, "COMMENT")) {
+ down_write(&tomoyo_policy_lock);
+ tomoyo_del_name(profile->comment);
profile->comment = tomoyo_save_name(cp + 1);
+ up_write(&tomoyo_policy_lock);
return 0;
}
for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) {
@@ -973,9 +978,13 @@ 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;
+ down_read(&tomoyo_policy_lock);
+ done = tomoyo_io_printf(head, "%u-COMMENT=%s\n",
+ index, profile->comment ?
+ profile->comment->name : "");
+ up_read(&tomoyo_policy_lock);
+ if (!done)
break;
continue;
}
@@ -1020,7 +1029,6 @@ struct tomoyo_policy_manager_entry {

/* The list for "struct tomoyo_policy_manager_entry". */
static LIST_HEAD(tomoyo_policy_manager_list);
-static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);

/**
* tomoyo_update_manager_entry - Add a manager entry.
@@ -1051,7 +1059,7 @@ static int tomoyo_update_manager_entry(c
if (!saved_manager)
return -ENOMEM;
/***** EXCLUSIVE SECTION START *****/
- down_write(&tomoyo_policy_manager_list_lock);
+ down_write(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
if (ptr->manager != saved_manager)
continue;
@@ -1067,11 +1075,13 @@ static int tomoyo_update_manager_entry(c
if (!new_entry)
goto out;
new_entry->manager = saved_manager;
+ saved_manager = NULL;
new_entry->is_domain = is_domain;
list_add_tail(&new_entry->list, &tomoyo_policy_manager_list);
error = 0;
out:
- up_write(&tomoyo_policy_manager_list_lock);
+ tomoyo_del_name(saved_manager);
+ up_write(&tomoyo_policy_lock);
/***** EXCLUSIVE SECTION END *****/
return error;
}
@@ -1095,6 +1105,23 @@ static int tomoyo_write_manager_policy(s
return tomoyo_update_manager_entry(data, is_delete);
}

+static void tomoyo_cleanup_manager(void)
+{
+ struct tomoyo_policy_manager_entry *ptr;
+ struct tomoyo_policy_manager_entry *tmp;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_policy_lock);
+ list_for_each_entry_safe(ptr, tmp, &tomoyo_policy_manager_list, list) {
+ if (!ptr->is_deleted || tomoyo_used_by_cookie(ptr))
+ continue;
+ list_del(&ptr->list);
+ tomoyo_del_name(ptr->manager);
+ tomoyo_free_element(ptr);
+ }
+ up_write(&tomoyo_policy_lock);
+ /***** EXCLUSIVE SECTION END *****/
+}
+
/**
* tomoyo_read_manager_policy - Read manager policy.
*
@@ -1109,8 +1136,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,
+ down_read(&tomoyo_policy_lock);
+ list_for_each_cookie(pos, head->read_var2.u.list,
&tomoyo_policy_manager_list) {
struct tomoyo_policy_manager_entry *ptr;
ptr = list_entry(pos, struct tomoyo_policy_manager_entry,
@@ -1122,8 +1149,10 @@ static int tomoyo_read_manager_policy(st
break;
}
}
- up_read(&tomoyo_policy_manager_list_lock);
+ up_read(&tomoyo_policy_lock);
head->read_eof = done;
+ if (done)
+ tomoyo_cleanup_manager();
return 0;
}

@@ -1145,7 +1174,7 @@ 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);
+ down_read(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
if (!ptr->is_deleted && ptr->is_domain
&& !tomoyo_pathcmp(domainname, ptr->manager)) {
@@ -1153,13 +1182,13 @@ static bool tomoyo_is_policy_manager(voi
break;
}
}
- up_read(&tomoyo_policy_manager_list_lock);
+ up_read(&tomoyo_policy_lock);
if (found)
return true;
exe = tomoyo_get_exe();
if (!exe)
return false;
- down_read(&tomoyo_policy_manager_list_lock);
+ down_read(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
if (!ptr->is_deleted && !ptr->is_domain
&& !strcmp(exe, ptr->manager->name)) {
@@ -1167,7 +1196,7 @@ static bool tomoyo_is_policy_manager(voi
break;
}
}
- up_read(&tomoyo_policy_manager_list_lock);
+ up_read(&tomoyo_policy_lock);
if (!found) { /* Reduce error messages. */
static pid_t last_pid;
const pid_t pid = current->pid;
@@ -1194,25 +1223,30 @@ static bool tomoyo_is_select_one(struct
{
unsigned int pid;
struct tomoyo_domain_info *domain = NULL;
+ struct tomoyo_cookie *cookie = &head->write_var1;

if (sscanf(data, "pid=%u", &pid) == 1) {
struct task_struct *p;
+ down_read(&tomoyo_policy_lock);
/***** CRITICAL SECTION START *****/
- read_lock(&tasklist_lock);
+ rcu_read_lock();
p = find_task_by_vpid(pid);
if (p)
domain = tomoyo_real_domain(p);
- read_unlock(&tasklist_lock);
+ rcu_read_unlock();
/***** CRITICAL SECTION END *****/
+ if (domain)
+ tomoyo_update_cookie(cookie, domain);
+ up_read(&tomoyo_policy_lock);
} else if (!strncmp(data, "domain=", 7)) {
if (tomoyo_is_domain_def(data + 7)) {
- down_read(&tomoyo_domain_list_lock);
- domain = tomoyo_find_domain(data + 7);
- up_read(&tomoyo_domain_list_lock);
+ down_read(&tomoyo_policy_lock);
+ if (tomoyo_find_domain(data + 7, cookie))
+ domain = cookie->u.domain;
+ up_read(&tomoyo_policy_lock);
}
} else
return false;
- head->write_var1 = domain;
/* Accessing read_buf is safe because head->io_sem is held. */
if (!head->read_buf)
return true; /* Do nothing if open(O_WRONLY). */
@@ -1222,15 +1256,15 @@ static bool tomoyo_is_select_one(struct
head->read_eof = !domain;
if (domain) {
struct tomoyo_domain_info *d;
- head->read_var1 = NULL;
- down_read(&tomoyo_domain_list_lock);
+ head->read_var1.u.domain = NULL;
+ down_read(&tomoyo_policy_lock);
list_for_each_entry(d, &tomoyo_domain_list, list) {
if (d == domain)
break;
- head->read_var1 = &d->list;
+ head->read_var1.u.list = &d->list;
}
- up_read(&tomoyo_domain_list_lock);
- head->read_var2 = NULL;
+ up_read(&tomoyo_policy_lock);
+ head->read_var2.u.list = NULL;
head->read_bit = 0;
head->read_step = 0;
if (domain->is_deleted)
@@ -1249,7 +1283,7 @@ 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 = head->write_var1.u.domain;
bool is_delete = false;
bool is_select = false;
unsigned int profile;
@@ -1264,16 +1298,16 @@ static int tomoyo_write_domain_policy(st
if (!tomoyo_is_policy_manager())
return -EPERM;
if (tomoyo_is_domain_def(data)) {
- domain = NULL;
- if (is_delete)
+ struct tomoyo_cookie *cookie = &head->write_var1;
+ if (is_delete) {
tomoyo_delete_domain(data);
- else if (is_select) {
- down_read(&tomoyo_domain_list_lock);
- domain = tomoyo_find_domain(data);
- up_read(&tomoyo_domain_list_lock);
+ tomoyo_update_cookie(cookie, NULL);
+ } else if (is_select) {
+ down_read(&tomoyo_policy_lock);
+ tomoyo_find_domain(data, cookie);
+ up_read(&tomoyo_policy_lock);
} else
- domain = tomoyo_find_or_assign_new_domain(data, 0);
- head->write_var1 = domain;
+ tomoyo_find_or_assign_new_domain(data, 0, cookie);
return 0;
}
if (!domain)
@@ -1410,6 +1444,57 @@ static bool tomoyo_print_entry(struct to
return false;
}

+static void tomoyo_cleanup_domain_policy(void)
+{
+ struct tomoyo_domain_info *domain;
+ struct tomoyo_domain_info *next_domain;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_policy_lock);
+ list_for_each_entry_safe(domain, next_domain, &tomoyo_domain_list,
+ list) {
+ struct tomoyo_acl_info *acl;
+ struct tomoyo_acl_info *next_acl;
+ const bool can_delete_domain = domain->is_deleted &&
+ !tomoyo_used_by_cookie(domain);
+ if (can_delete_domain) {
+ list_for_each_entry(acl, &domain->acl_info_list, list)
+ acl->type |= TOMOYO_ACL_DELETED;
+ }
+ list_for_each_entry_safe(acl, next_acl, &domain->acl_info_list,
+ list) {
+ if (!(acl->type & TOMOYO_ACL_DELETED)
+ || tomoyo_used_by_cookie(acl))
+ continue;
+ list_del(&acl->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_del_name(acl1->filename);
+ break;
+ case TOMOYO_TYPE_DOUBLE_PATH_ACL:
+ acl2 = container_of(acl,
+ struct tomoyo_double_path_acl_record,
+ head);
+ tomoyo_del_name(acl2->filename1);
+ tomoyo_del_name(acl2->filename2);
+ break;
+ }
+ tomoyo_free_element(acl);
+ }
+ if (can_delete_domain && list_empty(&domain->acl_info_list)) {
+ tomoyo_del_name(domain->domainname);
+ list_del(&domain->list);
+ tomoyo_free_element(domain);
+ }
+ }
+ up_write(&tomoyo_policy_lock);
+ /***** EXCLUSIVE SECTION END *****/
+}
+
/**
* tomoyo_read_domain_policy - Read domain policy.
*
@@ -1427,8 +1512,9 @@ 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) {
+ down_read(&tomoyo_policy_lock);
+ list_for_each_cookie(dpos, head->read_var1.u.list,
+ &tomoyo_domain_list) {
struct tomoyo_domain_info *domain;
const char *quota_exceeded = "";
const char *transition_failed = "";
@@ -1461,18 +1547,16 @@ 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(apos, head->read_var2.u.list,
+ &domain->acl_info_list) {
struct tomoyo_acl_info *ptr
= list_entry(apos, struct tomoyo_acl_info,
- list);
+ list);
if (!tomoyo_print_entry(head, ptr)) {
done = false;
break;
}
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (!done)
break;
head->read_step = 3;
@@ -1485,8 +1569,10 @@ tail_mark:
if (head->read_single_domain)
break;
}
- up_read(&tomoyo_domain_list_lock);
+ up_read(&tomoyo_policy_lock);
head->read_eof = done;
+ if (done)
+ tomoyo_cleanup_domain_policy();
return 0;
}

@@ -1496,30 +1582,25 @@ tail_mark:
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, -EINVAL otherwise.
- *
- * This is equivalent to doing
- *
- * ( echo "select " $domainname; echo "use_profile " $profile ) |
- * /usr/lib/ccs/loadpolicy -d
*/
static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
{
char *data = head->write_buf;
char *cp = strchr(data, ' ');
- struct tomoyo_domain_info *domain;
unsigned long profile;
+ struct tomoyo_cookie cookie;

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))
+ if (strict_strtoul(data, 10, &profile) ||
+ profile >= TOMOYO_MAX_PROFILES)
return -EINVAL;
- if (domain && profile < TOMOYO_MAX_PROFILES
- && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded))
- domain->profile = (u8) profile;
+ down_read(&tomoyo_policy_lock);
+ if (tomoyo_find_domain(cp + 1, &cookie) &&
+ (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded))
+ cookie.u.domain->profile = (u8) profile;
+ up_read(&tomoyo_policy_lock);
return 0;
}

@@ -1529,13 +1610,6 @@ static int tomoyo_write_domain_profile(s
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns list of profile number and domainname pairs.
- *
- * This is equivalent to doing
- *
- * grep -A 1 '^<kernel>' /sys/kernel/security/tomoyo/domain_policy |
- * awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
- * domainname = $0; } else if ( $1 == "use_profile" ) {
- * print $2 " " domainname; domainname = ""; } } ; '
*/
static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
{
@@ -1544,8 +1618,8 @@ 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) {
+ down_read(&tomoyo_policy_lock);
+ list_for_each_cookie(pos, head->read_var1.u.list, &tomoyo_domain_list) {
struct tomoyo_domain_info *domain;
domain = list_entry(pos, struct tomoyo_domain_info, list);
if (domain->is_deleted)
@@ -1556,7 +1630,7 @@ static int tomoyo_read_domain_profile(st
break;
}
}
- up_read(&tomoyo_domain_list_lock);
+ up_read(&tomoyo_policy_lock);
head->read_eof = done;
return 0;
}
@@ -1595,11 +1669,11 @@ static int tomoyo_read_pid(struct tomoyo
struct task_struct *p;
struct tomoyo_domain_info *domain = NULL;
/***** CRITICAL SECTION START *****/
- read_lock(&tasklist_lock);
+ rcu_read_lock();
p = find_task_by_vpid(pid);
if (p)
domain = tomoyo_real_domain(p);
- read_unlock(&tasklist_lock);
+ rcu_read_unlock();
/***** CRITICAL SECTION END *****/
if (domain)
tomoyo_io_printf(head, "%d %u %s", pid, domain->profile,
@@ -1655,43 +1729,43 @@ static int tomoyo_read_exception_policy(
if (!head->read_eof) {
switch (head->read_step) {
case 0:
- head->read_var2 = NULL;
+ head->read_var2.u.list = NULL;
head->read_step = 1;
case 1:
if (!tomoyo_read_domain_keeper_policy(head))
break;
- head->read_var2 = NULL;
+ head->read_var2.u.list = NULL;
head->read_step = 2;
case 2:
if (!tomoyo_read_globally_readable_policy(head))
break;
- head->read_var2 = NULL;
+ head->read_var2.u.list = NULL;
head->read_step = 3;
case 3:
- head->read_var2 = NULL;
+ head->read_var2.u.list = NULL;
head->read_step = 4;
case 4:
if (!tomoyo_read_domain_initializer_policy(head))
break;
- head->read_var2 = NULL;
+ head->read_var2.u.list = NULL;
head->read_step = 5;
case 5:
if (!tomoyo_read_alias_policy(head))
break;
- head->read_var2 = NULL;
+ head->read_var2.u.list = NULL;
head->read_step = 6;
case 6:
- head->read_var2 = NULL;
+ head->read_var2.u.list = NULL;
head->read_step = 7;
case 7:
if (!tomoyo_read_file_pattern(head))
break;
- head->read_var2 = NULL;
+ head->read_var2.u.list = NULL;
head->read_step = 8;
case 8:
if (!tomoyo_read_no_rewrite_policy(head))
break;
- head->read_var2 = NULL;
+ head->read_var2.u.list = NULL;
head->read_step = 9;
case 9:
head->read_eof = true;
@@ -1778,7 +1852,7 @@ void tomoyo_load_policy(const char *file
tomoyo_policy_loaded = true;
{ /* Check all profiles currently assigned to domains are defined. */
struct tomoyo_domain_info *domain;
- down_read(&tomoyo_domain_list_lock);
+ down_read(&tomoyo_policy_lock);
list_for_each_entry(domain, &tomoyo_domain_list, list) {
const u8 profile = domain->profile;
if (tomoyo_profile_ptr[profile])
@@ -1786,7 +1860,7 @@ void tomoyo_load_policy(const char *file
panic("Profile %u (used by '%s') not defined.\n",
profile, domain->domainname->name);
}
- up_read(&tomoyo_domain_list_lock);
+ up_read(&tomoyo_policy_lock);
}
}

@@ -1919,6 +1993,9 @@ static int tomoyo_open_control(const u8
return -ENOMEM;
}
}
+ tomoyo_add_cookie(&head->read_var1, NULL);
+ tomoyo_add_cookie(&head->read_var2, NULL);
+ tomoyo_add_cookie(&head->write_var1, NULL);
file->private_data = head;
/*
* Call the handler now if the file is
@@ -2043,6 +2120,9 @@ static int tomoyo_close_control(struct f
head->read_buf = NULL;
tomoyo_free(head->write_buf);
head->write_buf = NULL;
+ tomoyo_del_cookie(&head->read_var1);
+ tomoyo_del_cookie(&head->read_var2);
+ tomoyo_del_cookie(&head->write_var1);
tomoyo_free(head);
head = NULL;
file->private_data = NULL;
@@ -2171,9 +2251,10 @@ static void __init tomoyo_create_entry(c
static int __init tomoyo_initerface_init(void)
{
struct dentry *tomoyo_dir;
+ struct tomoyo_cookie *cookie = current_cred()->security;

/* Don't create securityfs entries unless registered. */
- if (current_cred()->security != &tomoyo_kernel_domain)
+ if (!cookie || cookie->u.domain != &tomoyo_kernel_domain)
return 0;

tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
--- security-testing-2.6.git.orig/security/tomoyo/common.h
+++ security-testing-2.6.git/security/tomoyo/common.h
@@ -26,6 +26,18 @@
struct dentry;
struct vfsmount;

+struct tomoyo_domain_info;
+struct tomoyo_path_info;
+struct tomoyo_cookie {
+ struct list_head list;
+ union {
+ const void *ptr;
+ struct list_head *list;
+ struct tomoyo_domain_info *domain;
+ const struct tomoyo_path_info *path;
+ } u;
+};
+
/* Temporary buffer for holding pathnames. */
struct tomoyo_page_buffer {
char buffer[4096];
@@ -160,11 +172,11 @@ struct tomoyo_io_buffer {
/* Exclusive lock for this structure. */
struct mutex io_sem;
/* The position currently reading from. */
- struct list_head *read_var1;
+ struct tomoyo_cookie read_var1;
/* Extra variables for reading. */
- struct list_head *read_var2;
+ struct tomoyo_cookie read_var2;
/* The position currently writing to. */
- struct tomoyo_domain_info *write_var1;
+ struct tomoyo_cookie write_var1;
/* The step for reading. */
int read_step;
/* Buffer for reading. */
@@ -258,11 +270,10 @@ int tomoyo_write_no_rewrite_policy(char
/* Create "file_pattern" entry in exception policy. */
int tomoyo_write_pattern_policy(char *data, const bool is_delete);
/* Find a domain by the given name. */
-struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
+bool tomoyo_find_domain(const char *domainname, struct tomoyo_cookie *cookie);
/* Find or create a domain by the given name. */
-struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
- domainname,
- const u8 profile);
+bool tomoyo_find_or_assign_new_domain(const char *domainname, const u8 profile,
+ struct tomoyo_cookie *cookie);
/* Check mode for specified functionality. */
unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
const u8 index);
@@ -276,6 +287,11 @@ void tomoyo_load_policy(const char *file
void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
const bool is_delete, const u8 flags);

+static inline struct tomoyo_domain_info *tomoyo_domain(void)
+{
+ return ((struct tomoyo_cookie *) current_cred()->security)->u.domain;
+}
+
/* strcmp() for "struct tomoyo_path_info" structure. */
static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
const struct tomoyo_path_info *b)
@@ -319,12 +335,11 @@ static inline bool tomoyo_is_invalid(con
return c && (c <= ' ' || c >= 127);
}

+/* Lock for modifying policy. */
+extern struct rw_semaphore tomoyo_policy_lock;
+
/* 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;

/* Has /sbin/init started? */
extern bool tomoyo_policy_loaded;
--- security-testing-2.6.git.orig/security/tomoyo/domain.c
+++ security-testing-2.6.git/security/tomoyo/domain.c
@@ -21,7 +21,7 @@ struct tomoyo_domain_info tomoyo_kernel_

/* The list for "struct tomoyo_domain_info". */
LIST_HEAD(tomoyo_domain_list);
-DECLARE_RWSEM(tomoyo_domain_list_lock);
+DECLARE_RWSEM(tomoyo_policy_lock);

/* Structure for "initialize_domain" and "no_initialize_domain" keyword. */
struct tomoyo_domain_initializer_entry {
@@ -96,7 +96,6 @@ const char *tomoyo_get_last_name(const s

/* The list for "struct tomoyo_domain_initializer_entry". */
static LIST_HEAD(tomoyo_domain_initializer_list);
-static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);

/**
* tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
@@ -133,10 +132,12 @@ static int tomoyo_update_domain_initiali
return -ENOMEM;
}
saved_program = tomoyo_save_name(program);
- if (!saved_program)
+ if (!saved_program) {
+ tomoyo_del_name(saved_domainname);
return -ENOMEM;
+ }
/***** EXCLUSIVE SECTION START *****/
- down_write(&tomoyo_domain_initializer_list_lock);
+ down_write(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
@@ -154,17 +155,40 @@ static int tomoyo_update_domain_initiali
if (!new_entry)
goto out;
new_entry->domainname = saved_domainname;
+ saved_domainname = NULL;
new_entry->program = saved_program;
+ 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);
error = 0;
out:
- up_write(&tomoyo_domain_initializer_list_lock);
+ tomoyo_del_name(saved_domainname);
+ tomoyo_del_name(saved_program);
+ up_write(&tomoyo_policy_lock);
/***** EXCLUSIVE SECTION END *****/
return error;
}

+static void tomoyo_cleanup_initializer(void)
+{
+ struct tomoyo_domain_initializer_entry *ptr;
+ struct tomoyo_domain_initializer_entry *tmp;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_policy_lock);
+ list_for_each_entry_safe(ptr, tmp, &tomoyo_domain_initializer_list,
+ list) {
+ if (!ptr->is_deleted || tomoyo_used_by_cookie(ptr))
+ continue;
+ list_del(&ptr->list);
+ tomoyo_del_name(ptr->domainname);
+ tomoyo_del_name(ptr->program);
+ tomoyo_free_element(ptr);
+ }
+ up_write(&tomoyo_policy_lock);
+ /***** EXCLUSIVE SECTION END *****/
+}
+
/**
* tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list.
*
@@ -177,8 +201,8 @@ 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,
+ down_read(&tomoyo_policy_lock);
+ list_for_each_cookie(pos, head->read_var2.u.list,
&tomoyo_domain_initializer_list) {
const char *no;
const char *from = "";
@@ -201,7 +225,9 @@ bool tomoyo_read_domain_initializer_poli
break;
}
}
- up_read(&tomoyo_domain_initializer_list_lock);
+ up_read(&tomoyo_policy_lock);
+ if (done)
+ tomoyo_cleanup_initializer();
return done;
}

@@ -248,7 +274,7 @@ static bool tomoyo_is_domain_initializer
struct tomoyo_domain_initializer_entry *ptr;
bool flag = false;

- down_read(&tomoyo_domain_initializer_list_lock);
+ down_read(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
if (ptr->is_deleted)
continue;
@@ -269,13 +295,12 @@ static bool tomoyo_is_domain_initializer
}
flag = true;
}
- up_read(&tomoyo_domain_initializer_list_lock);
+ up_read(&tomoyo_policy_lock);
return flag;
}

/* The list for "struct tomoyo_domain_keeper_entry". */
static LIST_HEAD(tomoyo_domain_keeper_list);
-static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);

/**
* tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
@@ -313,10 +338,12 @@ static int tomoyo_update_domain_keeper_e
return -ENOMEM;
}
saved_domainname = tomoyo_save_name(domainname);
- if (!saved_domainname)
+ if (!saved_domainname) {
+ tomoyo_del_name(saved_program);
return -ENOMEM;
+ }
/***** EXCLUSIVE SECTION START *****/
- down_write(&tomoyo_domain_keeper_list_lock);
+ down_write(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
@@ -334,13 +361,17 @@ static int tomoyo_update_domain_keeper_e
if (!new_entry)
goto out;
new_entry->domainname = saved_domainname;
+ saved_domainname = NULL;
new_entry->program = saved_program;
+ 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);
error = 0;
out:
- up_write(&tomoyo_domain_keeper_list_lock);
+ tomoyo_del_name(saved_domainname);
+ tomoyo_del_name(saved_program);
+ up_write(&tomoyo_policy_lock);
/***** EXCLUSIVE SECTION END *****/
return error;
}
@@ -366,6 +397,24 @@ int tomoyo_write_domain_keeper_policy(ch
return tomoyo_update_domain_keeper_entry(data, NULL, is_not, is_delete);
}

+static void tomoyo_cleanup_keep_domain(void)
+{
+ struct tomoyo_domain_keeper_entry *ptr;
+ struct tomoyo_domain_keeper_entry *tmp;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_policy_lock);
+ list_for_each_entry_safe(ptr, tmp, &tomoyo_domain_keeper_list, list) {
+ if (!ptr->is_deleted || tomoyo_used_by_cookie(ptr))
+ continue;
+ list_del(&ptr->list);
+ tomoyo_del_name(ptr->domainname);
+ tomoyo_del_name(ptr->program);
+ tomoyo_free_element(ptr);
+ }
+ up_write(&tomoyo_policy_lock);
+ /***** EXCLUSIVE SECTION END *****/
+}
+
/**
* tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list.
*
@@ -378,8 +427,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,
+ down_read(&tomoyo_policy_lock);
+ list_for_each_cookie(pos, head->read_var2.u.list,
&tomoyo_domain_keeper_list) {
struct tomoyo_domain_keeper_entry *ptr;
const char *no;
@@ -402,7 +451,9 @@ bool tomoyo_read_domain_keeper_policy(st
break;
}
}
- up_read(&tomoyo_domain_keeper_list_lock);
+ up_read(&tomoyo_policy_lock);
+ if (done)
+ tomoyo_cleanup_keep_domain();
return done;
}

@@ -423,7 +474,7 @@ static bool tomoyo_is_domain_keeper(cons
struct tomoyo_domain_keeper_entry *ptr;
bool flag = false;

- down_read(&tomoyo_domain_keeper_list_lock);
+ down_read(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
if (ptr->is_deleted)
continue;
@@ -442,13 +493,12 @@ static bool tomoyo_is_domain_keeper(cons
}
flag = true;
}
- up_read(&tomoyo_domain_keeper_list_lock);
+ up_read(&tomoyo_policy_lock);
return flag;
}

/* The list for "struct tomoyo_alias_entry". */
static LIST_HEAD(tomoyo_alias_list);
-static DECLARE_RWSEM(tomoyo_alias_list_lock);

/**
* tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
@@ -474,10 +524,13 @@ static int tomoyo_update_alias_entry(con
return -EINVAL; /* No patterns allowed. */
saved_original_name = tomoyo_save_name(original_name);
saved_aliased_name = tomoyo_save_name(aliased_name);
- if (!saved_original_name || !saved_aliased_name)
+ if (!saved_original_name || !saved_aliased_name) {
+ tomoyo_del_name(saved_original_name);
+ tomoyo_del_name(saved_aliased_name);
return -ENOMEM;
+ }
/***** EXCLUSIVE SECTION START *****/
- down_write(&tomoyo_alias_list_lock);
+ down_write(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_alias_list, list) {
if (ptr->original_name != saved_original_name ||
ptr->aliased_name != saved_aliased_name)
@@ -494,15 +547,37 @@ static int tomoyo_update_alias_entry(con
if (!new_entry)
goto out;
new_entry->original_name = saved_original_name;
+ saved_original_name = NULL;
new_entry->aliased_name = saved_aliased_name;
+ saved_aliased_name = NULL;
list_add_tail(&new_entry->list, &tomoyo_alias_list);
error = 0;
out:
- up_write(&tomoyo_alias_list_lock);
+ tomoyo_del_name(saved_original_name);
+ tomoyo_del_name(saved_aliased_name);
+ up_write(&tomoyo_policy_lock);
/***** EXCLUSIVE SECTION END *****/
return error;
}

+static void tomoyo_cleanup_alias(void)
+{
+ struct tomoyo_alias_entry *ptr;
+ struct tomoyo_alias_entry *tmp;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_policy_lock);
+ list_for_each_entry_safe(ptr, tmp, &tomoyo_alias_list, list) {
+ if (!ptr->is_deleted || tomoyo_used_by_cookie(ptr))
+ continue;
+ list_del(&ptr->list);
+ tomoyo_del_name(ptr->original_name);
+ tomoyo_del_name(ptr->aliased_name);
+ tomoyo_free_element(ptr);
+ }
+ up_write(&tomoyo_policy_lock);
+ /***** EXCLUSIVE SECTION END *****/
+}
+
/**
* tomoyo_read_alias_policy - Read "struct tomoyo_alias_entry" list.
*
@@ -515,8 +590,8 @@ 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) {
+ down_read(&tomoyo_policy_lock);
+ list_for_each_cookie(pos, head->read_var2.u.list, &tomoyo_alias_list) {
struct tomoyo_alias_entry *ptr;

ptr = list_entry(pos, struct tomoyo_alias_entry, list);
@@ -529,7 +604,9 @@ bool tomoyo_read_alias_policy(struct tom
break;
}
}
- up_read(&tomoyo_alias_list_lock);
+ up_read(&tomoyo_policy_lock);
+ if (done)
+ tomoyo_cleanup_alias();
return done;
}

@@ -568,7 +645,7 @@ int tomoyo_delete_domain(char *domainnam
name.name = domainname;
tomoyo_fill_path_info(&name);
/***** EXCLUSIVE SECTION START *****/
- down_write(&tomoyo_domain_list_lock);
+ down_write(&tomoyo_policy_lock);
/* Is there an active domain? */
list_for_each_entry(domain, &tomoyo_domain_list, list) {
/* Never delete tomoyo_kernel_domain */
@@ -580,7 +657,7 @@ int tomoyo_delete_domain(char *domainnam
domain->is_deleted = true;
break;
}
- up_write(&tomoyo_domain_list_lock);
+ up_write(&tomoyo_policy_lock);
/***** EXCLUSIVE SECTION END *****/
return 0;
}
@@ -590,81 +667,49 @@ int tomoyo_delete_domain(char *domainnam
*
* @domainname: The name of domain.
* @profile: Profile number to assign if the domain was newly created.
+ * @cookie: Pointer to "struct tomoyo_cookie".
*
- * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ * Returns true on success, false otherwise.
*/
-struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
- domainname,
- const u8 profile)
+bool tomoyo_find_or_assign_new_domain(const char *domainname, const u8 profile,
+ struct tomoyo_cookie *cookie)
{
- struct tomoyo_domain_info *domain = NULL;
- const struct tomoyo_path_info *saved_domainname;
+ struct tomoyo_domain_info *domain;
+ const struct tomoyo_path_info *saved_domainname = NULL;

/***** EXCLUSIVE SECTION START *****/
- down_write(&tomoyo_domain_list_lock);
- domain = tomoyo_find_domain(domainname);
- if (domain)
+ down_write(&tomoyo_policy_lock);
+ if (tomoyo_find_domain(domainname, cookie))
goto out;
if (!tomoyo_is_correct_domain(domainname, __func__))
goto out;
saved_domainname = tomoyo_save_name(domainname);
if (!saved_domainname)
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;
- /***** 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;
- }
- /* No memory reusable. Create using new memory. */
domain = tomoyo_alloc_element(sizeof(*domain));
if (domain) {
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);
+ tomoyo_update_cookie(cookie, domain);
}
out:
- up_write(&tomoyo_domain_list_lock);
+ tomoyo_del_name(saved_domainname);
+ up_write(&tomoyo_policy_lock);
/***** EXCLUSIVE SECTION END *****/
- return domain;
+ return cookie->u.domain != NULL;
}

/**
* 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
@@ -672,7 +717,6 @@ int tomoyo_find_next_domain(struct linux
*/
struct tomoyo_page_buffer *tmp = tomoyo_alloc(sizeof(*tmp));
struct tomoyo_domain_info *old_domain = tomoyo_domain();
- struct tomoyo_domain_info *domain = NULL;
const char *old_domain_name = old_domain->domainname->name;
const char *original_name = bprm->filename;
char *new_domain_name = NULL;
@@ -685,6 +729,8 @@ int tomoyo_find_next_domain(struct linux
struct tomoyo_path_info s; /* symlink name */
struct tomoyo_path_info l; /* last name */
static bool initialized;
+ struct tomoyo_cookie *cookie = bprm->cred->security;
+ bool found = false;

if (!tmp)
goto out;
@@ -723,7 +769,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);
+ down_read(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_alias_list, list) {
if (ptr->is_deleted ||
tomoyo_pathcmp(&r, ptr->original_name) ||
@@ -735,7 +781,7 @@ int tomoyo_find_next_domain(struct linux
tomoyo_fill_path_info(&r);
break;
}
- up_read(&tomoyo_alias_list_lock);
+ up_read(&tomoyo_policy_lock);
}

/* Check execute permission. */
@@ -755,28 +801,36 @@ int tomoyo_find_next_domain(struct linux
* /sbin/init. But transit from kernel domain if executing
* initializers because they might start before /sbin/init.
*/
- domain = old_domain;
+ found = true;
+ /*
+ * Since old_domain is already in cookie list,
+ * tomoyo_policy_lock is not needed.
+ */
+ tomoyo_update_cookie(cookie, old_domain);
} else if (tomoyo_is_domain_keeper(old_domain->domainname, &r, &l)) {
/* Keep current domain. */
- domain = old_domain;
+ found = true;
+ /*
+ * Since old_domain is already in cookie,
+ * tomoyo_policy_lock is not needed.
+ */
+ tomoyo_update_cookie(cookie, old_domain);
} else {
/* Normal domain transition. */
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;
- 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)
+ if (found || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
goto done;
- domain = tomoyo_find_or_assign_new_domain(new_domain_name,
- old_domain->profile);
+ down_read(&tomoyo_policy_lock);
+ found = tomoyo_find_domain(new_domain_name, cookie);
+ up_read(&tomoyo_policy_lock);
+ if (!found && !is_enforce)
+ found = tomoyo_find_or_assign_new_domain(new_domain_name,
+ old_domain->profile,
+ cookie);
done:
- if (domain)
+ if (found)
goto out;
printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",
new_domain_name);
@@ -788,7 +842,6 @@ int tomoyo_find_next_domain(struct linux
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
@@ -130,9 +130,6 @@ static struct tomoyo_path_info *tomoyo_g
return NULL;
}

-/* Lock for domain->acl_info_list. */
-DECLARE_RWSEM(tomoyo_domain_acl_info_list_lock);
-
static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
const char *filename2,
struct tomoyo_domain_info *
@@ -143,7 +140,6 @@ static int tomoyo_update_single_path_acl

/* The list for "struct tomoyo_globally_readable_file_entry". */
static LIST_HEAD(tomoyo_globally_readable_list);
-static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);

/**
* tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
@@ -167,7 +163,7 @@ static int tomoyo_update_globally_readab
if (!saved_filename)
return -ENOMEM;
/***** EXCLUSIVE SECTION START *****/
- down_write(&tomoyo_globally_readable_list_lock);
+ down_write(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
if (ptr->filename != saved_filename)
continue;
@@ -183,10 +179,12 @@ static int tomoyo_update_globally_readab
if (!new_entry)
goto out;
new_entry->filename = saved_filename;
+ saved_filename = NULL;
list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
error = 0;
out:
- up_write(&tomoyo_globally_readable_list_lock);
+ tomoyo_del_name(saved_filename);
+ up_write(&tomoyo_policy_lock);
/***** EXCLUSIVE SECTION END *****/
return error;
}
@@ -203,7 +201,7 @@ static bool tomoyo_is_globally_readable_
{
struct tomoyo_globally_readable_file_entry *ptr;
bool found = false;
- down_read(&tomoyo_globally_readable_list_lock);
+ down_read(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
if (!ptr->is_deleted &&
tomoyo_path_matches_pattern(filename, ptr->filename)) {
@@ -211,7 +209,7 @@ static bool tomoyo_is_globally_readable_
break;
}
}
- up_read(&tomoyo_globally_readable_list_lock);
+ up_read(&tomoyo_policy_lock);
return found;
}

@@ -228,6 +226,24 @@ int tomoyo_write_globally_readable_polic
return tomoyo_update_globally_readable_entry(data, is_delete);
}

+static void tomoyo_cleanup_allow_read(void)
+{
+ struct tomoyo_globally_readable_file_entry *ptr;
+ struct tomoyo_globally_readable_file_entry *tmp;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_policy_lock);
+ list_for_each_entry_safe(ptr, tmp, &tomoyo_globally_readable_list,
+ list) {
+ if (!ptr->is_deleted || tomoyo_used_by_cookie(ptr))
+ continue;
+ list_del(&ptr->list);
+ tomoyo_del_name(ptr->filename);
+ tomoyo_free_element(ptr);
+ }
+ up_write(&tomoyo_policy_lock);
+ /***** EXCLUSIVE SECTION END *****/
+}
+
/**
* tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list.
*
@@ -240,8 +256,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,
+ down_read(&tomoyo_policy_lock);
+ list_for_each_cookie(pos, head->read_var2.u.list,
&tomoyo_globally_readable_list) {
struct tomoyo_globally_readable_file_entry *ptr;
ptr = list_entry(pos,
@@ -255,13 +271,14 @@ bool tomoyo_read_globally_readable_polic
break;
}
}
- up_read(&tomoyo_globally_readable_list_lock);
+ up_read(&tomoyo_policy_lock);
+ if (done)
+ tomoyo_cleanup_allow_read();
return done;
}

/* The list for "struct tomoyo_pattern_entry". */
static LIST_HEAD(tomoyo_pattern_list);
-static DECLARE_RWSEM(tomoyo_pattern_list_lock);

/**
* tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
@@ -285,7 +302,7 @@ static int tomoyo_update_file_pattern_en
if (!saved_pattern)
return -ENOMEM;
/***** EXCLUSIVE SECTION START *****/
- down_write(&tomoyo_pattern_list_lock);
+ down_write(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
if (saved_pattern != ptr->pattern)
continue;
@@ -301,10 +318,12 @@ static int tomoyo_update_file_pattern_en
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
+ saved_pattern = NULL;
list_add_tail(&new_entry->list, &tomoyo_pattern_list);
error = 0;
out:
- up_write(&tomoyo_pattern_list_lock);
+ tomoyo_del_name(saved_pattern);
+ up_write(&tomoyo_policy_lock);
/***** EXCLUSIVE SECTION END *****/
return error;
}
@@ -312,17 +331,15 @@ static int tomoyo_update_file_pattern_en
/**
* tomoyo_get_file_pattern - Get patterned pathname.
*
- * @filename: The filename to find patterned pathname.
- *
- * Returns pointer to pathname pattern if matched, @filename otherwise.
+ * @cookie: Pointer to "struct tomoyo_cookie".
*/
-static const struct tomoyo_path_info *
-tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
+static void tomoyo_get_file_pattern(struct tomoyo_cookie *cookie)
{
struct tomoyo_pattern_entry *ptr;
const struct tomoyo_path_info *pattern = NULL;
+ const struct tomoyo_path_info *filename = cookie->u.path;

- down_read(&tomoyo_pattern_list_lock);
+ down_read(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
if (ptr->is_deleted)
continue;
@@ -336,10 +353,9 @@ tomoyo_get_file_pattern(const struct tom
break;
}
}
- up_read(&tomoyo_pattern_list_lock);
if (pattern)
- filename = pattern;
- return filename;
+ tomoyo_update_cookie(cookie, pattern);
+ up_read(&tomoyo_policy_lock);
}

/**
@@ -355,6 +371,23 @@ int tomoyo_write_pattern_policy(char *da
return tomoyo_update_file_pattern_entry(data, is_delete);
}

+static void tomoyo_cleanup_file_pattern(void)
+{
+ struct tomoyo_pattern_entry *ptr;
+ struct tomoyo_pattern_entry *tmp;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_policy_lock);
+ list_for_each_entry_safe(ptr, tmp, &tomoyo_pattern_list, list) {
+ if (!ptr->is_deleted || tomoyo_used_by_cookie(ptr))
+ continue;
+ list_del(&ptr->list);
+ tomoyo_del_name(ptr->pattern);
+ tomoyo_free_element(ptr);
+ }
+ up_write(&tomoyo_policy_lock);
+ /***** EXCLUSIVE SECTION END *****/
+}
+
/**
* tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list.
*
@@ -367,8 +400,9 @@ 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) {
+ down_read(&tomoyo_policy_lock);
+ list_for_each_cookie(pos, head->read_var2.u.list,
+ &tomoyo_pattern_list) {
struct tomoyo_pattern_entry *ptr;
ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
if (ptr->is_deleted)
@@ -379,13 +413,14 @@ bool tomoyo_read_file_pattern(struct tom
break;
}
}
- up_read(&tomoyo_pattern_list_lock);
+ up_read(&tomoyo_policy_lock);
+ if (done)
+ tomoyo_cleanup_file_pattern();
return done;
}

/* The list for "struct tomoyo_no_rewrite_entry". */
static LIST_HEAD(tomoyo_no_rewrite_list);
-static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);

/**
* tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
@@ -408,7 +443,7 @@ static int tomoyo_update_no_rewrite_entr
if (!saved_pattern)
return -ENOMEM;
/***** EXCLUSIVE SECTION START *****/
- down_write(&tomoyo_no_rewrite_list_lock);
+ down_write(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->pattern != saved_pattern)
continue;
@@ -424,10 +459,12 @@ static int tomoyo_update_no_rewrite_entr
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
+ saved_pattern = NULL;
list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
error = 0;
out:
- up_write(&tomoyo_no_rewrite_list_lock);
+ tomoyo_del_name(saved_pattern);
+ up_write(&tomoyo_policy_lock);
/***** EXCLUSIVE SECTION END *****/
return error;
}
@@ -445,7 +482,7 @@ static bool tomoyo_is_no_rewrite_file(co
struct tomoyo_no_rewrite_entry *ptr;
bool found = false;

- down_read(&tomoyo_no_rewrite_list_lock);
+ down_read(&tomoyo_policy_lock);
list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->is_deleted)
continue;
@@ -454,7 +491,7 @@ static bool tomoyo_is_no_rewrite_file(co
found = true;
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
+ up_read(&tomoyo_policy_lock);
return found;
}

@@ -471,6 +508,23 @@ int tomoyo_write_no_rewrite_policy(char
return tomoyo_update_no_rewrite_entry(data, is_delete);
}

+static void tomoyo_cleanup_no_rewrite(void)
+{
+ struct tomoyo_no_rewrite_entry *ptr;
+ struct tomoyo_no_rewrite_entry *tmp;
+ /***** EXCLUSIVE SECTION START *****/
+ down_write(&tomoyo_policy_lock);
+ list_for_each_entry_safe(ptr, tmp, &tomoyo_no_rewrite_list, list) {
+ if (!ptr->is_deleted || tomoyo_used_by_cookie(ptr))
+ continue;
+ list_del(&ptr->list);
+ tomoyo_del_name(ptr->pattern);
+ tomoyo_free_element(ptr);
+ }
+ up_write(&tomoyo_policy_lock);
+ /***** EXCLUSIVE SECTION END *****/
+}
+
/**
* tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list.
*
@@ -483,8 +537,9 @@ 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) {
+ down_read(&tomoyo_policy_lock);
+ list_for_each_cookie(pos, head->read_var2.u.list,
+ &tomoyo_no_rewrite_list) {
struct tomoyo_no_rewrite_entry *ptr;
ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
if (ptr->is_deleted)
@@ -495,7 +550,9 @@ bool tomoyo_read_no_rewrite_policy(struc
break;
}
}
- up_read(&tomoyo_no_rewrite_list_lock);
+ up_read(&tomoyo_policy_lock);
+ if (done)
+ tomoyo_cleanup_no_rewrite();
return done;
}

@@ -561,7 +618,7 @@ static int tomoyo_check_single_path_acl2
struct tomoyo_acl_info *ptr;
int error = -EPERM;

- down_read(&tomoyo_domain_acl_info_list_lock);
+ down_read(&tomoyo_policy_lock);
list_for_each_entry(ptr, &domain->acl_info_list, list) {
struct tomoyo_single_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
@@ -580,7 +637,7 @@ static int tomoyo_check_single_path_acl2
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
+ up_read(&tomoyo_policy_lock);
return error;
}

@@ -662,10 +719,17 @@ static int tomoyo_check_file_perm2(struc
return error;
if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
/* 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) {
+ tomoyo_update_file_acl(filename->name, perm, domain,
+ false);
+ } else {
+ struct tomoyo_cookie cookie;
+ tomoyo_add_cookie(&cookie, filename);
+ tomoyo_get_file_pattern(&cookie);
+ tomoyo_update_file_acl(cookie.u.path->name, perm,
+ domain, false);
+ tomoyo_del_cookie(&cookie);
+ }
}
return 0;
}
@@ -746,7 +810,7 @@ static int tomoyo_update_single_path_acl
if (!saved_filename)
return -ENOMEM;
/***** EXCLUSIVE SECTION START *****/
- down_write(&tomoyo_domain_acl_info_list_lock);
+ down_write(&tomoyo_policy_lock);
if (is_delete)
goto delete;
list_for_each_entry(ptr, &domain->acl_info_list, list) {
@@ -776,6 +840,7 @@ static int tomoyo_update_single_path_acl
if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
acl->perm |= rw_mask;
acl->filename = saved_filename;
+ saved_filename = NULL;
list_add_tail(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
@@ -799,7 +864,8 @@ static int tomoyo_update_single_path_acl
break;
}
out:
- up_write(&tomoyo_domain_acl_info_list_lock);
+ tomoyo_del_name(saved_filename);
+ up_write(&tomoyo_policy_lock);
/***** EXCLUSIVE SECTION END *****/
return error;
}
@@ -834,10 +900,13 @@ static int tomoyo_update_double_path_acl
return -EINVAL;
saved_filename1 = tomoyo_save_name(filename1);
saved_filename2 = tomoyo_save_name(filename2);
- if (!saved_filename1 || !saved_filename2)
+ if (!saved_filename1 || !saved_filename2) {
+ tomoyo_del_name(saved_filename1);
+ tomoyo_del_name(saved_filename2);
return -ENOMEM;
+ }
/***** EXCLUSIVE SECTION START *****/
- down_write(&tomoyo_domain_acl_info_list_lock);
+ down_write(&tomoyo_policy_lock);
if (is_delete)
goto delete;
list_for_each_entry(ptr, &domain->acl_info_list, list) {
@@ -862,7 +931,9 @@ static int tomoyo_update_double_path_acl
goto out;
acl->perm = perm;
acl->filename1 = saved_filename1;
+ saved_filename1 = NULL;
acl->filename2 = saved_filename2;
+ saved_filename2 = NULL;
list_add_tail(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
@@ -883,7 +954,9 @@ static int tomoyo_update_double_path_acl
break;
}
out:
- up_write(&tomoyo_domain_acl_info_list_lock);
+ tomoyo_del_name(saved_filename1);
+ tomoyo_del_name(saved_filename2);
+ up_write(&tomoyo_policy_lock);
/***** EXCLUSIVE SECTION END *****/
return error;
}
@@ -929,7 +1002,7 @@ static int tomoyo_check_double_path_acl(

if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
return 0;
- down_read(&tomoyo_domain_acl_info_list_lock);
+ down_read(&tomoyo_policy_lock);
list_for_each_entry(ptr, &domain->acl_info_list, list) {
struct tomoyo_double_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
@@ -945,7 +1018,7 @@ static int tomoyo_check_double_path_acl(
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
+ up_read(&tomoyo_policy_lock);
return error;
}

@@ -980,8 +1053,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_cookie cookie;
+ tomoyo_add_cookie(&cookie, filename);
+ tomoyo_get_file_pattern(&cookie);
+ tomoyo_update_single_path_acl(operation, cookie.u.path->name,
+ domain, false);
+ tomoyo_del_cookie(&cookie);
}
if (!is_enforce)
error = 0;
@@ -1227,10 +1304,17 @@ 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_cookie cookie1;
+ struct tomoyo_cookie cookie2;
+ tomoyo_add_cookie(&cookie1, buf1);
+ tomoyo_add_cookie(&cookie2, buf2);
+ tomoyo_get_file_pattern(&cookie1);
+ tomoyo_get_file_pattern(&cookie2);
+ tomoyo_update_double_path_acl(operation, cookie1.u.path->name,
+ cookie2.u.path->name, domain,
false);
+ tomoyo_del_cookie(&cookie1);
+ tomoyo_del_cookie(&cookie2);
}
out:
tomoyo_free(buf1);
--- security-testing-2.6.git.orig/security/tomoyo/realpath.c
+++ security-testing-2.6.git/security/tomoyo/realpath.c
@@ -194,68 +194,51 @@ char *tomoyo_realpath_nofollow(const cha
}

/* Memory allocated for non-string data. */
-static unsigned int tomoyo_allocated_memory_for_elements;
+static atomic_t tomoyo_allocated_memory_for_elements;
/* Quota for holding non-string data. */
static unsigned int tomoyo_quota_for_elements;

/**
- * tomoyo_alloc_element - Allocate permanent memory for structures.
+ * tomoyo_alloc_element - Allocate memory for structures.
*
* @size: Size in bytes.
*
* Returns pointer to allocated memory on success, NULL otherwise.
*
- * Memory has to be zeroed.
- * The RAM is chunked, so NEVER try to kfree() the returned pointer.
+ * Memory are zeroed.
*/
void *tomoyo_alloc_element(const unsigned int size)
{
- static char *buf;
- static DEFINE_MUTEX(lock);
- static unsigned int buf_used_len = PATH_MAX;
- char *ptr = NULL;
- /*Assumes sizeof(void *) >= sizeof(long) is true. */
- const unsigned int word_aligned_size
- = roundup(size, max(sizeof(void *), sizeof(long)));
- if (word_aligned_size > PATH_MAX)
- return NULL;
- /***** EXCLUSIVE SECTION START *****/
- mutex_lock(&lock);
- if (buf_used_len + word_aligned_size > PATH_MAX) {
- if (!tomoyo_quota_for_elements ||
- tomoyo_allocated_memory_for_elements
- + PATH_MAX <= tomoyo_quota_for_elements)
- ptr = kzalloc(PATH_MAX, GFP_KERNEL);
- if (!ptr) {
- printk(KERN_WARNING "ERROR: Out of memory "
- "for tomoyo_alloc_element().\n");
- if (!tomoyo_policy_loaded)
- panic("MAC Initialization failed.\n");
- } else {
- buf = ptr;
- tomoyo_allocated_memory_for_elements += PATH_MAX;
- buf_used_len = word_aligned_size;
- ptr = buf;
- }
- } else if (word_aligned_size) {
- int i;
- ptr = buf + buf_used_len;
- buf_used_len += word_aligned_size;
- for (i = 0; i < word_aligned_size; i++) {
- if (!ptr[i])
- continue;
- printk(KERN_ERR "WARNING: Reserved memory was tainted! "
- "The system might go wrong.\n");
- ptr[i] = '\0';
- }
+ char *ptr = kzalloc(size, GFP_KERNEL);
+ int len = ptr ? ksize(ptr) : 0;
+ atomic_add(len, &tomoyo_allocated_memory_for_elements);
+ if (!len ||
+ (tomoyo_quota_for_elements &&
+ atomic_read(&tomoyo_allocated_memory_for_elements)
+ > tomoyo_quota_for_elements)) {
+ kfree(ptr);
+ ptr = NULL;
+ atomic_sub(len, &tomoyo_allocated_memory_for_elements);
+ printk(KERN_WARNING "ERROR: Out of memory for %s().\n",
+ __func__);
+ if (!tomoyo_policy_loaded)
+ panic("MAC Initialization failed.\n");
}
- mutex_unlock(&lock);
- /***** EXCLUSIVE SECTION END *****/
return ptr;
}

+void tomoyo_free_element(void *ptr)
+{
+ int len;
+ if (!ptr)
+ return;
+ len = ksize(ptr);
+ kfree(ptr);
+ atomic_sub(len, &tomoyo_allocated_memory_for_elements);
+}
+
/* Memory allocated for string data in bytes. */
-static unsigned int tomoyo_allocated_memory_for_savename;
+static atomic_t tomoyo_allocated_memory_for_savename;
/* Quota for holding string data in bytes. */
static unsigned int tomoyo_quota_for_savename;

@@ -269,44 +252,27 @@ 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;
};

-/* Structure for available memory region. */
-struct tomoyo_free_memory_block_list {
- struct list_head list;
- char *ptr; /* Pointer to a free area. */
- int len; /* Length of the area. */
-};
-
-/*
- * The list for "struct tomoyo_name_entry".
- *
- * This list is updated only inside tomoyo_save_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_save_name - Allocate permanent memory for string data.
+ * tomoyo_save_name - Allocate shared memory for string data.
*
- * @name: The string to store into the permernent memory.
+ * @name: The string to add or find.
*
* 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_save_name(const char *name)
{
- static LIST_HEAD(fmb_list);
- static DEFINE_MUTEX(lock);
struct tomoyo_name_entry *ptr;
unsigned int hash;
- /* fmb contains available size in bytes.
- fmb is removed from the fmb_list when fmb->len becomes 0. */
- struct tomoyo_free_memory_block_list *fmb;
int len;
- char *cp;
+ int allocated_len;

if (!name)
return NULL;
@@ -318,74 +284,89 @@ const struct tomoyo_path_info *tomoyo_sa
}
hash = full_name_hash((const unsigned char *) name, len - 1);
/***** 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))
- goto out;
- }
- list_for_each_entry(fmb, &fmb_list, list) {
- if (len <= fmb->len)
- goto ready;
+ list) {
+ if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
+ continue;
+ atomic_inc(&ptr->users);
+ goto out;
}
- if (!tomoyo_quota_for_savename ||
- tomoyo_allocated_memory_for_savename + PATH_MAX
- <= tomoyo_quota_for_savename)
- cp = kzalloc(PATH_MAX, GFP_KERNEL);
- else
- cp = NULL;
- fmb = kzalloc(sizeof(*fmb), GFP_KERNEL);
- if (!cp || !fmb) {
- kfree(cp);
- kfree(fmb);
- printk(KERN_WARNING "ERROR: Out of memory "
- "for tomoyo_save_name().\n");
+ ptr = kmalloc(sizeof(*ptr) + len, GFP_KERNEL);
+ allocated_len = ptr ? ksize(ptr) : 0;
+ if (!allocated_len ||
+ (tomoyo_quota_for_savename &&
+ atomic_read(&tomoyo_allocated_memory_for_savename)
+ + allocated_len > tomoyo_quota_for_savename)) {
+ kfree(ptr);
+ ptr = NULL;
+ printk(KERN_WARNING "ERROR: Out of memory for %s().\n",
+ __func__);
if (!tomoyo_policy_loaded)
panic("MAC Initialization failed.\n");
- ptr = NULL;
goto out;
}
- tomoyo_allocated_memory_for_savename += PATH_MAX;
- list_add(&fmb->list, &fmb_list);
- fmb->ptr = cp;
- fmb->len = PATH_MAX;
- ready:
- ptr = tomoyo_alloc_element(sizeof(*ptr));
- if (!ptr)
- goto out;
- ptr->entry.name = fmb->ptr;
- memmove(fmb->ptr, name, len);
+ atomic_add(allocated_len, &tomoyo_allocated_memory_for_savename);
+ ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
+ memmove((char *) ptr->entry.name, name, len);
+ atomic_set(&ptr->users, 1);
tomoyo_fill_path_info(&ptr->entry);
- fmb->ptr += len;
- fmb->len -= len;
list_add_tail(&ptr->list, &tomoyo_name_list[hash % TOMOYO_MAX_HASH]);
- if (fmb->len == 0) {
- list_del(&fmb->list);
- kfree(fmb);
- }
out:
- mutex_unlock(&lock);
+ mutex_unlock(&tomoyo_name_list_lock);
/***** EXCLUSIVE SECTION END *****/
return ptr ? &ptr->entry : NULL;
}

/**
+ * tomoyo_del_name - Delete shared memory for string data.
+ *
+ * @name: Pointer to "struct tomoyo_path_info".
+ */
+void tomoyo_del_name(const struct tomoyo_path_info *name)
+{
+ struct tomoyo_name_entry *ptr;
+
+ if (!name)
+ return;
+ /***** EXCLUSIVE SECTION START *****/
+ mutex_lock(&tomoyo_name_list_lock);
+ list_for_each_entry(ptr, &tomoyo_name_list[name->hash %
+ TOMOYO_MAX_HASH], list) {
+ if (tomoyo_pathcmp(name, &ptr->entry))
+ continue;
+ if (atomic_dec_and_test(&ptr->users)) {
+ int len = ksize(ptr);
+ list_del(&ptr->list);
+ kfree(ptr);
+ atomic_sub(len, &tomoyo_allocated_memory_for_savename);
+ }
+ break;
+ }
+ mutex_unlock(&tomoyo_name_list_lock);
+ /***** EXCLUSIVE SECTION END *****/
+}
+
+/**
* tomoyo_realpath_init - Initialize realpath related code.
*/
void __init tomoyo_realpath_init(void)
{
int i;
+ struct tomoyo_cookie cookie;

BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX);
for (i = 0; i < TOMOYO_MAX_HASH; i++)
INIT_LIST_HEAD(&tomoyo_name_list[i]);
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
+ /* No lock because this domain is not deletable. */
tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
- down_read(&tomoyo_domain_list_lock);
- if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
+ down_read(&tomoyo_policy_lock);
+ if (!tomoyo_find_domain(TOMOYO_ROOT_NAME, &cookie) ||
+ cookie.u.domain != &tomoyo_kernel_domain)
panic("Can't register tomoyo_kernel_domain");
- up_read(&tomoyo_domain_list_lock);
+ up_read(&tomoyo_policy_lock);
}

/* Memory allocated for temporary purpose. */
@@ -432,9 +413,9 @@ int tomoyo_read_memory_counter(struct to
{
if (!head->read_eof) {
const unsigned int shared
- = tomoyo_allocated_memory_for_savename;
+ = atomic_read(&tomoyo_allocated_memory_for_savename);
const unsigned int private
- = tomoyo_allocated_memory_for_elements;
+ = atomic_read(&tomoyo_allocated_memory_for_elements);
const unsigned int dynamic
= atomic_read(&tomoyo_dynamic_memory_size);
char buffer[64];
@@ -480,3 +461,64 @@ int tomoyo_write_memory_quota(struct tom
tomoyo_quota_for_elements = size;
return 0;
}
+
+/* List of pointers referenced by cookies. */
+static LIST_HEAD(tomoyo_cookie_list);
+static DEFINE_RWLOCK(tomoyo_cookie_list_lock);
+
+/**
+ * tomoyo_add_cookie - Add a cookie to cookie list.
+ *
+ * @cookie: Pointer to "struct tomoyo_cookie".
+ * @ptr: Pointer to assign.
+ */
+void tomoyo_add_cookie(struct tomoyo_cookie *cookie, const void *ptr)
+{
+ unsigned long flags;
+ if (!cookie)
+ return;
+ tomoyo_update_cookie(cookie, ptr);
+ write_lock_irqsave(&tomoyo_cookie_list_lock, flags);
+ list_add_tail(&cookie->list, &tomoyo_cookie_list);
+ write_unlock_irqrestore(&tomoyo_cookie_list_lock, flags);
+}
+
+/**
+ * tomoyo_del_cookie - Delete a cookie from cookie list.
+ *
+ * @cookie: Pointer to "struct tomoyo_cookie".
+ */
+void tomoyo_del_cookie(struct tomoyo_cookie *cookie)
+{
+ unsigned long flags;
+ if (!cookie)
+ return;
+ write_lock_irqsave(&tomoyo_cookie_list_lock, flags);
+ list_del(&cookie->list);
+ write_unlock_irqrestore(&tomoyo_cookie_list_lock, flags);
+}
+
+/**
+ * tomoyo_used_by_cookie - Check whether the given pointer is referenced by a cookie or not.
+ *
+ * @ptr: Pointer to check.
+ *
+ * Returns true if @ptr is in use, false otherwise.
+ *
+ * Caller must hold tomoyo_policy_lock for writing.
+ */
+bool tomoyo_used_by_cookie(const void *ptr)
+{
+ unsigned long flags;
+ struct tomoyo_cookie *cookie;
+ bool in_use = false;
+ read_lock_irqsave(&tomoyo_cookie_list_lock, flags);
+ list_for_each_entry(cookie, &tomoyo_cookie_list, list) {
+ if (ptr != cookie->u.ptr)
+ continue;
+ in_use = true;
+ break;
+ }
+ read_unlock_irqrestore(&tomoyo_cookie_list_lock, flags);
+ return in_use;
+}
--- security-testing-2.6.git.orig/security/tomoyo/realpath.h
+++ security-testing-2.6.git/security/tomoyo/realpath.h
@@ -36,18 +36,6 @@ char *tomoyo_realpath_nofollow(const cha
/* Same with tomoyo_realpath() except that the pathname is already solved. */
char *tomoyo_realpath_from_path(struct path *path);

-/*
- * Allocate memory for ACL entry.
- * The RAM is chunked, so NEVER try to kfree() the returned pointer.
- */
-void *tomoyo_alloc_element(const unsigned int size);
-
-/*
- * Keep the given name on the RAM.
- * The RAM is shared, so NEVER try to modify or kfree() the returned name.
- */
-const struct tomoyo_path_info *tomoyo_save_name(const char *name);
-
/* Allocate memory for temporary use (e.g. permission checks). */
void *tomoyo_alloc(const size_t size);

@@ -63,4 +51,35 @@ int tomoyo_write_memory_quota(struct tom
/* Initialize realpath related code. */
void __init tomoyo_realpath_init(void);

+/* Allocate memory for ACL entry. */
+void *tomoyo_alloc_element(const unsigned int size);
+/* Delete memory for ACL entry. */
+void tomoyo_free_element(void *ptr);
+
+/* Allocate memory for the given name. */
+const struct tomoyo_path_info *tomoyo_save_name(const char *name);
+/* Delete memory for the given name. */
+void tomoyo_del_name(const struct tomoyo_path_info *name);
+
+/* Add a cookie to cookie list. */
+void tomoyo_add_cookie(struct tomoyo_cookie *cookie, const void *ptr);
+/**
+ * tomoyo_update_cookie - Assign the given pointer to a cookie.
+ *
+ * @cookie: Pointer to "struct tomoyo_cookie".
+ * @ptr: Pointer to assign.
+ *
+ * Caller must hold tomoyo_policy_lock for writing unless @ptr is NULL or
+ * @ptr is already in cookie list.
+ */
+static inline void tomoyo_update_cookie(struct tomoyo_cookie *cookie,
+ const void *ptr)
+{
+ cookie->u.ptr = ptr;
+}
+/* Delete a cookie from cookie list. */
+void tomoyo_del_cookie(struct tomoyo_cookie *cookie);
+/* Check whether the given pointer is referenced by a cookie or not. */
+bool tomoyo_used_by_cookie(const void *ptr);
+
#endif /* !defined(_SECURITY_TOMOYO_REALPATH_H) */
--- security-testing-2.6.git.orig/security/tomoyo/tomoyo.c
+++ security-testing-2.6.git/security/tomoyo/tomoyo.c
@@ -17,14 +17,23 @@
static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
- /*
- * Since "struct tomoyo_domain_info *" is a sharable pointer,
- * we don't need to duplicate.
- */
- new->security = old->security;
+ struct tomoyo_cookie *cookie = kzalloc(sizeof(*cookie), gfp);
+
+ if (!cookie)
+ return -ENOMEM;
+ tomoyo_add_cookie(cookie,
+ ((struct tomoyo_cookie *) old->security)->u.ptr);
+ new->security = cookie;
return 0;
}

+static void tomoyo_cred_free(struct cred *cred)
+{
+ struct tomoyo_cookie *cookie = cred->security;
+ tomoyo_del_cookie(cookie);
+ kfree(cookie);
+}
+
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
{
/*
@@ -43,26 +52,21 @@ 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;
+ ((struct tomoyo_cookie *) bprm->cred->security)->u.domain = NULL;
return 0;
}

static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
{
- struct tomoyo_domain_info *domain = bprm->cred->security;
+ struct tomoyo_domain_info *domain = ((struct tomoyo_cookie *)
+ bprm->cred->security)->u.domain;

/*
* 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).
@@ -259,6 +263,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
@@ -279,6 +284,7 @@ static struct security_operations tomoyo
static int __init tomoyo_init(void)
{
struct cred *cred = (struct cred *) current_cred();
+ struct tomoyo_cookie *cookie;

if (!security_module_enable(&tomoyo_security_ops))
return 0;
@@ -286,7 +292,9 @@ static int __init tomoyo_init(void)
if (register_security(&tomoyo_security_ops))
panic("Failure registering TOMOYO Linux");
printk(KERN_INFO "TOMOYO Linux initialized\n");
- cred->security = &tomoyo_kernel_domain;
+ cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
+ tomoyo_add_cookie(cookie, &tomoyo_kernel_domain);
+ cred->security = cookie;
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. */

@@ -85,18 +84,14 @@ int tomoyo_find_next_domain(struct linux

extern struct tomoyo_domain_info tomoyo_kernel_domain;

-static inline struct tomoyo_domain_info *tomoyo_domain(void)
-{
- return current_cred()->security;
-}
-
-/* Caller holds tasklist_lock spinlock. */
+/* Caller calls rcu_read_lock()/rcu_read_unlock(). */
static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
*task)
{
/***** CRITICAL SECTION START *****/
const struct cred *cred = get_task_cred(task);
- struct tomoyo_domain_info *domain = cred->security;
+ struct tomoyo_domain_info *domain = ((struct tomoyo_cookie *)
+ cred->security)->u.domain;

put_cred(cred);
return domain;