Skip to content

Commit

Permalink
crypto: nx - Fix SHA concurrence issue and sg limit bounds
Browse files Browse the repository at this point in the history
NX SHA algorithms stores the message digest into tfm what
cause a concurrence issue where hashes may be replaced by others.
This patch cleans up the cases where it's handling unnecessarily shared
variables in nx context and copies the current msg digest to a sctx->state
in order to safetly handle with the hashe's state.

Also fixes and does some clean ups regarding the right sg max limit
and bounds to the sg list avoind a memory crash.

Signed-off-by: Leonidas S. Barbosa <leosilva@linux.vnet.ibm.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
  • Loading branch information
Leonidas S. Barbosa authored and Herbert Xu committed Nov 6, 2014
1 parent 5313231 commit 0008511
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 230 deletions.
208 changes: 100 additions & 108 deletions drivers/crypto/nx/nx-sha256.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <crypto/sha.h>
#include <linux/module.h>
#include <asm/vio.h>
#include <asm/byteorder.h>

#include "nx_csbcpb.h"
#include "nx.h"
Expand All @@ -32,7 +33,8 @@ static int nx_sha256_init(struct shash_desc *desc)
{
struct sha256_state *sctx = shash_desc_ctx(desc);
struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
struct nx_sg *out_sg;
int len;
int rc;

nx_ctx_init(nx_ctx, HCOP_FC_SHA);

Expand All @@ -41,10 +43,28 @@ static int nx_sha256_init(struct shash_desc *desc)
nx_ctx->ap = &nx_ctx->props[NX_PROPS_SHA256];

NX_CPB_SET_DIGEST_SIZE(nx_ctx->csbcpb, NX_DS_SHA256);
out_sg = nx_build_sg_list(nx_ctx->out_sg, (u8 *)sctx->state,
SHA256_DIGEST_SIZE, nx_ctx->ap->sglen);
nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);

len = SHA256_DIGEST_SIZE;
rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->out_sg,
&nx_ctx->op.outlen,
&len,
(u8 *) sctx->state,
NX_DS_SHA256);

if (rc)
goto out;

sctx->state[0] = __cpu_to_be32(SHA256_H0);
sctx->state[1] = __cpu_to_be32(SHA256_H1);
sctx->state[2] = __cpu_to_be32(SHA256_H2);
sctx->state[3] = __cpu_to_be32(SHA256_H3);
sctx->state[4] = __cpu_to_be32(SHA256_H4);
sctx->state[5] = __cpu_to_be32(SHA256_H5);
sctx->state[6] = __cpu_to_be32(SHA256_H6);
sctx->state[7] = __cpu_to_be32(SHA256_H7);
sctx->count = 0;

out:
return 0;
}

Expand All @@ -54,63 +74,71 @@ static int nx_sha256_update(struct shash_desc *desc, const u8 *data,
struct sha256_state *sctx = shash_desc_ctx(desc);
struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
struct nx_sg *in_sg;
u64 to_process, leftover, total;
u32 max_sg_len;
u64 to_process = 0, leftover, total;
unsigned long irq_flags;
int rc = 0;
int data_len;
u64 buf_len = (sctx->count % SHA256_BLOCK_SIZE);

spin_lock_irqsave(&nx_ctx->lock, irq_flags);

/* 2 cases for total data len:
* 1: < SHA256_BLOCK_SIZE: copy into state, return 0
* 2: >= SHA256_BLOCK_SIZE: process X blocks, copy in leftover
*/
total = sctx->count + len;
total = (sctx->count % SHA256_BLOCK_SIZE) + len;
if (total < SHA256_BLOCK_SIZE) {
memcpy(sctx->buf + sctx->count, data, len);
memcpy(sctx->buf + buf_len, data, len);
sctx->count += len;
goto out;
}

in_sg = nx_ctx->in_sg;
max_sg_len = min_t(u32, nx_driver.of.max_sg_len/sizeof(struct nx_sg),
nx_ctx->ap->sglen);
memcpy(csbcpb->cpb.sha256.message_digest, sctx->state, SHA256_DIGEST_SIZE);
NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION;

do {
/*
* to_process: the SHA256_BLOCK_SIZE data chunk to process in
* this update. This value is also restricted by the sg list
* limits.
*/
to_process = min_t(u64, total, nx_ctx->ap->databytelen);
to_process = min_t(u64, to_process,
NX_PAGE_SIZE * (max_sg_len - 1));
to_process = total - to_process;
to_process = to_process & ~(SHA256_BLOCK_SIZE - 1);
leftover = total - to_process;

if (sctx->count) {
in_sg = nx_build_sg_list(nx_ctx->in_sg,
(u8 *) sctx->buf,
sctx->count, max_sg_len);
if (buf_len) {
data_len = buf_len;
rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
&nx_ctx->op.inlen,
&data_len,
(u8 *) sctx->buf,
NX_DS_SHA256);

if (rc || data_len != buf_len)
goto out;
}
in_sg = nx_build_sg_list(in_sg, (u8 *) data,
to_process - sctx->count,
max_sg_len);
nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) *
sizeof(struct nx_sg);

if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) {
/*
* we've hit the nx chip previously and we're updating
* again, so copy over the partial digest.
*/
memcpy(csbcpb->cpb.sha256.input_partial_digest,

data_len = to_process - buf_len;
rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
&nx_ctx->op.inlen,
&data_len,
(u8 *) data,
NX_DS_SHA256);

if (rc)
goto out;

to_process = (data_len + buf_len);
leftover = total - to_process;

/*
* we've hit the nx chip previously and we're updating
* again, so copy over the partial digest.
*/
memcpy(csbcpb->cpb.sha256.input_partial_digest,
csbcpb->cpb.sha256.message_digest,
SHA256_DIGEST_SIZE);
}

NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
if (!nx_ctx->op.inlen || !nx_ctx->op.outlen) {
rc = -EINVAL;
goto out;
Expand All @@ -122,22 +150,19 @@ static int nx_sha256_update(struct shash_desc *desc, const u8 *data,
goto out;

atomic_inc(&(nx_ctx->stats->sha256_ops));
csbcpb->cpb.sha256.message_bit_length += (u64)
(csbcpb->cpb.sha256.spbc * 8);

/* everything after the first update is continuation */
NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION;

total -= to_process;
data += to_process - sctx->count;
sctx->count = 0;
in_sg = nx_ctx->in_sg;
data += to_process - buf_len;
buf_len = 0;

} while (leftover >= SHA256_BLOCK_SIZE);

/* copy the leftover back into the state struct */
if (leftover)
memcpy(sctx->buf, data, leftover);
sctx->count = leftover;

sctx->count += len;
memcpy(sctx->state, csbcpb->cpb.sha256.message_digest, SHA256_DIGEST_SIZE);
out:
spin_unlock_irqrestore(&nx_ctx->lock, irq_flags);
return rc;
Expand All @@ -148,34 +173,46 @@ static int nx_sha256_final(struct shash_desc *desc, u8 *out)
struct sha256_state *sctx = shash_desc_ctx(desc);
struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
struct nx_sg *in_sg, *out_sg;
u32 max_sg_len;
unsigned long irq_flags;
int rc;
int len;

spin_lock_irqsave(&nx_ctx->lock, irq_flags);

max_sg_len = min_t(u32, nx_driver.of.max_sg_len, nx_ctx->ap->sglen);

if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) {
/* final is represented by continuing the operation and indicating that
* this is not an intermediate operation */
if (sctx->count >= SHA256_BLOCK_SIZE) {
/* we've hit the nx chip previously, now we're finalizing,
* so copy over the partial digest */
memcpy(csbcpb->cpb.sha256.input_partial_digest,
csbcpb->cpb.sha256.message_digest, SHA256_DIGEST_SIZE);
memcpy(csbcpb->cpb.sha256.input_partial_digest, sctx->state, SHA256_DIGEST_SIZE);
NX_CPB_FDM(csbcpb) &= ~NX_FDM_INTERMEDIATE;
NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION;
} else {
NX_CPB_FDM(csbcpb) &= ~NX_FDM_INTERMEDIATE;
NX_CPB_FDM(csbcpb) &= ~NX_FDM_CONTINUATION;
}

/* final is represented by continuing the operation and indicating that
* this is not an intermediate operation */
NX_CPB_FDM(csbcpb) &= ~NX_FDM_INTERMEDIATE;
csbcpb->cpb.sha256.message_bit_length = (u64) (sctx->count * 8);

csbcpb->cpb.sha256.message_bit_length += (u64)(sctx->count * 8);
len = sctx->count & (SHA256_BLOCK_SIZE - 1);
rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
&nx_ctx->op.inlen,
&len,
(u8 *) sctx->buf,
NX_DS_SHA256);

in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)sctx->buf,
sctx->count, max_sg_len);
out_sg = nx_build_sg_list(nx_ctx->out_sg, out, SHA256_DIGEST_SIZE,
max_sg_len);
nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg);
nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
if (rc || len != (sctx->count & (SHA256_BLOCK_SIZE - 1)))
goto out;

len = SHA256_DIGEST_SIZE;
rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->out_sg,
&nx_ctx->op.outlen,
&len,
out,
NX_DS_SHA256);

if (rc || len != SHA256_DIGEST_SIZE)
goto out;

if (!nx_ctx->op.outlen) {
rc = -EINVAL;
Expand All @@ -189,8 +226,7 @@ static int nx_sha256_final(struct shash_desc *desc, u8 *out)

atomic_inc(&(nx_ctx->stats->sha256_ops));

atomic64_add(csbcpb->cpb.sha256.message_bit_length / 8,
&(nx_ctx->stats->sha256_bytes));
atomic64_add(sctx->count, &(nx_ctx->stats->sha256_bytes));
memcpy(out, csbcpb->cpb.sha256.message_digest, SHA256_DIGEST_SIZE);
out:
spin_unlock_irqrestore(&nx_ctx->lock, irq_flags);
Expand All @@ -200,62 +236,18 @@ static int nx_sha256_final(struct shash_desc *desc, u8 *out)
static int nx_sha256_export(struct shash_desc *desc, void *out)
{
struct sha256_state *sctx = shash_desc_ctx(desc);
struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
struct sha256_state *octx = out;
unsigned long irq_flags;

spin_lock_irqsave(&nx_ctx->lock, irq_flags);

octx->count = sctx->count +
(csbcpb->cpb.sha256.message_bit_length / 8);
memcpy(octx->buf, sctx->buf, sizeof(octx->buf));

/* if no data has been processed yet, we need to export SHA256's
* initial data, in case this context gets imported into a software
* context */
if (csbcpb->cpb.sha256.message_bit_length)
memcpy(octx->state, csbcpb->cpb.sha256.message_digest,
SHA256_DIGEST_SIZE);
else {
octx->state[0] = SHA256_H0;
octx->state[1] = SHA256_H1;
octx->state[2] = SHA256_H2;
octx->state[3] = SHA256_H3;
octx->state[4] = SHA256_H4;
octx->state[5] = SHA256_H5;
octx->state[6] = SHA256_H6;
octx->state[7] = SHA256_H7;
}
memcpy(out, sctx, sizeof(*sctx));

spin_unlock_irqrestore(&nx_ctx->lock, irq_flags);
return 0;
}

static int nx_sha256_import(struct shash_desc *desc, const void *in)
{
struct sha256_state *sctx = shash_desc_ctx(desc);
struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
const struct sha256_state *ictx = in;
unsigned long irq_flags;

spin_lock_irqsave(&nx_ctx->lock, irq_flags);

memcpy(sctx->buf, ictx->buf, sizeof(ictx->buf));
memcpy(sctx, in, sizeof(*sctx));

sctx->count = ictx->count & 0x3f;
csbcpb->cpb.sha256.message_bit_length = (ictx->count & ~0x3f) * 8;

if (csbcpb->cpb.sha256.message_bit_length) {
memcpy(csbcpb->cpb.sha256.message_digest, ictx->state,
SHA256_DIGEST_SIZE);

NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION;
NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
}

spin_unlock_irqrestore(&nx_ctx->lock, irq_flags);
return 0;
}

Expand Down
Loading

0 comments on commit 0008511

Please sign in to comment.