linux/tools/testing/selftests/arm64/fp/kernel-test.c
Eric Biggers 8522104f75 crypto: crct10dif - remove from crypto API
Remove the "crct10dif" shash algorithm from the crypto API.  It has no
known user now that the lib is no longer built on top of it.  It has no
remaining references in kernel code.  The only other potential users
would be the usual components that allow specifying arbitrary hash
algorithms by name, namely AF_ALG and dm-integrity.   However there are
no indications that "crct10dif" is being used with these components.
Debian Code Search and web searches don't find anything relevant, and
explicitly grepping the source code of the usual suspects (cryptsetup,
libell, iwd) finds no matches either.  "crc32" and "crc32c" are used in
a few more places, but that doesn't seem to be the case for "crct10dif".

crc_t10dif_update() is also tested by crc_kunit now, so the test
coverage provided via the crypto self-tests is no longer needed.

Also note that the "crct10dif" shash algorithm was inconsistent with the
rest of the shash API in that it wrote the digest in CPU endianness,
making the resulting byte array differ on little endian vs. big endian
platforms.  This means it was effectively just built for use by the lib
functions, and it was not actually correct to treat it as "just another
hash function" that could be dropped in via the shash API.

Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: "Martin K. Petersen" <martin.petersen@oracle.com>
Link: https://lore.kernel.org/r/20250206173857.39794-1-ebiggers@kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
2025-02-08 20:06:30 -08:00

326 lines
6.6 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2024 ARM Limited.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/kernel.h>
#include <linux/if_alg.h>
#define DATA_SIZE (16 * 4096)
static int base, sock;
static int digest_len;
static char *ref;
static char *digest;
static char *alg_name;
static struct iovec data_iov;
static int zerocopy[2];
static int sigs;
static int iter;
static void handle_exit_signal(int sig, siginfo_t *info, void *context)
{
printf("Terminated by signal %d, iterations=%d, signals=%d\n",
sig, iter, sigs);
exit(0);
}
static void handle_kick_signal(int sig, siginfo_t *info, void *context)
{
sigs++;
}
static char *drivers[] = {
"sha1-ce",
"sha224-arm64",
"sha224-arm64-neon",
"sha224-ce",
"sha256-arm64",
"sha256-arm64-neon",
"sha256-ce",
"sha384-ce",
"sha512-ce",
"sha3-224-ce",
"sha3-256-ce",
"sha3-384-ce",
"sha3-512-ce",
"sm3-ce",
"sm3-neon",
};
static bool create_socket(void)
{
FILE *proc;
struct sockaddr_alg addr;
char buf[1024];
char *c, *driver_name;
bool is_shash, match;
int ret, i;
ret = socket(AF_ALG, SOCK_SEQPACKET, 0);
if (ret < 0) {
if (errno == EAFNOSUPPORT) {
printf("AF_ALG not supported\n");
return false;
}
printf("Failed to create AF_ALG socket: %s (%d)\n",
strerror(errno), errno);
return false;
}
base = ret;
memset(&addr, 0, sizeof(addr));
addr.salg_family = AF_ALG;
strncpy((char *)addr.salg_type, "hash", sizeof(addr.salg_type));
proc = fopen("/proc/crypto", "r");
if (!proc) {
printf("Unable to open /proc/crypto\n");
return false;
}
driver_name = NULL;
is_shash = false;
match = false;
/* Look through /proc/crypto for a driver with kernel mode FP usage */
while (!match) {
c = fgets(buf, sizeof(buf), proc);
if (!c) {
if (feof(proc)) {
printf("Nothing found in /proc/crypto\n");
return false;
}
continue;
}
/* Algorithm descriptions are separated by a blank line */
if (*c == '\n') {
if (is_shash && driver_name) {
for (i = 0; i < ARRAY_SIZE(drivers); i++) {
if (strcmp(drivers[i],
driver_name) == 0) {
match = true;
}
}
}
if (!match) {
digest_len = 0;
free(driver_name);
driver_name = NULL;
free(alg_name);
alg_name = NULL;
is_shash = false;
}
continue;
}
/* Remove trailing newline */
c = strchr(buf, '\n');
if (c)
*c = '\0';
/* Find the field/value separator and start of the value */
c = strchr(buf, ':');
if (!c)
continue;
c += 2;
if (strncmp(buf, "digestsize", strlen("digestsize")) == 0)
sscanf(c, "%d", &digest_len);
if (strncmp(buf, "name", strlen("name")) == 0)
alg_name = strdup(c);
if (strncmp(buf, "driver", strlen("driver")) == 0)
driver_name = strdup(c);
if (strncmp(buf, "type", strlen("type")) == 0)
if (strncmp(c, "shash", strlen("shash")) == 0)
is_shash = true;
}
strncpy((char *)addr.salg_name, alg_name,
sizeof(addr.salg_name) - 1);
ret = bind(base, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
printf("Failed to bind %s: %s (%d)\n",
addr.salg_name, strerror(errno), errno);
return false;
}
ret = accept(base, NULL, 0);
if (ret < 0) {
printf("Failed to accept %s: %s (%d)\n",
addr.salg_name, strerror(errno), errno);
return false;
}
sock = ret;
ret = pipe(zerocopy);
if (ret != 0) {
printf("Failed to create zerocopy pipe: %s (%d)\n",
strerror(errno), errno);
return false;
}
ref = malloc(digest_len);
if (!ref) {
printf("Failed to allocated %d byte reference\n", digest_len);
return false;
}
digest = malloc(digest_len);
if (!digest) {
printf("Failed to allocated %d byte digest\n", digest_len);
return false;
}
return true;
}
static bool compute_digest(void *buf)
{
struct iovec iov;
int ret, wrote;
iov = data_iov;
while (iov.iov_len) {
ret = vmsplice(zerocopy[1], &iov, 1, SPLICE_F_GIFT);
if (ret < 0) {
printf("Failed to send buffer: %s (%d)\n",
strerror(errno), errno);
return false;
}
wrote = ret;
ret = splice(zerocopy[0], NULL, sock, NULL, wrote, 0);
if (ret < 0) {
printf("Failed to splice buffer: %s (%d)\n",
strerror(errno), errno);
} else if (ret != wrote) {
printf("Short splice: %d < %d\n", ret, wrote);
}
iov.iov_len -= wrote;
iov.iov_base += wrote;
}
reread:
ret = recv(sock, buf, digest_len, 0);
if (ret == 0) {
printf("No digest returned\n");
return false;
}
if (ret != digest_len) {
if (errno == -EAGAIN)
goto reread;
printf("Failed to get digest: %s (%d)\n",
strerror(errno), errno);
return false;
}
return true;
}
int main(void)
{
char *data;
struct sigaction sa;
int ret;
/* Ensure we have unbuffered output */
setvbuf(stdout, NULL, _IOLBF, 0);
/* The parent will communicate with us via signals */
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handle_exit_signal;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&sa.sa_mask);
ret = sigaction(SIGTERM, &sa, NULL);
if (ret < 0)
printf("Failed to install SIGTERM handler: %s (%d)\n",
strerror(errno), errno);
sa.sa_sigaction = handle_kick_signal;
ret = sigaction(SIGUSR1, &sa, NULL);
if (ret < 0)
printf("Failed to install SIGUSR1 handler: %s (%d)\n",
strerror(errno), errno);
ret = sigaction(SIGUSR2, &sa, NULL);
if (ret < 0)
printf("Failed to install SIGUSR2 handler: %s (%d)\n",
strerror(errno), errno);
data = malloc(DATA_SIZE);
if (!data) {
printf("Failed to allocate data buffer\n");
return EXIT_FAILURE;
}
memset(data, 0, DATA_SIZE);
data_iov.iov_base = data;
data_iov.iov_len = DATA_SIZE;
/*
* If we can't create a socket assume it's a lack of system
* support and fall back to a basic FPSIMD test for the
* benefit of fp-stress.
*/
if (!create_socket()) {
execl("./fpsimd-test", "./fpsimd-test", NULL);
printf("Failed to fall back to fspimd-test: %d (%s)\n",
errno, strerror(errno));
return EXIT_FAILURE;
}
/*
* Compute a reference digest we hope is repeatable, we do
* this at runtime partly to make it easier to play with
* parameters.
*/
if (!compute_digest(ref)) {
printf("Failed to compute reference digest\n");
return EXIT_FAILURE;
}
printf("AF_ALG using %s\n", alg_name);
while (true) {
if (!compute_digest(digest)) {
printf("Failed to compute digest, iter=%d\n", iter);
return EXIT_FAILURE;
}
if (memcmp(ref, digest, digest_len) != 0) {
printf("Digest mismatch, iter=%d\n", iter);
return EXIT_FAILURE;
}
iter++;
}
return EXIT_FAILURE;
}