Bluetooth: btusb: fix excessive stack usage
authorArnd Bergmann <arnd@arndb.de>
Thu, 4 Feb 2021 15:47:07 +0000 (16:47 +0100)
committerMarcel Holtmann <marcel@holtmann.org>
Thu, 4 Feb 2021 17:11:23 +0000 (18:11 +0100)
Enlarging the size of 'struct btmtk_hci_wmt_cmd' makes it no longer
fit on the kernel stack, as seen from this compiler warning:

drivers/bluetooth/btusb.c:3365:12: error: stack frame size of 1036 bytes in function 'btusb_mtk_hci_wmt_sync' [-Werror,-Wframe-larger-than=]

Change the function to dynamically allocate the buffer instead.
As there are other sleeping functions called from the same location,
using GFP_KERNEL should be fine here, and the runtime overhead should
not matter as this is rarely called.

Unfortunately, I could not figure out why the message size is
increased in the previous patch. Using dynamic allocation means
any size is possible now, but there is still a range check that
limits the total size (including the five-byte header) to 255
bytes, so whatever was intended there is now undone.

Fixes: 48c13301e6ba ("Bluetooth: btusb: Fine-tune mt7663 mechanism.")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
drivers/bluetooth/btusb.c

index 01a8718..9c6836e 100644 (file)
@@ -3161,7 +3161,7 @@ struct btmtk_wmt_hdr {
 
 struct btmtk_hci_wmt_cmd {
        struct btmtk_wmt_hdr hdr;
-       u8 data[1000];
+       u8 data[];
 } __packed;
 
 struct btmtk_hci_wmt_evt {
@@ -3369,7 +3369,7 @@ static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
        struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc;
        u32 hlen, status = BTMTK_WMT_INVALID;
        struct btmtk_hci_wmt_evt *wmt_evt;
-       struct btmtk_hci_wmt_cmd wc;
+       struct btmtk_hci_wmt_cmd *wc;
        struct btmtk_wmt_hdr *hdr;
        int err;
 
@@ -3383,20 +3383,24 @@ static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
        if (hlen > 255)
                return -EINVAL;
 
-       hdr = (struct btmtk_wmt_hdr *)&wc;
+       wc = kzalloc(hlen, GFP_KERNEL);
+       if (!wc)
+               return -ENOMEM;
+
+       hdr = &wc->hdr;
        hdr->dir = 1;
        hdr->op = wmt_params->op;
        hdr->dlen = cpu_to_le16(wmt_params->dlen + 1);
        hdr->flag = wmt_params->flag;
-       memcpy(wc.data, wmt_params->data, wmt_params->dlen);
+       memcpy(wc->data, wmt_params->data, wmt_params->dlen);
 
        set_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
 
-       err = __hci_cmd_send(hdev, 0xfc6f, hlen, &wc);
+       err = __hci_cmd_send(hdev, 0xfc6f, hlen, wc);
 
        if (err < 0) {
                clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
-               return err;
+               goto err_free_wc;
        }
 
        /* The vendor specific WMT commands are all answered by a vendor
@@ -3413,13 +3417,14 @@ static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
        if (err == -EINTR) {
                bt_dev_err(hdev, "Execution of wmt command interrupted");
                clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
-               return err;
+               goto err_free_wc;
        }
 
        if (err) {
                bt_dev_err(hdev, "Execution of wmt command timed out");
                clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
-               return -ETIMEDOUT;
+               err = -ETIMEDOUT;
+               goto err_free_wc;
        }
 
        /* Parse and handle the return WMT event */
@@ -3463,7 +3468,8 @@ static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
 err_free_skb:
        kfree_skb(data->evt_skb);
        data->evt_skb = NULL;
-
+err_free_wc:
+       kfree(wc);
        return err;
 }