Commit 480db500 authored by Herbert Xu's avatar Herbert Xu
Browse files

crypto: hash - Fix test underflow in shash_ahash_digest



The test on PAGE_SIZE - offset in shash_ahash_digest can underflow,
leading to execution of the fast path even if the data cannot be
mapped into a single page.

Fix this by splitting the test into four cases:

1) nbytes > sg->length: More than one SG entry, slow path.
2) !IS_ENABLED(CONFIG_HIGHMEM): fast path.
3) nbytes > (unsigned int)PAGE_SIZE - offset: Two highmem pages, slow path.
4) Highmem fast path.

Fixes: 5f7082ed ("crypto: hash - Export shash through hash")
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent da6f9bf4
Loading
Loading
Loading
Loading
+28 −16
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/cryptouser.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -201,25 +202,36 @@ int shash_ahash_digest(struct ahash_request *req, struct shash_desc *desc)
	unsigned int nbytes = req->nbytes;
	struct scatterlist *sg;
	unsigned int offset;
	struct page *page;
	const u8 *data;
	int err;

	if (ahash_request_isvirt(req))
		return crypto_shash_digest(desc, req->svirt, nbytes,
					   req->result);
	data = req->svirt;
	if (!nbytes || ahash_request_isvirt(req))
		return crypto_shash_digest(desc, data, nbytes, req->result);

	if (nbytes &&
	    (sg = req->src, offset = sg->offset,
	     nbytes <= min(sg->length, ((unsigned int)(PAGE_SIZE)) - offset))) {
		void *data;
	sg = req->src;
	if (nbytes > sg->length)
		return crypto_shash_init(desc) ?:
		       shash_ahash_finup(req, desc);

	page = sg_page(sg);
	offset = sg->offset;
	data = lowmem_page_address(page) + offset;
	if (!IS_ENABLED(CONFIG_HIGHMEM))
		return crypto_shash_digest(desc, data, nbytes, req->result);

		data = kmap_local_page(sg_page(sg));
	page += offset >> PAGE_SHIFT;
	offset = offset_in_page(offset);

	if (nbytes > (unsigned int)PAGE_SIZE - offset)
		return crypto_shash_init(desc) ?:
		       shash_ahash_finup(req, desc);

	data = kmap_local_page(page);
	err = crypto_shash_digest(desc, data + offset, nbytes,
				  req->result);
	kunmap_local(data);
	} else
		err = crypto_shash_init(desc) ?:
		      shash_ahash_finup(req, desc);

	return err;
}
EXPORT_SYMBOL_GPL(shash_ahash_digest);