Skip to contentSkip to conversation

6.6.157-rc1 review

PATCH In review Greg Kroah-Hartman 12 participants 1,440 messages Sep 12 – Nov 10 Page 4 of 11

Subsystems (5)
Stable branch Documentation USB BPF +1 more subsystems
lore
Share & tools
lore
author maintainer
#406 lore raw

PATCH 406/1424 clk: qcom: gcc-mdm9607: Drop incorrect BIMC PLL and related clocks

In reply to Greg Kroah-Hartman

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

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

From: Stephan Gerhold <[email protected]>

commit e111ddda092ad961870a634e4c9263c10a3e8485 upstream.

The gcc-mdm9607 driver was originally based on gcc-msm8916, but a closer match nowadays is gcc-msm8909. Looking at the differences between gcc-mdm9607 and gcc-msm8909, there is quite some confusion around the definitions for the BIMC PLL.

It turns out the BIMC PLL on MDM9607 is actually an Alpha PLL just like on MSM8909. We can vote for it using BIT(2), which explains why BIT(3) was used for GPLL2.

In practice, the BIMC PLL is exclusively controlled by the RPM firmware and Linux should not touch it. So far, it was defined to model the full clock hierarchy, but even in read-only mode with CLK_GET_RATE_NOCACHE this is problematic since the RPM will silently change the clock parents without notifying Linux about it. The clock framework reads the clock parent only once during boot, so the resulting rates will still be often wrong.

Follow the example of more recent SoCs and drop the BIMC PLL and all remaining related clocks to avoid reporting stale status data. This means we cannot determine the rate of these clocks anymore. This is not a big problem in practice, since these are NoC-related clocks that are independently managed through the interconnect subsystem.

---
 drivers/clk/qcom/gcc-mdm9607.c |  125 -----------------------------------------
 1 file changed, 1 insertion(+), 124 deletions(-)
drivers/clk/qcom/gcc-mdm9607.c +1 -124
@@ -26,7 +26,6 @@
 
 enum {
 	P_XO,
-	P_BIMC,
 	P_GPLL0,
 	P_GPLL1,
 	P_GPLL2,
@@ -121,7 +120,7 @@ static struct clk_alpha_pll gpll2_early
 	.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT],
 	.clkr = {
 		.enable_reg = 0x45000,
-		.enable_mask = BIT(3), /* Yeah, apparently it's not 2 */
+		.enable_mask = BIT(3), /* BIT(2) is used for BIMC PLL */
 		.hw.init = &(struct clk_init_data)
 		{
 			.name = "gpll2_early",
@@ -178,68 +177,6 @@ static struct clk_rcg2 apss_ahb_clk_src
 	},
 };
 
-static struct clk_pll bimc_pll = {
-	.l_reg = 0x23004,
-	.m_reg = 0x23008,
-	.n_reg = 0x2300c,
-	.config_reg = 0x23010,
-	.mode_reg = 0x23000,
-	.status_reg = 0x2301c,
-	.status_bit = 17,
-	.clkr.hw.init = &(struct clk_init_data){
-		.name = "bimc_pll",
-		.parent_data = &(const struct clk_parent_data){
-			.fw_name = "xo",
-		},
-		.num_parents = 1,
-		.ops = &clk_pll_ops,
-	},
-};
-
-static struct clk_regmap bimc_pll_vote = {
-	.enable_reg = 0x45000,
-	.enable_mask = BIT(3),
-	.hw.init = &(struct clk_init_data){
-		.name = "bimc_pll_vote",
-		.parent_hws = (const struct clk_hw *[]){ &bimc_pll.clkr.hw },
-		.num_parents = 1,
-		.ops = &clk_pll_vote_ops,
-	},
-};
-
-static const struct parent_map gcc_xo_gpll0_bimc_map[] = {
-	{ P_XO, 0 },
-	{ P_GPLL0, 1 },
-	{ P_BIMC, 2 },
-};
-
-static const struct clk_parent_data gcc_xo_gpll0_bimc[] = {
-	{ .fw_name = "xo" },
-	{ .hw = &gpll0.clkr.hw },
-	{ .hw = &bimc_pll_vote.hw },
-};
-
-static const struct freq_tbl ftbl_pcnoc_bfdcd_clk_src[] = {
-	F(19200000, P_XO, 1, 0, 0),
-	F(50000000, P_GPLL0, 16, 0, 0),
-	F(100000000, P_GPLL0, 8, 0, 0),
-	{ }
-};
-
-static struct clk_rcg2 pcnoc_bfdcd_clk_src = {
-	.cmd_rcgr = 0x27000,
-	.freq_tbl = ftbl_pcnoc_bfdcd_clk_src,
-	.hid_width = 5,
-	.parent_map = gcc_xo_gpll0_bimc_map,
-	.clkr.hw.init = &(struct clk_init_data){
-		.name = "pcnoc_bfdcd_clk_src",
-		.parent_data = gcc_xo_gpll0_bimc,
-		.num_parents = ARRAY_SIZE(gcc_xo_gpll0_bimc),
-		.ops = &clk_rcg2_ops,
-		.flags = CLK_IS_CRITICAL,
-	},
-};
-
 static const struct freq_tbl ftbl_gcc_blsp1_qup1_6_i2c_apps_clk[] = {
 	F(19200000, P_XO, 1, 0, 0),
 	F(50000000, P_GPLL0, 16, 0, 0),
@@ -735,8 +672,6 @@ static struct clk_branch gcc_blsp1_ahb_c
 		.enable_mask = BIT(10),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_blsp1_ahb_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1038,8 +973,6 @@ static struct clk_branch gcc_boot_rom_ah
 		.enable_mask = BIT(7),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_boot_rom_ahb_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1053,9 +986,6 @@ static struct clk_branch gcc_crypto_ahb_
 		.enable_mask = BIT(0),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_crypto_ahb_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
-			.flags = CLK_SET_RATE_PARENT,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1069,9 +999,6 @@ static struct clk_branch gcc_crypto_axi_
 		.enable_mask = BIT(1),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_crypto_axi_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
-			.flags = CLK_SET_RATE_PARENT,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1145,9 +1072,6 @@ static struct clk_branch gcc_mss_cfg_ahb
 		.enable_mask = BIT(0),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_mss_cfg_ahb_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
-			.flags = CLK_SET_RATE_PARENT,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1175,9 +1099,6 @@ static struct clk_branch gcc_pdm_ahb_clk
 		.enable_mask = BIT(0),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_pdm_ahb_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
-			.flags = CLK_SET_RATE_PARENT,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1191,9 +1112,6 @@ static struct clk_branch gcc_prng_ahb_cl
 		.enable_mask = BIT(8),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_prng_ahb_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
-			.flags = CLK_SET_RATE_PARENT,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1206,9 +1124,6 @@ static struct clk_branch gcc_sdcc1_ahb_c
 		.enable_mask = BIT(0),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_sdcc1_ahb_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
-			.flags = CLK_SET_RATE_PARENT,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1236,9 +1151,6 @@ static struct clk_branch gcc_sdcc2_ahb_c
 		.enable_mask = BIT(0),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_sdcc2_ahb_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
-			.flags = CLK_SET_RATE_PARENT,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1259,19 +1171,6 @@ static struct clk_branch gcc_sdcc2_apps_
 	},
 };
 
-static struct clk_rcg2 bimc_ddr_clk_src = {
-	.cmd_rcgr = 0x32004,
-	.hid_width = 5,
-	.parent_map = gcc_xo_gpll0_bimc_map,
-	.clkr.hw.init = &(struct clk_init_data){
-		.name = "bimc_ddr_clk_src",
-		.parent_data = gcc_xo_gpll0_bimc,
-		.num_parents = 3,
-		.ops = &clk_rcg2_ops,
-		.flags = CLK_GET_RATE_NOCACHE,
-	},
-};
-
 static struct clk_branch gcc_mss_q6_bimc_axi_clk = {
 	.halt_reg = 0x49004,
 	.clkr = {
@@ -1279,9 +1178,6 @@ static struct clk_branch gcc_mss_q6_bimc
 		.enable_mask = BIT(0),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_mss_q6_bimc_axi_clk",
-			.parent_hws = (const struct clk_hw *[]){ &bimc_ddr_clk_src.clkr.hw },
-			.num_parents = 1,
-			.flags = CLK_SET_RATE_PARENT,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1295,8 +1191,6 @@ static struct clk_branch gcc_apss_tcu_cl
 		.enable_mask = BIT(1),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_apss_tcu_clk",
-			.parent_hws = (const struct clk_hw *[]){ &bimc_ddr_clk_src.clkr.hw },
-			.num_parents = 1,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1310,9 +1204,6 @@ static struct clk_branch gcc_smmu_cfg_cl
 		.enable_mask = BIT(12),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_smmu_cfg_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
-			.flags = CLK_SET_RATE_PARENT,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1360,9 +1251,6 @@ static struct clk_branch gcc_usb_hs_phy_
 		.enable_mask = BIT(0),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_usb_hs_phy_cfg_ahb_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
-			.flags = CLK_SET_RATE_PARENT,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1375,9 +1263,6 @@ static struct clk_branch gcc_usb_hs_ahb_
 		.enable_mask = BIT(0),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_usb_hs_ahb_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
-			.flags = CLK_SET_RATE_PARENT,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1406,8 +1291,6 @@ static struct clk_branch gcc_apss_ahb_cl
 		.enable_mask = BIT(14),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_apss_ahb_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1421,8 +1304,6 @@ static struct clk_branch gcc_apss_axi_cl
 		.enable_mask = BIT(13),
 		.hw.init = &(struct clk_init_data){
 			.name = "gcc_apss_axi_clk",
-			.parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw },
-			.num_parents = 1,
 			.ops = &clk_branch2_ops,
 		},
 	},
@@ -1435,10 +1316,6 @@ static struct clk_regmap *gcc_mdm9607_cl
 	[GPLL1_VOTE] = &gpll1_vote,
 	[GPLL2] = &gpll2.clkr,
 	[GPLL2_EARLY] = &gpll2_early.clkr,
-	[BIMC_PLL] = &bimc_pll.clkr,
-	[BIMC_PLL_VOTE] = &bimc_pll_vote,
-	[BIMC_DDR_CLK_SRC] = &bimc_ddr_clk_src.clkr,
-	[PCNOC_BFDCD_CLK_SRC] = &pcnoc_bfdcd_clk_src.clkr,
 	[APSS_AHB_CLK_SRC] = &apss_ahb_clk_src.clkr,
 	[BLSP1_QUP1_I2C_APPS_CLK_SRC] = &blsp1_qup1_i2c_apps_clk_src.clkr,
 	[BLSP1_QUP1_SPI_APPS_CLK_SRC] = &blsp1_qup1_spi_apps_clk_src.clkr,
author maintainer
#407 lore raw

PATCH 407/1424 i2c: mux: demux-pinctrl: fix OF node leak on kstrdup failure

In reply to Greg Kroah-Hartman

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

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

From: Linkai Gong <[email protected]>

commit 62edb8ca0aa44517cc23cfa26cd8a51f15ea92fe upstream.

of_parse_phandle() takes a reference on the parent node. If a later devm_kstrdup() fails, err_rollback only releases nodes for indices 0..i-1, so the current node is leaked.

of_node_put() the current parent before rolling back.

Fixes 7c0195fa9a9e ("i2c: mux: demux-pinctrl: check the return value of devm_kstrdup()")
Cc <[email protected]> # v6.6+
---
 drivers/i2c/muxes/i2c-demux-pinctrl.c |    1 +
 1 file changed, 1 insertion(+)
drivers/i2c/muxes/i2c-demux-pinctrl.c +1 -0
@@ -245,6 +245,7 @@ static int i2c_demux_pinctrl_probe(struc
 		props[i].value = devm_kstrdup(&pdev->dev, "ok", GFP_KERNEL);
 		if (!props[i].name || !props[i].value) {
 			err = -ENOMEM;
+			of_node_put(adap_np);
 			goto err_rollback;
 		}
 		props[i].length = 3;
author maintainer
#408 lore raw

PATCH 408/1424 ASoC: cs35l33: drain threaded IRQ before runtime suspend

In reply to Greg Kroah-Hartman

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

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

From: Runyu Xiao <[email protected]>

commit e074c12c428c633e079154301207a6079a208583 upstream.

cs35l33_runtime_suspend() currently switches the codec into regcache_cache_only(true) and powers it down without first quiescing the threaded IRQ registered by devm_request_threaded_irq(). That leaves a window where cs35l33_irq_thread() can still run after suspend has closed off live register access.

A running system can reach this during runtime PM while the driver still has critical fault IRQs unmasked. If the threaded handler runs in that window, it reads volatile INT_STATUS_1/2 after cache_only has been enabled, ignores the regmap_read() failures, and can still drive the AMP_SHORT_RLS, CAL_ERR_RLS, OTE_RLS, and OTW_RLS release paths.

Use disable_irq() before entering cache_only/power-off so any in-flight threaded handler is drained and no new IRQ thread can run during the suspended state. Re-enable the IRQ only after runtime_resume() has restored live register access with regcache_sync(). Since probe only warns if devm_request_threaded_irq() fails, track whether the IRQ was actually installed before disabling or re-enabling it.

Fixes 3333cb7187b9 ("ASoC: cs35l33: Initial commit of the cs35l33 CODEC driver.")
---
 sound/soc/codecs/cs35l33.c |   14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)
sound/soc/codecs/cs35l33.c +12 -2
@@ -42,6 +42,7 @@ struct cs35l33_private {
 	struct regmap *regmap;
 	struct gpio_desc *reset_gpio;
 	bool amp_cal;
+	bool irq_requested;
 	int mclk_int;
 	struct regulator_bulk_data core_supplies[2];
 	int num_core_supplies;
@@ -883,6 +884,9 @@ static int __maybe_unused cs35l33_runtim
 		goto err;
 	}
 
+	if (cs35l33->irq_requested)
+		enable_irq(to_i2c_client(dev)->irq);
+
 	return 0;
 
 err:
@@ -902,6 +906,10 @@ static int __maybe_unused cs35l33_runtim
 	/* redo the calibration in next power up */
 	cs35l33->amp_cal = false;
 
+	/* Drain and block the threaded IRQ before cache_only/power-off. */
+	if (cs35l33->irq_requested)
+		disable_irq(to_i2c_client(dev)->irq);
+
 	regcache_cache_only(cs35l33->regmap, true);
 	regcache_mark_dirty(cs35l33->regmap);
 	regulator_bulk_disable(cs35l33->num_core_supplies,
@@ -1158,10 +1166,12 @@ static int cs35l33_i2c_probe(struct i2c_
 	}
 
 	ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
-			cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
-			"cs35l33", cs35l33);
+				cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+				"cs35l33", cs35l33);
 	if (ret != 0)
 		dev_warn(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+	else
+		cs35l33->irq_requested = true;
 
 	/* We could issue !RST or skip it based on AMP topology */
 	cs35l33->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
author maintainer
#409 lore raw

PATCH 409/1424 ASoC: cs35l34: drain threaded IRQ before runtime suspend

In reply to Greg Kroah-Hartman

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

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

From: Runyu Xiao <[email protected]>

commit 4105a4c0678b2808fc8046b60321b4f1cc7dae75 upstream.

cs35l34_runtime_suspend() currently switches the codec into regcache_cache_only(true), asserts reset low, and powers the device off without first quiescing the threaded IRQ registered by devm_request_threaded_irq(). That leaves a window where cs35l34_irq_thread() can still run after suspend has removed live hardware access.

A running system can reach this during runtime PM while the driver still has critical fault IRQs unmasked. If the threaded handler runs in that window, it reads volatile INT_STATUS_1..4 after cache_only has been enabled, ignores the regmap_read() failures, and can still execute the PROT_RELEASE_CTL release sequence or the BST fault power-down writes.

Use disable_irq() before entering cache_only/reset-low/power-off so any in-flight threaded handler is drained and no new IRQ thread can run while the device is suspended. Re-enable the IRQ only after runtime_resume() has restored live register access with regcache_sync(). Since probe only logs request_threaded_irq() failures and keeps going, track whether the IRQ was actually installed before disabling or re-enabling it.

Fixes c1124c09e103 ("ASoC: cs35l34: Initial commit of the cs35l34 CODEC driver.")
---
 sound/soc/codecs/cs35l34.c |   14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)
sound/soc/codecs/cs35l34.c +12 -2
@@ -45,6 +45,7 @@ struct  cs35l34_private {
 	int num_core_supplies;
 	int mclk_int;
 	bool tdm_mode;
+	bool irq_requested;
 	struct gpio_desc *reset_gpio;	/* Active-low reset GPIO */
 };
 
@@ -1053,10 +1054,12 @@ static int cs35l34_i2c_probe(struct i2c_
 	}
 
 	ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
-			cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
-			"cs35l34", cs35l34);
+				cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+				"cs35l34", cs35l34);
 	if (ret != 0)
 		dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+	else
+		cs35l34->irq_requested = true;
 
 	cs35l34->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
 				"reset", GPIOD_OUT_LOW);
@@ -1161,6 +1164,9 @@ static int __maybe_unused cs35l34_runtim
 		dev_err(dev, "Failed to restore register cache\n");
 		goto err;
 	}
+
+	if (cs35l34->irq_requested)
+		enable_irq(to_i2c_client(dev)->irq);
 	return 0;
 err:
 	regcache_cache_only(cs35l34->regmap, true);
@@ -1174,6 +1180,10 @@ static int __maybe_unused cs35l34_runtim
 {
 	struct cs35l34_private *cs35l34 = dev_get_drvdata(dev);
 
+	/* Drain and block the threaded IRQ before cache_only/power-off. */
+	if (cs35l34->irq_requested)
+		disable_irq(to_i2c_client(dev)->irq);
+
 	regcache_cache_only(cs35l34->regmap, true);
 	regcache_mark_dirty(cs35l34->regmap);
 
author maintainer
#410 lore raw

PATCH 410/1424 ASoC: fsl: mpc5200-i2s: Free DMA resources on probe failure

In reply to Greg Kroah-Hartman

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

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

From: Haoxiang Li <[email protected]>

commit 3a89ddcf0c3d9a068631e8c24d5c9e81d1e6512a upstream.

mpc5200_audio_dma_create() creates the DMA resources before registering the component. If snd_soc_register_component() fails, the function returns directly and leaves the DMA resources allocated.

Call mpc5200_audio_dma_destroy() before returning from this error path.

Fixes f515b67381de ("ASoC: fsl: mpc5200 combine psc_dma platform data")
---
 sound/soc/fsl/mpc5200_psc_i2s.c |    1 +
 1 file changed, 1 insertion(+)
sound/soc/fsl/mpc5200_psc_i2s.c +1 -0
@@ -171,6 +171,7 @@ static int psc_i2s_of_probe(struct platf
 					psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai));
 	if (rc != 0) {
 		pr_err("Failed to register DAI\n");
+		mpc5200_audio_dma_destroy(op);
 		return rc;
 	}
 
author maintainer
#411 lore raw

PATCH 411/1424 AsoC: intel: sst: fix PCI device reference leak on probe failure

In reply to Greg Kroah-Hartman

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

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

From: Haoxiang Li <[email protected]>

commit 016f29997ebd29d6ab59c8162ce0e7f73bd1e517 upstream.

intel_sst_probe() takes a reference to the PCI device with pci_dev_get(). If sst_platform_get_resources() fails afterwards, the probe error path cleans up the driver context but does not drop the PCI device reference.

Add a pci_dev_put() error path for failures after pci_dev_get().

Fixes f533a035e4da ("ASoC: Intel: mrfld - create separate module for pci part")
---
 sound/soc/intel/atom/sst/sst_pci.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
sound/soc/intel/atom/sst/sst_pci.c +3 -1
@@ -140,13 +140,15 @@ static int intel_sst_probe(struct pci_de
 	sst_drv_ctx->pci = pci_dev_get(pci);
 	ret = sst_platform_get_resources(sst_drv_ctx);
 	if (ret < 0)
-		goto do_free_drv_ctx;
+		goto do_put_pci;
 
 	pci_set_drvdata(pci, sst_drv_ctx);
 	sst_configure_runtime_pm(sst_drv_ctx);
 
 	return ret;
 
+do_put_pci:
+	pci_dev_put(sst_drv_ctx->pci);
 do_free_drv_ctx:
 	sst_context_cleanup(sst_drv_ctx);
 	dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret);
author maintainer
#412 lore raw

PATCH 412/1424 ASoC: samsung: aries_audio_probe: double of_node_put due to direct assignment without of_node_get

In reply to Greg Kroah-Hartman

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

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

From: WenTao Liang <[email protected]>

commit fb5d1b1c5f8a920ee697545fa6dee16825085717 upstream.

In aries_audio_probe(), aries_dai[0].platforms->of_node is assigned the same pointer as aries_dai[0].cpus->of_node (from of_parse_phandle) without calling of_node_get(). When the sound card is deregistered, the ASoC framework calls of_node_put() on both cpus->of_node and platforms->of_node, causing a double put on the same node and a refcount underflow.

Add of_node_get(aries_dai[0].cpus->of_node) before the assignment.

Fixes 7a3a7671fa6c ("ASoC: samsung: Add driver for Aries boards")
---
 sound/soc/samsung/aries_wm8994.c |    1 +
 1 file changed, 1 insertion(+)
sound/soc/samsung/aries_wm8994.c +1 -0
@@ -659,6 +659,7 @@ static int aries_audio_probe(struct plat
 		goto out;
 	}
 
+	of_node_get(aries_dai[0].cpus->of_node);
 	aries_dai[0].platforms->of_node = aries_dai[0].cpus->of_node;
 
 	/* Set CPU of_node for BT DAI */
author maintainer
#413 lore raw

PATCH 413/1424 iio: chemical: atlas-sensor: fix PM reference leak in buffer postenable

In reply to Greg Kroah-Hartman

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

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

From: Moksh Panicker <[email protected]>

commit bcd3f72e26314edfce7eaf8d7160b3119c7b7fed upstream.

atlas_buffer_postenable() acquires a runtime PM reference with pm_runtime_resume_and_get() but returns the result of atlas_set_interrupt() directly. If atlas_set_interrupt() fails, the runtime PM reference is leaked and the device can never autosuspend.

Add pm_runtime_put_autosuspend() on the error path to balance the reference.

Fixes 0e4f336f50de ("iio: chemical: atlas-sensor: Balance runtime pm + pm_runtime_resume_and_get()")
---
 drivers/iio/chemical/atlas-sensor.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/iio/chemical/atlas-sensor.c +5 -1
@@ -414,7 +414,11 @@ static int atlas_buffer_postenable(struc
 	if (ret)
 		return ret;
 
-	return atlas_set_interrupt(data, true);
+	ret = atlas_set_interrupt(data, true);
+	if (ret)
+		pm_runtime_put_autosuspend(&data->client->dev);
+
+	return ret;
 }
 
 static int atlas_buffer_predisable(struct iio_dev *indio_dev)
author maintainer
#414 lore raw

PATCH 414/1424 iio: chemical: atlas-sensor: use iio_trigger_poll_nested() to fix remove UAF

In reply to Greg Kroah-Hartman

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

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

From: Fan Wu <[email protected]>

commit be61c8c6252671ecf1fee0ad90f87669e0be1e20 upstream.

The atlas driver requests its hardware data-ready IRQ with devm_request_threaded_irq(); its threaded handler queues an irq_work, atlas_work_handler(), that calls iio_trigger_poll(data->trig).

The IRQ is devm-managed, so free_irq() runs from the devres unwind after atlas_remove() returns without flushing that irq_work. Once a buffer is enabled, conversion-complete IRQs keep firing and queueing it; a pending irq_work can therefore run after the unwind has freed atlas_data/indio_dev and the trigger, when atlas_work_handler() derives the atlas_data pointer via container_of() and dereferences data->trig, a use-after-free.

Call iio_trigger_poll_nested() directly from the threaded handler instead of bouncing through irq_work. free_irq() then drains the threaded handler, closing the window; other iio drivers with a threaded data-ready IRQ do the same (e.g. bmi270).

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

Fixes 7103b99b031c ("iio: chemical: atlas-ph-sensor: reorg driver to allow multiple chips")
Assisted-by Codex:gpt-5.6
---
 drivers/iio/chemical/atlas-sensor.c |   13 +------------
 1 file changed, 1 insertion(+), 12 deletions(-)
drivers/iio/chemical/atlas-sensor.c +1 -12
@@ -13,7 +13,6 @@
 #include <linux/mutex.h>
 #include <linux/err.h>
 #include <linux/irq.h>
-#include <linux/irq_work.h>
 #include <linux/i2c.h>
 #include <linux/mod_devicetable.h>
 #include <linux/regmap.h>
@@ -89,7 +88,6 @@ struct atlas_data {
 	struct iio_trigger *trig;
 	struct atlas_device *chip;
 	struct regmap *regmap;
-	struct irq_work work;
 	unsigned int interrupt_enabled;
 	/* 96-bit data + 32-bit pad + 64-bit timestamp */
 	__be32 buffer[6] __aligned(8);
@@ -443,13 +441,6 @@ static const struct iio_buffer_setup_ops
 	.predisable = atlas_buffer_predisable,
 };
 
-static void atlas_work_handler(struct irq_work *work)
-{
-	struct atlas_data *data = container_of(work, struct atlas_data, work);
-
-	iio_trigger_poll(data->trig);
-}
-
 static irqreturn_t atlas_trigger_handler(int irq, void *private)
 {
 	struct iio_poll_func *pf = private;
@@ -475,7 +466,7 @@ static irqreturn_t atlas_interrupt_handl
 	struct iio_dev *indio_dev = private;
 	struct atlas_data *data = iio_priv(indio_dev);
 
-	irq_work_queue(&data->work);
+	iio_trigger_poll_nested(data->trig);
 
 	return IRQ_HANDLED;
 }
@@ -677,8 +668,6 @@ static int atlas_probe(struct i2c_client
 		goto unregister_trigger;
 	}
 
-	init_irq_work(&data->work, atlas_work_handler);
-
 	if (client->irq > 0) {
 		/* interrupt pin toggles on new conversion */
 		ret = devm_request_threaded_irq(&client->dev, client->irq,
author maintainer
#415 lore raw

PATCH 415/1424 iio: chemical: sgp30: Handle IAQ thread creation failure

In reply to Greg Kroah-Hartman

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

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

From: Linmao Li <[email protected]>

commit 1135d6875d2dbda3f6ec718f3421a6ce4378bd63 upstream.

kthread_run() can fail and return an error pointer, but sgp_probe() stores it and returns success, so the device is registered without its IAQ thread and sgp_remove() later passes the error pointer to kthread_stop(). Return the error from probe instead.

Fixes ce514124161a ("iio: chemical: sgp30: Support Sensirion SGP30/SGPC3 sensors")
---
 drivers/iio/chemical/sgp30.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/iio/chemical/sgp30.c +3 -0
@@ -548,6 +548,9 @@ static int sgp_probe(struct i2c_client *
 
 	data->iaq_thread = kthread_run(sgp_iaq_threadfn, data,
 				       "%s-iaq", data->client->name);
+	if (IS_ERR(data->iaq_thread))
+		return dev_err_probe(dev, PTR_ERR(data->iaq_thread),
+				     "failed to start IAQ thread\n");
 
 	return 0;
 }
author maintainer
#416 lore raw

PATCH 416/1424 iio: dac: m62332: Fix regulator reference count imbalance

In reply to Greg Kroah-Hartman

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

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

From: Erick Henrique <[email protected]>

commit a130404ce0b69ca1438126bd81c1985d3b4d2e6f upstream.

m62332_set_value() enables the Vcc regulator on every write of a non-zero value and disables it on every write of zero, without tracking the channel's current state. Because the regulator is reference counted, changing a channel directly from one non-zero value to another enables it more than once, while a later write of zero disables it only once. The reference count never returns to zero and the regulator is left enabled indefinitely.

Only enable the regulator on the transition from zero to non-zero, and only disable it on the transition from non-zero to zero, using the previously stored channel value to detect the edge. Balance the regulator on the I2C error path so the reference count stays consistent if the write fails.

---
 drivers/iio/dac/m62332.c |   17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)
drivers/iio/dac/m62332.c +12 -5
@@ -32,6 +32,7 @@ static int m62332_set_value(struct iio_d
 {
 	struct m62332_data *data = iio_priv(indio_dev);
 	struct i2c_client *client = data->client;
+	bool enabling, disabling;
 	u8 outbuf[2];
 	int res;
 
@@ -43,7 +44,10 @@ static int m62332_set_value(struct iio_d
 
 	mutex_lock(&data->mutex);
 
-	if (val) {
+	enabling = val && !data->raw[channel];
+	disabling = !val && data->raw[channel];
+
+	if (enabling) {
 		res = regulator_enable(data->vcc);
 		if (res)
 			goto out;
@@ -52,14 +56,17 @@ static int m62332_set_value(struct iio_d
 	res = i2c_master_send(client, outbuf, ARRAY_SIZE(outbuf));
 	if (res >= 0 && res != ARRAY_SIZE(outbuf))
 		res = -EIO;
-	if (res < 0)
+	if (res < 0) {
+		if (enabling)
+			regulator_disable(data->vcc);
 		goto out;
+	}
 
-	data->raw[channel] = val;
-
-	if (!val)
+	if (disabling)
 		regulator_disable(data->vcc);
 
+	data->raw[channel] = val;
+
 	mutex_unlock(&data->mutex);
 
 	return 0;
author maintainer
#417 lore raw

PATCH 417/1424 iio: gyro: mpu3050: fix sign of raw angular velocity readings

In reply to Greg Kroah-Hartman

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

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

From: Cong Nguyen <[email protected]>

commit 06fab97602fe400bea843176f485bbac07a668e2 upstream.

The MPU-3050 gyroscope output registers hold 16-bit two's complement values; the angular velocity channels are declared with .sign = 's'. When mpu3050_read_raw() handles IIO_CHAN_INFO_RAW it reads the register via a big-endian regmap_bulk_read() and assigns it with:

	*val = be16_to_cpu(raw_val);

be16_to_cpu() yields an unsigned 16-bit quantity, so negative rates (bit 15 set) are reported to userspace as large positive integers (e.g. -1 becomes 65535) instead of the correct negative value.

Cast to s16 before the assignment, matching the temperature channel a few lines above which already handles the sign correctly.

Fixes 3904b28efb2c ("iio: gyro: Add driver for the MPU-3050 gyroscope")
Assisted-by Claude:claude-opus-4
---
 drivers/iio/gyro/mpu3050-core.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/gyro/mpu3050-core.c +1 -1
@@ -356,7 +356,7 @@ static int mpu3050_read_raw(struct iio_d
 				goto out_read_raw_unlock;
 			}
 
-			*val = be16_to_cpu(raw_val);
+			*val = (s16)be16_to_cpu(raw_val);
 			ret = IIO_VAL_INT;
 
 			goto out_read_raw_unlock;
author maintainer
#418 lore raw

PATCH 418/1424 iio: light: cm32181: return zero after writing calibscale

In reply to Greg Kroah-Hartman

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

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

From: Giorgi Tchankvetadze <[email protected]>

commit 8756acd30919a3e9f547ea4a1d4b7f7895f4d340 upstream.

The write_raw callback is documented to return 0 on success or a negative error code. However, the IIO_CHAN_INFO_CALIBSCALE case returns 'val' (the user-supplied value) instead of 0.

Fix it by returning 0 on success, matching the behavior of other calibscale implementations in the subsystem.

Fixes 971672c0b3cc ("iio: add Capella CM32181 ambient light sensor driver.")
---
 drivers/iio/light/cm32181.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/light/cm32181.c +1 -1
@@ -370,7 +370,7 @@ static int cm32181_write_raw(struct iio_
 	switch (mask) {
 	case IIO_CHAN_INFO_CALIBSCALE:
 		cm32181->calibscale = val;
-		return val;
+		return 0;
 	case IIO_CHAN_INFO_INT_TIME:
 		ret = cm32181_write_als_it(cm32181, val2);
 		return ret;
author maintainer
#419 lore raw

PATCH 419/1424 iio: light: gp2ap002: Disable regulators on resume failure

In reply to Greg Kroah-Hartman

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

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

From: Laxman Acharya Padhya <[email protected]>

commit a41000ba3a230bed1e422f283486ff8f77fe0d30 upstream.

If enabling VIO fails after VDD has been enabled, runtime resume returns without disabling VDD. Likewise, if device reinitialization fails, both supplies remain enabled. The runtime PM core keeps the device suspended when its resume callback fails, so the supplies must be restored to the suspended state.

Disable the supplies enabled by the callback before returning an error.

Fixes 97d642e23037 ("iio: light: Add a driver for Sharp GP2AP002x00F")
Assisted-by Codex:gpt-5
---
 drivers/iio/light/gp2ap002.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/iio/light/gp2ap002.c +8 -2
@@ -671,7 +671,7 @@ static int gp2ap002_runtime_resume(struc
 	ret = regulator_enable(gp2ap002->vio);
 	if (ret) {
 		dev_err(dev, "failed to enable VIO regulator in resume path\n");
-		return ret;
+		goto out_disable_vdd;
 	}
 
 	msleep(20);
@@ -679,13 +679,19 @@ static int gp2ap002_runtime_resume(struc
 	ret = gp2ap002_init(gp2ap002);
 	if (ret) {
 		dev_err(dev, "re-initialization failed\n");
-		return ret;
+		goto out_disable_vio;
 	}
 
 	/* Re-activate the IRQ */
 	enable_irq(gp2ap002->irq);
 
 	return 0;
+
+out_disable_vio:
+	regulator_disable(gp2ap002->vio);
+out_disable_vdd:
+	regulator_disable(gp2ap002->vdd);
+	return ret;
 }
 
 static DEFINE_RUNTIME_DEV_PM_OPS(gp2ap002_dev_pm_ops, gp2ap002_runtime_suspend,
author maintainer
#420 lore raw

PATCH 420/1424 iio: pressure: dps310: fix NULL pointer dereference on ACPI probe

In reply to Greg Kroah-Hartman

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

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

From: Rupesh Majhi <[email protected]>

commit 26e9213898fc949923188ef0aeea31fc87708836 upstream.

When the device is enumerated through its ACPI HID (IFX3100), i2c_client_get_device_id() returns NULL: the ACPI-derived client name does not match the driver's i2c_device_id table. dps310_probe() then dereferences that NULL pointer in "iio->name = id->name" and crashes the kernel during probe.

The IIO device name is always "dps310", so set it directly and drop the now-unused device-id lookup.

Fixes 72ff282819d0 ("iio: pressure: dps310: Add ACPI HID table")
---
 drivers/iio/pressure/dps310.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/iio/pressure/dps310.c +1 -2
@@ -830,7 +830,6 @@ static const struct iio_info dps310_info
 
 static int dps310_probe(struct i2c_client *client)
 {
-	const struct i2c_device_id *id = i2c_client_get_device_id(client);
 	struct dps310_data *data;
 	struct iio_dev *iio;
 	int rc;
@@ -843,7 +842,7 @@ static int dps310_probe(struct i2c_clien
 	data->client = client;
 	mutex_init(&data->lock);
 
-	iio->name = id->name;
+	iio->name = DPS310_DEV_NAME;
 	iio->channels = dps310_channels;
 	iio->num_channels = ARRAY_SIZE(dps310_channels);
 	iio->info = &dps310_info;
author maintainer
#421 lore raw

PATCH 421/1424 iio: pressure: mpl115: Fix runtime PM cleanup

In reply to Greg Kroah-Hartman

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

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

From: Can Peng <[email protected]>

commit 0b5e142ced4bcf20532da051934bd694d1bbd470 upstream.

mpl115_probe() enables runtime PM when a shutdown GPIO is present and then returns the result of devm_iio_device_register(). If registration fails, runtime PM remains enabled and autosuspend remains selected.

The same unmanaged runtime PM state is also left behind on driver unbind, as the IIO device registration is managed but the runtime PM setup is not.

Use devm_pm_runtime_enable() so runtime PM is disabled automatically on probe failure and driver unbind, and check pm_runtime_set_active() so setup errors are reported.

Set the autosuspend parameters before enabling runtime PM. Once probe has completed, the driver core queues an idle request for the device, so an explicit pm_runtime_get_noresume()/pm_runtime_put() pair is not needed to start autosuspend.

Fixes 0c3a333524a3 ("iio: pressure: mpl115: Implementing low power mode by shutdown gpio")
Suggested-by [email protected]
---
 drivers/iio/pressure/mpl115.c |   11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/iio/pressure/mpl115.c +7 -4
@@ -205,9 +205,9 @@ int mpl115_probe(struct device *dev, con
 
 	if (data->shutdown) {
 		/* Enable runtime PM */
-		pm_runtime_get_noresume(dev);
-		pm_runtime_set_active(dev);
-		pm_runtime_enable(dev);
+		ret = pm_runtime_set_active(dev);
+		if (ret)
+			return ret;
 
 		/*
 		 * As the device takes 3 ms to come up with a fresh
@@ -217,7 +217,10 @@ int mpl115_probe(struct device *dev, con
 		 */
 		pm_runtime_set_autosuspend_delay(dev, 2000);
 		pm_runtime_use_autosuspend(dev);
-		pm_runtime_put(dev);
+
+		ret = devm_pm_runtime_enable(dev);
+		if (ret)
+			return ret;
 
 		dev_dbg(dev, "low-power mode enabled");
 	} else
author maintainer
#422 lore raw

PATCH 422/1424 iio: srf04: fix pm_runtime handling on probe error path

In reply to Greg Kroah-Hartman

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

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

From: Cong Nguyen <[email protected]>

commit a40b2e7a17f26e38ab054363c9c7cde149588357 upstream.

When pm_runtime_set_active() fails during probe, the driver logs the error and unregisters the IIO device, but then falls through and still calls pm_runtime_enable() before returning the error.

Since probe returns an error, srf04_remove() is never called, so runtime PM is left enabled without a matching pm_runtime_disable(). This leaks the enable and triggers an "Unbalanced pm_runtime_enable!" warning on a subsequent bind of the device.

Return the error right after unregistering the IIO device so that runtime PM is not enabled on the failure path.

Fixes 2251157b335b ("iio: srf04: add power management feature")
---
 drivers/iio/proximity/srf04.c |    1 +
 1 file changed, 1 insertion(+)
drivers/iio/proximity/srf04.c +1 -0
@@ -335,6 +335,7 @@ static int srf04_probe(struct platform_d
 		if (ret) {
 			dev_err(data->dev, "pm_runtime_set_active: %d\n", ret);
 			iio_device_unregister(indio_dev);
+			return ret;
 		}
 
 		pm_runtime_enable(data->dev);
author maintainer
#423 lore raw

PATCH 423/1424 iio: temperature: hid-sensor-temperature: switch to non-devm iio_device_register()

In reply to Greg Kroah-Hartman

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

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

From: Sanjay Chitroda <[email protected]>

commit 967d066f5334740f656577bc51c381a1bb707b61 upstream.

Avoid using devm_iio_device_register(), as this driver requires explicit error handling and teardown ordering.

With devm_iio_device_register(), IIO device remains registered until the devres cleanup phase. However, driver's remove() callback removes the sensor hub callback and trigger support. This can create a race window where IIO device is still visible and read_raw() requests are issued. These requests might call sensor_hub_input_attr_get_raw_value(), which waits up to 5 seconds for a response from the sensor hub callback that has already been removed.

Add an explicit iio_device_unregister() call in the teardown path to ensure deterministic cleanup, so that userspace can no longer access the device once backend resources begin to be dismantled.

---
 drivers/iio/temperature/hid-sensor-temperature.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/iio/temperature/hid-sensor-temperature.c +2 -1
@@ -243,7 +243,7 @@ static int hid_temperature_probe(struct
 	if (ret)
 		goto error_remove_trigger;
 
-	ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev);
+	ret = iio_device_register(indio_dev);
 	if (ret)
 		goto error_remove_callback;
 
@@ -263,6 +263,7 @@ static int hid_temperature_remove(struct
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct temperature_state *temp_st = iio_priv(indio_dev);
 
+	iio_device_unregister(indio_dev);
 	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE);
 	hid_sensor_remove_trigger(indio_dev, &temp_st->common_attributes);
 
author maintainer
#424 lore raw

PATCH 424/1424 iio: light: opt4001: Fix power down clearing bits of the wrong register

In reply to Greg Kroah-Hartman

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

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

From: Nikhil Gautam <[email protected]>

commit 3b2cd82c524c75a2173f2e3f874652a75f81cd1d upstream.

opt4001_power_down() intends to clear the operating mode bits in the CTRL register but reads OPT4001_DEVICE_ID instead of OPT4001_CTRL, so the value written back to CTRL contains device ID bits rather than the current configuration.

Fix and simplify this by using regmap_clear_bits() on the CTRL register directly in the devm action, and drop opt4001_power_down() which has no other users.

Fixes 9a9608418292 ("iio: light: Add support for TI OPT4001 light sensor")
---
 drivers/iio/light/opt4001.c | 27 ++++-----------------------
 1 file changed, 4 insertions(+), 23 deletions(-)
drivers/iio/light/opt4001.c +4 -23
index dd152d921b48..288e699734af 100644
@@ -222,33 +222,14 @@ static int opt4001_set_conf(struct opt4001_chip *chip)
 	return ret;
 }
 
-static int opt4001_power_down(struct opt4001_chip *chip)
-{
-	struct device *dev = &chip->client->dev;
-	int ret;
-	unsigned int reg;
-
-	ret = regmap_read(chip->regmap, OPT4001_DEVICE_ID, &reg);
-	if (ret) {
-		dev_err(dev, "Failed to read configuration\n");
-		return ret;
-	}
-
-	/* MODE_OFF is 0x0 so just set bits to 0 */
-	reg &= ~OPT4001_CTRL_OPER_MODE_MASK;
-
-	ret = regmap_write(chip->regmap, OPT4001_CTRL, reg);
-	if (ret)
-		dev_err(dev, "Failed to set configuration to power down\n");
-
-	return ret;
-}
-
 static void opt4001_chip_off_action(void *data)
 {
 	struct opt4001_chip *chip = data;
+	int ret;
 
-	opt4001_power_down(chip);
+	ret = regmap_clear_bits(chip->regmap, OPT4001_CTRL, OPT4001_CTRL_OPER_MODE_MASK);
+	if (ret)
+		dev_err(&chip->client->dev, "Failed to power down\n");
 }
 
 static const struct iio_chan_spec opt4001_channels[] = {
Signature · 1 line
2.55.0
author maintainer
#425 lore raw

PATCH 425/1424 iio: light: opt4001: Fix incompatible pointer type passed to div_u64_rem()

In reply to Greg Kroah-Hartman

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

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

From: Nikhil Gautam <[email protected]>

commit afa28741c9a2cf6edb2e41e25ff146a562160bb3 upstream.

div_u64_rem() takes a u32 * for the remainder but is passed val2, which is an int *. There is no functional impact as int and u32 have the same size and representation on all supported architectures and the remainder is always smaller than the divisor, so it fits in the positive range of int. Fix the type mismatch by using a local u32 for the remainder and assigning the result to *val2.

Fixes 9a9608418292 ("iio: light: Add support for TI OPT4001 light sensor")
---
 drivers/iio/light/opt4001.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/iio/light/opt4001.c +3 -2
index 288e699734af..944923cb8f28 100644
@@ -173,6 +173,7 @@ static int opt4001_read_lux_value(struct iio_dev *indio_dev,
 	u8 crc;
 	u8 calc_crc;
 	u64 lux_raw;
+	u32 rem;
 	int ret;
 
 	ret = regmap_read(chip->regmap, OPT4001_LIGHT1_MSB, &light1);
@@ -199,8 +200,8 @@ static int opt4001_read_lux_value(struct iio_dev *indio_dev,
 
 	lux_raw = lux_raw << exp;
 	lux_raw = lux_raw * chip->chip_info->mul;
-	*val = div_u64_rem(lux_raw, chip->chip_info->div, val2);
-	*val2 = *val2 * 100;
+	*val = div_u64_rem(lux_raw, chip->chip_info->div, &rem);
+	*val2 = rem * 100;
 
 	return IIO_VAL_INT_PLUS_NANO;
 }
Signature · 1 line
2.55.0
author maintainer
#426 lore raw

PATCH 426/1424 iio: light: opt4001: Reject integration times with a non-zero seconds part

In reply to Greg Kroah-Hartman

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

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

From: Nikhil Gautam <[email protected]>

commit d0f21621f8b2b46661ea066d20705dbf7253db87 upstream.

opt4001_write_raw() only looks at val2 when setting the integration time, so a write such as 1.000600 is silently accepted as 600 us. Return -EINVAL if val is non-zero.

Fixes 9a9608418292 ("iio: light: Add support for TI OPT4001 light sensor")
---
 drivers/iio/light/opt4001.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/iio/light/opt4001.c +3 -0
index 944923cb8f28..2448d6330489 100644
@@ -269,6 +269,9 @@ static int opt4001_write_raw(struct iio_dev *indio_dev,
 
 	switch (mask) {
 	case IIO_CHAN_INFO_INT_TIME:
+		if (val)
+			return -EINVAL;
+
 		int_time = opt4001_als_time_to_index(val2);
 		if (int_time < 0)
 			return int_time;
Signature · 1 line
2.55.0
author maintainer
#427 lore raw

PATCH 427/1424 iio: light: opt4001: Fix reversed GENMASK() arguments in fault count mask

In reply to Greg Kroah-Hartman

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

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

From: Nikhil Gautam <[email protected]>

commit d64bfd9f3352b9d9bdeca06de1a0a1c1bd47b896 upstream.

GENMASK(h, l) requires h >= l, but OPT4001_CTRL_FAULT_COUNT is defined as GENMASK(0, 1). The define is currently unused so there is no functional impact, but fix it before anyone builds on it, and add the _MASK suffix for consistency with the neighbouring definitions.

Fixes 9a9608418292 ("iio: light: Add support for TI OPT4001 light sensor")
---
 drivers/iio/light/opt4001.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/light/opt4001.c +1 -1
index 2448d6330489..aa3d87995b7c 100644
@@ -39,7 +39,7 @@
 #define OPT4001_CTRL_OPER_MODE_MASK      GENMASK(5, 4)
 #define OPT4001_CTRL_LATCH_MASK          GENMASK(3, 3)
 #define OPT4001_CTRL_INT_POL_MASK        GENMASK(2, 2)
-#define OPT4001_CTRL_FAULT_COUNT         GENMASK(0, 1)
+#define OPT4001_CTRL_FAULT_COUNT_MASK    GENMASK(1, 0)
 
 /* OPT4001 constants */
 #define OPT4001_DEVICE_ID_VAL            0x121
Signature · 1 line
2.55.0
author maintainer
#428 lore raw

PATCH 428/1424 KVM: nVMX: Always flush vpid02 on first use

In reply to Greg Kroah-Hartman

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

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

From: Yosry Ahmed <[email protected]>

commit f0772389413dce9657c7d6950abf3edbbd511356 upstream.

Make sure vpid02 is always flushed on first use by setting last_vpid=0 when allocating vpid02. nested_vmx_transition_tlb_flush() will always detect a VPID change on first VM-Enter after VMXON, because VPID=0 in vmcs12 is not allowed if L1 enables VPID.

This avoids using stale TLB entries from a previous lifetime of the VPID, that might have been associated with a different vCPU (or a completely different VM).

Note that last_vpid is already being initialized as 0 when the vCPU is created, but it is not reset when vpid02 is freed on VMXOFF. Hence, the problem can only occur if L1 does VMXOFF -> VMXON, runs an L2, and KVM happens to reuse a VPID that has TLB entries on the physical CPU.

---
 arch/x86/kvm/vmx/nested.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
arch/x86/kvm/vmx/nested.c +10 -0
@@ -1187,6 +1187,9 @@ static void nested_vmx_transition_tlb_fl
 	 * is the VPID incorporated into the MMU context.  I.e. KVM must assume
 	 * that the new vpid12 has never been used and thus represents a new
 	 * guest ASID that cannot have entries in the TLB.
+	 *
+	 * Note, last_vpid is initialized as 0, so the first nested VM-Enter
+	 * after VMXON will always flush the TLB to avoid using stale entries.
 	 */
 	if (is_vmenter && vmcs12->virtual_processor_id != vmx->nested.last_vpid) {
 		vmx->nested.last_vpid = vmcs12->virtual_processor_id;
@@ -5190,6 +5193,13 @@ static int enter_vmx_operation(struct kv
 
 	vmx->nested.vpid02 = allocate_vpid();
 
+	/*
+	 * Clear last_vpid to ensure that the VPID is flushed on the first
+	 * nested VM-Enter. Otherwise, stale TLB entries from a previous life of
+	 * the VPID (e.g. different vCPU or even different VM) could be used.
+	 */
+	vmx->nested.last_vpid = 0;
+
 	vmx->nested.vmcs02_initialized = false;
 	vmx->nested.vmxon = true;
 
author maintainer
#429 lore raw

PATCH 429/1424 KVM: nVMX: Service local TLB flushes on failed nested VM-Enter

In reply to Greg Kroah-Hartman

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

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

From: Yosry Ahmed <[email protected]>

commit 05a0b701d1089fb57beeb8982f23c3bbafe0fa8b upstream.

KVM services local TLB flushes on "full" nested VM-Exits (through __nested_vmx_vmexit()), but not if a nested VM-Enter fails (e.g. due to failed VMCS checks in nested_vmx_enter_non_root_mode()).

However, it is possible that KVM had queued TLB flushes that need to be performed, even if the nested VM-Enter was not successful. For example, if VPID is disabled for L2 (via nested_vmx_transition_tlb_flush(), or if via the MSR load lists, as the SDM says:

If any MSR is being loaded in such a way that would architecturally require a TLB flush, the TLBs are updated so that, after VM entry, the logical processor will not use any translations that were cached before the transition.

The SDM is unclear about when the TLB flush should occur, and whether or not a failed VM entry would flush the TLB, so it is safer to always do the TLB flush in this case.

More concretely, KVM also updates the last VPID L1 used for L2 in nested_vmx_transition_tlb_flush() (i.e. last_vpid), even if the VM entry ultimately fails. With the current code, KVM could miss a TLB flush if L1 changes L2's VPID, then does a failed VM entry followed by a successful one, as the failed VM entry would update last_vpid but not actually flush the TLB. Servicing local TLB flushes on failed VM entries makes sure that the TLB is always flushed when last_vpid is updated.

---
 arch/x86/kvm/vmx/nested.c |    8 ++++++++
 1 file changed, 8 insertions(+)
arch/x86/kvm/vmx/nested.c +8 -0
@@ -3555,6 +3555,14 @@ enum nvmx_vmentry_status nested_vmx_ente
 vmentry_fail_vmexit_guest_mode:
 	if (vmcs12->cpu_based_vm_exec_control & CPU_BASED_USE_TSC_OFFSETTING)
 		vcpu->arch.tsc_offset -= vmcs12->tsc_offset;
+
+	/*
+	 * Handle any TLB flush requests that were queued for L2 if KVM made it
+	 * far enough along to switch to L2 context.  Note, loading host state
+	 * will generate any flushes for L1 required by VM-Exit.
+	 */
+	kvm_service_local_tlb_flush_requests(vcpu);
+
 	leave_guest_mode(vcpu);
 
 vmentry_fail_vmexit:
author maintainer
#430 lore raw

PATCH 430/1424 KVM: x86: hyper-v: Clamp stimer deadline to avoid livelock

In reply to Greg Kroah-Hartman

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

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

From: Carlos López <[email protected]>

commit 0ca49fbd2883cd53d32d85b50feef17fa04d0fbf upstream.

Fix an issue where userspace or the guest can program an Hyper-V synthetic timer to have a deadline in the past via integer overflow, preventing the CPU from making progress and triggering an RCU stall.

Hyper-V's SynIC exposes 4 per-vCPU synthetic timers to the guest, which are emulated by KVM. Each is programmed through the HV_X64_MSR_STIMERi_CONFIG and HV_X64_MSR_STIMERi_COUNT MSRs. Depending on CONFIG, COUNT represents either the absolute expiration time or the period of a periodic timer, both expressed in 100ns ticks. These timers may be set both by the guest (WRMSR) and the host (KVM_SET_MSRS).

When the timer is enabled, stimer_start() translates COUNT to an absolute monotonic deadline and arms an hrtimer. If COUNT is set to a value close to U64_MAX, the deadline calculation can overflow.

    ktime_add_ns(ktime_now, 100 * (stimer->exp_time - time_now))

This can result in a CPU livelock. stimer_start() arms the timer via hrtimer_start() with a deadline in the past, which causes it to immediately fire. The stimer callback then raises KVM_RQ_HV_STIMER, with the intention of causing KVM to deliver a synthetic interrupt on the next vCPU guest enter.

Then, once userspace issues KVM_RUN, vcpu_enter_guest() consumes the request, calling kvm_hv_process_stimers(). This would normally disable the timer via stimer_expiration() once the deadline is in the past. However, the deadline comparison is done between the KVM reference counter and stime->exp_time, which is a big value close to U64_MAX, so this never happens for a few thousand years.

kvm_hv_process_timers() then re-arms the timer via stimer_start(), since it was not disabled, which again fires immediately. Before entering the guest, kvm_vcpu_exit_request() checks kvm_request_pending(), which returns true due to the newly raised KVM_REQ_HV_STIMER. Then vcpu_enter_guest() aborts the guest entry, returning early into vcpu_run(), which loops back again into vcpu_enter_guest(), restarting the cycle.

Since there are no manual yields in this loop, a task with SCHED_FIFO may starve RCU grace-period kthreads, which exposes the stalls found by syzcaller:

    rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
    rcu:    (detected by 1, t=10502 jiffies, g=14269, q=1142 ncpus=2)
    rcu: All QSes seen, last rcu_preempt kthread activity 10500 (4294965239-4294954739), jiffies_till_next_fqs=1, root ->qsmask 0x0
    rcu: rcu_preempt kthread starved for 10500 jiffies! g14269 f0x2 RCU_GP_WAIT_FQS(5) ->state=0x0 ->cpu=0
    rcu:    Unless rcu_preempt kthread gets sufficient CPU time, OOM is now expected behavior.
        ( ... )
    Call Trace:
     <IRQ>
     __run_hrtimer kernel/time/hrtimer.c:1773 [inline]
     __hrtimer_run_queues+0x408/0xc30 kernel/time/hrtimer.c:1841
     hrtimer_interrupt+0x45b/0xaa0 kernel/time/hrtimer.c:1903
     local_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1045 [inline]
     __sysvec_apic_timer_interrupt+0x102/0x3e0 arch/x86/kernel/apic/apic.c:1062
     instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1056 [inline]
     sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1056
     </IRQ>
     <TASK>
     asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:697
    RIP: 0010:__raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:152 [inline]
    RIP: 0010:_raw_spin_unlock_irqrestore+0xa8/0x110 kernel/locking/spinlock.c:194
    Code: 74 05 e8 0b f4 5f f6 48 c7 44 24 20 00 00 00 00 9c 8f 44 24 20 f6 44 24 21 02 75 4f f7 c3 00 02 00 00 74 01 fb bf 01 00 00 00 <e8> 23 6b 27 f6 65 8b 05 7c 60 5a 07 85 c0 74 40 48 c7 04 24 0e 36
    RSP: 0018:ffffc900040a7320 EFLAGS: 00000206
    RAX: 5de15cb931505900 RBX: 0000000000000a06 RCX: 5de15cb931505900
    RDX: 0000000000000007 RSI: ffffffff8daa9dc3 RDI: 0000000000000001
    RBP: ffffc900040a73b0 R08: ffffffff8fc3d077 R09: 1ffffffff1f87a0e
    R10: dffffc0000000000 R11: fffffbfff1f87a0f R12: dffffc0000000000
    R13: 0000000000000000 R14: ffff8880b8628240 R15: 1ffff92000814e64
     hrtimer_start include/linux/hrtimer.h:259 [inline]
     stimer_start arch/x86/kvm/hyperv.c:682 [inline]
     kvm_hv_process_stimers+0xd0a/0x16a0 arch/x86/kvm/hyperv.c:893
     vcpu_enter_guest arch/x86/kvm/x86.c:11193 [inline]
     vcpu_run+0x2240/0x76b0 arch/x86/kvm/x86.c:11639
     kvm_arch_vcpu_ioctl_run+0x1148/0x1c90 arch/x86/kvm/x86.c:11984
     kvm_vcpu_ioctl+0x99a/0xed0 virt/kvm/kvm_main.c:4492
     vfs_ioctl fs/ioctl.c:51 [inline]
     __do_sys_ioctl fs/ioctl.c:597 [inline]
     __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583
     do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
     do_syscall_64+0xfa/0xf80 arch/x86/entry/syscall_64.c:94
     entry_SYSCALL_64_after_hwframe+0x77/0x7f
    RIP: 0033:0x7f635278f749
    Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 a8 ff ff ff f7 d8 64 89 01 48
    RSP: 002b:00007f635365c038 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
    RAX: ffffffffffffffda RBX: 00007f63529e5fa0 RCX: 00007f635278f749
    RDX: 0000000000000000 RSI: 000000000000ae80 RDI: 0000000000000005
    RBP: 00007f6352813f91 R08: 0000000000000000 R09: 0000000000000000
    R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
    R13: 00007f63529e6038 R14: 00007f63529e5fa0 R15: 00007ffd5b219358
     </TASK>

Fix this by clamping the deadline computation to KTIME_MAX, which preserves the intent of arming a timer very far in the future. ktime_add_safe() already does this type of clamping, so use it after checking that that multiplying by the 100ns time tick also does not overflow.

---
 arch/x86/kvm/hyperv.c |   25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)
arch/x86/kvm/hyperv.c +18 -7
@@ -623,6 +623,18 @@ static enum hrtimer_restart stimer_timer
 }
 
 /*
+ * Translate a stimer expiry given in 100ns reference ticks into an
+ * an absolute deadline. Saturates on overflow.
+ */
+static ktime_t stimer_add_delta(ktime_t now, u64 delta_100ns)
+{
+	if (delta_100ns >= KTIME_MAX / 100)
+		return KTIME_MAX;
+
+	return ktime_add_safe(now, 100 * delta_100ns);
+}
+
+/*
  * stimer_start() assumptions:
  * a) stimer->count is not equal to 0
  * b) stimer->config has HV_STIMER_ENABLE flag
@@ -631,6 +643,7 @@ static int stimer_start(struct kvm_vcpu_
 {
 	u64 time_now;
 	ktime_t ktime_now;
+	ktime_t deadline;
 
 	time_now = get_time_ref_counter(hv_stimer_to_vcpu(stimer)->kvm);
 	ktime_now = ktime_get();
@@ -653,10 +666,8 @@ static int stimer_start(struct kvm_vcpu_
 					stimer->index,
 					time_now, stimer->exp_time);
 
-		hrtimer_start(&stimer->timer,
-			      ktime_add_ns(ktime_now,
-					   100 * (stimer->exp_time - time_now)),
-			      HRTIMER_MODE_ABS);
+		deadline = stimer_add_delta(ktime_now, stimer->exp_time - time_now);
+		hrtimer_start(&stimer->timer, deadline, HRTIMER_MODE_ABS);
 		return 0;
 	}
 	stimer->exp_time = stimer->count;
@@ -675,9 +686,9 @@ static int stimer_start(struct kvm_vcpu_
 					   stimer->index,
 					   time_now, stimer->count);
 
-	hrtimer_start(&stimer->timer,
-		      ktime_add_ns(ktime_now, 100 * (stimer->count - time_now)),
-		      HRTIMER_MODE_ABS);
+	deadline = stimer_add_delta(ktime_now, stimer->count - time_now);
+	hrtimer_start(&stimer->timer, deadline, HRTIMER_MODE_ABS);
+
 	return 0;
 }
 
author maintainer
#431 lore raw

PATCH 431/1424 KVM: s390: Fix length check __import_wp_info()

In reply to Greg Kroah-Hartman

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

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

From: Christian Borntraeger <[email protected]>

commit 4c07680a467e2f7697245bcd11691bffb2a6f0ed upstream.

struct kvm_hw_breakpoint::len is a __u64 that is fully controlled by user space. This is then assigned to wp_info->len, which is an int. The bounds check is done on the truncated value while the allocation uses the untruncated one:

	wp_info->len = bp_data->len;
	[...]
	if (wp_info->len < 0 || wp_info->len > MAX_WP_SIZE)
		return -EINVAL;

	wp_info->old_data = kmalloc(bp_data->len, GFP_KERNEL_ACCOUNT);

Use the validated value for the allocation as intended. Without this fix userspace can trigger >4GB allocations which will fail and result in a WARN due to MAX_PAGE_ORDER.

Fixes 27291e2165b6 ("KVM: s390: hardware support for guest debugging")

Message-ID: <[email protected]>

---
 arch/s390/kvm/guestdbg.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/s390/kvm/guestdbg.c +1 -1
@@ -184,7 +184,7 @@ static int __import_wp_info(struct kvm_v
 	if (wp_info->len < 0 || wp_info->len > MAX_WP_SIZE)
 		return -EINVAL;
 
-	wp_info->old_data = kmalloc(bp_data->len, GFP_KERNEL_ACCOUNT);
+	wp_info->old_data = kmalloc(wp_info->len, GFP_KERNEL_ACCOUNT);
 	if (!wp_info->old_data)
 		return -ENOMEM;
 	/* try to backup the original value */
author maintainer
#432 lore raw

PATCH 432/1424 KVM: s390: Fix memory leak in guest debug handling

In reply to Greg Kroah-Hartman

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

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

From: Christian Borntraeger <[email protected]>

commit 121ea1de927c8b9bfdf53c31cad27b86d5de0293 upstream.

bp_data is freed only for the error case by kfree(bp_data). Every successful KVM_SET_GUEST_DEBUG will leak bp_data.

Fixes 27291e2165b6 ("KVM: s390: hardware support for guest debugging")

Message-ID: <[email protected]>

---
 arch/s390/kvm/guestdbg.c |    1 +
 1 file changed, 1 insertion(+)
arch/s390/kvm/guestdbg.c +1 -0
@@ -271,6 +271,7 @@ int kvm_s390_import_bp_data(struct kvm_v
 	vcpu->arch.guestdbg.hw_bp_info = bp_info;
 	vcpu->arch.guestdbg.nr_hw_wp = nr_wp;
 	vcpu->arch.guestdbg.hw_wp_info = wp_info;
+	kfree(bp_data);
 	return 0;
 error:
 	kfree(bp_data);
author maintainer
#433 lore raw

PATCH 433/1424 KVM: s390: Fix old_data leak in guest debug error path

In reply to Greg Kroah-Hartman

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

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

From: Christian Borntraeger <[email protected]>

commit aa9c8e8baf1e765fa65b93212522c636f25d846f upstream.

__import_wp_info() allocates a per-watchpoint old_data buffer to back up the original guest memory contents. If a later watchpoint of the same KVM_SET_GUEST_DEBUG request fails to import, kvm_s390_import_bp_data() jumps to the error label, which frees the wp_info array but not the old_data buffers of the entries that were imported successfully. Up to MAX_BP_COUNT - 1 buffers of up to MAX_WP_SIZE bytes are leaked per failed request, and the request can be repeated.

Create error handling for cleaning up all created old_data memory areas.

Fixes 27291e2165b6 ("KVM: s390: hardware support for guest debugging")

Message-ID: <[email protected]>

---
 arch/s390/kvm/guestdbg.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
arch/s390/kvm/guestdbg.c +5 -1
@@ -256,7 +256,7 @@ int kvm_s390_import_bp_data(struct kvm_v
 			ret = __import_wp_info(vcpu, &bp_data[i],
 					       &wp_info[nr_wp]);
 			if (ret)
-				goto error;
+				goto error_wp;
 			nr_wp++;
 			break;
 		case KVM_HW_BP:
@@ -273,6 +273,10 @@ int kvm_s390_import_bp_data(struct kvm_v
 	vcpu->arch.guestdbg.hw_wp_info = wp_info;
 	kfree(bp_data);
 	return 0;
+
+error_wp:
+	while (nr_wp--)
+		kfree(wp_info[nr_wp].old_data);
 error:
 	kfree(bp_data);
 	kfree(wp_info);
author maintainer
#434 lore raw

PATCH 434/1424 KVM: s390: Free guest debug data on vcpu destroy

In reply to Greg Kroah-Hartman

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

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

From: Christian Borntraeger <[email protected]>

commit e7f698b09d4a7c36b299acf680fc50fe868e2bcd upstream.

kvm_s390_clear_bp_data() is only called from kvm_arch_vcpu_ioctl_set_guest_debug(), i.e. when user space changes or disables debugging. A vCPU that is destroyed while hardware breakpoints are still armed - the normal case when the VMM just exits or crashes - leaks hw_bp_info, hw_wp_info and all old_data buffers, since generic KVM frees the vCPU right after kvm_arch_vcpu_destroy().

That is bounded by MAX_BP_COUNT entries, so roughly 8 KiB per vCPU, but it is unbounded over VM lifetimes. The allocations are GFP_KERNEL_ACCOUNT, so the charge also outlives the exiting process and pins dying memcgs.

Fix by clearing the debug data on vCPU destruction. Calling it unconditionally is fine: struct kvm_vcpu is zero allocated, so for a vCPU that never enabled debugging the counters are 0 and the pointers NULL.

Fixes 27291e2165b6 ("KVM: s390: hardware support for guest debugging")

Message-ID: <[email protected]>

---
 arch/s390/kvm/kvm-s390.c |    1 +
 1 file changed, 1 insertion(+)
arch/s390/kvm/kvm-s390.c +1 -0
@@ -3437,6 +3437,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vc
 	trace_kvm_s390_destroy_vcpu(vcpu->vcpu_id);
 	kvm_s390_clear_local_irqs(vcpu);
 	kvm_clear_async_pf_completion_queue(vcpu);
+	kvm_s390_clear_bp_data(vcpu);
 	if (!kvm_is_ucontrol(vcpu->kvm))
 		sca_del_vcpu(vcpu);
 	kvm_s390_update_topology_change_report(vcpu->kvm, 1);
author maintainer
#435 lore raw

PATCH 435/1424 KVM: s390: Take srcu when importing watchpoint data

In reply to Greg Kroah-Hartman

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

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

From: Christian Borntraeger <[email protected]>

commit a4e482def8533ebace517d9f67f1465841b1f982 upstream.

__import_wp_info() backs up the original guest memory contents of a watchpoint with read_guest_abs(), which is kvm_read_guest() and therefore resolves the memslot via __kvm_memslots(). That requires kvm->srcu (or kvm->slots_lock) to be held, otherwise a concurrent memslot update can free the memslots array under us once its SRCU grace period has elapsed.

As this is not fast path, following lock ordering (mutex first, then srcu) take the big hammer and hold the srcu for the full import.

Fixes 27291e2165b6 ("KVM: s390: hardware support for guest debugging")

Message-ID: <[email protected]>

---
 arch/s390/kvm/kvm-s390.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
arch/s390/kvm/kvm-s390.c +4 -2
@@ -4389,8 +4389,10 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(
 		/* enforce guest PER */
 		kvm_s390_set_cpuflags(vcpu, CPUSTAT_P);
 
-		if (dbg->control & KVM_GUESTDBG_USE_HW_BP)
-			rc = kvm_s390_import_bp_data(vcpu, dbg);
+		if (dbg->control & KVM_GUESTDBG_USE_HW_BP) {
+			scoped_guard(srcu, &vcpu->kvm->srcu)
+				rc = kvm_s390_import_bp_data(vcpu, dbg);
+		}
 	} else {
 		kvm_s390_clear_cpuflags(vcpu, CPUSTAT_P);
 		vcpu->arch.guestdbg.last_bp = 0;
author maintainer
#436 lore raw

PATCH 436/1424 KVM: s390: Zero initialize irq in reinject_machine_check

In reply to Greg Kroah-Hartman

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

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

From: Christian Borntraeger <[email protected]>

commit b239410c7653ff6781d4cf1d63cfc52a1bb71788 upstream.

kvm_s390_reinject_machine_check() fills cr14, mcic, ext_damage_code and failing_storage_address of the on-stack struct kvm_s390_irq, but struct kvm_s390_mchk_info also has a pad word and a 16 byte fixed_logout array. struct mcck_volatile_info carries no logout data, so there is nothing to copy there and both stay whatever was on the stack.

__inject_mchk() then memcpy()s fixed_logout into the vcpu local interrupt state unconditionally. This will reach the guest during deliver and userspace during migration.

Reflecting zeroes is the correct behaviour here, as KVM has no logout data for a reinjected machine check.

This needs a host machine check while the cpu is in SIE so not trivial to trigger.

Fixes 4d62fcc0b692 ("KVM: s390: Inject machine check into the guest")

Message-ID: <[email protected]>

---
 arch/s390/kvm/interrupt.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/s390/kvm/interrupt.c +1 -1
@@ -2863,7 +2863,7 @@ void kvm_s390_reinject_machine_check(str
 				     struct mcck_volatile_info *mcck_info)
 {
 	struct kvm_s390_interrupt_info inti;
-	struct kvm_s390_irq irq;
+	struct kvm_s390_irq irq = {};
 	struct kvm_s390_mchk_info *mchk;
 	union mci mci;
 	__u64 cr14 = 0;         /* upper bits are not used */
author maintainer
#437 lore raw

PATCH 437/1424 KVM: s390: pv: Fix rc/rrc offset for PVM_DUMP

In reply to Greg Kroah-Hartman

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

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

From: Christian Borntraeger <[email protected]>

commit 1e3c8e7b3465fb8a49d3623d2d0f449c0b5b48f3 upstream.

The rc/rrc value is copied to the cmd location of the cmd in the kvm_pv_cmd structure. Fix the offset.

Fixes 8aba09588d2a ("KVM: s390: Add CPU dump functionality")

Message-ID: <[email protected]>

---
 arch/s390/kvm/kvm-s390.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/s390/kvm/kvm-s390.c +1 -1
@@ -5728,7 +5728,7 @@ long kvm_arch_vcpu_ioctl(struct file *fi
 		r = kvm_s390_handle_pv_vcpu_dump(vcpu, &cmd);
 
 		/* Always copy over UV rc / rrc data */
-		if (copy_to_user((__u8 __user *)argp, &cmd.rc,
+		if (copy_to_user(argp + offsetof(struct kvm_pv_cmd, rc), &cmd.rc,
 				 sizeof(cmd.rc) + sizeof(cmd.rrc)))
 			r = -EFAULT;
 		break;
author maintainer
#438 lore raw

PATCH 438/1424 KVM: s390: Restore sigset on error path

In reply to Greg Kroah-Hartman

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

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

From: Christian Borntraeger <[email protected]>

commit c44d36d8e6501c4934412d9014e5e02da9efdb8f upstream.

kvm_sigset_activate() installs vcpu->sigset via sigprocmask() and stashes the caller's mask in current->real_blocked; only kvm_sigset_deactivate() restores it.

For KVM_RUN on a STOPPED vcpu the error path will not restore the userspace mask. Re-arrange the error handling to also restore the signal mask.

Fixes 6352e4d2dd9a3 ("KVM: s390: implement KVM_(S|G)ET_MP_STATE for user space state control")

Message-ID: <[email protected]>

---
 arch/s390/kvm/kvm-s390.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
arch/s390/kvm/kvm-s390.c +4 -2
@@ -5078,7 +5078,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_v
 		pr_err_ratelimited("can't run stopped vcpu %d\n",
 				   vcpu->vcpu_id);
 		rc = -EINVAL;
-		goto out;
+		goto out_sigset;
 	}
 
 	sync_regs(vcpu);
@@ -5105,9 +5105,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_v
 	disable_cpu_timer_accounting(vcpu);
 	store_regs(vcpu);
 
+	vcpu->stat.exit_userspace++;
+
+out_sigset:
 	kvm_sigset_deactivate(vcpu);
 
-	vcpu->stat.exit_userspace++;
 out:
 	vcpu_put(vcpu);
 	return rc;
author maintainer
#439 lore raw

PATCH 439/1424 LoongArch: Do not save/restore percpu base register in rethook trampoline

In reply to Greg Kroah-Hartman

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

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

From: Wentao Guan <[email protected]>

commit c3f2feace5e4f4b01b68b9f947b19adb4155c32e upstream.

The rethook trampoline saves $r21 ($u0), the percpu base, into its frame at entry and restores it at exit. Inbetween rethook_trampoline_handler() may schedule via preempt_enable_notrace().

If the task migrates to another CPU, the frame's $r21 holds the old CPU's percpu base, and restoring it poisons $r21 on the new CPU. Until the next user->kernel transition heals $r21, all this_cpu_*() accesses (runqueues, RCU per-CPU data, timer tick programming, FPU ownership) hit the wrong CPU's percpu area.

Under kretprobe-heavy preemptible load this can corrupt scheduler and timer state: scheduling-while-atomic splats, wrong-CPU RCU warnings, WARN_ON_ONCE(rq != this_rq()) in nohz_balance_exit_idle(), and CPUs parking in the idle loop with the constant timer never re-armed (hard lockup). Reproduces on a Loongson-3A6000 with kretprobes on VFS paths plus heavy file churn (OS install / unsquashfs).

By convention $r21 always holds the current CPU's percpu base in kernel
mode: SAVE_SOME() at exception entry reloads it only when coming from user mode, and RESTORE_SOME() restores it only when returning to user mode; the context-switch path never writes it. Therefore the live $r21 at trampoline exit is already correct, and nothing inbetween can change it legitimately (kernel C code cannot write a global register variable). The same flaw existed even in the pre-rethook kretprobe trampoline since v6.3; it was carried over when rethook replaced it. Drop both the save and the restore here. Drop the restore is enough to solve the issue, and drop the save is to keep the code tidy and no need to clear it.

Fixes 3f5536860086d ("LoongArch: Add kretprobes support")
Assisted-by Kimi:Kimi-K3 # debug and root-cause analysis
---
 arch/loongarch/kernel/rethook_trampoline.S | 2 --
 1 file changed, 2 deletions(-)
arch/loongarch/kernel/rethook_trampoline.S +0 -2
index 2e009fbea53f..160189444684 100644
@@ -24,7 +24,6 @@
 	cfi_st	t6, PT_R18
 	cfi_st	t7, PT_R19
 	cfi_st	t8, PT_R20
-	cfi_st	u0, PT_R21
 	cfi_st	fp, PT_R22
 	cfi_st	s0, PT_R23
 	cfi_st	s1, PT_R24
@@ -59,7 +58,6 @@
 	cfi_ld	t6, PT_R18
 	cfi_ld	t7, PT_R19
 	cfi_ld	t8, PT_R20
-	cfi_ld	u0, PT_R21
 	cfi_ld	fp, PT_R22
 	cfi_ld	s0, PT_R23
 	cfi_ld	s1, PT_R24
Signature · 1 line
2.55.0
author maintainer
#440 lore raw

PATCH 440/1424 LoongArch: Avoid preempt count underflow without probe

In reply to Greg Kroah-Hartman

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

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

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

commit 72ce4b24676e8b3b75376c4c559dd81c1ac52d5a upstream.

LoongArch uses break 11 for the breakpoint placed after an instruction that Kprobes executes out of line. Since userspace can issue the same break instruction, do_bp() can reach kprobe_singlestep_handler() when there is no current probe.

The handler actually returns false in this case, but it first calls preempt_enable_no_resched(). The corresponding preempt_disable() is done by kprobe_breakpoint_handler() on a real Kprobe hit, so it has not run here. As a result, an ordinary userspace breakpoint (code 11) underflows the current task's preempt count.

This also makes in_interrupt() return true until the task schedules. One visible consequence is the socket cgroup attribution: cgroup_sk_alloc() treats the allocation as interrupt context and assigns the socket to the root cgroup. A socket opened from the SIGTRAP handler can then avoid a BPF_CGROUP_INET_SOCK_CREATE policy attached to the task's own cgroup.

Return as soon as kprobe_running() reports no active probe.

The same check has appeared in [PATCH v10 2/4] of the original LoongArch Kprobes series, but was dropped before the feature reached mainline.

Fixes 6d4cc40fb5f5 ("LoongArch: Add kprobes support")
Assisted-by Codex:gpt-5
---
 arch/loongarch/kernel/kprobes.c |    3 +++
 1 file changed, 3 insertions(+)
arch/loongarch/kernel/kprobes.c +3 -0
@@ -273,6 +273,9 @@ bool kprobe_singlestep_handler(struct pt
 	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
 	unsigned long addr = instruction_pointer(regs);
 
+	if (!cur)
+		return false;
+
 	if (cur && (kcb->kprobe_status & (KPROBE_HIT_SS | KPROBE_REENTER)) &&
 	    ((unsigned long)&cur->ainsn.insn[1] == addr)) {
 		restore_local_irqflag(kcb, regs);
author maintainer
#441 lore raw

PATCH 441/1424 media: airspy: use vb2_video_unregister_device() on disconnect to fix NULL deref

In reply to Greg Kroah-Hartman

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

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

From: Valery Borovsky <[email protected]>

commit 2f378dc45e685fc825d2dd08e7864666d6fcc009 upstream.

airspy_disconnect() clears s->udev under v4l2_lock, but airspy_stop_streaming() unconditionally calls airspy_ctrl_msg() and airspy_free_stream_bufs() afterwards. If a streaming user closes the device after disconnect, stop_streaming() runs and dereferences the NULL s->udev:

airspy_stop_streaming()

    airspy_ctrl_msg(s, CMD_RECEIVER_MODE, 0, 0, NULL, 0)
      usb_sndctrlpipe(s->udev, 0)         /* NULL deref */
    airspy_free_stream_bufs(s)
      usb_free_coherent(s->udev, ...)     /* NULL deref */

The airspy driver uses vb2_fop_release() in its file_operations, so replace video_unregister_device(&s->vdev) with vb2_video_unregister_device(&s->vdev) and move it before clearing s->udev. vb2_video_unregister_device() releases the vb2 queue, which synchronously runs airspy_stop_streaming() if streaming is active, so the URBs, coherent DMA stream buffers and the hardware stop control message all execute while s->udev is still valid.

vb2_video_unregister_device() locks vdev->queue->lock (vb_queue_lock) internally, and stop_streaming() locks v4l2_lock, so the previous outer mutex_lock(&s->vb_queue_lock) / mutex_lock(&s->v4l2_lock) pair around the unregister sequence would self-deadlock and has been removed. A short v4l2_lock critical section around s->udev = NULL remains so any ioctl path that still holds the file descriptor sees coherent state.

Issue identified by automated review of the INV-003 series at
https://sashiko.dev/

Fixes 634fe5033951 ("[media] airspy: AirSpy SDR driver")
---
 drivers/media/usb/airspy/airspy.c |   17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)
drivers/media/usb/airspy/airspy.c +12 -5
@@ -464,14 +464,21 @@ static void airspy_disconnect(struct usb
 
 	dev_dbg(s->dev, "\n");
 
-	mutex_lock(&s->vb_queue_lock);
+	/*
+	 * vb2_video_unregister_device() releases the vb2 queue, which
+	 * triggers airspy_stop_streaming() if streaming is active.
+	 * stop_streaming() dereferences s->udev via airspy_ctrl_msg() and
+	 * airspy_free_stream_bufs(), so it must run before s->udev is
+	 * cleared. vb2_video_unregister_device() locks vb_queue_lock
+	 * internally and stop_streaming() locks v4l2_lock, so neither may
+	 * be held by the caller.
+	 */
+	v4l2_device_disconnect(&s->v4l2_dev);
+	vb2_video_unregister_device(&s->vdev);
+
 	mutex_lock(&s->v4l2_lock);
-	/* No need to keep the urbs around after disconnection */
 	s->udev = NULL;
-	v4l2_device_disconnect(&s->v4l2_dev);
-	video_unregister_device(&s->vdev);
 	mutex_unlock(&s->v4l2_lock);
-	mutex_unlock(&s->vb_queue_lock);
 
 	v4l2_device_put(&s->v4l2_dev);
 }
author maintainer
#442 lore raw

PATCH 442/1424 media: cec: core: Fix kmemleak due to missed rc_free_device() call

In reply to Greg Kroah-Hartman

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

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

From: Jonas Karlman <[email protected]>

commit a24ba0653f7154e671dc8d2bf64682ab2d042792 upstream.

The commit dccc0c3ddf8f ("media: rc: fix race between unregister and urb/irq callbacks") removed the implicit call to rc_free_device() from rc_unregister_device(). However, the commit missed to remove the NULL assignment of adap->rc that is now causing rc_free_device() to never be called on an allocated rc device.

kmemleak reports following after e.g. dw-hdmi unbind:

unreferenced object 0xffff00010ac10000 (size 4096):
comm "kworker/u16:1", pid 39, jiffies 4294897739 hex dump (first 32 bytes):

    20 23 4b 0a 01 00 ff ff 08 00 c1 0a 01 00 ff ff   #K.............
    08 00 c1 0a 01 00 ff ff 00 00 00 00 00 00 00 00  ................
  backtrace (crc e11baccc):
    kmemleak_alloc+0x38/0x44
    __kmalloc_cache_noprof+0x4a8/0x5e0
    rc_allocate_device+0x48/0x2a0
    cec_allocate_adapter+0x3ac/0x800
    dw_hdmi_cec_probe+0x264/0x634
    platform_probe+0xc0/0x188
    really_probe+0x4a4/0x8e0
    __driver_probe_device+0x2f8/0x440
    driver_probe_device+0x60/0x160
    __device_attach_driver+0x1a0/0x2a0
    bus_for_each_drv+0x100/0x1a0
    __device_attach+0x174/0x350
    device_initial_probe+0x90/0xb0
    bus_probe_device+0x4c/0x120
    device_add+0xdec/0x116c
    platform_device_add+0x354/0x598

Remove the assignment of adap->rc to NULL to let cec_delete_adapter() free the allocated rc device after last user of the cec device exits to fix the kmemleak.

Fixes dccc0c3ddf8f ("media: rc: fix race between unregister and urb/irq callbacks")
---
 drivers/media/cec/core/cec-core.c |    2 --
 1 file changed, 2 deletions(-)
drivers/media/cec/core/cec-core.c +0 -2
@@ -399,9 +399,7 @@ void cec_unregister_adapter(struct cec_a
 		return;
 
 #ifdef CONFIG_MEDIA_CEC_RC
-	/* Note: rc_unregister also calls rc_free */
 	rc_unregister_device(adap->rc);
-	adap->rc = NULL;
 #endif
 	debugfs_remove_recursive(adap->cec_dir);
 #ifdef CONFIG_CEC_NOTIFIER
author maintainer
#443 lore raw

PATCH 443/1424 media: cec: meson: ao-cec-g12a: name the CEC core regmap to avoid debugfs clash

In reply to Greg Kroah-Hartman

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

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

From: Yi Ding <[email protected]>

commit 172c5a7d81deb922ddedd1bc920751c7fed6c43c upstream.

The driver registers two regmaps on the same platform device: an MMIO regmap for the AO CEC registers, and an indirect regmap (using reg_read()/reg_write() callbacks) for the CEC controller core registers. Neither regmap_config sets a .name, so both default their debugfs directory to the device name and collide:

debugfs: 'ff800280.cec' already exists in 'regmap'

Because of the clash the second regmap's debugfs directory fails to register, so its registers can no longer be inspected via debugfs.

Give the indirect CEC core regmap a distinct name. The two debugfs directories then become "<dev>.cec" and "<dev>.cec-core". This only affects debugfs naming; register access is unchanged.

Tested on an ODROID-N2 (Amlogic S922X): the warning is gone and both /sys/kernel/debug/regmap/ff800280.cec and ff800280.cec-core are present.

Fixes b7778c46683c ("media: platform: meson: Add Amlogic Meson G12A AO CEC Controller driver")
Assisted-by Claude:claude-opus-4-8
---
 drivers/media/cec/platform/meson/ao-cec-g12a.c |    1 +
 1 file changed, 1 insertion(+)
drivers/media/cec/platform/meson/ao-cec-g12a.c +1 -0
@@ -405,6 +405,7 @@ static int meson_ao_cec_g12a_write(void
 }
 
 static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = {
+	.name = "core",
 	.reg_bits = 8,
 	.val_bits = 8,
 	.reg_read = meson_ao_cec_g12a_read,
author maintainer
#444 lore raw

PATCH 444/1424 media: cec: Serialize exclusive follower delivery

In reply to Greg Kroah-Hartman

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

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

From: Ruoyu Wang <[email protected]>

commit 1924d0788caa6c66fd320dd4704fae99487fd2c7 upstream.

cec_receive_notify() reads the exclusive follower pointer without the adapter lock. Serialize the no-follower check and message delivery against mode changes and release.

Fixes 9881fe0ca187 ("[media] cec: add HDMI CEC framework (adapter)")
---
 drivers/media/cec/core/cec-adap.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/media/cec/core/cec-adap.c +7 -1
@@ -2155,9 +2155,13 @@ static int cec_receive_notify(struct cec
 		 * Unprocessed messages are aborted if userspace isn't doing
 		 * any processing either.
 		 */
+		mutex_lock(&adap->lock);
 		if (!is_broadcast && !is_reply && !adap->follower_cnt &&
-		    !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT)
+		    !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT) {
+			mutex_unlock(&adap->lock);
 			return cec_feature_abort(adap, msg);
+		}
+		mutex_unlock(&adap->lock);
 		break;
 	}
 
@@ -2170,10 +2174,12 @@ skip_processing:
 	 * Send to the exclusive follower if there is one, otherwise send
 	 * to all followers.
 	 */
+	mutex_lock(&adap->lock);
 	if (adap->cec_follower)
 		cec_queue_msg_fh(adap->cec_follower, msg);
 	else
 		cec_queue_msg_followers(adap, msg);
+	mutex_unlock(&adap->lock);
 	return 0;
 }
 
author maintainer
#445 lore raw

PATCH 445/1424 media: cedrus: fix memory leak in cedrus_init_ctrls()

In reply to Greg Kroah-Hartman

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

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

From: Dawei Feng <[email protected]>

commit 9df2fbe563194da1967a5db083442186c1323efe upstream.

In cedrus_init_ctrls(), the V4L2 control handler is initialized before allocating memory for ctx->ctrls. If this allocation fails, the function returns -ENOMEM without freeing the previously allocated handler resources, leading to a memory leak.

Fix this by calling v4l2_ctrl_handler_free() on the ctx->ctrls allocation failure path.

The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1.1.

An x86_64 allyesconfig build showed no new warnings. As we do not have an Allwinner SoC or board with a Cedrus VPU available to test with, no runtime testing was able to be performed.

---
 drivers/staging/media/sunxi/cedrus/cedrus.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/staging/media/sunxi/cedrus/cedrus.c +3 -1
@@ -285,8 +285,10 @@ static int cedrus_init_ctrls(struct cedr
 	ctrl_size = sizeof(ctrl) * CEDRUS_CONTROLS_COUNT + 1;
 
 	ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL);
-	if (!ctx->ctrls)
+	if (!ctx->ctrls) {
+		v4l2_ctrl_handler_free(hdl);
 		return -ENOMEM;
+	}
 
 	j = 0;
 	for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) {
author maintainer
#446 lore raw

PATCH 446/1424 media: cobalt: Avoid freeing ALSA private data twice

In reply to Greg Kroah-Hartman

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

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

From: Ruoyu Wang <[email protected]>

commit 3a7d6b9c4cb5ac18cbd3f1c7f8c7b159c42ba0b1 upstream.

snd_cobalt_card_create() stores cobsc in sc->private_data and installs snd_cobalt_card_private_free() as sc->private_free. From that point, snd_card_free(sc) releases cobsc through the ALSA card cleanup path.

If cobalt_alsa_init() fails after snd_cobalt_card_create(), the err_exit_free path calls snd_card_free(sc) and then kfree(cobsc). That second free releases the same object again.

Remove the explicit kfree(cobsc) and leave ownership with the ALSA card.

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

Fixes 85756a069c55 ("[media] cobalt: add new driver")
---
 drivers/media/pci/cobalt/cobalt-alsa-main.c |    1 -
 1 file changed, 1 deletion(-)
drivers/media/pci/cobalt/cobalt-alsa-main.c +0 -1
@@ -135,7 +135,6 @@ int cobalt_alsa_init(struct cobalt_strea
 err_exit_free:
 	if (sc != NULL)
 		snd_card_free(sc);
-	kfree(cobsc);
 err_exit:
 	return ret;
 }
author maintainer
#447 lore raw

PATCH 447/1424 media: cx231xx: reject geometry changes while the VBI queue is busy

In reply to Greg Kroah-Hartman

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

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

From: Bryam Vargas <[email protected]>

commit 627a121c15fe05a541f44d86016294b80bada75d upstream.

vidioc_s_fmt_vid_cap() and vidioc_s_std() change the device-wide dev->width / dev->norm but only refuse the change when the *video* queue (dev->vidq) is busy. The VBI queue (dev->vbiq) shares that same geometry:
cx231xx_init_vbi_isoc() latches dma_q->lines_per_field from dev->norm, the VBI videobuf2 plane is sized from dev->width / dev->norm in vbi_queue_setup() and vbi_buf_prepare(), and cx231xx_do_vbi_copy() then recomputes the destination offset from the *live* dev->width and the latched lines_per_field on every URB completion:

	offset = lines_completed * (dev->width << 1) + ...;
	if (dma_q->current_field == 2)
		offset += dev->width * 2 * dma_q->lines_per_field;
	memcpy(plane + offset, p_buffer, lencopy);

Because the VBI node shares video_ioctl_ops with the video node, an application can size a small VBI plane (REQBUFS/QBUF with a small width, or with the NTSC standard), then enlarge dev->width (or switch dev->norm to PAL) through the video node while the VBI stream is running -- the change is allowed because only dev->vidq is checked -- and let the device deliver a field-2 VBI payload. cx231xx_do_vbi_copy() now computes the offset with the larger geometry and memcpy()s past the end of the smaller plane that was already allocated, a heap out-of-bounds write whose offset is attacker-chosen and whose contents come from the device. The per-field guard in cx231xx_copy_vbi_line() does not help: it bounds the copy against the latched lines_per_field, not the plane's real capacity, and vb2 does not re-run buf_prepare() for an already prepared buffer.

Refuse the format/standard change when the VBI queue is busy as well, so the geometry cannot change underneath an allocated VBI buffer.

Fixes 7c617138b825 ("media: cx231xx: convert to the vb2 framework")
---
 drivers/media/usb/cx231xx/cx231xx-video.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/usb/cx231xx/cx231xx-video.c +2 -2
@@ -899,7 +899,7 @@ static int vidioc_s_fmt_vid_cap(struct f
 	if (rc)
 		return rc;
 
-	if (vb2_is_busy(&dev->vidq)) {
+	if (vb2_is_busy(&dev->vidq) || vb2_is_busy(&dev->vbiq)) {
 		dev_err(dev->dev, "%s: queue busy\n", __func__);
 		return -EBUSY;
 	}
@@ -934,7 +934,7 @@ static int vidioc_s_std(struct file *fil
 	if (dev->norm == norm)
 		return 0;
 
-	if (vb2_is_busy(&dev->vidq))
+	if (vb2_is_busy(&dev->vidq) || vb2_is_busy(&dev->vbiq))
 		return -EBUSY;
 
 	dev->norm = norm;
author maintainer
#448 lore raw

PATCH 448/1424 media: cx23885: cancel NetUP CI work before teardown

In reply to Greg Kroah-Hartman

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

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

From: Fan Wu <[email protected]>

commit 4e143d662ca94888b494b2427fc9e34494eb933a upstream.

netup_ci_exit() frees a netup_ci_state while its work item, netup_read_ci_status(), may still be pending or running on the system workqueue. The worker obtains the state with container_of() and dereferences it, so it must not outlive the state.

netup_ci_init() queues the initial status read, and CI GPIO interrupts subsequently queue the same work from netup_ci_slot_status(). During remove, cx23885_finidev() calls free_irq() before the CI device is unregistered. free_irq() prevents further IRQ handlers from running, but does not drain work queued previously, so the worker can run after netup_ci_exit() frees the state.

Call cancel_work_sync() before dvb_ca_en50221_release() and kfree().

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

Fixes c184dcd28233 ("V4L/DVB (10798): Add CIMax(R) SP2 Common Interface code for NetUP Dual DVB-S2 CI card")
Assisted-by Codex:gpt-5.6
---
 drivers/media/pci/cx23885/cimax2.c |    1 +
 1 file changed, 1 insertion(+)
drivers/media/pci/cx23885/cimax2.c +1 -0
@@ -528,6 +528,7 @@ void netup_ci_exit(struct cx23885_tsport
 	if (NULL == state->ca.data)
 		return;
 
+	cancel_work_sync(&state->work);
 	dvb_ca_en50221_release(&state->ca);
 	kfree(state);
 }
author maintainer
#449 lore raw

PATCH 449/1424 media: em28xx: defer audio-only extension registration

In reply to Greg Kroah-Hartman

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

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

From: Diego Fernando Mancera Gomez <[email protected]>

commit 95f76f51937fdfb0fc1e14cae606b1ef574a56f3 upstream.

The audio-only path registers extensions while probing the primary device. For a dual-TS board, this happens before dev_next is created. The duplicate device inherits is_audio_only and is then independently inserted into em28xx_devlist.

The list is intended to contain only primary devices: extension operations reach the secondary device through dev_next. The independently linked secondary can be freed during disconnect while its list node remains reachable, resulting in a use-after-free.

Defer audio-only extension registration to the module-request work item. It runs only after probing has completed construction of the optional secondary device, so only the primary is registered and extension callbacks reach the secondary through dev_next.

---
 drivers/media/usb/em28xx/em28xx-cards.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/media/usb/em28xx/em28xx-cards.c +1 -2
@@ -3396,6 +3396,7 @@ static void request_module_async(struct
 	 * intf. Don't register extensions twice on those devices.
 	 */
 	if (dev->is_audio_only) {
+		em28xx_init_extension(dev);
 #if defined(CONFIG_MODULES) && defined(MODULE)
 		request_module("em28xx-alsa");
 #endif
@@ -3624,8 +3625,6 @@ static int em28xx_init_dev(struct em28xx
 			retval = -ENODEV;
 			goto err_deinit_media;
 		}
-		em28xx_init_extension(dev);
-
 		return 0;
 	}
 
author maintainer
#450 lore raw

PATCH 450/1424 media: em28xx: fix use-after-free of dev_next->devlist on disconnect

In reply to Greg Kroah-Hartman

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

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

From: Jiangong.Han <[email protected]>

commit 826915b6b65e2d3251e7248ea54289a22d748c84 upstream.

When a device with has_dual_ts=1 is probed and the is_audio_only path is taken, both dev and dev->dev_next are added to the global em28xx_devlist via em28xx_init_extension(). However, during disconnect, em28xx_close_extension(dev) only calls list_del(&dev->devlist), leaving dev->dev_next->devlist still linked in the global list. When dev_next is subsequently freed via kref_put(), its devlist entry becomes a dangling pointer in em28xx_devlist. The next device probe that calls em28xx_init_extension() triggers a list corruption BUG when list_add_tail detects the freed node.

This bug was exposed by commit a368ecde8a50 ("USB: core: Fix duplicate endpoint bug by clearing reserved bits in the descriptor") which clears reserved bits in bEndpointAddress during endpoint parsing. This causes fuzzed endpoint addresses like 0xf3 to be normalized to 0x83, which em28xx interprets as a vendor audio endpoint, enabling the is_audio_only + has_dual_ts code path that was previously unreachable with such descriptors.

Fix this by removing dev->dev_next->devlist from the global list in em28xx_close_extension() before the device is freed.

Fixes f410b4093fdd ("media: em28xx: split up em28xx_dvb_init to reduce stack size")
Reported-by [email protected]
---
 drivers/media/usb/em28xx/em28xx-core.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/media/usb/em28xx/em28xx-core.c +2 -0
@@ -1133,6 +1133,8 @@ void em28xx_close_extension(struct em28x
 			ops->fini(dev);
 		}
 	}
+	if (dev->dev_next)
+		list_del(&dev->dev_next->devlist);
 	list_del(&dev->devlist);
 	mutex_unlock(&em28xx_devlist_mutex);
 }
author maintainer
#451 lore raw

PATCH 451/1424 media: go7007: defer the ALSA v4l2 put until card release

In reply to Greg Kroah-Hartman

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

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

From: Shuangpeng Bai <[email protected]>

commit 1bd456afeb8a515137e567967069fce6f8fcd23e upstream.

go7007_snd_init() already takes a v4l2_device reference for the ALSA side, but go7007_snd_remove() drops it immediately after calling snd_card_free_when_closed().

That is too early when a userspace process still has the capture PCM open. The ALSA card and its PCM callbacks remain alive until the last file is closed, so the release path can still reach struct go7007 through pcm->private_data and call go7007_snd_hw_free() after the V4L2 release path has freed the object.

Move the matching v4l2_device_put() to the ALSA card private_free callback so the existing ALSA reference covers the whole deferred card lifetime.

Fixes d5d3a7cc127d ("[media] go7007: fix unregister/disconnect handling")
---
 drivers/media/usb/go7007/snd-go7007.c |   10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/media/usb/go7007/snd-go7007.c +9 -1
@@ -195,6 +195,13 @@ static const struct snd_device_ops go700
 	.dev_free	= go7007_snd_free,
 };
 
+static void go7007_snd_card_free(struct snd_card *card)
+{
+	struct go7007 *go = card->private_data;
+
+	v4l2_device_put(&go->v4l2_dev);
+}
+
 int go7007_snd_init(struct go7007 *go)
 {
 	static int dev;
@@ -245,6 +252,8 @@ int go7007_snd_init(struct go7007 *go)
 	gosnd->substream = NULL;
 	go->snd_context = gosnd;
 	v4l2_device_get(&go->v4l2_dev);
+	gosnd->card->private_data = go;
+	gosnd->card->private_free = go7007_snd_card_free;
 	++dev;
 
 	return 0;
@@ -263,7 +272,6 @@ int go7007_snd_remove(struct go7007 *go)
 
 	snd_card_disconnect(gosnd->card);
 	snd_card_free_when_closed(gosnd->card);
-	v4l2_device_put(&go->v4l2_dev);
 	return 0;
 }
 EXPORT_SYMBOL(go7007_snd_remove);
author maintainer
#452 lore raw

PATCH 452/1424 media: i2c: ov02a10: fix endpoint parsing use-after-free

In reply to Greg Kroah-Hartman

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

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

From: Biren Pandya <[email protected]>

commit 94971ba0592ca77ec99b292226a4b398763315b8 upstream.

The ov02a10_check_hwcfg() function calls fwnode_handle_put(ep) immediately after allocating and parsing the endpoint. However, it subsequently calls fwnode_property_read_u32() using the same 'ep' handle, leading to a potential use-after-free.

Additionally, reading the optional 'ovti,mipi-clock-voltage' property used to overwrite the 'ret' variable. If the property was missing, 'ret' would become negative, and this failure code would be incorrectly returned at the end of the function, causing probe to fail entirely.

Fix the use-after-free by moving fwnode_property_read_u32() before the endpoint is parsed and freed. Avoid the error leak by not assigning the result of fwnode_property_read_u32() to 'ret'.

Fixes 91807efbe8ec ("media: i2c: add OV02A10 image sensor driver")
---
 drivers/media/i2c/ov02a10.c |   12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)
drivers/media/i2c/ov02a10.c +5 -7
@@ -821,18 +821,16 @@ static int ov02a10_check_hwcfg(struct de
 	if (!ep)
 		return -ENXIO;
 
+	/* Optional indication of MIPI clock voltage unit */
+	if (!fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage",
+				      &clk_volt))
+		ov02a10->mipi_clock_voltage = clk_volt;
+
 	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
 	fwnode_handle_put(ep);
 	if (ret)
 		return ret;
 
-	/* Optional indication of MIPI clock voltage unit */
-	ret = fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage",
-				       &clk_volt);
-
-	if (!ret)
-		ov02a10->mipi_clock_voltage = clk_volt;
-
 	for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) {
 		for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) {
 			if (link_freq_menu_items[i] ==
author maintainer
#453 lore raw

PATCH 453/1424 media: i2c: ov7740: fix use-after-destroy in remove

In reply to Greg Kroah-Hartman

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

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

From: Biren Pandya <[email protected]>

commit 5d1b3dea5a44124bab6c14a2d71b977dabed54e7 upstream.

The ov7740_remove() function had a severe teardown order bug where it destroyed the driver's mutex before freeing the V4L2 control handler which relies on that mutex, leading to a use-after-destroy kernel panic. Furthermore, the driver explicitly called v4l2_ctrl_handler_free() and mutex_destroy() sequentially, but then called ov7740_free_controls() which invokes both of them a second time, resulting in a double-free.

This patch fixes the issue by unregistering the subdevice first, and relying exclusively on ov7740_free_controls() to safely tear down the mutex and control handler in the correct order.

Fixes 39c5c4471b8d ("media: i2c: Add the ov7740 image sensor driver")
---
 drivers/media/i2c/ov7740.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/media/i2c/ov7740.c +1 -3
@@ -1158,10 +1158,8 @@ static void ov7740_remove(struct i2c_cli
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
 	struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev);
 
-	mutex_destroy(&ov7740->mutex);
-	v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler);
-	media_entity_cleanup(&ov7740->subdev.entity);
 	v4l2_async_unregister_subdev(sd);
+	media_entity_cleanup(&ov7740->subdev.entity);
 	ov7740_free_controls(ov7740);
 
 	pm_runtime_get_sync(&client->dev);
author maintainer
#454 lore raw

PATCH 454/1424 media: meson: vdec: fix NULL pointer deref in vdec_try_fmt_common

In reply to Greg Kroah-Hartman

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

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

From: Christian Hewitt <[email protected]>

commit 20aa934ace6917262ff579a73ec018d06a7bad1c upstream.

When VIDIOC_TRY_FMT is called with an unsupported pixel format on the OUTPUT queue, vdec_try_fmt_common() falls back to V4L2_PIX_FMT_MPEG2. However, if a distro has locally patched MPEG2 support out (as it has been broken for some time) the platform format table does not contain MPEG2 so find_format() returns NULL and the subsequent dereference of fmt_out->max_width triggers a NULL pointer dereference.

Fix this by falling back to the first format in the platform's format array instead of hardcoding V4L2_PIX_FMT_MPEG2. This is always valid since every platform defines at least one format.

Fixes 3e7f51bd9607 ("media: meson: add v4l2 m2m video decoder driver")
---
 drivers/staging/media/meson/vdec/vdec.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/staging/media/meson/vdec/vdec.c +2 -2
@@ -505,8 +505,8 @@ vdec_try_fmt_common(struct amvdec_sessio
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
 		fmt_out = find_format(fmts, size, pixmp->pixelformat);
 		if (!fmt_out) {
-			pixmp->pixelformat = V4L2_PIX_FMT_MPEG2;
-			fmt_out = find_format(fmts, size, pixmp->pixelformat);
+			pixmp->pixelformat = fmts[0].pixfmt;
+			fmt_out = &fmts[0];
 		}
 		break;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
author maintainer
#455 lore raw

PATCH 455/1424 media: nxp: imx8-isi: Fix stream ID validation bypass in crossbar routing

In reply to Greg Kroah-Hartman

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

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

From: Guoniu Zhou <[email protected]>

commit 795bd2863591f2fee33fab8f10cf9c383c94342e upstream.

The crossbar routing validation has a critical bug where it validates the wrong routing table, allowing userspace to bypass validation entirely.

The __mxc_isi_crossbar_set_routing() function is called to validate and apply a new routing table from userspace. However, the validation loop iterates over state->routing (the currently active routing table) instead of the routing parameter (the new table being validated):

    for_each_active_route(&state->routing, route) {

This means userspace can submit any invalid routing configuration and it will pass validation as long as the currently active routing is valid. This is a security issue as it allows userspace to configure routes that violate hardware constraints, potentially causing undefined hardware behavior.

Fix by validating the routing table that will actually be applied.

---
 drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +1 -1
@@ -107,7 +107,7 @@ static int __mxc_isi_crossbar_set_routin
 		return ret;
 
 	/* The memory input can be routed to the first pipeline only. */
-	for_each_active_route(&state->routing, route) {
+	for_each_active_route(routing, route) {
 		if (route->sink_pad == xbar->num_sinks - 1 &&
 		    route->source_pad != xbar->num_sinks) {
 			dev_dbg(xbar->isi->dev,
author maintainer
#456 lore raw

PATCH 456/1424 media: nxp: imx8-isi: Correct color map between V4L2 and ISI

In reply to Greg Kroah-Hartman

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

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

From: Guoniu Zhou <[email protected]>

commit 4640ec1cb0121473867e7a6850c7449340dcd428 upstream.

Fix the ISI input format for the color map V4L2_PIX_FMT_XBGR32 in memory-to-memory mode.

---
 drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +1 -1
index fe4adfa3a1f0..056603055c81 100644
@@ -151,7 +151,7 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = {
 		.fourcc		= V4L2_PIX_FMT_XBGR32,
 		.type		= MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT
 				| MXC_ISI_VIDEO_M2M_CAP,
-		.isi_in_format	= CHNL_MEM_RD_CTRL_IMG_TYPE_XBGR8,
+		.isi_in_format	= CHNL_MEM_RD_CTRL_IMG_TYPE_XRGB8,
 		.isi_out_format	= CHNL_IMG_CTRL_FORMAT_XRGB888,
 		.mem_planes	= 1,
 		.color_planes	= 1,
Signature · 1 line
2.55.0
author maintainer
#457 lore raw

PATCH 457/1424 media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks

In reply to Greg Kroah-Hartman

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

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

From: Guoniu Zhou <[email protected]>

commit 77e60a2c5d824ad2d493f53dc17137ae065753fe upstream.

Use BIT_ULL() instead of BIT() for u64 stream masks to avoid incorrect results on 32-bit architectures when stream IDs are 32 or greater.

---
 drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +2 -2
@@ -145,10 +145,10 @@ mxc_isi_crossbar_xlate_streams(struct mx
 	 */
 	for_each_active_route(&state->routing, route) {
 		if (route->source_pad != source_pad ||
-		    !(source_streams & BIT(route->source_stream)))
+		    !(source_streams & BIT_ULL(route->source_stream)))
 			continue;
 
-		sink_streams |= BIT(route->sink_stream);
+		sink_streams |= BIT_ULL(route->sink_stream);
 		sink_pad = route->sink_pad;
 	}
 
author maintainer
#458 lore raw

PATCH 458/1424 media: rc: sunxi-cir: Unregister rc device on probe failure

In reply to Greg Kroah-Hartman

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

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

From: Myeonghun Pak <[email protected]>

commit 479aa6fa8c50f1052f1451326ef7d4d586d340c3 upstream.

After rc_register_device() succeeds, later probe failures must undo the registration with rc_unregister_device(). The current error path jumps to the allocation cleanup label and only calls rc_free_device(), leaving the rc device registration and resources created by rc_register_device() behind.

Add a registered-device unwind label for the IRQ lookup, IRQ request, and hardware initialization failure paths. Keep rc_free_device() for failures before rc_register_device() succeeds.

Fixes b4e3e59fb59c ("[media] rc: add sunxi-ir driver")
---
 drivers/media/rc/sunxi-cir.c |    9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/media/rc/sunxi-cir.c +6 -3
@@ -343,22 +343,25 @@ static int sunxi_ir_probe(struct platfor
 	ir->irq = platform_get_irq(pdev, 0);
 	if (ir->irq < 0) {
 		ret = ir->irq;
-		goto exit_free_dev;
+		goto exit_unregister_dev;
 	}
 
 	ret = devm_request_irq(dev, ir->irq, sunxi_ir_irq, 0, SUNXI_IR_DEV, ir);
 	if (ret) {
 		dev_err(dev, "failed request irq\n");
-		goto exit_free_dev;
+		goto exit_unregister_dev;
 	}
 
 	ret = sunxi_ir_hw_init(dev);
 	if (ret)
-		goto exit_free_dev;
+		goto exit_unregister_dev;
 
 	dev_info(dev, "initialized sunXi IR driver\n");
 	return 0;
 
+exit_unregister_dev:
+	rc_unregister_device(ir->rc);
+
 exit_free_dev:
 	rc_free_device(ir->rc);
 
author maintainer
#459 lore raw

PATCH 459/1424 media: rtl2832_sdr: use vb2_video_unregister_device() on remove to fix DMA leak

In reply to Greg Kroah-Hartman

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

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

From: Valery Borovsky <[email protected]>

commit dabb047c62668f280998e29117c55e41aabac336 upstream.

rtl2832_sdr_remove() runs on USB disconnect and clears dev->udev to NULL before any pending streaming teardown has run. When user space later closes its file descriptor, vb2 calls rtl2832_sdr_stop_streaming() which in turn calls rtl2832_sdr_free_stream_bufs(). That helper releases each coherent buffer with:

    usb_free_coherent(dev->udev, dev->buf_size,
                      dev->buf_list[dev->buf_num],
                      dev->dma_addr[dev->buf_num]);

usb_free_coherent() returns immediately when its dev argument is NULL, so every DMA stream buffer that was live at disconnect is silently leaked. The URBs allocated in rtl2832_sdr_alloc_urbs() outlive the device for the same reason.

The rtl2832_sdr driver uses vb2_fop_release() in its file_operations, so replace video_unregister_device(&dev->vdev) with vb2_video_unregister_device(&dev->vdev) and move it before clearing dev->udev. vb2_video_unregister_device() releases the vb2 queue, which synchronously runs rtl2832_sdr_stop_streaming() if streaming is active, so URBs and coherent DMA stream buffers are freed while dev->udev is still valid.

vb2_video_unregister_device() locks vdev->queue->lock (vb_queue_lock) internally, and stop_streaming() locks v4l2_lock, so the previous outer mutex_lock(&dev->vb_queue_lock) / mutex_lock(&dev->v4l2_lock) pair around the unregister sequence would self-deadlock and has been removed. A short v4l2_lock critical section around dev->udev = NULL remains so any ioctl path that still holds the file descriptor sees coherent state.

Issue identified by automated review of the INV-003 series at
https://sashiko.dev/

Fixes 771138920eaf ("[media] rtl2832_sdr: Realtek RTL2832 SDR driver module")
---
 drivers/media/dvb-frontends/rtl2832_sdr.c |   18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)
drivers/media/dvb-frontends/rtl2832_sdr.c +13 -5
@@ -1478,14 +1478,22 @@ static void rtl2832_sdr_remove(struct pl
 
 	dev_dbg(&pdev->dev, "\n");
 
-	mutex_lock(&dev->vb_queue_lock);
+	/*
+	 * vb2_video_unregister_device() releases the vb2 queue, which
+	 * triggers rtl2832_sdr_stop_streaming() if streaming is active.
+	 * stop_streaming() uses dev->udev to free URBs and coherent DMA
+	 * stream buffers via usb_free_coherent(), so it must run before
+	 * dev->udev is cleared. vb2_video_unregister_device() locks
+	 * vb_queue_lock internally and stop_streaming() locks v4l2_lock,
+	 * so neither may be held by the caller.
+	 */
+	v4l2_device_disconnect(&dev->v4l2_dev);
+	vb2_video_unregister_device(&dev->vdev);
+
 	mutex_lock(&dev->v4l2_lock);
-	/* No need to keep the urbs around after disconnection */
 	dev->udev = NULL;
-	v4l2_device_disconnect(&dev->v4l2_dev);
-	video_unregister_device(&dev->vdev);
 	mutex_unlock(&dev->v4l2_lock);
-	mutex_unlock(&dev->vb_queue_lock);
+
 	v4l2_device_put(&dev->v4l2_dev);
 	module_put(pdev->dev.parent->driver->owner);
 }
author maintainer
#460 lore raw

PATCH 460/1424 media: rtl2832_sdr: release URBs and stream buffers on start_streaming() failure

In reply to Greg Kroah-Hartman

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

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

From: Valery Borovsky <[email protected]>

commit fe50cdaebf12cd32ff9a44d92bfd6fbc2300dbd4 upstream.

rtl2832_sdr_start_streaming() calls rtl2832_sdr_alloc_stream_bufs(), rtl2832_sdr_alloc_urbs() and rtl2832_sdr_submit_urbs() in sequence and shares a single err: label that only unlocks the mutex and returns. When alloc_urbs() succeeds but submit_urbs() fails, or when alloc_urbs() itself returns -ENOMEM after alloc_stream_bufs() has already succeeded, the URBs and/or the coherent DMA stream buffers stay allocated while streaming reports failure to vb2. Two latent defects follow on the next VIDIOC_STREAMON:

1) rtl2832_sdr_alloc_stream_bufs() unconditionally resets dev->buf_num to 0 and overwrites dev->buf_list[]/dev->dma_addr[], permanently leaking the coherent DMA memory allocated by the previous attempt.

2) rtl2832_sdr_alloc_urbs() never resets dev->urbs_initialized and only increments it. After a second successful pass urbs_initialized can exceed MAX_BULK_BUFS, so the subsequent rtl2832_sdr_free_urbs() walks from urbs_initialized - 1 down to 0 and reads past the end of dev->urb_list[], passing garbage pointers to usb_free_urb().

Mirror the teardown that stop_streaming() already performs: on the error path call rtl2832_sdr_free_urbs() and rtl2832_sdr_free_stream_bufs() before unlocking. Both helpers are idempotent (free_urbs kills and zeros urbs_initialized; free_stream_bufs is gated on URB_BUF and clears the buf_num counter), so partial-failure paths and the no-allocation paths remain safe.

Issue identified by automated review of the INV-003 series at
https://sashiko.dev/

Fixes 771138920eaf ("[media] rtl2832_sdr: Realtek RTL2832 SDR driver module")
---
 drivers/media/dvb-frontends/rtl2832_sdr.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/media/dvb-frontends/rtl2832_sdr.c +3 -0
@@ -905,9 +905,12 @@ static int rtl2832_sdr_start_streaming(s
 		goto err;
 
 	mutex_unlock(&dev->v4l2_lock);
+
 	return 0;
 
 err:
+	rtl2832_sdr_free_urbs(dev);
+	rtl2832_sdr_free_stream_bufs(dev);
 	rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
 	mutex_unlock(&dev->v4l2_lock);
 
author maintainer
#461 lore raw

PATCH 461/1424 media: s2255: bound JPEG frame size before copying into the buffer

In reply to Greg Kroah-Hartman

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

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

From: HyeongJun An <[email protected]>

commit e504cc888f42999dd76b6a43788c422610f2aad2 upstream.

s2255_fillbuff() memcpy()s vc->jpg_size bytes of a captured JPEG/MJPEG frame into the vb2 plane. vc->jpg_size is taken verbatim from the S2255_MARKER_FRAME header the device sends (pdword[4] in save_frame()) and, unlike the frame payload length just above it, is never bounded:

	payload = le32_to_cpu(pdword[3]);
	if (payload > vc->req_image_size)	/* payload is checked ... */
		return -EINVAL;
	vc->pkt_size = payload;
	vc->jpg_size = le32_to_cpu(pdword[4]);	/* ... jpg_size is not */

A malicious or malfunctioning device can therefore report a jpg_size larger than the destination vb2 plane, and the memcpy() writes past it. jpg_size is a signed int, so a value with the top bit set also turns into a huge length.

Reject a frame whose jpg_size is negative or exceeds the plane size before copying it.

Fixes 38f993ad8b1f ("V4L/DVB (8125): This driver adds support for the Sensoray 2255 devices.")
Assisted-by Claude:claude-opus-4-8
---
 drivers/media/usb/s2255/s2255drv.c |    6 ++++++
 1 file changed, 6 insertions(+)
drivers/media/usb/s2255/s2255drv.c +6 -0
@@ -617,6 +617,12 @@ static void s2255_fillbuff(struct s2255_
 			break;
 		case V4L2_PIX_FMT_JPEG:
 		case V4L2_PIX_FMT_MJPEG:
+			if (jpgsize < 0 ||
+			    jpgsize > vb2_plane_size(&buf->vb.vb2_buf, 0)) {
+				dprintk(dev, 1, "bad JPEG frame size %d\n",
+					jpgsize);
+				break;
+			}
 			vb2_set_plane_payload(&buf->vb.vb2_buf, 0, jpgsize);
 			memcpy(vbuf, tmpbuf, jpgsize);
 			break;
author maintainer
#462 lore raw

PATCH 462/1424 media: s2255: check firmware size before reading trailing marker

In reply to Greg Kroah-Hartman

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

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

From: Lei Huang <[email protected]>

commit 330f2936ab768c7215322a476f033143e8891d28 upstream.

s2255_probe() reads a 4-byte marker and version from the last 8 bytes of the firmware blob (fw->data[fw_size - 8] and [fw_size - 4]). If the firmware file is shorter than 8 bytes, fw_size - 8 underflows and the access reads out of bounds. Validate the firmware size before indexing.

Fixes 14d962602c8b ("V4L/DVB (8752): s2255drv: firmware improvement patch")
---
 drivers/media/usb/s2255/s2255drv.c |    5 +++++
 1 file changed, 5 insertions(+)
drivers/media/usb/s2255/s2255drv.c +5 -0
@@ -2288,6 +2288,11 @@ static int s2255_probe(struct usb_interf
 	}
 	/* check the firmware is valid */
 	fw_size = dev->fw_data->fw->size;
+	if (fw_size < 8) {
+		dev_err(&interface->dev, "Firmware invalid: too small.\n");
+		retval = -ENODEV;
+		goto errorFWMARKER;
+	}
 	pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
 
 	if (*pdata != S2255_FW_MARKER) {
author maintainer
#463 lore raw

PATCH 463/1424 media: saa7164: fix cleanup on resource allocation failure

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit 28e84c6e2e6753ed238ea097b2842a32a6a6879b upstream.

saa7164_dev_setup() adds the device to the global saa7164_devlist before requesting the PCI BAR memory regions.

If get_resources() fails, saa7164_dev_setup() decrements the device count and returns an error, but leaves the device on saa7164_devlist. The probe error path then frees the device, leaving a dangling entry on the global list.

Reuse the existing MMIO mapping error path to remove the device from saa7164_devlist and decrement the device count before returning.

Also release BAR0 if it was successfully requested but the BAR2 request fails.

Fixes 443c1228d505 ("V4L/DVB (12923): SAA7164: Add support for the NXP SAA7164 silicon")
---
 drivers/media/pci/saa7164/saa7164-core.c |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/media/pci/saa7164/saa7164-core.c +5 -3
@@ -878,6 +878,9 @@ static int get_resources(struct saa7164_
 		if (request_mem_region(pci_resource_start(dev->pci, 2),
 			pci_resource_len(dev->pci, 2), dev->name))
 			return 0;
+
+		release_mem_region(pci_resource_start(dev->pci, 0),
+				   pci_resource_len(dev->pci, 0));
 	}
 
 	printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n",
@@ -1000,8 +1003,7 @@ static int saa7164_dev_setup(struct saa7
 		       dev->name, dev->pci->subsystem_vendor,
 		       dev->pci->subsystem_device);
 
-		saa7164_devcount--;
-		return -ENODEV;
+		goto err_devlist;
 	}
 
 	/* PCI/e allocations */
@@ -1039,7 +1041,7 @@ err_ioremap_bar2:
 	iounmap(dev->lmmio);
 err_ioremap_bar0:
 	release_resources(dev);
-
+err_devlist:
 	scoped_guard(mutex, &devlist) {
 		list_del(&dev->devlist);
 	}
author maintainer
#464 lore raw

PATCH 464/1424 media: tda18250: fix possible integer overflow

In reply to Greg Kroah-Hartman

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

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

From: Ilya Krutskih <[email protected]>

commit 6dd8e257f7cafda7fbf10d81b3c55c9bba4825f4 upstream.

Integer overflow may occur, when variable exp equals to zero. Result of shift 1 << (exp - 1) may then leads to undefined behavior.

Fixes 148abd3b5b14 ("media: tda18250: support for new silicon tuner")
---
 drivers/media/tuners/tda18250.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/tuners/tda18250.c +2 -2
@@ -440,8 +440,8 @@ static int tda18250_pll_calc(struct dvb_
 		goto err;
 
 	exp = (uval & 0x70) >> 4;
-	if (exp > 5)
-		exp = 0;
+	if (exp == 0 || exp > 5)
+		exp = 1;
 	lopd = 1 << (exp - 1);
 	scale = uval & 0x0f;
 	fvco = lopd * scale * ((c->frequency / 1000) + dev->if_frequency);
author maintainer
#465 lore raw

PATCH 465/1424 media: v4l2-async: avoid deleting unlinked ASC entry on link error

In reply to Greg Kroah-Hartman

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

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

From: Xu Rao <[email protected]>

commit 47d82b605351c0e04f6365e42c8ffe2fcfdba615 upstream.

v4l2_async_match_notify() creates ancillary media links before adding asc->asc_subdev_entry to sd->asc_list.

If ancillary link creation fails, the function jumps to err_call_unbind while asc_subdev_entry has not been linked yet. Async connections are zero-allocated, so the list entry still has NULL next and prev pointers on this path. Calling list_del() on it can therefore dereference NULL instead of returning the original link creation error.

Do not delete asc_subdev_entry from err_call_unbind. There is no list insertion to undo on this path; the bound callback and sub-device registration are the operations that need to be rolled back.

Fixes 28a1295795d8 ("media: v4l: async: Allow multiple connections between entities")
---
 drivers/media/v4l2-core/v4l2-async.c |    1 -
 1 file changed, 1 deletion(-)
drivers/media/v4l2-core/v4l2-async.c +0 -1
@@ -388,7 +388,6 @@ static int v4l2_async_match_notify(struc
 
 err_call_unbind:
 	v4l2_async_nf_call_unbind(notifier, sd, asc);
-	list_del(&asc->asc_subdev_entry);
 
 err_unregister_subdev:
 	if (registered)
author maintainer
#466 lore raw

PATCH 466/1424 media: v4l2-ctrls: Allow unknown HDR10 white point and luminance

In reply to Greg Kroah-Hartman

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

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

From: Ming Qian <[email protected]>

commit 49af0c7cad889f7dabe5cf080b019392066122a3 upstream.

SMPTE ST 2086 defines the nominal ranges for mastering display chromaticity and luminance values. Its Annex A also documents that CTA 861-G uses zero maximum and minimum luminance values to signal that the corresponding values are unknown, and the xy chromaticity coordinate (0, 0) to signal that the white point chromaticity is unknown.

The V4L2 HDR10 mastering display compound control currently rejects these values. Consequently, an unknown white point or luminance value prevents the entire compound control from being updated, making the other valid mastering display metadata unavailable to userspace.

Accept (0, 0) as an unknown white point and zero as an unknown maximum or minimum mastering luminance. Continue to reject partially zero white point coordinates and non-zero values outside the nominal ranges. Display primary validation remains unchanged.

Document the newly accepted unknown values in the V4L2 userspace API.

Fixes 1ad0de78e794 ("media: v4l: Add HDR10 static metadata controls")
---
 Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst |   12 ++
 drivers/media/v4l2-core/v4l2-ctrls-core.c                       |   49 ++++++----
 2 files changed, 44 insertions(+), 17 deletions(-)
Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst +11 -1
@@ -79,15 +79,25 @@ Colorimetry Control IDs
       - ``white_point_x``
       - Specifies the normalized x chromaticity coordinate of the white
         point of the mastering display in increments of 0.00002.
+        When both ``white_point_x`` and ``white_point_y`` are zero,
+        the white point chromaticity is unknown. If either coordinate is
+        non-zero, both coordinates shall be within their valid ranges.
     * - __u16
       - ``white_point_y``
       - Specifies the normalized y chromaticity coordinate of the white
         point of the mastering display in increments of 0.00002.
+        When both ``white_point_x`` and ``white_point_y`` are zero,
+        the white point chromaticity is unknown. If either coordinate is
+        non-zero, both coordinates shall be within their valid ranges.
     * - __u32
       - ``max_luminance``
       - Specifies the nominal maximum display luminance of the mastering
         display in units of 0.0001 cd/m\ :sup:`2`.
+        A value of zero indicates that the nominal maximum display
+        luminance is unknown.
     * - __u32
       - ``min_luminance``
-      - specifies the nominal minimum display luminance of the mastering
+      - Specifies the nominal minimum display luminance of the mastering
         display in units of 0.0001 cd/m\ :sup:`2`.
+        A value of zero indicates that the nominal minimum display
+        luminance is unknown.
drivers/media/v4l2-core/v4l2-ctrls-core.c +33 -16

57 lines not on this page · raw message

author maintainer
#467 lore raw

PATCH 467/1424 media: v4l2-fwnode: Fix fwnode leak in v4l2_fwnode_parse_link

In reply to Greg Kroah-Hartman

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

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

From: Biren Pandya <[email protected]>

commit a6e86efd7f85e519bf48417f41923f8bd51f1597 upstream.

In v4l2_fwnode_parse_link(), the remote endpoint fwnode reference is acquired using fwnode_graph_get_remote_endpoint(). This reference is properly released in the error paths, but it is leaked on the success path.

Add the missing fwnode_handle_put() before returning 0 to prevent the reference leak.

Fixes ca50c197bd96 ("[media] v4l: fwnode: Support generic fwnode for parsing standardised properties")
[Sakari Ailus: Fix subject prefix and coding style a little.]
---
 drivers/media/v4l2-core/v4l2-fwnode.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/media/v4l2-core/v4l2-fwnode.c +2 -0
@@ -582,6 +582,8 @@ int v4l2_fwnode_parse_link(struct fwnode
 	if (!link->remote_node)
 		goto err_put_remote_endpoint;
 
+	fwnode_handle_put(fwnode);
+
 	return 0;
 
 err_put_remote_endpoint:
author maintainer
#468 lore raw

PATCH 468/1424 media: venus: fix payload size returned by parse_caps() and parse_alloc_mode()

In reply to Greg Kroah-Hartman

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

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

From: Mohammed EL Kadiri <[email protected]>

commit a51cea23e409278f6e2ea072280aba93cc1dd75e upstream.

parse_caps() and parse_alloc_mode() return only the size of their fixed header fields, excluding the flexible array payload. hfi_parser() uses this return value to advance through the firmware response buffer, so underreporting causes parser desynchronization.

Return the full consumed size (header + entries), matching the correct pattern used by parse_profile_level().

Fixes 9edaaa8e3e15 ("media: venus: hfi_parser: refactor hfi packet parsing logic")
---
 drivers/media/platform/qcom/venus/hfi_parser.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/platform/qcom/venus/hfi_parser.c +2 -2
@@ -85,7 +85,7 @@ parse_alloc_mode(struct venus_core *core
 		type++;
 	}
 
-	return sizeof(*mode);
+	return mode->num_entries * sizeof(u32) + sizeof(*mode);
 }
 
 static void fill_profile_level(struct hfi_plat_caps *cap, const void *data,
@@ -146,7 +146,7 @@ parse_caps(struct venus_core *core, u32
 	for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
 		       fill_caps, caps_arr, num_caps);
 
-	return sizeof(*caps);
+	return num_caps * sizeof(*cap) + sizeof(u32);
 }
 
 static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts,
author maintainer
#469 lore raw

PATCH 469/1424 media: venus: fix payload size calculation in parse_raw_formats()

In reply to Greg Kroah-Hartman

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

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

From: Mohammed EL Kadiri <[email protected]>

commit bd595b745eb770e80347c31ffc25351046935305 upstream.

The consumed size is computed after the loop using the num_planes value from the last iteration for all entries. When entries have different plane counts, this produces an incorrect total.

Accumulate the actual size during the loop instead.

Fixes 9edaaa8e3e15 ("media: venus: hfi_parser: refactor hfi packet parsing logic")
---
 drivers/media/platform/qcom/venus/hfi_parser.c |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/media/platform/qcom/venus/hfi_parser.c +2 -3
@@ -171,7 +171,7 @@ parse_raw_formats(struct venus_core *cor
 	u32 entries = fmt->format_entries;
 	unsigned int i = 0;
 	u32 num_planes = 0;
-	u32 size;
+	u32 size = 2 * sizeof(u32);
 
 	while (entries) {
 		num_planes = pinfo->num_planes;
@@ -186,6 +186,7 @@ parse_raw_formats(struct venus_core *cor
 		if (pinfo->num_planes > MAX_PLANES)
 			break;
 
+		size += sizeof(*constr) * num_planes + 2 * sizeof(u32);
 		pinfo = (void *)pinfo + sizeof(*constr) * num_planes +
 			2 * sizeof(u32);
 		entries--;
@@ -193,8 +194,6 @@ parse_raw_formats(struct venus_core *cor
 
 	for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
 		       fill_raw_fmts, rawfmts, i);
-	size = fmt->format_entries * (sizeof(*constr) * num_planes + 2 * sizeof(u32))
-		+ 2 * sizeof(u32);
 
 	return size;
 }
author maintainer
#470 lore raw

PATCH 470/1424 media: video-i2c: fix kthread error pointer left in kthread_vid_cap on failure

In reply to Greg Kroah-Hartman

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

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

From: Uday Khare <[email protected]>

commit 76e379754ba618989f6215be608d5c04774a611d upstream.

kthread_run() returns an ERR_PTR on failure, not NULL. When start_streaming() fails, data->kthread_vid_cap is left holding this error pointer instead of being cleared.

This causes two subsequent bugs:
1. A future call to start_streaming() sees a non-NULL kthread_vid_cap and returns 0 (success) immediately, without actually starting the capture thread.
2. A call to stop_streaming() checks 'kthread_vid_cap == NULL' which is false for an error pointer, and proceeds to call kthread_stop() on the error pointer, leading to a kernel crash.

Fix this by resetting kthread_vid_cap to NULL on failure before jumping to the error path.

Fixes 5cebaac60974 ("media: video-i2c: add video-i2c driver")
---
 drivers/media/i2c/video-i2c.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/media/i2c/video-i2c.c +6 -2
@@ -532,8 +532,12 @@ static int start_streaming(struct vb2_qu
 	data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data,
 					    "%s-vid-cap", data->v4l2_dev.name);
 	ret = PTR_ERR_OR_ZERO(data->kthread_vid_cap);
-	if (!ret)
-		return 0;
+	if (ret) {
+		data->kthread_vid_cap = NULL;
+		goto error_rpm_put;
+	}
+
+	return 0;
 
 error_rpm_put:
 	pm_runtime_mark_last_busy(dev);
author maintainer
#471 lore raw

PATCH 471/1424 media: vimc: fix pixel format lookup in enum_framesizes

In reply to Greg Kroah-Hartman

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

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

From: Arash Golgol <[email protected]>

commit ad4c65fa30cfb00e2e06adae9a8eb407086eaa66 upstream.

vimc_capture_enum_framesizes() looks up the requested format using vimc_pix_map_by_code(), which searches the pix map table by media bus code (MEDIA_BUS_FMT_*).

However, v4l2_frmsizeenum::pixel_format holds a V4L2 pixel format (V4L2_PIX_FMT_*), not a media bus code, so valid pixel formats end up being rejected with -EINVAL.

Fix this by using vimc_pix_map_by_pixelformat() instead, which performs the lookup by pixel format as the ioctl expects.

Fixes 09c41a23a2e2 ("media: Revert "media: vimc: propagate pixel format in the stream"")
---
 drivers/media/test-drivers/vimc/vimc-capture.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/test-drivers/vimc/vimc-capture.c +2 -2
@@ -177,8 +177,8 @@ static int vimc_capture_enum_framesizes(
 	if (fsize->index)
 		return -EINVAL;
 
-	/* Only accept code in the pix map table */
-	vpix = vimc_pix_map_by_code(fsize->pixel_format);
+	/* Only accept pixel_format in the pix map table */
+	vpix = vimc_pix_map_by_pixelformat(fsize->pixel_format);
 	if (!vpix)
 		return -EINVAL;
 
author maintainer
#472 lore raw

PATCH 472/1424 media: zoran: Avoid freeing a registered video_device twice

In reply to Greg Kroah-Hartman

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

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

From: Ruoyu Wang <[email protected]>

commit 0735e0b5a96761a9ce277a238e834008ad92a0a5 upstream.

zoran_init_video_device() installs zoran_vdev_release() as the video_device release callback through zoran_template. After video_register_device() succeeds, video_unregister_device() drops the registered video_device reference and the V4L2 core eventually invokes that release callback, which kfree()s the video_device.

zoran_exit_video_devices() called video_unregister_device() and then kfree(zr->video_dev), so device teardown could free the same video_device twice.

Remove the direct kfree() and clear the cached pointer after unregistering. The pre-registration failure path keeps its manual free because the video_device was not registered there.

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

Fixes 82e3a496eb56 ("media: staging: media: zoran: move videodev alloc")
---
 drivers/media/pci/zoran/zoran_card.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/pci/zoran/zoran_card.c +1 -1
@@ -889,7 +889,7 @@ static int zoran_init_video_device(struc
 static void zoran_exit_video_devices(struct zoran *zr)
 {
 	video_unregister_device(zr->video_dev);
-	kfree(zr->video_dev);
+	zr->video_dev = NULL;
 }
 
 static int zoran_init_video_devices(struct zoran *zr)
author maintainer
#473 lore raw

PATCH 473/1424 scsi: qla2xxx: Zero SFP DMA buffer in FRU/I2C bsg handlers

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit b47d4a1547d9ef21b2e9d1a739fe2204d4be05dc upstream.

The FRU and I2C bsg handlers stage their transfer in a DMA_POOL_SIZE (256-byte) bounce buffer obtained from dma_pool_alloc(), which does not zero the allocation. They initialize only a few leading bytes before handing the buffer to qla2x00_write_sfp().

qla2x00_write_sfp() can override the transfer length with a user-supplied value:

	if (len == 1)
		opt |= BIT_0;
	if (opt & BIT_0)
		len = *sfp;

*sfp is the first byte of the (user-controlled) payload, so len can grow up to 255. The device then DMA-reads len bytes from the 256-byte pool buffer. Since only a small prefix was written
(e.g. MAX_FRU_SIZE == 36 bytes for a FRU version, one byte for a FRU status register), the hardware reads past the initialized region and writes up to ~219 bytes of stale DMA-pool heap memory to the device flash.

Allocate the buffer with dma_pool_zalloc() in all five FRU/I2C handlers so any bytes beyond the initialized data are zero rather than stale heap contents.

Fixes 697a4bc69159 ("[SCSI] qla2xxx: Provide method for updating I2C attached VPD.")
Fixes 9ebb5d9c69f1 ("[SCSI] qla2xxx: Add I2C BSG interface.")
---
 drivers/scsi/qla2xxx/qla_bsg.c |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/scsi/qla2xxx/qla_bsg.c +5 -5
@@ -1565,7 +1565,7 @@ qla2x00_update_fru_versions(struct bsg_j
 	struct qla_image_version *image;
 	uint32_t count;
 	dma_addr_t sfp_dma;
-	void *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	void *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1616,7 +1616,7 @@ qla2x00_read_fru_status(struct bsg_job *
 	uint8_t bsg[DMA_POOL_SIZE];
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1667,7 +1667,7 @@ qla2x00_write_fru_status(struct bsg_job
 	uint8_t bsg[DMA_POOL_SIZE];
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1714,7 +1714,7 @@ qla2x00_write_i2c(struct bsg_job *bsg_jo
 	uint8_t bsg[DMA_POOL_SIZE];
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1760,7 +1760,7 @@ qla2x00_read_i2c(struct bsg_job *bsg_job
 	uint8_t bsg[DMA_POOL_SIZE];
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
author maintainer
#474 lore raw

PATCH 474/1424 scsi: qla2xxx: Bound i2c->length in I2C bsg handlers

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 0918ee2c0eeb4d7f45b82b3dc11e65c2d9b7ad59 upstream.

struct qla_i2c_access carries a 16-bit length field alongside a fixed 64-byte buffer:

	struct qla_i2c_access {
		uint16_t device, offset, option, length;
		uint8_t  buffer[0x40];
	} __packed;

qla2x00_write_i2c() and qla2x00_read_i2c() use the user-supplied i2c->length without any bounds check. i2c is overlaid on a 256-byte on-stack buffer and sfp is a 256-byte DMA-pool buffer, so a length up to 65535 overruns both:

- write: memcpy(sfp, i2c->buffer, i2c->length) over-reads the stack and over-writes the sfp heap buffer, and qla2x00_write_sfp() then DMAs i2c->length bytes out of the 256-byte buffer.
- read: qla2x00_read_sfp() DMAs i2c->length bytes into the 256-byte sfp, then memcpy(i2c->buffer, sfp, i2c->length) overflows the 64-byte buffer inside the on-stack array.

A caller holding CAP_SYS_RAWIO can use this to corrupt the heap and the kernel stack. Reject requests whose length exceeds the buffer before any copy or DMA transfer in both handlers.

---
 drivers/scsi/qla2xxx/qla_bsg.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/scsi/qla2xxx/qla_bsg.c +12 -0
@@ -1725,6 +1725,12 @@ qla2x00_write_i2c(struct bsg_job *bsg_jo
 	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
 	    bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
 
+	if (i2c->length > sizeof(i2c->buffer)) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+		    EXT_STATUS_INVALID_PARAM;
+		goto dealloc;
+	}
+
 	memcpy(sfp, i2c->buffer, i2c->length);
 	rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
 	    i2c->device, i2c->offset, i2c->length, i2c->option);
@@ -1771,6 +1777,12 @@ qla2x00_read_i2c(struct bsg_job *bsg_job
 	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
 	    bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
 
+	if (i2c->length > sizeof(i2c->buffer)) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+		    EXT_STATUS_INVALID_PARAM;
+		goto dealloc;
+	}
+
 	rval = qla2x00_read_sfp(vha, sfp_dma, sfp,
 		i2c->device, i2c->offset, i2c->length, i2c->option);
 
author maintainer
#475 lore raw

PATCH 475/1424 scsi: qla2xxx: edif: Fix NULL pointer deref in RX SA delete check

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit c20ee380ca59c5a8646750c4849969a815924e2e upstream.

qla_chk_edif_rx_sa_delete_pending() obtains the SCSI command via GET_CMD_SP(sp) and immediately dereferences cmd->sc_data_direction. That command pointer can be NULL: the firmware may post a status completion for a command that has already been returned or aborted. The caller qla2x00_status_entry() acknowledges this on the very same status path, re-fetching GET_CMD_SP(sp) and bailing out with the "Command already returned" message when it is NULL -- but that check runs only after qla_chk_edif_rx_sa_delete_pending() has already dereferenced the pointer, so a NULL cmd crashes the kernel in interrupt context.

Return early when cmd is NULL, before touching cmd->sc_data_direction.

---
 drivers/scsi/qla2xxx/qla_edif.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/qla2xxx/qla_edif.c +3 -0
@@ -3491,6 +3491,9 @@ void qla_chk_edif_rx_sa_delete_pending(s
 	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
 	uint32_t handle;
 
+	if (!cmd)
+		return;
+
 	handle = (uint32_t)LSW(sts24->handle);
 
 	/* find out if this status iosb is for a scsi read */
author maintainer
#476 lore raw

PATCH 476/1424 scsi: qla2xxx: Fix Name Server logout detection on FWI2 adapters

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit eb137255fd7aa834c4d639ae7b5e9e8ecf3a4fb2 upstream.

In the CS_PORT_LOGGED_OUT case of qla2x00_chk_ms_status(), the FWI2-capable branch compared ms_pkt->loop_id.extended against NPH_SNS to decide whether the Name Server had logged out. On FWI2 and later adapters the response is a ct_entry_24xx / ct_entry_24xx_ext, where loop_id.extended (via the legacy ms_iocb_entry_t view) aliases offset 8, which is comp_status, not nport_handle (offset 10). As this code runs under CS_PORT_LOGGED_OUT, the field read back 0x29 (CS_PORT_LOGGED_OUT) and the comparison against NPH_SNS (0x7fc) was always false.

As a result the driver never recognized a Name Server logout on FWI2/ 29xx adapters: it returned the generic QLA_FUNCTION_FAILED instead of QLA_NOT_LOGGED_IN and skipped setting LOOP_RESYNC_NEEDED / LOCAL_LOOP_UPDATE, so the fabric rediscovery triggered by an SNS logout did not happen.

Read nport_handle from the ct_entry_24xx layout (offset 10) instead. nport_handle is at the same offset in ct_entry_24xx and ct_entry_24xx_ext, so a single cast covers 24xx-class and 29xx. The non-FWI2 branch keeps using loop_id.extended, which is correct for the ms_iocb_entry_t response on those adapters.

---
 drivers/scsi/qla2xxx/qla_gs.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/scsi/qla2xxx/qla_gs.c +2 -2
@@ -157,8 +157,8 @@ qla2x00_chk_ms_status(scsi_qla_host_t *v
 			break;
 		case CS_PORT_LOGGED_OUT:
 			if (IS_FWI2_CAPABLE(ha)) {
-				if (le16_to_cpu(ms_pkt->loop_id.extended) ==
-				    NPH_SNS)
+				if (le16_to_cpu(((struct ct_entry_24xx *)
+				    ms_pkt)->nport_handle) == NPH_SNS)
 					lid_is_sns = true;
 			} else {
 				if (le16_to_cpu(ms_pkt->loop_id.extended) ==
author maintainer
#477 lore raw

PATCH 477/1424 scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 793cedee296fd819bfadc2a7ec4d52faf9c09a0a upstream.

In the format 1 path, the virtual port is located on ha->vp_list while holding vport_slock, but the lock is dropped before vp is used:
qla_update_host_map() is called and VP_IDX_ACQUIRED/REGISTER_FC4_NEEDED/ REGISTER_FDMI_NEEDED are set on vp. No reference is taken across that window, so a concurrent qla24xx_deallocate_vp_id() can tear the vport down and free it, leading to a use-after-free.

Take a vport reference (vref_count) under vport_slock when the matching vp is found, and drop it after the last use of
vp. qla24xx_deallocate_vp_id() waits for vref_count to reach zero before unlinking and freeing the vport, so the pointer stays valid. This matches the reference idiom already used by the other ha->vp_list traversals.

---
 drivers/scsi/qla2xxx/qla_mbx.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/scsi/qla2xxx/qla_mbx.c +3 -0
@@ -4146,6 +4146,7 @@ qla24xx_report_id_acquisition(scsi_qla_h
 			list_for_each_entry(vp, &ha->vp_list, list) {
 				if (rptid_entry->vp_idx == vp->vp_idx) {
 					found = 1;
+					atomic_inc(&vp->vref_count);
 					break;
 				}
 			}
@@ -4163,6 +4164,8 @@ qla24xx_report_id_acquisition(scsi_qla_h
 			set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
 			set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
 			set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
+
+			atomic_dec(&vp->vref_count);
 		}
 		set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
 		qla2xxx_wake_dpc(vha);
author maintainer
#478 lore raw

PATCH 478/1424 scsi: qla2xxx: Initialize NVMe abort_work once at submission

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 7e85f6dbc85616de2172bce8eaf84b387a723cd1 upstream.

qla_nvme_fcp_abort() and qla_nvme_ls_abort() ran INIT_WORK() on priv->abort_work immediately before schedule_work(). INIT_WORK() reinitializes the work_struct, resetting its list head and clearing the pending bit. If an abort is issued more than once for the same command (for example, concurrent transport teardown and a timeout-driven abort), the second INIT_WORK() reinitializes a work item that is already queued, which can corrupt the workqueue list and lead to crashes or a looping worker.

Initialize priv->abort_work once at command submission, next to the existing per-command spin_lock_init(&priv->cmd_lock), and leave only schedule_work() in the abort paths. schedule_work() already does nothing when the work item is still pending, so a repeated abort no longer disturbs an in-flight work item. The command is not returned to the transport until the final kref_put()/release callback runs after abort_work has completed, so the work item is idle before priv is reused and the single submission-time INIT_WORK() is safe.

---
 drivers/scsi/qla2xxx/qla_nvme.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/scsi/qla2xxx/qla_nvme.c +2 -2
@@ -464,7 +464,6 @@ static void qla_nvme_ls_abort(struct nvm
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	schedule_work(&priv->abort_work);
 }
 
@@ -502,6 +501,7 @@ static int qla_nvme_ls_req(struct nvme_f
 	priv->sp = sp;
 	kref_init(&sp->cmd_kref);
 	spin_lock_init(&priv->cmd_lock);
+	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	nvme = &sp->u.iocb_cmd;
 	priv->fd = fd;
 	nvme->u.nvme.desc = fd;
@@ -546,7 +546,6 @@ static void qla_nvme_fcp_abort(struct nv
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	schedule_work(&priv->abort_work);
 }
 
@@ -812,6 +811,7 @@ static int qla_nvme_post_cmd(struct nvme
 
 	kref_init(&sp->cmd_kref);
 	spin_lock_init(&priv->cmd_lock);
+	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	sp->priv = priv;
 	priv->sp = sp;
 	sp->type = SRB_NVME_CMD;
author maintainer
#479 lore raw

PATCH 479/1424 scsi: qla2xxx: Check entry_status in qla24xx_modify_vp_config()

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 9101c51649f5b6773a97bf5271785c948589ea1d upstream.

The Modify VP Config completion handler labelled its first error branch "error status" but tested vpmod->comp_status instead of vpmod->entry_status. Because CS_COMPLETE is 0, the following "comp_status != CS_COMPLETE" branch duplicated that test and was dead code, and entry_status was never examined at all.

When firmware rejects the IOCB early it sets entry_status while leaving comp_status zero. As the IOCB is allocated with dma_pool_zalloc(), both comp_status branches evaluate false and the handler falls through to the success path, calling fc_vport_set_state(FC_VPORT_INITIALIZING) for a configuration the firmware never accepted. This can leave the virtual port enabled on top of an invalid config and surface later as login timeouts or follow-on firmware errors.

Test entry_status in the first branch, matching qla_ctrlvp_completed() and the login/logout/abort/reset IOCB handlers; the comp_status branch then becomes the live completion-status check.

---
 drivers/scsi/qla2xxx/qla_mbx.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/scsi/qla2xxx/qla_mbx.c +2 -2
@@ -4276,10 +4276,10 @@ qla24xx_modify_vp_config(scsi_qla_host_t
 	if (rval != QLA_SUCCESS) {
 		ql_dbg(ql_dbg_mbx, vha, 0x10bd,
 		    "Failed to issue VP config IOCB (%x).\n", rval);
-	} else if (vpmod->comp_status != 0) {
+	} else if (vpmod->entry_status != 0) {
 		ql_dbg(ql_dbg_mbx, vha, 0x10be,
 		    "Failed to complete IOCB -- error status (%x).\n",
-		    vpmod->comp_status);
+		    vpmod->entry_status);
 		rval = QLA_FUNCTION_FAILED;
 	} else if (vpmod->comp_status != cpu_to_le16(CS_COMPLETE)) {
 		ql_dbg(ql_dbg_mbx, vha, 0x10bf,
author maintainer
#480 lore raw

PATCH 480/1424 scsi: qla2xxx: Bound image count in qla2x00_update_fru_versions()

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit de62cf265dbe309f34f144a6cdbca9240317727e upstream.

qla2x00_update_fru_versions() copies the user-supplied BSG request into a fixed 256-byte stack buffer (bsg[DMA_POOL_SIZE]) and then iterates list->count times over the qla_image_version array embedded in that buffer, advancing the image pointer each iteration. count is taken directly from user input with no upper bound, while only (DMA_POOL_SIZE
- sizeof(list->count)) / sizeof(struct qla_image_version) = 6 entries actually fit. A larger count walks the image pointer off the end of the stack buffer, reading adjacent kernel stack memory and sending it to the device via qla2x00_write_sfp().

Reject requests whose declared count does not fit in the buffer.

---
 drivers/scsi/qla2xxx/qla_bsg.c |    7 +++++++
 1 file changed, 7 insertions(+)
drivers/scsi/qla2xxx/qla_bsg.c +7 -0
@@ -1578,6 +1578,13 @@ qla2x00_update_fru_versions(struct bsg_j
 
 	image = list->version;
 	count = list->count;
+
+	if (struct_size(list, version, count) > sizeof(bsg)) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+		    EXT_STATUS_INVALID_PARAM;
+		goto dealloc;
+	}
+
 	while (count--) {
 		memcpy(sfp, &image->field_info, sizeof(image->field_info));
 		rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
author maintainer
#481 lore raw

PATCH 481/1424 scsi: qla2xxx: Hold qpair lock when sending NVMe LS reject

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit f743488e4a203049f27ec5d8cd0caccc483af01e upstream.

qla_nvme_ls_reject_iocb() allocates from and advances the request ring through __qla2x00_alloc_iocbs() (which assumes the hardware_lock is held) and qla2x00_start_iocbs() (which advances the ring and rings the request-in doorbell), but takes no lock itself. Two of its callers invoke it without the producer lock held:

- qla_nvme_xmt_ls_rsp(), the NVMe-FC .xmt_ls_rsp transport callback, on its error path, and

- qla2xxx_process_purls_pkt(), run from the purex work/DPC context.

Both use ha->base_qpair, whose qp_lock_ptr is hardware_lock, so they can run concurrently with normal I/O submission on the base ring and corrupt the ring producer state, leading to duplicated or dropped commands. The third caller, qla2xxx_process_purls_iocb(), runs inside qla24xx_process_response_queue() with the qpair lock already held and is safe; that is also why the lock cannot be taken inside the helper itself (it would recursively re-acquire hardware_lock on the response path).

Take qp_lock_ptr around the two unlocked callers and document the helper as caller-locked. Both run in process context, so spin_lock_irqsave() is used and nothing in the locked region sleeps.

Fixes 875386b98857 ("scsi: qla2xxx: Add Unsolicited LS Request and Response Support for NVMe")
---
 drivers/scsi/qla2xxx/qla_nvme.c |   10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/scsi/qla2xxx/qla_nvme.c +10 -0
@@ -375,6 +375,7 @@ static int qla_nvme_xmt_ls_rsp(struct nv
 	srb_t *sp;
 	int rval = QLA_FUNCTION_FAILED;
 	uint8_t cnt = 0;
+	unsigned long flags;
 
 	if (!fcport || fcport->deleted)
 		goto out;
@@ -441,7 +442,9 @@ out:
 	a.vp_idx = vha->vp_idx;
 	a.nport_handle = uctx->nport_handle;
 	a.xchg_address = uctx->exchange_address;
+	spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
 	qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
+	spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
 	kfree(uctx);
 	return rval;
 }
@@ -1128,6 +1131,10 @@ static void qla_nvme_lsrjt_pt_iocb(struc
 	lsrjt_iocb->rx_byte_count = 0;
 }
 
+/*
+ * Allocates from and advances the request ring, so the caller must hold
+ * qp->qp_lock_ptr (the response-queue caller already holds it).
+ */
 static int
 qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, struct qla_qpair *qp,
 			struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate)
@@ -1184,6 +1191,7 @@ qla2xxx_process_purls_pkt(struct scsi_ql
 {
 	struct qla_nvme_unsol_ctx *uctx = item->purls_context;
 	struct qla_nvme_lsrjt_pt_arg a;
+	unsigned long flags;
 	int ret = 1;
 
 #if (IS_ENABLED(CONFIG_NVME_FC))
@@ -1196,7 +1204,9 @@ qla2xxx_process_purls_pkt(struct scsi_ql
 		a.vp_idx = vha->vp_idx;
 		a.nport_handle = uctx->nport_handle;
 		a.xchg_address = uctx->exchange_address;
+		spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr, flags);
 		qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
+		spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr, flags);
 		list_del(&uctx->elem);
 		kfree(uctx);
 	}
author maintainer
#482 lore raw

PATCH 482/1424 scsi: qla2xxx: Clamp MSI-X derived queue counts to avoid truncation

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit ebfd35c64433821bd5619a6d07ccc2df8b5b1de3 upstream.

ha->msix_count is u16, but ha->max_req_queues, ha->max_rsp_queues and ha->max_qpairs are u8. Deriving the queue count as "ha->max_req_queues = ha->msix_count - 1" therefore truncates: a board (or a misconfigured/malicious hot-plugged device) advertising 257 MSI-X vectors yields msix_count - 1 == 256, which truncates to 0. An MSI-X count of 1 zeroes it as well, and in target mode the subsequent "ha->max_req_queues--" then underflows 0 to 255.

When the count is 0, qla2x00_alloc_queues() calls kzalloc_objs(struct req_que *, 0), which returns ZERO_SIZE_PTR. That is not NULL, so the allocation check passes and the following "ha->req_q_map[0] = req" dereferences ZERO_SIZE_PTR, corrupting memory or crashing the kernel.

Add qla_calc_queue_count() to clamp the derived value into [1, QLA_MAX_QUEUES - 1] so it always fits in u8 and is never zero, and use it at all three derivation sites (qla25xx_iospace_config(), qla83xx_iospace_config() and qla24xx_enable_msix()). Also guard the target-mode decrement so it cannot reintroduce a zero (which would in turn underflow max_qpairs).

---
 drivers/scsi/qla2xxx/qla_inline.h |   13 +++++++++++++
 drivers/scsi/qla2xxx/qla_isr.c    |    4 ++--
 drivers/scsi/qla2xxx/qla_os.c     |    6 +++---
 3 files changed, 18 insertions(+), 5 deletions(-)
drivers/scsi/qla2xxx/qla_inline.h +13 -0
@@ -54,6 +54,19 @@ qla2x00_debounce_register(volatile __le1
 	return (first);
 }
 
+static inline u8
+qla_calc_queue_count(u16 msix_count)
+{
+	/*
+	 * Request/response queues are bounded by the MSI-X vector count less
+	 * the mailbox vector.  These counters are u8, so a board advertising
+	 * e.g. 257 vectors would truncate msix_count - 1 (256) to 0 and hand
+	 * kzalloc_objs() a zero count (ZERO_SIZE_PTR), faulting on the first
+	 * ha->req_q_map[0] store.  Clamp into [1, QLA_MAX_QUEUES - 1].
+	 */
+	return clamp_t(u16, msix_count - 1, 1, QLA_MAX_QUEUES - 1);
+}
+
 static inline void
 qla2x00_poll(struct rsp_que *rsp)
 {
drivers/scsi/qla2xxx/qla_isr.c +2 -2
@@ -4554,10 +4554,10 @@ qla24xx_enable_msix(struct qla_hw_data *
 		ha->msix_count = ret;
 		/* Recalculate queue values */
 		if (ha->mqiobase && (ql2xmqsupport || ql2xnvmeenable)) {
-			ha->max_req_queues = ha->msix_count - 1;
+			ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
 
 			/* ATIOQ needs 1 vector. That's 1 less QPair */
-			if (QLA_TGT_MODE_ENABLED())
+			if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1)
 				ha->max_req_queues--;
 
 			ha->max_rsp_queues = ha->max_req_queues;
drivers/scsi/qla2xxx/qla_os.c +3 -3
@@ -2126,7 +2126,7 @@ skip_pio:
 		ha->msix_count = msix + 1;
 		/* Max queues are bounded by available msix vectors */
 		/* MB interrupt uses 1 vector */
-		ha->max_req_queues = ha->msix_count - 1;
+		ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
 		ha->max_rsp_queues = ha->max_req_queues;
 		/* Queue pairs is the max value minus the base queue pair */
 		ha->max_qpairs = ha->max_rsp_queues - 1;
@@ -2212,10 +2212,10 @@ qla83xx_iospace_config(struct qla_hw_dat
 		 */
 		if (ql2xmqsupport || ql2xnvmeenable) {
 			/* MB interrupt uses 1 vector */
-			ha->max_req_queues = ha->msix_count - 1;
+			ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
 
 			/* ATIOQ needs 1 vector. That's 1 less QPair */
-			if (QLA_TGT_MODE_ENABLED())
+			if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1)
 				ha->max_req_queues--;
 
 			ha->max_rsp_queues = ha->max_req_queues;
author maintainer
#483 lore raw

PATCH 483/1424 scsi: qla2xxx: Serialize flash version read in reset handler

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit f606ed93de0c4f1e7e3618779e9fad731455314a upstream.

The "update cache versions without reset" sysfs reset operation (0x20261) calls get_flash_version(), which reads hardware flash registers, without holding ha->optrom_mutex. The VPD update path serializes the same call under optrom_mutex, so this reset path can interleave its flash register accesses with a concurrent VPD or optrom flash operation and corrupt the reads.

Hold ha->optrom_mutex across the get_flash_version() call to match the VPD update path.

---
 drivers/scsi/qla2xxx/qla_attr.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/qla2xxx/qla_attr.c +2 -0
@@ -816,7 +816,9 @@ qla2x00_sysfs_write_reset(struct file *f
 			    "Unable to allocate memory for VPD information update.\n");
 			return -ENOMEM;
 		}
+		mutex_lock(&ha->optrom_mutex);
 		ha->isp_ops->get_flash_version(vha, tmp_data);
+		mutex_unlock(&ha->optrom_mutex);
 		vfree(tmp_data);
 		break;
 	}
author maintainer
#484 lore raw

PATCH 484/1424 scsi: qla2xxx: Fix cs84xx use-after-free on host teardown

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 33d102102d925357c5fd172dd6672a27d74b3215 upstream.

qla84xx_put_chip() drops the last reference to ha->cs84xx and frees it via __qla84xx_chip_release() without clearing ha->cs84xx. During teardown it ran before scsi_remove_host(), which is what removes the 84xx_fw_version host sysfs attribute. A concurrent read of that attribute in the window between the two calls executes qla24xx_84xx_fw_version_show(), which dereferences the freed ha->cs84xx, resulting in a use-after-free.

Move qla84xx_put_chip() to after scsi_remove_host() in both qla2x00_remove_one() and qla2x00_disable_board_on_pci_error(). Once scsi_remove_host() returns, the sysfs attribute is gone and kernfs has drained any in-flight show(), so no reader can touch cs84xx; the put still runs before the host and ha are freed.

Fixes fe1b806f4f71 ("[SCSI] qla2xxx: Refactor shutdown code so some functionality can be reused.")
---
 drivers/scsi/qla2xxx/qla_os.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/scsi/qla2xxx/qla_os.c +4 -4
@@ -3927,8 +3927,6 @@ qla2x00_remove_one(struct pci_dev *pdev)
 
 	qla2x00_dfs_remove(base_vha);
 
-	qla84xx_put_chip(base_vha);
-
 	/* Disable timer */
 	if (base_vha->timer_active)
 		qla2x00_stop_timer(base_vha);
@@ -3953,6 +3951,8 @@ qla2x00_remove_one(struct pci_dev *pdev)
 
 	scsi_remove_host(base_vha->host);
 
+	qla84xx_put_chip(base_vha);
+
 	qla2x00_free_device(base_vha);
 
 	qla2x00_clear_drv_active(ha);
@@ -6824,8 +6824,6 @@ qla2x00_disable_board_on_pci_error(struc
 
 	qla2x00_dfs_remove(base_vha);
 
-	qla84xx_put_chip(base_vha);
-
 	if (base_vha->timer_active)
 		qla2x00_stop_timer(base_vha);
 
@@ -6843,6 +6841,8 @@ qla2x00_disable_board_on_pci_error(struc
 
 	scsi_remove_host(base_vha->host);
 
+	qla84xx_put_chip(base_vha);
+
 	base_vha->flags.init_done = 0;
 	qla25xx_delete_queues(base_vha);
 	qla2x00_free_fcports(base_vha);
author maintainer
#485 lore raw

PATCH 485/1424 scsi: qla2xxx: Fix FCE trace use-after-free during firmware dump

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 53298efcbbb0f0438366d45cb7ed7e6d93dd5531 upstream.

qla2x00_free_fce_trace() freed and cleared ha->fce while holding only fce_mutex. The firmware-dump consumers qla27xx_fwdt_entry_t264() and qla25xx_copy_fce() read ha->fce (NULL check followed by a copy of the buffer) under hardware_lock and never take fce_mutex. A debugfs FCE disable could therefore free the DMA buffer between a dump's NULL check and its copy, resulting in a use-after-free.

Unpublish ha->fce under hardware_lock, then release the lock and free the DMA buffer (dma_free_coherent() may sleep). A concurrent dump either completes its check and copy with the buffer still valid, or observes ha->fce == NULL and skips it.

Fixes 841df27d619e ("scsi: qla2xxx: Move FCE Trace buffer allocation to user control")
---
 drivers/scsi/qla2xxx/qla_init.c |   20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)
drivers/scsi/qla2xxx/qla_init.c +18 -2
@@ -3764,11 +3764,27 @@ int qla2x00_alloc_fce_trace(scsi_qla_hos
 
 void qla2x00_free_fce_trace(struct qla_hw_data *ha)
 {
-	if (!ha->fce)
+	void *fce;
+	dma_addr_t fce_dma;
+	unsigned long flags;
+
+	/*
+	 * Unpublish ha->fce under hardware_lock so a firmware dump in
+	 * progress (which reads ha->fce under the same lock) cannot race
+	 * with the buffer being freed.
+	 */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	if (!ha->fce) {
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
 		return;
-	dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, ha->fce_dma);
+	}
+	fce = ha->fce;
+	fce_dma = ha->fce_dma;
 	ha->fce = NULL;
 	ha->fce_dma = 0;
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	dma_free_coherent(&ha->pdev->dev, FCE_SIZE, fce, fce_dma);
 }
 
 static void
author maintainer
#486 lore raw

PATCH 486/1424 scsi: qla2xxx: Zero mailbox struct in qla2x00_get_firmware_state()

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 9efaa782845b4d5fb3e01242be0d06ebc7428d8f upstream.

The mbx_cmd_t is allocated on the stack but left uninitialized. qla2x00_mailbox_command() has several early-return paths (PCI permanent failure, device failed, EEH busy, ISP abort pending, mailbox access timeout, purge mbox) that return without writing the input mailbox registers back into mcp->mb[]. qla2x00_get_firmware_state() then unconditionally copies mcp->mb[1..6] (and mb[12]) into the caller's states[] array regardless of the return value.

On such a failure the copied values are uninitialized kernel stack memory, which is then exposed to userspace via the fw_state and mpi_fw_state sysfs handlers. Zero the mailbox struct so a failed query yields deterministic zeroed state instead of leaking stack contents.

---
 drivers/scsi/qla2xxx/qla_mbx.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/qla2xxx/qla_mbx.c +2 -0
@@ -2214,6 +2214,8 @@ qla2x00_get_firmware_state(scsi_qla_host
 	if (!ha->flags.fw_started)
 		return QLA_FUNCTION_FAILED;
 
+	memset(&mc, 0, sizeof(mc));
+
 	mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
 	mcp->out_mb = MBX_0;
 	if (IS_FWI2_CAPABLE(vha->hw))
author maintainer
#487 lore raw

PATCH 487/1424 scsi: qla2xxx: Fix FCE trace enable parsing in debugfs

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit b7368687e3d11f51392d3c4774ec0263d5fbf31f upstream.

qla2x00_dfs_fce_write() called kstrtoul() with a NULL result pointer, so a successful parse would dereference NULL and oops. Worse, the int return value (0 on success, negative errno on failure) was assigned to the unsigned long enable flag, inverting the intended logic: a valid number was treated as "disable" while a parse failure enabled FCE.

Parse the value into enable and propagate parse errors to userspace.

Fixes 841df27d619e ("scsi: qla2xxx: Move FCE Trace buffer allocation to user control")
---
 drivers/scsi/qla2xxx/qla_dfs.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/scsi/qla2xxx/qla_dfs.c +3 -1
@@ -518,7 +518,9 @@ qla2x00_dfs_fce_write(struct file *file,
 		return PTR_ERR(buf);
 	}
 
-	enable = kstrtoul(buf, 0, 0);
+	rc = kstrtoul(buf, 0, &enable);
+	if (rc)
+		goto out_free;
 	rc = count;
 
 	mutex_lock(&ha->fce_mutex);
author maintainer
#488 lore raw

PATCH 488/1424 scsi: qla2xxx: Dont query firmware state while chip is down

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit e0cebe20dcffbed9c078fe30e2d18cd5046d9eff upstream.

qla2x00_fw_state_show() initializes rval to QLA_FUNCTION_FAILED and jumps to the out: label when the chip is down or EEH is busy. The out: block then re-issued qla2x00_get_firmware_state() because rval != QLA_SUCCESS, defeating the chip-down/EEH-busy guards and issuing a mailbox command (outside optrom_mutex) during ISP reset or PCI error recovery, which can hang the adapter. It also turned a normal in-lock mailbox failure into a second unsynchronized mailbox attempt.

Make the out: fallback only mark the firmware state as unknown. The mailbox is now issued at most once, inside optrom_mutex, and only when the chip is up and not EEH-busy.

---
 drivers/scsi/qla2xxx/qla_attr.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/scsi/qla2xxx/qla_attr.c +1 -3
@@ -1669,10 +1669,8 @@ qla2x00_fw_state_show(struct device *dev
 	rval = qla2x00_get_firmware_state(vha, state);
 	mutex_unlock(&vha->hw->optrom_mutex);
 out:
-	if (rval != QLA_SUCCESS) {
+	if (rval != QLA_SUCCESS)
 		memset(state, -1, sizeof(state));
-		rval = qla2x00_get_firmware_state(vha, state);
-	}
 
 	return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
 	    state[0], state[1], state[2], state[3], state[4], state[5]);
author maintainer
#489 lore raw

PATCH 489/1424 scsi: qla2xxx: Reject non-SCSI SRB on status IOCB fast path

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 0f41d07d72f2245208c45374ca8d0a1846cad667 upstream.

qla2x00_status_entry() filters out non-TYPE_SRB entries and the SRB_NVME_CMD, SRB_BIDI_CMD and SRB_TM_CMD types, then falls through to a SCSI fast path that assumes the command is an SRB_SCSI_CMD. The first thing on that path, qla_chk_edif_rx_sa_delete_pending(), and the subsequent handling both evaluate GET_CMD_SP(sp), i.e. sp->u.scmd.cmd.

The srb u union overlays the SCSI command pointer with other command layouts (bsg_job, iocb_cmd). If firmware delivers an unexpected STATUS_TYPE IOCB for a non-SCSI handle, sp->u.scmd.cmd can read as a non-NULL garbage pointer, bypassing the NULL checks in qla_chk_edif_rx_sa_delete_pending() and at the cp == NULL test, and leading to a wild pointer dereference.

Reject any SRB whose type is not SRB_SCSI_CMD before entering the fast path. The outstanding_cmds slot is left untouched so a genuinely non-SCSI command still completes through its proper handler.

---
 drivers/scsi/qla2xxx/qla_dbg.c |    2 +-
 drivers/scsi/qla2xxx/qla_isr.c |    8 ++++++++
 2 files changed, 9 insertions(+), 1 deletion(-)
drivers/scsi/qla2xxx/qla_dbg.c +1 -1
@@ -16,7 +16,7 @@
  * |                              |                    | 0x2127-0x2128  |
  * | Queue Command and IO tracing |       0x3074       | 0x300b         |
  * |                              |                    | 0x3027-0x3028  |
- * |                              |                    | 0x303d-0x3041  |
+ * |                              |                    | 0x303e-0x3041  |
  * |                              |                    | 0x302e,0x3033  |
  * |                              |                    | 0x3036,0x3038  |
  * |                              |                    | 0x303a		|
drivers/scsi/qla2xxx/qla_isr.c +8 -0
@@ -3412,6 +3412,14 @@ qla2x00_status_entry(scsi_qla_host_t *vh
 		return;
 	}
 
+	/* Everything below is the SCSI fast path; reject other SRB types. */
+	if (sp->type != SRB_SCSI_CMD) {
+		ql_dbg(ql_dbg_io, vha, 0x303d,
+		    "Unexpected SRB type %x for status IOCB, sp %p.\n",
+		    sp->type, sp);
+		return;
+	}
+
 	/* Fast path completion. */
 	qla_chk_edif_rx_sa_delete_pending(vha, sp, sts24);
 	sp->qpair->cmd_completion_cnt++;
author maintainer
#490 lore raw

PATCH 490/1424 scsi: qla2xxx: Fix response queue over-consumption in __qla_consume_iocb()

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 3ba019bdd89d931499d9476456b5d9c7ab7fa753 upstream.

qla24xx_process_response_queue() advances ring_ptr past the head IOCB before dispatching, so by the time __qla_consume_iocb() runs, ring_ptr already points at the first continuation IOCB. The function however looped purex->entry_count times starting at ring_ptr. As entry_count includes the head, this consumed one entry too many: it stamped RESPONSE_PROCESSED on the next, unrelated IOCB and advanced the ring past it, silently dropping a legitimate firmware response. The head IOCB's signature was also never marked.

Mark the head processed and account for it, then consume only the entry_count - 1 continuation IOCBs, matching __qla_copy_purex_to_buffer().

Fixes fac2807946c1 ("scsi: qla2xxx: edif: Add extraction of auth_els from the wire")
---
 drivers/scsi/qla2xxx/qla_isr.c |   11 +++++++++++
 1 file changed, 11 insertions(+)
drivers/scsi/qla2xxx/qla_isr.c +11 -0
@@ -205,6 +205,17 @@ void __qla_consume_iocb(struct scsi_qla_
 	struct purex_entry_24xx *purex = *pkt;
 
 	entry_count_remaining = purex->entry_count;
+
+	/*
+	 * The caller already advanced ring_ptr past the head IOCB, so mark
+	 * the head processed and account for it here, then consume only the
+	 * continuation IOCBs that follow.
+	 */
+	((response_t *)purex)->signature = RESPONSE_PROCESSED;
+	/* flush signature */
+	wmb();
+	--entry_count_remaining;
+
 	while (entry_count_remaining > 0) {
 		new_pkt = rsp_q->ring_ptr;
 		*pkt = new_pkt;
author maintainer
#491 lore raw

PATCH 491/1424 scsi: qla2xxx: Quiesce response IRQ before freeing request queue

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 505753ec2594c6af09a601f0dd60be7d840c1d2d upstream.

qla2xxx_delete_qpair() deletes the request queue before the response queue. qla25xx_delete_req_que() frees the request queue memory (kfree(req) in qla25xx_free_req_que()), but the response-queue MSI-X is only released later, in qla25xx_free_rsp_que(). In that window the response interrupt can still fire, qla2xxx_msix_rsp_q() queues qpair->q_work, and qla_do_work() -> qla24xx_process_response_queue() dereferences the now-freed rsp->req (LOGINOUT/CT/ELS entries and the status path), a use-after-free.

The cancel_work_sync() added for the qpair teardown lives in the response free path, which runs after the request queue is already freed, so it does not protect rsp->req.

Release the response-queue interrupt and flush qpair->q_work before deleting the request queue, so no late completion can reach the freed request queue. Clearing have_irq makes the subsequent qla25xx_free_rsp_que() skip its free_irq(), and the firmware queue-delete order (request then response) is preserved; the request-delete mailbox completes on the default vector and is unaffected by dropping the qpair response interrupt early.

---
 drivers/scsi/qla2xxx/qla_init.c |   17 +++++++++++++++++
 1 file changed, 17 insertions(+)
drivers/scsi/qla2xxx/qla_init.c +17 -0
@@ -9853,11 +9853,28 @@ int qla2xxx_delete_qpair(struct scsi_qla
 {
 	int ret = QLA_FUNCTION_FAILED;
 	struct qla_hw_data *ha = qpair->hw;
+	struct rsp_que *rsp = qpair->rsp;
 
 	qpair->delete_in_progress = 1;
 
 	qla_free_buf_pool(qpair);
 
+	/*
+	 * The response-queue interrupt schedules qla_do_work(), which
+	 * dereferences qpair->rsp->req.  Release the interrupt and flush
+	 * any pending work before the request queue is freed below so a
+	 * late completion cannot touch the freed request queue.  The
+	 * firmware queue-delete order (request then response) is kept.
+	 */
+	if (rsp && rsp->msix && rsp->msix->have_irq) {
+		free_irq(rsp->msix->vector, rsp->msix->handle);
+		rsp->msix->have_irq = 0;
+		rsp->msix->in_use = 0;
+		rsp->msix->handle = NULL;
+	}
+	if (rsp && ha->wq)
+		cancel_work_sync(&qpair->q_work);
+
 	ret = qla25xx_delete_req_que(vha, qpair->req);
 	if (ret != QLA_SUCCESS)
 		goto fail;
author maintainer
#492 lore raw

PATCH 492/1424 scsi: qla2xxx: Avoid double completion in async IOCB timeout

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit bb45bc4bd53c95a7bf6f782577b5ede94c0f8aa8 upstream.

qla2x00_async_iocb_timeout() tries to abort a timed-out async IOCB. When qla24xx_async_abort_cmd() fails, both the SRB_LOGIN_CMD path and the SRB_CTRL_VP/default path scan outstanding_cmds[] for the SRB and then call sp->done(sp, QLA_FUNCTION_TIMEOUT) unconditionally, without checking whether the SRB was actually found and removed.

If the response ISR completes the same handle first, it removes the SRB under qp_lock_ptr and runs sp->done() -> complete(sp->comp). The submitter qla24xx_control_vp() wakes from wait_for_completion(), clears sp->comp, drops its reference and returns, reclaiming the on-stack completion. The timer reference keeps the SRB alive across the timeout handler, but not the submitter's stack. The timeout then issues a second sp->done() -> qla_ctrlvp_sp_done(), which evaluates "if (sp->comp) complete(sp->comp)"; with the pointer loaded before the submitter's NULL store, complete() writes into the freed stack frame, a use-after-free.

Track whether this path removed the SRB from outstanding_cmds and only call sp->done() when it did, so the command is completed exactly once by whichever path owns it. This mirrors the sp_found guard already used in qla24xx_abort_iocb_timeout().

Fixes f6145e86d21f ("scsi: qla2xxx: Fix race between switch cmd completion and timeout")
---
 drivers/scsi/qla2xxx/qla_init.c |   24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)
drivers/scsi/qla2xxx/qla_init.c +21 -3
@@ -228,7 +228,7 @@ qla2x00_async_iocb_timeout(void *data)
 	srb_t *sp = data;
 	fc_port_t *fcport = sp->fcport;
 	struct srb_iocb *lio = &sp->u.iocb_cmd;
-	int rc, h;
+	int rc, h, found;
 	unsigned long flags;
 
 	if (fcport) {
@@ -251,6 +251,7 @@ qla2x00_async_iocb_timeout(void *data)
 			lio->u.logio.data[1] =
 				lio->u.logio.flags & SRB_LOGIN_RETRIED ?
 				QLA_LOGIO_LOGIN_RETRIED : 0;
+			found = 0;
 			spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags);
 			for (h = 1; h < sp->qpair->req->num_outstanding_cmds;
 			    h++) {
@@ -258,11 +259,19 @@ qla2x00_async_iocb_timeout(void *data)
 				    sp) {
 					sp->qpair->req->outstanding_cmds[h] =
 					    NULL;
+					found = 1;
 					break;
 				}
 			}
 			spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags);
-			sp->done(sp, QLA_FUNCTION_TIMEOUT);
+			/*
+			 * Only complete the command if this path removed it
+			 * from outstanding_cmds.  Otherwise the ISR already
+			 * completed it and a second sp->done() would race the
+			 * submitter's freeing of the on-stack completion.
+			 */
+			if (found)
+				sp->done(sp, QLA_FUNCTION_TIMEOUT);
 		}
 		break;
 	case SRB_LOGOUT_CMD:
@@ -275,6 +284,7 @@ qla2x00_async_iocb_timeout(void *data)
 	default:
 		rc = qla24xx_async_abort_cmd(sp, false);
 		if (rc) {
+			found = 0;
 			spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags);
 			for (h = 1; h < sp->qpair->req->num_outstanding_cmds;
 			    h++) {
@@ -282,11 +292,19 @@ qla2x00_async_iocb_timeout(void *data)
 				    sp) {
 					sp->qpair->req->outstanding_cmds[h] =
 					    NULL;
+					found = 1;
 					break;
 				}
 			}
 			spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags);
-			sp->done(sp, QLA_FUNCTION_TIMEOUT);
+			/*
+			 * Only complete the command if this path removed it
+			 * from outstanding_cmds.  Otherwise the ISR already
+			 * completed it and a second sp->done() would race the
+			 * submitter's freeing of the on-stack completion.
+			 */
+			if (found)
+				sp->done(sp, QLA_FUNCTION_TIMEOUT);
 		}
 		break;
 	}
author maintainer
#493 lore raw

PATCH 493/1424 scsi: qla2xxx: Bound rsp_info_len to avoid OOB sense-data read

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit ca6d880d6c70cb7946e7b3e05d7285f271b6d99e upstream.

In qla2x00_status_entry(), the FWI2 status path advances sense_data and shrinks par_sense_len by rsp_info_len:

	if (IS_FWI2_CAPABLE(ha)) {
		sense_data += rsp_info_len;
		par_sense_len -= rsp_info_len;
	}

rsp_info_len is a 32-bit value taken directly from the target's FCP response (sf.rsp_data_len), while par_sense_len is the IOCB data area size (28 bytes for 24xx, 60 bytes for 29xx). A hostile or buggy target reporting an rsp_info_len larger than par_sense_len makes the unsigned subtraction underflow to a huge value and advances sense_data out of bounds.

The underflowed par_sense_len then defeats the cap in qla2x00_handle_sense():

	if (sense_len > par_sense_len)
		sense_len = par_sense_len;
	memcpy(cp->sense_buffer, sense_data, sense_len);

so the memcpy reads up to SCSI_SENSE_BUFFERSIZE bytes from the out-of-bounds sense_data pointer, leaking adjacent response-ring/heap memory into the command's sense buffer.

Clamp rsp_info_len to par_sense_len before the subtraction so par_sense_len can never underflow and sense_data stays within the IOCB data area. The fix sits before the comp_status switch, covering both qla2x00_handle_sense() call sites.

---
 drivers/scsi/qla2xxx/qla_isr.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/scsi/qla2xxx/qla_isr.c +12 -0
@@ -3488,6 +3488,18 @@ qla2x00_status_entry(scsi_qla_host_t *vh
 	if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
 		/* Sense data lies beyond any FCP RESPONSE data. */
 		if (IS_FWI2_CAPABLE(ha)) {
+			/*
+			 * A hostile or buggy target may report an
+			 * rsp_info_len larger than the IOCB data area.
+			 * Clamp it so the par_sense_len subtraction cannot
+			 * underflow and walk sense_data out of bounds.
+			 */
+			if (rsp_info_len > par_sense_len) {
+				ql_log(ql_log_warn, fcport->vha, 0x3107,
+				       "Truncating bogus rsp_info_len 0x%x to 0x%x.\n",
+				       rsp_info_len, par_sense_len);
+				rsp_info_len = par_sense_len;
+			}
 			sense_data += rsp_info_len;
 			par_sense_len -= rsp_info_len;
 		}
author maintainer
#494 lore raw

PATCH 494/1424 scsi: qla2xxx: Avoid req_q_map double-read in qla2x00_error_entry()

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit deb8abde83a799d2501f3977f6d6051000253f5e upstream.

qla2x00_error_entry() reads ha->req_q_map[que] twice: once for the NULL check and again when assigning it to req. The map slot is cleared by qla25xx_free_req_que() (ha->req_q_map[que_id] = NULL under mq_lock) during queue teardown, while the response-queue interrupt that drives qla2x00_error_entry() is still registered (the IRQ is released later in qla25xx_free_rsp_que()). If the slot is set to NULL between the two reads, req becomes NULL and is dereferenced.

Read the slot once into req and NULL-check the local before use. mq_lock is a mutex and cannot be taken from interrupt context, so the single read plus local check is the appropriate fix for the reported NULL dereference.

Fixes a6fe35c052c4 ("[SCSI] qla2xxx: Avoid invalid request queue dereference for bad response packets.")
---
 drivers/scsi/qla2xxx/qla_isr.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/scsi/qla2xxx/qla_isr.c +3 -1
@@ -3815,10 +3815,12 @@ qla2x00_error_entry(scsi_qla_host_t *vha
 	    "iocb type %xh with error status %xh, handle %xh, rspq id %d\n",
 	    pkt->entry_type, pkt->entry_status, pkt->handle, rsp->id);
 
-	if (que >= ha->max_req_queues || !ha->req_q_map[que])
+	if (que >= ha->max_req_queues)
 		goto fatal;
 
 	req = ha->req_q_map[que];
+	if (!req)
+		goto fatal;
 
 	if (pkt->entry_status & RF_BUSY)
 		res = DID_BUS_BUSY << 16;
author maintainer
#495 lore raw

PATCH 495/1424 scsi: qla2xxx: Fix NVMe abort reference leak on repeated abort

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 06b5b2a5d499323f1c3256ead35798e8e3d15e60 upstream.

qla_nvme_ls_abort() and qla_nvme_fcp_abort() take a command reference with kref_get_unless_zero() and then call schedule_work() on priv->abort_work, ignoring its return value. qla_nvme_abort_work() runs once and drops exactly one reference via kref_put(&sp->cmd_kref, sp->put_fn).

Since the per-abort INIT_WORK() was moved to submission time, schedule_work() now returns false when the work is already pending, for example on a concurrent transport teardown and timeout-driven abort of the same command. In that case the reference taken for the second abort is never released because the work still executes only once, leaking a reference. The command is then never returned to the NVMe-FC transport, which can hang the port.

Drop the reference when schedule_work() returns false, so each kref_get_unless_zero() is balanced regardless of whether the work was newly queued. The held reference keeps priv->sp valid for the put.

Fixes 7e85f6dbc856 ("scsi: qla2xxx: Initialize NVMe abort_work once at submission")
---
 drivers/scsi/qla2xxx/qla_nvme.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/scsi/qla2xxx/qla_nvme.c +4 -2
@@ -467,7 +467,8 @@ static void qla_nvme_ls_abort(struct nvm
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	schedule_work(&priv->abort_work);
+	if (!schedule_work(&priv->abort_work))
+		kref_put(&priv->sp->cmd_kref, priv->sp->put_fn);
 }
 
 static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
@@ -549,7 +550,8 @@ static void qla_nvme_fcp_abort(struct nv
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	schedule_work(&priv->abort_work);
+	if (!schedule_work(&priv->abort_work))
+		kref_put(&priv->sp->cmd_kref, priv->sp->put_fn);
 }
 
 static inline int qla2x00_start_nvme_mq(srb_t *sp)
author maintainer
#496 lore raw

PATCH 496/1424 scsi: qla2xxx: Drop vport reference under lock in report ID acquisition

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 1154b16439ffc562f9461494c4508c63446eb684 upstream.

qla24xx_report_id_acquisition() format-1 handling takes the vport reference under vport_slock but drops it outside the lock, after setting vp->vp_flags and vp->dpc_flags:

	set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
	set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
	set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);

	atomic_dec(&vp->vref_count);

Neither set_bit() nor atomic_dec() imply a memory barrier, so on a weakly ordered architecture the decrement can become visible before the flag stores. qla24xx_deallocate_vp_id() polls vref_count under vport_slock and unlinks the vport once it reads zero, after which qla24xx_vport_delete() frees it via scsi_host_put(). The poller could therefore observe vref_count == 0 early and tear the vport down while the pending vp_flags/ dpc_flags stores land on freed memory.

Drop the reference under vport_slock, as is done for the matching increment and by every other vref_count user. The unlock release pairs with the deallocate poller's lock acquire so the flag stores are ordered before vref_count == 0 can be observed.

Fixes 793cedee296f ("scsi: qla2xxx: Hold vport reference in qla24xx_report_id_acquisition()")
---
 drivers/scsi/qla2xxx/qla_mbx.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/qla2xxx/qla_mbx.c +2 -0
@@ -4167,7 +4167,9 @@ qla24xx_report_id_acquisition(scsi_qla_h
 			set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
 			set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
 
+			spin_lock_irqsave(&ha->vport_slock, flags);
 			atomic_dec(&vp->vref_count);
+			spin_unlock_irqrestore(&ha->vport_slock, flags);
 		}
 		set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
 		qla2xxx_wake_dpc(vha);
author maintainer
#497 lore raw

PATCH 497/1424 scsi: qla2xxx: Hold vport_slock for host map update in report ID acquisition

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 7944039ba9cb5c3a935d17c91004e3b8649ff58e upstream.

qla24xx_report_id_acquisition() format-1 handling drops vport_slock after taking the vport reference and then calls qla_update_host_map() without the lock. That reaches qla_update_vp_map(), which mutates the ha->host_map btree via btree_insert32()/btree_update32()/btree_remove32() and is documented to require vport_slock to be held by the caller. Running it unlocked can race concurrent host_map updates and corrupt the btree.

The format-2 path in the same function already wraps its host_map update (SET_AL_PA) in vport_slock; the format-1 path is the lone outlier.

Hold vport_slock across the format-1 qla_update_host_map() call to honor the documented locking contract. The vref_count taken in the loop keeps the vport valid, so this only adds the missing host_map serialization.

---
 drivers/scsi/qla2xxx/qla_mbx.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/scsi/qla2xxx/qla_mbx.c +2 -0
@@ -4157,7 +4157,9 @@ qla24xx_report_id_acquisition(scsi_qla_h
 			if (!found)
 				return;
 
+			spin_lock_irqsave(&ha->vport_slock, flags);
 			qla_update_host_map(vp, id);
+			spin_unlock_irqrestore(&ha->vport_slock, flags);
 
 			/*
 			 * Cannot configure here as we are still sitting on the
author maintainer
#498 lore raw

PATCH 498/1424 scsi: qla2xxx: Use coherent DMA buffer for D_Port diagnostics

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit 7c4f3f50d83af4545efaa99b3d0d46fb8d52031e upstream.

qla26xx_dport_diagnostics() streaming-maps the caller's result buffer with dma_map_single(). The bsg path passes &dd->buf from the __packed struct qla_dport_diag, where buf lands at a 2-byte offset and shares cachelines with the surrounding options/unused fields. Mapping such a misaligned sub-buffer violates the DMA API requirement that streaming buffers be cacheline aligned and not share a cacheline with other data, and can corrupt data on non-DMA-coherent architectures.

Allocate a dedicated DMA-coherent buffer inside qla26xx_dport_diagnostics() for the mailbox command and copy the result back into the caller's buffer. This removes the streaming map of the misaligned sub-buffer entirely; the caller's buffer is now only a plain CPU buffer, so its packing no longer matters.

---
 drivers/scsi/qla2xxx/qla_mbx.c |   15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)
drivers/scsi/qla2xxx/qla_mbx.c +7 -8
@@ -6442,6 +6442,7 @@ qla26xx_dport_diagnostics(scsi_qla_host_
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 	dma_addr_t dd_dma;
+	void *dd;
 
 	if (!IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) &&
 	    !IS_QLA28XX(vha->hw))
@@ -6450,15 +6451,12 @@ qla26xx_dport_diagnostics(scsi_qla_host_
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x119f,
 	    "Entered %s.\n", __func__);
 
-	dd_dma = dma_map_single(&vha->hw->pdev->dev,
-	    dd_buf, size, DMA_FROM_DEVICE);
-	if (dma_mapping_error(&vha->hw->pdev->dev, dd_dma)) {
-		ql_log(ql_log_warn, vha, 0x1194, "Failed to map dma buffer.\n");
+	dd = dma_alloc_coherent(&vha->hw->pdev->dev, size, &dd_dma, GFP_KERNEL);
+	if (!dd) {
+		ql_log(ql_log_warn, vha, 0x1194, "Failed to allocate dma buffer.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 
-	memset(dd_buf, 0, size);
-
 	mcp->mb[0] = MBC_DPORT_DIAGNOSTICS;
 	mcp->mb[1] = options;
 	mcp->mb[2] = MSW(LSD(dd_dma));
@@ -6480,8 +6478,9 @@ qla26xx_dport_diagnostics(scsi_qla_host_
 		    "Done %s.\n", __func__);
 	}
 
-	dma_unmap_single(&vha->hw->pdev->dev, dd_dma,
-	    size, DMA_FROM_DEVICE);
+	memcpy(dd_buf, dd, size);
+
+	dma_free_coherent(&vha->hw->pdev->dev, size, dd, dd_dma);
 
 	return rval;
 }
author maintainer
#499 lore raw

PATCH 499/1424 scsi: qla2xxx: Zero-init bsg stack buffers to avoid info leak

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit b93d3bb3afe1b44489927de1eb4e66e8536a5935 upstream.

Several bsg handlers stage their request/reply in an uninitialized 256-byte on-stack buffer (uint8_t bsg[DMA_POOL_SIZE]) and fill it via sg_copy_to_buffer(), which only copies as many bytes as the user-supplied request payload. When the request is shorter than the structure, the remainder of the buffer is left holding stale stack data.

qla2x00_read_fru_status() and qla2x00_read_i2c() then copy the full structure back to the reply payload with sg_copy_from_buffer(), leaking the uninitialized stack bytes to user space. The write/update paths do not copy the buffer back, but can feed uninitialized fields to the device.

Zero the stack buffer at declaration in all five handlers, mirroring the heap kzalloc() approach, so short requests can no longer expose stale memory.

Fixes 697a4bc69159 ("[SCSI] qla2xxx: Provide method for updating I2C attached VPD.")
Fixes 9ebb5d9c69f1 ("[SCSI] qla2xxx: Add I2C BSG interface.")
---
 drivers/scsi/qla2xxx/qla_bsg.c |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/scsi/qla2xxx/qla_bsg.c +5 -5
@@ -1560,7 +1560,7 @@ qla2x00_update_fru_versions(struct bsg_j
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_image_version_list *list = (void *)bsg;
 	struct qla_image_version *image;
 	uint32_t count;
@@ -1620,7 +1620,7 @@ qla2x00_read_fru_status(struct bsg_job *
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
 	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
@@ -1671,7 +1671,7 @@ qla2x00_write_fru_status(struct bsg_job
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
 	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
@@ -1718,7 +1718,7 @@ qla2x00_write_i2c(struct bsg_job *bsg_jo
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
 	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
@@ -1770,7 +1770,7 @@ qla2x00_read_i2c(struct bsg_job *bsg_job
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
 	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
author maintainer
#500 lore raw

PATCH 500/1424 scsi: qla2xxx: Skip NVMe LS reject IOCB when FW not started

In reply to Greg Kroah-Hartman

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

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

From: Nilesh Javali <[email protected]>

commit f7e46ebffc5781aab3f1f5a5d4350addbb5833f4 upstream.

qla_nvme_xmt_ls_rsp() bails out to the out: label when firmware is not started (!ha->flags.fw_started), but the out: path unconditionally calls qla_nvme_ls_reject_iocb(), which ends in qla2x00_start_iocbs() and an unconditional doorbell write to the request queue in-pointer register. This rings the firmware doorbell and queues an IOCB that stopped or resetting firmware cannot consume, and touches MMIO during the reset/EEH window where fw_started is also clear.

Only emit the LS reject IOCB (and ring the doorbell) when fw_started is set; otherwise just clean up and return. The post-allocation failure cases (SRB alloc / qla2x00_start_sp() failure) run with firmware started and still send the reject. Apply the same guard to the reject emission in qla2xxx_process_purls_pkt().

Fixes 875386b98857 ("scsi: qla2xxx: Add Unsolicited LS Request and Response Support for NVMe")
---
 drivers/scsi/qla2xxx/qla_nvme.c |   19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)
drivers/scsi/qla2xxx/qla_nvme.c +13 -6
@@ -442,9 +442,11 @@ out:
 	a.vp_idx = vha->vp_idx;
 	a.nport_handle = uctx->nport_handle;
 	a.xchg_address = uctx->exchange_address;
-	spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
-	qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
-	spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
+	if (ha->flags.fw_started) {
+		spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
+		qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
+		spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
+	}
 	kfree(uctx);
 	return rval;
 }
@@ -1206,9 +1208,14 @@ qla2xxx_process_purls_pkt(struct scsi_ql
 		a.vp_idx = vha->vp_idx;
 		a.nport_handle = uctx->nport_handle;
 		a.xchg_address = uctx->exchange_address;
-		spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr, flags);
-		qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
-		spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr, flags);
+		if (vha->hw->flags.fw_started) {
+			spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr,
+					  flags);
+			qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a,
+						true);
+			spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr,
+					       flags);
+		}
 		list_del(&uctx->elem);
 		kfree(uctx);
 	}
author maintainer
#501 lore raw

PATCH 501/1424 f2fs: return symlink writeback errors

In reply to Greg Kroah-Hartman

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

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

From: Wenjie Qi <[email protected]>

commit a2c73a7a677afdaa8b16d775188f9ef5cfbfd8b2 upstream.

F2FS writes long symlink data with page_symlink() and then flushes the symlink mapping to reduce the chance of exposing a broken symlink.

That flush result is currently ignored. If the writeback fails, symlink() still returns success even though the symlink is not durable and the same operation can already surface -EIO through syncfs().

Return the writeback error to userspace and skip the dirsync flush once the symlink data flush has failed.

Fixes d0cae97cb600 ("f2fs: flush symlink path to avoid broken symlink after POR")
---
 fs/f2fs/namei.c |   11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)
fs/f2fs/namei.c +6 -5
@@ -673,15 +673,16 @@ err_out:
 	 * performance regression.
 	 */
 	if (!err) {
-		filemap_write_and_wait_range(inode->i_mapping, 0,
-							disk_link.len - 1);
+		err = filemap_write_and_wait_range(inode->i_mapping, 0,
+						   disk_link.len - 1);
 
-		if (IS_DIRSYNC(dir))
+		if (!err && IS_DIRSYNC(dir))
 			f2fs_sync_fs(sbi->sb, 1);
-	} else {
-		f2fs_unlink(dir, dentry);
 	}
 
+	if (err)
+		f2fs_unlink(dir, dentry);
+
 	f2fs_balance_fs(sbi, true);
 	goto out_free_encrypted_link;
 
author maintainer
#502 lore raw

PATCH 502/1424 f2fs: reject overlapping move range after len expansion

In reply to Greg Kroah-Hartman

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

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

From: Hao-Qun Huang <[email protected]>

commit 28c1ef094e7c86977d9bf570dc0362fc54e36437 upstream.

F2FS_IOC_MOVE_RANGE treats a zero length as a request to move data from pos_in to EOF. However, the same-file overlap check runs before that expansion, so a request with len == 0 bypasses the overlap rejection added for same-file moves.

For example, with a four-block file, moving from block 0 to block 1 with len == 0 is accepted by the old check because pos_in + len is still pos_in at that point. The code then expands len to cover the rest of the file and calls __exchange_data_block() on overlapping source and destination ranges in the same inode, which is the data-corruption case the overlap check was meant to reject.

Move the overlap check after the source range has been validated and len == 0 has been expanded, so it sees the effective length. This is a no-op for non-zero len (the value is unchanged there) and keeps the existing early return for identical positions.

Fixes d95fd91c1ac1 ("f2fs: exclude special cases for f2fs_move_file_range")
Assisted-by Claude:claude-fable-5
---
 fs/f2fs/file.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/f2fs/file.c +2 -2
@@ -3018,8 +3018,6 @@ static int f2fs_move_file_range(struct f
 	if (src == dst) {
 		if (pos_in == pos_out)
 			return 0;
-		if (pos_out > pos_in && pos_out < pos_in + len)
-			return -EINVAL;
 	}
 
 	inode_lock(src);
@@ -3045,6 +3043,8 @@ static int f2fs_move_file_range(struct f
 		goto out_unlock;
 	if (len == 0)
 		olen = len = src->i_size - pos_in;
+	if (src == dst && pos_out > pos_in && pos_out < pos_in + len)
+		goto out_unlock;
 	if (pos_in + len == src->i_size)
 		len = ALIGN(src->i_size, F2FS_BLKSIZE) - pos_in;
 	if (len == 0) {
author maintainer
#503 lore raw

PATCH 503/1424 f2fs: use the mount idmap for the owner check in f2fs_xattr_advise_set()

In reply to Greg Kroah-Hartman

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

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

From: Zhan Xusheng <[email protected]>

commit a54ffce4637acb0db8e695188a6c7f99f14c3576 upstream.

f2fs_xattr_advise_set() calls inode_owner_or_capable() with &nop_mnt_idmap before allowing the "system.advise" xattr to be set, instead of the idmap that the VFS passes to the ->set() handler.

f2fs supports idmapped mounts, so on such a mount this checks the caller's fsuid against the unmapped on-disk owner rather than the mapped owner: the actual owner can be wrongly denied with -EPERM and an unrelated caller wrongly allowed. Pass the handler's idmap instead.

Fixes 01beba7957a2 ("fs: port inode_owner_or_capable() to mnt_idmap")
Acked-by Christian Brauner (Amutable) <braurg>
---
 fs/f2fs/xattr.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/f2fs/xattr.c +1 -1
@@ -118,7 +118,7 @@ static int f2fs_xattr_advise_set(const s
 	unsigned char old_advise = F2FS_I(inode)->i_advise;
 	unsigned char new_advise;
 
-	if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
+	if (!inode_owner_or_capable(idmap, inode))
 		return -EPERM;
 	if (value == NULL)
 		return -EINVAL;
author maintainer
#504 lore raw

PATCH 504/1424 f2fs: return writeback error from collapse range

In reply to Greg Kroah-Hartman

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

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

From: Wenjie Qi <[email protected]>

commit f8a4108800254d6f7b2755515fbbd9d0caac561e upstream.

f2fs_collapse_range() writes back pages moved by f2fs_do_collapse(), but ignores the return value. If writeback fails, the ioctl can still truncate page cache, shrink blocks, and report success.

Return the error before truncating page cache or updating the file size.

Fixes b4ace3370324 ("f2fs: support FALLOC_FL_COLLAPSE_RANGE")
Assisted-by Codex:gpt-5.5
---
 fs/f2fs/file.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
fs/f2fs/file.c +4 -1
@@ -1531,11 +1531,14 @@ static int f2fs_collapse_range(struct in
 
 	/* write out all moved pages, if possible */
 	filemap_invalidate_lock(inode->i_mapping);
-	filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
+	ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
+	if (ret)
+		goto out_unlock;
 	truncate_pagecache(inode, offset);
 
 	new_size = i_size_read(inode) - len;
 	ret = f2fs_truncate_blocks(inode, new_size, true);
+out_unlock:
 	filemap_invalidate_unlock(inode->i_mapping);
 	if (!ret)
 		f2fs_i_size_write(inode, new_size);
author maintainer
#505 lore raw

PATCH 505/1424 f2fs: fix i_size when pinned fallocate partially fails

In reply to Greg Kroah-Hartman

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

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

From: Zhan Xusheng <[email protected]>

commit 0f448bb3767ef6119f5cdeabcae3f10d6e75aed6 upstream.

From: Zhan Xusheng <[email protected]>

Commit 4275b59673eb ("f2fs: fix to round down start offset of fallocate for pin file") moved the allocation loop's start down to a section boundary, but the error path still converts @expanded against @pg_start, which holds the unrounded start.

@pg_start exists for that conversion: commit 88f2cfc5fa90 ("f2fs: fix to update last i_size if fallocate partially succeeds") added it as an immutable base because map.m_lblk moves every round. Each round now maps exactly sec_blks blocks starting from rounddown(pg_start, sec_blks), so pg_start + expanded overshoots the last allocated block by pg_start % sec_blks, and a partial failure leaves i_size covering a tail that was never allocated. Nothing corrects that afterwards either, since file_dont_truncate() has already cleared FADVISE_TRUNC_BIT.

It needs a start offset that is not section aligned plus a fallocate that hits ENOSPC partway, so the error path runs with expanded > 0. On an 80 MiB image with 2 MiB sections:

truncate -s 80M img
mkfs.f2fs -s 1 -f img
mount -o loop img /mnt
touch /mnt/pinned
f2fs_io pinfile set /mnt/pinned
# 2093056 = block 511, so pg_start % sec_blks = 511 f2fs_io fallocate 0 2093056 536870912 /mnt/pinned stat -c %s /mnt/pinned
filefrag -v /mnt/pinned

The last extent ends at block 10737 either way. Before, i_size is 46075904, block 11249, so 511 blocks of it were never allocated, and filefrag does not mark the last extent eof. After, i_size is 43982848, block 10738, and eof is back. A kernel from before that commit also shows no overshoot.

Keep @pg_start pointing at where allocation actually begins.

Fixes 4275b59673eb ("f2fs: fix to round down start offset of fallocate for pin file")
---
 fs/f2fs/file.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
fs/f2fs/file.c +3 -2
@@ -1832,8 +1832,9 @@ static int f2fs_expand_inode_data(struct
 		block_t sec_len;
 
 		if (map.m_lblk % sec_blks) {
-			map.m_lblk = rounddown(map.m_lblk, sec_blks);
-			map.m_len = pg_end - map.m_lblk;
+			pg_start = rounddown(map.m_lblk, sec_blks);
+			map.m_lblk = pg_start;
+			map.m_len = pg_end - pg_start;
 			if (off_end)
 				map.m_len++;
 		}
author maintainer
#506 lore raw

PATCH 506/1424 f2fs: fix to off-by-one issue in f2fs_zero_post_eof_page()

In reply to Greg Kroah-Hartman

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

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

From: Chao Yu <[email protected]>

commit b2205d3cfd6c76fd5c5443ee9fdb498cfb0e1c66 upstream.

Otherwise, it will drop one more page after new_size which is not necessary.

Fixes ba8dac350faf ("f2fs: fix to zero post-eof page")
---
 fs/f2fs/file.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/f2fs/file.c +1 -1
@@ -50,7 +50,7 @@ static void f2fs_zero_post_eof_page(stru
 	if (lock)
 		filemap_invalidate_lock(inode->i_mapping);
 	/* zero or drop pages only in range of [old_size, new_size] */
-	truncate_inode_pages_range(inode->i_mapping, old_size, new_size);
+	truncate_inode_pages_range(inode->i_mapping, old_size, new_size - 1);
 	if (lock)
 		filemap_invalidate_unlock(inode->i_mapping);
 }
author maintainer
#507 lore raw

PATCH 507/1424 f2fs: fix to zero post-EOF data when extending file size

In reply to Greg Kroah-Hartman

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

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

From: Chao Yu <[email protected]>

commit 5eced87b7d19dbc76ebdddaf322046f9ac582fcb upstream.

generic/794 4s ... - output mismatch (see /share/git/fstests/results//generic/794.out.bad)

#    --- tests/generic/794.out   2026-06-12 08:46:32.766426241 +0800
#    +++ /share/git/fstests/results//generic/794.out.bad 2026-07-05 18:32:55.000000000 +0800
#    @@ -1,4 +1,16 @@
#     QA output created by 794
#     append_write
#    +FAIL: non-zero data in gap [4080,4096) after shutdown+remount
#    +000000 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a  >ZZZZZZZZZZZZZZZZ<
#    +*
#    +001000

# truncate_up
# ...
# (Run 'diff -u /share/git/fstests/tests/generic/794.out /share/git/fstests/results//generic/794.out.bad' to see the entire diff)
Ran: generic/794
Failures: generic/794
Failed 1 of 1 tests

Steps of generic/794:
1. write 4096 bytes to file w/ 0x5a
2. use fiemap to get PBA of first block in file
3. truncate file to 4080
4. umount; write 4096 bytes to file w/ 0x5a directly via PBA; mount
5. extend filesize via
a) append 4096 from offset 4096, or
b) truncate 8192, or
c) fallocate 4096 from offset 4096
6. verify the gap is zeroed in memory [4080,4096)
7. sync range 4096 from offset 4096; shutdown -f (flush meta before shutdown)
8. umount; mount; verify [4080,4096) is zeroed or not.

When extending file size (e.g. via truncate, fallocate, or write) across an unaligned EOF boundary, we need to ensure that post-EOF data in the partial page is zeroed out in pagecache and marked dirty, then writeback the cache to persist zeroed data before committing inode w/ updated i_size.

This help to prevent stale disk data beyond the previous EOF from being exposed after remounting or crash recovery.

Since f2fs is a LFS filesystem, we only support direct write via PBA in pinfile, and pinfile has section-aligned filesize, so in Android, there should no problem, but for other usage in different environment, let's fix this w/ fsync_mode=strict mount option.

---
 fs/f2fs/file.c |  100 +++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 84 insertions(+), 16 deletions(-)
fs/f2fs/file.c +84 -16
@@ -36,16 +36,52 @@
 #include <trace/events/f2fs.h>
 #include <uapi/linux/f2fs.h>
 
-static void f2fs_zero_post_eof_page(struct inode *inode,
-					loff_t new_size, bool lock)
+static int fill_zero(struct inode *inode, pgoff_t index,
+					loff_t start, loff_t len);
+
+static int do_zero_post_eof_page(struct inode *inode, loff_t new_size)
+{
+	loff_t old_size = i_size_read(inode);
+	unsigned int offset, len;
+	pgoff_t index;
+	int err;
+
+	offset = old_size & (PAGE_SIZE - 1);
+
+	if (!offset)
+		return 0;
+
+	len = min_t(loff_t, PAGE_SIZE - offset, new_size - old_size);
+	index = old_size >> PAGE_SHIFT;
+
+	if (f2fs_has_inline_data(inode)) {
+		/* data post eof should be always zero */
+		if (new_size <= MAX_INLINE_DATA(inode))
+			return 0;
+		err = f2fs_convert_inline_inode(inode);
+		if (err)
+			return err;
+	}
+
+	err = fill_zero(inode, index, offset, len);
+	if (err)
+		return err;
+	return filemap_write_and_wait_range(inode->i_mapping,
+				old_size, old_size + len - 1);
+}
+
+static int f2fs_zero_post_eof_page(struct inode *inode,
+					loff_t new_size, bool lock, bool writeback)
 {
 	loff_t old_size = i_size_read(inode);
+	bool strict =
+		F2FS_OPTION(F2FS_I_SB(inode)).fsync_mode == FSYNC_MODE_STRICT;
 
 	if (old_size >= new_size)
-		return;
+		return 0;
 
-	if (mapping_empty(inode->i_mapping))
-		return;
+	if (!strict && mapping_empty(inode->i_mapping))
+		return 0;
 
 	if (lock)
 		filemap_invalidate_lock(inode->i_mapping);
@@ -53,6 +89,16 @@ static void f2fs_zero_post_eof_page(stru
 	truncate_inode_pages_range(inode->i_mapping, old_size, new_size - 1);
 	if (lock)
 		filemap_invalidate_unlock(inode->i_mapping);
+
+	if (!writeback || !strict)
+		return 0;
+	/*
+	 * In fsync_mode=strict, when we expand an unaligned EOF size, we
+	 * should zero post EOF data and writeback the data immediately,
+	 * so that it can avoid exposing stale data after metadata flush
+	 * and POR.
+	 */
+	return do_zero_post_eof_page(inode, new_size);
 }
 
 static vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf)
@@ -123,7 +169,10 @@ static vm_fault_t f2fs_vm_page_mkwrite(s
 
 	f2fs_bug_on(sbi, f2fs_has_inline_data(inode));
 
-	f2fs_zero_post_eof_page(inode, (folio->index + 1) << PAGE_SHIFT, true);
+	err = f2fs_zero_post_eof_page(inode,
+		(folio->index + 1) << PAGE_SHIFT, true, false);
+	if (err)
+		goto out_pagefault;
 
 	file_update_time(vmf->vma->vm_file);
 	filemap_invalidate_lock_shared(inode->i_mapping);
@@ -180,7 +229,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(s
 
 out_sem:
 	filemap_invalidate_unlock_shared(inode->i_mapping);
-
+out_pagefault:
 	sb_end_pagefault(inode->i_sb);
 out:
 	ret = vmf_fs_error(err);
@@ -1085,8 +1134,12 @@ int f2fs_setattr(struct mnt_idmap *idmap
 		f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
 		filemap_invalidate_lock(inode->i_mapping);
 
-		if (attr->ia_size > old_size)
-			f2fs_zero_post_eof_page(inode, attr->ia_size, false);
+		if (attr->ia_size > old_size) {
+			err = f2fs_zero_post_eof_page(inode,
+				attr->ia_size, false, true);
+			if (err)
+				goto err_out;
+		}
 		truncate_setsize(inode, attr->ia_size);
 
 		if (attr->ia_size <= old_size)
@@ -1095,6 +1148,7 @@ int f2fs_setattr(struct mnt_idmap *idmap
 		 * do not trim all blocks after i_size if target size is
 		 * larger than i_size.
 		 */
+err_out:
 		filemap_invalidate_unlock(inode->i_mapping);
 		f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
 		if (err)
@@ -1205,7 +1259,9 @@ static int f2fs_punch_hole(struct inode
 	if (ret)
 		return ret;
 
-	f2fs_zero_post_eof_page(inode, offset + len, true);
+	ret = f2fs_zero_post_eof_page(inode, offset + len, true, false);
+	if (ret)
+		return ret;
 
 	pg_start = ((unsigned long long) offset) >> PAGE_SHIFT;
 	pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT;
@@ -1491,7 +1547,9 @@ static int f2fs_do_collapse(struct inode
 	f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
 	filemap_invalidate_lock(inode->i_mapping);
 
-	f2fs_zero_post_eof_page(inode, offset + len, false);
+	ret = f2fs_zero_post_eof_page(inode, offset + len, false, false);
+	if (ret)
+		goto out_unlock;
 
 	f2fs_lock_op(sbi);
 	f2fs_drop_extent_tree(inode);
@@ -1499,6 +1557,7 @@ static int f2fs_do_collapse(struct inode
 	ret = __exchange_data_block(inode, inode, end, start, nrpages - end, true);
 	f2fs_unlock_op(sbi);
 
+out_unlock:
 	filemap_invalidate_unlock(inode->i_mapping);
 	f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
 	return ret;
@@ -1621,7 +1680,9 @@ static int f2fs_zero_range(struct inode
 	if (ret)
 		return ret;
 
-	f2fs_zero_post_eof_page(inode, offset + len, true);
+	ret = f2fs_zero_post_eof_page(inode, offset + len, true, false);
+	if (ret)
+		return ret;
 
 	pg_start = ((unsigned long long) offset) >> PAGE_SHIFT;
 	pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT;
@@ -1755,7 +1816,9 @@ static int f2fs_insert_range(struct inod
 	f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
 	filemap_invalidate_lock(mapping);
 
-	f2fs_zero_post_eof_page(inode, offset + len, false);
+	ret = f2fs_zero_post_eof_page(inode, offset + len, false, false);
+	if (ret)
+		goto out_unlock;
 	truncate_pagecache(inode, offset);
 
 	while (!ret && idx > pg_start) {
@@ -1771,6 +1834,7 @@ static int f2fs_insert_range(struct inod
 					idx + delta, nr, false);
 		f2fs_unlock_op(sbi);
 	}
+out_unlock:
 	filemap_invalidate_unlock(mapping);
 	f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
 
@@ -1811,7 +1875,9 @@ static int f2fs_expand_inode_data(struct
 	if (err)
 		return err;
 
-	f2fs_zero_post_eof_page(inode, offset + len, true);
+	err = f2fs_zero_post_eof_page(inode, offset + len, true, true);
+	if (err)
+		return err;
 
 	f2fs_balance_fs(sbi, true);
 
@@ -4783,8 +4849,10 @@ static ssize_t f2fs_write_checks(struct
 	if (err)
 		return err;
 
-	f2fs_zero_post_eof_page(inode,
-		iocb->ki_pos + iov_iter_count(from), true);
+	err = f2fs_zero_post_eof_page(inode,
+		iocb->ki_pos + iov_iter_count(from), true, true);
+	if (err)
+		return err;
 	return count;
 }
 
author maintainer
#508 lore raw

PATCH 508/1424 drm/bridge: dw-hdmi: fix i2c adapter leak on probe failure

In reply to Greg Kroah-Hartman

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

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

From: Johan Hovold <[email protected]>

commit 09b195a7bb23df56269cd2a95d01ba3a5533af13 upstream.

Make sure to drop the i2c adapter device and module references before returning when detecting a malformed devicetree during probe.

Fixes 80e2f97968b5 ("drm: bridge: dw-hdmi: Switch to regmap for register access")
Cc Neil Armstrong <[email protected]>
---
 drivers/gpu/drm/bridge/synopsys/dw-hdmi.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +2 -1
@@ -3401,7 +3401,8 @@ struct dw_hdmi *dw_hdmi_probe(struct pla
 			break;
 		default:
 			dev_err(dev, "reg-io-width must be 1 or 4\n");
-			return ERR_PTR(-EINVAL);
+			ret = -EINVAL;
+			goto err_res;
 		}
 
 		iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
author maintainer
#509 lore raw

PATCH 509/1424 drm/panel-edp: fix i2c adapter leak on probe failure

In reply to Greg Kroah-Hartman

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

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

From: Johan Hovold <[email protected]>

commit e2a9e291275a74e309a21cbb1def6296a72d6aed upstream.

Make sure to drop the i2c adapter reference on probe failure (e.g. probe deferral) and on driver unbind also if a devicetree redundantly uses the 'ddc-i2c-bus' property to point to the aux ddc bus.

---
 drivers/gpu/drm/panel/panel-edp.c |   19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)
drivers/gpu/drm/panel/panel-edp.c +13 -6
@@ -830,6 +830,13 @@ exit:
 	return ret;
 }
 
+static void panel_edp_put_adapter(void *_adap)
+{
+	struct i2c_adapter *adap = _adap;
+
+	put_device(&adap->dev);
+}
+
 static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
 			   struct drm_dp_aux *aux)
 {
@@ -877,6 +884,11 @@ static int panel_edp_probe(struct device
 
 		if (!panel->ddc)
 			return -EPROBE_DEFER;
+
+		err = devm_add_action_or_reset(dev, panel_edp_put_adapter,
+					       panel->ddc);
+		if (err)
+			return err;
 	} else if (aux) {
 		panel->ddc = &aux->ddc;
 	}
@@ -890,7 +902,7 @@ static int panel_edp_probe(struct device
 
 	err = drm_panel_of_backlight(&panel->base);
 	if (err)
-		goto err_finished_ddc_init;
+		return err;
 
 	/*
 	 * We use runtime PM for prepare / unprepare since those power the panel
@@ -931,9 +943,6 @@ static int panel_edp_probe(struct device
 err_finished_pm_runtime:
 	pm_runtime_dont_use_autosuspend(dev);
 	pm_runtime_disable(dev);
-err_finished_ddc_init:
-	if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc))
-		put_device(&panel->ddc->dev);
 
 	return err;
 }
@@ -948,8 +957,6 @@ static void panel_edp_remove(struct devi
 
 	pm_runtime_dont_use_autosuspend(dev);
 	pm_runtime_disable(dev);
-	if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc))
-		put_device(&panel->ddc->dev);
 
 	kfree(panel->edid);
 	panel->edid = NULL;
author maintainer
#510 lore raw

PATCH 510/1424 drm: fix race between partial drm_dev_register() failure and ioctl

In reply to Greg Kroah-Hartman

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

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

From: Danilo Krummrich <[email protected]>

commit eb197f7d60f00d0f5b1b3505dfc86a7e36045a3e upstream.

If drm_dev_register() fails after registering a minor (e.g. render minor registered, primary minor fails), userspace could have opened the first minor and entered a drm_dev_enter() critical section. Since the unplugged flag was never set, the ioctl proceeds while the error path tears down device resources.

Fix this by introducing drm_dev_synchronize_unplug(), which sets the unplugged flag and waits for the SRCU barrier, ensuring all in-flight drm_dev_enter() critical sections complete before cleanup proceeds; call it on the error path of drm_dev_register().

---
 drivers/gpu/drm/drm_drv.c |   34 +++++++++++++++++++++++++---------
 1 file changed, 25 insertions(+), 9 deletions(-)
drivers/gpu/drm/drm_drv.c +25 -9
@@ -470,6 +470,22 @@ void drm_dev_exit(int idx)
 }
 EXPORT_SYMBOL(drm_dev_exit);
 
+/*
+ * Mark the device as unplugged and wait for any in-flight drm_dev_enter()
+ * critical sections to complete.
+ */
+static void drm_dev_synchronize_unplug(struct drm_device *dev)
+{
+	/*
+	 * After synchronizing any critical read section is guaranteed to see
+	 * the new value of ->unplugged, and any critical section which might
+	 * still have seen the old value of ->unplugged is guaranteed to have
+	 * finished.
+	 */
+	dev->unplugged = true;
+	synchronize_srcu(&drm_unplug_srcu);
+}
+
 /**
  * drm_dev_unplug - unplug a DRM device
  * @dev: DRM device
@@ -482,15 +498,7 @@ EXPORT_SYMBOL(drm_dev_exit);
  */
 void drm_dev_unplug(struct drm_device *dev)
 {
-	/*
-	 * After synchronizing any critical read section is guaranteed to see
-	 * the new value of ->unplugged, and any critical section which might
-	 * still have seen the old value of ->unplugged is guaranteed to have
-	 * finished.
-	 */
-	dev->unplugged = true;
-	synchronize_srcu(&drm_unplug_srcu);
-
+	drm_dev_synchronize_unplug(dev);
 	drm_dev_unregister(dev);
 
 	/* Clear all CPU mappings pointing to this device */
@@ -931,6 +939,7 @@ int drm_dev_register(struct drm_device *
 		goto err_minors;
 
 	dev->registered = true;
+	dev->unplugged = false;
 
 	if (driver->load) {
 		ret = driver->load(dev, flags);
@@ -956,6 +965,13 @@ err_unload:
 	if (dev->driver->unload)
 		dev->driver->unload(dev);
 err_minors:
+	/*
+	 * If a minor was registered before the failure, userspace could have
+	 * opened it and entered a drm_dev_enter() critical section. Ensure all
+	 * such sections complete before we clean up.
+	 */
+	drm_dev_synchronize_unplug(dev);
+
 	remove_compat_control_link(dev);
 	drm_minor_unregister(dev, DRM_MINOR_ACCEL);
 	drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
author maintainer
#511 lore raw

PATCH 511/1424 drm/i915: Guard against NULL driver_data in i915_pci_probe()

In reply to Greg Kroah-Hartman

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

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

From: Deepanshu Kartikey <[email protected]>

commit 3785d40831ba5601296283e0197e10e089392757 upstream.

pci_match_device() can return the dummy pci_device_id_any entry when a device is force-bound via sysfs driver_override, in which case ->driver_data is unset (NULL). i915_pci_probe() casts it to struct intel_device_info * unconditionally and dereferences intel_info->require_force_probe, causing a NULL-ptr-deref.

---
 drivers/gpu/drm/i915/i915_pci.c |    3 +++
 1 file changed, 3 insertions(+)
drivers/gpu/drm/i915/i915_pci.c +3 -0
@@ -1014,6 +1014,9 @@ static int i915_pci_probe(struct pci_dev
 		(struct intel_device_info *) ent->driver_data;
 	int err;
 
+	if (!intel_info)
+		return -ENODEV;
+
 	if (intel_info->require_force_probe && !id_forced(pdev->device)) {
 		dev_info(&pdev->dev,
 			 "Your graphics device %04x is not properly supported by i915 in this\n"
author maintainer
#512 lore raw

PATCH 512/1424 drm/sun4i: fix refcount leak in sun4i_backend_init_sat()

In reply to Greg Kroah-Hartman

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

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

From: Wentao Liang <[email protected]>

commit f7a56ff6240e6fd0cb36a3c0a911a1cd54789ce2 upstream.

When sun4i_backend_init_sat() calls reset_control_deassert() it increments the deassert_count of the reset controller, and must pair that with a reset_control_assert() call to decrement it. In the error path where clk_prepare_enable() fails, the function returns immediately without calling reset_control_assert(), leaking the reference count. Other error paths, like the devm_clk_get() failure, correctly jump to the err_assert_reset label which performs the missing assert.

Fix the leak by using the existing err_assert_reset label in the clk_prepare_enable error path instead of returning directly.

---
 drivers/gpu/drm/sun4i/sun4i_backend.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/sun4i/sun4i_backend.c +1 -1
@@ -683,7 +683,7 @@ static int sun4i_backend_init_sat(struct
 	ret = clk_prepare_enable(backend->sat_clk);
 	if (ret) {
 		dev_err(dev, "Couldn't enable the SAT clock\n");
-		return ret;
+		goto err_assert_reset;
 	}
 
 	return 0;
author maintainer
#513 lore raw

PATCH 513/1424 drm/hibmc: Fix list of formats on the primary plane

In reply to Greg Kroah-Hartman

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

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

From: Thomas Zimmermann <[email protected]>

commit 0e682e136c466ae37c62f18099f591c096260ee0 upstream.

Remove all formats from the primary plane that are unsupported for various reasons.

* Formats with alpha channel: planes should not announce alpha channels unless they support transparency. There's no transparency support in the primary plane's implementation.

* Formats with BGR order. The common format is in RGB channel order. There's no BGR support in the primary plane's implementation.

* RGB888: atomic_update programs the format from cpp[0] * 8 / 16. For RGB888's cpp value of 3 this returns 1.5; rounded to 1. Programming the value of 1 to HIBMC_CRT_DISP_CTL_FORMAT sets up RGB565. Hence, the output is distorted. This can be tested by booting with video=1024x768-24.

Removing all unsupported formats leaves XRGB8888 and RGB565. Both of which are supported and work correctly.

Fixes da52605eea8f ("drm/hisilicon/hibmc: Add support for display engine")
Cc Rongrong Zou <[email protected]>
Cc Sean Paul <[email protected]>
Cc Xinliang Liu <[email protected]>
Cc Dmitry Baryshkov <[email protected]>
Cc Yongbang Shi <[email protected]>
Cc Baihan Li <[email protected]>
Cc <[email protected]> # v4.10+
---
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c |    6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +2 -4
@@ -153,10 +153,8 @@ static void hibmc_plane_atomic_update(st
 }
 
 static const u32 channel_formats1[] = {
-	DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888,
-	DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
-	DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888,
-	DRM_FORMAT_ABGR8888
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_RGB565,
 };
 
 static const struct drm_plane_funcs hibmc_plane_funcs = {
author maintainer
#514 lore raw

PATCH 514/1424 drm/hibmc: Use drm_atomic_helper_check_plane_state()

In reply to Greg Kroah-Hartman

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

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

From: Thomas Zimmermann <[email protected]>

commit 715c5db68bdbd4a524b79ebf20fb61e880fffea0 upstream.

Call drm_atomic_helper_check_plane_state() from the primary plane's atomic-check helper and replace the custom implementation.

All plane's implementations of atomic_check should call the shared _check_plane_state() helper first. It adjusts the plane state for correct positioning, rotation and scaling of the plane. Do this even if the plane's CRTC has been disabled by setting the parameter can_update_disabled. The original code returned early in this case, but it's safe to so and cleaner to have all plane state initialized.

As we don't set can_position, drm_atomic_helper_check_plane_state()'s visibility check tests if the plane covers all of the CRTC. This is a small change from the original code, which tested if the plane is exactly the size of the CRTC. With the new test, the plane still has to cover all of the CRTC, but can be larger than the CRTC's size. A later patch can fully implement this feature in hibmc.

If the plane is disabled, the helper clears the visibility flag in the plane state. On errors or if the plane is not visible, the atomic-check helper can return early. Implement all this in hibmc and drop the custom code that does some of it.

v2:
- extend the commit description (Yongbang)

Fixes da52605eea8f ("drm/hisilicon/hibmc: Add support for display engine")
Cc Rongrong Zou <[email protected]>
Cc Sean Paul <[email protected]>
Cc Xinliang Liu <[email protected]>
Cc Dmitry Baryshkov <[email protected]>
Cc Baihan Li <[email protected]>
Cc Yongbang Shi <[email protected]>
Cc <[email protected]> # v4.10+
---
 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c |   50 ++++++++-----------------
 1 file changed, 16 insertions(+), 34 deletions(-)
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +16 -34
@@ -72,46 +72,28 @@ static int hibmc_get_best_clock_idx(cons
 static int hibmc_plane_atomic_check(struct drm_plane *plane,
 				    struct drm_atomic_state *state)
 {
-	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
-										 plane);
-	struct drm_framebuffer *fb = new_plane_state->fb;
-	struct drm_crtc *crtc = new_plane_state->crtc;
-	struct drm_crtc_state *crtc_state;
-	u32 src_w = new_plane_state->src_w >> 16;
-	u32 src_h = new_plane_state->src_h >> 16;
-
-	if (!crtc || !fb)
-		return 0;
-
-	crtc_state = drm_atomic_get_crtc_state(state, crtc);
-	if (IS_ERR(crtc_state))
-		return PTR_ERR(crtc_state);
-
-	if (src_w != new_plane_state->crtc_w || src_h != new_plane_state->crtc_h) {
-		drm_dbg_atomic(plane->dev, "scale not support\n");
-		return -EINVAL;
-	}
-
-	if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0) {
-		drm_dbg_atomic(plane->dev, "crtc_x/y of drm_plane state is invalid\n");
-		return -EINVAL;
-	}
-
-	if (!crtc_state->enable)
+	struct drm_plane_state *new_plane_state =
+		drm_atomic_get_new_plane_state(state, plane);
+	struct drm_crtc_state *new_crtc_state = NULL;
+	int ret;
+
+	if (new_plane_state->crtc)
+		new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc);
+
+	ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
+						  DRM_PLANE_NO_SCALING,
+						  DRM_PLANE_NO_SCALING,
+						  false, true);
+	if (ret)
+		return ret;
+	else if (!new_plane_state->visible)
 		return 0;
 
-	if (new_plane_state->crtc_x + new_plane_state->crtc_w >
-	    crtc_state->adjusted_mode.hdisplay ||
-	    new_plane_state->crtc_y + new_plane_state->crtc_h >
-	    crtc_state->adjusted_mode.vdisplay) {
-		drm_dbg_atomic(plane->dev, "visible portion of plane is invalid\n");
-		return -EINVAL;
-	}
-
 	if (new_plane_state->fb->pitches[0] % 128 != 0) {
 		drm_dbg_atomic(plane->dev, "wrong stride with 128-byte aligned\n");
 		return -EINVAL;
 	}
+
 	return 0;
 }
 
author maintainer
#515 lore raw

PATCH 515/1424 drm/amd/display: avoid divide-by-zero in __is_lut_linear()

In reply to Greg Kroah-Hartman

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

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

From: Harry Wentland <[email protected]>

commit 4f40873f8a4107df2b9c8e68c947c4fd0cd519d2 upstream.

__is_lut_linear() computes the expected value of each entry with

	expected = i * MAX_DRM_LUT_VALUE / (size - 1);

If it is ever called with a single-entry LUT, size - 1 is zero and the kernel takes a divide error (#DE). A LUT with fewer than two entries cannot describe a linear mapping anyway, so return false early instead of dividing by zero.

---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c |    6 ++++++
 1 file changed, 6 insertions(+)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +6 -0
@@ -118,6 +118,12 @@ static bool __is_lut_linear(const struct
 	uint32_t expected;
 	int delta;
 
+	/* A LUT with fewer than two entries can't be interpolated and would
+	 * divide by zero below (size - 1); it can't be treated as linear.
+	 */
+	if (size < 2)
+		return false;
+
 	for (i = 0; i < size; i++) {
 		/* All color values should equal */
 		if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue))
author maintainer
#516 lore raw

PATCH 516/1424 drm/amdgpu/vcn: fix integer overflow in dec_msg buffer count check

In reply to Greg Kroah-Hartman

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

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

From: David (Ming Qiang) Wu <[email protected]>

commit 4d7390530853eb7befda9cc786e4c86e8ad7ac9e upstream.

If the supplied msg[2] (num_buffers) is 0x3FFFFFFF, the expression 6 + num_buffers * 4 wraps to 2 and the bounds check passes, letting the parser loop far past the end of the message BO. Triggering it additionally requires a ~4GiB mapping so that msg[1] survives the earlier "header does not fit in BO" check.

Rewrite the test in division form, which is overflow-free by construction. Also update the message to reflect that msg is invalid.

Fixes b193019860d6 ("drm/amdgpu/vcn3: Prevent OOB reads when parsing dec msg")
Fixes 0a78f2bac142 ("drm/amdgpu/vcn4: Prevent OOB reads when parsing dec msg")
---
 drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c |   10 +++++++---
 drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c |   10 +++++++---
 2 files changed, 14 insertions(+), 6 deletions(-)
drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c +7 -3
@@ -1844,9 +1844,13 @@ static int vcn_v3_0_dec_msg(struct amdgp
 	len_dw = msg[1] / 4;
 	num_buffers = msg[2];
 
-	/* Verify that all indices fit within the claimed length. Each index is 4 DWORDs */
-	if (num_buffers > len_dw || 6 + num_buffers * 4 > len_dw) {
-		DRM_ERROR("VCN message has too many buffers!\n");
+	/* Verify that all indices fit within the claimed length.
+	 * There are 6 dwords in the header before the first buffer.
+	 * Each buffer has 4 dwords. Any trailing dwords after the
+	 * last buffer are ignored.
+	 */
+	if (len_dw < 6 || num_buffers > (len_dw - 6) / 4) {
+		DRM_ERROR("Invalid VCN message!\n");
 		r = -EINVAL;
 		goto out;
 	}
drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +7 -3
@@ -1722,9 +1722,13 @@ static int vcn_v4_0_dec_msg(struct amdgp
 	len_dw = msg[1] / 4;
 	num_buffers = msg[2];
 
-	/* Verify that all indices fit within the claimed length. Each index is 4 DWORDs */
-	if (num_buffers > len_dw || 6 + num_buffers * 4 > len_dw) {
-		DRM_ERROR("VCN message has too many buffers!\n");
+	/* Verify that all indices fit within the claimed length.
+	 * There are 6 dwords in the header before the first buffer.
+	 * Each buffer has 4 dwords. Any trailing dwords after the
+	 * last buffer are ignored.
+	 */
+	if (len_dw < 6 || num_buffers > (len_dw - 6) / 4) {
+		DRM_ERROR("Invalid VCN message!\n");
 		r = -EINVAL;
 		goto out;
 	}
author maintainer
#517 lore raw

PATCH 517/1424 drm/gud: NUL-terminate TV mode names read from the device

In reply to Greg Kroah-Hartman

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

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

From: Deepanshu Kartikey <[email protected]>

commit 500cb24cd61bad8a2747ddfc49b7034899c82d94 upstream.

gud_connector_add_tv_mode() reads a buffer of fixed-size mode names from the USB device and passes pointers into it to drm_mode_create_tv_properties_legacy(), which calls strlen() on each one. Nothing guarantees the device NUL-terminates a name, so strlen() can run past the end of a slot and, for the last mode, past the end of the allocation.

Terminate each name at the end of its slot before use.

---
 drivers/gpu/drm/gud/gud_connector.c |    9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/gpu/drm/gud/gud_connector.c +7 -2
@@ -397,8 +397,13 @@ static int gud_connector_add_tv_mode(str
 	}
 
 	num_modes = ret / GUD_CONNECTOR_TV_MODE_NAME_LEN;
-	for (i = 0; i < num_modes; i++)
-		modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
+	for (i = 0; i < num_modes; i++) {
+		char *mode = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
+
+		/* The device is not trusted to NUL-terminate the name */
+		mode[GUD_CONNECTOR_TV_MODE_NAME_LEN - 1] = '\0';
+		modes[i] = mode;
+	}
 
 	ret = drm_mode_create_tv_properties_legacy(connector->dev, num_modes, modes);
 free:
author maintainer
#518 lore raw

PATCH 518/1424 drm/gud: validate TV mode names before creating enum property

In reply to Greg Kroah-Hartman

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

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

From: Tao Yu <[email protected]>

commit da1ea35fea67ad841f4ada28dd61b41be65e5437 upstream.

The GUD protocol returns TV mode names as fixed-size GUD_CONNECTOR_TV_MODE_NAME_LEN entries and requires each name to be NUL-terminated.

gud_connector_add_tv_mode() currently passes each fixed-size entry directly to drm_mode_create_tv_properties_legacy(), which eventually reaches drm_property_add_enum() and strlen(). If a device returns an entry without a terminating NUL byte, strlen() reads past the end of the slot and can run beyond the allocated buffer, triggering an out-of-bounds read.

Validate that each returned TV mode name contains a NUL terminator within its fixed-size slot before passing it to the DRM property code. If a malformed entry is found, reject the device response with -EIO.

This fixes the out-of-bounds read without changing the handling of valid devices, and avoids silently truncating malformed protocol data.

---
 drivers/gpu/drm/gud/gud_connector.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/gpu/drm/gud/gud_connector.c +5 -2
@@ -400,8 +400,11 @@ static int gud_connector_add_tv_mode(str
 	for (i = 0; i < num_modes; i++) {
 		char *mode = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
 
-		/* The device is not trusted to NUL-terminate the name */
-		mode[GUD_CONNECTOR_TV_MODE_NAME_LEN - 1] = '\0';
+		if (!memchr(mode, '\0', GUD_CONNECTOR_TV_MODE_NAME_LEN)) {
+			ret = -EIO;
+			goto free;
+		}
+
 		modes[i] = mode;
 	}
 
author maintainer
#519 lore raw

PATCH 519/1424 drm/msm/dsi: round 6G byte clock rate to the PLL-achievable value

In reply to Greg Kroah-Hartman

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

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

From: Kavan Smith <[email protected]>

commit 6cd33b6f4155efc20485929fd0b56bb704641db9 upstream.

MSM8916 runtime DSI commands still go through msm_dsi_host_xfer_prepare(), which re-applies the link clock rate before enabling the link clocks. That is fine in principle, but on DSI 6G the requested byte clock rate often does not exactly match the DSI PHY PLL's realizable rate. For example, the driver can request 56250000 Hz while the PLL actually runs at 56246337 Hz.

Because the requested and actual rates differ slightly, every later link_clk_set_rate() call is treated as a real clock change and re-locks the PLL. On a video-mode panel without an internal timing generator, such as samsung,s6d7aa0 / lsl080al03 on MSM8916, that live-clock glitch makes the panel lose pixel lock and visibly corrupts scanout on each runtime DCS command, including backlight writes.

Fix this by rounding the computed 6G byte clock rate up front, before it is stored in msm_host->byte_clk_rate and reused by later transfers. Once the host carries the PLL-achievable rate instead of the idealized one, repeated link_clk_set_rate() calls become no-ops in the common clock framework and no longer re-lock the PLL.

This keeps the normal transfer callback sequencing intact, preserves the OPP vote path in link_clk_set_rate(), and matches the fix direction suggested in the original 2018 discussion.

Fixes 6b16f05aa39f ("drm/msm/dsi: Split clk rate setting and enable")

Patchwork: https://patchwork.freedesktop.org/patch/738234/

---
 drivers/gpu/drm/msm/dsi/dsi_host.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)
drivers/gpu/drm/msm/dsi/dsi_host.c +12 -0
@@ -635,12 +635,24 @@ static void dsi_calc_pclk(struct msm_dsi
 
 int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
 {
+	long rounded_byte_clk_rate;
+
 	if (!msm_host->mode) {
 		pr_err("%s: mode not set\n", __func__);
 		return -EINVAL;
 	}
 
 	dsi_calc_pclk(msm_host, is_bonded_dsi);
+
+	rounded_byte_clk_rate = clk_round_rate(msm_host->byte_clk,
+					       msm_host->byte_clk_rate);
+	if (rounded_byte_clk_rate < 0) {
+		pr_err("%s: failed to round byte clock rate, %ld\n",
+		       __func__, rounded_byte_clk_rate);
+		return rounded_byte_clk_rate;
+	}
+
+	msm_host->byte_clk_rate = rounded_byte_clk_rate;
 	msm_host->esc_clk_rate = clk_get_rate(msm_host->esc_clk);
 	return 0;
 }
author maintainer
#520 lore raw

PATCH 520/1424 drm: Fix drm_crtc_commit leak if signaled when PAGE_FLIP_EVENT is used

In reply to Greg Kroah-Hartman

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

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

From: Thadeu Lima de Souza Cascardo <[email protected]>

commit 4d4be202165e832d74849b4a68e289a2a377039c upstream.

Commit 1c6ceeee6ebb ("drm/atomic: Fix memleak on ERESTARTSYS during non-blocking commits") fixed a very similar issue when the event was allocated by drm_atomic_helper_setup_commit() itself.

However, if the event is allocated in prepare_signaling(), it will also be set to NULL in complete_signaling(), which prevents drm_crtc_commit from being put in __drm_atomic_helper_crtc_destroy_state().

Dropping the reference when the event is set to NULL at complete_signaling() fixes the leak.

The leak can be reproduced by sending a signal to the thread using DRM_MODE_PAGE_FLIP_EVENT and using a sw_sync fence to cause the atomic ioctl to block at drm_atomic_helper_wait_for_fences(). It happened both with amdgpu and vkms.

---
 drivers/gpu/drm/drm_atomic_uapi.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/gpu/drm/drm_atomic_uapi.c +2 -0
@@ -1304,6 +1304,8 @@ static void complete_signaling(struct dr
 		 * to prevent a double free in drm_atomic_state_clear.
 		 */
 		if (event && (event->base.fence || event->base.file_priv)) {
+			if (crtc_state->commit && crtc_state->commit->abort_completion)
+				drm_crtc_commit_put(crtc_state->commit);
 			drm_event_cancel_free(dev, &event->base);
 			crtc_state->event = NULL;
 		}
author maintainer
#521 lore raw

PATCH 521/1424 drm/amdgpu: check thunderbolt before switcheroo registration

In reply to Greg Kroah-Hartman

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

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

From: Yang Wang <[email protected]>

commit 8587d48d694da5aca580f92461658ec14470592b upstream.

Introduce a helper to consolidate the vga_switcheroo registration condition used by the init and fini paths.

Keep the explicit pci_is_thunderbolt_attached() check, as dev_is_removable() does not provide equivalent coverage for Thunderbolt-attached GPUs. This ensures such devices remain excluded from switcheroo registration while preserving the existing PX and Apple gmux handling.

---
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c |   14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +10 -4
@@ -3586,6 +3586,14 @@ static void amdgpu_device_set_mcbp(struc
 		DRM_INFO("MCBP is enabled\n");
 }
 
+static bool
+amdgpu_device_should_register_switcheroo(struct amdgpu_device *adev, bool px)
+{
+	return !pci_is_thunderbolt_attached(adev->pdev) &&
+	       (px || (!dev_is_removable(&adev->pdev->dev) &&
+		       apple_gmux_detect(NULL, NULL)));
+}
+
 /**
  * amdgpu_device_init - initialize the driver
  *
@@ -4002,8 +4010,7 @@ fence_driver_init:
 
 	px = amdgpu_device_supports_px(ddev);
 
-	if (px || (!dev_is_removable(&adev->pdev->dev) &&
-				apple_gmux_detect(NULL, NULL)))
+	if (amdgpu_device_should_register_switcheroo(adev, px))
 		vga_switcheroo_register_client(adev->pdev,
 					       &amdgpu_switcheroo_ops, px);
 
@@ -4162,8 +4169,7 @@ void amdgpu_device_fini_sw(struct amdgpu
 
 	px = amdgpu_device_supports_px(adev_to_drm(adev));
 
-	if (px || (!dev_is_removable(&adev->pdev->dev) &&
-				apple_gmux_detect(NULL, NULL)))
+	if (amdgpu_device_should_register_switcheroo(adev, px))
 		vga_switcheroo_unregister_client(adev->pdev);
 
 	if (px)
author maintainer
#522 lore raw

PATCH 522/1424 drm/amdgpu: fix autosuspend cleanup during removal

In reply to Greg Kroah-Hartman

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

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

From: Guangshuo Li <[email protected]>

commit ef5fcf2a6c320676bf8be2dadac93d9023b468b7 upstream.

amdgpu_pci_probe() calls pm_runtime_use_autosuspend(), but amdgpu_pci_remove() does not call the matching pm_runtime_dont_use_autosuspend().

If the autosuspend delay is set to a negative value while autosuspend is enabled, the runtime PM core increments usage_count to prevent runtime suspend. Without calling pm_runtime_dont_use_autosuspend() during teardown, this reference is not dropped and usage_count remains unbalanced.

The documentation for pm_runtime_use_autosuspend() also notes that it is important to undo it with pm_runtime_dont_use_autosuspend() at driver exit time, unless runtime PM was initially enabled with devm_pm_runtime_enable().

Add the missing pm_runtime_dont_use_autosuspend() call to the remove path.

This issue was found by manual code inspection.

---
 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c |    1 +
 1 file changed, 1 insertion(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +1 -0
@@ -2247,6 +2247,7 @@ amdgpu_pci_remove(struct pci_dev *pdev)
 	if (adev->pm.rpm_mode != AMDGPU_RUNPM_NONE) {
 		pm_runtime_get_sync(dev->dev);
 		pm_runtime_forbid(dev->dev);
+		pm_runtime_dont_use_autosuspend(dev->dev);
 	}
 
 	if (adev->ip_versions[MP1_HWIP][0] == IP_VERSION(13, 0, 2) &&
author maintainer
#523 lore raw

PATCH 523/1424 drm/amdgpu: use AMDGPU_GPU_PAGE_SHIFT instead of PAGE_SHIFT

In reply to Greg Kroah-Hartman

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

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

From: Sunil Khatri <[email protected]>

commit d6e16df7df4d2c39e2b04b355d0434fb90e2d62c upstream.

For different address types the variable PAGE_SHIFT might not work well and it's better to use the GPU specific one

(cherry picked from commit 3494b77d10375e0f9ab784e9b20763339844b55b)
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +1 -1
@@ -1760,7 +1760,7 @@ int amdgpu_vm_bo_clear_mappings(struct a
 			after->start = eaddr + 1;
 			after->last = tmp->last;
 			after->offset = tmp->offset;
-			after->offset += (after->start - tmp->start) << PAGE_SHIFT;
+			after->offset += (after->start - tmp->start) << AMDGPU_GPU_PAGE_SHIFT;
 			after->flags = tmp->flags;
 			after->bo_va = tmp->bo_va;
 			list_add(&after->list, &tmp->bo_va->invalids);
author maintainer
#524 lore raw

PATCH 524/1424 drm/amdkfd: Reject zero-sized AQL queue allocations after size halving

In reply to Greg Kroah-Hartman

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

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

From: Sunday Clement <[email protected]>

commit 40ba09e11188d1b7f79d51fc28aca5ea45e0c138 upstream.

KFD_IOC_ALLOC_MEMORY_OF_GPU with flag KFD_IOC_ALLOC_MEM_FLAGS_AQL_QUEUE_MEM and size=1 triggers the AQL wraparound workaround (size >>= 1), reducing size to 0. The resulting zero passes through PAGE_ALIGN(0) = 0 without validation, bypassing the per-process VRAM quota check in reserve_mem_limit() (vram_used + 0 > vram_available is always false).

The fix adds post-halving zero-size validation in the primary allocation path (amdgpu_amdkfd_gpuvm.c). The check happens after size halving but before reserve_mem_limit(), and uses err_alignment_size error path to properly clean up the allocated kgd_mem structure and mutex.

---
 drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c |    7 +++++++
 drivers/gpu/drm/amd/amdkfd/kfd_chardev.c         |    3 ++-
 2 files changed, 9 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +7 -0
@@ -1719,6 +1719,12 @@ int amdgpu_amdkfd_gpuvm_alloc_memory_of_
 		size >>= 1;
 	aligned_size = PAGE_ALIGN(size);
 
+	/* reject AQL queue with size < 2 */
+	if (!aligned_size) {
+		ret = -EINVAL;
+		goto err_alignment_size;
+	}
+
 	(*mem)->alloc_flags = flags;
 
 	amdgpu_sync_create(&(*mem)->sync);
@@ -1795,6 +1801,7 @@ err_bo_create:
 	amdgpu_amdkfd_unreserve_mem_limit(adev, aligned_size, flags, xcp_id);
 err_reserve_limit:
 	amdgpu_sync_free(&(*mem)->sync);
+err_alignment_size:
 	mutex_destroy(&(*mem)->lock);
 	if (gobj)
 		drm_gem_object_put(gobj);
drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +2 -1
@@ -1183,7 +1183,8 @@ static int kfd_ioctl_alloc_memory_of_gpu
 
 		if (flags & KFD_IOC_ALLOC_MEM_FLAGS_AQL_QUEUE_MEM)
 			size >>= 1;
-		atomic64_add(PAGE_ALIGN(size), &pdd->vram_usage);
+		size = PAGE_ALIGN(size);
+		atomic64_add(size, &pdd->vram_usage);
 	}
 
 	mutex_unlock(&p->mutex);
author maintainer
#525 lore raw

PATCH 525/1424 drm/nouveau: Use write-combined maps for coherent

In reply to Greg Kroah-Hartman

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

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

From: Faith Ekstrand <[email protected]>

commit 774b73428e6eabb4f0382aeeb76e569c7b106a29 upstream.

On Tegra devices, uncached maps translate to device memory, causing unaligned accesses by userspace resulting in a SIGBUS. Instead, use write-combined maps to ensure proper access.

This would also affect discrete cards on any Arm device. It was determined that discrete cards regardless of cpu arch should use write-combined maps for coherent anyways. Thus this change is made for all gpu types.

---
 drivers/gpu/drm/nouveau/nouveau_sgdma.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/gpu/drm/nouveau/nouveau_sgdma.c +1 -3
@@ -72,9 +72,7 @@ nouveau_sgdma_create_ttm(struct ttm_buff
 	struct nouveau_sgdma_be *nvbe;
 	enum ttm_caching caching;
 
-	if (nvbo->force_coherent)
-		caching = ttm_uncached;
-	else if (drm->agp.bridge)
+	if (nvbo->force_coherent || drm->agp.bridge)
 		caching = ttm_write_combined;
 	else
 		caching = ttm_cached;
author maintainer
#526 lore raw

PATCH 526/1424 drm/nouveau/uvmm: fix premature region free on failed OP_UNMAP_SPARSE

In reply to Greg Kroah-Hartman

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

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

From: Zhenhao Wan <[email protected]>

commit ccf930812f23b8259ef64fd3394d53b093e4651a upstream.

In nouveau_uvmm_bind_job_submit()'s OP_UNMAP_SPARSE arm, op->reg is set from nouveau_uvma_region_find(), which only looks the region up and takes no reference; a region's sole reference is its membership in uvmm->region_mt. Two failure paths leave op->reg set: the -ENOENT check when the region is busy, and the drm_gpuvm_sm_unmap_ops_create() failure. The sibling nouveau_uvmm_sm_unmap_prepare() failure just below clears op->reg; these two do not.

unwind_continue steps back one op, so the failing op is skipped by the unwind loop and its op->reg stays set. nouveau_uvmm_bind_job_cleanup() then enters its if (op->reg) branch and calls nouveau_uvma_region_remove() and nouveau_uvma_region_put() on it, dropping the tree's sole reference and freeing a region this job never created. The comment above the cleanup loop documents the broken invariant: op->reg must be NULL on submit failure.

This frees a live region on an unrelated failure, reachable single-job when drm_gpuvm_sm_unmap_ops_create() returns -ENOMEM; if another job owns the same region, its cleanup then removes and puts the freed region, a use-after-free. Clear op->reg on both failure paths.

Fixes b88baab82871 ("drm/nouveau: implement new VM_BIND uAPI")
Assisted-by Claude:claude-opus-5
---
 drivers/gpu/drm/nouveau/nouveau_uvmm.c |    2 ++
 1 file changed, 2 insertions(+)
drivers/gpu/drm/nouveau/nouveau_uvmm.c +2 -0
@@ -1191,6 +1191,7 @@ nouveau_uvmm_bind_job_submit(struct nouv
 							   op->va.range);
 			if (!op->reg || op->reg->dirty) {
 				ret = -ENOENT;
+				op->reg = NULL;
 				goto unwind_continue;
 			}
 
@@ -1199,6 +1200,7 @@ nouveau_uvmm_bind_job_submit(struct nouv
 								op->va.range);
 			if (IS_ERR(op->ops)) {
 				ret = PTR_ERR(op->ops);
+				op->reg = NULL;
 				goto unwind_continue;
 			}
 
author maintainer
#527 lore raw

PATCH 527/1424 drm/nouveau/uvmm: clear the dirty flag when unwinding an OP_UNMAP_SPARSE

In reply to Greg Kroah-Hartman

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

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

From: Zhenhao Wan <[email protected]>

commit 38a62306c4266bcb3cd89e33c7111ee33096ebb3 upstream.

A successful OP_UNMAP_SPARSE marks its region dirty with nouveau_uvma_region_dirty() and defers the teardown to nouveau_uvmm_bind_job_cleanup(); it does not remove the region from uvmm->region_mt.

If a later op in the job fails, the unwind path never clears reg->dirty (set in one place, cleared nowhere) and sets op->reg = NULL, so cleanup skips the teardown. The region is left in the tree with dirty set and its completion never signalled. Later binds over that range then fail permanently -- -ENOENT or -EINVAL from the dirty checks, or an unkillable wait_for_completion() in bind_validate_region() -- for the lifetime of the uvmm.

Clear reg->dirty when the unwind reverts the sparse unmap, restoring the region to the state it was found in.

Fixes b88baab82871 ("drm/nouveau: implement new VM_BIND uAPI")
Assisted-by Claude:claude-opus-5
---
 drivers/gpu/drm/nouveau/nouveau_uvmm.c |    1 +
 1 file changed, 1 insertion(+)
drivers/gpu/drm/nouveau/nouveau_uvmm.c +1 -0
@@ -1392,6 +1392,7 @@ unwind:
 						    op->va.range);
 			break;
 		case OP_UNMAP_SPARSE:
+			op->reg->dirty = false;
 			__nouveau_uvma_region_insert(uvmm, op->reg);
 			nouveau_uvmm_sm_unmap_prepare_unwind(uvmm, &op->new,
 							     op->ops);
author maintainer
#528 lore raw

PATCH 528/1424 xhci: fix lost bounce buffers on TDs spanning several ring segments

In reply to Greg Kroah-Hartman

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

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

From: Arthur Gautier <[email protected]>

commit ff44dfb03a293bf30e31f98772a1dd316a6071d1 upstream.

When a TD reaches a link TRB with data that is not aligned to the endpoint's wMaxPacketSize, xhci_align_td() stages the unalignable tail through the bounce buffer of the ring segment holding that link TRB. xhci_unmap_td_bounce_buffer() later unmaps it and, for IN transfers, copies the data back into the URB's buffer.

The enqueue path records the segment that was bounced in td->bounce_seg, under the assumption that a TD never spans more than two ring segments. That assumption does not hold: a TD large enough to span three or more segments crosses several link TRBs and can be bounced at each of them. Only the last one survives in td->bounce_seg, so every earlier bounce buffer is neither copied back nor DMA unmapped.

The URB still completes with actual_length equal to the requested length and no error, so the transfer looks successful while a wMaxPacketSize sized hole in the destination buffer silently keeps its previous contents. It also leaks a DMA mapping per dropped bounce.

Any sufficiently large and fragmented bulk transfer can hit this. It was found with a USB mass storage device behind xHCI backing a dm-verity target with 512 byte hash blocks, where the stale data is detected rather than silently consumed. The device enumerates as SuperSpeed, so wMaxPacketSize is 1024, while dm-bufio issues one 512 byte bio per hash block. verity_prefetch_io() makes the block layer merge hundreds of them into a single request of up to 512 scatterlist entries of 512 bytes each. At 256 TRBs per ring segment such a TD spans three segments, and every segment boundary falls on an odd multiple of 512, i.e. unaligned to wMaxPacketSize. dm-bufio then caches a hash block holding stale data and dm-verity declares the metadata block corrupted:

device-mapper: verity: 8:2: metadata block 10850 is corrupted

A reproducer running this under qemu is available at
https://github.com/baloo/xhci-verity

The bounce state (bounce_buf, bounce_dma, bounce_len, bounce_offs) already lives on the ring segment, so there is nothing extra to track. Keep recording the last bounced segment in td->bounce_seg and, on completion, walk the segments from td->start_seg up to it, unmapping every segment that still has a pending bounce.

Stopping at td->bounce_seg rather than td->end_seg matters: a bounce implies the TD continues past that segment's link TRB, so bounce_seg is always strictly before end_seg, and a later TD may already have started in end_seg and been bounced there. Walking that far would copy a foreign bounce buffer into this URB and unmap it twice. It also keeps the walk correct if a TD ever wraps the whole ring so that end_seg == start_seg.

[mn: Add ring->num_segs check to prevent unlikely infinite for loop.]

Fixes f9c589e142d0 ("xhci: TD-fragment, align the unsplittable case with a bounce buffer")
[Michal: solved context conflict due to xhci_td_cleanup() type]
---
 drivers/usb/host/xhci-ring.c |   32 ++++++++++++++++++++++++--------
 1 file changed, 24 insertions(+), 8 deletions(-)
drivers/usb/host/xhci-ring.c +24 -8
@@ -802,21 +802,18 @@ static void xhci_giveback_urb_in_irq(str
 	usb_hcd_giveback_urb(hcd, urb, status);
 }
 
-static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci,
-		struct xhci_ring *ring, struct xhci_td *td)
+static void xhci_unmap_one_bounce_buffer(struct xhci_hcd *xhci,
+		struct xhci_ring *ring, struct xhci_td *td,
+		struct xhci_segment *seg)
 {
 	struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
-	struct xhci_segment *seg = td->bounce_seg;
 	struct urb *urb = td->urb;
 	size_t len;
 
-	if (!ring || !seg || !urb)
-		return;
-
 	if (usb_urb_dir_out(urb)) {
 		dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len,
 				 DMA_TO_DEVICE);
-		return;
+		goto done;
 	}
 
 	dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len,
@@ -832,10 +829,29 @@ static void xhci_unmap_td_bounce_buffer(
 		memcpy(urb->transfer_buffer + seg->bounce_offs, seg->bounce_buf,
 		       seg->bounce_len);
 	}
+done:
 	seg->bounce_len = 0;
 	seg->bounce_offs = 0;
 }
 
+static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci,
+		struct xhci_ring *ring, struct xhci_td *td)
+{
+	struct xhci_segment *seg;
+	int i = 0;
+
+	if (!td->bounce_seg || !ring || !td->urb)
+		return;
+
+	/* td->bounce_seg is the last one bounced, unmap them all */
+	for (seg = td->start_seg; i++ < ring->num_segs; seg = seg->next) {
+		if (seg->bounce_len)
+			xhci_unmap_one_bounce_buffer(xhci, ring, td, seg);
+		if (seg == td->bounce_seg)
+			break;
+	}
+}
+
 static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td,
 			   struct xhci_ring *ep_ring, int status)
 {
@@ -3787,7 +3803,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *
 						  &trb_buff_len,
 						  ring->enq_seg)) {
 					send_addr = ring->enq_seg->bounce_dma;
-					/* assuming TD won't span 2 segs */
+					/* TD bounced at least, and last on this seg */
 					td->bounce_seg = ring->enq_seg;
 				}
 			}
author maintainer
#529 lore raw

PATCH 529/1424 tcp: clear sock_ops cb flags before force-closing a child socket

In reply to Greg Kroah-Hartman

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

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

From: Sechang Lim <[email protected]>

[ Upstream commit 990348e5bb457697c2f1f7f7b65154a3334d9d2b ]

A child socket inherits the listener's bpf_sock_ops_cb_flags via sk_clone_lock(). If its setup fails in tcp_v4_syn_recv_sock() / tcp_v6_syn_recv_sock(), the child is freed through put_and_exit, where inet_csk_prepare_forced_close() drops the socket lock and tcp_done() runs without it.

If BPF_SOCK_OPS_STATE_CB_FLAG was inherited, tcp_done() -> tcp_set_state() calls tcp_call_bpf(), which expects the lock and trips sock_owned_by_me():

  WARNING: include/net/sock.h:1799 at tcp_set_state+0x433/0x550
  RIP: 0010:tcp_set_state+0x433/0x550 include/net/sock.h:1799
  Call Trace:
   <IRQ>
   tcp_done+0xba/0x250 net/ipv4/tcp.c:5095
   tcp_v4_syn_recv_sock+0x850/0xa50 net/ipv4/tcp_ipv4.c:1787
   tcp_check_req+0xf30/0x1360 net/ipv4/tcp_minisocks.c:926
   tcp_v4_rcv+0x1047/0x1b50 net/ipv4/tcp_ipv4.c:2164
   </IRQ>

The child is freed before it is ever established, so it should run no sock_ops callback. Clear its cb flags in inet_csk_prepare_for_destroy_sock(), the common point for the IPv4, IPv6 and chtls forced-close paths and for the MPTCP ->syn_recv_sock() failure path (dispose_child), which reaches tcp_done() on a child that was never established too.

[ Moved inet_csk_prepare_for_destroy_sock() to inet_connection_sock.c due to header dependency,
Added a guard arount the call to tcp_clear_sock_ops_cb_flags to avoid
calling it for DDCP sockets.]

---
 include/net/inet_connection_sock.h | 8 +-------
 include/net/tcp.h                  | 9 +++++++++
 net/ipv4/inet_connection_sock.c    | 9 +++++++++
 3 files changed, 19 insertions(+), 7 deletions(-)
include/net/inet_connection_sock.h +1 -7
index b7935e2937577..4ed883cad65a0 100644
@@ -298,14 +298,8 @@ reqsk_timeout(struct request_sock *req, unsigned long max_timeout)
 	return (unsigned long)min_t(u64, timeout, max_timeout);
 }
 
-static inline void inet_csk_prepare_for_destroy_sock(struct sock *sk)
-{
-	/* The below has to be done to allow calling inet_csk_destroy_sock */
-	sock_set_flag(sk, SOCK_DEAD);
-	this_cpu_inc(*sk->sk_prot->orphan_count);
-}
-
 void inet_csk_destroy_sock(struct sock *sk);
+void inet_csk_prepare_for_destroy_sock(struct sock *sk);
 void inet_csk_prepare_forced_close(struct sock *sk);
 
 /*
include/net/tcp.h +9 -0
index 7392f51a3479b..c75d12e5156db 100644
@@ -2515,6 +2515,11 @@ static inline int tcp_call_bpf_3arg(struct sock *sk, int op, u32 arg1, u32 arg2,
 	return tcp_call_bpf(sk, op, 3, args);
 }
 
+static inline void tcp_clear_sock_ops_cb_flags(struct sock *sk)
+{
+	tcp_sk(sk)->bpf_sock_ops_cb_flags = 0;
+}
+
 #else
 static inline int tcp_call_bpf(struct sock *sk, int op, u32 nargs, u32 *args)
 {
@@ -2532,6 +2537,10 @@ static inline int tcp_call_bpf_3arg(struct sock *sk, int op, u32 arg1, u32 arg2,
 	return -EPERM;
 }
 
+static inline void tcp_clear_sock_ops_cb_flags(struct sock *sk)
+{
+}
+
 #endif
 
 static inline u32 tcp_timeout_init(struct sock *sk)
net/ipv4/inet_connection_sock.c +9 -0
index 208f8b173a056..530d495ff7d28 100644
@@ -1252,6 +1252,15 @@ void inet_csk_destroy_sock(struct sock *sk)
 }
 EXPORT_SYMBOL(inet_csk_destroy_sock);
 
+void inet_csk_prepare_for_destroy_sock(struct sock *sk)
+{
+	/* The below has to be done to allow calling inet_csk_destroy_sock */
+	if (sk->sk_protocol == IPPROTO_TCP)
+		tcp_clear_sock_ops_cb_flags(sk);
+	sock_set_flag(sk, SOCK_DEAD);
+	this_cpu_inc(*sk->sk_prot->orphan_count);
+}
+
 /* This function allows to force a closure of a socket after the call to
  * tcp/dccp_create_openreq_child().
  */
Signature · 1 line
2.53.0
author maintainer
#530 lore raw

PATCH 530/1424 net/mlx5e: xsk: Fix unlocked writing to ICOSQ

In reply to Greg Kroah-Hartman

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

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

From: Dragos Tatulea <[email protected]>

commit c326f9c68921e2f14dfcecb2f6b4216313d50248 upstream.

During napi poll, when the affinity changes and there's still XSK work to be done, we trigger an ICOSQ interrupt on the new CPU. However, this triggering on the ICOSQ is done unprotected.

There are 2 such races:

A) mlx5e_trigger_irq() is called while mlx5e_xsk_alloc_rx_mpwqe() is running from a different CPU due to affinity change. This can happen because IRQ triggering is done after napi_complete_done(). At this point the NAPI can be scheduled on a different CPU. Like this:

CPU A (old affinity, NAPI tail) CPU B (new affinity, fresh NAPI)
------------------------------- --------------------------------
napi_complete_done() clears SCHED
mlx5e_cq_arm(...)

                                     napi_schedule_prep() sets SCHED
                                     mlx5e_napi_poll()
                                       mlx5e_xsk_alloc_rx_mpwqe()
                                         mlx5e_icosq_sync_lock() // noop
                                         memcpy 640 B UMR body
                                         advance sq->pc by 10
  mlx5e_trigger_irq(&c->icosq)
    wqe_info[pi] = {NOP, 1}
    mlx5e_post_nop() advances sq->pc

B) mlx5e_trigger_irq() is called on the ICOSQ when mlx5e_trigger_napi_icosq() is running.

The obvious fix would be to lock the ICOSQ. But ICOSQ has an optimized locking scheme that doesn't work for this scenario. Kick the async ICOSQ instead which is always locked.

This issue was noticed in the wild with the following splat:

netdevice: ge-0-0-1: Bad OP in ICOSQ CQE: 0xd

  WARNING: drivers/net/ethernet/mellanox/mlx5/core/en_rx.c:826 [...]
  [...]
  Call Trace:
   <IRQ>
   mlx5e_napi_poll+0x11d/0x7f0 [mlx5_core]
   __napi_poll+0x30/0x200
   ? skb_defer_free_flush+0x9c/0xc0
   net_rx_action+0x2fe/0x3f0
   handle_softirqs+0xd8/0x340
   __irq_exit_rcu+0xbc/0xe0
   common_interrupt+0x85/0xa0
   </IRQ>
   <TASK>
   asm_common_interrupt+0x26/0x40
  [...]
  ---[ end trace 0000000000000000 ]---
  mlx5_core 0000:08:00.0 ge-0-0-1: Error cqe on cqn 0x548, ci 0x2022, qn 0x8f4,
  opcode 0xd, syndrome 0x2, vendor syndrome 0x68
  00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00000030: 00 00 00 00 01 00 68 02 01 00 08 f4 de 14 59 d2
  WQE DUMP: WQ size 16384 WQ cur size 0, WQE index 0x1e14, len: 64
  00000000: 00 00 00 01 d9 ed 80 02 00 00 00 01 d9 ed 90 02
  00000010: 00 00 00 01 d9 ed a0 02 00 00 00 01 d9 ed b0 02
  00000020: 00 00 00 01 d9 ed c0 02 00 00 00 01 d9 ed d0 02
  00000030: 00 00 00 01 d9 ed e0 02 00 00 00 01 d9 ed f0 02
  mlx5_core 0000:08:00.0 ge-0-0-1: Error cqe on cqn 0x548, ci 0x2023, qn 0x8f4,
  opcode 0xd, syndrome 0x5, vendor syndrome 0xf9
  00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00000030: 00 00 00 00 01 00 f9 05 01 00 08 f4 de 15 cf d2

[ Backport to 6.18.y and older: upstream commit calls
mlx5e_trigger_napi_async_icosq(), which was introduced by commit 0da1dba72616 ("net/mlx5e: XSK, Fix unintended ICOSQ change") and is not present here. In these trees mlx5e_trigger_napi_icosq() is the equivalent helper: it takes c->async_icosq_lock and triggers c->async_icosq, which is unconditionally opened, activated, polled and armed for every channel. Race B does not apply, as it concerns the sync-ICOSQ variant of mlx5e_trigger_napi_icosq() that only exists upstream, and mlx5e_icosq_sync_lock() in the race A diagram has no equivalent here. ]

---
 drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +1 -1
index a7d9b7cb4297f..1affcf149f379 100644
@@ -245,7 +245,7 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
 	}
 
 	if (unlikely(aff_change && busy_xsk)) {
-		mlx5e_trigger_irq(&c->icosq);
+		mlx5e_trigger_napi_icosq(c);
 		ch_stats->force_irq++;
 	}
 
Signature · 1 line
2.53.0
author maintainer
#531 lore raw

PATCH 531/1424 nvmet-auth: Synchronize timeout work during SQ teardown

In reply to Greg Kroah-Hartman

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

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

From: Kazuki Hanai <[email protected]>

[ Upstream commit eaa948c0e19b1bb2d93262207bca0c3d19cc3406 ]

nvmet_auth_sq_free() cancels auth_expired_work with cancel_delayed_work(). If the work has already started, cancellation does not wait for the callback. Transport teardown can consequently free or reuse the queue containing struct nvmet_sq while nvmet_auth_expired_work() still accesses that SQ.

Add a teardown-specific helper that synchronously drains the delayed work before freeing authentication state, and use it from nvmet_sq_destroy(). Keep the non-synchronous helper for in-band authentication state cleanup, where the SQ owner remains alive.

Fixes 1a70200f404a ("nvmet-auth: expire authentication sessions")
---
 drivers/nvme/target/auth.c  | 6 ++++++
 drivers/nvme/target/core.c  | 2 +-
 drivers/nvme/target/nvmet.h | 2 ++
 3 files changed, 9 insertions(+), 1 deletion(-)
drivers/nvme/target/auth.c +6 -0
index 9fe6ad89bf83a..b3a0e19464539 100644
@@ -227,6 +227,12 @@ void nvmet_auth_sq_free(struct nvmet_sq *sq)
 	sq->dhchap_skey = NULL;
 }
 
+void nvmet_auth_sq_destroy(struct nvmet_sq *sq)
+{
+	cancel_delayed_work_sync(&sq->auth_expired_work);
+	nvmet_auth_sq_free(sq);
+}
+
 void nvmet_destroy_auth(struct nvmet_ctrl *ctrl)
 {
 	ctrl->shash_id = 0;
drivers/nvme/target/core.c +1 -1
index e435996c55374..3b1b72022a979 100644
@@ -804,7 +804,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq)
 	wait_for_completion(&sq->confirm_done);
 	wait_for_completion(&sq->free_done);
 	percpu_ref_exit(&sq->ref);
-	nvmet_auth_sq_free(sq);
+	nvmet_auth_sq_destroy(sq);
 
 	/*
 	 * we must reference the ctrl again after waiting for inflight IO
drivers/nvme/target/nvmet.h +2 -0
index c0648216b9e4b..c037d1daf94d1 100644
@@ -704,6 +704,7 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl);
 void nvmet_auth_sq_init(struct nvmet_sq *sq);
 void nvmet_destroy_auth(struct nvmet_ctrl *ctrl);
 void nvmet_auth_sq_free(struct nvmet_sq *sq);
+void nvmet_auth_sq_destroy(struct nvmet_sq *sq);
 int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id);
 bool nvmet_check_auth_status(struct nvmet_req *req);
 int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
@@ -728,6 +729,7 @@ static inline void nvmet_auth_sq_init(struct nvmet_sq *sq)
 }
 static inline void nvmet_destroy_auth(struct nvmet_ctrl *ctrl) {};
 static inline void nvmet_auth_sq_free(struct nvmet_sq *sq) {};
+static inline void nvmet_auth_sq_destroy(struct nvmet_sq *sq) {};
 static inline bool nvmet_check_auth_status(struct nvmet_req *req)
 {
 	return true;
Signature · 1 line
2.53.0
author maintainer
#532 lore raw

PATCH 532/1424 mm/damon/core-kunit: check region count before testing in split_at()

In reply to Greg Kroah-Hartman

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

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

From: SJ Park <[email protected]>

[ Upstream commit 9b18ef3c3aa1ce24176e340061caf15fa2742564 ]

damon_test_split_at() test next region that is assumed to be created by damon_split_region_at() invocation. But the split might fail. In this case, the succeeding test may dereference invalid pointers returned by damon_next_region().

The invalid pointer may not cause a really bad user impact, because of the implementation detail. It would only read wrong contents in the belonging damon_target struct. Depending on the future change of the offset from the link header to the accessing field, this could also be really dangerous, though. Still, the realistic user impact would be limited. It would affect only test run setups.

Fix it by testing if the number of regions was also changed as expected and exit early for the failure.

The issue was discovered [1] by Sashiko.

---
 mm/damon/core-test.h | 5 +++++
 1 file changed, 5 insertions(+)
mm/damon/core-test.h +5 -0
index a2aa410d6d0d0..f2f0978495086 100644
@@ -162,6 +162,10 @@ static void damon_test_split_at(struct kunit *test)
 	}
 	damon_add_region(r, t);
 	damon_split_region_at(t, r, 25);
+	KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 2);
+	if (damon_nr_regions(t) != 2)
+		goto out;
+
 	KUNIT_EXPECT_EQ(test, r->ar.start, 0ul);
 	KUNIT_EXPECT_EQ(test, r->ar.end, 25ul);
 
@@ -169,6 +173,7 @@ static void damon_test_split_at(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, r->ar.start, 25ul);
 	KUNIT_EXPECT_EQ(test, r->ar.end, 100ul);
 
+out:
 	damon_free_target(t);
 	damon_destroy_ctx(c);
 }
Signature · 1 line
2.53.0
author maintainer
#533 lore raw

PATCH 533/1424 mm/damon/vaddr: drop last same folio access check optimization

In reply to Greg Kroah-Hartman

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

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

From: SJ Park <[email protected]>

[ Upstream commit 831846078caa14b7d00b2ccca8b8fe522afe3204 ]

The optimization can race when multiple kdamonds are running. Meanwhile, the impact of the optimization is quite doubtful. Just remove it.

The user impact of the issue should be quite trivial. After all, the race can happen only when the user intentionally setup DAMON in the way. Even if it happens, it would be rare and only degrade the best-effort monitoring results. No critical consequences like kernel panic or memory corruption happen.

The race possibility was discovered [1] by Sashiko.

Fixes 3f49584b262c ("mm/damon: implement primitives for the virtual memory address spaces")
Cc <[email protected]> # 5.15.x
---
 mm/damon/vaddr.c | 34 ++++++----------------------------
 1 file changed, 6 insertions(+), 28 deletions(-)
mm/damon/vaddr.c +6 -28
index 4d7dc9f65f682..c74b3fd1d227a 100644
@@ -429,8 +429,6 @@ static void damon_va_prepare_access_checks(struct damon_ctx *ctx)
 }
 
 struct damon_young_walk_private {
-	/* size of the folio for the access checked virtual memory address */
-	unsigned long *folio_sz;
 	bool young;
 };
 
@@ -466,7 +464,6 @@ static int damon_young_pmd_entry(pmd_t *pmd, unsigned long addr,
 					mmu_notifier_test_young(walk->mm,
 						addr))
 			priv->young = true;
-		*priv->folio_sz = HPAGE_PMD_SIZE;
 		folio_put(folio);
 huge_out:
 		spin_unlock(ptl);
@@ -488,7 +485,6 @@ static int damon_young_pmd_entry(pmd_t *pmd, unsigned long addr,
 	if (pte_young(ptent) || !folio_test_idle(folio) ||
 			mmu_notifier_test_young(walk->mm, addr))
 		priv->young = true;
-	*priv->folio_sz = folio_size(folio);
 	folio_put(folio);
 out:
 	pte_unmap_unlock(pte, ptl);
@@ -517,7 +513,6 @@ static int damon_young_hugetlb_entry(pte_t *pte, unsigned long hmask,
 	if (pte_young(entry) || !folio_test_idle(folio) ||
 	    mmu_notifier_test_young(walk->mm, addr))
 		priv->young = true;
-	*priv->folio_sz = huge_page_size(h);
 
 	folio_put(folio);
 
@@ -535,11 +530,9 @@ static const struct mm_walk_ops damon_young_ops = {
 	.walk_lock = PGWALK_RDLOCK,
 };
 
-static bool damon_va_young(struct mm_struct *mm, unsigned long addr,
-		unsigned long *folio_sz)
+static bool damon_va_young(struct mm_struct *mm, unsigned long addr)
 {
 	struct damon_young_walk_private arg = {
-		.folio_sz = folio_sz,
 		.young = false,
 	};
 
@@ -556,25 +549,13 @@ static bool damon_va_young(struct mm_struct *mm, unsigned long addr,
  * r	the region to be checked
  */
 static void __damon_va_check_access(struct mm_struct *mm,
-				struct damon_region *r, bool same_target)
+				struct damon_region *r)
 {
-	static unsigned long last_addr;
-	static unsigned long last_folio_sz = PAGE_SIZE;
-	static bool last_accessed;
-
-	/* If the region is in the last checked page, reuse the result */
-	if (same_target && (ALIGN_DOWN(last_addr, last_folio_sz) ==
-				ALIGN_DOWN(r->sampling_addr, last_folio_sz))) {
-		if (last_accessed)
-			r->nr_accesses++;
-		return;
-	}
+	bool accessed;
 
-	last_accessed = damon_va_young(mm, r->sampling_addr, &last_folio_sz);
-	if (last_accessed)
+	accessed = damon_va_young(mm, r->sampling_addr);
+	if (accessed)
 		r->nr_accesses++;
-
-	last_addr = r->sampling_addr;
 }
 
 static unsigned int damon_va_check_accesses(struct damon_ctx *ctx)
@@ -583,17 +564,14 @@ static unsigned int damon_va_check_accesses(struct damon_ctx *ctx)
 	struct mm_struct *mm;
 	struct damon_region *r;
 	unsigned int max_nr_accesses = 0;
-	bool same_target;
 
 	damon_for_each_target(t, ctx) {
 		mm = damon_get_mm(t);
 		if (!mm)
 			continue;
-		same_target = false;
 		damon_for_each_region(r, t) {
-			__damon_va_check_access(mm, r, same_target);
+			__damon_va_check_access(mm, r);
 			max_nr_accesses = max(r->nr_accesses, max_nr_accesses);
-			same_target = true;
 		}
 		mmput(mm);
 	}
Signature · 1 line
2.53.0
author maintainer
#534 lore raw

PATCH 534/1424 mm/damon/paddr: drop last same folio access check reuse optimization

In reply to Greg Kroah-Hartman

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

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

From: SJ Park <[email protected]>

[ Upstream commit f23f0aa62b2f32c2b12f95959fc4603ef81678b2 ]

It can race when multiple kdamonds are being used. The problem from the race is doubtful, but the gain from the optimization is also doubtful. Simply drop the optimization in favor of code simplicity.

The user impact is doubtfully trivial. After all, this kind of interference can happen only by intentional user setup. Even if it happens, it will be rare, and the consequence is degradation of the best-effort monitoring results. No critical consequences like kernel panic or memory corruption happen.

The race was discovered [1] by Sashiko.

Fixes a28397beb55b ("mm/damon: implement primitives for physical address space monitoring")
Cc <[email protected]> # 5.16.x
---
 mm/damon/paddr.c | 21 ++++-----------------
 1 file changed, 4 insertions(+), 17 deletions(-)
mm/damon/paddr.c +4 -17
index 909db25efb35e..fe481ae64f37e 100644
@@ -111,7 +111,7 @@ static bool __damon_pa_young(struct folio *folio, struct vm_area_struct *vma,
 	return *accessed == false;
 }
 
-static bool damon_pa_young(unsigned long paddr, unsigned long *folio_sz)
+static bool damon_pa_young(unsigned long paddr)
 {
 	struct folio *folio = damon_get_folio(PHYS_PFN(paddr));
 	bool accessed = false;
@@ -143,30 +143,17 @@ static bool damon_pa_young(unsigned long paddr, unsigned long *folio_sz)
 		folio_unlock(folio);
 
 out:
-	*folio_sz = folio_size(folio);
 	folio_put(folio);
 	return accessed;
 }
 
 static void __damon_pa_check_access(struct damon_region *r)
 {
-	static unsigned long last_addr;
-	static unsigned long last_folio_sz = PAGE_SIZE;
-	static bool last_accessed;
-
-	/* If the region is in the last checked page, reuse the result */
-	if (ALIGN_DOWN(last_addr, last_folio_sz) ==
-				ALIGN_DOWN(r->sampling_addr, last_folio_sz)) {
-		if (last_accessed)
-			r->nr_accesses++;
-		return;
-	}
+	bool accessed;
 
-	last_accessed = damon_pa_young(r->sampling_addr, &last_folio_sz);
-	if (last_accessed)
+	accessed = damon_pa_young(r->sampling_addr);
+	if (accessed)
 		r->nr_accesses++;
-
-	last_addr = r->sampling_addr;
 }
 
 static unsigned int damon_pa_check_accesses(struct damon_ctx *ctx)
Signature · 1 line
2.53.0
author maintainer
#535 lore raw

PATCH 535/1424 mm/damon/vaddr-kunit: check region count in three_regions test

In reply to Greg Kroah-Hartman

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

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

From: SJ Park <[email protected]>

[ Upstream commit 5fea07e460874c8c7cf00f728efbe22abc62c8d8 ]

damon_do_test_apply_three_regions() iterates regions after damon_set_regions() call assuming the function would succeed at setting the number of regions the same to the expected one. It might have failed. In this case, __nth_region_of() in the iteration could return NULL and NULL dereference can happen in the test.

The consequent user impact (NULL dereference) is quite bad. The realistic user impact would be limited, though. It would affect only test run setups.

Fix it by testing if the number of regions was also changed as expected and exit early for the failure.

The issue was discovered [1] by Sashiko.

---
 mm/damon/vaddr-test.h | 5 +++++
 1 file changed, 5 insertions(+)
mm/damon/vaddr-test.h +5 -0
index 292736d3f3954..c0546447b21e6 100644
@@ -149,12 +149,17 @@ static void damon_do_test_apply_three_regions(struct kunit *test,
 
 	damon_set_regions(t, three_regions, 3);
 
+	KUNIT_EXPECT_EQ(test, damon_nr_regions(t), nr_expected / 2);
+	if (damon_nr_regions(t) != nr_expected / 2)
+		goto out;
+
 	for (i = 0; i < nr_expected / 2; i++) {
 		r = __nth_region_of(t, i);
 		KUNIT_EXPECT_EQ(test, r->ar.start, expected[i * 2]);
 		KUNIT_EXPECT_EQ(test, r->ar.end, expected[i * 2 + 1]);
 	}
 
+out:
 	damon_destroy_target(t);
 }
 
Signature · 1 line
2.53.0
author maintainer
#536 lore raw

PATCH 536/1424 mm/damon/core-kunit: handle region split failure in filter_out()

In reply to Greg Kroah-Hartman

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

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

From: SJ Park <[email protected]>

[ Upstream commit 3423fe70395210e2f0cd795270292f6a27cd57b5 ]

damos_test_filter_out() test checks if damos_filter_match() of an address filter splits the region as expected under a given condition. But, the test continued regardless of the split successes. As a result, the later part of the test could dereference invalid pointers that returned from damon_next_region(). Further, it could corrupt memory from damon_destroy_region().

The consequent user impact (memory corruption) is quite bad. The realistic user impact would be limited, though. It would affect only test run setups.

Fix it by exiting early for the number of regions test failure.

The issue was discovered [1] by Sashiko.

Fixes 26713c890875 ("mm/damon/core-test: add a unit test for __damos_filter_out()")
Cc Brendan Higgins <[email protected]>
Cc <[email protected]> # 6.6.x
---
 mm/damon/core-test.h | 5 +++++
 1 file changed, 5 insertions(+)
mm/damon/core-test.h +5 -0
index f2f0978495086..53212bcc9eda7 100644
@@ -502,6 +502,8 @@ static void damos_test_filter_out(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, r->ar.start, DAMON_MIN_REGION * 1);
 	KUNIT_EXPECT_EQ(test, r->ar.end, DAMON_MIN_REGION * 2);
 	KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 2);
+	if (damon_nr_regions(t) != 2)
+		goto out;
 	r2 = damon_next_region(r);
 	KUNIT_EXPECT_EQ(test, r2->ar.start, DAMON_MIN_REGION * 2);
 	KUNIT_EXPECT_EQ(test, r2->ar.end, DAMON_MIN_REGION * 4);
@@ -515,11 +517,14 @@ static void damos_test_filter_out(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, r->ar.start, DAMON_MIN_REGION * 2);
 	KUNIT_EXPECT_EQ(test, r->ar.end, DAMON_MIN_REGION * 6);
 	KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 2);
+	if (damon_nr_regions(t) != 2)
+		goto out;
 	r2 = damon_next_region(r);
 	KUNIT_EXPECT_EQ(test, r2->ar.start, DAMON_MIN_REGION * 6);
 	KUNIT_EXPECT_EQ(test, r2->ar.end, DAMON_MIN_REGION * 8);
 	damon_destroy_region(r2, t);
 
+out:
 	damon_free_target(t);
 	damos_free_filter(f);
 }
Signature · 1 line
2.53.0
author maintainer
#537 lore raw

PATCH 537/1424 mm/damon/tests/core-kunit: catch test failure in test_merge_regions_of()

In reply to Greg Kroah-Hartman

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

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

From: SJ Park <[email protected]>

[ Upstream commit b640708929aa956235866bf7717d89018c661083 ]

KUNIT_EXPECT_EQ() does not abort the execution of test code when the expectation is not met. But damon_test_merge_regions_of() code after its initial KUNIT_EXPECT_EQ() call assumes the expectation is met. It does a per-region test with a hard-coded number of regions that is correct only if the expectation was met. As a result, __nth_region_of() could return NULL, and the test code can dereference NULL pointers. Fix the issue by catching the expectation failure and skip the per-region tests.

The user impact on realistic setups should be negligible, as it is a unit test.

The issue was discovered [1] by Sashiko.

---
 mm/damon/core-test.h | 3 +++
 1 file changed, 3 insertions(+)
mm/damon/core-test.h +3 -0
index 53212bcc9eda7..e6c19a23b0806 100644
@@ -258,11 +258,14 @@ static void damon_test_merge_regions_of(struct kunit *test)
 	damon_merge_regions_of(t, 9, 9999);
 	/* 0-112, 114-130, 130-156, 156-170 */
 	KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 5u);
+	if (damon_nr_regions(t) != 5)
+		goto out;
 	for (i = 0; i < 5; i++) {
 		r = __nth_region_of(t, i);
 		KUNIT_EXPECT_EQ(test, r->ar.start, saddrs[i]);
 		KUNIT_EXPECT_EQ(test, r->ar.end, eaddrs[i]);
 	}
+out:
 	damon_free_target(t);
 }
 
Signature · 1 line
2.53.0
author maintainer
#538 lore raw

PATCH 538/1424 net: hns3: dont auto enable misc vector

In reply to Greg Kroah-Hartman

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

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

From: Jian Shen <[email protected]>

[ Upstream commit 98b1e3b27734139c76295754b6c317aa4df6d32e ]

Currently, there is a time window between misc irq enabled and service task inited. If an interrupte is reported at this time, it will cause warning like below:

[   16.324639] Call trace:
[   16.324641]  __queue_delayed_work+0xb8/0xe0
[   16.324643]  mod_delayed_work_on+0x78/0xd0
[   16.324655]  hclge_errhand_task_schedule+0x58/0x90 [hclge]
[   16.324662]  hclge_misc_irq_handle+0x168/0x240 [hclge]
[   16.324666]  __handle_irq_event_percpu+0x64/0x1e0
[   16.324667]  handle_irq_event+0x80/0x170
[   16.324670]  handle_fasteoi_edge_irq+0x110/0x2bc
[   16.324671]  __handle_domain_irq+0x84/0xfc
[   16.324673]  gic_handle_irq+0x88/0x2c0
[   16.324674]  el1_irq+0xb8/0x140
[   16.324677]  arch_cpu_idle+0x18/0x40
[   16.324679]  default_idle_call+0x5c/0x1bc
[   16.324682]  cpuidle_idle_call+0x18c/0x1c4
[   16.324684]  do_idle+0x174/0x17c
[   16.324685]  cpu_startup_entry+0x30/0x6c
[   16.324687]  secondary_start_kernel+0x1a4/0x280
[   16.324688] ---[ end trace 6aa0bff672a964aa ]---

So don't auto enable misc vector when request irq..

Fixes 7be1b9f3e99f ("net: hns3: make hclge_service use delayed workqueue")
---
 .../net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c  | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +7 -5
index 04c58928585ca..5c66eb21bb7ea 100644
@@ -6,6 +6,7 @@
 #include <linux/etherdevice.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
@@ -3713,7 +3714,7 @@ static int hclge_misc_irq_init(struct hclge_dev *hdev)
 	snprintf(hdev->misc_vector.name, HNAE3_INT_NAME_LEN, "%s-misc-%s",
 		 HCLGE_NAME, pci_name(hdev->pdev));
 	ret = request_irq(hdev->misc_vector.vector_irq, hclge_misc_irq_handle,
-			  0, hdev->misc_vector.name, hdev);
+			  IRQF_NO_AUTOEN, hdev->misc_vector.name, hdev);
 	if (ret) {
 		hclge_free_vector(hdev, 0);
 		dev_err(&hdev->pdev->dev, "request misc irq(%d) fail\n",
@@ -11815,9 +11816,6 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
 
 	hclge_init_rxd_adv_layout(hdev);
 
-	/* Enable MISC vector(vector0) */
-	hclge_enable_vector(&hdev->misc_vector, true);
-
 	ret = hclge_init_wol(hdev);
 	if (ret)
 		dev_warn(&pdev->dev,
@@ -11830,6 +11828,10 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
 	hclge_state_init(hdev);
 	hdev->last_reset_time = jiffies;
 
+	/* Enable MISC vector(vector0) */
+	enable_irq(hdev->misc_vector.vector_irq);
+	hclge_enable_vector(&hdev->misc_vector, true);
+
 	dev_info(&hdev->pdev->dev, "%s driver initialization finished.\n",
 		 HCLGE_DRIVER_NAME);
 
@@ -12233,7 +12235,7 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
 
 	/* Disable MISC vector(vector0) */
 	hclge_enable_vector(&hdev->misc_vector, false);
-	synchronize_irq(hdev->misc_vector.vector_irq);
+	disable_irq(hdev->misc_vector.vector_irq);
 
 	/* Disable all hw interrupts */
 	hclge_config_mac_tnl_int(hdev, false);
Signature · 1 line
2.53.0
author maintainer
#539 lore raw

PATCH 539/1424 net: hns3: fix kernel crash when 1588 is sent on HIP08 devices

In reply to Greg Kroah-Hartman

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

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

From: Jie Wang <[email protected]>

[ Upstream commit 9741e72b2286de8b38de9db685588ac421a95c87 ]

Currently, HIP08 devices does not register the ptp devices, so the hdev->ptp is NULL. But the tx process would still try to set hardware time stamp info with SKBTX_HW_TSTAMP flag and cause a kernel crash.

[  128.087798] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000018
...
[  128.280251] pc : hclge_ptp_set_tx_info+0x2c/0x140 [hclge]
[  128.286600] lr : hclge_ptp_set_tx_info+0x20/0x140 [hclge]
[  128.292938] sp : ffff800059b93140
[  128.297200] x29: ffff800059b93140 x28: 0000000000003280
[  128.303455] x27: ffff800020d48280 x26: ffff0cb9dc814080
[  128.309715] x25: ffff0cb9cde93fa0 x24: 0000000000000001
[  128.315969] x23: 0000000000000000 x22: 0000000000000194
[  128.322219] x21: ffff0cd94f986000 x20: 0000000000000000
[  128.328462] x19: ffff0cb9d2a166c0 x18: 0000000000000000
[  128.334698] x17: 0000000000000000 x16: ffffcf1fc523ed24
[  128.340934] x15: 0000ffffd530a518 x14: 0000000000000000
[  128.347162] x13: ffff0cd6bdb31310 x12: 0000000000000368
[  128.353388] x11: ffff0cb9cfbc7070 x10: ffff2cf55dd11e02
[  128.359606] x9 : ffffcf1f85a212b4 x8 : ffff0cd7cf27dab0
[  128.365831] x7 : 0000000000000a20 x6 : ffff0cd7cf27d000
[  128.372040] x5 : 0000000000000000 x4 : 000000000000ffff
[  128.378243] x3 : 0000000000000400 x2 : ffffcf1f85a21294
[  128.384437] x1 : ffff0cb9db520080 x0 : ffff0cb9db500080
[  128.390626] Call trace:
[  128.393964]  hclge_ptp_set_tx_info+0x2c/0x140 [hclge]
[  128.399893]  hns3_nic_net_xmit+0x39c/0x4c4 [hns3]
[  128.405468]  xmit_one.constprop.0+0xc4/0x200
[  128.410600]  dev_hard_start_xmit+0x54/0xf0
[  128.415556]  sch_direct_xmit+0xe8/0x634
[  128.420246]  __dev_queue_xmit+0x224/0xc70
[  128.425101]  dev_queue_xmit+0x1c/0x40
[  128.429608]  ovs_vport_send+0xac/0x1a0 [openvswitch]
[  128.435409]  do_output+0x60/0x17c [openvswitch]
[  128.440770]  do_execute_actions+0x898/0x8c4 [openvswitch]
[  128.446993]  ovs_execute_actions+0x64/0xf0 [openvswitch]
[  128.453129]  ovs_dp_process_packet+0xa0/0x224 [openvswitch]
[  128.459530]  ovs_vport_receive+0x7c/0xfc [openvswitch]
[  128.465497]  internal_dev_xmit+0x34/0xb0 [openvswitch]
[  128.471460]  xmit_one.constprop.0+0xc4/0x200
[  128.476561]  dev_hard_start_xmit+0x54/0xf0
[  128.481489]  __dev_queue_xmit+0x968/0xc70
[  128.486330]  dev_queue_xmit+0x1c/0x40
[  128.490856]  ip_finish_output2+0x250/0x570
[  128.495810]  __ip_finish_output+0x170/0x1e0
[  128.500832]  ip_finish_output+0x3c/0xf0
[  128.505504]  ip_output+0xbc/0x160
[  128.509654]  ip_send_skb+0x58/0xd4
[  128.513892]  udp_send_skb+0x12c/0x354
[  128.518387]  udp_sendmsg+0x7a8/0x9c0
[  128.522793]  inet_sendmsg+0x4c/0x8c
[  128.527116]  __sock_sendmsg+0x48/0x80
[  128.531609]  __sys_sendto+0x124/0x164
[  128.536099]  __arm64_sys_sendto+0x30/0x5c
[  128.540935]  invoke_syscall+0x50/0x130
[  128.545508]  el0_svc_common.constprop.0+0x10c/0x124
[  128.551205]  do_el0_svc+0x34/0xdc
[  128.555347]  el0_svc+0x20/0x30
[  128.559227]  el0_sync_handler+0xb8/0xc0
[  128.563883]  el0_sync+0x160/0x180
---
 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c +3 -0
index c1e88e67ebb65..5e94410d8dc2e 100644
@@ -58,6 +58,9 @@ bool hclge_ptp_set_tx_info(struct hnae3_handle *handle, struct sk_buff *skb)
 	struct hclge_dev *hdev = vport->back;
 	struct hclge_ptp *ptp = hdev->ptp;
 
+	if (!ptp)
+		return false;
+
 	if (!test_bit(HCLGE_PTP_FLAG_TX_EN, &ptp->flags) ||
 	    test_and_set_bit(HCLGE_STATE_PTP_TX_HANDLING, &hdev->state)) {
 		ptp->tx_skipped++;
Signature · 1 line
2.53.0
author maintainer
#540 lore raw

PATCH 540/1424 wifi: ath11k: Clear affinity hint before calling ath11k_pcic_free_irq() in error path

In reply to Greg Kroah-Hartman

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

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

From: Manivannan Sadhasivam <[email protected]>

[ Upstream commit 68410c5bd381a81bcc92b808e7dc4e6b9ed25d11 ]

If a shared IRQ is used by the driver due to platform limitation, then the IRQ affinity hint is set right after the allocation of IRQ vectors in ath11k_pci_alloc_msi(). This does no harm unless one of the functions requesting the IRQ fails and attempt to free the IRQ. This results in the below warning:

WARNING: CPU: 7 PID: 349 at kernel/irq/manage.c:1929 free_irq+0x278/0x29c
Call trace:
 free_irq+0x278/0x29c
 ath11k_pcic_free_irq+0x70/0x10c [ath11k]
 ath11k_pci_probe+0x800/0x820 [ath11k_pci]
 local_pci_probe+0x40/0xbc

The warning is due to not clearing the affinity hint before freeing the IRQs.

So to fix this issue, clear the IRQ affinity hint before calling ath11k_pcic_free_irq() in the error path. The affinity will be cleared once again further down the error path due to code organization, but that does no harm.

Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-05266-QCAHSTSWPLZ_V2_TO_X86-1

Cc Baochen Qiang <[email protected]>
Fixes 39564b475ac5 ("wifi: ath11k: fix boot failure with one MSI vector")
---
 drivers/net/wireless/ath/ath11k/pci.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/net/wireless/ath/ath11k/pci.c +2 -0
index 966ba712ff909..02c261a30a4db 100644
@@ -908,6 +908,8 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
 	return 0;
 
 err_free_irq:
+	/* __free_irq() expects the caller to have cleared the affinity hint */
+	ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
 	ath11k_pcic_free_irq(ab);
 
 err_ce_free:
Signature · 1 line
2.53.0
author maintainer
#541 lore raw

PATCH 541/1424 md/md-bitmap: fix wrong bitmap_limit for clustermd when write sb

In reply to Greg Kroah-Hartman

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

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

From: Su Yue <[email protected]>

[ Upstream commit 6130825f34d41718c98a9b1504a79a23e379701e ]

In clustermd, separate write-intent-bitmaps are used for each cluster node:

0 4k 8k 12k
-------------------------------------------------------------------

| idle                | md super            | bm super [0] + bits |
| bm bits[0, contd]   | bm super[1] + bits  | bm bits[1, contd]   |
| bm super[2] + bits  | bm bits [2, contd]  | bm super[3] + bits  |
| bm bits [3, contd]  |                     |                     |

So in node 1, pg_index in __write_sb_page() could equal to bitmap->storage.file_pages. Then bitmap_limit will be calculated to
0. md_super_write() will be called with 0 size.
That means the first 4k sb area of node 1 will never be updated through filemap_write_page().
This bug causes hang of mdadm/clustermd_tests/01r1_Grow_resize.

Here use (pg_index % bitmap->storage.file_pages) to make calculation of bitmap_limit correct.

Fixes ab99a87542f1 ("md/md-bitmap: fix writing non bitmap pages")
---
 drivers/md/md-bitmap.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/md/md-bitmap.c +3 -3
index ace28eb81111c..03be53313e3c1 100644
@@ -227,8 +227,8 @@ static int __write_sb_page(struct md_rdev *rdev, struct bitmap *bitmap,
 	struct block_device *bdev;
 	struct mddev *mddev = bitmap->mddev;
 	struct bitmap_storage *store = &bitmap->storage;
-	unsigned int bitmap_limit = (bitmap->storage.file_pages - pg_index) <<
-		PAGE_SHIFT;
+	unsigned long num_pages = bitmap->storage.file_pages;
+	unsigned int bitmap_limit = (num_pages - pg_index % num_pages) << PAGE_SHIFT;
 	loff_t sboff, offset = mddev->bitmap_info.offset;
 	sector_t ps = pg_index * PAGE_SIZE / SECTOR_SIZE;
 	unsigned int size = PAGE_SIZE;
@@ -237,7 +237,7 @@ static int __write_sb_page(struct md_rdev *rdev, struct bitmap *bitmap,
 
 	bdev = (rdev->meta_bdev) ? rdev->meta_bdev : rdev->bdev;
 	/* we compare length (page numbers), not page offset. */
-	if ((pg_index - store->sb_index) == store->file_pages - 1) {
+	if ((pg_index - store->sb_index) == num_pages - 1) {
 		unsigned int last_page_size = store->bytes & (PAGE_SIZE - 1);
 
 		if (last_page_size == 0)
Signature · 1 line
2.53.0
author maintainer
#542 lore raw

PATCH 542/1424 net: libwx: fix Tx L4 checksum

In reply to Greg Kroah-Hartman

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

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

From: Jiawen Wu <[email protected]>

[ Upstream commit c7d82913d5f9e97860772ee4051eaa66b56a6273 ]

The hardware only supports L4 checksum offload for TCP/UDP/SCTP protocol. There was a bug to set Tx checksum flag for the other protocol that results in Tx ring hang. Fix to compute software checksum for these packets.

Fixes 3403960cdf86 ("net: wangxun: libwx add tx offload functions")
---
 drivers/net/ethernet/wangxun/libwx/wx_lib.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/ethernet/wangxun/libwx/wx_lib.c +3 -1
index 23dbe4e4b36c0..c27e68e320e62 100644
@@ -1327,6 +1327,7 @@ static void wx_tx_csum(struct wx_ring *tx_ring, struct wx_tx_buffer *first,
 	u8 tun_prot = 0;
 
 	if (skb->ip_summed != CHECKSUM_PARTIAL) {
+csum_failed:
 		if (!(first->tx_flags & WX_TX_FLAGS_HW_VLAN) &&
 		    !(first->tx_flags & WX_TX_FLAGS_CC))
 			return;
@@ -1420,7 +1421,8 @@ static void wx_tx_csum(struct wx_ring *tx_ring, struct wx_tx_buffer *first,
 					WX_TXD_L4LEN_SHIFT;
 			break;
 		default:
-			break;
+			skb_checksum_help(skb);
+			goto csum_failed;
 		}
 
 		/* update TX checksum flag */
Signature · 1 line
2.53.0
author maintainer
#543 lore raw

PATCH 543/1424 ksmbd: fix overflow in dacloffset bounds check

In reply to Greg Kroah-Hartman

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

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

From: Norbert Szetei <[email protected]>

[ Upstream commit beff0bc9d69bc8e733f9bca28e2d3df5b3e10e42 ]

The dacloffset field was originally typed as int and used in an unchecked addition, which could overflow and bypass the existing bounds check in both smb_check_perm_dacl() and smb_inherit_dacl().

This could result in out-of-bounds memory access and a kernel crash when dereferencing the DACL pointer.

This patch converts dacloffset to unsigned int and uses check_add_overflow() to validate access to the DACL.

---
 fs/smb/server/smbacl.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)
fs/smb/server/smbacl.c +12 -4
index 06936ae521cf0..a70b6f535d86f 100644
@@ -1135,7 +1135,9 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
 	struct dentry *parent = path->dentry->d_parent;
 	struct mnt_idmap *idmap = mnt_idmap(path->mnt);
 	int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size;
-	int rc = 0, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size;
+	int rc = 0, pntsd_type, pntsd_size, acl_len, aces_size;
+	unsigned int dacloffset;
+	size_t dacl_struct_end;
 	u16 num_aces, ace_cnt = 0;
 	char *aces_base;
 	bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);
@@ -1144,8 +1146,11 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
 					    parent, &parent_pntsd);
 	if (pntsd_size <= 0)
 		return -ENOENT;
+
 	dacloffset = le32_to_cpu(parent_pntsd->dacloffset);
-	if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) {
+	if (!dacloffset ||
+	    check_add_overflow(dacloffset, sizeof(struct smb_acl), &dacl_struct_end) ||
+	    dacl_struct_end > (size_t)pntsd_size) {
 		rc = -EINVAL;
 		goto free_parent_pntsd;
 	}
@@ -1367,7 +1372,9 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
 	struct smb_ntsd *pntsd = NULL;
 	struct smb_acl *pdacl;
 	struct posix_acl *posix_acls;
-	int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset;
+	int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size;
+	unsigned int dacl_offset;
+	size_t dacl_struct_end;
 	struct smb_sid sid;
 	int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE);
 	struct smb_ace *ace;
@@ -1386,7 +1393,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
 
 	dacl_offset = le32_to_cpu(pntsd->dacloffset);
 	if (!dacl_offset ||
-	    (dacl_offset + sizeof(struct smb_acl) > pntsd_size))
+	    check_add_overflow(dacl_offset, sizeof(struct smb_acl), &dacl_struct_end) ||
+	    dacl_struct_end > (size_t)pntsd_size)
 		goto err_out;
 
 	pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));
Signature · 1 line
2.53.0
author maintainer
#544 lore raw

PATCH 544/1424 ksmbd: fix WARNING "do not call blocking ops when !TASK_RUNNING"

In reply to Greg Kroah-Hartman

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

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

From: Namjae Jeon <[email protected]>

[ Upstream commit 1df0d4c616138784e033ad337961b6e1a6bcd999 ]

wait_event_timeout() will set the state of the current
task to TASK_UNINTERRUPTIBLE, before doing the condition check. This means that ksmbd_durable_scavenger_alive() will try to acquire the mutex while already in a sleeping state. The scheduler warns us by giving the following warning:

do not call blocking ops when !TASK_RUNNING; state=2 set at

 [<0000000061515a6f>] prepare_to_wait_event+0x9f/0x6c0
WARNING: CPU: 2 PID: 4147 at kernel/sched/core.c:10099 __might_sleep+0x12f/0x160

mutex lock is not needed in ksmbd_durable_scavenger_alive().

---
 fs/smb/server/vfs_cache.c | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)
fs/smb/server/vfs_cache.c +1 -7
index 12db854e4c519..2864ea08d75a6 100644
@@ -971,12 +971,8 @@ static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon,
 
 static bool ksmbd_durable_scavenger_alive(void)
 {
-	mutex_lock(&durable_scavenger_lock);
-	if (!durable_scavenger_running) {
-		mutex_unlock(&durable_scavenger_lock);
+	if (!durable_scavenger_running)
 		return false;
-	}
-	mutex_unlock(&durable_scavenger_lock);
 
 	if (kthread_should_stop())
 		return false;
@@ -1085,9 +1081,7 @@ static int ksmbd_durable_scavenger(void *dummy)
 			break;
 	}
 
-	mutex_lock(&durable_scavenger_lock);
 	durable_scavenger_running = false;
-	mutex_unlock(&durable_scavenger_lock);
 
 	module_put(THIS_MODULE);
 
Signature · 1 line
2.53.0
author maintainer
#545 lore raw

PATCH 545/1424 net/niu: Niu requires MSIX ENTRY_DATA fields touch before entry reads

In reply to Greg Kroah-Hartman

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

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

From: Jonathan Currier <[email protected]>

[ Upstream commit fbb429ddff5c8e479edcc7dde5a542c9295944e6 ]

Fix niu_try_msix() to not cause a fatal trap on sparc systems.

Set PCI_DEV_FLAGS_MSIX_TOUCH_ENTRY_DATA_FIRST on the struct pci_dev to work around a bug in the hardware or firmware.

For each vector entry in the msix table, niu chips will cause a fatal trap if any registers in that entry are read before that entries' ENTRY_DATA register is written to. Testing indicates writes to other registers are not sufficient to prevent the fatal trap, however the value does not appear to matter. This only needs to happen once after power up, so simply rebooting into a kernel lacking this fix will NOT cause the trap.

NON-RESUMABLE ERROR: Reporting on cpu 64
NON-RESUMABLE ERROR: TPC [0x00000000005f6900] <msix_prepare_msi_desc+0x90/0xa0> NON-RESUMABLE ERROR: RAW [4010000000000016:00000e37f93e32ff:0000000202000080:ffffffffffffffff
NON-RESUMABLE ERROR: 0000000800000000:0000000000000000:0000000000000000:0000000000000000]
NON-RESUMABLE ERROR: handle [0x4010000000000016] stick [0x00000e37f93e32ff] NON-RESUMABLE ERROR: type [precise nonresumable]
NON-RESUMABLE ERROR: attrs [0x02000080] < ASI sp-faulted priv >
NON-RESUMABLE ERROR: raddr [0xffffffffffffffff]
NON-RESUMABLE ERROR: insn effective address [0x000000c50020000c]
NON-RESUMABLE ERROR: size [0x8]
NON-RESUMABLE ERROR: asi [0x00]

CPU: 64 UID: 0 PID: 745 Comm: kworker/64:1 Not tainted 6.11.5 #63
Workqueue: events work_for_cpu_fn
TSTATE: 0000000011001602 TPC: 00000000005f6900 TNPC: 00000000005f6904 Y: 00000000    Not tainted
TPC: <msix_prepare_msi_desc+0x90/0xa0>
g0: 00000000000002e9 g1: 000000000000000c g2: 000000c50020000c g3: 0000000000000100
g4: ffff8000470307c0 g5: ffff800fec5be000 g6: ffff800047a08000 g7: 0000000000000000
o0: ffff800014feb000 o1: ffff800047a0b620 o2: 0000000000000011 o3: ffff800047a0b620
o4: 0000000000000080 o5: 0000000000000011 sp: ffff800047a0ad51 ret_pc: 00000000005f7128
RPC: <__pci_enable_msix_range+0x3cc/0x460>
l0: 000000000000000d l1: 000000000000c01f l2: ffff800014feb0a8 l3: 0000000000000020
l4: 000000000000c000 l5: 0000000000000001 l6: 0000000020000000 l7: ffff800047a0b734
i0: ffff800014feb000 i1: ffff800047a0b730 i2: 0000000000000001 i3: 000000000000000d
i4: 0000000000000000 i5: 0000000000000000 i6: ffff800047a0ae81 i7: 00000000101888b0
I7: <niu_try_msix.constprop.0+0xc0/0x130 [niu]>
Call Trace:
[<00000000101888b0>] niu_try_msix.constprop.0+0xc0/0x130 [niu]
[<000000001018f840>] niu_get_invariants+0x183c/0x207c [niu]
[<00000000101902fc>] niu_pci_init_one+0x27c/0x2fc [niu]
[<00000000005ef3e4>] local_pci_probe+0x28/0x74
[<0000000000469240>] work_for_cpu_fn+0x8/0x1c
[<000000000046b008>] process_scheduled_works+0x144/0x210
[<000000000046b518>] worker_thread+0x13c/0x1c0
[<00000000004710e0>] kthread+0xb8/0xc8
[<00000000004060c8>] ret_from_fork+0x1c/0x2c
[<0000000000000000>] 0x0
Kernel panic - not syncing: Non-resumable error.
---
 drivers/net/ethernet/sun/niu.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/net/ethernet/sun/niu.c +2 -0
index fc6217917fc22..0645ef20f6645 100644
@@ -9075,6 +9075,8 @@ static void niu_try_msix(struct niu *np, u8 *ldg_num_map)
 		msi_vec[i].entry = i;
 	}
 
+	pdev->dev_flags |= PCI_DEV_FLAGS_MSIX_TOUCH_ENTRY_DATA_FIRST;
+
 	num_irqs = pci_enable_msix_range(pdev, msi_vec, 1, num_irqs);
 	if (num_irqs < 0) {
 		np->flags &= ~NIU_FLAGS_MSIX;
Signature · 1 line
2.53.0
author maintainer
#546 lore raw

PATCH 546/1424 parse_longname(): strrchr() expects NUL-terminated string

In reply to Greg Kroah-Hartman

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

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

From: Al Viro <[email protected]>

[ Upstream commit 101841c38346f4ca41dc1802c867da990ffb32eb ]

... and parse_longname() is not guaranteed that. That's the reason why it uses kmemdup_nul() to build the argument for kstrtou64(); the problem is, kstrtou64() is not the only thing that need it.

Just get a NUL-terminated copy of the entire thing and be done with that...

Fixes dd66df0053ef "ceph: add support for encrypted snapshot names"
---
 fs/ceph/crypto.c | 31 ++++++++++++-------------------
 1 file changed, 12 insertions(+), 19 deletions(-)
fs/ceph/crypto.c +12 -19
index d692ebfddedbc..f98f120d0fd7d 100644
@@ -214,35 +214,31 @@ static struct inode *parse_longname(const struct inode *parent,
 	struct ceph_client *cl = ceph_inode_to_client(parent);
 	struct inode *dir = NULL;
 	struct ceph_vino vino = { .snap = CEPH_NOSNAP };
-	char *inode_number;
-	char *name_end;
-	int orig_len = *name_len;
+	char *name_end, *inode_number;
 	int ret = -EIO;
-
+	/* NUL-terminate */
+	char *str __free(kfree) = kmemdup_nul(name, *name_len, GFP_KERNEL);
+	if (!str)
+		return ERR_PTR(-ENOMEM);
 	/* Skip initial '_' */
-	name++;
-	name_end = strrchr(name, '_');
+	str++;
+	name_end = strrchr(str, '_');
 	if (!name_end) {
-		doutc(cl, "failed to parse long snapshot name: %s\n", name);
+		doutc(cl, "failed to parse long snapshot name: %s\n", str);
 		return ERR_PTR(-EIO);
 	}
-	*name_len = (name_end - name);
+	*name_len = (name_end - str);
 	if (*name_len <= 0) {
 		pr_err_client(cl, "failed to parse long snapshot name\n");
 		return ERR_PTR(-EIO);
 	}
 
 	/* Get the inode number */
-	inode_number = kmemdup_nul(name_end + 1,
-				   orig_len - *name_len - 2,
-				   GFP_KERNEL);
-	if (!inode_number)
-		return ERR_PTR(-ENOMEM);
+	inode_number = name_end + 1;
 	ret = kstrtou64(inode_number, 10, &vino.ino);
 	if (ret) {
-		doutc(cl, "failed to parse inode number: %s\n", name);
-		dir = ERR_PTR(ret);
-		goto out;
+		doutc(cl, "failed to parse inode number: %s\n", str);
+		return ERR_PTR(ret);
 	}
 
 	/* And finally the inode */
@@ -253,9 +249,6 @@ static struct inode *parse_longname(const struct inode *parent,
 		if (IS_ERR(dir))
 			doutc(cl, "can't find inode %s (%s)\n", inode_number, name);
 	}
-
-out:
-	kfree(inode_number);
 	return dir;
 }
 
Signature · 1 line
2.53.0
author maintainer
#547 lore raw

PATCH 547/1424 ceph: fix oops due to invalid pointer for kfree() in parse_longname()

In reply to Greg Kroah-Hartman

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

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

From: Daniel Vogelbacher <[email protected]>

[ Upstream commit bc8dedae022ce3058659c3addef3ec4b41d15e00 ]

This fixes a kernel oops when reading ceph snapshot directories (.snap), for example by simply running ls /mnt/my_ceph/.snap.

The variable str is guarded by __free(kfree), but advanced by one for skipping the initial '_' in snapshot names. Thus, kfree() is called with an invalid pointer. This patch removes the need for advancing the pointer so kfree() is called with correct memory pointer.

Steps to reproduce:

1. Create snapshots on a cephfs volume (I've 63 snaps in my testcase)

2. Add cephfs mount to fstab

$ echo "[email protected]=/volumes/datapool/stuff/3461082b-ecc9-4e82-8549-3fd2590d3fb6      /mnt/test/stuff   ceph     acl,noatime,_netdev    0       0" >> /etc/fstab

3. Reboot the system

$ systemctl reboot

4. Check if it's really mounted

$ mount | grep stuff

5. List snapshots (expected 63 snapshots on my system)

$ ls /mnt/test/stuff/.snap

Now ls hangs forever and the kernel log shows the oops.

Fixes 101841c38346 ("[ceph] parse_longname(): strrchr() expects NUL-terminated string")
---
 fs/ceph/crypto.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
fs/ceph/crypto.c +5 -4
index f98f120d0fd7d..42b066729d97c 100644
@@ -216,12 +216,13 @@ static struct inode *parse_longname(const struct inode *parent,
 	struct ceph_vino vino = { .snap = CEPH_NOSNAP };
 	char *name_end, *inode_number;
 	int ret = -EIO;
-	/* NUL-terminate */
-	char *str __free(kfree) = kmemdup_nul(name, *name_len, GFP_KERNEL);
+	/* Snapshot name must start with an underscore */
+	if (*name_len <= 0 || name[0] != '_')
+		return ERR_PTR(-EIO);
+	/* Skip initial '_' and NUL-terminate */
+	char *str __free(kfree) = kmemdup_nul(name + 1, *name_len - 1, GFP_KERNEL);
 	if (!str)
 		return ERR_PTR(-ENOMEM);
-	/* Skip initial '_' */
-	str++;
 	name_end = strrchr(str, '_');
 	if (!name_end) {
 		doutc(cl, "failed to parse long snapshot name: %s\n", str);
Signature · 1 line
2.53.0
author maintainer
#548 lore raw

PATCH 548/1424 bpf: Reject narrower access to pointer ctx fields

In reply to Greg Kroah-Hartman

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

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

From: Paul Chaignon <[email protected]>

[ Upstream commit e09299225d5ba3916c91ef70565f7d2187e4cca0 ]

The following BPF program, simplified from a syzkaller repro, causes a kernel warning:

    r0 = *(u8 *)(r1 + 169);
    exit;

With pointer field sk being at offset 168 in __sk_buff. This access is detected as a narrower read in bpf_skb_is_valid_access because it doesn't match offsetof(struct __sk_buff, sk). It is therefore allowed and later proceeds to bpf_convert_ctx_access. Note that for the "is_narrower_load" case in the convert_ctx_accesses(), the insn->off is aligned, so the cnt may not be 0 because it matches the offsetof(struct __sk_buff, sk) in the bpf_convert_ctx_access. However, the target_size stays 0 and the verifier errors with a kernel warning:

verifier bug: error during ctx access conversion(1)

This patch fixes that to return a proper "invalid bpf_context access off=X size=Y" error on the load instruction.

The same issue affects multiple other fields in context structures that allow narrow access. Some other non-affected fields (for sk_msg, sk_lookup, and sockopt) were also changed to use bpf_ctx_range_ptr for consistency.

Note this syzkaller crash was reported in the "Closes" link below, which used to be about a different bug, fixed in
commit fce7bd8e385a ("bpf/verifier: Handle BPF_LOAD_ACQ instructions in insn_def_regno()"). Because syzbot somehow confused the two bugs, the new crash and repro didn't get reported to the mailing list.

---
 kernel/bpf/cgroup.c |  8 ++++----
 net/core/filter.c   | 20 ++++++++++----------
 2 files changed, 14 insertions(+), 14 deletions(-)
kernel/bpf/cgroup.c +4 -4
index b75b63e675066..edf38cbe9228d 100644
@@ -2436,22 +2436,22 @@ static bool cg_sockopt_is_valid_access(int off, int size,
 	}
 
 	switch (off) {
-	case offsetof(struct bpf_sockopt, sk):
+	case bpf_ctx_range_ptr(struct bpf_sockopt, sk):
 		if (size != sizeof(__u64))
 			return false;
 		info->reg_type = PTR_TO_SOCKET;
 		break;
-	case offsetof(struct bpf_sockopt, optval):
+	case bpf_ctx_range_ptr(struct bpf_sockopt, optval):
 		if (size != sizeof(__u64))
 			return false;
 		info->reg_type = PTR_TO_PACKET;
 		break;
-	case offsetof(struct bpf_sockopt, optval_end):
+	case bpf_ctx_range_ptr(struct bpf_sockopt, optval_end):
 		if (size != sizeof(__u64))
 			return false;
 		info->reg_type = PTR_TO_PACKET_END;
 		break;
-	case offsetof(struct bpf_sockopt, retval):
+	case bpf_ctx_range(struct bpf_sockopt, retval):
 		if (size != size_default)
 			return false;
 		return prog->expected_attach_type == BPF_CGROUP_GETSOCKOPT;
net/core/filter.c +10 -10
index 23e9deba098c9..a9fcae823ca90 100644
@@ -8651,7 +8651,7 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type
 		if (size != sizeof(__u64))
 			return false;
 		break;
-	case offsetof(struct __sk_buff, sk):
+	case bpf_ctx_range_ptr(struct __sk_buff, sk):
 		if (type == BPF_WRITE || size != sizeof(__u64))
 			return false;
 		info->reg_type = PTR_TO_SOCK_COMMON_OR_NULL;
@@ -9220,7 +9220,7 @@ static bool sock_addr_is_valid_access(int off, int size,
 				return false;
 		}
 		break;
-	case offsetof(struct bpf_sock_addr, sk):
+	case bpf_ctx_range_ptr(struct bpf_sock_addr, sk):
 		if (type != BPF_READ)
 			return false;
 		if (size != sizeof(__u64))
@@ -9274,17 +9274,17 @@ static bool sock_ops_is_valid_access(int off, int size,
 			if (size != sizeof(__u64))
 				return false;
 			break;
-		case offsetof(struct bpf_sock_ops, sk):
+		case bpf_ctx_range_ptr(struct bpf_sock_ops, sk):
 			if (size != sizeof(__u64))
 				return false;
 			info->reg_type = PTR_TO_SOCKET_OR_NULL;
 			break;
-		case offsetof(struct bpf_sock_ops, skb_data):
+		case bpf_ctx_range_ptr(struct bpf_sock_ops, skb_data):
 			if (size != sizeof(__u64))
 				return false;
 			info->reg_type = PTR_TO_PACKET;
 			break;
-		case offsetof(struct bpf_sock_ops, skb_data_end):
+		case bpf_ctx_range_ptr(struct bpf_sock_ops, skb_data_end):
 			if (size != sizeof(__u64))
 				return false;
 			info->reg_type = PTR_TO_PACKET_END;
@@ -9293,7 +9293,7 @@ static bool sock_ops_is_valid_access(int off, int size,
 			bpf_ctx_record_field_size(info, size_default);
 			return bpf_ctx_narrow_access_ok(off, size,
 							size_default);
-		case offsetof(struct bpf_sock_ops, skb_hwtstamp):
+		case bpf_ctx_range(struct bpf_sock_ops, skb_hwtstamp):
 			if (size != sizeof(__u64))
 				return false;
 			break;
@@ -9363,17 +9363,17 @@ static bool sk_msg_is_valid_access(int off, int size,
 		return false;
 
 	switch (off) {
-	case offsetof(struct sk_msg_md, data):
+	case bpf_ctx_range_ptr(struct sk_msg_md, data):
 		info->reg_type = PTR_TO_PACKET;
 		if (size != sizeof(__u64))
 			return false;
 		break;
-	case offsetof(struct sk_msg_md, data_end):
+	case bpf_ctx_range_ptr(struct sk_msg_md, data_end):
 		info->reg_type = PTR_TO_PACKET_END;
 		if (size != sizeof(__u64))
 			return false;
 		break;
-	case offsetof(struct sk_msg_md, sk):
+	case bpf_ctx_range_ptr(struct sk_msg_md, sk):
 		if (size != sizeof(__u64))
 			return false;
 		info->reg_type = PTR_TO_SOCKET;
@@ -11584,7 +11584,7 @@ static bool sk_lookup_is_valid_access(int off, int size,
 		return false;
 
 	switch (off) {
-	case offsetof(struct bpf_sk_lookup, sk):
+	case bpf_ctx_range_ptr(struct bpf_sk_lookup, sk):
 		info->reg_type = PTR_TO_SOCKET_OR_NULL;
 		return size == sizeof(__u64);
 
Signature · 1 line
2.53.0
author maintainer
#549 lore raw

PATCH 549/1424 netfilter: nft_counter: serialize reset with spinlock

In reply to Greg Kroah-Hartman

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

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

From: Brian Witte <[email protected]>

[ Upstream commit 779c60a5190c42689534172f4b49e927c9959e4e ]

Add a global static spinlock to serialize counter fetch+reset operations, preventing concurrent dump-and-reset from underrunning values.

The lock is taken before fetching the total so that two parallel resets cannot both read the same counter values and then both subtract them.

A global lock is used for simplicity since resets are infrequent. If this becomes a bottleneck, it can be replaced with a per-net lock later.

Fixes bd662c4218f9 ("netfilter: nf_tables: Add locking for NFT_MSG_GETOBJ_RESET requests")
Fixes 3d483faa6663 ("netfilter: nf_tables: Add locking for NFT_MSG_GETSETELEM_RESET requests")
Fixes 3cb03edb4de3 ("netfilter: nf_tables: Add locking for NFT_MSG_GETRULE_RESET requests")
---
 net/netfilter/nft_counter.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)
net/netfilter/nft_counter.c +16 -4
index 813c75cd2c259..3fa6369790f4d 100644
@@ -32,6 +32,9 @@ struct nft_counter_percpu_priv {
 
 static DEFINE_PER_CPU(struct u64_stats_sync, nft_counter_sync);
 
+/* control plane only: sync fetch+reset */
+static DEFINE_SPINLOCK(nft_counter_lock);
+
 static inline void nft_counter_do_eval(struct nft_counter_percpu_priv *priv,
 				       struct nft_regs *regs,
 				       const struct nft_pktinfo *pkt)
@@ -148,13 +151,25 @@ static void nft_counter_fetch(struct nft_counter_percpu_priv *priv,
 	}
 }
 
+static void nft_counter_fetch_and_reset(struct nft_counter_percpu_priv *priv,
+					struct nft_counter_tot *total)
+{
+	spin_lock(&nft_counter_lock);
+	nft_counter_fetch(priv, total);
+	nft_counter_reset(priv, total);
+	spin_unlock(&nft_counter_lock);
+}
+
 static int nft_counter_do_dump(struct sk_buff *skb,
 			       struct nft_counter_percpu_priv *priv,
 			       bool reset)
 {
 	struct nft_counter_tot total;
 
-	nft_counter_fetch(priv, &total);
+	if (unlikely(reset))
+		nft_counter_fetch_and_reset(priv, &total);
+	else
+		nft_counter_fetch(priv, &total);
 
 	if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes),
 			 NFTA_COUNTER_PAD) ||
@@ -162,9 +177,6 @@ static int nft_counter_do_dump(struct sk_buff *skb,
 			 NFTA_COUNTER_PAD))
 		goto nla_put_failure;
 
-	if (reset)
-		nft_counter_reset(priv, &total);
-
 	return 0;
 
 nla_put_failure:
Signature · 1 line
2.53.0
author maintainer
#550 lore raw

PATCH 550/1424 bridge: mrp: reject zero test interval to avoid OOM panic

In reply to Greg Kroah-Hartman

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

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

From: Xiang Mei <[email protected]>

[ Upstream commit fa6e24963342de4370e3a3c9af41e38277b74cf3 ]

br_mrp_start_test() and br_mrp_start_in_test() accept the user-supplied interval value from netlink without validation. When interval is 0, usecs_to_jiffies(0) yields 0, causing the delayed work (br_mrp_test_work_expired / br_mrp_in_test_work_expired) to reschedule itself with zero delay. This creates a tight loop on system_percpu_wq that allocates and transmits MRP test frames at maximum rate, exhausting all system memory and causing a kernel panic via OOM deadlock.

The same zero-interval issue applies to br_mrp_start_in_test_parse() for interconnect test frames.

Use NLA_POLICY_MIN(NLA_U32, 1) in the nla_policy tables for both IFLA_BRIDGE_MRP_START_TEST_INTERVAL and IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL, so zero is rejected at the netlink attribute parsing layer before the value ever reaches the workqueue scheduling code. This is consistent with how other bridge subsystems (br_fdb, br_mst) enforce range constraints on netlink attributes.

Fixes 20f6a05ef635 ("bridge: mrp: Rework the MRP netlink interface")
Fixes 7ab1748e4ce6 ("bridge: mrp: Extend MRP netlink interface for configuring MRP interconnect")
---
 net/bridge/br_mrp_netlink.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/bridge/br_mrp_netlink.c +2 -2
index ce6f63c77cc0a..86f0e75d6e345 100644
@@ -196,7 +196,7 @@ static const struct nla_policy
 br_mrp_start_test_policy[IFLA_BRIDGE_MRP_START_TEST_MAX + 1] = {
 	[IFLA_BRIDGE_MRP_START_TEST_UNSPEC]	= { .type = NLA_REJECT },
 	[IFLA_BRIDGE_MRP_START_TEST_RING_ID]	= { .type = NLA_U32 },
-	[IFLA_BRIDGE_MRP_START_TEST_INTERVAL]	= { .type = NLA_U32 },
+	[IFLA_BRIDGE_MRP_START_TEST_INTERVAL]	= NLA_POLICY_MIN(NLA_U32, 1),
 	[IFLA_BRIDGE_MRP_START_TEST_MAX_MISS]	= { .type = NLA_U32 },
 	[IFLA_BRIDGE_MRP_START_TEST_PERIOD]	= { .type = NLA_U32 },
 	[IFLA_BRIDGE_MRP_START_TEST_MONITOR]	= { .type = NLA_U32 },
@@ -316,7 +316,7 @@ static const struct nla_policy
 br_mrp_start_in_test_policy[IFLA_BRIDGE_MRP_START_IN_TEST_MAX + 1] = {
 	[IFLA_BRIDGE_MRP_START_IN_TEST_UNSPEC]	= { .type = NLA_REJECT },
 	[IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID]	= { .type = NLA_U32 },
-	[IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL]	= { .type = NLA_U32 },
+	[IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL]	= NLA_POLICY_MIN(NLA_U32, 1),
 	[IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS]	= { .type = NLA_U32 },
 	[IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD]	= { .type = NLA_U32 },
 };
Signature · 1 line
2.53.0
author maintainer
#551 lore raw

PATCH 551/1424 bpf: Fix same-register dst/src OOB read and pointer leak in sock_ops

In reply to Greg Kroah-Hartman

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

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

From: Jiayuan Chen <[email protected]>

[ Upstream commit 10f86a2a5c91fc4c4d001960f1c21abe52545ef6 ]

When a BPF sock_ops program accesses ctx fields with dst_reg == src_reg, the SOCK_OPS_GET_SK() and SOCK_OPS_GET_FIELD() macros fail to zero the destination register in the !fullsock / !locked_tcp_sock path.

Both macros borrow a temporary register to check is_fullsock / is_locked_tcp_sock when dst_reg == src_reg, because dst_reg holds the ctx pointer. When the check is false (e.g., TCP_NEW_SYN_RECV state with a request_sock), dst_reg should be zeroed but is not, leaving the stale ctx pointer:

- SOCK_OPS_GET_SK: dst_reg retains the ctx pointer, passes NULL checks as PTR_TO_SOCKET_OR_NULL, and can be used as a bogus socket pointer, leading to stack-out-of-bounds access in helpers like bpf_skc_to_tcp6_sock().

- SOCK_OPS_GET_FIELD: dst_reg retains the ctx pointer which the verifier believes is a SCALAR_VALUE, leaking a kernel pointer.

Fix both macros by:
- Changing JMP_A(1) to JMP_A(2) in the fullsock path to skip the added instruction.
- Adding BPF_MOV64_IMM(si->dst_reg, 0) after the temp register restore in the !fullsock path, placed after the restore because dst_reg == src_reg means we need src_reg intact to read ctx->temp.

Fixes fd09af010788 ("bpf: sock_ops ctx access may stomp registers in corner case")
Fixes 84f44df664e9 ("bpf: sock_ops sk access may stomp registers when dst_reg = src_reg")
---
 net/core/filter.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
net/core/filter.c +4 -2
index a9fcae823ca90..f568006baafa8 100644
@@ -10404,10 +10404,11 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
 				      si->dst_reg, si->dst_reg,		      \
 				      offsetof(OBJ, OBJ_FIELD));	      \
 		if (si->dst_reg == si->src_reg)	{			      \
-			*insn++ = BPF_JMP_A(1);				      \
+			*insn++ = BPF_JMP_A(2);				      \
 			*insn++ = BPF_LDX_MEM(BPF_DW, reg, si->src_reg,	      \
 				      offsetof(struct bpf_sock_ops_kern,      \
 				      temp));				      \
+			*insn++ = BPF_MOV64_IMM(si->dst_reg, 0);	      \
 		}							      \
 	} while (0)
 
@@ -10441,10 +10442,11 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
 				      si->dst_reg, si->src_reg,		      \
 				      offsetof(struct bpf_sock_ops_kern, sk));\
 		if (si->dst_reg == si->src_reg)	{			      \
-			*insn++ = BPF_JMP_A(1);				      \
+			*insn++ = BPF_JMP_A(2);				      \
 			*insn++ = BPF_LDX_MEM(BPF_DW, reg, si->src_reg,	      \
 				      offsetof(struct bpf_sock_ops_kern,      \
 				      temp));				      \
+			*insn++ = BPF_MOV64_IMM(si->dst_reg, 0);	      \
 		}							      \
 	} while (0)
 
Signature · 1 line
2.53.0
author maintainer
#552 lore raw

PATCH 552/1424 ksmbd: fix use-after-free in smb2_open during durable reconnect

In reply to Greg Kroah-Hartman

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

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

From: Akif <[email protected]>

[ Upstream commit 1baff47b81f94f9231c91236aa511420d0e266b9 ]

In smb2_open, the call to ksmbd_put_durable_fd(fp) drops the reference to the durable file descriptor early during the durable reconnect process. If an error occurs subsequently (eg, ksmbd_iov_pin_rsp fails) or a scavenger accesses the file, it leads to a use-after-free when accessing fp properties (eg fp->create_time).

Move the single put to the end of the function below err_out2 so fp stays valid until smb2_open returns.

Fixes c8efcc786146 ("ksmbd: add support for durable handles v1/v2")
---
 fs/smb/server/smb2pdu.c | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)
fs/smb/server/smb2pdu.c +5 -8
index d97ffe3d2a710..b61b97bb21cd7 100644
@@ -2996,29 +2996,23 @@ int smb2_open(struct ksmbd_work *work)
 		if (dh_info.reconnected == true) {
 			rc = smb2_check_durable_oplock(conn, share, dh_info.fp,
 					lc, sess->user, name);
-			if (rc) {
-				ksmbd_put_durable_fd(dh_info.fp);
+			if (rc)
 				goto err_out2;
-			}
 
 			rc = ksmbd_reopen_durable_fd(work, dh_info.fp);
-			if (rc) {
-				ksmbd_put_durable_fd(dh_info.fp);
+			if (rc)
 				goto err_out2;
-			}
 
 			fp = dh_info.fp;
 
 			if (ksmbd_override_fsids(work)) {
 				rc = -ENOMEM;
-				ksmbd_put_durable_fd(dh_info.fp);
 				goto err_out2;
 			}
 
 			file_info = FILE_OPENED;
 
 			rc = ksmbd_vfs_getattr(&fp->filp->f_path, &stat);
-			ksmbd_put_durable_fd(fp);
 			if (rc)
 				goto err_out2;
 
@@ -3788,6 +3782,9 @@ int smb2_open(struct ksmbd_work *work)
 		ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status);
 	}
 
+	if (dh_info.reconnected)
+		ksmbd_put_durable_fd(dh_info.fp);
+
 	kfree(name);
 	kfree(lc);
 
Signature · 1 line
2.53.0
author maintainer
#553 lore raw

PATCH 553/1424 ksmbd: fix durable reconnect error path file lifetime

In reply to Greg Kroah-Hartman

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

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

From: Junyi Liu <[email protected]>

[ Upstream commit 3515503322f4819277091839eed46b695096aca5 ]

After a durable reconnect succeeds, ksmbd_reopen_durable_fd() republishes the same ksmbd_file into the session volatile-id table. If smb2_open() then takes a later error path, cleanup first calls ksmbd_fd_put(work, fp) and then unconditionally calls ksmbd_put_durable_fd(dh_info.fp).

In this case fp and dh_info.fp are the same object. The first put drops the reconnect lookup reference, but the final durable put can run __ksmbd_close_fd(NULL, fp). Because the final close is not session-aware, it can free the file object without removing the volatile-id entry that was just published into the session table.

Use the session-aware put for the final reconnect drop when the reconnect had already succeeded and the error path is cleaning up the republished file. Earlier reconnect failures, before fp is assigned to dh_info.fp, keep using the durable-only put path.

Fixes 1baff47b81f9 ("ksmbd: fix use-after-free in smb2_open during durable reconnect")
---
 fs/smb/server/smb2pdu.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)
fs/smb/server/smb2pdu.c +13 -2
index b61b97bb21cd7..8d04f64cb0830 100644
@@ -3782,8 +3782,19 @@ int smb2_open(struct ksmbd_work *work)
 		ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status);
 	}
 
-	if (dh_info.reconnected)
-		ksmbd_put_durable_fd(dh_info.fp);
+	if (dh_info.reconnected) {
+		/*
+		 * If reconnect succeeded, fp was republished in the
+		 * session file table.  On a later error, ksmbd_fd_put()
+		 * above drops the session reference; drop the durable
+		 * lookup reference through the same session-aware path so
+		 * final close removes the volatile id before freeing fp.
+		 */
+		if (rc && fp == dh_info.fp)
+			ksmbd_fd_put(work, dh_info.fp);
+		else
+			ksmbd_put_durable_fd(dh_info.fp);
+	}
 
 	kfree(name);
 	kfree(lc);
Signature · 1 line
2.53.0
author maintainer
#554 lore raw

PATCH 554/1424 ceph: fix BUG_ON in __ceph_build_xattrs_blob() due to stale blob size

In reply to Greg Kroah-Hartman

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

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

From: Viacheslav Dubeyko <[email protected]>

[ Upstream commit 0c22d9511cbde746622f8e4c11aaa63fe76d45f9 ]

The generic/642 test-case can reproduce the kernel crash:

[40243.605254] ------------[ cut here ]------------
[40243.605956] kernel BUG at fs/ceph/xattr.c:918!
[40243.607142] Oops: invalid opcode: 0000 [#1] SMP PTI
[40243.608067] CPU: 7 UID: 0 PID: 498762 Comm: kworker/7:1 Not tainted 7.0.0-rc7+ #3 PREEMPT(full)
[40243.609700] Hardware name: QEMU Ubuntu 25.10 PC v2 (i440FX + PIIX, + 10.1 machine, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[40243.611820] Workqueue: ceph-msgr ceph_con_workfn
[40243.612715] RIP: 0010:__ceph_build_xattrs_blob+0x1b8/0x1e0
[40243.613731] Code: 0f 84 82 fe ff ff e9 cf 8e 56 ff 48 8d 65 e8 31 c0 5b 41 5c 41 5d 5d 31 d2 31 c9 31 f6 31 ff 45 31 c0 45 31 c9 c3 cc cc cc cc <0f> 0b 4c 8b 62 08 41 8b 85 24 07 00 00 49 83 c4 04 41 89 44 24 fc
[40243.616888] RSP: 0018:ffffcc80c4d4b688 EFLAGS: 00010287
[40243.617773] RAX: 0000000000010026 RBX: 0000000000000001 RCX: 0000000000000000
[40243.618928] RDX: ffff8a773798dee0 RSI: 0000000000000000 RDI: 0000000000000000
[40243.620158] RBP: ffffcc80c4d4b6a0 R08: 0000000000000000 R09: 0000000000000000
[40243.621573] R10: 0000000000000000 R11: 0000000000000000 R12: ffff8a75f3b58000
[40243.622907] R13: ffff8a75f3b58000 R14: 0000000000000080 R15: 000000000000bffd
[40243.624054] FS:  0000000000000000(0000) GS:ffff8a787d1b4000(0000) knlGS:0000000000000000
[40243.625331] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[40243.626269] CR2: 000072f390b623c0 CR3: 000000011c02a003 CR4: 0000000000372ef0
[40243.627408] Call Trace:
[40243.627839]  <TASK>
[40243.628188]  __prep_cap+0x3fd/0x4a0
[40243.628789]  ? do_raw_spin_unlock+0x4e/0xe0
[40243.629474]  ceph_check_caps+0x46a/0xc80
[40243.630094]  ? __lock_acquire+0x4a2/0x2650
[40243.630773]  ? find_held_lock+0x31/0x90
[40243.631347]  ? handle_cap_grant+0x79f/0x1060
[40243.632068]  ? lock_release+0xd9/0x300
[40243.632696]  ? __mutex_unlock_slowpath+0x3e/0x340
[40243.633429]  ? lock_release+0xd9/0x300
[40243.634052]  handle_cap_grant+0xcf6/0x1060
[40243.634745]  ceph_handle_caps+0x122b/0x2110
[40243.635415]  mds_dispatch+0x5bd/0x2160
[40243.636034]  ? ceph_con_process_message+0x65/0x190
[40243.636828]  ? lock_release+0xd9/0x300
[40243.637431]  ceph_con_process_message+0x7a/0x190
[40243.638184]  ? kfree+0x311/0x4f0
[40243.638749]  ? kfree+0x311/0x4f0
[40243.639268]  process_message+0x16/0x1a0
[40243.639915]  ? sg_free_table+0x39/0x90
[40243.640572]  ceph_con_v2_try_read+0xf58/0x2120
[40243.641255]  ? lock_acquire+0xc8/0x300
[40243.641863]  ceph_con_workfn+0x151/0x820
[40243.642493]  process_one_work+0x22f/0x630
[40243.643093]  ? process_one_work+0x254/0x630
[40243.643770]  worker_thread+0x1e2/0x400
[40243.644332]  ? __pfx_worker_thread+0x10/0x10
[40243.645020]  kthread+0x109/0x140
[40243.645560]  ? __pfx_kthread+0x10/0x10
[40243.646125]  ret_from_fork+0x3f8/0x480
[40243.646752]  ? __pfx_kthread+0x10/0x10
[40243.647316]  ? __pfx_kthread+0x10/0x10
[40243.647919]  ret_from_fork_asm+0x1a/0x30
[40243.648556]  </TASK>
[40243.648902] Modules linked in: overlay hctr2 libpolyval chacha libchacha adiantum libnh libpoly1305 essiv intel_rapl_msr intel_rapl_common intel_uncore_frequency_common skx_edac_common nfit kvm_intel kvm irqbypass joydev ghash_clmulni_intel aesni_intel rapl input_leds mac_hid psmouse vga16fb serio_raw vgastate floppy i2c_piix4 pata_acpi bochs qemu_fw_cfg i2c_smbus sch_fq_codel rbd dm_crypt msr parport_pc ppdev lp parport efi_pstore
[40243.654766] ---[ end trace 0000000000000000 ]---

Commit d93231a6bc8a ("ceph: prevent a client from exceeding the MDS maximum xattr size") moved the required_blob_size computation to before the __build_xattrs() call, introducing a race.

__build_xattrs() releases and reacquires i_ceph_lock during execution. In that window, handle_cap_grant() may update i_xattrs.blob with a newer MDS-provided blob and bump i_xattrs.version. When __build_xattrs() detects that index_version < version, it destroys and rebuilds the entire xattr rb-tree from the new blob, potentially increasing count, names_size, and vals_size.

The prealloc_blob size check that follows still uses the stale required_blob_size computed before the rebuild, so it passes even when prealloc_blob is too small for the now-larger tree. After __set_xattr() adds one more xattr on top, __ceph_build_xattrs_blob() is called from the cap flush path and hits:

    BUG_ON(need > ci->i_xattrs.prealloc_blob->alloc_len);

Fix this by recomputing required_blob_size after __build_xattrs() returns, using the current tree state. Also re-validate against m_max_xattr_size to fall back to the sync path if the rebuilt tree now exceeds the MDS limit.

Fixes d93231a6bc8a ("ceph: prevent a client from exceeding the MDS maximum xattr size")
---
 fs/ceph/xattr.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
fs/ceph/xattr.c +16 -0
index b9c3f000c9ffb..17da8be8423fe 100644
@@ -1257,6 +1257,22 @@ int __ceph_setxattr(struct inode *inode, const char *name,
 	      ceph_vinop(inode), name, ceph_cap_string(issued));
 	__build_xattrs(inode);
 
+	/*
+	 * __build_xattrs() may have released and reacquired i_ceph_lock,
+	 * during which handle_cap_grant() could have replaced i_xattrs.blob
+	 * with a newer MDS-provided blob and bumped i_xattrs.version. If that
+	 * caused __build_xattrs() to rebuild the rb-tree from the new blob,
+	 * count/names_size/vals_size may now be larger than when
+	 * required_blob_size was computed above. Recompute it here so the
+	 * prealloc_blob size check below reflects the current tree state.
+	 */
+	required_blob_size = __get_required_blob_size(ci, name_len, val_len);
+	if (required_blob_size > mdsc->mdsmap->m_max_xattr_size) {
+		doutc(cl, "sync (size too large): %d > %llu\n",
+		      required_blob_size, mdsc->mdsmap->m_max_xattr_size);
+		goto do_sync;
+	}
+
 	if (!ci->i_xattrs.prealloc_blob ||
 	    required_blob_size > ci->i_xattrs.prealloc_blob->alloc_len) {
 		struct ceph_buffer *blob;
Signature · 1 line
2.53.0
author maintainer
#555 lore raw

PATCH 555/1424 batman-adv: dat: atomically update mac addresses

In reply to Greg Kroah-Hartman

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

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

From: Sven Eckelmann <[email protected]>

commit e6de568d3eda3e3c01c868fabd7a9535d5ee4a73 upstream.

When a MAC address is updated in batadv_dat_entry_add(), it is done using a simple copy function. A parallel reader might only see parts of this update. In worst case, the reader is transporting the half updated MAC address over the network or is creating an ARP response using it - poisoning the ARP cache.

atomic64_t can be used to store the 48 bit of a mac address. A reader will then either see the old mac address or the new one - never a mixture of both.

Fixes 2f1dfbe18507 ("batman-adv: Distributed ARP Table - implement local storage")
[ Context ]
---
 net/batman-adv/distributed-arp-table.c | 58 +++++++++++++++++---------
 net/batman-adv/types.h                 |  2 +-
 2 files changed, 40 insertions(+), 20 deletions(-)
net/batman-adv/distributed-arp-table.c +39 -19
index 0594794e2e118..c6b7b3bece813 100644
@@ -376,18 +376,19 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
 static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
 				 u8 *mac_addr, unsigned short vid)
 {
+	u64 u64_mac = ether_addr_to_u64(mac_addr);
 	struct batadv_dat_entry *dat_entry;
 	int hash_added;
 
 	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid);
 	/* if this entry is already known, just update it */
 	if (dat_entry) {
-		if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr))
-			ether_addr_copy(dat_entry->mac_addr, mac_addr);
+		atomic64_set(&dat_entry->mac_addr, u64_mac);
+
 		dat_entry->last_update = jiffies;
 		batadv_dbg(BATADV_DBG_DAT, bat_priv,
 			   "Entry updated: %pI4 %pM (vid: %d)\n",
-			   &dat_entry->ip, dat_entry->mac_addr,
+			   &dat_entry->ip, mac_addr,
 			   batadv_print_vid(vid));
 		goto out;
 	}
@@ -398,7 +399,7 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
 
 	dat_entry->ip = ip;
 	dat_entry->vid = vid;
-	ether_addr_copy(dat_entry->mac_addr, mac_addr);
+	atomic64_set(&dat_entry->mac_addr, u64_mac);
 	dat_entry->last_update = jiffies;
 	kref_init(&dat_entry->refcount);
 
@@ -414,7 +415,7 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
 	}
 
 	batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM (vid: %d)\n",
-		   &dat_entry->ip, dat_entry->mac_addr, batadv_print_vid(vid));
+		   &dat_entry->ip, mac_addr, batadv_print_vid(vid));
 
 out:
 	batadv_dat_entry_put(dat_entry);
@@ -870,6 +871,8 @@ batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid,
 			    struct netlink_callback *cb,
 			    struct batadv_dat_entry *dat_entry)
 {
+	u8 mac[ETH_ALEN];
+	u64 u64_mac;
 	int msecs;
 	void *hdr;
 
@@ -882,11 +885,12 @@ batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid,
 	genl_dump_check_consistent(cb, hdr);
 
 	msecs = jiffies_to_msecs(jiffies - dat_entry->last_update);
+	u64_mac = atomic64_read(&dat_entry->mac_addr);
+	u64_to_ether_addr(u64_mac, mac);
 
 	if (nla_put_in_addr(msg, BATADV_ATTR_DAT_CACHE_IP4ADDRESS,
 			    dat_entry->ip) ||
-	    nla_put(msg, BATADV_ATTR_DAT_CACHE_HWADDRESS, ETH_ALEN,
-		    dat_entry->mac_addr) ||
+	    nla_put(msg, BATADV_ATTR_DAT_CACHE_HWADDRESS, ETH_ALEN, mac) ||
 	    nla_put_u16(msg, BATADV_ATTR_DAT_CACHE_VID, dat_entry->vid) ||
 	    nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS, msecs)) {
 		genlmsg_cancel(msg, hdr);
@@ -1162,6 +1166,8 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
 	struct net_device *soft_iface = bat_priv->soft_iface;
 	int hdr_size = 0;
 	unsigned short vid;
+	u8 mac[ETH_ALEN];
+	u64 u64_mac;
 
 	if (!atomic_read(&bat_priv->distributed_arp_table))
 		goto out;
@@ -1189,6 +1195,9 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
 
 	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
 	if (dat_entry) {
+		u64_mac = atomic64_read(&dat_entry->mac_addr);
+		u64_to_ether_addr(u64_mac, mac);
+
 		/* If the ARP request is destined for a local client the local
 		 * client will answer itself. DAT would only generate a
 		 * duplicate packet.
@@ -1197,7 +1206,7 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
 		 * additional DAT answer may trigger kernel warnings about
 		 * a packet coming from the wrong port.
 		 */
-		if (batadv_is_my_client(bat_priv, dat_entry->mac_addr, vid)) {
+		if (batadv_is_my_client(bat_priv, mac, vid)) {
 			ret = true;
 			goto out;
 		}
@@ -1207,18 +1216,16 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
 		 * the backbone gws belonging to our backbone has claimed the
 		 * destination.
 		 */
-		if (!batadv_bla_check_claim(bat_priv,
-					    dat_entry->mac_addr, vid)) {
+		if (!batadv_bla_check_claim(bat_priv, mac, vid)) {
 			batadv_dbg(BATADV_DBG_DAT, bat_priv,
 				   "Device %pM claimed by another backbone gw. Don't send ARP reply!",
-				   dat_entry->mac_addr);
+				   mac);
 			ret = true;
 			goto out;
 		}
 
 		skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src,
-						      dat_entry->mac_addr,
-						      hw_src, vid);
+						      mac, hw_src, vid);
 		if (!skb_new)
 			goto out;
 
@@ -1260,6 +1267,8 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
 	struct batadv_dat_entry *dat_entry = NULL;
 	bool ret = false;
 	unsigned short vid;
+	u8 mac[ETH_ALEN];
+	u64 u64_mac;
 	int err;
 
 	if (!atomic_read(&bat_priv->distributed_arp_table))
@@ -1287,8 +1296,11 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
 	if (!dat_entry)
 		goto out;
 
+	u64_mac = atomic64_read(&dat_entry->mac_addr);
+	u64_to_ether_addr(u64_mac, mac);
+
 	skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src,
-					      dat_entry->mac_addr, hw_src, vid);
+					      mac, hw_src, vid);
 	if (!skb_new)
 		goto out;
 
@@ -1379,6 +1391,8 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
 	u8 *hw_src, *hw_dst;
 	bool dropped = false;
 	unsigned short vid;
+	u8 mac[ETH_ALEN];
+	u64 u64_mac;
 
 	if (!atomic_read(&bat_priv->distributed_arp_table))
 		goto out;
@@ -1407,11 +1421,17 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
 	 * this frame would lead to doubled receive of an ARP reply.
 	 */
 	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_src, vid);
-	if (dat_entry && batadv_compare_eth(hw_src, dat_entry->mac_addr)) {
-		batadv_dbg(BATADV_DBG_DAT, bat_priv, "Doubled ARP reply removed: ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]; dat_entry: %pM-%pI4\n",
-			   hw_src, &ip_src, hw_dst, &ip_dst,
-			   dat_entry->mac_addr,	&dat_entry->ip);
-		dropped = true;
+	if (dat_entry) {
+		u64_mac = atomic64_read(&dat_entry->mac_addr);
+		u64_to_ether_addr(u64_mac, mac);
+
+		if (batadv_compare_eth(hw_src, mac)) {
+			batadv_dbg(BATADV_DBG_DAT, bat_priv,
+				   "Doubled ARP reply removed: ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]; dat_entry: %pM-%pI4\n",
+				   hw_src, &ip_src, hw_dst, &ip_dst,
+				   mac, &dat_entry->ip);
+			dropped = true;
+		}
 	}
 
 	/* Update our internal cache with both the IP addresses the node got
net/batman-adv/types.h +1 -1
index 4856cc98fc3c5..2ebc718bc3249 100644
@@ -2295,7 +2295,7 @@ struct batadv_dat_entry {
 	__be32 ip;
 
 	/** @mac_addr: the MAC address associated to the stored IPv4 */
-	u8 mac_addr[ETH_ALEN];
+	atomic64_t mac_addr;
 
 	/** @vid: the vlan ID associated to this entry */
 	unsigned short vid;
Signature · 1 line
2.53.0
author maintainer
#556 lore raw

PATCH 556/1424 batman-adv: bla: avoid CRC corruption due to parallel claim add

In reply to Greg Kroah-Hartman

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

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

From: Sven Eckelmann <[email protected]>

commit 08645ab95768b88e2ff85a89211994651710465b upstream.

batadv_bla_add_claim() is used to add claims and modify the backbone of claims for CLAIM frames from remote backbones and local packets. When it handles a claim, it needs to either

* add the new claim's CRC to the backbone CRC
* remove the already existing claim's CRC from the old backbone and add it to the new backbone

But when the "new" claim code was running in parallel to the "change backbone" code, it can happen that the CRC was invalid because the backbone_gw of the claim was changed twice in the "new" claim code path:

* CPU0 creates the claim for gateway A and publishes it in the claim hash. The crc16 of the address has not yet been added to A's crc at this point.

* CPU1 processes a claim frame of gateway B for the same client, finds the just published claim, and performs the ownership change: it switches the pointer to B, removes the crc16 from A's crc - which never contained it - and adds it to B's crc.

* CPU0 continues behind the creation branch, unconditionally switches the pointer back to A without compensating B's crc (its remove_crc is false for the creation path), and finally adds the crc16 to A's crc

The CRC is then wrong for both:

* claim belongs to A: but CRC is not part of backbone A's CRC
* claim doesn't belong to B: CRC is still part of backbone B's CRC

This wrong CRC is never recomputated from the stored claims. For local backbone claims, this can also not recovered using syncs.

To avoid this, split the functionality in clear separate parts:

* new claim which always adds claim CRC to the backbone CRC (but never changes the already set backbone_gw of the claim back)

* update of existing claim which automatically changes the backbone_gw entry and only updates both backbone CRCs when there was an actual change

Fixes 23721387c409 ("batman-adv: add basic bridge loop avoidance code")
[ Context ]
---
 net/batman-adv/bridge_loop_avoidance.c | 63 ++++++++++++++++----------
 1 file changed, 39 insertions(+), 24 deletions(-)
net/batman-adv/bridge_loop_avoidance.c +39 -24
index 148b8d726afa6..f84638421437d 100644
@@ -693,12 +693,14 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
 	struct batadv_bla_backbone_gw *old_backbone_gw;
 	struct batadv_bla_claim *claim;
 	struct batadv_bla_claim search_claim;
-	bool remove_crc = false;
 	int hash_added;
+	u16 claim_crc;
+	bool changed;
 
 	ether_addr_copy(search_claim.addr, mac);
 	search_claim.vid = vid;
 	claim = batadv_claim_hash_find(bat_priv, &search_claim);
+	claim_crc = crc16(0, mac, ETH_ALEN);
 
 	/* create a new claim entry if it does not exist yet. */
 	if (!claim) {
@@ -730,43 +732,56 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
 			kfree(claim);
 			return;
 		}
+
+		spin_lock_bh(&backbone_gw->crc_lock);
+		backbone_gw->crc ^= claim_crc;
+		spin_unlock_bh(&backbone_gw->crc_lock);
+
+		WRITE_ONCE(backbone_gw->lasttime, jiffies);
+
+		batadv_claim_put(claim);
+		return;
+	}
+
+	WRITE_ONCE(claim->lasttime, jiffies);
+
+	/* replace backbone_gw atomically and adjust reference counters */
+	spin_lock_bh(&claim->backbone_lock);
+	if (claim->backbone_gw != backbone_gw) {
+		changed = true;
+
+		old_backbone_gw = claim->backbone_gw;
+		kref_get(&backbone_gw->refcount);
+		claim->backbone_gw = backbone_gw;
 	} else {
-		WRITE_ONCE(claim->lasttime, jiffies);
-		if (claim->backbone_gw == backbone_gw)
-			/* no need to register a new backbone */
-			goto claim_free_ref;
+		old_backbone_gw = NULL;
+		changed = false;
+	}
+	spin_unlock_bh(&claim->backbone_lock);
 
+	if (changed) {
 		batadv_dbg(BATADV_DBG_BLA, bat_priv,
 			   "%s(): changing ownership for %pM, vid %d to gw %pM\n",
 			   __func__, mac, batadv_print_vid(vid),
 			   backbone_gw->orig);
 
-		remove_crc = true;
-	}
+		/* add claim address to new backbone_gw */
+		spin_lock_bh(&backbone_gw->crc_lock);
+		backbone_gw->crc ^= claim_crc;
+		spin_unlock_bh(&backbone_gw->crc_lock);
 
-	/* replace backbone_gw atomically and adjust reference counters */
-	spin_lock_bh(&claim->backbone_lock);
-	old_backbone_gw = claim->backbone_gw;
-	kref_get(&backbone_gw->refcount);
-	claim->backbone_gw = backbone_gw;
-	spin_unlock_bh(&claim->backbone_lock);
+		WRITE_ONCE(backbone_gw->lasttime, jiffies);
+	}
 
-	if (remove_crc) {
+	if (old_backbone_gw) {
 		/* remove claim address from old backbone_gw */
 		spin_lock_bh(&old_backbone_gw->crc_lock);
-		old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+		old_backbone_gw->crc ^= claim_crc;
 		spin_unlock_bh(&old_backbone_gw->crc_lock);
-	}
-
-	batadv_backbone_gw_put(old_backbone_gw);
 
-	/* add claim address to new backbone_gw */
-	spin_lock_bh(&backbone_gw->crc_lock);
-	backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
-	spin_unlock_bh(&backbone_gw->crc_lock);
-	WRITE_ONCE(backbone_gw->lasttime, jiffies);
+		batadv_backbone_gw_put(old_backbone_gw);
+	}
 
-claim_free_ref:
 	batadv_claim_put(claim);
 }
 
Signature · 1 line
2.53.0
author maintainer
#557 lore raw

PATCH 557/1424 perf sched: Fix register_pid() overflow, strcpy, and BUG_ON

In reply to Greg Kroah-Hartman

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

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

From: Arnaldo Carvalho de Melo <[email protected]>

commit 5949d339f5ec98752d56dcd4e36f619a59d513a5 upstream.

register_pid() has several issues when processing untrusted perf.data:

1. Integer overflow: (pid + 1) * sizeof(struct task_desc *) can wrap to a small value on 32-bit systems when pid is large (e.g. 0x40000000), causing realloc to return a tiny buffer followed by out-of-bounds writes in the initialization loop.

2. Heap buffer overflow: strcpy(task->comm, comm) copies the untrusted comm string into a fixed 20-byte COMM_LEN buffer with no length check.

3. BUG_ON on allocation failure: perf.data is untrusted input, so allocation failures should be handled gracefully rather than killing the process.

4. Realloc of sched->tasks assigned directly back, leaking the old pointer on failure; nr_tasks incremented before the realloc, leaving corrupted state on failure.

Cap pid at PID_MAX_LIMIT (4194304, matching the kernel's maximum on 64-bit), replace strcpy with strlcpy, guard against NULL comm, replace BUG_ON with NULL returns using safe realloc patterns, and add NULL checks in callers that dereference the result.

Fixes ec156764d424 ("perf sched: Import schedbench.c")
Cc Ingo Molnar <[email protected]>
Assisted-by Claude:claude-opus-4.6
[doebel: context adjustment for 6.6]
---
 tools/perf/builtin-sched.c | 40 ++++++++++++++++++++++++++++----------
 1 file changed, 30 insertions(+), 10 deletions(-)
tools/perf/builtin-sched.c +30 -10
index 16cb7278fabaf..dfdfac2a11ad4 100644
@@ -51,6 +51,7 @@
 #define COMM_LEN		20
 #define SYM_LEN			129
 #define MAX_PID			1024000
+#define PID_MAX_LIMIT		4194304 /* kernel limit on 64-bit */
 
 static const char *cpu_list;
 static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
@@ -448,17 +449,28 @@ static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *ta
 static struct task_desc *register_pid(struct perf_sched *sched,
 				      unsigned long pid, const char *comm)
 {
-	struct task_desc *task;
+	struct task_desc *task, **tasks_p;
 	static int pid_max;
 
+	/* perf.data is untrusted — cap pid to prevent overflow in size calculations */
+	if (pid >= PID_MAX_LIMIT) {
+		pr_err("pid %lu exceeds limit %d, skipping\n", pid, PID_MAX_LIMIT);
+		return NULL;
+	}
+
 	if (sched->pid_to_task == NULL) {
 		if (sysctl__read_int("kernel/pid_max", &pid_max) < 0)
 			pid_max = MAX_PID;
-		BUG_ON((sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *))) == NULL);
+		sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *));
+		if (sched->pid_to_task == NULL)
+			return NULL;
 	}
 	if (pid >= (unsigned long)pid_max) {
-		BUG_ON((sched->pid_to_task = realloc(sched->pid_to_task, (pid + 1) *
-			sizeof(struct task_desc *))) == NULL);
+		void *p = realloc(sched->pid_to_task, (pid + 1) * sizeof(struct task_desc *));
+
+		if (p == NULL)
+			return NULL;
+		sched->pid_to_task = p;
 		while (pid >= (unsigned long)pid_max)
 			sched->pid_to_task[pid_max++] = NULL;
 	}
@@ -469,9 +481,11 @@ static struct task_desc *register_pid(struct perf_sched *sched,
 		return task;
 
 	task = zalloc(sizeof(*task));
+	if (task == NULL)
+		return NULL;
 	task->pid = pid;
-	task->nr = sched->nr_tasks;
-	strcpy(task->comm, comm);
+	if (comm)
+		strlcpy(task->comm, comm, sizeof(task->comm));
 	/*
 	 * every task starts in sleeping state - this gets ignored
 	 * if there's no wakeup pointing to this sleep state:
@@ -479,10 +493,12 @@ static struct task_desc *register_pid(struct perf_sched *sched,
 	add_sched_event_sleep(sched, task, 0, 0);
 
 	sched->pid_to_task[pid] = task;
-	sched->nr_tasks++;
-	sched->tasks = realloc(sched->tasks, sched->nr_tasks * sizeof(struct task_desc *));
-	BUG_ON(!sched->tasks);
-	sched->tasks[task->nr] = task;
+	tasks_p = realloc(sched->tasks, (sched->nr_tasks + 1) * sizeof(struct task_desc *));
+	if (!tasks_p)
+		return NULL;
+	sched->tasks = tasks_p;
+	sched->tasks[sched->nr_tasks] = task;
+	task->nr = sched->nr_tasks++;
 
 	if (verbose > 0)
 		printf("registered task #%ld, PID %ld (%s)\n", sched->nr_tasks, pid, comm);
@@ -846,6 +862,8 @@ replay_wakeup_event(struct perf_sched *sched,
 
 	waker = register_pid(sched, sample->tid, "<unknown>");
 	wakee = register_pid(sched, pid, comm);
+	if (waker == NULL || wakee == NULL)
+		return -1;
 
 	add_sched_event_wakeup(sched, waker, sample->time, wakee);
 	return 0;
@@ -888,6 +906,8 @@ static int replay_switch_event(struct perf_sched *sched,
 
 	prev = register_pid(sched, prev_pid, prev_comm);
 	next = register_pid(sched, next_pid, next_comm);
+	if (prev == NULL || next == NULL)
+		return -1;
 
 	sched->cpu_last_switched[cpu] = timestamp;
 
Signature · 1 line
2.53.0
author maintainer
#558 lore raw

PATCH 558/1424 clk: meson: align gxbb_32k_clk_sel number of parents with actual count

In reply to Greg Kroah-Hartman

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

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

From: Martin Blumenstingl <[email protected]>

[ Upstream commit 628b6fee9fca292f12d07f0f1bcf1edefa949d81 ]

The following out-of-bounds read has been observed by Christian on a GXBB WeTek Hub:
==================================================================

BUG: KASAN: global-out-of-bounds in __clk_register+0x1b70/0x2418
Read of size 8 at addr ffffd66320cf88e0 by task swapper/0/1

CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Not tainted 7.0.0-rc5 #1 PREEMPT
Hardware name: WeTek Hub (DT)
Call trace:
 show_stack+0x14/0x20 (C)
 dump_stack_lvl+0x74/0x94
 print_report+0x164/0x4b0
 kasan_report+0x98/0xd8
 __asan_report_load8_noabort+0x1c/0x24
 __clk_register+0x1b70/0x2418
 devm_clk_hw_register+0x74/0x15c
 meson_clkc_init+0xd4/0x20c
 meson_clkc_syscon_probe+0x5c/0x94
 platform_probe+0xbc/0x17c
 really_probe+0x184/0x844
 __driver_probe_device+0x154/0x35c
 driver_probe_device+0x60/0x188
 __driver_attach+0x168/0x4a0
 bus_for_each_dev+0xec/0x180
 driver_attach+0x38/0x58
 bus_add_driver+0x238/0x4c0
 driver_register+0x150/0x388
 __platform_driver_register+0x54/0x7c
 gxbb_clkc_driver_init+0x18/0x20
 do_one_initcall+0xb8/0x340
 kernel_init_freeable+0x49c/0x52c
 kernel_init+0x24/0x148
 ret_from_fork+0x10/0x20

The buggy address belongs to the variable:
 gxbb_32k_clk_parents+0x60/0x400

The buggy address belongs to a vmalloc virtual mapping
The buggy address belongs to the physical page:

Memory state around the buggy address:
 ffffd66320cf8780: 00 00 00 00 f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9
 ffffd66320cf8800: 00 04 f9 f9 f9 f9 f9 f9 00 04 f9 f9 f9 f9 f9 f9
>ffffd66320cf8880: 00 00 00 00 00 00 00 00 00 00 00 00 f9 f9 f9 f9
                                                       ^
 ffffd66320cf8900: 00 01 f9 f9 f9 f9 f9 f9 00 06 f9 f9 f9 f9 f9 f9
 ffffd66320cf8980: 00 00 02 f9 f9 f9 f9 f9 00 00 02 f9 f9 f9 f9 f9
==================================================================

Commit 7915d7d5407c ("clk: amlogic: gxbb: drop non existing 32k clock parent") dropped a non-existing clock parent from the gxbb_32k_clk_sel mux but didn't adjust the hard-coded num_parents field. Fix the actual number of parents of that mux by using ARRAY_SIZE instead (avoiding similar problems in future).

---
 drivers/clk/meson/gxbb.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/clk/meson/gxbb.c +1 -1
index 00eaca92b388a..a6b46ad047949 100644
@@ -1305,7 +1305,7 @@ static struct clk_regmap gxbb_32k_clk_sel = {
 		.name = "32k_clk_sel",
 		.ops = &clk_regmap_mux_ops,
 		.parent_data = gxbb_32k_clk_parent_data,
-		.num_parents = 4,
+		.num_parents = ARRAY_SIZE(gxbb_32k_clk_parent_data),
 		.flags = CLK_SET_RATE_PARENT,
 	},
 };
Signature · 1 line
2.53.0
author maintainer
#559 lore raw

PATCH 559/1424 usb: typec: ucsi: use UCSI_TIMEOUT_MS for sync command completion

In reply to Greg Kroah-Hartman

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

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

From: Huang Wei <[email protected]>

commit eb4573cf2fd860b20adfae050c3f6ec6ddc3abdb upstream.

The synchronous command completion path in ucsi_acpi_sync_write() hardcodes a 5 second (5 * HZ) timeout when waiting for the PPM to signal command completion via ACPI notification. This value matched UCSI_TIMEOUT_MS when it was still 5000 ms, but it was not updated when that macro was later raised to 10000 ms to fix PPM reset timeouts.

As a result, the two PPM communication paths are now inconsistent: the polling path in ucsi_reset_ppm() respects the 10 second timeout, while the event-driven completion path still uses 5 seconds. On machines where the firmware is slow to respond during boot (e.g. some Lenovo ThinkPad models such as the E14 Gen 7), commands sent after the PPM reset, such as SET_NOTIFICATION_ENABLE and GET_CAPABILITY, can exceed 5 seconds and cause UCSI initialization to fail with:

ucsi_acpi USBC000:00: error -ETIMEDOUT: PPM init failed

Once UCSI init aborts, USB-C PD negotiation never completes, which in turn blocks USB-C dock enumeration since the dock depends on a successful PD contract.

Replace the hardcoded 5 * HZ with msecs_to_jiffies(UCSI_TIMEOUT_MS) so that both communication paths share a single, consistent timeout value. Move the UCSI_TIMEOUT_MS definition to ucsi.h so the ACPI backend can use it.

Fixes bf4f9ae1cb08c ("usb: typec: ucsi: increase timeout for PPM reset operations")
---
 drivers/usb/typec/ucsi/ucsi.c      | 10 ----------
 drivers/usb/typec/ucsi/ucsi.h      |  3 +++
 drivers/usb/typec/ucsi/ucsi_acpi.c |  3 ++-
 3 files changed, 5 insertions(+), 11 deletions(-)
drivers/usb/typec/ucsi/ucsi.c +0 -10
index b0d5f071664fe..8cfde3cb85af0 100644
@@ -17,16 +17,6 @@
 #include "ucsi.h"
 #include "trace.h"
 
-/*
- * UCSI_TIMEOUT_MS - PPM communication timeout
- *
- * Ideally we could use MIN_TIME_TO_RESPOND_WITH_BUSY (which is defined in UCSI
- * specification) here as reference, but unfortunately we can't. It is very
- * difficult to estimate the time it takes for the system to process the command
- * before it is actually passed to the PPM.
- */
-#define UCSI_TIMEOUT_MS		10000
-
 /*
  * UCSI_SWAP_TIMEOUT_MS - Timeout for role swap requests
  *
drivers/usb/typec/ucsi/ucsi.h +3 -0
index 9ac5ad87f4015..f0cf52c6ddeba 100644
@@ -26,6 +26,9 @@ struct dentry;
 #define UCSI_MESSAGE_OUT		32
 #define UCSIv2_MESSAGE_OUT		272
 
+/* PPM communication timeout in milliseconds */
+#define UCSI_TIMEOUT_MS		10000
+
 /* UCSI versions */
 #define UCSI_VERSION_1_2	0x0120
 #define UCSI_VERSION_2_0	0x0200
drivers/usb/typec/ucsi/ucsi_acpi.c +2 -1
index e40f3bf650ad8..1054947497bb2 100644
@@ -88,7 +88,8 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
 	if (ret)
 		goto out_clear_bit;
 
-	if (!wait_for_completion_timeout(&ua->complete, 5 * HZ))
+	if (!wait_for_completion_timeout(&ua->complete,
+					 msecs_to_jiffies(UCSI_TIMEOUT_MS)))
 		ret = -ETIMEDOUT;
 
 out_clear_bit:
Signature · 1 line
2.53.0
author maintainer
#560 lore raw

PATCH 560/1424 mm/damon/core: skip aging from repeated aggressive merging

In reply to Greg Kroah-Hartman

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

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

From: SJ Park <[email protected]>

[ Upstream commit 0250dbe08c730d003ef9f484da56ae09a1ea0c4c ]

The number of DAMON regions could temporarily exceed the user-defined maximum number of regions limit for corner cases. For example, users could lower the limit via runtime parameters update. For such a case, kdamond_merge_regions() repeats merging regions in the case doubling the merge threshold. The repeated merge operation could update the age of regions multiple times. This corrupts the monitoring results. Fix the issue by asking the merge operation to skip aging for the corner case.

The user impact is degradation of the monitoring quality. The impact should be mild, since the degradation is only temporal, and it is not common to happen in realistic setups.

The issue was discovered [1,2] by Sashiko.

---
 mm/damon/core-test.h |  2 +-
 mm/damon/core.c      | 17 +++++++++++------
 2 files changed, 12 insertions(+), 7 deletions(-)
mm/damon/core-test.h +1 -1
index e6c19a23b0806..dd91b0eea61ff 100644
@@ -255,7 +255,7 @@ static void damon_test_merge_regions_of(struct kunit *test)
 		damon_add_region(r, t);
 	}
 
-	damon_merge_regions_of(t, 9, 9999);
+	damon_merge_regions_of(t, 9, 9999, true);
 	/* 0-112, 114-130, 130-156, 156-170 */
 	KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 5u);
 	if (damon_nr_regions(t) != 5)
mm/damon/core.c +11 -6
index 734ab958ec576..0713fda22682c 100644
@@ -1192,15 +1192,17 @@ static void damon_merge_two_regions(struct damon_target *t,
  * sz_limit	size upper limit of each region
  */
 static void damon_merge_regions_of(struct damon_target *t, unsigned int thres,
-				   unsigned long sz_limit)
+		unsigned long sz_limit, bool count_age)
 {
 	struct damon_region *r, *prev = NULL, *next;
 
 	damon_for_each_region_safe(r, next, t) {
-		if (abs(r->nr_accesses - r->last_nr_accesses) > thres)
-			r->age = 0;
-		else
-			r->age++;
+		if (count_age) {
+			if (abs(r->nr_accesses - r->last_nr_accesses) > thres)
+				r->age = 0;
+			else
+				r->age++;
+		}
 
 		if (prev && prev->ar.end == r->ar.start &&
 		    abs(prev->nr_accesses - r->nr_accesses) <= thres &&
@@ -1234,15 +1236,18 @@ static void kdamond_merge_regions(struct damon_ctx *c, unsigned int threshold,
 	struct damon_target *t;
 	unsigned int nr_regions;
 	unsigned int max_thres;
+	bool count_age = true;
 
 	max_thres = c->attrs.aggr_interval /
 		(c->attrs.sample_interval ?  c->attrs.sample_interval : 1);
 	do {
 		nr_regions = 0;
 		damon_for_each_target(t, c) {
-			damon_merge_regions_of(t, threshold, sz_limit);
+			damon_merge_regions_of(t, threshold, sz_limit,
+					count_age);
 			nr_regions += damon_nr_regions(t);
 		}
+		count_age = false;
 		threshold = max(1, threshold * 2);
 	} while (nr_regions > c->attrs.max_nr_regions &&
 			threshold / 2 < max_thres);
Signature · 1 line
2.53.0
author maintainer
#561 lore raw

PATCH 561/1424 drm: Remove unused header in drm_dumb_buffers.c

In reply to Greg Kroah-Hartman

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

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

From: Yicong Hui <[email protected]>

[ Upstream commit 38b4ce17ef3421fb0e5e6dbdab1974282bde1165 ]

Remove the header #include "drm_internal.h" from drm_dumb_buffers.c, which is included but not used.

Header was introduced in commit 47f10854ca89 ("drm: Don't export the drm_gem_dumb_destroy() function") when moving functions, but was not removed in commit 96a7b60f6ddb ("drm: remove dumb_destroy callback") when the drm_gem_dumb_destroy function was removed.

Compiles successfully with DRM enabled, pass kunit tests and IGT-tests in a vng virtual machine.

---
 drivers/gpu/drm/drm_dumb_buffers.c | 1 -
 1 file changed, 1 deletion(-)
drivers/gpu/drm/drm_dumb_buffers.c +0 -1
index 70032bba1c97e..7b4ba9fab299a 100644
@@ -29,7 +29,6 @@
 #include <drm/drm_mode.h>
 
 #include "drm_crtc_internal.h"
-#include "drm_internal.h"
 
 /**
  * DOC: overview
Signature · 1 line
2.53.0
author maintainer
#562 lore raw

PATCH 562/1424 drm: lcdif: Wait for vblank before disabling DMA

In reply to Greg Kroah-Hartman

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

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

From: Paul Kocialkowski <[email protected]>

[ Upstream commit 351af554edd994898db12217c3be39979e168d35 ]

It is necessary to wait for the full frame to finish streaming through the DMA engine before we can safely disable it by removing the DISP_PARA_DISP_ON bit. Disabling it in-flight can leave the hardware confused and unable to resume streaming for the next frame.

This causes the FIFO underrun and empty status bits to be set and a single solid color to be shown on the display, coming from one of the pixels of the previous frame. The issue occurs sporadically when a new mode is set, which triggers the crtc disable and enable paths.

Setting the shadow load bit and waiting for it to be cleared by the DMA engine allows waiting for completion.

The NXP BSP driver addresses this issue with a hardcoded 25 ms sleep.

---
 drivers/gpu/drm/mxsfb/lcdif_kms.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)
drivers/gpu/drm/mxsfb/lcdif_kms.c +12 -3
index 2541d2de4e45f..16dd2957128eb 100644
@@ -373,14 +373,23 @@ static void lcdif_disable_controller(struct lcdif_drm_private *lcdif)
 	int ret;
 
 	reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
+	/* Disable the layer for DMA. */
 	reg &= ~CTRLDESCL0_5_EN;
+	/*
+	 * It is necessary to wait for the full frame to finish streaming
+	 * through the DMA engine before we can safely disable it by removing
+	 * the DISP_PARA_DISP_ON bit. Disabling it in-flight can leave the
+	 * hardware confused and unable to resume streaming for the next frame.
+	 */
+	reg |= CTRLDESCL0_5_SHADOW_LOAD_EN;
 	writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5);
 
+	/* Wait for the frame to finish or timeout after 50 ms. */
 	ret = readl_poll_timeout(lcdif->base + LCDC_V8_CTRLDESCL0_5,
-				 reg, !(reg & CTRLDESCL0_5_EN),
-				 0, 36000);	/* Wait ~2 frame times max */
+				 reg, !(reg & CTRLDESCL0_5_SHADOW_LOAD_EN),
+				 200, 50000);
 	if (ret)
-		drm_err(lcdif->drm, "Failed to disable controller!\n");
+		drm_err(lcdif->drm, "Timed out waiting for final vblank!\n");
 
 	reg = readl(lcdif->base + LCDC_V8_DISP_PARA);
 	reg &= ~DISP_PARA_DISP_ON;
Signature · 1 line
2.53.0
author maintainer
#563 lore raw

PATCH 563/1424 drm/rockchip: vop2: Recognise 10-bit YUV422 as YUV format

In reply to Greg Kroah-Hartman

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

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

From: Nicolas Frattaroli <[email protected]>

[ Upstream commit c1bfe8dac0a79d47eed313b9bcaa2658898684ec ]

The Rockchip VOP2 video output driver has a "is_yuv_output" function, which returns true when a given bus format is a YUV format, and false otherwise.

This switch statement is lacking the bus format used for YUV422 10-bit.

Add the two component orderings of the YUV422 10-bit bus formats to the switch statement.

---
 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +2 -0
index 97486eba01b7b..bd5bce9d8c2cf 100644
@@ -425,6 +425,8 @@ static bool is_yuv_output(u32 bus_format)
 	switch (bus_format) {
 	case MEDIA_BUS_FMT_YUV8_1X24:
 	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_YUYV10_1X20:
+	case MEDIA_BUS_FMT_UYVY10_1X20:
 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
 	case MEDIA_BUS_FMT_YUYV8_2X8:
Signature · 1 line
2.53.0