Skip to content

Commit 0d73a55

Browse files
kdsossMimi Zohar
authored andcommitted
ima: re-introduce own integrity cache lock
Before IMA appraisal was introduced, IMA was using own integrity cache lock along with i_mutex. process_measurement and ima_file_free took the iint->mutex first and then the i_mutex, while setxattr, chmod and chown took the locks in reverse order. To resolve the potential deadlock, i_mutex was moved to protect entire IMA functionality and the redundant iint->mutex was eliminated. Solution was based on the assumption that filesystem code does not take i_mutex further. But when file is opened with O_DIRECT flag, direct-io implementation takes i_mutex and produces deadlock. Furthermore, certain other filesystem operations, such as llseek, also take i_mutex. More recently some filesystems have replaced their filesystem specific lock with the global i_rwsem to read a file. As a result, when IMA attempts to calculate the file hash, reading the file attempts to take the i_rwsem again. To resolve O_DIRECT related deadlock problem, this patch re-introduces iint->mutex. But to eliminate the original chmod() related deadlock problem, this patch eliminates the requirement for chmod hooks to take the iint->mutex by introducing additional atomic iint->attr_flags to indicate calling of the hooks. The allowed locking order is to take the iint->mutex first and then the i_rwsem. Original flags were cleared in chmod(), setxattr() or removwxattr() hooks and tested when file was closed or opened again. New atomic flags are set or cleared in those hooks and tested to clear iint->flags on close or on open. Atomic flags are following: * IMA_CHANGE_ATTR - indicates that chATTR() was called (chmod, chown, chgrp) and file attributes have changed. On file open, it causes IMA to clear iint->flags to re-evaluate policy and perform IMA functions again. * IMA_CHANGE_XATTR - indicates that setxattr or removexattr was called and extended attributes have changed. On file open, it causes IMA to clear iint->flags IMA_DONE_MASK to re-appraise. * IMA_UPDATE_XATTR - indicates that security.ima needs to be updated. It is cleared if file policy changes and no update is needed. * IMA_DIGSIG - indicates that file security.ima has signature and file security.ima must not update to file has on file close. * IMA_MUST_MEASURE - indicates the file is in the measurement policy. Fixes: Commit 6552321 ("xfs: remove i_iolock and use i_rwsem in the VFS inode instead") Signed-off-by: Dmitry Kasatkin <[email protected]> Signed-off-by: Mimi Zohar <[email protected]>
1 parent 50b9774 commit 0d73a55

File tree

4 files changed

+77
-40
lines changed

4 files changed

+77
-40
lines changed

security/integrity/iint.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,14 @@ static void init_once(void *foo)
155155
memset(iint, 0, sizeof(*iint));
156156
iint->version = 0;
157157
iint->flags = 0UL;
158+
iint->atomic_flags = 0;
158159
iint->ima_file_status = INTEGRITY_UNKNOWN;
159160
iint->ima_mmap_status = INTEGRITY_UNKNOWN;
160161
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
161162
iint->ima_read_status = INTEGRITY_UNKNOWN;
162163
iint->evm_status = INTEGRITY_UNKNOWN;
163164
iint->measured_pcrs = 0;
165+
mutex_init(&iint->mutex);
164166
}
165167

166168
static int __init integrity_iintcache_init(void)

security/integrity/ima/ima_appraise.c

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ int ima_appraise_measurement(enum ima_hooks func,
251251
status = INTEGRITY_FAIL;
252252
break;
253253
}
254+
clear_bit(IMA_DIGSIG, &iint->atomic_flags);
254255
if (xattr_len - sizeof(xattr_value->type) - hash_start >=
255256
iint->ima_hash->length)
256257
/* xattr length may be longer. md5 hash in previous
@@ -269,7 +270,7 @@ int ima_appraise_measurement(enum ima_hooks func,
269270
status = INTEGRITY_PASS;
270271
break;
271272
case EVM_IMA_XATTR_DIGSIG:
272-
iint->flags |= IMA_DIGSIG;
273+
set_bit(IMA_DIGSIG, &iint->atomic_flags);
273274
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
274275
(const char *)xattr_value, rc,
275276
iint->ima_hash->digest,
@@ -320,7 +321,7 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
320321
int rc = 0;
321322

322323
/* do not collect and update hash for digital signatures */
323-
if (iint->flags & IMA_DIGSIG)
324+
if (test_bit(IMA_DIGSIG, &iint->atomic_flags))
324325
return;
325326

326327
if (iint->ima_file_status != INTEGRITY_PASS)
@@ -330,7 +331,9 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
330331
if (rc < 0)
331332
return;
332333

334+
inode_lock(file_inode(file));
333335
ima_fix_xattr(dentry, iint);
336+
inode_unlock(file_inode(file));
334337
}
335338

336339
/**
@@ -353,16 +356,14 @@ void ima_inode_post_setattr(struct dentry *dentry)
353356
return;
354357

355358
must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
359+
if (!must_appraise)
360+
__vfs_removexattr(dentry, XATTR_NAME_IMA);
356361
iint = integrity_iint_find(inode);
357362
if (iint) {
358-
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
359-
IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
360-
IMA_ACTION_RULE_FLAGS);
361-
if (must_appraise)
362-
iint->flags |= IMA_APPRAISE;
363+
set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
364+
if (!must_appraise)
365+
clear_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
363366
}
364-
if (!must_appraise)
365-
__vfs_removexattr(dentry, XATTR_NAME_IMA);
366367
}
367368

368369
/*
@@ -391,12 +392,12 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig)
391392
iint = integrity_iint_find(inode);
392393
if (!iint)
393394
return;
394-
395-
iint->flags &= ~IMA_DONE_MASK;
396395
iint->measured_pcrs = 0;
396+
set_bit(IMA_CHANGE_XATTR, &iint->atomic_flags);
397397
if (digsig)
398-
iint->flags |= IMA_DIGSIG;
399-
return;
398+
set_bit(IMA_DIGSIG, &iint->atomic_flags);
399+
else
400+
clear_bit(IMA_DIGSIG, &iint->atomic_flags);
400401
}
401402

402403
int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,

security/integrity/ima/ima_main.c

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,13 @@ static void ima_rdwr_violation_check(struct file *file,
9696
if (!iint)
9797
iint = integrity_iint_find(inode);
9898
/* IMA_MEASURE is set from reader side */
99-
if (iint && (iint->flags & IMA_MEASURE))
99+
if (iint && test_bit(IMA_MUST_MEASURE,
100+
&iint->atomic_flags))
100101
send_tomtou = true;
101102
}
102103
} else {
104+
if (must_measure)
105+
set_bit(IMA_MUST_MEASURE, &iint->atomic_flags);
103106
if ((atomic_read(&inode->i_writecount) > 0) && must_measure)
104107
send_writers = true;
105108
}
@@ -121,21 +124,24 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
121124
struct inode *inode, struct file *file)
122125
{
123126
fmode_t mode = file->f_mode;
127+
bool update;
124128

125129
if (!(mode & FMODE_WRITE))
126130
return;
127131

128-
inode_lock(inode);
132+
mutex_lock(&iint->mutex);
129133
if (atomic_read(&inode->i_writecount) == 1) {
134+
update = test_and_clear_bit(IMA_UPDATE_XATTR,
135+
&iint->atomic_flags);
130136
if ((iint->version != inode->i_version) ||
131137
(iint->flags & IMA_NEW_FILE)) {
132138
iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
133139
iint->measured_pcrs = 0;
134-
if (iint->flags & IMA_APPRAISE)
140+
if (update)
135141
ima_update_xattr(iint, file);
136142
}
137143
}
138-
inode_unlock(inode);
144+
mutex_unlock(&iint->mutex);
139145
}
140146

141147
/**
@@ -168,7 +174,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
168174
char *pathbuf = NULL;
169175
char filename[NAME_MAX];
170176
const char *pathname = NULL;
171-
int rc = -ENOMEM, action, must_appraise;
177+
int rc = 0, action, must_appraise = 0;
172178
int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
173179
struct evm_ima_xattr_data *xattr_value = NULL;
174180
int xattr_len = 0;
@@ -199,17 +205,31 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
199205
if (action) {
200206
iint = integrity_inode_get(inode);
201207
if (!iint)
202-
goto out;
208+
rc = -ENOMEM;
203209
}
204210

205-
if (violation_check) {
211+
if (!rc && violation_check)
206212
ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
207213
&pathbuf, &pathname);
208-
if (!action) {
209-
rc = 0;
210-
goto out_free;
211-
}
212-
}
214+
215+
inode_unlock(inode);
216+
217+
if (rc)
218+
goto out;
219+
if (!action)
220+
goto out;
221+
222+
mutex_lock(&iint->mutex);
223+
224+
if (test_and_clear_bit(IMA_CHANGE_ATTR, &iint->atomic_flags))
225+
/* reset appraisal flags if ima_inode_post_setattr was called */
226+
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
227+
IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
228+
IMA_ACTION_FLAGS);
229+
230+
if (test_and_clear_bit(IMA_CHANGE_XATTR, &iint->atomic_flags))
231+
/* reset all flags if ima_inode_setxattr was called */
232+
iint->flags &= ~IMA_DONE_MASK;
213233

214234
/* Determine if already appraised/measured based on bitmask
215235
* (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
@@ -227,7 +247,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
227247
if (!action) {
228248
if (must_appraise)
229249
rc = ima_get_cache_status(iint, func);
230-
goto out_digsig;
250+
goto out_locked;
231251
}
232252

233253
template_desc = ima_template_desc_current();
@@ -240,34 +260,40 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
240260

241261
rc = ima_collect_measurement(iint, file, buf, size, hash_algo);
242262
if (rc != 0 && rc != -EBADF && rc != -EINVAL)
243-
goto out_digsig;
263+
goto out_locked;
244264

245265
if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
246266
pathname = ima_d_path(&file->f_path, &pathbuf, filename);
247267

248268
if (action & IMA_MEASURE)
249269
ima_store_measurement(iint, file, pathname,
250270
xattr_value, xattr_len, pcr);
251-
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK))
271+
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
272+
inode_lock(inode);
252273
rc = ima_appraise_measurement(func, iint, file, pathname,
253274
xattr_value, xattr_len, opened);
275+
inode_unlock(inode);
276+
}
254277
if (action & IMA_AUDIT)
255278
ima_audit_measurement(iint, pathname);
256279

257280
if ((file->f_flags & O_DIRECT) && (iint->flags & IMA_PERMIT_DIRECTIO))
258281
rc = 0;
259-
out_digsig:
260-
if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG) &&
282+
out_locked:
283+
if ((mask & MAY_WRITE) && test_bit(IMA_DIGSIG, &iint->atomic_flags) &&
261284
!(iint->flags & IMA_NEW_FILE))
262285
rc = -EACCES;
286+
mutex_unlock(&iint->mutex);
263287
kfree(xattr_value);
264-
out_free:
288+
out:
265289
if (pathbuf)
266290
__putname(pathbuf);
267-
out:
268-
inode_unlock(inode);
269-
if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
270-
return -EACCES;
291+
if (must_appraise) {
292+
if (rc && (ima_appraise & IMA_APPRAISE_ENFORCE))
293+
return -EACCES;
294+
if (file->f_mode & FMODE_WRITE)
295+
set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
296+
}
271297
return 0;
272298
}
273299

security/integrity/integrity.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,10 @@
2929
/* iint cache flags */
3030
#define IMA_ACTION_FLAGS 0xff000000
3131
#define IMA_ACTION_RULE_FLAGS 0x06000000
32-
#define IMA_DIGSIG 0x01000000
33-
#define IMA_DIGSIG_REQUIRED 0x02000000
34-
#define IMA_PERMIT_DIRECTIO 0x04000000
35-
#define IMA_NEW_FILE 0x08000000
36-
#define EVM_IMMUTABLE_DIGSIG 0x10000000
32+
#define IMA_DIGSIG_REQUIRED 0x01000000
33+
#define IMA_PERMIT_DIRECTIO 0x02000000
34+
#define IMA_NEW_FILE 0x04000000
35+
#define EVM_IMMUTABLE_DIGSIG 0x08000000
3736

3837
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
3938
IMA_APPRAISE_SUBMASK)
@@ -54,6 +53,13 @@
5453
#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
5554
IMA_BPRM_APPRAISED | IMA_READ_APPRAISED)
5655

56+
/* iint cache atomic_flags */
57+
#define IMA_CHANGE_XATTR 0
58+
#define IMA_UPDATE_XATTR 1
59+
#define IMA_CHANGE_ATTR 2
60+
#define IMA_DIGSIG 3
61+
#define IMA_MUST_MEASURE 4
62+
5763
enum evm_ima_xattr_type {
5864
IMA_XATTR_DIGEST = 0x01,
5965
EVM_XATTR_HMAC,
@@ -102,10 +108,12 @@ struct signature_v2_hdr {
102108
/* integrity data associated with an inode */
103109
struct integrity_iint_cache {
104110
struct rb_node rb_node; /* rooted in integrity_iint_tree */
111+
struct mutex mutex; /* protects: version, flags, digest */
105112
struct inode *inode; /* back pointer to inode in question */
106113
u64 version; /* track inode changes */
107114
unsigned long flags;
108115
unsigned long measured_pcrs;
116+
unsigned long atomic_flags;
109117
enum integrity_status ima_file_status:4;
110118
enum integrity_status ima_mmap_status:4;
111119
enum integrity_status ima_bprm_status:4;

0 commit comments

Comments
 (0)