From: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
To: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Cc: linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 3/3] TOMOYO: Add SRCU based garbage collector.
Date: Thu, 18 Jun 2009 09:05:21 -0700 [thread overview]
Message-ID: <20090618160521.GC6794@linux.vnet.ibm.com> (raw)
In-Reply-To: <200906180645.n5I6jxQn096226@www262.sakura.ne.jp>
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@I-love.SAKURA.ne.jp>
> ---
> 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;
> }
next prev parent reply other threads:[~2009-06-18 16:05 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-06-17 11:19 [PATCH] TOMOYO: Add garbage collector support. (v3) Tetsuo Handa
2009-06-17 11:21 ` [PATCH 1/3] TOMOYO: Move sleeping operations to outside the semaphore Tetsuo Handa
2009-06-17 11:22 ` [PATCH 2/3] TOMOYO: Replace tomoyo_save_name() with tomoyo_get_name()/tomoyo_put_name() Tetsuo Handa
2009-06-17 11:23 ` [PATCH 3/3] TOMOYO: Add RCU-like garbage collector Tetsuo Handa
2009-06-17 12:28 ` [PATCH] TOMOYO: Add garbage collector support. (v3) Peter Zijlstra
2009-06-17 16:31 ` Paul E. McKenney
2009-06-18 5:34 ` Tetsuo Handa
2009-06-18 6:45 ` [PATCH 3/3] TOMOYO: Add SRCU based garbage collector Tetsuo Handa
2009-06-18 16:05 ` Paul E. McKenney [this message]
2009-06-18 15:28 ` [PATCH] TOMOYO: Add garbage collector support. (v3) Paul E. McKenney
2009-06-19 4:57 ` Tetsuo Handa
2009-06-20 1:28 ` Paul E. McKenney
2009-06-20 7:04 ` Tetsuo Handa
2009-06-21 4:07 ` Paul E. McKenney
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20090618160521.GC6794@linux.vnet.ibm.com \
--to=paulmck@linux.vnet.ibm.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=penguin-kernel@i-love.sakura.ne.jp \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.