heap: Add new heap_caps_check_integrity_all() & heap_caps_check_integrity_addr() debugging functions

Easier to either check all heaps, or focus on checking a particular region.
This commit is contained in:
Angus Gratton 2017-10-02 14:31:20 +11:00 committed by Angus Gratton
parent 325bd3a4dc
commit f0d7cfdafe
3 changed files with 58 additions and 8 deletions

View file

@ -359,3 +359,17 @@ bool heap_caps_check_integrity(uint32_t caps, bool print_errors)
return valid;
}
bool heap_caps_check_integrity_all(bool print_errors)
{
return heap_caps_check_integrity(MALLOC_CAP_INVALID, print_errors);
}
bool heap_caps_check_integrity_addr(intptr_t addr, bool print_errors)
{
heap_t *heap = find_containing_heap((void *)addr);
if (heap == NULL) {
return false;
}
return multi_heap_check(heap->heap, print_errors);
}

View file

@ -148,7 +148,7 @@ void heap_caps_get_info( multi_heap_info_t *info, uint32_t caps );
/**
* @brief Print a summary of all memory with the given capabilities.
*
* Calls multi_heap_info() on all heaps which share the given capabilities, and
* Calls multi_heap_info on all heaps which share the given capabilities, and
* prints a two-line summary for each, then a total summary.
*
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type
@ -157,14 +157,29 @@ void heap_caps_get_info( multi_heap_info_t *info, uint32_t caps );
*/
void heap_caps_print_heap_info( uint32_t caps );
/**
* @brief Check integrity of all heap memory in the system.
*
* Calls multi_heap_check on all heaps. Optionally print errors if heaps are corrupt.
*
* Calling this function is equivalent to calling heap_caps_check_integrity
* with the caps argument set to MALLOC_CAP_INVALID.
*
* @param print_errors Print specific errors if heap corruption is found.
*
* @return True if all heaps are valid, False if at least one heap is corrupt.
*/
bool heap_caps_check_integrity_all(bool print_errors);
/**
* @brief Check integrity of all heaps with the given capabilities.
*
* Calls multi_heap_check() on all heaps which share the given capabilities. Optionally
* Calls multi_heap_check on all heaps which share the given capabilities. Optionally
* print errors if the heaps are corrupt.
*
* Call ``heap_caps_check_integrity(MALLOC_CAP_INVALID, print_errors)`` to check
* all regions' heaps.
* See also heap_caps_check_integrity_all to check all heap memory
* in the system and heap_caps_check_integrity_addr to check memory
* around a single address.
*
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type
* of memory
@ -174,7 +189,28 @@ void heap_caps_print_heap_info( uint32_t caps );
*/
bool heap_caps_check_integrity(uint32_t caps, bool print_errors);
/**
* @brief Check integrity of heap memory around a given address.
*
* This function can be used to check the integrity of a single region of heap memory,
* which contains the given address.
*
* This can be useful if debugging heap integrity for corruption at a known address,
* as it has a lower overhead than checking all heap regions. Note that if the corrupt
* address moves around between runs (due to timing or other factors) then this approach
* won't work and you should call heap_caps_check_integrity or
* heap_caps_check_integrity_all instead.
*
* @note The entire heap region around the address is checked, not only the adjacent
* heap blocks.
*
* @param addr Address in memory. Check for corruption in region containing this address.
* @param print_errors Print specific errors if heap corruption is found.
*
* @return True if the heap containing the specified address is valid,
* False if at least one heap is corrupt or the address doesn't belong to a heap region.
*/
bool heap_caps_check_integrity_addr(intptr_t addr, bool print_errors);
/**
* @brief Enable malloc() in external memory and set limit below which

View file

@ -40,7 +40,7 @@ The heap implementation (``multi_heap.c``, etc.) includes a lot of assertions wh
If a heap integrity assertion fails, a line will be printed like ``CORRUPT HEAP: multi_heap.c:225 detected at 0x3ffbb71c``. The memory address which is printed is the address of the heap structure which has corrupt content.
It's also possible to manually check heap integrity by calling :cpp:func:`heap_caps_check_integrity` function. This function checks all of requested heap memory for integrity, and can be used even if assertions are disabled. If the integrity check prints an error, it will also contain the address(es) of corrupt heap structures.
It's also possible to manually check heap integrity by calling :cpp:func:`heap_caps_check_integrity_all` or related functions. This function checks all of requested heap memory for integrity, and can be used even if assertions are disabled. If the integrity check prints an error, it will also contain the address(es) of corrupt heap structures.
Finding Heap Corruption
^^^^^^^^^^^^^^^^^^^^^^^
@ -49,7 +49,7 @@ Memory corruption can be one of the hardest classes of bugs to find and fix, as
- A crash with a ``CORRUPT HEAP:`` message will usually include a stack trace, but this stack trace is rarely useful. The crash is the symptom of memory corruption when the system realises the heap is corrupt, but usually the corruption happened elsewhere and earlier in time.
- Increasing the Heap memory debugging `Configuration`_ level to "Light impact" or "Comprehensive" can give you a more accurate message with the first corrupt memory address.
- Adding regular calls to :cpp:func:`heap_caps_check_integrity` in your code will help you pin down the exact time that the corruption happened. You can move these checks around to "close in on" the section of code that corrupted the heap.
- Adding regular calls to :cpp:func:`heap_caps_check_integrity_all` or :cpp:func:`heap_caps_check_integrity_addr` in your code will help you pin down the exact time that the corruption happened. You can move these checks around to "close in on" the section of code that corrupted the heap.
- Based on the memory address which is being corrupted, you can use :ref:`JTAG debugging <jtag-debugging-introduction>` to set a watchpoint on this address and have the CPU halt when it is written to.
- If you don't have JTAG, but you do know roughly when the corruption happens, then you can set a watchpoint in software just beforehand via :cpp:func:`esp_set_watchpoint`. A fatal exception will occur when the watchpoint triggers. For example ``esp_set_watchpoint(0, (void *)addr, 4, ESP_WATCHPOINT_STORE``. Note that watchpoints are per-CPU and are set on the current running CPU only, so if you don't know which CPU is corrupting memory then you will need to call this function on both CPUs.
- For buffer overflows, `heap tracing`_ in ``HEAP_TRACE_ALL`` mode lets you see which callers are allocating from heap. If you can find the function which allocates memory with an address immediately before the address which is corrupted, this will probably be the function which overflows the buffer.
@ -75,7 +75,7 @@ At this level, heap memory is additionally "poisoned" with head and tail "canary
"Basic" heap corruption checks can also detect most out of bounds writes, but this setting is more precise as even a single byte overrun will always be detected. With Basic heap checks, the number of overrun bytes before a failure is detected will depend on the properties of the heap.
Similar to other heap checks, these "canary bytes" are checked via assertion whenever memory is freed and can also be checked manually via :cpp:func:`heap_caps_check_integrity`.
Similar to other heap checks, these "canary bytes" are checked via assertion whenever memory is freed and can also be checked manually via :cpp:func:`heap_caps_check_integrity` or related functions.
This level increases memory usage, each individual allocation will use 9 to 12 additional bytes of memory (depending on alignment).