Skip to content

Commit 97cf5c5

Browse files
dhalbertclaude
andcommitted
espressif/_bleio: guard buffer state shared with the NimBLE host task
CharacteristicBuffer and PacketBuffer ringbufs are filled from the nimble_host task, which preempts the VM task at arbitrary points, but the non-atomic ringbuf operations were unguarded: the comment claiming the BLE host task "won't interrupt us" has been wrong since the code was written (the host task runs at a much higher priority on the same core). PacketBuffer's pending outgoing buffers have the same problem: write() on the VM task appends to them while queue_next_write() on the host task consumes them. Guard these operations with interrupts disabled, mirroring the nordic port's critical sections around the same code, in the same places. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 2bae80a commit 97cf5c5

2 files changed

Lines changed: 36 additions & 5 deletions

File tree

ports/espressif/common-hal/_bleio/CharacteristicBuffer.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,19 @@
1616
#include "shared-bindings/_bleio/__init__.h"
1717
#include "shared-bindings/_bleio/Connection.h"
1818
#include "shared-bindings/_bleio/CharacteristicBuffer.h"
19+
#include "shared-bindings/microcontroller/__init__.h"
1920

2021
#include "supervisor/shared/tick.h"
2122

2223
#include "common-hal/_bleio/ble_events.h"
2324

25+
// The ringbuf is filled from the nimble_host task, which preempts the VM task
26+
// at arbitrary points, and ringbuf operations are not atomic, so guard them
27+
// with interrupts disabled.
28+
29+
// Runs on the nimble_host task.
2430
void bleio_characteristic_buffer_extend(bleio_characteristic_buffer_obj_t *self, const uint8_t *data, size_t len) {
31+
common_hal_mcu_disable_interrupts();
2532
if (self->watch_for_interrupt_char) {
2633
for (uint16_t i = 0; i < len; i++) {
2734
if (data[i] == mp_interrupt_char) {
@@ -34,6 +41,7 @@ void bleio_characteristic_buffer_extend(bleio_characteristic_buffer_obj_t *self,
3441
} else {
3542
ringbuf_put_n(&self->ringbuf, data, len);
3643
}
44+
common_hal_mcu_enable_interrupts();
3745
}
3846

3947
void _common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self,
@@ -70,20 +78,23 @@ uint32_t common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer
7078
}
7179
}
7280

81+
common_hal_mcu_disable_interrupts();
7382
uint32_t num_bytes_read = ringbuf_get_n(&self->ringbuf, data, len);
83+
common_hal_mcu_enable_interrupts();
7484
return num_bytes_read;
7585
}
7686

77-
// NOTE: The nRF port has protection around these operations because the ringbuf
78-
// is filled from an interrupt. On ESP the ringbuf is filled from the BLE host
79-
// task that won't interrupt us.
80-
8187
uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self) {
82-
return ringbuf_num_filled(&self->ringbuf);
88+
common_hal_mcu_disable_interrupts();
89+
uint32_t count = ringbuf_num_filled(&self->ringbuf);
90+
common_hal_mcu_enable_interrupts();
91+
return count;
8392
}
8493

8594
void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self) {
95+
common_hal_mcu_disable_interrupts();
8696
ringbuf_clear(&self->ringbuf);
97+
common_hal_mcu_enable_interrupts();
8798
}
8899

89100
bool common_hal_bleio_characteristic_buffer_deinited(bleio_characteristic_buffer_obj_t *self) {

ports/espressif/common-hal/_bleio/PacketBuffer.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "shared-bindings/_bleio/__init__.h"
1616
#include "shared-bindings/_bleio/Connection.h"
1717
#include "shared-bindings/_bleio/PacketBuffer.h"
18+
#include "shared-bindings/microcontroller/__init__.h"
1819

1920
#include "supervisor/shared/tick.h"
2021
#include "supervisor/shared/bluetooth/serial.h"
@@ -23,6 +24,11 @@
2324

2425
#include "host/ble_att.h"
2526

27+
// The ringbuf and the pending outgoing buffers are shared with the nimble_host
28+
// task, which preempts the VM task at arbitrary points, and ringbuf operations
29+
// are not atomic, so guard the shared accesses with interrupts disabled.
30+
31+
// Runs on the nimble_host task.
2632
void bleio_packet_buffer_extend(bleio_packet_buffer_obj_t *self, uint16_t conn_handle, const uint8_t *data, size_t len) {
2733
if (self->conn_handle != conn_handle) {
2834
return;
@@ -34,6 +40,8 @@ void bleio_packet_buffer_extend(bleio_packet_buffer_obj_t *self, uint16_t conn_h
3440
return;
3541
}
3642

43+
common_hal_mcu_disable_interrupts();
44+
3745
// Make room for the new value by dropping the oldest packets first.
3846
while (ringbuf_num_empty(&self->ringbuf) < len + sizeof(uint16_t)) {
3947
uint16_t packet_length;
@@ -45,6 +53,8 @@ void bleio_packet_buffer_extend(bleio_packet_buffer_obj_t *self, uint16_t conn_h
4553
}
4654
ringbuf_put_n(&self->ringbuf, (uint8_t *)&len, sizeof(uint16_t));
4755
ringbuf_put_n(&self->ringbuf, data, len);
56+
57+
common_hal_mcu_enable_interrupts();
4858
}
4959

5060
static int packet_buffer_on_ble_client_evt(struct ble_gap_event *event, void *param);
@@ -264,6 +274,8 @@ mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self
264274
return 0;
265275
}
266276

277+
common_hal_mcu_disable_interrupts();
278+
267279
// Get packet length, which is in first two bytes of packet.
268280
uint16_t packet_length;
269281
ringbuf_get_n(&self->ringbuf, (uint8_t *)&packet_length, sizeof(uint16_t));
@@ -282,6 +294,8 @@ mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self
282294
ret = packet_length;
283295
}
284296

297+
common_hal_mcu_enable_interrupts();
298+
285299
return ret;
286300
}
287301

@@ -324,6 +338,10 @@ mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, c
324338

325339
size_t num_bytes_written = 0;
326340

341+
// The nimble_host task may modify pending_size, pending_index, and
342+
// packet_queued via queue_next_write(), so guard the append.
343+
common_hal_mcu_disable_interrupts();
344+
327345
uint32_t *pending = self->outgoing[self->pending_index];
328346

329347
if (self->pending_size == 0) {
@@ -335,6 +353,8 @@ mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, c
335353
self->pending_size += len;
336354
num_bytes_written += len;
337355

356+
common_hal_mcu_enable_interrupts();
357+
338358
// If no writes are queued then sneak in this data.
339359
if (!self->packet_queued) {
340360
// This will queue up the packet even if it can't send immediately.

0 commit comments

Comments
 (0)