fix: whiteListRemove use-after-free — loop continues on invalidated iterator#1171
Closed
sanastasiou wants to merge 2 commits into
Closed
fix: whiteListRemove use-after-free — loop continues on invalidated iterator#1171sanastasiou wants to merge 2 commits into
sanastasiou wants to merge 2 commits into
Conversation
…rink whiteListRemove erases the matched element (invalidating the iterator) and shrink-to-fits m_whiteList (freeing the buffer the iterator points into), then continues the loop on the dangling iterator. A layout-dependent spurious re-match then calls erase() with a foreign iterator, and the vector's internal element move writes across unrelated heap memory. Observed on ESP32-C3 as intermittent heap corruption under whitelist churn (multi_heap_free bad-head asserts with address bytes in the corrupted header; a clobbered NimBLE host mutex tripping xQueueSemaphoreTake). Verified fixed under CONFIG_HEAP_POISONING_COMPREHENSIVE plus periodic heap_caps_check_integrity_all() sweeps. whiteListAdd guards duplicates via onWhiteList(), so at most one element can match - breaking out after the removal preserves semantics.
After erasing the last whitelist entry, &m_whiteList[0] indexes an empty vector before ble_gap_wl_set(..., 0). data() is well-defined for empty vectors; same change applied to the whiteListAdd call site for consistency.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
whiteListRemoveiteratesm_whiteListand, on a match:m_whiteList.erase(it)— invalidatesit(and all iterators at/after it),std::vector<NimBLEAddress>(m_whiteList).swap(m_whiteList)— the shrink-to-fit swap frees the ENTIRE bufferitpoints into,++itandit < m_whiteList.end()operate on a dangling iterator into freed memory — undefined behavior on every removal that finds a match.When the freed buffer's stale pointer happens to compare "in range" against the new allocation (pure heap-layout luck),
*it == addressreads freed memory, and a spurious re-match callsm_whiteList.erase(it)with a foreign iterator — the vector's internal element move then writes address-shaped data across unrelated heap memory.Observed in the field (ESP32-C3, arduino-esp32 / IDF 5.5.4) as two distinct intermittent heap-corruption crashes under whitelist churn, both inside or adjacent to
whiteListRemove:assert failed: multi_heap_free multi_heap_poisoning.c:279 (head != NULL)— the vector buffer's heap-block header overwritten with BLE-address bytes (CORRUPT HEAP: Bad head ... Expected 0xabba1234 got 0xbb250000, the25 bbbeing the LSB-first wire bytes of a whitelisted peer address);assert failed: xQueueSemaphoreTake queue.c:1713 (pxQueue->uxItemSize == 0)— the NimBLE host mutex clobbered by the same wild write, tripping on the nextble_hs_lock().Fix:
breakafter the shrink. SincewhiteListAddguards against duplicates (onWhiteList()check beforepush_back), at most one element can match — breaking out after the removal preserves semantics exactly.Verification: with this fix, the same churn workload (repeated whitelist rebuilds around pair/disconnect cycles) runs clean under
CONFIG_HEAP_POISONING_COMPREHENSIVEplus periodicheap_caps_check_integrity_all()sweeps — previously ~2 crashes per 5 cycles.Side note (not touched here to keep the diff minimal):
&m_whiteList[0]on line 751 is also UB when the vector becomes empty after erasing the last entry —m_whiteList.data()would be the safe spelling.