All of lore.kernel.org
 help / color / mirror / Atom feed
From: Roberto Sassu <roberto.sassu@huaweicloud.com>
To: corbet@lwn.net, zohar@linux.ibm.com, dmitry.kasatkin@gmail.com,
	eric.snowberg@oracle.com, paul@paul-moore.com, jmorris@namei.org,
	serge@hallyn.com
Cc: linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-integrity@vger.kernel.org,
	linux-security-module@vger.kernel.org, wufan@linux.microsoft.com,
	pbrobinson@gmail.com, zbyszek@in.waw.pl, hch@lst.de,
	mjg59@srcf.ucam.org, pmatilai@redhat.com, jannh@google.com,
	dhowells@redhat.com, jikos@kernel.org, mkoutny@suse.com,
	ppavlu@suse.com, petr.vorel@gmail.com, mzerqung@0pointer.de,
	kgold@linux.ibm.com, Roberto Sassu <roberto.sassu@huawei.com>
Subject: [RFC][PATCH v2 2/9] ima: Nest iint mutex for DIGEST_LIST_CHECK hook
Date: Mon, 15 Apr 2024 18:10:37 +0200	[thread overview]
Message-ID: <20240415161044.2572438-3-roberto.sassu@huaweicloud.com> (raw)
In-Reply-To: <20240415161044.2572438-1-roberto.sassu@huaweicloud.com>

From: Roberto Sassu <roberto.sassu@huawei.com>

Invoking digest_cache_get() inside the iint->mutex critical region can
cause deadlocks due to the fact that IMA can be recursively invoked for
reading the digest list. The deadlock would occur if the digest_cache LSM
attempts to read the same inode that is already locked by IMA.

However, since the digest_cache LSM makes sure that the above situation
never happens, as it checks the inodes, it is safe to call
digest_cache_get() inside the critical region and nest the iint->mutex
when the DIGEST_LIST_CHECK hook is executed.

Add a lockdep subclass to the iint->mutex, that is 0 if the IMA hook
executed is not DIGEST_LIST_CHECK, and 1 when it is. Since lockdep allows
nesting with higher classes and subclasses, that effectively eliminates the
warning about the unsafe lock.

Pass the new lockdep subclass (nested variable) from ima_inode_get() to
ima_iint_init_always() and ima_iint_lockdep_annotate().

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 security/integrity/ima/ima.h      |  2 +-
 security/integrity/ima/ima_iint.c | 17 +++++++++++------
 security/integrity/ima/ima_main.c |  6 +++---
 3 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index cea4517e73ab..c9140a57b591 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -216,7 +216,7 @@ static inline void ima_inode_set_iint(const struct inode *inode,
 }
 
 struct ima_iint_cache *ima_iint_find(struct inode *inode);
-struct ima_iint_cache *ima_inode_get(struct inode *inode);
+struct ima_iint_cache *ima_inode_get(struct inode *inode, bool nested);
 void ima_inode_free(struct inode *inode);
 void __init ima_iintcache_init(void);
 
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
index e7c9c216c1c6..c98a30815c8a 100644
--- a/security/integrity/ima/ima_iint.c
+++ b/security/integrity/ima/ima_iint.c
@@ -39,9 +39,12 @@ struct ima_iint_cache *ima_iint_find(struct inode *inode)
  * files both on overlayfs and on underlying fs, we need to annotate the iint
  * mutex to avoid lockdep false positives related to IMA + overlayfs.
  * See ovl_lockdep_annotate_inode_mutex_key() for more details.
+ *
+ * In addition to that, safely ignored nested locks for digest lists, since
+ * the digest_cache LSM prevents circular dependencies.
  */
 static inline void ima_iint_lockdep_annotate(struct ima_iint_cache *iint,
-					     struct inode *inode)
+					     struct inode *inode, bool nested)
 {
 #ifdef CONFIG_LOCKDEP
 	static struct lock_class_key ima_iint_mutex_key[IMA_MAX_NESTING];
@@ -51,12 +54,13 @@ static inline void ima_iint_lockdep_annotate(struct ima_iint_cache *iint,
 	if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING))
 		depth = 0;
 
-	lockdep_set_class(&iint->mutex, &ima_iint_mutex_key[depth]);
+	lockdep_set_class_and_subclass(&iint->mutex, &ima_iint_mutex_key[depth],
+				       nested);
 #endif
 }
 
 static void ima_iint_init_always(struct ima_iint_cache *iint,
-				 struct inode *inode)
+				 struct inode *inode, bool nested)
 {
 	iint->ima_hash = NULL;
 	iint->version = 0;
@@ -69,7 +73,7 @@ static void ima_iint_init_always(struct ima_iint_cache *iint,
 	iint->ima_creds_status = INTEGRITY_UNKNOWN;
 	iint->measured_pcrs = 0;
 	mutex_init(&iint->mutex);
-	ima_iint_lockdep_annotate(iint, inode);
+	ima_iint_lockdep_annotate(iint, inode, nested);
 }
 
 static void ima_iint_free(struct ima_iint_cache *iint)
@@ -82,13 +86,14 @@ static void ima_iint_free(struct ima_iint_cache *iint)
 /**
  * ima_inode_get - Find or allocate an iint associated with an inode
  * @inode: Pointer to the inode
+ * @nested: Whether or not the iint->mutex lock can be nested
  *
  * Find an iint associated with an inode, and allocate a new one if not found.
  * Caller must lock i_mutex.
  *
  * Return: An iint on success, NULL on error.
  */
-struct ima_iint_cache *ima_inode_get(struct inode *inode)
+struct ima_iint_cache *ima_inode_get(struct inode *inode, bool nested)
 {
 	struct ima_iint_cache *iint;
 
@@ -100,7 +105,7 @@ struct ima_iint_cache *ima_inode_get(struct inode *inode)
 	if (!iint)
 		return NULL;
 
-	ima_iint_init_always(iint, inode);
+	ima_iint_init_always(iint, inode, nested);
 
 	inode->i_flags |= S_IMA;
 	ima_inode_set_iint(inode, iint);
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 780627b0cde7..18285fc8ac07 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -248,7 +248,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
 	inode_lock(inode);
 
 	if (action) {
-		iint = ima_inode_get(inode);
+		iint = ima_inode_get(inode, func == DIGEST_LIST_CHECK);
 		if (!iint)
 			rc = -ENOMEM;
 	}
@@ -699,7 +699,7 @@ static void ima_post_create_tmpfile(struct mnt_idmap *idmap,
 		return;
 
 	/* Nothing to do if we can't allocate memory */
-	iint = ima_inode_get(inode);
+	iint = ima_inode_get(inode, false);
 	if (!iint)
 		return;
 
@@ -731,7 +731,7 @@ static void ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
 		return;
 
 	/* Nothing to do if we can't allocate memory */
-	iint = ima_inode_get(inode);
+	iint = ima_inode_get(inode, false);
 	if (!iint)
 		return;
 
-- 
2.34.1


  parent reply	other threads:[~2024-04-15 16:11 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-15 16:10 [RFC][PATCH v2 0/9] ima: Integrate with digest_cache LSM Roberto Sassu
2024-04-15 16:10 ` [RFC][PATCH v2 1/9] ima: Introduce hook DIGEST_LIST_CHECK Roberto Sassu
2024-04-15 16:10 ` Roberto Sassu [this message]
2024-04-15 16:10 ` [RFC][PATCH v2 3/9] ima: Add digest_cache policy keyword Roberto Sassu
2024-04-15 16:10 ` [RFC][PATCH v2 4/9] ima: Add digest_cache_measure/appraise boot-time built-in policies Roberto Sassu
2024-04-15 16:10 ` [RFC][PATCH v2 5/9] ima: Modify existing boot-time built-in policies with digest cache policies Roberto Sassu
2024-04-15 16:10 ` [RFC][PATCH v2 6/9] ima: Store allowed usage in digest cache based on integrity metadata flags Roberto Sassu
2024-04-15 16:10 ` [RFC][PATCH v2 7/9] ima: Use digest caches for measurement Roberto Sassu
2024-04-15 16:10 ` [RFC][PATCH v2 8/9] ima: Use digest caches for appraisal Roberto Sassu
2024-04-15 16:10 ` [RFC][PATCH v2 9/9] ima: Register to the digest_cache LSM notifier and process events Roberto Sassu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240415161044.2572438-3-roberto.sassu@huaweicloud.com \
    --to=roberto.sassu@huaweicloud.com \
    --cc=corbet@lwn.net \
    --cc=dhowells@redhat.com \
    --cc=dmitry.kasatkin@gmail.com \
    --cc=eric.snowberg@oracle.com \
    --cc=hch@lst.de \
    --cc=jannh@google.com \
    --cc=jikos@kernel.org \
    --cc=jmorris@namei.org \
    --cc=kgold@linux.ibm.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-integrity@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=mjg59@srcf.ucam.org \
    --cc=mkoutny@suse.com \
    --cc=mzerqung@0pointer.de \
    --cc=paul@paul-moore.com \
    --cc=pbrobinson@gmail.com \
    --cc=petr.vorel@gmail.com \
    --cc=pmatilai@redhat.com \
    --cc=ppavlu@suse.com \
    --cc=roberto.sassu@huawei.com \
    --cc=serge@hallyn.com \
    --cc=wufan@linux.microsoft.com \
    --cc=zbyszek@in.waw.pl \
    --cc=zohar@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.