Commit 8fac8dfc authored by Vinayak Kariappa Chettimada's avatar Vinayak Kariappa Chettimada Committed by Anas Nashif
Browse files

Bluetooth: controller: Fix handling of invalid Ctrl PDU lengths


Fix the controller implementation to handle Control PDUs
with invalid lengths by responding with Unknown Response
PDU.

Fixes LL.TS.5.0.2 conformance tests:
LL/PAC/SLA/BI-01-C [Control PDUs with Invalid Length from
                    Master]
LL/PAC/MAS/BI-01-C [Control PDUs with Invalid Length from
                    Slave]
Signed-off-by: default avatarVinayak Kariappa Chettimada <vich@nordicsemi.no>
parent cce8bf55
Showing with 184 additions and 3 deletions
+184 -3
......@@ -2365,6 +2365,66 @@ send_length_resp:
}
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
static inline bool pdu_len_cmp(u8_t opcode, u8_t len)
{
const u8_t ctrl_len_lut[] = {
(offsetof(struct pdu_data_llctrl, ctrldata.conn_update_ind) +
sizeof(struct pdu_data_llctrl_conn_update_ind)),
(offsetof(struct pdu_data_llctrl, ctrldata.chan_map_ind) +
sizeof(struct pdu_data_llctrl_chan_map_ind)),
(offsetof(struct pdu_data_llctrl, ctrldata.terminate_ind) +
sizeof(struct pdu_data_llctrl_terminate_ind)),
(offsetof(struct pdu_data_llctrl, ctrldata.enc_req) +
sizeof(struct pdu_data_llctrl_enc_req)),
(offsetof(struct pdu_data_llctrl, ctrldata.enc_rsp) +
sizeof(struct pdu_data_llctrl_enc_rsp)),
(offsetof(struct pdu_data_llctrl, ctrldata.start_enc_req) +
sizeof(struct pdu_data_llctrl_start_enc_req)),
(offsetof(struct pdu_data_llctrl, ctrldata.start_enc_rsp) +
sizeof(struct pdu_data_llctrl_start_enc_rsp)),
(offsetof(struct pdu_data_llctrl, ctrldata.unknown_rsp) +
sizeof(struct pdu_data_llctrl_unknown_rsp)),
(offsetof(struct pdu_data_llctrl, ctrldata.feature_req) +
sizeof(struct pdu_data_llctrl_feature_req)),
(offsetof(struct pdu_data_llctrl, ctrldata.feature_rsp) +
sizeof(struct pdu_data_llctrl_feature_rsp)),
(offsetof(struct pdu_data_llctrl, ctrldata.pause_enc_req) +
sizeof(struct pdu_data_llctrl_pause_enc_req)),
(offsetof(struct pdu_data_llctrl, ctrldata.pause_enc_rsp) +
sizeof(struct pdu_data_llctrl_pause_enc_rsp)),
(offsetof(struct pdu_data_llctrl, ctrldata.version_ind) +
sizeof(struct pdu_data_llctrl_version_ind)),
(offsetof(struct pdu_data_llctrl, ctrldata.reject_ind) +
sizeof(struct pdu_data_llctrl_reject_ind)),
(offsetof(struct pdu_data_llctrl, ctrldata.slave_feature_req) +
sizeof(struct pdu_data_llctrl_slave_feature_req)),
(offsetof(struct pdu_data_llctrl, ctrldata.conn_param_req) +
sizeof(struct pdu_data_llctrl_conn_param_req)),
(offsetof(struct pdu_data_llctrl, ctrldata.conn_param_rsp) +
sizeof(struct pdu_data_llctrl_conn_param_rsp)),
(offsetof(struct pdu_data_llctrl, ctrldata.reject_ext_ind) +
sizeof(struct pdu_data_llctrl_reject_ext_ind)),
(offsetof(struct pdu_data_llctrl, ctrldata.ping_req) +
sizeof(struct pdu_data_llctrl_ping_req)),
(offsetof(struct pdu_data_llctrl, ctrldata.ping_rsp) +
sizeof(struct pdu_data_llctrl_ping_rsp)),
(offsetof(struct pdu_data_llctrl, ctrldata.length_req) +
sizeof(struct pdu_data_llctrl_length_req)),
(offsetof(struct pdu_data_llctrl, ctrldata.length_rsp) +
sizeof(struct pdu_data_llctrl_length_rsp)),
(offsetof(struct pdu_data_llctrl, ctrldata.phy_req) +
sizeof(struct pdu_data_llctrl_phy_req)),
(offsetof(struct pdu_data_llctrl, ctrldata.phy_rsp) +
sizeof(struct pdu_data_llctrl_phy_rsp)),
(offsetof(struct pdu_data_llctrl, ctrldata.phy_upd_ind) +
sizeof(struct pdu_data_llctrl_phy_upd_ind)),
(offsetof(struct pdu_data_llctrl, ctrldata.min_used_chans_ind) +
sizeof(struct pdu_data_llctrl_min_used_chans_ind)),
};
return ctrl_len_lut[opcode] == len;
}
static inline u8_t
isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
u8_t *rx_enqueue)
......@@ -2375,6 +2435,9 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data;
/* Invalid packet */
/* TODO: Move into individual switch-case for better conditional
* compilation and efficiency by reuse of switch-case construct.
*/
if (_radio.conn_curr->role) {
/* Slave */
switch (pdu_data_rx->payload.llctrl.opcode) {
......@@ -2410,6 +2473,11 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
switch (pdu_data_rx->payload.llctrl.opcode) {
case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
if (conn_update(_radio.conn_curr, pdu_data_rx) == 0) {
/* conn param req procedure, if any, is complete */
_radio.conn_curr->procedure_expire = 0;
......@@ -2419,12 +2487,22 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
break;
case PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
if (chan_map_update(_radio.conn_curr, pdu_data_rx)) {
_radio.conn_curr->llcp_terminate.reason_peer = 0x28;
}
break;
case PDU_DATA_LLCTRL_TYPE_TERMINATE_IND:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_TERMINATE_IND,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
/* Ack and then terminate */
_radio.conn_curr->llcp_terminate.reason_peer =
pdu_data_rx->payload.llctrl.ctrldata.terminate_ind.error_code;
......@@ -2432,6 +2510,10 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
#if defined(CONFIG_BT_CTLR_LE_ENC)
case PDU_DATA_LLCTRL_TYPE_ENC_REQ:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_ENC_REQ,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
#if defined(CONFIG_BT_CTLR_FAST_ENC)
/* TODO: BT Spec. text: may finalize the sending of additional
......@@ -2464,6 +2546,11 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
break;
case PDU_DATA_LLCTRL_TYPE_ENC_RSP:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_ENC_RSP,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
/* things sent by slave stored for session key calculation */
memcpy(&_radio.conn_curr->llcp.encryption.skd[8],
&pdu_data_rx->payload.llctrl.ctrldata.enc_rsp.skds[0],
......@@ -2481,6 +2568,11 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
_radio.conn_curr->llcp_ack) ||
(_radio.conn_curr->llcp_type == LLCP_ENCRYPTION));
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_START_ENC_REQ,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
/* start enc rsp to be scheduled in master prepare */
_radio.conn_curr->llcp.encryption.initiate = 0;
_radio.conn_curr->llcp_type = LLCP_ENCRYPTION;
......@@ -2488,6 +2580,11 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
break;
case PDU_DATA_LLCTRL_TYPE_START_ENC_RSP:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_START_ENC_RSP,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
if (_radio.role == ROLE_SLAVE) {
#if !defined(CONFIG_BT_CTLR_FAST_ENC)
LL_ASSERT((_radio.conn_curr->llcp_req ==
......@@ -2532,15 +2629,23 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
case PDU_DATA_LLCTRL_TYPE_FEATURE_REQ:
case PDU_DATA_LLCTRL_TYPE_SLAVE_FEATURE_REQ:
{
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_FEATURE_REQ,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
nack = feature_rsp_send(_radio.conn_curr, pdu_data_rx);
}
break;
break;
case PDU_DATA_LLCTRL_TYPE_FEATURE_RSP:
{
struct pdu_data_llctrl_feature_rsp *rsp;
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_FEATURE_RSP,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
rsp = &pdu_data_rx->payload.llctrl.ctrldata.feature_rsp;
/* AND the feature set to get Feature USED */
......@@ -2559,21 +2664,41 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
#if defined(CONFIG_BT_CTLR_LE_ENC)
case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
nack = pause_enc_rsp_send(_radio.conn_curr, 1);
break;
case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
nack = pause_enc_rsp_send(_radio.conn_curr, 0);
break;
#endif /* CONFIG_BT_CTLR_LE_ENC */
case PDU_DATA_LLCTRL_TYPE_VERSION_IND:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_VERSION_IND,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
nack = version_ind_send(_radio.conn_curr, pdu_data_rx,
rx_enqueue);
break;
#if defined(CONFIG_BT_CTLR_LE_ENC)
case PDU_DATA_LLCTRL_TYPE_REJECT_IND:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_REJECT_IND,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
/* resume data packet rx and tx */
_radio.conn_curr->pause_rx = 0;
_radio.conn_curr->pause_tx = 0;
......@@ -2588,6 +2713,11 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
/* check CUI/CPR mutex for other connections having CPR in
* progress.
......@@ -2788,6 +2918,11 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
break;
case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
if (!_radio.conn_curr->role &&
(_radio.conn_curr->llcp_conn_param.req !=
_radio.conn_curr->llcp_conn_param.ack) &&
......@@ -2838,21 +2973,41 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
isr_rx_conn_pkt_ctrl_rej(radio_pdu_node_rx, rx_enqueue);
break;
#if defined(CONFIG_BT_CTLR_LE_PING)
case PDU_DATA_LLCTRL_TYPE_PING_REQ:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_PING_REQ,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
nack = ping_resp_send(_radio.conn_curr);
break;
case PDU_DATA_LLCTRL_TYPE_PING_RSP:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_PING_RSP,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
/* Procedure complete */
_radio.conn_curr->procedure_expire = 0;
break;
#endif /* CONFIG_BT_CTLR_LE_PING */
case PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
if (0) {
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
} else if (_radio.conn_curr->llcp_conn_param.ack !=
......@@ -2991,12 +3146,22 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
case PDU_DATA_LLCTRL_TYPE_LENGTH_RSP:
case PDU_DATA_LLCTRL_TYPE_LENGTH_REQ:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_LENGTH_REQ,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
nack = isr_rx_conn_pkt_ctrl_dle(pdu_data_rx, rx_enqueue);
break;
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_PHY)
case PDU_DATA_LLCTRL_TYPE_PHY_REQ:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_PHY_REQ,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
if (_radio.role == ROLE_MASTER) {
if ((_radio.conn_curr->llcp_phy.ack !=
_radio.conn_curr->llcp_phy.req) &&
......@@ -3069,6 +3234,11 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
break;
case PDU_DATA_LLCTRL_TYPE_PHY_RSP:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_PHY_RSP,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
if ((_radio.role == ROLE_MASTER) &&
(_radio.conn_curr->llcp_phy.ack !=
_radio.conn_curr->llcp_phy.req) &&
......@@ -3088,6 +3258,11 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
break;
case PDU_DATA_LLCTRL_TYPE_PHY_UPD_IND:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_PHY_UPD_IND,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
if (phy_upd_ind(radio_pdu_node_rx, rx_enqueue)) {
_radio.conn_curr->llcp_terminate.reason_peer = 0x28;
}
......@@ -3096,6 +3271,11 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN)
case PDU_DATA_LLCTRL_TYPE_MIN_USED_CHAN_IND:
if (!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_MIN_USED_CHAN_IND,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
if (!_radio.conn_curr->role) {
struct pdu_data_llctrl_min_used_chans_ind *p =
&pdu_data_rx->payload.llctrl.ctrldata.min_used_chans_ind;
......@@ -3126,6 +3306,7 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
#endif /* CONFIG_BT_MIN_USED_CHAN */
default:
isr_rx_conn_unknown_rsp_send:
nack = unknown_rsp_send(_radio.conn_curr,
pdu_data_rx->payload.llctrl.opcode);
break;
......
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