Partially mitigate quadratic-time malloc tests in unit tests

Malloc failure testing is quadratic in the number of allocations. To
test a failure at allocation N, we must first run the previous N-1
allocations. Now that we have combined GTest binaries, this does not
work very well.

Use the test listener to reset the counter across independent tests. We
assume failures in a previous test won't interfere in the next one and
run each test's counter in parallel.

The assumption isn't *quite* true because we have a lot of internal
init-once machinery that is reused across otherwise "independent" tests,
but it's close enough that I was able to find some bugs, fixed in the
next commit. That said, the tests still take too long to run to
completion.

Bug: 127
Change-Id: I6836793448fbdc740a8cc424361e6b3dd66fb8a6
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/56926
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
fips-20230428
David Benjamin 2 years ago committed by Boringssl LUCI CQ
parent a9ce915318
commit 5e356a8a9a
  1. 10
      crypto/internal.h
  2. 14
      crypto/mem.c
  3. 18
      crypto/test/gtest_main.h

@ -225,6 +225,16 @@ typedef __uint128_t uint128_t;
#define OPENSSL_SSE2
#endif
#if defined(BORINGSSL_MALLOC_FAILURE_TESTING)
// OPENSSL_reset_malloc_counter_for_testing, when malloc testing is enabled,
// resets the internal malloc counter, to simulate further malloc failures. This
// should be called in between independent tests, at a point where failure from
// a previous test will not impact subsequent ones.
OPENSSL_EXPORT void OPENSSL_reset_malloc_counter_for_testing(void);
#else
OPENSSL_INLINE void OPENSSL_reset_malloc_counter_for_testing(void) {}
#endif
// Pointer utility functions.

@ -146,11 +146,14 @@ static struct CRYPTO_STATIC_MUTEX malloc_failure_lock =
CRYPTO_STATIC_MUTEX_INIT;
static uint64_t current_malloc_count = 0;
static uint64_t malloc_number_to_fail = 0;
static int malloc_failure_enabled = 0, break_on_malloc_fail = 0;
static int malloc_failure_enabled = 0, break_on_malloc_fail = 0,
any_malloc_failed = 0;
static void malloc_exit_handler(void) {
CRYPTO_STATIC_MUTEX_lock_read(&malloc_failure_lock);
if (malloc_failure_enabled && current_malloc_count > malloc_number_to_fail) {
if (any_malloc_failed) {
// Signal to the test driver that some allocation failed, so it knows to
// increment the counter and continue.
_exit(88);
}
CRYPTO_STATIC_MUTEX_unlock_read(&malloc_failure_lock);
@ -183,6 +186,7 @@ static int should_fail_allocation() {
CRYPTO_STATIC_MUTEX_lock_write(&malloc_failure_lock);
int should_fail = current_malloc_count == malloc_number_to_fail;
current_malloc_count++;
any_malloc_failed = any_malloc_failed || should_fail;
CRYPTO_STATIC_MUTEX_unlock_write(&malloc_failure_lock);
if (should_fail && break_on_malloc_fail) {
@ -194,6 +198,12 @@ static int should_fail_allocation() {
return should_fail;
}
void OPENSSL_reset_malloc_counter_for_testing(void) {
CRYPTO_STATIC_MUTEX_lock_write(&malloc_failure_lock);
current_malloc_count = 0;
CRYPTO_STATIC_MUTEX_unlock_write(&malloc_failure_lock);
}
#else
static int should_fail_allocation(void) { return 0; }
#endif

@ -31,13 +31,15 @@ OPENSSL_MSVC_PRAGMA(warning(pop))
#include <signal.h>
#endif
#include "../internal.h"
BSSL_NAMESPACE_BEGIN
class ErrorTestEventListener : public testing::EmptyTestEventListener {
class TestEventListener : public testing::EmptyTestEventListener {
public:
ErrorTestEventListener() {}
~ErrorTestEventListener() override {}
TestEventListener() {}
~TestEventListener() override {}
void OnTestEnd(const testing::TestInfo &test_info) override {
if (test_info.result()->Failed()) {
@ -48,6 +50,13 @@ class ErrorTestEventListener : public testing::EmptyTestEventListener {
// error queue without printing.
ERR_clear_error();
}
// Malloc failure testing is quadratic in the number of mallocs. Running
// multiple tests sequentially thus scales badly. Reset the malloc counter
// between tests. This way we will test, each test with the first allocation
// failing, then the second, and so on, until the test with the most
// allocations runs out.
OPENSSL_reset_malloc_counter_for_testing();
}
};
@ -75,8 +84,7 @@ inline void SetupGoogleTest() {
signal(SIGPIPE, SIG_IGN);
#endif
testing::UnitTest::GetInstance()->listeners().Append(
new ErrorTestEventListener);
testing::UnitTest::GetInstance()->listeners().Append(new TestEventListener);
}
BSSL_NAMESPACE_END

Loading…
Cancel
Save