aboutsummaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2025-03-13 13:14:58 +0800
committerHerbert Xu <herbert@gondor.apana.org.au>2025-03-21 17:33:38 +0800
commit480db5009571aa8d7f39b0357a6fd337fa3caf31 (patch)
treed1a96c1ed949819fb4314c12f84dd8d012e4fe01 /crypto
parentcrypto: krb5 - Use SG miter instead of doing it by hand (diff)
downloadlaptop-kernel-480db5009571aa8d7f39b0357a6fd337fa3caf31.tar.xz
laptop-kernel-480db5009571aa8d7f39b0357a6fd337fa3caf31.zip
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: 5f7082ed4f48 ("crypto: hash - Export shash through hash") Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'crypto')
-rw-r--r--crypto/ahash.c44
1 files changed, 28 insertions, 16 deletions
diff --git a/crypto/ahash.c b/crypto/ahash.c
index 9c26175c21a8..1fe594880295 100644
--- a/crypto/ahash.c
+++ b/crypto/ahash.c
@@ -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);
-
- if (nbytes &&
- (sg = req->src, offset = sg->offset,
- nbytes <= min(sg->length, ((unsigned int)(PAGE_SIZE)) - offset))) {
- void *data;
-
- 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);
+ data = req->svirt;
+ if (!nbytes || ahash_request_isvirt(req))
+ return crypto_shash_digest(desc, data, nbytes, req->result);
+
+ 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);
+
+ 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);