Re: [PATCH v3] Bluetooth: hci_sync: pause advertising for the scan address update
From: Luiz Augusto von Dentz
Date: Tue Sep 01 2026 - 10:52:46 EST
Hi Valentin,
On Mon, Aug 31, 2026 at 5:03 AM Valentin Kindschi
<valentin.kindschi@xxxxxxxxx> wrote:
>
> hci_active_scan_sync() calls hci_update_random_address_sync() with
> require_privacy set on every active scan start, which generates a
> non-resolvable private address and programs it with LE Set Random
> Address.
>
> BLUETOOTH CORE SPECIFICATION Vol 4, Part E, 7.8.4 states the controller
> shall return Command Disallowed (0x0C) for LE Set Random Address while
> legacy advertising or scanning is enabled.
>
> Advertising is only stopped beforehand when LL privacy is in use.
> hci_pause_addr_resolution(), called just above, returns early when
> !use_ll_privacy(hdev), so its hci_pause_advertising_sync() never runs.
> On a device that advertises while active scanning - a peripheral that is
> also a central - every scan start therefore issues a
> command the host can already know will be rejected:
>
> Bluetooth: hci0: Opcode 0x2005 failed: -16
>
> The address write is not retried either: hci_set_random_addr_sync()
> defers only when hdev->random_addr is already set, and it never becomes
> set because the write keeps failing, so HCI_RPA_EXPIRED is not used here.
>
> Observed on a BCM43455 with Privacy=off, one advertising instance and
> continuous active discovery, at the scan restart period (~10 s):
>
> < LE Set Random Address Address: 02:16:91:90:F1:D4 (Non-Resolvable)
> > Command Complete LE Set Random Address, Command Disallowed
> < LE Set Random Address Address: 26:90:57:96:9A:3E (Non-Resolvable)
> > Command Complete LE Set Random Address, Command Disallowed
> < LE Set Random Address Address: 16:32:01:BC:B5:CE (Non-Resolvable)
> > Command Complete LE Set Random Address, Command Disallowed
>
> Pause advertising for the address update regardless of privacy, and
> resume it on every exit path. Previously the resume was guarded by
> use_ll_privacy() and only reached on the error path, which matched the
> pause being privacy-only.
>
> One caveat this widens: when HCI_ADVERTISING is set,
> hci_pause_advertising_sync() also clears HCI_DISCOVERABLE and zeroes
> discov_timeout, and hci_resume_advertising_sync() restores only
> HCI_ADVERTISING. That side effect previously happened only with LL
> privacy; it is now reachable on every active scan start, so a device made
> discoverable through mgmt while repeatedly running active discovery would
> lose discoverability. Restoring it belongs in the pause/resume pair rather
> than here, but it is worth flagging.
Sashiko flagged this, so I think we need to fix it; otherwise, the
controller will not be considered discoverable upon resume:
https://sashiko.dev/#/patchset/20260831090232.1726063-1-valentin.kindschi%40fiveco.ch
> With the patch, the same scan restart on the same hardware:
>
> < LE Set Advertising Enable Success
> < LE Set Random Address Success
> < LE Set Scan Parameters Success
> < LE Set Scan Enable Success
> < LE Set Advertising Parameters Success
> < LE Set Advertising Enable Success
>
> Over 35425 records / ~4 min of btmon with continuous active discovery and
> advertising enabled there were no Command Disallowed responses of any
> kind, against one per scan restart before. A central could still connect
> to the device, confirming advertising is restored after the pause.
>
> A second capture on the same device, 221 s with an out-of-range peer
> causing two cancelled outgoing connections, shows the same result under a
> different mix of traffic: 2 LE Set Random Address, both Success, and no
> Command Disallowed of any opcode.
>
> Tooling disclosure (Documentation/process/generated-content.rst): an AI
> coding assistant was used to investigate this and to draft the change;
> the patch text and code are its output, reviewed by me. Inputs were btmon
> captures and kernel logs from the affected device, with the request to
> identify what re-issues LE Set Random Address every ~10 s and to fix it.
> Three earlier explanations it proposed were discarded after being checked
> against the captures: bluetoothd restarting service discovery; RPA
> rotation (excluded, Privacy=off); and the static-address branch of
> hci_update_random_address_sync() (excluded, both random_address and
> static_address read 00:00:00:00:00:00). The cause was only established
> after decoding the command payloads, which showed a freshly generated
> non-resolvable address per attempt. Testing is as described above, on the
> device, using btmon and a second device to confirm connectability.
>
> Fixes: abfeea476c68 ("Bluetooth: hci_sync: Convert MGMT_OP_START_DISCOVERY")
> Cc: stable@xxxxxxxxxxxxxxx
> Assisted-by: Claude:claude-opus-5 btmon
> Signed-off-by: Valentin Kindschi <valentin.kindschi@xxxxxxxxx>
> ---
> Changes in v3:
> - Resend, no code change; v2 had no reply. Rechecked that it still applies
> to bluetooth-next.
> - Added the second capture described above, taken with the two patches from
> the "endless adv params retry" series applied, since in bluetooth-next,
> confirming the fix holds with cancelled outgoing connections in the mix.
>
> Changes in v2:
> - Rebased onto bluetooth-next: mainline renamed use_ll_privacy() to
> ll_privacy_capable(). No functional change.
> net/bluetooth/hci_sync.c | 21 +++++++++++++++++----
> 1 file changed, 17 insertions(+), 4 deletions(-)
>
> diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> --- a/net/bluetooth/hci_sync.c
> +++ b/net/bluetooth/hci_sync.c
> @@ -6244,6 +6244,14 @@ static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
> if (err)
> goto failed;
>
> + /* LE Set Random Address is disallowed while advertising is enabled, so
> + * pause it for the address update. hci_pause_addr_resolution() above
> + * only does this when LL privacy is in use.
> + */
> + err = hci_pause_advertising_sync(hdev);
> + if (err)
> + goto failed;
> +
> /* All active scans will be done with either a resolvable private
> * address (when privacy feature has been enabled) or non-resolvable
> * private address.
> @@ -6272,13 +6280,18 @@ static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
> err = hci_start_scan_sync(hdev, LE_SCAN_ACTIVE, interval,
> hdev->le_scan_window_discovery,
> own_addr_type, filter_policy, filter_dup);
> - if (!err)
> + if (!err) {
> + /* Advertising was paused for the address update above. */
> + hci_resume_advertising_sync(hdev);
> return err;
> + }
>
> failed:
> - /* Resume advertising if it was paused */
> - if (ll_privacy_capable(hdev))
> - hci_resume_advertising_sync(hdev);
> + /* Resume advertising if it was paused. hci_resume_advertising_sync()
> + * is a no-op when hdev->advertising_paused is not set, so this covers
> + * both the privacy and the address-update pause.
> + */
> + hci_resume_advertising_sync(hdev);
>
> /* Resume passive scanning */
> hci_update_passive_scan_sync(hdev);
> --
> 2.34.1
>
--
Luiz Augusto von Dentz