Skip to content

Commit

Permalink
crypto: user - fix use_after_free of struct xxx_request
Browse files Browse the repository at this point in the history
All crypto_stats functions use the struct xxx_request for feeding stats,
but in some case this structure could already be freed.

For fixing this, the needed parameters (len and alg) will be stored
before the request being executed.
Fixes: cac5818 ("crypto: user - Implement a generic crypto statistics")
Reported-by: syzbot <[email protected]>

Signed-off-by: Corentin Labbe <[email protected]>
Signed-off-by: Herbert Xu <[email protected]>
  • Loading branch information
montjoie authored and herbertx committed Dec 7, 2018
1 parent 76d09ea commit f7d76e0
Show file tree
Hide file tree
Showing 11 changed files with 376 additions and 276 deletions.
17 changes: 14 additions & 3 deletions crypto/ahash.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,34 +364,45 @@ static int crypto_ahash_op(struct ahash_request *req,

int crypto_ahash_final(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct crypto_alg *alg = tfm->base.__crt_alg;
unsigned int nbytes = req->nbytes;
int ret;

crypto_stats_get(alg);
ret = crypto_ahash_op(req, crypto_ahash_reqtfm(req)->final);
crypto_stat_ahash_final(req, ret);
crypto_stats_ahash_final(nbytes, ret, alg);
return ret;
}
EXPORT_SYMBOL_GPL(crypto_ahash_final);

int crypto_ahash_finup(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct crypto_alg *alg = tfm->base.__crt_alg;
unsigned int nbytes = req->nbytes;
int ret;

crypto_stats_get(alg);
ret = crypto_ahash_op(req, crypto_ahash_reqtfm(req)->finup);
crypto_stat_ahash_final(req, ret);
crypto_stats_ahash_final(nbytes, ret, alg);
return ret;
}
EXPORT_SYMBOL_GPL(crypto_ahash_finup);

int crypto_ahash_digest(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct crypto_alg *alg = tfm->base.__crt_alg;
unsigned int nbytes = req->nbytes;
int ret;

crypto_stats_get(alg);
if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
ret = -ENOKEY;
else
ret = crypto_ahash_op(req, tfm->digest);
crypto_stat_ahash_final(req, ret);
crypto_stats_ahash_final(nbytes, ret, alg);
return ret;
}
EXPORT_SYMBOL_GPL(crypto_ahash_digest);
Expand Down
233 changes: 233 additions & 0 deletions crypto/algapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,239 @@ int crypto_type_has_alg(const char *name, const struct crypto_type *frontend,
}
EXPORT_SYMBOL_GPL(crypto_type_has_alg);

#ifdef CONFIG_CRYPTO_STATS
void crypto_stats_get(struct crypto_alg *alg)
{
crypto_alg_get(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_get);

void crypto_stats_ablkcipher_encrypt(unsigned int nbytes, int ret,
struct crypto_alg *alg)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&alg->cipher_err_cnt);
} else {
atomic64_inc(&alg->encrypt_cnt);
atomic64_add(nbytes, &alg->encrypt_tlen);
}
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_ablkcipher_encrypt);

void crypto_stats_ablkcipher_decrypt(unsigned int nbytes, int ret,
struct crypto_alg *alg)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&alg->cipher_err_cnt);
} else {
atomic64_inc(&alg->decrypt_cnt);
atomic64_add(nbytes, &alg->decrypt_tlen);
}
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_ablkcipher_decrypt);

void crypto_stats_aead_encrypt(unsigned int cryptlen, struct crypto_alg *alg,
int ret)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&alg->aead_err_cnt);
} else {
atomic64_inc(&alg->encrypt_cnt);
atomic64_add(cryptlen, &alg->encrypt_tlen);
}
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_aead_encrypt);

void crypto_stats_aead_decrypt(unsigned int cryptlen, struct crypto_alg *alg,
int ret)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&alg->aead_err_cnt);
} else {
atomic64_inc(&alg->decrypt_cnt);
atomic64_add(cryptlen, &alg->decrypt_tlen);
}
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_aead_decrypt);

void crypto_stats_akcipher_encrypt(unsigned int src_len, int ret,
struct crypto_alg *alg)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&alg->akcipher_err_cnt);
} else {
atomic64_inc(&alg->encrypt_cnt);
atomic64_add(src_len, &alg->encrypt_tlen);
}
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_akcipher_encrypt);

void crypto_stats_akcipher_decrypt(unsigned int src_len, int ret,
struct crypto_alg *alg)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&alg->akcipher_err_cnt);
} else {
atomic64_inc(&alg->decrypt_cnt);
atomic64_add(src_len, &alg->decrypt_tlen);
}
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_akcipher_decrypt);

void crypto_stats_akcipher_sign(int ret, struct crypto_alg *alg)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY)
atomic64_inc(&alg->akcipher_err_cnt);
else
atomic64_inc(&alg->sign_cnt);
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_akcipher_sign);

void crypto_stats_akcipher_verify(int ret, struct crypto_alg *alg)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY)
atomic64_inc(&alg->akcipher_err_cnt);
else
atomic64_inc(&alg->verify_cnt);
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_akcipher_verify);

void crypto_stats_compress(unsigned int slen, int ret, struct crypto_alg *alg)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&alg->compress_err_cnt);
} else {
atomic64_inc(&alg->compress_cnt);
atomic64_add(slen, &alg->compress_tlen);
}
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_compress);

void crypto_stats_decompress(unsigned int slen, int ret, struct crypto_alg *alg)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&alg->compress_err_cnt);
} else {
atomic64_inc(&alg->decompress_cnt);
atomic64_add(slen, &alg->decompress_tlen);
}
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_decompress);

void crypto_stats_ahash_update(unsigned int nbytes, int ret,
struct crypto_alg *alg)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY)
atomic64_inc(&alg->hash_err_cnt);
else
atomic64_add(nbytes, &alg->hash_tlen);
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_ahash_update);

void crypto_stats_ahash_final(unsigned int nbytes, int ret,
struct crypto_alg *alg)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&alg->hash_err_cnt);
} else {
atomic64_inc(&alg->hash_cnt);
atomic64_add(nbytes, &alg->hash_tlen);
}
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_ahash_final);

void crypto_stats_kpp_set_secret(struct crypto_alg *alg, int ret)
{
if (ret)
atomic64_inc(&alg->kpp_err_cnt);
else
atomic64_inc(&alg->setsecret_cnt);
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_kpp_set_secret);

void crypto_stats_kpp_generate_public_key(struct crypto_alg *alg, int ret)
{
if (ret)
atomic64_inc(&alg->kpp_err_cnt);
else
atomic64_inc(&alg->generate_public_key_cnt);
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_kpp_generate_public_key);

void crypto_stats_kpp_compute_shared_secret(struct crypto_alg *alg, int ret)
{
if (ret)
atomic64_inc(&alg->kpp_err_cnt);
else
atomic64_inc(&alg->compute_shared_secret_cnt);
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_kpp_compute_shared_secret);

void crypto_stats_rng_seed(struct crypto_alg *alg, int ret)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY)
atomic64_inc(&alg->rng_err_cnt);
else
atomic64_inc(&alg->seed_cnt);
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_rng_seed);

void crypto_stats_rng_generate(struct crypto_alg *alg, unsigned int dlen,
int ret)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&alg->rng_err_cnt);
} else {
atomic64_inc(&alg->generate_cnt);
atomic64_add(dlen, &alg->generate_tlen);
}
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_rng_generate);

void crypto_stats_skcipher_encrypt(unsigned int cryptlen, int ret,
struct crypto_alg *alg)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&alg->cipher_err_cnt);
} else {
atomic64_inc(&alg->encrypt_cnt);
atomic64_add(cryptlen, &alg->encrypt_tlen);
}
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_skcipher_encrypt);

void crypto_stats_skcipher_decrypt(unsigned int cryptlen, int ret,
struct crypto_alg *alg)
{
if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&alg->cipher_err_cnt);
} else {
atomic64_inc(&alg->decrypt_cnt);
atomic64_add(cryptlen, &alg->decrypt_tlen);
}
crypto_alg_put(alg);
}
EXPORT_SYMBOL_GPL(crypto_stats_skcipher_decrypt);
#endif

static int __init crypto_algapi_init(void)
{
crypto_init_proc();
Expand Down
4 changes: 3 additions & 1 deletion crypto/rng.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ static int crypto_default_rng_refcnt;

int crypto_rng_reset(struct crypto_rng *tfm, const u8 *seed, unsigned int slen)
{
struct crypto_alg *alg = tfm->base.__crt_alg;
u8 *buf = NULL;
int err;

crypto_stats_get(alg);
if (!seed && slen) {
buf = kmalloc(slen, GFP_KERNEL);
if (!buf)
Expand All @@ -50,7 +52,7 @@ int crypto_rng_reset(struct crypto_rng *tfm, const u8 *seed, unsigned int slen)
}

err = crypto_rng_alg(tfm)->seed(tfm, seed, slen);
crypto_stat_rng_seed(tfm, err);
crypto_stats_rng_seed(alg, err);
out:
kzfree(buf);
return err;
Expand Down
38 changes: 8 additions & 30 deletions include/crypto/acompress.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,34 +234,6 @@ static inline void acomp_request_set_params(struct acomp_req *req,
req->flags |= CRYPTO_ACOMP_ALLOC_OUTPUT;
}

static inline void crypto_stat_compress(struct acomp_req *req, int ret)
{
#ifdef CONFIG_CRYPTO_STATS
struct crypto_acomp *tfm = crypto_acomp_reqtfm(req);

if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&tfm->base.__crt_alg->compress_err_cnt);
} else {
atomic64_inc(&tfm->base.__crt_alg->compress_cnt);
atomic64_add(req->slen, &tfm->base.__crt_alg->compress_tlen);
}
#endif
}

static inline void crypto_stat_decompress(struct acomp_req *req, int ret)
{
#ifdef CONFIG_CRYPTO_STATS
struct crypto_acomp *tfm = crypto_acomp_reqtfm(req);

if (ret && ret != -EINPROGRESS && ret != -EBUSY) {
atomic64_inc(&tfm->base.__crt_alg->compress_err_cnt);
} else {
atomic64_inc(&tfm->base.__crt_alg->decompress_cnt);
atomic64_add(req->slen, &tfm->base.__crt_alg->decompress_tlen);
}
#endif
}

/**
* crypto_acomp_compress() -- Invoke asynchronous compress operation
*
Expand All @@ -274,10 +246,13 @@ static inline void crypto_stat_decompress(struct acomp_req *req, int ret)
static inline int crypto_acomp_compress(struct acomp_req *req)
{
struct crypto_acomp *tfm = crypto_acomp_reqtfm(req);
struct crypto_alg *alg = tfm->base.__crt_alg;
unsigned int slen = req->slen;
int ret;

crypto_stats_get(alg);
ret = tfm->compress(req);
crypto_stat_compress(req, ret);
crypto_stats_compress(slen, ret, alg);
return ret;
}

Expand All @@ -293,10 +268,13 @@ static inline int crypto_acomp_compress(struct acomp_req *req)
static inline int crypto_acomp_decompress(struct acomp_req *req)
{
struct crypto_acomp *tfm = crypto_acomp_reqtfm(req);
struct crypto_alg *alg = tfm->base.__crt_alg;
unsigned int slen = req->slen;
int ret;

crypto_stats_get(alg);
ret = tfm->decompress(req);
crypto_stat_decompress(req, ret);
crypto_stats_decompress(slen, ret, alg);
return ret;
}

Expand Down
Loading

0 comments on commit f7d76e0

Please sign in to comment.