Commit 6603523b authored by Herbert Xu's avatar Herbert Xu

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

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@syzkaller.appspotmail.com
Fixes: 4f87ee11 ("crypto: api - Do not zap spawn->alg")
Fixes: 73669cc5 ("crypto: api - Fix race condition in...")
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent eebac678
...@@ -716,17 +716,27 @@ EXPORT_SYMBOL_GPL(crypto_drop_spawn); ...@@ -716,17 +716,27 @@ EXPORT_SYMBOL_GPL(crypto_drop_spawn);
static struct crypto_alg *crypto_spawn_alg(struct crypto_spawn *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); down_read(&crypto_alg_sem);
if (!spawn->dead) {
alg = spawn->alg; alg = spawn->alg;
if (!spawn->dead && !crypto_mod_get(alg)) { if (!crypto_mod_get(alg)) {
alg->cra_flags |= CRYPTO_ALG_DYING; target = crypto_alg_get(alg);
alg = NULL; shoot = true;
alg = ERR_PTR(-EAGAIN);
}
} }
up_read(&crypto_alg_sem); up_read(&crypto_alg_sem);
return alg ?: ERR_PTR(-EAGAIN); if (shoot) {
crypto_shoot_alg(target);
crypto_alg_put(target);
}
return alg;
} }
struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn, u32 type, struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn, u32 type,
......
...@@ -333,12 +333,13 @@ static unsigned int crypto_ctxsize(struct crypto_alg *alg, u32 type, u32 mask) ...@@ -333,12 +333,13 @@ static unsigned int crypto_ctxsize(struct crypto_alg *alg, u32 type, u32 mask)
return len; return len;
} }
static void crypto_shoot_alg(struct crypto_alg *alg) void crypto_shoot_alg(struct crypto_alg *alg)
{ {
down_write(&crypto_alg_sem); down_write(&crypto_alg_sem);
alg->cra_flags |= CRYPTO_ALG_DYING; alg->cra_flags |= CRYPTO_ALG_DYING;
up_write(&crypto_alg_sem); up_write(&crypto_alg_sem);
} }
EXPORT_SYMBOL_GPL(crypto_shoot_alg);
struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type, struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
u32 mask) u32 mask)
......
...@@ -65,6 +65,7 @@ void crypto_alg_tested(const char *name, int err); ...@@ -65,6 +65,7 @@ void crypto_alg_tested(const char *name, int err);
void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list, void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list,
struct crypto_alg *nalg); struct crypto_alg *nalg);
void crypto_remove_final(struct list_head *list); void crypto_remove_final(struct list_head *list);
void crypto_shoot_alg(struct crypto_alg *alg);
struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type, struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
u32 mask); u32 mask);
void *crypto_create_tfm(struct crypto_alg *alg, void *crypto_create_tfm(struct crypto_alg *alg,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment