| From cb3b3152b2f5939d67005cff841a1ca748b19888 Mon Sep 17 00:00:00 2001 |
| From: Johan Hedberg <johan.hedberg@intel.com> |
| Date: Tue, 28 May 2013 13:46:30 +0300 |
| Subject: Bluetooth: Fix missing length checks for L2CAP signalling PDUs |
| |
| From: Johan Hedberg <johan.hedberg@intel.com> |
| |
| commit cb3b3152b2f5939d67005cff841a1ca748b19888 upstream. |
| |
| There has been code in place to check that the L2CAP length header |
| matches the amount of data received, but many PDU handlers have not been |
| checking that the data received actually matches that expected by the |
| specific PDU. This patch adds passing the length header to the specific |
| handler functions and ensures that those functions fail cleanly in the |
| case of an incorrect amount of data. |
| |
| Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> |
| Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk> |
| Signed-off-by: John W. Linville <linville@tuxdriver.com> |
| [bwh: Backported to 3.2: |
| - Adjust context |
| - Move uses of *req below the new check in l2cap_connect_req] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| [wujg: Backported to 3.4: |
| - Adjust context |
| - Adjust l2cap_create_channel_rsp()'s parameters] |
| Signed-off-by: Jianguo Wu <wujianguo@huawei.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/bluetooth/l2cap_core.c | 92 +++++++++++++++++++++++++++++++++------------ |
| 1 file changed, 69 insertions(+), 23 deletions(-) |
| |
| --- a/net/bluetooth/l2cap_core.c |
| +++ b/net/bluetooth/l2cap_core.c |
| @@ -2620,10 +2620,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; |
| |
| @@ -2640,7 +2645,8 @@ static inline int l2cap_command_rej(stru |
| 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; |
| @@ -2648,8 +2654,14 @@ static inline int l2cap_connect_req(stru |
| 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); |
| |
| @@ -2770,7 +2782,9 @@ sendresp: |
| 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; |
| @@ -2778,6 +2792,9 @@ static inline int l2cap_connect_rsp(stru |
| u8 req[128]; |
| int err; |
| |
| + 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); |
| @@ -2857,6 +2874,9 @@ static inline int l2cap_config_req(struc |
| struct l2cap_chan *chan; |
| int len; |
| |
| + if (cmd_len < sizeof(*req)) |
| + return -EPROTO; |
| + |
| dcid = __le16_to_cpu(req->dcid); |
| flags = __le16_to_cpu(req->flags); |
| |
| @@ -2882,7 +2902,7 @@ static inline int l2cap_config_req(struc |
| |
| /* 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); |
| @@ -2959,12 +2979,17 @@ 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; |
| - 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); |
| @@ -3066,7 +3091,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; |
| @@ -3074,6 +3101,9 @@ static inline int l2cap_disconnect_req(s |
| 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); |
| |
| @@ -3110,12 +3140,17 @@ static inline int l2cap_disconnect_req(s |
| 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; |
| |
| + if (cmd_len != sizeof(*rsp)) |
| + return -EPROTO; |
| + |
| scid = __le16_to_cpu(rsp->scid); |
| dcid = __le16_to_cpu(rsp->dcid); |
| |
| @@ -3142,11 +3177,16 @@ static inline int l2cap_disconnect_rsp(s |
| 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); |
| @@ -3192,11 +3232,16 @@ static inline int l2cap_information_req( |
| 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); |
| |
| @@ -3282,11 +3327,12 @@ static inline int l2cap_create_channel_r |
| } |
| |
| static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, |
| - struct l2cap_cmd_hdr *cmd, void *data) |
| + struct l2cap_cmd_hdr *cmd, u16 cmd_len, |
| + void *data) |
| { |
| BT_DBG("conn %p", conn); |
| |
| - return l2cap_connect_rsp(conn, cmd, data); |
| + return l2cap_connect_rsp(conn, cmd, cmd_len, data); |
| } |
| |
| static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, |
| @@ -3479,15 +3525,15 @@ static inline int l2cap_bredr_sig_cmd(st |
| |
| 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: |
| @@ -3495,15 +3541,15 @@ static inline int l2cap_bredr_sig_cmd(st |
| 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: |
| @@ -3514,11 +3560,11 @@ static inline int l2cap_bredr_sig_cmd(st |
| 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; |
| |
| case L2CAP_CREATE_CHAN_REQ: |
| @@ -3526,7 +3572,7 @@ static inline int l2cap_bredr_sig_cmd(st |
| break; |
| |
| case L2CAP_CREATE_CHAN_RSP: |
| - err = l2cap_create_channel_rsp(conn, cmd, data); |
| + err = l2cap_create_channel_rsp(conn, cmd, cmd_len, data); |
| break; |
| |
| case L2CAP_MOVE_CHAN_REQ: |