From: Roberto Sassu <roberto.sassu@xxxxxxxxxx> 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@xxxxxxxxxx> --- 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