mirror of
https://github.com/followmsi/android_kernel_google_msm.git
synced 2024-11-06 23:17:41 +00:00
Bluetooth: Properly check L2CAP config option output buffer length
upstream a8149a65c1db9c3980873a32e4a96331b7a61f5b from https://github.com/LineageOS/android_kernel_samsung_msm8974.git 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>
This commit is contained in:
parent
4315ddc3a6
commit
bcd33ae876
3 changed files with 50 additions and 41 deletions
|
@ -670,7 +670,7 @@ void l2cap_cleanup_sockets(void);
|
|||
|
||||
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);
|
||||
|
|
|
@ -926,7 +926,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
|
|||
|
||||
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++;
|
||||
}
|
||||
|
||||
|
@ -2915,12 +2915,15 @@ static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned
|
|||
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;
|
||||
|
||||
|
@ -3304,12 +3307,13 @@ static void l2cap_get_ertm_timeouts(struct l2cap_conf_rfc *rfc,
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -3330,7 +3334,7 @@ int l2cap_build_conf_req(struct sock *sk, void *data)
|
|||
|
||||
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:
|
||||
|
@ -3344,7 +3348,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:
|
||||
|
@ -3361,12 +3365,12 @@ done:
|
|||
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) {
|
||||
|
@ -3374,7 +3378,7 @@ done:
|
|||
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))
|
||||
|
@ -3383,7 +3387,7 @@ done:
|
|||
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;
|
||||
|
||||
|
@ -3398,11 +3402,11 @@ done:
|
|||
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))
|
||||
|
@ -3411,7 +3415,7 @@ done:
|
|||
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;
|
||||
}
|
||||
|
@ -3423,12 +3427,13 @@ done:
|
|||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
|
@ -3449,7 +3454,7 @@ static int l2cap_build_amp_reconf_req(struct sock *sk, void *data)
|
|||
}
|
||||
|
||||
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 */
|
||||
|
@ -3460,7 +3465,7 @@ static int l2cap_build_amp_reconf_req(struct sock *sk, void *data)
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -3470,11 +3475,12 @@ static int l2cap_build_amp_reconf_req(struct sock *sk, void *data)
|
|||
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;
|
||||
|
@ -3597,7 +3603,8 @@ done:
|
|||
return -ECONNREFUSED;
|
||||
|
||||
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
|
||||
sizeof(rfc), (unsigned long) &rfc);
|
||||
sizeof(rfc), (unsigned long) &rfc,
|
||||
endptr - ptr);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3616,7 +3623,7 @@ done:
|
|||
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:
|
||||
|
@ -3634,11 +3641,11 @@ done:
|
|||
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;
|
||||
|
||||
|
@ -3648,7 +3655,7 @@ done:
|
|||
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;
|
||||
|
||||
|
@ -3688,11 +3695,12 @@ done:
|
|||
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;
|
||||
|
@ -3779,13 +3787,13 @@ static int l2cap_parse_amp_move_reconf_req(struct sock *sk, void *data)
|
|||
|
||||
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;
|
||||
|
@ -3798,7 +3806,7 @@ static int l2cap_parse_amp_move_reconf_req(struct sock *sk, void *data)
|
|||
}
|
||||
|
||||
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)
|
||||
|
@ -3817,11 +3825,12 @@ done:
|
|||
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;
|
||||
|
@ -3844,13 +3853,13 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data,
|
|||
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:
|
||||
|
@ -3864,14 +3873,14 @@ static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data,
|
|||
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:
|
||||
|
@ -4242,7 +4251,7 @@ sendresp:
|
|||
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++;
|
||||
}
|
||||
|
||||
|
@ -4293,7 +4302,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
|||
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;
|
||||
|
||||
|
@ -4397,9 +4406,9 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
|
|||
|
||||
/* 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);
|
||||
|
@ -4448,7 +4457,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
|
|||
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++;
|
||||
}
|
||||
|
||||
|
@ -4540,7 +4549,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
|
|||
/* 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;
|
||||
|
@ -5340,7 +5349,7 @@ void l2cap_amp_physical_complete(int result, u8 local_id, u8 remote_id,
|
|||
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 {
|
||||
|
@ -6902,7 +6911,7 @@ static int l2cap_amp_move_reconf(struct sock *sk)
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -7637,7 +7646,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
|||
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++;
|
||||
}
|
||||
|
|
|
@ -1035,7 +1035,7 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms
|
|||
|
||||
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);
|
||||
|
|
Loading…
Reference in a new issue