ble: retry more often/faster, also when no buffer is available
What changed, and why it matters
This commit changes how Blockstream Jade's Bluetooth Low Energy (BLE) code retries sending data. Previously, if the system ran out of temporary memory buffers, the code would crash with an assertion failure. Now it treats that situation like any other send failure and retries. It also retries more often and faster to stay within typical BLE timeouts, and reduces how often warning messages are logged. The change is a robustness improvement rather than a clear security fix, but the removed assertion crash could have been triggered by an attacker exhausting buffers.
Review whether the new retry loop can be abused to keep the BLE task busy or to delay other operations. Confirm that ble_hs_mbuf_from_flat() failure is truly transient and that retrying does not starve other tasks. Consider whether an attacker can force buffer exhaustion repeatedly to cause denial of service. No immediate patch action is indicated beyond normal review and testing.
Security signals we found
Removed assertion on memory-buffer allocation (JADE_ASSERT(data)) that could crash the device
Added retry logic for buffer exhaustion instead of hard failure
Reduced retry interval and increased retry count to meet BLE timeout expectations
Throttled warning logs to reduce log noise
Evidence from the diff
In main/ble/ble.c, write_ble() no longer asserts that ble_hs_mbuf_from_flat() returns a non-NULL mbuf. Instead, a NULL result sets rc = ESP_FAIL and the loop retries. The retry delay drops from 100 ms to 10 ms, max tries rises from 10 to 209 (approx. 2 s), and warning logs are throttled to every 16th attempt. The previous JADE_ASSERT(data) would abort the firmware if the NimBLE host had no free mbufs. The patch removes that abort path and converts it into a transient retry condition.
Changed components
main/ble/ble.cBLE GATT indicate/send pathNimBLE mbuf allocation handlingInspect captured patch +12 / −8
diff --git a/main/ble/ble.c b/main/ble/ble.c
index 227ebe0..73b45fd 100644
--- a/main/ble/ble.c
+++ b/main/ble/ble.c
@@ -315,23 +315,27 @@ static bool write_ble(const uint8_t* msg, const size_t towrite, void* ignore)
while (written < towrite) {
const size_t writenow = written + ble_max_write_size <= towrite ? ble_max_write_size : towrite - written;
int rc = 0, try = 0;
+ const int max_tries = 13 * 16 + 1; // 209, i.e. approx 2s of 10ms waits
do {
++try;
// os_mbuf data is consumed by indicate_custom, regardless of the outcome
struct os_mbuf* data = ble_hs_mbuf_from_flat(msg + written, writenow);
- JADE_ASSERT(data);
-
- rc = ble_gatts_indicate_custom(peer_conn_handle, tx_val_handle, data);
+ if (!data) {
+ rc = ESP_FAIL;
+ } else {
+ rc = ble_gatts_indicate_custom(peer_conn_handle, tx_val_handle, data);
+ }
if (rc != 0) {
- JADE_LOGW("ble_gattc_indicate_custom() returned error %d trying to write %u bytes, attempt %u", rc,
- writenow, try);
- vTaskDelay(100 / portTICK_PERIOD_MS);
+ if ((try % 16) == 1) {
+ JADE_LOGW("write_ble error %d writing %u bytes, try %d", rc, writenow, try);
+ }
+ vTaskDelay(10 / portTICK_PERIOD_MS);
}
- } while (rc != 0 && try < 10);
+ } while (rc != 0 && try <= max_tries);
if (rc != 0) {
- JADE_LOGE("ble_gattc_indicate_custom() multiple failures writing %u bytes - written %u bytes of %u, bad "
+ JADE_LOGE("write_ble timeout writing %u bytes - written %u bytes of %u, bad "
"connection",
writenow, written, towrite);
// FIXME: fail/error the connection ?
Why this scored 26/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.