blob: e002eafe030eff8d413a3e291649f47c556defc5 [file] [edit]
/*
* Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include "../testutil.h"
#include "tu_local.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <openssl/crypto.h>
static int mfail_fail_after = -1;
static int mfail_alloc_count = 0;
static int mfail_triggered = 0;
static int mfail_counting = 0;
static int mfail_do_skip_all = 0;
static int mfail_do_skip_slow = 0;
static int mfail_single_point = -1;
static int mfail_start_point = 0;
static int mfail_installed = 0;
static int should_fail(void)
{
if (mfail_fail_after < 0 || !mfail_counting || mfail_triggered)
return 0;
if (mfail_alloc_count++ == mfail_fail_after) {
mfail_triggered = 1;
return 1;
}
return 0;
}
static void *mfail_malloc(size_t num, const char *file, int line)
{
if (num == 0)
return NULL;
if (should_fail())
return NULL;
return malloc(num);
}
static void *mfail_realloc(void *addr, size_t num, const char *file, int line)
{
if (addr == NULL)
return mfail_malloc(num, file, line);
if (num == 0) {
free(addr);
return NULL;
}
if (should_fail())
return NULL;
return realloc(addr, num);
}
static void mfail_free(void *addr, const char *file, int line)
{
free(addr);
}
static int env_is_true(const char *name)
{
const char *val = getenv(name);
return val != NULL && *val != '\0' && strcmp(val, "0") != 0;
}
void mfail_install(void)
{
if (env_is_true("OPENSSL_TEST_MFAIL_DISABLE"))
return;
if (!CRYPTO_set_mem_functions(mfail_malloc, mfail_realloc, mfail_free))
return;
mfail_installed = 1;
}
void mfail_start(void)
{
mfail_alloc_count = 0;
mfail_counting = 1;
}
void mfail_end(void)
{
mfail_counting = 0;
}
static void mfail_arm(int fail_after)
{
mfail_fail_after = fail_after;
mfail_alloc_count = 0;
mfail_triggered = 0;
mfail_counting = 0;
}
static void mfail_disarm(void)
{
mfail_fail_after = -1;
mfail_alloc_count = 0;
mfail_triggered = 0;
mfail_counting = 0;
}
static double elapsed_secs(clock_t start)
{
return (double)(clock() - start) / CLOCKS_PER_SEC;
}
void mfail_init(void)
{
const char *env;
mfail_do_skip_all = env_is_true("OPENSSL_TEST_MFAIL_SKIP_ALL");
mfail_do_skip_slow = env_is_true("OPENSSL_TEST_MFAIL_SKIP_SLOW");
env = getenv("OPENSSL_TEST_MFAIL_POINT");
if (env != NULL && *env != '\0')
mfail_single_point = atoi(env);
env = getenv("OPENSSL_TEST_MFAIL_START");
if (env != NULL && *env != '\0')
mfail_start_point = atoi(env);
}
int mfail_should_skip(int slow)
{
if (!mfail_installed)
return 1;
return mfail_do_skip_all || (slow && mfail_do_skip_slow);
}
int mfail_run_test(const char *test_case_name, int (*test_fn)(void))
{
int alloc_point, ret = 1;
clock_t start;
start = clock();
if (mfail_single_point >= 0) {
int rv, triggered;
ERR_clear_error();
mfail_arm(mfail_single_point);
rv = test_fn();
triggered = mfail_triggered;
mfail_disarm();
if (!triggered) {
TEST_info("mfail test '%s': point %d is beyond the last "
"allocation point, test %s",
test_case_name, mfail_single_point,
rv == 1 ? "succeeded" : "failed");
} else if (!TEST_int_eq(rv, 0)) {
TEST_error("mfail test '%s': allocation failure at point %d "
"not handled",
test_case_name, mfail_single_point);
ret = 0;
}
} else {
for (alloc_point = mfail_start_point;; alloc_point++) {
int rv, triggered;
ERR_clear_error();
mfail_arm(alloc_point);
rv = test_fn();
triggered = mfail_triggered;
mfail_disarm();
if (!triggered) {
if (!TEST_int_eq(rv, 1)) {
TEST_error("mfail test '%s': no injection but test failed",
test_case_name);
ret = 0;
}
break;
}
if (!TEST_int_eq(rv, 0)) {
TEST_error("mfail test '%s': allocation failure at point %d "
"not handled",
test_case_name, alloc_point);
ret = 0;
}
}
TEST_info("mfail test '%s': points %d..%d, %d iterations, %.6f seconds",
test_case_name, mfail_start_point, alloc_point,
alloc_point - mfail_start_point + 1, elapsed_secs(start));
}
return ret;
}