All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes
@ 2016-09-23 17:56 Dean Jenkins
  2016-09-23 17:56 ` [PATCH V2 1/7] Bluetooth: Tidy-up coding style in hci_bcsp.c Dean Jenkins
                   ` (7 more replies)
  0 siblings, 8 replies; 11+ messages in thread
From: Dean Jenkins @ 2016-09-23 17:56 UTC (permalink / raw)
  To: Dean Jenkins, Marcel Holtmann, Gustavo Padovan, Johan Hedberg
  Cc: linux-bluetooth

This patchset contains the following commits:

Dean Jenkins (4):
  Bluetooth: Tidy-up coding style in hci_bcsp.c
  Bluetooth: BCSP fails to ACK re-transmitted frames from the peer
  Bluetooth: Add mutex to hci_uart_tty_ioctl()
  Revert "Bluetooth: hci_ldisc: Fix null pointer derefence in case of
    early data"

Deepak Das (1):
  Bluetooth: Prevent scheduling of work after hci_uart_tty_close()

Vignesh Raman (2):
  Bluetooth: Use single return in hci_uart_tty_ioctl() call
  Bluetooth: Fix HCI UART HCI_UART_PROTO_SET locking

 drivers/bluetooth/hci_bcsp.c  | 128 ++++++++++++++++++++++++------------------
 drivers/bluetooth/hci_ldisc.c |  63 +++++++++++++--------
 drivers/bluetooth/hci_uart.h  |   4 +-
 3 files changed, 117 insertions(+), 78 deletions(-)

This V2 patchset is in response to my previous V1 patchset released here:
http://www.spinics.net/lists/linux-bluetooth/index.html#68405
with E-mail subject "[PATCH v1 0/4] Bluetooth HCI LDISC and BCSP fixes"

Changes since patchset V1
-------------------------

New commit as requested by Marcel
"Bluetooth: Tidy-up coding style in hci_bcsp.c"

Spilt commit as requested by Marcel
"Bluetooth: prevent a race condition on hci_uart_tty_ioctl() call"
into
"Bluetooth: Use single return in hci_uart_tty_ioctl() call"
"Bluetooth: Add mutex to hci_uart_tty_ioctl()"

Revert commit
"Bluetooth: hci_ldisc: Fix null pointer derefence in case of early data"

Reordered the patches
The patchset contains 2 BCSP related patches and 5 HCI UART LDISC patches.
The BCSP related patches can be applied independently of the HCI UART LDISC
patches.


Background information
----------------------

6 out of these 7 commits were originally developed and validated on an
ARM i.MX6 based commercial project running a highly modified 3.14 Linux kernel.
The commits were needed to improve BCSP recovery and to avoid kernel crashes in
the Bluetooth sub-system start-up and shutdown scenarios.

The "Bluetooth: hci_ldisc: Fix null pointer derefence in case of early data"
commit from upstream kernel v4.7-rc1 was not included in the i.MX6 kernel
so this patchset is reverting this commit as this patchset fixes the same issue.

The commits are used with a UART based Bluetooth Radio Module that uses BCSP.
This means the commits should have no impact on USB based Bluetooth Radio
Modules which are typically used on x86 based computer systems.

The commits have been forward-ported to bluetooth-next master branch HEAD
(acf91ec Bluetooth: btwilink: Save the packet type before sending)
and built but not tested.

The commits were also sanity tested on top of linux-stable v4.7.4 on a 64-bit
x86 Linux laptop with a USB to UART based Bluetooth Radio Module. The sanity
tests used l2ping to confirm that a remote Bluetooth smartphone could be
contacted.


Risks
-----

The HCI UART LDISC patches have only been validated with the BCSP Data Link
protocol layer.

Recommend that other Data Link Layer protocols are tested as we are unable to
test them. However, code inspection suggests that the HCI UART LDISC patches
should be OK but the changes have not been proven to work for the other Data
Link Layer protocols.


Commit details
--------------

The following 2 BCSP commits are a pair.

1. "Bluetooth: Tidy-up coding style in hci_bcsp.c"

This patch does some coding style cleanups to make is easier to apply subsequent
patches that have a preferred coding style.


2. "Bluetooth: BCSP fails to ACK re-transmitted frames from the peer"

This patch had previously been released to the mailing list in 2014 but somehow
never got into bluetooth-next at that time. Probably, there was an oversight in
keeping track of the patch to ensure it got reviewed by the maintainer.

The idea of this patch is to make sure that the BCSP header of all received BCSP
frames from the Bluetooth Radio Module are consumed. The BCSP header contains
sequence counters for reliable transmitted, and received acknowledgment frames.
The acknowledgment counter is received in either reliable or unreliable frames.
These 2 independent counters are needed for a duplex link to track
acknowledgments and to trigger retransmissions.

In the local BCSP peer, each new reliable transmit frame increments the local TX
sequence number (modulo 8). The local BCSP peer has a window size of 4 so must
not transmit any new reliable frames until less than 4 acknowledgments are
pending. When a reliable BCSP frame is received, the remote BCSP peer's TX
sequence number is read and checked, and an acknowledgment frame should be sent
back to confirm the remote BCSP peer's transmission status.

The current implementation has a flaw because when the received reliable BCSP
frame does not have the expected remote TX sequence counter value, it causes the
whole frame to be dropped and causes the so-called "Out-of-order packet arrived"
error message to be generated. This incorrect behaviour means that the local
BCSP peer's acknowledgment counter number is not processed meaning that the
remote BCSP peer may have acknowledged the local BCSP peer's transmission but
the indication is ignored by the local BCSP peer. This can cause the local BCSP
peer to unnecessarily re-transmit. In addition the remote BCSP peer is not sent
an acknowledgment frame to update its transmission status. This can cause the
remote BCSP peer to unnecessarily retransmit frames.

In other words, the current implementation is weak in sending acknowledgments
in response to already received frames.

The flaw is observed with a poor performing UART driver which causes occasional
corruption of received or transmitted frames. BCSP is designed to compensate
for such issues by using its recovery mechanism and the flaw is in the
recovery mechanism.

The flaw can be triggered when the local BCSP peer fails to receive a reliable
frame from the remote BCSP peer so that reception of the next new reliable frame
from the remote BCSP peer causes the received transmission counter to skip over
(modulo 8) a value due to the missing frame. This generates the so-called
"Out-of-order packet arrived" error message but the acknowledgment counter
in the received frame's BCSP header is valid and should be processed and not
ignored. This protocol failure can be recovered when the local BCSP peer
retransmits an unacknowledged frame and the remote BCSP peer responds with an
acknowledgment using an unreliable frame so avoids the flawed code.

Alternatively, the flaw is triggered when the local BCSP peer tries to send
an acknowledgment frame to the remote BCSP peer but the remote BCSP peer failed
to receive the acknowledgment. This causes the local BCSP peer to increment
(modulo 8) the expected next remote BCSP peer's transmission sequence counter.
When the remote BCSP peer retransmits the unacknowledged frame, the local BCSP
peer is expecting the next counter value so causing the so-called
"Out-of-order packet arrived" error message to be generated. This causes the
received BCSP frame to be dropped so the local BCSP peer fails to send an
acknowledgment frame. This protocol failure due to the flaw may be
unrecoverable. However, recovery is sometimes possible when the remote BCSP peer
resends multiple unacknowledged frames so the next expected counter value is
seen so gets past the flawed code.


The following 5 HCI UART LDISC commits are related.

3. "Bluetooth: Use single return in hci_uart_tty_ioctl() call"

This commit tidies up hci_uart_tty_ioctl() to have a single return statement.
This allows the next commit below to add a mutex to protect against concurrency.


4. "Bluetooth: Add mutex to hci_uart_tty_ioctl()"

This patch avoids a race condition by adding a mutex to hci_uart_tty_ioctl()
to prevent concurrency.

Note this commit removes the lockless role of the HCI_UART_PROTO_SET flag
by using a mutex lock.


5. "Bluetooth: Fix HCI UART HCI_UART_PROTO_SET locking"

This patch fixes a kernel NULL pointer dereference crash when starting-up the
Bluetooth sub-system.

There is a flaw that HCI_UART_PROTO_SET is set before hci_uart_set_proto() is
successfully run to set the hu->proto->recv function pointer. This allows the
UART driver to attempt to process RX characters before the recv function pointer
has been set so causing a NULL pointer dereference crash.

The commit modifies the implementation to only set HCI_UART_PROTO_SET after
hci_uart_set_proto() has successfully executed. The HCI_UART_PROTO_SET flag
is no longer used as a lockless solution for hci_uart_tty_ioctl() as a mutex is
used.

These 3 commits fixes the same issue as the kernel v4.7-rc1
"Bluetooth: hci_ldisc: Fix null pointer derefence in case of early data"
which fixes the same crash by adding HCI_UART_PROTO_READY.


6. "Revert "Bluetooth: hci_ldisc: Fix null pointer derefence in case of early
data""

The following 3 commits from this patchset
"Bluetooth: Use single return in hci_uart_tty_ioctl() call"
"Bluetooth: Add mutex to hci_uart_tty_ioctl()"
"Bluetooth: Fix HCI UART HCI_UART_PROTO_SET locking"

fixes the same issue and avoids a potential race condition introduced in kernel
v4.7-rc1.

Therefore, recommend reverting the v4.7-rc1 commmit
"Bluetooth: hci_ldisc: Fix null pointer derefence in case of early data"

The git commit text of the reverted patch gives a fuller explanation so please
read that.

Both solutions can co-exist but better to revert the v4.7-rc1 commmit in my
opinion because the v4.7-rc1 commmit is weaker.


7. "Bluetooth: Prevent scheduling of work after hci_uart_tty_close()"

This patch prevents adding a transmission work item to the hu->write_work work
queue during execution of hci_uart_tty_close(). A kernel crash has been
observed whist BCSP was scheduling a retransmission and shutdown of the HCI UART
was in progress.

The fix introduces a new HCI_UART proto flag bit called HCI_UART_UNREGISTERING
and adds a spinlock to hci_uart_tx_wakeup() to force that function to run
consecutively with respect to hci_uart_tty_close().

The git commit text of the patch gives a fuller explanation so please read
that.


Further work
------------

Note we are working on some cleanups to hci_uart_tty_close() which are now
under test.

Recommend that hci_unregister_dev() is investigated because:

a) Execution time seen as 2 seconds (on x86)- suggesting some timed event occurs
b) Seems to be trying to send HCI message(s) but these will never get through
   to the Radio Module due to the various flags whilst the HCI UART is closing.
c) Suspect that hci_unregister_dev() is responsible for triggering various
   crashes which have been worked around elsewhere over the many years.

-- 
2.7.4

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH V2 1/7] Bluetooth: Tidy-up coding style in hci_bcsp.c
  2016-09-23 17:56 [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes Dean Jenkins
@ 2016-09-23 17:56 ` Dean Jenkins
  2016-09-23 17:56 ` [PATCH V2 2/7] Bluetooth: BCSP fails to ACK re-transmitted frames from the peer Dean Jenkins
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Dean Jenkins @ 2016-09-23 17:56 UTC (permalink / raw)
  To: Dean Jenkins, Marcel Holtmann, Gustavo Padovan, Johan Hedberg
  Cc: linux-bluetooth

drivers/bluetooth/hci_bcsp.c contains some style issues as
highlighted by

./scripts/checkpatch.pl --strict -f drivers/bluetooth/hci_bcsp.c

a) comments - maintainer prefers network style comments
b) positioning of lines in multi-line statements
c) spaces after casts
d) missing blank lines after declarations

Therefore, tidy-up the above to make it easier to apply
future code changes that have conforming style.

Signed-off-by: Dean Jenkins <Dean_Jenkins@mentor.com>
---
 drivers/bluetooth/hci_bcsp.c | 55 +++++++++++++++++++++++---------------------
 1 file changed, 29 insertions(+), 26 deletions(-)

diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
index d7d23ce..2628b36 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -90,7 +90,8 @@ struct bcsp_struct {
 /* ---- BCSP CRC calculation ---- */
 
 /* Table for calculating CRC for polynomial 0x1021, LSB processed first,
-initial value 0xffff, bits shifted in reverse order. */
+ * initial value 0xffff, bits shifted in reverse order.
+ */
 
 static const u16 crc_table[] = {
 	0x0000, 0x1081, 0x2102, 0x3183,
@@ -174,7 +175,7 @@ static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb)
 }
 
 static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
-		int len, int pkt_type)
+					int len, int pkt_type)
 {
 	struct sk_buff *nskb;
 	u8 hdr[4], chan;
@@ -213,6 +214,7 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
 		/* Vendor specific commands */
 		if (hci_opcode_ogf(__le16_to_cpu(opcode)) == 0x3f) {
 			u8 desc = *(data + HCI_COMMAND_HDR_SIZE);
+
 			if ((desc & 0xf0) == 0xc0) {
 				data += HCI_COMMAND_HDR_SIZE + 1;
 				len  -= HCI_COMMAND_HDR_SIZE + 1;
@@ -271,8 +273,8 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
 	/* Put CRC */
 	if (bcsp->use_crc) {
 		bcsp_txmsg_crc = bitrev16(bcsp_txmsg_crc);
-		bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff));
-		bcsp_slip_one_byte(nskb, (u8) (bcsp_txmsg_crc & 0x00ff));
+		bcsp_slip_one_byte(nskb, (u8)((bcsp_txmsg_crc >> 8) & 0x00ff));
+		bcsp_slip_one_byte(nskb, (u8)(bcsp_txmsg_crc & 0x00ff));
 	}
 
 	bcsp_slip_msgdelim(nskb);
@@ -287,7 +289,8 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
 	struct sk_buff *skb;
 
 	/* First of all, check for unreliable messages in the queue,
-	   since they have priority */
+	 * since they have priority
+	 */
 
 	skb = skb_dequeue(&bcsp->unrel);
 	if (skb != NULL) {
@@ -414,7 +417,7 @@ static void bcsp_handle_le_pkt(struct hci_uart *hu)
 
 	/* spot "conf" pkts and reply with a "conf rsp" pkt */
 	if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
-			!memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
+	    !memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
 		struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
 
 		BT_DBG("Found a LE conf pkt");
@@ -428,7 +431,7 @@ static void bcsp_handle_le_pkt(struct hci_uart *hu)
 	}
 	/* Spot "sync" pkts. If we find one...disaster! */
 	else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
-			!memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) {
+		 !memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) {
 		BT_ERR("Found a LE sync pkt, card has reset");
 	}
 }
@@ -446,7 +449,7 @@ static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char
 		default:
 			memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1);
 			if ((bcsp->rx_skb->data[0] & 0x40) != 0 &&
-					bcsp->rx_state != BCSP_W4_CRC)
+			    bcsp->rx_state != BCSP_W4_CRC)
 				bcsp_crc_update(&bcsp->message_crc, byte);
 			bcsp->rx_count--;
 		}
@@ -457,7 +460,7 @@ static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char
 		case 0xdc:
 			memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1);
 			if ((bcsp->rx_skb->data[0] & 0x40) != 0 &&
-					bcsp->rx_state != BCSP_W4_CRC)
+			    bcsp->rx_state != BCSP_W4_CRC)
 				bcsp_crc_update(&bcsp->message_crc, 0xc0);
 			bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
 			bcsp->rx_count--;
@@ -466,7 +469,7 @@ static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char
 		case 0xdd:
 			memcpy(skb_put(bcsp->rx_skb, 1), &db, 1);
 			if ((bcsp->rx_skb->data[0] & 0x40) != 0 &&
-					bcsp->rx_state != BCSP_W4_CRC)
+			    bcsp->rx_state != BCSP_W4_CRC)
 				bcsp_crc_update(&bcsp->message_crc, 0xdb);
 			bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
 			bcsp->rx_count--;
@@ -502,18 +505,18 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
 
 	bcsp_pkt_cull(bcsp);
 	if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
-			bcsp->rx_skb->data[0] & 0x80) {
+	    bcsp->rx_skb->data[0] & 0x80) {
 		hci_skb_pkt_type(bcsp->rx_skb) = HCI_ACLDATA_PKT;
 		pass_up = 1;
 	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
-			bcsp->rx_skb->data[0] & 0x80) {
+		   bcsp->rx_skb->data[0] & 0x80) {
 		hci_skb_pkt_type(bcsp->rx_skb) = HCI_EVENT_PKT;
 		pass_up = 1;
 	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
 		hci_skb_pkt_type(bcsp->rx_skb) = HCI_SCODATA_PKT;
 		pass_up = 1;
 	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
-			!(bcsp->rx_skb->data[0] & 0x80)) {
+		   !(bcsp->rx_skb->data[0] & 0x80)) {
 		bcsp_handle_le_pkt(hu);
 		pass_up = 0;
 	} else
@@ -537,9 +540,9 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
 				hci_recv_frame(hu->hdev, bcsp->rx_skb);
 			} else {
 				BT_ERR("Packet for unknown channel (%u %s)",
-					bcsp->rx_skb->data[1] & 0x0f,
-					bcsp->rx_skb->data[0] & 0x80 ?
-					"reliable" : "unreliable");
+				       bcsp->rx_skb->data[1] & 0x0f,
+				       bcsp->rx_skb->data[0] & 0x80 ?
+				       "reliable" : "unreliable");
 				kfree_skb(bcsp->rx_skb);
 			}
 		} else
@@ -567,7 +570,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
 	const unsigned char *ptr;
 
 	BT_DBG("hu %p count %d rx_state %d rx_count %ld",
-		hu, count, bcsp->rx_state, bcsp->rx_count);
+	       hu, count, bcsp->rx_state, bcsp->rx_count);
 
 	ptr = data;
 	while (count) {
@@ -586,18 +589,18 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
 
 		switch (bcsp->rx_state) {
 		case BCSP_W4_BCSP_HDR:
-			if ((0xff & (u8) ~ (bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] +
-					bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
+			if ((0xff & (u8)~(bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] +
+			    bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
 				BT_ERR("Error in BCSP hdr checksum");
 				kfree_skb(bcsp->rx_skb);
 				bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
 				bcsp->rx_count = 0;
 				continue;
 			}
-			if (bcsp->rx_skb->data[0] & 0x80	/* reliable pkt */
-						&& (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) {
+			if (bcsp->rx_skb->data[0] & 0x80 &&	/* reliable pkt */
+			    (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) {
 				BT_ERR("Out-of-order packet arrived, got %u expected %u",
-					bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
+				       bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
 
 				kfree_skb(bcsp->rx_skb);
 				bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
@@ -620,8 +623,8 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
 		case BCSP_W4_CRC:
 			if (bitrev16(bcsp->message_crc) != bscp_get_crc(bcsp)) {
 				BT_ERR("Checksum failed: computed %04x received %04x",
-					bitrev16(bcsp->message_crc),
-					bscp_get_crc(bcsp));
+				       bitrev16(bcsp->message_crc),
+				       bscp_get_crc(bcsp));
 
 				kfree_skb(bcsp->rx_skb);
 				bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
@@ -679,7 +682,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
 	/* Arrange to retransmit all messages in the relq. */
 static void bcsp_timed_event(unsigned long arg)
 {
-	struct hci_uart *hu = (struct hci_uart *) arg;
+	struct hci_uart *hu = (struct hci_uart *)arg;
 	struct bcsp_struct *bcsp = hu->priv;
 	struct sk_buff *skb;
 	unsigned long flags;
@@ -715,7 +718,7 @@ static int bcsp_open(struct hci_uart *hu)
 
 	init_timer(&bcsp->tbcsp);
 	bcsp->tbcsp.function = bcsp_timed_event;
-	bcsp->tbcsp.data     = (u_long) hu;
+	bcsp->tbcsp.data     = (u_long)hu;
 
 	bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH V2 2/7] Bluetooth: BCSP fails to ACK re-transmitted frames from the peer
  2016-09-23 17:56 [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes Dean Jenkins
  2016-09-23 17:56 ` [PATCH V2 1/7] Bluetooth: Tidy-up coding style in hci_bcsp.c Dean Jenkins
@ 2016-09-23 17:56 ` Dean Jenkins
  2016-09-23 17:56 ` [PATCH V2 3/7] Bluetooth: Use single return in hci_uart_tty_ioctl() call Dean Jenkins
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Dean Jenkins @ 2016-09-23 17:56 UTC (permalink / raw)
  To: Dean Jenkins, Marcel Holtmann, Gustavo Padovan, Johan Hedberg
  Cc: linux-bluetooth

Send an ACK frame with the current txack value in response to
every received reliable frame unless a TX reliable frame is being
sent. This modification allows re-transmitted frames from the remote
peer to be acknowledged rather than ignored. It means that the remote
peer knows which frame number to start re-transmitting from.

Without this modification, the recovery time to a missing frame
from the remote peer was unnecessarily being extended because the
headers of the out of order reliable frames were being discarded rather
than being processed. The frame headers of received frames will
indicate whether the local peer's transmissions have been
acknowledged by the remote peer. Therefore, the local peer may
unnecessarily re-transmit despite the remote peer already indicating
that the frame had been acknowledged in out of order reliable frame.

Signed-off-by: Dean Jenkins <Dean_Jenkins@mentor.com>
Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
Signed-off-by: Rajeev Kumar <rajeev_kumar@mentor.com>
---
 drivers/bluetooth/hci_bcsp.c | 85 ++++++++++++++++++++++++++------------------
 1 file changed, 51 insertions(+), 34 deletions(-)

diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
index 2628b36..a2c921f 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -488,13 +488,28 @@ static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char
 static void bcsp_complete_rx_pkt(struct hci_uart *hu)
 {
 	struct bcsp_struct *bcsp = hu->priv;
-	int pass_up;
+	int pass_up = 0;
 
 	if (bcsp->rx_skb->data[0] & 0x80) {	/* reliable pkt */
 		BT_DBG("Received seqno %u from card", bcsp->rxseq_txack);
-		bcsp->rxseq_txack++;
-		bcsp->rxseq_txack %= 0x8;
-		bcsp->txack_req    = 1;
+
+		/* check the rx sequence number is as expected */
+		if ((bcsp->rx_skb->data[0] & 0x07) == bcsp->rxseq_txack) {
+			bcsp->rxseq_txack++;
+			bcsp->rxseq_txack %= 0x8;
+		} else {
+			/* handle re-transmitted packet or
+			 * when packet was missed
+			 */
+			BT_ERR("Out-of-order packet arrived, got %u expected %u",
+			       bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
+
+			/* do not process out-of-order packet payload */
+			pass_up = 2;
+		}
+
+		/* send current txack value to all received reliable packets */
+		bcsp->txack_req = 1;
 
 		/* If needed, transmit an ack pkt */
 		hci_uart_tx_wakeup(hu);
@@ -503,26 +518,33 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
 	bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07;
 	BT_DBG("Request for pkt %u from card", bcsp->rxack);
 
+	/* handle received ACK indications,
+	 * including those from out-of-order packets
+	 */
 	bcsp_pkt_cull(bcsp);
-	if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
-	    bcsp->rx_skb->data[0] & 0x80) {
-		hci_skb_pkt_type(bcsp->rx_skb) = HCI_ACLDATA_PKT;
-		pass_up = 1;
-	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
-		   bcsp->rx_skb->data[0] & 0x80) {
-		hci_skb_pkt_type(bcsp->rx_skb) = HCI_EVENT_PKT;
-		pass_up = 1;
-	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
-		hci_skb_pkt_type(bcsp->rx_skb) = HCI_SCODATA_PKT;
-		pass_up = 1;
-	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
-		   !(bcsp->rx_skb->data[0] & 0x80)) {
-		bcsp_handle_le_pkt(hu);
-		pass_up = 0;
-	} else
-		pass_up = 0;
-
-	if (!pass_up) {
+
+	if (pass_up != 2) {
+		if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
+		    (bcsp->rx_skb->data[0] & 0x80)) {
+			hci_skb_pkt_type(bcsp->rx_skb) = HCI_ACLDATA_PKT;
+			pass_up = 1;
+		} else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
+			   (bcsp->rx_skb->data[0] & 0x80)) {
+			hci_skb_pkt_type(bcsp->rx_skb) = HCI_EVENT_PKT;
+			pass_up = 1;
+		} else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
+			hci_skb_pkt_type(bcsp->rx_skb) = HCI_SCODATA_PKT;
+			pass_up = 1;
+		} else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
+			   !(bcsp->rx_skb->data[0] & 0x80)) {
+			bcsp_handle_le_pkt(hu);
+			pass_up = 0;
+		} else {
+			pass_up = 0;
+		}
+	}
+
+	if (pass_up == 0) {
 		struct hci_event_hdr hdr;
 		u8 desc = (bcsp->rx_skb->data[1] & 0x0f);
 
@@ -547,11 +569,16 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
 			}
 		} else
 			kfree_skb(bcsp->rx_skb);
-	} else {
+	} else if (pass_up == 1) {
 		/* Pull out BCSP hdr */
 		skb_pull(bcsp->rx_skb, 4);
 
 		hci_recv_frame(hu->hdev, bcsp->rx_skb);
+	} else {
+		/* ignore packet payload of already ACKed re-transmitted
+		 * packets or when a packet was missed in the BCSP window
+		 */
+		kfree_skb(bcsp->rx_skb);
 	}
 
 	bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
@@ -597,16 +624,6 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
 				bcsp->rx_count = 0;
 				continue;
 			}
-			if (bcsp->rx_skb->data[0] & 0x80 &&	/* reliable pkt */
-			    (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) {
-				BT_ERR("Out-of-order packet arrived, got %u expected %u",
-				       bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
-
-				kfree_skb(bcsp->rx_skb);
-				bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
-				bcsp->rx_count = 0;
-				continue;
-			}
 			bcsp->rx_state = BCSP_W4_DATA;
 			bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) +
 					(bcsp->rx_skb->data[2] << 4);	/* May be 0 */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH V2 3/7] Bluetooth: Use single return in hci_uart_tty_ioctl() call
  2016-09-23 17:56 [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes Dean Jenkins
  2016-09-23 17:56 ` [PATCH V2 1/7] Bluetooth: Tidy-up coding style in hci_bcsp.c Dean Jenkins
  2016-09-23 17:56 ` [PATCH V2 2/7] Bluetooth: BCSP fails to ACK re-transmitted frames from the peer Dean Jenkins
@ 2016-09-23 17:56 ` Dean Jenkins
  2016-09-23 17:56 ` [PATCH V2 4/7] Bluetooth: Add mutex to hci_uart_tty_ioctl() Dean Jenkins
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Dean Jenkins @ 2016-09-23 17:56 UTC (permalink / raw)
  To: Dean Jenkins, Marcel Holtmann, Gustavo Padovan, Johan Hedberg
  Cc: linux-bluetooth

From: Vignesh Raman <Vignesh_Raman@mentor.com>

Remove multiple return statements in hci_uart_tty_ioctl() call and
added a single return statement.

This code re-organisation allows subsequent locking to be easily
added.

Signed-off-by: Vignesh Raman <Vignesh_Raman@mentor.com>
Signed-off-by: Dean Jenkins <Dean_Jenkins@mentor.com>
Signed-off-by: Rajeev Kumar <rajeev_kumar@mentor.com>
---
 drivers/bluetooth/hci_ldisc.c | 28 +++++++++++++++-------------
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 9a3aab6..9497c46 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -697,34 +697,36 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
 	case HCIUARTSETPROTO:
 		if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
 			err = hci_uart_set_proto(hu, arg);
-			if (err) {
+			if (err)
 				clear_bit(HCI_UART_PROTO_SET, &hu->flags);
-				return err;
-			}
 		} else
-			return -EBUSY;
+			err = -EBUSY;
 		break;
 
 	case HCIUARTGETPROTO:
 		if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
-			return hu->proto->id;
-		return -EUNATCH;
+			err = hu->proto->id;
+		else
+			err = -EUNATCH;
+		break;
 
 	case HCIUARTGETDEVICE:
 		if (test_bit(HCI_UART_REGISTERED, &hu->flags))
-			return hu->hdev->id;
-		return -EUNATCH;
+			err = hu->hdev->id;
+		else
+			err = -EUNATCH;
+		break;
 
 	case HCIUARTSETFLAGS:
 		if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
-			return -EBUSY;
-		err = hci_uart_set_flags(hu, arg);
-		if (err)
-			return err;
+			err = -EBUSY;
+		else
+			err = hci_uart_set_flags(hu, arg);
 		break;
 
 	case HCIUARTGETFLAGS:
-		return hu->hdev_flags;
+		err = hu->hdev_flags;
+		break;
 
 	default:
 		err = n_tty_ioctl_helper(tty, file, cmd, arg);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH V2 4/7] Bluetooth: Add mutex to hci_uart_tty_ioctl()
  2016-09-23 17:56 [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes Dean Jenkins
                   ` (2 preceding siblings ...)
  2016-09-23 17:56 ` [PATCH V2 3/7] Bluetooth: Use single return in hci_uart_tty_ioctl() call Dean Jenkins
@ 2016-09-23 17:56 ` Dean Jenkins
  2016-09-24  4:41   ` Marcel Holtmann
  2016-09-23 17:56 ` [PATCH V2 5/7] Bluetooth: Fix HCI UART HCI_UART_PROTO_SET locking Dean Jenkins
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 11+ messages in thread
From: Dean Jenkins @ 2016-09-23 17:56 UTC (permalink / raw)
  To: Dean Jenkins, Marcel Holtmann, Gustavo Padovan, Johan Hedberg
  Cc: linux-bluetooth

Add global mutex lock to prevent reentrancy in the hci_uart_tty_ioctl()
ioctl handling. This prevents concurrency of all function calls within
hci_uart_tty_ioctl() handling including hci_uart_set_proto().

Signed-off-by: Vignesh Raman <Vignesh_Raman@mentor.com>
Signed-off-by: Dean Jenkins <Dean_Jenkins@mentor.com>
---
 drivers/bluetooth/hci_ldisc.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 9497c46..01590f6 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -51,6 +51,8 @@
 
 #define VERSION "2.3"
 
+static DEFINE_MUTEX(ioctl_mutex);
+
 static const struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
 
 int hci_uart_register_proto(const struct hci_uart_proto *p)
@@ -693,6 +695,7 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
 	if (!hu)
 		return -EBADF;
 
+	mutex_lock(&ioctl_mutex);
 	switch (cmd) {
 	case HCIUARTSETPROTO:
 		if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
@@ -732,6 +735,7 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
 		err = n_tty_ioctl_helper(tty, file, cmd, arg);
 		break;
 	}
+	mutex_unlock(&ioctl_mutex);
 
 	return err;
 }
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH V2 5/7] Bluetooth: Fix HCI UART HCI_UART_PROTO_SET locking
  2016-09-23 17:56 [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes Dean Jenkins
                   ` (3 preceding siblings ...)
  2016-09-23 17:56 ` [PATCH V2 4/7] Bluetooth: Add mutex to hci_uart_tty_ioctl() Dean Jenkins
@ 2016-09-23 17:56 ` Dean Jenkins
  2016-09-24  4:41   ` Marcel Holtmann
  2016-09-23 17:56 ` [PATCH V2 6/7] Revert "Bluetooth: hci_ldisc: Fix null pointer derefence in case of early data" Dean Jenkins
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 11+ messages in thread
From: Dean Jenkins @ 2016-09-23 17:56 UTC (permalink / raw)
  To: Dean Jenkins, Marcel Holtmann, Gustavo Padovan, Johan Hedberg
  Cc: linux-bluetooth

From: Vignesh Raman <Vignesh_Raman@mentor.com>

Here is a NULL pointer dereference that causes a Bluetooth crash:

(hci_uart_tty_receive+0x0/0x84 [hci_uart]) from (flush_to_ldisc+0x104/0x190)
(flush_to_ldisc+0x0/0x190) from (tty_flip_buffer_push+0x50/0x5c)
(tty_flip_buffer_push+0x0/0x5c) from (imx_rxint+0x228/0x23c)
(imx_rxint+0x0/0x23c) from (imx_int+0xa8/0x174)
(imx_int+0x0/0x174) from (handle_irq_event_percpu+0x90/0x280)
(handle_irq_event_percpu+0x0/0x280) from (handle_irq_event+0x44/0x64)
(handle_irq_event+0x0/0x64) from (handle_fasteoi_irq+0xc0/0x108)
(handle_fasteoi_irq+0x0/0x108) from (generic_handle_irq+0x28/0x38)
(generic_handle_irq+0x0/0x38) from (handle_IRQ+0x6c/0x94)
(handle_IRQ+0x0/0x94) from (gic_handle_irq+0x44/0x68)
(gic_handle_irq+0x0/0x68) from (__irq_svc+0x40/0x70)
(bcsp_open+0x0/0xcc [hci_uart]) from (hci_uart_tty_ioctl+0xa8/0x238 [hci_uart])
(hci_uart_tty_ioctl+0x0/0x238 [hci_uart]) from (tty_ioctl+0xa88/0xae8)
(tty_ioctl+0x0/0xae8) from (vfs_ioctl+0x30/0x44)
(vfs_ioctl+0x0/0x44) from (do_vfs_ioctl+0x53c/0x590)
(do_vfs_ioctl+0x0/0x590) from (sys_ioctl+0x40/0x68)
(sys_ioctl+0x0/0x68) from (ret_fast_syscall+0x0/0x30)

It can be seen that bcsp_open() is pre-empted by an interrupt relating
to reception of UART characters. bcsp_open() is called from
hci_uart_set_proto() which is called from hci_uart_tty_ioctl() due to
HCIUARTSETPROTO handling.

The crash occurs in hci_uart_tty_receive() and analysis shows:
	if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
		return;

	spin_lock(&hu->rx_lock);
	hu->proto->recv(hu, (void *) data, count);

The crash occurs in the dereference hu->proto->recv. Presumably no recv
function has been set and a NULL pointer dereference occurs.

Note that the flag HCI_UART_PROTO_SET being clear should prevent the failure
but in fact fails. This indicates that HCI_UART_PROTO_SET is in the set state.

The HCI_UART_PROTO_SET locking fails because HCI_UART_PROTO_SET is put into
the set state in hci_uart_tty_ioctl() BEFORE calling hci_uart_set_proto()
to set the recv function pointer. Therefore, there is a race condition.

It would seem that HCI_UART_PROTO_SET was being used as a dual flag:

a) HCI_UART_PROTO_SET being used to indicate the protocol Data Link
layer had been successfully opened.

b) HCI_UART_PROTO_SET being used as a lockless solution for
hci_uart_tty_ioctl(). (mutex now added by a previous commit to prevent
concurrency so HCI_UART_PROTO_SET no longer needs to fill that role.)

The solution is to set the flag HCI_UART_PROTO_SET after successfully
calling hci_uart_set_proto() in the HCIUARTSETPROTO handling in
hci_uart_tty_ioctl(). This will prevent hci_uart_tty_receive() from
performing the recv function pointer dereference until hci_uart_set_proto()
has set the recv function pointer.

Signed-off-by: Vignesh Raman <Vignesh_Raman@mentor.com>
Signed-off-by: Dean Jenkins <Dean_Jenkins@mentor.com>
Signed-off-by: Mark Craske <Mark_Craske@mentor.com>
---
 drivers/bluetooth/hci_ldisc.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 01590f6..1fc9817 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -698,10 +698,10 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
 	mutex_lock(&ioctl_mutex);
 	switch (cmd) {
 	case HCIUARTSETPROTO:
-		if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+		if (!test_bit(HCI_UART_PROTO_SET, &hu->flags)) {
 			err = hci_uart_set_proto(hu, arg);
-			if (err)
-				clear_bit(HCI_UART_PROTO_SET, &hu->flags);
+			if (!err)
+				set_bit(HCI_UART_PROTO_SET, &hu->flags);
 		} else
 			err = -EBUSY;
 		break;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH V2 6/7] Revert "Bluetooth: hci_ldisc: Fix null pointer derefence in case of early data"
  2016-09-23 17:56 [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes Dean Jenkins
                   ` (4 preceding siblings ...)
  2016-09-23 17:56 ` [PATCH V2 5/7] Bluetooth: Fix HCI UART HCI_UART_PROTO_SET locking Dean Jenkins
@ 2016-09-23 17:56 ` Dean Jenkins
  2016-09-23 17:56 ` [PATCH V2 7/7] Bluetooth: Prevent scheduling of work after hci_uart_tty_close() Dean Jenkins
  2016-09-24  4:41 ` [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes Marcel Holtmann
  7 siblings, 0 replies; 11+ messages in thread
From: Dean Jenkins @ 2016-09-23 17:56 UTC (permalink / raw)
  To: Dean Jenkins, Marcel Holtmann, Gustavo Padovan, Johan Hedberg
  Cc: linux-bluetooth

This reverts commit 84cb3df02aea4b00405521e67c4c67c2d525c364.

This commit solved the specific race condition mentioned in the commit:

"HCI_UART_PROTO_SET flag is set before hci_uart_set_proto call. If we
receive data from tty layer during this procedure, proto pointer may
not be assigned yet, leading to null pointer dereference in rx
method hci_uart_tty_receive.

This patch fixes this issue by introducing HCI_UART_PROTO_READY flag
in order to avoid any proto operation before proto opening and
assignment."

The design of the commit seems to be attempting to implement a
lockless solution by relying on atomic variables.

However, HCI_UART_PROTO_READY introduces a race condition here:

static int hci_uart_set_proto(struct hci_uart *hu,
...
        hu->proto = p;
+       set_bit(HCI_UART_PROTO_READY, &hu->flags);

        err = hci_uart_register_dev(hu);
        if (err) {
+               clear_bit(HCI_UART_PROTO_READY, &hu->flags);
                p->close(hu);
                return err;
        }

The issue here is that hci_uart_register_dev() allocates the
memory for hu->hdev and HCI_UART_PROTO_READY is set before
hdev is allocated. This means there is a risk that an unallocated hdev
is accessed because HCI_UART_PROTO_READY is set too early.

For example, hci_uart_tty_wakeup() used the HCI_UART_PROTO_READY flag
which means a transmission work item could potentially be scheduled
in hci_uart_tx_wakeup() when hdev has not been allocated.

Instead use a solution using locking supplied in commits:
"Bluetooth: Use single return in hci_uart_tty_ioctl() call"
"Bluetooth: Add mutex to hci_uart_tty_ioctl()"
"Bluetooth: Fix HCI UART HCI_UART_PROTO_SET locking"

Signed-off-by: Dean Jenkins <Dean_Jenkins@mentor.com>
---
 drivers/bluetooth/hci_ldisc.c | 11 ++++-------
 drivers/bluetooth/hci_uart.h  |  1 -
 2 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 1fc9817..499fb09 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -229,7 +229,7 @@ static int hci_uart_flush(struct hci_dev *hdev)
 	tty_ldisc_flush(tty);
 	tty_driver_flush_buffer(tty);
 
-	if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
+	if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
 		hu->proto->flush(hu);
 
 	return 0;
@@ -494,7 +494,7 @@ static void hci_uart_tty_close(struct tty_struct *tty)
 
 	cancel_work_sync(&hu->write_work);
 
-	if (test_and_clear_bit(HCI_UART_PROTO_READY, &hu->flags)) {
+	if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
 		if (hdev) {
 			if (test_bit(HCI_UART_REGISTERED, &hu->flags))
 				hci_unregister_dev(hdev);
@@ -502,7 +502,6 @@ static void hci_uart_tty_close(struct tty_struct *tty)
 		}
 		hu->proto->close(hu);
 	}
-	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
 
 	kfree(hu);
 }
@@ -529,7 +528,7 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty)
 	if (tty != hu->tty)
 		return;
 
-	if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
+	if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
 		hci_uart_tx_wakeup(hu);
 }
 
@@ -553,7 +552,7 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
 	if (!hu || tty != hu->tty)
 		return;
 
-	if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
+	if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
 		return;
 
 	/* It does not need a lock here as it is already protected by a mutex in
@@ -641,11 +640,9 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
 		return err;
 
 	hu->proto = p;
-	set_bit(HCI_UART_PROTO_READY, &hu->flags);
 
 	err = hci_uart_register_dev(hu);
 	if (err) {
-		clear_bit(HCI_UART_PROTO_READY, &hu->flags);
 		p->close(hu);
 		return err;
 	}
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 0701395..f0707f4 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -97,7 +97,6 @@ struct hci_uart {
 /* HCI_UART proto flag bits */
 #define HCI_UART_PROTO_SET	0
 #define HCI_UART_REGISTERED	1
-#define HCI_UART_PROTO_READY	2
 
 /* TX states  */
 #define HCI_UART_SENDING	1
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH V2 7/7] Bluetooth: Prevent scheduling of work after hci_uart_tty_close()
  2016-09-23 17:56 [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes Dean Jenkins
                   ` (5 preceding siblings ...)
  2016-09-23 17:56 ` [PATCH V2 6/7] Revert "Bluetooth: hci_ldisc: Fix null pointer derefence in case of early data" Dean Jenkins
@ 2016-09-23 17:56 ` Dean Jenkins
  2016-09-24  4:41 ` [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes Marcel Holtmann
  7 siblings, 0 replies; 11+ messages in thread
From: Dean Jenkins @ 2016-09-23 17:56 UTC (permalink / raw)
  To: Dean Jenkins, Marcel Holtmann, Gustavo Padovan, Johan Hedberg
  Cc: linux-bluetooth

From: Deepak Das <Deepak_Das@mentor.com>

There is a work queue hu->write_work that can be scheduled to handle
a transmission work item in BCSP scenarios that are described below.

In these scenarios, the payload of a BCSP frame can contain a HCI
message as follows:

a) Normal sending of a HCI message within a BCSP frame from upper
layers via hci_uart_send_frame().

b) BCSP retransmission due to a failure to get a BCSP ACK indication.
This resends an old HCI message triggered by the BCSP timer timeout event
handled by bcsp_timed_event().

c) Sending of a BCSP ACK frame in response to a received reliable BCSP
frame is handled in bcsp_complete_rx_pkt() which allows an empty payload
(no HCI message) to be sent.

A good reproduction scenario of the crash is for a bad UART driver to
corrupt or cause a total loss of incoming BCSP frames. This leads to a link
failure so the BCSP timer is periodically triggered in an attempt to
retransmit unacknowledged frames. Then upper layers request the HCI UART
to close immediately before the BCSP timer event is handled. This causes
a race condition between closing the HCI UART and retransmitting a BCSP
frame.

The current solution is using cancel_work_sync(&hu->write_work) within
hci_uart_tty_close() in an attempt to cleanse the work queue of the
transmission work item.

It should be noted that a transmission work item is scheduled into the work
queue by hci_uart_tx_wakeup() which can be running in an atomic context
such as a UART driver interrupt thread or in a process context. Therefore,
it is possible for hci_uart_tx_wakeup() to interrupt or run concurrently
with hci_uart_tty_close().

In addition, hu->proto->close(hu) calls bcsp_close() which frees the
various queues of messages and deletes the BCSP timer.

The current solution has a flaw because hci_uart_tty_close() fails to
inhibit the asynchronous b) and c) transmission events from adding a work
item to the work queue AFTER cancel_work_sync(&hu->write_work) has been
executed. A crash occurs after bcsp_close() has freed the message queues
which are subsequently accessed when the work queue work item runs
hci_uart_write_work(). Therefore, there is a race condition.

Also it has been seen that hci_unregister_dev(hdev) may attempt
to send HCI frames via hci_uart_send_frame() which is AFTER
cancel_work_sync(&hu->write_work) runs. hci_unregister_dev(hdev) was
seen to run for 2 seconds. This could trigger BCSP retransmissions
leading to a crash after the subsequent call to hu->proto->close(hu).
The cancel_work_sync(&hu->write_work) provides no protection in this
case.

This flaw was masked as typically retransmissions should be rare and
Bluetooth has a low sub-system rate of being actively shutdown.

This fix introduces a new HCI_UART proto flag bit called
HCI_UART_UNREGISTERING to indicate the unregistering state of the HCI UART.
This flag is used to prevent the addition of a transmission work item after
hci_uart_tty_close() starts to run.

Note that a spinlock is necessary because it is not possible to check
the state of the atomic flag and to schedule the work queue in an atomic
sequence of operations in hci_uart_tx_wakeup(). This means that the flag
must be prevented from changing state after the flag is checked and before
the work queue is scheduled in hci_uart_tx_wakeup().

Signed-off-by: Deepak Das <Deepak_Das@mentor.com>
Signed-off-by: Rajeev Kumar <rajeev_kumar@mentor.com>
Signed-off-by: Dean_Jenkins@mentor.com
---
 drivers/bluetooth/hci_ldisc.c | 16 +++++++++++++++-
 drivers/bluetooth/hci_uart.h  |  3 +++
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 499fb09..44bdbbc 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -125,15 +125,22 @@ static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
 
 int hci_uart_tx_wakeup(struct hci_uart *hu)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&hu->schedule_lock, flags);
+	if (test_bit(HCI_UART_UNREGISTERING, &hu->flags))
+		goto no_schedule;
 	if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) {
 		set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
-		return 0;
+		goto no_schedule;
 	}
 
 	BT_DBG("");
 
 	schedule_work(&hu->write_work);
 
+no_schedule:
+	spin_unlock_irqrestore(&hu->schedule_lock, flags);
 	return 0;
 }
 
@@ -464,6 +471,8 @@ static int hci_uart_tty_open(struct tty_struct *tty)
 	INIT_WORK(&hu->init_ready, hci_uart_init_work);
 	INIT_WORK(&hu->write_work, hci_uart_write_work);
 
+	spin_lock_init(&hu->schedule_lock);
+
 	/* Flush any pending characters in the driver */
 	tty_driver_flush_buffer(tty);
 
@@ -479,6 +488,7 @@ static void hci_uart_tty_close(struct tty_struct *tty)
 {
 	struct hci_uart *hu = tty->disc_data;
 	struct hci_dev *hdev;
+	unsigned long flags;
 
 	BT_DBG("tty %p", tty);
 
@@ -488,6 +498,10 @@ static void hci_uart_tty_close(struct tty_struct *tty)
 	if (!hu)
 		return;
 
+	spin_lock_irqsave(&hu->schedule_lock, flags);
+	set_bit(HCI_UART_UNREGISTERING, &hu->flags);
+	spin_unlock_irqrestore(&hu->schedule_lock, flags);
+
 	hdev = hu->hdev;
 	if (hdev)
 		hci_uart_close(hdev);
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index f0707f4..2cc63db 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -90,6 +90,8 @@ struct hci_uart {
 	struct sk_buff		*tx_skb;
 	unsigned long		tx_state;
 
+	/* prevent scheduling work during close */
+	spinlock_t		schedule_lock;
 	unsigned int init_speed;
 	unsigned int oper_speed;
 };
@@ -97,6 +99,7 @@ struct hci_uart {
 /* HCI_UART proto flag bits */
 #define HCI_UART_PROTO_SET	0
 #define HCI_UART_REGISTERED	1
+#define HCI_UART_UNREGISTERING	2
 
 /* TX states  */
 #define HCI_UART_SENDING	1
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes
  2016-09-23 17:56 [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes Dean Jenkins
                   ` (6 preceding siblings ...)
  2016-09-23 17:56 ` [PATCH V2 7/7] Bluetooth: Prevent scheduling of work after hci_uart_tty_close() Dean Jenkins
@ 2016-09-24  4:41 ` Marcel Holtmann
  7 siblings, 0 replies; 11+ messages in thread
From: Marcel Holtmann @ 2016-09-24  4:41 UTC (permalink / raw)
  To: Dean Jenkins; +Cc: Gustavo F. Padovan, Johan Hedberg, linux-bluetooth

Hi Dean,

> This patchset contains the following commits:
> 
> Dean Jenkins (4):
>  Bluetooth: Tidy-up coding style in hci_bcsp.c
>  Bluetooth: BCSP fails to ACK re-transmitted frames from the peer
>  Bluetooth: Add mutex to hci_uart_tty_ioctl()
>  Revert "Bluetooth: hci_ldisc: Fix null pointer derefence in case of
>    early data"
> 
> Deepak Das (1):
>  Bluetooth: Prevent scheduling of work after hci_uart_tty_close()
> 
> Vignesh Raman (2):
>  Bluetooth: Use single return in hci_uart_tty_ioctl() call
>  Bluetooth: Fix HCI UART HCI_UART_PROTO_SET locking
> 
> drivers/bluetooth/hci_bcsp.c  | 128 ++++++++++++++++++++++++------------------
> drivers/bluetooth/hci_ldisc.c |  63 +++++++++++++--------
> drivers/bluetooth/hci_uart.h  |   4 +-
> 3 files changed, 117 insertions(+), 78 deletions(-)

I applied patches 1-3 to bluetooth-next tree.

Regards

Marcel


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH V2 4/7] Bluetooth: Add mutex to hci_uart_tty_ioctl()
  2016-09-23 17:56 ` [PATCH V2 4/7] Bluetooth: Add mutex to hci_uart_tty_ioctl() Dean Jenkins
@ 2016-09-24  4:41   ` Marcel Holtmann
  0 siblings, 0 replies; 11+ messages in thread
From: Marcel Holtmann @ 2016-09-24  4:41 UTC (permalink / raw)
  To: Dean Jenkins; +Cc: Gustavo F. Padovan, Johan Hedberg, linux-bluetooth

Hi Dean,

> Add global mutex lock to prevent reentrancy in the hci_uart_tty_ioctl()
> ioctl handling. This prevents concurrency of all function calls within
> hci_uart_tty_ioctl() handling including hci_uart_set_proto().
> 
> Signed-off-by: Vignesh Raman <Vignesh_Raman@mentor.com>
> Signed-off-by: Dean Jenkins <Dean_Jenkins@mentor.com>
> ---
> drivers/bluetooth/hci_ldisc.c | 4 ++++
> 1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
> index 9497c46..01590f6 100644
> --- a/drivers/bluetooth/hci_ldisc.c
> +++ b/drivers/bluetooth/hci_ldisc.c
> @@ -51,6 +51,8 @@
> 
> #define VERSION "2.3"
> 
> +static DEFINE_MUTEX(ioctl_mutex);
> +

explain to me why we need a global lock. I think that look can be per struct hci_uart.

Regards

Marcel


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH V2 5/7] Bluetooth: Fix HCI UART HCI_UART_PROTO_SET locking
  2016-09-23 17:56 ` [PATCH V2 5/7] Bluetooth: Fix HCI UART HCI_UART_PROTO_SET locking Dean Jenkins
@ 2016-09-24  4:41   ` Marcel Holtmann
  0 siblings, 0 replies; 11+ messages in thread
From: Marcel Holtmann @ 2016-09-24  4:41 UTC (permalink / raw)
  To: Dean Jenkins; +Cc: Gustavo F. Padovan, Johan Hedberg, linux-bluetooth

Hi Dean,

> Here is a NULL pointer dereference that causes a Bluetooth crash:
> 
> (hci_uart_tty_receive+0x0/0x84 [hci_uart]) from (flush_to_ldisc+0x104/0x190)
> (flush_to_ldisc+0x0/0x190) from (tty_flip_buffer_push+0x50/0x5c)
> (tty_flip_buffer_push+0x0/0x5c) from (imx_rxint+0x228/0x23c)
> (imx_rxint+0x0/0x23c) from (imx_int+0xa8/0x174)
> (imx_int+0x0/0x174) from (handle_irq_event_percpu+0x90/0x280)
> (handle_irq_event_percpu+0x0/0x280) from (handle_irq_event+0x44/0x64)
> (handle_irq_event+0x0/0x64) from (handle_fasteoi_irq+0xc0/0x108)
> (handle_fasteoi_irq+0x0/0x108) from (generic_handle_irq+0x28/0x38)
> (generic_handle_irq+0x0/0x38) from (handle_IRQ+0x6c/0x94)
> (handle_IRQ+0x0/0x94) from (gic_handle_irq+0x44/0x68)
> (gic_handle_irq+0x0/0x68) from (__irq_svc+0x40/0x70)
> (bcsp_open+0x0/0xcc [hci_uart]) from (hci_uart_tty_ioctl+0xa8/0x238 [hci_uart])
> (hci_uart_tty_ioctl+0x0/0x238 [hci_uart]) from (tty_ioctl+0xa88/0xae8)
> (tty_ioctl+0x0/0xae8) from (vfs_ioctl+0x30/0x44)
> (vfs_ioctl+0x0/0x44) from (do_vfs_ioctl+0x53c/0x590)
> (do_vfs_ioctl+0x0/0x590) from (sys_ioctl+0x40/0x68)
> (sys_ioctl+0x0/0x68) from (ret_fast_syscall+0x0/0x30)
> 
> It can be seen that bcsp_open() is pre-empted by an interrupt relating
> to reception of UART characters. bcsp_open() is called from
> hci_uart_set_proto() which is called from hci_uart_tty_ioctl() due to
> HCIUARTSETPROTO handling.
> 
> The crash occurs in hci_uart_tty_receive() and analysis shows:
> 	if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
> 		return;
> 
> 	spin_lock(&hu->rx_lock);
> 	hu->proto->recv(hu, (void *) data, count);
> 
> The crash occurs in the dereference hu->proto->recv. Presumably no recv
> function has been set and a NULL pointer dereference occurs.
> 
> Note that the flag HCI_UART_PROTO_SET being clear should prevent the failure
> but in fact fails. This indicates that HCI_UART_PROTO_SET is in the set state.
> 
> The HCI_UART_PROTO_SET locking fails because HCI_UART_PROTO_SET is put into
> the set state in hci_uart_tty_ioctl() BEFORE calling hci_uart_set_proto()
> to set the recv function pointer. Therefore, there is a race condition.
> 
> It would seem that HCI_UART_PROTO_SET was being used as a dual flag:
> 
> a) HCI_UART_PROTO_SET being used to indicate the protocol Data Link
> layer had been successfully opened.
> 
> b) HCI_UART_PROTO_SET being used as a lockless solution for
> hci_uart_tty_ioctl(). (mutex now added by a previous commit to prevent
> concurrency so HCI_UART_PROTO_SET no longer needs to fill that role.)
> 
> The solution is to set the flag HCI_UART_PROTO_SET after successfully
> calling hci_uart_set_proto() in the HCIUARTSETPROTO handling in
> hci_uart_tty_ioctl(). This will prevent hci_uart_tty_receive() from
> performing the recv function pointer dereference until hci_uart_set_proto()
> has set the recv function pointer.
> 
> Signed-off-by: Vignesh Raman <Vignesh_Raman@mentor.com>
> Signed-off-by: Dean Jenkins <Dean_Jenkins@mentor.com>
> Signed-off-by: Mark Craske <Mark_Craske@mentor.com>
> ---
> drivers/bluetooth/hci_ldisc.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
> index 01590f6..1fc9817 100644
> --- a/drivers/bluetooth/hci_ldisc.c
> +++ b/drivers/bluetooth/hci_ldisc.c
> @@ -698,10 +698,10 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
> 	mutex_lock(&ioctl_mutex);
> 	switch (cmd) {
> 	case HCIUARTSETPROTO:
> -		if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
> +		if (!test_bit(HCI_UART_PROTO_SET, &hu->flags)) {
> 			err = hci_uart_set_proto(hu, arg);
> -			if (err)
> -				clear_bit(HCI_UART_PROTO_SET, &hu->flags);
> +			if (!err)
> +				set_bit(HCI_UART_PROTO_SET, &hu->flags);
> 		} else

the extensive commit message is great, but can be get a small comment included here why we are doing it this way. I think that would help to accidentally revert it back to what it was in the future.

Regards

Marcel


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2016-09-24  4:41 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-23 17:56 [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes Dean Jenkins
2016-09-23 17:56 ` [PATCH V2 1/7] Bluetooth: Tidy-up coding style in hci_bcsp.c Dean Jenkins
2016-09-23 17:56 ` [PATCH V2 2/7] Bluetooth: BCSP fails to ACK re-transmitted frames from the peer Dean Jenkins
2016-09-23 17:56 ` [PATCH V2 3/7] Bluetooth: Use single return in hci_uart_tty_ioctl() call Dean Jenkins
2016-09-23 17:56 ` [PATCH V2 4/7] Bluetooth: Add mutex to hci_uart_tty_ioctl() Dean Jenkins
2016-09-24  4:41   ` Marcel Holtmann
2016-09-23 17:56 ` [PATCH V2 5/7] Bluetooth: Fix HCI UART HCI_UART_PROTO_SET locking Dean Jenkins
2016-09-24  4:41   ` Marcel Holtmann
2016-09-23 17:56 ` [PATCH V2 6/7] Revert "Bluetooth: hci_ldisc: Fix null pointer derefence in case of early data" Dean Jenkins
2016-09-23 17:56 ` [PATCH V2 7/7] Bluetooth: Prevent scheduling of work after hci_uart_tty_close() Dean Jenkins
2016-09-24  4:41 ` [PATCH V2 0/7] Bluetooth HCI LDISC and BCSP fixes Marcel Holtmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.