mirror of
https://github.com/LineageOS/android_kernel_samsung_msm8226.git
synced 2026-09-28 00:00:22 +02:00
Bluetooth: Check L2CAP option sizes returned from l2cap_get_conf_opt
commit af3d5d1c87664a4f150fcf3534c6567cb19909b0 upstream. 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 <marcel@holtmann.org> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Change-Id: I202cc0952afb33f928f61284c3ef084a4fd08a71 CVE-2019-3460 [haggertk: Backport to 3.4/msm8974] Signed-off-by: Kevin F. Haggerty <haggertk@lineageos.org>
This commit is contained in:
committed by
Francescodario Cuzzocrea
parent
bc0214666e
commit
5b25db83cd
+42
-23
@@ -3513,10 +3513,14 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data, size_t data_size)
|
||||
|
||||
switch (type) {
|
||||
case L2CAP_CONF_MTU:
|
||||
if (olen != 2)
|
||||
break;
|
||||
mtu = val;
|
||||
break;
|
||||
|
||||
case L2CAP_CONF_FLUSH_TO:
|
||||
if (olen != 2)
|
||||
break;
|
||||
pi->flush_to = val;
|
||||
if (pi->conf_state & L2CAP_CONF_LOCKSTEP)
|
||||
result = L2CAP_CONF_UNACCEPT;
|
||||
@@ -3530,11 +3534,14 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data, size_t data_size)
|
||||
break;
|
||||
|
||||
case L2CAP_CONF_RFC:
|
||||
if (olen == sizeof(rfc))
|
||||
memcpy(&rfc, (void *) val, olen);
|
||||
if (olen != sizeof(rfc))
|
||||
break;
|
||||
memcpy(&rfc, (void *) val, olen);
|
||||
break;
|
||||
|
||||
case L2CAP_CONF_FCS:
|
||||
if (olen != 1)
|
||||
break;
|
||||
if (val == L2CAP_FCS_NONE)
|
||||
pi->conf_state |= L2CAP_CONF_NO_FCS_RECV;
|
||||
pi->remote_conf.fcs = val;
|
||||
@@ -3747,23 +3754,26 @@ static int l2cap_parse_amp_move_reconf_req(struct sock *sk, void *data, size_t d
|
||||
break;
|
||||
|
||||
case L2CAP_CONF_RFC:
|
||||
if (olen == sizeof(rfc))
|
||||
memcpy(&rfc, (void *) val, olen);
|
||||
if (olen != sizeof(rfc))
|
||||
break;
|
||||
memcpy(&rfc, (void *) val, olen);
|
||||
break;
|
||||
|
||||
case L2CAP_CONF_FCS:
|
||||
if (olen != 1)
|
||||
break;
|
||||
pi->remote_conf.fcs = val;
|
||||
break;
|
||||
|
||||
case L2CAP_CONF_EXT_FS:
|
||||
if (olen == sizeof(fs)) {
|
||||
memcpy(&fs, (void *) val, olen);
|
||||
if (fs.type != L2CAP_SERVICE_BEST_EFFORT)
|
||||
result = L2CAP_CONF_FLOW_SPEC_REJECT;
|
||||
else {
|
||||
pi->remote_conf.flush_to =
|
||||
le32_to_cpu(fs.flush_to);
|
||||
}
|
||||
if (olen != sizeof(fs))
|
||||
break;
|
||||
memcpy(&fs, (void *) val, olen);
|
||||
if (fs.type != L2CAP_SERVICE_BEST_EFFORT)
|
||||
result = L2CAP_CONF_FLOW_SPEC_REJECT;
|
||||
else {
|
||||
pi->remote_conf.flush_to =
|
||||
le32_to_cpu(fs.flush_to);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3774,7 +3784,6 @@ static int l2cap_parse_amp_move_reconf_req(struct sock *sk, void *data, size_t d
|
||||
default:
|
||||
if (hint)
|
||||
break;
|
||||
|
||||
result = L2CAP_CONF_UNKNOWN;
|
||||
*((u8 *) ptr++) = type;
|
||||
break;
|
||||
@@ -3860,35 +3869,42 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data,
|
||||
|
||||
switch (type) {
|
||||
case L2CAP_CONF_MTU:
|
||||
if (olen != 2)
|
||||
break;
|
||||
if (val < L2CAP_DEFAULT_MIN_MTU) {
|
||||
*result = L2CAP_CONF_UNACCEPT;
|
||||
pi->imtu = L2CAP_DEFAULT_MIN_MTU;
|
||||
} else
|
||||
pi->imtu = val;
|
||||
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu, endptr - ptr);
|
||||
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu,
|
||||
endptr - ptr);
|
||||
break;
|
||||
|
||||
case L2CAP_CONF_FLUSH_TO:
|
||||
if (olen != 2)
|
||||
break;
|
||||
pi->flush_to = val;
|
||||
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO,
|
||||
2, pi->flush_to, endptr - ptr);
|
||||
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2,
|
||||
pi->flush_to, endptr - ptr);
|
||||
break;
|
||||
|
||||
case L2CAP_CONF_RFC:
|
||||
if (olen == sizeof(rfc))
|
||||
memcpy(&rfc, (void *)val, olen);
|
||||
|
||||
if (olen != sizeof(rfc))
|
||||
break;
|
||||
memcpy(&rfc, (void *)val, olen);
|
||||
if ((pi->conf_state & L2CAP_CONF_STATE2_DEVICE) &&
|
||||
rfc.mode != pi->mode)
|
||||
return -ECONNREFUSED;
|
||||
|
||||
pi->fcs = 0;
|
||||
|
||||
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
|
||||
sizeof(rfc), (unsigned long) &rfc, endptr - ptr);
|
||||
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
|
||||
(unsigned long) &rfc, endptr - ptr);
|
||||
break;
|
||||
|
||||
case L2CAP_CONF_EXT_WINDOW:
|
||||
if (olen != 2)
|
||||
break;
|
||||
pi->ack_win = min_t(u16, val, pi->ack_win);
|
||||
|
||||
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EXT_WINDOW,
|
||||
@@ -3968,10 +3984,13 @@ static void l2cap_conf_rfc_get(struct sock *sk, void *rsp, int len)
|
||||
|
||||
switch (type) {
|
||||
case L2CAP_CONF_RFC:
|
||||
if (olen == sizeof(rfc))
|
||||
memcpy(&rfc, (void *)val, olen);
|
||||
if (olen != sizeof(rfc))
|
||||
break;
|
||||
memcpy(&rfc, (void *)val, olen);
|
||||
break;
|
||||
case L2CAP_CONF_EXT_WINDOW:
|
||||
if (olen != 2)
|
||||
break;
|
||||
txwin_ext = val;
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user