get_maintainer: add ability to skip moderated mailing lists
[linux-2.6-microblaze.git] / crypto / testmgr.c
index 658a7ee..d0b5b33 100644 (file)
@@ -1032,6 +1032,205 @@ static void crypto_reenable_simd_for_test(void)
 }
 #endif /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */
 
+static int build_hash_sglist(struct test_sglist *tsgl,
+                            const struct hash_testvec *vec,
+                            const struct testvec_config *cfg,
+                            unsigned int alignmask,
+                            const struct test_sg_division *divs[XBUFSIZE])
+{
+       struct kvec kv;
+       struct iov_iter input;
+
+       kv.iov_base = (void *)vec->plaintext;
+       kv.iov_len = vec->psize;
+       iov_iter_kvec(&input, WRITE, &kv, 1, vec->psize);
+       return build_test_sglist(tsgl, cfg->src_divs, alignmask, vec->psize,
+                                &input, divs);
+}
+
+static int check_hash_result(const char *type,
+                            const u8 *result, unsigned int digestsize,
+                            const struct hash_testvec *vec,
+                            const char *vec_name,
+                            const char *driver,
+                            const struct testvec_config *cfg)
+{
+       if (memcmp(result, vec->digest, digestsize) != 0) {
+               pr_err("alg: %s: %s test failed (wrong result) on test vector %s, cfg=\"%s\"\n",
+                      type, driver, vec_name, cfg->name);
+               return -EINVAL;
+       }
+       if (!testmgr_is_poison(&result[digestsize], TESTMGR_POISON_LEN)) {
+               pr_err("alg: %s: %s overran result buffer on test vector %s, cfg=\"%s\"\n",
+                      type, driver, vec_name, cfg->name);
+               return -EOVERFLOW;
+       }
+       return 0;
+}
+
+static inline int check_shash_op(const char *op, int err,
+                                const char *driver, const char *vec_name,
+                                const struct testvec_config *cfg)
+{
+       if (err)
+               pr_err("alg: shash: %s %s() failed with err %d on test vector %s, cfg=\"%s\"\n",
+                      driver, op, err, vec_name, cfg->name);
+       return err;
+}
+
+static inline const void *sg_data(struct scatterlist *sg)
+{
+       return page_address(sg_page(sg)) + sg->offset;
+}
+
+/* Test one hash test vector in one configuration, using the shash API */
+static int test_shash_vec_cfg(const char *driver,
+                             const struct hash_testvec *vec,
+                             const char *vec_name,
+                             const struct testvec_config *cfg,
+                             struct shash_desc *desc,
+                             struct test_sglist *tsgl,
+                             u8 *hashstate)
+{
+       struct crypto_shash *tfm = desc->tfm;
+       const unsigned int alignmask = crypto_shash_alignmask(tfm);
+       const unsigned int digestsize = crypto_shash_digestsize(tfm);
+       const unsigned int statesize = crypto_shash_statesize(tfm);
+       const struct test_sg_division *divs[XBUFSIZE];
+       unsigned int i;
+       u8 result[HASH_MAX_DIGESTSIZE + TESTMGR_POISON_LEN];
+       int err;
+
+       /* Set the key, if specified */
+       if (vec->ksize) {
+               err = crypto_shash_setkey(tfm, vec->key, vec->ksize);
+               if (err) {
+                       if (err == vec->setkey_error)
+                               return 0;
+                       pr_err("alg: shash: %s setkey failed on test vector %s; expected_error=%d, actual_error=%d, flags=%#x\n",
+                              driver, vec_name, vec->setkey_error, err,
+                              crypto_shash_get_flags(tfm));
+                       return err;
+               }
+               if (vec->setkey_error) {
+                       pr_err("alg: shash: %s setkey unexpectedly succeeded on test vector %s; expected_error=%d\n",
+                              driver, vec_name, vec->setkey_error);
+                       return -EINVAL;
+               }
+       }
+
+       /* Build the scatterlist for the source data */
+       err = build_hash_sglist(tsgl, vec, cfg, alignmask, divs);
+       if (err) {
+               pr_err("alg: shash: %s: error preparing scatterlist for test vector %s, cfg=\"%s\"\n",
+                      driver, vec_name, cfg->name);
+               return err;
+       }
+
+       /* Do the actual hashing */
+
+       testmgr_poison(desc->__ctx, crypto_shash_descsize(tfm));
+       testmgr_poison(result, digestsize + TESTMGR_POISON_LEN);
+
+       if (cfg->finalization_type == FINALIZATION_TYPE_DIGEST ||
+           vec->digest_error) {
+               /* Just using digest() */
+               if (tsgl->nents != 1)
+                       return 0;
+               if (cfg->nosimd)
+                       crypto_disable_simd_for_test();
+               err = crypto_shash_digest(desc, sg_data(&tsgl->sgl[0]),
+                                         tsgl->sgl[0].length, result);
+               if (cfg->nosimd)
+                       crypto_reenable_simd_for_test();
+               if (err) {
+                       if (err == vec->digest_error)
+                               return 0;
+                       pr_err("alg: shash: %s digest() failed on test vector %s; expected_error=%d, actual_error=%d, cfg=\"%s\"\n",
+                              driver, vec_name, vec->digest_error, err,
+                              cfg->name);
+                       return err;
+               }
+               if (vec->digest_error) {
+                       pr_err("alg: shash: %s digest() unexpectedly succeeded on test vector %s; expected_error=%d, cfg=\"%s\"\n",
+                              driver, vec_name, vec->digest_error, cfg->name);
+                       return -EINVAL;
+               }
+               goto result_ready;
+       }
+
+       /* Using init(), zero or more update(), then final() or finup() */
+
+       if (cfg->nosimd)
+               crypto_disable_simd_for_test();
+       err = crypto_shash_init(desc);
+       if (cfg->nosimd)
+               crypto_reenable_simd_for_test();
+       err = check_shash_op("init", err, driver, vec_name, cfg);
+       if (err)
+               return err;
+
+       for (i = 0; i < tsgl->nents; i++) {
+               if (i + 1 == tsgl->nents &&
+                   cfg->finalization_type == FINALIZATION_TYPE_FINUP) {
+                       if (divs[i]->nosimd)
+                               crypto_disable_simd_for_test();
+                       err = crypto_shash_finup(desc, sg_data(&tsgl->sgl[i]),
+                                                tsgl->sgl[i].length, result);
+                       if (divs[i]->nosimd)
+                               crypto_reenable_simd_for_test();
+                       err = check_shash_op("finup", err, driver, vec_name,
+                                            cfg);
+                       if (err)
+                               return err;
+                       goto result_ready;
+               }
+               if (divs[i]->nosimd)
+                       crypto_disable_simd_for_test();
+               err = crypto_shash_update(desc, sg_data(&tsgl->sgl[i]),
+                                         tsgl->sgl[i].length);
+               if (divs[i]->nosimd)
+                       crypto_reenable_simd_for_test();
+               err = check_shash_op("update", err, driver, vec_name, cfg);
+               if (err)
+                       return err;
+               if (divs[i]->flush_type == FLUSH_TYPE_REIMPORT) {
+                       /* Test ->export() and ->import() */
+                       testmgr_poison(hashstate + statesize,
+                                      TESTMGR_POISON_LEN);
+                       err = crypto_shash_export(desc, hashstate);
+                       err = check_shash_op("export", err, driver, vec_name,
+                                            cfg);
+                       if (err)
+                               return err;
+                       if (!testmgr_is_poison(hashstate + statesize,
+                                              TESTMGR_POISON_LEN)) {
+                               pr_err("alg: shash: %s export() overran state buffer on test vector %s, cfg=\"%s\"\n",
+                                      driver, vec_name, cfg->name);
+                               return -EOVERFLOW;
+                       }
+                       testmgr_poison(desc->__ctx, crypto_shash_descsize(tfm));
+                       err = crypto_shash_import(desc, hashstate);
+                       err = check_shash_op("import", err, driver, vec_name,
+                                            cfg);
+                       if (err)
+                               return err;
+               }
+       }
+
+       if (cfg->nosimd)
+               crypto_disable_simd_for_test();
+       err = crypto_shash_final(desc, result);
+       if (cfg->nosimd)
+               crypto_reenable_simd_for_test();
+       err = check_shash_op("final", err, driver, vec_name, cfg);
+       if (err)
+               return err;
+result_ready:
+       return check_hash_result("shash", result, digestsize, vec, vec_name,
+                                driver, cfg);
+}
+
 static int do_ahash_op(int (*op)(struct ahash_request *req),
                       struct ahash_request *req,
                       struct crypto_wait *wait, bool nosimd)
@@ -1049,31 +1248,32 @@ static int do_ahash_op(int (*op)(struct ahash_request *req),
        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, const char *vec_name,
-                                 const struct testvec_config *cfg)
+static int check_nonfinal_ahash_op(const char *op, int err,
+                                  u8 *result, unsigned int digestsize,
+                                  const char *driver, const char *vec_name,
+                                  const struct testvec_config *cfg)
 {
        if (err) {
-               pr_err("alg: hash: %s %s() failed with err %d on test vector %s, cfg=\"%s\"\n",
+               pr_err("alg: ahash: %s %s() failed with err %d on test vector %s, cfg=\"%s\"\n",
                       driver, op, err, vec_name, cfg->name);
                return err;
        }
        if (!testmgr_is_poison(result, digestsize)) {
-               pr_err("alg: hash: %s %s() used result buffer on test vector %s, cfg=\"%s\"\n",
+               pr_err("alg: ahash: %s %s() used result buffer on test vector %s, cfg=\"%s\"\n",
                       driver, op, vec_name, cfg->name);
                return -EINVAL;
        }
        return 0;
 }
 
-static int test_hash_vec_cfg(const char *driver,
-                            const struct hash_testvec *vec,
-                            const char *vec_name,
-                            const struct testvec_config *cfg,
-                            struct ahash_request *req,
-                            struct test_sglist *tsgl,
-                            u8 *hashstate)
+/* Test one hash test vector in one configuration, using the ahash API */
+static int test_ahash_vec_cfg(const char *driver,
+                             const struct hash_testvec *vec,
+                             const char *vec_name,
+                             const struct testvec_config *cfg,
+                             struct ahash_request *req,
+                             struct test_sglist *tsgl,
+                             u8 *hashstate)
 {
        struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
        const unsigned int alignmask = crypto_ahash_alignmask(tfm);
@@ -1082,8 +1282,6 @@ static int test_hash_vec_cfg(const char *driver,
        const u32 req_flags = CRYPTO_TFM_REQ_MAY_BACKLOG | cfg->req_flags;
        const struct test_sg_division *divs[XBUFSIZE];
        DECLARE_CRYPTO_WAIT(wait);
-       struct kvec _input;
-       struct iov_iter input;
        unsigned int i;
        struct scatterlist *pending_sgl;
        unsigned int pending_len;
@@ -1096,26 +1294,22 @@ static int test_hash_vec_cfg(const char *driver,
                if (err) {
                        if (err == vec->setkey_error)
                                return 0;
-                       pr_err("alg: hash: %s setkey failed on test vector %s; expected_error=%d, actual_error=%d, flags=%#x\n",
+                       pr_err("alg: ahash: %s setkey failed on test vector %s; expected_error=%d, actual_error=%d, flags=%#x\n",
                               driver, vec_name, vec->setkey_error, err,
                               crypto_ahash_get_flags(tfm));
                        return err;
                }
                if (vec->setkey_error) {
-                       pr_err("alg: hash: %s setkey unexpectedly succeeded on test vector %s; expected_error=%d\n",
+                       pr_err("alg: ahash: %s setkey unexpectedly succeeded on test vector %s; expected_error=%d\n",
                               driver, vec_name, vec->setkey_error);
                        return -EINVAL;
                }
        }
 
        /* Build the scatterlist for the source data */
-       _input.iov_base = (void *)vec->plaintext;
-       _input.iov_len = vec->psize;
-       iov_iter_kvec(&input, WRITE, &_input, 1, vec->psize);
-       err = build_test_sglist(tsgl, cfg->src_divs, alignmask, vec->psize,
-                               &input, divs);
+       err = build_hash_sglist(tsgl, vec, cfg, alignmask, divs);
        if (err) {
-               pr_err("alg: hash: %s: error preparing scatterlist for test vector %s, cfg=\"%s\"\n",
+               pr_err("alg: ahash: %s: error preparing scatterlist for test vector %s, cfg=\"%s\"\n",
                       driver, vec_name, cfg->name);
                return err;
        }
@@ -1135,13 +1329,13 @@ static int test_hash_vec_cfg(const char *driver,
                if (err) {
                        if (err == vec->digest_error)
                                return 0;
-                       pr_err("alg: hash: %s digest() failed on test vector %s; expected_error=%d, actual_error=%d, cfg=\"%s\"\n",
+                       pr_err("alg: ahash: %s digest() failed on test vector %s; expected_error=%d, actual_error=%d, cfg=\"%s\"\n",
                               driver, vec_name, vec->digest_error, err,
                               cfg->name);
                        return err;
                }
                if (vec->digest_error) {
-                       pr_err("alg: hash: %s digest() unexpectedly succeeded on test vector %s; expected_error=%d, cfg=\"%s\"\n",
+                       pr_err("alg: ahash: %s digest() unexpectedly succeeded on test vector %s; expected_error=%d, cfg=\"%s\"\n",
                               driver, vec_name, vec->digest_error, cfg->name);
                        return -EINVAL;
                }
@@ -1153,8 +1347,8 @@ 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 = do_ahash_op(crypto_ahash_init, req, &wait, cfg->nosimd);
-       err = check_nonfinal_hash_op("init", err, result, digestsize,
-                                    driver, vec_name, cfg);
+       err = check_nonfinal_ahash_op("init", err, result, digestsize,
+                                     driver, vec_name, cfg);
        if (err)
                return err;
 
@@ -1170,9 +1364,9 @@ static int test_hash_vec_cfg(const char *driver,
                                                pending_len);
                        err = do_ahash_op(crypto_ahash_update, req, &wait,
                                          divs[i]->nosimd);
-                       err = check_nonfinal_hash_op("update", err,
-                                                    result, digestsize,
-                                                    driver, vec_name, cfg);
+                       err = check_nonfinal_ahash_op("update", err,
+                                                     result, digestsize,
+                                                     driver, vec_name, cfg);
                        if (err)
                                return err;
                        pending_sgl = NULL;
@@ -1183,23 +1377,23 @@ static int test_hash_vec_cfg(const char *driver,
                        testmgr_poison(hashstate + statesize,
                                       TESTMGR_POISON_LEN);
                        err = crypto_ahash_export(req, hashstate);
-                       err = check_nonfinal_hash_op("export", err,
-                                                    result, digestsize,
-                                                    driver, vec_name, cfg);
+                       err = check_nonfinal_ahash_op("export", err,
+                                                     result, digestsize,
+                                                     driver, vec_name, cfg);
                        if (err)
                                return err;
                        if (!testmgr_is_poison(hashstate + statesize,
                                               TESTMGR_POISON_LEN)) {
-                               pr_err("alg: hash: %s export() overran state buffer on test vector %s, cfg=\"%s\"\n",
+                               pr_err("alg: ahash: %s export() overran state buffer on test vector %s, cfg=\"%s\"\n",
                                       driver, vec_name, cfg->name);
                                return -EOVERFLOW;
                        }
 
                        testmgr_poison(req->__ctx, crypto_ahash_reqsize(tfm));
                        err = crypto_ahash_import(req, hashstate);
-                       err = check_nonfinal_hash_op("import", err,
-                                                    result, digestsize,
-                                                    driver, vec_name, cfg);
+                       err = check_nonfinal_ahash_op("import", err,
+                                                     result, digestsize,
+                                                     driver, vec_name, cfg);
                        if (err)
                                return err;
                }
@@ -1213,13 +1407,13 @@ static int test_hash_vec_cfg(const char *driver,
        if (cfg->finalization_type == FINALIZATION_TYPE_FINAL) {
                /* finish with update() and final() */
                err = do_ahash_op(crypto_ahash_update, req, &wait, cfg->nosimd);
-               err = check_nonfinal_hash_op("update", err, result, digestsize,
-                                            driver, vec_name, cfg);
+               err = check_nonfinal_ahash_op("update", err, result, digestsize,
+                                             driver, vec_name, cfg);
                if (err)
                        return err;
                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 %s, cfg=\"%s\"\n",
+                       pr_err("alg: ahash: %s final() failed with err %d on test vector %s, cfg=\"%s\"\n",
                               driver, err, vec_name, cfg->name);
                        return err;
                }
@@ -1227,31 +1421,49 @@ static int test_hash_vec_cfg(const char *driver,
                /* finish with finup() */
                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 %s, cfg=\"%s\"\n",
+                       pr_err("alg: ahash: %s finup() failed with err %d on test vector %s, cfg=\"%s\"\n",
                               driver, err, vec_name, cfg->name);
                        return err;
                }
        }
 
 result_ready:
-       /* Check that the algorithm produced the correct digest */
-       if (memcmp(result, vec->digest, digestsize) != 0) {
-               pr_err("alg: hash: %s test failed (wrong result) on test vector %s, cfg=\"%s\"\n",
-                      driver, vec_name, cfg->name);
-               return -EINVAL;
-       }
-       if (!testmgr_is_poison(&result[digestsize], TESTMGR_POISON_LEN)) {
-               pr_err("alg: hash: %s overran result buffer on test vector %s, cfg=\"%s\"\n",
-                      driver, vec_name, cfg->name);
-               return -EOVERFLOW;
+       return check_hash_result("ahash", result, digestsize, vec, vec_name,
+                                driver, cfg);
+}
+
+static int test_hash_vec_cfg(const char *driver,
+                            const struct hash_testvec *vec,
+                            const char *vec_name,
+                            const struct testvec_config *cfg,
+                            struct ahash_request *req,
+                            struct shash_desc *desc,
+                            struct test_sglist *tsgl,
+                            u8 *hashstate)
+{
+       int err;
+
+       /*
+        * For algorithms implemented as "shash", most bugs will be detected by
+        * both the shash and ahash tests.  Test the shash API first so that the
+        * failures involve less indirection, so are easier to debug.
+        */
+
+       if (desc) {
+               err = test_shash_vec_cfg(driver, vec, vec_name, cfg, desc, tsgl,
+                                        hashstate);
+               if (err)
+                       return err;
        }
 
-       return 0;
+       return test_ahash_vec_cfg(driver, vec, vec_name, cfg, req, tsgl,
+                                 hashstate);
 }
 
 static int test_hash_vec(const char *driver, const struct hash_testvec *vec,
                         unsigned int vec_num, struct ahash_request *req,
-                        struct test_sglist *tsgl, u8 *hashstate)
+                        struct shash_desc *desc, struct test_sglist *tsgl,
+                        u8 *hashstate)
 {
        char vec_name[16];
        unsigned int i;
@@ -1262,7 +1474,7 @@ static int test_hash_vec(const char *driver, const struct hash_testvec *vec,
        for (i = 0; i < ARRAY_SIZE(default_hash_testvec_configs); i++) {
                err = test_hash_vec_cfg(driver, vec, vec_name,
                                        &default_hash_testvec_configs[i],
-                                       req, tsgl, hashstate);
+                                       req, desc, tsgl, hashstate);
                if (err)
                        return err;
        }
@@ -1276,9 +1488,10 @@ static int test_hash_vec(const char *driver, const struct hash_testvec *vec,
                        generate_random_testvec_config(&cfg, cfgname,
                                                       sizeof(cfgname));
                        err = test_hash_vec_cfg(driver, vec, vec_name, &cfg,
-                                               req, tsgl, hashstate);
+                                               req, desc, tsgl, hashstate);
                        if (err)
                                return err;
+                       cond_resched();
                }
        }
 #endif
@@ -1290,14 +1503,12 @@ static int test_hash_vec(const char *driver, const struct hash_testvec *vec,
  * Generate a hash test vector from the given implementation.
  * Assumes the buffers in 'vec' were already allocated.
  */
-static void generate_random_hash_testvec(struct crypto_shash *tfm,
+static void generate_random_hash_testvec(struct shash_desc *desc,
                                         struct hash_testvec *vec,
                                         unsigned int maxkeysize,
                                         unsigned int maxdatasize,
                                         char *name, size_t max_namelen)
 {
-       SHASH_DESC_ON_STACK(desc, tfm);
-
        /* Data */
        vec->psize = generate_random_length(maxdatasize);
        generate_random_bytes((u8 *)vec->plaintext, vec->psize);
@@ -1314,7 +1525,7 @@ static void generate_random_hash_testvec(struct crypto_shash *tfm,
                        vec->ksize = 1 + (prandom_u32() % maxkeysize);
                generate_random_bytes((u8 *)vec->key, vec->ksize);
 
-               vec->setkey_error = crypto_shash_setkey(tfm, vec->key,
+               vec->setkey_error = crypto_shash_setkey(desc->tfm, vec->key,
                                                        vec->ksize);
                /* If the key couldn't be set, no need to continue to digest. */
                if (vec->setkey_error)
@@ -1322,7 +1533,6 @@ static void generate_random_hash_testvec(struct crypto_shash *tfm,
        }
 
        /* Digest */
-       desc->tfm = tfm;
        vec->digest_error = crypto_shash_digest(desc, vec->plaintext,
                                                vec->psize, (u8 *)vec->digest);
 done:
@@ -1338,6 +1548,7 @@ static int test_hash_vs_generic_impl(const char *driver,
                                     const char *generic_driver,
                                     unsigned int maxkeysize,
                                     struct ahash_request *req,
+                                    struct shash_desc *desc,
                                     struct test_sglist *tsgl,
                                     u8 *hashstate)
 {
@@ -1348,10 +1559,11 @@ static int test_hash_vs_generic_impl(const char *driver,
        const char *algname = crypto_hash_alg_common(tfm)->base.cra_name;
        char _generic_driver[CRYPTO_MAX_ALG_NAME];
        struct crypto_shash *generic_tfm = NULL;
+       struct shash_desc *generic_desc = NULL;
        unsigned int i;
        struct hash_testvec vec = { 0 };
        char vec_name[64];
-       struct testvec_config cfg;
+       struct testvec_config *cfg;
        char cfgname[TESTVEC_CONFIG_NAMELEN];
        int err;
 
@@ -1381,6 +1593,20 @@ static int test_hash_vs_generic_impl(const char *driver,
                return err;
        }
 
+       cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+       if (!cfg) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       generic_desc = kzalloc(sizeof(*desc) +
+                              crypto_shash_descsize(generic_tfm), GFP_KERNEL);
+       if (!generic_desc) {
+               err = -ENOMEM;
+               goto out;
+       }
+       generic_desc->tfm = generic_tfm;
+
        /* Check the algorithm properties for consistency. */
 
        if (digestsize != crypto_shash_digestsize(generic_tfm)) {
@@ -1412,23 +1638,25 @@ static int test_hash_vs_generic_impl(const char *driver,
        }
 
        for (i = 0; i < fuzz_iterations * 8; i++) {
-               generate_random_hash_testvec(generic_tfm, &vec,
+               generate_random_hash_testvec(generic_desc, &vec,
                                             maxkeysize, maxdatasize,
                                             vec_name, sizeof(vec_name));
-               generate_random_testvec_config(&cfg, cfgname, sizeof(cfgname));
+               generate_random_testvec_config(cfg, cfgname, sizeof(cfgname));
 
-               err = test_hash_vec_cfg(driver, &vec, vec_name, &cfg,
-                                       req, tsgl, hashstate);
+               err = test_hash_vec_cfg(driver, &vec, vec_name, cfg,
+                                       req, desc, tsgl, hashstate);
                if (err)
                        goto out;
                cond_resched();
        }
        err = 0;
 out:
+       kfree(cfg);
        kfree(vec.key);
        kfree(vec.plaintext);
        kfree(vec.digest);
        crypto_free_shash(generic_tfm);
+       kzfree(generic_desc);
        return err;
 }
 #else /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */
@@ -1436,6 +1664,7 @@ static int test_hash_vs_generic_impl(const char *driver,
                                     const char *generic_driver,
                                     unsigned int maxkeysize,
                                     struct ahash_request *req,
+                                    struct shash_desc *desc,
                                     struct test_sglist *tsgl,
                                     u8 *hashstate)
 {
@@ -1443,26 +1672,67 @@ static int test_hash_vs_generic_impl(const char *driver,
 }
 #endif /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */
 
+static int alloc_shash(const char *driver, u32 type, u32 mask,
+                      struct crypto_shash **tfm_ret,
+                      struct shash_desc **desc_ret)
+{
+       struct crypto_shash *tfm;
+       struct shash_desc *desc;
+
+       tfm = crypto_alloc_shash(driver, type, mask);
+       if (IS_ERR(tfm)) {
+               if (PTR_ERR(tfm) == -ENOENT) {
+                       /*
+                        * This algorithm is only available through the ahash
+                        * API, not the shash API, so skip the shash tests.
+                        */
+                       return 0;
+               }
+               pr_err("alg: hash: failed to allocate shash transform for %s: %ld\n",
+                      driver, PTR_ERR(tfm));
+               return PTR_ERR(tfm);
+       }
+
+       desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
+       if (!desc) {
+               crypto_free_shash(tfm);
+               return -ENOMEM;
+       }
+       desc->tfm = tfm;
+
+       *tfm_ret = tfm;
+       *desc_ret = desc;
+       return 0;
+}
+
 static int __alg_test_hash(const struct hash_testvec *vecs,
                           unsigned int num_vecs, const char *driver,
                           u32 type, u32 mask,
                           const char *generic_driver, unsigned int maxkeysize)
 {
-       struct crypto_ahash *tfm;
+       struct crypto_ahash *atfm = NULL;
        struct ahash_request *req = NULL;
+       struct crypto_shash *stfm = NULL;
+       struct shash_desc *desc = NULL;
        struct test_sglist *tsgl = NULL;
        u8 *hashstate = NULL;
+       unsigned int statesize;
        unsigned int i;
        int err;
 
-       tfm = crypto_alloc_ahash(driver, type, mask);
-       if (IS_ERR(tfm)) {
+       /*
+        * Always test the ahash API.  This works regardless of whether the
+        * algorithm is implemented as ahash or shash.
+        */
+
+       atfm = crypto_alloc_ahash(driver, type, mask);
+       if (IS_ERR(atfm)) {
                pr_err("alg: hash: failed to allocate transform for %s: %ld\n",
-                      driver, PTR_ERR(tfm));
-               return PTR_ERR(tfm);
+                      driver, PTR_ERR(atfm));
+               return PTR_ERR(atfm);
        }
 
-       req = ahash_request_alloc(tfm, GFP_KERNEL);
+       req = ahash_request_alloc(atfm, GFP_KERNEL);
        if (!req) {
                pr_err("alg: hash: failed to allocate request for %s\n",
                       driver);
@@ -1470,6 +1740,14 @@ static int __alg_test_hash(const struct hash_testvec *vecs,
                goto out;
        }
 
+       /*
+        * If available also test the shash API, to cover corner cases that may
+        * be missed by testing the ahash API only.
+        */
+       err = alloc_shash(driver, type, mask, &stfm, &desc);
+       if (err)
+               goto out;
+
        tsgl = kmalloc(sizeof(*tsgl), GFP_KERNEL);
        if (!tsgl || init_test_sglist(tsgl) != 0) {
                pr_err("alg: hash: failed to allocate test buffers for %s\n",
@@ -1480,8 +1758,10 @@ static int __alg_test_hash(const struct hash_testvec *vecs,
                goto out;
        }
 
-       hashstate = kmalloc(crypto_ahash_statesize(tfm) + TESTMGR_POISON_LEN,
-                           GFP_KERNEL);
+       statesize = crypto_ahash_statesize(atfm);
+       if (stfm)
+               statesize = max(statesize, crypto_shash_statesize(stfm));
+       hashstate = kmalloc(statesize + TESTMGR_POISON_LEN, GFP_KERNEL);
        if (!hashstate) {
                pr_err("alg: hash: failed to allocate hash state buffer for %s\n",
                       driver);
@@ -1490,20 +1770,24 @@ static int __alg_test_hash(const struct hash_testvec *vecs,
        }
 
        for (i = 0; i < num_vecs; i++) {
-               err = test_hash_vec(driver, &vecs[i], i, req, tsgl, hashstate);
+               err = test_hash_vec(driver, &vecs[i], i, req, desc, tsgl,
+                                   hashstate);
                if (err)
                        goto out;
+               cond_resched();
        }
        err = test_hash_vs_generic_impl(driver, generic_driver, maxkeysize, req,
-                                       tsgl, hashstate);
+                                       desc, tsgl, hashstate);
 out:
        kfree(hashstate);
        if (tsgl) {
                destroy_test_sglist(tsgl);
                kfree(tsgl);
        }
+       kfree(desc);
+       crypto_free_shash(stfm);
        ahash_request_free(req);
-       crypto_free_ahash(tfm);
+       crypto_free_ahash(atfm);
        return err;
 }
 
@@ -1755,6 +2039,7 @@ static int test_aead_vec(const char *driver, int enc,
                                                &cfg, req, tsgls);
                        if (err)
                                return err;
+                       cond_resched();
                }
        }
 #endif
@@ -1864,7 +2149,7 @@ static int test_aead_vs_generic_impl(const char *driver,
        unsigned int i;
        struct aead_testvec vec = { 0 };
        char vec_name[64];
-       struct testvec_config cfg;
+       struct testvec_config *cfg;
        char cfgname[TESTVEC_CONFIG_NAMELEN];
        int err;
 
@@ -1894,6 +2179,12 @@ static int test_aead_vs_generic_impl(const char *driver,
                return err;
        }
 
+       cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+       if (!cfg) {
+               err = -ENOMEM;
+               goto out;
+       }
+
        generic_req = aead_request_alloc(generic_tfm, GFP_KERNEL);
        if (!generic_req) {
                err = -ENOMEM;
@@ -1948,13 +2239,13 @@ static int test_aead_vs_generic_impl(const char *driver,
                generate_random_aead_testvec(generic_req, &vec,
                                             maxkeysize, maxdatasize,
                                             vec_name, sizeof(vec_name));
-               generate_random_testvec_config(&cfg, cfgname, sizeof(cfgname));
+               generate_random_testvec_config(cfg, cfgname, sizeof(cfgname));
 
-               err = test_aead_vec_cfg(driver, ENCRYPT, &vec, vec_name, &cfg,
+               err = test_aead_vec_cfg(driver, ENCRYPT, &vec, vec_name, cfg,
                                        req, tsgls);
                if (err)
                        goto out;
-               err = test_aead_vec_cfg(driver, DECRYPT, &vec, vec_name, &cfg,
+               err = test_aead_vec_cfg(driver, DECRYPT, &vec, vec_name, cfg,
                                        req, tsgls);
                if (err)
                        goto out;
@@ -1962,6 +2253,7 @@ static int test_aead_vs_generic_impl(const char *driver,
        }
        err = 0;
 out:
+       kfree(cfg);
        kfree(vec.key);
        kfree(vec.iv);
        kfree(vec.assoc);
@@ -1994,6 +2286,7 @@ static int test_aead(const char *driver, int enc,
                                    tsgls);
                if (err)
                        return err;
+               cond_resched();
        }
        return 0;
 }
@@ -2336,6 +2629,7 @@ static int test_skcipher_vec(const char *driver, int enc,
                                                    &cfg, req, tsgls);
                        if (err)
                                return err;
+                       cond_resched();
                }
        }
 #endif
@@ -2409,7 +2703,7 @@ static int test_skcipher_vs_generic_impl(const char *driver,
        unsigned int i;
        struct cipher_testvec vec = { 0 };
        char vec_name[64];
-       struct testvec_config cfg;
+       struct testvec_config *cfg;
        char cfgname[TESTVEC_CONFIG_NAMELEN];
        int err;
 
@@ -2443,6 +2737,12 @@ static int test_skcipher_vs_generic_impl(const char *driver,
                return err;
        }
 
+       cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+       if (!cfg) {
+               err = -ENOMEM;
+               goto out;
+       }
+
        generic_req = skcipher_request_alloc(generic_tfm, GFP_KERNEL);
        if (!generic_req) {
                err = -ENOMEM;
@@ -2490,20 +2790,21 @@ static int test_skcipher_vs_generic_impl(const char *driver,
        for (i = 0; i < fuzz_iterations * 8; i++) {
                generate_random_cipher_testvec(generic_req, &vec, maxdatasize,
                                               vec_name, sizeof(vec_name));
-               generate_random_testvec_config(&cfg, cfgname, sizeof(cfgname));
+               generate_random_testvec_config(cfg, cfgname, sizeof(cfgname));
 
                err = test_skcipher_vec_cfg(driver, ENCRYPT, &vec, vec_name,
-                                           &cfg, req, tsgls);
+                                           cfg, req, tsgls);
                if (err)
                        goto out;
                err = test_skcipher_vec_cfg(driver, DECRYPT, &vec, vec_name,
-                                           &cfg, req, tsgls);
+                                           cfg, req, tsgls);
                if (err)
                        goto out;
                cond_resched();
        }
        err = 0;
 out:
+       kfree(cfg);
        kfree(vec.key);
        kfree(vec.iv);
        kfree(vec.ptext);
@@ -2535,6 +2836,7 @@ static int test_skcipher(const char *driver, int enc,
                                        tsgls);
                if (err)
                        return err;
+               cond_resched();
        }
        return 0;
 }
@@ -4125,6 +4427,7 @@ static const struct alg_test_desc alg_test_descs[] = {
                }
        }, {
                .alg = "ecb(arc4)",
+               .generic_driver = "ecb(arc4)-generic",
                .test = alg_test_skcipher,
                .suite = {
                        .cipher = __VECS(arc4_tv_template)
@@ -4789,6 +5092,13 @@ static const struct alg_test_desc alg_test_descs[] = {
                .alg = "xts512(paes)",
                .test = alg_test_null,
                .fips_allowed = 1,
+       }, {
+               .alg = "xxhash64",
+               .test = alg_test_hash,
+               .fips_allowed = 1,
+               .suite = {
+                       .hash = __VECS(xxhash64_tv_template)
+               }
        }, {
                .alg = "zlib-deflate",
                .test = alg_test_comp,