Skip to content

Commit f9ba537

Browse files
committed
Merge branch 'ima-memory-use-fixes'
* ima-memory-use-fixes: IMA: fix the ToMToU logic IMA: explicit IMA i_flag to remove global lock on inode_delete IMA: drop refcnt from ima_iint_cache since it isn't needed IMA: only allocate iint when needed IMA: move read counter into struct inode IMA: use i_writecount rather than a private counter IMA: use inode->i_lock to protect read and write counters IMA: convert internal flags from long to char IMA: use unsigned int instead of long for counters IMA: drop the inode opencount since it isn't needed for operation IMA: use rbtree instead of radix tree for inode information cache
2 parents 45352bb + bade72d commit f9ba537

File tree

7 files changed

+202
-177
lines changed

7 files changed

+202
-177
lines changed

fs/inode.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/mount.h>
2525
#include <linux/async.h>
2626
#include <linux/posix_acl.h>
27+
#include <linux/ima.h>
2728

2829
/*
2930
* This is needed for the following functions:

include/linux/fs.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ struct inodes_stat_t {
231231
#define S_NOCMTIME 128 /* Do not update file c/mtime */
232232
#define S_SWAPFILE 256 /* Do not truncate: swapon got its bmaps */
233233
#define S_PRIVATE 512 /* Inode is fs-internal */
234+
#define S_IMA 1024 /* Inode has an associated IMA struct */
234235

235236
/*
236237
* Note that nosuid etc flags are inode-specific: setting some file-system
@@ -265,6 +266,7 @@ struct inodes_stat_t {
265266
#define IS_NOCMTIME(inode) ((inode)->i_flags & S_NOCMTIME)
266267
#define IS_SWAPFILE(inode) ((inode)->i_flags & S_SWAPFILE)
267268
#define IS_PRIVATE(inode) ((inode)->i_flags & S_PRIVATE)
269+
#define IS_IMA(inode) ((inode)->i_flags & S_IMA)
268270

269271
/* the read-only stuff doesn't really belong here, but any other place is
270272
probably as bad and I don't want to create yet another include file. */
@@ -772,6 +774,10 @@ struct inode {
772774

773775
unsigned int i_flags;
774776

777+
#ifdef CONFIG_IMA
778+
/* protected by i_lock */
779+
unsigned int i_readcount; /* struct files open RO */
780+
#endif
775781
atomic_t i_writecount;
776782
#ifdef CONFIG_SECURITY
777783
void *i_security;

security/integrity/ima/ima.h

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ int ima_init(void);
7070
void ima_cleanup(void);
7171
int ima_fs_init(void);
7272
void ima_fs_cleanup(void);
73+
int ima_inode_alloc(struct inode *inode);
7374
int ima_add_template_entry(struct ima_template_entry *entry, int violation,
7475
const char *op, struct inode *inode);
7576
int ima_calc_hash(struct file *file, char *digest);
@@ -96,19 +97,16 @@ static inline unsigned long ima_hash_key(u8 *digest)
9697
}
9798

9899
/* iint cache flags */
99-
#define IMA_MEASURED 1
100+
#define IMA_MEASURED 0x01
100101

101102
/* integrity data associated with an inode */
102103
struct ima_iint_cache {
104+
struct rb_node rb_node; /* rooted in ima_iint_tree */
105+
struct inode *inode; /* back pointer to inode in question */
103106
u64 version; /* track inode changes */
104-
unsigned long flags;
107+
unsigned char flags;
105108
u8 digest[IMA_DIGEST_SIZE];
106109
struct mutex mutex; /* protects: version, flags, digest */
107-
long readcount; /* measured files readcount */
108-
long writecount; /* measured files writecount */
109-
long opencount; /* opens reference count */
110-
struct kref refcount; /* ima_iint_cache reference count */
111-
struct rcu_head rcu;
112110
};
113111

114112
/* LIM API function definitions */
@@ -122,13 +120,11 @@ int ima_store_template(struct ima_template_entry *entry, int violation,
122120
void ima_template_show(struct seq_file *m, void *e,
123121
enum ima_show_type show);
124122

125-
/* radix tree calls to lookup, insert, delete
123+
/* rbtree tree calls to lookup, insert, delete
126124
* integrity data associated with an inode.
127125
*/
128126
struct ima_iint_cache *ima_iint_insert(struct inode *inode);
129-
struct ima_iint_cache *ima_iint_find_get(struct inode *inode);
130-
void iint_free(struct kref *kref);
131-
void iint_rcu_free(struct rcu_head *rcu);
127+
struct ima_iint_cache *ima_iint_find(struct inode *inode);
132128

133129
/* IMA policy related functions */
134130
enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };

security/integrity/ima/ima_api.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
116116
{
117117
int must_measure;
118118

119-
if (iint->flags & IMA_MEASURED)
119+
if (iint && iint->flags & IMA_MEASURED)
120120
return 1;
121121

122122
must_measure = ima_match_policy(inode, function, mask);

security/integrity/ima/ima_iint.c

Lines changed: 92 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -12,98 +12,119 @@
1212
* File: ima_iint.c
1313
* - implements the IMA hooks: ima_inode_alloc, ima_inode_free
1414
* - cache integrity information associated with an inode
15-
* using a radix tree.
15+
* using a rbtree tree.
1616
*/
1717
#include <linux/slab.h>
1818
#include <linux/module.h>
1919
#include <linux/spinlock.h>
20-
#include <linux/radix-tree.h>
20+
#include <linux/rbtree.h>
2121
#include "ima.h"
2222

23-
RADIX_TREE(ima_iint_store, GFP_ATOMIC);
24-
DEFINE_SPINLOCK(ima_iint_lock);
23+
static struct rb_root ima_iint_tree = RB_ROOT;
24+
static DEFINE_SPINLOCK(ima_iint_lock);
2525
static struct kmem_cache *iint_cache __read_mostly;
2626

2727
int iint_initialized = 0;
2828

29-
/* ima_iint_find_get - return the iint associated with an inode
30-
*
31-
* ima_iint_find_get gets a reference to the iint. Caller must
32-
* remember to put the iint reference.
29+
/*
30+
* __ima_iint_find - return the iint associated with an inode
3331
*/
34-
struct ima_iint_cache *ima_iint_find_get(struct inode *inode)
32+
static struct ima_iint_cache *__ima_iint_find(struct inode *inode)
3533
{
3634
struct ima_iint_cache *iint;
35+
struct rb_node *n = ima_iint_tree.rb_node;
36+
37+
assert_spin_locked(&ima_iint_lock);
38+
39+
while (n) {
40+
iint = rb_entry(n, struct ima_iint_cache, rb_node);
41+
42+
if (inode < iint->inode)
43+
n = n->rb_left;
44+
else if (inode > iint->inode)
45+
n = n->rb_right;
46+
else
47+
break;
48+
}
49+
if (!n)
50+
return NULL;
3751

38-
rcu_read_lock();
39-
iint = radix_tree_lookup(&ima_iint_store, (unsigned long)inode);
40-
if (!iint)
41-
goto out;
42-
kref_get(&iint->refcount);
43-
out:
44-
rcu_read_unlock();
4552
return iint;
4653
}
4754

48-
/**
49-
* ima_inode_alloc - allocate an iint associated with an inode
50-
* @inode: pointer to the inode
55+
/*
56+
* ima_iint_find - return the iint associated with an inode
5157
*/
52-
int ima_inode_alloc(struct inode *inode)
58+
struct ima_iint_cache *ima_iint_find(struct inode *inode)
5359
{
54-
struct ima_iint_cache *iint = NULL;
55-
int rc = 0;
56-
57-
iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
58-
if (!iint)
59-
return -ENOMEM;
60+
struct ima_iint_cache *iint;
6061

61-
rc = radix_tree_preload(GFP_NOFS);
62-
if (rc < 0)
63-
goto out;
62+
if (!IS_IMA(inode))
63+
return NULL;
6464

6565
spin_lock(&ima_iint_lock);
66-
rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint);
66+
iint = __ima_iint_find(inode);
6767
spin_unlock(&ima_iint_lock);
68-
radix_tree_preload_end();
69-
out:
70-
if (rc < 0)
71-
kmem_cache_free(iint_cache, iint);
7268

73-
return rc;
69+
return iint;
7470
}
7571

76-
/* iint_free - called when the iint refcount goes to zero */
77-
void iint_free(struct kref *kref)
72+
static void iint_free(struct ima_iint_cache *iint)
7873
{
79-
struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache,
80-
refcount);
8174
iint->version = 0;
8275
iint->flags = 0UL;
83-
if (iint->readcount != 0) {
84-
printk(KERN_INFO "%s: readcount: %ld\n", __func__,
85-
iint->readcount);
86-
iint->readcount = 0;
87-
}
88-
if (iint->writecount != 0) {
89-
printk(KERN_INFO "%s: writecount: %ld\n", __func__,
90-
iint->writecount);
91-
iint->writecount = 0;
92-
}
93-
if (iint->opencount != 0) {
94-
printk(KERN_INFO "%s: opencount: %ld\n", __func__,
95-
iint->opencount);
96-
iint->opencount = 0;
97-
}
98-
kref_init(&iint->refcount);
9976
kmem_cache_free(iint_cache, iint);
10077
}
10178

102-
void iint_rcu_free(struct rcu_head *rcu_head)
79+
/**
80+
* ima_inode_alloc - allocate an iint associated with an inode
81+
* @inode: pointer to the inode
82+
*/
83+
int ima_inode_alloc(struct inode *inode)
10384
{
104-
struct ima_iint_cache *iint = container_of(rcu_head,
105-
struct ima_iint_cache, rcu);
106-
kref_put(&iint->refcount, iint_free);
85+
struct rb_node **p;
86+
struct rb_node *new_node, *parent = NULL;
87+
struct ima_iint_cache *new_iint, *test_iint;
88+
int rc;
89+
90+
new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
91+
if (!new_iint)
92+
return -ENOMEM;
93+
94+
new_iint->inode = inode;
95+
new_node = &new_iint->rb_node;
96+
97+
mutex_lock(&inode->i_mutex); /* i_flags */
98+
spin_lock(&ima_iint_lock);
99+
100+
p = &ima_iint_tree.rb_node;
101+
while (*p) {
102+
parent = *p;
103+
test_iint = rb_entry(parent, struct ima_iint_cache, rb_node);
104+
105+
rc = -EEXIST;
106+
if (inode < test_iint->inode)
107+
p = &(*p)->rb_left;
108+
else if (inode > test_iint->inode)
109+
p = &(*p)->rb_right;
110+
else
111+
goto out_err;
112+
}
113+
114+
inode->i_flags |= S_IMA;
115+
rb_link_node(new_node, parent, p);
116+
rb_insert_color(new_node, &ima_iint_tree);
117+
118+
spin_unlock(&ima_iint_lock);
119+
mutex_unlock(&inode->i_mutex); /* i_flags */
120+
121+
return 0;
122+
out_err:
123+
spin_unlock(&ima_iint_lock);
124+
mutex_unlock(&inode->i_mutex); /* i_flags */
125+
iint_free(new_iint);
126+
127+
return rc;
107128
}
108129

109130
/**
@@ -116,11 +137,20 @@ void ima_inode_free(struct inode *inode)
116137
{
117138
struct ima_iint_cache *iint;
118139

140+
if (inode->i_readcount)
141+
printk(KERN_INFO "%s: readcount: %u\n", __func__, inode->i_readcount);
142+
143+
inode->i_readcount = 0;
144+
145+
if (!IS_IMA(inode))
146+
return;
147+
119148
spin_lock(&ima_iint_lock);
120-
iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode);
149+
iint = __ima_iint_find(inode);
150+
rb_erase(&iint->rb_node, &ima_iint_tree);
121151
spin_unlock(&ima_iint_lock);
122-
if (iint)
123-
call_rcu(&iint->rcu, iint_rcu_free);
152+
153+
iint_free(iint);
124154
}
125155

126156
static void init_once(void *foo)
@@ -131,10 +161,6 @@ static void init_once(void *foo)
131161
iint->version = 0;
132162
iint->flags = 0UL;
133163
mutex_init(&iint->mutex);
134-
iint->readcount = 0;
135-
iint->writecount = 0;
136-
iint->opencount = 0;
137-
kref_init(&iint->refcount);
138164
}
139165

140166
static int __init ima_iintcache_init(void)

0 commit comments

Comments
 (0)