mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:46:16 +00:00
63dff3e488
The LSM framework has an existing inode_free_security() hook which is used by LSMs that manage state associated with an inode, but due to the use of RCU to protect the inode, special care must be taken to ensure that the LSMs do not fully release the inode state until it is safe from a RCU perspective. This patch implements a new inode_free_security_rcu() implementation hook which is called when it is safe to free the LSM's internal inode state. Unfortunately, this new hook does not have access to the inode itself as it may already be released, so the existing inode_free_security() hook is retained for those LSMs which require access to the inode. Cc: stable@vger.kernel.org Reported-by: syzbot+5446fbf332b0602ede0b@syzkaller.appspotmail.com Closes: https://lore.kernel.org/r/00000000000076ba3b0617f65cc8@google.com Signed-off-by: Paul Moore <paul@paul-moore.com>
139 lines
3.4 KiB
C
139 lines
3.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2008 IBM Corporation
|
|
*
|
|
* Authors:
|
|
* Mimi Zohar <zohar@us.ibm.com>
|
|
*
|
|
* File: ima_iint.c
|
|
* - implements the IMA hook: ima_inode_free
|
|
* - cache integrity information in the inode security blob
|
|
*/
|
|
#include <linux/slab.h>
|
|
|
|
#include "ima.h"
|
|
|
|
static struct kmem_cache *ima_iint_cache __ro_after_init;
|
|
|
|
/**
|
|
* ima_iint_find - Return the iint associated with an inode
|
|
* @inode: Pointer to the inode
|
|
*
|
|
* Return the IMA integrity information (iint) associated with an inode, if the
|
|
* inode was processed by IMA.
|
|
*
|
|
* Return: Found iint or NULL.
|
|
*/
|
|
struct ima_iint_cache *ima_iint_find(struct inode *inode)
|
|
{
|
|
if (!IS_IMA(inode))
|
|
return NULL;
|
|
|
|
return ima_inode_get_iint(inode);
|
|
}
|
|
|
|
#define IMA_MAX_NESTING (FILESYSTEM_MAX_STACK_DEPTH + 1)
|
|
|
|
/*
|
|
* It is not clear that IMA should be nested at all, but as long is it measures
|
|
* 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.
|
|
*/
|
|
static inline void ima_iint_lockdep_annotate(struct ima_iint_cache *iint,
|
|
struct inode *inode)
|
|
{
|
|
#ifdef CONFIG_LOCKDEP
|
|
static struct lock_class_key ima_iint_mutex_key[IMA_MAX_NESTING];
|
|
|
|
int depth = inode->i_sb->s_stack_depth;
|
|
|
|
if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING))
|
|
depth = 0;
|
|
|
|
lockdep_set_class(&iint->mutex, &ima_iint_mutex_key[depth]);
|
|
#endif
|
|
}
|
|
|
|
static void ima_iint_init_always(struct ima_iint_cache *iint,
|
|
struct inode *inode)
|
|
{
|
|
iint->ima_hash = NULL;
|
|
iint->real_inode.version = 0;
|
|
iint->flags = 0UL;
|
|
iint->atomic_flags = 0UL;
|
|
iint->ima_file_status = INTEGRITY_UNKNOWN;
|
|
iint->ima_mmap_status = INTEGRITY_UNKNOWN;
|
|
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
|
|
iint->ima_read_status = INTEGRITY_UNKNOWN;
|
|
iint->ima_creds_status = INTEGRITY_UNKNOWN;
|
|
iint->measured_pcrs = 0;
|
|
mutex_init(&iint->mutex);
|
|
ima_iint_lockdep_annotate(iint, inode);
|
|
}
|
|
|
|
static void ima_iint_free(struct ima_iint_cache *iint)
|
|
{
|
|
kfree(iint->ima_hash);
|
|
mutex_destroy(&iint->mutex);
|
|
kmem_cache_free(ima_iint_cache, iint);
|
|
}
|
|
|
|
/**
|
|
* ima_inode_get - Find or allocate an iint associated with an inode
|
|
* @inode: Pointer to the inode
|
|
*
|
|
* 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 *iint;
|
|
|
|
iint = ima_iint_find(inode);
|
|
if (iint)
|
|
return iint;
|
|
|
|
iint = kmem_cache_alloc(ima_iint_cache, GFP_NOFS);
|
|
if (!iint)
|
|
return NULL;
|
|
|
|
ima_iint_init_always(iint, inode);
|
|
|
|
inode->i_flags |= S_IMA;
|
|
ima_inode_set_iint(inode, iint);
|
|
|
|
return iint;
|
|
}
|
|
|
|
/**
|
|
* ima_inode_free_rcu - Called to free an inode via a RCU callback
|
|
* @inode_security: The inode->i_security pointer
|
|
*
|
|
* Free the IMA data associated with an inode.
|
|
*/
|
|
void ima_inode_free_rcu(void *inode_security)
|
|
{
|
|
struct ima_iint_cache **iint_p = inode_security + ima_blob_sizes.lbs_inode;
|
|
|
|
/* *iint_p should be NULL if !IS_IMA(inode) */
|
|
if (*iint_p)
|
|
ima_iint_free(*iint_p);
|
|
}
|
|
|
|
static void ima_iint_init_once(void *foo)
|
|
{
|
|
struct ima_iint_cache *iint = (struct ima_iint_cache *)foo;
|
|
|
|
memset(iint, 0, sizeof(*iint));
|
|
}
|
|
|
|
void __init ima_iintcache_init(void)
|
|
{
|
|
ima_iint_cache =
|
|
kmem_cache_create("ima_iint_cache", sizeof(struct ima_iint_cache),
|
|
0, SLAB_PANIC, ima_iint_init_once);
|
|
}
|