From 98347d76337d8f99e31bc672e3d1c9718dd42f6c Mon Sep 17 00:00:00 2001 From: Stefanos Anastasiou Date: Mon, 10 Aug 2026 21:46:11 +0200 Subject: [PATCH] nimble/host: Evict stale resolving list entry before adding Some controllers reject LE Add Device To Resolving List with Invalid HCI Command Parameters (0x12) when an entry for that peer identity already exists. ble_hs_pvcy_add_entry() does not handle this: the error is returned to ble_store_write_peer_sec(), which discards it ("There is not much to do here if it fails"). The controller is then left resolving the OLD IRK. This is reachable whenever a peer re-pairs over an existing bond. Peers that rotate their IRK on unpair -- Android does -- become permanently unresolvable afterwards: the controller silently drops their CONNECT_IND because it still holds the previous IRK, and the host never learns why. The only recovery is a reset that rebuilds the resolving list from the bond store. Peers with a stable IRK are unaffected, which is why this tends to show up as "Android cannot reconnect after re-pairing, iOS is fine". Remove any existing entry for the identity before the add. Removing an absent entry is harmless, so it needs no prior lookup -- and the resolving list cannot be read back, so a lookup is not possible in the general case anyway. Note this calls ble_hs_pvcy_remove_entry_hci() rather than ble_hs_pvcy_remove_entry(). The latter wraps the command in its own ble_gap_preempt()/ble_gap_preempt_done() pair, and ble_gap_preempt_done() is not depth counted, so using it here would end the preemption established above before the add is issued. Observed on an ESP32-C3 (NimBLE-Arduino, host based privacy disabled) with rc=530 == BLE_HS_HCI_ERR(0x12) logged from the add path on every re-pair, and fixed by this change on the same hardware. --- nimble/host/src/ble_hs_pvcy.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/nimble/host/src/ble_hs_pvcy.c b/nimble/host/src/ble_hs_pvcy.c index c122633563..fb9ae4ca23 100644 --- a/nimble/host/src/ble_hs_pvcy.c +++ b/nimble/host/src/ble_hs_pvcy.c @@ -152,6 +152,19 @@ ble_hs_pvcy_add_entry(const uint8_t *addr, uint8_t addr_type, */ ble_gap_preempt(); + /* Evict any existing entry for this identity first. Some controllers + * reject LE Add Device To Resolving List with Invalid HCI Command + * Parameters (0x12) when the identity is already present, which would + * leave the OLD IRK resolving after a re-pair. Removing a non-existent + * entry is harmless, so this is safe unconditionally. + * + * Use the _hci variant, not ble_hs_pvcy_remove_entry(): the latter runs + * its own ble_gap_preempt()/ble_gap_preempt_done() pair, and + * ble_gap_preempt_done() is not depth-counted, so calling it here would + * end the preemption before the add below. + */ + ble_hs_pvcy_remove_entry_hci(addr_type, addr); + /* Try to add the entry now that GAP is halted. */ rc = ble_hs_pvcy_add_entry_hci(addr, addr_type, irk);