summaryrefslogtreecommitdiff
path: root/security/integrity/ima
diff options
context:
space:
mode:
authorDmitry Kasatkin <d.kasatkin@samsung.com>2014-05-06 15:54:27 +0400
committerMimi Zohar <zohar@linux.vnet.ibm.com>2014-07-17 17:35:11 +0400
commit32c2e6752ff0f48fe03b9e1c7c64bde580a840d2 (patch)
tree82d66c533fb992e6f9f2f7dcc10f8ec0d1057314 /security/integrity/ima
parent6edf7a89260859c5e72861dc4e6e169495f076c8 (diff)
downloadlinux-32c2e6752ff0f48fe03b9e1c7c64bde580a840d2.tar.xz
ima: provide double buffering for hash calculation
The asynchronous hash API allows initiating a hash calculation and then performing other tasks, while waiting for the hash calculation to complete. This patch introduces usage of double buffering for simultaneous hashing and reading of the next chunk of data from storage. Changes in v3: - better comments Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com> Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
Diffstat (limited to 'security/integrity/ima')
-rw-r--r--security/integrity/ima/ima_crypto.c65
1 files changed, 49 insertions, 16 deletions
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index 84b938f86bea..0bd732843fe7 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -253,12 +253,12 @@ static int ima_calc_file_hash_atfm(struct file *file,
struct crypto_ahash *tfm)
{
loff_t i_size, offset;
- char *rbuf;
- int rc, read = 0, rbuf_len;
+ char *rbuf[2] = { NULL, };
+ int rc, read = 0, rbuf_len, active = 0, ahash_rc = 0;
struct ahash_request *req;
struct scatterlist sg[1];
struct ahash_completion res;
- size_t rbuf_size;
+ size_t rbuf_size[2];
hash->length = crypto_ahash_digestsize(tfm);
@@ -284,36 +284,69 @@ static int ima_calc_file_hash_atfm(struct file *file,
* Try to allocate maximum size of memory.
* Fail if even a single page cannot be allocated.
*/
- rbuf = ima_alloc_pages(i_size, &rbuf_size, 1);
- if (!rbuf) {
+ rbuf[0] = ima_alloc_pages(i_size, &rbuf_size[0], 1);
+ if (!rbuf[0]) {
rc = -ENOMEM;
goto out1;
}
+ /* Only allocate one buffer if that is enough. */
+ if (i_size > rbuf_size[0]) {
+ /*
+ * Try to allocate secondary buffer. If that fails fallback to
+ * using single buffering. Use previous memory allocation size
+ * as baseline for possible allocation size.
+ */
+ rbuf[1] = ima_alloc_pages(i_size - rbuf_size[0],
+ &rbuf_size[1], 0);
+ }
+
if (!(file->f_mode & FMODE_READ)) {
file->f_mode |= FMODE_READ;
read = 1;
}
for (offset = 0; offset < i_size; offset += rbuf_len) {
- rbuf_len = ima_kernel_read(file, offset, rbuf, PAGE_SIZE);
- if (rbuf_len < 0) {
- rc = rbuf_len;
- break;
+ if (!rbuf[1] && offset) {
+ /* Not using two buffers, and it is not the first
+ * read/request, wait for the completion of the
+ * previous ahash_update() request.
+ */
+ rc = ahash_wait(ahash_rc, &res);
+ if (rc)
+ goto out3;
+ }
+ /* read buffer */
+ rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]);
+ rc = ima_kernel_read(file, offset, rbuf[active], rbuf_len);
+ if (rc != rbuf_len)
+ goto out3;
+
+ if (rbuf[1] && offset) {
+ /* Using two buffers, and it is not the first
+ * read/request, wait for the completion of the
+ * previous ahash_update() request.
+ */
+ rc = ahash_wait(ahash_rc, &res);
+ if (rc)
+ goto out3;
}
- if (rbuf_len == 0)
- break;
- sg_init_one(&sg[0], rbuf, rbuf_len);
+ sg_init_one(&sg[0], rbuf[active], rbuf_len);
ahash_request_set_crypt(req, sg, NULL, rbuf_len);
- rc = ahash_wait(crypto_ahash_update(req), &res);
- if (rc)
- break;
+ ahash_rc = crypto_ahash_update(req);
+
+ if (rbuf[1])
+ active = !active; /* swap buffers, if we use two */
}
+ /* wait for the last update request to complete */
+ rc = ahash_wait(ahash_rc, &res);
+out3:
if (read)
file->f_mode &= ~FMODE_READ;
- ima_free_pages(rbuf, rbuf_size);
+ ima_free_pages(rbuf[0], rbuf_size[0]);
+ ima_free_pages(rbuf[1], rbuf_size[1]);
out2:
if (!rc) {
ahash_request_set_crypt(req, NULL, hash->digest, 0);