From nobody Mon May 25 05:54:18 2026 Received: from mail-qt1-f178.google.com (mail-qt1-f178.google.com [209.85.160.178]) (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 E3A3E175A93 for ; Mon, 18 May 2026 00:28:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779064098; cv=none; b=kSrYolXq1broAs00jPt/Y2fzhMpSmm2hKcv9ctWiJ+p4kabIOdf4NzoAWX49vkmsTVJwwWn4L9CxqB5zAIf3JDq9D5vdv2y7CgbfHJMz7irlYA39h2BiR7CWvG9+kVgdyZwyWTYtOV0v2R5dIKWmm6lbXiAyhX3jCX25AvYsxrU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779064098; c=relaxed/simple; bh=Y0LxEBhsMr4gNxeNyXGpziEzfGFTgI7ItLbjmKjA4nY=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=ROAkdle4ZpfaaS6afM+trw2xKtdJE9tWp4N6Bzm15UfILkpzL6LU5OzmLNMnYek5qWgEZmf6rtXWw70R2q6sflFcoZk2UjW1jGux7wdWFn7udaNEBmI2ti7g3sibJKiyVaVqChaSAgoEiO2ttTc3Gf31SuazoyOgE4hr8plfOKk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=kfd+zD//; arc=none smtp.client-ip=209.85.160.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="kfd+zD//" Received: by mail-qt1-f178.google.com with SMTP id d75a77b69052e-50d876329bbso20243761cf.2 for ; Sun, 17 May 2026 17:28:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779064096; x=1779668896; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=6Dj+Yd73bYH9NPALmuOPqYKQcj6z/TCA/BVWI2XWYRk=; b=kfd+zD//OGbkNygm6MdwT6e5jZpKihCdfd7Q5kDlltCxz0W7w4jHOwRXdToJU4wDPV PB+Vs9LFwNRY958zq+dvQWtGJjtGTB9dfT3vLg6MkQX687CZSlUYBwviol7ES7ScpSs8 ekZbyWHW0ls1f5oY5vcsrDu2cLjVX1mzXAP9te0nQgS72INtiEDXbuAhjb9KcSp3VU4s FfgU3Z6mhoL/+21+FUFHxpIzGUZErv/TyXckFIHyr5zpqt4WlyeU7iP35PfwwXwV1AMN nyYAxHAVXWcIBkDPutTCmOSccG9Jj/LmfS5woipnlXzNxB6YgEeGFJeIRd4ualEt/vqC Gcow== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779064096; x=1779668896; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=6Dj+Yd73bYH9NPALmuOPqYKQcj6z/TCA/BVWI2XWYRk=; b=DkJ4VElQznsMcnea6q4MzE447gjLov0iXJ+QR0hccvNUeXQ7Zusy6/f+44rXKyGpfY rMLL6DWrkBNQH6Yyi38MeL2U1tH4nyuVhjg/5ilp9syRj4Nv09B5EXGtEJh/9sS13KsV mmKXuUxo73Dnajdh0WRUHkfWmR9/qYjh8OK4Y++/tecleE7Ju5XXUxhtndTB3KkNAMx8 6T2Uu+WXu5p0hESOeloUuVv3WbqGNOVSJmPEFqyzEy3JOjtQECT7JvaQrRy7JbUcPQEJ 27A3wWbRhRj+a3tbGkhU2f9C+xho82QSShrgELqxnwuUZ8MPL6qdXAqfbJOsCWvt6nt4 jNqg== X-Forwarded-Encrypted: i=1; AFNElJ9vBbxZRv7kWSZU/NQLWED5GM1cV6NNpKmFn0JyxEzUNnlDQWLYe0/2LPIh9cYDxv0tEpJJNgU+UoLYD6U=@vger.kernel.org X-Gm-Message-State: AOJu0Yy+Ga/xb6+ifAxPuVglMa7oq5butRwyqc69xruVVAR83TNuCteP ZRSkNwI1S92ZXyruB7tQ6QUkn3mBoRmmG+03RpaCCJxQmDhm2J7Od9pO X-Gm-Gg: Acq92OFkTTxLMHecbJtwTTlZQUzgeXQPaRh1bG4eU3fpHqL/p/m37PR3kTog6MAXTvA rw51QMEL1dy068k9DVWa/GKliGScSDhVRx5s4RJjWNtfXmOAw1/AQgG0USp41KOWupaRUsUXCEc rcQbLhB1+1A9S9wB1DP3h0VWh9Q+t2XCHIS3gQT2+0yRg2xtc9gEThUrMTlNITEN3b7iwcaXyIx IlmJDQN69aCpHyQlJdjfyZU3gwD1Mbk+Q4+qqlaI85CrZrA0g8eRpqq7xWSnOWMdF4s2WX5CNm7 A+zVE4MGsIFtYaySKfQB4Zrxc7cnpHPY7A1qzk6yQwARhAo7VY+Weny9A4qSO7LGaehObO6sq1f w6A3b+5+/GImGmqoAVJk7xd5S9ZQP0yRxGW1gNeN5yXiZ78mdqPOIIAlOa+wvWw5ELOUMIWWWcq gZdHWPojV23iI4QDJIl47cJWRvpR/YmQgGCLKe91Rhl6V/wDiygswxnNckgBaH00DlLleHFGVb2 COtMAjqBZHDq7sGr7M36gmpLANhDsYMY3ur6jAz5sw= X-Received: by 2002:a05:622a:54a:b0:50e:490c:7432 with SMTP id d75a77b69052e-5165a2ca13cmr182033201cf.48.1779064095658; Sun, 17 May 2026 17:28:15 -0700 (PDT) Received: from server0.tail6e7dd.ts.net (c-68-48-65-54.hsd1.mi.comcast.net. [68.48.65.54]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8ca3619b03fsm37308476d6.37.2026.05.17.17.28.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 17 May 2026 17:28:14 -0700 (PDT) From: Michael Bommarito To: Marcel Holtmann , Luiz Augusto von Dentz Cc: linux-bluetooth@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH] Bluetooth: L2CAP: rate-limit ECHO_RSP per signaling PDU Date: Sun, 17 May 2026 20:28:00 -0400 Message-ID: <20260518002800.1361430-1-michael.bommarito@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" l2cap_sig_channel() walks every L2CAP signaling command packed in one inbound ACL frame and dispatches each to l2cap_bredr_sig_cmd(). The L2CAP_ECHO_REQ handler unconditionally calls l2cap_send_cmd(conn, ident, L2CAP_ECHO_RSP, cmd_len, data); per request, with no per-PDU cap and no per-connection rate limit anywhere in the path (HCI -> hci_conn -> l2cap_sig_channel -> l2cap_bredr_sig_cmd). A peer that packs N L2CAP_ECHO_REQ commands (4 bytes each) into a single inbound signaling PDU forces the kernel to emit N separate ACL frames carrying L2CAP_ECHO_RSP. Measured between two unmodified upstream kernels over real radio (Intel AX210 attacker, Intel BE200 target): N=3D1 : 13 B in, 13 B out, 1 ACL frame back, 23 ms wall N=3D4 : 25 B in, 52 B out, 4 ACL frames back, 12 ms wall N=3D16 : 73 B in, 208 B out, 16 ACL frames back, 28 ms wall N=3D48 : 201 B in, 624 B out, 48 ACL frames back, 67 ms wall N=3D168 : 681 B in, 2184 B out, 168 ACL frames back, 220 ms wall The 10x latency growth at N=3D168 vs N=3D1 (220ms vs 23ms) is the target's signaling work-queue and radio TX queue saturating on the burst. A sustained flood (50 inbound PDUs of 168 packed echoes each) drives ~840 ECHO_RSPs/sec from the target until the kernel's input ACL queue back-pressures the attacker; during the attack window the target's BT radio is fully occupied answering echoes and any other ongoing BT traffic (audio, HID, file transfer) is starved. bluetoothd CPU stays at 0% throughout -- the entire path is kernel-side. L2CAP signaling channel CID 0x0001 is pre-auth, so no pairing is required to reach the bug. Reproducible from any device with a programmable Bluetooth radio within range of a target running an unpatched kernel with CONFIG_BT_BREDR=3Dy (effectively every Linux distribution that ships Bluetooth). Cap the number of echoes answered per inbound signaling PDU to L2CAP_SIG_ECHO_BURST (4). Subsequent ECHO_REQs in the same PDU are silently dropped. Legitimate clients (l2ping, BlueZ test suite, specification-compliant L2CAP) send one ECHO_REQ per PDU and are unaffected. Worst-case amplification ratio is now 4:1 per PDU, bounded. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Michael Bommarito Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 --- net/bluetooth/l2cap_core.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 77dec104a9c36..1c1179fddb8ba 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5626,11 +5626,25 @@ static inline void l2cap_sig_send_rej(struct l2cap_= conn *conn, u16 ident) l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); } =20 +/* + * Maximum L2CAP_ECHO_REQ commands handled in a single inbound + * signaling PDU. An inbound PDU may legitimately pack multiple + * commands, but a peer that packs many L2CAP_ECHO_REQs into one + * PDU triggers an N:1 outbound ECHO_RSP storm that saturates the + * link's signaling and ACL TX paths. Cap the number of echoes + * answered per inbound PDU to a small burst. Subsequent ECHO_REQs + * in the same PDU are silently dropped; the peer can pace itself + * with one ECHO_REQ per PDU (the l2ping shape and the only legit + * use that has ever been observed in the wild). + */ +#define L2CAP_SIG_ECHO_BURST 4 + static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) { struct hci_conn *hcon =3D conn->hcon; struct l2cap_cmd_hdr *cmd; + unsigned int echo_count =3D 0; int err; =20 l2cap_raw_recv(conn, skb); @@ -5656,6 +5670,13 @@ static inline void l2cap_sig_channel(struct l2cap_co= nn *conn, continue; } =20 + if (cmd->code =3D=3D L2CAP_ECHO_REQ && + ++echo_count > L2CAP_SIG_ECHO_BURST) { + /* Drop excess echoes packed into one PDU. */ + skb_pull(skb, len); + continue; + } + err =3D l2cap_bredr_sig_cmd(conn, cmd, len, skb->data); if (err) { BT_ERR("Wrong link type (%d)", err); --=20 2.53.0