Bluetooth: Verify that l2cap_get_conf_opt provides large enough buffer
[pandora-kernel.git] / net / bluetooth / l2cap_core.c
index b3bdb48..74f57d8 100644 (file)
@@ -68,7 +68,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
                                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);
 
@@ -251,7 +251,7 @@ static void l2cap_chan_timeout(unsigned long arg)
 
        if (sock_owned_by_user(sk)) {
                /* sk is owned by user. Try again later */
-               __set_chan_timer(chan, HZ / 5);
+               __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
                bh_unlock_sock(sk);
                chan_put(chan);
                return;
@@ -787,7 +787,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
 
                        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++;
                }
 
@@ -862,6 +862,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
        write_lock_bh(&conn->chan_lock);
 
        hci_conn_hold(conn->hcon);
+       conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
 
        bacpy(&bt_sk(sk)->src, conn->src);
        bacpy(&bt_sk(sk)->dst, conn->dst);
@@ -901,12 +902,16 @@ static void l2cap_chan_ready(struct sock *sk)
 static void l2cap_conn_ready(struct l2cap_conn *conn)
 {
        struct l2cap_chan *chan;
+       struct hci_conn *hcon = conn->hcon;
 
        BT_DBG("conn %p", conn);
 
-       if (!conn->hcon->out && conn->hcon->type == LE_LINK)
+       if (!hcon->out && hcon->type == LE_LINK)
                l2cap_le_conn_ready(conn);
 
+       if (hcon->out && hcon->type == LE_LINK)
+               smp_conn_security(hcon, hcon->pending_sec_level);
+
        read_lock(&conn->chan_lock);
 
        list_for_each_entry(chan, &conn->chan_l, list) {
@@ -914,8 +919,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
 
                bh_lock_sock(sk);
 
-               if (conn->hcon->type == LE_LINK) {
-                       if (smp_conn_security(conn, chan->sec_level))
+               if (hcon->type == LE_LINK) {
+                       if (smp_conn_security(hcon, chan->sec_level))
                                l2cap_chan_ready(sk);
 
                } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
@@ -986,8 +991,10 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
        if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
                del_timer_sync(&conn->info_timer);
 
-       if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+       if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) {
                del_timer(&conn->security_timer);
+               smp_chan_destroy(conn);
+       }
 
        hcon->l2cap_data = NULL;
        kfree(conn);
@@ -1240,7 +1247,7 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
                __clear_retrans_timer(chan);
 }
 
-void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
+static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
 {
        struct hci_conn *hcon = chan->conn->hcon;
        u16 flags;
@@ -1256,7 +1263,7 @@ void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
        hci_send_acl(hcon, skb, flags);
 }
 
-void l2cap_streaming_send(struct l2cap_chan *chan)
+static void l2cap_streaming_send(struct l2cap_chan *chan)
 {
        struct sk_buff *skb;
        u16 control, fcs;
@@ -1322,7 +1329,7 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
        l2cap_do_send(chan, tx_skb);
 }
 
-int l2cap_ertm_send(struct l2cap_chan *chan)
+static int l2cap_ertm_send(struct l2cap_chan *chan)
 {
        struct sk_buff *skb, *tx_skb;
        u16 control, fcs;
@@ -1460,7 +1467,7 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in
        return sent;
 }
 
-struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
+static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
 {
        struct sock *sk = chan->sk;
        struct l2cap_conn *conn = chan->conn;
@@ -1490,7 +1497,7 @@ struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr
        return skb;
 }
 
-struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
+static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
 {
        struct sock *sk = chan->sk;
        struct l2cap_conn *conn = chan->conn;
@@ -1519,7 +1526,9 @@ struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *m
        return skb;
 }
 
-struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u16 control, u16 sdulen)
+static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
+                                               struct msghdr *msg, size_t len,
+                                               u16 control, u16 sdulen)
 {
        struct sock *sk = chan->sk;
        struct l2cap_conn *conn = chan->conn;
@@ -1565,7 +1574,7 @@ struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *
        return skb;
 }
 
-int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
+static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
 {
        struct sk_buff *skb;
        struct sk_buff_head sar_queue;
@@ -1728,6 +1737,9 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
        BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d",
                        conn, code, ident, dlen);
 
+       if (conn->mtu < L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE)
+               return NULL;
+
        len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen;
        count = min_t(unsigned int, conn->mtu, len);
 
@@ -1813,12 +1825,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;
 
@@ -1889,11 +1904,12 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
        }
 }
 
-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);
 
@@ -1914,7 +1930,7 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
 
 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:
@@ -1930,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:
@@ -1944,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;
@@ -1952,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;
 
@@ -1967,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;
@@ -1975,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;
        }
@@ -1986,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;
@@ -2002,6 +2021,8 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
 
        while (len >= L2CAP_CONF_OPT_SIZE) {
                len -= l2cap_get_conf_opt(&req, &type, &olen, &val);
+               if (len < 0)
+                       break;
 
                hint  = type & L2CAP_CONF_HINT;
                type &= L2CAP_CONF_MASK;
@@ -2065,8 +2086,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);
        }
 
 
@@ -2080,7 +2101,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:
@@ -2105,7 +2126,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;
 
@@ -2117,8 +2138,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;
 
@@ -2139,18 +2160,22 @@ 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;
+       struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
 
        BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data);
 
        while (len >= L2CAP_CONF_OPT_SIZE) {
                len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
+               if (len < 0)
+                       break;
 
                switch (type) {
                case L2CAP_CONF_MTU:
@@ -2159,13 +2184,13 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
                                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:
@@ -2179,7 +2204,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
                        chan->fcs = 0;
 
                        l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
-                                       sizeof(rfc), (unsigned long) &rfc);
+                                          sizeof(rfc), (unsigned long) &rfc, endptr - ptr);
                        break;
                }
        }
@@ -2238,7 +2263,7 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan)
                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++;
 }
 
@@ -2255,15 +2280,29 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
 
        while (len >= L2CAP_CONF_OPT_SIZE) {
                len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
+               if (len < 0)
+                       break;
 
-               switch (type) {
-               case L2CAP_CONF_RFC:
-                       if (olen == sizeof(rfc))
-                               memcpy(&rfc, (void *)val, olen);
-                       goto done;
-               }
+               if (type != L2CAP_CONF_RFC)
+                       continue;
+
+               if (olen != sizeof(rfc))
+                       break;
+
+               memcpy(&rfc, (void *)val, olen);
+               goto done;
        }
 
+       /* Use sane default values in case a misbehaving remote device
+        * did not send an RFC option.
+        */
+       rfc.mode = chan->mode;
+       rfc.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
+       rfc.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
+       rfc.max_pdu_size = cpu_to_le16(chan->imtu);
+
+       BT_ERR("Expected RFC option was not found, using defaults");
+
 done:
        switch (rfc.mode) {
        case L2CAP_MODE_ERTM:
@@ -2276,10 +2315,15 @@ done:
        }
 }
 
-static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_command_rej(struct l2cap_conn *conn,
+                                   struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+                                   u8 *data)
 {
        struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data;
 
+       if (cmd_len < sizeof(*rej))
+               return -EPROTO;
+
        if (rej->reason != L2CAP_REJ_NOT_UNDERSTOOD)
                return 0;
 
@@ -2296,7 +2340,8 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
        return 0;
 }
 
-static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static int l2cap_connect_req(struct l2cap_conn *conn,
+                            struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data)
 {
        struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
        struct l2cap_conn_rsp rsp;
@@ -2304,8 +2349,14 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
        struct sock *parent, *sk = NULL;
        int result, status = L2CAP_CS_NO_INFO;
 
-       u16 dcid = 0, scid = __le16_to_cpu(req->scid);
-       __le16 psm = req->psm;
+       u16 dcid = 0, scid;
+       __le16 psm;
+
+       if (cmd_len < sizeof(struct l2cap_conn_req))
+               return -EPROTO;
+
+       scid = __le16_to_cpu(req->scid);
+       psm = req->psm;
 
        BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
 
@@ -2423,14 +2474,16 @@ 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++;
        }
 
        return 0;
 }
 
-static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static int l2cap_connect_rsp(struct l2cap_conn *conn,
+                             struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+                             u8 *data)
 {
        struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;
        u16 scid, dcid, result, status;
@@ -2438,6 +2491,9 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
        struct sock *sk;
        u8 req[128];
 
+       if (cmd_len < sizeof(*rsp))
+               return -EPROTO;
+
        scid   = __le16_to_cpu(rsp->scid);
        dcid   = __le16_to_cpu(rsp->dcid);
        result = __le16_to_cpu(rsp->result);
@@ -2468,7 +2524,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
                        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;
 
@@ -2481,7 +2537,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
                if (sock_owned_by_user(sk)) {
                        l2cap_state_change(chan, BT_DISCONN);
                        __clear_chan_timer(chan);
-                       __set_chan_timer(chan, HZ / 5);
+                       __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
                        break;
                }
 
@@ -2513,6 +2569,9 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
        struct sock *sk;
        int len;
 
+       if (cmd_len < sizeof(*req))
+               return -EPROTO;
+
        dcid  = __le16_to_cpu(req->dcid);
        flags = __le16_to_cpu(req->flags);
 
@@ -2538,7 +2597,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
 
        /* Reject if config buffer is too small. */
        len = cmd_len - sizeof(*req);
-       if (len < 0 || chan->conf_len + len > sizeof(chan->conf_req)) {
+       if (chan->conf_len + len > sizeof(chan->conf_req)) {
                l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
                                l2cap_build_conf_rsp(chan, rsp,
                                        L2CAP_CONF_REJECT, flags), rsp);
@@ -2558,7 +2617,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
        }
 
        /* 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;
@@ -2591,7 +2650,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
        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++;
        }
 
@@ -2600,13 +2659,18 @@ unlock:
        return 0;
 }
 
-static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_config_rsp(struct l2cap_conn *conn,
+                                  struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+                                  u8 *data)
 {
        struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data;
        u16 scid, flags, result;
        struct l2cap_chan *chan;
        struct sock *sk;
-       int len = cmd->len - sizeof(*rsp);
+       int len = cmd_len - sizeof(*rsp);
+
+       if (cmd_len < sizeof(*rsp))
+               return -EPROTO;
 
        scid   = __le16_to_cpu(rsp->scid);
        flags  = __le16_to_cpu(rsp->flags);
@@ -2638,7 +2702,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(chan, rsp->data, len,
-                                                               req, &result);
+                                                  req, sizeof(req), &result);
                        if (len < 0) {
                                l2cap_send_disconn_req(conn, chan, ECONNRESET);
                                goto done;
@@ -2654,7 +2718,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
 
        default:
                sk->sk_err = ECONNRESET;
-               __set_chan_timer(chan, HZ * 5);
+               __set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT);
                l2cap_send_disconn_req(conn, chan, ECONNRESET);
                goto done;
        }
@@ -2682,7 +2746,9 @@ done:
        return 0;
 }
 
-static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
+                                      struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+                                      u8 *data)
 {
        struct l2cap_disconn_req *req = (struct l2cap_disconn_req *) data;
        struct l2cap_disconn_rsp rsp;
@@ -2690,6 +2756,9 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
        struct l2cap_chan *chan;
        struct sock *sk;
 
+       if (cmd_len != sizeof(*req))
+               return -EPROTO;
+
        scid = __le16_to_cpu(req->scid);
        dcid = __le16_to_cpu(req->dcid);
 
@@ -2711,7 +2780,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
        if (sock_owned_by_user(sk)) {
                l2cap_state_change(chan, BT_DISCONN);
                __clear_chan_timer(chan);
-               __set_chan_timer(chan, HZ / 5);
+               __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
                bh_unlock_sock(sk);
                return 0;
        }
@@ -2723,13 +2792,18 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
        return 0;
 }
 
-static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
+                                      struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+                                      u8 *data)
 {
        struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data;
        u16 dcid, scid;
        struct l2cap_chan *chan;
        struct sock *sk;
 
+       if (cmd_len != sizeof(*rsp))
+               return -EPROTO;
+
        scid = __le16_to_cpu(rsp->scid);
        dcid = __le16_to_cpu(rsp->dcid);
 
@@ -2745,7 +2819,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
        if (sock_owned_by_user(sk)) {
                l2cap_state_change(chan,BT_DISCONN);
                __clear_chan_timer(chan);
-               __set_chan_timer(chan, HZ / 5);
+               __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
                bh_unlock_sock(sk);
                return 0;
        }
@@ -2757,11 +2831,16 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
        return 0;
 }
 
-static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_information_req(struct l2cap_conn *conn,
+                                       struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+                                       u8 *data)
 {
        struct l2cap_info_req *req = (struct l2cap_info_req *) data;
        u16 type;
 
+       if (cmd_len != sizeof(*req))
+               return -EPROTO;
+
        type = __le16_to_cpu(req->type);
 
        BT_DBG("type 0x%4.4x", type);
@@ -2797,11 +2876,16 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm
        return 0;
 }
 
-static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_information_rsp(struct l2cap_conn *conn,
+                                       struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+                                       u8 *data)
 {
        struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) data;
        u16 type, result;
 
+       if (cmd_len < sizeof(*rsp))
+               return -EPROTO;
+
        type   = __le16_to_cpu(rsp->type);
        result = __le16_to_cpu(rsp->result);
 
@@ -2920,15 +3004,15 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
 
        switch (cmd->code) {
        case L2CAP_COMMAND_REJ:
-               l2cap_command_rej(conn, cmd, data);
+               l2cap_command_rej(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_CONN_REQ:
-               err = l2cap_connect_req(conn, cmd, data);
+               err = l2cap_connect_req(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_CONN_RSP:
-               err = l2cap_connect_rsp(conn, cmd, data);
+               err = l2cap_connect_rsp(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_CONF_REQ:
@@ -2936,15 +3020,15 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
                break;
 
        case L2CAP_CONF_RSP:
-               err = l2cap_config_rsp(conn, cmd, data);
+               err = l2cap_config_rsp(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_DISCONN_REQ:
-               err = l2cap_disconnect_req(conn, cmd, data);
+               err = l2cap_disconnect_req(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_DISCONN_RSP:
-               err = l2cap_disconnect_rsp(conn, cmd, data);
+               err = l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_ECHO_REQ:
@@ -2955,11 +3039,11 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
                break;
 
        case L2CAP_INFO_REQ:
-               err = l2cap_information_req(conn, cmd, data);
+               err = l2cap_information_req(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_INFO_RSP:
-               err = l2cap_information_rsp(conn, cmd, data);
+               err = l2cap_information_rsp(conn, cmd, cmd_len, data);
                break;
 
        default:
@@ -3121,102 +3205,104 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
        return 0;
 }
 
-static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
+static void append_skb_frag(struct sk_buff *skb,
+                       struct sk_buff *new_frag, struct sk_buff **last_frag)
 {
-       struct sk_buff *_skb;
-       int err;
+       /* skb->len reflects data in skb as well as all fragments
+        * skb->data_len reflects only data in fragments
+        */
+       if (!skb_has_frag_list(skb))
+               skb_shinfo(skb)->frag_list = new_frag;
+
+       new_frag->next = NULL;
+
+       (*last_frag)->next = new_frag;
+       *last_frag = new_frag;
+
+       skb->len += new_frag->len;
+       skb->data_len += new_frag->len;
+       skb->truesize += new_frag->truesize;
+}
+
+static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
+{
+       int err = -EINVAL;
 
        switch (control & L2CAP_CTRL_SAR) {
        case L2CAP_SDU_UNSEGMENTED:
-               if (test_bit(CONN_SAR_SDU, &chan->conn_state))
-                       goto drop;
+               if (chan->sdu)
+                       break;
 
-               return chan->ops->recv(chan->data, skb);
+               err = chan->ops->recv(chan->data, skb);
+               break;
 
        case L2CAP_SDU_START:
-               if (test_bit(CONN_SAR_SDU, &chan->conn_state))
-                       goto drop;
+               if (chan->sdu)
+                       break;
 
                chan->sdu_len = get_unaligned_le16(skb->data);
+               skb_pull(skb, 2);
 
-               if (chan->sdu_len > chan->imtu)
-                       goto disconnect;
-
-               chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC);
-               if (!chan->sdu)
-                       return -ENOMEM;
+               if (chan->sdu_len > chan->imtu) {
+                       err = -EMSGSIZE;
+                       break;
+               }
 
-               /* pull sdu_len bytes only after alloc, because of Local Busy
-                * condition we have to be sure that this will be executed
-                * only once, i.e., when alloc does not fail */
-               skb_pull(skb, 2);
+               if (skb->len >= chan->sdu_len)
+                       break;
 
-               memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
+               chan->sdu = skb;
+               chan->sdu_last_frag = skb;
 
-               set_bit(CONN_SAR_SDU, &chan->conn_state);
-               chan->partial_sdu_len = skb->len;
+               skb = NULL;
+               err = 0;
                break;
 
        case L2CAP_SDU_CONTINUE:
-               if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
-                       goto disconnect;
-
                if (!chan->sdu)
-                       goto disconnect;
+                       break;
 
-               chan->partial_sdu_len += skb->len;
-               if (chan->partial_sdu_len > chan->sdu_len)
-                       goto drop;
+               append_skb_frag(chan->sdu, skb,
+                               &chan->sdu_last_frag);
+               skb = NULL;
 
-               memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
+               if (chan->sdu->len >= chan->sdu_len)
+                       break;
 
+               err = 0;
                break;
 
        case L2CAP_SDU_END:
-               if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
-                       goto disconnect;
-
                if (!chan->sdu)
-                       goto disconnect;
-
-               chan->partial_sdu_len += skb->len;
-
-               if (chan->partial_sdu_len > chan->imtu)
-                       goto drop;
+                       break;
 
-               if (chan->partial_sdu_len != chan->sdu_len)
-                       goto drop;
+               append_skb_frag(chan->sdu, skb,
+                               &chan->sdu_last_frag);
+               skb = NULL;
 
-               memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
+               if (chan->sdu->len != chan->sdu_len)
+                       break;
 
-               _skb = skb_clone(chan->sdu, GFP_ATOMIC);
-               if (!_skb) {
-                       return -ENOMEM;
-               }
+               err = chan->ops->recv(chan->data, chan->sdu);
 
-               err = chan->ops->recv(chan->data, _skb);
-               if (err < 0) {
-                       kfree_skb(_skb);
-                       return err;
+               if (!err) {
+                       /* Reassembly complete */
+                       chan->sdu = NULL;
+                       chan->sdu_last_frag = NULL;
+                       chan->sdu_len = 0;
                }
-
-               clear_bit(CONN_SAR_SDU, &chan->conn_state);
-
-               kfree_skb(chan->sdu);
                break;
        }
 
-       kfree_skb(skb);
-       return 0;
-
-drop:
-       kfree_skb(chan->sdu);
-       chan->sdu = NULL;
+       if (err) {
+               kfree_skb(skb);
+               kfree_skb(chan->sdu);
+               chan->sdu = NULL;
+               chan->sdu_last_frag = NULL;
+               chan->sdu_len = 0;
+       }
 
-disconnect:
-       l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
-       kfree_skb(skb);
-       return 0;
+       return err;
 }
 
 static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
@@ -3270,99 +3356,6 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
        }
 }
 
-static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
-{
-       struct sk_buff *_skb;
-       int err = -EINVAL;
-
-       /*
-        * TODO: We have to notify the userland if some data is lost with the
-        * Streaming Mode.
-        */
-
-       switch (control & L2CAP_CTRL_SAR) {
-       case L2CAP_SDU_UNSEGMENTED:
-               if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
-                       kfree_skb(chan->sdu);
-                       break;
-               }
-
-               err = chan->ops->recv(chan->data, skb);
-               if (!err)
-                       return 0;
-
-               break;
-
-       case L2CAP_SDU_START:
-               if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
-                       kfree_skb(chan->sdu);
-                       break;
-               }
-
-               chan->sdu_len = get_unaligned_le16(skb->data);
-               skb_pull(skb, 2);
-
-               if (chan->sdu_len > chan->imtu) {
-                       err = -EMSGSIZE;
-                       break;
-               }
-
-               chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC);
-               if (!chan->sdu) {
-                       err = -ENOMEM;
-                       break;
-               }
-
-               memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
-
-               set_bit(CONN_SAR_SDU, &chan->conn_state);
-               chan->partial_sdu_len = skb->len;
-               err = 0;
-               break;
-
-       case L2CAP_SDU_CONTINUE:
-               if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
-                       break;
-
-               memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
-
-               chan->partial_sdu_len += skb->len;
-               if (chan->partial_sdu_len > chan->sdu_len)
-                       kfree_skb(chan->sdu);
-               else
-                       err = 0;
-
-               break;
-
-       case L2CAP_SDU_END:
-               if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
-                       break;
-
-               memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
-
-               clear_bit(CONN_SAR_SDU, &chan->conn_state);
-               chan->partial_sdu_len += skb->len;
-
-               if (chan->partial_sdu_len > chan->imtu)
-                       goto drop;
-
-               if (chan->partial_sdu_len == chan->sdu_len) {
-                       _skb = skb_clone(chan->sdu, GFP_ATOMIC);
-                       err = chan->ops->recv(chan->data, _skb);
-                       if (err < 0)
-                               kfree_skb(_skb);
-               }
-               err = 0;
-
-drop:
-               kfree_skb(chan->sdu);
-               break;
-       }
-
-       kfree_skb(skb);
-       return err;
-}
-
 static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
 {
        struct sk_buff *skb;
@@ -3377,7 +3370,7 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
 
                skb = skb_dequeue(&chan->srej_q);
                control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
-               err = l2cap_ertm_reassembly_sdu(chan, skb, control);
+               err = l2cap_reassemble_sdu(chan, skb, control);
 
                if (err < 0) {
                        l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
@@ -3537,7 +3530,7 @@ expected:
                return 0;
        }
 
-       err = l2cap_ertm_reassembly_sdu(chan, skb, rx_control);
+       err = l2cap_reassemble_sdu(chan, skb, rx_control);
        chan->buffer_seq = (chan->buffer_seq + 1) % 64;
        if (err < 0) {
                l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
@@ -3853,12 +3846,20 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
 
                tx_seq = __get_txseq(control);
 
-               if (chan->expected_tx_seq == tx_seq)
-                       chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
-               else
-                       chan->expected_tx_seq = (tx_seq + 1) % 64;
+               if (chan->expected_tx_seq != tx_seq) {
+                       /* Frame(s) missing - must discard partial SDU */
+                       kfree_skb(chan->sdu);
+                       chan->sdu = NULL;
+                       chan->sdu_last_frag = NULL;
+                       chan->sdu_len = 0;
+
+                       /* TODO: Notify userland of missing data */
+               }
 
-               l2cap_streaming_reassembly_sdu(chan, skb, control);
+               chan->expected_tx_seq = (tx_seq + 1) % 64;
+
+               if (l2cap_reassemble_sdu(chan, skb, control) == -EMSGSIZE)
+                       l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
 
                goto done;
 
@@ -4074,7 +4075,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
        if (encrypt == 0x00) {
                if (chan->sec_level == BT_SECURITY_MEDIUM) {
                        __clear_chan_timer(chan);
-                       __set_chan_timer(chan, HZ * 5);
+                       __set_chan_timer(chan, L2CAP_ENC_TIMEOUT);
                } else if (chan->sec_level == BT_SECURITY_HIGH)
                        l2cap_chan_close(chan, ECONNREFUSED);
        } else {
@@ -4093,6 +4094,11 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
 
        BT_DBG("conn %p", conn);
 
+       if (hcon->type == LE_LINK) {
+               smp_distribute_keys(conn, 0);
+               del_timer(&conn->security_timer);
+       }
+
        read_lock(&conn->chan_lock);
 
        list_for_each_entry(chan, &conn->chan_l, list) {
@@ -4105,9 +4111,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
                if (chan->scid == L2CAP_CID_LE_DATA) {
                        if (!status && encrypt) {
                                chan->sec_level = hcon->sec_level;
-                               del_timer(&conn->security_timer);
                                l2cap_chan_ready(sk);
-                               smp_distribute_keys(conn, 0);
                        }
 
                        bh_unlock_sock(sk);
@@ -4139,7 +4143,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
                                        L2CAP_CONN_REQ, sizeof(req), &req);
                        } else {
                                __clear_chan_timer(chan);
-                               __set_chan_timer(chan, HZ / 10);
+                               __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
                        }
                } else if (chan->state == BT_CONNECT2) {
                        struct l2cap_conn_rsp rsp;
@@ -4159,7 +4163,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
                                }
                        } else {
                                l2cap_state_change(chan, BT_DISCONN);
-                               __set_chan_timer(chan, HZ / 10);
+                               __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
                                res = L2CAP_CR_SEC_BLOCK;
                                stat = L2CAP_CS_NO_INFO;
                        }