[PATCH 3.2 65/74] Bluetooth: Properly check L2CAP config option output buffer length
3.2.94-rc1 review patch. If anyone has any objections, please let me know. -- From: Ben Sericommit e860d2c904d1a9f38a24eb44c9f34b8f915a6ea3 upstream. Validate the output buffer length for L2CAP config requests and responses to avoid overflowing the stack buffer used for building the option blocks. Signed-off-by: Ben Seri Signed-off-by: Marcel Holtmann Signed-off-by: Linus Torvalds [bwh: Backported to 3.2: - Drop changes to handling of L2CAP_CONF_EFS, L2CAP_CONF_EWS - Drop changes to l2cap_do_create(), l2cap_security_cfm(), and L2CAP_CONF_PENDING case in l2cap_config_rsp() - In l2cap_config_rsp(), s/buf/req/ - Adjust context] Signed-off-by: Ben Hutchings --- --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -68,7 +68,7 @@ static struct sk_buff *l2cap_build_cmd(s u8 code, u8 ident, u16 dlen, void *data); static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, 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 void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err); @@ -787,7 +787,7 @@ static void l2cap_conn_start(struct l2ca set_bit(CONF_REQ_SENT, >conf_state); 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++; } @@ -1825,12 +1825,15 @@ 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; @@ -1901,11 +1904,12 @@ 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); @@ -1926,7 +1930,7 @@ static int l2cap_build_conf_req(struct l done: if (chan->imtu != L2CAP_DEFAULT_MTU) - l2cap_add_conf_opt(, L2CAP_CONF_MTU, 2, chan->imtu); + l2cap_add_conf_opt(, L2CAP_CONF_MTU, 2, chan->imtu, endptr - ptr); switch (chan->mode) { case L2CAP_MODE_BASIC: @@ -1942,7 +1946,7 @@ done: rfc.max_pdu_size= 0; l2cap_add_conf_opt(, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) ); + (unsigned long) , endptr - ptr); break; case L2CAP_MODE_ERTM: @@ -1956,7 +1960,7 @@ done: rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); l2cap_add_conf_opt(, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) ); + (unsigned long) , endptr - ptr); if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) break; @@ -1964,7 +1968,8 @@ done: if (chan->fcs == L2CAP_FCS_NONE || test_bit(CONF_NO_FCS_RECV, >conf_state)) { chan->fcs = L2CAP_FCS_NONE; - l2cap_add_conf_opt(, L2CAP_CONF_FCS, 1, chan->fcs); + l2cap_add_conf_opt(, L2CAP_CONF_FCS, 1, chan->fcs, + endptr - ptr); } break; @@ -1979,7 +1984,7 @@ done: rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); l2cap_add_conf_opt(, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) ); + (unsigned long) , endptr - ptr); if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) break; @@ -1987,7 +1992,8 @@ done: if (chan->fcs == L2CAP_FCS_NONE || test_bit(CONF_NO_FCS_RECV, >conf_state)) {
[PATCH 3.2 65/74] Bluetooth: Properly check L2CAP config option output buffer length
3.2.94-rc1 review patch. If anyone has any objections, please let me know. -- From: Ben Seri commit e860d2c904d1a9f38a24eb44c9f34b8f915a6ea3 upstream. Validate the output buffer length for L2CAP config requests and responses to avoid overflowing the stack buffer used for building the option blocks. Signed-off-by: Ben Seri Signed-off-by: Marcel Holtmann Signed-off-by: Linus Torvalds [bwh: Backported to 3.2: - Drop changes to handling of L2CAP_CONF_EFS, L2CAP_CONF_EWS - Drop changes to l2cap_do_create(), l2cap_security_cfm(), and L2CAP_CONF_PENDING case in l2cap_config_rsp() - In l2cap_config_rsp(), s/buf/req/ - Adjust context] Signed-off-by: Ben Hutchings --- --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -68,7 +68,7 @@ static struct sk_buff *l2cap_build_cmd(s u8 code, u8 ident, u16 dlen, void *data); static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, 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 void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err); @@ -787,7 +787,7 @@ static void l2cap_conn_start(struct l2ca set_bit(CONF_REQ_SENT, >conf_state); 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++; } @@ -1825,12 +1825,15 @@ 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; @@ -1901,11 +1904,12 @@ 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); @@ -1926,7 +1930,7 @@ static int l2cap_build_conf_req(struct l done: if (chan->imtu != L2CAP_DEFAULT_MTU) - l2cap_add_conf_opt(, L2CAP_CONF_MTU, 2, chan->imtu); + l2cap_add_conf_opt(, L2CAP_CONF_MTU, 2, chan->imtu, endptr - ptr); switch (chan->mode) { case L2CAP_MODE_BASIC: @@ -1942,7 +1946,7 @@ done: rfc.max_pdu_size= 0; l2cap_add_conf_opt(, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) ); + (unsigned long) , endptr - ptr); break; case L2CAP_MODE_ERTM: @@ -1956,7 +1960,7 @@ done: rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); l2cap_add_conf_opt(, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) ); + (unsigned long) , endptr - ptr); if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) break; @@ -1964,7 +1968,8 @@ done: if (chan->fcs == L2CAP_FCS_NONE || test_bit(CONF_NO_FCS_RECV, >conf_state)) { chan->fcs = L2CAP_FCS_NONE; - l2cap_add_conf_opt(, L2CAP_CONF_FCS, 1, chan->fcs); + l2cap_add_conf_opt(, L2CAP_CONF_FCS, 1, chan->fcs, + endptr - ptr); } break; @@ -1979,7 +1984,7 @@ done: rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); l2cap_add_conf_opt(, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) ); + (unsigned long) , endptr - ptr); if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) break; @@ -1987,7 +1992,8 @@ done: if (chan->fcs == L2CAP_FCS_NONE || test_bit(CONF_NO_FCS_RECV, >conf_state)) { chan->fcs = L2CAP_FCS_NONE; - l2cap_add_conf_opt(, L2CAP_CONF_FCS, 1, chan->fcs); +