summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);