From: Dan Carpenter <dan.carpenter@linaro.org>
To: "Frédéric Danis" <frederic.danis@collabora.com>
Cc: linux-bluetooth@vger.kernel.org
Subject: [bug report] Bluetooth: l2cap: Process valid commands in too long frame
Date: Wed, 16 Apr 2025 13:53:18 +0300 [thread overview]
Message-ID: <Z_-MHr5yIFm8kNym@stanley.mountain> (raw)
Hello Frédéric Danis,
Commit ad5747d4eed1 ("Bluetooth: l2cap: Process valid commands in too
long frame") from Apr 14, 2025 (linux-next), leads to the following
Smatch static checker warning:
net/bluetooth/l2cap_core.c:7613 l2cap_recv_acldata()
error: double free of 'skb' (line 7557)
net/bluetooth/l2cap_core.c
7484 void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
7485 {
7486 struct l2cap_conn *conn;
7487 int len;
7488
7489 /* Lock hdev to access l2cap_data to avoid race with l2cap_conn_del */
7490 hci_dev_lock(hcon->hdev);
7491
7492 conn = hcon->l2cap_data;
7493
7494 if (!conn)
7495 conn = l2cap_conn_add(hcon);
7496
7497 conn = l2cap_conn_hold_unless_zero(conn);
7498
7499 hci_dev_unlock(hcon->hdev);
7500
7501 if (!conn) {
7502 kfree_skb(skb);
7503 return;
7504 }
7505
7506 BT_DBG("conn %p len %u flags 0x%x", conn, skb->len, flags);
7507
7508 mutex_lock(&conn->lock);
7509
7510 switch (flags) {
7511 case ACL_START:
7512 case ACL_START_NO_FLUSH:
7513 case ACL_COMPLETE:
7514 if (conn->rx_skb) {
7515 BT_ERR("Unexpected start frame (len %d)", skb->len);
7516 l2cap_recv_reset(conn);
7517 l2cap_conn_unreliable(conn, ECOMM);
7518 }
7519
7520 /* Start fragment may not contain the L2CAP length so just
7521 * copy the initial byte when that happens and use conn->mtu as
7522 * expected length.
7523 */
7524 if (skb->len < L2CAP_LEN_SIZE) {
7525 l2cap_recv_frag(conn, skb, conn->mtu);
7526 break;
7527 }
7528
7529 len = get_unaligned_le16(skb->data) + L2CAP_HDR_SIZE;
7530
7531 if (len == skb->len) {
7532 /* Complete frame received */
7533 l2cap_recv_frame(conn, skb);
7534 goto unlock;
7535 }
7536
7537 BT_DBG("Start: total len %d, frag len %u", len, skb->len);
7538
7539 if (skb->len > len) {
7540 BT_ERR("Frame is too long (len %u, expected len %d)",
7541 skb->len, len);
7542 /* PTS test cases L2CAP/COS/CED/BI-14-C and BI-15-C
7543 * (Multiple Signaling Command in one PDU, Data
7544 * Truncated, BR/EDR) send a C-frame to the IUT with
7545 * PDU Length set to 8 and Channel ID set to the
7546 * correct signaling channel for the logical link.
7547 * The Information payload contains one L2CAP_ECHO_REQ
7548 * packet with Data Length set to 0 with 0 octets of
7549 * echo data and one invalid command packet due to
7550 * data truncated in PDU but present in HCI packet.
7551 *
7552 * Shorter the socket buffer to the PDU length to
7553 * allow to process valid commands from the PDU before
7554 * setting the socket unreliable.
7555 */
7556 skb->len = len;
7557 l2cap_recv_frame(conn, skb);
^^^
Freed
7558 l2cap_conn_unreliable(conn, ECOMM);
7559 goto drop;
^^^^^^^^^
7560 }
7561
7562 /* Append fragment into frame (with header) */
7563 if (l2cap_recv_frag(conn, skb, len) < 0)
7564 goto drop;
7565
7566 break;
7567
7568 case ACL_CONT:
7569 BT_DBG("Cont: frag len %u (expecting %u)", skb->len, conn->rx_len);
7570
7571 if (!conn->rx_skb) {
7572 BT_ERR("Unexpected continuation frame (len %d)", skb->len);
7573 l2cap_conn_unreliable(conn, ECOMM);
7574 goto drop;
7575 }
7576
7577 /* Complete the L2CAP length if it has not been read */
7578 if (conn->rx_skb->len < L2CAP_LEN_SIZE) {
7579 if (l2cap_recv_len(conn, skb) < 0) {
7580 l2cap_conn_unreliable(conn, ECOMM);
7581 goto drop;
7582 }
7583
7584 /* Header still could not be read just continue */
7585 if (conn->rx_skb->len < L2CAP_LEN_SIZE)
7586 break;
7587 }
7588
7589 if (skb->len > conn->rx_len) {
7590 BT_ERR("Fragment is too long (len %u, expected %u)",
7591 skb->len, conn->rx_len);
7592 l2cap_recv_reset(conn);
7593 l2cap_conn_unreliable(conn, ECOMM);
7594 goto drop;
7595 }
7596
7597 /* Append fragment into frame (with header) */
7598 l2cap_recv_frag(conn, skb, skb->len);
7599
7600 if (!conn->rx_len) {
7601 /* Complete frame received. l2cap_recv_frame
7602 * takes ownership of the skb so set the global
7603 * rx_skb pointer to NULL first.
7604 */
7605 struct sk_buff *rx_skb = conn->rx_skb;
7606 conn->rx_skb = NULL;
7607 l2cap_recv_frame(conn, rx_skb);
7608 }
7609 break;
7610 }
7611
7612 drop:
--> 7613 kfree_skb(skb);
double freed.
7614 unlock:
7615 mutex_unlock(&conn->lock);
7616 l2cap_conn_put(conn);
7617 }
regards,
dan carpenter
reply other threads:[~2025-04-16 10:53 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=Z_-MHr5yIFm8kNym@stanley.mountain \
--to=dan.carpenter@linaro.org \
--cc=frederic.danis@collabora.com \
--cc=linux-bluetooth@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