On Mon, Nov 05, 2018 at 05:49:06PM -0800, Eric Biggers wrote:
> Hi Corentin,
> 
> On Mon, Nov 05, 2018 at 12:51:14PM +0000, Corentin Labbe wrote:
> > 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: cac5818c25d0 ("crypto: user - Implement a generic crypto statistics")
> > Reported-by: syzbot <[email protected]>
> > 
> > Signed-off-by: Corentin Labbe <[email protected]>
> > ---
> >  crypto/ahash.c             | 14 ++++++++--
> >  include/crypto/acompress.h | 30 ++++++++++----------
> >  include/crypto/aead.h      | 30 ++++++++++----------
> >  include/crypto/akcipher.h  | 56 ++++++++++++++++++--------------------
> >  include/crypto/hash.h      | 25 +++++++++--------
> >  include/crypto/kpp.h       | 22 +++++++--------
> >  include/crypto/skcipher.h  | 16 +++++++----
> >  include/linux/crypto.h     | 34 +++++++++++------------
> >  8 files changed, 118 insertions(+), 109 deletions(-)
> > 
> > diff --git a/crypto/ahash.c b/crypto/ahash.c
> > index 3a348fbcf8f9..3f4c44c1e80f 100644
> > --- a/crypto/ahash.c
> > +++ b/crypto/ahash.c
> > @@ -364,20 +364,26 @@ 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;
> >  
> >     ret = crypto_ahash_op(req, crypto_ahash_reqtfm(req)->final);
> > -   crypto_stat_ahash_final(req, ret);
> > +   crypto_stat_ahash_final(nbytes, ret, alg);
> >     return ret;
> >  }
> >  EXPORT_SYMBOL_GPL(crypto_ahash_final);
> 
> I'm not confident this is enough.  If the request is being processed
> asynchronously, the completion callback could be used to signal another thread
> to go ahead and free the resources, including not only the request but also 
> the
> 'tfm', which could even result in the last reference to the 'alg' being 
> dropped
> and therefore the 'alg' being freed.  If I'm wrong, then please explain why 
> :-)
> 
> Note: I'd also prefer a solution that is more obviously zero-overhead in the
> !CONFIG_CRYPTO_STATS case.
> 
> - Eric
> 

I think the best solution is to grab a crypto_alg refcnt before the operation 
and release it after.
So for example this will lead to:
--- a/include/crypto/skcipher.h
+++ b/include/crypto/skcipher.h
@@ -486,7 +486,7 @@ static inline struct crypto_sync_skcipher 
*crypto_sync_skcipher_reqtfm(
        return container_of(tfm, struct crypto_sync_skcipher, base);
 }
 
-static inline void crypto_stat_skcipher_encrypt(struct skcipher_request *req,
+static inline void crypto_stat_skcipher_encrypt(unsigned int cryptlen,
                                                int ret, struct crypto_alg *alg)
 {
 #ifdef CONFIG_CRYPTO_STATS
@@ -494,12 +494,13 @@ static inline void crypto_stat_skcipher_encrypt(struct 
skcipher_request *req,
                atomic64_inc(&alg->cipher_err_cnt);
        } else {
                atomic64_inc(&alg->encrypt_cnt);
-               atomic64_add(req->cryptlen, &alg->encrypt_tlen);
+               atomic64_add(cryptlen, &alg->encrypt_tlen);
        }
+       crypto_alg_put(alg);
 #endif
 }
 
-static inline void crypto_stat_skcipher_decrypt(struct skcipher_request *req,
+static inline void crypto_stat_skcipher_decrypt(unsigned int cryptlen,
                                                int ret, struct crypto_alg *alg)
 {
 #ifdef CONFIG_CRYPTO_STATS
@@ -507,8 +508,9 @@ static inline void crypto_stat_skcipher_decrypt(struct 
skcipher_request *req,
                atomic64_inc(&alg->cipher_err_cnt);
        } else {
                atomic64_inc(&alg->decrypt_cnt);
-               atomic64_add(req->cryptlen, &alg->decrypt_tlen);
+               atomic64_add(cryptlen, &alg->decrypt_tlen);
        }
+       crypto_alg_put(alg);
 #endif
 }
 
@@ -526,13 +528,18 @@ static inline void crypto_stat_skcipher_decrypt(struct 
skcipher_request *req,
 static inline int crypto_skcipher_encrypt(struct skcipher_request *req)
 {
        struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+       struct crypto_alg *alg = tfm->base.__crt_alg;
+       unsigned int cryptlen = req->cryptlen;
        int ret;
 
+#ifdef CONFIG_CRYPTO_STATS
+       crypto_alg_get(alg);
+#endif
        if (crypto_skcipher_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
                ret = -ENOKEY;
        else
                ret = tfm->encrypt(req);
-       crypto_stat_skcipher_encrypt(req, ret, tfm->base.__crt_alg);
+       crypto_stat_skcipher_encrypt(cryptlen, ret, alg);
        return ret;
 }
 
@@ -550,13 +557,18 @@ static inline int crypto_skcipher_encrypt(struct 
skcipher_request *req)
 static inline int crypto_skcipher_decrypt(struct skcipher_request *req)
 {
        struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+       struct crypto_alg *alg = tfm->base.__crt_alg;
+       unsigned int cryptlen = req->cryptlen;
        int ret;
 
+#ifdef CONFIG_CRYPTO_STATS
+       crypto_alg_get(alg);
+#endif
        if (crypto_skcipher_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
                ret = -ENOKEY;
        else
                ret = tfm->decrypt(req);
-       crypto_stat_skcipher_decrypt(req, ret, tfm->base.__crt_alg);
+       crypto_stat_skcipher_decrypt(cryptlen, ret, alg);
        return ret;
 }

This should not have any overhead with !CONFIG_CRYPTO_STATS
The only drawback is that crypto_alg_get/crypto_alg_put need to be moved out of 
crypto/internal.h to include/linux/crypto.h

Herbert what do you think about that ?

Regards

Reply via email to