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: 5f7082ed4f ("crypto: hash - Export shash through hash")
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
+26
-14
@@ -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);
|
||||
|
||||
data = kmap_local_page(sg_page(sg));
|
||||
err = crypto_shash_digest(desc, data + offset, nbytes,
|
||||
req->result);
|
||||
kunmap_local(data);
|
||||
} else
|
||||
err = 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);
|
||||
|
||||
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);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(shash_ahash_digest);
|
||||
|
||||
Reference in New Issue
Block a user