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

From: Paul E. McKenney
Date: Thu Jun 18 2009 - 12:05:42 EST


On Thu, Jun 18, 2009 at 03:45:59PM +0900, Tetsuo Handa wrote:
> 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, that does make things easier. ;-)

I won't bother with set_srcu_timeout(), then.

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

I was able to make it through about the first 16% before reality intruded.
Assuming I can trust the various "Caller holds srcu_read_lock(&tomoyo_ss)"
comments, that part of the code looks good.

Thanx, Paul

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