6.18.39-rc1 review
Greg Kroah-Hartman
maintainerPATCH 136/480 PCI: altera: Do not dispose parent IRQ mapping
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mahesh Vaidya <[email protected]>
commit 5ef4bac02189bee0b7c170e352d7a38e13fe9678 upstream.
altera_pcie_irq_teardown() calls irq_dispose_mapping() on pcie->irq. However, pcie->irq is the parent IRQ returned by platform_get_irq(), not the mapping created by Altera INTx irq_domain.
The Altera driver only sets the chained handler on the parent IRQ. It should detach that handler during teardown, but it should not dispose the parent IRQ mapping, which belongs to the parent interrupt controller's irq_domain.
Drop irq_dispose_mapping(pcie->irq) from the teardown path.
Note that during irqchip remove(), the child IRQs should've disposed. But since the chained handler itself is removed, there is no way the stale child IRQs (if exists) could fire. So it is safe here.
--- drivers/pci/controller/pcie-altera.c | 1 - 1 file changed, 1 deletion(-)
drivers/pci/controller/pcie-altera.c +0 -1
@@ -868,7 +868,6 @@ static void altera_pcie_irq_teardown(str {irq_set_chained_handler_and_data (pcie->irq,NULL ,NULL );irq_domain_remove (pcie->irq_domain); -irq_dispose_mapping (pcie->irq); }static int altera_pcie_parse_dt (struct altera_pcie *pcie)
Greg Kroah-Hartman
maintainerPATCH 137/480 PCI: altera: Fix resource leaks on probe failure
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mahesh Vaidya <[email protected]>
commit 7a94138caeb27f3c49c1dbd93bf422098925bb28 upstream.
The chained IRQ handler is set during probe, but is only removed during the driver remove(). If pci_host_probe() fails, the handler and INTx IRQ domain remain set even though the devm-managed host bridge storage containing struct altera_pcie will be released, leaving the handler with a stale data pointer.
Interrupts are also enabled before pci_host_probe() is called. If probe fails after that point, the controller interrupt source should be disabled before the chained handler and INTx domain are removed.
So set the chained handler only after the INTx domain has been created. Disable controller interrupts during IRQ teardown, and tear the IRQ setup down if pci_host_probe() fails.
--- drivers/pci/controller/pcie-altera.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-)
drivers/pci/controller/pcie-altera.c +33 -2
@@ -864,8 +864,23 @@ static int altera_pcie_init_irq_domain(sreturn 0 ; } +static void altera_pcie_disable_irq (struct altera_pcie *pcie) +{ +if (pcie->pcie_data->version ==ALTERA_PCIE_V1 || + pcie->pcie_data->version ==ALTERA_PCIE_V2 ) { +/* Disable all P2A interrupts */ +cra_writel (pcie,0 ,P2A_INT_ENABLE ); + }else if (pcie->pcie_data->version ==ALTERA_PCIE_V3 ) { +/* Disable port-level interrupts (CFG_AER, etc.) */ +writel (0 , pcie->hip_base + + pcie->pcie_data->port_conf_offset + + pcie->pcie_data->port_irq_enable_offset); + } +} +static void altera_pcie_irq_teardown (struct altera_pcie *pcie) { +altera_pcie_disable_irq (pcie);irq_set_chained_handler_and_data (pcie->irq,NULL ,NULL );irq_domain_remove (pcie->irq_domain); }
@@ -890,7 +905,6 @@ static int altera_pcie_parse_dt(struct aif (pcie->irq <0 )return pcie->irq; -irq_set_chained_handler_and_data (pcie->irq, pcie->pcie_data->ops->rp_isr, pcie);return 0 ; }
@@ -1019,6 +1033,14 @@ static int altera_pcie_probe(struct platreturn ret; } +/* +* The chained handler uses pcie->irq_domain, so set it only after the +* INTx domain has been created. +*/ +irq_set_chained_handler_and_data (pcie->irq, + pcie->pcie_data->ops->rp_isr, + pcie); +if (pcie->pcie_data->version ==ALTERA_PCIE_V1 || pcie->pcie_data->version ==ALTERA_PCIE_V2 ) {/* clear all interrupts */
@@ -1036,7 +1058,16 @@ static int altera_pcie_probe(struct plat bridge->busnr = pcie->root_bus_nr; bridge->ops = &altera_pcie_ops; -return pci_host_probe (bridge); + ret =pci_host_probe (bridge); +if (ret) + goto err_teardown_irq; + +return 0 ; + +err_teardown_irq: +altera_pcie_irq_teardown (pcie); + +return ret; }static void altera_pcie_remove (struct platform_device *pdev)
Greg Kroah-Hartman
maintainerPATCH 138/480 PCI: Always lift 2.5GT/s restriction in PCIe failed link retraining
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maciej W. Rozycki <[email protected]>
commit 72780f7964684939d7d2f69c348876213b184484 upstream.
Discard Vendor:Device ID matching in the PCIe failed link retraining quirk and ignore the link status for the removal of the 2.5GT/s speed clamp, whether applied by the quirk itself or the firmware earlier on. Revert to the original target link speed if this final link retraining has failed.
This is so that link training noise in hot-plug scenarios does not make a link remain clamped to the 2.5GT/s speed where an event race has led the quirk to apply the speed clamp for one device, only to leave it in place for a subsequent device to be plugged in.
Refer to the Link Capabilities register directly for the maximum link speed determination so as to streamline backporting.
--- drivers/pci/quirks.c | 51 ++++++++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 33 deletions(-)
drivers/pci/quirks.c +18 -33
@@ -79,11 +79,10 @@ static bool pcie_lbms_seen(struct pci_de* Restrict the speed to 2.5GT/s then with the Target Link Speed field, * request a retrain and check the result. * -* If this turns out successful and we know by the Vendor:Device ID it is -* safe to do so, then lift the restriction, letting the devices negotiate -* a higher speed. Also check for a similar 2.5GT/s speed restriction the -* firmware may have already arranged and lift it with ports that already -* report their data link being up. +* If this turns out successful, or where a 2.5GT/s speed restriction has +* been previously arranged by the firmware and the port reports its link +* already being up, lift the restriction, in a hope it is safe to do so, +* letting the devices negotiate a higher speed. * * Otherwise revert the speed to the original setting and request a retrain * again to remove any residual state, ignoring the result as it's supposed
@@ -94,52 +93,38 @@ static bool pcie_lbms_seen(struct pci_de*/ int pcie_failed_link_retrain (struct pci_dev *dev) { -static const struct pci_device_id ids[] = { - {PCI_VDEVICE (ASMEDIA ,0x2824 ) },/* ASMedia ASM2824 */ - {} - }; -u16 lnksta, lnkctl2; +u16 lnksta, lnkctl2, oldlnkctl2;int ret = -ENOTTY ; +u32 lnkcap;if (!pci_is_pcie (dev) || !pcie_downstream_port (dev) || !pcie_cap_has_lnkctl2 (dev) || !dev->link_active_reporting)return ret;pcie_capability_read_word (dev,PCI_EXP_LNKSTA , &lnksta); +pcie_capability_read_word (dev,PCI_EXP_LNKCTL2 , &oldlnkctl2);if (!(lnksta &PCI_EXP_LNKSTA_DLLLA ) &&pcie_lbms_seen (dev, lnksta)) { -u16 oldlnkctl2; -pci_info (dev,"broken device, retraining non-functional downstream link at 2.5GT/s\n" ); - -pcie_capability_read_word (dev,PCI_EXP_LNKCTL2 , &oldlnkctl2); ret =pcie_set_target_speed (dev,PCIE_SPEED_2_5GT , false); -if (ret) { -pci_info (dev,"retraining failed\n" ); -pcie_set_target_speed (dev,PCIE_LNKCTL2_TLS2SPEED (oldlnkctl2), - true); -return ret; - } - -pcie_capability_read_word (dev,PCI_EXP_LNKSTA , &lnksta); +if (ret) + goto err; }pcie_capability_read_word (dev,PCI_EXP_LNKCTL2 , &lnkctl2); - -if ((lnksta &PCI_EXP_LNKSTA_DLLLA ) && - (lnkctl2 &PCI_EXP_LNKCTL2_TLS ) ==PCI_EXP_LNKCTL2_TLS_2_5GT && -pci_match_id (ids, dev)) { -u32 lnkcap; - +pcie_capability_read_dword (dev,PCI_EXP_LNKCAP , &lnkcap); +if ((lnkctl2 &PCI_EXP_LNKCTL2_TLS ) ==PCI_EXP_LNKCTL2_TLS_2_5GT && + (lnkcap &PCI_EXP_LNKCAP_SLS ) !=PCI_EXP_LNKCAP_SLS_2_5GB ) {pci_info (dev,"removing 2.5GT/s downstream link speed restriction\n" ); -pcie_capability_read_dword (dev,PCI_EXP_LNKCAP , &lnkcap); ret =pcie_set_target_speed (dev,PCIE_LNKCAP_SLS2SPEED (lnkcap), false); -if (ret) { -pci_info (dev,"retraining failed\n" ); -return ret; - } +if (ret) + goto err; }return ret; +err: +pci_info (dev,"retraining failed\n" ); +pcie_set_target_speed (dev,PCIE_LNKCTL2_TLS2SPEED (oldlnkctl2), true); +return ret; } static ktime_t fixup_debug_start(struct pci_dev *dev,
Greg Kroah-Hartman
maintainerPATCH 139/480 PCI: host-common: Request bus reassignment when not probe-only
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ratheesh Kannoth <[email protected]>
commit fda8749ba73638f5bbca3ffb39bc6861eb3b23fa upstream.
pci_host_common_init() is used by several generic ECAM host drivers. After PCI core changes around pci_flags and preserve_config, these hosts no longer opted into full bus number reassignment the way they did before, which broke enumeration of devices on a Marvell CN106XX board.
When PCI_PROBE_ONLY is not set, add PCI_REASSIGN_ALL_BUS so pci_scan_bridge_extend() takes the reassignment path: bus numbers can be assigned from firmware EA data (e.g. pci_ea_fixed_busnrs()). Skip the flag in probe-only mode so existing assignments are not overridden.
--- drivers/pci/controller/pci-host-common.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/pci/controller/pci-host-common.c +4 -0
@@ -72,6 +72,10 @@ int pci_host_common_init(struct platformif (IS_ERR (cfg))return PTR_ERR (cfg); +/* Do not reassign bus numbers if probe only */ +if (!pci_has_flag (PCI_PROBE_ONLY )) +pci_add_flags (PCI_REASSIGN_ALL_BUS ); + bridge->sysdata = cfg; bridge->ops = (struct pci_ops *)&ops->pci_ops; bridge->enable_device = ops->enable_device;
Greg Kroah-Hartman
maintainerPATCH 140/480 PCI: imx6: Fix IMX6SX_GPR12_PCIE_TEST_POWERDOWN handling
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Richard Zhu <[email protected]>
commit aad953fb4eed0df5486cd54ccad80ac197678e01 upstream.
The IMX6SX_GPR12_PCIE_TEST_POWERDOWN bit does not control the PCIe reference clock on i.MX6SX. Instead, it is part of i.MX6SX PCIe core reset sequence.
Move the IMX6SX_GPR12_PCIE_TEST_POWERDOWN assertion/deassertion into the core reset functions to properly reflect its purpose. Remove the .enable_ref_clk() callback for i.MX6SX since it was incorrectly manipulating this bit.
--- drivers/pci/controller/dwc/pci-imx6.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-)
drivers/pci/controller/dwc/pci-imx6.c +3 -9
@@ -659,14 +659,6 @@ static int imx_pcie_attach_pd(struct devreturn 0 ; } -static int imx6sx_pcie_enable_ref_clk (struct imx_pcie *imx_pcie,bool enable) -{ -regmap_update_bits (imx_pcie->iomuxc_gpr,IOMUXC_GPR12 , -IMX6SX_GPR12_PCIE_TEST_POWERDOWN , - enable ?0 :IMX6SX_GPR12_PCIE_TEST_POWERDOWN ); -return 0 ; -} -static int imx6q_pcie_enable_ref_clk (struct imx_pcie *imx_pcie,bool enable) {if (enable) {
@@ -765,6 +757,9 @@ static int imx6sx_pcie_core_reset(structif (assert)regmap_set_bits (imx_pcie->iomuxc_gpr,IOMUXC_GPR12 ,IMX6SX_GPR12_PCIE_TEST_POWERDOWN ); +else +regmap_clear_bits (imx_pcie->iomuxc_gpr,IOMUXC_GPR12 , +IMX6SX_GPR12_PCIE_TEST_POWERDOWN );/* Force PCIe PHY reset */ regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR5, IMX6SX_GPR5_PCIE_BTNRST_RESET,
@@ -1852,7 +1847,6 @@ static const struct imx_pcie_drvdata drv .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .init_phy = imx6sx_pcie_init_phy, - .enable_ref_clk = imx6sx_pcie_enable_ref_clk, .core_reset = imx6sx_pcie_core_reset, .ops = &imx_pcie_host_ops, },
Greg Kroah-Hartman
maintainerPATCH 141/480 PCI: mediatek: Fix IRQ domain leak when port fails to enable
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Manivannan Sadhasivam <[email protected]>
commit f865a57896bd92d7662eb2818d8f48872e2cbbc7 upstream.
When mtk_pcie_enable_port() fails, mtk_pcie_port_free() removes the port from pcie->ports and frees the port structure. However, the IRQ domains set up earlier by mtk_pcie_init_irq_domain() are never freed.
Fix this by refactoring mtk_pcie_irq_teardown() into a per-port helper, mtk_pcie_irq_teardown_port(), and calling it from mtk_pcie_setup() when mtk_pcie_enable_port() fails. Since the IRQ teardown must only happen in the probe error path (during resume, child devices may have active MSI mappings and the NOIRQ context prohibits sleeping locks), mtk_pcie_enable_port() is changed to return an error code so callers can distinguish the two paths and act accordingly.
This issue was reported by Sashiko while reviewing the EcoNet EN7528 SoC support series.
--- drivers/pci/controller/pcie-mediatek.c | 63 ++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 23 deletions(-)
drivers/pci/controller/pcie-mediatek.c +40 -23
@@ -519,23 +519,27 @@ static void mtk_pcie_enable_msi(struct mwritel (val, port->base +PCIE_INT_MASK ); } -static void mtk_pcie_irq_teardown (struct mtk_pcie *pcie) +static void mtk_pcie_irq_teardown_port (struct mtk_pcie_port *port) { -struct mtk_pcie_port *port, *tmp; +irq_set_chained_handler_and_data (port->irq,NULL ,NULL ); -list_for_each_entry_safe (port, tmp, &pcie->ports, list) { -irq_set_chained_handler_and_data (port->irq,NULL ,NULL ); +if (port->irq_domain) +irq_domain_remove (port->irq_domain); -if (port->irq_domain) -irq_domain_remove (port->irq_domain); +if (IS_ENABLED (CONFIG_PCI_MSI )) { +if (port->inner_domain) +irq_domain_remove (port->inner_domain); + } -if (IS_ENABLED (CONFIG_PCI_MSI )) { -if (port->inner_domain) -irq_domain_remove (port->inner_domain); - } +irq_dispose_mapping (port->irq); +} -irq_dispose_mapping (port->irq); - } +static void mtk_pcie_irq_teardown (struct mtk_pcie *pcie) +{ +struct mtk_pcie_port *port, *tmp; + +list_for_each_entry_safe (port, tmp, &pcie->ports, list) +mtk_pcie_irq_teardown_port (port); } static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
@@ -823,7 +827,7 @@ static int mtk_pcie_startup_port(structreturn 0 ; } -static void mtk_pcie_enable_port (struct mtk_pcie_port *port) +static int mtk_pcie_enable_port (struct mtk_pcie_port *port) {struct mtk_pcie *pcie = port->pcie;struct device *dev = pcie->dev;
@@ -832,7 +836,7 @@ static void mtk_pcie_enable_port(struct err =clk_prepare_enable (port->sys_ck);if (err) {dev_err (dev,"failed to enable sys_ck%d clock\n" , port->slot); - goto err_sys_clk; +return err; } err =clk_prepare_enable (port->ahb_ck);
@@ -880,11 +884,15 @@ static void mtk_pcie_enable_port(struct goto err_phy_on; } -if (!pcie->soc->startup (port)) -return ; + err = pcie->soc->startup (port); +if (err) { +dev_info (dev,"Port%d link down\n" , port->slot); + goto err_soc_startup; + } -dev_info (dev,"Port%d link down\n" , port->slot); +return 0 ; +err_soc_startup:phy_power_off (port->phy); err_phy_on:phy_exit (port->phy);
@@ -900,8 +908,8 @@ err_aux_clk:clk_disable_unprepare (port->ahb_ck); err_ahb_clk:clk_disable_unprepare (port->sys_ck); -err_sys_clk: -mtk_pcie_port_free (port); + +return err; } static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
@@ -1067,8 +1075,13 @@ static int mtk_pcie_setup(struct mtk_pcireturn err;/* enable each port, and then check link status */ -list_for_each_entry_safe (port, tmp, &pcie->ports, list) -mtk_pcie_enable_port (port); +list_for_each_entry_safe (port, tmp, &pcie->ports, list) { + err =mtk_pcie_enable_port (port); +if (err) { +mtk_pcie_irq_teardown_port (port); +mtk_pcie_port_free (port); + } + }/* power down PCIe subsys if slots are all empty (link down) */ if (list_empty (&pcie->ports))
@@ -1167,14 +1180,18 @@ static int mtk_pcie_resume_noirq(struct {struct mtk_pcie *pcie =dev_get_drvdata (dev);struct mtk_pcie_port *port, *tmp; +int err;if (list_empty (&pcie->ports))return 0 ;clk_prepare_enable (pcie->free_ck); -list_for_each_entry_safe (port, tmp, &pcie->ports, list) -mtk_pcie_enable_port (port); +list_for_each_entry_safe (port, tmp, &pcie->ports, list) { + err =mtk_pcie_enable_port (port); +if (err) +mtk_pcie_port_free (port); + }/* In case of EP was removed while system suspend. */ if (list_empty (&pcie->ports))
Greg Kroah-Hartman
maintainerPATCH 142/480 PCI: qcom: Initialize DWC MSI lock for firmware-managed ECAM hosts
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yadu M G <[email protected]>
commit e0779713a1e2f891aeec53e629dbbd33f423c629 upstream.
A lockdep warning is observed during boot on a Qcom firmware-managed platform:
INFO: trying to register non-static key.
The code is fine but needs lockdep annotation, or maybe you didn't initialize this object before use? turning off the locking correctness validator.
...
Call trace: register_lock_class+0x128/0x4d8 __lock_acquire+0x110/0x1db0 lock_acquire+0x278/0x3d8 _raw_spin_lock_irq+0x6c/0xc0 dw_pcie_irq_domain_alloc+0x48/0x190 irq_domain_alloc_irqs_parent+0x2c/0x48 msi_domain_alloc+0x90/0x160 ...
dw_pcie_irq_domain_alloc() takes pp->lock while allocating MSI interrupts. pp->lock is normally initialized by dw_pcie_host_init(), but Qcom firmware-managed hosts use the ECAM init path instead:
pci_host_common_ecam_create()
pci_ecam_create()
qcom_pcie_ecam_host_init()
dw_pcie_msi_host_init()
dw_pcie_allocate_domains()That path constructs a fresh struct dw_pcie_rp and calls dw_pcie_msi_host_init() directly, without going through dw_pcie_host_init(). As a result, pp->lock was not initialized, which triggers the warning.
Initialize pp->lock in qcom_pcie_ecam_host_init() before registering the MSI domains so the firmware-managed ECAM path matches the normal DWC host initialization sequence.
--- drivers/pci/controller/dwc/pcie-qcom.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/pci/controller/dwc/pcie-qcom.c +6 -0
@@ -1626,6 +1626,12 @@ static int qcom_pcie_ecam_host_init(stru pci->dbi_base = cfg->win; pp->num_vectors =MSI_DEF_NUM_VECTORS ; +/* +* dw_pcie_msi_host_init() is called directly here, bypassing +* dw_pcie_host_init() where pp->lock is normally initialized. +*/ +raw_spin_lock_init (&pp->lock); + ret =dw_pcie_msi_host_init (pp);if (ret)return ret;
Greg Kroah-Hartman
maintainerPATCH 143/480 PCI/IOV: Skip VF Resizable BAR restore on read error
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marco Nenciarini <[email protected]>
commit f34f1712229d71ce4286440fef12526fd4590b37 upstream.
sriov_restore_vf_rebar_state() uses the VF Resizable BAR Control register to decide how many VF BARs to restore (nbars) and which VF BAR each iteration addresses (bar_idx). bar_idx indexes into dev->sriov->barsz[], which has only PCI_SRIOV_NUM_BARS (6) entries.
When a device does not respond, config reads typically return PCI_ERROR_RESPONSE (~0). Both fields are 3 bits wide, so nbars and bar_idx both evaluate to 7. The barsz[] access then goes out of bounds. UBSAN reports this as:
UBSAN: array-index-out-of-bounds in drivers/pci/iov.c:948:51 index 7 is out of range for type 'resource_size_t [6]'
Observed on an NVIDIA RTX PRO 1000 GPU (GB207GLM) that stopped responding during a failed GC6 power state exit. The subsequent pci_restore_state() invoked sriov_restore_vf_rebar_state() while config reads returned 0xffffffff, triggering the splat.
Bail out if any VF Resizable BAR Control read returns PCI_ERROR_RESPONSE. No further VF BARs are touched, which is safe because a config read that returns PCI_ERROR_RESPONSE indicates the device is unreachable and restoration is pointless. This mirrors the guard in pci_restore_rebar_state().
--- drivers/pci/iov.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/pci/iov.c +6 -0
@@ -938,12 +938,18 @@ static void sriov_restore_vf_rebar_statereturn ;pci_read_config_dword (dev, pos +PCI_VF_REBAR_CTRL , &ctrl); +if (PCI_POSSIBLE_ERROR (ctrl)) +return ; + nbars =FIELD_GET (PCI_VF_REBAR_CTRL_NBAR_MASK , ctrl);for (i =0 ; i < nbars; i++, pos +=8 ) {int bar_idx, size;pci_read_config_dword (dev, pos +PCI_VF_REBAR_CTRL , &ctrl); +if (PCI_POSSIBLE_ERROR (ctrl)) +return ; + bar_idx =FIELD_GET (PCI_VF_REBAR_CTRL_BAR_IDX , ctrl); size =pci_rebar_bytes_to_size (dev->sriov->barsz[bar_idx]); ctrl &= ~PCI_VF_REBAR_CTRL_BAR_SIZE ;
Greg Kroah-Hartman
maintainerPATCH 144/480 tcp: restore RCU grace period in tcp_ao_destroy_sock
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 8bc4d43bccbd60efe85d0a44d5bf41762f2f0c30 upstream.
Commit 51e547e8c89c ("tcp: Free TCP-AO/TCP-MD5 info/keys without RCU") removed the call_rcu() callback from tcp_ao_destroy_sock(), arguing that "the destruction of info/keys is delayed until the socket destructor" and therefore "no one can discover it anymore".
That argument does not hold for the call site in tcp_connect() (net/ipv4/tcp_output.c:4327-4332). At that point the socket is in TCP_SYN_SENT, has already been inserted into the inet ehash by inet_hash_connect() in tcp_v4_connect(), and is therefore very much
discoverable: any softirq running tcp_v4_rcv() on another CPU can take the socket out of the ehash, walk into tcp_inbound_hash(), and load tp->ao_info via implicit RCU before bh_lock_sock_nested() is taken on the destroying CPU.
The reader path then enters __tcp_ao_do_lookup() (net/ipv4/tcp_ao.c:208) which re-loads tp->ao_info via rcu_dereference_check(); the re-load can still observe the (about-to-be-freed) pointer because there is no synchronize_rcu() between rcu_assign_pointer(tp->ao_info, NULL) and tcp_ao_info_free() in tcp_ao_destroy_sock(). The captured pointer is then walked at line 223:
hlist_for_each_entry_rcu(key, &ao->head, node, ...)
The writer's synchronous kfree() is free to complete between the line 218 re-fetch and the line 223 hlist iteration. The slab is reused (or simply LIST_POISON1-stamped if not yet reused) and the iteration walks attacker-controlled or poison memory in softirq context.
Reproducer (no debug shim, stock x86_64 v7.1-rc2 SMP+KASAN, QEMU+KVM):
an unprivileged uid=1000 process inside CLONE_NEWUSER|CLONE_NEWNET installs TCP_MD5SIG + TCP_AO_ADD_KEY on a TCP socket, sprays forged TCP-AO segments toward its eventual 4-tuple via raw sockets, then calls connect(). The md5-wins reconciliation in tcp_connect() fires tcp_ao_destroy_sock(); the softirq backlog reader on the loopback NAPI path crashes on the freed ao->head.first walk:
Oops: general protection fault, probably for non-canonical
address 0xfbd59c000000002f
KASAN: maybe wild-memory-access in range
[0xdead000000000178-0xdead00000000017f]
CPU: 0 UID: 1000 PID: 100 Comm: repro_userns
RIP: 0010:__tcp_ao_do_lookup+0x107/0x1c0
Call Trace: <IRQ>
__tcp_ao_do_lookup+0x107/0x1c0
tcp_ao_inbound_lookup.constprop.0+0x12a/0x200
tcp_inbound_ao_hash+0x5ea/0x1520
tcp_inbound_hash+0x7ce/0x1240
tcp_v4_rcv+0x1e7a/0x3e10
...Restore the RCU grace period: re-add struct rcu_head to tcp_ao_info and replace the synchronous tcp_ao_info_free() with a call_rcu() callback. Readers that captured tp->ao_info before rcu_assign_pointer NULLed it now see the object remain valid until rcu_read_unlock(). With the patch applied the reproducer runs cleanly for 2000 iterations on the same kernel build.
--- include/net/tcp_ao.h | 1 + net/ipv4/tcp_ao.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-)
include/net/tcp_ao.h +1 -0
@@ -130,6 +130,7 @@ struct tcp_ao_info {u32 snd_sne;u32 rcv_sne;refcount_t refcnt;/* Protects twsk destruction */ +struct rcu_head rcu; };#ifdef CONFIG_TCP_MD5SIG
net/ipv4/tcp_ao.c +3 -2
@@ -270,8 +270,9 @@ static void tcp_ao_key_free_rcu(struct rkfree_sensitive (key); } -static void tcp_ao_info_free (struct tcp_ao_info *ao) +static void tcp_ao_info_free_rcu (struct rcu_head *head) { + struct tcp_ao_info *ao = container_of(head, struct tcp_ao_info, rcu);struct tcp_ao_key *key;struct hlist_node *n;
@@ -311,7 +312,7 @@ void tcp_ao_destroy_sock(struct sock *skif (!twsk)tcp_ao_sk_omem_free (sk, ao); -tcp_ao_info_free (ao); +call_rcu (&ao->rcu, tcp_ao_info_free_rcu); }void tcp_ao_time_wait (struct tcp_timewait_sock *tcptw,struct tcp_sock *tp)
Greg Kroah-Hartman
maintainerPATCH 145/480 mm/damon/ops-common: handle extreme intervals in damon_hot_score()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: SeongJae Park <[email protected]>
commit 35d4a3cf70a855b50e53189ac2f8463e20a02046 upstream.
Fix three issues in damon_hot_score() that comes from wrong handling of extreme (zero or too high) monitoring intervals user setup.
When the user sets sampling interval zero, damon_max_nr_accesses(), which is called from damon_hot_score(), causes a divide-by-zero. Needless to say, it is a problem.
When the user sets the aggregation interval zero, the function returns zero. It is wrong, since the real maximum nr_acceses in the setup should be one. Worse yet, it can cause another divide-by-zero from its caller, damon_hot_score(), since it uses damon_max_nr_accesses() return value as a denominator.
When the user sets the aggregation interval very high, damon_hot_score() could return a value out of [0, DAMOS_MAX_SCORE] range. Since the return value is used as an index to the regions_score_histogram array, which is DAMOS_MAX_SCORE+1 size, it causes out of bounds array access.
The issues can be relatively easily reproduced like below. The sysfs write permission is required, though.
# ./damo start --damos_action lru_prio --damos_quota_space 100M \
--damos_quota_interval 1s
# cd /sys/kernel/mm/damon/admin/kdamonds/0
# echo 0 > contexts/0/monitoring_attrs/intervals/sample_us
# echo 0 > contexts/0/monitoring_attrs/intervals/aggr_us
# echo commit > state
# dmesg
[...]
[ 131.329762] Oops: divide error: 0000 [#1] SMP NOPTI
[...]
[ 131.336089] RIP: 0010:damon_hot_score+0x27/0xd0
[...]Fix the divide-by-zero intervals problems by explicitly handling the zero intervals in damon_max_nr_accesses(). Fix the out-of-bound array access by applying [0, DAMOS_MAX_SCORE] bounds before returning from damon_hot_score().
The issue was discovered [1] by Sashiko.
--- include/linux/damon.h | 8 ++++++-- mm/damon/ops-common.c | 1 + 2 files changed, 7 insertions(+), 2 deletions(-)
include/linux/damon.h +6 -2
@@ -939,9 +939,13 @@ static inline bool damon_target_has_pid(static inline unsigned int damon_max_nr_accesses (const struct damon_attrs *attrs) { -/* {aggr,sample}_interval are unsigned long, hence could overflow */ -return min (attrs->aggr_interval / attrs->sample_interval, +unsigned long sample_interval; +unsigned long max_nr_accesses; + + sample_interval = attrs->sample_interval ? :1 ; + max_nr_accesses =min (attrs->aggr_interval / sample_interval, (unsigned long )UINT_MAX ); +return max_nr_accesses ? :1 ; }
mm/damon/ops-common.c +1 -0
@@ -128,6 +128,7 @@ int damon_hot_score(struct damon_ctx *c,* Transform it to fit in [0, DAMOS_MAX_SCORE] */ hotness = hotness *DAMOS_MAX_SCORE /DAMON_MAX_SUBSCORE ; + hotness =max (min (hotness,DAMOS_MAX_SCORE ),0 );return hotness; }
Greg Kroah-Hartman
maintainerPATCH 146/480 netfilter: ipset: fix race between dump and ip_set_list resize
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
commit 7cd9103283b26b917360ec99d7d2f2d761bcf1ab upstream.
The release path of ip_set_dump_do() and ip_set_dump_done() read inst->ip_set_list via ip_set_ref_netlink(), a plain rcu_dereference_raw() of the array pointer. These run from netlink_recvmsg() without the nfnl mutex and without an RCU read-side critical section.
A concurrent ip_set_create() can grow the array: it publishes the new array, calls synchronize_net() and then kvfree()s the old one. Since the dump paths read the array outside any RCU reader, synchronize_net() does not wait for them and the old array can be freed while they still index into it, causing a use-after-free.
The dumped set itself stays pinned via set->ref_netlink, so only the array load needs protecting. Take rcu_read_lock() around it, matching ip_set_get_byname() and __ip_set_put_byindex().
BUG: KASAN: slab-use-after-free in ip_set_dump_do (net/netfilter/ipset/ip_set_core.c:1697) Read of size 8 at addr ffff88800b5c4018 by task exploit/150 Call Trace: ... kasan_report (mm/kasan/report.c:595) ip_set_dump_do (net/netfilter/ipset/ip_set_core.c:1697) netlink_dump (net/netlink/af_netlink.c:2325) netlink_recvmsg (net/netlink/af_netlink.c:1976) sock_recvmsg (net/socket.c:1159) __sys_recvfrom (net/socket.c:2315) ... Oops: general protection fault, probably for non-canonical address ... KASAN NOPTI KASAN: maybe wild-memory-access in range [0x02d6...d0-0x02d6...d7] RIP: 0010:ip_set_dump_do (net/netfilter/ipset/ip_set_core.c:1698) Kernel panic - not syncing: Fatal exception
--- net/netfilter/ipset/ip_set_core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
net/netfilter/ipset/ip_set_core.c +7 -1
@@ -1480,7 +1480,11 @@ ip_set_dump_done(struct netlink_callbackstruct ip_set_net *inst = (struct ip_set_net *)cb->args[IPSET_CB_NET ];ip_set_id_t index = (ip_set_id_t )cb->args[IPSET_CB_INDEX ]; -struct ip_set *set =ip_set_ref_netlink (inst, index); +struct ip_set *set; + +rcu_read_lock (); + set =ip_set_ref_netlink (inst, index); +rcu_read_unlock ();if (set->variant->uref) set->variant->uref (set, cb, false);
@@ -1685,7 +1689,9 @@ next_set: release_refcount:/* If there was an error or set is done, release set */ if (ret || !cb->args[IPSET_CB_ARG0 ]) { +rcu_read_lock (); set =ip_set_ref_netlink (inst, index); +rcu_read_unlock ();if (set->variant->uref) set->variant->uref (set, cb, false);pr_debug ("release set %s\n" , set->name);
Greg Kroah-Hartman
maintainerPATCH 147/480 virtio_pci: fix vq info pointer lookup via wrong index
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ammar Faizi <[email protected]>
commit f7d380fb525c13bdd114369a1979c80c346e6abc upstream.
Unbinding a virtio balloon device:
echo virtio0 > /sys/bus/virtio/drivers/virtio_balloon/unbind
triggers a NULL pointer dereference. The dmesg says:
BUG: kernel NULL pointer dereference, address: 0000000000000008
[...]
RIP: 0010:__list_del_entry_valid_or_report+0x5/0xf0
Call Trace:
<TASK>
vp_del_vqs+0x121/0x230
remove_common+0x135/0x150
virtballoon_remove+0xee/0x100
virtio_dev_remove+0x3b/0x80
device_release_driver_internal+0x187/0x2c0
unbind_store+0xb9/0xe0
kernfs_fop_write_iter.llvm.11660790530567441834+0xf6/0x180
vfs_write+0x2a9/0x3b0
ksys_write+0x5c/0xd0
do_syscall_64+0x54/0x230
entry_SYSCALL_64_after_hwframe+0x29/0x31
[...]
</TASK>The virtio_balloon device registers 5 queues (inflate, deflate, stats, free_page, reporting) but only the first two are unconditional. The stats, free_page and reporting queues are each conditional on their respective feature bits. When any of these features are absent, the corresponding vqs_info entry has name == NULL, creating holes in the array.
The root cause is an indexing mismatch introduced when vq info storage was changed to be passed as an argument. vp_find_vqs_msix() and vp_find_vqs_intx() store the info pointer at vp_dev->vqs[i], where 'i' is the caller's sparse array index. However, the virtqueue itself gets vq->index assigned from queue_idx, a dense index that skips NULL entries. When holes exist, 'i' and queue_idx diverge. Later, vp_del_vqs() looks up info via vp_dev->vqs[vq->index] using the dense index into the sparsely-populated array, and hits NULL.
Fix this by storing info at vp_dev->vqs[queue_idx] instead of vp_dev->vqs[i], so the store index matches the lookup index (vq->index). Apply the fix to both the MSIX and INTX paths.
Message-Id: <[email protected]>
--- drivers/virtio/virtio_pci_common.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
drivers/virtio/virtio_pci_common.c +6 -4
@@ -426,10 +426,11 @@ static int vp_find_vqs_msix(struct virti vqs[i] =NULL ;continue ; } - vqs[i] =vp_find_one_vq_msix (vdev, queue_idx++, vqi->callback, + vqs[i] =vp_find_one_vq_msix (vdev, queue_idx, vqi->callback, vqi->name, vqi->ctx, false, &allocated_vectors, vector_policy, - &vp_dev->vqs[i]); + &vp_dev->vqs[queue_idx]); + queue_idx++;if (IS_ERR (vqs[i])) { err =PTR_ERR (vqs[i]); goto error_find;
@@ -488,9 +489,10 @@ static int vp_find_vqs_intx(struct virti vqs[i] =NULL ;continue ; } - vqs[i] =vp_setup_vq (vdev, queue_idx++, vqi->callback, + vqs[i] =vp_setup_vq (vdev, queue_idx, vqi->callback, vqi->name, vqi->ctx, -VIRTIO_MSI_NO_VECTOR , &vp_dev->vqs[i]); +VIRTIO_MSI_NO_VECTOR , &vp_dev->vqs[queue_idx]); + queue_idx++;if (IS_ERR (vqs[i])) { err =PTR_ERR (vqs[i]); goto out_del_vqs;
Greg Kroah-Hartman
maintainerPATCH 148/480 virtio-mmio: fix device release warning on module unload
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit c687bc35694698ec4c7f92bf929c3d659f0cecb8 upstream.
Driver core expects devices to be allocated dynamically and complains loudly when a device that lacks a release function is freed.
Use __root_device_register() to allocate and register the root device instead of open coding using a static device.
Note that root_device_register(), which also creates a link to the module, cannot be used as the device is registered when parsing the module parameters which happens before the module kobject has been set up.
Message-ID: <[email protected]>
--- drivers/virtio/virtio_mmio.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)
drivers/virtio/virtio_mmio.c +13 -13
@@ -662,9 +662,7 @@ static void virtio_mmio_remove(struct pl#if defined(CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES ) -static struct device vm_cmdline_parent = { - .init_name ="virtio-mmio-cmdline" , -}; +static struct device *vm_cmdline_parent;static int vm_cmdline_parent_registered;static int vm_cmdline_id;
@@ -672,7 +670,6 @@ static int vm_cmdline_id;static int vm_cmdline_set (const char *device,const struct kernel_param *kp) { -int err;struct resource resources[2 ] = {};char *str;long long base, size;
@@ -704,11 +701,10 @@ static int vm_cmdline_set(const char *de resources[1 ].start = resources[1 ].end = irq;if (!vm_cmdline_parent_registered) { - err =device_register (&vm_cmdline_parent); -if (err) { -put_device (&vm_cmdline_parent); + vm_cmdline_parent =__root_device_register ("virtio-mmio-cmdline" ,NULL ); +if (IS_ERR (vm_cmdline_parent)) {pr_err ("Failed to register parent device!\n" ); -return err; +return PTR_ERR (vm_cmdline_parent); } vm_cmdline_parent_registered =1 ; }
@@ -719,7 +715,7 @@ static int vm_cmdline_set(const char *de (unsigned long long )resources[0 ].end, (int)resources[1].start); - pdev =platform_device_register_resndata (&vm_cmdline_parent, + pdev =platform_device_register_resndata (vm_cmdline_parent,"virtio-mmio" , vm_cmdline_id++, resources,ARRAY_SIZE (resources),NULL ,0 );
@@ -743,8 +739,12 @@ static int vm_cmdline_get_device(structstatic int vm_cmdline_get (char *buffer,const struct kernel_param *kp) { buffer[0 ] ='\0' ; -device_for_each_child (&vm_cmdline_parent, buffer, - vm_cmdline_get_device); + +if (vm_cmdline_parent_registered) { +device_for_each_child (vm_cmdline_parent, buffer, + vm_cmdline_get_device); + } +return strlen (buffer) +1 ; }
@@ -766,9 +766,9 @@ static int vm_unregister_cmdline_device(static void vm_unregister_cmdline_devices (void ) {if (vm_cmdline_parent_registered) { -device_for_each_child (&vm_cmdline_parent,NULL , +device_for_each_child (vm_cmdline_parent,NULL , vm_unregister_cmdline_device); -device_unregister (&vm_cmdline_parent); +root_device_unregister (vm_cmdline_parent); vm_cmdline_parent_registered =0 ; } }
Greg Kroah-Hartman
maintainerPATCH 149/480 hwrng: virtio: clamp device-reported used.len at copy_data()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit e3046eeada299f917a8ad883af4434bfb86556b1 upstream.
random_recv_done() stores the device-reported used.len directly into vi->data_avail. copy_data() then indexes vi->data[] using vi->data_idx (advanced by previous copy_data() calls) and issues a memcpy() without re-validating either value against the posted buffer size sizeof(vi->data) (SMP_CACHE_BYTES bytes, typically 32 or 64).
A malicious or buggy virtio-rng backend can set used.len beyond sizeof(vi->data), steering the memcpy() past the end of the inline array into adjacent kmalloc-1k slab bytes. hwrng_fillfn() mixes those bytes into the guest RNG, and guest root can also observe them directly via /dev/hwrng.
Concrete impact is inside the guest:
- Memory-safety / hardening: any virtio-rng backend that over-reports used.len causes the driver to read past vi->data into unrelated slab contents. hwrng_fillfn() is a kernel thread that runs as soon as the device is probed; no guest userspace interaction is required to first-trigger the OOB.
- Cross-boundary leak (confidential-compute threat model): a malicious hypervisor cooperating with a malicious or compromised guest root userspace can use /dev/hwrng as a leak channel for guest-kernel heap data. The host sets a large used.len, guest root reads /dev/hwrng, and the returned bytes contain guest kernel slab contents that were adjacent to vi->data. In practice, confidential-compute guests (SEV-SNP, TDX) usually disable virtio-rng entirely, so this path is narrow, but the fix is still worth carrying because the underlying memory-safety bug contaminates the guest RNG on any host.
KASAN confirms the OOB on a 7.1-rc4 guest whose virtio-rng backend has been patched to report used.len = 0x10000:
BUG: KASAN: slab-out-of-bounds in virtio_read+0x394/0x5d0 Read of size 64 at addr ffff88800ae0ba20 by task hwrng/52 Call Trace: __asan_memcpy+0x23/0x60 virtio_read+0x394/0x5d0 hwrng_fillfn+0xb2/0x470 kthread+0x2cc/0x3a0 Allocated by task 1: probe_common+0xa5/0x660 virtio_dev_probe+0x549/0xbc0 The buggy address belongs to the object at ffff88800ae0b800 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 0 bytes to the right of allocated 544-byte region [ffff88800ae0b800, ffff88800ae0ba20)
Same class of bug as commit c04db81cd028 ("net/9p: Fix buffer overflow in USB transport layer"), which hardened usb9pfs_rx_complete() against unchecked device-reported length in the USB 9p transport.
With the clamp at point of use and array_index_nospec() in place, the same harness boots cleanly: copy_data() returns zero for the bogus report, the device-supplied bytes after data_idx are discarded, and the driver issues a fresh request.
Message-ID: <[email protected]>
--- drivers/char/hw_random/virtio-rng.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)
drivers/char/hw_random/virtio-rng.c +21 -2
@@ -7,6 +7,7 @@#include <asm/barrier.h> #include <linux/err.h> #include <linux/hw_random.h> +#include <linux/nospec.h> #include <linux/scatterlist.h> #include <linux/spinlock.h> #include <linux/virtio.h>
@@ -66,8 +67,26 @@ static void request_entropy(struct virtrstatic unsigned int copy_data (struct virtrng_info *vi,void *buf,unsigned int size) { - size = min_t(unsigned int, size, vi->data_avail); -memcpy (buf, vi->data + vi->data_idx, size); +unsigned int idx, avail; + +/* +* vi->data_avail was set from the device-reported used.len and +* vi->data_idx was advanced by previous copy_data() calls. A +* malicious or buggy virtio-rng backend can drive either past +* sizeof(vi->data). Clamp at point of use and harden the index +* with array_index_nospec() so the memcpy() below cannot be +* steered into adjacent slab memory, including under +* speculation. +*/ + avail = min_t(unsigned int, vi->data_avail, sizeof(vi->data)); +if (vi->data_idx >= avail) { + vi->data_avail =0 ; +request_entropy (vi); +return 0 ; + } + size = min_t(unsigned int, size, avail - vi->data_idx); + idx =array_index_nospec (vi->data_idx,sizeof (vi->data)); +memcpy (buf, vi->data + idx, size); vi->data_idx += size; vi->data_avail -= size;if (vi->data_avail ==0 )
Greg Kroah-Hartman
maintainerPATCH 150/480 USB: chaoskey: Fix slab-use-after-free in chaoskey_release()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alan Stern <[email protected]>
commit abf76d3239dee97b66e7241ad04811f1ce562e28 upstream.
The chaoskey driver has a use-after-free bug in its release routine. If the user closes the device file after the USB device has been unplugged, a debugging log statement will try to access the usb_interface structure after it has been deallocated:
BUG: KASAN: slab-use-after-free in dev_driver_string (drivers/base/core.c:2406) Read of size 8 at addr ffff888168e8a0b8 by task chaoskey_raw_re/10106 Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: <TASK> dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120) print_report (mm/kasan/report.c:378 mm/kasan/report.c:482) kasan_report (mm/kasan/report.c:595) dev_driver_string (drivers/base/core.c:2406) __dynamic_dev_dbg (lib/dynamic_debug.c:906) chaoskey_release (drivers/usb/misc/chaoskey.c:323) __fput (fs/file_table.c:510) fput_close_sync (fs/file_table.c:615) __x64_sys_close (fs/open.c:1507 fs/open.c:1492 fs/open.c:1492) do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
The driver's last reference to the interface structure is dropped in the chaoskey_free() routine, so the code must not use the interface -- even in a debugging statement -- after that routine returns. (Exception: If we know that another reference is held by someone else, such as the device core while the disconnect routine runs, there's no problem. Thanks to Johan Hovold for pointing this out.)
Since the bad access is part of an unimportant debugging statement, we can fix the problem simply by removing the whole statement.
--- drivers/usb/misc/chaoskey.c | 1 - 1 file changed, 1 deletion(-)
drivers/usb/misc/chaoskey.c +0 -1
@@ -320,7 +320,6 @@ bail:mutex_unlock (&dev->lock); destruction:mutex_unlock (&chaoskey_list_lock); -usb_dbg (interface,"release success" );return rv; }
Greg Kroah-Hartman
maintainerPATCH 151/480 usb: dwc3: run gadget disconnect from sleepable suspend context
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
commit 010382937fb69892b3469ac4d30af072262f59e8 upstream.
dwc3_gadget_suspend() takes dwc->lock with IRQs disabled and then calls dwc3_disconnect_gadget(). For async callbacks that helper only uses plain spin_unlock()/spin_lock(), so the gadget ->disconnect() callback still runs with IRQs disabled and any sleepable callback trips Lockdep.
This issue was found by our static analysis tool and then manually reviewed against the current tree.
The grounded PoC kept the dwc3_gadget_suspend() -> dwc3_disconnect_gadget() -> gadget_driver->disconnect() chain, and Lockdep reported:
BUG: sleeping function called from invalid context gadget_disconnect+0x21/0x39 [vuln_msv] dwc3_gadget_suspend.constprop.0+0x2b/0x42 [vuln_msv]
Keep the disconnect callback selection in one common helper, but add a sleepable suspend-side wrapper which snapshots the callback under dwc->lock and then runs it after spin_unlock_irqrestore(). The regular event path still uses the existing spin_unlock()/spin_lock() window.
--- drivers/usb/dwc3/gadget.c | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-)
drivers/usb/dwc3/gadget.c +36 -7
@@ -3934,15 +3934,48 @@ static void dwc3_endpoint_interrupt(stru } } +static bool dwc3_prepare_disconnect_gadget (struct dwc3 *dwc, +struct usb_gadget_driver **driver, +struct usb_gadget **gadget) +{ +if (!dwc->async_callbacks || !dwc->gadget_driver || + !dwc->gadget_driver->disconnect) +return false; + + *driver = dwc->gadget_driver; + *gadget = dwc->gadget; + +return true; +} +static void dwc3_disconnect_gadget (struct dwc3 *dwc) { -if (dwc->async_callbacks && dwc->gadget_driver->disconnect) { +struct usb_gadget_driver *driver; +struct usb_gadget *gadget; + +if (dwc3_prepare_disconnect_gadget (dwc, &driver, &gadget)) {spin_unlock (&dwc->lock); - dwc->gadget_driver->disconnect (dwc->gadget); + driver->disconnect (gadget);spin_lock (&dwc->lock); } } +static void dwc3_disconnect_gadget_sleepable (struct dwc3 *dwc) +{ +struct usb_gadget_driver *driver; +struct usb_gadget *gadget; +unsigned long flags; + +spin_lock_irqsave (&dwc->lock, flags); +if (!dwc3_prepare_disconnect_gadget (dwc, &driver, &gadget)) { +spin_unlock_irqrestore (&dwc->lock, flags); +return ; + } + +spin_unlock_irqrestore (&dwc->lock, flags); + driver->disconnect (gadget); +} +static void dwc3_suspend_gadget (struct dwc3 *dwc) {if (dwc->async_callbacks && dwc->gadget_driver->suspend) {
@@ -4836,7 +4869,6 @@ void dwc3_gadget_exit(struct dwc3 *dwc)int dwc3_gadget_suspend (struct dwc3 *dwc) { -unsigned long flags;int ret; ret =dwc3_gadget_soft_disconnect (dwc);
@@ -4850,10 +4882,7 @@ int dwc3_gadget_suspend(struct dwc3 *dwcreturn -EAGAIN ; } -spin_lock_irqsave (&dwc->lock, flags); -if (dwc->gadget_driver) -dwc3_disconnect_gadget (dwc); -spin_unlock_irqrestore (&dwc->lock, flags); +dwc3_disconnect_gadget_sleepable (dwc);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 152/480 usb: misc: usbio: fix disconnect UAF in client teardown
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang <[email protected]>
commit 0bfeec21984fedd32987f4e4c0cde34b445af404 upstream.
usbio_disconnect() walks usbio->cli_list in reverse and uninitializes each auxiliary device. auxiliary_device_uninit() drops the device reference, and for an unbound child that can run usbio_auxdev_release() and free the containing struct usbio_client.
list_for_each_entry_reverse() advances after the loop body by reading client->link.prev. If the current client is freed by auxiliary_device_uninit(), the iterator dereferences freed memory.
Use list_for_each_entry_safe_reverse() so the previous client is cached before the body can drop the final reference. This preserves reverse teardown order while keeping the next iterator cursor independent of the current client's lifetime.
Validation reproduced this kernel report:
BUG: KASAN: slab-use-after-free in usbio_disconnect+0x12e/0x150 Call Trace: <TASK> dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? usbio_disconnect+0x12e/0x150 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x188/0x320 ? usbio_disconnect+0x12e/0x150 kasan_report+0xe0/0x110 ? usbio_disconnect+0x12e/0x150 usbio_disconnect+0x12e/0x150 usb_unbind_interface+0xf3/0x400 really_probe+0x316/0x660 __driver_probe_device+0x106/0x240 driver_probe_device+0x4a/0x110 __device_attach_driver+0xf1/0x1a0 ? __pfx___device_attach_driver+0x10/0x10 bus_for_each_drv+0xf9/0x160 ? __pfx_bus_for_each_drv+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on+0x18/0x130 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock_irqrestore+0x44/0x60 __device_attach+0x133/0x2a0 ? __pfx___device_attach+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? do_raw_spin_unlock+0x9a/0x100 ? srso_alias_return_thunk+0x5/0xfbef5 device_initial_probe+0x55/0x70 bus_probe_device+0x4a/0xd0 device_add+0x9b9/0xc10 ? __pfx_device_add+0x10/0x10 ? _raw_spin_unlock_irqrestore+0x44/0x60 ? srso_alias_return_thunk+0x5/0xfbef5 ? lockdep_hardirqs_on_prepare+0xea/0x1a0 ? srso_alias_return_thunk+0x5/0xfbef5 ? usb_enable_lpm+0x3c/0x260 usb_set_configuration+0xb64/0xf20 usb_generic_driver_probe+0x5f/0x90 usb_probe_device+0x71/0x1b0 really_probe+0x46b/0x660 __driver_probe_device+0x106/0x240 driver_probe_device+0x4a/0x110 __device_attach_driver+0xf1/0x1a0 ? __pfx___device_attach_driver+0x10/0x10 bus_for_each_drv+0xf9/0x160 ? __pfx_bus_for_each_drv+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? trace_hardirqs_on+0x18/0x130 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock_irqrestore+0x44/0x60 __device_attach+0x133/0x2a0 ? __pfx___device_attach+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? do_raw_spin_unlock+0x9a/0x100 ? srso_alias_return_thunk+0x5/0xfbef5 device_initial_probe+0x55/0x70 bus_probe_device+0x4a/0xd0 device_add+0x9b9/0xc10 ? __pfx_device_add+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? add_device_randomness+0xb7/0xf0 usb_new_device+0x492/0x870 hub_event+0x1b10/0x29c0 ? __pfx_hub_event+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_acquire+0x187/0x300 ? process_one_work+0x475/0xb90 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0xc8/0x290 ? srso_alias_return_thunk+0x5/0xfbef5 process_one_work+0x4d7/0xb90 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_hub_event+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x2d8/0x570 ? __pfx_worker_thread+0x10/0x10 kthread+0x1ad/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x3c9/0x540 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x2e9/0x730 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 </TASK>
--- drivers/usb/misc/usbio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/usb/misc/usbio.c +2 -2
@@ -518,7 +518,7 @@ static int usbio_resume(struct usb_interstatic void usbio_disconnect (struct usb_interface *intf) {struct usbio_device *usbio =usb_get_intfdata (intf); -struct usbio_client *client; +struct usbio_client *client, *next;/* Wakeup any clients waiting for a reply */ usbio->rxdat_len =0 ;
@@ -535,7 +535,7 @@ static void usbio_disconnect(struct usb_usb_kill_urb (usbio->urb);usb_free_urb (usbio->urb); -list_for_each_entry_reverse (client, &usbio->cli_list, link) { +list_for_each_entry_safe_reverse (client, next, &usbio->cli_list, link) {auxiliary_device_delete (&client->auxdev);auxiliary_device_uninit (&client->auxdev); }
Greg Kroah-Hartman
maintainerPATCH 153/480 6lowpan: fix NHC entry use-after-free on error path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yizhou Zhao <[email protected]>
commit 1720db928e5a58ca7d75ac1d514c3b73fd7061a7 upstream.
lowpan_nhc_do_uncompression() looks up an NHC descriptor while holding lowpan_nhc_lock. If the descriptor has no uncompress callback, the error path drops the lock before printing nhc->name.
lowpan_nhc_del() removes descriptors under the same lock and then relies on synchronize_net() before the owning module can be unloaded. That only waits for net RX RCU readers. lowpan_header_decompress() is also exported and can be reached from callers that are not necessarily covered by the net core RX critical section, for example the Bluetooth 6LoWPAN L2CAP receive path.
This leaves a race where one task drops lowpan_nhc_lock in the error path, another task unregisters and frees the matching descriptor after synchronize_net() returns, and the first task then dereferences nhc->name for the warning.
With the post-unlock window widened, KASAN reports:
BUG: KASAN: slab-use-after-free in lowpan_nhc_do_uncompression+0x1f4/0x220 Read of size 8 lowpan_nhc_do_uncompression lowpan_header_decompress
Fix this by printing the warning before dropping lowpan_nhc_lock, so the descriptor name is read while unregister is still excluded. The malformed packet is still rejected with -ENOTSUPP.
--- net/6lowpan/nhc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/6lowpan/nhc.c +1 -1
@@ -117,9 +117,9 @@ int lowpan_nhc_do_uncompression(struct sreturn ret; } }else { -spin_unlock_bh (&lowpan_nhc_lock);netdev_warn (dev,"received nhc id for %s which is not implemented.\n" , nhc->name); +spin_unlock_bh (&lowpan_nhc_lock);return -ENOTSUPP ; } }else {
Greg Kroah-Hartman
maintainerPATCH 154/480 tipc: fix out-of-bounds read in broadcast Gap ACK blocks
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Samuel Page <[email protected]>
commit 2b66974a1b6134a4bbc3bfed181f7418f688eb54 upstream.
A broadcast PROTOCOL/STATE_MSG can carry a Gap ACK blocks record in its data area. tipc_get_gap_ack_blks() only verifies that the record's len field is self-consistent with its ugack_cnt/bgack_cnt counts (sz == struct_size(p, gacks, ugack_cnt + bgack_cnt)); it does not check that the record actually fits in the message data area, msg_data_sz().
The unicast caller tipc_link_proto_rcv() bounds it ("if (glen > dlen) break;"), but the broadcast caller tipc_bcast_sync_rcv() discards the returned size, so tipc_link_advance_transmq() copies the record off the receive skb with an attacker-controlled count:
this_ga = kmemdup(ga, struct_size(ga, gacks, ga->bgack_cnt), GFP_ATOMIC);
A TIPC neighbour that negotiated TIPC_GAP_ACK_BLOCK triggers it with one ordinary broadcast STATE_MSG (msg_bc_ack_invalid() clear), sized so its data area is short, carrying a Gap ACK record with len = 0x400, bgack_cnt = 0xff and ugack_cnt = 0. len then equals struct_size(p, gacks, 255), so the consistency check passes and ga is non-NULL; kmemdup() reads struct_size(ga, gacks, 255) = 1024 bytes out of the much smaller skb:
BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x48/0x60 Read of size 1024 at addr ffff0000c7030d38 by task poc864/69 Call trace: kmemdup_noprof+0x48/0x60 tipc_link_advance_transmq+0x86c/0xb80 tipc_link_bc_ack_rcv+0x19c/0x1e0 tipc_bcast_sync_rcv+0x1c4/0x2c4 tipc_rcv+0x85c/0x1340 tipc_l2_rcv_msg+0xac/0x104 The buggy address belongs to the object at ffff0000c7030d00 which belongs to the cache skbuff_small_head of size 704 The buggy address is located 56 bytes inside of allocated 704-byte region [ffff0000c7030d00, ffff0000c7030fc0)
The copied-out bytes are subsequently consumed as gap/ack values, but the read is already out of bounds at the kmemdup() regardless of how they are used.
The unicast STATE path drops such a message: "if (glen > dlen) break;" skips the rest of STATE_MSG handling and the skb is freed. Make the broadcast path drop it too. tipc_bcast_sync_rcv() now bounds the record against msg_data_sz() and, when it does not fit, reports it back through tipc_node_bc_sync_rcv() to tipc_rcv() so the skb is discarded rather than processed. ga is not cleared on this path: ga == NULL already means "legacy peer without Selective ACK", a distinct legitimate state.
--- net/tipc/bcast.c | 22 ++++++++++++++-------- net/tipc/bcast.h | 2 +- net/tipc/node.c | 15 ++++++++++++--- 3 files changed, 27 insertions(+), 12 deletions(-)
net/tipc/bcast.c +14 -8
@@ -497,12 +497,13 @@ void tipc_bcast_ack_rcv(struct net *net,*/ int tipc_bcast_sync_rcv (struct net *net,struct tipc_link *l,struct tipc_msg *hdr, -struct sk_buff_head *retrq) +struct sk_buff_head *retrq,bool *valid) {struct sk_buff_head *inputq = &tipc_bc_base (net)->inputq;struct tipc_gap_ack_blks *ga;struct sk_buff_head xmitq;int rc =0 ; +u16 glen;__skb_queue_head_init (&xmitq);
@@ -510,13 +511,18 @@ int tipc_bcast_sync_rcv(struct net *net,if (msg_type (hdr) !=STATE_MSG ) {tipc_link_bc_init_rcv (l, hdr); }else if (!msg_bc_ack_invalid (hdr)) { -tipc_get_gap_ack_blks (&ga, l, hdr, false); -if (!sysctl_tipc_bc_retruni) - retrq = &xmitq; - rc =tipc_link_bc_ack_rcv (l,msg_bcast_ack (hdr), -msg_bc_gap (hdr), ga, &xmitq, - retrq); - rc |=tipc_link_bc_sync_rcv (l, hdr, &xmitq); + glen =tipc_get_gap_ack_blks (&ga, l, hdr, false); +if (glen >msg_data_sz (hdr)) { +/* Malformed Gap ACK blocks; caller drops the msg */ + *valid = false; + }else { +if (!sysctl_tipc_bc_retruni) + retrq = &xmitq; + rc =tipc_link_bc_ack_rcv (l,msg_bcast_ack (hdr), +msg_bc_gap (hdr), ga, &xmitq, + retrq); + rc |=tipc_link_bc_sync_rcv (l, hdr, &xmitq); + } }tipc_bcast_unlock (net);
net/tipc/bcast.h +1 -1
@@ -97,7 +97,7 @@ void tipc_bcast_ack_rcv(struct net *net, struct tipc_msg *hdr);int tipc_bcast_sync_rcv (struct net *net,struct tipc_link *l,struct tipc_msg *hdr, -struct sk_buff_head *retrq); +struct sk_buff_head *retrq,bool *valid);int tipc_nl_add_bc_link (struct net *net,struct tipc_nl_msg *msg,struct tipc_link *bcl);int tipc_nl_bc_link_set (struct net *net,struct nlattr *attrs[]);
net/tipc/node.c +12 -3
@@ -1831,12 +1831,15 @@ static void tipc_node_mcast_rcv(struct t }static void tipc_node_bc_sync_rcv (struct tipc_node *n,struct tipc_msg *hdr, -int bearer_id,struct sk_buff_head *xmitq) +int bearer_id,struct sk_buff_head *xmitq, +bool *valid) {struct tipc_link *ucl;int rc; - rc =tipc_bcast_sync_rcv (n->net, n->bc_entry.link, hdr, xmitq); + rc =tipc_bcast_sync_rcv (n->net, n->bc_entry.link, hdr, xmitq, valid); +if (!*valid) +return ;if (rc &TIPC_LINK_DOWN_EVT ) {tipc_node_reset_links (n);
@@ -2140,12 +2143,18 @@ rcv:/* Ensure broadcast reception is in synch with peer's send state */ if (unlikely (usr ==LINK_PROTOCOL )) { +bool valid = true; +if (unlikely (skb_linearize (skb))) {tipc_node_put (n); goto discard; } hdr =buf_msg (skb); -tipc_node_bc_sync_rcv (n, hdr, bearer_id, &xmitq); +tipc_node_bc_sync_rcv (n, hdr, bearer_id, &xmitq, &valid); +if (!valid) { +tipc_node_put (n); + goto discard; + } }else if (unlikely (tipc_link_acked (n->bc_entry.link) != bc_ack)) {tipc_bcast_ack_rcv (net, n->bc_entry.link, hdr); }
Greg Kroah-Hartman
maintainerPATCH 155/480 staging: vme_user: bound slave read/write to the kern_buf size
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Tautschnig <[email protected]>
commit 9f32f38265014fac7f5dc9490fb01a638ce6e121 upstream.
The SLAVE-path helpers buffer_to_user() and buffer_from_user() copy 'count' bytes into/out of the fixed-size kern_buf (size_buf == PCI_BUF_SIZE == 0x20000, 128 KiB) using *ppos as the offset, without bounding *ppos + count against size_buf.
vme_user_write()/vme_user_read() only clamp count to the VME window size (image_size = vme_get_size(resource)), which VME_SET_SLAVE sets from the user-supplied slave.size -- validated against the VME address space (up to VME_A32_MAX = 4 GiB), not against PCI_BUF_SIZE. When the window exceeds 128 KiB, a write()/read() copies past the kern_buf allocation.
Clamp count against size_buf in both helpers, with an early return when *ppos is already at/after the buffer end. *ppos is >= 0 here (the caller rejects negative offsets), so size_buf - *ppos cannot wrap. This mirrors the existing clamp in the MASTER-path helpers resource_to_user() / resource_from_user(), and matches the read()/write() convention of a short transfer at end-of-buffer.
Found by static analysis (CodeQL taint tracking + CBMC bounded model checking) and confirmed dynamically under KASAN with the vme_fake bridge:
BUG: KASAN: slab-out-of-bounds in _copy_from_user+0x2d/0x80
Write of size 262144 at addr ffff888004100000 by task trigger/68
_copy_from_user+0x2d/0x80
vme_user_write+0x13e/0x240 [vme_user]
vfs_write+0x1b8/0x7a0
ksys_write+0xb8/0x150--- drivers/staging/vme_user/vme_user.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
drivers/staging/vme_user/vme_user.c +22 -0
@@ -156,6 +156,17 @@ static ssize_t buffer_to_user(unsigned i {void *image_ptr; +/* +* The slave window (image_size) can exceed the fixed kern_buf +* (size_buf == PCI_BUF_SIZE), so bound the copy to kern_buf. +* *ppos is >= 0 here (checked by the caller), so the +* subtraction below cannot wrap. +*/ +if (*ppos >= image[minor].size_buf) +return 0 ; +if (count > image[minor].size_buf - *ppos) + count = image[minor].size_buf - *ppos; + image_ptr = image[minor].kern_buf + *ppos;if (copy_to_user (buf, image_ptr, (unsigned long )count))return -EFAULT ;
@@ -168,6 +179,17 @@ static ssize_t buffer_from_user(unsigned {void *image_ptr; +/* +* The slave window (image_size) can exceed the fixed kern_buf +* (size_buf == PCI_BUF_SIZE), so bound the copy to kern_buf. +* *ppos is >= 0 here (checked by the caller), so the +* subtraction below cannot wrap. +*/ +if (*ppos >= image[minor].size_buf) +return 0 ; +if (count > image[minor].size_buf - *ppos) + count = image[minor].size_buf - *ppos; + image_ptr = image[minor].kern_buf + *ppos;if (copy_from_user (image_ptr, buf, (unsigned long )count))return -EFAULT ;
Greg Kroah-Hartman
maintainerPATCH 156/480 smb: client: restrict implied bcc[0] exemption to responses without data area
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shoichiro Miyamoto <[email protected]>
commit 53b7c271f06be4dd5cfc8c6ef552a8355c891a7f upstream.
smb2_check_message() has a long-standing quirk that accepts a response whose calculated length is one byte larger than the bytes actually received ("server can return one byte more due to implied bcc[0]"). This was introduced to accommodate servers that omit the trailing bcc[0] overlap byte when no data area is present.
However, the exemption is applied unconditionally, regardless of whether the command actually carries a data area (has_smb2_data_area[]). When a response with a data area is subject to the +1 exemption, the reported data can extend one byte beyond the bytes actually received, yet smb2_check_message() still accepts it. The subsequent decoder then reads past the end of the receive buffer. This is reachable during NEGOTIATE and SESSION_SETUP, before the session is established.
The resulting out-of-bounds reads are visible under KASAN when mounting against a non-conforming server; both the SPNEGO/negTokenInit and the NTLMSSP challenge decoders are affected:
BUG: KASAN: slab-out-of-bounds in asn1_ber_decoder+0x16a7/0x1b00 Read of size 1 at addr ffff8880084d67c0 by task mount.cifs/81 CPU: 1 UID: 0 PID: 81 Comm: mount.cifs Not tainted 7.1.0-rc6 #1 Call Trace: <TASK> dump_stack_lvl+0x4e/0x70 print_report+0x157/0x4c9 kasan_report+0xce/0x100 asn1_ber_decoder+0x16a7/0x1b00 decode_negTokenInit+0x19/0x30 SMB2_negotiate+0x31d9/0x4c90 cifs_negotiate_protocol+0x1f2/0x3f0 cifs_get_smb_ses+0x93f/0x17e0 cifs_mount_get_session+0x7f/0x3a0 cifs_mount+0xb4/0xcf0 cifs_smb3_do_mount+0x23a/0x1500 smb3_get_tree+0x3b0/0x630 vfs_get_tree+0x82/0x2d0 fc_mount+0x10/0x1b0 path_mount+0x50d/0x1de0 __x64_sys_mount+0x20b/0x270 do_syscall_64+0xee/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> Allocated by task 85: kmem_cache_alloc_noprof+0x106/0x380 mempool_alloc_noprof+0x116/0x1e0 cifs_small_buf_get+0x31/0x80 allocate_buffers+0x10d/0x2b0 cifs_demultiplex_thread+0x1d5/0x1d50 kthread+0x2c6/0x390 ret_from_fork+0x36e/0x5a0 ret_from_fork_asm+0x1a/0x30 The buggy address is located 0 bytes to the right of allocated 448-byte region [ffff8880084d6600, ffff8880084d67c0) which belongs to the cache cifs_small_rq of size 448 BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x36/0x50 Read of size 329 at addr ffff88800726c678 by task mount.cifs/89 CPU: 0 UID: 0 PID: 89 Comm: mount.cifs Tainted: G B 7.1.0-rc6 #1 Call Trace: <TASK> dump_stack_lvl+0x4e/0x70 print_report+0x157/0x4c9 kasan_report+0xce/0x100 kasan_check_range+0x10f/0x1e0 __asan_memcpy+0x23/0x60 kmemdup_noprof+0x36/0x50 decode_ntlmssp_challenge+0x457/0x680 SMB2_sess_auth_rawntlmssp_negotiate+0x6f0/0xcb0 SMB2_sess_setup+0x219/0x4f0 cifs_setup_session+0x248/0xaf0 cifs_get_smb_ses+0xf79/0x17e0 cifs_mount_get_session+0x7f/0x3a0 cifs_mount+0xb4/0xcf0 cifs_smb3_do_mount+0x23a/0x1500 smb3_get_tree+0x3b0/0x630 vfs_get_tree+0x82/0x2d0 fc_mount+0x10/0x1b0 path_mount+0x50d/0x1de0 __x64_sys_mount+0x20b/0x270 do_syscall_64+0xee/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> Allocated by task 93: kmem_cache_alloc_noprof+0x106/0x380 mempool_alloc_noprof+0x116/0x1e0 cifs_small_buf_get+0x31/0x80 allocate_buffers+0x10d/0x2b0 cifs_demultiplex_thread+0x1d5/0x1d50 kthread+0x2c6/0x390 ret_from_fork+0x36e/0x5a0 ret_from_fork_asm+0x1a/0x30 The buggy address is located 120 bytes inside of allocated 448-byte region [ffff88800726c600, ffff88800726c7c0) which belongs to the cache cifs_small_rq of size 448
Restrict the +1 exemption to responses that have no data area, so that it still covers the bcc[0] omission it was meant for. When a data area is present, the +1 discrepancy instead means the reported data length overruns the received buffer, so the response must be rejected.
--- fs/smb/client/smb2misc.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-)
fs/smb/client/smb2misc.c +26 -6
@@ -19,6 +19,8 @@#include "nterr.h" #include "cached_dir.h" +static unsigned int __smb2_calc_size (void *buf,bool *have_data); +static int check_smb2_hdr (struct smb2_hdr *shdr,__u64 mid) {
@@ -144,6 +146,7 @@ smb2_check_message(char *buf, unsigned iint command;__u32 calc_len;/* calculated length */ __u64 mid; +bool have_data;/* If server is a channel, select the primary channel */ pserver =SERVER_IS_CHAN (server) ? server->primary_server : server;
@@ -227,7 +230,8 @@ smb2_check_message(char *buf, unsigned i } } - calc_len =smb2_calc_size (buf); + have_data = false; + calc_len =__smb2_calc_size (buf, &have_data);/* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might * be 0, and not a real miscalculation */
@@ -246,8 +250,13 @@ smb2_check_message(char *buf, unsigned i/* Windows 7 server returns 24 bytes more */ if (calc_len +24 == len && command ==SMB2_OPLOCK_BREAK_HE )return 0 ; -/* server can return one byte more due to implied bcc[0] */ -if (calc_len == len +1 ) +/* +* Server can return one byte more due to implied bcc[0]. +* Allow it only when there is no data area; if data_length > 0 +* the +1 gap indicates an overreported data length rather than +* the bcc[0] omission. +*/ +if (calc_len == len +1 && !have_data)return 0 ;/*
@@ -408,14 +417,17 @@ smb2_get_data_area_len(int *off, int *le/* * Calculate the size of the SMB message based on the fixed header * portion, the number of word parameters and the data portion of the message. +* If have_data is non-NULL, it is set to true when a non-empty data area was +* found (data_length > 0), allowing callers to distinguish the implied bcc[0] +* case (no data area) from an overreported data length. */ -unsigned int -smb2_calc_size (void *buf) +static unsigned int +__smb2_calc_size (void *buf,bool *have_data) {struct smb2_pdu *pdu = buf;struct smb2_hdr *shdr = &pdu->hdr;int offset;/* the offset from the beginning of SMB to data area */ -int data_length;/* the length of the variable length data area */ +int data_length =0 ;/* the length of the variable length data area */ /* Structure Size has already been checked to make sure it is 64 */ int len =le16_to_cpu (shdr->StructureSize);
@@ -448,9 +460,17 @@ smb2_calc_size(void *buf) } calc_size_exit:cifs_dbg (FYI ,"SMB2 len %d\n" , len); +if (have_data) + *have_data = (data_length >0 );return len; } +unsigned int +smb2_calc_size (void *buf) +{ +return __smb2_calc_size (buf,NULL ); +} +/* Note: caller must free return buffer */ __le16 *cifs_convert_path_to_utf16 (const char *from,struct cifs_sb_info *cifs_sb)
Greg Kroah-Hartman
maintainerPATCH 157/480 staging: vme_user: fix location monitor leak in fake bridge
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hao-Qun Huang <[email protected]>
commit e8422d89e8af41d87f0e9db564be8e2634f4c602 upstream.
fake_init() allocates a location monitor resource and links it into fake_bridge->lm_resources. The init error path frees this list, but fake_exit() only frees the slave and master resource lists. Loading and unloading the module therefore triggers a kmemleak warning:
unreferenced object 0xffff8b8b82aebe40 (size 64):
comm "init", pid 1, jiffies 4294894572
backtrace (crc c1e013ef):
kmemleak_alloc+0x4e/0x90
__kmalloc_cache_noprof+0x338/0x430
0xffffffffc0602246
do_one_initcall+0x4f/0x320
do_init_module+0x68/0x270
load_module+0x2a3b/0x2d90Free the lm_resources list in fake_exit() as well, before fake_bridge is freed.
--- drivers/staging/vme_user/vme_fake.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/staging/vme_user/vme_fake.c +8 -0
@@ -1239,6 +1239,7 @@ static void __exit fake_exit(void) {struct list_head *pos =NULL ;struct list_head *tmplist; +struct vme_lm_resource *lm;struct vme_master_resource *master_image;struct vme_slave_resource *slave_image;int i;
@@ -1269,6 +1270,13 @@ static void __exit fake_exit(void)fake_crcsr_exit (fake_bridge);/* resources are stored in link list */ +list_for_each_safe (pos, tmplist, &fake_bridge->lm_resources) { + lm = list_entry(pos, struct vme_lm_resource, list); +list_del (pos); +kfree (lm); + } + +/* resources are stored in link list */ list_for_each_safe (pos, tmplist, &fake_bridge->slave_resources) { slave_image = list_entry(pos, struct vme_slave_resource, list);list_del (pos);
Greg Kroah-Hartman
maintainerPATCH 158/480 staging: vme_user: fix location monitor leak in tsi148 bridge
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hao-Qun Huang <[email protected]>
commit 151edde741f8bc7f2931c5f44ab376d32b0c8beb upstream.
tsi148_probe() allocates a location monitor resource and links it into tsi148_bridge->lm_resources. The probe error path frees this list, but tsi148_remove() only frees the dma, slave and master resource lists, so the location monitor resource is leaked on device unbind or module unload.
Free the lm_resources list in tsi148_remove() as well, before tsi148_bridge is freed.
--- drivers/staging/vme_user/vme_tsi148.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/staging/vme_user/vme_tsi148.c +8 -0
@@ -2536,6 +2536,7 @@ static void tsi148_remove(struct pci_dev {struct list_head *pos =NULL ;struct list_head *tmplist; +struct vme_lm_resource *lm;struct vme_master_resource *master_image;struct vme_slave_resource *slave_image;struct vme_dma_resource *dma_ctrlr;
@@ -2593,6 +2594,13 @@ static void tsi148_remove(struct pci_devtsi148_crcsr_exit (tsi148_bridge, pdev);/* resources are stored in link list */ +list_for_each_safe (pos, tmplist, &tsi148_bridge->lm_resources) { + lm = list_entry(pos, struct vme_lm_resource, list); +list_del (pos); +kfree (lm); + } + +/* resources are stored in link list */ list_for_each_safe (pos, tmplist, &tsi148_bridge->dma_resources) { dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);list_del (pos);
Greg Kroah-Hartman
maintainerPATCH 159/480 media: staging: ipu3-imgu: Add range check for imgu_css_cfg_acc_stripe
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ricardo Ribalda <[email protected]>
commit c32fe4c4918c9aa49f61359e3b42619c4d8686de upstream.
If the driver's stripe information is invalid it can result in an integer underflow. Add a range check to avoid this kind of error.
This patch fixes the following smatch error:drivers/staging/media/ipu3/ipu3-css-params.c:1792 imgu_css_cfg_acc_stripe() warn: 'acc->stripe.bds_out_stripes[0]->width - 2 * f' 4294967168 can't fit into 65535 'acc->stripe.bds_out_stripes[1]->offset'
--- drivers/staging/media/ipu3/ipu3-css-params.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/staging/media/ipu3/ipu3-css-params.c +6 -2
@@ -1770,6 +1770,8 @@ static int imgu_css_cfg_acc_stripe(struc acc->stripe.bds_out_stripes[0 ].width =ALIGN (css_pipe->rect[IPU3_CSS_RECT_BDS ].width, f); }else { +u32 offset; +/* Image processing is divided into two stripes */ acc->stripe.bds_out_stripes[0 ].width = acc->stripe.bds_out_stripes[1].width =
@@ -1788,8 +1790,10 @@ static int imgu_css_cfg_acc_stripe(struc acc->stripe.bds_out_stripes[1 ].width += f; }/* Overlap between stripes is IPU3_UAPI_ISP_VEC_ELEMS * 4 */ - acc->stripe.bds_out_stripes[1 ].offset = - acc->stripe.bds_out_stripes[0 ].width -2 * f; + offset = acc->stripe.bds_out_stripes[0 ].width -2 * f; +if (offset >65535 ) +return -EINVAL ; + acc->stripe.bds_out_stripes[1 ].offset = offset; } acc->stripe.effective_stripes[0].height =
Greg Kroah-Hartman
maintainerPATCH 160/480 staging: media: atomisp: reduce load_primary_binaries() stack usage
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arnd Bergmann <[email protected]>
commit f4d51e55dd47ef467fbe37d8575e20eee41b092d upstream.
The load_primary_binaries() function is overly complex and has som large variables on the stack, which can cause warnings depending on CONFIG_FRAME_WARN setting:
drivers/staging/media/atomisp/pci/sh_css.c: In function 'load_primary_binaries':drivers/staging/media/atomisp/pci/sh_css.c:5260:1: error: the frame size of 1560 bytes is larger than 1536 bytes [-Werror=frame-larger-than=]
Half of the stack usage is for the prim_descr[] array, but only one member of the array is used at any given time.
Reduce the stack usage by turning the array into a single structure.
--- drivers/staging/media/atomisp/pci/sh_css.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/staging/media/atomisp/pci/sh_css.c +3 -3
@@ -5021,7 +5021,6 @@ static int load_primary_binaries(struct ia_css_capture_settings *mycs;unsigned int i;bool need_extra_yuv_scaler = false; -struct ia_css_binary_descr prim_descr[MAX_NUM_PRIMARY_STAGES ];IA_CSS_ENTER_PRIVATE ("" );assert (pipe);
@@ -5191,15 +5190,16 @@ static int load_primary_binaries(/* Primary */ for (i =0 ; i < mycs->num_primary_stage; i++) { +struct ia_css_binary_descr prim_descr;struct ia_css_frame_info *local_vf_info =NULL ;if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0 ] && (i == mycs->num_primary_stage -1 )) local_vf_info = &vf_info; -ia_css_pipe_get_primary_binarydesc (pipe, &prim_descr[i], +ia_css_pipe_get_primary_binarydesc (pipe, &prim_descr, &prim_in_info, &prim_out_info, local_vf_info, i); - err =ia_css_binary_find (&prim_descr[i], &mycs->primary_binary[i]); + err =ia_css_binary_find (&prim_descr, &mycs->primary_binary[i]);if (err) {IA_CSS_LEAVE_ERR_PRIVATE (err);return err;
Greg Kroah-Hartman
maintainerPATCH 161/480 staging: media: ipu7: fix double-free and use-after-free in error paths
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandru Hossu <[email protected]>
commit d3a9a8cf2d7fd61a2f63df61f6cbc0a9bb007cc0 upstream.
In both ipu7_isys_init() and ipu7_psys_init(), pdata is allocated and then passed to ipu7_bus_initialize_device(), which stores it in adev->pdata. The ipu7_bus_release() function frees adev->pdata when the device's reference count drops to zero.
Two error paths incorrectly call kfree(pdata) after the device teardown has already freed it:
1. When ipu7_mmu_init() fails: put_device() is called, which drops the reference count to zero and triggers ipu7_bus_release() -> kfree(pdata). The subsequent kfree(pdata) is a double-free.
2. When ipu7_bus_add_device() fails: it calls auxiliary_device_uninit() internally, which calls put_device() -> ipu7_bus_release() -> kfree(pdata). The subsequent kfree(pdata) is again a double-free.
Note that the kfree(pdata) when ipu7_bus_initialize_device() itself fails is correct, because in that case auxiliary_device_init() failed and the release function was never set up, so pdata must be freed manually.
Additionally, the error code was not saved before calling put_device(), causing ERR_CAST() to dereference the already-freed adev pointer when constructing the return value. Fix this by saving the error from dev_err_probe() before put_device() and returning ERR_PTR() instead.
Remove the redundant kfree(pdata) calls and fix the use-after-free in the return values of the two affected error paths.
--- drivers/staging/media/ipu7/ipu7.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-)
drivers/staging/media/ipu7/ipu7.c +8 -14
@@ -2169,21 +2169,18 @@ ipu7_isys_init(struct pci_dev *pdev, struct device *parent, isys_adev->mmu =ipu7_mmu_init (dev, base,ISYS_MMID , &ipdata->hw_variant);if (IS_ERR (isys_adev->mmu)) { -dev_err_probe (dev,PTR_ERR (isys_adev->mmu), -"ipu7_mmu_init(isys_adev->mmu) failed\n" ); + ret =dev_err_probe (dev,PTR_ERR (isys_adev->mmu), +"ipu7_mmu_init(isys_adev->mmu) failed\n" );put_device (&isys_adev->auxdev.dev); -kfree (pdata); -return ERR_CAST (isys_adev->mmu); +return ERR_PTR (ret); } isys_adev->mmu->dev = &isys_adev->auxdev.dev; isys_adev->subsys =IPU_IS ; ret =ipu7_bus_add_device (isys_adev); -if (ret) { -kfree (pdata); +if (ret)return ERR_PTR (ret); - }return isys_adev; }
@@ -2216,21 +2213,18 @@ ipu7_psys_init(struct pci_dev *pdev, struct device *parent, psys_adev->mmu =ipu7_mmu_init (&pdev->dev, base,PSYS_MMID , &ipdata->hw_variant);if (IS_ERR (psys_adev->mmu)) { -dev_err_probe (&pdev->dev,PTR_ERR (psys_adev->mmu), -"ipu7_mmu_init(psys_adev->mmu) failed\n" ); + ret =dev_err_probe (&pdev->dev,PTR_ERR (psys_adev->mmu), +"ipu7_mmu_init(psys_adev->mmu) failed\n" );put_device (&psys_adev->auxdev.dev); -kfree (pdata); -return ERR_CAST (psys_adev->mmu); +return ERR_PTR (ret); } psys_adev->mmu->dev = &psys_adev->auxdev.dev; psys_adev->subsys =IPU_PS ; ret =ipu7_bus_add_device (psys_adev); -if (ret) { -kfree (pdata); +if (ret)return ERR_PTR (ret); - }return psys_adev; }
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 162/480 staging: rtl8723bs: fix heap buffer overflow in rtw_cfg80211_set_wpa_ie()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandru Hossu <[email protected]>
commit 5a752a616e756844388a1a45404db9fc29fec655 upstream.
supplicant_ie is a 256-byte array in struct security_priv. The WPA and WPA2 IE copy paths use:
memcpy(padapter->securitypriv.supplicant_ie, &pwpa[0], wpa_ielen + 2);
where wpa_ielen is the raw IE length field (u8, 0-255). When a local user supplies a connect request via nl80211 with a crafted WPA IE of length 255, wpa_ielen + 2 equals 257, overflowing the 256-byte buffer by one byte into the adjacent last_mic_err_time field.
rtw_parse_wpa_ie() does not prevent this: its length consistency check compares *(wpa_ie+1) against (u8)(wpa_ie_len-2), which is (u8)(255) == 255 when wpa_ie_len = 257, so the check passes silently.
Add explicit bounds checks for both the WPA and WPA2 paths before the memcpy, rejecting any IE whose total size (wpa_ielen + 2) exceeds the supplicant_ie buffer.
--- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +8 -0
@@ -1446,6 +1446,10 @@ static int rtw_cfg80211_set_wpa_ie(struc pwpa =rtw_get_wpa_ie (buf, &wpa_ielen, ielen);if (pwpa && wpa_ielen >0 ) { +if (wpa_ielen +2 >sizeof (padapter->securitypriv.supplicant_ie)) { + ret = -EINVAL ; + goto exit; + }if (rtw_parse_wpa_ie (pwpa, wpa_ielen +2 , &group_cipher, &pairwise_cipher,NULL ) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK;
@@ -1455,6 +1459,10 @@ static int rtw_cfg80211_set_wpa_ie(struc pwpa2 =rtw_get_wpa2_ie (buf, &wpa2_ielen, ielen);if (pwpa2 && wpa2_ielen >0 ) { +if (wpa2_ielen +2 >sizeof (padapter->securitypriv.supplicant_ie)) { + ret = -EINVAL ; + goto exit; + }if (rtw_parse_wpa2_ie (pwpa2, wpa2_ielen +2 , &group_cipher, &pairwise_cipher,NULL ) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK;
Greg Kroah-Hartman
maintainerPATCH 163/480 staging: rtl8723bs: fix WEP length underflow and OOB read in OnAuth()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandru Hossu <[email protected]>
commit a1fc19d61f661d47204f095b593de507884849f7 upstream.
OnAuth() has two bugs in the shared-key authentication path.
When the Privacy bit is set, rtw_wep_decrypt() is called without verifying that the frame is long enough to contain a valid WEP IV and ICV. Inside rtw_wep_decrypt(), length is computed as:
length = len - WLAN_HDR_A3_LEN - iv_len
and then passed as (length - 4) to crc32_le(). If len is less than WLAN_HDR_A3_LEN + iv_len + icv_len (32 bytes), length - 4 is negative and, after the implicit cast to size_t, causes crc32_le() to read far beyond the frame buffer. Add a minimum length check before accessing the IV field and calling the decryption path.
When processing a seq=3 response, rtw_get_ie() stores the Challenge Text IE length in ie_len, but the subsequent memcmp() always reads 128 bytes regardless of ie_len. IEEE 802.11 mandates a challenge text of exactly 128 bytes; reject any IE whose length field differs, matching the check already applied to OnAuthClient().
--- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +7 -1
@@ -703,6 +703,9 @@ unsigned int OnAuth(struct adapter *padaif ((pmlmeinfo->state&0x03 ) !=WIFI_FW_AP_STATE )return _FAIL; +if (len <WLAN_HDR_A3_LEN ) +return _FAIL; + sa =GetAddr2Ptr (pframe); auth_mode = psecuritypriv->dot11AuthAlgrthm;
@@ -714,6 +717,9 @@ unsigned int OnAuth(struct adapter *pada prxattrib->hdrlen =WLAN_HDR_A3_LEN ; prxattrib->encrypt = _WEP40_; +if (len <WLAN_HDR_A3_LEN +8 ) +return _FAIL; + iv = pframe+prxattrib->hdrlen; prxattrib->key_index = ((iv[3 ]>>6 )&0x3 );
@@ -818,7 +824,7 @@ unsigned int OnAuth(struct adapter *pada p =rtw_get_ie (pframe +WLAN_HDR_A3_LEN +4 + _AUTH_IE_OFFSET_,WLAN_EID_CHALLENGE , (int *)&ie_len, len -WLAN_HDR_A3_LEN - _AUTH_IE_OFFSET_ -4 ); -if (!p || ie_len <=0 ) { +if (!p || ie_len !=128 ) { status =WLAN_STATUS_CHALLENGE_FAIL ; goto auth_fail; }
Greg Kroah-Hartman
maintainerPATCH 164/480 staging: rtl8723bs: fix OOB read in OnAssocRsp() IE loop
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandru Hossu <[email protected]>
commit f9654207e92283e0acac5d64fe5f8835383b5a23 upstream.
The IE parsing loop in OnAssocRsp() advances by (pIE->length + 2) each iteration but only guards on i < pkt_len. When a malicious AP sends an AssocResponse whose last IE has only one byte remaining in the frame (the element_id byte lands at pkt_len-1), the loop reads pIE->length from pframe[pkt_len], which is one byte past the allocated receive buffer.
Additionally, even when the header bytes are in bounds, pIE->length itself can extend the data window beyond pkt_len, silently passing a truncated IE to the handler functions.
Add two guards at the top of the loop body:
1. Break if fewer than sizeof(*pIE) bytes remain (can't read header).
2. Break if the IE's declared data extends past pkt_len.
--- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +4 -0
@@ -1430,7 +1430,11 @@ unsigned int OnAssocRsp(struct adapter */* to handle HT, WMM, rate adaptive, update MAC reg */ /* for not to handle the synchronous IO in the tasklet */ for (i = (6 +WLAN_HDR_A3_LEN ); i < pkt_len;) { +if (i +sizeof (*pIE) > pkt_len) +break ; pIE = (struct ndis_80211_var_ie *)(pframe + i); +if (i +sizeof (*pIE) + pIE->length > pkt_len) +break ;switch (pIE->element_id) {case WLAN_EID_VENDOR_SPECIFIC :
Greg Kroah-Hartman
maintainerPATCH 165/480 staging: rtl8723bs: fix OOB read in update_beacon_info() IE loop
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandru Hossu <[email protected]>
commit ed51de4a86e173c3b0ef78e039c2e49e08b11f16 upstream.
The IE parsing loop in update_beacon_info() advances by (pIE->length + 2) each iteration but only guards on i < len. When a malicious AP sends a Beacon whose last IE has only one byte remaining in the frame (the element_id byte lands at len-1), the loop reads pIE->length from one byte past the allocated receive buffer.
Additionally, even when the header bytes are in bounds, pIE->length itself can extend the data window beyond len, passing a truncated IE to the handler functions.
Add two guards at the top of the loop body:
1. Break if fewer than sizeof(*pIE) bytes remain (can't read header).
2. Break if the IE's declared data extends past len.
Also replace i += (pIE->length + 2) with i += sizeof(*pIE) + pIE->length for consistency with the sizeof(*pIE) guards added above.
--- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/staging/rtl8723bs/core/rtw_wlan_util.c +5 -1
@@ -1288,7 +1288,11 @@ void update_beacon_info(struct adapter * len = pkt_len - (_BEACON_IE_OFFSET_ +WLAN_HDR_A3_LEN );for (i =0 ; i < len;) { +if (i +sizeof (*pIE) > len) +break ; pIE = (struct ndis_80211_var_ie *)(pframe + (_BEACON_IE_OFFSET_ +WLAN_HDR_A3_LEN ) + i); +if (i +sizeof (*pIE) + pIE->length > len) +break ;switch (pIE->element_id) {case WLAN_EID_VENDOR_SPECIFIC :
@@ -1313,7 +1317,7 @@ void update_beacon_info(struct adapter *break ; } - i += (pIE->length +2 ); + i +=sizeof (*pIE) + pIE->length; } }
Greg Kroah-Hartman
maintainerPATCH 166/480 staging: rtl8723bs: fix OOB reads in IE loops in issue_assocreq() and join_cmd_hdl()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandru Hossu <[email protected]>
commit ef61d628dfad38fead1fd2e08979ae9126d011d5 upstream.
Two IE parsing loops are missing the header bounds checks before they dereference pIE->length:
- issue_assocreq() walks pmlmeinfo->network.ies to build the association request. If the stored IE data ends with only an element_id byte and no length byte, pIE->length is read one byte past the end of the buffer.
- join_cmd_hdl() walks pnetwork->ies during station join and has the same problem under the same conditions.
Both buffers are filled from AP beacon and probe-response frames, so a malicious AP that sends a truncated final IE can trigger the issue.
Apply the two-guard pattern established in update_beacon_info():
1. Break if fewer than sizeof(*pIE) bytes remain.
2. Break if the IE's declared data extends past the buffer end.
--- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +8 -0
@@ -2972,7 +2972,11 @@ void issue_assocreq(struct adapter *pada/* vendor specific IE, such as WPA, WMM, WPS */ for (i =sizeof (struct ndis_802_11_fix_ie ); i < pmlmeinfo->network.ie_length;) { +if (i +sizeof (*pIE) > pmlmeinfo->network.ie_length) +break ; pIE = (struct ndis_80211_var_ie *)(pmlmeinfo->network.ies + i); +if (i +sizeof (*pIE) + pIE->length > pmlmeinfo->network.ie_length) +break ;switch (pIE->element_id) {case WLAN_EID_VENDOR_SPECIFIC :
@@ -5370,7 +5374,11 @@ u8 join_cmd_hdl(struct adapter *padapter/* sizeof(struct ndis_802_11_fix_ie) */ for (i = _FIXED_IE_LENGTH_; i < pnetwork->ie_length;) { +if (i +sizeof (*pIE) > pnetwork->ie_length) +break ; pIE = (struct ndis_80211_var_ie *)(pnetwork->ies + i); +if (i +sizeof (*pIE) + pIE->length > pnetwork->ie_length) +break ;switch (pIE->element_id) {case WLAN_EID_VENDOR_SPECIFIC :/* Get WMM IE. */
Greg Kroah-Hartman
maintainerPATCH 167/480 staging: rtl8723bs: fix OOB reads in is_ap_in_tkip() IE loop
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandru Hossu <[email protected]>
commit 3bf39f711ff27c64be8680a8938bcc5001982e81 upstream.
The loop in is_ap_in_tkip() iterates over IEs without verifying that enough bytes remain before dereferencing the IE header or its payload:
- pIE->element_id and pIE->length are read without checking that i + sizeof(*pIE) <= ie_length, so a truncated IE at the end of the buffer causes an OOB read.
- For WLAN_EID_VENDOR_SPECIFIC the code compares pIE->data + 12, which requires pIE->length >= 16. For WLAN_EID_RSN it compares pIE->data + 8, requiring pIE->length >= 12. Neither requirement is checked.
Add the missing IE header and payload bounds checks and guard each data access with an explicit pIE->length minimum, matching the pattern established in update_beacon_info().
--- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
drivers/staging/rtl8723bs/core/rtw_wlan_util.c +11 -3
@@ -1333,15 +1333,23 @@ unsigned int is_ap_in_tkip(struct adaptefor (i =sizeof (struct ndis_802_11_fix_ie ); i < pmlmeinfo->network.ie_length;) { pIE = (struct ndis_80211_var_ie *)(pmlmeinfo->network.ies + i); +if (i +sizeof (*pIE) > pmlmeinfo->network.ie_length) +break ; +if (i +sizeof (*pIE) + pIE->length > pmlmeinfo->network.ie_length) +break ; +switch (pIE->element_id) {case WLAN_EID_VENDOR_SPECIFIC : -if ((!memcmp (pIE->data,RTW_WPA_OUI ,4 )) && (!memcmp ((pIE->data +12 ),WPA_TKIP_CIPHER ,4 ))) +if (pIE->length >=16 && + !memcmp (pIE->data,RTW_WPA_OUI ,4 ) && + !memcmp ((pIE->data +12 ),WPA_TKIP_CIPHER ,4 ))return true;break ;case WLAN_EID_RSN : -if (!memcmp ((pIE->data +8 ),RSN_TKIP_CIPHER ,4 )) +if (pIE->length >=12 && + !memcmp ((pIE->data +8 ),RSN_TKIP_CIPHER ,4 ))return true;break ;
@@ -1349,7 +1357,7 @@ unsigned int is_ap_in_tkip(struct adaptebreak ; } - i += (pIE->length +2 ); + i +=sizeof (*pIE) + pIE->length; }return false;
Greg Kroah-Hartman
maintainerPATCH 168/480 staging: rtl8723bs: fix OOB write in HT_caps_handler()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandru Hossu <[email protected]>
commit f8001e1a516ba3b495728c65b61f799cbfad6bd0 upstream.
HT_caps_handler() iterates pIE->length bytes and writes into HT_caps.u.HT_cap[], which is a fixed 26-byte array (sizeof struct HT_caps_element). Because pIE->length is a raw u8 from an over-the-air 802.11 AssocResponse frame and is never validated, a malicious AP can set it up to 255, causing up to 229 bytes of out-of-bounds writes into adjacent fields of struct mlme_ext_info.
Truncate the iteration count to the size of HT_caps.u.HT_cap using umin() so that data from a longer-than-expected IE is silently ignored rather than written out of bounds, preserving interoperability with APs that pad the element. An early return on oversized IEs was considered but rejected: it would bypass the pmlmeinfo->HT_caps_enable = 1 assignment that precedes the loop, silently disabling HT mode for APs that append extra bytes to the HT Capabilities IE.
--- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/staging/rtl8723bs/core/rtw_wlan_util.c +2 -1
@@ -935,7 +935,8 @@ void HT_caps_handler(struct adapter *pad pmlmeinfo->HT_caps_enable =1 ; -for (i =0 ; i < (pIE->length); i++) { +for (i =0 ; i <umin (pIE->length, +sizeof (pmlmeinfo->HT_caps.u.HT_cap)); i++) {if (i !=2 ) {/* Commented by Albert 2010/07/12 */ /* Got the endian issue here. */
Greg Kroah-Hartman
maintainerPATCH 169/480 crypto: amlogic - avoid double cleanup in meson_crypto_probe()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dawei Feng <[email protected]>
commit 6d827ade51a24e18d81afb9f32756d339520a14c upstream.
When meson_allocate_chanlist() fails after a partial allocation, it already unwinds the allocated chanlist state through its local error path. meson_crypto_probe() then jump to error_flow and calls meson_free_chanlist() again, causing the same per-flow resources to be torn down twice. In the reproduced failure path, the second teardown re-entered crypto_engine_exit() on an already destroyed worker and KASAN reported a slab-use-after-free in kthread_destroy_worker().
Prevent double-free by handling partial allocation failures locally within meson_allocate_chanlist() and skipping the outer cleanup path.
The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available.
The bug was reproduced in a QEMU x86_64 guest booted with KASAN on v7.1, using the reproducer under tools/testing/meson_crypto_probe. The reproducer forces the second dma_alloc_attrs() call in the gxl-crypto probe path to return NULL, making meson_allocate_chanlist() fail after partial initialization. On the unpatched kernel this reliably triggered a slab-use-after-free. With this fix applied, the same reproducer no longer emits any KASAN report and the probe fails cleanly with -ENOMEM.
==================================================================
BUG: KASAN: slab-use-after-free in kthread_destroy_worker+0xb2/0xd0
Read of size 8 at addr ff1100010c057a68 by task insmod/265
CPU: 1 UID: 0 PID: 265 Comm: insmod Tainted: G O 7.1.0-rc2-00376-g810af9adc907-dirty #10 PREEMPT(lazy)
Tainted: [O]=OOT_MODULE
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0x68/0xa0
print_report+0xcb/0x5e0
? __virt_addr_valid+0x21d/0x3f0
? kthread_destroy_worker+0xb2/0xd0
? kthread_destroy_worker+0xb2/0xd0
kasan_report+0xca/0x100
? kthread_destroy_worker+0xb2/0xd0
kthread_destroy_worker+0xb2/0xd0
meson_crypto_probe+0x4d0/0xc10 [amlogic_gxl_crypto]
platform_probe+0x99/0x140
really_probe+0x1c6/0x6a0
? __pfx___device_attach_driver+0x10/0x10
__driver_probe_device+0x248/0x310
? acpi_driver_match_device+0xb0/0x100
driver_probe_device+0x48/0x210
? __pfx___device_attach_driver+0x10/0x10
__device_attach_driver+0x160/0x320
bus_for_each_drv+0x104/0x190
? __pfx_bus_for_each_drv+0x10/0x10
? _raw_spin_unlock_irqrestore+0x2c/0x50
__device_attach+0x19d/0x3b0
? __pfx___device_attach+0x10/0x10
? do_raw_spin_unlock+0x53/0x220
device_initial_probe+0x78/0xa0
bus_probe_device+0x5b/0x130
device_add+0xcfd/0x1430
? __pfx_device_add+0x10/0x10
? insert_resource+0x34/0x50
? lock_release+0xc9/0x290
platform_device_add+0x24e/0x590
? __pfx_meson_crypto_probe_repro_init+0x10/0x10 [meson_crypto_probe_repro]
meson_crypto_probe_repro_init+0x330/0xff0 [meson_crypto_probe_repro]
do_one_initcall+0xc0/0x450
? __pfx_do_one_initcall+0x10/0x10
? _raw_spin_unlock_irqrestore+0x2c/0x50
? __create_object+0x59/0x80
? kasan_unpoison+0x27/0x60
do_init_module+0x27b/0x7d0
? __pfx_do_init_module+0x10/0x10
? kasan_quarantine_put+0x84/0x1d0
? kfree+0x32c/0x510
? load_module+0x561e/0x5ff0
load_module+0x54fe/0x5ff0
? __pfx_load_module+0x10/0x10
? security_file_permission+0x20/0x40
? kernel_read_file+0x23d/0x6e0
? mmap_region+0x235/0x4a0
? __pfx_kernel_read_file+0x10/0x10
? __file_has_perm+0x2c0/0x3e0
init_module_from_file+0x158/0x180
? __pfx_init_module_from_file+0x10/0x10
? __lock_acquire+0x45a/0x1ba0
? idempotent_init_module+0x315/0x610
? lock_release+0xc9/0x290
? lockdep_init_map_type+0x4b/0x220
? do_raw_spin_unlock+0x53/0x220
idempotent_init_module+0x330/0x610
? __pfx_idempotent_init_module+0x10/0x10
? __pfx_cred_has_capability.isra.0+0x10/0x10
? ksys_mmap_pgoff+0x385/0x520
__x64_sys_finit_module+0xbe/0x120
do_syscall_64+0x115/0x690
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f7d6d31690d
Code: 5b 41 5c c3 66 0f 1f 84 00 00 00 00 00 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d f3 b4 0f 00 f7 d8 >
RSP: 002b:00007fffc027ac68 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
RAX: ffffffffffffffda RBX: 000055f7b81967c0 RCX: 00007f7d6d31690d
RDX: 0000000000000000 RSI: 000055f79a0d6cd2 RDI: 0000000000000003
RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000003 R11: 0000000000000246 R12: 000055f79a0d6cd2
R13: 000055f7b8196790 R14: 000055f79a0d5888 R15: 000055f7b81968e0
</TASK>--- drivers/crypto/amlogic/amlogic-gxl-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/crypto/amlogic/amlogic-gxl-core.c +1 -1
@@ -291,8 +291,8 @@ static int meson_crypto_probe(struct plareturn 0 ; error_alg:meson_unregister_algs (mc); -error_flow:meson_free_chanlist (mc,MAXFLOW -1 ); +error_flow:clk_disable_unprepare (mc->busclk);return err; }
Greg Kroah-Hartman
maintainerPATCH 170/480 crypto: krb5 - filter out async aead implementations at alloc
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 6c9dddeb582fde005360f4fe02c760d45ca05fb5 upstream.
krb5_aead_encrypt(), krb5_aead_decrypt() in rfc3961_simplified.c and rfc8009_encrypt(), rfc8009_decrypt() in rfc8009_aes2.c set a NULL completion callback and treat any negative return from crypto_aead_{encrypt,decrypt}() as terminal, falling through to kfree_sensitive(buffer). When the encrypt_name resolves to an async AEAD instance the request returns -EINPROGRESS, the buffer is freed while the backend's worker still holds a pointer, and the worker dereferences the freed slab on completion.
KASAN report under UML+SLUB with a synthetic async aead backend bound to krb5->encrypt_name:
BUG: KASAN: slab-use-after-free in t5_stub_complete+0x7d/0xc7
The helpers were written synchronously, so filter the async instances out at allocation time instead of plumbing crypto_wait_req() through every call site.
Reachable via net/rxrpc/rxgk.c, fs/afs/cm_security.c and net/ceph/crypto.c on systems with an async AEAD provider bound to the krb5 enctype name.
--- crypto/krb5/krb5_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
crypto/krb5/krb5_api.c +1 -1
@@ -207,7 +207,7 @@ struct crypto_aead *krb5_prepare_encryptstruct crypto_aead *ci =NULL ;int ret = -ENOMEM ; - ci =crypto_alloc_aead (krb5->encrypt_name,0 ,0 ); + ci =crypto_alloc_aead (krb5->encrypt_name,0 ,CRYPTO_ALG_ASYNC );if (IS_ERR (ci)) { ret =PTR_ERR (ci);if (ret == -ENOENT )
Greg Kroah-Hartman
maintainerPATCH 171/480 ksmbd: fix use-after-free of a deferred file_lock on SMB2_CLOSE then SMB2_CANCEL
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gil Portnoy <[email protected]>
commit 10f293a07f9e10e988b0ae44e2e99c631f5a68e0 upstream.
Commit f580d27e8928 ("ksmbd: fix use-after-free of a deferred file_lock on double SMB2_CANCEL") made smb2_cancel() skip a work whose state is KSMBD_WORK_CANCELLED, so its cancel_fn cannot be fired a second time. But KSMBD_WORK has three states (ACTIVE, CANCELLED, CLOSED), and the same freeing producer path is reached for CLOSED too:
SMB2_CLOSE on the locking handle -> set_close_state_blocked_works() sets the deferred work's state to KSMBD_WORK_CLOSED and wakes the smb2_lock() worker. The worker takes the non-ACTIVE early-exit, locks_free_lock()s the file_lock and, because the state is not KSMBD_WORK_CANCELLED, takes the STATUS_RANGE_NOT_LOCKED branch with "goto out2" -- which, like the cancelled branch, skips release_async_work(). The work stays on conn->async_requests with a live cancel_fn = smb2_remove_blocked_lock pointing at the freed file_lock.
A subsequent SMB2_CANCEL for the same AsyncId then passes the KSMBD_WORK_CANCELLED-only guard (its state is KSMBD_WORK_CLOSED), so smb2_cancel() fires cancel_fn again over the freed file_lock -- the same use-after-free fixed, via SMB2_CLOSE instead of a first SMB2_CANCEL:
BUG: KASAN: slab-use-after-free in __locks_delete_block
__locks_delete_block
locks_delete_block
ksmbd_vfs_posix_lock_unblock
smb2_remove_blocked_lock
smb2_cancel <- 2nd SMB2_CANCEL fires cancel_fn
handle_ksmbd_work
Allocated by ...: locks_alloc_lock <- smb2_lock
Freed by ...: locks_free_lock <- smb2_lock (non-ACTIVE early-exit)
... cache file_lock_cache of size 192Reproduced on mainline 7.1-rc7 (which already contains f580d27e8928) with KASAN by an authenticated SMB client; the double-SMB2_CANCEL control is silent on that kernel, so the splat is attributable to the CLOSE trigger.
Only an ACTIVE deferred work may have its cancel_fn fired: both terminal states (CANCELLED and CLOSED) reach the smb2_lock() early-exit that frees the file_lock and skips release_async_work(). Guard on KSMBD_WORK_ACTIVE so any non-active work is skipped.
--- fs/smb/server/smb2pdu.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
fs/smb/server/smb2pdu.c +7 -7
@@ -7334,14 +7334,14 @@ int smb2_cancel(struct ksmbd_work *work)continue ;/* -* A cancelled deferred byte-range lock frees its -* file_lock and takes the smb2_lock() early-exit that -* skips release_async_work(), so the work stays on -* conn->async_requests with a live cancel_fn pointing -* at the freed file_lock. Re-firing it on a second -* SMB2_CANCEL is a use-after-free. +* Only an ACTIVE deferred work may have its cancel_fn +* fired. A CANCELLED or CLOSED work already took the +* smb2_lock() non-ACTIVE early-exit that frees the +* file_lock and skips release_async_work(), so it is +* still on conn->async_requests with a live cancel_fn +* pointing at the freed file_lock. */ -if (iter->state ==KSMBD_WORK_CANCELLED ) +if (iter->state !=KSMBD_WORK_ACTIVE )break ; ksmbd_debug(SMB,
Greg Kroah-Hartman
maintainerPATCH 172/480 net: af_key: initialize alg_key_len for IPComp states
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zijing Yin <[email protected]>
commit d129c3177d7b1138fd5066fcc63a698b3ba415b0 upstream.
pfkey_msg2xfrm_state() handles the IPComp (SADB_X_SATYPE_IPCOMP) case by allocating x->calg and copying only the algorithm name:
x->calg = kmalloc_obj(*x->calg);
if (!x->calg) {
err = -ENOMEM;
goto out;
}
strcpy(x->calg->alg_name, a->name);
x->props.calgo = sa->sadb_sa_encrypt;Unlike the authentication (x->aalg) and encryption (x->ealg) branches of the same function, the compression branch never initializes calg->alg_key_len. IPComp carries no key and the allocation only reserves sizeof(struct xfrm_algo) (i.e. no room for a key), so the field is left containing uninitialized slab data.
calg->alg_key_len is later used as a length by xfrm_algo_clone() when an IPComp state is cloned during XFRM_MSG_MIGRATE:
xfrm_state_migrate() xfrm_state_clone_and_setup() x->calg = xfrm_algo_clone(orig->calg); kmemdup(orig, xfrm_alg_len(orig));
where xfrm_alg_len() returns sizeof(*alg) + (alg_key_len + 7) / 8. With a non-zero garbage alg_key_len, kmemdup() reads past the end of the 68-byte calg object. Adding an IPComp SA via PF_KEY and then migrating it triggers (net-next, KASAN, init_on_alloc=0):
BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x44/0x60 Read of size 4164 at addr ff11000025a74980 by task diag2/9287 CPU: 3 UID: 0 PID: 9287 Comm: diag2 7.1.0-rc6-g903db046d557 #1 Call Trace: <TASK> dump_stack_lvl+0x10e/0x1f0 print_report+0xf7/0x600 kasan_report+0xe4/0x120 kasan_check_range+0x105/0x1b0 __asan_memcpy+0x23/0x60 kmemdup_noprof+0x44/0x60 xfrm_state_migrate+0x70a/0x1da0 xfrm_migrate+0x753/0x18a0 xfrm_do_migrate+0xb47/0xf10 xfrm_user_rcv_msg+0x411/0xb50 netlink_rcv_skb+0x158/0x420 xfrm_netlink_rcv+0x71/0x90 netlink_unicast+0x584/0x850 netlink_sendmsg+0x8b0/0xdc0 ____sys_sendmsg+0x9f7/0xb90 ___sys_sendmsg+0x134/0x1d0 __sys_sendmsg+0x16d/0x220 do_syscall_64+0x116/0x7d0 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> Allocated by task 9287: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 pfkey_add+0x2652/0x2ea0 pfkey_process+0x6d0/0x830 pfkey_sendmsg+0x42c/0x850 __sys_sendto+0x461/0x4b0 __x64_sys_sendto+0xe0/0x1c0 do_syscall_64+0x116/0x7d0 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ff11000025a74980 which belongs to the cache kmalloc-96 of size 96 The buggy address is located 0 bytes inside of allocated 68-byte region [ff11000025a74980, ff11000025a749c4)
Depending on the uninitialized value the same field can instead request an oversized kmemdup() allocation and make the migration clone fail.
The XFRM netlink path is not affected: verify_one_alg() rejects an XFRMA_ALG_COMP attribute shorter than xfrm_alg_len(), so a calg added via XFRM_MSG_NEWSA is always self-consistent.
Initialize calg->alg_key_len to 0, matching the aalg/ealg branches.
--- net/key/af_key.c | 1 + 1 file changed, 1 insertion(+)
net/key/af_key.c +1 -0
@@ -1218,6 +1218,7 @@ static struct xfrm_state * pfkey_msg2xfr goto out; }strcpy (x->calg->alg_name, a->name); + x->calg->alg_key_len =0 ; x->props.calgo = sa->sadb_sa_encrypt; }else {int keysize =0 ;
Greg Kroah-Hartman
maintainerPATCH 173/480 audit: Fix data races of skb_queue_len() readers on audit_queue
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chi Wang <[email protected]>
commit c9a71daaecb2fb1d8c704545cc0b1c920b9bf5d7 upstream.
Multiple readers access audit_queue.qlen via skb_queue_len() without holding the queue lock or using READ_ONCE(), while kauditd writes to this field via the skb_dequeue() → __skb_unlink() path with WRITE_ONCE() protected by a spinlock. This constitutes data races.
All affected skb_queue_len(&audit_queue) call sites:
- kauditd_thread() wait_event_freezable() condition
- audit_receive_msg() AUDIT_GET handler (s.backlog assignment)
- audit_receive() backlog check
- audit_log_start() backlog check and pr_warn()
KCSAN reports the following conflicting access pattern (one example):
==================================================================
BUG: KCSAN: data-race in audit_log_start / skb_dequeue
write (marked) to 0xffffffff8512ee20 of 4 bytes by task 661 on cpu 57:
skb_dequeue+0x70/0xf0 kauditd_send_queue+0x71/0x220 kauditd_thread+0x1cb/0x430 kthread+0x1c2/0x210 ret_from_fork+0x162/0x1a0 ret_from_fork_asm+0x1a/0x30
read to 0xffffffff8512ee20 of 4 bytes by task 36586 on cpu 1:
audit_log_start+0x2a0/0x6b0 audit_core_dumps+0x64/0xa0 do_coredump+0x14b/0x1260 get_signal+0xeb2/0xf70 arch_do_signal_or_restart+0x41/0x170 exit_to_user_mode_loop+0xa2/0x1c0 do_syscall_64+0x1a3/0x1c0 entry_SYSCALL_64_after_hwframe+0x76/0xe0
value changed: 0x00000001 -> 0x00000000
==================================================================
Resolve the race by switching to lockless helper skb_queue_len_lockless(), which internally uses READ_ONCE() and properly pairs with the WRITE_ONCE() write accesses already present on the writer side.
--- kernel/audit.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
kernel/audit.c +5 -5
@@ -946,7 +946,7 @@ main_queue:* do the multicast send and rotate records from the * main queue to the retry/hold queues */ wait_event_freezable (kauditd_wait, - (skb_queue_len (&audit_queue) ?1 :0 )); + (skb_queue_len_lockless (&audit_queue) ?1 :0 )); }return 0 ;
@@ -1279,7 +1279,7 @@ static int audit_receive_msg(struct sk_b s.rate_limit = audit_rate_limit; s.backlog_limit = audit_backlog_limit; s.lost =atomic_read (&audit_lost); - s.backlog =skb_queue_len (&audit_queue); + s.backlog =skb_queue_len_lockless (&audit_queue); s.feature_bitmap =AUDIT_FEATURE_BITMAP_ALL ; s.backlog_wait_time = audit_backlog_wait_time; s.backlog_wait_time_actual =atomic_read (&audit_backlog_wait_time_actual);
@@ -1622,7 +1622,7 @@ static void audit_receive(struct sk_buff/* can't block with the ctrl lock, so penalize the sender now */ if (audit_backlog_limit && - (skb_queue_len (&audit_queue) > audit_backlog_limit)) { + (skb_queue_len_lockless (&audit_queue) > audit_backlog_limit)) {DECLARE_WAITQUEUE (wait, current);/* wake kauditd to try and flush the queue */
@@ -1928,7 +1928,7 @@ struct audit_buffer *audit_log_start(strlong stime = audit_backlog_wait_time;while (audit_backlog_limit && - (skb_queue_len (&audit_queue) > audit_backlog_limit)) { + (skb_queue_len_lockless (&audit_queue) > audit_backlog_limit)) {/* wake kauditd to try and flush the queue */ wake_up_interruptible (&kauditd_wait);
@@ -1948,7 +1948,7 @@ struct audit_buffer *audit_log_start(str }else {if (audit_rate_check () &&printk_ratelimit ())pr_warn ("audit_backlog=%d > audit_backlog_limit=%d\n" , -skb_queue_len (&audit_queue), +skb_queue_len_lockless (&audit_queue), audit_backlog_limit);audit_log_lost ("backlog limit exceeded" );return NULL ;
Greg Kroah-Hartman
maintainerPATCH 174/480 Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marco Elver <[email protected]>
commit b66774b48dd98f07254951f74ea6f513efe7ff8b upstream.
l2cap_chan_timeout() runs asynchronously and accesses chan->conn. If the connection is torn down while the timer is running or pending, chan->conn can be freed, leading to a use-after-free when the timer worker attempts to lock conn->lock:
| BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline] | BUG: KASAN: slab-use-after-free in atomic_long_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:4456 [inline] | BUG: KASAN: slab-use-after-free in __mutex_trylock_fast kernel/locking/mutex.c:161 [inline] | BUG: KASAN: slab-use-after-free in mutex_lock+0x4f/0xa0 kernel/locking/mutex.c:318 | Write of size 8 at addr ffff8881298d9550 by task kworker/2:1/83 | | CPU: 2 UID: 0 PID: 83 Comm: kworker/2:1 Not tainted 7.1.0-rc6-next-20260601-dirty #6 PREEMPT(full) | Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014 | Workqueue: events l2cap_chan_timeout | Call Trace: | <TASK> | instrument_atomic_read_write include/linux/instrumented.h:112 [inline] | atomic_long_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:4456 [inline] | __mutex_trylock_fast kernel/locking/mutex.c:161 [inline] | mutex_lock+0x4f/0xa0 kernel/locking/mutex.c:318 | l2cap_chan_timeout+0x5d/0x1b0 net/bluetooth/l2cap_core.c:422 | process_one_work kernel/workqueue.c:3326 [inline] | process_scheduled_works+0x7c8/0xfb0 kernel/workqueue.c:3409 | worker_thread+0x8a9/0xcf0 kernel/workqueue.c:3490 | kthread+0x346/0x430 kernel/kthread.c:436 | ret_from_fork+0x1a3/0x470 arch/x86/kernel/process.c:158 | ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 | </TASK> | | Allocated by task 320: | l2cap_conn_add+0xa7/0x820 net/bluetooth/l2cap_core.c:7075 | l2cap_connect_cfm+0xdb/0xd70 net/bluetooth/l2cap_core.c:7452 | hci_connect_cfm include/net/bluetooth/hci_core.h:2139 [inline] | hci_remote_features_evt+0x52f/0x9f0 net/bluetooth/hci_event.c:3760 | hci_event_func net/bluetooth/hci_event.c:7796 [inline] | hci_event_packet+0x561/0xa70 net/bluetooth/hci_event.c:7847 | hci_rx_work+0x370/0x890 net/bluetooth/hci_core.c:4040 | process_one_work kernel/workqueue.c:3326 [inline] | process_scheduled_works+0x7c8/0xfb0 kernel/workqueue.c:3409 | worker_thread+0x8a9/0xcf0 kernel/workqueue.c:3490 | kthread+0x346/0x430 kernel/kthread.c:436 | ret_from_fork+0x1a3/0x470 arch/x86/kernel/process.c:158 | ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 | | Freed by task 322: | hci_disconn_cfm include/net/bluetooth/hci_core.h:2154 [inline] | hci_conn_hash_flush+0x101/0x1f0 net/bluetooth/hci_conn.c:2736 | hci_dev_close_sync+0x889/0xde0 net/bluetooth/hci_sync.c:5405 | hci_dev_do_close net/bluetooth/hci_core.c:502 [inline] | hci_unregister_dev+0x1f7/0x370 net/bluetooth/hci_core.c:2679 | vhci_release+0x12a/0x180 drivers/bluetooth/hci_vhci.c:690 | __fput+0x369/0x890 fs/file_table.c:510 | task_work_run+0x160/0x1d0 kernel/task_work.c:233 | get_signal+0xf5b/0x1120 kernel/signal.c:2810 | arch_do_signal_or_restart+0x4d/0x600 arch/x86/kernel/signal.c:337 | __exit_to_user_mode_loop kernel/entry/common.c:64 [inline] | exit_to_user_mode_loop+0x85/0x510 kernel/entry/common.c:98 | do_syscall_64+0x263/0x3d0 arch/x86/entry/syscall_64.c:100 | entry_SYSCALL_64_after_hwframe+0x77/0x7f | | The buggy address belongs to the object at ffff8881298d9400 | which belongs to the cache kmalloc-512 of size 512 | The buggy address is located 336 bytes inside of | freed 512-byte region [ffff8881298d9400, ffff8881298d9600)
Fix it by having chan->conn hold a reference to l2cap_conn (via l2cap_conn_get) when the channel is added to the connection, and releasing it in the channel destructor. This ensures the l2cap_conn remains alive as long as the channel exists.
A new FLAG_DEL channel flag is introduced to indicate that the channel has been deleted from its connection. l2cap_chan_del() atomically sets this flag using test_and_set_bit() instead of setting chan->conn to NULL. All asynchronous workers (l2cap_chan_timeout, l2cap_ack_timeout, l2cap_monitor_timeout, l2cap_retrans_timeout) and l2cap_chan_send() check FLAG_DEL to determine whether the channel has been torn down, rather than testing chan->conn for NULL.
--- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 34 ++++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 14 deletions(-)
include/net/bluetooth/l2cap.h +1 -0
@@ -748,6 +748,7 @@ enum {FLAG_ECRED_CONN_REQ_SENT ,FLAG_PENDING_SECURITY ,FLAG_HOLD_HCI_CONN , + FLAG_DEL, };/* Lock nesting levels for L2CAP channels. We need these because lockdep
net/bluetooth/l2cap_core.c +20 -14
@@ -411,7 +411,7 @@ static void l2cap_chan_timeout(struct woBT_DBG ("chan %p state %s" , chan,state_to_string (chan->state)); -if (!conn) { +if (test_bit (FLAG_DEL , &chan->flags)) {l2cap_chan_put (chan);return ; }
@@ -422,6 +422,9 @@ static void l2cap_chan_timeout(struct wo*/ l2cap_chan_lock (chan); +if (test_bit (FLAG_DEL , &chan->flags)) + goto unlock; +if (chan->state ==BT_CONNECTED || chan->state ==BT_CONFIG ) reason =ECONNREFUSED ; else if (chan->state == BT_CONNECT &&
@@ -434,10 +437,10 @@ static void l2cap_chan_timeout(struct wo chan->ops->close (chan); +unlock:l2cap_chan_unlock (chan); -l2cap_chan_put (chan); -mutex_unlock (&conn->lock); +l2cap_chan_put (chan); }struct l2cap_chan *l2cap_chan_create (void )
@@ -490,6 +493,9 @@ static void l2cap_chan_destroy(struct krlist_del (&chan->global_l);write_unlock (&chan_list_lock); +if (chan->conn) +l2cap_conn_put (chan->conn); +kfree (chan); }
@@ -593,7 +599,7 @@ void __l2cap_chan_add(struct l2cap_conn conn->disc_reason =HCI_ERROR_REMOTE_USER_TERM ; - chan->conn = conn; + chan->conn =l2cap_conn_get (conn);switch (chan->chan_type) {case L2CAP_CHAN_CONN_ORIENTED :
@@ -648,30 +654,26 @@ void l2cap_chan_add(struct l2cap_conn *cvoid l2cap_chan_del (struct l2cap_chan *chan,int err) { -struct l2cap_conn *conn = chan->conn; -__clear_chan_timer (chan); -BT_DBG ("chan %p, conn %p, err %d, state %s" , chan, conn, err, +BT_DBG ("chan %p, err %d, state %s" , chan, err,state_to_string (chan->state)); chan->ops->teardown (chan, err); -if (conn) { +if (!test_and_set_bit (FLAG_DEL , &chan->flags)) {/* Delete from channel list */ list_del (&chan->list);l2cap_chan_put (chan); - chan->conn =NULL ; -/* Reference was only held for non-fixed channels or * fixed channels that explicitly requested it using the * FLAG_HOLD_HCI_CONN flag. */ if (chan->chan_type !=L2CAP_CHAN_FIXED ||test_bit (FLAG_HOLD_HCI_CONN , &chan->flags)) -hci_conn_drop (conn->hcon); +hci_conn_drop (chan->conn->hcon); }if (test_bit (CONF_NOT_COMPLETE , &chan->conf_state))
@@ -1903,7 +1905,7 @@ static void l2cap_monitor_timeout(structl2cap_chan_lock (chan); -if (!chan->conn) { +if (test_bit (FLAG_DEL , &chan->flags)) {l2cap_chan_unlock (chan);l2cap_chan_put (chan);return ;
@@ -1924,7 +1926,7 @@ static void l2cap_retrans_timeout(structl2cap_chan_lock (chan); -if (!chan->conn) { +if (test_bit (FLAG_DEL , &chan->flags)) {l2cap_chan_unlock (chan);l2cap_chan_put (chan);return ;
@@ -2565,7 +2567,7 @@ int l2cap_chan_send(struct l2cap_chan *cint err;struct sk_buff_head seg_queue; -if (!chan->conn) +if (test_bit (FLAG_DEL , &chan->flags))return -ENOTCONN ;/* Connectionless channel */
@@ -3160,12 +3162,16 @@ static void l2cap_ack_timeout(struct worl2cap_chan_lock (chan); +if (test_bit (FLAG_DEL , &chan->flags)) + goto unlock; + frames_to_ack =__seq_offset (chan, chan->buffer_seq, chan->last_acked_seq);if (frames_to_ack)l2cap_send_rr_or_rnr (chan,0 ); +unlock:l2cap_chan_unlock (chan);l2cap_chan_put (chan); }
Greg Kroah-Hartman
maintainerPATCH 175/480 Bluetooth: MGMT: Fix UAF of hci_conn_params in add_device_complete
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Samuel Page <[email protected]>
commit fa85d985f614bc3feb343000f14a1072e99b0df1 upstream.
add_device_complete() runs from the hci_cmd_sync_work kworker, which holds only hci_req_sync_lock and *not* hci_dev_lock. It calls hci_conn_params_lookup() and then dereferences the returned object (params->flags) without taking hci_dev_lock:
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, le_addr_type(cp->addr.type)); ... device_flags_changed(NULL, hdev, &cp->addr.bdaddr, cp->addr.type, hdev->conn_flags, params ? params->flags : 0);
hci_conn_params_lookup() walks hdev->le_conn_params and is documented to require hdev->lock. A concurrent MGMT_OP_REMOVE_DEVICE (remove_device()), which does run under hci_dev_lock, can call hci_conn_params_free() to list_del() and kfree() the very object the lookup returned, so the subsequent params->flags read touches freed memory [0].
Hold hci_dev_lock() across the hci_conn_params_lookup() and the read of params->flags (and the matching event emission) so the lookup result cannot be freed by a concurrent remove_device() before it is used, honouring the locking contract of hci_conn_params_lookup().
[0]: (trailing page/memory-state dump trimmed)
BUG: KASAN: slab-use-after-free in add_device_complete+0x358/0x3d8 net/bluetooth/mgmt.c:7671 Read of size 1 at addr ffff000017ab26c1 by task kworker/u9:8/388 CPU: 1 UID: 0 PID: 388 Comm: kworker/u9:8 Not tainted 7.0.11 #20 PREEMPT Hardware name: linux,dummy-virt (DT) Workqueue: hci0 hci_cmd_sync_work Call trace: show_stack+0x2c/0x3c arch/arm64/kernel/stacktrace.c:499 (C) __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0xb4/0xd4 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0x118/0x5d8 mm/kasan/report.c:482 kasan_report+0xb0/0xf4 mm/kasan/report.c:595 __asan_report_load1_noabort+0x20/0x2c mm/kasan/report_generic.c:378 add_device_complete+0x358/0x3d8 net/bluetooth/mgmt.c:7671 hci_cmd_sync_work+0x14c/0x240 net/bluetooth/hci_sync.c:334 process_one_work+0x628/0xd38 kernel/workqueue.c:3289 process_scheduled_works kernel/workqueue.c:3372 [inline] worker_thread+0x7a8/0xac0 kernel/workqueue.c:3453 kthread+0x39c/0x444 kernel/kthread.c:436 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:860 Allocated by task 3401: kasan_save_stack+0x3c/0x64 mm/kasan/common.c:57 kasan_save_track+0x20/0x3c mm/kasan/common.c:78 kasan_save_alloc_info+0x40/0x54 mm/kasan/generic.c:570 poison_kmalloc_redzone mm/kasan/common.c:398 [inline] __kasan_kmalloc+0xd4/0xd8 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 [inline] __kmalloc_cache_noprof+0x1b0/0x458 mm/slub.c:5385 kmalloc_noprof include/linux/slab.h:950 [inline] kzalloc_noprof include/linux/slab.h:1188 [inline] hci_conn_params_add+0x10c/0x4b0 net/bluetooth/hci_core.c:2279 hci_conn_params_set net/bluetooth/mgmt.c:5162 [inline] add_device+0x5b4/0xa54 net/bluetooth/mgmt.c:7755 hci_mgmt_cmd net/bluetooth/hci_sock.c:1721 [inline] hci_sock_sendmsg+0x10b4/0x1dd0 net/bluetooth/hci_sock.c:1841 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg+0xe0/0x128 net/socket.c:742 sock_write_iter+0x250/0x390 net/socket.c:1195 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x66c/0xab0 fs/read_write.c:688 ksys_write+0x1fc/0x24c fs/read_write.c:740 __do_sys_write fs/read_write.c:751 [inline] __se_sys_write fs/read_write.c:748 [inline] __arm64_sys_write+0x70/0xa4 fs/read_write.c:748 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline] invoke_syscall+0x84/0x2a8 arch/arm64/kernel/syscall.c:49 el0_svc_common.constprop.0+0xe4/0x294 arch/arm64/kernel/syscall.c:132 do_el0_svc+0x44/0x5c arch/arm64/kernel/syscall.c:151 el0_svc+0x38/0xac arch/arm64/kernel/entry-common.c:724 el0t_64_sync_handler+0xa0/0xe4 arch/arm64/kernel/entry-common.c:743 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:596 Freed by task 3740: kasan_save_stack+0x3c/0x64 mm/kasan/common.c:57 kasan_save_track+0x20/0x3c mm/kasan/common.c:78 kasan_save_free_info+0x4c/0x74 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 [inline] __kasan_slab_free+0x88/0xb8 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2685 [inline] slab_free mm/slub.c:6170 [inline] kfree+0x14c/0x458 mm/slub.c:6488 hci_conn_params_free+0x288/0x484 net/bluetooth/hci_core.c:2312 remove_device+0x4b0/0x968 net/bluetooth/mgmt.c:7919 hci_mgmt_cmd net/bluetooth/hci_sock.c:1721 [inline] hci_sock_sendmsg+0x10b4/0x1dd0 net/bluetooth/hci_sock.c:1841 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg+0xe0/0x128 net/socket.c:742 sock_write_iter+0x250/0x390 net/socket.c:1195 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x66c/0xab0 fs/read_write.c:688 ksys_write+0x1fc/0x24c fs/read_write.c:740 __do_sys_write fs/read_write.c:751 [inline] __se_sys_write fs/read_write.c:748 [inline] __arm64_sys_write+0x70/0xa4 fs/read_write.c:748 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline] invoke_syscall+0x84/0x2a8 arch/arm64/kernel/syscall.c:49 el0_svc_common.constprop.0+0xe4/0x294 arch/arm64/kernel/syscall.c:132 do_el0_svc+0x44/0x5c arch/arm64/kernel/syscall.c:151 el0_svc+0x38/0xac arch/arm64/kernel/entry-common.c:724 el0t_64_sync_handler+0xa0/0xe4 arch/arm64/kernel/entry-common.c:743 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:596
--- net/bluetooth/mgmt.c | 3 +++ 1 file changed, 3 insertions(+)
net/bluetooth/mgmt.c +3 -0
@@ -7641,6 +7641,8 @@ static void add_device_complete(struct hif (!err) {struct hci_conn_params *params; +hci_dev_lock (hdev); + params =hci_conn_params_lookup (hdev, &cp->addr.bdaddr,le_addr_type (cp->addr.type));
@@ -7649,6 +7651,7 @@ static void add_device_complete(struct hdevice_flags_changed (NULL , hdev, &cp->addr.bdaddr, cp->addr.type, hdev->conn_flags, params ? params->flags :0 ); +hci_dev_unlock (hdev); } mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_ADD_DEVICE,
Greg Kroah-Hartman
maintainerPATCH 176/480 coresight: etb10: restore atomic_t for shared reading state
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
commit fa09f08ede3db3050ae16ae1ed92c902d0cada23 upstream.
The etb10 miscdevice uses drvdata->reading as a shared exclusivity gate for userspace buffer access. etb_open() claims that gate with local_cmpxchg(), and etb_release() clears it with local_set().
That gate is shared per-device state rather than CPU-local state. A running system can reach it whenever /dev/<etb> is opened, closed, and reopened by different tasks while the device remains registered, so the same drvdata->reading variable may be claimed on one CPU and later cleared on another.
This code used to use atomic_t for the same gate, but commit 27b10da8fff2 ("coresight: etb10: moving to local atomic operations") changed it to local_t even though the access pattern remained cross-task and cross-CPU. Restore atomic_t together with atomic_cmpxchg() and atomic_set() so the exclusivity gate again uses a primitive intended for shared state.
The issue was found on Linux v6.18.21 by our static analysis tool while scanning surviving local_t-on-shared-state sites, and then manually reviewed against the live etb10 file-op path.
It was runtime-validated with a reproducible QEMU no-device KCSAN PoC that kept the same report-local contract:
1. use one shared struct etb_drvdata carrier and its
drvdata->reading gate;
2. call etb_open() and etb_release() sequentially on that gate to
confirm the original claim/clear path;
3. bind the open side to CPU0 and the release side to CPU1 for the
same gate to show cross-CPU ownership; 4. run bound workers that repeatedly race etb_open() and etb_release() on the same gate until KCSAN reports a target hit.
The harness recorded:
L1 passed open=1 release=1
reading_after_open=1 reading_after_release=0
L2 passed open_cpu=0 release_cpu=1
cross_cpu_release=1 reading_after=0 open_ret=0
Representative KCSAN excerpt from the no-device validation run:
BUG: KCSAN: data-race in etb_open.constprop.0.isra.0 [vuln_msv]
write to 0xffffffffc0003810 of 4 bytes by task 216 on cpu 1:
etb_open.constprop.0.isra.0+0x38/0x80 [vuln_msv] l3_worker_thread_fn+0x4f/0xf0 [vuln_msv] kthread+0x17e/0x1c0 ret_from_fork+0x22/0x30
read to 0xffffffffc0003810 of 4 bytes by task 215 on cpu 0:
etb_open.constprop.0.isra.0+0x18/0x80 [vuln_msv] l3_worker_thread_fn+0x4f/0xf0 [vuln_msv] kthread+0x17e/0x1c0 ret_from_fork+0x22/0x30
value changed: 0x00000000 -> 0x00000001
Reported by Kernel Concurrency Sanitizer on:
CPU: 0 PID: 215 Comm: etb10_l3_a Tainted: G O 6.1.66 #2
This no-device harness is not a real ETB10 hardware end-to-end run, but it preserves the same shared drvdata->reading gate and the same etb_open()/etb_release() claim/clear contract. No real ETB10 hardware was available for runtime testing.
Build-tested with:
make olddefconfig
make -j"$(nproc)" drivers/hwtracing/coresight/coresight-etb10.o
--- drivers/hwtracing/coresight/coresight-etb10.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/hwtracing/coresight/coresight-etb10.c +3 -3
@@ -85,7 +85,7 @@ struct etb_drvdata {struct coresight_device *csdev;struct miscdevice miscdev;raw_spinlock_t spinlock; -local_t reading; +atomic_t reading;pid_t pid;u8 *buf;u32 buffer_depth;
@@ -603,7 +603,7 @@ static int etb_open(struct inode *inode,struct etb_drvdata *drvdata =container_of (file->private_data, struct etb_drvdata, miscdev); -if (local_cmpxchg (&drvdata->reading,0 ,1 )) +if (atomic_cmpxchg (&drvdata->reading,0 ,1 ))return -EBUSY ;dev_dbg (&drvdata->csdev->dev,"%s: successfully opened\n" , __func__);
@@ -641,7 +641,7 @@ static int etb_release(struct inode *ino {struct etb_drvdata *drvdata =container_of (file->private_data, struct etb_drvdata, miscdev); -local_set (&drvdata->reading,0 ); +atomic_set (&drvdata->reading,0 );dev_dbg (&drvdata->csdev->dev,"%s: released\n" , __func__);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 177/480 debugobjects: Plug race against a concurrent OOM disable
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Gleixner <[email protected]>
commit b81dde13cc163450dcb402dcc915ef13ba241e01 upstream.
syzbot reported a puzzling splat:
WARNING: kernel/time/hrtimer.c:443 at stub_timer+0xa/0x20
stub_timer() is installed as timer callback function in hrtimer_fixup_assert_init(), which is invoked when debug_object_assert_init() can't find a shadow object. In that case debug objects emits a warning about it before invoking the fixup.
Though the provided console log lacks this warning and instead has the following a few seconds before the splat:
ODEBUG: Out of memory. ODEBUG disabled
So the object was looked up in debug_object_assert_init() and the lookup failed due a concurrent out of memory situation which disabled debug objects and freed the shadow objects:
debug_object_assert_init()
if (!debug_objects_enabled)
return; obj = alloc();
if (!obj) {
// Out of memory
debug_objects_enabled = false;
free_objects();
obj = lookup_or_alloc();
// The lookup failed because the other side
// removed the objects, so this returns
// an error code as the object in question
// is not statically initialized
if (!IS_ERR_OR_NULL(obj))
return;
if (!obj) {
debug_oom();
return;
}
print(...)
if (!debug_objects_enabled)
return;
fixup(...)The debug object splat is skipped because debug_objects_enabled is false, but the fixup callback is invoked unconditionally, which makes the timer disfunctional.
This is only a problem in debug_object_assert_init() and debug_object_activate() as both have to handle statically initialized objects and therefore must handle the error pointer return case gracefully. All other places only handle the found/not found case and the NULL pointer return is a signal for OOM. Otherwise they get a valid shadow object.
Plug the hole by checking whether debug objects are still enabled before invoking the print and fixup function in those two places.
--- lib/debugobjects.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
lib/debugobjects.c +17 -0
@@ -894,6 +894,14 @@ int debug_object_activate(void *addr, co }raw_spin_unlock_irqrestore (&db->lock, flags); + +/* +* lookup_object_or_alloc() might have raced with a concurrent +* allocation failure which disabled debug objects. +*/ +if (!debug_objects_enabled) +return 0 ; +debug_print_object (&o,"activate" );switch (o.state) {
@@ -1071,6 +1079,15 @@ void debug_object_assert_init(void *addrreturn ; } +/* +* lookup_object_or_alloc() might have raced with a concurrent +* allocation failure which disabled debug objects. Don't run the fixup +* as it might turn a valid object useless. See for example +* hrtimer_fixup_assert_init(). +*/ +if (!debug_objects_enabled) +return ; +/* Object is neither tracked nor static. It's not initialized. */ debug_print_object (&o,"assert_init" );debug_object_fixup (descr->fixup_assert_init, addr,ODEBUG_STATE_NOTAVAILABLE );
Greg Kroah-Hartman
maintainerPATCH 178/480 fs/ntfs3: validate Dirty Page Table capacity in log_replay copy_lcns
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yunpeng Tian <[email protected]>
commit 57382ec6ac63b63dce2789e835fded28b698ae79 upstream.
In the analysis pass of $LogFile journal replay, log_replay() copies LCNs from each action log record into an existing Dirty Page Table (DPT) entry without bounding the destination index. A crafted NTFS image with DPT entry lcns_follow=1 and an action log record with lcns_follow=2 produces a kernel slab out-of-bounds write at mount time:
BUG: KASAN: slab-out-of-bounds in log_replay+0x654c/0xdb60 Write of size 8 at addr ffff8880095e1040 by task mount
Two attacker-controlled fields can drive j+i past the allocated page_lcns[] array:
1. dp->lcns_follow (capacity) can be smaller than lrh->lcns_follow.
2. lrh->target_vcn may be smaller than dp->vcn, making the u64 subtraction wrap to a huge size_t.
Validate target VCN delta and per-record LCN count against the DPT entry capacity, bail via the existing out: cleanup label with -EINVAL.
This mirrors the bounds-check pattern added in commit b2bc7c44ed17 ("fs/ntfs3: Fix slab-out-of-bounds read in DeleteIndexEntryRoot") and commit 0ca0485e4b2e ("fs/ntfs3: validate rec->used in journal-replay file record check").
--- fs/ntfs3/fslog.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-)
fs/ntfs3/fslog.c +15 -5
@@ -4546,11 +4546,21 @@ copy_lcns:* whole routine a loop, case Lcns do not fit below. */ t16 =le16_to_cpu (lrh->lcns_follow); -for (i =0 ; i < t16; i++) { -size_t j = (size_t )(le64_to_cpu (lrh->target_vcn) - -le64_to_cpu (dp->vcn)); - dp->page_lcns[j + i] = lrh->page_lcns[i]; - } + t32 =le32_to_cpu (dp->lcns_follow); +if (le64_to_cpu (lrh->target_vcn) <le64_to_cpu (dp->vcn)) { + err = -EINVAL ; + goto out; + } + +for (i =0 ; i < t16; i++) { +size_t j = (size_t )(le64_to_cpu (lrh->target_vcn) - +le64_to_cpu (dp->vcn)); +if (j >= t32 || i >= t32 - j) { + err = -EINVAL ; + goto out; + } + dp->page_lcns[j + i] = lrh->page_lcns[i]; + } goto next_log_record_analyze;
Greg Kroah-Hartman
maintainerPATCH 179/480 NTB: epf: Avoid calling pci_irq_vector() from hardirq context
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
commit 4dcddc1c794d1c65eda68f1f8dd04a0fecc0870f upstream.
ntb_epf_vec_isr() calls pci_irq_vector() in hardirq context to derive the vector number. pci_irq_vector() calls msi_get_virq() that takes a mutex and can therefore trigger "scheduling while atomic" splats:
BUG: scheduling while atomic: kworker/u33:0/55/0x00010001 ... Call trace: ... schedule+0x38/0x110 schedule_preempt_disabled+0x28/0x50 __mutex_lock.constprop.0+0x848/0x908 __mutex_lock_slowpath+0x18/0x30 mutex_lock+0x4c/0x60 msi_domain_get_virq+0xe8/0x138 pci_irq_vector+0x2c/0x60 ntb_epf_vec_isr+0x28/0x120 [ntb_hw_epf] __handle_irq_event_percpu+0x70/0x3a8 handle_irq_event+0x48/0x100 handle_edge_irq+0x100/0x1c8 ...
Cache the Linux IRQ number for vector 0 when vectors are allocated and use it as a base in the ISR. Running the ISR in a threaded IRQ handler would also avoid the problem, but that would be unnecessary here.
--- drivers/ntb/hw/epf/ntb_hw_epf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/ntb/hw/epf/ntb_hw_epf.c +3 -1
@@ -92,6 +92,7 @@ struct ntb_epf_dev {int db_val;u64 db_valid_mask; +int irq_base; };#define ntb_ndev (__ntb) container_of(__ntb, struct ntb_epf_dev, ntb)
@@ -318,7 +319,7 @@ static irqreturn_t ntb_epf_vec_isr(int istruct ntb_epf_dev *ndev = dev;int irq_no; - irq_no = irq -pci_irq_vector (ndev->ntb.pdev,0 ); + irq_no = irq - ndev->irq_base; ndev->db_val = irq_no +1 ;if (irq_no ==0 )
@@ -350,6 +351,7 @@ static int ntb_epf_init_isr(struct ntb_e argument &= ~MSIX_ENABLE ; } + ndev->irq_base =pci_irq_vector (pdev,0 );for (i =0 ; i < irq; i++) { ret =request_irq (pci_irq_vector (pdev, i), ntb_epf_vec_isr,0 ,"ntb_epf" , ndev);
Greg Kroah-Hartman
maintainerPATCH 180/480 gpio: eic-sprd: use raw_spinlock_t in the irq startup path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
commit 90f0109019e6817eb40a486671b7722d1544ae29 upstream.
sprd_eic_irq_unmask() enables the GPIO IRQ and then updates controller state through sprd_eic_update(), which takes sprd_eic->lock with spin_lock_irqsave(). The callback can be reached from irq_startup() while setting up a requested IRQ. That path is not sleepable, but on PREEMPT_RT a regular spinlock_t becomes a sleeping lock.
This issue was found by our static analysis tool and then manually reviewed against the current tree.
The grounded PoC kept the request_threaded_irq() -> __setup_irq() -> irq_startup() -> sprd_eic_irq_unmask() -> sprd_eic_update() carrier and used the original spin_lock_irqsave(&sprd_eic->lock) edge. Lockdep reported:
BUG: sleeping function called from invalid context hardirqs last disabled at ... __setup_irq.constprop.0 ... [vuln_msv] sprd_rt_spin_lock_irqsave+0x1c/0x30 [vuln_msv] sprd_eic_update.constprop.0+0x48/0x90 [vuln_msv] sprd_eic_irq_unmask.constprop.0+0x35/0x50 [vuln_msv] __setup_irq.constprop.0+0xd/0x30 [vuln_msv]
Convert the Spreadtrum EIC controller lock to raw_spinlock_t. The locked section only serializes MMIO register updates and does not contain sleepable operations, so keeping it non-sleeping is appropriate for the irqchip callbacks.
--- drivers/gpio/gpio-eic-sprd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/gpio/gpio-eic-sprd.c +4 -4
@@ -95,7 +95,7 @@ struct sprd_eic {struct notifier_block irq_nb;void __iomem *base[SPRD_EIC_MAX_BANK ];enum sprd_eic_type type; -spinlock_t lock; +raw_spinlock_t lock;int irq; };
@@ -149,7 +149,7 @@ static void sprd_eic_update(struct gpio_unsigned long flags;u32 tmp; -spin_lock_irqsave (&sprd_eic->lock, flags); +raw_spin_lock_irqsave (&sprd_eic->lock, flags); tmp =readl_relaxed (base + reg);if (val)
@@ -158,7 +158,7 @@ static void sprd_eic_update(struct gpio_ tmp &= ~BIT (SPRD_EIC_BIT (offset));writel_relaxed (tmp, base + reg); -spin_unlock_irqrestore (&sprd_eic->lock, flags); +raw_spin_unlock_irqrestore (&sprd_eic->lock, flags); }static int sprd_eic_read (struct gpio_chip *chip,unsigned int offset,u16 reg)
@@ -628,7 +628,7 @@ static int sprd_eic_probe(struct platforif (!sprd_eic)return -ENOMEM ; -spin_lock_init (&sprd_eic->lock); +raw_spin_lock_init (&sprd_eic->lock); sprd_eic->type = pdata->type; sprd_eic->irq =platform_get_irq (pdev,0 );
Greg Kroah-Hartman
maintainerPATCH 181/480 gpio: sch: use raw_spinlock_t in the irq startup path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
commit 286533cb14a3c8a8bd39ff64ea2fc8e1aa0f638b upstream.
sch_irq_unmask() enables the GPIO IRQ and then updates the controller state through sch_irq_mask_unmask(), which takes sch->lock with spin_lock_irqsave(). The callback can be reached from irq_startup() while setting up a requested IRQ. That path is not sleepable, but on PREEMPT_RT a regular spinlock_t becomes a sleeping lock.
This issue was found by our static analysis tool and then manually reviewed against the current tree.
The grounded PoC kept the request_threaded_irq() -> __setup_irq() -> irq_startup() -> sch_irq_unmask() -> sch_irq_mask_unmask() carrier and used the original spin_lock_irqsave(&sch->lock) edge. Lockdep reported:
BUG: sleeping function called from invalid context hardirqs last disabled at ... __setup_irq.constprop.0 ... [vuln_msv] sch_rt_spin_lock_irqsave+0x1c/0x30 [vuln_msv] sch_irq_mask_unmask.constprop.0+0x31/0x70 [vuln_msv] __setup_irq.constprop.0+0xd/0x30 [vuln_msv]
Convert the SCH controller lock to raw_spinlock_t. The same lock is also used by the GPIO direction and value callbacks, but those critical sections only update MMIO-backed GPIO registers and do not contain sleepable operations. Keeping this register lock non-sleeping is therefore appropriate for the irqchip callbacks and does not change the GPIO-side locking contract.
--- drivers/gpio/gpio-sch.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-)
drivers/gpio/gpio-sch.c +16 -16
@@ -39,7 +39,7 @@struct sch_gpio {struct gpio_chip chip;void __iomem *regs; -spinlock_t lock; +raw_spinlock_t lock;unsigned short resume_base;/* GPE handling */
@@ -104,9 +104,9 @@ static int sch_gpio_direction_in(structstruct sch_gpio *sch =gpiochip_get_data (gc);unsigned long flags; -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags);sch_gpio_reg_set (sch, gpio_num,GIO ,1 ); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags);return 0 ; }
@@ -122,9 +122,9 @@ static int sch_gpio_set(struct gpio_chipstruct sch_gpio *sch =gpiochip_get_data (gc);unsigned long flags; -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags);sch_gpio_reg_set (sch, gpio_num,GLV , val); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags);return 0 ; }
@@ -135,9 +135,9 @@ static int sch_gpio_direction_out(structstruct sch_gpio *sch =gpiochip_get_data (gc);unsigned long flags; -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags);sch_gpio_reg_set (sch, gpio_num,GIO ,0 ); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags);/* * according to the datasheet, writing to the level register has no
@@ -196,14 +196,14 @@ static int sch_irq_type(struct irq_datareturn -EINVAL ; } -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags);sch_gpio_reg_set (sch, gpio_num,GTPE , rising);sch_gpio_reg_set (sch, gpio_num,GTNE , falling);irq_set_handler_locked (d, handle_edge_irq); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags);return 0 ; }
@@ -215,9 +215,9 @@ static void sch_irq_ack(struct irq_datairq_hw_number_t gpio_num =irqd_to_hwirq (d);unsigned long flags; -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags);sch_gpio_reg_set (sch, gpio_num,GTS ,1 ); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags); }static void sch_irq_mask_unmask (struct gpio_chip *gc,irq_hw_number_t gpio_num,int val)
@@ -225,9 +225,9 @@ static void sch_irq_mask_unmask(struct gstruct sch_gpio *sch =gpiochip_get_data (gc);unsigned long flags; -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags);sch_gpio_reg_set (sch, gpio_num,GGPE , val); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags); }static void sch_irq_mask (struct irq_data *d)
@@ -268,12 +268,12 @@ static u32 sch_gpio_gpe_handler(acpi_hanint offset;u32 ret; -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags); core_status =ioread32 (sch->regs +CORE_BANK_OFFSET +GTS ); resume_status =ioread32 (sch->regs +RESUME_BANK_OFFSET +GTS ); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags); pending = (resume_status << sch->resume_base) | core_status;for_each_set_bit (offset, &pending, sch->chip.ngpio)
@@ -343,7 +343,7 @@ static int sch_gpio_probe(struct platfor sch->regs = regs; -spin_lock_init (&sch->lock); +raw_spin_lock_init (&sch->lock); sch->chip = sch_gpio_chip; sch->chip.label =dev_name (dev); sch->chip.parent = dev;
Greg Kroah-Hartman
maintainerPATCH 182/480 io_uring/nop: fix file reference leak with IOSQE_FIXED_FILE
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasileios Almpanis <[email protected]>
commit 2564ca2e31bd8ee8348362941af2ee4671e487ca upstream.
NOP file-acquisition support choses between a fixed (registered) file and a normal fget()'d file based on its own IORING_NOP_FIXED_FILE flag in sqe->nop_flags. However, a request's REQ_F_FIXED_FILE is set independently from the generic IOSQE_FIXED_FILE sqe flag during request init, before the issue handler runs.
If a NOP is submitted with IOSQE_FIXED_FILE set (so REQ_F_FIXED_FILE is set) but without IORING_NOP_FIXED_FILE, io_nop() takes the normal path and grabs a real reference via io_file_get_normal(). On completion, io_put_file() only drops the reference when REQ_F_FIXED_FILE is clear, so the fget()'d file is never released and leaks:
BUG: memory leak
unreferenced object 0xffff88800f42c240 (size 176):
kmem_cache_alloc_noprof+0x358/0x440
alloc_empty_file+0x57/0x180
path_openat+0x44/0x1e50
do_file_open+0x121/0x200
do_sys_openat2+0xa7/0x150
__x64_sys_openat+0x82/0xf0Decide between fixed and normal file acquisition from REQ_F_FIXED_FILE, the same way io_assign_file() does for every other opcode, and fold IORING_NOP_FIXED_FILE into REQ_F_FIXED_FILE at prep time.
--- io_uring/nop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
io_uring/nop.c +4 -4
@@ -41,6 +41,8 @@ int io_nop_prep(struct io_kiocb *req, co nop->fd =READ_ONCE (sqe->fd); else nop->fd = -1; +if (nop->flags &IORING_NOP_FIXED_FILE ) + req->flags |=REQ_F_FIXED_FILE ;if (nop->flags &IORING_NOP_FIXED_BUFFER ) req->buf_index =READ_ONCE (sqe->buf_index);if (nop->flags &IORING_NOP_CQE32 ) {
@@ -60,12 +62,10 @@ int io_nop(struct io_kiocb *req, unsigneint ret = nop->result;if (nop->flags &IORING_NOP_FILE ) { -if (nop->flags &IORING_NOP_FIXED_FILE ) { +if (req->flags &REQ_F_FIXED_FILE ) req->file =io_file_get_fixed (req, nop->fd, issue_flags); - req->flags |=REQ_F_FIXED_FILE ; - }else { +else req->file =io_file_get_normal (req, nop->fd); - }if (!req->file) { ret = -EBADF ; goto done;
Greg Kroah-Hartman
maintainerPATCH 183/480 io_uring/io-wq: re-check IO_WQ_BIT_EXIT for each linked work item
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
commit 29bef9934b2521f787bb15dd1985d4c0d12ae02a upstream.
commit 10dc95939817 ("io_uring/io-wq: check IO_WQ_BIT_EXIT inside work run loop") fixed the obvious case where io_worker_handle_work() took one exit-bit snapshot before draining pending work, but the fix stops one level too early.
io_worker_handle_work() now re-checks IO_WQ_BIT_EXIT in its outer work run loop, yet it still snapshots that bit once before processing a whole dependent linked-work chain. If io_wq_exit_start() sets IO_WQ_BIT_EXIT after the first linked item has started, the remaining linked items can still reuse stale do_kill = false, skip IO_WQ_WORK_CANCEL, and continue running after exit has begun.
Move the check further inside, so it covers linked items too. Note: this is a syzbot special as it loves setting up tons of slow linked work on weird devices like msr that take forever to read, and immediately close the ring. Exit then takes a long time.
--- io_uring/io-wq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
io_uring/io-wq.c +1 -1
@@ -601,7 +601,6 @@ static void io_worker_handle_work(structstruct io_wq *wq = worker->wq; do { - bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state); struct io_wq_work *work; /*
@@ -637,6 +636,7 @@ static void io_worker_handle_work(struct/* handle a whole dependent link */ do { + bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state); struct io_wq_work *next_hashed, *linked; unsigned int work_flags = atomic_read(&work->flags); unsigned int hash = __io_wq_is_hashed(work_flags)
Greg Kroah-Hartman
maintainerPATCH 184/480 io_uring/rw: preserve partial result for iopoll
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Wigham <[email protected]>
commit c554246ff4c68abf71b61a89c6e39d3cf94f523e upstream.
A partial read will store the completed byte count in io->bytes_done. The regular completion path applies io_fixup_rw_res() so that, when the following operation reaches EOF, the number of bytes already read is returned.
The iopoll completion path does not apply this fixup to the return value and can return zero instead.
Use the fixup result when updating the CQE, and the raw result for the reissue check.
--- io_uring/rw.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
io_uring/rw.c +6 -6
@@ -611,15 +611,15 @@ static void io_complete_rw_iopoll(struct { struct io_rw *rw = container_of(kiocb, struct io_rw, kiocb);struct io_kiocb *req =cmd_to_io_kiocb (rw); +int final_res =io_fixup_rw_res (req, res);if (kiocb->ki_flags &IOCB_WRITE )io_req_end_write (req); -if (unlikely (res != req->cqe.res)) { -if (res == -EAGAIN &&io_rw_should_reissue (req)) - req->flags |=REQ_F_REISSUE |REQ_F_BL_NO_RECYCLE ; -else - req->cqe.res = res; - } + +if (res == -EAGAIN &&io_rw_should_reissue (req)) + req->flags |=REQ_F_REISSUE |REQ_F_BL_NO_RECYCLE ; +else if (unlikely (final_res != req->cqe.res)) + req->cqe.res = final_res;/* order with io_iopoll_complete() checking ->iopoll_completed */ smp_store_release (&req->iopoll_completed,1 );
Greg Kroah-Hartman
maintainerPATCH 185/480 netpoll: fix a use-after-free on shutdown path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
commit 45f1458a85017a023f138b22ac5c76abd477db42 upstream.
There is a use-after-free error on netpoll, which is clearly detected by KASAN.
BUG: KASAN: slab-use-after-free in _raw_spin_lock_irqsave+0x3b/0x80
Read of size 1 at addr ... by task kworker/9:1
Workqueue: events queue_process
Call Trace:
skb_dequeue+0x1e/0xb0
queue_process+0x2c/0x600
process_scheduled_works+0x4b6/0x850
worker_thread+0x414/0x5a0
Allocated by task 242:
__netpoll_setup+0x201/0x4a0
netpoll_setup+0x249/0x550
enabled_store+0x32f/0x380
Freed by task 0:
kfree+0x1b7/0x540
rcu_core+0x3f8/0x7a0The problem happens when there is a pending TX worker running in parallel with the cleanup path.
This is what happens on netpoll shutdown path:
1) __netpoll_cleanup() is called
2) set dev->npinfo to NULL
3) call_rcu() with rcu_cleanup_netpoll_info()
3.1) rcu_cleanup_netpoll_info() tries to cancel all workers with
cancel_delayed_work(), but doesn't wait for the worker to finish
4) and kfree(npinfo);
Because 3.1) doesn't really cancel the work, as the comment says "we can't call cancel_delayed_work_sync here, as we are in softirq", the TX worker can run after 4).
Tl;DR: queue_process() is not an RCU reader, it reaches npinfo through the work item via container_of().
Use disable_delayed_work_sync() to ensure the worker is completely stopped and prevent any future re-arming attempts. Once npinfo is set to NULL, senders will bail out and not queue new work. The disable flag ensures any in-flight re-arming attempts also fail silently.
In the future, we can do the cleanup inline here without needing the npinfo->rcu rcu_head, but that is net-next material.
--- net/core/netpoll.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-)
net/core/netpoll.c +1 -8
@@ -812,14 +812,6 @@ static void rcu_cleanup_netpoll_info(str container_of(rcu_head, struct netpoll_info, rcu);skb_queue_purge (&npinfo->txq); - -/* we can't call cancel_delayed_work_sync here, as we are in softirq */ -cancel_delayed_work (&npinfo->tx_work); - -/* clean after last, unfinished work */ -__skb_queue_purge (&npinfo->txq); -/* now cancel it again */ -cancel_delayed_work (&npinfo->tx_work);kfree (npinfo); }
@@ -843,6 +835,7 @@ static void __netpoll_cleanup(struct net ops->ndo_netpoll_cleanup (np->dev);RCU_INIT_POINTER (np->dev->npinfo,NULL ); +disable_delayed_work_sync (&npinfo->tx_work);call_rcu (&npinfo->rcu, rcu_cleanup_netpoll_info); }
Greg Kroah-Hartman
maintainerPATCH 186/480 ipv4: igmp: remove multicast group from hash table on device destruction
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuyang Huang <[email protected]>
commit 7993211bde166471dffac074dc965489f86531f8 upstream.
When a device is destroyed under RTNL, ip_mc_destroy_dev() iterates through the multicast list and calls ip_ma_put() on each membership, scheduling them for RCU reclamation. However, they are not unlinked from the device's multicast hash table (mc_hash).
Since the device remains published in dev->ip_ptr until after ip_mc_destroy_dev() completes, concurrent RCU readers traversing mc_hash can still locate and access the multicast group after its refcount is decremented. If the RCU callback runs and frees the group while a reader is accessing it, a use-after-free occurs.
Fix this by unlinking the multicast group from mc_hash using ip_mc_hash_remove() before scheduling it for reclamation.
BUG: KASAN: slab-use-after-free in ip_check_mc_rcu+0x149/0x3f0 Read of size 4 at addr ffff888009bf1408 by task mausezahn/2276 Call Trace: <IRQ> dump_stack_lvl+0x67/0x90 print_report+0x175/0x7c0 kasan_report+0x147/0x180 ip_check_mc_rcu+0x149/0x3f0 udp_v4_early_demux+0x36d/0x12d0 ip_rcv_finish_core+0xb8b/0x1390 ip_rcv_finish+0x54/0x120 NF_HOOK+0x213/0x2b0 __netif_receive_skb+0x126/0x340 process_backlog+0x4f2/0xf00 __napi_poll+0x92/0x2c0 net_rx_action+0x583/0xc60 handle_softirqs+0x236/0x7f0 do_softirq+0x57/0x80 </IRQ> Allocated by task 2239: kasan_save_track+0x3e/0x80 __kasan_kmalloc+0x72/0x90 ____ip_mc_inc_group+0x31a/0xa40 __ip_mc_join_group+0x334/0x3f0 do_ip_setsockopt+0x16fa/0x2010 ip_setsockopt+0x3f/0x90 do_sock_setsockopt+0x1ad/0x300 Freed by task 0: kasan_save_track+0x3e/0x80 kasan_save_free_info+0x40/0x50 __kasan_slab_free+0x3a/0x60 __rcu_free_sheaf_prepare+0xd4/0x220 rcu_free_sheaf+0x36/0x190 rcu_core+0x8d9/0x12f0 handle_softirqs+0x236/0x7f0
--- net/ipv4/igmp.c | 1 + 1 file changed, 1 insertion(+)
net/ipv4/igmp.c +1 -0
@@ -1904,6 +1904,7 @@ void ip_mc_destroy_dev(struct in_device#endif while ((i =rtnl_dereference (in_dev->mc_list)) !=NULL ) { +ip_mc_hash_remove (in_dev, i); in_dev->mc_list = i->next_rcu; in_dev->mc_count--;ip_mc_clear_src (i);
Greg Kroah-Hartman
maintainerPATCH 187/480 net: ipv4: bound TCP reordering sysctl writes and MTU probe sizes
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wyatt Feng <[email protected]>
commit efb8763d7bbb40cff4cc55a6b62c3095a038149c upstream.
Reject invalid net.ipv4.tcp_reordering values before they reach TCP socket state. The sysctl is stored as an int but copied into the u32 tp->reordering field for new sockets, so negative writes wrap to large values.
With tcp_mtu_probing=2, the wrapped value can overflow the tcp_mtu_probe() size calculation and drive the MTU probing path into an out-of-bounds read. Route tcp_reordering writes through proc_dointvec_minmax() and require it to be at least 1. Also require tcp_max_reordering to be at least 1 so the configured maximum cannot become negative either.
When registering the table for a non-init network namespace, relocate extra2 pointers that refer into init_net.ipv4 so the tcp_reordering upper bound follows that namespace's tcp_max_reordering.
Harden tcp_mtu_probe() itself by computing size_needed as u64. This keeps the send queue and window checks from being bypassed through signed integer overflow.
--- net/ipv4/sysctl_net_ipv4.c | 10 ++++++++-- net/ipv4/tcp_output.c | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-)
net/ipv4/sysctl_net_ipv4.c +8 -2
@@ -1040,7 +1040,9 @@ static struct ctl_table ipv4_net_table[] .data = &init_net.ipv4.sysctl_tcp_reordering, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ONE, + .extra2 = &init_net.ipv4.sysctl_tcp_max_reordering, }, { .procname = "tcp_retries1",
@@ -1275,7 +1277,8 @@ static struct ctl_table ipv4_net_table[] .data = &init_net.ipv4.sysctl_tcp_max_reordering, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ONE, }, { .procname = "tcp_dsack",
@@ -1640,6 +1643,9 @@ static __net_init int ipv4_sysctl_init_n*/ table[i].mode &= ~0222 ; } +if (table[i].extra2 >= (void *)&init_net.ipv4 && + table[i].extra2 < (void *)(&init_net.ipv4 +1 )) + table[i].extra2 += (void *)net - (void *)&init_net; } }
net/ipv4/tcp_output.c +2 -2
@@ -2604,7 +2604,7 @@ static int tcp_mtu_probe(struct sock *skstruct sk_buff *skb, *nskb, *next;struct net *net =sock_net (sk);int probe_size; -int size_needed; +u64 size_needed;int copy, len;int mss_now;int interval;
@@ -2628,7 +2628,7 @@ static int tcp_mtu_probe(struct sock *sk mss_now =tcp_current_mss (sk); probe_size =tcp_mtu_to_mss (sk, (icsk->icsk_mtup.search_high + icsk->icsk_mtup.search_low) >>1 ); - size_needed = probe_size + (tp->reordering +1 ) * tp->mss_cache; + size_needed = probe_size + (tp->reordering +1 ) * (u64 )tp->mss_cache; interval = icsk->icsk_mtup.search_high - icsk->icsk_mtup.search_low;/* When misfortune happens, we are reprobing actively, * and then reprobe timer has expired. We stick with current
Greg Kroah-Hartman
maintainerPATCH 188/480 mfd: cros_ec: Delay dev_set_drvdata() until probe success
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrei Kuchynski <[email protected]>
commit 8b2c1d41bc36c100b38ce5ee6def246c527eaf8a upstream.
If ec_device_probe() fails, cros_ec_class_release releases memory for the cros_ec_dev structure. However, because the drvdata was already set, sub-drivers like cros_ec_typec can still retrieve the stale pointer via the platform device. This leads to a use-after-free when cros_ec_typec attempts to access &typec->ec->ec->dev on a device that has already been released. Move dev_set_drvdata() to ensure that the pointer is only made available once all initialization steps have succeeded.
sysfs: cannot create duplicate filename '/class/chromeos/cros_ec'
Call trace: sysfs_do_create_link_sd+0x94/0xdc sysfs_create_link+0x30/0x44 device_add_class_symlinks+0x90/0x13c device_add+0xf0/0x50c ec_device_probe+0x150/0x4f0 platform_probe+0xa0/0xe0 ... BUG: KASAN: invalid-access in __memcpy+0x44/0x230 Write at addr f5ffff809e2d33ac by task kworker/u32:5/125 Pointer tag: [f5], memory tag: [fe] Tainted : [W]=WARN, [O]=OOT_MODULE Hardware name: Google Navi unprovisioned 0x7FFFFFFF/sku0 board/sku3 Workqueue: events_unbound deferred_probe_work_func Call trace: __memcpy+0x44/0x230 cros_ec_check_features+0x60/0xcc [cros_ec_proto] cros_typec_probe+0xe8/0x6e0 [cros_ec_typec] platform_probe+0xa0/0xe0
--- drivers/mfd/cros_ec_dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/mfd/cros_ec_dev.c +2 -1
@@ -195,7 +195,6 @@ static int ec_device_probe(struct platfoif (!ec)return retval; -dev_set_drvdata (dev, ec); ec->ec_dev =dev_get_drvdata (dev->parent); ec->dev = dev; ec->cmd_offset = ec_platform->cmd_offset;
@@ -237,6 +236,8 @@ static int ec_device_probe(struct platfoif (retval) goto failed; +dev_set_drvdata (dev, ec); +/* check whether this EC is a sensor hub. */ if (cros_ec_get_sensor_count (ec) >0 ) { retval = mfd_add_hotplug_devices(ec->dev,
Greg Kroah-Hartman
maintainerPATCH 189/480 mm/shrinker: do not hold RCU lock in shrinker_debugfs_count_show()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shakeel Butt <[email protected]>
commit b902890c62d200b3509cb5e09cf1e0a66553c128 upstream.
Reading the debugfs "count" file of a memcg-aware shrinker can sleep inside an RCU read-side critical section:
BUG: sleeping function called from invalid context at kernel/cgroup/rstat.c:421 RCU nest depth: 1, expected: 0 css_rstat_flush mem_cgroup_flush_stats zswap_shrinker_count shrinker_debugfs_count_show
shrinker_debugfs_count_show() invokes the ->count_objects() callback under rcu_read_lock(). The zswap callback flushes memcg stats via css_rstat_flush(), which may sleep, so it must not run under RCU.
The RCU lock is not needed here. mem_cgroup_iter() takes RCU internally and returns a memcg holding a css reference (dropped on the next iteration or by mem_cgroup_iter_break()), so the memcg stays alive without it. The shrinker is kept alive by the open debugfs file: shrinker_free() removes the debugfs entries via debugfs_remove_recursive(), which waits for in-flight readers to drain, before call_rcu(..., shrinker_free_rcu_cb). The sibling "scan" handler already invokes the sleeping ->scan_objects() callback with no RCU section.
Drop the rcu_read_lock()/rcu_read_unlock().
--- mm/shrinker_debug.c | 4 ---- 1 file changed, 4 deletions(-)
mm/shrinker_debug.c +0 -4
@@ -57,8 +57,6 @@ static int shrinker_debugfs_count_show(sif (!count_per_node)return -ENOMEM ; -rcu_read_lock (); - memcg_aware = shrinker->flags &SHRINKER_MEMCG_AWARE ; memcg =mem_cgroup_iter (NULL ,NULL ,NULL );
@@ -88,8 +86,6 @@ static int shrinker_debugfs_count_show(s } }while ((memcg =mem_cgroup_iter (NULL , memcg,NULL )) !=NULL ); -rcu_read_unlock (); -kfree (count_per_node);return ret; }
Greg Kroah-Hartman
maintainerPATCH 190/480 mm: shrinker: fix shrinker_info teardown race with expansion
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qi Zheng <[email protected]>
commit 65476d31d8056e859c48580f82295ce159196ffe upstream.
expand_shrinker_info() iterates all visible memcgs under shrinker_mutex, including memcgs that have not finished ->css_online() yet.
Once pn->shrinker_info has been published, teardown must stay serialized with expand_shrinker_info() until that memcg is either fully online or no longer visible to iteration. Today alloc_shrinker_info() breaks that rule by dropping shrinker_mutex before freeing a partially initialized shrinker_info array, which may cause the following race:
CPU0 CPU1
==== ====
css_create
--> list_add_tail_rcu(&css->sibling, &parent_css->children);
online_css
--> mem_cgroup_css_online
--> alloc_shrinker_info
--> alloc node0 info
rcu_assign_pointer(C->node0->shrinker_info, old0)
alloc node1 info -> FAIL -> goto err
mutex_unlock(shrinker_mutex)
shrinker_alloc()
--> shrinker_memcg_alloc
--> mutex_lock(shrinker_mutex)
expand_shrinker_info
--> mem_cgroup_iter see the memcg
expand_one_shrinker_info
--> old0 = C->node0->shrinker_info
memcpy(new->unit, old0->unit, ...);
free_shrinker_info
--> kvfree(old0);
/* double free !! */
kvfree_rcu(old0, rcu);The same problem exists later in mem_cgroup_css_online(). If alloc_shrinker_info() succeeds but a subsequent objcg allocation fails, the free_objcg -> free_shrinker_info() unwind path tears down the already published pn->shrinker_info arrays without shrinker_mutex. The expand_one_shrinker_info() can race with that teardown in the same way, leading to use-after-free or double-free of the old shrinker_info.
Fix this by serializing shrinker_info teardown with shrinker_mutex, and by keeping alloc_shrinker_info() error cleanup inside the locked section.
--- mm/shrinker.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
mm/shrinker.c +11 -2
@@ -59,12 +59,14 @@ static inline int shrinker_unit_alloc(streturn 0 ; } -void free_shrinker_info (struct mem_cgroup *memcg) +static void __free_shrinker_info (struct mem_cgroup *memcg) {struct mem_cgroup_per_node *pn;struct shrinker_info *info;int nid; +lockdep_assert_held (&shrinker_mutex); +for_each_node (nid) { pn = memcg->nodeinfo[nid]; info =rcu_dereference_protected (pn->shrinker_info, true);
@@ -74,6 +76,13 @@ void free_shrinker_info(struct mem_cgrou } } +void free_shrinker_info (struct mem_cgroup *memcg) +{ +mutex_lock (&shrinker_mutex); +__free_shrinker_info (memcg); +mutex_unlock (&shrinker_mutex); +} +int alloc_shrinker_info (struct mem_cgroup *memcg) {int nid, ret =0 ;
@@ -98,8 +107,8 @@ int alloc_shrinker_info(struct mem_cgroureturn ret; err: +__free_shrinker_info (memcg);mutex_unlock (&shrinker_mutex); -free_shrinker_info (memcg);return -ENOMEM ; }
Greg Kroah-Hartman
maintainerPATCH 191/480 mm: shrinker: fix NULL pointer dereference in debugfs
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qi Zheng <[email protected]>
commit e30453c61e185e914fde83c650e268067b140218 upstream.
shrinker_debugfs_add() creates both "count" and "scan" debugfs files unconditionally.
That assumes every shrinker implements both count_objects() and scan_objects(), which is not guaranteed. For example, the xen-backend shrinker sets count_objects() but leaves scan_objects() NULL, so writing to its scan file calls through a NULL function pointer and panics the kernel:
BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:0x0 Code: Unable to access opcode bytes at 0xffffffffffffffd6. Call Trace: <TASK> shrinker_debugfs_scan_write+0x12e/0x270 full_proxy_write+0x5f/0x90 vfs_write+0xde/0x420 ? filp_flush+0x75/0x90 ? filp_close+0x1d/0x30 ? do_dup2+0xb8/0x120 ksys_write+0x68/0xf0 ? filp_flush+0x75/0x90 do_syscall_64+0xb3/0x5b0 entry_SYSCALL_64_after_hwframe+0x76/0x7e
The count path has the same issue in principle if a shrinker omits count_objects().
To fix it, only create "count" and "scan" debugfs files when the corresponding callbacks are present.
--- mm/shrinker_debug.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
mm/shrinker_debug.c +6 -4
@@ -182,10 +182,12 @@ int shrinker_debugfs_add(struct shrinker } shrinker->debugfs_entry = entry; -debugfs_create_file ("count" ,0440 , entry, shrinker, - &shrinker_debugfs_count_fops); -debugfs_create_file ("scan" ,0220 , entry, shrinker, - &shrinker_debugfs_scan_fops); +if (shrinker->count_objects) +debugfs_create_file ("count" ,0440 , entry, shrinker, + &shrinker_debugfs_count_fops); +if (shrinker->scan_objects) +debugfs_create_file ("scan" ,0220 , entry, shrinker, + &shrinker_debugfs_scan_fops);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 192/480 mm: swap_cgroup: fix NULL deref in lookup_swap_cgroup_id on swapless host
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jose Fernandez (Anthropic) <[email protected]>
commit 63b02a9409cb5180398491b093e48bcb5315f5fb upstream.
lookup_swap_cgroup_id() passes swap_cgroup_ctrl[type].map to __swap_cgroup_id_lookup() without checking that the type was ever registered via swap_cgroup_swapon(). On a swapless host every ctrl->map is NULL, so __swap_cgroup_id_lookup() dereferences NULL + a scaled swp_offset().
Since commit bea67dcc5eea ("mm: attempt to batch free swap entries for zap_pte_range()"), zap_pte_range() -> swap_pte_batch() calls lookup_swap_cgroup_id() on any non-present, non-none PTE that decodes as a real swap entry, without first validating it against swap_info[]. A single PTE corrupted into a type-0 swap entry takes the host down at process exit.
We hit this in production on a swapless 6.12.58 host: ~1s of "get_swap_device: Bad swap file entry 3f800204222bb" (do_swap_page() being correctly defensive about the same entry) followed by
BUG: unable to handle page fault for address: 000003f800204220 RIP: 0010:lookup_swap_cgroup_id+0x2b/0x60 Call Trace: swap_pte_batch+0xbf/0x230 zap_pte_range+0x4c8/0x780 unmap_page_range+0x190/0x3e0 exit_mmap+0xd9/0x3c0 do_exit+0x20c/0x4b0
syzbot has reported the identical stack.
The source of the PTE corruption is a separate bug; this change makes the teardown path as robust as the fault path already is. Every other caller of lookup_swap_cgroup_id() is downstream of a get_swap_device() that has already validated the entry, so the new branch is cold.
--- mm/swap_cgroup.c | 2 ++ 1 file changed, 2 insertions(+)
mm/swap_cgroup.c +2 -0
@@ -124,6 +124,8 @@ unsigned short lookup_swap_cgroup_id(swpreturn 0 ; ctrl = &swap_cgroup_ctrl[swp_type (ent)]; +if (unlikely (!ctrl->map)) +return 0 ;return __swap_cgroup_id_lookup (ctrl->map,swp_offset (ent)); }
Greg Kroah-Hartman
maintainerPATCH 193/480 mm/swap: add cond_resched() in swap_reclaim_full_clusters to prevent softlockup
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zijiang Huang <[email protected]>
commit 66366d291f666ddeda5f8c84f253e308de3e6b55 upstream.
We hit a real softlockup in an internal stress test environment. The workload was LTP memory/swap stress on a large arm64 machine, with 320 CPUs, about 1TB memory and an 8.6GB swap device. The system was under heavy load and the swap device had a large number of full clusters. The softlockup was triggered during a stress test after about 3 days.
So, add periodic cond_resched() calls during large full_clusters reclaim operations to prevent softlockup issues.
Detailed call trace as follow:
PID: 3817773 TASK: ffff0883bb28b780 CPU: 48 COMMAND: "kworker/48:7"
#0 [ffff800080183d10] __crash_kexec at ffffa4c1361e5de4 #1 [ffff800080183d90] panic at ffffa4c1360d5e9c
#2 [ffff800080183e20] watchdog_timer_fn at ffffa4c136231fa8
...
#16 [ffff8000c4ad3cb0] swap_cache_del_folio at ffffa4c1363e1614 #17 [ffff8000c4ad3ce0] __try_to_reclaim_swap at ffffa4c1363e4bfc #18 [ffff8000c4ad3d40] swap_reclaim_full_clusters at ffffa4c1363e5474 #19 [ffff8000c4ad3da0] swap_reclaim_work at ffffa4c1363e550c #20 [ffff8000c4ad3dc0] process_one_work at ffffa4c136102edc #21 [ffff8000c4ad3e10] worker_thread at ffffa4c136103398 #22 [ffff8000c4ad3e70] kthread at ffffa4c13610d95c
--- mm/swapfile.c | 1 + 1 file changed, 1 insertion(+)
mm/swapfile.c +1 -0
@@ -1008,6 +1008,7 @@ static void swap_reclaim_full_clusters(sswap_cluster_unlock (ci);if (to_scan <=0 )break ; +cond_resched (); } }
Greg Kroah-Hartman
maintainerPATCH 194/480 netfilter: ctnetlink: use nf_ct_exp_net() in expectation dump
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pratham Gupta <[email protected]>
commit a7f57320bbbc67e347bf5fff4b4a9bab980d5956 upstream.
Commit 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation") introduced exp->net so RCU-only expectation paths no longer need to dereference exp->master for netns lookups.
Commit 3db5647984de ("netfilter: nf_conntrack_expect: skip expectations in other netns via proc") updated the proc path accordingly, but ctnetlink_exp_dump_table() still
compares against nf_ct_net(exp->master).
Use nf_ct_exp_net(exp) here as well so the netlink dump path matches the rest of the March 2026 expectation netns/RCU cleanup.
--- net/netfilter/nf_conntrack_netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/netfilter/nf_conntrack_netlink.c +1 -1
@@ -3159,7 +3159,7 @@ restart:if (l3proto && exp->tuple.src.l3num != l3proto)continue ; -if (!net_eq (nf_ct_net (exp->master), net)) +if (!net_eq (nf_ct_exp_net (exp), net))continue ;if (cb->args[1 ]) {
Greg Kroah-Hartman
maintainerPATCH 195/480 netfilter: handle unreadable frags
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
commit da5b58478a9c1b85608c9e40a3b8432d071b409e upstream.
sashiko reports:
When an skb with unreadable fragments (such as from devmem TCP, where skb_frags_readable(skb) returns false) is processed by the u32 module, skb_copy_bits() will safely return a negative error code [..]
xt_u32: bail out with hotdrop in this case.gather_frags: return -1, just as if we had no fragment header.nfnetlink_queue: restrict to the linear part.nfnetlink_log: restrict to the linear part.
v2:
- skb_zerocopy helpers don't copy readable flag, i.e. nfnetlink_queue is broken too
xt_u32 shouldn't return true if hotdrop was set.
--- net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +- net/netfilter/nfnetlink_log.c | 26 +++++++++++++++++--------- net/netfilter/nfnetlink_queue.c | 16 ++++++++++++---- net/netfilter/xt_u32.c | 16 +++++++++++----- 4 files changed, 41 insertions(+), 19 deletions(-)
net/ipv6/netfilter/nf_conntrack_reasm.c +1 -1
@@ -418,7 +418,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *return -1 ; }if (skb_copy_bits (skb, start, &hdr,sizeof (hdr))) -BUG (); +return -1 ;if (nexthdr ==NEXTHDR_AUTH ) hdrlen =ipv6_authlen (&hdr);else
net/netfilter/nfnetlink_log.c +17 -9
@@ -668,7 +668,7 @@ __build_packet_message(struct nfnl_log_n goto nla_put_failure;if (skb_copy_bits (skb,0 ,nla_data (nla), data_len)) -BUG (); + goto nla_put_failure; } nlh->nlmsg_len = inst->skb->tail - old_tail;
@@ -690,6 +690,21 @@ static const struct nf_loginfo default_l }, }; +static unsigned int nfulnl_get_copy_len (const struct nf_loginfo *li, +const struct sk_buff *skb, +unsigned int copy_len) +{ +unsigned int len = skb->len; + +if ((li->u.ulog.flags &NF_LOG_F_COPY_LEN ) && + li->u.ulog.copy_len < copy_len) + copy_len = li->u.ulog.copy_len; +if (!skb_frags_readable (skb)) + len =skb_headlen (skb); + +return min (len, copy_len); +} +/* log handler for internal netfilter logging api */ static void nfulnl_log_packet(struct net *net,
@@ -782,14 +797,7 @@ nfulnl_log_packet(struct net *net,break ;case NFULNL_COPY_PACKET : - data_len = inst->copy_range; -if ((li->u.ulog.flags &NF_LOG_F_COPY_LEN ) && - (li->u.ulog.copy_len < data_len)) - data_len = li->u.ulog.copy_len; - -if (data_len > skb->len) - data_len = skb->len; - + data_len =nfulnl_get_copy_len (li, skb, inst->copy_range); size +=nla_total_size (data_len);break ;
net/netfilter/nfnetlink_queue.c +12 -4
@@ -676,6 +676,17 @@ static int nfqnl_put_master_ifindex(stru }#endif +static unsigned int nfqnl_get_data_len (const struct sk_buff *entskb, +unsigned int copy_range) +{ +unsigned int data_len = entskb->len; + +if (!skb_frags_readable (entskb)) + data_len =skb_headlen (entskb); + +return min (data_len, copy_range); +} +static struct sk_buff * nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, struct nf_queue_entry *entry,
@@ -741,10 +752,7 @@ nfqnl_build_packet_message(struct net *n nf_queue_checksum_help(entskb))return NULL ; - data_len =READ_ONCE (queue->copy_range); -if (data_len > entskb->len) - data_len = entskb->len; - + data_len =nfqnl_get_data_len (entskb,READ_ONCE (queue->copy_range)); hlen =skb_zerocopy_headlen (entskb); hlen = min_t(unsigned int, hlen, data_len); size +=sizeof (struct nlattr ) + hlen;
net/netfilter/xt_u32.c +11 -5
@@ -14,8 +14,8 @@#include <linux/netfilter/x_tables.h> #include <linux/netfilter/xt_u32.h> -static bool u32_match_it (const struct xt_u32 *data, -const struct sk_buff *skb) +static int u32_match_it (const struct xt_u32 *data, +const struct sk_buff *skb) {const struct xt_u32_test *ct;unsigned int testind;
@@ -40,7 +40,8 @@ static bool u32_match_it(const struct xtreturn false;if (skb_copy_bits (skb, pos, &n,sizeof (n)) <0 ) -BUG (); +return -1 ; + val =ntohl (n); nnums = ct->nnums;
@@ -68,7 +69,7 @@ static bool u32_match_it(const struct xtif (skb_copy_bits (skb, at + pos, &n,sizeof (n)) <0 ) -BUG (); +return -1 ; val =ntohl (n);break ; }
@@ -90,9 +91,14 @@ static bool u32_match_it(const struct xtstatic bool u32_mt (const struct sk_buff *skb,struct xt_action_param *par) {const struct xt_u32 *data = par->matchinfo; -bool ret; +int ret; ret =u32_match_it (data, skb); +if (ret <0 ) { + par->hotdrop = true; +return false; + } +return ret ^ data->invert; }
Greg Kroah-Hartman
maintainerPATCH 196/480 netfilter: ebtables: zero chainstack array
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
commit cbfe53599eebffd188938ab6774cc41794f6f9d5 upstream.
sashiko reports:
looking at ebtables table
translation, could a sparse cpu_possible_mask lead to an uninitialized pointer free?
If cpu_possible_mask is sparse (for example, CPU 0 and CPU 2 are possible, but CPU 1 is not), the allocation loop skips CPU 1. If vmalloc_node() fails at CPU 2, the cleanup loop will blindly decrement and call vfree() on newinfo->chainstack[1].
Not a real-world bug, such allocation isn't expected to fail in the first place.
--- net/bridge/netfilter/ebtables.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
net/bridge/netfilter/ebtables.c +1 -2
@@ -921,8 +921,7 @@ static int translate_table(struct net *n* if an error occurs */ newinfo->chainstack = -vmalloc_array (nr_cpu_ids, -sizeof (*(newinfo->chainstack))); +vcalloc (nr_cpu_ids,sizeof (*(newinfo->chainstack)));if (!newinfo->chainstack)return -ENOMEM ;for_each_possible_cpu (i) {
Greg Kroah-Hartman
maintainerPATCH 197/480 netfilter: ebtables: module names must be null-terminated
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
commit 084d23f818321390509e9738a0b08bbf46df6425 upstream.
We need to explicitly check the length, else we may pass non-null terminated string to request_module().
--- net/bridge/netfilter/ebtables.c | 3 +++ 1 file changed, 3 insertions(+)
net/bridge/netfilter/ebtables.c +3 -0
@@ -403,6 +403,9 @@ ebt_check_match(struct ebt_entry_match * left - sizeof(struct ebt_entry_match) < m->match_size)return -EINVAL ; +if (strnlen (m->u.name,XT_EXTENSION_MAXNAMELEN ) ==XT_EXTENSION_MAXNAMELEN ) +return -EINVAL ; + match =xt_find_match (NFPROTO_BRIDGE , m->u.name, m->u.revision);if (IS_ERR (match) || match->family !=NFPROTO_BRIDGE ) {if (!IS_ERR (match))
Greg Kroah-Hartman
maintainerPATCH 198/480 netfilter: ebtables: terminate table name before find_table_lock()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
commit a622d2e9608c9dff47fc2e5759ac7aa3a836b45d upstream.
update_counters() and compat_update_counters() forward a user-supplied 32-byte table name to find_table_lock() without NUL-terminating it. On a lookup miss, find_inlist_lock() calls try_then_request_module(..., "%s%s", "ebtable_", name), and vsnprintf() reads past the name field and the stack object until it hits a zero byte.
BUG: KASAN: stack-out-of-bounds in string (lib/vsprintf.c:648 lib/vsprintf.c:730) Read of size 1 at addr ffff8880119dfb20 by task exploit/147 Call Trace: ... string (lib/vsprintf.c:648 lib/vsprintf.c:730) vsnprintf (lib/vsprintf.c:2945) __request_module (kernel/module/kmod.c:150) do_update_counters.isra.0 (net/bridge/netfilter/ebtables.c:371 net/bridge/netfilter/ebtables.c:380) update_counters (net/bridge/netfilter/ebtables.c:1440) do_ebt_set_ctl (net/bridge/netfilter/ebtables.c:2573) nf_setsockopt (net/netfilter/nf_sockopt.c:101) ip_setsockopt (net/ipv4/ip_sockglue.c:1424) raw_setsockopt (net/ipv4/raw.c:847) __sys_setsockopt (net/socket.c:2393) ...
compat_do_replace() shares the same unterminated name via compat_copy_ebt_replace_from_user(); terminate it there too so all find_table_lock() callers behave alike. The other callers already terminate the name after the copy.
--- net/bridge/netfilter/ebtables.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/bridge/netfilter/ebtables.c +6 -0
@@ -1436,6 +1436,8 @@ static int update_counters(struct net *nif (copy_from_sockptr (&hlp, arg,sizeof (hlp)))return -EFAULT ; + hlp.name[sizeof (hlp.name) -1 ] ='\0' ; +if (len !=sizeof (hlp) + hlp.num_counters *sizeof (struct ebt_counter ))return -EINVAL ;
@@ -2275,6 +2277,8 @@ static int compat_copy_ebt_replace_from_memcpy (repl, &tmp, offsetof(struct ebt_replace , hook_entry)); + repl->name[sizeof (repl->name) -1 ] ='\0' ; +/* starting with hook_entry, 32 vs. 64 bit structures are different */ for (i =0 ; i <NF_BR_NUMHOOKS ; i++) repl->hook_entry[i] =compat_ptr (tmp.hook_entry[i]);
@@ -2397,6 +2401,8 @@ static int compat_update_counters(structif (copy_from_sockptr (&hlp, arg,sizeof (hlp)))return -EFAULT ; + hlp.name[sizeof (hlp.name) -1 ] ='\0' ; +/* try real handler in case userland supplied needed padding */ if (len !=sizeof (hlp) + hlp.num_counters *sizeof (struct ebt_counter ))return update_counters (net, arg, len);
Greg Kroah-Hartman
maintainerPATCH 199/480 netfilter: flowtable: fix offloaded ct timeout never being extended
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Adrian Bente <[email protected]>
commit 53b3e60edb674b442b2b3bbdba484667b0f47a5d upstream.
OpenWrt has recently migrated many platforms to kernel 6.18. On the MediaTek platform, which supports hardware network offloading, WiFi connections accelerated via the WED path were observed to drop after roughly 300 seconds.
After several debugging sessions, assisted by the Claude LLM, the problem was narrowed down as follows:
nf_flow_table_extend_ct_timeout() extends ct->timeout for offloaded flows using:
cmpxchg(&ct->timeout, expires, new_timeout);
'expires' comes from nf_ct_expires(ct) and is a relative value, while ct->timeout holds an absolute timestamp. The two are never equal, so the cmpxchg always fails and the timeout is never extended.
This goes unnoticed for most flows, but a long-lived hardware (WED) offloaded flow on MediaTek MT7986 eventually has ct->timeout decay to zero, the conntrack entry is reaped and the connection breaks.
Open-code the relative value from a single READ_ONCE(ct->timeout) snapshot and compare against that same absolute snapshot in the cmpxchg, so the timeout extension actually takes effect while the datapath remains authoritative if it updates ct->timeout concurrently.
--- net/netfilter/nf_flow_table_core.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
net/netfilter/nf_flow_table_core.c +9 -4
@@ -500,8 +500,13 @@ static u32 nf_flow_table_tcp_timeout(con*/ static void nf_flow_table_extend_ct_timeout (struct nf_conn *ct) { -static const u32 min_timeout =5 *60 *HZ ; -u32 expires =nf_ct_expires (ct); +static const s32 min_timeout =5 *60 *HZ ; +u32 ct_timeout =READ_ONCE (ct->timeout); +s32 expires; + + expires = ct_timeout - nfct_time_stamp; +if (expires <=0 )/* already expired */ +return ;/* normal case: large enough timeout, nothing to do. */ if (likely (expires >= min_timeout))
@@ -519,7 +524,7 @@ static void nf_flow_table_extend_ct_timeif (nf_ct_is_confirmed (ct) &&test_bit (IPS_OFFLOAD_BIT , &ct->status)) {u8 l4proto =nf_ct_protonum (ct); -u32 new_timeout = true; +u32 new_timeout =1 ;switch (l4proto) {case IPPROTO_UDP :
@@ -544,7 +549,7 @@ static void nf_flow_table_extend_ct_time*/ if (new_timeout) { new_timeout += nfct_time_stamp; -cmpxchg (&ct->timeout, expires, new_timeout); +cmpxchg (&ct->timeout, ct_timeout, new_timeout); } }
Greg Kroah-Hartman
maintainerPATCH 200/480 Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Senozhatsky <[email protected]>
commit a257407e2bbbb099ed427719a50563f67fa366d8 upstream.
Every once in a while we see a hung btmtksdio_flush() task:
INFO: task kworker/u17:0:189 blocked for more than 122 seconds. __cancel_work_timer+0x3f4/0x460 cancel_work_sync+0x1c/0x2c btmtksdio_flush+0x2c/0x40 hci_dev_open_sync+0x10c4/0x2190 [..]
It all boils down to incorrect time_is_before_jiffies() usage in btmtksdio_txrx_work(). The btmtksdio_txrx_work() loop is expected to be terminated if running for longer than 5*HZ. However the timeout check is twisted: time_is_before_jiffies(old_jiffies + 5*HZ) evaluates to true when old_jiffies + 5*HZ is in the past i.e. when a timeout has occurred. Using OR with time_is_before_jiffies(txrx_timeout) means that:
- before the 5-second timeout: the condition is int_status || false, so it loops as long as there are pending interrupts.
- after the 5-second timeout: the condition becomes int_status || true, which is always true.
When the loop becomes infinite btmtksdio_txrx_work() loop never terminates and never releases the SDIO host.
Fix loop termination condition to actually enforce a 5*HZ timeout.
--- drivers/bluetooth/btmtksdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/bluetooth/btmtksdio.c +1 -1
@@ -607,7 +607,7 @@ static void btmtksdio_txrx_work(struct wif (btmtksdio_rx_packet (bdev, rx_size) <0 ) bdev->hdev->stat.err_rx++; } - }while (int_status ||time_is_before_jiffies (txrx_timeout)); + }while (int_status &&time_is_after_jiffies (txrx_timeout));/* Enable interrupt */ if (bdev->func->irq_handler)
Greg Kroah-Hartman
maintainerPATCH 201/480 Bluetooth: bnep: pin L2CAP connection during netdev registration
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yousef Alhouseen <[email protected]>
commit bb067a99a0356196c0b89a95721985485ebce5a5 upstream.
bnep_add_connection() reads the L2CAP connection without holding the channel lock, then passes its HCI device to register_netdev(). Controller teardown can clear and release that connection concurrently, leaving the network device registration path to dereference a freed parent device.
Take a reference to the L2CAP connection while holding the channel lock. Retain it until register_netdev() has taken the parent device reference.
--- net/bluetooth/bnep/core.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-)
net/bluetooth/bnep/core.c +21 -6
@@ -562,14 +562,18 @@ static int bnep_session(void *arg)return 0 ; } -static struct device *bnep_get_device (struct bnep_session *session) +static struct l2cap_conn *bnep_get_conn (struct bnep_session *session) { -struct l2cap_conn *conn =l2cap_pi (session->sock->sk)->chan->conn; +struct l2cap_chan *chan =l2cap_pi (session->sock->sk)->chan; +struct l2cap_conn *conn; -if (!conn || !conn->hcon) -return NULL ; +l2cap_chan_lock (chan); + conn = chan->conn; +if (conn) +l2cap_conn_get (conn); +l2cap_chan_unlock (chan); -return &conn->hcon->dev; +return conn; }static const struct device_type bnep_type = {
@@ -581,6 +585,7 @@ int bnep_add_connection(struct bnep_connu32 valid_flags =BIT (BNEP_SETUP_RESPONSE );struct net_device *dev;struct bnep_session *s, *ss; +struct l2cap_conn *conn =NULL ;u8 dst[ETH_ALEN ], src[ETH_ALEN ];int err;
@@ -640,10 +645,18 @@ int bnep_add_connection(struct bnep_connbnep_set_default_proto_filter (s);#endif -SET_NETDEV_DEV (dev,bnep_get_device (s)); + conn =bnep_get_conn (s); +if (!conn) { + err = -ENOTCONN ; + goto failed; + } + +SET_NETDEV_DEV (dev, &conn->hcon->dev);SET_NETDEV_DEVTYPE (dev, &bnep_type); err =register_netdev (dev); +l2cap_conn_put (conn); + conn =NULL ;if (err) goto failed;
@@ -665,6 +678,8 @@ int bnep_add_connection(struct bnep_connreturn 0 ; failed: +if (conn) +l2cap_conn_put (conn);up_write (&bnep_session_sem);free_netdev (dev);return err;
Greg Kroah-Hartman
maintainerPATCH 202/480 Bluetooth: btnxpuart: Fix out-of-bounds firmware read in nxp_recv_fw_req_v3()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
commit badff6c3bed8923a1257a853f137d447976eec30 upstream.
During the v3 firmware download the controller sends a v3_data_req with a 32 bit offset and a 16 bit len. nxp_recv_fw_req_v3() checks only the lower bound of the offset and then sends firmware from that offset.
nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction;
serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data +
nxpdev->fw_dnld_v3_offset, len);Nothing checks that fw_dnld_v3_offset + len stays within nxpdev->fw->size, so a controller that asks for an offset or length past the firmware image makes the driver read past the end of nxpdev->fw->data and send that memory back over UART.
nxp_recv_fw_req_v1() already bounds the same write. Add the equivalent check to the v3 path, reject the request when it falls outside the firmware image, and zero len on the error path so the fw_v3_prev_sent bookkeeping at free_skb stays consistent.
--- drivers/bluetooth/btnxpuart.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/bluetooth/btnxpuart.c +6 -0
@@ -1267,6 +1267,12 @@ static int nxp_recv_fw_req_v3(struct hci } nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction; +if (nxpdev->fw_dnld_v3_offset >= nxpdev->fw->size || + len > nxpdev->fw->size - nxpdev->fw_dnld_v3_offset) { +bt_dev_err (hdev,"FW download out of bounds, ignoring request" ); + len =0 ; + goto free_skb; + }serdev_device_write_buf (nxpdev->serdev, nxpdev->fw->data + nxpdev->fw_dnld_v3_offset, len);
Greg Kroah-Hartman
maintainerPATCH 203/480 Bluetooth: fix UAF in bt_accept_dequeue()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yousef Alhouseen <[email protected]>
commit 4bd0b274054f2679f28b70222b607bb0afc3ab9a upstream.
bt_accept_get() takes a temporary reference before dropping the accept queue lock. bt_accept_dequeue() currently drops that reference before bt_accept_unlink(), leaving only the queue reference.
bt_accept_unlink() drops the queue reference. The subsequent sock_hold() therefore accesses freed memory if it was the final reference, as observed by KASAN during listening L2CAP socket cleanup.
Retain the temporary queue-walk reference through unlink and hand it to the caller on success. Drop it explicitly on the closed and not-yet-connected paths.
--- net/bluetooth/af_bluetooth.c | 17 +++-------------- net/bluetooth/l2cap_sock.c | 4 ++-- 2 files changed, 5 insertions(+), 16 deletions(-)
net/bluetooth/af_bluetooth.c +3 -14
@@ -308,7 +308,7 @@ struct sock *bt_accept_dequeue(struct so restart:for (sk =bt_accept_get (parent,NULL ); sk; sk = next) { -/* Prevent early freeing of sk due to unlink and sock_kill */ +/* The reference from bt_accept_get() keeps sk alive. */ lock_sock (sk);/* Check sk has not already been unlinked via
@@ -324,13 +324,11 @@ restart: next =bt_accept_get (parent, sk); -/* sk is safely in the parent list so reduce reference count */ -sock_put (sk); -/* FIXME: Is this check still needed */ if (sk->sk_state ==BT_CLOSED ) {bt_accept_unlink (sk);release_sock (sk); +sock_put (sk);continue ; }
@@ -340,16 +338,6 @@ restart:if (newsock)sock_graft (sk, newsock); -/* Hand the caller a reference taken while sk is -* still locked. bt_accept_unlink() just dropped -* the accept-queue reference; without this hold a -* concurrent teardown (e.g. l2cap_conn_del() -> -* l2cap_sock_kill()) could free sk between -* release_sock() and the caller using it. Every -* caller drops this with sock_put() when done. -*/ -sock_hold (sk); -release_sock (sk);if (next)sock_put (next);
@@ -357,6 +345,7 @@ restart: }release_sock (sk); +sock_put (sk); }return NULL ;
net/bluetooth/l2cap_sock.c +2 -2
@@ -1464,8 +1464,8 @@ static void l2cap_sock_cleanup_listen(st/* Close not yet accepted channels. * -* bt_accept_dequeue() now returns sk with an extra reference held -* (taken while sk was still locked) so a concurrent l2cap_conn_del() +* bt_accept_dequeue() returns sk with its temporary queue-walk +* reference held, so a concurrent l2cap_conn_del() * -> l2cap_sock_kill() cannot free sk under us. * * cleanup_listen() runs under the parent sk lock, so unlike
Greg Kroah-Hartman
maintainerPATCH 204/480 Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Siwei Zhang <[email protected]>
commit 12917f591cea1af36087dba5b9ec888652f0b42a upstream.
hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection was pending, but hdev->sent_cmd can be NULL while req_status is still HCI_REQ_PEND, leading to a NULL pointer dereference and a general protection fault from the hci_rx_work() receive path.
Instead of inspecting hdev->sent_cmd, track the in-flight create connection command with a new per-connection HCI_CONN_CREATE flag and route all cancellation through hci_cancel_connect_sync(), which dispatches to a dedicated per-type cancel function. The create command is in exactly one of two states: still queued, or in flight. The cancel function holds cmd_sync_work_lock across the whole decision: the worker takes this lock to dequeue every entry, so while it is held a queued command cannot start running and an in-flight command cannot complete and let the next command become pending. This keeps the flag test and hci_cmd_sync_cancel() atomic with respect to the worker, so a queued command is simply dequeued, and an in-flight command owned by this connection is cancelled without the risk of cancelling an unrelated command that became pending in the meantime. CIS uses the same flag mechanism via HCI_CONN_CREATE_CIS but cannot be dequeued per-connection.
hci_acl_create_conn_sync() and hci_le_create_conn_sync() clear HCI_CONN_CREATE after the create command completes, but the command status handler can free conn via hci_conn_del() (for example when the controller rejects the connection) while the worker is still blocked on the connection complete event. Hold a reference on conn across the create command so the flag can be cleared without a use-after-free.
--- include/net/bluetooth/hci_core.h | 1 net/bluetooth/hci_conn.c | 21 ------ net/bluetooth/hci_sync.c | 133 ++++++++++++++++++++++++++++++++++----- 3 files changed, 123 insertions(+), 32 deletions(-)
include/net/bluetooth/hci_core.h +1 -0
@@ -984,6 +984,7 @@ enum {HCI_CONN_AUTH_FAILURE ,HCI_CONN_PER_ADV ,HCI_CONN_BIG_CREATED , +HCI_CONN_CREATE ,HCI_CONN_CREATE_CIS ,HCI_CONN_CREATE_BIG_SYNC , HCI_CONN_BIG_SYNC,
net/bluetooth/hci_conn.c +3 -18
@@ -2979,26 +2979,11 @@ int hci_abort_conn(struct hci_conn *conn conn->abort_reason = reason; -/* If the connection is pending check the command opcode since that -* might be blocking on hci_cmd_sync_work while waiting its respective -* event so we need to hci_cmd_sync_cancel to cancel it. -* -* hci_connect_le serializes the connection attempts so only one -* connection can be in BT_CONNECT at time. +/* Cancel the connect attempt. A return of 0 means the create command +* was still queued and got dequeued, so there is nothing to disconnect. */ -if (conn->state ==BT_CONNECT &&READ_ONCE (hdev->req_status) ==HCI_REQ_PEND ) { -switch (hci_skb_event (hdev->sent_cmd)) { -case HCI_EV_CONN_COMPLETE : -case HCI_EV_LE_CONN_COMPLETE : -case HCI_EV_LE_ENHANCED_CONN_COMPLETE : -case HCI_EVT_LE_CIS_ESTABLISHED : -hci_cmd_sync_cancel (hdev,ECANCELED ); -break ; - } -/* Cancel connect attempt if still queued/pending */ - }else if (!hci_cancel_connect_sync (hdev, conn)) { +if (!hci_cancel_connect_sync (hdev, conn))return 0 ; - }/* Run immediately if on cmd_sync_work since this may be called * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
net/bluetooth/hci_sync.c +119 -14
180 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 205/480 Bluetooth: hci_uart: clear HCI_UART_SENDING when write_work is canceled
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <[email protected]>
commit 1b0d946d6f08bd39211385bc703a440911b41e46 upstream.
HCI_UART_SENDING bit in tx_state means write_work is pending and blocks queueing it again. Currently this bit is not cleared when canceling the work in hci_uart_close(), which blocks future writes when device is reopened later if write_work was pending.
Fix by clearing HCI_UART_SENDING when canceling the work.
Also make clearing of tx_skb safe by using disable_work_sync + enable_work instead of just cancel_work_sync. hci_uart_flush() purges the proto tx queue so we can cancel the pending write_work there, instead of doing it just in hci_uart_close(). Re-enable and possibly requeue the work after queue flush.
--- drivers/bluetooth/hci_ldisc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
drivers/bluetooth/hci_ldisc.c +10 -4
@@ -239,6 +239,8 @@ static int hci_uart_flush(struct hci_devBT_DBG ("hdev %p tty %p" , hdev, tty); +disable_work_sync (&hu->write_work); +if (hu->tx_skb) {kfree_skb (hu->tx_skb); hu->tx_skb =NULL ; }
@@ -254,6 +256,14 @@ static int hci_uart_flush(struct hci_devpercpu_up_read (&hu->proto_lock); +/* Resume TX. Also reschedule in case work was queued concurrently; +* this may schedule write_work although there's nothing to do. +*/ +enable_work (&hu->write_work); +clear_bit (HCI_UART_SENDING , &hu->tx_state); +if (test_bit (HCI_UART_TX_WAKEUP , &hu->tx_state)) +hci_uart_tx_wakeup (hu); +return 0 ; }
@@ -271,12 +281,8 @@ static int hci_uart_open(struct hci_dev/* Close device */ static int hci_uart_close (struct hci_dev *hdev) { -struct hci_uart *hu =hci_get_drvdata (hdev); -BT_DBG ("hdev %p" , hdev); -cancel_work_sync (&hu->write_work); -hci_uart_flush (hdev); hdev->flush =NULL ;return 0 ;
Greg Kroah-Hartman
maintainerPATCH 206/480 Bluetooth: ISO: avoid NULL deref of conn in iso_conn_big_sync()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit d5541eb148da72d5e0a1bca8ecd171f9fc8b366f upstream.
iso_conn_big_sync() drops the socket lock to call hci_get_route() and then re-acquires it, but dereferences iso_pi(sk)->conn->hcon afterwards without re-checking that conn is still valid.
While the lock is dropped, the connection can be torn down under the same socket lock: iso_disconn_cfm() -> iso_conn_del() -> iso_chan_del() sets iso_pi(sk)->conn to NULL (and the broadcast teardown path can also clear conn->hcon on its own). When iso_conn_big_sync() re-acquires the lock and reads conn->hcon, conn may be NULL, causing a NULL pointer dereference (hcon is the first member of struct iso_conn).
This path is reached from iso_sock_recvmsg() for a PA-sync broadcast sink socket (BT_SK_DEFER_SETUP | BT_SK_PA_SYNC), so the dropped-lock window can race with connection teardown driven by controller events.
Re-validate iso_pi(sk)->conn and its hcon after re-acquiring the socket lock and bail out if the connection went away, as already done in the sibling iso_sock_rebind_bc().
--- net/bluetooth/iso.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
net/bluetooth/iso.c +12 -1
@@ -1515,6 +1515,7 @@ static void iso_conn_big_sync(struct soc {int err;struct hci_dev *hdev; +struct iso_conn *conn;bdaddr_t src, dst;u8 src_type;
@@ -1537,8 +1538,17 @@ static void iso_conn_big_sync(struct sochci_dev_lock (hdev);lock_sock (sk); +/* The socket lock was dropped for hci_get_route(), so the connection +* may have been torn down meanwhile: iso_chan_del() clears conn and +* the broadcast teardown path can clear conn->hcon on its own. Check +* both before dereferencing conn->hcon. +*/ + conn =iso_pi (sk)->conn; +if (!conn || !conn->hcon) + goto unlock; +if (!test_and_set_bit (BT_SK_BIG_SYNC , &iso_pi (sk)->flags)) { - err = hci_conn_big_create_sync(hdev, iso_pi(sk)->conn->hcon, + err = hci_conn_big_create_sync(hdev, conn->hcon, &iso_pi(sk)->qos, iso_pi(sk)->sync_handle, iso_pi(sk)->bc_num_bis,
@@ -1547,6 +1557,7 @@ static void iso_conn_big_sync(struct socbt_dev_err (hdev,"hci_big_create_sync: %d" , err); } +unlock:release_sock (sk);hci_dev_unlock (hdev);hci_dev_put (hdev);
Greg Kroah-Hartman
maintainerPATCH 207/480 Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
commit 2641a9e0a1dd4af2e21995470a21d55dd35e5203 upstream.
l2cap_conn_del() takes conn->lock and then calls cancel_work_sync() for pending_rx_work. process_pending_rx() takes the same mutex, so teardown can deadlock against the worker it is flushing.
This issue was found by our static analysis tool and then manually reviewed against the current tree.
The grounded PoC kept the l2cap_conn_ready() -> queue_work(..., &conn->pending_rx_work) submit path, the l2cap_conn_del() -> cancel_work_sync(&conn->pending_rx_work) teardown path, and the process_pending_rx() -> mutex_lock(&conn->lock) worker edge. Lockdep reported:
WARNING: possible circular locking dependency detected
process_pending_rx+0x21/0x2a [vuln_msv] l2cap_conn_del.constprop.0+0x3f/0x4e [vuln_msv] *** DEADLOCK ***
Cancel pending_rx_work before taking conn->lock, matching the existing lock-before-drain ordering used for the two delayed works in the same teardown path. The pending_rx queue is still purged after the work has been cancelled and conn->lock has been acquired.
--- net/bluetooth/l2cap_core.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-)
net/bluetooth/l2cap_core.c +2 -8
@@ -1778,19 +1778,13 @@ static void l2cap_conn_del(struct hci_codisable_delayed_work_sync (&conn->info_timer);disable_delayed_work_sync (&conn->id_addr_timer); +cancel_work_sync (&conn->pending_rx_work); +mutex_lock (&conn->lock);kfree_skb (conn->rx_skb);skb_queue_purge (&conn->pending_rx); - -/* We can not call flush_work(&conn->pending_rx_work) here since we -* might block if we are running on a worker from the same workqueue -* pending_rx_work is waiting on. -*/ -if (work_pending (&conn->pending_rx_work)) -cancel_work_sync (&conn->pending_rx_work); -ida_destroy (&conn->tx_ida);l2cap_unregister_all_users (conn);
Greg Kroah-Hartman
maintainerPATCH 208/480 Bluetooth: L2CAP: validate option length before reading conf opt value
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit 687617555cedfb74c9e3cb85d759b908dcb17856 upstream.
l2cap_get_conf_opt() derives the option length from the attacker-controlled opt->len field and immediately dereferences opt->val (as u8, get_unaligned_le16() or get_unaligned_le32(), or a raw pointer for the default case) before any caller has confirmed that opt->len bytes are present in the buffer. The callers (l2cap_parse_conf_req(), l2cap_parse_conf_rsp() and l2cap_conf_rfc_get()) only detect a malformed option afterwards, once the running length has gone negative, by which point the out-of-bounds read has already executed.
An existing post-hoc length check keeps the garbage value from being consumed, so this is not a data leak in the current control flow. It is still a validate-after-use ordering bug: up to 4 bytes are read past the end of the buffer before it is known to contain them, and it is fragile to future changes in the callers.
Fix it at the source. Pass the end of the buffer into l2cap_get_conf_opt() and refuse to touch opt->val unless the full option (header + value) fits. Each caller computes an end pointer once before the loop and checks the return value directly instead of inferring the error from a negative length.
--- net/bluetooth/l2cap_core.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-)
net/bluetooth/l2cap_core.c +28 -8
@@ -3048,13 +3048,24 @@ fail:return NULL ; } -static inline int l2cap_get_conf_opt (void **ptr,int *type,int *olen, -unsigned long *val) +static inline int l2cap_get_conf_opt (void **ptr,void *end,int *type, +int *olen,unsigned long *val) {struct l2cap_conf_opt *opt = *ptr;int len; +/* opt->len is attacker-controlled. Validate that the full option +* (header + value) actually fits in the buffer before touching +* opt->val, otherwise the switch below reads past the end of the +* caller's buffer. +*/ +if (end - *ptr <L2CAP_CONF_OPT_SIZE ) +return -EINVAL ; + len =L2CAP_CONF_OPT_SIZE + opt->len; +if (end - *ptr < len) +return -EINVAL ; + *ptr += len; *type = opt->type;
@@ -3426,6 +3437,7 @@ static int l2cap_parse_conf_req(struct lvoid *ptr = rsp->data;void *endptr = data + data_size;void *req = chan->conf_req; +void *req_end = req + chan->conf_len;int len = chan->conf_len;int type, hint, olen;unsigned long val;
@@ -3439,9 +3451,11 @@ static int l2cap_parse_conf_req(struct lBT_DBG ("chan %p" , chan);while (len >=L2CAP_CONF_OPT_SIZE ) { - len -=l2cap_get_conf_opt (&req, &type, &olen, &val); -if (len <0 ) +int ret =l2cap_get_conf_opt (&req, req_end, &type, &olen, &val); + +if (ret <0 )break ; + len -= ret; hint = type &L2CAP_CONF_HINT ; type &=L2CAP_CONF_MASK ;
@@ -3669,6 +3683,7 @@ static int l2cap_parse_conf_rsp(struct lstruct l2cap_conf_req *req = data;void *ptr = req->data;void *endptr = data + size; +void *rsp_end = rsp + len;int type, olen;unsigned long val;struct l2cap_conf_rfc rfc = { .mode =L2CAP_MODE_BASIC };
@@ -3677,9 +3692,11 @@ static int l2cap_parse_conf_rsp(struct lBT_DBG ("chan %p, rsp %p, len %d, req %p" , chan, rsp, len, data);while (len >=L2CAP_CONF_OPT_SIZE ) { - len -=l2cap_get_conf_opt (&rsp, &type, &olen, &val); -if (len <0 ) +int ret =l2cap_get_conf_opt (&rsp, rsp_end, &type, &olen, &val); + +if (ret <0 )break ; + len -= ret;switch (type) {case L2CAP_CONF_MTU :
@@ -3930,6 +3947,7 @@ static void l2cap_conf_rfc_get(struct l2 {int type, olen;unsigned long val; +void *rsp_end = rsp + len;/* Use sane default values in case a misbehaving remote device * did not send an RFC or extended window size option. */
@@ -3948,9 +3966,11 @@ static void l2cap_conf_rfc_get(struct l2return ;while (len >=L2CAP_CONF_OPT_SIZE ) { - len -=l2cap_get_conf_opt (&rsp, &type, &olen, &val); -if (len <0 ) +int ret =l2cap_get_conf_opt (&rsp, rsp_end, &type, &olen, &val); + +if (ret <0 )break ; + len -= ret;switch (type) {case L2CAP_CONF_RFC :
Greg Kroah-Hartman
maintainerPATCH 209/480 iommu/vt-d: Fix race condition during PASID entry replacement
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lu Baolu <[email protected]>
commit c3b1edea3791fa91ab7032faa90355913ad9451b upstream.
The Intel VT-d PASID table entry is 512 bits (64 bytes). When replacing an active PASID entry (e.g., during domain replacement), the current implementation calculates a new entry on the stack and copies it to the table using a single structure assignment.
struct pasid_entry *pte, new_pte;
pte = intel_pasid_get_entry(dev, pasid);
pasid_pte_config_first_level(iommu, &new_pte, ...);
*pte = new_pte;Because the hardware may fetch the 512-bit PASID entry in multiple 128-bit chunks, updating the entire entry while it is active (Present bit set) risks a "torn" read. In this scenario, the IOMMU hardware could observe an inconsistent state — partially new data and partially old data — leading to unpredictable behavior or spurious faults.
Fix this by removing the unsafe "replace" helpers and following the "clear-then-update" flow, which ensures the Present bit is cleared and the required invalidation handshake is completed before the new configuration is applied.
[vineeth: adjusted for 6.18.y: the removed intel_pasid_replace_second_level()
in 6.18 still computed pgd/pgd_val locally and used the older pasid_pte_config_second_level() signature; since the function is deleted entirely, the resolution only affects removed lines. The prerequisite "Clear Present bit" patches are already in 6.18.y.
Built and tested on 6.18.38: PASID replace, iommufd RID replace and DMA sanity on QEMU VT-d in scalable mode (FS and SS), no regressions.]
--- drivers/iommu/intel/iommu.c | 29 ++---- drivers/iommu/intel/nested.c | 9 -- drivers/iommu/intel/pasid.c | 190 ------------------------------------------- drivers/iommu/intel/pasid.h | 14 --- 4 files changed, 16 insertions(+), 226 deletions(-)
drivers/iommu/intel/iommu.c +12 -17
@@ -1734,12 +1734,10 @@ int __domain_setup_first_level(struct in ioasid_t pasid, u16 did, phys_addr_t fsptptr, int flags, struct iommu_domain *old) { - if (!old) - return intel_pasid_setup_first_level(iommu, dev, fsptptr, pasid, - did, flags); - return intel_pasid_replace_first_level(iommu, dev, fsptptr, pasid, did, -iommu_domain_did (old ,iommu ), -flags ); + if (old) + intel_pasid_tear_down_entry(iommu, dev, pasid, false); + +return intel_pasid_setup_first_level (iommu, dev, fsptptr, pasid, did, flags); } static int domain_setup_second_level(struct intel_iommu *iommu,
@@ -1747,23 +1745,20 @@ static int domain_setup_second_level(str struct device *dev, ioasid_t pasid, struct iommu_domain *old) { - if (!old) - return intel_pasid_setup_second_level(iommu, domain, - dev, pasid); -return intel_pasid_replace_second_level (iommu, domain, dev, -iommu_domain_did (old, iommu), - pasid); + if (old) + intel_pasid_tear_down_entry(iommu, dev, pasid, false); + +return intel_pasid_setup_second_level (iommu, domain, dev, pasid); }static int domain_setup_passthrough (struct intel_iommu *iommu,struct device *dev,ioasid_t pasid,struct iommu_domain *old) { -if (!old) -return intel_pasid_setup_pass_through (iommu, dev, pasid); -return intel_pasid_replace_pass_through (iommu, dev, -iommu_domain_did (old, iommu), - pasid); +if (old) +intel_pasid_tear_down_entry (iommu, dev, pasid, false); + +return intel_pasid_setup_pass_through (iommu, dev, pasid); } static int domain_setup_first_level(struct intel_iommu *iommu,
drivers/iommu/intel/nested.c +4 -5
@@ -141,11 +141,10 @@ static int domain_setup_nested(struct in struct device *dev, ioasid_t pasid, struct iommu_domain *old) { - if (!old) - return intel_pasid_setup_nested(iommu, dev, pasid, domain); -return intel_pasid_replace_nested (iommu, dev, pasid, -iommu_domain_did (old, iommu), - domain); + if (old) + intel_pasid_tear_down_entry(iommu, dev, pasid, false); + +return intel_pasid_setup_nested (iommu, dev, pasid, domain); } static int intel_nested_set_dev_pasid(struct iommu_domain *domain,
drivers/iommu/intel/pasid.c +0 -190
214 lines not on this page · raw message
drivers/iommu/intel/pasid.h +0 -14
@@ -315,20 +315,6 @@ int intel_pasid_setup_pass_through(struc struct device *dev, u32 pasid);int intel_pasid_setup_nested (struct intel_iommu *iommu,struct device *dev,u32 pasid,struct dmar_domain *domain); -int intel_pasid_replace_first_level (struct intel_iommu *iommu, -struct device *dev,phys_addr_t fsptptr, -u32 pasid,u16 did,u16 old_did,int flags); -int intel_pasid_replace_second_level (struct intel_iommu *iommu, -struct dmar_domain *domain, -struct device *dev,u16 old_did, -u32 pasid); -int intel_pasid_replace_pass_through (struct intel_iommu *iommu, -struct device *dev,u16 old_did, -u32 pasid); -int intel_pasid_replace_nested (struct intel_iommu *iommu, -struct device *dev,u32 pasid, -u16 old_did,struct dmar_domain *domain); -void intel_pasid_tear_down_entry (struct intel_iommu *iommu,struct device *dev,u32 pasid,bool fault_ignore);
Greg Kroah-Hartman
maintainerPATCH 210/480 fs/ntfs3: rename ni_readpage_cmpr into ni_read_folio_cmpr
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Konstantin Komarov <[email protected]>
[ Upstream commit 4248f563f0b76f3fb74b2a28ee068bf66fcbbedf ]
The old "readpage" naming is still used in ni_readpage_cmpr(), even though the vfs has transitioned to the folio-based read_folio() API.
This patch performs a straightforward renaming of the helper:ni_readpage_cmpr() -> ni_read_folio_cmpr().
Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()")
--- fs/ntfs3/frecord.c | 8 ++++---- fs/ntfs3/inode.c | 2 +- fs/ntfs3/ntfs_fs.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-)
fs/ntfs3/frecord.c +4 -4
@@ -2046,18 +2046,18 @@ static struct page *ntfs_lock_new_page(struct address_space *mapping, }/* -* ni_readpage_cmpr +* ni_read_folio_cmpr * * When decompressing, we typically obtain more than one page per reference. * We inject the additional pages into the page cache. */ -int ni_readpage_cmpr (struct ntfs_inode *ni,struct folio *folio) +int ni_read_folio_cmpr (struct ntfs_inode *ni,struct folio *folio) {int err;struct ntfs_sb_info *sbi = ni->mi.sbi;struct address_space *mapping = folio->mapping; -pgoff_t index = folio->index; -u64 frame_vbo, vbo = (u64 )index <<PAGE_SHIFT ; +pgoff_t index; +u64 frame_vbo, vbo =folio_pos (folio);struct page **pages =NULL ;/* Array of at most 16 pages. stack? */ u8 frame_bits;CLST frame;
fs/ntfs3/inode.c +1 -1
@@ -736,7 +736,7 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)if (is_compressed (ni)) {ni_lock (ni); - err =ni_readpage_cmpr (ni, folio); + err =ni_read_folio_cmpr (ni, folio);ni_unlock (ni);return err; }
fs/ntfs3/ntfs_fs.h +1 -1
@@ -567,7 +567,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint);#define _ni_write_inode (i, w) ni_write_inode(i, w, __func__)int ni_fiemap (struct ntfs_inode *ni,struct fiemap_extent_info *fieinfo,__u64 vbo,__u64 len); -int ni_readpage_cmpr (struct ntfs_inode *ni,struct folio *folio); +int ni_read_folio_cmpr (struct ntfs_inode *ni,struct folio *folio);int ni_decompress_file (struct ntfs_inode *ni);int ni_read_frame (struct ntfs_inode *ni,u64 frame_vbo,struct page **pages,u32 pages_per_frame);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 211/480 fs/ntfs3: fsync files by syncing parent inodes
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Konstantin Komarov <[email protected]>
[ Upstream commit dcd9d6a47199565d83d61a11bbf91fa2ade4d676 ]
Some xfstests expect fsync() on a file or directory to also persist directory metadata up the parent chain. Using generic_file_fsync() is not sufficient for ntfs, because parent directories are not explicitly written out.
Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()")
--- fs/ntfs3/dir.c | 2 +- fs/ntfs3/file.c | 30 ++++++++++++++++++++++++--- fs/ntfs3/frecord.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ fs/ntfs3/ntfs_fs.h | 2 ++ 4 files changed, 81 insertions(+), 4 deletions(-)
fs/ntfs3/dir.c +1 -1
@@ -624,7 +624,7 @@ const struct file_operations ntfs_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .iterate_shared = ntfs_readdir, - .fsync = generic_file_fsync, + .fsync = ntfs_file_fsync, .open = ntfs_file_open, .unlocked_ioctl = ntfs_ioctl,#ifdef CONFIG_COMPAT
fs/ntfs3/file.c +27 -3
@@ -1378,13 +1378,37 @@ static ssize_t ntfs_file_splice_write(struct pipe_inode_info *pipe,/* * ntfs_file_fsync - file_operations::fsync */ -static int ntfs_file_fsync (struct file *file,loff_t start,loff_t end,int datasync) +int ntfs_file_fsync (struct file *file,loff_t start,loff_t end,int datasync) {struct inode *inode =file_inode (file); -if (unlikely (ntfs3_forced_shutdown (inode->i_sb))) +struct super_block *sb = inode->i_sb; +struct ntfs_sb_info *sbi = sb->s_fs_info; +int err, ret; + +if (unlikely (ntfs3_forced_shutdown (sb)))return -EIO ; -return generic_file_fsync (file, start, end, datasync); + ret =file_write_and_wait_range (file, start, end); +if (ret) +return ret; + + ret =write_inode_now (inode, !datasync); + +if (!ret) { + ret =ni_write_parents (ntfs_i (inode), !datasync); + } + +if (!ret) { +ntfs_set_state (sbi,NTFS_DIRTY_CLEAR ); +ntfs_update_mftmirr (sbi, false); + } + + err =sync_blockdev (sb->s_bdev); +if (unlikely (err && !ret)) + ret = err; +if (!ret) +blkdev_issue_flush (sb->s_bdev); +return ret; }// clang-format off
fs/ntfs3/frecord.c +51 -0
57 lines not on this page · raw message
fs/ntfs3/ntfs_fs.h +2 -0
@@ -510,6 +510,7 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,int ntfs_file_open (struct inode *inode,struct file *file);int ntfs_fiemap (struct inode *inode,struct fiemap_extent_info *fieinfo,__u64 start,__u64 len); +int ntfs_file_fsync (struct file *file,loff_t start,loff_t end,int datasync);long ntfs_ioctl (struct file *filp,u32 cmd,unsigned long arg);long ntfs_compat_ioctl (struct file *filp,u32 cmd,unsigned long arg);extern const struct inode_operations ntfs_special_inode_operations;
@@ -587,6 +588,7 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni, struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de);bool ni_is_dirty (struct inode *inode); +int ni_write_parents (struct ntfs_inode *ni,int sync);/* Globals from fslog.c */ bool check_index_header (const struct INDEX_HDR *hdr,size_t bytes);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 212/480 fs/ntfs3: zero-fill folios beyond i_valid in ntfs_read_folio()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Konstantin Komarov <[email protected]>
[ Upstream commit 356fa248168be90109b66f32a61b8eaedc98424a ]
Handle ntfs_read_folio() early when the folio offset is beyond i_valid by zero-filling the folio and marking it uptodate. This avoids needless I/O and locking, improves read performance.
Stable-dep-of: d7ea8495fd30 ("fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()")
--- fs/ntfs3/inode.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
fs/ntfs3/inode.c +13 -0
@@ -723,6 +723,19 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)struct address_space *mapping = folio->mapping;struct inode *inode = mapping->host;struct ntfs_inode *ni =ntfs_i (inode); +loff_t vbo =folio_pos (folio); + +if (unlikely (is_bad_ni (ni))) { +folio_unlock (folio); +return -EIO ; + } + +if (ni->i_valid <= vbo) { +folio_zero_range (folio,0 ,folio_size (folio)); +folio_mark_uptodate (folio); +folio_unlock (folio); +return 0 ; + }if (is_resident (ni)) {ni_lock (ni);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 213/480 fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Deepanshu Kartikey <[email protected]>
[ Upstream commit d7ea8495fd307b58f8867acd81a1b40075b1d3ba ]
When a compressed or sparse attribute has its clusters frame-aligned, vcn is rounded down to the frame start using cmask, which can result in vcn != vcn0. In this case, vcn and vcn0 may reside in different attribute segments.
The code already handles the case where vcn is in a different segment by loading its runs before allocation. However, it fails to load runs for vcn0 when vcn0 resides in a different segment than vcn. This causes run_lookup_entry() to return SPARSE_LCN for vcn0 since its segment was never loaded into the in-memory run list, triggering the WARN_ON(1).
Fix this by adding a missing check for vcn0 after the existing vcn segment check. If vcn0 falls outside the current segment range [svcn, evcn1), find and load the attribute segment containing vcn0 before performing the run lookup.
The following scenario triggers the bug:
attr_data_get_block_locked()
vcn = vcn0 & cmask <- vcn != vcn0 after frame alignment
load runs for vcn segment <- vcn0 segment not loaded! attr_allocate_clusters() <- allocation succeeds
run_lookup_entry(vcn0) <- vcn0 not in run -> SPARSE_LCN
WARN_ON(1) <- bug fires here!
--- fs/ntfs3/attrib.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
fs/ntfs3/attrib.c +14 -0
@@ -1045,6 +1045,20 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,if (err) goto out; } + +if (vcn0 < svcn || evcn1 <= vcn0) { +struct ATTRIB *attr2; + + attr2 =ni_find_attr (ni, attr_b, &le_b,ATTR_DATA ,NULL , +0 , &vcn0, &mi); +if (!attr2) { + err = -EINVAL ; + goto out; + } + err =attr_load_runs (attr2, ni, run,NULL ); +if (err) + goto out; + } }if (vcn + to_alloc > asize)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 214/480 coresight: ultrasoc-smb: Fix OOB write in smb_sync_perf_buffer()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junrui Luo <[email protected]>
commit 98495b5a4d77dd22e106f462b76e1093a55b29a7 upstream.
When the SMB sink is used as a perf AUX sink, smb_update_buffer() calls smb_sync_perf_buffer() to copy hardware trace data into the perf AUX ring buffer pages. It derives pg_idx = head >> PAGE_SHIFT from @head, which is handle->head, and indexes dst_pages[pg_idx]. The pg_idx %= nr_pages normalization is only applied after the first loop iteration.
This leaves the initial page index underived from the buffer size, which can result in an out-of-bounds write past dst_pages[] when head exceeds the AUX buffer size.
Normalize head modulo the AUX buffer size before deriving the page index and offset, mirroring tmc_etr_sync_perf_buffer().
--- drivers/hwtracing/coresight/ultrasoc-smb.c | 1 + 1 file changed, 1 insertion(+)
drivers/hwtracing/coresight/ultrasoc-smb.c +1 -0
@@ -338,6 +338,7 @@ static void smb_sync_perf_buffer(structunsigned long to_copy;long pg_idx, pg_offset; + head %= (unsigned long )buf->nr_pages <<PAGE_SHIFT ; pg_idx = head >>PAGE_SHIFT ; pg_offset = head & (PAGE_SIZE -1 );
Greg Kroah-Hartman
maintainerPATCH 215/480 smb: client: resolve SWN tcon from live registrations
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit ec457f9afe5ae9538bdcd58fd4cb442b9787e183 upstream.
cifs_swn_notify() looks up a witness registration by id under cifs_swnreg_idr_mutex, drops the mutex, and then uses the registration's cached tcon pointer. That pointer is not a lifetime reference, and it is not a stable representative once cifs_get_swn_reg() lets multiple tcons for the same net/share name share one registration id.
A same-share second mount can keep the cifs_swn_reg alive after the first tcon unregisters and is freed. The registration then still points at the freed first tcon, so taking tc_lock or incrementing tc_count through swnreg->tcon only moves the use-after-free earlier. Taking tc_lock while holding cifs_swnreg_idr_mutex also violates the documented CIFS lock order.
Fix this by making the registration store only the stable witness
identity: id, net name, share name, and notify flags. When a notify arrives, copy that identity under cifs_swnreg_idr_mutex, drop the mutex, then find and pin a live witness tcon that currently matches the net/share pair under the normal cifs_tcp_ses_lock -> tc_lock order. The notification path uses that pinned tcon directly and drops the reference when done.
Registration and unregister messages now use the live tcon passed by the caller instead of a cached tcon in the registration. The final unregister send is folded into cifs_swn_unregister() while the registration is still protected by cifs_swnreg_idr_mutex. This removes the previous find/drop/reacquire raw-pointer window. The release path only removes the idr entry and frees the stable identity strings.
This preserves the intended one-registration/many-tcon behavior: a registration id represents a net/share pair, and notify handling acts on a live representative selected at use time. It also preserves CLIENT_MOVE ordering for the representative tcon because the old-IP unregister is sent before cifs_swn_register() sends the new-IP register.
--- fs/smb/client/cifs_swn.c | 314 ++++++++++++++++++++++++++++++++++++++--------- fs/smb/client/trace.h | 2 2 files changed, 262 insertions(+), 54 deletions(-)
fs/smb/client/cifs_swn.c +260 -54
@@ -28,10 +28,54 @@ struct cifs_swn_reg {bool net_name_notify;bool share_name_notify;bool ip_notify; +}; -struct cifs_tcon *tcon; +struct cifs_swn_reg_info { +int id; +unsigned int ref_count; +const char *net_name; +const char *share_name; +bool net_name_notify; +bool share_name_notify; +bool ip_notify; }; +static void cifs_swn_snapshot_reg (struct cifs_swn_reg *swnreg, +struct cifs_swn_reg_info *info) +{ + info->id = swnreg->id; + info->ref_count =kref_read (&swnreg->ref_count); + info->net_name = swnreg->net_name; + info->share_name = swnreg->share_name; + info->net_name_notify = swnreg->net_name_notify; + info->share_name_notify = swnreg->share_name_notify; + info->ip_notify = swnreg->ip_notify; +} + +static int cifs_swn_dup_reg (struct cifs_swn_reg *swnreg, +struct cifs_swn_reg_info *info) +{ +cifs_swn_snapshot_reg (swnreg, info); + + info->net_name =kstrdup (swnreg->net_name,GFP_KERNEL ); +if (!info->net_name) +return -ENOMEM ; + + info->share_name =kstrdup (swnreg->share_name,GFP_KERNEL ); +if (!info->share_name) { +kfree (info->net_name); +return -ENOMEM ; + } + +return 0 ; +} + +static void cifs_swn_free_reg_info (struct cifs_swn_reg_info *info) +{ +kfree (info->net_name); +kfree (info->share_name); +} +static int cifs_swn_auth_info_krb (struct cifs_tcon *tcon,struct sk_buff *skb) {int ret;
@@ -73,7 +117,8 @@ static int cifs_swn_auth_info_ntlm(struc* The authentication information to connect to the witness service is bundled * into the message. */ -static int cifs_swn_send_register_message (struct cifs_swn_reg *swnreg) +static int cifs_swn_send_register_message (struct cifs_swn_reg_info *swnreg, +struct cifs_tcon *tcon) {struct sk_buff *skb;struct genlmsghdr *hdr;
@@ -109,10 +154,10 @@ static int cifs_swn_send_register_messag* told to switch to it (client move message). In these cases we unregister from the * server address and register to the new address when we receive the notification. */ -if (swnreg->tcon->ses->server->use_swn_dstaddr) - addr = &swnreg->tcon->ses->server->swn_dstaddr; +if (tcon->ses->server->use_swn_dstaddr) + addr = &tcon->ses->server->swn_dstaddr;else - addr = &swnreg->tcon->ses->server->dstaddr; + addr = &tcon->ses->server->dstaddr; ret =nla_put (skb,CIFS_GENL_ATTR_SWN_IP ,sizeof (struct sockaddr_storage ), addr);if (ret <0 )
@@ -136,10 +181,10 @@ static int cifs_swn_send_register_messag goto nlmsg_fail; } - authtype =cifs_select_sectype (swnreg->tcon->ses->server, swnreg->tcon->ses->sectype); + authtype =cifs_select_sectype (tcon->ses->server, tcon->ses->sectype);switch (authtype) {case Kerberos: - ret =cifs_swn_auth_info_krb (swnreg->tcon, skb); + ret =cifs_swn_auth_info_krb (tcon, skb);if (ret <0 ) {cifs_dbg (VFS ,"%s: Failed to get kerberos auth info: %d\n" , __func__, ret); goto nlmsg_fail;
@@ -147,7 +192,7 @@ static int cifs_swn_send_register_messagbreak ;case NTLMv2:case RawNTLMSSP: - ret =cifs_swn_auth_info_ntlm (swnreg->tcon, skb); + ret =cifs_swn_auth_info_ntlm (tcon, skb);if (ret <0 ) {cifs_dbg (VFS ,"%s: Failed to get NTLM auth info: %d\n" , __func__, ret); goto nlmsg_fail;
@@ -176,7 +221,8 @@ nlmsg_fail:/* * Sends an uregister message to the userspace daemon based on the registration */ -static int cifs_swn_send_unregister_message (struct cifs_swn_reg *swnreg) +static int cifs_swn_send_unregister_message (struct cifs_swn_reg_info *swnreg, +struct cifs_tcon *tcon) {struct sk_buff *skb;struct genlmsghdr *hdr;
@@ -205,7 +251,7 @@ static int cifs_swn_send_unregister_mess goto nlmsg_fail; ret =nla_put (skb,CIFS_GENL_ATTR_SWN_IP ,sizeof (struct sockaddr_storage ), - &swnreg->tcon->ses->server->dstaddr); + &tcon->ses->server->dstaddr);if (ret <0 ) goto nlmsg_fail;
@@ -242,6 +288,88 @@ nlmsg_fail: }/* +* Allocation-free mirror of extract_hostname() + extract_sharename() from +* fs/smb/client/unc.c. Those helpers kmalloc(GFP_KERNEL); this runs under +* cifs_tcp_ses_lock and tcon->tc_lock, both spinlocks, so we mirror their +* parsing in place against the caller's stable net_name/share_name strings. +* Keep in sync with unc.c. +*/ +static bool cifs_swn_tcon_matches (struct cifs_tcon *tcon, +const char *net_name, +const char *share_name) +{ +const char *unc = tcon->tree_name; +const char *host, *share, *delim; +size_t host_len, share_len; + +if (!tcon->use_witness) +return false; + +/* extract_hostname: require strlen(unc) >= 3 */ +if (strnlen (unc,3 ) <3 ) +return false; +/* extract_hostname: skip all leading '\' characters */ +for (host = unc; *host =='\\' ; host++) + ; +if (!*host) +return false; + delim =strchr (host,'\\' ); +if (!delim) +return false; + host_len = delim - host; +if (strlen (net_name) != host_len || +strncasecmp (host, net_name, host_len)) +return false; + +/* extract_sharename: start at unc + 2, then first '\' onward */ + share = unc +2 ; + delim =strchr (share,'\\' ); +if (!delim) +return false; + share = delim +1 ; + share_len =strlen (share); + +return strlen (share_name) == share_len && + !strncasecmp (share, share_name, share_len); +} + +/* +* One SWN registration id represents one net/share name pair. Multiple +* mounted tcons can therefore share the id. Pick a live representative at +* use time instead of caching the first tcon pointer in the registration. +*/ +static struct cifs_tcon *cifs_swn_get_tcon (struct cifs_swn_reg_info *swnreg) +{ +struct TCP_Server_Info *server; +struct cifs_ses *ses; +struct cifs_tcon *tcon; + +spin_lock (&cifs_tcp_ses_lock); +list_for_each_entry (server, &cifs_tcp_ses_list, tcp_ses_list) { +list_for_each_entry (ses, &server->smb_ses_list, smb_ses_list) { +list_for_each_entry (tcon, &ses->tcon_list, tcon_list) { +spin_lock (&tcon->tc_lock); +if (tcon->status ==TID_EXITING || + !cifs_swn_tcon_matches (tcon, swnreg->net_name, + swnreg->share_name)) { +spin_unlock (&tcon->tc_lock); +continue ; + } + ++tcon->tc_count; +trace_smb3_tcon_ref (tcon->debug_id, + tcon->tc_count, + netfs_trace_tcon_ref_get_swn_notify); +spin_unlock (&tcon->tc_lock); +spin_unlock (&cifs_tcp_ses_lock); +return tcon; + } + } + } +spin_unlock (&cifs_tcp_ses_lock); +return NULL ; +} + +/* * Try to find a matching registration for the tcon's server name and share name. * Calls to this function must be protected by cifs_swnreg_idr_mutex. * TODO Try to avoid memory allocations
@@ -347,8 +475,6 @@ static struct cifs_swn_reg *cifs_get_swn reg->net_name_notify = true; reg->share_name_notify = true; reg->ip_notify = (tcon->capabilities &SMB2_SHARE_CAP_SCALEOUT ); - - reg->tcon = tcon; unlock:mutex_unlock (&cifs_swnreg_idr_mutex);
@@ -368,11 +494,6 @@ fail_unlock:static void cifs_swn_reg_release (struct kref *ref) { struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count); -int ret; - - ret =cifs_swn_send_unregister_message (swnreg); -if (ret <0 ) -cifs_dbg (VFS ,"%s: Failed to send unregister message: %d\n" , __func__, ret);idr_remove (&cifs_swnreg_idr, swnreg->id);kfree (swnreg->net_name);
@@ -380,23 +501,33 @@ static void cifs_swn_reg_release(structkfree (swnreg); } -static void cifs_put_swn_reg (struct cifs_swn_reg *swnreg) +static void cifs_put_swn_reg_locked (struct cifs_swn_reg *swnreg, +struct cifs_tcon *tcon) { -mutex_lock (&cifs_swnreg_idr_mutex); +if (kref_read (&swnreg->ref_count) ==1 ) { +struct cifs_swn_reg_info swnreg_info; +int ret; + +cifs_swn_snapshot_reg (swnreg, &swnreg_info); + ret =cifs_swn_send_unregister_message (&swnreg_info, tcon); +if (ret <0 ) +cifs_dbg (VFS ,"%s: Failed to send unregister message: %d\n" , + __func__, ret); + } +kref_put (&swnreg->ref_count, cifs_swn_reg_release); -mutex_unlock (&cifs_swnreg_idr_mutex); } -static int cifs_swn_resource_state_changed (struct cifs_swn_reg *swnreg,const char *name,int state) +static int cifs_swn_resource_state_changed (struct cifs_tcon *tcon,const char *name,int state) {switch (state) {case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE :cifs_dbg (FYI ,"%s: resource name '%s' become unavailable\n" , __func__, name); -cifs_signal_cifsd_for_reconnect (swnreg->tcon->ses->server, true); +cifs_signal_cifsd_for_reconnect (tcon->ses->server, true);break ;case CIFS_SWN_RESOURCE_STATE_AVAILABLE :cifs_dbg (FYI ,"%s: resource name '%s' become available\n" , __func__, name); -cifs_signal_cifsd_for_reconnect (swnreg->tcon->ses->server, true); +cifs_signal_cifsd_for_reconnect (tcon->ses->server, true);break ;case CIFS_SWN_RESOURCE_STATE_UNKNOWN :cifs_dbg (FYI ,"%s: resource name '%s' changed to unknown state\n" , __func__, name);
@@ -502,7 +633,7 @@ unlock:return ret; } -static int cifs_swn_client_move (struct cifs_swn_reg *swnreg,struct sockaddr_storage *addr) +static int cifs_swn_client_move (struct cifs_tcon *tcon,struct sockaddr_storage *addr) {struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr;struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr;
@@ -512,14 +643,17 @@ static int cifs_swn_client_move(struct celse if (addr->ss_family ==AF_INET6 )cifs_dbg (FYI ,"%s: move to %pI6\n" , __func__, &ipv6->sin6_addr); -return cifs_swn_reconnect (swnreg->tcon, addr); +return cifs_swn_reconnect (tcon, addr); }int cifs_swn_notify (struct sk_buff *skb,struct genl_info *info) {struct cifs_swn_reg *swnreg; +struct cifs_swn_reg_info swnreg_info; +struct cifs_tcon *tcon;char name[256 ];int type; +int ret =0 ;if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID ]) {int swnreg_id;
@@ -527,21 +661,34 @@ int cifs_swn_notify(struct sk_buff *skb, swnreg_id =nla_get_u32 (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID ]);mutex_lock (&cifs_swnreg_idr_mutex); swnreg =idr_find (&cifs_swnreg_idr, swnreg_id); -mutex_unlock (&cifs_swnreg_idr_mutex);if (swnreg ==NULL ) { +mutex_unlock (&cifs_swnreg_idr_mutex);cifs_dbg (FYI ,"%s: registration id %d not found\n" , __func__, swnreg_id);return -EINVAL ; } + ret =cifs_swn_dup_reg (swnreg, &swnreg_info); +mutex_unlock (&cifs_swnreg_idr_mutex); +if (ret) +return ret; }else {cifs_dbg (FYI ,"%s: missing registration id attribute\n" , __func__);return -EINVAL ; } + tcon =cifs_swn_get_tcon (&swnreg_info); +if (!tcon) { +cifs_dbg (FYI ,"%s: registration id %d has no live tcon\n" , + __func__, swnreg_info.id); + ret = -ENODEV ; + goto free_info; + } +if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE ]) { type =nla_get_u32 (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE ]); }else {cifs_dbg (FYI ,"%s: missing notification type attribute\n" , __func__); -return -EINVAL ; + ret = -EINVAL ; + goto out; }switch (type) {
@@ -553,15 +700,18 @@ int cifs_swn_notify(struct sk_buff *skb, sizeof(name)); }else {cifs_dbg (FYI ,"%s: missing resource name attribute\n" , __func__); -return -EINVAL ; + ret = -EINVAL ; + goto out; }if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE ]) { state =nla_get_u32 (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE ]); }else {cifs_dbg (FYI ,"%s: missing resource state attribute\n" , __func__); -return -EINVAL ; + ret = -EINVAL ; + goto out; } -return cifs_swn_resource_state_changed (swnreg, name, state); + ret =cifs_swn_resource_state_changed (tcon, name, state); +break ; }case CIFS_SWN_NOTIFICATION_CLIENT_MOVE : {struct sockaddr_storage addr;
@@ -570,28 +720,36 @@ int cifs_swn_notify(struct sk_buff *skb,nla_memcpy (&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP ],sizeof (addr)); }else {cifs_dbg (FYI ,"%s: missing IP address attribute\n" , __func__); -return -EINVAL ; + ret = -EINVAL ; + goto out; } -return cifs_swn_client_move (swnreg, &addr); + ret =cifs_swn_client_move (tcon, &addr); +break ; }default :cifs_dbg (FYI ,"%s: unknown notification type %d\n" , __func__, type);break ; } -return 0 ; +out: +cifs_put_tcon (tcon, netfs_trace_tcon_ref_put_swn_notify); +free_info: +cifs_swn_free_reg_info (&swnreg_info); +return ret; }int cifs_swn_register (struct cifs_tcon *tcon) {struct cifs_swn_reg *swnreg; +struct cifs_swn_reg_info swnreg_info;int ret; swnreg =cifs_get_swn_reg (tcon);if (IS_ERR (swnreg))return PTR_ERR (swnreg); - ret =cifs_swn_send_register_message (swnreg); +cifs_swn_snapshot_reg (swnreg, &swnreg_info); + ret =cifs_swn_send_register_message (&swnreg_info, tcon);if (ret <0 ) {cifs_dbg (VFS ,"%s: Failed to send swn register message: %d\n" , __func__, ret);/* Do not put the swnreg or return error, the echo task will retry */
@@ -612,35 +770,68 @@ int cifs_swn_unregister(struct cifs_tconreturn PTR_ERR (swnreg); } +cifs_put_swn_reg_locked (swnreg, tcon);mutex_unlock (&cifs_swnreg_idr_mutex); -cifs_put_swn_reg (swnreg); -return 0 ; } -void cifs_swn_dump (struct seq_file *m) +/* +* Snapshot one registration under cifs_swnreg_idr_mutex and return. Callers +* intentionally do the per-registration network/genlmsg work without the +* mutex held, both to keep the critical section short and to avoid nesting +* cifs_swnreg_idr_mutex inside the higher tc_lock when a live tcon is then +* pinned for the send. +*/ +static int cifs_swn_get_next_reg_info (int *id,struct cifs_swn_reg_info *info) {struct cifs_swn_reg *swnreg; +int ret =0 ; + +mutex_lock (&cifs_swnreg_idr_mutex); + swnreg =idr_get_next (&cifs_swnreg_idr, id); +if (swnreg) { + ret =cifs_swn_dup_reg (swnreg, info); +if (!ret) { + *id = swnreg->id +1 ; + ret =1 ; + } + } +mutex_unlock (&cifs_swnreg_idr_mutex); + +return ret; +} + +void cifs_swn_dump (struct seq_file *m) +{ +struct cifs_swn_reg_info swnreg_info; +struct cifs_tcon *tcon;struct sockaddr_in *sa;struct sockaddr_in6 *sa6; -int id; +int id =0 ; +int ret;seq_puts (m,"Witness registrations:" ); -mutex_lock (&cifs_swnreg_idr_mutex); -idr_for_each_entry (&cifs_swnreg_idr, swnreg, id) { +while ((ret =cifs_swn_get_next_reg_info (&id, &swnreg_info)) >0 ) {seq_printf (m,"\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: " , - id,kref_read (&swnreg->ref_count), - swnreg->net_name, swnreg->net_name_notify ?"(y)" :"(n)" , - swnreg->share_name, swnreg->share_name_notify ?"(y)" :"(n)" ); -switch (swnreg->tcon->ses->server->dstaddr.ss_family) { + swnreg_info.id, swnreg_info.ref_count, + swnreg_info.net_name, swnreg_info.net_name_notify ?"(y)" :"(n)" , + swnreg_info.share_name, swnreg_info.share_name_notify ?"(y)" :"(n)" ); + + tcon =cifs_swn_get_tcon (&swnreg_info); +if (!tcon) { +seq_puts (m,"(no live tcon)" ); + goto next; + } + +switch (tcon->ses->server->dstaddr.ss_family) {case AF_INET : - sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr; + sa = (struct sockaddr_in *)&tcon->ses->server->dstaddr;seq_printf (m,"%pI4" , &sa->sin_addr.s_addr);break ;case AF_INET6 : - sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr; + sa6 = (struct sockaddr_in6 *)&tcon->ses->server->dstaddr;seq_printf (m,"%pI6" , &sa6->sin6_addr.s6_addr);if (sa6->sin6_scope_id)seq_printf (m,"%%%u" , sa6->sin6_scope_id);
@@ -648,23 +839,38 @@ void cifs_swn_dump(struct seq_file *m)default :seq_puts (m,"(unknown)" ); } -seq_printf (m,"%s" , swnreg->ip_notify ?"(y)" :"(n)" ); +cifs_put_tcon (tcon, netfs_trace_tcon_ref_put_swn_notify); +next: +seq_printf (m,"%s" , swnreg_info.ip_notify ?"(y)" :"(n)" ); +cifs_swn_free_reg_info (&swnreg_info); } -mutex_unlock (&cifs_swnreg_idr_mutex); +if (ret <0 ) +seq_printf (m,"\nFailed to snapshot witness registration: %d" , ret);seq_puts (m,"\n" ); }void cifs_swn_check (void ) { -struct cifs_swn_reg *swnreg; -int id; +struct cifs_swn_reg_info swnreg_info; +struct cifs_tcon *tcon; +int id =0 ;int ret; -mutex_lock (&cifs_swnreg_idr_mutex); -idr_for_each_entry (&cifs_swnreg_idr, swnreg, id) { - ret =cifs_swn_send_register_message (swnreg); +while ((ret =cifs_swn_get_next_reg_info (&id, &swnreg_info)) >0 ) { + tcon =cifs_swn_get_tcon (&swnreg_info); +if (!tcon) { +cifs_dbg (FYI ,"%s: registration id %d has no live tcon\n" , + __func__, swnreg_info.id); + goto free_info; + } + + ret =cifs_swn_send_register_message (&swnreg_info, tcon);if (ret <0 )cifs_dbg (FYI ,"%s: Failed to send register message: %d\n" , __func__, ret); +cifs_put_tcon (tcon, netfs_trace_tcon_ref_put_swn_notify); +free_info: +cifs_swn_free_reg_info (&swnreg_info); } -mutex_unlock (&cifs_swnreg_idr_mutex); +if (ret <0 ) +cifs_dbg (FYI ,"%s: Failed to snapshot registration: %d\n" , __func__, ret); }
fs/smb/client/trace.h +2 -0
@@ -52,6 +52,7 @@ EM(netfs_trace_tcon_ref_get_find, "GET Find ") \ EM(netfs_trace_tcon_ref_get_find_sess_tcon, "GET FndSes") \ EM(netfs_trace_tcon_ref_get_reconnect_server, "GET Reconn") \ + EM(netfs_trace_tcon_ref_get_swn_notify, "GET SwnNot") \ EM(netfs_trace_tcon_ref_new, "NEW ") \ EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \EM (netfs_trace_tcon_ref_new_reconnect_server,"NEW Reconn" ) \
@@ -63,6 +64,7 @@ EM(netfs_trace_tcon_ref_put_mnt_ctx, "PUT MntCtx") \ EM(netfs_trace_tcon_ref_put_dfs_refer, "PUT DfsRfr") \ EM(netfs_trace_tcon_ref_put_reconnect_server, "PUT Reconn") \ + EM(netfs_trace_tcon_ref_put_swn_notify, "PUT SwnNot") \ EM(netfs_trace_tcon_ref_put_tlink, "PUT Tlink ") \ EM(netfs_trace_tcon_ref_see_cancelled_close, "SEE Cn-Cls") \EM (netfs_trace_tcon_ref_see_fscache_collision,"SEE FV-CO!" ) \
Greg Kroah-Hartman
maintainerPATCH 216/480 smb/client: Fix error code in smb2_aead_req_alloc()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dan Carpenter <[email protected]>
commit 61f28012e5650c619223decdb7970e0d3162e949 upstream.
The "*num_sgs" variable is a u32 so "ERR_PTR(*num_sgs)" doesn't work. We would have to do something similar to the previous line where it's cast to int and then long. However, it's simpler to store the return in an int ret variable.
This bug would eventually result in a crash when dereference the invalid error pointer.
--- fs/smb/client/smb2ops.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
fs/smb/client/smb2ops.c +5 -3
@@ -4314,11 +4314,13 @@ static void *smb2_aead_req_alloc(structunsigned int req_size =sizeof (**req) +crypto_aead_reqsize (tfm);unsigned int iv_size =crypto_aead_ivsize (tfm);unsigned int len; +int ret;u8 *p; - *num_sgs =cifs_get_num_sgs (rqst, num_rqst, sig); -if (IS_ERR_VALUE ((long )(int )*num_sgs)) -return ERR_PTR (*num_sgs); + ret =cifs_get_num_sgs (rqst, num_rqst, sig); +if (ret <0 ) +return ERR_PTR (ret); + *num_sgs = ret; len = iv_size; len +=crypto_aead_alignmask (tfm) & ~(crypto_tfm_ctx_alignment () -1 );
Greg Kroah-Hartman
maintainerPATCH 217/480 ksmbd: add permission checks for FSCTL_DUPLICATE_EXTENTS_TO_FILE
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gil Portnoy <[email protected]>
commit 388e4139db27a9e3612c9d356b826f5b1ff6a9e3 upstream.
The FSCTL_DUPLICATE_EXTENTS_TO_FILE arm of smb2_ioctl() overwrites the destination file's data via vfs_clone_file_range() with neither the share-level KSMBD_TREE_CONN_FLAG_WRITABLE check nor a per-handle fp->daccess check that the other write-bearing arms carry. A client can overwrite destination data on a read-only share, or from a handle opened with only FILE_WRITE_ATTRIBUTES (which still yields an FMODE_WRITE filp). FILE_WRITE_ATTRIBUTES-only destination handle overwrote the file's data via the clone. Add both checks, matching the FSCTL_SET_SPARSE permission fix; require FILE_WRITE_DATA since this writes data.
--- fs/smb/server/smb2pdu.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
fs/smb/server/smb2pdu.c +11 -0
@@ -8563,6 +8563,17 @@ int smb2_ioctl(struct ksmbd_work *work) goto dup_ext_out; } +if (!test_tree_conn_flag (work->tcon, +KSMBD_TREE_CONN_FLAG_WRITABLE )) { + ret = -EACCES ; + goto dup_ext_out; + } + +if (!(fp_out->daccess &FILE_WRITE_DATA_LE )) { + ret = -EACCES ; + goto dup_ext_out; + } + src_off =le64_to_cpu (dup_ext->SourceFileOffset); dst_off =le64_to_cpu (dup_ext->TargetFileOffset); length =le64_to_cpu (dup_ext->ByteCount);
Greg Kroah-Hartman
maintainerPATCH 218/480 ksmbd: add a permission check for FSCTL_SET_ZERO_DATA
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gil Portnoy <[email protected]>
commit 3320ba068198adc144c89d6661b805acce01735b upstream.
FSCTL_SET_ZERO_DATA in smb2_ioctl() destroys file data via ksmbd_vfs_zero_data() -> vfs_fallocate(PUNCH_HOLE/ZERO_RANGE) after checking only the share-level KSMBD_TREE_CONN_FLAG_WRITABLE, with no per-handle access check. A handle opened with only FILE_WRITE_ATTRIBUTES still yields an FMODE_WRITE filp (FILE_WRITE_ATTRIBUTES is part of FILE_WRITE_DESIRE_ACCESS_LE, so smb2_create_open_flags() opens it O_WRONLY), so the vfs_fallocate FMODE_WRITE check does not stop it; only the missing fp->daccess gate would. Reproduced on mainline 7.1-rc7 with KASAN by an authenticated SMB client: a FILE_WRITE_ATTRIBUTES-only handle zeroed 4096 bytes of file data it had no FILE_WRITE_DATA right to (6/6; a FILE_READ_DATA-only handle was correctly denied).
This is the unfixed sibling of commit cc57232cae23 ("ksmbd: fix FSCTL permission bypass by adding a permission check for FSCTL_SET_SPARSE"). Because SET_ZERO_DATA writes data (not an attribute), require FILE_WRITE_DATA.
--- fs/smb/server/smb2pdu.c | 6 ++++++ 1 file changed, 6 insertions(+)
fs/smb/server/smb2pdu.c +6 -0
@@ -8488,6 +8488,12 @@ int smb2_ioctl(struct ksmbd_work *work) goto out; } +if (!(fp->daccess &FILE_WRITE_DATA_LE )) { +ksmbd_fd_put (work, fp); + ret = -EACCES ; + goto out; + } + ret =ksmbd_vfs_zero_data (work, fp, off, len);ksmbd_fd_put (work, fp);if (ret <0 )
Greg Kroah-Hartman
maintainerPATCH 219/480 ksmbd: serialize QUERY_DIRECTORY requests per file
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
commit be6d26bf27499977c746abc163659915082348d8 upstream.
smb2_query_dir() stores a pointer to its stack-allocated private data in the ksmbd_file readdir_data. Concurrent QUERY_DIRECTORY requests using the same file handle can overwrite this pointer while an iterate_dir() callback is still using it, resulting in a stack use-after-free.
Add a per-file mutex and hold it while accessing the shared directory enumeration state. The lock covers scan restart, dot entry state, readdir_data setup and iteration, and response construction. This prevents another request from replacing readdir_data.private before the current request has finished using it and also serializes the shared file position.
--- fs/smb/server/smb2pdu.c | 4 ++++ fs/smb/server/vfs_cache.c | 1 + fs/smb/server/vfs_cache.h | 2 ++ 3 files changed, 7 insertions(+)
fs/smb/server/smb2pdu.c +4 -0
@@ -4472,6 +4472,8 @@ int smb2_query_dir(struct ksmbd_work *woksmbd_debug (SMB ,"Search pattern is %s\n" , srch_ptr); } +mutex_lock (&dir_fp->readdir_lock); +if (srch_flag &SMB2_REOPEN || srch_flag &SMB2_RESTART_SCANS ) {ksmbd_debug (SMB ,"Restart directory scan\n" );generic_file_llseek (dir_fp->filp,0 ,SEEK_SET );
@@ -4576,6 +4578,7 @@ no_buf_len: goto err_out; } +mutex_unlock (&dir_fp->readdir_lock);kfree (srch_ptr);ksmbd_fd_put (work, dir_fp);ksmbd_revert_fsids (work);
@@ -4583,6 +4586,7 @@ no_buf_len: err_out:pr_err ("error while processing smb2 query dir rc = %d\n" , rc); +mutex_unlock (&dir_fp->readdir_lock);kfree (srch_ptr); err_out2:
fs/smb/server/vfs_cache.c +1 -0
@@ -686,6 +686,7 @@ struct ksmbd_file *ksmbd_open_fd(structINIT_LIST_HEAD (&fp->node);INIT_LIST_HEAD (&fp->lock_list);spin_lock_init (&fp->f_lock); +mutex_init (&fp->readdir_lock);atomic_set (&fp->refcount,1 ); fp->filp = filp;
fs/smb/server/vfs_cache.h +2 -0
@@ -8,6 +8,7 @@#include <linux/file.h> #include <linux/fs.h> +#include <linux/mutex.h> #include <linux/rwsem.h> #include <linux/spinlock.h> #include <linux/idr.h>
@@ -113,6 +114,7 @@ struct ksmbd_file {/* if ls is happening on directory, below is valid*/ struct ksmbd_readdir_data readdir_data; +struct mutex readdir_lock;int dot_dotdot[2 ];unsigned int f_state;bool reserve_lease_break;
Greg Kroah-Hartman
maintainerPATCH 220/480 ksmbd: fix UAF of struct file_lock in SMB2_LOCK deferred-lock cancellation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Davide Ornaghi <[email protected]>
commit d20d1c8ba5765d1d12eefc0aee6385ab3f240e1e upstream.
When a blocking byte-range lock request is deferred in the FILE_LOCK_DEFERRED path, ksmbd registers the asynchronous work into the connection's async_requests list via setup_async_work(). The cancel callback smb2_remove_blocked_lock() holds a reference to the flock.
If the lock waiter is subsequently woken up but the work state is no longer KSMBD_WORK_ACTIVE (e.g., due to a concurrent cancellation), the cleanup path calls locks_free_lock(flock) without dequeuing the work from the async_requests list. Concurrently, smb2_cancel() walks the list under conn->request_lock and invokes the cancel callback, which then dereferences the already freed 'flock'. This leads to a slab-use-after-free inside __wake_up_common.
Fix this by restructuring the cleanup logic after the worker returns from ksmbd_vfs_posix_lock_wait(). Move list_del(&smb_lock->llist) and release_async_work(work) to the top of the cleanup block. This guarantees that the async work is completely dequeued and serialized under conn->request_lock before locks_free_lock(flock) is called, rendering the flock unreachable for any concurrent smb2_cancel().
--- fs/smb/server/smb2pdu.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-)
fs/smb/server/smb2pdu.c +16 -18
@@ -7747,29 +7747,27 @@ skip:list_del (&work->fp_entry);spin_unlock (&fp->f_lock); -if (work->state !=KSMBD_WORK_ACTIVE ) { -list_del (&smb_lock->llist); -locks_free_lock (flock); +list_del (&smb_lock->llist); +release_async_work (work); + +if (work->state ==KSMBD_WORK_ACTIVE ) + goto retry; -if (work->state ==KSMBD_WORK_CANCELLED ) { - rsp->hdr.Status = -STATUS_CANCELLED ; -kfree (smb_lock); -smb2_send_interim_resp (work, -STATUS_CANCELLED ); - work->send_no_response =1 ; - goto out; - } +locks_free_lock (flock); - rsp->hdr.Status = -STATUS_RANGE_NOT_LOCKED ; +if (work->state ==KSMBD_WORK_CANCELLED ) { + rsp->hdr.Status =STATUS_CANCELLED ;kfree (smb_lock); - goto out2; +smb2_send_interim_resp (work, +STATUS_CANCELLED ); + work->send_no_response =1 ; + goto out; } -list_del (&smb_lock->llist); -release_async_work (work); - goto retry; + rsp->hdr.Status = +STATUS_RANGE_NOT_LOCKED ; +kfree (smb_lock); + goto out2; }else if (!rc) {list_add (&smb_lock->llist, &rollback_list);spin_lock (&work->conn->llist_lock);
Greg Kroah-Hartman
maintainerPATCH 221/480 ksmbd: require source read access for duplicate extents
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
commit cedff600f1642aa982178503552f0d007bc829c8 upstream.
FSCTL_DUPLICATE_EXTENTS_TO_FILE passes the source file directly to vfs_clone_file_range() or vfs_copy_file_range() without checking the SMB access mask granted to the source handle. A handle opened with attribute access can consequently be used to copy file contents into an attacker-readable destination.
Require FILE_READ_DATA on the source handle before either VFS operation, matching other ksmbd data-copy paths.
--- fs/smb/server/smb2pdu.c | 4 ++++ 1 file changed, 4 insertions(+)
fs/smb/server/smb2pdu.c +4 -0
@@ -8581,6 +8581,10 @@ int smb2_ioctl(struct ksmbd_work *work) ret = -EACCES ; goto dup_ext_out; } +if (!(fp_in->daccess &FILE_READ_DATA_LE )) { + ret = -EACCES ; + goto dup_ext_out; + } src_off =le64_to_cpu (dup_ext->SourceFileOffset); dst_off =le64_to_cpu (dup_ext->TargetFileOffset);
Greg Kroah-Hartman
maintainerPATCH 222/480 ksmbd: add a WRITE_DAC/WRITE_OWNER check to SMB2 SET_INFO SECURITY
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gil Portnoy <[email protected]>
commit 44df157a1183a7f746caa970c169255da5ac61f8 upstream.
commit cc57232cae23 ("ksmbd: fix FSCTL permission bypass by adding a permission check for FSCTL_SET_SPARSE") added a fp->daccess gate to fsctl_set_sparse and noted that "similar handle-level checks exist in other functions but are missing here." The SMB2 SET_INFO SECURITY arm is one of the missing ones, and the most security-relevant: smb2_set_info_sec() calls set_info_sec() with no per-handle access check.
set_info_sec() (fs/smb/server/smbacl.c) re-permissions the file: it rewrites owner/group/mode via notify_change(), rewrites the POSIX ACL via set_posix_acl(), and on KSMBD_SHARE_FLAG_ACL_XATTR shares removes and rewrites the Windows security descriptor via ksmbd_vfs_set_sd_xattr(). Every other persistent-mutation arm of the sibling handler smb2_set_info_file() checks fp->daccess first (FILE_WRITE_DATA / FILE_DELETE / FILE_WRITE_EA / FILE_WRITE_ATTRIBUTES); the SECURITY arm — which mutates the access control itself — is the only one with no gate.
A client can therefore open a handle with FILE_WRITE_ATTRIBUTES only (no FILE_WRITE_DAC / FILE_WRITE_OWNER) and use SMB2_SET_INFO with InfoType SMB2_O_INFO_SECURITY to rewrite the file's DACL and owner, granting itself access the handle's daccess never carried. Unlike the FSCTL data arms this is a metadata/xattr operation, so there is no FMODE_WRITE VFS backstop — the missing fp->daccess check is the entire gate.
Setting a security descriptor is the WRITE_DAC / WRITE_OWNER operation, so require at least one of those on the handle before re-permissioning the file. -EACCES is mapped to STATUS_ACCESS_DENIED by smb2_set_info().
--- fs/smb/server/smb2pdu.c | 3 +++ 1 file changed, 3 insertions(+)
fs/smb/server/smb2pdu.c +3 -0
@@ -6623,6 +6623,9 @@ static int smb2_set_info_sec(struct ksmb fp->saccess |=FILE_SHARE_DELETE_LE ; +if (!(fp->daccess & (FILE_WRITE_DAC_LE |FILE_WRITE_OWNER_LE ))) +return -EACCES ; +return set_info_sec (fp->conn, fp->tcon, &fp->filp->f_path, pntsd, buf_len, false, true); }
Greg Kroah-Hartman
maintainerPATCH 223/480 ksmbd: run set info with opener credentials
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
commit b383bcad3d2fe634b26efbce53e22bbb5753a520 upstream.
SMB2 SET_INFO handlers call path-based VFS helpers after checking the access mask granted to the SMB handle. Those helpers perform their owner, inode permission and LSM checks using the current ksmbd worker credentials.
Run the complete SET_INFO dispatch with the credentials captured when the handle was opened. This also removes the separate security information credential setup and keeps all SET_INFO classes under one credential scope.
Direct override_creds() is used because it can nest with the request credential overrides already used by rename and link helpers.
--- fs/smb/server/smb2pdu.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
fs/smb/server/smb2pdu.c +3 -5
@@ -6638,6 +6638,7 @@ static int smb2_set_info_sec(struct ksmb*/ int smb2_set_info (struct ksmbd_work *work) { +const struct cred *saved_cred;struct smb2_set_info_req *req;struct smb2_set_info_rsp *rsp;struct ksmbd_file *fp =NULL ;
@@ -6679,6 +6680,7 @@ int smb2_set_info(struct ksmbd_work *wor goto err_out; } + saved_cred =override_creds (fp->filp->f_cred);switch (req->InfoType) {case SMB2_O_INFO_FILE :ksmbd_debug (SMB ,"GOT SMB2_O_INFO_FILE\n" );
@@ -6686,19 +6688,15 @@ int smb2_set_info(struct ksmbd_work *worbreak ;case SMB2_O_INFO_SECURITY :ksmbd_debug (SMB ,"GOT SMB2_O_INFO_SECURITY\n" ); -if (ksmbd_override_fsids (work)) { - rc = -ENOMEM ; - goto err_out; - } rc =smb2_set_info_sec (fp,le32_to_cpu (req->AdditionalInformation), (char *)req +le16_to_cpu (req->BufferOffset),le32_to_cpu (req->BufferLength)); -ksmbd_revert_fsids (work);break ;default : rc = -EOPNOTSUPP ; } +revert_creds (saved_cred);if (rc <0 ) goto err_out;
Greg Kroah-Hartman
maintainerPATCH 224/480 ksmbd: enforce FILE_READ_ATTRIBUTES on SMB_FIND_FILE_POSIX_INFORMATION
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gil Portnoy <[email protected]>
commit 20c8442dc1003f9f7bb522d3dcd81d09ea59a79e upstream.
find_file_posix_info() in smb2_query_info() returns file metadata (owner uid, group gid, mode, inode, size, allocation size, hard-link count and all four timestamps) but performs no per-handle access check. Every sibling query handler gates on the handle's granted access first -- get_file_basic_info(), get_file_all_info(), get_file_network_open_info() and get_file_attribute_tag_info() all reject a handle lacking FILE_READ_ATTRIBUTES_LE with -EACCES. The POSIX handler is gated only by the connection-scoped tcon->posix_extensions flag, which is not a per-handle authorization, so a handle opened with only FILE_WRITE_DATA is correctly denied FileBasicInformation yet is allowed the strict-superset POSIX info. Mirror the FILE_READ_ATTRIBUTES_LE gate the sibling info handlers already use.
--- fs/smb/server/smb2pdu.c | 6 ++++++ 1 file changed, 6 insertions(+)
fs/smb/server/smb2pdu.c +6 -0
@@ -5339,6 +5339,12 @@ static int find_file_posix_info(struct sint out_buf_len =sizeof (struct smb311_posix_qinfo ) +32 ;int ret; +if (!(fp->daccess &FILE_READ_ATTRIBUTES_LE )) { +pr_err ("no right to read the attributes : 0x%x\n" , + fp->daccess); +return -EACCES ; + } + ret =vfs_getattr (&fp->filp->f_path, &stat,STATX_BASIC_STATS ,AT_STATX_SYNC_AS_STAT );if (ret)
Greg Kroah-Hartman
maintainerPATCH 225/480 ksmbd: add per-handle permission check to FILE_LINK_INFORMATION
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gil Portnoy <[email protected]>
commit 13f3942f2bf45856bb751faed2f0c4618f41ca20 upstream.
The FILE_LINK_INFORMATION arm of smb2_set_info_file() calls smb2_create_link() with no per-handle fp->daccess check. On the ReplaceIfExists path smb2_create_link() unlinks an existing file at the target name (ksmbd_vfs_remove_file) and creates a hardlink (ksmbd_vfs_link); neither helper checks daccess. A handle opened with FILE_READ_DATA only (no FILE_DELETE, no FILE_WRITE_DATA) can therefore delete an arbitrary file in the share and plant a hardlink over its name.
The sibling delete/move arms in the same switch already gate:FILE_RENAME_INFORMATION and FILE_DISPOSITION_INFORMATION both require FILE_DELETE_LE; FILE_FULL_EA_INFORMATION requires FILE_WRITE_EA_LE. Gate the link arm the same way as its closest analogue (rename), since it mutates the namespace and, on replace, deletes an existing entry.
This is a sibling of commit cc57232cae23 ("ksmbd: fix FSCTL permission bypass by adding a permission check for FSCTL_SET_SPARSE").
--- fs/smb/server/smb2pdu.c | 5 +++++ 1 file changed, 5 insertions(+)
fs/smb/server/smb2pdu.c +5 -0
@@ -6572,6 +6572,11 @@ static int smb2_set_info_file(struct ksm }case FILE_LINK_INFORMATION : { +if (!(fp->daccess &FILE_DELETE_LE )) { +pr_err ("no right to delete : 0x%x\n" , fp->daccess); +return -EACCES ; + } +if (buf_len <sizeof (struct smb2_file_link_info ))return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 226/480 ksmbd: use opener credentials for delete-on-close
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
commit 52e2f21911158ec961cd5aae19c56460db382af0 upstream.
Delete-on-close can be completed by deferred or durable handle teardown, where no request work is available. Both the base-file unlink and the ADS xattr removal consequently run with the ksmbd worker credentials and can bypass filesystem permission checks.
Run both operations with the credentials captured in struct file when the handle was opened. This preserves the authenticated user's fsuid, fsgid, supplementary groups and capability restrictions at final close.
--- fs/smb/server/vfs.c | 7 +++++-- fs/smb/server/vfs_cache.c | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-)
fs/smb/server/vfs.c +5 -2
@@ -1078,13 +1078,15 @@ int ksmbd_vfs_remove_xattr(struct mnt_idint ksmbd_vfs_unlink (struct file *filp) { +const struct cred *saved_cred;int err =0 ;struct dentry *dir, *dentry = filp->f_path.dentry;struct mnt_idmap *idmap =file_mnt_idmap (filp); + saved_cred =override_creds (filp->f_cred); err =mnt_want_write (filp->f_path.mnt);if (err) -return err; + goto out_revert; dir =dget_parent (dentry); err =ksmbd_vfs_lock_parent (dir, dentry);
@@ -1104,7 +1106,8 @@ int ksmbd_vfs_unlink(struct file *filp) out:dput (dir);mnt_drop_write (filp->f_path.mnt); - +out_revert: +revert_creds (saved_cred);return err; }
fs/smb/server/vfs_cache.c +4 -0
@@ -292,10 +292,14 @@ static void __ksmbd_inode_close(struct kup_write (&ci->m_lock);if (remove_stream_xattr) { +const struct cred *saved_cred; + + saved_cred =override_creds (filp->f_cred); err =ksmbd_vfs_remove_xattr (file_mnt_idmap (filp), &filp->f_path, fp->stream.name, true); +revert_creds (saved_cred);if (err)pr_err ("remove xattr failed : %s\n" , fp->stream.name);
Greg Kroah-Hartman
maintainerPATCH 227/480 ksmbd: use opener credentials for ADS I/O
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
commit baa5e094886fffa7e6272edcb5e08be5ce28262c upstream.
Alternate data streams are stored as xattrs. Unlike regular file I/O, their read and write paths therefore call VFS xattr helpers which recheck inode permissions and LSM policy using the current task credentials.
Run ADS I/O with the credentials captured when the SMB handle was opened.
--- fs/smb/server/vfs.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
fs/smb/server/vfs.c +9 -2
@@ -278,17 +278,20 @@ out:static int ksmbd_vfs_stream_read (struct ksmbd_file *fp,char *buf,loff_t *pos,size_t count) { +const struct cred *saved_cred;ssize_t v_len;char *stream_buf =NULL ;ksmbd_debug (VFS ,"read stream data pos : %llu, count : %zd\n" , *pos, count); + saved_cred =override_creds (fp->filp->f_cred); v_len =ksmbd_vfs_getcasexattr (file_mnt_idmap (fp->filp), fp->filp->f_path.dentry, fp->stream.name, fp->stream.size, &stream_buf); +revert_creds (saved_cred);if ((int )v_len <=0 )return (int )v_len;
@@ -411,6 +414,7 @@ int ksmbd_vfs_read(struct ksmbd_work *wostatic int ksmbd_vfs_stream_write (struct ksmbd_file *fp,char *buf,loff_t *pos,size_t count) { +const struct cred *saved_cred;char *stream_buf =NULL , *wbuf;struct mnt_idmap *idmap =file_mnt_idmap (fp->filp);size_t size;
@@ -431,6 +435,7 @@ static int ksmbd_vfs_stream_write(struct count =XATTR_SIZE_MAX - *pos; } + saved_cred =override_creds (fp->filp->f_cred); v_len = ksmbd_vfs_getcasexattr(idmap, fp->filp->f_path.dentry, fp->stream.name,
@@ -439,14 +444,14 @@ static int ksmbd_vfs_stream_write(structif (v_len <0 ) {pr_err ("not found stream in xattr : %zd\n" , v_len); err = v_len; - goto out; + goto out_revert; }if (v_len < size) { wbuf =kvzalloc (size,KSMBD_DEFAULT_GFP );if (!wbuf) { err = -ENOMEM ; - goto out; + goto out_revert; }if (v_len >0 )
@@ -464,6 +469,8 @@ static int ksmbd_vfs_stream_write(struct size,0 , true); +out_revert: +revert_creds (saved_cred);if (err <0 ) goto out;else
Greg Kroah-Hartman
maintainerPATCH 228/480 smb: client: fix query directory replay double-free
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henrique Carvalho <[email protected]>
commit 9647492b5e41954be59d5157eddbcd4cdc1656f7 upstream.
A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_query_directory_init() fails before the next send, cleanup retains the previous buffer type and frees that response again.
Reset response bookkeeping before each attempt to prevent the stale free.
--- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+)
fs/smb/client/smb2pdu.c +2 -0
@@ -5668,6 +5668,8 @@ send_set_info(const unsigned int xid, st replay_again:/* reinitialize for possible replay */ + resp_buftype =CIFS_NO_BUFFER ; +memset (&rsp_iov,0 ,sizeof (rsp_iov)); flags =0 ; server =cifs_pick_channel (ses);
Greg Kroah-Hartman
maintainerPATCH 229/480 smb: client: fix query_info() replay double-free
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henrique Carvalho <[email protected]>
commit 2a88561d66eb855813cf004a0abe648bbb17de5e upstream.
A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_query_info_init() fails before the next send, cleanup retains the previous buffer type and frees that response again.
Reset response bookkeeping before each attempt to prevent the stale free.
--- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+)
fs/smb/client/smb2pdu.c +2 -0
@@ -3821,6 +3821,8 @@ query_info(const unsigned int xid, struc replay_again:/* reinitialize for possible replay */ + resp_buftype =CIFS_NO_BUFFER ; +memset (&rsp_iov,0 ,sizeof (rsp_iov)); flags =0 ; allocated = false; server =cifs_pick_channel (ses);
Greg Kroah-Hartman
maintainerPATCH 230/480 smb: client: fix double-free in SMB2_ioctl() replay
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henrique Carvalho <[email protected]>
commit f9bbadb6c94583e3b4af1afc449bfceb1d1ddec9 upstream.
A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_ioctl_init() fails before the next send, cleanup retains the previous buffer type and frees that response again.
Reset response bookkeeping before each attempt to prevent the stale free.
--- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+)
fs/smb/client/smb2pdu.c +2 -0
@@ -3425,6 +3425,8 @@ SMB2_ioctl(const unsigned int xid, struc replay_again:/* reinitialize for possible replay */ + resp_buftype =CIFS_NO_BUFFER ; +memset (&rsp_iov,0 ,sizeof (rsp_iov)); flags =0 ; server =cifs_pick_channel (ses);
Greg Kroah-Hartman
maintainerPATCH 231/480 smb: client: fix change notify replay double-free
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henrique Carvalho <[email protected]>
commit 145f820dcbb2cced374f2532f8a61a44dce4a615 upstream.
A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_notify_init() fails before the next send, cleanup retains the previous buffer type and frees that response again.
Reset response bookkeeping before each attempt to prevent the stale free.
--- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+)
fs/smb/client/smb2pdu.c +2 -0
@@ -4010,6 +4010,8 @@ SMB2_change_notify(const unsigned int xi replay_again:/* reinitialize for possible replay */ + resp_buftype =CIFS_NO_BUFFER ; +memset (&rsp_iov,0 ,sizeof (rsp_iov)); flags =0 ; server =cifs_pick_channel (ses);
Greg Kroah-Hartman
maintainerPATCH 232/480 smb: client: fix double-free in SMB2_flush() replay
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhao Zhang <[email protected]>
commit 4be31c943a3a27a5a0251dbb8f5cb89059ec3d5a upstream.
SMB2_flush() keeps its response buffer bookkeeping across replay attempts. If a replayable flush response is received and the retry then fails before cifs_send_recv() stores a replacement response, flush_exit will free the stale response pointer a second time.
Reinitialize resp_buftype and rsp_iov at the top of the replay loop so cleanup only acts on response state produced by the current attempt. This fixes a double-free without changing replay handling for successful requests.
--- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+)
fs/smb/client/smb2pdu.c +2 -0
@@ -4351,6 +4351,8 @@ SMB2_flush(const unsigned int xid, struc replay_again:/* reinitialize for possible replay */ + resp_buftype =CIFS_NO_BUFFER ; +memset (&rsp_iov,0 ,sizeof (rsp_iov)); flags =0 ; server =cifs_pick_channel (ses);
Greg Kroah-Hartman
maintainerPATCH 233/480 smb: client: fix double-free in SMB2_open() replay
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henrique Carvalho <[email protected]>
commit b55e182f2324bc6a604c21a47aa6c448f719a532 upstream.
A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_open_init() fails before the next send, cleanup retains the previous buffer type and frees that response again.
Reset response bookkeeping before each attempt to prevent the stale free.
--- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+)
fs/smb/client/smb2pdu.c +2 -0
@@ -3205,6 +3205,8 @@ SMB2_open(const unsigned int xid, struct replay_again:/* reinitialize for possible replay */ + resp_buftype =CIFS_NO_BUFFER ; +memset (&rsp_iov,0 ,sizeof (rsp_iov)); flags =0 ; server =cifs_pick_channel (ses); oparms->replay = !!(retries);
Greg Kroah-Hartman
maintainerPATCH 234/480 smb: client: fix double-free in SMB2_close() replay
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henrique Carvalho <[email protected]>
commit f96e1cdcb63ed3321142ff2fcdf784e32cda8fee upstream.
A response-bearing attempt can return a replayable error and free its response buffer. If SMB2_close_init() fails before the next send, cleanup retains the previous buffer type and frees that response again.
Reset response bookkeeping before each attempt to prevent the stale free.
--- fs/smb/client/smb2pdu.c | 2 ++ 1 file changed, 2 insertions(+)
fs/smb/client/smb2pdu.c +2 -0
@@ -3617,6 +3617,8 @@ __SMB2_close(const unsigned int xid, str replay_again:/* reinitialize for possible replay */ + resp_buftype =CIFS_NO_BUFFER ; +memset (&rsp_iov,0 ,sizeof (rsp_iov)); flags =0 ; query_attrs = false; server =cifs_pick_channel (ses);
Greg Kroah-Hartman
maintainerPATCH 235/480 smb: client: Fix next buffer leak in receive_encrypted_standard()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
commit 1c6267a1d5cf4c73b656f8181b310cbbb3e4767b upstream.
receive_encrypted_standard() allocates next_buffer before checking whether the number of compound PDUs already reached MAX_COMPOUND. If the limit check fails, the function returns immediately and the newly allocated next_buffer is not assigned to server->smallbuf/server->bigbuf, making it leaked.
Move the MAX_COMPOUND check before allocating next_buffer.
--- fs/smb/client/smb2ops.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
fs/smb/client/smb2ops.c +6 -4
@@ -5096,6 +5096,12 @@ receive_encrypted_standard(struct TCP_Se one_more: shdr = (struct smb2_hdr *)buf; next_cmd =le32_to_cpu (shdr->NextCommand); + +if (*num_mids >=MAX_COMPOUND ) { +cifs_server_dbg (VFS ,"too many PDUs in compound\n" ); +return -1 ; + } +if (next_cmd) {if (WARN_ON_ONCE (next_cmd > pdu_length))return -1 ;
@@ -5119,10 +5125,6 @@ one_more: mid_entry->resp_buf_size = server->pdu_size; } -if (*num_mids >=MAX_COMPOUND ) { -cifs_server_dbg (VFS ,"too many PDUs in compound\n" ); -return -1 ; - } bufs[*num_mids] = buf; mids[(*num_mids)++] = mid_entry;
Greg Kroah-Hartman
maintainerPATCH 236/480 smb: client: use unaligned reads in parse_posix_ctxt()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zihan Xi <[email protected]>
commit b86467cd2691192ad4809a5a6e922fc24b8e9839 upstream.
The server controls create-context DataOffset, so the POSIX context data pointer may be misaligned on strict-alignment architectures. Use get_unaligned_le32() when reading nlink, reparse_tag, and mode.
--- fs/smb/client/smb2pdu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
fs/smb/client/smb2pdu.c +3 -3
@@ -2297,9 +2297,9 @@ parse_posix_ctxt(struct create_context *memset (posix,0 ,sizeof (*posix)); - posix->nlink =le32_to_cpu (*(__le32 *)(beg +0 )); - posix->reparse_tag =le32_to_cpu (*(__le32 *)(beg +4 )); - posix->mode =le32_to_cpu (*(__le32 *)(beg +8 )); + posix->nlink =get_unaligned_le32 (beg); + posix->reparse_tag =get_unaligned_le32 (beg +4 ); + posix->mode =get_unaligned_le32 (beg +8 ); sid = beg +12 ; sid_len =posix_info_sid_size (sid, end);
Greg Kroah-Hartman
maintainerPATCH 237/480 smb: client: harden POSIX SID length parsing
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zihan Xi <[email protected]>
commit 7ad2bcf2441430bb2e918fb3ef9a90d775a6e422 upstream.
posix_info_sid_size() reads sid[1] to obtain the subauthority count, but its existing boundary check still accepts buffers with only one remaining byte. Require two bytes before reading sid[1] so all client paths that reuse the helper reject truncated POSIX SIDs safely.
--- fs/smb/client/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/smb/client/smb2pdu.c +1 -1
@@ -5230,7 +5230,7 @@ int posix_info_sid_size(const void *beg,size_t subauth;int total; -if (beg +1 > end) +if (beg +2 > end)return -1 ; subauth = *(u8 *)(beg+1 );
Greg Kroah-Hartman
maintainerPATCH 238/480 smb: client: fix atime clamp check in read completion
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
commit 0b043279e73880bee21d3b1f221bafda5af1b27e upstream.
cifs_rreq_done() updates the inode atime to current_time(inode) after a netfs read. It then preserves the CIFS rule that atime should not be older than mtime, because some applications break if atime is less than mtime. That rule only requires clamping when atime < mtime.
The current check uses the raw non-zero result of timespec64_compare(). It therefore takes the clamp path for both atime < mtime and atime > mtime. The latter is the normal case when reading an older file:
the newly recorded atime is newer than the file mtime. The completion handler then immediately moves atime back to mtime, losing the access time that was just recorded. Userspace tools that rely on atime, such as stat, find -atime, backup tools or cold-data classifiers, can therefore see a recently read CIFS file as not recently accessed.
This is easy to miss because the bug is silent: read I/O still succeeds, no error is reported, and many systems either do not check atime after reads or mount with policies such as relatime/noatime. It becomes visible when a CIFS file has an mtime older than the current time, the file is read, and the local inode atime is inspected before a later revalidation replaces the cached timestamps.
Clamp only when atime is actually older than mtime. This matches the same atime/mtime rule used when applying CIFS inode attributes.
--- fs/smb/client/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/smb/client/file.c +1 -1
@@ -302,7 +302,7 @@ static void cifs_rreq_done(struct netfs_/* we do not want atime to be less than mtime, it broke some apps */ atime =inode_set_atime_to_ts (inode,current_time (inode)); mtime =inode_get_mtime (inode); -if (timespec64_compare (&atime, &mtime)) +if (timespec64_compare (&atime, &mtime) <0 )inode_set_atime_to_ts (inode,inode_get_mtime (inode)); }
Greg Kroah-Hartman
maintainerPATCH 239/480 smb: client: mask server-provided mode to 07777 in modefromsid
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Manthey <[email protected]>
commit e3d9c7160d483fc8f9e225aafad8ecbbc43f3151 upstream.
When modefromsid is active, parse_dacl() applies the server-provided sub_auth[2] value from the NFS mode SID to cf_mode without masking to 07777. Apply the correct masking, same as in the read path.
--- fs/smb/client/cifsacl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/smb/client/cifsacl.c +1 -1
@@ -889,7 +889,7 @@ static void parse_dacl(struct smb_acl *p*/ fattr->cf_mode &= ~07777 ; fattr->cf_mode |= -le32_to_cpu (ppace[i]->sid.sub_auth[2 ]); +le32_to_cpu (ppace[i]->sid.sub_auth[2 ]) &07777 ;break ; }else {if (compare_sids (&(ppace[i]->sid), pownersid) ==0 ) {
Greg Kroah-Hartman
maintainerPATCH 240/480 writeback: fix race between cgroup_writeback_umount() and inode_switch_wbs()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baokun Li <[email protected]>
commit cba38ec4cbd3a7b8b942a8d52531a05be8a9ff0d upstream.
When a container exits, the following BUG_ON() is occasionally triggered:
==================================================================
VFS: Busy inodes after unmount of sdb (ext4)
------------[ cut here ]------------ kernel BUG at fs/super.c:695! CPU: 3 PID: 6 Comm: containerd-shim Tainted: G OE K 6.6 #1 pstate: 63400009 (nZCv daif +PAN -UAO +TCO +DIT -SSBS BTYPE=--) pc : generic_shutdown_super+0xf0/0x100 lr : generic_shutdown_super+0xf0/0x100 Call trace: generic_shutdown_super+0xf0/0x100 kill_block_super+0x20/0x48 ext4_kill_sb+0x28/0x60 deactivate_locked_super+0x54/0x130 deactivate_super+0x84/0xa0 cleanup_mnt+0xa4/0x140 __cleanup_mnt+0x18/0x28 task_work_run+0x78/0xe0 do_notify_resume+0x204/0x240 ==================================================================
The root cause is a race between cgroup_writeback_umount() and inode_switch_wbs()/cleanup_offline_cgwb(). There is a window between inode_prepare_wbs_switch() returning true and the subsequent wb_queue_isw() call. Following is the process that triggers the issue:
CPU A (umount) | CPU B (writeback)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
inode_switch_wbs/cleanup_offline_cgwb
atomic_inc(&isw_nr_in_flight)
inode_prepare_wbs_switch
-> passes SB_ACTIVE check
__iget(inode)
generic_shutdown_super
sb->s_flags &= ~SB_ACTIVE
cgroup_writeback_umount(sb)
smp_mb()
atomic_read(&isw_nr_in_flight)
rcu_barrier()
-> no pending RCU callbacks
flush_workqueue(isw_wq)
-> nothing queued, returns
evict_inodes(sb)
-> Inode skipped as isw still holds a ref.
sop->put_super(sb)
/* destroys percpu counters */
-> VFS: Busy inodes after unmount!
wb_queue_isw()
queue_work(isw_wq, ...)
/* later in work function */
inode_switch_wbs_work_fn
process_inode_switch_wbs
iput() -> evict
percpu_counter_dec() // UAF!Fix this by extending the RCU read-side critical section in inode_switch_wbs() and cleanup_offline_cgwb() to cover from inode_prepare_wbs_switch() through wb_queue_isw(). Since there is no sleep in this window, rcu_read_lock() can be used. Then add a synchronize_rcu() in cgroup_writeback_umount() before the existing rcu_barrier(), so that all in-flight switchers that have passed the SB_ACTIVE check have completed queue_work() before flush_workqueue() is called.
The existing rcu_barrier() is intentionally retained so this fix can be backported unchanged to stable kernels (5.10.y, 6.6.y, ...) that still queue switches via queue_rcu_work(). It is a no-op on current mainline (since commit e1b849cfa6b6 ("writeback: Avoid contention on wb->list_lock when switching inodes")) and is removed in a follow-up patch.
--- fs/fs-writeback.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-)
fs/fs-writeback.c +29 -2
@@ -650,12 +650,19 @@ static void inode_switch_wbs(struct inodatomic_inc (&isw_nr_in_flight); -/* find and pin the new wb */ +/* +* Paired with synchronize_rcu() in cgroup_writeback_umount(): +* holding rcu_read_lock across inode_prepare_wbs_switch() +* (covering the SB_ACTIVE check and the inode grab) and +* wb_queue_isw() ensures synchronize_rcu() cannot return until +* the work is queued, so the subsequent flush_workqueue() will +* wait for the switch. +*/ rcu_read_lock (); +/* find and pin the new wb */ memcg_css =css_from_id (new_wb_id, &memory_cgrp_subsys);if (memcg_css && !css_tryget (memcg_css)) memcg_css =NULL ; -rcu_read_unlock ();if (!memcg_css) goto out_free;
@@ -671,9 +678,11 @@ static void inode_switch_wbs(struct inodtrace_inode_switch_wbs_queue (inode->i_wb, new_wb,1 );wb_queue_isw (new_wb, isw); +rcu_read_unlock ();return ; out_free: +rcu_read_unlock ();atomic_dec (&isw_nr_in_flight);if (new_wb)wb_put (new_wb);
@@ -732,6 +741,14 @@ bool cleanup_offline_cgwb(struct bdi_wri new_wb = &wb->bdi->wb;/* wb_get() is noop for bdi's wb */ nr =0 ; +/* +* Paired with synchronize_rcu() in cgroup_writeback_umount(). +* Holding rcu_read_lock across the SB_ACTIVE check, the inode grab +* and wb_queue_isw() ensures synchronize_rcu() cannot return until +* the work is queued, so the subsequent flush_workqueue() will wait +* for the switch. +*/ +rcu_read_lock ();spin_lock (&wb->list_lock);/* * In addition to the inodes that have completed writeback, also switch
@@ -749,6 +766,7 @@ bool cleanup_offline_cgwb(struct bdi_wri/* no attached inodes? bail out */ if (nr ==0 ) { +rcu_read_unlock ();atomic_dec (&isw_nr_in_flight);wb_put (new_wb);kfree (isw);
@@ -757,6 +775,7 @@ bool cleanup_offline_cgwb(struct bdi_writrace_inode_switch_wbs_queue (wb, new_wb, nr);wb_queue_isw (new_wb, isw); +rcu_read_unlock ();return restart; }
@@ -1211,6 +1230,14 @@ void cgroup_writeback_umount(struct supeif (atomic_read (&isw_nr_in_flight)) {/* +* Paired with rcu_read_lock() in inode_switch_wbs() and +* cleanup_offline_cgwb(). synchronize_rcu() waits for any +* in-flight switcher that already passed the SB_ACTIVE check +* to finish queueing its work, so flush_workqueue() below +* will then drain it. +*/ +synchronize_rcu (); +/* * Use rcu_barrier() to wait for all pending callbacks to * ensure that all in-flight wb switches are in the workqueue. */
Greg Kroah-Hartman
maintainerPATCH 241/480 OPP: of: Fix potential memory leak in opp_parse_supplies()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 69f888381d2ecbe18ed9f112c096f8fd3623db98 upstream.
The memory allocated for microvolt, microamp and microwatt is not freed in one of the paths in opp_parse_supplies() which returns directly. Fix that by adding a goto to the error unwind ladder.
--- drivers/opp/of.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/opp/of.c +2 -1
@@ -669,7 +669,7 @@ static int opp_parse_supplies(struct dev*/ if (unlikely (opp_table->regulator_count ==-1 )) { opp_table->regulator_count =0 ; -return 0 ; + goto free_microwatt; }for (i =0 , j =0 ; i < opp_table->regulator_count; i++) {
@@ -692,6 +692,7 @@ static int opp_parse_supplies(struct dev opp->supplies[i].u_watt = microwatt[i]; } +free_microwatt:kfree (microwatt); free_microamp:kfree (microamp);
Greg Kroah-Hartman
maintainerPATCH 242/480 cpufreq: qcom-cpufreq-hw: Fix possible double free
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit bcb8889c4981fdde42d4fd2c29a77d510fe21da2 upstream.
qcom_cpufreq.data is allocated with devm_kzalloc() in probe() as an array of per-domain data. qcom_cpufreq_hw_cpu_init() stores a pointer to one element of this array in policy->driver_data.
qcom_cpufreq_hw_cpu_exit() currently calls kfree() on policy->driver_data. This is not valid because the memory is devm-managed. For the first domain, this can free the devm-managed allocation while the devres entry is still active, leading to a possible double free when the platform device is later detached. For other domains, the pointer may refer to an element inside the array rather than the allocation base.
Remove the kfree(data) call and let devres release qcom_cpufreq.data.
This issue was found by a static analysis tool I am developing.
--- drivers/cpufreq/qcom-cpufreq-hw.c | 1 - 1 file changed, 1 deletion(-)
drivers/cpufreq/qcom-cpufreq-hw.c +0 -1
@@ -578,7 +578,6 @@ static void qcom_cpufreq_hw_cpu_exit(strdev_pm_opp_of_cpumask_remove_table (policy->related_cpus);qcom_cpufreq_hw_lmh_exit (data);kfree (policy->freq_table); -kfree (data); }static void qcom_cpufreq_ready (struct cpufreq_policy *policy)
Greg Kroah-Hartman
maintainerPATCH 243/480 firmware_loader: fix device reference leak in firmware_upload_register()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit 896df22ee57648b0c505bd76ddbc6b2341834696 upstream.
firmware_upload_register()
-> fw_create_instance()
-> device_initialize()
After fw_create_instance() succeeds, the lifetime of the embedded struct device is expected to be managed through the device core reference counting, since fw_create_instance() has already called device_initialize().
In firmware_upload_register(), if alloc_lookup_fw_priv() fails after fw_create_instance() succeeds, the code reaches free_fw_sysfs and frees fw_sysfs directly instead of releasing the device reference with put_device(). This may leave the reference count of the embedded struct device unbalanced, resulting in a refcount leak.
The issue was identified by a static analysis tool I developed and confirmed by manual review. Fix this by using put_device(fw_dev) in the failure path and letting fw_dev_release() handle the final cleanup, instead of freeing the instance directly from the error path.
--- drivers/base/firmware_loader/sysfs_upload.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
drivers/base/firmware_loader/sysfs_upload.c +3 -5
@@ -341,7 +341,6 @@ firmware_upload_register(struct module * goto free_fw_upload_priv; } fw_upload->priv = fw_sysfs; - fw_sysfs->fw_upload_priv = fw_upload_priv; fw_dev = &fw_sysfs->dev; ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, NULL, 0, 0,
@@ -349,10 +348,12 @@ firmware_upload_register(struct module *if (ret !=0 ) {if (ret >0 ) ret = -EINVAL ; - goto free_fw_sysfs; +put_device (fw_dev); + goto free_fw_upload_priv; } fw_priv->is_paged_buf = true; fw_sysfs->fw_priv = fw_priv; + fw_sysfs->fw_upload_priv = fw_upload_priv; ret =device_add (fw_dev);if (ret) {
@@ -363,9 +364,6 @@ firmware_upload_register(struct module *return fw_upload; -free_fw_sysfs: -kfree (fw_sysfs); - free_fw_upload_priv:kfree (fw_upload_priv);
Greg Kroah-Hartman
maintainerPATCH 244/480 libfs: set SB_I_NOEXEC and SB_I_NODEV by default in init_pseudo()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: John Hubbard <[email protected]>
commit 6de2aeffabaafaeda819e60ec8d04f199711e11a upstream.
Since commit 1e7ab6f67824 ("anon_inode: rework assertions"), path_noexec() warns when an anonymous-inode file is mmap'd from a superblock that has not set SB_I_NOEXEC. dma-buf backs its files this way and never set the flag, so mmap of any exported buffer trips the warning on a CONFIG_DEBUG_VFS=y kernel:
WARNING: CPU: 11 PID: 121813 at fs/exec.c:118 path_noexec+0x47/0x50 do_mmap+0x2b5/0x680 vm_mmap_pgoff+0x129/0x210 ksys_mmap_pgoff+0x177/0x240 __x64_sys_mmap+0x33/0x70
init_pseudo() sets up internal SB_NOUSER mounts that are never path-reachable. Set both flags here so every pseudo filesystem gets them by default instead of each caller setting them.
SB_I_NODEV is inert for unreachable mounts. SB_I_NOEXEC has one visible effect: an executable mapping of a pseudo-fs fd, such as a dma-buf, now fails with -EPERM, which is the invariant the assertion enforces. No in-tree caller maps these executable.
Reproduce on CONFIG_DEBUG_VFS=y:
make -C tools/testing/selftests/dmabuf-heaps
sudo ./tools/testing/selftests/dmabuf-heaps/dmabuf-heap -t system
--- fs/libfs.c | 1 + 1 file changed, 1 insertion(+)
fs/libfs.c +1 -0
@@ -721,6 +721,7 @@ struct pseudo_fs_context *init_pseudo(st fc->fs_private = ctx; fc->ops = &pseudo_fs_context_ops; fc->sb_flags |=SB_NOUSER ; + fc->s_iflags |=SB_I_NOEXEC |SB_I_NODEV ; fc->global = true; }return ctx;
Greg Kroah-Hartman
maintainerPATCH 245/480 perf/x86/intel/uncore: Defer ADL global PMON enable to enable_box()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zide Chen <[email protected]>
commit 9a0bb848a37150aeccc10088e141339917d995dc upstream.
On some Raptor Cove CPUs, enabling uncore PMON globally at driver init may increase power consumption even when no perf events are in use.
Drop adl_uncore_msr_init_box() and defer programming the global control register to enable_box(), so it is only set when a box is actually used.
IMC and IMC freerunning counters use a separate control path and are unaffected.
--- arch/x86/events/intel/uncore_snb.c | 7 ------- 1 file changed, 7 deletions(-)
arch/x86/events/intel/uncore_snb.c +0 -7
@@ -539,12 +539,6 @@ void tgl_uncore_cpu_init(void) skl_uncore_msr_ops.init_box = rkl_uncore_msr_init_box; } -static void adl_uncore_msr_init_box (struct intel_uncore_box *box) -{ -if (box->pmu->pmu_idx ==0 ) -wrmsrq (ADL_UNC_PERF_GLOBAL_CTL ,SNB_UNC_GLOBAL_CTL_EN ); -} -static void adl_uncore_msr_enable_box (struct intel_uncore_box *box) {wrmsrq (ADL_UNC_PERF_GLOBAL_CTL ,SNB_UNC_GLOBAL_CTL_EN );
@@ -563,7 +557,6 @@ static void adl_uncore_msr_exit_box(stru }static struct intel_uncore_ops adl_uncore_msr_ops = { - .init_box = adl_uncore_msr_init_box, .enable_box = adl_uncore_msr_enable_box, .disable_box = adl_uncore_msr_disable_box, .exit_box = adl_uncore_msr_exit_box,
Greg Kroah-Hartman
maintainerPATCH 246/480 cpufreq: intel_pstate: Sync policy->cur during CPU offline
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fushuai Wang <[email protected]>
commit bcbdaa1086c25a8a5d48e04e1b82fdfb0682b681 upstream.
When a CPU goes offline with HWP disabled, intel_pstate_set_min_pstate() sets the MSR_IA32_PERF_CTL to minimum frequency to prevent SMT siblings from being restricted. However, the policy->cur value was not updated, leaving it at the previous value.
When the CPU comes back online, governor->limits() checks if target_freq equals policy->cur and skips the frequency adjustment if they match. Since policy->cur still holds the previous value, the governor does not call cpufreq_driver->target to update MSR_IA32_PERF_CTL.
Fix this by synchronizing policy->cur with the hardware state when setting minimum pstate during CPU offline.
--- drivers/cpufreq/intel_pstate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/cpufreq/intel_pstate.c +4 -2
@@ -3016,10 +3016,12 @@ static int intel_cpufreq_cpu_offline(str* from getting to lower performance levels, so force the minimum * performance on CPU offline to prevent that from happening. */ -if (hwp_active) +if (hwp_active) {intel_pstate_hwp_offline (cpu); -else + }else {intel_pstate_set_min_pstate (cpu); + policy->cur = cpu->pstate.min_freq; + }intel_pstate_exit_perf_limits (policy);
Greg Kroah-Hartman
maintainerPATCH 247/480 sched/rt: Have RT_PUSH_IPI be default off for non PREEMPT_RT
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Steven Rostedt <[email protected]>
commit dd29c017aed628076e915fe4cdfb5392fd4c5cab upstream.
RT migration is done aggressively. When a CPU schedules out a high priority RT task for a lower priority task, it will look to see if there's any RT tasks that are waiting to run on another CPU that is of higher priority than the task this CPU is about to run. If it finds one, it will pull that task over to the CPU and allow it to run there instead.
Normally, this pulling is done by looking at the RT overloaded mask (rto) which contains all the CPUs in the scheduler domain with RT tasks that are waiting to run due to a higher priority RT task currently running on their CPU. The CPU that is about to schedule a lower priority task will grab the rq lock of the overloaded CPU and move the RT task from that CPU's runqueue to the local one and schedule the higher priority RT task.
This caused issues when a lot of CPUs would schedule a lower priority task at the same time. They would all try to grab the same runqueue lock of the CPU with the overloaded RT tasks. Only the first CPU that got in will get that task. All the others would wait until they got the runqueue lock and see there's nothing to pull and do nothing. On systems with lots of CPUs, this caused a large latency (up to 500us) which is beyond what PREEMPT_RT is to allow.
The solution to that was to create an RT_PUSH_IPI logic. When any CPU wanted to pull a task, instead of grabbing the runqueue lock of the overloaded CPU, it would start by sending an IPI to the overloaded CPU, and that IPI handler would have the CPU with the waiting RT task do a push instead. Then that handler would send an IPI to the next CPU with overloaded RT tasks, and so on. Note, after the first CPU starts this process, if another CPU wanted to do a pull, it would see that the process has already begun and would only increment a counter to have the IPIs continue again.
The RT_PUSH_IPI solved the latency problem with PREEMPT_RT but could cause a new issue with non PREEMPT_RT. Namely, softirqs run in a threaded context on PREEMPT_RT but they can run in an interrupt context in non-RT.
If an IPI lands on a CPU that has just woken up multiple RT tasks and the current CPU is running a non RT or a low priority RT task, instead of doing a push, it would simply do a schedule on that CPU. But if a softirq was also executing on this CPU, the schedule would need to wait until the softirq finished. Until then, the CPU would still be considered overloaded as there are RT tasks still waiting to run on it.
A live lock occurred on a workload that was doing heavy networking traffic on a large machine where the softirqs would run 500us out of 750us. And it would also be waking up RT tasks, causing the RT pull logic to be constantly executed.
When a softirq triggered on a CPU with RT tasks queued but not running yet, and the other CPUs would see this CPU as being overloaded, they would send an IPI over to it. The CPU would notice that the waiting RT tasks are of higher priority than the currently running task and simply schedule that CPU instead. But because the softirq was executing, before it could schedule, it would receive another IPI to do the same. The amount of IPIs would slow down the currently running softirq so much that before it could return back to task context, it would execute another softirq never allowing the CPU to schedule. This live locked that CPU.
As RT_PUSH_IPI was created to help PREEMPT_RT, make it default off if PREEMPT_RT is not enabled.
--- kernel/sched/features.h | 8 ++++++++ 1 file changed, 8 insertions(+)
kernel/sched/features.h +8 -0
@@ -103,8 +103,16 @@ SCHED_FEAT(WARN_DOUBLE_CLOCK, false)* rq lock and possibly create a large contention, sending an * IPI to that CPU and let that CPU push the RT task to where * it should go may be a better scenario. +* +* This is best for PREEMPT_RT, but for non-RT it can cause issues +* when preemption is disabled for long periods of time. Have +* it only default enabled for PREEMPT_RT. */ +# ifdef CONFIG_PREEMPT_RT SCHED_FEAT(RT_PUSH_IPI, true) +# else +SCHED_FEAT(RT_PUSH_IPI, false) +# endif #endif SCHED_FEAT (RT_RUNTIME_SHARE , false)
Greg Kroah-Hartman
maintainerPATCH 248/480 cpufreq: Fix hotplug-suspend race during reboot
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tianxiang Chen <[email protected]>
commit a9029dd55696c651ee46912afa2a166fa456bb3e upstream.
During system reboot, cpufreq_suspend() is called via the kernel_restart() -> device_shutdown() path. Unlike the normal system suspend path, the reboot path does not call freeze_processes(), so userspace processes and kernel threads remain active.
This allows CPU hotplug operations to run concurrently with cpufreq_suspend(). The original code has no synchronization with CPU hotplug, leading to a race condition where governor_data can be freed by the hotplug path while cpufreq_suspend() is still accessing it, resulting in a null pointer dereference:
Unable to handle kernel NULL pointer dereference Call Trace: do_kernel_fault+0x28/0x3c cpufreq_suspend+0xdc/0x160 device_shutdown+0x18/0x200 kernel_restart+0x40/0x80 arm64_sys_reboot+0x1b0/0x200
Fix this by adding cpus_read_lock()/cpus_read_unlock() to cpufreq_suspend() to block CPU hotplug operations while suspend is in progress.
--- drivers/cpufreq/cpufreq.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/cpufreq/cpufreq.c +2 -0
@@ -1973,6 +1973,7 @@ void cpufreq_suspend(void)if (!cpufreq_driver)return ; +cpus_read_lock ();if (!has_target () && !cpufreq_driver->suspend) goto suspend;
@@ -1992,6 +1993,7 @@ void cpufreq_suspend(void) suspend: cpufreq_suspended = true; +cpus_read_unlock (); }/**
Greg Kroah-Hartman
maintainerPATCH 249/480 cpufreq: pcc: fix use-after-free and double free in _OSC evaluation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuho Choi <[email protected]>
commit 266d3dd8b757b48a576e90f018b51f7b7563cc32 upstream.
pcc_cpufreq_do_osc() calls acpi_evaluate_object() twice for the two-phase _OSC negotiation. Between the two calls it freed output.pointer but left output.length unchanged. Since acpi_evaluate_object() treats a non-zero length with a non-NULL pointer as an existing buffer to write into, the second call wrote into freed memory (use-after-free). The subsequent kfree(output.pointer) at out_free then freed the same pointer a second time (double free).
Reset output.pointer to NULL and output.length to ACPI_ALLOCATE_BUFFER after freeing the first result, so ACPICA allocates a fresh buffer for each phase independently.
--- drivers/cpufreq/pcc-cpufreq.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/cpufreq/pcc-cpufreq.c +2 -0
@@ -352,6 +352,8 @@ static int __init pcc_cpufreq_do_osc(acp }kfree (output.pointer); + output.pointer =NULL ; + output.length =ACPI_ALLOCATE_BUFFER ; capabilities[0 ] =0x0 ; capabilities[1 ] =0x1 ;
Greg Kroah-Hartman
maintainerPATCH 250/480 posix-cpu-timers: Fix pid refcount leak in do_cpu_nanosleep() error path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: WenTao Liang <[email protected]>
commit 87bd2ad568e15b90d5f7d4bcd70342d05dad649c upstream.
In do_cpu_nanosleep(), posix_cpu_timer_create() takes a pid reference via get_pid() and stores it in timer.it.cpu.pid. If the subsequent posix_cpu_timer_set() call fails, the function returns immediately without calling posix_cpu_timer_del() to release the pid reference, causing a leak.
Fix it by calling posix_cpu_timer_del() before the unlock-and-return on the error path, consistent with the other exit paths in the same function.
--- kernel/time/posix-cpu-timers.c | 1 + 1 file changed, 1 insertion(+)
kernel/time/posix-cpu-timers.c +1 -0
@@ -1504,6 +1504,7 @@ static int do_cpu_nanosleep(const clockispin_lock_irq (&timer.it_lock); error =posix_cpu_timer_set (&timer, flags, &it,NULL );if (error) { +posix_cpu_timer_del (&timer);spin_unlock_irq (&timer.it_lock);return error; }
Greg Kroah-Hartman
maintainerPATCH 251/480 time/jiffies: Register jiffies clocksource before usage
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Gleixner <[email protected]>
commit f24df84cbe05e4471c04ac4b921fc0340bbc7752 upstream.
Teddy reported that a XEN HVM has a long boot delay, which was bisected to the recent enhancements to the negative motion detection. It turned out that the jiffies clocksource is used in early boot before it is registered, which leaves the max_delta_raw field at zero. That causes the read out to be clamped to the max delta of 0, which means time is not making progress.
Cure it by ensuring that it is initialized before its first usage in timekeeping_init().
--- kernel/time/jiffies.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
kernel/time/jiffies.c +5 -6
@@ -61,15 +61,14 @@ EXPORT_SYMBOL(get_jiffies_64);EXPORT_SYMBOL (jiffies); -static int __init init_jiffies_clocksource (void ) -{ -return __clocksource_register (&clocksource_jiffies); -} - -core_initcall (init_jiffies_clocksource); +static bool cs_jiffies_registered__initdata ;struct clocksource *__init __weak clocksource_default_clock (void ) { +if (!cs_jiffies_registered) { +__clocksource_register (&clocksource_jiffies); + cs_jiffies_registered = true; + }return &clocksource_jiffies; }
Greg Kroah-Hartman
maintainerPATCH 252/480 clocksource/drivers/timer-tegra186: Fix support for multiple watchdog instances
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kartik Rajput <[email protected]>
commit ca57bf46e7a94f8c53d05c376df9fcfdcb482100 upstream.
Tegra SoCs support multiple watchdogs; currently only one (WDT0) is used. When multiple watchdogs are registered, tegra186_wdt_enable() overwrites the TKEIE(x) register, discarding any existing watchdog interrupt enable bits. As a result, enabling one watchdog inadvertently disables interrupts for the others.
Fix this by preserving the existing TKEIE(x) value and updating it using a read-modify-write sequence.
--- drivers/clocksource/timer-tegra186.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/clocksource/timer-tegra186.c +2 -1
@@ -149,7 +149,8 @@ static void tegra186_wdt_enable(struct tu32 value;/* unmask hardware IRQ, this may have been lost across powergate */ - value =TKEIE_WDT_MASK (wdt->index,1 ); + value =readl (tegra->regs +TKEIE (wdt->tmr->hwirq)); + value |=TKEIE_WDT_MASK (wdt->index,1 );writel (value, tegra->regs +TKEIE (wdt->tmr->hwirq));/* clear interrupt */
Greg Kroah-Hartman
maintainerPATCH 253/480 s390: Revert support for DCACHE_WORD_ACCESS
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Heiko Carstens <[email protected]>
commit 37540b8c287fc817bdbd0c62bb75ad6eab0e5d03 upstream.
load_unaligned_zeropad() reads eight bytes from unaligned addresses and may cross page boundaries. It handles exceptions which may happen if reading from the second page results in an exception.
For pages which are donated to the Ultravisor for secure execution purposes the do_secure_storage_access() exception handler however does not handle such exceptions correctly. Such an exception may result in an endless exception loop which will never be resolved.
An attempt to fix this [1] turned out to be not sufficient. For now revert load_unaligned_zeropad() until this problem has been resolved in a proper way.
Note that the implementation of load_unaligned_zeropad() itself is correct. The revert is just a temporary workaround until there is complete fix for secure storage access exceptions.
[1] commit b00be77302d7 ("s390/mm: Add missing secure storage access fixups for donated memory")
--- arch/s390/Kconfig | 1 - arch/s390/include/asm/asm-extable.h | 4 ---- arch/s390/include/asm/word-at-a-time.h | 22 ---------------------- arch/s390/mm/extable.c | 18 ------------------ 4 files changed, 45 deletions(-)
arch/s390/Kconfig +0 -1
@@ -161,7 +161,6 @@ config S390 select ARCH_WANTS_THP_SWAP select BUILDTIME_TABLE_SORT select CLONE_BACKWARDS2 - select DCACHE_WORD_ACCESS if !KMSAN select DYNAMIC_FTRACE if FUNCTION_TRACER select FUNCTION_ALIGNMENT_8B if CC_IS_GCC select FUNCTION_ALIGNMENT_16B if !CC_IS_GCC
arch/s390/include/asm/asm-extable.h +0 -4
@@ -12,7 +12,6 @@#define EX_TYPE_UA_FAULT 3#define EX_TYPE_UA_LOAD_REG 5#define EX_TYPE_UA_LOAD_REGPAIR 6 -#define EX_TYPE_ZEROPAD 7#define EX_TYPE_FPC 8#define EX_TYPE_UA_MVCOS_TO 9#define EX_TYPE_UA_MVCOS_FROM 10
@@ -80,9 +79,6 @@#define EX_TABLE_UA_LOAD_REGPAIR (_fault, _target, _regerr, _regzero) \ __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_UA_LOAD_REGPAIR, _regerr, _regzero, 0) -#define EX_TABLE_ZEROPAD (_fault, _target, _regdata, _regaddr) \ - __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_ZEROPAD, _regdata, _regaddr, 0) -#define EX_TABLE_FPC (_fault, _target) \ __EX_TABLE(__ex_table, _fault, _target, EX_TYPE_FPC, __stringify(%%r0), __stringify(%%r0), 0)
arch/s390/include/asm/word-at-a-time.h +0 -22
@@ -4,7 +4,6 @@#include <linux/bitops.h> #include <linux/wordpart.h> -#include <asm/asm-extable.h> #include <asm/bitsperlong.h> struct word_at_a_time {
@@ -41,25 +40,4 @@ static inline unsigned long zero_bytemas return ~1UL << data; } -/* -* Load an unaligned word from kernel space. -* -* In the (very unlikely) case of the word being a page-crosser -* and the next page not being mapped, take the exception and -* return zeroes in the non-existing part. -*/ -static inline unsigned long load_unaligned_zeropad (const void *addr) -{ -unsigned long data; - - asm_inline volatile( -"0: lg %[data],0(%[addr])\n" -"1: nopr %%r7\n" - EX_TABLE_ZEROPAD(0b, 1b, %[data], %[addr]) - EX_TABLE_ZEROPAD(1b, 1b, %[data], %[addr]) - : [data] "=d" (data) - : [addr] "a" (addr), "m" (*(unsigned long *)addr)); -return data; -} -#endif /* _ASM_WORD_AT_A_TIME_H */
arch/s390/mm/extable.c +0 -18
@@ -50,22 +50,6 @@ static bool ex_handler_ua_load_reg(constreturn true; } -static bool ex_handler_zeropad (const struct exception_table_entry *ex,struct pt_regs *regs) -{ -unsigned int reg_addr =FIELD_GET (EX_DATA_REG_ADDR , ex->data); -unsigned int reg_data =FIELD_GET (EX_DATA_REG_ERR , ex->data); -unsigned long data, addr, offset; - - addr = regs->gprs[reg_addr]; - offset = addr & (sizeof (unsigned long ) -1 ); - addr &= ~(sizeof (unsigned long ) -1 ); - data = *(unsigned long *)addr; - data <<=BITS_PER_BYTE * offset; - regs->gprs[reg_data] = data; - regs->psw.addr =extable_fixup (ex); -return true; -} -static bool ex_handler_fpc (const struct exception_table_entry *ex,struct pt_regs *regs) {fpu_sfpc (0 );
@@ -134,8 +118,6 @@ bool fixup_exception(struct pt_regs *regreturn ex_handler_ua_load_reg (ex, false, regs);case EX_TYPE_UA_LOAD_REGPAIR :return ex_handler_ua_load_reg (ex, true, regs); -case EX_TYPE_ZEROPAD : -return ex_handler_zeropad (ex, regs);case EX_TYPE_FPC :return ex_handler_fpc (ex, regs);case EX_TYPE_UA_MVCOS_TO :
Greg Kroah-Hartman
maintainerPATCH 254/480 perf/arm-cmn: Fix DVM node events
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Robin Murphy <[email protected]>
commit 5936245125f78d896fdb1bbc2ae79213e28a6579 upstream.
The new DVM node events added in CMN-700 also apply to CMN S3; fix the model encoding so that we can expose the aliases and handle occupancy filtering on newer CMNs too.
--- drivers/perf/arm-cmn.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-)
drivers/perf/arm-cmn.c +12 -11
@@ -197,13 +197,14 @@enum cmn_model {CMN600 =1 ,CMN650 =2 , -CMN700 =4 , -CI700 =8 , +CI700 =4 , +CMN700 =8 ,CMNS3 =16 ,/* ...and then we can use bitmap tricks for commonality */ CMN_ANY =-1 ,NOT_CMN600 =-2 , -CMN_650ON =CMN650 |CMN700 |CMNS3 , +CMN_700ON = ~(CMN700 -1 ), +CMN_650ON =CMN_700ON |CMN650 , };/* Actual part numbers and revision IDs defined by the hardware */
@@ -919,14 +920,14 @@ static struct attribute *arm_cmn_event_aCMN_EVENT_DVM (NOT_CMN600 , txsnp_stall,0x0a ),CMN_EVENT_DVM (NOT_CMN600 , trkfull,0x0b ),CMN_EVENT_DVM_OCC (NOT_CMN600 , trk_occupancy,0x0c ), -CMN_EVENT_DVM_OCC (CMN700 , trk_occupancy_cxha,0x0d ), -CMN_EVENT_DVM_OCC (CMN700 , trk_occupancy_pdn,0x0e ), -CMN_EVENT_DVM (CMN700 , trk_alloc,0x0f ), -CMN_EVENT_DVM (CMN700 , trk_cxha_alloc,0x10 ), -CMN_EVENT_DVM (CMN700 , trk_pdn_alloc,0x11 ), -CMN_EVENT_DVM (CMN700 , txsnp_stall_limit,0x12 ), -CMN_EVENT_DVM (CMN700 , rxsnp_stall_starv,0x13 ), -CMN_EVENT_DVM (CMN700 , txsnp_sync_stall_op,0x14 ), +CMN_EVENT_DVM_OCC (CMN_700ON , trk_occupancy_cxha,0x0d ), +CMN_EVENT_DVM_OCC (CMN_700ON , trk_occupancy_pdn,0x0e ), +CMN_EVENT_DVM (CMN_700ON , trk_alloc,0x0f ), +CMN_EVENT_DVM (CMN_700ON , trk_cxha_alloc,0x10 ), +CMN_EVENT_DVM (CMN_700ON , trk_pdn_alloc,0x11 ), +CMN_EVENT_DVM (CMN_700ON , txsnp_stall_limit,0x12 ), +CMN_EVENT_DVM (CMN_700ON , rxsnp_stall_starv,0x13 ), +CMN_EVENT_DVM (CMN_700ON , txsnp_sync_stall_op,0x14 ),CMN_EVENT_HNF (CMN_ANY , cache_miss,0x01 ), CMN_EVENT_HNF(CMN_ANY, slc_sf_cache_access, 0x02),
Greg Kroah-Hartman
maintainerPATCH 255/480 X.509: Fix validation of ASN.1 certificate header
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lukas Wunner <[email protected]>
commit 3b626ba431c4501512ad07549310685e07fe4706 upstream.
x509_load_certificate_list() seeks to enforce that a certificate starts with 0x30 0x82 (ASN.1 SEQUENCE tag followed by a length of more than 256 and less than 65535 bytes).
But it only enforces that *either* of those two byte values are present, instead of checking for the *conjunction* of the two values. Fix it.
--- crypto/asymmetric_keys/x509_loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
crypto/asymmetric_keys/x509_loader.c +1 -1
@@ -20,7 +20,7 @@ int x509_load_certificate_list(const u8*/ if (end - p <4 ) goto dodgy_cert; -if (p[0 ] !=0x30 && +if (p[0 ] !=0x30 || p[1 ] !=0x82 ) goto dodgy_cert; plen = (p[2 ] <<8 ) | p[3 ];
Greg Kroah-Hartman
maintainerPATCH 256/480 mm/slab: do not limit zeroing to orig_size when only red zoning is enabled
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vlastimil Babka (SUSE) <[email protected]>
commit 648927ceb84021a25a0fbd5673740956f318d534 upstream.
When init (zeroing) on allocation is requested, for kmalloc() we generally have to zero the full object size even if a smaller size is requested, in order to provide krealloc()'s __GFP_ZERO guarantees.
But if we track the requested size, krealloc() uses that information to do the right thing, so we can zero only the requested size. With red zoning also enabled, any extra size became part of the red zone, so it must not be zeroed and thus we must zero only the requested size.
However the current check is imprecise, and will trigger also when only SLAB_RED_ZONE is enabled without SLAB_STORE_USER (which enables tracking the requested size). This means enabling red zoning alone can compromise krealloc()'s __GFP_ZERO contract.
Fix this by using slub_debug_orig_size() instead, which is the exact check for whether the requested size is tracked. We don't need to care if red zoning is also enabled or not. Also update and expand the comment accordingly.
--- mm/slub.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-)
mm/slub.c +10 -8
@@ -5004,15 +5004,17 @@ bool slab_post_alloc_hook(struct kmem_cagfp_t init_flags = flags & gfp_allowed_mask;/* -* For kmalloc object, the allocated memory size(object_size) is likely -* larger than the requested size(orig_size). If redzone check is -* enabled for the extra space, don't zero it, as it will be redzoned -* soon. The redzone operation for this extra space could be seen as a -* replacement of current poisoning under certain debug option, and -* won't break other sanity checks. +* For kmalloc object, the allocated size (object_size) can be larger +* than the requested size (orig_size). We however need to zero the +* whole object_size to handle possible later krealloc() with +*__GFP_ZERO properly. +* +* But if we keep track of the requested size, krealloc() uses that +* information. Additionally if red zoning is enabled, the extra space +* is also red zone, so we should not overwrite it. So limit zeroing to +* orig_size if we track it. */ -if (kmem_cache_debug_flags (s,SLAB_STORE_USER |SLAB_RED_ZONE ) && - (s->flags &SLAB_KMALLOC )) +if (slub_debug_orig_size (s)) zero_size = orig_size;/*
Greg Kroah-Hartman
maintainerPATCH 257/480 tools/mm/slabinfo: Fix trace disable logic inversion
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xuewen Wang <[email protected]>
commit 235ab68d67eadbef1fdbfb771f21f5bacc77a2ae upstream.
The disable trace path in slab_debug() had a logic error where it would set trace=1 instead of trace=0. This made trace functionality permanently enabled once turned on for any slab cache.
WARNING: From:/Signed-off-by: email address mismatch: 'From: wangxuewen <[email protected]>' != 'Signed-off-by: wangxuewen <[email protected]>'
--- tools/mm/slabinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/mm/slabinfo.c +1 -1
@@ -798,7 +798,7 @@ static void slab_debug(struct slabinfo *fprintf (stderr,"%s can only enable trace for one slab at a time\n" , s->name); }if (!tracing && s->trace) -set_obj (s,"trace" ,1 ); +set_obj (s,"trace" ,0 ); }static void totals (void )
Greg Kroah-Hartman
maintainerPATCH 258/480 tools/mm/slabinfo: fix total_objects attribute name
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yichong Chen <[email protected]>
commit 892a7864730775c3dbee2a39e9ead4fa8d4256e7 upstream.
SLUB exports the total_objects sysfs attribute, but slabinfo tries to read objects_total. As a result, the lookup fails and the field remains zero.
Use the correct attribute name and rename the corresponding structure member to match.
--- tools/mm/slabinfo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
tools/mm/slabinfo.c +2 -2
@@ -33,7 +33,7 @@ struct slabinfo {unsigned int hwcache_align, object_size, objs_per_slab;unsigned int sanity_checks, slab_size, store_user, trace;int order, poison, reclaim_account, red_zone; -unsigned long partial, objects, slabs, objects_partial, objects_total; +unsigned long partial, objects, slabs, objects_partial, total_objects;unsigned long alloc_fastpath, alloc_slowpath;unsigned long free_fastpath, free_slowpath;unsigned long free_frozen, free_add_partial, free_remove_partial;
@@ -1263,7 +1263,7 @@ static void read_slab_dir(void) slab->object_size =get_obj ("object_size" ); slab->objects =get_obj ("objects" ); slab->objects_partial =get_obj ("objects_partial" ); - slab->objects_total =get_obj ("objects_total" ); + slab->total_objects =get_obj ("total_objects" ); slab->objs_per_slab =get_obj ("objs_per_slab" ); slab->order =get_obj ("order" ); slab->partial =get_obj ("partial" );
Greg Kroah-Hartman
maintainerPATCH 259/480 HID: hid-goodix-spi: validate report size to prevent stack buffer overflow
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tianchu Chen <[email protected]>
commit db0a0768d09273aadadeb76730cd658d720333a4 upstream.
goodix_hid_set_raw_report() builds a protocol frame in a 128-byte stack buffer (tmp_buf), writing an 11-12 byte header followed by the caller-supplied report data. The HID core caps report size at HID_MAX_BUFFER_SIZE (16384) by default, while the driver does not set hid_ll_driver.max_buffer_size and performs no bounds checking before copying the payload:
memcpy(tmp_buf + tx_len, buf, len);
A hidraw SET_REPORT ioctl with a report larger than ~116 bytes overflows the stack buffer.
Add a size check after constructing the header, rejecting reports that would exceed the buffer capacity.
Discovered by Atuin - Automated Vulnerability Discovery Engine.
--- drivers/hid/hid-goodix-spi.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/hid/hid-goodix-spi.c +3 -0
@@ -520,6 +520,9 @@ static int goodix_hid_set_raw_report(strmemcpy (tmp_buf + tx_len, args, args_len); tx_len += args_len; +if (tx_len + len >sizeof (tmp_buf)) +return -EINVAL ; +memcpy (tmp_buf + tx_len, buf, len); tx_len += len;
Greg Kroah-Hartman
maintainerPATCH 260/480 HID: uhid: convert to hid_safe_input_report()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Carlos Llamas <[email protected]>
commit 63a694c51bf120a37550890b8e7736b4888985e9 upstream.
Commit 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()"), added a check in hid_report_raw_event() to reject reports if the received data size is smaller than expected. This was intended to prevent OOB errors by no longer allowing zeroing-out of shorter reports due to the lack of buffer size information.
However, this leads to regressions in hid_report_raw_event(), where shorter than expected reports are rejected, even though their buffers are sufficiently large to be zero-padded.
To solve this issue, Benjamin introduced a safer alternative in commit 206342541fc8 ("HID: core: introduce hid_safe_input_report()"), which forwards the buffer size and allows hid_report_raw_event() to safely zero-pad the data.
Convert uhid to use hid_safe_input_report() and pass UHID_DATA_MAX as the buffer size. This prevents the reported regressions [1], allowing hid core to zero-pad the shorter reports safely as expected.
--- drivers/hid/uhid.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/hid/uhid.c +4 -4
@@ -595,8 +595,8 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)if (!READ_ONCE (uhid->running))return -EINVAL ; -hid_input_report (uhid->hid,HID_INPUT_REPORT , ev->u.input.data, -min_t (size_t, ev->u.input.size,UHID_DATA_MAX ),0 ); +hid_safe_input_report (uhid->hid,HID_INPUT_REPORT , ev->u.input.data,UHID_DATA_MAX , +min_t (size_t, ev->u.input.size,UHID_DATA_MAX ),0 );return 0 ; }
@@ -606,8 +606,8 @@ static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)if (!READ_ONCE (uhid->running))return -EINVAL ; -hid_input_report (uhid->hid,HID_INPUT_REPORT , ev->u.input2.data, -min_t (size_t, ev->u.input2.size,UHID_DATA_MAX ),0 ); +hid_safe_input_report (uhid->hid,HID_INPUT_REPORT , ev->u.input2.data,UHID_DATA_MAX , +min_t (size_t, ev->u.input2.size,UHID_DATA_MAX ),0 );return 0 ; }
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 261/480 HID: wacom: stop hardware after post-start probe failures
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit ec2612b8ad9e642596db011dd8b6568ef1edeaa1 upstream.
wacom_parse_and_register() starts HID hardware before registering inputs and initializing pad LEDs/remotes. Those later steps can fail, but their error paths currently release Wacom resources without stopping the HID hardware.
Route post-hid_hw_start() failures through hid_hw_stop() before releasing driver resources.
This issue was identified during our ongoing static-analysis research while reviewing kernel code.
--- drivers/hid/wacom_sys.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/hid/wacom_sys.c +6 -6
@@ -2462,16 +2462,16 @@ static int wacom_parse_and_register(stru error =wacom_register_inputs (wacom);if (error) - goto fail; + goto fail_hw_stop;if (wacom->wacom_wac.features.device_type &WACOM_DEVICETYPE_PAD ) { error =wacom_initialize_leds (wacom);if (error) - goto fail; + goto fail_hw_stop; error =wacom_initialize_remotes (wacom);if (error) - goto fail; + goto fail_hw_stop; }if (!wireless) {
@@ -2485,14 +2485,14 @@ static int wacom_parse_and_register(strucancel_delayed_work_sync (&wacom->init_work);_wacom_query_tablet_data (wacom); error = -ENODEV ; - goto fail_quirks; + goto fail_hw_stop; }if (features->device_type &WACOM_DEVICETYPE_WL_MONITOR ) { error =hid_hw_open (hdev);if (error) {hid_err (hdev,"hw open failed\n" ); - goto fail_quirks; + goto fail_hw_stop; } }
@@ -2501,7 +2501,7 @@ static int wacom_parse_and_register(strureturn 0 ; -fail_quirks: +fail_hw_stop:hid_hw_stop (hdev); fail:wacom_release_resources (wacom);
Greg Kroah-Hartman
maintainerPATCH 262/480 HID: pidff: Use correct effect type in effect update
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oleg Makarenko <[email protected]>
commit b251598b8bf37300510868f739a79e07800d41ce upstream.
When updating an existing effect, the effect type from the last created effect was sent to the device instead of the updated one. This caused incorrect reports when a game creates multiple different effects and updates only one that is not the last created.
Fixes FFB in multiple games that create multiple simultaneous effects (Forza Horizon 5/6).
--- drivers/hid/usbhid/hid-pidff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hid/usbhid/hid-pidff.c +1 -1
@@ -521,7 +521,7 @@ static void pidff_set_effect_report(stru pidff->set_effect[PID_EFFECT_BLOCK_INDEX ].value[0 ] = pidff->block_load[PID_EFFECT_BLOCK_INDEX ].value[0 ]; pidff->set_effect_type->value[0 ] = - pidff->create_new_effect_type->value[0 ]; +pidff_get_effect_type_id (pidff, effect);pidff_set_duration (&pidff->set_effect[PID_DURATION ], effect->replay.length);
Greg Kroah-Hartman
maintainerPATCH 263/480 HID: wacom: fix slab-out-of-bounds write in wacom_wac_queue_insert
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jinmo Yang <[email protected]>
commit 6b3014ec0e9a390ca563030b2d7689921f0daef5 upstream.
wacom_wac_queue_insert() calls kfifo_skip() in a loop when the kfifo doesn't have enough space for the incoming report. If the kfifo is empty, kfifo_skip() reads stale data left in the kmalloc'd buffer via __kfifo_peek_n() and interprets it as a record length, advancing fifo->out by that garbage value. This corrupts the internal kfifo state, causing kfifo_unused() to return a value much larger than the actual buffer size, which bypasses __kfifo_in_r()'s guard:
if (len + recsize > kfifo_unused(fifo))
return 0;
kfifo_copy_in() then performs an out-of-bounds memcpy, writing up to 3842 bytes past the 256-byte buffer.
Add a !kfifo_is_empty() condition to the while loop so kfifo_skip() is never called on an empty fifo, and check the return value of kfifo_in() to reject reports that are too large for the fifo.
--- drivers/hid/wacom_sys.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/hid/wacom_sys.c +4 -2
@@ -54,7 +54,7 @@ static void wacom_wac_queue_insert(struc {bool warned = false; -while (kfifo_avail (fifo) < size) { +while (kfifo_avail (fifo) < size && !kfifo_is_empty (fifo)) {if (!warned)hid_warn (hdev,"%s: kfifo has filled, starting to drop events\n" , __func__); warned = true;
@@ -62,7 +62,9 @@ static void wacom_wac_queue_insert(struckfifo_skip (fifo); } -kfifo_in (fifo, raw_data, size); +if (!kfifo_in (fifo, raw_data, size)) +hid_warn_ratelimited (hdev,"%s: report is too large (%d)\n" , + __func__, size); } static void wacom_wac_queue_flush(struct hid_device *hdev,
Greg Kroah-Hartman
maintainerPATCH 264/480 HID: wacom: use GFP_ATOMIC in wacom_wac_queue_flush()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jinmo Yang <[email protected]>
commit 55f1ad573e34abf9a0443c34bc5a63d74edba7d7 upstream.
wacom_wac_queue_flush() is called via the .raw_event callback (wacom_raw_event → wacom_wac_pen_serial_enforce → wacom_wac_queue_flush). For USB HID devices, this callback is invoked from hid_irq_in(), which is a URB completion handler running in atomic context. Using GFP_KERNEL in this path can sleep, leading to a "scheduling while atomic" bug.
Use GFP_ATOMIC instead. The existing code already handles allocation failure by skipping the fifo entry and continuing.
--- drivers/hid/wacom_sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hid/wacom_sys.c +1 -1
@@ -76,7 +76,7 @@ static void wacom_wac_queue_flush(structunsigned int count;int err; - buf =kzalloc (size,GFP_KERNEL ); + buf =kzalloc (size,GFP_ATOMIC );if (!buf) {kfifo_skip (fifo);continue ;
Greg Kroah-Hartman
maintainerPATCH 265/480 HID: letsketch: fix UAF on inrange_timer at driver unbind
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Manish Khadka <[email protected]>
commit 46c8beeccd8ab2c863827254a85ea877654a3534 upstream.
letsketch_driver does not provide a .remove callback, but letsketch_probe() arms a per-device timer:
timer_setup(&data->inrange_timer, letsketch_inrange_timeout, 0);
The timer is re-armed from letsketch_raw_event() with a 100 ms timeout on every pen-in-range report, and its callback dereferences data->input_tablet to deliver a synthetic BTN_TOOL_PEN release.
letsketch_data is allocated with devm_kzalloc(), and its input_dev fields are devm-allocated via letsketch_setup_input_tablet(). On device unbind (USB unplug or rmmod), the HID core runs its default teardown and devm cleanup frees both letsketch_data and the input devices. Because no .remove callback exists, nothing drains the timer first: if raw_event armed it within ~100 ms of the unbind, the pending timer fires on freed memory. This is a UAF read of data and of data->input_tablet, followed by input_report_key() / input_sync() into the freed input_dev.
The same problem can occur on the probe error path: if hid_hw_start() enabled I/O on an always-poll-quirk device and then failed, raw_event may have armed the timer before devm releases data.
Fix by adding a .remove callback that calls hid_hw_stop() first. hid_hw_stop() synchronously kills the URBs that deliver raw_event(), so once it returns no path can re-arm the timer. timer_shutdown_sync() then drains any in-flight callback and permanently disables further mod_timer() calls. Apply the same timer_shutdown_sync() in the probe error path so the timer is guaranteed not to outlive data.
--- drivers/hid/hid-letsketch.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-)
drivers/hid/hid-letsketch.c +33 -3
@@ -296,13 +296,42 @@ static int letsketch_probe(struct hid_de ret =letsketch_setup_input_tablet (data);if (ret) -return ret; + goto err_shutdown_timer; ret =letsketch_setup_input_tablet_pad (data);if (ret) -return ret; + goto err_shutdown_timer; -return hid_hw_start (hdev,HID_CONNECT_HIDRAW ); + ret =hid_hw_start (hdev,HID_CONNECT_HIDRAW ); +if (ret) + goto err_shutdown_timer; + +return 0 ; + +err_shutdown_timer: +/* +* Drain any pending callback and permanently disable the timer +* before devm releases data: if hid_hw_start() enabled I/O on an +* always-poll-quirk device and then failed, raw_event may have +* armed the timer already. +*/ +timer_shutdown_sync (&data->inrange_timer); +return ret; +} + +static void letsketch_remove (struct hid_device *hdev) +{ +struct letsketch_data *data =hid_get_drvdata (hdev); + +/* +* hid_hw_stop() synchronously kills the URBs that deliver +* raw_event(), so once it returns no path can re-arm +* inrange_timer. timer_shutdown_sync() then drains any +* in-flight callback and permanently disables further +* mod_timer() calls before devm releases data. +*/ +hid_hw_stop (hdev); +timer_shutdown_sync (&data->inrange_timer); }static const struct hid_device_id letsketch_devices[] = {
@@ -315,6 +344,7 @@ static struct hid_driver letsketch_drive .name = "letsketch", .id_table = letsketch_devices, .probe = letsketch_probe, + .remove = letsketch_remove, .raw_event = letsketch_raw_event, };module_hid_driver (letsketch_driver);
Greg Kroah-Hartman
maintainerPATCH 266/480 HID: multitouch: fix out-of-bounds bit access on mt_io_flags
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Trung Nguyen <[email protected]>
commit 8813b0612275cc61fe9e6603d0ee019247ade6be upstream.
mt_io_flags is a single unsigned long, but mt_process_slot(), mt_release_pending_palms() and mt_release_contacts() use it as a per-slot bitmap indexed by the slot number. That slot number is only bounded by td->maxcontacts, which is taken from the device's ContactCountMaximum feature report and can be up to 255, not by BITS_PER_LONG.
As a result, a multitouch device that advertises a large contact count makes set_bit()/clear_bit() operate past the mt_io_flags word and corrupt the adjacent members of struct mt_device. The sticky-fingers release timer is the easiest way to reach this. mt_release_contacts() runs
for (i = 0; i < mt->num_slots; i++) clear_bit(i, &td->mt_io_flags);
with num_slots == maxcontacts. For maxcontacts around 250 the loop clears the bits that overlap td->applications.next, zeroing that list head, and the list_for_each_entry() that immediately follows then dereferences NULL. The kernel panics from timer (softirq) context. On a KASAN build this shows up as a general protection fault in mt_release_contacts() with a null-ptr-deref at offset 0x58, which is offsetof(struct mt_application, num_received).
The state is reachable from an untrusted USB or Bluetooth HID multitouch device; no local privileges are required.
Store the per-slot active state in a separately allocated bitmap sized for maxcontacts, the same pattern already used for pending_palm_slots, and keep only MT_IO_FLAGS_RUNNING in mt_io_flags. The two "mt_io_flags & MT_IO_SLOTS_MASK" arming checks become bitmap_empty(td->active_slots, td->maxcontacts).
Move MT_IO_FLAGS_RUNNING back to bit 0. It was bumped to bit 32 by the same commit to leave the low byte for the slot bits; with the slot bits gone it fits in bit 0 again, which also keeps it within the unsigned long on 32-bit.
--- drivers/hid/hid-multitouch.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-)
drivers/hid/hid-multitouch.c +20 -12
@@ -31,6 +31,7 @@* [1] https://gitlab.freedesktop.org/libevdev/hid-tools */ +#include <linux/bitmap.h> #include <linux/bits.h> #include <linux/device.h> #include <linux/hid.h>
@@ -96,8 +97,7 @@ enum report_mode { TOUCHPAD_REPORT_ALL = TOUCHPAD_REPORT_BUTTONS | TOUCHPAD_REPORT_CONTACTS, }; -#define MT_IO_SLOTS_MASK GENMASK(7, 0)/* reserve first 8 bits for slot tracking */ -#define MT_IO_FLAGS_RUNNING 32 +#define MT_IO_FLAGS_RUNNING 0static const bool mtrue = true;/* default for true */ static const bool mfalse;/* default for false */
@@ -173,10 +173,9 @@ struct mt_device {struct timer_list release_timer;/* to release sticky fingers */ struct hid_haptic_device *haptic;/* haptic related configuration */ struct hid_device *hdev;/* hid_device we're attached to */ -unsigned long mt_io_flags;/* mt flags (MT_IO_FLAGS_RUNNING) -* first 8 bits are reserved for keeping the slot -* states, this is fine because we only support up -* to 250 slots (MT_MAX_MAXCONTACT) +unsigned long mt_io_flags;/* mt flags (MT_IO_FLAGS_RUNNING) */ +unsigned long *active_slots;/* bitmap of slots with an active +* contact, sized for maxcontacts */ __u8 inputmode_value;/* InputMode HID feature value */ __u8 maxcontacts;
@@ -1025,7 +1024,7 @@ static void mt_release_pending_palms(strfor_each_set_bit (slotnum, app->pending_palm_slots, td->maxcontacts) {clear_bit (slotnum, app->pending_palm_slots); -clear_bit (slotnum, &td->mt_io_flags); +clear_bit (slotnum, td->active_slots);input_mt_slot (input, slotnum);input_mt_report_slot_inactive (input);
@@ -1236,9 +1235,9 @@ static int mt_process_slot(struct mt_devinput_event (input,EV_ABS ,ABS_MT_TOUCH_MAJOR , major);input_event (input,EV_ABS ,ABS_MT_TOUCH_MINOR , minor); -set_bit (slotnum, &td->mt_io_flags); +set_bit (slotnum, td->active_slots); }else { -clear_bit (slotnum, &td->mt_io_flags); +clear_bit (slotnum, td->active_slots); }return 0 ;
@@ -1373,7 +1372,7 @@ static void mt_touch_report(struct hid_d* defect. */ if (app->quirks &MT_QUIRK_STICKY_FINGERS ) { -if (td->mt_io_flags &MT_IO_SLOTS_MASK ) +if (!bitmap_empty (td->active_slots, td->maxcontacts))mod_timer (&td->release_timer, jiffies +msecs_to_jiffies (100 ));else
@@ -1430,6 +1429,15 @@ static int mt_touch_input_configured(strif (td->is_buttonpad)__set_bit (INPUT_PROP_BUTTONPAD , input->propbit); +if (!td->active_slots) { + td->active_slots =devm_kcalloc (&td->hdev->dev, +BITS_TO_LONGS (td->maxcontacts), +sizeof (long ), +GFP_KERNEL ); +if (!td->active_slots) +return -ENOMEM ; + } + app->pending_palm_slots = devm_kcalloc(&hi->input->dev, BITS_TO_LONGS(td->maxcontacts), sizeof(long),
@@ -1907,7 +1915,7 @@ static void mt_release_contacts(struct hfor (i =0 ; i < mt->num_slots; i++) {input_mt_slot (input_dev, i);input_mt_report_slot_inactive (input_dev); -clear_bit (i, &td->mt_io_flags); +clear_bit (i, td->active_slots); }input_mt_sync_frame (input_dev);input_sync (input_dev);
@@ -1930,7 +1938,7 @@ static void mt_expired_timeout(struct ti*/ if (test_and_set_bit_lock (MT_IO_FLAGS_RUNNING , &td->mt_io_flags))return ; -if (td->mt_io_flags &MT_IO_SLOTS_MASK ) +if (!bitmap_empty (td->active_slots, td->maxcontacts))mt_release_contacts (hdev);clear_bit_unlock (MT_IO_FLAGS_RUNNING , &td->mt_io_flags); }
Greg Kroah-Hartman
maintainerPATCH 267/480 HID: appleir: fix UAF on pending key_up_timer in remove()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Manish Khadka <[email protected]>
commit 75fe87e19d8aff81eb2c64d15d244ab8da4de945 upstream.
appleir_remove() runs hid_hw_stop() before timer_delete_sync(). hid_hw_stop() synchronously unregisters the HID input device via hid_disconnect() -> hidinput_disconnect() -> input_unregister_device(), which drops the last reference and frees the underlying input_dev when no userspace handle holds it open.
key_up_tick() reads appleir->input_dev and calls input_report_key() / input_sync() on it. The timer is armed from appleir_raw_event() with a HZ/8 (~125 ms) timeout on every keydown and key-repeat report. If a key was pressed shortly before the device is disconnected, the timer can fire after hid_hw_stop() has freed input_dev but before the teardown drains it.
A simple reorder is not sufficient. Putting the timer drain first still leaves a window where a USB URB completion (raw_event) running during hid_hw_stop() can call mod_timer() and re-arm the timer, which then fires after hidinput_disconnect() has freed input_dev. The same URB-completion window also lets raw_event() reach key_up(), key_down() and battery_flat() directly, all of which dereference appleir->input_dev.
Introduce a 'removing' flag on struct appleir, gated by the existing spinlock. appleir_remove() sets the flag under the lock and then shuts down the timer with timer_shutdown_sync(), which both drains any in-flight callback and permanently disables further mod_timer() calls. appleir_raw_event() and key_up_tick() bail out early if the flag is set, so no path can arm or run the timer, or dereference appleir->input_dev, after remove() has started tearing down.
The keyrepeat and flatbattery branches of appleir_raw_event() previously called into the input layer without holding the spinlock; take it now so the flag check is well-defined. This incidentally closes a pre-existing read-side race on appleir->current_key in the keyrepeat branch.
This bug is structurally a sibling of commit 4db2af929279 ("HID:
appletb-kbd: fix UAF in inactivity-timer cleanup path") and has been present since the driver was introduced.
--- drivers/hid/hid-appleir.c | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-)
drivers/hid/hid-appleir.c +35 -10
@@ -109,9 +109,10 @@ struct appleir {struct hid_device *hid;unsigned short keymap[ARRAY_SIZE (appleir_key_table)];struct timer_list key_up_timer;/* timer for key up */ -spinlock_t lock;/* protects .current_key */ +spinlock_t lock;/* protects .current_key, .removing */ int current_key;/* the currently pressed key */ int prev_key_idx;/* key index in a 2 packets message */ +bool removing;/* set during teardown; gates input_dev access */ };static int get_key (int data)
@@ -172,7 +173,7 @@ static void key_up_tick(struct timer_lisunsigned long flags;spin_lock_irqsave (&appleir->lock, flags); -if (appleir->current_key) { +if (!appleir->removing && appleir->current_key) {key_up (hid, appleir, appleir->current_key); appleir->current_key =0 ; }
@@ -195,6 +196,10 @@ static int appleir_raw_event(struct hid_int index;spin_lock_irqsave (&appleir->lock, flags); +if (appleir->removing) { +spin_unlock_irqrestore (&appleir->lock, flags); + goto out; + }/* * If we already have a key down, take it up before marking * this one down
@@ -229,17 +234,25 @@ static int appleir_raw_event(struct hid_ appleir->prev_key_idx =0 ;if (!memcmp (data, keyrepeat,sizeof (keyrepeat))) { -key_down (hid, appleir, appleir->current_key); -/* -* Remote doesn't do key up, either pull them up, in the test -* above, or here set a timer which pulls them up after 1/8 s -*/ -mod_timer (&appleir->key_up_timer, jiffies +HZ /8 ); +spin_lock_irqsave (&appleir->lock, flags); +if (!appleir->removing) { +key_down (hid, appleir, appleir->current_key); +/* +* Remote doesn't do key up, either pull them up, in +* the test above, or here set a timer which pulls them +* up after 1/8 s +*/ +mod_timer (&appleir->key_up_timer, jiffies +HZ /8 ); + } +spin_unlock_irqrestore (&appleir->lock, flags); goto out; }if (!memcmp (data, flatbattery,sizeof (flatbattery))) { -battery_flat (appleir); +spin_lock_irqsave (&appleir->lock, flags); +if (!appleir->removing) +battery_flat (appleir); +spin_unlock_irqrestore (&appleir->lock, flags);/* Fall through */ }
@@ -318,8 +331,20 @@ fail:static void appleir_remove (struct hid_device *hid) {struct appleir *appleir =hid_get_drvdata (hid); +unsigned long flags; + +/* +* Mark the driver as tearing down so that any concurrent raw_event +* (e.g. from a USB URB completion that hid_hw_stop() has not yet +* killed) and the key_up_timer softirq stop touching input_dev +* before hid_hw_stop() frees it via hidinput_disconnect(). +*/ +spin_lock_irqsave (&appleir->lock, flags); + appleir->removing = true; +spin_unlock_irqrestore (&appleir->lock, flags); + +timer_shutdown_sync (&appleir->key_up_timer);hid_hw_stop (hid); -timer_delete_sync (&appleir->key_up_timer); }static const struct hid_device_id appleir_devices[] = {
Greg Kroah-Hartman
maintainerPATCH 268/480 HID: lg-g15: cancel pending work on remove to fix a use-after-free
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
commit 7705b4140d188ce22656f6e541ae7ef834c7e11a upstream.
lg_g15_data is allocated with devm and holds a work item. The report handlers schedule that work straight from device input. lg_g15_event() and lg_g15_v2_event() do it on the backlight cycle key, and lg_g510_leds_event() does it too. The worker dereferences the lg_g15_data back through container_of.
The driver had no remove callback and never cancelled the work. So if a report scheduled the work and the keyboard was then unplugged, devres freed lg_g15_data while the work was still pending or running, and the worker touched freed memory. This is a use-after-free. It is reachable as a race on device unplug.
Add a remove callback that cancels the work before devres frees the state. g15->work is only initialized for the models that schedule it (G15, G15 v2, G510). The G13 and Z-10 leave it zeroed, so guard the cancel on g15->work.func to avoid cancelling a work that was never set up. The g15 NULL test mirrors the one already in lg_g15_raw_event().
--- drivers/hid/hid-lg-g15.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
drivers/hid/hid-lg-g15.c +16 -0
@@ -929,11 +929,27 @@ static const struct hid_device_id lg_g15 };MODULE_DEVICE_TABLE (hid, lg_g15_devices); +static void lg_g15_remove (struct hid_device *hdev) +{ +struct lg_g15_data *g15 =hid_get_drvdata (hdev); + +/* +* g15->work is only initialized for the models that schedule it +* (G15, G15 v2, G510). The G13 and Z-10 leave it zeroed, so only +* cancel it when it was set up. +*/ +if (g15 && g15->work.func) +cancel_work_sync (&g15->work); + +hid_hw_stop (hdev); +} +static struct hid_driver lg_g15_driver = { .name ="lg-g15" , .id_table = lg_g15_devices, .raw_event = lg_g15_raw_event, .probe = lg_g15_probe, + .remove = lg_g15_remove, };module_hid_driver (lg_g15_driver);
Greg Kroah-Hartman
maintainerPATCH 269/480 HID: sensor-hub: Add sensor_hub_input_attr_read_values() for multi-byte reads
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Pandruvada <[email protected]>
commit f784fcea450617055d2d12eec5b2f6e0e38bf878 upstream.
sensor_hub_input_attr_get_raw_value() is limited to returning a single 32-bit value, which is insufficient for sensors that report data larger than 32 bits, such as a quaternion with four s16 elements.
Add sensor_hub_input_attr_read_values() that accepts a caller-provided buffer and accumulates incoming data until the buffer is full. The two paths are distinguished in sensor_hub_raw_event() by pending.max_raw_size being non-zero, preserving backward compatibility.
--- drivers/hid/hid-sensor-hub.c | 77 +++++++++++++++++++++++++++++++++++++---- include/linux/hid-sensor-hub.h | 25 +++++++++++++ 2 files changed, 96 insertions(+), 6 deletions(-)
drivers/hid/hid-sensor-hub.c +71 -6
@@ -286,6 +286,54 @@ done_proc: }EXPORT_SYMBOL_GPL (sensor_hub_get_feature); +int sensor_hub_input_attr_read_values (struct hid_sensor_hub_device *hsdev, +u32 usage_id,u32 attr_usage_id, +u32 report_id, +enum sensor_hub_read_flags flag, +u32 buffer_size,u8 *buffer) +{ +struct sensor_hub_data *data =hid_get_drvdata (hsdev->hdev); +struct hid_report *report; +unsigned long flags; +long cycles; +int ret; + + report =sensor_hub_report (report_id, hsdev->hdev,HID_INPUT_REPORT ); +if (!report) +return -EINVAL ; + +mutex_lock (hsdev->mutex_ptr); +if (flag ==SENSOR_HUB_SYNC ) { +memset (&hsdev->pending,0 ,sizeof (hsdev->pending)); +init_completion (&hsdev->pending.ready); + hsdev->pending.usage_id = usage_id; + hsdev->pending.attr_usage_id = attr_usage_id; + hsdev->pending.max_raw_size = buffer_size; + hsdev->pending.raw_data = buffer; + +spin_lock_irqsave (&data->lock, flags); + hsdev->pending.status = true; +spin_unlock_irqrestore (&data->lock, flags); + } +mutex_lock (&data->mutex); +hid_hw_request (hsdev->hdev, report,HID_REQ_GET_REPORT ); +mutex_unlock (&data->mutex); + ret =0 ; +if (flag ==SENSOR_HUB_SYNC ) { + cycles =wait_for_completion_interruptible_timeout (&hsdev->pending.ready, +HZ *5 ); +if (cycles ==0 ) + ret = -ETIMEDOUT ; +else if (cycles <0 ) + ret = cycles; + + hsdev->pending.status = false; + } +mutex_unlock (hsdev->mutex_ptr); + +return ret; +} +EXPORT_SYMBOL_GPL (sensor_hub_input_attr_read_values); int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, u32 usage_id,
@@ -480,6 +528,8 @@ static int sensor_hub_raw_event(struct hstruct hid_collection *collection =NULL ;void *priv =NULL ;struct hid_sensor_hub_device *hsdev =NULL ; +u32 copy_size; +u32 avail;hid_dbg (hdev,"sensor_hub_raw_event report id:0x%x size:%d type:%d\n" , report->id, size, report->type);
@@ -520,12 +570,27 @@ static int sensor_hub_raw_event(struct h hsdev->pending.attr_usage_id == report->field[i]->logical)) {hid_dbg (hdev,"data was pending ...\n" ); - hsdev->pending.raw_data =kmemdup (ptr, sz,GFP_ATOMIC ); -if (hsdev->pending.raw_data) - hsdev->pending.raw_size = sz; -else - hsdev->pending.raw_size =0 ; -complete (&hsdev->pending.ready); +if (hsdev->pending.max_raw_size) { +if (hsdev->pending.index < hsdev->pending.max_raw_size) { + avail = hsdev->pending.max_raw_size - hsdev->pending.index; + copy_size =clamp (sz,0U , avail); + +memcpy (hsdev->pending.raw_data + hsdev->pending.index, + ptr, copy_size); + hsdev->pending.index += copy_size; +if (hsdev->pending.index >= hsdev->pending.max_raw_size) { + hsdev->pending.raw_size = hsdev->pending.index; +complete (&hsdev->pending.ready); + } + } + }else { + hsdev->pending.raw_data =kmemdup (ptr, sz,GFP_ATOMIC ); +if (hsdev->pending.raw_data) + hsdev->pending.raw_size = sz; +else + hsdev->pending.raw_size =0 ; +complete (&hsdev->pending.ready); + } }if (callback->capture_sample) {if (report->field[i]->logical)
include/linux/hid-sensor-hub.h +25 -0
@@ -43,6 +43,8 @@ struct hid_sensor_hub_attribute_info {* @attr_usage_id: Usage Id of a field, e.g. X-axis for a gyro. * @raw_size: Response size for a read request. * @raw_data: Place holder for received response. +* @index: Current write index into raw_data for multi-byte reads. +* @max_raw_size: Total buffer size for multi-byte reads; 0 for single-value reads. */ struct sensor_hub_pending {bool status;
@@ -51,6 +53,8 @@ struct sensor_hub_pending {u32 attr_usage_id;int raw_size;u8 *raw_data; +u32 index; +u32 max_raw_size; };/**
@@ -184,6 +188,27 @@ int sensor_hub_input_attr_get_raw_value( );/** +* sensor_hub_input_attr_read_values() - Synchronous multi-byte read request +* @hsdev: Hub device instance. +* @usage_id: Attribute usage id of parent physical device as per spec +* @attr_usage_id: Attribute usage id as per spec +* @report_id: Report id to look for +* @flag: Synchronous or asynchronous read +* @buffer_size: Size of the buffer in bytes +* @buffer: Buffer to store the read data +* +* Issues a synchronous or asynchronous read request for an input attribute, +* accumulating data into the provided buffer until it is full. +* Return: 0 on success, -ETIMEDOUT if the device did not respond, or a +* negative error code. +*/ +int sensor_hub_input_attr_read_values (struct hid_sensor_hub_device *hsdev, +u32 usage_id,u32 attr_usage_id, +u32 report_id, +enum sensor_hub_read_flags flag, +u32 buffer_size,u8 *buffer); + +/** * sensor_hub_set_feature() - Feature set request * @hsdev: Hub device instance. * @report_id: Report id to look for
Greg Kroah-Hartman
maintainerPATCH 270/480 hfs/hfsplus: zero-initialize buffer in hfs_bnode_read
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tristan Madani <[email protected]>
commit d67aadee19ffdf3cc8520c5a4f4d5b2916d30baf upstream.
hfs_bnode_read() can return early without writing to the output buffer when is_bnode_offset_valid() fails or when check_and_correct_requested_ length() corrects the length to zero. Callers such as hfs_bnode_read_ u16() and hfs_bnode_read_u8() pass stack-allocated buffers and use the result unconditionally, leading to KMSAN uninit-value reports.
Rather than initializing at each individual call site, zero the buffer at the start of hfs_bnode_read() before any validation checks. This ensures all callers in both hfs and hfsplus get a deterministic zero value regardless of which early-return path is taken.
--- fs/hfs/bnode.c | 2 ++ fs/hfsplus/bnode.c | 2 ++ 2 files changed, 4 insertions(+)
fs/hfs/bnode.c +2 -0
@@ -64,6 +64,8 @@ void hfs_bnode_read(struct hfs_bnode *noint bytes_read;int bytes_to_read; +memset (buf,0 , len); +if (!is_bnode_offset_valid (node, off))return ;
fs/hfsplus/bnode.c +2 -0
@@ -25,6 +25,8 @@ void hfs_bnode_read(struct hfs_bnode *nostruct page **pagep;int l; +memset (buf,0 , len); +if (!is_bnode_offset_valid (node, off))return ;
Greg Kroah-Hartman
maintainerPATCH 271/480 nilfs2: reject CLEAN_SEGMENTS ioctl with out-of-range segment numbers
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Deepanshu Kartikey <[email protected]>
commit 0e7a690fe435f8d5ea3feb7c1d8d73ba7e8b8aa9 upstream.
Syzbot reported a hung task in nilfs_transaction_begin() where multiple tasks performing chmod() on a nilfs2 mount blocked for over 143 seconds waiting to acquire ns_segctor_sem for read:
INFO: task syz.0.17:5918 blocked for more than 143 seconds. Call Trace: schedule+0x164/0x360 rwsem_down_read_slowpath+0x6d9/0x940 down_read+0x99/0x2e0 nilfs_transaction_begin+0x364/0x710 fs/nilfs2/segment.c:221 nilfs_setattr+0x124/0x2c0 fs/nilfs2/inode.c:921 notify_change+0xc1a/0xf40 chmod_common+0x273/0x4a0 do_fchmodat+0x12d/0x230
The writer holding ns_segctor_sem was a concurrent NILFS_IOCTL_CLEAN_SEGMENTS caller, stuck inside printk while emitting per-element warnings from nilfs_sufile_updatev():
__nilfs_msg+0x373/0x450 fs/nilfs2/super.c:78 nilfs_sufile_updatev+0x21c/0x6d0 fs/nilfs2/sufile.c:186 nilfs_sufile_freev fs/nilfs2/sufile.h:93 [inline] nilfs_free_segments fs/nilfs2/segment.c:1140 [inline] nilfs_segctor_collect_blocks fs/nilfs2/segment.c:1261 [inline] nilfs_segctor_do_construct+0x1f55/0x76c0 nilfs_clean_segments+0x3bd/0xa50 nilfs_ioctl_clean_segments fs/nilfs2/ioctl.c:922 [inline] nilfs_ioctl+0x261f/0x2780
The root cause is that user-supplied segment numbers are not validated before nilfs_clean_segments() begins doing work; the range check on each segnum is performed deep inside the call chain by nilfs_sufile_updatev(), which emits a nilfs_warn() per invalid entry while still holding the segctor lock and the sufile mi_sem. Under load (repeated invocations across multiple mounts saturating the global printk path), the cumulative printk latency keeps ns_segctor_sem held long enough to trip the hung_task watchdog, blocking concurrent operations such as chmod() that need ns_segctor_sem for read.
Fix by validating the contents of kbufs[4] in nilfs_clean_segments() immediately after acquiring ns_segctor_sem via nilfs_transaction_lock(). Holding ns_segctor_sem serializes the check against nilfs_ioctl_resize(), which can modify ns_nsegments, so the validation uses a consistent value. Out-of-range segment numbers are rejected with -EINVAL before any segment-cleaning work begins, so the bad entries never reach the per-element diagnostic path inside nilfs_sufile_updatev().
--- fs/nilfs2/segment.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
fs/nilfs2/segment.c +22 -0
@@ -2512,12 +2512,33 @@ int nilfs_clean_segments(struct super_blstruct nilfs_sc_info *sci = nilfs->ns_writer;struct nilfs_transaction_info ti;int err; +size_t i, nfreesegs = argv[4 ].v_nmembs; +__u64 *segnumv = kbufs[4 ];if (unlikely (!sci))return -EROFS ;nilfs_transaction_lock (sb, &ti,1 ); +/* +* Validate segment numbers under ns_segctor_sem (held for write +* by nilfs_transaction_lock above) so the check is serialized +* against nilfs_ioctl_resize(), which can modify ns_nsegments. +* Rejecting bad input here, before any segment-cleaning work +* begins, avoids the per-element diagnostic path inside +* nilfs_sufile_updatev() that would otherwise run under this +* same lock and stall concurrent readers. +*/ +for (i =0 ; i < nfreesegs; i++) { +if (segnumv[i] >= nilfs->ns_nsegments) { +nilfs_err (sb, +"Segment number %llu to be freed is out of range" , + (unsigned long long )segnumv[i]); + err = -EINVAL ; + goto bail_unlock; + } + } + err =nilfs_mdt_save_to_shadow_map (nilfs->ns_dat);if (unlikely (err)) goto out_unlock;
@@ -2558,6 +2579,7 @@ int nilfs_clean_segments(struct super_bl sci->sc_freesegs =NULL ; sci->sc_nfreesegs =0 ;nilfs_mdt_clear_shadow_map (nilfs->ns_dat); + bail_unlock:nilfs_transaction_unlock (sb);return err; }
Greg Kroah-Hartman
maintainerPATCH 272/480 media: mtk-jpeg: cancel workqueue on release for supported platforms only
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Louis-Alexis Eyraud <[email protected]>
commit b1845a227fda37b2fe5327df3ca0015d7e290235 upstream.
Since a recent fix the mtk_jpeg_release function cancels any pending or running work present in the driver workqueue using cancel_work_sync function.
Currently, only the multicore based variants use this workqueue and they have the jpeg_worker platform data field initialized with a workqueue callback function. For the others, this field value remain NULL by default.
The cancel_work_sync function is unconditionally called in mtk_jpeg_release function, even for the variants that do not use the workqueue. This call generates a WARN_ON print in __flush_work because the workqueue callback function presence check fails in __flush_work function (used by cancel_work_sync).
So, to avoid these warnings, call cancel_work_sync only if a workqueue callback is defined in platform data.
--- drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c +2 -1
@@ -1209,7 +1209,8 @@ static int mtk_jpeg_release(struct filestruct mtk_jpeg_dev *jpeg =video_drvdata (file);struct mtk_jpeg_ctx *ctx =mtk_jpeg_file_to_ctx (file); -cancel_work_sync (&ctx->jpeg_work); +if (jpeg->variant->jpeg_worker) +cancel_work_sync (&ctx->jpeg_work);mutex_lock (&jpeg->lock);v4l2_m2m_ctx_release (ctx->fh.m2m_ctx);v4l2_ctrl_handler_free (&ctx->ctrl_hdl);
Greg Kroah-Hartman
maintainerPATCH 273/480 serial: 8250_mid: Disable DMA for selected platforms
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
commit b1b4efea05a56c0995e4702a86d6624b4fdff32f upstream.
In accordance with Errata (specification updates)
HSUART May Stop Functioning when DMA is Active.
- Denverton document #572409, rev 3.4, DNV60
- Ice Lake Xeon D document #714070, ICXD65
- Snowridge document #731931, SNR44
For a quick fix just disable the respective callbacks during the device probe. Depending on the future development we might remove them completely.
--- drivers/tty/serial/8250/8250_mid.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/tty/serial/8250/8250_mid.c +11 -2
@@ -10,6 +10,7 @@#include <linux/module.h> #include <linux/pci.h> #include <linux/rational.h> +#include <linux/util_macros.h> #include <linux/dma/hsu.h>
@@ -368,8 +369,16 @@ static const struct mid8250_board dnv_bo .freq = 133333333, .base_baud = 115200, .bar = 1, - .setup = dnv_setup, - .exit = dnv_exit, +/* +* Errata: +* HSUART May Stop Functioning when DMA is Active. +* +* - Denverton document #572409, rev 3.4, DNV60 +* - Ice Lake Xeon D document #714070, ICXD65 +* - Snowridge document #731931, SNR44 +*/ + .setup = PTR_IF(false, dnv_setup), + .exit = PTR_IF(false, dnv_exit), };static const struct pci_device_id pci_ids[] = {
Greg Kroah-Hartman
maintainerPATCH 274/480 xfs: use null daddr for unset first bad log block
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yousef Alhouseen <[email protected]>
commit cc9af5e461ea5f6e37738f3f1e41c45a9b7f45d6 upstream.
xlog_do_recovery_pass() may return before setting first_bad. The caller must distinguish that case from an error at a valid log block, including block zero after the log wraps.
Initialize first_bad to XFS_BUF_DADDR_NULL and test it explicitly before treating the error as a torn write.
--- fs/xfs/xfs_log_recover.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
fs/xfs/xfs_log_recover.c +3 -3
@@ -1028,7 +1028,7 @@ xlog_verify_head( {struct xlog_rec_header *tmp_rhead;char *tmp_buffer; -xfs_daddr_t first_bad; +xfs_daddr_t first_bad =XFS_BUF_DADDR_NULL ;xfs_daddr_t tmp_rhead_blk;int found;int error;
@@ -1057,7 +1057,8 @@ xlog_verify_head(*/ error =xlog_do_recovery_pass (log, *head_blk, tmp_rhead_blk,XLOG_RECOVER_CRCPASS , &first_bad); -if ((error == -EFSBADCRC || error == -EFSCORRUPTED ) && first_bad) { +if ((error == -EFSBADCRC || error == -EFSCORRUPTED ) && + first_bad !=XFS_BUF_DADDR_NULL ) {/* * We've hit a potential torn write. Reset the error and warn * about it.
@@ -3582,4 +3583,3 @@ xlog_recover_cancel(if (xlog_recovery_needed (log))xlog_recover_cancel_intents (log); } -
Greg Kroah-Hartman
maintainerPATCH 275/480 xfs: release dquot buffer after dqflush failure
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yingjie Gao <[email protected]>
commit 0c1b3a823a22af623d55f225fe2ac7e8b9052821 upstream.
xfs_qm_dqpurge() gets a locked buffer from xfs_dquot_use_attached_buf(). If xfs_qm_dqflush() fails, the error path skips xfs_buf_relse() and then calls xfs_dquot_detach_buf(), which tries to lock the same buffer again.
Release the buffer after xfs_qm_dqflush() returns so the error path drops the caller hold and unlocks the buffer before the dquot is detached, matching the other dqflush callers.
--- fs/xfs/xfs_qm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
fs/xfs/xfs_qm.c +2 -3
@@ -163,10 +163,9 @@ xfs_qm_dqpurge(* does it on success. */ error =xfs_qm_dqflush (dqp, bp); -if (!error) { +if (!error) error =xfs_bwrite (bp); -xfs_buf_relse (bp); - } +xfs_buf_relse (bp);xfs_dqflock (dqp); }xfs_dquot_detach_buf (dqp);