diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 9c2b735c898..8e281b41588 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -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); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a55416043e0..aba12f242cd 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -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++; } @@ -2923,12 +2923,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; @@ -3312,12 +3315,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); @@ -3338,7 +3342,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: @@ -3352,7 +3356,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: @@ -3369,12 +3373,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) { @@ -3382,7 +3386,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)) @@ -3391,7 +3395,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; @@ -3406,11 +3410,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)) @@ -3419,7 +3423,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; } @@ -3431,12 +3435,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); @@ -3457,7 +3462,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 */ @@ -3468,7 +3473,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; } @@ -3478,11 +3483,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; @@ -3605,7 +3611,8 @@ done: 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 @@ 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: @@ -3642,11 +3649,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; @@ -3656,7 +3663,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; @@ -3696,11 +3703,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; @@ -3787,13 +3795,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; @@ -3806,7 +3814,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) @@ -3825,11 +3833,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; @@ -3852,13 +3861,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: @@ -3872,14 +3881,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: @@ -4262,7 +4271,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++; } @@ -4320,7 +4329,7 @@ static inline int l2cap_connect_rsp(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, req), req); + l2cap_build_conf_req(sk, req, sizeof(req)), req); l2cap_pi(sk)->num_conf_req++; break; @@ -4427,9 +4436,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); @@ -4478,7 +4487,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++; } @@ -4575,7 +4584,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, /* 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 @@ 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 { @@ -6959,7 +6968,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; } @@ -7694,7 +7703,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++; } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 6da494fd85a..baf1af7b0c0 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1036,7 +1036,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);