mirror of
https://github.com/Divested-Mobile/DivestOS-Build.git
synced 2024-10-01 01:35:54 -04:00
412 lines
12 KiB
Diff
412 lines
12 KiB
Diff
From a8149a65c1db9c3980873a32e4a96331b7a61f5b Mon Sep 17 00:00:00 2001
|
|
From: Ben Seri <ben@armis.com>
|
|
Date: Sat, 09 Sep 2017 23:15:59 +0200
|
|
Subject: [PATCH] Bluetooth: Properly check L2CAP config option output buffer length
|
|
|
|
Validate the output buffer length for L2CAP config requests and responses
|
|
to avoid overflowing the stack buffer used for building the option blocks.
|
|
|
|
Change-Id: I7a0ff0b9dd0156c0e6383214a9c86e4ec4c0d236
|
|
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>
|
|
CVE-2017-1000251
|
|
Signed-off-by: Kevin F. Haggerty <haggertk@lineageos.org>
|
|
---
|
|
|
|
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
|
|
index 9c2b735..8e281b4 100644
|
|
--- a/include/net/bluetooth/l2cap.h
|
|
+++ b/include/net/bluetooth/l2cap.h
|
|
@@ -670,7 +670,7 @@
|
|
|
|
u8 l2cap_get_ident(struct l2cap_conn *conn);
|
|
void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data);
|
|
-int l2cap_build_conf_req(struct sock *sk, void *data);
|
|
+int l2cap_build_conf_req(struct sock *sk, void *data, size_t data_size);
|
|
int __l2cap_wait_ack(struct sock *sk);
|
|
|
|
struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, size_t len);
|
|
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
|
|
index a554160..aba12f2 100644
|
|
--- a/net/bluetooth/l2cap_core.c
|
|
+++ b/net/bluetooth/l2cap_core.c
|
|
@@ -926,7 +926,7 @@
|
|
|
|
l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
|
|
- l2cap_build_conf_req(sk, buf), buf);
|
|
+ l2cap_build_conf_req(sk, buf, sizeof(buf)), buf);
|
|
l2cap_pi(sk)->num_conf_req++;
|
|
}
|
|
|
|
@@ -2923,11 +2923,14 @@
|
|
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;
|
|
@@ -3312,12 +3315,13 @@
|
|
}
|
|
}
|
|
|
|
-int l2cap_build_conf_req(struct sock *sk, void *data)
|
|
+int l2cap_build_conf_req(struct sock *sk, void *data, size_t data_size)
|
|
{
|
|
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
|
struct l2cap_conf_req *req = data;
|
|
struct l2cap_conf_rfc rfc = { .mode = pi->mode };
|
|
void *ptr = req->data;
|
|
+ void *endptr = data + data_size;
|
|
|
|
BT_DBG("sk %p mode %d", sk, pi->mode);
|
|
|
|
@@ -3338,7 +3342,7 @@
|
|
|
|
done:
|
|
if (pi->imtu != L2CAP_DEFAULT_MTU)
|
|
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
|
|
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu, endptr - ptr);
|
|
|
|
switch (pi->mode) {
|
|
case L2CAP_MODE_BASIC:
|
|
@@ -3352,7 +3356,7 @@
|
|
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:
|
|
@@ -3369,12 +3373,12 @@
|
|
rfc.max_pdu_size = cpu_to_le16(pi->imtu);
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
|
|
- (unsigned long) &rfc);
|
|
+ (unsigned long) &rfc, endptr - ptr);
|
|
|
|
if ((pi->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW) &&
|
|
pi->extended_control) {
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EXT_WINDOW, 2,
|
|
- pi->tx_win);
|
|
+ pi->tx_win, endptr - ptr);
|
|
}
|
|
|
|
if (pi->amp_id) {
|
|
@@ -3382,7 +3386,7 @@
|
|
struct l2cap_conf_ext_fs fs = {1, 1, 0xFFFF,
|
|
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EXT_FS,
|
|
- sizeof(fs), (unsigned long) &fs);
|
|
+ sizeof(fs), (unsigned long) &fs, endptr - ptr);
|
|
}
|
|
|
|
if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS))
|
|
@@ -3391,7 +3395,7 @@
|
|
if (pi->fcs == L2CAP_FCS_NONE ||
|
|
pi->conf_state & L2CAP_CONF_NO_FCS_RECV) {
|
|
pi->fcs = L2CAP_FCS_NONE;
|
|
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs);
|
|
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs, endptr - ptr);
|
|
}
|
|
break;
|
|
|
|
@@ -3406,11 +3410,11 @@
|
|
rfc.max_pdu_size = cpu_to_le16(pi->imtu);
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
|
|
- (unsigned long) &rfc);
|
|
+ (unsigned long) &rfc, endptr - ptr);
|
|
|
|
if ((pi->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW) &&
|
|
pi->extended_control) {
|
|
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_EXT_WINDOW, 2, 0);
|
|
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_EXT_WINDOW, 2, 0, endptr - ptr);
|
|
}
|
|
|
|
if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS))
|
|
@@ -3419,7 +3423,7 @@
|
|
if (pi->fcs == L2CAP_FCS_NONE ||
|
|
pi->conf_state & L2CAP_CONF_NO_FCS_RECV) {
|
|
pi->fcs = L2CAP_FCS_NONE;
|
|
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs);
|
|
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs, endptr - ptr);
|
|
}
|
|
break;
|
|
}
|
|
@@ -3431,12 +3435,13 @@
|
|
}
|
|
|
|
|
|
-static int l2cap_build_amp_reconf_req(struct sock *sk, void *data)
|
|
+static int l2cap_build_amp_reconf_req(struct sock *sk, void *data, size_t data_size)
|
|
{
|
|
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
|
struct l2cap_conf_req *req = data;
|
|
struct l2cap_conf_rfc rfc = { .mode = pi->mode };
|
|
void *ptr = req->data;
|
|
+ void *endptr = data + data_size;
|
|
|
|
BT_DBG("sk %p", sk);
|
|
|
|
@@ -3457,7 +3462,7 @@
|
|
}
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
|
|
- (unsigned long) &rfc);
|
|
+ (unsigned long) &rfc, endptr - ptr);
|
|
|
|
if (pi->conn->feat_mask & L2CAP_FEAT_FCS) {
|
|
/* TODO assign fcs for br/edr based on socket config option */
|
|
@@ -3468,7 +3473,7 @@
|
|
else
|
|
pi->local_conf.fcs = L2CAP_FCS_CRC16;
|
|
|
|
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->local_conf.fcs);
|
|
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->local_conf.fcs, endptr - ptr);
|
|
pi->fcs = pi->local_conf.fcs | pi->remote_conf.fcs;
|
|
}
|
|
|
|
@@ -3478,11 +3483,12 @@
|
|
return ptr - data;
|
|
}
|
|
|
|
-static int l2cap_parse_conf_req(struct sock *sk, void *data)
|
|
+static int l2cap_parse_conf_req(struct sock *sk, void *data, size_t data_size)
|
|
{
|
|
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
|
struct l2cap_conf_rsp *rsp = data;
|
|
void *ptr = rsp->data;
|
|
+ void *endptr = data + data_size;
|
|
void *req = pi->conf_req;
|
|
int len = pi->conf_len;
|
|
int type, hint, olen;
|
|
@@ -3605,7 +3611,8 @@
|
|
return -ECONNREFUSED;
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
|
|
- sizeof(rfc), (unsigned long) &rfc);
|
|
+ sizeof(rfc), (unsigned long) &rfc,
|
|
+ endptr - ptr);
|
|
}
|
|
|
|
|
|
@@ -3624,7 +3631,7 @@
|
|
pi->omtu = mtu;
|
|
pi->conf_state |= L2CAP_CONF_MTU_DONE;
|
|
}
|
|
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
|
|
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu, endptr - ptr);
|
|
|
|
switch (rfc.mode) {
|
|
case L2CAP_MODE_BASIC:
|
|
@@ -3642,11 +3649,11 @@
|
|
pi->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);
|
|
|
|
if (pi->conf_state & L2CAP_CONF_LOCKSTEP)
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EXT_FS,
|
|
- sizeof(fs), (unsigned long) &fs);
|
|
+ sizeof(fs), (unsigned long) &fs, endptr - ptr);
|
|
|
|
break;
|
|
|
|
@@ -3656,7 +3663,7 @@
|
|
pi->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;
|
|
|
|
@@ -3696,11 +3703,12 @@
|
|
return ptr - data;
|
|
}
|
|
|
|
-static int l2cap_parse_amp_move_reconf_req(struct sock *sk, void *data)
|
|
+static int l2cap_parse_amp_move_reconf_req(struct sock *sk, void *data, size_t data_size)
|
|
{
|
|
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
|
struct l2cap_conf_rsp *rsp = data;
|
|
void *ptr = rsp->data;
|
|
+ void *endptr = data + data_size;
|
|
void *req = pi->conf_req;
|
|
int len = pi->conf_len;
|
|
int type, hint, olen;
|
|
@@ -3787,13 +3795,13 @@
|
|
|
|
BT_DBG("mtu %d omtu %d", mtu, pi->omtu);
|
|
|
|
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
|
|
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu, endptr - ptr);
|
|
|
|
/* Don't allow extended transmit window to change. */
|
|
if (tx_win != pi->remote_tx_win) {
|
|
result = L2CAP_CONF_UNACCEPT;
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EXT_WINDOW, 2,
|
|
- pi->remote_tx_win);
|
|
+ pi->remote_tx_win, endptr - ptr);
|
|
}
|
|
|
|
pi->remote_mps = rfc.max_pdu_size;
|
|
@@ -3806,7 +3814,7 @@
|
|
}
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
|
|
- sizeof(rfc), (unsigned long) &rfc);
|
|
+ sizeof(rfc), (unsigned long) &rfc, endptr - ptr);
|
|
}
|
|
|
|
if (result != L2CAP_CONF_SUCCESS)
|
|
@@ -3825,11 +3833,12 @@
|
|
return ptr - data;
|
|
}
|
|
|
|
-static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, u16 *result)
|
|
+static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, size_t size, u16 *result)
|
|
{
|
|
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
|
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;
|
|
@@ -3852,13 +3861,13 @@
|
|
pi->imtu = L2CAP_DEFAULT_MIN_MTU;
|
|
} else
|
|
pi->imtu = val;
|
|
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
|
|
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu, endptr - ptr);
|
|
break;
|
|
|
|
case L2CAP_CONF_FLUSH_TO:
|
|
pi->flush_to = val;
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO,
|
|
- 2, pi->flush_to);
|
|
+ 2, pi->flush_to, endptr - ptr);
|
|
break;
|
|
|
|
case L2CAP_CONF_RFC:
|
|
@@ -3872,14 +3881,14 @@
|
|
pi->fcs = 0;
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
|
|
- sizeof(rfc), (unsigned long) &rfc);
|
|
+ sizeof(rfc), (unsigned long) &rfc, endptr - ptr);
|
|
break;
|
|
|
|
case L2CAP_CONF_EXT_WINDOW:
|
|
pi->ack_win = min_t(u16, val, pi->ack_win);
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EXT_WINDOW,
|
|
- 2, pi->tx_win);
|
|
+ 2, pi->tx_win, endptr - ptr);
|
|
break;
|
|
|
|
default:
|
|
@@ -4262,7 +4271,7 @@
|
|
u8 buf[128];
|
|
l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
|
|
- l2cap_build_conf_req(sk, buf), buf);
|
|
+ l2cap_build_conf_req(sk, buf, sizeof(buf)), buf);
|
|
l2cap_pi(sk)->num_conf_req++;
|
|
}
|
|
|
|
@@ -4320,7 +4329,7 @@
|
|
l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
|
|
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
|
|
- l2cap_build_conf_req(sk, req), req);
|
|
+ l2cap_build_conf_req(sk, req, sizeof(req)), req);
|
|
l2cap_pi(sk)->num_conf_req++;
|
|
break;
|
|
|
|
@@ -4427,9 +4436,9 @@
|
|
|
|
/* Complete config. */
|
|
if (!amp_move_reconf)
|
|
- len = l2cap_parse_conf_req(sk, rspbuf);
|
|
+ len = l2cap_parse_conf_req(sk, rspbuf, sizeof(rspbuf));
|
|
else
|
|
- len = l2cap_parse_amp_move_reconf_req(sk, rspbuf);
|
|
+ len = l2cap_parse_amp_move_reconf_req(sk, rspbuf, sizeof(rspbuf));
|
|
|
|
if (len < 0) {
|
|
l2cap_send_disconn_req(conn, sk, ECONNRESET);
|
|
@@ -4478,7 +4487,7 @@
|
|
u8 buf[64];
|
|
l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
|
|
- l2cap_build_conf_req(sk, buf), buf);
|
|
+ l2cap_build_conf_req(sk, buf, sizeof(buf)), buf);
|
|
l2cap_pi(sk)->num_conf_req++;
|
|
}
|
|
|
|
@@ -4575,7 +4584,7 @@
|
|
/* throw out any old stored conf requests */
|
|
result = L2CAP_CONF_SUCCESS;
|
|
len = l2cap_parse_conf_rsp(sk, rsp->data,
|
|
- len, req, &result);
|
|
+ len, req, sizeof(req), &result);
|
|
if (len < 0) {
|
|
l2cap_send_disconn_req(conn, sk, ECONNRESET);
|
|
goto done;
|
|
@@ -5397,7 +5406,7 @@
|
|
l2cap_send_cmd(pi->conn,
|
|
l2cap_get_ident(pi->conn),
|
|
L2CAP_CONF_REQ,
|
|
- l2cap_build_conf_req(sk, buf), buf);
|
|
+ l2cap_build_conf_req(sk, buf, sizeof(buf)), buf);
|
|
l2cap_pi(sk)->num_conf_req++;
|
|
}
|
|
} else {
|
|
@@ -6959,7 +6968,7 @@
|
|
pi = l2cap_pi(sk);
|
|
|
|
l2cap_send_cmd(pi->conn, l2cap_get_ident(pi->conn), L2CAP_CONF_REQ,
|
|
- l2cap_build_amp_reconf_req(sk, buf), buf);
|
|
+ l2cap_build_amp_reconf_req(sk, buf, sizeof(buf)), buf);
|
|
return err;
|
|
}
|
|
|
|
@@ -7694,7 +7703,7 @@
|
|
l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn),
|
|
L2CAP_CONF_REQ,
|
|
- l2cap_build_conf_req(sk, buf),
|
|
+ l2cap_build_conf_req(sk, buf, sizeof(buf)),
|
|
buf);
|
|
l2cap_pi(sk)->num_conf_req++;
|
|
}
|
|
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
|
|
index 6da494f..baf1af7 100644
|
|
--- a/net/bluetooth/l2cap_sock.c
|
|
+++ b/net/bluetooth/l2cap_sock.c
|
|
@@ -1036,7 +1036,7 @@
|
|
|
|
l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
|
|
- l2cap_build_conf_req(sk, buf), buf);
|
|
+ l2cap_build_conf_req(sk, buf, sizeof(buf)), buf);
|
|
l2cap_pi(sk)->num_conf_req++;
|
|
|
|
release_sock(sk);
|