diff options
-rw-r--r-- | libc/malloc_debug/Config.cpp | 3 | ||||
-rw-r--r-- | libc/malloc_debug/Config.h | 1 | ||||
-rw-r--r-- | libc/malloc_debug/GuardData.cpp | 3 | ||||
-rw-r--r-- | libc/malloc_debug/PointerData.cpp | 8 | ||||
-rw-r--r-- | libc/malloc_debug/README.md | 7 | ||||
-rw-r--r-- | libc/malloc_debug/malloc_debug.cpp | 3 | ||||
-rw-r--r-- | libc/malloc_debug/tests/malloc_debug_config_tests.cpp | 18 | ||||
-rw-r--r-- | libc/malloc_debug/tests/malloc_debug_unit_tests.cpp | 56 |
8 files changed, 98 insertions, 1 deletions
diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp index 926b26581..dd20b5cd5 100644 --- a/libc/malloc_debug/Config.cpp +++ b/libc/malloc_debug/Config.cpp @@ -132,6 +132,9 @@ const std::unordered_map<std::string, Config::OptionInfo> Config::kOptions = { { "verify_pointers", {TRACK_ALLOCS, &Config::VerifyValueEmpty}, }, + { + "abort_on_error", {ABORT_ON_ERROR, &Config::VerifyValueEmpty}, + }, }; bool Config::ParseValue(const std::string& option, const std::string& value, size_t min_value, diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h index 86d1ee4e0..011dc77bd 100644 --- a/libc/malloc_debug/Config.h +++ b/libc/malloc_debug/Config.h @@ -44,6 +44,7 @@ constexpr uint64_t TRACK_ALLOCS = 0x80; constexpr uint64_t LEAK_TRACK = 0x100; constexpr uint64_t RECORD_ALLOCS = 0x200; constexpr uint64_t BACKTRACE_FULL = 0x400; +constexpr uint64_t ABORT_ON_ERROR = 0x800; // In order to guarantee posix compliance, set the minimum alignment // to 8 bytes for 32 bit systems and 16 bytes for 64 bit systems. diff --git a/libc/malloc_debug/GuardData.cpp b/libc/malloc_debug/GuardData.cpp index debc14e0c..c307dc996 100644 --- a/libc/malloc_debug/GuardData.cpp +++ b/libc/malloc_debug/GuardData.cpp @@ -64,6 +64,9 @@ void GuardData::LogFailure(const Header* header, const void* pointer, const void error_log("Backtrace at time of failure:"); BacktraceAndLog(); error_log(LOG_DIVIDER); + if (g_debug->config().options() & ABORT_ON_ERROR) { + abort(); + } } FrontGuardData::FrontGuardData(DebugData* debug_data, const Config& config, size_t* offset) diff --git a/libc/malloc_debug/PointerData.cpp b/libc/malloc_debug/PointerData.cpp index b0e2fc849..638061b11 100644 --- a/libc/malloc_debug/PointerData.cpp +++ b/libc/malloc_debug/PointerData.cpp @@ -206,7 +206,7 @@ void PointerData::Remove(const void* ptr) { std::lock_guard<std::mutex> pointer_guard(pointer_mutex_); auto entry = pointers_.find(pointer); if (entry == pointers_.end()) { - // Error. + // Attempt to remove unknown pointer. error_log("No tracked pointer found for 0x%" PRIxPTR, pointer); return; } @@ -283,6 +283,9 @@ void PointerData::LogFreeError(const FreePointerInfoType& info, size_t usable_si } error_log(LOG_DIVIDER); + if (g_debug->config().options() & ABORT_ON_ERROR) { + abort(); + } } void PointerData::VerifyFreedPointer(const FreePointerInfoType& info) { @@ -295,6 +298,9 @@ void PointerData::VerifyFreedPointer(const FreePointerInfoType& info) { error_log("+++ ALLOCATION 0x%" PRIxPTR " HAS CORRUPTED HEADER TAG 0x%x AFTER FREE", info.pointer, header->tag); error_log(LOG_DIVIDER); + if (g_debug->config().options() & ABORT_ON_ERROR) { + abort(); + } // Stop processing here, it is impossible to tell how the header // may have been damaged. diff --git a/libc/malloc_debug/README.md b/libc/malloc_debug/README.md index a8289b3d1..93b9b1ea1 100644 --- a/libc/malloc_debug/README.md +++ b/libc/malloc_debug/README.md @@ -394,6 +394,13 @@ malloc\_usable\_size, realloc. **NOTE**: This option is not available until the P release of Android. +### abort\_on\_error +When malloc debug detects an error, abort after sending the error +log message. + +**NOTE**: If leak\_track is enabled, no abort occurs if leaks have been +detected when the process is exiting. + Additional Errors ----------------- There are a few other error messages that might appear in the log. diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp index 9075a9caa..2e6afff55 100644 --- a/libc/malloc_debug/malloc_debug.cpp +++ b/libc/malloc_debug/malloc_debug.cpp @@ -154,6 +154,9 @@ static void LogError(const void* pointer, const char* error_str) { error_log("Backtrace at time of failure:"); BacktraceAndLog(); error_log(LOG_DIVIDER); + if (g_debug->config().options() & ABORT_ON_ERROR) { + abort(); + } } static bool VerifyPointer(const void* pointer, const char* function_name) { diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp index a083b4f05..fb54ee54f 100644 --- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp +++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp @@ -725,3 +725,21 @@ TEST_F(MallocDebugConfigTest, record_allocs_max_error) { "value must be <= 50000000: 100000000\n"); ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str()); } + +TEST_F(MallocDebugConfigTest, abort_on_error) { + ASSERT_TRUE(InitConfig("abort_on_error")) << getFakeLogPrint(); + ASSERT_EQ(ABORT_ON_ERROR, config->options()); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); +} + +TEST_F(MallocDebugConfigTest, trigger_abort_fail) { + ASSERT_FALSE(InitConfig("abort_on_error=200")) << getFakeLogPrint(); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + std::string log_msg( + "6 malloc_debug malloc_testing: value set for option 'abort_on_error' " + "which does not take a value\n"); + ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str()); +} diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp index 2d6346fea..44f9795bc 100644 --- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp +++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp @@ -2380,3 +2380,59 @@ TEST_F(MallocDebugTest, verify_pointers) { expected_log += DIVIDER; ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); } + +TEST_F(MallocDebugTest, abort_on_error_log_error) { + Init("abort_on_error verify_pointers"); + + void* pointer = debug_malloc(10); + memset(pointer, 0, 10); + debug_free(pointer); + + ASSERT_STREQ("", getFakeLogBuf().c_str()); + ASSERT_STREQ("", getFakeLogPrint().c_str()); + + EXPECT_DEATH(debug_free(pointer), ""); +} + +TEST_F(MallocDebugTest, abort_on_error_guard_corrupted) { + Init("abort_on_error front_guard=32"); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); + ASSERT_TRUE(pointer != nullptr); + pointer[-16] = 0x00; + EXPECT_DEATH(debug_free(pointer), ""); + pointer[-16] = 0xaa; + debug_free(pointer); +} + +TEST_F(MallocDebugTest, abort_on_error_use_after_free) { + Init("abort_on_error free_track=100 free_track_backtrace_num_frames=0"); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); + ASSERT_TRUE(pointer != nullptr); + memset(pointer, 0, 100); + debug_free(pointer); + + pointer[56] = 0x91; + + EXPECT_DEATH(debug_finalize(), ""); + + pointer[56] = 0xef; +} + +TEST_F(MallocDebugTest, abort_on_error_header_tag_corrupted) { + Init("abort_on_error free_track=100 free_track_backtrace_num_frames=0 rear_guard"); + + uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); + ASSERT_TRUE(pointer != nullptr); + memset(pointer, 0, 100); + debug_free(pointer); + + uint8_t tag_value = pointer[-get_tag_offset()]; + pointer[-get_tag_offset()] = 0x00; + + EXPECT_DEATH(debug_finalize(), ""); + + pointer[-get_tag_offset()] = tag_value; +} + |