crypto: testmgr - test in-place en/decryption with two sglists

As was established in the thread
https://lore.kernel.org/linux-crypto/20220223080400.139367-1-gilad@benyossef.com/T/#u,
many crypto API users doing in-place en/decryption don't use the same
scatterlist pointers for the source and destination, but rather use
separate scatterlists that point to the same memory.  This case isn't
tested by the self-tests, resulting in bugs.

This is the natural usage of the crypto API in some cases, so requiring
API users to avoid this usage is not reasonable.

Therefore, update the self-tests to start testing this case.

Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
Eric Biggers 2022-03-26 00:11:59 -07:00 committed by Herbert Xu
parent f16a005cde
commit f17f9e9069
1 changed files with 63 additions and 12 deletions

View File

@ -232,6 +232,20 @@ enum finalization_type {
FINALIZATION_TYPE_DIGEST, /* use digest() */ FINALIZATION_TYPE_DIGEST, /* use digest() */
}; };
/*
* Whether the crypto operation will occur in-place, and if so whether the
* source and destination scatterlist pointers will coincide (req->src ==
* req->dst), or whether they'll merely point to two separate scatterlists
* (req->src != req->dst) that reference the same underlying memory.
*
* This is only relevant for algorithm types that support in-place operation.
*/
enum inplace_mode {
OUT_OF_PLACE,
INPLACE_ONE_SGLIST,
INPLACE_TWO_SGLISTS,
};
#define TEST_SG_TOTAL 10000 #define TEST_SG_TOTAL 10000
/** /**
@ -265,7 +279,7 @@ struct test_sg_division {
* crypto test vector can be tested. * crypto test vector can be tested.
* *
* @name: name of this config, logged for debugging purposes if a test fails * @name: name of this config, logged for debugging purposes if a test fails
* @inplace: operate on the data in-place, if applicable for the algorithm type? * @inplace_mode: whether and how to operate on the data in-place, if applicable
* @req_flags: extra request_flags, e.g. CRYPTO_TFM_REQ_MAY_SLEEP * @req_flags: extra request_flags, e.g. CRYPTO_TFM_REQ_MAY_SLEEP
* @src_divs: description of how to arrange the source scatterlist * @src_divs: description of how to arrange the source scatterlist
* @dst_divs: description of how to arrange the dst scatterlist, if applicable * @dst_divs: description of how to arrange the dst scatterlist, if applicable
@ -282,7 +296,7 @@ struct test_sg_division {
*/ */
struct testvec_config { struct testvec_config {
const char *name; const char *name;
bool inplace; enum inplace_mode inplace_mode;
u32 req_flags; u32 req_flags;
struct test_sg_division src_divs[XBUFSIZE]; struct test_sg_division src_divs[XBUFSIZE];
struct test_sg_division dst_divs[XBUFSIZE]; struct test_sg_division dst_divs[XBUFSIZE];
@ -307,11 +321,16 @@ struct testvec_config {
/* Configs for skciphers and aeads */ /* Configs for skciphers and aeads */
static const struct testvec_config default_cipher_testvec_configs[] = { static const struct testvec_config default_cipher_testvec_configs[] = {
{ {
.name = "in-place", .name = "in-place (one sglist)",
.inplace = true, .inplace_mode = INPLACE_ONE_SGLIST,
.src_divs = { { .proportion_of_total = 10000 } },
}, {
.name = "in-place (two sglists)",
.inplace_mode = INPLACE_TWO_SGLISTS,
.src_divs = { { .proportion_of_total = 10000 } }, .src_divs = { { .proportion_of_total = 10000 } },
}, { }, {
.name = "out-of-place", .name = "out-of-place",
.inplace_mode = OUT_OF_PLACE,
.src_divs = { { .proportion_of_total = 10000 } }, .src_divs = { { .proportion_of_total = 10000 } },
}, { }, {
.name = "unaligned buffer, offset=1", .name = "unaligned buffer, offset=1",
@ -349,7 +368,7 @@ static const struct testvec_config default_cipher_testvec_configs[] = {
.key_offset = 3, .key_offset = 3,
}, { }, {
.name = "misaligned splits crossing pages, inplace", .name = "misaligned splits crossing pages, inplace",
.inplace = true, .inplace_mode = INPLACE_ONE_SGLIST,
.src_divs = { .src_divs = {
{ {
.proportion_of_total = 7500, .proportion_of_total = 7500,
@ -749,18 +768,39 @@ static int build_cipher_test_sglists(struct cipher_test_sglists *tsgls,
iov_iter_kvec(&input, WRITE, inputs, nr_inputs, src_total_len); iov_iter_kvec(&input, WRITE, inputs, nr_inputs, src_total_len);
err = build_test_sglist(&tsgls->src, cfg->src_divs, alignmask, err = build_test_sglist(&tsgls->src, cfg->src_divs, alignmask,
cfg->inplace ? cfg->inplace_mode != OUT_OF_PLACE ?
max(dst_total_len, src_total_len) : max(dst_total_len, src_total_len) :
src_total_len, src_total_len,
&input, NULL); &input, NULL);
if (err) if (err)
return err; return err;
if (cfg->inplace) { /*
* In-place crypto operations can use the same scatterlist for both the
* source and destination (req->src == req->dst), or can use separate
* scatterlists (req->src != req->dst) which point to the same
* underlying memory. Make sure to test both cases.
*/
if (cfg->inplace_mode == INPLACE_ONE_SGLIST) {
tsgls->dst.sgl_ptr = tsgls->src.sgl; tsgls->dst.sgl_ptr = tsgls->src.sgl;
tsgls->dst.nents = tsgls->src.nents; tsgls->dst.nents = tsgls->src.nents;
return 0; return 0;
} }
if (cfg->inplace_mode == INPLACE_TWO_SGLISTS) {
/*
* For now we keep it simple and only test the case where the
* two scatterlists have identical entries, rather than
* different entries that split up the same memory differently.
*/
memcpy(tsgls->dst.sgl, tsgls->src.sgl,
tsgls->src.nents * sizeof(tsgls->src.sgl[0]));
memcpy(tsgls->dst.sgl_saved, tsgls->src.sgl,
tsgls->src.nents * sizeof(tsgls->src.sgl[0]));
tsgls->dst.sgl_ptr = tsgls->dst.sgl;
tsgls->dst.nents = tsgls->src.nents;
return 0;
}
/* Out of place */
return build_test_sglist(&tsgls->dst, return build_test_sglist(&tsgls->dst,
cfg->dst_divs[0].proportion_of_total ? cfg->dst_divs[0].proportion_of_total ?
cfg->dst_divs : cfg->src_divs, cfg->dst_divs : cfg->src_divs,
@ -995,9 +1035,19 @@ static void generate_random_testvec_config(struct testvec_config *cfg,
p += scnprintf(p, end - p, "random:"); p += scnprintf(p, end - p, "random:");
if (prandom_u32() % 2 == 0) { switch (prandom_u32() % 4) {
cfg->inplace = true; case 0:
p += scnprintf(p, end - p, " inplace"); case 1:
cfg->inplace_mode = OUT_OF_PLACE;
break;
case 2:
cfg->inplace_mode = INPLACE_ONE_SGLIST;
p += scnprintf(p, end - p, " inplace_one_sglist");
break;
default:
cfg->inplace_mode = INPLACE_TWO_SGLISTS;
p += scnprintf(p, end - p, " inplace_two_sglists");
break;
} }
if (prandom_u32() % 2 == 0) { if (prandom_u32() % 2 == 0) {
@ -1034,7 +1084,7 @@ static void generate_random_testvec_config(struct testvec_config *cfg,
cfg->req_flags); cfg->req_flags);
p += scnprintf(p, end - p, "]"); p += scnprintf(p, end - p, "]");
if (!cfg->inplace && prandom_u32() % 2 == 0) { if (cfg->inplace_mode == OUT_OF_PLACE && prandom_u32() % 2 == 0) {
p += scnprintf(p, end - p, " dst_divs=["); p += scnprintf(p, end - p, " dst_divs=[");
p = generate_random_sgl_divisions(cfg->dst_divs, p = generate_random_sgl_divisions(cfg->dst_divs,
ARRAY_SIZE(cfg->dst_divs), ARRAY_SIZE(cfg->dst_divs),
@ -2085,7 +2135,8 @@ static int test_aead_vec_cfg(int enc, const struct aead_testvec *vec,
/* Check for the correct output (ciphertext or plaintext) */ /* Check for the correct output (ciphertext or plaintext) */
err = verify_correct_output(&tsgls->dst, enc ? vec->ctext : vec->ptext, err = verify_correct_output(&tsgls->dst, enc ? vec->ctext : vec->ptext,
enc ? vec->clen : vec->plen, enc ? vec->clen : vec->plen,
vec->alen, enc || !cfg->inplace); vec->alen,
enc || cfg->inplace_mode == OUT_OF_PLACE);
if (err == -EOVERFLOW) { if (err == -EOVERFLOW) {
pr_err("alg: aead: %s %s overran dst buffer on test vector %s, cfg=\"%s\"\n", pr_err("alg: aead: %s %s overran dst buffer on test vector %s, cfg=\"%s\"\n",
driver, op, vec_name, cfg->name); driver, op, vec_name, cfg->name);