Skip to contentSkip to conversation

6.18.50-rc1 review

PATCH In review Greg Kroah-Hartman 11 participants 571 messages Sep 4 – Sep 18 Page 4 of 5

Subsystems (5)
lore
Share & tools
lore
author maintainer
#407 lore raw

PATCH 396/552 PCI/ASPM: Avoid L0s for Realtek RTS525A

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Max Lee <[email protected]>

commit ec3d987fcaf92516d13ee18c305c82281557046d upstream.

The Realtek RTS525A PCIe card reader reports an AER Correctable Replay Timer Timeout storm when ASPM L0s is enabled on its link. On an affected HP ZBook Power 16 inch G11, the Root Port received tens of millions of AER interrupts from the RTS525A even when the rtsx_pci driver was blacklisted and the endpoint was not enabled by a driver.

For example:

pcieport 0000:00:1c.6: AER: Multiple Correctable error message received from 0000:58:00.0 rtsx_pci 0000:58:00.0: PCIe Bus Error: severity=Correctable, type=Data Link Layer, (Transmitter ID)
rtsx_pci 0000:58:00.0: device [10ec:525a] error status/mask=00001000/00006000
rtsx_pci 0000:58:00.0: [12] Timeout
pcieport 0000:00:1c.6: AER: Correctable error message received from 0000:58:00.0

Testing with OS-native AER control showed that disabling only L0s on the RTS525A link stops new AER interrupt and counter growth while leaving L1 enabled. Disabling L1, L1 substates, or Clock PM alone did not stop the storm.

Prevent the broken L0s configuration by removing L0s from the RTS525A advertised ASPM capability. This avoids enabling the non-working ASPM state instead of masking the resulting AER Replay Timer Timeout reports.

---
 drivers/pci/quirks.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/pci/quirks.c +3 -0
@@ -2506,6 +2506,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10f4, quirk_disable_aspm_l0s);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1508, quirk_disable_aspm_l0s);
 
+/* Realtek RTS525A generates a Replay Timer Timeout storm when L0s is enabled. */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x525a, quirk_disable_aspm_l0s);
+
 static void quirk_disable_aspm_l0s_l1(struct pci_dev *dev)
 {
 	pcie_aspm_remove_cap(dev,
author maintainer
#408 lore raw

PATCH 397/552 PCI/DPC: Allow DPC on all Downstream Ports when OS controls AER

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Darshit Shah <[email protected]>

commit 97ca178c899d0049210d325b123ed024eb5ac000 upstream.

PCIe r7.0, sec 6.2.11, "Implementation Note: Determination of DPC Control", recommends that "... operating systems always link control of DPC to the control of Advanced Error Reporting."

Any PCIe device may advertise AER, but only Root Ports and Root Complex Event Collectors can generate AER interrupts, so the AER driver only binds to RPs and RCECs.

Any Root Port or Switch Downstream Port may advertise Downstream Port Containment (DPC), but previously the DPC driver was limited to devices the AER driver could bind to, i.e., only RPs that advertised AER.

Since any Port with DPC can generate DPC interrupts, allow the DPC driver to bind to such a Port as long as the OS controls AER, regardless of whether the AER driver binds to it.

---
 drivers/pci/pcie/portdrv.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/pcie/portdrv.c +1 -1
@@ -264,7 +264,7 @@ static int get_port_device_capability(st
 	 */
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) &&
 	    pci_aer_available() &&
-	    (pcie_ports_dpc_native || (services & PCIE_PORT_SERVICE_AER)))
+	    (pcie_ports_dpc_native || host->native_aer))
 		services |= PCIE_PORT_SERVICE_DPC;
 
 	/* Enable bandwidth control if more than one speed is supported. */
author maintainer
#409 lore raw

PATCH 398/552 PCI/MSI: Enable memory decoding before restoring MSI-X messages

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Farhan Ali <[email protected]>

commit 231c7a57d19304beb0931e6cbe3a4929daf49747 upstream.

The current MSI-X restoration path assumes the Command register Memory bit is enabled when writing MSI-X messages. But it's possible the last saved and restored state of a device may not have the Memory bit enabled, even if a device driver later enables Memory bit and MSI-X. Attempting to access Memory space without Memory bit enabled can lead to Unsupported Request (UR) from the device. Fix this by enabling Memory bit and restore it afterwards.

Fixes 41017f0cac92 ("[PATCH] PCI: MSI(X) save/restore for suspend/resume")
[bhelgaas: comment]
---
 drivers/pci/msi/msi.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/pci/msi/msi.c +10 -0
@@ -860,6 +860,7 @@ void __pci_restore_msix_state(struct pci
 {
 	struct msi_desc *entry;
 	bool write_msg;
+	u16 cmd;
 
 	if (!dev->msix_enabled)
 		return;
@@ -869,6 +870,14 @@ void __pci_restore_msix_state(struct pci
 	pci_msix_clear_and_set_ctrl(dev, 0,
 				PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL);
 
+	/*
+	 * The restored device state may not have Memory Space enabled.
+	 * Since the MSI-X Table and PBA are in Memory Space, enable it
+	 * while restoring them.
+	 */
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
+
 	write_msg = arch_restore_msi_irqs(dev);
 
 	scoped_guard (msi_descs_lock, &dev->dev) {
@@ -879,6 +888,7 @@ void __pci_restore_msix_state(struct pci
 		}
 	}
 
+	pci_write_config_word(dev, PCI_COMMAND, cmd);
 	pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
 }
 
author maintainer
#410 lore raw

PATCH 399/552 PCI/proc: Avoid spurious runtime PM wakeup on config space accesses

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Krzysztof Wilczyński <[email protected]>

commit 4ff664a81d729b37f2eb65de80a670abfb61c9a0 upstream.

Currently, proc_bus_pci_read() and proc_bus_pci_write() do not return early for zero-length configuration space accesses at valid offsets.

Such an access invokes pci_config_pm_runtime_get() and pci_config_pm_runtime_put() around transfer blocks that do nothing.

This is a problem because pci_config_pm_runtime_get() synchronously resumes the upstream bridge through pm_runtime_get_sync(), and resumes the device itself through pm_runtime_resume() when it is in D3cold, only for the handler to return zero immediately afterwards. Such a spurious wakeup wastes power and adds needless resume latency.

The sysfs core already returns early for in-range zero-length binary attribute accesses before pci_read_config() or pci_write_config() is invoked. In contrast, the VFS forwards zero-length requests to the procfs callbacks, where they continue into runtime PM handling.

Return early from proc_bus_pci_read() and proc_bus_pci_write() when nbytes is zero, before any runtime PM involvement.

The value returned to userspace at these offsets remains zero, so the change is not visible to userspace.

---
 drivers/pci/proc.c |    6 ++++++
 1 file changed, 6 insertions(+)
drivers/pci/proc.c +6 -0
@@ -45,6 +45,9 @@ static ssize_t proc_bus_pci_read(struct
 	else
 		size = 64;
 
+	if (!nbytes)
+		return 0;
+
 	if (pos >= size)
 		return 0;
 	if (nbytes >= size)
@@ -121,6 +124,9 @@ static ssize_t proc_bus_pci_write(struct
 	if (ret)
 		return ret;
 
+	if (!nbytes)
+		return 0;
+
 	if (pos >= size)
 		return 0;
 	if (nbytes >= size)
author maintainer
#411 lore raw

PATCH 400/552 PCI/proc: Use file_ns_capable() when checking config space read access

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Krzysztof Wilczyński <[email protected]>

commit f82f53e75eff382fc8f56b73279b54f7cf5a5c65 upstream.

proc_bus_pci_read() decides how much of the config space is readable based on capable(CAP_SYS_ADMIN), which checks the credentials of the task calling read(), not the credentials of the process that opened the file.

The sysfs equivalent, pci_read_config(), has checked the credentials of the opening process since commit de139a339395 ("pci: check caps from sysfs file open to read device dependent config space"), so a privileged process can open the config space file and pass the file descriptor to an unprivileged process (for example, a process running a KVM guest with an assigned device), which can then read the entire config space. The check was subsequently routed through the LSM framework in commit 47970b1b2aa6 ("pci:
use security_capable() when checking capablities during config space read") and converted to the dedicated helper in commit ab0fa82b2df9 ("pci-sysfs:
use proper file capability helper function").

Thus, the two interfaces check the same capability against different credentials. Checking the credentials of the task calling read() makes the outcome depend on who reads rather than who opened, so the restriction is bypassed whenever a more privileged process reads through the descriptor. Checking the credentials recorded in file->f_cred settles the decision at open() time and ties it to the file, where it cannot change with the caller.

Use file_ns_capable() to check CAP_SYS_ADMIN against the credentials in effect when the file was opened, bringing the procfs interface in line with the sysfs behaviour.

As a result, a file descriptor opened by a privileged process and passed to an unprivileged one now allows the entire config space to be read through procfs, matching sysfs.

---
 drivers/pci/proc.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/proc.c +1 -1
@@ -38,7 +38,7 @@ static ssize_t proc_bus_pci_read(struct
 	 * undefined locations (think of Intel PIIX4 as a typical example).
 	 */
 
-	if (capable(CAP_SYS_ADMIN))
+	if (file_ns_capable(file, &init_user_ns, CAP_SYS_ADMIN))
 		size = dev->cfg_size;
 	else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
 		size = 128;
author maintainer
#412 lore raw

PATCH 401/552 PCI/proc: Warn on writes to kernel-exclusive config space regions

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Krzysztof Wilczyński <[email protected]>

commit 3359e044d597dd5344f17613e4be6b6e12067f60 upstream.

Currently, a driver can claim a region of a device's config space as exclusive using pci_request_config_region_exclusive(), after which a write to that region originating from user space is expected to emit a warning and taint the kernel. The check is advisory only, as the write itself is still allowed to proceed.

Since commit 278294798ac9 ("PCI: Allow drivers to request exclusive config regions"), the sysfs config space attribute performs this check in pci_write_config(), but the procfs interface was never updated. A write performed through /proc/bus/pci/BB/DD.F therefore bypasses the detection entirely, even though both interfaces offer the same level of access.

Add the same resource_is_exclusive() check to proc_bus_pci_write().

---
 drivers/pci/proc.c |    8 ++++++++
 1 file changed, 8 insertions(+)
drivers/pci/proc.c +8 -0
@@ -14,6 +14,8 @@
 #include <linux/capability.h>
 #include <linux/uaccess.h>
 #include <linux/security.h>
+#include <linux/panic.h>
+#include <linux/sched.h>
 #include <asm/byteorder.h>
 #include "pci.h"
 
@@ -127,6 +129,12 @@ static ssize_t proc_bus_pci_write(struct
 	if (!nbytes)
 		return 0;
 
+	if (resource_is_exclusive(&dev->driver_exclusive_resource, pos, nbytes)) {
+		pci_warn_once(dev, "%s: Unexpected write to kernel-exclusive config offset %x",
+			      current->comm, pos);
+		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+	}
+
 	if (pos >= size)
 		return 0;
 	if (nbytes >= size)
author maintainer
#413 lore raw

PATCH 402/552 iommu/amd: Put PCI device after handling PPR faults

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Shuai Xue <[email protected]>

commit af3b69b16383fbc8fe5f61b5b0150d2e41ede71f upstream.

iommu_call_iopf_notifier() looks up the requester with pci_get_domain_bus_and_slot(), which returns a PCI device with its reference count incremented.

Neither the successful iommu_report_device_fault() path nor the abort path drops that reference, so every handled PPR request leaks a PCI device reference.

This is the same ownership rule that was fixed for the old iommu_v2 ppr_notifier() path by commit 6cf0981c2233 ("iommu/amd: Fix pci device refcount leak in ppr_notifier()"), but iommu_call_iopf_notifier() was added later as a separate PPR/IOPF notifier path.

Drop the PCI device reference after handling the PPR entry.

Fixes 978d626b8f1a ("iommu/amd: Add IO page fault notifier handler")
Assisted-by Qoder:Qwen-3.8-MAX-Preview
---
 drivers/iommu/amd/ppr.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/iommu/amd/ppr.c +2 -1
@@ -151,7 +151,7 @@ static void iommu_call_iopf_notifier(str
 
 	/* Submit event */
 	iommu_report_device_fault(&pdev->dev, &event);
-
+	pci_dev_put(pdev);
 	return;
 
 out:
@@ -159,6 +159,7 @@ out:
 	amd_iommu_complete_ppr(&pdev->dev, PPR_PASID(raw[0]),
 			       IOMMU_PAGE_RESP_FAILURE,
 			       PPR_TAG(raw[0]) & 0x1FF);
+	pci_dev_put(pdev);
 }
 
 void amd_iommu_poll_ppr_log(struct amd_iommu *iommu)
author maintainer
#414 lore raw

PATCH 403/552 iommu/msm: Unwind probe state on registration failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Weimin Xiong <[email protected]>

commit 535a200220ca2c83bc8bf54bd2cbe045d6ee70c4 upstream.

msm_iommu_probe() adds its devm-managed IOMMU object to qcom_iommu_devices before adding the IOMMU sysfs device and registering it with the IOMMU core.

If iommu_device_sysfs_add() fails, probe returns with the object still on qcom_iommu_devices. The driver core then releases the devm allocation, leaving a dangling list entry that later list walks may dereference.

If iommu_device_register() fails, the same dangling list entry remains and the sysfs device is left registered as well.

Unwind the sysfs device and global list entry in reverse setup order on the corresponding failure paths.

Fixes 42df43b36163 ("iommu/msm: Make use of iommu_device_register interface")
---
 drivers/iommu/msm_iommu.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/iommu/msm_iommu.c +8 -2
@@ -783,19 +783,25 @@ static int msm_iommu_probe(struct platfo
 				     "msm-smmu.%pa", &ioaddr);
 	if (ret) {
 		pr_err("Could not add msm-smmu at %pa to sysfs\n", &ioaddr);
-		return ret;
+		goto err_remove_list;
 	}
 
 	ret = iommu_device_register(&iommu->iommu, &msm_iommu_ops, &pdev->dev);
 	if (ret) {
 		pr_err("Could not register msm-smmu at %pa\n", &ioaddr);
-		return ret;
+		goto err_remove_sysfs;
 	}
 
 	pr_info("device mapped at %p, irq %d with %d ctx banks\n",
 		iommu->base, iommu->irq, iommu->ncb);
 
 	return ret;
+
+err_remove_sysfs:
+	iommu_device_sysfs_remove(&iommu->iommu);
+err_remove_list:
+	list_del(&iommu->dev_node);
+	return ret;
 }
 
 static const struct of_device_id msm_iommu_dt_match[] = {
author maintainer
#415 lore raw

PATCH 404/552 iommu/sva: Set handle->dev before the SVA handle is visible

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Shuai Xue <[email protected]>

commit 530f8f9c3546cb3ebee1b135375aaee08a073ebb upstream.

iommu_attach_device_pasid() installs the new SVA attach handle in the group PASID lookup before iommu_sva_bind_device() returns. A concurrent bind can therefore find and reuse the same handle after iommu_sva_lock is dropped.

handle->dev was initialized after dropping iommu_sva_lock. This leaves a window where a racing bind can return a handle whose dev pointer is still NULL. A subsequent iommu_sva_unbind_device() can then dereference it via handle->dev->iommu_group.

Initialize handle->dev before releasing iommu_sva_lock so any visible SVA handle is fully initialized.

Fixes be51b1d6bbff ("iommu/sva: Refactoring iommu_sva_bind/unbind_device()")
Assisted-by Qoder:Qwen-3.8-MAX-Preview
---
 drivers/iommu/iommu-sva.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iommu/iommu-sva.c +1 -1
@@ -145,8 +145,8 @@ struct iommu_sva *iommu_sva_bind_device(
 	list_add(&domain->next, &iommu_mm->sva_domains);
 out:
 	refcount_set(&handle->users, 1);
-	mutex_unlock(&iommu_sva_lock);
 	handle->dev = dev;
+	mutex_unlock(&iommu_sva_lock);
 	return handle;
 
 out_free_domain:
author maintainer
#416 lore raw

PATCH 405/552 iommu/tegra241-cmdqv: Reject a vSID wider than the SID_MATCH field

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nicolin Chen <[email protected]>

commit 4379610c79bd88ddbea10e7f6c21e16d4b338c6b upstream.

tegra241_vintf_init_vsid() programs the guest-provided vSID into SID_MATCH, whose VIRT_SID field spans bits [20:1] with bit 0 as the match-enable flag. The HW therefore matches only a 20-bit Stream ID.

The bound check rejects only virt_sid > UINT_MAX, which admits a value far wider than the field. The write "virt_sid << 1 | 0x1" then drops every bit above 20: a virt_sid of 0x80000000 lands as SID_MATCH = 0x1, a valid match on vSID 0, so the entry aliases the wrong Stream ID. Because vdev->virt_id is guest-controlled, a VMM can trigger it.

Validate virt_sid against the field width with FIELD_MAX(), and program the register with FIELD_PREP() so the value and the field stay consistent.

Fixes 4dc0d12474f9 ("iommu/tegra241-cmdqv: Add user-space use support")
Assisted-by Claude:claude-opus-4-8
---
 drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +6 -2
@@ -58,6 +58,8 @@
 #define  VINTF_ENABLED			BIT(0)
 
 #define TEGRA241_VINTF_SID_MATCH(s)	(0x0040 + 0x4*(s))
+#define  VINTF_SID_MATCH_VIRT_SID	GENMASK(20, 1)
+#define  VINTF_SID_MATCH_ENABLE		BIT(0)
 #define TEGRA241_VINTF_SID_REPLACE(s)	(0x0080 + 0x4*(s))
 
 #define TEGRA241_VINTF_LVCMDQ_ERR_MAP_64(m) \
@@ -1249,7 +1251,7 @@ static int tegra241_vintf_init_vsid(stru
 	u64 virt_sid = vdev->virt_id;
 	int sidx;
 
-	if (virt_sid > UINT_MAX)
+	if (virt_sid > FIELD_MAX(VINTF_SID_MATCH_VIRT_SID))
 		return -EINVAL;
 
 	WARN_ON_ONCE(master->num_streams != 1);
@@ -1261,7 +1263,9 @@ static int tegra241_vintf_init_vsid(stru
 		return sidx;
 
 	writel(stream->id, REG_VINTF(vintf, SID_REPLACE(sidx)));
-	writel(virt_sid << 1 | 0x1, REG_VINTF(vintf, SID_MATCH(sidx)));
+	writel(FIELD_PREP(VINTF_SID_MATCH_VIRT_SID, virt_sid) |
+		       VINTF_SID_MATCH_ENABLE,
+	       REG_VINTF(vintf, SID_MATCH(sidx)));
 	dev_dbg(vintf->cmdqv->dev,
 		"VINTF%u: allocated SID_REPLACE%d for pSID=%x, vSID=%x\n",
 		vintf->idx, sidx, stream->id, (u32)virt_sid);
author maintainer
#417 lore raw

PATCH 406/552 iommu/arm-smmu-v3: Manage teardown with devm

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Shameer Kolothum <[email protected]>

commit 2bd22a0d40503a65d243b011de146603c8ce1cbc upstream.

arm_smmu_device_remove() manually frees the IOPF queue, destroys the vmid_map and disables the device, while the IRQs and queues are devm managed. devm unwinds only after remove() returns, so the cleanup runs in the wrong order. The IOPF queue is freed before the event-queue IRQ whose handler uses it.

Manage all of it with devm so the unwind order is correct. Free the IOPF queue and vmid_map via devm actions, and disable the device from one registered after arm_smmu_device_reset().

This is also a prerequisite for fixing a Tegra241 CMDQV CMD_SYNC use-after-free in the subsequent patch.

---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c |   56 ++++++++++++++++++++--------
 1 file changed, 40 insertions(+), 16 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +40 -16
@@ -3837,6 +3837,20 @@ int arm_smmu_cmdq_init(struct arm_smmu_d
 	return 0;
 }
 
+static void arm_smmu_free_iopf_action(void *data)
+{
+	struct iopf_queue *queue = data;
+
+	iopf_queue_free(queue);
+}
+
+static void arm_smmu_destroy_vmid_map(void *data)
+{
+	struct ida *ida = data;
+
+	ida_destroy(ida);
+}
+
 static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
 {
 	int ret;
@@ -3864,6 +3878,11 @@ static int arm_smmu_init_queues(struct a
 		smmu->evtq.iopf = iopf_queue_alloc(dev_name(smmu->dev));
 		if (!smmu->evtq.iopf)
 			return -ENOMEM;
+		ret = devm_add_action_or_reset(smmu->dev,
+					       arm_smmu_free_iopf_action,
+					       smmu->evtq.iopf);
+		if (ret)
+			return ret;
 	}
 
 	/* priq */
@@ -3942,7 +3961,8 @@ static int arm_smmu_init_strtab(struct a
 
 	ida_init(&smmu->vmid_map);
 
-	return 0;
+	return devm_add_action_or_reset(smmu->dev, arm_smmu_destroy_vmid_map,
+					&smmu->vmid_map);
 }
 
 static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
@@ -4155,6 +4175,13 @@ static int arm_smmu_device_disable(struc
 	return ret;
 }
 
+static void arm_smmu_disable_action(void *data)
+{
+	struct arm_smmu_device *smmu = data;
+
+	arm_smmu_device_disable(smmu);
+}
+
 static void arm_smmu_write_strtab(struct arm_smmu_device *smmu)
 {
 	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
@@ -4878,7 +4905,7 @@ static int arm_smmu_device_probe(struct
 	/* Initialise in-memory data structures */
 	ret = arm_smmu_init_structures(smmu);
 	if (ret)
-		goto err_free_iopf;
+		return ret;
 
 	/* Record our private device structure */
 	platform_set_drvdata(pdev, smmu);
@@ -4888,30 +4915,30 @@ static int arm_smmu_device_probe(struct
 
 	/* Reset the device */
 	ret = arm_smmu_device_reset(smmu);
+	if (ret) {
+		arm_smmu_device_disable(smmu);
+		return ret;
+	}
+
+	/* Register last so it unwinds first, while the CMDQ is still up. */
+	ret = devm_add_action_or_reset(smmu->dev, arm_smmu_disable_action, smmu);
 	if (ret)
-		goto err_disable;
+		return ret;
 
 	/* And we're up. Go go go! */
 	ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL,
 				     "smmu3.%pa", &ioaddr);
 	if (ret)
-		goto err_disable;
+		return ret;
 
 	ret = iommu_device_register(&smmu->iommu, &arm_smmu_ops, dev);
 	if (ret) {
 		dev_err(dev, "Failed to register iommu\n");
-		goto err_free_sysfs;
+		iommu_device_sysfs_remove(&smmu->iommu);
+		return ret;
 	}
 
 	return 0;
-
-err_free_sysfs:
-	iommu_device_sysfs_remove(&smmu->iommu);
-err_disable:
-	arm_smmu_device_disable(smmu);
-err_free_iopf:
-	iopf_queue_free(smmu->evtq.iopf);
-	return ret;
 }
 
 static void arm_smmu_device_remove(struct platform_device *pdev)
@@ -4920,9 +4947,6 @@ static void arm_smmu_device_remove(struc
 
 	iommu_device_unregister(&smmu->iommu);
 	iommu_device_sysfs_remove(&smmu->iommu);
-	arm_smmu_device_disable(smmu);
-	iopf_queue_free(smmu->evtq.iopf);
-	ida_destroy(&smmu->vmid_map);
 }
 
 static void arm_smmu_device_shutdown(struct platform_device *pdev)
author maintainer
#418 lore raw

PATCH 407/552 iommu: Fix dev_iommu memory leak when device_add fails in iommu_mock_device_add

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Peiyang He <[email protected]>

commit b7b0b3851474883d4aba6ed72da87141204b23e5 upstream.

iommu_mock_device_add() first calls iommu_fwspec_init(), which on success allocates both dev->iommu (via dev_iommu_get()) and dev->iommu->fwspec. If the subsequent device_add(dev) call fails, the error path only calls iommu_fwspec_free(dev), which frees fwspec but leaves dev->iommu still allocated.

This triggers the following kmemleak report when fuzzing with Syzkaller:

BUG: memory leak
unreferenced object 0xffff888011e0a200 (size 192):
  comm "syz.1.1695", pid 24885, jiffies 4295222527
  hex dump (first 32 bytes):
    00 00 00 00 00 00 00 00 00 00 00 00 ad 4e ad de  .............N..
    ff ff ff ff 00 00 00 00 ff ff ff ff ff ff ff ff  ................
  backtrace (crc 25df5bb3):
    kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
    slab_post_alloc_hook mm/slub.c:4575 [inline]
    slab_alloc_node mm/slub.c:4899 [inline]
    __kmalloc_cache_noprof+0x47a/0x710 mm/slub.c:5415
    kmalloc_noprof include/linux/slab.h:950 [inline]
    kzalloc_noprof include/linux/slab.h:1188 [inline]
    dev_iommu_get+0x10c/0x1a0 drivers/iommu/iommu.c:408
    iommu_fwspec_init+0x288/0x4d0 drivers/iommu/iommu.c:3087
    iommu_mock_device_add+0x46/0xb0 drivers/iommu/iommu.c:385
    mock_dev_create drivers/iommu/iommufd/selftest.c:1025 [inline]
    iommufd_test_mock_domain drivers/iommu/iommufd/selftest.c:1066 [inline]
    iommufd_test+0x2f8a/0x6190 drivers/iommu/iommufd/selftest.c:2072
    iommufd_fops_ioctl+0x367/0x540 drivers/iommu/iommufd/main.c:533
    vfs_ioctl fs/ioctl.c:51 [inline]
    __do_sys_ioctl fs/ioctl.c:597 [inline]
    __se_sys_ioctl fs/ioctl.c:583 [inline]
    __x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:583
    do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
    do_syscall_64+0x116/0x800 arch/x86/entry/syscall_64.c:94
    entry_SYSCALL_64_after_hwframe+0x77/0x7f

Fix this by calling dev_iommu_free(dev) instead of iommu_fwspec_free(dev) in the device_add() failure path. dev_iommu_free() frees both fwspec and the outer dev_iommu struct and clears dev->iommu.

---
 drivers/iommu/iommu.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iommu/iommu.c +1 -1
@@ -360,7 +360,7 @@ int iommu_mock_device_add(struct device
 
 	rc = device_add(dev);
 	if (rc)
-		iommu_fwspec_free(dev);
+		dev_iommu_free(dev);
 	return rc;
 }
 EXPORT_SYMBOL_GPL(iommu_mock_device_add);
author maintainer
#419 lore raw

PATCH 408/552 iommu/vt-d: Fix no_iommu to disable platform opt-in

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kevin Tian <[email protected]>

commit 219cc978d69ce9b538d0d73936c569d4ca5b0a24 upstream.

If user explicitly requests to disable iommu (via "iommu=off" or "intel_iommu=off"), there is no reason to force enabling it due to platform opt-in (for external-facing devices). User should be aware of any security implication of doing so.

"intel_iommu=off" implements this policy by setting no_platform_optin to skip platform opt-in in platform_optin_force_iommu().

However, "iommu=off" (no_iommu=1) doesn't set no_platform_optin hence is broken in this aspect:

- detect_intel_iommu() doesn't request ACS if no_iommu=1
- platform_optin_force_iommu() forces iommu on if external-facing devices exist and no_platform_optin is not set

This leads to a bad configuration with ACS disabled while DMA remapping is enabled.

Instead of setting no_platform_optin (will soon be removed) for no_iommu=1, directly check no_iommu in platform_optin_force_iommu().

Fixes 89a6079df791 ("iommu/vt-d: Force IOMMU on for platform opt in hint")
---
 drivers/iommu/intel/iommu.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/iommu/intel/iommu.c +3 -3
@@ -2956,10 +2956,11 @@ static bool has_external_pci(void)
 
 static int __init platform_optin_force_iommu(void)
 {
-	if (!dmar_platform_optin() || no_platform_optin || !has_external_pci())
+	if (no_iommu || !dmar_platform_optin() || no_platform_optin ||
+	    !has_external_pci())
 		return 0;
 
-	if (no_iommu || dmar_disabled)
+	if (dmar_disabled)
 		pr_info("Intel-IOMMU force enabled due to platform opt in\n");
 
 	/*
@@ -2970,7 +2971,6 @@ static int __init platform_optin_force_i
 		iommu_set_default_passthrough(false);
 
 	dmar_disabled = 0;
-	no_iommu = 0;
 
 	return 1;
 }
author maintainer
#420 lore raw

PATCH 409/552 iommu/vt-d: Force requesting ACS when tboot is enabled

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kevin Tian <[email protected]>

commit 607432b2618b61df81134be0ef2562b8300c1216 upstream.

Currently the conditions of requesting ACS in detect_intel_iommu() don't include tboot, leading to a possible misconfiguration with ACS disabled (e.g. due to user opts) while iommu is later forced on by tboot_force_iommu().

Fix it by checking tboot in detect_intel_iommu().

Fixes 5d990b627537 ("PCI: add pci_request_acs")
---
 drivers/iommu/intel/dmar.c  |   15 +++++++++++++--
 drivers/iommu/intel/iommu.c |    2 +-
 drivers/iommu/intel/iommu.h |    2 ++
 3 files changed, 16 insertions(+), 3 deletions(-)
drivers/iommu/intel/dmar.c +13 -2
@@ -915,6 +915,18 @@ dmar_validate_one_drhd(struct acpi_dmar_
 	return 0;
 }
 
+static bool dmar_required(void)
+{
+	/* tboot supersedes any user/platform opt */
+	if (!intel_iommu_tboot_noforce && tboot_enabled())
+		return true;
+
+	if (!no_iommu && (!dmar_disabled || dmar_platform_optin()))
+		return true;
+
+	return false;
+}
+
 void __init detect_intel_iommu(void)
 {
 	int ret;
@@ -928,8 +940,7 @@ void __init detect_intel_iommu(void)
 	if (!ret)
 		ret = dmar_walk_dmar_table((struct acpi_table_dmar *)dmar_tbl,
 					   &validate_drhd_cb);
-	if (!ret && !no_iommu && !iommu_detected &&
-	    (!dmar_disabled || dmar_platform_optin())) {
+	if (!ret && !iommu_detected && dmar_required()) {
 		iommu_detected = 1;
 		/* Make sure ACS will be enabled */
 		pci_request_acs();
drivers/iommu/intel/iommu.c +1 -1
@@ -64,7 +64,7 @@ static int rwbf_quirk;
  * (used when kernel is launched w/ TXT)
  */
 static int force_on = 0;
-static int intel_iommu_tboot_noforce;
+int intel_iommu_tboot_noforce;
 static int no_platform_optin;
 
 #define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry))
drivers/iommu/intel/iommu.h +2 -0
@@ -1434,6 +1434,7 @@ static inline bool ecmd_has_pmu_essentia
 
 extern int dmar_disabled;
 extern int intel_iommu_enabled;
+extern int intel_iommu_tboot_noforce;
 #else
 static inline int iommu_calculate_agaw(struct intel_iommu *iommu)
 {
@@ -1446,6 +1447,7 @@ static inline int iommu_calculate_max_sa
 #define dmar_disabled	(1)
 #define intel_iommu_enabled (0)
 #define intel_iommu_sm (0)
+#define intel_iommu_tboot_noforce (0)
 #endif
 
 static inline const char *decode_prq_descriptor(char *str, size_t size,
author maintainer
#421 lore raw

PATCH 410/552 iommufd: Avoid locking internal accesses during unmap

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Shuai Xue <[email protected]>

commit 0dbcdf4473a614adbd732d567c9b39ac0e040e0c upstream.

iommufd_access_notify_unmap() skips internal accesses because they do not have an external unmap callback to invoke.

However, the current test calls iommufd_lock_obj() before checking whether the access is internal. If iommufd_lock_obj() succeeds, the loop then sees the internal access and continues, bypassing the matching iommufd_put_object() used by the normal unmap path. This leaks the object reference taken by iommufd_lock_obj().

Check for internal accesses first so skipped entries are never locked.

Fixes 27b77ea5feaa ("iommufd/access: Bypass access->ops->unmap for internal use")
Assisted-by Qoder:Qwen-3.8-MAX-Preview
---
 drivers/iommu/iommufd/device.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/iommu/iommufd/device.c +2 -2
index 170a7005f0bc..e4537db724d5 100644
@@ -1307,8 +1307,8 @@ void iommufd_access_notify_unmap(struct io_pagetable *iopt, unsigned long iova,
 
 	xa_lock(&ioas->iopt.access_list);
 	xa_for_each(&ioas->iopt.access_list, index, access) {
-		if (!iommufd_lock_obj(&access->obj) ||
-		    iommufd_access_is_internal(access))
+		if (iommufd_access_is_internal(access) ||
+		    !iommufd_lock_obj(&access->obj))
 			continue;
 		xa_unlock(&ioas->iopt.access_list);
 
Signature · 1 line
2.55.0
author maintainer
#422 lore raw

PATCH 411/552 iommufd: Release current IOAS on xa_store() failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Shuai Xue <[email protected]>

commit 4ac2ce123824d5f885c868fa1f9f4d463141a2ba upstream.

iommufd_take_all_iova_rwsem() takes an object reference and the iova_rwsem write lock before storing the IOAS in the temporary ioas_list xarray.

If xa_store() fails, the current IOAS has not been inserted into ioas_list yet. iommufd_release_all_iova_rwsem() only unwinds IOAS objects already present in that xarray, so it cannot release the current IOAS.

Release the current IOAS rwsem and object reference before unwinding the previously stored entries.

Fixes 051ae5aa73d7 ("iommufd: Lock all IOAS objects")
Assisted-by Qoder:Qwen-3.8-MAX-Preview
---
 drivers/iommu/iommufd/ioas.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/iommu/iommufd/ioas.c +2 -0
@@ -433,6 +433,8 @@ static int iommufd_take_all_iova_rwsem(s
 
 		rc = xa_err(xa_store(ioas_list, index, ioas, GFP_KERNEL));
 		if (rc) {
+			up_write(&ioas->iopt.iova_rwsem);
+			refcount_dec(&ioas->obj.users);
 			iommufd_release_all_iova_rwsem(ictx, ioas_list);
 			return rc;
 		}
author maintainer
#423 lore raw

PATCH 412/552 iommufd: Fix UAF in selftest IOPF reporting

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Peiyang He <[email protected]>

commit 8c07df7cdfcf52f1ff276c588612aabc6c6b8399 upstream.

IOMMUFD selftest TRIGGER_IOPF borrows an attach handle from group->pasid_array without synchronizing against PASID detach, then a concurrent iommu_report_device_fault() can dereference that borrowed handle's domain pointer after the detach erases the handle and frees the backing struct iommufd_attach_handle. TRIGGER_IOPF then dereferences the freed handle, causing a UAF.

Fix by adding a iopf_rwsem in mock_dev to follow the expected design of a real driver. Hold its read side across the whole iommu_report_device_fault() call, and its write side around every path that attaches, detaches, or replaces a device domain. This can block new reports and drains in-flight reports before an old attach handle or the IOPF fault parameter can be removed. Also take the write side while registering a mock device, since it can invoke the mock driver's default-domain attach callback.

---
 drivers/iommu/iommufd/selftest.c |   31 ++++++++++++++++++++++++++++++-
 1 file changed, 30 insertions(+), 1 deletion(-)
drivers/iommu/iommufd/selftest.c +30 -1
@@ -183,6 +183,7 @@ struct mock_dev {
 	struct device dev;
 	struct mock_viommu *viommu;
 	struct rw_semaphore viommu_rwsem;
+	struct rw_semaphore iopf_rwsem;
 	unsigned long flags;
 	unsigned long vdev_id;
 	int id;
@@ -1101,6 +1102,7 @@ static struct mock_dev *mock_dev_create(
 		return ERR_PTR(-ENOMEM);
 
 	init_rwsem(&mdev->viommu_rwsem);
+	init_rwsem(&mdev->iopf_rwsem);
 	device_initialize(&mdev->dev);
 	mdev->flags = dev_flags;
 	mdev->dev.release = mock_dev_release;
@@ -1126,7 +1128,9 @@ static struct mock_dev *mock_dev_create(
 		goto err_put;
 	}
 
+	down_write(&mdev->iopf_rwsem);
 	rc = iommu_mock_device_add(&mdev->dev, &mock_iommu.iommu_dev);
+	up_write(&mdev->iopf_rwsem);
 	if (rc)
 		goto err_put;
 	return mdev;
@@ -1181,7 +1185,9 @@ static int iommufd_test_mock_domain(stru
 	}
 	sobj->idev.idev = idev;
 
+	down_write(&sobj->idev.mock_dev->iopf_rwsem);
 	rc = iommufd_device_attach(idev, IOMMU_NO_PASID, &pt_id);
+	up_write(&sobj->idev.mock_dev->iopf_rwsem);
 	if (rc)
 		goto out_unbind;
 
@@ -1196,7 +1202,9 @@ static int iommufd_test_mock_domain(stru
 	return 0;
 
 out_detach:
+	down_write(&sobj->idev.mock_dev->iopf_rwsem);
 	iommufd_device_detach(idev, IOMMU_NO_PASID);
+	up_write(&sobj->idev.mock_dev->iopf_rwsem);
 out_unbind:
 	iommufd_device_unbind(idev);
 out_mdev:
@@ -1240,7 +1248,9 @@ static int iommufd_test_mock_domain_repl
 	if (IS_ERR(sobj))
 		return PTR_ERR(sobj);
 
+	down_write(&sobj->idev.mock_dev->iopf_rwsem);
 	rc = iommufd_device_replace(sobj->idev.idev, IOMMU_NO_PASID, &pt_id);
+	up_write(&sobj->idev.mock_dev->iopf_rwsem);
 	if (rc)
 		goto out_sobj;
 
@@ -1852,10 +1862,16 @@ static int iommufd_test_trigger_iopf(str
 {
 	struct iopf_fault event = {};
 	struct iommufd_device *idev;
+	struct mock_dev *mdev;
 
 	idev = iommufd_get_device(ucmd, cmd->trigger_iopf.dev_id);
 	if (IS_ERR(idev))
 		return PTR_ERR(idev);
+	if (!iommufd_selftest_is_mock_dev(idev->dev)) {
+		iommufd_put_object(ucmd->ictx, &idev->obj);
+		return -EINVAL;
+	}
+	mdev = to_mock_dev(idev->dev);
 
 	event.fault.prm.flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE;
 	if (cmd->trigger_iopf.pasid != IOMMU_NO_PASID)
@@ -1866,7 +1882,9 @@ static int iommufd_test_trigger_iopf(str
 	event.fault.prm.grpid = cmd->trigger_iopf.grpid;
 	event.fault.prm.perm = cmd->trigger_iopf.perm;
 
+	down_read(&mdev->iopf_rwsem);
 	iommu_report_device_fault(idev->dev, &event);
+	up_read(&mdev->iopf_rwsem);
 	iommufd_put_object(ucmd->ictx, &idev->obj);
 
 	return 0;
@@ -1974,14 +1992,19 @@ static int iommufd_test_pasid_attach(str
 	if (IS_ERR(sobj))
 		return PTR_ERR(sobj);
 
+	down_write(&sobj->idev.mock_dev->iopf_rwsem);
 	rc = iommufd_device_attach(sobj->idev.idev, cmd->pasid_attach.pasid,
 				   &cmd->pasid_attach.pt_id);
+	up_write(&sobj->idev.mock_dev->iopf_rwsem);
 	if (rc)
 		goto out_sobj;
 
 	rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
-	if (rc)
+	if (rc) {
+		down_write(&sobj->idev.mock_dev->iopf_rwsem);
 		iommufd_device_detach(sobj->idev.idev, cmd->pasid_attach.pasid);
+		up_write(&sobj->idev.mock_dev->iopf_rwsem);
+	}
 
 out_sobj:
 	iommufd_put_object(ucmd->ictx, &sobj->obj);
@@ -1998,8 +2021,10 @@ static int iommufd_test_pasid_replace(st
 	if (IS_ERR(sobj))
 		return PTR_ERR(sobj);
 
+	down_write(&sobj->idev.mock_dev->iopf_rwsem);
 	rc = iommufd_device_replace(sobj->idev.idev, cmd->pasid_attach.pasid,
 				    &cmd->pasid_attach.pt_id);
+	up_write(&sobj->idev.mock_dev->iopf_rwsem);
 	if (rc)
 		goto out_sobj;
 
@@ -2019,7 +2044,9 @@ static int iommufd_test_pasid_detach(str
 	if (IS_ERR(sobj))
 		return PTR_ERR(sobj);
 
+	down_write(&sobj->idev.mock_dev->iopf_rwsem);
 	iommufd_device_detach(sobj->idev.idev, cmd->pasid_detach.pasid);
+	up_write(&sobj->idev.mock_dev->iopf_rwsem);
 	iommufd_put_object(ucmd->ictx, &sobj->obj);
 	return 0;
 }
@@ -2030,7 +2057,9 @@ void iommufd_selftest_destroy(struct iom
 
 	switch (sobj->type) {
 	case TYPE_IDEV:
+		down_write(&sobj->idev.mock_dev->iopf_rwsem);
 		iommufd_device_detach(sobj->idev.idev, IOMMU_NO_PASID);
+		up_write(&sobj->idev.mock_dev->iopf_rwsem);
 		iommufd_device_unbind(sobj->idev.idev);
 		mock_dev_destroy(sobj->idev.mock_dev);
 		break;
author maintainer
#424 lore raw

PATCH 413/552 platform/x86: dell-wmi-sysman: Dont hex dump attribute security buffer

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: HyeongJun An <[email protected]>

commit 83c80495e45eddf64c6525fb582d8db68f256b71 upstream.

set_attribute() populates the security area of the BIOS attribute request buffer with the current admin password via populate_security_buffer(), then dumps the whole request buffer with print_hex_dump_bytes(). This can expose the plaintext admin password in the kernel log.

The same issue was fixed for the password attribute path by commit d1a196e0a6dc ("platform/x86: dell-wmi-sysman: Don't hex dump plaintext password data"). Remove the remaining dump from the BIOS attribute path.

Fixes e8a60aa7404b ("platform/x86: Introduce support for Systems Management Driver over WMI for Dell Systems")
Assisted-by Codex:gpt-5
---
 drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c |    1 -
 1 file changed, 1 deletion(-)
drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c +0 -1
@@ -84,7 +84,6 @@ int set_attribute(const char *a_name, co
 	if (ret < 0)
 		goto out;
 
-	print_hex_dump_bytes("set attribute data: ", DUMP_PREFIX_NONE, buffer, buffer_size);
 	ret = call_biosattributes_interface(wmi_priv.bios_attr_wdev,
 					    buffer, buffer_size,
 					    SETATTRIBUTE_METHOD_ID);
author maintainer
#425 lore raw

PATCH 414/552 platform/x86: ISST: Validate level in perf mask ioctls

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: HyeongJun An <[email protected]>

commit 80e0d353c86a9a168ad6d213f494796294381538 upstream.

isst_if_get_perf_level_mask() and isst_if_get_base_freq_mask() use the user-provided level as an index into perf_levels[] via _read_pp_level_info() and _read_bf_level_info(), but neither helper validates it first.

The adjacent level-info helpers reject levels above max_level before reading the same per-level register block. Add the same bounds checks to the mask helpers, and reject disabled SST-PP levels in isst_if_get_perf_level_mask() to match isst_if_get_perf_level_info().

This prevents out-of-bounds reads from the per-level offset table on invalid ioctl input.

Fixes ea009e4769fa3 ("platform/x86: ISST: Add SST-PP support via TPMI")
Fixes 06a61df83209 ("platform/x86: ISST: Add SST-BF support via TPMI")
Assisted-by Claude:claude-opus-5
---
 drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c |    9 +++++++++
 1 file changed, 9 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +9 -0
@@ -1260,6 +1260,12 @@ static int isst_if_get_perf_level_mask(v
 	if (!power_domain_info)
 		return -EINVAL;
 
+	if (cpumask.level > power_domain_info->max_level)
+		return -EINVAL;
+
+	if (!(power_domain_info->pp_header.level_en_mask & BIT(cpumask.level)))
+		return -EINVAL;
+
 	_read_pp_level_info("mask", mask, cpumask.level, SST_PP_INFO_2_OFFSET,
 			    SST_PP_RSLVD_CORE_MASK_START, SST_PP_RSLVD_CORE_MASK_WIDTH,
 			    SST_MUL_FACTOR_NONE)
@@ -1341,6 +1347,9 @@ static int isst_if_get_base_freq_mask(vo
 	if (!power_domain_info)
 		return -EINVAL;
 
+	if (cpumask.level > power_domain_info->max_level)
+		return -EINVAL;
+
 	_read_bf_level_info("BF-cpumask", mask, cpumask.level, SST_BF_INFO_1_OFFSET,
 			    P1_HI_CORE_MASK_START, P1_HI_CORE_MASK_WIDTH,
 			    SST_MUL_FACTOR_NONE)
author maintainer
#426 lore raw

PATCH 415/552 platform/x86: ISST: Validate socket ID in clos_assoc ioctl

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: HyeongJun An <[email protected]>

commit a89f07db0cb95c54dac4a8406c79a04e44a73c3c upstream.

isst_if_clos_assoc() validates the user-supplied socket_id with 'socket_id > topology_max_packages()', but isst_common.sst_inst[] is allocated with topology_max_packages() entries, so the valid index range is [0, topology_max_packages()). The '>' comparison lets socket_id == topology_max_packages() pass and index one entry past the array.

In addition, isst_common.sst_inst[socket_id] is NULL for an in-range package that has no bound TPMI SST instance, and the pointer is used without a NULL check. Both the out-of-bounds entry and the NULL pointer are then dereferenced by map_partition_power_domain_id() and the following power_domain_info access.

Reject socket_id >= topology_max_packages() and a NULL sst_inst, matching the checks already performed by get_instance().

Fixes 12a7d2cb811d ("platform/x86: ISST: Add SST-CP support via TPMI")
Assisted-by Claude:claude-opus-5
---
 drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +3 -1
@@ -729,7 +729,7 @@ static long isst_if_clos_assoc(void __us
 		if (copy_from_user(&clos_assoc, ptr, sizeof(clos_assoc)))
 			return -EFAULT;
 
-		if (clos_assoc.socket_id > topology_max_packages())
+		if (clos_assoc.socket_id >= topology_max_packages())
 			return -EINVAL;
 
 		cpu = clos_assoc.logical_cpu;
@@ -747,6 +747,8 @@ static long isst_if_clos_assoc(void __us
 		pkg_id = clos_assoc.socket_id;
 
 		sst_inst = isst_common.sst_inst[pkg_id];
+		if (!sst_inst)
+			return -EINVAL;
 
 		punit_id = map_partition_power_domain_id(sst_inst, punit_id, &part);
 		if (punit_id < 0)
author maintainer
#427 lore raw

PATCH 416/552 mmc: via-sdmmc: cancel card-detect work on remove

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Fan Wu <[email protected]>

commit 57e5d877f898d5e5c9d672a77bb6bdd24f0d9bf5 upstream.

Disabling the device interrupt and freeing the IRQ prevents new card-detect work from being queued, but carddet_work already queued by the handler can still run after via_sd_remove() returns. via_sdc_card_detect() recovers the host through container_of() and dereferences its MMIO base; once remove() returns the host can be freed, so that work would touch freed memory.

Cancel carddet_work after freeing the IRQ and before cancelling finish_bh_work, which the card-detect handler can also queue. carddet_work can re-enable the interrupt through via_reset_pcictrl(); mask it again afterwards.

This issue was found by an in-house static analysis tool and confirmed by manual code review.

Fixes f0bf7f61b840 ("mmc: Add new via-sdmmc host controller driver")
Assisted-by Codex:gpt-5.6
---
 drivers/mmc/host/via-sdmmc.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/mmc/host/via-sdmmc.c +4 -0
@@ -1200,6 +1200,10 @@ static void via_sd_remove(struct pci_dev
 
 	free_irq(pcidev->irq, sdhost);
 
+	cancel_work_sync(&sdhost->carddet_work);
+	/* carddet_work may re-enable the interrupt via via_reset_pcictrl(). */
+	writeb(0x0, sdhost->pcictrl_mmiobase + VIA_CRDR_PCIINTCTRL);
+
 	timer_delete_sync(&sdhost->timer);
 
 	cancel_work_sync(&sdhost->finish_bh_work);
author maintainer
#428 lore raw

PATCH 417/552 mmc: via-sdmmc: stop card-detect handling on probe failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Fan Wu <[email protected]>

commit 088eaa92fcebaa6b957ccf9635afdf39643a577d upstream.

request_irq() registers the SD card-detect interrupt and the probe enables it before mmc_add_host() runs. If mmc_add_host() fails, the error path only unmaps the registers and returns: the interrupt stays registered, so the handler keeps running against the host once it is freed. via_sdc_isr() dereferences sdhost and its MMIO base and schedules carddet_work, which via_sdc_card_detect() also runs against freed memory through its container_of() dereference.

Add a probe-error path that disables and frees the interrupt and cancels carddet_work before unmapping. carddet_work can re-enable the device interrupt via via_reset_pcictrl(), which restores PCIINTCTRL, so mask it again after cancelling the work.

This issue was found by an in-house static analysis tool and confirmed by manual code review.

Fixes e4e46fb61e3b ("mmc: via-sdmmc: fix return value check of mmc_add_host()")
Assisted-by Codex:gpt-5.6
---
 drivers/mmc/host/via-sdmmc.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/mmc/host/via-sdmmc.c +7 -1
@@ -1154,10 +1154,16 @@ static int via_sd_probe(struct pci_dev *
 
 	ret = mmc_add_host(mmc);
 	if (ret)
-		goto unmap;
+		goto free_irq;
 
 	return 0;
 
+free_irq:
+	writeb(0x0, sdhost->pcictrl_mmiobase + VIA_CRDR_PCIINTCTRL);
+	free_irq(pcidev->irq, sdhost);
+	cancel_work_sync(&sdhost->carddet_work);
+	/* carddet_work may re-enable the interrupt via via_reset_pcictrl(). */
+	writeb(0x0, sdhost->pcictrl_mmiobase + VIA_CRDR_PCIINTCTRL);
 unmap:
 	iounmap(sdhost->mmiobase);
 release:
author maintainer
#429 lore raw

PATCH 418/552 platform/x86: ISST: Add a NULL check for sst_inst[]

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Srinivas Pandruvada <[email protected]>

commit 3de2776e9d7073765c10c2326c2bda5926811ea6 upstream.

To be consistent with other places, add a NULL check for failed socket loading by checking isst_common.sst_inst[].

---
 drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +2 -0
@@ -1380,6 +1380,8 @@ static int isst_if_get_tpmi_instance_cou
 		return -EINVAL;
 
 	sst_inst = isst_common.sst_inst[tpmi_inst.socket_id];
+	if (!sst_inst)
+		return -EINVAL;
 
 	tpmi_inst.count = isst_instance_count(sst_inst);
 
author maintainer
#430 lore raw

PATCH 419/552 platform/x86: ISST: Just allow 2 bits for SST feature enable

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Srinivas Pandruvada <[email protected]>

commit 0f377f2b47646abe6ec3616ae6a8670d9ff7eb86 upstream.

Currently only 2 features SST-TF and SST-BF are supported, so only allow bit 0 and bit 1.

---
 drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +4 -0
@@ -846,6 +846,7 @@ static long isst_if_clos_assoc(void __us
 
 #define SST_PP_FEATURE_STATE_START	8
 #define SST_PP_FEATURE_STATE_WIDTH	8
+#define SST_PP_FEATURE_STATE_VALID_MASK	GENMASK(1, 0)
 
 #define SST_BF_FEATURE_SUPPORTED_START	12
 #define SST_BF_FEATURE_SUPPORTED_WIDTH	1
@@ -996,6 +997,9 @@ static int isst_if_set_perf_feature(void
 	if (power_domain_info->write_blocked)
 		return -EPERM;
 
+	if (perf_feature.feature & ~SST_PP_FEATURE_STATE_VALID_MASK)
+		return -EINVAL;
+
 	_write_pp_info("perf_feature", perf_feature.feature, SST_PP_CONTROL_OFFSET,
 		       SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH,
 		       SST_MUL_FACTOR_NONE)
author maintainer
#431 lore raw

PATCH 420/552 platform/x86: ISST: Use PP level enable mask

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Srinivas Pandruvada <[email protected]>

commit 9b9026943b19d06ebf520b1f4786621947cf43c8 upstream.

Add check for enabled levels only when reading MMIO. Some levels can be disabled by BIOS. If the level is not enabled, return an error.

Reset the enable and allowed level masks if there is a failure to add a perf level.

---
 drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c |   16 ++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +14 -2
@@ -336,8 +336,11 @@ static int sst_add_perf_profiles(struct
 	int i;
 
 	pd_info->perf_levels = devm_kcalloc(dev, levels, sizeof(struct perf_level), GFP_KERNEL);
-	if (!pd_info->perf_levels)
+	if (!pd_info->perf_levels) {
+		pd_info->pp_header.allowed_level_mask = 0;
+		pd_info->pp_header.level_en_mask = 0;
 		return 0;
+	}
 
 	pd_info->ratio_unit = pd_info->pp_header.ratio_unit;
 	pd_info->avx_levels = SST_MAX_AVX_LEVELS;
@@ -879,7 +882,7 @@ static int isst_if_get_perf_level(void _
 		      SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH, SST_MUL_FACTOR_NONE)
 	perf_level.enabled = !!(power_domain_info->sst_header.cap_mask & BIT(1));
 
-	level_mask = perf_level.level_mask;
+	level_mask = perf_level.level_mask & power_domain_info->pp_header.level_en_mask;
 	perf_level.sst_bf_support = 0;
 	for_each_set_bit(level, &level_mask, BITS_PER_BYTE) {
 		/*
@@ -1317,6 +1320,9 @@ static int isst_if_get_base_freq_info(vo
 	if (base_freq.level > power_domain_info->max_level)
 		return -EINVAL;
 
+	if (!(power_domain_info->pp_header.level_en_mask & BIT(base_freq.level)))
+		return -EINVAL;
+
 	_read_bf_level_info("p1_high", base_freq.high_base_freq_mhz, base_freq.level,
 			    SST_BF_INFO_0_OFFSET, SST_BF_P1_HIGH_START, SST_BF_P1_HIGH_WIDTH,
 			    SST_MUL_FACTOR_FREQ)
@@ -1356,6 +1362,9 @@ static int isst_if_get_base_freq_mask(vo
 	if (cpumask.level > power_domain_info->max_level)
 		return -EINVAL;
 
+	if (!(power_domain_info->pp_header.level_en_mask & BIT(cpumask.level)))
+		return -EINVAL;
+
 	_read_bf_level_info("BF-cpumask", mask, cpumask.level, SST_BF_INFO_1_OFFSET,
 			    P1_HI_CORE_MASK_START, P1_HI_CORE_MASK_WIDTH,
 			    SST_MUL_FACTOR_NONE)
@@ -1453,6 +1462,9 @@ static int isst_if_get_turbo_freq_info(v
 	if (turbo_freq.level > power_domain_info->max_level)
 		return -EINVAL;
 
+	if (!(power_domain_info->pp_header.level_en_mask & BIT(turbo_freq.level)))
+		return -EINVAL;
+
 	turbo_freq.max_buckets = TRL_MAX_BUCKETS;
 	turbo_freq.max_trl_levels = TRL_MAX_LEVELS;
 	turbo_freq.max_clip_freqs = SST_TF_MAX_LP_CLIP_RATIOS;
author maintainer
#432 lore raw

PATCH 421/552 platform/x86: ISST: Validate logical CPU id and clos id

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Srinivas Pandruvada <[email protected]>

commit 124e2dbabe460c2a6e7440f4ad8af560131295c9 upstream.

Validate max CLOS ID and logical CPU ID for core power feature. Reject any clos level or logical CPU number greater than the supported maximum. These are used to calculate MMIO offset.

---
 drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c |   13 ++++++++++++
 1 file changed, 13 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +13 -0
@@ -652,6 +652,8 @@ static long isst_if_core_power_state(voi
 #define SST_CLOS_CONFIG_MAX_START	16
 #define SST_CLOS_CONFIG_MAX_WIDTH	8
 
+#define SST_MAX_CLOS			3
+
 static long isst_if_clos_param(void __user *argp)
 {
 	struct tpmi_per_power_domain_info *power_domain_info;
@@ -660,6 +662,9 @@ static long isst_if_clos_param(void __us
 	if (copy_from_user(&clos_param, argp, sizeof(clos_param)))
 		return -EFAULT;
 
+	if (clos_param.clos > SST_MAX_CLOS)
+		return -EINVAL;
+
 	power_domain_info = get_instance(clos_param.socket_id, clos_param.power_domain_id);
 	if (!power_domain_info)
 		return -EINVAL;
@@ -706,6 +711,8 @@ static long isst_if_clos_param(void __us
 #define SST_CLOS_ASSOC_CPUS_PER_REG	16
 #define SST_CLOS_ASSOC_BITS_PER_CPU	4
 
+#define SST_CLOS_ASSOC_MAX_LOGICAL_CPU	63
+
 static long isst_if_clos_assoc(void __user *argp)
 {
 	struct isst_if_clos_assoc_cmds assoc_cmds;
@@ -732,9 +739,15 @@ static long isst_if_clos_assoc(void __us
 		if (copy_from_user(&clos_assoc, ptr, sizeof(clos_assoc)))
 			return -EFAULT;
 
+		if (clos_assoc.clos > SST_MAX_CLOS)
+			return -EINVAL;
+
 		if (clos_assoc.socket_id >= topology_max_packages())
 			return -EINVAL;
 
+		if (clos_assoc.logical_cpu > SST_CLOS_ASSOC_MAX_LOGICAL_CPU)
+			return -EINVAL;
+
 		cpu = clos_assoc.logical_cpu;
 		clos = clos_assoc.clos;
 
author maintainer
#433 lore raw

PATCH 422/552 platform/x86: ISST: Validate parameter for core power state

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Srinivas Pandruvada <[email protected]>

commit 1700b4f804555467b7eff58dff7acc11d508b3a1 upstream.

Allow only 0 or 1 for core_power enable and priority_type parameters.

---
 drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c |    7 +++++++
 1 file changed, 7 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +7 -0
@@ -602,6 +602,9 @@ static bool disable_dynamic_sst_features
 #define SST_CP_PRIORITY_TYPE_START	1
 #define SST_CP_PRIORITY_TYPE_WIDTH	1
 
+#define SST_CP_MAX_ENABLE		1
+#define SST_CP_MAX_PRIORITY_TYPE	1
+
 static long isst_if_core_power_state(void __user *argp)
 {
 	struct tpmi_per_power_domain_info *power_domain_info;
@@ -621,6 +624,10 @@ static long isst_if_core_power_state(voi
 		if (power_domain_info->write_blocked)
 			return -EPERM;
 
+		if (core_power.enable > SST_CP_MAX_ENABLE ||
+		    core_power.priority_type > SST_CP_MAX_PRIORITY_TYPE)
+			return -EINVAL;
+
 		_write_cp_info("cp_enable", core_power.enable, SST_CP_CONTROL_OFFSET,
 			       SST_CP_ENABLE_START, SST_CP_ENABLE_WIDTH, SST_MUL_FACTOR_NONE)
 		_write_cp_info("cp_prio_type", core_power.priority_type, SST_CP_CONTROL_OFFSET,
author maintainer
#434 lore raw

PATCH 423/552 platform/x86: ISST: Validate parameter for frequency and priority

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Srinivas Pandruvada <[email protected]>

commit 574b59bb4b6bfcfd1f639d02f1041b314d43a2e6 upstream.

Validate range for frequency and proportional priority while setting CLOS parameters.

---
 drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +12 -0
@@ -661,6 +661,9 @@ static long isst_if_core_power_state(voi
 
 #define SST_MAX_CLOS			3
 
+#define SST_MAX_FREQ			0xff
+#define SST_CLOS_MAX_PRIORITY		0x0f
+
 static long isst_if_clos_param(void __user *argp)
 {
 	struct tpmi_per_power_domain_info *power_domain_info;
@@ -680,6 +683,15 @@ static long isst_if_clos_param(void __us
 		if (power_domain_info->write_blocked)
 			return -EPERM;
 
+		if (!in_range(clos_param.min_freq_mhz / SST_MUL_FACTOR_FREQ, 0, SST_MAX_FREQ + 1))
+			return -EINVAL;
+
+		if (!in_range(clos_param.max_freq_mhz / SST_MUL_FACTOR_FREQ, 0, SST_MAX_FREQ + 1))
+			return -EINVAL;
+
+		if (!in_range(clos_param.prop_prio, 0, SST_CLOS_MAX_PRIORITY + 1))
+			return -EINVAL;
+
 		_write_cp_info("clos.min_freq", clos_param.min_freq_mhz,
 			       (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE),
 			       SST_CLOS_CONFIG_MIN_START, SST_CLOS_CONFIG_MIN_WIDTH,
author maintainer
#435 lore raw

PATCH 424/552 platform/x86: ISST: Return error during profile addition

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Srinivas Pandruvada <[email protected]>

commit f9a647cb8d90c09633a49a1e766e140e78012444 upstream.

If sst_add_perf_profiles() fails for memory allocation, it continues to allow SST-CP (core-power) feature. But in practice this is not very useful as to achieve some frequencies via SST-CP, an SST-PP (perf-profile) level change is required.

Fixes 0ab147bb840f ("platform/x86: ISST: Parse SST MMIO and update instance")
Cc HyeongJun An <[email protected]>
---
 drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c |   10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +7 -3
@@ -339,7 +339,7 @@ static int sst_add_perf_profiles(struct
 	if (!pd_info->perf_levels) {
 		pd_info->pp_header.allowed_level_mask = 0;
 		pd_info->pp_header.level_en_mask = 0;
-		return 0;
+		return -ENOMEM;
 	}
 
 	pd_info->ratio_unit = pd_info->pp_header.ratio_unit;
@@ -370,7 +370,7 @@ static int sst_add_perf_profiles(struct
 static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domain_info *pd_info)
 {
 	struct device *dev = &auxdev->dev;
-	int i, mask, levels;
+	int i, ret, mask, levels;
 
 	*((u64 *)&pd_info->sst_header) = readq(pd_info->sst_base);
 	pd_info->sst_header.cp_offset *= 8;
@@ -402,8 +402,12 @@ static int sst_main(struct auxiliary_dev
 			levels = i;
 		mask <<= 1;
 	}
+
+	ret = sst_add_perf_profiles(auxdev, pd_info, levels + 1);
+	if (ret)
+		return ret;
+
 	pd_info->max_level = levels;
-	sst_add_perf_profiles(auxdev, pd_info, levels + 1);
 
 	return 0;
 }
author maintainer
#436 lore raw

PATCH 425/552 platform/x86: ishtp_eclite: Fix ACPI device reference leak in probe error path

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ma Ke <[email protected]>

commit 62b57396c26a1ce54963709928ea0d01fa522eea upstream.

ecl_ishtp_cl_probe() acquires a reference to an ACPI device via acpi_find_eclite_device() but fails to release it in the error path when acpi_opregion_init() fails. This results in a reference count leak, preventing proper cleanup of the ACPI device.

Calling path: acpi_find_eclite_device() -> acpi_dev_get_first_match_dev() -> acpi_dev_get_next_match_dev() -> bus_find_device() -> get_device().

Found by code review.

---
 drivers/platform/x86/intel/ishtp_eclite.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/platform/x86/intel/ishtp_eclite.c +4 -1
@@ -600,13 +600,16 @@ static int ecl_ishtp_cl_probe(struct ish
 	rv = acpi_opregion_init(opr_dev);
 	if (rv) {
 		dev_err(cl_data_to_dev(opr_dev), "ACPI opregion init failed\n");
-		goto err_exit;
+		goto err_put;
 	}
 
 	/* Reprobe devices depending on ECLite - battery, fan, etc. */
 	acpi_dev_clear_dependencies(opr_dev->adev);
 
 	return 0;
+
+err_put:
+	acpi_dev_put(opr_dev->adev);
 err_exit:
 	ishtp_set_connection_state(ecl_ishtp_cl, ISHTP_CL_DISCONNECTING);
 	ishtp_cl_disconnect(ecl_ishtp_cl);
author maintainer
#437 lore raw

PATCH 426/552 platform/x86: lenovo/ymc: Only match lower byte in WMI lid switch query response

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Julian Haarmann <[email protected]>

commit bbc497b5231829d32c3a53b6e66be1add76c0064 upstream.

On newer Lenovo Yoga devices like the "Yoga 9 2-in-1 14IPH11 - Type 83SE", the hinge switch WMI query returns extra data in the upper bits (e.g. 0x50001 laptop mode, 0x50002 tablet mode, ect.).

The driver previously checked for exact matches (0x01 laptop, 0x02 tablet, ect.) causing newer switches to not work.

Mask the WMI query result to only match the lower byte and ignore upper bits.

---
 drivers/platform/x86/lenovo/ymc.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/platform/x86/lenovo/ymc.c +7 -1
@@ -8,6 +8,8 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
 #include <linux/dmi.h>
 #include <linux/input.h>
 #include <linux/input/sparse-keymap.h>
@@ -20,6 +22,8 @@
 #define LENOVO_YMC_QUERY_INSTANCE 0
 #define LENOVO_YMC_QUERY_METHOD 0x01
 
+#define LENOVO_YMC_STATE_MASK GENMASK(7, 0)
+
 static bool force;
 module_param(force, bool, 0444);
 MODULE_PARM_DESC(force, "Force loading on boards without a convertible DMI chassis-type");
@@ -85,7 +89,9 @@ static void lenovo_ymc_notify(struct wmi
 			"WMI event data is not an integer\n");
 		goto free_obj;
 	}
-	code = obj->integer.value;
+
+	/* strip upper bits (e.g. 0x50000) on newer devices */
+	code = FIELD_GET(LENOVO_YMC_STATE_MASK, obj->integer.value);
 
 	if (!sparse_keymap_report_event(priv->input_dev, code, 1, true))
 		dev_warn(&wdev->dev, "Unknown key %d pressed\n", code);
author maintainer
#438 lore raw

PATCH 427/552 platform/x86: think-lmi: Fix certificate thumbprint sysfs output

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Thorsten Blum <[email protected]>

commit 4f3183f5ae9b8ddfe338d79a96146a05342bbe50 upstream.

cert_thumbprint() already returns the accumulated output length, but certificate_thumbprint_show() adds that value to count again, making the next line use the wrong offset. Errors returned by cert_thumbprint() are also ignored and their negative values added to count.

Assign the total length to count instead and propagate errors correctly.

---
 drivers/platform/x86/lenovo/think-lmi.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/platform/x86/lenovo/think-lmi.c +7 -1
@@ -741,6 +741,8 @@ static ssize_t certificate_thumbprint_sh
 		return -EOPNOTSUPP;
 
 	for (i = 0; i < ARRAY_SIZE(thumbtypes); i++) {
+		ssize_t ret;
+
 		if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
 			/* Format: 'SVC | SMC, Thumbtype' */
 			wmistr = kasprintf(GFP_KERNEL, "%s,%s",
@@ -752,8 +754,12 @@ static ssize_t certificate_thumbprint_sh
 		}
 		if (!wmistr)
 			return -ENOMEM;
-		count += cert_thumbprint(buf, wmistr, count);
+
+		ret = cert_thumbprint(buf, wmistr, count);
 		kfree(wmistr);
+		if (ret < 0)
+			return ret;
+		count = ret;
 	}
 
 	return count;
author maintainer
#439 lore raw

PATCH 428/552 platform/x86: think-lmi: Free system certificate signatures

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Thorsten Blum <[email protected]>

commit abca989604f60fe29d7170431f819e28ec7d868a upstream.

Multi-certificate support also allows the system authentication object to store ->signature and ->save_signature, which leak when the driver is removed. Free the signatures to avoid leaking memory.

---
 drivers/platform/x86/lenovo/think-lmi.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/platform/x86/lenovo/think-lmi.c +4 -0
@@ -1458,6 +1458,10 @@ static void tlmi_release_attr(void)
 	/* Free up any saved signatures */
 	kfree(tlmi_priv.pwd_admin->signature);
 	kfree(tlmi_priv.pwd_admin->save_signature);
+	if (tlmi_priv.pwd_system) {
+		kfree(tlmi_priv.pwd_system->signature);
+		kfree(tlmi_priv.pwd_system->save_signature);
+	}
 
 	/* Authentication structures */
 	list_for_each_entry_safe(pos, n, &tlmi_priv.authentication_kset->list, entry)
author maintainer
#440 lore raw

PATCH 429/552 platform/x86: think-lmi: Fix current password length check

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Thorsten Blum <[email protected]>

commit 54745d563114b74f6fecebce68cd020d06c1772b upstream.

current_password_store() checks the password length before removing the trailing newline, which can reject valid passwords that are exactly ->maxlen bytes long.

It also passes ->maxlen to strscpy(), which truncates passwords without a newline.

Use strchrnul() to measure the password length up to the newline, then copy that many bytes and add a trailing NUL terminator using strscpy().

Fixes a40cd7ef22fb ("platform/x86: think-lmi: Add WMI interface support on Lenovo platforms")
---
 drivers/platform/x86/lenovo/think-lmi.c |    7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
drivers/platform/x86/lenovo/think-lmi.c +3 -4
@@ -438,14 +438,13 @@ static ssize_t current_password_store(st
 	struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
 	size_t pwdlen;
 
-	pwdlen = strlen(buf);
+	/* Strip newline; setting password won't work if one is present. */
+	pwdlen = strchrnul(buf, '\n') - buf;
 	/* pwdlen == 0 is allowed to clear the password */
 	if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen)))
 		return -EINVAL;
 
-	strscpy(setting->password, buf, setting->maxlen);
-	/* Strip out CR if one is present, setting password won't work if it is present */
-	strreplace(setting->password, '\n', '\0');
+	strscpy(setting->password, buf, pwdlen + 1);
 	return count;
 }
 
author maintainer
#441 lore raw

PATCH 430/552 platform/chrome: sensorhub: Bound the EC-reported sensor number

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryam Vargas <[email protected]>

commit 833740a2333c2e4db4e02e3d0ffba04e8718a5f3 upstream.

Each EC FIFO event carries an 8-bit sensor number (in->sensor_num). cros_ec_sensorhub_ring_handler() validates the FIFO event count, the per-read count and the ring bound, but not the sensor number, which cros_ec_sensor_ring_process_event() then uses unchecked to index sensorhub->batch_state[] - allocated with only sensorhub->sensor_num entries. A sensor number of sensor_num or larger is an out-of-bounds read and write of batch_state[].

Validate the sensor number in the ring handler, where each event is read from the EC, and drop a malformed event before it is used.

Fixes 145d59baff59 ("platform/chrome: cros_ec_sensorhub: Add FIFO support")
---
 drivers/platform/chrome/cros_ec_sensorhub_ring.c |    8 ++++++++
 1 file changed, 8 insertions(+)
drivers/platform/chrome/cros_ec_sensorhub_ring.c +8 -0
@@ -879,6 +879,14 @@ static void cros_ec_sensorhub_ring_handl
 
 		for (in = sensorhub->resp->fifo_read.data, j = 0;
 		     j < number_data; j++, in++) {
+			/* Skip event if sensor_num from EC is out of bounds. */
+			if (in->sensor_num >= sensorhub->sensor_num) {
+				dev_warn_ratelimited(sensorhub->dev,
+						     "Invalid sensor number %u from EC\n",
+						     in->sensor_num);
+				continue;
+			}
+
 			if (cros_ec_sensor_ring_process_event(
 						sensorhub, fifo_info,
 						fifo_timestamp,
author maintainer
#442 lore raw

PATCH 431/552 platform/x86/amd/pmc: Restore msg_port on amd_stb_s2d_init() error paths

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Mario Limonciello <[email protected]>

commit 9cef693bce96bb4c6952f48d855284cf7fa4f367 upstream.

dev->msg_port is switched to MSG_PORT_S2D before issuing the S2D SMU commands but is only restored to MSG_PORT_PMC on the success path. The early "return -EIO" and "return -ENOMEM" leave the port stuck on MSG_PORT_S2D, so all subsequent SMU communication - including the s2idle prepare/restore handlers - is directed at the wrong mailbox.

Consolidate the exit path through a single label so the message port is always restored.

Fixes 3d7d407dfb05 ("platform/x86: amd-pmc: Add support for AMD Spill to DRAM STB feature")
---
 drivers/platform/x86/amd/pmc/mp1_stb.c |   24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)
drivers/platform/x86/amd/pmc/mp1_stb.c +15 -9
@@ -289,7 +289,7 @@ int amd_stb_s2d_init(struct amd_pmc_dev
 	u32 phys_addr_low, phys_addr_hi;
 	u64 stb_phys_addr;
 	u32 size = 0;
-	int ret;
+	int ret = 0;
 
 	if (!enable_stb)
 		return 0;
@@ -307,8 +307,10 @@ int amd_stb_s2d_init(struct amd_pmc_dev
 	dev->msg_port = MSG_PORT_S2D;
 
 	amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->stb_arg.s2d_msg_id, true);
-	if (size != S2D_TELEMETRY_BYTES_MAX)
-		return -EIO;
+	if (size != S2D_TELEMETRY_BYTES_MAX) {
+		ret = -EIO;
+		goto out;
+	}
 
 	/* Get DRAM size */
 	ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->stb_arg.s2d_msg_id, true);
@@ -321,12 +323,16 @@ int amd_stb_s2d_init(struct amd_pmc_dev
 
 	stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low);
 
-	/* Clear msg_port for other SMU operation */
-	dev->msg_port = MSG_PORT_PMC;
-
 	dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size);
-	if (!dev->stb_virt_addr)
-		return -ENOMEM;
+	if (!dev->stb_virt_addr) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = 0;
 
-	return 0;
+out:
+	/* Restore the default message port for subsequent SMU operations */
+	dev->msg_port = MSG_PORT_PMC;
+	return ret;
 }
author maintainer
#443 lore raw

PATCH 432/552 platform/x86/amd/pmc: Propagate SMU errors and validate S2D address

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Mario Limonciello <[email protected]>

commit 0225c1d637687b03726f00ac65b6def843d2c464 upstream.

amd_stb_s2d_init() discards the return value of several S2D SMU commands. When the SMU refuses a command (e.g. "SMU cmd failed. err: 0xff") the failure is only noticed indirectly - if at all - and reported as -EIO, masking the real error.

More seriously, the S2D_PHYS_ADDR_LOW/HIGH return values are ignored, so on failure phys_addr_low/hi are left uninitialised and the assembled address is passed straight to devm_ioremap(). When the SMU leaves them at zero this maps physical address 0 and trips the ioremap-on-RAM warning:

amd_pmc AMDI000B:00: SMU cmd failed. err: 0xff ioremap on RAM at 0x0000000000000000 - 0x0000000000ffffff

  WARNING: CPU: 13 PID: 4592 at arch/x86/mm/ioremap.c:...

Check the return value of each SMU command and propagate it, and reject a zero physical address before calling devm_ioremap().

---
 drivers/platform/x86/amd/pmc/mp1_stb.c |   31 ++++++++++++++++++++-----------
 1 file changed, 20 insertions(+), 11 deletions(-)
drivers/platform/x86/amd/pmc/mp1_stb.c +20 -11
@@ -306,30 +306,39 @@ int amd_stb_s2d_init(struct amd_pmc_dev
 	/* Spill to DRAM feature uses separate SMU message port */
 	dev->msg_port = MSG_PORT_S2D;
 
-	amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->stb_arg.s2d_msg_id, true);
+	ret = amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->stb_arg.s2d_msg_id, true);
+	if (ret)
+		goto out;
 	if (size != S2D_TELEMETRY_BYTES_MAX) {
 		ret = -EIO;
 		goto out;
 	}
 
-	/* Get DRAM size */
-	ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->stb_arg.s2d_msg_id, true);
-	if (ret || !dev->dram_size)
+	/* Get DRAM size; fall back to the default if the query fails */
+	if (amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->stb_arg.s2d_msg_id, true) ||
+	    !dev->dram_size)
 		dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX;
 
 	/* Get STB DRAM address */
-	amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->stb_arg.s2d_msg_id, true);
-	amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->stb_arg.s2d_msg_id, true);
+	ret = amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low,
+			       dev->stb_arg.s2d_msg_id, true);
+	if (ret)
+		goto out;
+	ret = amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi,
+			       dev->stb_arg.s2d_msg_id, true);
+	if (ret)
+		goto out;
 
 	stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low);
-
-	dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size);
-	if (!dev->stb_virt_addr) {
-		ret = -ENOMEM;
+	if (!stb_phys_addr) {
+		dev_err(dev->dev, "S2D phys addr query returned invalid address\n");
+		ret = -ENXIO;
 		goto out;
 	}
 
-	ret = 0;
+	dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size);
+	if (!dev->stb_virt_addr)
+		ret = -ENOMEM;
 
 out:
 	/* Restore the default message port for subsequent SMU operations */
author maintainer
#444 lore raw

PATCH 433/552 platform/x86/amd/pmc: Fix LPS0 and debugfs leaks when STB init fails

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Mario Limonciello <[email protected]>

commit 76f650a76d6a36a4bee79d94db90a0e935a95477 upstream.

amd_pmc_probe() registers the LPS0 s2idle handler with acpi_register_lps0_dev() and creates the driver's debugfs directory before calling amd_stb_s2d_init(), which is the last step in probe that can fail.

When amd_stb_s2d_init() fails (for example the S2D telemetry region cannot be ioremapped on a long-running system, or the SMU rejects the S2D setup) the error path only calls pci_dev_put() and returns. This leaves amd_pmc_s2idle_dev_ops on the global lps0_s2idle_devops_head list and leaks the debugfs directory, while the devm-managed resources backing the handler are torn down.

Reloading the module then walks the corrupted list in acpi_register_lps0_dev() and hits:

list_add corruption. next->prev should be prev, but was NULL. kernel BUG at lib/list_debug.c:29!

   acpi_register_lps0_dev+0x44/0x80
   amd_pmc_probe+0x224/0x380 [amd_pmc]
   platform_probe+0x67/0x90

Even without a reload, the stale registration means the next s2idle transition calls into torn-down driver state.

Unwind the debugfs directory and the LPS0 registration on the amd_stb_s2d_init() error path. acpi_unregister_lps0_dev() is safe to call unconditionally here: it is guarded on the same conditions as acpi_register_lps0_dev(), which is exactly what amd_pmc_remove() already relies on.

---
 drivers/platform/x86/amd/pmc/pmc.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/platform/x86/amd/pmc/pmc.c +5 -1
@@ -870,13 +870,17 @@ static int amd_pmc_probe(struct platform
 	amd_pmc_dbgfs_register(dev);
 	err = amd_stb_s2d_init(dev);
 	if (err)
-		goto err_pci_dev_put;
+		goto err_dbgfs_unregister;
 
 	if (IS_ENABLED(CONFIG_AMD_MP2_STB))
 		amd_mp2_stb_init(dev);
 	pm_report_max_hw_sleep(U64_MAX);
 	return 0;
 
+err_dbgfs_unregister:
+	amd_pmc_dbgfs_unregister(dev);
+	if (IS_ENABLED(CONFIG_SUSPEND))
+		acpi_unregister_lps0_dev(&amd_pmc_s2idle_dev_ops);
 err_pci_dev_put:
 	pci_dev_put(rdev);
 	return err;
author maintainer
#445 lore raw

PATCH 434/552 platform/x86: hp-bioscfg: accept reduced ACPI packages from older HP BIOS

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Muhammad Bilal <[email protected]>

commit 40e10e6cc8f70c041431a1e30186807e28ec46e0 upstream.

hp_init_bios_package_attribute() hard-fails when a WMI ACPI package contains fewer elements than the type-specific expected count (e.g. 11 elements instead of 13 for INTEGER or ENUMERATION attributes). This causes the entire hp_bioscfg driver to skip attribute enumeration on older HP hardware whose BIOS returns shortened packages when optional fields like prerequisites or possible values are absent.

Observed on HP EliteBook 840 G2 (BIOS M71 Ver. 01.31):

hp_bioscfg: ACPI-package does not have enough elements: 11 < 13

The element layout has two tiers:
- Elements 0-9 (SECURITY_LEVEL+1 = 10): common to all attribute types
- Elements 10-N: type-specific (bounds, values, encodings, ...)

The per-type populate functions (hp_populate_*_elements_from_package) already handle sparse packages correctly via their own elem < count loop guards and inner-loop bounds checks. The only unsafe case is when we lack even the common elements needed to register the attribute.

Fix by introducing COMMON_ELEM_CNT to mark the hard minimum (10), and splitting the check into two tiers:
- Fewer than COMMON_ELEM_CNT elements: hard fail, can't proceed.
- Fewer than expected type-specific elements: warn, but let the populate function parse what is available.

---
 drivers/platform/x86/hp/hp-bioscfg/bioscfg.c |   11 ++++++++---
 drivers/platform/x86/hp/hp-bioscfg/bioscfg.h |    3 +++
 2 files changed, 11 insertions(+), 3 deletions(-)
drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +8 -3
@@ -661,12 +661,17 @@ static int hp_init_bios_package_attribut
 	int ret = 0;
 
 	/* Take action appropriate to each ACPI TYPE */
-	if (obj->package.count < min_elements) {
-		pr_err("ACPI-package does not have enough elements: %d < %d\n",
-		       obj->package.count, min_elements);
+	if (obj->package.count < COMMON_ELEM_CNT) {
+		pr_err("ACPI-package is missing common elements: %d < %d\n",
+		       obj->package.count, COMMON_ELEM_CNT);
 		goto pack_attr_exit;
 	}
 
+	if (obj->package.count < min_elements) {
+		pr_warn("ACPI-package has fewer elements than expected: %d < %d, parsing available elements\n",
+			obj->package.count, min_elements);
+	}
+
 	elements = obj->package.elements;
 
 	/* sanity checking */
drivers/platform/x86/hp/hp-bioscfg/bioscfg.h +3 -0
@@ -279,6 +279,9 @@ enum hp_wmi_data_elements {
 	PSWD_ENCODINGS = 13,
 	PSWD_IS_SET = 14,
 	PSWD_ELEM_CNT = 15,
+
+	/* Minimum elements shared by all attribute types (NAME..SECURITY_LEVEL) */
+	COMMON_ELEM_CNT = SECURITY_LEVEL + 1,
 };
 
 #define GET_INSTANCE_ID(type)						\
author maintainer
#446 lore raw

PATCH 435/552 platform/x86: hp-bioscfg: advance elem past consumed array elements

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Muhammad Bilal <[email protected]>

commit 05c808362e808e196f75696b8a64f7aa8b2245ce upstream.

The outer parsing loop in each attribute-type parser advances "elem" (the index into the ACPI package element array) by exactly one per iteration, but cases that consume multi-element arrays (PREREQUISITES, ENUM_POSSIBLE_VALUES, PSWD_ENCODINGS) read "size" consecutive elements without adjusting "elem" for the extra entries consumed beyond the first. The next outer iteration then re-reads a leftover element from the array just consumed instead of the next real property, and the type check fails on that stale element, aborting the parse with -EIO.

This produces exactly the failure visible in dmesg on the test hardware, on every boot:

Error expected type 2 for elem 13, but got type 1 instead
hp_bioscfg: Returned error 0x3, "Invalid command value/Feature not supported"

Fix by advancing "elem" by (size - 1) after each array-consuming loop, so the outer loop's own "elem++" lands on the correct next element. "eloc" is intentionally left alone: it indexes the logical property schema, not the physical element array, and each array case is still exactly one logical property regardless of how many physical elements it spans.

The defect is identical across all five attribute-type parsers (enum, integer, string, ordered-list, password), which were copy-pasted from the same template when the driver was introduced.

Fixes 6b2770bfd6f9 ("platform/x86: hp-bioscfg: enum-attributes")
Fixes 6f2c06d5a467 ("platform/x86: hp-bioscfg: int-attributes")
Fixes e6c7b3e15559 ("platform/x86: hp-bioscfg: string-attributes")
Fixes 4b2672ec71a3 ("platform/x86: hp-bioscfg: order-list-attributes")
Fixes 8646a3b5ee3a ("platform/x86: hp-bioscfg: passwdobj-attributes")
---
 drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c       |    4 ++++
 drivers/platform/x86/hp/hp-bioscfg/int-attributes.c        |    2 ++
 drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c |    2 ++
 drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c  |    4 ++++
 drivers/platform/x86/hp/hp-bioscfg/string-attributes.c     |    2 ++
 5 files changed, 14 insertions(+)
drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c +4 -0
@@ -227,6 +227,8 @@ static int hp_populate_enumeration_eleme
 				kfree(str_value);
 				str_value = NULL;
 			}
+			if (size)
+				elem += size - 1;
 			break;
 
 		case SECURITY_LEVEL:
@@ -280,6 +282,8 @@ static int hp_populate_enumeration_eleme
 				kfree(str_value);
 				str_value = NULL;
 			}
+			if (size)
+				elem += (size < MAX_VALUES_SIZE ? size : MAX_VALUES_SIZE) - 1;
 			break;
 		default:
 			pr_warn("Invalid element: %d found in Enumeration attribute or data may be malformed\n", elem);
drivers/platform/x86/hp/hp-bioscfg/int-attributes.c +2 -0
@@ -243,6 +243,8 @@ static int hp_populate_integer_elements_
 				kfree(str_value);
 				str_value = NULL;
 			}
+			if (size)
+				elem += size - 1;
 			break;
 
 		case SECURITY_LEVEL:
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +2 -0
@@ -233,6 +233,8 @@ static int hp_populate_ordered_list_elem
 				kfree(str_value);
 				str_value = NULL;
 			}
+			if (size)
+				elem += size - 1;
 			break;
 
 		case SECURITY_LEVEL:
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +4 -0
@@ -321,6 +321,8 @@ static int hp_populate_password_elements
 				str_value = NULL;
 
 			}
+			if (size)
+				elem += size - 1;
 			break;
 		case SECURITY_LEVEL:
 			password_data->common.security_level = int_value;
@@ -362,6 +364,8 @@ static int hp_populate_password_elements
 				str_value = NULL;
 
 			}
+			if (size)
+				elem += size - 1;
 			break;
 		case PSWD_IS_SET:
 			password_data->is_enabled = int_value;
drivers/platform/x86/hp/hp-bioscfg/string-attributes.c +2 -0
@@ -233,6 +233,8 @@ static int hp_populate_string_elements_f
 				kfree(str_value);
 				str_value = NULL;
 			}
+			if (size)
+				elem += size - 1;
 			break;
 
 		case SECURITY_LEVEL:
author maintainer
#447 lore raw

PATCH 436/552 platform/x86: hp-bioscfg: bound ordered-list parsing by the package count

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Muhammad Bilal <[email protected]>

commit 1d143d78299d0eb4536698bf98c1815ec69f22a9 upstream.

hp_populate_ordered_list_elements_from_package() differs from the other per-type parsers: its main loop is bounded only by the fixed per-type count and never checks elem against the number of elements actually present in the package,

for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT; elem++, eloc++)

whereas the string, integer, enumeration and password parsers bound their main loop with "elem < count" as well.

This is safe today because hp_init_bios_package_attribute() rejects any package with fewer than ORD_ELEM_CNT elements before the parser runs. An upcoming change, however, relaxes that check to accept shorter packages.

Bound the loop by the validated element count as well, so it stops at whichever comes first, the per-type count or the real package size,

  for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT && elem < order_obj_count;
       elem++, eloc++)

order_obj_count is the validated element count, now correctly forwarded from the caller. No functional change for packages that enumerate correctly today.

---
 drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +1 -1
@@ -146,7 +146,7 @@ static int hp_populate_ordered_list_elem
 	if (!order_obj)
 		return -EINVAL;
 
-	for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT; elem++, eloc++) {
+	for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT && elem < order_obj_count; elem++, eloc++) {
 
 		switch (order_obj[elem].type) {
 		case ACPI_TYPE_STRING:
author maintainer
#448 lore raw

PATCH 437/552 platform/x86: hp-bioscfg: fix heap OOB read in sk_store() and kek_store()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Muhammad Bilal <[email protected]>

commit a7508c7959ff8d037327d377ed21a9c0eabe4674 upstream.

sk_store() and kek_store() strip a trailing newline from the sysfs write before allocating the key buffer:

	length = count;
	if (buf[length - 1] == '\n')
		length--;
	bioscfg_drv.spm_data.signing_key = kmemdup(buf, length, GFP_KERNEL);

but then pass the original "count" (not "length") as the copy size to hp_wmi_perform_query(), which memcpy()s that many bytes out of the "length"-sized allocation, reading one byte past it whenever the write ends in a newline, the normal case for a shell "echo" into sysfs.

KASAN confirms this directly:

  BUG: KASAN: slab-out-of-bounds in hp_wmi_perform_query+0x1e9/0x460 [hp_bioscfg]
  Read of size 28 at addr ffff88813c8e2b80 by task python3/16022
  ...
  sk_store+0xa7/0x240 [hp_bioscfg]
  kernfs_fop_write_iter+0x3e1/0x5d0
  ...
  The buggy address is located 0 bytes inside of
  allocated 27-byte region [ffff88813c8e2b80, ffff88813c8e2b9b)

Reproduced identically for kek_store, and at multiple write sizes (28, 57, 201 bytes), each time reading exactly one byte past a kmemdup() allocation one byte smaller than the write.

Fix by passing "length" instead of "count" to hp_wmi_perform_query() in both functions.

---
 drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c +2 -2
@@ -238,7 +238,7 @@ static ssize_t sk_store(struct kobject *
 	ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_SK,
 				   HPWMI_SECUREPLATFORM,
 				   (void *)bioscfg_drv.spm_data.signing_key,
-				   count, 0);
+				   length, 0);
 
 	if (!ret) {
 		bioscfg_drv.spm_data.mechanism = SIGNING_KEY;
@@ -274,7 +274,7 @@ static ssize_t kek_store(struct kobject
 	ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_KEK,
 				   HPWMI_SECUREPLATFORM,
 				   (void *)bioscfg_drv.spm_data.endorsement_key,
-				   count, 0);
+				   length, 0);
 
 	if (!ret) {
 		bioscfg_drv.spm_data.mechanism = ENDORSEMENT_KEY;
author maintainer
#449 lore raw

PATCH 438/552 platform/x86: hp-bioscfg: fix heap OOB read on empty password write

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Muhammad Bilal <[email protected]>

commit 2b2ec354f905c14e3270e8ec3ab50f7d8ad73bab upstream.

validate_password_input() computes length = strlen(buf) and then checks buf[length - 1] to strip a trailing newline, without checking that length is nonzero first. Writing an empty string (a bare '\n') to current_password or new_password gives length == 0, and buf[length - 1] reads buf[-1], one byte before the heap allocation holding the copied input.

KASAN confirms this directly:

  BUG: KASAN: slab-out-of-bounds in store_password_instance.constprop.0+0x223/0x2a0 [hp_bioscfg]
  Read of size 1 at addr ffff88811bd8da9f by task sh/13740
  ...
  store_password_instance.constprop.0+0x223/0x2a0 [hp_bioscfg]
  current_password_store+0x14/0x20 [hp_bioscfg]
  ...
  The buggy address is located 23 bytes to the right of
  allocated 8-byte region [ffff88811bd8da80, ffff88811bd8da88)

Reproduced identically via new_password_store. Execution continues past the bad read (the garbage byte only affects whether "length" is decremented by one), so the write completes and returns success; this is a pure information read past the buffer, not a crash, but it is still an out-of-bounds access KASAN correctly flags.

Fix by only checking buf[length - 1] when length is nonzero.

---
 drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +1 -1
@@ -66,7 +66,7 @@ static int validate_password_input(int i
 	struct password_data *password_data = &bioscfg_drv.password_data[instance_id];
 
 	length = strlen(buf);
-	if (buf[length - 1] == '\n')
+	if (length > 0 && buf[length - 1] == '\n')
 		length--;
 
 	if (length > MAX_PASSWD_SIZE)
author maintainer
#450 lore raw

PATCH 439/552 platform/x86: hp-bioscfg: fix new_password_store() overwriting current_password

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Muhammad Bilal <[email protected]>

commit 2ea12a467a9cb12170417b30784fe26a243a75fe upstream.

current_password_store() and new_password_store() both call store_password_instance() with is_current = true:

	static ssize_t new_password_store(...)
	{
		return store_password_instance(kobj, buf, count, true);
	}

so a write to new_password is routed to current_password instead, and the new_password field is never written by either sysfs entry point.

Fix by passing false from new_password_store(), matching what the is_current parameter is meant to select.

---
 drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +1 -1
@@ -123,7 +123,7 @@ static ssize_t new_password_store(struct
 				  struct kobj_attribute *attr,
 				  const char *buf, size_t count)
 {
-	return store_password_instance(kobj, buf, count, true);
+	return store_password_instance(kobj, buf, count, false);
 }
 
 static struct kobj_attribute password_new_password = __ATTR_WO(new_password);
author maintainer
#451 lore raw

PATCH 440/552 platform/x86: hp-bioscfg: fix off-by-one write in hp_get_string_from_buffer()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Muhammad Bilal <[email protected]>

commit dc03f05e419f3460342fb7564884f244622634b6 upstream.

hp_get_string_from_buffer() clamps the converted string length against the destination buffer size with "size > dst_size", so when the converted length is exactly equal to dst_size, conv_dst_size is left at dst_size and the unconditional NUL terminator write

	dst[conv_dst_size] = 0;

lands one byte past the destination buffer. This is the same shape of bug as the previously fixed off-by-one in hp_convert_hexstr_to_str():
the buffer is sized correctly for the content, but the terminator write is never checked against that size.

Fix by changing the comparison to ">=" so conv_dst_size is always left with room for the terminator.

All fixed-size destinations that reach this function (path[512], current_value[512], current_password/current_value[64], and the per-entry buffers in encodings[][512] and prerequisites[][512]) are affected.

---
 drivers/platform/x86/hp/hp-bioscfg/bioscfg.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +1 -1
@@ -85,7 +85,7 @@ int hp_get_string_from_buffer(u8 **buffe
 	 * bytes.
 	 */
 	conv_dst_size = size;
-	if (size > dst_size)
+	if (size >= dst_size)
 		conv_dst_size = dst_size - 1;
 
 	/*
author maintainer
#452 lore raw

PATCH 441/552 platform/x86: hp-bioscfg: fix ORD_LIST_ELEMENTS never being parsed

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Muhammad Bilal <[email protected]>

commit cb6b1b0fb236a9581cae213c2a9182e68cc3ffe5 upstream.

The ACPI_TYPE_STRING case explicitly skips the string conversion for elem == ORD_LIST_ELEMENTS:

	if (elem != PREREQUISITES && elem != ORD_LIST_ELEMENTS) {
		ret = hp_convert_hexstr_to_str(..., &str_value, &value_len);
		if (ret)
			continue;
	}

so by the time the ORD_LIST_ELEMENTS case in the eloc switch runs, str_value is NULL (it was freed and reset to NULL at the end of the previous iteration). That case then does:

	ret = hp_convert_hexstr_to_str(str_value, value_len, &tmpstr, &tmp_len);

hp_convert_hexstr_to_str() rejects a NULL input with -EINVAL, which sends this function to exit_list, and exit_list unconditionally returns 0. The net effect is that any ordered-list attribute with elements present silently ends up with an empty elements list, with no error surfaced anywhere.

Fix by converting the current element directly, order_obj[elem], the same way the PREREQUISITES case already handles its own array elements, instead of reusing the unrelated str_value/value_len left over from earlier processing.

---
 drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +3 -1
@@ -264,7 +264,9 @@ static int hp_populate_ordered_list_elem
 			 * Ordered list data is stored in hex and comma separated format
 			 * Convert the data and split it to show each element
 			 */
-			ret = hp_convert_hexstr_to_str(str_value, value_len, &tmpstr, &tmp_len);
+			ret = hp_convert_hexstr_to_str(order_obj[elem].string.pointer,
+						       order_obj[elem].string.length,
+						       &tmpstr, &tmp_len);
 			if (ret)
 				goto exit_list;
 
author maintainer
#453 lore raw

PATCH 442/552 platform/x86: hp-bioscfg: pass validated element count to package parsers

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Muhammad Bilal <[email protected]>

commit e0ddfd77c0c320b7d12b6c9169303b140b798775 upstream.

The per-type package parsers are handed the wrong element count.

hp_init_bios_package_attribute() validates obj->package.count and then calls one of the five hp_populate_*_package_data() wrappers (string, integer, enumeration, ordered list, password). Each wrapper forwards a count to its hp_populate_*_elements_from_package() parser, but instead of forwarding the validated obj->package.count it derives the count from elements[0]. elements[0] is the NAME field and is always an ACPI_TYPE_STRING, so reading ->package.count from it in fact reads ->string.length through the union acpi_object. The parsers thus bound themselves against the length of the name string rather than against the real number of elements in the package.

This is safe today because hp_init_bios_package_attribute() refuses any package that has fewer than the type's element count, so a parser only ever runs on a full package and never reads past it regardless of the bogus bound.

An upcoming change relaxes that check to accept shorter packages. Once a parser can receive fewer elements than its per-type count, a bound taken from the name length no longer reflects the array size, and the "elem < count" loop conditions and "elem + n >= count" sub-loop guards read past the end of elements[] - an out-of-bounds heap read.

Forward the validated obj->package.count to every *_package_data() wrapper so the parsers bound themselves against the real package size. This does not change behaviour for the packages that enumerate correctly today and is a prerequisite for accepting shorter packages safely.

---
 drivers/platform/x86/hp/hp-bioscfg/bioscfg.c               |    5 +++++
 drivers/platform/x86/hp/hp-bioscfg/bioscfg.h               |    5 +++++
 drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c       |    4 +++-
 drivers/platform/x86/hp/hp-bioscfg/int-attributes.c        |    4 +++-
 drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c |    6 ++++--
 drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c  |    6 ++++--
 drivers/platform/x86/hp/hp-bioscfg/string-attributes.c     |    4 +++-
 7 files changed, 27 insertions(+), 7 deletions(-)
drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +5 -0
@@ -736,26 +736,31 @@ static int hp_init_bios_package_attribut
 	switch (attr_type) {
 	case HPWMI_STRING_TYPE:
 		ret = hp_populate_string_package_data(elements,
+						      obj->package.count,
 						      instance_id,
 						      attr_name_kobj);
 		break;
 	case HPWMI_INTEGER_TYPE:
 		ret = hp_populate_integer_package_data(elements,
+						       obj->package.count,
 						       instance_id,
 						       attr_name_kobj);
 		break;
 	case HPWMI_ENUMERATION_TYPE:
 		ret = hp_populate_enumeration_package_data(elements,
+							   obj->package.count,
 							   instance_id,
 							   attr_name_kobj);
 		break;
 	case HPWMI_ORDERED_LIST_TYPE:
 		ret = hp_populate_ordered_list_package_data(elements,
+							    obj->package.count,
 							    instance_id,
 							    attr_name_kobj);
 		break;
 	case HPWMI_PASSWORD_TYPE:
 		ret = hp_populate_password_package_data(elements,
+							obj->package.count,
 							instance_id,
 							attr_name_kobj);
 		break;
drivers/platform/x86/hp/hp-bioscfg/bioscfg.h +5 -0
@@ -404,6 +404,7 @@ int hp_populate_string_buffer_data(u8 *b
 int hp_alloc_string_data(void);
 void hp_exit_string_attributes(void);
 int hp_populate_string_package_data(union acpi_object *str_obj,
+				    int str_obj_count,
 				    int instance_id,
 				    struct kobject *attr_name_kobj);
 
@@ -414,6 +415,7 @@ int hp_populate_integer_buffer_data(u8 *
 int hp_alloc_integer_data(void);
 void hp_exit_integer_attributes(void);
 int hp_populate_integer_package_data(union acpi_object *integer_obj,
+				     int integer_obj_count,
 				     int instance_id,
 				     struct kobject *attr_name_kobj);
 
@@ -424,6 +426,7 @@ int hp_populate_enumeration_buffer_data(
 int hp_alloc_enumeration_data(void);
 void hp_exit_enumeration_attributes(void);
 int hp_populate_enumeration_package_data(union acpi_object *enum_obj,
+					 int enum_obj_count,
 					 int instance_id,
 					 struct kobject *attr_name_kobj);
 
@@ -435,6 +438,7 @@ int hp_populate_ordered_list_buffer_data
 int hp_alloc_ordered_list_data(void);
 void hp_exit_ordered_list_attributes(void);
 int hp_populate_ordered_list_package_data(union acpi_object *order_obj,
+					  int order_obj_count,
 					  int instance_id,
 					  struct kobject *attr_name_kobj);
 
@@ -443,6 +447,7 @@ int hp_populate_password_buffer_data(u8
 				     int instance_id,
 				     struct kobject *attr_name_kobj);
 int hp_populate_password_package_data(union acpi_object *password_obj,
+				      int password_obj_count,
 				      int instance_id,
 				      struct kobject *attr_name_kobj);
 int hp_alloc_password_data(void);
drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c +3 -1
@@ -304,10 +304,12 @@ exit_enumeration_package:
  * Populate all properties of an instance under enumeration attribute
  *
  * @enum_obj: ACPI object with enumeration data
+ * @enum_obj_count: Number of elements in @enum_obj
  * @instance_id: The instance to enumerate
  * @attr_name_kobj: The parent kernel object
  */
 int hp_populate_enumeration_package_data(union acpi_object *enum_obj,
+					 int enum_obj_count,
 					 int instance_id,
 					 struct kobject *attr_name_kobj)
 {
@@ -316,7 +318,7 @@ int hp_populate_enumeration_package_data
 	enum_data->attr_name_kobj = attr_name_kobj;
 
 	hp_populate_enumeration_elements_from_package(enum_obj,
-						      enum_obj->package.count,
+						      enum_obj_count,
 						      instance_id);
 	hp_update_attribute_permissions(enum_data->common.is_readonly,
 					&enumeration_current_val);
drivers/platform/x86/hp/hp-bioscfg/int-attributes.c +3 -1
@@ -277,10 +277,12 @@ exit_integer_package:
  * Populate all properties of an instance under integer attribute
  *
  * @integer_obj: ACPI object with integer data
+ * @integer_obj_count: Number of elements in @integer_obj
  * @instance_id: The instance to enumerate
  * @attr_name_kobj: The parent kernel object
  */
 int hp_populate_integer_package_data(union acpi_object *integer_obj,
+				     int integer_obj_count,
 				     int instance_id,
 				     struct kobject *attr_name_kobj)
 {
@@ -288,7 +290,7 @@ int hp_populate_integer_package_data(uni
 
 	integer_data->attr_name_kobj = attr_name_kobj;
 	hp_populate_integer_elements_from_package(integer_obj,
-						  integer_obj->package.count,
+						  integer_obj_count,
 						  instance_id);
 	hp_update_attribute_permissions(integer_data->common.is_readonly,
 					&integer_current_val);
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +4 -2
@@ -303,10 +303,12 @@ exit_list:
  * Populate all properties of an instance under ordered_list attribute
  *
  * @order_obj: ACPI object with ordered_list data
+ * @order_obj_count: Number of elements in @order_obj
  * @instance_id: The instance to enumerate
  * @attr_name_kobj: The parent kernel object
  */
-int hp_populate_ordered_list_package_data(union acpi_object *order_obj, int instance_id,
+int hp_populate_ordered_list_package_data(union acpi_object *order_obj, int order_obj_count,
+					  int instance_id,
 					  struct kobject *attr_name_kobj)
 {
 	struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id];
@@ -314,7 +316,7 @@ int hp_populate_ordered_list_package_dat
 	ordered_list_data->attr_name_kobj = attr_name_kobj;
 
 	hp_populate_ordered_list_elements_from_package(order_obj,
-						       order_obj->package.count,
+						       order_obj_count,
 						       instance_id);
 	hp_update_attribute_permissions(ordered_list_data->common.is_readonly,
 					&ordered_list_current_val);
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +4 -2
@@ -389,10 +389,12 @@ exit_package:
  *	Populate all properties for an instance under password attribute
  *
  * @password_obj: ACPI object with password data
+ * @password_obj_count: Number of elements in @password_obj
  * @instance_id: The instance to enumerate
  * @attr_name_kobj: The parent kernel object
  */
-int hp_populate_password_package_data(union acpi_object *password_obj, int instance_id,
+int hp_populate_password_package_data(union acpi_object *password_obj, int password_obj_count,
+				      int instance_id,
 				      struct kobject *attr_name_kobj)
 {
 	struct password_data *password_data = &bioscfg_drv.password_data[instance_id];
@@ -400,7 +402,7 @@ int hp_populate_password_package_data(un
 	password_data->attr_name_kobj = attr_name_kobj;
 
 	hp_populate_password_elements_from_package(password_obj,
-						   password_obj->package.count,
+						   password_obj_count,
 						   instance_id);
 
 	hp_friendly_user_name_update(password_data->common.path,
drivers/platform/x86/hp/hp-bioscfg/string-attributes.c +3 -1
@@ -265,10 +265,12 @@ exit_string_package:
  * Populate all properties of an instance under string attribute
  *
  * @string_obj: ACPI object with string data
+ * @string_obj_count: Number of elements in @string_obj
  * @instance_id: The instance to enumerate
  * @attr_name_kobj: The parent kernel object
  */
 int hp_populate_string_package_data(union acpi_object *string_obj,
+				    int string_obj_count,
 				    int instance_id,
 				    struct kobject *attr_name_kobj)
 {
@@ -277,7 +279,7 @@ int hp_populate_string_package_data(unio
 	string_data->attr_name_kobj = attr_name_kobj;
 
 	hp_populate_string_elements_from_package(string_obj,
-						 string_obj->package.count,
+						 string_obj_count,
 						 instance_id);
 
 	hp_update_attribute_permissions(string_data->common.is_readonly,
author maintainer
#454 lore raw

PATCH 443/552 platform/x86: hp-bioscfg: warn on element type mismatch instead of failing

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Muhammad Bilal <[email protected]>

commit b0e2af3ec94e0431adb59d9f249ebbd3b7285158 upstream.

hp_populate_enumeration_elements_from_package() returns -EIO and aborts enumeration of the entire attribute when any single element has an unexpected ACPI type. This is observed on HP EliteBook 840 G2 when the BIOS returns malformed ACPI data following a failed WMI query:

ACPI BIOS Error (bug): AE_AML_BUFFER_LIMIT, Index (0x000000032)

    is beyond end of object (length 0x32)

ACPI Error: Aborting method \_SB.WMID.WQBE due to previous error Error expected type 2 for elem 13, but got type 1 instead
hp_bioscfg: Returned error 0x3,
"Invalid command value/Feature not supported"

Aborting immediately discards the attribute entirely.

Warn about the unexpected element type, free the temporary string, skip the offending element, and continue parsing the remaining package instead of failing the whole attribute.

---
 drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c |    7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c +4 -3
@@ -163,10 +163,11 @@ static int hp_populate_enumeration_eleme
 
 		/* Check that both expected and read object type match */
 		if (expected_enum_types[eloc] != enum_obj[elem].type) {
-			pr_err("Error expected type %d for elem %d, but got type %d instead\n",
-			       expected_enum_types[eloc], elem, enum_obj[elem].type);
+			pr_warn("Unexpected element type at elem %d: expected %d, got %d, skipping\n",
+				elem, expected_enum_types[eloc], enum_obj[elem].type);
 			kfree(str_value);
-			return -EIO;
+			str_value = NULL;
+			continue;
 		}
 
 		/* Assign appropriate element value to corresponding field */
author maintainer
#455 lore raw

PATCH 444/552 io_uring/query: cap user size passed to copy_struct_to_user

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Laxman Acharya Padhya <[email protected]>

commit ba77efee1b95b4ad7559b1cdbe7cd7fa36dca95b upstream.

io_handle_query_entry() clamps hdr.size for the inbound copy_from_user() but keeps the original user value as usize. copy_struct_to_user() uses that usize and, when it is larger than the kernel result, clear_user()s the trailing bytes.

As hdr.size is a __u32, a query can request nearly 4 GiB of zeroing, including on the error path where res_size stays 0. The interface is reachable without a ring via IORING_REGISTER_QUERY.

Reject sizes larger than PAGE_SIZE, as recommended for copy_struct_* interfaces.

---
 io_uring/query.c |    3 +++
 1 file changed, 3 insertions(+)
io_uring/query.c +3 -0
@@ -36,6 +36,9 @@ static int io_handle_query_entry(struct
 
 	if (copy_from_user(&hdr, uhdr, sizeof(hdr)))
 		return -EFAULT;
+	/* copy_struct_to_user() zeros up to usize bytes */
+	if (hdr.size > PAGE_SIZE)
+		return -E2BIG;
 	usize = hdr.size;
 	hdr.size = min(hdr.size, IO_MAX_QUERY_SIZE);
 	udata = u64_to_user_ptr(hdr.query_data);
author maintainer
#456 lore raw

PATCH 445/552 interconnect: Fix use after free in icc_get() and of_icc_get_by_index()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Kuan-Wei Chiu <[email protected]>

commit 25c7e242aca084fdc1098248194032317dca625d upstream.

In of_icc_get_by_index() and icc_get(), if the dynamic allocation for path->name fails via kasprintf(), the error handling path directly calls kfree(path) to free the path object and returns an error.

However, prior to this point, path_find() calls path_init(), which already links the path's requests into the req_list of the respective interconnect nodes via hlist_add_head(). Directly invoking kfree(path) leaves dangling pointers in the hlist. A subsequent call to icc_get() or icc_set_bw() will traverse or modify these corrupted lists, triggering a slab use afterfree.

KASAN report showing the vulnerability when reproducing via debugfs:

  BUG: KASAN: slab-use-after-free in path_find+0x6f8/0xcfc
  Write of size 8 at addr fff000000d43f748 by task sh/1
  ...
  Call trace:
   kasan_report+0xac/0xfc
   path_find+0x6f8/0xcfc
   icc_get+0x148/0x380
   icc_get_set+0xf8/0x2d0
  ...
  Freed by task 1:
   kfree+0x1a0/0x4a4
   icc_get+0x2cc/0x380
   icc_get_set+0xf8/0x2d0

Fix this by replacing kfree(path) with the proper teardown function, icc_put(path), which safely removes the requests from the req_list using hlist_del() and drops the provider usage references before freeing the memory.

Additionally, in icc_get(), ensure that the icc_lock mutex is released prior to calling icc_put(path) to avoid a deadlock, as icc_put() internally acquires the same lock.

Fixes 3791163602f7 ("interconnect: Handle memory allocation errors")
---
 drivers/interconnect/core.c |    7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/interconnect/core.c +4 -3
@@ -528,7 +528,7 @@ struct icc_path *of_icc_get_by_index(str
 	path->name = kasprintf(GFP_KERNEL, "%s-%s",
 			       src_data->node->name, dst_data->node->name);
 	if (!path->name) {
-		kfree(path);
+		icc_put(path);
 		path = ERR_PTR(-ENOMEM);
 	}
 
@@ -626,8 +626,9 @@ struct icc_path *icc_get(struct device *
 
 	path->name = kasprintf(GFP_KERNEL, "%s-%s", src_node->name, dst_node->name);
 	if (!path->name) {
-		kfree(path);
-		path = ERR_PTR(-ENOMEM);
+		mutex_unlock(&icc_lock);
+		icc_put(path);
+		return ERR_PTR(-ENOMEM);
 	}
 out:
 	mutex_unlock(&icc_lock);
author maintainer
#457 lore raw

PATCH 446/552 ipmi: ipmb: validate write message length

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yousef Alhouseen <[email protected]>

commit 53637506884dbd5c91a89b1a3547d99d80f8ed2c upstream.

ipmb_write() read message fields before validating the length byte.

A zero or short write can read uninitialized stack bytes.

A length smaller than the SMBus header underflows the block write length.

Require a non-empty buffer and the minimum IPMB request length.

Also require the length byte plus payload before parsing the message.

Fixes 51bd6f291583 ("Add support for IPMB driver")

Message-ID: <[email protected]>

---
 drivers/char/ipmi/ipmb_dev_int.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/char/ipmi/ipmb_dev_int.c +3 -2
@@ -141,13 +141,14 @@ static ssize_t ipmb_write(struct file *f
 	u8 msg[MAX_MSG_LEN];
 	ssize_t ret;
 
-	if (count > sizeof(msg))
+	if (!count || count > sizeof(msg))
 		return -EINVAL;
 
 	if (copy_from_user(&msg, buf, count))
 		return -EFAULT;
 
-	if (count < msg[0])
+	if (msg[IPMB_MSG_LEN_IDX] < IPMB_REQUEST_LEN_MIN ||
+	    count < (size_t)msg[IPMB_MSG_LEN_IDX] + 1)
 		return -EINVAL;
 
 	rq_sa = GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]);
author maintainer
#458 lore raw

PATCH 447/552 ipmi: Remove all sysfs files on registration failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yuho Choi <[email protected]>

commit b6c46ab0bdee90c238e96ea4a74972118c97900d upstream.

ipmi_add_smi() creates the nr_users and nr_msgs files before trying to create the maintenance_mode file. If that last creation fails, the error path removes only nr_users before dropping the final reference to the interface.

Remove nr_msgs as well so no sysfs attribute embedded in the freed interface remains registered.

Fixes 627118470fcc ("ipmi: Add a maintenance mode sysfs file")

Message-ID: <[email protected]>

---
 drivers/char/ipmi/ipmi_msghandler.c |    1 +
 1 file changed, 1 insertion(+)
drivers/char/ipmi/ipmi_msghandler.c +1 -0
@@ -3741,6 +3741,7 @@ int ipmi_add_smi(struct module         *
 	sysfs_attr_init(&intf->maintenance_mode_devattr.attr);
 	rv = device_create_file(intf->si_dev, &intf->maintenance_mode_devattr);
 	if (rv) {
+		device_remove_file(intf->si_dev, &intf->nr_msgs_devattr);
 		device_remove_file(intf->si_dev, &intf->nr_users_devattr);
 		goto out_err_bmc_reg;
 	}
author maintainer
#459 lore raw

PATCH 448/552 ipmi: si: Fix NULL pointer dereference after failed registration

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Seiji Nishikawa <[email protected]>

commit 6d920a75df9a83ab096b3cde7a643b656e4fdfeb upstream.

try_smi_init() allocates new_smi->si_sm and later calls ipmi_register_smi_mod(), which maps to ipmi_add_smi().

During ipmi_add_smi(), the upper IPMI message handler obtains the initial BMC device information through __bmc_get_device_id(). This can fail if the BMC does not return a successful response to the Get Device ID command.

When the BMC returns a nonzero completion code, the device-id helper retries the command and eventually returns -EIO if the device ID still cannot be fetched.

On this failure path, ipmi_add_smi() logs "Unable to get the device id" and goes to out_err_started, where it invokes the lower driver's shutdown callback. try_smi_init() then logs the returned registration failure:

ipmi_si IPI0001:00: IPMI message handler: Unable to get the device id: -5 ipmi_si IPI0001:00: Unable to register device: error -5

For ipmi_si, the shutdown callback is shutdown_smi(), which cleans up the SI state machine data, frees smi_info->si_sm, and sets smi_info->si_sm and smi_info->intf to NULL.

However, intf->in_shutdown is not set on this failed-registration rollback path. Therefore, the asynchronous redo_bmc_reg work item can still retry BMC device-id probing after the lower driver has already cleared its SI state machine data. In the observed case, that retry path reached start_next_msg(), which passed the NULL smi_info->si_sm pointer to the selected KCS state machine handler:

BUG: unable to handle kernel NULL pointer dereference at 0000000000000000
Workqueue: events redo_bmc_reg [ipmi_msghandler]
RIP: start_kcs_transaction+0x2c/0x190 [ipmi_si]
Call Trace:
 start_next_msg+0x50/0x80 [ipmi_si]
 check_start_timer_thread.part.9+0x3b/0x50 [ipmi_si]
 sender+0x69/0x80 [ipmi_si]
 i_ipmi_request+0x2ac/0x9d0 [ipmi_msghandler]
 __get_device_id.isra.29+0xaa/0x180 [ipmi_msghandler]
 __bmc_get_device_id+0xef/0x950 [ipmi_msghandler]
 redo_bmc_reg+0x52/0x60 [ipmi_msghandler]
 process_one_work+0x1a7/0x360

Set intf->in_shutdown on the out_err_started path before invoking the lower driver's shutdown callback. This prevents later redo_bmc_reg retries from using an interface whose lower driver state has been cleaned up, and applies the same shutdown state to other IPMI interfaces as well.

Fixes 2512e40e48d2 ("ipmi: Rework SMI registration failure")

Message-ID: <[email protected]>

---
 drivers/char/ipmi/ipmi_msghandler.c |    1 +
 1 file changed, 1 insertion(+)
drivers/char/ipmi/ipmi_msghandler.c +1 -0
@@ -3759,6 +3759,7 @@ int ipmi_add_smi(struct module         *
  out_err_bmc_reg:
 	ipmi_bmc_unregister(intf);
  out_err_started:
+	intf->in_shutdown = true;
 	if (intf->handlers->shutdown)
 		intf->handlers->shutdown(intf->send_info);
  out_err:
author maintainer
#460 lore raw

PATCH 449/552 ipmi:msghandler: Cancel work cleanly on an error

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Corey Minyard <[email protected]>

commit ae84a2536577057e97f23f75a202e26d0e86cf01 upstream.

If an error occurs during startup of an IPMI interface, it may have scheduled work to run. The work needs to be canceled before the interface can be freed.

Fixes 62cd145453d5 ("ipmi:msghandler: Handle error returns from the SMI sender")
---
 drivers/char/ipmi/ipmi_msghandler.c |    1 +
 1 file changed, 1 insertion(+)
drivers/char/ipmi/ipmi_msghandler.c +1 -0
@@ -3766,6 +3766,7 @@ int ipmi_add_smi(struct module         *
 	list_del(&intf->link);
 	mutex_unlock(&ipmi_interfaces_mutex);
 	mutex_unlock(&smi_watchers_mutex);
+	cancel_work_sync(&intf->smi_work);
 	kref_put(&intf->refcount, intf_free);
 
 	return rv;
author maintainer
#461 lore raw

PATCH 450/552 net/iucv: filter frames in afiucv_hs_rcv() by ingress device

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Alexandra Winter <[email protected]>

commit 80230a18c164a4b5bbc048fe2768b219ac17bc5a upstream.

afiucv_hs_rcv() selects a socket from iucv_sk_list by matching four 8-byte name fields in the transport header alone. No check is made against the net_device the frame arrived on.

This can cause a frame arriving on any netdev to be delivered to an AF_IUCV socket. Three problems follow.

First, a frame arriving over HiperSockets can be delivered to a socket bound to the classic z/VM IUCV transport, which has iucv->hs_dev == NULL. iucv_sock_bind() takes the classic path whenever the requested userid matches iucv_userid, even on a guest that also has a HiperSockets device carrying the same identifier. The child socket created by afiucv_hs_callback_syn() for such a match inherits hs_dev = NULL and transport = AF_IUCV_TRANS_HIPER, so the first send() on it returns -ENODEV. The socket delivered to accept() is unusable.

Second, a frame arriving on one netdev can be delivered to a socket bound to a different IQD device. Which can lead to
- Accept-queue exhaustion (DoS)
- Attacker-controlled peer identity in the child socket
- Data injection into existing sockets
- Fabric noise on the IQD fabric, where bogus replies are sent
- killing established connections

Third, all AF_IUCV sockets live in init_net, as iucv_sock_alloc() calls sk_alloc(&init_net, ...). But even frames arriving on netdev devices in a namespace can be delivered to an IUCV socket. So a process in an unprivileged user and network namespace holding only the CAP_NET_RAW capability valid within that namespace can send a raw ETH_P_AF_IUCV frame on its own lo device and have it matched against init_net sockets.

Fix all three by skipping any socket whose hs_dev does not match the ingress device. A classic z/VM IUCV socket has hs_dev == NULL; the ingress dev is never NULL, so classic sockets are skipped automatically. An unbound HIPER socket also has hs_dev == NULL and is skipped. A bound HIPER socket is only reachable from the exact IQD device it was bound to. Because hs_dev is always a device in init_net (iucv_sock_bind() scans for_each_netdev_rcu(&init_net, ...) exclusively), a frame whose ingress device belongs to another namespace never matches any socket.

Note that AF_IUCV over HiperSockets provides no per-connection
authentication: no sequence numbers, no TLS, no nonce. The four name fields identifying a connection are exchanged in plaintext on the shared HiperSockets segment (VCHID). Any host on the same HiperSockets segment could spoof any frame type against an existing connection. That is a protocol-level property unchanged by this patch. The fix reduces the attack surface to peers present on the same HiperSockets segment.

---
 net/iucv/af_iucv.c |    2 ++
 1 file changed, 2 insertions(+)
net/iucv/af_iucv.c +2 -0
@@ -2079,6 +2079,8 @@ static int afiucv_hs_rcv(struct sk_buff
 	sk = NULL;
 	read_lock(&iucv_sk_list.lock);
 	sk_for_each(sk, &iucv_sk_list.head) {
+		if (iucv_sk(sk)->hs_dev != dev)
+			continue;
 		if (trans_hdr->flags == AF_IUCV_FLAG_SYN) {
 			if ((!memcmp(&iucv_sk(sk)->src_name,
 				     trans_hdr->destAppName, 8)) &&
author maintainer
#462 lore raw

PATCH 451/552 xdp: fix zero-copy frame layout

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Weiming Shi <[email protected]>

commit 71283aaa6c65b3cec84caf1dc78560985737641f upstream.

xdp_convert_zc_to_xdp_frame() clones an XSK packet into an order-0 page and advertises PAGE_SIZE as its frame size. It allows the copied frame to occupy the page tail needed by skb_shared_info and records zero headroom even when metadata separates the frame header from packet data. An AF_XDP zero-copy packet redirected through cpumap can therefore make the skb overlap skb_shared_info or place it beyond the allocated page.

Limit the copied layout to SKB_WITH_OVERHEAD(PAGE_SIZE) and include the metadata length in frame headroom. Redirect callers already handle a NULL conversion result.

BUG: KASAN: slab-out-of-bounds in skb_gro_receive
Write of size 4 at addr ffff88800cf37004 by task cpumap/1/map:1/146
Call Trace:
 skb_gro_receive (net/core/gro.c:174)
 udp_gro_receive (net/ipv4/udp_offload.c:812)
 inet_gro_receive (net/ipv4/af_inet.c:1539)
 dev_gro_receive (net/core/gro.c:515)
 gro_receive_skb (net/core/gro.c:633)
 cpu_map_kthread_run (kernel/bpf/cpumap.c:395)
 kthread (kernel/kthread.c:436)
 ret_from_fork (arch/x86/kernel/process.c:164)
 ret_from_fork_asm (arch/x86/entry/entry_64.S:255)
Kernel panic - not syncing: KASAN: panic_on_warn set ...
Fixes b0d1beeff2a9 ("xdp: implement convert_to_xdp_frame for MEM_TYPE_ZERO_COPY")
---
 net/core/xdp.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/core/xdp.c +2 -2
@@ -585,7 +585,7 @@ struct xdp_frame *xdp_convert_zc_to_xdp_
 		   xdp->data - xdp->data_meta;
 	totsize = xdp->data_end - xdp->data + metasize;
 
-	if (sizeof(*xdpf) + totsize > PAGE_SIZE)
+	if (sizeof(*xdpf) + totsize > SKB_WITH_OVERHEAD(PAGE_SIZE))
 		return NULL;
 
 	page = dev_alloc_page();
@@ -602,7 +602,7 @@ struct xdp_frame *xdp_convert_zc_to_xdp_
 
 	xdpf->data = addr + metasize;
 	xdpf->len = totsize - metasize;
-	xdpf->headroom = 0;
+	xdpf->headroom = metasize;
 	xdpf->metasize = metasize;
 	xdpf->frame_sz = PAGE_SIZE;
 	xdpf->mem_type = MEM_TYPE_PAGE_ORDER0;
author maintainer
#463 lore raw

PATCH 452/552 slip: fix use-after-free in sl_sync()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Aleksandr Khromov <[email protected]>

commit 2c4e7c42d77e78ad595dbb9e4b5886b58b45d89d upstream.

slip_devs[] stores bare net_device pointers and takes no reference on them. sl_sync() and sl_alloc() walk that table from slip_open() under rtnl_lock(), while an entry is dropped by sl_free_netdev(), which sl_setup() installs as dev->priv_destructor.

priv_destructor is called from netdev_run_todo(), which deliberately runs with the RTNL semaphore released so that it can sleep while waiting for the device refcount to drop:

	/* Snapshot list, allow later requests */
	list_replace_init(&net_todo_list, &list);

	__rtnl_unlock();
	...
		if (dev->priv_destructor)
			dev->priv_destructor(dev);	/* slip_devs[i] = NULL */
		if (dev->needs_free_netdev)
			free_netdev(dev);
		...
		/* Free network device */
		kobject_put(&dev->dev.kobj);

So rtnl_lock() does not serialise slip_open() against the teardown at all. sl_sync() can load slip_devs[i] while the entry is still published and dereference it after netdev_run_todo() has run the destructor and released the device:

  CPU0 (slip_open)                 CPU1 (slip_close)
                                   unregister_netdev()
                                     rtnl_unlock()
                                       netdev_run_todo()
                                         __rtnl_unlock()
  rtnl_lock()
  sl_sync()
    dev = slip_devs[i]
                                         priv_destructor(dev)
                                           slip_devs[i] = NULL
                                         kobject_put(&dev->dev.kobj)
                                           /* dev is freed */
    sl = netdev_priv(dev)
    if (sl->tty || sl->leased)     /* use-after-free */
  BUG: KASAN: use-after-free in sl_sync drivers/net/slip/slip.c:730 [inline]
  BUG: KASAN: use-after-free in slip_open+0xef4/0x1210 drivers/net/slip/slip.c:806
  Read of size 1 at addr ffff8880712dac71 by task syz-executor.2/6506

  CPU: 2 PID: 6506 Comm: syz-executor.2 Not tainted 6.1.134-syzkaller-00260-g0c8fc3469765 #0
  Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014
  Call Trace:
   sl_sync drivers/net/slip/slip.c:730 [inline]
   slip_open+0xef4/0x1210 drivers/net/slip/slip.c:806
   tty_ldisc_open+0xa2/0x120 drivers/tty/tty_ldisc.c:433
   tty_set_ldisc+0x324/0x720 drivers/tty/tty_ldisc.c:564
   tiocsetd drivers/tty/tty_io.c:2428 [inline]
   tty_ioctl+0x5f0/0x1530 drivers/tty/tty_io.c:2712

  Allocated by task 6502:
   alloc_netdev_mqs+0x98/0xfe0 net/core/dev.c:10719
   sl_alloc drivers/net/slip/slip.c:756 [inline]
   slip_open+0x36d/0x1210 drivers/net/slip/slip.c:817
   tty_ldisc_open+0xa2/0x120 drivers/tty/tty_ldisc.c:433
   tty_set_ldisc+0x324/0x720 drivers/tty/tty_ldisc.c:564

  Freed by task 6497:
   device_release+0xa2/0x240 drivers/base/core.c:2507
   kobject_put+0x179/0x280 lib/kobject.c:729
   netdev_run_todo+0x6c8/0xef0 net/core/dev.c:10509
   slip_close+0x166/0x1c0 drivers/net/slip/slip.c:906
   tty_ldisc_close+0x113/0x1a0 drivers/tty/tty_ldisc.c:456
   tty_ldisc_kill+0x94/0x160 drivers/tty/tty_ldisc.c:614
   tty_ldisc_release+0xe3/0x2b0 drivers/tty/tty_ldisc.c:782
   tty_release+0xbcc/0xe70 drivers/tty/tty_io.c:1860

Commit e58c19124189 ("slip: Fix use-after-free Read in slip_open") fixed a different source of stale entries - a device left in slip_devs[] after slip_open() freed it on the registration error path - and does not address this race, which is why the report survives it.

Drop the entry from ndo_uninit instead. unregister_netdevice() calls ndo_uninit under RTNL, before the device is queued to netdev_run_todo(), so an entry that sl_sync() can still see while holding RTNL belongs to a device that cannot be freed until RTNL is dropped. sl_free_netdev() stays only for the slip_open() error path, where register_netdevice() may have failed before ndo_init and ndo_uninit is then not called either. Both running for the same device is harmless: they run under the same RTNL section, so the slot cannot have been reused in between.

This also removes the second symptom of the missing exclusion: a destructor running after sl_alloc() had already handed the slot out to another channel used to clear a live entry, so sl_sync() stopped at that NULL, sl_alloc() returned the same index again, and register_netdevice() failed with -EEXIST because slN was still there.

Reproduced on x86_64 with several threads looping over open("/dev/ptmx") + ioctl(TIOCSETD, N_SLIP) + close().

Found by Linux Verification Center (linuxtesting.org) with Syzkaller.

---
 drivers/net/slip/slip.c |   11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/net/slip/slip.c +8 -3
@@ -628,9 +628,15 @@ static void sl_uninit(struct net_device
 	struct slip *sl = netdev_priv(dev);
 
 	sl_free_bufs(sl);
+	/* Drop the slip_devs[] entry here rather than from the destructor:
+	 * ndo_uninit runs under RTNL, so it cannot race sl_sync().
+	 */
+	slip_devs[dev->base_addr] = NULL;
 }
 
-/* Hook the destructor so we can free slip devices at the right point in time */
+/* Only for the slip_open() error path: register_netdevice() can fail before
+ * ndo_init, and then ndo_uninit is not called either.
+ */
 static void sl_free_netdev(struct net_device *dev)
 {
 	int i = dev->base_addr;
@@ -657,7 +663,6 @@ static void sl_setup(struct net_device *
 {
 	dev->netdev_ops		= &sl_netdev_ops;
 	dev->needs_free_netdev	= true;
-	dev->priv_destructor	= sl_free_netdev;
 
 	dev->hard_header_len	= 0;
 	dev->addr_len		= 0;
@@ -908,7 +913,7 @@ static void slip_close(struct tty_struct
 #endif
 	/* Flush network side */
 	unregister_netdev(sl->dev);
-	/* This will complete via sl_free_netdev */
+	/* sl_uninit() has dropped the slip_devs[] entry by now */
 }
 
 static void slip_hangup(struct tty_struct *tty)
author maintainer
#464 lore raw

PATCH 453/552 net: usb: qmi_wwan: add Telit Cinterion FE990D50 composition

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Fabio Porcedda <[email protected]>

commit 1056e79fffd0841f43c6a1b25664b196b3caf1c6 upstream.

Add the followin Telit Cinterion FE990D50 composition:

0x0991: rmnet + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) +
tty (diag) + ADPL + adb

T:  Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 10 Spd=480  MxCh= 0
D:  Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
P:  Vendor=1bc7 ProdID=0991 Rev=06.06
S:  Manufacturer=Telit Cinterion
S:  Product=FE990
S:  SerialNumber=2aa802d2
C:  #Ifs= 9 Cfg#= 1 Atr=e0 MxPwr=500mA
I:  If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=qmi_wwan
E:  Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=82(I) Atr=03(Int.) MxPS=   8 Ivl=32ms
I:  If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option
E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=84(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
I:  If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
E:  Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=86(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
I:  If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
E:  Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=88(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
I:  If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
E:  Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=8a(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
I:  If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option
E:  Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:  If#= 6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none)
E:  Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:  If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none)
E:  Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:  If#= 8 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none)
E:  Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
---
 drivers/net/usb/qmi_wwan.c |    1 +
 1 file changed, 1 insertion(+)
drivers/net/usb/qmi_wwan.c +1 -0
@@ -1360,6 +1360,7 @@ static const struct usb_device_id produc
 	{QMI_FIXED_INTF(0x1bbb, 0x0203, 2)},	/* Alcatel L800MA */
 	{QMI_FIXED_INTF(0x2357, 0x0201, 4)},	/* TP-LINK HSUPA Modem MA180 */
 	{QMI_FIXED_INTF(0x2357, 0x9000, 4)},	/* TP-LINK MA260 */
+	{QMI_QUIRK_SET_DTR(0x1bc7, 0x0991, 0)}, /* Telit FE990D50 */
 	{QMI_QUIRK_SET_DTR(0x1bc7, 0x1031, 3)}, /* Telit LE910C1-EUX */
 	{QMI_QUIRK_SET_DTR(0x1bc7, 0x1034, 2)}, /* Telit LE910C4-WWX */
 	{QMI_QUIRK_SET_DTR(0x1bc7, 0x1037, 4)}, /* Telit LE910C4-WWX */
author maintainer
#465 lore raw

PATCH 454/552 net: tun: bound receive headroom

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Asim Viladi Oglu Manizada <[email protected]>

commit 447c9303942c439a117d9b76ce6d6e2116b38ee7 upstream.

tun_get_user() uses tun->align both as skb headroom and when choosing how much packet data to keep linear. OVS can propagate an oversized headroom request from another port to TUN or TAP.

When align is larger than the usable space in a one-page skb head, SKB_MAX_HEAD(align) underflows and the result becomes negative when stored in good_linear. That value later wraps when assigned to the size_t linear variable, and tun_alloc_skb() can place skb->data outside the allocated head.

Bound the headroom stored by TUN to the one-page skb-head budget and the largest non-sentinel 16-bit skb header offset. Leave one linear byte for raw TUN and a complete Ethernet header for TAP, including NET_IP_ALIGN.

Also pull the raw-TUN protocol byte and the TAP Ethernet header before accessing them, so these checks remain safe for nonlinear skbs supplied by other allocation paths.

---
 drivers/net/tun.c |   21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)
drivers/net/tun.c +16 -5
@@ -1105,11 +1105,16 @@ static netdev_features_t tun_net_fix_fea
 static void tun_set_headroom(struct net_device *dev, int new_hr)
 {
 	struct tun_struct *tun = netdev_priv(dev);
+	size_t max_headroom;
 
-	if (new_hr < NET_SKB_PAD)
-		new_hr = NET_SKB_PAD;
+	max_headroom = min_t(size_t, SKB_MAX_HEAD(0), U16_MAX - 1);
 
-	tun->align = new_hr;
+	if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP)
+		max_headroom -= ETH_HLEN + NET_IP_ALIGN;
+	else
+		max_headroom -= 1;
+
+	tun->align = clamp_t(int, new_hr, NET_SKB_PAD, max_headroom);
 }
 
 static void
@@ -1820,7 +1825,13 @@ static ssize_t tun_get_user(struct tun_s
 	switch (tun->flags & TUN_TYPE_MASK) {
 	case IFF_TUN:
 		if (tun->flags & IFF_NO_PI) {
-			u8 ip_version = skb->len ? (skb->data[0] >> 4) : 0;
+			u8 ip_version;
+
+			if (!pskb_may_pull(skb, 1)) {
+				err = -EINVAL;
+				goto drop;
+			}
+			ip_version = skb->data[0] >> 4;
 
 			switch (ip_version) {
 			case 4:
@@ -1840,7 +1851,7 @@ static ssize_t tun_get_user(struct tun_s
 		skb->dev = tun->dev;
 		break;
 	case IFF_TAP:
-		if (frags && !pskb_may_pull(skb, ETH_HLEN)) {
+		if (!pskb_may_pull(skb, ETH_HLEN)) {
 			err = -ENOMEM;
 			drop_reason = SKB_DROP_REASON_HDR_TRUNC;
 			goto drop;
author maintainer
#466 lore raw

PATCH 455/552 net: dsa: realtek: use gpiod_set_value_cansleep for reset GPIO

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ahmad Fatoum <[email protected]>

commit fb58b6a696b30bcbfbe0cfc0a91b19c816a955fc upstream.

rtl83xx_reset_assert() and rtl83xx_reset_deassert() are only called from the probe path, which may sleep and is not timing-critical. When the reset GPIO is provided by a sleeping controller such as an I2C I/O expander, gpiod_set_value() warns:

  WARNING: drivers/gpio/gpiolib.c:4030 at gpiod_set_value+0x44/0x80, CPU#1: kworker/u16:4/61
  Hardware name: B&O MAP CA33 Rev f (UNKNOWN) (DT)
  Workqueue: events_unbound deferred_probe_work_func
  pc : gpiod_set_value+0x44/0x80
  lr : rtl83xx_probe+0x1d8/0x3a0
  Call trace:
   gpiod_set_value+0x44/0x80 (P)
   rtl83xx_probe+0x1d8/0x3a0
   realtek_mdio_probe+0x24/0xa0
   mdio_probe+0x38/0x78
   really_probe+0xc4/0x3e0
   __driver_probe_device+0x15c/0x1b8
   driver_probe_device+0xb4/0x120
   __device_attach_driver+0xb8/0x1a0
   bus_for_each_drv+0x88/0xf0
   __device_attach+0xa0/0x1d8
   device_initial_probe+0x54/0x68
   bus_probe_device+0x38/0xa0
   deferred_probe_work_func+0xb8/0x120
   process_one_work+0x184/0x4e8
   worker_thread+0x188/0x308
   kthread+0x130/0x150
   ret_from_fork+0x10/0x20

Switch both helpers to gpiod_set_value_cansleep() so such a reset GPIO can be used without triggering the warning.

The reset GPIO has been driven with the non-sleeping gpiod_set_value() since the driver was added in v4.19. The call has since been refactored across several files - from realtek-smi.c / realtek-mdio.c into the common rtl83xx.c module and then into the rtl83xx_reset_assert() and rtl83xx_reset_deassert() helpers (both in v6.9). This patch therefore applies as-is only to kernels that carry those helpers (v6.9+); older stable kernels need the same gpiod_set_value_cansleep() conversion at the corresponding open-coded call sites.

---
 drivers/net/dsa/realtek/rtl83xx.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/dsa/realtek/rtl83xx.c +2 -2
@@ -309,7 +309,7 @@ void rtl83xx_reset_assert(struct realtek
 			 "Failed to assert the switch reset control: %pe\n",
 			 ERR_PTR(ret));
 
-	gpiod_set_value(priv->reset, true);
+	gpiod_set_value_cansleep(priv->reset, true);
 }
 
 void rtl83xx_reset_deassert(struct realtek_priv *priv)
@@ -322,7 +322,7 @@ void rtl83xx_reset_deassert(struct realt
 			 "Failed to deassert the switch reset control: %pe\n",
 			 ERR_PTR(ret));
 
-	gpiod_set_value(priv->reset, false);
+	gpiod_set_value_cansleep(priv->reset, false);
 }
 
 MODULE_AUTHOR("Luiz Angelo Daros de Luca <[email protected]>");
author maintainer
#467 lore raw

PATCH 456/552 net: ibm: emac: mal: fix NAPI locking

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Runyu Xiao <[email protected]>

commit 43b0213529c6ae2fd4cbf8dbb9baff87a34c27d7 upstream.

Since commit 413f0271f396 ("net: protect NAPI enablement with netdev_lock()"), napi_enable() and napi_disable() take netdev_lock().

mal_register_commac() and mal_unregister_commac() call these helpers while holding mal->lock with interrupts disabled. In the unregister path, napi_disable() may also wait for polling to finish, while the poll completion path takes mal->lock.

Take netdev_lock() before mal->lock, use the locked NAPI helpers, and drop mal->lock before napi_disable_locked().

---
 drivers/net/ethernet/ibm/emac/mal.c |   13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)
drivers/net/ethernet/ibm/emac/mal.c +10 -3
@@ -35,6 +35,7 @@ int mal_register_commac(struct mal_insta
 {
 	unsigned long flags;
 
+	netdev_lock(mal->napi.dev);
 	spin_lock_irqsave(&mal->lock, flags);
 
 	MAL_DBG(mal, "reg(%08x, %08x)" NL,
@@ -44,18 +45,20 @@ int mal_register_commac(struct mal_insta
 	if ((mal->tx_chan_mask & commac->tx_chan_mask) ||
 	    (mal->rx_chan_mask & commac->rx_chan_mask)) {
 		spin_unlock_irqrestore(&mal->lock, flags);
+		netdev_unlock(mal->napi.dev);
 		printk(KERN_WARNING "mal%d: COMMAC channels conflict!\n",
 		       mal->index);
 		return -EBUSY;
 	}
 
 	if (list_empty(&mal->list))
-		napi_enable(&mal->napi);
+		napi_enable_locked(&mal->napi);
 	mal->tx_chan_mask |= commac->tx_chan_mask;
 	mal->rx_chan_mask |= commac->rx_chan_mask;
 	list_add(&commac->list, &mal->list);
 
 	spin_unlock_irqrestore(&mal->lock, flags);
+	netdev_unlock(mal->napi.dev);
 
 	return 0;
 }
@@ -64,7 +67,9 @@ void mal_unregister_commac(struct mal_in
 		struct mal_commac *commac)
 {
 	unsigned long flags;
+	bool disable_napi;
 
+	netdev_lock(mal->napi.dev);
 	spin_lock_irqsave(&mal->lock, flags);
 
 	MAL_DBG(mal, "unreg(%08x, %08x)" NL,
@@ -73,10 +78,12 @@ void mal_unregister_commac(struct mal_in
 	mal->tx_chan_mask &= ~commac->tx_chan_mask;
 	mal->rx_chan_mask &= ~commac->rx_chan_mask;
 	list_del_init(&commac->list);
-	if (list_empty(&mal->list))
-		napi_disable(&mal->napi);
+	disable_napi = list_empty(&mal->list);
 
 	spin_unlock_irqrestore(&mal->lock, flags);
+	if (disable_napi)
+		napi_disable_locked(&mal->napi);
+	netdev_unlock(mal->napi.dev);
 }
 
 int mal_set_rcbs(struct mal_instance *mal, int channel, unsigned long size)
author maintainer
#468 lore raw

PATCH 457/552 net: ipa: fix stalled modem TX queue after runtime resume

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jorijn van der Graaf <[email protected]>

commit 3cbfd627ee720f3d2460d2cbe2fe9e4130240db6 upstream.

ipa_start_xmit() unconditionally stops the TX queue before calling pm_runtime_get(), relying on the wake scheduled by runtime resume (ipa_modem_wake_queue_work()) to restart it once power is ACTIVE. But that work is queued from within the runtime resume callback, before the device's power state reaches RPM_ACTIVE, so it can run while the device is still RPM_RESUMING. The wake is then consumed too early: the transmit it restarts stops the queue again, pm_runtime_get() returns -EINPROGRESS without arranging any future wake (deferred_resume exists only for RPM_SUSPENDING), and after the resume completes nothing is left to wake the queue. Transmit stalls
permanently: packets pile up in the qdisc behind the stopped queue, the device runtime-suspends, and since the netdev registers no ndo_tx_timeout the watchdog never fires. Observed on SM7635 (Fairphone 6) as the cellular data path going permanently deaf within hours, RX included, since nothing resumes the suspended endpoints.

Close the window by making the wake work wait for the resume to complete (pm_runtime_get_sync()) before waking the queue. Every queue stop is then guaranteed a later wake that happens while power is ACTIVE; a transmit racing a new suspend/resume cycle re-schedules the work. If the device could not be resumed, wake the queue anyway so pending packets are dropped by the transmit path rather than stranded.

The STARTED power flag used to narrow this window: a wake running before the transmit path's stop suppressed that stop, but only once, as the flag was cleared by the first stop it absorbed. Removing the flag made a single transmit during an in-flight resume sufficient to strand the queue, which is the form observed.

With an accelerated reproducer (autosuspend delay shortened to 5 ms, ~20 packets/s of TX), an unpatched kernel stalled three times in 230 s / 4380 packets; with this patch the same test ran 3601 s / 70298 packets without a stall.

---
 drivers/net/ipa/ipa_modem.c |   18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)
drivers/net/ipa/ipa_modem.c +17 -1
@@ -269,13 +269,29 @@ void ipa_modem_suspend(struct net_device
  * the modem.  We can't enable the queue directly in ipa_modem_resume()
  * because transmits restart the instant the queue is awakened; but the
  * device power state won't be ACTIVE until *after* ipa_modem_resume()
- * returns.
+ * returns.  A transmit restarted before that would stop the queue
+ * again and get -EINPROGRESS from pm_runtime_get(), and with this
+ * work having already run, nothing would ever wake the queue again.
+ * So wait for the resume to complete before waking the queue.
  */
 static void ipa_modem_wake_queue_work(struct work_struct *work)
 {
 	struct ipa_priv *priv = container_of(work, struct ipa_priv, work);
+	struct device *dev = priv->ipa->dev;
+	int ret;
 
+	ret = pm_runtime_get_sync(dev);
+
+	/* Wake the queue even if the device could not be resumed, so
+	 * that pending packets are dropped by the transmit path rather
+	 * than stranded behind a stopped queue.
+	 */
 	netif_wake_queue(priv->tx->netdev);
+
+	if (ret < 0)
+		pm_runtime_put_noidle(dev);
+	else
+		(void)pm_runtime_put_autosuspend(dev);
 }
 
 /** ipa_modem_resume() - resume callback for runtime_pm
author maintainer
#469 lore raw

PATCH 458/552 net: l2tp: do not propagate multicast notification errors

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zihan Xi <[email protected]>

commit af20e269f7459d2ce69887fdf2fad7caf986c865 upstream.

The tunnel create, tunnel modify, session create, and session modify netlink handlers send multicast notifications through helpers that can fail while allocating or encoding a message, or while multicasting it.

For tunnel and session create/modify, a notification is sent after the live operation has completed. Returning a best-effort notification error as the command result can therefore report failure for an operation that already committed and can cause callers to retry and accumulate live objects.

Keep sending notifications for listener visibility, but do not propagate their best-effort status as the command result. This also keeps the tunnel modify command consistent with the other notification-only paths.

---
 net/l2tp/l2tp_netlink.c |   16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)
net/l2tp/l2tp_netlink.c +8 -8
@@ -248,8 +248,8 @@ static int l2tp_nl_cmd_tunnel_create(str
 		kfree(tunnel);
 		goto out;
 	}
-	ret = l2tp_tunnel_notify(&l2tp_nl_family, info, tunnel,
-				 L2TP_CMD_TUNNEL_CREATE);
+	l2tp_tunnel_notify(&l2tp_nl_family, info, tunnel,
+			   L2TP_CMD_TUNNEL_CREATE);
 	l2tp_tunnel_put(tunnel);
 
 out:
@@ -305,8 +305,8 @@ static int l2tp_nl_cmd_tunnel_modify(str
 		goto out;
 	}
 
-	ret = l2tp_tunnel_notify(&l2tp_nl_family, info,
-				 tunnel, L2TP_CMD_TUNNEL_MODIFY);
+	l2tp_tunnel_notify(&l2tp_nl_family, info,
+			   tunnel, L2TP_CMD_TUNNEL_MODIFY);
 
 	l2tp_tunnel_put(tunnel);
 
@@ -645,8 +645,8 @@ static int l2tp_nl_cmd_session_create(st
 		session = l2tp_session_get(net, tunnel->sock, tunnel->version,
 					   tunnel_id, session_id);
 		if (session) {
-			ret = l2tp_session_notify(&l2tp_nl_family, info, session,
-						  L2TP_CMD_SESSION_CREATE);
+			l2tp_session_notify(&l2tp_nl_family, info, session,
+					    L2TP_CMD_SESSION_CREATE);
 			l2tp_session_put(session);
 		}
 	}
@@ -710,8 +710,8 @@ static int l2tp_nl_cmd_session_modify(st
 	if (info->attrs[L2TP_ATTR_RECV_TIMEOUT])
 		session->reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]);
 
-	ret = l2tp_session_notify(&l2tp_nl_family, info,
-				  session, L2TP_CMD_SESSION_MODIFY);
+	l2tp_session_notify(&l2tp_nl_family, info,
+			    session, L2TP_CMD_SESSION_MODIFY);
 
 	l2tp_session_put(session);
 
author maintainer
#470 lore raw

PATCH 459/552 net: openvswitch: fix flow mask use-after-free on flow deletion

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ilya Maximets <[email protected]>

commit 4e30317ff67a2eb12b4d890d39f72fd7e7117d48 upstream.

The commit in the Fixes tag below made so flow->mask free is scheduled via RCU right after it is removed from the flow table. The pointer stays in the flow structure and it can be accessible while in the same RCU critical section. This is done to avoid requiring ovs_mutex for the ovs_flow_free().

However, while removing the flow during processing of CMD_DEL, we do not take RCU read lock before the removal, and ovs_flow_cmd_fill_info() uses the flow->mask pointer afterwards. The RCU read lock is taken, but it's already late at that point. The comment on that line acknowledges that the lock is cosmetic and doesn't serve a real purpose.

This leads to use-after-free if the RCU grace period passes between removal and the filling. It is a short race window, but it is there and can lead to a real crash in case memory allocation for the info takes a bit longer:

 BUG: KASAN: slab-use-after-free in __ovs_nla_put_key
             net/openvswitch/flow_netlink.c:1996
 BUG: KASAN: slab-use-after-free in ovs_nla_put_key+0x2463/0x2e30
             net/openvswitch/flow_netlink.c:2250
 Read of size 4 at addr ffff88801ee89970 by task ovs_flow_del_ec/9487

 Call Trace:
  <TASK>
  __ovs_nla_put_key net/openvswitch/flow_netlink.c:1996
  ovs_nla_put_key+0x2463/0x2e30 net/openvswitch/flow_netlink.c:2250
  ovs_flow_cmd_fill_info+0x420/0x9c0 net/openvswitch/datapath.c:930
  ovs_flow_cmd_del+0x53a/0x970 net/openvswitch/datapath.c:1467
  ...
  netlink_rcv_skb+0x156/0x420 net/netlink/af_netlink.c:2556
  </TASK>

 Allocated by task 9487:
  mask_alloc net/openvswitch/flow_table.c:967
  flow_mask_insert net/openvswitch/flow_table.c:1012
  ovs_flow_tbl_insert+0xea2/0x1a90 net/openvswitch/flow_table.c:1084
  ovs_flow_cmd_new+0x7e3/0xd90 net/openvswitch/datapath.c:1086
  ...
  netlink_rcv_skb+0x156/0x420 net/netlink/af_netlink.c:2556

 Freed by task 9485:
  rcu_free_sheaf+0x1e/0x100 mm/slub.c:5978
  rcu_do_batch kernel/rcu/tree.c:2645
  rcu_core+0x59c/0x10c0 kernel/rcu/tree.c:2897
  handle_softirqs+0x1e4/0x9a0 kernel/softirq.c:622
  ...
  instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062

ovs_flow_tbl_remove() must be called after the ovs_flow_cmd_fill_info() to avoid this race. This also helps with cleaning up the forced cast and the cosmetic RCU read lock. Before the commit in the Fixes tag the order did not matter as long as the flow object itself was not freed.

A wider RCU critical section could be another option, but we have a GFP_KERNEL allocation in the way.

Reported by Trend Micro's Zero Day Initiative as ZDI-CAN-32042.

---
 net/openvswitch/datapath.c |   45 +++++++++++++++++++++++----------------------
 1 file changed, 23 insertions(+), 22 deletions(-)
net/openvswitch/datapath.c +23 -22
@@ -1472,33 +1472,34 @@ static int ovs_flow_cmd_del(struct sk_bu
 		goto unlock;
 	}
 
-	ovs_flow_tbl_remove(&dp->table, flow);
-	ovs_unlock();
-
-	reply = ovs_flow_cmd_alloc_info((const struct sw_flow_actions __force *) flow->sf_acts,
+	reply = ovs_flow_cmd_alloc_info(ovsl_dereference(flow->sf_acts),
 					&flow->id, info, false, ufid_flags);
-	if (likely(reply)) {
-		if (!IS_ERR(reply)) {
-			rcu_read_lock();	/*To keep RCU checker happy. */
-			err = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex,
-						     reply, info->snd_portid,
-						     info->snd_seq, 0,
-						     OVS_FLOW_CMD_DEL,
-						     ufid_flags);
-			rcu_read_unlock();
-			if (WARN_ON_ONCE(err < 0)) {
-				kfree_skb(reply);
-				goto out_free;
-			}
+	if (IS_ERR(reply)) {
+		netlink_set_err(sock_net(skb->sk)->genl_sock, 0, 0,
+				PTR_ERR(reply));
+		reply = NULL;
+	}
 
-			ovs_notify(&dp_flow_genl_family, reply, info);
-		} else {
-			netlink_set_err(sock_net(skb->sk)->genl_sock, 0, 0,
-					PTR_ERR(reply));
+	if (likely(reply)) {
+		err = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex,
+					     reply, info->snd_portid,
+					     info->snd_seq, 0,
+					     OVS_FLOW_CMD_DEL, ufid_flags);
+		if (WARN_ON_ONCE(err < 0)) {
+			kfree_skb(reply);
+			reply = NULL;
 		}
 	}
+	/* Removal has to happen after ovs_flow_cmd_fill_info(), as it uses
+	 * the flow->mask that can be scheduled to be freed by the
+	 * ovs_flow_tbl_remove() and we're not holding the RCU read lock.
+	 */
+	ovs_flow_tbl_remove(&dp->table, flow);
+	ovs_unlock();
+
+	if (likely(reply))
+		ovs_notify(&dp_flow_genl_family, reply, info);
 
-out_free:
 	ovs_flow_free(flow, true);
 	return 0;
 unlock:
author maintainer
#471 lore raw

PATCH 460/552 net: openvswitch: fix nf_connlabels leak in ovs_ct_init

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ruoyu Wang <[email protected]>

commit f9de5db270a4c2641de87ee558c16a9bc6eb4cd8 upstream.

ovs_ct_init() acquires a connlabels reference before initializing the conntrack limit state. If ovs_ct_limit_init() fails, its error is returned directly. The pernet core does not invoke the exit callback for the operation whose initialization failed, so ovs_ct_exit() cannot drop the reference.

This leaves labels_used elevated when Open vSwitch pernet registration fails for an existing network namespace. Subsequent conntrack entries in that namespace may allocate label extensions even though Open vSwitch failed to register.

Drop the connlabels reference before returning a conntrack limit initialization error. ovs_ct_limit_init() already releases its partial state, and the original error remains unchanged.

This issue was found by a static analysis checker and confirmed by manual source review.

---
 net/openvswitch/conntrack.c |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
net/openvswitch/conntrack.c +5 -3
@@ -2003,6 +2003,7 @@ int ovs_ct_init(struct net *net)
 {
 	unsigned int n_bits = sizeof(struct ovs_key_ct_labels) * BITS_PER_BYTE;
 	struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
+	int err = 0;
 
 	if (nf_connlabels_get(net, n_bits - 1)) {
 		ovs_net->xt_label = false;
@@ -2012,10 +2013,11 @@ int ovs_ct_init(struct net *net)
 	}
 
 #if	IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT)
-	return ovs_ct_limit_init(net, ovs_net);
-#else
-	return 0;
+	err = ovs_ct_limit_init(net, ovs_net);
+	if (err && ovs_net->xt_label)
+		nf_connlabels_put(net);
 #endif
+	return err;
 }
 
 void ovs_ct_exit(struct net *net)
author maintainer
#472 lore raw

PATCH 461/552 net: phylink: correctly validate returned PCS in phylink_inband_caps

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Christian Marangi <[email protected]>

commit f2849b1fd059ec9b3281b771e6ac5aad9feee851 upstream.

In phylink_inband_caps(), the PCS returned by mac_select_pcs is only checked if NULL but mac_select_pcs can also return an error pointer.

This can cause a kernel panic as phylink_pcs_inband_caps() only checks if passed PCS is not NULL and directly dereference ops from the phylink_pcs struct.

Use the IS_ERR_OR_NULL macro to address both case where the returned PCS can be NULL or an error pointer and prevent a kernel panic.

---
 drivers/net/phy/phylink.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/phy/phylink.c +1 -1
@@ -959,7 +959,7 @@ static unsigned int phylink_inband_caps(
 		return 0;
 
 	pcs = pl->mac_ops->mac_select_pcs(pl->config, interface);
-	if (!pcs)
+	if (IS_ERR_OR_NULL(pcs))
 		return 0;
 
 	return phylink_pcs_inband_caps(pcs, interface);
author maintainer
#473 lore raw

PATCH 462/552 net: ravb: avoid dereferencing an invalid PTP clock

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xuanqiang Luo <[email protected]>

commit 1f77af0aaf277413ff32f6ff8c2c4282bd64c897 upstream.

The PTP clock is unavailable before the first open, so querying its index can dereference a NULL pointer. Registration failures can also leave an error pointer in priv->ptp.clock.

Cache the PHC index separately and report -1 while no clock is registered. Normalize registration errors to NULL and preserve the static timestamping capabilities.

---
 drivers/net/ethernet/renesas/ravb.h      |    1 +
 drivers/net/ethernet/renesas/ravb_main.c |    3 ++-
 drivers/net/ethernet/renesas/ravb_ptp.c  |   15 +++++++++++++--
 3 files changed, 16 insertions(+), 3 deletions(-)
drivers/net/ethernet/renesas/ravb.h +1 -0
@@ -1039,6 +1039,7 @@ struct ravb_ptp_perout {
 struct ravb_ptp {
 	struct ptp_clock *clock;
 	struct ptp_clock_info info;
+	int phc_index;
 	u32 default_addend;
 	u32 current_addend;
 	int extts[N_EXT_TS];
drivers/net/ethernet/renesas/ravb_main.c +2 -1
@@ -1757,7 +1757,7 @@ static int ravb_get_ts_info(struct net_d
 			(1 << HWTSTAMP_FILTER_NONE) |
 			(1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
 			(1 << HWTSTAMP_FILTER_ALL);
-		info->phc_index = ptp_clock_index(priv->ptp.clock);
+		info->phc_index = READ_ONCE(priv->ptp.phc_index);
 	}
 
 	return 0;
@@ -2963,6 +2963,7 @@ static int ravb_probe(struct platform_de
 	priv->rstc = rstc;
 	priv->ndev = ndev;
 	priv->pdev = pdev;
+	priv->ptp.phc_index = -1;
 	priv->num_tx_ring[RAVB_BE] = BE_TX_RING_SIZE;
 	priv->num_rx_ring[RAVB_BE] = BE_RX_RING_SIZE;
 	if (info->nc_queues) {
drivers/net/ethernet/renesas/ravb_ptp.c +13 -2
@@ -315,6 +315,7 @@ void ravb_ptp_interrupt(struct net_devic
 void ravb_ptp_init(struct net_device *ndev, struct platform_device *pdev)
 {
 	struct ravb_private *priv = netdev_priv(ndev);
+	struct ptp_clock *clock;
 	unsigned long flags;
 
 	priv->ptp.info = ravb_ptp_info;
@@ -327,7 +328,15 @@ void ravb_ptp_init(struct net_device *nd
 	ravb_modify(ndev, GCCR, GCCR_TCSS, GCCR_TCSS_ADJGPTP);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
-	priv->ptp.clock = ptp_clock_register(&priv->ptp.info, &pdev->dev);
+	clock = ptp_clock_register(&priv->ptp.info, &pdev->dev);
+	if (IS_ERR(clock)) {
+		netdev_err(ndev, "failed to register PTP clock: %pe\n", clock);
+		clock = NULL;
+	}
+
+	priv->ptp.clock = clock;
+	if (clock)
+		WRITE_ONCE(priv->ptp.phc_index, ptp_clock_index(clock));
 }
 
 void ravb_ptp_stop(struct net_device *ndev)
@@ -337,5 +346,7 @@ void ravb_ptp_stop(struct net_device *nd
 	ravb_write(ndev, 0, GIC);
 	ravb_write(ndev, 0, GIS);
 
-	ptp_clock_unregister(priv->ptp.clock);
+	WRITE_ONCE(priv->ptp.phc_index, -1);
+	if (priv->ptp.clock)
+		ptp_clock_unregister(priv->ptp.clock);
 }
author maintainer
#474 lore raw

PATCH 463/552 net: ravb: serialize PTP clock teardown

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xuanqiang Luo <[email protected]>

commit 1cb9663789c5b7a12fcd419fcca6d6254c398252 upstream.

ravb_ptp_interrupt() can race with ravb_ptp_stop() and pass the clock to ptp_clock_event() while ptp_clock_unregister() is freeing it. This can lead to a use-after-free.

Use READ_ONCE() and WRITE_ONCE() for lockless access to the clock pointer. Atomically detach it with xchg() before disabling PTP interrupts, then synchronize all IRQs which can invoke ravb_ptp_interrupt() before unregistering the detached clock.

A handler which read the old pointer completes before the clock is unregistered, while later handlers read NULL and skip the event.

---
 drivers/net/ethernet/renesas/ravb.h      |    2 +
 drivers/net/ethernet/renesas/ravb_main.c |    6 +++--
 drivers/net/ethernet/renesas/ravb_ptp.c  |   37 ++++++++++++++++++++++++-------
 3 files changed, 35 insertions(+), 10 deletions(-)
drivers/net/ethernet/renesas/ravb.h +2 -0
@@ -1134,6 +1134,8 @@ struct ravb_private {
 	int msg_enable;
 	int speed;
 	int emac_irq;
+	int err_irq;
+	int mgmt_irq;
 
 	unsigned no_avb_link:1;
 	unsigned avb_link_active_low:1;
drivers/net/ethernet/renesas/ravb_main.c +4 -2
@@ -2896,11 +2896,13 @@ static int ravb_setup_irqs(struct ravb_p
 		return error;
 
 	if (info->err_mgmt_irqs) {
-		error = ravb_setup_irq(priv, "err_a", "err_a", NULL, ravb_multi_interrupt);
+		error = ravb_setup_irq(priv, "err_a", "err_a", &priv->err_irq,
+				       ravb_multi_interrupt);
 		if (error)
 			return error;
 
-		error = ravb_setup_irq(priv, "mgmt_a", "mgmt_a", NULL, ravb_multi_interrupt);
+		error = ravb_setup_irq(priv, "mgmt_a", "mgmt_a", &priv->mgmt_irq,
+				       ravb_multi_interrupt);
 		if (error)
 			return error;
 	}
drivers/net/ethernet/renesas/ravb_ptp.c +29 -8
@@ -289,16 +289,17 @@ static const struct ptp_clock_info ravb_
 void ravb_ptp_interrupt(struct net_device *ndev)
 {
 	struct ravb_private *priv = netdev_priv(ndev);
+	struct ptp_clock *clock = READ_ONCE(priv->ptp.clock);
 	u32 gis = ravb_read(ndev, GIS);
 
 	gis &= ravb_read(ndev, GIC);
-	if (gis & GIS_PTCF) {
+	if ((gis & GIS_PTCF) && clock) {
 		struct ptp_clock_event event;
 
 		event.type = PTP_CLOCK_EXTTS;
 		event.index = 0;
 		event.timestamp = ravb_read(ndev, GCPT);
-		ptp_clock_event(priv->ptp.clock, &event);
+		ptp_clock_event(clock, &event);
 	}
 	if (gis & GIS_PTMF) {
 		struct ravb_ptp_perout *perout = priv->ptp.perout;
@@ -334,19 +335,39 @@ void ravb_ptp_init(struct net_device *nd
 		clock = NULL;
 	}
 
-	priv->ptp.clock = clock;
+	WRITE_ONCE(priv->ptp.clock, clock);
 	if (clock)
 		WRITE_ONCE(priv->ptp.phc_index, ptp_clock_index(clock));
 }
 
-void ravb_ptp_stop(struct net_device *ndev)
+static void ravb_ptp_disable(struct net_device *ndev)
 {
-	struct ravb_private *priv = netdev_priv(ndev);
-
 	ravb_write(ndev, 0, GIC);
 	ravb_write(ndev, 0, GIS);
+}
+
+static void ravb_ptp_sync_irqs(struct net_device *ndev)
+{
+	struct ravb_private *priv = netdev_priv(ndev);
+
+	synchronize_irq(ndev->irq);
+	if (priv->info->err_mgmt_irqs) {
+		synchronize_irq(priv->err_irq);
+		synchronize_irq(priv->mgmt_irq);
+	}
+}
+
+void ravb_ptp_stop(struct net_device *ndev)
+{
+	struct ravb_private *priv = netdev_priv(ndev);
+	struct ptp_clock *clock;
 
 	WRITE_ONCE(priv->ptp.phc_index, -1);
-	if (priv->ptp.clock)
-		ptp_clock_unregister(priv->ptp.clock);
+	clock = xchg(&priv->ptp.clock, NULL);
+
+	ravb_ptp_disable(ndev);
+	ravb_ptp_sync_irqs(ndev);
+
+	if (clock)
+		ptp_clock_unregister(clock);
 }
author maintainer
#475 lore raw

PATCH 464/552 net: thunderbolt: Release the Rx HopID that was handed out on mismatch

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Fan Ye <[email protected]>

commit 2f1463554d0561a2fead81e3888604e5c1125e29 upstream.

tb_xdomain_alloc_in_hopid() passes the wanted HopID to ida_alloc_range() as the lower bound, so a taken id is not an error there: the allocator returns the next free one above it. tbnet_connected_work() asks for the peer's transmit path, treats any other id as a failure and returns without releasing what it got, so that allocation stays live for the rest of the XDomain connection with nothing left holding a reference to it.

Release the id when it is not the one we asked for, the same way the error unwind at the end of the function releases the expected one.

Fixes 180b0689425c ("thunderbolt: Allow multiple DMA tunnels over a single XDomain connection")
---
 drivers/net/thunderbolt/main.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/net/thunderbolt/main.c +2 -0
@@ -650,6 +650,8 @@ static void tbnet_connected_work(struct
 	ret = tb_xdomain_alloc_in_hopid(net->xd, net->remote_transmit_path);
 	if (ret != net->remote_transmit_path) {
 		netdev_err(net->dev, "failed to allocate Rx HopID\n");
+		if (ret >= 0)
+			tb_xdomain_release_in_hopid(net->xd, ret);
 		return;
 	}
 
author maintainer
#476 lore raw

PATCH 465/552 net: thunderbolt: Mark the connection down when bringing it up fails

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Fan Ye <[email protected]>

commit 3c8b26ebf525ba5960510f48c6e9936a79ebe76f upstream.

Every failure path in tbnet_connected_work() undoes its own work and returns without clearing login_sent, so the connection still looks established. The next tbnet_tear_down() therefore takes its main branch and repeats a teardown that already happened: it stops rings that are already stopped, which is a dev_WARN() and fatal under panic_on_warn, and it releases net->remote_transmit_path even on the HopID mismatch path, where this connection never owned that id, silently freeing one that someone else is still using.

Clear login_sent on those paths. That is enough for tbnet_tear_down() to leave the unwound state alone, and login_received has to stay set: it records that the peer has logged in and carries the transmit path it gave us, which nothing on this side can make the peer send again. Two things change beyond keeping the teardown out of the way: the logout request in that block is no longer sent, and the peer's next login request now re-queues our login work rather than connected_work, giving the connection a fresh login instead of a retry on stale state.

---
 drivers/net/thunderbolt/main.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/net/thunderbolt/main.c +10 -0
@@ -629,6 +629,14 @@ static int tbnet_alloc_tx_buffers(struct
 	return 0;
 }
 
+static void tbnet_connect_failed(struct tbnet *net)
+{
+	/* Leave login_received set: only the peer can make it true again. */
+	mutex_lock(&net->connection_lock);
+	net->login_sent = false;
+	mutex_unlock(&net->connection_lock);
+}
+
 static void tbnet_connected_work(struct work_struct *work)
 {
 	struct tbnet *net = container_of(work, typeof(*net), connected_work);
@@ -652,6 +660,7 @@ static void tbnet_connected_work(struct
 		netdev_err(net->dev, "failed to allocate Rx HopID\n");
 		if (ret >= 0)
 			tb_xdomain_release_in_hopid(net->xd, ret);
+		tbnet_connect_failed(net);
 		return;
 	}
 
@@ -696,6 +705,7 @@ err_stop_rings:
 	tb_ring_stop(net->rx_ring.ring);
 	tb_ring_stop(net->tx_ring.ring);
 	tb_xdomain_release_in_hopid(net->xd, net->remote_transmit_path);
+	tbnet_connect_failed(net);
 }
 
 static void tbnet_login_work(struct work_struct *work)
author maintainer
#477 lore raw

PATCH 466/552 NTB: ntb_transport: Recycle TX entries before client callbacks

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Koichiro Den <[email protected]>

commit 256496397287334a19ed80ec7be92bffcae76b9d upstream.

ntb_tx_copy_callback() invokes the client callback before returning the entry to tx_free_q. The callback may wake a stopped client queue, only for the next enqueue to find no local entry and return -EBUSY. The window is narrow, but the retry is unnecessary.

Save the callback data and length, then return the entry to tx_free_q before invoking the client. A completion callback then means both the client buffer and transport entry are ready for reuse.

---
 drivers/ntb/ntb_transport.c |   23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)
drivers/ntb/ntb_transport.c +14 -9
@@ -1755,9 +1755,16 @@ static void ntb_transport_rxc_db(unsigne
 static void ntb_tx_copy_callback(void *data,
 				 const struct dmaengine_result *res)
 {
+	struct ntb_payload_header __iomem *hdr;
 	struct ntb_queue_entry *entry = data;
-	struct ntb_transport_qp *qp = entry->qp;
-	struct ntb_payload_header __iomem *hdr = entry->tx_hdr;
+	struct ntb_transport_qp *qp;
+	unsigned int len;
+	void *cb_data;
+
+	qp = entry->qp;
+	hdr = entry->tx_hdr;
+	cb_data = entry->cb_data;
+	len = entry->len;
 
 	/* we need to check DMA results if we are using DMA */
 	if (res) {
@@ -1797,15 +1804,13 @@ static void ntb_tx_copy_callback(void *d
 	 * "link down" or similar.  Since no payload is being sent in these
 	 * cases, there is nothing to add to the completion queue.
 	 */
-	if (entry->len > 0) {
-		qp->tx_bytes += entry->len;
-
-		if (qp->tx_handler)
-			qp->tx_handler(qp, qp->cb_data, entry->cb_data,
-				       entry->len);
-	}
+	if (len > 0)
+		qp->tx_bytes += len;
 
 	ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry, &qp->tx_free_q);
+
+	if (len > 0 && qp->tx_handler)
+		qp->tx_handler(qp, qp->cb_data, cb_data, len);
 }
 
 static void ntb_memcpy_tx(struct ntb_queue_entry *entry, void __iomem *offset)
author maintainer
#478 lore raw

PATCH 467/552 NTB: ntb_transport: Fail TX enqueue when the QP link is down

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Koichiro Den <[email protected]>

commit 873ce713fef5dde0939220f04f3484ec86a16fba upstream.

Commit f195a1a6fe41 ("ntb: Drop packets when qp link is down") meant to make ntb_transport_tx_enqueue() drop packets submitted while the QP link is down, but it only returns 0 without consuming the packet. Zero means success by this function's contract, so ntb_netdev reports NETDEV_TX_OK and forgets the skb: nothing queued it, nothing frees it, and it leaks, one skb for every transmit racing a link-down.

Return -ENOLINK instead, restoring the contract that a non-zero return leaves the buffer owned by the caller. With the preceding patch, ntb_netdev frees the skb on non-retryable enqueue failures and returns NETDEV_TX_OK, so a packet racing with link-down is dropped without leaking or entering a busy retry loop.

---
 drivers/ntb/ntb_transport.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/ntb/ntb_transport.c +1 -2
@@ -2315,9 +2315,8 @@ int ntb_transport_tx_enqueue(struct ntb_
 	if (!qp || !len)
 		return -EINVAL;
 
-	/* If the qp link is down already, just ignore. */
 	if (!qp->link_is_up)
-		return 0;
+		return -ENOLINK;
 
 	entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q);
 	if (!entry) {
author maintainer
#479 lore raw

PATCH 468/552 NTB: ntb_transport: Reject oversized TX buffers

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Koichiro Den <[email protected]>

commit a4f2387db6f1cc2f03abba7f3a6807ad61e26ff7 upstream.

ntb_process_tx() handles an oversized buffer by calling tx_handler() with a NULL data pointer and returning success. ntb_netdev therefore neither frees the skb in its completion callback nor takes its enqueue error path, leaking it.

Reject oversized buffers in ntb_transport_tx_enqueue() before acquiring a queue entry and return -EMSGSIZE. The caller retains ownership of the buffer, and the preceding netdev patch frees the skb when enqueue returns this permanent error.

---
 drivers/ntb/ntb_transport.c |   12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)
drivers/ntb/ntb_transport.c +3 -9
@@ -1935,15 +1935,6 @@ static int ntb_process_tx(struct ntb_tra
 		return -EAGAIN;
 	}
 
-	if (entry->len > qp->tx_max_frame - sizeof(struct ntb_payload_header)) {
-		if (qp->tx_handler)
-			qp->tx_handler(qp, qp->cb_data, NULL, -EIO);
-
-		ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry,
-			     &qp->tx_free_q);
-		return 0;
-	}
-
 	ntb_async_tx(qp, entry);
 
 	qp->tx_index++;
@@ -2318,6 +2309,9 @@ int ntb_transport_tx_enqueue(struct ntb_
 	if (!qp->link_is_up)
 		return -ENOLINK;
 
+	if (len > qp->tx_max_frame - sizeof(struct ntb_payload_header))
+		return -EMSGSIZE;
+
 	entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q);
 	if (!entry) {
 		qp->tx_err_no_buf++;
author maintainer
#480 lore raw

PATCH 469/552 net: ntb_netdev: Fix TX busy and drop handling

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Koichiro Den <[email protected]>

commit 8aaa47351db0f93a5c5297fbafdfa8bc75e8ae49 upstream.

Currently, ntb_netdev returns NETDEV_TX_BUSY for every enqueue error. It also increments the drop and error counters while leaving the skb owned by the qdisc, and may return BUSY with the subqueue still awake. Retrying a permanent error cannot succeed either.

The unconditional BUSY return and premature accounting date back to the initial driver. The error-path queue stop was later removed without changing that return value. The current flow-control code includes a resource check, but ntb_netdev does not honor its result before enqueue.

Honor the resource check before enqueue. For -EAGAIN and -EBUSY, stop the subqueue, arm the existing reaper timer, and return BUSY without touching the skb. For other errors, free the skb, increment tx_dropped, and return NETDEV_TX_OK.

Fixes 548c237c0a99 ("net: Add support for NTB virtual ethernet device")
Fixes d723485cb4ca ("ntb_netdev: remove tx timeout")
Fixes e74bfeedad08 ("NTB: Add flow control to the ntb_netdev")
---
 drivers/net/ntb_netdev.c |   27 +++++++++++++++++++--------
 1 file changed, 19 insertions(+), 8 deletions(-)
drivers/net/ntb_netdev.c +19 -8
@@ -187,8 +187,10 @@ static int __ntb_netdev_maybe_stop_tx(st
 static int ntb_netdev_maybe_stop_tx(struct net_device *ndev,
 				    struct ntb_netdev_queue *q, int size)
 {
-	if (__netif_subqueue_stopped(ndev, q->qid) ||
-	    (ntb_transport_tx_free_entry(q->qp) >= size))
+	if (__netif_subqueue_stopped(ndev, q->qid))
+		return -EBUSY;
+
+	if (ntb_transport_tx_free_entry(q->qp) >= size)
 		return 0;
 
 	return __ntb_netdev_maybe_stop_tx(ndev, q, size);
@@ -237,21 +239,30 @@ static netdev_tx_t ntb_netdev_start_xmit
 
 	q = &dev->queues[qid];
 
-	ntb_netdev_maybe_stop_tx(ndev, q, tx_stop);
+	if (unlikely(ntb_netdev_maybe_stop_tx(ndev, q, tx_stop)))
+		return NETDEV_TX_BUSY;
 
 	rc = ntb_transport_tx_enqueue(q->qp, skb, skb->data, skb->len);
-	if (rc)
-		goto err;
+	if (rc) {
+		if (rc == -EAGAIN || rc == -EBUSY) {
+			netif_stop_subqueue(ndev, q->qid);
+			mod_timer(&q->tx_timer,
+				  jiffies + usecs_to_jiffies(tx_time));
+			return NETDEV_TX_BUSY;
+		}
+
+		goto drop;
+	}
 
 	/* check for next submit */
 	ntb_netdev_maybe_stop_tx(ndev, q, tx_stop);
 
 	return NETDEV_TX_OK;
 
-err:
+drop:
+	dev_kfree_skb_any(skb);
 	ndev->stats.tx_dropped++;
-	ndev->stats.tx_errors++;
-	return NETDEV_TX_BUSY;
+	return NETDEV_TX_OK;
 }
 
 static void ntb_netdev_tx_timer(struct timer_list *t)
author maintainer
#481 lore raw

PATCH 470/552 net: ntb_netdev: Avoid double-accounting netif_rx() drops

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Koichiro Den <[email protected]>

commit 82e15be2d8b9efa6fb1750528d9b6f40e6a8eea7 upstream.

netif_rx() already accounts packets it drops in the core rx_dropped counter. ntb_netdev counts them again as both errors and drops.

Leave netif_rx() drops to the core. Count the packet and bytes unconditionally since it was received successfully by the driver.

---
 drivers/net/ntb_netdev.c |   10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)
drivers/net/ntb_netdev.c +3 -7
@@ -146,13 +146,9 @@ static void ntb_netdev_rx_handler(struct
 	skb->ip_summed = CHECKSUM_NONE;
 	skb_record_rx_queue(skb, q->qid);
 
-	if (netif_rx(skb) == NET_RX_DROP) {
-		ndev->stats.rx_errors++;
-		ndev->stats.rx_dropped++;
-	} else {
-		ndev->stats.rx_packets++;
-		ndev->stats.rx_bytes += len;
-	}
+	netif_rx(skb);
+	ndev->stats.rx_packets++;
+	ndev->stats.rx_bytes += len;
 
 	skb = new_skb;
 
author maintainer
#482 lore raw

PATCH 471/552 net: ntb_netdev: Count packets dropped on RX refill failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Koichiro Den <[email protected]>

commit 31ded341c375bb2faac1d77ab0012a732ba3e2a6 upstream.

When replacement skb allocation fails, ntb_netdev drops a packet that was received successfully and requeues the original buffer. The drop is counted, but rx_packets and rx_bytes are not.

Count every good packet before allocating its replacement.

Fixes d2121faf133a ("NTB: ntb_netdev: Preserve RX queue depth on allocation failure")
---
 drivers/net/ntb_netdev.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/net/ntb_netdev.c +3 -2
@@ -135,6 +135,9 @@ static void ntb_netdev_rx_handler(struct
 		goto enqueue_again;
 	}
 
+	ndev->stats.rx_packets++;
+	ndev->stats.rx_bytes += len;
+
 	new_skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN);
 	if (!new_skb) {
 		ndev->stats.rx_dropped++;
@@ -147,8 +150,6 @@ static void ntb_netdev_rx_handler(struct
 	skb_record_rx_queue(skb, q->qid);
 
 	netif_rx(skb);
-	ndev->stats.rx_packets++;
-	ndev->stats.rx_bytes += len;
 
 	skb = new_skb;
 
author maintainer
#483 lore raw

PATCH 472/552 net/smc: bound the peer rkey counts in SMC-Rv2 LLC messages

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yehyeong Lee <[email protected]>

commit 2d1e7c5aaa3326e95e2058457f172ca99a9a4577 upstream.

On a link whose device has max_recv_sge == 1 there is no shared v2 receive buffer, and smc_llc_save_add_link_rkeys() takes the v2 extension from 44 bytes past the start of the queue entry's inline message:

  ext = (struct smc_llc_msg_add_link_v2_ext *)(llc_msg + SMC_WR_TX_SIZE);

The entry is a 72-byte allocation and the extension starts at offset 68, so ext->num_rkeys at offset 94 is already past it. This happens on every SMC-Rv2 link addition, whatever the peer sends:

  [    2.490065] BUG: KASAN: slab-out-of-bounds in smc_llc_save_add_link_rkeys+0x333/0x350
  [    2.490431] Read of size 2 at addr ffff8880056406de by task smctest/106
  [    2.490709]
  [    2.490792] CPU: 0 UID: 0 PID: 106 Comm: smctest Not tainted 7.2.0-rc5-p1-g77a5d9d9c99f #32 PREEMPT(lazy)
  [    2.490795] 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
  [    2.490798] Call Trace:
  [    2.490803]  <TASK>
  [    2.490805]  dump_stack_lvl+0x53/0x70
  [    2.490810]  print_report+0xd0/0x630
  [    2.490828]  ? __pfx__raw_spin_lock_irqsave+0x10/0x10
  [    2.490832]  ? smc_llc_save_add_link_rkeys+0x333/0x350
  [    2.490834]  kasan_report+0xce/0x100
  [    2.490836]  ? smc_llc_save_add_link_rkeys+0x333/0x350
  [    2.490837]  smc_llc_save_add_link_rkeys+0x333/0x350
  [    2.490839]  ? smcr_buf_map_lgr+0x1bf/0x2b0
  [    2.490844]  smc_llc_cli_add_link+0xca7/0x1e80
  [    2.490848]  ? smc_llc_wait+0x355/0x810
  [    2.490850]  ? __pfx_smc_llc_wait+0x10/0x10
  [    2.490851]  ? __pfx_smc_llc_cli_add_link+0x10/0x10
  [    2.490853]  ? __pfx_autoremove_wake_function+0x10/0x10
  [    2.490863]  __smc_connect+0x3f5c/0x4980
  [    2.490873]  ? __pfx_kernel_connect+0x10/0x10
  [    2.490888]  ? __pfx___smc_connect+0x10/0x10
  [    2.490891]  ? release_sock+0x148/0x1d0
  [    2.490894]  smc_connect+0x42c/0x580
  [    2.490896]  __sys_connect+0xfc/0x130
  [    2.490898]  ? __pfx___sys_connect+0x10/0x10
  [    2.490900]  ? handle_mm_fault+0x1a1/0x430
  [    2.490908]  __x64_sys_connect+0x6d/0xb0
  [    2.490909]  ? fpregs_assert_state_consistent+0x56/0xe0
  [    2.490917]  do_syscall_64+0xf9/0x540
  [    2.490921]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
  [    2.490924] RIP: 0033:0x421bb4
  [    2.490927] Code: ff f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 80 3d ad 34 09 00 00 74 13 b8 2a 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 4c c3 0f 1f 00 55 48 89 e5 48 83 ec 10 89 55
  [    2.490929] RSP: 002b:00007ffd473b01a8 EFLAGS: 00000202 ORIG_RAX: 000000000000002a
  [    2.490935] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 0000000000421bb4
  [    2.490936] RDX: 0000000000000010 RSI: 00007ffd473b01d0 RDI: 0000000000000003
  [    2.490937] RBP: 0000000000003930 R08: 0000000000000004 R09: 0000000000000000
  [    2.490938] R10: 00007ffd473b0f98 R11: 0000000000000202 R12: 0000000000000006
  [    2.490939] R13: 00007ffd473b0f87 R14: 0000000000000003 R15: 00007ffd473b0f90
  [    2.490940]  </TASK>
  [    2.490941]
  [    2.499545] Allocated by task 44:
  [    2.499693]  kasan_save_stack+0x33/0x60
  [    2.499860]  kasan_save_track+0x14/0x30
  [    2.500026]  __kasan_kmalloc+0x8f/0xa0
  [    2.500190]  __kmalloc_cache_noprof+0x158/0x370
  [    2.500393]  smc_llc_enqueue+0x72/0x560
  [    2.500559]  smc_wr_rx_tasklet_fn+0x474/0xa80
  [    2.500747]  tasklet_action_common+0x20f/0x8a0
  [    2.500945]  handle_softirqs+0x18e/0x590
  [    2.501115]  do_softirq+0x3b/0x60
  [    2.501266]  __local_bh_enable_ip+0x61/0x70
  [    2.501446]  __alloc_skb+0x732/0x890
  [    2.501604]  rxe_init_packet+0x16b/0x4f0
  [    2.501783]  prepare_ack_packet+0xb8/0x830
  [    2.501962]  rxe_receiver+0x495/0x96e0
  [    2.502125]  do_work+0x144/0x470
  [    2.502269]  process_one_work+0x633/0x1030
  [    2.502450]  worker_thread+0x45b/0xd10
  [    2.502617]  kthread+0x2c6/0x3b0
  [    2.502762]  ret_from_fork+0x36e/0x5a0
  [    2.502925]  ret_from_fork_asm+0x1a/0x30
  [    2.503103]
  [    2.503177] The buggy address belongs to the object at ffff888005640680
  [    2.503177]  which belongs to the cache kmalloc-96 of size 96
  [    2.503692] The buggy address is located 22 bytes to the right of
  [    2.503692]  allocated 72-byte region [ffff888005640680, ffff8880056406c8)
  [    2.504227]
  [    2.504300] The buggy address belongs to the physical page:
  [    2.504535] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5640
  [    2.504865] flags: 0x100000000000000(node=0|zone=1)
  [    2.505076] page_type: f5(slab)
  [    2.505221] raw: 0100000000000000 ffff888001041280 dead000000000122 0000000000000000
  [    2.505544] raw: 0000000000000000 0000000000200020 00000000f5000000 0000000000000000
  [    2.505867] page dumped because: kasan: bad access detected
  [    2.506102]
  [    2.506176] Memory state around the buggy address:
  [    2.506380]  ffff888005640580: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc
  [    2.506683]  ffff888005640600: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc
  [    2.506987] >ffff888005640680: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc
  [    2.507291]                                                     ^
  [    2.507548]  ffff888005640700: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc
  [    2.507850]  ffff888005640780: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc

Whatever that read finds then bounds the ext->rt[] loop, so a peer that declares 255 rkeys reads much further. smc_llc_rmt_delete_rkey() has the same shape for llcv2->rkey[].

Bound both loops by the buffer they read from, and skip the extension altogether when there is no shared v2 receive buffer. The extension does arrive on the link, but smc_llc_enqueue() copies only sizeof(union smc_llc_msg) into the queue entry, so what that code read past the 44 inline bytes was heap and not peer data.

Fixes 27ef6a9981fe ("net/smc: support SMC-R V2 for rdma devices with max_recv_sge equals to 1")
---
 net/smc/smc_llc.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)
net/smc/smc_llc.c +16 -0
@@ -1000,13 +1000,21 @@ static void smc_llc_save_add_link_rkeys(
 					struct smc_link *link_new,
 					u8 *llc_msg)
 {
+	const u32 rt_off = offsetof(struct smc_llc_msg_add_link_v2_ext, rt);
 	struct smc_llc_msg_add_link_v2_ext *ext;
 	struct smc_link_group *lgr = link->lgr;
 	int max, i;
 
+	/* Without a shared v2 receive buffer the extension is not copied
+	 * into the queue entry, so not even ext->num_rkeys is there.
+	 */
+	if (!smc_link_shared_v2_rxbuf(link))
+		return;
 	ext = (struct smc_llc_msg_add_link_v2_ext *)(llc_msg +
 						     SMC_WR_TX_SIZE);
 	max = min_t(u8, ext->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2);
+	max = min_t(u32, max, (SMC_WR_BUF_V2_SIZE - SMC_WR_TX_SIZE - rt_off) /
+			      sizeof(ext->rt[0]));
 	down_write(&lgr->rmbs_lock);
 	for (i = 0; i < max; i++) {
 		smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
@@ -1810,17 +1818,25 @@ static void smc_llc_rmt_delete_rkey(stru
 	link = qentry->link;
 
 	if (lgr->smc_version == SMC_V2) {
+		const u32 rkey_off =
+			offsetof(struct smc_llc_msg_delete_rkey_v2, rkey);
 		struct smc_llc_msg_delete_rkey_v2 *llcv2;
+		u32 buf_len;
 
 		if (smc_link_shared_v2_rxbuf(link)) {
 			memcpy(lgr->wr_rx_buf_v2, llc, sizeof(*llc));
 			llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)lgr->wr_rx_buf_v2;
+			buf_len = SMC_WR_BUF_V2_SIZE;
 		} else {
 			llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)llc;
+			buf_len = sizeof(qentry->msg);
 		}
 		llcv2->num_inval_rkeys = 0;
 
 		max = min_t(u8, llcv2->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2);
+		/* bound by the buffer llcv2 points at */
+		max = min_t(u32, max, (buf_len - rkey_off) /
+				      sizeof(llcv2->rkey[0]));
 		for (i = 0; i < max; i++) {
 			if (smc_rtoken_delete(link, llcv2->rkey[i]))
 				llcv2->num_inval_rkeys++;
author maintainer
#484 lore raw

PATCH 473/552 net/smc: do not dereference an unset send buffer on the SMC-D teardown path

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryam Vargas <[email protected]>

commit b395dd319cea422239cb45b998fb38d7e373af87 upstream.

smc_close_stream_wait() calls smc_tx_prepared_sends() from inside its sk_wait_event() condition, and sk_wait_event() evaluates that condition once with the socket lock released. smcd_buf_detach() clears conn->sndbuf_desc from smc_conn_kill() under lock_sock(), so a link group terminating while a socket waits there leaves the helper dereferencing NULL, faulting out of close(). SIOCOUTQ reads the field by hand, and smc_close_cancel_work() drops the lock across two cancel_*_sync() calls.

Sample the pointer once in the helper, report nothing prepared while it is unset, and bound the ioctl the same way. The receive tasklet dereferences the field directly in smc_cdc_msg_recv_action(), not through this helper; 1/2 is what keeps it from running that late.

---
 net/smc/af_smc.c |    3 ++-
 net/smc/smc_tx.h |    6 +++++-
 2 files changed, 7 insertions(+), 2 deletions(-)
net/smc/af_smc.c +2 -1
@@ -3237,7 +3237,8 @@ int smc_ioctl(struct socket *sock, unsig
 			return -EINVAL;
 		}
 		if (smc->sk.sk_state == SMC_INIT ||
-		    smc->sk.sk_state == SMC_CLOSED)
+		    smc->sk.sk_state == SMC_CLOSED ||
+		    !READ_ONCE(smc->conn.sndbuf_desc))
 			answ = 0;
 		else
 			answ = smc->conn.sndbuf_desc->len -
net/smc/smc_tx.h +5 -1
@@ -20,11 +20,15 @@
 
 static inline int smc_tx_prepared_sends(struct smc_connection *conn)
 {
+	struct smc_buf_desc *sndbuf_desc = READ_ONCE(conn->sndbuf_desc);
 	union smc_host_cursor sent, prep;
 
+	if (!sndbuf_desc)
+		return 0;
+
 	smc_curs_copy(&sent, &conn->tx_curs_sent, conn);
 	smc_curs_copy(&prep, &conn->tx_curs_prep, conn);
-	return smc_curs_diff(conn->sndbuf_desc->len, &sent, &prep);
+	return smc_curs_diff(sndbuf_desc->len, &sent, &prep);
 }
 
 void smc_tx_pending(struct smc_connection *conn);
author maintainer
#485 lore raw

PATCH 474/552 net/smc: fix socket refcount leak in smc_switch_conns()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Hidayath Khan <[email protected]>

commit 719296c4aa8213d4ac8002e77d5956d436bc98d0 upstream.

smc_switch_conns() takes a reference on the SMC socket before dropping lgr->conns_lock, so the connection stays alive while the CDC slot is fetched:

        sock_hold(&smc->sk);
        read_unlock_bh(&lgr->conns_lock);
        /* pre-fetch buffer outside of send_lock, might sleep */
        rc = smc_cdc_get_free_slot(conn, to_lnk, &wr_buf, NULL, &pend);
        if (rc)
                goto err_out;

The err_out label only drops the wr_tx link reference, so this early exit returns without the matching sock_put(). The second error exit is not affected, because sock_put() has already run by then.

A leaked sk_refcnt means the smc_sock is never destroyed. Its send and receive buffers stay allocated, and for a user socket the reference held on the network namespace is never released, so the netns can no longer be torn down.

smc_cdc_get_free_slot() fails when the target link goes down or when the connection has been killed while the switch is in progress. Both are reachable during the link failover this function implements, so the leak is triggered by the same hardware events that make smc_switch_conns() run in the first place.

Restructure so there is a single sock_put() covering both outcomes, instead of adding a second one to the error path.

---
 net/smc/smc_core.c |   14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
net/smc/smc_core.c +7 -7
@@ -1134,13 +1134,13 @@ again:
 		read_unlock_bh(&lgr->conns_lock);
 		/* pre-fetch buffer outside of send_lock, might sleep */
 		rc = smc_cdc_get_free_slot(conn, to_lnk, &wr_buf, NULL, &pend);
-		if (rc)
-			goto err_out;
-		/* avoid race with smcr_tx_sndbuf_nonempty() */
-		spin_lock_bh(&conn->send_lock);
-		smc_switch_link_and_count(conn, to_lnk);
-		rc = smc_switch_cursor(smc, pend, wr_buf);
-		spin_unlock_bh(&conn->send_lock);
+		if (!rc) {
+			/* avoid race with smcr_tx_sndbuf_nonempty() */
+			spin_lock_bh(&conn->send_lock);
+			smc_switch_link_and_count(conn, to_lnk);
+			rc = smc_switch_cursor(smc, pend, wr_buf);
+			spin_unlock_bh(&conn->send_lock);
+		}
 		sock_put(&smc->sk);
 		if (rc)
 			goto err_out;
author maintainer
#486 lore raw

PATCH 475/552 net/smc: fix use-after-free in smc_rx_pipe_buf_release()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Hidayath Khan <[email protected]>

commit c924884743e948e25625b7fbf3ee2a9325a204a7 upstream.

smc_rx_splice() hands RMB pages to a pipe and takes a socket reference per entry so the smc_sock stays alive until the reader finishes. The connection does not: a concurrent close runs smc_conn_free(), which releases the receive buffer back to the link group pool.

smc_rx_pipe_buf_release() tests sk_state before taking the socket lock. The state can change between the test and the lock, and smc_rx_update_cons() then dereferences conn->rmb_desc and walks conn->lgr, which smc_conn_free() has already released. On the is_reg_err path smcr_buf_unuse() frees the descriptor outright, so this is a use-after-free.

Take the socket lock first and test conn->freed instead. smc_conn_free() sets that flag before releasing anything, and every caller holds the socket lock. The two paths exclude each other: either the pipe release runs first with everything valid, or it sees the flag and skips the update.

---
 net/smc/smc_rx.c |   11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)
net/smc/smc_rx.c +5 -6
@@ -115,16 +115,15 @@ static void smc_rx_pipe_buf_release(stru
 				    struct pipe_buffer *buf)
 {
 	struct smc_spd_priv *priv = (struct smc_spd_priv *)buf->private;
+	struct smc_connection *conn = &priv->smc->conn;
 	struct smc_sock *smc = priv->smc;
-	struct smc_connection *conn;
 	struct sock *sk = &smc->sk;
 
-	if (sk->sk_state == SMC_CLOSED ||
-	    sk->sk_state == SMC_PEERFINCLOSEWAIT ||
-	    sk->sk_state == SMC_APPFINCLOSEWAIT)
-		goto out;
-	conn = &smc->conn;
 	lock_sock(sk);
+	if (conn->freed) {
+		release_sock(sk);
+		goto out;
+	}
 	smc_rx_update_cons(smc, priv->len);
 	release_sock(sk);
 	if (atomic_sub_and_test(priv->len, &conn->splice_pending))
author maintainer
#487 lore raw

PATCH 476/552 net/smc: fix use-after-free of the LLC qentry in smc_llc_srv_add_link()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yehyeong Lee <[email protected]>

commit a42a459ef0e54cb0c4b3e43e21cb0e658e664f64 upstream.

smc_llc_srv_add_link() keeps add_llc pointing into the queue entry:

add_llc = &qentry->msg.add_link; smc_llc.c:1482
...
smc_llc_save_add_link_info(link_new, add_llc); smc_llc.c:1494
smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); smc_llc.c:1495
...
u8 *llc_msg = smc_link_shared_v2_rxbuf(link) ?

	(u8 *)lgr->wr_rx_buf_v2 : (u8 *)add_llc;	smc_llc.c:1504

smc_llc_save_add_link_rkeys(link, link_new, llc_msg); smc_llc.c:1506

smc_llc_flow_qentry_del() kfree()s the entry, so on a link without a shared v2 receive buffer the pointer handed to smc_llc_save_add_link_rkeys() is already freed. Before the Fixes: commit that branch always used lgr->wr_rx_buf_v2 and add_llc was not used after the free.

Reproduced on an unpatched tree over rxe, with KASAN, kasan_multi_shot and a link forced to max_recv_sge == 1: the entry is freed and read by the same call, and the freeing frame is smc_llc_srv_add_link() itself.

  [    2.523161] BUG: KASAN: slab-use-after-free in smc_llc_save_add_link_rkeys+0x333/0x350
  [    2.523499] Read of size 2 at addr ffff8880052194de by task kworker/0:1/11
  [    2.523789]
  [    2.523862] CPU: 0 UID: 0 PID: 11 Comm: kworker/0:1 Not tainted 7.2.0-rc5-p0-g2c9dd296545d #35 PREEMPT(lazy)
  [    2.523865] 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
  [    2.523866] Workqueue: smc_hs_wq smc_listen_work
  [    2.523869] Call Trace:
  [    2.523870]  <TASK>
  [    2.523871]  dump_stack_lvl+0x53/0x70
  [    2.523872]  print_report+0xd0/0x630
  [    2.523874]  ? __pfx__raw_spin_lock_irqsave+0x10/0x10
  [    2.523876]  ? smc_llc_save_add_link_rkeys+0x333/0x350
  [    2.523878]  kasan_report+0xce/0x100
  [    2.523879]  ? smc_llc_save_add_link_rkeys+0x333/0x350
  [    2.523881]  smc_llc_save_add_link_rkeys+0x333/0x350
  [    2.523883]  ? smcr_buf_reg_lgr+0x2a4/0x660
  [    2.523885]  smc_llc_srv_add_link+0xaa2/0x1e50
  [    2.523888]  ? _printk+0xba/0xf0
  [    2.523897]  ? __pfx_smc_llc_srv_add_link+0x10/0x10
  [    2.523899]  ? down_write+0xb0/0x130
  [    2.523903]  ? __pfx_down_write+0x10/0x10
  [    2.523905]  smc_listen_work+0x489e/0x4d00
  [    2.523907]  ? kmem_cache_free+0x1c6/0x3a0
  [    2.523911]  ? __pfx_smc_listen_work+0x10/0x10
  [    2.523913]  ? release_sock+0x148/0x1d0
  [    2.523915]  ? smc_tcp_listen_work+0xb4f/0xfc0
  [    2.523917]  ? _raw_spin_lock_irq+0x80/0xe0
  [    2.523918]  ? __pfx__raw_spin_lock_irq+0x10/0x10
  [    2.523920]  process_one_work+0x633/0x1030
  [    2.523922]  ? assign_work+0x11d/0x370
  [    2.523924]  worker_thread+0x45b/0xd10
  [    2.523926]  ? __pfx_worker_thread+0x10/0x10
  [    2.523928]  ? __pfx_worker_thread+0x10/0x10
  [    2.523929]  kthread+0x2c6/0x3b0
  [    2.523931]  ? recalc_sigpending+0x15c/0x1e0
  [    2.523934]  ? __pfx_kthread+0x10/0x10
  [    2.523935]  ret_from_fork+0x36e/0x5a0
  [    2.523937]  ? __pfx_ret_from_fork+0x10/0x10
  [    2.523938]  ? __switch_to+0x572/0xdd0
  [    2.523943]  ? __pfx_kthread+0x10/0x10
  [    2.523944]  ret_from_fork_asm+0x1a/0x30
  [    2.523947]  </TASK>
  [    2.523948]
  [    2.531253] Allocated by task 48:
  [    2.531399]  kasan_save_stack+0x33/0x60
  [    2.531570]  kasan_save_track+0x14/0x30
  [    2.531737]  __kasan_kmalloc+0x8f/0xa0
  [    2.531905]  __kmalloc_cache_noprof+0x158/0x370
  [    2.532100]  smc_llc_enqueue+0x72/0x560
  [    2.532268]  smc_wr_rx_tasklet_fn+0x474/0xa80
  [    2.532491]  tasklet_action_common+0x20f/0x8a0
  [    2.532714]  handle_softirqs+0x18e/0x590
  [    2.532886]  do_softirq+0x3b/0x60
  [    2.533036]  __local_bh_enable_ip+0x61/0x70
  [    2.533221]  __alloc_skb+0x732/0x890
  [    2.533384]  rxe_init_packet+0x16b/0x4f0
  [    2.533567]  prepare_ack_packet+0xb8/0x830
  [    2.533760]  rxe_receiver+0x495/0x96e0
  [    2.533933]  do_work+0x144/0x470
  [    2.534078]  process_one_work+0x633/0x1030
  [    2.534257]  worker_thread+0x45b/0xd10
  [    2.534424]  kthread+0x2c6/0x3b0
  [    2.534569]  ret_from_fork+0x36e/0x5a0
  [    2.534737]  ret_from_fork_asm+0x1a/0x30
  [    2.534907]
  [    2.534980] Freed by task 11:
  [    2.535112]  kasan_save_stack+0x33/0x60
  [    2.535279]  kasan_save_track+0x14/0x30
  [    2.535444]  kasan_save_free_info+0x3b/0x60
  [    2.535625]  __kasan_slab_free+0x43/0x70
  [    2.535798]  kfree+0x121/0x380
  [    2.535935]  smc_llc_srv_add_link+0x9a8/0x1e50
  [    2.536128]  smc_listen_work+0x489e/0x4d00
  [    2.536305]  process_one_work+0x633/0x1030
  [    2.536482]  worker_thread+0x45b/0xd10
  [    2.536652]  kthread+0x2c6/0x3b0
  [    2.536794]  ret_from_fork+0x36e/0x5a0
  [    2.536958]  ret_from_fork_asm+0x1a/0x30
  [    2.537133]
  [    2.537205] The buggy address belongs to the object at ffff888005219480
  [    2.537205]  which belongs to the cache kmalloc-96 of size 96
  [    2.537719] The buggy address is located 94 bytes inside of
  [    2.537719]  freed 96-byte region [ffff888005219480, ffff8880052194e0)
  [    2.538216]
  [    2.538289] The buggy address belongs to the physical page:
  [    2.538524] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x5219
  [    2.538857] flags: 0x100000000000000(node=0|zone=1)
  [    2.539066] page_type: f5(slab)
  [    2.539210] raw: 0100000000000000 ffff888001041280 dead000000000122 0000000000000000
  [    2.539534] raw: 0000000000000000 0000000000200020 00000000f5000000 0000000000000000
  [    2.539863] page dumped because: kasan: bad access detected
  [    2.540098]
  [    2.540170] Memory state around the buggy address:
  [    2.540379]  ffff888005219380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
  [    2.540684]  ffff888005219400: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
  [    2.540988] >ffff888005219480: fa fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
  [    2.541291]                                                     ^
  [    2.541548]  ffff888005219500: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc
  [    2.541857]  ffff888005219580: 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc

The offset is past the 72-byte queue entry because the out-of-bounds read fixed by the next patch is on the same line; what this patch removes is the free at smc_llc_srv_add_link+0x9a8 happening before the read at +0xaa2.

Detach the entry instead of freeing it there, and free it at the single exit label. The reject path has to detach as well, otherwise it would be freed twice.

This changes only the lifetime of the entry. The same read still runs past its end until the next two patches bound it, so a backport wants all three.

Fixes 27ef6a9981fe ("net/smc: support SMC-R V2 for rdma devices with max_recv_sge equals to 1")
---
 net/smc/smc_llc.c |    9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
net/smc/smc_llc.c +5 -4
@@ -1489,7 +1489,7 @@ int smc_llc_srv_add_link(struct smc_link
 	}
 	add_llc = &qentry->msg.add_link;
 	if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) {
-		smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+		smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
 		rc = -ENOLINK;
 		goto out_err;
 	}
@@ -1500,7 +1500,8 @@ int smc_llc_srv_add_link(struct smc_link
 		lgr_new_t = SMC_LGR_ASYMMETRIC_PEER;
 	}
 	smc_llc_save_add_link_info(link_new, add_llc);
-	smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+	/* add_llc still points into qentry, so only detach it here */
+	smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
 
 	rc = smc_ib_ready_link(link_new);
 	if (rc)
@@ -1520,14 +1521,14 @@ int smc_llc_srv_add_link(struct smc_link
 	rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t);
 	if (rc)
 		goto out_err;
-	kfree(ini);
-	return 0;
+	goto out;
 out_err:
 	if (link_new) {
 		link_new->state = SMC_LNK_INACTIVE;
 		smcr_link_clear(link_new, false);
 	}
 out:
+	kfree(qentry);
 	kfree(ini);
 	if (send_req_add_link_resp)
 		smc_llc_send_req_add_link_response(req_qentry);
author maintainer
#488 lore raw

PATCH 477/552 net/smc: stop killed, freed and out_of_sync sharing a byte

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Hidayath Khan <[email protected]>

commit db51a8658c11a82432b64999519a269c3aabb447 upstream.

The three connection state flags are single-bit bitfields, so they occupy one byte of struct smc_connection and every store to one is a read-modify-write of the other two:

    u8  killed : 1;
    u8  freed : 1;
    u8  out_of_sync : 1;

They are not written under a common lock. smc_cdc_msg_validate() sets out_of_sync from the receive tasklet, while smc_conn_kill() sets killed from process context under lock_sock(), and the receive path does not defer to the backlog when the socket is owned -- smc_cdc_msg_recv() takes only bh_lock_sock().

Give each flag its own byte so a store no longer touches its neighbours. All readers test them as booleans and are unchanged. struct smc_connection grows by two bytes.

---
 net/smc/smc.h |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
net/smc/smc.h +3 -3
@@ -277,9 +277,9 @@ struct smc_connection {
 						 * 0 for SMC-R, 32 for SMC-D
 						 */
 	u64			peer_token;	/* SMC-D token of peer */
-	u8			killed : 1;	/* abnormal termination */
-	u8			freed : 1;	/* normal termination */
-	u8			out_of_sync : 1; /* out of sync with peer */
+	u8			killed;		/* abnormal termination */
+	u8			freed;		/* normal termination */
+	u8			out_of_sync;	/* out of sync with peer */
 };
 
 struct smc_sock {				/* smc sock container */
author maintainer
#489 lore raw

PATCH 478/552 net/smc: unregister the connection before draining the rx tasklet

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryam Vargas <[email protected]>

commit 36cdf5d48ca191dcd71c28cadbe0981b1d25318d upstream.

smc_conn_free() calls smc_ism_unset_conn() only while the link group is still on its device list, and never sets conn->killed. smc_lgr_terminate_sched() unlinks the group immediately and defers killing its connections to a work item, so a connection freed in that window keeps its smcd->conn[] slot with both gates in smcd_handle_irq() open, and the device can re-arm the receive tasklet after tasklet_kill() has returned. On the DMB-nocopy path the ghost send buffer is freed right after that drain, so the re-armed tasklet dereferences it.

Unregister unconditionally and drain before the detach at both teardown sites, mirroring rmb_desc, which smc_buf_unuse() releases after the drain. Clear conn->sndbuf_desc before freeing it as well, so a reader that samples the pointer cannot get one that is already freed.

---
 net/smc/smc_core.c |   13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)
net/smc/smc_core.c +7 -6
@@ -1195,14 +1195,16 @@ static void smcd_buf_detach(struct smc_c
 {
 	struct smcd_dev *smcd = conn->lgr->smcd;
 	u64 peer_token = conn->peer_token;
+	struct smc_buf_desc *buf_desc;
 
 	if (!conn->sndbuf_desc)
 		return;
 
 	smc_ism_detach_dmb(smcd, peer_token);
 
-	kfree(conn->sndbuf_desc);
+	buf_desc = conn->sndbuf_desc;
 	conn->sndbuf_desc = NULL;
+	kfree(buf_desc);
 }
 
 static void smc_buf_unuse(struct smc_connection *conn,
@@ -1254,11 +1256,10 @@ void smc_conn_free(struct smc_connection
 		goto lgr_put;
 
 	if (lgr->is_smcd) {
-		if (!list_empty(&lgr->list))
-			smc_ism_unset_conn(conn);
+		smc_ism_unset_conn(conn);
+		tasklet_kill(&conn->rx_tsklet);
 		if (smc_ism_support_dmb_nocopy(lgr->smcd))
 			smcd_buf_detach(conn);
-		tasklet_kill(&conn->rx_tsklet);
 	} else {
 		smc_cdc_wait_pend_tx_wr(conn);
 		if (current_work() != &conn->abort_work)
@@ -1511,12 +1512,12 @@ static void smc_conn_kill(struct smc_con
 	smc_sk_wake_ups(smc);
 	if (conn->lgr->is_smcd) {
 		smc_ism_unset_conn(conn);
-		if (smc_ism_support_dmb_nocopy(conn->lgr->smcd))
-			smcd_buf_detach(conn);
 		if (soft)
 			tasklet_kill(&conn->rx_tsklet);
 		else
 			tasklet_unlock_wait(&conn->rx_tsklet);
+		if (smc_ism_support_dmb_nocopy(conn->lgr->smcd))
+			smcd_buf_detach(conn);
 	} else {
 		smc_cdc_wait_pend_tx_wr(conn);
 	}
author maintainer
#490 lore raw

PATCH 479/552 net: cap advertised IP tunnel headroom

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zhiling Zou <[email protected]>

commit 6b222adeb9340306e2ff97127c76117abb9b3df8 upstream.

IP tunnel devices derive their advertised needed_headroom from lower output devices. A stack of user-created devices can make the derived value larger than the 16-bit skb header offsets can represent. Once IP output reserves it, skb head expansion can wrap those offsets.

The runtime transmit path already caps a growing needed_headroom at 512. Apply the same cap when tunnel configuration publishes needed_headroom derived from a lower output device.

Capping the advertised value is safe: IP tunnel transmit still expands the skb when a packet needs more headroom. A nonsensical stacked configuration can therefore incur an extra reallocation, but it cannot publish an unbounded reservation to upper layers.

---
 include/net/ip_tunnels.h |   11 +++++++++--
 net/ipv4/ip_tunnel.c     |    2 +-
 net/ipv6/ip6_gre.c       |    7 +++++--
 net/ipv6/ip6_tunnel.c    |    7 +++++--
 net/ipv6/sit.c           |    2 +-
 5 files changed, 21 insertions(+), 8 deletions(-)
include/net/ip_tunnels.h +9 -2
@@ -629,8 +629,7 @@ struct metadata_dst *iptunnel_metadata_r
 int skb_tunnel_check_pmtu(struct sk_buff *skb, struct dst_entry *encap_dst,
 			  int headroom, bool reply);
 
-static inline void ip_tunnel_adj_headroom(struct net_device *dev,
-					  unsigned int headroom)
+static inline unsigned int ip_tunnel_limit_headroom(unsigned int headroom)
 {
 	/* we must cap headroom to some upperlimit, else pskb_expand_head
 	 * will overflow header offsets in skb_headers_offset_update().
@@ -640,6 +639,14 @@ static inline void ip_tunnel_adj_headroo
 	if (headroom > max_allowed)
 		headroom = max_allowed;
 
+	return headroom;
+}
+
+static inline void ip_tunnel_adj_headroom(struct net_device *dev,
+					  unsigned int headroom)
+{
+	headroom = ip_tunnel_limit_headroom(headroom);
+
 	if (headroom > READ_ONCE(dev->needed_headroom))
 		WRITE_ONCE(dev->needed_headroom, headroom);
 }
net/ipv4/ip_tunnel.c +1 -1
@@ -317,7 +317,7 @@ static int ip_tunnel_bind_dev(struct net
 		mtu = min(tdev->mtu, IP_MAX_MTU);
 	}
 
-	dev->needed_headroom = t_hlen + hlen;
+	dev->needed_headroom = ip_tunnel_limit_headroom(t_hlen + hlen);
 	mtu -= t_hlen + (dev->type == ARPHRD_ETHER ? dev->hard_header_len : 0);
 
 	if (mtu < IPV4_MIN_MTU)
net/ipv6/ip6_gre.c +5 -2
@@ -1136,8 +1136,11 @@ static void ip6gre_tnl_link_config_route
 			return;
 
 		if (rt->dst.dev) {
-			dev->needed_headroom = rt->dst.dev->hard_header_len +
-					       t_hlen;
+			unsigned int headroom;
+
+			headroom = rt->dst.dev->hard_header_len + t_hlen;
+			headroom = ip_tunnel_limit_headroom(headroom);
+			dev->needed_headroom = headroom;
 
 			if (set_mtu) {
 				int mtu = rt->dst.dev->mtu - t_hlen;
net/ipv6/ip6_tunnel.c +5 -2
@@ -1506,8 +1506,11 @@ static void ip6_tnl_link_config(struct i
 			tdev = __dev_get_by_index(t->net, p->link);
 
 		if (tdev) {
-			dev->needed_headroom = tdev->hard_header_len +
-				tdev->needed_headroom + t_hlen;
+			unsigned int headroom;
+
+			headroom = tdev->hard_header_len + tdev->needed_headroom;
+			headroom += t_hlen;
+			dev->needed_headroom = ip_tunnel_limit_headroom(headroom);
 			mtu = min_t(unsigned int, tdev->mtu, IP6_MAX_MTU);
 
 			mtu = mtu - t_hlen;
net/ipv6/sit.c +1 -1
@@ -1128,7 +1128,7 @@ static void ipip6_tunnel_bind_dev(struct
 		WRITE_ONCE(dev->mtu, mtu);
 		hlen = tdev->hard_header_len + tdev->needed_headroom;
 	}
-	dev->needed_headroom = t_hlen + hlen;
+	dev->needed_headroom = ip_tunnel_limit_headroom(t_hlen + hlen);
 }
 
 static void ipip6_tunnel_update(struct ip_tunnel *t,
author maintainer
#491 lore raw

PATCH 480/552 net: fix spurious TX timeout after dev_activate()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Breno Leitao <[email protected]>

commit 82aeed2400786bd3f79d88cb8b8f42e6127e5923 upstream.

While debugging another issue today, I found out that my TX queue is reported as stopped for 4294907392 ms (49.7 days), on a machine that had been up for four minutes.

    bnxt_en 0002:01:00.0 eth0: NETDEV WATCHDOG: CPU: 28: transmit queue 23 timed out 4294907392 ms

4294907392 is not an elapsed time. It is the value of jiffies at that
moment: INITIAL_JIFFIES is 4294667296, which leaves jiffies 59 seconds short of wrapping.

dev_activate() runs transition_one_qdisc() over every TX queue, which resets trans_start to 0, and then stamps only queue 0 through netif_trans_update().

Stamp jiffies instead. A queue stopped across dev_activate() now gets a full watchdog_timeo of grace, and is still reported if it is stopped that long.

---
 net/sched/sch_generic.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/sched/sch_generic.c +1 -1
@@ -1259,7 +1259,7 @@ static void transition_one_qdisc(struct
 
 	rcu_assign_pointer(dev_queue->qdisc, new_qdisc);
 	if (need_watchdog_p) {
-		WRITE_ONCE(dev_queue->trans_start, 0);
+		WRITE_ONCE(dev_queue->trans_start, jiffies);
 		*need_watchdog_p = 1;
 	}
 }
author maintainer
#492 lore raw

PATCH 481/552 net: skbuff: dont touch shared zerocopy state in skb_tx_error()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Norbert Szetei <[email protected]>

commit f66bdb1cc0fcd227a062378f8be0b5873aa5600a upstream.

skb_tx_error() completes the zerocopy uarg and clears SKBFL_ALL_ZEROCOPY, and skb_zcopy_downgrade_managed() clears SKBFL_MANAGED_FRAG_REFS. Both live in skb_shinfo(), which every clone shares, while the caller only owns the reference it is about to drop. Through a clone it tells the producer its pages are free and drops SKBFL_SHARED_FRAG for an skb that is still in flight.

Open vSwitch reaches this with a non-last OVS_ACTION_ATTR_RECIRC:
clone_execute() sends a skb_clone() into ovs_dp_process_packet() while do_execute_actions() keeps forwarding the original, and skb_clone() does not privatise the frags here -- skb_orphan_frags() returns early on SKBFL_DONT_ORPHAN. A flow miss on the clone then strips the marker from the packet still being forwarded, and a later local ESP delivery decrypts in place over frags it does not own privately.

Skip it for a cloned skb. Nothing is lost: skb_release_data() clears the zerocopy state once the last reference to the shared data goes.

---
 net/core/skbuff.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
net/core/skbuff.c +4 -1
@@ -1373,10 +1373,13 @@ EXPORT_SYMBOL(skb_dump);
  *
  *	Report xmit error if a device callback is tracking this skb.
  *	skb must be freed afterwards.
+ *
+ *	Does nothing for a cloned skb: the zerocopy state lives in
+ *	skb_shinfo(), which the clones share.
  */
 void skb_tx_error(struct sk_buff *skb)
 {
-	if (skb) {
+	if (skb && !skb_cloned(skb)) {
 		skb_zcopy_downgrade_managed(skb);
 		skb_zcopy_clear(skb, true);
 	}
author maintainer
#493 lore raw

PATCH 482/552 seg6: reset IP6CB after IPv6 decapsulation

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zhiling Zou <[email protected]>

commit f967455fb2a5a2079b9eb5823e9ccf359174bf9f upstream.

decap_and_validate() pulls the outer SRv6 headers and makes the inner packet the skb network header. The IPv6 control block still contains values collected while parsing the outer packet, including nhoff and extension-header flags.

End.DX6 and End.DT6 route the inner IPv6 packet directly to the IPv6 input path. An unprivileged user can reach End.DT6 from a user and net namespace by installing a local SID and injecting an outer packet with Hop-by-Hop and Destination Options headers followed by an SRH and a minimal inner IPv6 packet.

The outer extension headers leave a large nhoff in IP6CB. After decapsulation, ip6_protocol_deliver_rcu() uses that stale offset on the inner packet and reads beyond the skb head. KASAN reports:

  BUG: KASAN: slab-out-of-bounds in ip6_protocol_deliver_rcu
  ip6_protocol_deliver_rcu+0x1118/0x1450
  ip6_input_finish+0x11b/0x240
  seg6_local_input_core+0xed/0x2e0
  lwtunnel_input+0x1e9/0x4e0
  ipv6_rthdr_rcv+0x525f/0x6c50
  ip6_protocol_deliver_rcu+0xcb7/0x1450

Before clearing IP6CB for an inner IPv6 packet, save its incoming interface index and L3 slave state. Restore both after the clear and set nhoff to the inner IPv6 base-header nexthdr field.

Use IP6CB(skb)->iif rather than skb->skb_iif because VRF processing can replace skb_iif with the L3 master while IP6CB keeps the receiving interface. Preserve IP6SKB_L3SLAVE for the same reason.

Fixes d7a669dd2f8b ("ipv6: sr: add helper functions for seg6local")
---
 net/ipv6/seg6_local.c |    9 +++++++++
 1 file changed, 9 insertions(+)
net/ipv6/seg6_local.c +9 -0
@@ -261,6 +261,15 @@ static bool decap_and_validate(struct sk
 
 		memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
 		IPCB(skb)->iif = iif;
+	} else if (proto == IPPROTO_IPV6) {
+		bool l3slave = ipv6_l3mdev_skb(IP6CB(skb)->flags);
+		int iif = IP6CB(skb)->iif;
+
+		memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
+		IP6CB(skb)->iif = iif;
+		IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
+		if (l3slave)
+			IP6CB(skb)->flags |= IP6SKB_L3SLAVE;
 	}
 
 	return true;
author maintainer
#494 lore raw

PATCH 483/552 hwrng: stm32 - Fix runtime PM cleanup on registration failure

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Can Peng <[email protected]>

commit 1163a476a568f6c0f852d469c8e4c5a5f805adac upstream.

stm32_rng_probe() enables autosuspend and runtime PM before registering the hwrng. If devm_hwrng_register() fails, probe returns with runtime PM left enabled and autosuspend still selected.

The remove callback also only disables runtime PM and does not undo pm_runtime_use_autosuspend().

Use devm_pm_runtime_enable() so runtime PM is unwound automatically on probe failure and driver detach. Since the managed cleanup also disables runtime PM,drop the remove callback.

Fixes c6a97c42e399 ("hwrng: stm32 - add support for STM32 HW RNG")
---
 drivers/char/hw_random/stm32-rng.c |   10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)
drivers/char/hw_random/stm32-rng.c +3 -7
@@ -368,11 +368,6 @@ static int stm32_rng_init(struct hwrng *
 	return 0;
 }
 
-static void stm32_rng_remove(struct platform_device *ofdev)
-{
-	pm_runtime_disable(&ofdev->dev);
-}
-
 static int __maybe_unused stm32_rng_runtime_suspend(struct device *dev)
 {
 	struct stm32_rng_private *priv = dev_get_drvdata(dev);
@@ -590,7 +585,9 @@ static int stm32_rng_probe(struct platfo
 
 	pm_runtime_set_autosuspend_delay(dev, 100);
 	pm_runtime_use_autosuspend(dev);
-	pm_runtime_enable(dev);
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
 
 	return devm_hwrng_register(dev, &priv->rng);
 }
@@ -602,7 +599,6 @@ static struct platform_driver stm32_rng_
 		.of_match_table = stm32_rng_match,
 	},
 	.probe = stm32_rng_probe,
-	.remove = stm32_rng_remove,
 };
 
 module_platform_driver(stm32_rng_driver);
author maintainer
#495 lore raw

PATCH 484/552 mfd: cgbc: Fix teardown ordering in cgbc_remove()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Thomas Richard <[email protected]>

commit 2970c2db8ab3d97ea6250c14ca49b1e1731115ab upstream.

Release Board Controller session once children are removed by the core.

---
 drivers/mfd/cgbc-core.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/mfd/cgbc-core.c +2 -2
@@ -363,9 +363,9 @@ static void cgbc_remove(struct platform_
 {
 	struct cgbc_device_data *cgbc = platform_get_drvdata(pdev);
 
-	cgbc_session_release(cgbc);
-
 	mfd_remove_devices(&pdev->dev);
+
+	cgbc_session_release(cgbc);
 }
 
 static struct platform_driver cgbc_driver = {
author maintainer
#496 lore raw

PATCH 485/552 mfd: sm501: Fix potential memory leaks during remove

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Abdun Nihaal <[email protected]>

commit 83feedd9d83c0c5199f98c72df0a6196b4aefb4d upstream.

The memory allocated for struct sm501_devdata in sm501_pci_probe() and sm501_plat_probe() is not freed by the corresponding remove functions sm501_pci_remove() and sm501_plat_remove(). Fix that by adding a call to kfree().

---
 drivers/mfd/sm501.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/mfd/sm501.c +2 -0
@@ -1627,6 +1627,7 @@ static void sm501_pci_remove(struct pci_
 	release_mem_region(sm->io_res->start, 0x100);
 
 	pci_disable_device(dev);
+	kfree(sm);
 }
 
 static void sm501_plat_remove(struct platform_device *dev)
@@ -1637,6 +1638,7 @@ static void sm501_plat_remove(struct pla
 	iounmap(sm->regs);
 
 	release_mem_region(sm->io_res->start, 0x100);
+	kfree(sm);
 }
 
 static const struct pci_device_id sm501_pci_tbl[] = {
author maintainer
#497 lore raw

PATCH 486/552 ALSA: 6fire: bound the MIDI event length from the device

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Baul Lee <[email protected]>

commit a478893b59e36cfe7d77a76b352f2db55502e879 upstream.

usb6fire_comm_receiver_handler() forwards a MIDI event using a length byte the device supplies, with no bound and no check that the transfer delivered that many bytes:

	if (!urb->status) {
		if (rt->receiver_buffer[0] == 0x10) /* midi in event */
			if (midi_rt)
				midi_rt->in_received(midi_rt,
						rt->receiver_buffer + 2,
						rt->receiver_buffer[1]);
	}

receiver_buffer is a 64-byte kzalloc() buffer (COMM_RECEIVER_BUFSIZE), so only 62 bytes follow the two-byte header. receiver_buffer[1] is a u8 the device chooses, so a device that answers with 0x10 and a length of 0xFF makes snd_rawmidi_receive() read 255 bytes starting two bytes into a 64-byte object. The bytes past the buffer are handed to userspace through the rawmidi read path.

urb->actual_length is not consulted either, so a short transfer leaves both the type byte and the length byte at their previous values and the handler acts on stale data.

The receiver URB is submitted from usb6fire_comm_init() at probe, so the read happens on plug with no user action; forwarding to userspace also needs a MIDI input substream open, since usb6fire_midi_in_received() only calls snd_rawmidi_receive() when rt->in is set.

KASAN on 7.2.0-rc5 (arm64), single packet from an emulated device:

  BUG: KASAN: slab-out-of-bounds in snd_rawmidi_receive
  Read of size 255 at addr ffff000009f64682 by task bash/183
   __asan_memcpy
   snd_rawmidi_receive
   usb6fire_midi_in_received [snd_usb_6fire]
   usb6fire_comm_receiver_handler [snd_usb_6fire]
  Allocated by task 11:
   usb6fire_comm_init [snd_usb_6fire]
   usb6fire_chip_probe [snd_usb_6fire]
  The buggy address is located 2 bytes inside of
   allocated 64-byte region [ffff000009f64680, ffff000009f646c0)

Reject the event when the length exceeds the bytes that follow the header, and require the transfer to have delivered the header plus that many bytes. The receiver URB is submitted with a 64-byte transfer_buffer_length, so a genuine device cannot deliver an event longer than those 62 bytes and nothing valid is dropped.

Discovered by XBOW, triaged by Baul Lee <[email protected]>

---
 sound/usb/6fire/comm.c |    9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
sound/usb/6fire/comm.c +6 -3
@@ -36,11 +36,14 @@ static void usb6fire_comm_receiver_handl
 	struct midi_runtime *midi_rt = rt->chip->midi;
 
 	if (!urb->status) {
-		if (rt->receiver_buffer[0] == 0x10) /* midi in event */
+		u8 len = rt->receiver_buffer[1];
+
+		if (rt->receiver_buffer[0] == 0x10 && /* midi in event */
+		    len <= COMM_RECEIVER_BUFSIZE - 2 &&
+		    urb->actual_length >= len + 2)
 			if (midi_rt)
 				midi_rt->in_received(midi_rt,
-						rt->receiver_buffer + 2,
-						rt->receiver_buffer[1]);
+						rt->receiver_buffer + 2, len);
 	}
 
 	if (!rt->chip->shutdown) {
author maintainer
#498 lore raw

PATCH 487/552 ALSA: aloop: Check card index validity at probe

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Takashi Iwai <[email protected]>

commit 819b106a9fd2ef3fd8abf898b9a8e4524eca8f48 upstream.

aloop driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.

Add a sanity check for the card index and warn/correct it if it's a value out of the range.

---
 sound/drivers/aloop.c |    6 ++++++
 1 file changed, 6 insertions(+)
sound/drivers/aloop.c +6 -0
@@ -1805,6 +1805,12 @@ static int loopback_probe(struct platfor
 	int dev = devptr->id;
 	int err;
 
+	if (dev < 0 || dev >= SNDRV_CARDS) {
+		dev_warn(&devptr->dev,
+			 "Invalid card index %d, using default 0\n", dev);
+		dev = 0;
+	}
+
 	err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
 				sizeof(struct loopback), &card);
 	if (err < 0)
author maintainer
#499 lore raw

PATCH 488/552 ALSA: bcd2000: clear the URB pointers on disconnect

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Baul Lee <[email protected]>

commit 459d3a64766f5ca2f1886daeaf24582831a5f5ab upstream.

bcd2000_free_usb_related_resources() frees both URBs and leaves the pointers behind:

	usb_kill_urb(bcd2k->midi_out_urb);
	usb_kill_urb(bcd2k->midi_in_urb);

	usb_free_urb(bcd2k->midi_out_urb);
	usb_free_urb(bcd2k->midi_in_urb);

The rawmidi device outlives that call. A substream that is still open when the device is unplugged reaches bcd2000_midi_send() from the trigger path on close. That function writes to the freed URB and then hands it to the USB core:

	bcd2k->midi_out_urb->transfer_buffer_length = BUFSIZE;
	...
	ret = usb_submit_urb(bcd2k->midi_out_urb, GFP_ATOMIC);

usb_kill_urb() does not stop a later submission either, so a submit that races the disconnect can requeue the URB after it has been reaped. midi_in_urb is exposed the same way: bcd2000_input_complete() resubmits it from the completion handler.

KASAN on 7.2.0-rc5 (arm64):

  BUG: KASAN: slab-use-after-free in bcd2000_midi_send [snd_bcd2000]
  Write of size 4 at addr ffff00001827d388 by task bpoc/168
   __asan_store4
   bcd2000_midi_send [snd_bcd2000]
   bcd2000_midi_output_trigger [snd_bcd2000]
   snd_rawmidi_kernel_write1
   close_substream.part.0
  Freed by task 168:
   usb_free_urb
   bcd2000_disconnect [snd_bcd2000]

  BUG: KASAN: slab-use-after-free in usb_submit_urb
  Read of size 8 at addr ffff00001827d3b8 by task bpoc/168

Clear both pointers after freeing and test them on the paths that can still run. Poison the URBs before freeing them: usb_poison_urb() waits for a running completion handler and rejects any later submission, so after it returns the input path is quiesced and only the rawmidi trigger path can still reach bcd2000_midi_send(). No unpoison is needed; the URBs are freed on the next line.

Discovered by XBOW, triaged by Baul Lee <[email protected]>

---
 sound/usb/bcd2000/bcd2000.c |   11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
sound/usb/bcd2000/bcd2000.c +8 -3
@@ -134,6 +134,9 @@ static void bcd2000_midi_send(struct bcd
 	if (!midi_out_substream)
 		return;
 
+	if (!bcd2k->midi_out_urb)
+		return;
+
 	/* copy command prefix bytes */
 	memcpy(bcd2k->midi_out_buf, device_cmd_prefix,
 		sizeof(device_cmd_prefix));
@@ -178,7 +181,7 @@ static int bcd2000_midi_output_close(str
 {
 	struct bcd2000 *bcd2k = substream->rmidi->private_data;
 
-	if (bcd2k->midi_out_active) {
+	if (bcd2k->midi_out_active && bcd2k->midi_out_urb) {
 		usb_kill_urb(bcd2k->midi_out_urb);
 		bcd2k->midi_out_active = 0;
 	}
@@ -348,11 +351,13 @@ static int bcd2000_init_midi(struct bcd2
 static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k,
 						struct usb_interface *interface)
 {
-	usb_kill_urb(bcd2k->midi_out_urb);
-	usb_kill_urb(bcd2k->midi_in_urb);
+	usb_poison_urb(bcd2k->midi_out_urb);
+	usb_poison_urb(bcd2k->midi_in_urb);
 
 	usb_free_urb(bcd2k->midi_out_urb);
 	usb_free_urb(bcd2k->midi_in_urb);
+	bcd2k->midi_out_urb = NULL;
+	bcd2k->midi_in_urb = NULL;
 
 	if (bcd2k->intf) {
 		usb_set_intfdata(bcd2k->intf, NULL);
author maintainer
#500 lore raw

PATCH 489/552 ALSA: hda/ext: preserve PPLCCTL bits when clearing reset

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Xu Rao <[email protected]>

commit 36aa66de481d29edd63cbad9b5c4dc18c340fdf6 upstream.

snd_hdac_ext_stream_reset() polls PPLCCTL for STRST by masking the register value with AZX_PPLCCTL_STRST:

	val = readl(...) & AZX_PPLCCTL_STRST;

The same masked value is then used when clearing STRST. Since val contains no bits other than STRST, clearing STRST from it always produces zero. The subsequent writel() therefore writes zero to the entire PPLCCTL register instead of clearing only the reset bit.

PPLCCTL contains other stream control fields, including the stream tag in AZX_PPLCCTL_STRM_MASK. Those fields must not be modified as a side effect of clearing stream reset.

Use snd_hdac_updatel() to clear STRST, matching the existing set-reset path and preserving all unrelated PPLCCTL bits.

Fixes df203a4e46f4 ("ALSA: hdac_ext: add extended stream capabilities")
---
 sound/hda/core/ext/stream.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
sound/hda/core/ext/stream.c +2 -2
@@ -211,8 +211,8 @@ void snd_hdac_ext_stream_reset(struct hd
 			break;
 		udelay(3);
 	} while (--timeout);
-	val &= ~AZX_PPLCCTL_STRST;
-	writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
+	snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
+			 AZX_PPLCCTL_STRST, 0);
 	udelay(3);
 
 	timeout = 50;
author maintainer
#501 lore raw

PATCH 490/552 ALSA: mpu401: Check card index validity at probe

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Takashi Iwai <[email protected]>

commit f7dcecb92ed192ff5fcf842918fb1aaea84b5bdd upstream.

mpu401 driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.

Add a sanity check for the card index and warn/correct it if it's a value out of the range.

---
 sound/drivers/mpu401/mpu401.c |    6 ++++++
 1 file changed, 6 insertions(+)
sound/drivers/mpu401/mpu401.c +6 -0
@@ -89,6 +89,12 @@ static int snd_mpu401_probe(struct platf
 	int err;
 	struct snd_card *card;
 
+	if (dev < 0 || dev >= SNDRV_CARDS) {
+		dev_warn(&devptr->dev,
+			 "Invalid card index %d, using default 0\n", dev);
+		dev = 0;
+	}
+
 	if (port[dev] == SNDRV_AUTO_PORT) {
 		dev_err(&devptr->dev, "specify port\n");
 		return -EINVAL;
author maintainer
#502 lore raw

PATCH 491/552 ALSA: mts64: Check card index validity at probe

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Takashi Iwai <[email protected]>

commit d18a260720f86a5f8b5fcfefc4ba2e9dd01c10f8 upstream.

Although mts64 driver has a check of the given devptr->id value, it doesn't check for a negative id, which is often given as "none" or such value when bound via sysfs. This may lead to OOB access for index[] and other parameters.

Add a sanity check for the card index and warn/correct it if it's a value out of the range.

---
 sound/drivers/mts64.c |    6 ++++++
 1 file changed, 6 insertions(+)
sound/drivers/mts64.c +6 -0
@@ -900,6 +900,12 @@ static int snd_mts64_probe(struct platfo
 	p = platform_get_drvdata(pdev);
 	platform_set_drvdata(pdev, NULL);
 
+	if (dev < 0) {
+		dev_warn(&pdev->dev,
+			 "Invalid card index %d, using default 0\n", dev);
+		dev = 0;
+	}
+
 	if (dev >= SNDRV_CARDS)
 		return -ENODEV;
 	if (!enable[dev]) 
author maintainer
#503 lore raw

PATCH 492/552 ALSA: pcxhr: initialize mutexes before requesting threaded IRQ

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Runyu Xiao <[email protected]>

commit 6c97817e20598e5473094e0e38d1f51f1cf4dfff upstream.

pcxhr_probe() requests pcxhr_threaded_irq() before initializing mgr->lock, even though the threaded handler takes that mutex.

Initialize the manager locks before request_threaded_irq() so an early interrupt cannot run against uninitialized mutex state during probe.

---
 sound/pci/pcxhr/pcxhr.c |   14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
sound/pci/pcxhr/pcxhr.c +7 -7
@@ -1532,6 +1532,13 @@ static int pcxhr_probe(struct pci_dev *p
 	mgr->pci = pci;
 	mgr->irq = -1;
 
+	/* ISR lock  */
+	mutex_init(&mgr->lock);
+	mutex_init(&mgr->msg_lock);
+
+	/* init setup mutex*/
+	mutex_init(&mgr->setup_mutex);
+
 	if (request_threaded_irq(pci->irq, pcxhr_interrupt,
 				 pcxhr_threaded_irq, IRQF_SHARED,
 				 KBUILD_MODNAME, mgr)) {
@@ -1545,13 +1552,6 @@ static int pcxhr_probe(struct pci_dev *p
 		 "Digigram at 0x%lx & 0x%lx, 0x%lx irq %i",
 		 mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq);
 
-	/* ISR lock  */
-	mutex_init(&mgr->lock);
-	mutex_init(&mgr->msg_lock);
-
-	/* init setup mutex*/
-	mutex_init(&mgr->setup_mutex);
-
 	mgr->prmh = kmalloc(sizeof(*mgr->prmh) +
 			    sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS -
 					   PCXHR_SIZE_MAX_STATUS),
author maintainer
#504 lore raw

PATCH 493/552 ALSA: portman2x4: Check card index validity at probe

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Takashi Iwai <[email protected]>

commit 3690ef20469d5959378260e2752f2314a2572913 upstream.

Although portman2x4 driver has a check of the given devptr->id value, it doesn't check for a negative id, which is often given as "none" or such value when bound via sysfs. This may lead to OOB access for index[] and other parameters.

Add a sanity check for the card index and warn/correct it if it's a value out of the range.

---
 sound/drivers/portman2x4.c |    6 ++++++
 1 file changed, 6 insertions(+)
sound/drivers/portman2x4.c +6 -0
@@ -697,6 +697,12 @@ static int snd_portman_probe(struct plat
 	p = platform_get_drvdata(pdev);
 	platform_set_drvdata(pdev, NULL);
 
+	if (dev < 0) {
+		dev_warn(&pdev->dev,
+			 "Invalid card index %d, using default 0\n", dev);
+		dev = 0;
+	}
+
 	if (dev >= SNDRV_CARDS)
 		return -ENODEV;
 	if (!enable[dev]) 
author maintainer
#505 lore raw

PATCH 494/552 ALSA: serial-u16550: Check card index validity at probe

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Takashi Iwai <[email protected]>

commit e0fb960b227fcdebe22e4f26c9486d60943c0424 upstream.

serial-u16550 driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.

Add a sanity check for the card index and warn/correct it if it's a value out of the range.

---
 sound/drivers/serial-u16550.c |    6 ++++++
 1 file changed, 6 insertions(+)
sound/drivers/serial-u16550.c +6 -0
@@ -846,6 +846,12 @@ static int snd_serial_probe(struct platf
 	int err;
 	int dev = devptr->id;
 
+	if (dev < 0 || dev >= SNDRV_CARDS) {
+		dev_warn(&devptr->dev,
+			 "Invalid card index %d, using default 0\n", dev);
+		dev = 0;
+	}
+
 	switch (adaptor[dev]) {
 	case SNDRV_SERIAL_SOUNDCANVAS:
 		ins[dev] = 1;
author maintainer
#506 lore raw

PATCH 495/552 ALSA: virmidi: Check card index validity at probe

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Takashi Iwai <[email protected]>

commit b65d5182ecd6b7a24a83d980a0d06e809ef876c5 upstream.

virmidi driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.

Add a sanity check for the card index and warn/correct it if it's a value out of the range.

---
 sound/drivers/virmidi.c |    6 ++++++
 1 file changed, 6 insertions(+)
sound/drivers/virmidi.c +6 -0
@@ -75,6 +75,12 @@ static int snd_virmidi_probe(struct plat
 	int idx, err;
 	int dev = devptr->id;
 
+	if (dev < 0 || dev >= SNDRV_CARDS) {
+		dev_warn(&devptr->dev,
+			 "Invalid card index %d, using default 0\n", dev);
+		dev = 0;
+	}
+
 	err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
 				sizeof(struct snd_card_virmidi), &card);
 	if (err < 0)
author maintainer
#507 lore raw

PATCH 496/552 ALSA: hda/realtek: Add quirk for TongFang XxAF5xxx

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eckhart Mohr <[email protected]>

commit e72d5659a2606056a0c34af212b46a3275a55bbf upstream.

Fix microphone detection on built in headphone jack for some devices

---
 sound/hda/codecs/realtek/alc269.c |    1 +
 1 file changed, 1 insertion(+)
sound/hda/codecs/realtek/alc269.c +1 -0
@@ -7563,6 +7563,7 @@ static const struct hda_quirk alc269_fix
 	SND_PCI_QUIRK(0x1d05, 0x3031, "TongFang X6AR55xU", ALC2XX_FIXUP_HEADSET_MIC),
 	SND_PCI_QUIRK(0x1d05, 0x3033, "TongFang X6SP45xU", ALC2XX_FIXUP_HEADSET_MIC),
 	SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6KK45xU", ALC2XX_FIXUP_HEADSET_MIC),
+	SND_PCI_QUIRK(0x1d05, 0x30ba, "TongFang XxAF5xxx", ALC2XX_FIXUP_HEADSET_MIC),
 	SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
 	SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
 	SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE),
author maintainer
#508 lore raw

PATCH 497/552 ALSA: hda/realtek: Enable micmute LED on HP EliteBook 6 G1a p/n: AD3Q9ET#UUG

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Denis Batishchev <[email protected]>

commit e7da28b820d12927de30abf554c727a319f80359 upstream.

The HP EliteBook 6 G1a (SSID 103c:8e0d) uses a Realtek ALC236 codec. Without a quirk no fixup is selected and the mic-mute LED stays off. It needs the same ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF quirk as the already-supported 14" variant (SSID 103c:8dfb), so add it.

---
 sound/hda/codecs/realtek/alc269.c |    1 +
 1 file changed, 1 insertion(+)
sound/hda/codecs/realtek/alc269.c +1 -0
@@ -7014,6 +7014,7 @@ static const struct hda_quirk alc269_fix
 	SND_PCI_QUIRK(0x103c, 0x8dfc, "HP EliteBook 645 G12", ALC236_FIXUP_HP_GPIO_LED),
 	SND_PCI_QUIRK(0x103c, 0x8dfd, "HP EliteBook 6 G1a 16", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
 	SND_PCI_QUIRK(0x103c, 0x8dfe, "HP EliteBook 665 G12", ALC236_FIXUP_HP_GPIO_LED),
+	SND_PCI_QUIRK(0x103c, 0x8e0d, "HP EliteBook 6 G1a 14 (AD3Q9ET#UUG)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
 	SND_PCI_QUIRK(0x103c, 0x8e11, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
 	SND_PCI_QUIRK(0x103c, 0x8e12, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
 	SND_PCI_QUIRK(0x103c, 0x8e13, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
author maintainer
#509 lore raw

PATCH 498/552 ALSA: hda/realtek: Fix Lenovo Yoga Slim 7 14AKP10 quirk ordering

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Zhang Heng <[email protected]>

commit 75dc2eda659f6be4a370734f11baf25df8a9fd80 upstream.

The Yoga Slim 7 14AKP10 has a PCI SSID of 17aa:38b4 but a codec SSID of 17aa:391a. The current quirk table contains a PCI quirk for 17aa:38b4 (for the Legion Slim 7 16IRH8) which matches first, so the codec-specific quirk for 17aa:391a is never applied.

This results in the wrong fixup being used (CS35L41_I2C_2 instead of the correct bass speaker fixup), leaving the internal speakers misconfigured or silent.

Remove the 17aa:391a entry from its PCI-SSID-sorted position and add it as an HDA_CODEC_QUIRK directly before the 17aa:38b4 entry, because it must match on the codec subsystem ID rather than the PCI SSID and it has to win over the colliding PCI quirk for the Legion Slim 7 16IRH8. A comment is added to explain the out-of-order placement, following the same style already used for the 17aa:38bb and 17aa:38f9 codec-SSID overrides.

With this change, the correct ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN is applied, restoring speaker output and auto-mute functionality.

The original quirk added in commit e6c888202297 ("ALSA: hda/realtek: Add quirk for Lenovo Yoga Slim 7 14AKP10") matched on the PCI SSID 17aa:391a, but this model actually exposes PCI SSID 17aa:38b4 (shared with the Legion Slim 7 16IRH8), so that quirk never matched and the bass speaker remained silent. Fix it by matching on the codec SSID and placing the entry before the colliding 17aa:38b4 PCI quirk.

---
 sound/hda/codecs/realtek/alc269.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
sound/hda/codecs/realtek/alc269.c +4 -1
@@ -7465,6 +7465,10 @@ static const struct hda_quirk alc269_fix
 	SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C),
 	SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
 	SND_PCI_QUIRK(0x17aa, 0x38ab, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+	/* Lenovo Yoga Slim 7 14AKP10 shares PCI SSID 17aa:38b4 with Legion Slim 7
+	 * 16IRH8; use codec SSID to distinguish them
+	 */
+	HDA_CODEC_QUIRK(0x17aa, 0x391a, "Lenovo Yoga Slim 7 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
 	SND_PCI_QUIRK(0x17aa, 0x38b4, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2),
 	HDA_CODEC_QUIRK(0x17aa, 0x391c, "Lenovo Yoga 7 2-in-1 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
 	HDA_CODEC_QUIRK(0x17aa, 0x391d, "Lenovo Yoga 7 2-in-1 16AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
@@ -7499,7 +7503,6 @@ static const struct hda_quirk alc269_fix
 	SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
 	SND_PCI_QUIRK(0x17aa, 0x3911, "Lenovo Yoga Pro 7 14IAH10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
 	SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC),
-	SND_PCI_QUIRK(0x17aa, 0x391a, "Lenovo Yoga Slim 7 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
 	SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C),
 	SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C),
 	SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
author maintainer
#510 lore raw

PATCH 499/552 ring-buffer: Fix subbuf resize race with ring buffer readers

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Vincent Donnefort <[email protected]>

[ Upstream commit 8a5f63637890f03177146efddaba5ec7a1b4d61f ]

trace_buffer subbuf_size is read lockless in ring_buffer_read_page() and ring_buffer_read_start(), while it can simultaneously be resized with ring_buffer_subbuf_order_set().

Instead of trace_buffer::subbuf_size, use bpage::order in ring_buffer_read_start() and ring_buffer_read_page().

In ring_buffer_read_start(), even with resize_disabled, there is still a possibility of a race with a buffer modification. Hold the trace_buffer mutex to synchronise with any pending ring buffer order modification.

trace_buffer::subbuf_size is now actually useless, remove it. Also, create accessors rb_subbuf_capacity() and rb_page_capacity() which return the actual size available for storing events, while rb_subbuf_size() returns the actual subbuf page-size.

---
 kernel/trace/ring_buffer.c | 167 +++++++++++++++++++++++--------------
 1 file changed, 106 insertions(+), 61 deletions(-)
kernel/trace/ring_buffer.c +106 -61
index c0ca860c9b5b9..5936389188bd7 100644
@@ -396,6 +396,17 @@ static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage)
 	return local_read(&bpage->page->commit);
 }
 
+/**
+ * rb_page_capacity - Get the capacity of a buffer page
+ * @bpage:	The buffer page
+ *
+ * Return: The maximum size available for events in the given buffer page.
+ */
+static __always_inline unsigned int rb_page_capacity(struct buffer_page *bpage)
+{
+	return (PAGE_SIZE << bpage->order) - BUF_PAGE_HDR_SIZE;
+}
+
 static void free_buffer_page(struct buffer_page *bpage)
 {
 	/* Range pages are not to be freed */
@@ -595,11 +606,42 @@ struct trace_buffer {
 
 	struct ring_buffer_meta		*meta;
 
-	unsigned int			subbuf_size;
 	unsigned int			subbuf_order;
 	unsigned int			max_data_size;
 };
 
+static __always_inline unsigned int rb_subbuf_size(struct trace_buffer *buffer)
+{
+	return PAGE_SIZE << buffer->subbuf_order;
+}
+
+/**
+ * rb_subbuf_capacity - Get the capacity of a subbuffer
+ * @buffer:	A trace buffer
+ *
+ * Unsafe to use without holding trace_buffer::mutex or with resizing enabled.
+ * Consider rb_page_capacity() instead.
+ *
+ * Return: The maximum size available for events in a trace buffer subbuffer.
+ */
+static __always_inline unsigned int rb_subbuf_capacity(struct trace_buffer *buffer)
+{
+	return rb_subbuf_size(buffer) - BUF_PAGE_HDR_SIZE;
+}
+
+/**
+ * rb_subbuf_start - Get the start address of a subbuffer
+ * @buffer:	A trace buffer
+ * @addr:	An address of an event on a subbuffer
+ *
+ * Return: The start of the subbuffer for where @addr sits
+ */
+static __always_inline
+unsigned long rb_subbuf_start(struct trace_buffer *buffer, unsigned long addr)
+{
+	return addr & ~((unsigned long)(rb_subbuf_size(buffer) - 1));
+}
+
 struct ring_buffer_iter {
 	struct ring_buffer_per_cpu	*cpu_buffer;
 	unsigned long			head;
@@ -639,7 +681,7 @@ int ring_buffer_print_page_header(struct trace_buffer *buffer, struct trace_seq
 	trace_seq_printf(s, "\tfield: char data;\t"
 			 "offset:%u;\tsize:%u;\tsigned:%u;\n",
 			 (unsigned int)offsetof(typeof(field), data),
-			 (unsigned int)buffer->subbuf_size,
+			 (unsigned int)rb_subbuf_capacity(buffer),
 			 (unsigned int)is_signed_type(char));
 
 	return !trace_seq_has_overflowed(s);
@@ -1628,7 +1670,7 @@ rb_range_align_subbuf(unsigned long addr, int subbuf_size, int nr_subbufs)
  */
 static void *rb_range_meta(struct trace_buffer *buffer, int nr_pages, int cpu)
 {
-	int subbuf_size = buffer->subbuf_size + BUF_PAGE_HDR_SIZE;
+	int subbuf_size = rb_subbuf_size(buffer);
 	struct ring_buffer_cpu_meta *meta;
 	struct ring_buffer_meta *bmeta;
 	unsigned long ptr;
@@ -2302,8 +2344,8 @@ static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
 			bpage->range = 1;
 			bpage->id = i + 1;
 		} else {
-			int order = cpu_buffer->buffer->subbuf_order;
-			bpage->page = alloc_cpu_data(cpu_buffer->cpu, order);
+			bpage->page = alloc_cpu_data(cpu_buffer->cpu,
+						     cpu_buffer->buffer->subbuf_order);
 			if (!bpage->page)
 				goto free_pages;
 		}
@@ -2402,8 +2444,7 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu)
 
 		atomic_inc(&cpu_buffer->resize_disabled);
 	} else {
-		int order = cpu_buffer->buffer->subbuf_order;
-		bpage->page = alloc_cpu_data(cpu, order);
+		bpage->page = alloc_cpu_data(cpu, bpage->order);
 		if (!bpage->page)
 			goto fail_free_reader;
 	}
@@ -2512,10 +2553,9 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
 
 	buffer->subbuf_order = order;
 	subbuf_size = (PAGE_SIZE << order);
-	buffer->subbuf_size = subbuf_size - BUF_PAGE_HDR_SIZE;
 
 	/* Max payload is buffer page size - header (8bytes) */
-	buffer->max_data_size = buffer->subbuf_size - (sizeof(u32) * 2);
+	buffer->max_data_size = rb_subbuf_capacity(buffer) - (sizeof(u32) * 2);
 
 	buffer->flags = flags;
 	buffer->clock = trace_clock_local;
@@ -2588,9 +2628,8 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
 
 		rb_range_meta_init(buffer, nr_pages, scratch_size);
 	} else {
-
 		/* need at least two pages */
-		nr_pages = DIV_ROUND_UP(size, buffer->subbuf_size);
+		nr_pages = DIV_ROUND_UP(size, rb_subbuf_capacity(buffer));
 		if (nr_pages < 2)
 			nr_pages = 2;
 	}
@@ -2963,7 +3002,7 @@ static void update_pages_handler(struct work_struct *work)
  * @size: the new size.
  * @cpu_id: the cpu buffer to resize
  *
- * Minimum size is 2 * buffer->subbuf_size.
+ * Minimum size is 2 * rb_subbuf_capacity(buffer).
  *
  * Returns 0 on success and < 0 on failure.
  */
@@ -2985,12 +3024,6 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size,
 	    !cpumask_test_cpu(cpu_id, buffer->cpumask))
 		return 0;
 
-	nr_pages = DIV_ROUND_UP(size, buffer->subbuf_size);
-
-	/* we need a minimum of two pages */
-	if (nr_pages < 2)
-		nr_pages = 2;
-
 	/*
 	 * Keep CPUs from coming online while resizing to synchronize
 	 * with new per CPU buffers being created.
@@ -3001,6 +3034,12 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size,
 	mutex_lock(&buffer->mutex);
 	atomic_inc(&buffer->resizing);
 
+	nr_pages = DIV_ROUND_UP(size, rb_subbuf_capacity(buffer));
+
+	/* we need a minimum of two pages */
+	if (nr_pages < 2)
+		nr_pages = 2;
+
 	if (cpu_id == RING_BUFFER_ALL_CPUS) {
 		/*
 		 * Don't succeed if resizing is disabled, as a reader might be
@@ -3279,7 +3318,7 @@ rb_event_index(struct ring_buffer_per_cpu *cpu_buffer, struct ring_buffer_event
 {
 	unsigned long addr = (unsigned long)event;
 
-	addr &= (PAGE_SIZE << cpu_buffer->buffer->subbuf_order) - 1;
+	addr &= (unsigned long)rb_subbuf_size(cpu_buffer->buffer) - 1;
 
 	return addr - BUF_PAGE_HDR_SIZE;
 }
@@ -3518,8 +3557,8 @@ static inline void
 rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer,
 	      unsigned long tail, struct rb_event_info *info)
 {
-	unsigned long bsize = READ_ONCE(cpu_buffer->buffer->subbuf_size);
 	struct buffer_page *tail_page = info->tail_page;
+	unsigned long bsize = rb_page_capacity(tail_page);
 	struct ring_buffer_event *event;
 	unsigned long length = info->length;
 
@@ -3871,8 +3910,7 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer,
 
 	new_index = rb_event_index(cpu_buffer, event);
 	old_index = new_index + rb_event_ts_length(event);
-	addr = (unsigned long)event;
-	addr &= ~((PAGE_SIZE << cpu_buffer->buffer->subbuf_order) - 1);
+	addr = rb_subbuf_start(cpu_buffer->buffer, (unsigned long)event);
 
 	bpage = READ_ONCE(cpu_buffer->tail_page);
 
@@ -4519,7 +4557,7 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer,
 	tail = write - info->length;
 
 	/* See if we shot pass the end of this buffer page */
-	if (unlikely(write > cpu_buffer->buffer->subbuf_size)) {
+	if (unlikely(write > rb_page_capacity(tail_page))) {
 		check_buffer(cpu_buffer, info, CHECK_FULL_PAGE);
 		return rb_move_tail(cpu_buffer, tail, info);
 	}
@@ -4764,7 +4802,7 @@ rb_decrement_entry(struct ring_buffer_per_cpu *cpu_buffer,
 	struct buffer_page *bpage = cpu_buffer->commit_page;
 	struct buffer_page *start;
 
-	addr &= ~((PAGE_SIZE << cpu_buffer->buffer->subbuf_order) - 1);
+	addr = rb_subbuf_start(cpu_buffer->buffer, addr);
 
 	/* Do the likely case first */
 	if (likely(bpage->page == (void *)addr)) {
@@ -5441,7 +5479,6 @@ static struct buffer_page *
 rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
 {
 	struct buffer_page *reader = NULL;
-	unsigned long bsize = READ_ONCE(cpu_buffer->buffer->subbuf_size);
 	unsigned long overwrite;
 	unsigned long flags;
 	int nr_loops = 0;
@@ -5580,7 +5617,7 @@ rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
 #define USECS_WAIT	1000000
         for (nr_loops = 0; nr_loops < USECS_WAIT; nr_loops++) {
 		/* If the write is past the end of page, a writer is still updating it */
-		if (likely(!reader || rb_page_write(reader) <= bsize))
+		if (likely(!reader || rb_page_write(reader) <= rb_page_capacity(reader)))
 			break;
 
 		udelay(1);
@@ -6002,36 +6039,44 @@ EXPORT_SYMBOL_GPL(ring_buffer_consume);
 struct ring_buffer_iter *
 ring_buffer_read_start(struct trace_buffer *buffer, int cpu, gfp_t flags)
 {
+	struct ring_buffer_iter *iter __free(kfree) = kzalloc_obj(*iter, flags);
 	struct ring_buffer_per_cpu *cpu_buffer;
-	struct ring_buffer_iter *iter;
-
-	if (!cpumask_test_cpu(cpu, buffer->cpumask))
-		return NULL;
 
-	iter = kzalloc(sizeof(*iter), flags);
 	if (!iter)
 		return NULL;
 
-	/* Holds the entire event: data and meta data */
-	iter->event_size = buffer->subbuf_size;
-	iter->event = kmalloc(iter->event_size, flags);
-	if (!iter->event) {
-		kfree(iter);
+	if (!cpumask_test_cpu(cpu, buffer->cpumask))
 		return NULL;
-	}
 
 	cpu_buffer = buffer->buffers[cpu];
 
-	iter->cpu_buffer = cpu_buffer;
+	/*
+	 * Only KDB is using GFP_ATOMIC, for the others, lock the buffer to
+	 * prevent concurrent resizing.
+	 */
+	if (gfpflags_allow_blocking(flags))
+		mutex_lock(&buffer->mutex);
 
 	atomic_inc(&cpu_buffer->resize_disabled);
 
+	if (gfpflags_allow_blocking(flags))
+		mutex_unlock(&buffer->mutex);
+
+	/* Holds the entire event: data and meta data. */
+	iter->event_size = rb_page_capacity(READ_ONCE(cpu_buffer->reader_page));
+	iter->event = kmalloc(iter->event_size, flags);
+	if (!iter->event) {
+		atomic_dec(&cpu_buffer->resize_disabled);
+		return NULL;
+	}
+	iter->cpu_buffer = cpu_buffer;
+
 	guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock);
 	arch_spin_lock(&cpu_buffer->lock);
 	rb_iter_reset(iter);
 	arch_spin_unlock(&cpu_buffer->lock);
 
-	return iter;
+	return_ptr(iter);
 }
 EXPORT_SYMBOL_GPL(ring_buffer_read_start);
 
@@ -6085,7 +6130,7 @@ unsigned long ring_buffer_size(struct trace_buffer *buffer, int cpu)
 	if (!cpumask_test_cpu(cpu, buffer->cpumask))
 		return 0;
 
-	return buffer->subbuf_size * buffer->buffers[cpu]->nr_pages;
+	return rb_subbuf_capacity(buffer) * buffer->buffers[cpu]->nr_pages;
 }
 EXPORT_SYMBOL_GPL(ring_buffer_size);
 
@@ -6655,15 +6700,15 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
 	if (!data_page || !data_page->data)
 		return -1;
 
-	if (data_page->order != buffer->subbuf_order)
-		return -1;
-
 	bpage = data_page->data;
 	if (!bpage)
 		return -1;
 
 	guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock);
 
+	if (data_page->order != cpu_buffer->reader_page->order)
+		return -1;
+
 	reader = rb_get_reader_page(cpu_buffer);
 	if (!reader)
 		return -1;
@@ -6778,7 +6823,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
 		/* If there is room at the end of the page to save the
 		 * missed events, then record it there.
 		 */
-		if (buffer->subbuf_size - commit >= sizeof(missed_events)) {
+		if (rb_page_capacity(reader) - commit >= sizeof(missed_events)) {
 			memcpy(&bpage->data[commit], &missed_events,
 			       sizeof(missed_events));
 			local_add(RB_MISSED_STORED, &bpage->commit);
@@ -6790,8 +6835,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
 	/*
 	 * This page may be off to user land. Zero it out here.
 	 */
-	if (commit < buffer->subbuf_size)
-		memset(&bpage->data[commit], 0, buffer->subbuf_size - commit);
+	if (commit < rb_page_capacity(reader))
+		memset(&bpage->data[commit], 0, rb_page_capacity(reader) - commit);
 
 	return read;
 }
@@ -6817,7 +6862,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_read_page_data);
  */
 int ring_buffer_subbuf_size_get(struct trace_buffer *buffer)
 {
-	return buffer->subbuf_size + BUF_PAGE_HDR_SIZE;
+	return rb_subbuf_size(buffer);
 }
 EXPORT_SYMBOL_GPL(ring_buffer_subbuf_size_get);
 
@@ -6862,7 +6907,8 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
 {
 	struct ring_buffer_per_cpu *cpu_buffer;
 	struct buffer_page *bpage, *tmp;
-	int old_order, old_size;
+	unsigned int old_capacity;
+	int old_order;
 	int nr_pages;
 	int psize;
 	int err;
@@ -6871,9 +6917,6 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
 	if (!buffer || order < 0)
 		return -EINVAL;
 
-	if (buffer->subbuf_order == order)
-		return 0;
-
 	psize = (1 << order) * PAGE_SIZE;
 	if (psize <= BUF_PAGE_HDR_SIZE)
 		return -EINVAL;
@@ -6882,18 +6925,21 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
 	if (psize > RB_WRITE_MASK + 1)
 		return -EINVAL;
 
-	old_order = buffer->subbuf_order;
-	old_size = buffer->subbuf_size;
-
 	/* prevent another thread from changing buffer sizes */
 	guard(mutex)(&buffer->mutex);
+
+	old_order = buffer->subbuf_order;
+	if (old_order == order)
+		return 0;
+
+	old_capacity = rb_subbuf_capacity(buffer);
+
 	atomic_inc(&buffer->record_disabled);
 
 	/* Make sure all commits have finished */
 	synchronize_rcu();
 
 	buffer->subbuf_order = order;
-	buffer->subbuf_size = psize - BUF_PAGE_HDR_SIZE;
 
 	/* Make sure all new buffers are allocated, before deleting the old ones */
 	for_each_buffer_cpu(buffer, cpu) {
@@ -6909,8 +6955,8 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
 		}
 
 		/* Update the number of pages to match the new size */
-		nr_pages = old_size * buffer->buffers[cpu]->nr_pages;
-		nr_pages = DIV_ROUND_UP(nr_pages, buffer->subbuf_size);
+		nr_pages = old_capacity * buffer->buffers[cpu]->nr_pages;
+		nr_pages = DIV_ROUND_UP(nr_pages, rb_subbuf_capacity(buffer));
 
 		/* we need a minimum of two pages */
 		if (nr_pages < 2)
@@ -6998,7 +7044,6 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
 
 error:
 	buffer->subbuf_order = old_order;
-	buffer->subbuf_size = old_size;
 
 	atomic_dec(&buffer->record_disabled);
 
@@ -7076,7 +7121,7 @@ static void rb_setup_ids_meta_page(struct ring_buffer_per_cpu *cpu_buffer,
 
 	meta->meta_struct_len = sizeof(*meta);
 	meta->nr_subbufs = nr_subbufs;
-	meta->subbuf_size = cpu_buffer->buffer->subbuf_size + BUF_PAGE_HDR_SIZE;
+	meta->subbuf_size = rb_subbuf_size(cpu_buffer->buffer);
 	meta->meta_page_size = meta->subbuf_size;
 
 	rb_update_meta_page(cpu_buffer);
@@ -7438,7 +7483,7 @@ int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu)
 			 * missed events, then record it there.
 			 */
 			commit = rb_page_size(reader);
-			if (buffer->subbuf_size - commit >= sizeof(missed_events)) {
+			if (rb_subbuf_capacity(buffer) - commit >= sizeof(missed_events)) {
 				memcpy(&bpage->data[commit], &missed_events,
 				       sizeof(missed_events));
 				local_add(RB_MISSED_STORED, &bpage->commit);
@@ -7470,7 +7515,7 @@ int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu)
 out:
 	/* Some archs do not have data cache coherency between kernel and user-space */
 	flush_kernel_vmap_range(cpu_buffer->reader_page->page,
-				buffer->subbuf_size + BUF_PAGE_HDR_SIZE);
+				rb_subbuf_size(buffer));
 
 	rb_update_meta_page(cpu_buffer);
 
Signature · 1 line
2.53.0
author maintainer
#511 lore raw

PATCH 500/552 ovpn: run deferred work on a module-owned workqueue

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Ralf Lici <[email protected]>

commit e9714db8041763f59dde152c812b96b3de05c6d9 upstream.

ovpn queues several work items whose callbacks execute module text. These works currently run on the global system workqueues, so module exit has no driver-owned drain point that guarantees the callbacks have fully returned before the module text can be freed.

Object references protect the objects used by the callbacks, but they do not prove that a workqueue function has returned. In particular, a worker can drop the final reference that unblocks device teardown while it is still executing ovpn code.

Add a module-owned workqueue and queue all ovpn work items on it. During module exit, unregister rtnl and netlink first, flush the workqueue so ordinary ovpn workers finish, run the final RCU barrier, and destroy the workqueue last. This keeps the workqueue available for cleanup work queued from RCU callbacks, while ensuring no ovpn work item can outlive the module text.

The per-device delayed keepalive work remains explicitly disabled during netdev teardown (disable_delayed_work_sync in ndo_uninit), since flush_workqueue does not flush delayed work that is still only pending on its timer.

Fixes 3ecfd9349f40 ("ovpn: implement keepalive mechanism")
Fixes 11851cbd60ea ("ovpn: implement TCP transport")
---
 drivers/net/ovpn/main.c     | 19 ++++++++++++++++++-
 drivers/net/ovpn/ovpnpriv.h |  4 ++++
 drivers/net/ovpn/peer.c     |  8 ++++----
 drivers/net/ovpn/tcp.c      |  9 ++++-----
 4 files changed, 30 insertions(+), 10 deletions(-)
drivers/net/ovpn/main.c +18 -1
index 0262926449f2e..c18ed8690baff 100644
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/inetdevice.h>
+#include <linux/workqueue.h>
 #include <net/gro_cells.h>
 #include <net/ip.h>
 #include <net/rtnetlink.h>
@@ -26,6 +27,9 @@
 #include "tcp.h"
 #include "udp.h"
 
+/* module-owned workqueue on which all ovpn-specific work is queued */
+struct workqueue_struct *ovpn_wq;
+
 static void ovpn_priv_free(struct net_device *net)
 {
 	struct ovpn_priv *ovpn = netdev_priv(net);
@@ -264,10 +268,16 @@ static int __init ovpn_init(void)
 
 	ovpn_tcp_init();
 
+	ovpn_wq = alloc_workqueue("ovpn", WQ_PERCPU, 0);
+	if (!ovpn_wq) {
+		pr_err("ovpn: cannot allocate workqueue\n");
+		return -ENOMEM;
+	}
+
 	err = rtnl_link_register(&ovpn_link_ops);
 	if (err) {
 		pr_err("ovpn: can't register rtnl link ops: %d\n", err);
-		return err;
+		goto destroy_wq;
 	}
 
 	err = ovpn_nl_register();
@@ -280,6 +290,9 @@ static int __init ovpn_init(void)
 
 unreg_rtnl:
 	rtnl_link_unregister(&ovpn_link_ops);
+destroy_wq:
+	destroy_workqueue(ovpn_wq);
+	ovpn_wq = NULL;
 	return err;
 }
 
@@ -288,7 +301,11 @@ static __exit void ovpn_cleanup(void)
 	ovpn_nl_unregister();
 	rtnl_link_unregister(&ovpn_link_ops);
 
+	flush_workqueue(ovpn_wq);
 	rcu_barrier();
+
+	destroy_workqueue(ovpn_wq);
+	ovpn_wq = NULL;
 }
 
 module_init(ovpn_init);
drivers/net/ovpn/ovpnpriv.h +4 -0
index 5898f6adada7f..84499140e4bd9 100644
@@ -15,6 +15,10 @@
 #include <uapi/linux/if_link.h>
 #include <uapi/linux/ovpn.h>
 
+struct workqueue_struct;
+
+extern struct workqueue_struct *ovpn_wq;
+
 /**
  * struct ovpn_peer_collection - container of peers for MultiPeer mode
  * @by_id: table of peers index by ID
drivers/net/ovpn/peer.c +4 -4
index 412ef09abd5e7..6b9f5f12fc5a8 100644
@@ -62,7 +62,7 @@ void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout)
 	/* now that interval and timeout have been changed, kick
 	 * off the worker so that the next delay can be recomputed
 	 */
-	mod_delayed_work(system_wq, &peer->ovpn->keepalive_work, 0);
+	mod_delayed_work(ovpn_wq, &peer->ovpn->keepalive_work, 0);
 }
 
 /**
@@ -1366,7 +1366,7 @@ static time64_t ovpn_peer_keepalive_work_single(struct ovpn_peer *peer,
 			   peer->id);
 		if (WARN_ON(!ovpn_peer_hold(peer)))
 			return 0;
-		if (!schedule_work(&peer->keepalive_work))
+		if (!queue_work(ovpn_wq, &peer->keepalive_work))
 			ovpn_peer_put(peer);
 	}
 
@@ -1458,8 +1458,8 @@ void ovpn_peer_keepalive_work(struct work_struct *work)
 		netdev_dbg(ovpn->dev,
 			   "scheduling keepalive work: now=%llu next_run=%llu delta=%llu\n",
 			   next_run, now, next_run - now);
-		schedule_delayed_work(&ovpn->keepalive_work,
-				      (next_run - now) * HZ);
+		queue_delayed_work(ovpn_wq, &ovpn->keepalive_work,
+				   (next_run - now) * HZ);
 	}
 	unlock_ovpn(ovpn, &release_list);
 }
drivers/net/ovpn/tcp.c +4 -5
index 0af14055c39aa..8fe8a8e750a40 100644
@@ -151,7 +151,7 @@ static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb)
 	/* take reference for deferred peer deletion. should never fail */
 	if (WARN_ON(!ovpn_peer_hold(peer)))
 		goto err_nopeer;
-	if (!schedule_work(&peer->tcp.defer_del_work))
+	if (!queue_work(ovpn_wq, &peer->tcp.defer_del_work))
 		ovpn_peer_put(peer);
 	ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
 err_nopeer:
@@ -284,13 +284,12 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk)
 			 * stream therefore we abort the connection
 			 */
 			ovpn_peer_hold(peer);
-			if (!schedule_work(&peer->tcp.defer_del_work))
+			if (!queue_work(ovpn_wq, &peer->tcp.defer_del_work))
 				ovpn_peer_put(peer);
 
 			/* we bail out immediately and keep tx_in_progress set
 			 * to true. This way we prevent more TX attempts
-			 * which would lead to more invocations of
-			 * schedule_work()
+			 * which would lead to more invocations of queue_work()
 			 */
 			return;
 		}
@@ -487,7 +486,7 @@ static void ovpn_tcp_write_space(struct sock *sk)
 	rcu_read_lock();
 	sock = rcu_dereference_sk_user_data(sk);
 	if (likely(sock && sock->peer)) {
-		schedule_work(&sock->tcp_tx_work);
+		queue_work(ovpn_wq, &sock->tcp_tx_work);
 		sock->peer->tcp.sk_cb.sk_write_space(sk);
 	}
 	rcu_read_unlock();
Signature · 1 line
2.53.0
author maintainer
#512 lore raw

PATCH 501/552 rust: rust_is_available: warn for `bindgen` < 0.72.1 && libclang >= 22

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Miguel Ojeda <[email protected]>

[ Upstream commit dc01dfb37b34beeefcfe1c3055364d41a4070c7e ]

Starting with LLVM 22, clang_getTypeDeclaration() may return a forward declaration instead of the type definition. This made bindgen generate opaque types [1][2], which in turn made us fail with e.g.

    error[E0609]: no field `__bindgen_anon_1` on type `bindings::kernel_param`
      --> rust/kernel/module_param.rs:78:46
       |
    78 |         let container = unsafe { &*((*param).__bindgen_anon_1.arg.cast::<SetOnce<T>>()) };
       |                                              ^^^^^^^^^^^^^^^^ unknown field
       |
       = note: available field is: `_address`

This was fixed in bindgen 0.72.1 [3].

In order to clarify what is going on and avoid confusion [4][5], add a warning to rust_is_available.sh about it when the versions match, similar to past warnings like the one removed in:

  commit ae64324ad5c1 ("rust: rust_is_available: remove warning for `bindgen` < 0.69.5 && libclang >= 19.1")

In addition, even if the versions match, check if the issue appears to not reproduce with the given binaries, to avoid a warning in such a case.

Finally, include tests.

[ Nathan, in parallel, updated the instructions of the LLVM+Rust
kernel.org toolchains [6] so that --version is not passed to cargo for bindgen, and thus the latest bindgen is installed by default, which should help to avoid some of these situations.

Thanks!

- Miguel ]

---
 scripts/rust_is_available.sh                  | 14 +++++++++
 .../rust_is_available_bindgen_libclang_22.h   |  5 ++++
 scripts/rust_is_available_test.py             | 30 ++++++++++++++++++-
 3 files changed, 48 insertions(+), 1 deletion(-)
 create mode 100644 scripts/rust_is_available_bindgen_libclang_22.h
scripts/rust_is_available.sh +14 -0
index d2323de0692c1..82be88d44c184 100755
@@ -242,6 +242,20 @@ if [ "$bindgen_libclang_cversion" -ge 1900100 ] &&
 	fi
 fi
 
+if [ "$bindgen_libclang_cversion" -ge 2200000 ] &&
+	[ "$rust_bindings_generator_cversion" -lt 7201 ]; then
+	# Distributions may have patched the issue.
+	if ! "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang_22.h | grep -q 'pub foo'; then
+		echo >&2 "***"
+		echo >&2 "*** Rust bindings generator '$BINDGEN' < 0.72.1 together with libclang >= 22"
+		echo >&2 "*** may not work due to a bug (https://github.com/rust-lang/rust-bindgen/pull/3278)."
+		echo >&2 "***   Your bindgen version:  $rust_bindings_generator_version"
+		echo >&2 "***   Your libclang version: $bindgen_libclang_version"
+		echo >&2 "***"
+		warning=1
+	fi
+fi
+
 # If the C compiler is Clang, then we can also check whether its version
 # matches the `libclang` version used by the Rust bindings generator.
 #
scripts/rust_is_available_bindgen_libclang_22.h new file +5 -0
new file mode 100644
index 0000000000000..6b33544c14a81
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+struct S;
+struct S {
+	int foo;
+};
scripts/rust_is_available_test.py +29 -1
index 4fcc319dea84e..a5fadef98132f 100755
@@ -54,7 +54,7 @@ else:
 """)
 
     @classmethod
-    def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False, libclang_concat_patched=False):
+    def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False, libclang_concat_patched=False, libclang_22_patched=False):
         if libclang_stderr is None:
             libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})"
         else:
@@ -70,6 +70,11 @@ else:
         else:
             libclang_concat_case = "pass"
 
+        if libclang_22_patched:
+            libclang_22_case = "print('pub foo: ::std::os::raw::c_int,')"
+        else:
+            libclang_22_case = "pass"
+
         return cls.generate_executable(f"""#!/usr/bin/env python3
 import sys
 if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv):
@@ -78,6 +83,8 @@ elif "rust_is_available_bindgen_0_66.h" in " ".join(sys.argv):
     {version_0_66_case}
 elif "rust_is_available_bindgen_libclang_concat.h" in " ".join(sys.argv):
     {libclang_concat_case}
+elif "rust_is_available_bindgen_libclang_22.h" in " ".join(sys.argv):
+    {libclang_22_case}
 else:
     print({repr(version_stdout)})
 """)
@@ -300,6 +307,27 @@ else:
                 bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_concat_patched=True)
                 result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc })
 
+    def test_bindgen_bad_libclang_22(self):
+        for (bindgen_version, libclang_version, expected_not_patched) in (
+            ("0.71.1", "21.1.0", self.Expected.SUCCESS),
+            ("0.71.1", "22.0.0", self.Expected.SUCCESS_WITH_WARNINGS),
+            ("0.71.1", "22.1.0", self.Expected.SUCCESS_WITH_WARNINGS),
+
+            ("0.72.0", "22.0.0", self.Expected.SUCCESS_WITH_WARNINGS),
+
+            ("0.72.1", "22.0.0", self.Expected.SUCCESS),
+        ):
+            with self.subTest(bindgen_version=bindgen_version, libclang_version=libclang_version):
+                cc = self.generate_clang(f"clang version {libclang_version}")
+                libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {libclang_version} [-W#pragma-messages], err: false"
+                bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr)
+                result = self.run_script(expected_not_patched, { "BINDGEN": bindgen, "CC": cc })
+                if expected_not_patched == self.Expected.SUCCESS_WITH_WARNINGS:
+                    self.assertIn(f"Rust bindings generator '{bindgen}' < 0.72.1 together with libclang >= 22", result.stderr)
+
+                bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_22_patched=True)
+                result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc })
+
     def test_clang_matches_bindgen_libclang_different_bindgen(self):
         bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 999.0.0 [-W#pragma-messages], err: false")
         result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })
Signature · 1 line
2.53.0
author maintainer
#513 lore raw

PATCH 502/552 net/smc: carry oversized SMC-Rv2 LLC messages in the queue entry

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Yehyeong Lee <[email protected]>

[ Upstream commit 8d3c1ab82c11d4fadebf817a825fd221b3e197ea ]

smc_llc_rmt_delete_rkey() and smc_llc_save_add_link_rkeys() read the part of a v2 message that does not fit into the 44-byte union smc_llc_msg, and both bound themselves by the size of the buffer it landed in, not by what arrived. On a link with a shared v2 receive buffer a 44-byte DELETE_RKEY_V2 declaring 255 rkeys reaches rkey[9..254] in whatever an earlier message left in lgr->wr_rx_buf_v2, and passes each of them to smc_rtoken_delete(). One of those 255 matched a registered rtoken and deleted it. An ADD_LINK on such a link installs up to 255 rtokens from the same bytes.

Copy the tail into the queue entry, so its length is the length of the message that arrived, and declare the rkeys that fit inline as a member of the union instead of reaching them through a cast. The same DELETE_RKEY_V2 now processes the 9 rkeys it carries. The copy is limited to the longest tail the two functions can read, so the peer does not pick the size of the entry.

The bound the previous patch placed on links without a shared v2 receive buffer is no longer needed.

Fixes 27ef6a9981fe ("net/smc: support SMC-R V2 for rdma devices with max_recv_sge equals to 1")
---
 net/smc/smc_llc.c | 125 ++++++++++++++++++++++++++++++++--------------
 net/smc/smc_wr.c  |   6 +--
 2 files changed, 91 insertions(+), 40 deletions(-)
net/smc/smc_llc.c +88 -37
index 53db1e0a05797..51937265203c8 100644
@@ -157,6 +157,7 @@ struct smc_llc_msg_confirm_rkey {	/* type 0x06 */
 };
 
 #define SMC_LLC_DEL_RKEY_MAX	8
+#define SMC_LLC_DEL_RKEY_V2_INLINE	9
 #define SMC_LLC_FLAG_RKEY_RETRY	0x10
 #define SMC_LLC_FLAG_RKEY_NEG	0x20
 
@@ -177,6 +178,15 @@ struct smc_llc_msg_delete_rkey_v2 {	/* type 0x29 */
 	__be32 rkey[];
 };
 
+/* the leading rkeys of a DELETE_RKEY_V2 fit into union smc_llc_msg */
+struct smc_llc_msg_delete_rkey_v2_inline {	/* type 0x29 */
+	struct smc_llc_hdr hd;
+	u8 num_rkeys;
+	u8 num_inval_rkeys;
+	u8 reserved[2];
+	__be32 rkey[SMC_LLC_DEL_RKEY_V2_INLINE];
+};
+
 union smc_llc_msg {
 	struct smc_llc_msg_confirm_link confirm_link;
 	struct smc_llc_msg_add_link add_link;
@@ -186,6 +196,7 @@ union smc_llc_msg {
 
 	struct smc_llc_msg_confirm_rkey confirm_rkey;
 	struct smc_llc_msg_delete_rkey delete_rkey;
+	struct smc_llc_msg_delete_rkey_v2_inline delete_rkey_v2;
 
 	struct smc_llc_msg_test_link test_link;
 	struct {
@@ -194,15 +205,25 @@ union smc_llc_msg {
 	} raw;
 };
 
+static_assert(SMC_LLC_DEL_RKEY_V2_INLINE ==
+	      (sizeof(union smc_llc_msg) -
+	       offsetof(struct smc_llc_msg_delete_rkey_v2, rkey)) /
+	      sizeof(__be32));
+static_assert(offsetof(struct smc_llc_msg_delete_rkey_v2_inline, rkey) ==
+	      offsetof(struct smc_llc_msg_delete_rkey_v2, rkey));
+
 #define SMC_LLC_FLAG_RESP		0x80
 
 struct smc_llc_qentry {
 	struct list_head list;
 	struct smc_link *link;
+	u16 body_len;
 	union smc_llc_msg msg;
+	u8 body[] __counted_by(body_len);
 };
 
-static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc);
+static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc,
+			    u32 byte_len);
 
 struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow)
 {
@@ -998,22 +1019,19 @@ static int smc_llc_cli_conf_link(struct smc_link *link,
 
 static void smc_llc_save_add_link_rkeys(struct smc_link *link,
 					struct smc_link *link_new,
-					u8 *llc_msg)
+					struct smc_llc_qentry *qentry)
 {
 	const u32 rt_off = offsetof(struct smc_llc_msg_add_link_v2_ext, rt);
 	struct smc_llc_msg_add_link_v2_ext *ext;
 	struct smc_link_group *lgr = link->lgr;
 	int max, i;
 
-	/* Without a shared v2 receive buffer the extension is not copied
-	 * into the queue entry, so not even ext->num_rkeys is there.
-	 */
-	if (!smc_link_shared_v2_rxbuf(link))
+	/* the rkey count itself is only there if enough bytes arrived */
+	if (qentry->body_len < rt_off)
 		return;
-	ext = (struct smc_llc_msg_add_link_v2_ext *)(llc_msg +
-						     SMC_WR_TX_SIZE);
+	ext = (struct smc_llc_msg_add_link_v2_ext *)qentry->body;
 	max = min_t(u8, ext->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2);
-	max = min_t(u32, max, (SMC_WR_BUF_V2_SIZE - SMC_WR_TX_SIZE - rt_off) /
+	max = min_t(u32, max, (qentry->body_len - rt_off) /
 			      sizeof(ext->rt[0]));
 	down_write(&lgr->rmbs_lock);
 	for (i = 0; i < max; i++) {
@@ -1107,9 +1125,7 @@ int smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry)
 	if (rc)
 		goto out_clear_lnk;
 	if (lgr->smc_version == SMC_V2) {
-		u8 *llc_msg = smc_link_shared_v2_rxbuf(link) ?
-			(u8 *)lgr->wr_rx_buf_v2 : (u8 *)llc;
-		smc_llc_save_add_link_rkeys(link, lnk_new, llc_msg);
+		smc_llc_save_add_link_rkeys(link, lnk_new, qentry);
 	} else {
 		rc = smc_llc_cli_rkey_exchange(link, lnk_new);
 		if (rc) {
@@ -1510,9 +1526,7 @@ int smc_llc_srv_add_link(struct smc_link *link,
 	if (rc)
 		goto out_err;
 	if (lgr->smc_version == SMC_V2) {
-		u8 *llc_msg = smc_link_shared_v2_rxbuf(link) ?
-			(u8 *)lgr->wr_rx_buf_v2 : (u8 *)add_llc;
-		smc_llc_save_add_link_rkeys(link, link_new, llc_msg);
+		smc_llc_save_add_link_rkeys(link, link_new, qentry);
 	} else {
 		rc = smc_llc_srv_rkey_exchange(link, link_new);
 		if (rc)
@@ -1561,7 +1575,8 @@ void smc_llc_add_link_local(struct smc_link *link)
 	add_llc.hd.common.llc_type = SMC_LLC_ADD_LINK;
 	smc_llc_init_msg_hdr(&add_llc.hd, link->lgr, sizeof(add_llc));
 	/* no dev and port needed */
-	smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc);
+	smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc,
+			sizeof(union smc_llc_msg));
 }
 
 /* worker to process an add link message */
@@ -1597,7 +1612,8 @@ void smc_llc_srv_delete_link_local(struct smc_link *link, u8 del_link_id)
 	del_llc.link_num = del_link_id;
 	del_llc.reason = htonl(SMC_LLC_DEL_LOST_PATH);
 	del_llc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
-	smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc);
+	smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc,
+			sizeof(union smc_llc_msg));
 }
 
 static void smc_llc_process_cli_delete_link(struct smc_link_group *lgr)
@@ -1819,27 +1835,28 @@ static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr)
 	link = qentry->link;
 
 	if (lgr->smc_version == SMC_V2) {
-		const u32 rkey_off =
-			offsetof(struct smc_llc_msg_delete_rkey_v2, rkey);
-		struct smc_llc_msg_delete_rkey_v2 *llcv2;
-		u32 buf_len;
-
-		if (smc_link_shared_v2_rxbuf(link)) {
-			memcpy(lgr->wr_rx_buf_v2, llc, sizeof(*llc));
-			llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)lgr->wr_rx_buf_v2;
-			buf_len = SMC_WR_BUF_V2_SIZE;
-		} else {
-			llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)llc;
-			buf_len = sizeof(qentry->msg);
-		}
+		struct smc_llc_msg_delete_rkey_v2_inline *llcv2;
+
+		/* The leading SMC_LLC_DEL_RKEY_V2_INLINE rkeys are declared in
+		 * the message itself, any further ones were received into
+		 * qentry->body.
+		 */
+		llcv2 = &qentry->msg.delete_rkey_v2;
 		llcv2->num_inval_rkeys = 0;
 
 		max = min_t(u8, llcv2->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2);
-		/* bound by the buffer llcv2 points at */
-		max = min_t(u32, max, (buf_len - rkey_off) /
-				      sizeof(llcv2->rkey[0]));
+		max = min_t(u32, max, SMC_LLC_DEL_RKEY_V2_INLINE +
+				      qentry->body_len / sizeof(__be32));
 		for (i = 0; i < max; i++) {
-			if (smc_rtoken_delete(link, llcv2->rkey[i]))
+			__be32 rkey;
+
+			if (i < SMC_LLC_DEL_RKEY_V2_INLINE)
+				rkey = llcv2->rkey[i];
+			else
+				memcpy(&rkey, qentry->body +
+					      (i - SMC_LLC_DEL_RKEY_V2_INLINE) *
+					      sizeof(rkey), sizeof(rkey));
+			if (smc_rtoken_delete(link, rkey))
 				llcv2->num_inval_rkeys++;
 		}
 		memset(&llc->rkey[0], 0, sizeof(llc->rkey));
@@ -2081,18 +2098,52 @@ static void smc_llc_rx_response(struct smc_link *link,
 	wake_up(&link->lgr->llc_msg_waiter);
 }
 
-static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc)
+/* the longest tail either reader of qentry->body can use */
+static u32 smc_llc_max_body_len(union smc_llc_msg *llc)
+{
+	switch (llc->raw.hdr.common.llc_type) {
+	case SMC_LLC_ADD_LINK:
+		return offsetof(struct smc_llc_msg_add_link_v2_ext, rt) +
+		       SMC_LLC_RKEYS_PER_MSG_V2 *
+		       sizeof(struct smc_llc_msg_add_link_cont_rt);
+	case SMC_LLC_DELETE_RKEY:
+		return (SMC_LLC_RKEYS_PER_MSG_V2 -
+			SMC_LLC_DEL_RKEY_V2_INLINE) * sizeof(__be32);
+	default:
+		return 0;
+	}
+}
+
+static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc,
+			    u32 byte_len)
 {
 	struct smc_link_group *lgr = link->lgr;
 	struct smc_llc_qentry *qentry;
 	unsigned long flags;
+	u16 body_len = 0;
+
+	/* V2 messages can be longer than the inline union smc_llc_msg. Carry
+	 * the remainder in the qentry itself, so that its lifetime and its
+	 * length match the message the peer actually sent.
+	 */
+	if (lgr->smc_version == SMC_V2 && byte_len > SMC_WR_TX_SIZE)
+		body_len = min_t(u32, byte_len, SMC_WR_BUF_V2_SIZE) -
+			   SMC_WR_TX_SIZE;
+	body_len = min_t(u32, body_len, smc_llc_max_body_len(llc));
 
-	qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC);
+	qentry = kmalloc_flex(*qentry, body, body_len, GFP_ATOMIC);
 	if (!qentry)
 		return;
+	qentry->body_len = body_len;
 	qentry->link = link;
 	INIT_LIST_HEAD(&qentry->list);
 	memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg));
+	if (body_len) {
+		u8 *src = smc_link_shared_v2_rxbuf(link) ?
+			(u8 *)lgr->wr_rx_buf_v2 : (u8 *)llc;
+
+		memcpy(qentry->body, src + SMC_WR_TX_SIZE, body_len);
+	}
 
 	/* process responses immediately */
 	if ((llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) &&
@@ -2124,7 +2175,7 @@ static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
 			return; /* invalid message */
 	}
 
-	smc_llc_enqueue(link, llc);
+	smc_llc_enqueue(link, llc, wc->byte_len);
 }
 
 /***************************** worker, utils *********************************/
net/smc/smc_wr.c +3 -3
index b04a21b8c5111..4624817137be8 100644
@@ -602,9 +602,9 @@ static void smc_wr_init_sge(struct smc_link *lnk)
 
 	/* With SMC-Rv2 there can be messages larger than SMC_WR_TX_SIZE.
 	 * Each ib_recv_wr gets 2 sges, the second one is a spillover buffer
-	 * and the same buffer for all sges. When a larger message arrived then
-	 * the content of the first small sge is copied to the beginning of
-	 * the larger spillover buffer, allowing easy data mapping.
+	 * and the same buffer for all sges. The spillover sge starts at
+	 * SMC_WR_TX_SIZE, so the leading bytes of that buffer are never
+	 * written.
 	 */
 	for (i = 0; i < lnk->wr_rx_cnt; i++) {
 		int x = i * lnk->wr_rx_sge_cnt;
Signature · 1 line
2.53.0
author maintainer
#514 lore raw

PATCH 503/552 arch_numa: avoid false positive fortify warning in setup_node_to_cpumask_map()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Nathan Chancellor <[email protected]>

commit f2b1cb39d5ccab090d8353788f186f7e7a1fffd4 upstream.

When building ARCH=riscv using clang with CONFIG_FORTIFY_SOURCE and CONFIG_UBSAN_BOUNDS enabled, CONFIG_NR_CPUS > 64, and the default value of 2 for CONFIG_NODES_SHIFT, there is a compiletime warning from the fortify routines.

In file included from mm/arch_numa.c:11:
In file included from include/linux/acpi.h:14:
In file included from include/linux/resource_ext.h:11:
In file included from include/linux/slab.h:17:
In file included from include/linux/gfp.h:7:
In file included from include/linux/mmzone.h:8:
In file included from include/linux/spinlock.h:60:
In file included from include/linux/interrupt_rc.h:17:
In file included from include/linux/smp.h:13:
In file included from include/linux/cpumask.h:11:
In file included from include/linux/bitmap.h:13:
In file included from include/linux/string.h:383:
include/linux/fortify-string.h:430:4: warning: call to '__write_overflow_field' declared with 'warning' attribute: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Wattribue-warning]

    430 |                         __write_overflow_field(p_size_field, size);
        |                         ^
  include/linux/fortify-string.h:430:4: note: called by function 'fortify_memset_chk(unsigned long, unsigned long, unsigned long)'
  include/linux/bitmap.h:248:3: note: inlined by function 'setup_node_to_cpumask_map'
    248 |                 memset(dst, 0, len);
        |                 ^
  include/linux/fortify-string.h:462:25: note: expanded from macro 'memset'
    462 | #define memset(p, c, s) __fortify_memset_chk(p, c, s,                   \
        |                         ^
  include/linux/fortify-string.h:453:2: note: expanded from macro '__fortify_memset_chk'
    453 |         fortify_memset_chk(__fortify_size, p_size, p_size_field),       \
        |         ^
  include/linux/fortify-string.h:430:4: note: use '-gline-directives-only' (implied by '-g1') or higher for more accurate inlining chain locations
    430 |                         __write_overflow_field(p_size_field, size);
        |                         ^

1 warning generated.

In this configuration, MAX_NUMNODES is 4. clang unrolls the for loop in setup_node_to_cpumask_map() past this, which triggers the fortify check when accessing node_to_cpumask_map on the theoretical fifth loop iteration because it would be an out of bounds write.

Make it clear to clang that nr_node_ids is bounded by MAX_NUMNODES due to the logic in setup_nr_node_ids() by early returning in setup_node_to_cpumask_map() should that condition be violated.

---
 drivers/base/arch_numa.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/base/arch_numa.c +12 -0
@@ -105,6 +105,18 @@ static void __init setup_node_to_cpumask
 	if (nr_node_ids == MAX_NUMNODES)
 		setup_nr_node_ids();
 
+	/*
+	 * This check should never be true but it makes it clear to compilers
+	 * that node_to_cpumask_map is bound by nr_node_ids, avoiding false
+	 * positive fortify warnings when accessing node_to_cpumask_map in the
+	 * for loop below.
+	 */
+	if (unlikely(nr_node_ids > MAX_NUMNODES)) {
+		pr_err("nr_node_ids (%u) is larger than MAX_NUMNODES (%u)\n",
+		       nr_node_ids, MAX_NUMNODES);
+		return;
+	}
+
 	/* allocate and clear the mapping */
 	for (node = 0; node < nr_node_ids; node++) {
 		alloc_bootmem_cpumask_var(&node_to_cpumask_map[node]);
author maintainer
#515 lore raw

PATCH 504/552 dm-stats: fix a crash if allocation of per-cpu data fails

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Mikulas Patocka <[email protected]>

commit cc87e26d9cce22061dc21e51e11afef29dbbc36a upstream.

If "dm_kvzalloc(percpu_alloc_size, cpu_to_node(cpu))" fails, the code jumps to the "out" label and calls dm_stat_free. dm_stat_free does "for_each_possible_cpu(cpu) { dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size);", which crashes with NULL pointer dereference if s->stat_percpu[cpu] is NULL.

This commit fixes the bug by testing s->stat_percpu[cpu] for NULL before using it.

Fixes fd2ed4d25270 ("dm: add statistics support")
---
 drivers/md/dm-stats.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/md/dm-stats.c +4 -2
@@ -178,8 +178,10 @@ static void dm_stat_free(struct rcu_head
 	kfree(s->program_id);
 	kfree(s->aux_data);
 	for_each_possible_cpu(cpu) {
-		dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size);
-		dm_kvfree(s->stat_percpu[cpu], s->percpu_alloc_size);
+		if (s->stat_percpu[cpu]) {
+			dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size);
+			dm_kvfree(s->stat_percpu[cpu], s->percpu_alloc_size);
+		}
 	}
 	dm_kvfree(s->stat_shared[0].tmp.histogram, s->histogram_alloc_size);
 	dm_kvfree(s, s->shared_alloc_size);
author maintainer
#516 lore raw

PATCH 505/552 dm-switch: use WRITE_ONCE() in switch_region_table_write()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Haotian Zhang <[email protected]>

commit c7391ebe33162c7962b313caea4d8e6b0bc2a671 upstream.

switch_region_table_read() accesses the region table with READ_ONCE() and is called from the lockless switch_map() IO path. However, switch_region_table_write() stores to the same array with a plain assignment. This results in an inconsistent access pattern for a lockless shared variable and may trigger data race reports.

Use WRITE_ONCE() to pair with the existing READ_ONCE() in switch_region_table_read().

Fixes 99eb1908e643 ("dm switch: factor out switch_region_table_read")
---
 drivers/md/dm-switch.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/md/dm-switch.c +1 -1
@@ -184,7 +184,7 @@ static void switch_region_table_write(st
 	pte = sctx->region_table[region_index];
 	pte &= ~((((region_table_slot_t)1 << sctx->region_table_entry_bits) - 1) << bit);
 	pte |= (region_table_slot_t)value << bit;
-	sctx->region_table[region_index] = pte;
+	WRITE_ONCE(sctx->region_table[region_index], pte);
 }
 
 /*
author maintainer
#517 lore raw

PATCH 506/552 dm-pcache: validate geometry fields from on-disk cache_info

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryam Vargas <[email protected]>

commit 32d1809da31094ef76fd98dc1f1a8b55ca1295dd upstream.

cache_segs_init() iterates cache_info->n_segs times indexing cache->segments[], which is sized to the cache device geometry, and get_seg_id() takes each segment id from the on-media cache_info and the per-segment next_seg link. Both come from cache device metadata that is only CRC-protected with a fixed public seed, so whoever supplies the cache device on a table load (CAP_SYS_ADMIN) controls them: an oversized n_segs or an out-of-range id drives an out-of-bounds access of cache->segments[] and a wild CACHE_DEV_SEGMENT() pointer into the device mapping -- an out-of-bounds read and write from on-disk data.

Reject an n_segs that exceeds the device segment count and a segment id that is out of range before either is used. Valid metadata is unaffected.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache.c |   14 ++++++++++++++
 1 file changed, 14 insertions(+)
drivers/md/dm-pcache/cache.c +14 -0
@@ -246,6 +246,13 @@ static int get_seg_id(struct pcache_cach
 		} else {
 			*seg_id = cache->cache_info.seg_id;
 		}
+
+		if (*seg_id >= cache_dev->seg_num) {
+			pcache_dev_err(pcache, "invalid segment id %u from cache device (seg_num %u)\n",
+				       *seg_id, cache_dev->seg_num);
+			ret = -EIO;
+			goto err;
+		}
 	}
 	return 0;
 err:
@@ -261,6 +268,13 @@ static int cache_segs_init(struct pcache
 	int ret;
 	u32 i;
 
+	if (cache_info->n_segs > cache->cache_dev->seg_num) {
+		pcache_dev_err(CACHE_TO_PCACHE(cache),
+			       "cache_info n_segs %u exceeds cache device segments %u\n",
+			       cache_info->n_segs, cache->cache_dev->seg_num);
+		return -EIO;
+	}
+
 	for (i = 0; i < cache_info->n_segs; i++) {
 		ret = get_seg_id(cache, prev_cache_seg, new_cache, &seg_id);
 		if (ret)
author maintainer
#518 lore raw

PATCH 507/552 dm-pcache: validate kset key_num and intra-segment bounds

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryam Vargas <[email protected]>

commit f11deb032fd84081e7831cffcba895d893054a22 upstream.

Two more fields decoded from the cache device go unbounded. The kset key_num drives cache_kset_crc() and the replay loop in cache_replay(), the writeback worker and the GC worker, but only the magic and a fixed-seed CRC are checked first, so a non-last kset whose key_num exceeds the PCACHE_KSET_KEYS_MAX buffer reads past its end before the CRC compare. A key's intra-segment offset and length in cache_key_decode() are taken verbatim, so a key running past its segment is replayed into the cache tree and the data CRC check and every later read hit then copy adjacent persistent memory into the caller's bio -- an out-of-bounds read that leaks to user space. Both fields are controlled by whoever supplies the cache device (CAP_SYS_ADMIN); the CRC seed is public.

Add kset_onmedia_valid() to bound key_num before any kset read, and reject a key whose offset plus length, computed in 64 bits, exceeds the segment data_size. Valid metadata is unaffected.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache.h           |   21 +++++++++++++++++++++
 drivers/md/dm-pcache/cache_gc.c        |    8 ++++----
 drivers/md/dm-pcache/cache_key.c       |   10 +++++++++-
 drivers/md/dm-pcache/cache_writeback.c |    8 ++++----
 4 files changed, 38 insertions(+), 9 deletions(-)
drivers/md/dm-pcache/cache.h +21 -0
@@ -491,6 +491,27 @@ static inline u32 cache_key_data_crc(str
 	return crc32c(PCACHE_CRC_SEED, data, key->len);
 }
 
+/**
+ * kset_onmedia_valid - Validate a kset header read from the cache device.
+ * @kset_onmedia: Pointer to the kset copied from on-media metadata.
+ *
+ * The magic and CRC are attacker-computable (fixed public seed). A non-last
+ * kset stores key_num keys inline, and cache_kset_crc() and the replay loop
+ * read struct_size(.., data, key_num) bytes from a buffer sized for
+ * PCACHE_KSET_KEYS_MAX keys, so key_num must be bounded before any such use.
+ */
+static inline bool kset_onmedia_valid(struct pcache_cache_kset_onmedia *kset_onmedia)
+{
+	if (kset_onmedia->magic != PCACHE_KSET_MAGIC)
+		return false;
+
+	if (!(kset_onmedia->flags & PCACHE_KSET_FLAGS_LAST) &&
+	    kset_onmedia->key_num > PCACHE_KSET_KEYS_MAX)
+		return false;
+
+	return true;
+}
+
 static inline u32 cache_kset_crc(struct pcache_cache_kset_onmedia *kset_onmedia)
 {
 	u32 crc_size;
drivers/md/dm-pcache/cache_gc.c +4 -4
@@ -44,11 +44,11 @@ static bool need_gc(struct pcache_cache
 		return false;
 	}
 
-	/* Check if kset_onmedia is corrupted */
-	if (kset_onmedia->magic != PCACHE_KSET_MAGIC) {
-		pcache_dev_debug(pcache, "gc error: magic is not as expected. key_tail: %u:%u magic: %llx, expected: %llx\n",
+	/* Reject a corrupted or out-of-bounds kset before reading its keys */
+	if (!kset_onmedia_valid(kset_onmedia)) {
+		pcache_dev_debug(pcache, "gc error: invalid kset. key_tail: %u:%u magic: %llx, key_num: %u\n",
 					key_tail->cache_seg->cache_seg_id, key_tail->seg_off,
-					kset_onmedia->magic, PCACHE_KSET_MAGIC);
+					kset_onmedia->magic, kset_onmedia->key_num);
 		return false;
 	}
 
drivers/md/dm-pcache/cache_key.c +9 -1
@@ -97,6 +97,14 @@ int cache_key_decode(struct pcache_cache
 	key->cache_pos.cache_seg = &cache->segments[key_onmedia->cache_seg_id];
 	key->cache_pos.seg_off = key_onmedia->cache_seg_off;
 
+	if ((u64)key->cache_pos.seg_off + key->len >
+			key->cache_pos.cache_seg->segment.data_size) {
+		pcache_dev_err(pcache, "key seg_off %u + len %u exceeds segment data size %u\n",
+				key->cache_pos.seg_off, key->len,
+				key->cache_pos.cache_seg->segment.data_size);
+		return -EIO;
+	}
+
 	key->seg_gen = key_onmedia->seg_gen;
 	key->flags = key_onmedia->flags;
 
@@ -778,7 +786,7 @@ int cache_replay(struct pcache_cache *ca
 			goto out;
 		}
 
-		if (kset_onmedia->magic != PCACHE_KSET_MAGIC ||
+		if (!kset_onmedia_valid(kset_onmedia) ||
 				kset_onmedia->crc != cache_kset_crc(kset_onmedia)) {
 			break;
 		}
drivers/md/dm-pcache/cache_writeback.c +4 -4
@@ -55,11 +55,11 @@ static inline bool is_cache_clean(struct
 		return true;
 	}
 
-	/* Check if the magic number matches the expected value */
-	if (kset_onmedia->magic != PCACHE_KSET_MAGIC) {
-		pcache_dev_debug(pcache, "dirty_tail: %u:%u magic: %llx, not expected: %llx\n",
+	/* Reject a corrupted or out-of-bounds kset before reading its keys */
+	if (!kset_onmedia_valid(kset_onmedia)) {
+		pcache_dev_debug(pcache, "dirty_tail: %u:%u invalid kset magic: %llx, key_num: %u\n",
 				dirty_tail->cache_seg->cache_seg_id, dirty_tail->seg_off,
-				kset_onmedia->magic, PCACHE_KSET_MAGIC);
+				kset_onmedia->magic, kset_onmedia->key_num);
 		return true;
 	}
 
author maintainer
#519 lore raw

PATCH 508/552 dm-pcache: validate on-media seg_num against the cache device size

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryam Vargas <[email protected]>

commit 62d92e45abe9e087370f9fc5d876b95673aced34 upstream.

seg_num is read from the crc32c-only superblock, so whoever supplies the cache device on a table load (CAP_SYS_ADMIN) controls it. It sizes cache->segments[] and is the value every later on-media segment id is bounded against, yet it is never checked against the device. Because cache_dev->mapping is the direct map of the pmem, CACHE_DEV_SEGMENT() for a segment id past the device resolves to ordinary kernel memory beyond the mapping; a new-cache init reaching such an id has cache_seg_init() -> cache_dev_zero_range() memset() 12 KiB over that memory -- an out-of-bounds write into the kernel heap at table load. A zero seg_num makes the segment allocations ZERO_SIZE_PTR.

Reject a seg_num that is zero, larger than the device can hold, or larger than PCACHE_CACHE_SEGS_MAX before it is used.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache_dev.c | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)
drivers/md/dm-pcache/cache_dev.c +21 -1
index ece689e6ce59..f0259353ee39 100644
@@ -242,6 +242,8 @@ int cache_dev_start(struct dm_pcache *pcache)
 	struct pcache_cache_dev *cache_dev = &pcache->cache_dev;
 	struct pcache_sb sb;
 	bool format = false;
+	u32 seg_num;
+	u64 max_segs;
 	int ret;
 
 	mutex_init(&cache_dev->seg_lock);
@@ -269,7 +271,25 @@ int cache_dev_start(struct dm_pcache *pcache)
 		goto dax_release;
 
 	cache_dev->sb_flags = le32_to_cpu(sb.flags);
-	ret = cache_dev_init(cache_dev, le32_to_cpu(sb.seg_num));
+
+	/*
+	 * seg_num is read from the crc32c-only superblock, so whoever supplies
+	 * the cache device controls it. It is the ceiling every later on-media
+	 * segment id is validated against, so bound it against what the device
+	 * physically holds before it is trusted, or a forged seg_num lets a
+	 * segment id address past the DAX mapping.
+	 */
+	seg_num = le32_to_cpu(sb.seg_num);
+	max_segs = (bdev_nr_bytes(cache_dev->dm_dev->bdev) - PCACHE_SEGMENTS_OFF) /
+		   PCACHE_SEG_SIZE;
+	if (seg_num == 0 || seg_num > max_segs || seg_num > PCACHE_CACHE_SEGS_MAX) {
+		pcache_dev_err(pcache, "invalid seg_num %u from cache device (device holds %llu, max %u)\n",
+			       seg_num, max_segs, (u32)PCACHE_CACHE_SEGS_MAX);
+		ret = -EIO;
+		goto dax_release;
+	}
+
+	ret = cache_dev_init(cache_dev, seg_num);
 	if (ret)
 		goto dax_release;
 
Signature · 1 line
2.55.0
author maintainer
#520 lore raw

PATCH 509/552 dm-pcache: bound the persisted tail-position offset

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryam Vargas <[email protected]>

commit d1898576090a10d2ac2715218a652e78fb65a6b0 upstream.

cache_pos_decode() takes the persisted key_tail and dirty_tail seg_off from the cache device and addresses within the segment with it. A seg_off at or past the segment data_size, controllable by whoever supplies the device (CAP_SYS_ADMIN), reads past the segment data.

Reject a decoded seg_off that is not below the segment data_size.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/md/dm-pcache/cache.c +4 -0
@@ -119,6 +119,10 @@ int cache_pos_decode(struct pcache_cache
 		return -EIO;
 
 	pos->cache_seg = &cache->segments[latest.cache_seg_id];
+
+	if (latest.seg_off >= pos->cache_seg->segment.data_size)
+		return -EIO;
+
 	pos->seg_off = latest.seg_off;
 	*seq = latest.header.seq;
 	*index = (latest_addr - pos_onmedia);
author maintainer
#521 lore raw

PATCH 510/552 dm-pcache: clamp the tail kset read to the segment data region

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryam Vargas <[email protected]>

commit becf07e2b0053027495ecd671b1f82fb2e615f68 upstream.

The tail-kset read in cache_replay(), the writeback worker and the GC worker bounds its length by PCACHE_SEG_SIZE - seg_off, the raw segment size rather than the data region. A tail near the segment end reads past the segment data into the following control area.

Clamp the read to cache_seg_remain(), the data region.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache_gc.c        |    2 +-
 drivers/md/dm-pcache/cache_key.c       |    2 +-
 drivers/md/dm-pcache/cache_writeback.c |    2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)
drivers/md/dm-pcache/cache_gc.c +1 -1
@@ -37,7 +37,7 @@ static bool need_gc(struct pcache_cache
 
 	kset_onmedia = (struct pcache_cache_kset_onmedia *)cache->gc_kset_onmedia_buf;
 
-	to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - key_tail->seg_off);
+	to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(key_tail));
 	ret = copy_mc_to_kernel(kset_onmedia, key_addr, to_copy);
 	if (ret) {
 		pcache_dev_err(pcache, "error to read kset: %d", ret);
drivers/md/dm-pcache/cache_key.c +1 -1
@@ -779,7 +779,7 @@ int cache_replay(struct pcache_cache *ca
 	__set_bit(pos->cache_seg->cache_seg_id, cache->seg_map);
 
 	while (true) {
-		to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - pos->seg_off);
+		to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(pos));
 		ret = copy_mc_to_kernel(kset_onmedia, cache_pos_addr(pos), to_copy);
 		if (ret) {
 			ret = -EIO;
drivers/md/dm-pcache/cache_writeback.c +1 -1
@@ -48,7 +48,7 @@ static inline bool is_cache_clean(struct
 	addr = cache_pos_addr(dirty_tail);
 	kset_onmedia = (struct pcache_cache_kset_onmedia *)cache->wb_kset_onmedia_buf;
 
-	to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - dirty_tail->seg_off);
+	to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(dirty_tail));
 	ret = copy_mc_to_kernel(kset_onmedia, addr, to_copy);
 	if (ret) {
 		pcache_dev_err(pcache, "error to read kset: %d", ret);
author maintainer
#522 lore raw

PATCH 511/552 dm-pcache: detect a cycle in the last-kset chain during replay

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryam Vargas <[email protected]>

commit 16c3b3a326e70f246a605b3dc27b7f83ba4743e3 upstream.

cache_replay() follows the on-media last-kset chain by next_cache_seg_id with no cond_resched(). A forged chain that points back into a segment it has already visited makes the replay loop follow it forever.

Cap the last-kset hops at cache->n_segs; a valid chain visits each segment at most once.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache_key.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/md/dm-pcache/cache_key.c +6 -1
@@ -762,7 +762,7 @@ int cache_replay(struct pcache_cache *ca
 	struct pcache_cache_pos pos_tail;
 	struct pcache_cache_pos *pos;
 	struct pcache_cache_kset_onmedia *kset_onmedia;
-	u32 to_copy, count = 0;
+	u32 to_copy, count = 0, last_hops = 0;
 	int ret = 0;
 
 	kset_onmedia = kzalloc(PCACHE_KSET_ONMEDIA_SIZE_MAX, GFP_KERNEL);
@@ -797,6 +797,11 @@ int cache_replay(struct pcache_cache *ca
 
 			pcache_dev_debug(pcache, "last kset replay, next: %u\n", kset_onmedia->next_cache_seg_id);
 
+			if (++last_hops > cache->n_segs) {
+				ret = -EIO;
+				goto out;
+			}
+
 			next_seg = &cache->segments[kset_onmedia->next_cache_seg_id];
 
 			pos->cache_seg = next_seg;
author maintainer
#523 lore raw

PATCH 512/552 dm-pcache: only hand out initialized cache segments

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryam Vargas <[email protected]>

commit 2df0fc042e299bae3c0f60ea5cd2af9285658e9f upstream.

get_cache_segment() scans the segment map up to cache->n_segs, the physical device segment count, but cache_segs_init() only initializes the first cache_info->n_segs segments. A crafted image with cache_info->n_segs smaller than the device count leaves the remaining pcache_cache_segment structs zeroed (segment.data == NULL), and the allocator can hand one to cache_kset_close(), which writes through the returned segment's data pointer with no NULL check.

Bound the allocator's search to cache_info->n_segs so only initialized segments are ever returned. A conforming cache sets n_segs equal to the device segment count, so this rejects nothing legitimate.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache_segment.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)
drivers/md/dm-pcache/cache_segment.c +10 -2
index 9d92e2b067ed..c698ebbc626d 100644
@@ -243,8 +243,16 @@ struct pcache_cache_segment *get_cache_segment(struct pcache_cache *cache)
 
 	spin_lock(&cache->seg_map_lock);
 again:
-	seg_id = find_next_zero_bit(cache->seg_map, cache->n_segs, cache->last_cache_seg);
-	if (seg_id == cache->n_segs) {
+	/*
+	 * Only allocate initialized segments. cache_segs_init() initializes
+	 * cache_info.n_segs of the cache->n_segs device segments; a forged
+	 * smaller cache_info.n_segs leaves the rest as zeroed structs whose data
+	 * pointer is NULL. Bounding the search to cache_info.n_segs keeps such a
+	 * segment from reaching cache_kset_close(), which writes through it.
+	 */
+	seg_id = find_next_zero_bit(cache->seg_map, cache->cache_info.n_segs,
+				    cache->last_cache_seg);
+	if (seg_id == cache->cache_info.n_segs) {
 		/* reset the hint of ->last_cache_seg and retry */
 		if (cache->last_cache_seg) {
 			cache->last_cache_seg = 0;
Signature · 1 line
2.55.0
author maintainer
#524 lore raw

PATCH 513/552 dm-pcache: fix implicit u8 truncation of gc_percent in message handler

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jianyun Gao <[email protected]>

commit fb9e17287a4ea1cbbcedc77e6866978ecc2a7b55 upstream.

When setting gc_percent via message, kstrtoul parses the input into an unsigned long, which is then implicitly truncated to u8 when passed to pcache_cache_set_gc_percent(). For example, value 266 (0x10A) silently truncates to 10 (0x0A), successfully bypassing the > 90 upper bound check in pcache_cache_set_gc_percent(), and setting a different value than the user intended.

Use kstrtou8 directly instead of kstrtoul, so that overflow values are properly rejected.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/dm_pcache.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/md/dm-pcache/dm_pcache.c +2 -2
index d5cfd162c063..645fc27d82ba 100644
@@ -439,13 +439,13 @@ static int dm_pcache_message(struct dm_target *ti, unsigned int argc,
 			     char **argv, char *result, unsigned int maxlen)
 {
 	struct dm_pcache *pcache = ti->private;
-	unsigned long val;
+	u8 val;
 
 	if (argc != 2)
 		goto err;
 
 	if (!strcasecmp(argv[0], "gc_percent")) {
-		if (kstrtoul(argv[1], 10, &val))
+		if (kstrtou8(argv[1], 10, &val))
 			goto err;
 
 		return pcache_cache_set_gc_percent(&pcache->cache, val);
Signature · 1 line
2.55.0
author maintainer
#525 lore raw

PATCH 514/552 dm-pcache: fix use-after-free and invalid seg operations in kset_replay()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Jianyun Gao <[email protected]>

commit c2e894eac398b258f12fdec73ed6ba081047f7b3 upstream.

In kset_replay, when key->seg_gen is stale (key->seg_gen < key->cache_pos.cache_seg->gen), cache_key_put(key) is called but then key->cache_pos.cache_seg is accessed as the argument to cache_seg_get(). This is a use-after-free on the freed key memory. Although mempool recycled memory is not immediately reclaimed or overwritten in practice, this is still a potential UAF bug.

Additionally, for expired invalid keys, setting the cache->seg_map bit and calling cache_seg_get() is unreasonable since the corresponding segment data is no longer valid.

Fix both issues by moving cache_seg_get() and __set_bit() after the gen check, so they only execute for valid keys, and using continue to skip invalid keys.

Fixes 1d57628ff95b ("dm-pcache: add persistent cache target in device-mapper")
---
 drivers/md/dm-pcache/cache_key.c |   13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)
drivers/md/dm-pcache/cache_key.c +6 -7
@@ -736,18 +736,17 @@ static int kset_replay(struct pcache_cac
 			goto err;
 		}
 
-		__set_bit(key->cache_pos.cache_seg->cache_seg_id, cache->seg_map);
-
 		/* Check if the segment generation is valid for insertion. */
 		if (key->seg_gen < key->cache_pos.cache_seg->gen) {
 			cache_key_put(key);
-		} else {
-			cache_subtree = get_subtree(&cache->req_key_tree, key->off);
-			spin_lock(&cache_subtree->tree_lock);
-			cache_key_insert(&cache->req_key_tree, key, true);
-			spin_unlock(&cache_subtree->tree_lock);
+			continue;
 		}
 
+		__set_bit(key->cache_pos.cache_seg->cache_seg_id, cache->seg_map);
+		cache_subtree = get_subtree(&cache->req_key_tree, key->off);
+		spin_lock(&cache_subtree->tree_lock);
+		cache_key_insert(&cache->req_key_tree, key, true);
+		spin_unlock(&cache_subtree->tree_lock);
 		cache_seg_get(key->cache_pos.cache_seg);
 	}
 
author maintainer
#526 lore raw

PATCH 515/552 i3c: master: adi: initialize the lock before enabling interrupts

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Runyu Xiao <[email protected]>

commit 8a53f9102a0d3eeb8784999f925028acf339c276 upstream.

adi_i3c_master_probe() requests the IRQ and unmasks REG_IRQ_PENDING_CMDR before the controller's IBI state, transfer queue list and transfer queue lock are initialized. A pending CMDR interrupt can therefore run adi_i3c_master_irq() and take master->xferqueue.lock before the dynamic lock has been initialized.

This issue was found by our static analysis tool and then manually reviewed against the current tree.

The grounded PoC kept the probe ordering and the IRQ path adi_i3c_master_probe() -> adi_i3c_master_irq() -> xferqueue.lock, with a pending CMDR interrupt arriving after REG_IRQ_PENDING_CMDR is unmasked. Lockdep reported:

INFO: trying to register non-static key.
you didn't initialize this object before use?

  lock_acquire+0xbb/0x290
  _raw_spin_lock_irqsave+0x36/0x60
  adi_i3c_master_irq+0x32/0x56 [vuln_msv]
  adi_i3c_master_probe+0x5a/0xf47 [vuln_msv]

Initialize the transfer queue and IBI state before requesting and unmasking the IRQ.

Fixes a79ac2cdc91d ("i3c: master: Add driver for Analog Devices I3C Controller IP")
---
 drivers/i3c/master/adi-i3c-master.c |   15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)
drivers/i3c/master/adi-i3c-master.c +7 -8
@@ -967,17 +967,9 @@ static int adi_i3c_master_probe(struct p
 	writel(0x00, master->regs + REG_ENABLE);
 	writel(0x00, master->regs + REG_IRQ_MASK);
 
-	ret = devm_request_irq(&pdev->dev, irq, adi_i3c_master_irq, 0,
-			       dev_name(&pdev->dev), master);
-	if (ret)
-		return ret;
-
 	platform_set_drvdata(pdev, master);
 
 	master->free_rr_slots = GENMASK(ADI_MAX_DEVS, 1);
-
-	writel(REG_IRQ_PENDING_CMDR, master->regs + REG_IRQ_MASK);
-
 	spin_lock_init(&master->ibi.lock);
 	master->ibi.num_slots = 15;
 	master->ibi.slots = devm_kcalloc(&pdev->dev, master->ibi.num_slots,
@@ -989,6 +981,13 @@ static int adi_i3c_master_probe(struct p
 	spin_lock_init(&master->xferqueue.lock);
 	INIT_LIST_HEAD(&master->xferqueue.list);
 
+	ret = devm_request_irq(&pdev->dev, irq, adi_i3c_master_irq, 0,
+			       dev_name(&pdev->dev), master);
+	if (ret)
+		return ret;
+
+	writel(REG_IRQ_PENDING_CMDR, master->regs + REG_IRQ_MASK);
+
 	return i3c_master_register(&master->base, &pdev->dev,
 				   &adi_i3c_master_ops, false);
 }
author maintainer
#527 lore raw

PATCH 516/552 i3c: master: Fix info leak and UAF in device unregister path

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Adrian Hunter <[email protected]>

commit d2c743efd2d1ee64e94324664808f623dd865872 upstream.

i3c_master_unregister_i3c_devs() clears i3cdev->dev->desc before calling device_unregister(). During device_unregister(), device_del() emits a KOBJ_REMOVE uevent and unbinds the driver while the device descriptor is still expected to be valid. As a result, i3c_device_uevent() and a racing modalias_show() can observe a NULL desc and fall back to an uninitialized stack struct i3c_device_info, leaking kernel stack contents in the generated modalias. Driver .remove() callbacks may also encounter an unexpected NULL desc during unbind.

Keep desc valid until device_unregister() has completed. Since device_unregister() drops the device reference and may free the device, take an extra reference with get_device() before unregistering. Clear desc afterwards and release the extra reference with put_device(). This preserves the release-time invariant that desc must be NULL while avoiding both the information leak and a potential use-after-free from writing desc after the device has been released.

---
 drivers/i3c/master.c |    9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/i3c/master.c +5 -4
@@ -2808,11 +2808,12 @@ static void i3c_master_unregister_i3c_de
 		if (!i3cdev->dev)
 			continue;
 
-		i3cdev->dev->desc = NULL;
-		if (device_is_registered(&i3cdev->dev->dev))
+		if (device_is_registered(&i3cdev->dev->dev)) {
+			get_device(&i3cdev->dev->dev);
 			device_unregister(&i3cdev->dev->dev);
-		else
-			put_device(&i3cdev->dev->dev);
+		}
+		i3cdev->dev->desc = NULL;
+		put_device(&i3cdev->dev->dev);
 		i3cdev->dev = NULL;
 	}
 }
author maintainer
#528 lore raw

PATCH 517/552 i3c: master: svc: bound IBI payload to the requested max_payload_len

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Maoyi Xie <[email protected]>

commit e2bda39d7f9f285ec803e200b5c1f17143d0b483 upstream.

svc_i3c_master_handle_ibi() reads the IBI payload from the RX FIFO into the IBI slot. The loop is bounded by the hardware FIFO size (SVC_I3C_FIFO_SIZE), not by the slot size.

slot->data points into the IBI pool, which i3c_generic_ibi_alloc_pool() sizes at max_payload_len per slot. svc_i3c_master_request_ibi() only rejects a max_payload_len larger than SVC_I3C_FIFO_SIZE, so a driver can request a smaller one. mctp-i3c requests 1. Each readsb() then copies the controller RXCOUNT bytes (up to 31) with no check against the slot size. A device that sends more bytes than the slot holds writes past slot->data, an out-of-bounds write into the IBI pool.

Bound the loop by dev->ibi->max_payload_len and clamp each read to the space left in the slot, the same way dw-i3c does. A device can still send more than the requested payload. Flush the leftover bytes from the RX FIFO so they do not leak into the next transfer.

---
 drivers/i3c/master/svc-i3c-master.c |   10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/i3c/master/svc-i3c-master.c +9 -1
@@ -418,14 +418,22 @@ static int svc_i3c_master_handle_ibi(str
 	buf = slot->data;
 
 	while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))  &&
-	       slot->len < SVC_I3C_FIFO_SIZE) {
+	       slot->len < dev->ibi->max_payload_len) {
 		mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
 		count = SVC_I3C_MDATACTRL_RXCOUNT(mdatactrl);
+		count = min(count, dev->ibi->max_payload_len - slot->len);
 		readsb(master->regs + SVC_I3C_MRDATAB, buf, count);
 		slot->len += count;
 		buf += count;
 	}
 
+	/*
+	 * The device may have sent more than the requested payload. Drop the
+	 * extra bytes so they do not leak into the next transfer.
+	 */
+	if (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS)))
+		writel(SVC_I3C_MDATACTRL_FLUSHRB, master->regs + SVC_I3C_MDATACTRL);
+
 	master->ibi.tbq_slot = slot;
 
 	return 0;
author maintainer
#529 lore raw

PATCH 518/552 i3c: master: Fix potential UAF in i3c_device_uevent()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Adrian Hunter <[email protected]>

commit e5e8dd2e959f470524c16ca444d001c90d6bb3ad upstream.

i3c_device_uevent() dereferences i3cdev->desc without holding the bus normal-use lock. Since the descriptor pointer can be replaced concurrently, including when a uevent is generated from sysfs, this can result in dereferencing a stale descriptor and lead to a use-after-free.

Use i3c_device_get_info() instead, which protects access to the descriptor with the normal-use lock.

Commit 6cf7b65f7029 ("i3c: Use i3cdev->desc->info instead of calling i3c_device_get_info() to avoid deadlock") replaced the accessor with a direct descriptor dereference because i3c_device_get_info() would recursively acquire bus->lock during device registration.

This change depends on "i3c: master: Fix recursive locking during device registration", which moves device registration out from under bus->lock and removes the possibility of that deadlock. Without that change, restoring the i3c_device_get_info() call would reintroduce the deadlock.

Fixes 6cf7b65f7029 ("i3c: Use i3cdev->desc->info instead of calling i3c_device_get_info() to avoid deadlock")
Cc [email protected] # requires "i3c: master: Fix recursive locking during device registration"
---
 drivers/i3c/master.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/i3c/master.c +1 -2
@@ -315,8 +315,7 @@ static int i3c_device_uevent(const struc
 	struct i3c_device_info devinfo;
 	u16 manuf, part, ext;
 
-	if (i3cdev->desc)
-		devinfo = i3cdev->desc->info;
+	i3c_device_get_info(i3cdev, &devinfo);
 	manuf = I3C_PID_MANUF_ID(devinfo.pid);
 	part = I3C_PID_PART_ID(devinfo.pid);
 	ext = I3C_PID_EXTRA_INFO(devinfo.pid);
author maintainer
#530 lore raw

PATCH 519/552 i3c: renesas: Check that the transfer is valid before accessing it

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Claudiu Beznea <[email protected]>

commit 5f1a76ecfe90544a28d657306c9b3caa66ba0e63 upstream.

The Renesas I3C driver uses an asynchronous model to transfer data. It prepares a struct renesas_i3c_xfer, enqueues it, and waits for completion. The interrupt handler dequeues the transfer, updates/uses it, and signals the waiting thread.

If the completion times out, the waiting thread dequeues the transfer and free it. If an interrupt fires after that, the handler may access freed memory, leading to crashes.

Check that the transfer is still valid before accessing it in the interrupt handler. With it clear any status flags and disable all the interrupts to avoid triggering the same interrupts again.

---
 drivers/i3c/master/renesas-i3c.c |   52 +++++++++++++++++++++++++++++++++------
 1 file changed, 45 insertions(+), 7 deletions(-)
drivers/i3c/master/renesas-i3c.c +45 -7
@@ -428,6 +428,21 @@ static void renesas_i3c_enqueue_xfer(str
 	}
 }
 
+static void renesas_i3c_irqs_mask_and_clear_locked(struct renesas_i3c *i3c)
+{
+	/* Disable all the interrupts. */
+	renesas_writel(i3c->regs, BIE, 0);
+	renesas_writel(i3c->regs, NTIE, 0);
+
+	/* Clear normal transfer status flags. */
+	renesas_writel(i3c->regs, NTST, 0);
+
+	/* Clear bus status flags. */
+	renesas_writel(i3c->regs, BST, 0);
+	/* Read back registers to confirm writes have fully propagated. */
+	renesas_readl(i3c->regs, BST);
+}
+
 static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer)
 {
 	unsigned long time_left;
@@ -1000,6 +1015,11 @@ static irqreturn_t renesas_i3c_tx_isr(in
 
 	scoped_guard(spinlock, &i3c->xferqueue.lock) {
 		xfer = i3c->xferqueue.cur;
+		if (!xfer) {
+			renesas_i3c_irqs_mask_and_clear_locked(i3c);
+			return IRQ_HANDLED;
+		}
+
 		cmd = xfer->cmds;
 
 		if (xfer->is_i2c_xfer) {
@@ -1040,6 +1060,11 @@ static irqreturn_t renesas_i3c_resp_isr(
 
 	scoped_guard(spinlock, &i3c->xferqueue.lock) {
 		xfer = i3c->xferqueue.cur;
+		if (!xfer) {
+			renesas_i3c_irqs_mask_and_clear_locked(i3c);
+			return IRQ_HANDLED;
+		}
+
 		cmd = xfer->cmds;
 
 		/* Clear the Respone Queue Full status flag*/
@@ -1124,6 +1149,11 @@ static irqreturn_t renesas_i3c_tend_isr(
 
 	scoped_guard(spinlock, &i3c->xferqueue.lock) {
 		xfer = i3c->xferqueue.cur;
+		if (!xfer) {
+			renesas_i3c_irqs_mask_and_clear_locked(i3c);
+			return IRQ_HANDLED;
+		}
+
 		cmd = xfer->cmds;
 
 		if (xfer->is_i2c_xfer) {
@@ -1170,6 +1200,11 @@ static irqreturn_t renesas_i3c_rx_isr(in
 
 	scoped_guard(spinlock, &i3c->xferqueue.lock) {
 		xfer = i3c->xferqueue.cur;
+		if (!xfer) {
+			renesas_i3c_irqs_mask_and_clear_locked(i3c);
+			return IRQ_HANDLED;
+		}
+
 		cmd = xfer->cmds;
 
 		if (xfer->is_i2c_xfer) {
@@ -1220,15 +1255,13 @@ static irqreturn_t renesas_i3c_stop_isr(
 	struct renesas_i3c_xfer *xfer;
 
 	scoped_guard(spinlock, &i3c->xferqueue.lock) {
-		xfer = i3c->xferqueue.cur;
-
-		/* read back registers to confirm writes have fully propagated */
-		renesas_writel(i3c->regs, BST, 0);
-		renesas_readl(i3c->regs, BST);
-		renesas_writel(i3c->regs, BIE, 0);
-		renesas_clear_bit(i3c->regs, NTST, NTST_TDBEF0 | NTST_RDBFF0);
+		renesas_i3c_irqs_mask_and_clear_locked(i3c);
 		renesas_clear_bit(i3c->regs, SCSTRCTL, SCSTRCTL_RWE);
 
+		xfer = i3c->xferqueue.cur;
+		if (!xfer)
+			return IRQ_HANDLED;
+
 		xfer->ret = 0;
 		complete(&xfer->comp);
 	}
@@ -1245,6 +1278,11 @@ static irqreturn_t renesas_i3c_start_isr
 
 	scoped_guard(spinlock, &i3c->xferqueue.lock) {
 		xfer = i3c->xferqueue.cur;
+		if (!xfer) {
+			renesas_i3c_irqs_mask_and_clear_locked(i3c);
+			return IRQ_HANDLED;
+		}
+
 		cmd = xfer->cmds;
 
 		if (xfer->is_i2c_xfer) {
author maintainer
#531 lore raw

PATCH 520/552 i3c: renesas: Clean DATBAS register on detach

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Claudiu Beznea <[email protected]>

commit 797ed83c0cd495be4b345750c59e0363bf4d6207 upstream.

The controller uses DATBAS registers on TX/RX logic. Clean the DATBAS register for the detached I3C device to avoid issues.

---
 drivers/i3c/master/renesas-i3c.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/i3c/master/renesas-i3c.c +2 -0
@@ -906,6 +906,8 @@ static void renesas_i3c_detach_i3c_dev(s
 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
 	struct renesas_i3c *i3c = to_renesas_i3c(m);
 
+	renesas_writel(i3c->regs, DATBAS(data->index), 0);
+
 	i3c_dev_set_master_data(dev, NULL);
 	i3c->addrs[data->index] = 0;
 	i3c->free_pos |= BIT(data->index);
author maintainer
#532 lore raw

PATCH 521/552 i3c: renesas: Reconfigure the DATBAS register on re-attach

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Claudiu Beznea <[email protected]>

commit 1364afd3e2e76e007a2c07ec95704d56980226f0 upstream.

During re-attach, the device may change its position in the i3c->addrs[] array. As a result, it may use a different Device Address Table Basic Register (DATBAS), which needs to be reconfigured.

Reconfigure the DATBAS register on re-attach. Along with it update software caches.

---
 drivers/i3c/master/renesas-i3c.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)
drivers/i3c/master/renesas-i3c.c +16 -0
@@ -893,10 +893,26 @@ static int renesas_i3c_reattach_i3c_dev(
 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
 	struct renesas_i3c *i3c = to_renesas_i3c(m);
 	struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+	int pos;
+
+	pos = renesas_i3c_get_free_pos(i3c);
+
+	if (data->index > pos && pos >= 0) {
+		renesas_writel(i3c->regs, DATBAS(data->index), 0);
+		i3c->addrs[data->index] = 0;
+		i3c->free_pos |= BIT(data->index);
+
+		data->index = pos;
+		i3c->free_pos &= ~BIT(data->index);
+	}
 
 	i3c->addrs[data->index] = dev->info.dyn_addr ? dev->info.dyn_addr :
 							dev->info.static_addr;
 
+	renesas_writel(i3c->regs, DATBAS(data->index),
+		       DATBAS_DVSTAD(dev->info.static_addr) |
+		       datbas_dvdyad_with_parity(i3c->addrs[data->index]));
+
 	return 0;
 }
 
author maintainer
#533 lore raw

PATCH 522/552 wifi: brcmfmac: Fix memory leak in brcmf_sdio_read_control()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Abdun Nihaal <[email protected]>

commit 0d10db8e94fcb23a799789aaa696b4d8f937e207 upstream.

The memory allocated for buf is not freed in some of the error paths in brcmf_sdio_read_control(). Fix that by adding vfree() calls.

Fixes dd43a01c5cdb ("brcmfmac: use dynamically allocated control frame buffer")
[arend: rework as suggested by Johannes]
---
 drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +2 -1
@@ -1827,17 +1827,18 @@ gotpkt:
 	if (bus->rxctl) {
 		brcmf_err("last control frame is being processed.\n");
 		spin_unlock_bh(&bus->rxctl_lock);
-		vfree(buf);
 		goto done;
 	}
 	bus->rxctl = buf + doff;
 	bus->rxctl_orig = buf;
 	bus->rxlen = len - doff;
 	spin_unlock_bh(&bus->rxctl_lock);
+	buf = NULL;
 
 done:
 	/* Awake any waiters */
 	brcmf_sdio_dcmd_resp_wake(bus);
+	vfree(buf);
 }
 
 /* Pad read to blocksize for efficiency */
author maintainer
#534 lore raw

PATCH 523/552 wifi: iwlwifi: dvm: fix memory leak in iwl_op_mode_dvm_start()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Dawei Feng <[email protected]>

commit 67105abd6195a685a84dcb8a5daf54a1f4bfdb60 upstream.

In iwl_op_mode_dvm_start(), jumping to out_free_eeprom currently bypasses the out_free_eeprom_blob label. Consequently, error paths triggered after successfully parsing the EEPROM free priv->nvm_data but leak priv->eeprom_blob.

Fix this memory leak by reordering the error handling labels so that out_free_eeprom falls through to out_free_eeprom_blob.

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. Manual inspection confirms that the bug is still present in v7.1-rc6.

An x86_64 allyesconfig build showed no new warnings. As we do not have supported Intel DVM wireless hardware and firmware to test with, no runtime testing was able to be performed.

---
 drivers/net/wireless/intel/iwlwifi/dvm/main.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/wireless/intel/iwlwifi/dvm/main.c +2 -2
@@ -1511,10 +1511,10 @@ out_destroy_workqueue:
 	priv->workqueue = NULL;
 out_uninit_drv:
 	iwl_uninit_drv(priv);
-out_free_eeprom_blob:
-	kfree(priv->eeprom_blob);
 out_free_eeprom:
 	kfree(priv->nvm_data);
+out_free_eeprom_blob:
+	kfree(priv->eeprom_blob);
 out_leave_trans:
 	iwl_trans_op_mode_leave(priv->trans);
 out_free_hw:
author maintainer
#535 lore raw

PATCH 524/552 wifi: rtl8xxxu: fix use-after-free from rx_urb_wq on stop

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Fan Wu <[email protected]>

commit 6c080026ecc17eecb103f8927c64ea73a74bb818 upstream.

rtl8xxxu arms rx_urb_wq from the RX completion path:
rtl8xxxu_rx_complete() hands the URB to rtl8xxxu_queue_rx_urb(), which queues it on rx_urb_pending_list and, once the list grows past RTL8XXXU_RX_URB_PENDING_WATER, schedules rx_urb_wq. The worker rtl8xxxu_rx_urb_work() drains rx_urb_pending_list, recovers priv through container_of, and resubmits each URB through rtl8xxxu_submit_rx_urb(), which anchors it on rx_anchor and dereferences priv->udev.

rtl8xxxu_stop() cancels the sibling work items (c2hcmd_work, ra_watchdog, update_beacon_work) but never cancels rx_urb_wq, so a worker armed during the last burst of RX traffic can run rtl8xxxu_rx_urb_work() after rtl8xxxu_disconnect() has called ieee80211_free_hw(), which frees priv, producing a use-after-free. The window opens under active RX traffic (pending count above the watermark) followed by a disconnect.

There are two teardown races to close:

* rtl8xxxu_queue_rx_urb() decided whether to enqueue under rx_urb_lock but called schedule_work() after dropping the lock. A completion that observed shutdown == false and released the lock could then call schedule_work() after rtl8xxxu_stop() had set shutdown and cancel_work_sync() had already returned, arming the worker to run after the teardown. Move schedule_work() under the same !shutdown branch so the arming decision is atomic with the shutdown check.

* rtl8xxxu_rx_urb_work() anchors every URB it drained back onto rx_anchor through rtl8xxxu_submit_rx_urb(). A worker still running when usb_kill_anchored_urbs(&priv->rx_anchor) returned would submit a URB that escaped the kill. In rtl8xxxu_stop(), call cancel_work_sync(&priv->rx_urb_wq) before the kill so the worker is drained first.

After priv->shutdown is set under rx_urb_lock, completions can no longer queue rx_urb_wq. cancel_work_sync() then drains the last queued or running worker, and the following usb_kill_anchored_urbs() kills the URBs it may have submitted.

rtl8xxxu_disconnect() is covered because ieee80211_unregister_hw() guarantees .stop() runs for a live interface before ieee80211_free_hw() frees priv. The probe error path needs no cancel: rx_urb_wq is INIT_WORK()'d there but cannot have been scheduled, since no URB is submitted before ieee80211_register_hw() succeeds.

This bug was found by static analysis.

---
 drivers/net/wireless/realtek/rtl8xxxu/core.c |   19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)
drivers/net/wireless/realtek/rtl8xxxu/core.c +14 -5
@@ -5885,14 +5885,19 @@ static void rtl8xxxu_queue_rx_urb(struct
 {
 	struct sk_buff *skb;
 	unsigned long flags;
-	int pending = 0;
 
 	spin_lock_irqsave(&priv->rx_urb_lock, flags);
 
 	if (!priv->shutdown) {
 		list_add_tail(&rx_urb->list, &priv->rx_urb_pending_list);
 		priv->rx_urb_pending_count++;
-		pending = priv->rx_urb_pending_count;
+		/*
+		 * Arm the worker under rx_urb_lock so this is atomic with the
+		 * shutdown check: moving it out of the lock would let a
+		 * completion arm the work after rtl8xxxu_stop() canceled it.
+		 */
+		if (priv->rx_urb_pending_count > RTL8XXXU_RX_URB_PENDING_WATER)
+			schedule_work(&priv->rx_urb_wq);
 	} else {
 		skb = (struct sk_buff *)rx_urb->urb.context;
 		dev_kfree_skb_irq(skb);
@@ -5900,9 +5905,6 @@ static void rtl8xxxu_queue_rx_urb(struct
 	}
 
 	spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
-
-	if (pending > RTL8XXXU_RX_URB_PENDING_WATER)
-		schedule_work(&priv->rx_urb_wq);
 }
 
 static void rtl8xxxu_rx_urb_work(struct work_struct *work)
@@ -7550,6 +7552,13 @@ static void rtl8xxxu_stop(struct ieee802
 	priv->shutdown = true;
 	spin_unlock_irqrestore(&priv->rx_urb_lock, flags);
 
+	/*
+	 * Cancel before killing rx_anchor: the worker re-anchors every URB
+	 * it drained via rtl8xxxu_submit_rx_urb(), so a worker still running
+	 * after the kill could submit a URB that escapes it.
+	 */
+	cancel_work_sync(&priv->rx_urb_wq);
+
 	usb_kill_anchored_urbs(&priv->rx_anchor);
 	usb_kill_anchored_urbs(&priv->tx_anchor);
 	if (priv->usb_interrupts)
author maintainer
#536 lore raw

PATCH 525/552 crypto: sun8i-ce - Remove crypto_rng interface

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eric Biggers <[email protected]>

commit 011556f71d094da61379ae3672692cae2795304e upstream.

Since the crypto_rng interface for hardware PRNGs is unused and is redundant with hwrng and the actual Linux RNG, it's being phased out. Most drivers for it were already removed. Go ahead and remove the sun8i-ce support which is one of the only remaining ones.

Note that the sun8i-ce support for hwrng remains in place. That is the interface that actually matters.

As usual for crypto_rng, this driver was also buggy: its ->generate() function had a use-after-free vulnerability due to using wait_for_completion_interruptible_timeout() without handling shutting down the DMA operation if a signal is sent. There's no point in fixing this separately only to remove the code anyway, so this commit is marked with Fixes and Cc stable.

Fixes 5eb7e9468884 ("crypto: sun8i-ce - Add support for the PRNG")
Cc Corentin Labbe <[email protected]>
---
 drivers/crypto/allwinner/Kconfig                  |    8 -
 drivers/crypto/allwinner/sun8i-ce/Makefile        |    1 
 drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c |   57 -------
 drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c |  159 ----------------------
 drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h      |   29 ----
 5 files changed, 254 deletions(-)
 delete mode 100644 drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c
drivers/crypto/allwinner/Kconfig +0 -8
@@ -70,14 +70,6 @@ config CRYPTO_DEV_SUN8I_CE_HASH
 	help
 	  Say y to enable support for hash algorithms.
 
-config CRYPTO_DEV_SUN8I_CE_PRNG
-	bool "Support for Allwinner Crypto Engine PRNG"
-	depends on CRYPTO_DEV_SUN8I_CE
-	select CRYPTO_RNG
-	help
-	  Select this option if you want to provide kernel-side support for
-	  the Pseudo-Random Number Generator found in the Crypto Engine.
-
 config CRYPTO_DEV_SUN8I_CE_TRNG
 	bool "Support for Allwinner Crypto Engine TRNG"
 	depends on CRYPTO_DEV_SUN8I_CE
drivers/crypto/allwinner/sun8i-ce/Makefile +0 -1
@@ -1,5 +1,4 @@
 obj-$(CONFIG_CRYPTO_DEV_SUN8I_CE) += sun8i-ce.o
 sun8i-ce-y += sun8i-ce-core.o sun8i-ce-cipher.o
 sun8i-ce-$(CONFIG_CRYPTO_DEV_SUN8I_CE_HASH) += sun8i-ce-hash.o
-sun8i-ce-$(CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG) += sun8i-ce-prng.o
 sun8i-ce-$(CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG) += sun8i-ce-trng.o
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c +0 -57

141 lines not on this page · raw message

drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c deleted +0 -159

159 lines not on this page · raw message

drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h +0 -29
@@ -15,7 +15,6 @@
 #include <linux/hw_random.h>
 #include <crypto/internal/hash.h>
 #include <crypto/md5.h>
-#include <crypto/rng.h>
 #include <crypto/sha1.h>
 #include <crypto/sha2.h>
 
@@ -58,9 +57,7 @@
 #define CE_ALG_SHA384           20
 #define CE_ALG_SHA512           21
 #define CE_ALG_TRNG		48
-#define CE_ALG_PRNG		49
 #define CE_ALG_TRNG_V2		0x1c
-#define CE_ALG_PRNG_V2		0x1d
 
 /* Used in ce_variant */
 #define CE_ID_NOTSUPP		0xFF
@@ -96,10 +93,6 @@
 #define ESR_H6	4
 #define ESR_D1	5
 
-#define PRNG_DATA_SIZE (160 / 8)
-#define PRNG_SEED_SIZE DIV_ROUND_UP(175, 8)
-#define PRNG_LD BIT(17)
-
 #define CE_DIE_ID_SHIFT	16
 #define CE_DIE_ID_MASK	0x07
 
@@ -136,13 +129,10 @@ struct ce_clock {
  *				bytes or words
  * @hash_t_dlen_in_bytes:	Does the request size for hash is in
  *				bits or words
- * @prng_t_dlen_in_bytes:	Does the request size for PRNG is in
- *				bytes or words
  * @trng_t_dlen_in_bytes:	Does the request size for TRNG is in
  *				bytes or words
  * @ce_clks:	list of clocks needed by this variant
  * @esr:	The type of error register
- * @prng:	The CE_ALG_XXX value for the PRNG
  * @trng:	The CE_ALG_XXX value for the TRNG
  */
 struct ce_variant {
@@ -151,12 +141,10 @@ struct ce_variant {
 	u32 op_mode[CE_ID_OP_MAX];
 	bool cipher_t_dlen_in_bytes;
 	bool hash_t_dlen_in_bits;
-	bool prng_t_dlen_in_bytes;
 	bool trng_t_dlen_in_bytes;
 	bool needs_word_addresses;
 	struct ce_clock ce_clks[CE_MAX_CLOCKS];
 	int esr;
-	unsigned char prng;
 	unsigned char trng;
 };
 
@@ -328,16 +316,6 @@ struct sun8i_ce_hash_reqctx {
 };
 
 /*
- * struct sun8i_ce_prng_ctx - context for PRNG TFM
- * @seed:	The seed to use
- * @slen:	The size of the seed
- */
-struct sun8i_ce_rng_tfm_ctx {
-	void *seed;
-	unsigned int slen;
-};
-
-/*
  * struct sun8i_ce_alg_template - crypto_alg template
  * @type:		the CRYPTO_ALG_TYPE for this template
  * @ce_algo_id:		the CE_ID for this template
@@ -357,7 +335,6 @@ struct sun8i_ce_alg_template {
 	union {
 		struct skcipher_engine_alg skcipher;
 		struct ahash_engine_alg hash;
-		struct rng_alg rng;
 	} alg;
 	unsigned long stat_req;
 	unsigned long stat_fb;
@@ -398,11 +375,5 @@ int sun8i_ce_hash_finup(struct ahash_req
 int sun8i_ce_hash_digest(struct ahash_request *areq);
 int sun8i_ce_hash_run(struct crypto_engine *engine, void *breq);
 
-int sun8i_ce_prng_generate(struct crypto_rng *tfm, const u8 *src,
-			   unsigned int slen, u8 *dst, unsigned int dlen);
-int sun8i_ce_prng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen);
-void sun8i_ce_prng_exit(struct crypto_tfm *tfm);
-int sun8i_ce_prng_init(struct crypto_tfm *tfm);
-
 int sun8i_ce_hwrng_register(struct sun8i_ce_dev *ce);
 void sun8i_ce_hwrng_unregister(struct sun8i_ce_dev *ce);
author maintainer
#537 lore raw

PATCH 526/552 crypto: sun8i-ss - Remove crypto_rng interface

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eric Biggers <[email protected]>

commit a78446ee6fae86ac8733f120e3ffce2e5d9384f5 upstream.

Since the crypto_rng interface for hardware PRNGs is unused and is redundant with hwrng and the actual Linux RNG, it's being phased out. Most drivers for it were already removed. Go ahead and remove the sun8i-ss support which is one of the only remaining ones.

As usual for crypto_rng, this driver was also buggy: its ->generate() function had a use-after-free vulnerability due to using wait_for_completion_interruptible_timeout() without handling shutting down the DMA operation if a signal is sent. Also, it had a buffer overread bug in the line 'memcpy(ctx->seed, d + dlen, ctx->slen);'. There's no point in fixing these bugs separately only to remove the code anyway, so this commit is marked with Fixes and Cc stable.

Fixes ac2614d721de ("crypto: sun8i-ss - Add support for the PRNG")
Cc Corentin Labbe <[email protected]>
---
 drivers/crypto/allwinner/Kconfig                  |    8 
 drivers/crypto/allwinner/sun8i-ss/Makefile        |    1 
 drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c |   39 ----
 drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c |  177 ----------------------
 drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h      |   23 --
 5 files changed, 248 deletions(-)
 delete mode 100644 drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c
drivers/crypto/allwinner/Kconfig +0 -8
@@ -105,14 +105,6 @@ config CRYPTO_DEV_SUN8I_SS_DEBUG
 	  This will create /sys/kernel/debug/sun8i-ss/stats for displaying
 	  the number of requests per flow and per algorithm.
 
-config CRYPTO_DEV_SUN8I_SS_PRNG
-	bool "Support for Allwinner Security System PRNG"
-	depends on CRYPTO_DEV_SUN8I_SS
-	select CRYPTO_RNG
-	help
-	  Select this option if you want to provide kernel-side support for
-	  the Pseudo-Random Number Generator found in the Security System.
-
 config CRYPTO_DEV_SUN8I_SS_HASH
 	bool "Enable support for hash on sun8i-ss"
 	depends on CRYPTO_DEV_SUN8I_SS
drivers/crypto/allwinner/sun8i-ss/Makefile +0 -1
@@ -1,4 +1,3 @@
 obj-$(CONFIG_CRYPTO_DEV_SUN8I_SS) += sun8i-ss.o
 sun8i-ss-y += sun8i-ss-core.o sun8i-ss-cipher.o
-sun8i-ss-$(CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG) += sun8i-ss-prng.o
 sun8i-ss-$(CONFIG_CRYPTO_DEV_SUN8I_SS_HASH) += sun8i-ss-hash.o
drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c +0 -39
@@ -11,7 +11,6 @@
  */
 
 #include <crypto/engine.h>
-#include <crypto/internal/rng.h>
 #include <crypto/internal/skcipher.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
@@ -283,25 +282,6 @@ static struct sun8i_ss_alg_template ss_a
 		.do_one_request = sun8i_ss_handle_cipher_request,
 	},
 },
-#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG
-{
-	.type = CRYPTO_ALG_TYPE_RNG,
-	.alg.rng = {
-		.base = {
-			.cra_name		= "stdrng",
-			.cra_driver_name	= "sun8i-ss-prng",
-			.cra_priority		= 300,
-			.cra_ctxsize = sizeof(struct sun8i_ss_rng_tfm_ctx),
-			.cra_module		= THIS_MODULE,
-			.cra_init		= sun8i_ss_prng_init,
-			.cra_exit		= sun8i_ss_prng_exit,
-		},
-		.generate               = sun8i_ss_prng_generate,
-		.seed                   = sun8i_ss_prng_seed,
-		.seedsize               = PRNG_SEED_SIZE,
-	}
-},
-#endif
 #ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_HASH
 {	.type = CRYPTO_ALG_TYPE_AHASH,
 	.ss_algo_id = SS_ID_HASH_MD5,
@@ -501,12 +481,6 @@ static int sun8i_ss_debugfs_show(struct
 			seq_printf(seq, "\tFallback due to SG numbers: %lu\n",
 				   ss_algs[i].stat_fb_sgnum);
 			break;
-		case CRYPTO_ALG_TYPE_RNG:
-			seq_printf(seq, "%s %s reqs=%lu tsize=%lu\n",
-				   ss_algs[i].alg.rng.base.cra_driver_name,
-				   ss_algs[i].alg.rng.base.cra_name,
-				   ss_algs[i].stat_req, ss_algs[i].stat_bytes);
-			break;
 		case CRYPTO_ALG_TYPE_AHASH:
 			seq_printf(seq, "%s %s reqs=%lu fallback=%lu\n",
 				   ss_algs[i].alg.hash.base.halg.base.cra_driver_name,
@@ -707,14 +681,6 @@ static int sun8i_ss_register_algs(struct
 				return err;
 			}
 			break;
-		case CRYPTO_ALG_TYPE_RNG:
-			err = crypto_register_rng(&ss_algs[i].alg.rng);
-			if (err) {
-				dev_err(ss->dev, "Fail to register %s\n",
-					ss_algs[i].alg.rng.base.cra_name);
-				ss_algs[i].ss = NULL;
-			}
-			break;
 		case CRYPTO_ALG_TYPE_AHASH:
 			id = ss_algs[i].ss_algo_id;
 			ss_method = ss->variant->alg_hash[id];
@@ -756,11 +722,6 @@ static void sun8i_ss_unregister_algs(str
 				 ss_algs[i].alg.skcipher.base.base.cra_name);
 			crypto_engine_unregister_skcipher(&ss_algs[i].alg.skcipher);
 			break;
-		case CRYPTO_ALG_TYPE_RNG:
-			dev_info(ss->dev, "Unregister %d %s\n", i,
-				 ss_algs[i].alg.rng.base.cra_name);
-			crypto_unregister_rng(&ss_algs[i].alg.rng);
-			break;
 		case CRYPTO_ALG_TYPE_AHASH:
 			dev_info(ss->dev, "Unregister %d %s\n", i,
 				 ss_algs[i].alg.hash.base.halg.base.cra_name);
drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c deleted +0 -177

177 lines not on this page · raw message

drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h +0 -23
@@ -8,7 +8,6 @@
 #include <crypto/aes.h>
 #include <crypto/des.h>
 #include <crypto/engine.h>
-#include <crypto/rng.h>
 #include <crypto/skcipher.h>
 #include <linux/atomic.h>
 #include <linux/debugfs.h>
@@ -27,7 +26,6 @@
 #define SS_ALG_DES		(1 << 2)
 #define SS_ALG_3DES		(2 << 2)
 #define SS_ALG_MD5		(3 << 2)
-#define SS_ALG_PRNG		(4 << 2)
 #define SS_ALG_SHA1		(6 << 2)
 #define SS_ALG_SHA224		(7 << 2)
 #define SS_ALG_SHA256		(8 << 2)
@@ -68,8 +66,6 @@
 #define SS_FLOW0	BIT(30)
 #define SS_FLOW1	BIT(31)
 
-#define SS_PRNG_CONTINUE	BIT(18)
-
 #define MAX_SG 8
 
 #define MAXFLOW 2
@@ -79,9 +75,6 @@
 #define SS_DIE_ID_SHIFT	20
 #define SS_DIE_ID_MASK	0x07
 
-#define PRNG_DATA_SIZE (160 / 8)
-#define PRNG_SEED_SIZE DIV_ROUND_UP(175, 8)
-
 #define MAX_PAD_SIZE 4096
 
 /*
@@ -214,16 +207,6 @@ struct sun8i_cipher_tfm_ctx {
 };
 
 /*
- * struct sun8i_ss_prng_ctx - context for PRNG TFM
- * @seed:	The seed to use
- * @slen:	The size of the seed
- */
-struct sun8i_ss_rng_tfm_ctx {
-	void *seed;
-	unsigned int slen;
-};
-
-/*
  * struct sun8i_ss_hash_tfm_ctx - context for an ahash TFM
  * @fallback_tfm:	pointer to the fallback TFM
  * @ss:			pointer to the private data of driver handling this TFM
@@ -272,7 +255,6 @@ struct sun8i_ss_alg_template {
 	struct sun8i_ss_dev *ss;
 	union {
 		struct skcipher_engine_alg skcipher;
-		struct rng_alg rng;
 		struct ahash_engine_alg hash;
 	} alg;
 	unsigned long stat_req;
@@ -298,11 +280,6 @@ int sun8i_ss_skencrypt(struct skcipher_r
 int sun8i_ss_get_engine_number(struct sun8i_ss_dev *ss);
 
 int sun8i_ss_run_task(struct sun8i_ss_dev *ss, struct sun8i_cipher_req_ctx *rctx, const char *name);
-int sun8i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src,
-			   unsigned int slen, u8 *dst, unsigned int dlen);
-int sun8i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen);
-int sun8i_ss_prng_init(struct crypto_tfm *tfm);
-void sun8i_ss_prng_exit(struct crypto_tfm *tfm);
 
 int sun8i_ss_hash_init_tfm(struct crypto_ahash *tfm);
 void sun8i_ss_hash_exit_tfm(struct crypto_ahash *tfm);
author maintainer
#538 lore raw

PATCH 527/552 wifi: mwifiex: Detach sync cmd buffer on interrupted wait

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Fabio Estevam <[email protected]>

commit ef06882c7d8a7400b67d0d003b1008093dd589ed upstream.

mwifiex synchronous commands keep the caller-provided data buffer in cmd_node->data_buf. Several callers pass stack-allocated objects there.

If wait_event_interruptible_timeout() is interrupted, the caller can return and release that stack object while the firmware command is still the current command. A late firmware response then reaches the normal response handler, which can copy data through cmd_node->data_buf into the stale stack address.

This fixes a stack corruption observed during repeated association and disassociation cycles. The panic trace showed the command wait being interrupted immediately before a bad pointer dereference:

cmd_wait_q terminated: -512

  Unable to handle kernel paging request at virtual address 002c583837384662
  Kernel panic - not syncing: stack-protector: Kernel stack is corrupted
  ...
  Tainted: [M]=MACHINE_CHECK

The fault address decodes as little-endian ASCII:

0x002c583837384662 -> "bF878X,\0"

which is a fragment of the VERSION_EXT firmware string exposed as debugfs "verext":

w8997o-V4, RF878X, FP92, 16.92.21.p153.7

The same runs also showed corrupted control data containing:

0x2400372e333531 -> "153.7\0$"

which is the tail of the same VERSION_EXT string. This points at a late VERSION_EXT response writing through a stale stack-backed data_buf after the interrupted wait returned.

After cancelling pending commands on an interrupted or timed-out wait, detach the caller-owned data buffer from the still-current command. This preserves the existing command cancellation behaviour while preventing a late response from writing through a pointer whose lifetime ended with the waiting caller.

Tested on an i.MX8MP board using an 88W8997.

---
 drivers/net/wireless/marvell/mwifiex/sta_ioctl.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +12 -0
@@ -57,6 +57,18 @@ int mwifiex_wait_queue_complete(struct m
 		mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n",
 			    status);
 		mwifiex_cancel_all_pending_cmd(adapter);
+
+		/* The command response path writes through cmd_node->data_buf.
+		 * On an interrupted wait, the caller can return and release a
+		 * stack-allocated data_buf before a late firmware response is
+		 * processed. Detach the caller-owned buffer from the current
+		 * command so a late response cannot corrupt freed stack memory.
+		 */
+		spin_lock_bh(&adapter->mwifiex_cmd_lock);
+		if (adapter->curr_cmd == cmd_queued)
+			adapter->curr_cmd->data_buf = NULL;
+		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
+
 		return status;
 	}
 
author maintainer
#539 lore raw

PATCH 528/552 wifi: rtl818x: initialize eeprom_93cx6 struct to zero

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Stanislaw Gruszka <[email protected]>

commit 799b5f45cb8194ebd06c9c89e0afdad5bedd2cc5 upstream.

Commit 7738a7ab9d12 ("misc: eeprom: eeprom_93cx6: Add quirk for extra read clock cycle") added extra 'quirk' field to struct eeprom_93cx6.

Many existing users of eeprom_93cx6, including rtl818x drivers, allocate the structure on the stack without initializing all fields. As a result, the added quirk field has an undefined value and can randomly cause reading wrong data from the EEPROM.

Fix by initializing the structures with {}.

Fixes 7738a7ab9d12 ("misc: eeprom: eeprom_93cx6: Add quirk for extra read clock cycle")
Cc [email protected] # v6.13+
---
 drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c |    2 +-
 drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c +1 -1
@@ -1652,7 +1652,7 @@ static void rtl8180_eeprom_register_writ
 
 static void rtl8180_eeprom_read(struct rtl8180_priv *priv)
 {
-	struct eeprom_93cx6 eeprom;
+	struct eeprom_93cx6 eeprom = {};
 	int eeprom_cck_table_adr;
 	u16 eeprom_val;
 	int i;
drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +1 -1
@@ -1445,7 +1445,7 @@ static int rtl8187_probe(struct usb_inte
 	struct usb_device *udev = interface_to_usbdev(intf);
 	struct ieee80211_hw *dev;
 	struct rtl8187_priv *priv;
-	struct eeprom_93cx6 eeprom;
+	struct eeprom_93cx6 eeprom = {};
 	struct ieee80211_channel *channel;
 	const char *chip_name;
 	u16 txpwr, reg;
author maintainer
#540 lore raw

PATCH 529/552 wifi: rtlwifi: rtl8192du: check QoS TID before indexing tids

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Runyu Xiao <[email protected]>

commit ed4f05d9f2f42fd866f55108db8123eefcc5fb33 upstream.

rtl92du_tx_fill_desc() uses ieee80211_get_tid() to read the QoS TID from the 802.11 header and then uses it as an index into sta_entry->tids[]. ieee80211_get_tid() returns the low 4-bit QoS TID value, so the result can be in the range 0..15.

rtlwifi only allocates MAX_TID_COUNT entries for sta_entry->tids[], and MAX_TID_COUNT is 9. A QoS TID greater than 8 therefore indexes past the aggregation state array. Keep the default RTL_AGG_STOP state for out-of-range TIDs, matching rtl92cu_tx_fill_desc().

This issue was detected by our static analysis tool and confirmed by manual audit. UBSAN validation for the same bug pattern reports an array-index-out-of-bounds access with index 10 for type 'rtl_tid_data [9]'.

---
 drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c +2 -1
@@ -106,7 +106,8 @@ void rtl92du_tx_fill_desc(struct ieee802
 	if (sta) {
 		sta_entry = (struct rtl_sta_info *)sta->drv_priv;
 		tid = ieee80211_get_tid(hdr);
-		agg_state = sta_entry->tids[tid].agg.agg_state;
+		if (tid < MAX_TID_COUNT)
+			agg_state = sta_entry->tids[tid].agg.agg_state;
 		ampdu_density = sta->deflink.ht_cap.ampdu_density;
 	}
 
author maintainer
#541 lore raw

PATCH 530/552 wifi: rtlwifi: rtl8192du: Fix possible memory leak in rtl92du_init_sw_vars()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Abdun Nihaal <[email protected]>

commit 6496ce90845df2d22fb8e8ed235cd2936fad41c8 upstream.

The memory allocated inside rtl92du_init_shared_data() is not freed in any of the subsequent error paths in rtl92du_init_sw_vars(). Fix that by adding a call to rtl92du_deinit_shared_data() in the error path.

---
 drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c |   12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)
drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c +9 -3
@@ -147,8 +147,10 @@ static int rtl92du_init_sw_vars(struct i
 
 	/* for firmware buf */
 	rtlpriv->rtlhal.pfirmware = kmalloc(0x8000, GFP_KERNEL);
-	if (!rtlpriv->rtlhal.pfirmware)
-		return -ENOMEM;
+	if (!rtlpriv->rtlhal.pfirmware) {
+		err = -ENOMEM;
+		goto error;
+	}
 
 	rtlpriv->max_fw_size = 0x8000;
 	pr_info("Driver for Realtek RTL8192DU WLAN interface\n");
@@ -162,10 +164,14 @@ static int rtl92du_init_sw_vars(struct i
 		pr_err("Failed to request firmware!\n");
 		kfree(rtlpriv->rtlhal.pfirmware);
 		rtlpriv->rtlhal.pfirmware = NULL;
-		return err;
+		goto error;
 	}
 
 	return 0;
+
+error:
+	rtl92du_deinit_shared_data(hw);
+	return err;
 }
 
 static void rtl92du_deinit_sw_vars(struct ieee80211_hw *hw)
author maintainer
#542 lore raw

PATCH 531/552 wifi: rtw88: Fix potential memory leak in rtw_txq_push_skb()

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Abdun Nihaal <[email protected]>

commit 9f2948010764d708bda27369d09ce6f194abe8e3 upstream.

The skb passed to the rtw_hci_tx_write() is expected to be freed when the function fails, but the error path in rtw_txq_push_skb() does not free the skb before returning. This can lead to a memory leak in rtw_txq_push() where a dequeued skb is passed to rtw_txq_push_skb().

---
 drivers/net/wireless/realtek/rtw88/tx.c |    1 +
 1 file changed, 1 insertion(+)
drivers/net/wireless/realtek/rtw88/tx.c +1 -0
@@ -619,6 +619,7 @@ static int rtw_txq_push_skb(struct rtw_d
 	ret = rtw_hci_tx_write(rtwdev, &pkt_info, skb);
 	if (ret) {
 		rtw_err(rtwdev, "failed to write TX skb to HCI\n");
+		ieee80211_free_txskb(rtwdev->hw, skb);
 		return ret;
 	}
 	return 0;
author maintainer
#543 lore raw

PATCH 532/552 wifi: rtw88: pci: fix resource leak on failed NAPI setup

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Dawei Feng <[email protected]>

commit e779df4806cd29cbcca5c9dc0a1073662c76b889 upstream.

rtw_pci_probe() allocates PCI resources through rtw_pci_setup_resource() before it sets up NAPI. If rtw_pci_napi_init() fails, the error path jumps straight to err_pci_declaim and skips rtw_pci_destroy(), leaving the PCI resources allocated by rtw_pci_setup_resource() behind.

Add a dedicated cleanup label for the NAPI setup failure path so probe destroys the PCI resources.

The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing current mainline kernels. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc7.

An x86_64 allyesconfig build showed no new warnings. As we do not have a suitable rtw88 PCI board to test with, no runtime testing was able to be performed.

---
 drivers/net/wireless/realtek/rtw88/pci.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/wireless/realtek/rtw88/pci.c +3 -1
@@ -1799,7 +1799,7 @@ int rtw_pci_probe(struct pci_dev *pdev,
 	ret = rtw_pci_napi_init(rtwdev);
 	if (ret) {
 		rtw_err(rtwdev, "failed to setup NAPI\n");
-		goto err_pci_declaim;
+		goto err_destroy_rsrc;
 	}
 
 	ret = rtw_chip_info_setup(rtwdev);
@@ -1831,6 +1831,8 @@ int rtw_pci_probe(struct pci_dev *pdev,
 
 err_destroy_pci:
 	rtw_pci_napi_deinit(rtwdev);
+
+err_destroy_rsrc:
 	rtw_pci_destroy(rtwdev, pdev);
 
 err_pci_declaim:
author maintainer
#544 lore raw

PATCH 533/552 wifi: mt76: mt7615: avoid waiting for mac work under the mt76 mutex

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Runyu Xiao <[email protected]>

commit bda8324270b1ac91bfba1df8928e0570e29759e8 upstream.

mt7615_suspend() acquired the mt76 mutex and then called cancel_delayed_work_sync() on mac_work. mt7615_mac_work() acquires the same mutex via mt7615_mutex_acquire() at the top of the worker, so if mac_work is already running and blocked on the mutex, the suspend path deadlocks waiting for the work it holds the mutex against.

Flush scan_work and mac_work before taking the mutex, matching the suspend paths in mt7921 and mt7925. scan_work only takes the mt76 spinlock, but moving it keeps the sequence consistent. This also keeps mac_work from running over an already suspended HIF, which the previous split (async cancel under the lock, sync cancel after release) would have allowed.

---
 drivers/net/wireless/mediatek/mt76/mt7615/main.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7615/main.c +3 -2
@@ -1239,11 +1239,12 @@ static int mt7615_suspend(struct ieee802
 	cancel_delayed_work_sync(&dev->pm.ps_work);
 	mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
 
+	cancel_delayed_work_sync(&phy->scan_work);
+	cancel_delayed_work_sync(&phy->mt76->mac_work);
+
 	mt7615_mutex_acquire(dev);
 
 	clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
-	cancel_delayed_work_sync(&phy->scan_work);
-	cancel_delayed_work_sync(&phy->mt76->mac_work);
 
 	set_bit(MT76_STATE_SUSPEND, &phy->mt76->state);
 	ieee80211_iterate_active_interfaces(hw,
author maintainer
#545 lore raw

PATCH 534/552 wifi: mt76: mt7915: bound the device EEPROM address before the EFUSE copy

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Bryam Vargas <[email protected]>

commit 44b5adfe49499f53002737f5fe81d608c08122fc upstream.

mt7915_mcu_get_eeprom() copies a fixed EFUSE block into the driver's dev->mt76.eeprom.data buffer at the offset reported by the MCU response (res->addr, a device-controlled __le32) without checking it against the buffer size. A malicious or malfunctioning device can report an arbitrary address and drive a 16-byte out-of-bounds write past eeprom.data.

Reject a response whose address would place the copy outside eeprom.data before deriving the destination pointer. Devices that echo the requested in-bounds offset are unaffected.

Fixes e57b7901469f ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets")
---
 drivers/net/wireless/mediatek/mt76/mt7915/mcu.c |   11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +9 -2
@@ -2917,8 +2917,15 @@ int mt7915_mcu_get_eeprom(struct mt7915_
 		return ret;
 
 	res = (struct mt7915_mcu_eeprom_info *)skb->data;
-	if (!buf)
-		buf = dev->mt76.eeprom.data + le32_to_cpu(res->addr);
+	if (!buf) {
+		u32 addr = le32_to_cpu(res->addr);
+
+		if (addr > dev->mt76.eeprom.size - MT7915_EEPROM_BLOCK_SIZE) {
+			dev_kfree_skb(skb);
+			return -EINVAL;
+		}
+		buf = dev->mt76.eeprom.data + addr;
+	}
 	memcpy(buf, res->data, MT7915_EEPROM_BLOCK_SIZE);
 
 	dev_kfree_skb(skb);
author maintainer
#546 lore raw

PATCH 535/552 wifi: mt76: mt7925: cancel mlo_pm_work on stop

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Devin Wittmayer <[email protected]>

commit 81faf578320df2dfc682a96baa6e85851dd68b6f upstream.

mt7925 queues mlo_pm_work with a 5 second delay during multi-link power-save setup and never cancels it on the stop path. If the device is torn down inside that window, the work outlives the teardown and its timer fires afterwards, trying to queue onto the workqueue that is already gone:

workqueue: cannot queue mt7925_mlo_pm_work [mt7925_common] on wq phy0

  WARNING: kernel/workqueue.c:2283 at __queue_work+0x59/0xa0, CPU#1: swapper/1/0
   call_timer_fn+0x2a/0x140
   __run_timers+0x203/0x330
   run_timer_softirq+0x86/0xf0

mt7921 already has its own stop callback, so add one for mt7925 that cancels the work before calling mt792x_stop(). mt7925_ops backs both the PCIe and USB drivers, so this covers both.

---
 drivers/net/wireless/mediatek/mt76/mt7925/main.c |   11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7925/main.c +10 -1
@@ -2181,10 +2181,19 @@ static void mt7925_rfkill_poll(struct ie
 	wiphy_rfkill_set_hw_state(hw->wiphy, ret == 0);
 }
 
+static void mt7925_stop(struct ieee80211_hw *hw, bool suspend)
+{
+	struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+	cancel_delayed_work_sync(&dev->mlo_pm_work);
+
+	mt792x_stop(hw, suspend);
+}
+
 const struct ieee80211_ops mt7925_ops = {
 	.tx = mt792x_tx,
 	.start = mt7925_start,
-	.stop = mt792x_stop,
+	.stop = mt7925_stop,
 	.add_interface = mt7925_add_interface,
 	.remove_interface = mt792x_remove_interface,
 	.config = mt7925_config,
author maintainer
#547 lore raw

PATCH 536/552 wifi: mt76: mt7996: fix TX DMA mapping leak for AddBA req frames

In reply to Greg Kroah-Hartman

6.18-stable review patch. If anyone has any objections, please let me know.

------------------

From: Felix Fietkau <[email protected]>

commit deaa2e3656937fbbe312f0ee2616c756c6e2511f upstream.

mt7996/mt7992 hand the firmware a HW MAC-TXP for AddBA req action frames (MT_TXD7_MAC_TXD, set in mt7996_mac_write_txwi_80211()), but are otherwise FW-TXP devices. On tx free mt76_connac_txp_skb_unmap() therefore decodes the per-frame txp as a struct mt76_connac_fw_txp. For a MAC-TXP the fw_txp.nbuf byte aliases the AddBA TID word (MT_TXP1_TID_ADDBA), which is always zero, so the unmap loop runs zero times and the skb DMA mapping in buf[1] is never unmapped. buf[1].skip_unmap is set unconditionally, so the generic DMA-ring cleanup skips it as well.

Each AddBA req therefore leaks one TX DMA mapping, roughly one per (re)association. With WED enabled these mappings are bounced through the WED swiotlb pool, so under continuous client reconnect churn the pool is exhausted after ~1-2 days, after which DMA mapping fails for WED, the WiFi MCU and other on-SoC consumers.

Keep the deferred (token release) unmap that the design relies on, and add an mt7996-specific txp unmap that inspects MT_TXD7_MAC_TXD and unmaps buf[1] from the MAC-TXP layout for those frames, delegating to mt76_connac_txp_skb_unmap() otherwise.

Fixes cb6ebbdffef2 ("wifi: mt76: mt7996: support writing MAC TXD for AddBA Request")
---
 drivers/net/wireless/mediatek/mt76/mt7996/mac.c |   26 +++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7996/mac.c +25 -1
@@ -1279,6 +1279,30 @@ mt7996_tx_check_aggr(struct ieee80211_li
 }
 
 static void
+mt7996_txp_skb_unmap(struct mt76_dev *mdev, struct mt76_txwi_cache *t)
+{
+	u8 *txwi_ptr = mt76_get_txwi_ptr(mdev, t);
+	__le32 *txwi = (__le32 *)txwi_ptr;
+	__le32 *txp;
+	dma_addr_t addr;
+	u32 val;
+
+	if (!(le32_to_cpu(txwi[7]) & MT_TXD7_MAC_TXD)) {
+		mt76_connac_txp_skb_unmap(mdev, t);
+		return;
+	}
+
+	txp = (__le32 *)(txwi_ptr + MT_TXD_SIZE);
+	val = le32_to_cpu(txp[3]);
+	addr = le32_to_cpu(txp[2]);
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+	addr |= (dma_addr_t)FIELD_GET(MT_TXP3_DMA_ADDR_H, val) << 32;
+#endif
+	dma_unmap_single(mdev->dma_dev, addr, FIELD_GET(MT_TXP_BUF_LEN, val),
+			 DMA_TO_DEVICE);
+}
+
+static void
 mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
 		 struct ieee80211_link_sta *link_sta,
 		 struct mt76_wcid *wcid, struct list_head *free_list)
@@ -1287,7 +1311,7 @@ mt7996_txwi_free(struct mt7996_dev *dev,
 	__le32 *txwi;
 	u16 wcid_idx;
 
-	mt76_connac_txp_skb_unmap(mdev, t);
+	mt7996_txp_skb_unmap(mdev, t);
 	if (!t->skb)
 		goto out;