mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-03 21:45:08 -04:00
Merge tag 'for-net-2024-05-03' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth
Luiz Augusto von Dentz says: ==================== bluetooth pull request for net: - mediatek: mt8183-pico6: Fix bluetooth node - sco: Fix use-after-free bugs caused by sco_sock_timeout - l2cap: fix null-ptr-deref in l2cap_chan_timeout - qca: Various fixes - l2cap: Fix slab-use-after-free in l2cap_connect() - msft: fix slab-use-after-free in msft_do_close() - HCI: Fix potential null-ptr-deref * tag 'for-net-2024-05-03' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth: Bluetooth: qca: fix firmware check error path Bluetooth: l2cap: fix null-ptr-deref in l2cap_chan_timeout Bluetooth: HCI: Fix potential null-ptr-deref arm64: dts: mediatek: mt8183-pico6: Fix bluetooth node Bluetooth: qca: fix info leak when fetching board id Bluetooth: qca: fix info leak when fetching fw build id Bluetooth: qca: generalise device address check Bluetooth: qca: fix NVM configuration parsing Bluetooth: qca: add missing firmware sanity checks Bluetooth: msft: fix slab-use-after-free in msft_do_close() Bluetooth: L2CAP: Fix slab-use-after-free in l2cap_connect() Bluetooth: qca: fix wcn3991 device address check Bluetooth: Fix use-after-free bugs caused by sco_sock_timeout ==================== Link: https://lore.kernel.org/r/20240503171933.3851244-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -82,7 +82,8 @@ pins-clk {
|
||||
};
|
||||
|
||||
&mmc1 {
|
||||
bt_reset: bt-reset {
|
||||
bluetooth@2 {
|
||||
reg = <2>;
|
||||
compatible = "mediatek,mt7921s-bluetooth";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&bt_pins_reset>;
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
|
||||
#define VERSION "0.1"
|
||||
|
||||
#define QCA_BDADDR_DEFAULT (&(bdaddr_t) {{ 0xad, 0x5a, 0x00, 0x00, 0x00, 0x00 }})
|
||||
|
||||
int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
|
||||
enum qca_btsoc_type soc_type)
|
||||
{
|
||||
@@ -101,7 +99,8 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct edl_event_hdr *edl;
|
||||
char cmd, build_label[QCA_FW_BUILD_VER_LEN];
|
||||
char *build_label;
|
||||
char cmd;
|
||||
int build_lbl_len, err = 0;
|
||||
|
||||
bt_dev_dbg(hdev, "QCA read fw build info");
|
||||
@@ -116,6 +115,11 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (skb->len < sizeof(*edl)) {
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
|
||||
edl = (struct edl_event_hdr *)(skb->data);
|
||||
if (!edl) {
|
||||
bt_dev_err(hdev, "QCA read fw build info with no header");
|
||||
@@ -131,14 +135,25 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
build_lbl_len = edl->data[0];
|
||||
if (build_lbl_len <= QCA_FW_BUILD_VER_LEN - 1) {
|
||||
memcpy(build_label, edl->data + 1, build_lbl_len);
|
||||
*(build_label + build_lbl_len) = '\0';
|
||||
if (skb->len < sizeof(*edl) + 1) {
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
|
||||
build_lbl_len = edl->data[0];
|
||||
|
||||
if (skb->len < sizeof(*edl) + 1 + build_lbl_len) {
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
|
||||
build_label = kstrndup(&edl->data[1], build_lbl_len, GFP_KERNEL);
|
||||
if (!build_label)
|
||||
goto out;
|
||||
|
||||
hci_set_fw_info(hdev, "%s", build_label);
|
||||
|
||||
kfree(build_label);
|
||||
out:
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
@@ -237,6 +252,11 @@ static int qca_read_fw_board_id(struct hci_dev *hdev, u16 *bid)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (skb->len < 3) {
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*bid = (edl->data[1] << 8) + edl->data[2];
|
||||
bt_dev_dbg(hdev, "%s: bid = %x", __func__, *bid);
|
||||
|
||||
@@ -267,9 +287,10 @@ int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qca_send_pre_shutdown_cmd);
|
||||
|
||||
static void qca_tlv_check_data(struct hci_dev *hdev,
|
||||
static int qca_tlv_check_data(struct hci_dev *hdev,
|
||||
struct qca_fw_config *config,
|
||||
u8 *fw_data, enum qca_btsoc_type soc_type)
|
||||
u8 *fw_data, size_t fw_size,
|
||||
enum qca_btsoc_type soc_type)
|
||||
{
|
||||
const u8 *data;
|
||||
u32 type_len;
|
||||
@@ -279,12 +300,16 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
|
||||
struct tlv_type_patch *tlv_patch;
|
||||
struct tlv_type_nvm *tlv_nvm;
|
||||
uint8_t nvm_baud_rate = config->user_baud_rate;
|
||||
u8 type;
|
||||
|
||||
config->dnld_mode = QCA_SKIP_EVT_NONE;
|
||||
config->dnld_type = QCA_SKIP_EVT_NONE;
|
||||
|
||||
switch (config->type) {
|
||||
case ELF_TYPE_PATCH:
|
||||
if (fw_size < 7)
|
||||
return -EINVAL;
|
||||
|
||||
config->dnld_mode = QCA_SKIP_EVT_VSE_CC;
|
||||
config->dnld_type = QCA_SKIP_EVT_VSE_CC;
|
||||
|
||||
@@ -293,6 +318,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
|
||||
bt_dev_dbg(hdev, "File version : 0x%x", fw_data[6]);
|
||||
break;
|
||||
case TLV_TYPE_PATCH:
|
||||
if (fw_size < sizeof(struct tlv_type_hdr) + sizeof(struct tlv_type_patch))
|
||||
return -EINVAL;
|
||||
|
||||
tlv = (struct tlv_type_hdr *)fw_data;
|
||||
type_len = le32_to_cpu(tlv->type_len);
|
||||
tlv_patch = (struct tlv_type_patch *)tlv->data;
|
||||
@@ -332,25 +360,64 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
|
||||
break;
|
||||
|
||||
case TLV_TYPE_NVM:
|
||||
if (fw_size < sizeof(struct tlv_type_hdr))
|
||||
return -EINVAL;
|
||||
|
||||
tlv = (struct tlv_type_hdr *)fw_data;
|
||||
|
||||
type_len = le32_to_cpu(tlv->type_len);
|
||||
length = (type_len >> 8) & 0x00ffffff;
|
||||
length = type_len >> 8;
|
||||
type = type_len & 0xff;
|
||||
|
||||
BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
|
||||
/* Some NVM files have more than one set of tags, only parse
|
||||
* the first set when it has type 2 for now. When there is
|
||||
* more than one set there is an enclosing header of type 4.
|
||||
*/
|
||||
if (type == 4) {
|
||||
if (fw_size < 2 * sizeof(struct tlv_type_hdr))
|
||||
return -EINVAL;
|
||||
|
||||
tlv++;
|
||||
|
||||
type_len = le32_to_cpu(tlv->type_len);
|
||||
length = type_len >> 8;
|
||||
type = type_len & 0xff;
|
||||
}
|
||||
|
||||
BT_DBG("TLV Type\t\t : 0x%x", type);
|
||||
BT_DBG("Length\t\t : %d bytes", length);
|
||||
|
||||
if (type != 2)
|
||||
break;
|
||||
|
||||
if (fw_size < length + (tlv->data - fw_data))
|
||||
return -EINVAL;
|
||||
|
||||
idx = 0;
|
||||
data = tlv->data;
|
||||
while (idx < length) {
|
||||
while (idx < length - sizeof(struct tlv_type_nvm)) {
|
||||
tlv_nvm = (struct tlv_type_nvm *)(data + idx);
|
||||
|
||||
tag_id = le16_to_cpu(tlv_nvm->tag_id);
|
||||
tag_len = le16_to_cpu(tlv_nvm->tag_len);
|
||||
|
||||
if (length < idx + sizeof(struct tlv_type_nvm) + tag_len)
|
||||
return -EINVAL;
|
||||
|
||||
/* Update NVM tags as needed */
|
||||
switch (tag_id) {
|
||||
case EDL_TAG_ID_BD_ADDR:
|
||||
if (tag_len != sizeof(bdaddr_t))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&config->bdaddr, tlv_nvm->data, sizeof(bdaddr_t));
|
||||
|
||||
break;
|
||||
|
||||
case EDL_TAG_ID_HCI:
|
||||
if (tag_len < 3)
|
||||
return -EINVAL;
|
||||
|
||||
/* HCI transport layer parameters
|
||||
* enabling software inband sleep
|
||||
* onto controller side.
|
||||
@@ -366,6 +433,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
|
||||
break;
|
||||
|
||||
case EDL_TAG_ID_DEEP_SLEEP:
|
||||
if (tag_len < 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* Sleep enable mask
|
||||
* enabling deep sleep feature on controller.
|
||||
*/
|
||||
@@ -374,14 +444,16 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
|
||||
break;
|
||||
}
|
||||
|
||||
idx += (sizeof(u16) + sizeof(u16) + 8 + tag_len);
|
||||
idx += sizeof(struct tlv_type_nvm) + tag_len;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_ERR("Unknown TLV type %d", config->type);
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
|
||||
@@ -531,7 +603,9 @@ static int qca_download_firmware(struct hci_dev *hdev,
|
||||
memcpy(data, fw->data, size);
|
||||
release_firmware(fw);
|
||||
|
||||
qca_tlv_check_data(hdev, config, data, soc_type);
|
||||
ret = qca_tlv_check_data(hdev, config, data, size, soc_type);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
segment = data;
|
||||
remain = size;
|
||||
@@ -614,7 +688,7 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);
|
||||
|
||||
static int qca_check_bdaddr(struct hci_dev *hdev)
|
||||
static int qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *config)
|
||||
{
|
||||
struct hci_rp_read_bd_addr *bda;
|
||||
struct sk_buff *skb;
|
||||
@@ -638,7 +712,7 @@ static int qca_check_bdaddr(struct hci_dev *hdev)
|
||||
}
|
||||
|
||||
bda = (struct hci_rp_read_bd_addr *)skb->data;
|
||||
if (!bacmp(&bda->bdaddr, QCA_BDADDR_DEFAULT))
|
||||
if (!bacmp(&bda->bdaddr, &config->bdaddr))
|
||||
set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
|
||||
|
||||
kfree_skb(skb);
|
||||
@@ -667,7 +741,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
enum qca_btsoc_type soc_type, struct qca_btsoc_version ver,
|
||||
const char *firmware_name)
|
||||
{
|
||||
struct qca_fw_config config;
|
||||
struct qca_fw_config config = {};
|
||||
int err;
|
||||
u8 rom_ver = 0;
|
||||
u32 soc_ver;
|
||||
@@ -852,7 +926,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
break;
|
||||
}
|
||||
|
||||
err = qca_check_bdaddr(hdev);
|
||||
err = qca_check_bdaddr(hdev, &config);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#define EDL_PATCH_CONFIG_RES_EVT (0x00)
|
||||
#define QCA_DISABLE_LOGGING_SUB_OP (0x14)
|
||||
|
||||
#define EDL_TAG_ID_BD_ADDR 2
|
||||
#define EDL_TAG_ID_HCI (17)
|
||||
#define EDL_TAG_ID_DEEP_SLEEP (27)
|
||||
|
||||
@@ -47,7 +48,6 @@
|
||||
#define get_soc_ver(soc_id, rom_ver) \
|
||||
((le32_to_cpu(soc_id) << 16) | (le16_to_cpu(rom_ver)))
|
||||
|
||||
#define QCA_FW_BUILD_VER_LEN 255
|
||||
#define QCA_HSP_GF_SOC_ID 0x1200
|
||||
#define QCA_HSP_GF_SOC_MASK 0x0000ff00
|
||||
|
||||
@@ -94,6 +94,7 @@ struct qca_fw_config {
|
||||
uint8_t user_baud_rate;
|
||||
enum qca_tlv_dnld_mode dnld_mode;
|
||||
enum qca_tlv_dnld_mode dnld_type;
|
||||
bdaddr_t bdaddr;
|
||||
};
|
||||
|
||||
struct edl_event_hdr {
|
||||
|
||||
@@ -2768,8 +2768,6 @@ void hci_unregister_dev(struct hci_dev *hdev)
|
||||
|
||||
hci_unregister_suspend_notifier(hdev);
|
||||
|
||||
msft_unregister(hdev);
|
||||
|
||||
hci_dev_do_close(hdev);
|
||||
|
||||
if (!test_bit(HCI_INIT, &hdev->flags) &&
|
||||
@@ -2823,6 +2821,7 @@ void hci_release_dev(struct hci_dev *hdev)
|
||||
hci_discovery_filter_clear(hdev);
|
||||
hci_blocked_keys_clear(hdev);
|
||||
hci_codec_list_clear(&hdev->local_codecs);
|
||||
msft_release(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
ida_destroy(&hdev->unset_handle_ida);
|
||||
|
||||
@@ -7037,6 +7037,8 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
|
||||
u16 handle = le16_to_cpu(ev->bis[i]);
|
||||
|
||||
bis = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!bis)
|
||||
continue;
|
||||
|
||||
set_bit(HCI_CONN_BIG_SYNC_FAILED, &bis->flags);
|
||||
hci_connect_cfm(bis, ev->status);
|
||||
|
||||
@@ -415,6 +415,9 @@ static void l2cap_chan_timeout(struct work_struct *work)
|
||||
|
||||
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
|
||||
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
/* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling
|
||||
* this work. No need to call l2cap_chan_hold(chan) here again.
|
||||
@@ -3902,13 +3905,12 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
|
||||
struct l2cap_cmd_hdr *cmd,
|
||||
u8 *data, u8 rsp_code, u8 amp_id)
|
||||
static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
|
||||
u8 *data, u8 rsp_code, u8 amp_id)
|
||||
{
|
||||
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
|
||||
struct l2cap_conn_rsp rsp;
|
||||
struct l2cap_chan *chan = NULL, *pchan;
|
||||
struct l2cap_chan *chan = NULL, *pchan = NULL;
|
||||
int result, status = L2CAP_CS_NO_INFO;
|
||||
|
||||
u16 dcid = 0, scid = __le16_to_cpu(req->scid);
|
||||
@@ -3921,7 +3923,7 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
|
||||
&conn->hcon->dst, ACL_LINK);
|
||||
if (!pchan) {
|
||||
result = L2CAP_CR_BAD_PSM;
|
||||
goto sendresp;
|
||||
goto response;
|
||||
}
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
@@ -4008,17 +4010,15 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
|
||||
}
|
||||
|
||||
response:
|
||||
l2cap_chan_unlock(pchan);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
l2cap_chan_put(pchan);
|
||||
|
||||
sendresp:
|
||||
rsp.scid = cpu_to_le16(scid);
|
||||
rsp.dcid = cpu_to_le16(dcid);
|
||||
rsp.result = cpu_to_le16(result);
|
||||
rsp.status = cpu_to_le16(status);
|
||||
l2cap_send_cmd(conn, cmd->ident, rsp_code, sizeof(rsp), &rsp);
|
||||
|
||||
if (!pchan)
|
||||
return;
|
||||
|
||||
if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) {
|
||||
struct l2cap_info_req info;
|
||||
info.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
|
||||
@@ -4041,7 +4041,9 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
|
||||
chan->num_conf_req++;
|
||||
}
|
||||
|
||||
return chan;
|
||||
l2cap_chan_unlock(pchan);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
l2cap_chan_put(pchan);
|
||||
}
|
||||
|
||||
static int l2cap_connect_req(struct l2cap_conn *conn,
|
||||
|
||||
@@ -769,7 +769,7 @@ void msft_register(struct hci_dev *hdev)
|
||||
mutex_init(&msft->filter_lock);
|
||||
}
|
||||
|
||||
void msft_unregister(struct hci_dev *hdev)
|
||||
void msft_release(struct hci_dev *hdev)
|
||||
{
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
bool msft_monitor_supported(struct hci_dev *hdev);
|
||||
void msft_register(struct hci_dev *hdev);
|
||||
void msft_unregister(struct hci_dev *hdev);
|
||||
void msft_release(struct hci_dev *hdev);
|
||||
void msft_do_open(struct hci_dev *hdev);
|
||||
void msft_do_close(struct hci_dev *hdev);
|
||||
void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb);
|
||||
@@ -35,7 +35,7 @@ static inline bool msft_monitor_supported(struct hci_dev *hdev)
|
||||
}
|
||||
|
||||
static inline void msft_register(struct hci_dev *hdev) {}
|
||||
static inline void msft_unregister(struct hci_dev *hdev) {}
|
||||
static inline void msft_release(struct hci_dev *hdev) {}
|
||||
static inline void msft_do_open(struct hci_dev *hdev) {}
|
||||
static inline void msft_do_close(struct hci_dev *hdev) {}
|
||||
static inline void msft_vendor_evt(struct hci_dev *hdev, void *data,
|
||||
|
||||
@@ -83,6 +83,10 @@ static void sco_sock_timeout(struct work_struct *work)
|
||||
struct sock *sk;
|
||||
|
||||
sco_conn_lock(conn);
|
||||
if (!conn->hcon) {
|
||||
sco_conn_unlock(conn);
|
||||
return;
|
||||
}
|
||||
sk = conn->sk;
|
||||
if (sk)
|
||||
sock_hold(sk);
|
||||
|
||||
Reference in New Issue
Block a user