Bluetooth: Controller: Add validation of received LL_CIS_REQ

Validate that a received LL_CIS_REQ is valid and reject if it is not

Fixes EBQ test failure in LL/CIS/PER/BI-07-C

Signed-off-by: Troels Nilsson <trnn@demant.com>
This commit is contained in:
Troels Nilsson 2025-04-08 10:36:26 +02:00 committed by Benjamin Cabé
parent 070bd0e295
commit c3df8fcd92
2 changed files with 283 additions and 9 deletions

View File

@ -53,6 +53,10 @@
#include <soc.h>
#include "hal/debug.h"
#include <zephyr/bluetooth/iso.h>
#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 =

View File

@ -47,6 +47,10 @@ DEFINE_FFF_GLOBALS;
#include "helper_pdu.h"
#include "helper_util.h"
#include <zephyr/bluetooth/iso.h>
#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'