]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
Bluetooth: RFCOMM: validate skb length in MCC handlers
authorSeungJu Cheon <suunj1331@gmail.com>
Mon, 25 May 2026 11:04:43 +0000 (20:04 +0900)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 3 Jun 2026 15:21:03 +0000 (11:21 -0400)
The RFCOMM MCC handlers cast skb->data to protocol-specific structs
without validating skb->len first. A malicious remote device can send
truncated MCC frames and trigger out-of-bounds reads in these handlers.

Fix this by using skb_pull_data() to validate and access the required
data before dereferencing it.

rfcomm_recv_rpn() requires special handling since ETSI TS 07.10 allows
1-byte RPN requests. Handle this by validating only the DLCI byte first,
and validating the full struct only when len > 1.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Suggested-by: Muhammad Bilal <meatuni001@gmail.com>
Signed-off-by: SeungJu Cheon <suunj1331@gmail.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/rfcomm/core.c

index d11bd5337d573ee452804a566bea12daf5f6ece6..364b9381c2dc6b128051d55202b78d670b4addb5 100644 (file)
@@ -1431,10 +1431,15 @@ static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
 
 static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb)
 {
-       struct rfcomm_pn *pn = (void *) skb->data;
+       struct rfcomm_pn *pn;
        struct rfcomm_dlc *d;
-       u8 dlci = pn->dlci;
+       u8 dlci;
+
+       pn = skb_pull_data(skb, sizeof(*pn));
+       if (!pn)
+               return -EILSEQ;
 
+       dlci = pn->dlci;
        BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
 
        if (!dlci)
@@ -1483,8 +1488,8 @@ static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb)
 
 static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb)
 {
-       struct rfcomm_rpn *rpn = (void *) skb->data;
-       u8 dlci = __get_dlci(rpn->dlci);
+       struct rfcomm_rpn *rpn;
+       u8 dlci;
 
        u8 bit_rate  = 0;
        u8 data_bits = 0;
@@ -1495,15 +1500,16 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
        u8 xoff_char = 0;
        u16 rpn_mask = RFCOMM_RPN_PM_ALL;
 
-       BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x",
-               dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl,
-               rpn->xon_char, rpn->xoff_char, rpn->param_mask);
+       if (len == 1) {
+               rpn = skb_pull_data(skb, 1);
+               if (!rpn)
+                       return -EILSEQ;
 
-       if (!cr)
-               return 0;
+               dlci = __get_dlci(rpn->dlci);
+
+               if (!cr)
+                       return 0;
 
-       if (len == 1) {
-               /* This is a request, return default (according to ETSI TS 07.10) settings */
                bit_rate  = RFCOMM_RPN_BR_9600;
                data_bits = RFCOMM_RPN_DATA_8;
                stop_bits = RFCOMM_RPN_STOP_1;
@@ -1514,6 +1520,19 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
                goto rpn_out;
        }
 
+       rpn = skb_pull_data(skb, sizeof(*rpn));
+       if (!rpn)
+               return -EILSEQ;
+
+       dlci = __get_dlci(rpn->dlci);
+
+       BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x",
+              dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl,
+              rpn->xon_char, rpn->xoff_char, rpn->param_mask);
+
+       if (!cr)
+               return 0;
+
        /* Check for sane values, ignore/accept bit_rate, 8 bits, 1 stop bit,
         * no parity, no flow control lines, normal XON/XOFF chars */
 
@@ -1589,9 +1608,14 @@ rpn_out:
 
 static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb)
 {
-       struct rfcomm_rls *rls = (void *) skb->data;
-       u8 dlci = __get_dlci(rls->dlci);
+       struct rfcomm_rls *rls;
+       u8 dlci;
 
+       rls = skb_pull_data(skb, sizeof(*rls));
+       if (!rls)
+               return -EILSEQ;
+
+       dlci = __get_dlci(rls->dlci);
        BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status);
 
        if (!cr)
@@ -1608,10 +1632,15 @@ static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb
 
 static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb)
 {
-       struct rfcomm_msc *msc = (void *) skb->data;
+       struct rfcomm_msc *msc;
        struct rfcomm_dlc *d;
-       u8 dlci = __get_dlci(msc->dlci);
+       u8 dlci;
+
+       msc = skb_pull_data(skb, sizeof(*msc));
+       if (!msc)
+               return -EILSEQ;
 
+       dlci = __get_dlci(msc->dlci);
        BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
 
        d = rfcomm_dlc_get(s, dlci);
@@ -1644,17 +1673,19 @@ static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb
 
 static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb)
 {
-       struct rfcomm_mcc *mcc = (void *) skb->data;
+       struct rfcomm_mcc *mcc;
        u8 type, cr, len;
 
+       mcc = skb_pull_data(skb, sizeof(*mcc));
+       if (!mcc)
+               return -EILSEQ;
+
        cr   = __test_cr(mcc->type);
        type = __get_mcc_type(mcc->type);
        len  = __get_mcc_len(mcc->len);
 
        BT_DBG("%p type 0x%x cr %d", s, type, cr);
 
-       skb_pull(skb, 2);
-
        switch (type) {
        case RFCOMM_PN:
                rfcomm_recv_pn(s, cr, skb);