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>
diff --git a/crypto/internal.h b/crypto/internal.h index 46a4e70..576ad85 100644 --- a/crypto/internal.h +++ b/crypto/internal.h
@@ -225,6 +225,16 @@ #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.
diff --git a/crypto/mem.c b/crypto/mem.c index abba4a4..97a85e9 100644 --- a/crypto/mem.c +++ b/crypto/mem.c
@@ -146,11 +146,14 @@ 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 @@ 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 @@ 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
diff --git a/crypto/test/gtest_main.h b/crypto/test/gtest_main.h index 20ccf21..05d468e 100644 --- a/crypto/test/gtest_main.h +++ b/crypto/test/gtest_main.h
@@ -31,13 +31,15 @@ #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 @@ // 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 @@ signal(SIGPIPE, SIG_IGN); #endif - testing::UnitTest::GetInstance()->listeners().Append( - new ErrorTestEventListener); + testing::UnitTest::GetInstance()->listeners().Append(new TestEventListener); } BSSL_NAMESPACE_END