提交 8282e42e 编写于 作者: M Marcel Holtmann 提交者: Xie XiuQi

Bluetooth: Check L2CAP option sizes returned from l2cap_get_conf_opt

mainline inclusion
from mainline-5.0
commit af3d5d1c87664a4f150fcf3534c6567cb19909b0
category: bugfix
bugzilla: NA
CVE: CVE-2019-3460

-------------------------------------------------

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: NMarcel Holtmann <marcel@holtmann.org>
Reviewed-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: NJohan Hedberg <johan.hedberg@intel.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 a3dbdb59
...@@ -3344,10 +3344,14 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data ...@@ -3344,10 +3344,14 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
switch (type) { switch (type) {
case L2CAP_CONF_MTU: case L2CAP_CONF_MTU:
if (olen != 2)
break;
mtu = val; mtu = val;
break; break;
case L2CAP_CONF_FLUSH_TO: case L2CAP_CONF_FLUSH_TO:
if (olen != 2)
break;
chan->flush_to = val; chan->flush_to = val;
break; break;
...@@ -3355,26 +3359,30 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data ...@@ -3355,26 +3359,30 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
break; break;
case L2CAP_CONF_RFC: case L2CAP_CONF_RFC:
if (olen == sizeof(rfc)) if (olen != sizeof(rfc))
break;
memcpy(&rfc, (void *) val, olen); memcpy(&rfc, (void *) val, olen);
break; break;
case L2CAP_CONF_FCS: case L2CAP_CONF_FCS:
if (olen != 1)
break;
if (val == L2CAP_FCS_NONE) if (val == L2CAP_FCS_NONE)
set_bit(CONF_RECV_NO_FCS, &chan->conf_state); set_bit(CONF_RECV_NO_FCS, &chan->conf_state);
break; break;
case L2CAP_CONF_EFS: case L2CAP_CONF_EFS:
if (olen == sizeof(efs)) { if (olen != sizeof(efs))
break;
remote_efs = 1; remote_efs = 1;
memcpy(&efs, (void *) val, olen); memcpy(&efs, (void *) val, olen);
}
break; break;
case L2CAP_CONF_EWS: case L2CAP_CONF_EWS:
if (olen != 2)
break;
if (!(chan->conn->local_fixed_chan & L2CAP_FC_A2MP)) if (!(chan->conn->local_fixed_chan & L2CAP_FC_A2MP))
return -ECONNREFUSED; return -ECONNREFUSED;
set_bit(FLAG_EXT_CTRL, &chan->flags); set_bit(FLAG_EXT_CTRL, &chan->flags);
set_bit(CONF_EWS_RECV, &chan->conf_state); set_bit(CONF_EWS_RECV, &chan->conf_state);
chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
...@@ -3384,7 +3392,6 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data ...@@ -3384,7 +3392,6 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
default: default:
if (hint) if (hint)
break; break;
result = L2CAP_CONF_UNKNOWN; result = L2CAP_CONF_UNKNOWN;
*((u8 *) ptr++) = type; *((u8 *) ptr++) = type;
break; break;
...@@ -3554,55 +3561,60 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, ...@@ -3554,55 +3561,60 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
switch (type) { switch (type) {
case L2CAP_CONF_MTU: case L2CAP_CONF_MTU:
if (olen != 2)
break;
if (val < L2CAP_DEFAULT_MIN_MTU) { if (val < L2CAP_DEFAULT_MIN_MTU) {
*result = L2CAP_CONF_UNACCEPT; *result = L2CAP_CONF_UNACCEPT;
chan->imtu = L2CAP_DEFAULT_MIN_MTU; chan->imtu = L2CAP_DEFAULT_MIN_MTU;
} else } else
chan->imtu = val; chan->imtu = val;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu, endptr - ptr); l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu,
endptr - ptr);
break; break;
case L2CAP_CONF_FLUSH_TO: case L2CAP_CONF_FLUSH_TO:
if (olen != 2)
break;
chan->flush_to = val; chan->flush_to = val;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2,
2, chan->flush_to, endptr - ptr); chan->flush_to, endptr - ptr);
break; break;
case L2CAP_CONF_RFC: case L2CAP_CONF_RFC:
if (olen == sizeof(rfc)) if (olen != sizeof(rfc))
break;
memcpy(&rfc, (void *)val, olen); memcpy(&rfc, (void *)val, olen);
if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) && if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) &&
rfc.mode != chan->mode) rfc.mode != chan->mode)
return -ECONNREFUSED; return -ECONNREFUSED;
chan->fcs = 0; chan->fcs = 0;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, (unsigned long) &rfc, endptr - ptr);
sizeof(rfc), (unsigned long) &rfc, endptr - ptr);
break; break;
case L2CAP_CONF_EWS: case L2CAP_CONF_EWS:
if (olen != 2)
break;
chan->ack_win = min_t(u16, val, chan->ack_win); chan->ack_win = min_t(u16, val, chan->ack_win);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
chan->tx_win, endptr - ptr); chan->tx_win, endptr - ptr);
break; break;
case L2CAP_CONF_EFS: case L2CAP_CONF_EFS:
if (olen == sizeof(efs)) { if (olen != sizeof(efs))
break;
memcpy(&efs, (void *)val, olen); memcpy(&efs, (void *)val, olen);
if (chan->local_stype != L2CAP_SERV_NOTRAFIC && if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
efs.stype != L2CAP_SERV_NOTRAFIC && efs.stype != L2CAP_SERV_NOTRAFIC &&
efs.stype != chan->local_stype) efs.stype != chan->local_stype)
return -ECONNREFUSED; return -ECONNREFUSED;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs),
(unsigned long) &efs, endptr - ptr); (unsigned long) &efs, endptr - ptr);
}
break; break;
case L2CAP_CONF_FCS: case L2CAP_CONF_FCS:
if (olen != 1)
break;
if (*result == L2CAP_CONF_PENDING) if (*result == L2CAP_CONF_PENDING)
if (val == L2CAP_FCS_NONE) if (val == L2CAP_FCS_NONE)
set_bit(CONF_RECV_NO_FCS, set_bit(CONF_RECV_NO_FCS,
...@@ -3736,10 +3748,13 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) ...@@ -3736,10 +3748,13 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
switch (type) { switch (type) {
case L2CAP_CONF_RFC: case L2CAP_CONF_RFC:
if (olen == sizeof(rfc)) if (olen != sizeof(rfc))
break;
memcpy(&rfc, (void *)val, olen); memcpy(&rfc, (void *)val, olen);
break; break;
case L2CAP_CONF_EWS: case L2CAP_CONF_EWS:
if (olen != 2)
break;
txwin_ext = val; txwin_ext = val;
break; break;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册