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/