mirror of
https://github.com/Divested-Mobile/DivestOS-Build.git
synced 2024-12-26 07:59:30 -05:00
404 lines
12 KiB
Diff
404 lines
12 KiB
Diff
|
From 2e073d2001f354982b777a38d931002a62395de1 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 ef5e849..b868343 100644
|
||
|
--- a/include/net/bluetooth/l2cap.h
|
||
|
+++ b/include/net/bluetooth/l2cap.h
|
||
|
@@ -668,7 +668,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 af0dcba..a4ab0ff 100644
|
||
|
--- a/net/bluetooth/l2cap_core.c
|
||
|
+++ b/net/bluetooth/l2cap_core.c
|
||
|
@@ -910,7 +910,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++;
|
||
|
}
|
||
|
|
||
|
@@ -2892,11 +2892,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;
|
||
|
@@ -3275,12 +3278,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", sk);
|
||
|
|
||
|
@@ -3301,7 +3305,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:
|
||
|
@@ -3316,7 +3320,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:
|
||
|
@@ -3333,12 +3337,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) {
|
||
|
@@ -3346,7 +3350,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))
|
||
|
@@ -3355,7 +3359,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;
|
||
|
|
||
|
@@ -3369,11 +3373,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))
|
||
|
@@ -3382,7 +3386,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;
|
||
|
}
|
||
|
@@ -3394,12 +3398,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);
|
||
|
|
||
|
@@ -3420,7 +3425,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 */
|
||
|
@@ -3431,7 +3436,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;
|
||
|
}
|
||
|
|
||
|
@@ -3441,11 +3446,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;
|
||
|
@@ -3563,7 +3569,8 @@
|
||
|
return -ECONNREFUSED;
|
||
|
|
||
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
|
||
|
- sizeof(rfc), (unsigned long) &rfc);
|
||
|
+ sizeof(rfc), (unsigned long) &rfc,
|
||
|
+ endptr - ptr);
|
||
|
}
|
||
|
|
||
|
|
||
|
@@ -3583,7 +3590,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:
|
||
|
@@ -3601,11 +3608,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;
|
||
|
|
||
|
@@ -3615,7 +3622,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;
|
||
|
|
||
|
@@ -3659,11 +3666,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;
|
||
|
@@ -3750,13 +3758,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;
|
||
|
@@ -3769,7 +3777,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)
|
||
|
@@ -3788,11 +3796,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;
|
||
|
@@ -3815,13 +3824,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:
|
||
|
@@ -3835,7 +3844,7 @@
|
||
|
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:
|
||
|
@@ -3845,7 +3854,7 @@
|
||
|
pi->tx_win = L2CAP_TX_WIN_MAX_ENHANCED;
|
||
|
|
||
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EXT_WINDOW,
|
||
|
- 2, pi->tx_win);
|
||
|
+ 2, pi->tx_win, endptr - ptr);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
@@ -4204,7 +4213,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++;
|
||
|
}
|
||
|
|
||
|
@@ -4255,7 +4264,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;
|
||
|
|
||
|
@@ -4359,9 +4368,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);
|
||
|
@@ -4410,7 +4419,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++;
|
||
|
}
|
||
|
|
||
|
@@ -4506,7 +4515,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;
|
||
|
@@ -5306,7 +5315,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 {
|
||
|
@@ -6870,7 +6879,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;
|
||
|
}
|
||
|
|
||
|
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
|
||
|
index 6e229d1..aa17999 100644
|
||
|
--- a/net/bluetooth/l2cap_sock.c
|
||
|
+++ b/net/bluetooth/l2cap_sock.c
|
||
|
@@ -1031,7 +1031,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);
|