diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c index 92e66dd8e35..f0d09e76356 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_cc.c @@ -53,6 +53,10 @@ #include #include "hal/debug.h" +#include + +#define SUB_INTERVAL_MIN 400 + static void cc_ntf_established(struct ll_conn *conn, struct proc_ctx *ctx) { struct node_rx_conn_iso_estab *pdu; @@ -321,6 +325,82 @@ static uint8_t rp_cc_check_phy(struct ll_conn *conn, struct proc_ctx *ctx, return BT_HCI_ERR_SUCCESS; } +/* Validate that the LL_CIS_REQ parameters are valid according to the rules in + * Core Specification v5.4, Vol. 6, Part B, Section 2.4.2.29 + */ +static uint8_t rp_cc_validate_req(struct ll_conn *conn, struct proc_ctx *ctx, + struct pdu_data *pdu) +{ + uint32_t cis_offset_max; + uint32_t cis_offset_min; + uint32_t c_sdu_interval; + uint32_t p_sdu_interval; + uint32_t sub_interval; + uint16_t iso_interval; + uint16_t c_max_pdu; + uint16_t p_max_pdu; + uint8_t result; + + result = rp_cc_check_phy(conn, ctx, pdu); + if (result != BT_HCI_ERR_SUCCESS) { + return result; + } + + /* Note: SDU intervals are 20 bits; Mask away RFU bits */ + c_sdu_interval = sys_get_le24(pdu->llctrl.cis_req.c_sdu_interval) & 0x0FFFFF; + p_sdu_interval = sys_get_le24(pdu->llctrl.cis_req.p_sdu_interval) & 0x0FFFFF; + if (c_sdu_interval < BT_HCI_ISO_SDU_INTERVAL_MIN || + p_sdu_interval < BT_HCI_ISO_SDU_INTERVAL_MIN) { + return BT_HCI_ERR_INVALID_LL_PARAM; + } + + c_max_pdu = sys_le16_to_cpu(pdu->llctrl.cis_req.c_max_pdu); + p_max_pdu = sys_le16_to_cpu(pdu->llctrl.cis_req.p_max_pdu); + if (c_max_pdu > BT_ISO_PDU_MAX || p_max_pdu > BT_ISO_PDU_MAX) { + return BT_HCI_ERR_INVALID_LL_PARAM; + } + + if (!IN_RANGE(pdu->llctrl.cis_req.nse, BT_ISO_NSE_MIN, BT_ISO_NSE_MAX)) { + return BT_HCI_ERR_INVALID_LL_PARAM; + } + + iso_interval = sys_le16_to_cpu(pdu->llctrl.cis_req.iso_interval); + sub_interval = sys_get_le24(pdu->llctrl.cis_req.sub_interval); + if (pdu->llctrl.cis_req.nse == 1) { + if (sub_interval > 0) { + return BT_HCI_ERR_INVALID_LL_PARAM; + } + } else if (!IN_RANGE(sub_interval, SUB_INTERVAL_MIN, + iso_interval * CONN_INT_UNIT_US - 1)) { + return BT_HCI_ERR_INVALID_LL_PARAM; + } + + if ((pdu->llctrl.cis_req.c_bn == 0 && c_max_pdu > 0) || + (pdu->llctrl.cis_req.p_bn == 0 && p_max_pdu > 0)) { + return BT_HCI_ERR_INVALID_LL_PARAM; + } + + if (pdu->llctrl.cis_req.c_ft == 0 || pdu->llctrl.cis_req.p_ft == 0) { + return BT_HCI_ERR_INVALID_LL_PARAM; + } + + if (!IN_RANGE(iso_interval, BT_HCI_ISO_INTERVAL_MIN, BT_HCI_ISO_INTERVAL_MAX)) { + return BT_HCI_ERR_INVALID_LL_PARAM; + } + + cis_offset_min = sys_get_le24(pdu->llctrl.cis_req.cis_offset_min); + if (cis_offset_min < PDU_CIS_OFFSET_MIN_US) { + return BT_HCI_ERR_INVALID_LL_PARAM; + } + + cis_offset_max = sys_get_le24(pdu->llctrl.cis_req.cis_offset_max); + if (!IN_RANGE(cis_offset_max, cis_offset_min, conn->lll.interval * CONN_INT_UNIT_US - 1)) { + return BT_HCI_ERR_INVALID_LL_PARAM; + } + + return BT_HCI_ERR_SUCCESS; +} + static void rp_cc_state_wait_rx_cis_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param) { @@ -331,8 +411,8 @@ static void rp_cc_state_wait_rx_cis_req(struct ll_conn *conn, struct proc_ctx *c /* Handle CIS request */ llcp_pdu_decode_cis_req(ctx, pdu); - /* Check PHY */ - ctx->data.cis_create.error = rp_cc_check_phy(conn, ctx, pdu); + /* Check validity of request */ + ctx->data.cis_create.error = rp_cc_validate_req(conn, ctx, pdu); if (ctx->data.cis_create.error == BT_HCI_ERR_SUCCESS) { ctx->data.cis_create.error = diff --git a/tests/bluetooth/controller/ctrl_cis_create/src/main.c b/tests/bluetooth/controller/ctrl_cis_create/src/main.c index 67242a254bc..4e9820fb46a 100644 --- a/tests/bluetooth/controller/ctrl_cis_create/src/main.c +++ b/tests/bluetooth/controller/ctrl_cis_create/src/main.c @@ -47,6 +47,10 @@ DEFINE_FFF_GLOBALS; #include "helper_pdu.h" #include "helper_util.h" +#include + +#define SUB_INTERVAL_MIN 400 + static struct ll_conn conn; static struct ll_conn_iso_group cig_mock = { 0 }; @@ -84,11 +88,11 @@ static struct pdu_data_llctrl_cis_req remote_cis_req = { .p_ft = 1, .iso_interval = 6, .conn_event_count = 12, - .c_sdu_interval = { 0, 0, 0}, - .p_sdu_interval = { 0, 0, 0}, - .sub_interval = { 0, 0, 0}, - .cis_offset_min = { 0, 0, 0}, - .cis_offset_max = { 0, 0, 0} + .c_sdu_interval = { 0, 0x02, 0}, /* 512 us */ + .p_sdu_interval = { 0, 0x02, 0}, /* 512 us */ + .sub_interval = { 0x90, 0x01, 0x00}, /* 400 us */ + .cis_offset_min = { 0, 0x02, 0}, /* 512 us */ + .cis_offset_max = { 0, 0x02, 0} /* 512 us */ }; static struct pdu_data_llctrl_cis_ind remote_cis_ind = { @@ -169,8 +173,8 @@ ZTEST(cis_create, test_cc_create_periph_rem_host_accept) .cis_id = 0x02 }; struct pdu_data_llctrl_cis_rsp local_cis_rsp = { - .cis_offset_max = { 0, 0, 0}, - .cis_offset_min = { 0, 0, 0}, + .cis_offset_max = { 0, 0x02, 0}, + .cis_offset_min = { 0, 0x02, 0}, .conn_event_count = 12 }; struct node_rx_conn_iso_estab cis_estab = { @@ -525,6 +529,196 @@ ZTEST(cis_create, test_cc_create_periph_rem_invalid_phy) "Free CTX buffers %d", llcp_ctx_buffers_free()); } +static void rx_remote_cis_req_invalid_param(struct pdu_data_llctrl_cis_req *req) +{ + struct node_tx *tx; + struct pdu_data_llctrl_reject_ext_ind local_reject = { + .error_code = BT_HCI_ERR_INVALID_LL_PARAM, + .reject_opcode = PDU_DATA_LLCTRL_TYPE_CIS_REQ + }; + + test_setup(&conn); + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + lt_tx(LL_CIS_REQ, &conn, req); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_REJECT_EXT_IND, &conn, &tx, &local_reject); + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", llcp_ctx_buffers_free()); +} + +/* + * Central-initiated CIS Create procedure. + * Central requests CIS w. invalid param, peripheral Host rejects. + * + * +-----+ +-------+ +-----+ + * | UT | | LL_S | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_CIS_REQ (invalid param) | + * | |<------------------------------| + * | | | + * | | | + * | | | + * | | | + * | | | + * | | | + * | | LL_REJECT_EXT_IND | + * | |------------------------------>| + * | | | + */ +ZTEST(cis_create, test_cc_create_periph_rem_invalid_param) +{ + struct pdu_data_llctrl_cis_req remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with c_sdu_interval < 255 */ + sys_put_le24(BT_HCI_ISO_SDU_INTERVAL_MIN-1, remote_cis_req_invalid_param.c_sdu_interval); + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with p_sdu_interval < 255 */ + sys_put_le24(BT_HCI_ISO_SDU_INTERVAL_MIN-1, remote_cis_req_invalid_param.p_sdu_interval); + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with c_max_pdu > 251 */ + remote_cis_req_invalid_param.c_max_pdu = sys_cpu_to_le16(BT_ISO_PDU_MAX + 1); + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with p_max_pdu > 251 */ + remote_cis_req_invalid_param.p_max_pdu = sys_cpu_to_le16(BT_ISO_PDU_MAX + 1); + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with nse == 0 */ + remote_cis_req_invalid_param.nse = 0; + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with nse > 31 */ + remote_cis_req_invalid_param.nse = BT_ISO_NSE_MAX + 1; + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with sub_interval > 0 && nse == 1 */ + remote_cis_req_invalid_param.nse = 1; + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with sub_interval < 400 && nse > 1 */ + sys_put_le24(SUB_INTERVAL_MIN - 1, remote_cis_req_invalid_param.sub_interval); + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with sub_interval >= iso_interval */ + sys_put_le24(sys_le16_to_cpu(remote_cis_req_invalid_param.iso_interval) * CONN_INT_UNIT_US, + remote_cis_req_invalid_param.sub_interval); + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + + /* Rx with c_bn == 0 && c_max_pdu > 0 */ + remote_cis_req_invalid_param.c_bn = 0; + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with p_bn == 0 && p_max_pdu > 0 */ + remote_cis_req_invalid_param.p_bn = 0; + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with c_ft == 0 */ + remote_cis_req_invalid_param.c_ft = 0; + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with p_ft == 0 */ + remote_cis_req_invalid_param.p_ft = 0; + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with iso_interval < 4 */ + remote_cis_req_invalid_param.iso_interval = sys_cpu_to_le16(BT_HCI_ISO_INTERVAL_MIN - 1); + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with iso_interval > 3200 */ + remote_cis_req_invalid_param.iso_interval = sys_cpu_to_le16(BT_HCI_ISO_INTERVAL_MAX + 1); + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with cis_offset_min < 500 */ + sys_put_le24(PDU_CIS_OFFSET_MIN_US - 1, remote_cis_req_invalid_param.cis_offset_min); + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with cis_offset_max < cis_offset_min */ + sys_put_le24(0x01FF, remote_cis_req_invalid_param.cis_offset_max); + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); + + /* Restore to valid params */ + remote_cis_req_invalid_param = remote_cis_req; + + /* Rx with cis_offset_max >= conn_interval */ + sys_put_le24(conn.lll.interval * CONN_INT_UNIT_US, + remote_cis_req_invalid_param.cis_offset_max); + rx_remote_cis_req_invalid_param(&remote_cis_req_invalid_param); +} + /* * Central-initiated CIS Create procedure. * Host requests CIS, LL replies with 'remote feature unsupported'