mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +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)
|
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;
|
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);
|
BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
|
||||||
|
|
||||||
if (!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)
|
static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct rfcomm_rpn *rpn = (void *) skb->data;
|
struct rfcomm_rpn *rpn;
|
||||||
u8 dlci = __get_dlci(rpn->dlci);
|
u8 dlci;
|
||||||
|
|
||||||
u8 bit_rate = 0;
|
u8 bit_rate = 0;
|
||||||
u8 data_bits = 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;
|
u8 xoff_char = 0;
|
||||||
u16 rpn_mask = RFCOMM_RPN_PM_ALL;
|
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) {
|
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;
|
bit_rate = RFCOMM_RPN_BR_9600;
|
||||||
data_bits = RFCOMM_RPN_DATA_8;
|
data_bits = RFCOMM_RPN_DATA_8;
|
||||||
stop_bits = RFCOMM_RPN_STOP_1;
|
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;
|
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,
|
/* Check for sane values, ignore/accept bit_rate, 8 bits, 1 stop bit,
|
||||||
* no parity, no flow control lines, normal XON/XOFF chars */
|
* 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)
|
static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct rfcomm_rls *rls = (void *) skb->data;
|
struct rfcomm_rls *rls;
|
||||||
u8 dlci = __get_dlci(rls->dlci);
|
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);
|
BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status);
|
||||||
|
|
||||||
if (!cr)
|
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)
|
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;
|
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);
|
BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
|
||||||
|
|
||||||
d = rfcomm_dlc_get(s, dlci);
|
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)
|
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;
|
u8 type, cr, len;
|
||||||
|
|
||||||
|
mcc = skb_pull_data(skb, sizeof(*mcc));
|
||||||
|
if (!mcc)
|
||||||
|
return -EILSEQ;
|
||||||
|
|
||||||
cr = __test_cr(mcc->type);
|
cr = __test_cr(mcc->type);
|
||||||
type = __get_mcc_type(mcc->type);
|
type = __get_mcc_type(mcc->type);
|
||||||
len = __get_mcc_len(mcc->len);
|
len = __get_mcc_len(mcc->len);
|
||||||
|
|
||||||
BT_DBG("%p type 0x%x cr %d", s, type, cr);
|
BT_DBG("%p type 0x%x cr %d", s, type, cr);
|
||||||
|
|
||||||
skb_pull(skb, 2);
|
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case RFCOMM_PN:
|
case RFCOMM_PN:
|
||||||
rfcomm_recv_pn(s, cr, skb);
|
rfcomm_recv_pn(s, cr, skb);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user