]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 27 Mar 2019 17:01:15 +0000 (02:01 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 27 Mar 2019 17:01:15 +0000 (02:01 +0900)
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

queue-4.4/bluetooth-check-l2cap-option-sizes-returned-from-l2cap_get_conf_opt.patch [new file with mode: 0644]
queue-4.4/bluetooth-verify-that-l2cap_get_conf_opt-provides-large-enough-buffer.patch [new file with mode: 0644]

diff --git a/queue-4.4/bluetooth-check-l2cap-option-sizes-returned-from-l2cap_get_conf_opt.patch b/queue-4.4/bluetooth-check-l2cap-option-sizes-returned-from-l2cap_get_conf_opt.patch
new file mode 100644 (file)
index 0000000..35bb02e
--- /dev/null
@@ -0,0 +1,196 @@
+From af3d5d1c87664a4f150fcf3534c6567cb19909b0 Mon Sep 17 00:00:00 2001
+From: Marcel Holtmann <marcel@holtmann.org>
+Date: Fri, 18 Jan 2019 12:56:20 +0100
+Subject: Bluetooth: Check L2CAP option sizes returned from l2cap_get_conf_opt
+
+From: Marcel Holtmann <marcel@holtmann.org>
+
+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>
+
+---
+ 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
+@@ -3321,10 +3321,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;
+@@ -3332,26 +3336,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;
+@@ -3361,7 +3369,6 @@ static int l2cap_parse_conf_req(struct l
+               default:
+                       if (hint)
+                               break;
+-
+                       result = L2CAP_CONF_UNKNOWN;
+                       *((u8 *) ptr++) = type;
+                       break;
+@@ -3529,55 +3536,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,
+@@ -3709,10 +3721,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.4/bluetooth-verify-that-l2cap_get_conf_opt-provides-large-enough-buffer.patch b/queue-4.4/bluetooth-verify-that-l2cap_get_conf_opt-provides-large-enough-buffer.patch
new file mode 100644 (file)
index 0000000..f71e54a
--- /dev/null
@@ -0,0 +1,64 @@
+From 7c9cbd0b5e38a1672fcd137894ace3b042dfbf69 Mon Sep 17 00:00:00 2001
+From: Marcel Holtmann <marcel@holtmann.org>
+Date: Fri, 18 Jan 2019 13:43:19 +0100
+Subject: Bluetooth: Verify that l2cap_get_conf_opt provides large enough buffer
+
+From: Marcel Holtmann <marcel@holtmann.org>
+
+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 <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>
+
+---
+ net/bluetooth/l2cap_core.c |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -3315,6 +3315,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;
+@@ -3533,6 +3535,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:
+@@ -3718,6 +3722,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: