]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
Bluetooth: mgmt: fix pending command UAF in EIR updates
authorZihan Xi <zihanx@nebusec.ai>
Thu, 23 Jul 2026 16:43:46 +0000 (00:43 +0800)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Tue, 28 Jul 2026 18:52:31 +0000 (14:52 -0400)
MGMT_OP_SET_LOCAL_NAME is handled asynchronously on powered controllers
and can run set_name_sync().  When the controller is BR/EDR capable,
set_name_sync() updates the local name and then rebuilds EIR data through
eir_create().  The EIR builder walks hdev->uuids, but the UUID list can
be changed and entries can be freed by MGMT_OP_ADD_UUID and
MGMT_OP_REMOVE_UUID.

pending_eir_or_class() is meant to serialize management commands that
can change EIR or the class of device, but it did not include
MGMT_OP_SET_LOCAL_NAME.  In addition, it walked hdev->mgmt_pending
without hdev->mgmt_pending_lock even though pending commands are added
and removed under that mutex.  A racing command completion can therefore
remove and free a pending command while pending_eir_or_class() is still
inspecting it, leading to a use-after-free in the pending-command list or
allowing a local name update to rebuild EIR while UUID entries are being
removed.

Take hdev->mgmt_pending_lock while scanning hdev->mgmt_pending and treat
MGMT_OP_SET_LOCAL_NAME as an EIR/class-affecting pending command on the
powered asynchronous path.  Check for a conflicting pending command before
copying the new short name so a rejected SET_LOCAL_NAME request does not
modify hdev->short_name.

Fixes: 6fe26f694c82 ("Bluetooth: MGMT: Protect mgmt_pending list with its own lock")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zihan Xi <zihanx@nebusec.ai>
Signed-off-by: Ren Wei <enjou1224z@gmail.com>
Reported-by: Vega <vega@nebusec.ai>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/mgmt.c

index 4fd37ac799863bc387085c9805bbd468cdee5fa7..167d75e345266dc6a1f304f04cd519c9291ac04c 100644 (file)
@@ -2696,18 +2696,28 @@ static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev,
 static bool pending_eir_or_class(struct hci_dev *hdev)
 {
        struct mgmt_pending_cmd *cmd;
+       bool pending = false;
+
+       mutex_lock(&hdev->mgmt_pending_lock);
 
        list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
                switch (cmd->opcode) {
                case MGMT_OP_ADD_UUID:
                case MGMT_OP_REMOVE_UUID:
                case MGMT_OP_SET_DEV_CLASS:
+               case MGMT_OP_SET_LOCAL_NAME:
                case MGMT_OP_SET_POWERED:
-                       return true;
+                       pending = true;
+                       break;
                }
+
+               if (pending)
+                       break;
        }
 
-       return false;
+       mutex_unlock(&hdev->mgmt_pending_lock);
+
+       return pending;
 }
 
 static const u8 bluetooth_base_uuid[] = {
@@ -4071,6 +4081,12 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
                goto failed;
        }
 
+       if (hdev_is_powered(hdev) && pending_eir_or_class(hdev)) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
+                                     MGMT_STATUS_BUSY);
+               goto failed;
+       }
+
        memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name));
 
        if (!hdev_is_powered(hdev)) {