mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
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:
parent
de23fb6225
commit
23882b828c
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user