Skip to contentSkip to conversation

7.2.10-rc1 review

PATCH In review Greg Kroah-Hartman 15 participants 447 messages Oct 9 – Oct 16 Stable branch Page 3 of 4

lore
Share & tools
lore
author maintainer
#271 lore raw

PATCH 269/428 ALSA: ua101: reject mismatched capture/playback packet sizes

In reply to Greg Kroah-Hartman

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

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

From: Xiang Mei <[email protected]>

commit 86f86e6fdedc33c5b8d38b9208b350f8accf57ee upstream.

detect_usb_format() cross-checks bSubframeSize, bBitResolution and tSamFreq between the capture and playback interfaces, but never relates the two endpoints' wMaxPacketSize and bNrChannels. Each playback URB gets a buffer of ua->playback.max_packet_bytes, while the number of bytes written into it is derived from the capture stream:
capture_urb_complete() computes frames from the received capture packet and capture.frame_bytes, and start_usb_playback() and playback_work() multiply that by playback.frame_bytes.

A device declaring a large capture wMaxPacketSize with few capture channels and a small playback wMaxPacketSize with many playback channels therefore memset()s and memcpy()s past the end of the playback buffer, in open() of the PCM node the driver registers during probe. usb_submit_urb() rejects the over-long iso_frame_desc[0].length with -EMSGSIZE, but only after the write.

Reject such descriptors at probe time. Genuine UA-101/UA-1000 hardware declares proportional packet sizes and is unaffected.

  BUG: KASAN: slab-out-of-bounds in start_usb_playback (sound/usb/misc/ua101.c:586)
  Write of size 2048 at addr ffff8881098f3c00 by task exploit/5021
  Call Trace:
   __asan_memset (mm/kasan/shadow.c:84)
   start_usb_playback (sound/usb/misc/ua101.c:586)
   playback_pcm_open (sound/usb/misc/ua101.c:679)
   snd_pcm_open_substream (sound/core/pcm_native.c:2829)
   snd_pcm_open (sound/core/pcm_native.c:2865 sound/core/pcm_native.c:2932)
   snd_pcm_playback_open (sound/core/pcm_native.c:2891)
   snd_open (sound/core/sound.c:166)
   chrdev_open (fs/char_dev.c:411)
   do_dentry_open (fs/open.c:996)
   vfs_open (fs/open.c:1101)
   path_openat (fs/namei.c:4837 fs/namei.c:5000)
   do_file_open (fs/namei.c:5029)
   do_sys_openat2 (fs/open.c:1417)
   __x64_sys_openat (fs/open.c:1423 fs/open.c:1439 fs/open.c:1434)
   do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84)
   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)

  The buggy address belongs to the object at ffff8881098f3c00
   which belongs to the cache kmalloc-192 of size 192
  The buggy address is located 0 bytes inside of
   allocated 168-byte region [ffff8881098f3c00, ffff8881098f3ca8)
Fixes 63978ab3e3e9 ("sound: add Edirol UA-101 support")
Assisted-by LLM
---
 sound/usb/misc/ua101.c |    9 +++++++++
 1 file changed, 9 insertions(+)
sound/usb/misc/ua101.c +9 -0
@@ -1002,6 +1002,15 @@ static int detect_usb_format(struct ua10
 	}
 	ua->playback.usb_pipe = usb_sndisocpipe(ua->dev, usb_endpoint_num(epd));
 	ua->playback.max_packet_bytes = usb_endpoint_maxp(epd);
+
+	if (ua->capture.max_packet_bytes / ua->capture.frame_bytes *
+	    ua->playback.frame_bytes > ua->playback.max_packet_bytes) {
+		dev_err(&ua->dev->dev,
+			"playback packet size %u too small for %u capture frames\n",
+			ua->playback.max_packet_bytes,
+			ua->capture.max_packet_bytes / ua->capture.frame_bytes);
+		return -ENXIO;
+	}
 	return 0;
 }
 
author maintainer
#272 lore raw

PATCH 270/428 ALSA: hda/realtek: Fix square wave output on ASUS Strix G615LR

In reply to Greg Kroah-Hartman

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

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

From: Zhang Heng <[email protected]>

commit cfc0a117d773eb585ca7e87d20c50d1c415058e5 upstream.

ASUS ROG Strix G16 G615LR (PCI SSID 1043:3f20) outputs a loud full-scale square wave from internal speakers since the TAS2781 amplifier driver started loading. The quirk was using ALC287_FIXUP_TXNW2781_I2C which chains to the ThinkPad headset jack fixup, but this is an ASUS device that should use ALC287_FIXUP_TXNW2781_I2C_ASUS which chains to ALC294_FIXUP_ASUS_SPK for proper EAPD and pin configuration.

---
 sound/hda/codecs/realtek/alc269.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
sound/hda/codecs/realtek/alc269.c +1 -1
@@ -7682,7 +7682,7 @@ static const struct hda_quirk alc269_fix
 	SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C),
 	SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TXNW2781_I2C),
 	SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C),
-	SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TXNW2781_I2C),
+	SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TXNW2781_I2C_ASUS),
 	SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TXNW2781_I2C),
 	SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2),
 	SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2),
author maintainer
#273 lore raw

PATCH 271/428 ALSA: hda/realtek: Fix silent speaker on Higole F9B

In reply to Greg Kroah-Hartman

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

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

From: Yu-Hsiang Tseng <[email protected]>

commit 2a8ee6897302ca12ff39e8eb180503956797c60c upstream.

The Higole F9B is an Alder Lake-N mini PC with a 7" touchscreen and an ALC269VC codec (subsystem ID 0x10ec111e). Its DMI strings are all "Default string", so the SSID is the only way to identify it.

The internal speaker is silent out of the box. The generic parser assigns DAC 0x03 to the speaker pin 0x14 and DAC 0x02 to the headphone pin 0x15, but the speaker only reproduces what DAC 0x02 plays, whatever its connection selection says. Checked one path at a time, only the gain of DAC 0x02 and the mute of pin 0x14 change what comes out of the speaker. User space only knows the path it was told the speaker uses, so selecting the speaker turns the path that actually reaches it all the way down.

Route both 0x14 and 0x15 through mixer 0x0c (DAC 0x02), which is what ALC290_FIXUP_MONO_SPEAKERS already does. Tested on the device: the speaker plays.

---
 sound/hda/codecs/realtek/alc269.c |    1 +
 1 file changed, 1 insertion(+)
sound/hda/codecs/realtek/alc269.c +1 -0
@@ -7705,6 +7705,7 @@ static const struct hda_quirk alc269_fix
 	SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN),
 	SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC),
 	SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE),
+	SND_PCI_QUIRK(0x10ec, 0x111e, "Higole F9B", ALC290_FIXUP_MONO_SPEAKERS),
 	SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE),
 	SND_PCI_QUIRK(0x10ec, 0x119e, "Positivo SU C1400", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
 	SND_PCI_QUIRK(0x10ec, 0x11bc, "VAIO VJFE-IL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
author maintainer
#274 lore raw

PATCH 272/428 ALSA: hda/realtek: Add micmute LED quirk for Acer Swift SFG16-72

In reply to Greg Kroah-Hartman

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

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

From: Jakub Gawlik <[email protected]>

commit 819f6aacbb1d1621c6a62721e708835fe1fb75d8 upstream.

The Acer Swift SFG16-72 (1025:173b) uses the ALC245 codec, but its mic-mute LED is not enabled by default. Apply the existing ALC245_FIXUP_ACER_MICMUTE_LED quirk to enable the mic-mute LED.

---
 sound/hda/codecs/realtek/alc269.c |    1 +
 1 file changed, 1 insertion(+)
sound/hda/codecs/realtek/alc269.c +1 -0
@@ -6975,6 +6975,7 @@ static const struct hda_quirk alc269_fix
 	SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED),
 	SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED),
 	SND_PCI_QUIRK(0x1025, 0x173a, "Acer Swift SFG14-73", ALC245_FIXUP_ACER_MICMUTE_LED),
+	SND_PCI_QUIRK(0x1025, 0x173b, "Acer Swift SFG16-72", ALC245_FIXUP_ACER_MICMUTE_LED),
 	SND_PCI_QUIRK(0x1025, 0x1758, "Acer Nitro ANV15-41", ALC245_FIXUP_ACER_MICMUTE_LED),
 	SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2),
 	SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2),
author maintainer
#275 lore raw

PATCH 273/428 EDAC/versalnet: Drop remote processor handle refcount on driver removal

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit 5f43a2d35d5e748c09a50a98fc766dc95bb3a6bd upstream.

mc_probe() acquires a reference to the remote processor with rproc_get_by_phandle(), but mc_remove() does not release the reference.

rproc_shutdown() only balances the power reference acquired by rproc_boot(); it does not drop the device reference acquired by rproc_get_by_phandle(). As a result, successful driver removal leaves the remoteproc reference unbalanced.

Call rproc_put() during removal to release the reference acquired in mc_probe().

[ bp: Massage commit message. ]

---
 drivers/edac/versalnet_edac.c |    1 +
 1 file changed, 1 insertion(+)
drivers/edac/versalnet_edac.c +1 -0
@@ -953,6 +953,7 @@ static void mc_remove(struct platform_de
 	remove_versalnet(priv);
 	rproc_shutdown(priv->mcdi->r5_rproc);
 	cdx_mcdi_finish(priv->mcdi);
+	rproc_put(priv->mcdi->r5_rproc);
 	kfree(priv->mcdi);
 }
 
author maintainer
#276 lore raw

PATCH 274/428 EDAC/altera: Do not allow driver unbinding

In reply to Greg Kroah-Hartman

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

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

From: Dinh Nguyen <[email protected]>

commit 19a4f1a6ed00286d70229f4fd5f690cc2fb033dc upstream.

The driver must remain bound; unbinding and re-binding it would erase active system memory.

Remove the .remove functions because they will not ever get used.

Fixes 588cb03ea208 ("EDAC, altera: Add Arria10 L2 Cache ECC handling")
---
 drivers/edac/altera_edac.c |   25 ++++---------------------
 1 file changed, 4 insertions(+), 21 deletions(-)
drivers/edac/altera_edac.c +4 -21
@@ -453,15 +453,6 @@ free:
 	return res;
 }
 
-static void altr_sdram_remove(struct platform_device *pdev)
-{
-	struct mem_ctl_info *mci = platform_get_drvdata(pdev);
-
-	edac_mc_del_mc(&pdev->dev);
-	edac_mc_free(mci);
-	platform_set_drvdata(pdev, NULL);
-}
-
 /*
  * If you want to suspend, need to disable EDAC by removing it
  * from the device tree or defconfig.
@@ -481,13 +472,13 @@ static const struct dev_pm_ops altr_sdra
 
 static struct platform_driver altr_sdram_edac_driver = {
 	.probe = altr_sdram_probe,
-	.remove = altr_sdram_remove,
 	.driver = {
 		.name = "altr_sdram_edac",
 #ifdef CONFIG_PM
 		.pm = &altr_sdram_pm_ops,
 #endif
 		.of_match_table = altr_sdram_ctrl_of_match,
+		.suppress_bind_attrs = true,
 	},
 };
 
@@ -517,6 +508,7 @@ static struct platform_driver altr_edac_
 	.driver = {
 		.name = "socfpga_ecc_manager",
 		.of_match_table = altr_edac_of_match,
+		.suppress_bind_attrs = true,
 	},
 };
 module_platform_driver(altr_edac_driver);
@@ -803,22 +795,12 @@ fail:
 	return res;
 }
 
-static void altr_edac_device_remove(struct platform_device *pdev)
-{
-	struct edac_device_ctl_info *dci = platform_get_drvdata(pdev);
-	struct altr_edac_device_dev *drvdata = dci->pvt_info;
-
-	debugfs_remove_recursive(drvdata->debugfs_dir);
-	edac_device_del_device(&pdev->dev);
-	edac_device_free_ctl_info(dci);
-}
-
 static struct platform_driver altr_edac_device_driver = {
 	.probe =  altr_edac_device_probe,
-	.remove = altr_edac_device_remove,
 	.driver = {
 		.name = "altr_edac_device",
 		.of_match_table = altr_edac_device_of_match,
+		.suppress_bind_attrs = true,
 	},
 };
 module_platform_driver(altr_edac_device_driver);
@@ -2217,6 +2199,7 @@ static struct platform_driver altr_edac_
 	.driver = {
 		.name = "socfpga_a10_ecc_manager",
 		.of_match_table = altr_edac_a10_of_match,
+		.suppress_bind_attrs = true,
 	},
 };
 module_platform_driver(altr_edac_a10_driver);
author maintainer
#277 lore raw

PATCH 275/428 EDAC/altera: Drop __init from ECC setup paths for re-probe safety

In reply to Greg Kroah-Hartman

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

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

From: Dinh Nguyen <[email protected]>

commit b62a264163ca51bee04785c581344751812395df upstream.

Sashiko reports:

"Does suppressing sysfs unbinding fully prevent the execution of freed __init memory? If altr_sysmgr_regmap_lookup_by_phandle() returns -EPROBE_DEFER, the probe is deferred until after __init memory is freed."

The a10 EDAC .setup callbacks (sdmmc, ethernet, nand, dma, usb, qspi) and their helpers (altr_init_a10_ecc_device_type, altr_init_a10_ecc_block) were marked __init. These run from the probe path, which may execute after init memory is freed -- e.g. a probe deferred via -EPROBE_DEFER that only succeeds once a late/module dependency appears, or a manual unbind/rebind. Calling __init code then dereferences freed memory. Remove __init so these functions remain valid at runtime.

Fixes 788586efd116 ("EDAC/altera: Initialize peripheral FIFOs in probe()")
Assisted-by LLM
---
 drivers/edac/altera_edac.c |   16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)
drivers/edac/altera_edac.c +8 -8
@@ -926,7 +926,7 @@ static int __maybe_unused altr_init_memo
 	return ret;
 }
 
-static __init int __maybe_unused
+static int __maybe_unused
 altr_init_a10_ecc_block(struct device_node *np, u32 irq_mask,
 			u32 ecc_ctrl_en_mask, bool dual_port)
 {
@@ -1001,7 +1001,7 @@ out:
 
 static int validate_parent_available(struct device_node *np);
 static const struct of_device_id altr_edac_a10_device_of_match[];
-static int __init __maybe_unused altr_init_a10_ecc_device_type(char *compat)
+static int __maybe_unused altr_init_a10_ecc_device_type(char *compat)
 {
 	int irq;
 	struct device_node *child, *np;
@@ -1330,7 +1330,7 @@ static const struct edac_device_prv_data
 
 #ifdef CONFIG_EDAC_ALTERA_ETHERNET
 
-static int __init socfpga_init_ethernet_ecc(struct altr_edac_device_dev *dev)
+static int socfpga_init_ethernet_ecc(struct altr_edac_device_dev *dev)
 {
 	int ret;
 
@@ -1360,7 +1360,7 @@ static const struct edac_device_prv_data
 
 #ifdef CONFIG_EDAC_ALTERA_NAND
 
-static int __init socfpga_init_nand_ecc(struct altr_edac_device_dev *device)
+static int socfpga_init_nand_ecc(struct altr_edac_device_dev *device)
 {
 	int ret;
 
@@ -1390,7 +1390,7 @@ static const struct edac_device_prv_data
 
 #ifdef CONFIG_EDAC_ALTERA_DMA
 
-static int __init socfpga_init_dma_ecc(struct altr_edac_device_dev *device)
+static int socfpga_init_dma_ecc(struct altr_edac_device_dev *device)
 {
 	int ret;
 
@@ -1420,7 +1420,7 @@ static const struct edac_device_prv_data
 
 #ifdef CONFIG_EDAC_ALTERA_USB
 
-static int __init socfpga_init_usb_ecc(struct altr_edac_device_dev *device)
+static int socfpga_init_usb_ecc(struct altr_edac_device_dev *device)
 {
 	int ret;
 
@@ -1450,7 +1450,7 @@ static const struct edac_device_prv_data
 
 #ifdef CONFIG_EDAC_ALTERA_QSPI
 
-static int __init socfpga_init_qspi_ecc(struct altr_edac_device_dev *device)
+static int socfpga_init_qspi_ecc(struct altr_edac_device_dev *device)
 {
 	int ret;
 
@@ -1599,7 +1599,7 @@ err_release_group_1:
 	return rc;
 }
 
-static int __init socfpga_init_sdmmc_ecc(struct altr_edac_device_dev *device)
+static int socfpga_init_sdmmc_ecc(struct altr_edac_device_dev *device)
 {
 	int rc = -ENODEV;
 	struct device_node *child;
author maintainer
#278 lore raw

PATCH 276/428 EDAC/altera: Fix memory leak on dci allocation failure

In reply to Greg Kroah-Hartman

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

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

From: Dinh Nguyen <[email protected]>

commit 3e4a10a4718e3d963ee4d6c5f26bc5ad57c1d2b1 upstream.

Sashiko reports:

"If devres_open_group() fails, the function returns -ENOMEM without freeing the dci structure allocated earlier with edac_device_alloc_ctl_info()."

Free the dci structure if devres_open_group() fails.

Fixes c3eea1942a16 ("EDAC, altera: Add Altera L2 cache and OCRAM support")
---
 drivers/edac/altera_edac.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/edac/altera_edac.c +3 -1
@@ -1515,8 +1515,10 @@ static int altr_portb_setup(struct altr_
 	altdev = dci->pvt_info;
 	*altdev = *device;
 
-	if (!devres_open_group(device->edac->dev, altr_portb_setup, GFP_KERNEL))
+	if (!devres_open_group(device->edac->dev, altr_portb_setup, GFP_KERNEL)) {
+		edac_device_free_ctl_info(dci);
 		return -ENOMEM;
+	}
 
 	/* Update PortB specific values */
 	altdev->edac_dev_name = ecc_name;
author maintainer
#279 lore raw

PATCH 277/428 EDAC/altera: Fix use-after-free in error paths

In reply to Greg Kroah-Hartman

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

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

From: Dinh Nguyen <[email protected]>

commit eef3b67f9c6782127163b631c9043011a68016e2 upstream.

In both altr_edac_a10_device_add() and altr_portb_setup(), the error path freed the dci structure before releasing the devres group. Since the managed single and double bit IRQ handlers use altdev(dci->pvt_info) as their data, an IRQ firing between freeing dci and unregistering the IRQs could dereference the freed memory.

Release the devres group first so the managed IRQs are unregistered before the dci structure is freed.

Fixes 911049845d70 ("EDAC, altera: Add Arria10 SD-MMC EDAC support")
Fixes 588cb03ea208 ("EDAC, altera: Add Arria10 L2 Cache ECC handling")
Assisted-by LLM
Cc [email protected] ## 6.18+
---
 drivers/edac/altera_edac.c |   15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)
drivers/edac/altera_edac.c +14 -1
@@ -1594,8 +1594,13 @@ static int altr_portb_setup(struct altr_
 	return 0;
 
 err_release_group_1:
-	edac_device_free_ctl_info(dci);
+	/*
+	 * Release the devres group first so the managed IRQs are
+	 * unregistered before dci (which contains the IRQ handler's
+	 * data via dci->pvt_info) is freed, avoiding a use-after-free.
+	 */
 	devres_release_group(device->edac->dev, altr_portb_setup);
+	edac_device_free_ctl_info(dci);
 	edac_printk(KERN_ERR, EDAC_DEVICE,
 		    "%s:Error setting up EDAC device: %d\n", ecc_name, rc);
 	return rc;
@@ -1998,9 +2003,17 @@ static int altr_edac_a10_device_add(stru
 	return 0;
 
 err_release_group1:
+	/*
+	 * Release the devres group first so the managed IRQs are
+	 * unregistered before dci (which contains the IRQ handler's
+	 * data via dci->pvt_info) is freed, avoiding a use-after-free.
+	 */
+	devres_release_group(edac->dev, NULL);
 	edac_device_free_ctl_info(dci);
+	goto err_print;
 err_release_group:
 	devres_release_group(edac->dev, NULL);
+err_print:
 	edac_printk(KERN_ERR, EDAC_DEVICE,
 		    "%s:Error setting up EDAC device: %d\n", ecc_name, rc);
 
author maintainer
#280 lore raw

PATCH 278/428 EDAC/altera: Fix device node reference leaks in the SDMMC ECC setup

In reply to Greg Kroah-Hartman

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

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

From: Rounak Das <[email protected]>

commit 7d5a36a5490d496e17823085fbe7ec6c0a7bf43d upstream.

Under altr_portb_setup() and socfpga_init_sdmmc_ecc(), of_find_compatible_node() was being used to look up the sdmmc-ecc node. This node wasn't being dropped using of_node_put().

altr_portb_setup() did not drop its reference under its success path or on any error path.

socfpga_init_sdmmc_ecc() did an early return thereby skipping the common exit label and thus leaking the reference.

Add the missing of_node_put() calls in altr_portb_setup(), and route socfpga_init_sdmmc_ecc()'s success path through the common exit label.

Fixes 911049845d70 ("EDAC, altera: Add Arria10 SD-MMC EDAC support")
Fixes 788586efd116 ("EDAC/altera: Initialize peripheral FIFOs in probe()")
---
 drivers/edac/altera_edac.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/edac/altera_edac.c +6 -1
@@ -1505,6 +1505,7 @@ static int altr_portb_setup(struct altr_
 	dci = edac_device_alloc_ctl_info(sizeof(*altdev), ecc_name, 1,
 					 ecc_name, 1, 0, edac_idx);
 	if (!dci) {
+		of_node_put(np);
 		edac_printk(KERN_ERR, EDAC_DEVICE,
 			    "%s: Unable to allocate PortB EDAC device\n",
 			    ecc_name);
@@ -1517,6 +1518,7 @@ static int altr_portb_setup(struct altr_
 
 	if (!devres_open_group(device->edac->dev, altr_portb_setup, GFP_KERNEL)) {
 		edac_device_free_ctl_info(dci);
+		of_node_put(np);
 		return -ENOMEM;
 	}
 
@@ -1585,6 +1587,8 @@ static int altr_portb_setup(struct altr_
 		rc = -ENOMEM;
 		goto err_release_group_1;
 	}
+	of_node_put(np);
+
 	altr_create_edacdev_dbgfs(dci, prv);
 
 	list_add(&altdev->next, &altdev->edac->a10_ecc_devices);
@@ -1601,6 +1605,7 @@ err_release_group_1:
 	 */
 	devres_release_group(device->edac->dev, altr_portb_setup);
 	edac_device_free_ctl_info(dci);
+	of_node_put(np);
 	edac_printk(KERN_ERR, EDAC_DEVICE,
 		    "%s:Error setting up EDAC device: %d\n", ecc_name, rc);
 	return rc;
@@ -1628,7 +1633,7 @@ static int socfpga_init_sdmmc_ecc(struct
 		goto exit;
 
 	/* Setup portB */
-	return altr_portb_setup(device);
+	rc = altr_portb_setup(device);
 
 exit:
 	of_node_put(child);
author maintainer
#281 lore raw

PATCH 279/428 i2c: xiic: preserve PEC byte length in SMBus block read setup

In reply to Greg Kroah-Hartman

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

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

From: Abdurrahman Hussain <[email protected]>

commit b7e6df2f52ed5c02838832f53c171a5645f9b376 upstream.

xiic_smbus_block_read_setup() recalculates i2c->rx_msg->len based on the length byte returned by the device, but historically clobbered the PEC byte expectation the SMBus core had baked into msg->len. That dropped the PEC byte from the caller's buffer on the normal and chunked receive-fifo branches.

Compute pec_len up-front as (i2c->rx_msg->len - 1) -- the trailing bytes the caller has already accounted for beyond the length byte, 1 when the SMBus core enabled PEC, and possibly more for an I2C_M_RECV_LEN request coming from i2c-dev -- and add it to the new length in every branch:

- chunked: the trailing bytes do not fit in the Rx FIFO, so drain in chunks. The guard becomes (rxmsg_len + pec_len > IIC_RX_FIFO_DEPTH) rather than rxmsg_len alone, both because pec_len bytes also have to fit and because it is what bounds rfd_set in the else branch below to the 4 bits of XIIC_RFD_REG_OFFSET.

- padded (1 + rxmsg_len + pec_len < SMBUS_BLOCK_READ_MIN_LEN): the hardware needs at least 3 bytes on the bus to exit the read cleanly (the second byte is already being clocked in by the time the ISR reads the length byte and is too late to NACK), so we still pad rx_msg->len up to SMBUS_BLOCK_READ_MIN_LEN. The dummy trailing byte that gets drained must then be trimmed off before handing the message back to the SMBus core; otherwise i2c_smbus_check_pec() reads buf[len-1] (= dummy) instead of the real PEC byte at buf[1] and rejects every clean zero-length block read with -EBADMSG.

Record the true valid byte count in a new field i2c->smbus_actual_len and trim rx_msg->len down to it in xiic_smbus_trim_len(), called from both completion sites that clear
rx_msg: xiic_process()'s RX_FULL branch and xiic_recv_atomic(), which drains the FIFO with interrupts off.

smbus_actual_len is per-receive state, so xiic_start_recv() clears it before every receive. Only the padded branch ever sets it, and a block read aborted by arbitration loss or a TX error never reaches the completion site, so without that clear a stale value would trim the length of an unrelated later read.

The condition is expressed in total bytes rather than the old "(rxmsg_len == 1) || (rxmsg_len == 0)" so that a request carrying more than one trailing byte does not get padded: padding records a length the drain never reaches, which would hand the caller a byte that was never received.

- normal: all trailing bytes fit in one FIFO fill. rfd_set gains pec_len for the same reason the length does. Because the padded branch above has already taken every case with fewer than SMBUS_BLOCK_READ_MIN_LEN total bytes, rxmsg_len + pec_len is at least 2 here and the subtraction cannot underflow the u8.

---
 drivers/i2c/busses/i2c-xiic.c |   54 +++++++++++++++++++++++++++++++-----------
 1 file changed, 41 insertions(+), 13 deletions(-)
drivers/i2c/busses/i2c-xiic.c +41 -13
@@ -73,6 +73,9 @@ enum i2c_scl_freq {
  * @prev_msg_tx: Previous message is Tx
  * @quirks: To hold platform specific bug info
  * @smbus_block_read: Flag to handle block read
+ * @smbus_actual_len: Valid byte count (length + payload + optional PEC) of a
+ *	padded SMBus block read. msg->len is trimmed to this on completion.
+ *	Zero when no trimming is needed.
  * @input_clk: Input clock to I2C controller
  * @i2c_clk: I2C SCL frequency
  * @atomic: Mode of transfer
@@ -98,6 +101,7 @@ struct xiic_i2c {
 	bool prev_msg_tx;
 	u32 quirks;
 	bool smbus_block_read;
+	unsigned int smbus_actual_len;
 	unsigned long input_clk;
 	unsigned int i2c_clk;
 	bool atomic;
@@ -539,30 +543,38 @@ static void xiic_smbus_block_read_setup(
 
 	/* Check if received length is valid */
 	if (rxmsg_len <= I2C_SMBUS_BLOCK_MAX) {
+		unsigned int pec_len = i2c->rx_msg->len - 1;
+
 		/* Set Receive fifo depth */
-		if (rxmsg_len > IIC_RX_FIFO_DEPTH) {
+		if (rxmsg_len + pec_len > IIC_RX_FIFO_DEPTH) {
 			/*
-			 * When Rx msg len greater than or equal to Rx fifo capacity
-			 * Receive fifo depth should set to Rx fifo capacity minus 1
+			 * Trailing bytes (payload plus any PEC) exceed Rx FIFO
+			 * capacity, so drain in chunks. This also keeps the
+			 * else branch below from pushing rfd_set past the
+			 * 4-bit XIIC_RFD_REG_OFFSET field.
 			 */
 			rfd_set = IIC_RX_FIFO_DEPTH - 1;
-			i2c->rx_msg->len = rxmsg_len + 1;
-		} else if ((rxmsg_len == 1) ||
-			(rxmsg_len == 0)) {
+			i2c->rx_msg->len = rxmsg_len + 1 + pec_len;
+		} else if (1 + rxmsg_len + pec_len < SMBUS_BLOCK_READ_MIN_LEN) {
 			/*
-			 * Minimum of 3 bytes required to exit cleanly. 1 byte
-			 * already received, Second byte is being received. Have
-			 * to set NACK in read_rx before receiving the last byte
+			 * The HW needs SMBUS_BLOCK_READ_MIN_LEN bytes on the
+			 * bus to exit cleanly: by the time the ISR reads the
+			 * length byte the second byte is already being clocked
+			 * in, too late to NACK. Pad the drain target and record
+			 * the real length, trimmed back on completion so the
+			 * PEC check sees the right byte.
 			 */
 			rfd_set = 0;
 			i2c->rx_msg->len = SMBUS_BLOCK_READ_MIN_LEN;
+			i2c->smbus_actual_len = 1 + rxmsg_len + pec_len;
 		} else {
 			/*
-			 * When Rx msg len less than Rx fifo capacity
-			 * Receive fifo depth should set to Rx msg len minus 2
+			 * All trailing bytes fit in the Rx FIFO. The widened
+			 * condition above guarantees rxmsg_len + pec_len >= 2,
+			 * so this cannot underflow.
 			 */
-			rfd_set = rxmsg_len - 2;
-			i2c->rx_msg->len = rxmsg_len + 1;
+			rfd_set = rxmsg_len + pec_len - 2;
+			i2c->rx_msg->len = rxmsg_len + 1 + pec_len;
 		}
 		xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, rfd_set);
 
@@ -575,6 +587,16 @@ static void xiic_smbus_block_read_setup(
 	dev_err(i2c->adap.dev.parent, "smbus_block_read Invalid msg length\n");
 }
 
+/*
+ * Undo the setup-time padding of a short SMBus block read before rx_msg is
+ * cleared, so the PEC check sees the right byte.
+ */
+static void xiic_smbus_trim_len(struct xiic_i2c *i2c)
+{
+	if (i2c->rx_msg && i2c->smbus_actual_len)
+		i2c->rx_msg->len = i2c->smbus_actual_len;
+}
+
 static void xiic_read_rx(struct xiic_i2c *i2c)
 {
 	u8 bytes_in_fifo, cr = 0, bytes_to_read = 0;
@@ -797,6 +819,8 @@ static irqreturn_t xiic_process(int irq,
 
 		xiic_read_rx(i2c);
 		if (xiic_rx_space(i2c) == 0) {
+			xiic_smbus_trim_len(i2c);
+
 			/* this is the last part of the message */
 			i2c->rx_msg = NULL;
 
@@ -938,6 +962,7 @@ static void xiic_recv_atomic(struct xiic
 			return;
 	}
 
+	xiic_smbus_trim_len(i2c);
 	i2c->rx_msg = NULL;
 	xiic_irq_clr_en(i2c, XIIC_INTR_TX_ERROR_MASK);
 
@@ -955,6 +980,9 @@ static void xiic_start_recv(struct xiic_
 	u8 cr = 0, rfd_set = 0;
 	struct i2c_msg *msg = i2c->rx_msg = i2c->tx_msg;
 
+	/* A stale value from an aborted block read would truncate this msg. */
+	i2c->smbus_actual_len = 0;
+
 	if (!i2c->atomic)
 		dev_dbg(i2c->adap.dev.parent, "%s entry, ISR: 0x%x, CR: 0x%x\n",
 			__func__, xiic_getreg32(i2c, XIIC_IISR_OFFSET),
author maintainer
#282 lore raw

PATCH 280/428 i2c: xiic: defer RX_FULL until all trailing bytes are in FIFO

In reply to Greg Kroah-Hartman

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

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

From: Abdurrahman Hussain <[email protected]>

commit e6fe3ea04f0113fe4d47c03d76e03805a4768ca7 upstream.

For the normal path of xiic_smbus_block_read_setup() -- the trailing bytes all fit in one Rx FIFO fill -- RFD was programmed two below the byte count, which fires the RX_FULL interrupt while the last byte is still in flight. xiic_read_rx() then lands in its bytes_rem == 1 branch and sets NACK on a byte still on the wire, truncating the read.

Without PEC this is harmless: the truncated byte is the dummy one the caller never looks at. With PEC enabled it is the PEC byte itself, and i2c_smbus_check_pec() fails the transfer with -EBADMSG.

Raise the threshold by one so RX_FULL fires only once every remaining byte is already buffered. That routes the drain through xiic_read_rx()'s bytes_rem == 0 path, which reads everything out and emits the stop cleanly. The only change for the non-PEC case is that the controller waits one extra byte-time before servicing the interrupt.

rfd_set stays inside the 4 bits of XIIC_RFD_REG_OFFSET: this branch is only reached when rxmsg_len + pec_len <= IIC_RX_FIFO_DEPTH, so the value is at most IIC_RX_FIFO_DEPTH - 1.

---
 drivers/i2c/busses/i2c-xiic.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/i2c/busses/i2c-xiic.c +4 -4
@@ -569,11 +569,11 @@ static void xiic_smbus_block_read_setup(
 			i2c->smbus_actual_len = 1 + rxmsg_len + pec_len;
 		} else {
 			/*
-			 * All trailing bytes fit in the Rx FIFO. The widened
-			 * condition above guarantees rxmsg_len + pec_len >= 2,
-			 * so this cannot underflow.
+			 * All trailing bytes fit in the Rx FIFO. Defer RX_FULL
+			 * until every one of them is buffered, so the drain
+			 * takes xiic_read_rx()'s bytes_rem == 0 path.
 			 */
-			rfd_set = rxmsg_len + pec_len - 2;
+			rfd_set = rxmsg_len + pec_len - 1;
 			i2c->rx_msg->len = rxmsg_len + 1 + pec_len;
 		}
 		xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, rfd_set);
#283 lore raw

Re: [PATCH 7.2 280/428] i2c: xiic: defer RX_FULL until all trailing bytes are in FIFO

Quoted 2 lines · Greg Kroah-Hartman

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

Hi Greg,

This one is being reverted upstream, and Sasha has already dropped it from the 7.2, 6.18, 6.12 and 6.6 queues:

https://lore.kernel.org/linux-i2c/[email protected]/

Noting it here so the review thread matches. 279 and 281 are fine and should stay.

Thanks,
Abdurrahman

author maintainer
#284 lore raw

PATCH 281/428 i2c: xiic: dont clobber msg->len to signal block-read completion

In reply to Greg Kroah-Hartman

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

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

From: Abdurrahman Hussain <[email protected]>

commit 840d8acc87925deb3c75566fde9ca81d2f47f98c upstream.

At the end of a SMBus block read the BNB handler force-set tx_msg->len = 1 to push xiic_tx_space() to zero so the STATE_DONE branch would fire. Two problems:

1. tx_msg and rx_msg alias the same i2c_msg struct during a receive (see xiic_start_recv), so overwriting tx_msg->len also changes rx_msg->len. The i2c core's i2c_smbus_check_pec() then reads the PEC from the wrong offset -- buf[0] instead of buf[rxmsg_len + 1] -- and either mis-validates or returns -EBADMSG.

2. xiic_start_recv sets tx_pos = msg->len (typically 2 when PEC is enabled). xiic_tx_space() is unsigned msg->len - tx_pos, so setting msg->len = 1 with tx_pos = 2 underflows to 0xFFFFFFFF and xiic_tx_space() never compares equal to 0 -- the STATE_DONE check falls through to STATE_ERROR, giving -EIO.

Instead, advance tx_pos up to msg->len. That drives tx_space to 0 without touching msg->len, preserving the buffer length that xiic_smbus_block_read_setup() already grew to cover the length byte, the payload and the optional PEC byte.

---
 drivers/i2c/busses/i2c-xiic.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/i2c/busses/i2c-xiic.c +5 -2
@@ -889,8 +889,11 @@ static irqreturn_t xiic_process(int irq,
 
 		if (i2c->tx_msg && i2c->smbus_block_read) {
 			i2c->smbus_block_read = false;
-			/* Set requested message len=1 to indicate STATE_DONE */
-			i2c->tx_msg->len = 1;
+			/*
+			 * Drive xiic_tx_space() to 0 to signal STATE_DONE
+			 * without truncating the rx_msg length.
+			 */
+			i2c->tx_pos = i2c->tx_msg->len;
 		}
 
 		if (!i2c->tx_msg)
author maintainer
#285 lore raw

PATCH 282/428 Bluetooth: RFCOMM: Fix initial port reference race

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit 2d696c1ed3468dcb62578a3911e687110d463520 upstream.

For RFCOMM_RELEASE_ONHUP devices, rfcomm_tty_install() and __rfcomm_release_dev() both use RFCOMM_TTY_OWNED to decide who drops the initial tty_port reference. The release path tests the bit separately from the install path setting it, so both can drop that reference.

The synchronous hangup does not prevent this race: port->tty is not assigned until tty_port_open(), after installation. The following interleaving is possible:

1. Install and release each obtain a reference with rfcomm_dev_get().
2. Release observes RFCOMM_TTY_OWNED clear.
3. Install sets RFCOMM_TTY_OWNED and drops the initial reference.
4. Release drops the same initial reference again.
5. TTY cleanup drops its reference and frees the device.
6. Release performs its final tty_port_put() on the freed port.

KASAN reported:

  BUG: KASAN: slab-use-after-free in tty_port_put+0x22/0x190
  Write of size 4 at addr ffff8881001d3d5c by task poc/92

  Call Trace:
   tty_port_put+0x22/0x190
   rfcomm_dev_ioctl+0x1d4/0x1930
   sock_do_ioctl+0x110/0x260
   sock_ioctl+0x380/0x590
   __x64_sys_ioctl+0x134/0x1c0

  Allocated by task 88:
   rfcomm_dev_ioctl+0x8f6/0x1930
   sock_do_ioctl+0x110/0x260
   sock_ioctl+0x380/0x590
   __x64_sys_ioctl+0x134/0x1c0

  Freed by task 65:
   kfree+0x131/0x3c0
   rfcomm_dev_destruct+0x23b/0x2f0
   release_one_tty+0xc1/0x370
   process_one_work+0x661/0x1090

Use test_and_set_bit() in both paths so that only the caller that changes RFCOMM_TTY_OWNED from clear to set drops the initial reference. Each path keeps its own lookup reference until release or TTY cleanup, preserving the existing callback ordering and error handling.

Fixes 80ea73378af4 ("Bluetooth: Fix unreleased rfcomm_dev reference")
Assisted-by LLM
---
 net/bluetooth/rfcomm/tty.c |    7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
net/bluetooth/rfcomm/tty.c +3 -4
@@ -462,7 +462,7 @@ static int __rfcomm_release_dev(void __u
 	/* Shut down TTY synchronously before freeing rfcomm_dev */
 	tty_port_tty_vhangup(&dev->port);
 
-	if (!test_bit(RFCOMM_TTY_OWNED, &dev->status))
+	if (!test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status))
 		tty_port_put(&dev->port);
 
 	tty_port_put(&dev->port);
@@ -724,10 +724,9 @@ static int rfcomm_tty_install(struct tty
 	 * when the last process closes the tty. The behaviour is expected by
 	 * userspace.
 	 */
-	if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
-		set_bit(RFCOMM_TTY_OWNED, &dev->status);
+	if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) &&
+	    !test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status))
 		tty_port_put(&dev->port);
-	}
 
 	return 0;
 }
author maintainer
#286 lore raw

PATCH 283/428 Bluetooth: Serialize SMP remote OOB data access

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit 81a2345f1984f0b36d3226571bc196316a01b648 upstream.

build_pairing_cmd() looks up remote OOB data and copies its contents without holding hdev->lock, which serializes the list's writers. After SMP finds an entry, a concurrent management Remove Remote OOB Data command can unlink and free it before SMP reads its present flag or copies its random and confirmation values. Removal can also invalidate an entry while the lookup is still traversing the list.

KASAN reported:

  BUG: KASAN: slab-use-after-free in build_pairing_cmd+0x948/0x9b0
  Call Trace:
   build_pairing_cmd+0x948/0x9b0
   smp_recv_cb+0x459f/0x8110
   l2cap_recv_frame+0xf14/0x9190
   l2cap_recv_acldata+0xa64/0xd40
   hci_rx_work+0x4ca/0x730

  Allocated by task 87:
   hci_add_remote_oob_data+0x11d/0x530
   add_remote_oob_data+0x282/0x400
   hci_sock_sendmsg+0x1033/0x1ea0

  Freed by task 93:
   hci_remote_oob_data_clear+0x108/0x1c0
   remove_remote_oob_data+0x198/0x220
   hci_sock_sendmsg+0x1033/0x1ea0

Taking hdev->lock in build_pairing_cmd() would recurse for callers that already hold it and invert the device-to-L2CAP lock order on the receive path. Add a per-device remote_oob_lock instead, held across the SMP lookup and copies and by the add, remove and clear helpers. Cover initialization and in-place updates as well, so SMP cannot read partially initialized or updated OOB values. Release the mutex on allocation failure, preserving the existing error return.

The new critical sections acquire no device, connection or channel locks. Writers retain their existing hdev->lock protection, which continues to serialize the other readers without changing their locking or behavior.

Fixes 02b05bd8b0a6 ("Bluetooth: Set SMP OOB flag if OOB data is available")
---
 include/net/bluetooth/hci_core.h |    1 +
 net/bluetooth/hci_core.c         |   12 +++++++++++-
 net/bluetooth/smp.c              |    2 ++
 3 files changed, 14 insertions(+), 1 deletion(-)
include/net/bluetooth/hci_core.h +1 -0
@@ -561,6 +561,7 @@ struct hci_dev {
 	struct list_head	link_keys;
 	struct list_head	long_term_keys;
 	struct list_head	identity_resolving_keys;
+	struct mutex		remote_oob_lock;
 	struct list_head	remote_oob_data;
 	struct list_head	le_accept_list;
 	struct list_head	le_resolv_list;
net/bluetooth/hci_core.c +11 -1
@@ -1486,8 +1486,10 @@ int hci_remove_remote_oob_data(struct hc
 
 	BT_DBG("%s removing %pMR (%u)", hdev->name, bdaddr, bdaddr_type);
 
+	mutex_lock(&hdev->remote_oob_lock);
 	list_del(&data->list);
 	kfree(data);
+	mutex_unlock(&hdev->remote_oob_lock);
 
 	return 0;
 }
@@ -1496,10 +1498,12 @@ void hci_remote_oob_data_clear(struct hc
 {
 	struct oob_data *data, *n;
 
+	mutex_lock(&hdev->remote_oob_lock);
 	list_for_each_entry_safe(data, n, &hdev->remote_oob_data, list) {
 		list_del(&data->list);
 		kfree(data);
 	}
+	mutex_unlock(&hdev->remote_oob_lock);
 }
 
 int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
@@ -1508,11 +1512,14 @@ int hci_add_remote_oob_data(struct hci_d
 {
 	struct oob_data *data;
 
+	mutex_lock(&hdev->remote_oob_lock);
 	data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type);
 	if (!data) {
 		data = kmalloc_obj(*data);
-		if (!data)
+		if (!data) {
+			mutex_unlock(&hdev->remote_oob_lock);
 			return -ENOMEM;
+		}
 
 		bacpy(&data->bdaddr, bdaddr);
 		data->bdaddr_type = bdaddr_type;
@@ -1545,6 +1552,8 @@ int hci_add_remote_oob_data(struct hci_d
 
 	BT_DBG("%s for %pMR", hdev->name, bdaddr);
 
+	mutex_unlock(&hdev->remote_oob_lock);
+
 	return 0;
 }
 
@@ -2482,6 +2491,7 @@ struct hci_dev *hci_alloc_dev_priv(int s
 	mutex_init(&hdev->lock);
 	mutex_init(&hdev->req_lock);
 	mutex_init(&hdev->mgmt_pending_lock);
+	mutex_init(&hdev->remote_oob_lock);
 
 	ida_init(&hdev->unset_handle_ida);
 
net/bluetooth/smp.c +2 -0
@@ -661,6 +661,7 @@ static void build_pairing_cmd(struct l2c
 		else
 			bdaddr_type = BDADDR_LE_RANDOM;
 
+		mutex_lock(&hdev->remote_oob_lock);
 		oob_data = hci_find_remote_oob_data(hdev, &hcon->dst,
 						    bdaddr_type);
 		if (oob_data && oob_data->present) {
@@ -671,6 +672,7 @@ static void build_pairing_cmd(struct l2c
 			SMP_DBG("OOB Remote Confirmation: %16phN", smp->pcnf);
 			SMP_DBG("OOB Remote Random: %16phN", smp->rr);
 		}
+		mutex_unlock(&hdev->remote_oob_lock);
 
 	} else {
 		authreq &= ~SMP_AUTH_SC;
author maintainer
#287 lore raw

PATCH 284/428 Bluetooth: hci_sync: Fix inquiry cache use-after-free

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit 8ed67535fdff40ca652bcd4ac7da68d11d01bd34 upstream.

The sync command worker holds hdev->req_lock, but inquiry-cache updates and flushes use hdev->lock. Both hci_acl_create_conn_sync() and hci_stop_discovery_sync() look up entries and read their fields without taking hdev->lock.

After either lookup returns, a concurrent HCIINQUIRY ioctl can acquire hdev->lock and flush the cache, freeing the entry. The worker then reads the freed entry while preparing a create-connection or remote-name-cancel command. The list traversal also races with cache updates and removal.

KASAN reported these accesses:

  BUG: KASAN: slab-use-after-free in hci_acl_create_conn_sync+0x5f1/0x650
  Workqueue: hci0 hci_cmd_sync_work
  Call Trace:
   hci_acl_create_conn_sync+0x5f1/0x650
   hci_cmd_sync_work+0x13c/0x290

  Allocated by task 90:
   hci_inquiry_cache_update+0x3e6/0x7d0
   hci_inquiry_result_evt+0x3cb/0x560

  Freed by task 93:
   hci_inquiry_cache_flush+0x111/0x2b0
   hci_inquiry+0x2f2/0x780
   hci_sock_ioctl+0x269/0x5f0

  BUG: KASAN: slab-use-after-free in hci_stop_discovery_sync+0x3b1/0x3c0
  Workqueue: hci0 hci_cmd_sync_work
  Call Trace:
   hci_stop_discovery_sync+0x3b1/0x3c0
   hci_cmd_sync_work+0x173/0x300

  Allocated by task 86:
   hci_inquiry_cache_update+0x483/0x940
   hci_inquiry_result_evt+0x3cb/0x560

  Freed by task 91:
   hci_inquiry_cache_flush+0x13e/0x2f0
   hci_inquiry+0x2f2/0x780
   hci_sock_ioctl+0x269/0x5f0

Hold hdev->lock across each lookup and all reads from its result. Copy the remote address before unlocking so discovery cancellation does not retain a cache entry pointer. Release the lock before sending synchronous HCI commands, since their completion handlers may need the same lock.

Fixes cf75ad8b41d2 ("Bluetooth: hci_sync: Convert MGMT_SET_POWERED")
Fixes 45340097ce6e ("Bluetooth: hci_conn: Only do ACL connections sequentially")
---
 net/bluetooth/hci_sync.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
net/bluetooth/hci_sync.c +11 -2
@@ -5684,6 +5684,7 @@ int hci_stop_discovery_sync(struct hci_d
 {
 	struct discovery_state *d = &hdev->discovery;
 	struct inquiry_entry *e;
+	bdaddr_t addr;
 	int err;
 
 	bt_dev_dbg(hdev, "state %u", hdev->discovery.state);
@@ -5719,15 +5720,21 @@ int hci_stop_discovery_sync(struct hci_d
 		return 0;
 
 	if (d->state == DISCOVERY_RESOLVING || d->state == DISCOVERY_STOPPING) {
+		hci_dev_lock(hdev);
 		e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY,
 						     NAME_PENDING);
-		if (!e)
+		if (!e) {
+			hci_dev_unlock(hdev);
 			return 0;
+		}
+
+		bacpy(&addr, &e->data.bdaddr);
+		hci_dev_unlock(hdev);
 
 		/* Ignore cancel errors since it should interfere with stopping
 		 * of the discovery.
 		 */
-		hci_remote_name_cancel_sync(hdev, &e->data.bdaddr);
+		hci_remote_name_cancel_sync(hdev, &addr);
 	}
 
 	return 0;
@@ -7157,6 +7164,7 @@ static int hci_acl_create_conn_sync(stru
 	bacpy(&cp.bdaddr, &conn->dst);
 	cp.pscan_rep_mode = 0x02;
 
+	hci_dev_lock(hdev);
 	ie = hci_inquiry_cache_lookup(hdev, &conn->dst);
 	if (ie) {
 		if (inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
@@ -7168,6 +7176,7 @@ static int hci_acl_create_conn_sync(stru
 
 		memcpy(conn->dev_class, ie->data.dev_class, 3);
 	}
+	hci_dev_unlock(hdev);
 
 	cp.pkt_type = cpu_to_le16(conn->pkt_type);
 	if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER))
author maintainer
#288 lore raw

PATCH 285/428 Bluetooth: hci_conn: Lock parent access during enhanced SCO setup

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit 024e05f73a4c1263d031614bc36700ec135eecb4 upstream.

Bluetooth: hci_conn: Lock parent access during enhanced SCO setup

hci_enhanced_setup_sync() runs on the request workqueue without the hci_dev_lock held by its caller when setup was queued. Its CVSD capability check and find_next_esco_param() dereference conn->parent while the receive workqueue can unlink and release that parent.

The following interleaving can cause a use-after-free:

hci_enhanced_setup_sync() hci_disconn_complete_evt()
load conn->parent
hci_dev_lock() hci_conn_del(ACL parent)
unlink SCO child drop link's parent reference clear child->parent release ACL parent
bt_link_release()
kfree(parent)
hci_dev_unlock()
read parent->features[0][3]

The reference held for the queued SCO child does not keep its ACL parent alive after unlinking. Commit 42de40abe25d ("Bluetooth: hci_conn: fix the SCO setup context lifetime") protects the child stored in the queued context, but leaves these parent accesses unprotected.

With a 40 ms diagnostic delay after loading conn->parent, an instrumented kernel based on fd179f8a05be, which already contains 42de40abe25d, reported:

  BUG: KASAN: slab-use-after-free in hci_enhanced_setup_sync+0xda5/0xdf0
  Read of size 1 at addr ffff888102204047 by task kworker/u17:0/93
  Call Trace:
   hci_enhanced_setup_sync+0xda5/0xdf0
   hci_cmd_sync_work+0x13c/0x290
   process_one_work+0x6b4/0x10e0

  Allocated by task 92:
   __hci_conn_add+0x304/0x1df0
   hci_connect_acl+0x349/0x3e0
   hci_connect_sco+0x3b/0x9a0
   sco_sock_connect+0x475/0xca0

  Freed by task 94:
   kfree+0x121/0x3c0
   bt_link_release+0x79/0xa0
   device_release+0xc8/0x240
   kobject_put+0x14d/0x280
   hci_conn_del+0x524/0xe30
   hci_disconn_complete_evt+0x403/0x8c0
   hci_event_packet+0x71b/0xb20
   hci_rx_work+0x293/0x730

The accessed address is 71 bytes into the freed ACL parent, at its features[0][3] byte; the queued SCO child is a different object. The diagnostic preserves the loaded parent across the delay, matching the unmodified compiled capability check, and does not change the parent references or teardown path.

Hold hci_dev_lock() across the codec switch, including every call to find_next_esco_param(), and release it on all selection errors. Keep configure_datapath_sync() outside the critical section because it waits for HCI events. Preserve parameter selection and existing return values.

Fixes e07a06b4eb41 ("Bluetooth: Convert SCO configure_datapath to hci_sync")
Assisted-by GPT-6-Astra
---
 net/bluetooth/hci_conn.c |   18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)
net/bluetooth/hci_conn.c +13 -5
@@ -302,11 +302,13 @@ static int hci_enhanced_setup_sync(struc
 	cp.tx_bandwidth   = cpu_to_le32(0x00001f40);
 	cp.rx_bandwidth   = cpu_to_le32(0x00001f40);
 
+	hci_dev_lock(hdev);
+
 	switch (conn->codec.id) {
 	case BT_CODEC_MSBC:
 		if (!find_next_esco_param(conn, esco_param_msbc,
 					  ARRAY_SIZE(esco_param_msbc)))
-			return -EINVAL;
+			goto unlock;
 
 		param = &esco_param_msbc[conn->attempt - 1];
 		cp.tx_coding_format.id = 0x05;
@@ -332,7 +334,7 @@ static int hci_enhanced_setup_sync(struc
 	case BT_CODEC_TRANSPARENT:
 		if (!find_next_esco_param(conn, esco_param_msbc,
 					  ARRAY_SIZE(esco_param_msbc)))
-			return -EINVAL;
+			goto unlock;
 
 		param = &esco_param_msbc[conn->attempt - 1];
 		cp.tx_coding_format.id = 0x03;
@@ -359,11 +361,11 @@ static int hci_enhanced_setup_sync(struc
 		if (conn->parent && lmp_esco_capable(conn->parent)) {
 			if (!find_next_esco_param(conn, esco_param_cvsd,
 						  ARRAY_SIZE(esco_param_cvsd)))
-				return -EINVAL;
+				goto unlock;
 			param = &esco_param_cvsd[conn->attempt - 1];
 		} else {
 			if (conn->attempt > ARRAY_SIZE(sco_param_cvsd))
-				return -EINVAL;
+				goto unlock;
 			param = &sco_param_cvsd[conn->attempt - 1];
 		}
 		cp.tx_coding_format.id = 2;
@@ -386,9 +388,11 @@ static int hci_enhanced_setup_sync(struc
 		cp.out_transport_unit_size = 16;
 		break;
 	default:
-		return -EINVAL;
+		goto unlock;
 	}
 
+	hci_dev_unlock(hdev);
+
 	cp.retrans_effort = param->retrans_effort;
 	cp.pkt_type = __cpu_to_le16(param->pkt_type);
 	cp.max_latency = __cpu_to_le16(param->max_latency);
@@ -397,6 +401,10 @@ static int hci_enhanced_setup_sync(struc
 		return -EIO;
 
 	return 0;
+
+unlock:
+	hci_dev_unlock(hdev);
+	return -EINVAL;
 }
 
 static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle)
author maintainer
#289 lore raw

PATCH 286/428 Bluetooth: hci_core: Fix inquiry cache timestamps on 64-bit systems

In reply to Greg Kroah-Hartman

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

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

From: Linmao Li <[email protected]>

commit 74847177eca5638827ab74cdf77f0b10fc40ca6c upstream.

On 64-bit systems, outgoing BR/EDR connections always fall back to page scan repetition mode R2 with no clock offset once the system has been up for more than five minutes, even when inquiry found the peer only seconds earlier. This lengthens paging and can increase the risk of a Page Timeout.

The inquiry cache stores jiffies in __u32 timestamps, but its age helpers subtract them from unsigned long jiffies. INITIAL_JIFFIES casts -300 * HZ through unsigned int, so jiffies crosses 2^32 five minutes after boot on 64-bit systems. Assigning it to __u32 then drops the upper 32 bits. In one trace, a 7.6-second-old entry (HZ=1000) was reported as 2^32 + 7620 ticks old and rejected by hci_acl_create_conn_sync(). hci_inquiry() is affected by the same truncation when checking the whole cache. 32-bit systems are unaffected because unsigned long is 32 bits wide there.

Use unsigned long for both timestamps so they have the same width as jiffies on 32-bit and 64-bit systems, and update the debugfs format specifier accordingly.

---
 include/net/bluetooth/hci_core.h |    4 ++--
 net/bluetooth/hci_debugfs.c      |    2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)
include/net/bluetooth/hci_core.h +2 -2
@@ -62,7 +62,7 @@ struct inquiry_entry {
 		NAME_PENDING,
 		NAME_KNOWN,
 	} name_state;
-	__u32			timestamp;
+	unsigned long		timestamp;
 	struct inquiry_data	data;
 };
 
@@ -78,7 +78,7 @@ struct discovery_state {
 	struct list_head	all;	/* All devices found during inquiry */
 	struct list_head	unknown;	/* Name state not known */
 	struct list_head	resolve;	/* Name needs to be resolved */
-	__u32			timestamp;
+	unsigned long		timestamp;
 	bdaddr_t		last_adv_addr;
 	u8			last_adv_addr_type;
 	s8			last_adv_rssi;
net/bluetooth/hci_debugfs.c +1 -1
@@ -364,7 +364,7 @@ static int inquiry_cache_show(struct seq
 
 	list_for_each_entry(e, &cache->all, all) {
 		struct inquiry_data *data = &e->data;
-		seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
+		seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %lu\n",
 			   &data->bdaddr,
 			   data->pscan_rep_mode, data->pscan_period_mode,
 			   data->pscan_mode, data->dev_class[2],
author maintainer
#290 lore raw

PATCH 287/428 Bluetooth: hci_core: Serialize ACL scheduling with channel deletion

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit f4845ab191a4f686a5da481fbba90ed73bb693b0 upstream.

hci_chan_sent() selects a channel under RCU but drops the read lock before accessing chan->conn and returning the channel. The ACL and LE schedulers then use its packet queue and update its transmit counters without any protection against channel deletion.

After TX selects a channel and releases RCU, a disconnect command timeout on the separate request workqueue can run hci_conn_failed() and l2cap_conn_del(). hci_chan_del() can then unlink the channel, complete synchronize_rcu(), purge its queue and free it before TX resumes. This causes use-after-free both in hci_chan_sent() and in its callers.

KASAN reported:

  BUG: KASAN: slab-use-after-free in hci_chan_sent+0x892/0x9b0
  Workqueue: hci0 hci_tx_work
  Call Trace:
   hci_chan_sent+0x892/0x9b0
   hci_tx_work+0x5e6/0xb70
  Allocated by task 91:
   hci_chan_create+0xe3/0x350
   l2cap_conn_add.part.0+0x12/0xa30
   l2cap_chan_connect+0x110d/0x1b60
   l2cap_sock_connect+0x310/0x530
  Freed by task 99:
   hci_chan_del+0x11f/0x170
   l2cap_conn_del+0x4f1/0x800
   l2cap_connect_cfm+0x88c/0xd30
   hci_conn_failed+0x150/0x250
   hci_abort_conn_sync+0x3e3/0x800
   hci_cmd_sync_run+0x7e/0xc0
   hci_abort_conn+0x105/0x1f0
   disconnect_sync+0x157/0x290
   hci_cmd_sync_work+0x13c/0x290

Hold the existing device mutex across channel selection and transmission in both schedulers to serialize them with channel teardown. Keep timeout handling outside the critical sections because hci_link_tx_to() acquires the same mutex. This also permits the transmit path to sleep, unlike extending the RCU read-side critical section across packet submission.

Fixes 3eff45eaf817 ("Bluetooth: convert tx_task to workqueue")
Assisted-by LLM
---
 net/bluetooth/hci_core.c |    8 ++++++++
 1 file changed, 8 insertions(+)
net/bluetooth/hci_core.c +8 -0
@@ -3669,6 +3669,8 @@ static void hci_sched_acl_pkt(struct hci
 
 	__check_timeout(hdev, cnt, ACL_LINK);
 
+	hci_dev_lock(hdev);
+
 	while (hdev->acl_cnt &&
 	       (chan = hci_chan_sent(hdev, ACL_LINK, &quote))) {
 		u32 priority = (skb_peek(&chan->data_q))->priority;
@@ -3700,6 +3702,8 @@ static void hci_sched_acl_pkt(struct hci
 
 	if (cnt != hdev->acl_cnt)
 		hci_prio_recalculate(hdev, ACL_LINK);
+
+	hci_dev_unlock(hdev);
 }
 
 static void hci_sched_acl(struct hci_dev *hdev)
@@ -3728,6 +3732,8 @@ static void hci_sched_le(struct hci_dev
 
 	__check_timeout(hdev, *cnt, LE_LINK);
 
+	hci_dev_lock(hdev);
+
 	tmp = *cnt;
 	while (*cnt && (chan = hci_chan_sent(hdev, LE_LINK, &quote))) {
 		u32 priority = (skb_peek(&chan->data_q))->priority;
@@ -3756,6 +3762,8 @@ static void hci_sched_le(struct hci_dev
 
 	if (*cnt != tmp)
 		hci_prio_recalculate(hdev, LE_LINK);
+
+	hci_dev_unlock(hdev);
 }
 
 /* Schedule iso */
author maintainer
#291 lore raw

PATCH 288/428 Bluetooth: hci_core: Serialize SCO and ISO scheduling with teardown

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit 848a7a91c7f03675d3bd8db6f7721cbf9cb50e21 upstream.

hci_low_sent() selects a connection under RCU but drops the read lock before calculating its quota and returning it to the scheduler. The connection is then used without lifetime protection by hci_sched_iso() and hci_sched_sco().

After the TX worker drops the RCU read lock, hci_abort_conn_sync() on hdev->req_workqueue can remove the connection from the hash, complete synchronize_rcu(), purge its queues and release it. The TX worker on hdev->workqueue can then access the freed connection in hci_quote_sent() or while dequeuing packets and updating conn->sent.

KASAN reported:

  BUG: KASAN: slab-use-after-free in hci_low_sent+0x730/0x840
  Workqueue: hci0 hci_tx_work
  Call Trace:
   hci_low_sent+0x730/0x840
   hci_sched_iso+0x25e/0x4d0
   hci_tx_work+0x239/0xcb0

  Allocated by task 93:
   __hci_conn_add+0x16f/0x1b40
   hci_bind_bis+0x782/0x17b0
   hci_connect_bis+0xa0/0x510
   iso_sock_connect+0x589/0x1050

  Freed by task 88:
   kfree+0x131/0x3c0
   device_release+0xc8/0x240
   kobject_put+0x14d/0x280
   hci_conn_del+0x55a/0xe80
   hci_disconnect_sync+0x156/0x180
   hci_abort_conn_sync+0x3e7/0x940
   hci_cmd_sync_work+0x13c/0x290

Hold hci_dev_lock() across connection selection and transmission in both SCO and ISO scheduling, serializing them with connection teardown. This also prevents queuing completion timestamps after the connection queues have been purged. Extending RCU across transmission would be unsafe because the transmit path can sleep. Keep the ISO timeout check outside the mutex since hci_link_tx_to() takes it itself.

The preceding channel fix already holds this mutex in the ACL and LE schedulers, which call the SCO scheduler between packets. Move the SCO body to __hci_sched_sco(), assert that its caller holds the mutex, and use it directly from these locked paths. Keep a locking hci_sched_sco() wrapper for the direct calls from hci_tx_work(). This avoids recursively acquiring the device mutex while preserving the scheduling order.

Remove the obsolete claim that connection removal disables TX.

Fixes bf4c63252490 ("Bluetooth: convert conn hash to RCU")
Assisted-by LLM
---
 net/bluetooth/hci_core.c |   26 ++++++++++++++++++--------
 1 file changed, 18 insertions(+), 8 deletions(-)
net/bluetooth/hci_core.c +18 -8
@@ -3412,9 +3412,6 @@ static struct hci_conn *hci_low_sent(str
 	struct hci_conn *conn = NULL, *c;
 	unsigned int num = 0, min = ~0;
 
-	/* We don't have to lock device here. Connections are always
-	 * added and removed with TX task disabled. */
-
 	rcu_read_lock();
 
 	list_for_each_entry_rcu(c, &h->list, list) {
@@ -3619,13 +3616,15 @@ static void __check_timeout(struct hci_d
 }
 
 /* Schedule SCO */
-static void hci_sched_sco(struct hci_dev *hdev, __u8 type)
+static void __hci_sched_sco(struct hci_dev *hdev, __u8 type)
 {
 	struct hci_conn *conn;
 	struct sk_buff *skb;
 	int quote, *cnt;
 	unsigned int pkts = hdev->sco_pkts;
 
+	lockdep_assert_held(&hdev->lock);
+
 	bt_dev_dbg(hdev, "type %u", type);
 
 	if (!hci_conn_num(hdev, type) || !pkts)
@@ -3660,6 +3659,13 @@ static void hci_sched_sco(struct hci_dev
 		queue_work(hdev->workqueue, &hdev->tx_work);
 }
 
+static void hci_sched_sco(struct hci_dev *hdev, __u8 type)
+{
+	hci_dev_lock(hdev);
+	__hci_sched_sco(hdev, type);
+	hci_dev_unlock(hdev);
+}
+
 static void hci_sched_acl_pkt(struct hci_dev *hdev)
 {
 	unsigned int cnt = hdev->acl_cnt;
@@ -3695,8 +3701,8 @@ static void hci_sched_acl_pkt(struct hci
 			chan->conn->sent++;
 
 			/* Send pending SCO packets right away */
-			hci_sched_sco(hdev, SCO_LINK);
-			hci_sched_sco(hdev, ESCO_LINK);
+			__hci_sched_sco(hdev, SCO_LINK);
+			__hci_sched_sco(hdev, ESCO_LINK);
 		}
 	}
 
@@ -3755,8 +3761,8 @@ static void hci_sched_le(struct hci_dev
 			chan->conn->sent++;
 
 			/* Send pending SCO packets right away */
-			hci_sched_sco(hdev, SCO_LINK);
-			hci_sched_sco(hdev, ESCO_LINK);
+			__hci_sched_sco(hdev, SCO_LINK);
+			__hci_sched_sco(hdev, ESCO_LINK);
 		}
 	}
 
@@ -3782,6 +3788,8 @@ static void hci_sched_iso(struct hci_dev
 
 	__check_timeout(hdev, *cnt, type);
 
+	hci_dev_lock(hdev);
+
 	while (*cnt && (conn = hci_low_sent(hdev, type, &quote))) {
 		while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
 			BT_DBG("skb %p len %d", skb, skb->len);
@@ -3795,6 +3803,8 @@ static void hci_sched_iso(struct hci_dev
 			(*cnt)--;
 		}
 	}
+
+	hci_dev_unlock(hdev);
 }
 
 static void hci_tx_work(struct work_struct *work)
author maintainer
#292 lore raw

PATCH 289/428 Bluetooth: hci_core: Serialize fragmented ISO packet queueing

In reply to Greg Kroah-Hartman

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

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

From: Chengfeng Ye <[email protected]>

commit 1a572db43c057566771b485598ad2e66cb390821 upstream.

The fragmented path in hci_queue_iso() calls __skb_queue_tail() without holding conn->data_q.lock. The socket lock serializes senders, but the transmit worker can concurrently remove packets from this queue using skb_dequeue().

After an insertion saves the old tail pointer, hci_sched_iso() can dequeue that packet and pass it to the driver. The driver can free the packet before the insertion resumes and writes to the old tail's next pointer, causing a use-after-free. Concurrent updates can also corrupt the queue.

KASAN reported:

  BUG: KASAN: slab-use-after-free in hci_send_iso+0xc7d/0xe10
  Call Trace:
   hci_send_iso+0xc7d/0xe10
   iso_sock_sendmsg+0x710/0x900
   __sys_sendto+0x34a/0x3a0

  Allocated by task 86:
   __alloc_skb+0xdd/0x820
   alloc_skb_with_frags+0x7e/0x770
   sock_alloc_send_pskb+0x67a/0x820
   bt_skb_sendmsg.constprop.0+0xc0/0x6c0
   iso_sock_sendmsg+0x44e/0x900

  Freed by task 90:
   kmem_cache_free+0xcb/0x3d0
   vhci_read+0x33f/0x4d0
   vfs_read+0x177/0xa20

Hold the queue lock across the entire fragment batch, as hci_queue_acl() does, to serialize insertion against dequeue while preserving fragment ordering.

Fixes 26afbd826ee3 ("Bluetooth: Add initial implementation of CIS connections")
---
 net/bluetooth/hci_core.c |    4 ++++
 1 file changed, 4 insertions(+)
net/bluetooth/hci_core.c +4 -0
@@ -3335,6 +3335,8 @@ static void hci_queue_iso(struct hci_con
 
 		skb_shinfo(skb)->frag_list = NULL;
 
+		spin_lock_bh(&queue->lock);
+
 		__skb_queue_tail(queue, skb);
 
 		do {
@@ -3349,6 +3351,8 @@ static void hci_queue_iso(struct hci_con
 
 			__skb_queue_tail(queue, skb);
 		} while (list);
+
+		spin_unlock_bh(&queue->lock);
 	}
 
 	bt_dev_dbg(hdev, "hcon %p queued %d", conn, skb_queue_len(queue));
author maintainer
#293 lore raw

PATCH 290/428 Bluetooth: btintel: fix buffer over-read in btintel_hw_error()

In reply to Greg Kroah-Hartman

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

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

From: Chandrashekar Devegowda <[email protected]>

commit 40eb4b18ad167b63644c27fa67a7400ba050379a upstream.

The "Exception info %s" print uses skb->data + 1, but the 12-byte payload is not guaranteed to be NUL-terminated and can be read past its end.

Bound the print with "%.*s" and skb->len - 1.

Fixes 973bb97e5aee ("Bluetooth: btintel: Add generic function for handling hardware errors")
---
 drivers/bluetooth/btintel.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/bluetooth/btintel.c +2 -1
@@ -288,7 +288,8 @@ void btintel_hw_error(struct hci_dev *hd
 		goto unlock;
 	}
 
-	bt_dev_err(hdev, "Exception info %s", (char *)(skb->data + 1));
+	bt_dev_err(hdev, "Exception info %.*s", (int)(skb->len - 1),
+		   (char *)(skb->data + 1));
 
 	kfree_skb(skb);
 
author maintainer
#294 lore raw

PATCH 291/428 Bluetooth: btintel_pcie: fix plen overflow in btintel_pcie_recv_frame()

In reply to Greg Kroah-Hartman

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

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

From: Chandrashekar Devegowda <[email protected]>

commit 7dbeb6935ac331b0cba6c41cabd8e9d02d5f64ea upstream.

plen is u16 and computed as HDR_SIZE + dlen. For ACL/ISO frames dlen is a 16-bit field, so a value >= 0xFFFC wraps the sum, bypassing the "skb->len < plen" check and letting skb_trim() truncate the packet.

Widen plen to u32 so the addition cannot wrap.

Fixes c2b636b3f788 ("Bluetooth: btintel_pcie: Add support for PCIe transport")
---
 drivers/bluetooth/btintel_pcie.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/bluetooth/btintel_pcie.c +1 -1
@@ -1197,7 +1197,7 @@ static int btintel_pcie_recv_frame(struc
 {
 	int ret;
 	u8 pkt_type;
-	u16 plen;
+	u32 plen;
 	u32 pcie_pkt_type;
 	void *pdata;
 	struct hci_dev *hdev = data->hdev;
author maintainer
#295 lore raw

PATCH 292/428 Bluetooth: btintel_pcie: reject oversized TX packets in send_frame()

In reply to Greg Kroah-Hartman

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

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

From: Chandrashekar Devegowda <[email protected]>

commit f771f96557a4d953a7e4d045870547271e36e819 upstream.

btintel_pcie_send_frame() eventually copies skb->data into a fixed BTINTEL_PCIE_BUFFER_SIZE (4096) DMA buffer via memcpy() in btintel_pcie_prepare_tx(), with a 4-byte PCIe type header prepended. A user-space process with HCI_CHANNEL_USER access can inject an oversized packet and overflow the buffer.

Reject packets larger than
BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN on entry.

Fixes 6e65a09f9275 ("Bluetooth: btintel_pcie: Add *setup* function to download firmware")
---
 drivers/bluetooth/btintel_pcie.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/bluetooth/btintel_pcie.c +10 -0
@@ -2157,6 +2157,16 @@ static int btintel_pcie_send_frame(struc
 	if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
 		return -ENODEV;
 
+	/* Account for the 4-byte PCIe type header prepended before the
+	 * DMA copy.  Written as a subtraction to avoid wrap-around on
+	 * attacker-controlled skb->len.
+	 */
+	if (skb->len > BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN) {
+		bt_dev_err(hdev, "Packet too large: %u > %u", skb->len,
+			   BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN);
+		return -EMSGSIZE;
+	}
+
 	/* Due to the fw limitation, the type header of the packet should be
 	 * 4 bytes unlike 1 byte for UART. In UART, the firmware can read
 	 * the first byte to get the packet type and redirect the rest of data
author maintainer
#296 lore raw

PATCH 293/428 x86/mm: Dont apply va_align to hugetlb mappings on AMD F15h

In reply to Greg Kroah-Hartman

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

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

From: Laurent Wandrebeck <[email protected]>

commit d2457a7e2727dc747a61a50d65c2f259afce8c45 upstream.

get_align_mask() returns huge_page_mask_align() for hugetlbfs, but get_align_bits() adds va_align.bits regardless, so vm_unmapped_area() returns an address off the huge page boundary and __unmap_hugepage_range() hits BUG_ON(start & ~huge_page_mask(h)) at teardown.

This can be triggered on Carrizo and FX-8370E, both hstates.

Pass the file to get_align_bits() and skip the randomisation for hugetlbfs.

[ bp: Massage commit message. ]

Fixes 1317a5e7f7b1 ("arch/x86: teach arch_get_unmapped_area_vmflags to handle hugetlb mappings")
---
 arch/x86/kernel/sys_x86_64.c |   15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)
arch/x86/kernel/sys_x86_64.c +11 -4
@@ -50,9 +50,16 @@ static unsigned long get_align_mask(stru
  * value before calling vm_unmapped_area() or ORed directly to the
  * address.
  */
-static unsigned long get_align_bits(void)
+static unsigned long get_align_bits(struct file *filp)
 {
-	return va_align.bits & get_align_mask(NULL);
+	/*
+	 * va_align.bits is smaller than the huge page size and will
+	 * lead to misaligned huge pages. Ignore it for huge mappings.
+	 */
+	if (is_file_hugepages(filp))
+		return 0;
+
+	return va_align.bits & get_align_mask(filp);
 }
 
 static int __init control_va_addr_alignment(char *str)
@@ -157,7 +164,7 @@ arch_get_unmapped_area(struct file *filp
 	}
 	if (filp) {
 		info.align_mask = get_align_mask(filp);
-		info.align_offset += get_align_bits();
+		info.align_offset += get_align_bits(filp);
 	}
 
 	return vm_unmapped_area(&info);
@@ -222,7 +229,7 @@ get_unmapped_area:
 
 	if (filp) {
 		info.align_mask = get_align_mask(filp);
-		info.align_offset += get_align_bits();
+		info.align_offset += get_align_bits(filp);
 	}
 	addr = vm_unmapped_area(&info);
 	if (!(addr & ~PAGE_MASK))
author maintainer
#297 lore raw

PATCH 294/428 x86/mm: Drop unnecessary PMD page copy when freeing

In reply to Greg Kroah-Hartman

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

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

From: Mikhail Gavrilov <[email protected]>

commit e3ee38c1bc0b11a8d3e63dce7050759b61864286 upstream.

On a box with a discrete GPU, lockdep reports a possible deadlock as soon as kswapd shrinks the TTM page pool. The immediate cause is an x86 commit that added an mmap_read_lock() to kernel page protection munging code.

The huge vmap code holds the same lock over a GFP_KERNEL allocation, which is a no-no now that reclaim can take it. That allocation is in a page table *free* path and ends up being for dubious purposes[1]. Basically, it tries to avoid hardware setting Accessed=1 in page table entries that are unreachable by the hardware, a non-issue.

Remove the PMD copy. Detach the original PMD page at the PUD, flush the mid-level caches, and free the PTE tables straight from the detached PMD page. With no allocation left, the locking issue is gone.

Lockdep splat/analysis:

WARNING: possible circular locking dependency detected
7.3.0-rc3-f6e7b42bf05b+ #183 Tainted: G U
------------------------------------------------------
kswapd0/269 is trying to acquire lock:
((init_mm).mmap_lock){++++}-{4:4}, at: change_page_attr_set_clr+0x29a/0x4a0 but task is already holding lock:
(pool_shrink_rwsem){.+.+}-{4:4}, at: ttm_pool_shrink+0xb2/0x330 [ttm]

  Chain exists of:
    (init_mm).mmap_lock --> fs_reclaim --> pool_shrink_rwsem

The cycle is built from three edges:

1) pool_shrink_rwsem -> (init_mm).mmap_lock

     The TTM shrinker restores the caching attribute of every page it
     frees, while holding pool_shrink_rwsem:

       ttm_pool_shrink()
       -> ttm_pool_dispose_list()
       -> ttm_pool_free_page()
       -> set_pages_wb()
       -> change_page_attr_set_clr()   [ init_mm mmap read lock ]

2) fs_reclaim -> pool_shrink_rwsem

The same shrinker, called from reclaim.

3) (init_mm).mmap_lock -> fs_reclaim

     ioremap() installing a huge PUD mapping over an existing PMD table:

       ioremap_page_range()
       -> vmap_range_noflush()
       -> vmap_try_huge_pud()          [ init_mm mmap read lock ]
       -> pud_free_pmd_page()
       -> __get_free_page(GFP_KERNEL)  [ enters reclaim ]

[ dhansen: Lots of changelog munging/trimming and merged comments from my
version of the fix. ]

---
 arch/x86/mm/pgtable.c |   38 ++++++++++++++------------------------
 1 file changed, 14 insertions(+), 24 deletions(-)
arch/x86/mm/pgtable.c +14 -24
@@ -705,47 +705,37 @@ int pmd_clear_huge(pmd_t *pmd)
 }
 
 #ifdef CONFIG_X86_64
-/**
- * pud_free_pmd_page - Clear PUD entry and free PMD page
- * @pud: Pointer to a PUD
- * @addr: Virtual address associated with PUD
- *
- * Context: The PUD range has been unmapped and TLB purged.
- * Return: 1 if clearing the entry succeeded. 0 otherwise.
- *
- * NOTE: Callers must allow a single page allocation.
+/*
+ * Given a PUD poitner, detach and free the pointed-to
+ * PMD page and any PTE page children. The entire range
+ * under the PUD must not have any valid translations
+ * and the TLB must have already been flushed.
  */
 int pud_free_pmd_page(pud_t *pud, unsigned long addr)
 {
-	pmd_t *pmd, *pmd_sv;
 	struct ptdesc *pt;
+	pmd_t *pmd;
 	int i;
 
 	pmd = pud_pgtable(*pud);
-	pmd_sv = (pmd_t *)__get_free_page(GFP_KERNEL);
-	if (!pmd_sv)
-		return 0;
-
-	for (i = 0; i < PTRS_PER_PMD; i++) {
-		pmd_sv[i] = pmd[i];
-		if (!pmd_none(pmd[i]))
-			pmd_clear(&pmd[i]);
-	}
 
+	/* Detach the PMD page: */
 	pud_clear(pud);
 
-	/* INVLPG to clear all paging-structure caches */
+	/*
+	 * PMD and all its descendents are unreachable
+	 * via normal page walks. Make them unreachable
+	 * in cached mid-level walks too:
+	 */
 	flush_tlb_kernel_range(addr, addr + PAGE_SIZE-1);
 
 	for (i = 0; i < PTRS_PER_PMD; i++) {
-		if (!pmd_none(pmd_sv[i])) {
-			pt = page_ptdesc(pmd_page(pmd_sv[i]));
+		if (!pmd_none(pmd[i])) {
+			pt = page_ptdesc(pmd_page(pmd[i]));
 			pagetable_dtor_free(pt);
 		}
 	}
 
-	free_page((unsigned long)pmd_sv);
-
 	pmd_free(&init_mm, pmd);
 
 	return 1;
author maintainer
#298 lore raw

PATCH 295/428 tpm: fix off-by-four bounds check in tpm2_get_random()

In reply to Greg Kroah-Hartman

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

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

From: Jiangshan Yi <[email protected]>

[ Upstream commit 9b522400bf5c082feb9a0037a053ea822a2c7e4d ]

When the response carries the TPM2_ST_SESSIONS tag, tpm2_get_random() skips the 4-byte parameter size field before locating the random data, but the bounds check still validates the response length against TPM_HEADER_SIZE. A truncated response can pass the check and make memcpy() read up to 4 bytes past the response end, so stale buffer contents end up in the caller's random bytes.

Fix this by checking the response length against 'offset', which already includes the skipped parameter size field.

---
 drivers/char/tpm/tpm2-cmd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/char/tpm/tpm2-cmd.c +1 -1
index 52ee350da8671..e12e245faeb1e 100644
@@ -318,7 +318,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 		out = (struct tpm2_get_random_out *)&buf.data[offset];
 		recd = min_t(u32, be16_to_cpu(out->size), num_bytes);
 		if (tpm_buf_length(&buf) <
-		    TPM_HEADER_SIZE +
+		    offset +
 		    offsetof(struct tpm2_get_random_out, buffer) +
 		    recd) {
 			err = -EFAULT;
Signature · 1 line
2.53.0
author maintainer
#299 lore raw

PATCH 296/428 nvme-multipath: set BLK_FEAT_ZONED only after the zone info is known

In reply to Greg Kroah-Hartman

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

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

From: Guixin Liu <[email protected]>

commit 4a5e49ba0abb8b4328d6318c9aef0c0121f95507 upstream.

The namespace head is marked zoned at allocation time based only on the command set identifier. If the zone info query reports a zero zone size, the path namespace is registered without zoned limits while the head still advertises the zoned capability with a zone size of zero, and reporting zones or writing to the head then shifts by ilog2(0).

Drop the zoned feature from the head allocation and let the head limits refresh stack it in from the path namespace instead.

Fixes 28982ad73d6a ("nvme: set BLK_FEAT_ZONED for ZNS multipath disks")
Fixes 3838e80fcfb3 ("nvme: skip the zoned limits update if the zone info query failed")
---
 drivers/nvme/host/multipath.c |    2 --
 1 file changed, 2 deletions(-)
drivers/nvme/host/multipath.c +0 -2
@@ -737,8 +737,6 @@ int nvme_mpath_alloc_disk(struct nvme_ct
 	lim.dma_alignment = 3;
 	lim.features |= BLK_FEAT_IO_STAT | BLK_FEAT_NOWAIT |
 		BLK_FEAT_POLL | BLK_FEAT_ATOMIC_WRITES | BLK_FEAT_PCI_P2PDMA;
-	if (head->ids.csi == NVME_CSI_ZNS)
-		lim.features |= BLK_FEAT_ZONED;
 
 	head->disk = blk_alloc_disk(&lim, ctrl->numa_node);
 	if (IS_ERR(head->disk))
author maintainer
#300 lore raw

PATCH 297/428 nvme-multipath: fix underflow in ANA log bounds checks

In reply to Greg Kroah-Hartman

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

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

From: Daehyeon Ko <[email protected]>

commit 63fa2d9c9c37020bd2703fe2a6a38027b5abeb15 upstream.

The number of ANA groups advertised through Identify Controller sizes the ANA log buffer. The ANA log header and group descriptors independently supply the number of groups and namespace IDs to parse.

Both bounds checks subtract an untrusted object size from ana_log_size before comparing the current offset. If the object is larger than the buffer, the size_t subtraction underflows and lets the parser read beyond ana_log_buf.

Check the current offset before the first subtraction and compare each object size with the remaining buffer instead. This rejects inconsistent ANA data before dereferencing a truncated group descriptor or walking an oversized namespace ID array.

Fixes 0d0b660f214d ("nvme: add ANA support")
Assisted-by LLM
---
 drivers/nvme/host/multipath.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/nvme/host/multipath.c +3 -2
@@ -815,7 +815,8 @@ static int nvme_parse_ana_log(struct nvm
 		u32 nr_nsids;
 		size_t nsid_buf_size;
 
-		if (WARN_ON_ONCE(offset > ctrl->ana_log_size - sizeof(*desc)))
+		if (WARN_ON_ONCE(offset > ctrl->ana_log_size ||
+				 sizeof(*desc) > ctrl->ana_log_size - offset))
 			return -EINVAL;
 
 		nr_nsids = le32_to_cpu(desc->nnsids);
@@ -831,7 +832,7 @@ static int nvme_parse_ana_log(struct nvm
 			return -EINVAL;
 
 		offset += sizeof(*desc);
-		if (WARN_ON_ONCE(offset > ctrl->ana_log_size - nsid_buf_size))
+		if (WARN_ON_ONCE(nsid_buf_size > ctrl->ana_log_size - offset))
 			return -EINVAL;
 
 		error = cb(ctrl, desc, data);
author maintainer
#301 lore raw

PATCH 298/428 nvme-pci: add NVME_QUIRK_DMAPOOL_ALIGN_512 for Micron 4100AT

In reply to Greg Kroah-Hartman

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

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

From: Bean Huo <[email protected]>

commit c72f9f7bd23c91e944910d9bcbf12503d21a4a1f upstream.

The Micron 4100AT fetches PRP Lists in 512 byte strides, but the driver allocates small PRP List descriptors in 256 byte strides. A descriptor in the last 256 bytes of a dmapool page makes the controller read past the page: the access lands on an adjacent IOVA, the IOMMU faults the command. That last block only became reachable with commit da9619a30e73 ("dmapool: link blocks across pages"), so this affects v6.4 and later.

Align the small descriptor pool to 512 bytes, as commit ebefac564796 ("nvme-pci: 512 byte aligned dma pool segment quirk") already does for another controller with the same erratum. The last block then starts at offset 0xE00 and the fetch stays inside the page.

Cc <[email protected]> # 6.4.x: ebefac564796: nvme-pci: 512 byte aligned dma pool segment quirk
Cc <[email protected]> # 6.4.x
---
 drivers/nvme/host/pci.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/nvme/host/pci.c +2 -0
@@ -4182,6 +4182,8 @@ static const struct pci_device_id nvme_i
 		.driver_data = NVME_QUIRK_IGNORE_DEV_SUBNQN },
 	 { PCI_DEVICE(0x1344, 0x6001),   /* Micron Nitro NVMe */
 		 .driver_data = NVME_QUIRK_BOGUS_NID, },
+	{ PCI_DEVICE(0x1344, 0x6004),	/* Micron 4100AT */
+		.driver_data = NVME_QUIRK_DMAPOOL_ALIGN_512, },
 	{ PCI_DEVICE(0x1c5c, 0x1504),   /* SK Hynix PC400 */
 		.driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, },
 	{ PCI_DEVICE(0x1c5c, 0x174a),   /* SK Hynix P31 SSD */
author maintainer
#302 lore raw

PATCH 299/428 nvmet-auth: fix out-of-bounds write in nvmet_auth_challenge()

In reply to Greg Kroah-Hartman

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

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

From: Weiming Shi <[email protected]>

commit fa6778e030bed9841ddcdf96bc7018da9ac03562 upstream.

nvmet_auth_challenge() receives its output buffer as a void pointer. sizeof(*d) therefore evaluates to one with GCC and undercounts the fixed challenge header by 15 bytes. A short AUTH_RECEIVE buffer can pass the check before the challenge is copied past the end of its allocation.

Use the typed challenge pointer when calculating the response size.

  BUG: KASAN: slab-out-of-bounds in nvmet_execute_auth_receive (drivers/nvme/target/fabrics-cmd-auth.c:442 drivers/nvme/target/fabrics-cmd-auth.c:569)
  Write of size 32 by task kworker/1:1H/64
  Workqueue: nvmet_tcp_wq nvmet_tcp_io_work
  Call Trace:
  nvmet_execute_auth_receive (drivers/nvme/target/fabrics-cmd-auth.c:442 drivers/nvme/target/fabrics-cmd-auth.c:569)
  nvmet_tcp_try_recv_pdu (drivers/nvme/target/tcp.c:1119 drivers/nvme/target/tcp.c:1243)
  nvmet_tcp_io_work (drivers/nvme/target/tcp.c:1350 drivers/nvme/target/tcp.c:1382 drivers/nvme/target/tcp.c:1445)
  process_one_work (kernel/workqueue.c:3322)
  worker_thread (kernel/workqueue.c:3405 kernel/workqueue.c:3486)
  kthread (kernel/kthread.c:436)
  ret_from_fork (arch/x86/kernel/process.c:158)
  ret_from_fork_asm (arch/x86/entry/entry_64.S:245)
  The buggy address is located 16 bytes inside of
   allocated 33-byte region [ffff888000554e80, ffff888000554ea1)
  Kernel panic - not syncing: KASAN: panic_on_warn set ...
Fixes db1312dd9548 ("nvmet: implement basic In-Band Authentication")
Reported-by [email protected]
Assisted-by Codex:gpt-5.6
---
 drivers/nvme/target/fabrics-cmd-auth.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/target/fabrics-cmd-auth.c +1 -1
@@ -426,7 +426,7 @@ static int nvmet_auth_challenge(struct n
 	struct nvmet_ctrl *ctrl = req->sq->ctrl;
 	int ret = 0;
 	int hash_len = nvme_auth_hmac_hash_len(ctrl->shash_id);
-	int data_size = sizeof(*d) + hash_len;
+	int data_size = sizeof(*data) + hash_len;
 
 	if (ctrl->dh_tfm)
 		data_size += ctrl->dh_keysize;
author maintainer
#303 lore raw

PATCH 300/428 nvmet: pci-epf: reject too-short SGL segments

In reply to Greg Kroah-Hartman

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

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

From: Daehyeon Ko <[email protected]>

commit 5d5fbfbbacd1efb13a9bf91317d4fde859ede4b9 upstream.

The host controls an SGL segment's length. A nonzero length smaller than one SGL descriptor makes nr_descs zero, so nvmet_pci_epf_get_sgl_segment() reads the type byte of sgls[-1] outside the allocated buffer. If that byte contains a segment descriptor type, the function also copies the preceding 16 bytes and returns a negative descriptor count.

The read was reproduced in 3/3 KASAN boots with only the host transfer mocked by a KUnit test:

  BUG: KASAN: slab-out-of-bounds in nvmet_pci_epf_get_sgl_segment
  Read of size 1 by task kunit_try_catch
  Call Trace:
   nvmet_pci_epf_get_sgl_segment
   nvmet_pci_epf_short_sgl_test
  Kernel panic - not syncing: KASAN: panic_on_warn set ...

Reject segments that cannot hold one descriptor before allocating or transferring their contents.

Fixes 0faa0fe6f90e ("nvmet: New NVMe PCI endpoint function target driver")
Assisted-by LLM
---
 drivers/nvme/target/pci-epf.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/nvme/target/pci-epf.c +3 -0
@@ -1001,6 +1001,9 @@ nvmet_pci_epf_get_sgl_segment(struct nvm
 	int nr_descs, ret;
 	void *buf;
 
+	if (length < sizeof(struct nvme_sgl_desc))
+		return NULL;
+
 	buf = kmalloc(length, GFP_KERNEL);
 	if (!buf)
 		return NULL;
author maintainer
#304 lore raw

PATCH 301/428 nvmet: copy the hostid into the ctrl before creating PR pc_refs

In reply to Greg Kroah-Hartman

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

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

From: Guixin Liu <[email protected]>

commit 5211fed41d7ec90e9ab7239dc4bffffe4d0b2b62 upstream.

Commit 6202783184bf ("nvmet: Improve nvmet_alloc_ctrl() interface and implementation") added a second uuid_copy() of args->hostid near the end of nvmet_alloc_ctrl(), and commit 7b658153f1b8 ("nvmet: Remove duplicate uuid_copy") removed the original copy that sat before nvmet_ctrl_init_pr() instead of the new one.

Since then nvmet_ctrl_init_pr() snapshots ctrl->hostid into the per-controller per-namespace reservation refs while the uuid_copy() from the connect data runs later, after the controller is published. The ctrl is allocated with kzalloc(), so every pc_ref created on this path stores the nil UUID.

pc_ref->hostid has a single consumer: nvmet_pr_set_ctrl_to_abort() matches it against the preempted registrant's hostid to kill and drain the victim's in-flight I/O for Preempt and Abort. The match can never hit with the nil UUID, so whenever a namespace with reservations enabled exists before a host connects, which includes every reconnect, Preempt and Abort silently degrades into a plain Preempt: the preempting host sees success while the victim's in-flight I/O is still in the air.

Copy the hostid where the rest of the connect data is consumed, before the controller is published and before nvmet_ctrl_init_pr() takes its snapshot.

---
 drivers/nvme/target/core.c |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/nvme/target/core.c +2 -3
@@ -1641,6 +1641,8 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(stru
 	INIT_DELAYED_WORK(&ctrl->ka_work, nvmet_keep_alive_timer);
 
 	memcpy(ctrl->hostnqn, args->hostnqn, NVMF_NQN_SIZE);
+	if (args->hostid)
+		uuid_copy(&ctrl->hostid, args->hostid);
 
 	kref_init(&ctrl->ref);
 	ctrl->subsys = subsys;
@@ -1699,9 +1701,6 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(stru
 
 	nvmet_start_keep_alive_timer(ctrl);
 
-	if (args->hostid)
-		uuid_copy(&ctrl->hostid, args->hostid);
-
 	dhchap_status = nvmet_setup_auth(ctrl, args->sq, false);
 	if (dhchap_status) {
 		pr_err("Failed to setup authentication, dhchap status %u\n",
author maintainer
#305 lore raw

PATCH 302/428 mtd: block2mtd: Fix divide error when erase_size is zero

In reply to Greg Kroah-Hartman

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

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

From: Pei Xiao <[email protected]>

commit b5965c4221165045e19736794ca5f48ae3d67142 upstream.

The erase size is parsed from the "block2mtd" module parameter and can be set to zero. add_device() then evaluates

if (size % erase_size)

with a zero divisor, which triggers a divide error:

divide error: 0000 [#1] PREEMPT SMP PTI

RIP: 0010:add_device drivers/mtd/devices/block2mtd.c:296 [inline]
RIP: 0010:block2mtd_setup2+0x592/0xda0 drivers/mtd/devices/block2mtd.c:459
Call Trace:
block2mtd_setup+0x27/0xe0 drivers/mtd/devices/block2mtd.c:476
param_attr_store+0x214/0x310 kernel/params.c:589
module_attr_store+0x65/0x90 kernel/params.c:904
kernfs_fop_write_iter+0x3a4/0x540 fs/kernfs/file.c:345

...

Reject a zero erase size before performing the modulo operation so the existing "erasesize must be a divisor of device size" error path reports the invalid argument and frees the device.

---
 drivers/mtd/devices/block2mtd.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/mtd/devices/block2mtd.c +1 -1
@@ -293,7 +293,7 @@ static struct block2mtd_dev *add_device(
 	}
 
 	size = bdev_nr_bytes(bdev);
-	if ((long)size % erase_size) {
+	if (!erase_size || (long)size % erase_size) {
 		pr_err("erasesize must be a divisor of device size\n");
 		goto err_free_block2mtd;
 	}
author maintainer
#306 lore raw

PATCH 303/428 mtd: mtd_intel_dg: reset poll counter for each erase

In reply to Greg Kroah-Hartman

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

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

From: Menachem Adin <[email protected]>

commit 200c32e3cd53132fbf99f1e2a5751f556755cf6d upstream.

The non-posted erase polling counter is initialized only once per MTD erase request. Large requests therefore share the polling budget across all 4K erase commands and can fail with -ETIME even though no individual command timed out.

Reset the counter for each 4K erase command so every operation gets the intended completion timeout.

Fixes a1c940cbf505 ("drm/xe/nvm: add support for non-posted erase")
---
 drivers/mtd/devices/mtd_intel_dg.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/mtd/devices/mtd_intel_dg.c +2 -1
@@ -387,7 +387,7 @@ idg_erase(struct intel_dg_nvm *nvm, u8 r
 	void __iomem *base2 = nvm->base2;
 	void __iomem *base = nvm->base;
 	const u32 block = 0x10;
-	u32 iter = 0;
+	u32 iter;
 	u32 reg;
 	u64 i;
 
@@ -396,6 +396,7 @@ idg_erase(struct intel_dg_nvm *nvm, u8 r
 		iowrite32(region << 24 | block, base + NVM_ERASE_REG);
 		if (nvm->non_posted_erase) {
 			/* Wait for Erase Done */
+			iter = 0;
 			reg = ioread32(base2 + NVM_DEBUG_REG);
 			while (!(reg & NVM_NON_POSTED_ERASE_DONE) &&
 			       ++iter < NVM_NON_POSTED_ERASE_DONE_ITER) {
author maintainer
#307 lore raw

PATCH 304/428 mtd: rawnand: cadence: Initialize IRQ state before requesting IRQ

In reply to Greg Kroah-Hartman

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

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

From: Runyu Xiao <[email protected]>

commit 21f027016b1290d13c30b198ae7a00e6b3d1d5a5 upstream.

The Cadence NAND interrupt handler uses both the IRQ lock and completion object. Registering the IRQ before initializing them leaves a window in which a pending interrupt can access uninitialized synchronization state.

Initialize them before registering the handler.

Fixes ec4ba01e894d ("mtd: rawnand: Add new Cadence NAND driver to MTD subsystem")
Assisted-by Codex:GPT-5
---
 drivers/mtd/nand/raw/cadence-nand-controller.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/mtd/nand/raw/cadence-nand-controller.c +3 -3
@@ -3143,6 +3143,9 @@ static int cadence_nand_init(struct cdns
 		goto free_buf_desc;
 	}
 
+	spin_lock_init(&cdns_ctrl->irq_lock);
+	init_completion(&cdns_ctrl->complete);
+
 	if (devm_request_irq(cdns_ctrl->dev, cdns_ctrl->irq, cadence_nand_isr,
 			     IRQF_SHARED, "cadence-nand-controller",
 			     cdns_ctrl)) {
@@ -3151,9 +3154,6 @@ static int cadence_nand_init(struct cdns
 		goto free_buf;
 	}
 
-	spin_lock_init(&cdns_ctrl->irq_lock);
-	init_completion(&cdns_ctrl->complete);
-
 	ret = cadence_nand_hw_init(cdns_ctrl);
 	if (ret)
 		goto disable_irq;
author maintainer
#308 lore raw

PATCH 305/428 mtd: rawnand: vf610_nfc: fix reads on chips with more than 64 bytes of OOB

In reply to Greg Kroah-Hartman

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

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

From: Mehmet Fide <[email protected]>

commit 37adc9c5789c76db3b1ee7aeec3999b8503020d0 upstream.

The controller transfers 64 spare bytes per page and the driver only implements the matching 64-byte ECC layout, so attach_chip() shrinks mtd->oobsize when the chip provides more. That clamp does not survive:
nand_scan_tail() runs nanddev_init() after ->attach_chip(), and it restores mtd->oobsize from the memory organization, which still holds the value detected from the chip. The driver then transfers writesize plus the chip's full OOB size, the hardware ECC parity ends up at a different offset than the layout the controller was set up for, and every ECC-protected read fails with -EBADMSG.

Measured on a Colibri VF61 (MX30LF4G28AC, 2048-byte pages, 112 bytes of OOB): with the clamp lost, UBI cannot read the erase counter headers of the pages U-Boot has just written, and the on-flash bad block table written by an older kernel reads back with ECC errors, so the board does not boot. Kernels before commit a7ab085d7c16 ("mtd: rawnand:
Initialize the nand_device object") are not affected because nothing overwrote the clamp there, which is why the same chip works with a v4.4 kernel and with U-Boot, whose copy of this driver has no memory organization to restore the value from. Edward Karpicz reported that the clamp no longer takes effect on this chip; see the link below.

Instead of modifying the memory organization, keep the detected OOB size and give the driver its own mtd_ooblayout_ops: the same layout the NAND core uses for large pages, but computed on the first 64 OOB bytes instead of the whole OOB, so the ECC bytes stay where U-Boot and the old kernels put them. The data paths transfer writesize plus those 64 bytes, as the controller always has.

Since mtd->oobsize now reports the chip's real spare size, fill the tail of oob_poi with 0xff after the 64 transferred bytes on ECC page
reads: the core may copy the full mtd->oobsize from it, which would otherwise expose whatever the buffer held before. 0xff also matches what a raw read returns from flash, since the write path only ever programs the first 64 spare bytes.

---
 drivers/mtd/nand/raw/vf610_nfc.c |   72 ++++++++++++++++++++++++++++++++-------
 1 file changed, 60 insertions(+), 12 deletions(-)
drivers/mtd/nand/raw/vf610_nfc.c +60 -12
@@ -505,6 +505,11 @@ static int vf610_nfc_exec_op(struct nand
 				      check_only);
 }
 
+static unsigned int vf610_nfc_spare_size(struct mtd_info *mtd)
+{
+	return min_t(unsigned int, mtd->oobsize, 64);
+}
+
 static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat,
 					 uint8_t *oob, int page)
 {
@@ -522,7 +527,7 @@ static inline int vf610_nfc_correct_data
 		return ecc_count;
 
 	nfc->data_access = true;
-	nand_read_oob_op(&nfc->chip, page, 0, oob, mtd->oobsize);
+	nand_read_oob_op(&nfc->chip, page, 0, oob, vf610_nfc_spare_size(mtd));
 	nfc->data_access = false;
 
 	/*
@@ -530,7 +535,7 @@ static inline int vf610_nfc_correct_data
 	 * at least less then half of the ECC strength.
 	 */
 	return nand_check_erased_ecc_chunk(dat, nfc->chip.ecc.size, oob,
-					   mtd->oobsize, NULL, 0,
+					   vf610_nfc_spare_size(mtd), NULL, 0,
 					   flips_threshold);
 }
 
@@ -551,7 +556,7 @@ static int vf610_nfc_read_page(struct na
 {
 	struct vf610_nfc *nfc = chip_to_nfc(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	int trfr_sz = mtd->writesize + mtd->oobsize;
+	int trfr_sz = mtd->writesize + vf610_nfc_spare_size(mtd);
 	u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0;
 	int stat;
 
@@ -577,11 +582,16 @@ static int vf610_nfc_read_page(struct na
 	 */
 	vf610_nfc_rd_from_sram(buf, nfc->regs + NFC_MAIN_AREA(0),
 			       mtd->writesize, false);
-	if (oob_required)
+	if (oob_required) {
+		unsigned int spare = vf610_nfc_spare_size(mtd);
+
 		vf610_nfc_rd_from_sram(chip->oob_poi,
 				       nfc->regs + NFC_MAIN_AREA(0) +
 						   mtd->writesize,
-				       mtd->oobsize, false);
+				       spare, false);
+		/* Not transferred, and never written: reads back erased */
+		memset(chip->oob_poi + spare, 0xff, mtd->oobsize - spare);
+	}
 
 	stat = vf610_nfc_correct_data(chip, buf, chip->oob_poi, page);
 
@@ -599,7 +609,7 @@ static int vf610_nfc_write_page(struct n
 {
 	struct vf610_nfc *nfc = chip_to_nfc(chip);
 	struct mtd_info *mtd = nand_to_mtd(chip);
-	int trfr_sz = mtd->writesize + mtd->oobsize;
+	int trfr_sz = mtd->writesize + vf610_nfc_spare_size(mtd);
 	u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0;
 	u8 status;
 	int ret;
@@ -740,6 +750,49 @@ static void vf610_nfc_init_controller(st
 	}
 }
 
+/*
+ * With 64 byte OOB chips the core's large page layout matches what
+ * U-Boot uses, and on chips with more, U-Boot and older kernels clamped
+ * mtd->oobsize to 64. Modifying the OOB size is no longer possible, the
+ * actual chip geometry must be respected, so to avoid breaking those
+ * existing setups use our own layout: the core's large page one,
+ * computed over the first 64 spare bytes only.
+ */
+static int vf610_nfc_ooblayout_ecc(struct mtd_info *mtd, int section,
+				   struct mtd_oob_region *oobregion)
+{
+	struct nand_device *nand = mtd_to_nanddev(mtd);
+	unsigned int total_ecc_bytes = nand->ecc.ctx.total;
+
+	if (section || !total_ecc_bytes)
+		return -ERANGE;
+
+	oobregion->length = total_ecc_bytes;
+	oobregion->offset = vf610_nfc_spare_size(mtd) - oobregion->length;
+
+	return 0;
+}
+
+static int vf610_nfc_ooblayout_free(struct mtd_info *mtd, int section,
+				    struct mtd_oob_region *oobregion)
+{
+	struct nand_device *nand = mtd_to_nanddev(mtd);
+	unsigned int total_ecc_bytes = nand->ecc.ctx.total;
+
+	if (section)
+		return -ERANGE;
+
+	oobregion->length = vf610_nfc_spare_size(mtd) - total_ecc_bytes - 2;
+	oobregion->offset = 2;
+
+	return 0;
+}
+
+static const struct mtd_ooblayout_ops vf610_nfc_ooblayout_ops = {
+	.ecc = vf610_nfc_ooblayout_ecc,
+	.free = vf610_nfc_ooblayout_free,
+};
+
 static int vf610_nfc_attach_chip(struct nand_chip *chip)
 {
 	struct mtd_info *mtd = nand_to_mtd(chip);
@@ -770,12 +823,7 @@ static int vf610_nfc_attach_chip(struct
 		return -ENXIO;
 	}
 
-	/* Only 64 byte ECC layouts known */
-	if (mtd->oobsize > 64)
-		mtd->oobsize = 64;
-
-	/* Use default large page ECC layout defined in NAND core */
-	mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout());
+	mtd_set_ooblayout(mtd, &vf610_nfc_ooblayout_ops);
 	if (chip->ecc.strength == 32) {
 		nfc->ecc_mode = ECC_60_BYTE;
 		chip->ecc.bytes = 60;
author maintainer
#309 lore raw

PATCH 306/428 mtd: spi-nor: core: Fix mutex leak in spi_nor_rww_start_exclusive()

In reply to Greg Kroah-Hartman

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

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

From: Runyu Xiao <[email protected]>

commit 44b8a0bf5f96e8393312fc34b956766882341f16 upstream.

Use the same guard(mutex) pattern as the other RWW helpers so nor->lock is released on both the busy and successful return paths.

Fixes 03e7bb864d9a ("mtd: spi-nor: use scope-based mutex cleanup helpers")
[mw: rephrased commit message]
---
 drivers/mtd/spi-nor/core.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/mtd/spi-nor/core.c +1 -1
@@ -1310,7 +1310,7 @@ static bool spi_nor_rww_start_exclusive(
 {
 	struct spi_nor_rww *rww = &nor->rww;
 
-	mutex_lock(&nor->lock);
+	guard(mutex)(&nor->lock);
 
 	if (rww->ongoing_io || rww->ongoing_rd || rww->ongoing_pe)
 		return false;
author maintainer
#310 lore raw

PATCH 307/428 mtd: spinand: Enable QE on all dies

In reply to Greg Kroah-Hartman

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

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

From: Han Xu <[email protected]>

commit 63d6cace2c4a7f36c1cb44f1bb5e0f3ef19e5c7f upstream.

The QUAD ENABLE (QE) bit is stored in a per-die configuration register on some SPI-NAND devices. When a device contains multiple dies, updating the QE bit only on the currently selected die can leave the remaining dies operating in non-quad mode.

Iterate over all targets and update the QE setting on each die during initialization to ensure consistent quad I/O operation across the entire device.

Tested on ISSI IS38SMW04G8B.

Fixes 7529df465248 ("mtd: nand: Add core infrastructure to support SPI NANDs")
---
 drivers/mtd/nand/spi/core.c |   22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)
drivers/mtd/nand/spi/core.c +20 -2
@@ -281,8 +281,26 @@ static int spinand_init_cfg_cache(struct
 static int spinand_init_quad_enable(struct spinand_device *spinand,
 				    bool enable)
 {
-	return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE,
-			       enable ? CFG_QUAD_ENABLE : 0);
+	struct nand_device *nand = spinand_to_nand(spinand);
+	unsigned int target;
+	int ret;
+
+	/*
+	 * QE is a per-die setting on some devices. Program each target
+	 * individually when enabling or disabling quad I/O mode.
+	 */
+	for (target = 0; target < nand->memorg.ntargets; target++) {
+		ret = spinand_select_target(spinand, target);
+		if (ret)
+			return ret;
+
+		ret = spinand_upd_cfg(spinand, CFG_QUAD_ENABLE,
+				      enable ? CFG_QUAD_ENABLE : 0);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
 }
 
 static int spinand_ecc_enable(struct spinand_device *spinand,
author maintainer
#311 lore raw

PATCH 308/428 mtd: spinand: fix NULL pointer dereference with no ECC engine

In reply to Greg Kroah-Hartman

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

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

From: Nuno Sá <[email protected]>

commit b890e6163761ddb580cc74f43e15f8bbde15647e upstream.

When "nand-no-ecc-engine" is set in DT, nanddev_get_ecc_engine() takes the NAND_ECC_ENGINE_TYPE_NONE path and returns success while leaving nand->ecc.engine NULL. The SPI-NAND code nevertheless dereferences it unconditionally to test for a pipelined engine, so probing such a device oopses immediately.

Rather than open-coding the test three times, add a nand_ecc_is_pipelined() helper to the NAND core that folds the NULL check into the integration comparison, and use it everywhere. Future callers then cannot reintroduce the problem.

Fixes f9d7c7265bcf ("mtd: spinand: Create direct mapping descriptors for ECC operations")
---
 drivers/mtd/nand/ecc.c      |    7 +++++++
 drivers/mtd/nand/spi/core.c |    8 +++-----
 include/linux/mtd/nand.h    |    2 ++
 3 files changed, 12 insertions(+), 5 deletions(-)
drivers/mtd/nand/ecc.c +7 -0
@@ -159,6 +159,13 @@ int nand_ecc_finish_io_req(struct nand_d
 }
 EXPORT_SYMBOL(nand_ecc_finish_io_req);
 
+bool nand_ecc_is_pipelined(const struct nand_device *nand)
+{
+	return nand->ecc.engine &&
+	       nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED;
+}
+EXPORT_SYMBOL(nand_ecc_is_pipelined);
+
 /* Define default OOB placement schemes for large and small page devices */
 static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
 				 struct mtd_oob_region *oobregion)
drivers/mtd/nand/spi/core.c +3 -5
@@ -526,8 +526,7 @@ static int spinand_read_from_cache_op(st
 	else
 		rdesc->info.op_tmpl = &rdesc->info.primary_op_tmpl;
 
-	if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED &&
-	    req->mode != MTD_OPS_RAW)
+	if (nand_ecc_is_pipelined(nand) && req->mode != MTD_OPS_RAW)
 		rdesc->info.op_tmpl->data.ecc = true;
 	else
 		rdesc->info.op_tmpl->data.ecc = false;
@@ -621,8 +620,7 @@ static int spinand_write_to_cache_op(str
 
 	wdesc = spinand->dirmaps[req->pos.plane].wdesc;
 
-	if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED &&
-	    req->mode != MTD_OPS_RAW)
+	if (nand_ecc_is_pipelined(nand) && req->mode != MTD_OPS_RAW)
 		wdesc->info.op_tmpl->data.ecc = true;
 	else
 		wdesc->info.op_tmpl->data.ecc = false;
@@ -1279,7 +1277,7 @@ static int spinand_create_dirmap(struct
 	struct spi_mem_dirmap_desc *desc;
 	bool enable_ecc = false, secondary_op = false;
 
-	if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED)
+	if (nand_ecc_is_pipelined(nand))
 		enable_ecc = true;
 
 	if (spinand->cont_read_possible && spinand->op_templates->cont_read_cache)
include/linux/mtd/nand.h +2 -0
@@ -305,6 +305,8 @@ int nand_ecc_prepare_io_req(struct nand_
 			    struct nand_page_io_req *req);
 int nand_ecc_finish_io_req(struct nand_device *nand,
 			   struct nand_page_io_req *req);
+bool nand_ecc_is_pipelined(const struct nand_device *nand);
+
 bool nand_ecc_is_strong_enough(struct nand_device *nand);
 
 #if IS_REACHABLE(CONFIG_MTD_NAND_CORE)
author maintainer
#312 lore raw

PATCH 309/428 mtd: spinand: fix zero oobavail when no ECC engine is used

In reply to Greg Kroah-Hartman

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

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

From: Nuno Sá <[email protected]>

commit 636fe10d8f3559210ff63108a208d39ea2e9c3bd upstream.

Commit 00c15b78b4b4 ("mtd: spinand: Allow the case where there is no ECC engine") made the OOB free bytes count conditional on having an ECC engine so that probing would not fail when none is requested. However mtd->oobavail is still assigned from ret just after that block, and ret is 0 there, so the device ends up advertising no available OOB bytes at all.

mtd_oobavail() returns mtd->oobavail for MTD_OPS_AUTO_OOB, so a zero value makes every automatic OOB access fail with -EINVAL. JFFS2 fares
worse: it keeps its cleanmarker in the OOB area on NAND and refuses to mount outright, reporting "inconsistent device description".

No ECC engine also means no ooblayout was ever installed, which would make the count return -ENOTSUPP, so install the same fallback layout the on-die path already uses before counting unconditionally.

Fixes 00c15b78b4b4 ("mtd: spinand: Allow the case where there is no ECC engine")
---
 drivers/mtd/nand/spi/core.c |   13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/mtd/nand/spi/core.c +9 -4
@@ -1995,12 +1995,17 @@ static int spinand_init(struct spinand_d
 			goto err_cleanup_ecc_engine;
 	}
 
-	if (nand->ecc.engine) {
-		ret = mtd_ooblayout_count_freebytes(mtd);
-		if (ret < 0)
-			goto err_cleanup_ecc_engine;
+	if (!nand->ecc.engine) {
+		if (spinand->eccinfo.ooblayout)
+			mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout);
+		else
+			mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout);
 	}
 
+	ret = mtd_ooblayout_count_freebytes(mtd);
+	if (ret < 0)
+		goto err_cleanup_ecc_engine;
+
 	mtd->oobavail = ret;
 
 	/* Propagate ECC information to mtd_info */
author maintainer
#313 lore raw

PATCH 310/428 mtd: spinand: Do not update the QE bit on devices without one

In reply to Greg Kroah-Hartman

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

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

From: Sagnik Sasmal <[email protected]>

commit 1c1a342aceec79528b3ba51f376eca2be5928fe7 upstream.

Commit be0b86c648bf ("mtd: spinand: Gather all the bus interface steps in one single function") moved quad-enable setup into spinand_configure_chip(). The new code only determines whether quad mode is needed when SPINAND_HAS_QE_BIT is set, but calls spinand_init_quad_enable() unconditionally. This clears configuration register bit 0 on devices without a QE bit.

That bit is not universally a QE bit. On the Winbond W25N02KV it is H-DIS, which disables the active-low HOLD function. Clearing H-DIS enables HOLD during single and dual I/O operations. If IO3 is not kept high, the flash can pause a command and ignore clock and data.

H-DIS is not restored by the FFh reset command, allowing the incorrect state to survive an SoC warm reboot while the flash remains powered.

Before the refactoring, spinand_init_quad_enable() returned without touching the configuration register on devices without SPINAND_HAS_QE_BIT. Restore that behavior by only calling the helper when the flag is set.

Return zero explicitly once SSDR configuration completes, as all errors are returned immediately. This avoids returning an uninitialized value when neither optional configuration step runs.

The regression was reproduced on a JioRouter JIDU6401 with an MT7986 SoC and a W25N02KV. With Linux 6.18.44, sysupgrade failed and the following warm reboot hung in BL2. With this change applied, both sysupgrade and warm reboot completed successfully.

Fixes be0b86c648bf ("mtd: spinand: Gather all the bus interface steps in one single function")
Assisted-by LLM
---
 drivers/mtd/nand/spi/core.c |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/mtd/nand/spi/core.c +5 -5
@@ -1812,11 +1812,11 @@ try_ssdr:
 		    spinand->ssdr_op_templates.write_cache->data.buswidth == 4 ||
 		    spinand->ssdr_op_templates.update_cache->data.buswidth == 4)
 			quad_enable = true;
-	}
 
-	ret = spinand_init_quad_enable(spinand, quad_enable);
-	if (ret)
-		return ret;
+		ret = spinand_init_quad_enable(spinand, quad_enable);
+		if (ret)
+			return ret;
+	}
 
 	if (spinand->configure_chip) {
 		ret = spinand->configure_chip(spinand, SSDR);
@@ -1824,7 +1824,7 @@ try_ssdr:
 			return ret;
 	}
 
-	return ret;
+	return 0;
 }
 
 static int spinand_init_flash(struct spinand_device *spinand)
author maintainer
#314 lore raw

PATCH 311/428 KVM: nVMX: Force MSR bitmap refresh if runtime eVMCS controls are modified

In reply to Greg Kroah-Hartman

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

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

From: Sean Christopherson <[email protected]>

commit 2abb25273c82061dd4670690c81c319c03740bf6 upstream.

Force a refresh of the vmcs02 MSR bitmap during nested VM-Enter if the runtime eVMCS controls (pin, primary, secondary, etc.) are being updated. If L1 isn't intercepting TPR writes, runs L2 with TPR virtualization, and then runs the same L2 with TPR virtualization disabled, KVM will fail to refresh msr_bitmap02 and leave TPR in passthrough mode even though TPR virtualization is disabled. I.e. failure to refresh the bitmap lets L2 (or L1 by proxy) read and write L0's TPR.

Fixes 502d2bf5f2fd ("KVM: nVMX: Implement Enlightened MSR Bitmap feature")
---
 arch/x86/kvm/vmx/nested.c |    6 ++++++
 1 file changed, 6 insertions(+)
arch/x86/kvm/vmx/nested.c +6 -0
@@ -1758,6 +1758,9 @@ static void copy_vmcs12_to_shadow(struct
 static void copy_enlightened_to_vmcs12(struct vcpu_vmx *vmx, u32 hv_clean_fields)
 {
 #ifdef CONFIG_KVM_HYPERV
+	const u64 runtime_controls = HV_VMX_ENLIGHTENED_CLEAN_FIELD_CONTROL_GRP1 |
+				     HV_VMX_ENLIGHTENED_CLEAN_FIELD_CONTROL_GRP2 |
+				     HV_VMX_ENLIGHTENED_CLEAN_FIELD_CONTROL_PROC;
 	struct vmcs12 *vmcs12 = vmx->nested.cached_vmcs12;
 	struct hv_enlightened_vmcs *evmcs = nested_vmx_evmcs(vmx);
 	struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(&vmx->vcpu);
@@ -1766,6 +1769,9 @@ static void copy_enlightened_to_vmcs12(s
 	vmcs12->tpr_threshold = evmcs->tpr_threshold;
 	vmcs12->guest_rip = evmcs->guest_rip;
 
+	if ((hv_clean_fields & runtime_controls) != runtime_controls)
+		vmx->nested.force_msr_bitmap_recalc = true;
+
 	if (unlikely(!(hv_clean_fields &
 		       HV_VMX_ENLIGHTENED_CLEAN_FIELD_ENLIGHTENMENTSCONTROL))) {
 		hv_vcpu->nested.pa_page_gpa = evmcs->partition_assist_page;
author maintainer
#315 lore raw

PATCH 312/428 KVM: SEV: Nullify "have run CPUs" mask pointer when freeing it

In reply to Greg Kroah-Hartman

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

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

From: Sean Christopherson <[email protected]>

commit 8abbc76120a74bfba1851bd97528099d715e45c6 upstream.

Nullify have_run_cpus when freeing the mask, particularly in the error path of __sev_guest_init(), so that KVM doesn't have to subtly use sev->active to track whether or not the mask has been freed. As pointed out by Sashiko, blindly freeing the mask in sev_vm_destroy() results in a double-free if the mask is freed if __sev_guest_init() fails.

Throw the logic in a helper as nullifying the pointer is frustratingly difficult and weird due to have_run_cpus being a single-entry array when CPUMASK_OFFSTACK=n. Deliberately don't use CPUMASK_VAR_NULL, as it's not directly assignable when the cpumask is on-stack, e.g. requires using a local variable and a memcpy(), which is beyond ridiculous. Furthermore, while clearing the on-stack bitmask is an unnecessary and arguably unwanted side effect, KVM absolutely relies on '0' being the "null" value given that the struct is zero-allocated.

Opportunistically add an alloc() helper to pair with free(); there are just enough call sites to make doing so worthwhile.

Fixes 12c1f6e03f94 ("KVM: SEV: Free have_run_cpus during VM destruction even if VM is no longer SEV")

Message-ID: <[email protected]>

---
 arch/x86/kvm/svm/sev.c |   33 ++++++++++++++++++++++-----------
 1 file changed, 22 insertions(+), 11 deletions(-)
arch/x86/kvm/svm/sev.c +22 -11
@@ -488,6 +488,20 @@ static void snp_guest_req_cleanup(struct
 	sev->guest_resp_buf = NULL;
 }
 
+static int sev_alloc_have_run_cpus(struct kvm_sev_info *sev)
+{
+	if (!zalloc_cpumask_var(&sev->have_run_cpus, GFP_KERNEL_ACCOUNT))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void sev_free_have_run_cpus(struct kvm_sev_info *sev)
+{
+	free_cpumask_var(sev->have_run_cpus);
+	memset(&sev->have_run_cpus, 0, sizeof(sev->have_run_cpus));
+}
+
 static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
 			    struct kvm_sev_init *data,
 			    unsigned long vm_type)
@@ -545,10 +559,9 @@ static int __sev_guest_init(struct kvm *
 	if (ret)
 		goto e_free_asid;
 
-	if (!zalloc_cpumask_var(&sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) {
-		ret = -ENOMEM;
+	ret = sev_alloc_have_run_cpus(sev);
+	if (ret)
 		goto e_free_asid;
-	}
 
 	/* This needs to happen after SEV/SNP firmware initialization. */
 	if (snp_active) {
@@ -566,7 +579,7 @@ static int __sev_guest_init(struct kvm *
 	return 0;
 
 e_free:
-	free_cpumask_var(sev->have_run_cpus);
+	sev_free_have_run_cpus(sev);
 e_free_asid:
 	argp->error = init_args.error;
 	sev_asid_free(sev);
@@ -2198,10 +2211,9 @@ int sev_vm_move_enc_context_from(struct
 	 * does not, i.e. KVM could skip flushes if memory is reclaimed from
 	 * the old VM but not the new VM.
 	 */
-	if (!zalloc_cpumask_var(&dst_sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) {
-		ret = -ENOMEM;
+	ret = sev_alloc_have_run_cpus(dst_sev);
+	if (ret)
 		goto out_source_vcpu;
-	}
 
 	sev_migrate_from(kvm, source_kvm);
 	kvm_vm_dead(source_kvm);
@@ -2899,10 +2911,9 @@ int sev_vm_copy_enc_context_from(struct
 	}
 
 	mirror_sev = to_kvm_sev_info(kvm);
-	if (!zalloc_cpumask_var(&mirror_sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) {
-		ret = -ENOMEM;
+	ret = sev_alloc_have_run_cpus(mirror_sev);
+	if (ret)
 		goto e_unlock;
-	}
 
 	/*
 	 * The mirror kvm holds an enc_context_owner ref so its asid can't
@@ -2995,7 +3006,7 @@ void sev_vm_destroy(struct kvm *kvm)
 	 * Free the mask even if the VM is not *currently* an SEV VM, as it may
 	 * have been an SEV VM prior to intra-host migration.
 	 */
-	free_cpumask_var(sev->have_run_cpus);
+	sev_free_have_run_cpus(sev);
 
 	if (!sev_guest(kvm))
 		return;
author maintainer
#316 lore raw

PATCH 313/428 KVM: SEV: Do cache maintenance on the source VM *before* clearing SEV state

In reply to Greg Kroah-Hartman

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

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

From: Sean Christopherson <[email protected]>

commit 973ea70393e885e540f714904e51bc6cac80e3d7 upstream.

Explicitly flush caches after intra-host migration before clearing "SEV active" on the source VM, as doing cache maintenance afterwards creates a tiny window where memory reclaim could return memory to the host without performing a cache flush, e.g. as pointed out by Sashiko:

  CPU1 in sev_migrate_from():
    src->active = false;

CPU2 running concurrent unmap:
Since active is false, the automatic cache flush in sev_guest_memory_reclaimed is skipped.
The host frees and reallocates the page.

CPU1 in sev_migrate_from():
sev_writeback_caches(src_kvm);
Executes a hardware cache flush (wbnoinvd), which writes the guest's old dirty ciphertext over the new page owner's data.

Fixes 93de2a6a4b91 ("KVM: SEV: Do cache maintenance on the source VM during intra-host migration")

Message-ID: <[email protected]>

---
 arch/x86/kvm/svm/sev.c |   13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)
arch/x86/kvm/svm/sev.c +7 -6
@@ -2048,6 +2048,13 @@ static void sev_migrate_from(struct kvm
 	struct kvm_sev_info *mirror;
 	unsigned long i;
 
+	/*
+	 * Do cache maintenance on the source VM *before* clearing "SEV active",
+	 * as memory reclaim flows won't trigger cache maintenance on the VM
+	 * once it's no longer an SEV VM.
+	 */
+	sev_writeback_caches(src_kvm);
+
 	dst->active = true;
 	dst->asid = src->asid;
 	dst->handle = src->handle;
@@ -2061,12 +2068,6 @@ static void sev_migrate_from(struct kvm
 	src->pages_locked = 0;
 	src->es_active = false;
 
-	/*
-	 * Do cache maintenance on the source VM as it is no longer an SEV VM,
-	 * i.e. memory reclaim flows won't trigger cache maintenance on the VM.
-	 */
-	sev_writeback_caches(src_kvm);
-
 	list_cut_before(&dst->regions_list, &src->regions_list, &src->regions_list);
 
 	mutex_lock(&sev_mirror_lock);
author maintainer
#317 lore raw

PATCH 314/428 KVM: x86/mmu: Bail from shadow walks if the root is invalid or a dummy

In reply to Greg Kroah-Hartman

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

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

From: Sean Christopherson <[email protected]>

commit b6249c1d5665098c8bf12f8d3e4712bf04c775bc upstream.

When walking shadow page tables, immediately terminate the walk if the root is a "dummy" root, i.e. a root whose top-level page table is backed by the zero page, but otherwise doesn't exist. If memslot creation races with a stage-2 page fault (EPT violation or #NPF) from L2, then if the stars align, KVM will attempt to walk shadow page tables using the zero page and hit a NULL pointer deref.

  BUG: kernel NULL pointer dereference, address: 0000000000000021
  #PF: supervisor read access in kernel mode
  #PF: error_code(0x0000) - not-present page
  PGD 0 P4D 0
  Oops: Oops: 0000 [#1] SMP
  CPU: 30 UID: 1000 PID: 941 Comm: qemu Not tainted 7.2.0-rc2-1b731e5ded48-next-vm #1741 PREEMPTLAZY
  Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015
  RIP: 0010:__kvm_mmu_invalidate_addr+0xea/0x210 [kvm]
  Call Trace:
   <TASK>
   kvm_mmu_invalidate_addr+0x92/0xe0 [kvm]
   __kvm_inject_emulated_page_fault+0x67/0x80 [kvm]
   ept_page_fault+0x160/0x850 [kvm]
   kvm_mmu_do_page_fault+0x102/0x1f0 [kvm]
   kvm_mmu_page_fault+0x8e/0x6b0 [kvm]
   vmx_handle_exit+0x163/0x640 [kvm_intel]
   kvm_arch_vcpu_ioctl_run+0x960/0x2120 [kvm]
   kvm_vcpu_ioctl+0x2c7/0x970 [kvm]
   __x64_sys_ioctl+0x90/0xd0
   do_syscall_64+0x67/0x5f0
   entry_SYSCALL_64_after_hwframe+0x4b/0x53
  RIP: 0033:0x7f606d0b53bb

Opportunistically harden the shadow walks against fully invalid roots, but WARN, as all callers are expected/required to pre-check for a valid root. Don't WARN in the dummy root case as the whole point of using a dummy root is to provide a root that's valid enough to enter the guest, i.e. it should Just Work for all flows except those that *need* to know about dummy roots.

Alternatively, KVM could allocate a dedicated page and associated shadow page structure for the dummy root, which is very tempting as it such an approach should be more resilient against unexpected behavior. But that would be a much larger and thus riskier change than simply terminating walks of dummy roots.

Fixes 0e3223d8d00a ("KVM: x86/mmu: Use dummy root, backed by zero page, for !visible guest roots")

Message-ID: <[email protected]>

---
 arch/x86/kvm/mmu/mmu.c |    6 ++++++
 1 file changed, 6 insertions(+)
arch/x86/kvm/mmu/mmu.c +6 -0
@@ -2506,6 +2506,12 @@ static void shadow_walk_init_using_root(
 {
 	iterator->addr = addr;
 	iterator->shadow_addr = root;
+
+	if (WARN_ON_ONCE(!VALID_PAGE(root)) || kvm_mmu_is_dummy_root(root)) {
+		iterator->level = 0;
+		return;
+	}
+
 	iterator->level = vcpu->arch.mmu->root_role.level;
 
 	if (iterator->level >= PT64_ROOT_4LEVEL &&
author maintainer
#318 lore raw

PATCH 315/428 KVM: SVM: Use the active VMCBs MSR bitmap when checking if MSR is intercepted

In reply to Greg Kroah-Hartman

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

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

From: Sean Christopherson <[email protected]>

commit 9e06bbb9ade7cdf28d4a3b5b14e5812881c8df85 upstream.

Use the MSR permission bitmap of the active VMCB instead of assuming that KVM is always using vmcb02's bitmap when L2 is active, as KVM uses msrpm02 if and only if L1 wants to intercept MSR accesses, i.e. if and only if KVM needs to merge msprm01 with msrpm12.

Don't bother tracking the virtual address of the bitmap that's being used, as __va() is cheap on x86, and caching the virtual address would introduce yet another source of potentially stale information.

Fixes b2ac58f90540 ("KVM/SVM: Allow direct access to MSR_IA32_SPEC_CTRL")

Message-ID: <[email protected]>

---
 arch/x86/kvm/svm/svm.c |   11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)
arch/x86/kvm/svm/svm.c +1 -10
@@ -673,16 +673,7 @@ static void clr_dr_intercepts(struct vcp
 
 static bool msr_write_intercepted(struct vcpu_svm *svm, u32 msr)
 {
-	/*
-	 * For non-nested case:
-	 * If the L01 MSR bitmap does not intercept the MSR, then we need to
-	 * save it.
-	 *
-	 * For nested case:
-	 * If the L02 MSR bitmap does not intercept the MSR, then we need to
-	 * save it.
-	 */
-	void *msrpm = is_guest_mode(&svm->vcpu) ? svm->nested.msrpm : svm->msrpm;
+	void *msrpm = __va(__sme_clr(svm->vmcb->control.msrpm_base_pa));
 
 	return svm_test_msr_bitmap_write(msrpm, msr);
 }
author maintainer
#319 lore raw

PATCH 316/428 KVM: SVM: Preserve TLB control (i.e. pending TLB flush) on failed VMRUN

In reply to Greg Kroah-Hartman

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

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

From: Sean Christopherson <[email protected]>

commit 60d93c27859fb3ec5b5a689e24de303f166477d0 upstream.

Don't reset the VMCB's TLB control back to "do nothing" on a failed VMRUN, as empirical testing shows that the CPU performs the requested TLB flush if and only if VMRUN is successful, i.e. clearing TLB control on a failed VMRUN effectively drops a TLB flush.

Explicitly track the need to flush all ASIDs on a per-CPU basis, as the ASID reuse condition is tied to the pCPU, not to the vCPU. As a bonus, this also obviates the need to avoid clobbering FLUSH_ALL_ASID with TLB_CONTROL_FLUSH_ASID, e.g. in svm_flush_tlb_asid().

Deliberately don't bother saving/restoring the "old" tlb_ctl on failure, in quotes because it's not exactly the old tlb_ctl, it's the tlb_ctl from after pre_svm_run(), but before updating tlb_ctl for flush_all_asids. If VMRUN fails and TLB_CONTROL_FLUSH_ALL_ASID is forced, then the next successful run of the VMCB *may* unnecessarily flush all ASIDs, which strictly speaking could result in noisy neighbor issues. However, the fact that new_asid() is already guest-triggerable, because of KVM's flawed behavior of clearing the ASID on emulated INIT, means that a guest can already trigger a flush of all ASIDs at roughly the same rate. And once KVM stops clobbering the ASID on emulated INIT, *or* assigns a static ASID to each vCPU, this flaw goes away.

Fixes 38e5e92fe8c0 ("KVM: SVM: Implement Flush-By-Asid feature")
Cc Tom Lendacky <[email protected]>
Cc Jim Mattson <[email protected]>

Message-ID: <[email protected]>

---
 arch/x86/kvm/svm/svm.c |   13 ++++++++++---
 arch/x86/kvm/svm/svm.h |    1 +
 2 files changed, 11 insertions(+), 3 deletions(-)
arch/x86/kvm/svm/svm.c +10 -3
@@ -1897,8 +1897,7 @@ static void new_asid(struct vcpu_svm *sv
 	if (sd->next_asid > sd->max_asid) {
 		++sd->asid_generation;
 		sd->next_asid = sd->min_asid;
-		svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ALL_ASID;
-		vmcb_mark_dirty(svm->vmcb, VMCB_ASID);
+		sd->flush_all_asids = true;
 	}
 
 	svm->current_vmcb->asid_generation = sd->asid_generation;
@@ -4518,6 +4517,11 @@ static __no_kcsan fastpath_t svm_vcpu_ru
 		svm->vmcb->control.asid = svm->asid;
 		vmcb_mark_dirty(svm->vmcb, VMCB_ASID);
 	}
+	if (this_cpu_ptr(&svm_data)->flush_all_asids) {
+		svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ALL_ASID;
+		vmcb_mark_dirty(svm->vmcb, VMCB_ASID);
+	}
+
 	svm->vmcb->save.cr2 = vcpu->arch.cr2;
 
 	if (guest_cpu_cap_has(vcpu, X86_FEATURE_ERAPS) &&
@@ -4608,7 +4612,10 @@ static __no_kcsan fastpath_t svm_vcpu_ru
 		vcpu->arch.nested_run_pending = 0;
 	}
 
-	svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING;
+	if (!svm_is_vmrun_failure(svm->vmcb->control.exit_code)) {
+		this_cpu_ptr(&svm_data)->flush_all_asids = false;
+		svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING;
+	}
 
 	/*
 	 * Unconditionally mask off the CLEAR_RAP bit, the AND is just as cheap
arch/x86/kvm/svm/svm.h +1 -0
@@ -376,6 +376,7 @@ struct svm_cpu_data {
 	u32 next_asid;
 	u32 min_asid;
 
+	bool flush_all_asids;
 	bool bp_spec_reduce_set;
 
 	struct vmcb *save_area;
author maintainer
#320 lore raw

PATCH 317/428 KVM: SVM: Update control fields on #VMEXIT if and only if VMRUN succeeded

In reply to Greg Kroah-Hartman

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

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

From: Sean Christopherson <[email protected]>

commit e27fc915c8f575ab5630e27d97465c2077d4b9d1 upstream.

Leave control.erap_ctl and control.clean as-is in the VMCS if VMRUN fails, because as per AMD:

there's no explicit architectural guarantee about the behavior in the presence of VMRUN failures. So the best thing to do would be to assume that if VMRUN fails, the actions requested in the control fields may not have been performed.

Message-ID: <[email protected]>

---
 arch/x86/kvm/svm/svm.c |   16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)
arch/x86/kvm/svm/svm.c +8 -8
@@ -4615,16 +4615,16 @@ static __no_kcsan fastpath_t svm_vcpu_ru
 	if (!svm_is_vmrun_failure(svm->vmcb->control.exit_code)) {
 		this_cpu_ptr(&svm_data)->flush_all_asids = false;
 		svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING;
-	}
 
-	/*
-	 * Unconditionally mask off the CLEAR_RAP bit, the AND is just as cheap
-	 * as the TEST+Jcc to avoid it.
-	 */
-	if (cpu_feature_enabled(X86_FEATURE_ERAPS))
-		svm->vmcb->control.erap_ctl &= ~ERAP_CONTROL_CLEAR_RAP;
+		/*
+		 * Unconditionally mask off the CLEAR_RAP bit, the AND is just
+		 * as cheap as the TEST+Jcc to avoid it.
+		 */
+		if (cpu_feature_enabled(X86_FEATURE_ERAPS))
+			svm->vmcb->control.erap_ctl &= ~ERAP_CONTROL_CLEAR_RAP;
 
-	vmcb_mark_all_clean(svm->vmcb);
+		vmcb_mark_all_clean(svm->vmcb);
+	}
 
 	/* if exit due to PF check for async PF */
 	if (svm->vmcb->control.exit_code == SVM_EXIT_EXCP_BASE + PF_VECTOR)
author maintainer
#321 lore raw

PATCH 318/428 KVM: arm64: Clear HCR_EL2.RW for 32-bit non-protected vCPUs

In reply to Greg Kroah-Hartman

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

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

From: Fuad Tabba <[email protected]>

commit 80ae56184b57176b908cd6a7ececf274a7e4b3c7 upstream.

pKVM keeps its own copy of each vCPU's HCR_EL2 at EL2, initialised with RW set unconditionally. Nothing stops userspace from creating a non-protected AArch32 VM, and with RW set, entering one of its vCPUs is an illegal exception return: KVM_RUN fails with KVM_EXIT_FAIL_ENTRY.

Clear RW for a vCPU that is AArch32 at EL1, when the CPU has AArch32 EL1. On a CPU without it, RW stays set and the entry still fails, rather than EL2 switching *32_EL2 registers that are UNDEFINED there. The host already rejects such a vCPU, but EL2 takes the vCPU's features from the host and doesn't rely on that.

Fixes b56680de9c648 ("KVM: arm64: Initialize trap register values in hyp in pKVM")
---
 arch/arm64/kvm/hyp/nvhe/pkvm.c |    9 +++++++++
 1 file changed, 9 insertions(+)
arch/arm64/kvm/hyp/nvhe/pkvm.c +9 -0
@@ -54,6 +54,15 @@ static void pkvm_vcpu_reset_hcr(struct k
 	else
 		vcpu->arch.hcr_el2 |= HCR_TID2;
 
+	/*
+	 * Without AArch32 EL1, leave RW set and let the entry fail with an
+	 * illegal exception return: the *32_EL2 registers EL2 would otherwise
+	 * switch are UNDEFINED there.
+	 */
+	if (vcpu_has_feature(vcpu, KVM_ARM_VCPU_EL1_32BIT) &&
+	    cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
+		vcpu->arch.hcr_el2 &= ~HCR_EL2_RW;
+
 	if (vcpu_has_ptrauth(vcpu))
 		vcpu->arch.hcr_el2 |= (HCR_API | HCR_APK);
 
author maintainer
#322 lore raw

PATCH 319/428 KVM: arm64: Apply the fine-grained UNDEFs without FEAT_FGT

In reply to Greg Kroah-Hartman

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

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

From: Fuad Tabba <[email protected]>

commit 4bdd2b3a708c1feb701cb8b62feb8ad2fd77bb07 upstream.

FGUs don't actually require hardware support, so don't gate fgt trap config insertion on FGT. This is the only action required to allow FGUs always, as kvm_calculate_traps() already computes the FGU bits with or without FEAT_FGT.

This also fixes a spurious WARN when a TLBI OS is run on a guest in vEL1 without FEAT_TLBIOS on non FEAT_FGT hardware. In this configuration the course-grained HCR_EL2.TTLBOS trap reaches the handler handle_tlbi_el1(), which expects an EL1 TLBI only from vEL2. With FGUs enabled the UNDEF will be injected without needing the specific handler.

Fixes f5a5a406b4b8b ("KVM: arm64: Propagate and handle Fine-Grained UNDEF bits")
[oliver: Take Wei-Lin's suggested changelog]
---
 arch/arm64/kvm/emulate-nested.c |    3 ---
 1 file changed, 3 deletions(-)
arch/arm64/kvm/emulate-nested.c +0 -3
@@ -2378,9 +2378,6 @@ int __init populate_nv_trap_config(void)
 				print_nv_trap_error(fgt, "FGT bit is reserved", ret);
 			}
 
-			if (!cpus_have_final_cap(ARM64_HAS_FGT))
-				continue;
-
 			prev = xa_store(&sr_forward_xa, enc,
 					xa_mk_value(tc.val), GFP_KERNEL);
 
author maintainer
#323 lore raw

PATCH 320/428 KVM: arm64: vgic: Allow last_lr_irq to be NULL when LRs are not overflowing

In reply to Greg Kroah-Hartman

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

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

From: Marc Zyngier <[email protected]>

commit e079ef0b1c40c7a4a1be0c1ac5dbff95685c3beb upstream.

last_lr_irq is always populated when there is any interrupt populated in the AP list. Not only this is not necessary (it is only useful when we completely fill the LRs), but this is in the way of further fixes.

Make sure last_lr_irq is kept to NULL when we LRs are not completely full.

Fixes 6da5e537f5afe ("KVM: arm64: vgic: Pick EOIcount deactivations from AP-list tail")
---
 arch/arm64/kvm/vgic/vgic-v2.c |    6 ++++--
 arch/arm64/kvm/vgic/vgic-v3.c |    6 ++++--
 arch/arm64/kvm/vgic/vgic.c    |    8 +++-----
 3 files changed, 11 insertions(+), 9 deletions(-)
arch/arm64/kvm/vgic/vgic-v2.c +4 -2
@@ -122,6 +122,10 @@ void vgic_v2_fold_lr_state(struct kvm_vc
 	for (int lr = 0; lr < vgic_cpu->vgic_v2.used_lrs; lr++)
 		vgic_v2_fold_lr(vcpu, cpuif->vgic_lr[lr]);
 
+	cpuif->used_lrs = 0;
+	if (!irq)
+		return;
+
 	/* See the GICv3 equivalent for the EOIcount handling rationale */
 	list_for_each_entry_continue(irq, &vgic_cpu->ap_list_head, ap_list) {
 		u32 lr;
@@ -144,8 +148,6 @@ void vgic_v2_fold_lr_state(struct kvm_vc
 		vgic_v2_fold_lr(vcpu, lr);
 		eoicount--;
 	}
-
-	cpuif->used_lrs = 0;
 }
 
 void vgic_v2_deactivate(struct kvm_vcpu *vcpu, u32 val)
arch/arm64/kvm/vgic/vgic-v3.c +4 -2
@@ -155,6 +155,10 @@ void vgic_v3_fold_lr_state(struct kvm_vc
 	for (int lr = 0; lr < cpuif->used_lrs; lr++)
 		vgic_v3_fold_lr(vcpu, cpuif->vgic_lr[lr]);
 
+	cpuif->used_lrs = 0;
+	if (!irq)
+		return;
+
 	/*
 	 * EOIMode=0: use EOIcount to emulate deactivation. We are
 	 * guaranteed to deactivate in reverse order of the activation, so
@@ -188,8 +192,6 @@ void vgic_v3_fold_lr_state(struct kvm_vc
 		vgic_v3_fold_lr(vcpu, lr);
 		eoicount--;
 	}
-
-	cpuif->used_lrs = 0;
 }
 
 void vgic_v3_deactivate(struct kvm_vcpu *vcpu, u64 val)
arch/arm64/kvm/vgic/vgic.c +3 -5
@@ -863,9 +863,6 @@ static void vgic_fold_state(struct kvm_v
 		return;
 	}
 
-	if (!*host_data_ptr(last_lr_irq))
-		return;
-
 	if (kvm_vgic_global_state.type == VGIC_V2)
 		vgic_v2_fold_lr_state(vcpu);
 	else
@@ -1018,11 +1015,12 @@ static void vgic_flush_lr_state(struct k
 		scoped_guard(raw_spinlock,  &irq->irq_lock) {
 			if (likely(vgic_target_oracle(irq) == vcpu)) {
 				vgic_populate_lr(vcpu, irq, count++);
-				*host_data_ptr(last_lr_irq) = irq;
+				if (count == kvm_vgic_global_state.nr_lr)
+					*host_data_ptr(last_lr_irq) = irq;
 			}
 		}
 
-		if (count == kvm_vgic_global_state.nr_lr)
+		if (*host_data_ptr(last_lr_irq))
 			break;
 	}
 
author maintainer
#324 lore raw

PATCH 321/428 KVM: arm64: vgic: Take a refcount on IRQs referenced by last_lr_irq

In reply to Greg Kroah-Hartman

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

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

From: Marc Zyngier <[email protected]>

commit 19bc5d79ece613f242b5ec03921a3b465321bfed upstream.

Referencing the last interrupt inserted in an LR is rather fragile, as this interrupt can vanish if a concurrently unmapped LPI.

Solve this by bumping up the refcount on the interrupt when populating last_lr_irq, and drop it at vgic_prune_ap_list() time, when LPIs are being reclaimed.

---
 arch/arm64/kvm/vgic/vgic.c |   15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)
arch/arm64/kvm/vgic/vgic.c +14 -1
@@ -850,6 +850,17 @@ retry:
 		goto retry;
 	}
 
+	/*
+	 * Fix the last_lr_irq refcount which was obtained while
+	 * populating the LRs. This can also result in the LPI being
+	 * deleted.
+	 */
+	irq = *host_data_ptr(last_lr_irq);
+	if (irq) {
+		deleted_lpis |= vgic_put_irq_norelease(vcpu->kvm, irq);
+		*host_data_ptr(last_lr_irq) = NULL;
+	}
+
 	raw_spin_unlock(&vgic_cpu->ap_list_lock);
 
 	if (unlikely(deleted_lpis))
@@ -1015,8 +1026,10 @@ static void vgic_flush_lr_state(struct k
 		scoped_guard(raw_spinlock,  &irq->irq_lock) {
 			if (likely(vgic_target_oracle(irq) == vcpu)) {
 				vgic_populate_lr(vcpu, irq, count++);
-				if (count == kvm_vgic_global_state.nr_lr)
+				if (count == kvm_vgic_global_state.nr_lr) {
+					vgic_get_irq_ref(irq);
 					*host_data_ptr(last_lr_irq) = irq;
+				}
 			}
 		}
 
author maintainer
#325 lore raw

PATCH 322/428 KVM: arm64: vgic-its: Fix MOVALL handling of source redistributor

In reply to Greg Kroah-Hartman

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

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

From: Marc Zyngier <[email protected]>

commit 751f4641560be8568dcc5af400c0e55a283abf51 upstream.

The MOVALL command moves the pending state of all LPIs targeting a given redistributor to another one. However, we seem to have lost the filtering on the source RD, which means we move all LPIs to the target.

Not quite what the spec mandates.

Hack update_affinity() to take an optional source vcpu that is used as a filter when non-NULL, restoring the filtering that was performed by vgic_copy_lpi_list() back in the days.

Fixes 11f4f8f3e6e06 ("KVM: arm64: vgic-its: Walk LPI xarray in vgic_its_cmd_handle_movall()")
---
 arch/arm64/kvm/vgic/vgic-its.c |   12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)
arch/arm64/kvm/vgic/vgic-its.c +8 -4
@@ -319,12 +319,16 @@ static int update_lpi_config(struct kvm
 	return ret;
 }
 
-static int update_affinity(struct vgic_irq *irq, struct kvm_vcpu *vcpu)
+static int update_affinity(struct vgic_irq *irq,
+			   struct kvm_vcpu *from_vcpu, struct kvm_vcpu *vcpu)
 {
 	struct its_vlpi_map map;
 	int ret;
 
 	guard(raw_spinlock_irqsave)(&irq->irq_lock);
+	if (from_vcpu && irq->target_vcpu != from_vcpu)
+		return 0;
+
 	irq->target_vcpu = vcpu;
 
 	if (!irq->hw)
@@ -362,7 +366,7 @@ static void update_affinity_ite(struct k
 		return;
 
 	vcpu = collection_to_vcpu(kvm, ite->collection);
-	update_affinity(ite->irq, vcpu);
+	update_affinity(ite->irq, NULL, vcpu);
 }
 
 /*
@@ -856,7 +860,7 @@ static int vgic_its_cmd_handle_movi(stru
 
 	vgic_its_invalidate_cache(its);
 
-	return update_affinity(ite->irq, vcpu);
+	return update_affinity(ite->irq, NULL, vcpu);
 }
 
 static bool __is_visible_gfn_locked(struct vgic_its *its, gpa_t gpa)
@@ -1383,7 +1387,7 @@ static int vgic_its_cmd_handle_movall(st
 		if (!irq)
 			continue;
 
-		update_affinity(irq, vcpu2);
+		update_affinity(irq, vcpu1, vcpu2);
 
 		vgic_put_irq(kvm, irq);
 	}
author maintainer
#326 lore raw

PATCH 323/428 KVM: arm64: Use stage-1 leaf size for VM_PFNMAP

In reply to Greg Kroah-Hartman

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

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

From: Mostafa Saleh <[email protected]>

commit 71cc2c67fb8f8d5aa8154eb482e9846d2214f11b upstream.

When commit 2aa53d68cee6 ("KVM: arm64: Try stage2 block mapping for host device MMIO") added VM_PFNMAP support to get_vma_page_shift(), stage-1 page tables did not support huge PFNMAP (as in VFIO-PCI vfio_pci_mmap_huge_fault()) and transparent_hugepage_adjust() was unsafe for MMIO as it dereferenced struct page.

Since commit 6011cf68c885 ("KVM: arm64: Walk userspace page tables to compute the THP mapping size"), transparent_hugepage_adjust() instead walks the host stage-1 page tables via get_user_mapping_size() without touching struct page. Meanwhile, commit 3e509c9b03f9 ("mm/arm64:
support large pfn mappings") enabled stage-1 huge PFNMAP.

So. we can drop the VMA-based VM_PFNMAP size calculation in get_vma_page_shift() and let transparent_hugepage_adjust() derive the stage-2 mapping size directly from the populated stage-1 leaf for non-cacheable mappings as well.

Assisted-by LLM
Fixes 2aa53d68cee6 ("KVM: arm64: Try stage2 block mapping for host device MMIO")
---
 arch/arm64/kvm/mmu.c |   47 ++++++-----------------------------------------
 1 file changed, 6 insertions(+), 41 deletions(-)
arch/arm64/kvm/mmu.c +6 -41
@@ -1468,32 +1468,11 @@ transparent_hugepage_adjust(struct kvm *
 	return PAGE_SIZE;
 }
 
-static int get_vma_page_shift(struct vm_area_struct *vma, unsigned long hva)
+static int get_vma_page_shift(struct vm_area_struct *vma)
 {
-	unsigned long pa;
-
-	if (is_vm_hugetlb_page(vma) && !(vma->vm_flags & VM_PFNMAP))
+	if (is_vm_hugetlb_page(vma))
 		return huge_page_shift(hstate_vma(vma));
 
-	if (!(vma->vm_flags & VM_PFNMAP))
-		return PAGE_SHIFT;
-
-	VM_BUG_ON(is_vm_hugetlb_page(vma));
-
-	pa = (vma->vm_pgoff << PAGE_SHIFT) + (hva - vma->vm_start);
-
-#ifndef __PAGETABLE_PMD_FOLDED
-	if ((hva & (PUD_SIZE - 1)) == (pa & (PUD_SIZE - 1)) &&
-	    ALIGN_DOWN(hva, PUD_SIZE) >= vma->vm_start &&
-	    ALIGN(hva, PUD_SIZE) <= vma->vm_end)
-		return PUD_SHIFT;
-#endif
-
-	if ((hva & (PMD_SIZE - 1)) == (pa & (PMD_SIZE - 1)) &&
-	    ALIGN_DOWN(hva, PMD_SIZE) >= vma->vm_start &&
-	    ALIGN(hva, PMD_SIZE) <= vma->vm_end)
-		return PMD_SHIFT;
-
 	return PAGE_SHIFT;
 }
 
@@ -1794,7 +1773,7 @@ static short kvm_s2_resolve_vma_size(con
 		vma_shift = PAGE_SHIFT;
 	} else {
 		s2vi->max_map_size = PUD_SIZE;
-		vma_shift = get_vma_page_shift(vma, s2fd->hva);
+		vma_shift = get_vma_page_shift(vma);
 	}
 
 	switch (vma_shift) {
@@ -1952,16 +1931,6 @@ static int kvm_s2_fault_pin_pfn(const st
 				return -EFAULT;
 			}
 		} else {
-			/*
-			 * If the page was identified as device early by looking at
-			 * the VMA flags, vma_pagesize is already representing the
-			 * largest quantity we can map.  If instead it was mapped
-			 * via __kvm_faultin_pfn(), vma_pagesize is set to PAGE_SIZE
-			 * and must not be upgraded.
-			 *
-			 * In both cases, we don't let transparent_hugepage_adjust()
-			 * change things at the last minute.
-			 */
 			s2vi->map_non_cacheable = true;
 		}
 
@@ -2051,10 +2020,10 @@ static int kvm_s2_fault_map(const struct
 
 	/*
 	 * If we are not forced to use page mapping, check if we are
-	 * backed by a THP and thus use block mapping if possible.
+	 * backed by a huge stage-1 mapping and thus use block mapping if
+	 * possible.
 	 */
-	if (mapping_size == PAGE_SIZE &&
-	    !(s2vi->max_map_size == PAGE_SIZE || s2vi->map_non_cacheable)) {
+	if (mapping_size == PAGE_SIZE && s2vi->max_map_size != PAGE_SIZE) {
 		if (perm_fault_granule > PAGE_SIZE) {
 			mapping_size = perm_fault_granule;
 		} else {
@@ -2132,10 +2101,6 @@ static int user_mem_abort(const struct k
 			return ret;
 	}
 
-	/*
-	 * Let's check if we will get back a huge page backed by hugetlbfs, or
-	 * get block mapping for device MMIO region.
-	 */
 	ret = kvm_s2_fault_pin_pfn(s2fd, &s2vi);
 	if (ret != 1)
 		return ret;
author maintainer
#327 lore raw

PATCH 324/428 wifi: cfg80211: fix RTS threshold setting for single-radio PHY

In reply to Greg Kroah-Hartman

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

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

From: Stanislaw Gruszka <[email protected]>

commit 9eda41e32263806a66835e275efdcc0c5f46bbc4 upstream.

Due to an incorrect old_radio_rts_threshold check, we do not set rdev->wiphy.rts_threshold for a single-radio PHY, and the threshold value is not propagated to lower layers.

Fixes 264637941cf4 ("wifi: cfg80211: Add Support to Set RTS Threshold for each Radio")
---
 net/wireless/nl80211.c |   11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)
net/wireless/nl80211.c +6 -5
@@ -4562,12 +4562,13 @@ static int nl80211_set_wiphy(struct sk_b
 			rdev->wiphy.retry_long = retry_long;
 		if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
 			rdev->wiphy.frag_threshold = frag_threshold;
-		if ((changed & WIPHY_PARAM_RTS_THRESHOLD) &&
-		    old_radio_rts_threshold) {
+		if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
 			rdev->wiphy.rts_threshold = rts_threshold;
-			for (i = 0 ; i < rdev->wiphy.n_radio; i++)
-				rdev->wiphy.radio_cfg[i].rts_threshold =
-					rdev->wiphy.rts_threshold;
+			if (old_radio_rts_threshold) {
+				for (i = 0; i < rdev->wiphy.n_radio; i++)
+					rdev->wiphy.radio_cfg[i].rts_threshold =
+						rdev->wiphy.rts_threshold;
+			}
 		}
 		if (changed & WIPHY_PARAM_COVERAGE_CLASS)
 			rdev->wiphy.coverage_class = coverage_class;
author maintainer
#328 lore raw

PATCH 325/428 wifi: cw1200: fix TX padding OOB read leaking heap data to device

In reply to Greg Kroah-Hartman

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

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

From: Tianchu Chen <[email protected]>

commit 7c1611780823b76219cb1478db53dfd890c4a9a2 upstream.

The TX path rounds the transfer size up to the bus alignment (align_size(), up to 511 bytes for SDIO, 1 byte for SPI) but sends the padded length straight from the frame buffer, which has no room reserved for the padding. cw1200_data_write() therefore reads past the end of the frame and the bus transfer carries those bytes to the device. A bogus device can collect up to 511 bytes of kernel heap data per TX frame.

The RX path does not have this problem: it allocates the skb with the aligned length (alloc_len) before reading.

Compute the exact padding with the bus align_size() callback, make sure queued TX frames have tailroom for it, and zero the padding bytes before the transfer so nothing past the frame is exposed.

This is not expected to change driver behavior on most cases: frame contents and transfer sizes are unchanged, only the content of the padding bytes (previously undefined stale memory, now zero), which the device never reads since the WSM header carries the real frame length. The skb expansion can only fail under memory exhaustion, in which case the frame is dropped like on the existing error paths of cw1200_tx().

Discovered by Atuin - Automated Vulnerability Discovery Engine.

Fixes a910e4a94f69 ("cw1200: add driver for the ST-E CW1100 & CW1200 WLAN chipsets")
Assisted-by LLM
---
 drivers/net/wireless/st/cw1200/bh.c   |    8 +++++++-
 drivers/net/wireless/st/cw1200/txrx.c |   14 ++++++++++++++
 2 files changed, 21 insertions(+), 1 deletion(-)
drivers/net/wireless/st/cw1200/bh.c +7 -1
@@ -344,6 +344,7 @@ static int cw1200_bh_tx_helper(struct cw
 			       int *tx_burst)
 {
 	size_t tx_len;
+	size_t aligned_len;
 	u8 *data;
 	int ret;
 	struct wsm_hdr *wsm;
@@ -376,9 +377,14 @@ static int cw1200_bh_tx_helper(struct cw
 
 	atomic_inc(&priv->bh_tx);
 
-	tx_len = priv->hwbus_ops->align_size(
+	aligned_len = priv->hwbus_ops->align_size(
 		priv->hwbus_priv, tx_len);
 
+	/* Zero the bus alignment padding so no stale data is sent. */
+	if (aligned_len > tx_len)
+		memset(data + tx_len, 0, aligned_len - tx_len);
+	tx_len = aligned_len;
+
 	/* Check if not exceeding CW1200 capabilities */
 	if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
 		pr_debug("Write aligned len: %zu\n", tx_len);
drivers/net/wireless/st/cw1200/txrx.c +14 -0
@@ -14,6 +14,7 @@
 #include "wsm.h"
 #include "bh.h"
 #include "sta.h"
+#include "hwbus.h"
 #include "debug.h"
 
 #define CW1200_INVALID_RATE_ID (0xFF)
@@ -716,6 +717,7 @@ void cw1200_tx(struct ieee80211_hw *dev,
 	struct ieee80211_sta *sta;
 	struct wsm_tx *wsm;
 	bool tid_update = false;
+	size_t pad_len;
 	u8 flags = 0;
 	int ret;
 
@@ -764,6 +766,18 @@ void cw1200_tx(struct ieee80211_hw *dev,
 
 	sta = t.sta;
 
+	/*
+	 * The bus transfer is padded up to the bus alignment and the
+	 * padding is transferred from the frame buffer.  Make sure the
+	 * buffer has room for the padding so the device never receives
+	 * data past the end of the frame.
+	 */
+	pad_len = priv->hwbus_ops->align_size(priv->hwbus_priv, skb->len) -
+		skb->len;
+	if (skb_tailroom(skb) < pad_len &&
+	    pskb_expand_head(skb, 0, pad_len, GFP_ATOMIC))
+		goto drop;
+
 	spin_lock_bh(&priv->ps_state_lock);
 	{
 		tid_update = cw1200_tx_h_pm_state(priv, &t);
author maintainer
#329 lore raw

PATCH 326/428 wifi: iwlegacy: 3945: free EEPROM data on remove

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit f8ebe286394c0bbb4a6dbacb182a34ab6376922d upstream.

il3945_pci_probe() calls il_eeprom_init(), which allocates memory for the EEPROM data. The probe failure paths release this allocation with il_eeprom_free(), but the remove path does not free it after a successful probe.

As a result, the EEPROM data is leaked when the PCI device is removed.

Free the EEPROM data in il3945_pci_remove() before releasing the mac80211 hardware structure.

This issue was found by manual code inspection.

---
 drivers/net/wireless/intel/iwlegacy/3945-mac.c |    1 +
 1 file changed, 1 insertion(+)
drivers/net/wireless/intel/iwlegacy/3945-mac.c +1 -0
@@ -3807,6 +3807,7 @@ il3945_pci_remove(struct pci_dev *pdev)
 
 	il_free_channel_map(il);
 	il_free_geos(il);
+	il_eeprom_free(il);
 	kfree(il->scan_cmd);
 	dev_kfree_skb(il->beacon_skb);
 	ieee80211_free_hw(il->hw);
author maintainer
#330 lore raw

PATCH 327/428 wifi: mac80211: count only matching reservations in reserved switch

In reply to Greg Kroah-Hartman

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

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

From: Zhiling Zou <[email protected]>

commit 437f3727b4fd715266c296435c116288f82666fd upstream.

ieee80211_vif_use_reserved_switch() validates a replacement by counting assigned links on ctx->replace_ctx and treating reservations too broadly. A link can still be assigned to the old channel context while holding a reservation that belongs to a different replacement operation.

That can make the current replacement appear complete too early. If another link finalizes first, the function can free the WILL_BE_REPLACED chanctx while the unmatched link and driver still use it, leading to a use-after-free in later beacon generation.

Require an in-place reservation to point at the current replacement context, and require the reserved and assigned contexts to be the matching replacement pair, before counting or moving the link.

---
 net/mac80211/chan.c |   30 +++++++++++++++++++++++-------
 1 file changed, 23 insertions(+), 7 deletions(-)
net/mac80211/chan.c +23 -7
@@ -1770,7 +1770,8 @@ out:
 }
 
 static bool
-ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link)
+ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link,
+					struct ieee80211_chanctx *ctx)
 {
 	struct ieee80211_sub_if_data *sdata = link->sdata;
 	struct ieee80211_chanctx *old_ctx, *new_ctx;
@@ -1780,6 +1781,9 @@ ieee80211_link_has_in_place_reservation(
 	new_ctx = link->reserved_chanctx;
 	old_ctx = ieee80211_link_get_chanctx(link);
 
+	if (new_ctx != ctx)
+		return false;
+
 	if (!old_ctx)
 		return false;
 
@@ -1792,6 +1796,12 @@ ieee80211_link_has_in_place_reservation(
 	if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
 		return false;
 
+	if (new_ctx->replace_ctx != old_ctx)
+		return false;
+
+	if (old_ctx->replace_ctx != new_ctx)
+		return false;
+
 	return true;
 }
 
@@ -1821,7 +1831,8 @@ static int ieee80211_chsw_switch_vifs(st
 		}
 
 		for_each_chanctx_user_reserved(local, ctx, &iter) {
-			if (!ieee80211_link_has_in_place_reservation(iter.link))
+			if (!ieee80211_link_has_in_place_reservation(iter.link,
+								     ctx))
 				continue;
 
 			old_ctx = ieee80211_link_get_chanctx(iter.link);
@@ -1923,7 +1934,9 @@ static int ieee80211_vif_use_reserved_sw
 
 		for_each_chanctx_user_assigned(local, ctx->replace_ctx, &iter) {
 			n_assigned++;
-			if (iter.link && iter.link->reserved_chanctx) {
+			if (iter.link &&
+			    ieee80211_link_has_in_place_reservation(iter.link,
+								    ctx)) {
 				n_reserved++;
 				if (iter.link->reserved_ready)
 					n_ready++;
@@ -1948,7 +1961,8 @@ static int ieee80211_vif_use_reserved_sw
 use_reserved:
 		ctx->conf.radar_enabled = false;
 		for_each_chanctx_user_reserved(local, ctx, &iter) {
-			if (ieee80211_link_has_in_place_reservation(iter.link) &&
+			if (ieee80211_link_has_in_place_reservation(iter.link,
+								    ctx) &&
 			    !iter.link->reserved_ready)
 				return -EAGAIN;
 
@@ -1989,7 +2003,8 @@ use_reserved:
 		}
 
 		for_each_chanctx_user_reserved(local, ctx, &iter) {
-			if (!ieee80211_link_has_in_place_reservation(iter.link))
+			if (!ieee80211_link_has_in_place_reservation(iter.link,
+								     ctx))
 				continue;
 
 			ieee80211_chan_bw_change(local,
@@ -2038,7 +2053,7 @@ use_reserved:
 			struct ieee80211_bss_conf *link_conf = link->conf;
 			u64 changed = 0;
 
-			if (!ieee80211_link_has_in_place_reservation(link))
+			if (!ieee80211_link_has_in_place_reservation(link, ctx))
 				continue;
 
 			rcu_assign_pointer(link_conf->chanctx_conf,
@@ -2089,7 +2104,8 @@ use_reserved:
 		for_each_chanctx_user_reserved(local, ctx, &iter) {
 			struct ieee80211_link_data *link = iter.link;
 
-			if (WARN_ON(ieee80211_link_has_in_place_reservation(link)))
+			if (WARN_ON(ieee80211_link_has_in_place_reservation(link,
+									    ctx)))
 				continue;
 
 			if (!link->reserved_ready)
author maintainer
#331 lore raw

PATCH 328/428 wifi: mac80211: keep paired old chanctx alive

In reply to Greg Kroah-Hartman

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

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

From: Zhiling Zou <[email protected]>

commit 3412e9a800786d1dec4a58367db1cba275e7ef11 upstream.

An in-place replacement keeps reciprocal replace_ctx pointers between the old WILL_BE_REPLACED chanctx and the new REPLACES_OTHER chanctx. However, the early free paths only consider assigned and reserved link users when deciding whether to free the old chanctx.

That lets the old chanctx be freed too early while the replacement partner still points back to it, either when the last assigned link is released or when that link successfully reassigns to another existing context. Later unreserve and switch-finalization paths can then follow a stale replace_ctx pointer.

Keep a paired old chanctx alive until the replacement pair has been torn down. Apply the same check to both __ieee80211_link_release_channel() and ieee80211_link_use_reserved_reassign(). If the replacement is later abandoned, ieee80211_link_unreserve_chanctx() already tears down the pairing before freeing the old chanctx once no users remain.

---
 net/mac80211/chan.c |   20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)
net/mac80211/chan.c +18 -2
@@ -231,6 +231,13 @@ int ieee80211_chanctx_refcount(struct ie
 	return num;
 }
 
+static bool
+ieee80211_chanctx_has_replace_partner(struct ieee80211_chanctx *ctx)
+{
+	return ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED &&
+	       ctx->replace_ctx;
+}
+
 static int ieee80211_num_chanctx(struct ieee80211_local *local, int radio_idx)
 {
 	struct ieee80211_chanctx *ctx;
@@ -1403,6 +1410,7 @@ void ieee80211_link_unreserve_chanctx(st
 {
 	struct ieee80211_sub_if_data *sdata = link->sdata;
 	struct ieee80211_chanctx *ctx = link->reserved_chanctx;
+	struct ieee80211_chanctx *old_ctx = NULL;
 
 	lockdep_assert_wiphy(sdata->local->hw.wiphy);
 
@@ -1416,6 +1424,7 @@ void ieee80211_link_unreserve_chanctx(st
 			if (WARN_ON(!ctx->replace_ctx))
 				return;
 
+			old_ctx = ctx->replace_ctx;
 			WARN_ON(ctx->replace_ctx->replace_state !=
 			        IEEE80211_CHANCTX_WILL_BE_REPLACED);
 			WARN_ON(ctx->replace_ctx->replace_ctx != ctx);
@@ -1426,6 +1435,11 @@ void ieee80211_link_unreserve_chanctx(st
 
 			list_del_rcu(&ctx->list);
 			kfree_rcu(ctx, rcu_head);
+
+			if (ieee80211_chanctx_refcount(sdata->local,
+						       old_ctx) == 0)
+				ieee80211_free_chanctx(sdata->local, old_ctx,
+						       false);
 		} else {
 			ieee80211_free_chanctx(sdata->local, ctx, false);
 		}
@@ -1705,7 +1719,8 @@ ieee80211_link_use_reserved_reassign(str
 
 	ieee80211_check_fast_xmit_iface(sdata);
 
-	if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
+	if (ieee80211_chanctx_refcount(local, old_ctx) == 0 &&
+	    !ieee80211_chanctx_has_replace_partner(old_ctx))
 		ieee80211_free_chanctx(local, old_ctx, false);
 
 	ieee80211_recalc_chanctx_min_def(local, new_ctx);
@@ -2190,7 +2205,8 @@ void __ieee80211_link_release_channel(st
 	}
 
 	ieee80211_assign_link_chanctx(link, NULL, false);
-	if (ieee80211_chanctx_refcount(local, ctx) == 0)
+	if (ieee80211_chanctx_refcount(local, ctx) == 0 &&
+	    !ieee80211_chanctx_has_replace_partner(ctx))
 		ieee80211_free_chanctx(local, ctx, skip_idle_recalc);
 
 	link->radar_required = false;
author maintainer
#332 lore raw

PATCH 329/428 wifi: mac80211: shut down RX BA session timer on teardown

In reply to Greg Kroah-Hartman

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

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

From: Zhao Li <[email protected]>

commit e588e83e5c7a0f6d7a77dbbb58927c8c8cfd598e upstream.

Tearing down an RX BA session can leave the session timer queued on a freed tid_rx. Later timer-wheel processing and the timer callback itself then run against freed memory.

An RX BAR handler can dereference tid_rx under RCU before teardown unpublishes it and stay in its read-side critical section while teardown deletes the session timer. The stale handler can then rearm the timer after timer_delete_sync() returns and before the RCU callback frees tid_rx.

Use timer_shutdown_sync() so rearm attempts become ineffective once teardown starts. Keep reorder_timer unchanged: teardown marks the session removed under reorder_lock and its release path checks that state before rearming. session_timer has no equivalent guard because ieee80211_rx_h_ctrl() rearms it before taking reorder_lock.

Fixes a87f736d942c ("mac80211: use RCU for RX aggregation")
Assisted-by LLM sparse kasan
---
 net/mac80211/agg-rx.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/mac80211/agg-rx.c +1 -1
@@ -102,7 +102,7 @@ void __ieee80211_stop_rx_ba_session(stru
 	if (!tid_rx)
 		return;
 
-	timer_delete_sync(&tid_rx->session_timer);
+	timer_shutdown_sync(&tid_rx->session_timer);
 
 	/* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */
 	spin_lock_bh(&tid_rx->reorder_lock);
author maintainer
#333 lore raw

PATCH 330/428 wifi: mac80211: drain PS delivery work during station teardown

In reply to Greg Kroah-Hartman

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

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

From: Zhao Li <[email protected]>

commit 67a9e80c3ca20c9044cd6ee72cd6278dd19b5023 upstream.

Removing an AP or mesh station can notify the driver and wake TXQs after the station has already been removed from the driver, and can update num_sta_ps and the power-save queues after station cleanup has purged them.

sta_deliver_ps_frames() tests sta->dead only before it starts delivery. A worker that passes that test can be delayed while teardown sets sta->dead and removes the station from the driver; when it resumes it still runs the full delivery.

The existing cancel_work_sync() sits near the end of __cleanup_single_sta(), after the final driver state transition and after PS accounting and queue purging, so it drains the worker only once the state it has to protect has already changed. Before commit d34ba2168a3c ("mac80211: don't delay station destruction") cleanup was deferred through the same ordered workqueue as the delivery work, which kept the two ordered.

Cancel the work after setting sta->dead and before moving the driver state, and move the existing cancellation to the start of __cleanup_single_sta(). The first drain closes the driver-lifetime window; driver callbacks during the remaining state transitions can still queue drv_deliver_wk through ieee80211_sta_block_awake(), so retain the second drain at the start of __cleanup_single_sta(). That drain also covers insertion failures, which reach cleanup without setting sta->dead.

Fixes d34ba2168a3c ("mac80211: don't delay station destruction")
Assisted-by LLM sparse smatch coccinelle kasan
---
 net/mac80211/sta_info.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
net/mac80211/sta_info.c +4 -2
@@ -137,6 +137,8 @@ static void __cleanup_single_sta(struct
 	struct ieee80211_local *local = sdata->local;
 	struct ps_data *ps;
 
+	cancel_work_sync(&sta->drv_deliver_wk);
+
 	if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
 	    test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
 	    test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
@@ -166,8 +168,6 @@ static void __cleanup_single_sta(struct
 	if (ieee80211_vif_is_mesh(&sdata->vif))
 		mesh_sta_cleanup(sta);
 
-	cancel_work_sync(&sta->drv_deliver_wk);
-
 	/*
 	 * Destroy aggregation state here. It would be nice to wait for the
 	 * driver to finish aggregation stop and then clean up, but for now
@@ -1582,6 +1582,8 @@ static void __sta_info_destroy_part2(str
 
 	sta->dead = true;
 
+	cancel_work_sync(&sta->drv_deliver_wk);
+
 	local->num_sta--;
 	local->sta_generation++;
 
author maintainer
#334 lore raw

PATCH 331/428 wifi: mac80211: account proxy paths against the mesh path limit

In reply to Greg Kroah-Hartman

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

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

From: Zhao Li <[email protected]>

commit a5f35d4b112af1415ff06bd501c8cc26cc08aa8b upstream.

Proxy-path churn can drive the interface path counter negative and let later mesh path additions exceed MESH_MAX_MPATHS.

mesh_path_free_rcu() decrements the interface mpaths counter for entries from both the mesh and proxy path tables, but mpp_path_add() never reserves a slot in that counter. Removing or expiring a proxy path therefore returns a slot that was never charged.

mesh_path_add() uses the same counter to enforce MESH_MAX_MPATHS, so once it falls below the number of installed paths the limit no longer holds.

Reserve the shared quota before allocating a proxy path and release it on every unsuccessful addition. Mesh and proxy paths now share one 1024-entry budget, so mpp_path_add() can return -ENOSPC at that limit.

Fixes ece1a2e7e860 ("mac80211: Remove mesh paths when an interface is removed")
Assisted-by LLM sparse kasan
---
 net/mac80211/mesh_pathtbl.c |   13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)
net/mac80211/mesh_pathtbl.c +10 -3
@@ -733,10 +733,15 @@ int mpp_path_add(struct ieee80211_sub_if
 	if (is_multicast_ether_addr(dst))
 		return -EOPNOTSUPP;
 
+	if (!atomic_add_unless(&sdata->u.mesh.mpaths, 1, MESH_MAX_MPATHS))
+		return -ENOSPC;
+
 	new_mpath = mesh_path_new(sdata, dst, GFP_ATOMIC);
 
-	if (!new_mpath)
+	if (!new_mpath) {
+		atomic_dec(&sdata->u.mesh.mpaths);
 		return -ENOMEM;
+	}
 
 	memcpy(new_mpath->mpp, mpp, ETH_ALEN);
 	tbl = &sdata->u.mesh.mpp_paths;
@@ -749,10 +754,12 @@ int mpp_path_add(struct ieee80211_sub_if
 		hlist_add_head_rcu(&new_mpath->walk_list, &tbl->walk_head);
 	spin_unlock_bh(&tbl->walk_lock);
 
-	if (ret)
+	if (ret) {
 		kfree(new_mpath);
-	else
+		atomic_dec(&sdata->u.mesh.mpaths);
+	} else {
 		mesh_fast_tx_flush_addr(sdata, dst);
+	}
 
 	sdata->u.mesh.mpp_paths_generation++;
 	return ret;
author maintainer
#335 lore raw

PATCH 332/428 wifi: mac80211: keep fallback association elements alive

In reply to Greg Kroah-Hartman

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

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

From: Zhao Li <[email protected]>

commit a10a2f80476d166fd81c361ec0319493671b694e upstream.

Associating to a non-transmitted MBSSID profile reads freed memory while determining the operating channel and applying HT capabilities.

When an association response omits WMM, HT, or VHT information, ieee80211_assoc_config_link() parses the cached BSS IEs and copies the missing element pointers out of that fallback parse. For a non-transmitted MBSSID profile those pointers refer to merged-profile storage owned by the parse object, and the fallback block frees that object at its end. The copied pointers are used afterwards to determine the operating channel, configure bandwidth, and apply HT capabilities.

Keep the fallback parse result alive until association setup finishes.

Fixes 5023b14cf4df ("mac80211: support profile split between elements")
Assisted-by LLM sparse kasan
[reorder variable declaration]
---
 net/mac80211/mlme.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/mac80211/mlme.c +2 -2
@@ -5871,6 +5871,7 @@ static bool ieee80211_assoc_config_link(
 	bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
 	bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ;
 	const struct cfg80211_bss_ies *bss_ies = NULL;
+	struct ieee802_11_elems *bss_elems = NULL;
 	struct ieee80211_supported_band *sband;
 	struct ieee802_11_elems *elems;
 	const __le16 prof_bss_param_ch_present =
@@ -5954,7 +5955,6 @@ static bool ieee80211_assoc_config_link(
 	     (is_5ghz && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_VHT &&
 	      (!elems->vht_cap_elem || !elems->vht_operation)))) {
 		const struct cfg80211_bss_ies *ies;
-		struct ieee802_11_elems *bss_elems;
 
 		rcu_read_lock();
 		ies = rcu_dereference(cbss->ies);
@@ -6016,7 +6016,6 @@ static bool ieee80211_assoc_config_link(
 					   "AP bug: VHT operation missing from AssocResp\n");
 			}
 		}
-		kfree(bss_elems);
 	}
 
 	/*
@@ -6289,6 +6288,7 @@ static bool ieee80211_assoc_config_link(
 	ret = true;
 out:
 	kfree(elems);
+	kfree(bss_elems);
 	kfree(bss_ies);
 	return ret;
 }
author maintainer
#336 lore raw

PATCH 333/428 wifi: mac80211: minstrel_ht: validate fixed rate index

In reply to Greg Kroah-Hartman

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

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

From: Yuqi Xu <[email protected]>

commit 15fb9e3cea55fbc378261ce35a32b0f8828f0000 upstream.

The fixed_rate_idx debugfs file is a plain u32 attribute, so any value can be written to it. minstrel_ht_update_stats() then copies the value into mi->max_tp_rate[] and mi->max_prob_rate, and minstrel_ht_set_rate() uses MI_RATE_GROUP()/MI_RATE_IDX() to index minstrel_mcs_groups[] (42 entries) and mi->groups[].rates[] (10 entries) with it.

Writing e.g. 65535 selects group 4095 and rate index 15, far outside both arrays. The out-of-bounds entries are dereferenced and updated as struct minstrel_rate_stats (retry counts, etc.), so the invalid index corrupts adjacent kernel memory. With CONFIG_UBSAN_BOUNDS the access is reported as an array-index-out-of-bounds in minstrel_ht_set_rate().

Only a valid group/rate pair, or U32_MAX to disable fixed rate processing, can be used safely, so reject any other value when the debugfs file is written.

The file is only reachable through a root-only debugfs mount, so this is not a privilege boundary. The out-of-bounds access is still a bug that must not be reachable through a writable debugfs attribute.

---
 net/mac80211/rc80211_minstrel_ht.c |   30 ++++++++++++++++++++++++++++--
 1 file changed, 28 insertions(+), 2 deletions(-)
net/mac80211/rc80211_minstrel_ht.c +28 -2
@@ -7,6 +7,7 @@
 #include <linux/types.h>
 #include <linux/skbuff.h>
 #include <linux/debugfs.h>
+#include <linux/limits.h>
 #include <linux/random.h>
 #include <linux/moduleparam.h>
 #include <linux/ieee80211.h>
@@ -1994,14 +1995,39 @@ minstrel_ht_alloc(struct ieee80211_hw *h
 }
 
 #ifdef CONFIG_MAC80211_DEBUGFS
+static int minstrel_ht_fixed_rate_idx_get(void *data, u64 *val)
+{
+	*val = *(u32 *)data;
+	return 0;
+}
+
+static int minstrel_ht_fixed_rate_idx_set(void *data, u64 val)
+{
+	u32 idx = val;
+
+	/* U32_MAX is the default and keeps fixed rate processing disabled */
+	if (val != U32_MAX &&
+	    (val > U16_MAX ||
+	     MI_RATE_GROUP(idx) >= ARRAY_SIZE(minstrel_mcs_groups) ||
+	     MI_RATE_IDX(idx) >= MCS_GROUP_RATES))
+		return -EINVAL;
+
+	*(u32 *)data = idx;
+	return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(minstrel_ht_fixed_rate_idx_fops,
+			 minstrel_ht_fixed_rate_idx_get,
+			 minstrel_ht_fixed_rate_idx_set, "%llu\n");
+
 static void minstrel_ht_add_debugfs(struct ieee80211_hw *hw, void *priv,
 				    struct dentry *debugfsdir)
 {
 	struct minstrel_priv *mp = priv;
 
 	mp->fixed_rate_idx = (u32) -1;
-	debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
-			   &mp->fixed_rate_idx);
+	debugfs_create_file("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
+			    &mp->fixed_rate_idx, &minstrel_ht_fixed_rate_idx_fops);
 }
 #endif
 
author maintainer
#337 lore raw

PATCH 334/428 wifi: mac80211: reject invalid 320 MHz CSA bandwidth

In reply to Greg Kroah-Hartman

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

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

From: Ruide Cao <[email protected]>

commit 56e236759f0a8dc910c2ae7cd3c161f298ee5794 upstream.

The HT/VHT channel definition validator can receive a 320 MHz bandwidth indication from a received CSA frame on a non-6 GHz link. It warns for this unsupported width but continues with an uninitialized vht_operation.chan_width, which is then read by ieee80211_chandef_vht_oper(). With panic_on_warn enabled, this lets a received frame panic the kernel.

Reject the channel definition before entering the VHT operation conversion. This preserves the existing CSA fallback while avoiding both the warning and the uninitialized read.

---
 net/mac80211/spectmgmt.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/mac80211/spectmgmt.c +2 -2
@@ -111,8 +111,8 @@ validate_chandef_by_ht_vht_oper(struct i
 
 	switch (chan_width) {
 	case NL80211_CHAN_WIDTH_320:
-		WARN_ON(1);
-		break;
+		chandef->chan = NULL;
+		return;
 	case NL80211_CHAN_WIDTH_160:
 		vht_oper.chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
 		vht_oper.center_freq_seg1_idx = vht_oper.center_freq_seg0_idx;
author maintainer
#338 lore raw

PATCH 335/428 wifi: mac80211: release mesh path quota on failed additions

In reply to Greg Kroah-Hartman

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

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

From: Zhao Li <[email protected]>

commit 7325a44474f20c1393a1c114c6811abce6ddfaf4 upstream.

Repeated failed or duplicate mesh path additions can exhaust MESH_MAX_MPATHS while the path table holds fewer paths, which stops new paths to reachable destinations from being created.

mesh_path_add() reserves a slot before allocating and inserting a path. If allocation fails, the function returns without releasing the slot. If the rhashtable insertion reports an error or an existing destination, the function discards its candidate but retains the reservation. No new path is installed in any of these cases.

Release the reservation whenever the candidate is not installed.

Fixes ae76eef027f7 ("mac80211: return new mpath from mesh_path_add()")
Fixes 60854fd94573 ("mac80211: mesh: convert path table to rhashtable")
Assisted-by LLM sparse kasan
---
 net/mac80211/mesh_pathtbl.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
net/mac80211/mesh_pathtbl.c +4 -1
@@ -694,8 +694,10 @@ struct mesh_path *mesh_path_add(struct i
 		return ERR_PTR(-ENOSPC);
 
 	new_mpath = mesh_path_new(sdata, dst, GFP_ATOMIC);
-	if (!new_mpath)
+	if (!new_mpath) {
+		atomic_dec(&sdata->u.mesh.mpaths);
 		return ERR_PTR(-ENOMEM);
+	}
 
 	tbl = &sdata->u.mesh.mesh_paths;
 	spin_lock_bh(&tbl->walk_lock);
@@ -708,6 +710,7 @@ struct mesh_path *mesh_path_add(struct i
 
 	if (mpath) {
 		kfree(new_mpath);
+		atomic_dec(&sdata->u.mesh.mpaths);
 
 		if (IS_ERR(mpath))
 			return mpath;
author maintainer
#339 lore raw

PATCH 336/428 wifi: mac80211: set info->band for 802.3 encap offload frames

In reply to Greg Kroah-Hartman

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

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

From: Felix Fietkau <[email protected]>

commit 4febc02cd02f021f2b6d0768332be643ef14a12d upstream.

Unlike the 802.11 TX paths, ieee80211_8023_xmit() does not set info->band from the channel context on non-MLD interfaces. It stays at 0, so ieee80211_get_tx_rates() uses the wrong band for these frames.

Set it the same way as ieee80211_build_hdr(): drop the frame if there is no channel context, and leave the band at 0 on MLD interfaces.

---
 net/mac80211/tx.c |   11 +++++++++++
 1 file changed, 11 insertions(+)
net/mac80211/tx.c +11 -0
@@ -4707,6 +4707,7 @@ static void ieee80211_8023_xmit(struct i
 {
 	struct ieee80211_tx_info *info;
 	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_chanctx_conf *chanctx_conf;
 	struct tid_ampdu_tx *tid_tx = NULL;
 	struct sk_buff *seg, *next;
 	unsigned int skbs = 0, len = 0;
@@ -4756,6 +4757,16 @@ static void ieee80211_8023_xmit(struct i
 		sdata = container_of(sdata->bss,
 				     struct ieee80211_sub_if_data, u.ap);
 
+	/* MLD transmissions must not rely on the band */
+	if (!ieee80211_vif_is_mld(&sdata->vif)) {
+		chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
+		if (unlikely(!chanctx_conf)) {
+			kfree_skb_list(skb);
+			return;
+		}
+		info->band = chanctx_conf->def.chan->band;
+	}
+
 	info->flags |= IEEE80211_TX_CTL_HW_80211_ENCAP;
 	info->control.vif = &sdata->vif;
 
author maintainer
#340 lore raw

PATCH 337/428 wifi: mac80211: fix mesh fast xmit path deletion UAF

In reply to Greg Kroah-Hartman

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

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

From: Zihan Xi <[email protected]>

commit ee3aadc43ea69d98f666431e787137ac0badbb85 upstream.

mesh_fast_tx_cache() stores raw mesh_path pointers. Path deletion flushes the cache and then frees the path with kfree_rcu(), but a lookup that already holds the path can insert a new cache entry after the flush. The cache then points at freed memory.

Set MESH_PATH_DELETED before flushing, and skip inserting a cache entry if the path or MPP path is already deleted. Check this under the cache walk lock so it is ordered with the flush. Use WRITE_ONCE() for the deletion flag updates because the cache check reads flags without taking state_lock.

---
 net/mac80211/mesh_pathtbl.c |   12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)
net/mac80211/mesh_pathtbl.c +11 -1
@@ -577,6 +577,12 @@ void mesh_fast_tx_cache(struct ieee80211
 		goto unlock_sta;
 
 	spin_lock(&cache->walk_lock);
+	if ((READ_ONCE(mpath->flags) & MESH_PATH_DELETED) ||
+	    (mppath && (READ_ONCE(mppath->flags) & MESH_PATH_DELETED))) {
+		kfree(entry);
+		goto unlock_cache;
+	}
+
 	prev = rhashtable_lookup_get_insert_fast(&cache->rht,
 						 &entry->rhash,
 						 fast_tx_rht_params);
@@ -808,7 +814,8 @@ static void mesh_path_free_rcu(struct me
 	struct ieee80211_sub_if_data *sdata = mpath->sdata;
 
 	spin_lock_bh(&mpath->state_lock);
-	mpath->flags |= MESH_PATH_RESOLVING | MESH_PATH_DELETED;
+	WRITE_ONCE(mpath->flags,
+		   mpath->flags | MESH_PATH_RESOLVING | MESH_PATH_DELETED);
 	mesh_gate_del(tbl, mpath);
 	spin_unlock_bh(&mpath->state_lock);
 	timer_shutdown_sync(&mpath->timer);
@@ -822,6 +829,9 @@ static void __mesh_path_del(struct mesh_
 {
 	hlist_del_rcu(&mpath->walk_list);
 	rhashtable_remove_fast(&tbl->rhead, &mpath->rhash, mesh_rht_params);
+	spin_lock_bh(&mpath->state_lock);
+	WRITE_ONCE(mpath->flags, mpath->flags | MESH_PATH_DELETED);
+	spin_unlock_bh(&mpath->state_lock);
 	if (tbl == &mpath->sdata->u.mesh.mpp_paths)
 		mesh_fast_tx_flush_addr(mpath->sdata, mpath->dst);
 	else
author maintainer
#341 lore raw

PATCH 338/428 wifi: mac80211: handle empty FILS association request payload

In reply to Greg Kroah-Hartman

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

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

From: Yuqi Xu <[email protected]>

commit 5bfd4b0b40b79781d900cb2f7da0685070f53c67 upstream.

fils_encrypt_assoc_req() accepts a (Re)Association Request frame whose last element is the FILS Session element. crypt_len is then zero and aes_siv_encrypt() is called with an empty plaintext.

kmemdup() returns ZERO_SIZE_PTR for a zero-length allocation, so the unconditional sg_init_one() puts an invalid page into the scatterlist. With CONFIG_DEBUG_SG this triggers a kernel BUG in sg_set_buf(); without the debug check the bogus page is handed to the crypto backend.

AES-SIV is defined for empty plaintext and the S2V result is then the complete output, so handle this case before the CTR step, which has nothing to encrypt.

---
 net/mac80211/fils_aead.c |    8 ++++++++
 1 file changed, 8 insertions(+)
net/mac80211/fils_aead.c +8 -0
@@ -90,6 +90,14 @@ static int aes_siv_encrypt(const u8 *key
 	if (res)
 		return res;
 
+	/* With an empty plaintext there is no ciphertext to produce and
+	 * the S2V result is the complete output.
+	 */
+	if (!plain_len) {
+		memcpy(out, v, AES_BLOCK_SIZE);
+		return 0;
+	}
+
 	/* Use a temporary buffer of the plaintext to handle need for
 	 * overwriting this during AES-CTR.
 	 */
author maintainer
#342 lore raw

PATCH 339/428 USB: serial: cp210x: add Corsair AX1500i Power Supply

In reply to Greg Kroah-Hartman

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

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

From: Jérémy Carrat <[email protected]>

commit d84e27d87738634b92001500435ae4dadba10a13 upstream.

The Corsair AX1500i power supply exposes its Corsair Link monitoring interface on a mini-USB port through an on-board CP2103, using a Corsair-specific product ID. Without the ID in the table no driver binds and no tty is created.

Tested by forcing the driver association with

echo 1b1c 1c02 > /sys/bus/usb-serial/drivers/cp210x/new_id

and then setting the serial port to 115200 8N1. Adding the ID is sufficient to talk to the device: the tty reaches a USB-to-SMBus bridge sitting behind the UART, which identifies itself as "USB to SMB Bridge (Firmware by Ross Fosler)" version 0.9 and relays PMBus reads to the power supply controller. Input voltage and current, input and output power, internal temperature and fan speed were all read back on an AX1500i.

  [    7.806181] usb 3-14: New USB device found, idVendor=1b1c, idProduct=1c02, bcdDevice= 1.00
  [  698.526754] usbcore: registered new interface driver cp210x
  [  698.526767] usbserial: USB Serial support registered for cp210x
  [  698.527863] cp210x 3-14:1.0: cp210x converter detected
  [  698.529263] usb 3-14: cp210x converter now attached to ttyUSB0

The related AX1600i (1b1c:1c11, iProduct "USB API") is expected to be the same kind of device but has not been tested here, so it is not added.

---
 drivers/usb/serial/cp210x.c |    1 +
 1 file changed, 1 insertion(+)
drivers/usb/serial/cp210x.c +1 -0
@@ -223,6 +223,7 @@ static const struct usb_device_id id_tab
 	{ USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */
 	{ USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
 	{ USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
+	{ USB_DEVICE(0x1B1C, 0x1C02) }, /* Corsair AX1500i Power Supply */
 	{ USB_DEVICE(0x1B93, 0x1013) }, /* Phoenix Contact UPS Device */
 	{ USB_DEVICE(0x1BA4, 0x0002) },	/* Silicon Labs 358x factory default */
 	{ USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */
author maintainer
#343 lore raw

PATCH 340/428 USB: serial: quatech2: fix baud rate overflow

In reply to Greg Kroah-Hartman

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

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

From: Johan Hovold <[email protected]>

commit ec06546b43b612cefc851558981b4955c0ef9e46 upstream.

The requested baud rate is incorrectly truncated to 16 bits so that line speeds above 65535 bps cannot be set.

Use 32 bits for the rate while rejecting rates outside of [50,921600] to avoid having the 16-bit divisor overflow.

Fixes f7a33e608d9a ("USB: serial: add quatech2 usb to serial driver")
Cc Bill Pemberton <[email protected]>
---
 drivers/usb/serial/quatech2.c |   19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)
drivers/usb/serial/quatech2.c +16 -3
@@ -52,6 +52,7 @@
 #define QT2_XMIT_FLUSH      0x05  /* no following info */
 #define QT2_CONTROL_ESCAPE  0xff  /* pass through previous 2 control bytes */
 
+#define  MIN_BAUD_RATE              50
 #define  MAX_BAUD_RATE              921600
 #define  DEFAULT_BAUD_RATE          9600
 
@@ -156,7 +157,7 @@ static inline int calc_baud_divisor(int
 
 static inline int qt2_set_port_config(struct usb_device *dev,
 				      unsigned char port_number,
-				      u16 baudrate, u16 lcr)
+				      speed_t baudrate, u16 lcr)
 {
 	int divisor = calc_baud_divisor(baudrate);
 	u16 index = ((u16) (lcr << 8) | (u16) (port_number));
@@ -257,9 +258,9 @@ static void qt2_set_termios(struct tty_s
 	struct usb_device *dev = port->serial->dev;
 	struct qt2_port_private *port_priv;
 	struct ktermios *termios = &tty->termios;
-	u16 baud;
 	unsigned int cflag = termios->c_cflag;
 	u16 new_lcr = 0;
+	speed_t baud;
 	int status;
 
 	port_priv = usb_get_serial_port_data(port);
@@ -277,6 +278,18 @@ static void qt2_set_termios(struct tty_s
 	if (!baud)
 		baud = 9600;
 
+	if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) {
+		if (old_termios)
+			baud = tty_termios_baud_rate(old_termios);
+		else
+			baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE);
+
+		tty_encode_baud_rate(tty, baud, baud);
+
+		if (!baud)
+			baud = 9600;
+	}
+
 	status = qt2_set_port_config(dev, port_priv->device_port, baud,
 				     new_lcr);
 	if (status < 0)
@@ -373,7 +386,7 @@ static int qt2_open(struct tty_struct *t
 	port_priv->device_port = (u8) device_port;
 
 	if (tty)
-		qt2_set_termios(tty, port, &tty->termios);
+		qt2_set_termios(tty, port, NULL);
 
 	return 0;
 
author maintainer
#344 lore raw

PATCH 341/428 USB: serial: ssu100: fix baud rate overflow

In reply to Greg Kroah-Hartman

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

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

From: Johan Hovold <[email protected]>

commit afc235a217360c6859068d2e37a4df305c4d1be2 upstream.

The requested baud rate is incorrectly truncated to 16 bits so that line speeds above 65535 bps cannot be set.

Use 32 bits for the rate and remainder while rejecting rates outside of [50,460800] to avoid having the divisor or remainder overflow.

This issue was flagged by an LLM.

Fixes 52af95459939 ("USB: add USB serial ssu100 driver")
Cc [email protected] # 2.6.36
Cc Bill Pemberton <[email protected]>
Assisted-by LLM
---
 drivers/usb/serial/ssu100.c |   18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)
drivers/usb/serial/ssu100.c +16 -2
@@ -32,6 +32,7 @@
 
 #define  SERIAL_EVEN_PARITY         (UART_LCR_PARITY | UART_LCR_EPAR)
 
+#define  MIN_BAUD_RATE              50
 #define  MAX_BAUD_RATE              460800
 
 #define ATC_DISABLED                0x00
@@ -217,9 +218,10 @@ static void ssu100_set_termios(struct tt
 {
 	struct usb_device *dev = port->serial->dev;
 	struct ktermios *termios = &tty->termios;
-	u16 baud, divisor, remainder;
+	speed_t baud, remainder;
 	unsigned int cflag = termios->c_cflag;
 	u16 urb_value = 0; /* will hold the new flags */
+	u16 divisor;
 	int result;
 
 	if (cflag & PARENB) {
@@ -235,6 +237,18 @@ static void ssu100_set_termios(struct tt
 	if (!baud)
 		baud = 9600;
 
+	if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) {
+		if (old_termios)
+			baud = tty_termios_baud_rate(old_termios);
+		else
+			baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE);
+
+		tty_encode_baud_rate(tty, baud, baud);
+
+		if (!baud)
+			baud = 9600;
+	}
+
 	dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud);
 
 
@@ -310,7 +324,7 @@ static int ssu100_open(struct tty_struct
 		dev_dbg(&port->dev, "%s - set uart failed\n", __func__);
 
 	if (tty)
-		ssu100_set_termios(tty, port, &tty->termios);
+		ssu100_set_termios(tty, port, NULL);
 
 	return usb_serial_generic_open(tty, port);
 }
author maintainer
#345 lore raw

PATCH 342/428 USB: serial: fix dynamic id driver deregistration race

In reply to Greg Kroah-Hartman

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

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

From: Johan Hovold <[email protected]>

commit 63594e8ffe097bc0b7639936f33799a7653dded2 upstream.

Only free the dynamic ids after having deregistered the driver and removed the "new_id" attribute to avoid leaking any id added by a racing write to the attribute.

Fixes 93bacefc4cc0 ("USB serial: add dynamic id support to usb-serial core")
Cc [email protected] # 2.6.21
---
 drivers/usb/serial/bus.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/usb/serial/bus.c +1 -1
@@ -165,7 +165,7 @@ int usb_serial_bus_register(struct usb_s
 
 void usb_serial_bus_deregister(struct usb_serial_driver *driver)
 {
-	free_dynids(driver);
 	driver_unregister(&driver->driver);
+	free_dynids(driver);
 }
 
author maintainer
#346 lore raw

PATCH 343/428 USB: serial: fix driver deregistration order

In reply to Greg Kroah-Hartman

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

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

From: Johan Hovold <[email protected]>

commit 646e5b28b4fdbc1b828a2398d6ced2c1dd308a46 upstream.

USB serial driver modules register one driver for the USB bus and one or more drivers for the ports on the USB serial bus.

When unloading a driver module, the USB driver must be deregistered before the USB serial bus drivers so that I/O is stopped before unbinding the ports to avoid use-after-free in completion handlers accessing port data.

Note that the "new_id" attributes must first be removed to prevent new ids from being added and triggering a probe of the USB driver after it has been deregistered.

Fixes 765e0ba62613 ("usb-serial: new API for driver registration")
Cc Alan Stern <[email protected]>
---
 drivers/usb/serial/bus.c        |    4 ++++
 drivers/usb/serial/usb-serial.c |   21 ++++++++++++++++++---
 include/linux/usb/serial.h      |    1 +
 3 files changed, 23 insertions(+), 3 deletions(-)
drivers/usb/serial/bus.c +4 -0
@@ -169,3 +169,7 @@ void usb_serial_bus_deregister(struct us
 	free_dynids(driver);
 }
 
+void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver)
+{
+	driver_remove_file(&driver->driver, &driver_attr_new_id);
+}
drivers/usb/serial/usb-serial.c +18 -3
@@ -1470,7 +1470,7 @@ int __usb_serial_register_drivers(struct
 {
 	int rc;
 	struct usb_driver *udriver;
-	struct usb_serial_driver * const *sd;
+	struct usb_serial_driver * const *sd, * const *s;
 
 	/*
 	 * udriver must be registered before any of the serial drivers,
@@ -1523,9 +1523,11 @@ int __usb_serial_register_drivers(struct
 	return 0;
 
 err_deregister_drivers:
+	for (s = serial_drivers; s < sd; ++s)
+		usb_serial_bus_remove_new_id(*s);
+	usb_deregister(udriver);
 	while (sd-- > serial_drivers)
 		usb_serial_deregister(*sd);
-	usb_deregister(udriver);
 err_free_driver:
 	kfree(udriver);
 	return rc;
@@ -1543,10 +1545,23 @@ EXPORT_SYMBOL_GPL(__usb_serial_register_
 void usb_serial_deregister_drivers(struct usb_serial_driver *const serial_drivers[])
 {
 	struct usb_driver *udriver = (*serial_drivers)->usb_driver;
+	struct usb_serial_driver * const *sd;
+
+	/*
+	 * udriver must be deregistered before the serial drivers so that
+	 * I/O is stopped before unbinding the ports.
+	 *
+	 * Remove the new_id attributes to prevent ids from being added and
+	 * triggering a probe of udriver after it has been deregistered.
+	 */
+	for (sd = serial_drivers; *sd; ++sd)
+		usb_serial_bus_remove_new_id(*sd);
+
+	usb_deregister(udriver);
 
 	for (; *serial_drivers; ++serial_drivers)
 		usb_serial_deregister(*serial_drivers);
-	usb_deregister(udriver);
+
 	kfree(udriver);
 }
 EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers);
include/linux/usb/serial.h +1 -0
@@ -381,6 +381,7 @@ void usb_serial_handle_dcd_change(struct
 
 int usb_serial_bus_register(struct usb_serial_driver *device);
 void usb_serial_bus_deregister(struct usb_serial_driver *device);
+void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver);
 
 extern const struct bus_type usb_serial_bus_type;
 extern struct tty_driver *usb_serial_tty_driver;
author maintainer
#347 lore raw

PATCH 344/428 USB: serial: fix ioctl hangup race

In reply to Greg Kroah-Hartman

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

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

From: Johan Hovold <[email protected]>

commit ae0e61d95677efa778379853e495d748e6efbf95 upstream.

The tty ioctls can race with hangup and end up calling into a tty driver for a device that is already gone or powered down.

Serialise the callbacks using the tty port mutex and add the missing checks to make sure the port has not been hung up before accessing port driver data or hardware.

This specifically avoids dereferencing a NULL-pointer if a device is disconnected during lengthy break signalling.

Note that TIOCGICOUNT and TIOCMIWAIT do not access port driver data or hardware and are therefore not affected by the race.

---
 drivers/usb/serial/generic.c    |   10 +++-
 drivers/usb/serial/usb-serial.c |   94 +++++++++++++++++++++++++++++++---------
 drivers/usb/serial/xr_serial.c  |   10 +---
 3 files changed, 86 insertions(+), 28 deletions(-)
drivers/usb/serial/generic.c +9 -1
@@ -266,6 +266,7 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_cha
 void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout)
 {
 	struct usb_serial_port *port = tty->driver_data;
+	struct tty_port *tport = &port->port;
 	unsigned int bps;
 	unsigned long period;
 	unsigned long expire;
@@ -285,7 +286,14 @@ void usb_serial_generic_wait_until_sent(
 					__func__, jiffies_to_msecs(timeout),
 					jiffies_to_msecs(period));
 	expire = jiffies + timeout;
-	while (!port->serial->type->tx_empty(port)) {
+	for (;;) {
+		mutex_lock(&tport->mutex);
+		if (tty_io_error(tty) || port->serial->type->tx_empty(port)) {
+			mutex_unlock(&tport->mutex);
+			break;
+		}
+		mutex_unlock(&tport->mutex);
+
 		schedule_timeout_interruptible(period);
 		if (signal_pending(current))
 			break;
drivers/usb/serial/usb-serial.c +74 -20

178 lines not on this page · raw message

drivers/usb/serial/xr_serial.c +3 -7
@@ -850,12 +850,9 @@ static int xr_get_rs485_config(struct tt
 	struct usb_serial_port *port = tty->driver_data;
 	struct xr_data *data = usb_get_serial_port_data(port);
 
-	down_read(&tty->termios_rwsem);
-	if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) {
-		up_read(&tty->termios_rwsem);
+	/* core holds port mutex */
+	if (copy_to_user(argp, &data->rs485, sizeof(data->rs485)))
 		return -EFAULT;
-	}
-	up_read(&tty->termios_rwsem);
 
 	return 0;
 }
@@ -871,10 +868,9 @@ static int xr_set_rs485_config(struct tt
 		return -EFAULT;
 	xr_sanitize_serial_rs485(&rs485);
 
-	down_write(&tty->termios_rwsem);
+	/* core holds termios rwsem and port mutex */
 	data->rs485 = rs485;
 	xr_set_flow_mode(tty, port, NULL);
-	up_write(&tty->termios_rwsem);
 
 	if (copy_to_user(argp, &rs485, sizeof(rs485)))
 		return -EFAULT;
author maintainer
#348 lore raw

PATCH 345/428 USB: serial: option: add support for SIMCom SIM8260C

In reply to Greg Kroah-Hartman

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

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

From: Zhao Dongdong <[email protected]>

commit 35c950c9ec2c4c4d45dc9b63cfe8fca774ce33a0 upstream.

Add support for SIMCom SIM8260C (1e0e:902b).

T:  Bus=08 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=5000 MxCh= 0
D:  Ver= 3.20 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs=  1
P:  Vendor=1e0e ProdID=902b Rev= 5.04
S:  Manufacturer=SIMCOM
S:  Product=SDXLEMUR-LITE-MTP _SN:120696AB
S:  SerialNumber=0123456789ABCDEF
C:* #Ifs= 8 Cfg#= 1 Atr=a0 MxPwr=896mA
A:  FirstIf#= 5 IfCount= 3 Cls=01(audio) Sub=00 Prot=20
I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option
E:  Ad=01(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E:  Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option
E:  Ad=83(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E:  Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
E:  Ad=85(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
E:  Ad=84(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E:  Ad=03(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
E:  Ad=87(I) Atr=03(Int.) MxPS=  10 Ivl=32ms
E:  Ad=86(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E:  Ad=04(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=qmi_wwan_simcom
E:  Ad=88(I) Atr=03(Int.) MxPS=   8 Ivl=32ms
E:  Ad=8e(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E:  Ad=0f(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 5 Alt= 0 #EPs= 0 Cls=01(audio) Sub=01 Prot=20 Driver=snd-usb-audio
I:* If#= 6 Alt= 0 #EPs= 0 Cls=01(audio) Sub=02 Prot=20 Driver=snd-usb-audio
I:  If#= 6 Alt= 1 #EPs= 1 Cls=01(audio) Sub=02 Prot=20 Driver=snd-usb-audio
E:  Ad=05(O) Atr=0d(Isoc) MxPS=  34 Ivl=1ms
I:* If#= 7 Alt= 0 #EPs= 0 Cls=01(audio) Sub=02 Prot=20 Driver=(none)
I:  If#= 7 Alt= 1 #EPs= 1 Cls=01(audio) Sub=02 Prot=20 Driver=(none)
E:  Ad=89(I) Atr=0d(Isoc) MxPS=  34 Ivl=1ms
---
 drivers/usb/serial/option.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/usb/serial/option.c +2 -0
@@ -2168,6 +2168,8 @@ static const struct usb_device_id option
 	{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) },	/* Simcom SIM7500/SIM7600 MBIM mode */
 	{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9011, 0xff),	/* Simcom SIM7500/SIM7600 RNDIS mode */
 	  .driver_info = RSVD(7) },
+	{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x902b, 0xff),
+	  .driver_info = RSVD(4) },
 	{ USB_DEVICE(0x1e0e, 0x9071),				/* Simcom SIM8230 RMNET mode */
 	  .driver_info = RSVD(3) | RSVD(4) },
 	{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9078, 0xff),	/* Simcom SIM8230 ECM mode */
author maintainer
#349 lore raw

PATCH 346/428 USB: serial: option: add Quectel EG060W

In reply to Greg Kroah-Hartman

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

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

From: Carlo Szelinsky <[email protected]>

commit cd243401994880b8b6810c53177bce56eebaece5 upstream.

Add Quectel EG060W with product ID 0x6004.
The three vendor-specific interfaces are DIAG, NMEA and AT; the CDC-NCM data interfaces are handled by the cdc_ncm driver.

T:  Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#=  2 Spd=480  MxCh= 0
D:  Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=2c7c ProdID=6004 Rev= 3.18
S:  Manufacturer=Quectel
S:  Product=EG060W-EAAA
S:  SerialNumber=123456789ABCD
C:* #Ifs= 5 Cfg#= 1 Atr=e0 MxPwr=  2mA
A:  FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0d Prot=00
I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0d Prot=00 Driver=cdc_ncm
E:  Ad=82(I) Atr=03(Int.) MxPS=  16 Ivl=32ms
I:  If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm
I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm
E:  Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option
E:  Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option
E:  Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option
E:  Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
---
 drivers/usb/serial/option.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/usb/serial/option.c +2 -0
@@ -280,6 +280,7 @@ static void option_instat_callback(struc
 #define QUECTEL_PRODUCT_EC200U			0x0901
 #define QUECTEL_PRODUCT_EG912Y			0x6001
 #define QUECTEL_PRODUCT_EC200S_CN		0x6002
+#define QUECTEL_PRODUCT_EG060W			0x6004
 #define QUECTEL_PRODUCT_EC200A			0x6005
 #define QUECTEL_PRODUCT_EG916Q			0x6007
 #define QUECTEL_PRODUCT_EM061K_LWW		0x6008
@@ -1268,6 +1269,7 @@ static const struct usb_device_id option
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200A, 0xff, 0, 0) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200U, 0xff, 0, 0) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200S_CN, 0xff, 0, 0) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG060W, 0xff, 0, 0) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200T, 0xff, 0, 0) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG912Y, 0xff, 0, 0) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG916Q, 0xff, 0x00, 0x00) },
author maintainer
#350 lore raw

PATCH 347/428 USB: serial: option: add Compal EXM-G1x support

In reply to Greg Kroah-Hartman

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

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

From: Ian Lin <[email protected]>

commit 7f11480cda081de78a05d8c97073df66326b50b3 upstream.

The Compal EXM-G1x is a Qualcomm SDX12-based LTE modem. Add support for its vendor-specific serial interfaces with protocols 0x30, 0x40, and 0x60.

Relevant output from usb-devices:

T:  Bus=04 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  4 Spd=5000 MxCh= 0
D:  Ver= 3.20 Cls=ef(misc ) Sub=02 Prot=01 MxPS= 9 #Cfgs=  1
P:  Vendor=04b7 ProdID=8217 Rev=05.04
S:  Manufacturer=COMPAL
S:  Product=EXM-G1x
C:  #Ifs= 6 Cfg#= 1 Atr=a0 MxPwr=896mA
I:  If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option
I:  If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none)
I:  If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option
I:  If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option
I:  If#= 4 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
I:  If#= 8 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=qmi_wwan

Tested on a Compal EXM-G1x modem.

---
 drivers/usb/serial/option.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/usb/serial/option.c +3 -0
@@ -2433,6 +2433,9 @@ static const struct usb_device_id option
 	  .driver_info = RSVD(5) },
 	{ USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe167, 0xff),                     /* Foxconn T99W640 MBIM */
 	  .driver_info = RSVD(3) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x30) },	/* Compal EXM-G1x */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x40) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x60) },
 	{ USB_DEVICE(0x1508, 0x1001),						/* Fibocom NL668 (IOT version) */
 	  .driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
 	{ USB_DEVICE(0x1782, 0x4d10) },						/* Fibocom L610 (AT mode) */
author maintainer
#351 lore raw

PATCH 348/428 USB: serial: option: add Quectel RG660QB

In reply to Greg Kroah-Hartman

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

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

From: Sebastian Sjoholm <[email protected]>

commit 6177bcd94be83f80a032c2e69a06606d325335b9 upstream.

Add support for the Quectel RG660QB 5G module (USB ID 2c7c:013d). The interface layout is the same as the RG650V:

  If 0: DIAG   ff/ff/30, 2 endpoints
  If 1: NMEA   ff/00/00, 2 endpoints
  If 2: AT     ff/00/00, 3 endpoints
  If 3: Modem  ff/00/00, 3 endpoints
  If 4: QMI    ff/ff/ff, 3 endpoints

Match the serial interfaces on class/subclass/protocol so that the QMI interface is left to qmi_wwan.

  # lsusb -v -d 2c7c:013d

Bus 004 Device 002: ID 2c7c:013d Quectel Wireless Solutions Co., Ltd. RG660QB-EU Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 3.20
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 9
idVendor 0x2c7c Quectel Wireless Solutions Co., Ltd.
idProduct 0x013d
bcdDevice 6.06
iManufacturer 1 Quectel
iProduct 2 RG660QB-EU
iSerial 3 f50119d0
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0105
bNumInterfaces 5
bConfigurationValue 1
iConfiguration 4 DIAG_SER_RMNET
bmAttributes 0xa0
(Bus Powered)
Remote Wakeup
MaxPower 896mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 48
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 0
bNumEndpoints 3
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 6 CDEV Serial
** UNRECOGNIZED: 05 24 00 10 01
** UNRECOGNIZED: 05 24 01 00 00
** UNRECOGNIZED: 04 24 02 02
** UNRECOGNIZED: 05 24 06 00 00
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x84 EP 4 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x000a 1x 10 bytes
bInterval 9
bMaxBurst 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 EP 3 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 3
bAlternateSetting 0
bNumEndpoints 3
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 6 CDEV Serial
** UNRECOGNIZED: 05 24 00 10 01
** UNRECOGNIZED: 05 24 01 00 00
** UNRECOGNIZED: 04 24 02 02
** UNRECOGNIZED: 05 24 06 00 00
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x86 EP 6 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x000a 1x 10 bytes
bInterval 9
bMaxBurst 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x85 EP 5 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x04 EP 4 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 4
bAlternateSetting 0
bNumEndpoints 3
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 255 Vendor Specific Protocol
iInterface 7 RmNet
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x88 EP 8 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0008 1x 8 bytes
bInterval 9
bMaxBurst 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x87 EP 7 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 6
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x05 EP 5 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 2
Binary Object Store Descriptor:
bLength 5
bDescriptorType 15
wTotalLength 0x0023
bNumDeviceCaps 2
SuperSpeed USB Device Capability:
bLength 10
bDescriptorType 16
bDevCapabilityType 3
bmAttributes 0x00
wSpeedsSupported 0x000f
Device can operate at Low Speed (1Mbps)
Device can operate at Full Speed (12Mbps)
Device can operate at High Speed (480Mbps)
Device can operate at SuperSpeed (5Gbps)
bFunctionalitySupport 1
Lowest fully-functional device speed is Full Speed (12Mbps)
bU1DevExitLat 0 micro seconds
bU2DevExitLat 0 micro seconds
SuperSpeedPlus USB Device Capability:
bLength 20
bDescriptorType 16
bDevCapabilityType 10
bmAttributes 0x00000001
Sublink Speed Attribute count 1
Sublink Speed ID count 0
wFunctionalitySupport 0x1100
bmSublinkSpeedAttr[0] 0x000a4030
Speed Attribute ID: 0 10Gb/s Symmetric RX SuperSpeedPlus
bmSublinkSpeedAttr[1] 0x000a40b0
Speed Attribute ID: 0 10Gb/s Symmetric TX SuperSpeedPlus
Device Status: 0x0000
(Bus Powered)

Tested with an early sample of the module on a Quectel 5G EVB connected over USB 3 to a Raspberry Pi 5.

---
 drivers/usb/serial/option.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/usb/serial/option.c +3 -0
@@ -260,6 +260,7 @@ static void option_instat_callback(struc
 #define QUECTEL_PRODUCT_EM060K_12a		0x012a
 #define QUECTEL_PRODUCT_EM060K_12b		0x012b
 #define QUECTEL_PRODUCT_EM060K_12c		0x012c
+#define QUECTEL_PRODUCT_RG660QB			0x013d
 #define QUECTEL_PRODUCT_EG91			0x0191
 #define QUECTEL_PRODUCT_EG95			0x0195
 #define QUECTEL_PRODUCT_BG96			0x0296
@@ -1276,6 +1277,8 @@ static const struct usb_device_id option
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500K, 0xff, 0x00, 0x00) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0xff, 0x30) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0, 0) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0xff, 0x30) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0, 0) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x30) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0, 0) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x40) },
author maintainer
#352 lore raw

PATCH 349/428 USB: serial: option: add Compal EXC-T1 support

In reply to Greg Kroah-Hartman

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

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

From: Ian Lin <[email protected]>

commit b51faf3f2e307f114854bbe6d495f03cd5370686 upstream.

The Compal EXC-T1 is a Qualcomm MDM9207-based LTE modem which reports "EXC-x1" as its USB product string. It exposes diagnostic, modem, AT, and NMEA ports as vendor-specific interfaces with subclass 0x10 and protocols 0x01 through 0x04.

Add exact interface matches for these four ports, leaving the ff/ff/ff interface unbound.

Relevant output from usb-devices:

T:  Bus=03 Lev=02 Prnt=03 Port=03 Cnt=01 Dev#=  6 Spd=480  MxCh= 0
D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
P:  Vendor=04b7 ProdID=4d22 Rev=03.18
S:  Manufacturer=CEI
S:  Product=EXC-x1
C:  #Ifs= 6 Cfg#= 1 Atr=a0 MxPwr=500mA
I:  If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=10 Prot=01 Driver=option
I:  If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=10 Prot=02 Driver=option
I:  If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=10 Prot=03 Driver=option
I:  If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=10 Prot=04 Driver=option
I:  If#= 4 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
I:  If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=10 Prot=05 Driver=qmi_wwan

Tested on a Compal EXC-T1 modem using an Ubuntu kernel build. The four serial interfaces bound to option and exposed ttyUSB3 through ttyUSB6.

---
 drivers/usb/serial/option.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/usb/serial/option.c +4 -0
@@ -2436,6 +2436,10 @@ static const struct usb_device_id option
 	  .driver_info = RSVD(5) },
 	{ USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe167, 0xff),                     /* Foxconn T99W640 MBIM */
 	  .driver_info = RSVD(3) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x01) },	/* Compal EXC-T1 */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x02) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x03) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x04) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x30) },	/* Compal EXM-G1x */
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x40) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x60) },
author maintainer
#353 lore raw

PATCH 350/428 thunderbolt: Fix KASAN reported use-after-free when request is canceled

In reply to Greg Kroah-Hartman

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

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

From: Mika Westerberg <[email protected]>

commit a02188ddc24b7872f0c5e0c5873f827318717803 upstream.

Alan reported that when doing stress testing sometimes KASAN notices use-after-free during control channel operation (stripped down keeping the relevant parts):

 BUG: KASAN: slab-use-after-free in tb_cfg_request_sync+0x240/0x250 [thunderbolt]
 Read of size 24 at addr ffff88811067f290 by task kworker/u40:2/1760
  <TASK>
  tb_cfg_request_sync+0x240/0x250 [thunderbolt]
  tb_cfg_read_raw+0x367/0x510 [thunderbolt]
  tb_cfg_read+0xec/0x240 [thunderbolt]
  tb_port_get_link_generation+0x258/0x420 [thunderbolt]
  tb_usb3_consumed_bandwidth+0x1c1/0x2c0 [thunderbolt]
  tb_tunnel_consumed_bandwidth+0xfd/0x910 [thunderbolt]
  tb_available_bandwidth+0x5f2/0xeb0 [thunderbolt]
  tb_recalc_estimated_bandwidth+0x2a0/0x1bc0 [thunderbolt]
  tb_handle_dp_bandwidth_request+0x1897/0x5e20 [thunderbolt]
  process_one_work+0x675/0x1230
  worker_thread+0x5e6/0xf70
  kthread+0x365/0x470
  ret_from_fork+0x54d/0x710
  ret_from_fork_asm+0x1a/0x30
  </TASK>

 Allocated by task 1760:
  __kmalloc_cache_noprof+0x1ee/0x550
  tb_cfg_read_raw+0x1d3/0x510 [thunderbolt]
  tb_cfg_read+0xec/0x240 [thunderbolt]
  tb_port_get_link_generation+0x258/0x420 [thunderbolt]
  tb_usb3_consumed_bandwidth+0x1c1/0x2c0 [thunderbolt]
  tb_tunnel_consumed_bandwidth+0xfd/0x910 [thunderbolt]
  tb_available_bandwidth+0x5f2/0xeb0 [thunderbolt]
  tb_recalc_estimated_bandwidth+0x2a0/0x1bc0 [thunderbolt]
  tb_handle_dp_bandwidth_request+0x1897/0x5e20 [thunderbolt]
  process_one_work+0x675/0x1230
  worker_thread+0x5e6/0xf70
  kthread+0x365/0x470
  ret_from_fork+0x54d/0x710
  ret_from_fork_asm+0x1a/0x30

 Freed by task 926:
  kfree+0x18f/0x4a0
  tb_cfg_request_put+0xb7/0xe0 [thunderbolt]
  tb_cfg_request_work+0x82/0x120 [thunderbolt]
  process_one_work+0x675/0x1230
  worker_thread+0x5e6/0xf70
  kthread+0x365/0x470
  ret_from_fork+0x54d/0x710
  ret_from_fork_asm+0x1a/0x30

 Second to last potentially related work creation:
  __queue_work+0x575/0xd00
  queue_work_on+0x77/0x80
  tb_cfg_request_cancel+0xc7/0x260 [thunderbolt]
  tb_cfg_request_sync+0x1f6/0x250 [thunderbolt]
  tb_cfg_read_raw+0x367/0x510 [thunderbolt]
  tb_cfg_read+0xec/0x240 [thunderbolt]
  tb_port_get_link_generation+0x258/0x420 [thunderbolt]
  tb_usb3_consumed_bandwidth+0x1c1/0x2c0 [thunderbolt]
  tb_tunnel_consumed_bandwidth+0xfd/0x910 [thunderbolt]
  tb_available_bandwidth+0x5f2/0xeb0 [thunderbolt]
  tb_recalc_estimated_bandwidth+0x2a0/0x1bc0 [thunderbolt]
  tb_handle_dp_bandwidth_request+0x1897/0x5e20 [thunderbolt]
  process_one_work+0x675/0x1230
  worker_thread+0x5e6/0xf70
  kthread+0x365/0x470
  ret_from_fork+0x54d/0x710
  ret_from_fork_asm+0x1a/0x30

The last stack trace is helpful because it shows that we are cancelling a request and looking at tb_cfg_request_cancel() what might happen is that tb_cfg_request_work() completes right before tb_cfg_request_cancel() starts and because of this it will call schedule_work() queueing the same work to run again. However, it is already removed from the request_queue and reference count is dropped so when tb_cfg_request_work() triggers again it will access memory that is already released.

Fix this so that we first make sure a cancelled request is not handed away from tb_cfg_request_find() or scheduled to run. Then instead of relying on the worker to clean up the request we will do it in tb_cfg_request_cancel() after the work is canceled from running. Make tb_cfg_request_dequeue() release the request only if it was actually removed from the queue.

Fixes d7f781bfdbf4 ("thunderbolt: Rework control channel to be more reliable")
---
 drivers/thunderbolt/ctl.c |   63 ++++++++++++++++++++++------------------------
 1 file changed, 31 insertions(+), 32 deletions(-)
drivers/thunderbolt/ctl.c +31 -32
@@ -73,7 +73,6 @@ struct tb_ctl {
 #define tb_ctl_dbg_once(ctl, format, arg...) \
 	dev_dbg_once((ctl)->nhi->dev, format, ## arg)
 
-static DECLARE_WAIT_QUEUE_HEAD(tb_cfg_request_cancel_queue);
 /* Serializes access to request kref_get/put */
 static DEFINE_MUTEX(tb_cfg_request_lock);
 
@@ -133,41 +132,42 @@ void tb_cfg_request_put(struct tb_cfg_re
 static int tb_cfg_request_enqueue(struct tb_ctl *ctl,
 				  struct tb_cfg_request *req)
 {
+	tb_cfg_request_get(req);
+
 	WARN_ON(test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags));
 	WARN_ON(req->ctl);
 
-	mutex_lock(&ctl->request_queue_lock);
+	guard(mutex)(&ctl->request_queue_lock);
 	if (!ctl->running) {
-		mutex_unlock(&ctl->request_queue_lock);
+		tb_cfg_request_put(req);
 		return -ENOTCONN;
 	}
 	req->ctl = ctl;
 	list_add_tail(&req->list, &ctl->request_queue);
 	set_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
-	mutex_unlock(&ctl->request_queue_lock);
 	return 0;
 }
 
-static void tb_cfg_request_dequeue(struct tb_cfg_request *req)
+static bool tb_cfg_request_is_active(struct tb_cfg_request *req)
 {
-	struct tb_ctl *ctl = req->ctl;
-
-	mutex_lock(&ctl->request_queue_lock);
-	if (!test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags)) {
-		mutex_unlock(&ctl->request_queue_lock);
-		return;
-	}
+	return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
+}
 
-	list_del(&req->list);
-	clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
-	if (test_bit(TB_CFG_REQUEST_CANCELED, &req->flags))
-		wake_up(&tb_cfg_request_cancel_queue);
-	mutex_unlock(&ctl->request_queue_lock);
+static bool tb_cfg_request_is_canceled(struct tb_cfg_request *req)
+{
+	return test_bit(TB_CFG_REQUEST_CANCELED, &req->flags);
 }
 
-static bool tb_cfg_request_is_active(struct tb_cfg_request *req)
+static void tb_cfg_request_dequeue(struct tb_cfg_request *req)
 {
-	return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
+	struct tb_ctl *ctl = req->ctl;
+
+	guard(mutex)(&ctl->request_queue_lock);
+	if (tb_cfg_request_is_active(req)) {
+		list_del(&req->list);
+		clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
+		tb_cfg_request_put(req);
+	}
 }
 
 static struct tb_cfg_request *
@@ -178,7 +178,7 @@ tb_cfg_request_find(struct tb_ctl *ctl,
 	mutex_lock(&pkg->ctl->request_queue_lock);
 	list_for_each_entry(iter, &pkg->ctl->request_queue, list) {
 		tb_cfg_request_get(iter);
-		if (iter->match(iter, pkg)) {
+		if (!tb_cfg_request_is_canceled(iter) && iter->match(iter, pkg)) {
 			req = iter;
 			break;
 		}
@@ -512,8 +512,11 @@ static void tb_ctl_rx_callback(struct tb
 	trace_tb_rx(pkg->ctl->index, frame->eof, pkg->buffer, frame->size, !req);
 
 	if (req) {
-		if (req->copy(req, pkg))
-			schedule_work(&req->work);
+		scoped_guard(mutex, &pkg->ctl->request_queue_lock) {
+			if (!tb_cfg_request_is_canceled(req) &&
+			    req->copy(req, pkg))
+				schedule_work(&req->work);
+		}
 		tb_cfg_request_put(req);
 	}
 
@@ -525,11 +528,10 @@ static void tb_cfg_request_work(struct w
 {
 	struct tb_cfg_request *req = container_of(work, typeof(*req), work);
 
-	if (!test_bit(TB_CFG_REQUEST_CANCELED, &req->flags))
+	if (!tb_cfg_request_is_canceled(req))
 		req->callback(req->callback_data);
 
 	tb_cfg_request_dequeue(req);
-	tb_cfg_request_put(req);
 }
 
 /**
@@ -555,10 +557,9 @@ int tb_cfg_request(struct tb_ctl *ctl, s
 	INIT_WORK(&req->work, tb_cfg_request_work);
 	INIT_LIST_HEAD(&req->list);
 
-	tb_cfg_request_get(req);
 	ret = tb_cfg_request_enqueue(ctl, req);
 	if (ret)
-		goto err_put;
+		return ret;
 
 	ret = tb_ctl_tx(ctl, req->request, req->request_size,
 			req->request_type);
@@ -572,9 +573,6 @@ int tb_cfg_request(struct tb_ctl *ctl, s
 
 err_dequeue:
 	tb_cfg_request_dequeue(req);
-err_put:
-	tb_cfg_request_put(req);
-
 	return ret;
 }
 
@@ -588,9 +586,10 @@ err_put:
  */
 void tb_cfg_request_cancel(struct tb_cfg_request *req, int err)
 {
-	set_bit(TB_CFG_REQUEST_CANCELED, &req->flags);
-	schedule_work(&req->work);
-	wait_event(tb_cfg_request_cancel_queue, !tb_cfg_request_is_active(req));
+	scoped_guard(mutex, &req->ctl->request_queue_lock)
+		set_bit(TB_CFG_REQUEST_CANCELED, &req->flags);
+	cancel_work_sync(&req->work);
+	tb_cfg_request_dequeue(req);
 	req->result.err = err;
 }
 
author maintainer
#354 lore raw

PATCH 351/428 thunderbolt: Hold a router reference for each allocated HopID

In reply to Greg Kroah-Hartman

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

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

From: Sven Peter <[email protected]>

commit 032c59c7681c661b63123231824170c025afb7e7 upstream.

tb_stop() drops the reference to all DP tunnels but does not deactivate them, thus nothing cancels a dprx_work still in flight (which holds its own tunnel reference) and the tunnel can outlive tb_switch_remove(). The HopID releases in tb_path_free() then operate on freed IDAs and trigger warnings like

ida_free called for id=8 which is not allocated.

This can be triggered by unbinding the driver while a DP tunnel is still waiting for the DPRX capabilities read to finish. On the Apple NHI unplugging the cable runs into just that reliably because the read can never finish right now and because the unplug powers down the entire USB4 complex and removes the NHI device.

Take a router reference whenever an input or output HopID is allocated and drop it again after the HopID is released. This keeps the ports and their HopID IDAs alive for as long as they are used.

The KUnit tests allocate routers without ever registering their devices so initialize the embedded struct device there as well and drop its initial reference when the test is finished.

Fixes d6d458d42e1e ("thunderbolt: Handle DisplayPort tunnel activation asynchronously")
---
 drivers/thunderbolt/switch.c |    9 ++++++++-
 drivers/thunderbolt/test.c   |   21 +++++++++++++++++++++
 2 files changed, 29 insertions(+), 1 deletion(-)
drivers/thunderbolt/switch.c +8 -1
@@ -765,6 +765,7 @@ static int tb_port_alloc_hopid(struct tb
 {
 	int port_max_hopid;
 	struct ida *ida;
+	int ret;
 
 	if (in) {
 		port_max_hopid = port->config.max_in_hop_id;
@@ -784,7 +785,11 @@ static int tb_port_alloc_hopid(struct tb
 	if (max_hopid < 0 || max_hopid > port_max_hopid)
 		max_hopid = port_max_hopid;
 
-	return ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL);
+	ret = ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL);
+	if (ret >= 0)
+		tb_switch_get(port->sw);
+
+	return ret;
 }
 
 /**
@@ -823,6 +828,7 @@ int tb_port_alloc_out_hopid(struct tb_po
 void tb_port_release_in_hopid(struct tb_port *port, int hopid)
 {
 	ida_free(&port->in_hopids, hopid);
+	tb_switch_put(port->sw);
 }
 
 /**
@@ -833,6 +839,7 @@ void tb_port_release_in_hopid(struct tb_
 void tb_port_release_out_hopid(struct tb_port *port, int hopid)
 {
 	ida_free(&port->out_hopids, hopid);
+	tb_switch_put(port->sw);
 }
 
 static inline bool tb_switch_is_reachable(const struct tb_switch *parent,
drivers/thunderbolt/test.c +21 -0
@@ -33,6 +33,18 @@ static void kunit_ida_init(struct kunit
 	kunit_alloc_resource(test, __ida_init, __ida_destroy, GFP_KERNEL, ida);
 }
 
+static void tb_test_switch_release(struct device *dev)
+{
+	/* The memory is owned by KUnit, nothing to do here */
+}
+
+static void tb_test_switch_put(void *data)
+{
+	struct tb_switch *sw = data;
+
+	put_device(&sw->dev);
+}
+
 static struct tb_switch *alloc_switch(struct kunit *test, u64 route,
 				      u8 upstream_port, u8 max_port_number)
 {
@@ -44,6 +56,15 @@ static struct tb_switch *alloc_switch(st
 	if (!sw)
 		return NULL;
 
+	/*
+	 * HopID allocations take a reference to their routers and those devices
+	 * have to be initialized for that to work.
+	 */
+	sw->dev.release = tb_test_switch_release;
+	device_initialize(&sw->dev);
+	if (kunit_add_action_or_reset(test, tb_test_switch_put, sw))
+		return NULL;
+
 	sw->config.upstream_port_number = upstream_port;
 	sw->config.depth = tb_route_length(route);
 	sw->config.route_hi = upper_32_bits(route);
author maintainer
#355 lore raw

PATCH 352/428 thunderbolt: Make the DP tunnel activation callback mandatory

In reply to Greg Kroah-Hartman

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

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

From: Sven Peter <[email protected]>

commit 12f5d8b85a66a0ac08d082517d92ce136f8c0d42 upstream.

tb_tunnel_alloc_dp() takes an optional callback which is run from dprx_work once the DPRX capabilities read has completed. Without that callback tb_dp_dprx_start() reads the capabilities synchronously and never queues the work. It however always takes a tunnel reference which is only dropped by dprx_work itself or by tb_dp_dprx_stop() when cancel_delayed_work() actually canceled that work. That reference is thus leaked for every tunnel without a callback.

The only tunnels without one are those from tb_tunnel_discover_dp(), which are activated again when restoring from hibernation. Pass the callback to tb_tunnel_discover_dp() as well and drop the synchronous path such that the DPRX capabilities are always read from dprx_work. Hibernation restore then also no longer blocks for up to 12 seconds while waiting for that read to complete.

Also fix up the KUnit tests.

Fixes d6d458d42e1e ("thunderbolt: Handle DisplayPort tunnel activation asynchronously")
---
 drivers/thunderbolt/tb.c     |    4 ++-
 drivers/thunderbolt/test.c   |   37 +++++++++++++++++++++++----------
 drivers/thunderbolt/tunnel.c |   47 +++++++++++++++++++++++--------------------
 drivers/thunderbolt/tunnel.h |    8 ++++---
 4 files changed, 60 insertions(+), 36 deletions(-)
drivers/thunderbolt/tb.c +3 -1
@@ -89,6 +89,7 @@ static void tb_dp_resource_unavailable(s
 				       const char *reason);
 static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port,
 					  int retry, unsigned long delay);
+static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data);
 
 static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug)
 {
@@ -385,7 +386,8 @@ static void tb_switch_discover_tunnels(s
 
 		switch (port->config.type) {
 		case TB_TYPE_DP_HDMI_IN:
-			tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids);
+			tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids,
+						       tb_dp_tunnel_active, tb);
 			tb_increase_tmu_accuracy(tunnel);
 			break;
 
drivers/thunderbolt/test.c +26 -11
@@ -1407,6 +1407,10 @@ static void tb_test_tunnel_pcie(struct k
 	tb_tunnel_put(tunnel1);
 }
 
+static void tb_test_dp_tunnel_active(struct tb_tunnel *tunnel, void *data)
+{
+}
+
 static void tb_test_tunnel_dp(struct kunit *test)
 {
 	struct tb_switch *host, *dev;
@@ -1427,7 +1431,8 @@ static void tb_test_tunnel_dp(struct kun
 	in = &host->ports[5];
 	out = &dev->ports[13];
 
-	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+				    tb_test_dp_tunnel_active, NULL);
 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 	KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
 	KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@@ -1473,7 +1478,8 @@ static void tb_test_tunnel_dp_chain(stru
 	in = &host->ports[5];
 	out = &dev4->ports[14];
 
-	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+				    tb_test_dp_tunnel_active, NULL);
 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 	KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
 	KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@@ -1523,7 +1529,8 @@ static void tb_test_tunnel_dp_tree(struc
 	in = &dev2->ports[13];
 	out = &dev5->ports[13];
 
-	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+				    tb_test_dp_tunnel_active, NULL);
 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 	KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
 	KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@@ -1588,7 +1595,8 @@ static void tb_test_tunnel_dp_max_length
 	in = &dev6->ports[13];
 	out = &dev12->ports[13];
 
-	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+				    tb_test_dp_tunnel_active, NULL);
 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 	KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
 	KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@@ -1658,7 +1666,8 @@ static void tb_test_tunnel_3dp(struct ku
 	out2 = &dev5->ports[13];
 	out3 = &dev4->ports[14];
 
-	tunnel1 = tb_tunnel_alloc_dp(NULL, in1, out1, 1, 0, 0, NULL, NULL);
+	tunnel1 = tb_tunnel_alloc_dp(NULL, in1, out1, 1, 0, 0,
+				     tb_test_dp_tunnel_active, NULL);
 	KUNIT_ASSERT_TRUE(test, tunnel1 != NULL);
 	KUNIT_EXPECT_EQ(test, tunnel1->type, TB_TUNNEL_DP);
 	KUNIT_EXPECT_PTR_EQ(test, tunnel1->src_port, in1);
@@ -1666,7 +1675,8 @@ static void tb_test_tunnel_3dp(struct ku
 	KUNIT_ASSERT_EQ(test, tunnel1->npaths, 3);
 	KUNIT_ASSERT_EQ(test, tunnel1->paths[0]->path_length, 3);
 
-	tunnel2 = tb_tunnel_alloc_dp(NULL, in2, out2, 1, 0, 0, NULL, NULL);
+	tunnel2 = tb_tunnel_alloc_dp(NULL, in2, out2, 1, 0, 0,
+				     tb_test_dp_tunnel_active, NULL);
 	KUNIT_ASSERT_TRUE(test, tunnel2 != NULL);
 	KUNIT_EXPECT_EQ(test, tunnel2->type, TB_TUNNEL_DP);
 	KUNIT_EXPECT_PTR_EQ(test, tunnel2->src_port, in2);
@@ -1674,7 +1684,8 @@ static void tb_test_tunnel_3dp(struct ku
 	KUNIT_ASSERT_EQ(test, tunnel2->npaths, 3);
 	KUNIT_ASSERT_EQ(test, tunnel2->paths[0]->path_length, 4);
 
-	tunnel3 = tb_tunnel_alloc_dp(NULL, in3, out3, 1, 0, 0, NULL, NULL);
+	tunnel3 = tb_tunnel_alloc_dp(NULL, in3, out3, 1, 0, 0,
+				     tb_test_dp_tunnel_active, NULL);
 	KUNIT_ASSERT_TRUE(test, tunnel3 != NULL);
 	KUNIT_EXPECT_EQ(test, tunnel3->type, TB_TUNNEL_DP);
 	KUNIT_EXPECT_PTR_EQ(test, tunnel3->src_port, in3);
@@ -1772,7 +1783,8 @@ static void tb_test_tunnel_port_on_path(
 	in = &dev2->ports[13];
 	out = &dev5->ports[13];
 
-	dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+	dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+				       tb_test_dp_tunnel_active, NULL);
 	KUNIT_ASSERT_NOT_NULL(test, dp_tunnel);
 
 	KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, in));
@@ -2204,7 +2216,8 @@ static void tb_test_credit_alloc_dp(stru
 	in = &host->ports[5];
 	out = &dev->ports[14];
 
-	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+	tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+				    tb_test_dp_tunnel_active, NULL);
 	KUNIT_ASSERT_NOT_NULL(test, tunnel);
 	KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)3);
 
@@ -2440,7 +2453,8 @@ static struct tb_tunnel *TB_TEST_DP_TUNN
 
 	in = &host->ports[5];
 	out = &dev->ports[13];
-	dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+	dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+					tb_test_dp_tunnel_active, NULL);
 	KUNIT_ASSERT_NOT_NULL(test, dp_tunnel1);
 	KUNIT_ASSERT_EQ(test, dp_tunnel1->npaths, (size_t)3);
 
@@ -2477,7 +2491,8 @@ static struct tb_tunnel *TB_TEST_DP_TUNN
 
 	in = &host->ports[6];
 	out = &dev->ports[14];
-	dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+	dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+					tb_test_dp_tunnel_active, NULL);
 	KUNIT_ASSERT_NOT_NULL(test, dp_tunnel2);
 	KUNIT_ASSERT_EQ(test, dp_tunnel2->npaths, (size_t)3);
 
drivers/thunderbolt/tunnel.c +26 -21

98 lines not on this page · raw message

drivers/thunderbolt/tunnel.h +5 -3
@@ -66,8 +66,8 @@ enum tb_tunnel_state {
  * @dprx_canceled: Was DPRX capabilities read poll canceled
  * @dprx_timeout: If set DPRX capabilities read poll work will timeout after this passes
  * @dprx_work: Worker that is scheduled to poll completion of DPRX capabilities read
- * @callback: Optional callback called when DP tunnel is fully activated
- * @callback_data: Optional data for @callback
+ * @callback: Callback called when DP tunnel is fully activated
+ * @callback_data: Data for @callback
  * @paths: All paths required by the tunnel
  */
 struct tb_tunnel {
@@ -117,7 +117,9 @@ struct tb_tunnel *tb_tunnel_alloc_pci(st
 bool tb_tunnel_reserved_pci(struct tb_port *port, int *reserved_up,
 			    int *reserved_down);
 struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
-					bool alloc_hopid);
+					bool alloc_hopid,
+					void (*callback)(struct tb_tunnel *, void *),
+					void *callback_data);
 struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
 				     struct tb_port *out, int link_nr,
 				     int max_up, int max_down,
author maintainer
#356 lore raw

PATCH 353/428 thunderbolt: Mark discovered tunnels as active

In reply to Greg Kroah-Hartman

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

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

From: Sven Peter <[email protected]>

commit c222f80be55f6c17851af1c68a70ae4046c848e2 upstream.

Discovered tunnels that have been activated by whatever was running before us stay in TB_TUNNEL_INACTIVE until hibernation restore such that anything depending on tb_tunnel_is_active() skips them. Mark them active during discovery instead. These now also emit TUNNEL_EVENT=activated uevents during discovery.

Fixes d6d458d42e1e ("thunderbolt: Handle DisplayPort tunnel activation asynchronously")
---
 drivers/thunderbolt/tunnel.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/thunderbolt/tunnel.c +3 -0
@@ -507,6 +507,7 @@ struct tb_tunnel *tb_tunnel_discover_pci
 		goto err_deactivate;
 	}
 
+	tb_tunnel_set_active(tunnel, true);
 	tb_tunnel_dbg(tunnel, "discovered\n");
 	return tunnel;
 
@@ -1658,6 +1659,7 @@ struct tb_tunnel *tb_tunnel_discover_dp(
 
 	tb_dp_dump(tunnel);
 
+	tb_tunnel_set_active(tunnel, true);
 	tb_tunnel_dbg(tunnel, "discovered\n");
 	return tunnel;
 
@@ -2286,6 +2288,7 @@ struct tb_tunnel *tb_tunnel_discover_usb
 			tb_usb3_reclaim_available_bandwidth;
 	}
 
+	tb_tunnel_set_active(tunnel, true);
 	tb_tunnel_dbg(tunnel, "discovered\n");
 	return tunnel;
 
author maintainer
#357 lore raw

PATCH 354/428 thunderbolt: Tear down inactive DP tunnels when the domain is stopped

In reply to Greg Kroah-Hartman

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

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

From: Sven Peter <[email protected]>

commit 0b457c6733f73421a3fb03786f13d67c618b5119 upstream.

tb_stop() only tears down DMA tunnels so a DP tunnel that is still waiting for dprx_work to complete keeps that work queued while the routers are removed and the control channel is stopped. The work only stops once the DPRX timeout has passed and because it requeues itself until then the flush_workqueue() in tb_domain_remove() won't wait for its final run. The callback then runs against a domain that is already torn down. A reference to that domain is kept so the completion waiting for that domain to disappear in unbind will block until the timeout is eventually reached.

Tear down DP tunnels that are not active yet as well which also cancels that work. Tunnels for displays that are already alive are untouched and keep working.

Fixes d6d458d42e1e ("thunderbolt: Handle DisplayPort tunnel activation asynchronously")
---
 drivers/thunderbolt/tb.c |    9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/thunderbolt/tb.c +5 -4
@@ -2950,11 +2950,12 @@ static void tb_stop(struct tb *tb)
 	/* tunnels are only present after everything has been initialized */
 	list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
 		/*
-		 * DMA tunnels require the driver to be functional so we
-		 * tear them down. Other protocol tunnels can be left
-		 * intact.
+		 * DMA tunnels and DP tunnels which are not yet active require
+		 * the driver to be functional so we tear them down.
+		 * Other protocol tunnels can be left intact.
 		 */
-		if (tb_tunnel_is_dma(tunnel))
+		if (tb_tunnel_is_dma(tunnel) ||
+		    (tb_tunnel_is_dp(tunnel) && !tb_tunnel_is_active(tunnel)))
 			tb_tunnel_deactivate(tunnel);
 		tb_tunnel_put(tunnel);
 	}
author maintainer
#358 lore raw

PATCH 355/428 thunderbolt: Fix domain reference leak when DPRX read is canceled

In reply to Greg Kroah-Hartman

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

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

From: Sven Peter <[email protected]>

commit 1fd1f67c94d30889377bba774f032ec15e8b9299 upstream.

tb_tunnel_one_dp() takes a domain reference which is only dropped once tb_dp_tunnel_active() has run on the work queue. If that work is cancelled that reference is leaked. Since
commit f5cc545f5969 ("thunderbolt: Wait for tb_domain_release() to complete when driver is removed") instead of just leaking memory this now also blocks in the completion wait forever when unbinding the driver.

This can be triggered whenever a DP tunnel is torn down before the DPRX read has completed, e.g. by unplugging within the timeout, and then unbinding the driver.

That reference only exists to keep the domain around while the DPRX work is scheduled so let the work itself own it: take it in tb_dp_dprx_start() and drop it in both places that end the work. Get/put are then paired inside the same file and it doesn't matter anymore if the callback ever runs.

Fixes d6d458d42e1e ("thunderbolt: Handle DisplayPort tunnel activation asynchronously")
---
 drivers/thunderbolt/tb.c     |    6 +-----
 drivers/thunderbolt/tunnel.c |   12 +++++++++---
 2 files changed, 10 insertions(+), 8 deletions(-)
drivers/thunderbolt/tb.c +1 -5
@@ -1966,8 +1966,6 @@ static void tb_dp_tunnel_active(struct t
 		tb_dp_resource_unavailable(tb, in, "DPRX negotiation failed");
 	}
 	mutex_unlock(&tb->lock);
-
-	tb_domain_put(tb);
 }
 
 static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in,
@@ -2028,8 +2026,7 @@ static void tb_tunnel_one_dp(struct tb *
 	       available_up, available_down);
 
 	tunnel = tb_tunnel_alloc_dp(tb, in, out, link_nr, available_up,
-				    available_down, tb_dp_tunnel_active,
-				    tb_domain_get(tb));
+				    available_down, tb_dp_tunnel_active, tb);
 	if (!tunnel) {
 		tb_port_dbg(out, "could not allocate DP tunnel\n");
 		goto err_reclaim_usb;
@@ -2050,7 +2047,6 @@ err_free:
 	tb_tunnel_put(tunnel);
 err_reclaim_usb:
 	tb_reclaim_usb3_bandwidth(tb, in, out);
-	tb_domain_put(tb);
 err_detach_group:
 	tb_detach_bandwidth_group(in);
 err_dealloc_dp:
drivers/thunderbolt/tunnel.c +9 -3
@@ -1109,15 +1109,17 @@ static void tb_dp_dprx_work(struct work_
 
 	tunnel->callback(tunnel, tunnel->callback_data);
 	tb_tunnel_put(tunnel);
+	tb_domain_put(tb);
 }
 
 static int tb_dp_dprx_start(struct tb_tunnel *tunnel)
 {
 	/*
-	 * Bump up the reference to keep the tunnel around. It will be
-	 * dropped in tb_dp_dprx_stop() once the tunnel is deactivated.
+	 * Bump up the references to keep the tunnel and the domain around
+	 * until the work has run or has been canceled.
 	 */
 	tb_tunnel_get(tunnel);
+	tb_domain_get(tunnel->tb);
 
 	tunnel->dprx_started = true;
 	tunnel->dprx_timeout = dprx_timeout_to_ktime(dprx_timeout);
@@ -1128,11 +1130,15 @@ static int tb_dp_dprx_start(struct tb_tu
 
 static void tb_dp_dprx_stop(struct tb_tunnel *tunnel)
 {
+	struct tb *tb = tunnel->tb;
+
 	if (tunnel->dprx_started) {
 		tunnel->dprx_started = false;
 		tunnel->dprx_canceled = true;
-		if (cancel_delayed_work(&tunnel->dprx_work))
+		if (cancel_delayed_work(&tunnel->dprx_work)) {
 			tb_tunnel_put(tunnel);
+			tb_domain_put(tb);
+		}
 	}
 }
 
author maintainer
#359 lore raw

PATCH 356/428 thunderbolt: Dont access a DP tunnel after its DPRX read was canceled

In reply to Greg Kroah-Hartman

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

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

From: Sven Peter <[email protected]>

commit 419fa32fa5bc2d7fb9d0d15d5630b1c1090808b3 upstream.

tb_dp_dprx_work() checks ->dprx_canceled before it takes tb->lock so it misses a tb_dp_dprx_stop() that could not cancel the already running work. It then polls the DPRX capabilities and runs the callback for a tunnel that has already been torn down while the domain is suspending or going away.

This can be hit by cancelling the DPRX read from outside the ordered tb->wq: During suspend tb_disconnect_and_release_dp() does just this and with a later patch tb_stop() will do it as well. The latter in combination with the Apple NHI where the DPRX read never completed is how I hit this.

Check the flag with tb->lock held instead and check it again in tb_dp_tunnel_active() because the callback runs after the lock has been dropped again.

Also clear the flag in tb_dp_dprx_start() so that it only ever describes the work that is currently in flight.

Fixes d6d458d42e1e ("thunderbolt: Handle DisplayPort tunnel activation asynchronously")
---
 drivers/thunderbolt/tb.c     |   12 ++++++++++++
 drivers/thunderbolt/tunnel.c |   11 +++++++++--
 2 files changed, 21 insertions(+), 2 deletions(-)
drivers/thunderbolt/tb.c +12 -0
@@ -1912,6 +1912,18 @@ static void tb_dp_tunnel_active(struct t
 	struct tb *tb = data;
 
 	mutex_lock(&tb->lock);
+
+	/*
+	 * If the DPRX read was canceled the tunnel is already being torn
+	 * down by whoever canceled it. Do not touch the adapters here
+	 * because the routers may be gone by now.
+	 */
+	if (tunnel->dprx_canceled) {
+		tb_tunnel_dbg(tunnel, "DPRX read canceled, not activating\n");
+		mutex_unlock(&tb->lock);
+		return;
+	}
+
 	if (tb_tunnel_is_active(tunnel)) {
 		int consumed_up, consumed_down, ret;
 
drivers/thunderbolt/tunnel.c +9 -2
@@ -1091,8 +1091,14 @@ static void tb_dp_dprx_work(struct work_
 	struct tb_tunnel *tunnel = container_of(work, typeof(*tunnel), dprx_work.work);
 	struct tb *tb = tunnel->tb;
 
+	/*
+	 * The DPRX read can be canceled while this work is waiting for
+	 * tb->lock. Check the flag only once it is held: while the lock is
+	 * held the tunnel cannot be torn down under us and the adapters are
+	 * safe to access.
+	 */
+	mutex_lock(&tb->lock);
 	if (!tunnel->dprx_canceled) {
-		mutex_lock(&tb->lock);
 		if (tb_dp_is_usb4(tunnel->src_port->sw) &&
 		    tb_dp_wait_dprx(tunnel, TB_DPRX_WAIT_TIMEOUT)) {
 			if (ktime_before(ktime_get(), tunnel->dprx_timeout)) {
@@ -1104,8 +1110,8 @@ static void tb_dp_dprx_work(struct work_
 		} else {
 			tb_tunnel_set_active(tunnel, true);
 		}
-		mutex_unlock(&tb->lock);
 	}
+	mutex_unlock(&tb->lock);
 
 	tunnel->callback(tunnel, tunnel->callback_data);
 	tb_tunnel_put(tunnel);
@@ -1122,6 +1128,7 @@ static int tb_dp_dprx_start(struct tb_tu
 	tb_domain_get(tunnel->tb);
 
 	tunnel->dprx_started = true;
+	tunnel->dprx_canceled = false;
 	tunnel->dprx_timeout = dprx_timeout_to_ktime(dprx_timeout);
 	queue_delayed_work(tunnel->tb->wq, &tunnel->dprx_work, 0);
 
author maintainer
#360 lore raw

PATCH 357/428 thunderbolt: Fix NULL dereference in tb_remove_work()

In reply to Greg Kroah-Hartman

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

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

From: Fedor Pchelkin <[email protected]>

commit 4310c6b8e75d6a47f7548e5948fbe6318aa4440a upstream.

There is a slight race between tb_remove_work() and tb_domain_remove() which leads to dereferencing a NULL tb->root_switch pointer inside tb_free_unplugged_xdomains():

Thread A Thread B

tb_remove_work()

                                    tb_domain_remove()
                                      mutex_lock(&tb->lock)
                                      tb_stop()
                                        /* doesn't cancel a running callback */
                                        cancel_delayed_work(&tcm->remove_work)
                                        ...
                                        tb_switch_remove(tb->root_switch)
                                        tb->root_switch = NULL
                                      mutex_unlock(&tb->lock)
    mutex_lock(&tb->lock)
    ...
    /* without checking ->root_switch */
    tb_free_unplugged_xdomains(tb->root_switch)
    mutex_unlock(&tb->lock)

Commit a8937f35cf39 ("thunderbolt: Remove XDomain from the bus without holding tb->lock") doesn't seem right to move tb_free_unplugged_xdomains() out of the &tb->lock section and the check for tb->root_switch, in particular. It states:

For this reason separate removing the XDomain from the topology data structures (where we need the lock) from unregistering the device from the bus (where remove callbacks of the drivers are being called).

tb_free_unplugged_xdomains() belongs to the former group of functions requiring the lock. And it also calls tb_xdomain_remove() which should only be called with &tb->lock held.

Found by Linux Verification Center (linuxtesting.org) with Svace static analysis tool.

Fixes a8937f35cf39 ("thunderbolt: Remove XDomain from the bus without holding tb->lock")
---
 drivers/thunderbolt/tb.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/thunderbolt/tb.c +3 -3
@@ -3264,11 +3264,11 @@ static void tb_remove_work(struct work_s
 	struct tb *tb = tcm_to_tb(tcm);
 
 	mutex_lock(&tb->lock);
-	if (tb->root_switch)
+	if (tb->root_switch) {
 		tb_free_unplugged_children(tb->root_switch);
+		tb_free_unplugged_xdomains(tb->root_switch);
+	}
 	mutex_unlock(&tb->lock);
-
-	tb_free_unplugged_xdomains(tb->root_switch);
 }
 
 static int tb_runtime_resume(struct tb *tb)
author maintainer
#361 lore raw

PATCH 358/428 thunderbolt: Disable CL states for the Anker Prime TB5 dock

In reply to Greg Kroah-Hartman

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

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

From: Kurt Lieber <[email protected]>

commit 395e9f2967a7ac6898026e8f136c369805dc2fd9 upstream.

The Anker Prime TB5 dock identifies itself in the DROM as 01ea:83b5. Its router config space is an Intel Barlow Ridge 80G hub (8087:5786). The upstream lane adapter advertises CL0s, CL1, and CL2. With CLx left enabled, TMU uni-directional LowRes setup fails with -ENOTCONN, the USB3 and DisplayPort tunnels are aborted, and the router reconnects in a loop. Loading thunderbolt with clx=0 keeps the link up on this machine.

Match the DROM id together with the Barlow Ridge hub id and disable CL states for this dock only. QUIRK_NO_CLX takes the same early-out as the module parameter, without disabling CLx for every other router.

---
 drivers/thunderbolt/quirks.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/thunderbolt/quirks.c +3 -0
@@ -99,6 +99,9 @@ static const struct tb_quirk tb_quirks[]
 		  quirk_usb3_maximum_bandwidth },
 	{ 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_40G_BRIDGE, 0x0000, 0x0000,
 		  quirk_usb3_maximum_bandwidth },
+	/* CLx is unstable on the Anker Prime TB5 dock (DROM 01ea:83b5) */
+	{ 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_80G_BRIDGE, 0x01ea, 0x83b5,
+		  quirk_clx_disable },
 	/*
 	 * Block Runtime PM in DP redrive mode for Intel Barlow Ridge host
 	 * controllers.
author maintainer
#362 lore raw

PATCH 359/428 thunderbolt: Reject oversized XDomain properties responses

In reply to Greg Kroah-Hartman

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

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

From: Daehyeon Ko <[email protected]>

commit 80756e263846ab694984e749e8c626897331438f upstream.

tb_xdp_properties_request() allocates room for 45 data dwords in its 252-byte response buffer. The XDomain length field is six bits wide, however, and a malicious peer can set it to 63. After the fixed response fields are subtracted, the driver treats this as 48 data dwords.

Commit 322e93448d90 ("thunderbolt: Clamp XDomain response data copy to allocation size") only bounds the copy against data_len. If data_len is at least 48, memcpy() reads 192 bytes from the 180-byte res->data array, causing a 12-byte heap out-of-bounds read. Commit 4db2bd2ed478 ("thunderbolt: Limit XDomain response copy to actual frame size") limits the earlier copy but does not constrain this header-derived length.

Reject response data lengths that exceed the allocated source buffer before copying them into the assembled property block.

Fixes d1ff70241a27 ("thunderbolt: Add support for XDomain discovery protocol")
Assisted-by LLM
---
 drivers/thunderbolt/xdomain.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/thunderbolt/xdomain.c +4 -0
@@ -377,6 +377,10 @@ static int tb_xdp_properties_request(str
 
 		len += sizeof(res->hdr.xd_hdr) / 4;
 		len -= sizeof(*res) / 4;
+		if (len > TB_XDP_PROPERTIES_MAX_DATA_LENGTH) {
+			ret = -EINVAL;
+			goto err;
+		}
 
 		if (res->offset != req.offset) {
 			ret = -EINVAL;
author maintainer
#363 lore raw

PATCH 360/428 smb: client: discard post-EOF pagecache when extending a file via zero range

In reply to Greg Kroah-Hartman

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

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

From: Paulo Alcantara <[email protected]>

commit da4fe97fb6c730d9c94a115c7bfa6f98e0d578d0 upstream.

smb3_zero_range() removed the pagecache only for the range being zeroed, so when it extends the file the folio straddling the old EOF - which may hold data dirtied past EOF through an mmap - was never dropped and became visible as file content once the file was extended.

Lower the discard boundary to the smaller of the zero-range offset and the old EOF.

Fixes 72c419d9b073 ("cifs: fix smb3_zero_range so it can expand the file-size when required")
Cc Christian Brauner <[email protected]>
Cc Matthew Wilcox <[email protected]>
Cc Ronnie Sahlberg <[email protected]>
Cc Shyam Prasad N <[email protected]>
Cc Tom Talpey <[email protected]>
Cc Bharath SM <[email protected]>
---
 fs/smb/client/smb2ops.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
fs/smb/client/smb2ops.c +4 -1
@@ -3555,8 +3555,11 @@ static long smb3_zero_range(struct file
 	/*
 	 * We zero the range through ioctl, so we need remove the page caches
 	 * first, otherwise the data may be inconsistent with the server.
+	 *
+	 * Start at the old EOF when extending so the folio straddling it, which
+	 * may hold data written past EOF through an mmap, is dropped too.
 	 */
-	truncate_pagecache_range(inode, offset, offset + len - 1);
+	truncate_pagecache_range(inode, min(offset, i_size), offset + len - 1);
 	netfs_wait_for_outstanding_io(inode);
 
 	/* if file not oplocked can't be sure whether asking to extend size */
author maintainer
#364 lore raw

PATCH 361/428 smb: client: discard post-EOF pagecache when extending a file via copy range

In reply to Greg Kroah-Hartman

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

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

From: Paulo Alcantara <[email protected]>

commit 7b473ac7cd6f7e087876d3c67b1bc6df662fb429 upstream.

cifs_file_copychunk_range() invalidated the pagecache only for the destination region, so when a copy extends the file the folio straddling the old EOF - which may hold data dirtied past EOF through an mmap - was never dropped and became visible as file content once the file was extended.

Start the invalidation at the smaller of the destination offset and the old EOF. filemap_invalidate_inode() writes back before invalidating while i_size is still old, so nothing past the old EOF is flushed to the server.

Fixes 8101d6e112e2 ("cifs: Fix copy offload to flush destination region")
Cc Christian Brauner <[email protected]>
Cc Matthew Wilcox <[email protected]>
Cc Ronnie Sahlberg <[email protected]>
Cc Shyam Prasad N <[email protected]>
Cc Tom Talpey <[email protected]>
Cc Bharath SM <[email protected]>
---
 fs/smb/client/cifsfs.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
fs/smb/client/cifsfs.c +6 -1
@@ -1585,8 +1585,13 @@ ssize_t cifs_file_copychunk_range(unsign
 	/* Flush and invalidate all the folios in the destination region.  If
 	 * the copy was successful, then some of the flush is extra overhead,
 	 * but we need to allow for the copy failing in some way (eg. ENOSPC).
+	 *
+	 * Start at the old EOF when extending so the folio straddling it, which
+	 * may hold data written past EOF through an mmap, is dropped too.
 	 */
-	rc = filemap_invalidate_inode(target_inode, true, destoff, destoff + len - 1);
+	rc = filemap_invalidate_inode(target_inode, true,
+				      min(destoff, i_size_read(target_inode)),
+				      destoff + len - 1);
 	if (rc)
 		goto unlock;
 
author maintainer
#365 lore raw

PATCH 362/428 smb: client: discard post-EOF pagecache when extending a file via clone range

In reply to Greg Kroah-Hartman

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

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

From: Paulo Alcantara <[email protected]>

commit 4721587f96cb8de8164f3fd203093a0875560999 upstream.

cifs_remap_file_range() discarded the pagecache only for the folios overlapping the destination region, so when a clone extends the file the folio straddling the old EOF - which may hold data dirtied past EOF through an mmap - was never dropped and became visible as file content once the file was extended.

Start the discard at the smaller of fstart and the old EOF. i_size is still old here, so nothing past the old EOF is flushed to the server.

Fixes c54fc3a4f375 ("cifs: Fix flushing, invalidation and file size with FICLONE")
Cc Christian Brauner <[email protected]>
Cc Matthew Wilcox <[email protected]>
Cc Ronnie Sahlberg <[email protected]>
Cc Shyam Prasad N <[email protected]>
Cc Tom Talpey <[email protected]>
Cc Bharath SM <[email protected]>
---
 fs/smb/client/cifsfs.c |    9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
fs/smb/client/cifsfs.c +7 -2
@@ -1467,9 +1467,14 @@ static loff_t cifs_remap_file_range(stru
 	i_size = target_inode->i_size;
 	spin_unlock(&target_inode->i_lock);
 
-	/* Discard all the folios that overlap the destination region. */
+	/*
+	 * Discard all the folios that overlap the destination region.  Start at
+	 * the old EOF when extending so the folio straddling it, which may hold
+	 * data written past EOF through an mmap, is dropped too.
+	 */
 	cifs_dbg(FYI, "about to discard pages %llx-%llx\n", fstart, fend);
-	truncate_inode_pages_range(&target_inode->i_data, fstart, fend);
+	truncate_inode_pages_range(&target_inode->i_data,
+				   min(fstart, i_size), fend);
 
 	fscache_invalidate(cifs_inode_cookie(target_inode), NULL, i_size, 0);
 
author maintainer
#366 lore raw

PATCH 363/428 smb: client: flush and commit data before querying allocated ranges

In reply to Greg Kroah-Hartman

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

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

From: Paulo Alcantara <[email protected]>

commit aa994489bd835547c191827acc460192e147032d upstream.

The mode 0 / FALLOC_FL_KEEP_SIZE fallocate emulation for small interior ranges in smb3_simple_fallocate_range() issues FSCTL_QUERY_ALLOCATED_RANGES and zero-fills any sub-range the server reports as an unallocated hole, to force block allocation without changing file contents.

This is unsafe against Windows servers. Per the Win32 documentation, FSCTL_QUERY_ALLOCATED_RANGES only reports ranges that *may* contain nonzero data and is explicitly not coherent with recently written data:

"a call to FSCTL_QUERY_ALLOCATED_RANGES [after writing to a network
file] would not necessarily return a correct list of allocated regions. To ensure coherency [...] flush the data to the file"

fsx (generic/363) reproduces the resulting corruption against Windows Server 2022: a range is written, an uncached read returns the data, yet the next FSCTL_QUERY_ALLOCATED_RANGES on that range reports it as a whole hole, so the emulation overwrites live data with zeroes. A network trace confirmed the WRITE, the READ returning data and the query returning an empty range list within microseconds. Samba does not exhibit this; its query is coherent with the written data.

Write back the dirty pages, drain any outstanding I/O and issue an SMB2 FLUSH to force the server to commit the data before querying the allocated ranges, so the query reflects the data actually present.

With this, generic/363 passes against both Windows Server 2022 and Samba over 100000 fsx operations.

Fixes 966a3cb7c7db ("cifs: improve fallocate emulation")
Cc Christian Brauner <[email protected]>
Cc Matthew Wilcox <[email protected]>
Cc Ronnie Sahlberg <[email protected]>
Cc Shyam Prasad N <[email protected]>
Cc Tom Talpey <[email protected]>
Cc Bharath SM <[email protected]>
---
 fs/smb/client/smb2ops.c |   32 ++++++++++++++++++++++++++------
 1 file changed, 26 insertions(+), 6 deletions(-)
fs/smb/client/smb2ops.c +26 -6
@@ -3745,6 +3745,24 @@ static int smb3_simple_fallocate_range(u
 		goto out;
 	}
 
+	filemap_invalidate_lock(inode->i_mapping);
+
+	/*
+	 * Flush and commit the data to the server, otherwise
+	 * FSCTL_QUERY_ALLOCATED_RANGES might report recently written data as
+	 * unallocated holes on Windows Servers, and the loop below would
+	 * then zero-fill them and corrupt the file.
+	 */
+	rc = filemap_write_and_wait_range(inode->i_mapping, off,
+					  off + len - 1);
+	if (rc)
+		goto out_unlock;
+	netfs_wait_for_outstanding_io(inode);
+	rc = SMB2_flush(xid, tcon, cfile->fid.persistent_fid,
+			cfile->fid.volatile_fid);
+	if (rc)
+		goto out_unlock;
+
 	in_data.file_offset = cpu_to_le64(off);
 	in_data.length = cpu_to_le64(len);
 	rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
@@ -3754,7 +3772,7 @@ static int smb3_simple_fallocate_range(u
 			1024 * sizeof(struct file_allocated_range_buffer),
 			(char **)&out_data, &out_data_len);
 	if (rc)
-		goto out;
+		goto out_unlock;
 
 	tmp_data = out_data;
 	while (len) {
@@ -3764,12 +3782,12 @@ static int smb3_simple_fallocate_range(u
 		if (out_data_len == 0) {
 			rc = smb3_simple_fallocate_write_range(xid, tcon,
 					       cfile, off, len, buf);
-			goto out;
+			goto out_unlock;
 		}
 
 		if (out_data_len < sizeof(struct file_allocated_range_buffer)) {
 			rc = -EINVAL;
-			goto out;
+			goto out_unlock;
 		}
 
 		range_start = le64_to_cpu(tmp_data->file_offset);
@@ -3777,7 +3795,7 @@ static int smb3_simple_fallocate_range(u
 		if (check_add_overflow(range_start, range_len, &range_end) ||
 		    range_end > S64_MAX) {
 			rc = -EINVAL;
-			goto out;
+			goto out_unlock;
 		}
 
 		if (off < range_start) {
@@ -3792,11 +3810,11 @@ static int smb3_simple_fallocate_range(u
 			rc = smb3_simple_fallocate_write_range(xid, tcon,
 					       cfile, off, l, buf);
 			if (rc)
-				goto out;
+				goto out_unlock;
 			off = off + l;
 			len = len - l;
 			if (len == 0)
-				goto out;
+				goto out_unlock;
 		}
 		/*
 		 * We are at a section of allocated data, just skip forward
@@ -3815,6 +3833,8 @@ static int smb3_simple_fallocate_range(u
 		out_data_len -= sizeof(struct file_allocated_range_buffer);
 	}
 
+ out_unlock:
+	filemap_invalidate_unlock(inode->i_mapping);
  out:
 	kfree(out_data);
 	kvfree(buf);
author maintainer
#367 lore raw

PATCH 364/428 smb: client: only require read lease for size-extending zero range

In reply to Greg Kroah-Hartman

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

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

From: Paulo Alcantara <[email protected]>

commit fafc68cccf8a00ed0a1f47c1413a3f16ce4dcdfa upstream.

smb3_zero_range() refuses any FALLOC_FL_ZERO_RANGE that clears FALLOC_FL_KEEP_SIZE with -EOPNOTSUPP whenever the inode is not read caching:

	/* if file not oplocked can't be sure whether asking to extend size */
	rc = -EOPNOTSUPP;
	if (keep_size == false && !CIFS_CACHE_READ(cifsi))
		goto zero_range_exit;

The read lease is only needed to trust the cached i_size when deciding whether the range extends the file. When it is not held, the size can instead be fetched from the server, which is authoritative, rather than refusing the request outright: query the server's end of file and take the larger of it and the cached size for the interior-vs-extend decision. The larger of the two is used because the server's end of file reflects another client's growth while the cached size reflects this client's own writes that may not have reached the server yet; using the server size alone would wrongly shrink the file when the range flush above left an extending write unwritten.

The query isn't lease-protected either, so its result is only trusted to confirm the range is interior, never to justify extending: if it still shows the range going past EOF, refuse with -EOPNOTSUPP instead of calling SMB2_set_eof(), which could otherwise shrink the file if another client extended it further between the query and the call. For the same reason, take the larger of the already-computed i_size and a fresh i_size_read(inode) right before that call, instead of relying on either
alone: the local variable carries the query's result, which is never written back to the inode, while a concurrent write on this client can still extend the cached i_size during the flush, the query, or the zero-data round trip that happen in between.

Rejecting interior ranges is observed as generic/363 randomly failing against Windows Server with

do_zero_range: fallocate: Operation not supported

fsx issues an interior, non-KEEP_SIZE zero range while the inode is transiently not read caching: the server had just downgraded the file's lease from RWH to RH after breaking the write caching, and the ensuing handle reopen/revalidation left CIFS_CACHE_READ momentarily clear. The range sat well within the server's end of file, so no extend was needed, yet the range was refused and fsx aborted. This keeps the emulation correct even when a genuine lease break from another client leaves the inode without read caching -- the case the -EOPNOTSUPP guard turned into a hard failure. The extra round trip only happens on the no-lease path; the common cached case is unchanged.

Fixes 30175628bf7f ("[SMB3] Enable fallocate -z support for SMB3 mounts")
Cc Christian Brauner <[email protected]>
Cc Matthew Wilcox <[email protected]>
Cc Ronnie Sahlberg <[email protected]>
Cc Shyam Prasad N <[email protected]>
Cc Tom Talpey <[email protected]>
Cc Bharath SM <[email protected]>
---
 fs/smb/client/smb2ops.c |   31 ++++++++++++++++++++++++++-----
 1 file changed, 26 insertions(+), 5 deletions(-)
fs/smb/client/smb2ops.c +26 -5
@@ -3517,6 +3517,21 @@ static long smb3_zero_data(struct file *
 			  0, NULL, NULL);
 }
 
+static long query_server_eof(const unsigned int xid,
+			     struct cifs_tcon *tcon,
+			     struct cifsFileInfo *cfile,
+			     unsigned long long *eof)
+{
+	struct smb2_file_all_info file_inf = {};
+	long rc;
+
+	rc = SMB2_query_info(xid, tcon, cfile->fid.persistent_fid,
+			     cfile->fid.volatile_fid, &file_inf);
+	if (!rc)
+		*eof = le64_to_cpu(file_inf.EndOfFile);
+	return rc;
+}
+
 static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
 			    unsigned long long offset, unsigned long long len,
 			    bool keep_size)
@@ -3562,10 +3577,16 @@ static long smb3_zero_range(struct file
 	truncate_pagecache_range(inode, min(offset, i_size), offset + len - 1);
 	netfs_wait_for_outstanding_io(inode);
 
-	/* if file not oplocked can't be sure whether asking to extend size */
-	rc = -EOPNOTSUPP;
-	if (keep_size == false && !CIFS_CACHE_READ(cifsi))
-		goto zero_range_exit;
+	if (!keep_size && !CIFS_CACHE_READ(cifsi)) {
+		rc = query_server_eof(xid, tcon, cfile, &remote_i_size);
+		if (rc)
+			goto zero_range_exit;
+		i_size = max(i_size, remote_i_size);
+		if (i_size < new_size) {
+			rc = -EOPNOTSUPP;
+			goto zero_range_exit;
+		}
+	}
 
 	fscache_invalidate(cifs_inode_cookie(inode), NULL,
 			   i_size_read(inode), 0);
@@ -3577,7 +3598,7 @@ static long smb3_zero_range(struct file
 	/*
 	 * do we also need to change the size of the file?
 	 */
-	if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) {
+	if (!keep_size && umax(i_size, i_size_read(inode)) < new_size) {
 		rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
 				  cfile->fid.volatile_fid, cfile->pid, new_size);
 		if (rc >= 0) {
author maintainer
#368 lore raw

PATCH 365/428 smb: client: flush dirty data before zeroing a range

In reply to Greg Kroah-Hartman

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

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

From: Paulo Alcantara <[email protected]>

commit 8f8d32c1044f70e46e87550e039a15ff20a4b21c upstream.

smb3_zero_range() emulates FALLOC_FL_ZERO_RANGE by invalidating the page cache over the target range with truncate_pagecache_range() and then issuing FSCTL_SET_ZERO_DATA to the server.

Dirty data was only flushed conditionally, when the range reached or extended EOF. For a purely interior zero range that does not reach EOF, no flush happened, so a dirty folio overlapping the range could be written back to the server after the FSCTL and refill the range that was just zeroed with stale data.

Fix this by unconditionally flushing and waiting for dirty data in the range before invalidating the page cache and issuing FSCTL_SET_ZERO_DATA, exactly as was done for smb3_punch_hole() in commit d7d2adcd022b ("smb/client: flush dirty data before punching a hole"); the two paths are structurally identical here.

This is observed as generic/363 randomly reading stale data where a zeroed range is expected against Windows Server.

Fixes 91d1dfae4649 ("cifs: Fix FALLOC_FL_ZERO_RANGE to preflush buffered part of target region")
Cc Christian Brauner <[email protected]>
Cc Matthew Wilcox <[email protected]>
Cc Ronnie Sahlberg <[email protected]>
Cc Shyam Prasad N <[email protected]>
Cc Tom Talpey <[email protected]>
Cc Bharath SM <[email protected]>
---
 fs/smb/client/smb2ops.c |   10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)
fs/smb/client/smb2ops.c +4 -6
@@ -3559,13 +3559,11 @@ static long smb3_zero_range(struct file
 	filemap_invalidate_lock(inode->i_mapping);
 
 	netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point);
-	if (offset + len >= remote_i_size && offset < i_size) {
-		unsigned long long top = umin(offset + len, i_size);
 
-		rc = filemap_write_and_wait_range(inode->i_mapping, offset, top - 1);
-		if (rc < 0)
-			goto zero_range_exit;
-	}
+	rc = filemap_write_and_wait_range(inode->i_mapping, offset,
+					  offset + len - 1);
+	if (rc < 0)
+		goto zero_range_exit;
 
 	/*
 	 * We zero the range through ioctl, so we need remove the page caches
author maintainer
#369 lore raw

PATCH 366/428 smb: client: distinguish real EOF from a stale remote_i_size on read

In reply to Greg Kroah-Hartman

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

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

From: Paulo Alcantara <[email protected]>

commit 75aa4b4d557114276bb72e19a9bce5cb27b5e4b8 upstream.

smb2_readv_callback() sets NETFS_SREQ_HIT_EOF whenever a short read lines up with netfs_read_remote_i_size(inode), the server's EOF as the client currently believes it. That belief can be stale: after a lease downgrade and handle reopen, the tracked remote_i_size can sit below the client's own i_size while an extending write hasn't reached the server yet. A read in that gap comes back short for a reason that has nothing to do with the file's real size, but was still marked HIT_EOF, and netfs reports a short read for it as-is.

Only treat it as real EOF when the position is also at or past the client's own i_size; otherwise mark it NETFS_SREQ_CLEAR_TAIL instead, which tells netfs the shortfall is safe to zero-fill rather than report as a short read.

This is what fsx (generic/363) sees as "short read: 0x0 bytes instead of 0x<n>" against a Windows server.

Fixes 1da29f2c39b6 ("netfs, cifs: Fix handling of short DIO read")
Cc Christian Brauner <[email protected]>
Cc Matthew Wilcox <[email protected]>
Cc Ronnie Sahlberg <[email protected]>
Cc Shyam Prasad N <[email protected]>
Cc Tom Talpey <[email protected]>
Cc Bharath SM <[email protected]>
---
 fs/smb/client/smb2pdu.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
fs/smb/client/smb2pdu.c +8 -2
@@ -4796,14 +4796,20 @@ do_retry:
 				     rdata->got_bytes);
 
 	if (rdata->result == -ENODATA) {
-		__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
 		rdata->result = 0;
+		if (rdata->subreq.start + rdata->subreq.transferred >= i_size_read(inode))
+			__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
+		else
+			__set_bit(NETFS_SREQ_CLEAR_TAIL, &rdata->subreq.flags);
 	} else {
 		size_t trans = rdata->subreq.transferred + rdata->got_bytes;
 		if (trans < rdata->subreq.len &&
 		    rdata->subreq.start + trans >= netfs_read_remote_i_size(inode)) {
-			__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
 			rdata->result = 0;
+			if (rdata->subreq.start + trans >= i_size_read(inode))
+				__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
+			else
+				__set_bit(NETFS_SREQ_CLEAR_TAIL, &rdata->subreq.flags);
 		}
 		if (rdata->got_bytes)
 			__set_bit(NETFS_SREQ_MADE_PROGRESS, &rdata->subreq.flags);
author maintainer
#370 lore raw

PATCH 367/428 smb: client: drain and invalidate before server-side copy/clone

In reply to Greg Kroah-Hartman

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

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

From: Paulo Alcantara <[email protected]>

commit c9b1194e8ec99d95db25db45edd2fbf690f043ab upstream.

cifs_file_copychunk_range() and the clone (FICLONE) path of cifs_remap_file_range() do not serialise the destination page cache against the server-side copy the way the other server-side range operations (smb3_zero_range(), smb3_punch_hole(), smb3_collapse_range() and smb3_insert_range()) do.

cifs_file_copychunk_range() invalidates the destination range with filemap_invalidate_inode(), which takes and drops the mapping's invalidate_lock internally, so the lock is no longer held when the copychunk ioctl is issued. It also never drains in-flight netfs I/O on the target. The clone path never takes the invalidate_lock at all (only i_rwsem), uses a bare truncate_inode_pages_range() and likewise does not drain outstanding I/O.

As a result an asynchronous destination writeback can complete after the server-side copy/clone has run and reinstate stale data over the region just written by the server, corrupting the file. This is the same class of corruption as commit d7d2adcd022b ("smb/client: flush dirty data before punching a hole") and has been seen randomly in generic/363 against Windows Server.

Fix both paths to follow the established ordering: hold the target mapping's invalidate_lock across the flush and invalidation of the destination and the server ioctl, and call netfs_wait_for_outstanding_io() on the target to drain in-flight writes before the ioctl is issued. Only the target inode's invalidate_lock is required, as the source is merely flushed and not invalidated; i_rwsem (already held via lock_two_nondirectories()) is acquired before the invalidate_lock, matching the VFS lock ordering. Since filemap_invalidate_inode() takes that same lock internally, replace it with its own unmap/flush/invalidate steps instead of calling it, and return early on a zero-length copy to avoid a range underflow.

Fixes 8101d6e112e2 ("cifs: Fix copy offload to flush destination region")
Fixes c54fc3a4f375 ("cifs: Fix flushing, invalidation and file size with FICLONE")
Cc Christian Brauner <[email protected]>
Cc Matthew Wilcox <[email protected]>
Cc Ronnie Sahlberg <[email protected]>
Cc Shyam Prasad N <[email protected]>
Cc Tom Talpey <[email protected]>
Cc Bharath SM <[email protected]>
---
 fs/smb/client/cifsfs.c |   31 ++++++++++++++++++++++++++-----
 1 file changed, 26 insertions(+), 5 deletions(-)
fs/smb/client/cifsfs.c +26 -5
@@ -1412,6 +1412,7 @@ static loff_t cifs_remap_file_range(stru
 	 * server could even support copy of range where source = target
 	 */
 	lock_two_nondirectories(target_inode, src_inode);
+	filemap_invalidate_lock(target_inode->i_mapping);
 
 	if (len == 0) {
 		loff_t src_size = i_size_read(src_inode);
@@ -1475,6 +1476,7 @@ static loff_t cifs_remap_file_range(stru
 	cifs_dbg(FYI, "about to discard pages %llx-%llx\n", fstart, fend);
 	truncate_inode_pages_range(&target_inode->i_data,
 				   min(fstart, i_size), fend);
+	netfs_wait_for_outstanding_io(target_inode);
 
 	fscache_invalidate(cifs_inode_cookie(target_inode), NULL, i_size, 0);
 
@@ -1517,6 +1519,7 @@ static loff_t cifs_remap_file_range(stru
 	if (rc)
 		CIFS_I(target_inode)->time = 0;
 unlock:
+	filemap_invalidate_unlock(target_inode->i_mapping);
 	/* although unlocking in the reverse order from locking is not
 	   strictly necessary here it is a little cleaner to be consistent */
 	unlock_two_nondirectories(src_inode, target_inode);
@@ -1540,6 +1543,9 @@ ssize_t cifs_file_copychunk_range(unsign
 	struct cifs_tcon *target_tcon;
 	ssize_t rc;
 
+	if (len == 0)
+		return 0;
+
 	cifs_dbg(FYI, "copychunk range\n");
 
 	if (!src_file->private_data || !dst_file->private_data) {
@@ -1569,6 +1575,7 @@ ssize_t cifs_file_copychunk_range(unsign
 	 * server could even support copy of range where source = target
 	 */
 	lock_two_nondirectories(target_inode, src_inode);
+	filemap_invalidate_lock(target_inode->i_mapping);
 
 	cifs_dbg(FYI, "about to flush pages\n");
 
@@ -1594,11 +1601,24 @@ ssize_t cifs_file_copychunk_range(unsign
 	 * Start at the old EOF when extending so the folio straddling it, which
 	 * may hold data written past EOF through an mmap, is dropped too.
 	 */
-	rc = filemap_invalidate_inode(target_inode, true,
-				      min(destoff, i_size_read(target_inode)),
-				      destoff + len - 1);
-	if (rc)
-		goto unlock;
+	if (target_inode->i_mapping->nrpages) {
+		loff_t fstart = min(destoff, i_size_read(target_inode));
+		loff_t fend = destoff + len - 1;
+
+		unmap_mapping_pages(target_inode->i_mapping,
+				    fstart >> PAGE_SHIFT,
+				    (fend >> PAGE_SHIFT) -
+				    (fstart >> PAGE_SHIFT) + 1,
+				    false);
+		rc = filemap_write_and_wait_range(target_inode->i_mapping,
+						  fstart, fend);
+		if (rc)
+			goto unlock;
+		invalidate_inode_pages2_range(target_inode->i_mapping,
+					      fstart >> PAGE_SHIFT,
+					      fend >> PAGE_SHIFT);
+	}
+	netfs_wait_for_outstanding_io(target_inode);
 
 	fscache_invalidate(cifs_inode_cookie(target_inode), NULL,
 			   i_size_read(target_inode), 0);
@@ -1630,6 +1650,7 @@ ssize_t cifs_file_copychunk_range(unsign
 	CIFS_I(target_inode)->time = 0;
 
 unlock:
+	filemap_invalidate_unlock(target_inode->i_mapping);
 	/* although unlocking in the reverse order from locking is not
 	 * strictly necessary here it is a little cleaner to be consistent
 	 */
author maintainer
#371 lore raw

PATCH 368/428 smb: client: require stable pages for signed connections

In reply to Greg Kroah-Hartman

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

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

From: Paulo Alcantara <[email protected]>

commit 53c5c2c1095eb21e214811fec16cd75f89d72ee2 upstream.

When signing, cifs computes the SMB signature over the pagecache folios in place and then hands those same folios to the socket. If a buffered write mutates a folio while a write subrequest is still in flight, the signature no longer matches the data that follows it, the server rejects the write with STATUS_ACCESS_DENIED (-EACCES), and the error is latched in the mapping, so the next fsync()/fallocate() returns -EIO.

Mark the mapping for stable writes so netfs_perform_write() waits for writeback to complete before modifying an in-flight folio. This is only needed when the connection is signed.

Fixes 3ee1a1fc3981 ("cifs: Cut over to using netfslib")
Cc Christian Brauner <[email protected]>
Cc Matthew Wilcox <[email protected]>
Cc Ronnie Sahlberg <[email protected]>
Cc Shyam Prasad N <[email protected]>
Cc Tom Talpey <[email protected]>
Cc Bharath SM <[email protected]>
---
 fs/smb/client/inode.c |    2 ++
 1 file changed, 2 insertions(+)
fs/smb/client/inode.c +2 -0
@@ -88,6 +88,8 @@ static void cifs_set_ops(struct inode *i
 		else
 			inode->i_data.a_ops = &cifs_addr_ops;
 		mapping_set_large_folios(inode->i_mapping);
+		if (tcon->ses->server->sign)
+			mapping_set_stable_writes(inode->i_mapping);
 		break;
 	case S_IFDIR:
 		if (IS_AUTOMOUNT(inode)) {
author maintainer
#372 lore raw

PATCH 369/428 smb: client: split cifsFileInfo bitfields to avoid shared-byte RMW races

In reply to Greg Kroah-Hartman

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

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

From: Frank Sorenson <[email protected]>

commit 19465a9aeb664f1d710d0d22c07c31bfb7c85446 upstream.

The invalidHandle, swapfile, oplock_break_cancelled, offload, and status_file_deleted fields are stored in the same bitfield byte in struct cifsFileInfo, but are updated in different code paths that may run simultaneously, and are protected by different locks. Since bitfield assignments generate byte-level read-modify-write operations, a modification to one flag can overwrite a concurrent modification to another flag.

To avoid these races, convert these flags from a bitfield to separate bool fields.

Fixes 3bc303c254335 ("cifs: convert oplock breaks to use slow_work facility (try #4)")
Fixes 4e8aea30f7751 ("smb3: enable swap on SMB3 mounts")
Fixes ffceb7640cbfe ("smb: client: do not defer close open handles to deleted files")
Fixes 173217bd73365 ("smb3: retrying on failed server close")
---
 fs/smb/client/cifsglob.h |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
fs/smb/client/cifsglob.h +5 -5
@@ -1450,11 +1450,11 @@ struct cifsFileInfo {
 	struct dentry *dentry;
 	struct tcon_link *tlink;
 	unsigned int f_flags;
-	bool invalidHandle:1;	/* file closed via session abend */
-	bool swapfile:1;
-	bool oplock_break_cancelled:1;
-	bool status_file_deleted:1; /* file has been deleted */
-	bool offload:1; /* offload final part of _put to a wq */
+	bool invalidHandle;	/* file closed via session abend */
+	bool swapfile;
+	bool oplock_break_cancelled;
+	bool status_file_deleted; /* file has been deleted */
+	bool offload; /* offload final part of _put to a wq */
 	__u16 oplock_epoch; /* epoch from the lease break */
 	__u32 oplock_level; /* oplock/lease level from the lease break */
 	int count;
author maintainer
#373 lore raw

PATCH 370/428 iio: accel: kionix-kx022a: Prevent memory leak and fix state

In reply to Greg Kroah-Hartman

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

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

From: Matti Vaittinen <[email protected]>

commit e65e128b51a37dd1a9056ac3d1705d805a5c8bb0 upstream.

The driver allocates memory for samples at buffer enable path. If regmap operation fails in the kx022a_fifo_enable() at the buffer enable path, the allocated memory is never freed. Furthermore, the state information and previous hardware configuration(s) aren't undone, potentially leaving WMI interrupts and buffers enabled, or driver state flags wrong.

Free the memory and revert the hardware configuration and state flags on error path.

Fixes e7123a4dfcd7 ("iio: accel: kionix-kx022a: Refactor driver and add chip_info structure")
---
 drivers/iio/accel/kionix-kx022a.c |   28 +++++++++++++++++++++++-----
 1 file changed, 23 insertions(+), 5 deletions(-)
drivers/iio/accel/kionix-kx022a.c +23 -5
@@ -980,26 +980,44 @@ static int kx022a_fifo_enable(struct kx0
 	guard(mutex)(&data->mutex);
 	ret = __kx022a_turn_on_off(data, false);
 	if (ret)
-		return ret;
+		goto err_free_out;
 
 	/* Update watermark to HW */
 	ret = kx022a_fifo_set_wmi(data);
 	if (ret)
-		return ret;
+		goto err_turn_on_out;
 
 	/* Enable buffer */
 	ret = regmap_set_bits(data->regmap, data->chip_info->buf_cntl2,
 			      KX022A_MASK_BUF_EN);
 	if (ret)
-		return ret;
+		goto err_turn_on_out;
 
 	data->state |= KX022A_STATE_FIFO;
 	ret = regmap_set_bits(data->regmap, data->ien_reg,
 			      KX022A_MASK_WMI);
 	if (ret)
-		return ret;
+		goto err_buf_disable_out;
 
-	return __kx022a_turn_on_off(data, true);
+	ret = __kx022a_turn_on_off(data, true);
+	if (ret)
+		goto err_wmi_clear_out;
+
+	return ret;
+
+err_wmi_clear_out:
+	regmap_clear_bits(data->regmap, data->ien_reg,
+			  KX022A_MASK_WMI);
+err_buf_disable_out:
+	regmap_clear_bits(data->regmap, data->chip_info->buf_cntl2,
+			  KX022A_MASK_BUF_EN);
+	data->state &= ~KX022A_STATE_FIFO;
+err_turn_on_out:
+	__kx022a_turn_on_off(data, true);
+err_free_out:
+	kfree(data->fifo_buffer);
+
+	return ret;
 }
 
 static int kx022a_buffer_postenable(struct iio_dev *idev)
author maintainer
#374 lore raw

PATCH 371/428 iio: accel: kxcjk-1013: reject duplicate event disable

In reply to Greg Kroah-Hartman

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

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

From: Jiale Yao <[email protected]>

commit dd30c10ff60d2b30386c23bb7c61cc97f2385c97 upstream.

The IIO core does not filter duplicate writes to the event enable attribute. kxcjk1013_write_event_config() already ignores repeated enable requests, but a repeated disable request still calls kxcjk1013_set_power_state(data, false), dropping a runtime PM reference that was not acquired for this request. This can underflow the runtime PM usage count and trigger a "Runtime PM usage count underflow" warning.

Return early when the requested state already matches ev_enable_state.

Fixes b4b491c0832e ("iio: accel: kxcjk-1013: Support thresholds")
---
 drivers/iio/accel/kxcjk-1013.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/accel/kxcjk-1013.c +1 -1
@@ -1030,7 +1030,7 @@ static int kxcjk1013_write_event_config(
 	struct kxcjk1013_data *data = iio_priv(indio_dev);
 	int ret;
 
-	if (state && data->ev_enable_state)
+	if (state == data->ev_enable_state)
 		return 0;
 
 	mutex_lock(&data->mutex);
author maintainer
#375 lore raw

PATCH 372/428 iio: accel: sca3000: fix frequency divider condition check

In reply to Greg Kroah-Hartman

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

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

From: Salah Triki <[email protected]>

commit d380bf5f94a892e546d9a94e8557ad866b043a2a upstream.

When setting the sampling frequency, the check for base_freq / 2 is followed by an independent if statement for base_freq / 4. If val equals base_freq / 2, the second check fails and falls through to the else if (val != base_freq) branch, returning -EINVAL erroneously.

Fix this by chaining the checks with else if.

Fixes e0f3fc9b47e6 ("iio: accel: sca3000_core: implemented IIO_CHAN_INFO_SAMP_FREQ")
---
 drivers/iio/accel/sca3000.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/accel/sca3000.c +1 -1
@@ -638,7 +638,7 @@ static int sca3000_write_raw_samp_freq(s
 
 	if (val == base_freq / 2)
 		ctrlval |= SCA3000_REG_OUT_CTRL_BUF_DIV_2;
-	if (val == base_freq / 4)
+	else if (val == base_freq / 4)
 		ctrlval |= SCA3000_REG_OUT_CTRL_BUF_DIV_4;
 	else if (val != base_freq)
 		return -EINVAL;
author maintainer
#376 lore raw

PATCH 373/428 iio: adc: ad4030: fix invalid oversampling_ratio validation

In reply to Greg Kroah-Hartman

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

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

From: Salah Triki <[email protected]>

commit d615210564205993e8f0e7ccb57cc2e66b7d5706 upstream.

In ad4030_set_avg_frame_len(), the logarithm is calculated before input validation. Passing zero or negative values leads to an undefined result from ilog2().

Validate that the input is strictly positive prior to computing its logarithm.

Fixes 949abd1ca5a4 ("iio: adc: ad4030: add averaging support")
Assisted-by LLM
---
 drivers/iio/adc/ad4030.c |   11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/iio/adc/ad4030.c +9 -2
@@ -746,14 +746,21 @@ static int ad4030_set_chan_calibbias(str
 static int ad4030_set_avg_frame_len(struct iio_dev *dev, int avg_val)
 {
 	struct ad4030_state *st = iio_priv(dev);
-	unsigned int avg_log2 = ilog2(avg_val);
 	unsigned int last_avg_idx = ARRAY_SIZE(ad4030_average_modes) - 1;
+	unsigned int avg_log2;
 	int freq_hz;
 	int ret;
 
-	if (avg_val < 0 || avg_val > ad4030_average_modes[last_avg_idx])
+	/* Reject unsupported modes */
+	if (avg_val > ad4030_average_modes[last_avg_idx])
 		return -EINVAL;
 
+	/* Avoid invalid values for logarithm since it's undefined */
+	if (avg_val < 1)
+		return -EINVAL;
+
+	avg_log2 = ilog2(avg_val);
+
 	if (st->offload_trigger) {
 		/*
 		 * The sample averaging and sampling frequency configurations
author maintainer
#377 lore raw

PATCH 374/428 iio: adc: ad7173: Fix digital filter configuration

In reply to Greg Kroah-Hartman

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

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

From: Marcelo Schmitt <[email protected]>

commit 9ec6ecc95dda6d91fa6f671cd972dd4bfc05ba38 upstream.

Filter enable and filter type selection masks were swapped on data preparation for filter configuration register write. Use the correct masks to set each property of post filter configuration.

Fixes ff06b39be1a1 ("iio: adc: ad7173: support changing filter type")
---
 drivers/iio/adc/ad7173.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/iio/adc/ad7173.c +2 -2
@@ -775,9 +775,9 @@ static int ad7173_load_config(struct ad7
 
 	return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2,
 			       FIELD_PREP(AD7173_FILTER_SINC3_MAP, 0) |
-			       FIELD_PREP(AD7173_FILTER_ENHFILT_MASK,
-					  post_filter_enable) |
 			       FIELD_PREP(AD7173_FILTER_ENHFILTEN,
+					  post_filter_enable) |
+			       FIELD_PREP(AD7173_FILTER_ENHFILT_MASK,
 					  post_filter_select) |
 			       FIELD_PREP(AD7173_FILTER_ORDER, 0) |
 			       FIELD_PREP(AD7173_FILTER_ODR_MASK,
author maintainer
#378 lore raw

PATCH 375/428 iio: adc: ad_sigma_delta: fix use-after-free on unbind

In reply to Greg Kroah-Hartman

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

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

From: Fan Wu <[email protected]>

commit 9ee8306121495d2a25aa5d1bfd519f2748786b83 upstream.

ad_sd_buffer_postenable() allocates sigma_delta->samples_buf with devm_krealloc() at runtime, so its devres entry sits after all probe-time entries of the driver. devm resources are released in reverse allocation order, which means unbind frees samples_buf before iio_device_unregister() disables the buffers and detaches the trigger pollfunc. The data ready IRQ is still enabled at that point, so ad_sd_trigger_handler() can still run and memcpy() incoming samples into the freed samples_buf.

Fix this by preallocating the buffer in devm_ad_sd_setup_buffer_and_trigger(), before the triggered buffer and the IRQ are set up, so it is freed only after iio_device_unregister() has drained the trigger handler via free_irq(). Size it for the worst case of all sequencer slots being active; ad_sd_validate_scan_mask() already caps the number of active channels at num_slots.

This issue was found by an in-house static analysis tool.

Fixes 8bea9af887de ("iio: adc: ad_sigma_delta: Add sequencer support")
---
 drivers/iio/adc/ad_sigma_delta.c |   31 ++++++++++++++++++-------------
 1 file changed, 18 insertions(+), 13 deletions(-)
drivers/iio/adc/ad_sigma_delta.c +18 -13
@@ -498,7 +498,6 @@ static int ad_sd_buffer_postenable(struc
 	const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type;
 	struct spi_transfer *xfer = sigma_delta->sample_xfer;
 	unsigned int i, slot, channel;
-	u8 *samples_buf;
 	int ret;
 
 	if (sigma_delta->num_slots == 1) {
@@ -530,7 +529,7 @@ static int ad_sd_buffer_postenable(struc
 		xfer[1].bits_per_word = scan_type->realbits;
 		xfer[1].len = spi_bpw_to_bytes(scan_type->realbits);
 	} else {
-		unsigned int samples_buf_size, scan_size;
+		unsigned int scan_size;
 
 		if (sigma_delta->active_slots > 1) {
 			ret = ad_sigma_delta_append_status(sigma_delta, true);
@@ -538,17 +537,6 @@ static int ad_sd_buffer_postenable(struc
 				return ret;
 		}
 
-		samples_buf_size =
-			ALIGN(slot * BITS_TO_BYTES(scan_type->storagebits),
-			      sizeof(s64));
-		samples_buf_size += sizeof(s64);
-		samples_buf = devm_krealloc(&sigma_delta->spi->dev,
-					    sigma_delta->samples_buf,
-					    samples_buf_size, GFP_KERNEL);
-		if (!samples_buf)
-			return -ENOMEM;
-
-		sigma_delta->samples_buf = samples_buf;
 		scan_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift);
 		/* For 24-bit data, there is an extra byte of padding. */
 		xfer[1].rx_buf = &sigma_delta->rx_buf[scan_size == 3 ? 1 : 0];
@@ -855,6 +843,23 @@ int devm_ad_sd_setup_buffer_and_trigger(
 
 		indio_dev->setup_ops = &ad_sd_buffer_setup_ops;
 	} else {
+		const struct iio_scan_type *scan_type =
+			&indio_dev->channels[0].scan_type;
+		unsigned int samples_buf_size;
+
+		/*
+		 * Worst-case size: all sequencer slots can be active, capped
+		 * at num_slots by ad_sd_validate_scan_mask().
+		 */
+		samples_buf_size =
+			ALIGN(sigma_delta->num_slots *
+			      BITS_TO_BYTES(scan_type->storagebits),
+			      sizeof(s64));
+		samples_buf_size += sizeof(s64);
+		sigma_delta->samples_buf = devm_kzalloc(dev, samples_buf_size, GFP_KERNEL);
+		if (!sigma_delta->samples_buf)
+			return -ENOMEM;
+
 		ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
 						      &iio_pollfunc_store_time,
 						      &ad_sd_trigger_handler,
author maintainer
#379 lore raw

PATCH 376/428 iio: adc: ade9000: fix NULL pointer dereference in clkout registration

In reply to Greg Kroah-Hartman

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

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

From: Linmao Li <[email protected]>

commit 49ee1c6a3ebda6b7de06b2961b0e09c1c3fe536a upstream.

ade9000_setup_clkout() passes NULL as the register address when registering a divider clock. During clock registration, the common clock framework calls clk_divider_recalc_rate(), which dereferences the address through readl(). As a result, probing an ADE9000 configured as a clock provider with an external input clock crashes.

CLKOUT passes CLKIN through without changing its rate. Register it as a 1:1 fixed-factor clock, which does not require register access.

This change does not affect the configuration using the internal clock, for which the driver does not register a clock provider.

Fixes 81de7b4619fc ("iio: adc: add ade9000 support")
---
 drivers/iio/adc/ade9000.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/iio/adc/ade9000.c +3 -2
@@ -1647,8 +1647,9 @@ static int ade9000_setup_clkout(struct d
 		return 0;
 
 	/* CLKOUT passes through CLKIN with divider of 1 */
-	clkout_hw = devm_clk_hw_register_divider(dev, "clkout", __clk_get_name(st->clkin),
-						 CLK_SET_RATE_PARENT, NULL, 0, 1, 0, NULL);
+	clkout_hw = devm_clk_hw_register_fixed_factor(dev, "clkout",
+						      __clk_get_name(st->clkin),
+						      CLK_SET_RATE_PARENT, 1, 1);
 	if (IS_ERR(clkout_hw))
 		return dev_err_probe(dev, PTR_ERR(clkout_hw), "Failed to register clkout");
 
author maintainer
#380 lore raw

PATCH 377/428 iio: adc: ade9000: fix overlapping scan_index for current and voltage channels

In reply to Greg Kroah-Hartman

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

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

From: Antoniu Miclaus <[email protected]>

commit d01f63f673e18f3a99ce8d6fbf1a5f58b70990f7 upstream.

Current channels used scan_index "num" and voltage "num + 1", so with phases 0, 1 and 2 the indices overlapped and no longer matched the IA=0, VA=1, IB=2, VB=3, IC=4, VC=5 layout expected by ade9000_waveform_buffer_config(). Phase B and C buffers were configured incorrectly.

Use "num * 2" for current and "num * 2 + 1" for voltage to get the non-overlapping 0/1, 2/3, 4/5 layout.

Fixes 81de7b4619fc ("iio: adc: add ade9000 support")
---
 drivers/iio/adc/ade9000.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/iio/adc/ade9000.c +2 -2
@@ -444,7 +444,7 @@ static const struct iio_chan_spec_ext_in
 			      BIT(IIO_CHAN_INFO_CALIBSCALE),		\
 	.event_spec = ade9000_current_events,				\
 	.num_event_specs = ARRAY_SIZE(ade9000_current_events),		\
-	.scan_index = num,						\
+	.scan_index = num * 2,						\
 	.indexed = 1,							\
 	.scan_type = {							\
 		.sign = 's',						\
@@ -464,7 +464,7 @@ static const struct iio_chan_spec_ext_in
 			      BIT(IIO_CHAN_INFO_FREQUENCY),		\
 	.event_spec = ade9000_voltage_events,				\
 	.num_event_specs = ARRAY_SIZE(ade9000_voltage_events),		\
-	.scan_index = num + 1,	/* interleave with current channels */	\
+	.scan_index = num * 2 + 1, /* interleave with current channels */	\
 	.indexed = 1,							\
 	.scan_type = {							\
 		.sign = 's',						\
author maintainer
#381 lore raw

PATCH 378/428 iio: adc: ade9000: handle Phase C dip events in IRQ1 handler

In reply to Greg Kroah-Hartman

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

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

From: Antoniu Miclaus <[email protected]>

commit fafefa925ed7ec10383d9bf0378abf878b7f13cb upstream.

ADE9000_ST1_CROSSING_DEPTH is the exclusive upper bound for scanning the STATUS1 event bits, so a value of 25 left out the highest event bit, ADE9000_ST1_DIPC_BIT (bit 25). Phase C dip events were never reported.

Bump the value to 26 so bit 25 is scanned.

Fixes 81de7b4619fc ("iio: adc: add ade9000 support")
---
 drivers/iio/adc/ade9000.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/adc/ade9000.c +1 -1
@@ -219,7 +219,7 @@
 #define ADE9000_ST1_ERROR2_BIT		BIT(30)
 #define ADE9000_ST1_ERROR3_BIT		BIT(31)
 #define ADE9000_ST1_CROSSING_FIRST	6
-#define ADE9000_ST1_CROSSING_DEPTH	25
+#define ADE9000_ST1_CROSSING_DEPTH	26
 
 #define ADE9000_WFB_TRG_DIP_BIT		BIT(0)
 #define ADE9000_WFB_TRG_SWELL_BIT	BIT(1)
author maintainer
#382 lore raw

PATCH 379/428 iio: adc: ade9000: request interrupts after powering the device

In reply to Greg Kroah-Hartman

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

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

From: Antoniu Miclaus <[email protected]>

commit 88fda1ed51a579eb51980436abc6557006a520a3 upstream.

The IRQ handlers do SPI register accesses, but the interrupts were requested before the vdd regulator was enabled. An interrupt arriving while the chip is unpowered runs a handler against a dead chip, causing SPI errors or garbage reads.

Request the interrupts after enabling the regulator. This keeps irq1 registered before ade9000_reset(), which waits on it, and lets devm free the interrupts before the regulator is disabled.

Fixes 81de7b4619fc ("iio: adc: add ade9000 support")
---
 drivers/iio/adc/ade9000.c |   25 +++++++++++++------------
 1 file changed, 13 insertions(+), 12 deletions(-)
drivers/iio/adc/ade9000.c +13 -12
@@ -1706,18 +1706,6 @@ static int ade9000_probe(struct spi_devi
 	if (ret)
 		return ret;
 
-	ret = ade9000_request_irq(dev, "irq0", ade9000_irq0_thread, indio_dev);
-	if (ret)
-		return ret;
-
-	ret = ade9000_request_irq(dev, "irq1", ade9000_irq1_thread, indio_dev);
-	if (ret)
-		return ret;
-
-	ret = ade9000_request_irq(dev, "dready", ade9000_dready_thread, indio_dev);
-	if (ret)
-		return ret;
-
 	/* External CMOS clock input (optional - crystal can be used instead) */
 	st->clkin = devm_clk_get_optional_enabled(dev, NULL);
 	if (IS_ERR(st->clkin))
@@ -1737,6 +1725,19 @@ static int ade9000_probe(struct spi_devi
 		return dev_err_probe(&spi->dev, ret,
 				     "Failed to get and enable vdd regulator\n");
 
+	/* Request IRQs only after powering the chip; the handlers do SPI access. */
+	ret = ade9000_request_irq(dev, "irq0", ade9000_irq0_thread, indio_dev);
+	if (ret)
+		return ret;
+
+	ret = ade9000_request_irq(dev, "irq1", ade9000_irq1_thread, indio_dev);
+	if (ret)
+		return ret;
+
+	ret = ade9000_request_irq(dev, "dready", ade9000_dready_thread, indio_dev);
+	if (ret)
+		return ret;
+
 	indio_dev->channels = ade9000_channels;
 	indio_dev->num_channels = ARRAY_SIZE(ade9000_channels);
 
author maintainer
#383 lore raw

PATCH 380/428 iio: adc: adi-axi-adc: Initialize state mutex

In reply to Greg Kroah-Hartman

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

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

From: Janani Sunil <[email protected]>

commit 549b71ce050637dd32678371ad76c445a2f8c9aa upstream.

The AXI ADC register access paths serialize transactions with st->lock, but probe does not initialize it. Initialize the mutex before registering the backend.

Fixes 7ecb8ee5c93b ("iio: adc: adi-axi-adc: support digital interface calibration")
---
 drivers/iio/adc/adi-axi-adc.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/iio/adc/adi-axi-adc.c +4 -0
@@ -714,6 +714,10 @@ static int adi_axi_adc_probe(struct plat
 	if (!st)
 		return -ENOMEM;
 
+	ret = devm_mutex_init(dev, &st->lock);
+	if (ret)
+		return ret;
+
 	base = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(base))
 		return PTR_ERR(base);
author maintainer
#384 lore raw

PATCH 381/428 iio: adc: aspeed: propagate reset deassert errors

In reply to Greg Kroah-Hartman

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

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

From: Pengpeng Hou <[email protected]>

commit 95e4eb426b4bd666fa2f37886c0ddf74a0cc6167 upstream.

aspeed_adc_probe() continues to register ADC resources after deasserting the shared reset, even if the reset controller reports a failure.

A failed deassertion leaves the hardware unavailable, so stop probing before installing the cleanup action and enabling the ADC.

Fixes edf7550a1f93 ("iio: adc: aspeed: Deassert reset in probe")
---
 drivers/iio/adc/aspeed_adc.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/iio/adc/aspeed_adc.c +3 -1
@@ -585,7 +585,9 @@ static int aspeed_adc_probe(struct platf
 		return dev_err_probe(dev, PTR_ERR(data->rst),
 				     "invalid or missing reset controller device tree entry");
 
-	reset_control_deassert(data->rst);
+	ret = reset_control_deassert(data->rst);
+	if (ret)
+		return ret;
 
 	ret = devm_add_action_or_reset(dev, aspeed_adc_reset_assert, data->rst);
 	if (ret)
author maintainer
#385 lore raw

PATCH 382/428 iio: adc: axp288: Add TS bias override for Haier HV103H

In reply to Greg Kroah-Hartman

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

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

From: Vladyslav Ivashchenko <[email protected]>

commit 0f300db7b7315b2c7ea0e2d4437b88de162d191f upstream.

The Haier HV103H firmware configures the AXP288 TS pin bias current to 60 uA. This causes the battery temperature reading to cross the charging temperature threshold under load, incorrectly stopping battery charging.

Setting the TS bias current to 80 uA fixes the temperature measurement and prevents charging from being incorrectly disabled.

Add a DMI quirk to use the 80 uA TS bias current on the Haier HV103H.

Fixes 9bcf15f75cac ("iio: adc: axp288: Fix TS-pin handling")
Fixes 048058399f19 ("iio: adc: axp288: Override TS pin bias current for some models") # Add necessary infrastructure
---
 drivers/iio/adc/axp288_adc.c |    8 ++++++++
 1 file changed, 8 insertions(+)
drivers/iio/adc/axp288_adc.c +8 -0
@@ -207,6 +207,14 @@ static const struct dmi_system_id axp288
 		},
 		.driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA,
 	},
+	{
+		/* Haier HV103H */
+		.matches = {
+		  DMI_MATCH(DMI_SYS_VENDOR, "Haier"),
+		  DMI_MATCH(DMI_PRODUCT_NAME, "HV103H"),
+		},
+		.driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA,
+	},
 	{ }
 };
 
author maintainer
#386 lore raw

PATCH 383/428 iio: adc: max1363: sign-extend bipolar differential channel reads

In reply to Greg Kroah-Hartman

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

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

From: Cong Nguyen <[email protected]>

commit 91908c0a78baddc8572aa3ab69e62ee2eb496ba4 upstream.

MAX1363 differential channels are bipolar, but max1363_read_single_chan() masks the raw value to the ADC resolution without sign-extending it. Negative differential readings are therefore reported to userspace as large positive values (e.g. -1 as 4095 on a 12-bit part).

Sign-extend the masked value from the resolution bit for differential channels. Single-ended channels are unipolar and are left unchanged.

Fixes d1325cf45077 ("Staging: IIO: max1363 ADC driver")
Assisted-by Claude:claude-opus-4
---
 drivers/iio/adc/max1363.c |    8 ++++++++
 1 file changed, 8 insertions(+)
drivers/iio/adc/max1363.c +8 -0
@@ -404,6 +404,14 @@ static int max1363_read_single_chan(stru
 
 		data = rxbuf[0];
 	}
+
+	/*
+	 * Differential channels are bipolar and the device returns the sample
+	 * in two's complement, so sign-extend it from the resolution bit.
+	 */
+	if (chan->differential)
+		data = sign_extend32(data, st->chip_info->bits - 1);
+
 	*val = data;
 
 	return 0;
author maintainer
#387 lore raw

PATCH 384/428 iio: adc: pac1934: check ACPI label duplication

In reply to Greg Kroah-Hartman

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

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

From: Slavin Liu <[email protected]>

commit 64c82e7bd9c82b0c9276297df34a3579e8658f55 upstream.

A label duplication failure currently reaches the terminating-byte write. Release the ACPI result and abort before accessing the missing label; earlier labels are managed by devres.

Detected by static analysis and reviewed with AI-assisted source auditing.

Fixes 0fb528c8255b ("iio: adc: adding support for PAC193x")
Assisted-by LLM
---
 drivers/iio/adc/pac1934.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/iio/adc/pac1934.c +4 -0
@@ -1108,6 +1108,10 @@ static int pac1934_acpi_parse_channel_co
 			devm_kmemdup(dev, rez->package.elements[i].string.pointer,
 				     (size_t)rez->package.elements[i].string.length + 1,
 				     GFP_KERNEL);
+		if (!info->labels[idx]) {
+			ACPI_FREE(rez);
+			return -ENOMEM;
+		}
 		info->labels[idx][rez->package.elements[i].string.length] = '\0';
 		info->shunts[idx] = rez->package.elements[i + 1].integer.value * 1000;
 		info->active_channels[idx] = (info->shunts[idx] != 0);
author maintainer
#388 lore raw

PATCH 385/428 iio: adc: rohm-bd79124: Catch regmap errors at measurement start/stop

In reply to Greg Kroah-Hartman

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

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

From: Matti Vaittinen <[email protected]>

commit 196cc715dce3576a54065f5cb07185c75778f9a8 upstream.

The bd79124_start_measurement() and bd79124_stop_measurement() ignore errors from the regmap reads, causing potential use of uninitialized stack variable when deciding whether the measurement is already started/stopped. The bd79124_stop_measurement() may also ignore failure to clear the sequencer state bits, which may make the hardware to ignore the setting and leave hardware and driver states out of sync.

Check the return value and bail-out if error is detected.

Fixes 3f57a3b9ab74 ("iio: adc: Support ROHM BD79124 ADC")
---
 drivers/iio/adc/rohm-bd79124.c |    8 ++++++++
 1 file changed, 8 insertions(+)
drivers/iio/adc/rohm-bd79124.c +8 -0
@@ -381,6 +381,9 @@ static int bd79124_start_measurement(str
 
 	/* See if already started */
 	ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, &val);
+	if (ret)
+		return ret;
+
 	if (val & BIT(chan))
 		return 0;
 
@@ -420,11 +423,16 @@ static int bd79124_stop_measurement(stru
 
 	/* See if already stopped */
 	ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, &enabled_chans);
+	if (ret)
+		return ret;
+
 	if (!(enabled_chans & BIT(chan)))
 		return 0;
 
 	ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG,
 				BD79124_MSK_SEQ_START);
+	if (ret)
+		return ret;
 
 	/* Clear the channel from the measured channels */
 	enabled_chans &= ~BIT(chan);
author maintainer
#389 lore raw

PATCH 386/428 iio: adc: rohm-bd79124: Fix channel initialization

In reply to Greg Kroah-Hartman

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

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

From: Matti Vaittinen <[email protected]>

commit edd56c01d174153bf90160279742ff6f3ad27a93 upstream.

The alarm limit register on BD79124 span to two consecutive registers. The first register for high-limit containing also hysteresis configuration. The initialization in driver writes only one 8-bit register, overwriting the hysteresis and leaving the other limit register uninitialized.

Fix the limit initialization by using the bd79124_write_int_to_reg(), which correctly initializes the limit on both of the registers.

Fixes 3f57a3b9ab74 ("iio: adc: Support ROHM BD79124 ADC")
---
 drivers/iio/adc/rohm-bd79124.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/iio/adc/rohm-bd79124.c +4 -4
@@ -927,13 +927,13 @@ static int bd79124_chan_init(struct bd79
 {
 	int ret;
 
-	ret = regmap_write(data->map, BD79124_GET_HIGH_LIMIT_REG(channel),
-			   BD79124_HIGH_LIMIT_MAX);
+	ret = bd79124_write_int_to_reg(data, BD79124_GET_HIGH_LIMIT_REG(channel),
+				       BD79124_HIGH_LIMIT_MAX);
 	if (ret)
 		return ret;
 
-	return regmap_write(data->map, BD79124_GET_LOW_LIMIT_REG(channel),
-			    BD79124_LOW_LIMIT_MIN);
+	return bd79124_write_int_to_reg(data, BD79124_GET_LOW_LIMIT_REG(channel),
+					BD79124_LOW_LIMIT_MIN);
 }
 
 static int bd79124_get_gpio_pins(const struct iio_chan_spec *cs, int num_channels)
author maintainer
#390 lore raw

PATCH 387/428 iio: adc: rohm-bd79124: Fix GPIO mask check

In reply to Greg Kroah-Hartman

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

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

From: Matti Vaittinen <[email protected]>

commit d081c116a16489f7808d35fcf1ee9d4b9869d204 upstream.

The ROHM BD79124 has pins which can be configured as GPOs or as ADC inputs. The bd79124gpo_set_multiple() is intended to ensure that a pin which is requested to be toggled, is indeed configured as a GPO.

The check uses XOR, causing it to fail if mask is not exactly same as GPO configuration. Eg, if not all GPOs are asked to be toggled at once.

Fix this by checking that mask does not contain pins that are not configured as GPO, allowing some of the pins which are configured as GPO to be untouched.

Fixes 3f57a3b9ab74 ("iio: adc: Support ROHM BD79124 ADC")
---
 drivers/iio/adc/rohm-bd79124.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/adc/rohm-bd79124.c +1 -1
@@ -200,7 +200,7 @@ static int bd79124gpo_set_multiple(struc
 	if (ret)
 		return ret;
 
-	if (all_gpos ^ *mask) {
+	if (*mask & ~all_gpos) {
 		dev_dbg(data->dev, "Invalid mux config. Can't set value.\n");
 
 		return -EINVAL;
author maintainer
#391 lore raw

PATCH 388/428 iio: adc: rohm-bd79124: Fix rising alarm

In reply to Greg Kroah-Hartman

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

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

From: Matti Vaittinen <[email protected]>

commit 0d3f6b5e639f0bca5c1466ec523a94ec4f9f4c24 upstream.

The rising alarm is using the cached falling alarm value causing wrong value to be used.

Fix this by using the correct cached value for rising alarm.

Fixes 3f57a3b9ab74 ("iio: adc: Support ROHM BD79124 ADC")
---
 drivers/iio/adc/rohm-bd79124.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/adc/rohm-bd79124.c +1 -1
@@ -532,7 +532,7 @@ static int bd79124_enable_event(struct b
 		return ret;
 
 	if (dir == IIO_EV_DIR_RISING) {
-		limit = &data->alarm_f_limit[channel];
+		limit = &data->alarm_r_limit[channel];
 		reg = BD79124_GET_HIGH_LIMIT_REG(channel);
 	} else {
 		limit = &data->alarm_f_limit[channel];
author maintainer
#392 lore raw

PATCH 389/428 iio: adc: stm32-adc: fix check on internal channel availability

In reply to Greg Kroah-Hartman

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

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

From: Fabrice Gasnier <[email protected]>

commit 9db3deeb2de485a9d412f0825f9ab4c8e2bccf44 upstream.

If an unsupported internal channel like vddgpu is requested, the driver prints a warning but falls through and assigns it a valid int_ch below.

This causes a problem later during setup:

stm32_adc_int_ch_enable() {
    ...
    case STM32_ADC_INT_CH_VDDGPU:
        stm32_adc_set_bits(adc, adc->cfg->regs->or_vddgpu.reg,
                           adc->cfg->regs->or_vddgpu.mask);
    ...
}

Because the register offset is uninitialized (0), this performs a read-modify-write on offset 0, which corresponds to the ISR register.

Fix this by returning before a valid int_ch is assigned. Choice is made to keep current driver behavior to warn about the channel name.

---
 drivers/iio/adc/stm32-adc.c |   34 +++++++++++++++++++---------------
 1 file changed, 19 insertions(+), 15 deletions(-)
drivers/iio/adc/stm32-adc.c +19 -15
@@ -2263,33 +2263,37 @@ static int stm32_adc_populate_int_ch(str
 
 	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
 		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
+			bool na;
+
 			/* Check internal channel availability */
 			switch (i) {
 			case STM32_ADC_INT_CH_VDDCORE:
-				if (!adc->cfg->regs->or_vddcore.reg)
-					dev_warn(&indio_dev->dev,
-						 "%s channel not available\n", ch_name);
+				na = !adc->cfg->regs->or_vddcore.reg;
 				break;
 			case STM32_ADC_INT_CH_VDDCPU:
-				if (!adc->cfg->regs->or_vddcpu.reg)
-					dev_warn(&indio_dev->dev,
-						 "%s channel not available\n", ch_name);
+				na = !adc->cfg->regs->or_vddcpu.reg;
 				break;
 			case STM32_ADC_INT_CH_VDDQ_DDR:
-				if (!adc->cfg->regs->or_vddq_ddr.reg)
-					dev_warn(&indio_dev->dev,
-						 "%s channel not available\n", ch_name);
+				na = !adc->cfg->regs->or_vddq_ddr.reg;
 				break;
 			case STM32_ADC_INT_CH_VREFINT:
-				if (!adc->cfg->regs->ccr_vref.reg)
-					dev_warn(&indio_dev->dev,
-						 "%s channel not available\n", ch_name);
+				na = !adc->cfg->regs->ccr_vref.reg;
 				break;
 			case STM32_ADC_INT_CH_VBAT:
-				if (!adc->cfg->regs->ccr_vbat.reg)
-					dev_warn(&indio_dev->dev,
-						 "%s channel not available\n", ch_name);
+				na = !adc->cfg->regs->ccr_vbat.reg;
 				break;
+			default:
+				return -EINVAL;
+			}
+
+			if (na) {
+				/*
+				 * Channel label matches an internal STM32 ADC channel.
+				 * Warn about it, as there's normally no restriction on the
+				 * name but that's not among available internal channels.
+				 */
+				dev_warn(&indio_dev->dev, "no %s internal channel\n", ch_name);
+				return 0;
 			}
 
 			if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT) {
author maintainer
#393 lore raw

PATCH 390/428 iio: adc: stm32-adc: fix possible division by zero in processed channel

In reply to Greg Kroah-Hartman

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

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

From: Fabrice Gasnier <[email protected]>

commit c5de6a6f855fdcf1e0ea549fb227a316db51c405 upstream.

In case the conversion has failed or returned zero, processing *val can lead to a division by zero. Need to check for errors, or converted value is zero, before processing the data. In case the converted value is zero, e.g. the Vrefint channel, this should be considered as invalid in all cases.

---
 drivers/iio/adc/stm32-adc.c |    9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/iio/adc/stm32-adc.c +7 -2
@@ -1608,11 +1608,16 @@ static int stm32_adc_read_raw(struct iio
 			ret = stm32_adc_single_conv(indio_dev, chan, val);
 		else
 			ret = -EINVAL;
+		iio_device_release_direct(indio_dev);
+		if (ret < 0)
+			return ret;
 
-		if (mask == IIO_CHAN_INFO_PROCESSED)
+		if (mask == IIO_CHAN_INFO_PROCESSED) {
+			if (*val == 0)
+				return -EINVAL;
 			*val = STM32_ADC_VREFINT_VOLTAGE * adc->vrefint.vrefint_cal / *val;
+		}
 
-		iio_device_release_direct(indio_dev);
 		return ret;
 
 	case IIO_CHAN_INFO_SCALE:
author maintainer
#394 lore raw

PATCH 391/428 iio: adc: sun4i-gpadc-iio: clean up on thermal zone registration failure

In reply to Greg Kroah-Hartman

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

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

From: Felix Gu <[email protected]>

commit 2cdc7ef31827db4fbc6283750420639569e1f0e9 upstream.

If devm_thermal_of_zone_register() fails, probe returns without unregistering the IIO map array or disabling runtime PM.

Jump to err_map to release them.

Fixes b0a242894f11 ("iio: adc: sun4i-gpadc-iio: register in the thermal after registering in pm")
---
 drivers/iio/adc/sun4i-gpadc-iio.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/iio/adc/sun4i-gpadc-iio.c +4 -4
@@ -638,10 +638,10 @@ static int sun4i_gpadc_probe(struct plat
 		 * thermal because no thermal DT node is found.
 		 */
 		if (IS_ERR(info->tzd) && PTR_ERR(info->tzd) != -ENODEV) {
-			dev_err(&pdev->dev,
-				"could not register thermal sensor: %ld\n",
-				PTR_ERR(info->tzd));
-			return PTR_ERR(info->tzd);
+			ret = dev_err_probe(&pdev->dev,
+					    PTR_ERR(info->tzd),
+					    "could not register thermal sensor\n");
+			goto err_map;
 		}
 	}
 
author maintainer
#395 lore raw

PATCH 392/428 iio: adc: sun4i-gpadc-iio: drop underflowing pm_runtime_put() calls

In reply to Greg Kroah-Hartman

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

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

From: Felix Gu <[email protected]>

commit 8b958dd214ad0c003f56549e9e039cb8067dc1a2 upstream.

Neither the error path in sun4i_gpadc_probe() nor sun4i_gpadc_remove() ever holds a runtime PM usage count. So the pm_runtime_put() in both places always triggers the "Runtime PM usage count underflow!" warning on every failed probe and every unbind.

Drop both calls.

Fixes d1caa9905538 ("iio: adc: add support for Allwinner SoCs ADC")
---
 drivers/iio/adc/sun4i-gpadc-iio.c |    2 --
 1 file changed, 2 deletions(-)
drivers/iio/adc/sun4i-gpadc-iio.c +0 -2
@@ -657,7 +657,6 @@ err_map:
 	if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
 		iio_map_array_unregister(indio_dev);
 
-	pm_runtime_put(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
 
 	return ret;
@@ -668,7 +667,6 @@ static void sun4i_gpadc_remove(struct pl
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
 
-	pm_runtime_put(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
 
 	if (!IS_ENABLED(CONFIG_THERMAL_OF))
author maintainer
#396 lore raw

PATCH 393/428 iio: adc: xilinx-xadc: free IRQ before cancelling the unmask worker on unbind

In reply to Greg Kroah-Hartman

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

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

From: Fan Wu <[email protected]>

commit 0f3f427076240e75ff265e61645627a71ca4918c upstream.

The ZYNQ XADC alarm IRQ handler queues zynq_unmask_work via schedule_delayed_work(); that work is cleared by a devm callback. Register the devm callback before devm_request_irq() so the devres LIFO teardown frees the IRQ first, ensuring the handler can no longer queue work by the time the workqueue is cleared. Otherwise the handler could re-arm the work and run it after the xadc structure has been freed.

This issue was found by an in-house static analysis tool.

Fixes 2a9685d1a3b7 ("iio: adc: xilinx: use more devres helpers and remove remove()")
Assisted-by Codex:gpt-5.6
---
 drivers/iio/adc/xilinx-xadc-core.c |    9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/iio/adc/xilinx-xadc-core.c +5 -4
@@ -1401,13 +1401,14 @@ static int xadc_probe(struct platform_de
 	}
 
 	if (irq > 0) {
-		ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler,
-				       0, dev_name(dev), indio_dev);
+		/* Clear out any work queued by the interrupt */
+		ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work,
+					       &xadc->zynq_unmask_work);
 		if (ret)
 			return ret;
 
-		ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work,
-					       &xadc->zynq_unmask_work);
+		ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler,
+				       0, dev_name(dev), indio_dev);
 		if (ret)
 			return ret;
 	}
author maintainer
#397 lore raw

PATCH 394/428 iio: admv1013: initialize callback mutex before registering notifier

In reply to Greg Kroah-Hartman

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

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

From: Runyu Xiao <[email protected]>

commit baf7e43b7fb5afb777353bf265ddf9d909f72b9f upstream.

admv1013_probe() registers a clock notifier whose callback takes st->lock on POST_RATE_CHANGE. Initialize the mutex before devm_clk_notifier_register() so the callback cannot observe an uninitialized lock during probe.

Use devm_mutex_init() so the lock lifetime is tied to the device and cleanup stays paired with the rest of the managed probe resources.

Fixes da35a7b526d9 ("iio: frequency: admv1013: add support for ADMV1013")
---
 drivers/iio/frequency/admv1013.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/iio/frequency/admv1013.c +5 -2
@@ -601,11 +601,14 @@ static int admv1013_probe(struct spi_dev
 				     "failed to get the LO input clock\n");
 
 	st->nb.notifier_call = admv1013_freq_change;
-	ret = devm_clk_notifier_register(dev, st->clkin, &st->nb);
+
+	ret = devm_mutex_init(dev, &st->lock);
 	if (ret)
 		return ret;
 
-	mutex_init(&st->lock);
+	ret = devm_clk_notifier_register(dev, st->clkin, &st->nb);
+	if (ret)
+		return ret;
 
 	ret = admv1013_init(st, vcm_uv);
 	if (ret)
author maintainer
#398 lore raw

PATCH 395/428 iio: buffer: serialize buffer teardown with mode claims

In reply to Greg Kroah-Hartman

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

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

From: Jinseob Kim <[email protected]>

commit 84bb0fc46ccf6a545b8fdf57cda83770186ed8dd upstream.

Buffer-mode claims hold mlock to guarantee that the device remains in buffer mode until the claim is released. Normal buffer updates take info_exist_lock followed by mlock in iio_update_buffers().

However, iio_device_unregister() disables and deactivates all buffers without taking mlock. This can invalidate buffer state, including active_scan_mask, while a buffer-mode claim is held.

Take mlock in iio_disable_all_buffers() so that unregister honors the mode-claim lifetime guarantee. The info_exist_lock -> mlock ordering matches iio_update_buffers().

Fixes 0a8565425afd ("iio: core: introduce iio_device_{claim|release}_buffer_mode() APIs")
---
 drivers/iio/industrialio-buffer.c |    4 ++++
 1 file changed, 4 insertions(+)
drivers/iio/industrialio-buffer.c +4 -0
@@ -1377,6 +1377,10 @@ EXPORT_SYMBOL_GPL(iio_update_buffers);
 
 void iio_disable_all_buffers(struct iio_dev *indio_dev)
 {
+	struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
+
+	guard(mutex)(&iio_dev_opaque->mlock);
+
 	iio_disable_buffers(indio_dev);
 	iio_buffer_deactivate_all(indio_dev);
 }
author maintainer
#399 lore raw

PATCH 396/428 iio: buffer: Ensure bounce buffer used for unaligned case is zeroed.

In reply to Greg Kroah-Hartman

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

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

From: Jonathan Cameron <[email protected]>

commit 5330036bc68d23672bfcc16c60c54701ae123083 upstream.

iio_push_to_buffers_with_ts_unaligned() leaks uninitialized heap memory to userspace if the data passed in is not a multiple of 8 bytes and the timestamp is enabled. Specify __GFP_ZERO for the devm_krealloc() to ensure any extra space is cleared.

---
 drivers/iio/industrialio-buffer.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/iio/industrialio-buffer.c +2 -1
@@ -2466,7 +2466,8 @@ int iio_push_to_buffers_with_ts_unaligne
 
 		bb = devm_krealloc(&indio_dev->dev,
 				   iio_dev_opaque->bounce_buffer,
-				   indio_dev->scan_bytes, GFP_KERNEL);
+				   indio_dev->scan_bytes,
+				   GFP_KERNEL | __GFP_ZERO);
 		if (!bb)
 			return -ENOMEM;
 		iio_dev_opaque->bounce_buffer = bb;
author maintainer
#400 lore raw

PATCH 397/428 iio: cdc: ad7150: fix OF matching and publish module aliases

In reply to Greg Kroah-Hartman

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

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

From: Pengpeng Hou <[email protected]>

commit 52da294027f53e7084c18dd4b4f73354a40382f3 upstream.

The OF match entries use positional initializers, which initialize the name field rather than compatible. Consequently the advertised AD7150, AD7151 and AD7156 compatible strings do not describe OF compatible matches. The table is also not exported for module alias generation.

Use designated compatible initializers and publish the OF table. Keep the existing I2C ID table and its device-variant selection unchanged.

The issue was found by our static-analysis tool.

Fixes 89f2d5b080bc ("staging:iio:cdc:ad7150: Add of_match_table")
Assisted-by LLM
---
 drivers/iio/cdc/ad7150.c |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/iio/cdc/ad7150.c +5 -3
@@ -636,11 +636,13 @@ static const struct i2c_device_id ad7150
 MODULE_DEVICE_TABLE(i2c, ad7150_id);
 
 static const struct of_device_id ad7150_of_match[] = {
-	{ "adi,ad7150" },
-	{ "adi,ad7151" },
-	{ "adi,ad7156" },
+	{ .compatible = "adi,ad7150" },
+	{ .compatible = "adi,ad7151" },
+	{ .compatible = "adi,ad7156" },
 	{ }
 };
+MODULE_DEVICE_TABLE(of, ad7150_of_match);
+
 static struct i2c_driver ad7150_driver = {
 	.driver = {
 		.name = "ad7150",
author maintainer
#401 lore raw

PATCH 398/428 iio: frequency: adf4377: Fully initialize clk_init_data and clk_parent_data

In reply to Greg Kroah-Hartman

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

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

From: Geert Uytterhoeven <[email protected]>

commit e787fd23d5063c61582c3c27522240b6e6703fde upstream.

The clk_init_data structure contains several mutually-exclusive members for different methods to specify the possible parents of a clock, prompting drivers to initialize only the members they need. However, not initializing all members may cause subtle issues, which are only exposed when CONFIG_INIT_STACK_ALL_PATTERN or CONFIG_INIT_STACK_NONE is enabled.

adf4377_clk_register() fills in init.parent_data, and assumes that init.parent_names is NULL. However, the latter is uninitialized, and thus may cause a crash.

Similarly, adf4377_clk_register() fills in only parent_data.fw_name, leaving other members of the clk_parent_data structure uninitialized.

Make sure all members are fully initialized, to fix such bugs, and to avoid future breakage when converting drivers to a different method for specifying the parents.

Fixes 60e5448ddbec2dc2 ("iio: frequency: adf4377: add clk provider support")
---
 drivers/iio/frequency/adf4377.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/iio/frequency/adf4377.c +2 -2
@@ -997,10 +997,10 @@ static const struct clk_ops adf4377_clk_
 
 static int adf4377_clk_register(struct adf4377_state *st)
 {
+	struct clk_parent_data parent_data = { };
 	struct spi_device *spi = st->spi;
 	struct device *dev = &spi->dev;
-	struct clk_init_data init;
-	struct clk_parent_data parent_data;
+	struct clk_init_data init = { };
 	int ret;
 
 	if (!device_property_present(dev, "#clock-cells"))
author maintainer
#402 lore raw

PATCH 399/428 iio: frequency: admv1013: fix wrong channel field used in admv1013_read_raw()

In reply to Greg Kroah-Hartman

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

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

From: Salah Triki <[email protected]>

commit 2093236e886ad9446ec3285ba520e024dd1464e1 upstream.

admv1013_read_raw() switches on chan->channel instead of chan->channel2 when handling IIO_CHAN_INFO_CALIBBIAS. The channel field only ever holds 0 or 1 (see ADMV1013_CHAN_CALIB()), while the IIO_MOD_I / IIO_MOD_Q modifiers are stored in channel2. As a result, the switch always falls through to the default case and calibbias reads always fail with -EINVAL, even though the corresponding admv1013_write_raw() path correctly uses channel2 and works as expected.

Fix the read path to switch on chan->channel2, matching the write path and the actual channel_spec definition.

Fixes da35a7b526d9 ("iio: frequency: admv1013: add support for ADMV1013")
---
 drivers/iio/frequency/admv1013.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/frequency/admv1013.c +1 -1
@@ -195,7 +195,7 @@ static int admv1013_read_raw(struct iio_
 
 	switch (info) {
 	case IIO_CHAN_INFO_CALIBBIAS:
-		switch (chan->channel) {
+		switch (chan->channel2) {
 		case IIO_MOD_I:
 			addr = ADMV1013_REG_OFFSET_ADJUST_I;
 			break;
author maintainer
#403 lore raw

PATCH 400/428 iio: gyro: adis16136: fix unprotected debugfs reads

In reply to Greg Kroah-Hartman

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

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

From: Arka Mondal <[email protected]>

commit 2b498df75d6c29aa3e760127ae0f0003df598b53 upstream.

The serial_number file operations are open coded and never call debugfs_file_get(), which debugfs_create_file_unsafe() requires. debugfs_remove_recursive() therefore does not wait for a read in progress, and unbind frees the iio_dev underneath it.

Use debugfs_create_file() instead.

Fixes 9aaea09b4cbd ("gyro:adis16136: fix debugfs_simple_attr.cocci warnings")
---
 drivers/iio/gyro/adis16136.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/gyro/adis16136.c +1 -1
@@ -145,7 +145,7 @@ static int adis16136_debugfs_init(struct
 	struct adis16136 *adis16136 = iio_priv(indio_dev);
 	struct dentry *d = iio_get_debugfs_dentry(indio_dev);
 
-	debugfs_create_file_unsafe("serial_number", 0400,
+	debugfs_create_file("serial_number", 0400,
 		d, adis16136, &adis16136_serial_fops);
 	debugfs_create_file_unsafe("product_id", 0400,
 		d, adis16136, &adis16136_product_id_fops);
author maintainer
#404 lore raw

PATCH 401/428 iio: health: max30102: fix NULL dereference in interrupt handler

In reply to Greg Kroah-Hartman

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

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

From: Marco Chen <[email protected]>

commit f0ea4824deebf21dacfbe397c4b0a5fd3e745a8d upstream.

The interrupt is requested in max30102_probe() and stays enabled for the lifetime of the device, but indio_dev->active_scan_mask is only valid while a buffer is enabled. When an interrupt arrives while no buffer is enabled, the handler dereferences the NULL active_scan_mask:

  Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
  Call trace:
   __bitmap_weight+0x64/0x98 (P)
   max30102_interrupt_handler+0x48/0x160 [max30102]

Call max30102_fifo_count() at the top of the handler and return early unless it reports a FIFO sample is ready. Because FIFO_RDY is the only interrupt source enabled in max30102_chip_init(), an invocation of max30102_interrupt_handler() without the FIFO_RDY interrupt status bit set carries no data to read and can return before touching active_scan_mask. A negative return from max30102_fifo_count() indicates a failed interrupt status read and is treated the same way.

Fixes 90579b69e94b ("iio: health: max30102: Add MAX30105 support")
---
 drivers/iio/health/max30102.c |   12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)
drivers/iio/health/max30102.c +9 -3
@@ -290,9 +290,15 @@ static irqreturn_t max30102_interrupt_ha
 {
 	struct iio_dev *indio_dev = private;
 	struct max30102_data *data = iio_priv(indio_dev);
-	unsigned int measurements = bitmap_weight(indio_dev->active_scan_mask,
-						  iio_get_masklength(indio_dev));
-	int ret, cnt = 0;
+	unsigned int measurements;
+	int ret, cnt;
+
+	cnt = max30102_fifo_count(data);
+	if (cnt <= 0)
+		return IRQ_HANDLED;
+
+	measurements = bitmap_weight(indio_dev->active_scan_mask,
+				     iio_get_masklength(indio_dev));
 
 	mutex_lock(&data->lock);
 
author maintainer
#405 lore raw

PATCH 402/428 iio: imu: adis16400: fix unprotected debugfs reads

In reply to Greg Kroah-Hartman

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

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

From: Arka Mondal <[email protected]>

commit d94787287a113a1326211647d5e0deed1e9de105 upstream.

adis16400_serial_number_fops is open coded and never calls debugfs_file_get(), so debugfs_remove_recursive() does not wait for a read in progress and unbind frees the iio_dev underneath it.

Commit ae1d37a9bb4b ("iio: imu: adis16400: use DEFINE_DEBUGFS_ATTRIBUTE instead of DEFINE_SIMPLE_ATTRIBUTE") converted product_id and flash_count to DEFINE_DEBUGFS_ATTRIBUTE, which protects itself, but moved all three files to debugfs_create_file_unsafe().

serial_number prints three registers as "%.4x-%.4x-%.4x" and DEFINE_DEBUGFS_ATTRIBUTE() formats a single u64, so it cannot be converted without changing what the file returns. adis16475 and adis16550 already use debugfs_create_file() for their open coded files. Use it here too.

Fixes ae1d37a9bb4b ("iio: imu: adis16400: use DEFINE_DEBUGFS_ATTRIBUTE instead of DEFINE_SIMPLE_ATTRIBUTE")
---
 drivers/iio/imu/adis16400.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/imu/adis16400.c +1 -1
@@ -280,7 +280,7 @@ static void adis16400_debugfs_init(struc
 		return;
 
 	if (st->variant->flags & ADIS16400_HAS_SERIAL_NUMBER)
-		debugfs_create_file_unsafe("serial_number", 0400,
+		debugfs_create_file("serial_number", 0400,
 				d, st, &adis16400_serial_number_fops);
 	if (st->variant->flags & ADIS16400_HAS_PROD_ID)
 		debugfs_create_file_unsafe("product_id", 0400,
author maintainer
#406 lore raw

PATCH 403/428 iio: imu: adis16480: fix unprotected debugfs reads

In reply to Greg Kroah-Hartman

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

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

From: Arka Mondal <[email protected]>

commit e1dea54496f90a62318cf4eeaa1e9ef8316e92b6 upstream.

The firmware_revision and firmware_date file operations are open coded and never call debugfs_file_get(), which debugfs_create_file_unsafe() requires. debugfs_remove_recursive() therefore does not wait for a read in progress, and unbind frees the iio_dev underneath it.

Use debugfs_create_file() instead.

Fixes 9bf94f836e32 ("imu:adis16480: fix debugfs_simple_attr.cocci warnings")
---
 drivers/iio/imu/adis16480.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/iio/imu/adis16480.c +2 -2
@@ -309,9 +309,9 @@ static void adis16480_debugfs_init(struc
 	if (!IS_ENABLED(CONFIG_DEBUG_FS))
 		return;
 
-	debugfs_create_file_unsafe("firmware_revision", 0400,
+	debugfs_create_file("firmware_revision", 0400,
 		d, adis16480, &adis16480_firmware_revision_fops);
-	debugfs_create_file_unsafe("firmware_date", 0400,
+	debugfs_create_file("firmware_date", 0400,
 		d, adis16480, &adis16480_firmware_date_fops);
 	debugfs_create_file_unsafe("serial_number", 0400,
 		d, adis16480, &adis16480_serial_number_fops);
author maintainer
#407 lore raw

PATCH 404/428 iio: light: gp2ap020a00f: drain irq_work after free_irq

In reply to Greg Kroah-Hartman

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

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

From: Fan Wu <[email protected]>

commit 63213927b141085b6c8719b8037dab3074b7e0fe upstream.

The threaded IRQ handler queues data->work through irq_work_queue() so the trigger is polled from a per-CPU context. free_irq() does not flush an irq_work the handler already queued, so after gp2ap020a00f_remove() returns that work may still run and call iio_trigger_poll() on data->trig, which the devm cleanup has already freed, causing a use-after-free.

Add irq_work_sync(&data->work) after free_irq() in remove() and in the probe error path, mirroring commit 78601726d4a5 ("iio: trigger: sysfs:
fix use-after-free on remove").

Found by an in-house static analysis tool, confirmed by manual review.

Fixes bf29fbeaa13d ("iio: gp2ap020a00f: Add a driver for the device")
Assisted-by Codex:gpt-5.6
---
 drivers/iio/light/gp2ap020a00f.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/iio/light/gp2ap020a00f.c +2 -0
@@ -1497,6 +1497,7 @@ error_trigger_unregister:
 	iio_trigger_unregister(data->trig);
 error_free_irq:
 	free_irq(client->irq, indio_dev);
+	irq_work_sync(&data->work);
 error_uninit_buffer:
 	iio_triggered_buffer_cleanup(indio_dev);
 error_regulator_disable:
@@ -1519,6 +1520,7 @@ static void gp2ap020a00f_remove(struct i
 	iio_device_unregister(indio_dev);
 	iio_trigger_unregister(data->trig);
 	free_irq(client->irq, indio_dev);
+	irq_work_sync(&data->work);
 	iio_triggered_buffer_cleanup(indio_dev);
 	regulator_disable(data->vled_reg);
 }
author maintainer
#408 lore raw

PATCH 405/428 iio: light: rohm-bu27034: Fix infinite delay on error

In reply to Greg Kroah-Hartman

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

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

From: Matti Vaittinen <[email protected]>

commit 8065a6c02f629187131f3136bedb26f9080e3cca upstream.

When reading an integration-time fails, the code will use error code to compute the sleep time.

Fix this by using the smallest integration time as a default if reading fails.

Fixes e52afbd61039 ("iio: light: ROHM BU27034 Ambient Light Sensor")
---
 drivers/iio/light/rohm-bu27034.c |   60 +++++++++++++++++++++++----------------
 1 file changed, 36 insertions(+), 24 deletions(-)
drivers/iio/light/rohm-bu27034.c +36 -24
@@ -137,6 +137,7 @@ static const struct iio_gain_sel_pair bu
 #define BU27034_MEAS_MODE_200MS		2
 #define BU27034_MEAS_MODE_400MS		4
 
+#define BU27034_INT_TIME_US_MIN (55 * USEC_PER_MSEC)
 static const struct iio_itime_sel_mul bu27034_itimes[] = {
 	GAIN_SCALE_ITIME_US(400000, BU27034_MEAS_MODE_400MS, 8),
 	GAIN_SCALE_ITIME_US(200000, BU27034_MEAS_MODE_200MS, 4),
@@ -296,7 +297,7 @@ static int bu27034_get_gain(struct bu270
 	return 0;
 }
 
-static int bu27034_get_int_time(struct bu27034_data *data)
+static int bu27034_get_int_time(struct bu27034_data *data, int *itime)
 {
 	int ret, sel;
 
@@ -304,24 +305,30 @@ static int bu27034_get_int_time(struct b
 	if (ret)
 		return ret;
 
-	return iio_gts_find_int_time_by_sel(&data->gts,
-					    sel & BU27034_MASK_MEAS_MODE);
+	ret = iio_gts_find_int_time_by_sel(&data->gts,
+					   sel & BU27034_MASK_MEAS_MODE);
+	if (ret < 0)
+		return ret;
+
+	*itime = ret;
+
+	return 0;
 }
 
 static int _bu27034_get_scale(struct bu27034_data *data, int channel, int *val,
 			      int *val2)
 {
-	int gain, ret;
+	int gain, itime, ret;
 
 	ret = bu27034_get_gain(data, channel, &gain);
 	if (ret)
 		return ret;
 
-	ret = bu27034_get_int_time(data);
-	if (ret < 0)
+	ret = bu27034_get_int_time(data, &itime);
+	if (ret)
 		return ret;
 
-	return iio_gts_get_scale(&data->gts, gain, ret, val, val2);
+	return iio_gts_get_scale(&data->gts, gain, itime, val, val2);
 }
 
 static int bu27034_get_scale(struct bu27034_data *data, int channel, int *val,
@@ -397,12 +404,10 @@ static int bu27034_try_set_int_time(stru
 	int ret, int_time_old, i;
 
 	guard(mutex)(&data->mutex);
-	ret = bu27034_get_int_time(data);
-	if (ret < 0)
+	ret = bu27034_get_int_time(data, &int_time_old);
+	if (ret)
 		return ret;
 
-	int_time_old = ret;
-
 	if (!iio_gts_valid_time(&data->gts, time_us)) {
 		dev_err(data->dev, "Unsupported integration time %u\n",
 			time_us);
@@ -841,7 +846,7 @@ static int bu27034_meas_set(struct bu270
 static int bu27034_get_single_result(struct bu27034_data *data, int chan,
 				     int *val)
 {
-	int ret;
+	int ret, itime;
 
 	if (chan < BU27034_CHAN_DATA0 || chan > BU27034_CHAN_DATA1)
 		return -EINVAL;
@@ -850,11 +855,11 @@ static int bu27034_get_single_result(str
 	if (ret)
 		return ret;
 
-	ret = bu27034_get_int_time(data);
-	if (ret < 0)
+	ret = bu27034_get_int_time(data, &itime);
+	if (ret)
 		return ret;
 
-	msleep(ret / 1000);
+	msleep(itime / 1000);
 
 	return bu27034_read_result(data, chan, val);
 }
@@ -904,12 +909,10 @@ static int bu27034_calc_mlux(struct bu27
 	if (ret)
 		return ret;
 
-	ret = bu27034_get_int_time(data);
-	if (ret < 0)
+	ret = bu27034_get_int_time(data, &meastime);
+	if (ret)
 		return ret;
 
-	meastime = ret;
-
 	d1_d0_ratio_scaled = (unsigned int)ch1 * (unsigned int)gain0 * 100;
 	helper64 = (u64)ch1 * (u64)gain0 * 100LLU;
 
@@ -970,9 +973,9 @@ static int bu27034_read_raw(struct iio_d
 	switch (mask) {
 	case IIO_CHAN_INFO_INT_TIME:
 		*val = 0;
-		*val2 = bu27034_get_int_time(data);
-		if (*val2 < 0)
-			return *val2;
+		ret = bu27034_get_int_time(data, val2);
+		if (ret)
+			return ret;
 
 		return IIO_VAL_INT_PLUS_MICRO;
 
@@ -1157,11 +1160,20 @@ static int bu27034_buffer_thread(void *a
 {
 	struct iio_dev *idev = arg;
 	struct bu27034_data *data;
-	int wait_ms;
+	int wait_ms, ret;
 
 	data = iio_priv(idev);
 
-	wait_ms = bu27034_get_int_time(data);
+	/*
+	 * If reading the integration time fails, default to the minimum so we
+	 * don't lose samples. This may waste CPU cycles, but as a hardening
+	 * against theoretical, once-in-a-blue-moon error, this should be Ok.
+	 */
+	wait_ms = BU27034_INT_TIME_US_MIN;
+	ret = bu27034_get_int_time(data, &wait_ms);
+	if (ret)
+		dev_warn(data->dev, "Failed to get integration time\n");
+
 	wait_ms /= 1000;
 
 	wait_ms -= BU27034_MEAS_WAIT_PREMATURE_MS;
author maintainer
#409 lore raw

PATCH 406/428 iio: pressure: bmp280: fix out-of-bounds access in sampling frequency lookup

In reply to Greg Kroah-Hartman

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

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

From: Hui Su <[email protected]>

commit 56c423b233b214182c58c99861899327b45b0d6c upstream.

The sampling frequency tables store each frequency as an integer part and a fractional part in micro units. num_sampling_freq_avail is initialized to the number of flattened integer elements because read_avail() returns the table as a flat array.

bmp280_write_sampling_frequency(), however, indexes the same table as a two-dimensional array and uses num_sampling_freq_avail as the number of rows. Convert the flattened element count back to the number of rows before iterating over the table.

Fixes 10b40ffba2f9 ("iio: pressure: bmp280: Add more tunable config parameters for BMP380")
---
 drivers/iio/pressure/bmp280-core.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/pressure/bmp280-core.c +1 -1
@@ -836,7 +836,7 @@ static int bmp280_write_sampling_frequen
 					   int val, int val2)
 {
 	const int (*avail)[2] = data->chip_info->sampling_freq_avail;
-	const int n = data->chip_info->num_sampling_freq_avail;
+	const int n = data->chip_info->num_sampling_freq_avail / 2;
 	int ret, prev;
 	int i;
 
author maintainer
#410 lore raw

PATCH 407/428 iio: pressure: rohm-bm1390: Return error when read fails

In reply to Greg Kroah-Hartman

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

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

From: Matti Vaittinen <[email protected]>

commit b0e951ffc5a28141a106562997ca136ec6a0e1a3 upstream.

The data reading function ignores the cached error value, and unconditionally returns 0. Return cached 'ret' -value after stopping the measurement so user knows if read failed and data is garbage.

Fixes 534674463a59 ("iio: bm1390: simplify using guard(mutex)")
---
 drivers/iio/pressure/rohm-bm1390.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/pressure/rohm-bm1390.c +1 -1
@@ -289,7 +289,7 @@ static int bm1390_read_data(struct bm139
 	if (warn)
 		dev_warn(data->dev, "Failed to stop measurement (%d)\n", warn);
 
-	return 0;
+	return ret;
 }
 
 static int bm1390_read_raw(struct iio_dev *idev,
author maintainer
#411 lore raw

PATCH 408/428 iio: proximity: aw96103: validate firmware data length

In reply to Greg Kroah-Hartman

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

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

From: Laxman Acharya Padhya <[email protected]>

commit 1a7c522a35477d70320bd64aba8e9de0c1058c6f upstream.

The firmware parser reads a length from the binary header and then uses it to walk six-byte register/value entries. A truncated firmware image can make the parser read beyond the copied firmware buffer. A value smaller than the four-byte count field also underflows, producing a very large length.

Read the little-endian field with get_unaligned_le32(), as previously proposed by David Lechner. Validate the header size, reject an underflowed length, ensure the resulting payload is contained in the firmware image, and require it to contain whole register/value entries.

---
 drivers/iio/proximity/aw96103.c |   30 ++++++++++++++++++++++++------
 1 file changed, 24 insertions(+), 6 deletions(-)
drivers/iio/proximity/aw96103.c +24 -6
@@ -24,6 +24,7 @@
 #define AW96103_BIN_VALID_DATA_OFFSET		64
 #define AW96103_BIN_DATA_LEN_OFFSET		16
 #define AW96103_BIN_DATA_REG_NUM_SIZE		4
+#define AW96103_BIN_REG_SIZE			6
 #define AW96103_BIN_CHIP_TYPE_SIZE		8
 #define AW96103_BIN_CHIP_TYPE_OFFSET		24
 
@@ -229,14 +230,27 @@ static const struct aw_chip_info aw_chip
 	},
 };
 
-static void aw96103_parsing_bin_file(struct aw_bin *bin)
+static int aw96103_parsing_bin_file(struct aw_bin *bin)
 {
+	u32 data_len;
+
+	if (bin->len < AW96103_BIN_VALID_DATA_OFFSET)
+		return -EINVAL;
+
+	data_len = get_unaligned_le32(bin->data + AW96103_BIN_DATA_LEN_OFFSET);
+	if (data_len < AW96103_BIN_DATA_REG_NUM_SIZE)
+		return -EINVAL;
+
+	bin->valid_data_len = data_len - AW96103_BIN_DATA_REG_NUM_SIZE;
+	if (bin->valid_data_len > bin->len - AW96103_BIN_VALID_DATA_OFFSET ||
+	    bin->valid_data_len % AW96103_BIN_REG_SIZE)
+		return -EINVAL;
+
 	bin->valid_data_addr = AW96103_BIN_VALID_DATA_OFFSET;
-	bin->valid_data_len =
-		*(unsigned int *)(bin->data + AW96103_BIN_DATA_LEN_OFFSET) -
-		AW96103_BIN_DATA_REG_NUM_SIZE;
 	memcpy(bin->chip_type, bin->data + AW96103_BIN_CHIP_TYPE_OFFSET,
 	       AW96103_BIN_CHIP_TYPE_SIZE);
+
+	return 0;
 }
 
 static const struct regmap_config aw96103_regmap_confg = {
@@ -500,7 +514,7 @@ static int aw96103_bin_valid_loaded(stru
 	int ret;
 
 	for (i = 0; i < aw_bin_data_s->valid_data_len;
-	     i += 6, start_addr += 6) {
+	     i += AW96103_BIN_REG_SIZE, start_addr += AW96103_BIN_REG_SIZE) {
 		reg_addr = get_unaligned_le16(aw_bin_data_s->data + start_addr);
 		reg_data = get_unaligned_le32(aw_bin_data_s->data +
 					      start_addr + 2);
@@ -550,6 +564,8 @@ static int aw96103_para_loaded(struct aw
 static int aw96103_cfg_all_loaded(const struct firmware *cont,
 				  struct aw96103 *aw96103)
 {
+	int ret;
+
 	if (!cont)
 		return -EINVAL;
 
@@ -561,7 +577,9 @@ static int aw96103_cfg_all_loaded(const
 	aw_bin->len = cont->size;
 	memcpy(aw_bin->data, cont->data, cont->size);
 	release_firmware(cont);
-	aw96103_parsing_bin_file(aw_bin);
+	ret = aw96103_parsing_bin_file(aw_bin);
+	if (ret)
+		return ret;
 
 	return aw96103_bin_valid_loaded(aw96103, aw_bin);
 }
author maintainer
#412 lore raw

PATCH 409/428 iio: proximity: isl29501: Fix return type of isl29501_register_write

In reply to Greg Kroah-Hartman

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

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

From: Salah Triki <[email protected]>

commit e8dd273d199f5bbe295a8e3d8b777c5505b6b2e1 upstream.

isl29501_register_write() was returning a u32 instead of an int. Since the function returns negative error codes (such as -ERANGE or return values from i2c_smbus_write_byte_data()), returning an unsigned integer type prevents callers from correctly checking for negative error conditions.

Fix this by changing the function return type from u32 to int.

This was found through manual code review.

Fixes 1c28799257bc ("iio: light: isl29501: Add support for the ISL29501 ToF sensor.")
---
 drivers/iio/proximity/isl29501.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/proximity/isl29501.c +1 -1
@@ -226,7 +226,7 @@ err:
 	return ret;
 }
 
-static u32 isl29501_register_write(struct isl29501_private *isl29501,
+static int isl29501_register_write(struct isl29501_private *isl29501,
 				   enum isl29501_register_name name,
 				   u32 value)
 {
author maintainer
#413 lore raw

PATCH 410/428 iio: proximity: pulsedlight: fix iio_device left registered on PM setup failure

In reply to Greg Kroah-Hartman

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

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

From: Cong Nguyen <[email protected]>

commit 4daa9ca557b4b98ae1ad38a6d1deaa3e31eb8edf upstream.

pm_runtime_set_active() failing in probe() jumps to error_unreg_buffer, which only calls iio_triggered_buffer_cleanup() -- it does not undo the iio_device_register() that already succeeded a few lines above. probe() then returns the error, the devm-managed indio_dev is freed, but the iio core still has it registered: the sysfs/chardev nodes stay live and point at freed memory.

Add an error_unreg_dev label that unregisters the iio device before falling through to the existing buffer cleanup, mirroring the teardown order already used in lidar_remove().

Fixes 4ac4e086fd8c ("iio: pulsedlight-lidar-lite: add runtime PM")
Assisted-by Claude:claude-opus-4
---
 drivers/iio/proximity/pulsedlight-lidar-lite-v2.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +3 -1
@@ -294,12 +294,14 @@ static int lidar_probe(struct i2c_client
 
 	ret = pm_runtime_set_active(&client->dev);
 	if (ret)
-		goto error_unreg_buffer;
+		goto error_unreg_dev;
 	pm_runtime_enable(&client->dev);
 	pm_runtime_idle(&client->dev);
 
 	return 0;
 
+error_unreg_dev:
+	iio_device_unregister(indio_dev);
 error_unreg_buffer:
 	iio_triggered_buffer_cleanup(indio_dev);
 
author maintainer
#414 lore raw

PATCH 411/428 iio: proximity: sx9324: Correct proximity channel resolution

In reply to Greg Kroah-Hartman

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

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

From: Zhang Jie <[email protected]>

commit fbf33cb18334e7cab1ed4ad2e9a98a1299ad874c upstream.

The proximity channels were previously defined with 12 realbits. However, PROXDIFF is read from RegDiffMsb (0x65) and RegDiffLsb (0x66). The SX9324 datasheet assigns bits 7:0 of each register to PROXDIFF and documents it as a signed two's-complement value (Revision 3, Section 8, Table 8, page 43). In contrast, RegOffsetMsb explicitly marks bits 7:6 as reserved. Thus, PROXDIFF is a 16-bit signed value.

With realbits = 12, sx_common_read_proximity() uses bit 11 as the sign bit in sign_extend32(), causing samples outside the 12-bit signed range to wrap into the [-2048, 2047] range.

Correct the realbits value to 16 to accurately reflect the hardware.

Tested on an SX9324-based device: a phase 0 DIFF readback of 0x7fff was reported as -1 before this change and as 32767 afterward.

Fixes 4c18a890dff8 ("iio:proximity:sx9324: Add SX9324 support")
---
 drivers/iio/proximity/sx9324.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/proximity/sx9324.c +1 -1
@@ -222,7 +222,7 @@ static const struct iio_chan_spec_ext_in
 	.scan_index = idx,					 \
 	.scan_type = {						 \
 		.sign = 's',					 \
-		.realbits = 12,					 \
+		.realbits = 16,					 \
 		.storagebits = 16,				 \
 		.endianness = IIO_BE,				 \
 	},							 \
author maintainer
#415 lore raw

PATCH 412/428 iio: proximity: vcnl3020: fix ISR bitmask check in IRQ handler

In reply to Greg Kroah-Hartman

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

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

From: Salah Triki <[email protected]>

commit 7d38af8f20239a66e90de7632a79c9425256c574 upstream.

The threaded IRQ handler contained multiple issues in handling interrupt events and clearing status flags:

1. ISR bit check: The handler incorrectly checked the Interrupt Status Register (VCNL_ISR) against VCNL_ICR_THRES_EN (BIT(1)), which is a bitmask meant for the Control Register (VCNL_PS_ICR). In VCNL_ISR, BIT(1) corresponds only to low-threshold interrupts. A high-threshold interrupt (VCNL_INT_TH_HI, BIT(0)) on its own was completely ignored and returned IRQ_NONE.

2. Event direction & channel index: The handler unconditionally pushed a RISING event code on channel index 1. The driver only registers a single proximity channel (index 0), and low-threshold interrupts should be reported with IIO_EV_DIR_FALLING.

3. ISR clearing: The write-back to acknowledge the interrupt only preserved BIT(1) instead of masking against both valid status bits.

Fix this by checking both VCNL_INT_TH_HI and VCNL_INT_TH_LOW bits in VCNL_ISR, pushing separate IIO events with the correct direction and channel index (0), and properly clearing handled status bits.

Fixes 3363fbbe19e5 ("iio: proximity: vcnl3020: add periodic mode")
---
 drivers/iio/proximity/vcnl3020.c |   24 +++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)
drivers/iio/proximity/vcnl3020.c +17 -7
@@ -584,16 +584,26 @@ static irqreturn_t vcnl3020_handle_irq_t
 		return IRQ_HANDLED;
 	}
 
-	if (!(isr & VCNL_ICR_THRES_EN))
+	if (!(isr & (VCNL_INT_TH_HI | VCNL_INT_TH_LOW)))
 		return IRQ_NONE;
 
-	iio_push_event(indio_dev,
-		       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1,
-				            IIO_EV_TYPE_THRESH,
-				            IIO_EV_DIR_RISING),
-		       iio_get_time_ns(indio_dev));
+	if (isr & VCNL_INT_TH_HI) {
+		iio_push_event(indio_dev,
+			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+						    IIO_EV_TYPE_THRESH,
+						    IIO_EV_DIR_RISING),
+			       iio_get_time_ns(indio_dev));
+	}
+
+	if (isr & VCNL_INT_TH_LOW) {
+		iio_push_event(indio_dev,
+			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+						    IIO_EV_TYPE_THRESH,
+						    IIO_EV_DIR_FALLING),
+			       iio_get_time_ns(indio_dev));
+	}
 
-	rc = regmap_write(data->regmap, VCNL_ISR, isr & VCNL_ICR_THRES_EN);
+	rc = regmap_write(data->regmap, VCNL_ISR, isr & (VCNL_INT_TH_HI | VCNL_INT_TH_LOW));
 	if (rc)
 		dev_err(data->dev, "Error (%d) writing in reg (0x%x)\n",
 			rc, VCNL_ISR);
author maintainer
#416 lore raw

PATCH 413/428 iio: proximity: vl53l0x-i2c: claim direct mode for raw reads

In reply to Greg Kroah-Hartman

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

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

From: Donggeun Yoo <[email protected]>

commit e24328974a88aa8541e94e8bb8dfa2ade1b3c45d upstream.

vl53l0x_read_raw() starts a single-shot ranging measurement and reads back the result. Once the triggered buffer is enabled the sensor runs in continuous mode and its data-ready interrupt is routed to the trigger, so a concurrent in_distance_raw read disturbs the streaming setup and never gets its completion, returning -ETIMEDOUT.

The original submission claimed direct mode here, but it was dropped during review because the driver had no buffer support at the time [1]. Continuous (buffered) mode was later added without restoring the claim [2], reintroducing the conflict.

Reject direct reads while buffered capture is active by claiming direct mode around the measurement, as the vl53l1x sibling already does.

---
 drivers/iio/proximity/vl53l0x-i2c.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/iio/proximity/vl53l0x-i2c.c +3 -0
@@ -221,7 +221,10 @@ static int vl53l0x_read_raw(struct iio_d
 
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
+		if (!iio_device_claim_direct(indio_dev))
+			return -EBUSY;
 		ret = vl53l0x_read_proximity(data, chan, val);
+		iio_device_release_direct(indio_dev);
 		if (ret < 0)
 			return ret;
 
author maintainer
#417 lore raw

PATCH 414/428 iio: trigger: cancel reenable_work before freeing trigger

In reply to Greg Kroah-Hartman

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

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

From: Fan Wu <[email protected]>

commit 2a57b9246cecd5dd797a42eb310d6c8816f26d9e upstream.

iio_trigger_notify_done_atomic() defers ->reenable() into trig->reenable_work on the system workqueue, and the worker dereferences the owning trigger through container_of(). Nothing cancels this work before iio_trig_release() frees the trigger, so a worker armed by the last in-flight IRQ can outlive the free and touch freed memory.

Cancel it at the top of iio_trig_release(), which every free path reaches through the device core's final put_device().

Found by an in-house static analysis tool.

Fixes 9020ef659885 ("iio: trigger: Fix a scheduling whilst atomic issue seen on tsc2046")
Assisted-by Codex:gpt-5.6
---
 drivers/iio/industrialio-trigger.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/iio/industrialio-trigger.c +2 -0
@@ -509,6 +509,8 @@ static void iio_trig_release(struct devi
 	struct iio_trigger *trig = to_iio_trigger(device);
 	int i;
 
+	cancel_work_sync(&trig->reenable_work);
+
 	if (trig->subirq_base) {
 		for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
 			irq_modify_status(trig->subirq_base + i,
author maintainer
#418 lore raw

PATCH 415/428 mptcp: fix msk->timer_ival reset

In reply to Greg Kroah-Hartman

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

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

From: Matthieu Baerts (NGI0) <[email protected]>

In this kernel version, icsk->icsk_rto_min is not initialised, because this version doesn't contain commit ea4eb2adb0d3 ("mptcp: honour configured min/max RTO in retransmit paths").

Reset it to TCP_RTO_MIN, which is how msk->timer_ival is also reset in __mptcp_init_sock() in this version.

Fixes 69a7b2a91267 ("mptcp: prevent race between disconnect() and rtx")
---
 net/mptcp/protocol.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
net/mptcp/protocol.c +1 -1
index de6f289a6e14e..79ec52f64dc50 100644
@@ -3475,7 +3475,7 @@ static int mptcp_disconnect(struct sock *sk, int flags)
 	/* The later subflow close can not kick again the tout timer,
 	 * as the msk is already in closed status.
 	 */
-	msk->timer_ival = icsk->icsk_rto_min;
+	msk->timer_ival = TCP_RTO_MIN;
 	sk_stop_timer_sync(sk, &sk->mptcp_retransmit_timer);
 	icsk->icsk_mtup.probe_timestamp = 0;
 	sk_stop_timer_sync(sk, &icsk->mptcp_tout_timer);
Signature · 1 line
2.53.0
author maintainer
#419 lore raw

PATCH 416/428 mtd: rawnand: vf610_nfc: fix false bitflips on reads of erased pages

In reply to Greg Kroah-Hartman

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

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

From: Mehmet Fide <[email protected]>

[ Upstream commit 68fe2faf5c69d0e21f94cd695d28f0ba677d484f ]

When the ECC engine fails to decode a page, the driver re-reads the OOB area with the engine bypassed, but runs the erased-page check for the data area on the buffer left in the controller SRAM by the failed transfer.

That buffer does not hold what is on the flash: the failing engine writes a bogus single-bit "correction" into it. In the 60-byte ECC mode the all-0xff content of an erased page always decodes to the same error location, so every erased page shows one stale zero bit at data offset 0x5FD, which the erased-page check then reports as a corrected bitflip.

Edward Karpicz discovered this behaviour and identified the offset on a Colibri VF61; the analysis and the fix build on his finding. Measured with an instrumented driver on a Colibri VF50 (MX30LF1G18AC, 32-bit ECC): reading a 126 MiB partition with nanddump increased the corrected counter by 18035, exactly one per erased page, while raw reads of the same pages return clean 0xff. A v4.4 kernel on the VF61 (MX30LF4G28AC) accumulates the same false counts, so the behaviour follows the controller rather than the chip or the driver generation. Neither the Vybrid reference manual nor the published mask set errata (VFXXX_2N02G) document it. The 45-byte ECC mode is not affected.

Restoring the known byte is not enough: on pages that fail to decode with content other than all-0xff the engine writes its correction wherever the syndrome points (measured at a different offset on such a page), so the check has to run on what the flash holds. Re-read the data area with the ECC engine bypassed, exactly as already done for the OOB area. The corrected counter then stays at zero on both boards.

---
 drivers/mtd/nand/raw/vf610_nfc.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)
drivers/mtd/nand/raw/vf610_nfc.c +11 -4
index 1c3e7b167e533..1b9b370adfab6 100644
@@ -519,6 +519,7 @@ static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat,
 	u8 ecc_status;
 	u8 ecc_count;
 	int flips_threshold = nfc->chip.ecc.strength / 2;
+	int ret;
 
 	ecc_status = vf610_nfc_read(nfc, ecc_status_off) & 0xff;
 	ecc_count = ecc_status & ECC_STATUS_ERR_COUNT;
@@ -526,15 +527,21 @@ static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat,
 	if (!(ecc_status & ECC_STATUS_MASK))
 		return ecc_count;
 
+	/* The failed decode leaves a bogus correction in SRAM; re-read without ECC */
 	nfc->data_access = true;
-	nand_read_oob_op(&nfc->chip, page, 0, oob, vf610_nfc_spare_size(mtd));
+	ret = nand_read_page_op(&nfc->chip, page, 0, dat, mtd->writesize);
+	if (!ret)
+		ret = nand_read_oob_op(&nfc->chip, page, 0, oob,
+				       vf610_nfc_spare_size(mtd));
 	nfc->data_access = false;
+	if (ret)
+		return ret;
 
 	/*
-	 * On an erased page, bit count (including OOB) should be zero or
-	 * at least less then half of the ECC strength.
+	 * Run the erased-page check with the driver's historic threshold
+	 * of half the ECC strength.
 	 */
-	return nand_check_erased_ecc_chunk(dat, nfc->chip.ecc.size, oob,
+	return nand_check_erased_ecc_chunk(dat, mtd->writesize, oob,
 					   vf610_nfc_spare_size(mtd), NULL, 0,
 					   flips_threshold);
 }
Signature · 1 line
2.53.0
author maintainer
#420 lore raw

PATCH 417/428 rust: irq: pass RegistrationInner as cookie to request_irq

In reply to Greg Kroah-Hartman

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

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

From: Alice Ryhl <[email protected]>

With commit ba268514ea14 ("rust: devres: fix race condition due to nesting"), Devres::new() returns Result<Self> by value instead of initializing in-place via PinInit. Because request_irq() is called inside Devres::new(), registration.inner is still uninitialized when the IRQ is enabled, so accessing registration.inner.device() in the IRQ callback can read uninitialized memory.

Fix this by storing the Device and handler pointer in RegistrationInner and passing RegistrationInner as the IRQ cookie after its fields are initialized.

This is not needed in mainline because commit 98c63ce4d760 ("rust: irq:
make Registration compatible with lifetime-bound drivers") removed Devres from irq::Registration and moved request_irq() after all fields are initialized.

Fixes 29e16fcd67ee ("rust: irq: add &Device<Bound> argument to irq callbacks")
Fixes ba268514ea14 ("rust: devres: fix race condition due to nesting")
---
 rust/kernel/irq/request.rs |   89 +++++++++++++++++++++++++++------------------
 1 file changed, 54 insertions(+), 35 deletions(-)
rust/kernel/irq/request.rs +54 -35
@@ -14,7 +14,7 @@ use crate::error::to_result;
 use crate::irq::flags::Flags;
 use crate::prelude::*;
 use crate::str::CStr;
-use crate::sync::Arc;
+use crate::sync::{aref::ARef, Arc};
 
 /// The value that can be returned from a [`Handler`] or a [`ThreadedHandler`].
 #[repr(u32)]
@@ -54,13 +54,18 @@ impl<T: ?Sized + Handler, A: Allocator +
 /// # Invariants
 ///
 /// - `self.irq` is the same as the one passed to `request_{threaded}_irq`.
-/// - `cookie` was passed to `request_{threaded}_irq` as the cookie. It is guaranteed to be unique
+/// - `&self` was passed to `request_{threaded}_irq` as the cookie. It is guaranteed to be unique
 ///   by the type system, since each call to `new` will return a different instance of
 ///   `Registration`.
+/// - `self.handler` points to a valid instance of the handler `T` that lives at least until
+///   `Self::drop` completes.
 #[pin_data(PinnedDrop)]
 struct RegistrationInner {
     irq: u32,
-    cookie: *mut c_void,
+    dev: ARef<Device>,
+    handler: *const c_void,
+    #[pin]
+    _pin: PhantomPinned,
 }
 
 impl RegistrationInner {
@@ -77,18 +82,22 @@ impl PinnedDrop for RegistrationInner {
         //
         // Safe as per the invariants of `RegistrationInner` and:
         //
-        // - The containing struct is `!Unpin` and was initialized using
+        // - `RegistrationInner` is `!Unpin` and was initialized using
         // pin-init, so it occupied the same memory location for the entirety of
         // its lifetime.
         //
         // Notice that this will block until all handlers finish executing,
         // i.e.: at no point will &self be invalid while the handler is running.
-        unsafe { bindings::free_irq(self.irq, self.cookie) };
+        unsafe {
+            bindings::free_irq(
+                self.irq,
+                core::ptr::from_mut::<Self>(self.get_unchecked_mut()).cast::<c_void>(),
+            )
+        };
     }
 }
 
-// SAFETY: We only use `inner` on drop, which called at most once with no
-// concurrent access.
+// SAFETY: `RegistrationInner` has no interior mutability and `handler` points to a `Sync` handler.
 unsafe impl Sync for RegistrationInner {}
 
 // SAFETY: It is safe to send `RegistrationInner` across threads.
@@ -179,7 +188,7 @@ impl<'a> IrqRequest<'a> {
 ///
 /// # Invariants
 ///
-/// * We own an irq handler whose cookie is a pointer to `Self`.
+/// * We own an irq handler whose cookie is a pointer to `Self::inner`.
 #[pin_data]
 pub struct Registration<T: Handler> {
     #[pin]
@@ -206,10 +215,13 @@ impl<T: Handler> Registration<T> {
             handler <- handler,
             inner <- Devres::new(
                 request.dev,
-                try_pin_init!(RegistrationInner {
-                    // INVARIANT: `this` is a valid pointer to the `Registration` instance
-                    cookie: this.as_ptr().cast::<c_void>(),
-                    irq: {
+                try_pin_init!(&inner_this in RegistrationInner {
+                    irq: request.irq,
+                    dev: request.dev.into(),
+                    // SAFETY: `this` is a valid pointer to the `Registration` instance.
+                    handler: unsafe { &raw const (*this.as_ptr()).handler }.cast(),
+                    _pin: PhantomPinned,
+                    _: {
                         // SAFETY:
                         // - The callbacks are valid for use with request_irq.
                         // - If this succeeds, the slot is guaranteed to be valid until the
@@ -224,11 +236,10 @@ impl<T: Handler> Registration<T> {
                                 Some(handle_irq_callback::<T>),
                                 flags.into_inner(),
                                 name.as_char_ptr(),
-                                this.as_ptr().cast::<c_void>(),
+                                inner_this.as_ptr().cast::<c_void>(),
                             )
                         })?;
-                        request.irq
-                    }
+                    },
                 })
             ),
             _pin: PhantomPinned,
@@ -261,13 +272,15 @@ impl<T: Handler> Registration<T> {
 ///
 /// This function should be only used as the callback in `request_irq`.
 unsafe extern "C" fn handle_irq_callback<T: Handler>(_irq: i32, ptr: *mut c_void) -> c_uint {
-    // SAFETY: `ptr` is a pointer to `Registration<T>` set in `Registration::new`
-    let registration = unsafe { &*(ptr as *const Registration<T>) };
+    // SAFETY: `ptr` is a pointer to `RegistrationInner` set in `Registration::new`
+    let inner = unsafe { &*(ptr as *const RegistrationInner) };
+    // SAFETY: `inner.handler` is a pointer to `T` set in `Registration::new`
+    let handler = unsafe { &*inner.handler.cast::<T>() };
     // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq
     // callback is running implies that the device has not yet been unbound.
-    let device = unsafe { registration.inner.device().as_bound() };
+    let device = unsafe { inner.dev.as_bound() };
 
-    T::handle(&registration.handler, device) as c_uint
+    T::handle(handler, device) as c_uint
 }
 
 /// The value that can be returned from [`ThreadedHandler::handle`].
@@ -396,7 +409,7 @@ impl<T: ?Sized + ThreadedHandler, A: All
 ///
 /// # Invariants
 ///
-/// * We own an irq handler whose cookie is a pointer to `Self`.
+/// * We own an irq handler whose cookie is a pointer to `Self::inner`.
 #[pin_data]
 pub struct ThreadedRegistration<T: ThreadedHandler> {
     #[pin]
@@ -423,10 +436,13 @@ impl<T: ThreadedHandler> ThreadedRegistr
             handler <- handler,
             inner <- Devres::new(
                 request.dev,
-                try_pin_init!(RegistrationInner {
-                    // INVARIANT: `this` is a valid pointer to the `ThreadedRegistration` instance.
-                    cookie: this.as_ptr().cast::<c_void>(),
-                    irq: {
+                try_pin_init!(&inner_this in RegistrationInner {
+                    irq: request.irq,
+                    dev: request.dev.into(),
+                    // SAFETY: `this` is a valid pointer to the `ThreadedRegistration` instance.
+                    handler: unsafe { &raw const (*this.as_ptr()).handler }.cast(),
+                    _pin: PhantomPinned,
+                    _: {
                         // SAFETY:
                         // - The callbacks are valid for use with request_threaded_irq.
                         // - If this succeeds, the slot is guaranteed to be valid until the
@@ -442,11 +458,10 @@ impl<T: ThreadedHandler> ThreadedRegistr
                                 Some(thread_fn_callback::<T>),
                                 flags.into_inner(),
                                 name.as_char_ptr(),
-                                this.as_ptr().cast::<c_void>(),
+                                inner_this.as_ptr().cast::<c_void>(),
                             )
                         })?;
-                        request.irq
-                    }
+                    },
                 })
             ),
             _pin: PhantomPinned,
@@ -482,24 +497,28 @@ unsafe extern "C" fn handle_threaded_irq
     _irq: i32,
     ptr: *mut c_void,
 ) -> c_uint {
-    // SAFETY: `ptr` is a pointer to `ThreadedRegistration<T>` set in `ThreadedRegistration::new`
-    let registration = unsafe { &*(ptr as *const ThreadedRegistration<T>) };
+    // SAFETY: `ptr` is a pointer to `RegistrationInner` set in `ThreadedRegistration::new`
+    let inner = unsafe { &*(ptr as *const RegistrationInner) };
+    // SAFETY: `inner.handler` is a pointer to `T` set in `ThreadedRegistration::new`
+    let handler = unsafe { &*inner.handler.cast::<T>() };
     // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq
     // callback is running implies that the device has not yet been unbound.
-    let device = unsafe { registration.inner.device().as_bound() };
+    let device = unsafe { inner.dev.as_bound() };
 
-    T::handle(&registration.handler, device) as c_uint
+    T::handle(handler, device) as c_uint
 }
 
 /// # Safety
 ///
 /// This function should be only used as the callback in `request_threaded_irq`.
 unsafe extern "C" fn thread_fn_callback<T: ThreadedHandler>(_irq: i32, ptr: *mut c_void) -> c_uint {
-    // SAFETY: `ptr` is a pointer to `ThreadedRegistration<T>` set in `ThreadedRegistration::new`
-    let registration = unsafe { &*(ptr as *const ThreadedRegistration<T>) };
+    // SAFETY: `ptr` is a pointer to `RegistrationInner` set in `ThreadedRegistration::new`
+    let inner = unsafe { &*(ptr as *const RegistrationInner) };
+    // SAFETY: `inner.handler` is a pointer to `T` set in `ThreadedRegistration::new`
+    let handler = unsafe { &*inner.handler.cast::<T>() };
     // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq
     // callback is running implies that the device has not yet been unbound.
-    let device = unsafe { registration.inner.device().as_bound() };
+    let device = unsafe { inner.dev.as_bound() };
 
-    T::handle_threaded(&registration.handler, device) as c_uint
+    T::handle_threaded(handler, device) as c_uint
 }
author maintainer
#421 lore raw

PATCH 418/428 drm/amd/display: map PWM brightness through custom backlight curve

In reply to Greg Kroah-Hartman

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

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

From: Ray Wu <[email protected]>

[ Upstream commit 51cc916648d1421e0271090fb4fa86550a7a96d9 ]

[Why/How]
On the PWM path, convert the userspace brightness to an input signal and derive the target luminance from the custom backlight curve, then pass the resulting millipercent to the power module.

This keeps PWM programming and backlight curve mapping in the power module, aligning the Linux path with the Windows behavior.

Fixes 3c108046e1d6 ("drm/amd/display: Add power module on Linux")
Assisted-by Cursor:Claude-Opus-4.8
(cherry picked from commit bc4e099caa85e8d4c3555851a0feb07058c4a1fe)
[ sashal: Relocated the changes into the existing display manager code because the backlight code had not been split out. ]
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |   92 +++++++++++++++++++---
 1 file changed, 80 insertions(+), 12 deletions(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +80 -12
@@ -70,6 +70,7 @@
 #include "ivsrcid/ivsrcid_vislands30.h"
 
 #include <linux/backlight.h>
+#include <linux/minmax.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/types.h>
@@ -1974,6 +1975,8 @@ static int amdgpu_dm_init_power_module(s
 		init_data[i].use_custom_backlight_caps = false;
 		init_data[i].custom_backlight_caps_config_no = 0;
 		init_data[i].use_linear_backlight_curve = false;
+		if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)
+			init_data[i].use_linear_backlight_curve = true;
 		init_data[i].def_varibright_enable = 0;
 		init_data[i].varibright_level = 0;
 		/*
@@ -5304,6 +5307,68 @@ static inline u32 scale_fw_to_input(int
 	return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL);
 }
 
+static u32 backlight_signal_to_luminance(const struct amdgpu_dm_backlight_caps *caps,
+					 u32 signal)
+{
+	u32 lower_signal, upper_signal;
+	u32 lower_lum, upper_lum;
+	int i;
+
+	if (!caps->data_points)
+		return DIV_ROUND_CLOSEST(signal * 100, AMDGPU_MAX_BL_LEVEL);
+
+	if (signal < caps->luminance_data[0].input_signal) {
+		lower_signal = 0;
+		lower_lum = 0;
+		upper_signal = caps->luminance_data[0].input_signal;
+		upper_lum = caps->luminance_data[0].luminance;
+	} else if (signal >=
+		   caps->luminance_data[caps->data_points - 1].input_signal) {
+		lower_signal = caps->luminance_data[caps->data_points - 1].input_signal;
+		lower_lum = caps->luminance_data[caps->data_points - 1].luminance;
+		upper_signal = caps->max_input_signal;
+		upper_lum = 100;
+	} else {
+		for (i = 1;
+		     i < caps->data_points &&
+		     signal > caps->luminance_data[i].input_signal;
+		     i++)
+			;
+
+		lower_signal = caps->luminance_data[i - 1].input_signal;
+		upper_signal = caps->luminance_data[i].input_signal;
+		lower_lum = caps->luminance_data[i - 1].luminance;
+		upper_lum = caps->luminance_data[i].luminance;
+	}
+
+	if (upper_signal == lower_signal)
+		return upper_lum;
+
+	return lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) *
+					     (signal - lower_signal),
+					     upper_signal - lower_signal);
+}
+
+static u32 convert_brightness_to_millipercent(const struct amdgpu_dm_backlight_caps *caps,
+					      u32 user_brightness)
+{
+	unsigned int min, max;
+	u32 signal, luminance;
+
+	if (!get_brightness_range(caps, &min, &max))
+		return DIV_ROUND_CLOSEST_ULL((u64)user_brightness * 100000,
+					     MAX_BACKLIGHT_LEVEL);
+
+	user_brightness = clamp(user_brightness, min, max);
+	if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)
+		return DIV_ROUND_CLOSEST_ULL((u64)user_brightness * 100000, max);
+
+	signal = scale_input_to_fw(max, user_brightness);
+	luminance = backlight_signal_to_luminance(caps, signal);
+
+	return min_t(u32, luminance * 1000, 100000);
+}
+
 static void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
 				      unsigned int min, unsigned int max,
 				      uint32_t *user_brightness)
@@ -5421,10 +5486,10 @@ static void amdgpu_dm_backlight_set_leve
 	struct amdgpu_dm_backlight_caps *caps;
 	struct dc_link *link;
 	u32 brightness = 0;
+	u32 millipercent = 0;
 	bool rc = false, reallow_idle = false;
 	struct drm_connector *connector;
 	struct dc_stream_state *stream;
-	unsigned int min, max;
 
 	list_for_each_entry(connector, &dm->ddev->mode_config.connector_list, head) {
 		struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
@@ -5447,12 +5512,21 @@ static void amdgpu_dm_backlight_set_leve
 	/* update scratch register */
 	if (bl_idx == 0)
 		amdgpu_atombios_scratch_regs_set_backlight_level(dm->adev, dm->brightness[bl_idx]);
-	brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]);
+
 	link = (struct dc_link *)dm->backlight_link[bl_idx];
+	stream = dm_find_stream_with_link(dm, link);
+	if (!stream)
+		return;
 
-	/* Apply brightness quirk */
-	if (caps->brightness_mask)
-		brightness |= caps->brightness_mask;
+	if (caps->aux_support) {
+		brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]);
+		/* Apply brightness quirk */
+		if (caps->brightness_mask)
+			brightness |= caps->brightness_mask;
+	} else {
+		millipercent = convert_brightness_to_millipercent(caps,
+								  user_brightness);
+	}
 
 	if (trace_amdgpu_dm_brightness_enabled()) {
 		trace_amdgpu_dm_brightness(__builtin_return_address(0),
@@ -5462,10 +5536,6 @@ static void amdgpu_dm_backlight_set_leve
 					   power_supply_is_system_supplied() > 0);
 	}
 
-	stream = dm_find_stream_with_link(dm, link);
-	if (!stream)
-		return;
-
 	mutex_lock(&dm->dc_lock);
 	if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) {
 		dc_allow_idle_optimizations(dm->dc, false);
@@ -5477,10 +5547,8 @@ static void amdgpu_dm_backlight_set_leve
 			AUX_BL_DEFAULT_TRANSITION_TIME_MS, false, true);
 	} else {
 		/* power module uses millipercent */
-		get_brightness_range(caps, &min, &max);
-		brightness = DIV_ROUND_CLOSEST(brightness * 100, (max - min)) * 1000;
 		rc = mod_power_set_backlight_percent(dm->power_module, stream,
-						     brightness, 0, false);
+						     millipercent, 0, false);
 	}
 
 	/*