Re: [PATCH] Bluetooth: RFCOMM: add minimum length check in rfcomm_recv_frame
From: Luiz Augusto von Dentz
Date: Tue May 19 2026 - 09:56:22 EST
Hi Muhammad,
On Tue, May 19, 2026 at 12:20 AM Muhammad Bilal <meatuni001@xxxxxxxxx> wrote:
>
> rfcomm_recv_frame() casts skb->data to struct rfcomm_hdr * and
> immediately dereferences hdr->addr and hdr->ctrl without first
> validating that skb->len is large enough to hold the header. A
> remote device can send a crafted short RFCOMM frame over L2CAP to
> trigger an out-of-bounds read before any session state is checked.
>
> The FCS trimming code that follows compounds the problem:
>
> skb->len--; skb->tail--;
>
> If skb->len is already zero the decrement wraps to UINT_MAX, causing
> skb_tail_pointer() to return a pointer far outside the skb and
> producing a second out-of-bounds read when the FCS byte is consumed.
>
> Add a minimum length check before the header pointer is assigned. A
> well-formed RFCOMM frame requires at least addr(1) + ctrl(1) +
> len(1) + fcs(1) = sizeof(struct rfcomm_hdr) + 1 bytes. This single
> guard prevents both the header out-of-bounds read and the skb->len
> integer underflow.
>
> Note: SeungJu Cheon posted a related patch that adds equivalent
> length checks inside the individual MCC sub-handlers
> (rfcomm_recv_pn, rfcomm_recv_rpn, rfcomm_recv_rls, rfcomm_recv_msc,
> rfcomm_recv_mcc). That fix and this one are complementary and
> independent; neither subsumes the other.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Signed-off-by: Muhammad Bilal <meatuni001@xxxxxxxxx>
> ---
> net/bluetooth/rfcomm/core.c | 11 +++++++++--
> 1 file changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
> index d11bd5337..6b300237c 100644
> --- a/net/bluetooth/rfcomm/core.c
> +++ b/net/bluetooth/rfcomm/core.c
> @@ -1741,7 +1741,7 @@ static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk
> static struct rfcomm_session *rfcomm_recv_frame(struct rfcomm_session *s,
> struct sk_buff *skb)
> {
> - struct rfcomm_hdr *hdr = (void *) skb->data;
> + struct rfcomm_hdr *hdr;
> u8 type, dlci, fcs;
>
> if (!s) {
> @@ -1750,10 +1750,17 @@ static struct rfcomm_session *rfcomm_recv_frame(struct rfcomm_session *s,
> return s;
> }
>
> + /* Minimum valid frame: addr(1) + ctrl(1) + len(1) + fcs(1) */
> + if (skb->len < sizeof(*hdr) + 1) {
> + kfree_skb(skb);
> + return s;
> + }
> +
> + hdr = (void *) skb->data;
Let's replace this with skb_pull_data.
> dlci = __get_dlci(hdr->addr);
> type = __get_type(hdr->ctrl);
>
> - /* Trim FCS */
> + /* Trim FCS - safe: skb->len >= sizeof(*hdr) + 1 >= 1 */
> skb->len--; skb->tail--;
It might be a good idea to check if we can use skb_pull_data here as
well, instead of changing the tail to then use skb_tail_pointer.
> fcs = *(u8 *)skb_tail_pointer(skb);
>
> --
> 2.54.0
>
--
Luiz Augusto von Dentz