Re: crypto: api - Fix use-after-free and race in crypto_spawn_alg

From: Eric Biggers
Date: Wed Apr 15 2020 - 22:17:20 EST


On Fri, Apr 10, 2020 at 04:09:42PM +1000, Herbert Xu wrote:
> There are two problems in crypto_spawn_alg. First of all it may
> return spawn->alg even if spawn->dead is set. This results in a
> double-free as detected by syzbot.
>
> Secondly the setting of the DYING flag is racy because we hold
> the read-lock instead of the write-lock. We should instead call
> crypto_shoot_alg in a safe manner by gaining a refcount, dropping
> the lock, and then releasing the refcount.
>
> This patch fixes both problems.
>
> Reported-by: syzbot+fc0674cde00b66844470@xxxxxxxxxxxxxxxxxxxxxxxxx
> Fixes: 4f87ee118d16 ("crypto: api - Do not zap spawn->alg")
> Fixes: 73669cc55646 ("crypto: api - Fix race condition in...")
> Cc: <stable@xxxxxxxxxxxxxxx>
> Signed-off-by: Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx>
>
> diff --git a/crypto/algapi.c b/crypto/algapi.c
> index 69605e21af92..f8b4dc161c02 100644
> --- a/crypto/algapi.c
> +++ b/crypto/algapi.c
> @@ -716,17 +716,27 @@ EXPORT_SYMBOL_GPL(crypto_drop_spawn);
>
> static struct crypto_alg *crypto_spawn_alg(struct crypto_spawn *spawn)
> {
> - struct crypto_alg *alg;
> + struct crypto_alg *alg = ERR_PTR(-EAGAIN);
> + struct crypto_alg *target;
> + bool shoot = false;
>
> down_read(&crypto_alg_sem);
> - alg = spawn->alg;
> - if (!spawn->dead && !crypto_mod_get(alg)) {
> - alg->cra_flags |= CRYPTO_ALG_DYING;
> - alg = NULL;
> + if (!spawn->dead) {
> + alg = spawn->alg;
> + if (!crypto_mod_get(alg)) {
> + target = crypto_alg_get(alg);
> + shoot = true;
> + alg = ERR_PTR(-EAGAIN);
> + }
> }
> up_read(&crypto_alg_sem);
>
> - return alg ?: ERR_PTR(-EAGAIN);
> + if (shoot) {
> + crypto_shoot_alg(target);
> + crypto_alg_put(target);
> + }
> +
> + return alg;
> }

Wouldn't it be a bit simpler to set 'target = NULL', remove 'shoot',
and use 'if (target)' instead of 'if (shoot)'?

- Eric