Merge tag 'selinux-pr-20190917' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 23 Sep 2019 18:21:04 +0000 (11:21 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 23 Sep 2019 18:21:04 +0000 (11:21 -0700)
Pull selinux updates from Paul Moore:

 - Add LSM hooks, and SELinux access control hooks, for dnotify,
   fanotify, and inotify watches. This has been discussed with both the
   LSM and fs/notify folks and everybody is good with these new hooks.

 - The LSM stacking changes missed a few calls to current_security() in
   the SELinux code; we fix those and remove current_security() for
   good.

 - Improve our network object labeling cache so that we always return
   the object's label, even when under memory pressure. Previously we
   would return an error if we couldn't allocate a new cache entry, now
   we always return the label even if we can't create a new cache entry
   for it.

 - Convert the sidtab atomic_t counter to a normal u32 with
   READ/WRITE_ONCE() and memory barrier protection.

 - A few patches to policydb.c to clean things up (remove forward
   declarations, long lines, bad variable names, etc)

* tag 'selinux-pr-20190917' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
  lsm: remove current_security()
  selinux: fix residual uses of current_security() for the SELinux blob
  selinux: avoid atomic_t usage in sidtab
  fanotify, inotify, dnotify, security: add security hook for fs notifications
  selinux: always return a secid from the network caches if we find one
  selinux: policydb - rename type_val_to_struct_array
  selinux: policydb - fix some checkpatch.pl warnings
  selinux: shuffle around policydb.c to get rid of forward declarations

18 files changed:
fs/notify/dnotify/dnotify.c
fs/notify/fanotify/fanotify_user.c
fs/notify/inotify/inotify_user.c
include/linux/cred.h
include/linux/lsm_hooks.h
include/linux/security.h
security/security.c
security/selinux/hooks.c
security/selinux/include/classmap.h
security/selinux/include/objsec.h
security/selinux/netif.c
security/selinux/netnode.c
security/selinux/netport.c
security/selinux/ss/policydb.c
security/selinux/ss/policydb.h
security/selinux/ss/services.c
security/selinux/ss/sidtab.c
security/selinux/ss/sidtab.h

index c03758c..7a42c2e 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/sched/signal.h>
 #include <linux/dnotify.h>
 #include <linux/init.h>
+#include <linux/security.h>
 #include <linux/spinlock.h>
 #include <linux/slab.h>
 #include <linux/fdtable.h>
@@ -279,6 +280,17 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
                goto out_err;
        }
 
+       /*
+        * convert the userspace DN_* "arg" to the internal FS_*
+        * defined in fsnotify
+        */
+       mask = convert_arg(arg);
+
+       error = security_path_notify(&filp->f_path, mask,
+                       FSNOTIFY_OBJ_TYPE_INODE);
+       if (error)
+               goto out_err;
+
        /* expect most fcntl to add new rather than augment old */
        dn = kmem_cache_alloc(dnotify_struct_cache, GFP_KERNEL);
        if (!dn) {
@@ -293,9 +305,6 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
                goto out_err;
        }
 
-       /* convert the userspace DN_* "arg" to the internal FS_* defines in fsnotify */
-       mask = convert_arg(arg);
-
        /* set up the new_fsn_mark and new_dn_mark */
        new_fsn_mark = &new_dn_mark->fsn_mark;
        fsnotify_init_mark(new_fsn_mark, dnotify_group);
index 91006f4..8508ab5 100644 (file)
@@ -528,7 +528,8 @@ static const struct file_operations fanotify_fops = {
 };
 
 static int fanotify_find_path(int dfd, const char __user *filename,
-                             struct path *path, unsigned int flags)
+                             struct path *path, unsigned int flags, __u64 mask,
+                             unsigned int obj_type)
 {
        int ret;
 
@@ -567,8 +568,15 @@ static int fanotify_find_path(int dfd, const char __user *filename,
 
        /* you can only watch an inode if you have read permissions on it */
        ret = inode_permission(path->dentry->d_inode, MAY_READ);
+       if (ret) {
+               path_put(path);
+               goto out;
+       }
+
+       ret = security_path_notify(path, mask, obj_type);
        if (ret)
                path_put(path);
+
 out:
        return ret;
 }
@@ -947,6 +955,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
        __kernel_fsid_t __fsid, *fsid = NULL;
        u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
        unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
+       unsigned int obj_type;
        int ret;
 
        pr_debug("%s: fanotify_fd=%d flags=%x dfd=%d pathname=%p mask=%llx\n",
@@ -961,8 +970,13 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
 
        switch (mark_type) {
        case FAN_MARK_INODE:
+               obj_type = FSNOTIFY_OBJ_TYPE_INODE;
+               break;
        case FAN_MARK_MOUNT:
+               obj_type = FSNOTIFY_OBJ_TYPE_VFSMOUNT;
+               break;
        case FAN_MARK_FILESYSTEM:
+               obj_type = FSNOTIFY_OBJ_TYPE_SB;
                break;
        default:
                return -EINVAL;
@@ -1030,7 +1044,8 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
                goto fput_and_out;
        }
 
-       ret = fanotify_find_path(dfd, pathname, &path, flags);
+       ret = fanotify_find_path(dfd, pathname, &path, flags,
+                       (mask & ALL_FSNOTIFY_EVENTS), obj_type);
        if (ret)
                goto fput_and_out;
 
index 0b81517..107537a 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/poll.h>
 #include <linux/wait.h>
 #include <linux/memcontrol.h>
+#include <linux/security.h>
 
 #include "inotify.h"
 #include "../fdinfo.h"
@@ -331,7 +332,8 @@ static const struct file_operations inotify_fops = {
 /*
  * find_inode - resolve a user-given path to a specific inode
  */
-static int inotify_find_inode(const char __user *dirname, struct path *path, unsigned flags)
+static int inotify_find_inode(const char __user *dirname, struct path *path,
+                                               unsigned int flags, __u64 mask)
 {
        int error;
 
@@ -340,8 +342,15 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns
                return error;
        /* you can only watch an inode if you have read permissions on it */
        error = inode_permission(path->dentry->d_inode, MAY_READ);
+       if (error) {
+               path_put(path);
+               return error;
+       }
+       error = security_path_notify(path, mask,
+                               FSNOTIFY_OBJ_TYPE_INODE);
        if (error)
                path_put(path);
+
        return error;
 }
 
@@ -733,7 +742,8 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
        if (mask & IN_ONLYDIR)
                flags |= LOOKUP_DIRECTORY;
 
-       ret = inotify_find_inode(pathname, &path, flags);
+       ret = inotify_find_inode(pathname, &path, flags,
+                       (mask & IN_ALL_EVENTS));
        if (ret)
                goto fput_and_out;
 
index f7a30e0..18639c0 100644 (file)
@@ -386,7 +386,6 @@ static inline void put_cred(const struct cred *_cred)
 #define current_fsgid()        (current_cred_xxx(fsgid))
 #define current_cap()          (current_cred_xxx(cap_effective))
 #define current_user()         (current_cred_xxx(user))
-#define current_security()     (current_cred_xxx(security))
 
 extern struct user_namespace init_user_ns;
 #ifdef CONFIG_USER_NS
index df1318d..3fced58 100644 (file)
  *     Check for permission to change root directory.
  *     @path contains the path structure.
  *     Return 0 if permission is granted.
+ * @path_notify:
+ *     Check permissions before setting a watch on events as defined by @mask,
+ *     on an object at @path, whose type is defined by @obj_type.
  * @inode_readlink:
  *     Check the permission to read the symbolic link.
  *     @dentry contains the dentry structure for the file link.
@@ -1535,7 +1538,9 @@ union security_list_options {
        int (*path_chown)(const struct path *path, kuid_t uid, kgid_t gid);
        int (*path_chroot)(const struct path *path);
 #endif
-
+       /* Needed for inode based security check */
+       int (*path_notify)(const struct path *path, u64 mask,
+                               unsigned int obj_type);
        int (*inode_alloc_security)(struct inode *inode);
        void (*inode_free_security)(struct inode *inode);
        int (*inode_init_security)(struct inode *inode, struct inode *dir,
@@ -1860,6 +1865,8 @@ struct security_hook_heads {
        struct hlist_head path_chown;
        struct hlist_head path_chroot;
 #endif
+       /* Needed for inode based modules as well */
+       struct hlist_head path_notify;
        struct hlist_head inode_alloc_security;
        struct hlist_head inode_free_security;
        struct hlist_head inode_init_security;
index 5f7441a..ace6fdb 100644 (file)
@@ -259,7 +259,8 @@ int security_dentry_create_files_as(struct dentry *dentry, int mode,
                                        struct qstr *name,
                                        const struct cred *old,
                                        struct cred *new);
-
+int security_path_notify(const struct path *path, u64 mask,
+                                       unsigned int obj_type);
 int security_inode_alloc(struct inode *inode);
 void security_inode_free(struct inode *inode);
 int security_inode_init_security(struct inode *inode, struct inode *dir,
@@ -387,7 +388,6 @@ int security_ismaclabel(const char *name);
 int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
 int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
 void security_release_secctx(char *secdata, u32 seclen);
-
 void security_inode_invalidate_secctx(struct inode *inode);
 int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
 int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
@@ -621,6 +621,12 @@ static inline int security_move_mount(const struct path *from_path,
        return 0;
 }
 
+static inline int security_path_notify(const struct path *path, u64 mask,
+                               unsigned int obj_type)
+{
+       return 0;
+}
+
 static inline int security_inode_alloc(struct inode *inode)
 {
        return 0;
index 250ee2d..25ee5c7 100644 (file)
@@ -870,6 +870,12 @@ int security_move_mount(const struct path *from_path, const struct path *to_path
        return call_int_hook(move_mount, 0, from_path, to_path);
 }
 
+int security_path_notify(const struct path *path, u64 mask,
+                               unsigned int obj_type)
+{
+       return call_int_hook(path_notify, 0, path, mask, obj_type);
+}
+
 int security_inode_alloc(struct inode *inode)
 {
        int rc = lsm_inode_alloc(inode);
index 74dd46d..9625b99 100644 (file)
@@ -89,6 +89,8 @@
 #include <linux/kernfs.h>
 #include <linux/stringhash.h>  /* for hashlen_string() */
 #include <uapi/linux/mount.h>
+#include <linux/fsnotify.h>
+#include <linux/fanotify.h>
 
 #include "avc.h"
 #include "objsec.h"
@@ -3275,6 +3277,50 @@ static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
        return -EACCES;
 }
 
+static int selinux_path_notify(const struct path *path, u64 mask,
+                                               unsigned int obj_type)
+{
+       int ret;
+       u32 perm;
+
+       struct common_audit_data ad;
+
+       ad.type = LSM_AUDIT_DATA_PATH;
+       ad.u.path = *path;
+
+       /*
+        * Set permission needed based on the type of mark being set.
+        * Performs an additional check for sb watches.
+        */
+       switch (obj_type) {
+       case FSNOTIFY_OBJ_TYPE_VFSMOUNT:
+               perm = FILE__WATCH_MOUNT;
+               break;
+       case FSNOTIFY_OBJ_TYPE_SB:
+               perm = FILE__WATCH_SB;
+               ret = superblock_has_perm(current_cred(), path->dentry->d_sb,
+                                               FILESYSTEM__WATCH, &ad);
+               if (ret)
+                       return ret;
+               break;
+       case FSNOTIFY_OBJ_TYPE_INODE:
+               perm = FILE__WATCH;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* blocking watches require the file:watch_with_perm permission */
+       if (mask & (ALL_FSNOTIFY_PERM_EVENTS))
+               perm |= FILE__WATCH_WITH_PERM;
+
+       /* watches on read-like events need the file:watch_reads permission */
+       if (mask & (FS_ACCESS | FS_ACCESS_PERM | FS_CLOSE_NOWRITE))
+               perm |= FILE__WATCH_READS;
+
+       return path_has_perm(current_cred(), path, perm);
+}
+
 /*
  * Copy the inode security context value to the user.
  *
@@ -3403,7 +3449,7 @@ static int selinux_inode_copy_up_xattr(const char *name)
 static int selinux_kernfs_init_security(struct kernfs_node *kn_dir,
                                        struct kernfs_node *kn)
 {
-       const struct task_security_struct *tsec = current_security();
+       const struct task_security_struct *tsec = selinux_cred(current_cred());
        u32 parent_sid, newsid, clen;
        int rc;
        char *context;
@@ -6818,6 +6864,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid),
        LSM_HOOK_INIT(inode_copy_up, selinux_inode_copy_up),
        LSM_HOOK_INIT(inode_copy_up_xattr, selinux_inode_copy_up_xattr),
+       LSM_HOOK_INIT(path_notify, selinux_path_notify),
 
        LSM_HOOK_INIT(kernfs_init_security, selinux_kernfs_init_security),
 
index 201f7e5..32e9b03 100644 (file)
@@ -7,7 +7,8 @@
 
 #define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \
     "rename", "execute", "quotaon", "mounton", "audit_access", \
-    "open", "execmod"
+       "open", "execmod", "watch", "watch_mount", "watch_sb", \
+       "watch_with_perm", "watch_reads"
 
 #define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \
     "listen", "accept", "getopt", "setopt", "shutdown", "recvfrom",  \
@@ -60,7 +61,7 @@ struct security_class_mapping secclass_map[] = {
        { "filesystem",
          { "mount", "remount", "unmount", "getattr",
            "relabelfrom", "relabelto", "associate", "quotamod",
-           "quotaget", NULL } },
+           "quotaget", "watch", NULL } },
        { "file",
          { COMMON_FILE_PERMS,
            "execute_no_trans", "entrypoint", NULL } },
index 91c5395..586b7ab 100644 (file)
@@ -37,16 +37,6 @@ struct task_security_struct {
        u32 sockcreate_sid;     /* fscreate SID */
 };
 
-/*
- * get the subjective security ID of the current task
- */
-static inline u32 current_sid(void)
-{
-       const struct task_security_struct *tsec = current_security();
-
-       return tsec->sid;
-}
-
 enum label_initialized {
        LABEL_INVALID,          /* invalid or not initialized */
        LABEL_INITIALIZED,      /* initialized */
@@ -185,4 +175,14 @@ static inline struct ipc_security_struct *selinux_ipc(
        return ipc->security + selinux_blob_sizes.lbs_ipc;
 }
 
+/*
+ * get the subjective security ID of the current task
+ */
+static inline u32 current_sid(void)
+{
+       const struct task_security_struct *tsec = selinux_cred(current_cred());
+
+       return tsec->sid;
+}
+
 #endif /* _SELINUX_OBJSEC_H_ */
index 9cb83ee..e40fecd 100644 (file)
@@ -132,9 +132,9 @@ static void sel_netif_destroy(struct sel_netif *netif)
  */
 static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid)
 {
-       int ret;
+       int ret = 0;
        struct sel_netif *netif;
-       struct sel_netif *new = NULL;
+       struct sel_netif *new;
        struct net_device *dev;
 
        /* NOTE: we always use init's network namespace since we don't
@@ -151,32 +151,27 @@ static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid)
        netif = sel_netif_find(ns, ifindex);
        if (netif != NULL) {
                *sid = netif->nsec.sid;
-               ret = 0;
                goto out;
        }
-       new = kzalloc(sizeof(*new), GFP_ATOMIC);
-       if (new == NULL) {
-               ret = -ENOMEM;
-               goto out;
-       }
-       ret = security_netif_sid(&selinux_state, dev->name, &new->nsec.sid);
-       if (ret != 0)
-               goto out;
-       new->nsec.ns = ns;
-       new->nsec.ifindex = ifindex;
-       ret = sel_netif_insert(new);
+
+       ret = security_netif_sid(&selinux_state, dev->name, sid);
        if (ret != 0)
                goto out;
-       *sid = new->nsec.sid;
+       new = kzalloc(sizeof(*new), GFP_ATOMIC);
+       if (new) {
+               new->nsec.ns = ns;
+               new->nsec.ifindex = ifindex;
+               new->nsec.sid = *sid;
+               if (sel_netif_insert(new))
+                       kfree(new);
+       }
 
 out:
        spin_unlock_bh(&sel_netif_lock);
        dev_put(dev);
-       if (unlikely(ret)) {
+       if (unlikely(ret))
                pr_warn("SELinux: failure in %s(), unable to determine network interface label (%d)\n",
                        __func__, ifindex);
-               kfree(new);
-       }
        return ret;
 }
 
index cae1fca..9ab84ef 100644 (file)
@@ -189,9 +189,9 @@ static void sel_netnode_insert(struct sel_netnode *node)
  */
 static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
 {
-       int ret = -ENOMEM;
+       int ret;
        struct sel_netnode *node;
-       struct sel_netnode *new = NULL;
+       struct sel_netnode *new;
 
        spin_lock_bh(&sel_netnode_lock);
        node = sel_netnode_find(addr, family);
@@ -200,38 +200,36 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
                spin_unlock_bh(&sel_netnode_lock);
                return 0;
        }
+
        new = kzalloc(sizeof(*new), GFP_ATOMIC);
-       if (new == NULL)
-               goto out;
        switch (family) {
        case PF_INET:
                ret = security_node_sid(&selinux_state, PF_INET,
                                        addr, sizeof(struct in_addr), sid);
-               new->nsec.addr.ipv4 = *(__be32 *)addr;
+               if (new)
+                       new->nsec.addr.ipv4 = *(__be32 *)addr;
                break;
        case PF_INET6:
                ret = security_node_sid(&selinux_state, PF_INET6,
                                        addr, sizeof(struct in6_addr), sid);
-               new->nsec.addr.ipv6 = *(struct in6_addr *)addr;
+               if (new)
+                       new->nsec.addr.ipv6 = *(struct in6_addr *)addr;
                break;
        default:
                BUG();
                ret = -EINVAL;
        }
-       if (ret != 0)
-               goto out;
-
-       new->nsec.family = family;
-       new->nsec.sid = *sid;
-       sel_netnode_insert(new);
+       if (ret == 0 && new) {
+               new->nsec.family = family;
+               new->nsec.sid = *sid;
+               sel_netnode_insert(new);
+       } else
+               kfree(new);
 
-out:
        spin_unlock_bh(&sel_netnode_lock);
-       if (unlikely(ret)) {
+       if (unlikely(ret))
                pr_warn("SELinux: failure in %s(), unable to determine network node label\n",
                        __func__);
-               kfree(new);
-       }
        return ret;
 }
 
index 364b6d5..3f8b2c0 100644 (file)
@@ -137,9 +137,9 @@ static void sel_netport_insert(struct sel_netport *port)
  */
 static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)
 {
-       int ret = -ENOMEM;
+       int ret;
        struct sel_netport *port;
-       struct sel_netport *new = NULL;
+       struct sel_netport *new;
 
        spin_lock_bh(&sel_netport_lock);
        port = sel_netport_find(protocol, pnum);
@@ -148,25 +148,23 @@ static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)
                spin_unlock_bh(&sel_netport_lock);
                return 0;
        }
-       new = kzalloc(sizeof(*new), GFP_ATOMIC);
-       if (new == NULL)
-               goto out;
+
        ret = security_port_sid(&selinux_state, protocol, pnum, sid);
        if (ret != 0)
                goto out;
-
-       new->psec.port = pnum;
-       new->psec.protocol = protocol;
-       new->psec.sid = *sid;
-       sel_netport_insert(new);
+       new = kzalloc(sizeof(*new), GFP_ATOMIC);
+       if (new) {
+               new->psec.port = pnum;
+               new->psec.protocol = protocol;
+               new->psec.sid = *sid;
+               sel_netport_insert(new);
+       }
 
 out:
        spin_unlock_bh(&sel_netport_lock);
-       if (unlikely(ret)) {
+       if (unlikely(ret))
                pr_warn("SELinux: failure in %s(), unable to determine network port label\n",
                        __func__);
-               kfree(new);
-       }
        return ret;
 }
 
index f8efaa9..1260f5f 100644 (file)
@@ -177,6 +177,195 @@ static struct policydb_compat_info *policydb_lookup_compat(int version)
 }
 
 /*
+ * The following *_destroy functions are used to
+ * free any memory allocated for each kind of
+ * symbol data in the policy database.
+ */
+
+static int perm_destroy(void *key, void *datum, void *p)
+{
+       kfree(key);
+       kfree(datum);
+       return 0;
+}
+
+static int common_destroy(void *key, void *datum, void *p)
+{
+       struct common_datum *comdatum;
+
+       kfree(key);
+       if (datum) {
+               comdatum = datum;
+               hashtab_map(comdatum->permissions.table, perm_destroy, NULL);
+               hashtab_destroy(comdatum->permissions.table);
+       }
+       kfree(datum);
+       return 0;
+}
+
+static void constraint_expr_destroy(struct constraint_expr *expr)
+{
+       if (expr) {
+               ebitmap_destroy(&expr->names);
+               if (expr->type_names) {
+                       ebitmap_destroy(&expr->type_names->types);
+                       ebitmap_destroy(&expr->type_names->negset);
+                       kfree(expr->type_names);
+               }
+               kfree(expr);
+       }
+}
+
+static int cls_destroy(void *key, void *datum, void *p)
+{
+       struct class_datum *cladatum;
+       struct constraint_node *constraint, *ctemp;
+       struct constraint_expr *e, *etmp;
+
+       kfree(key);
+       if (datum) {
+               cladatum = datum;
+               hashtab_map(cladatum->permissions.table, perm_destroy, NULL);
+               hashtab_destroy(cladatum->permissions.table);
+               constraint = cladatum->constraints;
+               while (constraint) {
+                       e = constraint->expr;
+                       while (e) {
+                               etmp = e;
+                               e = e->next;
+                               constraint_expr_destroy(etmp);
+                       }
+                       ctemp = constraint;
+                       constraint = constraint->next;
+                       kfree(ctemp);
+               }
+
+               constraint = cladatum->validatetrans;
+               while (constraint) {
+                       e = constraint->expr;
+                       while (e) {
+                               etmp = e;
+                               e = e->next;
+                               constraint_expr_destroy(etmp);
+                       }
+                       ctemp = constraint;
+                       constraint = constraint->next;
+                       kfree(ctemp);
+               }
+               kfree(cladatum->comkey);
+       }
+       kfree(datum);
+       return 0;
+}
+
+static int role_destroy(void *key, void *datum, void *p)
+{
+       struct role_datum *role;
+
+       kfree(key);
+       if (datum) {
+               role = datum;
+               ebitmap_destroy(&role->dominates);
+               ebitmap_destroy(&role->types);
+       }
+       kfree(datum);
+       return 0;
+}
+
+static int type_destroy(void *key, void *datum, void *p)
+{
+       kfree(key);
+       kfree(datum);
+       return 0;
+}
+
+static int user_destroy(void *key, void *datum, void *p)
+{
+       struct user_datum *usrdatum;
+
+       kfree(key);
+       if (datum) {
+               usrdatum = datum;
+               ebitmap_destroy(&usrdatum->roles);
+               ebitmap_destroy(&usrdatum->range.level[0].cat);
+               ebitmap_destroy(&usrdatum->range.level[1].cat);
+               ebitmap_destroy(&usrdatum->dfltlevel.cat);
+       }
+       kfree(datum);
+       return 0;
+}
+
+static int sens_destroy(void *key, void *datum, void *p)
+{
+       struct level_datum *levdatum;
+
+       kfree(key);
+       if (datum) {
+               levdatum = datum;
+               if (levdatum->level)
+                       ebitmap_destroy(&levdatum->level->cat);
+               kfree(levdatum->level);
+       }
+       kfree(datum);
+       return 0;
+}
+
+static int cat_destroy(void *key, void *datum, void *p)
+{
+       kfree(key);
+       kfree(datum);
+       return 0;
+}
+
+static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =
+{
+       common_destroy,
+       cls_destroy,
+       role_destroy,
+       type_destroy,
+       user_destroy,
+       cond_destroy_bool,
+       sens_destroy,
+       cat_destroy,
+};
+
+static int filenametr_destroy(void *key, void *datum, void *p)
+{
+       struct filename_trans *ft = key;
+
+       kfree(ft->name);
+       kfree(key);
+       kfree(datum);
+       cond_resched();
+       return 0;
+}
+
+static int range_tr_destroy(void *key, void *datum, void *p)
+{
+       struct mls_range *rt = datum;
+
+       kfree(key);
+       ebitmap_destroy(&rt->level[0].cat);
+       ebitmap_destroy(&rt->level[1].cat);
+       kfree(datum);
+       cond_resched();
+       return 0;
+}
+
+static void ocontext_destroy(struct ocontext *c, int i)
+{
+       if (!c)
+               return;
+
+       context_destroy(&c->context[0]);
+       context_destroy(&c->context[1]);
+       if (i == OCON_ISID || i == OCON_FS ||
+           i == OCON_NETIF || i == OCON_FSUSE)
+               kfree(c->u.name);
+       kfree(c);
+}
+
+/*
  * Initialize the role table.
  */
 static int roles_init(struct policydb *p)
@@ -250,6 +439,7 @@ static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2)
 static u32 rangetr_hash(struct hashtab *h, const void *k)
 {
        const struct range_trans *key = k;
+
        return (key->source_type + (key->target_type << 3) +
                (key->target_class << 5)) & (h->size - 1);
 }
@@ -272,8 +462,6 @@ static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2)
        return v;
 }
 
-static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap);
-
 /*
  * Initialize a policy database structure.
  */
@@ -301,7 +489,8 @@ static int policydb_init(struct policydb *p)
        if (rc)
                goto out;
 
-       p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp, (1 << 10));
+       p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp,
+                                          (1 << 10));
        if (!p->filename_trans) {
                rc = -ENOMEM;
                goto out;
@@ -399,7 +588,7 @@ static int type_index(void *key, void *datum, void *datap)
                    || typdatum->bounds > p->p_types.nprim)
                        return -EINVAL;
                p->sym_val_to_name[SYM_TYPES][typdatum->value - 1] = key;
-               p->type_val_to_struct_array[typdatum->value - 1] = typdatum;
+               p->type_val_to_struct[typdatum->value - 1] = typdatum;
        }
 
        return 0;
@@ -477,9 +666,9 @@ static void hash_eval(struct hashtab *h, const char *hash_name)
        struct hashtab_info info;
 
        hashtab_stat(h, &info);
-       pr_debug("SELinux: %s:  %d entries and %d/%d buckets used, "
-              "longest chain length %d\n", hash_name, h->nel,
-              info.slots_used, h->size, info.max_chain_len);
+       pr_debug("SELinux: %s:  %d entries and %d/%d buckets used, longest chain length %d\n",
+                hash_name, h->nel, info.slots_used, h->size,
+                info.max_chain_len);
 }
 
 static void symtab_hash_eval(struct symtab *s)
@@ -541,10 +730,10 @@ static int policydb_index(struct policydb *p)
        if (!p->user_val_to_struct)
                return -ENOMEM;
 
-       p->type_val_to_struct_array = kvcalloc(p->p_types.nprim,
-                                              sizeof(*p->type_val_to_struct_array),
-                                              GFP_KERNEL);
-       if (!p->type_val_to_struct_array)
+       p->type_val_to_struct = kvcalloc(p->p_types.nprim,
+                                        sizeof(*p->type_val_to_struct),
+                                        GFP_KERNEL);
+       if (!p->type_val_to_struct)
                return -ENOMEM;
 
        rc = cond_init_bool_indexes(p);
@@ -568,193 +757,6 @@ out:
 }
 
 /*
- * The following *_destroy functions are used to
- * free any memory allocated for each kind of
- * symbol data in the policy database.
- */
-
-static int perm_destroy(void *key, void *datum, void *p)
-{
-       kfree(key);
-       kfree(datum);
-       return 0;
-}
-
-static int common_destroy(void *key, void *datum, void *p)
-{
-       struct common_datum *comdatum;
-
-       kfree(key);
-       if (datum) {
-               comdatum = datum;
-               hashtab_map(comdatum->permissions.table, perm_destroy, NULL);
-               hashtab_destroy(comdatum->permissions.table);
-       }
-       kfree(datum);
-       return 0;
-}
-
-static void constraint_expr_destroy(struct constraint_expr *expr)
-{
-       if (expr) {
-               ebitmap_destroy(&expr->names);
-               if (expr->type_names) {
-                       ebitmap_destroy(&expr->type_names->types);
-                       ebitmap_destroy(&expr->type_names->negset);
-                       kfree(expr->type_names);
-               }
-               kfree(expr);
-       }
-}
-
-static int cls_destroy(void *key, void *datum, void *p)
-{
-       struct class_datum *cladatum;
-       struct constraint_node *constraint, *ctemp;
-       struct constraint_expr *e, *etmp;
-
-       kfree(key);
-       if (datum) {
-               cladatum = datum;
-               hashtab_map(cladatum->permissions.table, perm_destroy, NULL);
-               hashtab_destroy(cladatum->permissions.table);
-               constraint = cladatum->constraints;
-               while (constraint) {
-                       e = constraint->expr;
-                       while (e) {
-                               etmp = e;
-                               e = e->next;
-                               constraint_expr_destroy(etmp);
-                       }
-                       ctemp = constraint;
-                       constraint = constraint->next;
-                       kfree(ctemp);
-               }
-
-               constraint = cladatum->validatetrans;
-               while (constraint) {
-                       e = constraint->expr;
-                       while (e) {
-                               etmp = e;
-                               e = e->next;
-                               constraint_expr_destroy(etmp);
-                       }
-                       ctemp = constraint;
-                       constraint = constraint->next;
-                       kfree(ctemp);
-               }
-               kfree(cladatum->comkey);
-       }
-       kfree(datum);
-       return 0;
-}
-
-static int role_destroy(void *key, void *datum, void *p)
-{
-       struct role_datum *role;
-
-       kfree(key);
-       if (datum) {
-               role = datum;
-               ebitmap_destroy(&role->dominates);
-               ebitmap_destroy(&role->types);
-       }
-       kfree(datum);
-       return 0;
-}
-
-static int type_destroy(void *key, void *datum, void *p)
-{
-       kfree(key);
-       kfree(datum);
-       return 0;
-}
-
-static int user_destroy(void *key, void *datum, void *p)
-{
-       struct user_datum *usrdatum;
-
-       kfree(key);
-       if (datum) {
-               usrdatum = datum;
-               ebitmap_destroy(&usrdatum->roles);
-               ebitmap_destroy(&usrdatum->range.level[0].cat);
-               ebitmap_destroy(&usrdatum->range.level[1].cat);
-               ebitmap_destroy(&usrdatum->dfltlevel.cat);
-       }
-       kfree(datum);
-       return 0;
-}
-
-static int sens_destroy(void *key, void *datum, void *p)
-{
-       struct level_datum *levdatum;
-
-       kfree(key);
-       if (datum) {
-               levdatum = datum;
-               if (levdatum->level)
-                       ebitmap_destroy(&levdatum->level->cat);
-               kfree(levdatum->level);
-       }
-       kfree(datum);
-       return 0;
-}
-
-static int cat_destroy(void *key, void *datum, void *p)
-{
-       kfree(key);
-       kfree(datum);
-       return 0;
-}
-
-static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =
-{
-       common_destroy,
-       cls_destroy,
-       role_destroy,
-       type_destroy,
-       user_destroy,
-       cond_destroy_bool,
-       sens_destroy,
-       cat_destroy,
-};
-
-static int filenametr_destroy(void *key, void *datum, void *p)
-{
-       struct filename_trans *ft = key;
-       kfree(ft->name);
-       kfree(key);
-       kfree(datum);
-       cond_resched();
-       return 0;
-}
-
-static int range_tr_destroy(void *key, void *datum, void *p)
-{
-       struct mls_range *rt = datum;
-       kfree(key);
-       ebitmap_destroy(&rt->level[0].cat);
-       ebitmap_destroy(&rt->level[1].cat);
-       kfree(datum);
-       cond_resched();
-       return 0;
-}
-
-static void ocontext_destroy(struct ocontext *c, int i)
-{
-       if (!c)
-               return;
-
-       context_destroy(&c->context[0]);
-       context_destroy(&c->context[1]);
-       if (i == OCON_ISID || i == OCON_FS ||
-           i == OCON_NETIF || i == OCON_FSUSE)
-               kfree(c->u.name);
-       kfree(c);
-}
-
-/*
  * Free any memory allocated by a policy database structure.
  */
 void policydb_destroy(struct policydb *p)
@@ -777,7 +779,7 @@ void policydb_destroy(struct policydb *p)
        kfree(p->class_val_to_struct);
        kfree(p->role_val_to_struct);
        kfree(p->user_val_to_struct);
-       kvfree(p->type_val_to_struct_array);
+       kvfree(p->type_val_to_struct);
 
        avtab_destroy(&p->te_avtab);
 
@@ -1722,7 +1724,7 @@ static int type_bounds_sanity_check(void *key, void *datum, void *datap)
                        return -EINVAL;
                }
 
-               upper = p->type_val_to_struct_array[upper->bounds - 1];
+               upper = p->type_val_to_struct[upper->bounds - 1];
                BUG_ON(!upper);
 
                if (upper->attribute) {
index fcc6366..162d0e7 100644 (file)
@@ -253,7 +253,7 @@ struct policydb {
        struct class_datum **class_val_to_struct;
        struct role_datum **role_val_to_struct;
        struct user_datum **user_val_to_struct;
-       struct type_datum **type_val_to_struct_array;
+       struct type_datum **type_val_to_struct;
 
        /* type enforcement access vectors and transitions */
        struct avtab te_avtab;
index d61563a..3a29e7c 100644 (file)
@@ -542,13 +542,13 @@ static void type_attribute_bounds_av(struct policydb *policydb,
        struct type_datum *target;
        u32 masked = 0;
 
-       source = policydb->type_val_to_struct_array[scontext->type - 1];
+       source = policydb->type_val_to_struct[scontext->type - 1];
        BUG_ON(!source);
 
        if (!source->bounds)
                return;
 
-       target = policydb->type_val_to_struct_array[tcontext->type - 1];
+       target = policydb->type_val_to_struct[tcontext->type - 1];
        BUG_ON(!target);
 
        memset(&lo_avd, 0, sizeof(lo_avd));
@@ -891,7 +891,7 @@ int security_bounded_transition(struct selinux_state *state,
 
        index = new_context->type;
        while (true) {
-               type = policydb->type_val_to_struct_array[index - 1];
+               type = policydb->type_val_to_struct[index - 1];
                BUG_ON(!type);
 
                /* not bounded anymore */
index 1f0a6ea..7d49994 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/spinlock.h>
-#include <linux/atomic.h>
+#include <asm/barrier.h>
 #include "flask.h"
 #include "security.h"
 #include "sidtab.h"
@@ -23,14 +23,14 @@ int sidtab_init(struct sidtab *s)
 
        memset(s->roots, 0, sizeof(s->roots));
 
+       /* max count is SIDTAB_MAX so valid index is always < SIDTAB_MAX */
        for (i = 0; i < SIDTAB_RCACHE_SIZE; i++)
-               atomic_set(&s->rcache[i], -1);
+               s->rcache[i] = SIDTAB_MAX;
 
        for (i = 0; i < SECINITSID_NUM; i++)
                s->isids[i].set = 0;
 
-       atomic_set(&s->count, 0);
-
+       s->count = 0;
        s->convert = NULL;
 
        spin_lock_init(&s->lock);
@@ -130,14 +130,12 @@ static struct context *sidtab_do_lookup(struct sidtab *s, u32 index, int alloc)
 
 static struct context *sidtab_lookup(struct sidtab *s, u32 index)
 {
-       u32 count = (u32)atomic_read(&s->count);
+       /* read entries only after reading count */
+       u32 count = smp_load_acquire(&s->count);
 
        if (index >= count)
                return NULL;
 
-       /* read entries after reading count */
-       smp_rmb();
-
        return sidtab_do_lookup(s, index, 0);
 }
 
@@ -210,10 +208,10 @@ static int sidtab_find_context(union sidtab_entry_inner entry,
 static void sidtab_rcache_update(struct sidtab *s, u32 index, u32 pos)
 {
        while (pos > 0) {
-               atomic_set(&s->rcache[pos], atomic_read(&s->rcache[pos - 1]));
+               WRITE_ONCE(s->rcache[pos], READ_ONCE(s->rcache[pos - 1]));
                --pos;
        }
-       atomic_set(&s->rcache[0], (int)index);
+       WRITE_ONCE(s->rcache[0], index);
 }
 
 static void sidtab_rcache_push(struct sidtab *s, u32 index)
@@ -227,14 +225,14 @@ static int sidtab_rcache_search(struct sidtab *s, struct context *context,
        u32 i;
 
        for (i = 0; i < SIDTAB_RCACHE_SIZE; i++) {
-               int v = atomic_read(&s->rcache[i]);
+               u32 v = READ_ONCE(s->rcache[i]);
 
-               if (v < 0)
+               if (v >= SIDTAB_MAX)
                        continue;
 
-               if (context_cmp(sidtab_do_lookup(s, (u32)v, 0), context)) {
-                       sidtab_rcache_update(s, (u32)v, i);
-                       *index = (u32)v;
+               if (context_cmp(sidtab_do_lookup(s, v, 0), context)) {
+                       sidtab_rcache_update(s, v, i);
+                       *index = v;
                        return 0;
                }
        }
@@ -245,8 +243,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
                                 u32 *index)
 {
        unsigned long flags;
-       u32 count = (u32)atomic_read(&s->count);
-       u32 count_locked, level, pos;
+       u32 count, count_locked, level, pos;
        struct sidtab_convert_params *convert;
        struct context *dst, *dst_convert;
        int rc;
@@ -255,11 +252,10 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
        if (rc == 0)
                return 0;
 
+       /* read entries only after reading count */
+       count = smp_load_acquire(&s->count);
        level = sidtab_level_from_count(count);
 
-       /* read entries after reading count */
-       smp_rmb();
-
        pos = 0;
        rc = sidtab_find_context(s->roots[level], &pos, count, level,
                                 context, index);
@@ -272,7 +268,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
        spin_lock_irqsave(&s->lock, flags);
 
        convert = s->convert;
-       count_locked = (u32)atomic_read(&s->count);
+       count_locked = s->count;
        level = sidtab_level_from_count(count_locked);
 
        /* if count has changed before we acquired the lock, then catch up */
@@ -320,7 +316,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
                }
 
                /* at this point we know the insert won't fail */
-               atomic_set(&convert->target->count, count + 1);
+               convert->target->count = count + 1;
        }
 
        if (context->len)
@@ -331,9 +327,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
        *index = count;
 
        /* write entries before writing new count */
-       smp_wmb();
-
-       atomic_set(&s->count, count + 1);
+       smp_store_release(&s->count, count + 1);
 
        rc = 0;
 out_unlock:
@@ -423,7 +417,7 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
                return -EBUSY;
        }
 
-       count = (u32)atomic_read(&s->count);
+       count = s->count;
        level = sidtab_level_from_count(count);
 
        /* allocate last leaf in the new sidtab (to avoid race with
@@ -436,7 +430,7 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
        }
 
        /* set count in case no new entries are added during conversion */
-       atomic_set(&params->target->count, count);
+       params->target->count = count;
 
        /* enable live convert of new entries */
        s->convert = params;
index bbd5c0d..1f47631 100644 (file)
@@ -40,8 +40,8 @@ union sidtab_entry_inner {
 #define SIDTAB_LEAF_ENTRIES \
        (SIDTAB_NODE_ALLOC_SIZE / sizeof(struct sidtab_entry_leaf))
 
-#define SIDTAB_MAX_BITS 31 /* limited to INT_MAX due to atomic_t range */
-#define SIDTAB_MAX (((u32)1 << SIDTAB_MAX_BITS) - 1)
+#define SIDTAB_MAX_BITS 32
+#define SIDTAB_MAX U32_MAX
 /* ensure enough tree levels for SIDTAB_MAX entries */
 #define SIDTAB_MAX_LEVEL \
        DIV_ROUND_UP(SIDTAB_MAX_BITS - size_to_shift(SIDTAB_LEAF_ENTRIES), \
@@ -69,13 +69,22 @@ struct sidtab_convert_params {
 #define SIDTAB_RCACHE_SIZE 3
 
 struct sidtab {
+       /*
+        * lock-free read access only for as many items as a prior read of
+        * 'count'
+        */
        union sidtab_entry_inner roots[SIDTAB_MAX_LEVEL + 1];
-       atomic_t count;
+       /*
+        * access atomically via {READ|WRITE}_ONCE(); only increment under
+        * spinlock
+        */
+       u32 count;
+       /* access only under spinlock */
        struct sidtab_convert_params *convert;
        spinlock_t lock;
 
-       /* reverse lookup cache */
-       atomic_t rcache[SIDTAB_RCACHE_SIZE];
+       /* reverse lookup cache - access atomically via {READ|WRITE}_ONCE() */
+       u32 rcache[SIDTAB_RCACHE_SIZE];
 
        /* index == SID - 1 (no entry for SECSID_NULL) */
        struct sidtab_isid_entry isids[SECINITSID_NUM];