Skip to content

Commit 0a79f5a

Browse files
committed
[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 0a79f5a

File tree

2 files changed

+49
-19
lines changed

2 files changed

+49
-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: 47 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,6 +2179,12 @@ 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);
21662189

21672190
LOG_DBG("dcid 0x%04x scid 0x%04x", sys_le16_to_cpu(rsp->dcid), scid);
@@ -2230,7 +2253,7 @@ static void reject_cmd(struct bt_l2cap *l2cap, uint8_t ident,
22302253
struct bt_conn *conn = l2cap->chan.chan.conn;
22312254
struct bt_l2cap_le_chan *chan;
22322255

2233-
while ((chan = l2cap_remove_ident(conn, ident))) {
2256+
while ((chan = l2cap_remove_ident(conn, ident, ANY_OPCODE))) {
22342257
bt_l2cap_chan_del(&chan->chan);
22352258
}
22362259
}
@@ -3097,6 +3120,7 @@ int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu)
30973120
ch = BT_L2CAP_LE_CHAN(chans[j]);
30983121

30993122
ch->ident = ident;
3123+
ch->pending_req = BT_L2CAP_ECRED_RECONF_REQ;
31003124
ch->pending_rx_mtu = mtu;
31013125

31023126
net_buf_add_le16(buf, ch->rx.cid);
@@ -3184,6 +3208,7 @@ int bt_l2cap_ecred_chan_reconfigure_explicit(struct bt_l2cap_chan **chans, size_
31843208
ch = BT_L2CAP_LE_CHAN(chans[i]);
31853209

31863210
ch->ident = ident;
3211+
ch->pending_req = BT_L2CAP_ECRED_RECONF_REQ;
31873212
ch->pending_rx_mtu = mtu;
31883213

31893214
net_buf_add_le16(buf, ch->rx.cid);
@@ -3236,6 +3261,7 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
32363261
struct net_buf *buf;
32373262
struct bt_l2cap_disconn_req *req;
32383263
struct bt_l2cap_le_chan *le_chan;
3264+
uint8_t ident;
32393265

32403266
if (!conn) {
32413267
return -ENOTCONN;
@@ -3250,14 +3276,16 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
32503276

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

3253-
le_chan->ident = get_ident();
3279+
ident = get_ident();
32543280

3255-
buf = l2cap_create_le_sig_pdu(BT_L2CAP_DISCONN_REQ,
3256-
le_chan->ident, sizeof(*req));
3281+
buf = l2cap_create_le_sig_pdu(BT_L2CAP_DISCONN_REQ, ident, sizeof(*req));
32573282
if (!buf) {
32583283
return -ENOMEM;
32593284
}
32603285

3286+
le_chan->ident = ident;
3287+
le_chan->pending_req = BT_L2CAP_DISCONN_REQ;
3288+
32613289
req = net_buf_add(buf, sizeof(*req));
32623290
req->dcid = sys_cpu_to_le16(le_chan->tx.cid);
32633291
req->scid = sys_cpu_to_le16(le_chan->rx.cid);

0 commit comments

Comments
 (0)