Skip to content

Commit

Permalink
Bluetooth: Fix missing length checks for L2CAP signalling PDUs
Browse files Browse the repository at this point in the history
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>
Cc: stable@vger.kernel.org
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Johan Hedberg authored and linvjw committed Jun 12, 2013
1 parent 22f2efe commit cb3b315
Showing 1 changed file with 52 additions and 18 deletions.
70 changes: 52 additions & 18 deletions net/bluetooth/l2cap_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -3677,10 +3677,14 @@ 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)
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;

Expand Down Expand Up @@ -3829,11 +3833,14 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
}

static int l2cap_connect_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u8 *data)
struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data)
{
struct hci_dev *hdev = conn->hcon->hdev;
struct hci_conn *hcon = conn->hcon;

if (cmd_len < sizeof(struct l2cap_conn_req))
return -EPROTO;

hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags))
Expand All @@ -3847,14 +3854,18 @@ static int l2cap_connect_req(struct l2cap_conn *conn,
}

static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u8 *data)
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;
struct l2cap_chan *chan;
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);
Expand Down Expand Up @@ -3952,6 +3963,9 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
struct l2cap_chan *chan;
int len, err = 0;

if (cmd_len < sizeof(*req))
return -EPROTO;

dcid = __le16_to_cpu(req->dcid);
flags = __le16_to_cpu(req->flags);

Expand All @@ -3975,7 +3989,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,

/* 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);
Expand Down Expand Up @@ -4053,14 +4067,18 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
}

static inline int l2cap_config_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u8 *data)
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 = le16_to_cpu(cmd->len) - sizeof(*rsp);
int len = cmd_len - sizeof(*rsp);
int err = 0;

if (cmd_len < sizeof(*rsp))
return -EPROTO;

scid = __le16_to_cpu(rsp->scid);
flags = __le16_to_cpu(rsp->flags);
result = __le16_to_cpu(rsp->result);
Expand Down Expand Up @@ -4161,14 +4179,18 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
}

static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u8 *data)
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;
u16 dcid, scid;
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);

Expand Down Expand Up @@ -4208,12 +4230,16 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
}

static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u8 *data)
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);

Expand Down Expand Up @@ -4243,11 +4269,15 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
}

static inline int l2cap_information_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u8 *data)
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);
Expand Down Expand Up @@ -4294,11 +4324,15 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
}

static inline int l2cap_information_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u8 *data)
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);

Expand Down Expand Up @@ -5164,32 +5198,32 @@ 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:
case L2CAP_CREATE_CHAN_RSP:
err = l2cap_connect_create_rsp(conn, cmd, data);
err = l2cap_connect_create_rsp(conn, cmd, cmd_len, data);
break;

case L2CAP_CONF_REQ:
err = l2cap_config_req(conn, cmd, cmd_len, data);
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:
Expand All @@ -5200,11 +5234,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;

case L2CAP_CREATE_CHAN_REQ:
Expand Down

0 comments on commit cb3b315

Please sign in to comment.