diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2015-04-02 10:31:22 -0400 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2015-04-03 05:53:32 -0400 |
commit | 1f7237109951ebe8dc194461716443a5d8caf308 (patch) | |
tree | d66524f7b6ed702c7939975abd53af31141599f3 | |
parent | 13cf394c8c79b5655cdc76f7ae0d9869a1434103 (diff) |
crypto: api - Fix races in crypto_unregister_instance
There are multiple problems in crypto_unregister_instance:
1) The cra_refcnt BUG_ON check is racy and can cause crashes.
2) The cra_refcnt check shouldn't exist at all.
3) There is no reference on tmpl to protect the tmpl->free call.
This patch rewrites the function using crypto_remove_spawn which
now morphs into crypto_remove_instance.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
-rw-r--r-- | crypto/algapi.c | 23 |
1 files changed, 7 insertions, 16 deletions
diff --git a/crypto/algapi.c b/crypto/algapi.c index 83b04e0884b1..0f1976eceb27 100644 --- a/crypto/algapi.c +++ b/crypto/algapi.c | |||
@@ -99,10 +99,9 @@ static struct list_head *crypto_more_spawns(struct crypto_alg *alg, | |||
99 | return &n->list == stack ? top : &n->inst->alg.cra_users; | 99 | return &n->list == stack ? top : &n->inst->alg.cra_users; |
100 | } | 100 | } |
101 | 101 | ||
102 | static void crypto_remove_spawn(struct crypto_spawn *spawn, | 102 | static void crypto_remove_instance(struct crypto_instance *inst, |
103 | struct list_head *list) | 103 | struct list_head *list) |
104 | { | 104 | { |
105 | struct crypto_instance *inst = spawn->inst; | ||
106 | struct crypto_template *tmpl = inst->tmpl; | 105 | struct crypto_template *tmpl = inst->tmpl; |
107 | 106 | ||
108 | if (crypto_is_dead(&inst->alg)) | 107 | if (crypto_is_dead(&inst->alg)) |
@@ -167,7 +166,7 @@ void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list, | |||
167 | if (spawn->alg) | 166 | if (spawn->alg) |
168 | list_move(&spawn->list, &spawn->alg->cra_users); | 167 | list_move(&spawn->list, &spawn->alg->cra_users); |
169 | else | 168 | else |
170 | crypto_remove_spawn(spawn, list); | 169 | crypto_remove_instance(spawn->inst, list); |
171 | } | 170 | } |
172 | } | 171 | } |
173 | EXPORT_SYMBOL_GPL(crypto_remove_spawns); | 172 | EXPORT_SYMBOL_GPL(crypto_remove_spawns); |
@@ -554,28 +553,20 @@ EXPORT_SYMBOL_GPL(crypto_register_instance); | |||
554 | 553 | ||
555 | int crypto_unregister_instance(struct crypto_alg *alg) | 554 | int crypto_unregister_instance(struct crypto_alg *alg) |
556 | { | 555 | { |
557 | int err; | ||
558 | struct crypto_instance *inst = (void *)alg; | 556 | struct crypto_instance *inst = (void *)alg; |
559 | struct crypto_template *tmpl = inst->tmpl; | 557 | LIST_HEAD(list); |
560 | LIST_HEAD(users); | ||
561 | 558 | ||
562 | if (!(alg->cra_flags & CRYPTO_ALG_INSTANCE)) | 559 | if (!(alg->cra_flags & CRYPTO_ALG_INSTANCE)) |
563 | return -EINVAL; | 560 | return -EINVAL; |
564 | 561 | ||
565 | BUG_ON(atomic_read(&alg->cra_refcnt) != 1); | ||
566 | |||
567 | down_write(&crypto_alg_sem); | 562 | down_write(&crypto_alg_sem); |
568 | 563 | ||
569 | hlist_del_init(&inst->list); | 564 | crypto_remove_spawns(alg, &list, NULL); |
570 | err = crypto_remove_alg(alg, &users); | 565 | crypto_remove_instance(inst, &list); |
571 | 566 | ||
572 | up_write(&crypto_alg_sem); | 567 | up_write(&crypto_alg_sem); |
573 | 568 | ||
574 | if (err) | 569 | crypto_remove_final(&list); |
575 | return err; | ||
576 | |||
577 | tmpl->free(inst); | ||
578 | crypto_remove_final(&users); | ||
579 | 570 | ||
580 | return 0; | 571 | return 0; |
581 | } | 572 | } |