mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	Bluetooth: hci_sync: Fix overwriting request callback
In a few cases the stack may generate commands as responses to events
which would happen to overwrite the sent_cmd, so this attempts to store
the request in req_skb so even if sent_cmd is replaced with a new
command the pending request will remain in stored in req_skb.
Fixes: 6a98e3836f ("Bluetooth: Add helper for serialized HCI command execution")
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
			
			
This commit is contained in:
		
							parent
							
								
									22cbf4f84c
								
							
						
					
					
						commit
						2615fd9a7c
					
				
					 5 changed files with 61 additions and 27 deletions
				
			
		| 
						 | 
					@ -552,6 +552,7 @@ struct hci_dev {
 | 
				
			||||||
	__u32			req_status;
 | 
						__u32			req_status;
 | 
				
			||||||
	__u32			req_result;
 | 
						__u32			req_result;
 | 
				
			||||||
	struct sk_buff		*req_skb;
 | 
						struct sk_buff		*req_skb;
 | 
				
			||||||
 | 
						struct sk_buff		*req_rsp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void			*smp_data;
 | 
						void			*smp_data;
 | 
				
			||||||
	void			*smp_bredr_data;
 | 
						void			*smp_bredr_data;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2918,7 +2918,7 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
 | 
				
			||||||
		case HCI_EV_LE_CONN_COMPLETE:
 | 
							case HCI_EV_LE_CONN_COMPLETE:
 | 
				
			||||||
		case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
 | 
							case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
 | 
				
			||||||
		case HCI_EVT_LE_CIS_ESTABLISHED:
 | 
							case HCI_EVT_LE_CIS_ESTABLISHED:
 | 
				
			||||||
			hci_cmd_sync_cancel(hdev, -ECANCELED);
 | 
								hci_cmd_sync_cancel(hdev, ECANCELED);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	/* Cancel connect attempt if still queued/pending */
 | 
						/* Cancel connect attempt if still queued/pending */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1522,8 +1522,8 @@ static void hci_cmd_timeout(struct work_struct *work)
 | 
				
			||||||
	struct hci_dev *hdev = container_of(work, struct hci_dev,
 | 
						struct hci_dev *hdev = container_of(work, struct hci_dev,
 | 
				
			||||||
					    cmd_timer.work);
 | 
										    cmd_timer.work);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (hdev->sent_cmd) {
 | 
						if (hdev->req_skb) {
 | 
				
			||||||
		u16 opcode = hci_skb_opcode(hdev->sent_cmd);
 | 
							u16 opcode = hci_skb_opcode(hdev->req_skb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		bt_dev_err(hdev, "command 0x%4.4x tx timeout", opcode);
 | 
							bt_dev_err(hdev, "command 0x%4.4x tx timeout", opcode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2828,6 +2828,7 @@ void hci_release_dev(struct hci_dev *hdev)
 | 
				
			||||||
	ida_destroy(&hdev->unset_handle_ida);
 | 
						ida_destroy(&hdev->unset_handle_ida);
 | 
				
			||||||
	ida_free(&hci_index_ida, hdev->id);
 | 
						ida_free(&hci_index_ida, hdev->id);
 | 
				
			||||||
	kfree_skb(hdev->sent_cmd);
 | 
						kfree_skb(hdev->sent_cmd);
 | 
				
			||||||
 | 
						kfree_skb(hdev->req_skb);
 | 
				
			||||||
	kfree_skb(hdev->recv_event);
 | 
						kfree_skb(hdev->recv_event);
 | 
				
			||||||
	kfree(hdev);
 | 
						kfree(hdev);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3157,21 +3158,33 @@ int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen,
 | 
				
			||||||
EXPORT_SYMBOL(__hci_cmd_send);
 | 
					EXPORT_SYMBOL(__hci_cmd_send);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Get data from the previously sent command */
 | 
					/* Get data from the previously sent command */
 | 
				
			||||||
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
 | 
					static void *hci_cmd_data(struct sk_buff *skb, __u16 opcode)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct hci_command_hdr *hdr;
 | 
						struct hci_command_hdr *hdr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!hdev->sent_cmd)
 | 
						if (!skb || skb->len < HCI_COMMAND_HDR_SIZE)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hdr = (void *) hdev->sent_cmd->data;
 | 
						hdr = (void *)skb->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (hdr->opcode != cpu_to_le16(opcode))
 | 
						if (hdr->opcode != cpu_to_le16(opcode))
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
 | 
						return skb->data + HCI_COMMAND_HDR_SIZE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
 | 
					/* Get data from the previously sent command */
 | 
				
			||||||
 | 
					void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						void *data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Check if opcode matches last sent command */
 | 
				
			||||||
 | 
						data = hci_cmd_data(hdev->sent_cmd, opcode);
 | 
				
			||||||
 | 
						if (!data)
 | 
				
			||||||
 | 
							/* Check if opcode matches last request */
 | 
				
			||||||
 | 
							data = hci_cmd_data(hdev->req_skb, opcode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return data;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Get data from last received event */
 | 
					/* Get data from last received event */
 | 
				
			||||||
| 
						 | 
					@ -4072,17 +4085,19 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
 | 
				
			||||||
	if (!status && !hci_req_is_complete(hdev))
 | 
						if (!status && !hci_req_is_complete(hdev))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						skb = hdev->req_skb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* If this was the last command in a request the complete
 | 
						/* If this was the last command in a request the complete
 | 
				
			||||||
	 * callback would be found in hdev->sent_cmd instead of the
 | 
						 * callback would be found in hdev->req_skb instead of the
 | 
				
			||||||
	 * command queue (hdev->cmd_q).
 | 
						 * command queue (hdev->cmd_q).
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (bt_cb(hdev->sent_cmd)->hci.req_flags & HCI_REQ_SKB) {
 | 
						if (skb && bt_cb(skb)->hci.req_flags & HCI_REQ_SKB) {
 | 
				
			||||||
		*req_complete_skb = bt_cb(hdev->sent_cmd)->hci.req_complete_skb;
 | 
							*req_complete_skb = bt_cb(skb)->hci.req_complete_skb;
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (bt_cb(hdev->sent_cmd)->hci.req_complete) {
 | 
						if (skb && bt_cb(skb)->hci.req_complete) {
 | 
				
			||||||
		*req_complete = bt_cb(hdev->sent_cmd)->hci.req_complete;
 | 
							*req_complete = bt_cb(skb)->hci.req_complete;
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4199,8 +4214,11 @@ static void hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (hci_req_status_pend(hdev))
 | 
						if (hci_req_status_pend(hdev) &&
 | 
				
			||||||
		hci_dev_set_flag(hdev, HCI_CMD_PENDING);
 | 
						    !hci_dev_test_and_set_flag(hdev, HCI_CMD_PENDING)) {
 | 
				
			||||||
 | 
							kfree_skb(hdev->req_skb);
 | 
				
			||||||
 | 
							hdev->req_skb = skb_clone(skb, GFP_KERNEL);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	atomic_dec(&hdev->cmd_cnt);
 | 
						atomic_dec(&hdev->cmd_cnt);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4368,7 +4368,7 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, void *data,
 | 
				
			||||||
	 * (since for this kind of commands there will not be a command
 | 
						 * (since for this kind of commands there will not be a command
 | 
				
			||||||
	 * complete event).
 | 
						 * complete event).
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (ev->status || (hdev->sent_cmd && !hci_skb_event(hdev->sent_cmd))) {
 | 
						if (ev->status || (hdev->req_skb && !hci_skb_event(hdev->req_skb))) {
 | 
				
			||||||
		hci_req_cmd_complete(hdev, *opcode, ev->status, req_complete,
 | 
							hci_req_cmd_complete(hdev, *opcode, ev->status, req_complete,
 | 
				
			||||||
				     req_complete_skb);
 | 
									     req_complete_skb);
 | 
				
			||||||
		if (hci_dev_test_flag(hdev, HCI_CMD_PENDING)) {
 | 
							if (hci_dev_test_flag(hdev, HCI_CMD_PENDING)) {
 | 
				
			||||||
| 
						 | 
					@ -7170,10 +7170,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
 | 
				
			||||||
	bt_dev_dbg(hdev, "subevent 0x%2.2x", ev->subevent);
 | 
						bt_dev_dbg(hdev, "subevent 0x%2.2x", ev->subevent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Only match event if command OGF is for LE */
 | 
						/* Only match event if command OGF is for LE */
 | 
				
			||||||
	if (hdev->sent_cmd &&
 | 
						if (hdev->req_skb &&
 | 
				
			||||||
	    hci_opcode_ogf(hci_skb_opcode(hdev->sent_cmd)) == 0x08 &&
 | 
						    hci_opcode_ogf(hci_skb_opcode(hdev->req_skb)) == 0x08 &&
 | 
				
			||||||
	    hci_skb_event(hdev->sent_cmd) == ev->subevent) {
 | 
						    hci_skb_event(hdev->req_skb) == ev->subevent) {
 | 
				
			||||||
		*opcode = hci_skb_opcode(hdev->sent_cmd);
 | 
							*opcode = hci_skb_opcode(hdev->req_skb);
 | 
				
			||||||
		hci_req_cmd_complete(hdev, *opcode, 0x00, req_complete,
 | 
							hci_req_cmd_complete(hdev, *opcode, 0x00, req_complete,
 | 
				
			||||||
				     req_complete_skb);
 | 
									     req_complete_skb);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -7541,10 +7541,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Only match event if command OGF is not for LE */
 | 
						/* Only match event if command OGF is not for LE */
 | 
				
			||||||
	if (hdev->sent_cmd &&
 | 
						if (hdev->req_skb &&
 | 
				
			||||||
	    hci_opcode_ogf(hci_skb_opcode(hdev->sent_cmd)) != 0x08 &&
 | 
						    hci_opcode_ogf(hci_skb_opcode(hdev->req_skb)) != 0x08 &&
 | 
				
			||||||
	    hci_skb_event(hdev->sent_cmd) == event) {
 | 
						    hci_skb_event(hdev->req_skb) == event) {
 | 
				
			||||||
		hci_req_cmd_complete(hdev, hci_skb_opcode(hdev->sent_cmd),
 | 
							hci_req_cmd_complete(hdev, hci_skb_opcode(hdev->req_skb),
 | 
				
			||||||
				     status, &req_complete, &req_complete_skb);
 | 
									     status, &req_complete, &req_complete_skb);
 | 
				
			||||||
		req_evt = event;
 | 
							req_evt = event;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,10 @@ static void hci_cmd_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
 | 
				
			||||||
	hdev->req_result = result;
 | 
						hdev->req_result = result;
 | 
				
			||||||
	hdev->req_status = HCI_REQ_DONE;
 | 
						hdev->req_status = HCI_REQ_DONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Free the request command so it is not used as response */
 | 
				
			||||||
 | 
						kfree_skb(hdev->req_skb);
 | 
				
			||||||
 | 
						hdev->req_skb = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (skb) {
 | 
						if (skb) {
 | 
				
			||||||
		struct sock *sk = hci_skb_sk(skb);
 | 
							struct sock *sk = hci_skb_sk(skb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,7 +43,7 @@ static void hci_cmd_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
 | 
				
			||||||
		if (sk)
 | 
							if (sk)
 | 
				
			||||||
			sock_put(sk);
 | 
								sock_put(sk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		hdev->req_skb = skb_get(skb);
 | 
							hdev->req_rsp = skb_get(skb);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wake_up_interruptible(&hdev->req_wait_q);
 | 
						wake_up_interruptible(&hdev->req_wait_q);
 | 
				
			||||||
| 
						 | 
					@ -187,8 +191,8 @@ struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hdev->req_status = 0;
 | 
						hdev->req_status = 0;
 | 
				
			||||||
	hdev->req_result = 0;
 | 
						hdev->req_result = 0;
 | 
				
			||||||
	skb = hdev->req_skb;
 | 
						skb = hdev->req_rsp;
 | 
				
			||||||
	hdev->req_skb = NULL;
 | 
						hdev->req_rsp = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bt_dev_dbg(hdev, "end: err %d", err);
 | 
						bt_dev_dbg(hdev, "end: err %d", err);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5021,6 +5025,11 @@ int hci_dev_open_sync(struct hci_dev *hdev)
 | 
				
			||||||
			hdev->sent_cmd = NULL;
 | 
								hdev->sent_cmd = NULL;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (hdev->req_skb) {
 | 
				
			||||||
 | 
								kfree_skb(hdev->req_skb);
 | 
				
			||||||
 | 
								hdev->req_skb = NULL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		clear_bit(HCI_RUNNING, &hdev->flags);
 | 
							clear_bit(HCI_RUNNING, &hdev->flags);
 | 
				
			||||||
		hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
 | 
							hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5181,6 +5190,12 @@ int hci_dev_close_sync(struct hci_dev *hdev)
 | 
				
			||||||
		hdev->sent_cmd = NULL;
 | 
							hdev->sent_cmd = NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Drop last request */
 | 
				
			||||||
 | 
						if (hdev->req_skb) {
 | 
				
			||||||
 | 
							kfree_skb(hdev->req_skb);
 | 
				
			||||||
 | 
							hdev->req_skb = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	clear_bit(HCI_RUNNING, &hdev->flags);
 | 
						clear_bit(HCI_RUNNING, &hdev->flags);
 | 
				
			||||||
	hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
 | 
						hci_sock_dev_event(hdev, HCI_DEV_CLOSE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue