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
Loading
Loading
Loading
Loading
+184 −3
Original line number Diff line number Diff line
@@ -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:
	{
		nack = feature_rsp_send(_radio.conn_curr, pdu_data_rx);
		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;

	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;