Skip to content

Commit d09afa7

Browse files
holtmannjfvogel
authored andcommitted
Bluetooth: Check L2CAP option sizes returned from l2cap_get_conf_opt
When doing option parsing for standard type values of 1, 2 or 4 octets, the value is converted directly into a variable instead of a pointer. To avoid being tricked into being a pointer, check that for these option types that sizes actually match. In L2CAP every option is fixed size and thus it is prudent anyway to ensure that the remote side sends us the right option size along with option paramters. If the option size is not matching the option type, then that option is silently ignored. It is a protocol violation and instead of trying to give the remote attacker any further hints just pretend that option is not present and proceed with the default values. Implementation following the specification and its qualification procedures will always use the correct size and thus not being impacted here. To keep the code readable and consistent accross all options, a few cosmetic changes were also required. Signed-off-by: Marcel Holtmann <[email protected]> Reviewed-by: Greg Kroah-Hartman <[email protected]> Signed-off-by: Johan Hedberg <[email protected]> Orabug: 29526424 CVE: CVE-2019-3459 (cherry picked from commit af3d5d1) Signed-off-by: Dan Duval <[email protected]> Reviewed-by: Jack Vogel <[email protected]>
1 parent cb3989b commit d09afa7

File tree

1 file changed

+46
-31
lines changed

1 file changed

+46
-31
lines changed

net/bluetooth/l2cap_core.c

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3342,37 +3342,45 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
33423342

33433343
switch (type) {
33443344
case L2CAP_CONF_MTU:
3345+
if (olen != 2)
3346+
break;
33453347
mtu = val;
33463348
break;
33473349

33483350
case L2CAP_CONF_FLUSH_TO:
3351+
if (olen != 2)
3352+
break;
33493353
chan->flush_to = val;
33503354
break;
33513355

33523356
case L2CAP_CONF_QOS:
33533357
break;
33543358

33553359
case L2CAP_CONF_RFC:
3356-
if (olen == sizeof(rfc))
3357-
memcpy(&rfc, (void *) val, olen);
3360+
if (olen != sizeof(rfc))
3361+
break;
3362+
memcpy(&rfc, (void *) val, olen);
33583363
break;
33593364

33603365
case L2CAP_CONF_FCS:
3366+
if (olen != 1)
3367+
break;
33613368
if (val == L2CAP_FCS_NONE)
33623369
set_bit(CONF_RECV_NO_FCS, &chan->conf_state);
33633370
break;
33643371

33653372
case L2CAP_CONF_EFS:
3366-
if (olen == sizeof(efs)) {
3367-
remote_efs = 1;
3368-
memcpy(&efs, (void *) val, olen);
3369-
}
3373+
if (olen != sizeof(efs))
3374+
break;
3375+
remote_efs = 1;
3376+
memcpy(&efs, (void *) val, olen);
33703377
break;
33713378

33723379
case L2CAP_CONF_EWS:
3380+
if (olen != 2)
3381+
break;
33733382
if (!(chan->conn->local_fixed_chan & L2CAP_FC_A2MP))
33743383
return -ECONNREFUSED;
3375-
33763384
set_bit(FLAG_EXT_CTRL, &chan->flags);
33773385
set_bit(CONF_EWS_RECV, &chan->conf_state);
33783386
chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
@@ -3382,7 +3390,6 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
33823390
default:
33833391
if (hint)
33843392
break;
3385-
33863393
result = L2CAP_CONF_UNKNOWN;
33873394
*((u8 *) ptr++) = type;
33883395
break;
@@ -3550,55 +3557,60 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
35503557

35513558
switch (type) {
35523559
case L2CAP_CONF_MTU:
3560+
if (olen != 2)
3561+
break;
35533562
if (val < L2CAP_DEFAULT_MIN_MTU) {
35543563
*result = L2CAP_CONF_UNACCEPT;
35553564
chan->imtu = L2CAP_DEFAULT_MIN_MTU;
35563565
} else
35573566
chan->imtu = val;
3558-
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu, endptr - ptr);
3567+
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu,
3568+
endptr - ptr);
35593569
break;
35603570

35613571
case L2CAP_CONF_FLUSH_TO:
3572+
if (olen != 2)
3573+
break;
35623574
chan->flush_to = val;
3563-
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO,
3564-
2, chan->flush_to, endptr - ptr);
3575+
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2,
3576+
chan->flush_to, endptr - ptr);
35653577
break;
35663578

35673579
case L2CAP_CONF_RFC:
3568-
if (olen == sizeof(rfc))
3569-
memcpy(&rfc, (void *)val, olen);
3570-
3580+
if (olen != sizeof(rfc))
3581+
break;
3582+
memcpy(&rfc, (void *)val, olen);
35713583
if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) &&
35723584
rfc.mode != chan->mode)
35733585
return -ECONNREFUSED;
3574-
35753586
chan->fcs = 0;
3576-
3577-
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
3578-
sizeof(rfc), (unsigned long) &rfc, endptr - ptr);
3587+
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
3588+
(unsigned long) &rfc, endptr - ptr);
35793589
break;
35803590

35813591
case L2CAP_CONF_EWS:
3592+
if (olen != 2)
3593+
break;
35823594
chan->ack_win = min_t(u16, val, chan->ack_win);
35833595
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
35843596
chan->tx_win, endptr - ptr);
35853597
break;
35863598

35873599
case L2CAP_CONF_EFS:
3588-
if (olen == sizeof(efs)) {
3589-
memcpy(&efs, (void *)val, olen);
3590-
3591-
if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
3592-
efs.stype != L2CAP_SERV_NOTRAFIC &&
3593-
efs.stype != chan->local_stype)
3594-
return -ECONNREFUSED;
3595-
3596-
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs),
3597-
(unsigned long) &efs, endptr - ptr);
3598-
}
3600+
if (olen != sizeof(efs))
3601+
break;
3602+
memcpy(&efs, (void *)val, olen);
3603+
if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
3604+
efs.stype != L2CAP_SERV_NOTRAFIC &&
3605+
efs.stype != chan->local_stype)
3606+
return -ECONNREFUSED;
3607+
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs),
3608+
(unsigned long) &efs, endptr - ptr);
35993609
break;
36003610

36013611
case L2CAP_CONF_FCS:
3612+
if (olen != 1)
3613+
break;
36023614
if (*result == L2CAP_CONF_PENDING)
36033615
if (val == L2CAP_FCS_NONE)
36043616
set_bit(CONF_RECV_NO_FCS,
@@ -3730,10 +3742,13 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
37303742

37313743
switch (type) {
37323744
case L2CAP_CONF_RFC:
3733-
if (olen == sizeof(rfc))
3734-
memcpy(&rfc, (void *)val, olen);
3745+
if (olen != sizeof(rfc))
3746+
break;
3747+
memcpy(&rfc, (void *)val, olen);
37353748
break;
37363749
case L2CAP_CONF_EWS:
3750+
if (olen != 2)
3751+
break;
37373752
txwin_ext = val;
37383753
break;
37393754
}

0 commit comments

Comments
 (0)