fix(nordic/ble): adjust advertising interval settings to please apple devices
What changed, and why it matters
This commit changes how a Trezor Bluetooth device broadcasts its presence to Apple devices. It makes the device advertise more frequently at first, then slow down after 30 seconds, and adds a lock to prevent simultaneous changes. There is no clear security bug being fixed; it appears to be a compatibility/stability improvement for iPhones and iPads.
No immediate security action required. Treat as a normal firmware maintenance/Bluetooth compatibility update. If reviewing for security, verify that the new mutex correctly orders bt_le_adv_stop/start calls with the timer work handler and that global state (advertising, advertising_wl, adv_options) remains consistent on error paths.
Security signals we found
No explicit security relevance stated by vendor
No CVE or advisory referenced
Changes are compatibility/robustness oriented (advertising intervals, synchronization)
Mutex addition reduces race-condition surface between start/stop and timer callback
No input parsing, buffer handling, cryptographic, or authorization changes observed
Evidence from the diff
The patch modifies advertising.c in the Nordic BLE firmware. It replaces fixed advertising intervals (160–1600 units, i.e., 100 ms–1 s) with fast (20–25 ms) and slow (152.5–211.25 ms) intervals, and starts a 30-second timer to switch from fast to slow advertising. It also adds a mutex around start/stop operations and stores advertising options in a global variable so the timer work handler can restart advertising with the same options. The commit message frames this as making Apple devices happy, not as a security fix.
Changed components
nordic/trezor/trezor-ble/src/ble/advertising.cTrezor BLE advertising subsystemInspect captured patch +85 / −3
diff --git a/nordic/trezor/trezor-ble/src/ble/advertising.c b/nordic/trezor/trezor-ble/src/ble/advertising.c
index 6cedb820c..5b45737a4 100644
--- a/nordic/trezor/trezor-ble/src/ble/advertising.c
+++ b/nordic/trezor/trezor-ble/src/ble/advertising.c
@@ -37,9 +37,25 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#define ADV_FLAG_DEV_CONNECTED 0x04
#define ADV_FLAG_USER_DISCONNECT 0x08
+#define ADV_INTERVAL_FAST_MIN_MS 20
+#define ADV_INTERVAL_FAST_MAX_MS 25
+#define ADV_INTERVAL_SLOW_MIN_MS 152.5
+#define ADV_INTERVAL_SLOW_MAX_MS 211.25
+
+#define ADV_INTERVAL_MS_TO_UNITS(x) ((int)(x / 0.625))
+
+#define ADV_INTERVAL_FAST_MIN ADV_INTERVAL_MS_TO_UNITS(ADV_INTERVAL_FAST_MIN_MS)
+#define ADV_INTERVAL_FAST_MAX ADV_INTERVAL_MS_TO_UNITS(ADV_INTERVAL_FAST_MAX_MS)
+#define ADV_INTERVAL_SLOW_MIN ADV_INTERVAL_MS_TO_UNITS(ADV_INTERVAL_SLOW_MIN_MS)
+#define ADV_INTERVAL_SLOW_MAX ADV_INTERVAL_MS_TO_UNITS(ADV_INTERVAL_SLOW_MAX_MS)
+
bool advertising = false;
bool advertising_wl = false;
+// Add mutex for synchronization
+static K_MUTEX_DEFINE(adv_mutex);
+
+uint32_t adv_options = 0;
uint8_t manufacturer_data[8] = {0x29, 0x0F, 0, 0, 0, 0, 0, 0};
static struct bt_data advertising_data[2];
@@ -49,6 +65,16 @@ static const struct bt_data scan_response_data[] = {
BT_DATA(BT_DATA_MANUFACTURER_DATA, manufacturer_data, 8),
};
+static void change_adv_work_handler(struct k_work *work);
+static void change_adv_interval_handler(struct k_timer *timer_id);
+K_TIMER_DEFINE(change_adv_timer, change_adv_interval_handler, NULL);
+static K_WORK_DEFINE(change_adv_work, change_adv_work_handler);
+
+static void change_adv_interval_handler(struct k_timer *timer_id) {
+ // This is safe to do in an ISR
+ k_work_submit(&change_adv_work);
+}
+
static void add_to_whitelist(const struct bt_bond_info *info, void *user_data) {
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(&info->addr, addr, sizeof(addr));
@@ -66,11 +92,49 @@ void advertising_setup_wl(void) {
bt_foreach_bond(BT_ID_DEFAULT, add_to_whitelist, NULL);
}
+static void change_adv_work_handler(struct k_work *work) {
+ k_mutex_lock(&adv_mutex, K_FOREVER);
+
+ if (!advertising) {
+ k_mutex_unlock(&adv_mutex);
+ return;
+ }
+
+ int err;
+ LOG_INF("30s timer expired. Switching to slow advertising interval (%d ms).",
+ ADV_INTERVAL_SLOW_MIN);
+
+ // Stop current advertising
+ err = bt_le_adv_stop();
+ if (err) {
+ LOG_ERR("Failed to stop advertising (err %d)", err);
+ k_mutex_unlock(&adv_mutex);
+ return;
+ }
+
+ // Restart advertising with new parameters
+ err = bt_le_adv_start(BT_LE_ADV_PARAM(adv_options, ADV_INTERVAL_SLOW_MIN,
+ ADV_INTERVAL_SLOW_MAX, NULL),
+ advertising_data, ARRAY_SIZE(advertising_data),
+ scan_response_data, ARRAY_SIZE(scan_response_data));
+
+ if (err) {
+ LOG_ERR("Failed to restart advertising with slow interval (err %d)", err);
+ } else {
+ LOG_INF("Successfully restarted advertising with slow interval.");
+ }
+
+ k_mutex_unlock(&adv_mutex);
+}
+
void advertising_start(bool wl, bool user_disconnect, uint8_t color,
uint8_t device_code, bool static_addr, char *name,
int name_len) {
+ k_mutex_lock(&adv_mutex, K_FOREVER);
+
if (advertising) {
LOG_WRN("Restarting advertising");
+ k_timer_stop(&change_adv_timer);
bt_le_adv_stop();
}
int err;
@@ -124,7 +188,10 @@ void advertising_start(bool wl, bool user_disconnect, uint8_t color,
options |= BT_LE_ADV_OPT_USE_IDENTITY;
}
- err = bt_le_adv_start(BT_LE_ADV_PARAM(options, 160, 1600, NULL),
+ adv_options = options;
+
+ err = bt_le_adv_start(BT_LE_ADV_PARAM(options, ADV_INTERVAL_FAST_MIN,
+ ADV_INTERVAL_FAST_MAX, NULL),
advertising_data, ARRAY_SIZE(advertising_data),
scan_response_data, ARRAY_SIZE(scan_response_data));
} else {
@@ -138,38 +205,53 @@ void advertising_start(bool wl, bool user_disconnect, uint8_t color,
options |= BT_LE_ADV_OPT_USE_IDENTITY;
}
- err = bt_le_adv_start(BT_LE_ADV_PARAM(options, 160, 1600, NULL),
+ adv_options = options;
+
+ err = bt_le_adv_start(BT_LE_ADV_PARAM(options, ADV_INTERVAL_FAST_MIN,
+ ADV_INTERVAL_FAST_MAX, NULL),
advertising_data, ARRAY_SIZE(advertising_data),
scan_response_data, ARRAY_SIZE(scan_response_data));
}
if (err) {
LOG_ERR("Advertising failed to start (err %d)", err);
ble_management_send_status_event();
+ k_mutex_unlock(&adv_mutex);
return;
}
advertising = true;
advertising_wl = wl;
+ k_timer_start(&change_adv_timer, K_SECONDS(30), K_NO_WAIT);
+ LOG_INF("Started 30-second timer to switch advertising interval.");
+
ble_management_send_status_event();
+ k_mutex_unlock(&adv_mutex);
}
void advertising_stop(void) {
+ k_mutex_lock(&adv_mutex, K_FOREVER);
+
if (!advertising) {
LOG_WRN("Not advertising");
-
ble_management_send_status_event();
+ k_mutex_unlock(&adv_mutex);
return;
}
+ // Stop the timer first to prevent work handler from running
+ k_timer_stop(&change_adv_timer);
+
int err = bt_le_adv_stop();
if (err) {
LOG_ERR("Advertising failed to stop (err %d)", err);
ble_management_send_status_event();
+ k_mutex_unlock(&adv_mutex);
return;
}
advertising = false;
advertising_wl = false;
ble_management_send_status_event();
+ k_mutex_unlock(&adv_mutex);
}
bool advertising_is_advertising(void) { return advertising; }
Why this scored 18/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.