From 2534e1d8882e7945c6eb79b6b8815ffcd0b6427b Mon Sep 17 00:00:00 2001 From: Stefanos Anastasiou Date: Fri, 17 Jul 2026 00:24:09 +0200 Subject: [PATCH 1/2] fix: whiteListRemove use-after-free - stop iterating after erase + shrink 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. --- src/NimBLEDevice.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index 313abd2f..a5f98b97 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -756,6 +756,10 @@ bool NimBLEDevice::whiteListRemove(const NimBLEAddress& address) { } std::vector(m_whiteList).swap(m_whiteList); + break; // `it` was invalidated by erase() and its buffer freed by the + // swap above; continuing would iterate a dangling iterator. + // Duplicates are impossible (whiteListAdd checks onWhiteList), + // so a single match is total. } } From 947755056ea82d4b984c1976a1037c37f61ef051 Mon Sep 17 00:00:00 2001 From: Stefanos Anastasiou Date: Fri, 17 Jul 2026 00:42:15 +0200 Subject: [PATCH 2/2] fix: use m_whiteList.data() - operator[] on an empty vector is UB 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. --- src/NimBLEDevice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index a5f98b97..e140d620 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -728,7 +728,7 @@ bool NimBLEDevice::onWhiteList(const NimBLEAddress& address) { bool NimBLEDevice::whiteListAdd(const NimBLEAddress& address) { if (!NimBLEDevice::onWhiteList(address)) { m_whiteList.push_back(address); - int rc = ble_gap_wl_set(reinterpret_cast(&m_whiteList[0]), m_whiteList.size()); + int rc = ble_gap_wl_set(reinterpret_cast(m_whiteList.data()), m_whiteList.size()); if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "Failed adding to whitelist rc=%d", rc); m_whiteList.pop_back(); @@ -748,7 +748,7 @@ bool NimBLEDevice::whiteListRemove(const NimBLEAddress& address) { for (auto it = m_whiteList.begin(); it < m_whiteList.end(); ++it) { if (*it == address) { m_whiteList.erase(it); - int rc = ble_gap_wl_set(reinterpret_cast(&m_whiteList[0]), m_whiteList.size()); + int rc = ble_gap_wl_set(reinterpret_cast(m_whiteList.data()), m_whiteList.size()); if (rc != 0) { m_whiteList.push_back(address); NIMBLE_LOGE(LOG_TAG, "Failed removing from whitelist rc=%d", rc);