From: Greg Kroah-Hartman Date: Wed, 27 Mar 2019 17:01:15 +0000 (+0900) Subject: 4.9-stable patches X-Git-Tag: v3.18.138~86 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cbc3587907b5e86646e442c5620f08a839a119cb;p=thirdparty%2Fkernel%2Fstable-queue.git 4.9-stable patches added patches: bluetooth-check-l2cap-option-sizes-returned-from-l2cap_get_conf_opt.patch bluetooth-verify-that-l2cap_get_conf_opt-provides-large-enough-buffer.patch --- diff --git a/queue-4.9/bluetooth-check-l2cap-option-sizes-returned-from-l2cap_get_conf_opt.patch b/queue-4.9/bluetooth-check-l2cap-option-sizes-returned-from-l2cap_get_conf_opt.patch new file mode 100644 index 00000000000..4f771463d9a --- /dev/null +++ b/queue-4.9/bluetooth-check-l2cap-option-sizes-returned-from-l2cap_get_conf_opt.patch @@ -0,0 +1,196 @@ +From af3d5d1c87664a4f150fcf3534c6567cb19909b0 Mon Sep 17 00:00:00 2001 +From: Marcel Holtmann +Date: Fri, 18 Jan 2019 12:56:20 +0100 +Subject: Bluetooth: Check L2CAP option sizes returned from l2cap_get_conf_opt + +From: Marcel Holtmann + +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 +Reviewed-by: Greg Kroah-Hartman +Signed-off-by: Johan Hedberg +Signed-off-by: Greg Kroah-Hartman + +--- + net/bluetooth/l2cap_core.c | 77 ++++++++++++++++++++++++++------------------- + 1 file changed, 46 insertions(+), 31 deletions(-) + +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -3332,10 +3332,14 @@ static int l2cap_parse_conf_req(struct l + + switch (type) { + case L2CAP_CONF_MTU: ++ if (olen != 2) ++ break; + mtu = val; + break; + + case L2CAP_CONF_FLUSH_TO: ++ if (olen != 2) ++ break; + chan->flush_to = val; + break; + +@@ -3343,26 +3347,30 @@ static int l2cap_parse_conf_req(struct l + 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) + set_bit(CONF_RECV_NO_FCS, &chan->conf_state); + break; + + case L2CAP_CONF_EFS: +- if (olen == sizeof(efs)) { +- remote_efs = 1; +- memcpy(&efs, (void *) val, olen); +- } ++ if (olen != sizeof(efs)) ++ break; ++ remote_efs = 1; ++ memcpy(&efs, (void *) val, olen); + break; + + case L2CAP_CONF_EWS: ++ if (olen != 2) ++ break; + if (!(chan->conn->local_fixed_chan & L2CAP_FC_A2MP)) + return -ECONNREFUSED; +- + set_bit(FLAG_EXT_CTRL, &chan->flags); + set_bit(CONF_EWS_RECV, &chan->conf_state); + chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; +@@ -3372,7 +3380,6 @@ static int l2cap_parse_conf_req(struct l + default: + if (hint) + break; +- + result = L2CAP_CONF_UNKNOWN; + *((u8 *) ptr++) = type; + break; +@@ -3540,55 +3547,60 @@ static int l2cap_parse_conf_rsp(struct l + + switch (type) { + case L2CAP_CONF_MTU: ++ if (olen != 2) ++ break; + if (val < L2CAP_DEFAULT_MIN_MTU) { + *result = L2CAP_CONF_UNACCEPT; + chan->imtu = L2CAP_DEFAULT_MIN_MTU; + } else + 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; + + case L2CAP_CONF_FLUSH_TO: ++ if (olen != 2) ++ break; + chan->flush_to = val; +- l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, +- 2, chan->flush_to, endptr - ptr); ++ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, ++ chan->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 (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) && + rfc.mode != chan->mode) + return -ECONNREFUSED; +- + chan->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_EWS: ++ if (olen != 2) ++ break; + chan->ack_win = min_t(u16, val, chan->ack_win); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, + chan->tx_win, endptr - ptr); + break; + + case L2CAP_CONF_EFS: +- if (olen == sizeof(efs)) { +- memcpy(&efs, (void *)val, olen); +- +- if (chan->local_stype != L2CAP_SERV_NOTRAFIC && +- efs.stype != L2CAP_SERV_NOTRAFIC && +- efs.stype != chan->local_stype) +- return -ECONNREFUSED; +- +- l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), +- (unsigned long) &efs, endptr - ptr); +- } ++ if (olen != sizeof(efs)) ++ break; ++ memcpy(&efs, (void *)val, olen); ++ if (chan->local_stype != L2CAP_SERV_NOTRAFIC && ++ efs.stype != L2CAP_SERV_NOTRAFIC && ++ efs.stype != chan->local_stype) ++ return -ECONNREFUSED; ++ l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), ++ (unsigned long) &efs, endptr - ptr); + break; + + case L2CAP_CONF_FCS: ++ if (olen != 1) ++ break; + if (*result == L2CAP_CONF_PENDING) + if (val == L2CAP_FCS_NONE) + set_bit(CONF_RECV_NO_FCS, +@@ -3720,10 +3732,13 @@ static void l2cap_conf_rfc_get(struct l2 + + 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_EWS: ++ if (olen != 2) ++ break; + txwin_ext = val; + break; + } diff --git a/queue-4.9/bluetooth-verify-that-l2cap_get_conf_opt-provides-large-enough-buffer.patch b/queue-4.9/bluetooth-verify-that-l2cap_get_conf_opt-provides-large-enough-buffer.patch new file mode 100644 index 00000000000..017299cf992 --- /dev/null +++ b/queue-4.9/bluetooth-verify-that-l2cap_get_conf_opt-provides-large-enough-buffer.patch @@ -0,0 +1,64 @@ +From 7c9cbd0b5e38a1672fcd137894ace3b042dfbf69 Mon Sep 17 00:00:00 2001 +From: Marcel Holtmann +Date: Fri, 18 Jan 2019 13:43:19 +0100 +Subject: Bluetooth: Verify that l2cap_get_conf_opt provides large enough buffer + +From: Marcel Holtmann + +commit 7c9cbd0b5e38a1672fcd137894ace3b042dfbf69 upstream. + +The function l2cap_get_conf_opt will return L2CAP_CONF_OPT_SIZE + opt->len +as length value. The opt->len however is in control over the remote user +and can be used by an attacker to gain access beyond the bounds of the +actual packet. + +To prevent any potential leak of heap memory, it is enough to check that +the resulting len calculation after calling l2cap_get_conf_opt is not +below zero. A well formed packet will always return >= 0 here and will +end with the length value being zero after the last option has been +parsed. In case of malformed packets messing with the opt->len field the +length value will become negative. If that is the case, then just abort +and ignore the option. + +In case an attacker uses a too short opt->len value, then garbage will +be parsed, but that is protected by the unknown option handling and also +the option parameter size checks. + +Signed-off-by: Marcel Holtmann +Reviewed-by: Greg Kroah-Hartman +Signed-off-by: Johan Hedberg +Signed-off-by: Greg Kroah-Hartman + +--- + net/bluetooth/l2cap_core.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -3326,6 +3326,8 @@ static int l2cap_parse_conf_req(struct l + + while (len >= L2CAP_CONF_OPT_SIZE) { + len -= l2cap_get_conf_opt(&req, &type, &olen, &val); ++ if (len < 0) ++ break; + + hint = type & L2CAP_CONF_HINT; + type &= L2CAP_CONF_MASK; +@@ -3544,6 +3546,8 @@ static int l2cap_parse_conf_rsp(struct l + + while (len >= L2CAP_CONF_OPT_SIZE) { + len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); ++ if (len < 0) ++ break; + + switch (type) { + case L2CAP_CONF_MTU: +@@ -3729,6 +3733,8 @@ static void l2cap_conf_rfc_get(struct l2 + + while (len >= L2CAP_CONF_OPT_SIZE) { + len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); ++ if (len < 0) ++ break; + + switch (type) { + case L2CAP_CONF_RFC: