From: John Johansen Date: Thu, 8 Dec 2016 02:59:07 +0000 (-0800) Subject: UBUNTU: SAUCE: apparmor: fix lock ordering for mkdir X-Git-Tag: Ubuntu-4.10.0-9.11~44 X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;h=21c9d3b055706c4b0240e2b9ad88629878e46c83;p=mirror_ubuntu-zesty-kernel.git UBUNTU: SAUCE: apparmor: fix lock ordering for mkdir 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 Signed-off-by: Tim Gardner --- diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 2ded41887731..290c20ee52c0 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -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; diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index 4c16c9acdbcf..2cf2d3f1295a 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -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); diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 88043fcfad8e..10ccf02d551b 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -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;