[PATCH 3.2 65/74] Bluetooth: Properly check L2CAP config option output buffer length

2017-10-09 Thread Ben Hutchings
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)) {

[PATCH 3.2 65/74] Bluetooth: Properly check L2CAP config option output buffer length

2017-10-09 Thread Ben Hutchings
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);
+