Commit ade988a9 authored by Johan Hedberg's avatar Johan Hedberg Committed by Greg Kroah-Hartman

Bluetooth: Fix missing length checks for L2CAP signalling PDUs

commit cb3b3152 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: default avatarJohan Hedberg <johan.hedberg@intel.com>
Signed-off-by: default avatarGustavo Padovan <gustavo.padovan@collabora.co.uk>
Signed-off-by: default avatarJohn 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: default avatarBen Hutchings <ben@decadent.org.uk>
[wujg: Backported to 3.4:
 - Adjust context
 - Adjust l2cap_create_channel_rsp()'s parameters]
Signed-off-by: default avatarJianguo Wu <wujianguo@huawei.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 653b6fe7
...@@ -2620,10 +2620,15 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) ...@@ -2620,10 +2620,15 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
} }
} }
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; 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) if (rej->reason != L2CAP_REJ_NOT_UNDERSTOOD)
return 0; return 0;
...@@ -2640,7 +2645,8 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd ...@@ -2640,7 +2645,8 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
return 0; 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_req *req = (struct l2cap_conn_req *) data;
struct l2cap_conn_rsp rsp; struct l2cap_conn_rsp rsp;
...@@ -2648,8 +2654,14 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd ...@@ -2648,8 +2654,14 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
struct sock *parent, *sk = NULL; struct sock *parent, *sk = NULL;
int result, status = L2CAP_CS_NO_INFO; int result, status = L2CAP_CS_NO_INFO;
u16 dcid = 0, scid = __le16_to_cpu(req->scid); u16 dcid = 0, scid;
__le16 psm = req->psm; __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); BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
...@@ -2770,7 +2782,9 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd ...@@ -2770,7 +2782,9 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
return 0; 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; struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;
u16 scid, dcid, result, status; u16 scid, dcid, result, status;
...@@ -2778,6 +2792,9 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd ...@@ -2778,6 +2792,9 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
u8 req[128]; u8 req[128];
int err; int err;
if (cmd_len < sizeof(*rsp))
return -EPROTO;
scid = __le16_to_cpu(rsp->scid); scid = __le16_to_cpu(rsp->scid);
dcid = __le16_to_cpu(rsp->dcid); dcid = __le16_to_cpu(rsp->dcid);
result = __le16_to_cpu(rsp->result); result = __le16_to_cpu(rsp->result);
...@@ -2857,6 +2874,9 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr ...@@ -2857,6 +2874,9 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
struct l2cap_chan *chan; struct l2cap_chan *chan;
int len; int len;
if (cmd_len < sizeof(*req))
return -EPROTO;
dcid = __le16_to_cpu(req->dcid); dcid = __le16_to_cpu(req->dcid);
flags = __le16_to_cpu(req->flags); flags = __le16_to_cpu(req->flags);
...@@ -2882,7 +2902,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr ...@@ -2882,7 +2902,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
/* Reject if config buffer is too small. */ /* Reject if config buffer is too small. */
len = cmd_len - sizeof(*req); 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_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
l2cap_build_conf_rsp(chan, rsp, l2cap_build_conf_rsp(chan, rsp,
L2CAP_CONF_REJECT, flags), rsp); L2CAP_CONF_REJECT, flags), rsp);
...@@ -2959,12 +2979,17 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr ...@@ -2959,12 +2979,17 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
return 0; 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; struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data;
u16 scid, flags, result; u16 scid, flags, result;
struct l2cap_chan *chan; 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); scid = __le16_to_cpu(rsp->scid);
flags = __le16_to_cpu(rsp->flags); flags = __le16_to_cpu(rsp->flags);
...@@ -3066,7 +3091,9 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr ...@@ -3066,7 +3091,9 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
return 0; 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_req *req = (struct l2cap_disconn_req *) data;
struct l2cap_disconn_rsp rsp; struct l2cap_disconn_rsp rsp;
...@@ -3074,6 +3101,9 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd ...@@ -3074,6 +3101,9 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
struct l2cap_chan *chan; struct l2cap_chan *chan;
struct sock *sk; struct sock *sk;
if (cmd_len != sizeof(*req))
return -EPROTO;
scid = __le16_to_cpu(req->scid); scid = __le16_to_cpu(req->scid);
dcid = __le16_to_cpu(req->dcid); dcid = __le16_to_cpu(req->dcid);
...@@ -3110,12 +3140,17 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd ...@@ -3110,12 +3140,17 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
return 0; 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; struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data;
u16 dcid, scid; u16 dcid, scid;
struct l2cap_chan *chan; struct l2cap_chan *chan;
if (cmd_len != sizeof(*rsp))
return -EPROTO;
scid = __le16_to_cpu(rsp->scid); scid = __le16_to_cpu(rsp->scid);
dcid = __le16_to_cpu(rsp->dcid); dcid = __le16_to_cpu(rsp->dcid);
...@@ -3142,11 +3177,16 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd ...@@ -3142,11 +3177,16 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
return 0; 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; struct l2cap_info_req *req = (struct l2cap_info_req *) data;
u16 type; u16 type;
if (cmd_len != sizeof(*req))
return -EPROTO;
type = __le16_to_cpu(req->type); type = __le16_to_cpu(req->type);
BT_DBG("type 0x%4.4x", type); BT_DBG("type 0x%4.4x", type);
...@@ -3192,11 +3232,16 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm ...@@ -3192,11 +3232,16 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm
return 0; 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; struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) data;
u16 type, result; u16 type, result;
if (cmd_len != sizeof(*rsp))
return -EPROTO;
type = __le16_to_cpu(rsp->type); type = __le16_to_cpu(rsp->type);
result = __le16_to_cpu(rsp->result); result = __le16_to_cpu(rsp->result);
...@@ -3282,11 +3327,12 @@ static inline int l2cap_create_channel_req(struct l2cap_conn *conn, ...@@ -3282,11 +3327,12 @@ static inline int l2cap_create_channel_req(struct l2cap_conn *conn,
} }
static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, 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); 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, static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident,
...@@ -3479,15 +3525,15 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, ...@@ -3479,15 +3525,15 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
switch (cmd->code) { switch (cmd->code) {
case L2CAP_COMMAND_REJ: case L2CAP_COMMAND_REJ:
l2cap_command_rej(conn, cmd, data); l2cap_command_rej(conn, cmd, cmd_len, data);
break; break;
case L2CAP_CONN_REQ: case L2CAP_CONN_REQ:
err = l2cap_connect_req(conn, cmd, data); err = l2cap_connect_req(conn, cmd, cmd_len, data);
break; break;
case L2CAP_CONN_RSP: case L2CAP_CONN_RSP:
err = l2cap_connect_rsp(conn, cmd, data); err = l2cap_connect_rsp(conn, cmd, cmd_len, data);
break; break;
case L2CAP_CONF_REQ: case L2CAP_CONF_REQ:
...@@ -3495,15 +3541,15 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, ...@@ -3495,15 +3541,15 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
break; break;
case L2CAP_CONF_RSP: case L2CAP_CONF_RSP:
err = l2cap_config_rsp(conn, cmd, data); err = l2cap_config_rsp(conn, cmd, cmd_len, data);
break; break;
case L2CAP_DISCONN_REQ: case L2CAP_DISCONN_REQ:
err = l2cap_disconnect_req(conn, cmd, data); err = l2cap_disconnect_req(conn, cmd, cmd_len, data);
break; break;
case L2CAP_DISCONN_RSP: case L2CAP_DISCONN_RSP:
err = l2cap_disconnect_rsp(conn, cmd, data); err = l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
break; break;
case L2CAP_ECHO_REQ: case L2CAP_ECHO_REQ:
...@@ -3514,11 +3560,11 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, ...@@ -3514,11 +3560,11 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
break; break;
case L2CAP_INFO_REQ: case L2CAP_INFO_REQ:
err = l2cap_information_req(conn, cmd, data); err = l2cap_information_req(conn, cmd, cmd_len, data);
break; break;
case L2CAP_INFO_RSP: case L2CAP_INFO_RSP:
err = l2cap_information_rsp(conn, cmd, data); err = l2cap_information_rsp(conn, cmd, cmd_len, data);
break; break;
case L2CAP_CREATE_CHAN_REQ: case L2CAP_CREATE_CHAN_REQ:
...@@ -3526,7 +3572,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, ...@@ -3526,7 +3572,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
break; break;
case L2CAP_CREATE_CHAN_RSP: case L2CAP_CREATE_CHAN_RSP:
err = l2cap_create_channel_rsp(conn, cmd, data); err = l2cap_create_channel_rsp(conn, cmd, cmd_len, data);
break; break;
case L2CAP_MOVE_CHAN_REQ: case L2CAP_MOVE_CHAN_REQ:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment