Linux bluetooth development
 help / color / mirror / Atom feed
From: Valentin Kindschi <valentin.kindschi@fiveco.ch>
To: <linux-bluetooth@vger.kernel.org>
Cc: <marcel@holtmann.org>, <johan.hedberg@gmail.com>,
	<luiz.dentz@gmail.com>, <linux-kernel@vger.kernel.org>,
	Valentin Kindschi <valentin.kindschi@fiveco.ch>,
	<stable@vger.kernel.org>
Subject: [PATCH v3] Bluetooth: hci_sync: pause advertising for the scan address update
Date: Mon, 31 Aug 2026 11:02:32 +0200	[thread overview]
Message-ID: <20260831090232.1726063-1-valentin.kindschi@fiveco.ch> (raw)

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.

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@vger.kernel.org
Assisted-by: Claude:claude-opus-5 btmon
Signed-off-by: Valentin Kindschi <valentin.kindschi@fiveco.ch>
---
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

             reply	other threads:[~2026-08-31  9:03 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-31  9:02 Valentin Kindschi [this message]
2026-08-31 12:44 ` [v3] Bluetooth: hci_sync: pause advertising for the scan address update bluez.test.bot
2026-09-01 14:51 ` [PATCH v3] " Luiz Augusto von Dentz
2026-09-02 12:19   ` Valentin Kindschi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260831090232.1726063-1-valentin.kindschi@fiveco.ch \
    --to=valentin.kindschi@fiveco.ch \
    --cc=johan.hedberg@gmail.com \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luiz.dentz@gmail.com \
    --cc=marcel@holtmann.org \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox