public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Dan Klishch <danilklishch@gmail.com>
To: Marcel Holtmann <marcel@holtmann.org>,
	Luiz Augusto von Dentz <luiz.dentz@gmail.com>
Cc: linux-bluetooth@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [RFC PATCH] Bluetooth: fix Set Public Address on controller in HCI_AUTO_OFF grace period
Date: Thu, 30 Apr 2026 00:55:22 -0400	[thread overview]
Message-ID: <20260430045522.7881-1-danilklishch@gmail.com> (raw)

When mgmt's Set Public Address command (and Set External Configuration)
stages a configuration change, set_public_address() sets HCI_CONFIG and
HCI_AUTO_OFF on the device and queues hci_power_on. The intent is for
the queued power_on to run hci_dev_init_sync() -- which detects
HCI_CONFIG and calls hdev->set_bdaddr() to program the new address into
the firmware -- and then re-emit Index Added with the controller back in
the boot-init grace period (HCI_UP=1, HCI_AUTO_OFF=1) so userspace can
re-push pending config commands and drive a fresh Set Powered 1 cycle.

The bug bites when the command is issued while the controller is in
that same grace period itself -- the normal post-boot state, before
bluetoothd has claimed the controller via Set Powered 1. In that state
HCI_UP=1 and HCI_AUTO_OFF=1, so hdev_is_powered() is false (the command
is accepted) but hci_power_on()'s "already up" early-return condition
HCI_UP && HCI_MGMT && HCI_AUTO_OFF is true. The early-return path runs
hci_powered_update_sync() -- which is the wrong thing for this case:
neither hci_dev_init_sync() nor mgmt_index_added() runs. The result:

  - hdev->public_addr is recorded but never reaches the firmware via
    hdev->set_bdaddr(),
  - userspace sees Index Removed (from set_public_address) but no
    Index Added, leaving the controller invisible to mgmt clients;
    it is not in the configured, unconfigured or extended index list
    yet remains registered in the kernel.

The other two starting states are fine: HCI_UP=0 falls through to the
full hci_dev_do_open() path (correct); HCI_UP=1 with HCI_AUTO_OFF=0
makes hdev_is_powered() true, so set_public_address() rejects with
MGMT_STATUS_REJECTED before queueing power_on at all.

Fix: at the top of hci_power_on(), if HCI_CONFIG is pending and we are
in the affected grace-period state (HCI_UP, HCI_MGMT, !HCI_RFKILLED),
close the device first. The early-return condition then fails and we
fall through to hci_dev_do_open() -> hci_dev_init_sync(), which honors
HCI_CONFIG by invoking hdev->set_bdaddr(); the post-open block then
re-emits Index Added via the existing HCI_CONFIG branch.

hci_dev_close_sync() clears HCI_AUTO_OFF as a side effect of going
through the regular power-down path, but set_public_address() had set
it deliberately so the post-reopen state matches the boot-init grace
period that mgmt clients expect ("treat as new one" per the protocol
contract). Several mgmt commands -- Set Privacy, Set Wideband Speech,
Set LE, Set BR/EDR, etc. -- gate on !hdev_is_powered(), and bluetoothd
expects to push them during this window before issuing Set Powered 1
itself. Restore HCI_AUTO_OFF after the close so hdev_is_powered() ==
false again.

This matches the documented behavior for Set Public Address on a
fully-configured controller ("Once the address has been successfully
changed an Index Added event will be sent.")

Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Dan Klishch <danilklishch@gmail.com>
---
 net/bluetooth/hci_core.c | 8 ++++++++
 1 file changed, 8 insertions(+)

--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -948,6 +948,14 @@ static void hci_power_on(struct work_struct *work)

 	BT_DBG("%s", hdev->name);

+	if (hci_dev_test_flag(hdev, HCI_CONFIG) &&
+	    hci_dev_test_flag(hdev, HCI_MGMT) &&
+	    !hci_dev_test_flag(hdev, HCI_RFKILLED) &&
+	    test_bit(HCI_UP, &hdev->flags)) {
+		hci_dev_do_close(hdev);
+		hci_dev_set_flag(hdev, HCI_AUTO_OFF);
+	}
+
 	if (test_bit(HCI_UP, &hdev->flags) &&
 	    hci_dev_test_flag(hdev, HCI_MGMT) &&
 	    hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {

---

Claude was able to fully convince me that the patch and the explanation
are correct. Moreover, this indeed fixed a problem I had with a custom
patched bluetoothd that happened to override public-addr in the 2
second post-boot window. However, both the patch and commit description
are fully AI-generated.

             reply	other threads:[~2026-04-30  4:55 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-30  4:55 Dan Klishch [this message]
2026-05-04 18:27 ` [RFC PATCH] Bluetooth: fix Set Public Address on controller in HCI_AUTO_OFF grace period Luiz Augusto von Dentz
2026-05-04 19:15   ` Dan Klishch

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=20260430045522.7881-1-danilklishch@gmail.com \
    --to=danilklishch@gmail.com \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luiz.dentz@gmail.com \
    --cc=marcel@holtmann.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