Skip to content

Commit 47d74ed

Browse files
committed
[Bugfix] scan response timer causing crash on reinit cycle.
Fixes a crash when the stack is reinitialized without clearing all data on deinit then starting scanning.
1 parent 4675434 commit 47d74ed

3 files changed

Lines changed: 43 additions & 6 deletions

File tree

src/NimBLEDevice.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,12 @@ bool NimBLEDevice::init(const std::string& deviceName) {
10291029
bool NimBLEDevice::deinit(bool clearAll) {
10301030
int rc = 0;
10311031
if (m_initialized) {
1032+
# if MYNEWT_VAL(BLE_ROLE_OBSERVER)
1033+
if (NimBLEDevice::m_pScan != nullptr) {
1034+
NimBLEDevice::m_pScan->onHostDeinit();
1035+
}
1036+
# endif
1037+
10321038
rc = nimble_port_stop();
10331039
if (rc == 0) {
10341040
nimble_port_deinit();

src/NimBLEScan.cpp

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,17 @@ NimBLEScan::NimBLEScan()
7373
m_scanParams{0, 0, BLE_HCI_SCAN_FILT_NO_WL, 0, 1, 1},
7474
m_pTaskData{nullptr},
7575
m_maxResults{0xFF} {
76-
ble_npl_callout_init(&m_srTimer, nimble_port_get_dflt_eventq(), NimBLEScan::srTimerCb, nullptr);
7776
ble_npl_time_ms_to_ticks(DEFAULT_SCAN_RESP_TIMEOUT_MS, &m_srTimeoutTicks);
7877
} // NimBLEScan::NimBLEScan
7978

8079
/**
8180
* @brief Scan destructor, release any allocated resources.
8281
*/
8382
NimBLEScan::~NimBLEScan() {
84-
ble_npl_callout_deinit(&m_srTimer);
83+
if (m_srTimerInitialized) {
84+
ble_npl_callout_deinit(&m_srTimer);
85+
m_srTimerInitialized = false;
86+
}
8587

8688
for (const auto& dev : m_scanResults.m_deviceVec) {
8789
delete dev;
@@ -167,7 +169,9 @@ void NimBLEScan::removeWaitingDevice(NimBLEAdvertisedDevice* pDev) {
167169
void NimBLEScan::clearWaitingList() {
168170
// Stop the timer and remove any pending timeout events since we're clearing
169171
// the list and won't be processing any more timeouts for these devices
170-
ble_npl_callout_stop(&m_srTimer);
172+
if (m_srTimerInitialized) {
173+
ble_npl_callout_stop(&m_srTimer);
174+
}
171175
ble_npl_hw_enter_critical();
172176
NimBLEAdvertisedDevice* current = m_pWaitingListHead;
173177
while (current != nullptr) {
@@ -185,10 +189,19 @@ void NimBLEScan::clearWaitingList() {
185189
*/
186190
void NimBLEScan::resetWaitingTimer() {
187191
if (m_srTimeoutTicks == 0 || m_pWaitingListHead == nullptr) {
188-
ble_npl_callout_stop(&m_srTimer);
192+
if (m_srTimerInitialized) {
193+
ble_npl_callout_stop(&m_srTimer);
194+
}
189195
return;
190196
}
191197

198+
if (!m_srTimerInitialized) {
199+
m_srTimerInitialized = ble_npl_callout_init(&m_srTimer, nimble_port_get_dflt_eventq(), NimBLEScan::srTimerCb, nullptr) == 0;
200+
if (!m_srTimerInitialized) {
201+
return;
202+
}
203+
}
204+
192205
ble_npl_time_t now = ble_npl_time_get();
193206
ble_npl_time_t elapsed = now - m_pWaitingListHead->m_time;
194207
ble_npl_time_t nextTime = elapsed >= m_srTimeoutTicks ? 1 : m_srTimeoutTicks - elapsed;
@@ -335,7 +348,9 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
335348
}
336349

337350
case BLE_GAP_EVENT_DISC_COMPLETE: {
338-
ble_npl_callout_stop(&pScan->m_srTimer);
351+
if (pScan->m_srTimerInitialized) {
352+
ble_npl_callout_stop(&pScan->m_srTimer);
353+
}
339354

340355
// If we have any scannable devices that haven't received a scan response,
341356
// we should trigger the callback with whatever data we have since the scan is complete
@@ -381,7 +396,9 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
381396
*/
382397
void NimBLEScan::setScanResponseTimeout(uint32_t timeoutMs) {
383398
if (timeoutMs == 0) {
384-
ble_npl_callout_stop(&m_srTimer);
399+
if (m_srTimerInitialized) {
400+
ble_npl_callout_stop(&m_srTimer);
401+
}
385402
m_srTimeoutTicks = 0;
386403
return;
387404
}
@@ -662,6 +679,18 @@ void NimBLEScan::onHostSync() {
662679
m_pScanCallbacks->onScanEnd(m_scanResults, BLE_HS_ENOTSYNCED);
663680
}
664681

682+
/**
683+
* @brief Called before host deinit so callouts don't outlive the default event queue.
684+
*/
685+
void NimBLEScan::onHostDeinit() {
686+
clearWaitingList();
687+
688+
if (m_srTimerInitialized) {
689+
ble_npl_callout_deinit(&m_srTimer);
690+
m_srTimerInitialized = false;
691+
}
692+
}
693+
665694
/**
666695
* @brief Start scanning and block until scanning has been completed.
667696
* @param [in] duration The duration in milliseconds for which to scan.

src/NimBLEScan.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ class NimBLEScan {
181181
~NimBLEScan();
182182
static int handleGapEvent(ble_gap_event* event, void* arg);
183183
void onHostSync();
184+
void onHostDeinit();
184185
static void srTimerCb(ble_npl_event* event);
185186

186187
// Linked list helpers for devices awaiting scan responses
@@ -194,6 +195,7 @@ class NimBLEScan {
194195
NimBLEScanResults m_scanResults;
195196
NimBLEUtils::TaskData* m_pTaskData;
196197
ble_npl_callout m_srTimer{};
198+
bool m_srTimerInitialized{false};
197199
ble_npl_time_t m_srTimeoutTicks{};
198200
uint8_t m_maxResults;
199201
NimBLEAdvertisedDevice* m_pWaitingListHead{}; // head of linked list for devices awaiting scan responses

0 commit comments

Comments
 (0)