Re: [PATCH] Bluetooth: hci_core: Fix IRK lookup lifetime races

From: Kazuki Hanai

Date: Tue Sep 01 2026 - 07:17:30 EST


Resending as plain text because my previous reply contained an HTML part
and was rejected by the mailing lists. Sorry for the duplicate sent to the
direct recipients.

Hi Luiz,

Thank you for the review.

I agree that the previous patch mixed the IRK lifetime bug with several
separate synchronization issues and became too broad.

I have prepared a much smaller v3 that is limited to the escaped-pointer
lifetime problem:

- remove the dedicated IRK lock and payload snapshots
- remove the late-add and initialization changes
- remove the production selftests
- retain only list-owned and caller-owned references
- acquire the reference before leaving the RCU read-side critical section
- release it after each caller finishes using the IRK

My understanding is that synchronize_rcu() alone would not protect this
case because the returned pointer is used after rcu_read_unlock(). A
caller paused after the lookup is no longer an RCU reader, so the grace
period could finish and the object could be freed before that caller
resumes.

Regarding the test: yes, I used an AI assistant to help develop
deterministic test-only kernel instrumentation. It is not included in
the production patch. On the vulnerable parent, it reproduces KASAN
use-after-free reports for the 16-byte irk->val write and the 6-byte
irk->rpa write. With the reduced v3, the same schedule completes cleanly
and the test also confirms the final release of the IRK.

The regular BlueZ tests also passed:

- mgmt-tester IRK: 6/6
- mgmt-tester Privacy: 30/30
- smp-tester: 8/8

Would it be okay for me to send this reduced v3 patch?

I am still relatively new to upstream kernel development, so if the
reference-counting approach is not appropriate, please correct me. I
would also be happy to provide only the UAF report and reproducer if you
would prefer to handle the fix differently.

Thanks,
Kazuki

On Tue, Sep 1, 2026 at 6:57 PM Kazuki Hanai <hnkz.64@xxxxxxxxx> wrote:
>
> Hi Luiz,
>
> Thank you for the review.
>
> I agree that the previous patch mixed the IRK lifetime bug with several
> separate synchronization issues and became too broad.
>
> I have prepared a much smaller v3 that is limited to the escaped-pointer
> lifetime problem:
>
> - remove the dedicated IRK lock and payload snapshots
> - remove the late-add and initialization changes
> - remove the production selftests
> - retain only list-owned and caller-owned references
> - acquire the reference before leaving the RCU read-side critical section
> - release it after each caller finishes using the IRK
>
> My understanding is that synchronize_rcu() alone would not protect this
> case because the returned pointer is used after rcu_read_unlock(). A
> caller paused after the lookup is no longer an RCU reader, so the grace
> period could finish and the object could be freed before that caller
> resumes.
>
> Regarding the test: yes, I used an AI assistant to help develop
> deterministic test-only kernel instrumentation. It is not included in
> the production patch. On the vulnerable parent, it reproduces KASAN
> use-after-free reports for the 16-byte irk->val write and the 6-byte
> irk->rpa write. With the reduced v3, the same schedule completes cleanly
> and the test also confirms the final release of the IRK.
>
> The regular BlueZ tests also passed:
>
> - mgmt-tester IRK: 6/6
> - mgmt-tester Privacy: 30/30
> - smp-tester: 8/8
>
> Would it be okay for me to send this reduced v3 patch?
>
> I am still relatively new to upstream kernel development, so if the
> reference-counting approach is not appropriate, please correct me. I
> would also be happy to provide only the UAF report and reproducer if you
> would prefer to handle the fix differently.
>
> Thanks,
> Kazuki
>
>
> On Tue, Sep 1, 2026 at 12:35 AM Luiz Augusto von Dentz <luiz.dentz@xxxxxxxxx> wrote:
>>
>> Hi Kazuki,
>>
>> On Thu, Aug 27, 2026 at 10:27 PM Kazuki Hanai <hnkz.64@xxxxxxxxx> wrote:
>> >
>> > The IRK lookup helpers traverse the identity resolving key list under
>> > RCU, but return a raw pointer after leaving the read-side critical
>> > section. A concurrent management unpair or key reload can unlink and
>> > free that entry while SMP key distribution still updates its value and
>> > RPA through hci_add_irk().
>> >
>> > RCU also does not serialize list mutations. SMP cleanup and key
>> > distribution can update the IRK list without the hdev mutex while
>> > management paths update it with that mutex held, allowing concurrent
>> > list_add_rcu() and list_del_rcu() operations on the same list.
>>
>> So rather than using the hdev lock, is it better to introduce a lock
>> specific to the IRK, why?
>>
>> > Give each IRK a list-owned reference and return caller-owned references
>> > from lookup and add helpers. Keep the SMP context reference until pairing
>> > teardown, and drop the list reference only once when an entry is
>> > unlinked. Add a dedicated spinlock for IRK list and payload updates, and
>> > copy payload snapshots under that lock so readers do not race updates.
>>
>> I understand why we could need a reference, but the locking here just
>> seems excessive, especially since many other lists are doing:
>>
>> list_del_rcu(&k->list);
>> kfree_rcu(k, rcu);
>>
>> Which I thought would garantee there is not code attempting to access
>> the entry under rcu_read_lock but perhaps we need to force it with
>> synchronize_rcu before its freed.
>>
>> > Initialize new entries completely before publishing them. Unlink an IRK
>> > added during unpair before dropping the SMP context reference.
>>
>> Sounds like a different issue.
>>
>> > An exact KASAN interleaving that removes and drains the RCU entry after
>> > lookup but before hci_add_irk() resumes now completes without a
>> > use-after-free. A forced late-add/unpair interleaving leaves no linked IRK
>> > behind. A KASAN and lockdep enabled VHCI pairing/unpair test also
>> > completes successfully.
>>
>> What test are you referring to? Was this a test generated by the AI to
>> validate the change?
>>
>> > Fixes: a7ec73386ce2 ("Bluetooth: Fix removing any IRKs when unpairing devices")
>> > Cc: stable@xxxxxxxxxxxxxxx
>> > Assisted-by: LLM
>> > Signed-off-by: Kazuki Hanai <hnkz.64@xxxxxxxxx>
>> > ---
>> > include/net/bluetooth/hci_core.h | 19 ++-
>> > net/bluetooth/hci_conn.c | 34 ++++--
>> > net/bluetooth/hci_core.c | 202 +++++++++++++++++++++++++------
>> > net/bluetooth/hci_debugfs.c | 6 +-
>> > net/bluetooth/hci_event.c | 16 ++-
>> > net/bluetooth/hci_sync.c | 16 ++-
>> > net/bluetooth/iso.c | 18 ++-
>> > net/bluetooth/mgmt.c | 12 +-
>> > net/bluetooth/smp.c | 20 ++-
>> > 9 files changed, 274 insertions(+), 69 deletions(-)
>> >
>> > diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
>> > index 4105c446ca98..75f3f26d9e31 100644
>> > --- a/include/net/bluetooth/hci_core.h
>> > +++ b/include/net/bluetooth/hci_core.h
>> > @@ -24,6 +24,7 @@
>> > #define __HCI_CORE_H
>> >
>> > #include <linux/idr.h>
>> > +#include <linux/kref.h>
>> > #include <linux/leds.h>
>> > #include <linux/rculist.h>
>> > #include <linux/spinlock.h>
>> > @@ -211,6 +212,15 @@ struct smp_ltk {
>> > struct smp_irk {
>> > struct list_head list;
>> > struct rcu_head rcu;
>> > + struct kref ref;
>> > + unsigned long flags;
>> > + bdaddr_t rpa;
>> > + bdaddr_t bdaddr;
>> > + u8 addr_type;
>> > + u8 val[16];
>> > +};
>> > +
>> > +struct smp_irk_data {
>> > bdaddr_t rpa;
>> > bdaddr_t bdaddr;
>> > u8 addr_type;
>> > @@ -561,6 +571,7 @@ struct hci_dev {
>> > struct list_head uuids;
>> > struct list_head link_keys;
>> > struct list_head long_term_keys;
>> > + spinlock_t irk_lock; /* protects IRK list and data */
>> > struct list_head identity_resolving_keys;
>> > struct list_head remote_oob_data;
>> > struct list_head le_accept_list;
>> > @@ -1885,11 +1896,16 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type);
>> > void hci_smp_ltks_clear(struct hci_dev *hdev);
>> > int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
>> >
>> > +/* Returned IRKs hold a reference that must be released with hci_irk_put(). */
>> > struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa);
>> > struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
>> > u8 addr_type);
>> > struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
>> > u8 addr_type, u8 val[16], bdaddr_t *rpa);
>> > +void hci_irk_read(struct hci_dev *hdev, struct smp_irk *irk,
>> > + struct smp_irk_data *data);
>> > +void hci_irk_put(struct smp_irk *irk);
>> > +void hci_irk_unlink(struct hci_dev *hdev, struct smp_irk *irk);
>> > void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type);
>> > bool hci_is_blocked_key(struct hci_dev *hdev, u8 type, u8 val[16]);
>> > void hci_blocked_keys_clear(struct hci_dev *hdev);
>> > @@ -2505,7 +2521,8 @@ void mgmt_resuming(struct hci_dev *hdev, u8 reason, bdaddr_t *bdaddr,
>> > u8 addr_type);
>> > bool mgmt_powering_down(struct hci_dev *hdev);
>> > void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent);
>> > -void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk, bool persistent);
>> > +void mgmt_new_irk(struct hci_dev *hdev, const struct smp_irk_data *irk,
>> > + bool persistent);
>> > void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk,
>> > bool persistent);
>> > void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr,
>> > diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
>> > index 8de98af2fb58..5e19fc6ef930 100644
>> > --- a/net/bluetooth/hci_conn.c
>> > +++ b/net/bluetooth/hci_conn.c
>> > @@ -70,6 +70,8 @@ void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
>> > struct hci_conn_params *params;
>> > struct hci_dev *hdev = conn->hdev;
>> > struct smp_irk *irk;
>> > + struct smp_irk_data irk_data;
>> > + bdaddr_t identity_addr;
>> > bdaddr_t *bdaddr;
>> > u8 bdaddr_type;
>> >
>> > @@ -79,8 +81,11 @@ void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
>> > /* Check if we need to convert to identity address */
>> > irk = hci_get_irk(hdev, bdaddr, bdaddr_type);
>> > if (irk) {
>> > - bdaddr = &irk->bdaddr;
>> > - bdaddr_type = irk->addr_type;
>> > + hci_irk_read(hdev, irk, &irk_data);
>> > + bacpy(&identity_addr, &irk_data.bdaddr);
>> > + bdaddr_type = irk_data.addr_type;
>> > + hci_irk_put(irk);
>> > + bdaddr = &identity_addr;
>> > }
>> >
>> > params = hci_pend_le_action_lookup(&hdev->pend_le_conns, bdaddr,
>> > @@ -1004,6 +1009,7 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
>> > {
>> > struct hci_conn *conn;
>> > struct smp_irk *irk = NULL;
>> > + struct smp_irk_data irk_data;
>> >
>> > switch (type) {
>> > case ACL_LINK:
>> > @@ -1037,16 +1043,21 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
>> > bt_dev_dbg(hdev, "dst %pMR handle 0x%4.4x", dst, handle);
>> >
>> > conn = kzalloc_obj(*conn);
>> > - if (!conn)
>> > + if (!conn) {
>> > + if (irk)
>> > + hci_irk_put(irk);
>> > return ERR_PTR(-ENOMEM);
>> > + }
>> >
>> > /* If and IRK exists use its identity address */
>> > if (!irk) {
>> > bacpy(&conn->dst, dst);
>> > conn->dst_type = dst_type;
>> > } else {
>> > - bacpy(&conn->dst, &irk->bdaddr);
>> > - conn->dst_type = irk->addr_type;
>> > + hci_irk_read(hdev, irk, &irk_data);
>> > + bacpy(&conn->dst, &irk_data.bdaddr);
>> > + conn->dst_type = irk_data.addr_type;
>> > + hci_irk_put(irk);
>> > }
>> >
>> > bacpy(&conn->src, &hdev->bdaddr);
>> > @@ -1458,6 +1469,8 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
>> > {
>> > struct hci_conn *conn;
>> > struct smp_irk *irk;
>> > + struct smp_irk_data irk_data;
>> > + bdaddr_t rpa;
>> > int err;
>> >
>> > /* Let's make sure that le is enabled.*/
>> > @@ -1498,9 +1511,14 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
>> > * from the connect request.
>> > */
>> > irk = hci_find_irk_by_addr(hdev, dst, dst_type);
>> > - if (irk && bacmp(&irk->rpa, BDADDR_ANY)) {
>> > - dst = &irk->rpa;
>> > - dst_type = ADDR_LE_DEV_RANDOM;
>> > + if (irk) {
>> > + hci_irk_read(hdev, irk, &irk_data);
>> > + if (bacmp(&irk_data.rpa, BDADDR_ANY)) {
>> > + bacpy(&rpa, &irk_data.rpa);
>> > + dst = &rpa;
>> > + dst_type = ADDR_LE_DEV_RANDOM;
>> > + }
>> > + hci_irk_put(irk);
>> > }
>> > }
>> >
>> > diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
>> > index 35a1be57e386..4503e2afbb77 100644
>> > --- a/net/bluetooth/hci_core.c
>> > +++ b/net/bluetooth/hci_core.c
>> > @@ -1031,13 +1031,36 @@ void hci_smp_ltks_clear(struct hci_dev *hdev)
>> > }
>> > }
>> >
>> > +enum {
>> > + SMP_IRK_LINKED,
>> > +};
>> > +
>> > +static bool __hci_irk_unlink(struct smp_irk *irk)
>> > +{
>> > + if (!test_and_clear_bit(SMP_IRK_LINKED, &irk->flags))
>> > + return false;
>> > +
>> > + list_del_rcu(&irk->list);
>> > + return true;
>> > +}
>> > +
>> > void hci_smp_irks_clear(struct hci_dev *hdev)
>> > {
>> > - struct smp_irk *k, *tmp;
>> > + struct smp_irk *k;
>> >
>> > - list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
>> > - list_del_rcu(&k->list);
>> > - kfree_rcu(k, rcu);
>> > + for (;;) {
>> > + spin_lock_bh(&hdev->irk_lock);
>> > + if (list_empty(&hdev->identity_resolving_keys)) {
>> > + spin_unlock_bh(&hdev->irk_lock);
>> > + break;
>> > + }
>> > +
>> > + k = list_first_entry(&hdev->identity_resolving_keys,
>> > + struct smp_irk, list);
>> > + __hci_irk_unlink(k);
>> > + spin_unlock_bh(&hdev->irk_lock);
>> > +
>> > + hci_irk_put(k);
>> > }
>> > }
>> >
>> > @@ -1171,37 +1194,73 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
>> > return NULL;
>> > }
>> >
>> > +void hci_irk_read(struct hci_dev *hdev, struct smp_irk *irk,
>> > + struct smp_irk_data *data)
>> > +{
>> > + spin_lock_bh(&hdev->irk_lock);
>> > + bacpy(&data->rpa, &irk->rpa);
>> > + bacpy(&data->bdaddr, &irk->bdaddr);
>> > + data->addr_type = irk->addr_type;
>> > + memcpy(data->val, irk->val, sizeof(data->val));
>> > + spin_unlock_bh(&hdev->irk_lock);
>> > +}
>> > +
>> > +static bool hci_irk_get(struct smp_irk *irk)
>> > +{
>> > + if (!test_bit(SMP_IRK_LINKED, &irk->flags))
>> > + return false;
>> > +
>> > + if (!kref_get_unless_zero(&irk->ref))
>> > + return false;
>> > +
>> > + if (test_bit(SMP_IRK_LINKED, &irk->flags))
>> > + return true;
>> > +
>> > + hci_irk_put(irk);
>> > + return false;
>> > +}
>> > +
>> > struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa)
>> > {
>> > struct smp_irk *irk_to_return = NULL;
>> > + struct smp_irk_data data;
>> > struct smp_irk *irk;
>> >
>> > rcu_read_lock();
>> > list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
>> > - if (!bacmp(&irk->rpa, rpa)) {
>> > + hci_irk_read(hdev, irk, &data);
>> > + if (!bacmp(&data.rpa, rpa) && hci_irk_get(irk)) {
>> > irk_to_return = irk;
>> > goto done;
>> > }
>> > }
>> >
>> > list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
>> > - if (smp_irk_matches(hdev, irk->val, rpa)) {
>> > - bacpy(&irk->rpa, rpa);
>> > + hci_irk_read(hdev, irk, &data);
>> > + if (smp_irk_matches(hdev, data.val, rpa) && hci_irk_get(irk)) {
>> > + spin_lock_bh(&hdev->irk_lock);
>> > + if (test_bit(SMP_IRK_LINKED, &irk->flags))
>> > + bacpy(&irk->rpa, rpa);
>> > + spin_unlock_bh(&hdev->irk_lock);
>> > irk_to_return = irk;
>> > goto done;
>> > }
>> > }
>> >
>> > done:
>> > + rcu_read_unlock();
>> > +
>> > + if (irk_to_return)
>> > + hci_irk_read(hdev, irk_to_return, &data);
>> > +
>> > if (irk_to_return && hci_is_blocked_key(hdev, HCI_BLOCKED_KEY_TYPE_IRK,
>> > - irk_to_return->val)) {
>> > + data.val)) {
>> > bt_dev_warn_ratelimited(hdev, "Identity key blocked for %pMR",
>> > - &irk_to_return->bdaddr);
>> > + &data.bdaddr);
>> > + hci_irk_put(irk_to_return);
>> > irk_to_return = NULL;
>> > }
>> >
>> > - rcu_read_unlock();
>> > -
>> > return irk_to_return;
>> > }
>> >
>> > @@ -1209,6 +1268,7 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
>> > u8 addr_type)
>> > {
>> > struct smp_irk *irk_to_return = NULL;
>> > + struct smp_irk_data data;
>> > struct smp_irk *irk;
>> >
>> > /* Identity Address must be public or static random */
>> > @@ -1217,25 +1277,53 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
>> >
>> > rcu_read_lock();
>> > list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
>> > - if (addr_type == irk->addr_type &&
>> > - bacmp(bdaddr, &irk->bdaddr) == 0) {
>> > + hci_irk_read(hdev, irk, &data);
>> > + if (addr_type == data.addr_type &&
>> > + bacmp(bdaddr, &data.bdaddr) == 0 && hci_irk_get(irk)) {
>> > irk_to_return = irk;
>> > break;
>> > }
>> > }
>> > + rcu_read_unlock();
>> > +
>> > + if (irk_to_return)
>> > + hci_irk_read(hdev, irk_to_return, &data);
>> >
>> > if (irk_to_return && hci_is_blocked_key(hdev, HCI_BLOCKED_KEY_TYPE_IRK,
>> > - irk_to_return->val)) {
>> > + data.val)) {
>> > bt_dev_warn_ratelimited(hdev, "Identity key blocked for %pMR",
>> > - &irk_to_return->bdaddr);
>> > + &data.bdaddr);
>> > + hci_irk_put(irk_to_return);
>> > irk_to_return = NULL;
>> > }
>> >
>> > - rcu_read_unlock();
>> > -
>> > return irk_to_return;
>> > }
>> >
>> > +static void hci_irk_release(struct kref *ref)
>> > +{
>> > + struct smp_irk *irk = container_of(ref, struct smp_irk, ref);
>> > +
>> > + kfree_rcu(irk, rcu);
>> > +}
>> > +
>> > +void hci_irk_put(struct smp_irk *irk)
>> > +{
>> > + kref_put(&irk->ref, hci_irk_release);
>> > +}
>> > +
>> > +void hci_irk_unlink(struct hci_dev *hdev, struct smp_irk *irk)
>> > +{
>> > + bool unlinked;
>> > +
>> > + spin_lock_bh(&hdev->irk_lock);
>> > + unlinked = __hci_irk_unlink(irk);
>> > + spin_unlock_bh(&hdev->irk_lock);
>> > +
>> > + if (unlinked)
>> > + hci_irk_put(irk);
>> > +}
>> > +
>> > struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
>> > bdaddr_t *bdaddr, u8 *val, u8 type,
>> > u8 pin_len, bool *persistent)
>> > @@ -1315,24 +1403,47 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
>> > struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
>> > u8 addr_type, u8 val[16], bdaddr_t *rpa)
>> > {
>> > - struct smp_irk *irk;
>> > + struct smp_irk *irk, *new_irk;
>> >
>> > irk = hci_find_irk_by_addr(hdev, bdaddr, addr_type);
>> > - if (!irk) {
>> > - irk = kzalloc_obj(*irk);
>> > - if (!irk)
>> > - return NULL;
>> > + if (irk) {
>> > + spin_lock_bh(&hdev->irk_lock);
>> > + memcpy(irk->val, val, sizeof(irk->val));
>> > + bacpy(&irk->rpa, rpa);
>> > + spin_unlock_bh(&hdev->irk_lock);
>> > + return irk;
>> > + }
>> > +
>> > + new_irk = kzalloc_obj(*new_irk);
>> > + if (!new_irk)
>> > + return NULL;
>> >
>> > - bacpy(&irk->bdaddr, bdaddr);
>> > - irk->addr_type = addr_type;
>> > + bacpy(&new_irk->bdaddr, bdaddr);
>> > + new_irk->addr_type = addr_type;
>> > + memcpy(new_irk->val, val, sizeof(new_irk->val));
>> > + bacpy(&new_irk->rpa, rpa);
>> >
>> > - list_add_rcu(&irk->list, &hdev->identity_resolving_keys);
>> > + spin_lock_bh(&hdev->irk_lock);
>> > + list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
>> > + if (addr_type != irk->addr_type ||
>> > + bacmp(bdaddr, &irk->bdaddr))
>> > + continue;
>> > +
>> > + kref_get(&irk->ref);
>> > + memcpy(irk->val, val, sizeof(irk->val));
>> > + bacpy(&irk->rpa, rpa);
>> > + spin_unlock_bh(&hdev->irk_lock);
>> > + kfree(new_irk);
>> > + return irk;
>> > }
>> >
>> > - memcpy(irk->val, val, 16);
>> > - bacpy(&irk->rpa, rpa);
>> > + kref_init(&new_irk->ref);
>> > + kref_get(&new_irk->ref);
>> > + set_bit(SMP_IRK_LINKED, &new_irk->flags);
>> > + list_add_rcu(&new_irk->list, &hdev->identity_resolving_keys);
>> > + spin_unlock_bh(&hdev->irk_lock);
>> >
>> > - return irk;
>> > + return new_irk;
>> > }
>> >
>> > int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
>> > @@ -1372,16 +1483,27 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)
>> >
>> > void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type)
>> > {
>> > - struct smp_irk *k, *tmp;
>> > + struct smp_irk *k, *removed;
>> >
>> > - list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
>> > - if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type)
>> > - continue;
>> > + for (;;) {
>> > + removed = NULL;
>> > + spin_lock_bh(&hdev->irk_lock);
>> > + list_for_each_entry(k, &hdev->identity_resolving_keys, list) {
>> > + if (bacmp(bdaddr, &k->bdaddr) ||
>> > + k->addr_type != addr_type)
>> > + continue;
>> >
>> > - BT_DBG("%s removing %pMR", hdev->name, bdaddr);
>> > + __hci_irk_unlink(k);
>> > + removed = k;
>> > + break;
>> > + }
>> > + spin_unlock_bh(&hdev->irk_lock);
>> >
>> > - list_del_rcu(&k->list);
>> > - kfree_rcu(k, rcu);
>> > + if (!removed)
>> > + break;
>> > +
>> > + BT_DBG("%s removing %pMR", hdev->name, bdaddr);
>> > + hci_irk_put(removed);
>> > }
>> > }
>> >
>> > @@ -1389,6 +1511,8 @@ bool hci_bdaddr_is_paired(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
>> > {
>> > struct smp_ltk *k;
>> > struct smp_irk *irk;
>> > + struct smp_irk_data irk_data;
>> > + bdaddr_t identity_addr;
>> > u8 addr_type;
>> >
>> > if (type == BDADDR_BREDR) {
>> > @@ -1405,8 +1529,11 @@ bool hci_bdaddr_is_paired(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
>> >
>> > irk = hci_get_irk(hdev, bdaddr, addr_type);
>> > if (irk) {
>> > - bdaddr = &irk->bdaddr;
>> > - addr_type = irk->addr_type;
>> > + hci_irk_read(hdev, irk, &irk_data);
>> > + bacpy(&identity_addr, &irk_data.bdaddr);
>> > + addr_type = irk_data.addr_type;
>> > + hci_irk_put(irk);
>> > + bdaddr = &identity_addr;
>> > }
>> >
>> > rcu_read_lock();
>> > @@ -2495,6 +2622,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
>> > INIT_LIST_HEAD(&hdev->uuids);
>> > INIT_LIST_HEAD(&hdev->link_keys);
>> > INIT_LIST_HEAD(&hdev->long_term_keys);
>> > + spin_lock_init(&hdev->irk_lock);
>> > INIT_LIST_HEAD(&hdev->identity_resolving_keys);
>> > INIT_LIST_HEAD(&hdev->remote_oob_data);
>> > INIT_LIST_HEAD(&hdev->le_accept_list);
>> > diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c
>> > index aadffaaff20e..3b3f7a481990 100644
>> > --- a/net/bluetooth/hci_debugfs.c
>> > +++ b/net/bluetooth/hci_debugfs.c
>> > @@ -817,13 +817,15 @@ DEFINE_SHOW_ATTRIBUTE(resolv_list);
>> > static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
>> > {
>> > struct hci_dev *hdev = f->private;
>> > + struct smp_irk_data irk_data;
>> > struct smp_irk *irk;
>> >
>> > rcu_read_lock();
>> > list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
>> > + hci_irk_read(hdev, irk, &irk_data);
>> > seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
>> > - &irk->bdaddr, irk->addr_type,
>> > - 16, irk->val, &irk->rpa);
>> > + &irk_data.bdaddr, irk_data.addr_type,
>> > + 16, irk_data.val, &irk_data.rpa);
>> > }
>> > rcu_read_unlock();
>> >
>> > diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
>> > index 2f5e21ff9752..f2971319410d 100644
>> > --- a/net/bluetooth/hci_event.c
>> > +++ b/net/bluetooth/hci_event.c
>> > @@ -5757,6 +5757,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
>> > struct hci_conn_params *params;
>> > struct hci_conn *conn;
>> > struct smp_irk *irk;
>> > + struct smp_irk_data irk_data;
>> > u8 addr_type;
>> > int err;
>> >
>> > @@ -5842,8 +5843,10 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
>> > */
>> > irk = hci_get_irk(hdev, &conn->dst, conn->dst_type);
>> > if (irk) {
>> > - bacpy(&conn->dst, &irk->bdaddr);
>> > - conn->dst_type = irk->addr_type;
>> > + hci_irk_read(hdev, irk, &irk_data);
>> > + bacpy(&conn->dst, &irk_data.bdaddr);
>> > + conn->dst_type = irk_data.addr_type;
>> > + hci_irk_put(irk);
>> > }
>> >
>> > conn->dst_type = ev_bdaddr_type(hdev, conn->dst_type, NULL);
>> > @@ -6234,7 +6237,9 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
>> > {
>> > struct discovery_state *d = &hdev->discovery;
>> > struct smp_irk *irk;
>> > + struct smp_irk_data irk_data;
>> > struct hci_conn *conn;
>> > + bdaddr_t identity_addr;
>> > bool match, bdaddr_resolved;
>> > u32 flags;
>> > u8 *ptr;
>> > @@ -6309,8 +6314,11 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
>> > /* Check if we need to convert to identity address */
>> > irk = hci_get_irk(hdev, bdaddr, bdaddr_type);
>> > if (irk) {
>> > - bdaddr = &irk->bdaddr;
>> > - bdaddr_type = irk->addr_type;
>> > + hci_irk_read(hdev, irk, &irk_data);
>> > + bacpy(&identity_addr, &irk_data.bdaddr);
>> > + bdaddr_type = irk_data.addr_type;
>> > + hci_irk_put(irk);
>> > + bdaddr = &identity_addr;
>> > }
>> >
>> > bdaddr_type = ev_bdaddr_type(hdev, bdaddr_type, &bdaddr_resolved);
>> > diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
>> > index ffd7b37e7401..fcb96c4b0793 100644
>> > --- a/net/bluetooth/hci_sync.c
>> > +++ b/net/bluetooth/hci_sync.c
>> > @@ -2469,6 +2469,7 @@ static int hci_le_add_resolve_list_sync(struct hci_dev *hdev,
>> > {
>> > struct hci_cp_le_add_to_resolv_list cp;
>> > struct smp_irk *irk;
>> > + struct smp_irk_data irk_data;
>> > struct bdaddr_list_with_irk *entry;
>> > struct hci_conn_params *p;
>> >
>> > @@ -2496,12 +2497,16 @@ static int hci_le_add_resolve_list_sync(struct hci_dev *hdev,
>> > entry = hci_bdaddr_list_lookup_with_irk(&hdev->le_resolv_list,
>> > &params->addr,
>> > params->addr_type);
>> > - if (entry)
>> > + if (entry) {
>> > + hci_irk_put(irk);
>> > return 0;
>> > + }
>> >
>> > cp.bdaddr_type = params->addr_type;
>> > bacpy(&cp.bdaddr, &params->addr);
>> > - memcpy(cp.peer_irk, irk->val, 16);
>> > + hci_irk_read(hdev, irk, &irk_data);
>> > + memcpy(cp.peer_irk, irk_data.val, sizeof(cp.peer_irk));
>> > + hci_irk_put(irk);
>> >
>> > /* Default privacy mode is always Network */
>> > params->privacy_mode = HCI_NETWORK_PRIVACY;
>> > @@ -2532,6 +2537,7 @@ static int hci_le_set_privacy_mode_sync(struct hci_dev *hdev,
>> > {
>> > struct hci_cp_le_set_privacy_mode cp;
>> > struct smp_irk *irk;
>> > + struct smp_irk_data irk_data;
>> >
>> > if (!ll_privacy_capable(hdev) ||
>> > !(params->flags & HCI_CONN_FLAG_ADDRESS_RESOLUTION))
>> > @@ -2552,10 +2558,12 @@ static int hci_le_set_privacy_mode_sync(struct hci_dev *hdev,
>> > if (!irk)
>> > return 0;
>> >
>> > + hci_irk_read(hdev, irk, &irk_data);
>> > memset(&cp, 0, sizeof(cp));
>> > - cp.bdaddr_type = irk->addr_type;
>> > - bacpy(&cp.bdaddr, &irk->bdaddr);
>> > + cp.bdaddr_type = irk_data.addr_type;
>> > + bacpy(&cp.bdaddr, &irk_data.bdaddr);
>> > cp.mode = HCI_DEVICE_PRIVACY;
>> > + hci_irk_put(irk);
>> >
>> > /* Note: params->privacy_mode is not updated since it is a copy */
>> >
>> > diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
>> > index 75bfd5938b2e..928b761453d6 100644
>> > --- a/net/bluetooth/iso.c
>> > +++ b/net/bluetooth/iso.c
>> > @@ -723,22 +723,34 @@ static struct sock *iso_get_sock(struct hci_dev *hdev, bdaddr_t *src,
>> > /* Match Broadcast destination */
>> > if (bacmp(dst, BDADDR_ANY) && bacmp(&iso_pi(sk)->dst, dst)) {
>> > struct smp_irk *irk1, *irk2;
>> > + struct smp_irk_data irk_data;
>> > + bool resolved = false;
>> >
>> > /* Check if destination is an RPA that we can resolve */
>> > irk1 = hci_find_irk_by_rpa(hdev, dst);
>> > if (!irk1)
>> > continue;
>> >
>> > + hci_irk_read(hdev, irk1, &irk_data);
>> > +
>> > /* Match with identity address */
>> > - if (bacmp(&iso_pi(sk)->dst, &irk1->bdaddr)) {
>> > + if (!bacmp(&iso_pi(sk)->dst, &irk_data.bdaddr)) {
>> > + resolved = true;
>> > + } else {
>> > /* Check if socket destination address is also
>> > * an RPA and if the IRK matches.
>> > */
>> > irk2 = hci_find_irk_by_rpa(hdev,
>> > &iso_pi(sk)->dst);
>> > - if (!irk2 || irk1 != irk2)
>> > - continue;
>> > + if (irk2) {
>> > + resolved = irk1 == irk2;
>> > + hci_irk_put(irk2);
>> > + }
>> > }
>> > +
>> > + hci_irk_put(irk1);
>> > + if (!resolved)
>> > + continue;
>> > }
>> >
>> > /* Use Match function if provided */
>> > diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
>> > index ac4864e56ec7..841702cdf14e 100644
>> > --- a/net/bluetooth/mgmt.c
>> > +++ b/net/bluetooth/mgmt.c
>> > @@ -7315,6 +7315,7 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data,
>> >
>> > for (i = 0; i < irk_count; i++) {
>> > struct mgmt_irk_info *irk = &cp->irks[i];
>> > + struct smp_irk *smp_irk;
>> >
>> > if (hci_is_blocked_key(hdev,
>> > HCI_BLOCKED_KEY_TYPE_IRK,
>> > @@ -7324,9 +7325,11 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data,
>> > continue;
>> > }
>> >
>> > - hci_add_irk(hdev, &irk->addr.bdaddr,
>> > - le_addr_type(irk->addr.type), irk->val,
>> > - BDADDR_ANY);
>> > + smp_irk = hci_add_irk(hdev, &irk->addr.bdaddr,
>> > + le_addr_type(irk->addr.type), irk->val,
>> > + BDADDR_ANY);
>> > + if (smp_irk)
>> > + hci_irk_put(smp_irk);
>> > }
>> >
>> > hci_dev_set_flag(hdev, HCI_RPA_RESOLVING);
>> > @@ -9945,7 +9948,8 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent)
>> > mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL);
>> > }
>> >
>> > -void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk, bool persistent)
>> > +void mgmt_new_irk(struct hci_dev *hdev, const struct smp_irk_data *irk,
>> > + bool persistent)
>> > {
>> > struct mgmt_ev_new_irk ev;
>> >
>> > diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
>> > index 6091c47cb002..37000495b987 100644
>> > --- a/net/bluetooth/smp.c
>> > +++ b/net/bluetooth/smp.c
>> > @@ -761,11 +761,13 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
>> > }
>> >
>> > if (smp->remote_irk) {
>> > - list_del_rcu(&smp->remote_irk->list);
>> > - kfree_rcu(smp->remote_irk, rcu);
>> > + hci_irk_unlink(hcon->hdev, smp->remote_irk);
>> > }
>> > }
>> >
>> > + if (smp->remote_irk)
>> > + hci_irk_put(smp->remote_irk);
>> > +
>> > chan->data = NULL;
>> > kfree_sensitive(smp);
>> > hci_conn_drop(hcon);
>> > @@ -1017,6 +1019,7 @@ static void smp_notify_keys(struct l2cap_conn *conn)
>> > struct hci_dev *hdev = hcon->hdev;
>> > struct smp_cmd_pairing *req = (void *) &smp->preq[1];
>> > struct smp_cmd_pairing *rsp = (void *) &smp->prsp[1];
>> > + struct smp_irk_data irk_data;
>> > bool persistent;
>> >
>> > if (hcon->type == ACL_LINK) {
>> > @@ -1035,15 +1038,16 @@ static void smp_notify_keys(struct l2cap_conn *conn)
>> > }
>> >
>> > if (smp->remote_irk) {
>> > - mgmt_new_irk(hdev, smp->remote_irk, persistent);
>> > + hci_irk_read(hdev, smp->remote_irk, &irk_data);
>> > + mgmt_new_irk(hdev, &irk_data, persistent);
>> >
>> > /* Now that user space can be considered to know the
>> > * identity address track the connection based on it
>> > * from now on (assuming this is an LE link).
>> > */
>> > if (hcon->type == LE_LINK) {
>> > - bacpy(&hcon->dst, &smp->remote_irk->bdaddr);
>> > - hcon->dst_type = smp->remote_irk->addr_type;
>> > + bacpy(&hcon->dst, &irk_data.bdaddr);
>> > + hcon->dst_type = irk_data.addr_type;
>> > /* Use a short delay to make sure the new address is
>> > * propagated _before_ the channels.
>> > */
>> > @@ -2443,7 +2447,11 @@ int smp_cancel_and_remove_pairing(struct hci_dev *hdev, bdaddr_t *bdaddr,
>> > * remove and free already invalidated rcu list entries. */
>> > smp->ltk = NULL;
>> > smp->responder_ltk = NULL;
>> > - smp->remote_irk = NULL;
>> > + if (smp->remote_irk) {
>> > + hci_irk_unlink(hdev, smp->remote_irk);
>> > + hci_irk_put(smp->remote_irk);
>> > + smp->remote_irk = NULL;
>> > + }
>> >
>> > if (test_bit(SMP_FLAG_COMPLETE, &smp->flags))
>> > smp_failure(conn, 0);
>> > --
>> > 2.53.0
>> >
>>
>>
>> --
>> Luiz Augusto von Dentz