]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
UBUNTU: SAUCE: apparmor: fix lock ordering for mkdir
authorJohn Johansen <john.johansen@canonical.com>
Thu, 8 Dec 2016 02:59:07 +0000 (18:59 -0800)
committerTim Gardner <tim.gardner@canonical.com>
Mon, 20 Feb 2017 03:57:58 +0000 (20:57 -0700)
There is a lock inversion that can result in a dead lock when profile
replacements are racing with dir creation for a namespace in apparmorfs.

BugLink: http://bugs.launchpad.net/bugs/1645037
Signed-off-by: John Johansen <john.johansen@canonical.com>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
security/apparmor/apparmorfs.c
security/apparmor/include/policy_ns.h
security/apparmor/policy_ns.c

index 2ded4188773127ec6a044ba9cf5e88f82c1f068a..290c20ee52c00c68333e8899e5b5d359f23e8056 100644 (file)
@@ -1050,6 +1050,7 @@ static int ns_mkdir_op(struct inode *dir, struct dentry *dentry, umode_t mode)
         */
        inode_unlock(dir);
        error = securityfs_pin_fs();
+       mutex_lock(&parent->lock);
        inode_lock_nested(dir, I_MUTEX_PARENT);
        if (error)
                goto out;
@@ -1059,7 +1060,8 @@ static int ns_mkdir_op(struct inode *dir, struct dentry *dentry, umode_t mode)
        if (error)
                goto out_pin;
 
-       ns = aa_create_ns(parent, ACCESS_ONCE(dentry->d_name.name), dentry);
+       ns = __aa_find_or_create_ns(parent, READ_ONCE(dentry->d_name.name),
+                                   dentry);
        if (IS_ERR(ns)) {
                error = PTR_ERR(ns);
                ns = NULL;
@@ -1069,6 +1071,7 @@ static int ns_mkdir_op(struct inode *dir, struct dentry *dentry, umode_t mode)
 out_pin:
        securityfs_release_fs();
 out:
+       mutex_unlock(&parent->lock);
        aa_put_ns(parent);
 
        return error;
index 4c16c9acdbcfb78c3e5e910981a577635bcd83a2..2cf2d3f1295ac71398d94b95fda22d5a82d8af6b 100644 (file)
@@ -88,8 +88,8 @@ void aa_free_ns_kref(struct kref *kref);
 
 struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name);
 struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n);
-struct aa_ns *aa_create_ns(struct aa_ns *parent, const char *name,
-                          struct dentry *dir);
+struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name,
+                                    struct dentry *dir);
 struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name);
 void __aa_remove_ns(struct aa_ns *ns);
 
index 88043fcfad8e55eb9c1841c82c86bbb812f84e45..10ccf02d551bae40bc49c98cb3f2073bef38b889 100644 (file)
@@ -225,12 +225,13 @@ static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name,
  *
  * Returns: the a refcounted ns that has been add or an ERR_PTR
  */
-struct aa_ns *aa_create_ns(struct aa_ns *parent, const char *name,
-                          struct dentry *dir)
+struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name,
+                                    struct dentry *dir)
 {
        struct aa_ns *ns;
 
-       mutex_lock(&parent->lock);
+       AA_BUG(!mutex_is_locked(&parent->lock));
+
        /* try and find the specified ns */
        /* released by caller */
        ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name));
@@ -238,7 +239,6 @@ struct aa_ns *aa_create_ns(struct aa_ns *parent, const char *name,
                ns = __aa_create_ns(parent, name, dir);
        else
                ns = ERR_PTR(-EEXIST);
-       mutex_unlock(&parent->lock);
 
        /* return ref */
        return ns;