Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCho, Yu-Chen <acho@suse.com>2017-09-15 17:41:42 +0800
committerJiri Kosina <jkosina@suse.cz>2017-09-15 18:45:15 +0200
commitc791a76c6f45301a7af1a23c2ea046d464126699 (patch)
tree4d318d7f770ca19085961f6260adf28c5a694e33
parent51fea8b8e27a8419410b2ce9f4f8452df2954f8f (diff)
Bluetooth: Properly check L2CAP config option output buffer
length (bsc#1057389 CVE-2017-1000251).
-rw-r--r--patches.fixes/Bluetooth-Properly-check-L2CAP-config-option-output-.patch284
-rw-r--r--series.conf3
2 files changed, 287 insertions, 0 deletions
diff --git a/patches.fixes/Bluetooth-Properly-check-L2CAP-config-option-output-.patch b/patches.fixes/Bluetooth-Properly-check-L2CAP-config-option-output-.patch
new file mode 100644
index 0000000000..56db9ff540
--- /dev/null
+++ b/patches.fixes/Bluetooth-Properly-check-L2CAP-config-option-output-.patch
@@ -0,0 +1,284 @@
+From: Ben Seri <ben@armis.com>
+Date: Sat, 9 Sep 2017 23:15:59 +0200
+Subject: [PATCH] Bluetooth: Properly check L2CAP config option output buffer
+ length
+Patch-mainline: Queued in subsystem maintainer repository
+Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
+Git-commit: e860d2c904d1a9f38a24eb44c9f34b8f915a6ea3
+References: bsc#1057389 CVE-2017-1000251
+
+Validate the output buffer length for L2CAP config requests and responses
+to avoid overflowing the stack buffer used for building the option blocks.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Ben Seri <ben@armis.com>
+Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Acked-by: Cho, Yu-Chen <acho@suse.com>
+---
+ net/bluetooth/l2cap_core.c | 73 ++++++++++++++++++++++++++++-----------------
+ 1 file changed, 46 insertions(+), 27 deletions(-)
+
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -69,7 +69,8 @@ static void l2cap_busy_work(struct work_
+
+ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
+ u8 code, u8 ident, u16 dlen, void *data);
+-static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data);
++static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data,
++ size_t data_size);
+
+ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb);
+
+@@ -644,7 +645,7 @@ static void l2cap_conn_start(struct l2ca
+
+ chan->conf_state |= L2CAP_CONF_REQ_SENT;
+ l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
+- l2cap_build_conf_req(chan, buf), buf);
++ l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
+ chan->num_conf_req++;
+ }
+
+@@ -1602,12 +1603,17 @@ static inline int l2cap_get_conf_opt(voi
+ return len;
+ }
+
+-static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val)
++static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val,
++ size_t size)
+ {
+ struct l2cap_conf_opt *opt = *ptr;
+
+ BT_DBG("type 0x%2.2x len %d val 0x%lx", type, len, val);
+
++ if (size < (L2CAP_CONF_OPT_SIZE + len)) {
++ return;
++ }
++
+ opt->type = type;
+ opt->len = len;
+
+@@ -1680,11 +1686,13 @@ static inline __u8 l2cap_select_mode(__u
+ }
+ }
+
+-static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
++static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data,
++ size_t data_size)
+ {
+ struct l2cap_conf_req *req = data;
+ struct l2cap_conf_rfc rfc = { .mode = chan->mode };
+ void *ptr = req->data;
++ void *endptr = data + data_size;
+
+ BT_DBG("chan %p", chan);
+
+@@ -1705,7 +1713,8 @@ static int l2cap_build_conf_req(struct l
+
+ done:
+ if (chan->imtu != L2CAP_DEFAULT_MTU)
+- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu);
++ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu,
++ endptr - ptr);
+
+ switch (chan->mode) {
+ case L2CAP_MODE_BASIC:
+@@ -1721,7 +1730,7 @@ done:
+ rfc.max_pdu_size = 0;
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+- (unsigned long) &rfc);
++ (unsigned long) &rfc, endptr - ptr);
+ break;
+
+ case L2CAP_MODE_ERTM:
+@@ -1735,7 +1744,7 @@ done:
+ rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+- (unsigned long) &rfc);
++ (unsigned long) &rfc, endptr - ptr);
+
+ if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS))
+ break;
+@@ -1743,7 +1752,8 @@ done:
+ if (chan->fcs == L2CAP_FCS_NONE ||
+ chan->conf_state & L2CAP_CONF_NO_FCS_RECV) {
+ chan->fcs = L2CAP_FCS_NONE;
+- l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
++ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs,
++ endptr - ptr);
+ }
+ break;
+
+@@ -1758,7 +1768,7 @@ done:
+ rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+- (unsigned long) &rfc);
++ (unsigned long) &rfc, endptr - ptr);
+
+ if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS))
+ break;
+@@ -1766,7 +1776,8 @@ done:
+ if (chan->fcs == L2CAP_FCS_NONE ||
+ chan->conf_state & L2CAP_CONF_NO_FCS_RECV) {
+ chan->fcs = L2CAP_FCS_NONE;
+- l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
++ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs,
++ endptr - ptr);
+ }
+ break;
+ }
+@@ -1777,10 +1788,12 @@ done:
+ return ptr - data;
+ }
+
+-static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
++static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data,
++ size_t data_size)
+ {
+ struct l2cap_conf_rsp *rsp = data;
+ void *ptr = rsp->data;
++ void *endptr = data + data_size;
+ void *req = chan->conf_req;
+ int len = chan->conf_len;
+ int type, hint, olen;
+@@ -1856,8 +1869,8 @@ done:
+ if (chan->num_conf_rsp == 1)
+ return -ECONNREFUSED;
+
+- l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
+- sizeof(rfc), (unsigned long) &rfc);
++ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
++ (unsigned long) &rfc, endptr - ptr);
+ }
+
+
+@@ -1871,7 +1884,8 @@ done:
+ chan->omtu = mtu;
+ chan->conf_state |= L2CAP_CONF_MTU_DONE;
+ }
+- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu);
++ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu,
++ endptr - ptr);
+
+ switch (rfc.mode) {
+ case L2CAP_MODE_BASIC:
+@@ -1896,7 +1910,8 @@ done:
+ chan->conf_state |= L2CAP_CONF_MODE_DONE;
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
+- sizeof(rfc), (unsigned long) &rfc);
++ sizeof(rfc), (unsigned long) &rfc,
++ endptr - ptr);
+
+ break;
+
+@@ -1908,8 +1923,8 @@ done:
+
+ chan->conf_state |= L2CAP_CONF_MODE_DONE;
+
+- l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
+- sizeof(rfc), (unsigned long) &rfc);
++ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
++ (unsigned long) &rfc, endptr - ptr);
+
+ break;
+
+@@ -1930,10 +1945,12 @@ done:
+ return ptr - data;
+ }
+
+-static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, void *data, u16 *result)
++static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
++ void *data, size_t size, u16 *result)
+ {
+ struct l2cap_conf_req *req = data;
+ void *ptr = req->data;
++ void *endptr = data + size;
+ int type, olen;
+ unsigned long val;
+ struct l2cap_conf_rfc rfc;
+@@ -1950,13 +1967,14 @@ static int l2cap_parse_conf_rsp(struct l
+ chan->imtu = L2CAP_DEFAULT_MIN_MTU;
+ } else
+ chan->imtu = val;
+- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu);
++ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu,
++ endptr - ptr);
+ break;
+
+ case L2CAP_CONF_FLUSH_TO:
+ chan->flush_to = val;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO,
+- 2, chan->flush_to);
++ 2, chan->flush_to, endptr - ptr);
+ break;
+
+ case L2CAP_CONF_RFC:
+@@ -1970,7 +1988,8 @@ static int l2cap_parse_conf_rsp(struct l
+ chan->fcs = 0;
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
+- sizeof(rfc), (unsigned long) &rfc);
++ sizeof(rfc), (unsigned long) &rfc,
++ endptr - ptr);
+ break;
+ }
+ }
+@@ -2030,7 +2049,7 @@ void __l2cap_connect_rsp_defer(struct l2
+
+ chan->conf_state |= L2CAP_CONF_REQ_SENT;
+ l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
+- l2cap_build_conf_req(chan, buf), buf);
++ l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
+ chan->num_conf_req++;
+ }
+
+@@ -2222,7 +2241,7 @@ sendresp:
+ u8 buf[128];
+ chan->conf_state |= L2CAP_CONF_REQ_SENT;
+ l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
+- l2cap_build_conf_req(chan, buf), buf);
++ l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
+ chan->num_conf_req++;
+ }
+
+@@ -2269,7 +2288,7 @@ static inline int l2cap_connect_rsp(stru
+ chan->conf_state |= L2CAP_CONF_REQ_SENT;
+
+ l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
+- l2cap_build_conf_req(chan, req), req);
++ l2cap_build_conf_req(chan, req, sizeof(req)), req);
+ chan->num_conf_req++;
+ break;
+
+@@ -2358,7 +2377,7 @@ static inline int l2cap_config_req(struc
+ }
+
+ /* Complete config. */
+- len = l2cap_parse_conf_req(chan, rsp);
++ len = l2cap_parse_conf_req(chan, rsp, sizeof(rsp));
+ if (len < 0) {
+ l2cap_send_disconn_req(conn, chan, ECONNRESET);
+ goto unlock;
+@@ -2392,7 +2411,7 @@ static inline int l2cap_config_req(struc
+ u8 buf[64];
+ chan->conf_state |= L2CAP_CONF_REQ_SENT;
+ l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
+- l2cap_build_conf_req(chan, buf), buf);
++ l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
+ chan->num_conf_req++;
+ }
+
+@@ -2439,7 +2458,7 @@ static inline int l2cap_config_rsp(struc
+ /* throw out any old stored conf requests */
+ result = L2CAP_CONF_SUCCESS;
+ len = l2cap_parse_conf_rsp(chan, rsp->data, len,
+- req, &result);
++ req, sizeof(req), &result);
+ if (len < 0) {
+ l2cap_send_disconn_req(conn, chan, ECONNRESET);
+ goto done;
diff --git a/series.conf b/series.conf
index 68813317be..4a70adc6ab 100644
--- a/series.conf
+++ b/series.conf
@@ -18576,6 +18576,9 @@
# bsc#1003925, CVE-2015-8956
patches.fixes/Bluetooth-Fix-potential-NULL-dereference-in-RFCOMM-b.patch
+ # bsc#1057389, CVE-2017-1000251
+ patches.fixes/Bluetooth-Properly-check-L2CAP-config-option-output-.patch
+
# bsc#959190, CVE-2015-8569
patches.fixes/pptp-verify-sockaddr_len-in-pptp_bind-and-pptp_connect.patch
patches.fixes/vmxnet3-fix-netpoll-race-condition.patch