Bluetooth: Host: Fix not clearing IDs and keys upon bt_disable()

Expectation: After calling `bt_disable()` it is possible to
use the Bluetooth APIs as if `bt_enable()` was never called.

This was not the case for `bt_id_create()`, it was not possible
to set the default identity. This prevented an application
developer to restart the stack as a different identity.

Keys also need to be cleared to avoid the following pattern:
1. Pair two devices
2. Central calls `bt_disable()` and `bt_enable()`.
   The central will now generate a new identity address.
3. Connect the two devices.
4. Re-establish encryption. Now the central will try to use
   the previously used keys. The procedure will fail
   because the peripheral does not have any keys associated
   with the new central address.

The API documentation is updated accordingly.

Signed-off-by: Rubin Gerritsen <rubin.gerritsen@nordicsemi.no>
This commit is contained in:
Rubin Gerritsen 2024-05-13 13:51:57 +02:00 committed by Carles Cufí
commit 3ce106c620
7 changed files with 106 additions and 0 deletions

View file

@ -231,6 +231,10 @@ int bt_enable(bt_ready_cb_t cb);
*
* Disable Bluetooth. Can't be called before bt_enable has completed.
*
* This API will clear all configured identities and keys that are not persistently
* stored with @kconfig{CONFIG_BT_SETTINGS}. These can be restored
* with settings_load() before reenabling the stack.
*
* Close and release HCI resources. Result is architecture dependent.
*
* @return Zero on success or (negative) error code otherwise.

View file

@ -4233,6 +4233,13 @@ int bt_disable(void)
/* Some functions rely on checking this bitfield */
memset(bt_dev.supported_commands, 0x00, sizeof(bt_dev.supported_commands));
/* Reset IDs and corresponding keys. */
bt_dev.id_count = 0;
#if defined(CONFIG_BT_SMP)
bt_dev.le.rl_entries = 0;
bt_keys_reset();
#endif
/* If random address was set up - clear it */
bt_addr_le_copy(&bt_dev.random_addr, BT_ADDR_LE_ANY);

View file

@ -80,6 +80,11 @@ static bool key_is_in_use(uint8_t id)
}
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
void bt_keys_reset(void)
{
memset(key_pool, 0, sizeof(key_pool));
}
struct bt_keys *bt_keys_get_addr(uint8_t id, const bt_addr_le_t *addr)
{
struct bt_keys *keys;

View file

@ -82,6 +82,12 @@ struct bt_keys {
#define BT_KEYS_STORAGE_LEN (sizeof(struct bt_keys) - \
offsetof(struct bt_keys, storage_start))
/** Clears all keys.
*
* Keys stored in settings are not cleared.
*/
void bt_keys_reset(void);
/**
* @brief Get a call through the callback for each key with the same type
*

View file

@ -2,6 +2,7 @@ CONFIG_BT=y
CONFIG_LOG=y
CONFIG_ASSERT=y
CONFIG_BT_SMP=y
CONFIG_BT_MAX_PAIRED=2
CONFIG_BT_DEVICE_NAME="CIS test"

View file

@ -35,6 +35,62 @@ static void test_disable_main(void)
PASS("Disable test passed\n");
}
static void test_disable_set_default_id(void)
{
/* FIXME: Temporary workaround to get around a bug in the controller.
* The controller gets stuck in the POWER_CLOCK ISR without this.
* See open PR: https://github.com/zephyrproject-rtos/zephyr/pull/73342
* for more details.
*/
k_sleep(K_MSEC(1));
for (int i = 0; i < NUM_ITERATIONS; i++) {
int err;
size_t id_count;
bt_addr_le_t stored_id;
bt_addr_le_t addr = {.type = BT_ADDR_LE_RANDOM,
.a = {.val = {i, 2, 3, 4, 5, 0xc0}}};
err = bt_id_create(&addr, NULL);
if (err != 0) {
FAIL("Creating ID failed (err %d)\n", err);
}
err = bt_enable(NULL);
if (err != 0) {
FAIL("Enable failed (err %d)\n", err);
}
bt_id_get(NULL, &id_count);
if (id_count != 1) {
FAIL("Expected only one ID, but got: %u\n", id_count);
}
id_count = 1;
bt_id_get(&stored_id, &id_count);
if (id_count != 1) {
FAIL("Expected only one ID, but got: %u\n", id_count);
}
if (!bt_addr_le_eq(&stored_id, &addr)) {
uint8_t addr_set_str[BT_ADDR_LE_STR_LEN];
uint8_t addr_stored_str[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(&addr, addr_set_str, BT_ADDR_LE_STR_LEN);
bt_addr_le_to_str(&stored_id, addr_stored_str, BT_ADDR_LE_STR_LEN);
FAIL("Expected stored ID to be equal to set ID: %s, %s\n",
addr_set_str, addr_stored_str);
}
err = bt_disable();
if (err) {
FAIL("Enable failed (err %d)\n", err);
}
}
PASS("Disable set default ID test passed\n");
}
static const struct bst_test_instance test_def[] = {
{
.test_id = "disable",
@ -43,6 +99,13 @@ static const struct bst_test_instance test_def[] = {
.test_tick_f = test_tick,
.test_main_f = test_disable_main
},
{
.test_id = "disable_set_default_id",
.test_descr = "disable_test where each iteration sets the default ID",
.test_pre_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_disable_set_default_id
},
BSTEST_END_MARKER
};

View file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Copyright (c) 2022 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
# Disable test: bt_enable and bt_disable are called in a loop.
# Each iteration the default ID is updated
simulation_id="disable_test_set_default_id"
verbosity_level=2
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_host_misc_disable_prj_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=disable_set_default_id
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=1 -sim_length=10e6 $@
wait_for_background_jobs