Merge branch 'master' into next
James Morris [Sun, 17 Jan 2010 22:56:22 +0000 (09:56 +1100)]
16 files changed:
include/linux/security.h
kernel/capability.c
security/security.c
security/selinux/avc.c
security/selinux/include/security.h
security/selinux/selinuxfs.c
security/selinux/ss/mls.c
security/selinux/ss/services.c
security/tomoyo/common.c
security/tomoyo/common.h
security/tomoyo/domain.c
security/tomoyo/file.c
security/tomoyo/realpath.c
security/tomoyo/realpath.h
security/tomoyo/tomoyo.c
security/tomoyo/tomoyo.h

index 2c627d3..26eca85 100644 (file)
@@ -985,6 +985,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     Check permissions on incoming network packets.  This hook is distinct
  *     from Netfilter's IP input hooks since it is the first time that the
  *     incoming sk_buff @skb has been associated with a particular socket, @sk.
+ *     Must not sleep inside this hook because some callers hold spinlocks.
  *     @sk contains the sock (not socket) associated with the incoming sk_buff.
  *     @skb contains the incoming network data.
  * @socket_getpeersec_stream:
index 7f876e6..9e4697e 100644 (file)
@@ -135,7 +135,7 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
        if (pid && (pid != task_pid_vnr(current))) {
                struct task_struct *target;
 
-               read_lock(&tasklist_lock);
+               rcu_read_lock();
 
                target = find_task_by_vpid(pid);
                if (!target)
@@ -143,7 +143,7 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
                else
                        ret = security_capget(target, pEp, pIp, pPp);
 
-               read_unlock(&tasklist_lock);
+               rcu_read_unlock();
        } else
                ret = security_capget(current, pEp, pIp, pPp);
 
index 24e060b..440afe5 100644 (file)
@@ -389,42 +389,42 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
 EXPORT_SYMBOL(security_inode_init_security);
 
 #ifdef CONFIG_SECURITY_PATH
-int security_path_mknod(struct path *path, struct dentry *dentry, int mode,
+int security_path_mknod(struct path *dir, struct dentry *dentry, int mode,
                        unsigned int dev)
 {
-       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+       if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
                return 0;
-       return security_ops->path_mknod(path, dentry, mode, dev);
+       return security_ops->path_mknod(dir, dentry, mode, dev);
 }
 EXPORT_SYMBOL(security_path_mknod);
 
-int security_path_mkdir(struct path *path, struct dentry *dentry, int mode)
+int security_path_mkdir(struct path *dir, struct dentry *dentry, int mode)
 {
-       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+       if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
                return 0;
-       return security_ops->path_mkdir(path, dentry, mode);
+       return security_ops->path_mkdir(dir, dentry, mode);
 }
 
-int security_path_rmdir(struct path *path, struct dentry *dentry)
+int security_path_rmdir(struct path *dir, struct dentry *dentry)
 {
-       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+       if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
                return 0;
-       return security_ops->path_rmdir(path, dentry);
+       return security_ops->path_rmdir(dir, dentry);
 }
 
-int security_path_unlink(struct path *path, struct dentry *dentry)
+int security_path_unlink(struct path *dir, struct dentry *dentry)
 {
-       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+       if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
                return 0;
-       return security_ops->path_unlink(path, dentry);
+       return security_ops->path_unlink(dir, dentry);
 }
 
-int security_path_symlink(struct path *path, struct dentry *dentry,
+int security_path_symlink(struct path *dir, struct dentry *dentry,
                          const char *old_name)
 {
-       if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+       if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
                return 0;
-       return security_ops->path_symlink(path, dentry, old_name);
+       return security_ops->path_symlink(dir, dentry, old_name);
 }
 
 int security_path_link(struct dentry *old_dentry, struct path *new_dir,
@@ -630,14 +630,14 @@ int security_inode_killpriv(struct dentry *dentry)
 int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc)
 {
        if (unlikely(IS_PRIVATE(inode)))
-               return 0;
+               return -EOPNOTSUPP;
        return security_ops->inode_getsecurity(inode, name, buffer, alloc);
 }
 
 int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)
 {
        if (unlikely(IS_PRIVATE(inode)))
-               return 0;
+               return -EOPNOTSUPP;
        return security_ops->inode_setsecurity(inode, name, value, size, flags);
 }
 
index f2dde26..3ee9b6a 100644 (file)
@@ -746,9 +746,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
                else
                        avd = &avd_entry;
 
-               rc = security_compute_av(ssid, tsid, tclass, requested, avd);
-               if (rc)
-                       goto out;
+               security_compute_av(ssid, tsid, tclass, avd);
                rcu_read_lock();
                node = avc_insert(ssid, tsid, tclass, avd);
        } else {
@@ -770,7 +768,6 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
        }
 
        rcu_read_unlock();
-out:
        return rc;
 }
 
index 2553266..022cf06 100644 (file)
@@ -96,13 +96,11 @@ struct av_decision {
 /* definitions of av_decision.flags */
 #define AVD_FLAGS_PERMISSIVE   0x0001
 
-int security_compute_av(u32 ssid, u32 tsid,
-                       u16 tclass, u32 requested,
-                       struct av_decision *avd);
+void security_compute_av(u32 ssid, u32 tsid,
+                        u16 tclass, struct av_decision *avd);
 
-int security_compute_av_user(u32 ssid, u32 tsid,
-                            u16 tclass, u32 requested,
-                            struct av_decision *avd);
+void security_compute_av_user(u32 ssid, u32 tsid,
+                            u16 tclass, struct av_decision *avd);
 
 int security_transition_sid(u32 ssid, u32 tsid,
                            u16 tclass, u32 *out_sid);
index fab36fd..b7bb0f5 100644 (file)
@@ -494,7 +494,6 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
        char *scon, *tcon;
        u32 ssid, tsid;
        u16 tclass;
-       u32 req;
        struct av_decision avd;
        ssize_t length;
 
@@ -512,7 +511,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
                goto out;
 
        length = -EINVAL;
-       if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4)
+       if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
                goto out2;
 
        length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
@@ -522,9 +521,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
        if (length < 0)
                goto out2;
 
-       length = security_compute_av_user(ssid, tsid, tclass, req, &avd);
-       if (length < 0)
-               goto out2;
+       security_compute_av_user(ssid, tsid, tclass, &avd);
 
        length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
                          "%x %x %x %x %u %x",
index 3f2b270..e6654b5 100644 (file)
@@ -541,8 +541,8 @@ int mls_compute_sid(struct context *scontext,
        case AVTAB_MEMBER:
                /* Use the process effective MLS attributes. */
                return mls_context_cpy_low(newcontext, scontext);
-       default:
-               return -EINVAL;
+
+       /* fall through */
        }
        return -EINVAL;
 }
index b3efae2..3b42b15 100644 (file)
@@ -87,11 +87,10 @@ static u32 latest_granting;
 static int context_struct_to_string(struct context *context, char **scontext,
                                    u32 *scontext_len);
 
-static int context_struct_compute_av(struct context *scontext,
-                                    struct context *tcontext,
-                                    u16 tclass,
-                                    u32 requested,
-                                    struct av_decision *avd);
+static void context_struct_compute_av(struct context *scontext,
+                                     struct context *tcontext,
+                                     u16 tclass,
+                                     struct av_decision *avd);
 
 struct selinux_mapping {
        u16 value; /* policy value */
@@ -196,23 +195,6 @@ static u16 unmap_class(u16 tclass)
        return tclass;
 }
 
-static u32 unmap_perm(u16 tclass, u32 tperm)
-{
-       if (tclass < current_mapping_size) {
-               unsigned i;
-               u32 kperm = 0;
-
-               for (i = 0; i < current_mapping[tclass].num_perms; i++)
-                       if (tperm & (1<<i)) {
-                               kperm |= current_mapping[tclass].perms[i];
-                               tperm &= ~(1<<i);
-                       }
-               return kperm;
-       }
-
-       return tperm;
-}
-
 static void map_decision(u16 tclass, struct av_decision *avd,
                         int allow_unknown)
 {
@@ -532,7 +514,6 @@ out:
 static void type_attribute_bounds_av(struct context *scontext,
                                     struct context *tcontext,
                                     u16 tclass,
-                                    u32 requested,
                                     struct av_decision *avd)
 {
        struct context lo_scontext;
@@ -553,7 +534,6 @@ static void type_attribute_bounds_av(struct context *scontext,
                context_struct_compute_av(&lo_scontext,
                                          tcontext,
                                          tclass,
-                                         requested,
                                          &lo_avd);
                if ((lo_avd.allowed & avd->allowed) == avd->allowed)
                        return;         /* no masked permission */
@@ -569,7 +549,6 @@ static void type_attribute_bounds_av(struct context *scontext,
                context_struct_compute_av(scontext,
                                          &lo_tcontext,
                                          tclass,
-                                         requested,
                                          &lo_avd);
                if ((lo_avd.allowed & avd->allowed) == avd->allowed)
                        return;         /* no masked permission */
@@ -586,7 +565,6 @@ static void type_attribute_bounds_av(struct context *scontext,
                context_struct_compute_av(&lo_scontext,
                                          &lo_tcontext,
                                          tclass,
-                                         requested,
                                          &lo_avd);
                if ((lo_avd.allowed & avd->allowed) == avd->allowed)
                        return;         /* no masked permission */
@@ -607,11 +585,10 @@ static void type_attribute_bounds_av(struct context *scontext,
  * Compute access vectors based on a context structure pair for
  * the permissions in a particular class.
  */
-static int context_struct_compute_av(struct context *scontext,
-                                    struct context *tcontext,
-                                    u16 tclass,
-                                    u32 requested,
-                                    struct av_decision *avd)
+static void context_struct_compute_av(struct context *scontext,
+                                     struct context *tcontext,
+                                     u16 tclass,
+                                     struct av_decision *avd)
 {
        struct constraint_node *constraint;
        struct role_allow *ra;
@@ -622,19 +599,14 @@ static int context_struct_compute_av(struct context *scontext,
        struct ebitmap_node *snode, *tnode;
        unsigned int i, j;
 
-       /*
-        * Initialize the access vectors to the default values.
-        */
        avd->allowed = 0;
        avd->auditallow = 0;
        avd->auditdeny = 0xffffffff;
-       avd->seqno = latest_granting;
-       avd->flags = 0;
 
        if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
                if (printk_ratelimit())
                        printk(KERN_WARNING "SELinux:  Invalid class %hu\n", tclass);
-               return -EINVAL;
+               return;
        }
 
        tclass_datum = policydb.class_val_to_struct[tclass - 1];
@@ -705,9 +677,7 @@ static int context_struct_compute_av(struct context *scontext,
         * permission and notice it to userspace via audit.
         */
        type_attribute_bounds_av(scontext, tcontext,
-                                tclass, requested, avd);
-
-       return 0;
+                                tclass, avd);
 }
 
 static int security_validtrans_handle_fail(struct context *ocontext,
@@ -886,110 +856,116 @@ out:
        return rc;
 }
 
-
-static int security_compute_av_core(u32 ssid,
-                                   u32 tsid,
-                                   u16 tclass,
-                                   u32 requested,
-                                   struct av_decision *avd)
+static void avd_init(struct av_decision *avd)
 {
-       struct context *scontext = NULL, *tcontext = NULL;
-       int rc = 0;
-
-       scontext = sidtab_search(&sidtab, ssid);
-       if (!scontext) {
-               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
-                      __func__, ssid);
-               return -EINVAL;
-       }
-       tcontext = sidtab_search(&sidtab, tsid);
-       if (!tcontext) {
-               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
-                      __func__, tsid);
-               return -EINVAL;
-       }
-
-       rc = context_struct_compute_av(scontext, tcontext, tclass,
-                                      requested, avd);
-
-       /* permissive domain? */
-       if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
-               avd->flags |= AVD_FLAGS_PERMISSIVE;
-
-       return rc;
+       avd->allowed = 0;
+       avd->auditallow = 0;
+       avd->auditdeny = 0xffffffff;
+       avd->seqno = latest_granting;
+       avd->flags = 0;
 }
 
+
 /**
  * security_compute_av - Compute access vector decisions.
  * @ssid: source security identifier
  * @tsid: target security identifier
  * @tclass: target security class
- * @requested: requested permissions
  * @avd: access vector decisions
  *
  * Compute a set of access vector decisions based on the
  * SID pair (@ssid, @tsid) for the permissions in @tclass.
- * Return -%EINVAL if any of the parameters are invalid or %0
- * if the access vector decisions were computed successfully.
  */
-int security_compute_av(u32 ssid,
-                       u32 tsid,
-                       u16 orig_tclass,
-                       u32 orig_requested,
-                       struct av_decision *avd)
+void security_compute_av(u32 ssid,
+                        u32 tsid,
+                        u16 orig_tclass,
+                        struct av_decision *avd)
 {
        u16 tclass;
-       u32 requested;
-       int rc;
+       struct context *scontext = NULL, *tcontext = NULL;
 
        read_lock(&policy_rwlock);
-
+       avd_init(avd);
        if (!ss_initialized)
                goto allow;
 
-       requested = unmap_perm(orig_tclass, orig_requested);
+       scontext = sidtab_search(&sidtab, ssid);
+       if (!scontext) {
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, ssid);
+               goto out;
+       }
+
+       /* permissive domain? */
+       if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
+               avd->flags |= AVD_FLAGS_PERMISSIVE;
+
+       tcontext = sidtab_search(&sidtab, tsid);
+       if (!tcontext) {
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, tsid);
+               goto out;
+       }
+
        tclass = unmap_class(orig_tclass);
        if (unlikely(orig_tclass && !tclass)) {
                if (policydb.allow_unknown)
                        goto allow;
-               rc = -EINVAL;
                goto out;
        }
-       rc = security_compute_av_core(ssid, tsid, tclass, requested, avd);
+       context_struct_compute_av(scontext, tcontext, tclass, avd);
        map_decision(orig_tclass, avd, policydb.allow_unknown);
 out:
        read_unlock(&policy_rwlock);
-       return rc;
+       return;
 allow:
        avd->allowed = 0xffffffff;
-       avd->auditallow = 0;
-       avd->auditdeny = 0xffffffff;
-       avd->seqno = latest_granting;
-       avd->flags = 0;
-       rc = 0;
        goto out;
 }
 
-int security_compute_av_user(u32 ssid,
-                            u32 tsid,
-                            u16 tclass,
-                            u32 requested,
-                            struct av_decision *avd)
+void security_compute_av_user(u32 ssid,
+                             u32 tsid,
+                             u16 tclass,
+                             struct av_decision *avd)
 {
-       int rc;
+       struct context *scontext = NULL, *tcontext = NULL;
 
-       if (!ss_initialized) {
-               avd->allowed = 0xffffffff;
-               avd->auditallow = 0;
-               avd->auditdeny = 0xffffffff;
-               avd->seqno = latest_granting;
-               return 0;
+       read_lock(&policy_rwlock);
+       avd_init(avd);
+       if (!ss_initialized)
+               goto allow;
+
+       scontext = sidtab_search(&sidtab, ssid);
+       if (!scontext) {
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, ssid);
+               goto out;
        }
 
-       read_lock(&policy_rwlock);
-       rc = security_compute_av_core(ssid, tsid, tclass, requested, avd);
+       /* permissive domain? */
+       if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
+               avd->flags |= AVD_FLAGS_PERMISSIVE;
+
+       tcontext = sidtab_search(&sidtab, tsid);
+       if (!tcontext) {
+               printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
+                      __func__, tsid);
+               goto out;
+       }
+
+       if (unlikely(!tclass)) {
+               if (policydb.allow_unknown)
+                       goto allow;
+               goto out;
+       }
+
+       context_struct_compute_av(scontext, tcontext, tclass, avd);
+ out:
        read_unlock(&policy_rwlock);
-       return rc;
+       return;
+allow:
+       avd->allowed = 0xffffffff;
+       goto out;
 }
 
 /*
@@ -2555,7 +2531,7 @@ int security_get_classes(char ***classes, int *nclasses)
        read_lock(&policy_rwlock);
 
        *nclasses = policydb.p_classes.nprim;
-       *classes = kcalloc(*nclasses, sizeof(*classes), GFP_ATOMIC);
+       *classes = kcalloc(*nclasses, sizeof(**classes), GFP_ATOMIC);
        if (!*classes)
                goto out;
 
@@ -2602,7 +2578,7 @@ int security_get_permissions(char *class, char ***perms, int *nperms)
        }
 
        *nperms = match->permissions.nprim;
-       *perms = kcalloc(*nperms, sizeof(*perms), GFP_ATOMIC);
+       *perms = kcalloc(*nperms, sizeof(**perms), GFP_ATOMIC);
        if (!*perms)
                goto out;
 
index e0d0354..e331e69 100644 (file)
@@ -16,6 +16,9 @@
 #include "common.h"
 #include "tomoyo.h"
 
+/* Lock for protecting policy. */
+DEFINE_MUTEX(tomoyo_policy_lock);
+
 /* Has loading policy done? */
 bool tomoyo_policy_loaded;
 
@@ -365,10 +368,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 +379,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 +831,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,61 +841,34 @@ 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)) {
-                       struct tomoyo_single_path_acl_record *acl1;
-                       struct tomoyo_double_path_acl_record *acl2;
-                       u16 perm;
+                       struct tomoyo_single_path_acl_record *acl;
+                       u32 perm;
+                       u8 i;
                case TOMOYO_TYPE_SINGLE_PATH_ACL:
-                       acl1 = container_of(ptr,
-                                   struct tomoyo_single_path_acl_record,
-                                           head);
-                       perm = acl1->perm;
-                       if (perm & (1 << TOMOYO_TYPE_EXECUTE_ACL))
-                               count++;
-                       if (perm &
-                           ((1 << TOMOYO_TYPE_READ_ACL) |
-                            (1 << TOMOYO_TYPE_WRITE_ACL)))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_CREATE_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_UNLINK_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_MKDIR_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_RMDIR_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_MKFIFO_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_MKSOCK_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_MKBLOCK_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_MKCHAR_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_TRUNCATE_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_SYMLINK_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_REWRITE_ACL))
-                               count++;
+                       acl = container_of(ptr,
+                                          struct tomoyo_single_path_acl_record,
+                                          head);
+                       perm = acl->perm | (((u32) acl->perm_high) << 16);
+                       for (i = 0; i < TOMOYO_MAX_SINGLE_PATH_OPERATION; i++)
+                               if (perm & (1 << i))
+                                       count++;
+                       if (perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL))
+                               count -= 2;
                        break;
                case TOMOYO_TYPE_DOUBLE_PATH_ACL:
-                       acl2 = container_of(ptr,
+                       perm = container_of(ptr,
                                    struct tomoyo_double_path_acl_record,
-                                           head);
-                       perm = acl2->perm;
-                       if (perm & (1 << TOMOYO_TYPE_LINK_ACL))
-                               count++;
-                       if (perm & (1 << TOMOYO_TYPE_RENAME_ACL))
-                               count++;
+                                           head)->perm;
+                       for (i = 0; i < TOMOYO_MAX_DOUBLE_PATH_OPERATION; i++)
+                               if (perm & (1 << i))
+                                       count++;
                        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) {
@@ -923,9 +900,11 @@ static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned
        ptr = tomoyo_profile_ptr[profile];
        if (ptr)
                goto ok;
-       ptr = tomoyo_alloc_element(sizeof(*ptr));
-       if (!ptr)
+       ptr = kmalloc(sizeof(*ptr), GFP_KERNEL);
+       if (!tomoyo_memory_ok(ptr)) {
+               kfree(ptr);
                goto ok;
+       }
        for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++)
                ptr->value[i] = tomoyo_control_array[i].current_value;
        mb(); /* Avoid out-of-order execution. */
@@ -1112,7 +1091,6 @@ struct tomoyo_policy_manager_entry {
  * # cat /sys/kernel/security/tomoyo/manager
  */
 static LIST_HEAD(tomoyo_policy_manager_list);
-static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
 
 /**
  * tomoyo_update_manager_entry - Add a manager entry.
@@ -1121,6 +1099,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)
@@ -1142,8 +1122,9 @@ static int tomoyo_update_manager_entry(const char *manager,
        saved_manager = tomoyo_save_name(manager);
        if (!saved_manager)
                return -ENOMEM;
-       down_write(&tomoyo_policy_manager_list_lock);
-       list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+       new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
                if (ptr->manager != saved_manager)
                        continue;
                ptr->is_deleted = is_delete;
@@ -1154,15 +1135,16 @@ static int tomoyo_update_manager_entry(const char *manager,
                error = -ENOENT;
                goto out;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
+       if (!tomoyo_memory_ok(new_entry))
                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);
+       new_entry = NULL;
        error = 0;
  out:
-       up_write(&tomoyo_policy_manager_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
+       kfree(new_entry);
        return error;
 }
 
@@ -1172,6 +1154,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)
 {
@@ -1191,6 +1175,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)
 {
@@ -1199,7 +1185,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;
@@ -1211,7 +1196,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;
 }
@@ -1221,6 +1205,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)
 {
@@ -1234,29 +1220,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;
@@ -1277,6 +1259,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)
@@ -1292,11 +1276,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;
@@ -1310,13 +1291,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;
@@ -1332,6 +1311,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)
 {
@@ -1340,9 +1321,9 @@ static int tomoyo_delete_domain(char *domainname)
 
        name.name = domainname;
        tomoyo_fill_path_info(&name);
-       down_write(&tomoyo_domain_list_lock);
+       mutex_lock(&tomoyo_policy_lock);
        /* Is there an active domain? */
-       list_for_each_entry(domain, &tomoyo_domain_list, list) {
+       list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
                /* Never delete tomoyo_kernel_domain */
                if (domain == &tomoyo_kernel_domain)
                        continue;
@@ -1352,7 +1333,7 @@ static int tomoyo_delete_domain(char *domainname)
                domain->is_deleted = true;
                break;
        }
-       up_write(&tomoyo_domain_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
        return 0;
 }
 
@@ -1362,6 +1343,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)
 {
@@ -1384,11 +1367,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;
@@ -1426,7 +1407,7 @@ static bool tomoyo_print_single_path_acl(struct tomoyo_io_buffer *head,
        u8 bit;
        const char *atmark = "";
        const char *filename;
-       const u16 perm = ptr->perm;
+       const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16);
 
        filename = ptr->filename->name;
        for (bit = head->read_bit; bit < TOMOYO_MAX_SINGLE_PATH_OPERATION;
@@ -1533,6 +1514,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)
 {
@@ -1544,7 +1527,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 = "";
@@ -1577,7 +1559,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 +1568,6 @@ acl_loop:
                        if (!done)
                                break;
                }
-               up_read(&tomoyo_domain_acl_info_list_lock);
                if (!done)
                        break;
                head->read_step = 3;
@@ -1599,7 +1579,6 @@ tail_mark:
                if (head->read_single_domain)
                        break;
        }
-       up_read(&tomoyo_domain_list_lock);
        head->read_eof = done;
        return 0;
 }
@@ -1615,6 +1594,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 +1607,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
@@ -1650,6 +1629,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)
 {
@@ -1658,7 +1639,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);
@@ -1669,7 +1649,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;
 }
@@ -1726,6 +1705,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)
 {
@@ -1760,6 +1741,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)
 {
@@ -1889,15 +1872,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);
        }
 }
 
@@ -1945,6 +1926,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)
 {
@@ -2030,6 +2013,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
@@ -2051,6 +2035,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)
@@ -2094,6 +2080,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)
@@ -2144,11 +2132,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;
@@ -2161,35 +2152,6 @@ static int tomoyo_close_control(struct file *file)
 }
 
 /**
- * tomoyo_alloc_acl_element - Allocate permanent memory for ACL entry.
- *
- * @acl_type:  Type of ACL entry.
- *
- * Returns pointer to the ACL entry on success, NULL otherwise.
- */
-void *tomoyo_alloc_acl_element(const u8 acl_type)
-{
-       int len;
-       struct tomoyo_acl_info *ptr;
-
-       switch (acl_type) {
-       case TOMOYO_TYPE_SINGLE_PATH_ACL:
-               len = sizeof(struct tomoyo_single_path_acl_record);
-               break;
-       case TOMOYO_TYPE_DOUBLE_PATH_ACL:
-               len = sizeof(struct tomoyo_double_path_acl_record);
-               break;
-       default:
-               return NULL;
-       }
-       ptr = tomoyo_alloc_element(len);
-       if (!ptr)
-               return NULL;
-       ptr->type = acl_type;
-       return ptr;
-}
-
-/**
  * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
  *
  * @inode: Pointer to "struct inode".
index 92169d2..610a6a0 100644 (file)
@@ -108,7 +108,7 @@ struct tomoyo_path_info_with_data {
  *      (b) type & 0x80 : whether the entry is marked as "deleted".
  *
  * Packing "struct tomoyo_acl_info" allows
- * "struct tomoyo_single_path_acl_record" to embed "u16" and
+ * "struct tomoyo_single_path_acl_record" to embed "u8" + "u16" and
  * "struct tomoyo_double_path_acl_record" to embed "u8"
  * without enlarging their structure size.
  */
@@ -184,10 +184,13 @@ struct tomoyo_domain_info {
  * Directives held by this structure are "allow_read/write", "allow_execute",
  * "allow_read", "allow_write", "allow_create", "allow_unlink", "allow_mkdir",
  * "allow_rmdir", "allow_mkfifo", "allow_mksock", "allow_mkblock",
- * "allow_mkchar", "allow_truncate", "allow_symlink" and "allow_rewrite".
+ * "allow_mkchar", "allow_truncate", "allow_symlink", "allow_rewrite",
+ * "allow_chmod", "allow_chown", "allow_chgrp", "allow_chroot", "allow_mount"
+ * and "allow_unmount".
  */
 struct tomoyo_single_path_acl_record {
        struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_SINGLE_PATH_ACL */
+       u8 perm_high;
        u16 perm;
        /* Pointer to single pathname. */
        const struct tomoyo_path_info *filename;
@@ -195,7 +198,7 @@ struct tomoyo_single_path_acl_record {
 
 /*
  * tomoyo_double_path_acl_record is a structure which is used for holding an
- * entry with two pathnames operation (i.e. link() and rename()).
+ * entry with two pathnames operation (i.e. link(), rename() and pivot_root()).
  * It has following fields.
  *
  *  (1) "head" which is a "struct tomoyo_acl_info".
@@ -203,7 +206,8 @@ struct tomoyo_single_path_acl_record {
  *  (3) "filename1" is the source/old pathname.
  *  (4) "filename2" is the destination/new pathname.
  *
- * Directives held by this structure are "allow_rename" and "allow_link".
+ * Directives held by this structure are "allow_rename", "allow_link" and
+ * "allow_pivot_root".
  */
 struct tomoyo_double_path_acl_record {
        struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_DOUBLE_PATH_ACL */
@@ -265,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.         */
@@ -370,8 +376,6 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
 /* Check mode for specified functionality. */
 unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
                                const u8 index);
-/* Allocate memory for structures. */
-void *tomoyo_alloc_acl_element(const u8 acl_type);
 /* Fill in "struct tomoyo_path_info" members. */
 void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
 /* Run policy loader when /sbin/init starts. */
@@ -425,10 +429,9 @@ static inline bool tomoyo_is_invalid(const unsigned char c)
 
 /* The list for "struct tomoyo_domain_info". */
 extern struct list_head tomoyo_domain_list;
-extern struct rw_semaphore tomoyo_domain_list_lock;
 
-/* Lock for domain->acl_info_list. */
-extern struct rw_semaphore tomoyo_domain_acl_info_list_lock;
+/* Lock for protecting policy. */
+extern struct mutex tomoyo_policy_lock;
 
 /* Has /sbin/init started? */
 extern bool tomoyo_policy_loaded;
@@ -442,16 +445,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) */
index fcf52ac..a55a1cc 100644 (file)
@@ -58,7 +58,6 @@ struct tomoyo_domain_info tomoyo_kernel_domain;
  * exceptions.
  */
 LIST_HEAD(tomoyo_domain_list);
-DECLARE_RWSEM(tomoyo_domain_list_lock);
 
 /*
  * tomoyo_domain_initializer_entry is a structure which is used for holding
@@ -206,7 +205,6 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain)
  * unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain.
  */
 static LIST_HEAD(tomoyo_domain_initializer_list);
-static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
 
 /**
  * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
@@ -217,6 +215,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,
@@ -245,8 +245,9 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
        saved_program = tomoyo_save_name(program);
        if (!saved_program)
                return -ENOMEM;
-       down_write(&tomoyo_domain_initializer_list_lock);
-       list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+       new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
                if (ptr->is_not != is_not ||
                    ptr->domainname != saved_domainname ||
                    ptr->program != saved_program)
@@ -259,17 +260,18 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
                error = -ENOENT;
                goto out;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
+       if (!tomoyo_memory_ok(new_entry))
                goto out;
        new_entry->domainname = saved_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);
+       new_entry = NULL;
        error = 0;
  out:
-       up_write(&tomoyo_domain_initializer_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
+       kfree(new_entry);
        return error;
 }
 
@@ -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;
 }
 
@@ -419,7 +423,6 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
  * explicitly specified by "initialize_domain".
  */
 static LIST_HEAD(tomoyo_domain_keeper_list);
-static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
 
 /**
  * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
@@ -430,6 +433,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,
@@ -458,8 +463,9 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
        saved_domainname = tomoyo_save_name(domainname);
        if (!saved_domainname)
                return -ENOMEM;
-       down_write(&tomoyo_domain_keeper_list_lock);
-       list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+       new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
                if (ptr->is_not != is_not ||
                    ptr->domainname != saved_domainname ||
                    ptr->program != saved_program)
@@ -472,17 +478,18 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
                error = -ENOENT;
                goto out;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
+       if (!tomoyo_memory_ok(new_entry))
                goto out;
        new_entry->domainname = saved_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);
+       new_entry = NULL;
        error = 0;
  out:
-       up_write(&tomoyo_domain_keeper_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
+       kfree(new_entry);
        return error;
 }
 
@@ -493,6 +500,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 +521,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 +551,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 +563,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 +573,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 +591,6 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
                }
                flag = true;
        }
-       up_read(&tomoyo_domain_keeper_list_lock);
        return flag;
 }
 
@@ -617,7 +625,6 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
  * execve() succeeds is calculated using /bin/cat rather than /bin/busybox .
  */
 static LIST_HEAD(tomoyo_alias_list);
-static DECLARE_RWSEM(tomoyo_alias_list_lock);
 
 /**
  * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
@@ -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,
@@ -645,8 +654,9 @@ static int tomoyo_update_alias_entry(const char *original_name,
        saved_aliased_name = tomoyo_save_name(aliased_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) {
+       new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
                if (ptr->original_name != saved_original_name ||
                    ptr->aliased_name != saved_aliased_name)
                        continue;
@@ -658,15 +668,16 @@ static int tomoyo_update_alias_entry(const char *original_name,
                error = -ENOENT;
                goto out;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
+       if (!tomoyo_memory_ok(new_entry))
                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);
+       new_entry = NULL;
        error = 0;
  out:
-       up_write(&tomoyo_alias_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
+       kfree(new_entry);
        return error;
 }
 
@@ -676,13 +687,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 +707,6 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
                if (!done)
                        break;
        }
-       up_read(&tomoyo_alias_list_lock);
        return done;
 }
 
@@ -706,6 +717,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,15 +737,17 @@ 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,
                                                            const u8 profile)
 {
-       struct tomoyo_domain_info *domain = NULL;
+       struct tomoyo_domain_info *domain;
        const struct tomoyo_path_info *saved_domainname;
 
-       down_write(&tomoyo_domain_list_lock);
+       mutex_lock(&tomoyo_policy_lock);
        domain = tomoyo_find_domain(domainname);
        if (domain)
                goto out;
@@ -741,45 +756,19 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
        saved_domainname = tomoyo_save_name(domainname);
        if (!saved_domainname)
                goto out;
-       /* Can I reuse memory of deleted domain? */
-       list_for_each_entry(domain, &tomoyo_domain_list, list) {
-               struct task_struct *p;
-               struct tomoyo_acl_info *ptr;
-               bool flag;
-               if (!domain->is_deleted ||
-                   domain->domainname != saved_domainname)
-                       continue;
-               flag = false;
-               read_lock(&tasklist_lock);
-               for_each_process(p) {
-                       if (tomoyo_real_domain(p) != domain)
-                               continue;
-                       flag = true;
-                       break;
-               }
-               read_unlock(&tasklist_lock);
-               if (flag)
-                       continue;
-               list_for_each_entry(ptr, &domain->acl_info_list, list) {
-                       ptr->type |= TOMOYO_ACL_DELETED;
-               }
-               tomoyo_set_domain_flag(domain, true, domain->flags);
-               domain->profile = profile;
-               domain->quota_warned = false;
-               mb(); /* Avoid out-of-order execution. */
-               domain->is_deleted = false;
-               goto out;
-       }
-       /* No memory reusable. Create using new memory. */
-       domain = tomoyo_alloc_element(sizeof(*domain));
-       if (domain) {
+       domain = kmalloc(sizeof(*domain), GFP_KERNEL);
+       if (tomoyo_memory_ok(domain)) {
                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);
+       } else {
+               kfree(domain);
+               domain = NULL;
        }
+
  out:
-       up_write(&tomoyo_domain_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
        return domain;
 }
 
@@ -789,6 +778,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 +840,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 +851,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 +881,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)
index 9a6c588..cfcb096 100644 (file)
@@ -80,12 +80,20 @@ static const char *tomoyo_sp_keyword[TOMOYO_MAX_SINGLE_PATH_OPERATION] = {
        [TOMOYO_TYPE_TRUNCATE_ACL]   = "truncate",
        [TOMOYO_TYPE_SYMLINK_ACL]    = "symlink",
        [TOMOYO_TYPE_REWRITE_ACL]    = "rewrite",
+       [TOMOYO_TYPE_IOCTL_ACL]      = "ioctl",
+       [TOMOYO_TYPE_CHMOD_ACL]      = "chmod",
+       [TOMOYO_TYPE_CHOWN_ACL]      = "chown",
+       [TOMOYO_TYPE_CHGRP_ACL]      = "chgrp",
+       [TOMOYO_TYPE_CHROOT_ACL]     = "chroot",
+       [TOMOYO_TYPE_MOUNT_ACL]      = "mount",
+       [TOMOYO_TYPE_UMOUNT_ACL]     = "unmount",
 };
 
 /* Keyword array for double path operations. */
 static const char *tomoyo_dp_keyword[TOMOYO_MAX_DOUBLE_PATH_OPERATION] = {
        [TOMOYO_TYPE_LINK_ACL]    = "link",
        [TOMOYO_TYPE_RENAME_ACL]  = "rename",
+       [TOMOYO_TYPE_PIVOT_ROOT_ACL] = "pivot_root",
 };
 
 /**
@@ -158,9 +166,6 @@ static struct tomoyo_path_info *tomoyo_get_path(struct path *path)
        return NULL;
 }
 
-/* Lock for domain->acl_info_list. */
-DECLARE_RWSEM(tomoyo_domain_acl_info_list_lock);
-
 static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
                                         const char *filename2,
                                         struct tomoyo_domain_info *
@@ -195,7 +200,6 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
  * belongs to.
  */
 static LIST_HEAD(tomoyo_globally_readable_list);
-static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
 
 /**
  * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
@@ -204,6 +208,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)
@@ -218,8 +224,9 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
        saved_filename = tomoyo_save_name(filename);
        if (!saved_filename)
                return -ENOMEM;
-       down_write(&tomoyo_globally_readable_list_lock);
-       list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+       new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
                if (ptr->filename != saved_filename)
                        continue;
                ptr->is_deleted = is_delete;
@@ -230,14 +237,15 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
                error = -ENOENT;
                goto out;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
+       if (!tomoyo_memory_ok(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);
+       new_entry = NULL;
        error = 0;
  out:
-       up_write(&tomoyo_globally_readable_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
+       kfree(new_entry);
        return error;
 }
 
@@ -247,21 +255,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;
 }
 
@@ -272,6 +281,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)
 {
@@ -284,13 +295,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;
@@ -304,7 +316,6 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
                if (!done)
                        break;
        }
-       up_read(&tomoyo_globally_readable_list_lock);
        return done;
 }
 
@@ -338,7 +349,6 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
  * current process from accessing other process's information.
  */
 static LIST_HEAD(tomoyo_pattern_list);
-static DECLARE_RWSEM(tomoyo_pattern_list_lock);
 
 /**
  * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
@@ -347,6 +357,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)
@@ -361,8 +373,9 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
        saved_pattern = tomoyo_save_name(pattern);
        if (!saved_pattern)
                return -ENOMEM;
-       down_write(&tomoyo_pattern_list_lock);
-       list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+       new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
                if (saved_pattern != ptr->pattern)
                        continue;
                ptr->is_deleted = is_delete;
@@ -373,14 +386,15 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
                error = -ENOENT;
                goto out;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
+       if (!tomoyo_memory_ok(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);
+       new_entry = NULL;
        error = 0;
  out:
-       up_write(&tomoyo_pattern_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
+       kfree(new_entry);
        return error;
 }
 
@@ -390,6 +404,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)
@@ -397,8 +413,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))
@@ -411,7 +426,6 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
                        break;
                }
        }
-       up_read(&tomoyo_pattern_list_lock);
        if (pattern)
                filename = pattern;
        return filename;
@@ -424,6 +438,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)
 {
@@ -436,13 +452,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);
@@ -453,7 +470,6 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
                if (!done)
                        break;
        }
-       up_read(&tomoyo_pattern_list_lock);
        return done;
 }
 
@@ -487,7 +503,6 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
  * need to worry whether the file is already unlink()ed or not.
  */
 static LIST_HEAD(tomoyo_no_rewrite_list);
-static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
 
 /**
  * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
@@ -496,6 +511,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)
@@ -509,8 +526,9 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
        saved_pattern = tomoyo_save_name(pattern);
        if (!saved_pattern)
                return -ENOMEM;
-       down_write(&tomoyo_no_rewrite_list_lock);
-       list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+       new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
                if (ptr->pattern != saved_pattern)
                        continue;
                ptr->is_deleted = is_delete;
@@ -521,14 +539,15 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
                error = -ENOENT;
                goto out;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
+       if (!tomoyo_memory_ok(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);
+       new_entry = NULL;
        error = 0;
  out:
-       up_write(&tomoyo_no_rewrite_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
+       kfree(new_entry);
        return error;
 }
 
@@ -539,14 +558,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))
@@ -554,7 +574,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;
 }
 
@@ -565,6 +584,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)
 {
@@ -577,13 +598,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);
@@ -594,7 +616,6 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
                if (!done)
                        break;
        }
-       up_read(&tomoyo_no_rewrite_list_lock);
        return done;
 }
 
@@ -612,6 +633,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,
@@ -649,26 +672,32 @@ 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,
                                         const struct tomoyo_path_info *
                                         filename,
-                                        const u16 perm,
+                                        const u32 perm,
                                         const bool may_use_pattern)
 {
        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;
                acl = container_of(ptr, struct tomoyo_single_path_acl_record,
                                   head);
-               if (!(acl->perm & perm))
-                       continue;
+               if (perm <= 0xFFFF) {
+                       if (!(acl->perm & perm))
+                               continue;
+               } else {
+                       if (!(acl->perm_high & (perm >> 16)))
+                               continue;
+               }
                if (may_use_pattern || !acl->filename->is_patterned) {
                        if (!tomoyo_path_matches_pattern(filename,
                                                         acl->filename))
@@ -679,7 +708,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;
 }
 
@@ -691,12 +719,14 @@ 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,
                                 const u8 operation)
 {
-       u16 perm = 0;
+       u32 perm = 0;
 
        if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
                return 0;
@@ -724,6 +754,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,
@@ -777,6 +809,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)
@@ -824,18 +858,20 @@ 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 *
                                         const domain, const bool is_delete)
 {
-       static const u16 rw_mask =
+       static const u32 rw_mask =
                (1 << TOMOYO_TYPE_READ_ACL) | (1 << TOMOYO_TYPE_WRITE_ACL);
        const struct tomoyo_path_info *saved_filename;
        struct tomoyo_acl_info *ptr;
        struct tomoyo_single_path_acl_record *acl;
        int error = -ENOMEM;
-       const u16 perm = 1 << type;
+       const u32 perm = 1 << type;
 
        if (!domain)
                return -EINVAL;
@@ -844,10 +880,10 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
        saved_filename = tomoyo_save_name(filename);
        if (!saved_filename)
                return -ENOMEM;
-       down_write(&tomoyo_domain_acl_info_list_lock);
+       mutex_lock(&tomoyo_policy_lock);
        if (is_delete)
                goto delete;
-       list_for_each_entry(ptr, &domain->acl_info_list, list) {
+       list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
                if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
                        continue;
                acl = container_of(ptr, struct tomoyo_single_path_acl_record,
@@ -857,7 +893,10 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
                /* Special case. Clear all bits if marked as deleted. */
                if (ptr->type & TOMOYO_ACL_DELETED)
                        acl->perm = 0;
-               acl->perm |= perm;
+               if (perm <= 0xFFFF)
+                       acl->perm |= perm;
+               else
+                       acl->perm_high |= (perm >> 16);
                if ((acl->perm & rw_mask) == rw_mask)
                        acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE_ACL;
                else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL))
@@ -867,37 +906,47 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
                goto out;
        }
        /* Not found. Append it to the tail. */
-       acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_SINGLE_PATH_ACL);
-       if (!acl)
+       acl = kmalloc(sizeof(*acl), GFP_KERNEL);
+       if (!tomoyo_memory_ok(acl)) {
+               kfree(acl);
+               acl = NULL;
                goto out;
-       acl->perm = perm;
+       }
+       acl->head.type = TOMOYO_TYPE_SINGLE_PATH_ACL;
+       if (perm <= 0xFFFF)
+               acl->perm = perm;
+       else
+               acl->perm_high = (perm >> 16);
        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,
                                   head);
                if (acl->filename != saved_filename)
                        continue;
-               acl->perm &= ~perm;
+               if (perm <= 0xFFFF)
+                       acl->perm &= ~perm;
+               else
+                       acl->perm_high &= ~(perm >> 16);
                if ((acl->perm & rw_mask) != rw_mask)
                        acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE_ACL);
                else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)))
                        acl->perm &= ~rw_mask;
-               if (!acl->perm)
+               if (!acl->perm && !acl->perm_high)
                        ptr->type |= TOMOYO_ACL_DELETED;
                error = 0;
                break;
        }
  out:
-       up_write(&tomoyo_domain_acl_info_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
        return error;
 }
 
@@ -911,6 +960,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,
@@ -933,10 +984,10 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
        saved_filename2 = tomoyo_save_name(filename2);
        if (!saved_filename1 || !saved_filename2)
                return -ENOMEM;
-       down_write(&tomoyo_domain_acl_info_list_lock);
+       mutex_lock(&tomoyo_policy_lock);
        if (is_delete)
                goto delete;
-       list_for_each_entry(ptr, &domain->acl_info_list, list) {
+       list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
                if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
                        continue;
                acl = container_of(ptr, struct tomoyo_double_path_acl_record,
@@ -953,18 +1004,22 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
                goto out;
        }
        /* Not found. Append it to the tail. */
-       acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_DOUBLE_PATH_ACL);
-       if (!acl)
+       acl = kmalloc(sizeof(*acl), GFP_KERNEL);
+       if (!tomoyo_memory_ok(acl)) {
+               kfree(acl);
+               acl = NULL;
                goto out;
+       }
+       acl->head.type = TOMOYO_TYPE_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,
@@ -979,7 +1034,7 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
                break;
        }
  out:
-       up_write(&tomoyo_domain_acl_info_list_lock);
+       mutex_unlock(&tomoyo_policy_lock);
        return error;
 }
 
@@ -991,6 +1046,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,
@@ -1010,6 +1067,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,
@@ -1024,8 +1083,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;
@@ -1040,7 +1098,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;
 }
 
@@ -1053,6 +1110,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,
@@ -1101,6 +1160,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)
@@ -1129,6 +1190,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;
@@ -1140,6 +1202,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;
@@ -1165,13 +1228,14 @@ 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;
 }
 
 /**
- * tomoyo_check_1path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate" and "symlink".
+ * tomoyo_check_1path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate", "symlink", "ioctl", "chmod", "chown", "chgrp", "chroot", "mount" and "unmount".
  *
  * @domain:    Pointer to "struct tomoyo_domain_info".
  * @operation: Type of operation.
@@ -1186,15 +1250,18 @@ 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;
        switch (operation) {
        case TOMOYO_TYPE_MKDIR_ACL:
        case TOMOYO_TYPE_RMDIR_ACL:
+       case TOMOYO_TYPE_CHROOT_ACL:
                if (!buf->is_dir) {
                        /*
                         * tomoyo_get_path() reserves space for appending "/."
@@ -1207,6 +1274,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;
@@ -1227,9 +1295,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;
@@ -1242,13 +1313,14 @@ 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;
 }
 
 /**
- * tomoyo_check_2path_perm - Check permission for "rename" and "link".
+ * tomoyo_check_2path_perm - Check permission for "rename", "link" and "pivot_root".
  *
  * @domain:    Pointer to "struct tomoyo_domain_info".
  * @operation: Type of operation.
@@ -1266,9 +1338,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)
@@ -1307,6 +1381,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;
index 18369d4..54226d5 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/mnt_namespace.h>
 #include <linux/fs_struct.h>
 #include <linux/hash.h>
+#include <linux/magic.h>
 
 #include "common.h"
 #include "realpath.h"
@@ -112,7 +113,7 @@ int tomoyo_realpath_from_path2(struct path *path, char *newname,
                path_put(&ns_root);
                /* Prepend "/proc" prefix if using internal proc vfs mount. */
                if (!IS_ERR(sp) && (path->mnt->mnt_parent == path->mnt) &&
-                   (strcmp(path->mnt->mnt_sb->s_type->name, "proc") == 0)) {
+                   (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
                        sp -= 5;
                        if (sp >= newname)
                                memcpy(sp, "/proc", 5);
@@ -211,57 +212,32 @@ static unsigned int tomoyo_allocated_memory_for_elements;
 static unsigned int tomoyo_quota_for_elements;
 
 /**
- * tomoyo_alloc_element - Allocate permanent memory for structures.
+ * tomoyo_memory_ok - Check memory quota.
  *
- * @size: Size in bytes.
+ * @ptr: Pointer to allocated memory.
  *
- * Returns pointer to allocated memory on success, NULL otherwise.
+ * Returns true on success, false otherwise.
  *
- * Memory has to be zeroed.
- * The RAM is chunked, so NEVER try to kfree() the returned pointer.
+ * Caller holds tomoyo_policy_lock.
+ * Memory pointed by @ptr will be zeroed on success.
  */
-void *tomoyo_alloc_element(const unsigned int size)
+bool tomoyo_memory_ok(void *ptr)
 {
-       static char *buf;
-       static DEFINE_MUTEX(lock);
-       static unsigned int buf_used_len = PATH_MAX;
-       char *ptr = NULL;
-       /*Assumes sizeof(void *) >= sizeof(long) is true. */
-       const unsigned int word_aligned_size
-               = roundup(size, max(sizeof(void *), sizeof(long)));
-       if (word_aligned_size > PATH_MAX)
-               return NULL;
-       mutex_lock(&lock);
-       if (buf_used_len + word_aligned_size > PATH_MAX) {
-               if (!tomoyo_quota_for_elements ||
-                   tomoyo_allocated_memory_for_elements
-                   + PATH_MAX <= tomoyo_quota_for_elements)
-                       ptr = kzalloc(PATH_MAX, GFP_KERNEL);
-               if (!ptr) {
-                       printk(KERN_WARNING "ERROR: Out of memory "
-                              "for tomoyo_alloc_element().\n");
-                       if (!tomoyo_policy_loaded)
-                               panic("MAC Initialization failed.\n");
-               } else {
-                       buf = ptr;
-                       tomoyo_allocated_memory_for_elements += PATH_MAX;
-                       buf_used_len = word_aligned_size;
-                       ptr = buf;
-               }
-       } else if (word_aligned_size) {
-               int i;
-               ptr = buf + buf_used_len;
-               buf_used_len += word_aligned_size;
-               for (i = 0; i < word_aligned_size; i++) {
-                       if (!ptr[i])
-                               continue;
-                       printk(KERN_ERR "WARNING: Reserved memory was tainted! "
-                              "The system might go wrong.\n");
-                       ptr[i] = '\0';
-               }
+       int allocated_len = ptr ? ksize(ptr) : 0;
+       bool result = false;
+       if (!ptr || (tomoyo_quota_for_elements &&
+                    tomoyo_allocated_memory_for_elements
+                    + allocated_len > tomoyo_quota_for_elements)) {
+               printk(KERN_WARNING "ERROR: Out of memory "
+                      "for tomoyo_alloc_element().\n");
+               if (!tomoyo_policy_loaded)
+                       panic("MAC Initialization failed.\n");
+       } else {
+               result = true;
+               tomoyo_allocated_memory_for_elements += allocated_len;
+               memset(ptr, 0, allocated_len);
        }
-       mutex_unlock(&lock);
-       return ptr;
+       return result;
 }
 
 /* Memory allocated for string data in bytes. */
@@ -292,13 +268,6 @@ struct tomoyo_name_entry {
        struct tomoyo_path_info entry;
 };
 
-/* Structure for available memory region. */
-struct tomoyo_free_memory_block_list {
-       struct list_head list;
-       char *ptr;             /* Pointer to a free area. */
-       int len;               /* Length of the area.     */
-};
-
 /*
  * tomoyo_name_list is used for holding string data used by TOMOYO.
  * Since same string data is likely used for multiple times (e.g.
@@ -313,52 +282,32 @@ static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
  * @name: The string to store into the permernent memory.
  *
  * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
- *
- * The RAM is shared, so NEVER try to modify or kfree() the returned name.
  */
 const struct tomoyo_path_info *tomoyo_save_name(const char *name)
 {
-       static LIST_HEAD(fmb_list);
        static DEFINE_MUTEX(lock);
        struct tomoyo_name_entry *ptr;
        unsigned int hash;
-       /* fmb contains available size in bytes.
-          fmb is removed from the fmb_list when fmb->len becomes 0. */
-       struct tomoyo_free_memory_block_list *fmb;
        int len;
-       char *cp;
+       int allocated_len;
        struct list_head *head;
 
        if (!name)
                return NULL;
        len = strlen(name) + 1;
-       if (len > TOMOYO_MAX_PATHNAME_LEN) {
-               printk(KERN_WARNING "ERROR: Name too long "
-                      "for tomoyo_save_name().\n");
-               return NULL;
-       }
        hash = full_name_hash((const unsigned char *) name, len - 1);
        head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)];
-
        mutex_lock(&lock);
        list_for_each_entry(ptr, head, list) {
                if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name))
                        goto out;
        }
-       list_for_each_entry(fmb, &fmb_list, list) {
-               if (len <= fmb->len)
-                       goto ready;
-       }
-       if (!tomoyo_quota_for_savename ||
-           tomoyo_allocated_memory_for_savename + PATH_MAX
-           <= tomoyo_quota_for_savename)
-               cp = kzalloc(PATH_MAX, GFP_KERNEL);
-       else
-               cp = NULL;
-       fmb = kzalloc(sizeof(*fmb), GFP_KERNEL);
-       if (!cp || !fmb) {
-               kfree(cp);
-               kfree(fmb);
+       ptr = kzalloc(sizeof(*ptr) + len, GFP_KERNEL);
+       allocated_len = ptr ? ksize(ptr) : 0;
+       if (!ptr || (tomoyo_quota_for_savename &&
+                    tomoyo_allocated_memory_for_savename + allocated_len
+                    > tomoyo_quota_for_savename)) {
+               kfree(ptr);
                printk(KERN_WARNING "ERROR: Out of memory "
                       "for tomoyo_save_name().\n");
                if (!tomoyo_policy_loaded)
@@ -366,24 +315,11 @@ const struct tomoyo_path_info *tomoyo_save_name(const char *name)
                ptr = NULL;
                goto out;
        }
-       tomoyo_allocated_memory_for_savename += PATH_MAX;
-       list_add(&fmb->list, &fmb_list);
-       fmb->ptr = cp;
-       fmb->len = PATH_MAX;
- ready:
-       ptr = tomoyo_alloc_element(sizeof(*ptr));
-       if (!ptr)
-               goto out;
-       ptr->entry.name = fmb->ptr;
-       memmove(fmb->ptr, name, len);
+       tomoyo_allocated_memory_for_savename += allocated_len;
+       ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
+       memmove((char *) ptr->entry.name, name, len);
        tomoyo_fill_path_info(&ptr->entry);
-       fmb->ptr += len;
-       fmb->len -= len;
        list_add_tail(&ptr->list, head);
-       if (fmb->len == 0) {
-               list_del(&fmb->list);
-               kfree(fmb);
-       }
  out:
        mutex_unlock(&lock);
        return ptr ? &ptr->entry : NULL;
@@ -401,11 +337,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. */
index 78217a3..47b4f59 100644 (file)
@@ -36,11 +36,8 @@ char *tomoyo_realpath_nofollow(const char *pathname);
 /* Same with tomoyo_realpath() except that the pathname is already solved. */
 char *tomoyo_realpath_from_path(struct path *path);
 
-/*
- * Allocate memory for ACL entry.
- * The RAM is chunked, so NEVER try to kfree() the returned pointer.
- */
-void *tomoyo_alloc_element(const unsigned int size);
+/* Check memory quota. */
+bool tomoyo_memory_ok(void *ptr);
 
 /*
  * Keep the given name on the RAM.
index 8a00ade..714daa3 100644 (file)
@@ -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).
@@ -194,6 +204,60 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
        return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
 }
 
+static int tomoyo_file_ioctl(struct file *file, unsigned int cmd,
+                            unsigned long arg)
+{
+       return tomoyo_check_1path_perm(tomoyo_domain(), TOMOYO_TYPE_IOCTL_ACL,
+                                      &file->f_path);
+}
+
+static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
+                            mode_t mode)
+{
+       struct path path = { mnt, dentry };
+       return tomoyo_check_1path_perm(tomoyo_domain(), TOMOYO_TYPE_CHMOD_ACL,
+                                      &path);
+}
+
+static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid)
+{
+       int error = 0;
+       if (uid != (uid_t) -1)
+               error = tomoyo_check_1path_perm(tomoyo_domain(),
+                                               TOMOYO_TYPE_CHOWN_ACL, path);
+       if (!error && gid != (gid_t) -1)
+               error = tomoyo_check_1path_perm(tomoyo_domain(),
+                                               TOMOYO_TYPE_CHGRP_ACL, path);
+       return error;
+}
+
+static int tomoyo_path_chroot(struct path *path)
+{
+       return tomoyo_check_1path_perm(tomoyo_domain(), TOMOYO_TYPE_CHROOT_ACL,
+                                      path);
+}
+
+static int tomoyo_sb_mount(char *dev_name, struct path *path,
+                          char *type, unsigned long flags, void *data)
+{
+       return tomoyo_check_1path_perm(tomoyo_domain(), TOMOYO_TYPE_MOUNT_ACL,
+                                      path);
+}
+
+static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
+{
+       struct path path = { mnt, mnt->mnt_root };
+       return tomoyo_check_1path_perm(tomoyo_domain(), TOMOYO_TYPE_UMOUNT_ACL,
+                                      &path);
+}
+
+static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)
+{
+       return tomoyo_check_2path_perm(tomoyo_domain(),
+                                      TOMOYO_TYPE_PIVOT_ROOT_ACL,
+                                      new_path, old_path);
+}
+
 /*
  * tomoyo_security_ops is a "struct security_operations" which is used for
  * registering TOMOYO.
@@ -215,8 +279,18 @@ static struct security_operations tomoyo_security_ops = {
        .path_mknod          = tomoyo_path_mknod,
        .path_link           = tomoyo_path_link,
        .path_rename         = tomoyo_path_rename,
+       .file_ioctl          = tomoyo_file_ioctl,
+       .path_chmod          = tomoyo_path_chmod,
+       .path_chown          = tomoyo_path_chown,
+       .path_chroot         = tomoyo_path_chroot,
+       .sb_mount            = tomoyo_sb_mount,
+       .sb_umount           = tomoyo_sb_umount,
+       .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();
@@ -224,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;
index ed75832..bf3986a 100644 (file)
@@ -62,11 +62,19 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm);
 #define TOMOYO_TYPE_TRUNCATE_ACL     12
 #define TOMOYO_TYPE_SYMLINK_ACL      13
 #define TOMOYO_TYPE_REWRITE_ACL      14
-#define TOMOYO_MAX_SINGLE_PATH_OPERATION 15
+#define TOMOYO_TYPE_IOCTL_ACL        15
+#define TOMOYO_TYPE_CHMOD_ACL        16
+#define TOMOYO_TYPE_CHOWN_ACL        17
+#define TOMOYO_TYPE_CHGRP_ACL        18
+#define TOMOYO_TYPE_CHROOT_ACL       19
+#define TOMOYO_TYPE_MOUNT_ACL        20
+#define TOMOYO_TYPE_UMOUNT_ACL       21
+#define TOMOYO_MAX_SINGLE_PATH_OPERATION 22
 
 #define TOMOYO_TYPE_LINK_ACL         0
 #define TOMOYO_TYPE_RENAME_ACL       1
-#define TOMOYO_MAX_DOUBLE_PATH_OPERATION 2
+#define TOMOYO_TYPE_PIVOT_ROOT_ACL   2
+#define TOMOYO_MAX_DOUBLE_PATH_OPERATION 3
 
 #define TOMOYO_DOMAINPOLICY          0
 #define TOMOYO_EXCEPTIONPOLICY       1