[PATCH 3/3] TOMOYO: Add RCU-like garbage collector.

From: Tetsuo Handa
Date: Wed Jun 17 2009 - 07:23:49 EST


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 "atomic_t users" to
"struct tomoyo_domain_info" and "struct tomoyo_name_entry" structures
and adds global counter "atomic_t tomoyo_users_counter[2]" and
active counter indicator "tomoyo_users_counter_idx".

Reader threads do "idx = atomic_read(&tomoyo_users_counter_idx);" and
"atomic_inc(&tomoyo_users_counter[idx]);" before start reading, and do
"atomic_dec(&tomoyo_users_counter[idx]);" after finished reading.

The garbage collector thread removes marked-as-deleted elements using
list_del_rcu(). Then, GC updates "tomoyo_users_counter_idx" so that subsequent
readers shall use the other global counter. Then, GC waits for previously used
global counter to become 0, which indicates that RCU's grace period has
expired. To be able to release all marked-as-deleted elements with single
RCU grace period, GC temporarily stores marked-as-deleted elements in a private
list.

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

--- security-testing-2.6.git.orig/security/tomoyo/common.c
+++ security-testing-2.6.git/security/tomoyo/common.c
@@ -12,10 +12,14 @@
#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"

+atomic_t tomoyo_users_counter[2];
+atomic_t tomoyo_users_counter_idx;
+
/* Has loading policy done? */
bool tomoyo_policy_loaded;

@@ -340,10 +344,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 tomoyo_lock().
*/
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
{
@@ -352,7 +355,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 +791,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 tomoyo_lock().
*/
bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
{
@@ -796,8 +801,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 +854,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 +1032,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 +1061,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 +1093,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 +1105,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 +1149,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 +1160,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 +1169,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 tomoyo_lock().
*/
static bool tomoyo_is_policy_manager(void)
{
@@ -1202,29 +1184,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 +1223,8 @@ static bool tomoyo_is_policy_manager(voi
* @data: String to parse.
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_lock().
*/
static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
const char *data)
@@ -1260,11 +1240,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 +1255,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 +1275,8 @@ static bool tomoyo_is_select_one(struct
* @domainname: The name of domain.
*
* Returns 0.
+ *
+ * Caller holds tomoyo_lock().
*/
static int tomoyo_delete_domain(char *domainname)
{
@@ -1308,9 +1285,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 +1297,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 +1307,8 @@ static int tomoyo_delete_domain(char *do
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_lock().
*/
static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
{
@@ -1352,11 +1331,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 +1488,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 +1519,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 +1528,6 @@ acl_loop:
if (!done)
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (!done)
break;
head->read_step = 3;
@@ -1565,7 +1539,6 @@ tail_mark:
if (head->read_single_domain)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1581,6 +1554,8 @@ tail_mark:
*
* ( echo "select " $domainname; echo "use_profile " $profile ) |
* /usr/lib/ccs/loadpolicy -d
+ *
+ * Caller holds tomoyo_lock().
*/
static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
{
@@ -1592,9 +1567,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 +1597,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 +1607,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 +1825,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 = tomoyo_lock();
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);
+ tomoyo_unlock(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 +1976,7 @@ static int tomoyo_open_control(const u8
}
}
file->private_data = head;
+ head->tomoyo_users_counter_index = tomoyo_lock();
/*
* Call the handler now if the file is
* /sys/kernel/security/tomoyo/self_domain
@@ -2114,6 +2094,7 @@ static int tomoyo_write_control(struct f
static int tomoyo_close_control(struct file *file)
{
struct tomoyo_io_buffer *head = file->private_data;
+ tomoyo_unlock(head->tomoyo_users_counter_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_users_counter_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,193 @@ 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))
+
+extern atomic_t tomoyo_users_counter[2];
+extern atomic_t tomoyo_users_counter_idx;
+
+static inline int tomoyo_lock(void)
+{
+ int idx = atomic_read(&tomoyo_users_counter_idx);
+ atomic_inc(&tomoyo_users_counter[idx]);
+ return idx;
+}
+
+static inline void tomoyo_unlock(int idx)
+{
+ atomic_dec(&tomoyo_users_counter[idx]);
+}
+
+/*
+ * 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 tomoyo_lock().
*/
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 tomoyo_lock().
*/
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 tomoyo_lock().
*/
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 = tomoyo_lock();

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;
+ tomoyo_unlock(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 tomoyo_lock().
*/
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 tomoyo_lock().
*/
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 tomoyo_lock().
*/
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 = tomoyo_lock();

- 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);
+ tomoyo_unlock(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 = tomoyo_lock();
+ 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);
+ tomoyo_unlock(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 = tomoyo_lock();
/*
* 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);
+ tomoyo_unlock(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 = tomoyo_lock();
error = tomoyo_check_single_path_permission2(domain, operation, buf,
mode);
+ tomoyo_unlock(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 = tomoyo_lock();
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:
+ tomoyo_unlock(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 = tomoyo_lock();
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:
+ tomoyo_unlock(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
@@ -15,6 +15,7 @@
#include <linux/fs_struct.h>
#include "common.h"
#include "realpath.h"
+#include "tomoyo.h"

/**
* tomoyo_encode: Convert binary string to ascii string.
@@ -223,6 +224,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 +250,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 +294,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 +313,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 +330,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)
@@ -332,11 +366,11 @@ void __init tomoyo_realpath_init(void)
INIT_LIST_HEAD(&tomoyo_name_list[i]);
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME);
- list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
- down_read(&tomoyo_domain_list_lock);
+ list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
+ i = tomoyo_lock();
if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
panic("Can't register tomoyo_kernel_domain");
- up_read(&tomoyo_domain_list_lock);
+ tomoyo_unlock(i);
}

/* Memory allocated for temporary purpose. */
@@ -431,3 +465,327 @@ 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;
+ {
+ /* Swap active counter. */
+ const int idx = atomic_read(&tomoyo_users_counter_idx);
+ atomic_set(&tomoyo_users_counter_idx, idx ^ 1);
+ /*
+ * Wait for readers who are using previously active counter.
+ * This is similar to synchronize_rcu() while this code allows
+ * readers to do operations which may sleep.
+ */
+ while (atomic_read(&tomoyo_users_counter[idx]))
+ msleep(1000);
+ }
+ {
+ /*
+ * Nobody is using previously active counter.
+ * Ready to release memory of elements removed from the list
+ * during previously active counter was active.
+ */
+ 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/