[PATCH 3/3] TOMOYO: Add SRCU based garbage collector.

From: Tetsuo Handa
Date: Thu Jun 18 2009 - 02:46:26 EST


Tetsuo Handa wrote:
> I have one worry regarding SRCU.
> Inside synchronize_srcu(), there is a loop
>
> while (srcu_readers_active_idx(sp, idx))
> schedule_timeout_interruptible(1);
>
> but the reader's sleeping duration varies from less than one second to
> more than hours.
>
> Checking for counters for every jiffies sounds too much waste of CPU.
> Delaying kfree() for seconds or minutes won't cause troubles for TOMOYO.
> It would be nice if checking interval is configurable like
> "schedule_timeout_interruptible(sp->timeout);".
>
Well, GC thread's schedule_timeout_interruptible(1); loop does not
appear on /usr/bin/top , thus I don't need to worry about checking interval.

OK. Here is SRCU version.
------------------------------
Subject: [PATCH 3/3] TOMOYO: Add SRCU based garbage collector.

As of now, TOMOYO cannot release memory used by marked-as-deleted list elements
because TOMOYO does not know how many readers are there.

This patch adds SRCU based garbage collector.

Signed-off-by: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx>
---
security/tomoyo/common.c | 124 ++++++--------
security/tomoyo/common.h | 180 ++++++++++++++++++++-
security/tomoyo/domain.c | 191 ++++-------------------
security/tomoyo/file.c | 174 ++++++++------------
security/tomoyo/realpath.c | 373 +++++++++++++++++++++++++++++++++++++++++++--
security/tomoyo/realpath.h | 5
security/tomoyo/tomoyo.c | 18 ++
7 files changed, 707 insertions(+), 358 deletions(-)

--- security-testing-2.6.git.orig/security/tomoyo/common.c
+++ security-testing-2.6.git/security/tomoyo/common.c
@@ -12,6 +12,7 @@
#include <linux/uaccess.h>
#include <linux/security.h>
#include <linux/hardirq.h>
+#include <linux/kthread.h>
#include "realpath.h"
#include "common.h"
#include "tomoyo.h"
@@ -340,10 +341,9 @@ bool tomoyo_is_domain_def(const unsigned
*
* @domainname: The domainname to find.
*
- * Caller must call down_read(&tomoyo_domain_list_lock); or
- * down_write(&tomoyo_domain_list_lock); .
- *
* Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ *
+ * Caller holds srcu_read_lock(&tomoyo_ss).
*/
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
{
@@ -352,7 +352,7 @@ struct tomoyo_domain_info *tomoyo_find_d

name.name = domainname;
tomoyo_fill_path_info(&name);
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
if (!domain->is_deleted &&
!tomoyo_pathcmp(&name, domain->domainname))
return domain;
@@ -788,6 +788,8 @@ bool tomoyo_verbose_mode(const struct to
* @domain: Pointer to "struct tomoyo_domain_info".
*
* Returns true if the domain is not exceeded quota, false otherwise.
+ *
+ * Caller holds srcu_read_lock(&tomoyo_ss).
*/
bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
{
@@ -796,8 +798,7 @@ bool tomoyo_domain_quota_is_ok(struct to

if (!domain)
return true;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (ptr->type & TOMOYO_ACL_DELETED)
continue;
switch (tomoyo_acl_type2(ptr)) {
@@ -850,7 +851,6 @@ bool tomoyo_domain_quota_is_ok(struct to
break;
}
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
return true;
if (!domain->quota_warned) {
@@ -1029,27 +1029,6 @@ static int tomoyo_read_profile(struct to
}

/*
- * tomoyo_policy_manager_entry is a structure which is used for holding list of
- * domainnames or programs which are permitted to modify configuration via
- * /sys/kernel/security/tomoyo/ interface.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_policy_manager_list .
- * (2) "manager" is a domainname or a program's pathname.
- * (3) "is_domain" is a bool which is true if "manager" is a domainname, false
- * otherwise.
- * (4) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- */
-struct tomoyo_policy_manager_entry {
- struct list_head list;
- /* A path to program or a domainname. */
- const struct tomoyo_path_info *manager;
- bool is_domain; /* True if manager is a domainname. */
- bool is_deleted; /* True if this entry is deleted. */
-};
-
-/*
* tomoyo_policy_manager_list is used for holding list of domainnames or
* programs which are permitted to modify configuration via
* /sys/kernel/security/tomoyo/ interface.
@@ -1079,8 +1058,7 @@ struct tomoyo_policy_manager_entry {
*
* # cat /sys/kernel/security/tomoyo/manager
*/
-static LIST_HEAD(tomoyo_policy_manager_list);
-static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
+LIST_HEAD(tomoyo_policy_manager_list);

/**
* tomoyo_update_manager_entry - Add a manager entry.
@@ -1112,8 +1090,8 @@ static int tomoyo_update_manager_entry(c
return -ENOMEM;
if (!is_delete)
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
- down_write(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (ptr->manager != saved_manager)
continue;
ptr->is_deleted = is_delete;
@@ -1124,11 +1102,12 @@ static int tomoyo_update_manager_entry(c
new_entry->manager = saved_manager;
saved_manager = NULL;
new_entry->is_domain = is_domain;
- list_add_tail(&new_entry->list, &tomoyo_policy_manager_list);
+ list_add_tail_rcu(&new_entry->list,
+ &tomoyo_policy_manager_list);
new_entry = NULL;
error = 0;
}
- up_write(&tomoyo_policy_manager_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
tomoyo_put_name(saved_manager);
kfree(new_entry);
return error;
@@ -1167,9 +1146,8 @@ static int tomoyo_read_manager_policy(st

if (head->read_eof)
return 0;
- down_read(&tomoyo_policy_manager_list_lock);
- list_for_each_cookie(pos, head->read_var2,
- &tomoyo_policy_manager_list) {
+ list_for_each_cookie_rcu(pos, head->read_var2,
+ &tomoyo_policy_manager_list) {
struct tomoyo_policy_manager_entry *ptr;
ptr = list_entry(pos, struct tomoyo_policy_manager_entry,
list);
@@ -1179,7 +1157,6 @@ static int tomoyo_read_manager_policy(st
if (!done)
break;
}
- up_read(&tomoyo_policy_manager_list_lock);
head->read_eof = done;
return 0;
}
@@ -1189,6 +1166,8 @@ static int tomoyo_read_manager_policy(st
*
* Returns true if the current process is permitted to modify policy
* via /sys/kernel/security/tomoyo/ interface.
+ *
+ * Caller holds srcu_read_lock(&tomoyo_ss).
*/
static bool tomoyo_is_policy_manager(void)
{
@@ -1202,29 +1181,25 @@ static bool tomoyo_is_policy_manager(voi
return true;
if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
return false;
- down_read(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (!ptr->is_deleted && ptr->is_domain
&& !tomoyo_pathcmp(domainname, ptr->manager)) {
found = true;
break;
}
}
- up_read(&tomoyo_policy_manager_list_lock);
if (found)
return true;
exe = tomoyo_get_exe();
if (!exe)
return false;
- down_read(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (!ptr->is_deleted && !ptr->is_domain
&& !strcmp(exe, ptr->manager->name)) {
found = true;
break;
}
}
- up_read(&tomoyo_policy_manager_list_lock);
if (!found) { /* Reduce error messages. */
static pid_t last_pid;
const pid_t pid = current->pid;
@@ -1245,6 +1220,8 @@ static bool tomoyo_is_policy_manager(voi
* @data: String to parse.
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds srcu_read_lock(&tomoyo_ss).
*/
static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
const char *data)
@@ -1260,11 +1237,8 @@ static bool tomoyo_is_select_one(struct
domain = tomoyo_real_domain(p);
read_unlock(&tasklist_lock);
} else if (!strncmp(data, "domain=", 7)) {
- if (tomoyo_is_domain_def(data + 7)) {
- down_read(&tomoyo_domain_list_lock);
+ if (tomoyo_is_domain_def(data + 7))
domain = tomoyo_find_domain(data + 7);
- up_read(&tomoyo_domain_list_lock);
- }
} else
return false;
head->write_var1 = domain;
@@ -1278,13 +1252,11 @@ static bool tomoyo_is_select_one(struct
if (domain) {
struct tomoyo_domain_info *d;
head->read_var1 = NULL;
- down_read(&tomoyo_domain_list_lock);
- list_for_each_entry(d, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(d, &tomoyo_domain_list, list) {
if (d == domain)
break;
head->read_var1 = &d->list;
}
- up_read(&tomoyo_domain_list_lock);
head->read_var2 = NULL;
head->read_bit = 0;
head->read_step = 0;
@@ -1300,6 +1272,8 @@ static bool tomoyo_is_select_one(struct
* @domainname: The name of domain.
*
* Returns 0.
+ *
+ * Caller holds srcu_read_lock(&tomoyo_ss).
*/
static int tomoyo_delete_domain(char *domainname)
{
@@ -1308,9 +1282,9 @@ static int tomoyo_delete_domain(char *do

name.name = domainname;
tomoyo_fill_path_info(&name);
- down_write(&tomoyo_domain_list_lock);
+ mutex_lock(&tomoyo_policy_lock);
/* Is there an active domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
/* Never delete tomoyo_kernel_domain */
if (domain == &tomoyo_kernel_domain)
continue;
@@ -1320,7 +1294,7 @@ static int tomoyo_delete_domain(char *do
domain->is_deleted = true;
break;
}
- up_write(&tomoyo_domain_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
return 0;
}

@@ -1330,6 +1304,8 @@ static int tomoyo_delete_domain(char *do
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds srcu_read_lock(&tomoyo_ss).
*/
static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
{
@@ -1352,11 +1328,9 @@ static int tomoyo_write_domain_policy(st
domain = NULL;
if (is_delete)
tomoyo_delete_domain(data);
- else if (is_select) {
- down_read(&tomoyo_domain_list_lock);
+ else if (is_select)
domain = tomoyo_find_domain(data);
- up_read(&tomoyo_domain_list_lock);
- } else
+ else
domain = tomoyo_find_or_assign_new_domain(data, 0);
head->write_var1 = domain;
return 0;
@@ -1511,8 +1485,7 @@ static int tomoyo_read_domain_policy(str
return 0;
if (head->read_step == 0)
head->read_step = 1;
- down_read(&tomoyo_domain_list_lock);
- list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) {
+ list_for_each_cookie_rcu(dpos, head->read_var1, &tomoyo_domain_list) {
struct tomoyo_domain_info *domain;
const char *quota_exceeded = "";
const char *transition_failed = "";
@@ -1543,9 +1516,8 @@ acl_loop:
if (head->read_step == 3)
goto tail_mark;
/* Print ACL entries in the domain. */
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_cookie(apos, head->read_var2,
- &domain->acl_info_list) {
+ list_for_each_cookie_rcu(apos, head->read_var2,
+ &domain->acl_info_list) {
struct tomoyo_acl_info *ptr
= list_entry(apos, struct tomoyo_acl_info,
list);
@@ -1553,7 +1525,6 @@ acl_loop:
if (!done)
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (!done)
break;
head->read_step = 3;
@@ -1565,7 +1536,6 @@ tail_mark:
if (head->read_single_domain)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1581,6 +1551,8 @@ tail_mark:
*
* ( echo "select " $domainname; echo "use_profile " $profile ) |
* /usr/lib/ccs/loadpolicy -d
+ *
+ * Caller holds srcu_read_lock(&tomoyo_ss).
*/
static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
{
@@ -1592,9 +1564,7 @@ static int tomoyo_write_domain_profile(s
if (!cp)
return -EINVAL;
*cp = '\0';
- down_read(&tomoyo_domain_list_lock);
domain = tomoyo_find_domain(cp + 1);
- up_read(&tomoyo_domain_list_lock);
if (strict_strtoul(data, 10, &profile))
return -EINVAL;
if (domain && profile < TOMOYO_MAX_PROFILES
@@ -1624,8 +1594,7 @@ static int tomoyo_read_domain_profile(st

if (head->read_eof)
return 0;
- down_read(&tomoyo_domain_list_lock);
- list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
+ list_for_each_cookie_rcu(pos, head->read_var1, &tomoyo_domain_list) {
struct tomoyo_domain_info *domain;
domain = list_entry(pos, struct tomoyo_domain_info, list);
if (domain->is_deleted)
@@ -1635,7 +1604,6 @@ static int tomoyo_read_domain_profile(st
if (!done)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1854,16 +1822,24 @@ void tomoyo_load_policy(const char *file
printk(KERN_INFO "Mandatory Access Control activated.\n");
tomoyo_policy_loaded = true;
{ /* Check all profiles currently assigned to domains are defined. */
+ const int idx = srcu_read_lock(&tomoyo_ss);
struct tomoyo_domain_info *domain;
- down_read(&tomoyo_domain_list_lock);
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
const u8 profile = domain->profile;
if (tomoyo_profile_ptr[profile])
continue;
panic("Profile %u (used by '%s') not defined.\n",
profile, domain->domainname->name);
}
- up_read(&tomoyo_domain_list_lock);
+ srcu_read_unlock(&tomoyo_ss, idx);
+ }
+ {
+ struct task_struct *task =
+ kthread_create(tomoyo_gc_thread, NULL, "GC for TOMOYO");
+ if (IS_ERR(task))
+ printk(KERN_ERR "GC thread not available.\n");
+ else
+ wake_up_process(task);
}
}

@@ -1997,6 +1973,7 @@ static int tomoyo_open_control(const u8
}
}
file->private_data = head;
+ head->tomoyo_srcu_index = srcu_read_lock(&tomoyo_ss);
/*
* Call the handler now if the file is
* /sys/kernel/security/tomoyo/self_domain
@@ -2114,6 +2091,7 @@ static int tomoyo_write_control(struct f
static int tomoyo_close_control(struct file *file)
{
struct tomoyo_io_buffer *head = file->private_data;
+ srcu_read_unlock(&tomoyo_ss, head->tomoyo_srcu_index);

/* Release memory used for policy I/O. */
tomoyo_free(head->read_buf);
--- security-testing-2.6.git.orig/security/tomoyo/common.h
+++ security-testing-2.6.git/security/tomoyo/common.h
@@ -156,6 +156,7 @@ struct tomoyo_domain_info {
struct list_head acl_info_list;
/* Name of this domain. Never NULL. */
const struct tomoyo_path_info *domainname;
+ atomic_t users;
u8 profile; /* Profile number to use. */
bool is_deleted; /* Delete flag. */
bool quota_warned; /* Quota warnning flag. */
@@ -266,6 +267,8 @@ struct tomoyo_io_buffer {
int (*write) (struct tomoyo_io_buffer *);
/* Exclusive lock for this structure. */
struct mutex io_sem;
+ /* counter which this structure locked. */
+ int tomoyo_srcu_index;
/* The position currently reading from. */
struct list_head *read_var1;
/* Extra variables for reading. */
@@ -421,10 +424,9 @@ static inline bool tomoyo_is_invalid(con

/* The list for "struct tomoyo_domain_info". */
extern struct list_head tomoyo_domain_list;
-extern struct rw_semaphore tomoyo_domain_list_lock;

-/* Lock for domain->acl_info_list. */
-extern struct rw_semaphore tomoyo_domain_acl_info_list_lock;
+/* Lock for modifying policy. */
+extern struct mutex tomoyo_policy_lock;

/* Has /sbin/init started? */
extern bool tomoyo_policy_loaded;
@@ -433,21 +435,181 @@ extern bool tomoyo_policy_loaded;
extern struct tomoyo_domain_info tomoyo_kernel_domain;

/**
- * list_for_each_cookie - iterate over a list with cookie.
+ * list_for_each_cookie_rcu - iterate over a list with cookie.
* @pos: the &struct list_head to use as a loop cursor.
* @cookie: the &struct list_head to use as a cookie.
* @head: the head for your list.
*
- * Same with list_for_each() except that this primitive uses @cookie
+ * Same with __list_for_each_rcu() except that this primitive uses @cookie
* so that we can continue iteration.
* @cookie must be NULL when iteration starts, and @cookie will become
* NULL when iteration finishes.
*/
-#define list_for_each_cookie(pos, cookie, head) \
+#define list_for_each_cookie_rcu(pos, cookie, head) \
for (({ if (!cookie) \
- cookie = head; }), \
- pos = (cookie)->next; \
+ cookie = head; }), \
+ pos = rcu_dereference((cookie)->next); \
prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
- (cookie) = pos, pos = pos->next)
+ (cookie) = pos, pos = rcu_dereference(pos->next))
+
+/* SRCU structure for GC */
+extern struct srcu_struct tomoyo_ss;
+
+/*
+ * tomoyo_policy_manager_entry is a structure which is used for holding list of
+ * domainnames or programs which are permitted to modify configuration via
+ * /sys/kernel/security/tomoyo/ interface.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_policy_manager_list .
+ * (2) "manager" is a domainname or a program's pathname.
+ * (3) "is_domain" is a bool which is true if "manager" is a domainname, false
+ * otherwise.
+ * (4) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_policy_manager_entry {
+ struct list_head list;
+ /* A path to program or a domainname. */
+ const struct tomoyo_path_info *manager;
+ bool is_domain; /* True if manager is a domainname. */
+ bool is_deleted; /* True if this entry is deleted. */
+};
+
+extern struct list_head tomoyo_policy_manager_list;
+
+/*
+ * tomoyo_globally_readable_file_entry is a structure which is used for holding
+ * "allow_read" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_globally_readable_list .
+ * (2) "filename" is a pathname which is allowed to open(O_RDONLY).
+ * (3) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_globally_readable_file_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *filename;
+ bool is_deleted;
+};
+
+extern struct list_head tomoyo_globally_readable_list;
+
+/*
+ * tomoyo_pattern_entry is a structure which is used for holding
+ * "tomoyo_pattern_list" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_pattern_list .
+ * (2) "pattern" is a pathname pattern which is used for converting pathnames
+ * to pathname patterns during learning mode.
+ * (3) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_pattern_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *pattern;
+ bool is_deleted;
+};
+
+extern struct list_head tomoyo_pattern_list;
+
+/*
+ * tomoyo_no_rewrite_entry is a structure which is used for holding
+ * "deny_rewrite" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_no_rewrite_list .
+ * (2) "pattern" is a pathname which is by default not permitted to modify
+ * already existing content.
+ * (3) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_no_rewrite_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *pattern;
+ bool is_deleted;
+};
+
+extern struct list_head tomoyo_no_rewrite_list;
+
+/*
+ * tomoyo_domain_initializer_entry is a structure which is used for holding
+ * "initialize_domain" and "no_initialize_domain" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_domain_initializer_list .
+ * (2) "domainname" which is "a domainname" or "the last component of a
+ * domainname". This field is NULL if "from" clause is not specified.
+ * (3) "program" which is a program's pathname.
+ * (4) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ * (5) "is_not" is a bool which is true if "no_initialize_domain", false
+ * otherwise.
+ * (6) "is_last_name" is a bool which is true if "domainname" is "the last
+ * component of a domainname", false otherwise.
+ */
+struct tomoyo_domain_initializer_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *domainname; /* This may be NULL */
+ const struct tomoyo_path_info *program;
+ bool is_deleted;
+ bool is_not; /* True if this entry is "no_initialize_domain". */
+ /* True if the domainname is tomoyo_get_last_name(). */
+ bool is_last_name;
+};
+
+extern struct list_head tomoyo_domain_initializer_list;
+
+/*
+ * tomoyo_domain_keeper_entry is a structure which is used for holding
+ * "keep_domain" and "no_keep_domain" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_domain_keeper_list .
+ * (2) "domainname" which is "a domainname" or "the last component of a
+ * domainname".
+ * (3) "program" which is a program's pathname.
+ * This field is NULL if "from" clause is not specified.
+ * (4) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ * (5) "is_not" is a bool which is true if "no_initialize_domain", false
+ * otherwise.
+ * (6) "is_last_name" is a bool which is true if "domainname" is "the last
+ * component of a domainname", false otherwise.
+ */
+struct tomoyo_domain_keeper_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *domainname;
+ const struct tomoyo_path_info *program; /* This may be NULL */
+ bool is_deleted;
+ bool is_not; /* True if this entry is "no_keep_domain". */
+ /* True if the domainname is tomoyo_get_last_name(). */
+ bool is_last_name;
+};
+
+extern struct list_head tomoyo_domain_keeper_list;
+
+/*
+ * tomoyo_alias_entry is a structure which is used for holding "alias" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_alias_list .
+ * (2) "original_name" which is a dereferenced pathname.
+ * (3) "aliased_name" which is a symlink's pathname.
+ * (4) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_alias_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *original_name;
+ const struct tomoyo_path_info *aliased_name;
+ bool is_deleted;
+};
+
+extern struct list_head tomoyo_alias_list;
+
+int tomoyo_gc_thread(void *unused);

#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
--- security-testing-2.6.git.orig/security/tomoyo/domain.c
+++ security-testing-2.6.git/security/tomoyo/domain.c
@@ -58,77 +58,6 @@ struct tomoyo_domain_info tomoyo_kernel_
* exceptions.
*/
LIST_HEAD(tomoyo_domain_list);
-DECLARE_RWSEM(tomoyo_domain_list_lock);
-
-/*
- * tomoyo_domain_initializer_entry is a structure which is used for holding
- * "initialize_domain" and "no_initialize_domain" entries.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_domain_initializer_list .
- * (2) "domainname" which is "a domainname" or "the last component of a
- * domainname". This field is NULL if "from" clause is not specified.
- * (3) "program" which is a program's pathname.
- * (4) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- * (5) "is_not" is a bool which is true if "no_initialize_domain", false
- * otherwise.
- * (6) "is_last_name" is a bool which is true if "domainname" is "the last
- * component of a domainname", false otherwise.
- */
-struct tomoyo_domain_initializer_entry {
- struct list_head list;
- const struct tomoyo_path_info *domainname; /* This may be NULL */
- const struct tomoyo_path_info *program;
- bool is_deleted;
- bool is_not; /* True if this entry is "no_initialize_domain". */
- /* True if the domainname is tomoyo_get_last_name(). */
- bool is_last_name;
-};
-
-/*
- * tomoyo_domain_keeper_entry is a structure which is used for holding
- * "keep_domain" and "no_keep_domain" entries.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_domain_keeper_list .
- * (2) "domainname" which is "a domainname" or "the last component of a
- * domainname".
- * (3) "program" which is a program's pathname.
- * This field is NULL if "from" clause is not specified.
- * (4) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- * (5) "is_not" is a bool which is true if "no_initialize_domain", false
- * otherwise.
- * (6) "is_last_name" is a bool which is true if "domainname" is "the last
- * component of a domainname", false otherwise.
- */
-struct tomoyo_domain_keeper_entry {
- struct list_head list;
- const struct tomoyo_path_info *domainname;
- const struct tomoyo_path_info *program; /* This may be NULL */
- bool is_deleted;
- bool is_not; /* True if this entry is "no_keep_domain". */
- /* True if the domainname is tomoyo_get_last_name(). */
- bool is_last_name;
-};
-
-/*
- * tomoyo_alias_entry is a structure which is used for holding "alias" entries.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_alias_list .
- * (2) "original_name" which is a dereferenced pathname.
- * (3) "aliased_name" which is a symlink's pathname.
- * (4) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- */
-struct tomoyo_alias_entry {
- struct list_head list;
- const struct tomoyo_path_info *original_name;
- const struct tomoyo_path_info *aliased_name;
- bool is_deleted;
-};

/**
* tomoyo_get_last_name - Get last component of a domainname.
@@ -183,8 +112,7 @@ const char *tomoyo_get_last_name(const s
* will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain
* unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain.
*/
-static LIST_HEAD(tomoyo_domain_initializer_list);
-static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
+LIST_HEAD(tomoyo_domain_initializer_list);

/**
* tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
@@ -227,8 +155,8 @@ static int tomoyo_update_domain_initiali
}
if (!is_delete)
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
- down_write(&tomoyo_domain_initializer_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -244,12 +172,12 @@ static int tomoyo_update_domain_initiali
saved_program = NULL;
new_entry->is_not = is_not;
new_entry->is_last_name = is_last_name;
- list_add_tail(&new_entry->list,
- &tomoyo_domain_initializer_list);
+ list_add_tail_rcu(&new_entry->list,
+ &tomoyo_domain_initializer_list);
new_entry = NULL;
error = 0;
}
- up_write(&tomoyo_domain_initializer_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
tomoyo_put_name(saved_domainname);
tomoyo_put_name(saved_program);
kfree(new_entry);
@@ -268,15 +196,14 @@ bool tomoyo_read_domain_initializer_poli
struct list_head *pos;
bool done = true;

- down_read(&tomoyo_domain_initializer_list_lock);
- list_for_each_cookie(pos, head->read_var2,
- &tomoyo_domain_initializer_list) {
+ list_for_each_cookie_rcu(pos, head->read_var2,
+ &tomoyo_domain_initializer_list) {
const char *no;
const char *from = "";
const char *domain = "";
struct tomoyo_domain_initializer_entry *ptr;
ptr = list_entry(pos, struct tomoyo_domain_initializer_entry,
- list);
+ list);
if (ptr->is_deleted)
continue;
no = ptr->is_not ? "no_" : "";
@@ -291,7 +218,6 @@ bool tomoyo_read_domain_initializer_poli
if (!done)
break;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return done;
}

@@ -328,6 +254,8 @@ int tomoyo_write_domain_initializer_poli
*
* Returns true if executing @program reinitializes domain transition,
* false otherwise.
+ *
+ * Caller holds srcu_read_lock(&tomoyo_ss).
*/
static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
domainname,
@@ -338,8 +266,7 @@ static bool tomoyo_is_domain_initializer
struct tomoyo_domain_initializer_entry *ptr;
bool flag = false;

- down_read(&tomoyo_domain_initializer_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
if (ptr->is_deleted)
continue;
if (ptr->domainname) {
@@ -359,7 +286,6 @@ static bool tomoyo_is_domain_initializer
}
flag = true;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return flag;
}

@@ -401,8 +327,7 @@ static bool tomoyo_is_domain_initializer
* "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless
* explicitly specified by "initialize_domain".
*/
-static LIST_HEAD(tomoyo_domain_keeper_list);
-static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
+LIST_HEAD(tomoyo_domain_keeper_list);

/**
* tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
@@ -445,8 +370,8 @@ static int tomoyo_update_domain_keeper_e
}
if (!is_delete)
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
- down_write(&tomoyo_domain_keeper_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -462,11 +387,12 @@ static int tomoyo_update_domain_keeper_e
saved_program = NULL;
new_entry->is_not = is_not;
new_entry->is_last_name = is_last_name;
- list_add_tail(&new_entry->list, &tomoyo_domain_keeper_list);
+ list_add_tail_rcu(&new_entry->list,
+ &tomoyo_domain_keeper_list);
new_entry = NULL;
error = 0;
}
- up_write(&tomoyo_domain_keeper_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
tomoyo_put_name(saved_domainname);
tomoyo_put_name(saved_program);
kfree(new_entry);
@@ -506,9 +432,8 @@ bool tomoyo_read_domain_keeper_policy(st
struct list_head *pos;
bool done = true;

- down_read(&tomoyo_domain_keeper_list_lock);
- list_for_each_cookie(pos, head->read_var2,
- &tomoyo_domain_keeper_list) {
+ list_for_each_cookie_rcu(pos, head->read_var2,
+ &tomoyo_domain_keeper_list) {
struct tomoyo_domain_keeper_entry *ptr;
const char *no;
const char *from = "";
@@ -529,7 +454,6 @@ bool tomoyo_read_domain_keeper_policy(st
if (!done)
break;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return done;
}

@@ -542,6 +466,8 @@ bool tomoyo_read_domain_keeper_policy(st
*
* Returns true if executing @program supresses domain transition,
* false otherwise.
+ *
+ * Caller holds srcu_read_lock(&tomoyo_ss).
*/
static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
const struct tomoyo_path_info *program,
@@ -550,8 +476,7 @@ static bool tomoyo_is_domain_keeper(cons
struct tomoyo_domain_keeper_entry *ptr;
bool flag = false;

- down_read(&tomoyo_domain_keeper_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
if (ptr->is_deleted)
continue;
if (!ptr->is_last_name) {
@@ -569,7 +494,6 @@ static bool tomoyo_is_domain_keeper(cons
}
flag = true;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return flag;
}

@@ -603,8 +527,7 @@ static bool tomoyo_is_domain_keeper(cons
* /bin/busybox and domainname which the current process will belong to after
* execve() succeeds is calculated using /bin/cat rather than /bin/busybox .
*/
-static LIST_HEAD(tomoyo_alias_list);
-static DECLARE_RWSEM(tomoyo_alias_list_lock);
+LIST_HEAD(tomoyo_alias_list);

/**
* tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
@@ -637,8 +560,8 @@ static int tomoyo_update_alias_entry(con
}
if (!is_delete)
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
- down_write(&tomoyo_alias_list_lock);
- list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
if (ptr->original_name != saved_original_name ||
ptr->aliased_name != saved_aliased_name)
continue;
@@ -651,11 +574,11 @@ static int tomoyo_update_alias_entry(con
saved_original_name = NULL;
new_entry->aliased_name = saved_aliased_name;
saved_aliased_name = NULL;
- list_add_tail(&new_entry->list, &tomoyo_alias_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_alias_list);
new_entry = NULL;
error = 0;
}
- up_write(&tomoyo_alias_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
tomoyo_put_name(saved_original_name);
tomoyo_put_name(saved_aliased_name);
kfree(new_entry);
@@ -674,8 +597,7 @@ bool tomoyo_read_alias_policy(struct tom
struct list_head *pos;
bool done = true;

- down_read(&tomoyo_alias_list_lock);
- list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
+ list_for_each_cookie_rcu(pos, head->read_var2, &tomoyo_alias_list) {
struct tomoyo_alias_entry *ptr;

ptr = list_entry(pos, struct tomoyo_alias_entry, list);
@@ -687,7 +609,6 @@ bool tomoyo_read_alias_policy(struct tom
if (!done)
break;
}
- up_read(&tomoyo_alias_list_lock);
return done;
}

@@ -731,52 +652,18 @@ struct tomoyo_domain_info *tomoyo_find_o
if (!saved_domainname)
return NULL;
new_domain = kmalloc(sizeof(*new_domain), GFP_KERNEL);
- down_write(&tomoyo_domain_list_lock);
+ mutex_lock(&tomoyo_policy_lock);
domain = tomoyo_find_domain(domainname);
- if (domain)
- goto out;
- /* Can I reuse memory of deleted domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
- struct task_struct *p;
- struct tomoyo_acl_info *ptr;
- bool flag;
- if (!domain->is_deleted ||
- domain->domainname != saved_domainname)
- continue;
- flag = false;
- read_lock(&tasklist_lock);
- for_each_process(p) {
- if (tomoyo_real_domain(p) != domain)
- continue;
- flag = true;
- break;
- }
- read_unlock(&tasklist_lock);
- if (flag)
- continue;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
- ptr->type |= TOMOYO_ACL_DELETED;
- }
- domain->ignore_global_allow_read = false;
- domain->domain_transition_failed = false;
- domain->profile = profile;
- domain->quota_warned = false;
- mb(); /* Avoid out-of-order execution. */
- domain->is_deleted = false;
- goto out;
- }
- /* No memory reusable. Create using new memory. */
- if (tomoyo_memory_ok(new_domain)) {
+ if (!domain && tomoyo_memory_ok(new_domain)) {
domain = new_domain;
new_domain = NULL;
INIT_LIST_HEAD(&domain->acl_info_list);
domain->domainname = saved_domainname;
saved_domainname = NULL;
domain->profile = profile;
- list_add_tail(&domain->list, &tomoyo_domain_list);
+ list_add_tail_rcu(&domain->list, &tomoyo_domain_list);
}
- out:
- up_write(&tomoyo_domain_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
tomoyo_put_name(saved_domainname);
kfree(new_domain);
return domain;
@@ -788,6 +675,8 @@ struct tomoyo_domain_info *tomoyo_find_o
* @bprm: Pointer to "struct linux_binprm".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds srcu_read_lock(&tomoyo_ss).
*/
int tomoyo_find_next_domain(struct linux_binprm *bprm)
{
@@ -810,6 +699,7 @@ int tomoyo_find_next_domain(struct linux
struct tomoyo_path_info s; /* symlink name */
struct tomoyo_path_info l; /* last name */
static bool initialized;
+ const int idx = srcu_read_lock(&tomoyo_ss);

if (!tmp)
goto out;
@@ -848,8 +738,7 @@ int tomoyo_find_next_domain(struct linux
if (tomoyo_pathcmp(&r, &s)) {
struct tomoyo_alias_entry *ptr;
/* Is this program allowed to be called via symbolic links? */
- down_read(&tomoyo_alias_list_lock);
- list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
if (ptr->is_deleted ||
tomoyo_pathcmp(&r, ptr->original_name) ||
tomoyo_pathcmp(&s, ptr->aliased_name))
@@ -860,7 +749,6 @@ int tomoyo_find_next_domain(struct linux
tomoyo_fill_path_info(&r);
break;
}
- up_read(&tomoyo_alias_list_lock);
}

/* Check execute permission. */
@@ -891,9 +779,7 @@ int tomoyo_find_next_domain(struct linux
}
if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
goto done;
- down_read(&tomoyo_domain_list_lock);
domain = tomoyo_find_domain(new_domain_name);
- up_read(&tomoyo_domain_list_lock);
if (domain)
goto done;
if (is_enforce)
@@ -910,9 +796,12 @@ int tomoyo_find_next_domain(struct linux
else
old_domain->domain_transition_failed = true;
out:
+ BUG_ON(bprm->cred->security);
if (!domain)
domain = old_domain;
+ atomic_inc(&domain->users);
bprm->cred->security = domain;
+ srcu_read_unlock(&tomoyo_ss, idx);
tomoyo_free(real_program_name);
tomoyo_free(symlink_program_name);
tomoyo_free(tmp);
--- security-testing-2.6.git.orig/security/tomoyo/file.c
+++ security-testing-2.6.git/security/tomoyo/file.c
@@ -14,56 +14,6 @@
#include "realpath.h"
#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])

-/*
- * tomoyo_globally_readable_file_entry is a structure which is used for holding
- * "allow_read" entries.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_globally_readable_list .
- * (2) "filename" is a pathname which is allowed to open(O_RDONLY).
- * (3) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- */
-struct tomoyo_globally_readable_file_entry {
- struct list_head list;
- const struct tomoyo_path_info *filename;
- bool is_deleted;
-};
-
-/*
- * tomoyo_pattern_entry is a structure which is used for holding
- * "tomoyo_pattern_list" entries.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_pattern_list .
- * (2) "pattern" is a pathname pattern which is used for converting pathnames
- * to pathname patterns during learning mode.
- * (3) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- */
-struct tomoyo_pattern_entry {
- struct list_head list;
- const struct tomoyo_path_info *pattern;
- bool is_deleted;
-};
-
-/*
- * tomoyo_no_rewrite_entry is a structure which is used for holding
- * "deny_rewrite" entries.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_no_rewrite_list .
- * (2) "pattern" is a pathname which is by default not permitted to modify
- * already existing content.
- * (3) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- */
-struct tomoyo_no_rewrite_entry {
- struct list_head list;
- const struct tomoyo_path_info *pattern;
- bool is_deleted;
-};
-
/* Keyword array for single path operations. */
static const char *tomoyo_sp_keyword[TOMOYO_MAX_SINGLE_PATH_OPERATION] = {
[TOMOYO_TYPE_READ_WRITE_ACL] = "read/write",
@@ -159,8 +109,8 @@ static struct tomoyo_path_info *tomoyo_g
return NULL;
}

-/* Lock for domain->acl_info_list. */
-DECLARE_RWSEM(tomoyo_domain_acl_info_list_lock);
+/* Lock for modifying TOMOYO's policy. */
+DEFINE_MUTEX(tomoyo_policy_lock);

static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
const char *filename2,
@@ -195,8 +145,7 @@ static int tomoyo_update_single_path_acl
* given "allow_read /lib/libc-2.5.so" to the domain which current process
* belongs to.
*/
-static LIST_HEAD(tomoyo_globally_readable_list);
-static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
+LIST_HEAD(tomoyo_globally_readable_list);

/**
* tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
@@ -221,8 +170,8 @@ static int tomoyo_update_globally_readab
return -ENOMEM;
if (!is_delete)
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
- down_write(&tomoyo_globally_readable_list_lock);
- list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
if (ptr->filename != saved_filename)
continue;
ptr->is_deleted = is_delete;
@@ -232,11 +181,12 @@ static int tomoyo_update_globally_readab
if (!is_delete && error && tomoyo_memory_ok(new_entry)) {
new_entry->filename = saved_filename;
saved_filename = NULL;
- list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
+ list_add_tail_rcu(&new_entry->list,
+ &tomoyo_globally_readable_list);
new_entry = NULL;
error = 0;
}
- up_write(&tomoyo_globally_readable_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
tomoyo_put_name(saved_filename);
kfree(new_entry);
return error;
@@ -248,21 +198,21 @@ static int tomoyo_update_globally_readab
* @filename: The filename to check.
*
* Returns true if any domain can open @filename for reading, false otherwise.
+ *
+ * Caller holds srcu_read_lock(&tomoyo_ss).
*/
static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
filename)
{
struct tomoyo_globally_readable_file_entry *ptr;
bool found = false;
- down_read(&tomoyo_globally_readable_list_lock);
- list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
if (!ptr->is_deleted &&
tomoyo_path_matches_pattern(filename, ptr->filename)) {
found = true;
break;
}
}
- up_read(&tomoyo_globally_readable_list_lock);
return found;
}

@@ -291,9 +241,8 @@ bool tomoyo_read_globally_readable_polic
struct list_head *pos;
bool done = true;

- down_read(&tomoyo_globally_readable_list_lock);
- list_for_each_cookie(pos, head->read_var2,
- &tomoyo_globally_readable_list) {
+ list_for_each_cookie_rcu(pos, head->read_var2,
+ &tomoyo_globally_readable_list) {
struct tomoyo_globally_readable_file_entry *ptr;
ptr = list_entry(pos,
struct tomoyo_globally_readable_file_entry,
@@ -305,7 +254,6 @@ bool tomoyo_read_globally_readable_polic
if (!done)
break;
}
- up_read(&tomoyo_globally_readable_list_lock);
return done;
}

@@ -338,8 +286,7 @@ bool tomoyo_read_globally_readable_polic
* which pretends as if /proc/self/ is not a symlink; so that we can forbid
* current process from accessing other process's information.
*/
-static LIST_HEAD(tomoyo_pattern_list);
-static DECLARE_RWSEM(tomoyo_pattern_list_lock);
+LIST_HEAD(tomoyo_pattern_list);

/**
* tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
@@ -364,8 +311,8 @@ static int tomoyo_update_file_pattern_en
return -ENOMEM;
if (!is_delete)
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
- down_write(&tomoyo_pattern_list_lock);
- list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (saved_pattern != ptr->pattern)
continue;
ptr->is_deleted = is_delete;
@@ -375,11 +322,11 @@ static int tomoyo_update_file_pattern_en
if (!is_delete && error && tomoyo_memory_ok(new_entry)) {
new_entry->pattern = saved_pattern;
saved_pattern = NULL;
- list_add_tail(&new_entry->list, &tomoyo_pattern_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_pattern_list);
new_entry = NULL;
error = 0;
}
- up_write(&tomoyo_pattern_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
tomoyo_put_name(saved_pattern);
kfree(new_entry);
return error;
@@ -391,6 +338,8 @@ static int tomoyo_update_file_pattern_en
* @filename: The filename to find patterned pathname.
*
* Returns pointer to pathname pattern if matched, @filename otherwise.
+ *
+ * Caller holds srcu_read_lock(&tomoyo_ss).
*/
static const struct tomoyo_path_info *
tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
@@ -398,8 +347,7 @@ tomoyo_get_file_pattern(const struct tom
struct tomoyo_pattern_entry *ptr;
const struct tomoyo_path_info *pattern = NULL;

- down_read(&tomoyo_pattern_list_lock);
- list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (ptr->is_deleted)
continue;
if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -412,7 +360,6 @@ tomoyo_get_file_pattern(const struct tom
break;
}
}
- up_read(&tomoyo_pattern_list_lock);
if (pattern)
filename = pattern;
return filename;
@@ -443,8 +390,7 @@ bool tomoyo_read_file_pattern(struct tom
struct list_head *pos;
bool done = true;

- down_read(&tomoyo_pattern_list_lock);
- list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
+ list_for_each_cookie_rcu(pos, head->read_var2, &tomoyo_pattern_list) {
struct tomoyo_pattern_entry *ptr;
ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
if (ptr->is_deleted)
@@ -454,7 +400,6 @@ bool tomoyo_read_file_pattern(struct tom
if (!done)
break;
}
- up_read(&tomoyo_pattern_list_lock);
return done;
}

@@ -487,8 +432,7 @@ bool tomoyo_read_file_pattern(struct tom
* " (deleted)" suffix if the file is already unlink()ed; so that we don't
* need to worry whether the file is already unlink()ed or not.
*/
-static LIST_HEAD(tomoyo_no_rewrite_list);
-static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
+LIST_HEAD(tomoyo_no_rewrite_list);

/**
* tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
@@ -513,8 +457,8 @@ static int tomoyo_update_no_rewrite_entr
return -ENOMEM;
if (!is_delete)
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
- down_write(&tomoyo_no_rewrite_list_lock);
- list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->pattern != saved_pattern)
continue;
ptr->is_deleted = is_delete;
@@ -524,11 +468,11 @@ static int tomoyo_update_no_rewrite_entr
if (!is_delete && error && tomoyo_memory_ok(new_entry)) {
new_entry->pattern = saved_pattern;
saved_pattern = NULL;
- list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_no_rewrite_list);
new_entry = NULL;
error = 0;
}
- up_write(&tomoyo_no_rewrite_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
tomoyo_put_name(saved_pattern);
return error;
}
@@ -540,14 +484,15 @@ static int tomoyo_update_no_rewrite_entr
*
* Returns true if @filename is specified by "deny_rewrite" directive,
* false otherwise.
+ *
+ * Caller holds srcu_read_lock(&tomoyo_ss).
*/
static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
{
struct tomoyo_no_rewrite_entry *ptr;
bool found = false;

- down_read(&tomoyo_no_rewrite_list_lock);
- list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->is_deleted)
continue;
if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -555,7 +500,6 @@ static bool tomoyo_is_no_rewrite_file(co
found = true;
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return found;
}

@@ -584,8 +528,8 @@ bool tomoyo_read_no_rewrite_policy(struc
struct list_head *pos;
bool done = true;

- down_read(&tomoyo_no_rewrite_list_lock);
- list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
+ list_for_each_cookie_rcu(pos, head->read_var2,
+ &tomoyo_no_rewrite_list) {
struct tomoyo_no_rewrite_entry *ptr;
ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
if (ptr->is_deleted)
@@ -595,7 +539,6 @@ bool tomoyo_read_no_rewrite_policy(struc
if (!done)
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return done;
}

@@ -660,9 +603,9 @@ static int tomoyo_check_single_path_acl2
{
struct tomoyo_acl_info *ptr;
int error = -EPERM;
+ const int idx = srcu_read_lock(&tomoyo_ss);

- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_single_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
@@ -680,7 +623,7 @@ static int tomoyo_check_single_path_acl2
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
+ srcu_read_unlock(&tomoyo_ss, idx);
return error;
}

@@ -846,10 +789,10 @@ static int tomoyo_update_single_path_acl
return -ENOMEM;
if (!is_delete)
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
- down_write(&tomoyo_domain_acl_info_list_lock);
+ mutex_lock(&tomoyo_policy_lock);
if (is_delete)
goto delete;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_single_path_acl_record *acl;
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
@@ -877,13 +820,14 @@ static int tomoyo_update_single_path_acl
new_entry->perm |= rw_mask;
new_entry->filename = saved_filename;
saved_filename = NULL;
- list_add_tail(&new_entry->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&new_entry->head.list,
+ &domain->acl_info_list);
new_entry = NULL;
error = 0;
}
goto out;
delete:
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_single_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
@@ -902,7 +846,7 @@ static int tomoyo_update_single_path_acl
break;
}
out:
- up_write(&tomoyo_domain_acl_info_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
tomoyo_put_name(saved_filename);
kfree(new_entry);
return error;
@@ -945,10 +889,10 @@ static int tomoyo_update_double_path_acl
}
if (!is_delete)
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
- down_write(&tomoyo_domain_acl_info_list_lock);
+ mutex_lock(&tomoyo_policy_lock);
if (is_delete)
goto delete;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_double_path_acl_record *acl;
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
@@ -973,13 +917,14 @@ static int tomoyo_update_double_path_acl
saved_filename1 = NULL;
new_entry->filename2 = saved_filename2;
saved_filename2 = NULL;
- list_add_tail(&new_entry->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&new_entry->head.list,
+ &domain->acl_info_list);
new_entry = NULL;
error = 0;
}
goto out;
delete:
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_double_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
@@ -995,7 +940,7 @@ static int tomoyo_update_double_path_acl
break;
}
out:
- up_write(&tomoyo_domain_acl_info_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
tomoyo_put_name(saved_filename1);
tomoyo_put_name(saved_filename2);
kfree(new_entry);
@@ -1040,11 +985,12 @@ static int tomoyo_check_double_path_acl(
struct tomoyo_acl_info *ptr;
const u8 perm = 1 << type;
int error = -EPERM;
+ int idx;

if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
return 0;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ idx = srcu_read_lock(&tomoyo_ss);
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_double_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
@@ -1059,7 +1005,7 @@ static int tomoyo_check_double_path_acl(
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
+ srcu_read_unlock(&tomoyo_ss, idx);
return error;
}

@@ -1169,6 +1115,7 @@ int tomoyo_check_open_permission(struct
struct tomoyo_path_info *buf;
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
+ int idx;

if (!mode || !path->mnt)
return 0;
@@ -1184,6 +1131,7 @@ int tomoyo_check_open_permission(struct
if (!buf)
goto out;
error = 0;
+ idx = srcu_read_lock(&tomoyo_ss);
/*
* If the filename is specified by "deny_rewrite" keyword,
* we need to check "allow_rewrite" permission when the filename is not
@@ -1203,6 +1151,7 @@ int tomoyo_check_open_permission(struct
error = tomoyo_check_single_path_permission2(domain,
TOMOYO_TYPE_TRUNCATE_ACL,
buf, mode);
+ srcu_read_unlock(&tomoyo_ss, idx);
out:
tomoyo_free(buf);
if (!is_enforce)
@@ -1226,6 +1175,7 @@ int tomoyo_check_1path_perm(struct tomoy
struct tomoyo_path_info *buf;
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
+ int idx;

if (!mode || !path->mnt)
return 0;
@@ -1243,8 +1193,10 @@ int tomoyo_check_1path_perm(struct tomoy
tomoyo_fill_path_info(buf);
}
}
+ idx = srcu_read_lock(&tomoyo_ss);
error = tomoyo_check_single_path_permission2(domain, operation, buf,
mode);
+ srcu_read_unlock(&tomoyo_ss, idx);
out:
tomoyo_free(buf);
if (!is_enforce)
@@ -1267,19 +1219,23 @@ int tomoyo_check_rewrite_permission(stru
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
struct tomoyo_path_info *buf;
+ int idx;

if (!mode || !filp->f_path.mnt)
return 0;
buf = tomoyo_get_path(&filp->f_path);
if (!buf)
goto out;
+ idx = srcu_read_lock(&tomoyo_ss);
if (!tomoyo_is_no_rewrite_file(buf)) {
error = 0;
- goto out;
+ goto ok;
}
error = tomoyo_check_single_path_permission2(domain,
TOMOYO_TYPE_REWRITE_ACL,
buf, mode);
+ ok:
+ srcu_read_unlock(&tomoyo_ss, idx);
out:
tomoyo_free(buf);
if (!is_enforce)
@@ -1306,6 +1262,7 @@ int tomoyo_check_2path_perm(struct tomoy
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
const char *msg;
+ int idx;

if (!mode || !path1->mnt || !path2->mnt)
return 0;
@@ -1329,10 +1286,11 @@ int tomoyo_check_2path_perm(struct tomoy
}
}
}
+ idx = srcu_read_lock(&tomoyo_ss);
error = tomoyo_check_double_path_acl(domain, operation, buf1, buf2);
msg = tomoyo_dp2keyword(operation);
if (!error)
- goto out;
+ goto ok;
if (tomoyo_verbose_mode(domain))
printk(KERN_WARNING "TOMOYO-%s: Access '%s %s %s' "
"denied for %s\n", tomoyo_get_msg(is_enforce),
@@ -1344,6 +1302,8 @@ int tomoyo_check_2path_perm(struct tomoy
tomoyo_update_double_path_acl(operation, name1, name2, domain,
false);
}
+ ok:
+ srcu_read_unlock(&tomoyo_ss, idx);
out:
tomoyo_free(buf1);
tomoyo_free(buf2);
--- security-testing-2.6.git.orig/security/tomoyo/realpath.c
+++ security-testing-2.6.git/security/tomoyo/realpath.c
@@ -1,3 +1,4 @@
+
/*
* security/tomoyo/realpath.c
*
@@ -15,6 +16,9 @@
#include <linux/fs_struct.h>
#include "common.h"
#include "realpath.h"
+#include "tomoyo.h"
+
+struct srcu_struct tomoyo_ss;

/**
* tomoyo_encode: Convert binary string to ascii string.
@@ -223,6 +227,17 @@ bool tomoyo_memory_ok(void *ptr)
return false;
}

+/**
+ * tomoyo_free_element - Free memory for elements.
+ *
+ * @ptr: Pointer to allocated memory.
+ */
+static void tomoyo_free_element(void *ptr)
+{
+ atomic_sub(ksize(ptr), &tomoyo_allocated_memory_for_elements);
+ kfree(ptr);
+}
+
/* Memory allocated for string data in bytes. */
static atomic_t tomoyo_allocated_memory_for_savename;
/* Quota for holding string data in bytes. */
@@ -238,15 +253,10 @@ static unsigned int tomoyo_quota_for_sav
/*
* tomoyo_name_entry is a structure which is used for linking
* "struct tomoyo_path_info" into tomoyo_name_list .
- *
- * Since tomoyo_name_list manages a list of strings which are shared by
- * multiple processes (whereas "struct tomoyo_path_info" inside
- * "struct tomoyo_path_info_with_data" is not shared), a reference counter will
- * be added to "struct tomoyo_name_entry" rather than "struct tomoyo_path_info"
- * when TOMOYO starts supporting garbage collector.
*/
struct tomoyo_name_entry {
struct list_head list;
+ atomic_t users;
struct tomoyo_path_info entry;
};

@@ -287,10 +297,11 @@ const struct tomoyo_path_info *tomoyo_ge
entry = kmalloc(sizeof(*entry) + len, GFP_KERNEL);
allocated_len = entry ? ksize(entry) : 0;
mutex_lock(&tomoyo_name_list_lock);
- list_for_each_entry(ptr, &tomoyo_name_list[hash % TOMOYO_MAX_HASH],
- list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_name_list[hash % TOMOYO_MAX_HASH],
+ list) {
if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
continue;
+ atomic_inc(&ptr->users);
error = 0;
break;
}
@@ -305,8 +316,9 @@ const struct tomoyo_path_info *tomoyo_ge
ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
memmove((char *) ptr->entry.name, name, len);
tomoyo_fill_path_info(&ptr->entry);
- list_add_tail(&ptr->list,
- &tomoyo_name_list[hash % TOMOYO_MAX_HASH]);
+ atomic_set(&ptr->users, 1);
+ list_add_tail_rcu(&ptr->list,
+ &tomoyo_name_list[hash % TOMOYO_MAX_HASH]);
entry = NULL;
error = 0;
}
@@ -321,6 +333,31 @@ const struct tomoyo_path_info *tomoyo_ge
}

/**
+ * tomoyo_put_name - Delete shared memory for string data.
+ *
+ * @ptr: Pointer to "struct tomoyo_path_info".
+ */
+void tomoyo_put_name(const struct tomoyo_path_info *name)
+{
+ struct tomoyo_name_entry *ptr;
+ bool can_delete = false;
+
+ if (!name)
+ return;
+ ptr = container_of(name, struct tomoyo_name_entry, entry);
+ mutex_lock(&tomoyo_name_list_lock);
+ if (atomic_dec_and_test(&ptr->users)) {
+ list_del(&ptr->list);
+ can_delete = true;
+ }
+ mutex_unlock(&tomoyo_name_list_lock);
+ if (can_delete) {
+ atomic_sub(ksize(ptr), &tomoyo_allocated_memory_for_savename);
+ kfree(ptr);
+ }
+}
+
+/**
* tomoyo_realpath_init - Initialize realpath related code.
*/
void __init tomoyo_realpath_init(void)
@@ -331,12 +368,14 @@ void __init tomoyo_realpath_init(void)
for (i = 0; i < TOMOYO_MAX_HASH; i++)
INIT_LIST_HEAD(&tomoyo_name_list[i]);
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
+ if (init_srcu_struct(&tomoyo_ss))
+ panic("Can't initialize tomoyo_ss");
tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME);
- list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
- down_read(&tomoyo_domain_list_lock);
+ list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
+ i = srcu_read_lock(&tomoyo_ss);
if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
panic("Can't register tomoyo_kernel_domain");
- up_read(&tomoyo_domain_list_lock);
+ srcu_read_unlock(&tomoyo_ss, i);
}

/* Memory allocated for temporary purpose. */
@@ -431,3 +470,311 @@ int tomoyo_write_memory_quota(struct tom
tomoyo_quota_for_elements = size;
return 0;
}
+
+/* Garbage collecter functions */
+
+static inline void tomoyo_gc_del_domain_initializer
+(struct tomoyo_domain_initializer_entry *ptr)
+{
+ tomoyo_put_name(ptr->domainname);
+ tomoyo_put_name(ptr->program);
+}
+
+static inline void tomoyo_gc_del_domain_keeper
+(struct tomoyo_domain_keeper_entry *ptr)
+{
+ tomoyo_put_name(ptr->domainname);
+ tomoyo_put_name(ptr->program);
+}
+
+static inline void tomoyo_gc_del_alias(struct tomoyo_alias_entry *ptr)
+{
+ tomoyo_put_name(ptr->original_name);
+ tomoyo_put_name(ptr->aliased_name);
+}
+
+static inline void tomoyo_gc_del_readable
+(struct tomoyo_globally_readable_file_entry *ptr)
+{
+ tomoyo_put_name(ptr->filename);
+}
+
+static inline void tomoyo_gc_del_pattern(struct tomoyo_pattern_entry *ptr)
+{
+ tomoyo_put_name(ptr->pattern);
+}
+
+static inline void tomoyo_gc_del_no_rewrite
+(struct tomoyo_no_rewrite_entry *ptr)
+{
+ tomoyo_put_name(ptr->pattern);
+}
+
+static inline void tomoyo_gc_del_manager
+(struct tomoyo_policy_manager_entry *ptr)
+{
+ tomoyo_put_name(ptr->manager);
+}
+
+static void tomoyo_gc_del_acl(struct tomoyo_acl_info *acl)
+{
+ switch (tomoyo_acl_type1(acl)) {
+ struct tomoyo_single_path_acl_record *acl1;
+ struct tomoyo_double_path_acl_record *acl2;
+ case TOMOYO_TYPE_SINGLE_PATH_ACL:
+ acl1 = container_of(acl, struct tomoyo_single_path_acl_record,
+ head);
+ tomoyo_put_name(acl1->filename);
+ break;
+ case TOMOYO_TYPE_DOUBLE_PATH_ACL:
+ acl2 = container_of(acl, struct tomoyo_double_path_acl_record,
+ head);
+ tomoyo_put_name(acl2->filename1);
+ tomoyo_put_name(acl2->filename2);
+ break;
+ }
+}
+
+static bool tomoyo_gc_del_domain(struct tomoyo_domain_info *domain)
+{
+ struct tomoyo_acl_info *acl;
+ struct tomoyo_acl_info *tmp;
+ /*
+ * We need to recheck domain->users because
+ * tomoyo_find_next_domain() increments it.
+ */
+ if (atomic_read(&domain->users))
+ return false;
+ /* Delete all entries in this domain. */
+ list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) {
+ list_del_rcu(&acl->list);
+ tomoyo_gc_del_acl(acl);
+ tomoyo_free_element(acl);
+ }
+ tomoyo_put_name(domain->domainname);
+ return true;
+}
+
+enum tomoyo_gc_id {
+ TOMOYO_ID_DOMAIN_INITIALIZER,
+ TOMOYO_ID_DOMAIN_KEEPER,
+ TOMOYO_ID_ALIAS,
+ TOMOYO_ID_GLOBALLY_READABLE,
+ TOMOYO_ID_PATTERN,
+ TOMOYO_ID_NO_REWRITE,
+ TOMOYO_ID_MANAGER,
+ TOMOYO_ID_ACL,
+ TOMOYO_ID_DOMAIN
+};
+
+struct tomoyo_gc_entry {
+ struct list_head list;
+ int type;
+ void *element;
+};
+
+
+/* Caller holds tomoyo_policy_lock mutex. */
+static bool tomoyo_add_to_gc(const int type, void *element,
+ struct list_head *head)
+{
+ struct tomoyo_gc_entry *entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return false;
+ entry->type = type;
+ entry->element = element;
+ list_add(&entry->list, head);
+ return true;
+}
+
+/**
+ * tomoyo_gc_thread_main - Garbage collector thread for TOMOYO.
+ *
+ * @unused: Not used.
+ *
+ * This function is exclusively executed.
+ */
+static int tomoyo_gc_thread_main(void *unused)
+{
+ static DEFINE_MUTEX(tomoyo_gc_mutex);
+ static LIST_HEAD(tomoyo_gc_queue);
+ if (!mutex_trylock(&tomoyo_gc_mutex))
+ return 0;
+
+ mutex_lock(&tomoyo_policy_lock);
+ {
+ struct tomoyo_globally_readable_file_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list,
+ list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_GLOBALLY_READABLE, ptr,
+ &tomoyo_gc_queue))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_pattern_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_PATTERN, ptr,
+ &tomoyo_gc_queue))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_no_rewrite_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_NO_REWRITE, ptr,
+ &tomoyo_gc_queue))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_domain_initializer_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list,
+ list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_INITIALIZER,
+ ptr, &tomoyo_gc_queue))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_domain_keeper_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list,
+ list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_KEEPER, ptr,
+ &tomoyo_gc_queue))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_alias_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_ALIAS, ptr,
+ &tomoyo_gc_queue))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_policy_manager_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list,
+ list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_MANAGER, ptr,
+ &tomoyo_gc_queue))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_domain_info *domain;
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+ struct tomoyo_acl_info *acl;
+ list_for_each_entry_rcu(acl, &domain->acl_info_list,
+ list) {
+ if (!(acl->type & TOMOYO_ACL_DELETED))
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_ACL, acl,
+ &tomoyo_gc_queue))
+ list_del_rcu(&acl->list);
+ else
+ break;
+ }
+ if (domain->is_deleted &&
+ !atomic_read(&domain->users)) {
+ if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, domain,
+ &tomoyo_gc_queue))
+ list_del_rcu(&domain->list);
+ else
+ break;
+ }
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ if (list_empty(&tomoyo_gc_queue))
+ goto done;
+ synchronize_srcu(&tomoyo_ss);
+ {
+ struct tomoyo_gc_entry *p;
+ struct tomoyo_gc_entry *tmp;
+ list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) {
+ switch (p->type) {
+ case TOMOYO_ID_DOMAIN_INITIALIZER:
+ tomoyo_gc_del_domain_initializer(p->element);
+ break;
+ case TOMOYO_ID_DOMAIN_KEEPER:
+ tomoyo_gc_del_domain_keeper(p->element);
+ break;
+ case TOMOYO_ID_ALIAS:
+ tomoyo_gc_del_alias(p->element);
+ break;
+ case TOMOYO_ID_GLOBALLY_READABLE:
+ tomoyo_gc_del_readable(p->element);
+ break;
+ case TOMOYO_ID_PATTERN:
+ tomoyo_gc_del_pattern(p->element);
+ break;
+ case TOMOYO_ID_NO_REWRITE:
+ tomoyo_gc_del_no_rewrite(p->element);
+ break;
+ case TOMOYO_ID_MANAGER:
+ tomoyo_gc_del_manager(p->element);
+ break;
+ case TOMOYO_ID_ACL:
+ tomoyo_gc_del_acl(p->element);
+ break;
+ case TOMOYO_ID_DOMAIN:
+ if (!tomoyo_gc_del_domain(p->element))
+ continue;
+ break;
+ }
+ tomoyo_free_element(p->element);
+ list_del(&p->list);
+ kfree(p);
+ }
+ }
+ done:
+ mutex_unlock(&tomoyo_gc_mutex);
+ return 0;
+}
+
+/**
+ * tomoyo_gc_thread - Garbage collector thread for TOMOYO.
+ *
+ * @unused: Not used.
+ */
+int tomoyo_gc_thread(void *unused)
+{
+ /*
+ * Maybe this thread should be created and terminated as needed
+ * rather than created upon boot and living forever...
+ */
+ while (1) {
+ msleep(30000);
+ tomoyo_gc_thread_main(unused);
+ }
+}
--- security-testing-2.6.git.orig/security/tomoyo/realpath.h
+++ security-testing-2.6.git/security/tomoyo/realpath.h
@@ -44,10 +44,7 @@ bool tomoyo_memory_ok(void *ptr);
* The RAM is shared, so NEVER try to modify or kfree() the returned name.
*/
const struct tomoyo_path_info *tomoyo_get_name(const char *name);
-static inline void tomoyo_put_name(const struct tomoyo_path_info *name)
-{
- /* It's a dummy so far. */
-}
+void tomoyo_put_name(const struct tomoyo_path_info *name);

/* Allocate memory for temporary use (e.g. permission checks). */
void *tomoyo_alloc(const size_t size);
--- security-testing-2.6.git.orig/security/tomoyo/tomoyo.c
+++ security-testing-2.6.git/security/tomoyo/tomoyo.c
@@ -22,9 +22,19 @@ static int tomoyo_cred_prepare(struct cr
* we don't need to duplicate.
*/
new->security = old->security;
+ if (new->security)
+ atomic_inc(&((struct tomoyo_domain_info *)
+ new->security)->users);
return 0;
}

+static void tomoyo_cred_free(struct cred *cred)
+{
+ struct tomoyo_domain_info *domain = cred->security;
+ if (domain)
+ atomic_dec(&domain->users);
+}
+
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
{
int rc;
@@ -49,7 +59,11 @@ static int tomoyo_bprm_set_creds(struct
* Tell tomoyo_bprm_check_security() is called for the first time of an
* execve operation.
*/
- bprm->cred->security = NULL;
+ if (bprm->cred->security) {
+ atomic_dec(&((struct tomoyo_domain_info *)
+ bprm->cred->security)->users);
+ bprm->cred->security = NULL;
+ }
return 0;
}

@@ -263,6 +277,7 @@ static int tomoyo_dentry_open(struct fil
static struct security_operations tomoyo_security_ops = {
.name = "tomoyo",
.cred_prepare = tomoyo_cred_prepare,
+ .cred_free = tomoyo_cred_free,
.bprm_set_creds = tomoyo_bprm_set_creds,
.bprm_check_security = tomoyo_bprm_check_security,
#ifdef CONFIG_SYSCTL
@@ -291,6 +306,7 @@ static int __init tomoyo_init(void)
panic("Failure registering TOMOYO Linux");
printk(KERN_INFO "TOMOYO Linux initialized\n");
cred->security = &tomoyo_kernel_domain;
+ atomic_inc(&tomoyo_kernel_domain.users);
tomoyo_realpath_init();
return 0;
}
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/