]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
crypto: sha512-mb - Protect sha512 mb ctx mgr access
authorTim Chen <tim.c.chen@linux.intel.com>
Wed, 1 Feb 2017 18:45:02 +0000 (10:45 -0800)
committerHerbert Xu <herbert@gondor.apana.org.au>
Sat, 11 Feb 2017 09:50:41 +0000 (17:50 +0800)
The flusher and regular multi-buffer computation via mcryptd may race with another.
Add here a lock and turn off interrupt to to access multi-buffer
computation state cstate->mgr before a round of computation. This should
prevent the flusher code jumping in.

Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
arch/x86/crypto/sha512-mb/sha512_mb.c

index 9c1bb6d58141c9012d1d78e7227de267da582e49..2dd3674b5a1e468357fee68b4335ed17fa5e50c0 100644 (file)
@@ -221,7 +221,7 @@ static struct sha512_hash_ctx *sha512_ctx_mgr_resubmit
 }
 
 static struct sha512_hash_ctx
-               *sha512_ctx_mgr_get_comp_ctx(struct sha512_ctx_mgr *mgr)
+               *sha512_ctx_mgr_get_comp_ctx(struct mcryptd_alg_cstate *cstate)
 {
        /*
         * If get_comp_job returns NULL, there are no jobs complete.
@@ -233,11 +233,17 @@ static struct sha512_hash_ctx
         * Otherwise, all jobs currently being managed by the hash_ctx_mgr
         * still need processing.
         */
+       struct sha512_ctx_mgr *mgr;
        struct sha512_hash_ctx *ctx;
+       unsigned long flags;
 
+       mgr = cstate->mgr;
+       spin_lock_irqsave(&cstate->work_lock, flags);
        ctx = (struct sha512_hash_ctx *)
                                sha512_job_mgr_get_comp_job(&mgr->mgr);
-       return sha512_ctx_mgr_resubmit(mgr, ctx);
+       ctx = sha512_ctx_mgr_resubmit(mgr, ctx);
+       spin_unlock_irqrestore(&cstate->work_lock, flags);
+       return ctx;
 }
 
 static void sha512_ctx_mgr_init(struct sha512_ctx_mgr *mgr)
@@ -246,12 +252,17 @@ static void sha512_ctx_mgr_init(struct sha512_ctx_mgr *mgr)
 }
 
 static struct sha512_hash_ctx
-                       *sha512_ctx_mgr_submit(struct sha512_ctx_mgr *mgr,
+                       *sha512_ctx_mgr_submit(struct mcryptd_alg_cstate *cstate,
                                          struct sha512_hash_ctx *ctx,
                                          const void *buffer,
                                          uint32_t len,
                                          int flags)
 {
+       struct sha512_ctx_mgr *mgr;
+       unsigned long irqflags;
+
+       mgr = cstate->mgr;
+       spin_lock_irqsave(&cstate->work_lock, irqflags);
        if (flags & (~HASH_ENTIRE)) {
                /*
                 * User should not pass anything other than FIRST, UPDATE, or
@@ -351,20 +362,26 @@ static struct sha512_hash_ctx
                }
        }
 
-       return sha512_ctx_mgr_resubmit(mgr, ctx);
+       ctx = sha512_ctx_mgr_resubmit(mgr, ctx);
+       spin_unlock_irqrestore(&cstate->work_lock, irqflags);
+       return ctx;
 }
 
-static struct sha512_hash_ctx *sha512_ctx_mgr_flush(struct sha512_ctx_mgr *mgr)
+static struct sha512_hash_ctx *sha512_ctx_mgr_flush(struct mcryptd_alg_cstate *cstate)
 {
+       struct sha512_ctx_mgr *mgr;
        struct sha512_hash_ctx *ctx;
+       unsigned long flags;
 
+       mgr = cstate->mgr;
+       spin_lock_irqsave(&cstate->work_lock, flags);
        while (1) {
                ctx = (struct sha512_hash_ctx *)
                                        sha512_job_mgr_flush(&mgr->mgr);
 
                /* If flush returned 0, there are no more jobs in flight. */
                if (!ctx)
-                       return NULL;
+                       break;
 
                /*
                 * If flush returned a job, resubmit the job to finish
@@ -378,8 +395,10 @@ static struct sha512_hash_ctx *sha512_ctx_mgr_flush(struct sha512_ctx_mgr *mgr)
                 * the sha512_ctx_mgr still need processing. Loop.
                 */
                if (ctx)
-                       return ctx;
+                       break;
        }
+       spin_unlock_irqrestore(&cstate->work_lock, flags);
+       return ctx;
 }
 
 static int sha512_mb_init(struct ahash_request *areq)
@@ -439,11 +458,11 @@ static int sha_finish_walk(struct mcryptd_hash_request_ctx **ret_rctx,
                sha_ctx = (struct sha512_hash_ctx *)
                                                ahash_request_ctx(&rctx->areq);
                kernel_fpu_begin();
-               sha_ctx = sha512_ctx_mgr_submit(cstate->mgr, sha_ctx,
+               sha_ctx = sha512_ctx_mgr_submit(cstate, sha_ctx,
                                                rctx->walk.data, nbytes, flag);
                if (!sha_ctx) {
                        if (flush)
-                               sha_ctx = sha512_ctx_mgr_flush(cstate->mgr);
+                               sha_ctx = sha512_ctx_mgr_flush(cstate);
                }
                kernel_fpu_end();
                if (sha_ctx)
@@ -471,11 +490,12 @@ static int sha_complete_job(struct mcryptd_hash_request_ctx *rctx,
        struct sha512_hash_ctx *sha_ctx;
        struct mcryptd_hash_request_ctx *req_ctx;
        int ret;
+       unsigned long flags;
 
        /* remove from work list */
-       spin_lock(&cstate->work_lock);
+       spin_lock_irqsave(&cstate->work_lock, flags);
        list_del(&rctx->waiter);
-       spin_unlock(&cstate->work_lock);
+       spin_unlock_irqrestore(&cstate->work_lock, flags);
 
        if (irqs_disabled())
                rctx->complete(&req->base, err);
@@ -486,14 +506,14 @@ static int sha_complete_job(struct mcryptd_hash_request_ctx *rctx,
        }
 
        /* check to see if there are other jobs that are done */
-       sha_ctx = sha512_ctx_mgr_get_comp_ctx(cstate->mgr);
+       sha_ctx = sha512_ctx_mgr_get_comp_ctx(cstate);
        while (sha_ctx) {
                req_ctx = cast_hash_to_mcryptd_ctx(sha_ctx);
                ret = sha_finish_walk(&req_ctx, cstate, false);
                if (req_ctx) {
-                       spin_lock(&cstate->work_lock);
+                       spin_lock_irqsave(&cstate->work_lock, flags);
                        list_del(&req_ctx->waiter);
-                       spin_unlock(&cstate->work_lock);
+                       spin_unlock_irqrestore(&cstate->work_lock, flags);
 
                        req = cast_mcryptd_ctx_to_req(req_ctx);
                        if (irqs_disabled())
@@ -504,7 +524,7 @@ static int sha_complete_job(struct mcryptd_hash_request_ctx *rctx,
                                local_bh_enable();
                        }
                }
-               sha_ctx = sha512_ctx_mgr_get_comp_ctx(cstate->mgr);
+               sha_ctx = sha512_ctx_mgr_get_comp_ctx(cstate);
        }
 
        return 0;
@@ -515,6 +535,7 @@ static void sha512_mb_add_list(struct mcryptd_hash_request_ctx *rctx,
 {
        unsigned long next_flush;
        unsigned long delay = usecs_to_jiffies(FLUSH_INTERVAL);
+       unsigned long flags;
 
        /* initialize tag */
        rctx->tag.arrival = jiffies;    /* tag the arrival time */
@@ -522,9 +543,9 @@ static void sha512_mb_add_list(struct mcryptd_hash_request_ctx *rctx,
        next_flush = rctx->tag.arrival + delay;
        rctx->tag.expire = next_flush;
 
-       spin_lock(&cstate->work_lock);
+       spin_lock_irqsave(&cstate->work_lock, flags);
        list_add_tail(&rctx->waiter, &cstate->work_list);
-       spin_unlock(&cstate->work_lock);
+       spin_unlock_irqrestore(&cstate->work_lock, flags);
 
        mcryptd_arm_flusher(cstate, delay);
 }
@@ -565,7 +586,7 @@ static int sha512_mb_update(struct ahash_request *areq)
        sha_ctx = (struct sha512_hash_ctx *) ahash_request_ctx(areq);
        sha512_mb_add_list(rctx, cstate);
        kernel_fpu_begin();
-       sha_ctx = sha512_ctx_mgr_submit(cstate->mgr, sha_ctx, rctx->walk.data,
+       sha_ctx = sha512_ctx_mgr_submit(cstate, sha_ctx, rctx->walk.data,
                                                        nbytes, HASH_UPDATE);
        kernel_fpu_end();
 
@@ -628,7 +649,7 @@ static int sha512_mb_finup(struct ahash_request *areq)
        sha512_mb_add_list(rctx, cstate);
 
        kernel_fpu_begin();
-       sha_ctx = sha512_ctx_mgr_submit(cstate->mgr, sha_ctx, rctx->walk.data,
+       sha_ctx = sha512_ctx_mgr_submit(cstate, sha_ctx, rctx->walk.data,
                                                                nbytes, flag);
        kernel_fpu_end();
 
@@ -677,8 +698,7 @@ static int sha512_mb_final(struct ahash_request *areq)
        /* flag HASH_FINAL and 0 data size */
        sha512_mb_add_list(rctx, cstate);
        kernel_fpu_begin();
-       sha_ctx = sha512_ctx_mgr_submit(cstate->mgr, sha_ctx, &data, 0,
-                                                               HASH_LAST);
+       sha_ctx = sha512_ctx_mgr_submit(cstate, sha_ctx, &data, 0, HASH_LAST);
        kernel_fpu_end();
 
        /* check if anything is returned */
@@ -940,7 +960,7 @@ static unsigned long sha512_mb_flusher(struct mcryptd_alg_cstate *cstate)
                        break;
                kernel_fpu_begin();
                sha_ctx = (struct sha512_hash_ctx *)
-                                       sha512_ctx_mgr_flush(cstate->mgr);
+                                       sha512_ctx_mgr_flush(cstate);
                kernel_fpu_end();
                if (!sha_ctx) {
                        pr_err("sha512_mb error: nothing got flushed for"