Skip to content

Commit 9757ffa

Browse files
PavelVPVkartben
authored andcommitted
bluetooth: host: smp: fix deadlock when public key generation fails
When `bt_le_oob_get_local` or `bt_le_ext_adv_oob_get_local` is called and SMP is enabled, `bt_smp_le_oob_generate_sc_data` is called to generate a Random Number and a Confirmation Value needed for OOB data. These values are based on the device's public key. The public key is generated only once when `bt_smp_init` is called. If public key generation fails, the callback passed to `bt_pub_key_get` is called with `pkey` set to NULL. The `bt_smp_pkey_ready` callback gets called, but it doesn't release the `sc_local_pkey_ready` semaphore thus leaving `bt_smp_le_oob_generate_sc_data` wait for semaphore with `K_FOREVER`. This commit replaces the semaphore with a conditional variable and requests a public key again if the key is NULL thus solving 2 issues: - handling the case where the callback was triggered notifying about the completion of the public key request, but the key was not generated, - handling the case where multiple threads trying to acquire the same sempahore. The timeout is used instead of K_FOREVER to avoid cases when callback has never been triggered. Signed-off-by: Pavel Vasilyev <[email protected]>
1 parent 56539d8 commit 9757ffa

File tree

1 file changed

+64
-9
lines changed
  • subsys/bluetooth/host

1 file changed

+64
-9
lines changed

subsys/bluetooth/host/smp.c

+64-9
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,19 @@ static bool sc_oobd_present;
284284
static bool legacy_oobd_present;
285285
static bool sc_supported;
286286
static const uint8_t *sc_public_key;
287-
static K_SEM_DEFINE(sc_local_pkey_ready, 0, 1);
287+
288+
static void bt_smp_pkey_ready(const uint8_t *pkey);
289+
static struct {
290+
struct k_mutex lock;
291+
struct k_condvar condvar;
292+
struct bt_pub_key_cb cb;
293+
} pub_key_gen = {
294+
.lock = Z_MUTEX_INITIALIZER(pub_key_gen.lock),
295+
.condvar = Z_CONDVAR_INITIALIZER(pub_key_gen.condvar),
296+
.cb = {
297+
.func = bt_smp_pkey_ready,
298+
},
299+
};
288300

289301
/* Pointer to internal data is used to mark that callbacks of given SMP channel are not initialized.
290302
* Value of NULL represents no authentication capabilities and cannot be used for that purpose.
@@ -4627,6 +4639,21 @@ static int bt_smp_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
46274639
return 0;
46284640
}
46294641

4642+
static void pub_key_ready_notify(void)
4643+
{
4644+
int err;
4645+
4646+
ARG_UNUSED(err);
4647+
4648+
err = k_mutex_lock(&pub_key_gen.lock, K_FOREVER);
4649+
__ASSERT_NO_MSG(err == 0);
4650+
4651+
(void)k_condvar_broadcast(&pub_key_gen.condvar);
4652+
4653+
err = k_mutex_unlock(&pub_key_gen.lock);
4654+
__ASSERT_NO_MSG(err == 0);
4655+
}
4656+
46304657
static void bt_smp_pkey_ready(const uint8_t *pkey)
46314658
{
46324659
int i;
@@ -4635,13 +4662,13 @@ static void bt_smp_pkey_ready(const uint8_t *pkey)
46354662

46364663
sc_public_key = pkey;
46374664

4665+
pub_key_ready_notify();
4666+
46384667
if (!pkey) {
46394668
LOG_WRN("Public key not available");
46404669
return;
46414670
}
46424671

4643-
k_sem_give(&sc_local_pkey_ready);
4644-
46454672
for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) {
46464673
struct bt_smp *smp = &bt_smp_pool[i];
46474674
uint8_t err;
@@ -5668,10 +5695,42 @@ int bt_smp_le_oob_generate_sc_data(struct bt_le_oob_sc_data *le_sc_oob)
56685695
}
56695696

56705697
if (!sc_public_key) {
5671-
err = k_sem_take(&sc_local_pkey_ready, K_FOREVER);
5698+
/* Public key request has not been finished yet, or finished, but generation failed.
5699+
* Retrying.
5700+
*/
5701+
err = k_mutex_lock(&pub_key_gen.lock, K_FOREVER);
5702+
__ASSERT_NO_MSG(err == 0);
5703+
5704+
err = bt_pub_key_gen(&pub_key_gen.cb);
5705+
if (err && err != -EALREADY) {
5706+
LOG_WRN("Public key re-generation request failed (%d)", err);
5707+
5708+
int mutex_err;
5709+
5710+
mutex_err = k_mutex_unlock(&pub_key_gen.lock);
5711+
__ASSERT_NO_MSG(mutex_err == 0);
5712+
5713+
return err;
5714+
}
5715+
5716+
/* 30 seconds is an arbitrary number. Increase if fails earlier on certain
5717+
* platforms.
5718+
*/
5719+
err = k_condvar_wait(&pub_key_gen.condvar,
5720+
&pub_key_gen.lock,
5721+
K_SECONDS(30));
56725722
if (err) {
5723+
LOG_WRN("Public key generation timeout");
56735724
return err;
56745725
}
5726+
5727+
err = k_mutex_unlock(&pub_key_gen.lock);
5728+
__ASSERT_NO_MSG(err == 0);
5729+
5730+
/* Public key has been re-requested but generation failed. */
5731+
if (!sc_public_key) {
5732+
return -EAGAIN;
5733+
}
56755734
}
56765735

56775736
if (IS_ENABLED(CONFIG_BT_OOB_DATA_FIXED)) {
@@ -6082,10 +6141,6 @@ BT_L2CAP_BR_CHANNEL_DEFINE(smp_br_fixed_chan, BT_L2CAP_CID_BR_SMP,
60826141

60836142
int bt_smp_init(void)
60846143
{
6085-
static struct bt_pub_key_cb pub_key_cb = {
6086-
.func = bt_smp_pkey_ready,
6087-
};
6088-
60896144
sc_supported = le_sc_supported();
60906145
if (IS_ENABLED(CONFIG_BT_SMP_SC_PAIR_ONLY) && !sc_supported) {
60916146
LOG_ERR("SC Pair Only Mode selected but LE SC not supported");
@@ -6100,7 +6155,7 @@ int bt_smp_init(void)
61006155
LOG_DBG("LE SC %s", sc_supported ? "enabled" : "disabled");
61016156

61026157
if (!IS_ENABLED(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)) {
6103-
bt_pub_key_gen(&pub_key_cb);
6158+
bt_pub_key_gen(&pub_key_gen.cb);
61046159
}
61056160

61066161
return smp_self_test();

0 commit comments

Comments
 (0)