crypto: testmgr - test the !may_use_simd() fallback code
authorEric Biggers <ebiggers@google.com>
Wed, 13 Mar 2019 05:12:52 +0000 (22:12 -0700)
committerHerbert Xu <herbert@gondor.apana.org.au>
Fri, 22 Mar 2019 12:57:28 +0000 (20:57 +0800)
All crypto API algorithms are supposed to support the case where they
are called in a context where SIMD instructions are unusable, e.g. IRQ
context on some architectures.  However, this isn't tested for by the
self-tests, causing bugs to go undetected.

Now that all algorithms have been converted to use crypto_simd_usable(),
update the self-tests to test the no-SIMD case.  First, a bool
testvec_config::nosimd is added.  When set, the crypto operation is
executed with preemption disabled and with crypto_simd_usable() mocked
out to return false on the current CPU.

A bool test_sg_division::nosimd is also added.  For hash algorithms it's
honored by the corresponding ->update().  By setting just a subset of
these bools, the case where some ->update()s are done in SIMD context
and some are done in no-SIMD context is also tested.

These bools are then randomly set by generate_random_testvec_config().

For now, all no-SIMD testing is limited to the extra crypto self-tests,
because it might be a bit too invasive for the regular self-tests.
But this could be changed later.

This has already found bugs in the arm64 AES-GCM and ChaCha algorithms.
This would have found some past bugs as well.

Signed-off-by: Eric Biggers <ebiggers@google.com>
Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
crypto/testmgr.c

index 52417dde811f19a8a2ee175220c27278d283e618..2c2ddebb48d3772af3e583bde4591c1e24557de3 100644 (file)
@@ -234,12 +234,14 @@ enum finalization_type {
  *                               @offset
  * @flush_type: for hashes, whether an update() should be done now vs.
  *             continuing to accumulate data
+ * @nosimd: if doing the pending update(), do it with SIMD disabled?
  */
 struct test_sg_division {
        unsigned int proportion_of_total;
        unsigned int offset;
        bool offset_relative_to_alignmask;
        enum flush_type flush_type;
+       bool nosimd;
 };
 
 /**
@@ -259,6 +261,7 @@ struct test_sg_division {
  * @iv_offset_relative_to_alignmask: if true, add the algorithm's alignmask to
  *                                  the @iv_offset
  * @finalization_type: what finalization function to use for hashes
+ * @nosimd: execute with SIMD disabled?  Requires !CRYPTO_TFM_REQ_MAY_SLEEP.
  */
 struct testvec_config {
        const char *name;
@@ -269,6 +272,7 @@ struct testvec_config {
        unsigned int iv_offset;
        bool iv_offset_relative_to_alignmask;
        enum finalization_type finalization_type;
+       bool nosimd;
 };
 
 #define TESTVEC_CONFIG_NAMELEN 192
@@ -420,8 +424,11 @@ static unsigned int count_test_sg_divisions(const struct test_sg_division *divs)
        return ndivs;
 }
 
+#define SGDIVS_HAVE_FLUSHES    BIT(0)
+#define SGDIVS_HAVE_NOSIMD     BIT(1)
+
 static bool valid_sg_divisions(const struct test_sg_division *divs,
-                              unsigned int count, bool *any_flushes_ret)
+                              unsigned int count, int *flags_ret)
 {
        unsigned int total = 0;
        unsigned int i;
@@ -432,7 +439,9 @@ static bool valid_sg_divisions(const struct test_sg_division *divs,
                        return false;
                total += divs[i].proportion_of_total;
                if (divs[i].flush_type != FLUSH_TYPE_NONE)
-                       *any_flushes_ret = true;
+                       *flags_ret |= SGDIVS_HAVE_FLUSHES;
+               if (divs[i].nosimd)
+                       *flags_ret |= SGDIVS_HAVE_NOSIMD;
        }
        return total == TEST_SG_TOTAL &&
                memchr_inv(&divs[i], 0, (count - i) * sizeof(divs[0])) == NULL;
@@ -445,19 +454,18 @@ static bool valid_sg_divisions(const struct test_sg_division *divs,
  */
 static bool valid_testvec_config(const struct testvec_config *cfg)
 {
-       bool any_flushes = false;
+       int flags = 0;
 
        if (cfg->name == NULL)
                return false;
 
        if (!valid_sg_divisions(cfg->src_divs, ARRAY_SIZE(cfg->src_divs),
-                               &any_flushes))
+                               &flags))
                return false;
 
        if (cfg->dst_divs[0].proportion_of_total) {
                if (!valid_sg_divisions(cfg->dst_divs,
-                                       ARRAY_SIZE(cfg->dst_divs),
-                                       &any_flushes))
+                                       ARRAY_SIZE(cfg->dst_divs), &flags))
                        return false;
        } else {
                if (memchr_inv(cfg->dst_divs, 0, sizeof(cfg->dst_divs)))
@@ -470,7 +478,12 @@ static bool valid_testvec_config(const struct testvec_config *cfg)
            MAX_ALGAPI_ALIGNMASK + 1)
                return false;
 
-       if (any_flushes && cfg->finalization_type == FINALIZATION_TYPE_DIGEST)
+       if ((flags & (SGDIVS_HAVE_FLUSHES | SGDIVS_HAVE_NOSIMD)) &&
+           cfg->finalization_type == FINALIZATION_TYPE_DIGEST)
+               return false;
+
+       if ((cfg->nosimd || (flags & SGDIVS_HAVE_NOSIMD)) &&
+           (cfg->req_flags & CRYPTO_TFM_REQ_MAY_SLEEP))
                return false;
 
        return true;
@@ -731,13 +744,14 @@ static int build_cipher_test_sglists(struct cipher_test_sglists *tsgls,
 #ifdef CONFIG_CRYPTO_MANAGER_EXTRA_TESTS
 static char *generate_random_sgl_divisions(struct test_sg_division *divs,
                                           size_t max_divs, char *p, char *end,
-                                          bool gen_flushes)
+                                          bool gen_flushes, u32 req_flags)
 {
        struct test_sg_division *div = divs;
        unsigned int remaining = TEST_SG_TOTAL;
 
        do {
                unsigned int this_len;
+               const char *flushtype_str;
 
                if (div == &divs[max_divs - 1] || prandom_u32() % 2 == 0)
                        this_len = remaining;
@@ -766,11 +780,31 @@ static char *generate_random_sgl_divisions(struct test_sg_division *divs,
                        }
                }
 
+               if (div->flush_type != FLUSH_TYPE_NONE &&
+                   !(req_flags & CRYPTO_TFM_REQ_MAY_SLEEP) &&
+                   prandom_u32() % 2 == 0)
+                       div->nosimd = true;
+
+               switch (div->flush_type) {
+               case FLUSH_TYPE_FLUSH:
+                       if (div->nosimd)
+                               flushtype_str = "<flush,nosimd>";
+                       else
+                               flushtype_str = "<flush>";
+                       break;
+               case FLUSH_TYPE_REIMPORT:
+                       if (div->nosimd)
+                               flushtype_str = "<reimport,nosimd>";
+                       else
+                               flushtype_str = "<reimport>";
+                       break;
+               default:
+                       flushtype_str = "";
+                       break;
+               }
+
                BUILD_BUG_ON(TEST_SG_TOTAL != 10000); /* for "%u.%u%%" */
-               p += scnprintf(p, end - p, "%s%u.%u%%@%s+%u%s",
-                              div->flush_type == FLUSH_TYPE_NONE ? "" :
-                              div->flush_type == FLUSH_TYPE_FLUSH ?
-                              "<flush> " : "<reimport> ",
+               p += scnprintf(p, end - p, "%s%u.%u%%@%s+%u%s", flushtype_str,
                               this_len / 100, this_len % 100,
                               div->offset_relative_to_alignmask ?
                                        "alignmask" : "",
@@ -820,18 +854,26 @@ static void generate_random_testvec_config(struct testvec_config *cfg,
                break;
        }
 
+       if (!(cfg->req_flags & CRYPTO_TFM_REQ_MAY_SLEEP) &&
+           prandom_u32() % 2 == 0) {
+               cfg->nosimd = true;
+               p += scnprintf(p, end - p, " nosimd");
+       }
+
        p += scnprintf(p, end - p, " src_divs=[");
        p = generate_random_sgl_divisions(cfg->src_divs,
                                          ARRAY_SIZE(cfg->src_divs), p, end,
                                          (cfg->finalization_type !=
-                                          FINALIZATION_TYPE_DIGEST));
+                                          FINALIZATION_TYPE_DIGEST),
+                                         cfg->req_flags);
        p += scnprintf(p, end - p, "]");
 
        if (!cfg->inplace && prandom_u32() % 2 == 0) {
                p += scnprintf(p, end - p, " dst_divs=[");
                p = generate_random_sgl_divisions(cfg->dst_divs,
                                                  ARRAY_SIZE(cfg->dst_divs),
-                                                 p, end, false);
+                                                 p, end, false,
+                                                 cfg->req_flags);
                p += scnprintf(p, end - p, "]");
        }
 
@@ -864,6 +906,23 @@ static void crypto_reenable_simd_for_test(void)
 }
 #endif /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */
 
+static int do_ahash_op(int (*op)(struct ahash_request *req),
+                      struct ahash_request *req,
+                      struct crypto_wait *wait, bool nosimd)
+{
+       int err;
+
+       if (nosimd)
+               crypto_disable_simd_for_test();
+
+       err = op(req);
+
+       if (nosimd)
+               crypto_reenable_simd_for_test();
+
+       return crypto_wait_req(err, wait);
+}
+
 static int check_nonfinal_hash_op(const char *op, int err,
                                  u8 *result, unsigned int digestsize,
                                  const char *driver, unsigned int vec_num,
@@ -938,7 +997,7 @@ static int test_hash_vec_cfg(const char *driver,
                ahash_request_set_callback(req, req_flags, crypto_req_done,
                                           &wait);
                ahash_request_set_crypt(req, tsgl->sgl, result, vec->psize);
-               err = crypto_wait_req(crypto_ahash_digest(req), &wait);
+               err = do_ahash_op(crypto_ahash_digest, req, &wait, cfg->nosimd);
                if (err) {
                        pr_err("alg: hash: %s digest() failed with err %d on test vector %u, cfg=\"%s\"\n",
                               driver, err, vec_num, cfg->name);
@@ -951,7 +1010,7 @@ static int test_hash_vec_cfg(const char *driver,
 
        ahash_request_set_callback(req, req_flags, crypto_req_done, &wait);
        ahash_request_set_crypt(req, NULL, result, 0);
-       err = crypto_wait_req(crypto_ahash_init(req), &wait);
+       err = do_ahash_op(crypto_ahash_init, req, &wait, cfg->nosimd);
        err = check_nonfinal_hash_op("init", err, result, digestsize,
                                     driver, vec_num, cfg);
        if (err)
@@ -967,7 +1026,8 @@ static int test_hash_vec_cfg(const char *driver,
                                                   crypto_req_done, &wait);
                        ahash_request_set_crypt(req, pending_sgl, result,
                                                pending_len);
-                       err = crypto_wait_req(crypto_ahash_update(req), &wait);
+                       err = do_ahash_op(crypto_ahash_update, req, &wait,
+                                         divs[i]->nosimd);
                        err = check_nonfinal_hash_op("update", err,
                                                     result, digestsize,
                                                     driver, vec_num, cfg);
@@ -1010,12 +1070,12 @@ static int test_hash_vec_cfg(const char *driver,
        ahash_request_set_crypt(req, pending_sgl, result, pending_len);
        if (cfg->finalization_type == FINALIZATION_TYPE_FINAL) {
                /* finish with update() and final() */
-               err = crypto_wait_req(crypto_ahash_update(req), &wait);
+               err = do_ahash_op(crypto_ahash_update, req, &wait, cfg->nosimd);
                err = check_nonfinal_hash_op("update", err, result, digestsize,
                                             driver, vec_num, cfg);
                if (err)
                        return err;
-               err = crypto_wait_req(crypto_ahash_final(req), &wait);
+               err = do_ahash_op(crypto_ahash_final, req, &wait, cfg->nosimd);
                if (err) {
                        pr_err("alg: hash: %s final() failed with err %d on test vector %u, cfg=\"%s\"\n",
                               driver, err, vec_num, cfg->name);
@@ -1023,7 +1083,7 @@ static int test_hash_vec_cfg(const char *driver,
                }
        } else {
                /* finish with finup() */
-               err = crypto_wait_req(crypto_ahash_finup(req), &wait);
+               err = do_ahash_op(crypto_ahash_finup, req, &wait, cfg->nosimd);
                if (err) {
                        pr_err("alg: hash: %s finup() failed with err %d on test vector %u, cfg=\"%s\"\n",
                               driver, err, vec_num, cfg->name);
@@ -1259,8 +1319,12 @@ static int test_aead_vec_cfg(const char *driver, int enc,
        aead_request_set_crypt(req, tsgls->src.sgl_ptr, tsgls->dst.sgl_ptr,
                               enc ? vec->plen : vec->clen, iv);
        aead_request_set_ad(req, vec->alen);
-       err = crypto_wait_req(enc ? crypto_aead_encrypt(req) :
-                             crypto_aead_decrypt(req), &wait);
+       if (cfg->nosimd)
+               crypto_disable_simd_for_test();
+       err = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
+       if (cfg->nosimd)
+               crypto_reenable_simd_for_test();
+       err = crypto_wait_req(err, &wait);
        if (err) {
                if (err == -EBADMSG && vec->novrfy)
                        return 0;
@@ -1594,8 +1658,12 @@ static int test_skcipher_vec_cfg(const char *driver, int enc,
        skcipher_request_set_callback(req, req_flags, crypto_req_done, &wait);
        skcipher_request_set_crypt(req, tsgls->src.sgl_ptr, tsgls->dst.sgl_ptr,
                                   vec->len, iv);
-       err = crypto_wait_req(enc ? crypto_skcipher_encrypt(req) :
-                             crypto_skcipher_decrypt(req), &wait);
+       if (cfg->nosimd)
+               crypto_disable_simd_for_test();
+       err = enc ? crypto_skcipher_encrypt(req) : crypto_skcipher_decrypt(req);
+       if (cfg->nosimd)
+               crypto_reenable_simd_for_test();
+       err = crypto_wait_req(err, &wait);
        if (err) {
                pr_err("alg: skcipher: %s %s failed with err %d on test vector %u, cfg=\"%s\"\n",
                       driver, op, err, vec_num, cfg->name);