Skip to content

Commit

Permalink
crypto: api - Fix built-in testing dependency failures
Browse files Browse the repository at this point in the history
When complex algorithms that depend on other algorithms are built
into the kernel, the order of registration must be done such that
the underlying algorithms are ready before the ones on top are
registered.  As otherwise they would fail during the self-test
which is required during registration.

In the past we have used subsystem initialisation ordering to
guarantee this.  The number of such precedence levels are limited
and they may cause ripple effects in other subsystems.

This patch solves this problem by delaying all self-tests during
boot-up for built-in algorithms.  They will be tested either when
something else in the kernel requests for them, or when we have
finished registering all built-in algorithms, whichever comes
earlier.

Reported-by: Vladis Dronov <vdronov@redhat.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
  • Loading branch information
herbertx committed Sep 24, 2021
1 parent 7c53296 commit adad556
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 27 deletions.
73 changes: 51 additions & 22 deletions crypto/algapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,29 +389,10 @@ void crypto_remove_final(struct list_head *list)
}
EXPORT_SYMBOL_GPL(crypto_remove_final);

static void crypto_wait_for_test(struct crypto_larval *larval)
{
int err;

err = crypto_probing_notify(CRYPTO_MSG_ALG_REGISTER, larval->adult);
if (err != NOTIFY_STOP) {
if (WARN_ON(err != NOTIFY_DONE))
goto out;
crypto_alg_tested(larval->alg.cra_driver_name, 0);
}

err = wait_for_completion_killable(&larval->completion);
WARN_ON(err);
if (!err)
crypto_notify(CRYPTO_MSG_ALG_LOADED, larval);

out:
crypto_larval_kill(&larval->alg);
}

int crypto_register_alg(struct crypto_alg *alg)
{
struct crypto_larval *larval;
bool test_started;
int err;

alg->cra_flags &= ~CRYPTO_ALG_DEAD;
Expand All @@ -421,12 +402,15 @@ int crypto_register_alg(struct crypto_alg *alg)

down_write(&crypto_alg_sem);
larval = __crypto_register_alg(alg);
test_started = static_key_enabled(&crypto_boot_test_finished);
larval->test_started = test_started;
up_write(&crypto_alg_sem);

if (IS_ERR(larval))
return PTR_ERR(larval);

crypto_wait_for_test(larval);
if (test_started)
crypto_wait_for_test(larval);
return 0;
}
EXPORT_SYMBOL_GPL(crypto_register_alg);
Expand Down Expand Up @@ -633,6 +617,8 @@ int crypto_register_instance(struct crypto_template *tmpl,
if (IS_ERR(larval))
goto unlock;

larval->test_started = true;

hlist_add_head(&inst->list, &tmpl->instances);
inst->tmpl = tmpl;

Expand Down Expand Up @@ -1261,9 +1247,48 @@ void crypto_stats_skcipher_decrypt(unsigned int cryptlen, int ret,
EXPORT_SYMBOL_GPL(crypto_stats_skcipher_decrypt);
#endif

static void __init crypto_start_tests(void)
{
for (;;) {
struct crypto_larval *larval = NULL;
struct crypto_alg *q;

down_write(&crypto_alg_sem);

list_for_each_entry(q, &crypto_alg_list, cra_list) {
struct crypto_larval *l;

if (!crypto_is_larval(q))
continue;

l = (void *)q;

if (!crypto_is_test_larval(l))
continue;

if (l->test_started)
continue;

l->test_started = true;
larval = l;
break;
}

up_write(&crypto_alg_sem);

if (!larval)
break;

crypto_wait_for_test(larval);
}

static_branch_enable(&crypto_boot_test_finished);
}

static int __init crypto_algapi_init(void)
{
crypto_init_proc();
crypto_start_tests();
return 0;
}

Expand All @@ -1272,7 +1297,11 @@ static void __exit crypto_algapi_exit(void)
crypto_exit_proc();
}

module_init(crypto_algapi_init);
/*
* We run this at late_initcall so that all the built-in algorithms
* have had a chance to register themselves first.
*/
late_initcall(crypto_algapi_init);
module_exit(crypto_algapi_exit);

MODULE_LICENSE("GPL");
Expand Down
52 changes: 47 additions & 5 deletions crypto/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <linux/err.h>
#include <linux/errno.h>
#include <linux/jump_label.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/module.h>
Expand All @@ -30,6 +31,8 @@ EXPORT_SYMBOL_GPL(crypto_alg_sem);
BLOCKING_NOTIFIER_HEAD(crypto_chain);
EXPORT_SYMBOL_GPL(crypto_chain);

DEFINE_STATIC_KEY_FALSE(crypto_boot_test_finished);

static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg);

struct crypto_alg *crypto_mod_get(struct crypto_alg *alg)
Expand All @@ -47,11 +50,6 @@ void crypto_mod_put(struct crypto_alg *alg)
}
EXPORT_SYMBOL_GPL(crypto_mod_put);

static inline int crypto_is_test_larval(struct crypto_larval *larval)
{
return larval->alg.cra_driver_name[0];
}

static struct crypto_alg *__crypto_alg_lookup(const char *name, u32 type,
u32 mask)
{
Expand Down Expand Up @@ -163,11 +161,55 @@ void crypto_larval_kill(struct crypto_alg *alg)
}
EXPORT_SYMBOL_GPL(crypto_larval_kill);

void crypto_wait_for_test(struct crypto_larval *larval)
{
int err;

err = crypto_probing_notify(CRYPTO_MSG_ALG_REGISTER, larval->adult);
if (err != NOTIFY_STOP) {
if (WARN_ON(err != NOTIFY_DONE))
goto out;
crypto_alg_tested(larval->alg.cra_driver_name, 0);
}

err = wait_for_completion_killable(&larval->completion);
WARN_ON(err);
if (!err)
crypto_notify(CRYPTO_MSG_ALG_LOADED, larval);

out:
crypto_larval_kill(&larval->alg);
}
EXPORT_SYMBOL_GPL(crypto_wait_for_test);

static void crypto_start_test(struct crypto_larval *larval)
{
if (!crypto_is_test_larval(larval))
return;

if (larval->test_started)
return;

down_write(&crypto_alg_sem);
if (larval->test_started) {
up_write(&crypto_alg_sem);
return;
}

larval->test_started = true;
up_write(&crypto_alg_sem);

crypto_wait_for_test(larval);
}

static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg)
{
struct crypto_larval *larval = (void *)alg;
long timeout;

if (!static_branch_likely(&crypto_boot_test_finished))
crypto_start_test(larval);

timeout = wait_for_completion_killable_timeout(
&larval->completion, 60 * HZ);

Expand Down
10 changes: 10 additions & 0 deletions crypto/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include <crypto/algapi.h>
#include <linux/completion.h>
#include <linux/jump_label.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/notifier.h>
Expand All @@ -27,6 +28,7 @@ struct crypto_larval {
struct crypto_alg *adult;
struct completion completion;
u32 mask;
bool test_started;
};

enum {
Expand All @@ -45,6 +47,8 @@ extern struct list_head crypto_alg_list;
extern struct rw_semaphore crypto_alg_sem;
extern struct blocking_notifier_head crypto_chain;

DECLARE_STATIC_KEY_FALSE(crypto_boot_test_finished);

#ifdef CONFIG_PROC_FS
void __init crypto_init_proc(void);
void __exit crypto_exit_proc(void);
Expand All @@ -70,6 +74,7 @@ struct crypto_alg *crypto_alg_mod_lookup(const char *name, u32 type, u32 mask);

struct crypto_larval *crypto_larval_alloc(const char *name, u32 type, u32 mask);
void crypto_larval_kill(struct crypto_alg *alg);
void crypto_wait_for_test(struct crypto_larval *larval);
void crypto_alg_tested(const char *name, int err);

void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list,
Expand Down Expand Up @@ -156,5 +161,10 @@ static inline void crypto_yield(u32 flags)
cond_resched();
}

static inline int crypto_is_test_larval(struct crypto_larval *larval)
{
return larval->alg.cra_driver_name[0];
}

#endif /* _CRYPTO_INTERNAL_H */

0 comments on commit adad556

Please sign in to comment.