Skip to content

Commit

Permalink
bluetooth: don't use bitmaps for random flag accesses
Browse files Browse the repository at this point in the history
The bluetooth code uses our bitmap infrastructure for the two bits (!)
of connection setup flags, and in the process causes odd problems when
it converts between a bitmap and just the regular values of said bits.

It's completely pointless to do things like bitmap_to_arr32() to convert
a bitmap into a u32.  It shoudln't have been a bitmap in the first
place.  The reason to use bitmaps is if you have arbitrary number of
bits you want to manage (not two!), or if you rely on the atomicity
guarantees of the bitmap setting and clearing.

The code could use an "atomic_t" and use "atomic_or/andnot()" to set and
clear the bit values, but considering that it then copies the bitmaps
around with "bitmap_to_arr32()" and friends, there clearly cannot be a
lot of atomicity requirements.

So just use a regular integer.

In the process, this avoids the warnings about erroneous use of
bitmap_from_u64() which were triggered on 32-bit architectures when
conversion from a u64 would access two words (and, surprise, surprise,
only one word is needed - and indeed overkill - for a 2-bit bitmap).

That was always problematic, but the compiler seems to notice it and
warn about the invalid pattern only after commit 0a97953 ("lib: add
bitmap_{from,to}_arr64") changed the exact implementation details of
'bitmap_from_u64()', as reported by Sudip Mukherjee and Stephen Rothwell.

Fixes: fe92ee6 ("Bluetooth: hci_core: Rework hci_conn_params flags")
Link: https://lore.kernel.org/all/YpyJ9qTNHJzz0FHY@debian/
Link: https://lore.kernel.org/all/[email protected]/
Link: https://lore.kernel.org/all/[email protected]/
Reported-by: Stephen Rothwell <[email protected]>
Reported-by: Sudip Mukherjee <[email protected]>
Reviewed-by: Yury Norov <[email protected]>
Cc: Luiz Augusto von Dentz <[email protected]>
Cc: Marcel Holtmann <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
torvalds committed Jun 5, 2022
1 parent d717180 commit e1cff70
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 39 deletions.
17 changes: 7 additions & 10 deletions include/net/bluetooth/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,21 +155,18 @@ struct bdaddr_list_with_irk {
u8 local_irk[16];
};

/* Bitmask of connection flags */
enum hci_conn_flags {
HCI_CONN_FLAG_REMOTE_WAKEUP,
HCI_CONN_FLAG_DEVICE_PRIVACY,

__HCI_CONN_NUM_FLAGS,
HCI_CONN_FLAG_REMOTE_WAKEUP = 1,
HCI_CONN_FLAG_DEVICE_PRIVACY = 2,
};

/* Make sure number of flags doesn't exceed sizeof(current_flags) */
static_assert(__HCI_CONN_NUM_FLAGS < 32);
typedef u8 hci_conn_flags_t;

struct bdaddr_list_with_flags {
struct list_head list;
bdaddr_t bdaddr;
u8 bdaddr_type;
DECLARE_BITMAP(flags, __HCI_CONN_NUM_FLAGS);
hci_conn_flags_t flags;
};

struct bt_uuid {
Expand Down Expand Up @@ -576,7 +573,7 @@ struct hci_dev {
struct rfkill *rfkill;

DECLARE_BITMAP(dev_flags, __HCI_NUM_FLAGS);
DECLARE_BITMAP(conn_flags, __HCI_CONN_NUM_FLAGS);
hci_conn_flags_t conn_flags;

__s8 adv_tx_power;
__u8 adv_data[HCI_MAX_EXT_AD_LENGTH];
Expand Down Expand Up @@ -775,7 +772,7 @@ struct hci_conn_params {

struct hci_conn *conn;
bool explicit_connect;
DECLARE_BITMAP(flags, __HCI_CONN_NUM_FLAGS);
hci_conn_flags_t flags;
u8 privacy_mode;
};

Expand Down
4 changes: 2 additions & 2 deletions net/bluetooth/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2153,7 +2153,7 @@ int hci_bdaddr_list_add_with_flags(struct list_head *list, bdaddr_t *bdaddr,

bacpy(&entry->bdaddr, bdaddr);
entry->bdaddr_type = type;
bitmap_from_u64(entry->flags, flags);
entry->flags = flags;

list_add(&entry->list, list);

Expand Down Expand Up @@ -2634,7 +2634,7 @@ int hci_register_dev(struct hci_dev *hdev)
* callback.
*/
if (hdev->wakeup)
set_bit(HCI_CONN_FLAG_REMOTE_WAKEUP, hdev->conn_flags);
hdev->conn_flags |= HCI_CONN_FLAG_REMOTE_WAKEUP;

hci_sock_dev_event(hdev, HCI_DEV_REG);
hci_dev_hold(hdev);
Expand Down
2 changes: 1 addition & 1 deletion net/bluetooth/hci_request.c
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ static int add_to_accept_list(struct hci_request *req,

/* During suspend, only wakeable devices can be in accept list */
if (hdev->suspended &&
!test_bit(HCI_CONN_FLAG_REMOTE_WAKEUP, params->flags))
!(params->flags & HCI_CONN_FLAG_REMOTE_WAKEUP))
return 0;

*num_entries += 1;
Expand Down
6 changes: 3 additions & 3 deletions net/bluetooth/hci_sync.c
Original file line number Diff line number Diff line change
Expand Up @@ -1637,7 +1637,7 @@ static int hci_le_set_privacy_mode_sync(struct hci_dev *hdev,
* indicates that LL Privacy has been enabled and
* HCI_OP_LE_SET_PRIVACY_MODE is supported.
*/
if (!test_bit(HCI_CONN_FLAG_DEVICE_PRIVACY, params->flags))
if (!(params->flags & HCI_CONN_FLAG_DEVICE_PRIVACY))
return 0;

irk = hci_find_irk_by_addr(hdev, &params->addr, params->addr_type);
Expand Down Expand Up @@ -1666,7 +1666,7 @@ static int hci_le_add_accept_list_sync(struct hci_dev *hdev,

/* During suspend, only wakeable devices can be in acceptlist */
if (hdev->suspended &&
!test_bit(HCI_CONN_FLAG_REMOTE_WAKEUP, params->flags))
!(params->flags & HCI_CONN_FLAG_REMOTE_WAKEUP))
return 0;

/* Select filter policy to accept all advertising */
Expand Down Expand Up @@ -4888,7 +4888,7 @@ static int hci_update_event_filter_sync(struct hci_dev *hdev)
hci_clear_event_filter_sync(hdev);

list_for_each_entry(b, &hdev->accept_list, list) {
if (!test_bit(HCI_CONN_FLAG_REMOTE_WAKEUP, b->flags))
if (!(b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP))
continue;

bt_dev_dbg(hdev, "Adding event filters for %pMR", &b->bdaddr);
Expand Down
37 changes: 14 additions & 23 deletions net/bluetooth/mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -4013,10 +4013,11 @@ static int exp_ll_privacy_feature_changed(bool enabled, struct hci_dev *hdev,
memcpy(ev.uuid, rpa_resolution_uuid, 16);
ev.flags = cpu_to_le32((enabled ? BIT(0) : 0) | BIT(1));

// Do we need to be atomic with the conn_flags?
if (enabled && privacy_mode_capable(hdev))
set_bit(HCI_CONN_FLAG_DEVICE_PRIVACY, hdev->conn_flags);
hdev->conn_flags |= HCI_CONN_FLAG_DEVICE_PRIVACY;
else
clear_bit(HCI_CONN_FLAG_DEVICE_PRIVACY, hdev->conn_flags);
hdev->conn_flags &= ~HCI_CONN_FLAG_DEVICE_PRIVACY;

return mgmt_limited_event(MGMT_EV_EXP_FEATURE_CHANGED, hdev,
&ev, sizeof(ev),
Expand Down Expand Up @@ -4435,8 +4436,7 @@ static int get_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,

hci_dev_lock(hdev);

bitmap_to_arr32(&supported_flags, hdev->conn_flags,
__HCI_CONN_NUM_FLAGS);
supported_flags = hdev->conn_flags;

memset(&rp, 0, sizeof(rp));

Expand All @@ -4447,17 +4447,15 @@ static int get_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,
if (!br_params)
goto done;

bitmap_to_arr32(&current_flags, br_params->flags,
__HCI_CONN_NUM_FLAGS);
current_flags = br_params->flags;
} else {
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
le_addr_type(cp->addr.type));

if (!params)
goto done;

bitmap_to_arr32(&current_flags, params->flags,
__HCI_CONN_NUM_FLAGS);
current_flags = params->flags;
}

bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
Expand Down Expand Up @@ -4502,8 +4500,8 @@ static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,
&cp->addr.bdaddr, cp->addr.type,
__le32_to_cpu(current_flags));

bitmap_to_arr32(&supported_flags, hdev->conn_flags,
__HCI_CONN_NUM_FLAGS);
// We should take hci_dev_lock() early, I think.. conn_flags can change
supported_flags = hdev->conn_flags;

if ((supported_flags | current_flags) != supported_flags) {
bt_dev_warn(hdev, "Bad flag given (0x%x) vs supported (0x%0x)",
Expand All @@ -4519,7 +4517,7 @@ static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,
cp->addr.type);

if (br_params) {
bitmap_from_u64(br_params->flags, current_flags);
br_params->flags = current_flags;
status = MGMT_STATUS_SUCCESS;
} else {
bt_dev_warn(hdev, "No such BR/EDR device %pMR (0x%x)",
Expand All @@ -4529,15 +4527,11 @@ static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
le_addr_type(cp->addr.type));
if (params) {
DECLARE_BITMAP(flags, __HCI_CONN_NUM_FLAGS);

bitmap_from_u64(flags, current_flags);

/* Devices using RPAs can only be programmed in the
* acceptlist LL Privacy has been enable otherwise they
* cannot mark HCI_CONN_FLAG_REMOTE_WAKEUP.
*/
if (test_bit(HCI_CONN_FLAG_REMOTE_WAKEUP, flags) &&
if ((current_flags & HCI_CONN_FLAG_REMOTE_WAKEUP) &&
!use_ll_privacy(hdev) &&
hci_find_irk_by_addr(hdev, &params->addr,
params->addr_type)) {
Expand All @@ -4546,14 +4540,13 @@ static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock;
}

bitmap_from_u64(params->flags, current_flags);
params->flags = current_flags;
status = MGMT_STATUS_SUCCESS;

/* Update passive scan if HCI_CONN_FLAG_DEVICE_PRIVACY
* has been set.
*/
if (test_bit(HCI_CONN_FLAG_DEVICE_PRIVACY,
params->flags))
if (params->flags & HCI_CONN_FLAG_DEVICE_PRIVACY)
hci_update_passive_scan(hdev);
} else {
bt_dev_warn(hdev, "No such LE device %pMR (0x%x)",
Expand Down Expand Up @@ -7154,8 +7147,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
addr_type);
if (params)
bitmap_to_arr32(&current_flags, params->flags,
__HCI_CONN_NUM_FLAGS);
current_flags = params->flags;
}

err = hci_cmd_sync_queue(hdev, add_device_sync, NULL, NULL);
Expand All @@ -7164,8 +7156,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,

added:
device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action);
bitmap_to_arr32(&supported_flags, hdev->conn_flags,
__HCI_CONN_NUM_FLAGS);
supported_flags = hdev->conn_flags;
device_flags_changed(NULL, hdev, &cp->addr.bdaddr, cp->addr.type,
supported_flags, current_flags);

Expand Down

0 comments on commit e1cff70

Please sign in to comment.