--- /dev/null
+/*
+ * Copyright 2025 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 <stdio.h>
+#include <string.h>
+
+#include <openssl/crypto.h>
+#include <openssl/ssl.h>
+
+#include "helpers/ssltestlib.h"
+#include "testutil.h"
+
+/**
+ * @brief Global static variables for certificate and key handling.
+ *
+ * These variables store the paths and context used for managing
+ * certificates and private keys in the application.
+ *
+ * - certsdir: Directory containing trusted certificates.
+ * - cert: Path to the certificate file in use.
+ * - privkey: Path to the private key file.
+ * - mcount: Number of mallocs counted
+ * - rcount: Number of reallocs counted
+ * - fcount: Number of frees counted
+ * - scount: Number of mallocs counted prior to workload
+ */
+static char *cert = NULL;
+static char *privkey = NULL;
+static int mcount, rcount, fcount, scount;
+
+/**
+ * @brief Performs an SSL/TLS handshake between a test client and server.
+ *
+ * This function sets up SSL/TLS contexts and objects for both client and
+ * server, then initiates a handshake to verify successful connection
+ * establishment. It is intended for use in testing scenarios to validate
+ * handshake behavior using specified certificates and keys.
+ *
+ * @return 1 on successful handshake, 0 on failure.
+ *
+ * @note The function uses @c TEST_true() macros to validate intermediate
+ * steps. All SSL objects and contexts are freed before returning.
+ */
+static int do_handshake(OSSL_LIB_CTX *libctx)
+{
+ SSL_CTX *cctx = NULL, *sctx = NULL;
+ SSL *clientssl = NULL, *serverssl = NULL;
+ int testresult = 0;
+
+ if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
+ TLS_client_method(),
+ TLS1_VERSION, 0,
+ &sctx, &cctx, cert, privkey)))
+ return 0;
+
+ /* Now do a handshake */
+ if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
+ &clientssl, NULL, NULL))
+ || !TEST_true(create_ssl_connection(serverssl, clientssl,
+ SSL_ERROR_NONE)))
+ goto end;
+
+ testresult = 1;
+
+end:
+ SSL_free(serverssl);
+ SSL_free(clientssl);
+ SSL_CTX_free(sctx);
+ SSL_CTX_free(cctx);
+
+ return testresult;
+}
+
+/**
+ * @brief run our workload to count the number of allocations we make.
+ *
+ * Creates a new OpenSSL library context and performs a test SSL/TLS
+ * handshake. The number of malloc operations is recorded and printed for
+ * diagnostic purposes.
+ *
+ * @return 1 if the handshake succeeds, 0 otherwise.
+ */
+static int test_record_alloc_counts(void)
+{
+ int ret;
+ OSSL_LIB_CTX *libctx;
+
+ libctx = OSSL_LIB_CTX_new();
+ if (!TEST_ptr(libctx))
+ return 0;
+
+ ret = do_handshake(libctx);
+
+ OSSL_LIB_CTX_free(libctx);
+ libctx = NULL;
+
+ return ret;
+}
+
+/**
+ * @brief run our workload to count the number of allocations we make.
+ *
+ * Creates a new OpenSSL library context and performs a test SSL/TLS
+ * handshake.
+ *
+ * Note this is exactly the same as test_record_alloc_counts with 1 difference
+ * The test always returns 1. We do this because with allocation failures
+ * in effect, we can't expect things to work, so we always return success
+ * so that the test keeps running.
+ */
+static int test_alloc_failures(void)
+{
+ OSSL_LIB_CTX *libctx;
+
+ libctx = OSSL_LIB_CTX_new();
+ if (!TEST_ptr(libctx))
+ return 1;
+
+ do_handshake(libctx);
+
+ OSSL_LIB_CTX_free(libctx);
+ libctx = NULL;
+
+ return 1;
+}
+
+static int test_report_alloc_counts(void)
+{
+ CRYPTO_get_alloc_counts(&mcount, &rcount, &fcount);
+ /*
+ * Report our memory allocations from the count run
+ * NOTE: We report a number of allocations to skip here
+ * (the scount value). These are the allocations that took
+ * place while the test harness itself was getting setup
+ * (i.e. calling OPENSSL_init_crypto/etc). We can't fail
+ * those allocations as they will cause the test to fail before
+ * we have even run the workload. So report them so we can
+ * allow them to function before we start doing any real testing
+ */
+ TEST_info("skip: %d count %d\n", scount, mcount - scount);
+ return 1;
+}
+
+int setup_tests(void)
+{
+ char *opmode = NULL;
+ char *certsdir = NULL;
+
+ if (!TEST_ptr(opmode = test_get_argument(0)))
+ goto err;
+
+ if (!TEST_ptr(certsdir = test_get_argument(1)))
+ goto err;
+
+ cert = test_mk_file_path(certsdir, "servercert.pem");
+ if (cert == NULL)
+ goto err;
+
+ privkey = test_mk_file_path(certsdir, "serverkey.pem");
+ if (privkey == NULL)
+ goto err;
+
+ if (strcmp(opmode, "count") == 0) {
+ CRYPTO_get_alloc_counts(&scount, &rcount, &fcount);
+ ADD_TEST(test_record_alloc_counts);
+ ADD_TEST(test_report_alloc_counts);
+ } else {
+ ADD_TEST(test_alloc_failures);
+ }
+ return 1;
+
+ err:
+ OPENSSL_free(cert);
+ OPENSSL_free(privkey);
+ return 0;
+}
+
+void cleanup_tests(void)
+{
+ OPENSSL_free(cert);
+ OPENSSL_free(privkey);
+}
--- /dev/null
+#! /usr/bin/env perl
+# Copyright 2025 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
+
+use OpenSSL::Test qw/:DEFAULT srctop_dir result_dir/;
+use OpenSSL::Test::Utils;
+use File::Temp qw(tempfile);
+use File::Path 2.00 qw(rmtree mkpath);
+
+setup("test_handshake_memfail");
+
+#
+# Don't run this test if mdebug isn't enabled, it won't work
+#
+plan skip_all => "$test_name requires allocfail-tests to be enabled"
+ if disabled("allocfail-tests");
+
+#
+# We need to know how many mallocs we plan to fail, so run the test in count mode
+# To tell us how many mallocs it executes
+# We capture the result of the test into countinfo.txt
+# and parse that to figure out what our values are
+#
+my $resultdir = result_dir();
+run(test(["handshake-memfail", "count", srctop_dir("test", "certs")], stderr => "$resultdir/countinfo.txt"));
+
+#
+# Read the result file into an array
+#
+open my $handle, '<', "$resultdir/countinfo.txt";
+chomp(my @lines = <$handle>);
+close $handle;
+
+#
+# some line contains our counts, find and split that into an array
+#
+my @vals;
+foreach(@lines) {
+ if ($_ =~/skip:/) {
+ @vals = split ' ', $_;
+ break;
+ }
+}
+
+#
+# The number of mallocs we need to skip is in entry two
+# The number of mallocs to test is in entry 4
+#
+my $skipcount = $vals[2];
+my $malloccount = $vals[4];
+
+#
+# Now we can plan our tests. We plan to run malloccount iterations of this
+# test
+#
+plan tests => $malloccount;
+
+my @seq = (1..$malloccount);
+for my $idx (@seq) {
+ #
+ # We need to setup our openssl malloc failures env var to fail the target malloc
+ # the format of this string is a series of A@B;C@D tuples where A,C are the number
+ # of mallocs to consider, and B,D are the likelyhood that they should fail.
+ # We always skip the first "skip" allocations, then iteratively guarantee that
+ # next <idx> mallocs pass, followed by the next single malloc failing, with the remainder
+ # passing
+ #
+ $ENV{OPENSSL_MALLOC_FAILURES} = "$skipcount\@0;$idx\@0;1\@100;0\@0";
+ ok(run(test(["handshake-memfail", "run", srctop_dir("test", "certs")])));
+}