mirror of
https://github.com/Mbed-TLS/mbedtls-framework.git
synced 2026-06-05 21:15:09 +00:00
Merge remote-tracking branch 'main' into mldsa-pqcp-driver-framework
This commit is contained in:
@@ -8,9 +8,11 @@ Please write a few sentences describing the overall goals of the pull request's
|
||||
|
||||
Please add the numbers (or links) of the associated pull requests for consuming branches. You can omit branches where this pull request is not needed.
|
||||
|
||||
- [ ] **TF-PSA-Crypto PR** provided # | not required because:
|
||||
- [ ] **development PR** provided # | not required because:
|
||||
- [ ] **3.6 PR** provided # | not required because:
|
||||
- [ ] **TF-PSA-Crypto development PR** provided # | not required because:
|
||||
- [ ] **TF-PSA-Crypto 1.1 PR** provided # | not required because:
|
||||
- [ ] **mbedtls development PR** provided # | not required because:
|
||||
- [ ] **mbedtls 4.1 PR** provided # | not required because:
|
||||
- [ ] **mbedtls 3.6 PR** provided # | not required because:
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/** Helper functions for testing with subprocesses.
|
||||
*/
|
||||
/*
|
||||
* Copyright The Mbed TLS Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef TEST_FORK_HELPERS_H
|
||||
#define TEST_FORK_HELPERS_H
|
||||
|
||||
#include "test/helpers.h"
|
||||
|
||||
/** Type of a function to run in a child process.
|
||||
*
|
||||
* The function can mark the test case as failed by calling
|
||||
* mbedtls_test_fail(). This information will be reported to the parent.
|
||||
*
|
||||
* \param param Parameter passed to the callback.
|
||||
* \param[out] output Buffer for data to pass to the parent.
|
||||
* This data is ignored if the test case is marked
|
||||
* as failed.
|
||||
* \param output_size Size of \p output in bytes.
|
||||
* \param[out] output_length Number of bytes written to \p output, to be
|
||||
* passed to the parent. The default is \c 0.
|
||||
*/
|
||||
typedef void mbedtls_test_fork_child_callback_t(
|
||||
void *param,
|
||||
unsigned char *output, size_t output_size, size_t *output_length);
|
||||
|
||||
/* Fork a child process and wait for it to collect some data.
|
||||
*
|
||||
* This is similar to backquotes or `$(...)` in a shell.
|
||||
*
|
||||
* This function blocks until the child exits.
|
||||
*
|
||||
* If the child marks the test as failed or skipped, the child's test
|
||||
* information (test result and failure location) is propagated to the
|
||||
* parent.
|
||||
*
|
||||
* \note Memory leak detection is disabled in the child.
|
||||
*
|
||||
* \param child_callback Callback function to run in the child.
|
||||
* \param param Parameter to pass to the callback function.
|
||||
* \param[out] child_output On success, data retrieved from the child.
|
||||
* Note that the data is only available if the
|
||||
* child did not mark the test case as failed
|
||||
* or skipped.
|
||||
* \param child_output_size Size of \p child_output in bytes.
|
||||
* \param[out] child_output_length On success, the number of bytes collected
|
||||
* from the child in \c child_output.
|
||||
*
|
||||
* \return \c 0 on success.
|
||||
* A nonzero value if the test case is marked as failed or skipped.
|
||||
*/
|
||||
int mbedtls_test_fork_run_child(
|
||||
mbedtls_test_fork_child_callback_t *child_callback,
|
||||
void *param,
|
||||
unsigned char *child_output, size_t child_output_size,
|
||||
size_t *child_output_length);
|
||||
|
||||
#endif /* TEST_FORK_HELPERS_H */
|
||||
@@ -147,6 +147,25 @@ void mbedtls_test_get_line1(char *line);
|
||||
*/
|
||||
void mbedtls_test_get_line2(char *line);
|
||||
|
||||
/**
|
||||
* \brief Get a copy of the test result information.
|
||||
*
|
||||
* \param[out] out On output, contains a copy of the current test info.
|
||||
*/
|
||||
void mbedtls_test_info_save(mbedtls_test_info_t *out);
|
||||
|
||||
/**
|
||||
* \brief Overwrite the test result information.
|
||||
* This is intended for some unusual scenarios.
|
||||
* You probably shouldn't use this in a test function.
|
||||
*
|
||||
* \param[in] replacement
|
||||
* The test info to use instead of the current one.
|
||||
* The function copies the data, so the pointer does
|
||||
* not need to be valid after this function returns.
|
||||
*/
|
||||
void mbedtls_test_info_overwrite(const mbedtls_test_info_t *replacement);
|
||||
|
||||
#if defined(MBEDTLS_TEST_MUTEX_USAGE)
|
||||
/**
|
||||
* \brief Get the current mutex usage error message
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
/** \file fork_helpers.c
|
||||
*
|
||||
* \brief Helper functions for testing with subprocesses.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright The Mbed TLS Contributors
|
||||
* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "test_common.h"
|
||||
#include <test/helpers.h>
|
||||
#include <test/macros.h>
|
||||
|
||||
#if defined(MBEDTLS_PLATFORM_IS_UNIXLIKE)
|
||||
|
||||
#include <test/fork_helpers.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
/** Child exit code for mbedtls_test_fork_run_child().
|
||||
*/
|
||||
typedef enum {
|
||||
/** Reporting of the child output or the child test result through
|
||||
* the pipe succeeded.
|
||||
*
|
||||
* The content sent on the pipe has the following format:
|
||||
* - [1 byte] #mbedtls_test_result_t \c test_result
|
||||
* - Case \c test_result:
|
||||
* - If \c test_result == #MBEDTLS_TEST_RESULT_SUCCESS:
|
||||
* the output from the child body function.
|
||||
* - Otherwise:
|
||||
* the child failure (or skip) information, a direct write of
|
||||
* a #mbedtls_test_result_t structure.
|
||||
*/
|
||||
CHILD_EXIT_CODE_OK = 0,
|
||||
/** Something went wrong, e.g. a write error on the pipe. */
|
||||
CHILD_EXIT_CODE_REPORTING_FAILED = 122,
|
||||
} child_exit_code_t;
|
||||
|
||||
static int env_contains_substring(const char *name, const char *substring)
|
||||
{
|
||||
const char *value = getenv(name);
|
||||
if (value == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
return strstr(value, substring) != NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int probably_running_under_valgrind(void)
|
||||
{
|
||||
if (env_contains_substring("LD_PRELOAD", "/vgpreload_")) {
|
||||
return 1;
|
||||
}
|
||||
if (env_contains_substring("DYLD_INSERT_LIBRARIES", "/vgpreload_")) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__GNUC__)
|
||||
__attribute__((__noreturn__))
|
||||
#endif
|
||||
static void run_child(
|
||||
int write_fd,
|
||||
mbedtls_test_fork_child_callback_t *child_callback,
|
||||
void *param,
|
||||
unsigned char *buf, size_t size)
|
||||
{
|
||||
/* If something goes wrong while trying to report what happened
|
||||
* in the child, exit with a nonzero status. */
|
||||
int child_exit_code = CHILD_EXIT_CODE_REPORTING_FAILED;
|
||||
/* We'll use stdio to write to the pipe, so we don't have to
|
||||
* manage EINTR and such. */
|
||||
FILE *file = fdopen(write_fd, "a");
|
||||
size_t length = 0;
|
||||
|
||||
if (file == NULL) {
|
||||
/* There's no way we can report anything other than the exit code.
|
||||
* So we might as well quit without even running the child callback. */
|
||||
goto write_done;
|
||||
}
|
||||
|
||||
child_callback(param, buf, size, &length);
|
||||
TEST_LE_U(length, size);
|
||||
|
||||
/* Label called `exit`: this is where TEST_ASSERT() and friends jump to. */
|
||||
exit:
|
||||
; // label followed by a declaration is not portable C
|
||||
char result_char = mbedtls_test_get_result();
|
||||
if (fputc(result_char, file) == EOF) {
|
||||
goto write_done;
|
||||
}
|
||||
if (result_char == MBEDTLS_TEST_RESULT_SUCCESS) {
|
||||
if (fwrite(buf, length, 1, file) != 1) {
|
||||
goto write_done;
|
||||
}
|
||||
} else {
|
||||
mbedtls_test_info_t test_info;
|
||||
mbedtls_test_info_save(&test_info);
|
||||
if (fwrite(&test_info, sizeof(test_info), 1, file) != 1) {
|
||||
goto write_done;
|
||||
}
|
||||
}
|
||||
if (fflush(file) != 0) {
|
||||
goto write_done;
|
||||
}
|
||||
child_exit_code = CHILD_EXIT_CODE_OK;
|
||||
|
||||
/* Label for `_exit()` call: this is where we jump to if the failure
|
||||
* reporting fails. */
|
||||
write_done:
|
||||
/* We must call _exit(), not exit(), because the child must not run the
|
||||
* things that normally run at exit.
|
||||
*
|
||||
* - Do not flush any stdio buffers! Any unflushed buffers are inherited
|
||||
* from our parent, and if we flushed them, we'd get duplicate output
|
||||
* since the parent would also write the same buffer content.
|
||||
* - Do not run atexit hooks, e.g. leak detection code from sanitizers
|
||||
* such as ASan. The child leaks any number of resources which are
|
||||
* inherited from the parent but not used in the child. It's the
|
||||
* parent's job to check for resource leaks.
|
||||
* (We deliberately do not clean up in the child. One reason is that
|
||||
* we try to minimize what happens in the child, because it's difficult
|
||||
* to debug. Another reason is that we must not cause external effects
|
||||
* such as destroying a PSA persistent key.)
|
||||
*/
|
||||
if (probably_running_under_valgrind()) {
|
||||
/* Valgrind overloads _exit(), and this makes it do weird things,
|
||||
* including an lseek() call to rewind the pointer on the file
|
||||
* description for the `.datax` file, causing the same test cases
|
||||
* to run again (or parse errors, depending on the exact amount
|
||||
* of rewinding).
|
||||
*
|
||||
* Valgrind doesn't overload execve() and friends. So instead of
|
||||
* _exit(), execute a shell command that returns the same status.
|
||||
*/
|
||||
char cmd[20];
|
||||
snprintf(cmd, sizeof(cmd), "exit %d", child_exit_code);
|
||||
execlp("sh", "sh", "-c", cmd, NULL);
|
||||
}
|
||||
_exit(child_exit_code);
|
||||
}
|
||||
|
||||
int mbedtls_test_fork_run_child(
|
||||
mbedtls_test_fork_child_callback_t *child_callback,
|
||||
void *param,
|
||||
unsigned char *child_output, size_t child_output_size,
|
||||
size_t *child_output_length)
|
||||
{
|
||||
*child_output_length = 0;
|
||||
|
||||
int ret = -1;
|
||||
pid_t pid = -1;
|
||||
int pipe_fd[2] = { -1, -1 };
|
||||
|
||||
/* Set up a pipe. The child will write to pipe_fd[1], and the
|
||||
* parent will read from pipe_fd[0]. */
|
||||
TEST_ASSERT_ERRNO(pipe(pipe_fd) != -1);
|
||||
|
||||
pid = fork();
|
||||
TEST_ASSERT_ERRNO(pid != -1);
|
||||
|
||||
if (pid == 0) {
|
||||
/* The child code */
|
||||
close(pipe_fd[0]);
|
||||
run_child(pipe_fd[1], child_callback, param,
|
||||
child_output, child_output_size);
|
||||
/* Unreachable */
|
||||
}
|
||||
/* Beyond this point, we're in the parent (original) process. */
|
||||
|
||||
close(pipe_fd[1]);
|
||||
pipe_fd[1] = -1;
|
||||
|
||||
unsigned char result_char;
|
||||
struct {
|
||||
mbedtls_test_info_t child_test_info;
|
||||
unsigned char excess;
|
||||
} reading_on_failure;
|
||||
/* Normally, the child should give us a 1-byte result, then either
|
||||
* the child body's output or a test info. */
|
||||
ssize_t n = read(pipe_fd[0], &result_char, 1);
|
||||
TEST_EQUAL(n, 1);
|
||||
|
||||
/* Tentatively read what we were promised. Don't commit to anything
|
||||
* until we have the child's exit status. */
|
||||
size_t bytes_read = 0;
|
||||
if (result_char == MBEDTLS_TEST_RESULT_SUCCESS) {
|
||||
do {
|
||||
n = read(pipe_fd[0],
|
||||
child_output + bytes_read,
|
||||
child_output_size - bytes_read);
|
||||
if (n > 0) {
|
||||
bytes_read += n;
|
||||
}
|
||||
} while (n > 0 && bytes_read < child_output_size);
|
||||
TEST_ASSERT_ERRNO(n != -1);
|
||||
} else {
|
||||
do {
|
||||
n = read(pipe_fd[0],
|
||||
(unsigned char *) &reading_on_failure + bytes_read,
|
||||
sizeof(reading_on_failure) - bytes_read);
|
||||
if (n > 0) {
|
||||
bytes_read += n;
|
||||
}
|
||||
} while (n > 0 && bytes_read < sizeof(reading_on_failure));
|
||||
TEST_ASSERT_ERRNO(n != -1);
|
||||
/* Check that the child wrote the amount of data that what we expect. */
|
||||
TEST_EQUAL(bytes_read, sizeof(reading_on_failure.child_test_info));
|
||||
}
|
||||
|
||||
/* Close the pipe. If we left it open, there could be a deadlock if the
|
||||
* child tried to write more than it should, while the parent is just
|
||||
* waiting for the child to exit. */
|
||||
close(pipe_fd[0]);
|
||||
pipe_fd[0] = -1;
|
||||
|
||||
int wstatus;
|
||||
TEST_ASSERT_ERRNO(waitpid(pid, &wstatus, 0) == pid);
|
||||
if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == CHILD_EXIT_CODE_OK) {
|
||||
if (result_char == MBEDTLS_TEST_RESULT_SUCCESS) {
|
||||
*child_output_length = bytes_read;
|
||||
ret = 0;
|
||||
} else {
|
||||
mbedtls_test_info_overwrite(&reading_on_failure.child_test_info);
|
||||
}
|
||||
} else {
|
||||
/* Weird status, just report it. */
|
||||
TEST_EQUAL(wstatus, 0);
|
||||
}
|
||||
|
||||
exit:
|
||||
close(pipe_fd[0]);
|
||||
close(pipe_fd[1]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* MBEDTLS_PLATFORM_IS_UNIXLIKE */
|
||||
@@ -460,6 +460,28 @@ void mbedtls_test_info_reset(void)
|
||||
#endif /* MBEDTLS_THREADING_C */
|
||||
}
|
||||
|
||||
void mbedtls_test_info_save(mbedtls_test_info_t *out)
|
||||
{
|
||||
#ifdef MBEDTLS_THREADING_C
|
||||
mbedtls_mutex_lock(&mbedtls_test_info_mutex);
|
||||
#endif /* MBEDTLS_THREADING_C */
|
||||
memcpy(out, &mbedtls_test_info, sizeof(mbedtls_test_info));
|
||||
#ifdef MBEDTLS_THREADING_C
|
||||
mbedtls_mutex_unlock(&mbedtls_test_info_mutex);
|
||||
#endif /* MBEDTLS_THREADING_C */
|
||||
}
|
||||
|
||||
void mbedtls_test_info_overwrite(const mbedtls_test_info_t *replacement)
|
||||
{
|
||||
#ifdef MBEDTLS_THREADING_C
|
||||
mbedtls_mutex_lock(&mbedtls_test_info_mutex);
|
||||
#endif /* MBEDTLS_THREADING_C */
|
||||
memcpy(&mbedtls_test_info, replacement, sizeof(mbedtls_test_info));
|
||||
#ifdef MBEDTLS_THREADING_C
|
||||
mbedtls_mutex_unlock(&mbedtls_test_info_mutex);
|
||||
#endif /* MBEDTLS_THREADING_C */
|
||||
}
|
||||
|
||||
int mbedtls_test_equal(const char *test, int line_no, const char *filename,
|
||||
unsigned long long value1, unsigned long long value2)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,18 @@
|
||||
#ifndef TEST_TEST_COMMON_H
|
||||
#define TEST_TEST_COMMON_H
|
||||
|
||||
#if !defined(_POSIX_C_SOURCE)
|
||||
/* For standards-compliant access to
|
||||
* clock_gettime(), gmtime_r(), ...
|
||||
*/
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
#endif
|
||||
|
||||
/* With GNU libc, define all the things, even when compiling with -pedantic. */
|
||||
#if !defined(_GNU_SOURCE)
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
/* On Mingw-w64, force the use of a C99-compliant printf() and friends.
|
||||
* This is necessary on older versions of Mingw and/or Windows runtimes
|
||||
* where snprintf does not always zero-terminate the buffer, and does
|
||||
@@ -33,4 +45,11 @@
|
||||
* is deemed necessary in test headers. */
|
||||
#include <test/build_info.h>
|
||||
|
||||
/* Give test code access to internal macros of the library. */
|
||||
#if defined(TF_PSA_CRYPTO_VERSION_NUMBER)
|
||||
#include "tf_psa_crypto_common.h"
|
||||
#else
|
||||
#include "common.h"
|
||||
#endif
|
||||
|
||||
#endif /* TEST_TEST_COMMON_H */
|
||||
|
||||
Reference in New Issue
Block a user