| 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 |
| @@ -3336,6 +3336,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; |
| @@ -3554,6 +3556,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: |
| @@ -3739,6 +3743,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: |