Commit 783388e3c6 for openssl.org
commit 783388e3c68cb3a49b815b5a8c34c0857d698eb4
Author: Jakub Zelenka <jakub.zelenka@openssl.foundation>
Date: Mon May 11 17:57:16 2026 +0200
Extend and separate mfail test framework
Separate the mfail framework so it can be used beyond testutil.
Specifically, this is a step toward using it in fuzzing.
This change also improves the way mfail tests are executed. It first
counts the number of allocations and then iterates through them.
This has a couple of advantages:
- It allows removal of MFAIL_SLOW_TEST by identifying slow tests based
on the number of allocations.
- It allows non-failing tests to be ignored.
In addition, it adds a new environment variable to print a backtrace on
memory failure.
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
MergeDate: Mon May 18 07:23:52 2026
(Merged from https://github.com/openssl/openssl/pull/31144)
diff --git a/test/README.md b/test/README.md
index 07beb56952..bc4bd73f49 100644
--- a/test/README.md
+++ b/test/README.md
@@ -190,15 +190,19 @@ Memory Allocation Failure Tests
Some tests use the `ADD_MFAIL_TEST` framework to exhaustively verify that
functions handle every possible allocation failure gracefully. These tests
-run repeatedly, failing one allocation later each iteration, and can be
-controlled with the following environment variables:
+run repeatedly, failing one allocation later each iteration. The
+`ADD_MFAIL_NO_CHECK_TEST` variant relaxes the requirement that the test
+return 0 when a failure was triggered. Behavior is controlled with the
+following environment variables:
OPENSSL_TEST_MFAIL_DISABLE=1 Disable mfail custom allocator installation.
OPENSSL_TEST_MFAIL_SKIP_ALL=1 Skip all mfail tests.
- OPENSSL_TEST_MFAIL_SKIP_SLOW=1 Skip only slow mfail tests
- (registered with ADD_MFAIL_SLOW_TEST).
+ OPENSSL_TEST_MFAIL_SKIP_SLOW=1 Skip mfail tests whose allocation count
+ exceeds the slow threshold.
+
+ OPENSSL_TEST_MFAIL_SLOW=N Slow threshold (default 1000).
OPENSSL_TEST_MFAIL_POINT=N Run only failure point N (0-indexed),
useful for debugging a specific failure.
@@ -206,6 +210,8 @@ controlled with the following environment variables:
OPENSSL_TEST_MFAIL_START=N Start iteration from point N, skipping
earlier points that are already fixed.
+ OPENSSL_TEST_MFAIL_BACKTRACE=1 Print a backtrace at each injection point.
+
For example, to debug a failure at allocation point 42:
$ OPENSSL_TEST_MFAIL_POINT=42 ./test/crltest -test test_crl_diff_mfail
diff --git a/test/build.info b/test/build.info
index 16266b594b..955a0ace58 100644
--- a/test/build.info
+++ b/test/build.info
@@ -32,8 +32,8 @@ IF[{- !$disabled{tests} -}]
testutil/test_cleanup.c testutil/main.c testutil/testutil_init.c \
testutil/options.c testutil/test_options.c testutil/provider.c \
testutil/apps_shims.c testutil/random.c testutil/helper.c \
- testutil/compare.c testutil/mfail.c $LIBAPPSSRC
- INCLUDE[libtestutil.a]=../include ../apps/include ..
+ testutil/compare.c mfail/mfail.c $LIBAPPSSRC
+ INCLUDE[libtestutil.a]=../include ../apps/include .. mfail
DEPEND[libtestutil.a]=../libcrypto
PROGRAMS{noinst}= \
diff --git a/test/mfail/mfail.c b/test/mfail/mfail.c
new file mode 100644
index 0000000000..034d9881da
--- /dev/null
+++ b/test/mfail/mfail.c
@@ -0,0 +1,367 @@
+/*
+ * 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 "mfail.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/crypto.h>
+
+#if defined(__has_feature)
+#if __has_feature(address_sanitizer)
+#define MFAIL_HAVE_ASAN 1
+#endif
+#endif
+#if defined(__SANITIZE_ADDRESS__) && !defined(MFAIL_HAVE_ASAN)
+#define MFAIL_HAVE_ASAN 1
+#endif
+
+#ifdef MFAIL_HAVE_ASAN
+extern void __sanitizer_print_stack_trace(void);
+#elif defined(__GLIBC__) || defined(__APPLE__)
+#include <execinfo.h>
+#define MFAIL_HAVE_BACKTRACE
+#define MFAIL_BT_MAX 128
+#endif
+
+static struct {
+ int installed;
+ int skip_all;
+ int skip_slow;
+ int single_point;
+ int start_point;
+ int env_count;
+ int slow_threshold;
+ int print_bt;
+ int mode;
+ int phase;
+ int seq;
+ int next_point;
+ int iter_index;
+ int n;
+ int total;
+ int iterations;
+ int started;
+ int fail_after;
+ int alloc_count;
+ int triggered;
+ int counting;
+ int slow_skipped;
+} mf;
+
+static int env_is_true(const char *name)
+{
+ const char *value = getenv(name);
+ return value != NULL && *value != '\0' && strcmp(value, "0") != 0;
+}
+
+static int env_int(const char *name, int dflt)
+{
+ const char *value = getenv(name);
+ return (value != NULL && *value != '\0') ? atoi(value) : dflt;
+}
+
+static void mfail_print_bt(void)
+{
+#ifdef MFAIL_HAVE_ASAN
+ fprintf(stderr, "# MFAIL_BT (failure injection point)\n");
+ __sanitizer_print_stack_trace();
+#elif defined(MFAIL_HAVE_BACKTRACE)
+ void *buf[MFAIL_BT_MAX];
+ char **syms;
+ int n, i;
+
+ n = backtrace(buf, MFAIL_BT_MAX);
+ syms = backtrace_symbols(buf, n);
+ if (syms == NULL)
+ return;
+
+ fprintf(stderr, "# MFAIL_BT (failure injection point)\n");
+ /* Skip frame 0 (this function) */
+ for (i = 1; i < n; i++)
+ fprintf(stderr, "# %s\n", syms[i]);
+
+ free(syms);
+#endif
+}
+
+static int should_fail(void)
+{
+ int idx;
+
+ if (!mf.counting)
+ return 0;
+
+ idx = mf.alloc_count++;
+
+ if (mf.fail_after < 0 || mf.triggered)
+ return 0;
+ if (idx == mf.fail_after) {
+ mf.triggered = 1;
+ if (mf.print_bt)
+ mfail_print_bt();
+ return 1;
+ }
+ return 0;
+}
+
+static void *mf_malloc(size_t num, const char *file, int line)
+{
+ if (num == 0)
+ return NULL;
+ if (should_fail())
+ return NULL;
+ return malloc(num);
+}
+
+static void *mf_realloc(void *addr, size_t num, const char *file, int line)
+{
+ if (addr == NULL)
+ return mf_malloc(num, file, line);
+ if (num == 0) {
+ free(addr);
+ return NULL;
+ }
+ if (should_fail())
+ return NULL;
+ return realloc(addr, num);
+}
+
+static void mf_free(void *addr, const char *file, int line)
+{
+ free(addr);
+}
+
+int mfail_install(int optional)
+{
+ if (mf.installed)
+ return 1;
+ if (env_is_true("OPENSSL_TEST_MFAIL_DISABLE"))
+ return 0;
+
+ mf.skip_all = env_is_true("OPENSSL_TEST_MFAIL_SKIP_ALL");
+ mf.skip_slow = env_is_true("OPENSSL_TEST_MFAIL_SKIP_SLOW");
+ mf.single_point = env_int("OPENSSL_TEST_MFAIL_POINT", -1);
+ mf.start_point = env_int("OPENSSL_TEST_MFAIL_START", 0);
+ mf.env_count = env_int("OPENSSL_TEST_MFAIL_COUNT", 0);
+ mf.slow_threshold = env_int("OPENSSL_TEST_MFAIL_SLOW", 1000);
+ mf.print_bt = env_is_true("OPENSSL_TEST_MFAIL_BACKTRACE");
+
+ /* if optional and nothing configured, then no point installing hooks */
+ if (optional && mf.env_count <= 0 && mf.single_point < 0)
+ return 0;
+
+ if (!CRYPTO_set_mem_functions(mf_malloc, mf_realloc, mf_free))
+ return -1;
+
+ mf.installed = 1;
+ mf.fail_after = -1;
+ return 1;
+}
+
+int mfail_is_installed(void)
+{
+ return mf.installed;
+}
+
+int mfail_env_skip_all(void)
+{
+ return !mf.installed || mf.skip_all;
+}
+
+int mfail_env_skip_slow(void)
+{
+ return !mf.installed || mf.skip_slow;
+}
+
+/* The i-th of n points distributed over [start, total), rotated by seq */
+static int compute_point(int i, int total, int n, int seq, int start)
+{
+ int range = total - start;
+ int stride_int, stride_rem_x2, stride_rnd, offset, p;
+
+ if (range <= 0 || n <= 0)
+ return start;
+
+ stride_int = range / n;
+ stride_rem_x2 = (range - stride_int * n) * 2;
+ stride_rnd = stride_int + (stride_rem_x2 >= n ? 1 : 0);
+ offset = (stride_rnd > 0) ? (seq % stride_rnd) : 0;
+
+ p = (int)(((long)i * range + n / 2) / n) + offset;
+ if (p >= range)
+ p = range - 1;
+ if (p < 0)
+ p = 0;
+ return start + p;
+}
+
+void mfail_init(int seq, int flags)
+{
+ mf.seq = seq;
+ mf.iter_index = 0;
+ mf.n = 0;
+ mf.total = 0;
+ mf.iterations = 0;
+ mf.started = 0;
+ mf.fail_after = -1;
+ mf.alloc_count = 0;
+ mf.triggered = 0;
+ mf.counting = 0;
+ mf.slow_skipped = 0;
+
+ if (mf.single_point >= 0) {
+ mf.mode = MFAIL_MODE_SINGLE;
+ } else if ((flags & MFAIL_FLAG_COUNT) && mf.env_count > 0) {
+ mf.mode = MFAIL_MODE_SAMPLED;
+ } else {
+ mf.mode = MFAIL_MODE_EXHAUSTIVE;
+ }
+ mf.phase = MFAIL_PHASE_COUNTING;
+ mf.next_point = -1;
+}
+
+int mfail_has_next(void)
+{
+ if (mf.started) {
+ mf.iterations++;
+ switch (mf.phase) {
+ case MFAIL_PHASE_COUNTING:
+ mf.total = mf.alloc_count;
+ if (mf.skip_slow && mf.total > mf.slow_threshold) {
+ mf.slow_skipped = 1;
+ mf.phase = MFAIL_PHASE_DONE;
+ break;
+ }
+ if (mf.mode == MFAIL_MODE_SINGLE) {
+ mf.phase = MFAIL_PHASE_INJECTING;
+ mf.next_point = mf.single_point;
+ } else if (mf.mode == MFAIL_MODE_EXHAUSTIVE) {
+ if (mf.total > mf.start_point) {
+ mf.phase = MFAIL_PHASE_INJECTING;
+ mf.next_point = mf.start_point;
+ } else {
+ mf.phase = MFAIL_PHASE_DONE;
+ }
+ } else { /* mf.mode is MFAIL_MODE_SAMPLED */
+ mf.n = mf.env_count;
+ if (mf.n > mf.total)
+ mf.n = mf.total;
+ if (mf.n > 0 && mf.total > mf.start_point) {
+ mf.phase = MFAIL_PHASE_INJECTING;
+ mf.iter_index = 0;
+ mf.next_point = compute_point(0, mf.total, mf.n, mf.seq,
+ mf.start_point);
+ } else {
+ mf.phase = MFAIL_PHASE_DONE;
+ }
+ }
+ break;
+ case MFAIL_PHASE_INJECTING:
+ if (mf.mode == MFAIL_MODE_SINGLE) {
+ mf.phase = MFAIL_PHASE_DONE;
+ } else if (mf.mode == MFAIL_MODE_EXHAUSTIVE) {
+ if (++mf.next_point >= mf.total)
+ mf.phase = MFAIL_PHASE_DONE;
+ } else { /* SAMPLED */
+ if (++mf.iter_index >= mf.n)
+ mf.phase = MFAIL_PHASE_DONE;
+ else
+ mf.next_point = compute_point(mf.iter_index, mf.total,
+ mf.n, mf.seq, mf.start_point);
+ }
+ break;
+ case MFAIL_PHASE_DONE:
+ default:
+ break;
+ }
+ } else {
+ mf.started = 1;
+ }
+
+ if (mf.phase == MFAIL_PHASE_DONE)
+ return 0;
+
+ mf.alloc_count = 0;
+ mf.counting = 0;
+ mf.triggered = 0;
+ mf.fail_after = (mf.phase == MFAIL_PHASE_INJECTING) ? mf.next_point : -1;
+ return 1;
+}
+
+void mfail_start(void)
+{
+ mf.alloc_count = 0;
+ mf.counting = 1;
+}
+
+void mfail_end(void)
+{
+ mf.counting = 0;
+}
+
+void mfail_arm_once(int point)
+{
+ mf.fail_after = point;
+ mf.alloc_count = 0;
+ mf.triggered = 0;
+}
+
+void mfail_disarm(void)
+{
+ mf.fail_after = -1;
+ mf.alloc_count = 0;
+ mf.triggered = 0;
+}
+
+int mfail_was_triggered(void)
+{
+ return mf.triggered;
+}
+
+int mfail_was_slow_skipped(void)
+{
+ return mf.slow_skipped;
+}
+
+int mfail_get_count(void)
+{
+ return mf.alloc_count;
+}
+
+int mfail_get_total(void)
+{
+ return mf.total;
+}
+
+int mfail_get_phase(void)
+{
+ return mf.phase;
+}
+
+int mfail_get_mode(void)
+{
+ return mf.mode;
+}
+
+int mfail_iterations(void)
+{
+ return mf.iterations;
+}
+
+int mfail_get_slow_threshold(void)
+{
+ return mf.slow_threshold;
+}
+
+int mfail_get_point(void)
+{
+ return (mf.phase == MFAIL_PHASE_INJECTING) ? mf.next_point : -1;
+}
diff --git a/test/mfail/mfail.h b/test/mfail/mfail.h
new file mode 100644
index 0000000000..35888423f8
--- /dev/null
+++ b/test/mfail/mfail.h
@@ -0,0 +1,67 @@
+/*
+ * 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
+ */
+
+#ifndef OSSL_TEST_MFAIL_H
+#define OSSL_TEST_MFAIL_H
+
+/* Flags for mfail_init(). */
+#define MFAIL_FLAG_COUNT (1 << 0)
+
+/* Modes */
+#define MFAIL_MODE_EXHAUSTIVE 0
+#define MFAIL_MODE_SAMPLED 1
+#define MFAIL_MODE_SINGLE 2
+
+/* Phases */
+#define MFAIL_PHASE_DONE 0
+#define MFAIL_PHASE_COUNTING 1
+#define MFAIL_PHASE_INJECTING 2
+
+/* Install mem hooks */
+int mfail_install(int optional);
+/* Check if hooks installed */
+int mfail_is_installed(void);
+/* Initialize the mfail for test case runs */
+void mfail_init(int seq, int flags);
+/* Check for the failure loop if another fail execution should be done */
+int mfail_has_next(void);
+/* Start the failure triggering block */
+void mfail_start(void);
+/* End the failure triggering block */
+void mfail_end(void);
+/* Check if the failure was triggered in the block */
+int mfail_was_triggered(void);
+/* Check if the inject phase was skipped because it got over slow threshold */
+int mfail_was_slow_skipped(void);
+/* If the counting was executed, get the total number of allocations */
+int mfail_get_count(void);
+/* Get the total number of failure points */
+int mfail_get_total(void);
+/* Get the number of iterations that run */
+int mfail_iterations(void);
+/* Get the current failure point */
+int mfail_get_point(void);
+/* Get execution phase */
+int mfail_get_phase(void);
+/* Get execution mode */
+int mfail_get_mode(void);
+/* Get the configured slow threshold */
+int mfail_get_slow_threshold(void);
+
+/* Low level arming at specific point (instead of start) */
+void mfail_arm_once(int point);
+/* Low level disarming (similar to end) */
+void mfail_disarm(void);
+
+/* Check whether to skip all tests */
+int mfail_env_skip_all(void);
+/* Check whether to skip only slow tests */
+int mfail_env_skip_slow(void);
+
+#endif /* OSSL_TEST_MFAIL_H */
diff --git a/test/testutil.h b/test/testutil.h
index 459f09e915..31e5f87412 100644
--- a/test/testutil.h
+++ b/test/testutil.h
@@ -20,6 +20,7 @@
#include <openssl/bn.h>
#include <openssl/x509.h>
#include "opt.h"
+#include "mfail/mfail.h"
/*-
* Simple unit tests should implement setup_tests().
@@ -64,13 +65,19 @@
* injected, asserts test_fn returns 0. When no failure is injected
* (all allocation points exhausted), asserts test_fn returns 1 and stops.
*
- * The slow variant is for marking the slow test that can be skipped using
- * environment variable.
+ * The NO_CHECK variant disables the assertion that failed tests must
+ * result in function failure.
*
* test_fn has no parameters and returns 1 on success, 0 on failure.
*/
-#define ADD_MFAIL_TEST(test_fn) add_mfail_test(#test_fn, test_fn, 0)
-#define ADD_MFAIL_SLOW_TEST(test_fn) add_mfail_test(#test_fn, test_fn, 1)
+
+/* Per-test flags for add_mfail_test() */
+#define MFAIL_TEST_NO_CHECK (1 << 0)
+
+#define ADD_MFAIL_TEST(test_fn) \
+ add_mfail_test(#test_fn, test_fn, 0)
+#define ADD_MFAIL_NO_CHECK_TEST(test_fn) \
+ add_mfail_test(#test_fn, test_fn, MFAIL_TEST_NO_CHECK)
/*
* A variant of the same without TAP output.
@@ -242,17 +249,8 @@ int test_arg_libctx(OSSL_LIB_CTX **libctx, OSSL_PROVIDER **default_null_prov,
void add_test(const char *test_case_name, int (*test_fn)(void));
void add_all_tests(const char *test_case_name, int (*test_fn)(int idx), int num,
int subtest);
-void add_mfail_test(const char *test_case_name, int (*test_fn)(void), int slow);
-
-/*
- * Start the memory allocation failure counter.
- */
-void mfail_start(void);
-
-/*
- * Stop the memory allocation failure counter.
- */
-void mfail_end(void);
+void add_mfail_test(const char *test_case_name, int (*test_fn)(void),
+ int flags);
#define MFAIL_start mfail_start
#define MFAIL_end mfail_end
diff --git a/test/testutil/driver.c b/test/testutil/driver.c
index 0b8391eaa6..2742cc3908 100644
--- a/test/testutil/driver.c
+++ b/test/testutil/driver.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2023 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2016-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
@@ -19,6 +19,9 @@
#include "platform.h" /* From libapps */
+#include "mfail.h"
+#include <time.h>
+
#if defined(_WIN32) && !defined(__BORLANDC__)
#define strdup _strdup
#endif
@@ -35,7 +38,7 @@ typedef struct test_info {
/* flags */
unsigned int subtest : 1;
unsigned int mfail : 1;
- unsigned int mfail_slow : 1;
+ int mfail_flags;
} TEST_INFO;
static TEST_INFO all_tests[1024];
@@ -46,7 +49,6 @@ static int single_iter = -1;
static int level = 0;
static int seed = 0;
static int rand_order = 0;
-static int mfail_added = 0;
/*
* A parameterised test runs a loop of test cases.
@@ -82,17 +84,16 @@ void add_all_tests(const char *test_case_name, int (*test_fn)(int idx),
num_test_cases += num;
}
-void add_mfail_test(const char *test_case_name, int (*test_fn)(void), int slow)
+void add_mfail_test(const char *test_case_name, int (*test_fn)(void), int flags)
{
assert(num_tests != OSSL_NELEM(all_tests));
all_tests[num_tests].test_case_name = test_case_name;
all_tests[num_tests].test_fn = test_fn;
all_tests[num_tests].num = -1;
all_tests[num_tests].mfail = 1;
- all_tests[num_tests].mfail_slow = slow ? 1 : 0;
+ all_tests[num_tests].mfail_flags = flags;
++num_tests;
++num_test_cases;
- mfail_added = 1;
}
static int gcd(int a, int b)
@@ -294,6 +295,64 @@ static void test_verdict(int verdict,
test_flush_tapout();
}
+static double mfail_elapsed_secs(clock_t start)
+{
+ return (double)(clock() - start) / CLOCKS_PER_SEC;
+}
+
+static int mfail_should_skip(void)
+{
+ if (!mfail_is_installed())
+ return 1;
+ return mfail_env_skip_all();
+}
+
+static int mfail_run_test(const char *test_case_name,
+ int (*test_fn)(void), int flags)
+{
+ int ret = 1;
+ int no_check = (flags & MFAIL_TEST_NO_CHECK) != 0;
+ clock_t start = clock();
+
+ mfail_init(0, 0);
+
+ while (mfail_has_next()) {
+ int rv;
+
+ ERR_clear_error();
+ rv = test_fn();
+
+ if (mfail_was_triggered()) {
+ if (!no_check && !TEST_int_eq(rv, 0)) {
+ TEST_error("mfail test '%s': allocation failure at point %d "
+ "not handled",
+ test_case_name, mfail_get_point());
+ ret = 0;
+ }
+ } else if (mfail_get_mode() == MFAIL_MODE_SINGLE) {
+ TEST_info("mfail test '%s': point %d is beyond the last "
+ "allocation point, test %s",
+ test_case_name, mfail_get_point(),
+ rv == 1 ? "succeeded" : "failed");
+ } else if (!TEST_int_eq(rv, 1)) {
+ TEST_error("mfail test '%s': no injection but test failed",
+ test_case_name);
+ ret = 0;
+ }
+ }
+
+ if (ret != 0 && mfail_was_slow_skipped())
+ return TEST_skip("mfail test '%s': %d allocations exceeds slow "
+ "threshold %d",
+ test_case_name, mfail_get_total(),
+ mfail_get_slow_threshold());
+
+ TEST_info("mfail test '%s': %d allocations, %d iterations, %.6f seconds",
+ test_case_name, mfail_get_total(), mfail_iterations(),
+ mfail_elapsed_secs(start));
+ return ret;
+}
+
int run_tests(const char *test_prog_name)
{
int num_failed = 0;
@@ -321,9 +380,6 @@ int run_tests(const char *test_prog_name)
test_flush_tapout();
- if (mfail_added)
- mfail_init();
-
for (i = 0; i < num_tests; i++)
permute[i] = i;
if (rand_order != 0)
@@ -353,11 +409,11 @@ int run_tests(const char *test_prog_name)
set_test_title(all_tests[i].test_case_name);
ERR_clear_error();
if (all_tests[i].mfail)
- if (mfail_should_skip(all_tests[i].mfail_slow))
+ if (mfail_should_skip())
verdict = TEST_skip("mfail test skipped");
else
verdict = mfail_run_test(all_tests[i].test_case_name,
- all_tests[i].test_fn);
+ all_tests[i].test_fn, all_tests[i].mfail_flags);
else
verdict = all_tests[i].test_fn();
finalize(verdict != 0);
diff --git a/test/testutil/main.c b/test/testutil/main.c
index 80ca7062be..940d25f707 100644
--- a/test/testutil/main.c
+++ b/test/testutil/main.c
@@ -31,7 +31,10 @@ int main(int argc, char *argv[])
int setup_res;
int gi_ret;
- mfail_install();
+ if (mfail_install(0) < 0) {
+ test_printf_stderr("MFAIL installation failed - aborting\n");
+ return ret;
+ }
gi_ret = global_init();
diff --git a/test/testutil/mfail.c b/test/testutil/mfail.c
deleted file mode 100644
index e002eafe03..0000000000
--- a/test/testutil/mfail.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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;
-}
diff --git a/test/testutil/tu_local.h b/test/testutil/tu_local.h
index b7169f8a97..6b900d19b4 100644
--- a/test/testutil/tu_local.h
+++ b/test/testutil/tu_local.h
@@ -49,11 +49,6 @@ void test_fail_memory_message(const char *prefix, const char *file,
__owur int setup_test_framework(int argc, char *argv[]);
__owur int pulldown_test_framework(int ret);
-void mfail_install(void);
-void mfail_init(void);
-int mfail_should_skip(int slow);
-int mfail_run_test(const char *test_case_name, int (*test_fn)(void));
-
__owur int run_tests(const char *test_prog_name);
void set_test_title(const char *title);