lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Mon,  4 Jul 2022 16:42:49 +0100
From:   Mike Leach <mike.leach@...aro.org>
To:     linux-arm-kernel@...ts.infradead.org, coresight@...ts.linaro.org,
        linux-kernel@...r.kernel.org
Cc:     mathieu.poirier@...aro.org, suzuki.poulose@....com,
        leo.yan@...aro.org, acme@...nel.org, james.clark@....com,
        Mike Leach <mike.leach@...aro.org>,
        Joel Becker <jlbec@...lplan.org>,
        Christoph Hellwig <hch@....de>
Subject: [PATCH v4 7/7] configfs: Fix LOCKDEP nesting issues with fragment semaphores

CoreSight uses configfs to represent the user interface to programmed
configurations, which can be loaded and unloaded dynamically via configfs.

These add and remove configurations using register/unregister group
calls.

It has been found that if CONFIG_LOCKDEP is enabled, then it appears to
be confused by the nesting inherent in the fragment semaphores used
by groups and the underlying subsystem.

This patch sets up a mechanism to use separate classes for the fragment
semaphores, in a similar way to that already in place to fix nesting
issues with the i_mutexes.

Cc: Joel Becker <jlbec@...lplan.org>
Cc: Christoph Hellwig <hch@....de>
Signed-off-by: Mike Leach <mike.leach@...aro.org>
---
 fs/configfs/configfs_internal.h |  3 +++
 fs/configfs/dir.c               | 45 +++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
index c0395363eab9..736c74ec4b7a 100644
--- a/fs/configfs/configfs_internal.h
+++ b/fs/configfs/configfs_internal.h
@@ -22,6 +22,9 @@ struct configfs_fragment {
 	atomic_t frag_count;
 	struct rw_semaphore frag_sem;
 	bool frag_dead;
+#ifdef CONFIG_LOCKDEP
+	int frag_depth;
+#endif
 };
 
 void put_fragment(struct configfs_fragment *);
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index d1f9d2632202..6ecd8961afc3 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -133,6 +133,41 @@ configfs_adjust_dir_dirent_depth_after_populate(struct configfs_dirent *sd)
 	sd->s_depth = -1;
 }
 
+/* fragment semaphore needs some lockdep handling */
+static struct lock_class_key default_frag_class[MAX_LOCK_DEPTH];
+
+/*
+ * Set the lockdep depth for a new fragment based on the parent frag depth.
+ * Called from register_subsystem() with NULL parent group to set root subsystem
+ * depth which defaults to 0 in a new fragment, and from register_group() with the
+ * parent group to set a new group fragment based on the parent fragment depth.
+ *
+ * Prevents lockdep getting upset on the unregister_group() call if it cannot
+ * understand the hierarchy of fragments.
+ */
+static void configfs_adjust_frag_depth(struct configfs_fragment *frag,
+				       struct config_group *parent_group)
+{
+	struct configfs_dirent *parent_dirent;
+
+	if (parent_group) {
+		// find parent frag
+		parent_dirent = parent_group->cg_item.ci_dentry->d_fsdata;
+		frag->frag_depth = parent_dirent->s_frag->frag_depth + 1;
+	}
+
+	if (frag->frag_depth < ARRAY_SIZE(default_frag_class)) {
+		lockdep_set_class(&frag->frag_sem,
+				  &default_frag_class[frag->frag_depth]);
+	} else {
+		/*
+		 * In practice the maximum level of locking depth is
+		 * already reached. Just inform about possible reasons.
+		 */
+		pr_info("Too many levels of fragments for the locking correctness validator.\n");
+	}
+}
+
 #else /* CONFIG_LOCKDEP */
 
 static void configfs_init_dirent_depth(struct configfs_dirent *sd)
@@ -154,6 +189,11 @@ configfs_adjust_dir_dirent_depth_after_populate(struct configfs_dirent *sd)
 {
 }
 
+static void configfs_adjust_frag_depth(struct configfs_fragment *frag,
+				       struct config_group *parent_group)
+{
+}
+
 #endif /* CONFIG_LOCKDEP */
 
 static struct configfs_fragment *new_fragment(void)
@@ -165,6 +205,9 @@ static struct configfs_fragment *new_fragment(void)
 		atomic_set(&p->frag_count, 1);
 		init_rwsem(&p->frag_sem);
 		p->frag_dead = false;
+#ifdef CONFIG_LOCKDEP
+		p->frag_depth = 0;
+#endif
 	}
 	return p;
 }
@@ -1742,6 +1785,7 @@ int configfs_register_group(struct config_group *parent_group,
 	parent = parent_group->cg_item.ci_dentry;
 
 	inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
+	configfs_adjust_frag_depth(frag, parent_group);
 	ret = create_default_group(parent_group, group, frag);
 	if (ret)
 		goto err_out;
@@ -1872,6 +1916,7 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
 	mutex_unlock(&configfs_subsystem_mutex);
 
 	inode_lock_nested(d_inode(root), I_MUTEX_PARENT);
+	configfs_adjust_frag_depth(frag, NULL);
 
 	err = -ENOMEM;
 	dentry = d_alloc_name(root, group->cg_item.ci_name);
-- 
2.17.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ