diff mbox

[10/14] UBUNTU: SAUCE: apparmor: fix lock ordering for mkdir

Message ID 20170201090606.22422-11-john.johansen@canonical.com
State New
Headers show

Commit Message

John Johansen Feb. 1, 2017, 9:06 a.m. UTC
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>
---
 security/apparmor/apparmorfs.c        | 5 ++++-
 security/apparmor/include/policy_ns.h | 4 ++--
 security/apparmor/policy_ns.c         | 8 ++++----
 3 files changed, 10 insertions(+), 7 deletions(-)
diff mbox

Patch

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index ee9a780..3bd61d2 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 4c16c9a..2cf2d3f 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 88043fc..10ccf02 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;