lkml.org 
[lkml]   [2017]   [Oct]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Date
Subject[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 <ben@armis.com>

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 <ben@armis.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
[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 <ben@decadent.org.uk>
---
--- 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, &chan->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(&ptr, L2CAP_CONF_MTU, 2, chan->imtu);
+ l2cap_add_conf_opt(&ptr, 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(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
- (unsigned long) &rfc);
+ (unsigned long) &rfc, 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(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
- (unsigned long) &rfc);
+ (unsigned long) &rfc, 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, &chan->conf_state)) {
chan->fcs = L2CAP_FCS_NONE;
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
+ l2cap_add_conf_opt(&ptr, 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(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
- (unsigned long) &rfc);
+ (unsigned long) &rfc, 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, &chan->conf_state)) {
chan->fcs = L2CAP_FCS_NONE;
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs,
+ endptr - ptr);
}
break;
}
@@ -1998,10 +2004,11 @@ done:
return ptr - data;
}

-static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
+static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data_size)
{
struct l2cap_conf_rsp *rsp = data;
void *ptr = rsp->data;
+ void *endptr = data + data_size;
void *req = chan->conf_req;
int len = chan->conf_len;
int type, hint, olen;
@@ -2077,8 +2084,8 @@ done:
if (chan->num_conf_rsp == 1)
return -ECONNREFUSED;

- l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
- sizeof(rfc), (unsigned long) &rfc);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+ (unsigned long) &rfc, endptr - ptr);
}


@@ -2092,7 +2099,7 @@ done:
chan->omtu = mtu;
set_bit(CONF_MTU_DONE, &chan->conf_state);
}
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu, endptr - ptr);

switch (rfc.mode) {
case L2CAP_MODE_BASIC:
@@ -2117,7 +2124,7 @@ done:
set_bit(CONF_MODE_DONE, &chan->conf_state);

l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
- sizeof(rfc), (unsigned long) &rfc);
+ sizeof(rfc), (unsigned long) &rfc, endptr - ptr);

break;

@@ -2129,8 +2136,8 @@ done:

set_bit(CONF_MODE_DONE, &chan->conf_state);

- l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
- sizeof(rfc), (unsigned long) &rfc);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+ (unsigned long) &rfc, endptr - ptr);

break;

@@ -2151,10 +2158,12 @@ done:
return ptr - data;
}

-static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, void *data, u16 *result)
+static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
+ void *data, size_t size, u16 *result)
{
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 = { .mode = L2CAP_MODE_BASIC };
@@ -2171,13 +2180,13 @@ static int l2cap_parse_conf_rsp(struct l
chan->imtu = L2CAP_DEFAULT_MIN_MTU;
} else
chan->imtu = val;
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu, endptr - ptr);
break;

case L2CAP_CONF_FLUSH_TO:
chan->flush_to = val;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO,
- 2, chan->flush_to);
+ 2, chan->flush_to, endptr - ptr);
break;

case L2CAP_CONF_RFC:
@@ -2191,7 +2200,7 @@ static int l2cap_parse_conf_rsp(struct l
chan->fcs = 0;

l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
- sizeof(rfc), (unsigned long) &rfc);
+ sizeof(rfc), (unsigned long) &rfc, endptr - ptr);
break;
}
}
@@ -2250,7 +2259,7 @@ void __l2cap_connect_rsp_defer(struct l2
return;

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++;
}

@@ -2459,7 +2468,7 @@ sendresp:
u8 buf[128];
set_bit(CONF_REQ_SENT, &chan->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++;
}

@@ -2509,7 +2518,7 @@ static int l2cap_connect_rsp(struct l2ca
break;

l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(chan, req), req);
+ l2cap_build_conf_req(chan, req, sizeof(req)), req);
chan->num_conf_req++;
break;

@@ -2602,7 +2611,7 @@ static inline int l2cap_config_req(struc
}

/* Complete config. */
- len = l2cap_parse_conf_req(chan, rsp);
+ len = l2cap_parse_conf_req(chan, rsp, sizeof(rsp));
if (len < 0) {
l2cap_send_disconn_req(conn, chan, ECONNRESET);
goto unlock;
@@ -2635,7 +2644,7 @@ static inline int l2cap_config_req(struc
if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) {
u8 buf[64];
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++;
}

@@ -2687,7 +2696,7 @@ static inline int l2cap_config_rsp(struc
/* throw out any old stored conf requests */
result = L2CAP_CONF_SUCCESS;
len = l2cap_parse_conf_rsp(chan, rsp->data, len,
- req, &result);
+ req, sizeof(req), &result);
if (len < 0) {
l2cap_send_disconn_req(conn, chan, ECONNRESET);
goto done;
\
 
 \ /
  Last update: 2017-10-09 15:03    [W:0.263 / U:0.636 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site