fix: whiteListRemove use-after-free — loop continues on invalidated iterator#1172
fix: whiteListRemove use-after-free — loop continues on invalidated iterator#1172sanastasiou 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.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughArrr, whitelist updates now use ChangesWhitelist update safety
Estimated code review effort: 1 (Trivial) | ~5 minutes Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.Summary by CodeRabbit