diff options
| author | Muhammad Bilal <meatuni001@gmail.com> | 2026-07-28 17:54:55 +0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-09-01 12:15:16 +0200 |
| commit | ff917923f4fb9c83717ba135ee47d7e4c1567bb7 (patch) | |
| tree | c38fd8fcf58e1937b17fdbc6518b6f8b797fc15a /drivers/bcma/driver_pcie2.c | |
| download | linux-stable-ff917923f4fb9c83717ba135ee47d7e4c1567bb7.tar.gz linux-stable-ff917923f4fb9c83717ba135ee47d7e4c1567bb7.zip | |
staging: rtl8723bs: fix OOB read in rtw_action_frame_parse()grafted
rtw_action_frame_parse() takes a frame_len parameter but never
actually checks it before indexing into the frame body:
const u8 *frame_body = frame + sizeof(struct ieee80211_hdr_3addr);
...
c = frame_body[0];
...
a = frame_body[1];
frame_body already points 24 bytes (sizeof(struct
ieee80211_hdr_3addr)) into frame, so reading frame_body[0] and
frame_body[1] requires frame_len >= 26. A management action frame
shorter than that (e.g. exactly 24 bytes, the minimum a malicious
peer can send) causes a 1-2 byte out-of-bounds read.
This is reachable from rtw_cfg80211_monitor_if_xmit_entry() and
cfg80211_rtw_mgmt_tx() in ioctl_cfg80211.c, both of which pass
attacker/user-influenced frame buffers and lengths straight through.
Add the missing length check before frame_body is dereferenced.
Fixes: 554c0a3abf216 ("staging: Add rtl8723bs sdio wifi driver")
Cc: stable@vger.kernel.org
Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
Link: https://patch.msgid.link/20260728125456.32359-3-meatuni001@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/bcma/driver_pcie2.c')
| -rw-r--r-- | drivers/bcma/driver_pcie2.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/drivers/bcma/driver_pcie2.c b/drivers/bcma/driver_pcie2.c new file mode 100644 index 000000000..cf889fc62 --- /dev/null +++ b/drivers/bcma/driver_pcie2.c @@ -0,0 +1,201 @@ +/* + * Broadcom specific AMBA + * PCIe Gen 2 Core + * + * Copyright 2014, Broadcom Corporation + * Copyright 2014, Rafał Miłecki <zajec5@gmail.com> + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include "bcma_private.h" +#include <linux/bcma/bcma.h> +#include <linux/pci.h> + +/************************************************** + * R/W ops. + **************************************************/ + +#if 0 +static u32 bcma_core_pcie2_cfg_read(struct bcma_drv_pcie2 *pcie2, u32 addr) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr); + pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR); + return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA); +} +#endif + +static void bcma_core_pcie2_cfg_write(struct bcma_drv_pcie2 *pcie2, u32 addr, + u32 val) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, val); +} + +/************************************************** + * Init. + **************************************************/ + +static u32 bcma_core_pcie2_war_delay_perst_enab(struct bcma_drv_pcie2 *pcie2, + bool enable) +{ + u32 val; + + /* restore back to default */ + val = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL); + val |= PCIE2_CLKC_DLYPERST; + val &= ~PCIE2_CLKC_DISSPROMLD; + if (enable) { + val &= ~PCIE2_CLKC_DLYPERST; + val |= PCIE2_CLKC_DISSPROMLD; + } + pcie2_write32(pcie2, (BCMA_CORE_PCIE2_CLK_CONTROL), val); + /* flush */ + return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL); +} + +static void bcma_core_pcie2_set_ltr_vals(struct bcma_drv_pcie2 *pcie2) +{ + /* LTR0 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x844); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x883c883c); + /* LTR1 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x848); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x88648864); + /* LTR2 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x84C); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x90039003); +} + +static void bcma_core_pcie2_hw_ltr_war(struct bcma_drv_pcie2 *pcie2) +{ + u8 core_rev = pcie2->core->id.rev; + u32 devstsctr2; + + if (core_rev < 2 || core_rev == 10 || core_rev > 13) + return; + + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_CAP_DEVSTSCTRL2_OFFSET); + devstsctr2 = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA); + if (devstsctr2 & PCIE2_CAP_DEVSTSCTRL2_LTRENAB) { + /* force the right LTR values */ + bcma_core_pcie2_set_ltr_vals(pcie2); + + /* TODO: + *si_core_wrapperreg(pcie2, 3, 0x60, 0x8080, 0); + */ + + /* enable the LTR */ + devstsctr2 |= PCIE2_CAP_DEVSTSCTRL2_LTRENAB; + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_CAP_DEVSTSCTRL2_OFFSET); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, devstsctr2); + + /* set the LTR state to be active */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE, + PCIE2_LTR_ACTIVE); + usleep_range(1000, 2000); + + /* set the LTR state to be sleep */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE, + PCIE2_LTR_SLEEP); + usleep_range(1000, 2000); + } +} + +static void pciedev_crwlpciegen2(struct bcma_drv_pcie2 *pcie2) +{ + u8 core_rev = pcie2->core->id.rev; + bool pciewar160, pciewar162; + + pciewar160 = core_rev == 7 || core_rev == 9 || core_rev == 11; + pciewar162 = core_rev == 5 || core_rev == 7 || core_rev == 8 || + core_rev == 9 || core_rev == 11; + + if (!pciewar160 && !pciewar162) + return; + +/* TODO */ +#if 0 + pcie2_set32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL, + PCIE_DISABLE_L1CLK_GATING); +#if 0 + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIEGEN2_COE_PVT_TL_CTRL_0); + pcie2_mask32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, + ~(1 << COE_PVT_TL_CTRL_0_PM_DIS_L1_REENTRY_BIT)); +#endif +#endif +} + +static void pciedev_crwlpciegen2_180(struct bcma_drv_pcie2 *pcie2) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_PMCR_REFUP); + pcie2_set32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x1f); +} + +static void pciedev_crwlpciegen2_182(struct bcma_drv_pcie2 *pcie2) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_SBMBX); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 1 << 0); +} + +static void pciedev_reg_pm_clk_period(struct bcma_drv_pcie2 *pcie2) +{ + struct bcma_drv_cc *drv_cc = &pcie2->core->bus->drv_cc; + u8 core_rev = pcie2->core->id.rev; + u32 alp_khz, pm_value; + + if (core_rev <= 13) { + alp_khz = bcma_pmu_get_alp_clock(drv_cc) / 1000; + pm_value = (1000000 * 2) / alp_khz; + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_PVT_REG_PM_CLK_PERIOD); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, pm_value); + } +} + +void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2) +{ + struct bcma_bus *bus = pcie2->core->bus; + struct bcma_chipinfo *ci = &bus->chipinfo; + u32 tmp; + + tmp = pcie2_read32(pcie2, BCMA_CORE_PCIE2_SPROM(54)); + if ((tmp & 0xe) >> 1 == 2) + bcma_core_pcie2_cfg_write(pcie2, 0x4e0, 0x17); + + switch (bus->chipinfo.id) { + case BCMA_CHIP_ID_BCM4360: + case BCMA_CHIP_ID_BCM4352: + pcie2->reqsize = 1024; + break; + default: + pcie2->reqsize = 128; + break; + } + + if (ci->id == BCMA_CHIP_ID_BCM4360 && ci->rev > 3) + bcma_core_pcie2_war_delay_perst_enab(pcie2, true); + bcma_core_pcie2_hw_ltr_war(pcie2); + pciedev_crwlpciegen2(pcie2); + pciedev_reg_pm_clk_period(pcie2); + pciedev_crwlpciegen2_180(pcie2); + pciedev_crwlpciegen2_182(pcie2); +} + +/************************************************** + * Runtime ops. + **************************************************/ + +void bcma_core_pcie2_up(struct bcma_drv_pcie2 *pcie2) +{ + struct bcma_bus *bus = pcie2->core->bus; + struct pci_dev *dev = bus->host_pci; + int err; + + err = pcie_set_readrq(dev, pcie2->reqsize); + if (err) + bcma_err(bus, "Error setting PCI_EXP_DEVCTL_READRQ: %d\n", err); +} |
