mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 03:27:30 -04:00
ima: Replace static htable queue with dynamically allocated array
The IMA hash table is a fixed-size array of hlist_head buckets:
struct hlist_head ima_htable[IMA_MEASURE_HTABLE_SIZE];
IMA_MEASURE_HTABLE_SIZE is (1 << IMA_HASH_BITS) = 1024 buckets, each a
struct hlist_head (one pointer, 8 bytes on 64-bit). That is 8 KiB allocated
in BSS for every kernel, regardless of whether IMA is ever used, and
regardless of how many measurements are actually made.
Replace the fixed-size array with a RCU-protected pointer to a dynamically
allocated array that is initialized in ima_init_htable(), which is called
from ima_init() during early boot. ima_init_htable() calls the static
function ima_alloc_replace_htable() which, other than initializing the hash
table the first time, can also hot-swap the existing hash table with a
blank one.
The allocation in ima_alloc_replace_htable() uses kcalloc() so the buckets
are zero-initialised (equivalent to HLIST_HEAD_INIT { .first = NULL }).
Callers of ima_alloc_replace_htable() must call synchronize_rcu() and free
the returned hash table.
Finally, access the hash table with rcu_dereference() in
ima_lookup_digest_entry() (reader side) and with
rcu_dereference_protected() in ima_add_digest_entry() (writer side).
No functional change: bucket count, hash function, and all locking remain
identical.
Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
This commit is contained in:
committed by
Mimi Zohar
parent
a01183bd6f
commit
7bc01800a7
@@ -311,6 +311,7 @@ bool ima_template_has_modsig(const struct ima_template_desc *ima_template);
|
||||
int ima_restore_measurement_entry(struct ima_template_entry *entry);
|
||||
int ima_restore_measurement_list(loff_t bufsize, void *buf);
|
||||
int ima_measurements_show(struct seq_file *m, void *v);
|
||||
int __init ima_init_htable(void);
|
||||
unsigned long ima_get_binary_runtime_size(void);
|
||||
int ima_init_template(void);
|
||||
void ima_init_template_list(void);
|
||||
@@ -328,7 +329,7 @@ extern spinlock_t ima_queue_lock;
|
||||
extern atomic_long_t ima_num_records;
|
||||
/* Total number of violations since hard boot. */
|
||||
extern atomic_long_t ima_num_violations;
|
||||
extern struct hlist_head ima_htable[IMA_MEASURE_HTABLE_SIZE];
|
||||
extern struct hlist_head __rcu *ima_htable;
|
||||
|
||||
static inline unsigned int ima_hash_key(u8 *digest)
|
||||
{
|
||||
|
||||
@@ -140,6 +140,11 @@ int __init ima_init(void)
|
||||
rc = ima_init_digests();
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = ima_init_htable();
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
@@ -36,9 +36,7 @@ atomic_long_t ima_num_records = ATOMIC_LONG_INIT(0);
|
||||
atomic_long_t ima_num_violations = ATOMIC_LONG_INIT(0);
|
||||
|
||||
/* key: inode (before secure-hashing a file) */
|
||||
struct hlist_head ima_htable[IMA_MEASURE_HTABLE_SIZE] = {
|
||||
[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
|
||||
};
|
||||
struct hlist_head __rcu *ima_htable;
|
||||
|
||||
/* mutex protects atomicity of extending measurement list
|
||||
* and extending the TPM PCR aggregate. Since tpm_extend can take
|
||||
@@ -52,17 +50,53 @@ static DEFINE_MUTEX(ima_extend_list_mutex);
|
||||
*/
|
||||
static bool ima_measurements_suspended;
|
||||
|
||||
/* Callers must call synchronize_rcu() and free the hash table. */
|
||||
static struct hlist_head *ima_alloc_replace_htable(void)
|
||||
{
|
||||
struct hlist_head *old_htable, *new_htable;
|
||||
|
||||
/* Initializing to zeros is equivalent to call HLIST_HEAD_INIT. */
|
||||
new_htable = kcalloc(IMA_MEASURE_HTABLE_SIZE, sizeof(struct hlist_head),
|
||||
GFP_KERNEL);
|
||||
if (!new_htable)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
old_htable = rcu_replace_pointer(ima_htable, new_htable,
|
||||
lockdep_is_held(&ima_extend_list_mutex));
|
||||
|
||||
return old_htable;
|
||||
}
|
||||
|
||||
int __init ima_init_htable(void)
|
||||
{
|
||||
struct hlist_head *old_htable;
|
||||
|
||||
mutex_lock(&ima_extend_list_mutex);
|
||||
old_htable = ima_alloc_replace_htable();
|
||||
mutex_unlock(&ima_extend_list_mutex);
|
||||
|
||||
if (IS_ERR(old_htable))
|
||||
return PTR_ERR(old_htable);
|
||||
|
||||
/* Synchronize_rcu() and kfree() not necessary, only for robustness. */
|
||||
synchronize_rcu();
|
||||
kfree(old_htable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* lookup up the digest value in the hash table, and return the entry */
|
||||
static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
|
||||
int pcr)
|
||||
{
|
||||
struct ima_queue_entry *qe, *ret = NULL;
|
||||
struct hlist_head *htable;
|
||||
unsigned int key;
|
||||
int rc;
|
||||
|
||||
key = ima_hash_key(digest_value);
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(qe, &ima_htable[key], hnext) {
|
||||
htable = rcu_dereference(ima_htable);
|
||||
hlist_for_each_entry_rcu(qe, &htable[key], hnext) {
|
||||
rc = memcmp(qe->entry->digests[ima_hash_algo_idx].digest,
|
||||
digest_value, hash_digest_size[ima_hash_algo]);
|
||||
if ((rc == 0) && (qe->entry->pcr == pcr)) {
|
||||
@@ -102,6 +136,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
|
||||
bool update_htable)
|
||||
{
|
||||
struct ima_queue_entry *qe;
|
||||
struct hlist_head *htable;
|
||||
unsigned int key;
|
||||
|
||||
qe = kmalloc_obj(*qe);
|
||||
@@ -114,10 +149,13 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
|
||||
INIT_LIST_HEAD(&qe->later);
|
||||
list_add_tail_rcu(&qe->later, &ima_measurements);
|
||||
|
||||
htable = rcu_dereference_protected(ima_htable,
|
||||
lockdep_is_held(&ima_extend_list_mutex));
|
||||
|
||||
atomic_long_inc(&ima_num_records);
|
||||
if (update_htable) {
|
||||
key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest);
|
||||
hlist_add_head_rcu(&qe->hnext, &ima_htable[key]);
|
||||
hlist_add_head_rcu(&qe->hnext, &htable[key]);
|
||||
}
|
||||
|
||||
if (binary_runtime_size != ULONG_MAX) {
|
||||
|
||||
Reference in New Issue
Block a user