Bluetooth: hci_qca: Fix QCA6390 memdump failure
authorZijun Hu <zijuhu@codeaurora.org>
Fri, 29 May 2020 14:38:31 +0000 (22:38 +0800)
committerMarcel Holtmann <marcel@holtmann.org>
Mon, 1 Jun 2020 06:07:33 +0000 (08:07 +0200)
QCA6390 memdump VSE sometimes come to bluetooth driver
with wrong sequence number as illustrated as follows:
frame # in dec: frame data in hex
1396: ff fd 01 08 74 05 00 37 8f 14
1397: ff fd 01 08 75 05 00 ff bf 38
1414: ff fd 01 08 86 05 00 fb 5e 4b
1399: ff fd 01 08 77 05 00 f3 44 0a
1400: ff fd 01 08 78 05 00 ca f7 41
it is mistook for controller missing packets, so results
in page fault after overwriting memdump buffer allocated.

Fixed by ignoring QCA6390 sequence number check and
checking buffer space before writing.

Signed-off-by: Zijun Hu <zijuhu@codeaurora.org>
Tested-by: Zijun Hu <zijuhu@codeaurora.org>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
drivers/bluetooth/hci_qca.c

index aa957d7..81c3c38 100644 (file)
@@ -114,6 +114,7 @@ struct qca_memdump_data {
        char *memdump_buf_tail;
        u32 current_seq_no;
        u32 received_dump;
+       u32 ram_dump_size;
 };
 
 struct qca_memdump_event_hdr {
@@ -976,6 +977,8 @@ static void qca_controller_memdump(struct work_struct *work)
        char nullBuff[QCA_DUMP_PACKET_SIZE] = { 0 };
        u16 seq_no;
        u32 dump_size;
+       u32 rx_size;
+       enum qca_btsoc_type soc_type = qca_soc_type(hu);
 
        while ((skb = skb_dequeue(&qca->rx_memdump_q))) {
 
@@ -1025,10 +1028,12 @@ static void qca_controller_memdump(struct work_struct *work)
                                    dump_size);
                        queue_delayed_work(qca->workqueue,
                                           &qca->ctrl_memdump_timeout,
-                                       msecs_to_jiffies(MEMDUMP_TIMEOUT_MS));
+                                          msecs_to_jiffies(MEMDUMP_TIMEOUT_MS)
+                                         );
 
                        skb_pull(skb, sizeof(dump_size));
                        memdump_buf = vmalloc(dump_size);
+                       qca_memdump->ram_dump_size = dump_size;
                        qca_memdump->memdump_buf_head = memdump_buf;
                        qca_memdump->memdump_buf_tail = memdump_buf;
                }
@@ -1051,26 +1056,57 @@ static void qca_controller_memdump(struct work_struct *work)
                 * the controller. In such cases let us store the dummy
                 * packets in the buffer.
                 */
+               /* For QCA6390, controller does not lost packets but
+                * sequence number field of packat sometimes has error
+                * bits, so skip this checking for missing packet.
+                */
                while ((seq_no > qca_memdump->current_seq_no + 1) &&
-                       seq_no != QCA_LAST_SEQUENCE_NUM) {
+                      (soc_type != QCA_QCA6390) &&
+                      seq_no != QCA_LAST_SEQUENCE_NUM) {
                        bt_dev_err(hu->hdev, "QCA controller missed packet:%d",
                                   qca_memdump->current_seq_no);
+                       rx_size = qca_memdump->received_dump;
+                       rx_size += QCA_DUMP_PACKET_SIZE;
+                       if (rx_size > qca_memdump->ram_dump_size) {
+                               bt_dev_err(hu->hdev,
+                                          "QCA memdump received %d, no space for missed packet",
+                                          qca_memdump->received_dump);
+                               break;
+                       }
                        memcpy(memdump_buf, nullBuff, QCA_DUMP_PACKET_SIZE);
                        memdump_buf = memdump_buf + QCA_DUMP_PACKET_SIZE;
                        qca_memdump->received_dump += QCA_DUMP_PACKET_SIZE;
                        qca_memdump->current_seq_no++;
                }
 
-               memcpy(memdump_buf, (unsigned char *) skb->data, skb->len);
-               memdump_buf = memdump_buf + skb->len;
-               qca_memdump->memdump_buf_tail = memdump_buf;
-               qca_memdump->current_seq_no = seq_no + 1;
-               qca_memdump->received_dump += skb->len;
+               rx_size = qca_memdump->received_dump + skb->len;
+               if (rx_size <= qca_memdump->ram_dump_size) {
+                       if ((seq_no != QCA_LAST_SEQUENCE_NUM) &&
+                           (seq_no != qca_memdump->current_seq_no))
+                               bt_dev_err(hu->hdev,
+                                          "QCA memdump unexpected packet %d",
+                                          seq_no);
+                       bt_dev_dbg(hu->hdev,
+                                  "QCA memdump packet %d with length %d",
+                                  seq_no, skb->len);
+                       memcpy(memdump_buf, (unsigned char *)skb->data,
+                              skb->len);
+                       memdump_buf = memdump_buf + skb->len;
+                       qca_memdump->memdump_buf_tail = memdump_buf;
+                       qca_memdump->current_seq_no = seq_no + 1;
+                       qca_memdump->received_dump += skb->len;
+               } else {
+                       bt_dev_err(hu->hdev,
+                                  "QCA memdump received %d, no space for packet %d",
+                                  qca_memdump->received_dump, seq_no);
+               }
                qca->qca_memdump = qca_memdump;
                kfree_skb(skb);
                if (seq_no == QCA_LAST_SEQUENCE_NUM) {
-                       bt_dev_info(hu->hdev, "QCA writing crash dump of size %d bytes",
-                                  qca_memdump->received_dump);
+                       bt_dev_info(hu->hdev,
+                                   "QCA memdump Done, received %d, total %d",
+                                   qca_memdump->received_dump,
+                                   qca_memdump->ram_dump_size);
                        memdump_buf = qca_memdump->memdump_buf_head;
                        dev_coredumpv(&hu->serdev->dev, memdump_buf,
                                      qca_memdump->received_dump, GFP_KERNEL);