Bluetooth: L2CAP: Fix use of uninitializer pointer

l2cap_br_conn_req_reply expects valid bt_l2cap_chan pointer but this
is achieved only on accept() callback. Use l2cap_br_send_conn_rsp
instead for rejecting cases where no channel was accepted. This also
makes success path being 'primary' function path ie erros all always
handled inside if() statements.

Jira: ZEP-1405

Change-Id: I890b4fcf029afce65eba4f2ebae0b1094feb007f
Signed-off-by: Szymon Janc <szymon.janc@codecoup.pl>
This commit is contained in:
Szymon Janc 2016-12-22 19:48:08 +01:00 committed by Johan Hedberg
commit ba2adb70b2

View file

@ -747,7 +747,7 @@ static void l2cap_br_conn_req(struct bt_l2cap_br *l2cap, uint8_t ident,
struct bt_l2cap_chan *chan; struct bt_l2cap_chan *chan;
struct bt_l2cap_server *server; struct bt_l2cap_server *server;
struct bt_l2cap_conn_req *req = (void *)buf->data; struct bt_l2cap_conn_req *req = (void *)buf->data;
uint16_t psm, scid, dcid, result; uint16_t psm, scid, result;
if (buf->len < sizeof(*req)) { if (buf->len < sizeof(*req)) {
BT_ERR("Too small L2CAP conn req packet size"); BT_ERR("Too small L2CAP conn req packet size");
@ -756,7 +756,6 @@ static void l2cap_br_conn_req(struct bt_l2cap_br *l2cap, uint8_t ident,
psm = sys_le16_to_cpu(req->psm); psm = sys_le16_to_cpu(req->psm);
scid = sys_le16_to_cpu(req->scid); scid = sys_le16_to_cpu(req->scid);
dcid = 0;
BT_DBG("psm 0x%02x scid 0x%04x", psm, scid); BT_DBG("psm 0x%02x scid 0x%04x", psm, scid);
@ -764,7 +763,7 @@ static void l2cap_br_conn_req(struct bt_l2cap_br *l2cap, uint8_t ident,
server = l2cap_br_server_lookup_psm(psm); server = l2cap_br_server_lookup_psm(psm);
if (!server) { if (!server) {
result = BT_L2CAP_BR_ERR_PSM_NOT_SUPP; result = BT_L2CAP_BR_ERR_PSM_NOT_SUPP;
goto done; goto no_chan;
} }
/* /*
@ -774,18 +773,23 @@ static void l2cap_br_conn_req(struct bt_l2cap_br *l2cap, uint8_t ident,
if (server->sec_level != BT_SECURITY_NONE && if (server->sec_level != BT_SECURITY_NONE &&
BT_FEAT_HOST_SSP(conn->br.features) && !conn->encrypt) { BT_FEAT_HOST_SSP(conn->br.features) && !conn->encrypt) {
result = BT_L2CAP_BR_ERR_SEC_BLOCK; result = BT_L2CAP_BR_ERR_SEC_BLOCK;
goto done; goto no_chan;
} }
if (!L2CAP_BR_CID_IS_DYN(scid)) { if (!L2CAP_BR_CID_IS_DYN(scid)) {
result = BT_L2CAP_BR_ERR_INVALID_SCID; result = BT_L2CAP_BR_ERR_INVALID_SCID;
goto done; goto no_chan;
} }
chan = bt_l2cap_br_lookup_tx_cid(conn, scid); chan = bt_l2cap_br_lookup_tx_cid(conn, scid);
if (chan) { if (chan) {
/*
* we have a chan here but this is due to SCID being already in
* use so it is not channel we are suppose to pass to
* l2cap_br_conn_req_reply as wrong DCID would be used
*/
result = BT_L2CAP_BR_ERR_SCID_IN_USE; result = BT_L2CAP_BR_ERR_SCID_IN_USE;
goto done; goto no_chan;
} }
/* /*
@ -794,16 +798,14 @@ static void l2cap_br_conn_req(struct bt_l2cap_br *l2cap, uint8_t ident,
* proper result and quit since chan pointer is uninitialized then. * proper result and quit since chan pointer is uninitialized then.
*/ */
if (server->accept(conn, &chan) < 0) { if (server->accept(conn, &chan) < 0) {
l2cap_br_send_conn_rsp(conn, scid, dcid, ident, result = BT_L2CAP_BR_ERR_NO_RESOURCES;
BT_L2CAP_BR_ERR_NO_RESOURCES); goto no_chan;
return;
} }
chan->required_sec_level = server->sec_level; chan->required_sec_level = server->sec_level;
l2cap_br_chan_add(conn, chan, l2cap_br_chan_destroy); l2cap_br_chan_add(conn, chan, l2cap_br_chan_destroy);
BR_CHAN(chan)->tx.cid = scid; BR_CHAN(chan)->tx.cid = scid;
dcid = BR_CHAN(chan)->rx.cid;
chan->ident = ident; chan->ident = ident;
bt_l2cap_chan_set_state(chan, BT_L2CAP_CONNECT); bt_l2cap_chan_set_state(chan, BT_L2CAP_CONNECT);
atomic_set_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_ACCEPTOR); atomic_set_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_ACCEPTOR);
@ -824,20 +826,25 @@ static void l2cap_br_conn_req(struct bt_l2cap_br *l2cap, uint8_t ident,
result = BT_L2CAP_BR_ERR_SEC_BLOCK; result = BT_L2CAP_BR_ERR_SEC_BLOCK;
break; break;
} }
done:
/* Reply on connection request as acceptor */ /* Reply on connection request as acceptor */
l2cap_br_conn_req_reply(chan, result); l2cap_br_conn_req_reply(chan, result);
/* Disconnect link when security rules were violated */ if (result != BT_L2CAP_SUCCESS) {
if (result == BT_L2CAP_BR_ERR_SEC_BLOCK) { /* Disconnect link when security rules were violated */
bt_conn_disconnect(conn, BT_HCI_ERR_AUTHENTICATION_FAIL); if (result == BT_L2CAP_BR_ERR_SEC_BLOCK) {
bt_conn_disconnect(conn,
BT_HCI_ERR_AUTHENTICATION_FAIL);
}
return; return;
} }
if (result == BT_L2CAP_SUCCESS) { bt_l2cap_chan_set_state(chan, BT_L2CAP_CONFIG);
bt_l2cap_chan_set_state(chan, BT_L2CAP_CONFIG); l2cap_br_conf(chan);
l2cap_br_conf(chan); return;
}
no_chan:
l2cap_br_send_conn_rsp(conn, scid, 0, ident, result);
} }
static void l2cap_br_conf_rsp(struct bt_l2cap_br *l2cap, uint8_t ident, static void l2cap_br_conf_rsp(struct bt_l2cap_br *l2cap, uint8_t ident,