ima: prevent dead lock when a file is opened for direct io (take II)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Files are measured or appraised based on the IMA policy.  When a file,
in policy, is opened with the O_DIRECT flag, a deadlock occurs.

ima_process_measurement() takes the i_mutex, before calculating the
hash, appraising the hash against a 'good' known value stored as an
extended attribute, and caching the result, before releasing the
i_mutex.  Holding the i_mutex results in a lockdep with
do_blockdev_direct_IO(), which takes the i_mutex and releases it
for each read.

The first attempt at resolving this lockdep, by Dmitry Kasatkin,
temporarily removed the O_DIRECT flag and restored it, after
calculating the hash.  This resolved the lockdep, but the benefits
of using O_DIRECT, in terms of page cache, were lost.  Al Viro
objected to this patch, because it made the "existing locking rule
more convoluted".  (The existing (undocumented) IMA locking
rule prohibits ->read() of regular files from taking the i_mutex.
The change would have prohibited ->read() of regular files from
taking the i_mutex, unless they were opened with O_DIRECT.)

The IMA specific 'iint->mutex' was removed, because it caused dead
locking due to different locking orders in different cases (eg.
regular vs. setxattr).

This patch defines a new flag, O_DIRECT_HAVELOCK for lack of a better
name, to indicate the i_mutex has already been taken.  Based on this
flag, the i_mutex is not taken, again, in do_blockdev_direct_IO().

Similar changes for filesystems, that define their own .aio_read
instead of using the generic_file_aio_read(), will probably need
to be made.  Before going down this path, I'd appreciate any
comments/suggestions.

thanks,

Mimi

Signed-off-by: Mimi Zohar <zohar@xxxxxxxxxxxxxxxxxx>
---
 fs/direct-io.c                    | 12 +++++++++---
 include/uapi/asm-generic/fcntl.h  |  1 +
 security/integrity/ima/ima_main.c |  5 +++++
 3 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/fs/direct-io.c b/fs/direct-io.c
index 0e04142..d6444b9 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -1126,6 +1126,7 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
 	size_t bytes;
 	struct buffer_head map_bh = { 0, };
 	struct blk_plug plug;
+	int skip_lock = 0;
 
 	if (rw & WRITE)
 		rw = WRITE_ODIRECT;
@@ -1179,14 +1180,19 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
 		if (rw == READ) {
 			struct address_space *mapping =
 					iocb->ki_filp->f_mapping;
+			struct file *file = iocb->ki_filp;
 
 			/* will be released by direct_io_worker */
-			mutex_lock(&inode->i_mutex);
+			if (file->f_flags & O_DIRECT_HAVELOCK)
+				skip_lock = 1;
+			if (!skip_lock)
+				mutex_lock(&inode->i_mutex);
 
 			retval = filemap_write_and_wait_range(mapping, offset,
 							      end - 1);
 			if (retval) {
-				mutex_unlock(&inode->i_mutex);
+				if (!skip_lock)
+					mutex_unlock(&inode->i_mutex);
 				kmem_cache_free(dio_cache, dio);
 				goto out;
 			}
@@ -1331,7 +1337,7 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
 	 * we can let i_mutex go now that its achieved its purpose
 	 * of protecting us from looking up uninitialized blocks.
 	 */
-	if (rw == READ && (dio->flags & DIO_LOCKING))
+	if (rw == READ && (dio->flags & DIO_LOCKING) && (!skip_lock))
 		mutex_unlock(&dio->inode->i_mutex);
 
 	/*
diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h
index 95e46c8..02c6996 100644
--- a/include/uapi/asm-generic/fcntl.h
+++ b/include/uapi/asm-generic/fcntl.h
@@ -45,6 +45,7 @@
 #endif
 #ifndef O_DIRECT
 #define O_DIRECT	00040000	/* direct disk access hint */
+#define O_DIRECT_HAVELOCK 04000000	/* direct disk access hint */
 #endif
 #ifndef O_LARGEFILE
 #define O_LARGEFILE	00100000
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 149ee11..0d2c93d 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -191,6 +191,9 @@ static int process_measurement(struct file *file, const char *filename,
 
 	mutex_lock(&inode->i_mutex);
 
+	if (file->f_flags & O_DIRECT)
+		file->f_flags |= O_DIRECT_HAVELOCK;
+
 	iint = integrity_inode_get(inode);
 	if (!iint)
 		goto out;
@@ -237,6 +240,8 @@ out_digsig:
 	if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG))
 		rc = -EACCES;
 out:
+	if (file->f_flags & O_DIRECT)
+		file->f_flags &= ~O_DIRECT_HAVELOCK;
 	mutex_unlock(&inode->i_mutex);
 	kfree(xattr_value);
 	if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
-- 
1.8.1.4



--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux