* [PATCH] TOMOYO: Use RCU primitives for list operation
@ 2009-12-11 13:53 Tetsuo Handa
2009-12-14 16:39 ` Serge E. Hallyn
0 siblings, 1 reply; 6+ messages in thread
From: Tetsuo Handa @ 2009-12-11 13:53 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel
Can somebody please review?
----------
[PATCH] TOMOYO: Use RCU primitives for list operation
Currently, TOMOYO is using down_read() only for protecting ->prev member from
writers. If we convert list operation using RCU primitives so that readers don't
look at ->prev member, we can replace down_read() by srcu_read_lock().
tomoyo_read_lock()/tomoyo_read_unlock() (these are wrappers for
srcu_read_lock()/srcu_read_unlock()) protects the data against the garbage
collector (which will be added in the future). I call tomoyo_read_lock() when
/sys/kernel/security/tomoyo/ interface is open()ed and call
tomoyo_read_unlock() when /sys/kernel/security/tomoyo/ interface is close()d
rather than calling tomoyo_read_lock()/tomoyo_read_unlock() upon individual
read()/write() requests. In this way, the pointers saved in
"struct tomoyo_io_buffer" are guaranteed to be valid. Please ignore
warning: context imbalance in 'tomoyo_open_control' - wrong count at exit
warning: context imbalance in 'tomoyo_close_control' - unexpected unlock
messages when built with "C=1" option.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
common.c | 90 +++++++++++++++++++++++++++----------------------
common.h | 28 +++++++++++----
domain.c | 63 ++++++++++++++++++++--------------
file.c | 110 ++++++++++++++++++++++++++++++++++++++++++-------------------
realpath.c | 8 ++--
tomoyo.c | 20 +++++++++--
6 files changed, 207 insertions(+), 112 deletions(-)
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index 6c60616..f01b936 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -365,10 +365,9 @@ bool tomoyo_is_domain_def(const unsigned char *buffer)
*
* @domainname: The domainname to find.
*
- * Caller must call down_read(&tomoyo_domain_list_lock); or
- * down_write(&tomoyo_domain_list_lock); .
- *
* Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
{
@@ -377,7 +376,7 @@ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
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;
@@ -829,6 +828,8 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain)
* @domain: Pointer to "struct tomoyo_domain_info".
*
* Returns true if the domain is not exceeded quota, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
{
@@ -837,8 +838,7 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
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)) {
@@ -866,7 +866,6 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
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) {
@@ -1096,6 +1095,8 @@ static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_manager_entry(const char *manager,
const bool is_delete)
@@ -1118,7 +1119,7 @@ static int tomoyo_update_manager_entry(const char *manager,
if (!saved_manager)
return -ENOMEM;
down_write(&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->manager != saved_manager)
continue;
ptr->is_deleted = is_delete;
@@ -1134,7 +1135,7 @@ static int tomoyo_update_manager_entry(const char *manager,
goto out;
new_entry->manager = saved_manager;
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);
error = 0;
out:
up_write(&tomoyo_policy_manager_list_lock);
@@ -1147,6 +1148,8 @@ static int tomoyo_update_manager_entry(const char *manager,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
{
@@ -1166,6 +1169,8 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
{
@@ -1174,7 +1179,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
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) {
struct tomoyo_policy_manager_entry *ptr;
@@ -1186,7 +1190,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_policy_manager_list_lock);
head->read_eof = done;
return 0;
}
@@ -1196,6 +1199,8 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
*
* Returns true if the current process is permitted to modify policy
* via /sys/kernel/security/tomoyo/ interface.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_policy_manager(void)
{
@@ -1209,29 +1214,25 @@ static bool tomoyo_is_policy_manager(void)
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;
@@ -1252,6 +1253,8 @@ static bool tomoyo_is_policy_manager(void)
* @data: String to parse.
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
const char *data)
@@ -1267,11 +1270,8 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
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;
@@ -1285,13 +1285,11 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
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;
@@ -1307,6 +1305,8 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
* @domainname: The name of domain.
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_delete_domain(char *domainname)
{
@@ -1317,7 +1317,7 @@ static int tomoyo_delete_domain(char *domainname)
tomoyo_fill_path_info(&name);
down_write(&tomoyo_domain_list_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;
@@ -1337,6 +1337,8 @@ static int tomoyo_delete_domain(char *domainname)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
{
@@ -1359,11 +1361,9 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
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;
@@ -1508,6 +1508,8 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
{
@@ -1519,7 +1521,6 @@ static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
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) {
struct tomoyo_domain_info *domain;
const char *quota_exceeded = "";
@@ -1552,7 +1553,6 @@ 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) {
struct tomoyo_acl_info *ptr
@@ -1562,7 +1562,6 @@ acl_loop:
if (!done)
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (!done)
break;
head->read_step = 3;
@@ -1574,7 +1573,6 @@ tail_mark:
if (head->read_single_domain)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1590,6 +1588,8 @@ tail_mark:
*
* ( echo "select " $domainname; echo "use_profile " $profile ) |
* /usr/lib/ccs/loadpolicy -d
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
{
@@ -1601,9 +1601,7 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
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
@@ -1625,6 +1623,8 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
* awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
* domainname = $0; } else if ( $1 == "use_profile" ) {
* print $2 " " domainname; domainname = ""; } } ; '
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
{
@@ -1633,7 +1633,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
if (head->read_eof)
return 0;
- down_read(&tomoyo_domain_list_lock);
list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
struct tomoyo_domain_info *domain;
domain = list_entry(pos, struct tomoyo_domain_info, list);
@@ -1644,7 +1643,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1701,6 +1699,8 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
{
@@ -1735,6 +1735,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, -EINVAL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
{
@@ -1864,15 +1866,13 @@ void tomoyo_load_policy(const char *filename)
tomoyo_policy_loaded = true;
{ /* Check all profiles currently assigned to domains are defined. */
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);
}
}
@@ -1920,6 +1920,8 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
* @file: Pointer to "struct file".
*
* Associates policy handler and returns 0 on success, -ENOMEM otherwise.
+ *
+ * Caller acquires tomoyo_read_lock().
*/
static int tomoyo_open_control(const u8 type, struct file *file)
{
@@ -2005,6 +2007,7 @@ static int tomoyo_open_control(const u8 type, struct file *file)
return -ENOMEM;
}
}
+ head->reader_idx = tomoyo_read_lock();
file->private_data = head;
/*
* Call the handler now if the file is
@@ -2026,6 +2029,8 @@ static int tomoyo_open_control(const u8 type, struct file *file)
* @buffer_len: Size of @buffer.
*
* Returns bytes read on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_control(struct file *file, char __user *buffer,
const int buffer_len)
@@ -2069,6 +2074,8 @@ static int tomoyo_read_control(struct file *file, char __user *buffer,
* @buffer_len: Size of @buffer.
*
* Returns @buffer_len on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_control(struct file *file, const char __user *buffer,
const int buffer_len)
@@ -2119,11 +2126,14 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer,
* @file: Pointer to "struct file".
*
* Releases memory and returns 0.
+ *
+ * Caller looses tomoyo_read_lock().
*/
static int tomoyo_close_control(struct file *file)
{
struct tomoyo_io_buffer *head = file->private_data;
+ tomoyo_read_unlock(head->reader_idx);
/* Release memory used for policy I/O. */
tomoyo_free(head->read_buf);
head->read_buf = NULL;
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index bd10f9f..c6f1392 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -269,6 +269,8 @@ struct tomoyo_io_buffer {
int (*write) (struct tomoyo_io_buffer *);
/* Exclusive lock for this structure. */
struct mutex io_sem;
+ /* Index returned by tomoyo_read_lock(). */
+ int reader_idx;
/* The position currently reading from. */
struct list_head *read_var1;
/* Extra variables for reading. */
@@ -446,16 +448,28 @@ extern struct tomoyo_domain_info tomoyo_kernel_domain;
* @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) \
- for (({ if (!cookie) \
- cookie = head; }), \
- pos = (cookie)->next; \
- prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
- (cookie) = pos, pos = pos->next)
+#define list_for_each_cookie(pos, cookie, head) \
+ for (({ if (!cookie) \
+ cookie = head; }), \
+ pos = rcu_dereference((cookie)->next); \
+ prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
+ (cookie) = pos, pos = rcu_dereference(pos->next))
+
+extern struct srcu_struct tomoyo_ss;
+
+static inline int tomoyo_read_lock(void)
+{
+ return srcu_read_lock(&tomoyo_ss);
+}
+
+static inline void tomoyo_read_unlock(int idx)
+{
+ srcu_read_unlock(&tomoyo_ss, idx);
+}
#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index fcf52ac..2fd1901 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -217,6 +217,8 @@ static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_domain_initializer_entry(const char *domainname,
const char *program,
@@ -246,7 +248,7 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
if (!saved_program)
return -ENOMEM;
down_write(&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_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -266,7 +268,7 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
new_entry->program = saved_program;
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);
error = 0;
out:
up_write(&tomoyo_domain_initializer_list_lock);
@@ -279,13 +281,14 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
{
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) {
const char *no;
@@ -308,7 +311,6 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return done;
}
@@ -320,6 +322,8 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
const bool is_delete)
@@ -345,6 +349,8 @@ int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
*
* Returns true if executing @program reinitializes domain transition,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
domainname,
@@ -355,8 +361,7 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
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) {
@@ -376,7 +381,6 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
}
flag = true;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return flag;
}
@@ -430,6 +434,8 @@ static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_domain_keeper_entry(const char *domainname,
const char *program,
@@ -459,7 +465,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
if (!saved_domainname)
return -ENOMEM;
down_write(&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_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -479,7 +485,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
new_entry->program = saved_program;
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);
error = 0;
out:
up_write(&tomoyo_domain_keeper_list_lock);
@@ -493,6 +499,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
* @is_not: True if it is "no_keep_domain" entry.
* @is_delete: True if it is a delete request.
*
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
const bool is_delete)
@@ -513,13 +520,14 @@ int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
{
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) {
struct tomoyo_domain_keeper_entry *ptr;
@@ -542,7 +550,6 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return done;
}
@@ -555,6 +562,8 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
*
* Returns true if executing @program supresses domain transition,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
const struct tomoyo_path_info *program,
@@ -563,8 +572,7 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
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) {
@@ -582,7 +590,6 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
}
flag = true;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return flag;
}
@@ -627,6 +634,8 @@ static DECLARE_RWSEM(tomoyo_alias_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_alias_entry(const char *original_name,
const char *aliased_name,
@@ -646,7 +655,7 @@ static int tomoyo_update_alias_entry(const char *original_name,
if (!saved_original_name || !saved_aliased_name)
return -ENOMEM;
down_write(&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->original_name != saved_original_name ||
ptr->aliased_name != saved_aliased_name)
continue;
@@ -663,7 +672,7 @@ static int tomoyo_update_alias_entry(const char *original_name,
goto out;
new_entry->original_name = saved_original_name;
new_entry->aliased_name = saved_aliased_name;
- list_add_tail(&new_entry->list, &tomoyo_alias_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_alias_list);
error = 0;
out:
up_write(&tomoyo_alias_list_lock);
@@ -676,13 +685,14 @@ static int tomoyo_update_alias_entry(const char *original_name,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_alias_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
struct tomoyo_alias_entry *ptr;
@@ -695,7 +705,6 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_alias_list_lock);
return done;
}
@@ -706,6 +715,8 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_alias_policy(char *data, const bool is_delete)
{
@@ -724,6 +735,8 @@ int tomoyo_write_alias_policy(char *data, const bool is_delete)
* @profile: Profile number to assign if the domain was newly created.
*
* Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
domainname,
@@ -742,7 +755,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
if (!saved_domainname)
goto out;
/* Can I reuse memory of deleted domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
struct task_struct *p;
struct tomoyo_acl_info *ptr;
bool flag;
@@ -760,7 +773,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
read_unlock(&tasklist_lock);
if (flag)
continue;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
ptr->type |= TOMOYO_ACL_DELETED;
}
tomoyo_set_domain_flag(domain, true, domain->flags);
@@ -776,7 +789,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
INIT_LIST_HEAD(&domain->acl_info_list);
domain->domainname = saved_domainname;
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);
@@ -789,6 +802,8 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
* @bprm: Pointer to "struct linux_binprm".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_find_next_domain(struct linux_binprm *bprm)
{
@@ -849,8 +864,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
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))
@@ -861,7 +875,6 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
tomoyo_fill_path_info(&r);
break;
}
- up_read(&tomoyo_alias_list_lock);
}
/* Check execute permission. */
@@ -892,9 +905,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
}
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)
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
index 482f0e7..3c47286 100644
--- a/security/tomoyo/file.c
+++ b/security/tomoyo/file.c
@@ -213,6 +213,8 @@ static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_globally_readable_entry(const char *filename,
const bool is_delete)
@@ -228,7 +230,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
if (!saved_filename)
return -ENOMEM;
down_write(&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->filename != saved_filename)
continue;
ptr->is_deleted = is_delete;
@@ -243,7 +245,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
if (!new_entry)
goto out;
new_entry->filename = saved_filename;
- list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_globally_readable_list);
error = 0;
out:
up_write(&tomoyo_globally_readable_list_lock);
@@ -256,21 +258,22 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
* @filename: The filename to check.
*
* Returns true if any domain can open @filename for reading, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
filename)
{
struct tomoyo_globally_readable_file_entry *ptr;
bool found = false;
- down_read(&tomoyo_globally_readable_list_lock);
- list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
if (!ptr->is_deleted &&
tomoyo_path_matches_pattern(filename, ptr->filename)) {
found = true;
break;
}
}
- up_read(&tomoyo_globally_readable_list_lock);
return found;
}
@@ -281,6 +284,8 @@ static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
{
@@ -293,13 +298,14 @@ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
{
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) {
struct tomoyo_globally_readable_file_entry *ptr;
@@ -313,7 +319,6 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_globally_readable_list_lock);
return done;
}
@@ -356,6 +361,8 @@ static DECLARE_RWSEM(tomoyo_pattern_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_file_pattern_entry(const char *pattern,
const bool is_delete)
@@ -371,7 +378,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
if (!saved_pattern)
return -ENOMEM;
down_write(&tomoyo_pattern_list_lock);
- list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (saved_pattern != ptr->pattern)
continue;
ptr->is_deleted = is_delete;
@@ -386,7 +393,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_pattern_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_pattern_list);
error = 0;
out:
up_write(&tomoyo_pattern_list_lock);
@@ -399,6 +406,8 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
* @filename: The filename to find patterned pathname.
*
* Returns pointer to pathname pattern if matched, @filename otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static const struct tomoyo_path_info *
tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
@@ -406,8 +415,7 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
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))
@@ -420,7 +428,6 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
break;
}
}
- up_read(&tomoyo_pattern_list_lock);
if (pattern)
filename = pattern;
return filename;
@@ -433,6 +440,8 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_pattern_policy(char *data, const bool is_delete)
{
@@ -445,13 +454,14 @@ int tomoyo_write_pattern_policy(char *data, const bool is_delete)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_pattern_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
struct tomoyo_pattern_entry *ptr;
ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
@@ -462,7 +472,6 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_pattern_list_lock);
return done;
}
@@ -505,6 +514,8 @@ static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_no_rewrite_entry(const char *pattern,
const bool is_delete)
@@ -519,7 +530,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
if (!saved_pattern)
return -ENOMEM;
down_write(&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->pattern != saved_pattern)
continue;
ptr->is_deleted = is_delete;
@@ -534,7 +545,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_no_rewrite_list);
error = 0;
out:
up_write(&tomoyo_no_rewrite_list_lock);
@@ -548,14 +559,15 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
*
* Returns true if @filename is specified by "deny_rewrite" directive,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
{
struct tomoyo_no_rewrite_entry *ptr;
bool found = false;
- down_read(&tomoyo_no_rewrite_list_lock);
- list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->is_deleted)
continue;
if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -563,7 +575,6 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
found = true;
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return found;
}
@@ -574,6 +585,8 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
{
@@ -586,13 +599,14 @@ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
{
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) {
struct tomoyo_no_rewrite_entry *ptr;
ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
@@ -603,7 +617,6 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return done;
}
@@ -621,6 +634,8 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
* Current policy syntax uses "allow_read/write" instead of "6",
* "allow_read" instead of "4", "allow_write" instead of "2",
* "allow_execute" instead of "1".
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_file_acl(const char *filename, u8 perm,
struct tomoyo_domain_info * const domain,
@@ -658,6 +673,8 @@ static int tomoyo_update_file_acl(const char *filename, u8 perm,
* @may_use_pattern: True if patterned ACL is permitted.
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
domain,
@@ -669,8 +686,7 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
struct tomoyo_acl_info *ptr;
int error = -EPERM;
- 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;
@@ -693,7 +709,6 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
@@ -705,6 +720,8 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
* @operation: Mode ("read" or "write" or "read/write" or "execute").
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
const struct tomoyo_path_info *filename,
@@ -738,6 +755,8 @@ static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
* @mode: Access control mode.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
const struct tomoyo_path_info *filename,
@@ -791,6 +810,8 @@ static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
const bool is_delete)
@@ -838,6 +859,8 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
struct tomoyo_domain_info *
@@ -861,7 +884,7 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
down_write(&tomoyo_domain_acl_info_list_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) {
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_single_path_acl_record,
@@ -894,12 +917,12 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
acl->perm |= rw_mask;
acl->filename = saved_filename;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_single_path_acl_record,
@@ -934,6 +957,8 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
const char *filename2,
@@ -959,7 +984,7 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
down_write(&tomoyo_domain_acl_info_list_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) {
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_double_path_acl_record,
@@ -982,12 +1007,12 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
acl->perm = perm;
acl->filename1 = saved_filename1;
acl->filename2 = saved_filename2;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_double_path_acl_record,
@@ -1014,6 +1039,8 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
* @filename: Filename to check.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
const u8 type,
@@ -1033,6 +1060,8 @@ static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
* @filename2: Second filename to check.
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
const u8 type,
@@ -1047,8 +1076,7 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
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) {
+ 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;
@@ -1063,7 +1091,6 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
@@ -1076,6 +1103,8 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
* @mode: Access control mode.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
const domain, u8 operation,
@@ -1124,6 +1153,8 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
* @filename: Check permission for "execute".
*
* Returns 0 on success, negativevalue otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
const struct tomoyo_path_info *filename)
@@ -1152,6 +1183,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
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;
@@ -1163,6 +1195,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
* don't call me.
*/
return 0;
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(path);
if (!buf)
goto out;
@@ -1188,6 +1221,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
buf, mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
@@ -1209,9 +1243,11 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
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;
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(path);
if (!buf)
goto out;
@@ -1231,6 +1267,7 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
@@ -1251,9 +1288,12 @@ int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
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;
+
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(&filp->f_path);
if (!buf)
goto out;
@@ -1266,6 +1306,7 @@ int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
buf, mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
@@ -1290,9 +1331,11 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
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;
+ idx = tomoyo_read_lock();
buf1 = tomoyo_get_path(path1);
buf2 = tomoyo_get_path(path2);
if (!buf1 || !buf2)
@@ -1331,6 +1374,7 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
out:
tomoyo_free(buf1);
tomoyo_free(buf2);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
index e3c7aa0..62363b3 100644
--- a/security/tomoyo/realpath.c
+++ b/security/tomoyo/realpath.c
@@ -402,11 +402,13 @@ void __init tomoyo_realpath_init(void)
INIT_LIST_HEAD(&tomoyo_name_list[i]);
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
- list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
- down_read(&tomoyo_domain_list_lock);
+ /*
+ * tomoyo_read_lock() is not needed because this function is
+ * called before the first "delete" request.
+ */
+ list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
panic("Can't register tomoyo_kernel_domain");
- up_read(&tomoyo_domain_list_lock);
}
/* Memory allocated for temporary purpose. */
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index ad9555f..714daa3 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -76,8 +76,18 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
* Execute permission is checked against pathname passed to do_execve()
* using current domain.
*/
- if (!domain)
- return tomoyo_find_next_domain(bprm);
+ if (!domain) {
+ /*
+ * We will need to protect whole execve() operation when GC
+ * starts kfree()ing "struct tomoyo_domain_info" because
+ * bprm->cred->security points to "struct tomoyo_domain_info"
+ * but "struct tomoyo_domain_info" does not have a refcounter.
+ */
+ const int idx = tomoyo_read_lock();
+ const int err = tomoyo_find_next_domain(bprm);
+ tomoyo_read_unlock(idx);
+ return err;
+ }
/*
* Read permission is checked against interpreters using next domain.
* '1' is the result of open_to_namei_flags(O_RDONLY).
@@ -278,6 +288,9 @@ static struct security_operations tomoyo_security_ops = {
.sb_pivotroot = tomoyo_sb_pivotroot,
};
+/* Lock for GC. */
+struct srcu_struct tomoyo_ss;
+
static int __init tomoyo_init(void)
{
struct cred *cred = (struct cred *) current_cred();
@@ -285,7 +298,8 @@ static int __init tomoyo_init(void)
if (!security_module_enable(&tomoyo_security_ops))
return 0;
/* register ourselves with the security framework */
- if (register_security(&tomoyo_security_ops))
+ if (register_security(&tomoyo_security_ops) ||
+ init_srcu_struct(&tomoyo_ss))
panic("Failure registering TOMOYO Linux");
printk(KERN_INFO "TOMOYO Linux initialized\n");
cred->security = &tomoyo_kernel_domain;
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH] TOMOYO: Use RCU primitives for list operation
2009-12-11 13:53 [PATCH] TOMOYO: Use RCU primitives for list operation Tetsuo Handa
@ 2009-12-14 16:39 ` Serge E. Hallyn
2009-12-15 1:39 ` Tetsuo Handa
0 siblings, 1 reply; 6+ messages in thread
From: Serge E. Hallyn @ 2009-12-14 16:39 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel
Quoting Tetsuo Handa (penguin-kernel@I-love.SAKURA.ne.jp):
> Can somebody please review?
Thanks, Tetsuo!
Just out of curiosity (and bc I'm personally much more familiar with plain old
rcu), I assume you have a list of places where sleeping under rcu is necessary
or greatly simplifies/cleans up the code?
Acked-by: Serge Hallyn <serue@us.ibm.com>
-serge
> ----------
> [PATCH] TOMOYO: Use RCU primitives for list operation
>
> Currently, TOMOYO is using down_read() only for protecting ->prev member from
> writers. If we convert list operation using RCU primitives so that readers don't
> look at ->prev member, we can replace down_read() by srcu_read_lock().
>
> tomoyo_read_lock()/tomoyo_read_unlock() (these are wrappers for
> srcu_read_lock()/srcu_read_unlock()) protects the data against the garbage
> collector (which will be added in the future). I call tomoyo_read_lock() when
> /sys/kernel/security/tomoyo/ interface is open()ed and call
> tomoyo_read_unlock() when /sys/kernel/security/tomoyo/ interface is close()d
> rather than calling tomoyo_read_lock()/tomoyo_read_unlock() upon individual
> read()/write() requests. In this way, the pointers saved in
> "struct tomoyo_io_buffer" are guaranteed to be valid. Please ignore
>
> warning: context imbalance in 'tomoyo_open_control' - wrong count at exit
> warning: context imbalance in 'tomoyo_close_control' - unexpected unlock
>
> messages when built with "C=1" option.
>
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
> common.c | 90 +++++++++++++++++++++++++++----------------------
> common.h | 28 +++++++++++----
> domain.c | 63 ++++++++++++++++++++--------------
> file.c | 110 ++++++++++++++++++++++++++++++++++++++++++-------------------
> realpath.c | 8 ++--
> tomoyo.c | 20 +++++++++--
> 6 files changed, 207 insertions(+), 112 deletions(-)
>
> diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
> index 6c60616..f01b936 100644
> --- a/security/tomoyo/common.c
> +++ b/security/tomoyo/common.c
> @@ -365,10 +365,9 @@ bool tomoyo_is_domain_def(const unsigned char *buffer)
> *
> * @domainname: The domainname to find.
> *
> - * Caller must call down_read(&tomoyo_domain_list_lock); or
> - * down_write(&tomoyo_domain_list_lock); .
> - *
> * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
> {
> @@ -377,7 +376,7 @@ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
>
> 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;
> @@ -829,6 +828,8 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain)
> * @domain: Pointer to "struct tomoyo_domain_info".
> *
> * Returns true if the domain is not exceeded quota, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
> {
> @@ -837,8 +838,7 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
>
> 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)) {
> @@ -866,7 +866,6 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
> 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) {
> @@ -1096,6 +1095,8 @@ static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_manager_entry(const char *manager,
> const bool is_delete)
> @@ -1118,7 +1119,7 @@ static int tomoyo_update_manager_entry(const char *manager,
> if (!saved_manager)
> return -ENOMEM;
> down_write(&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->manager != saved_manager)
> continue;
> ptr->is_deleted = is_delete;
> @@ -1134,7 +1135,7 @@ static int tomoyo_update_manager_entry(const char *manager,
> goto out;
> new_entry->manager = saved_manager;
> 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);
> error = 0;
> out:
> up_write(&tomoyo_policy_manager_list_lock);
> @@ -1147,6 +1148,8 @@ static int tomoyo_update_manager_entry(const char *manager,
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
> {
> @@ -1166,6 +1169,8 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns 0.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
> {
> @@ -1174,7 +1179,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
>
> 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) {
> struct tomoyo_policy_manager_entry *ptr;
> @@ -1186,7 +1190,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_policy_manager_list_lock);
> head->read_eof = done;
> return 0;
> }
> @@ -1196,6 +1199,8 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
> *
> * Returns true if the current process is permitted to modify policy
> * via /sys/kernel/security/tomoyo/ interface.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static bool tomoyo_is_policy_manager(void)
> {
> @@ -1209,29 +1214,25 @@ static bool tomoyo_is_policy_manager(void)
> 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;
> @@ -1252,6 +1253,8 @@ static bool tomoyo_is_policy_manager(void)
> * @data: String to parse.
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
> const char *data)
> @@ -1267,11 +1270,8 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
> 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;
> @@ -1285,13 +1285,11 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
> 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;
> @@ -1307,6 +1305,8 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
> * @domainname: The name of domain.
> *
> * Returns 0.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_delete_domain(char *domainname)
> {
> @@ -1317,7 +1317,7 @@ static int tomoyo_delete_domain(char *domainname)
> tomoyo_fill_path_info(&name);
> down_write(&tomoyo_domain_list_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;
> @@ -1337,6 +1337,8 @@ static int tomoyo_delete_domain(char *domainname)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
> {
> @@ -1359,11 +1361,9 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
> 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;
> @@ -1508,6 +1508,8 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns 0.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
> {
> @@ -1519,7 +1521,6 @@ static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
> 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) {
> struct tomoyo_domain_info *domain;
> const char *quota_exceeded = "";
> @@ -1552,7 +1553,6 @@ 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) {
> struct tomoyo_acl_info *ptr
> @@ -1562,7 +1562,6 @@ acl_loop:
> if (!done)
> break;
> }
> - up_read(&tomoyo_domain_acl_info_list_lock);
> if (!done)
> break;
> head->read_step = 3;
> @@ -1574,7 +1573,6 @@ tail_mark:
> if (head->read_single_domain)
> break;
> }
> - up_read(&tomoyo_domain_list_lock);
> head->read_eof = done;
> return 0;
> }
> @@ -1590,6 +1588,8 @@ tail_mark:
> *
> * ( echo "select " $domainname; echo "use_profile " $profile ) |
> * /usr/lib/ccs/loadpolicy -d
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
> {
> @@ -1601,9 +1601,7 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
> 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
> @@ -1625,6 +1623,8 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
> * awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
> * domainname = $0; } else if ( $1 == "use_profile" ) {
> * print $2 " " domainname; domainname = ""; } } ; '
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
> {
> @@ -1633,7 +1633,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
>
> if (head->read_eof)
> return 0;
> - down_read(&tomoyo_domain_list_lock);
> list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
> struct tomoyo_domain_info *domain;
> domain = list_entry(pos, struct tomoyo_domain_info, list);
> @@ -1644,7 +1643,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_domain_list_lock);
> head->read_eof = done;
> return 0;
> }
> @@ -1701,6 +1699,8 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
> {
> @@ -1735,6 +1735,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns 0 on success, -EINVAL otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
> {
> @@ -1864,15 +1866,13 @@ void tomoyo_load_policy(const char *filename)
> tomoyo_policy_loaded = true;
> { /* Check all profiles currently assigned to domains are defined. */
> 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);
> }
> }
>
> @@ -1920,6 +1920,8 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
> * @file: Pointer to "struct file".
> *
> * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
> + *
> + * Caller acquires tomoyo_read_lock().
> */
> static int tomoyo_open_control(const u8 type, struct file *file)
> {
> @@ -2005,6 +2007,7 @@ static int tomoyo_open_control(const u8 type, struct file *file)
> return -ENOMEM;
> }
> }
> + head->reader_idx = tomoyo_read_lock();
> file->private_data = head;
> /*
> * Call the handler now if the file is
> @@ -2026,6 +2029,8 @@ static int tomoyo_open_control(const u8 type, struct file *file)
> * @buffer_len: Size of @buffer.
> *
> * Returns bytes read on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_read_control(struct file *file, char __user *buffer,
> const int buffer_len)
> @@ -2069,6 +2074,8 @@ static int tomoyo_read_control(struct file *file, char __user *buffer,
> * @buffer_len: Size of @buffer.
> *
> * Returns @buffer_len on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_write_control(struct file *file, const char __user *buffer,
> const int buffer_len)
> @@ -2119,11 +2126,14 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer,
> * @file: Pointer to "struct file".
> *
> * Releases memory and returns 0.
> + *
> + * Caller looses tomoyo_read_lock().
> */
> static int tomoyo_close_control(struct file *file)
> {
> struct tomoyo_io_buffer *head = file->private_data;
>
> + tomoyo_read_unlock(head->reader_idx);
> /* Release memory used for policy I/O. */
> tomoyo_free(head->read_buf);
> head->read_buf = NULL;
> diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
> index bd10f9f..c6f1392 100644
> --- a/security/tomoyo/common.h
> +++ b/security/tomoyo/common.h
> @@ -269,6 +269,8 @@ struct tomoyo_io_buffer {
> int (*write) (struct tomoyo_io_buffer *);
> /* Exclusive lock for this structure. */
> struct mutex io_sem;
> + /* Index returned by tomoyo_read_lock(). */
> + int reader_idx;
> /* The position currently reading from. */
> struct list_head *read_var1;
> /* Extra variables for reading. */
> @@ -446,16 +448,28 @@ extern struct tomoyo_domain_info tomoyo_kernel_domain;
> * @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) \
> - for (({ if (!cookie) \
> - cookie = head; }), \
> - pos = (cookie)->next; \
> - prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
> - (cookie) = pos, pos = pos->next)
> +#define list_for_each_cookie(pos, cookie, head) \
> + for (({ if (!cookie) \
> + cookie = head; }), \
> + pos = rcu_dereference((cookie)->next); \
> + prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
> + (cookie) = pos, pos = rcu_dereference(pos->next))
> +
> +extern struct srcu_struct tomoyo_ss;
> +
> +static inline int tomoyo_read_lock(void)
> +{
> + return srcu_read_lock(&tomoyo_ss);
> +}
> +
> +static inline void tomoyo_read_unlock(int idx)
> +{
> + srcu_read_unlock(&tomoyo_ss, idx);
> +}
>
> #endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
> diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
> index fcf52ac..2fd1901 100644
> --- a/security/tomoyo/domain.c
> +++ b/security/tomoyo/domain.c
> @@ -217,6 +217,8 @@ static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_domain_initializer_entry(const char *domainname,
> const char *program,
> @@ -246,7 +248,7 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
> if (!saved_program)
> return -ENOMEM;
> down_write(&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_not != is_not ||
> ptr->domainname != saved_domainname ||
> ptr->program != saved_program)
> @@ -266,7 +268,7 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
> new_entry->program = saved_program;
> 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);
> error = 0;
> out:
> up_write(&tomoyo_domain_initializer_list_lock);
> @@ -279,13 +281,14 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
> {
> 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) {
> const char *no;
> @@ -308,7 +311,6 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_domain_initializer_list_lock);
> return done;
> }
>
> @@ -320,6 +322,8 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
> const bool is_delete)
> @@ -345,6 +349,8 @@ int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
> *
> * Returns true if executing @program reinitializes domain transition,
> * false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
> domainname,
> @@ -355,8 +361,7 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
> 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) {
> @@ -376,7 +381,6 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
> }
> flag = true;
> }
> - up_read(&tomoyo_domain_initializer_list_lock);
> return flag;
> }
>
> @@ -430,6 +434,8 @@ static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_domain_keeper_entry(const char *domainname,
> const char *program,
> @@ -459,7 +465,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
> if (!saved_domainname)
> return -ENOMEM;
> down_write(&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_not != is_not ||
> ptr->domainname != saved_domainname ||
> ptr->program != saved_program)
> @@ -479,7 +485,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
> new_entry->program = saved_program;
> 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);
> error = 0;
> out:
> up_write(&tomoyo_domain_keeper_list_lock);
> @@ -493,6 +499,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
> * @is_not: True if it is "no_keep_domain" entry.
> * @is_delete: True if it is a delete request.
> *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
> const bool is_delete)
> @@ -513,13 +520,14 @@ int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
> {
> 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) {
> struct tomoyo_domain_keeper_entry *ptr;
> @@ -542,7 +550,6 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_domain_keeper_list_lock);
> return done;
> }
>
> @@ -555,6 +562,8 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
> *
> * Returns true if executing @program supresses domain transition,
> * false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
> const struct tomoyo_path_info *program,
> @@ -563,8 +572,7 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
> 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) {
> @@ -582,7 +590,6 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
> }
> flag = true;
> }
> - up_read(&tomoyo_domain_keeper_list_lock);
> return flag;
> }
>
> @@ -627,6 +634,8 @@ static DECLARE_RWSEM(tomoyo_alias_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_alias_entry(const char *original_name,
> const char *aliased_name,
> @@ -646,7 +655,7 @@ static int tomoyo_update_alias_entry(const char *original_name,
> if (!saved_original_name || !saved_aliased_name)
> return -ENOMEM;
> down_write(&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->original_name != saved_original_name ||
> ptr->aliased_name != saved_aliased_name)
> continue;
> @@ -663,7 +672,7 @@ static int tomoyo_update_alias_entry(const char *original_name,
> goto out;
> new_entry->original_name = saved_original_name;
> new_entry->aliased_name = saved_aliased_name;
> - list_add_tail(&new_entry->list, &tomoyo_alias_list);
> + list_add_tail_rcu(&new_entry->list, &tomoyo_alias_list);
> error = 0;
> out:
> up_write(&tomoyo_alias_list_lock);
> @@ -676,13 +685,14 @@ static int tomoyo_update_alias_entry(const char *original_name,
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
> {
> struct list_head *pos;
> bool done = true;
>
> - down_read(&tomoyo_alias_list_lock);
> list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
> struct tomoyo_alias_entry *ptr;
>
> @@ -695,7 +705,6 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_alias_list_lock);
> return done;
> }
>
> @@ -706,6 +715,8 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_alias_policy(char *data, const bool is_delete)
> {
> @@ -724,6 +735,8 @@ int tomoyo_write_alias_policy(char *data, const bool is_delete)
> * @profile: Profile number to assign if the domain was newly created.
> *
> * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
> domainname,
> @@ -742,7 +755,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
> if (!saved_domainname)
> goto out;
> /* Can I reuse memory of deleted domain? */
> - list_for_each_entry(domain, &tomoyo_domain_list, list) {
> + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
> struct task_struct *p;
> struct tomoyo_acl_info *ptr;
> bool flag;
> @@ -760,7 +773,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
> read_unlock(&tasklist_lock);
> if (flag)
> continue;
> - list_for_each_entry(ptr, &domain->acl_info_list, list) {
> + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
> ptr->type |= TOMOYO_ACL_DELETED;
> }
> tomoyo_set_domain_flag(domain, true, domain->flags);
> @@ -776,7 +789,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
> INIT_LIST_HEAD(&domain->acl_info_list);
> domain->domainname = saved_domainname;
> 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);
> @@ -789,6 +802,8 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
> * @bprm: Pointer to "struct linux_binprm".
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_find_next_domain(struct linux_binprm *bprm)
> {
> @@ -849,8 +864,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
> 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))
> @@ -861,7 +875,6 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
> tomoyo_fill_path_info(&r);
> break;
> }
> - up_read(&tomoyo_alias_list_lock);
> }
>
> /* Check execute permission. */
> @@ -892,9 +905,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
> }
> 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)
> diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
> index 482f0e7..3c47286 100644
> --- a/security/tomoyo/file.c
> +++ b/security/tomoyo/file.c
> @@ -213,6 +213,8 @@ static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_globally_readable_entry(const char *filename,
> const bool is_delete)
> @@ -228,7 +230,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
> if (!saved_filename)
> return -ENOMEM;
> down_write(&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->filename != saved_filename)
> continue;
> ptr->is_deleted = is_delete;
> @@ -243,7 +245,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
> if (!new_entry)
> goto out;
> new_entry->filename = saved_filename;
> - list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
> + list_add_tail_rcu(&new_entry->list, &tomoyo_globally_readable_list);
> error = 0;
> out:
> up_write(&tomoyo_globally_readable_list_lock);
> @@ -256,21 +258,22 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
> * @filename: The filename to check.
> *
> * Returns true if any domain can open @filename for reading, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
> filename)
> {
> struct tomoyo_globally_readable_file_entry *ptr;
> bool found = false;
> - down_read(&tomoyo_globally_readable_list_lock);
> - list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
> +
> + list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
> if (!ptr->is_deleted &&
> tomoyo_path_matches_pattern(filename, ptr->filename)) {
> found = true;
> break;
> }
> }
> - up_read(&tomoyo_globally_readable_list_lock);
> return found;
> }
>
> @@ -281,6 +284,8 @@ static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
> {
> @@ -293,13 +298,14 @@ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
> {
> 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) {
> struct tomoyo_globally_readable_file_entry *ptr;
> @@ -313,7 +319,6 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_globally_readable_list_lock);
> return done;
> }
>
> @@ -356,6 +361,8 @@ static DECLARE_RWSEM(tomoyo_pattern_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_file_pattern_entry(const char *pattern,
> const bool is_delete)
> @@ -371,7 +378,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
> if (!saved_pattern)
> return -ENOMEM;
> down_write(&tomoyo_pattern_list_lock);
> - list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
> if (saved_pattern != ptr->pattern)
> continue;
> ptr->is_deleted = is_delete;
> @@ -386,7 +393,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
> if (!new_entry)
> goto out;
> new_entry->pattern = saved_pattern;
> - list_add_tail(&new_entry->list, &tomoyo_pattern_list);
> + list_add_tail_rcu(&new_entry->list, &tomoyo_pattern_list);
> error = 0;
> out:
> up_write(&tomoyo_pattern_list_lock);
> @@ -399,6 +406,8 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
> * @filename: The filename to find patterned pathname.
> *
> * Returns pointer to pathname pattern if matched, @filename otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static const struct tomoyo_path_info *
> tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
> @@ -406,8 +415,7 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
> 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))
> @@ -420,7 +428,6 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
> break;
> }
> }
> - up_read(&tomoyo_pattern_list_lock);
> if (pattern)
> filename = pattern;
> return filename;
> @@ -433,6 +440,8 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_pattern_policy(char *data, const bool is_delete)
> {
> @@ -445,13 +454,14 @@ int tomoyo_write_pattern_policy(char *data, const bool is_delete)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
> {
> struct list_head *pos;
> bool done = true;
>
> - down_read(&tomoyo_pattern_list_lock);
> list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
> struct tomoyo_pattern_entry *ptr;
> ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
> @@ -462,7 +472,6 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_pattern_list_lock);
> return done;
> }
>
> @@ -505,6 +514,8 @@ static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_no_rewrite_entry(const char *pattern,
> const bool is_delete)
> @@ -519,7 +530,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
> if (!saved_pattern)
> return -ENOMEM;
> down_write(&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->pattern != saved_pattern)
> continue;
> ptr->is_deleted = is_delete;
> @@ -534,7 +545,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
> if (!new_entry)
> goto out;
> new_entry->pattern = saved_pattern;
> - list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
> + list_add_tail_rcu(&new_entry->list, &tomoyo_no_rewrite_list);
> error = 0;
> out:
> up_write(&tomoyo_no_rewrite_list_lock);
> @@ -548,14 +559,15 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
> *
> * Returns true if @filename is specified by "deny_rewrite" directive,
> * false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
> {
> struct tomoyo_no_rewrite_entry *ptr;
> bool found = false;
>
> - down_read(&tomoyo_no_rewrite_list_lock);
> - list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
> + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
> if (ptr->is_deleted)
> continue;
> if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
> @@ -563,7 +575,6 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
> found = true;
> break;
> }
> - up_read(&tomoyo_no_rewrite_list_lock);
> return found;
> }
>
> @@ -574,6 +585,8 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
> {
> @@ -586,13 +599,14 @@ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
> * @head: Pointer to "struct tomoyo_io_buffer".
> *
> * Returns true on success, false otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
> {
> 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) {
> struct tomoyo_no_rewrite_entry *ptr;
> ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
> @@ -603,7 +617,6 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
> if (!done)
> break;
> }
> - up_read(&tomoyo_no_rewrite_list_lock);
> return done;
> }
>
> @@ -621,6 +634,8 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
> * Current policy syntax uses "allow_read/write" instead of "6",
> * "allow_read" instead of "4", "allow_write" instead of "2",
> * "allow_execute" instead of "1".
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_file_acl(const char *filename, u8 perm,
> struct tomoyo_domain_info * const domain,
> @@ -658,6 +673,8 @@ static int tomoyo_update_file_acl(const char *filename, u8 perm,
> * @may_use_pattern: True if patterned ACL is permitted.
> *
> * Returns 0 on success, -EPERM otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
> domain,
> @@ -669,8 +686,7 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
> struct tomoyo_acl_info *ptr;
> int error = -EPERM;
>
> - 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;
> @@ -693,7 +709,6 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
> error = 0;
> break;
> }
> - up_read(&tomoyo_domain_acl_info_list_lock);
> return error;
> }
>
> @@ -705,6 +720,8 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
> * @operation: Mode ("read" or "write" or "read/write" or "execute").
> *
> * Returns 0 on success, -EPERM otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
> const struct tomoyo_path_info *filename,
> @@ -738,6 +755,8 @@ static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
> * @mode: Access control mode.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
> const struct tomoyo_path_info *filename,
> @@ -791,6 +810,8 @@ static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
> const bool is_delete)
> @@ -838,6 +859,8 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
> struct tomoyo_domain_info *
> @@ -861,7 +884,7 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
> down_write(&tomoyo_domain_acl_info_list_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) {
> if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
> continue;
> acl = container_of(ptr, struct tomoyo_single_path_acl_record,
> @@ -894,12 +917,12 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
> if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
> acl->perm |= rw_mask;
> acl->filename = saved_filename;
> - list_add_tail(&acl->head.list, &domain->acl_info_list);
> + list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
> error = 0;
> goto out;
> delete:
> error = -ENOENT;
> - list_for_each_entry(ptr, &domain->acl_info_list, list) {
> + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
> if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
> continue;
> acl = container_of(ptr, struct tomoyo_single_path_acl_record,
> @@ -934,6 +957,8 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
> * @is_delete: True if it is a delete request.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
> const char *filename2,
> @@ -959,7 +984,7 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
> down_write(&tomoyo_domain_acl_info_list_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) {
> if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
> continue;
> acl = container_of(ptr, struct tomoyo_double_path_acl_record,
> @@ -982,12 +1007,12 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
> acl->perm = perm;
> acl->filename1 = saved_filename1;
> acl->filename2 = saved_filename2;
> - list_add_tail(&acl->head.list, &domain->acl_info_list);
> + list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
> error = 0;
> goto out;
> delete:
> error = -ENOENT;
> - list_for_each_entry(ptr, &domain->acl_info_list, list) {
> + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
> if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
> continue;
> acl = container_of(ptr, struct tomoyo_double_path_acl_record,
> @@ -1014,6 +1039,8 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
> * @filename: Filename to check.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
> const u8 type,
> @@ -1033,6 +1060,8 @@ static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
> * @filename2: Second filename to check.
> *
> * Returns 0 on success, -EPERM otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
> const u8 type,
> @@ -1047,8 +1076,7 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
>
> 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) {
> + 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;
> @@ -1063,7 +1091,6 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
> error = 0;
> break;
> }
> - up_read(&tomoyo_domain_acl_info_list_lock);
> return error;
> }
>
> @@ -1076,6 +1103,8 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
> * @mode: Access control mode.
> *
> * Returns 0 on success, negative value otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
> const domain, u8 operation,
> @@ -1124,6 +1153,8 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
> * @filename: Check permission for "execute".
> *
> * Returns 0 on success, negativevalue otherwise.
> + *
> + * Caller holds tomoyo_read_lock().
> */
> int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
> const struct tomoyo_path_info *filename)
> @@ -1152,6 +1183,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
> 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;
> @@ -1163,6 +1195,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
> * don't call me.
> */
> return 0;
> + idx = tomoyo_read_lock();
> buf = tomoyo_get_path(path);
> if (!buf)
> goto out;
> @@ -1188,6 +1221,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
> buf, mode);
> out:
> tomoyo_free(buf);
> + tomoyo_read_unlock(idx);
> if (!is_enforce)
> error = 0;
> return error;
> @@ -1209,9 +1243,11 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
> 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;
> + idx = tomoyo_read_lock();
> buf = tomoyo_get_path(path);
> if (!buf)
> goto out;
> @@ -1231,6 +1267,7 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
> mode);
> out:
> tomoyo_free(buf);
> + tomoyo_read_unlock(idx);
> if (!is_enforce)
> error = 0;
> return error;
> @@ -1251,9 +1288,12 @@ int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
> 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;
> +
> + idx = tomoyo_read_lock();
> buf = tomoyo_get_path(&filp->f_path);
> if (!buf)
> goto out;
> @@ -1266,6 +1306,7 @@ int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
> buf, mode);
> out:
> tomoyo_free(buf);
> + tomoyo_read_unlock(idx);
> if (!is_enforce)
> error = 0;
> return error;
> @@ -1290,9 +1331,11 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
> 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;
> + idx = tomoyo_read_lock();
> buf1 = tomoyo_get_path(path1);
> buf2 = tomoyo_get_path(path2);
> if (!buf1 || !buf2)
> @@ -1331,6 +1374,7 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
> out:
> tomoyo_free(buf1);
> tomoyo_free(buf2);
> + tomoyo_read_unlock(idx);
> if (!is_enforce)
> error = 0;
> return error;
> diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
> index e3c7aa0..62363b3 100644
> --- a/security/tomoyo/realpath.c
> +++ b/security/tomoyo/realpath.c
> @@ -402,11 +402,13 @@ void __init tomoyo_realpath_init(void)
> INIT_LIST_HEAD(&tomoyo_name_list[i]);
> INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
> tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
> - list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
> - down_read(&tomoyo_domain_list_lock);
> + /*
> + * tomoyo_read_lock() is not needed because this function is
> + * called before the first "delete" request.
> + */
> + list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
> if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
> panic("Can't register tomoyo_kernel_domain");
> - up_read(&tomoyo_domain_list_lock);
> }
>
> /* Memory allocated for temporary purpose. */
> diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
> index ad9555f..714daa3 100644
> --- a/security/tomoyo/tomoyo.c
> +++ b/security/tomoyo/tomoyo.c
> @@ -76,8 +76,18 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
> * Execute permission is checked against pathname passed to do_execve()
> * using current domain.
> */
> - if (!domain)
> - return tomoyo_find_next_domain(bprm);
> + if (!domain) {
> + /*
> + * We will need to protect whole execve() operation when GC
> + * starts kfree()ing "struct tomoyo_domain_info" because
> + * bprm->cred->security points to "struct tomoyo_domain_info"
> + * but "struct tomoyo_domain_info" does not have a refcounter.
> + */
> + const int idx = tomoyo_read_lock();
> + const int err = tomoyo_find_next_domain(bprm);
> + tomoyo_read_unlock(idx);
> + return err;
> + }
> /*
> * Read permission is checked against interpreters using next domain.
> * '1' is the result of open_to_namei_flags(O_RDONLY).
> @@ -278,6 +288,9 @@ static struct security_operations tomoyo_security_ops = {
> .sb_pivotroot = tomoyo_sb_pivotroot,
> };
>
> +/* Lock for GC. */
> +struct srcu_struct tomoyo_ss;
> +
> static int __init tomoyo_init(void)
> {
> struct cred *cred = (struct cred *) current_cred();
> @@ -285,7 +298,8 @@ static int __init tomoyo_init(void)
> if (!security_module_enable(&tomoyo_security_ops))
> return 0;
> /* register ourselves with the security framework */
> - if (register_security(&tomoyo_security_ops))
> + if (register_security(&tomoyo_security_ops) ||
> + init_srcu_struct(&tomoyo_ss))
> panic("Failure registering TOMOYO Linux");
> printk(KERN_INFO "TOMOYO Linux initialized\n");
> cred->security = &tomoyo_kernel_domain;
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] TOMOYO: Use RCU primitives for list operation
2009-12-14 16:39 ` Serge E. Hallyn
@ 2009-12-15 1:39 ` Tetsuo Handa
0 siblings, 0 replies; 6+ messages in thread
From: Tetsuo Handa @ 2009-12-15 1:39 UTC (permalink / raw)
To: serue; +Cc: linux-security-module, linux-kernel
Hello.
Serge E. Hallyn wrote:
> Just out of curiosity (and bc I'm personally much more familiar with plain old
> rcu), I assume you have a list of places where sleeping under rcu is necessary
> or greatly simplifies/cleans up the code?
Yes.
TOMOYO checks operations which are permitted to sleep so that we can load
policy like call_usermodehelper("/sbin/modprobe"). I tried to use non-sleeping
RCU while implementing garbage collector support. But that attempt resulted in
messing up the code with full of atomic_inc()/atomic_dec() calls.
Thus, I decided to use sleeping RCU.
I tested garbage collector support using sleeping RCU with non-LSM version of
TOMOYO. It is working well. Thus, I started breaking non-LSM version of TOMOYO
into smaller pieces and proposing for LSM version of TOMOYO.
Thank you for reviewing. I'll send next patches.
^ permalink raw reply [flat|nested] 6+ messages in thread
* [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown().
@ 2009-10-04 12:49 Tetsuo Handa
2009-10-08 17:10 ` John Johansen
0 siblings, 1 reply; 6+ messages in thread
From: Tetsuo Handa @ 2009-10-04 12:49 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel, Tetsuo Handa
[-- Attachment #1: lsm-add-chmod-chown-hooks.patch --]
[-- Type: text/plain, Size: 7056 bytes --]
This patch allows pathname based LSM modules to check chmod()/chown()
operations. Since notify_change() does not receive "struct vfsmount *",
we add security_path_chmod() and security_path_chown() to the caller of
notify_change().
These hooks are used by TOMOYO.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
fs/open.c | 24 ++++++++++++++++++++----
include/linux/security.h | 30 ++++++++++++++++++++++++++++++
security/capability.c | 13 +++++++++++++
security/security.c | 15 +++++++++++++++
4 files changed, 78 insertions(+), 4 deletions(-)
--- security-testing-2.6.orig/fs/open.c
+++ security-testing-2.6/fs/open.c
@@ -616,6 +616,9 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
err = mnt_want_write_file(file);
if (err)
goto out_putf;
+ err = security_path_chmod(dentry, file->f_vfsmnt, mode);
+ if (err)
+ goto out_drop_write;
mutex_lock(&inode->i_mutex);
if (mode == (mode_t) -1)
mode = inode->i_mode;
@@ -623,6 +626,7 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
err = notify_change(dentry, &newattrs);
mutex_unlock(&inode->i_mutex);
+out_drop_write:
mnt_drop_write(file->f_path.mnt);
out_putf:
fput(file);
@@ -645,6 +649,9 @@ SYSCALL_DEFINE3(fchmodat, int, dfd, cons
error = mnt_want_write(path.mnt);
if (error)
goto dput_and_out;
+ error = security_path_chmod(path.dentry, path.mnt, mode);
+ if (error)
+ goto out_drop_write;
mutex_lock(&inode->i_mutex);
if (mode == (mode_t) -1)
mode = inode->i_mode;
@@ -652,6 +659,7 @@ SYSCALL_DEFINE3(fchmodat, int, dfd, cons
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
error = notify_change(path.dentry, &newattrs);
mutex_unlock(&inode->i_mutex);
+out_drop_write:
mnt_drop_write(path.mnt);
dput_and_out:
path_put(&path);
@@ -700,7 +708,9 @@ SYSCALL_DEFINE3(chown, const char __user
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = chown_common(path.dentry, user, group);
+ error = security_path_chown(&path, user, group);
+ if (!error)
+ error = chown_common(path.dentry, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -725,7 +735,9 @@ SYSCALL_DEFINE5(fchownat, int, dfd, cons
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = chown_common(path.dentry, user, group);
+ error = security_path_chown(&path, user, group);
+ if (!error)
+ error = chown_common(path.dentry, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -744,7 +756,9 @@ SYSCALL_DEFINE3(lchown, const char __use
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = chown_common(path.dentry, user, group);
+ error = security_path_chown(&path, user, group);
+ if (!error)
+ error = chown_common(path.dentry, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -767,7 +781,9 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd
goto out_fput;
dentry = file->f_path.dentry;
audit_inode(NULL, dentry);
- error = chown_common(dentry, user, group);
+ error = security_path_chown(&file->f_path, user, group);
+ if (!error)
+ error = chown_common(dentry, user, group);
mnt_drop_write(file->f_path.mnt);
out_fput:
fput(file);
--- security-testing-2.6.orig/include/linux/security.h
+++ security-testing-2.6/include/linux/security.h
@@ -447,6 +447,18 @@ static inline void security_free_mnt_opt
* @new_dir contains the path structure for parent of the new link.
* @new_dentry contains the dentry structure of the new link.
* Return 0 if permission is granted.
+ * @path_chmod:
+ * Check for permission to change DAC's permission of a file or directory.
+ * @dentry contains the dentry structure.
+ * @mnt contains the vfsmnt structure.
+ * @mode contains DAC's mode.
+ * Return 0 if permission is granted.
+ * @path_chown:
+ * Check for permission to change owner/group of a file or directory.
+ * @path contains the path structure.
+ * @uid contains new owner's ID.
+ * @gid contains new group's ID.
+ * Return 0 if permission is granted.
* @inode_readlink:
* Check the permission to read the symbolic link.
* @dentry contains the dentry structure for the file link.
@@ -1488,6 +1500,9 @@ struct security_operations {
struct dentry *new_dentry);
int (*path_rename) (struct path *old_dir, struct dentry *old_dentry,
struct path *new_dir, struct dentry *new_dentry);
+ int (*path_chmod) (struct dentry *dentry, struct vfsmount *mnt,
+ mode_t mode);
+ int (*path_chown) (struct path *path, uid_t uid, gid_t gid);
#endif
int (*inode_alloc_security) (struct inode *inode);
@@ -2952,6 +2967,9 @@ int security_path_link(struct dentry *ol
struct dentry *new_dentry);
int security_path_rename(struct path *old_dir, struct dentry *old_dentry,
struct path *new_dir, struct dentry *new_dentry);
+int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
+ mode_t mode);
+int security_path_chown(struct path *path, uid_t uid, gid_t gid);
#else /* CONFIG_SECURITY_PATH */
static inline int security_path_unlink(struct path *dir, struct dentry *dentry)
{
@@ -3001,6 +3019,18 @@ static inline int security_path_rename(s
{
return 0;
}
+
+static inline int security_path_chmod(struct dentry *dentry,
+ struct vfsmount *mnt,
+ mode_t mode)
+{
+ return 0;
+}
+
+static inline int security_path_chown(struct path *path, uid_t uid, gid_t gid)
+{
+ return 0;
+}
#endif /* CONFIG_SECURITY_PATH */
#ifdef CONFIG_KEYS
--- security-testing-2.6.orig/security/capability.c
+++ security-testing-2.6/security/capability.c
@@ -308,6 +308,17 @@ static int cap_path_truncate(struct path
{
return 0;
}
+
+static int cap_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
+ mode_t mode)
+{
+ return 0;
+}
+
+static int cap_path_chown(struct path *path, uid_t uid, gid_t gid)
+{
+ return 0;
+}
#endif
static int cap_file_permission(struct file *file, int mask)
@@ -977,6 +988,8 @@ void security_fixup_ops(struct security_
set_to_cap_if_null(ops, path_link);
set_to_cap_if_null(ops, path_rename);
set_to_cap_if_null(ops, path_truncate);
+ set_to_cap_if_null(ops, path_chmod);
+ set_to_cap_if_null(ops, path_chown);
#endif
set_to_cap_if_null(ops, file_permission);
set_to_cap_if_null(ops, file_alloc_security);
--- security-testing-2.6.orig/security/security.c
+++ security-testing-2.6/security/security.c
@@ -434,6 +434,21 @@ int security_path_truncate(struct path *
return 0;
return security_ops->path_truncate(path, length, time_attrs);
}
+
+int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
+ mode_t mode)
+{
+ if (unlikely(IS_PRIVATE(dentry->d_inode)))
+ return 0;
+ return security_ops->path_chmod(dentry, mnt, mode);
+}
+
+int security_path_chown(struct path *path, uid_t uid, gid_t gid)
+{
+ if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+ return 0;
+ return security_ops->path_chown(path, uid, gid);
+}
#endif
int security_inode_create(struct inode *dir, struct dentry *dentry, int mode)
--
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown().
2009-10-04 12:49 [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown() Tetsuo Handa
@ 2009-10-08 17:10 ` John Johansen
2009-10-12 1:04 ` James Morris
0 siblings, 1 reply; 6+ messages in thread
From: John Johansen @ 2009-10-08 17:10 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel
Tetsuo Handa wrote:
> This patch allows pathname based LSM modules to check chmod()/chown()
> operations. Since notify_change() does not receive "struct vfsmount *",
> we add security_path_chmod() and security_path_chown() to the caller of
> notify_change().
>
> These hooks are used by TOMOYO.
This hooks would be useful for AppArmor as well.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown().
2009-10-08 17:10 ` John Johansen
@ 2009-10-12 1:04 ` James Morris
2009-10-13 11:34 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() andsecurity_path_chown() Tetsuo Handa
0 siblings, 1 reply; 6+ messages in thread
From: James Morris @ 2009-10-12 1:04 UTC (permalink / raw)
To: John Johansen; +Cc: Tetsuo Handa, linux-security-module, linux-kernel
On Thu, 8 Oct 2009, John Johansen wrote:
> Tetsuo Handa wrote:
> > This patch allows pathname based LSM modules to check chmod()/chown()
> > operations. Since notify_change() does not receive "struct vfsmount *",
> > we add security_path_chmod() and security_path_chown() to the caller of
> > notify_change().
> >
> > These hooks are used by TOMOYO.
>
> This hooks would be useful for AppArmor as well.
I've applied the first three patches to
git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6#next
It would be good to see some more evidence of review for the remaining
patches.
--
James Morris
<jmorris@namei.org>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [TOMOYO #16 01/25] LSM: Add security_path_chmod() andsecurity_path_chown().
2009-10-12 1:04 ` James Morris
@ 2009-10-13 11:34 ` Tetsuo Handa
2009-10-13 11:37 ` [PATCH] TOMOYO: Add recursive directory matching operator support Tetsuo Handa
0 siblings, 1 reply; 6+ messages in thread
From: Tetsuo Handa @ 2009-10-13 11:34 UTC (permalink / raw)
To: jmorris; +Cc: linux-security-module, linux-kernel
James Morris wrote:
> On Thu, 8 Oct 2009, John Johansen wrote:
>
> > Tetsuo Handa wrote:
> > > This patch allows pathname based LSM modules to check chmod()/chown()
> > > operations. Since notify_change() does not receive "struct vfsmount *",
> > > we add security_path_chmod() and security_path_chown() to the caller of
> > > notify_change().
> > >
> > > These hooks are used by TOMOYO.
> >
> > This hooks would be useful for AppArmor as well.
>
>
> I've applied the first three patches to
>
> git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6#next
>
Thank you.
> It would be good to see some more evidence of review for the remaining
> patches.
Yes. I'm ready to submit first three patches.
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH] TOMOYO: Add recursive directory matching operator support.
2009-10-13 11:34 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() andsecurity_path_chown() Tetsuo Handa
@ 2009-10-13 11:37 ` Tetsuo Handa
2009-10-13 11:39 ` [PATCH] TOMOYO: Use RCU primitives for list operation Tetsuo Handa
0 siblings, 1 reply; 6+ messages in thread
From: Tetsuo Handa @ 2009-10-13 11:37 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel
[PATCH] TOMOYO: Add recursive directory matching operator support.
This patch introduces new operator /\{dir\}/ which matches
'/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ /dir/dir/dir/ ).
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/common.c | 200 ++++++++++++++++++++++++++++-------------------
security/tomoyo/common.h | 4
2 files changed, 121 insertions(+), 83 deletions(-)
--- security-testing-2.6.orig/security/tomoyo/common.c
+++ security-testing-2.6/security/tomoyo/common.c
@@ -187,6 +187,8 @@ bool tomoyo_is_correct_path(const char *
const s8 pattern_type, const s8 end_type,
const char *function)
{
+ const char *const start = filename;
+ bool in_repetition = false;
bool contains_pattern = false;
unsigned char c;
unsigned char d;
@@ -212,9 +214,13 @@ bool tomoyo_is_correct_path(const char *
if (c == '/')
goto out;
}
- while ((c = *filename++) != '\0') {
+ while (1) {
+ c = *filename++;
+ if (!c)
+ break;
if (c == '\\') {
- switch ((c = *filename++)) {
+ c = *filename++;
+ switch (c) {
case '\\': /* "\\" */
continue;
case '$': /* "\$" */
@@ -231,6 +237,22 @@ bool tomoyo_is_correct_path(const char *
break; /* Must not contain pattern */
contains_pattern = true;
continue;
+ case '{': /* "/\{" */
+ if (filename - 3 < start ||
+ *(filename - 3) != '/')
+ break;
+ if (pattern_type == -1)
+ break; /* Must not contain pattern */
+ contains_pattern = true;
+ in_repetition = true;
+ continue;
+ case '}': /* "\}/" */
+ if (*filename != '/')
+ break;
+ if (!in_repetition)
+ break;
+ in_repetition = false;
+ continue;
case '0': /* "\ooo" */
case '1':
case '2':
@@ -246,6 +268,8 @@ bool tomoyo_is_correct_path(const char *
continue; /* pattern is not \000 */
}
goto out;
+ } else if (in_repetition && c == '/') {
+ goto out;
} else if (tomoyo_is_invalid(c)) {
goto out;
}
@@ -254,6 +278,8 @@ bool tomoyo_is_correct_path(const char *
if (!contains_pattern)
goto out;
}
+ if (in_repetition)
+ goto out;
return true;
out:
printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function,
@@ -360,33 +386,6 @@ struct tomoyo_domain_info *tomoyo_find_d
}
/**
- * tomoyo_path_depth - Evaluate the number of '/' in a string.
- *
- * @pathname: The string to evaluate.
- *
- * Returns path depth of the string.
- *
- * I score 2 for each of the '/' in the @pathname
- * and score 1 if the @pathname ends with '/'.
- */
-static int tomoyo_path_depth(const char *pathname)
-{
- int i = 0;
-
- if (pathname) {
- const char *ep = pathname + strlen(pathname);
- if (pathname < ep--) {
- if (*ep != '/')
- i++;
- while (pathname <= ep)
- if (*ep-- == '/')
- i += 2;
- }
- }
- return i;
-}
-
-/**
* tomoyo_const_part_length - Evaluate the initial length without a pattern in a token.
*
* @filename: The string to evaluate.
@@ -444,11 +443,10 @@ void tomoyo_fill_path_info(struct tomoyo
ptr->is_dir = len && (name[len - 1] == '/');
ptr->is_patterned = (ptr->const_len < len);
ptr->hash = full_name_hash(name, len);
- ptr->depth = tomoyo_path_depth(name);
}
/**
- * tomoyo_file_matches_to_pattern2 - Pattern matching without '/' character
+ * tomoyo_file_matches_pattern2 - Pattern matching without '/' character
* and "\-" pattern.
*
* @filename: The start of string to check.
@@ -458,10 +456,10 @@ void tomoyo_fill_path_info(struct tomoyo
*
* Returns true if @filename matches @pattern, false otherwise.
*/
-static bool tomoyo_file_matches_to_pattern2(const char *filename,
- const char *filename_end,
- const char *pattern,
- const char *pattern_end)
+static bool tomoyo_file_matches_pattern2(const char *filename,
+ const char *filename_end,
+ const char *pattern,
+ const char *pattern_end)
{
while (filename < filename_end && pattern < pattern_end) {
char c;
@@ -519,7 +517,7 @@ static bool tomoyo_file_matches_to_patte
case '*':
case '@':
for (i = 0; i <= filename_end - filename; i++) {
- if (tomoyo_file_matches_to_pattern2(
+ if (tomoyo_file_matches_pattern2(
filename + i, filename_end,
pattern + 1, pattern_end))
return true;
@@ -550,7 +548,7 @@ static bool tomoyo_file_matches_to_patte
j++;
}
for (i = 1; i <= j; i++) {
- if (tomoyo_file_matches_to_pattern2(
+ if (tomoyo_file_matches_pattern2(
filename + i, filename_end,
pattern + 1, pattern_end))
return true;
@@ -567,7 +565,7 @@ static bool tomoyo_file_matches_to_patte
}
/**
- * tomoyo_file_matches_to_pattern - Pattern matching without without '/' character.
+ * tomoyo_file_matches_pattern - Pattern matching without without '/' character.
*
* @filename: The start of string to check.
* @filename_end: The end of string to check.
@@ -576,7 +574,7 @@ static bool tomoyo_file_matches_to_patte
*
* Returns true if @filename matches @pattern, false otherwise.
*/
-static bool tomoyo_file_matches_to_pattern(const char *filename,
+static bool tomoyo_file_matches_pattern(const char *filename,
const char *filename_end,
const char *pattern,
const char *pattern_end)
@@ -589,10 +587,10 @@ static bool tomoyo_file_matches_to_patte
/* Split at "\-" pattern. */
if (*pattern++ != '\\' || *pattern++ != '-')
continue;
- result = tomoyo_file_matches_to_pattern2(filename,
- filename_end,
- pattern_start,
- pattern - 2);
+ result = tomoyo_file_matches_pattern2(filename,
+ filename_end,
+ pattern_start,
+ pattern - 2);
if (first)
result = !result;
if (result)
@@ -600,13 +598,79 @@ static bool tomoyo_file_matches_to_patte
first = false;
pattern_start = pattern;
}
- result = tomoyo_file_matches_to_pattern2(filename, filename_end,
- pattern_start, pattern_end);
+ result = tomoyo_file_matches_pattern2(filename, filename_end,
+ pattern_start, pattern_end);
return first ? result : !result;
}
/**
+ * tomoyo_path_matches_pattern2 - Do pathname pattern matching.
+ *
+ * @f: The start of string to check.
+ * @p: The start of pattern to compare.
+ *
+ * Returns true if @f matches @p, false otherwise.
+ */
+static bool tomoyo_path_matches_pattern2(const char *f, const char *p)
+{
+ const char *f_delimiter;
+ const char *p_delimiter;
+
+ while (*f && *p) {
+ f_delimiter = strchr(f, '/');
+ if (!f_delimiter)
+ f_delimiter = f + strlen(f);
+ p_delimiter = strchr(p, '/');
+ if (!p_delimiter)
+ p_delimiter = p + strlen(p);
+ if (*p == '\\' && *(p + 1) == '{')
+ goto recursive;
+ if (!tomoyo_file_matches_pattern(f, f_delimiter, p,
+ p_delimiter))
+ return false;
+ f = f_delimiter;
+ if (*f)
+ f++;
+ p = p_delimiter;
+ if (*p)
+ p++;
+ }
+ /* Ignore trailing "\*" and "\@" in @pattern. */
+ while (*p == '\\' &&
+ (*(p + 1) == '*' || *(p + 1) == '@'))
+ p += 2;
+ return !*f && !*p;
+ recursive:
+ /*
+ * The "\{" pattern is permitted only after '/' character.
+ * This guarantees that below "*(p - 1)" is safe.
+ * Also, the "\}" pattern is permitted only before '/' character
+ * so that "\{" + "\}" pair will not break the "\-" operator.
+ */
+ if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' ||
+ *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\')
+ return false; /* Bad pattern. */
+ do {
+ /* Compare current component with pattern. */
+ if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2,
+ p_delimiter - 2))
+ break;
+ /* Proceed to next component. */
+ f = f_delimiter;
+ if (!*f)
+ break;
+ f++;
+ /* Continue comparison. */
+ if (tomoyo_path_matches_pattern2(f, p_delimiter + 1))
+ return true;
+ f_delimiter = strchr(f, '/');
+ } while (f_delimiter);
+ return false; /* Not matched. */
+}
+
+/**
* tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern.
+ *
* @filename: The filename to check.
* @pattern: The pattern to compare.
*
@@ -615,24 +679,24 @@ static bool tomoyo_file_matches_to_patte
* The following patterns are available.
* \\ \ itself.
* \ooo Octal representation of a byte.
- * \* More than or equals to 0 character other than '/'.
- * \@ More than or equals to 0 character other than '/' or '.'.
+ * \* Zero or more repetitions of characters other than '/'.
+ * \@ Zero or more repetitions of characters other than '/' or '.'.
* \? 1 byte character other than '/'.
- * \$ More than or equals to 1 decimal digit.
+ * \$ One or more repetitions of decimal digits.
* \+ 1 decimal digit.
- * \X More than or equals to 1 hexadecimal digit.
+ * \X One or more repetitions of hexadecimal digits.
* \x 1 hexadecimal digit.
- * \A More than or equals to 1 alphabet character.
+ * \A One or more repetitions of alphabet characters.
* \a 1 alphabet character.
+ *
* \- Subtraction operator.
+ *
+ * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/
+ * /dir/dir/dir/ ).
*/
bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
const struct tomoyo_path_info *pattern)
{
- /*
- if (!filename || !pattern)
- return false;
- */
const char *f = filename->name;
const char *p = pattern->name;
const int len = pattern->const_len;
@@ -640,37 +704,15 @@ bool tomoyo_path_matches_pattern(const s
/* If @pattern doesn't contain pattern, I can use strcmp(). */
if (!pattern->is_patterned)
return !tomoyo_pathcmp(filename, pattern);
- /* Dont compare if the number of '/' differs. */
- if (filename->depth != pattern->depth)
+ /* Don't compare directory and non-directory. */
+ if (filename->is_dir != pattern->is_dir)
return false;
/* Compare the initial length without patterns. */
if (strncmp(f, p, len))
return false;
f += len;
p += len;
- /* Main loop. Compare each directory component. */
- while (*f && *p) {
- const char *f_delimiter = strchr(f, '/');
- const char *p_delimiter = strchr(p, '/');
- if (!f_delimiter)
- f_delimiter = f + strlen(f);
- if (!p_delimiter)
- p_delimiter = p + strlen(p);
- if (!tomoyo_file_matches_to_pattern(f, f_delimiter,
- p, p_delimiter))
- return false;
- f = f_delimiter;
- if (*f)
- f++;
- p = p_delimiter;
- if (*p)
- p++;
- }
- /* Ignore trailing "\*" and "\@" in @pattern. */
- while (*p == '\\' &&
- (*(p + 1) == '*' || *(p + 1) == '@'))
- p += 2;
- return !*f && !*p;
+ return tomoyo_path_matches_pattern2(f, p);
}
/**
--- security-testing-2.6.orig/security/tomoyo/common.h
+++ security-testing-2.6/security/tomoyo/common.h
@@ -56,9 +56,6 @@ struct tomoyo_page_buffer {
* (5) "is_patterned" is a bool which is true if "name" contains wildcard
* characters, false otherwise. This allows TOMOYO to use "hash" and
* strcmp() for string comparison if "is_patterned" is false.
- * (6) "depth" is calculated using the number of "/" characters in "name".
- * This allows TOMOYO to avoid comparing two pathnames which never match
- * (e.g. whether "/var/www/html/index.html" matches "/tmp/sh-thd-\$").
*/
struct tomoyo_path_info {
const char *name;
@@ -66,7 +63,6 @@ struct tomoyo_path_info {
u16 const_len; /* = tomoyo_const_part_length(name) */
bool is_dir; /* = tomoyo_strendswith(name, "/") */
bool is_patterned; /* = tomoyo_path_contains_pattern(name) */
- u16 depth; /* = tomoyo_path_depth(name) */
};
/*
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH] TOMOYO: Use RCU primitives for list operation
2009-10-13 11:37 ` [PATCH] TOMOYO: Add recursive directory matching operator support Tetsuo Handa
@ 2009-10-13 11:39 ` Tetsuo Handa
2009-10-29 5:40 ` Serge E. Hallyn
0 siblings, 1 reply; 6+ messages in thread
From: Tetsuo Handa @ 2009-10-13 11:39 UTC (permalink / raw)
To: linux-security-module; +Cc: linux-kernel
[PATCH] TOMOYO: Use RCU primitives for list operation
Remove down_read()/up_read() by replacing with RCU primitives.
SRCU based garbage collector will be added in the future.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/common.c | 52 ++++++++++-----------------------------------
security/tomoyo/common.h | 14 ++++++------
security/tomoyo/domain.c | 38 ++++++++++----------------------
security/tomoyo/file.c | 50 ++++++++++++++-----------------------------
security/tomoyo/realpath.c | 4 ---
5 files changed, 49 insertions(+), 109 deletions(-)
--- security-testing-2.6.orig/security/tomoyo/common.c
+++ security-testing-2.6/security/tomoyo/common.c
@@ -365,9 +365,6 @@ 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.
*/
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
@@ -377,7 +374,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;
@@ -837,8 +834,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)) {
@@ -891,7 +887,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) {
@@ -1143,7 +1138,7 @@ static int tomoyo_update_manager_entry(c
if (!saved_manager)
return -ENOMEM;
down_write(&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->manager != saved_manager)
continue;
ptr->is_deleted = is_delete;
@@ -1159,7 +1154,7 @@ static int tomoyo_update_manager_entry(c
goto out;
new_entry->manager = saved_manager;
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);
error = 0;
out:
up_write(&tomoyo_policy_manager_list_lock);
@@ -1199,7 +1194,6 @@ 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) {
struct tomoyo_policy_manager_entry *ptr;
@@ -1211,7 +1205,6 @@ static int tomoyo_read_manager_policy(st
if (!done)
break;
}
- up_read(&tomoyo_policy_manager_list_lock);
head->read_eof = done;
return 0;
}
@@ -1234,29 +1227,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;
@@ -1292,11 +1281,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;
@@ -1310,13 +1296,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;
@@ -1342,7 +1326,7 @@ static int tomoyo_delete_domain(char *do
tomoyo_fill_path_info(&name);
down_write(&tomoyo_domain_list_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;
@@ -1384,11 +1368,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;
@@ -1544,7 +1526,6 @@ 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) {
struct tomoyo_domain_info *domain;
const char *quota_exceeded = "";
@@ -1577,7 +1558,6 @@ 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) {
struct tomoyo_acl_info *ptr
@@ -1587,7 +1567,6 @@ acl_loop:
if (!done)
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (!done)
break;
head->read_step = 3;
@@ -1599,7 +1578,6 @@ tail_mark:
if (head->read_single_domain)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1626,9 +1604,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
@@ -1658,7 +1634,6 @@ 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) {
struct tomoyo_domain_info *domain;
domain = list_entry(pos, struct tomoyo_domain_info, list);
@@ -1669,7 +1644,6 @@ static int tomoyo_read_domain_profile(st
if (!done)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1889,15 +1863,13 @@ void tomoyo_load_policy(const char *file
tomoyo_policy_loaded = true;
{ /* Check all profiles currently assigned to domains are defined. */
struct tomoyo_domain_info *domain;
- down_read(&tomoyo_domain_list_lock);
- 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);
}
}
--- security-testing-2.6.orig/security/tomoyo/common.h
+++ security-testing-2.6/security/tomoyo/common.h
@@ -442,16 +442,16 @@ extern struct tomoyo_domain_info tomoyo_
* @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) \
- for (({ if (!cookie) \
- cookie = head; }), \
- pos = (cookie)->next; \
- prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
- (cookie) = pos, pos = pos->next)
+#define list_for_each_cookie(pos, cookie, head) \
+ for (({ if (!cookie) \
+ cookie = head; }), \
+ pos = rcu_dereference((cookie)->next); \
+ prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
+ (cookie) = pos, pos = rcu_dereference(pos->next))
#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
--- security-testing-2.6.orig/security/tomoyo/domain.c
+++ security-testing-2.6/security/tomoyo/domain.c
@@ -246,7 +246,7 @@ static int tomoyo_update_domain_initiali
if (!saved_program)
return -ENOMEM;
down_write(&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_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -266,7 +266,7 @@ static int tomoyo_update_domain_initiali
new_entry->program = saved_program;
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);
error = 0;
out:
up_write(&tomoyo_domain_initializer_list_lock);
@@ -285,7 +285,6 @@ 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) {
const char *no;
@@ -308,7 +307,6 @@ bool tomoyo_read_domain_initializer_poli
if (!done)
break;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return done;
}
@@ -355,8 +353,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) {
@@ -376,7 +373,6 @@ static bool tomoyo_is_domain_initializer
}
flag = true;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return flag;
}
@@ -459,7 +455,7 @@ static int tomoyo_update_domain_keeper_e
if (!saved_domainname)
return -ENOMEM;
down_write(&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_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -479,7 +475,7 @@ static int tomoyo_update_domain_keeper_e
new_entry->program = saved_program;
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);
error = 0;
out:
up_write(&tomoyo_domain_keeper_list_lock);
@@ -519,7 +515,6 @@ 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) {
struct tomoyo_domain_keeper_entry *ptr;
@@ -542,7 +537,6 @@ bool tomoyo_read_domain_keeper_policy(st
if (!done)
break;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return done;
}
@@ -563,8 +557,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) {
@@ -582,7 +575,6 @@ static bool tomoyo_is_domain_keeper(cons
}
flag = true;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return flag;
}
@@ -646,7 +638,7 @@ static int tomoyo_update_alias_entry(con
if (!saved_original_name || !saved_aliased_name)
return -ENOMEM;
down_write(&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->original_name != saved_original_name ||
ptr->aliased_name != saved_aliased_name)
continue;
@@ -663,7 +655,7 @@ static int tomoyo_update_alias_entry(con
goto out;
new_entry->original_name = saved_original_name;
new_entry->aliased_name = saved_aliased_name;
- list_add_tail(&new_entry->list, &tomoyo_alias_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_alias_list);
error = 0;
out:
up_write(&tomoyo_alias_list_lock);
@@ -682,7 +674,6 @@ 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) {
struct tomoyo_alias_entry *ptr;
@@ -695,7 +686,6 @@ bool tomoyo_read_alias_policy(struct tom
if (!done)
break;
}
- up_read(&tomoyo_alias_list_lock);
return done;
}
@@ -742,7 +732,7 @@ struct tomoyo_domain_info *tomoyo_find_o
if (!saved_domainname)
goto out;
/* Can I reuse memory of deleted domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
struct task_struct *p;
struct tomoyo_acl_info *ptr;
bool flag;
@@ -760,7 +750,7 @@ struct tomoyo_domain_info *tomoyo_find_o
read_unlock(&tasklist_lock);
if (flag)
continue;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
ptr->type |= TOMOYO_ACL_DELETED;
}
tomoyo_set_domain_flag(domain, true, domain->flags);
@@ -776,7 +766,7 @@ struct tomoyo_domain_info *tomoyo_find_o
INIT_LIST_HEAD(&domain->acl_info_list);
domain->domainname = saved_domainname;
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);
@@ -849,8 +839,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))
@@ -861,7 +850,6 @@ int tomoyo_find_next_domain(struct linux
tomoyo_fill_path_info(&r);
break;
}
- up_read(&tomoyo_alias_list_lock);
}
/* Check execute permission. */
@@ -892,9 +880,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)
--- security-testing-2.6.orig/security/tomoyo/file.c
+++ security-testing-2.6/security/tomoyo/file.c
@@ -220,7 +220,7 @@ static int tomoyo_update_globally_readab
if (!saved_filename)
return -ENOMEM;
down_write(&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->filename != saved_filename)
continue;
ptr->is_deleted = is_delete;
@@ -235,7 +235,7 @@ static int tomoyo_update_globally_readab
if (!new_entry)
goto out;
new_entry->filename = saved_filename;
- list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_globally_readable_list);
error = 0;
out:
up_write(&tomoyo_globally_readable_list_lock);
@@ -254,15 +254,13 @@ static bool tomoyo_is_globally_readable_
{
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,7 +289,6 @@ 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) {
struct tomoyo_globally_readable_file_entry *ptr;
@@ -305,7 +302,6 @@ bool tomoyo_read_globally_readable_polic
if (!done)
break;
}
- up_read(&tomoyo_globally_readable_list_lock);
return done;
}
@@ -363,7 +359,7 @@ static int tomoyo_update_file_pattern_en
if (!saved_pattern)
return -ENOMEM;
down_write(&tomoyo_pattern_list_lock);
- list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (saved_pattern != ptr->pattern)
continue;
ptr->is_deleted = is_delete;
@@ -378,7 +374,7 @@ static int tomoyo_update_file_pattern_en
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_pattern_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_pattern_list);
error = 0;
out:
up_write(&tomoyo_pattern_list_lock);
@@ -398,8 +394,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 +407,6 @@ tomoyo_get_file_pattern(const struct tom
break;
}
}
- up_read(&tomoyo_pattern_list_lock);
if (pattern)
filename = pattern;
return filename;
@@ -443,7 +437,6 @@ 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) {
struct tomoyo_pattern_entry *ptr;
ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
@@ -454,7 +447,6 @@ bool tomoyo_read_file_pattern(struct tom
if (!done)
break;
}
- up_read(&tomoyo_pattern_list_lock);
return done;
}
@@ -511,7 +503,7 @@ static int tomoyo_update_no_rewrite_entr
if (!saved_pattern)
return -ENOMEM;
down_write(&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->pattern != saved_pattern)
continue;
ptr->is_deleted = is_delete;
@@ -526,7 +518,7 @@ static int tomoyo_update_no_rewrite_entr
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_no_rewrite_list);
error = 0;
out:
up_write(&tomoyo_no_rewrite_list_lock);
@@ -546,8 +538,7 @@ static bool tomoyo_is_no_rewrite_file(co
struct tomoyo_no_rewrite_entry *ptr;
bool found = false;
- down_read(&tomoyo_no_rewrite_list_lock);
- 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 +546,6 @@ static bool tomoyo_is_no_rewrite_file(co
found = true;
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return found;
}
@@ -584,7 +574,6 @@ 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) {
struct tomoyo_no_rewrite_entry *ptr;
ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
@@ -595,7 +584,6 @@ bool tomoyo_read_no_rewrite_policy(struc
if (!done)
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return done;
}
@@ -661,8 +649,7 @@ static int tomoyo_check_single_path_acl2
struct tomoyo_acl_info *ptr;
int error = -EPERM;
- 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 +667,6 @@ static int tomoyo_check_single_path_acl2
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
@@ -848,7 +834,7 @@ static int tomoyo_update_single_path_acl
down_write(&tomoyo_domain_acl_info_list_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) {
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_single_path_acl_record,
@@ -875,12 +861,12 @@ static int tomoyo_update_single_path_acl
if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
acl->perm |= rw_mask;
acl->filename = saved_filename;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_single_path_acl_record,
@@ -937,7 +923,7 @@ static int tomoyo_update_double_path_acl
down_write(&tomoyo_domain_acl_info_list_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) {
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_double_path_acl_record,
@@ -960,12 +946,12 @@ static int tomoyo_update_double_path_acl
acl->perm = perm;
acl->filename1 = saved_filename1;
acl->filename2 = saved_filename2;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_double_path_acl_record,
@@ -1025,8 +1011,7 @@ static int tomoyo_check_double_path_acl(
if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
return 0;
- down_read(&tomoyo_domain_acl_info_list_lock);
- 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;
@@ -1041,7 +1026,6 @@ static int tomoyo_check_double_path_acl(
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
--- security-testing-2.6.orig/security/tomoyo/realpath.c
+++ security-testing-2.6/security/tomoyo/realpath.c
@@ -387,11 +387,9 @@ void __init tomoyo_realpath_init(void)
INIT_LIST_HEAD(&tomoyo_name_list[i]);
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
tomoyo_kernel_domain.domainname = tomoyo_save_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);
if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
panic("Can't register tomoyo_kernel_domain");
- up_read(&tomoyo_domain_list_lock);
}
/* Memory allocated for temporary purpose. */
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] TOMOYO: Use RCU primitives for list operation
2009-10-13 11:39 ` [PATCH] TOMOYO: Use RCU primitives for list operation Tetsuo Handa
@ 2009-10-29 5:40 ` Serge E. Hallyn
2009-12-04 12:34 ` Tetsuo Handa
0 siblings, 1 reply; 6+ messages in thread
From: Serge E. Hallyn @ 2009-10-29 5:40 UTC (permalink / raw)
To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel
Quoting Tetsuo Handa (penguin-kernel@I-love.SAKURA.ne.jp):
> [PATCH] TOMOYO: Use RCU primitives for list operation
>
> Remove down_read()/up_read() by replacing with RCU primitives.
> SRCU based garbage collector will be added in the future.
>
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
> security/tomoyo/common.c | 52 ++++++++++-----------------------------------
> security/tomoyo/common.h | 14 ++++++------
> security/tomoyo/domain.c | 38 ++++++++++----------------------
> security/tomoyo/file.c | 50 ++++++++++++++-----------------------------
> security/tomoyo/realpath.c | 4 ---
> 5 files changed, 49 insertions(+), 109 deletions(-)
>
> --- security-testing-2.6.orig/security/tomoyo/common.c
> +++ security-testing-2.6/security/tomoyo/common.c
> @@ -365,9 +365,6 @@ 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.
> */
> struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
> @@ -377,7 +374,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;
> @@ -837,8 +834,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)) {
You are removing the down_read()s, but not replacing them with
rcu_read_lock()s. I assume this is based on the same discussions
you had with Paul awhile ago about the safety of walking the list
bc you only append to the end (which I trust must have concluded
in your favor)?
If you'll be adding gc eventually anyway, is it really worthwhile
to 'violate the rules' now by calling list_for_each_entry_rcu()
without being inside rcu_read_lock() now? I fear it'll only serve
to confuse readers, especially those looking for rcu users to serve
as examples.
-serge
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] TOMOYO: Use RCU primitives for list operation
2009-10-29 5:40 ` Serge E. Hallyn
@ 2009-12-04 12:34 ` Tetsuo Handa
0 siblings, 0 replies; 6+ messages in thread
From: Tetsuo Handa @ 2009-12-04 12:34 UTC (permalink / raw)
To: serue; +Cc: linux-security-module, linux-kernel
Hello.
Serge E. Hallyn wrote:
> If you'll be adding gc eventually anyway, is it really worthwhile
> to 'violate the rules' now by calling list_for_each_entry_rcu()
> without being inside rcu_read_lock() now? I fear it'll only serve
> to confuse readers, especially those looking for rcu users to serve
> as examples.
You are right. I modified to call srcu_read_lock().
I don't modify tomoyo_file_permission() because it will be removed by
applying commit: c656ae95d1c5c8ed5763356263ace2d03087efec
(security/tomoyo: Remove now unnecessary handling of security_sysctl.).
Regards.
----------
[PATCH] TOMOYO: Use RCU primitives for list operation
Replace list operation with RCU primitives and replace
down_read()/up_read() with srcu_read_lock()/srcu_read_unlock().
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/common.c | 90 ++++++++++++++++++++----------------
security/tomoyo/common.h | 28 ++++++++---
security/tomoyo/domain.c | 63 +++++++++++++++----------
security/tomoyo/file.c | 110 +++++++++++++++++++++++++++++++--------------
security/tomoyo/realpath.c | 8 ++-
security/tomoyo/tomoyo.c | 20 ++++++--
6 files changed, 207 insertions(+), 112 deletions(-)
--- security-testing-2.6.orig/security/tomoyo/common.c
+++ security-testing-2.6/security/tomoyo/common.c
@@ -365,10 +365,9 @@ bool tomoyo_is_domain_def(const unsigned
*
* @domainname: The domainname to find.
*
- * Caller must call down_read(&tomoyo_domain_list_lock); or
- * down_write(&tomoyo_domain_list_lock); .
- *
* Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
{
@@ -377,7 +376,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;
@@ -829,6 +828,8 @@ bool tomoyo_verbose_mode(const struct to
* @domain: Pointer to "struct tomoyo_domain_info".
*
* Returns true if the domain is not exceeded quota, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
{
@@ -837,8 +838,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)) {
@@ -891,7 +891,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) {
@@ -1121,6 +1120,8 @@ static DECLARE_RWSEM(tomoyo_policy_manag
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_manager_entry(const char *manager,
const bool is_delete)
@@ -1143,7 +1144,7 @@ static int tomoyo_update_manager_entry(c
if (!saved_manager)
return -ENOMEM;
down_write(&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->manager != saved_manager)
continue;
ptr->is_deleted = is_delete;
@@ -1159,7 +1160,7 @@ static int tomoyo_update_manager_entry(c
goto out;
new_entry->manager = saved_manager;
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);
error = 0;
out:
up_write(&tomoyo_policy_manager_list_lock);
@@ -1172,6 +1173,8 @@ static int tomoyo_update_manager_entry(c
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
{
@@ -1191,6 +1194,8 @@ static int tomoyo_write_manager_policy(s
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
{
@@ -1199,7 +1204,6 @@ 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) {
struct tomoyo_policy_manager_entry *ptr;
@@ -1211,7 +1215,6 @@ static int tomoyo_read_manager_policy(st
if (!done)
break;
}
- up_read(&tomoyo_policy_manager_list_lock);
head->read_eof = done;
return 0;
}
@@ -1221,6 +1224,8 @@ static int tomoyo_read_manager_policy(st
*
* Returns true if the current process is permitted to modify policy
* via /sys/kernel/security/tomoyo/ interface.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_policy_manager(void)
{
@@ -1234,29 +1239,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;
@@ -1277,6 +1278,8 @@ static bool tomoyo_is_policy_manager(voi
* @data: String to parse.
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
const char *data)
@@ -1292,11 +1295,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;
@@ -1310,13 +1310,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;
@@ -1332,6 +1330,8 @@ static bool tomoyo_is_select_one(struct
* @domainname: The name of domain.
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_delete_domain(char *domainname)
{
@@ -1342,7 +1342,7 @@ static int tomoyo_delete_domain(char *do
tomoyo_fill_path_info(&name);
down_write(&tomoyo_domain_list_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;
@@ -1362,6 +1362,8 @@ static int tomoyo_delete_domain(char *do
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
{
@@ -1384,11 +1386,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;
@@ -1533,6 +1533,8 @@ static bool tomoyo_print_entry(struct to
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
{
@@ -1544,7 +1546,6 @@ 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) {
struct tomoyo_domain_info *domain;
const char *quota_exceeded = "";
@@ -1577,7 +1578,6 @@ 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) {
struct tomoyo_acl_info *ptr
@@ -1587,7 +1587,6 @@ acl_loop:
if (!done)
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (!done)
break;
head->read_step = 3;
@@ -1599,7 +1598,6 @@ tail_mark:
if (head->read_single_domain)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1615,6 +1613,8 @@ tail_mark:
*
* ( echo "select " $domainname; echo "use_profile " $profile ) |
* /usr/lib/ccs/loadpolicy -d
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
{
@@ -1626,9 +1626,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
@@ -1650,6 +1648,8 @@ static int tomoyo_write_domain_profile(s
* awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
* domainname = $0; } else if ( $1 == "use_profile" ) {
* print $2 " " domainname; domainname = ""; } } ; '
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
{
@@ -1658,7 +1658,6 @@ 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) {
struct tomoyo_domain_info *domain;
domain = list_entry(pos, struct tomoyo_domain_info, list);
@@ -1669,7 +1668,6 @@ static int tomoyo_read_domain_profile(st
if (!done)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1726,6 +1724,8 @@ static int tomoyo_read_pid(struct tomoyo
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
{
@@ -1760,6 +1760,8 @@ static int tomoyo_write_exception_policy
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, -EINVAL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
{
@@ -1889,15 +1891,13 @@ void tomoyo_load_policy(const char *file
tomoyo_policy_loaded = true;
{ /* Check all profiles currently assigned to domains are defined. */
struct tomoyo_domain_info *domain;
- down_read(&tomoyo_domain_list_lock);
- 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);
}
}
@@ -1945,6 +1945,8 @@ static int tomoyo_read_self_domain(struc
* @file: Pointer to "struct file".
*
* Associates policy handler and returns 0 on success, -ENOMEM otherwise.
+ *
+ * Caller acquires tomoyo_read_lock().
*/
static int tomoyo_open_control(const u8 type, struct file *file)
{
@@ -2030,6 +2032,7 @@ static int tomoyo_open_control(const u8
return -ENOMEM;
}
}
+ head->reader_idx = tomoyo_read_lock();
file->private_data = head;
/*
* Call the handler now if the file is
@@ -2051,6 +2054,8 @@ static int tomoyo_open_control(const u8
* @buffer_len: Size of @buffer.
*
* Returns bytes read on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_control(struct file *file, char __user *buffer,
const int buffer_len)
@@ -2094,6 +2099,8 @@ static int tomoyo_read_control(struct fi
* @buffer_len: Size of @buffer.
*
* Returns @buffer_len on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_control(struct file *file, const char __user *buffer,
const int buffer_len)
@@ -2144,11 +2151,14 @@ static int tomoyo_write_control(struct f
* @file: Pointer to "struct file".
*
* Releases memory and returns 0.
+ *
+ * Caller looses tomoyo_read_lock().
*/
static int tomoyo_close_control(struct file *file)
{
struct tomoyo_io_buffer *head = file->private_data;
+ tomoyo_read_unlock(head->reader_idx);
/* Release memory used for policy I/O. */
tomoyo_free(head->read_buf);
head->read_buf = NULL;
--- security-testing-2.6.orig/security/tomoyo/common.h
+++ security-testing-2.6/security/tomoyo/common.h
@@ -265,6 +265,8 @@ struct tomoyo_io_buffer {
int (*write) (struct tomoyo_io_buffer *);
/* Exclusive lock for this structure. */
struct mutex io_sem;
+ /* Index returned by tomoyo_read_lock(). */
+ int reader_idx;
/* The position currently reading from. */
struct list_head *read_var1;
/* Extra variables for reading. */
@@ -442,16 +444,28 @@ extern struct tomoyo_domain_info tomoyo_
* @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) \
- for (({ if (!cookie) \
- cookie = head; }), \
- pos = (cookie)->next; \
- prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
- (cookie) = pos, pos = pos->next)
+#define list_for_each_cookie(pos, cookie, head) \
+ for (({ if (!cookie) \
+ cookie = head; }), \
+ pos = rcu_dereference((cookie)->next); \
+ prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
+ (cookie) = pos, pos = rcu_dereference(pos->next))
+
+extern struct srcu_struct tomoyo_ss;
+
+static inline int tomoyo_read_lock(void)
+{
+ return srcu_read_lock(&tomoyo_ss);
+}
+
+static inline void tomoyo_read_unlock(int idx)
+{
+ srcu_read_unlock(&tomoyo_ss, idx);
+}
#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
--- security-testing-2.6.orig/security/tomoyo/domain.c
+++ security-testing-2.6/security/tomoyo/domain.c
@@ -217,6 +217,8 @@ static DECLARE_RWSEM(tomoyo_domain_initi
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_domain_initializer_entry(const char *domainname,
const char *program,
@@ -246,7 +248,7 @@ static int tomoyo_update_domain_initiali
if (!saved_program)
return -ENOMEM;
down_write(&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_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -266,7 +268,7 @@ static int tomoyo_update_domain_initiali
new_entry->program = saved_program;
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);
error = 0;
out:
up_write(&tomoyo_domain_initializer_list_lock);
@@ -279,13 +281,14 @@ static int tomoyo_update_domain_initiali
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
{
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) {
const char *no;
@@ -308,7 +311,6 @@ bool tomoyo_read_domain_initializer_poli
if (!done)
break;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return done;
}
@@ -320,6 +322,8 @@ bool tomoyo_read_domain_initializer_poli
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
const bool is_delete)
@@ -345,6 +349,8 @@ int tomoyo_write_domain_initializer_poli
*
* Returns true if executing @program reinitializes domain transition,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
domainname,
@@ -355,8 +361,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) {
@@ -376,7 +381,6 @@ static bool tomoyo_is_domain_initializer
}
flag = true;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return flag;
}
@@ -430,6 +434,8 @@ static DECLARE_RWSEM(tomoyo_domain_keepe
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_domain_keeper_entry(const char *domainname,
const char *program,
@@ -459,7 +465,7 @@ static int tomoyo_update_domain_keeper_e
if (!saved_domainname)
return -ENOMEM;
down_write(&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_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
@@ -479,7 +485,7 @@ static int tomoyo_update_domain_keeper_e
new_entry->program = saved_program;
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);
error = 0;
out:
up_write(&tomoyo_domain_keeper_list_lock);
@@ -493,6 +499,7 @@ static int tomoyo_update_domain_keeper_e
* @is_not: True if it is "no_keep_domain" entry.
* @is_delete: True if it is a delete request.
*
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
const bool is_delete)
@@ -513,13 +520,14 @@ int tomoyo_write_domain_keeper_policy(ch
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
{
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) {
struct tomoyo_domain_keeper_entry *ptr;
@@ -542,7 +550,6 @@ bool tomoyo_read_domain_keeper_policy(st
if (!done)
break;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return done;
}
@@ -555,6 +562,8 @@ bool tomoyo_read_domain_keeper_policy(st
*
* Returns true if executing @program supresses domain transition,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
const struct tomoyo_path_info *program,
@@ -563,8 +572,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) {
@@ -582,7 +590,6 @@ static bool tomoyo_is_domain_keeper(cons
}
flag = true;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return flag;
}
@@ -627,6 +634,8 @@ static DECLARE_RWSEM(tomoyo_alias_list_l
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_alias_entry(const char *original_name,
const char *aliased_name,
@@ -646,7 +655,7 @@ static int tomoyo_update_alias_entry(con
if (!saved_original_name || !saved_aliased_name)
return -ENOMEM;
down_write(&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->original_name != saved_original_name ||
ptr->aliased_name != saved_aliased_name)
continue;
@@ -663,7 +672,7 @@ static int tomoyo_update_alias_entry(con
goto out;
new_entry->original_name = saved_original_name;
new_entry->aliased_name = saved_aliased_name;
- list_add_tail(&new_entry->list, &tomoyo_alias_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_alias_list);
error = 0;
out:
up_write(&tomoyo_alias_list_lock);
@@ -676,13 +685,14 @@ static int tomoyo_update_alias_entry(con
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_alias_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
struct tomoyo_alias_entry *ptr;
@@ -695,7 +705,6 @@ bool tomoyo_read_alias_policy(struct tom
if (!done)
break;
}
- up_read(&tomoyo_alias_list_lock);
return done;
}
@@ -706,6 +715,8 @@ bool tomoyo_read_alias_policy(struct tom
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_alias_policy(char *data, const bool is_delete)
{
@@ -724,6 +735,8 @@ int tomoyo_write_alias_policy(char *data
* @profile: Profile number to assign if the domain was newly created.
*
* Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
domainname,
@@ -742,7 +755,7 @@ struct tomoyo_domain_info *tomoyo_find_o
if (!saved_domainname)
goto out;
/* Can I reuse memory of deleted domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
struct task_struct *p;
struct tomoyo_acl_info *ptr;
bool flag;
@@ -760,7 +773,7 @@ struct tomoyo_domain_info *tomoyo_find_o
read_unlock(&tasklist_lock);
if (flag)
continue;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
ptr->type |= TOMOYO_ACL_DELETED;
}
tomoyo_set_domain_flag(domain, true, domain->flags);
@@ -776,7 +789,7 @@ struct tomoyo_domain_info *tomoyo_find_o
INIT_LIST_HEAD(&domain->acl_info_list);
domain->domainname = saved_domainname;
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);
@@ -789,6 +802,8 @@ struct tomoyo_domain_info *tomoyo_find_o
* @bprm: Pointer to "struct linux_binprm".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_find_next_domain(struct linux_binprm *bprm)
{
@@ -849,8 +864,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))
@@ -861,7 +875,6 @@ int tomoyo_find_next_domain(struct linux
tomoyo_fill_path_info(&r);
break;
}
- up_read(&tomoyo_alias_list_lock);
}
/* Check execute permission. */
@@ -892,9 +905,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)
--- security-testing-2.6.orig/security/tomoyo/file.c
+++ security-testing-2.6/security/tomoyo/file.c
@@ -205,6 +205,8 @@ static DECLARE_RWSEM(tomoyo_globally_rea
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_globally_readable_entry(const char *filename,
const bool is_delete)
@@ -220,7 +222,7 @@ static int tomoyo_update_globally_readab
if (!saved_filename)
return -ENOMEM;
down_write(&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->filename != saved_filename)
continue;
ptr->is_deleted = is_delete;
@@ -235,7 +237,7 @@ static int tomoyo_update_globally_readab
if (!new_entry)
goto out;
new_entry->filename = saved_filename;
- list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_globally_readable_list);
error = 0;
out:
up_write(&tomoyo_globally_readable_list_lock);
@@ -248,21 +250,22 @@ static int tomoyo_update_globally_readab
* @filename: The filename to check.
*
* Returns true if any domain can open @filename for reading, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
filename)
{
struct tomoyo_globally_readable_file_entry *ptr;
bool found = false;
- down_read(&tomoyo_globally_readable_list_lock);
- list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
if (!ptr->is_deleted &&
tomoyo_path_matches_pattern(filename, ptr->filename)) {
found = true;
break;
}
}
- up_read(&tomoyo_globally_readable_list_lock);
return found;
}
@@ -273,6 +276,8 @@ static bool tomoyo_is_globally_readable_
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
{
@@ -285,13 +290,14 @@ int tomoyo_write_globally_readable_polic
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
{
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) {
struct tomoyo_globally_readable_file_entry *ptr;
@@ -305,7 +311,6 @@ bool tomoyo_read_globally_readable_polic
if (!done)
break;
}
- up_read(&tomoyo_globally_readable_list_lock);
return done;
}
@@ -348,6 +353,8 @@ static DECLARE_RWSEM(tomoyo_pattern_list
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_file_pattern_entry(const char *pattern,
const bool is_delete)
@@ -363,7 +370,7 @@ static int tomoyo_update_file_pattern_en
if (!saved_pattern)
return -ENOMEM;
down_write(&tomoyo_pattern_list_lock);
- list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (saved_pattern != ptr->pattern)
continue;
ptr->is_deleted = is_delete;
@@ -378,7 +385,7 @@ static int tomoyo_update_file_pattern_en
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_pattern_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_pattern_list);
error = 0;
out:
up_write(&tomoyo_pattern_list_lock);
@@ -391,6 +398,8 @@ static int tomoyo_update_file_pattern_en
* @filename: The filename to find patterned pathname.
*
* Returns pointer to pathname pattern if matched, @filename otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static const struct tomoyo_path_info *
tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
@@ -398,8 +407,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 +420,6 @@ tomoyo_get_file_pattern(const struct tom
break;
}
}
- up_read(&tomoyo_pattern_list_lock);
if (pattern)
filename = pattern;
return filename;
@@ -425,6 +432,8 @@ tomoyo_get_file_pattern(const struct tom
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_pattern_policy(char *data, const bool is_delete)
{
@@ -437,13 +446,14 @@ int tomoyo_write_pattern_policy(char *da
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_pattern_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
struct tomoyo_pattern_entry *ptr;
ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
@@ -454,7 +464,6 @@ bool tomoyo_read_file_pattern(struct tom
if (!done)
break;
}
- up_read(&tomoyo_pattern_list_lock);
return done;
}
@@ -497,6 +506,8 @@ static DECLARE_RWSEM(tomoyo_no_rewrite_l
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_no_rewrite_entry(const char *pattern,
const bool is_delete)
@@ -511,7 +522,7 @@ static int tomoyo_update_no_rewrite_entr
if (!saved_pattern)
return -ENOMEM;
down_write(&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->pattern != saved_pattern)
continue;
ptr->is_deleted = is_delete;
@@ -526,7 +537,7 @@ static int tomoyo_update_no_rewrite_entr
if (!new_entry)
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_no_rewrite_list);
error = 0;
out:
up_write(&tomoyo_no_rewrite_list_lock);
@@ -540,14 +551,15 @@ static int tomoyo_update_no_rewrite_entr
*
* Returns true if @filename is specified by "deny_rewrite" directive,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
{
struct tomoyo_no_rewrite_entry *ptr;
bool found = false;
- down_read(&tomoyo_no_rewrite_list_lock);
- list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->is_deleted)
continue;
if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -555,7 +567,6 @@ static bool tomoyo_is_no_rewrite_file(co
found = true;
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return found;
}
@@ -566,6 +577,8 @@ static bool tomoyo_is_no_rewrite_file(co
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
{
@@ -578,13 +591,14 @@ int tomoyo_write_no_rewrite_policy(char
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
{
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) {
struct tomoyo_no_rewrite_entry *ptr;
ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
@@ -595,7 +609,6 @@ bool tomoyo_read_no_rewrite_policy(struc
if (!done)
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return done;
}
@@ -613,6 +626,8 @@ bool tomoyo_read_no_rewrite_policy(struc
* Current policy syntax uses "allow_read/write" instead of "6",
* "allow_read" instead of "4", "allow_write" instead of "2",
* "allow_execute" instead of "1".
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_file_acl(const char *filename, u8 perm,
struct tomoyo_domain_info * const domain,
@@ -650,6 +665,8 @@ static int tomoyo_update_file_acl(const
* @may_use_pattern: True if patterned ACL is permitted.
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
domain,
@@ -661,8 +678,7 @@ static int tomoyo_check_single_path_acl2
struct tomoyo_acl_info *ptr;
int error = -EPERM;
- 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 +696,6 @@ static int tomoyo_check_single_path_acl2
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
@@ -692,6 +707,8 @@ static int tomoyo_check_single_path_acl2
* @operation: Mode ("read" or "write" or "read/write" or "execute").
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
const struct tomoyo_path_info *filename,
@@ -725,6 +742,8 @@ static int tomoyo_check_file_acl(const s
* @mode: Access control mode.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
const struct tomoyo_path_info *filename,
@@ -778,6 +797,8 @@ static int tomoyo_check_file_perm2(struc
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
const bool is_delete)
@@ -825,6 +846,8 @@ int tomoyo_write_file_policy(char *data,
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
struct tomoyo_domain_info *
@@ -848,7 +871,7 @@ static int tomoyo_update_single_path_acl
down_write(&tomoyo_domain_acl_info_list_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) {
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_single_path_acl_record,
@@ -875,12 +898,12 @@ static int tomoyo_update_single_path_acl
if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
acl->perm |= rw_mask;
acl->filename = saved_filename;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_single_path_acl_record,
@@ -912,6 +935,8 @@ static int tomoyo_update_single_path_acl
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
const char *filename2,
@@ -937,7 +962,7 @@ static int tomoyo_update_double_path_acl
down_write(&tomoyo_domain_acl_info_list_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) {
if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_double_path_acl_record,
@@ -960,12 +985,12 @@ static int tomoyo_update_double_path_acl
acl->perm = perm;
acl->filename1 = saved_filename1;
acl->filename2 = saved_filename2;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_double_path_acl_record,
@@ -992,6 +1017,8 @@ static int tomoyo_update_double_path_acl
* @filename: Filename to check.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
const u8 type,
@@ -1011,6 +1038,8 @@ static int tomoyo_check_single_path_acl(
* @filename2: Second filename to check.
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
const u8 type,
@@ -1025,8 +1054,7 @@ static int tomoyo_check_double_path_acl(
if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
return 0;
- down_read(&tomoyo_domain_acl_info_list_lock);
- 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;
@@ -1041,7 +1069,6 @@ static int tomoyo_check_double_path_acl(
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
@@ -1054,6 +1081,8 @@ static int tomoyo_check_double_path_acl(
* @mode: Access control mode.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
const domain, u8 operation,
@@ -1123,6 +1152,8 @@ int tomoyo_check_file_perm(struct tomoyo
* @filename: Check permission for "execute".
*
* Returns 0 on success, negativevalue otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
const struct tomoyo_path_info *filename)
@@ -1151,6 +1182,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;
@@ -1162,6 +1194,7 @@ int tomoyo_check_open_permission(struct
* don't call me.
*/
return 0;
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(path);
if (!buf)
goto out;
@@ -1187,6 +1220,7 @@ int tomoyo_check_open_permission(struct
buf, mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
@@ -1208,9 +1242,11 @@ 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;
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(path);
if (!buf)
goto out;
@@ -1229,6 +1265,7 @@ int tomoyo_check_1path_perm(struct tomoy
mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
@@ -1249,9 +1286,12 @@ 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;
+
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(&filp->f_path);
if (!buf)
goto out;
@@ -1264,6 +1304,7 @@ int tomoyo_check_rewrite_permission(stru
buf, mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
@@ -1288,9 +1329,11 @@ 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;
+ idx = tomoyo_read_lock();
buf1 = tomoyo_get_path(path1);
buf2 = tomoyo_get_path(path2);
if (!buf1 || !buf2)
@@ -1329,6 +1372,7 @@ int tomoyo_check_2path_perm(struct tomoy
out:
tomoyo_free(buf1);
tomoyo_free(buf2);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
--- security-testing-2.6.orig/security/tomoyo/realpath.c
+++ security-testing-2.6/security/tomoyo/realpath.c
@@ -392,11 +392,13 @@ void __init tomoyo_realpath_init(void)
INIT_LIST_HEAD(&tomoyo_name_list[i]);
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
- list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
- down_read(&tomoyo_domain_list_lock);
+ /*
+ * tomoyo_read_lock() is not needed because this function is
+ * called before the first "delete" request.
+ */
+ list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
panic("Can't register tomoyo_kernel_domain");
- up_read(&tomoyo_domain_list_lock);
}
/* Memory allocated for temporary purpose. */
--- security-testing-2.6.orig/security/tomoyo/tomoyo.c
+++ security-testing-2.6/security/tomoyo/tomoyo.c
@@ -76,8 +76,18 @@ static int tomoyo_bprm_check_security(st
* Execute permission is checked against pathname passed to do_execve()
* using current domain.
*/
- if (!domain)
- return tomoyo_find_next_domain(bprm);
+ if (!domain) {
+ /*
+ * We will need to protect whole execve() operation when GC
+ * starts kfree()ing "struct tomoyo_domain_info" because
+ * bprm->cred->security points to "struct tomoyo_domain_info"
+ * but "struct tomoyo_domain_info" does not have a refcounter.
+ */
+ const int idx = tomoyo_read_lock();
+ const int err = tomoyo_find_next_domain(bprm);
+ tomoyo_read_unlock(idx);
+ return err;
+ }
/*
* Read permission is checked against interpreters using next domain.
* '1' is the result of open_to_namei_flags(O_RDONLY).
@@ -297,6 +307,9 @@ static struct security_operations tomoyo
.path_rename = tomoyo_path_rename,
};
+/* Lock for GC. */
+struct srcu_struct tomoyo_ss;
+
static int __init tomoyo_init(void)
{
struct cred *cred = (struct cred *) current_cred();
@@ -304,7 +317,8 @@ static int __init tomoyo_init(void)
if (!security_module_enable(&tomoyo_security_ops))
return 0;
/* register ourselves with the security framework */
- if (register_security(&tomoyo_security_ops))
+ if (register_security(&tomoyo_security_ops) ||
+ init_srcu_struct(&tomoyo_ss))
panic("Failure registering TOMOYO Linux");
printk(KERN_INFO "TOMOYO Linux initialized\n");
cred->security = &tomoyo_kernel_domain;
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2009-12-15 1:39 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-12-11 13:53 [PATCH] TOMOYO: Use RCU primitives for list operation Tetsuo Handa
2009-12-14 16:39 ` Serge E. Hallyn
2009-12-15 1:39 ` Tetsuo Handa
-- strict thread matches above, loose matches on Subject: below --
2009-10-04 12:49 [TOMOYO #16 01/25] LSM: Add security_path_chmod() and security_path_chown() Tetsuo Handa
2009-10-08 17:10 ` John Johansen
2009-10-12 1:04 ` James Morris
2009-10-13 11:34 ` [TOMOYO #16 01/25] LSM: Add security_path_chmod() andsecurity_path_chown() Tetsuo Handa
2009-10-13 11:37 ` [PATCH] TOMOYO: Add recursive directory matching operator support Tetsuo Handa
2009-10-13 11:39 ` [PATCH] TOMOYO: Use RCU primitives for list operation Tetsuo Handa
2009-10-29 5:40 ` Serge E. Hallyn
2009-12-04 12:34 ` Tetsuo Handa
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox