| From 1c6f7381b6a4731d613c41354c26af8460ec741c Mon Sep 17 00:00:00 2001 |
| From: Herbert Xu <herbert@gondor.apana.org.au> |
| Date: Wed, 11 Dec 2019 10:50:11 +0800 |
| Subject: [PATCH] crypto: api - fix unexpectedly getting generic implementation |
| |
| commit 2bbb3375d967155bccc86a5887d4a6e29c56b683 upstream. |
| |
| When CONFIG_CRYPTO_MANAGER_EXTRA_TESTS=y, the first lookup of an |
| algorithm that needs to be instantiated using a template will always get |
| the generic implementation, even when an accelerated one is available. |
| |
| This happens because the extra self-tests for the accelerated |
| implementation allocate the generic implementation for comparison |
| purposes, and then crypto_alg_tested() for the generic implementation |
| "fulfills" the original request (i.e. sets crypto_larval::adult). |
| |
| This patch fixes this by only fulfilling the original request if |
| we are currently the best outstanding larval as judged by the |
| priority. If we're not the best then we will ask all waiters on |
| that larval request to retry the lookup. |
| |
| Note that this patch introduces a behaviour change when the module |
| providing the new algorithm is unregistered during the process. |
| Previously we would have failed with ENOENT, after the patch we |
| will instead redo the lookup. |
| |
| Fixes: 9a8a6b3f0950 ("crypto: testmgr - fuzz hashes against...") |
| Fixes: d435e10e67be ("crypto: testmgr - fuzz skciphers against...") |
| Fixes: 40153b10d91c ("crypto: testmgr - fuzz AEADs against...") |
| Reported-by: Eric Biggers <ebiggers@google.com> |
| Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> |
| Reviewed-by: Eric Biggers <ebiggers@google.com> |
| Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/crypto/algapi.c b/crypto/algapi.c |
| index b679ce675289..a34baf66cff6 100644 |
| --- a/crypto/algapi.c |
| +++ b/crypto/algapi.c |
| @@ -271,6 +271,7 @@ void crypto_alg_tested(const char *name, int err) |
| struct crypto_alg *alg; |
| struct crypto_alg *q; |
| LIST_HEAD(list); |
| + bool best; |
| |
| down_write(&crypto_alg_sem); |
| list_for_each_entry(q, &crypto_alg_list, cra_list) { |
| @@ -294,6 +295,21 @@ void crypto_alg_tested(const char *name, int err) |
| |
| alg->cra_flags |= CRYPTO_ALG_TESTED; |
| |
| + /* Only satisfy larval waiters if we are the best. */ |
| + best = true; |
| + list_for_each_entry(q, &crypto_alg_list, cra_list) { |
| + if (crypto_is_moribund(q) || !crypto_is_larval(q)) |
| + continue; |
| + |
| + if (strcmp(alg->cra_name, q->cra_name)) |
| + continue; |
| + |
| + if (q->cra_priority > alg->cra_priority) { |
| + best = false; |
| + break; |
| + } |
| + } |
| + |
| list_for_each_entry(q, &crypto_alg_list, cra_list) { |
| if (q == alg) |
| continue; |
| @@ -317,10 +333,12 @@ void crypto_alg_tested(const char *name, int err) |
| continue; |
| if ((q->cra_flags ^ alg->cra_flags) & larval->mask) |
| continue; |
| - if (!crypto_mod_get(alg)) |
| - continue; |
| |
| - larval->adult = alg; |
| + if (best && crypto_mod_get(alg)) |
| + larval->adult = alg; |
| + else |
| + larval->adult = ERR_PTR(-EAGAIN); |
| + |
| continue; |
| } |
| |
| diff --git a/crypto/api.c b/crypto/api.c |
| index d8ba54142620..9f99d0e70384 100644 |
| --- a/crypto/api.c |
| +++ b/crypto/api.c |
| @@ -97,7 +97,7 @@ static void crypto_larval_destroy(struct crypto_alg *alg) |
| struct crypto_larval *larval = (void *)alg; |
| |
| BUG_ON(!crypto_is_larval(alg)); |
| - if (larval->adult) |
| + if (!IS_ERR_OR_NULL(larval->adult)) |
| crypto_mod_put(larval->adult); |
| kfree(larval); |
| } |
| @@ -178,6 +178,8 @@ static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg) |
| alg = ERR_PTR(-ETIMEDOUT); |
| else if (!alg) |
| alg = ERR_PTR(-ENOENT); |
| + else if (IS_ERR(alg)) |
| + ; |
| else if (crypto_is_test_larval(larval) && |
| !(alg->cra_flags & CRYPTO_ALG_TESTED)) |
| alg = ERR_PTR(-EAGAIN); |
| -- |
| 2.7.4 |
| |