Skip to content

Commit 7f14815

Browse files
HaavardReinordicjm
authored andcommitted
[nrf fromlist] Bluetooth: Host: Add req/rsp l2cap validation
L2CAP channels will now, along with the ident, store the opcode of the pending request. This commit expands the ident lookup function to also compare received response types to this opcode, and will ignore unsolicited responses. Setting of idents for channels are moved after verification of buffer allocation for the request to be sent. A TODO is added for improving this functionality at a later time. Upstream PR #: 94080 Signed-off-by: Håvard Reierstad <[email protected]>
1 parent 8df4dd0 commit 7f14815

File tree

2 files changed

+53
-19
lines changed

2 files changed

+53
-19
lines changed

include/zephyr/bluetooth/l2cap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ struct bt_l2cap_le_chan {
264264
uint16_t psm;
265265
/** Helps match request context during CoC */
266266
uint8_t ident;
267+
/** Opcode of the pending request. Used to match responses with requests. */
268+
uint8_t pending_req;
267269
bt_security_t required_sec_level;
268270

269271
/* Response Timeout eXpired (RTX) timer */

subsys/bluetooth/host/l2cap.c

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,11 @@ NET_BUF_POOL_FIXED_DEFINE(disc_pool, 1,
8080
sizeof(struct bt_l2cap_disconn_req)),
8181
CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
8282

83-
#define l2cap_lookup_ident(conn, ident) __l2cap_lookup_ident(conn, ident, false)
84-
#define l2cap_remove_ident(conn, ident) __l2cap_lookup_ident(conn, ident, true)
83+
#define ANY_OPCODE 0x100
84+
#define l2cap_lookup_ident(conn, ident, req_opcode) \
85+
__l2cap_lookup_ident(conn, ident, req_opcode, false)
86+
#define l2cap_remove_ident(conn, ident, req_opcode) \
87+
__l2cap_lookup_ident(conn, ident, req_opcode, true)
8588

8689
static sys_slist_t servers = SYS_SLIST_STATIC_INIT(&servers);
8790
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
@@ -139,13 +142,15 @@ static struct bt_l2cap_le_chan *l2cap_chan_alloc_cid(struct bt_conn *conn,
139142
}
140143

141144
static struct bt_l2cap_le_chan *
142-
__l2cap_lookup_ident(struct bt_conn *conn, uint16_t ident, bool remove)
145+
__l2cap_lookup_ident(struct bt_conn *conn, uint16_t ident, uint16_t req_opcode, bool remove)
143146
{
144147
struct bt_l2cap_chan *chan;
145148
sys_snode_t *prev = NULL;
146149

147150
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
148-
if (BT_L2CAP_LE_CHAN(chan)->ident == ident) {
151+
if ((BT_L2CAP_LE_CHAN(chan)->ident == ident) &&
152+
((BT_L2CAP_LE_CHAN(chan)->pending_req == req_opcode) ||
153+
(req_opcode == ANY_OPCODE))) {
149154
if (remove) {
150155
sys_slist_remove(&conn->channels, prev,
151156
&chan->node);
@@ -303,7 +308,7 @@ static void l2cap_rtx_timeout(struct k_work *work)
303308
bt_l2cap_chan_del(&chan->chan);
304309

305310
/* Remove other channels if pending on the same ident */
306-
while ((chan = l2cap_remove_ident(conn, chan->ident))) {
311+
while ((chan = l2cap_remove_ident(conn, chan->ident, chan->pending_req))) {
307312
bt_l2cap_chan_del(&chan->chan);
308313
}
309314
}
@@ -523,15 +528,23 @@ static int l2cap_le_conn_req(struct bt_l2cap_le_chan *ch)
523528
{
524529
struct net_buf *buf;
525530
struct bt_l2cap_le_conn_req *req;
531+
uint8_t ident;
526532

527-
ch->ident = get_ident();
533+
ident = get_ident();
528534

529-
buf = l2cap_create_le_sig_pdu(BT_L2CAP_LE_CONN_REQ,
530-
ch->ident, sizeof(*req));
535+
buf = l2cap_create_le_sig_pdu(BT_L2CAP_LE_CONN_REQ, ident, sizeof(*req));
531536
if (!buf) {
532537
return -ENOMEM;
533538
}
534539

540+
/* TODO Ident handling/setting should ideally be done in l2cap_chan_send_req after the
541+
* request is successfully sent on the channel but will require special considerations for
542+
* functions such as l2cap_ecred_conn_req and bt_l2cap_ecred_chan_reconfigure where the
543+
* ident is set for multiple channels, but the request is only sent on one.
544+
*/
545+
ch->ident = ident;
546+
ch->pending_req = BT_L2CAP_LE_CONN_REQ;
547+
535548
req = net_buf_add(buf, sizeof(*req));
536549
req->psm = sys_cpu_to_le16(ch->psm);
537550
req->scid = sys_cpu_to_le16(ch->rx.cid);
@@ -590,6 +603,7 @@ static int l2cap_ecred_conn_req(struct bt_l2cap_chan **chan, int channels)
590603
"The MTU shall be the same for channels in the same request.");
591604

592605
ch->ident = ident;
606+
ch->pending_req = BT_L2CAP_ECRED_CONN_REQ;
593607

594608
net_buf_add_le16(buf, ch->rx.cid);
595609
}
@@ -1785,7 +1799,7 @@ static void le_ecred_reconf_rsp(struct bt_l2cap *l2cap, uint8_t ident,
17851799
rsp = net_buf_pull_mem(buf, sizeof(*rsp));
17861800
result = sys_le16_to_cpu(rsp->result);
17871801

1788-
while ((ch = l2cap_lookup_ident(conn, ident))) {
1802+
while ((ch = l2cap_lookup_ident(conn, ident, BT_L2CAP_ECRED_RECONF_REQ))) {
17891803
/* Stop timer started on REQ send. The timer is only set on one
17901804
* of the channels, but we don't want to make assumptions on
17911805
* which one it is.
@@ -1798,6 +1812,7 @@ static void le_ecred_reconf_rsp(struct bt_l2cap *l2cap, uint8_t ident,
17981812

17991813
ch->pending_rx_mtu = 0;
18001814
ch->ident = 0U;
1815+
ch->pending_req = 0U;
18011816

18021817
if (ch->chan.ops->reconfigured) {
18031818
ch->chan.ops->reconfigured(&ch->chan);
@@ -1943,7 +1958,7 @@ static void le_ecred_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
19431958

19441959
LOG_DBG("mtu 0x%04x mps 0x%04x credits 0x%04x result %u", mtu, mps, credits, result);
19451960

1946-
chan = l2cap_lookup_ident(conn, ident);
1961+
chan = l2cap_lookup_ident(conn, ident, BT_L2CAP_ECRED_CONN_REQ);
19471962
if (chan) {
19481963
psm = chan->psm;
19491964
} else {
@@ -1953,7 +1968,7 @@ static void le_ecred_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
19531968
switch (result) {
19541969
case BT_L2CAP_LE_ERR_AUTHENTICATION:
19551970
case BT_L2CAP_LE_ERR_ENCRYPTION:
1956-
while ((chan = l2cap_lookup_ident(conn, ident))) {
1971+
while ((chan = l2cap_lookup_ident(conn, ident, BT_L2CAP_ECRED_CONN_REQ))) {
19571972

19581973
/* Cancel RTX work */
19591974
k_work_cancel_delayable(&chan->rtx_work);
@@ -1973,7 +1988,7 @@ static void le_ecred_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
19731988
case BT_L2CAP_LE_ERR_SCID_IN_USE:
19741989
/* Some connections refused – not enough resources available */
19751990
case BT_L2CAP_LE_ERR_NO_RESOURCES:
1976-
while ((chan = l2cap_lookup_ident(conn, ident))) {
1991+
while ((chan = l2cap_lookup_ident(conn, ident, BT_L2CAP_ECRED_CONN_REQ))) {
19771992
struct bt_l2cap_chan *c;
19781993

19791994
/* Cancel RTX work */
@@ -2029,6 +2044,7 @@ static void le_ecred_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
20292044
chan->tx.cid = dcid;
20302045

20312046
chan->ident = 0U;
2047+
chan->pending_req = 0U;
20322048

20332049
chan->tx.mtu = mtu;
20342050
chan->tx.mps = mps;
@@ -2049,7 +2065,7 @@ static void le_ecred_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
20492065
break;
20502066
case BT_L2CAP_LE_ERR_PSM_NOT_SUPP:
20512067
default:
2052-
while ((chan = l2cap_remove_ident(conn, ident))) {
2068+
while ((chan = l2cap_remove_ident(conn, ident, BT_L2CAP_ECRED_CONN_REQ))) {
20532069
bt_l2cap_chan_del(&chan->chan);
20542070
}
20552071
break;
@@ -2088,9 +2104,9 @@ static void le_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
20882104
if (result == BT_L2CAP_LE_SUCCESS ||
20892105
result == BT_L2CAP_LE_ERR_AUTHENTICATION ||
20902106
result == BT_L2CAP_LE_ERR_ENCRYPTION) {
2091-
chan = l2cap_lookup_ident(conn, ident);
2107+
chan = l2cap_lookup_ident(conn, ident, BT_L2CAP_LE_CONN_REQ);
20922108
} else {
2093-
chan = l2cap_remove_ident(conn, ident);
2109+
chan = l2cap_remove_ident(conn, ident, BT_L2CAP_LE_CONN_REQ);
20942110
}
20952111

20962112
if (!chan) {
@@ -2103,6 +2119,7 @@ static void le_conn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
21032119

21042120
/* Reset ident since it got a response */
21052121
chan->ident = 0U;
2122+
chan->pending_req = 0U;
21062123

21072124
switch (result) {
21082125
case BT_L2CAP_LE_SUCCESS:
@@ -2162,7 +2179,17 @@ static void le_disconn_rsp(struct bt_l2cap *l2cap, uint8_t ident,
21622179
return;
21632180
}
21642181

2182+
chan = l2cap_lookup_ident(conn, ident, BT_L2CAP_DISCONN_REQ);
2183+
if (!chan) {
2184+
LOG_ERR("Cannot find channel for ident %u", ident);
2185+
return;
2186+
}
2187+
21652188
scid = sys_le16_to_cpu(rsp->scid);
2189+
if (scid != chan->rx.cid) {
2190+
LOG_ERR("Invalid scid 0x%04x", scid);
2191+
return;
2192+
}
21662193

21672194
LOG_DBG("dcid 0x%04x scid 0x%04x", sys_le16_to_cpu(rsp->dcid), scid);
21682195

@@ -2230,7 +2257,7 @@ static void reject_cmd(struct bt_l2cap *l2cap, uint8_t ident,
22302257
struct bt_conn *conn = l2cap->chan.chan.conn;
22312258
struct bt_l2cap_le_chan *chan;
22322259

2233-
while ((chan = l2cap_remove_ident(conn, ident))) {
2260+
while ((chan = l2cap_remove_ident(conn, ident, ANY_OPCODE))) {
22342261
bt_l2cap_chan_del(&chan->chan);
22352262
}
22362263
}
@@ -3097,6 +3124,7 @@ int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu)
30973124
ch = BT_L2CAP_LE_CHAN(chans[j]);
30983125

30993126
ch->ident = ident;
3127+
ch->pending_req = BT_L2CAP_ECRED_RECONF_REQ;
31003128
ch->pending_rx_mtu = mtu;
31013129

31023130
net_buf_add_le16(buf, ch->rx.cid);
@@ -3184,6 +3212,7 @@ int bt_l2cap_ecred_chan_reconfigure_explicit(struct bt_l2cap_chan **chans, size_
31843212
ch = BT_L2CAP_LE_CHAN(chans[i]);
31853213

31863214
ch->ident = ident;
3215+
ch->pending_req = BT_L2CAP_ECRED_RECONF_REQ;
31873216
ch->pending_rx_mtu = mtu;
31883217

31893218
net_buf_add_le16(buf, ch->rx.cid);
@@ -3236,6 +3265,7 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
32363265
struct net_buf *buf;
32373266
struct bt_l2cap_disconn_req *req;
32383267
struct bt_l2cap_le_chan *le_chan;
3268+
uint8_t ident;
32393269

32403270
if (!conn) {
32413271
return -ENOTCONN;
@@ -3250,14 +3280,16 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
32503280

32513281
LOG_DBG("chan %p scid 0x%04x dcid 0x%04x", chan, le_chan->rx.cid, le_chan->tx.cid);
32523282

3253-
le_chan->ident = get_ident();
3283+
ident = get_ident();
32543284

3255-
buf = l2cap_create_le_sig_pdu(BT_L2CAP_DISCONN_REQ,
3256-
le_chan->ident, sizeof(*req));
3285+
buf = l2cap_create_le_sig_pdu(BT_L2CAP_DISCONN_REQ, ident, sizeof(*req));
32573286
if (!buf) {
32583287
return -ENOMEM;
32593288
}
32603289

3290+
le_chan->ident = ident;
3291+
le_chan->pending_req = BT_L2CAP_DISCONN_REQ;
3292+
32613293
req = net_buf_add(buf, sizeof(*req));
32623294
req->dcid = sys_cpu_to_le16(le_chan->tx.cid);
32633295
req->scid = sys_cpu_to_le16(le_chan->rx.cid);

0 commit comments

Comments
 (0)