diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000000..60d318f805ff1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + checks: write + pull-requests: write + +jobs: + ci: + runs-on: ubuntu-latest + name: CI for Pull Request + steps: + - name: Checkout the source code + uses: actions/checkout@v3 + with: + path: src/src + + - name: CI + uses: bluez/action-ci@main + with: + task: ci + base_folder: src + space: kernel + github_token: ${{ secrets.GITHUB_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + patchwork_user: ${{ secrets.PATCHWORK_USER }} + diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 0000000000000..5e95af92ab1ab --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,44 @@ +name: Sync + +on: + schedule: + - cron: "*/5 * * * *" + +jobs: + sync_repo: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: master + + - name: Sync Repo + uses: bluez/action-ci@main + with: + task: sync + workflow: workflow + upstream_repo: 'https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git' + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Cleanup PR + uses: bluez/action-ci@main + with: + task: cleanup + github_token: ${{ secrets.ACTION_TOKEN }} + + sync_patchwork: + needs: sync_repo + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Sync Patchwork + uses: bluez/action-ci@main + with: + task: patchwork + workflow: workflow + space: kernel + github_token: ${{ secrets.ACTION_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + patchwork_user: ${{ secrets.PATCHWORK_USER }} diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c12cd6873f65e..a58fd64a00c0a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -24,6 +24,7 @@ #define __HCI_CORE_H #include +#include #include #include #include @@ -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; @@ -1929,11 +1940,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); @@ -2549,7 +2565,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 8de98af2fb581..5e19fc6ef9300 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 88df159d33937..0a1afc8926fff 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -38,6 +38,7 @@ #include #include "hci_debugfs.h" +#include "selftest.h" #include "smp.h" #include "leds.h" #include "msft.h" @@ -1031,13 +1032,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 +1195,99 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, return NULL; } +static void __hci_irk_read(struct smp_irk *irk, struct smp_irk_data *data) +{ + bacpy(&data->rpa, &irk->rpa); + bacpy(&data->bdaddr, &irk->bdaddr); + data->addr_type = irk->addr_type; + memcpy(data->val, irk->val, sizeof(data->val)); +} + +void hci_irk_read(struct hci_dev *hdev, struct smp_irk *irk, + struct smp_irk_data *data) +{ + spin_lock_bh(&hdev->irk_lock); + __hci_irk_read(irk, data); + 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; +} + +static bool hci_irk_get_by_rpa(struct hci_dev *hdev, struct smp_irk *irk, + const bdaddr_t *rpa, const u8 val[16], + struct smp_irk_data *data) +{ + bool match = false; + + spin_lock_bh(&hdev->irk_lock); + if (!test_bit(SMP_IRK_LINKED, &irk->flags)) + goto done; + + if (val) { + if (memcmp(irk->val, val, sizeof(irk->val))) + goto done; + + bacpy(&irk->rpa, rpa); + } else if (bacmp(&irk->rpa, rpa)) { + goto done; + } + + kref_get(&irk->ref); + __hci_irk_read(irk, data); + match = true; + +done: + spin_unlock_bh(&hdev->irk_lock); + return match; +} + 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)) { + if (hci_irk_get_by_rpa(hdev, irk, rpa, NULL, &data)) { 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_by_rpa(hdev, irk, rpa, data.val, &data)) { irk_to_return = irk; goto done; } } done: + rcu_read_unlock(); + 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 +1295,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 +1304,135 @@ 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); +} + +#if IS_ENABLED(CONFIG_BT_SELFTEST) +int __init bt_selftest_irk(void) +{ + static const u8 old_val[16] = { 0x11 }; + static const u8 new_val[16] = { 0x22 }; + static const bdaddr_t old_rpa = { { 1, 2, 3, 4, 5, 0x40 } }; + static const bdaddr_t new_rpa = { { 6, 7, 8, 9, 10, 0x40 } }; + struct smp_irk_data data; + struct hci_dev *hdev; + struct smp_irk *irk; + bool match; + int err = -EINVAL; + + hdev = kzalloc_obj(*hdev); + if (!hdev) + return -ENOMEM; + + irk = kzalloc_obj(*irk); + if (!irk) { + kfree(hdev); + return -ENOMEM; + } + + spin_lock_init(&hdev->irk_lock); + kref_init(&irk->ref); + set_bit(SMP_IRK_LINKED, &irk->flags); + memcpy(irk->val, new_val, sizeof(irk->val)); + bacpy(&irk->rpa, &new_rpa); + + /* A lookup that matched old_val before a rekey must not replace the + * new key's cached RPA or acquire a caller reference. + */ + match = hci_irk_get_by_rpa(hdev, irk, &old_rpa, old_val, + &data); + if (match) { + hci_irk_put(irk); + goto done; + } + + if (refcount_read(&irk->ref.refcount) != 1 || + bacmp(&irk->rpa, &new_rpa)) + goto done; + + match = hci_irk_get_by_rpa(hdev, irk, &old_rpa, NULL, + &data); + if (match) { + hci_irk_put(irk); + goto done; + } + if (refcount_read(&irk->ref.refcount) != 1) + goto done; + + /* The same cache update remains valid when the matched key is current. */ + match = hci_irk_get_by_rpa(hdev, irk, &old_rpa, new_val, + &data); + if (!match) + goto done; + + if (bacmp(&data.rpa, &old_rpa) || + memcmp(data.val, new_val, sizeof(data.val))) { + hci_irk_put(irk); + goto done; + } + hci_irk_put(irk); + + match = hci_irk_get_by_rpa(hdev, irk, &old_rpa, NULL, + &data); + if (!match) + goto done; + hci_irk_put(irk); + + err = 0; + BT_INFO("IRK lookup test passed"); + +done: + clear_bit(SMP_IRK_LINKED, &irk->flags); + hci_irk_put(irk); + kfree(hdev); + return err; +} +#endif + +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 +1512,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; + } - bacpy(&irk->bdaddr, bdaddr); - irk->addr_type = addr_type; + new_irk = kzalloc_obj(*new_irk); + if (!new_irk) + return NULL; + + 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); + + 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; - list_add_rcu(&irk->list, &hdev->identity_resolving_keys); + 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 +1592,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 +1620,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 +1638,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 +2731,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 aadffaaff20e1..3b3f7a4819903 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 2f5e21ff97529..f2971319410d2 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 24eeb76f72074..c6d5bdebf0aa0 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, ¶ms->addr, params->addr_type); - if (entry) + if (entry) { + hci_irk_put(irk); return 0; + } cp.bdaddr_type = params->addr_type; bacpy(&cp.bdaddr, ¶ms->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 75bfd5938b2ea..928b761453d6f 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 fd045460e2365..c98725b86462c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7327,6 +7327,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, @@ -7336,9 +7337,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); @@ -9957,7 +9960,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/selftest.c b/net/bluetooth/selftest.c index ae5b44bb9d3d3..5d30828e06688 100644 --- a/net/bluetooth/selftest.c +++ b/net/bluetooth/selftest.c @@ -270,6 +270,10 @@ static int __init run_selftest(void) if (err) goto done; + err = bt_selftest_irk(); + if (err) + goto done; + err = bt_selftest_smp(); done: diff --git a/net/bluetooth/selftest.h b/net/bluetooth/selftest.h index 34d684ee84fda..b18648fa490f2 100644 --- a/net/bluetooth/selftest.h +++ b/net/bluetooth/selftest.h @@ -40,3 +40,5 @@ static inline int bt_selftest(void) } #endif + +int bt_selftest_irk(void); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index f23b695c487bd..6bb8a884ac777 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -760,11 +760,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); @@ -1016,6 +1018,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) { @@ -1034,15 +1037,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. */ @@ -2442,7 +2446,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);