From a7cf34f558dc4418866125795d8830d41cbdbd57 Mon Sep 17 00:00:00 2001 From: Morten Priess Date: Mon, 6 May 2024 16:08:42 +0200 Subject: [PATCH 1/8] Bluetooth: controller: Prevent invalid compiler code reordering In ull_disable, it is imperative that the callback is set up before a second reference counter check, otherwise it may happen that an LLL done event has already passed when the disable callback and semaphore is assigned. This causes the HCI thread to wait until timeout and assert after ull_ticker_stop_with_mark. For certain compilers, due to compiler optimizations, it can be seen from the assembler code that the callback is assigned after the second reference counter check. By adding memory barriers, the code correctly reorders code to the expected sequence. Signed-off-by: Morten Priess (cherry picked from commit 7f82b6a2199f16892701f7df70130e3c6ea4b3a5) Signed-off-by: Vinayak Kariappa Chettimada --- subsys/bluetooth/controller/ll_sw/ull.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/subsys/bluetooth/controller/ll_sw/ull.c b/subsys/bluetooth/controller/ll_sw/ull.c index 3130978df4ebe6..396a962dfc17da 100644 --- a/subsys/bluetooth/controller/ll_sw/ull.c +++ b/subsys/bluetooth/controller/ll_sw/ull.c @@ -1951,12 +1951,15 @@ int ull_disable(void *lll) if (!ull_ref_get(hdr)) { return -EALREADY; } + cpu_dmb(); /* Ensure synchronized data access */ k_sem_init(&sem, 0, 1); hdr->disabled_param = &sem; hdr->disabled_cb = disabled_cb; + cpu_dmb(); /* Ensure synchronized data access */ + /* ULL_HIGH can run after we have call `ull_ref_get` and it can * decrement the ref count. Hence, handle this race condition by * ensuring that `disabled_cb` has been set while the ref count is still From a85ee2c3a2c5531c5cfaf3f56f7a77180a85bf87 Mon Sep 17 00:00:00 2001 From: Erik Brockhoff Date: Wed, 15 May 2024 13:54:05 +0200 Subject: [PATCH 2/8] Bluetooth: controller: fixing rx node leak on CPR reject of parallel CPR In case a CPR is intiated but rejected due to CPR active on other connection, rx nodes are leaked due to retained node not being properly released. Signed-off-by: Erik Brockhoff (cherry picked from commit edef1b7cf45ab8b67ff09ff38cc71f0b922d9a6f) Signed-off-by: Vinayak Kariappa Chettimada --- subsys/bluetooth/controller/ll_sw/ull_llcp.c | 16 +++++++++++++--- .../bluetooth/controller/ll_sw/ull_llcp_common.c | 1 + .../controller/ll_sw/ull_llcp_conn_upd.c | 16 ++++++++++++---- subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c | 1 + .../controller/ll_sw/ull_llcp_internal.h | 1 + .../bluetooth/controller/ll_sw/ull_llcp_local.c | 6 ++++++ subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c | 1 + .../bluetooth/controller/ll_sw/ull_llcp_remote.c | 6 ++++++ 8 files changed, 41 insertions(+), 7 deletions(-) diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp.c b/subsys/bluetooth/controller/ll_sw/ull_llcp.c index 2b93c17a201b42..a41049f100cd77 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp.c @@ -291,17 +291,30 @@ void llcp_rx_node_retain(struct proc_ctx *ctx) ctx->node_ref.rx->hdr.link = ctx->node_ref.link; } +void llcp_rx_node_release(struct proc_ctx *ctx) +{ + LL_ASSERT(ctx->node_ref.rx); + + if (ctx->node_ref.rx->hdr.type == NODE_RX_TYPE_RETAIN) { + /* Mark RX node to release and release */ + ctx->node_ref.rx->hdr.type = NODE_RX_TYPE_RELEASE; + ll_rx_put_sched(ctx->node_ref.rx->hdr.link, ctx->node_ref.rx); + } +} + void llcp_nodes_release(struct ll_conn *conn, struct proc_ctx *ctx) { if (ctx->node_ref.rx && ctx->node_ref.rx->hdr.type == NODE_RX_TYPE_RETAIN) { /* RX node retained, so release */ ctx->node_ref.rx->hdr.link->mem = conn->llcp.rx_node_release; + ctx->node_ref.rx->hdr.type = NODE_RX_TYPE_RELEASE; conn->llcp.rx_node_release = ctx->node_ref.rx; } #if defined(CONFIG_BT_CTLR_PHY) && defined(CONFIG_BT_CTLR_DATA_LENGTH) if (ctx->proc == PROC_PHY_UPDATE && ctx->data.pu.ntf_dle_node) { /* RX node retained, so release */ ctx->data.pu.ntf_dle_node->hdr.link->mem = conn->llcp.rx_node_release; + ctx->data.pu.ntf_dle_node->hdr.type = NODE_RX_TYPE_RELEASE; conn->llcp.rx_node_release = ctx->data.pu.ntf_dle_node; } #endif @@ -706,9 +719,6 @@ void ull_cp_release_nodes(struct ll_conn *conn) hdr = &rx->hdr; rx = hdr->link->mem; - /* Mark for buffer for release */ - hdr->type = NODE_RX_TYPE_RELEASE; - /* enqueue rx node towards Thread */ ll_rx_put(hdr->link, hdr); } diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c index b4cfb881f3fcd3..e9f5cf9c4d4cfb 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c @@ -1135,6 +1135,7 @@ static void rp_comm_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t gene /* Allocate ntf node */ ntf = ctx->node_ref.rx; + ctx->node_ref.rx = NULL; LL_ASSERT(ntf); /* This should be an 'old' RX node, so put/sched when done */ diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c index e922901f67d06b..381f5d3ae09d58 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c @@ -633,8 +633,7 @@ static void lp_cu_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint lp_cu_ntf_complete(conn, ctx, evt, param); } else { /* Release RX node kept for NTF */ - ctx->node_ref.rx->hdr.type = NODE_RX_TYPE_RELEASE; - ll_rx_put_sched(ctx->node_ref.rx->hdr.link, ctx->node_ref.rx); + llcp_rx_node_release(ctx); ctx->node_ref.rx = NULL; lp_cu_complete(conn, ctx); @@ -973,11 +972,18 @@ static void rp_cu_st_wait_conn_param_req_available(struct ll_conn *conn, struct case RP_CU_EVT_RUN: if (cpr_active_is_set(conn)) { ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_AVAILABLE; + if (!llcp_rr_ispaused(conn) && llcp_tx_alloc_peek(conn, ctx)) { /* We're good to reject immediately */ ctx->data.cu.rejected_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ; ctx->data.cu.error = BT_HCI_ERR_UNSUPP_LL_PARAM_VAL; rp_cu_send_reject_ext_ind(conn, ctx, evt, param); + + /* Possibly retained rx node to be released as we won't need it */ + llcp_rx_node_release(ctx); + ctx->node_ref.rx = NULL; + + break; } /* In case we have to defer NTF */ llcp_rx_node_retain(ctx); @@ -992,6 +998,9 @@ static void rp_cu_st_wait_conn_param_req_available(struct ll_conn *conn, struct rp_cu_conn_param_req_ntf(conn, ctx); ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY; } else { + /* Possibly retained rx node to be released as we won't need it */ + llcp_rx_node_release(ctx); + ctx->node_ref.rx = NULL; #if defined(CONFIG_BT_CTLR_USER_CPR_ANCHOR_POINT_MOVE) /* Handle APM as a vendor specific user extension */ if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL && @@ -1177,8 +1186,7 @@ static void rp_cu_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint cu_ntf(conn, ctx); } else { /* Release RX node kept for NTF */ - ctx->node_ref.rx->hdr.type = NODE_RX_TYPE_RELEASE; - ll_rx_put_sched(ctx->node_ref.rx->hdr.link, ctx->node_ref.rx); + llcp_rx_node_release(ctx); ctx->node_ref.rx = NULL; } rp_cu_complete(conn, ctx); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c index 10fc39d86a54b6..6955b2f7942c15 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c @@ -225,6 +225,7 @@ static void lp_enc_ntf(struct ll_conn *conn, struct proc_ctx *ctx) /* Piggy-back on RX node */ ntf = ctx->node_ref.rx; + ctx->node_ref.rx = NULL; LL_ASSERT(ntf); ntf->hdr.type = NODE_RX_TYPE_DC_PDU; diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h index 0e1afb9ff1d46c..d81ab9d6513f04 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h @@ -413,6 +413,7 @@ void llcp_ntf_set_pending(struct ll_conn *conn); void llcp_ntf_clear_pending(struct ll_conn *conn); bool llcp_ntf_pending(struct ll_conn *conn); void llcp_rx_node_retain(struct proc_ctx *ctx); +void llcp_rx_node_release(struct proc_ctx *ctx); /* * ULL -> LLL Interface diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c index 793e33934bce0a..f2a21b2f4f8b66 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c @@ -81,6 +81,12 @@ void llcp_lr_check_done(struct ll_conn *conn, struct proc_ctx *ctx) ctx_header = llcp_lr_peek(conn); LL_ASSERT(ctx_header == ctx); + /* If we have a node rx it must not be marked RETAIN as + * the memory referenced would leak + */ + LL_ASSERT(ctx->node_ref.rx == NULL || + ctx->node_ref.rx->hdr.type != NODE_RX_TYPE_RETAIN); + lr_dequeue(conn); llcp_proc_ctx_release(ctx); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c index 31fc053f8494a5..22a4fcf7423612 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c @@ -433,6 +433,7 @@ static void pu_ntf(struct ll_conn *conn, struct proc_ctx *ctx) /* Piggy-back on stored RX node */ ntf = ctx->node_ref.rx; + ctx->node_ref.rx = NULL; LL_ASSERT(ntf); if (ctx->data.pu.ntf_pu) { diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c index cb4f09956881c8..93a382be0e7cee 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c @@ -118,6 +118,12 @@ void llcp_rr_check_done(struct ll_conn *conn, struct proc_ctx *ctx) ctx_header = llcp_rr_peek(conn); LL_ASSERT(ctx_header == ctx); + /* If we have a node rx it must not be marked RETAIN as + * the memory referenced would leak + */ + LL_ASSERT(ctx->node_ref.rx == NULL || + ctx->node_ref.rx->hdr.type != NODE_RX_TYPE_RETAIN); + rr_dequeue(conn); llcp_proc_ctx_release(ctx); From 8d8b727516588728e783cc30e1f1e25c15b80dcb Mon Sep 17 00:00:00 2001 From: Erik Brockhoff Date: Wed, 15 May 2024 13:56:02 +0200 Subject: [PATCH 3/8] Bluetooth: controller: fix node_rx retention mechanism Ensure that in LLCP reference to node_rx is cleared when retention is NOT used, to avoid corruption of node_rx later re-allocated Signed-off-by: Erik Brockhoff (cherry picked from commit 806a4fcf92ae882c299d23de9d219b8f887e6619) Signed-off-by: Vinayak Kariappa Chettimada --- subsys/bluetooth/controller/ll_sw/ull_llcp_local.c | 5 +++++ subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c index f2a21b2f4f8b66..249943b10b2af7 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c @@ -318,6 +318,11 @@ void llcp_lr_rx(struct ll_conn *conn, struct proc_ctx *ctx, memq_link_t *link, break; } + /* If rx node was not retained clear reference */ + if (ctx->node_ref.rx && ctx->node_ref.rx->hdr.type != NODE_RX_TYPE_RETAIN) { + ctx->node_ref.rx = NULL; + } + llcp_lr_check_done(conn, ctx); } diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c index 93a382be0e7cee..74b33727a36bac 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c @@ -313,6 +313,12 @@ void llcp_rr_rx(struct ll_conn *conn, struct proc_ctx *ctx, memq_link_t *link, LL_ASSERT(0); break; } + + /* If rx node was not retained clear reference */ + if (ctx->node_ref.rx && ctx->node_ref.rx->hdr.type != NODE_RX_TYPE_RETAIN) { + ctx->node_ref.rx = NULL; + } + llcp_rr_check_done(conn, ctx); } From ebeb1fdeebe8ad17b752f5c3de19d0f6847cdd1b Mon Sep 17 00:00:00 2001 From: Erik Brockhoff Date: Wed, 15 May 2024 13:57:40 +0200 Subject: [PATCH 4/8] Bluetooth: controller: minor cleanup and a fix-up re. LLCP Only perform retention if not already done. Ensure 'sched' is performed on phy ntf even if dle is not. Signed-off-by: Erik Brockhoff (cherry picked from commit 9d8059b6e5541f7f6b4eb1697dccd475f2c1b39b) Signed-off-by: Vinayak Kariappa Chettimada --- subsys/bluetooth/controller/ll_sw/ull_llcp.c | 12 ++++++++---- subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c | 6 ------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp.c b/subsys/bluetooth/controller/ll_sw/ull_llcp.c index a41049f100cd77..efe26add8bf7f8 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp.c @@ -284,17 +284,21 @@ void llcp_rx_node_retain(struct proc_ctx *ctx) { LL_ASSERT(ctx->node_ref.rx); - /* Mark RX node to NOT release */ - ctx->node_ref.rx->hdr.type = NODE_RX_TYPE_RETAIN; + /* Only retain if not already retained */ + if (ctx->node_ref.rx->hdr.type != NODE_RX_TYPE_RETAIN) { + /* Mark RX node to NOT release */ + ctx->node_ref.rx->hdr.type = NODE_RX_TYPE_RETAIN; - /* store link element reference to use once this node is moved up */ - ctx->node_ref.rx->hdr.link = ctx->node_ref.link; + /* store link element reference to use once this node is moved up */ + ctx->node_ref.rx->hdr.link = ctx->node_ref.link; + } } void llcp_rx_node_release(struct proc_ctx *ctx) { LL_ASSERT(ctx->node_ref.rx); + /* Only release if retained */ if (ctx->node_ref.rx->hdr.type == NODE_RX_TYPE_RETAIN) { /* Mark RX node to release and release */ ctx->node_ref.rx->hdr.type = NODE_RX_TYPE_RELEASE; diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c index 22a4fcf7423612..7f5b20fd1de49b 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c @@ -450,15 +450,9 @@ static void pu_ntf(struct ll_conn *conn, struct proc_ctx *ctx) } /* Enqueue notification towards LL */ -#if defined(CONFIG_BT_CTLR_DATA_LENGTH) - /* only 'put' as the 'sched' is handled when handling DLE ntf */ - ll_rx_put(ntf->hdr.link, ntf); -#else ll_rx_put_sched(ntf->hdr.link, ntf); -#endif /* CONFIG_BT_CTLR_DATA_LENGTH */ ctx->data.pu.ntf_pu = 0; - ctx->node_ref.rx = NULL; } #if defined(CONFIG_BT_CTLR_DATA_LENGTH) From 7840a33dfd86079578859cecbb4b27d0e3f24290 Mon Sep 17 00:00:00 2001 From: Vinayak Kariappa Chettimada Date: Sat, 11 May 2024 06:35:19 +0200 Subject: [PATCH 5/8] Bluetooth: Controller: Fix missing conn update ind PDU validation Fix missing validation of Connection Update Ind PDU. Ignore invalid connection update parameters and force a silent local connection termination. Signed-off-by: Vinayak Kariappa Chettimada (cherry picked from commit 4b6d3f1e16ea8ccccbf85f1eaf7efd0caea11766) Signed-off-by: Vinayak Kariappa Chettimada --- .../controller/ll_sw/ull_llcp_conn_upd.c | 54 ++++++-- .../controller/ctrl_conn_update/src/main.c | 124 ++++++++++++++++++ 2 files changed, 169 insertions(+), 9 deletions(-) diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c index 381f5d3ae09d58..4d20aeed808718 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c @@ -196,6 +196,22 @@ static bool cu_check_conn_parameters(struct ll_conn *conn, struct proc_ctx *ctx) } #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ +static bool cu_check_conn_ind_parameters(struct ll_conn *conn, struct proc_ctx *ctx) +{ + const uint16_t interval_max = ctx->data.cu.interval_max; /* unit 1.25ms */ + const uint16_t timeout = ctx->data.cu.timeout; /* unit 10ms */ + const uint16_t latency = ctx->data.cu.latency; + + /* Valid conn_update_ind parameters */ + return (interval_max >= CONN_INTERVAL_MIN(conn)) && + (interval_max <= CONN_UPDATE_CONN_INTV_4SEC) && + (latency <= CONN_UPDATE_LATENCY_MAX) && + (timeout >= CONN_UPDATE_TIMEOUT_100MS) && + (timeout <= CONN_UPDATE_TIMEOUT_32SEC) && + ((timeout * 4U) > /* *4U re. conn events is equivalent to *2U re. ms */ + ((latency + 1U) * interval_max)); +} + static void cu_prepare_update_ind(struct ll_conn *conn, struct proc_ctx *ctx) { ctx->data.cu.win_size = 1U; @@ -585,8 +601,20 @@ static void lp_cu_st_wait_rx_conn_update_ind(struct ll_conn *conn, struct proc_c switch (evt) { case LP_CU_EVT_CONN_UPDATE_IND: llcp_pdu_decode_conn_update_ind(ctx, param); + + /* Invalid PDU, mark the connection for termination */ + if (!cu_check_conn_ind_parameters(conn, ctx)) { + llcp_rr_set_incompat(conn, INCOMPAT_NO_COLLISION); + conn->llcp_terminate.reason_final = BT_HCI_ERR_INVALID_LL_PARAM; + lp_cu_complete(conn, ctx); + break; + } + + llcp_rr_set_incompat(conn, INCOMPAT_RESERVED); + /* Keep RX node to use for NTF */ llcp_rx_node_retain(ctx); + ctx->state = LP_CU_STATE_WAIT_INSTANT; break; case LP_CU_EVT_UNKNOWN: @@ -1206,19 +1234,27 @@ static void rp_cu_st_wait_rx_conn_update_ind(struct ll_conn *conn, struct proc_c case BT_HCI_ROLE_PERIPHERAL: llcp_pdu_decode_conn_update_ind(ctx, param); - if (is_instant_not_passed(ctx->data.cu.instant, - ull_conn_event_counter(conn))) { + /* Valid PDU */ + if (cu_check_conn_ind_parameters(conn, ctx)) { + if (is_instant_not_passed(ctx->data.cu.instant, + ull_conn_event_counter(conn))) { + /* Keep RX node to use for NTF */ + llcp_rx_node_retain(ctx); - llcp_rx_node_retain(ctx); + ctx->state = RP_CU_STATE_WAIT_INSTANT; + + /* In case we only just received it in time */ + rp_cu_check_instant(conn, ctx, evt, param); + break; + } - ctx->state = RP_CU_STATE_WAIT_INSTANT; - /* In case we only just received it in time */ - rp_cu_check_instant(conn, ctx, evt, param); - } else { conn->llcp_terminate.reason_final = BT_HCI_ERR_INSTANT_PASSED; - llcp_rr_complete(conn); - ctx->state = RP_CU_STATE_IDLE; + } else { + conn->llcp_terminate.reason_final = BT_HCI_ERR_INVALID_LL_PARAM; } + + llcp_rr_complete(conn); + ctx->state = RP_CU_STATE_IDLE; break; default: /* Unknown role */ diff --git a/tests/bluetooth/controller/ctrl_conn_update/src/main.c b/tests/bluetooth/controller/ctrl_conn_update/src/main.c index 9e0b07227f13c0..4871491dac8f86 100644 --- a/tests/bluetooth/controller/ctrl_conn_update/src/main.c +++ b/tests/bluetooth/controller/ctrl_conn_update/src/main.c @@ -4593,6 +4593,128 @@ ZTEST(periph_loc_no_param_req, test_conn_update_periph_loc_disallowed_no_param_r } #endif +/* + * Central-initiated Connection Update procedure. + * Peripheral receives invalid Connection Update parameters. + * + * +-----+ +-------+ +-----+ + * | UT | | LL_P | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_CONNECTION_UPDATE_IND | + * | |<--------------------------| + * | | | + * ~~~~~~~~~~~~~~~~~~ TERMINATE CONNECTION ~~~~~~~~~~~~~~~~~ + * | | | + */ +ZTEST(periph_rem_invalid, test_conn_update_periph_rem_invalid_param) +{ + uint16_t interval; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + interval = conn_update_ind.interval; + conn_update_ind.interval = 0U; + conn_update_ind.instant = event_counter(&conn) + 6U; + lt_tx(LL_CONNECTION_UPDATE_IND, &conn, &conn_update_ind); + + /* Done */ + event_done(&conn); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_INVALID_LL_PARAM, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* Clear termination flag for subsequent test cycle */ + conn.llcp_terminate.reason_final = 0; + + /* Restore interval for other tests */ + conn_update_ind.interval = interval; +} + +#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) +/* + * Peripheral-initiated Connection Parameters Request procedure. + * Peripheral requests change in LE connection parameters, central’s Host accepts. + * Peripheral receives invalid Connection Update parameters. + * + * +-----+ +-------+ +-----+ + * | UT | | LL_P | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | LE Connection Update | | + * |-------------------------->| | + * | | LL_CONNECTION_PARAM_REQ | + * | |-------------------------->| + * | | | + * | | LL_CONNECTION_UPDATE_IND | + * | |<--------------------------| + * | | | + * ~~~~~~~~~~~~~~~~~~ TERMINATE CONNECTION ~~~~~~~~~~~~~~~~~ + * | | | + */ +ZTEST(periph_rem_invalid, test_conn_param_req_periph_rem_invalid_param) +{ + struct node_tx *tx; + uint16_t interval; + uint8_t err; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Initiate a Connection Parameter Request Procedure */ + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT, NULL); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Prepare */ + event_prepare(&conn); + conn_param_req.reference_conn_event_count = event_counter(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CONNECTION_PARAM_REQ, &conn, &tx, &conn_param_req); + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + interval = conn_update_ind.interval; + conn_update_ind.interval = 0U; + conn_update_ind.instant = event_counter(&conn) + 6U; + lt_tx(LL_CONNECTION_UPDATE_IND, &conn, &conn_update_ind); + + /* Done */ + event_done(&conn); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_INVALID_LL_PARAM, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* Clear termination flag for subsequent test cycle */ + conn.llcp_terminate.reason_final = 0; + + /* Restore interval for other tests */ + conn_update_ind.interval = interval; +} +#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ + #if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) ZTEST_SUITE(central_loc, NULL, NULL, conn_update_setup, NULL, NULL); ZTEST_SUITE(central_rem, NULL, NULL, conn_update_setup, NULL, NULL); @@ -4604,3 +4726,5 @@ ZTEST_SUITE(central_rem_no_param_req, NULL, NULL, conn_update_setup, NULL, NULL) ZTEST_SUITE(periph_loc_no_param_req, NULL, NULL, conn_update_setup, NULL, NULL); ZTEST_SUITE(periph_rem_no_param_req, NULL, NULL, conn_update_setup, NULL, NULL); #endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */ + +ZTEST_SUITE(periph_rem_invalid, NULL, NULL, conn_update_setup, NULL, NULL); From 4e7d9d01dfc7c8b9b0d79b3b26bd86844f9e8bb2 Mon Sep 17 00:00:00 2001 From: Vinayak Kariappa Chettimada Date: Mon, 20 May 2024 09:46:28 +0200 Subject: [PATCH 6/8] Bluetooth: Controller: Refactor BT_CTLR_LE_ENC implementation Refactor reused function in BT_CTLR_LE_ENC feature. Signed-off-by: Vinayak Kariappa Chettimada (cherry picked from commit fe205a598e38549bd16620eecaef0431cfcc1b84) Signed-off-by: Vinayak Kariappa Chettimada --- subsys/bluetooth/controller/hci/hci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/subsys/bluetooth/controller/hci/hci.c b/subsys/bluetooth/controller/hci/hci.c index c3dd070720b153..7711aff0019eaf 100644 --- a/subsys/bluetooth/controller/hci/hci.c +++ b/subsys/bluetooth/controller/hci/hci.c @@ -8505,7 +8505,7 @@ static void le_ltk_request(struct pdu_data *pdu_data, uint16_t handle, } static void encrypt_change(uint8_t err, uint16_t handle, - struct net_buf *buf) + struct net_buf *buf, bool encryption_on) { struct bt_hci_evt_encrypt_change *ep; @@ -8518,7 +8518,7 @@ static void encrypt_change(uint8_t err, uint16_t handle, ep->status = err; ep->handle = sys_cpu_to_le16(handle); - ep->encrypt = !err ? 1 : 0; + ep->encrypt = encryption_on ? 1 : 0; } #endif /* CONFIG_BT_CTLR_LE_ENC */ @@ -8660,7 +8660,7 @@ static void encode_data_ctrl(struct node_rx_pdu *node_rx, break; case PDU_DATA_LLCTRL_TYPE_START_ENC_RSP: - encrypt_change(0x00, handle, buf); + encrypt_change(0x00, handle, buf, true); break; #endif /* CONFIG_BT_CTLR_LE_ENC */ @@ -8677,7 +8677,7 @@ static void encode_data_ctrl(struct node_rx_pdu *node_rx, #if defined(CONFIG_BT_CTLR_LE_ENC) case PDU_DATA_LLCTRL_TYPE_REJECT_IND: encrypt_change(pdu_data->llctrl.reject_ind.error_code, handle, - buf); + buf, false); break; #endif /* CONFIG_BT_CTLR_LE_ENC */ From 2211c3483563d5ce655393c94c362d79cc911e60 Mon Sep 17 00:00:00 2001 From: Vinayak Kariappa Chettimada Date: Wed, 5 Jun 2024 12:47:12 +0200 Subject: [PATCH 7/8] Bluetooth: Controller: Use BT_HCI_ERR_UNSPECIFIED as needed A Host shall consider any error code that it does not explicitly understand equivalent to the error code Unspecified Error (0x1F). Signed-off-by: Vinayak Kariappa Chettimada (cherry picked from commit 78466c8f52c77d54c12641a83d61c4db1cb5dd8d) Signed-off-by: Vinayak Kariappa Chettimada --- subsys/bluetooth/controller/hci/hci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subsys/bluetooth/controller/hci/hci.c b/subsys/bluetooth/controller/hci/hci.c index 7711aff0019eaf..54f70278d22c88 100644 --- a/subsys/bluetooth/controller/hci/hci.c +++ b/subsys/bluetooth/controller/hci/hci.c @@ -8516,7 +8516,7 @@ static void encrypt_change(uint8_t err, uint16_t handle, hci_evt_create(buf, BT_HCI_EVT_ENCRYPT_CHANGE, sizeof(*ep)); ep = net_buf_add(buf, sizeof(*ep)); - ep->status = err; + ep->status = err ? err : (encryption_on ? err : BT_HCI_ERR_UNSPECIFIED); ep->handle = sys_cpu_to_le16(handle); ep->encrypt = encryption_on ? 1 : 0; } From 59510f9c9819bab589237107d651981e76f9369d Mon Sep 17 00:00:00 2001 From: Vinayak Kariappa Chettimada Date: Wed, 5 Jun 2024 13:56:08 +0200 Subject: [PATCH 8/8] Bluetooth: Controller: Add explicit LLCP error code check Add unit tests to cover explicit LLCP error code check and cover the same in the Controller implementation. Signed-off-by: Vinayak Kariappa Chettimada (cherry picked from commit d6f2bc96690f5c89e1dc5974024e07dae04a9d77) Signed-off-by: Vinayak Kariappa Chettimada --- .../bluetooth/controller/ll_sw/ull_llcp_enc.c | 16 +- .../controller/ctrl_encrypt/src/main.c | 421 +++++++++++++++++- 2 files changed, 433 insertions(+), 4 deletions(-) diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c index 6955b2f7942c15..4938d9902efc28 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c @@ -382,19 +382,29 @@ static void lp_enc_store_s(struct ll_conn *conn, struct proc_ctx *ctx, struct pd static inline uint8_t reject_error_code(struct pdu_data *pdu) { + uint8_t error; + if (pdu->llctrl.opcode == PDU_DATA_LLCTRL_TYPE_REJECT_IND) { - return pdu->llctrl.reject_ind.error_code; + error = pdu->llctrl.reject_ind.error_code; #if defined(CONFIG_BT_CTLR_EXT_REJ_IND) } else if (pdu->llctrl.opcode == PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND) { - return pdu->llctrl.reject_ext_ind.error_code; + error = pdu->llctrl.reject_ext_ind.error_code; #endif /* CONFIG_BT_CTLR_EXT_REJ_IND */ } else { /* Called with an invalid PDU */ LL_ASSERT(0); /* Keep compiler happy */ - return BT_HCI_ERR_UNSPECIFIED; + error = BT_HCI_ERR_UNSPECIFIED; + } + + /* Check expected error code from the peer */ + if (error != BT_HCI_ERR_PIN_OR_KEY_MISSING && + error != BT_HCI_ERR_UNSUPP_REMOTE_FEATURE) { + error = BT_HCI_ERR_UNSPECIFIED; } + + return error; } static void lp_enc_st_wait_rx_enc_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, diff --git a/tests/bluetooth/controller/ctrl_encrypt/src/main.c b/tests/bluetooth/controller/ctrl_encrypt/src/main.c index 7d968cba76ac34..66fe7e7b939508 100644 --- a/tests/bluetooth/controller/ctrl_encrypt/src/main.c +++ b/tests/bluetooth/controller/ctrl_encrypt/src/main.c @@ -962,6 +962,426 @@ ZTEST(encryption_start, test_encryption_start_central_loc_no_ltk_2) "Free CTX buffers %d", llcp_ctx_buffers_free()); } +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Initiate | | + * | Encryption Start Proc. | | + * |--------------------------->| | + * | -----------------\ | | + * | | Empty Tx queue |-| | + * | |----------------| | | + * | | | + * | | LL_ENC_REQ | + * | |-------------------->| + * | | | + * | | LL_REJECT_EXT_IND | + * | |<--------------------| + * | | | + * | Encryption Start Proc. | | + * | Complete | | + * |<---------------------------| | + * | | | + */ +ZTEST(encryption_start, test_encryption_start_central_loc_reject_ext_success) +{ + uint8_t err; + struct node_tx *tx; + struct node_rx_pdu *ntf; + + const uint8_t rand[] = { RAND }; + const uint8_t ediv[] = { EDIV }; + const uint8_t ltk[] = { LTK }; + + /* Prepare expected LL_ENC_REQ */ + struct pdu_data_llctrl_enc_req exp_enc_req = { + .rand = { RAND }, + .ediv = { EDIV }, + .skdm = { SKDM }, + .ivm = { IVM }, + }; + + /* Prepare mocked call to lll_csrand_get */ + lll_csrand_get_fake.return_val = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); + lll_csrand_get_custom_fake_context.buf = exp_enc_req.skdm; + lll_csrand_get_custom_fake_context.len = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); + + struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { + .reject_opcode = PDU_DATA_LLCTRL_TYPE_ENC_REQ, + .error_code = BT_HCI_ERR_SUCCESS + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */ + + /* Initiate an Encryption Start Procedure */ + err = ull_cp_encryption_start(&conn, rand, ediv, ltk); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_ENC_REQ, &conn, &tx, &exp_enc_req); + lt_rx_q_is_empty(&conn); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Rx */ + lt_tx(LL_REJECT_EXT_IND, &conn, &reject_ext_ind); + + /* Done */ + event_done(&conn); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */ + + struct pdu_data_llctrl_reject_ind reject_ind_expected = { + .error_code = BT_HCI_ERR_UNSPECIFIED }; + + /* There should be one host notification */ + ut_rx_pdu(LL_REJECT_IND, &ntf, &reject_ind_expected); + ut_rx_q_is_empty(); + + /* Release Ntf */ + release_ntf(ntf); + + zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", llcp_ctx_buffers_free()); +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Initiate | | + * | Encryption Start Proc. | | + * |--------------------------->| | + * | -----------------\ | | + * | | Empty Tx queue |-| | + * | |----------------| | | + * | | | + * | | LL_ENC_REQ | + * | |-------------------->| + * | | | + * | | LL_REJECT_IND | + * | |<--------------------| + * | | | + * | Encryption Start Proc. | | + * | Complete | | + * |<---------------------------| | + * | | | + */ +ZTEST(encryption_start, test_encryption_start_central_loc_reject_success) +{ + uint8_t err; + struct node_tx *tx; + struct node_rx_pdu *ntf; + + const uint8_t rand[] = { RAND }; + const uint8_t ediv[] = { EDIV }; + const uint8_t ltk[] = { LTK }; + + /* Prepare expected LL_ENC_REQ */ + struct pdu_data_llctrl_enc_req exp_enc_req = { + .rand = { RAND }, + .ediv = { EDIV }, + .skdm = { SKDM }, + .ivm = { IVM }, + }; + + /* Prepare mocked call to lll_csrand_get */ + lll_csrand_get_fake.return_val = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); + lll_csrand_get_custom_fake_context.buf = exp_enc_req.skdm; + lll_csrand_get_custom_fake_context.len = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); + + struct pdu_data_llctrl_reject_ind reject_ind = { .error_code = BT_HCI_ERR_SUCCESS }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */ + + /* Initiate an Encryption Start Procedure */ + err = ull_cp_encryption_start(&conn, rand, ediv, ltk); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_ENC_REQ, &conn, &tx, &exp_enc_req); + lt_rx_q_is_empty(&conn); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Rx */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + /* Done */ + event_done(&conn); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */ + + struct pdu_data_llctrl_reject_ind reject_ind_expected = { + .error_code = BT_HCI_ERR_UNSPECIFIED }; + + /* There should be one host notification */ + ut_rx_pdu(LL_REJECT_IND, &ntf, &reject_ind_expected); + ut_rx_q_is_empty(); + + /* Release Ntf */ + release_ntf(ntf); + + zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", llcp_ctx_buffers_free()); +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Initiate | | + * | Encryption Start Proc. | | + * |--------------------------->| | + * | -----------------\ | | + * | | Empty Tx queue |-| | + * | |----------------| | | + * | | | + * | | LL_ENC_REQ | + * | |-------------------->| + * | | | + * | | LL_ENC_RSP | + * | |<--------------------| + * | | | + * | | LL_REJECT_EXT_IND | + * | |<--------------------| + * | | | + * | Encryption Start Proc. | | + * | Complete | | + * |<---------------------------| | + * | | | + */ +ZTEST(encryption_start, test_encryption_start_central_loc_no_ltk_reject_ext_success) +{ + uint8_t err; + struct node_tx *tx; + struct node_rx_pdu *ntf; + + const uint8_t rand[] = { RAND }; + const uint8_t ediv[] = { EDIV }; + const uint8_t ltk[] = { LTK }; + + /* Prepare expected LL_ENC_REQ */ + struct pdu_data_llctrl_enc_req exp_enc_req = { + .rand = { RAND }, + .ediv = { EDIV }, + .skdm = { SKDM }, + .ivm = { IVM }, + }; + + /* Prepare LL_ENC_RSP */ + struct pdu_data_llctrl_enc_rsp enc_rsp = { .skds = { SKDS }, .ivs = { IVS } }; + + /* Prepare mocked call to lll_csrand_get */ + lll_csrand_get_fake.return_val = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); + lll_csrand_get_custom_fake_context.buf = exp_enc_req.skdm; + lll_csrand_get_custom_fake_context.len = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); + + struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { + .reject_opcode = PDU_DATA_LLCTRL_TYPE_ENC_REQ, + .error_code = BT_HCI_ERR_SUCCESS + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */ + + /* Initiate an Encryption Start Procedure */ + err = ull_cp_encryption_start(&conn, rand, ediv, ltk); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_ENC_REQ, &conn, &tx, &exp_enc_req); + lt_rx_q_is_empty(&conn); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Rx */ + lt_tx(LL_ENC_RSP, &conn, &enc_rsp); + + /* Rx */ + lt_tx(LL_REJECT_EXT_IND, &conn, &reject_ext_ind); + + /* Done */ + event_done(&conn); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */ + + struct pdu_data_llctrl_reject_ind reject_ind_expected = { + .error_code = BT_HCI_ERR_UNSPECIFIED }; + + /* There should be one host notification */ + ut_rx_pdu(LL_REJECT_IND, &ntf, &reject_ind_expected); + ut_rx_q_is_empty(); + + /* Release Ntf */ + release_ntf(ntf); + + zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", llcp_ctx_buffers_free()); +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Initiate | | + * | Encryption Start Proc. | | + * |--------------------------->| | + * | -----------------\ | | + * | | Empty Tx queue |-| | + * | |----------------| | | + * | | | + * | | LL_ENC_REQ | + * | |-------------------->| + * | | | + * | | LL_ENC_RSP | + * | |<--------------------| + * | | | + * | | LL_REJECT_IND | + * | |<--------------------| + * | | | + * | Encryption Start Proc. | | + * | Complete | | + * |<---------------------------| | + * | | | + */ +ZTEST(encryption_start, test_encryption_start_central_loc_no_ltk_2_reject_success) +{ + uint8_t err; + struct node_tx *tx; + struct node_rx_pdu *ntf; + + const uint8_t rand[] = { RAND }; + const uint8_t ediv[] = { EDIV }; + const uint8_t ltk[] = { LTK }; + + /* Prepare expected LL_ENC_REQ */ + struct pdu_data_llctrl_enc_req exp_enc_req = { + .rand = { RAND }, + .ediv = { EDIV }, + .skdm = { SKDM }, + .ivm = { IVM }, + }; + + /* Prepare LL_ENC_RSP */ + struct pdu_data_llctrl_enc_rsp enc_rsp = { .skds = { SKDS }, .ivs = { IVS } }; + + /* Prepare mocked call to lll_csrand_get */ + lll_csrand_get_fake.return_val = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); + lll_csrand_get_custom_fake_context.buf = exp_enc_req.skdm; + lll_csrand_get_custom_fake_context.len = sizeof(exp_enc_req.skdm) + sizeof(exp_enc_req.ivm); + + struct pdu_data_llctrl_reject_ind reject_ind = { .error_code = BT_HCI_ERR_SUCCESS }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */ + + /* Initiate an Encryption Start Procedure */ + err = ull_cp_encryption_start(&conn, rand, ediv, ltk); + zassert_equal(err, BT_HCI_ERR_SUCCESS); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_ENC_REQ, &conn, &tx, &exp_enc_req); + lt_rx_q_is_empty(&conn); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, PAUSED, UNENCRYPTED); /* Tx paused & unenc. */ + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Rx */ + lt_tx(LL_ENC_RSP, &conn, &enc_rsp); + + /* Rx */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + /* Done */ + event_done(&conn); + + /* Check state */ + CHECK_RX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Rx unenc. */ + CHECK_TX_PE_STATE(conn, RESUMED, UNENCRYPTED); /* Tx unenc. */ + + struct pdu_data_llctrl_reject_ind reject_ind_expected = { + .error_code = BT_HCI_ERR_UNSPECIFIED }; + + /* There should be one host notification */ + ut_rx_pdu(LL_REJECT_IND, &ntf, &reject_ind_expected); + ut_rx_q_is_empty(); + + /* Release Ntf */ + release_ntf(ntf); + + zassert_equal(llcp_ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", llcp_ctx_buffers_free()); +} + /* +-----+ +-------+ +-----+ * | UT | | LL_A | | LT | * +-----+ +-------+ +-----+ @@ -2216,7 +2636,6 @@ ZTEST(encryption_pause, test_encryption_pause_periph_rem_invalid) uint8_t err; struct node_tx *tx; - struct node_rx_pdu *ntf; struct ll_conn_iso_stream cis = { 0 }; const uint8_t rand[] = { RAND };