Commit 302a1f67 authored by Luiz Augusto von Dentz's avatar Luiz Augusto von Dentz
Browse files

Bluetooth: MGMT: Fix possible UAFs



This attemps to fix possible UAFs caused by struct mgmt_pending being
freed while still being processed like in the following trace, in order
to fix mgmt_pending_valid is introduce and use to check if the
mgmt_pending hasn't been removed from the pending list, on the complete
callbacks it is used to check and in addtion remove the cmd from the list
while holding mgmt_pending_lock to avoid TOCTOU problems since if the cmd
is left on the list it can still be accessed and freed.

BUG: KASAN: slab-use-after-free in mgmt_add_adv_patterns_monitor_sync+0x35/0x50 net/bluetooth/mgmt.c:5223
Read of size 8 at addr ffff8880709d4dc0 by task kworker/u11:0/55

CPU: 0 UID: 0 PID: 55 Comm: kworker/u11:0 Not tainted 6.16.4 #2 PREEMPT(full)
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
Workqueue: hci0 hci_cmd_sync_work
Call Trace:
 <TASK>
 dump_stack_lvl+0x189/0x250 lib/dump_stack.c:120
 print_address_description mm/kasan/report.c:378 [inline]
 print_report+0xca/0x240 mm/kasan/report.c:482
 kasan_report+0x118/0x150 mm/kasan/report.c:595
 mgmt_add_adv_patterns_monitor_sync+0x35/0x50 net/bluetooth/mgmt.c:5223
 hci_cmd_sync_work+0x210/0x3a0 net/bluetooth/hci_sync.c:332
 process_one_work kernel/workqueue.c:3238 [inline]
 process_scheduled_works+0xade/0x17b0 kernel/workqueue.c:3321
 worker_thread+0x8a0/0xda0 kernel/workqueue.c:3402
 kthread+0x711/0x8a0 kernel/kthread.c:464
 ret_from_fork+0x3fc/0x770 arch/x86/kernel/process.c:148
 ret_from_fork_asm+0x1a/0x30 home/kwqcheii/source/fuzzing/kernel/kasan/linux-6.16.4/arch/x86/entry/entry_64.S:245
 </TASK>

Allocated by task 12210:
 kasan_save_stack mm/kasan/common.c:47 [inline]
 kasan_save_track+0x3e/0x80 mm/kasan/common.c:68
 poison_kmalloc_redzone mm/kasan/common.c:377 [inline]
 __kasan_kmalloc+0x93/0xb0 mm/kasan/common.c:394
 kasan_kmalloc include/linux/kasan.h:260 [inline]
 __kmalloc_cache_noprof+0x230/0x3d0 mm/slub.c:4364
 kmalloc_noprof include/linux/slab.h:905 [inline]
 kzalloc_noprof include/linux/slab.h:1039 [inline]
 mgmt_pending_new+0x65/0x1e0 net/bluetooth/mgmt_util.c:269
 mgmt_pending_add+0x35/0x140 net/bluetooth/mgmt_util.c:296
 __add_adv_patterns_monitor+0x130/0x200 net/bluetooth/mgmt.c:5247
 add_adv_patterns_monitor+0x214/0x360 net/bluetooth/mgmt.c:5364
 hci_mgmt_cmd+0x9c9/0xef0 net/bluetooth/hci_sock.c:1719
 hci_sock_sendmsg+0x6ca/0xef0 net/bluetooth/hci_sock.c:1839
 sock_sendmsg_nosec net/socket.c:714 [inline]
 __sock_sendmsg+0x219/0x270 net/socket.c:729
 sock_write_iter+0x258/0x330 net/socket.c:1133
 new_sync_write fs/read_write.c:593 [inline]
 vfs_write+0x5c9/0xb30 fs/read_write.c:686
 ksys_write+0x145/0x250 fs/read_write.c:738
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Freed by task 12221:
 kasan_save_stack mm/kasan/common.c:47 [inline]
 kasan_save_track+0x3e/0x80 mm/kasan/common.c:68
 kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:576
 poison_slab_object mm/kasan/common.c:247 [inline]
 __kasan_slab_free+0x62/0x70 mm/kasan/common.c:264
 kasan_slab_free include/linux/kasan.h:233 [inline]
 slab_free_hook mm/slub.c:2381 [inline]
 slab_free mm/slub.c:4648 [inline]
 kfree+0x18e/0x440 mm/slub.c:4847
 mgmt_pending_free net/bluetooth/mgmt_util.c:311 [inline]
 mgmt_pending_foreach+0x30d/0x380 net/bluetooth/mgmt_util.c:257
 __mgmt_power_off+0x169/0x350 net/bluetooth/mgmt.c:9444
 hci_dev_close_sync+0x754/0x1330 net/bluetooth/hci_sync.c:5290
 hci_dev_do_close net/bluetooth/hci_core.c:501 [inline]
 hci_dev_close+0x108/0x200 net/bluetooth/hci_core.c:526
 sock_do_ioctl+0xd9/0x300 net/socket.c:1192
 sock_ioctl+0x576/0x790 net/socket.c:1313
 vfs_ioctl fs/ioctl.c:51 [inline]
 __do_sys_ioctl fs/ioctl.c:907 [inline]
 __se_sys_ioctl+0xf9/0x170 fs/ioctl.c:893
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Fixes: cf75ad8b ("Bluetooth: hci_sync: Convert MGMT_SET_POWERED")
Fixes: 2bd1b237 ("Bluetooth: hci_sync: Convert MGMT_OP_SET_DISCOVERABLE to use cmd_sync")
Fixes: f056a657 ("Bluetooth: hci_sync: Convert MGMT_OP_SET_CONNECTABLE to use cmd_sync")
Fixes: 3244845c ("Bluetooth: hci_sync: Convert MGMT_OP_SSP")
Fixes: d81a494c ("Bluetooth: hci_sync: Convert MGMT_OP_SET_LE")
Fixes: b338d917 ("Bluetooth: Implement support for Mesh")
Fixes: 6f6ff38a ("Bluetooth: hci_sync: Convert MGMT_OP_SET_LOCAL_NAME")
Fixes: 71efbb08 ("Bluetooth: hci_sync: Convert MGMT_OP_SET_PHY_CONFIGURATION")
Fixes: b747a836 ("Bluetooth: hci_sync: Refactor add Adv Monitor")
Fixes: abfeea47 ("Bluetooth: hci_sync: Convert MGMT_OP_START_DISCOVERY")
Fixes: 26ac4c56 ("Bluetooth: hci_sync: Convert MGMT_OP_SET_ADVERTISING")
Reported-by: default avatarcen zhang <zzzccc427@gmail.com>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent 9e622804
Loading
Loading
Loading
Loading
+182 −77
Original line number Diff line number Diff line
@@ -1323,8 +1323,7 @@ static void mgmt_set_powered_complete(struct hci_dev *hdev, void *data, int err)
	struct mgmt_mode *cp;

	/* Make sure cmd still outstanding. */
	if (err == -ECANCELED ||
	    cmd != pending_find(MGMT_OP_SET_POWERED, hdev))
	if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
		return;

	cp = cmd->param;
@@ -1351,23 +1350,29 @@ static void mgmt_set_powered_complete(struct hci_dev *hdev, void *data, int err)
				mgmt_status(err));
	}

	mgmt_pending_remove(cmd);
	mgmt_pending_free(cmd);
}

static int set_powered_sync(struct hci_dev *hdev, void *data)
{
	struct mgmt_pending_cmd *cmd = data;
	struct mgmt_mode *cp;
	struct mgmt_mode cp;

	mutex_lock(&hdev->mgmt_pending_lock);

	/* Make sure cmd still outstanding. */
	if (cmd != pending_find(MGMT_OP_SET_POWERED, hdev))
	if (!__mgmt_pending_listed(hdev, cmd)) {
		mutex_unlock(&hdev->mgmt_pending_lock);
		return -ECANCELED;
	}

	cp = cmd->param;
	memcpy(&cp, cmd->param, sizeof(cp));

	mutex_unlock(&hdev->mgmt_pending_lock);

	BT_DBG("%s", hdev->name);

	return hci_set_powered_sync(hdev, cp->val);
	return hci_set_powered_sync(hdev, cp.val);
}

static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -1516,8 +1521,7 @@ static void mgmt_set_discoverable_complete(struct hci_dev *hdev, void *data,
	bt_dev_dbg(hdev, "err %d", err);

	/* Make sure cmd still outstanding. */
	if (err == -ECANCELED ||
	    cmd != pending_find(MGMT_OP_SET_DISCOVERABLE, hdev))
	if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
		return;

	hci_dev_lock(hdev);
@@ -1539,12 +1543,15 @@ static void mgmt_set_discoverable_complete(struct hci_dev *hdev, void *data,
	new_settings(hdev, cmd->sk);

done:
	mgmt_pending_remove(cmd);
	mgmt_pending_free(cmd);
	hci_dev_unlock(hdev);
}

static int set_discoverable_sync(struct hci_dev *hdev, void *data)
{
	if (!mgmt_pending_listed(hdev, data))
		return -ECANCELED;

	BT_DBG("%s", hdev->name);

	return hci_update_discoverable_sync(hdev);
@@ -1691,8 +1698,7 @@ static void mgmt_set_connectable_complete(struct hci_dev *hdev, void *data,
	bt_dev_dbg(hdev, "err %d", err);

	/* Make sure cmd still outstanding. */
	if (err == -ECANCELED ||
	    cmd != pending_find(MGMT_OP_SET_CONNECTABLE, hdev))
	if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
		return;

	hci_dev_lock(hdev);
@@ -1707,7 +1713,7 @@ static void mgmt_set_connectable_complete(struct hci_dev *hdev, void *data,
	new_settings(hdev, cmd->sk);

done:
	mgmt_pending_remove(cmd);
	mgmt_pending_free(cmd);

	hci_dev_unlock(hdev);
}
@@ -1743,6 +1749,9 @@ static int set_connectable_update_settings(struct hci_dev *hdev,

static int set_connectable_sync(struct hci_dev *hdev, void *data)
{
	if (!mgmt_pending_listed(hdev, data))
		return -ECANCELED;

	BT_DBG("%s", hdev->name);

	return hci_update_connectable_sync(hdev);
@@ -1919,14 +1928,17 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
{
	struct cmd_lookup match = { NULL, hdev };
	struct mgmt_pending_cmd *cmd = data;
	struct mgmt_mode *cp = cmd->param;
	u8 enable = cp->val;
	struct mgmt_mode *cp;
	u8 enable;
	bool changed;

	/* Make sure cmd still outstanding. */
	if (err == -ECANCELED || cmd != pending_find(MGMT_OP_SET_SSP, hdev))
	if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
		return;

	cp = cmd->param;
	enable = cp->val;

	if (err) {
		u8 mgmt_err = mgmt_status(err);

@@ -1935,8 +1947,7 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
			new_settings(hdev, NULL);
		}

		mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, true,
				     cmd_status_rsp, &mgmt_err);
		mgmt_cmd_status(cmd->sk, cmd->hdev->id, cmd->opcode, mgmt_err);
		return;
	}

@@ -1946,7 +1957,7 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
		changed = hci_dev_test_and_clear_flag(hdev, HCI_SSP_ENABLED);
	}

	mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, true, settings_rsp, &match);
	settings_rsp(cmd, &match);

	if (changed)
		new_settings(hdev, match.sk);
@@ -1960,14 +1971,25 @@ static void set_ssp_complete(struct hci_dev *hdev, void *data, int err)
static int set_ssp_sync(struct hci_dev *hdev, void *data)
{
	struct mgmt_pending_cmd *cmd = data;
	struct mgmt_mode *cp = cmd->param;
	struct mgmt_mode cp;
	bool changed = false;
	int err;

	if (cp->val)
	mutex_lock(&hdev->mgmt_pending_lock);

	if (!__mgmt_pending_listed(hdev, cmd)) {
		mutex_unlock(&hdev->mgmt_pending_lock);
		return -ECANCELED;
	}

	memcpy(&cp, cmd->param, sizeof(cp));

	mutex_unlock(&hdev->mgmt_pending_lock);

	if (cp.val)
		changed = !hci_dev_test_and_set_flag(hdev, HCI_SSP_ENABLED);

	err = hci_write_ssp_mode_sync(hdev, cp->val);
	err = hci_write_ssp_mode_sync(hdev, cp.val);

	if (!err && changed)
		hci_dev_clear_flag(hdev, HCI_SSP_ENABLED);
@@ -2060,32 +2082,50 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)

static void set_le_complete(struct hci_dev *hdev, void *data, int err)
{
	struct mgmt_pending_cmd *cmd = data;
	struct cmd_lookup match = { NULL, hdev };
	u8 status = mgmt_status(err);

	bt_dev_dbg(hdev, "err %d", err);

	if (status) {
		mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, true, cmd_status_rsp,
				     &status);
	if (err == -ECANCELED || !mgmt_pending_valid(hdev, data))
		return;

	if (status) {
		mgmt_cmd_status(cmd->sk, cmd->hdev->id, cmd->opcode, status);
		goto done;
	}

	mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, true, settings_rsp, &match);
	settings_rsp(cmd, &match);

	new_settings(hdev, match.sk);

	if (match.sk)
		sock_put(match.sk);

done:
	mgmt_pending_free(cmd);
}

static int set_le_sync(struct hci_dev *hdev, void *data)
{
	struct mgmt_pending_cmd *cmd = data;
	struct mgmt_mode *cp = cmd->param;
	u8 val = !!cp->val;
	struct mgmt_mode cp;
	u8 val;
	int err;

	mutex_lock(&hdev->mgmt_pending_lock);

	if (!__mgmt_pending_listed(hdev, cmd)) {
		mutex_unlock(&hdev->mgmt_pending_lock);
		return -ECANCELED;
	}

	memcpy(&cp, cmd->param, sizeof(cp));
	val = !!cp.val;

	mutex_unlock(&hdev->mgmt_pending_lock);

	if (!val) {
		hci_clear_adv_instance_sync(hdev, NULL, 0x00, true);

@@ -2127,7 +2167,12 @@ static void set_mesh_complete(struct hci_dev *hdev, void *data, int err)
{
	struct mgmt_pending_cmd *cmd = data;
	u8 status = mgmt_status(err);
	struct sock *sk = cmd->sk;
	struct sock *sk;

	if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
		return;

	sk = cmd->sk;

	if (status) {
		mgmt_pending_foreach(MGMT_OP_SET_MESH_RECEIVER, hdev, true,
@@ -2142,24 +2187,37 @@ static void set_mesh_complete(struct hci_dev *hdev, void *data, int err)
static int set_mesh_sync(struct hci_dev *hdev, void *data)
{
	struct mgmt_pending_cmd *cmd = data;
	struct mgmt_cp_set_mesh *cp = cmd->param;
	size_t len = cmd->param_len;
	struct mgmt_cp_set_mesh cp;
	size_t len;

	mutex_lock(&hdev->mgmt_pending_lock);

	if (!__mgmt_pending_listed(hdev, cmd)) {
		mutex_unlock(&hdev->mgmt_pending_lock);
		return -ECANCELED;
	}

	memcpy(&cp, cmd->param, sizeof(cp));

	mutex_unlock(&hdev->mgmt_pending_lock);

	len = cmd->param_len;

	memset(hdev->mesh_ad_types, 0, sizeof(hdev->mesh_ad_types));

	if (cp->enable)
	if (cp.enable)
		hci_dev_set_flag(hdev, HCI_MESH);
	else
		hci_dev_clear_flag(hdev, HCI_MESH);

	hdev->le_scan_interval = __le16_to_cpu(cp->period);
	hdev->le_scan_window = __le16_to_cpu(cp->window);
	hdev->le_scan_interval = __le16_to_cpu(cp.period);
	hdev->le_scan_window = __le16_to_cpu(cp.window);

	len -= sizeof(*cp);
	len -= sizeof(cp);

	/* If filters don't fit, forward all adv pkts */
	if (len <= sizeof(hdev->mesh_ad_types))
		memcpy(hdev->mesh_ad_types, cp->ad_types, len);
		memcpy(hdev->mesh_ad_types, cp.ad_types, len);

	hci_update_passive_scan_sync(hdev);
	return 0;
@@ -3867,15 +3925,16 @@ static int name_changed_sync(struct hci_dev *hdev, void *data)
static void set_name_complete(struct hci_dev *hdev, void *data, int err)
{
	struct mgmt_pending_cmd *cmd = data;
	struct mgmt_cp_set_local_name *cp = cmd->param;
	struct mgmt_cp_set_local_name *cp;
	u8 status = mgmt_status(err);

	bt_dev_dbg(hdev, "err %d", err);

	if (err == -ECANCELED ||
	    cmd != pending_find(MGMT_OP_SET_LOCAL_NAME, hdev))
	if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
		return;

	cp = cmd->param;

	if (status) {
		mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
				status);
@@ -3887,16 +3946,27 @@ static void set_name_complete(struct hci_dev *hdev, void *data, int err)
			hci_cmd_sync_queue(hdev, name_changed_sync, NULL, NULL);
	}

	mgmt_pending_remove(cmd);
	mgmt_pending_free(cmd);
}

static int set_name_sync(struct hci_dev *hdev, void *data)
{
	struct mgmt_pending_cmd *cmd = data;
	struct mgmt_cp_set_local_name *cp = cmd->param;
	struct mgmt_cp_set_local_name cp;

	mutex_lock(&hdev->mgmt_pending_lock);

	if (!__mgmt_pending_listed(hdev, cmd)) {
		mutex_unlock(&hdev->mgmt_pending_lock);
		return -ECANCELED;
	}

	memcpy(&cp, cmd->param, sizeof(cp));

	mutex_unlock(&hdev->mgmt_pending_lock);

	if (lmp_bredr_capable(hdev)) {
		hci_update_name_sync(hdev, cp->name);
		hci_update_name_sync(hdev, cp.name);
		hci_update_eir_sync(hdev);
	}

@@ -4048,12 +4118,10 @@ int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip)
static void set_default_phy_complete(struct hci_dev *hdev, void *data, int err)
{
	struct mgmt_pending_cmd *cmd = data;
	struct sk_buff *skb = cmd->skb;
	struct sk_buff *skb;
	u8 status = mgmt_status(err);

	if (err == -ECANCELED ||
	    cmd != pending_find(MGMT_OP_SET_PHY_CONFIGURATION, hdev))
		return;
	skb = cmd->skb;

	if (!status) {
		if (!skb)
@@ -4080,7 +4148,7 @@ static void set_default_phy_complete(struct hci_dev *hdev, void *data, int err)
	if (skb && !IS_ERR(skb))
		kfree_skb(skb);

	mgmt_pending_remove(cmd);
	mgmt_pending_free(cmd);
}

static int set_default_phy_sync(struct hci_dev *hdev, void *data)
@@ -4088,7 +4156,9 @@ static int set_default_phy_sync(struct hci_dev *hdev, void *data)
	struct mgmt_pending_cmd *cmd = data;
	struct mgmt_cp_set_phy_configuration *cp = cmd->param;
	struct hci_cp_le_set_default_phy cp_phy;
	u32 selected_phys = __le32_to_cpu(cp->selected_phys);
	u32 selected_phys;

	selected_phys = __le32_to_cpu(cp->selected_phys);

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

@@ -4228,7 +4298,7 @@ static int set_phy_configuration(struct sock *sk, struct hci_dev *hdev,
		goto unlock;
	}

	cmd = mgmt_pending_add(sk, MGMT_OP_SET_PHY_CONFIGURATION, hdev, data,
	cmd = mgmt_pending_new(sk, MGMT_OP_SET_PHY_CONFIGURATION, hdev, data,
			       len);
	if (!cmd)
		err = -ENOMEM;
@@ -5189,7 +5259,17 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
{
	struct mgmt_rp_add_adv_patterns_monitor rp;
	struct mgmt_pending_cmd *cmd = data;
	struct adv_monitor *monitor = cmd->user_data;
	struct adv_monitor *monitor;

	/* This is likely the result of hdev being closed and mgmt_index_removed
	 * is attempting to clean up any pending command so
	 * hci_adv_monitors_clear is about to be called which will take care of
	 * freeing the adv_monitor instances.
	 */
	if (status == -ECANCELED && !mgmt_pending_valid(hdev, cmd))
		return;

	monitor = cmd->user_data;

	hci_dev_lock(hdev);

@@ -5215,9 +5295,20 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
static int mgmt_add_adv_patterns_monitor_sync(struct hci_dev *hdev, void *data)
{
	struct mgmt_pending_cmd *cmd = data;
	struct adv_monitor *monitor = cmd->user_data;
	struct adv_monitor *mon;

	mutex_lock(&hdev->mgmt_pending_lock);

	return hci_add_adv_monitor(hdev, monitor);
	if (!__mgmt_pending_listed(hdev, cmd)) {
		mutex_unlock(&hdev->mgmt_pending_lock);
		return -ECANCELED;
	}

	mon = cmd->user_data;

	mutex_unlock(&hdev->mgmt_pending_lock);

	return hci_add_adv_monitor(hdev, mon);
}

static int __add_adv_patterns_monitor(struct sock *sk, struct hci_dev *hdev,
@@ -5484,7 +5575,8 @@ static int remove_adv_monitor(struct sock *sk, struct hci_dev *hdev,
			       status);
}

static void read_local_oob_data_complete(struct hci_dev *hdev, void *data, int err)
static void read_local_oob_data_complete(struct hci_dev *hdev, void *data,
					 int err)
{
	struct mgmt_rp_read_local_oob_data mgmt_rp;
	size_t rp_size = sizeof(mgmt_rp);
@@ -5504,7 +5596,8 @@ static void read_local_oob_data_complete(struct hci_dev *hdev, void *data, int e
	bt_dev_dbg(hdev, "status %d", status);

	if (status) {
		mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, status);
		mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
				status);
		goto remove;
	}

@@ -5786,17 +5879,12 @@ static void start_discovery_complete(struct hci_dev *hdev, void *data, int err)

	bt_dev_dbg(hdev, "err %d", err);

	if (err == -ECANCELED)
		return;

	if (cmd != pending_find(MGMT_OP_START_DISCOVERY, hdev) &&
	    cmd != pending_find(MGMT_OP_START_LIMITED_DISCOVERY, hdev) &&
	    cmd != pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev))
	if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
		return;

	mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, mgmt_status(err),
			  cmd->param, 1);
	mgmt_pending_remove(cmd);
	mgmt_pending_free(cmd);

	hci_discovery_set_state(hdev, err ? DISCOVERY_STOPPED:
				DISCOVERY_FINDING);
@@ -5804,6 +5892,9 @@ static void start_discovery_complete(struct hci_dev *hdev, void *data, int err)

static int start_discovery_sync(struct hci_dev *hdev, void *data)
{
	if (!mgmt_pending_listed(hdev, data))
		return -ECANCELED;

	return hci_start_discovery_sync(hdev);
}

@@ -6009,15 +6100,14 @@ static void stop_discovery_complete(struct hci_dev *hdev, void *data, int err)
{
	struct mgmt_pending_cmd *cmd = data;

	if (err == -ECANCELED ||
	    cmd != pending_find(MGMT_OP_STOP_DISCOVERY, hdev))
	if (err == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
		return;

	bt_dev_dbg(hdev, "err %d", err);

	mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, mgmt_status(err),
			  cmd->param, 1);
	mgmt_pending_remove(cmd);
	mgmt_pending_free(cmd);

	if (!err)
		hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
@@ -6025,6 +6115,9 @@ static void stop_discovery_complete(struct hci_dev *hdev, void *data, int err)

static int stop_discovery_sync(struct hci_dev *hdev, void *data)
{
	if (!mgmt_pending_listed(hdev, data))
		return -ECANCELED;

	return hci_stop_discovery_sync(hdev);
}

@@ -6234,14 +6327,18 @@ static void enable_advertising_instance(struct hci_dev *hdev, int err)

static void set_advertising_complete(struct hci_dev *hdev, void *data, int err)
{
	struct mgmt_pending_cmd *cmd = data;
	struct cmd_lookup match = { NULL, hdev };
	u8 instance;
	struct adv_info *adv_instance;
	u8 status = mgmt_status(err);

	if (err == -ECANCELED || !mgmt_pending_valid(hdev, data))
		return;

	if (status) {
		mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, true,
				     cmd_status_rsp, &status);
		mgmt_cmd_status(cmd->sk, cmd->hdev->id, cmd->opcode, status);
		mgmt_pending_free(cmd);
		return;
	}

@@ -6250,8 +6347,7 @@ static void set_advertising_complete(struct hci_dev *hdev, void *data, int err)
	else
		hci_dev_clear_flag(hdev, HCI_ADVERTISING);

	mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, true, settings_rsp,
			     &match);
	settings_rsp(cmd, &match);

	new_settings(hdev, match.sk);

@@ -6283,10 +6379,23 @@ static void set_advertising_complete(struct hci_dev *hdev, void *data, int err)
static int set_adv_sync(struct hci_dev *hdev, void *data)
{
	struct mgmt_pending_cmd *cmd = data;
	struct mgmt_mode *cp = cmd->param;
	u8 val = !!cp->val;
	struct mgmt_mode cp;
	u8 val;

	if (cp->val == 0x02)
	mutex_lock(&hdev->mgmt_pending_lock);

	if (!__mgmt_pending_listed(hdev, cmd)) {
		mutex_unlock(&hdev->mgmt_pending_lock);
		return -ECANCELED;
	}

	memcpy(&cp, cmd->param, sizeof(cp));

	mutex_unlock(&hdev->mgmt_pending_lock);

	val = !!cp.val;

	if (cp.val == 0x02)
		hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
	else
		hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
@@ -8039,10 +8148,6 @@ static void read_local_oob_ext_data_complete(struct hci_dev *hdev, void *data,
	u8 status = mgmt_status(err);
	u16 eir_len;

	if (err == -ECANCELED ||
	    cmd != pending_find(MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev))
		return;

	if (!status) {
		if (!skb)
			status = MGMT_STATUS_FAILED;
@@ -8149,7 +8254,7 @@ static void read_local_oob_ext_data_complete(struct hci_dev *hdev, void *data,
		kfree_skb(skb);

	kfree(mgmt_rp);
	mgmt_pending_remove(cmd);
	mgmt_pending_free(cmd);
}

static int read_local_ssp_oob_req(struct hci_dev *hdev, struct sock *sk,
@@ -8158,7 +8263,7 @@ static int read_local_ssp_oob_req(struct hci_dev *hdev, struct sock *sk,
	struct mgmt_pending_cmd *cmd;
	int err;

	cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev,
	cmd = mgmt_pending_new(sk, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev,
			       cp, sizeof(*cp));
	if (!cmd)
		return -ENOMEM;
+46 −0
Original line number Diff line number Diff line
@@ -320,6 +320,52 @@ void mgmt_pending_remove(struct mgmt_pending_cmd *cmd)
	mgmt_pending_free(cmd);
}

bool __mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd)
{
	struct mgmt_pending_cmd *tmp;

	lockdep_assert_held(&hdev->mgmt_pending_lock);

	if (!cmd)
		return false;

	list_for_each_entry(tmp, &hdev->mgmt_pending, list) {
		if (cmd == tmp)
			return true;
	}

	return false;
}

bool mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd)
{
	bool listed;

	mutex_lock(&hdev->mgmt_pending_lock);
	listed = __mgmt_pending_listed(hdev, cmd);
	mutex_unlock(&hdev->mgmt_pending_lock);

	return listed;
}

bool mgmt_pending_valid(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd)
{
	bool listed;

	if (!cmd)
		return false;

	mutex_lock(&hdev->mgmt_pending_lock);

	listed = __mgmt_pending_listed(hdev, cmd);
	if (listed)
		list_del(&cmd->list);

	mutex_unlock(&hdev->mgmt_pending_lock);

	return listed;
}

void mgmt_mesh_foreach(struct hci_dev *hdev,
		       void (*cb)(struct mgmt_mesh_tx *mesh_tx, void *data),
		       void *data, struct sock *sk)
+3 −0
Original line number Diff line number Diff line
@@ -65,6 +65,9 @@ struct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
					  void *data, u16 len);
void mgmt_pending_free(struct mgmt_pending_cmd *cmd);
void mgmt_pending_remove(struct mgmt_pending_cmd *cmd);
bool __mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd);
bool mgmt_pending_listed(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd);
bool mgmt_pending_valid(struct hci_dev *hdev, struct mgmt_pending_cmd *cmd);
void mgmt_mesh_foreach(struct hci_dev *hdev,
		       void (*cb)(struct mgmt_mesh_tx *mesh_tx, void *data),
		       void *data, struct sock *sk);