[PATCH 2/2] ima: allocate user-space like memory for direct-io

From: Dmitry Kasatkin
Date: Tue May 13 2014 - 11:59:43 EST


For files opened with O_DIRECT flag, file I/O is done directly to/from
user-space pages. kmalloc or vmalloc allocated memory is not suitable
for direct IO and set_fs(get_ds()) trick does not work there.

This patch uses vm_mmap() call to allocated annonymous user-space like
memory from the kernel.

Signed-off-by: Dmitry Kasatkin <d.kasatkin@xxxxxxxxxxx>
---
security/integrity/ima/ima_crypto.c | 44 ++++++++++++++++++++++++++++++-------
1 file changed, 36 insertions(+), 8 deletions(-)

diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index de5b974..59efc0a 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -21,6 +21,7 @@
#include <linux/scatterlist.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/mman.h>
#include <crypto/hash.h>
#include <crypto/hash_info.h>
#include "ima.h"
@@ -36,7 +37,7 @@ static struct crypto_shash *ima_shash_tfm;
*
*/
static int ima_kernel_read(struct file *file, loff_t offset,
- char *addr, unsigned long count)
+ char *addr, unsigned long count, bool kmem)
{
mm_segment_t old_fs;
char __user *buf = addr;
@@ -47,13 +48,16 @@ static int ima_kernel_read(struct file *file, loff_t offset,
if (!file->f_op->read && !file->f_op->aio_read)
return -EINVAL;

- old_fs = get_fs();
- set_fs(get_ds());
+ if (kmem) {
+ old_fs = get_fs();
+ set_fs(get_ds());
+ }
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, &offset);
else
ret = do_sync_read(file, buf, count, &offset);
- set_fs(old_fs);
+ if (kmem)
+ set_fs(old_fs);
return ret;
}

@@ -102,6 +106,7 @@ static int ima_calc_file_hash_tfm(struct file *file,
{
loff_t i_size, offset = 0;
char *rbuf;
+ unsigned long addr = 0;
int rc, read = 0;
struct {
struct shash_desc shash;
@@ -122,9 +127,22 @@ static int ima_calc_file_hash_tfm(struct file *file,
if (i_size == 0)
goto out;

- rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
- if (!rbuf)
- return -ENOMEM;
+ if (file->f_flags & O_DIRECT) {
+ /* direct-io code writes to user-space buffers
+ * set_fs(get_ds()) trick does not work there
+ */
+ addr = vm_mmap(NULL, 0, PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, 0);
+ if (IS_ERR_VALUE(addr))
+ return addr;
+ }
+
+ rbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!rbuf) {
+ rc = -ENOMEM;
+ goto nomem;
+ }

if (!(file->f_mode & FMODE_READ)) {
file->f_mode |= FMODE_READ;
@@ -134,7 +152,9 @@ static int ima_calc_file_hash_tfm(struct file *file,
while (offset < i_size) {
int rbuf_len;

- rbuf_len = ima_kernel_read(file, offset, rbuf, PAGE_SIZE);
+ rbuf_len = ima_kernel_read(file, offset,
+ addr ? (char *)addr : rbuf,
+ PAGE_SIZE, !addr);
if (rbuf_len < 0) {
rc = rbuf_len;
break;
@@ -143,6 +163,11 @@ static int ima_calc_file_hash_tfm(struct file *file,
break;
offset += rbuf_len;

+ if (addr && __copy_from_user(rbuf, (void __user *)addr,
+ rbuf_len)) {
+ rc = -EFAULT;
+ break;
+ }
rc = crypto_shash_update(&desc.shash, rbuf, rbuf_len);
if (rc)
break;
@@ -150,6 +175,9 @@ static int ima_calc_file_hash_tfm(struct file *file,
if (read)
file->f_mode &= ~FMODE_READ;
kfree(rbuf);
+nomem:
+ if (addr)
+ vm_munmap(addr, PAGE_SIZE);
out:
if (!rc)
rc = crypto_shash_final(&desc.shash, hash->digest);
--
1.9.1

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