From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pj1-f52.google.com (mail-pj1-f52.google.com [209.85.216.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A9AA347278D for ; Fri, 7 Aug 2026 10:15:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.52 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786097749; cv=none; b=hcTlvkyMEnZ8euU5A9HhXcRryz1srMh8uGcQg5Ea/CIWkmg97w3BxNdqlDHUjFcyLI8PCd43Ha7hkQryMs1TZBSWOpbamnXq3YzGYRdYid26OV3vIGSMpecoy7h7V4E79RoU5SxxM+1WxVri1gEs0gKp6GKytbzdUqHBaRs6VaM= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786097749; c=relaxed/simple; bh=ANx/c0Jk601dswbPSYZBwq6fhwU5VWnvdJTjOTY1Sh4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ICojYelDp7C9yh+hWCDIZv54WMfcV1jWtFhuX4eSO0xRN2aMYu5h5YPOywHdszJNmwSBZe8XOysHxnSQZWlhXu7buJHqg4GmEEpuyQ8qA8Qmami+CIOWSGG8YIZZr82C3cnWxCSTtP+/bL2APQxyCBpvLLnz6BX3e5pv6XFFcMI= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xbow.com; spf=pass smtp.mailfrom=xbow.com; dkim=pass (2048-bit key) header.d=xbow.com header.i=@xbow.com header.b=eqfAp9p5; arc=none smtp.client-ip=209.85.216.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=xbow.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=xbow.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=xbow.com header.i=@xbow.com header.b="eqfAp9p5" Received: by mail-pj1-f52.google.com with SMTP id 98e67ed59e1d1-3900e39d935so3032393a91.0 for ; Fri, 07 Aug 2026 03:15:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xbow.com; s=google; t=1786097747; x=1786702547; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=exzP3UOlfEvklmVgKj5tdk2JL1c2GVIFvVBR+QT0kmQ=; b=eqfAp9p5R1m9lBCSnuKzrrDBcveDW51s3tfmNkgMenxwRRdsZs6V2Q8raP4ZRK44Iz asciyuy14dHVIEWo9eIhZS2/BnM61ZHEgjdlcbED6HatA1TTt9yfW3zg7s4BxT+UQ0Yu tDDoyQtFlQuAaUABtRfhslkw/1xpujewwz4B6sBR53O3p3jFCp4SY8oLIEj6kFUc+2AD g5L3VXImM5mCHGtDgI3B4O6Bex5BEqbQhslAUrUFTG6T83S9pofYPCKYHcb0KTiaOO4O MOKSU9rXzat/csJFb61Bkvf0H0GjlpX8kQ/8m/e5bgfBTopfoiACOaGA95t7VpEwTjN5 +U3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786097747; x=1786702547; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=exzP3UOlfEvklmVgKj5tdk2JL1c2GVIFvVBR+QT0kmQ=; b=dm/rib8s6dXZNUDo9RBRWsvZq5a2w5tR/uE+iEB0xPrSV0VN+PjoBalrQ+idgXB4dm R+d4KamH9uBQi2v4s5G1TKH162aIaJDSPC0iCPYhphwS6w9ZHtGKpAvJIs0mS1j8i4ws yfs+CESFhhAKeUv9cOf2008vrOq9lTM2gAp0PFZzszqyHhg2/8ExdYFZ0lbUbG2xUTIR aCYSj/37rdnhCULIur/GIkV+y7uYLgAt6uthwY3QvbzldanwRBpodi9dfEgBvwezLVBS pWO8RYkgWz3ToCzOFpwoE1Poj8aaghY5o8nTdq7cJN4O7M//8+/nX4blDkEFQDo3LwCV C+Gw== X-Forwarded-Encrypted: i=1; AHgh+RqsnxFmJAeIoPEyLEmv8eebfZ2anI7oIipIzgwm3Jy/59gMlNXNVvi5pDbNvcbjvzMO0vy7nQABWJiHy/E=@vger.kernel.org X-Gm-Message-State: AOJu0YyVWJpMDixTiK3WDHE8q5CJhaLMXiRCAN8ZGhoGrfyJw/2G2Xpy hzefU4wWEX22ZcKANqsQidFEDg8gJkbArMAdtQogVulLDtROILlv1Tzkpb4orwvw3C8= X-Gm-Gg: AR+sD12MNSkVmFqVC09TMt7+nWI1IYIgtjHxsPBFm/+E/mraaffyfLUxwcZdIQaOdyO TUABNd9ouJfnJBwThqZHVWIWksy8NZ9VORtKbKozF58DDy7H9U3bw3xImRTgAzFR5R5wMq7qb2M 7AUdpbW32Php7Bvhxjkg/9vtFKzkJwihIfARwRI+2GL3aF63obQVLyJ6TPTOp3n0EF6pabQSsqm uH0RNwPU0wLdiDASaMPd266h7VQwaujs/H3tcfS+HWYOaZuvE7zyeMBCfA8k9MeuvfAmCw2G5hG O1Z2o25jLeT7HzwIYK3nIekzqiErw06xA4S5/aakXWA2bjnnFKOyFsDekfEQXf8D57fDf4xCNXO fQkhCDg4UwV+KAy5tZNfkypIC8sLJEhy1HDkHTwFrFFo/os2GJHWd64aD0iJ/SjnYkn99C+0qmL Vd8lvNFXk8UUgjFpP7eSKZRSmqGVDSP04/cc0T8H7WFJusF07Zn9OQI4OAGiRL05GD0vw/mkpzd 9mLbj9gTO1ASdzIXusEspCVfzIMYeABAdQLsAs1STk= X-Received: by 2002:a17:90a:d644:b0:38e:2860:253f with SMTP id 98e67ed59e1d1-3903c582511mr22513549a91.12.1786097746982; Fri, 07 Aug 2026 03:15:46 -0700 (PDT) Received: from localhost.localdomain ([1.220.41.130]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-39085dea04bsm4399162a91.8.2026.08.07.03.15.43 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Fri, 07 Aug 2026 03:15:46 -0700 (PDT) From: Baul Lee To: Luiz Augusto von Dentz , Marcel Holtmann , Brian Gix Cc: Baul Lee , Luiz Augusto von Dentz , Dmitry Antipov , linux-bluetooth@vger.kernel.org, linux-kernel@vger.kernel.org, federico.kirschbaum@xbow.com, stable@vger.kernel.org Subject: [PATCH v2 3/3] Bluetooth: MGMT: reference-count struct mgmt_mesh_tx Date: Fri, 7 Aug 2026 19:15:29 +0900 Message-ID: <20260807101529.17348-4-baul.lee@xbow.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260807101529.17348-1-baul.lee@xbow.com> References: <20260807101529.17348-1-baul.lee@xbow.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit hci_cmd_sync_submit() stores the caller's pointer in a work entry and takes no reference to what it names. hci_cmd_sync_work() drains that list from the head on an ordered workqueue, so an entry queued earlier runs to completion before a later one is looked at. mesh_send() links a struct mgmt_mesh_tx onto hdev->mesh_pending and queues mesh_send_sync() with the raw pointer. A MESH_SEND_CANCEL issued before that send leaves its send_cancel() entry ahead in the queue, so send_cancel() runs first: it picks the object out of hdev->mesh_pending with mgmt_mesh_next(), which returns it without unlinking it, and frees it through mesh_send_complete(). Nothing invalidates the pointer the later entry still holds, so mesh_send_sync() and its destroy callback mesh_send_start_complete() run on freed memory: [ 43.618774] BUG: KASAN: slab-use-after-free in mesh_send_sync+0xec/0x1b4 [ 43.618851] Write of size 1 at addr ffff000009f2a6a9 by task kworker/u5:2/148 [ 43.619464] mesh_send_sync+0xec/0x1b4 [ 43.619531] hci_cmd_sync_work+0xac/0x128 [ 43.620882] Freed by task 148: [ 43.621171] mgmt_mesh_remove+0x94/0x110 [ 43.621218] send_cancel+0xd8/0x1cc [ 43.621270] hci_cmd_sync_work+0xac/0x128 Offset 41 is mesh_tx->instance, the only byte mesh_send_sync() stores through mesh_tx; it reads further fields of the same freed object, and hci_set_adv_instance_data() copies 31 of those bytes into a live struct adv_info. The free and the use are consecutive iterations of one hci_cmd_sync_work() loop on one kworker, so queue order alone decides it. Give struct mgmt_mesh_tx a reference count. hdev->mesh_pending holds one, and every pointer handed to hci_cmd_sync_queue() takes a second one that the destroy callback drops, so a cancel that unlinks the object while a work entry is still queued no longer releases it. mgmt_mesh_remove() becomes an unlink plus a put and returns early when the object is already unlinked, because it can now outlive its removal from the list. The list reference is still dropped under hci_dev_lock() only, so mesh_send()'s use of mesh_tx->handle after queueing stays covered by the lock it holds. A cancel that arrives before the queued mesh_send_sync() now lets that send run rather than freeing the object under it; suppressing the transmission as well is a separate change. hci_cmd_sync_dequeue() is the other in-tree option, but mgmt_mesh_remove() is not given the hci_dev it needs and cannot tell whether the work entry has already been taken off cmd_sync_work_list. Discovered by XBOW, triaged by Baul Lee Fixes: b338d91703fa ("Bluetooth: Implement support for Mesh") Cc: stable@vger.kernel.org Signed-off-by: Baul Lee --- net/bluetooth/mgmt.c | 25 +++++++++++++++++-------- net/bluetooth/mgmt_util.c | 23 +++++++++++++++++++++-- net/bluetooth/mgmt_util.h | 3 +++ 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 853a80fd15af..61d279ae2f71 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1133,13 +1133,15 @@ static void mesh_next(struct hci_dev *hdev, void *data, int err) return; } - err = hci_cmd_sync_queue(hdev, mesh_send_sync, mesh_tx, + err = hci_cmd_sync_queue(hdev, mesh_send_sync, mgmt_mesh_get(mesh_tx), mesh_send_start_complete); - if (err < 0) + if (err < 0) { + mgmt_mesh_put(mesh_tx); mesh_send_complete(hdev, mesh_tx, false); - else + } else { hci_dev_set_flag(hdev, HCI_MESH_SENDING); + } hci_dev_unlock(hdev); } @@ -2328,7 +2330,7 @@ static void mesh_send_start_complete(struct hci_dev *hdev, void *data, int err) u8 mgmt_err = mgmt_status(err); if (err == -ECANCELED) - return; + goto put; /* Report any errors here, but don't report completion */ @@ -2338,12 +2340,15 @@ static void mesh_send_start_complete(struct hci_dev *hdev, void *data, int err) hci_dev_lock(hdev); mesh_send_complete(hdev, mesh_tx, false); hci_dev_unlock(hdev); - return; + goto put; } mesh_send_interval = msecs_to_jiffies((send->cnt) * 25); queue_delayed_work(hdev->req_workqueue, &hdev->mesh_send_done, mesh_send_interval); + +put: + mgmt_mesh_put(mesh_tx); } static int mesh_send_sync(struct hci_dev *hdev, void *data) @@ -2549,11 +2554,15 @@ static int mesh_send(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) sending = hci_dev_test_flag(hdev, HCI_MESH_SENDING); mesh_tx = mgmt_mesh_add(sk, hdev, send, len); - if (!mesh_tx) + if (!mesh_tx) { err = -ENOMEM; - else if (!sending) - err = hci_cmd_sync_queue(hdev, mesh_send_sync, mesh_tx, + } else if (!sending) { + err = hci_cmd_sync_queue(hdev, mesh_send_sync, + mgmt_mesh_get(mesh_tx), mesh_send_start_complete); + if (err < 0) + mgmt_mesh_put(mesh_tx); + } if (err < 0) { bt_dev_err(hdev, "Send Mesh Failed %d", err); diff --git a/net/bluetooth/mgmt_util.c b/net/bluetooth/mgmt_util.c index a822091f2907..c7543964525a 100644 --- a/net/bluetooth/mgmt_util.c +++ b/net/bluetooth/mgmt_util.c @@ -422,6 +422,7 @@ struct mgmt_mesh_tx *mgmt_mesh_add(struct sock *sk, struct hci_dev *hdev, if (!mesh_tx) return NULL; + refcount_set(&mesh_tx->ref, 1); hdev->mesh_send_ref++; if (!hdev->mesh_send_ref) hdev->mesh_send_ref++; @@ -438,9 +439,27 @@ struct mgmt_mesh_tx *mgmt_mesh_add(struct sock *sk, struct hci_dev *hdev, return mesh_tx; } -void mgmt_mesh_remove(struct mgmt_mesh_tx *mesh_tx) +struct mgmt_mesh_tx *mgmt_mesh_get(struct mgmt_mesh_tx *mesh_tx) +{ + refcount_inc(&mesh_tx->ref); + + return mesh_tx; +} + +void mgmt_mesh_put(struct mgmt_mesh_tx *mesh_tx) { - list_del(&mesh_tx->list); + if (!refcount_dec_and_test(&mesh_tx->ref)) + return; + sock_put(mesh_tx->sk); kfree(mesh_tx); } + +void mgmt_mesh_remove(struct mgmt_mesh_tx *mesh_tx) +{ + if (list_empty(&mesh_tx->list)) + return; + + list_del_init(&mesh_tx->list); + mgmt_mesh_put(mesh_tx); +} diff --git a/net/bluetooth/mgmt_util.h b/net/bluetooth/mgmt_util.h index 20810cf06e81..b38970c1332c 100644 --- a/net/bluetooth/mgmt_util.h +++ b/net/bluetooth/mgmt_util.h @@ -19,6 +19,7 @@ struct mgmt_mesh_tx { struct list_head list; + refcount_t ref; int index; size_t param_len; struct sock *sk; @@ -72,4 +73,6 @@ struct mgmt_mesh_tx *mgmt_mesh_find(struct hci_dev *hdev, u8 handle); struct mgmt_mesh_tx *mgmt_mesh_next(struct hci_dev *hdev, struct sock *sk); struct mgmt_mesh_tx *mgmt_mesh_add(struct sock *sk, struct hci_dev *hdev, void *data, u16 len); +struct mgmt_mesh_tx *mgmt_mesh_get(struct mgmt_mesh_tx *mesh_tx); +void mgmt_mesh_put(struct mgmt_mesh_tx *mesh_tx); void mgmt_mesh_remove(struct mgmt_mesh_tx *mesh_tx); -- 2.50.1 (Apple Git-155)