Bluetooth: RFCOMM: validate skb length in MCC handlers

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: 1da177e4c3 ("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>
This commit is contained in:
SeungJu Cheon 2026-05-25 20:04:43 +09:00 committed by Luiz Augusto von Dentz
parent de23fb6225
commit 23882b828c

View 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 (!cr)
return 0;
if (len == 1) {
/* This is a request, return default (according to ETSI TS 07.10) settings */
rpn = skb_pull_data(skb, 1);
if (!rpn)
return -EILSEQ;
dlci = __get_dlci(rpn->dlci);
if (!cr)
return 0;
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 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
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);