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.kasat...@samsung.com>
---
 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to