From 946352b2f88fd2378f0341312e47dff1e8dc2fac Mon Sep 17 00:00:00 2001 From: Daniele Briguglio Date: Wed, 24 Jun 2026 14:39:14 +0200 Subject: [PATCH 001/171] clk: rockchip: rk3588: don't disable unused I2S MCLK output gates No in-tree board references these gates yet. Boards drive the codec MCLK through the parent I2S*_8CH_MCLKOUT, and now that the gates are managed clocks, clk_disable_unused() turns them off at boot. On a board that relied on firmware leaving the output enabled, that cuts the MCLK and analog audio stops working. Mark the four gates CLK_IGNORE_UNUSED so an unreferenced gate keeps the state firmware left. A board that wants the kernel to own the gate can reference I2S*_8CH_MCLKOUT_TO_IO from DT instead. Fixes: 02b9b0bb6269 ("clk: rockchip: rk3588: add GATE_GRF clocks for I2S MCLK output to IO") Reported-by: Diederik de Haas Closes: https://lore.kernel.org/linux-rockchip/DJGDSS875DDO.22TYPVYK5X8KZ@cknow-tech.com/ Tested-by: Diederik de Haas Signed-off-by: Daniele Briguglio Tested-by: Ricardo Pardini # on R58X-Pro+NanoPC-T6 Reviewed-by: Sebastian Reichel Link: https://patch.msgid.link/20260624123914.1767374-1-hello@superkali.me Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-rk3588.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c index 2ba9976654cf..86953f9ffee3 100644 --- a/drivers/clk/rockchip/clk-rk3588.c +++ b/drivers/clk/rockchip/clk-rk3588.c @@ -895,7 +895,7 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { MUX(I2S2_2CH_MCLKOUT, "i2s2_2ch_mclkout", i2s2_2ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_CLKSEL_CON(30), 2, 1, MFLAGS), GATE_GRF(I2S2_2CH_MCLKOUT_TO_IO, "i2s2_2ch_mclkout_to_io", "i2s2_2ch_mclkout", - 0, RK3588_SYSGRF_SOC_CON6, 2, GFLAGS, grf_type_sys), + CLK_IGNORE_UNUSED, RK3588_SYSGRF_SOC_CON6, 2, GFLAGS, grf_type_sys), COMPOSITE(CLK_I2S3_2CH_SRC, "clk_i2s3_2ch_src", gpll_aupll_p, 0, RK3588_CLKSEL_CON(30), 8, 1, MFLAGS, 3, 5, DFLAGS, @@ -912,7 +912,7 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { MUX(I2S3_2CH_MCLKOUT, "i2s3_2ch_mclkout", i2s3_2ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_CLKSEL_CON(32), 2, 1, MFLAGS), GATE_GRF(I2S3_2CH_MCLKOUT_TO_IO, "i2s3_2ch_mclkout_to_io", "i2s3_2ch_mclkout", - 0, RK3588_SYSGRF_SOC_CON6, 7, GFLAGS, grf_type_sys), + CLK_IGNORE_UNUSED, RK3588_SYSGRF_SOC_CON6, 7, GFLAGS, grf_type_sys), GATE(PCLK_ACDCDIG, "pclk_acdcdig", "pclk_audio_root", 0, RK3588_CLKGATE_CON(7), 11, GFLAGS), GATE(HCLK_I2S0_8CH, "hclk_i2s0_8ch", "hclk_audio_root", 0, @@ -942,7 +942,7 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { MUX(I2S0_8CH_MCLKOUT, "i2s0_8ch_mclkout", i2s0_8ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_CLKSEL_CON(28), 2, 2, MFLAGS), GATE_GRF(I2S0_8CH_MCLKOUT_TO_IO, "i2s0_8ch_mclkout_to_io", "i2s0_8ch_mclkout", - 0, RK3588_SYSGRF_SOC_CON6, 0, GFLAGS, grf_type_sys), + CLK_IGNORE_UNUSED, RK3588_SYSGRF_SOC_CON6, 0, GFLAGS, grf_type_sys), GATE(HCLK_PDM1, "hclk_pdm1", "hclk_audio_root", 0, RK3588_CLKGATE_CON(9), 6, GFLAGS), @@ -2229,7 +2229,7 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { MUX(I2S1_8CH_MCLKOUT, "i2s1_8ch_mclkout", i2s1_8ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_PMU_CLKSEL_CON(9), 2, 2, MFLAGS), GATE_GRF(I2S1_8CH_MCLKOUT_TO_IO, "i2s1_8ch_mclkout_to_io", "i2s1_8ch_mclkout", - 0, RK3588_SYSGRF_SOC_CON6, 1, GFLAGS, grf_type_sys), + CLK_IGNORE_UNUSED, RK3588_SYSGRF_SOC_CON6, 1, GFLAGS, grf_type_sys), GATE(PCLK_PMU1, "pclk_pmu1", "pclk_pmu0_root", CLK_IS_CRITICAL, RK3588_PMU_CLKGATE_CON(1), 0, GFLAGS), GATE(CLK_DDR_FAIL_SAFE, "clk_ddr_fail_safe", "clk_pmu0", CLK_IGNORE_UNUSED, From 2ef00630c5c0b7b2c08aba7643f47594952d357e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 23 Jun 2026 13:56:45 +0200 Subject: [PATCH 002/171] clk: qcom: dispcc-eliza: Fix disp_cc_mdss_mdp_clk_src RCG stall on Eliza EVK Eliza EVK (eliza-cqs-evk.dts) does not have display enabled, however its Display Clock Controller is enabled and references parent clocks from DSI PHYs, which causes clock reparenting issues during probe (init) and warning on Eliza EVK: disp_cc_mdss_mdp_clk_src: rcg didn't update its configuration. WARNING: drivers/clk/qcom/clk-rcg2.c:136 at update_config+0xd4/0xe4, CPU#1: udevd/273 ... update_config (drivers/clk/qcom/clk-rcg2.c:136 (discriminator 2)) (P) clk_rcg2_shared_disable (drivers/clk/qcom/clk-rcg2.c:1471) clk_rcg2_shared_init (drivers/clk/qcom/clk-rcg2.c:1540) __clk_register (drivers/clk/clk.c:3959 drivers/clk/clk.c:4368) devm_clk_hw_register (drivers/clk/clk.c:4448 (discriminator 1) drivers/clk/clk.c:4672 (discriminator 1)) devm_clk_register_regmap (drivers/clk/qcom/clk-regmap.c:104) qcom_cc_really_probe (drivers/clk/qcom/common.c:418) qcom_cc_probe (drivers/clk/qcom/common.c:445) disp_cc_eliza_probe (dispcc-eliza.c:?) dispcc_eliza platform_probe (drivers/base/platform.c:1432) Fixes: 0e66f10942b5 ("clk: qcom: dispcc-eliza: Add Eliza display clock controller support") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Reviewed-by: Taniya Das Link: https://lore.kernel.org/r/20260623115644.392477-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/dispcc-eliza.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/qcom/dispcc-eliza.c b/drivers/clk/qcom/dispcc-eliza.c index 479f26e0dde2..e31c24125274 100644 --- a/drivers/clk/qcom/dispcc-eliza.c +++ b/drivers/clk/qcom/dispcc-eliza.c @@ -757,7 +757,7 @@ static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = { .parent_data = disp_cc_parent_data_11, .num_parents = ARRAY_SIZE(disp_cc_parent_data_11), .flags = CLK_SET_RATE_PARENT, - .ops = &clk_rcg2_shared_ops, + .ops = &clk_rcg2_shared_no_init_park_ops, }, }; From 650c88738ae8976f46ba71b24dd8aa311adc6fde Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Wed, 17 Jun 2026 14:51:24 +0000 Subject: [PATCH 003/171] optee: ffa: Add NULL check in optee_ffa_lend_protmem Sashiko (locally) reports a possible null dereference under memory pressure due to the lack of validation of the allocated pointer. Fix that by adding the missing check. Fixes: 2b78d79cdf96 ("optee: FF-A: dynamic protected memory allocation") Signed-off-by: Mostafa Saleh Signed-off-by: Sebastian Ene Reviewed-by: Sumit Garg Reviewed-by: Sebastian Ene Signed-off-by: Jens Wiklander --- drivers/tee/optee/ffa_abi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c index b4372fa268d0..633715b98625 100644 --- a/drivers/tee/optee/ffa_abi.c +++ b/drivers/tee/optee/ffa_abi.c @@ -698,6 +698,9 @@ static int optee_ffa_lend_protmem(struct optee *optee, struct tee_shm *protmem, int rc; mem_attr = kzalloc_objs(*mem_attr, ma_count); + if (!mem_attr) + return -ENOMEM; + for (n = 0; n < ma_count; n++) { mem_attr[n].receiver = mem_attrs[n] & U16_MAX; mem_attr[n].attrs = mem_attrs[n] >> 16; From 51ed53630915c9cc290036fe4f430849e23bbf8e Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 18 May 2026 02:58:36 +0000 Subject: [PATCH 004/171] clk: spacemit: k3: fix USB2 bus clock According to SpacemiT K3's updated docs, the USB2 ahb reset and USB2 bus clock enable bit was wrongly swapped, the correct one should be: Register : APMU_USB_CLK_RES_CTRL bit[1] : usb2_port_bus_clk_en bit[0] : usb2_port_ahb_rstn Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Reported-by: Junzhong Pan Link: https://patch.msgid.link/20260518-06-clk-reset-usb-fix-v1-1-14fc235e692b@kernel.org Signed-off-by: Yixun Lan --- drivers/clk/spacemit/ccu-k3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index cb0c4277f72a..03de04144963 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -777,7 +777,7 @@ static const struct clk_parent_data sdh2_parents[] = { CCU_MUX_DIV_GATE_FC_DEFINE(sdh2_clk, sdh2_parents, APMU_SDH2_CLK_RES_CTRL, 8, 3, BIT(11), 5, 3, BIT(4), 0); -CCU_GATE_DEFINE(usb2_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(0), 0); +CCU_GATE_DEFINE(usb2_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(1), 0); CCU_GATE_DEFINE(usb3_porta_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(4), 0); CCU_GATE_DEFINE(usb3_portb_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(8), 0); CCU_GATE_DEFINE(usb3_portc_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(12), 0); From 09780b4b4bdcc7848249dfc8e44b4d55aa388024 Mon Sep 17 00:00:00 2001 From: Michael Reeves Date: Mon, 29 Jun 2026 20:01:01 +1000 Subject: [PATCH 005/171] arm64: dts: apple: t8122: Fix I2C resources Correct the MMIO ranges and IRQs for T8122 I2C controllers. They were mistakenly incorrect in the original commit. This fix is required for i2c dependent hardware to function correctly, i.e. audio codecs. Tested on J613 (MacBook Air M3). Fixes: c65ab4905e58 ("arm64: dts: apple: Initial t8122 (M3) device trees") Signed-off-by: Michael Reeves Link: https://patch.msgid.link/20260629-t8122-i2c-fix-v1-1-cec044001550@gmail.com Signed-off-by: Sven Peter --- arch/arm64/boot/dts/apple/t8122.dtsi | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8122.dtsi b/arch/arm64/boot/dts/apple/t8122.dtsi index c6196225e96e..1ee61c5b3409 100644 --- a/arch/arm64/boot/dts/apple/t8122.dtsi +++ b/arch/arm64/boot/dts/apple/t8122.dtsi @@ -186,12 +186,12 @@ soc { /* Required to get >32-bit DMA via DARTs */ dma-ranges = <0 0 0 0 0xffffffff 0xffffc000>; - i2c0: i2c@235010000 { + i2c0: i2c@2a1010000 { compatible = "apple,t8122-i2c", "apple,t8103-i2c"; - reg = <0x2 0x35010000 0x0 0x4000>; + reg = <0x2 0xa1010000 0x0 0x4000>; clocks = <&clkref>; interrupt-parent = <&aic>; - interrupts = ; + interrupts = ; pinctrl-0 = <&i2c0_pins>; pinctrl-names = "default"; #address-cells = <0x1>; @@ -200,12 +200,12 @@ i2c0: i2c@235010000 { status = "disabled"; }; - i2c1: i2c@235014000 { + i2c1: i2c@2a1014000 { compatible = "apple,t8122-i2c", "apple,t8103-i2c"; - reg = <0x2 0x35014000 0x0 0x4000>; + reg = <0x2 0xa1014000 0x0 0x4000>; clocks = <&clkref>; interrupt-parent = <&aic>; - interrupts = ; + interrupts = ; pinctrl-0 = <&i2c1_pins>; pinctrl-names = "default"; #address-cells = <0x1>; @@ -214,12 +214,12 @@ i2c1: i2c@235014000 { status = "disabled"; }; - i2c2: i2c@235018000 { + i2c2: i2c@2a1018000 { compatible = "apple,t8122-i2c", "apple,t8103-i2c"; - reg = <0x2 0x35018000 0x0 0x4000>; + reg = <0x2 0xa1018000 0x0 0x4000>; clocks = <&clkref>; interrupt-parent = <&aic>; - interrupts = ; + interrupts = ; pinctrl-0 = <&i2c2_pins>; pinctrl-names = "default"; #address-cells = <0x1>; @@ -228,12 +228,12 @@ i2c2: i2c@235018000 { status = "disabled"; }; - i2c3: i2c@23501c000 { + i2c3: i2c@2a101c000 { compatible = "apple,t8122-i2c", "apple,t8103-i2c"; - reg = <0x2 0x3501c000 0x0 0x4000>; + reg = <0x2 0xa101c000 0x0 0x4000>; clocks = <&clkref>; interrupt-parent = <&aic>; - interrupts = ; + interrupts = ; pinctrl-0 = <&i2c3_pins>; pinctrl-names = "default"; #address-cells = <0x1>; @@ -242,12 +242,12 @@ i2c3: i2c@23501c000 { status = "disabled"; }; - i2c4: i2c@235020000 { + i2c4: i2c@2a1020000 { compatible = "apple,t8122-i2c", "apple,t8103-i2c"; - reg = <0x2 0x35020000 0x0 0x4000>; + reg = <0x2 0xa1020000 0x0 0x4000>; clocks = <&clkref>; interrupt-parent = <&aic>; - interrupts = ; + interrupts = ; pinctrl-0 = <&i2c4_pins>; pinctrl-names = "default"; #address-cells = <0x1>; From eb525edd48907795c0d4e498ff57ad168070b289 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 30 Jun 2026 08:53:19 +0000 Subject: [PATCH 006/171] clk: spacemit: k3: set hdma clock as critical HDMA clock is responsible for the internal TCM access path of X100 RISC-V core, so set the clock flag as critical to prevent it from being shut off, otherwise the Linux system will hang, for example in the case of a vector instruction access generates a page fault. Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Reported-by: Han Gao Tested-by: Aurelien Jarno Link: https://patch.msgid.link/20260630-06-clk-hdma-critial-v1-1-443c0ac88c5f@kernel.org Signed-off-by: Yixun Lan --- drivers/clk/spacemit/ccu-k3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index 03de04144963..764426359540 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -1026,7 +1026,7 @@ CCU_MUX_DIV_GATE_DEFINE(isim_vclk_out3, isim_vclk_parents, APMU_SNR_ISIM_VCLK_CT /* APMU clocks end */ /* DCIU clocks start */ -CCU_GATE_DEFINE(hdma_clk, CCU_PARENT_HW(axi_clk), DCIU_DMASYS_CLK_EN, BIT(0), 0); +CCU_GATE_DEFINE(hdma_clk, CCU_PARENT_HW(axi_clk), DCIU_DMASYS_CLK_EN, BIT(0), CLK_IS_CRITICAL); CCU_GATE_DEFINE(dma350_clk, CCU_PARENT_HW(axi_clk), DCIU_DMASYS_SDMA_CLK_EN, BIT(0), 0); CCU_GATE_DEFINE(c2_tcm_pipe_clk, CCU_PARENT_HW(axi_clk), DCIU_C2_TCM_PIPE_CLK, BIT(0), 0); CCU_GATE_DEFINE(c3_tcm_pipe_clk, CCU_PARENT_HW(axi_clk), DCIU_C3_TCM_PIPE_CLK, BIT(0), 0); From 0aec16a93bac6b2db928119bd34cc1e896173af4 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 22 Jun 2026 12:46:05 +0200 Subject: [PATCH 007/171] Revert "clk: qcom: regmap-phy-mux: Rework the implementation" This reverts commit e108373c54fbc844b7f541c6fd7ecb31772afd3c. This has been reported to break PCIe on at least SM8350 and Eliza platforms. I had originally tested this on Hamoa (X1E) where there were no adverse effects. It's highly likely that this stems from a difference in how the bootloader configures the clocks. Revert the offending change to fix the issue in the immediate, with the intent to revisit it in the upcoming cycle. Reported-by: Dmitry Baryshkov Reported-by: Abel Vesa Signed-off-by: Konrad Dybcio Fixes: e108373c54fb ("clk: qcom: regmap-phy-mux: Rework the implementation") Closes: https://lore.kernel.org/all/c675lcfptr4xgg4hcjp66unmuozgsvgwvtymh7on6jcipjrdw7@jy4h7fkwqwjg/ Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260622-topic-phymux_revert-v1-1-f6ec85523840@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/clk-regmap-phy-mux.c | 58 +++++++++------------------ 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/drivers/clk/qcom/clk-regmap-phy-mux.c b/drivers/clk/qcom/clk-regmap-phy-mux.c index b7d1c69d62f7..7b7243b7107d 100644 --- a/drivers/clk/qcom/clk-regmap-phy-mux.c +++ b/drivers/clk/qcom/clk-regmap-phy-mux.c @@ -15,66 +15,48 @@ #define PHY_MUX_PHY_SRC 0 #define PHY_MUX_REF_SRC 2 -#define XO_RATE 19200000UL - static inline struct clk_regmap_phy_mux *to_clk_regmap_phy_mux(struct clk_regmap *clkr) { return container_of(clkr, struct clk_regmap_phy_mux, clkr); } -static unsigned long phy_mux_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +static int phy_mux_is_enabled(struct clk_hw *hw) { struct clk_regmap *clkr = to_clk_regmap(hw); struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); - u32 val; + unsigned int val; regmap_read(clkr->regmap, phy_mux->reg, &val); + val = FIELD_GET(PHY_MUX_MASK, val); - switch (FIELD_GET(PHY_MUX_MASK, val)) { - case PHY_MUX_PHY_SRC: - return ULONG_MAX; - case PHY_MUX_REF_SRC: - return XO_RATE; - default: - return 0; - } + WARN_ON(val != PHY_MUX_PHY_SRC && val != PHY_MUX_REF_SRC); + + return val == PHY_MUX_PHY_SRC; } -static int phy_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) -{ - if (req->rate == XO_RATE || req->rate == ULONG_MAX) - return 0; - - return -EINVAL; -} - -static int phy_mux_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) +static int phy_mux_enable(struct clk_hw *hw) { struct clk_regmap *clkr = to_clk_regmap(hw); struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); - u32 val; - switch (rate) { - case XO_RATE: - val = PHY_MUX_REF_SRC; - break; - case ULONG_MAX: - val = PHY_MUX_PHY_SRC; - break; - default: - return -EINVAL; - } + return regmap_update_bits(clkr->regmap, phy_mux->reg, + PHY_MUX_MASK, + FIELD_PREP(PHY_MUX_MASK, PHY_MUX_PHY_SRC)); +} + +static void phy_mux_disable(struct clk_hw *hw) +{ + struct clk_regmap *clkr = to_clk_regmap(hw); + struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); regmap_update_bits(clkr->regmap, phy_mux->reg, PHY_MUX_MASK, - FIELD_PREP(PHY_MUX_MASK, val)); - - return 0; + FIELD_PREP(PHY_MUX_MASK, PHY_MUX_REF_SRC)); } const struct clk_ops clk_regmap_phy_mux_ops = { - .recalc_rate = phy_mux_recalc_rate, - .determine_rate = phy_mux_determine_rate, - .set_rate = phy_mux_set_rate, + .enable = phy_mux_enable, + .disable = phy_mux_disable, + .is_enabled = phy_mux_is_enabled, }; EXPORT_SYMBOL_GPL(clk_regmap_phy_mux_ops); From 1aa4e2ed7caafbbbedff89fb226a982413469baf Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 23 Jun 2026 09:30:51 +0200 Subject: [PATCH 008/171] dt-bindings: clock: Replace bouncing emails Replace permanently bouncing email addresses (550 5.1.1 Recipient address rejected) of Adam Skladowski, Chanho Park, Anusha Rao and Sireesh Kodali. There are no new messages from them via other email addresses, so drop them permanently. Add Alim Akhtar to Samsung ExynosAutov9 SoC clocks, because he looks at other Samsung clock hardware and drivers. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260623073050.36262-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/clock/qcom,gcc-msm8953.yaml | 2 -- Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml | 1 - Documentation/devicetree/bindings/clock/qcom,ipq9574-nsscc.yaml | 1 - .../devicetree/bindings/clock/samsung,exynosautov9-clock.yaml | 2 +- 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8953.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8953.yaml index ced3118c8580..bbc38b7a018a 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8953.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8953.yaml @@ -7,8 +7,6 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Qualcomm Global Clock & Reset Controller on MSM8937, MSM8940, MSM8953 and SDM439 maintainers: - - Adam Skladowski - - Sireesh Kodali - Barnabas Czeman description: | diff --git a/Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml index 27ae9938febc..03fe290476be 100644 --- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml @@ -8,7 +8,6 @@ title: Qualcomm Global Clock & Reset Controller on IPQ9574 maintainers: - Bjorn Andersson - - Anusha Rao description: | Qualcomm global clock control module provides the clocks, resets and power diff --git a/Documentation/devicetree/bindings/clock/qcom,ipq9574-nsscc.yaml b/Documentation/devicetree/bindings/clock/qcom,ipq9574-nsscc.yaml index 7ff4ff3587ca..a35bbc7b1924 100644 --- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-nsscc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-nsscc.yaml @@ -8,7 +8,6 @@ title: Qualcomm Networking Sub System Clock & Reset Controller on IPQ9574 and IP maintainers: - Bjorn Andersson - - Anusha Rao description: | Qualcomm networking sub system clock control module provides the clocks, diff --git a/Documentation/devicetree/bindings/clock/samsung,exynosautov9-clock.yaml b/Documentation/devicetree/bindings/clock/samsung,exynosautov9-clock.yaml index 32f39e543b36..21e307d59775 100644 --- a/Documentation/devicetree/bindings/clock/samsung,exynosautov9-clock.yaml +++ b/Documentation/devicetree/bindings/clock/samsung,exynosautov9-clock.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Samsung Exynos Auto v9 SoC clock controller maintainers: - - Chanho Park + - Alim Akhtar - Chanwoo Choi - Krzysztof Kozlowski - Sylwester Nawrocki From cbb0140379de69fa46526672f3003f128f0b4e2e Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 16 Jul 2026 13:42:47 +0200 Subject: [PATCH 009/171] pmdomains: mediatek: Avoid setting RTFF's CLK_DIS before NRESTORE For the PCIE_PHY (also called PEXTP_PHY) type of RTFF hardware, there is special handling setting CLK_DIS before performing the NRESTORE sequence for resetting the RTFF and start sequencing from a clean state. That special handling, though, poses an issue in case the machine specific bootchain (bootloader in particular) ends up booting the kernel with both PCIe MAC and PHY enabled (not just power domains) as doing so will partially corrupt the PCIe MAC/PHY registers in an unpredictable manner, producing either an initialization fail in the PCI-Express drivers, or even a hard lockup! Resolve this by simply removing the special handling: in this case the bootchain, or remote processors, setting is getting honored by the later check for PWR_RTFF_SAVE_FLAG presence in the RTFF ctl register. Fixes: 9d02c94342b3 ("pmdomain: mediatek: Add support for RTFF Hardware in MT8196/MT6991") Signed-off-by: AngeloGioacchino Del Regno Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mtk-pm-domains.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index f0a6339affd7..881a1a3b3632 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -486,16 +486,8 @@ static int scpsys_ctl_pwrseq_on(struct scpsys_domain *pd) if (ret < 0) return ret; - if (pd->data->rtff_type == SCPSYS_RTFF_TYPE_PCIE_PHY) - regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_CLK_DIS); - regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_CLK_DIS_BIT); regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ISO_BIT); - - /* Wait for RTFF HW to sync buck isolation state if this is PCIe PHY RTFF */ - if (pd->data->rtff_type == SCPSYS_RTFF_TYPE_PCIE_PHY) - udelay(5); - regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT); /* From 3e013bc8b941bd52c8e3a99798d0ae8792cb71ca Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 17:20:38 +0800 Subject: [PATCH 010/171] pmdomain: mediatek: fix remaining %pOF after of_node_put() scpsys_get_bus_protection_legacy() looks up several legacy bus protection regmaps from device-tree nodes. Two error paths put the device node before checking whether the regmap lookup failed, but still pass that node to dev_err_probe() with %pOF on failure. If of_node_put() drops the last reference, the later %pOF formatting can dereference a freed device node. Keep the node reference until after the error message has been emitted in the infracfg and SMI lookup paths. Also drop the SMI node before returning when the SMI phandle is missing. Fixes: c29345fa5f66 ("pmdomain: mediatek: Refactor bus protection regmaps retrieval") Signed-off-by: Guangshuo Li Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mtk-pm-domains.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index 881a1a3b3632..6089c8cf5bbc 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -1050,12 +1050,15 @@ static int scpsys_get_bus_protection_legacy(struct device *dev, struct scpsys *s node = of_find_node_with_property(np, "mediatek,infracfg"); if (node) { regmap[0] = syscon_regmap_lookup_by_phandle(node, "mediatek,infracfg"); - of_node_put(node); num_regmaps++; - if (IS_ERR(regmap[0])) - return dev_err_probe(dev, PTR_ERR(regmap[0]), + if (IS_ERR(regmap[0])) { + ret = dev_err_probe(dev, PTR_ERR(regmap[0]), "%pOF: failed to get infracfg regmap\n", node); + of_node_put(node); + return ret; + } + of_node_put(node); } else { regmap[0] = NULL; } @@ -1064,17 +1067,22 @@ static int scpsys_get_bus_protection_legacy(struct device *dev, struct scpsys *s node = of_find_node_with_property(np, "mediatek,smi"); if (node) { smi_np = of_parse_phandle(node, "mediatek,smi", 0); - of_node_put(node); - if (!smi_np) + if (!smi_np) { + of_node_put(node); return -ENODEV; + } regmap[1] = device_node_to_regmap(smi_np); num_regmaps++; of_node_put(smi_np); - if (IS_ERR(regmap[1])) - return dev_err_probe(dev, PTR_ERR(regmap[1]), + if (IS_ERR(regmap[1])) { + ret = dev_err_probe(dev, PTR_ERR(regmap[1]), "%pOF: failed to get SMI regmap\n", node); + of_node_put(node); + return ret; + } + of_node_put(node); } else { regmap[1] = NULL; } From 90b5df5bf0a1acc811c6ecfe66c503e8268fdf96 Mon Sep 17 00:00:00 2001 From: Esteban Urrutia Date: Thu, 23 Jul 2026 10:53:52 -0400 Subject: [PATCH 011/171] regulator: dt-bindings: qcom,rpmh: Add support for PM8350B The PM8350B has only one LDO, which gets its power from a dedicated input. Add support for it. Signed-off-by: Esteban Urrutia Link: https://patch.msgid.link/20260723-pm8350b-ldo-v1-1-42b5428a0d3f@proton.me Signed-off-by: Mark Brown --- .../bindings/regulator/qcom,rpmh-regulator.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml index d93594304651..eed2ce7fa861 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml @@ -47,6 +47,7 @@ description: | For PM8150, smps1 - smps10, ldo1 - ldo18 For PM8150L, smps1 - smps8, ldo1 - ldo11, bob, flash, rgb For PM8350, smps1 - smps12, ldo1 - ldo10 + For PM8350B, ldo1 For PM8350C, smps1 - smps10, ldo1 - ldo13, bob For PM8450, smps1 - smps6, ldo1 - ldo4 For PM8550, smps1 - smps6, ldo1 - ldo17, bob1 - bob2 @@ -82,6 +83,7 @@ properties: - qcom,pm8150-rpmh-regulators - qcom,pm8150l-rpmh-regulators - qcom,pm8350-rpmh-regulators + - qcom,pm8350b-rpmh-regulators - qcom,pm8350c-rpmh-regulators - qcom,pm8450-rpmh-regulators - qcom,pm8550-rpmh-regulators @@ -337,6 +339,15 @@ allOf: patternProperties: "^vdd-s([1-9]|1[0-2])-supply$": true + - if: + properties: + compatible: + enum: + - qcom,pm8350b-rpmh-regulators + then: + properties: + vdd-l1-supply: true + - if: properties: compatible: From 6753251cfc9622a90feedc0c0883e3b0a6fcde8a Mon Sep 17 00:00:00 2001 From: Esteban Urrutia Date: Thu, 23 Jul 2026 10:53:53 -0400 Subject: [PATCH 012/171] regulator: qcom-rpmh: Add support for PM8350B The PM8350B has only one LDO, which gets its power from a dedicated input. Add support for it. Signed-off-by: Esteban Urrutia Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260723-pm8350b-ldo-v1-2-42b5428a0d3f@proton.me Signed-off-by: Mark Brown --- drivers/regulator/qcom-rpmh-regulator.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 756a4201225e..73856e3df337 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -1269,6 +1269,11 @@ static const struct rpmh_vreg_init_data pm8350_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pm8350b_vreg_data[] = { + RPMH_VREG("ldo1", LDO, 1, &pmic5_nldo, "vdd-l1"), + {} +}; + static const struct rpmh_vreg_init_data pm8350c_vreg_data[] = { RPMH_VREG("smps1", SMPS, 1, &pmic5_hfsmps515, "vdd-s1"), RPMH_VREG("smps2", SMPS, 2, &pmic5_ftsmps510, "vdd-s2"), @@ -1868,6 +1873,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pm8350-rpmh-regulators", .data = pm8350_vreg_data, }, + { + .compatible = "qcom,pm8350b-rpmh-regulators", + .data = pm8350b_vreg_data, + }, { .compatible = "qcom,pm8350c-rpmh-regulators", .data = pm8350c_vreg_data, From a7c28483fd57dd0e1487024af70622315320774b Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 28 Jul 2026 16:50:59 +0100 Subject: [PATCH 013/171] arm64: tegra: Add EL2 virtual timer interrupt for Tegra194 Commit d87773de9efe ("clocksource/drivers/arm_arch_timer: Default to EL2 virtual timer when running VHE") updated the ARM arch timer driver to use the virtual timer by default if the CPU is running at EL2 with VHE enabled. If the CPU is running at EL2 with VHE enabled but there is no interrupt provided for the virtual timer, then the following warning is displayed: arch_timer: [Firmware Bug]: VHE-capable CPU without EL2 virtual timer interrupt This warning is observed on Tegra194 platforms. Tegra194 SoC includes NVIDIA Carmel ARM v8.2 CPUs and support an EL2 virtual timer. Fix the above warning by adding the PPI for the EL2 virtual timer interrupt for Tegra194. Fixes: 5425fb15d8ee ("arm64: tegra: Add Tegra194 chip device tree") Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- arch/arm64/boot/dts/nvidia/tegra194.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi b/arch/arm64/boot/dts/nvidia/tegra194.dtsi index eb7e3c666df6..5e510f77b538 100644 --- a/arch/arm64/boot/dts/nvidia/tegra194.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra194.dtsi @@ -3176,6 +3176,8 @@ timer { , , + ; interrupt-parent = <&gic>; always-on; From 438f4896f78f69ec73d5f32d2c024193f1223569 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 20 Jul 2026 11:34:21 +1000 Subject: [PATCH 014/171] crypto: ccm - Set rfc4309 maxauthsize from child Set the maxauthsize of rfc4309 using that of the child algorithm. Fixes: 4a49b499dfa0 ("[CRYPTO] ccm: Added CCM mode") Reported-by: Seohyeon Maeng Signed-off-by: Herbert Xu --- crypto/ccm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/ccm.c b/crypto/ccm.c index 2ae929ffdef8..916441e4f2b8 100644 --- a/crypto/ccm.c +++ b/crypto/ccm.c @@ -747,7 +747,7 @@ static int crypto_rfc4309_create(struct crypto_template *tmpl, inst->alg.ivsize = 8; inst->alg.chunksize = crypto_aead_alg_chunksize(alg); - inst->alg.maxauthsize = 16; + inst->alg.maxauthsize = crypto_aead_alg_maxauthsize(alg); inst->alg.base.cra_ctxsize = sizeof(struct crypto_rfc4309_ctx); From 360f2974fcea49c61f6d6f81554741a9eeee7168 Mon Sep 17 00:00:00 2001 From: Vladislav Dronov Date: Tue, 21 Jul 2026 00:33:31 +0200 Subject: [PATCH 015/171] crypto: tegra - fix rctx->cryptlen calculation in tegra_gcm_do_one_req() Perform rctx->cryptlen calculation in tegra_gcm_do_one_req() the same way it is done in tegra_ccm_crypt_init(). The current formulae may lead to a crash if a caller does not call tegra_gcm_setauthsize() and so ctx->authsize remains zero. Then a decrypt operation with incorrect rctx->cryptlen will lead to a write beyound rctx->dst_sg buffer. As a follow-up cleanup delete struct tegra_aead_ctx->authsize field since it appears to be completely unused. Also simplify tegra_ccm_setauthsize() and tegra_gcm_setauthsize() functions respectively. Fixes: 0880bb3b00c8 ("crypto: tegra - Add Tegra Security Engine driver") Signed-off-by: Vladislav Dronov Signed-off-by: Herbert Xu --- drivers/crypto/tegra/tegra-se-aes.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/drivers/crypto/tegra/tegra-se-aes.c b/drivers/crypto/tegra/tegra-se-aes.c index 9094c03e991f..0fd1d7035899 100644 --- a/drivers/crypto/tegra/tegra-se-aes.c +++ b/drivers/crypto/tegra/tegra-se-aes.c @@ -45,7 +45,6 @@ struct tegra_aes_reqctx { struct tegra_aead_ctx { struct tegra_se *se; - unsigned int authsize; u32 alg; u32 key_id; u32 keylen; @@ -1290,7 +1289,7 @@ static int tegra_gcm_do_one_req(struct crypto_engine *engine, void *areq) if (rctx->encrypt) rctx->cryptlen = req->cryptlen; else - rctx->cryptlen = req->cryptlen - ctx->authsize; + rctx->cryptlen = req->cryptlen - rctx->authsize; memcpy(rctx->iv, req->iv, GCM_AES_IV_SIZE); rctx->iv[3] = (1 << 24); @@ -1394,8 +1393,6 @@ static int tegra_aead_cra_init(struct crypto_aead *tfm) static int tegra_ccm_setauthsize(struct crypto_aead *tfm, unsigned int authsize) { - struct tegra_aead_ctx *ctx = crypto_aead_ctx(tfm); - switch (authsize) { case 4: case 6: @@ -1404,28 +1401,15 @@ static int tegra_ccm_setauthsize(struct crypto_aead *tfm, unsigned int authsize case 12: case 14: case 16: - break; + return 0; default: return -EINVAL; } - - ctx->authsize = authsize; - - return 0; } static int tegra_gcm_setauthsize(struct crypto_aead *tfm, unsigned int authsize) { - struct tegra_aead_ctx *ctx = crypto_aead_ctx(tfm); - int ret; - - ret = crypto_gcm_check_authsize(authsize); - if (ret) - return ret; - - ctx->authsize = authsize; - - return 0; + return crypto_gcm_check_authsize(authsize); } static void tegra_aead_cra_exit(struct crypto_aead *tfm) From 1132c38927886259c694f77cd74fc577c2216eeb Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 22 Jul 2026 12:27:48 +0200 Subject: [PATCH 016/171] rhashtable: fix false-positive lockdep splat on rhltable destruction Blamed commit removed ht->mutex usage during destruction but forgot to switch rhashtable_free_one() to rcu_dereference_raw(), this triggers a lockdep splat when an rhltable gets zapped. Fixes: 09ae540e1d5c ("rhashtable: drop ht->mutex in rhashtable_free_and_destroy()") Signed-off-by: Florian Westphal Reviewed-by: Mikhail Gavrilov Signed-off-by: Herbert Xu --- lib/rhashtable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rhashtable.c b/lib/rhashtable.c index d459bef245f4..8b2c405e7a66 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -1261,7 +1261,7 @@ static void rhashtable_free_one(struct rhashtable *ht, struct rhash_head *obj, list = container_of(obj, struct rhlist_head, rhead); do { obj = &list->rhead; - list = rht_dereference(list->next, ht); + list = rcu_dereference_raw(list->next); free_fn(rht_obj(ht, obj), arg); } while (list); } From 9c75402286409f5e1a75e4a445555c84066f89db Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Fri, 24 Jul 2026 10:15:38 +0200 Subject: [PATCH 017/171] crypto: qce - fix error path in devm_qce_register_algs If ops->register_algs() fails, the error path repeatedly calls the same ops->unregister_algs() from the failed registration. Use the loop index to unregister the previously registered algorithms instead. Fixes: e80cf84b6087 ("crypto: qce - unregister previously registered algos in error path") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Reviewed-by: Bartosz Golaszewski Signed-off-by: Herbert Xu --- drivers/crypto/qce/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/qce/core.c b/drivers/crypto/qce/core.c index b966f3365b7d..7f005d1fca40 100644 --- a/drivers/crypto/qce/core.c +++ b/drivers/crypto/qce/core.c @@ -59,7 +59,7 @@ static int devm_qce_register_algs(struct qce_device *qce) ret = ops->register_algs(qce); if (ret) { for (j = i - 1; j >= 0; j--) - ops->unregister_algs(qce); + qce_ops[j]->unregister_algs(qce); return ret; } } From 6b36f13891ab4709b7d60023005176cdd5c368cf Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 25 Jul 2026 11:06:10 +0200 Subject: [PATCH 018/171] crypto: starfive - use scatterlist length before DMA mapping Using sg_dma_len() is only valid after mapping a scatterlist with dma_map_sg(). However, starfive_aes_aead_do_one_req() uses it before mapping the scatterlist. Use the original scatterlist length because the DMA length has not been populated yet when CONFIG_NEED_SG_DMA_LENGTH=y. Fixes: 7467147ef9bf ("crypto: starfive - Use dma for aes requests") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Signed-off-by: Herbert Xu --- drivers/crypto/starfive/jh7110-aes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/starfive/jh7110-aes.c b/drivers/crypto/starfive/jh7110-aes.c index a0713aa21250..f59adb2a5651 100644 --- a/drivers/crypto/starfive/jh7110-aes.c +++ b/drivers/crypto/starfive/jh7110-aes.c @@ -677,7 +677,7 @@ static int starfive_aes_aead_do_one_req(struct crypto_engine *engine, void *areq if (cryp->total_in) sg_zero_buffer(rctx->in_sg, sg_nents(rctx->in_sg), - sg_dma_len(rctx->in_sg) - cryp->total_in, + rctx->in_sg->length - cryp->total_in, cryp->total_in); ctx->rctx = rctx; From 285d8204638cf8be0dc304dc40f0290ada701340 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Sun, 26 Jul 2026 21:27:16 +0200 Subject: [PATCH 019/171] crypto: af_alg - Allow cbc(paes) Commit 7524070f26d8 ("crypto: af_alg - Drop support for off-CPU cryptography") breaks a special use case. The cbc-paes-caam driver implements the algorithm cbc(paes), it offers a way to use AES in CBC mode with key material unknown to userspace. Instead of an AES key a CAAM BLOB is passed to the kernel. So, this crypto operation cannot be implemented in a userspace library and needs always help from the kernel. Explicitly allow this use case. Cc: Demi Marie Obenour Suggested-by: Eric Biggers Fixes: 7524070f26d8 ("crypto: af_alg - Drop support for off-CPU cryptography") Signed-off-by: Richard Weinberger Reviewed-by: Eric Biggers Signed-off-by: Herbert Xu --- crypto/algif_skcipher.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index df20bdfe1f1f..035fed7db81f 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -32,6 +32,7 @@ #include #include #include +#include #include static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg, @@ -309,7 +310,12 @@ static struct proto_ops algif_skcipher_ops_nokey = { static void *skcipher_bind(const char *name) { - return crypto_alloc_skcipher(name, 0, AF_ALG_CRYPTOAPI_MASK); + u32 mask = AF_ALG_CRYPTOAPI_MASK; + + if (strcmp(name, "cbc(paes)") == 0) + mask = 0; + + return crypto_alloc_skcipher(name, 0, mask); } static void skcipher_release(void *private) From d761c7e38a000603a9d16270a1af770a0e8efb5e Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Wed, 29 Jul 2026 11:47:20 +0200 Subject: [PATCH 020/171] gpiolib: Check gc->get_direction() before calling gpiod_get_direction() According to 'struct gpio_chip' documentation in linux/gpio/driver.h, implementing .get_direction() is recommended but not mandatory. Most places verify that gc->get_direction() exists before calling gpiod_get_direction(), but gpiolib_dbg_show() doesn't. Until commit 471e998c0e31 ("gpiolib: remove redundant callback check") it was also verified by gpiod_get_direction() itself so calling it at all time from gpiolib_dbg_show() was not an issue. But after the check in gpiod_get_direction() has been removed, calling it inconditionaly leads to a big fat warning in gpiochip_get_direction(). In gpiod_get_direction(), verify that gc->get_direction() exists before calling gpiod_get_direction(). Fixes: 471e998c0e31 ("gpiolib: remove redundant callback check") Signed-off-by: Christophe Leroy (CS GROUP) Link: https://patch.msgid.link/ad89f92f91d004e63dd5599bb58e9581f373a601.1785318183.git.chleroy@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c433a095907f..ef8ccaf17c9c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -5420,7 +5420,8 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *gc) flags = READ_ONCE(desc->flags); is_irq = test_bit(GPIOD_FLAG_USED_AS_IRQ, &flags); if (is_irq || test_bit(GPIOD_FLAG_REQUESTED, &flags)) { - gpiod_get_direction(desc); + if (gc->get_direction) + gpiod_get_direction(desc); is_out = test_bit(GPIOD_FLAG_IS_OUT, &flags); value = gpio_chip_get_value(gc, desc); active_low = test_bit(GPIOD_FLAG_ACTIVE_LOW, &flags); From 66694b5f90f3876fccb87bbd02b453cdc33b3ae4 Mon Sep 17 00:00:00 2001 From: Robby Cai Date: Fri, 24 Jul 2026 18:34:40 +0800 Subject: [PATCH 021/171] regulator: fp9931: Fix VPOS/VNEG voltage selector table The VPOSNEG_table[] mapping does not match the FP9931 datasheet. The datasheet defines the VPOS/VNEG voltage mapping as: 00h-04h -> 7.04V (-7.04V) 05h -> 7.26V (-7.26V) 06h -> 7.49V (-7.49V) ... 28h-3Fh -> 15.06V (-15.06V) However, VPOSNEG_table[] has two issues: 1. Selector 0x00~0x04 should all map to 7.04V (5 entries), but the table has 6 entries of 7.04V, causing all subsequent entries to be shifted by one position. 2. Selectors 0x29~0x3F should all clamp to 15.06V (23 entries), but the table has only 41 entries. Any selector value above 0x28 would result in an out-of-bounds table access. Fix both issues by removing the duplicate 7.04V entry and appending the missing 23 clamped 15.06V entries, bringing the table to the correct size of 64 entries (0x00~0x3F). Fixes: 12d821bd13d4 ("regulator: Add FP9931/JD9930 driver") Signed-off-by: Robby Cai Link: https://patch.msgid.link/20260724103441.800522-2-robby.cai@oss.nxp.com Signed-off-by: Mark Brown --- drivers/regulator/fp9931.c | 54 +++++++++----------------------------- 1 file changed, 12 insertions(+), 42 deletions(-) diff --git a/drivers/regulator/fp9931.c b/drivers/regulator/fp9931.c index 002b41f53eff..ff743a8b0dfe 100644 --- a/drivers/regulator/fp9931.c +++ b/drivers/regulator/fp9931.c @@ -37,48 +37,18 @@ struct fp9931_data { }; static const unsigned int VPOSNEG_table[] = { - 7040000, - 7040000, - 7040000, - 7040000, - 7040000, - 7040000, - 7260000, - 7490000, - 7710000, - 7930000, - 8150000, - 8380000, - 8600000, - 8820000, - 9040000, - 9270000, - 9490000, - 9710000, - 9940000, - 10160000, - 10380000, - 10600000, - 10830000, - 11050000, - 11270000, - 11490000, - 11720000, - 11940000, - 12160000, - 12380000, - 12610000, - 12830000, - 13050000, - 13280000, - 13500000, - 13720000, - 13940000, - 14170000, - 14390000, - 14610000, - 14830000, - 15060000, + 7040000, 7040000, 7040000, 7040000, 7040000, /* 00h-04h */ + 7260000, 7490000, 7710000, 7930000, 8150000, 8380000, /* 05h-0Ah */ + 8600000, 8820000, 9040000, 9270000, 9490000, 9710000, /* 0Bh-10h */ + 9940000, 10160000, 10380000, 10600000, 10830000, 11050000, /* 11h-16h */ + 11270000, 11490000, 11720000, 11940000, 12160000, 12380000, /* 17h-1Ch */ + 12610000, 12830000, 13050000, 13280000, 13500000, 13720000, /* 1Dh-22h */ + 13940000, 14170000, 14390000, 14610000, 14830000, 15060000, /* 23h-28h */ + /* 29h-3Fh: clamped to 15.06V per datasheet */ + 15060000, 15060000, 15060000, 15060000, 15060000, 15060000, + 15060000, 15060000, 15060000, 15060000, 15060000, 15060000, + 15060000, 15060000, 15060000, 15060000, 15060000, 15060000, + 15060000, 15060000, 15060000, 15060000, 15060000, }; static const struct hwmon_channel_info *fp9931_info[] = { From ae31bcc92bb42502bb7c9029e6dc7a824cf6cd14 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Mon, 27 Jul 2026 18:38:49 +0800 Subject: [PATCH 022/171] mmc: sdhci: make tuning_err a signed int Coverity report INTEGER_OVERFLOW for host->tuning_err. The tuning_err field in struct sdhci_host is used to store an error code for re-tuning, but it was declared as unsigned int. Several call sites store negative error codes into it and later compare against negative values: - sdhci.c, sdhci-of-dwcmshc.c and sdhci-pci-gli.c assign it the return value of __sdhci_execute_tuning()/__sdhci_execute_tuning_9750(), both of which return a signed int (possibly a negative errno); - sdhci-of-esdhc.c assigns host->tuning_err = -EAGAIN and later does "ret = host->tuning_err; if (ret == -EAGAIN ...)"; - sdhci-of-dwcmshc.c prints it with the %d (signed) conversion. Storing a negative errno in an unsigned int and reading it back as a signed int only happens to work because of two's-complement, same-width integer conversions. It is misleading and triggers sign-conversion warnings. All users treat the value either as a signed error code or as a boolean (zero / non-zero), so changing the type to a signed int is safe and makes the intent explicit. Fixes: 7d8bb1f46e13 ("mmc: sdhci: add tuning error codes") Assisted-by: Cline:claude-sonnet [read_file, search_files, git] Signed-off-by: Haibo Chen Reviewed-by: Frank Li Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index b6a571d866fa..f0fd81409a86 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -659,7 +659,7 @@ struct sdhci_host { unsigned int tuning_count; /* Timer count for re-tuning */ unsigned int tuning_mode; /* Re-tuning mode supported by host */ - unsigned int tuning_err; /* Error code for re-tuning */ + int tuning_err; /* Error code for re-tuning */ #define SDHCI_TUNING_MODE_1 0 #define SDHCI_TUNING_MODE_2 1 #define SDHCI_TUNING_MODE_3 2 From 9e9f561269dff35e6f84ed21776ec37fd6360b03 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Mon, 27 Jul 2026 23:03:22 +0900 Subject: [PATCH 023/171] mmc: sdhci: unmap the bounce buffer before device release sdhci_allocate_bounce_buffer() allocates its buffer with devm_kmalloc() but maps it with dma_map_single(). The buffer is therefore released by devres without the streaming DMA mapping being unmapped. Register a managed action after dma_map_single() succeeds so the mapping is removed before devres releases the buffer. The action is registered only for buffers allocated and mapped by the SDHCI core, leaving buffers provided by host drivers under their existing ownership. Fixes: bd9b902798ab ("mmc: sdhci: Implement an SDHCI-specific bounce buffer") Cc: stable@vger.kernel.org Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e3bf901b10aa..efb4c7742fe2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -4187,6 +4187,14 @@ void __sdhci_read_caps(struct sdhci_host *host, const u16 *ver, } EXPORT_SYMBOL_GPL(__sdhci_read_caps); +static void sdhci_unmap_bounce_buffer(void *data) +{ + struct sdhci_host *host = data; + + dma_unmap_single(mmc_dev(host->mmc), host->bounce_addr, + host->bounce_buffer_size, DMA_BIDIRECTIONAL); +} + static void sdhci_allocate_bounce_buffer(struct sdhci_host *host) { struct mmc_host *mmc = host->mmc; @@ -4247,6 +4255,14 @@ static void sdhci_allocate_bounce_buffer(struct sdhci_host *host) } host->bounce_buffer_size = bounce_size; + ret = devm_add_action_or_reset(mmc_dev(mmc), + sdhci_unmap_bounce_buffer, host); + if (ret) { + devm_kfree(mmc_dev(mmc), host->bounce_buffer); + host->bounce_buffer = NULL; + host->bounce_buffer_size = 0; + return; + } out: /* Lie about this since we're bouncing */ From c125ee35a49a0518521b52b27631eef061b8719a Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Mon, 3 Aug 2026 17:48:21 +0800 Subject: [PATCH 024/171] mmc: atmel-mci: Fix use-after-free in atmci_remove due to race condition In atmci_probe, &host->bh_work is bound with atmci_work_func, and atmci_interrupt, atmci_timeout_timer and atmci_dma_complete can all queue this work on system_bh_wq. If we remove the module, atmci_remove makes cleanup and the memory allocated for host with devm_kzalloc() is released after the remove callback returns, while the work mentioned above may still be pending or running. The sequence of operations that may lead to a UAF bug is as follows: CPU0 CPU1 | atmci_interrupt | queue_work(system_bh_wq, | &host->bh_work) atmci_remove | atmci_cleanup_slot(...) | atmci_writel(host, ATMCI_IDR, ~0UL) | timer_delete_sync(&host->timer) | dma_release_channel(host->dma.chan) | free_irq(platform_get_irq(pdev, 0), host) | | atmci_work_func | // use host // devm resources released after | // remove returns, host is freed | | // use host (use-after-free) Fix it by canceling the work after all the sources that can schedule it (IRQ handler, timeout timer and DMA completion callback) have been stopped, and before proceeding with the remaining cleanup in atmci_remove. Fixes: 7d2be0749a59 ("atmel-mci: Driver for Atmel on-chip MMC controllers") Assisted-by: Codex:deepseek-v4-flash Signed-off-by: Pei Xiao Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/atmel-mci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 3b4928f5b9b2..8f4df250a77a 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -2610,6 +2610,8 @@ static void atmci_remove(struct platform_device *pdev) free_irq(platform_get_irq(pdev, 0), host); + cancel_work_sync(&host->bh_work); + clk_disable_unprepare(host->mck); pm_runtime_disable(dev); From f64ea900e4bda3055ef24a2c906f8d049cf1c3bd Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Tue, 4 Aug 2026 10:25:00 +0800 Subject: [PATCH 025/171] mmc: omap_hsmmc: fix busy_timeout overflow in ns conversion on 32-bit omap_hsmmc_prepare_data() converts the command busy timeout to nanoseconds with: timeout = req->cmd->busy_timeout * NSEC_PER_MSEC; busy_timeout is an unsigned int (milliseconds) and timeout is a u64, but NSEC_PER_MSEC is 1000000L. On 32-bit builds the multiplication is performed in 32-bit arithmetic and wraps for busy_timeout values above ~4294 ms, before the result is assigned to the u64. The driver does not set mmc->max_busy_timeout, so the core does not cap the busy timeout, and commands such as erase or SANITIZE (MMC_SANITIZE_TIMEOUT_MS is 240000 ms) can pass a busy_timeout far larger than 4294 ms. The wrapped, much smaller ns value is then programmed via set_data_timeout(), so the data timeout is set too short and the operation can time out prematurely. Cast busy_timeout to u64 before the multiplication so the conversion is done in 64-bit arithmetic. Fixes: 8cc9a3e73de1 ("mmc: host: omap_hsmmc: use generic_cmd6_time to program timeout value for CMD6") Cc: stable@vger.kernel.org Signed-off-by: Zhan Xusheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 58c881f2725b..3356ac5a1fa0 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1357,7 +1357,7 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req) if (req->data == NULL) { OMAP_HSMMC_WRITE(host->base, BLK, 0); if (req->cmd->flags & MMC_RSP_BUSY) { - timeout = req->cmd->busy_timeout * NSEC_PER_MSEC; + timeout = (u64)req->cmd->busy_timeout * NSEC_PER_MSEC; /* * Set an arbitrary 100ms data timeout for commands with From 00179ed9fbe07799676e2cb63c4e7f0e7cd80a5c Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Tue, 4 Aug 2026 14:12:00 +0800 Subject: [PATCH 026/171] mmc: loongson2: Fix sg iteration in data reorder functions In ls2k0500_mmc_reorder_cmd_data() and ls2k2000_mmc_reorder_cmd_data(), the for_each_sg() macro already iterates over the scatterlist entries, with 'sg' pointing to the current entry. However, the code incorrectly uses '&sg[i]' and 'sg_dma_len(&sg[i])' inside the loop, which treats 'sg' as an array base and indexes it again, leading to access of wrong sg entries (or out-of-bounds if the list is not an array). Cc: stable@vger.kernel.org Fixes: d0f8e961deae ("mmc: loongson2: Add Loongson-2K2000 SD/SDIO/eMMC controller driver") Fixes: 2115772014bd ("mmc: loongson2: Add Loongson-2K SD/SDIO controller driver") Signed-off-by: Binbin Zhou Signed-off-by: Ulf Hansson --- drivers/mmc/host/loongson2-mmc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/loongson2-mmc.c b/drivers/mmc/host/loongson2-mmc.c index f553e92fd9e5..118eccbaf8cd 100644 --- a/drivers/mmc/host/loongson2-mmc.c +++ b/drivers/mmc/host/loongson2-mmc.c @@ -641,8 +641,8 @@ static void ls2k0500_mmc_reorder_cmd_data(struct loongson2_mmc_host *host, return; for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) { - data = sg_virt(&sg[i]); - for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++) + data = sg_virt(sg); + for (j = 0; j < (sg_dma_len(sg) / 4); j++) if (cmd->opcode == SD_SWITCH) data[j] = bitrev8x4(data[j]); else @@ -758,8 +758,8 @@ static void ls2k2000_mmc_reorder_cmd_data(struct loongson2_mmc_host *host, return; for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) { - data = sg_virt(&sg[i]); - for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++) + data = sg_virt(sg); + for (j = 0; j < (sg_dma_len(sg) / 4); j++) data[j] = bitrev8x4(data[j]); } } From 818d56ac1d08b68a4c42d0326786f030cc640722 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 29 Jul 2026 15:59:07 +0300 Subject: [PATCH 027/171] pmdomain: mediatek: Fix mt8183 hang on boot Depending on firmware, part of the MFG domains may be partially left ON at boot time with kernel detecting PD as ON, while it's OFF. Some of MFG cores may be left powered after bootloader, to let the ACP to prefetch the GPU region when the display controller is brought up for a continuous splash animation performed by downstream stack. This doesn't play well with an eventual delay in probing upstream Panfrost driver when the display controller is fully set up, as that would make genpd's sync_state() to power off the domain while ACP tries to prefetch: this is causing an AXI stall, effectively freezing the AP indefinitely. In order to prevent trouble from happening, the sync_state() functionality must be obliterated on all of the MFG domains: while this guarantees a power leakage if the bootloader boots the kernel with MFG PDs partially powered on, this is the only way to ensure stable operation of the SoC during boot on devices with such firmware because, of course, those will never officially receive a firmware update. Fixes Kappa Chromebook hanging during system boot. Fixes: 0e789b491ba0 ("pmdomain: core: Leave powered-on genpds on until sync_state") Fixes: 13a4b7fb6260 ("pmdomain: core: Leave powered-on genpds on until late_initcall_sync") Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Dmitry Osipenko Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mt8183-pm-domains.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/mediatek/mt8183-pm-domains.h b/drivers/pmdomain/mediatek/mt8183-pm-domains.h index 3742782a2702..5e33b8628e85 100644 --- a/drivers/pmdomain/mediatek/mt8183-pm-domains.h +++ b/drivers/pmdomain/mediatek/mt8183-pm-domains.h @@ -47,7 +47,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = { .pwr_sta2nd_offs = 0x0184, .sram_pdn_bits = 0, .sram_pdn_ack_bits = 0, - .caps = MTK_SCPD_DOMAIN_SUPPLY, + .caps = MTK_SCPD_DOMAIN_SUPPLY | MTK_SCPD_KEEP_DEFAULT_OFF, }, [MT8183_POWER_DOMAIN_MFG] = { .name = "mfg", @@ -57,7 +57,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = { .pwr_sta2nd_offs = 0x0184, .sram_pdn_bits = GENMASK(8, 8), .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_DOMAIN_SUPPLY, + .caps = MTK_SCPD_DOMAIN_SUPPLY | MTK_SCPD_KEEP_DEFAULT_OFF, }, [MT8183_POWER_DOMAIN_MFG_CORE0] = { .name = "mfg_core0", @@ -67,6 +67,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = { .pwr_sta2nd_offs = 0x0184, .sram_pdn_bits = GENMASK(8, 8), .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, }, [MT8183_POWER_DOMAIN_MFG_CORE1] = { .name = "mfg_core1", @@ -76,6 +77,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = { .pwr_sta2nd_offs = 0x0184, .sram_pdn_bits = GENMASK(8, 8), .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, }, [MT8183_POWER_DOMAIN_MFG_2D] = { .name = "mfg_2d", @@ -85,6 +87,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = { .pwr_sta2nd_offs = 0x0184, .sram_pdn_bits = GENMASK(8, 8), .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, .bp_cfg = { BUS_PROT_WR(INFRA, MT8183_TOP_AXI_PROT_EN_1_MFG, From 9ed3d974a26644ad57b3d4d067e279188b2038b6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 5 Aug 2026 18:51:00 +0100 Subject: [PATCH 028/171] regcache: Use a consistent sort for defaults table When we look up registers in the defaults table we use a binary search, and we have a regcache_sort_defaults() API to help drivers that constuct their defaults tables on the fly. Unfortunately the lookup and the sort don't use the same comparison function, and to make matters worse the comparison function used during lookups is written for signed register numbers rather than the unsigned ones we actually have so can produce suprising results when some of the addresses have the top bit set. Standardise on the more explicitly coded function to ensure consistent results. Reviewed-by: Peter Ujfalusi Reviewed-by: Charles Keepax Tested-by: Charles Keepax Link: https://patch.msgid.link/20260805-regmap-regcache-sort-v1-1-162186aad8b9@kernel.org Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index aa8f2efed779..480bc76f9a02 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -727,14 +727,6 @@ unsigned int regcache_get_val(struct regmap *map, const void *base, return -1; } -static int regcache_default_cmp(const void *a, const void *b) -{ - const struct reg_default *_a = a; - const struct reg_default *_b = b; - - return _a->reg - _b->reg; -} - int regcache_lookup_reg(struct regmap *map, unsigned int reg) { struct reg_default key; @@ -744,7 +736,7 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg) key.def = 0; r = bsearch(&key, map->reg_defaults, map->num_reg_defaults, - sizeof(struct reg_default), regcache_default_cmp); + sizeof(struct reg_default), regcache_defaults_cmp); if (r) return r - map->reg_defaults; From 4b05ccb17f92268384d483221a577fccfc291c7a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 16:22:50 +0300 Subject: [PATCH 029/171] regcache: Sort the local copy of an unsorted reg_defaults array regcache_lookup_reg() bsearch()es the reg_defaults array, which requires it to be sorted by ascending register address. Entries following a descending step are never found, so regcache_reg_needs_sync() reports that they need a sync and they are written to the device on every regcache_sync() even when they were never touched. Detect the misordering while reg_defaults is validated against the register stride and sort the local copy. The check needs no new loop and sort() only runs for the affected drivers, which are also warned about. Note that sort() is not stable, so for arrays with duplicated register addresses it remains unspecified which entry is found. Signed-off-by: Peter Ujfalusi Reviewed-by: Charles Keepax Tested-by: Charles Keepax Link: https://patch.msgid.link/20260805132250.2637-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 480bc76f9a02..623db7a1f3bd 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -123,6 +123,8 @@ static void regcache_hw_exit(struct regmap *map) int regcache_init(struct regmap *map, const struct regmap_config *config) { + bool sort_defaults = false; + unsigned int reg_prev = 0; int count = 0; int ret; int i; @@ -149,10 +151,16 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) return -EINVAL; } - for (i = 0; i < config->num_reg_defaults; i++) + for (i = 0; i < config->num_reg_defaults; i++) { if (config->reg_defaults[i].reg % map->reg_stride) return -EINVAL; + if (reg_prev > config->reg_defaults[i].reg) + sort_defaults = true; + + reg_prev = config->reg_defaults[i].reg; + } + for (i = 0; i < ARRAY_SIZE(cache_types); i++) if (cache_types[i]->type == map->cache_type) break; @@ -186,6 +194,13 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) sizeof(*map->reg_defaults), GFP_KERNEL); if (!tmp_buf) return -ENOMEM; + + /* regcache_lookup_reg() bsearch()es this array */ + if (sort_defaults) { + dev_warn(map->dev, + "Driver needs fixing: Unsorted reg_defaults, sorting the copy\n"); + regcache_sort_defaults(tmp_buf, map->num_reg_defaults); + } map->reg_defaults = tmp_buf; } else if (map->num_reg_defaults_raw) { count = regcache_count_cacheable_registers(map); From e3775cbffaf0e61fda4808a06963a6bc3fcddf82 Mon Sep 17 00:00:00 2001 From: Swark Yang Date: Wed, 5 Aug 2026 22:06:22 -0700 Subject: [PATCH 030/171] spi: dt-bindings: snps,dw-apb-ssi: Document Axiado AX3005 The Axiado AX3005 SPI controller is software-compatible with the Synopsys DesignWare APB SSI 1.01a programming interface. Document the "axiado,ax3005-spi" SoC-specific compatible with "snps,dwc-ssi-1.01a" as its fallback. This preserves identification of the AX3005 integration while allowing the controller to use the existing DesignWare APB SSI support. Signed-off-by: Swark Yang Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260805-upstrea-ax3005-spi-v4-v4-1-5b8bc11cb841@axiado.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml index 95a5bd894e93..7f698195cc43 100644 --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml @@ -53,6 +53,11 @@ properties: - sophgo,sg2042-spi - thead,th1520-spi - const: snps,dw-apb-ssi + - description: Vendor controllers compatible with v1.01a + items: + - enum: + - axiado,ax3005-spi + - const: snps,dwc-ssi-1.01a - description: Vendor controllers which use snps,dwc-ssi-2.00a as fallback items: - enum: From 594d905195024b228c962627ae5ae7c17bd582a4 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Aug 2026 00:21:54 +0000 Subject: [PATCH 031/171] af_unix: Unlink scc_entry in unix_del_edge(). Kyle Zeng reported that GC could free a dead SCC partially. The scenario is as follows: 1) Create two SCCs: X -. A <-> B ^--' 2) Run the following concurrently: 2-1) send() sk-B to sk-B from sk-X 2-2) close() both A and B At 2-1), there is a small window where unix_add_edges() publishes a new edge (B <-> B) to GC but its skb is not queued by skb_queue_tail(). If 2-2) completes before skb_queue_tail() and GC is triggered, it judges A <-> B as dead, but B is not freed because GC cannot collect the not-yet-queued skb holding the B <-> B edge. X -. A <-> B -. This edge is visible ^--' ^..' but skb is not This itself is not a problem since the next GC run will judge B as dead as well and free it finally. X -. A <.> B -. ^--' ^--' However, X's SCC forces the next GC to call unix_walk_scc_fast(), and it iterates over A through B's scc_entry. Let's unlink scc_entry before freeing the vertex in unix_del_edge(). Fixes: 4090fa373f0e ("af_unix: Replace garbage collection algorithm.") Reported-by: Kyle Zeng Signed-off-by: Kuniyuki Iwashima Reviewed-by: Kyle Zeng Fixes: 4090fa373f0e ("af_unix: Replace garbage collection algorithm."). Link: https://patch.msgid.link/20260804002155.2233594-1-kuniyu@google.com Signed-off-by: Jakub Kicinski --- net/unix/garbage.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/unix/garbage.c b/net/unix/garbage.c index 0783555e2526..9fcaaf55cba5 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c @@ -186,6 +186,7 @@ static void unix_del_edge(struct scm_fp_list *fpl, struct unix_edge *edge) if (!vertex->out_degree) { edge->predecessor->vertex = NULL; list_move_tail(&vertex->entry, &fpl->vertices); + list_del(&vertex->scc_entry); } } From 41d44ac7a61e2f74453af40d4fe1b82af9ea0ada Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Wed, 29 Jul 2026 12:21:41 +0200 Subject: [PATCH 032/171] ovpn: fix NULL dereference when killing missing key ovpn_crypto_kill_key assumes both crypto slots are populated and dereferences each slot before checking it. That is not guaranteed: a peer can have only one installed key, and the kill path may be asked to remove a key that is not present. Read each slot once while holding the crypto state lock, check for NULL before looking at key_id, and only replace the slot that actually matches. Fixes: 89d3c0e4612a ("ovpn: kill key and notify userspace in case of IV exhaustion") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/crypto.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/net/ovpn/crypto.c b/drivers/net/ovpn/crypto.c index 90580e32052f..2e95f29514fc 100644 --- a/drivers/net/ovpn/crypto.c +++ b/drivers/net/ovpn/crypto.c @@ -58,15 +58,19 @@ void ovpn_crypto_state_release(struct ovpn_crypto_state *cs) bool ovpn_crypto_kill_key(struct ovpn_crypto_state *cs, u8 key_id) { struct ovpn_crypto_key_slot *ks = NULL; + struct ovpn_crypto_key_slot *tmp; + int slot = 0; spin_lock_bh(&cs->lock); - if (rcu_access_pointer(cs->slots[0])->key_id == key_id) { - ks = rcu_replace_pointer(cs->slots[0], NULL, - lockdep_is_held(&cs->lock)); - } else if (rcu_access_pointer(cs->slots[1])->key_id == key_id) { - ks = rcu_replace_pointer(cs->slots[1], NULL, - lockdep_is_held(&cs->lock)); + tmp = rcu_access_pointer(cs->slots[slot]); + if (!tmp || tmp->key_id != key_id) { + slot = 1; + tmp = rcu_access_pointer(cs->slots[slot]); } + + if (tmp && tmp->key_id == key_id) + ks = rcu_replace_pointer(cs->slots[slot], NULL, + lockdep_is_held(&cs->lock)); spin_unlock_bh(&cs->lock); if (ks) From 9e163917a86c6adfbe150e13f4c73653a54616de Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Wed, 29 Jul 2026 12:21:44 +0200 Subject: [PATCH 033/171] ovpn: finish crypto callback cleanup before peer release Crypto completion callbacks hold both key-slot and peer references. The peer reference pins the netdev, and dropping the last peer reference can let netdev unregistration and module removal make progress. Do not release that peer reference before the callback has finished its own cleanup. If ovpn_crypto_key_slot_put runs after ovpn_peer_put, it can schedule an RCU callback backed by module text after ovpn_cleanup rcu_barrier has already run. The TX error path also freed the remaining skb after ovpn_peer_put, leaving callback cleanup outside the peer/netdev lifetime window. Release the key slot and free any remaining skb first, then drop the peer reference as the last callback action. Fixes: 8534731dbf2d ("ovpn: implement packet processing") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/io.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index 9a66d693039a..9526f8096da6 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -204,10 +204,10 @@ void ovpn_decrypt_post(void *data, int ret) ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); kfree_skb(skb); drop_nocount: - if (likely(peer)) - ovpn_peer_put(peer); if (likely(ks)) ovpn_crypto_key_slot_put(ks); + if (likely(peer)) + ovpn_peer_put(peer); } /* RX path entry point: decrypt packet and forward it to the device */ @@ -302,11 +302,11 @@ void ovpn_encrypt_post(void *data, int ret) err: if (unlikely(skb)) ovpn_dev_dstats_tx_dropped(peer->ovpn->dev); - if (likely(peer)) - ovpn_peer_put(peer); + kfree_skb(skb); if (likely(ks)) ovpn_crypto_key_slot_put(ks); - kfree_skb(skb); + if (likely(peer)) + ovpn_peer_put(peer); } static bool ovpn_encrypt_one(struct ovpn_peer *peer, struct sk_buff *skb) From 2820d227ad4ee70805d693d698437cc3e88d6c3d Mon Sep 17 00:00:00 2001 From: Pu Lehui Date: Sun, 2 Aug 2026 09:49:29 +0000 Subject: [PATCH 034/171] riscv: ftrace: Fix ftrace_modify_call failure on kprobed functions We are frequently hitting the following splat during the riscv bpf selftests: 00000000026dc75a: expected (7c3ff297) but got (00100073) ------------[ ftrace bug ]------------ ftrace failed to modify [] bpf_kfunc_common_test+0x4/0x20 [bpf_testmod] actual: e7:82:c2:ce Updating ftrace call site to call a different ftrace function ftrace record flags: 80100002 (2) expected tramp: ffffffff80043904 ------------[ cut here ]------------ WARNING: kernel/trace/ftrace.c:2278 at ftrace_bug+0x46e/0x4b0, CPU#1: test_progs/98 ... [] ftrace_bug+0x46e/0x4b0 [] ftrace_replace_code+0x16e/0x170 [] ftrace_modify_all_code+0x12e/0x1b8 [] arch_ftrace_update_code+0x14/0x28 [] ftrace_startup+0x14c/0x2a0 [] ftrace_startup_subops+0x584/0x1050 [] register_ftrace_graph+0x4e6/0x1018 [] register_fprobe_ips+0xc66/0x12f8 [] bpf_kprobe_multi_link_attach+0x5d8/0xe68 [] __sys_bpf+0x3d5a/0x47f0 [] __riscv_sys_bpf+0xae/0x168 [] syscall_handler+0x60/0x100 [] do_trap_ecall_u+0x174/0x208 [] handle_exception+0x16c/0x178 After debugging, it can be triggered by similar commands below: ``` echo do_nanosleep > set_ftrace_filter echo function > current_tracer echo 'p do_nanosleep' > kprobe_events echo 1 > events/kprobes/enable echo 'f do_nanosleep' > dynamic_events echo 1 > events/fprobes/enable ``` The reason is that attaching a kprobe to an ftrace-traced function entry replaces its initial auipc insn with ebreak. When ftrace_modify_call later runs, it expects auipc insn, so verification fails and triggers ftrace_bug. The expected auipc logic remains conceptually unchanged, and kprobe single-stepping ensures normal execution. Therefore, if the first insn is ebreak, bypassing the check to continue patching the jalr insn is safe and avoids ftrace failures. Fixes: b2137c3b6d7a ("riscv: ftrace: prepare ftrace for atomic code patching") Signed-off-by: Pu Lehui Link: https://patch.msgid.link/20260802094929.3978390-1-pulehui@huaweicloud.com [pjw@kernel.org: fixed reproducer in commit message] Signed-off-by: Paul Walmsley --- arch/riscv/kernel/ftrace.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c index b430edfb83f4..be8b68514417 100644 --- a/arch/riscv/kernel/ftrace.c +++ b/arch/riscv/kernel/ftrace.c @@ -12,6 +12,7 @@ #include #include #include +#include #ifdef CONFIG_DYNAMIC_FTRACE void ftrace_arch_code_modify_prepare(void) @@ -63,7 +64,9 @@ static int __ftrace_modify_call(unsigned long source, unsigned long target, bool if (copy_from_kernel_nofault(replaced, (void *)source, 2 * MCOUNT_INSN_SIZE)) return -EFAULT; - if (replaced[0] != call[0]) { + /* Bypass the check if the auipc insn is a kprobe breakpoint */ + if (replaced[0] != call[0] && + !(riscv_insn_is_ebreak(replaced[0]) || riscv_insn_is_c_ebreak(replaced[0]))) { pr_err("%p: expected (%08x) but got (%08x)\n", (void *)source, call[0], replaced[0]); return -EINVAL; From 872f486259ae0bc6b73ca4735a15d013241f73e9 Mon Sep 17 00:00:00 2001 From: Hongjie Fang Date: Wed, 29 Jul 2026 19:16:14 +0800 Subject: [PATCH 035/171] scsi: core: pair EH runtime PM get and put shost->eh_noresume is currently consulted twice in one error handling iteration: once before scsi_autopm_get_host() and once again before scsi_autopm_put_host(). That is racy when a PM-triggered error path flips shost->eh_noresume while the SCSI EH thread is still running. The problem flow looks like this: PM path ufshcd_set_dev_pwr_mode() shost->eh_noresume = 1 ufshcd_execute_start_stop <-- trigger EH ... shost->eh_noresume = 0 EH path scsi_error_handler() if (!shost->eh_noresume) scsi_autopm_get_host() <-- skipped ... if (!shost->eh_noresume) scsi_autopm_put_host() <-- executed later In that case one EH iteration can skip autoresume on entry and still drop a runtime PM reference on exit. That leaves an unmatched runtime PM put and can trigger a runtime PM usage count underflow. Fix this by making eh_noresume a regular bool so it can be accessed with READ_ONCE() and WRITE_ONCE(). Snapshot it once per EH iteration and use that snapshot for both runtime PM get and put decisions. Fixes: ae0751ffc77e ("[SCSI] add flag to skip the runtime PM calls on the host") Signed-off-by: Hongjie Fang Reviewed-by: Bart Van Assche Reviewed-by: Peter Wang Link: https://patch.msgid.link/20260729111614.2407559-1-hongjiefang@asrmicro.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/scsi_error.c | 6 ++++-- drivers/ufs/core/ufshcd.c | 4 ++-- include/scsi/scsi_host.h | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 453a2232452d..74b70801269b 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -2362,6 +2362,7 @@ static void scsi_unjam_host(struct Scsi_Host *shost) int scsi_error_handler(void *data) { struct Scsi_Host *shost = data; + bool eh_noresume; /* * We use TASK_INTERRUPTIBLE so that the thread is not @@ -2403,7 +2404,8 @@ int scsi_error_handler(void *data) * what we need to do to get it up and online again (if we can). * If we fail, we end up taking the thing offline. */ - if (!shost->eh_noresume && scsi_autopm_get_host(shost) != 0) { + eh_noresume = READ_ONCE(shost->eh_noresume); + if (!eh_noresume && scsi_autopm_get_host(shost) != 0) { SCSI_LOG_ERROR_RECOVERY(1, shost_printk(KERN_ERR, shost, "scsi_eh_%d: unable to autoresume\n", @@ -2427,7 +2429,7 @@ int scsi_error_handler(void *data) * which are still online. */ scsi_restart_operations(shost); - if (!shost->eh_noresume) + if (!eh_noresume) scsi_autopm_put_host(shost); } __set_current_state(TASK_RUNNING); diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 13e6357a8bbb..a41b56dbf3b7 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -10016,7 +10016,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, * we are functional while we are here, skip host resume in error * handling context. */ - hba->host->eh_noresume = 1; + WRITE_ONCE(hba->host->eh_noresume, 1); /* * Current function would be generally called from the power management @@ -10038,7 +10038,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, } scsi_device_put(sdp); - hba->host->eh_noresume = 0; + WRITE_ONCE(hba->host->eh_noresume, 0); return ret; } diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index f6b286fa59f2..98b0ccf0813e 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -664,6 +664,9 @@ struct Scsi_Host { /* Asynchronous scan in progress */ bool async_scan __guarded_by(&scan_mutex); + /* Don't resume host in EH */ + bool eh_noresume; + unsigned active_mode:2; /* @@ -682,9 +685,6 @@ struct Scsi_Host { /* Task mgmt function in progress */ unsigned tmf_in_progress:1; - /* Don't resume host in EH */ - unsigned eh_noresume:1; - /* The controller does not support WRITE SAME */ unsigned no_write_same:1; From 732cb6bb37fd26863d5786522fb1997e7f5865b4 Mon Sep 17 00:00:00 2001 From: Zizhi Wo Date: Thu, 23 Jul 2026 12:12:38 +0800 Subject: [PATCH 036/171] scsi: core: Do not block on tag allocation in scsi_eh_lock_door() scsi_eh_lock_door() is called from scsi_restart_operations() while the host is still in the SHOST_RECOVERY state, i.e. before the host is switched back to SHOST_RUNNING and scsi_run_host_queues() restarts the queues. It allocates a request via scsi_alloc_request() with no flags, so blk_mq_get_tag() may block waiting for a free sched tag when all tags are already in use. Those tags can be held by commands that were just requeued by scsi_eh_flush_done_q() during error handling. Such commands cannot be dispatched until the host leaves SHOST_RECOVERY and scsi_run_host_queues() is called - which only happens *after* scsi_eh_lock_door() returns. This forms a circular dependency: - scsi_eh_lock_door(), running in the SCSI error handler thread, waits for a sched tag held by a requeued command; - the requeued command cannot complete and release its sched tag until the error handler thread leaves scsi_restart_operations() and restart the queues. For devices with a single driver tag (e.g. USB storage) it is a guaranteed deadlock and I/O that can never be submitted. This problem has also been reproduced in our environment. Locking the door is a best-effort operation, and scsi_eh_lock_door() already returns silently when the request allocation fails. Pass BLK_MQ_REQ_NOWAIT to scsi_alloc_request() so the allocation fails instead of blocking when no tag is available. This breaks the circular dependency and allows the error handler to finish restarting the queues, after which the pending commands are dispatched normally. Signed-off-by: Zizhi Wo Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260723041238.1584632-1-wozizhi@huaweicloud.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/scsi_error.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 74b70801269b..b729407348f7 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -2161,7 +2161,7 @@ static void scsi_eh_lock_door(struct scsi_device *sdev) struct scsi_cmnd *scmd; struct request *req; - req = scsi_alloc_request(sdev->request_queue, REQ_OP_DRV_IN, 0); + req = scsi_alloc_request(sdev->request_queue, REQ_OP_DRV_IN, BLK_MQ_REQ_NOWAIT); if (IS_ERR(req)) return; scmd = blk_mq_rq_to_pdu(req); From 24aa630f6259e6a2107936c06fed72063f712b64 Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Fri, 7 Aug 2026 16:15:12 +0800 Subject: [PATCH 037/171] selftests/ftrace: Convert ELF entry point to file offset in uprobe test The add_remove_uprobe test uses readelf -h to obtain the ELF entry point (e_entry) and passes it directly as the offset to uprobe_events. However, uprobe_events expects a file offset, not a virtual address. For PIE binaries, the virtual address happens to equal the file offset because the first LOAD segment has p_vaddr == p_offset, so the test works by coincidence. But for non-PIE executables, e_entry is an absolute virtual address that can far exceed the file size. When the probe is enabled, uprobe_register() checks offset > i_size_read(inode) and rejects it with -EINVAL. Fix this by converting the virtual address to a file offset using the ELF program headers: scan readelf -lW output for the LOAD segment containing the entry point, then compute file_offset = e_entry - p_vaddr + p_offset. For PIE binaries the result is unchanged; for non-PIE binaries the offset is correctly translated. The conversion uses only POSIX shell primitives, with no dependency on gawk or perl. Link: https://lore.kernel.org/all/20260807081512.2974757-3-qirui.001@bytedance.com/ Fixes: dc4b165855f2 ("selftests/ftrace: Use readelf to find entry point in uprobe test") Cc: stable@vger.kernel.org Signed-off-by: Rui Qi Signed-off-by: Masami Hiramatsu (Google) --- .../test.d/dynevent/add_remove_uprobe.tc | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_uprobe.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_uprobe.tc index f2048c244526..19430bd5864c 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_uprobe.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_uprobe.tc @@ -12,9 +12,32 @@ echo 0 > events/enable echo > dynamic_events REALBIN=`readlink -f /bin/sh` -ENTRYPOINT=`readelf -h ${REALBIN} | grep Entry | sed -e 's/[^0]*//'` -echo "p:myevent ${REALBIN}:${ENTRYPOINT}" >> uprobe_events +# Get the entry point virtual address from ELF header +ENTRY=`readelf -hW ${REALBIN} | grep "Entry point" | awk '{print $NF}'` + +# Convert virtual address to file offset: find the LOAD segment containing +# the entry point, then compute file_offset = e_entry - p_vaddr + p_offset. +# For PIE binaries this is a no-op (vaddr == file offset), but for non-PIE +# executables the virtual address is much larger than the file size and +# must be converted, otherwise uprobe_register() rejects it with -EINVAL. +ENTRY_DEC=$(printf '%d' "$ENTRY") +OFFSET=$ENTRY +while IFS= read -r line; do + set -- $line + [ "$1" = "LOAD" ] || continue + VA_DEC=$(printf '%d' "$3") + OFF_DEC=$(printf '%d' "$2") + FSZ_DEC=$(printf '%d' "$5") + if [ "$ENTRY_DEC" -ge "$VA_DEC" ] && [ "$ENTRY_DEC" -lt "$((VA_DEC + FSZ_DEC))" ]; then + OFFSET=$(printf '0x%x' "$((ENTRY_DEC - VA_DEC + OFF_DEC))") + break + fi +done << EOF +$(readelf -lW ${REALBIN} | grep LOAD) +EOF + +echo "p:myevent ${REALBIN}:${OFFSET}" >> uprobe_events grep -q myevent uprobe_events test -d events/uprobes/myevent From c4f6916a99cf105c3ff340b6210fcbba3fa66b35 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Mon, 3 Aug 2026 17:53:28 +0800 Subject: [PATCH 038/171] scsi: scsi_debug: Negate wrapped memcmp() result comp_write_worker() returns true when the compared data matches. memcmp() returns zero for equal data and non-zero for different data, so its result must be negated before it is stored in a bool. The first segment already uses !memcmp(), but the wrapped segment uses memcmp() directly, reversing the match result. Use !memcmp() there as well. Fixes: 38d5c8336e60 ("scsi_debug: add Report supported opcodes+tmfs; Compare and write") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Reviewed-by: John Garry Link: https://patch.msgid.link/E5AD42E9C0E18633+20260803095328.3445311-1-raoxu@uniontech.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/scsi_debug.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 643051332132..4fb2dc403962 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -4310,8 +4310,8 @@ static bool comp_write_worker(struct sdeb_store_info *sip, u64 lba, u32 num, if (!res) return res; if (rest) - res = memcmp(fsp, arr + ((num - rest) * lb_size), - rest * lb_size); + res = !memcmp(fsp, arr + ((num - rest) * lb_size), + rest * lb_size); if (!res) return res; if (compare_only) From fa091f46c3833fb22384f10eade2b4e1e1d0b278 Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Thu, 6 Aug 2026 13:56:55 -0700 Subject: [PATCH 039/171] perf: Reject exited events as group leaders perf_event_remove_on_exec() sets remove-on-exec events to the EXIT state and detaches their group relationships. The event's file descriptor can remain open, however, and perf_event_open() currently accepts that event as a group leader because its early validation rejects only REVOKED and DEAD events. A new sibling can consequently be linked to the detached leader. When the leader is closed, perf_group_detach() observes that its PERF_ATTACH_GROUP bit is already clear and skips the new sibling. The sibling then retains a group_leader pointer to the freed event. Reject group leaders in the EXIT state. Perform the check while holding the shared context mutex so that an exec in the target task cannot detach the leader between validation and group attachment. [peterz: make the earlier test fully consistent] Fixes: 037a3c43edfb ("perf/core: Detach event groups during remove_on_exec") Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Kyle Zeng Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/20260806205655.75722-1-kylebot@openai.com --- kernel/events/core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index ba5bd6a78fe7..bd25e1998faf 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -13972,7 +13972,7 @@ SYSCALL_DEFINE5(perf_event_open, goto err_fd; } group_leader = fd_file(group)->private_data; - if (group_leader->state <= PERF_EVENT_STATE_REVOKED) { + if (group_leader->state <= PERF_EVENT_STATE_EXIT) { err = -ENODEV; goto err_fd; } @@ -14103,6 +14103,12 @@ SYSCALL_DEFINE5(perf_event_open, if (group_leader->ctx != ctx) goto err_locked; + /* Recheck under ctx::mutex to serialize against remove-on-exec. */ + if (group_leader->state <= PERF_EVENT_STATE_EXIT) { + err = -ENODEV; + goto err_locked; + } + /* * Only a group leader can be exclusive or pinned */ From 42c5ca1f0a288a52878bd72a5595b08261057438 Mon Sep 17 00:00:00 2001 From: Aditya Chillara Date: Fri, 7 Aug 2026 18:11:52 +0530 Subject: [PATCH 040/171] perf/core: Fix group leader use-after-free after sibling detach perf_group_detach() handles leader and sibling detach differently. When the group leader is detached, all siblings are promoted to singleton events and their group_leader pointer is reset to themselves. When a sibling is detached, it is removed from the leader's sibling_list, but its group_leader pointer is left pointing at the old leader. That is harmless when the sibling is being closed and freed immediately, as in the DETACH_DEAD path. It is not safe when the sibling is detached but kept alive, such as during CPU hotplug with DETACH_GROUP. In that case the sibling is removed from the context, while its file descriptor can still keep it alive. A typical failing sequence is: - A group contains leader L and sibling S. - CPU hot-unplug detaches S with DETACH_GROUP, removing it from L->sibling_list but leaving S->group_leader == L. - L is later closed and freed. - A PERF_IOC_FLAG_GROUP ioctl on S follows S->group_leader and dereferences the freed leader. This was reproduced by running the perf event fuzzer, CPU hotplug, and a stress workload concurrently: Unable to handle kernel paging request at virtual address 006b6b6b6b6b6cdb CPU: 2 PID: 12489 Comm: perf_fuzzer 6.18.7 PREEMPT pc : perf_ioctl+0x34c/0xc68 x20: ffffff89a3fa2c70 x8 : 6b6b6b6b6b6b6b6b Code: 943c4a0e 340047a0 f9404a94 f9411e88 (f940b908) Call trace: perf_ioctl+0x34c/0xc68 (P) __arm64_sys_ioctl+0xa0/0xf4 invoke_syscall+0x58/0xe4 el0_svc_common+0xa8/0xdc do_el0_svc+0x1c/0x28 el0_svc+0x40/0xc0 el0t_64_sync_handler+0x68/0xdc el0t_64_sync+0x1c4/0x1c8 The fault happened in perf_ioctl(), where perf_event_for_each() follows the stale group_leader pointer and perf_event_for_each_child() then dereferences the freed leader's context. Fix the use-after-free by promoting the detached sibling to a singleton. Also fix __event_disable() cgroup accounting and event state change. Fixes: 8a49542c0554 ("perf_events: Fix races in group composition") Assisted-by: PatchWise:gpt-5.5 Signed-off-by: Aditya Chillara Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Dapeng Mi Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260807-fix-group-leader-uaf-v3-1-b0c2310c9a0d@oss.qualcomm.com --- kernel/events/core.c | 67 ++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index bd25e1998faf..1a73ba0747df 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -2343,6 +2343,34 @@ static inline struct list_head *get_event_list(struct perf_event *event) &event->pmu_ctx->flexible_active; } +/* @sibling must already be unlinked from its old leader's sibling_list. */ +static void perf_promote_sibling_to_leader(struct perf_event *sibling, + struct perf_event_context *ctx, + int group_caps) +{ + /* + * Events that have PERF_EV_CAP_SIBLING require being part of + * a group and cannot exist on their own, schedule them out + * and move them into the ERROR state. Also see + * _perf_event_enable(), it will not be able to recover this + * ERROR state. + */ + if (sibling->event_caps & PERF_EV_CAP_SIBLING) + __event_disable(sibling, ctx, PERF_EVENT_STATE_ERROR); + + sibling->group_leader = sibling; + sibling->group_caps = group_caps; + + if (sibling->attach_state & PERF_ATTACH_CONTEXT) { + add_event_to_groups(sibling, ctx); + + if (sibling->state == PERF_EVENT_STATE_ACTIVE) + list_add_tail(&sibling->active_list, get_event_list(sibling)); + } + + perf_event__header_size(sibling); +} + static void perf_group_detach(struct perf_event *event) { struct perf_event *leader = event->group_leader; @@ -2366,8 +2394,9 @@ static void perf_group_detach(struct perf_event *event) */ if (leader != event) { list_del_init(&event->sibling_list); - event->group_leader->nr_siblings--; - event->group_leader->group_generation++; + leader->nr_siblings--; + leader->group_generation++; + perf_promote_sibling_to_leader(event, ctx, event->event_caps); goto out; } @@ -2377,32 +2406,14 @@ static void perf_group_detach(struct perf_event *event) * to whatever list we are on. */ list_for_each_entry_safe(sibling, tmp, &event->sibling_list, sibling_list) { - - /* - * Events that have PERF_EV_CAP_SIBLING require being part of - * a group and cannot exist on their own, schedule them out - * and move them into the ERROR state. Also see - * _perf_event_enable(), it will not be able to recover this - * ERROR state. - */ - if (sibling->event_caps & PERF_EV_CAP_SIBLING) - __event_disable(sibling, ctx, PERF_EVENT_STATE_ERROR); - - sibling->group_leader = sibling; list_del_init(&sibling->sibling_list); /* Inherit group flags from the previous leader */ - sibling->group_caps = event->group_caps; - - if (sibling->attach_state & PERF_ATTACH_CONTEXT) { - add_event_to_groups(sibling, event->ctx); - - if (sibling->state == PERF_EVENT_STATE_ACTIVE) - list_add_tail(&sibling->active_list, get_event_list(sibling)); - } + perf_promote_sibling_to_leader(sibling, ctx, event->group_caps); WARN_ON_ONCE(sibling->ctx != event->ctx); } + event->nr_siblings = 0; out: for_each_sibling_event(tmp, leader) @@ -2592,12 +2603,7 @@ __perf_remove_from_context(struct perf_event *event, if (flags & DETACH_DEAD) state = PERF_EVENT_STATE_DEAD; - event_sched_out(event, ctx); - - if (event->state > PERF_EVENT_STATE_OFF) - perf_cgroup_event_disable(event, ctx); - - perf_event_set_state(event, min(event->state, state)); + __event_disable(event, ctx, state); if (flags & DETACH_GROUP) perf_group_detach(event); @@ -2666,8 +2672,9 @@ static void __event_disable(struct perf_event *event, enum perf_event_state state) { event_sched_out(event, ctx); - perf_cgroup_event_disable(event, ctx); - perf_event_set_state(event, state); + if (event->state > PERF_EVENT_STATE_OFF) + perf_cgroup_event_disable(event, ctx); + perf_event_set_state(event, min(event->state, state)); } /* From 3dbb44d88b1e94dd31fe43588af7437b34b44d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Jean?= Date: Tue, 4 Aug 2026 20:00:42 +0000 Subject: [PATCH 041/171] sctp: validate cookie AUTH state before use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When cookie authentication is disabled, COOKIE_ECHO restores fixed-size AUTH fields directly from peer-controlled cookie bytes. A forged RANDOM length, HMAC list, or CHUNKS list can then reach association consumers with lengths or identifiers that were never validated against the local backing arrays. A forged RANDOM length can cause out-of-bounds reads during key-vector construction. A forged HMAC identifier also caused a 32-byte write past a zero-length AUTH chunk, providing a primitive for a local privilege escalation chain. Validate the cookie's RANDOM, HMACS, and CHUNKS parameters at the cookie trust boundary before copying them into the association. Reject invalid types, malformed lengths, unsupported HMAC identifiers, HMAC lists without SHA1, and forbidden chunk ids. Fixes: bbd0d59809f9 ("[SCTP]: Implement the receive and verification of AUTH chunk") Fixes: 1f485649f529 ("[SCTP]: Implement SCTP-AUTH internals") Signed-off-by: Jérémy Jean Acked-by: Xin Long Link: https://patch.msgid.link/20260804200042.2412009-1-Jeremy.Jean@oss.cyber.gouv.fr Signed-off-by: Jakub Kicinski --- include/net/sctp/auth.h | 3 ++ net/sctp/auth.c | 75 ++++++++++++++++++++++++++++++++++++++++ net/sctp/sm_make_chunk.c | 3 ++ 3 files changed, 81 insertions(+) diff --git a/include/net/sctp/auth.h b/include/net/sctp/auth.h index 6f2cd562b1de..74b3790e2a3d 100644 --- a/include/net/sctp/auth.h +++ b/include/net/sctp/auth.h @@ -22,6 +22,7 @@ struct sctp_endpoint; struct sctp_association; struct sctp_authkey; struct sctp_hmacalgo; +struct sctp_cookie; /* Defines an HMAC algorithm supported by SCTP chunk authentication */ struct sctp_hmac { @@ -72,6 +73,8 @@ struct sctp_shared_key *sctp_auth_get_shkey( int sctp_auth_asoc_copy_shkeys(const struct sctp_endpoint *ep, struct sctp_association *asoc, gfp_t gfp); +bool sctp_auth_verify_cookie_params(const struct sctp_endpoint *ep, + const struct sctp_cookie *cookie); const struct sctp_hmac *sctp_auth_get_hmac(__u16 hmac_id); const struct sctp_hmac * sctp_auth_asoc_get_hmac(const struct sctp_association *asoc); diff --git a/net/sctp/auth.c b/net/sctp/auth.c index c901d373af80..cc4229ee116d 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -377,6 +377,81 @@ int sctp_auth_asoc_copy_shkeys(const struct sctp_endpoint *ep, return -ENOMEM; } +static bool sctp_auth_chunk_id_forbidden(__u8 chunk_id) +{ + switch (chunk_id) { + case SCTP_CID_INIT: + case SCTP_CID_INIT_ACK: + case SCTP_CID_SHUTDOWN_COMPLETE: + case SCTP_CID_AUTH: + return true; + default: + return false; + } +} + +/* Verify AUTH parameters copied from a state cookie before they are restored + * into an association. When cookie authentication is disabled these fields + * are peer-controlled, so they must satisfy the same constraints as locally + * generated AUTH parameters. + */ +bool sctp_auth_verify_cookie_params(const struct sctp_endpoint *ep, + const struct sctp_cookie *cookie) +{ + const struct sctp_paramhdr *random; + const struct sctp_hmac_algo_param *hmacs; + const struct sctp_chunks_param *chunks; + u16 hmacs_len, chunks_len; + u16 n_hmacs, n_chunks, i; + bool has_sha1 = false; + + if (sctp_sk(ep->base.sk)->cookie_auth_enable || !ep->auth_enable) + return true; + + random = (const struct sctp_paramhdr *)cookie->auth_random; + if (random->type != SCTP_PARAM_RANDOM || + ntohs(random->length) != sizeof(*random) + SCTP_AUTH_RANDOM_LENGTH) + return false; + + hmacs = (const struct sctp_hmac_algo_param *)cookie->auth_hmacs; + hmacs_len = ntohs(hmacs->param_hdr.length); + if (hmacs->param_hdr.type != SCTP_PARAM_HMAC_ALGO || + hmacs_len < sizeof(struct sctp_paramhdr) + + sizeof(hmacs->hmac_ids[0]) || + hmacs_len > sizeof(cookie->auth_hmacs) || + (hmacs_len - sizeof(struct sctp_paramhdr)) % + sizeof(hmacs->hmac_ids[0])) + return false; + + n_hmacs = (hmacs_len - sizeof(struct sctp_paramhdr)) / + sizeof(hmacs->hmac_ids[0]); + for (i = 0; i < n_hmacs; i++) { + u16 hmac_id = ntohs(hmacs->hmac_ids[i]); + + if (!sctp_hmac_supported(hmac_id)) + return false; + if (hmac_id == SCTP_AUTH_HMAC_ID_SHA1) + has_sha1 = true; + } + if (!has_sha1) + return false; + + chunks = (const struct sctp_chunks_param *)cookie->auth_chunks; + chunks_len = ntohs(chunks->param_hdr.length); + if (chunks->param_hdr.type != SCTP_PARAM_CHUNKS || + chunks_len < sizeof(struct sctp_paramhdr) || + chunks_len > sizeof(cookie->auth_chunks)) + return false; + + n_chunks = chunks_len - sizeof(struct sctp_paramhdr); + for (i = 0; i < n_chunks; i++) { + if (sctp_auth_chunk_id_forbidden(chunks->chunks[i])) + return false; + } + + return true; +} + /* Public interface to create the association shared key. * See code above for the algorithm. diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index e25612e9d082..236e25abc7a4 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1852,6 +1852,9 @@ struct sctp_association *sctp_unpack_cookie( /* Set up our peer's port number. */ retval->peer.port = ntohs(chunk->sctp_hdr->source); + if (!sctp_auth_verify_cookie_params(ep, bear_cookie)) + goto malformed; + /* Populate the association from the cookie. */ memcpy(&retval->c, bear_cookie, sizeof(*bear_cookie)); From 994dad686e755477e2b2700cca4e6e1a90a58bdc Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Tue, 21 Jul 2026 23:05:09 +0800 Subject: [PATCH 042/171] riscv: hwprobe: Register unaligned probes before usermode The hwprobe vDSO data is populated by the first riscv_hwprobe syscall. Some values, such as MISALIGNED_VECTOR_PERF, may depend on the async vector unaligned access speed probe registered by check_unaligned_access_all_cpus(). That initcall currently runs at late_initcall level. However, rootfs_initcall enables usermode helpers before late initcalls run, so an early helper can execute userspace and call riscv_hwprobe first. In that case complete_hwprobe_vdso_data() consumes the initial pending_boot_probes reference, populates the vDSO cache, and marks it ready before the later async probe is registered. The eventual probe result then cannot update the already-ready cache. Move check_unaligned_access_all_cpus() to fs_initcall_sync. This still runs after clocksource_done_booting(), so the ktime_get_mono_fast_ns() benchmark uses a stable clocksource, but it runs before rootfs_initcall enables usermode helpers. Any async hwprobe probe is therefore registered before userspace can trigger the one-time vDSO cache population. Cc: stable@vger.kernel.org Fixes: 6455c6c11827 ("riscv: Clean up & optimize unaligned scalar access probe") Signed-off-by: Rui Qi Reviewed-by: Nam Cao Link: https://patch.msgid.link/20260721150511.1607105-1-qirui.001@bytedance.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/unaligned_access_speed.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c index 5a5aa22124e7..36201613d2ca 100644 --- a/arch/riscv/kernel/unaligned_access_speed.c +++ b/arch/riscv/kernel/unaligned_access_speed.c @@ -411,4 +411,10 @@ static int __init check_unaligned_access_all_cpus(void) return 0; } -late_initcall(check_unaligned_access_all_cpus); +/* + * Run after clocksource_done_booting() so measure_cycles() uses a stable + * clocksource, but before rootfs_initcall() enables usermode helpers. Those + * helpers can reach hwprobe and populate the vDSO cache, so async hwprobe + * probes must be registered first. + */ +fs_initcall_sync(check_unaligned_access_all_cpus); From 33f2b2eb33d666ecac68031e0f31424fb70528db Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Mon, 3 Aug 2026 22:40:38 -0700 Subject: [PATCH 043/171] net: fix skb length accounting after generic XDP frag adjustment Generic XDP exposes non-linear skb fragments through an xdp_buff. If an XDP program adjusts the fragment area, bpf_prog_run_generic_xdp() copies xdp_frags_size back to skb->data_len but leaves skb->len containing the old fragment contribution. After a fragment shrink, this makes skb_headlen() larger than the actual linear area. In the reproduced UDP receive path, __skb_datagram_iter() copied 1024 bytes past the actual linear tail to userspace, starting at struct skb_shared_info. The copied bytes included the affected skb's nr_frags, xdp_frags_size and a kernel pointer from skb_shinfo(skb)->frags[0]. Real packet data was displaced by the same amount and truncated at the end. Subtract the old data_len before replacing it and add the new data_len afterwards, keeping skb->len and skb->data_len synchronized. A 60000-byte UDP datagram on a veth pair with MTU 64000 was shortened by 1024 bytes from its fragment area. Before the fix, all 10 runs produced corrupted payloads. After the fix, all 10 runs matched the expected payload exactly. Fixes: e6d5dbdd20aa ("xdp: add multi-buff support for xdp running in generic mode") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/bpf/al9T9Eto%2FhRIzP5W@boxer/ Reviewed-by: Mohsin Bashir Acked-by: Lorenzo Bianconi Signed-off-by: Sun Jian Link: https://patch.msgid.link/20260804054040.613675-2-sun.jian.kdev@gmail.com Signed-off-by: Jakub Kicinski --- net/core/dev.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index af260ff5462a..ece6700536d9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5517,12 +5517,16 @@ u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp, } /* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers - * (e.g. bpf_xdp_adjust_tail), we need to update data_len here. + * (e.g. bpf_xdp_adjust_tail). Remove the old fragment contribution + * from skb->len before updating data_len, then add the new one back. */ - if (xdp_buff_has_frags(xdp)) + skb->len -= skb->data_len; + if (xdp_buff_has_frags(xdp)) { skb->data_len = skb_shinfo(skb)->xdp_frags_size; - else + skb->len += skb->data_len; + } else { skb->data_len = 0; + } /* check if XDP changed eth hdr such SKB needs update */ eth = (struct ethhdr *)xdp->data; From cb6379feaaff11c4e1e79c26c745ffa23182768a Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Mon, 3 Aug 2026 22:40:39 -0700 Subject: [PATCH 044/171] veth: fix skb length accounting after XDP frag adjustment veth exposes non-linear skb fragments through an xdp_buff. If an XDP program adjusts the fragment area, veth_xdp_rcv_skb() copies xdp_frags_size back to skb->data_len but leaves skb->len containing the old fragment contribution. After a fragment shrink, this makes skb_headlen() larger than the actual linear area. In the reproduced UDP receive path, __skb_datagram_iter() copied 1024 bytes past the actual linear tail to userspace, starting at struct skb_shared_info. The copied bytes included the affected skb's nr_frags, xdp_frags_size, and a kernel pointer from skb_shinfo(skb)->frags[0]. Real packet data was displaced by the same amount and truncated at the end. Subtract the old data_len before replacing it and add the new data_len afterwards, keeping skb->len and skb->data_len synchronized. Additionally, bpf_xdp_pull_data() can advance data_end while leaving frags present. The skb is then still non-linear, so the old __skb_put(skb, off) triggers SKB_LINEAR_ASSERT(). Use skb_set_tail_pointer() and update skb->len explicitly instead, following bpf_prog_run_generic_xdp(). Unlike __skb_put(), skb_set_tail_pointer() does not require a linear skb. A 60000-byte UDP datagram on a veth pair with MTU 64000 was shortened by 1024 bytes from its fragment area. Before the fix, all 10 runs produced corrupted payloads. After the fix, all 10 runs matched the expected payload exactly. A forced-tailroom reproducer also exercises bpf_xdp_pull_data() with frags still present; the old code triggers SKB_LINEAR_ASSERT(), while this fix passes 10/10 runs. Fixes: 718a18a0c8a6 ("veth: Rework veth_xdp_rcv_skb in order to accept non-linear skb") Cc: stable@vger.kernel.org Reported-by: Mohsin Bashir Link: https://lore.kernel.org/bpf/80687d9c-9c27-494c-b3f2-efd0230b1895@gmail.com/ Suggested-by: Lorenzo Bianconi Acked-by: Lorenzo Bianconi Signed-off-by: Sun Jian Link: https://patch.msgid.link/20260804054040.613675-3-sun.jian.kdev@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/veth.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 00e34afd858e..f80f6584a6ff 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -865,18 +865,24 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, skb_reset_mac_header(skb); - /* check if bpf_xdp_adjust_tail was used */ - off = xdp->data_end - orig_data_end; - if (off != 0) - __skb_put(skb, off); /* positive on grow, negative on shrink */ - /* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers - * (e.g. bpf_xdp_adjust_tail), we need to update data_len here. + * (e.g. bpf_xdp_adjust_tail). Remove the old fragment contribution + * from skb->len before updating data_len, then add the new one back. */ - if (xdp_buff_has_frags(xdp)) + skb->len -= skb->data_len; + if (xdp_buff_has_frags(xdp)) { skb->data_len = skb_shinfo(skb)->xdp_frags_size; - else + skb->len += skb->data_len; + } else { skb->data_len = 0; + } + + /* Synchronize the skb tail with XDP's updated linear area. */ + off = xdp->data_end - orig_data_end; + if (off != 0) { + skb_set_tail_pointer(skb, xdp->data_end - xdp->data); + skb->len += off; /* positive on grow, negative on shrink */ + } skb->protocol = eth_type_trans(skb, rq->dev); From dd057113ac7ba5bdd2aed3d9405305911152f911 Mon Sep 17 00:00:00 2001 From: Christoph Paasch Date: Thu, 6 Aug 2026 12:49:22 -0700 Subject: [PATCH 045/171] mailmap: add entries for Christoph Paasch Map the email addresses used for previous kernel contributions to the current OpenAI address. This prevents get_maintainer.pl from listing historical addresses as patch recipients. Suggested-by: Matthieu Baerts Signed-off-by: Christoph Paasch Acked-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260806-b4-mailman-v1-1-b4d7bc0ffd1c@openai.com Signed-off-by: Jakub Kicinski --- .mailmap | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.mailmap b/.mailmap index 01453c5e1ab4..8032f87661f6 100644 --- a/.mailmap +++ b/.mailmap @@ -211,6 +211,10 @@ Christophe Ricard Christopher Obbard Christoph Hellwig Christoph Manszewski +Christoph Paasch +Christoph Paasch +Christoph Paasch +Christoph Paasch Chuck Lever Chuck Lever Chuck Lever From f8a80cfb68613fb7e6452b66447dbc63f435d140 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 8 Aug 2026 17:20:06 +0200 Subject: [PATCH 046/171] ALSA: usb: Fix UAF at delayed release of MIDI2 EPs The recent fix for UAF in ump_to_endpoint() caused another UAF because it tries to dereference the UMP endpoint object, but this might be executed at a delayed context where the endpoint has been already released. Add private_free to clear the associated data for avoiding the further dereference for delayed releases. Fixes: 4a05b2d1b464 ("ALSA: usb-audio: fix use-after-free in ump_to_endpoint()") Reported-by: syzbot+565b1138cfbe549d4422@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=565b1138cfbe549d4422 Cc: Link: https://patch.msgid.link/20260808152009.1947835-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/midi2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index 83980fb83ac8..1dc78f536ef9 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -677,6 +677,14 @@ static int parse_midi_2_0_endpoints(struct snd_usb_midi2_interface *umidi) return 0; } +static void free_ump_private_data(struct snd_ump_endpoint *ump) +{ + struct snd_usb_midi2_ump *rmidi = ump->private_data; + + if (rmidi) + rmidi->ump = NULL; +} + static void free_all_midi2_umps(struct snd_usb_midi2_interface *umidi) { struct snd_usb_midi2_ump *rmidi; @@ -727,6 +735,7 @@ static int create_midi2_ump(struct snd_usb_midi2_interface *umidi, ump->private_data = rmidi; ump->ops = &snd_usb_midi_v2_ump_ops; + ump->private_free = free_ump_private_data; rmidi->eps[STR_IN] = ep_in; rmidi->eps[STR_OUT] = ep_out; From 885c22d259c8b245c479f3e19e9eeee54dee8b24 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 8 Aug 2026 17:22:54 +0200 Subject: [PATCH 047/171] ALSA: usb-audio: Fix mixer regression on SteelSeries Arctis Nova 5 The recent "sticky mixer" sanity check in USB-audio driver caused a regression on SteelSeries Arctis Nova 5 (1038:2232); because the firmware doesn't handle GET_CUR requests, some mixers are effectively disabled, leading to the too low / soft volumes: usb 5-1.1: 9:0: sticky mixer values (-19712/0/256 => 0), disabling usb 5-1.1: 10:0: sticky mixer values (-21248/0/256 => 0), disabling Restore the functionality by ignoring GET_CUR errors intentionally with MIXER_GET_CUR_BROKEN quirk. Fixes: 86aa1ea1f15c ("ALSA: usb-audio: Do not expose sticky mixers") Reported-by: Gert Burger Closes: https://lore.kernel.org/CAEQ1D3kdA3mkQx7ei9Kq0gwky0qroJqCLKrkvgfkqgTbeu086A@mail.gmail.com Link: https://bbs.archlinux.org/viewtopic.php?id=314220 Link: https://patch.msgid.link/20260808152258.1948767-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 90ca39dbed18..4d2957e62fbd 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2367,6 +2367,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER), DEVICE_FLG(0x1038, 0x1294, /* SteelSeries Arctis Pro Wireless */ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), + DEVICE_FLG(0x1038, 0x2232, /* SteelSeries Arctis Nova 5 */ + QUIRK_FLAG_MIXER_GET_CUR_BROKEN), DEVICE_FLG(0x1101, 0x0003, /* Audioengine D1 */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x12d1, 0x3a07, /* HUAWEI USB-C HEADSET */ From 5d588c684833e678a0008eb69c33190f01a65f4b Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Mon, 13 Apr 2026 01:07:38 +0000 Subject: [PATCH 048/171] riscv: lib: Fix ZBB strnlen reading past count boundary The ZBB-optimized strnlen loop loads one word ahead before checking the aligned boundary: REG_L t1, SZREG(t0) // load next word addi t0, t0, SZREG // advance orc.b t1, t1 bgeu t0, t4, 4f // boundary check AFTER load where t4 = (s + count) & -SZREG. When s is aligned and count is a multiple of SZREG, t4 equals s + count and the loop loads a full word starting at exactly s + count. If s + count falls on a page boundary with the next page unmapped, this faults. Fix by computing the aligned boundary from the last valid byte (s + count - 1) instead of s + count. This makes the loop stop at the word containing the last valid byte rather than potentially loading the word after it. The count == 0 case is already handled by the beqz early exit. Also add a pre-loop guard (bgeu t0, t4) for the case where all valid bytes fit within the first word. With the adjusted boundary, t4 can equal t0, and entering the loop with stale register state from the first-word processing would produce incorrect results. The final minu clamp ensures the result is still correct when the last loaded word extends past s + count - 1 within the same aligned word. Fixes: 5ba15d419fab ("riscv: lib: add strnlen() implementation") Signed-off-by: Michael Neuling Assisted-by: Claude Opus4.6 High Thinking Link: https://patch.msgid.link/20260413010738.1622423-1-mikey@neuling.org Signed-off-by: Paul Walmsley --- arch/riscv/lib/strnlen.S | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/riscv/lib/strnlen.S b/arch/riscv/lib/strnlen.S index 53afa7b5b314..a8911605c248 100644 --- a/arch/riscv/lib/strnlen.S +++ b/arch/riscv/lib/strnlen.S @@ -83,8 +83,13 @@ strnlen_zbb: sub t3, t3, t2 slli t2, t2, 3 - /* Aligned boundary. */ + /* + * Aligned boundary. Use the address of the last valid byte + * (s + count - 1) to avoid loading a word past the count + * boundary in the loop below. count == 0 is handled above. + */ add t4, a0, a1 + addi t4, t4, -1 andi t4, t4, -SZREG /* Get the first word. */ @@ -120,6 +125,9 @@ strnlen_zbb: bgtu t3, a0, 2f + /* All remaining bytes are in the first word, no loop needed. */ + bgeu t0, t4, 2f + /* Prepare for the word comparison loop. */ addi t2, t0, SZREG li t3, -1 From e9714db8041763f59dde152c812b96b3de05c6d9 Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Fri, 7 Aug 2026 09:55:43 +0200 Subject: [PATCH 049/171] ovpn: run deferred work on a module-owned workqueue ovpn queues several work items whose callbacks execute module text. These works currently run on the global system workqueues, so module exit has no driver-owned drain point that guarantees the callbacks have fully returned before the module text can be freed. Object references protect the objects used by the callbacks, but they do not prove that a workqueue function has returned. In particular, a worker can drop the final reference that unblocks device teardown while it is still executing ovpn code. Add a module-owned workqueue and queue all ovpn work items on it. During module exit, unregister rtnl and netlink first, flush the workqueue so ordinary ovpn workers finish, run the final RCU barrier, and destroy the workqueue last. This keeps the workqueue available for cleanup work queued from RCU callbacks, while ensuring no ovpn work item can outlive the module text. The per-device delayed keepalive work remains explicitly disabled during netdev teardown (disable_delayed_work_sync in ndo_uninit), since flush_workqueue does not flush delayed work that is still only pending on its timer. Fixes: 3ecfd9349f40 ("ovpn: implement keepalive mechanism") Fixes: 11851cbd60ea ("ovpn: implement TCP transport") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.c | 19 ++++++++++++++++++- drivers/net/ovpn/ovpnpriv.h | 4 ++++ drivers/net/ovpn/peer.c | 8 ++++---- drivers/net/ovpn/tcp.c | 9 ++++----- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 168cfe9b59a9..0708249e9607 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,9 @@ #include "tcp.h" #include "udp.h" +/* module-owned workqueue on which all ovpn-specific work is queued */ +struct workqueue_struct *ovpn_wq; + static void ovpn_priv_free(struct net_device *net) { struct ovpn_priv *ovpn = netdev_priv(net); @@ -264,10 +268,16 @@ static int __init ovpn_init(void) ovpn_tcp_init(); + ovpn_wq = alloc_workqueue("ovpn", WQ_PERCPU, 0); + if (!ovpn_wq) { + pr_err("ovpn: cannot allocate workqueue\n"); + return -ENOMEM; + } + err = rtnl_link_register(&ovpn_link_ops); if (err) { pr_err("ovpn: can't register rtnl link ops: %d\n", err); - return err; + goto destroy_wq; } err = ovpn_nl_register(); @@ -280,6 +290,9 @@ static int __init ovpn_init(void) unreg_rtnl: rtnl_link_unregister(&ovpn_link_ops); +destroy_wq: + destroy_workqueue(ovpn_wq); + ovpn_wq = NULL; return err; } @@ -288,7 +301,11 @@ static __exit void ovpn_cleanup(void) ovpn_nl_unregister(); rtnl_link_unregister(&ovpn_link_ops); + flush_workqueue(ovpn_wq); rcu_barrier(); + + destroy_workqueue(ovpn_wq); + ovpn_wq = NULL; } module_init(ovpn_init); diff --git a/drivers/net/ovpn/ovpnpriv.h b/drivers/net/ovpn/ovpnpriv.h index 5898f6adada7..84499140e4bd 100644 --- a/drivers/net/ovpn/ovpnpriv.h +++ b/drivers/net/ovpn/ovpnpriv.h @@ -15,6 +15,10 @@ #include #include +struct workqueue_struct; + +extern struct workqueue_struct *ovpn_wq; + /** * struct ovpn_peer_collection - container of peers for MultiPeer mode * @by_id: table of peers index by ID diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index b0519f9840d8..c95656ca7c35 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -62,7 +62,7 @@ void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout) /* now that interval and timeout have been changed, kick * off the worker so that the next delay can be recomputed */ - mod_delayed_work(system_percpu_wq, &peer->ovpn->keepalive_work, 0); + mod_delayed_work(ovpn_wq, &peer->ovpn->keepalive_work, 0); } /** @@ -1371,7 +1371,7 @@ static time64_t ovpn_peer_keepalive_work_single(struct ovpn_peer *peer, peer->id); if (WARN_ON(!ovpn_peer_hold(peer))) return 0; - if (!schedule_work(&peer->keepalive_work)) + if (!queue_work(ovpn_wq, &peer->keepalive_work)) ovpn_peer_put(peer); } @@ -1463,8 +1463,8 @@ void ovpn_peer_keepalive_work(struct work_struct *work) netdev_dbg(ovpn->dev, "scheduling keepalive work: now=%llu next_run=%llu delta=%llu\n", next_run, now, next_run - now); - schedule_delayed_work(&ovpn->keepalive_work, - (next_run - now) * HZ); + queue_delayed_work(ovpn_wq, &ovpn->keepalive_work, + (next_run - now) * HZ); } unlock_ovpn(ovpn, &release_list); } diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c index 0af14055c39a..8fe8a8e750a4 100644 --- a/drivers/net/ovpn/tcp.c +++ b/drivers/net/ovpn/tcp.c @@ -151,7 +151,7 @@ static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb) /* take reference for deferred peer deletion. should never fail */ if (WARN_ON(!ovpn_peer_hold(peer))) goto err_nopeer; - if (!schedule_work(&peer->tcp.defer_del_work)) + if (!queue_work(ovpn_wq, &peer->tcp.defer_del_work)) ovpn_peer_put(peer); ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); err_nopeer: @@ -284,13 +284,12 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk) * stream therefore we abort the connection */ ovpn_peer_hold(peer); - if (!schedule_work(&peer->tcp.defer_del_work)) + if (!queue_work(ovpn_wq, &peer->tcp.defer_del_work)) ovpn_peer_put(peer); /* we bail out immediately and keep tx_in_progress set * to true. This way we prevent more TX attempts - * which would lead to more invocations of - * schedule_work() + * which would lead to more invocations of queue_work() */ return; } @@ -487,7 +486,7 @@ static void ovpn_tcp_write_space(struct sock *sk) rcu_read_lock(); sock = rcu_dereference_sk_user_data(sk); if (likely(sock && sock->peer)) { - schedule_work(&sock->tcp_tx_work); + queue_work(ovpn_wq, &sock->tcp_tx_work); sock->peer->tcp.sk_cb.sk_write_space(sk); } rcu_read_unlock(); From 2da3dfa1ddfe55a065f484750c83660e3bd4ac00 Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Wed, 29 Jul 2026 12:21:46 +0200 Subject: [PATCH 050/171] ovpn: defer key slot crypto freeing to workqueue Key slots are released through a kref and the existing release path frees the AEAD transforms from an RCU callback. That is not safe for all crypto implementations: crypto_free_aead can sleep, for example when an async or hardware implementation has teardown work to complete. Use queue_rcu_work for key-slot release. This keeps the RCU grace period needed by lockless key-slot readers, but runs the actual crypto teardown from workqueue context where sleeping is allowed. Once the rcu_work callback runs, pre-existing RCU readers are gone, and the final kref put already proves that no transform user remains, so the worker can release the AEAD transforms and free the slot directly. The previous patch drains ovpn_wq during module exit, so queued key-slot teardown work cannot outlive module text. Fixes: 8534731dbf2d ("ovpn: implement packet processing") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/crypto.c | 10 +--------- drivers/net/ovpn/crypto.h | 4 +++- drivers/net/ovpn/crypto_aead.c | 19 ++++++++++++++----- drivers/net/ovpn/crypto_aead.h | 1 - 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/net/ovpn/crypto.c b/drivers/net/ovpn/crypto.c index 2e95f29514fc..7e545428900a 100644 --- a/drivers/net/ovpn/crypto.c +++ b/drivers/net/ovpn/crypto.c @@ -18,20 +18,12 @@ #include "crypto_aead.h" #include "crypto.h" -static void ovpn_ks_destroy_rcu(struct rcu_head *head) -{ - struct ovpn_crypto_key_slot *ks; - - ks = container_of(head, struct ovpn_crypto_key_slot, rcu); - ovpn_aead_crypto_key_slot_destroy(ks); -} - void ovpn_crypto_key_slot_release(struct kref *kref) { struct ovpn_crypto_key_slot *ks; ks = container_of(kref, struct ovpn_crypto_key_slot, refcount); - call_rcu(&ks->rcu, ovpn_ks_destroy_rcu); + queue_rcu_work(ovpn_wq, &ks->free_work); } /* can only be invoked when all peer references have been dropped (i.e. RCU diff --git a/drivers/net/ovpn/crypto.h b/drivers/net/ovpn/crypto.h index 0e284fec3a75..e3feb16d5498 100644 --- a/drivers/net/ovpn/crypto.h +++ b/drivers/net/ovpn/crypto.h @@ -10,6 +10,8 @@ #ifndef _NET_OVPN_OVPNCRYPTO_H_ #define _NET_OVPN_OVPNCRYPTO_H_ +#include + #include "pktid.h" #include "proto.h" @@ -45,8 +47,8 @@ struct ovpn_crypto_key_slot { struct ovpn_pktid_recv pid_recv ____cacheline_aligned_in_smp; struct ovpn_pktid_xmit pid_xmit ____cacheline_aligned_in_smp; + struct rcu_work free_work; struct kref refcount; - struct rcu_head rcu; }; struct ovpn_crypto_state { diff --git a/drivers/net/ovpn/crypto_aead.c b/drivers/net/ovpn/crypto_aead.c index 8f07c418622b..74eaf6fac2f5 100644 --- a/drivers/net/ovpn/crypto_aead.c +++ b/drivers/net/ovpn/crypto_aead.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -380,13 +381,19 @@ static struct crypto_aead *ovpn_aead_init(const char *title, return ERR_PTR(ret); } -void ovpn_aead_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks) +static void ovpn_aead_crypto_key_slot_free(struct ovpn_crypto_key_slot *ks) { - if (!ks) - return; - crypto_free_aead(ks->encrypt); crypto_free_aead(ks->decrypt); +} + +static void ovpn_aead_crypto_key_slot_free_work(struct work_struct *work) +{ + struct ovpn_crypto_key_slot *ks; + + ks = container_of(to_rcu_work(work), struct ovpn_crypto_key_slot, + free_work); + ovpn_aead_crypto_key_slot_free(ks); kfree(ks); } @@ -420,6 +427,7 @@ ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc) ks->encrypt = NULL; ks->decrypt = NULL; + INIT_RCU_WORK(&ks->free_work, ovpn_aead_crypto_key_slot_free_work); kref_init(&ks->refcount); ks->key_id = kc->key_id; @@ -453,7 +461,8 @@ ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc) return ks; destroy_ks: - ovpn_aead_crypto_key_slot_destroy(ks); + ovpn_aead_crypto_key_slot_free(ks); + kfree(ks); return ERR_PTR(ret); } diff --git a/drivers/net/ovpn/crypto_aead.h b/drivers/net/ovpn/crypto_aead.h index 65a2ff307898..fae3b585a43b 100644 --- a/drivers/net/ovpn/crypto_aead.h +++ b/drivers/net/ovpn/crypto_aead.h @@ -22,7 +22,6 @@ int ovpn_aead_decrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks, struct ovpn_crypto_key_slot * ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc); -void ovpn_aead_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks); enum ovpn_cipher_alg ovpn_aead_crypto_alg(struct ovpn_crypto_key_slot *ks); From 63981fc786daaa626cb14d9be1406f674d79f98f Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sun, 2 Aug 2026 20:00:43 +0200 Subject: [PATCH 051/171] ovl: don't warn when the mount is completed from another user namespace fsopen() records the caller's user namespace in fc->user_ns and hands back an ordinary file descriptor. Nothing ties the task that calls fsconfig(FSCONFIG_CMD_CREATE) to the task that created the context. The fd is inherited across fork() and exec() and it can be passed over a unix socket. Completing a context from another user namespace is allowed on purpose. vfs_cmd_create() authorizes the create with mount_capable(), which for FS_USERNS_MOUNT checks ns_capable(fc->user_ns, CAP_SYS_ADMIN), and that succeeds for a task holding CAP_SYS_ADMIN in an ancestor of fc->user_ns. So an unprivileged task can reach the WARN_ON() in ovl_fill_super(): create a user and a mount namespace in a child, call fsopen("overlay") there, send the fscontext fd to the parent and let the parent issue FSCONFIG_CMD_CREATE. Both namespaces come from a plain unshare(1) and no capability is needed anywhere: WARNING: fs/overlayfs/super.c:1551 at ovl_fill_super+0x7b9/0x1e20 [overlay] CPU: 3 UID: 1000 PID: 3243376 Comm: fswarn Call Trace: get_tree_nodev+0x71/0xa0 ovl_get_tree+0x15/0x20 [overlay] vfs_get_tree+0x2a/0x100 vfs_cmd_create+0x60/0xf0 __do_sys_fsconfig+0x4b2/0x500 The child needs the mount namespace because fsopen() itself gates on may_mount(), which asks for CAP_SYS_ADMIN in the user namespace owning the caller's mount namespace. fsconfig() doesn't repeat that check. It is a WARN_ON() and not a WARN_ON_ONCE(), so the condition can be raised in a loop to taint the kernel and flood the log, and it panics a kernel booted with panic_on_warn. Keep refusing the mount and stop warning about it. ovl_parse_param() already spells a user namespace check this way for Opt_override_creds. Fixes: 1784fbc2ed9c ("ovl: port to new mount api") Cc: stable@vger.kernel.org # v6.5+ Link: https://patch.msgid.link/20260802-work-fill_super-warn-v1-1-4e987911a39a@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/overlayfs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 60f0b7ceef0a..60b808b85fc4 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1544,7 +1544,8 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) int err; err = -EIO; - if (WARN_ON(fc->user_ns != current_user_ns())) + /* The fscontext fd may have been passed to another user namespace. */ + if (fc->user_ns != current_user_ns()) goto out_err; ovl_set_d_op(sb); From 79fdf39f1a31f88cb3833b6f8091fbf6acdca2c6 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sun, 2 Aug 2026 20:00:44 +0200 Subject: [PATCH 052/171] binfmt_misc: don't warn when the mount is completed from another user namespace fsopen() records the caller's user namespace in fc->user_ns and hands back an ordinary file descriptor. Nothing ties the task that calls fsconfig(FSCONFIG_CMD_CREATE) to the task that created the context. The fd is inherited across fork() and exec() and it can be passed over a unix socket. Completing a context from another user namespace is allowed on purpose. vfs_cmd_create() authorizes the create with mount_capable(), which for FS_USERNS_MOUNT checks ns_capable(fc->user_ns, CAP_SYS_ADMIN), and that succeeds for a task holding CAP_SYS_ADMIN in an ancestor of fc->user_ns. So an unprivileged task can reach the WARN_ON() in bm_fill_super(): create a user and a mount namespace in a child, call fsopen("binfmt_misc") there, send the fscontext fd to the parent and let the parent issue FSCONFIG_CMD_CREATE. Both namespaces come from a plain unshare(1) and no capability is needed anywhere: WARNING: fs/binfmt_misc.c:938 at bm_fill_super+0xa2/0xc0 [binfmt_misc] CPU: 15 UID: 1000 PID: 3243382 Comm: fswarn Call Trace: get_tree_keyed+0x7d/0xb0 bm_get_tree+0x34/0x90 [binfmt_misc] vfs_get_tree+0x2a/0x100 vfs_cmd_create+0x60/0xf0 __do_sys_fsconfig+0x4b2/0x500 The child needs the mount namespace because fsopen() itself gates on may_mount(), which asks for CAP_SYS_ADMIN in the user namespace owning the caller's mount namespace. fsconfig() doesn't repeat that check. It is a WARN_ON() and not a WARN_ON_ONCE(), so the condition can be raised in a loop to taint the kernel and flood the log, and it panics a kernel booted with panic_on_warn. Keep refusing the mount and stop warning about it. Nothing in bm_fill_super() depends on the two namespaces matching, it derives everything from sb->s_user_ns. Fixes: 21ca59b365c0 ("binfmt_misc: enable sandboxed mounts") Cc: stable@vger.kernel.org # v6.7+ Link: https://patch.msgid.link/20260802-work-fill_super-warn-v1-2-4e987911a39a@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index c97f10b48b5b..613dd28e3f1a 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -937,7 +937,8 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc) /* last one */ {""} }; - if (WARN_ON(user_ns != current_user_ns())) + /* The fscontext fd may have been passed to another user namespace. */ + if (user_ns != current_user_ns()) return -EINVAL; /* Never exec off this instance and never let anything stack on it. */ From d64ba78b749ad6bae0e9a6f40614eda1c7db2f04 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sun, 2 Aug 2026 20:00:45 +0200 Subject: [PATCH 053/171] selftests/filesystems: test completing a context from another user namespace fsopen() records the caller's user namespace in fc->user_ns and hands back an ordinary file descriptor, so the task that issues FSCONFIG_CMD_CREATE need not be the one that created the context. mount_capable() authorizes that for a caller holding CAP_SYS_ADMIN in an ancestor of fc->user_ns, which any unprivileged user has over a user namespace it just created. binfmt_misc and overlayfs used to WARN_ON() the mismatch. Add a test for both. Also cover the handover within one user namespace. That is a supported thing to do and has to keep working. Link: https://patch.msgid.link/20260802-work-fill_super-warn-v1-3-4e987911a39a@kernel.org Signed-off-by: Christian Brauner (Amutable) --- tools/testing/selftests/Makefile | 1 + .../filesystems/fscontext_ns/Makefile | 10 + .../fscontext_ns/fscontext_ns_test.c | 239 ++++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 tools/testing/selftests/filesystems/fscontext_ns/Makefile create mode 100644 tools/testing/selftests/filesystems/fscontext_ns/fscontext_ns_test.c diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 8d4db2241cc2..b622052ec3e9 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -42,6 +42,7 @@ TARGETS += filesystems/fuse TARGETS += filesystems/move_mount TARGETS += filesystems/empty_mntns TARGETS += filesystems/fsmount_ns +TARGETS += filesystems/fscontext_ns TARGETS += firmware TARGETS += fpu TARGETS += ftrace diff --git a/tools/testing/selftests/filesystems/fscontext_ns/Makefile b/tools/testing/selftests/filesystems/fscontext_ns/Makefile new file mode 100644 index 000000000000..7e3506294757 --- /dev/null +++ b/tools/testing/selftests/filesystems/fscontext_ns/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +TEST_GEN_PROGS := fscontext_ns_test + +CFLAGS += -Wall -O0 -g $(KHDR_INCLUDES) $(TOOLS_INCLUDES) +LDLIBS := -lcap + +include ../../lib.mk + +$(OUTPUT)/fscontext_ns_test: fscontext_ns_test.c ../utils.c + $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) diff --git a/tools/testing/selftests/filesystems/fscontext_ns/fscontext_ns_test.c b/tools/testing/selftests/filesystems/fscontext_ns/fscontext_ns_test.c new file mode 100644 index 000000000000..0f30a3e6e197 --- /dev/null +++ b/tools/testing/selftests/filesystems/fscontext_ns/fscontext_ns_test.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Christian Brauner + * + * Test that completing a filesystem context from another user namespace + * doesn't warn. + * + * fsopen() records the caller's user namespace in fc->user_ns and hands + * back an ordinary file descriptor. The task that issues + * FSCONFIG_CMD_CREATE need not be the one that created the context: the fd + * is inherited across fork() and exec() and it can be passed over a unix + * socket. vfs_cmd_create() authorizes the create with mount_capable(), + * which for FS_USERNS_MOUNT checks ns_capable(fc->user_ns, CAP_SYS_ADMIN), + * and that succeeds for a task holding CAP_SYS_ADMIN in an ancestor of + * fc->user_ns. + * + * binfmt_misc and overlayfs used to WARN_ON() that mismatch, which let an + * unprivileged user taint the kernel, flood the log and panic a kernel + * booted with panic_on_warn. The mount must still be refused, but it must + * not warn. + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../wrappers.h" +#include "../utils.h" +#include "../../kselftest_harness.h" + +#ifndef FSCONFIG_CMD_CREATE +#define FSCONFIG_CMD_CREATE 6 +#endif + +/* TAINT_WARN, i.e. bit 9 of /proc/sys/kernel/tainted. */ +#define TAINT_WARN_BIT 9 + +static bool taint_warn_set(void) +{ + unsigned long taint = 0; + FILE *f; + + f = fopen("/proc/sys/kernel/tainted", "r"); + if (!f) + return false; + if (fscanf(f, "%lu", &taint) != 1) + taint = 0; + fclose(f); + + return taint & (1UL << TAINT_WARN_BIT); +} + +static int send_fd(int sock, int fd) +{ + char cmsgbuf[CMSG_SPACE(sizeof(int))] = {}; + char b[1] = { 'x' }; + struct iovec iov = { .iov_base = b, .iov_len = sizeof(b) }; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsgbuf, + .msg_controllen = sizeof(cmsgbuf), + }; + struct cmsghdr *cmsg; + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + + return sendmsg(sock, &msg, 0) < 0 ? -1 : 0; +} + +static int recv_fd(int sock) +{ + char cmsgbuf[CMSG_SPACE(sizeof(int))] = {}; + char b[1]; + struct iovec iov = { .iov_base = b, .iov_len = sizeof(b) }; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsgbuf, + .msg_controllen = sizeof(cmsgbuf), + }; + struct cmsghdr *cmsg; + int fd = -1; + + if (recvmsg(sock, &msg, 0) <= 0) + return -1; + + cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS) + return -1; + memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); + + return fd; +} + +/* + * Create a context for @fsname in a child and complete it here. With @nest + * the child first creates its own user namespace, so that the context is + * created in a descendant of the namespace completing it. The child needs a + * mount namespace of its own as well: fsopen() gates on may_mount(), which + * asks for CAP_SYS_ADMIN in the user namespace owning the caller's mount + * namespace. + * + * Returns the result of FSCONFIG_CMD_CREATE with errno set, or -ENODATA if + * the child could not create the context at all. + */ +static int create_from_child(const char *fsname, bool nest) +{ + int sock[2], fd, ret, status; + pid_t pid; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock)) + return -ENODATA; + + pid = fork(); + if (pid < 0) { + close(sock[0]); + close(sock[1]); + return -ENODATA; + } + + if (pid == 0) { + close(sock[0]); + + if (nest && unshare(CLONE_NEWUSER | CLONE_NEWNS)) + _exit(1); + + fd = sys_fsopen(fsname, 0); + if (fd < 0) + _exit(1); + if (send_fd(sock[1], fd)) + _exit(1); + _exit(0); + } + + close(sock[1]); + fd = recv_fd(sock[0]); + close(sock[0]); + wait_for_pid(pid); + waitpid(pid, &status, WNOHANG); + + if (fd < 0) + return -ENODATA; + + errno = 0; + ret = sys_fsconfig(fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0); + status = errno; + close(fd); + errno = status; + + return ret; +} + +FIXTURE(fscontext_ns) { + bool warn_before; +}; + +FIXTURE_SETUP(fscontext_ns) +{ + self->warn_before = taint_warn_set(); + + if (setup_userns() != 0) + SKIP(return, "setup_userns failed"); +} + +FIXTURE_TEARDOWN(fscontext_ns) +{ +} + +/* + * The condition the kernel used to WARN about. It has to be refused, and it + * has to be refused quietly: an unprivileged task reaches this. + */ +FIXTURE_VARIANT(fscontext_ns) { + const char *fsname; + int expected_errno; +}; + +FIXTURE_VARIANT_ADD(fscontext_ns, binfmt_misc) { + .fsname = "binfmt_misc", + .expected_errno = EINVAL, +}; + +FIXTURE_VARIANT_ADD(fscontext_ns, overlay) { + .fsname = "overlay", + .expected_errno = EIO, +}; + +TEST_F(fscontext_ns, create_from_descendant_userns) +{ + int ret; + + ret = create_from_child(variant->fsname, true); + if (ret == -ENODATA) + SKIP(return, "%s unavailable", variant->fsname); + + ASSERT_EQ(-1, ret); + ASSERT_EQ(variant->expected_errno, errno); + + /* + * Only meaningful if nothing had warned before us. Note that an + * unrelated warning racing this test would look like a failure. + */ + if (self->warn_before) + TH_LOG("TAINT_WARN already set, not checking for a new warning"); + else + ASSERT_FALSE(taint_warn_set()); +} + +/* + * The same handover within one user namespace is a supported thing to do and + * has to keep working. binfmt_misc takes no options, so the create succeeds + * outright and this also shows the test really drives the create path. + */ +TEST(create_from_same_userns) +{ + int ret; + + if (setup_userns() != 0) + SKIP(return, "setup_userns failed"); + + ret = create_from_child("binfmt_misc", false); + if (ret == -ENODATA) + SKIP(return, "binfmt_misc unavailable"); + + ASSERT_EQ(0, ret); +} + +TEST_HARNESS_MAIN From ada54c2ba652348c590d1ace6a2f4ff77cbbf809 Mon Sep 17 00:00:00 2001 From: Niels Pressel Date: Sun, 2 Aug 2026 14:44:23 +0200 Subject: [PATCH 054/171] rseq: Prevent hard lockup on granted time slice extension __exit_to_user_mode_loop() invokes rseq_grant_timeslice_extension() with interrupts enabled. If the extension is granted it invokes hrtimer_rearm_deferred_tif() to ensure that a pending deferred hrtimer rearm is handled before exiting to user space. Though this invokes __hrtimer_rearm_deferred() which expects to be invoked with interrupts disabled as it takes hrtimer_cpu_base::lock with raw_spin_lock(). That's a livelock waiting to happen and caught by lockdep: WARNING: ./include/linux/hrtimer_rearm.h:17 at irqentry_exit, CPU#1: slice_test WARNING: inconsistent lock state inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage. Prevent this by disabling interrupts around the invocation of hrtimer_rearm_deferred_tif() in rseq_grant_timeslice_extension(). [ tglx: Massaged change log ] Fixes: 15dd3a948855 ("hrtimer: Push reprogramming timers into the interrupt return path") Signed-off-by: Niels Pressel Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260802124423.51616-1-npressel@ethz.ch --- include/linux/rseq_entry.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/rseq_entry.h b/include/linux/rseq_entry.h index ed9da6e41a2a..31ce349ed42c 100644 --- a/include/linux/rseq_entry.h +++ b/include/linux/rseq_entry.h @@ -233,6 +233,7 @@ static __always_inline bool __rseq_grant_slice_extension(bool work_pending) static __always_inline bool rseq_grant_slice_extension(unsigned long ti_work, unsigned long mask) { if (unlikely(__rseq_grant_slice_extension(ti_work & mask))) { + guard(irq)(); hrtimer_rearm_deferred_tif(ti_work); return true; } From 600411ea1f2443fdf5b1af9b6480f616d7aff9d0 Mon Sep 17 00:00:00 2001 From: Junjie Cao Date: Fri, 31 Jul 2026 11:27:47 +0800 Subject: [PATCH 055/171] gpio: ml-ioh: use raw_spinlock_t for the register lock ioh_irq_type() is registered as the irq_chip .irq_set_type callback and takes chip->spinlock with spin_lock_irqsave(). This callback is reached from __setup_irq() -> __irq_set_trigger() -> chip->irq_set_type() while the caller holds desc->lock, a raw_spinlock_t, with hardirqs disabled. That context is not sleepable, but on PREEMPT_RT a regular spinlock_t is an rtmutex-backed sleeping lock, so acquiring it there is invalid. ioh_irq_enable() and ioh_irq_disable() take the same lock from the .irq_enable/.irq_disable callbacks, which are likewise invoked with desc->lock held. Convert the register lock to raw_spinlock_t. The same lock also serializes the GPIO direction/value callbacks and the suspend/resume register save/restore, and those critical sections only perform short sequences of MMIO register accesses (ioread32()/iowrite32()); the .irq_set_type callback additionally emits a dev_warn() on an unsupported type. None of these are sleepable operations, so keeping this register lock non-sleeping is appropriate for the irqchip callbacks and does not change the GPIO-side locking contract. This is the same fix as commit a02b8950d619 ("gpio: pch: use raw_spinlock_t for the register lock"); this driver shares the same structure as gpio-pch. Fixes: 54be566317b6 ("gpio-ml-ioh: Support interrupt function") Cc: stable@vger.kernel.org Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260731032747.2987292-1-junjie.cao@intel.com Signed-off-by: Junjie Cao --- drivers/gpio/gpio-ml-ioh.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 6576e5dcb0ee..0a9d34b7636e 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -84,7 +84,7 @@ struct ioh_gpio { u32 gpio_use_sel; int ch; int irq_base; - spinlock_t spinlock; + raw_spinlock_t spinlock; }; static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12}; @@ -95,7 +95,7 @@ static int ioh_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) struct ioh_gpio *chip = gpiochip_get_data(gpio); unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); reg_val = ioread32(&chip->reg->regs[chip->ch].po); if (val) reg_val |= BIT(nr); @@ -103,7 +103,7 @@ static int ioh_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) reg_val &= ~BIT(nr); iowrite32(reg_val, &chip->reg->regs[chip->ch].po); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -123,7 +123,7 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, u32 reg_val; unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); pm = ioread32(&chip->reg->regs[chip->ch].pm); pm &= BIT(num_ports[chip->ch]) - 1; pm |= BIT(nr); @@ -136,7 +136,7 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, reg_val &= ~BIT(nr); iowrite32(reg_val, &chip->reg->regs[chip->ch].po); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -147,12 +147,12 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) u32 pm; unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); pm = ioread32(&chip->reg->regs[chip->ch].pm); pm &= BIT(num_ports[chip->ch]) - 1; pm &= ~BIT(nr); iowrite32(pm, &chip->reg->regs[chip->ch].pm); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -256,7 +256,7 @@ static int ioh_irq_type(struct irq_data *d, unsigned int type) dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d type=%d\n", __func__, irq, type, ch, im_pos, type); - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); switch (type) { case IRQ_TYPE_EDGE_RISING: @@ -296,7 +296,7 @@ static int ioh_irq_type(struct irq_data *d, unsigned int type) ien = ioread32(&chip->reg->regs[chip->ch].ien); iowrite32(ien | BIT(ch), &chip->reg->regs[chip->ch].ien); end: - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -326,11 +326,11 @@ static void ioh_irq_disable(struct irq_data *d) unsigned long flags; u32 ien; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); ien = ioread32(&chip->reg->regs[chip->ch].ien); ien &= ~BIT(d->irq - chip->irq_base); iowrite32(ien, &chip->reg->regs[chip->ch].ien); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); } static void ioh_irq_enable(struct irq_data *d) @@ -340,11 +340,11 @@ static void ioh_irq_enable(struct irq_data *d) unsigned long flags; u32 ien; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); ien = ioread32(&chip->reg->regs[chip->ch].ien); ien |= BIT(d->irq - chip->irq_base); iowrite32(ien, &chip->reg->regs[chip->ch].ien); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); } static irqreturn_t ioh_gpio_handler(int irq, void *dev_id) @@ -440,7 +440,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, chip->base = base; chip->reg = chip->base; chip->ch = i; - spin_lock_init(&chip->spinlock); + raw_spin_lock_init(&chip->spinlock); ioh_gpio_setup(chip, num_ports[i]); ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); if (ret) { @@ -484,9 +484,9 @@ static int ioh_gpio_suspend(struct device *dev) struct ioh_gpio *chip = dev_get_drvdata(dev); unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); ioh_gpio_save_reg_conf(chip); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -496,11 +496,11 @@ static int ioh_gpio_resume(struct device *dev) struct ioh_gpio *chip = dev_get_drvdata(dev); unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); iowrite32(0x01, &chip->reg->srst); iowrite32(0x00, &chip->reg->srst); ioh_gpio_restore_reg_conf(chip); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } From a9253ee6771c8ab3c6de07ea75d9e2c1cef3cd97 Mon Sep 17 00:00:00 2001 From: Junjie Cao Date: Tue, 4 Aug 2026 17:59:35 +0800 Subject: [PATCH 056/171] gpio: ml-ioh: share the register lock across channels Suspend and resume hold channel 0's lock while saving and restoring registers for all eight channels. Code paths using the other seven locks can therefore run concurrently with PM. Use one controller-wide lock shared by all channels. Fixes: b490fa0bf86e ("gpio-ml-ioh: Fix suspend/resume issue") Reported-by: sashiko-bot Link: https://lore.kernel.org/r/20260731033956.EE6F61F000E9@smtp.kernel.org Signed-off-by: Junjie Cao Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260804095935.2132215-1-junjie.cao@intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ml-ioh.c | 69 ++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 0a9d34b7636e..391188c0f2c2 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -73,7 +73,7 @@ struct ioh_gpio_reg_data { * @gpio_use_sel: Save GPIO_USE_SEL1~4 register for PM * @ch: Indicate GPIO channel * @irq_base: Save base of IRQ number for interrupt - * @spinlock: Used for register access protection + * @spinlock: Shared register access lock */ struct ioh_gpio { void __iomem *base; @@ -84,7 +84,12 @@ struct ioh_gpio { u32 gpio_use_sel; int ch; int irq_base; + raw_spinlock_t *spinlock; +}; + +struct ioh_gpio_device { raw_spinlock_t spinlock; + struct ioh_gpio chip[8]; }; static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12}; @@ -95,7 +100,7 @@ static int ioh_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) struct ioh_gpio *chip = gpiochip_get_data(gpio); unsigned long flags; - raw_spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(chip->spinlock, flags); reg_val = ioread32(&chip->reg->regs[chip->ch].po); if (val) reg_val |= BIT(nr); @@ -103,7 +108,7 @@ static int ioh_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) reg_val &= ~BIT(nr); iowrite32(reg_val, &chip->reg->regs[chip->ch].po); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(chip->spinlock, flags); return 0; } @@ -123,7 +128,7 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, u32 reg_val; unsigned long flags; - raw_spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(chip->spinlock, flags); pm = ioread32(&chip->reg->regs[chip->ch].pm); pm &= BIT(num_ports[chip->ch]) - 1; pm |= BIT(nr); @@ -136,7 +141,7 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, reg_val &= ~BIT(nr); iowrite32(reg_val, &chip->reg->regs[chip->ch].po); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(chip->spinlock, flags); return 0; } @@ -147,12 +152,12 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) u32 pm; unsigned long flags; - raw_spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(chip->spinlock, flags); pm = ioread32(&chip->reg->regs[chip->ch].pm); pm &= BIT(num_ports[chip->ch]) - 1; pm &= ~BIT(nr); iowrite32(pm, &chip->reg->regs[chip->ch].pm); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(chip->spinlock, flags); return 0; } @@ -256,7 +261,7 @@ static int ioh_irq_type(struct irq_data *d, unsigned int type) dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d type=%d\n", __func__, irq, type, ch, im_pos, type); - raw_spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(chip->spinlock, flags); switch (type) { case IRQ_TYPE_EDGE_RISING: @@ -296,7 +301,7 @@ static int ioh_irq_type(struct irq_data *d, unsigned int type) ien = ioread32(&chip->reg->regs[chip->ch].ien); iowrite32(ien | BIT(ch), &chip->reg->regs[chip->ch].ien); end: - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(chip->spinlock, flags); return 0; } @@ -326,11 +331,11 @@ static void ioh_irq_disable(struct irq_data *d) unsigned long flags; u32 ien; - raw_spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(chip->spinlock, flags); ien = ioread32(&chip->reg->regs[chip->ch].ien); ien &= ~BIT(d->irq - chip->irq_base); iowrite32(ien, &chip->reg->regs[chip->ch].ien); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(chip->spinlock, flags); } static void ioh_irq_enable(struct irq_data *d) @@ -340,11 +345,11 @@ static void ioh_irq_enable(struct irq_data *d) unsigned long flags; u32 ien; - raw_spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(chip->spinlock, flags); ien = ioread32(&chip->reg->regs[chip->ch].ien); ien |= BIT(d->irq - chip->irq_base); iowrite32(ien, &chip->reg->regs[chip->ch].ien); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(chip->spinlock, flags); } static irqreturn_t ioh_gpio_handler(int irq, void *dev_id) @@ -407,8 +412,8 @@ static int ioh_gpio_probe(struct pci_dev *pdev, int ret; int i, j; struct ioh_gpio *chip; + struct ioh_gpio_device *priv; void __iomem *base; - void *chip_save; int irq_base; ret = pcim_enable_device(pdev); @@ -429,18 +434,18 @@ static int ioh_gpio_probe(struct pci_dev *pdev, return -ENOMEM; } - chip_save = devm_kcalloc(dev, 8, sizeof(*chip), GFP_KERNEL); - if (chip_save == NULL) { + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM; - } - chip = chip_save; + raw_spin_lock_init(&priv->spinlock); + chip = priv->chip; for (i = 0; i < 8; i++, chip++) { chip->dev = dev; chip->base = base; chip->reg = chip->base; chip->ch = i; - raw_spin_lock_init(&chip->spinlock); + chip->spinlock = &priv->spinlock; ioh_gpio_setup(chip, num_ports[i]); ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); if (ret) { @@ -449,7 +454,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, } } - chip = chip_save; + chip = priv->chip; for (j = 0; j < 8; j++, chip++) { irq_base = devm_irq_alloc_descs(dev, -1, IOH_IRQ_BASE, num_ports[j], NUMA_NO_NODE); @@ -466,7 +471,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, return ret; } - chip = chip_save; + chip = priv->chip; ret = devm_request_irq(dev, pdev->irq, ioh_gpio_handler, IRQF_SHARED, KBUILD_MODNAME, chip); if (ret != 0) { @@ -474,33 +479,33 @@ static int ioh_gpio_probe(struct pci_dev *pdev, return ret; } - pci_set_drvdata(pdev, chip); + pci_set_drvdata(pdev, priv); return 0; } static int ioh_gpio_suspend(struct device *dev) { - struct ioh_gpio *chip = dev_get_drvdata(dev); + struct ioh_gpio_device *priv = dev_get_drvdata(dev); unsigned long flags; - raw_spin_lock_irqsave(&chip->spinlock, flags); - ioh_gpio_save_reg_conf(chip); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_lock_irqsave(&priv->spinlock, flags); + ioh_gpio_save_reg_conf(priv->chip); + raw_spin_unlock_irqrestore(&priv->spinlock, flags); return 0; } static int ioh_gpio_resume(struct device *dev) { - struct ioh_gpio *chip = dev_get_drvdata(dev); + struct ioh_gpio_device *priv = dev_get_drvdata(dev); unsigned long flags; - raw_spin_lock_irqsave(&chip->spinlock, flags); - iowrite32(0x01, &chip->reg->srst); - iowrite32(0x00, &chip->reg->srst); - ioh_gpio_restore_reg_conf(chip); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_lock_irqsave(&priv->spinlock, flags); + iowrite32(0x01, &priv->chip->reg->srst); + iowrite32(0x00, &priv->chip->reg->srst); + ioh_gpio_restore_reg_conf(priv->chip); + raw_spin_unlock_irqrestore(&priv->spinlock, flags); return 0; } From 44f3468a0aef1aabdad551898ab7cfa2a9d20e99 Mon Sep 17 00:00:00 2001 From: Cengiz Can Date: Fri, 31 Jul 2026 01:02:57 +0300 Subject: [PATCH 057/171] gpio: sloppy-logic-analyzer: fix use-after-free via debugfs trigger on unbind The "trigger" debugfs file has a hand-rolled ->write handler (trigger_write()) that dereferences the per-device gpio_la_poll_priv. The file is created with debugfs_create_file_unsafe(), and the handler never takes a debugfs reference. Nothing keeps the object alive while the handler runs. priv is allocated with devm_kzalloc(). devres frees it when the platform device is unbound. debugfs_create_file_unsafe() installs no full_proxy wrapper, so debugfs_remove_recursive() in gpio_la_poll_remove() does not wait for an in-flight trigger_write(). The blob_lock taken there does not help, because trigger_write() never takes it. A write that races an unbind therefore writes into freed memory: trigger_write() gpio_la_poll_remove() priv = m->private buf = memdup_user() [may sleep] mutex_lock(&priv->blob_lock) debugfs_remove_recursive() [no wait] mutex_unlock(&priv->blob_lock) (remove returns; devres frees priv) priv->trig_data = buf <-- use-after-free write priv->trig_len = count The race is reachable by root via /sys/bus/platform/drivers/gpio-sloppy-logic-analyzer/unbind. Create "trigger" with debugfs_create_file() instead. Its full_proxy wrapper makes debugfs_remove_recursive() drain any in-flight ->write before it returns. The use-after-free is confirmed under KASAN with a minimal reproducer of the same debugfs_create_file_unsafe() plus devm_kzalloc() pattern (available on request); it produces a slab-use-after-free write in the handler. Fixes: 7828b7bbbf20 ("gpio: add sloppy logic analyzer using polling") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Cengiz Can Reviewed-by: Wolfram Sang Link: https://patch.msgid.link/20260730220258.358169-2-cengiz.can@canonical.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sloppy-logic-analyzer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c index aa23b5779802..2f9a1f0384ad 100644 --- a/drivers/gpio/gpio-sloppy-logic-analyzer.c +++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c @@ -300,7 +300,7 @@ static int gpio_la_poll_probe(struct platform_device *pdev) debugfs_create_ulong("delay_ns_acquisition", 0400, priv->debug_dir, &priv->acq_delay); debugfs_create_file_unsafe("buf_size", 0600, priv->debug_dir, priv, &fops_buf_size); debugfs_create_file_unsafe("capture", 0200, priv->debug_dir, priv, &fops_capture); - debugfs_create_file_unsafe("trigger", 0200, priv->debug_dir, priv, &fops_trigger); + debugfs_create_file("trigger", 0200, priv->debug_dir, priv, &fops_trigger); return 0; } From 566fec6a33075a0ea5c441c26571221f17f4ed98 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Fri, 7 Aug 2026 08:03:04 +0800 Subject: [PATCH 058/171] ASoC: tas2781: fix clang build error for goto bypassing cleanup variable Remove invalid goto exit paths that jump across guard(mutex) cleanup variable initialization, replace them with direct kfree(src) and return, to fix the s390 clang build error in acoustic_ctl_write(). Fixes: d75d38dc4604 ("ASoC: tas2781: Add a debugfs node for acoustic tuning") Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20260807000304.826-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-i2c.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 209067e98e1f..95d875c44b91 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -1544,8 +1544,8 @@ static ssize_t acoustic_ctl_write(struct file *file, if (src[0] > max_pkg_len && src[0] != count) { dev_err(priv->dev, "pkg(%u), max(%u), count(%u) mismatch.\n", src[0], max_pkg_len, (unsigned int)count); - ret = 0; - goto exit; + kfree(src); + return 0; } switch (src[1]) { @@ -1559,14 +1559,14 @@ static ssize_t acoustic_ctl_write(struct file *file, break; default: dev_err(priv->dev, "%s Wrong code %02x.\n", __func__, src[1]); - ret = 0; - goto exit; + kfree(src); + return 0; } if (len < 1) { dev_err(priv->dev, "pkg fmt invalid %02x.\n", len); - ret = 0; - goto exit; + kfree(src); + return 0; } for (j = 0; j < priv->ndev; j++) @@ -1576,8 +1576,8 @@ static ssize_t acoustic_ctl_write(struct file *file, } if (j >= priv->ndev) { dev_err(priv->dev, "no such device 0x%02x.\n", src[2]); - ret = 0; - goto exit; + kfree(src); + return 0; } reg = TASDEVICE_REG(src[3], src[4], src[5]); @@ -1608,7 +1608,7 @@ static ssize_t acoustic_ctl_write(struct file *file, dev_err(priv->dev, "i2c communication error.\n"); else ret = count; -exit: + kfree(src); return ret; } From fe21db8c5e7c2a9815a9be54a1f5d556f905506e Mon Sep 17 00:00:00 2001 From: Lin Xianglin <1021538027@qq.com> Date: Fri, 7 Aug 2026 22:19:43 +0800 Subject: [PATCH 059/171] ASoC: amd: yc: Add DMI quirk for HyperX OMEN Gaming Laptop 16-ap1xxx The HyperX OMEN Gaming Laptop 16-ap1xxx (HP board 8F06) has an internal digital microphone array attached to the AMD ACP PDM controller, but the acp6x machine driver does not register the DMIC sound card because this board is missing from the DMI quirk table, leaving the internal microphone unusable. Add a DMI quirk entry for the HP board "8F06" so the acp6x DMIC capture card gets registered. Signed-off-by: Lin Xianglin <1021538027@qq.com> Link: https://patch.msgid.link/tencent_428392223C2AD3BF23E7ABAA7521FE5C0C07@qq.com Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 14b970f16020..ae90e9f513d1 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -731,6 +731,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "8E35"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_BOARD_NAME, "8F06"), + } + }, { .driver_data = &acp6x_card, .matches = { From 8560d458509f798d8518d11cfee5c5cc58170558 Mon Sep 17 00:00:00 2001 From: Balamurugan C Date: Thu, 6 Aug 2026 18:57:41 +0800 Subject: [PATCH 060/171] ASoC: Intel: soc-acpi: Add entry for HDMI_In capture support in NVL match table Adding HDMI-In capture via I2S feature support in NVL platform. Signed-off-by: Balamurugan C Reviewed-by: Liam Girdwood Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260806105742.2676322-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_es8336.c | 10 ++++++++++ sound/soc/intel/common/soc-acpi-intel-nvl-match.c | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index e9ee752d5ec3..9b016136c639 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -847,6 +847,16 @@ static const struct platform_device_id board_ids[] = { SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | SOF_ES8336_JD_INVERTED), }, + { + .name = "nvl_es83x6_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) | + SOF_NO_OF_HDMI_CAPTURE_SSP(2) | + SOF_HDMI_CAPTURE_1_SSP(0) | + SOF_HDMI_CAPTURE_2_SSP(2) | + SOF_SSP_HDMI_CAPTURE_PRESENT | + SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | + SOF_ES8336_JD_INVERTED), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c index a7f4097d426c..51bbae5ed8fc 100644 --- a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c @@ -16,12 +16,24 @@ static const struct snd_soc_acpi_codecs nvl_essx_83x6 = { .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, }; +static const struct snd_soc_acpi_codecs nvl_lt6911_hdmi = { + .num_codecs = 1, + .codecs = {"INTC10B0"} +}; + static const struct snd_soc_acpi_codecs nvl_rt5682_rt5682s_hp = { .num_codecs = 2, .codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID}, }; struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { + { + .comp_ids = &nvl_essx_83x6, + .drv_name = "nvl_es83x6_c1_h02", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &nvl_lt6911_hdmi, + .sof_tplg_filename = "sof-nvl-es83x6-ssp1-hdmi-ssp02.tplg", + }, { .comp_ids = &nvl_essx_83x6, .drv_name = "sof-essx8336", From d10549c4bcaad7ef69641c58db15470ec6ec5761 Mon Sep 17 00:00:00 2001 From: Balamurugan C Date: Thu, 6 Aug 2026 18:57:42 +0800 Subject: [PATCH 061/171] ASoC: Intel: sof_rt5682: Add HDMI-In capture with rt5682 support for NVL. Added match table entry on nvl machines to support HDMI-In capture with rt5682 I2S audio codec. also added the respective quirk configuration in rt5682 machine driver. Signed-off-by: Balamurugan C Reviewed-by: Liam Girdwood Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260806105742.2676322-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 7 +++++++ sound/soc/intel/common/soc-acpi-intel-nvl-match.c | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 649378957b20..7899f7ffd99b 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -915,6 +915,13 @@ static const struct platform_device_id board_ids[] = { /* SSP 0 and SSP 2 are used for HDMI IN */ SOF_SSP_MASK_HDMI_CAPTURE(0x5)), }, + { + .name = "nvl_rt5682_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_SSP_PORT_CODEC(1) | + /* SSP 0 and SSP 2 are used for HDMI IN */ + SOF_SSP_MASK_HDMI_CAPTURE(0x5)), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c index 51bbae5ed8fc..4a6208f821eb 100644 --- a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c @@ -42,6 +42,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, }, + { + .comp_ids = &nvl_rt5682_rt5682s_hp, + .drv_name = "nvl_rt5682_c1_h02", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &nvl_lt6911_hdmi, + .sof_tplg_filename = "sof-nvl-rt5682-ssp1-hdmi-ssp02.tplg", + }, { .comp_ids = &nvl_rt5682_rt5682s_hp, .drv_name = "sof_rt5682", From 54b279699279411c77c8afbc73b83c70740a7303 Mon Sep 17 00:00:00 2001 From: Rudi Heitbaum Date: Wed, 5 Aug 2026 15:21:02 +0000 Subject: [PATCH 062/171] ASoC: rt5645: Perform the initial jack detect at probe The only initial jack detect is the rt5645_irq(0, rt5645) at the end of rt5645_set_jack_detect(). A card described with simple-audio-card has no machine driver to call that, so jack state is only ever sampled from an edge on hp-detect-gpios. A headphone already in the socket at boot is therefore never noticed, and the card is silent with every mixer control set correctly. rt5645_jack_detect() is what force enables the "LDO2" and "Mic Det Power" supplies that the "HP amp" widget depends on, and what programs RT5645_CHARGE_PUMP away from its reset value, so without it "HP amp" cannot power up. Unplugging and replugging the jack is the only way to recover. Do the detect at the end of the component probe when the driver owns a hp-detect GPIO and the codec's own jack detect is unused, which is the case that has no other trigger. A machine driver calling rt5645_set_jack_detect() later just repeats it. Signed-off-by: Rudi Heitbaum Link: https://patch.msgid.link/anNU3tOUR7rOReSB@5e001e58230e Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index e9819653b30d..e7e39671a576 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3496,6 +3496,10 @@ static int rt5645_probe(struct snd_soc_component *component) if (!rt5645->eq_param) return -ENOMEM; + /* no machine driver to call rt5645_set_jack_detect(), so detect here */ + if (!rt5645->pdata.jd_mode && rt5645->gpiod_hp_det) + rt5645_irq(0, rt5645); + return 0; } From 3314c90a2eda3df7da4ab6f4388e667b2758de7f Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Wed, 5 Aug 2026 01:27:39 +0530 Subject: [PATCH 063/171] pmdomain: arm: Fix -EINVAL from scmi_pd_set_perf_state() on state 0 Currently, scmi_pd_set_perf_state() treats a performance state of 0 as invalid and returns -EINVAL. As a result, devices attached to SCMI performance domains can report failures when relinquishing their performance vote. The OPP framework use performance state 0 to indicate that no performance vote is required. For example, dev_pm_opp_set_rate(dev, 0) is commonly used (by firmware or linux) when a device is runtime suspended. A zero performance state does not require any SCMI performance request to be sent. Treat it as a no-op and return success instead of reporting an error. Signed-off-by: Praveen Talari Fixes: 2af23ceb8624 ("pmdomain: arm: Add the SCMI performance domain") Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/arm/scmi_perf_domain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pmdomain/arm/scmi_perf_domain.c b/drivers/pmdomain/arm/scmi_perf_domain.c index 3693423459c9..e390f902a444 100644 --- a/drivers/pmdomain/arm/scmi_perf_domain.c +++ b/drivers/pmdomain/arm/scmi_perf_domain.c @@ -33,7 +33,7 @@ scmi_pd_set_perf_state(struct generic_pm_domain *genpd, unsigned int state) return 0; if (!state) - return -EINVAL; + return 0; ret = pd->perf_ops->level_set(pd->ph, pd->domain_id, state, false); if (ret) From abe7c8b09bd72a9c726016257c6281f129b4c02d Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Thu, 6 Aug 2026 08:54:57 -0700 Subject: [PATCH 064/171] x86/CPU: Add a tlbi= cmdline switch With the recently found INVLPGB / TLBSYNC issue, there has been some interest in disabling INVLPGB-based TLB flushing, in order to rule out that CPU issue as a cause of userspace crashes. Add a kernel command line option to control the TLB flushing behavior. If the need arises, we will add a "tlbi=broadcast" for the case when TLB invalidation broadcasts need to be explicitly selected, but this is not needed now yet. [ bp: Rewrite commit message, move to cpu/common.c, add documentation. ] Fixes: 767ae437a32d ("x86/mm: Add INVLPGB feature and Kconfig entry") Suggested-by: Borislav Petkov Signed-off-by: Rik van Riel Signed-off-by: Borislav Petkov (AMD) Cc: Link: https://patch.msgid.link/20260729204341.3eb0b5ea@fangorn --- Documentation/admin-guide/kernel-parameters.txt | 4 ++++ arch/x86/kernel/cpu/common.c | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 3d35270dddef..e8ff36982d97 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -7568,6 +7568,10 @@ Kernel parameters See Documentation/admin-guide/mm/transhuge.rst for more details. + tlbi= [X86-64] + Format: {ipi} + ipi: switch to IPI-based TLB flushing + topology= [S390,EARLY] Format: {off | on} Specify if the kernel should make use of the cpu diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index a3df21d26460..a3caddd411ec 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -2664,3 +2664,13 @@ void __init arch_cpu_finalize_init(void) */ mem_encrypt_init(); } + +/* Control TLB flushing methods */ +static int __init tlbi_setup(char *str) +{ + if (!strcmp(str, "ipi")) + setup_clear_cpu_cap(X86_FEATURE_INVLPGB); + + return 1; +} +__setup("tlbi=", tlbi_setup); From 45f8dffc0714c3ef49c83e5bba4c56a4499bd5fc Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Wed, 22 Jul 2026 08:21:41 +0200 Subject: [PATCH 065/171] tick: Include ktime.h and jiffies.h in linux/tick.h The !CONFIG_NO_HZ_COMMON stubs use ktime_add(), ktime_get() and TICK_NSEC, but tick.h includes neither nor . Most configurations build only because those declarations arrive transitively. Commit 6440966067dc ("cpuset: Remove cpuset_cpu_is_isolated()") removed from . The include chain had been satisfying these declarations before was parsed. Commit 8aa76aa41589 ("ring-buffer: Use a housekeeping CPU to wake up waiters") then added to ring_buffer.c ahead of any header which provides them. Neither change is wrong on its own: the failure requires both and appeared in v7.0. ARM rpc_defconfig + CONFIG_FUNCTION_TRACER fails to build: $ make ARCH=arm rpc_defconfig $ ./scripts/config -e FTRACE -e FUNCTION_TRACER $ make ARCH=arm olddefconfig $ make ARCH=arm kernel/trace/ring_buffer.o In file included from include/linux/sched/isolation.h:6, from kernel/trace/ring_buffer.c:8: include/linux/tick.h: In function 'tick_nohz_get_next_hrtimer': include/linux/tick.h:156:9: error: implicit declaration of function 'ktime_add'; did you mean 'size_add'? include/linux/tick.h:156:19: error: implicit declaration of function 'ktime_get'; did you mean 'time_init'? include/linux/tick.h:156:32: error: 'TICK_NSEC' undeclared Include the headers the file actually uses. Fixes: 8aa76aa41589 ("ring-buffer: Use a housekeeping CPU to wake up waiters") Signed-off-by: Karl Mehltretter Signed-off-by: Thomas Gleixner Assisted-by: Codex:gpt-5.6-sol Link: https://patch.msgid.link/20260722062141.19671-1-kmehltretter@gmail.com --- include/linux/tick.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/tick.h b/include/linux/tick.h index 1cf4651f09ad..47750ccd1314 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -7,6 +7,8 @@ #include #include +#include +#include #include #include #include From 9b30521074f01aff856f539c1241a48342b69f7c Mon Sep 17 00:00:00 2001 From: Sergey Lebedev Date: Tue, 4 Aug 2026 22:59:24 +0000 Subject: [PATCH 066/171] ASoC: rt1320: run the initialisation preset on the first hardware init rt1320_io_init() applies the vendor initialisation preset only when the amplifier's SDCA function status has FUNCTION_NEEDS_INITIALIZATION set: if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION)) { Its two sibling drivers guard the same write differently, also running the preset on the first hardware init: rt712-sdca.c: if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) { rt722-sdca.c: if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) { On the Microsoft Surface Pro 11 (Intel) the RT1320 never sets that bit. Its function status reads back 0x41 on every boot, cold or warm: rt1320-sdca sdw:0:0:025d:1320:01: rt1320_io_init amp func_status=0x41 which is NEWLY_ATTACHED | FUNCTION_HAS_BEEN_RESET: the function reports that it has been reset and does not consider itself in need of initialisation. Bit 5 is never set, so the preset never runs, rt1320_vc_preset() and the MCU patch load are skipped, and the amplifier is left unprogrammed. rt712 and rt722 would have run it via their first_hw_init fallback. Add the same fallback. With it rt1320_vc_preset() executes and the amplifier reports RT1320_KR0_INT_READY=0x1f where previously it did not. Signed-off-by: Sergey Lebedev Link: https://patch.msgid.link/20260804225853.31585-2-lsa.uz@pm.me Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 1e930b27c67a..42acb6cef77a 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1899,7 +1899,7 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status); /* initialization write */ - if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION)) { + if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || !rt1320->first_hw_init) { switch (rt1320->dev_id) { case RT1320_DEV_ID: if (rt1320->version_id < RT1320_VC) From 0c88868271653537ed443272dd8e7d13634d214b Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Wed, 22 Jul 2026 22:38:32 +0000 Subject: [PATCH 067/171] netfilter: ipset: fix refcount race between list:set GC and swap __ip_set_put_byindex() resolved the index to a set pointer under RCU, then took ip_set_ref_lock in __ip_set_put() to decrement set->ref. ip_set_swap() holds that same lock while swapping both the ip_set_list slots and the two sets' ref counters, so it can interleave between the dereference and the lock acquisition, leaving the caller to decrement a set whose reference already moved to the other index and hit BUG_ON(set->ref == 0). list_set_gc() reaches this from timer softirq, which the nfnl mutex does not serialize against swap: an expiring list:set member calls list_set_del() -> ip_set_put_byindex() while IPSET_CMD_SWAP runs on the referenced sets. Resolve the index and decrement under ip_set_ref_lock, as ip_set_swap() already does, keeping the refcount tied to the index rather than to a stale set pointer. kernel BUG at net/netfilter/ipset/ip_set_core.c:685! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI RIP: 0010:ip_set_put_byindex (net/netfilter/ipset/ip_set_core.c:870) Call Trace: list_set_del (net/netfilter/ipset/ip_set_list_set.c:159) set_cleanup_entries (net/netfilter/ipset/ip_set_list_set.c:181) list_set_gc (net/netfilter/ipset/ip_set_list_set.c:578) call_timer_fn (kernel/time/timer.c:1748) __run_timers (kernel/time/timer.c:1799 kernel/time/timer.c:2374) run_timer_softirq (kernel/time/timer.c:2405) Kernel panic - not syncing: Fatal exception in interrupt Fixes: 9076aea76538 ("netfilter: ipset: Increase the number of maximal sets automatically") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_core.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 543851a923d0..0a86a170ba90 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -679,12 +679,19 @@ __ip_set_get(struct ip_set *set) write_unlock_bh(&ip_set_ref_lock); } +static void +__ip_set_put_locked(struct ip_set *set) +{ + lockdep_assert_held(&ip_set_ref_lock); + BUG_ON(set->ref == 0); + set->ref--; +} + static void __ip_set_put(struct ip_set *set) { write_lock_bh(&ip_set_ref_lock); - BUG_ON(set->ref == 0); - set->ref--; + __ip_set_put_locked(set); write_unlock_bh(&ip_set_ref_lock); } @@ -855,11 +862,11 @@ __ip_set_put_byindex(struct ip_set_net *inst, ip_set_id_t index) { struct ip_set *set; - rcu_read_lock(); - set = rcu_dereference(inst->ip_set_list)[index]; + write_lock_bh(&ip_set_ref_lock); + set = ip_set(inst, index); if (set) - __ip_set_put(set); - rcu_read_unlock(); + __ip_set_put_locked(set); + write_unlock_bh(&ip_set_ref_lock); } void From d45cc8020d7c0a9f01dee42ff5c40bc14c9af72f Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Fri, 31 Jul 2026 14:36:53 +0800 Subject: [PATCH 068/171] netfilter: bridge: release template ct on non-IP path A bridge nftables ct zone set rule can attach a conntrack template to an skb before nf_ct_bridge_pre() sees it. For non-IPv4 and non-IPv6 EtherTypes, nf_ct_bridge_pre() currently overwrites skb->_nfct with IP_CT_UNTRACKED without releasing the existing template reference. That makes the per-cpu template, and any temporary templates allocated for concurrent use, unreachable and leaks memory until the host runs out of slab. Reset the skb conntrack state before marking the frame untracked so the existing template reference is dropped on the non-IP path. Fixes: 3c171f496ef5 ("netfilter: bridge: add connection tracking system") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/nf_conntrack_bridge.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bridge/netfilter/nf_conntrack_bridge.c b/net/bridge/netfilter/nf_conntrack_bridge.c index 58a33d0380b0..7ecb8a26bfa3 100644 --- a/net/bridge/netfilter/nf_conntrack_bridge.c +++ b/net/bridge/netfilter/nf_conntrack_bridge.c @@ -280,6 +280,7 @@ static unsigned int nf_ct_bridge_pre(void *priv, struct sk_buff *skb, ret = nf_ct_br_defrag6(skb, &bridge_state); break; default: + nf_reset_ct(skb); nf_ct_set(skb, NULL, IP_CT_UNTRACKED); return NF_ACCEPT; } From 04d2feaed8d0103c498727191ba04001d5100e67 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Fri, 31 Jul 2026 22:27:41 +0800 Subject: [PATCH 069/171] ipvs: add totalconns for dest Replace the inactconns dest counter with totalconns, now inactconns can be obtained from totalconns - activeconns. This reduces the atomic inc/dec ops for TCP/SCTP from 6 to 4 if the connection is established and then closed. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Julian Anastasov Signed-off-by: Yizhou Zhao Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 17 ++++++++++++----- net/netfilter/ipvs/ip_vs_conn.c | 24 +++++++----------------- net/netfilter/ipvs/ip_vs_ctl.c | 10 +++++----- net/netfilter/ipvs/ip_vs_lc.c | 4 ++-- net/netfilter/ipvs/ip_vs_proto_sctp.c | 2 -- net/netfilter/ipvs/ip_vs_proto_tcp.c | 2 -- net/netfilter/ipvs/ip_vs_sync.c | 7 ++----- 7 files changed, 28 insertions(+), 38 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index d2813eb795be..11f430646db8 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -987,7 +987,7 @@ struct ip_vs_dest { /* connection counters and thresholds */ atomic_t activeconns; /* active connections */ - atomic_t inactconns; /* inactive connections */ + atomic_t totalconns; /* total connections */ atomic_t persistconns; /* persistent connections */ __u32 u_threshold; /* upper threshold */ __u32 l_threshold; /* lower threshold */ @@ -2220,14 +2220,21 @@ void ip_vs_unregister_hooks(struct netns_ipvs *ipvs, unsigned int af); static inline int ip_vs_dest_conn_overhead(struct ip_vs_dest *dest) { - /* We think the overhead of processing active connections is 256 + /* We think the overhead of processing active connections is 257 * times higher than that of inactive connections in average. (This - * 256 times might not be accurate, we will change it later) We + * 257 times might not be accurate, we will change it later) We * use the following formula to estimate the overhead now: - * dest->activeconns*256 + dest->inactconns + * dest->activeconns*256 + dest->totalconns */ return (atomic_read(&dest->activeconns) << 8) + - atomic_read(&dest->inactconns); + atomic_read(&dest->totalconns); +} + +static inline int +ip_vs_dest_inactconns(const struct ip_vs_dest *dest) +{ + return max(atomic_read(&dest->totalconns) - + atomic_read(&dest->activeconns), 0); } #ifdef CONFIG_IP_VS_PROTO_TCP diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 36c5cba03f5b..4d0a6f718ced 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1081,12 +1081,6 @@ static inline void ip_vs_bind_xmit_v6(struct ip_vs_conn *cp) #endif -static inline int ip_vs_dest_totalconns(struct ip_vs_dest *dest) -{ - return atomic_read(&dest->activeconns) - + atomic_read(&dest->inactconns); -} - /* * Bind a connection entry with a virtual service destination * Called just after a new connection entry is created. @@ -1153,8 +1147,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) */ if (!(flags & IP_VS_CONN_F_INACTIVE)) atomic_inc(&dest->activeconns); - else - atomic_inc(&dest->inactconns); + atomic_inc(&dest->totalconns); } else { /* It is a persistent connection/template, so increase the persistent connection counter */ @@ -1162,7 +1155,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) } if (dest->u_threshold != 0 && - ip_vs_dest_totalconns(dest) >= dest->u_threshold) + atomic_read(&dest->totalconns) >= dest->u_threshold) dest->flags |= IP_VS_DEST_F_OVERLOAD; } @@ -1244,13 +1237,10 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp) /* Update the connection counters */ if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) { - /* It is a normal connection, so decrease the inactconns - or activeconns counter */ - if (cp->flags & IP_VS_CONN_F_INACTIVE) { - atomic_dec(&dest->inactconns); - } else { + /* It is a normal connection, so decrease the counters */ + if (!(cp->flags & IP_VS_CONN_F_INACTIVE)) atomic_dec(&dest->activeconns); - } + atomic_dec(&dest->totalconns); } else { /* It is a persistent connection/template, so decrease the persistent connection counter */ @@ -1258,10 +1248,10 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp) } if (dest->l_threshold != 0) { - if (ip_vs_dest_totalconns(dest) < dest->l_threshold) + if (atomic_read(&dest->totalconns) < dest->l_threshold) dest->flags &= ~IP_VS_DEST_F_OVERLOAD; } else if (dest->u_threshold != 0) { - if (ip_vs_dest_totalconns(dest) * 4 < dest->u_threshold * 3) + if (atomic_read(&dest->totalconns) * 4 < dest->u_threshold * 3) dest->flags &= ~IP_VS_DEST_F_OVERLOAD; } else { if (dest->flags & IP_VS_DEST_F_OVERLOAD) diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index bcf40b8c41cf..45f534427d23 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1445,7 +1445,7 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) dest->port = udest->port; atomic_set(&dest->activeconns, 0); - atomic_set(&dest->inactconns, 0); + atomic_set(&dest->totalconns, 0); atomic_set(&dest->persistconns, 0); refcount_set(&dest->refcnt, 1); @@ -3031,7 +3031,7 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v) ip_vs_fwd_name(atomic_read(&dest->conn_flags)), atomic_read(&dest->weight), atomic_read(&dest->activeconns), - atomic_read(&dest->inactconns)); + ip_vs_dest_inactconns(dest)); else #endif seq_printf(seq, @@ -3042,7 +3042,7 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v) ip_vs_fwd_name(atomic_read(&dest->conn_flags)), atomic_read(&dest->weight), atomic_read(&dest->activeconns), - atomic_read(&dest->inactconns)); + ip_vs_dest_inactconns(dest)); } } @@ -3670,7 +3670,7 @@ __ip_vs_get_dest_entries(struct netns_ipvs *ipvs, const struct ip_vs_get_dests * entry.u_threshold = dest->u_threshold; entry.l_threshold = dest->l_threshold; entry.activeconns = atomic_read(&dest->activeconns); - entry.inactconns = atomic_read(&dest->inactconns); + entry.inactconns = ip_vs_dest_inactconns(dest); entry.persistconns = atomic_read(&dest->persistconns); ip_vs_copy_stats(&kstats, &dest->stats); ip_vs_export_stats_user(&entry.stats, &kstats); @@ -4282,7 +4282,7 @@ static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest) nla_put_u32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS, atomic_read(&dest->activeconns)) || nla_put_u32(skb, IPVS_DEST_ATTR_INACT_CONNS, - atomic_read(&dest->inactconns)) || + ip_vs_dest_inactconns(dest)) || nla_put_u32(skb, IPVS_DEST_ATTR_PERSIST_CONNS, atomic_read(&dest->persistconns)) || nla_put_u16(skb, IPVS_DEST_ATTR_ADDR_FAMILY, dest->af)) diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c index 38cc38c5d8bb..18b976a7c4d2 100644 --- a/net/netfilter/ipvs/ip_vs_lc.c +++ b/net/netfilter/ipvs/ip_vs_lc.c @@ -30,7 +30,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, /* * Simply select the server with the least number of - * (activeconns<<5) + inactconns + * (activeconns*256) + totalconns * Except whose weight is equal to zero. * If the weight is equal to zero, it means that the server is * quiesced, the existing connections to the server still get @@ -56,7 +56,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, IP_VS_DBG_ADDR(least->af, &least->addr), ntohs(least->port), atomic_read(&least->activeconns), - atomic_read(&least->inactconns)); + ip_vs_dest_inactconns(least)); return least; } diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index c80567c73469..fb8af6b15a39 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -446,12 +446,10 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && (next_state != IP_VS_SCTP_S_ESTABLISHED)) { atomic_dec(&dest->activeconns); - atomic_inc(&dest->inactconns); cp->flags |= IP_VS_CONN_F_INACTIVE; } else if ((cp->flags & IP_VS_CONN_F_INACTIVE) && (next_state == IP_VS_SCTP_S_ESTABLISHED)) { atomic_inc(&dest->activeconns); - atomic_dec(&dest->inactconns); cp->flags &= ~IP_VS_CONN_F_INACTIVE; } } diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 99a286fdc90c..fec0e8b47b71 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -526,12 +526,10 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && !tcp_state_active(new_state)) { atomic_dec(&dest->activeconns); - atomic_inc(&dest->inactconns); cp->flags |= IP_VS_CONN_F_INACTIVE; } else if ((cp->flags & IP_VS_CONN_F_INACTIVE) && tcp_state_active(new_state)) { atomic_inc(&dest->activeconns); - atomic_dec(&dest->inactconns); cp->flags &= ~IP_VS_CONN_F_INACTIVE; } } diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 93038abbf5e0..ea5fdd4f4ce7 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -878,13 +878,10 @@ static void ip_vs_proc_conn(struct netns_ipvs *ipvs, struct ip_vs_conn_param *pa spin_lock_bh(&cp->lock); if ((cp->flags ^ flags) & IP_VS_CONN_F_INACTIVE && !(flags & IP_VS_CONN_F_TEMPLATE) && dest) { - if (flags & IP_VS_CONN_F_INACTIVE) { + if (flags & IP_VS_CONN_F_INACTIVE) atomic_dec(&dest->activeconns); - atomic_inc(&dest->inactconns); - } else { + else atomic_inc(&dest->activeconns); - atomic_dec(&dest->inactconns); - } } flags &= IP_VS_CONN_F_BACKUP_UPD_MASK; flags |= cp->flags & ~IP_VS_CONN_F_BACKUP_UPD_MASK; From 8f843441c4e7eae8ea83491e8c203c2b192edcf5 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Fri, 31 Jul 2026 22:27:42 +0800 Subject: [PATCH 070/171] ipvs: properly update the overload flag on dest edit The upper/lower connection thresholds for dest can be changed, so use ip_vs_dest_update_overload() to properly update the dest overload flag. The thresholds were not limited, fit them in the 0 .. INT_MAX range as already done in ipvsadm. As the thresholds are also read when connections are created and expired, use WRITE_ONCE/READ_ONCE to access them. As the lower threshold is optional, use (u - (u >> 2)) to calculate the 75% default value based on the upper threshold by preserving the integer rounding, as suggested by Yizhou Zhao. Trigger flag update when totalconns reaches one of the thresholds and use dst_lock to serialize the updating. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Julian Anastasov Signed-off-by: Yizhou Zhao Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 3 ++ net/netfilter/ipvs/ip_vs_conn.c | 27 +++++-------- net/netfilter/ipvs/ip_vs_ctl.c | 67 +++++++++++++++++++++++++++++---- 3 files changed, 72 insertions(+), 25 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 11f430646db8..e99382930617 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -991,6 +991,7 @@ struct ip_vs_dest { atomic_t persistconns; /* persistent connections */ __u32 u_threshold; /* upper threshold */ __u32 l_threshold; /* lower threshold */ + __u32 l_threshold_val;/* used lower threshold */ /* for destination cache */ spinlock_t dst_lock; /* lock of dst_cache */ @@ -1907,6 +1908,8 @@ static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest) kfree(dest); } +void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode); + /* IPVS sync daemon data and function prototypes * (from ip_vs_sync.c) */ diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 4d0a6f718ced..abf52a226fee 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1141,22 +1141,22 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) /* Update the connection counters */ if (!(flags & IP_VS_CONN_F_TEMPLATE)) { + int tc; + /* It is a normal connection, so modify the counters * according to the flags, later the protocol can * update them on state change */ if (!(flags & IP_VS_CONN_F_INACTIVE)) atomic_inc(&dest->activeconns); - atomic_inc(&dest->totalconns); + tc = atomic_inc_return(&dest->totalconns); + if (tc == READ_ONCE(dest->u_threshold)) + ip_vs_dest_update_overload(dest, 1); } else { /* It is a persistent connection/template, so increase the persistent connection counter */ atomic_inc(&dest->persistconns); } - - if (dest->u_threshold != 0 && - atomic_read(&dest->totalconns) >= dest->u_threshold) - dest->flags |= IP_VS_DEST_F_OVERLOAD; } @@ -1237,27 +1237,20 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp) /* Update the connection counters */ if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) { + int tc; + /* It is a normal connection, so decrease the counters */ if (!(cp->flags & IP_VS_CONN_F_INACTIVE)) atomic_dec(&dest->activeconns); - atomic_dec(&dest->totalconns); + tc = atomic_fetch_dec(&dest->totalconns); + if (tc == READ_ONCE(dest->l_threshold_val)) + ip_vs_dest_update_overload(dest, -1); } else { /* It is a persistent connection/template, so decrease the persistent connection counter */ atomic_dec(&dest->persistconns); } - if (dest->l_threshold != 0) { - if (atomic_read(&dest->totalconns) < dest->l_threshold) - dest->flags &= ~IP_VS_DEST_F_OVERLOAD; - } else if (dest->u_threshold != 0) { - if (atomic_read(&dest->totalconns) * 4 < dest->u_threshold * 3) - dest->flags &= ~IP_VS_DEST_F_OVERLOAD; - } else { - if (dest->flags & IP_VS_DEST_F_OVERLOAD) - dest->flags &= ~IP_VS_DEST_F_OVERLOAD; - } - ip_vs_dest_put(dest); } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 45f534427d23..974773642af8 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1304,6 +1304,40 @@ void ip_vs_stats_free(struct ip_vs_stats *stats) } } +/* Update overload flag based on number of dest conns and lower/upper + * connection thresholds: + * - conns reach u_threshold and exceed it: set the flag + * - conns go below l_threshold (or 75% of u_threshold): clear the flag + */ +static void __ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode) +{ + int conns; + u32 l, u; + + lockdep_assert_held(&dest->dst_lock); + u = READ_ONCE(dest->u_threshold); + if (!u) + goto unset; + l = READ_ONCE(dest->l_threshold_val); + conns = atomic_read(&dest->totalconns); + if (conns >= (mode > 0 ? l : u)) { + dest->flags |= IP_VS_DEST_F_OVERLOAD; + return; + } + if (conns >= (mode < 0 ? u : l)) + return; + +unset: + dest->flags &= ~IP_VS_DEST_F_OVERLOAD; +} + +void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode) +{ + spin_lock_bh(&dest->dst_lock); + __ip_vs_dest_update_overload(dest, mode); + spin_unlock_bh(&dest->dst_lock); +} + /* * Update a destination in the given service */ @@ -1370,10 +1404,19 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, /* set the dest status flags */ dest->flags |= IP_VS_DEST_F_AVAILABLE; - if (udest->u_threshold == 0 || udest->u_threshold > dest->u_threshold) - dest->flags &= ~IP_VS_DEST_F_OVERLOAD; - dest->u_threshold = udest->u_threshold; - dest->l_threshold = udest->l_threshold; + if (READ_ONCE(dest->u_threshold) != udest->u_threshold || + READ_ONCE(dest->l_threshold) != udest->l_threshold) { + spin_lock_bh(&dest->dst_lock); + WRITE_ONCE(dest->u_threshold, udest->u_threshold); + WRITE_ONCE(dest->l_threshold, udest->l_threshold); + /* Low threshold defaults to 75% of upper threshold */ + WRITE_ONCE(dest->l_threshold_val, + udest->l_threshold ? : + (udest->u_threshold - + (udest->u_threshold >> 2))); + __ip_vs_dest_update_overload(dest, 0); + spin_unlock_bh(&dest->dst_lock); + } dest->af = udest->af; @@ -1486,6 +1529,9 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) return -ERANGE; } + if (udest->u_threshold > INT_MAX) + return -EINVAL; + if (udest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) { if (udest->tun_port == 0) { pr_err("%s(): tunnel port is zero\n", __func__); @@ -1559,6 +1605,9 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) return -ERANGE; } + if (udest->u_threshold > INT_MAX) + return -EINVAL; + if (udest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) { if (udest->tun_port == 0) { pr_err("%s(): tunnel port is zero\n", __func__); @@ -3667,8 +3716,8 @@ __ip_vs_get_dest_entries(struct netns_ipvs *ipvs, const struct ip_vs_get_dests * entry.port = dest->port; entry.conn_flags = atomic_read(&dest->conn_flags); entry.weight = atomic_read(&dest->weight); - entry.u_threshold = dest->u_threshold; - entry.l_threshold = dest->l_threshold; + entry.u_threshold = READ_ONCE(dest->u_threshold); + entry.l_threshold = READ_ONCE(dest->l_threshold); entry.activeconns = atomic_read(&dest->activeconns); entry.inactconns = ip_vs_dest_inactconns(dest); entry.persistconns = atomic_read(&dest->persistconns); @@ -4277,8 +4326,10 @@ static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest) dest->tun_port) || nla_put_u16(skb, IPVS_DEST_ATTR_TUN_FLAGS, dest->tun_flags) || - nla_put_u32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold) || - nla_put_u32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold) || + nla_put_u32(skb, IPVS_DEST_ATTR_U_THRESH, + READ_ONCE(dest->u_threshold)) || + nla_put_u32(skb, IPVS_DEST_ATTR_L_THRESH, + READ_ONCE(dest->l_threshold)) || nla_put_u32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS, atomic_read(&dest->activeconns)) || nla_put_u32(skb, IPVS_DEST_ATTR_INACT_CONNS, From cdcc4e46180df8161f4d2f3c6fd6beaf6990133d Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Fri, 31 Jul 2026 22:27:43 +0800 Subject: [PATCH 071/171] ipvs: separate destination availability state IPVS configuration paths update destination availability while connection accounting updates destination overload state. The two independent states share dest->flags, so their read-modify-write updates can race and lose one another. Keep OVERLOAD in flags, where the preceding patch serializes its updates with dst_lock, and move AVAILABLE to cflags. This keeps configuration- controlled availability out of the scheduler hot cacheline until a scheduler needs to check it. It also prevents availability updates from clobbering overload state. The destination status bits are not exposed through the IPVS sockopt or netlink interfaces, so keep their definitions in the internal IPVS header. Readers can still observe stale destination state; this does not provide a cross-field snapshot. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Link: https://lore.kernel.org/all/8913381c-1e02-35c7-0ec4-61de5a12fd35@ssi.bg/ Assisted-by: Claude-Code:GLM-5.2 Suggested-by: Julian Anastasov Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 7 +++++++ include/uapi/linux/ip_vs.h | 6 ------ net/netfilter/ipvs/ip_vs_conn.c | 4 ++-- net/netfilter/ipvs/ip_vs_core.c | 6 +++--- net/netfilter/ipvs/ip_vs_ctl.c | 4 ++-- net/netfilter/ipvs/ip_vs_dh.c | 4 ++-- net/netfilter/ipvs/ip_vs_lblc.c | 2 +- net/netfilter/ipvs/ip_vs_lblcr.c | 8 ++++---- net/netfilter/ipvs/ip_vs_xmit.c | 4 ++-- 9 files changed, 23 insertions(+), 22 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index e99382930617..fc2ef5ef31a6 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -36,6 +36,12 @@ #define IP_VS_HDR_INVERSE 1 #define IP_VS_HDR_ICMP 2 +/* Destination Server Flags */ +#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */ + +/* Destination Server Config Flags */ +#define IP_VS_DEST_CF_AVAILABLE 0x0001 /* server is available */ + /* conn_tab limits (as per Kconfig) */ #define IP_VS_CONN_TAB_MIN_BITS 8 #if BITS_PER_LONG > 32 @@ -976,6 +982,7 @@ struct ip_vs_dest { volatile unsigned int flags; /* dest status flags */ atomic_t conn_flags; /* flags to copy to conn */ atomic_t weight; /* server weight */ + unsigned long cflags; /* config flags */ atomic_t last_weight; /* server latest weight */ __u16 tun_type; /* tunnel type */ __be16 tun_port; /* tunnel port */ diff --git a/include/uapi/linux/ip_vs.h b/include/uapi/linux/ip_vs.h index 1ed234e7f251..2c37c6ac7525 100644 --- a/include/uapi/linux/ip_vs.h +++ b/include/uapi/linux/ip_vs.h @@ -28,12 +28,6 @@ #define IP_VS_SVC_F_SCHED_SH_FALLBACK IP_VS_SVC_F_SCHED1 /* SH fallback */ #define IP_VS_SVC_F_SCHED_SH_PORT IP_VS_SVC_F_SCHED2 /* SH use port */ -/* - * Destination Server Flags - */ -#define IP_VS_DEST_F_AVAILABLE 0x0001 /* server is available */ -#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */ - /* * IPVS sync daemon states */ diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index abf52a226fee..6fa3e1dc534c 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1279,7 +1279,7 @@ int ip_vs_check_template(struct ip_vs_conn *ct, struct ip_vs_dest *cdest) * Checking the dest server status. */ if ((dest == NULL) || - !(dest->flags & IP_VS_DEST_F_AVAILABLE) || + !(dest->cflags & IP_VS_DEST_CF_AVAILABLE) || expire_quiescent_template(ipvs, dest) || (cdest && (dest != cdest))) { IP_VS_DBG_BUF(9, "check_template: dest not available for " @@ -2020,7 +2020,7 @@ void ip_vs_expire_nodest_conn_flush(struct netns_ipvs *ipvs) cp = ip_vs_hn0_to_conn(hn); resched_score++; dest = cp->dest; - if (!dest || (dest->flags & IP_VS_DEST_F_AVAILABLE)) + if (!dest || (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) continue; if (atomic_read(&cp->n_control)) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 0bdaeb4ed61e..95af77b68851 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -302,7 +302,7 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) struct ip_vs_dest *dest = cp->dest; struct netns_ipvs *ipvs = cp->ipvs; - if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { + if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) { struct ip_vs_cpu_stats *s; struct ip_vs_service *svc; @@ -338,7 +338,7 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) struct ip_vs_dest *dest = cp->dest; struct netns_ipvs *ipvs = cp->ipvs; - if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { + if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) { struct ip_vs_cpu_stats *s; struct ip_vs_service *svc; @@ -2210,7 +2210,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state } /* Check the server status */ - if (cp && cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) { + if (cp && cp->dest && !(cp->dest->cflags & IP_VS_DEST_CF_AVAILABLE)) { /* the destination server is not available */ if (sysctl_expire_nodest_conn(ipvs)) { bool old_ct = ip_vs_conn_uses_old_conntrack(cp, skb); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 974773642af8..8f9a8e491ad6 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1402,7 +1402,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, } /* set the dest status flags */ - dest->flags |= IP_VS_DEST_F_AVAILABLE; + dest->cflags |= IP_VS_DEST_CF_AVAILABLE; if (READ_ONCE(dest->u_threshold) != udest->u_threshold || READ_ONCE(dest->l_threshold) != udest->l_threshold) { @@ -1662,7 +1662,7 @@ static void __ip_vs_unlink_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, int svcupd) { - dest->flags &= ~IP_VS_DEST_F_AVAILABLE; + dest->cflags &= ~IP_VS_DEST_CF_AVAILABLE; spin_lock_bh(&dest->dst_lock); __ip_vs_dst_cache_reset(dest); diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c index e1f62f6b25e2..43abed7a26a6 100644 --- a/net/netfilter/ipvs/ip_vs_dh.c +++ b/net/netfilter/ipvs/ip_vs_dh.c @@ -219,8 +219,8 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, s = (struct ip_vs_dh_state *) svc->sched_data; dest = ip_vs_dh_get(svc->af, s, &iph->daddr); - if (!dest - || !(dest->flags & IP_VS_DEST_F_AVAILABLE) + if (!dest || + !(dest->cflags & IP_VS_DEST_CF_AVAILABLE) || atomic_read(&dest->weight) <= 0 || is_overloaded(dest)) { ip_vs_scheduler_err(svc, "no destination available"); diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 15ccb2b2fa1f..693bcc82ccb7 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -502,7 +502,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, */ dest = en->dest; - if ((dest->flags & IP_VS_DEST_F_AVAILABLE) && + if ((dest->cflags & IP_VS_DEST_CF_AVAILABLE) && atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc)) goto out; } diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index c90ea897c3f7..f53f05ceea36 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -169,8 +169,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) if (least->flags & IP_VS_DEST_F_OVERLOAD) continue; - if ((atomic_read(&least->weight) > 0) - && (least->flags & IP_VS_DEST_F_AVAILABLE)) { + if ((atomic_read(&least->weight) > 0) && + (least->cflags & IP_VS_DEST_CF_AVAILABLE)) { loh = ip_vs_dest_conn_overhead(least); goto nextstage; } @@ -186,8 +186,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) doh = ip_vs_dest_conn_overhead(dest); if (((__s64)loh * atomic_read(&dest->weight) > - (__s64)doh * atomic_read(&least->weight)) - && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { + (__s64)doh * atomic_read(&least->weight)) && + (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) { least = dest; loh = doh; } diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index c4508f3f43dd..fc7403186394 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -351,7 +351,7 @@ __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, * stored in dest_trash. */ if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)) && - dest->flags & IP_VS_DEST_F_AVAILABLE) + dest->cflags & IP_VS_DEST_CF_AVAILABLE) __ip_vs_dst_set(dest, dest_dst, &rt->dst, 0); else noref = 0; @@ -530,7 +530,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, * stored in dest_trash. */ if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)) && - dest->flags & IP_VS_DEST_F_AVAILABLE) + dest->cflags & IP_VS_DEST_CF_AVAILABLE) __ip_vs_dst_set(dest, dest_dst, &rt->dst, cookie); else noref = 0; From 2d19b95c9723001f214f7a47d67b09f46238f200 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Sat, 1 Aug 2026 14:27:17 +0000 Subject: [PATCH 072/171] netfilter: nf_conntrack: defer invalid log until after unlock TCP and SCTP conntrack paths can emit invalid-packet logs while ct->lock is still held. When invalid logging is routed to nfnetlink_log and conntrack export is enabled, the log path can re-enter conntrack netlink glue and dump the same conntrack again. Protocol attribute dumping may take ct->lock, so logging while holding that lock can deadlock. Defer the TCP invalid logs by storing only the minimal log context while ct->lock is held and emitting the log after unlocking. Also make the TCP timeout-lowering invalid path return whether a log is needed, then emit that log after unlocking. Do the same for the SCTP invalid state-transition log that can be reached while ct->lock is held. Add a lockdep assertion to nf_ct_l4proto_log_invalid() so future callers that log invalid conntracks while holding ct->lock are caught outside TCP and SCTP as well. Fixes: 628d694344a0 ("netfilter: conntrack: reduce timeout when receiving out-of-window fin or rst") Fixes: d9a6f0d0df18 ("netfilter: conntrack: prepare tcp_in_window for ternary return value") Fixes: f71cb8f45d09 ("netfilter: conntrack: sctp: use nf log infrastructure for invalid packets") Cc: stable@vger.kernel.org Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Zihan Xi Reviewed-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_proto.c | 6 ++ net/netfilter/nf_conntrack_proto_sctp.c | 12 ++- net/netfilter/nf_conntrack_proto_tcp.c | 132 ++++++++++++++++-------- 3 files changed, 102 insertions(+), 48 deletions(-) diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index ad96896516b6..7a40e4e0e33e 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -79,6 +79,12 @@ void nf_ct_l4proto_log_invalid(const struct sk_buff *skb, struct net *net; va_list args; + /* nfnetlink_log may re-enter conntrack attribute dumping and try to + * take ct->lock again via helpers such as tcp_to_nlattr(), so invalid + * conntrack logs must only be emitted after dropping ct->lock. + */ + lockdep_assert_not_held(&ct->lock); + net = nf_ct_net(ct); if (likely(net->ct.sysctl_log_invalid == 0)) return; diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 7e10fa65cbdd..71cc920f6856 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -336,10 +336,12 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, struct sctphdr _sctph; const struct sctp_chunkhdr *sch; struct sctp_chunkhdr _sch; + bool log_invalid = false; u_int32_t offset, count; unsigned int *timeouts; unsigned long map[256 / sizeof(unsigned long)] = { 0 }; bool ignore = false; + u8 invalid_type = 0; if (sctp_error(skb, dataoff, state)) return -NF_ACCEPT; @@ -451,10 +453,8 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, /* Invalid */ if (new_state == SCTP_CONNTRACK_MAX) { - nf_ct_l4proto_log_invalid(skb, ct, state, - "Invalid, old_state %d, dir %d, type %d", - old_state, dir, sch->type); - + log_invalid = true; + invalid_type = sch->type; goto out_unlock; } @@ -529,6 +529,10 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, out_unlock: spin_unlock_bh(&ct->lock); + if (log_invalid) + nf_ct_l4proto_log_invalid(skb, ct, state, + "Invalid, old_state %d, dir %d, type %d", + old_state, dir, invalid_type); out: return -NF_ACCEPT; } diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index ceeed3d7fe52..30b970e2ade5 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -480,37 +480,81 @@ static void tcp_init_sender(struct ip_ct_tcp_state *sender, } } -__printf(6, 7) -static enum nf_ct_tcp_action nf_tcp_log_invalid(const struct sk_buff *skb, - const struct nf_conn *ct, - const struct nf_hook_state *state, - const struct ip_ct_tcp_state *sender, - enum nf_ct_tcp_action ret, - const char *fmt, ...) +enum nf_tcp_invalid_log_type { + NF_TCP_LOG_NONE, + NF_TCP_LOG_OVERSHOT, + NF_TCP_LOG_SEQ_OVER, + NF_TCP_LOG_ACK_OVER, + NF_TCP_LOG_SEQ_UNDER, + NF_TCP_LOG_ACK_UNDER, +}; + +struct nf_tcp_invalid_log { + enum nf_tcp_invalid_log_type type; + u32 value; +}; + +static enum nf_ct_tcp_action +nf_tcp_store_invalid(const struct nf_conn *ct, + const struct ip_ct_tcp_state *sender, + struct nf_tcp_invalid_log *log, + enum nf_ct_tcp_action ret, + enum nf_tcp_invalid_log_type type, + u32 value) { const struct nf_tcp_net *tn = nf_tcp_pernet(nf_ct_net(ct)); - struct va_format vaf; - va_list args; bool be_liberal; be_liberal = sender->flags & IP_CT_TCP_FLAG_BE_LIBERAL || tn->tcp_be_liberal; if (be_liberal) return NFCT_TCP_ACCEPT; - va_start(args, fmt); - vaf.fmt = fmt; - vaf.va = &args; - nf_ct_l4proto_log_invalid(skb, ct, state, "%pV", &vaf); - va_end(args); - + log->type = type; + log->value = value; return ret; } +static void nf_tcp_log_invalid(const struct sk_buff *skb, + const struct nf_conn *ct, + const struct nf_hook_state *state, + const struct nf_tcp_invalid_log *log) +{ + switch (log->type) { + case NF_TCP_LOG_OVERSHOT: + nf_ct_l4proto_log_invalid(skb, ct, state, + "%u bytes more than expected", + log->value); + break; + case NF_TCP_LOG_SEQ_OVER: + nf_ct_l4proto_log_invalid(skb, ct, state, + "SEQ is over upper bound %u (over the window of the receiver)", + log->value); + break; + case NF_TCP_LOG_ACK_OVER: + nf_ct_l4proto_log_invalid(skb, ct, state, + "ACK is over upper bound %u (ACKed data not seen yet)", + log->value); + break; + case NF_TCP_LOG_SEQ_UNDER: + nf_ct_l4proto_log_invalid(skb, ct, state, + "SEQ is under lower bound %u (already ACKed data retransmitted)", + log->value); + break; + case NF_TCP_LOG_ACK_UNDER: + nf_ct_l4proto_log_invalid(skb, ct, state, + "ignored ACK under lower bound %u (possible overly delayed)", + log->value); + break; + case NF_TCP_LOG_NONE: + break; + } +} + static enum nf_ct_tcp_action tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir, unsigned int index, const struct sk_buff *skb, unsigned int dataoff, const struct tcphdr *tcph, - const struct nf_hook_state *hook_state) + struct nf_tcp_invalid_log *log) { struct ip_ct_tcp *state = &ct->proto.tcp; struct ip_ct_tcp_state *sender = &state->seen[dir]; @@ -640,31 +684,29 @@ tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir, sender->td_end = end; sender->flags |= IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED; - return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_IGNORE, - "%u bytes more than expected", overshot); + return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_IGNORE, + NF_TCP_LOG_OVERSHOT, overshot); } - return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_INVALID, - "SEQ is over upper bound %u (over the window of the receiver)", - sender->td_maxend + 1); + return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_INVALID, + NF_TCP_LOG_SEQ_OVER, sender->td_maxend + 1); } if (!before(sack, receiver->td_end + 1)) - return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_INVALID, - "ACK is over upper bound %u (ACKed data not seen yet)", - receiver->td_end + 1); + return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_INVALID, + NF_TCP_LOG_ACK_OVER, receiver->td_end + 1); /* Is the ending sequence in the receive window (if available)? */ in_recv_win = !receiver->td_maxwin || after(end, sender->td_end - receiver->td_maxwin - 1); if (!in_recv_win) - return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_IGNORE, - "SEQ is under lower bound %u (already ACKed data retransmitted)", - sender->td_end - receiver->td_maxwin - 1); + return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_IGNORE, + NF_TCP_LOG_SEQ_UNDER, + sender->td_end - receiver->td_maxwin - 1); if (!after(sack, receiver->td_end - MAXACKWINDOW(sender) - 1)) - return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_IGNORE, - "ignored ACK under lower bound %u (possible overly delayed)", - receiver->td_end - MAXACKWINDOW(sender) - 1); + return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_IGNORE, + NF_TCP_LOG_ACK_UNDER, + receiver->td_end - MAXACKWINDOW(sender) - 1); /* Take into account window scaling (RFC 1323). */ if (!tcph->syn) @@ -719,11 +761,8 @@ tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir, return NFCT_TCP_ACCEPT; } -static void __cold nf_tcp_handle_invalid(struct nf_conn *ct, - enum ip_conntrack_dir dir, - int index, - const struct sk_buff *skb, - const struct nf_hook_state *hook_state) +static bool __cold +nf_tcp_handle_invalid(struct nf_conn *ct, enum ip_conntrack_dir dir, int index) { const unsigned int *timeouts; const struct nf_tcp_net *tn; @@ -732,7 +771,7 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct, if (!test_bit(IPS_ASSURED_BIT, &ct->status) || test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) - return; + return false; /* We don't want to have connections hanging around in ESTABLISHED * state for long time 'just because' conntrack deemed a FIN/RST @@ -747,7 +786,7 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct, case TCP_FIN_SET: break; default: - return; + return false; } if (ct->proto.tcp.last_dir != dir && @@ -755,7 +794,7 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct, ct->proto.tcp.last_index == TCP_RST_SET)) { expires = nf_ct_expires(ct); if (expires < 120 * HZ) - return; + return false; tn = nf_tcp_pernet(nf_ct_net(ct)); timeouts = nf_ct_timeout_lookup(ct); @@ -764,16 +803,15 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct, timeout = READ_ONCE(timeouts[TCP_CONNTRACK_UNACK]); if (expires > timeout) { - nf_ct_l4proto_log_invalid(skb, ct, hook_state, - "packet (index %d, dir %d) response for index %d lower timeout to %u", - index, dir, ct->proto.tcp.last_index, timeout); - WRITE_ONCE(ct->timeout, timeout + nfct_time_stamp); + return true; } } else { ct->proto.tcp.last_index = index; ct->proto.tcp.last_dir = dir; } + + return false; } /* table of valid flag combinations - PUSH, ECE and CWR are always valid */ @@ -969,7 +1007,9 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct, struct net *net = nf_ct_net(ct); struct nf_tcp_net *tn = nf_tcp_pernet(net); enum tcp_conntrack new_state, old_state; + struct nf_tcp_invalid_log log = {}; unsigned int index, *timeouts; + bool lowered_timeout = false; enum nf_ct_tcp_action res; enum ip_conntrack_dir dir; const struct tcphdr *th; @@ -1252,14 +1292,18 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct, } res = tcp_in_window(ct, dir, index, - skb, dataoff, th, state); + skb, dataoff, th, &log); switch (res) { case NFCT_TCP_IGNORE: spin_unlock_bh(&ct->lock); + nf_tcp_log_invalid(skb, ct, state, &log); return NF_ACCEPT; case NFCT_TCP_INVALID: - nf_tcp_handle_invalid(ct, dir, index, skb, state); + lowered_timeout = nf_tcp_handle_invalid(ct, dir, index); spin_unlock_bh(&ct->lock); + nf_tcp_log_invalid(skb, ct, state, &log); + if (lowered_timeout) + nf_ct_l4proto_log_invalid(skb, ct, state, "lowered timeout to UNACK"); return -NF_ACCEPT; case NFCT_TCP_ACCEPT: break; From 33d1469b0124cc0baaea7a2032123b77a81e0940 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 3 Aug 2026 10:43:27 +0200 Subject: [PATCH 073/171] netfilter: nfnetlink_log: wait for rcu grace period before freeing pernet state sashiko reports: "nfnl_log_net_exit() calls nf_log_unset(), which clears the logger pointer without an RCU grace period. Immediately after, ops_free_list() frees the per-net state while concurrent packets might still be executing nf_log_packet() under rcu_read_lock()." Clear the pointer via .pre_exit to make sure rcu readers have completed before pernet storage is free'd. The change in nf_log_syslog.c is only done for consistency: it doesn't use pernet data. Link: https://sashiko.dev/#/patchset/20260731151806.849724-1-pablo%40netfilter.org Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_log_syslog.c | 4 ++-- net/netfilter/nfnetlink_log.c | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/net/netfilter/nf_log_syslog.c b/net/netfilter/nf_log_syslog.c index e37b09b3203b..5ffde27d450a 100644 --- a/net/netfilter/nf_log_syslog.c +++ b/net/netfilter/nf_log_syslog.c @@ -1014,7 +1014,7 @@ static int __net_init nf_log_syslog_net_init(struct net *net) return ret; } -static void __net_exit nf_log_syslog_net_exit(struct net *net) +static void __net_exit nf_log_syslog_net_pre_exit(struct net *net) { nf_log_unset(net, &nf_ip_logger); nf_log_unset(net, &nf_arp_logger); @@ -1025,7 +1025,7 @@ static void __net_exit nf_log_syslog_net_exit(struct net *net) static struct pernet_operations nf_log_syslog_net_ops = { .init = nf_log_syslog_net_init, - .exit = nf_log_syslog_net_exit, + .pre_exit = nf_log_syslog_net_pre_exit, }; static int __init nf_log_syslog_init(void) diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 5fee61b3813c..6c7fa2ed34f5 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -1170,21 +1170,26 @@ static int __net_init nfnl_log_net_init(struct net *net) return 0; } +static void __net_exit nfnl_log_net_pre_exit(struct net *net) +{ +#ifdef CONFIG_PROC_FS + remove_proc_entry("nfnetlink_log", net->nf.proc_netfilter); +#endif + nf_log_unset(net, &nfulnl_logger); +} + static void __net_exit nfnl_log_net_exit(struct net *net) { struct nfnl_log_net *log = nfnl_log_pernet(net); unsigned int i; -#ifdef CONFIG_PROC_FS - remove_proc_entry("nfnetlink_log", net->nf.proc_netfilter); -#endif - nf_log_unset(net, &nfulnl_logger); for (i = 0; i < INSTANCE_BUCKETS; i++) WARN_ON_ONCE(!hlist_empty(&log->instance_table[i])); } static struct pernet_operations nfnl_log_net_ops = { .init = nfnl_log_net_init, + .pre_exit = nfnl_log_net_pre_exit, .exit = nfnl_log_net_exit, .id = &nfnl_log_net_id, .size = sizeof(struct nfnl_log_net), From e0ba936287dfe9783426aac27e5fd76fe35b38c9 Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Tue, 4 Aug 2026 06:10:55 +0000 Subject: [PATCH 074/171] ipvs: clear IPv4 options after rebasing tunnel ICMP errors ip_vs_in_icmp() rebases an skb from the outer ICMP packet to the quoted original request before passing it to icmp_send(). However, IPCB(skb)->opt still describes the outer IPv4 header. A timestamp option in the outer header can therefore leave an offset that points into the quoted transport header after the rebase. __ip_options_echo() treats a byte at that stale location as the option length and copies it into the fixed-size option storage on the __icmp_send() stack, causing a stack out-of-bounds write. Clear the stale option metadata after resetting the network header. Keep the remaining control block fields, including the ingress interface used by the ICMP response path. Fixes: f2edb9f7706d ("ipvs: implement passive PMTUD for IPIP packets") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.6-sol Codex:gpt-5.5-cyber Signed-off-by: Kyle Zeng Co-developed-by: David Lee Signed-off-by: David Lee Acked-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 95af77b68851..a46e7acdd8e1 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1950,6 +1950,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (pskb_pull(skb, offset2) == NULL) goto ignore_tunnel; skb_reset_network_header(skb); + memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); /* Ensure the IP header is present in headroom */ if (!pskb_may_pull(skb, hlen_orig)) goto ignore_tunnel; From d93660df4dd1d116f608ada4a29a80a5d6f0a6ed Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Thu, 6 Aug 2026 13:52:11 +0300 Subject: [PATCH 075/171] ipvs: revalidate ihl to prevent out-of-bounds access While the outer IP header is already pulled into the skb head, we must be careful and revalidate the embedded headers after reading them from the skb frags to prevent out-of-bounds access. One such place reported by Sashiko is ip_vs_nat_icmp() where local process can change the ihl field and after skb_ensure_writable() we can see larger value which is a problem for the ip_send_check(cih) calls. Add check to drop the packet if the ihl field is changed. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Link: https://sashiko.dev/#/patchset/20260730183506.87473-1-ja%40ssi.bg Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 2 +- net/netfilter/ipvs/ip_vs_core.c | 11 +++++++++-- net/netfilter/ipvs/ip_vs_xmit.c | 3 ++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index fc2ef5ef31a6..be3a6617adf4 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -2068,7 +2068,7 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp) !(cp->flags & IP_VS_CONN_F_TEMPLATE); } -void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +bool ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int dir, unsigned int toff, bool has_ports, struct ip_vs_iphdr *ciph); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index a46e7acdd8e1..eb806813292a 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -923,7 +923,7 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, * Packet has been made sufficiently writable in caller * - inout: 1=in->out, 0=out->in */ -void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +bool ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int inout, unsigned int toff, bool has_ports, struct ip_vs_iphdr *ciph) { @@ -931,6 +931,11 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); struct iphdr *cih = (struct iphdr *)(icmph + 1); + /* Before now we may used ihl from skb frag, revalidate it after + * copying it into skb head to prevent out-of-bounds access + */ + if (cih->ihl * 4 != ciph->len - ciph->off) + return false; if (inout) { iph->saddr = cp->vaddr.ip; ip_send_check(iph); @@ -964,6 +969,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, else IP_VS_DBG_PKT(11, AF_INET, pp, skb, ciph->off, "Forwarding altered incoming ICMP"); + return true; } #ifdef CONFIG_IP_VS_IPV6 @@ -1055,7 +1061,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb, ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph); else #endif - ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports, ciph); + if (!ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports, ciph)) + goto out; if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) goto out; diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index fc7403186394..04450a48f01a 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -1580,7 +1580,8 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, if (skb_cow(skb, rt->dst.dev->hard_header_len)) goto tx_error; - ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports, ciph); + if (!ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports, ciph)) + goto tx_error; /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; From d02f592064347e0c1e0d84f24941ad338838cc48 Mon Sep 17 00:00:00 2001 From: Alexey Velichayshiy Date: Thu, 6 Aug 2026 19:11:38 +0300 Subject: [PATCH 076/171] netfilter: nf_tables_offload: suppress WARN_ON_ONCE for ENOMEM in abort path In nft_flow_rule_offload_abort(), WARN_ON_ONCE(err) is triggered on every error during rollback, including -ENOMEM. Memory allocation failures are expected under low-memory conditions and do not indicate a kernel bug. Trace for example: nft_flow_offload_chain() // FLOW_BLOCK_BIND nft_flow_block_chain() nft_chain_offload_cmd() nft_block_offload_cmd() ->ndo_setup_tc() nsim_setup_tc() flow_block_cb_setup_simple() flow_block_cb_alloc() // fails to -ENOMEM The warning was reproduced on the 5.10 stable kernel under memory pressure via fault injection, but the underlying bug exists in mainline as well, as demonstrated by the ENOMEM trace above. The following splat was triggered during nf_tables transaction processing: WARNING: CPU: 0 PID: 8567 at net/netfilter/nf_tables_offload.c:532 nft_flow_rule_offload_abort net/netfilter/nf_tables_offload.c:532 [inline] WARNING: CPU: 0 PID: 8567 at net/netfilter/nf_tables_offload.c:532 nft_flow_rule_offload_commit+0x971/0xcd0 net/netfilter/nf_tables_offload.c:591 Modules linked in: CPU: 0 PID: 8567 Comm: syz-executor.0 Not tainted 5.10.260-syzkaller #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 RIP: 0010:nft_flow_rule_offload_abort net/netfilter/nf_tables_offload.c:532 [inline] RIP: 0010:nft_flow_rule_offload_commit+0x971/0xcd0 net/netfilter/nf_tables_offload.c:591 Call Trace: nf_tables_commit+0x3bd/0x4bd0 net/netfilter/nf_tables_api.c:8604 nfnetlink_rcv_batch+0xb1e/0x1f20 net/netfilter/nfnetlink.c:509 nfnetlink_rcv_skb_batch net/netfilter/nfnetlink.c:579 [inline] nfnetlink_rcv+0x3b3/0x420 net/netfilter/nfnetlink.c:597 netlink_unicast_kernel net/netlink/af_netlink.c:1314 [inline] netlink_unicast+0x6cd/0xa00 net/netfilter/af_netlink.c:1340 netlink_sendmsg+0x906/0xe10 net/netfilter/af_netlink.c:1919 sock_sendmsg_nosec net/socket.c:651 [inline] __sock_sendmsg+0x155/0x190 net/socket.c:663 ____sys_sendmsg+0x705/0x870 net/socket.c:2379 ___sys_sendmsg+0x100/0x170 net/socket.c:2433 __sys_sendmsg+0xe9/0x1c0 net/socket.c:2462 do_syscall_64+0x33/0x40 arch/x86/entry/common.c:46 entry_SYSCALL_64_after_hwframe+0x67/0xd1 Change the condition to WARN_ON_ONCE(err && err != -ENOMEM) so that warnings are only emitted for unexpected errors. This aligns with the common kernel practice of not warning on -ENOMEM. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 63b48c73ff56 ("netfilter: nf_tables_offload: undo updates if transaction fails") Signed-off-by: Alexey Velichayshiy Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_offload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_tables_offload.c b/net/netfilter/nf_tables_offload.c index 8998a24651ff..0ac3c26dfb3d 100644 --- a/net/netfilter/nf_tables_offload.c +++ b/net/netfilter/nf_tables_offload.c @@ -558,7 +558,7 @@ static void nft_flow_rule_offload_abort(struct net *net, break; } - if (WARN_ON_ONCE(err)) + if (WARN_ON_ONCE(err && err != -ENOMEM)) break; } } From 2014ac62df9d45bb9a004a043e85df7be09ed780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Jean?= Date: Sat, 8 Aug 2026 12:40:02 +0000 Subject: [PATCH 077/171] netfilter: flowtable: publish GC-visible tuple last MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nf_flow_table_iterate() only treats original-direction tuple nodes as owning entries. Publishing the original node first lets GC observe and free a flow while flow_offload_add() is still inserting the reply node. Publish the reply node first and the original node last so GC never sees a partially installed flow. KASAN can trigger slab-use-after-free read and write reports in the flowtable/rhashtable path (rht_deferred_worker, jhash, flow_offload_del, flow_offload_lookup, etc.). Fixes: ac2a66665e23 ("netfilter: add generic flow table infrastructure") Signed-off-by: Jérémy Jean Assisted-by: Codex:gpt-5 Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index b66e65439341..8b1165f2b5a4 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -332,17 +332,18 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow) flow->timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow); err = rhashtable_insert_fast(&flow_table->rhashtable, - &flow->tuplehash[0].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].node, nf_flow_offload_rhash_params); if (err < 0) return err; + /* GC only iterates original-direction entries; publish original last. */ err = rhashtable_insert_fast(&flow_table->rhashtable, - &flow->tuplehash[1].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].node, nf_flow_offload_rhash_params); if (err < 0) { rhashtable_remove_fast(&flow_table->rhashtable, - &flow->tuplehash[0].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].node, nf_flow_offload_rhash_params); return err; } From 4cbd69766b35a089664cadb1f613bb85f7ef77a9 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 6 Aug 2026 15:53:41 +0200 Subject: [PATCH 078/171] netfilter: ipset: fix list type element drift bug If list_set_uadd() calls list_set_replace() to swap an expired entry, the element count remains the same, therefore the increment must be elided. Fixes: 702b71e7c666 ("netfilter: ipset: Add element count to all set types header") Link: https://sashiko.dev/#/patchset/20260806101947.2802-1-fw%40strlen.de Signed-off-by: Florian Westphal Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_list_set.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index ca3ef9479e83..0bc2370773af 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -301,9 +301,12 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, e->set = set; INIT_LIST_HEAD(&e->list); list_set_init_extensions(set, ext, e); - if (n) + if (n) { list_set_replace(set, e, n); - else if (next) + return 0; + } + + if (next) list_add_tail_rcu(&e->list, &next->list); else if (prev) list_add_rcu(&e->list, &prev->list); From 490937b88cb592cc0c5367758edd700fd5abd15c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 6 Aug 2026 16:17:50 +0200 Subject: [PATCH 079/171] netfilter: ipset: let destroy callbacks adjust ext mem size For bitmap this change makes no difference, because destructors are called synchronously. List type however calls them via call_rcu() so accounting decrement can happen after list_set_flush() set ext_size to 0. 'set->elements = 0' can be removed for the same reason in the list type case, it calls 'set->elements--' for each element. Fixes: 9e41f26a505c ("netfilter: ipset: Count non-static extension memory for userspace") Suggested-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_bitmap_gen.h | 2 +- net/netfilter/ipset/ip_set_list_set.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index 226fdf17b683..d6a7e6604542 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -77,7 +77,7 @@ mtype_flush(struct ip_set *set) mtype_ext_cleanup(set); bitmap_zero(map->members, map->elements); set->elements = 0; - atomic64_set(&set->ext_size, 0); + DEBUG_NET_WARN_ON_ONCE(atomic64_read(&set->ext_size) > 0); } /* Calculate the actual memory size of the set data */ diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 0bc2370773af..f070088742d6 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -423,8 +423,7 @@ list_set_flush(struct ip_set *set) list_for_each_entry_safe(e, n, &map->members, list) list_set_del(set, e); - set->elements = 0; - atomic64_set(&set->ext_size, 0); + DEBUG_NET_WARN_ON_ONCE(set->elements > 0); } static void From c703f62c30f2db7f40ad575f0034636d8a401fac Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 3 Aug 2026 12:31:32 -0700 Subject: [PATCH 080/171] eth: bnxt: cancel IRQ notifier before freeing affinity mask bnxt_irq_affinity_notify() copies into irq->cpu_mask. Cancel the notifier before freeing irq->cpu_mask. Fixes: c214410c47d6 ("bnxt_en: Add TPH support in BNXT driver") Reviewed-by: Michael Chan Link: https://patch.msgid.link/20260803193135.2030368-2-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 25099077fe4f..98c5d6d4b2cf 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11878,14 +11878,14 @@ static void bnxt_free_irq(struct bnxt *bp) irq = &bp->irq_tbl[map_idx]; if (irq->requested) { + bnxt_release_irq_notifier(irq); + if (irq->have_cpumask) { irq_update_affinity_hint(irq->vector, NULL); free_cpumask_var(irq->cpu_mask); irq->have_cpumask = 0; } - bnxt_release_irq_notifier(irq); - free_irq(irq->vector, bp->bnapi[i]); } From 4b5cb58a4443fff67aa18a0d7b645b2220f2fcf8 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 3 Aug 2026 12:31:33 -0700 Subject: [PATCH 081/171] eth: bnxt: keep the aRFS rmap updated when TPH is enabled The TPH support must have broken aRFS in bnxt. IRQ can only have one notifier, so installing the TPH notifier is overriding the one implicitly installed by irq_cpu_rmap_add(). Make sure we call cpu_rmap_update() from the TPH notifier. We need to be careful with the ordering and not free the rmap until we unregistered the notifier. Note that moving the rmap freeing after the early return in bnxt_free_irq() is fine - there's no path that could leave rmap with irq_tbl being NULL. Fixes: c214410c47d6 ("bnxt_en: Add TPH support in BNXT driver") Reviewed-by: Michael Chan Link: https://patch.msgid.link/20260803193135.2030368-3-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 98c5d6d4b2cf..505d1afea71c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11793,6 +11793,16 @@ static void bnxt_irq_affinity_notify(struct irq_affinity_notify *notify, irq = container_of(notify, struct bnxt_irq, affinity_notify); +#ifdef CONFIG_RFS_ACCEL + if (irq->bp->dev->rx_cpu_rmap && irq->ring_nr < irq->bp->rx_nr_rings) { + err = cpu_rmap_update(irq->bp->dev->rx_cpu_rmap, irq->ring_nr, + mask); + if (err) + netdev_warn(irq->bp->dev, + "aRFS rmap update failed: %d\n", err); + } +#endif + if (!irq->bp->tph_mode) return; @@ -11866,10 +11876,6 @@ static void bnxt_free_irq(struct bnxt *bp) struct bnxt_irq *irq; int i; -#ifdef CONFIG_RFS_ACCEL - free_irq_cpu_rmap(bp->dev->rx_cpu_rmap); - bp->dev->rx_cpu_rmap = NULL; -#endif if (!bp->irq_tbl || !bp->bnapi) return; @@ -11895,6 +11901,11 @@ static void bnxt_free_irq(struct bnxt *bp) /* Disable TPH support */ pcie_disable_tph(bp->pdev); bp->tph_mode = 0; + +#ifdef CONFIG_RFS_ACCEL + free_irq_cpu_rmap(bp->dev->rx_cpu_rmap); + bp->dev->rx_cpu_rmap = NULL; +#endif } static int bnxt_request_irq(struct bnxt *bp) From 0fd562c7b9e48e7958f7405e54bf98e8e22ad184 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 3 Aug 2026 12:31:34 -0700 Subject: [PATCH 082/171] eth: bnxt: decrease indent in bnxt_request_irq() bnxt_request_irq() has unnecessary level of indentation. Use continue instead. No need to re-fetch NUMA node for each IRQ, move to the function level. No functional changes. Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260803193135.2030368-4-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 50 +++++++++++------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 505d1afea71c..b06cd8fd19dd 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11910,6 +11910,7 @@ static void bnxt_free_irq(struct bnxt *bp) static int bnxt_request_irq(struct bnxt *bp) { + const int numa_node = dev_to_node(&bp->pdev->dev); struct cpu_rmap *rmap = NULL; int i, j, rc = 0; unsigned long flags = 0; @@ -11932,6 +11933,7 @@ static int bnxt_request_irq(struct bnxt *bp) for (i = 0, j = 0; i < bp->cp_nr_rings; i++) { int map_idx = bnxt_cp_num_to_irq_num(bp, i); struct bnxt_irq *irq = &bp->irq_tbl[map_idx]; + u16 tag; if (IS_ENABLED(CONFIG_RFS_ACCEL) && rmap && bp->bnapi[i]->rx_ring) { @@ -11950,33 +11952,31 @@ static int bnxt_request_irq(struct bnxt *bp) netif_napi_set_irq_locked(&bp->bnapi[i]->napi, irq->vector); irq->requested = 1; - if (zalloc_cpumask_var(&irq->cpu_mask, GFP_KERNEL)) { - int numa_node = dev_to_node(&bp->pdev->dev); - u16 tag; + if (!zalloc_cpumask_var(&irq->cpu_mask, GFP_KERNEL)) + continue; - irq->have_cpumask = 1; - irq->msix_nr = map_idx; - irq->ring_nr = i; - cpumask_set_cpu(cpumask_local_spread(i, numa_node), - irq->cpu_mask); - rc = irq_update_affinity_hint(irq->vector, irq->cpu_mask); - if (rc) { - netdev_warn(bp->dev, - "Update affinity hint failed, IRQ = %d\n", - irq->vector); - break; - } - - bnxt_register_irq_notifier(bp, irq); - - /* Init ST table entry */ - if (pcie_tph_get_cpu_st(irq->bp->pdev, TPH_MEM_TYPE_VM, - cpumask_first(irq->cpu_mask), - &tag)) - continue; - - pcie_tph_set_st_entry(irq->bp->pdev, irq->msix_nr, tag); + irq->have_cpumask = 1; + irq->msix_nr = map_idx; + irq->ring_nr = i; + cpumask_set_cpu(cpumask_local_spread(i, numa_node), + irq->cpu_mask); + rc = irq_update_affinity_hint(irq->vector, irq->cpu_mask); + if (rc) { + netdev_warn(bp->dev, + "Update affinity hint failed, IRQ = %d\n", + irq->vector); + break; } + + bnxt_register_irq_notifier(bp, irq); + + /* Init ST table entry */ + if (pcie_tph_get_cpu_st(irq->bp->pdev, TPH_MEM_TYPE_VM, + cpumask_first(irq->cpu_mask), + &tag)) + continue; + + pcie_tph_set_st_entry(irq->bp->pdev, irq->msix_nr, tag); } return rc; } From 51e96fa31f7e7eac2cba8f854e24d36600cc040b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 3 Aug 2026 12:31:35 -0700 Subject: [PATCH 083/171] eth: bnxt: avoid deadlock when canceling IRQ affinity notifier Unregistering IRQ affinity notifiers waits for the callback synchronously. bnxt takes the netdev instance lock in the notifier (to restart the queue) and cancels the work under the same lock. This may obviously deadlock. Move the restart to the async service task. The queue restart isn't super time sensitive. Store the new TPH tag, schedule the task. Safely canceling the service task is already ironed out. In bnxt_request_irq() the order of registering notifier, affinity and initial TPH programming has to be inverted. I think it was racy previously since user may trigger an update as soon as notifier is installed. There's a small known gap - if pcie_tph_get_cpu_st() fails at init and the target tag is 0 we may miss programming the entry. This does not seem worth fixing, the code has skip-on-failure all over the place, anyway. Fixes: c214410c47d6 ("bnxt_en: Add TPH support in BNXT driver") Tested-by: Vishvambar Panth S Link: https://patch.msgid.link/20260803193135.2030368-5-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 86 ++++++++++++++++------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 3 + 2 files changed, 64 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index b06cd8fd19dd..bc7b37cb74a7 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11789,12 +11789,13 @@ static void bnxt_irq_affinity_notify(struct irq_affinity_notify *notify, { struct bnxt_irq *irq; u16 tag; - int err; irq = container_of(notify, struct bnxt_irq, affinity_notify); #ifdef CONFIG_RFS_ACCEL if (irq->bp->dev->rx_cpu_rmap && irq->ring_nr < irq->bp->rx_nr_rings) { + int err; + err = cpu_rmap_update(irq->bp->dev->rx_cpu_rmap, irq->ring_nr, mask); if (err) @@ -11812,20 +11813,11 @@ static void bnxt_irq_affinity_notify(struct irq_affinity_notify *notify, return; if (pcie_tph_get_cpu_st(irq->bp->pdev, TPH_MEM_TYPE_VM, - cpumask_first(irq->cpu_mask), &tag)) + cpumask_first(mask), &tag)) return; - if (pcie_tph_set_st_entry(irq->bp->pdev, irq->msix_nr, tag)) - return; - - netdev_lock(irq->bp->dev); - if (netif_running(irq->bp->dev)) { - err = netdev_rx_queue_restart(irq->bp->dev, irq->ring_nr); - if (err) - netdev_err(irq->bp->dev, - "RX queue restart failed: err=%d\n", err); - } - netdev_unlock(irq->bp->dev); + WRITE_ONCE(irq->new_tag, tag); + bnxt_queue_sp_work(irq->bp, BNXT_TPH_UPDATE_SP_EVENT); } static void bnxt_irq_affinity_release(struct kref *ref) @@ -11896,6 +11888,8 @@ static void bnxt_free_irq(struct bnxt *bp) } irq->requested = 0; + irq->tag = 0; + irq->new_tag = 0; } /* Disable TPH support */ @@ -11933,6 +11927,7 @@ static int bnxt_request_irq(struct bnxt *bp) for (i = 0, j = 0; i < bp->cp_nr_rings; i++) { int map_idx = bnxt_cp_num_to_irq_num(bp, i); struct bnxt_irq *irq = &bp->irq_tbl[map_idx]; + unsigned int cpu_num; u16 tag; if (IS_ENABLED(CONFIG_RFS_ACCEL) && @@ -11958,8 +11953,19 @@ static int bnxt_request_irq(struct bnxt *bp) irq->have_cpumask = 1; irq->msix_nr = map_idx; irq->ring_nr = i; - cpumask_set_cpu(cpumask_local_spread(i, numa_node), - irq->cpu_mask); + cpu_num = cpumask_local_spread(i, numa_node); + cpumask_set_cpu(cpu_num, irq->cpu_mask); + + /* Init ST table entry if we can get the mapping */ + if (!pcie_tph_get_cpu_st(bp->pdev, TPH_MEM_TYPE_VM, + cpu_num, &tag)) { + pcie_tph_set_st_entry(bp->pdev, irq->msix_nr, tag); + irq->tag = tag; + irq->new_tag = tag; + } + + bnxt_register_irq_notifier(bp, irq); + rc = irq_update_affinity_hint(irq->vector, irq->cpu_mask); if (rc) { netdev_warn(bp->dev, @@ -11967,16 +11973,6 @@ static int bnxt_request_irq(struct bnxt *bp) irq->vector); break; } - - bnxt_register_irq_notifier(bp, irq); - - /* Init ST table entry */ - if (pcie_tph_get_cpu_st(irq->bp->pdev, TPH_MEM_TYPE_VM, - cpumask_first(irq->cpu_mask), - &tag)) - continue; - - pcie_tph_set_st_entry(irq->bp->pdev, irq->msix_nr, tag); } return rc; } @@ -14479,6 +14475,43 @@ static void bnxt_rtnl_unlock_sp(struct bnxt *bp) rtnl_unlock(); } +static void bnxt_tph_update(struct bnxt *bp) +{ + struct net_device *dev = bp->dev; + int i; + + bnxt_lock_sp(bp); + if (!test_bit(BNXT_STATE_OPEN, &bp->state)) + goto unlock; + + for (i = 0; i < bp->rx_nr_rings; i++) { + struct bnxt_irq *irq; + int map_idx, err; + u16 tag; + + map_idx = bnxt_cp_num_to_irq_num(bp, i); + irq = &bp->irq_tbl[map_idx]; + tag = READ_ONCE(irq->new_tag); + if (irq->tag == tag) + continue; + + if (pcie_tph_set_st_entry(bp->pdev, irq->msix_nr, tag)) + continue; + + err = netdev_rx_queue_restart(dev, irq->ring_nr); + if (err) { + netdev_err(dev, "RX queue restart failed: err=%d\n", + err); + continue; + } + + irq->tag = tag; + } + +unlock: + bnxt_unlock_sp(bp); +} + /* Only called from bnxt_sp_task() */ static void bnxt_reset(struct bnxt *bp, bool silent) { @@ -14903,6 +14936,9 @@ static void bnxt_sp_task(struct work_struct *work) bnxt_devlink_health_fw_report(bp); } + if (test_and_clear_bit(BNXT_TPH_UPDATE_SP_EVENT, &bp->sp_event)) + bnxt_tph_update(bp); + smp_mb__before_atomic(); clear_bit(BNXT_STATE_IN_SP_TASK, &bp->state); } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 44ea8fc7b766..dc8ec5e5733e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1268,6 +1268,8 @@ struct bnxt_irq { struct bnxt *bp; int msix_nr; int ring_nr; + u16 tag; + u16 new_tag; struct irq_affinity_notify affinity_notify; }; @@ -2642,6 +2644,7 @@ struct bnxt { #define BNXT_RING_COAL_NOW_SP_EVENT 17 #define BNXT_FW_RESET_NOTIFY_SP_EVENT 18 #define BNXT_FW_EXCEPTION_SP_EVENT 19 +#define BNXT_TPH_UPDATE_SP_EVENT 20 #define BNXT_LINK_CFG_CHANGE_SP_EVENT 21 #define BNXT_THERMAL_THRESHOLD_SP_EVENT 22 #define BNXT_FW_ECHO_REQUEST_SP_EVENT 23 From e16e960d55a40d36bd7c2494cc005e757dc9a1ef Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 6 Aug 2026 10:38:57 +0000 Subject: [PATCH 084/171] ipvlan: inherit needed_headroom and needed_tailroom from phy_dev ipvlan devices inherit hard_header_len from phy_dev during ipvlan_init(), but leave needed_headroom and needed_tailroom set to 0. When the underlying phy_dev (or stacked lower device) requires extra headroom or tailroom for headers/trailers (e.g. macsec, ipsec, wireguard, tunnels, or veth with rx headroom), upper layers calculating packet headroom and tailroom fail to reserve sufficient space. This can result in reallocation overhead, skb headroom underflows, or KASAN slab-use-after-free crashes when dev_hard_header() / ipvlan_hard_header() prepends header data or when lower devices append tailroom. Fix this by: 1. Inheriting needed_headroom and needed_tailroom from phy_dev in ipvlan_init(). 2. Propagating needed_headroom and needed_tailroom updates to attached ipvlans in ipvlan_device_event() when receiving NETDEV_FEAT_CHANGE events. Fixes: 2ad7bf363841 ("ipvlan: Initial check-in of the IPVLAN driver.") Reported-by: syzbot+1f9fd0f4b601cf88d6e6@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a720a21.40259c87.584f4.04bb.GAE@google.com/T/#u Reported-by: Tangxin Xie Closes: https://lore.kernel.org/netdev/CANn89i+1EW-sFNK8xoq98gMbPCeLS7e=+rs9gHfLg5Wj+4x0sw@mail.gmail.com/T/#mcc6307f115e500df23ea2980d5669fe95f20b6b4 Signed-off-by: Eric Dumazet Reviewed-by: Hangbin Liu Link: https://patch.msgid.link/20260806103857.115541-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/ipvlan/ipvlan_main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index ed46439a9f4e..92d3d340281f 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -146,6 +146,8 @@ static int ipvlan_init(struct net_device *dev) dev->lltx = true; netif_inherit_tso_max(dev, phy_dev); dev->hard_header_len = phy_dev->hard_header_len; + dev->needed_headroom = phy_dev->needed_headroom; + dev->needed_tailroom = phy_dev->needed_tailroom; netdev_lockdep_set_classes(dev); @@ -773,6 +775,8 @@ static int ipvlan_device_event(struct notifier_block *unused, case NETDEV_FEAT_CHANGE: list_for_each_entry(ipvlan, &port->ipvlans, pnode) { netif_inherit_tso_max(ipvlan->dev, dev); + ipvlan->dev->needed_headroom = dev->needed_headroom; + ipvlan->dev->needed_tailroom = dev->needed_tailroom; netdev_update_features(ipvlan->dev); } break; From cef51860becd9700217c81732ca1eb1ea6ed6fe1 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 6 Aug 2026 14:19:38 +0000 Subject: [PATCH 085/171] macvlan: inherit needed_headroom and needed_tailroom from lowerdev macvlan devices inherit hard_header_len from lowerdev during macvlan_init(), but leave needed_headroom and needed_tailroom set to 0. When the underlying lowerdev requires extra headroom or tailroom for headers/trailers (e.g. macsec, ipsec, wireguard, tunnels, or veth with rx headroom), upper layers calculating packet headroom and tailroom fail to reserve sufficient space. This can result in reallocation overhead, skb headroom underflows, or KASAN slab-use-after-free crashes when dev_hard_header() / macvlan_hard_header() prepends header data or when lower devices append tailroom. Fix this by: 1. Inheriting needed_headroom and needed_tailroom from lowerdev in macvlan_init(). 2. Propagating needed_headroom and needed_tailroom updates to attached macvlans in macvlan_device_event() when receiving NETDEV_FEAT_CHANGE events. Fixes: b863ceb7ddce ("[NET]: Add macvlan driver") Reported-by: Tangxin Xie Closes: https://lore.kernel.org/netdev/CANn89i+1EW-sFNK8xoq98gMbPCeLS7e=+rs9gHfLg5Wj+4x0sw@mail.gmail.com/T/#m16adf0ff972cbfd8066c3a8e656e75eaeb12d021 Signed-off-by: Eric Dumazet Reviewed-by: Hangbin Liu Link: https://patch.msgid.link/20260806141938.287660-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/macvlan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index c40fa331836b..ff2f4bb651b7 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -950,6 +950,8 @@ static int macvlan_init(struct net_device *dev) dev->lltx = true; netif_inherit_tso_max(dev, lowerdev); dev->hard_header_len = lowerdev->hard_header_len; + dev->needed_headroom = lowerdev->needed_headroom; + dev->needed_tailroom = lowerdev->needed_tailroom; macvlan_set_lockdep_class(dev); vlan->pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats); @@ -1824,6 +1826,8 @@ static int macvlan_device_event(struct notifier_block *unused, case NETDEV_FEAT_CHANGE: list_for_each_entry(vlan, &port->vlans, list) { netif_inherit_tso_max(vlan->dev, dev); + vlan->dev->needed_headroom = dev->needed_headroom; + vlan->dev->needed_tailroom = dev->needed_tailroom; netdev_update_features(vlan->dev); } break; From 92465f46f3c3a92e4758af20d2363b7adc43e1f5 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 6 Aug 2026 15:56:26 -0700 Subject: [PATCH 086/171] net: expect instance lock in netdev_queue_get_dma_dev() netdev_queue_get_dma_dev() uses "compat" locking assert which wants either the rtnl_lock or netdev instance lock. This is not right, the callers are taking the instance lock unconditionally. All entry points for queue config are purely instance locked. In other words the callers use netdev_get_by_index_lock(), not netdev_get_by_index_lock_ops_compat(). All the state we will access is effectively instance lock protected (it's const for devices which are not ops-locked). Update the assert to avoid false positive warnings. Cc: stable@vger.kernel.org Fixes: b6c5f9454ef34 ("io_uring/zcrx: call netdev_queue_get_dma_dev() under instance lock") Reported-by: syzbot+a78926bdac2adb52dc0e@syzkaller.appspotmail.com Reviewed-by: Simon Horman Acked-by: Daniel Borkmann Link: https://patch.msgid.link/20260806225627.3998672-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- net/core/netdev_queues.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/netdev_queues.c b/net/core/netdev_queues.c index 4d6864bb4f6b..f5558b12877c 100644 --- a/net/core/netdev_queues.c +++ b/net/core/netdev_queues.c @@ -40,7 +40,7 @@ struct device *netdev_queue_get_dma_dev(struct net_device *dev, struct netdev_rx_queue *hw_rxq; struct device *dma_dev; - netdev_assert_locked_ops_compat(dev); + netdev_assert_locked(dev); /* Only RX side supports queue leasing today. */ if (type != NETDEV_QUEUE_TYPE_RX || !netif_rxq_is_leased(dev, idx)) From 60db47f02bfa2aa688938aa199117ec4f8e31d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20K=C3=B6ppeler?= Date: Thu, 6 Aug 2026 13:43:49 +0200 Subject: [PATCH 087/171] veth: fix queue index used to wake the peer txq in veth_poll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit veth_poll() derives the index of the peer TX queue to wake from rq->xdp_rxq.queue_index. That field is only initialized by xdp_rxq_info_reg() in veth_enable_xdp_range(), which runs only when an XDP program is attached. On the plain GRO/NAPI path (veth_napi_enable_range()) xdp_rxq_info_reg() is never called, so queue_index stays 0 for every queue, as priv->rq is zero-allocated. So in a multi-queue setup with GRO enabled and no XDP program attached, every NAPI instance looks at the peer's TX queue 0. If veth_xmit() stops peer TX queue 1 because the ptr_ring is full (NETDEV_TX_BUSY), nothing ever wakes it again: the poller draining queue 1 wakes queue 0 instead. veth implements no ndo_tx_timeout, so the netdev watchdog does not kick in either, and the queue stays stopped indefinitely. Derive the index from the position of the rq within priv->rq instead, which is correct regardless of whether XDP was ever enabled. Scripts to reproduce the stall are available at https://github.com/netoptimizer/veth-backpressure-performance-testing Fixes: dc82a33297fc ("veth: apply qdisc backpressure on full ptr_ring to reduce TX drops") Signed-off-by: Jonas Köppeler Tested-by: Jesper Dangaard Brouer Acked-by: Jesper Dangaard Brouer Link: https://patch.msgid.link/20260806-veth-fix-poll-queue-idx-v1-1-c5357fb7573d@tu-berlin.de Signed-off-by: Jakub Kicinski --- drivers/net/veth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index f80f6584a6ff..6ab84c837a33 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -967,7 +967,7 @@ static int veth_poll(struct napi_struct *napi, int budget) struct veth_rq *rq = container_of(napi, struct veth_rq, xdp_napi); struct veth_priv *priv = netdev_priv(rq->dev); - int queue_idx = rq->xdp_rxq.queue_index; + int queue_idx = rq - priv->rq; struct netdev_queue *peer_txq; struct veth_stats stats = {}; struct net_device *peer_dev; From 883b56ae58fe657d8497806c7059646e9ba6dbd0 Mon Sep 17 00:00:00 2001 From: Hyunjung Ko Date: Thu, 6 Aug 2026 19:12:52 +0900 Subject: [PATCH 088/171] net/sched: act_gact, act_police: range check the fallback control action tcf_action_check_ctrlact() range checks the primary control action: if (!opcode) ret = action > TC_ACT_VALUE_MAX ? -EINVAL : 0; TC_ACT_VALUE_MAX is TC_ACT_TRAP, so kernel-internal verdicts above it cannot be set that way. But act_gact and act_police each carry a second, independent control action supplied by user space that never reaches that helper - TCA_GACT_PROB.paction and TCA_POLICE_RESULT. Both only reject TC_ACT_GOTO_CHAIN, so any other value is stored verbatim and returned verbatim from the action. In particular user space can store TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is deliberately not part of the UAPI value range. That verdict tells every caller the action took ownership of the skb, so nobody frees it: sch_handle_ingress(), sch_handle_egress() and tcf_qevent_handle() all deliberately skip the free for it. The result is one leaked sk_buff plus its data buffer per packet traversing the filter, unbounded, for all traffic on the chain including kernel-generated packets. Both are trivially deterministic. act_gact clamps tcfg_pval to >= 1, so with pval = 1 gact_determ() returns the fallback for every packet. act_police has no mandatory rate, so rate = 0 leaves tcfp_mtu = ~0 and tcf_police_mtu_check() always passes. TC_ACT_CONSUMED was added by commit 720f22fed81b ("net: sched: refactor reinsert action"), after both goto-chain guards were written: commit 9469f375ab09 ("net/sched: act_gact: disallow 'goto chain' on fallback control action") and commit c08f5ed5d625 ("net/sched: act_police: disallow 'goto chain' on fallback control action"). Neither guard was widened when the new verdict appeared. Factor the existing range test out of tcf_action_check_ctrlact() as tcf_action_valid() and apply it to both fallbacks. The helper cannot call tcf_action_check_ctrlact() directly because that also allocates a goto_chain, which is exactly what these two sites must not do. Reproduced on v7.2-rc6: kmemleak reports one leaked 232-byte skbuff_head_cache object plus its 704-byte data buffer per packet. With this patch both configurations are rejected with -EINVAL and kmemleak reports none. Fixes: 720f22fed81b ("net: sched: refactor reinsert action") Cc: stable@vger.kernel.org # v5.3+ Signed-off-by: Hyunjung Ko Acked-by: Jamal Hadi Salim Tested-by: Victor Nogueira Link: https://patch.msgid.link/20260806101252.809593-1-hj351016@gmail.com Signed-off-by: Jakub Kicinski --- include/net/act_api.h | 19 +++++++++++++++++++ net/sched/act_gact.c | 5 +++++ net/sched/act_police.c | 6 ++++++ 3 files changed, 30 insertions(+) diff --git a/include/net/act_api.h b/include/net/act_api.h index 20d9e55f8564..fd03f6319e88 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -270,6 +270,25 @@ int tcf_action_check_ctrlact(int action, struct tcf_proto *tp, struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action, struct tcf_chain *newchain); +/* Range check for a control action supplied by user space. + * + * This is the same test tcf_action_check_ctrlact() applies to the primary + * control action, factored out for the *fallback* control actions + * (act_gact's TCA_GACT_PROB.paction and act_police's TCA_POLICE_RESULT), + * which must not reach tcf_action_check_ctrlact() because they have no + * goto_chain to allocate. Without it, user space can store kernel-internal + * verdicts such as TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is + * deliberately not part of the UAPI value range. + */ +static inline bool tcf_action_valid(int action) +{ + int opcode = TC_ACT_EXT_OPCODE(action); + + if (!opcode) + return action <= TC_ACT_VALUE_MAX; + return opcode <= TC_ACT_EXT_OPCODE_MAX || action == TC_ACT_UNSPEC; +} + #ifdef CONFIG_INET DECLARE_STATIC_KEY_FALSE(tcf_frag_xmit_count); #endif diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index e949280eb800..565860cccba6 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -89,6 +89,11 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, p_parm = nla_data(tb[TCA_GACT_PROB]); if (p_parm->ptype >= MAX_RAND) return -EINVAL; + if (!tcf_action_valid(p_parm->paction)) { + NL_SET_ERR_MSG(extack, + "invalid fallback control action"); + return -EINVAL; + } if (TC_ACT_EXT_CMP(p_parm->paction, TC_ACT_GOTO_CHAIN)) { NL_SET_ERR_MSG(extack, "goto chain not allowed on fallback"); diff --git a/net/sched/act_police.c b/net/sched/act_police.c index b16468a98c55..ce08f6840ef7 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -128,6 +128,12 @@ static int tcf_police_init(struct net *net, struct nlattr *nla, if (tb[TCA_POLICE_RESULT]) { tcfp_result = nla_get_u32(tb[TCA_POLICE_RESULT]); + if (!tcf_action_valid(tcfp_result)) { + NL_SET_ERR_MSG(extack, + "invalid fallback control action"); + err = -EINVAL; + goto failure; + } if (TC_ACT_EXT_CMP(tcfp_result, TC_ACT_GOTO_CHAIN)) { NL_SET_ERR_MSG(extack, "goto chain not allowed on fallback"); From 60837e4b840a9c3f7ec826e3584df0bc6542a2c2 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Fri, 7 Aug 2026 09:44:36 +0800 Subject: [PATCH 089/171] tcp: fix icsk_ack.ato bitfield overflow On cross-region connections we observed delayed ACKs suddenly turning into immediate ACKs plus a TCP_MAX_QUICKACKS burst, as if the connection had just received its first data segment. Commit 95b9a87c6a6b ("tcp: record last received ipv6 flowlabel") squeezed icsk_ack.ato into 8 bits, sized for TCP_DELACK_MAX. But both writers still bound ato by icsk_rto, which can be well above 255 jiffies, so the bitfield assignment silently wraps mod 256: repeated delack timer misses double ato up to icsk_rto, storing 320 as 64 and 256 as 0, and ato == 0 is the "first data packet" sentinel in tcp_event_data_recv(). Clamp both writers to TCP_DELACK_MAX, which the static_assert already guarantees to fit and tcp_send_delayed_ack() effectively caps ato at anyway. Fixes: 95b9a87c6a6b ("tcp: record last received ipv6 flowlabel") Signed-off-by: Jiayuan Chen Reviewed-by: Neal Cardwell Link: https://patch.msgid.link/20260807014437.36687-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_input.c | 6 +++--- net/ipv4/tcp_timer.c | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 5b6378b94701..0f60a1dbf927 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1039,9 +1039,9 @@ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb) /* The fastest case is the first. */ icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + TCP_ATO_MIN / 2; } else if (m < icsk->icsk_ack.ato) { - icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + m; - if (icsk->icsk_ack.ato > icsk->icsk_rto) - icsk->icsk_ack.ato = icsk->icsk_rto; + icsk->icsk_ack.ato = min3((icsk->icsk_ack.ato >> 1) + (u32)m, + icsk->icsk_rto, + (u32)TCP_DELACK_MAX); } else if (m > icsk->icsk_rto) { /* Too long gap. Apparently sender failed to * restart window, so that we send ACKs quickly. diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index bf171b5e1eb3..4df1c9745c5d 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -334,7 +334,9 @@ void tcp_delack_timer_handler(struct sock *sk) if (inet_csk_ack_scheduled(sk)) { if (!inet_csk_in_pingpong_mode(sk)) { /* Delayed ACK missed: inflate ATO. */ - icsk->icsk_ack.ato = min_t(u32, icsk->icsk_ack.ato << 1, icsk->icsk_rto); + icsk->icsk_ack.ato = min3((u32)icsk->icsk_ack.ato << 1, + icsk->icsk_rto, + (u32)TCP_DELACK_MAX); } else { /* Delayed ACK missed: leave pingpong mode and * deflate ATO. From 202fef9bbbf5784487eec27581389c6fb97c350d Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 6 Aug 2026 15:47:16 +0200 Subject: [PATCH 090/171] net: phy: realtek: fix EEE advertisement write on the internal PHY MMD path In rtlgen_write_mmd(), the MDIO_AN_EEE_ADV case swaps the arguments to rtlgen_write_vend2(): it passes the MMD register number as the OCP address and the OCP address constant as the value. The caller's value is discarded and the write lands on the wrong register, so the EEE advertisement cannot be configured on the affected PHYs. Mirror rtlgen_read_mmd() and write the value to RTL_MDIO_AN_EEE_ADV. Fixes: da681ed73fb9 ("net: phy: realtek: improve mmd register access for internal PHY's") Signed-off-by: Oleksij Rempel Reviewed-by: Andrew Lunn Reviewed-by: Nicolai Buchwitz Link: https://patch.msgid.link/20260806134716.3511821-1-o.rempel@pengutronix.de Signed-off-by: Jakub Kicinski --- drivers/net/phy/realtek/realtek_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c index b65d0f5fa1a0..0d2321bd18c8 100644 --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c @@ -1370,7 +1370,7 @@ static int rtlgen_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, if (devnum == MDIO_MMD_VEND2) ret = rtlgen_write_vend2(phydev, regnum, val); else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) - ret = rtlgen_write_vend2(phydev, regnum, RTL_MDIO_AN_EEE_ADV); + ret = rtlgen_write_vend2(phydev, RTL_MDIO_AN_EEE_ADV, val); else ret = -EOPNOTSUPP; From 8a7ed561671aa6a911a2de99e59ef670a4d0b1df Mon Sep 17 00:00:00 2001 From: Hyunjung Ko Date: Thu, 6 Aug 2026 19:12:34 +0900 Subject: [PATCH 091/171] net/sched: act_ct: fix sk_buff leak when the header checks reject a packet tcf_ct_handle_fragments() runs its header sanity checks before handing anything to the defragmentation engine: if (family == NFPROTO_IPV4) err = tcf_ct_ipv4_is_fragment(skb, &frag); else err = tcf_ct_ipv6_is_fragment(skb, &frag); if (err || !frag) return err; tcf_ct_ipv4_is_fragment() returns -EINVAL or -ENOMEM; tcf_ct_ipv6_is_fragment() adds -EPROTO when ipv6_find_hdr() fails. None of them frees or queues the skb, so on that path the caller still owns it. tcf_ct_act() however funnels every non-zero return into the ownership-transfer exit: err = tcf_ct_handle_fragments(net, skb, family, p->zone, &defrag); if (err) goto out_frag; ... out_frag: if (err != -EINPROGRESS) tcf_action_inc_drop_qstats(&c->common); return TC_ACT_CONSUMED; TC_ACT_CONSUMED means the action took ownership of the skb, so no caller frees it - sch_handle_ingress(), sch_handle_egress() and tcf_qevent_handle() all deliberately skip the free for that verdict. The skb is therefore orphaned: one sk_buff plus its data buffer is leaked per malformed packet, unbounded. Note the drop counter is already incremented for these errors, so the statistics claim a drop that never happens. Three different ownership states reach out_frag: today - the skb may be queued by the defrag engine (-EINPROGRESS), already freed by nf_ct_handle_fragments(), or still owned by us. Tell the caller which of those it is, and free the packet ourselves in the last case, which restores the TC_ACT_SHOT behaviour that predated the Fixes: commit. Reproduced on v7.2-rc6 with a 54-byte frame carrying a 40-byte IPv6 header with nexthdr = 0 (hop-by-hop) and nothing after it, on a clsact ingress chain with "action ct". kmemleak reports one leaked 232-byte skbuff_head_cache object plus its 704-byte data buffer per packet; with this patch it reports none. Fixes: 3f14b377d01d ("net/sched: act_ct: fix skb leak and crash on ooo frags") Cc: stable@vger.kernel.org # v6.8+ Signed-off-by: Hyunjung Ko Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260806101235.809370-1-hj351016@gmail.com Signed-off-by: Jakub Kicinski --- net/sched/act_ct.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index be535a261fa0..e250969c84ac 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -840,8 +840,15 @@ static int tcf_ct_ipv6_is_fragment(struct sk_buff *skb, bool *frag) return 0; } +/* On error, tells the caller whether it still owns @skb and must free it + * itself. @skb is ours only when the header checks below reject the packet + * before it is handed to the defragmentation engine; once nf_ct_handle_ + * fragments() has been called the skb is either queued (-EINPROGRESS) or has + * already been freed by it. + */ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb, - u8 family, u16 zone, bool *defrag) + u8 family, u16 zone, bool *defrag, + bool *skb_is_ours) { enum ip_conntrack_info ctinfo; struct tc_skb_cb cb; @@ -859,8 +866,12 @@ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb, err = tcf_ct_ipv4_is_fragment(skb, &frag); else err = tcf_ct_ipv6_is_fragment(skb, &frag); - if (err || !frag) + if (err) { + *skb_is_ours = true; return err; + } + if (!frag) + return 0; cb = *tc_skb_cb(skb); err = nf_ct_handle_fragments(net, skb, zone, family, &proto, &cb.mru); @@ -977,6 +988,7 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a, int nh_ofs, err, retval; struct tcf_ct_params *p; bool add_helper = false; + bool skb_is_ours = false; bool skip_add = false; bool defrag = false; struct nf_conn *ct; @@ -1012,9 +1024,18 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a, */ nh_ofs = skb_network_offset(skb); skb_pull_rcsum(skb, nh_ofs); - err = tcf_ct_handle_fragments(net, skb, family, p->zone, &defrag); - if (err) + err = tcf_ct_handle_fragments(net, skb, family, p->zone, &defrag, + &skb_is_ours); + if (err) { + /* The skb is still ours only when the header checks rejected + * it; returning TC_ACT_CONSUMED for such a packet would leak + * it, since no caller frees an skb it was told it no longer + * owns. + */ + if (skb_is_ours) + goto drop; goto out_frag; + } err = nf_ct_skb_network_trim(skb, family); if (err) From f1b3416ceaf7ca4cb5cbd986ee8fe3ffaeda2d48 Mon Sep 17 00:00:00 2001 From: Hyunjung Ko Date: Thu, 6 Aug 2026 19:12:35 +0900 Subject: [PATCH 092/171] selftests: tc-testing: add act_ct test for malformed header handling Add a tdc case covering the leak fixed by the previous patch. The test attaches "action ct" to a clsact ingress chain and injects ten IPv6 frames whose nexthdr says hop-by-hop but which carry nothing after the 40-byte header, so ipv6_find_hdr() fails and tcf_ct_ipv6_is_fragment() returns -EPROTO. Before the fix act_ct returned TC_ACT_CONSUMED for these packets, so tc_run() never reached its TC_ACT_SHOT arm and the clsact drop counter stayed at zero while the skbs leaked. After the fix the packets are dropped properly and the counter reflects them, which is what the test matches on: before: Sent 476 bytes 11 pkt (dropped 0, overlimits 0 requeues 0) after: Sent 400 bytes 10 pkt (dropped 10, overlimits 0 requeues 0) Signed-off-by: Hyunjung Ko Reviewed-by: Victor Nogueira Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260806101235.809370-2-hj351016@gmail.com Signed-off-by: Jakub Kicinski --- .../tc-testing/tc-tests/actions/ct.json | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json b/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json index da65f838bd52..8ab48def89b6 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json @@ -702,5 +702,45 @@ "$TC qdisc del dev $DUMMY clsact", "$TC qdisc del dev $DUMMY root handle 1:" ] + }, + { + "id": "c7a3", + "name": "Verify act_ct drops a packet whose header checks fail", + "category": [ + "actions", + "ct", + "scapy" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + [ + "$TC qdisc del dev $DEV1 clsact", + 0, + 1, + 2, + 255 + ], + "$TC qdisc add dev $DEV1 clsact" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 ingress protocol all prio 1 matchall action ct", + "scapy": [ + { + "iface": "$DEV0", + "count": 10, + "packet": "Ether(type=0x86dd)/IPv6(nh=0, plen=0, src='::1', dst='::2')" + } + ], + "expExitCode": "0", + "verifyCmd": "$TC -s qdisc show dev $DEV1 clsact", + "matchPattern": "dropped 10", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 clsact" + ] } ] From d2121faf133ac3bf9531b53a7e21273649a08517 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Thu, 6 Aug 2026 12:25:37 +0900 Subject: [PATCH 093/171] NTB: ntb_netdev: Preserve RX queue depth on allocation failure ntb_netdev_rx_handler() hands the received skb to the network stack before allocating its replacement. If the allocation fails, nothing is reposted. Every failure therefore takes one buffer out of the RX queue while the interface remains up, and enough failures eventually stall reception. A retry path could refill the queue later, but ntb_netdev has none. Allocate the replacement first instead. If that fails, drop the packet and repost the same skb. This keeps the queue full and lets packet delivery resume as soon as memory is available again. Fixes: 548c237c0a99 ("net: Add support for NTB virtual ethernet device") Cc: stable@vger.kernel.org Signed-off-by: Koichiro Den Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260806032537.3526498-1-den@valinux.co.jp Signed-off-by: Paolo Abeni --- drivers/net/ntb_netdev.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c index c3a6ba96fc8a..029a4a532a10 100644 --- a/drivers/net/ntb_netdev.c +++ b/drivers/net/ntb_netdev.c @@ -127,8 +127,8 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, { struct ntb_netdev_queue *q = qp_data; struct ntb_netdev *dev = q->ntdev; + struct sk_buff *skb, *new_skb; struct net_device *ndev; - struct sk_buff *skb; int rc; ndev = dev->ndev; @@ -144,6 +144,12 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, goto enqueue_again; } + new_skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN); + if (!new_skb) { + ndev->stats.rx_dropped++; + goto enqueue_again; + } + skb_put(skb, len); skb->protocol = eth_type_trans(skb, ndev); skb->ip_summed = CHECKSUM_NONE; @@ -157,12 +163,7 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, ndev->stats.rx_bytes += len; } - skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN); - if (!skb) { - ndev->stats.rx_errors++; - ndev->stats.rx_frame_errors++; - return; - } + skb = new_skb; enqueue_again: rc = ntb_transport_rx_enqueue(qp, skb, skb->data, ndev->mtu + ETH_HLEN); From bc5bde9ce3cc36502839dfe98e068f7303a50982 Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Sat, 8 Aug 2026 02:17:10 +0800 Subject: [PATCH 094/171] ipv4: fix use-after-free in fib_nhc_update_mtu() fib_nhc_update_mtu() walks the nexthop exception table under RTNL, but RTNL does not serialize this walk with PMTU exception updates. The walk uses rcu_dereference_protected() with a constant true condition without holding fnhe_lock. The following interleaving can therefore occur: CPU 0 CPU 1 fib_nhc_update_mtu() update_or_create_fnhe() load fnhe spin_lock_bh(&fnhe_lock) fnhe_remove_oldest() unlink fnhe kfree_rcu(fnhe, rcu) access fnhe after grace period KASAN reported: BUG: KASAN: slab-use-after-free in fib_nhc_update_mtu+0x3df/0x410 Read of size 8 at addr ffff888107d49000 by task poc/90 Call Trace: fib_nhc_update_mtu+0x3df/0x410 fib_sync_mtu+0x7a/0xd0 fib_netdev_event+0x229/0x3f0 netif_set_mtu_ext+0x33a/0x570 dev_set_mtu+0x88/0x120 The same walk updates fnhe_pmtu and fnhe_mtu_locked. These fields form a pair and other writers serialize them with fnhe_lock. RCU alone prevents reclamation, but would still allow concurrent writers to leave a mixed pair. Walk the table under RCU and acquire fnhe_lock only while updating each exception. RCU keeps the current entry alive while the short critical section serializes its paired PMTU fields. This avoids holding the global lock while scanning all 2048 buckets for every nexthop. Fixes: af7d6cce5369 ("net: ipv4: update fnhe_pmtu when first hop's MTU changes") Cc: stable@vger.kernel.org Suggested-by: Ido Schimmel Signed-off-by: Chengfeng Ye Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260807181710.1178747-1-nicoyip.dev@gmail.com Signed-off-by: Paolo Abeni --- include/net/route.h | 2 ++ net/ipv4/fib_semantics.c | 34 +++++++++++----------------------- net/ipv4/route.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/include/net/route.h b/include/net/route.h index f90106f383c5..45290177a33c 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -276,6 +276,8 @@ int fib_dump_info_fnhe(struct sk_buff *skb, struct netlink_callback *cb, u32 table_id, struct fib_info *fi, int *fa_index, int fa_start, unsigned int flags); +void fnhe_update_pmtu(struct fib_nh_exception *fnhe, u32 new, u32 orig); + static inline void ip_rt_put(struct rtable *rt) { /* dst_release() accepts a NULL parameter. diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 78f84ae3ee12..0483519b7fb0 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -1895,42 +1895,30 @@ static int call_fib_nh_notifiers(struct fib_nh *nh, return NOTIFY_DONE; } -/* Update the PMTU of exceptions when: - * - the new MTU of the first hop becomes smaller than the PMTU - * - the old MTU was the same as the PMTU, and it limited discovery of - * larger MTUs on the path. With that limit raised, we can now - * discover larger MTUs - * A special case is locked exceptions, for which the PMTU is smaller - * than the minimal accepted PMTU: - * - if the new MTU is greater than the PMTU, don't make any change - * - otherwise, unlock and set PMTU +/* Walk the exceptions of a nexthop after its first hop MTU changed. The + * chain is RCU protected here, while fnhe_update_pmtu() takes fnhe_lock + * for the update of each entry. */ void fib_nhc_update_mtu(struct fib_nh_common *nhc, u32 new, u32 orig) { struct fnhe_hash_bucket *bucket; int i; - bucket = rcu_dereference_protected(nhc->nhc_exceptions, 1); + rcu_read_lock(); + bucket = rcu_dereference(nhc->nhc_exceptions); if (!bucket) - return; + goto out; for (i = 0; i < FNHE_HASH_SIZE; i++) { struct fib_nh_exception *fnhe; - for (fnhe = rcu_dereference_protected(bucket[i].chain, 1); + for (fnhe = rcu_dereference(bucket[i].chain); fnhe; - fnhe = rcu_dereference_protected(fnhe->fnhe_next, 1)) { - if (fnhe->fnhe_mtu_locked) { - if (new <= fnhe->fnhe_pmtu) { - fnhe->fnhe_pmtu = new; - fnhe->fnhe_mtu_locked = false; - } - } else if (new < fnhe->fnhe_pmtu || - orig == fnhe->fnhe_pmtu) { - fnhe->fnhe_pmtu = new; - } - } + fnhe = rcu_dereference(fnhe->fnhe_next)) + fnhe_update_pmtu(fnhe, new, orig); } +out: + rcu_read_unlock(); } void fib_sync_mtu(struct net_device *dev, u32 orig_mtu) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 152d8cb28f65..b82401a6baed 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -741,6 +741,35 @@ static void update_or_create_fnhe(struct fib_nh_common *nhc, __be32 daddr, spin_unlock_bh(&fnhe_lock); } +/* Update the PMTU of an exception when: + * - the new MTU of the first hop becomes smaller than the PMTU + * - the old MTU was the same as the PMTU, and it limited discovery of + * larger MTUs on the path. With that limit raised, we can now + * discover larger MTUs + * A special case is locked exceptions, for which the PMTU is smaller + * than the minimal accepted PMTU: + * - if the new MTU is greater than the PMTU, don't make any change + * - otherwise, unlock and set PMTU + * + * fnhe_lock keeps fnhe_pmtu and fnhe_mtu_locked consistent against + * update_or_create_fnhe(), which sets both under the same lock. + */ +void fnhe_update_pmtu(struct fib_nh_exception *fnhe, u32 new, u32 orig) +{ + spin_lock_bh(&fnhe_lock); + + if (fnhe->fnhe_mtu_locked) { + if (new <= fnhe->fnhe_pmtu) { + fnhe->fnhe_pmtu = new; + fnhe->fnhe_mtu_locked = false; + } + } else if (new < fnhe->fnhe_pmtu || orig == fnhe->fnhe_pmtu) { + fnhe->fnhe_pmtu = new; + } + + spin_unlock_bh(&fnhe_lock); +} + static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flowi4 *fl4, bool kill_route) { From b37971686ec59fb027fa4910ba16805e68fddb97 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 9 Aug 2026 20:18:29 +0900 Subject: [PATCH 095/171] vxlan: do not arm the ageing timer on a device that is down vxlan_changelink() arms vxlan->age_timer whenever the requested ageing interval differs from the configured one: if (conf.age_interval != vxlan->cfg.age_interval) mod_timer(&vxlan->age_timer, jiffies); There is no netif_running() test, so the timer is armed even on a device that was never brought up. The only synchronous cancel in the driver is the timer_delete_sync() in vxlan_stop(), which is .ndo_stop. netif_close_many() drops devices without IFF_UP before __dev_close_many() runs, so that cancel is skipped for such a device. vxlan_setup() sets dev->needs_free_netdev = true and age_timer is a member of struct vxlan_dev, so free_netdev() releases the allocation the timer lives in while it is still queued on a timer_base. expire_timers() unlinks the entry before it loads timer->function, so the timer core writes through the freed object's list pointers: BUG: KASAN: slab-use-after-free in __run_timers+0x208/0x654 Write of size 8 at addr ffff00001adace68 by task true/192 __asan_store8+0x84/0xac __run_timers+0x208/0x654 run_timer_softirq+0x154/0x18c Allocated by task 189: alloc_netdev_mqs+0x64/0x720 rtnl_create_link+0x4ac/0x520 rtnl_newlink+0x758/0xd00 Freed by task 191: netdev_release+0x40/0x58 netdev_run_todo+0x4a4/0x8c0 rtnl_dellink+0x200/0x4e8 The rtnl operations involved are netns-scoped, so an unprivileged user can perform them in a new user and network namespace. Arming the timer on a down device never had an effect: vxlan_cleanup() returns early on !netif_running(), and vxlan_open() arms the timer for any non-zero interval once the device is brought up. Add the missing test. Discovered by XBOW, triaged by Baul Lee Fixes: 40051c4dcad5 ("vxlan: Allow changing ageing time") Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260809111829.78834-1-baul.lee@xbow.com Signed-off-by: Paolo Abeni --- drivers/net/vxlan/vxlan_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 1ded27768a97..824144bb7774 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -4500,7 +4500,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], if (change_igmp && vxlan_addr_multicast(&dst->remote_ip)) err = vxlan_multicast_leave(vxlan); - if (conf.age_interval != vxlan->cfg.age_interval) + if (netif_running(dev) && conf.age_interval != vxlan->cfg.age_interval) mod_timer(&vxlan->age_timer, jiffies); netdev_adjacent_change_commit(dst->remote_dev, lowerdev, dev); From b48a0a0a76ccecec60f0568e2af4d89994b08bec Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 10 Aug 2026 14:10:38 +0300 Subject: [PATCH 096/171] pmdomain: qcom: rpmhpd: Add missing MXC and MMCX power domains for Eliza MXC and MMCX were initially omitted as they were believed to have no consumers, and they were not present downstream either. The Iris video codec requires these power domains, so add MXC and MMCX along with the MXC_AO and MMCX_AO. Fixes: f387ecdd492f ("pmdomain: qcom: rpmhpd: Add Eliza RPMh Power Domains") Signed-off-by: Abel Vesa Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/rpmhpd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index 63120e703923..ffc46de8d07a 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -245,9 +245,13 @@ static struct rpmhpd *eliza_rpmhpds[] = { [RPMHPD_GFX] = &gfx, [RPMHPD_LCX] = &lcx, [RPMHPD_LMX] = &lmx, + [RPMHPD_MMCX] = &mmcx, + [RPMHPD_MMCX_AO] = &mmcx_ao, [RPMHPD_MSS] = &mss, [RPMHPD_MX] = &mx, [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, [RPMHPD_NSP] = &nsp, }; From 01fdecc0480d916c799dbee584833a4a37e94d06 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Fri, 7 Aug 2026 14:34:04 +0800 Subject: [PATCH 097/171] net: packet: fix wrong transport_header when sending VLAN-tagged frame In packet_parse_headers(), when processing a VLAN-tagged frame, skb_set_network_header() is called to advance network_header past the VLAN tag to the inner protocol header. skb_probe_transport_header() is then called with skb->protocol still set to the outer VLAN EtherType (e.g. ETH_P_8021Q), while nhoff (derived from skb_network_offset()) already points past the VLAN tag to the inner protocol header. In __skb_flow_dissect(), proto is initialized to ETH_P_8021Q and nhoff points past the VLAN tag. When the dissector hits case ETH_P_8021Q, it reads a struct vlan_hdr at nhoff via __skb_header_pointer(), but that offset contains the inner protocol header (e.g. an IP header). The bytes are misinterpreted as a VLAN header, yielding a garbage encapsulated EtherType that matches no known protocol. The dissector returns false, so skb_probe_transport_header() never calls skb_set_transport_header(), leaving transport_header at its uninitialized sentinel value (~0U). Move skb_probe_transport_header() to before skb_set_network_header(). At the time skb_probe_transport_header() is called, network_header still points to the VLAN header, so nhoff correctly points to the VLAN header. The flow dissector can then parse the VLAN header, extract the inner EtherType, and advance nhoff to the inner protocol header, allowing transport_header to be set correctly. Fixes: dfed913e8b55 ("net/af_packet: add VLAN support for AF_PACKET SOCK_RAW GSO") Assisted-by: WChat:claude-opus-4-8 Signed-off-by: Wei Fang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260807063405.688780-2-wei.fang@oss.nxp.com Signed-off-by: Paolo Abeni --- net/packet/af_packet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 435756877aba..213a8a3b7ee5 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1943,13 +1943,13 @@ static void packet_parse_headers(struct sk_buff *skb, struct socket *sock) sock->type == SOCK_RAW) skb->protocol = dev_parse_header_protocol(skb); + skb_probe_transport_header(skb); + /* Move network header to the right position for VLAN tagged packets */ if (likely(skb->dev->type == ARPHRD_ETHER) && eth_type_vlan(skb->protocol) && vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); - - skb_probe_transport_header(skb); } /* From cbb35cbe8db268fefe34c23df15348cf99025298 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Fri, 7 Aug 2026 14:34:05 +0800 Subject: [PATCH 098/171] net: tap: fix wrong transport_header when sending VLAN-tagged frame In tap_get_user_xdp(), when processing a VLAN-tagged frame (e.g. ETH_P_8021Q), skb_set_network_header() is called first to advance network_header past the VLAN tag to the inner protocol header. skb_probe_transport_header() is then called with skb->protocol still set to ETH_P_8021Q, while nhoff (derived from skb_network_offset()) already points past the VLAN tag to the inner protocol header. In __skb_flow_dissect(), proto is initialized to ETH_P_8021Q and nhoff points past the VLAN tag. When the dissector hits case ETH_P_8021Q, it reads a struct vlan_hdr at the current nhoff via __skb_header_pointer(), but that offset contains the inner protocol header (e.g. an IP header). The bytes are misinterpreted as a VLAN header, yielding a garbage encapsulated EtherType that matches no known protocol. The dissector returns false, so skb_probe_transport_header() never calls skb_set_transport_header(), leaving transport_header at its uninitialized sentinel value (~0U). Move skb_set_network_header() to after skb_probe_transport_header(). At the time skb_probe_transport_header() is called, network_header still points to the VLAN header (offset ETH_HLEN), so nhoff is correct and the flow dissector can parse the VLAN header, extract the inner EtherType, and advance nhoff to the inner protocol header, allowing transport_header to be set correctly. Fixes: 8c76e77f9069 ("tap: call skb_probe_transport_header after setting skb->dev") Assisted-by: WChat:claude-opus-4-8 Signed-off-by: Wei Fang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260807063405.688780-3-wei.fang@oss.nxp.com Signed-off-by: Paolo Abeni --- drivers/net/tap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 5d2d34d24ce8..9b5af08cdd47 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -1091,12 +1091,13 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) } } + skb_probe_transport_header(skb); + /* Move network header to the right position for VLAN tagged packets */ if (eth_type_vlan(skb->protocol) && vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); - skb_probe_transport_header(skb); dev_queue_xmit(skb); rcu_read_unlock(); From 5f3a13e0bb5ebcc1ca2dfda42ea40b9f3c2be6ea Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Fri, 7 Aug 2026 14:22:14 +0800 Subject: [PATCH 099/171] net: ngbe: fix NULL pointer dereference in non-MSI-X interrupt enabling In non-MSI-X mode (such as legacy INTx or single MSI), wx->msix_entry is not allocated or initialized. Calling NGBE_INTR_MISC(wx) dereferences wx->msix_entry->entry, leading to a NULL pointer dereference crash. This issue was introduced by fixing the IRQ vector when the number of VFs is 7. Fix the issue by explicitly checking `pdev->msix_enabled` to determine the correct vector index. Additionally, as a side fix, set the interrupt mask to BIT(0) for the non-MSI-X fallback. In MSI/INTx mode, the MISC and queue interrupts share vector 0, and the WX_PX_MISC_IVAR register is only valid in the MSI-X case. Thus, BIT(0) is the correct mask for the miscellaneous cause when MSI-X is disabled. Fixes: 4174c0c331a2 ("net: ngbe: specify IRQ vector when the number of VFs is 7") Signed-off-by: Jiawen Wu Reviewed-by: Breno Leitao Reviewed-by: Simon Horman Link: https://patch.msgid.link/B2693E9A8BFAD110+20260807062214.410838-1-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index a16221995909..cb653861aad8 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -180,8 +180,10 @@ static void ngbe_irq_enable(struct wx *wx, bool queues) /* mask interrupt */ if (queues) wx_intr_enable(wx, NGBE_INTR_ALL); - else + else if (wx->pdev->msix_enabled) wx_intr_enable(wx, NGBE_INTR_MISC(wx)); + else + wx_intr_enable(wx, BIT(0)); } /** From 976df67f463db1fddaf2a32fb04f57ad2891a23d Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 6 Aug 2026 20:44:07 -0400 Subject: [PATCH 100/171] net/tls: Fail tls_sw_splice_read() after a failed async decrypt When an async decrypt fails, tls_decrypt_done() records the error in ctx->async_wait.err and calls tls_err_abort(), which stores it in sk_err. tls_sw_recvmsg() and tls_sw_read_sock() each read async_wait.err once they hold the reader lock and fail the call: a record that did not authenticate breaks the connection. tls_sw_splice_read() has no such check, and sk_err does not stand in for one. tls_rx_rec_wait() tests sk_err only inside the loop it skips whenever a record is already parsed, and the first reader to reach sock_error() clears it, while async_wait.err persists. A splice therefore keeps delivering records on a connection that recvmsg() and read_sock() refuse to read. Read async_wait.err in tls_sw_splice_read() as the other two readers do. Fixes: f314bfee81b1 ("tls: rx: return the already-copied data on crypto error") Reviewed-by: Jakub Kicinski Signed-off-by: Chuck Lever Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260806-tls-splice-crypto-fix-v1-1-a2624005a286@kernel.org Signed-off-by: Paolo Abeni --- net/tls/tls_sw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 62d46736e24b..d1ad31986cf2 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -2014,6 +2014,11 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, if (err < 0) return err; + /* If crypto failed the connection is broken */ + err = ctx->async_wait.err; + if (err) + goto splice_read_end; + if (!skb_queue_empty(&ctx->rx_list)) { skb = __skb_dequeue(&ctx->rx_list); } else { From 8a422297391328b8128e5f6b7e1c49b0240ffa82 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 6 Aug 2026 20:44:08 -0400 Subject: [PATCH 101/171] selftests: tls: cover splice after a failed decrypt Nothing in this file splices a socket whose last decrypt failed, so the check that fails tls_sw_splice_read() on a broken connection can be removed without a test noticing. Such a splice hands the application plaintext that recvmsg() and read_sock() already refuse to return. Extend the bad_auth pattern. Corrupt an authenticated record, confirm recvmsg() reports EBADMSG, then splice the same socket and require EBADMSG again. A synchronous decrypt fails again on the still-queued record, so only an async decrypt reaches EBADMSG through the recorded-failure check alone. bad_auth builds the same corrupted record, so its construction moves into a helper the two tests share. Signed-off-by: Chuck Lever Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260806-tls-splice-crypto-fix-v1-2-a2624005a286@kernel.org Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/tls.c | 75 ++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index a0e72e402748..9d3cd4fff062 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -24,6 +24,7 @@ #include "kselftest_harness.h" #define TLS_PAYLOAD_MAX_LEN 16384 +#define TLS_HDR_LEN 5 #define SOL_TLS 282 static int fips_enabled; @@ -2732,28 +2733,83 @@ TEST_F(tls_err, bad_rec) EXPECT_EQ(errno, EAGAIN); } +/* cfd carries a byte stream, so one recv() can return part of a + * record. Take the fragment length from the record header and wait + * for the remainder. + */ +static void tls_send_bad_auth(struct __test_metadata *_metadata, + int fd, int cfd, int fd2) +{ + char buf[128]; + int len; + + memrnd(buf, sizeof(buf) / 2); + ASSERT_EQ(send(fd, buf, sizeof(buf) / 2, 0), sizeof(buf) / 2); + + ASSERT_EQ(recv(cfd, buf, TLS_HDR_LEN, MSG_WAITALL), TLS_HDR_LEN); + + len = ((unsigned char)buf[3] << 8) | (unsigned char)buf[4]; + ASSERT_GT(len, 0); + ASSERT_LE(len, (int)sizeof(buf) - TLS_HDR_LEN); + + ASSERT_EQ(recv(cfd, buf + TLS_HDR_LEN, len, MSG_WAITALL), len); + + buf[TLS_HDR_LEN + len - 1]++; + + ASSERT_EQ(send(fd2, buf, TLS_HDR_LEN + len, 0), TLS_HDR_LEN + len); +} + TEST_F(tls_err, bad_auth) { char buf[128]; - int n; if (self->notls) SKIP(return, "no TLS support"); - memrnd(buf, sizeof(buf) / 2); - EXPECT_EQ(send(self->fd, buf, sizeof(buf) / 2, 0), sizeof(buf) / 2); - n = recv(self->cfd, buf, sizeof(buf), 0); - EXPECT_GT(n, sizeof(buf) / 2); + tls_send_bad_auth(_metadata, self->fd, self->cfd, self->fd2); - buf[n - 1]++; - - EXPECT_EQ(send(self->fd2, buf, n, 0), n); EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1); EXPECT_EQ(errno, EBADMSG); EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1); EXPECT_EQ(errno, EBADMSG); } +/* A record that did not authenticate breaks the connection for every + * reader, splice included. + * + * The two decrypt paths reach that result differently. A synchronous + * decrypt leaves the record parsed, so the splice re-runs the decrypt + * and fails on the record itself; the ctx->async_wait.err check in + * tls_sw_splice_read() is not what stops it. Only an asynchronous + * decrypt, which needs a TLS 1.2 socket and an AEAD advertising + * CRYPTO_ALG_ASYNC, consumes the record before the failure is + * recorded, leaving that check the sole reason the splice fails. + */ +TEST_F(tls_err, bad_auth_splice) +{ + char buf[128]; + ssize_t ret; + int p[2]; + + if (self->notls) + SKIP(return, "no TLS support"); + + tls_send_bad_auth(_metadata, self->fd, self->cfd, self->fd2); + + EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1); + EXPECT_EQ(errno, EBADMSG); + + ASSERT_GE(pipe(p), 0); + + ret = splice(self->cfd2, NULL, p[1], NULL, sizeof(buf), + SPLICE_F_NONBLOCK); + EXPECT_EQ(ret, -1); + EXPECT_EQ(errno, EBADMSG); + + close(p[0]); + close(p[1]); +} + TEST_F(tls_err, bad_in_large_read) { char txt[3][64]; @@ -3009,7 +3065,6 @@ static size_t parse_tls_records(struct __test_metadata *_metadata, { const __u8 *rec = rx_buf; size_t total_plaintext_rx = 0; - const __u8 rec_header_len = 5; while (rec < rx_buf + rx_len) { __u16 record_payload_len; @@ -3029,7 +3084,7 @@ static size_t parse_tls_records(struct __test_metadata *_metadata, /* Plaintext must not exceed the specified limit */ ASSERT_LE(plaintext_len, max_payload_len); - rec += rec_header_len + record_payload_len; + rec += TLS_HDR_LEN + record_payload_len; } return total_plaintext_rx; From 78983d82dc4c677c5cd2941ef828b1903ca51c9e Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Fri, 7 Aug 2026 20:46:23 +0530 Subject: [PATCH 102/171] ASoC: tac5xx2-sdw: select REGMAP_SOUNDWIRE_MBQ "select REGMAP_SOUNDWIRE_MBQ" to fix build error: ERROR: modpost: "__devm_regmap_init_sdw_mbq" [sound/soc/codecs/snd-soc-tac5xx2-sdw.ko] undefined! Signed-off-by: Niranjan H Y Link: https://patch.msgid.link/20260807151623.3005-1-niranjan.hy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index d43475a63ac2..e4d139108d72 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2157,6 +2157,7 @@ config SND_SOC_TAC5XX2_SDW tristate "Texas Instruments TAC5XX2 SoundWire Smart Amplifier" depends on SOUNDWIRE depends on SND_SOC_SDCA + select REGMAP_SOUNDWIRE_MBQ help This option enables support for Texas Instruments TAC5XX2 family of SoundWire Smart Amplifiers. This includes TAC5572, TAC5672, From cba9ccb47e9fa4cc77692fb896cc5ab57a667882 Mon Sep 17 00:00:00 2001 From: Jun Yang Date: Mon, 10 Aug 2026 18:21:38 +0800 Subject: [PATCH 103/171] tipc: read le->link under the node lock in tipc_node_link_down() tipc_node_link_down() caches the link pointer before taking n->lock: struct tipc_link *l = le->link; /* unlocked */ if (!l) return; tipc_node_write_lock(n); if (!tipc_link_is_establishing(l)) { /* deref l */ ... tipc_link_reset(l); /* write into l */ if (delete) { kfree(l); le->link = NULL; The delete=true caller frees that very object under n->lock, so the lock does not protect the cached pointer against it: - CPU A, delete=false: tipc_rcv() on TIPC_LINK_DOWN_EVT, or the link supervision timer via tipc_node_timeout(), reads l unlocked and then dereferences it under n->lock; - CPU B, delete=true: netlink TIPC_NL_BEARER_DISABLE -> bearer_disable() -> tipc_node_delete_links() -> tipc_node_link_down(n, bearer_id, true) -> kfree(l). The link is freed with plain kfree(), not kfree_rcu(), and for UDP bearers disable_media() only schedules the asynchronous cleanup_bearer() work, so its synchronize_net() runs after the links are already gone. An in-flight CPU A that has read l therefore dereferences freed memory once B frees it: a use-after-free read in tipc_link_is_establishing(), and a use-after-free write via tipc_link_reset() on the establishing branch. The following trace was captured on 7.2.0-rc5-00284-gaf39eb111ce6: BUG: KASAN: slab-use-after-free in tipc_link_is_establishing (net/tipc/link.c:285) Read of size 4 at addr ffff88802e2aa068 by task swapper/2/0 tipc_link_is_establishing (net/tipc/link.c:285) tipc_node_link_down (net/tipc/node.c:1076) tipc_node_timeout (net/tipc/node.c:843) Allocated by task 9549: tipc_link_create (net/tipc/link.c:490) tipc_node_check_dest (net/tipc/node.c:1279) tipc_disc_rcv (net/tipc/discover.c:252) tipc_udp_recv (net/tipc/udp_media.c:389) Freed by task 9549: tipc_node_link_down (net/tipc/node.c:1084) tipc_node_delete_links (net/tipc/node.c:1320) bearer_disable (net/tipc/bearer.c:414) __tipc_nl_bearer_disable (net/tipc/bearer.c:992) Move the le->link read inside tipc_node_write_lock(), so it is serialised against the kfree() in the delete path. A racing teardown now either has not run yet, and we see a valid link, or has already run, and we see NULL. Fixes: 73f646cec354 ("tipc: delay ESTABLISH state event when link is established") Cc: stable@kernel.org Reported-by: TencentOS Corvus AI Assisted-by: tencentos-corvus-ai:kimi-k3 Signed-off-by: Jun Yang Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260810102147.48191-1-juny24602@gmail.com Signed-off-by: Paolo Abeni --- net/tipc/node.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/net/tipc/node.c b/net/tipc/node.c index 8e4ef2630ae4..683a136e53ef 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -1061,18 +1061,23 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id, static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete) { - struct tipc_link_entry *le = &n->links[bearer_id]; struct tipc_media_addr *maddr = NULL; - struct tipc_link *l = le->link; int old_bearer_id = bearer_id; + struct tipc_link_entry *le; struct sk_buff_head xmitq; - - if (!l) - return; + struct tipc_link *l; __skb_queue_head_init(&xmitq); + /* Synchronize the link lookup with bearer teardown. */ tipc_node_write_lock(n); + le = &n->links[bearer_id]; + l = le->link; + if (!l) { + tipc_node_write_unlock_fast(n); + return; + } + if (!tipc_link_is_establishing(l)) { __tipc_node_link_down(n, &bearer_id, &xmitq, &maddr); } else { From 133c71b2c0bc976a4751f9e05ef7cdea67f964e5 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 3 Aug 2026 21:59:25 +0800 Subject: [PATCH 104/171] spi: dw: fix wrong RX_SAMPLE_DLY setting after resume On platforms which need a non-zero rx sample delay, the RX_SAMPLE_DLY reg setting is lost after resume. The reason is that the reg may be reset to 0 after resuming, but dws->cur_rx_sample_dly doesn't know this fact. Fix this issue by clearing dws->cur_rx_sample_dly in dw_spi_shutdown_chip(). Signed-off-by: Jisheng Zhang Suggested-by: Mark Brown Link: https://patch.msgid.link/20260803135925.12622-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-dw.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 2f2debc64e73..a9d055f9c5dd 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -283,6 +283,7 @@ static inline void dw_spi_shutdown_chip(struct dw_spi *dws) dw_spi_enable_chip(dws, 0); dw_spi_set_clk(dws, 0); dws->current_freq = 0; + dws->cur_rx_sample_dly = 0; } extern void dw_spi_set_cs(struct spi_device *spi, bool enable); From f12afefb7b01f94d6d66d397f323a9914edbf70e Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 6 Aug 2026 16:32:31 -0700 Subject: [PATCH 105/171] ASoC: xilinx: formatter_pcm: pass aud_drv_data to irq handlers The irq handlers take a struct device pointer and call dev_get_drvdata() to obtain the driver data. However, the driver data is only set at the end of probe, after devm_request_irq(), so an interrupt taken in between causes the handlers to pass a NULL pointer to readl() and crash. Pass the private data directly as the devm_request_irq() argument instead of the device pointer, matching what the handlers expect. Fixes: 6f6c3c36f091 ("ASoC: xlnx: add pcm formatter platform driver") Assisted-by: opencode:deepseek-v4-flash-free Signed-off-by: Rosen Penev Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260806233231.30631-1-rosenp@gmail.com Signed-off-by: Mark Brown --- sound/soc/xilinx/xlnx_formatter_pcm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c index 8f7a76758535..f15336197ed3 100644 --- a/sound/soc/xilinx/xlnx_formatter_pcm.c +++ b/sound/soc/xilinx/xlnx_formatter_pcm.c @@ -281,8 +281,7 @@ static irqreturn_t xlnx_mm2s_irq_handler(int irq, void *arg) { u32 val; void __iomem *reg; - struct device *dev = arg; - struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev); + struct xlnx_pcm_drv_data *adata = arg; reg = adata->mmio + XLNX_MM2S_OFFSET + XLNX_AUD_STS; val = readl(reg); @@ -300,8 +299,7 @@ static irqreturn_t xlnx_s2mm_irq_handler(int irq, void *arg) { u32 val; void __iomem *reg; - struct device *dev = arg; - struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev); + struct xlnx_pcm_drv_data *adata = arg; reg = adata->mmio + XLNX_S2MM_OFFSET + XLNX_AUD_STS; val = readl(reg); @@ -637,7 +635,7 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev) } ret = devm_request_irq(dev, aud_drv_data->mm2s_irq, xlnx_mm2s_irq_handler, 0, - "xlnx_formatter_pcm_mm2s_irq", dev); + "xlnx_formatter_pcm_mm2s_irq", aud_drv_data); if (ret) { dev_err(dev, "xlnx audio mm2s irq request failed\n"); goto clk_err; @@ -664,7 +662,7 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev) ret = devm_request_irq(dev, aud_drv_data->s2mm_irq, xlnx_s2mm_irq_handler, 0, "xlnx_formatter_pcm_s2mm_irq", - dev); + aud_drv_data); if (ret) { dev_err(dev, "xlnx audio s2mm irq request failed\n"); goto clk_err; From ae7fd6ff4c6713270d2efe6db87a4a58ccb7cc61 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 11 Aug 2026 14:18:16 +0100 Subject: [PATCH 106/171] regmap: sdw-mbq: Fix swap of timeout and retry times When polling Function Busy using read_poll_timeout() the total timeout and retry delay arguments are swapped. This leads to only a single retry being processed, it seems the existing users typically do succeed before the first retry. Swap the arguments over to ensure the correct polling time. Reported-by: Ville Saarinen Link: https://lore.kernel.org/linux-sound/ansTPGgVNoDJlA5r@opensource.cirrus.com/T/#m680731a2f307f1f5176b27ed5aa560ddc94e5d62 Fixes: 5bc493bf0c37 ("regmap: sdw-mbq: Add support for SDCA deferred controls") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260811131816.332082-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-sdw-mbq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap-sdw-mbq.c b/drivers/base/regmap/regmap-sdw-mbq.c index 2585933d4946..6d7c760cbef2 100644 --- a/drivers/base/regmap/regmap-sdw-mbq.c +++ b/drivers/base/regmap/regmap-sdw-mbq.c @@ -59,7 +59,7 @@ static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg, if (ctx->readable_reg(dev, reg)) { ret = read_poll_timeout(sdw_read_no_pm, val, val < 0 || !(val & SDCA_CTL_ENTITY_0_FUNCTION_BUSY), - ctx->cfg.timeout_us, ctx->cfg.retry_us, + ctx->cfg.retry_us, ctx->cfg.timeout_us, false, slave, reg); if (val < 0) return val; From 079e27f52b929b554b90514b081ea40b3d632a25 Mon Sep 17 00:00:00 2001 From: Balamurugan C Date: Tue, 11 Aug 2026 08:53:54 +0800 Subject: [PATCH 107/171] ASoC: Intel: NVL: Add entry for HDMI-In capture support to non-I2S codec boards. Adding HDMI-In capture support for the NVL products which doesn't have onboard I2S codec. But need to support HDMI-In capture via I2S and audio playback through HDMI/DP monitor. Signed-off-by: Balamurugan C Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260811005354.2884137-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_ssp_amp.c | 6 ++++++ sound/soc/intel/common/soc-acpi-intel-nvl-match.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c index 729c0cd7c19c..b80d24966cbd 100644 --- a/sound/soc/intel/boards/sof_ssp_amp.c +++ b/sound/soc/intel/boards/sof_ssp_amp.c @@ -222,6 +222,12 @@ static const struct platform_device_id board_ids[] = { /* SSP 0 and SSP 2 are used for HDMI IN */ SOF_HDMI_PLAYBACK_PRESENT), }, + { + .name = "nvl_lt6911_hdmi_ssp", + .driver_data = (kernel_ulong_t)(SOF_SSP_MASK_HDMI_CAPTURE(0x5) | + /* SSP 0 and SSP 2 are used for HDMI IN */ + SOF_HDMI_PLAYBACK_PRESENT), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c index 4a6208f821eb..8f6e987c791e 100644 --- a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c @@ -56,6 +56,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | SND_SOC_ACPI_TPLG_INTEL_SSP_MSB, }, + /* place amp/hdmi-in only boards in the end of table */ + { + .id = "INTC10B0", + .drv_name = "nvl_lt6911_hdmi_ssp", + .sof_tplg_filename = "sof-nvl-hdmi-ssp02.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_nvl_machines); From 6bcd76c134c55c697148acb5c0194e9666abdf84 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 10 Aug 2026 15:04:47 +0000 Subject: [PATCH 108/171] af_packet: Don't send zero-byte data in tpacket_snd(). syzbot reported a WARNING in __dev_queue_xmit() triggered via tpacket_snd(): skb_assert_len WARNING: at include/linux/skbuff.h:2753 skb_assert_len WARNING: at __dev_queue_xmit+0x21bc/0x4970 net/core/dev.c:4781 Call Trace: dev_queue_xmit include/linux/netdevice.h:3448 [inline] packet_xmit+0x243/0x310 net/packet/af_packet.c:276 tpacket_snd net/packet/af_packet.c:2907 [inline] packet_sendmsg+0x28d6/0x4eb0 net/packet/af_packet.c:3134 When sending 0-byte packets via TPACKET ring buffer on devices with no hard header (e.g. dev->hard_header_len == 0), tpacket_fill_skb() populates an skb with skb->len == 0 and returns 0. tpacket_snd() then forwards this empty skb to packet_xmit(), causing __dev_queue_xmit() to hit skb_assert_len(skb). Similar checks exist in packet_snd() via commit dc633700f00f ("net/af_packet: check len when min_header_len equals to 0") and in packet_sendmsg_spkt() via commit 6a341729fb31 ("af_packet: Don't send zero-byte data in packet_sendmsg_spkt()."). Return -EINVAL in tpacket_fill_skb() when skb->len is zero to reject zero-length packets in tpacket_snd(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot+30b93b6845b19cc38581@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a79e807.01d0871a.3a0d52.00ac.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Reviewed-by: Willem de Bruijn Reviewed-by: Jiayuan Chen Link: https://patch.msgid.link/20260810150447.1220864-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/packet/af_packet.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 213a8a3b7ee5..1168bd6b09cd 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2660,6 +2660,9 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, len = ((to_write > len_max) ? len_max : to_write); } + if (unlikely(!skb->len)) + return -EINVAL; + packet_parse_headers(skb, sock); return tp_len; From f60b396ee174206fe08ebf997d16cd3801b77b22 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sun, 9 Aug 2026 05:09:28 -0400 Subject: [PATCH 109/171] net/sched: act_api: fix TOCTOU NULL deref on a->goto_chain tcf_action_exec() handles TC_ACT_GOTO_CHAIN by first checking rcu_access_pointer(a->goto_chain) and then calling tcf_action_goto_chain_exec(), which does a second, independent rcu_dereference_bh(a->goto_chain) read and immediately dereferences chain->filter_chain. A concurrent tcf_action_set_ctrlact() (e.g. the gact replace path) can clear a->goto_chain between the two reads, so the second read returns NULL and tcf_action_goto_chain_exec() dereferences NULL. Fix the race by doing a single rcu_dereference_bh() read of a->goto_chain in tcf_action_exec(), checking it once for NULL, and passing the resulting chain pointer into tcf_action_goto_chain_exec(). This turns the split check/use into a single check/use on one value. Fixes: ee3bbfe806cd ("net/sched: let actions use RCU to access 'goto_chain'") Reported-by: vega@nebusec.ai Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Reviewed-by: Davide Caratti Link: https://patch.msgid.link/20260809090928.868186-1-jhs@mojatatu.com Signed-off-by: Jakub Kicinski --- net/sched/act_api.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index f141634df214..600b7804befd 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -41,11 +41,9 @@ int tcf_dev_queue_xmit(struct sk_buff *skb, int (*xmit)(struct sk_buff *skb)) } EXPORT_SYMBOL_GPL(tcf_dev_queue_xmit); -static void tcf_action_goto_chain_exec(const struct tc_action *a, +static void tcf_action_goto_chain_exec(const struct tcf_chain *chain, struct tcf_result *res) { - const struct tcf_chain *chain = rcu_dereference_bh(a->goto_chain); - res->goto_tp = rcu_dereference_bh(chain->filter_chain); } @@ -1170,12 +1168,14 @@ int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions, return TC_ACT_OK; } } else if (TC_ACT_EXT_CMP(ret, TC_ACT_GOTO_CHAIN)) { - if (unlikely(!rcu_access_pointer(a->goto_chain))) { + struct tcf_chain *chain = rcu_dereference_bh(a->goto_chain); + + if (unlikely(!chain)) { tcf_set_drop_reason(skb, SKB_DROP_REASON_TC_CHAIN_NOTFOUND); return TC_ACT_SHOT; } - tcf_action_goto_chain_exec(a, res); + tcf_action_goto_chain_exec(chain, res); } if (ret != TC_ACT_PIPE) From 6bf14575c65569dcded90ef78afb8a6d57323f04 Mon Sep 17 00:00:00 2001 From: Jordan Rhee Date: Fri, 7 Aug 2026 22:43:14 +0000 Subject: [PATCH 110/171] gve: fix zero-length skb frag with header-split When header split is enabled and a header-only packet is received such as a pure TCP ACK, GVE will indicate an RX SKB with a zero-length fragment. If this SKB is then hairpinned and sent back out, the GVE TX path will emit a zero-length descriptor. Hardware considers this an illegal descriptor and stops the queue, causing a TX timeout and interface reset. Fix it by not adding the zero-length skb frag. Cc: stable@vger.kernel.org Fixes: 5e37d8254e7f ("gve: Add header split data path") Suggested-by: Praveen Kaligineedi Co-developed-by: Ziwei Xiao Signed-off-by: Ziwei Xiao Signed-off-by: Jordan Rhee Signed-off-by: Harshitha Ramamurthy Link: https://patch.msgid.link/20260807224315.234152-2-hramamurthy@google.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/google/gve/gve_rx_dqo.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c index 8271f731a91f..0ece2f6fdffb 100644 --- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c @@ -886,6 +886,11 @@ static int gve_rx_dqo(struct napi_struct *napi, struct gve_rx_ring *rx, rx->rx_hsplit_unsplit_pkt += unsplit; rx->rx_hsplit_bytes += hdr_len; u64_stats_update_end(&rx->statss); + + if (!buf_len) { + gve_free_buffer(rx, buf_state); + return 0; + } } else if (!rx->ctx.skb_head && rx->dqo.page_pool && netmem_is_net_iov(buf_state->page_info.netmem)) { /* when header split is disabled, the header went to the packet From 3992ced109c70b771efad9e51ae68e5c7a04dea3 Mon Sep 17 00:00:00 2001 From: Jordan Rhee Date: Fri, 7 Aug 2026 22:43:15 +0000 Subject: [PATCH 111/171] gve: fix NULL dereference due to missing ptp adjfine Fix NULL dereference due to missing implementation of adjfine, which can be triggered from usermode as follows: sudo ./testptp -d /dev/ptp0 -f 0 [ 551.943697] BUG: kernel NULL pointer dereference, address: 0000000000000000 [...] [ 552.061946] Call Trace: [ 552.064487] [ 552.066681] ptp_clock_adjtime+0x1c0/0x2c0 [ 552.070874] ? get_clock_desc+0x6b/0xb0 [ 552.074825] pc_clock_adjtime+0x78/0xc0 [ 552.078755] __do_sys_clock_adjtime+0x85/0x110 [ 552.083293] do_syscall_64+0xea/0x610 Cc: stable@vger.kernel.org Fixes: acd16380523b ("gve: Add initial PTP device support") Signed-off-by: Jordan Rhee Signed-off-by: Harshitha Ramamurthy Reviewed-by: Vadim Fedorenko Link: https://patch.msgid.link/20260807224315.234152-3-hramamurthy@google.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/google/gve/gve_ptp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/google/gve/gve_ptp.c b/drivers/net/ethernet/google/gve/gve_ptp.c index 06b1cf4a5efc..1d6c59f4ead3 100644 --- a/drivers/net/ethernet/google/gve/gve_ptp.c +++ b/drivers/net/ethernet/google/gve/gve_ptp.c @@ -26,6 +26,11 @@ int gve_clock_nic_ts_read(struct gve_priv *priv) return 0; } +static int gve_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + return -EOPNOTSUPP; +} + static int gve_ptp_gettimex64(struct ptp_clock_info *info, struct timespec64 *ts, struct ptp_system_timestamp *sts) @@ -60,6 +65,7 @@ static long gve_ptp_do_aux_work(struct ptp_clock_info *info) static const struct ptp_clock_info gve_ptp_caps = { .owner = THIS_MODULE, .name = "gve clock", + .adjfine = gve_ptp_adjfine, .gettimex64 = gve_ptp_gettimex64, .settime64 = gve_ptp_settime64, .do_aux_work = gve_ptp_do_aux_work, From 6d3724e616faf952c3adcf8414fc21a828ef3709 Mon Sep 17 00:00:00 2001 From: Zhang Changzhong Date: Fri, 7 Aug 2026 15:50:38 +0800 Subject: [PATCH 112/171] net/sched: cls_u32: skip hash tables in u32_bind_class() u32_walk() enumerates both struct tc_u_hnode and struct tc_u_knode through the walker callback. u32_bind_class() unconditionally casts the passed fh to tc_u_knode and accesses &n->res, so when fh is actually a tc_u_hnode, which has no tcf_result member, this results in a slab-out-of-bounds read of res->classid in tc_cls_bind_class(). The issue can be reproduced with the following commands: tc qdisc add dev lo root handle 1: hfsc tc class add dev lo parent 1: classid 1:1 hfsc sc rate 1000kbit tc filter add dev lo parent 1:1 protocol ip prio 1 u32 match u32 0 0 flowid 1:1 tc class add dev lo parent 1: classid 1:2 hfsc sc rate 2000kbit Fix this by skipping hash tables via the TC_U32_KEY(handle) check. Fixes: 07d79fc7d94e ("net_sched: add reverse binding for tc class") Signed-off-by: Zhang Changzhong Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/1786089038-36366-1-git-send-email-zhangchangzhong@huawei.com Signed-off-by: Jakub Kicinski --- net/sched/cls_u32.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index ac98b1c2144a..c297d7dbcf91 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -1346,6 +1346,9 @@ static void u32_bind_class(void *fh, u32 classid, unsigned long cl, void *q, { struct tc_u_knode *n = fh; + if (TC_U32_KEY(n->handle) == 0) + return; + tc_cls_bind_class(classid, cl, q, &n->res, base); } From 090a95dbe13df9965279b588d97eda134831769c Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Sat, 8 Aug 2026 17:05:06 +0200 Subject: [PATCH 113/171] pmdomain: mediatek: mfg: initialize prev_o in mtk_mfg_attach_dev() mtk_mfg_attach_dev() reads prev_o on the first iteration of its loop, in "if (prev_o && prev_o->freq == o->freq)", before prev_o is assigned at the end of the loop body. On that first iteration, evaluating prev_o reads an indeterminate value. If it is non-NULL, the condition dereferences a stale or invalid pointer, potentially faulting or incorrectly skipping the first OPP. Initialize prev_o to NULL. This matches the intent as well: there is no previous OPP to compare against on the first iteration. Found with Clang's -Wconditional-uninitialized. Fixes: f08e7a4e8d6ac ("pmdomain: mediatek: Add support for MFlexGraphics") Assisted-by: Claude:claude-fable-5 Signed-off-by: Karl Mehltretter Reviewed-by: Nicolas Frattaroli Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c b/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c index 53bdab66cf15..9a8a38d98cce 100644 --- a/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c +++ b/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c @@ -657,7 +657,7 @@ static int mtk_mfg_attach_dev(struct generic_pm_domain *pd, struct device *dev) struct mtk_mfg *mfg = mtk_mfg_from_genpd(pd); struct dev_pm_opp_data *so = mfg->stack_opps; struct dev_pm_opp_data *go = mfg->gpu_opps; - struct dev_pm_opp_data *prev_o; + struct dev_pm_opp_data *prev_o = NULL; struct dev_pm_opp_data *o; int i, ret; From b64a9f67e082e04835ddd69d422a25168d69375b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Jean?= Date: Tue, 11 Aug 2026 19:10:11 +0000 Subject: [PATCH 114/171] pid: reject allocations through dead ancestor pid namespaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit alloc_pid() checks PIDNS_ADDING only on the leaf pid namespace before making a new struct pid visible in every ancestor namespace. That is insufficient when an unborn descendant pid namespace outlives an ancestor whose init task has already exited. The descendant can still be initialized later through setns(), and the new pid is then published into the dead ancestor as well. Keep the existing ENOMEM behavior, but require PIDNS_ADDING to be set in every namespace that will receive the new pid before publishing any of them. This preserves the invariant that free_pid() never decrements pid_allocated in a namespace whose child_reaper is no longer live. Fixes: a3bdc23ba8ea ("pid_namespace: allow opening pid_for_children before init was created") Signed-off-by: Jérémy Jean Reviewed-by: Pavel Tikhomirov Signed-off-by: Christian Brauner (Amutable) --- kernel/pid.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/pid.c b/kernel/pid.c index f55189a3d07d..d01d0dd7114b 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -324,8 +324,10 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid, * error path may try to wakeup the possibly freed ns->child_reaper. */ retval = -ENOMEM; - if (unlikely(!(ns->pid_allocated & PIDNS_ADDING))) - goto out_free; + for (upid = pid->numbers + ns->level; upid >= pid->numbers; --upid) + if (unlikely(!(upid->ns->pid_allocated & PIDNS_ADDING))) + goto out_free; + for (upid = pid->numbers + ns->level; upid >= pid->numbers; --upid) { /* Make the PID visible to find_pid_ns. */ idr_replace(&upid->ns->idr, pid, upid->nr); From 1fd495ef09eef96169a379a749c24b5e69974bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 31 Jul 2026 11:49:49 +0200 Subject: [PATCH 115/171] m68k: Define NR_CPUS to 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a Kconfig warning fs/erofs/Kconfig:137:warning: range is invalid which originates from EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS using NR_CPUS which up to now didn't exist for ARCH=m68k. All other architectures define this symbol, so fix the outlier. [geert] This also fixes: - CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS being set to the literal NR_CPUS instead of a number by automatic configs like "make allmodconfig" or "make olddefconfig", - An infinite loop in manual configs like "make oldconfig" when CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS is not present or has an invalid value in your existing .config. Fixes: c9b47e6b2311 ("erofs: cap LZMA stream pool size") Signed-off-by: Uwe Kleine-König Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260731094950.1988084-2-ukleinek@kernel.org Signed-off-by: Geert Uytterhoeven --- arch/m68k/Kconfig.cpu | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/m68k/Kconfig.cpu b/arch/m68k/Kconfig.cpu index 148f8a79d206..5a58cc13229e 100644 --- a/arch/m68k/Kconfig.cpu +++ b/arch/m68k/Kconfig.cpu @@ -549,6 +549,10 @@ config CACHE_COPYBACK endchoice endif # HAVE_CACHE_CB +config NR_CPUS + int + default "1" + # Coldfire cores that do not have a data cache configured can do coherent DMA. config COLDFIRE_COHERENT_DMA bool From 931cd1d1baeae68e8eb2c23bc1f3d8934dca6241 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Thu, 6 Aug 2026 12:45:24 +0800 Subject: [PATCH 116/171] drm/amdgpu: disallow multiple FENCE chunks in one submit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_cs_pass1() dispatches on chunk_id once per chunk without rejecting repeated ids. p->uf_bo is a single-slot field, so a submission carrying two AMDGPU_CHUNK_ID_FENCE chunks runs amdgpu_cs_p1_user_fence() twice, and the second run overwrites p->uf_bo with a freshly referenced BO without dropping the reference taken by the first. amdgpu_cs_parser_fini() only unrefs the final p->uf_bo, so every FENCE chunk but the last leaks a BO reference. The leaked BO outlives handle close and process exit. Reject duplicate FENCE chunks the same way commit fec5f8e8c6bc ("drm/amdgpu: disallow multiple BO_HANDLES chunks in one submit") did for p->bo_list. Fixes: d38ceaf99ed0 ("drm/amdgpu: add core driver (v4)") Reported-by: Yuhao Jiang Assisted-by: Claude:claude-opus-5 Cc: stable@vger.kernel.org Reviewed-by: Christian König Signed-off-by: Junrui Luo Signed-off-by: Alex Deucher (cherry picked from commit 665b1fc2a1845206408f9a2c6da67101789edb82) --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index f8bf0f6b5097..6798f6decc37 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -248,6 +248,10 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p, if (size < sizeof(struct drm_amdgpu_cs_chunk_fence)) goto free_partial_kdata; + /* Only a single user fence is allowed to simplify handling. */ + if (p->uf_bo) + goto free_partial_kdata; + ret = amdgpu_cs_p1_user_fence(p, p->chunks[i].kdata, &uf_offset); if (ret) From 5e9d136ad74df4edec67e502ce267597064d8f86 Mon Sep 17 00:00:00 2001 From: Candice Li Date: Mon, 27 Jul 2026 11:51:37 +0800 Subject: [PATCH 117/171] drm/amdgpu: validate GEM_CREATE domain combinations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AMDGPU_GEM_CREATE checked domain bits against AMDGPU_GEM_DOMAIN_MASK, but did not validate domain combinations. Userspace could combine CPU|GTT|VRAM with DOORBELL, GDS, GWS, or OA, making amdgpu_bo_placement_from_domain() exceed AMDGPU_BO_MAX_PLACEMENTS and hit BUG_ON(). Allow combinations only within CPU/GTT/VRAM, and require non-CPU/GTT/ VRAM domains to be specified one at a time. Return -EINVAL for invalid combinations in amdgpu_gem_create_ioctl(). v2: Rename helper from amdgpu_gem_domain_valid() to amdgpu_gem_are_domains_valid() (Christian) Signed-off-by: Candice Li Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit db39852d0c39843cb02048dfb47e4b8c703e9080) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 6a0699746fbc..f754a4a3a1c2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -397,6 +397,25 @@ const struct drm_gem_object_funcs amdgpu_gem_object_funcs = { .vm_ops = &amdgpu_gem_vm_ops, }; +static bool amdgpu_gem_are_domains_valid(u32 domains) +{ + u32 normal = AMDGPU_GEM_DOMAIN_CPU | + AMDGPU_GEM_DOMAIN_GTT | + AMDGPU_GEM_DOMAIN_VRAM; + /* Treat all non CPU/GTT/VRAM domains as special domains. */ + u32 special = AMDGPU_GEM_DOMAIN_MASK & ~normal; + u32 normal_mask = domains & normal; + u32 special_mask = domains & special; + + if (!special_mask) + return true; + + if (normal_mask) + return false; + + return !(special_mask & (special_mask - 1)); +} + /* * GEM ioctls. */ @@ -421,6 +440,8 @@ int amdgpu_gem_create_ioctl(struct drm_device *dev, void *data, /* reject invalid gem domains */ if (args->in.domains & ~AMDGPU_GEM_DOMAIN_MASK) return -EINVAL; + if (!amdgpu_gem_are_domains_valid(args->in.domains)) + return -EINVAL; if (!amdgpu_is_tmz(adev) && (flags & AMDGPU_GEM_CREATE_ENCRYPTED)) { DRM_NOTE_ONCE("Cannot allocate secure buffer since TMZ is disabled\n"); From f2a1c4c6fe0a6fcde02e59dde546dba28d283635 Mon Sep 17 00:00:00 2001 From: Samuel Pitoiset Date: Fri, 7 Aug 2026 16:58:55 +0200 Subject: [PATCH 118/171] drm/amd/display: Fix NULL pointer dereference in amdgpu_dm_crtc_set_vblank() amdgpu_dm_crtc_set_vblank() dereferences acrtc_state->stream when vblank is enabled/queried from DRM_IOCTL_MODE_CRTC_GET_SEQUENCE before a stream is attached to it. BUG: kernel NULL pointer dereference, address: 0000000000000008 RIP: amdgpu_dm_crtc_set_vblank+0x6b/0x4d0 [amdgpu] Call Trace: drm_vblank_enable drm_vblank_get drm_crtc_get_sequence_ioctl drm_ioctl_kernel drm_ioctl Reproduced by running VKCTS with WSI tests enabled on RADV. Guard the enable path on acrtc_state->stream being non-NULL, matching the existing checks in this function. Fixes: 34d66bc7ff10 ("drm/amd/display: Fix Xorg desktop unresponsive on Replay panel") Reviewed-by: Melissa Wen Signed-off-by: Samuel Pitoiset Signed-off-by: Alex Deucher (cherry picked from commit 7b1b31bf6942e6f43509b48da23f8e27269aac39) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index 06598273d481..56bf907f1f6c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -257,7 +257,7 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable) irq_type = amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id); - if (enable) { + if (enable && acrtc_state->stream) { struct dc *dc = adev->dm.dc; struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); struct psr_settings *psr = &acrtc_state->stream->link->psr_settings; From 47cd31185090bd1439d4587b835ac22d7ba6f1e3 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 5 Jun 2026 17:46:19 -0400 Subject: [PATCH 119/171] drm/amdgpu: fix missing check in vm_flush() We shouldn't return early if we need to emit spm update. Reviewed-by: David Rosca Signed-off-by: Alex Deucher (cherry picked from commit 54a118f1d7e184fcbb18f83889f48f17a767878a) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index dc6a9d7dd0b2..1baad7624f1f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -800,6 +800,7 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, mutex_unlock(&id_mgr->lock); gds_switch_needed &= !!ring->funcs->emit_gds_switch; + spm_update_needed &= !!adev->gfx.rlc.funcs->update_spm_vmid; vm_flush_needed &= !!ring->funcs->emit_vm_flush && job->vm_pd_addr != AMDGPU_BO_INVALID_OFFSET; pasid_mapping_needed &= adev->gmc.gmc_funcs->emit_pasid_mapping && @@ -811,7 +812,7 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, &job->base.s_fence->scheduled == isolation->spearhead; if (!vm_flush_needed && !gds_switch_needed && !need_pipe_sync && - !cleaner_shader_needed) + !cleaner_shader_needed && !spm_update_needed) return; amdgpu_ring_ib_begin(ring); From 587be7a17358ef8c0106775fcedae5a7bef50735 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Sat, 8 Aug 2026 21:59:42 +0800 Subject: [PATCH 120/171] drm/radeon: fix autosuspend cleanup during teardown radeon_driver_load_kms() calls pm_runtime_use_autosuspend() for PX devices, but radeon_driver_unload_kms() does not call the matching pm_runtime_dont_use_autosuspend() during teardown. 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. 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 driver unload path. This issue was found by manual code inspection. Fixes: 10ebc0bc0934 ("drm/radeon: add runtime PM support (v2)") Signed-off-by: Guangshuo Li Signed-off-by: Alex Deucher (cherry picked from commit 0fdc1ff82ea14844c22795e9e0813c3ca03235e1) Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/radeon_kms.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index dc43fd790a9c..4a99c09f4164 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -71,6 +71,7 @@ void radeon_driver_unload_kms(struct drm_device *dev) if (radeon_is_px(dev)) { pm_runtime_get_sync(dev->dev); pm_runtime_forbid(dev->dev); + pm_runtime_dont_use_autosuspend(dev->dev); } radeon_acpi_fini(rdev); From 2a9c5154a5650c09ad44ff5e1dff74754e15a3c6 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 5 Aug 2026 20:39:18 +0800 Subject: [PATCH 121/171] drm/amdgpu: check ASPM on the dGPU host link dGPUs with an internal PCIe switch expose graphics functions below the switch downstream port. The automatic ASPM check uses the display endpoint and evaluates the internal link instead of the host link. Use the switch upstream port for the check and report the selected link. Fixes: 0ab5d711ec74 ("drm/amd: Refactor `amdgpu_aspm` to be evaluated per device") Signed-off-by: Yang Wang Reviewed-by: Hawking Zhang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit 4e0d6f2876e704fff707b18c40dbd383aea4a1c9) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 50 +++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index bff3e06a756c..16049bfb627b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1370,6 +1370,31 @@ static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev) #endif } +/* + * Some dGPUs expose their display endpoint below an internal PCIe switch. + * Use the switch upstream port to query the host-facing link. + */ +static struct pci_dev *amdgpu_device_get_aspm_pdev(struct amdgpu_device *adev) +{ + struct pci_dev *swds, *swus; + + swds = pci_upstream_bridge(adev->pdev); + if (!swds || + (swds->vendor != PCI_VENDOR_ID_ATI && + swds->vendor != PCI_VENDOR_ID_AMD) || + pci_pcie_type(swds) != PCI_EXP_TYPE_DOWNSTREAM) + return adev->pdev; + + swus = pci_upstream_bridge(swds); + if (!swus || + (swus->vendor != PCI_VENDOR_ID_ATI && + swus->vendor != PCI_VENDOR_ID_AMD) || + pci_pcie_type(swus) != PCI_EXP_TYPE_UPSTREAM) + return adev->pdev; + + return swus; +} + /** * amdgpu_device_should_use_aspm - check if the device should program ASPM * @@ -1382,6 +1407,9 @@ static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev) */ bool amdgpu_device_should_use_aspm(struct amdgpu_device *adev) { + struct pci_dev *aspm_pdev, *parent; + bool enabled; + switch (amdgpu_aspm) { case -1: break; @@ -1396,7 +1424,27 @@ bool amdgpu_device_should_use_aspm(struct amdgpu_device *adev) return false; if (amdgpu_device_aspm_support_quirk(adev)) return false; - return pcie_aspm_enabled(adev->pdev); + + /* + * pcie_aspm_enabled() checks the link between its argument and + * the immediate upstream bridge. Use SWUS for dGPUs with an + * internal switch so that this is the host-facing link. + */ + aspm_pdev = amdgpu_device_get_aspm_pdev(adev); + parent = pci_upstream_bridge(aspm_pdev); + if (!parent) { + dev_dbg(adev->dev, "ASPM: no upstream PCIe link for %s\n", + pci_name(aspm_pdev)); + return false; + } + + enabled = pcie_aspm_enabled(aspm_pdev); + /* Report the exact link used for the automatic ASPM decision. */ + dev_dbg(adev->dev, "ASPM: link %s <-> %s is %s\n", + pci_name(parent), pci_name(aspm_pdev), + enabled ? "enabled" : "disabled"); + + return enabled; } /* if we get transitioned to only one device, take VGA back */ From 8c9aebcdd9f46f7a14b98d6ab18574b7a48fbb08 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Thu, 30 Jul 2026 17:37:44 +0200 Subject: [PATCH 122/171] drm/amdgpu: Reject UVD message with dimensions above 4096 Fixes potential overflow in DPB size calculations. Signed-off-by: David Rosca Acked-by: Leo Liu Reviewed-by: Ruijing Dong Signed-off-by: Alex Deucher (cherry picked from commit 05e1387d151f71569fbe122d2c89f9db0c21dc10) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 23383ac5323f..08b199518e22 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -655,8 +655,8 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, unsigned int image_size, tmp, min_dpb_size, num_dpb_buffer; unsigned int min_ctx_size = ~0; - /* Reject invalid dimensions to prevent division by zero */ - if (width < 16 || height < 16) { + /* Reject invalid dimensions */ + if (width < 16 || height < 16 || width > 4096 || height > 4096) { dev_WARN_ONCE(adev->dev, 1, "Invalid UVD decoding dimensions (%dx%d)!\n", width, height); From 21a8084cd76223a13493237e04d45f5226d7cee6 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Thu, 30 Jul 2026 17:56:17 +0200 Subject: [PATCH 123/171] drm/amdgpu: Fix UVD dpb min size calculation for H264 This should use actual number of references from the decode message, instead of maximum derived from level. Signed-off-by: David Rosca Acked-by: Leo Liu Reviewed-by: Ruijing Dong Signed-off-by: Alex Deucher (cherry picked from commit 64b525edb7e7bdfcdc77883c5e413804e2396856) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 62 ++----------------------- 1 file changed, 4 insertions(+), 58 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 08b199518e22..c29554e75e77 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -646,11 +646,9 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, unsigned int height = msg[7]; unsigned int dpb_size = msg[9]; unsigned int pitch = msg[28]; - unsigned int level = msg[57]; unsigned int width_in_mb = width / 16; unsigned int height_in_mb = ALIGN(height / 16, 2); - unsigned int fs_in_mb = width_in_mb * height_in_mb; unsigned int image_size, tmp, min_dpb_size, num_dpb_buffer; unsigned int min_ctx_size = ~0; @@ -669,35 +667,9 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, switch (stream_type) { case 0: /* H264 */ - switch (level) { - case 30: - num_dpb_buffer = 8100 / fs_in_mb; - break; - case 31: - num_dpb_buffer = 18000 / fs_in_mb; - break; - case 32: - num_dpb_buffer = 20480 / fs_in_mb; - break; - case 41: - num_dpb_buffer = 32768 / fs_in_mb; - break; - case 42: - num_dpb_buffer = 34816 / fs_in_mb; - break; - case 50: - num_dpb_buffer = 110400 / fs_in_mb; - break; - case 51: - num_dpb_buffer = 184320 / fs_in_mb; - break; - default: - num_dpb_buffer = 184320 / fs_in_mb; - break; - } - num_dpb_buffer++; + num_dpb_buffer = ((msg[61] >> 16) & 0xff) + 1; if (num_dpb_buffer > 17) - num_dpb_buffer = 17; + return -EINVAL; /* reference picture buffer */ min_dpb_size = image_size * num_dpb_buffer; @@ -747,35 +719,9 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, break; case 7: /* H264 Perf */ - switch (level) { - case 30: - num_dpb_buffer = 8100 / fs_in_mb; - break; - case 31: - num_dpb_buffer = 18000 / fs_in_mb; - break; - case 32: - num_dpb_buffer = 20480 / fs_in_mb; - break; - case 41: - num_dpb_buffer = 32768 / fs_in_mb; - break; - case 42: - num_dpb_buffer = 34816 / fs_in_mb; - break; - case 50: - num_dpb_buffer = 110400 / fs_in_mb; - break; - case 51: - num_dpb_buffer = 184320 / fs_in_mb; - break; - default: - num_dpb_buffer = 184320 / fs_in_mb; - break; - } - num_dpb_buffer++; + num_dpb_buffer = ((msg[61] >> 16) & 0xff) + 1; if (num_dpb_buffer > 17) - num_dpb_buffer = 17; + return -EINVAL; /* reference picture buffer */ min_dpb_size = image_size * num_dpb_buffer; From b8bb9ba3f101a1b0011f785a577a4a0a38371174 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Thu, 30 Jul 2026 18:01:51 +0200 Subject: [PATCH 124/171] drm/amdgpu: Fix UVD decode image min size calculation This needs to use pitch instead of width. Also reject pitch over 4096 to avoid overflow. Signed-off-by: David Rosca Acked-by: Leo Liu Reviewed-by: Ruijing Dong Signed-off-by: Alex Deucher (cherry picked from commit b41c8cb12e202b220353332ab87dc01a11f69304) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index c29554e75e77..004a95d63b09 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -759,7 +759,7 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, return -EINVAL; } - if (width > pitch) { + if (width > pitch || pitch > 4096) { DRM_ERROR("Invalid UVD decoding target pitch!\n"); return -EINVAL; } @@ -771,7 +771,7 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, } buf_sizes[0x1] = dpb_size; - buf_sizes[0x2] = image_size; + buf_sizes[0x2] = (pitch * height) * 3 / 2; buf_sizes[0x4] = min_ctx_size; /* store image width to adjust nb memory pstate */ adev->uvd.decode_image_width = width; From 18727670b44753865b81c56a9338c0d7bd102c54 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Thu, 30 Jul 2026 18:05:52 +0200 Subject: [PATCH 125/171] drm/amdgpu: Fix UVD min buffer sizes Use correct size for message buffer = sizeof(struct ruvd_msg). Add ITSCALING_TABLE_BUFFER size. Signed-off-by: David Rosca Acked-by: Leo Liu Reviewed-by: Ruijing Dong Signed-off-by: Alex Deucher (cherry picked from commit 37519d007e4261febbcf35b3045f8344f3145497) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 004a95d63b09..7560bee89507 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -918,15 +918,16 @@ static int amdgpu_uvd_cs_pass2(struct amdgpu_uvd_cs_ctx *ctx) ctx->buf_sizes[cmd]); return -EINVAL; } + } else if (cmd == 0x204 || cmd == 0x206) { + unsigned int min_size = ctx->buf_sizes[cmd == 0x204 ? 5 : 4]; - } else if (cmd == 0x206) { - if ((end - start) < ctx->buf_sizes[4]) { + if ((end - start) < min_size) { DRM_ERROR("buffer (%d) to small (%d / %d)!\n", cmd, (unsigned int)(end - start), - ctx->buf_sizes[4]); + min_size); return -EINVAL; } - } else if ((cmd != 0x100) && (cmd != 0x204)) { + } else if ((cmd != 0x100)) { DRM_ERROR("invalid UVD command %X!\n", cmd); return -EINVAL; } @@ -1056,11 +1057,12 @@ int amdgpu_uvd_ring_parse_cs(struct amdgpu_cs_parser *parser, { struct amdgpu_uvd_cs_ctx ctx = {}; unsigned int buf_sizes[] = { - [0x00000000] = 2048, + [0x00000000] = 3556, [0x00000001] = 0xFFFFFFFF, [0x00000002] = 0xFFFFFFFF, [0x00000003] = 2048, [0x00000004] = 0xFFFFFFFF, + [0x00000005] = 992, }; int r; From d5ab4c6a64efef2d143a96df5357f59703cd703d Mon Sep 17 00:00:00 2001 From: David Rosca Date: Mon, 10 Aug 2026 11:11:35 +0200 Subject: [PATCH 126/171] drm/amdgpu: Implement insert_end for VCE 3 After a recent change VCE now hangs when VCE_CMD_END is emitted after a pipeline sync without VM flush. Implement insert_end to correctly insert only one VCE_CMD_END per job. Fixes: bc639a9eadc7 ("drm/amdgpu: always emit the job vm fence") Signed-off-by: David Rosca Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 8897ea8c761b856f02061848a7908040a1fe5e68) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/vce_v3_0.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c index c69f7d82060f..a3eae7d4b57e 100644 --- a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c @@ -875,6 +875,23 @@ static void vce_v3_0_ring_emit_ib(struct amdgpu_ring *ring, amdgpu_ring_write(ring, ib->length_dw); } +static void vce_v3_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, + u64 seq, unsigned flags) +{ + WARN_ON(flags & AMDGPU_FENCE_FLAG_64BIT); + + amdgpu_ring_write(ring, VCE_CMD_FENCE); + amdgpu_ring_write(ring, addr); + amdgpu_ring_write(ring, upper_32_bits(addr)); + amdgpu_ring_write(ring, seq); + amdgpu_ring_write(ring, VCE_CMD_TRAP); +} + +static void vce_v3_0_ring_insert_end(struct amdgpu_ring *ring) +{ + amdgpu_ring_write(ring, VCE_CMD_END); +} + static void vce_v3_0_emit_vm_flush(struct amdgpu_ring *ring, unsigned int vmid, uint64_t pd_addr) { @@ -884,7 +901,6 @@ static void vce_v3_0_emit_vm_flush(struct amdgpu_ring *ring, amdgpu_ring_write(ring, VCE_CMD_FLUSH_TLB); amdgpu_ring_write(ring, vmid); - amdgpu_ring_write(ring, VCE_CMD_END); } static void vce_v3_0_emit_pipeline_sync(struct amdgpu_ring *ring) @@ -953,17 +969,19 @@ static const struct amdgpu_ring_funcs vce_v3_0_ring_vm_funcs = { .set_wptr = vce_v3_0_ring_set_wptr, .patch_cs_in_place = amdgpu_vce_ring_parse_cs_vm, .emit_frame_size = - 6 + /* vce_v3_0_emit_vm_flush */ + 5 + /* vce_v3_0_emit_vm_flush */ 4 + /* vce_v3_0_emit_pipeline_sync */ - 6 + 6, /* amdgpu_vce_ring_emit_fence x2 vm fence */ + 5 + 5 + /* vce_v3_0_ring_emit_fence x2 vm fence */ + 1, /* vce_v3_0_ring_insert_end */ .emit_ib_size = 5, /* vce_v3_0_ring_emit_ib */ .emit_ib = vce_v3_0_ring_emit_ib, .emit_vm_flush = vce_v3_0_emit_vm_flush, .emit_pipeline_sync = vce_v3_0_emit_pipeline_sync, - .emit_fence = amdgpu_vce_ring_emit_fence, + .emit_fence = vce_v3_0_ring_emit_fence, .test_ring = amdgpu_vce_ring_test_ring, .test_ib = amdgpu_vce_ring_test_ib, .insert_nop = amdgpu_ring_insert_nop, + .insert_end = vce_v3_0_ring_insert_end, .pad_ib = amdgpu_ring_generic_pad_ib, .begin_use = amdgpu_vce_ring_begin_use, .end_use = amdgpu_vce_ring_end_use, From 2f9a5c0f018d4a1586ee892f81f1383219676415 Mon Sep 17 00:00:00 2001 From: Nathan Lucas Date: Sun, 2 Aug 2026 08:35:23 -0600 Subject: [PATCH 127/171] drm/amd/display: fix BT.2020 YCbCr limited output CSC matrix COLOR_SPACE_YCBCR2020_TYPE, which is selected for COLOR_SPACE_2020_YCBCR_LIMITED color_space, has coefficients that are incorrect for limited-range output. Its luma and chroma scaling is full-range so output is too bright and colors are incorrect. COLOR_SPACE_YCBCR2020_TYPE is closer to a full-range conversion matrix with incorrect luma offset, so correct the luma offset for full-range and rename it to COLOR_SPACE_YCBCR2020_FULL_TYPE. Add COLOR_SPACE_YCBCR2020_LIMITED_TYPE with correct scaling and range for limited-range output. Fix related functions so COLOR_SPACE_YCBCR2020_LIMITED_TYPE and COLOR_SPACE_YCBCR2020_FULL_TYPE are correctly selected based on dc_color_space. Derivation of both matrices follows ITU-T H.273: Table 4, MatrixCoefficients 9, BT.2020-NCL weights: KR = 0.2627, KB = 0.0593, KG = 1 - KR - KB = 0.6780. Equations 45-47 in matrix form: [ KR KG KB 0 ] M2020_NCL = [ -KR/(2(1-KB)) -KG/(2(1-KB)) 1/2 0 ] [ 1/2 -KG/(2(1-KR)) -KB/(2(1-KR)) 0 ] [ 0 0 0 1 ] Limited and Full transforms based on equations 30-32 and 36-38 with bit depth 10, normalized by 1023: [ 876/1023 0 0 64/1023 ] MLimited = [ 0 896/1023 0 512/1023 ] [ 0 0 896/1023 512/1023 ] [ 0 0 0 1 ] [ 1023/1023 0 0 0 ] MFull = [ 0 1023/1023 0 512/1023 ] [ 0 0 1023/1023 512/1023 ] [ 0 0 0 1 ] M2020_NCL_Limited = MLimited x M2020_NCL M2020_NCL_Full = MFull x M2020_NCL The upper three rows of M2020_NCL_* are stored in CR, Y, CB order. Each M2020_NCL_* value is stored as Round(value * 8192) in its 16-bit two's-complement representation. Fixes: 973a9c810c78 ("drm/amd/display: Fix COLOR_SPACE_YCBCR2020_TYPE matrix") Assisted-by: OpenAI-Codex:GPT-5.6-Sol Tested-by: Igor Paunovic Tested-by: Satyajit Roy Signed-off-by: Nathan Lucas Signed-off-by: Alex Deucher (cherry picked from commit 3b906e1dc7e3c9ff9f7940f6828b367a6a9ec73c) Cc: stable@vger.kernel.org --- .../drm/amd/display/dc/core/dc_hw_sequencer.c | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index 88446817a71f..7f362c3926c6 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -58,7 +58,8 @@ enum dc_color_space_type { COLOR_SPACE_RGB_LIMITED_TYPE, COLOR_SPACE_YCBCR601_TYPE, COLOR_SPACE_YCBCR709_TYPE, - COLOR_SPACE_YCBCR2020_TYPE, + COLOR_SPACE_YCBCR2020_LIMITED_TYPE, + COLOR_SPACE_YCBCR2020_FULL_TYPE, COLOR_SPACE_YCBCR601_LIMITED_TYPE, COLOR_SPACE_YCBCR709_LIMITED_TYPE, COLOR_SPACE_YCBCR709_BLACK_TYPE, @@ -110,9 +111,15 @@ static const struct out_csc_color_matrix_type output_csc_matrix[] = { { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, - { COLOR_SPACE_YCBCR2020_TYPE, + /* Corrected. Not included in the TODO above. */ + { COLOR_SPACE_YCBCR2020_LIMITED_TYPE, + { 0x0E04, 0xF31D, 0xFEDF, 0x1004, + 0x0733, 0x1294, 0x01A0, 0x0201, + 0xFC16, 0xF5E6, 0x0E04, 0x1004} }, + /* Corrected. Not included in the TODO above. */ + { COLOR_SPACE_YCBCR2020_FULL_TYPE, { 0x1000, 0xF149, 0xFEB7, 0x1004, - 0x0868, 0x15B2, 0x01E6, 0x201, + 0x0868, 0x15B2, 0x01E6, 0, 0xFB88, 0xF478, 0x1000, 0x1004} }, { COLOR_SPACE_YCBCR709_BLACK_TYPE, { 0x0000, 0x0000, 0x0000, 0x1000, @@ -179,14 +186,14 @@ static bool is_ycbcr709_type( return ret; } -static bool is_ycbcr2020_type( - enum dc_color_space color_space) +static bool is_ycbcr2020_limited_type(enum dc_color_space color_space) { - bool ret = false; + return color_space == COLOR_SPACE_2020_YCBCR_LIMITED; +} - if (color_space == COLOR_SPACE_2020_YCBCR_LIMITED || color_space == COLOR_SPACE_2020_YCBCR_FULL) - ret = true; - return ret; +static bool is_ycbcr2020_full_type(enum dc_color_space color_space) +{ + return color_space == COLOR_SPACE_2020_YCBCR_FULL; } static bool is_ycbcr709_limited_type( @@ -215,8 +222,10 @@ static enum dc_color_space_type get_color_space_type(enum dc_color_space color_s type = COLOR_SPACE_YCBCR601_LIMITED_TYPE; else if (is_ycbcr709_limited_type(color_space)) type = COLOR_SPACE_YCBCR709_LIMITED_TYPE; - else if (is_ycbcr2020_type(color_space)) - type = COLOR_SPACE_YCBCR2020_TYPE; + else if (is_ycbcr2020_limited_type(color_space)) + type = COLOR_SPACE_YCBCR2020_LIMITED_TYPE; + else if (is_ycbcr2020_full_type(color_space)) + type = COLOR_SPACE_YCBCR2020_FULL_TYPE; else if (color_space == COLOR_SPACE_YCBCR709) type = COLOR_SPACE_YCBCR709_BLACK_TYPE; else if (color_space == COLOR_SPACE_YCBCR709_BLACK) From cd22349e86faf6e15e6c622d70c0efc57d43201e Mon Sep 17 00:00:00 2001 From: Nathan Lucas Date: Sun, 2 Aug 2026 08:35:24 -0600 Subject: [PATCH 128/171] drm/amd/display: fix BT.2020 YCbCr output CSC matrices for DCE The commit cited by the Fixes tag added separate limited and full-range BT.2020 YCbCr entries to the DCE output CSC tables, but populated both entries with the same matrix copied from the common DC table. That matrix combined full-range scaling with limited-range luma offset and was incorrect for both limited and full-range output. Replace the coefficients in both entries in the DCE paths with those from the new COLOR_SPACE_YCBCR2020_LIMITED_TYPE and COLOR_SPACE_YCBCR2020_FULL_TYPE entries in the preceding commit ("drm/amd/display: fix BT.2020 YCbCr limited output CSC matrix"). Fixes: 51e6668ab4ba ("drm/amd/display: add missing CSC entries for BT.2020 for DCE IPs") Assisted-by: OpenAI-Codex:GPT-5.6-Sol Tested-by: Igor Paunovic Tested-by: Satyajit Roy Signed-off-by: Nathan Lucas Signed-off-by: Alex Deucher (cherry picked from commit 14c8726b79d19934d6eb6d35c612e3f7204af2c6) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/dce/dce_transform.c | 7 ++++--- drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c index 12c85c3afd6a..a51c9b282055 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c @@ -115,10 +115,11 @@ static const struct out_csc_color_matrix global_color_matrix[] = { { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, { COLOR_SPACE_2020_RGB_LIMITEDRANGE, { 0x1B67, 0, 0, 0x201, 0, 0x1B67, 0, 0x201, 0, 0, 0x1B67, 0x201} }, -{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, - 0x15B2, 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} }, +/* COLOR_SPACE_2020_YCBCR_* values corrected. Not included in the TODO above. */ +{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x0E04, 0xF31D, 0xFEDF, 0x1004, 0x0733, + 0x1294, 0x01A0, 0x201, 0xFC16, 0xF5E6, 0x0E04, 0x1004} }, { COLOR_SPACE_2020_YCBCR_FULL, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, 0x15B2, - 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} } + 0x01E6, 0, 0xFB88, 0xF478, 0x1000, 0x1004} } }; static bool setup_scaling_configuration( diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c index 1ed018aaa4bb..f5f8cd2d47a5 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c @@ -93,10 +93,11 @@ static const struct out_csc_color_matrix global_color_matrix[] = { { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, { COLOR_SPACE_2020_RGB_LIMITEDRANGE, { 0x1B67, 0, 0, 0x201, 0, 0x1B67, 0, 0x201, 0, 0, 0x1B67, 0x201} }, -{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, - 0x15B2, 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} }, +/* COLOR_SPACE_2020_YCBCR_* values corrected. Not included in the TODO above. */ +{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x0E04, 0xF31D, 0xFEDF, 0x1004, 0x0733, + 0x1294, 0x01A0, 0x201, 0xFC16, 0xF5E6, 0x0E04, 0x1004} }, { COLOR_SPACE_2020_YCBCR_FULL, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, 0x15B2, - 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} } + 0x01E6, 0, 0xFB88, 0xF478, 0x1000, 0x1004} } }; enum csc_color_mode { From cd688a3cb342b9f56399aa076157f1c324c15c5a Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Mon, 10 Aug 2026 12:48:19 +0800 Subject: [PATCH 129/171] drm/amdgpu: fix nbif 6.3.1 l1 low power not functional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCIe L1 low‑power settings for NBIF 6.3.1 were never applied due to unresolved register mapping, which caused the relevant code to be compiled out. As a result, the PCIe link could not enter L1/L23 power‑down states or transition to L0s. Properly configure the link control register to enable L1 and L23 power‑down, and permit L0s link transitions. Keep LTR disabled and let the PCI core enable it only after verifying end‑to‑end root complex support across switches. Fixes: 894c6d3522d1 ("drm/amdgpu: Add nbif v6_3_1 ip block support") Signed-off-by: Yang Wang Signed-off-by: Kenneth Feng Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit c2417f9fd7049d5a8d87eefd82fd6e36ba1ff7b6) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c | 42 ++++++++++-------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c index 000516b5845a..61eb0513dc97 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c +++ b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c @@ -500,7 +500,6 @@ static u32 nbif_v6_3_1_get_rom_offset(struct amdgpu_device *adev) static void nbif_v6_3_1_program_ltr(struct amdgpu_device *adev) { uint32_t def, data; - u16 devctl2; def = RREG32_SOC15(NBIO, 0, regRCC_EP_DEV0_0_EP_PCIE_TX_LTR_CNTL); data = 0x35EB; @@ -514,15 +513,8 @@ static void nbif_v6_3_1_program_ltr(struct amdgpu_device *adev) if (def != data) WREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_BIF_STRAP2, data); - pcie_capability_read_word(adev->pdev, PCI_EXP_DEVCTL2, &devctl2); - - if (adev->pdev->ltr_path == (devctl2 & PCI_EXP_DEVCTL2_LTR_EN)) - return; - - if (adev->pdev->ltr_path) - pcie_capability_set_word(adev->pdev, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_LTR_EN); - else - pcie_capability_clear_word(adev->pdev, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_LTR_EN); + pcie_capability_set_word(adev->pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_LTR_EN); } #endif @@ -530,7 +522,7 @@ static void nbif_v6_3_1_program_aspm(struct amdgpu_device *adev) { #ifdef CONFIG_PCIEASPM uint32_t def, data; - u16 devctl2, ltr; + u16 ltr; def = data = RREG32_SOC15(PCIE, 0, regPCIE_LC_CNTL); data &= ~PCIE_LC_CNTL__LC_L1_INACTIVITY_MASK; @@ -560,11 +552,8 @@ static void nbif_v6_3_1_program_aspm(struct amdgpu_device *adev) if (def != data) WREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_BIF_STRAP5, data); - pcie_capability_read_word(adev->pdev, PCI_EXP_DEVCTL2, &devctl2); - data = def = devctl2; - data &= ~PCI_EXP_DEVCTL2_LTR_EN; - if (def != data) - pcie_capability_set_word(adev->pdev, PCI_EXP_DEVCTL2, (u16)data); + pcie_capability_clear_word(adev->pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_LTR_EN); ltr = pci_find_ext_capability(adev->pdev, PCI_EXT_CAP_ID_LTR); @@ -572,15 +561,13 @@ static void nbif_v6_3_1_program_aspm(struct amdgpu_device *adev) pci_write_config_dword(adev->pdev, ltr + PCI_LTR_MAX_SNOOP_LAT, 0x10011001); } -#if 0 - /* regPSWUSP0_PCIE_LC_CNTL2 should be replace by PCIE_LC_CNTL2 or someone else ? */ - def = data = RREG32_SOC15(NBIO, 0, regPSWUSP0_PCIE_LC_CNTL2); - data |= PSWUSP0_PCIE_LC_CNTL2__LC_ALLOW_PDWN_IN_L1_MASK | - PSWUSP0_PCIE_LC_CNTL2__LC_ALLOW_PDWN_IN_L23_MASK; - data &= ~PSWUSP0_PCIE_LC_CNTL2__LC_RCV_L0_TO_RCV_L0S_DIS_MASK; + def = data = RREG32_SOC15(PCIE, 0, regPCIE_LC_CNTL2); + data |= PCIE_LC_CNTL2__LC_ALLOW_PDWN_IN_L1_MASK | + PCIE_LC_CNTL2__LC_ALLOW_PDWN_IN_L23_MASK; + data &= ~PCIE_LC_CNTL2__LC_RCV_L0_TO_RCV_L0S_DIS_MASK; if (def != data) - WREG32_SOC15(NBIO, 0, regPSWUSP0_PCIE_LC_CNTL2, data); -#endif + WREG32_SOC15(PCIE, 0, regPCIE_LC_CNTL2, data); + def = data = RREG32_SOC15(PCIE, 0, regPCIE_LC_CNTL4); data |= PCIE_LC_CNTL4__LC_L1_POWERDOWN_MASK; if (def != data) @@ -591,7 +578,12 @@ static void nbif_v6_3_1_program_aspm(struct amdgpu_device *adev) if (def != data) WREG32_SOC15(PCIE, 0, regPCIE_LC_RXRECOVER_RXSTANDBY_CNTL, data); - nbif_v6_3_1_program_ltr(adev); + /* + * Do not enable endpoint LTR unless the Root Complex and every + * upstream switch support it. + */ + if (adev->pdev->ltr_path) + nbif_v6_3_1_program_ltr(adev); def = data = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_BIF_STRAP3); data |= 0x5DE0 << RCC_STRAP0_RCC_BIF_STRAP3__STRAP_VLINK_ASPM_IDLE_TIMER__SHIFT; From 9fca434208f1f9ab977feac62df8ebb1cc7ce893 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Tue, 11 Aug 2026 11:03:10 +0200 Subject: [PATCH 130/171] drm/amdgpu: Reject UVD message with invalid number of h265 refs Same change as for h264, avoids overflow later when calculating min dpb size. Signed-off-by: David Rosca Reviewed-by: Leo Liu Signed-off-by: Alex Deucher (cherry picked from commit a4b0720e4f1601f97f59a2be9c1b4b94fa6527d5) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 7560bee89507..de3dbc95e376 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -749,6 +749,9 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, image_size = ALIGN(image_size, 256); num_dpb_buffer = (le32_to_cpu(msg[59]) & 0xff) + 2; + if (num_dpb_buffer > 17) + return -EINVAL; + min_dpb_size = image_size * num_dpb_buffer; min_ctx_size = ((width + 255) / 16) * ((height + 255) / 16) * 16 * num_dpb_buffer + 52 * 1024; From ac828b94e027d29af82325fcc55556dc8173fd85 Mon Sep 17 00:00:00 2001 From: Lijo Lazar Date: Mon, 13 Jul 2026 16:34:24 +0530 Subject: [PATCH 131/171] drm/amdgpu: Prefer default discovery offset If a valid signature is seen at the default offset, use the default size/offset for discovery. Fixes: 01bdc7e219c4 ("drm/amdgpu: New interface to get IP discovery binary v3") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5447 Signed-off-by: Lijo Lazar Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 46a0df99a0b2fa2fa61d864b04b6a5d5fe748779) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 5e83edbd313b..47e0680ed7b1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -311,6 +311,19 @@ static int amdgpu_discovery_get_tmr_info(struct amdgpu_device *adev, goto out; } } else { + if (adev->discovery.offset) { + u32 signature; + + /* If VRAM holds a valid discovery signature at the default + * discovery offset, use it as-is. + */ + amdgpu_device_vram_access(adev, adev->discovery.offset, + &signature, sizeof(signature), + false); + if (le32_to_cpu(signature) == BINARY_SIGNATURE) + goto out; + } + tmr_size = RREG32(mmDRIVER_SCRATCH_2); if (tmr_size) { /* It's preferred to transition to PSP mailbox reg interface From 94f39e4c017e66130e476268bdaa0bf61e914fa2 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 10 Jul 2026 09:09:58 +0100 Subject: [PATCH 132/171] clocksource/drivers/arm_arch_timer: Workaround bcm2712 broken EL2 virtual timer It appears that the bcm2712 SoC found in the relatively popular RPi5 has a broken EL2 virtual timer. Tthe reason why the timer isn't working is unknown (the timer is ticking, but the interrupt never fires), and the SoC vendor doesn't communicate on the reason why this isn't working, leaving users and maintainers in the dark. Paper over the issue by detecting the broken HW, falling back to the physical timer instead, and let the user know about it. Also taint the kernel as the machine is definitely not compliant with the spec, and it's unknown what else is wrong with it. Reported-by: John Reported-by: Daniel Drake Reported-by: Marek Szyprowski Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Tested-by: Gary Guo Acked-by: Florian Fainelli Cc: Daniel Lezcano Link: https://patch.msgid.link/20260710080958.491620-1-maz@kernel.org --- drivers/clocksource/arm_arch_timer.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 4adf756423de..7b4a98df6962 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -1090,6 +1090,27 @@ static int __init arch_timer_common_init(void) return arch_timer_arch_init(); } +static bool __init has_broken_el2_vtimer(void) +{ + /* + * SoCs described here have been found to be broken, though no + * explanation has been volunteered by the vendor. Let the user know + * we're papering over the vendor's lack of communication. + */ + static const char * const broken_el2_vtimer[] __initconst = { + "brcm,bcm2712", + NULL + }; + + if (of_machine_compatible_match(broken_el2_vtimer)) { + add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK); + pr_warn_once(HW_ERR "Known broken EL2 virtual timer, ignoring it\n"); + return true; + } + + return false; +} + /** * arch_timer_select_ppi() - Select suitable PPI for the current system. * @@ -1115,7 +1136,8 @@ static int __init arch_timer_common_init(void) static enum arch_timer_ppi_nr __init arch_timer_select_ppi(void) { if (is_kernel_in_hyp_mode()) { - if (arch_timer_ppi[ARCH_TIMER_HYP_VIRT_PPI]) + if (arch_timer_ppi[ARCH_TIMER_HYP_VIRT_PPI] && + !has_broken_el2_vtimer()) return ARCH_TIMER_HYP_VIRT_PPI; pr_warn_once(FW_BUG "VHE-capable CPU without EL2 virtual timer interrupt\n"); From 00268f9452d2a0d660aa9c1bb0ca07a994af6a4f Mon Sep 17 00:00:00 2001 From: Andrey Golovko Date: Tue, 11 Aug 2026 22:14:35 +0300 Subject: [PATCH 133/171] regmap: sdw-mbq: don't call an unset readable_reg callback regmap_sdw_mbq_poll_busy() decides whether to poll the Function Busy bit by calling ctx->readable_reg(), which is a straight copy of config->readable_reg. That callback is optional: regmap_readable() treats a NULL ->readable_reg as "every register is readable", and drivers rely on that. es9356 and tac5xx2-sdw both build an MBQ regmap without one. Since commit ca1b11b36d82 ("regmap: sdw-mbq: Allow defers on undeferrable controls") the poll runs on every -ENODATA, not only for Controls the driver marked deferrable, so any of those devices answering COMMAND_IGNORED takes the kernel through a NULL function pointer. Treat a missing callback the way the rest of regmap does and poll. Fixes: 5bc493bf0c37 ("regmap: sdw-mbq: Add support for SDCA deferred controls") Signed-off-by: Andrey Golovko Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260811184500.5312-1-andrey.golovko@gmail.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-sdw-mbq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap-sdw-mbq.c b/drivers/base/regmap/regmap-sdw-mbq.c index 6d7c760cbef2..b0312f57c980 100644 --- a/drivers/base/regmap/regmap-sdw-mbq.c +++ b/drivers/base/regmap/regmap-sdw-mbq.c @@ -56,7 +56,7 @@ static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg, reg = SDW_SDCA_CTL(SDW_SDCA_CTL_FUNC(reg), 0, SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0); - if (ctx->readable_reg(dev, reg)) { + if (!ctx->readable_reg || ctx->readable_reg(dev, reg)) { ret = read_poll_timeout(sdw_read_no_pm, val, val < 0 || !(val & SDCA_CTL_ENTITY_0_FUNCTION_BUSY), ctx->cfg.retry_us, ctx->cfg.timeout_us, From 5a87925539acecfe88229bad76ab81bd75a7e3f5 Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Thu, 11 Jun 2026 19:32:51 +0800 Subject: [PATCH 134/171] libceph: tolerate addrvecs with multiple entries of the same type ceph_decode_entity_addrvec() rejects any addrvec containing more than one entry that matches the requested msgr type (LEGACY or MSGR2), logging "another match of type N in addrvec" and returning -EINVAL. Some admin tooling (e.g. pveceph mon create from Proxmox VE) generates addrvecs with multiple same-type entries when public_network lists more than one CIDR: it picks one local IP per subnet and emits both a v2 and a v1 entry for each IP. Monmaps shaped this way cause: libceph: mon0 (1)10.10.10.15:6789 session established libceph: another match of type 1 in addrvec libceph: problem decoding monmap, -22 No Ceph code uses the extra entries: since Nautilus, the userspace messenger (AsyncMessenger) unconditionally picks the first address of the requested type and ignores any subsequent matches. Match that behavior: use the first matching entry and silently skip any subsequent ones. This is a compatibility fix for existing deployments and does not enable dual-stack or multi-subnet address selection. [ idryomov: tweak ceph_decode_entity_addrvec() comment ] Cc: stable@vger.kernel.org Fixes: a5cbd5fc22d5 ("libceph, ceph: get and handle cluster maps with addrvecs") Link: https://bugzilla.proxmox.com/show_bug.cgi?id=7518 Signed-off-by: Kefu Chai Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov --- net/ceph/decode.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/net/ceph/decode.c b/net/ceph/decode.c index bc109a1a4616..2f21af38cd9a 100644 --- a/net/ceph/decode.c +++ b/net/ceph/decode.c @@ -87,8 +87,9 @@ ceph_decode_entity_addr(void **p, void *end, struct ceph_entity_addr *addr) EXPORT_SYMBOL(ceph_decode_entity_addr); /* - * Return addr of desired type (MSGR2 or LEGACY) or error. - * Make sure there is only one match. + * Return addr of desired type (MSGR2 or LEGACY) or error. In case of + * multiple matches, use the first one for compatibility with userspace + * messenger. * * Assume encoding with MSG_ADDR2. */ @@ -121,14 +122,13 @@ int ceph_decode_entity_addrvec(void **p, void *end, bool msgr2, dout("%s i %d addr %s\n", __func__, i, ceph_pr_addr(&tmp_addr)); if (tmp_addr.type == my_type) { - if (found) { - pr_err("another match of type %d in addrvec\n", - le32_to_cpu(my_type)); - return -EINVAL; + if (!found) { + memcpy(addr, &tmp_addr, sizeof(*addr)); + found = true; + } else { + dout("%s skipping extra match of type %d in addrvec\n", + __func__, le32_to_cpu(my_type)); } - - memcpy(addr, &tmp_addr, sizeof(*addr)); - found = true; } } From 437b6551cfcc235eea1d735a874f9d421f555e17 Mon Sep 17 00:00:00 2001 From: Pavitra Jha Date: Tue, 2 Jun 2026 01:02:19 -0400 Subject: [PATCH 135/171] libceph: fix multiple unsafe decodes in decode_locker() decode_locker() in cls_lock_client.c contains three unsafe decode operations that allow a malicious or compromised OSD to trigger slab-out-of-bounds reads: 1. ceph_decode_copy() at the locker_id_t name field has no preceding bounds check. With p == end after ceph_start_decoding() accepts struct_len=0, this reads sizeof(ceph_entity_name) = 9 bytes past the validated buffer boundary. 2. *p += sizeof(struct ceph_timespec) after the locker_info_t header is an unchecked pointer advance. A malicious OSD can position p past end, causing all subsequent _safe checks to pass against a bogus boundary. 3. len = ceph_decode_32(p) has no preceding bounds check, and the immediately following *p += len is uncapped. A malicious OSD can send len=0xffffffff, advancing p gigabytes past end and escaping the decode window entirely. Fix all three by replacing bare operations with their safe variants: ceph_decode_copy -> ceph_decode_copy_safe *p += sizeof(...) -> ceph_decode_skip_n ceph_decode_32(p) -> ceph_decode_32_safe *p += len -> ceph_decode_skip_n A new label is added to return -EINVAL on any bounds violation. -EINVAL is appropriate here: the data received from the OSD is structurally malformed, which is an invalid argument to the decode contract regardless of whether the caller or the wire is at fault. Attacker model: a malicious or compromised OSD in a multi-tenant Ceph deployment can trigger this against any kernel client that issues the lock.get_info class method (e.g. during RBD exclusive lock acquisition) without any further privileges beyond OSD session establishment. [ idryomov: use ceph_decode_skip_string() to skip description, trim changelog ] Cc: stable@vger.kernel.org Fixes: d4ed4a530562 ("libceph: support for lock.lock_info") Signed-off-by: Pavitra Jha Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov --- net/ceph/cls_lock_client.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/ceph/cls_lock_client.c b/net/ceph/cls_lock_client.c index 377336982f7d..2c8fefc1a721 100644 --- a/net/ceph/cls_lock_client.c +++ b/net/ceph/cls_lock_client.c @@ -259,7 +259,8 @@ static int decode_locker(void **p, void *end, struct ceph_locker *locker) if (ret) return ret; - ceph_decode_copy(p, &locker->id.name, sizeof(locker->id.name)); + ceph_decode_copy_safe(p, end, &locker->id.name, + sizeof(locker->id.name), bad); s = ceph_extract_encoded_string(p, end, NULL, GFP_NOIO); if (IS_ERR(s)) return PTR_ERR(s); @@ -270,19 +271,23 @@ static int decode_locker(void **p, void *end, struct ceph_locker *locker) if (ret) return ret; - *p += sizeof(struct ceph_timespec); /* skip expiration */ + /* skip expiration */ + ceph_decode_skip_n(p, end, sizeof(struct ceph_timespec), bad); ret = ceph_decode_entity_addr(p, end, &locker->info.addr); if (ret) return ret; - len = ceph_decode_32(p); - *p += len; /* skip description */ + /* skip description */ + ceph_decode_skip_string(p, end, bad); dout("%s %s%llu cookie %s addr %s\n", __func__, ENTITY_NAME(locker->id.name), locker->id.cookie, ceph_pr_addr(&locker->info.addr)); return 0; + +bad: + return -EINVAL; } static int decode_lockers(void **p, void *end, u8 *type, char **tag, From 00ead17c7de137a692edee59f2772e6af687e8eb Mon Sep 17 00:00:00 2001 From: Pavitra Jha Date: Wed, 8 Jul 2026 01:39:41 -0400 Subject: [PATCH 136/171] libceph: fix OOB read in decode_watchers() via missing bounds check ceph_start_decoding() validates that struct_len bytes remain in the buffer after the encoding header, but accepts struct_len=0 as valid: ceph_decode_need(p, end, 0, bad) always passes. When a malicious or compromised OSD sends an obj_list_watch_response_t reply with struct_len=0, ceph_start_decoding() returns success with p == end, leaving zero bytes guaranteed for subsequent reads. The immediately following ceph_decode_32(p) in decode_watchers() has no preceding bounds check. With p == end this is a 4-byte read past the validated buffer boundary. The garbage value is then passed directly to kzalloc_objs() as the watcher count. The sibling function decode_watcher() already uses the safe variants (ceph_decode_copy_safe, ceph_decode_64_safe, ceph_decode_skip_32) after its own ceph_start_decoding() call. decode_watchers() is the only site that uses the bare variant, confirming an oversight. Fix by replacing ceph_decode_32(p) with ceph_decode_32_safe(p, end, *num_watchers, bad), consistent with the established pattern. Attacker model: a malicious or compromised OSD in a multi-tenant Ceph deployment (e.g. cloud) can trigger this against any kernel client that calls CEPH_OSD_OP_LIST_WATCHERS, without any further privileges beyond OSD session establishment. [ idryomov: trim changelog ] Cc: stable@vger.kernel.org Fixes: a4ed38d7a180 ("libceph: support for CEPH_OSD_OP_LIST_WATCHERS") Signed-off-by: Pavitra Jha Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- net/ceph/osd_client.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 2ff00070c181..28d76c2f6b3e 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -5030,7 +5030,7 @@ static int decode_watchers(void **p, void *end, if (ret) return ret; - *num_watchers = ceph_decode_32(p); + ceph_decode_32_safe(p, end, *num_watchers, bad); *watchers = kzalloc_objs(**watchers, *num_watchers, GFP_NOIO); if (!*watchers) return -ENOMEM; @@ -5044,6 +5044,9 @@ static int decode_watchers(void **p, void *end, } return 0; + +bad: + return -EINVAL; } /* From 3660b98d1204b419f6a77e9a295f148dcf38d042 Mon Sep 17 00:00:00 2001 From: Raphael Zimmer Date: Tue, 28 Jul 2026 10:43:40 +0200 Subject: [PATCH 137/171] libceph: Avoid using invalid osd indices from primary_temp A corrupted osdmap received from a Ceph monitor or OSD may contain osd indices in its pg_temp, primary_temp, pg_upmap, and pg_upmap_items parts that don't exist, i.e., that are greater than max_osd or smaller than CEPH_HOMELESS_OSD (-1). These indices are used to create the up and acting set in ceph_pg_to_up_acting_osds(), called from calc_target(). While most of these osd indices are checked, the one from primary_temp is not. Subsequently, this may lead to calc_target() returning this (potentially invalid) index as target osd for a (linger) request. Because the osd_state, osd_weight, and osd_addr arrays only contain max_osd entries (with indices 0 to max_osd -1), this leads to out-of-bounds accesses when trying to read values from these arrays. This patch fixes the issue by adding a check to get_temp_osds(), so that only valid osd indices from primary_temp are used, and it falls back to using the primary from pg_temp or the up set if it is invalid. [ idryomov: changelog ] Cc: stable@vger.kernel.org Fixes: 5e8d4d36bf23 ("libceph: add support for primary_temp mappings") Signed-off-by: Raphael Zimmer Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov --- net/ceph/osdmap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index a4b0dd8672ec..d6282f0bcff8 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -2809,9 +2809,10 @@ static void get_temp_osds(struct ceph_osdmap *osdmap, } } - /* primary_temp? */ + /* primary_temp? (shouldn't ever be a nonexistent or down OSD) */ pg = lookup_pg_mapping(&osdmap->primary_temp, pgid); - if (pg) + if (pg && !WARN_ON_ONCE(ceph_osd_is_down(osdmap, + pg->primary_temp.osd))) temp->primary = pg->primary_temp.osd; } From 2c11c4bfdb7bd2808b3b3ac228e1f2d9bcf25457 Mon Sep 17 00:00:00 2001 From: Yiming Zhu Date: Fri, 24 Jul 2026 18:49:20 +0800 Subject: [PATCH 138/171] ceph: fix MDS random selection readiness predicate CEPH_MDS_IS_READY() is parsed so that the ternary expression can return true for an MDS entry with state 0 when it is not laggy. This allows the random selector to choose a down/DNE rank. Group the ternary expression under the state check so zero-state ranks are not treated as ready. Cc: stable@vger.kernel.org Fixes: b38c9eb4757d ("ceph: add possible_max_rank and make the code more readable") Link: https://tracker.ceph.com/issues/78648 Signed-off-by: Yiming Zhu Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- fs/ceph/mdsmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index d8e46eb7e5eb..450a4dc9662e 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -15,7 +15,7 @@ #include "super.h" #define CEPH_MDS_IS_READY(i, ignore_laggy) \ - (m->m_info[i].state > 0 && ignore_laggy ? true : !m->m_info[i].laggy) + (m->m_info[i].state > 0 && (ignore_laggy ? true : !m->m_info[i].laggy)) static int __mdsmap_get_random_mds(struct ceph_mdsmap *m, bool ignore_laggy) { From 91880e4a7fac45bc407771bc57bbaf4f37e9b4c3 Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Mon, 27 Jul 2026 09:47:44 +0800 Subject: [PATCH 139/171] ceph: use the mount idmap for the owner checks in the SET_LAYOUT ioctls ceph_ioctl_set_layout() and ceph_ioctl_set_layout_policy() call inode_owner_or_capable() with &nop_mnt_idmap instead of the idmap of the mount the ioctl was issued on. CephFS supports idmapped mounts (FS_ALLOW_IDMAP), so on such a mount this compares the caller's fsuid against the unmapped on-disk owner rather than the mapped owner: the actual owner can be wrongly denied with -EACCES and an unrelated caller wrongly allowed. Both functions already have the struct file, so use file_mnt_idmap(file) instead. Cc: stable@vger.kernel.org Fixes: cee38bbf5556 ("ceph: add owner/capability checks for CEPH_IOC_SET_LAYOUT*") Signed-off-by: Zhan Xusheng Reviewed-by: Xiubo Li Reviewed-by: Alex Markuze Signed-off-by: Ilya Dryomov --- fs/ceph/ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c index de07f19b0caa..12cb50e0a166 100644 --- a/fs/ceph/ioctl.c +++ b/fs/ceph/ioctl.c @@ -72,7 +72,7 @@ static long ceph_ioctl_set_layout(struct file *file, void __user *arg) struct ceph_ioctl_layout nl; int err; - if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) + if (!inode_owner_or_capable(file_mnt_idmap(file), inode)) return -EACCES; if (copy_from_user(&l, arg, sizeof(l))) @@ -145,7 +145,7 @@ static long ceph_ioctl_set_layout_policy (struct file *file, void __user *arg) int err; struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; - if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) + if (!inode_owner_or_capable(file_mnt_idmap(file), inode)) return -EACCES; /* copy and validate */ From 9e6869be49064915edb6c8776b27c376cfdb0df5 Mon Sep 17 00:00:00 2001 From: Alexandra Winter Date: Mon, 10 Aug 2026 13:14:32 +0200 Subject: [PATCH 140/171] net/dibs: Correct freeing of dmb_clientid_arr A dibs device interrupt handler can be active after dibs_dev_del() and may still access dmb_clientid_arr. (UAF) In case of a failure in dibs_dev_add() being called by dibs_lo_dev_probe() dmb_clientid_arr is freed twice (double free). Free dmb_clientid_arr in dibs_dev_release() after last reference is gone. Note that allocating in dibs_dev_add() instead of dibs_dev_alloc() is ok for now, because no dmbs can be registered before dibs_dev_add(). Fixes: cc21191b584c ("dibs: Move data path to dibs layer") Cc: stable@vger.kernel.org Co-developed-by: Hidayath Khan Signed-off-by: Hidayath Khan Signed-off-by: Alexandra Winter Reviewed-by: Dust Li Link: https://patch.msgid.link/20260810111432.2334900-1-wintera@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/dibs/dibs_loopback.c | 1 - drivers/dibs/dibs_main.c | 14 ++++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/dibs/dibs_loopback.c b/drivers/dibs/dibs_loopback.c index fd5caf1e19a8..649e4e375be3 100644 --- a/drivers/dibs/dibs_loopback.c +++ b/drivers/dibs/dibs_loopback.c @@ -335,7 +335,6 @@ static int dibs_lo_dev_probe(void) return 0; err_reg: - kfree(dibs->dmb_clientid_arr); /* pairs with dibs_dev_alloc() */ put_device(&dibs->dev); kfree(ldev); diff --git a/drivers/dibs/dibs_main.c b/drivers/dibs/dibs_main.c index 4c26fd06973f..2b53a9d277dc 100644 --- a/drivers/dibs/dibs_main.c +++ b/drivers/dibs/dibs_main.c @@ -128,6 +128,7 @@ static void dibs_dev_release(struct device *dev) dibs = container_of(dev, struct dibs_dev, dev); + kfree(dibs->dmb_clientid_arr); kfree(dibs); } @@ -194,12 +195,13 @@ int dibs_dev_add(struct dibs_dev *dibs) ret = device_add(&dibs->dev); if (ret) - goto free_client_arr; + return ret; ret = sysfs_create_group(&dibs->dev.kobj, &dibs_dev_attr_group); if (ret) { dev_err(&dibs->dev, "sysfs_create_group failed for dibs_dev\n"); - goto err_device_del; + device_del(&dibs->dev); + return ret; } mutex_lock(&dibs_dev_list.mutex); mutex_lock(&clients_lock); @@ -214,13 +216,6 @@ int dibs_dev_add(struct dibs_dev *dibs) mutex_unlock(&dibs_dev_list.mutex); return 0; - -err_device_del: - device_del(&dibs->dev); -free_client_arr: - kfree(dibs->dmb_clientid_arr); - return ret; - } EXPORT_SYMBOL_GPL(dibs_dev_add); @@ -247,7 +242,6 @@ void dibs_dev_del(struct dibs_dev *dibs) mutex_unlock(&dibs_dev_list.mutex); device_del(&dibs->dev); - kfree(dibs->dmb_clientid_arr); } EXPORT_SYMBOL_GPL(dibs_dev_del); From beb33f8ee1ca83acddb2a5ae80f3d22ec550b4c3 Mon Sep 17 00:00:00 2001 From: Qing Ming Date: Tue, 11 Aug 2026 23:28:03 +0800 Subject: [PATCH 141/171] sctp: clear new_transport when removing a peer sctp_process_asconf_param() stores a newly added peer transport in asoc->new_transport. After all parameters in the ASCONF chunk have been processed, sctp_sf_do_asconf() uses this pointer to send a HEARTBEAT to the new transport. An authenticated ASCONF from a remote SCTP peer can add a transport and remove it again with a wildcard DEL-IP parameter in the same chunk. The wildcard deletion preserves the transport on which the ASCONF arrived, but removes the newly added transport through sctp_assoc_del_nonprimary_peers(). The removal does not clear asoc->new_transport, leaving it pointing to the removed transport. sctp_sf_do_asconf() then creates a HEARTBEAT whose chunk->transport points to the removed transport without holding a transport reference. During local address replacement, src_out_of_asoc_ok keeps this HEARTBEAT on control_chunk_list. After the transport is freed by RCU, a successful ASCONF_ACK for the replacement address releases the queued HEARTBEAT and sctp_outq_select_transport() reads the freed transport's state. The issue was found during a static audit of SCTP objects. With an authenticated peer, the reproducer triggered the same KASAN report in 2 of 2 unpatched runs on a KASAN-enabled netdev/main kernel: BUG: KASAN: slab-use-after-free in sctp_outq_select_transport Read of size 4 at addr ffff88800b9bd95c by task python3/197 Call Trace: sctp_outq_select_transport+0x549/0x8b0 [sctp] sctp_outq_flush+0x306/0x2c60 [sctp] sctp_transport_immediate_rtx+0xaf/0x260 [sctp] sctp_process_asconf_ack+0xa48/0xf70 [sctp] Allocated by task 197: sctp_transport_new+0x68/0x650 [sctp] sctp_assoc_add_peer+0x258/0x12a0 [sctp] sctp_process_asconf+0x5e9/0x1090 [sctp] Last potentially related work creation: __call_rcu_common.constprop.0+0x77/0xb70 sctp_assoc_del_nonprimary_peers+0x7c/0xd0 [sctp] sctp_process_asconf+0xd9c/0x1090 [sctp] The first invalid access was a four-byte read of transport->state at net/sctp/outqueue.c:833. The same reproducer completed the full authenticated ASCONF and local-address replacement sequence with this change without a KASAN report or oops. Clear new_transport when its peer is removed, before it can be used to create the HEARTBEAT. Fixes: 6af29ccc223b ("sctp: Bundle HEAERTBEAT into ASCONF_ACK") Cc: stable@vger.kernel.org Signed-off-by: Qing Ming Acked-by: Xin Long Link: https://patch.msgid.link/20260811152803.5629-1-a0yami@mailbox.org Signed-off-by: Jakub Kicinski --- net/sctp/associola.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 5b0ae616e1ff..c65c83638cce 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -543,6 +543,9 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc, asoc->addip_last_asconf->transport == peer) asoc->addip_last_asconf->transport = NULL; + if (asoc->new_transport == peer) + asoc->new_transport = NULL; + /* If we have something on the transmitted list, we have to * save it off. The best place is the active path. */ From 36a05d2820077bb3955acb8111e1041d39148037 Mon Sep 17 00:00:00 2001 From: Siddharth Vadapalli Date: Fri, 7 Aug 2026 16:47:37 +0530 Subject: [PATCH 142/171] net: ethernet: ti: am65-cpsw-nuss: Fix port_id extraction from SRC TAG On the packet reception path, the ID of the MAC Port on which the packet was received, is embedded in the RX DMA Descriptor's metadata. The ID is extracted using the helper function cppi5_desc_get_tags_ids() which fills in the 16-bit Source Tag into the 'port_id' variable. However, it is only the lower 8-bits of the 16-bit Source Tag that represent the MAC Port ID, while the upper 8-bits are Hardware-Reserved and carry an arbitrary value. With the existing logic, sporadic kernel crash is observed due to the subsequent driver code accessing out-of-bound memory because of an invalid port_id. Hence, fix the port_id extraction logic to use only the lower 8-bits of the Source Tag as the MAC Port ID. Fixes: 93a76530316a ("net: ethernet: ti: introduce am65x/j721e gigabit eth subsystem driver") Signed-off-by: Siddharth Vadapalli Reviewed-by: Chintan Vankar Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260807111738.2055900-1-s-vadapalli@ti.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 434a31080855..6330540979d7 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -1310,6 +1310,8 @@ static int am65_cpsw_nuss_rx_packets(struct am65_cpsw_rx_flow *flow, k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma); pkt_len = cppi5_hdesc_get_pktlen(desc_rx); cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL); + /* Port ID is contained in the lower 8-bits of the 16-bit Source Tag */ + port_id &= 0xFF; dev_dbg(dev, "%s rx port_id:%d\n", __func__, port_id); port = am65_common_get_port(common, port_id); ndev = port->ndev; From 9da976eb649c9e2f588a4499410e4d8af687925f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 15 Jun 2026 22:12:29 -0700 Subject: [PATCH 143/171] Input: sur40 - fix input device registration ordering In sur40_probe(), input_register_device() was previously called early before the V4L2 video device and vb2_queue components were fully initialized. If userspace opened the input device immediately upon registration, sur40_open() would trigger and start the sur40_poll() worker thread. This worker thread invokes sur40_process_video() and accesses the uninitialized vb2_queue structure, leading to a data race and potential system crash. Furthermore, if V4L2 or video registration failed after input_register_device() succeeded, the error path fell through to calling input_free_device() on a successfully registered device instead of input_unregister_device(), corrupting input core state. Move input_register_device() to the very end of sur40_probe(). This ensures the V4L2 and video queue structures are fully initialized before polling can start, and naturally resolves the error path bug since input_free_device() is now only called when input registration has not yet occurred. To maintain strict LIFO (Last-In, First-Out) teardown ordering, also move input_unregister_device() to the very beginning of sur40_disconnect(). This guarantees that the input polling worker thread is stopped before V4L2 video components or control handlers are unregistered. Reported-by: sashiko-bot@kernel.org Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260616051235.1549517-1-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sur40.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index fe63d53d56db..8639ec3ad703 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -725,21 +725,13 @@ static int sur40_probe(struct usb_interface *interface, goto err_free_input; } - /* register the polled input device */ - error = input_register_device(input); - if (error) { - dev_err(&interface->dev, - "Unable to register polled input device."); - goto err_free_buffer; - } - /* register the video master device */ snprintf(sur40->v4l2.name, sizeof(sur40->v4l2.name), "%s", DRIVER_LONG); error = v4l2_device_register(sur40->dev, &sur40->v4l2); if (error) { dev_err(&interface->dev, "Unable to register video master device."); - goto err_unreg_v4l2; + goto err_free_buffer; } /* initialize the lock and subdevice */ @@ -798,6 +790,14 @@ static int sur40_probe(struct usb_interface *interface, goto err_unreg_video; } + /* register the polled input device */ + error = input_register_device(input); + if (error) { + dev_err(&interface->dev, + "Unable to register polled input device."); + goto err_unreg_video; + } + /* we can register the device now, as it is ready */ usb_set_intfdata(interface, sur40); dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC); @@ -823,11 +823,12 @@ static void sur40_disconnect(struct usb_interface *interface) { struct sur40_state *sur40 = usb_get_intfdata(interface); + input_unregister_device(sur40->input); + v4l2_ctrl_handler_free(&sur40->hdl); video_unregister_device(&sur40->vdev); v4l2_device_unregister(&sur40->v4l2); - input_unregister_device(sur40->input); kfree(sur40->bulk_in_buffer); kfree(sur40); From 062dc4693e2c10d77de06f61e6f3faf37c0a8383 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 15 Jun 2026 22:12:30 -0700 Subject: [PATCH 144/171] Input: sur40 - fix V4L error path cleanup In sur40_probe(), if video_register_device() fails, the error path jumps to err_unreg_video. This incorrectly attempts to unregister a video device that was never successfully registered, and fails to free the V4L2 control handler (v4l2_ctrl_handler_free) that was initialized immediately prior. Fix this by introducing an err_free_ctrl label to properly free the V4L2 control handler and bypass video_unregister_device() when video device registration fails. Reported-by: sashiko-bot@kernel.org Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260616051235.1549517-2-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sur40.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 8639ec3ad703..e9089b0c3e2f 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -787,7 +787,7 @@ static int sur40_probe(struct usb_interface *interface, if (error) { dev_err(&interface->dev, "Unable to register video subdevice."); - goto err_unreg_video; + goto err_free_ctrl; } /* register the polled input device */ @@ -806,6 +806,8 @@ static int sur40_probe(struct usb_interface *interface, err_unreg_video: video_unregister_device(&sur40->vdev); +err_free_ctrl: + v4l2_ctrl_handler_free(&sur40->hdl); err_unreg_v4l2: v4l2_device_unregister(&sur40->v4l2); err_free_buffer: From 8c283e7b56adce00193837f3311b06662466fb21 Mon Sep 17 00:00:00 2001 From: Yuxiang Yang Date: Sun, 9 Aug 2026 12:38:06 +0800 Subject: [PATCH 145/171] sctp: fix use-after-free of cached ASCONF chunk addip_last_asconf caches the outstanding outbound ASCONF chunk. The normal ASCONF-ACK completion path releases the chunk and clears the pointer. However, sctp_asconf_queue_teardown() releases the cached chunk without clearing addip_last_asconf. During peer restart handling, sctp_sf_do_dupcook_a() queues SCTP_CMD_PURGE_ASCONF_QUEUE, which invokes sctp_asconf_queue_teardown() while the association remains alive and leaves the pointer dangling. A delayed authenticated ASCONF-ACK can then reach sctp_sf_do_asconf_ack(), which accesses the stale chunk and passes it to sctp_process_asconf_ack(), causing a use-after-free and a second release. Clearing the pointer exposes a race with T4 expiry. Peer restart handling queues the timer stop before the purge, but SCTP_CMD_TIMER_STOP uses timer_delete(), which does not wait for a callback already running on another CPU. Such a callback can reach sctp_sf_t4_timer_expire() after the purge and dereference NULL. Clear addip_last_asconf after releasing the cached chunk, and make sctp_sf_t4_timer_expire() consume a stale T4 expiry if no outstanding ASCONF remains. Fixes: a000c01e60e4 ("sctp: stop pending timers and purge queues when peer restart asoc") Cc: stable@vger.kernel.org Suggested-by: Xin Long Signed-off-by: Yuxiang Yang Acked-by: Xin Long Link: https://patch.msgid.link/20260809043806.2768302-1-yangyx22@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski --- net/sctp/associola.c | 4 +++- net/sctp/sm_statefuns.c | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/net/sctp/associola.c b/net/sctp/associola.c index c65c83638cce..5be0bed2685e 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -1716,6 +1716,8 @@ void sctp_asconf_queue_teardown(struct sctp_association *asoc) sctp_assoc_free_asconf_queue(asoc); /* Free any cached ASCONF chunk. */ - if (asoc->addip_last_asconf) + if (asoc->addip_last_asconf) { sctp_chunk_free(asoc->addip_last_asconf); + asoc->addip_last_asconf = NULL; + } } diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 708fa07d5fff..3a8e16b29660 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -6145,8 +6145,12 @@ enum sctp_disposition sctp_sf_t4_timer_expire( struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = asoc->addip_last_asconf; - struct sctp_transport *transport = chunk->transport; + struct sctp_transport *transport; + if (!chunk) + return SCTP_DISPOSITION_CONSUME; + + transport = chunk->transport; SCTP_INC_STATS(net, SCTP_MIB_T4_RTO_EXPIREDS); /* ADDIP 4.1 B1) Increment the error counters and perform path failure From 6ca662cc71df7eb4eaf1b4bcb07cd3f188ad19f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Jean?= Date: Wed, 12 Aug 2026 20:30:42 +0000 Subject: [PATCH 146/171] io_uring/rsrc: reject overflowing regvec bvec byte counts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit io_import_reg_vec() converts the estimated number of bio_vec entries into iovec-sized storage when struct bio_vec is larger than struct iovec. The conversion still multiplies nr_segs by sizeof(struct bio_vec) in size_t without checking for overflow. On 32-bit kernels, a registered buffer large enough to make io_estimate_bvec_size() return 357913942 segments wraps the byte count from 0x100000008 to 8. io_vec_realloc() then reserves only the input iovecs plus one extra slot while io_vec_fill_bvec() writes the full bio_vec array. Check both the multiplication and the rounding addition before deriving the replacement iovec count. Fixes: b4e41050b212 ("io_uring/rsrc: raise registered buffer 1GB limit") Assisted-by: Codex:gpt-5 Signed-off-by: Jérémy Jean Link: https://patch.msgid.link/20260812203042.720348-1-Jeremy.Jean@oss.cyber.gouv.fr Signed-off-by: Jens Axboe --- io_uring/rsrc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c index 8d0f2ee24e0c..98dccefd801b 100644 --- a/io_uring/rsrc.c +++ b/io_uring/rsrc.c @@ -1653,8 +1653,12 @@ int io_import_reg_vec(int ddir, struct iov_iter *iter, if (sizeof(struct bio_vec) > sizeof(struct iovec)) { size_t bvec_bytes; - bvec_bytes = nr_segs * sizeof(struct bio_vec); - nr_segs = (bvec_bytes + sizeof(*iov) - 1) / sizeof(*iov); + if (check_mul_overflow((size_t)nr_segs, sizeof(struct bio_vec), + &bvec_bytes) || + check_add_overflow(bvec_bytes, sizeof(*iov) - 1, + &bvec_bytes)) + return -EOVERFLOW; + nr_segs = bvec_bytes / sizeof(*iov); nr_segs += nr_iovs; } From 120977e2c096deea4e866e4273be9220b957c29e Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sun, 9 Aug 2026 05:44:18 -0400 Subject: [PATCH 147/171] net/sched: cls_bpf: reject dev-bound programs bound to a different device cls_bpf_prog_from_efd() obtained a SCHED_CLS program via bpf_prog_get_type_dev() but never verified that a device-bound (offloaded) program's bound netdev matches the TC netdev the classifier is being attached to. This let a program loaded with prog_ifindex for device A be attached via cls_bpf + skip_sw to device B; deleting device A then destroyed the program's offload state while it was still attached to device B, triggering a netdevsim WARN (panic with panic_on_warn=1). Mirror the XDP attach path (net/core/dev.c) and reject the attach with -EINVAL when a dev-bound program's bound device does not match the target device. Fixes: 2b3486bc2d23 ("bpf: Introduce device-bound XDP programs") Reported-by: vega@nebusec.ai Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Acked-by: Daniel Borkmann Link: https://patch.msgid.link/20260809094418.901607-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni --- net/sched/cls_bpf.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 001d8c4ebfed..6d19155becc8 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -374,7 +374,8 @@ static int cls_bpf_prog_from_ops(struct nlattr **tb, struct cls_bpf_prog *prog) } static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog, - u32 gen_flags, const struct tcf_proto *tp) + u32 gen_flags, const struct tcf_proto *tp, + struct netlink_ext_ack *extack) { struct bpf_prog *fp; char *name = NULL; @@ -388,6 +389,19 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog, if (IS_ERR(fp)) return PTR_ERR(fp); + if (bpf_prog_is_dev_bound(fp->aux)) { + struct tcf_block *block = tp->chain->block; + struct net_device *dev; + + dev = block->q ? qdisc_dev(block->q) : NULL; + if (!dev || !bpf_offload_dev_match(fp, dev)) { + NL_SET_ERR_MSG(extack, + "Program is bound to a different device"); + bpf_prog_put(fp); + return -EINVAL; + } + } + if (tb[TCA_BPF_NAME]) { name = nla_memdup(tb[TCA_BPF_NAME], GFP_KERNEL); if (!name) { @@ -492,7 +506,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, prog->gen_flags = gen_flags; ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog) : - cls_bpf_prog_from_efd(tb, prog, gen_flags, tp); + cls_bpf_prog_from_efd(tb, prog, gen_flags, tp, extack); if (ret < 0) goto errout_idr; From 9006c116dd111d457bf5d074990210f70a4ad2c8 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 11 Aug 2026 14:46:51 +0000 Subject: [PATCH 148/171] l2tp: fix tunnel and session refcount leak on seq_file release In pppol2tp_proc_open() and l2tp_dfs_seq_open(), iteration state (pd->tunnel and pd->session) is kept in seq_file private data to allow iteration across multiple read() system calls. However, if userspace closes /proc/net/pppol2tp or /sys/kernel/debug/l2tp/tunnels before reading to end-of-file (EOF), any tunnel or session reference stored in pd->tunnel / pd->session is left un-dropped when seq_file private data is freed. Fix this by dropping any remaining pd->tunnel and pd->session references in pppol2tp_proc_release() and l2tp_dfs_seq_release() when closing the file. Fixes: 0e0c3fee3a59 ("l2tp: hold reference on tunnels printed in pppol2tp proc file") Fixes: f726214d9b23 ("l2tp: hold reference on tunnels printed in l2tp/tunnels debugfs file") Reported-by: syzbot+d6fa74e3f19d6ee01e3a@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a760f32.01d0871a.3a0d52.004f.GAE@google.com/T/#u Assisted-by: Jetski:Gemini-3.1-Pro Cc: James Chapman Cc: Guillaume Nault Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260811144651.2733424-1-edumazet@google.com Signed-off-by: Paolo Abeni --- net/l2tp/l2tp_debugfs.c | 4 +++ net/l2tp/l2tp_ppp.c | 56 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/net/l2tp/l2tp_debugfs.c b/net/l2tp/l2tp_debugfs.c index b26986fda9d6..c12d5e1ee550 100644 --- a/net/l2tp/l2tp_debugfs.c +++ b/net/l2tp/l2tp_debugfs.c @@ -306,6 +306,10 @@ static int l2tp_dfs_seq_release(struct inode *inode, struct file *file) seq = file->private_data; pd = seq->private; + if (pd->session) + l2tp_session_put(pd->session); + if (pd->tunnel) + l2tp_tunnel_put(pd->tunnel); if (pd->net) put_net_track(pd->net, &pd->ns_tracker); kfree(pd); diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index f8881699e1ca..ef8fdfaf051d 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -1597,7 +1597,53 @@ static const struct seq_operations pppol2tp_seq_ops = { .stop = pppol2tp_seq_stop, .show = pppol2tp_seq_show, }; -#endif /* CONFIG_PROC_FS */ + +static int pppol2tp_proc_open(struct inode *inode, struct file *file) +{ + struct net *net = pde_data(inode); + struct pppol2tp_seq_data *pd; + + net = maybe_get_net(net); + if (!net) + return -ENXIO; + + pd = __seq_open_private(file, &pppol2tp_seq_ops, sizeof(*pd)); + if (!pd) { + put_net(net); + return -ENOMEM; + } + +#ifdef CONFIG_NET_NS + pd->p.net = net; + netns_tracker_alloc(net, &pd->p.ns_tracker, GFP_KERNEL); +#endif + return 0; +} + +static int pppol2tp_proc_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq = file->private_data; + struct pppol2tp_seq_data *pd = seq->private; + + if (pd->session) + l2tp_session_put(pd->session); + if (pd->tunnel) + l2tp_tunnel_put(pd->tunnel); + +#ifdef CONFIG_NET_NS + put_net_track(pd->p.net, &pd->p.ns_tracker); +#else + put_net(&init_net); +#endif + return seq_release_private(inode, file); +} + +static const struct proc_ops pppol2tp_proc_ops = { + .proc_open = pppol2tp_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = pppol2tp_proc_release, +}; /***************************************************************************** * Network namespace @@ -1608,8 +1654,8 @@ static __net_init int pppol2tp_init_net(struct net *net) struct proc_dir_entry *pde; int err = 0; - pde = proc_create_net("pppol2tp", 0444, net->proc_net, - &pppol2tp_seq_ops, sizeof(struct pppol2tp_seq_data)); + pde = proc_create_data("pppol2tp", 0444, net->proc_net, + &pppol2tp_proc_ops, net); if (!pde) { err = -ENOMEM; goto out; @@ -1624,9 +1670,13 @@ static __net_exit void pppol2tp_exit_net(struct net *net) remove_proc_entry("pppol2tp", net->proc_net); } +#endif /* CONFIG_PROC_FS */ + static struct pernet_operations pppol2tp_net_ops = { +#ifdef CONFIG_PROC_FS .init = pppol2tp_init_net, .exit = pppol2tp_exit_net, +#endif }; /***************************************************************************** From 42d217add8d80d6e7d9f58f80d11ea9b07ea113e Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Fri, 7 Aug 2026 14:25:26 +0000 Subject: [PATCH 149/171] firewire: ohci: fix NULL pointer dereference in ar_context_release During the error handling path of the driver's probe function, a NULL pointer dereference can occur in ar_context_release(). When pci_probe() fails early (e.g., if pcim_enable_device() or MMIO mapping fails), the devres cleanup mechanism invokes release_ohci(). This function unconditionally calls ar_context_release() to clean up the asynchronous receive contexts. However, if ar_context_init() was not yet called, ctx->ohci remains NULL (as the fw_ohci structure is zero-initialized by devres_alloc()). ar_context_release() immediately dereferences ctx->ohci to get the dev pointer before checking if the context was actually initialized, leading to a crash: Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] RIP: 0010:ar_context_release+0x3f/0x380 drivers/firewire/ohci.c:543 Call Trace: release_ohci+0x3f/0x60 drivers/firewire/ohci.c:3567 release_nodes drivers/base/devres.c:546 [inline] devres_release_all+0x1a8/0x260 drivers/base/devres.c:576 device_unbind_cleanup drivers/base/dd.c:597 [inline] really_probe+0x451/0xae0 drivers/base/dd.c:772 To fix this, move the assignment of the dev pointer after the !ctx->buffer check. If ctx->buffer is NULL, it indicates that the context was never successfully initialized and there is nothing to release, safely avoiding the dereference of the uninitialized ctx->ohci pointer. Fixes: 5716e58aecdd ("firewire: ohci: release buffer for AR req/resp contexts when managed resource is released") Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot Reported-by: syzbot+d30aad27833a559defab@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=d30aad27833a559defab Link: https://syzkaller.appspot.com/ai_job?id=10a18617-7893-42dd-bf1c-cd49e19e95d9 Signed-off-by: Aleksandr Nogikh Link: https://lore.kernel.org/r/90c5db71-dd1f-4d46-b9d3-2f1046cbd5ea@mail.kernel.org Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 8153d62c58f0..e947227e01ac 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -540,11 +540,13 @@ static void ar_context_link_page(struct ar_context *ctx, unsigned int index) static void ar_context_release(struct ar_context *ctx) { - struct device *dev = ctx->ohci->card.device; + struct device *dev; if (!ctx->buffer) return; + dev = ctx->ohci->card.device; + for (int i = 0; i < AR_BUFFERS; ++i) { dma_addr_t dma_addr = ctx->dma_addrs[i]; if (dma_addr) From fc648757908304aedbad74f74bf58192aec383db Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 30 Jun 2026 15:55:20 +0200 Subject: [PATCH 150/171] drm/xe: Fix DPT allocation paths. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the fallback for VRAM to system memory, I tested it and that doesn't work at all, only a black screen with pipe fault errors were observed. On systems with media GT, extra latency is added when accessing stolen memory when the GT is in MC6. Since we additionally aren't counting how much memory is used for stolen and we could in theory fill up the entire stolen area with DPT's, avoid using stolen and only use the default memory region. Using stolen may also result in random system hangs under load. Link: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7513 Fixes: 775d0adc01a5 ("drm/xe/fbdev: Limit the usage of stolen for LNL+") Cc: # v6.12+ Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260630135523.1775379-2-dev@lankhorst.se Signed-off-by: Maarten Lankhorst Acked-by: Matthew Brost #teams (cherry picked from commit a196406a3831291598fe8e73245914f7acffdfe0) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/display/xe_fb_pin.c | 33 +++++++------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_fb_pin.c b/drivers/gpu/drm/xe/display/xe_fb_pin.c index 5f4a0cd8deca..73469ea5f333 100644 --- a/drivers/gpu/drm/xe/display/xe_fb_pin.c +++ b/drivers/gpu/drm/xe/display/xe_fb_pin.c @@ -164,31 +164,14 @@ static int __xe_pin_fb_vma_dpt(struct drm_gem_object *obj, dpt_size = ALIGN(intel_rotation_info_size(&view->rotated) * 8, XE_PAGE_SIZE); - if (IS_DGFX(xe)) - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_VRAM0 | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE, - pin_params->alignment, false); - else - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_STOLEN | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE, - pin_params->alignment, false); - if (IS_ERR(dpt)) - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_SYSTEM | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE | - XE_BO_FLAG_FORCE_WC, - pin_params->alignment, false); + dpt = xe_bo_create_pin_map_at_novm(xe, tile0, + dpt_size, ~0ull, + ttm_bo_type_kernel, + XE_BO_FLAG_VRAM_IF_DGFX(tile0) | + XE_BO_FLAG_GGTT | + XE_BO_FLAG_PAGETABLE | + XE_BO_FLAG_FORCE_WC, + pin_params->alignment, false); if (IS_ERR(dpt)) return PTR_ERR(dpt); From 99b01815957bd7d848420cb697f79ed11f7f215c Mon Sep 17 00:00:00 2001 From: Jia Yao Date: Tue, 4 Aug 2026 16:50:55 +0000 Subject: [PATCH 151/171] drm/xe/guc_ads: allocate UM queues in a separate BO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the UM queues into a dedicated BO (ads->um_queue_bo) and avoid CPU memset operations on it, which eliminates the CPU as a potential cacheline-polluting agent and helps maintain consistency between GAM writes and GuC reads. We also need to ensure the base_dpa for the queue is contiguous on hw where this is used instead of a GGTT address. Another good reason to split this out to a separate BO. Fixes: 9c57bc08652a ("drm/xe/lnl: Drop force_probe requirement") Cc: Gwan-gyeong Mun Cc: Matthew Auld Cc: # v6.12+ Signed-off-by: Jia Yao Reviewed-by: Matthew Auld Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260804165057.129529-2-jia.yao@intel.com (cherry picked from commit 6af05de0dc19bbf3aaeec2020fe48b37c834b811) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_bo.h | 14 +++++ drivers/gpu/drm/xe/xe_guc.c | 6 ++ drivers/gpu/drm/xe/xe_guc_ads.c | 90 +++++++++++++++++---------- drivers/gpu/drm/xe/xe_guc_ads_types.h | 5 ++ 4 files changed, 83 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h index 7ae1d9ac0574..57039cf42ea7 100644 --- a/drivers/gpu/drm/xe/xe_bo.h +++ b/drivers/gpu/drm/xe/xe_bo.h @@ -6,6 +6,7 @@ #ifndef _XE_BO_H_ #define _XE_BO_H_ +#include #include #include "xe_bo_types.h" @@ -548,6 +549,19 @@ void xe_bo_dev_fini(struct xe_bo_dev *bo_device); struct sg_table *xe_bo_sg(struct xe_bo *bo); +/** + * xe_bo_sg_is_contiguous() - Check if a BO's DMA address space is contiguous. + * @bo: the BO to check (must have a valid sg table, i.e. !xe_bo_is_vram()) + * @len: required contiguous length in bytes + * + * Returns true if the first @len bytes of the BO are mapped to a contiguous + * DMA address range. + */ +static inline bool xe_bo_sg_is_contiguous(struct xe_bo *bo, size_t len) +{ + return drm_prime_get_contiguous_size(xe_bo_sg(bo)) >= len; +} + /* * xe_sg_segment_size() - Provides upper limit for sg segment size. * @dev: device pointer diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c index 4023700ff2a9..21cf3edef922 100644 --- a/drivers/gpu/drm/xe/xe_guc.c +++ b/drivers/gpu/drm/xe/xe_guc.c @@ -714,6 +714,12 @@ static int xe_guc_realloc_post_hwconfig(struct xe_guc *guc) if (ret) return ret; + if (guc->ads.um_queue_bo) { + ret = xe_managed_bo_reinit_in_vram(xe, tile, &guc->ads.um_queue_bo); + if (ret) + return ret; + } + return 0; } diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index c98454545a85..a411d6440ba6 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -63,10 +63,14 @@ ads_to_map(struct xe_guc_ads *ads) /* * The Additional Data Struct (ADS) has pointers for different buffers used by - * the GuC. One single gem object contains the ADS struct itself (guc_ads) and - * all the extra buffers indirectly linked via the ADS struct's entries. + * the GuC. One gem object (ads->bo) contains the ADS struct itself (guc_ads) + * and most of the extra buffers linked via the ADS struct's entries. The UM + * fault queues (PAGE_FAULT, PAGE_FAULT_RESPONSE, ACCESS_COUNTER rings) are + * kept in a separate BO (ads->um_queue_bo) so that the full memset of ads->bo + * performed on every GT reset does not discard fault descriptors already + * written into the rings by the GPU. * - * Layout of the ADS blob allocated for the GuC: + * Layout of the ADS blob (ads->bo): * * +---------------------------------------+ <== base * | guc_ads | @@ -98,10 +102,6 @@ ads_to_map(struct xe_guc_ads *ads) * +---------------------------------------+ * | padding | * +---------------------------------------+ <== 4K aligned - * | UM queues | - * +---------------------------------------+ - * | padding | - * +---------------------------------------+ <== 4K aligned * | private data | * +---------------------------------------+ * | padding | @@ -155,16 +155,6 @@ static size_t guc_ads_capture_size(struct xe_guc_ads *ads) return PAGE_ALIGN(ads->capture_size); } -static size_t guc_ads_um_queues_size(struct xe_guc_ads *ads) -{ - struct xe_device *xe = ads_to_xe(ads); - - if (!xe->info.has_usm) - return 0; - - return GUC_UM_QUEUE_SIZE * GUC_UM_HW_QUEUE_MAX; -} - static size_t guc_ads_private_data_size(struct xe_guc_ads *ads) { return PAGE_ALIGN(ads_to_guc(ads)->fw.private_data_size); @@ -205,22 +195,12 @@ static size_t guc_ads_capture_offset(struct xe_guc_ads *ads) return PAGE_ALIGN(offset); } -static size_t guc_ads_um_queues_offset(struct xe_guc_ads *ads) -{ - u32 offset; - - offset = guc_ads_capture_offset(ads) + - guc_ads_capture_size(ads); - - return PAGE_ALIGN(offset); -} - static size_t guc_ads_private_data_offset(struct xe_guc_ads *ads) { size_t offset; - offset = guc_ads_um_queues_offset(ads) + - guc_ads_um_queues_size(ads); + offset = guc_ads_capture_offset(ads) + + guc_ads_capture_size(ads); return PAGE_ALIGN(offset); } @@ -409,6 +389,46 @@ int xe_guc_ads_init(struct xe_guc_ads *ads) ads->bo = bo; + if (xe->info.has_usm) { + /* + * Allocate a separate BO for the HW fault ring (UM queues). + * + * Round the size up to the next power of two so that + * (system memory, no IOMMU) the TTM pool issues a single + * alloc_pages(order=N) call, maximising the chance of getting + * a physically contiguous block. GuC requires contiguous DPA. + */ + size_t um_size = roundup_pow_of_two(GUC_UM_QUEUE_SIZE * + GUC_UM_HW_QUEUE_MAX); + + u32 um_flags = XE_BO_FLAG_SYSTEM | + XE_BO_FLAG_GGTT | + XE_BO_FLAG_GGTT_INVALIDATE | + XE_BO_FLAG_PINNED_NORESTORE; + + bo = xe_managed_bo_create_pin_map(xe, tile, um_size, um_flags); + if (IS_ERR(bo)) + return PTR_ERR(bo); + + /* + * On pre-Xe3p platforms, GAM (not GuC) accesses the UM queue + * ring via base_dpa, which must be a contiguous DMA address + * range. Verify that the allocated pages are contiguous in + * DMA address space. + */ + if (!xe_bo_is_vram(bo) && + !xe_guc_using_main_gamctrl_queues(ads_to_guc(ads)) && + unlikely(!xe_bo_sg_is_contiguous(bo, + GUC_UM_QUEUE_SIZE * + GUC_UM_HW_QUEUE_MAX))) { + drm_err(&xe->drm, + "UM fault queue memory is not contiguous in DMA address space; GAM requires contiguous DPA\n"); + return -ENOMEM; + } + + ads->um_queue_bo = bo; + } + return 0; } ALLOW_ERROR_INJECTION(xe_guc_ads_init, ERRNO); /* See xe_pci_probe() */ @@ -820,7 +840,7 @@ static void guc_mmio_reg_state_init(struct xe_guc_ads *ads) static void guc_um_init_params(struct xe_guc_ads *ads) { - u32 um_queue_offset = guc_ads_um_queues_offset(ads); + struct xe_bo *um_bo = ads->um_queue_bo; struct xe_guc *guc = ads_to_guc(ads); struct xe_device *xe = ads_to_xe(ads); u64 base_dpa; @@ -830,8 +850,14 @@ static void guc_um_init_params(struct xe_guc_ads *ads) with_dpa = !xe_guc_using_main_gamctrl_queues(guc); - base_ggtt = xe_bo_ggtt_addr(ads->bo) + um_queue_offset; - base_dpa = xe_bo_main_addr(ads->bo, PAGE_SIZE) + um_queue_offset; + if (um_bo) { + /* All USM platforms: UM queues in dedicated um_queue_bo */ + base_ggtt = xe_bo_ggtt_addr(um_bo); + base_dpa = xe_bo_main_addr(um_bo, PAGE_SIZE); + } else { + /* Platform does not support USM: no UM queues, nothing to do */ + return; + } for (i = 0; i < GUC_UM_HW_QUEUE_MAX; ++i) { /* diff --git a/drivers/gpu/drm/xe/xe_guc_ads_types.h b/drivers/gpu/drm/xe/xe_guc_ads_types.h index 48a8e092023f..845c1fbd93a4 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads_types.h +++ b/drivers/gpu/drm/xe/xe_guc_ads_types.h @@ -16,6 +16,11 @@ struct xe_bo; struct xe_guc_ads { /** @bo: Xe BO for GuC ads blob */ struct xe_bo *bo; + /** + * @um_queue_bo: Dedicated BO for the HW fault ring (UM queues). + * NULL if the platform does not support USM. + */ + struct xe_bo *um_queue_bo; /** @golden_lrc_size: golden LRC size */ size_t golden_lrc_size; /** @regset_size: size of register set passed to GuC for save/restore */ From f342810a141f8a7e8b3786a6e4b6c0695a078a74 Mon Sep 17 00:00:00 2001 From: Jia Yao Date: Tue, 4 Aug 2026 16:50:56 +0000 Subject: [PATCH 152/171] drm/xe/guc_ads: allocate UM queues in VRAM on dGFX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On iGPU, the UM queue BO is allocated in system memory. On dGFX, the BO was previously created in system memory and later reallocated in xe_guc_realloc_post_hwconfig(). Allocate the UM queue BO directly in VRAM on dGFX, where it is ultimately required. Fixes: 9c57bc08652a ("drm/xe/lnl: Drop force_probe requirement") Cc: Gwan-gyeong Mun Cc: Matthew Auld Cc: # v6.12+ Signed-off-by: Jia Yao Reviewed-by: Matthew Auld Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260804165057.129529-3-jia.yao@intel.com (cherry picked from commit ace076ef0a854ab5940bacc539bf66afd61d118c) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_guc.c | 6 ------ drivers/gpu/drm/xe/xe_guc_ads.c | 8 +++++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c index 21cf3edef922..4023700ff2a9 100644 --- a/drivers/gpu/drm/xe/xe_guc.c +++ b/drivers/gpu/drm/xe/xe_guc.c @@ -714,12 +714,6 @@ static int xe_guc_realloc_post_hwconfig(struct xe_guc *guc) if (ret) return ret; - if (guc->ads.um_queue_bo) { - ret = xe_managed_bo_reinit_in_vram(xe, tile, &guc->ads.um_queue_bo); - if (ret) - return ret; - } - return 0; } diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index a411d6440ba6..b6d07f2e73bf 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -393,15 +393,17 @@ int xe_guc_ads_init(struct xe_guc_ads *ads) /* * Allocate a separate BO for the HW fault ring (UM queues). * - * Round the size up to the next power of two so that + * Round the size up to the next power of two so that on iGPU * (system memory, no IOMMU) the TTM pool issues a single * alloc_pages(order=N) call, maximising the chance of getting * a physically contiguous block. GuC requires contiguous DPA. */ - size_t um_size = roundup_pow_of_two(GUC_UM_QUEUE_SIZE * + size_t um_size = IS_DGFX(xe) ? + GUC_UM_QUEUE_SIZE * GUC_UM_HW_QUEUE_MAX : + roundup_pow_of_two(GUC_UM_QUEUE_SIZE * GUC_UM_HW_QUEUE_MAX); - u32 um_flags = XE_BO_FLAG_SYSTEM | + u32 um_flags = XE_BO_FLAG_VRAM_IF_DGFX(tile) | XE_BO_FLAG_GGTT | XE_BO_FLAG_GGTT_INVALIDATE | XE_BO_FLAG_PINNED_NORESTORE; From 8d5134ae4177fa4f5a9bc8e71e6656cfc2852882 Mon Sep 17 00:00:00 2001 From: Jia Yao Date: Tue, 4 Aug 2026 16:50:57 +0000 Subject: [PATCH 153/171] drm/xe/guc_ads: use uncached mapping for UM queue BO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Pre-Xe3p platform, the GAM write the UM queue through DPA using UC. if GuC reads the queue via GGTT (WB), stale data may be observed when the cacheline has been polluted by another agent. To match the GAM's UC writes, configure the GuC mapping as UC as well. Fixes: 9c57bc08652a ("drm/xe/lnl: Drop force_probe requirement") Cc: Gwan-gyeong Mun Cc: Matthew Auld Cc: # v6.12+ Signed-off-by: Jia Yao Reviewed by: Matthew Auld Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260804165057.129529-4-jia.yao@intel.com (cherry picked from commit 9daa302a82590eeee7bdc68023ddad302df4b88c) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_guc_ads.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index b6d07f2e73bf..886bafd31f45 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -406,7 +406,8 @@ int xe_guc_ads_init(struct xe_guc_ads *ads) u32 um_flags = XE_BO_FLAG_VRAM_IF_DGFX(tile) | XE_BO_FLAG_GGTT | XE_BO_FLAG_GGTT_INVALIDATE | - XE_BO_FLAG_PINNED_NORESTORE; + XE_BO_FLAG_PINNED_NORESTORE | + XE_BO_FLAG_NEEDS_UC; bo = xe_managed_bo_create_pin_map(xe, tile, um_size, um_flags); if (IS_ERR(bo)) From 9f83c94469ff0fa37274b873ba24922e02531fa7 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 7 Aug 2026 10:17:16 -0700 Subject: [PATCH 154/171] drm/xe: Order ring writes before ring tail updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ring buffer and the LRC context image are both mapped WC, so the ring tail update can become visible to the device before the ring contents it is meant to publish. The GuC CT send does contain an xe_device_wmb(), so sending the H2G would flush the ring contents. The problem is that it comes too late: xe_lrc_set_ring_tail() publishes the tail before the H2G is sent, and the device samples the tail from the context image independently of it, either at context switch-in or while the context is already resident. A submitter which is interrupted between updating the tail and sending its H2G therefore leaves the device free to observe the new tail while the ring contents behind it are not yet visible: 1. Thread A emits a job into the ring, sets the tail to T_A and sends the H2G, which flushes A's ring contents. The GuC starts scheduling the context in, but it is not executing yet. 2. Thread B emits a job into ring[T_A..T_B]. Those writes are not yet visible to the device. 3. Thread B updates the ring tail to T_B. That write targets a different page and becomes visible first. 4. Thread B is interrupted before it sends its H2G, so the flush which would have published ring[T_A..T_B] has not happened yet. 5. The context is switched in and samples the ring tail from the context image, picking up T_B rather than T_A. 6. The GPU executes A's job, advances HEAD to T_A, and continues on to ring[T_A..T_B], which still holds the previous wrap's contents, so the CS parses stale commands. The result is command stream corruption, which typically manifests as a hang or a spurious pagefault rather than anything that points back at the submission path. Kernel jobs are by far the most likely to hit this. Kernel queues such as the migration queue are shared and can be driven by many threads concurrently, producing back-to-back submissions on an LRC which is already executing. User queues are typically tied to a single submitting thread, so the same interleaving is much harder to produce. Add an xe_device_wmb() at the end of xe_lrc_write_ring() so that it covers every ring tail publication site, and so the invariant is local: once xe_lrc_write_ring() returns, the ring contents are visible to the device. Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8651 Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7810 Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Assisted-by: GitHub_Copilot:claude-opus-5 Reviewed-by: Stuart Summers Link: https://patch.msgid.link/20260807171716.140475-1-matthew.brost@intel.com (cherry picked from commit 136360290f314890428a3fbf31aaa8e4f1d43567) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_lrc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index a4292a11391d..78969d3e5904 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -1849,6 +1849,13 @@ void xe_lrc_write_ring(struct xe_lrc *lrc, const void *data, size_t size) __xe_lrc_write_ring(lrc, ring, &noop, sizeof(noop)); } + + /* + * The ring and the LRC context image are both WC, so the ring tail + * update which publishes these writes can become visible to the device + * first. Ensure the ring contents are visible before returning. + */ + xe_device_wmb(xe); } u64 xe_lrc_descriptor(struct xe_lrc *lrc) From 51afaf53e01e01bda489fc6ffacf07a706e72783 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Mon, 20 Jul 2026 15:27:58 -0700 Subject: [PATCH 155/171] drm/xe/pxp: add termination on resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suspend/resume causes the PXP keys to become invalid, but doesn't actually kill the session. The driver also doesn't explicitly kill and re-start the session until a new PXP request comes in, which means that the "zombie" session can potentially stick around if there are no new requests from userspace. While this is not an issue for PXP, HDCP has a new behavior starting on PTL where a communication is sent to GSC if a session is active at suspend time (even if it doesn't have a valid key), which can lead to delays in the suspend flow if we suspend while the zombie session is still active. To avoid this, we can trigger a termination on resume and kill the zombie session immediately, instead of delaying the termination to the next PXP request. Due to restrictions in the rpm suspend/resume flow, we can't call the termination flow from within the resume call itself, so the pxp irq worker is expanded to cover this scenario. The existing logic in the worker doesn't work as-is for the new flow, because the pm_get_if_active will fail if the worker runs before the pci_resume call has completed (which is possible, since we queue it from within that call) or after we're started to suspend again. Given that we always want to run the worker after a resume (differently from the irq case, where we want to skip if we're suspended), we can solve this by just taking the PM reference before queueing the worker. As part of this rework, the pxp->events variable has been moved to atomic, to avoid having to take xe->irq.lock from non-irq related paths. Fixes: b1dcec9bd8a1 ("drm/xe/ptl: Enable PXP for PTL") Signed-off-by: Daniele Ceraolo Spurio Cc: Rodrigo Vivi Cc: Julia Filipchuk Cc: Alan Previn Reviewed-by: Alan Previn Link: https://patch.msgid.link/20260720222757.3876338-2-daniele.ceraolospurio@intel.com (cherry picked from commit 757bda2b8b93fa36ad9b2c7993081d5f9d0d6e3b) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_pxp.c | 162 ++++++++++++++++++++++-------- drivers/gpu/drm/xe/xe_pxp_types.h | 27 +++-- 2 files changed, 139 insertions(+), 50 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index 968b7e70b3f9..d17ab6e59df5 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -8,6 +8,8 @@ #include #include +#include + #include "xe_bo.h" #include "xe_bo_types.h" #include "xe_device_types.h" @@ -164,16 +166,9 @@ static void mark_termination_in_progress(struct xe_pxp *pxp) pxp->status = XE_PXP_TERMINATION_IN_PROGRESS; } -static void pxp_terminate(struct xe_pxp *pxp) +static bool pxp_prep_for_termination(struct xe_pxp *pxp) { - int ret = 0; - struct xe_device *xe = pxp->xe; - - if (!wait_for_completion_timeout(&pxp->activation, - msecs_to_jiffies(PXP_ACTIVATION_TIMEOUT_MS))) - drm_err(&xe->drm, "failed to wait for PXP start before termination\n"); - - mutex_lock(&pxp->mutex); + lockdep_assert_held(&pxp->mutex); if (pxp->status == XE_PXP_ACTIVE) pxp->key_instance++; @@ -182,10 +177,8 @@ static void pxp_terminate(struct xe_pxp *pxp) * we'll mark the status as needing termination on resume, so no need to * emit a termination now. */ - if (pxp->status == XE_PXP_SUSPENDED) { - mutex_unlock(&pxp->mutex); - return; - } + if (pxp->status == XE_PXP_SUSPENDED) + return false; /* * If we have a termination already in progress, we need to wait for @@ -195,15 +188,44 @@ static void pxp_terminate(struct xe_pxp *pxp) */ if (pxp->status == XE_PXP_TERMINATION_IN_PROGRESS) { pxp->status = XE_PXP_NEEDS_ADDITIONAL_TERMINATION; - mutex_unlock(&pxp->mutex); - return; + return false; } mark_termination_in_progress(pxp); - mutex_unlock(&pxp->mutex); + return true; +} - pxp_invalidate_queues(pxp); +static void pxp_terminate(struct xe_pxp *pxp, bool hw_only) +{ + struct xe_device *xe = pxp->xe; + int ret = 0; + + if (!wait_for_completion_timeout(&pxp->activation, + msecs_to_jiffies(PXP_ACTIVATION_TIMEOUT_MS))) + drm_err(&xe->drm, "failed to wait for PXP start before termination\n"); + + if (!hw_only) { + bool prep_ok; + + mutex_lock(&pxp->mutex); + + prep_ok = pxp_prep_for_termination(pxp); + + mutex_unlock(&pxp->mutex); + + if (!prep_ok) + return; + + pxp_invalidate_queues(pxp); + } else { + /* + * The caller of the HW-only termination should have already + * called pxp_prep_for_termination and marked the termination as + * in progress. + */ + xe_assert(xe, !completion_done(&pxp->termination)); + } ret = pxp_terminate_hw(pxp); if (ret) { @@ -249,33 +271,46 @@ static void pxp_terminate_complete(struct xe_pxp *pxp) mutex_unlock(&pxp->mutex); } -static void pxp_irq_work(struct work_struct *work) +static void pxp_events_work(struct work_struct *work) { - struct xe_pxp *pxp = container_of(work, typeof(*pxp), irq.work); + struct xe_pxp *pxp = container_of(work, typeof(*pxp), events.work); struct xe_device *xe = pxp->xe; + bool hw_only = false; u32 events = 0; - spin_lock_irq(&xe->irq.lock); - events = pxp->irq.events; - pxp->irq.events = 0; - spin_unlock_irq(&xe->irq.lock); + events = atomic_xchg(&pxp->events.pending, 0); if (!events) return; /* - * If we're processing a termination irq while suspending then don't - * bother, we're going to re-init everything on resume anyway. + * If the termination request comes from an irq while we're suspending, + * then we can defer it to the resume path instead of waking the device + * up. + * In the case of the termination on resume the pm reference is taken + * in xe_pxp_pm_resume() and released here. + * Note that we do not expect both events to be set at the same time, + * but if it does happen due to a spurious interrupt we want to behave + * as if the only request we got was the one from the resume path; this + * is because the termination prep has already been done in + * xe_pxp_pm_resume() and it is impossible for any PXP operations to + * occur between the prep and the termination completion, so there is no + * need for a new SW prep. */ - if ((events & PXP_TERMINATION_REQUEST) && !xe_pm_runtime_get_if_active(xe)) + if (events & PXP_TERMINATION_REQUEST_ON_RESUME) { + events &= ~PXP_TERMINATION_REQUEST_IRQ; + hw_only = true; + } + + if ((events & PXP_TERMINATION_REQUEST_IRQ) && !xe_pm_runtime_get_if_active(xe)) return; if (events & PXP_TERMINATION_REQUEST) { - events &= ~PXP_TERMINATION_COMPLETE; - pxp_terminate(pxp); + events &= ~PXP_TERMINATION_COMPLETE_IRQ; + pxp_terminate(pxp, hw_only); } - if (events & PXP_TERMINATION_COMPLETE) + if (events & PXP_TERMINATION_COMPLETE_IRQ) pxp_terminate_complete(pxp); if (events & PXP_TERMINATION_REQUEST) @@ -296,20 +331,18 @@ void xe_pxp_irq_handler(struct xe_device *xe, u16 iir) return; } - lockdep_assert_held(&xe->irq.lock); - if (unlikely(!iir)) return; if (iir & (KCR_PXP_STATE_TERMINATED_INTERRUPT | KCR_APP_TERMINATED_PER_FW_REQ_INTERRUPT)) - pxp->irq.events |= PXP_TERMINATION_REQUEST; + atomic_or(PXP_TERMINATION_REQUEST_IRQ, &pxp->events.pending); if (iir & KCR_PXP_STATE_RESET_COMPLETE_INTERRUPT) - pxp->irq.events |= PXP_TERMINATION_COMPLETE; + atomic_or(PXP_TERMINATION_COMPLETE_IRQ, &pxp->events.pending); - if (pxp->irq.events) - queue_work(pxp->irq.wq, &pxp->irq.work); + if (atomic_read(&pxp->events.pending)) + queue_work(pxp->events.wq, &pxp->events.work); } static int kcr_pxp_set_status(const struct xe_pxp *pxp, bool enable) @@ -340,7 +373,7 @@ static void pxp_fini(void *arg) { struct xe_pxp *pxp = arg; - destroy_workqueue(pxp->irq.wq); + destroy_workqueue(pxp->events.wq); xe_pxp_destroy_execution_resources(pxp); /* no need to explicitly disable KCR since we're going to do an FLR */ @@ -402,7 +435,7 @@ int xe_pxp_init(struct xe_device *xe) INIT_LIST_HEAD(&pxp->queues.list); spin_lock_init(&pxp->queues.lock); - INIT_WORK(&pxp->irq.work, pxp_irq_work); + INIT_WORK(&pxp->events.work, pxp_events_work); pxp->xe = xe; pxp->gt = gt; @@ -421,8 +454,8 @@ int xe_pxp_init(struct xe_device *xe) mutex_init(&pxp->mutex); - pxp->irq.wq = alloc_ordered_workqueue("pxp-wq", 0); - if (!pxp->irq.wq) { + pxp->events.wq = alloc_ordered_workqueue("pxp-wq", 0); + if (!pxp->events.wq) { err = -ENOMEM; goto out_free; } @@ -442,7 +475,7 @@ int xe_pxp_init(struct xe_device *xe) out_kcr_disable: kcr_pxp_disable(pxp); out_wq: - destroy_workqueue(pxp->irq.wq); + destroy_workqueue(pxp->events.wq); out_free: drmm_kfree(&xe->drm, pxp); out: @@ -889,6 +922,7 @@ int xe_pxp_pm_suspend(struct xe_pxp *pxp) fallthrough; case XE_PXP_ACTIVE: pxp->key_instance++; + pxp->needs_termination_on_resume = true; needs_queue_inval = true; break; } @@ -924,6 +958,7 @@ int xe_pxp_pm_suspend(struct xe_pxp *pxp) */ void xe_pxp_pm_resume(struct xe_pxp *pxp) { + bool has_pm = false; int err; if (!xe_pxp_is_enabled(pxp)) @@ -931,14 +966,57 @@ void xe_pxp_pm_resume(struct xe_pxp *pxp) err = kcr_pxp_enable(pxp); + /* + * We want to avoid the device runtime suspending before we're done with + * the termination queued below, so we need a runtime PM reference; we + * can't call the rpm functions from within the PXP lock, so we take the + * ref here. Note that we don't want the rpm resume code to actually run + * here as that would call back into this function, but as long as we + * don't enable DPM_FLAG_SMART_SUSPEND (which we currently do not) we're + * guaranteed to not be runtime suspended at this point, so we can + * safely use the get_noresume variant. + */ + if (pxp->needs_termination_on_resume) { + has_pm = true; + + xe_assert(pxp->xe, !dev_pm_smart_suspend(pxp->xe->drm.dev)); + xe_pm_runtime_get_noresume(pxp->xe); + } + mutex_lock(&pxp->mutex); xe_assert(pxp->xe, pxp->status == XE_PXP_SUSPENDED); - if (err) + if (err) { pxp->status = XE_PXP_ERROR; - else + } else { pxp->status = XE_PXP_NEEDS_TERMINATION; + if (pxp->needs_termination_on_resume) { + pxp->needs_termination_on_resume = false; + + /* + * We can't call pxp_terminate_hw directly from here + * because we're not allowed to do allocations within + * the rpm resume call, so we defer the termination to + * the worker that we use for the termination irqs. + * However, we do not want any PXP ops to go through + * between the suspend completing and the worker + * starting, so we need to do the termination prep + * immediately, which will mark the termination as in + * progress and stall PXP ops. + */ + if (pxp_prep_for_termination(pxp)) { + has_pm = false; /* move PM ref ownership to worker */ + + atomic_or(PXP_TERMINATION_REQUEST_ON_RESUME, &pxp->events.pending); + queue_work(pxp->events.wq, &pxp->events.work); + } + } + } + mutex_unlock(&pxp->mutex); + + if (has_pm) + xe_pm_runtime_put(pxp->xe); } diff --git a/drivers/gpu/drm/xe/xe_pxp_types.h b/drivers/gpu/drm/xe/xe_pxp_types.h index ec86306e16f4..8132a9750b6e 100644 --- a/drivers/gpu/drm/xe/xe_pxp_types.h +++ b/drivers/gpu/drm/xe/xe_pxp_types.h @@ -85,17 +85,20 @@ struct xe_pxp { /** @gsc_res: kernel-owned objects for PXP submissions to the GSCCS */ struct xe_pxp_gsc_client_resources gsc_res; - /** @irq: wrapper for the worker and queue used for PXP irq support */ + /** @events: wrapper for the worker and queue used for PXP event handling */ struct { - /** @irq.work: worker that manages irq events. */ + /** @events.work: worker that manages termination events. */ struct work_struct work; - /** @irq.wq: workqueue on which to queue the irq work. */ + /** @events.wq: workqueue on which to queue the work. */ struct workqueue_struct *wq; - /** @irq.events: pending events, protected with xe->irq.lock. */ - u32 events; -#define PXP_TERMINATION_REQUEST BIT(0) -#define PXP_TERMINATION_COMPLETE BIT(1) - } irq; + /** @events.pending: pending events */ + atomic_t pending; +#define PXP_TERMINATION_REQUEST_IRQ BIT(0) +#define PXP_TERMINATION_REQUEST_ON_RESUME BIT(1) +#define PXP_TERMINATION_REQUEST (PXP_TERMINATION_REQUEST_IRQ | \ + PXP_TERMINATION_REQUEST_ON_RESUME) +#define PXP_TERMINATION_COMPLETE_IRQ BIT(2) + } events; /** @mutex: protects the pxp status and the queue list */ struct mutex mutex; @@ -130,6 +133,14 @@ struct xe_pxp { * suspend cycles. */ u32 last_suspend_key_instance; + /** + * @needs_termination_on_resume: indicates if PXP termination is needed + * on resume. This is set if PXP was active when we suspend and it is + * cleared when we queue the termination on resume. Since the suspend + * and resume calls cannot execute at the same time, this variable does + * not need to be protected by the PXP lock. + */ + bool needs_termination_on_resume; }; #endif /* _XE_PXP_TYPES_H_ */ From cb4afddf9e018a83fec8614d8e337d313871569f Mon Sep 17 00:00:00 2001 From: Gajendra Uttamchand Date: Mon, 10 Aug 2026 07:18:14 +0000 Subject: [PATCH 156/171] drm/xe/lrc: document sentinel and make CTX_TIMESTAMP read TOCTOU-safe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: CTX_TIMESTAMP MMIO reads could be stale if a context switched out between check and read; LRC stores a sentinel while a context starts that must not be treated as a real timestamp. Fix: Check the LRC-stored sentinel before and after the MMIO read; return the LRC value if the context switched out to avoid TOCTOU. Note: Keep XE_LRC_CTX_TIMESTAMP_ACTIVE in xe_lrc.h as the canonical sentinel. Fixes: d243ef6a39c6 ("drm/xe/lrc: Refactor xe_lrc_timestamp to simplify logic") Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7956 Assisted-by: GitHub-Copilot:claude-sonnet-5 Signed-off-by: Gajendra Uttamchand Reviewed-by: Umesh Nerlige Ramappa Acked-by: Matthew Brost Signed-off-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260810071812.213358-4-gajendra.uttamchand@intel.com (cherry picked from commit a806534474df071a730d930df479976a812b699d) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_lrc.c | 22 ++++++++++++++-------- drivers/gpu/drm/xe/xe_lrc.h | 7 +++++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index 78969d3e5904..9f8217ff1904 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -1096,7 +1096,7 @@ static void xe_lrc_finish(struct xe_lrc *lrc) * on until it is scheduled, we also read the ENGINE_ID MMIO in the WA BB and * store it in the PPHSWP. */ -#define CONTEXT_ACTIVE 1ULL +#define CONTEXT_ACTIVE XE_LRC_CTX_TIMESTAMP_ACTIVE static ssize_t setup_utilization_wa(struct xe_lrc *lrc, struct xe_hw_engine *hwe, u32 *batch, @@ -2727,21 +2727,27 @@ static u64 xe_lrc_update_multi_queue_timestamp(struct xe_lrc *lrc, u64 *old_ts) static u64 xe_lrc_context_timestamp(struct xe_lrc *lrc) { u64 reg_ts, new_ts = lrc->ctx_timestamp; + u64 stored; /* CTX_TIMESTAMP mmio read is invalid on VF, so return the LRC value */ if (IS_SRIOV_VF(lrc_to_xe(lrc))) return xe_lrc_ctx_timestamp(lrc); - if (context_active(lrc) && - !get_ctx_timestamp(lrc, xe_lrc_engine_id(lrc), ®_ts)) + /* Safely read CTX_TIMESTAMP: Avoid TOCTOU on LRC-stored CONTEXT_ACTIVE sentinel */ + stored = xe_lrc_ctx_timestamp(lrc); + if (stored != CONTEXT_ACTIVE) + return stored; + + /* Context is active: read the live timestamp from the engine's MMIO register */ + if (!get_ctx_timestamp(lrc, xe_lrc_engine_id(lrc), ®_ts)) new_ts = reg_ts; - /* - * If context swicthed out while we were here, just return the latest - * LRC CTX TIMESTAMP value. + /* If the context switched out prefer using the value + * from context-save over the stale MMIO read. */ - if (!context_active(lrc)) - return xe_lrc_ctx_timestamp(lrc); + stored = xe_lrc_ctx_timestamp(lrc); + if (stored != CONTEXT_ACTIVE) + return stored; return new_ts; } diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h index 0a3a611391ee..7be5e3da8bc8 100644 --- a/drivers/gpu/drm/xe/xe_lrc.h +++ b/drivers/gpu/drm/xe/xe_lrc.h @@ -9,6 +9,13 @@ #include "xe_lrc_types.h" +/* + * Sentinel value stored in lrc->ctx_timestamp while a context is starting. + * The hardware hasn't yet written the real CTX_TIMESTAMP, so this is not a + * valid elapsed-time sample and must not be used as one. + */ +#define XE_LRC_CTX_TIMESTAMP_ACTIVE 1ULL + struct drm_printer; struct xe_bb; struct xe_device; From 8d33c4987cd162527375a3905017ae129ba7c3fe Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Fri, 31 Jul 2026 09:19:32 +0800 Subject: [PATCH 157/171] drm/xe/oa: Fix sync entry leak on OA config emit failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xe_oa_emit_oa_config() releases the sync entries and the syncs array only on its success path. When it fails before the point of no return (fence allocation, config buffer allocation or batch submission), it returns without touching stream->syncs. The stream open path handles such failures in the caller, but xe_oa_config_locked() propagates the error without any cleanup, so the syncs array and the fence references held by the parsed entries are leaked. The next config ioctl overwrites stream->syncs, making the memory unreachable for good. Clean up the parsed syncs when xe_oa_emit_oa_config() fails, matching the cleanup done by the stream open error path. Fixes: 9920c8b88c5c ("drm/xe/oa: Add syncs support to OA config ioctl") Signed-off-by: Linmao Li Reviewed-by: Ashutosh Dixit Signed-off-by: Ashutosh Dixit Link: https://patch.msgid.link/20260731011932.3426219-1-lilinmao@kylinos.cn (cherry picked from commit 8af97b3da2cfce04e6b457c6eb17ed3c1daf912b) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_oa.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 2dce6a47202c..9cf962b71cb6 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -1594,6 +1594,10 @@ static long xe_oa_config_locked(struct xe_oa_stream *stream, u64 arg) config = xchg(&stream->oa_config, config); drm_dbg(&stream->oa->xe->drm, "changed to oa config uuid=%s\n", stream->oa_config->uuid); + } else { + while (param.num_syncs--) + xe_sync_entry_cleanup(¶m.syncs[param.num_syncs]); + kfree(param.syncs); } err_config_put: From f110dbbfa2a94c91704bf19806907a98fd73ca14 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Mon, 13 Jul 2026 16:30:53 +0800 Subject: [PATCH 158/171] drm/xe/oa: Check managed mutex initialization errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drmm_mutex_init() can fail while registering its managed cleanup action. On failure, the reset path destroys the mutex, so continuing OA setup leaves an unusable lock that later paths may acquire. Return the error from per-GT OA initialization and abort device-wide OA initialization if the metrics lock cannot be initialized. Fixes: a9f905ae7b6f ("drm/xe/oa/uapi: Initialize OA units") Fixes: cdf02fe1a94a ("drm/xe/oa/uapi: Add/remove OA config perf ops") Signed-off-by: Linmao Li Reviewed-by: Umesh Nerlige Ramappa Signed-off-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260713083053.321091-1-lilinmao@kylinos.cn (cherry picked from commit 360b293de27bfdd0d07047f8efd5ba8e91fa90b7) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_oa.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 9cf962b71cb6..18d990c5d4ec 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -2717,9 +2717,7 @@ static int xe_oa_init_gt(struct xe_gt *gt) __xe_oa_init_oa_units(gt); - drmm_mutex_init(>_to_xe(gt)->drm, >->oa.gt_lock); - - return 0; + return drmm_mutex_init(>_to_xe(gt)->drm, >->oa.gt_lock); } static void xe_oa_print_gt_oa_units(struct xe_gt *gt) @@ -2859,7 +2857,10 @@ int xe_oa_init(struct xe_device *xe) oa->xe = xe; oa->oa_formats = oa_formats; - drmm_mutex_init(&oa->xe->drm, &oa->metrics_lock); + ret = drmm_mutex_init(&oa->xe->drm, &oa->metrics_lock); + if (ret) + goto exit; + idr_init_base(&oa->metrics_idr, 1); ret = xe_oa_init_oa_units(oa); From 5cf82c8cec90056511eb881a267aab6101eaf57a Mon Sep 17 00:00:00 2001 From: Vinay Belgaumkar Date: Wed, 5 Aug 2026 16:46:49 -0700 Subject: [PATCH 159/171] drm/xe: Fix a bug in pc_adjust_freq_bounds() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In cases where min frequency was actually greater than BMG_MIN_FREQ, we were not using the updated min frequency as there was a missing call to pc_action_query_task_state() between the two settings of min frequency. Since we know what min_freq was last set, use that cached value while comparing to BMG_MIN_FREQ to fix this issue. v2: pc->freq_ready is not set until after pc_adjust_freq_bounds(). Stay with pc_action_query_task_state() instead. v3: Update commit message (Stuart) Fixes: bdde16c9ac5c ("drm/xe/bmg: Update Wa_14022085890") Signed-off-by: Balasubramani Vivekanandan Signed-off-by: Vinay Belgaumkar Reviewed-by: Stuart Summers Link: https://patch.msgid.link/20260805234649.2076384-1-vinay.belgaumkar@intel.com (cherry picked from commit a2c2d2b13a9ea9494d2d76b46273833111749507) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_guc_pc.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c index 59f2fa79ad42..7cf8f4858598 100644 --- a/drivers/gpu/drm/xe/xe_guc_pc.c +++ b/drivers/gpu/drm/xe/xe_guc_pc.c @@ -911,6 +911,7 @@ static bool pc_needs_min_freq_change(struct xe_guc_pc *pc) static int pc_adjust_freq_bounds(struct xe_guc_pc *pc) { int ret; + u32 min_freq; lockdep_assert_held(&pc->freq_lock); @@ -933,8 +934,14 @@ static int pc_adjust_freq_bounds(struct xe_guc_pc *pc) * Same thing happens for Server platforms where min is listed as * RPMax */ - if (pc_get_min_freq(pc) > pc->rp0_freq) + min_freq = pc_get_min_freq(pc); + if (min_freq > pc->rp0_freq) { ret = pc_set_min_freq(pc, pc->rp0_freq); + if (ret) + goto out; + + min_freq = pc->rp0_freq; + } /* * Setting GT RP min frequency to 1.2GHz by default for @@ -947,8 +954,8 @@ static int pc_adjust_freq_bounds(struct xe_guc_pc *pc) * we aren't expecting high power output across board * */ - if (pc_needs_min_freq_change(pc)) - ret = pc_set_min_freq(pc, max(BMG_MIN_FREQ, pc_get_min_freq(pc))); + if (pc_needs_min_freq_change(pc) && min_freq < BMG_MIN_FREQ) + ret = pc_set_min_freq(pc, BMG_MIN_FREQ); out: return ret; From ef526d122b62af5afa437f095aa6661a953676c4 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Mon, 10 Aug 2026 18:08:20 +0530 Subject: [PATCH 160/171] drm/xe: Fix xe_device_probe() failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, xe_device_probe() jumps to err_unregister_display label in case of failure except for its last call, which directly returns the error without required cleanup handling. This results in stale drm device that isn't cleaned up on unwind. Fix it. [ 810.194180] sysfs: cannot create duplicate filename '/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:01.0/0000:03:00.0/drm/renderD128' [ 810.194183] CPU: 9 UID: 0 PID: 5616 Comm: modprobe Kdump: loaded Tainted: G S U E 7.2.0-rc2-xe #382 PREEMPT(full) [ 810.194185] Tainted: [S]=CPU_OUT_OF_SPEC, [U]=USER, [E]=UNSIGNED_MODULE [ 810.194186] Hardware name: ASUS System Product Name/PRIME Z790-P WIFI, BIOS 1805 10/30/2024 [ 810.194186] Call Trace: [ 810.194187] [ 810.194188] dump_stack_lvl+0xe0/0x100 [ 810.194195] dump_stack+0x14/0x20 [ 810.194197] sysfs_warn_dup+0x5f/0x80 [ 810.194204] sysfs_create_dir_ns+0xbe/0xd0 [ 810.194210] kobject_add_internal+0xbc/0x2b0 [ 810.194215] kobject_add+0x7c/0xe0 [ 810.194220] ? get_device_parent+0xcf/0x1e0 [ 810.194227] device_add+0xe3/0x870 [ 810.194231] ? __pfx_drm_gem_name_info+0x10/0x10 [drm] [ 810.194280] drm_minor_register+0x73/0x130 [drm] [ 810.194322] drm_dev_register+0x76/0x2a0 [drm] Cc: stable@vger.kernel.org Fixes: da3799c97572 ("drm/xe: Use GuC to do GGTT invalidations for the GuC firmware") Signed-off-by: Raag Jadav Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260810123821.105605-1-raag.jadav@intel.com Signed-off-by: Matt Roper (cherry picked from commit 5ce3042c67c539480882567137ff8d56118885d6) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_device.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index dcb48caa485d..a2b076b1dc60 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -1102,7 +1102,11 @@ int xe_device_probe(struct xe_device *xe) if (err) goto err_unregister_display; - return devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe); + err = devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe); + if (err) + goto err_unregister_display; + + return 0; err_unregister_display: xe_display_unregister(xe); From 3aa1dcaa4f6f5ae08936491e08bd456f331f2d40 Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Tue, 4 Aug 2026 17:00:04 +0500 Subject: [PATCH 161/171] Revert "wifi: mt76: Disable napi when removing device" This reverts commit 13b7e6a96a005c656d38f3da51581deaf9866375. That commit made mt76_dma_cleanup() disable every RX NAPI instance before deleting it, to silence WARNs in __netif_napi_del_locked() and page_pool_disable_direct_recycling() seen when unloading mt7915e with an MT7916. On mt7921e and mt7925e the same instances are already disabled earlier, in mt7921e_unregister_device() and mt7925e_unregister_device(), which only afterwards call mt792x_dma_cleanup() -> mt76_dma_cleanup(). Each instance is therefore disabled twice, and napi_disable() is not idempotent: on return it leaves NAPIF_STATE_SCHED and NAPIF_STATE_NPSVC set, so the second call spins in usleep_range() forever, waiting for bits that nobody will clear. mt7921_pci_shutdown() and mt7925_pci_shutdown() reuse the remove path, so this is hit on every reboot, poweroff and module unload. It is silent: the stuck task keeps sleeping and rescheduling, so neither the hung task detector nor the lockup detectors fire, and the last line on the console is "systemd-shutdown[1]: Rebooting." task:modprobe state:D stack:25720 pid:7954 tgid:7954 Call Trace: __schedule+0x11b8/0x26d0 schedule+0xe7/0x2f0 schedule_hrtimeout_range_clock+0x218/0x330 usleep_range_state+0x133/0x1b0 napi_disable_locked+0x37d/0x5f0 napi_disable+0x43/0x80 mt76_dma_cleanup+0x2b4/0x860 [mt76] mt7921_pci_remove+0x17f/0x350 [mt7921e] pci_device_remove+0xb6/0x1e0 device_release_driver_internal+0x38d/0x540 driver_detach+0xd0/0x1b0 bus_remove_driver+0x127/0x2d0 pci_unregister_driver+0x2a/0x280 __do_sys_delete_module+0x36a/0x5b0 do_syscall_64+0x11c/0x6d0 entry_SYSCALL_64_after_hwframe+0x76/0x7e Dropping the two driver-side loops instead was tried and rejected: with them gone, the RX poll can reach mt76_token_release() via PKT_TYPE_TXRX_NOTIFY and mt7921_mac_tx_free() while mt76_connac2_tx_token_put() is running idr_destroy(&dev->token) outside token_lock, which is a use-after-free rather than a hang [1]. Revert for now, so that reboot, poweroff and module unload work again. The WARNs on mt7915e are a less severe problem than an unbootable machine, and fixing them belongs in the drivers that delete the NAPI instances, where each one can pick a point that is safe for its own teardown order, rather than in the shared mt76_dma_cleanup(). [ This is the "landing soonish" known regression fix mentioned in the previous networking merge commit - Linus ] Reported-by: Bert Karwatzki Closes: https://lore.kernel.org/all/20260724151419.26014-1-spasswolf@web.de/ Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221818 Link: https://lore.kernel.org/all/20260730050428.GA73812@sol/ [1] Signed-off-by: Mikhail Gavrilov Acked-by: Nicolas Cavallari Fixes: 13b7e6a96a00 ("wifi: mt76: Disable napi when removing device") Tested-by: Devin Wittmayer Signed-off-by: Linus Torvalds --- drivers/net/wireless/mediatek/mt76/dma.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 322041859217..f8c2fe5f2f58 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -1189,10 +1189,7 @@ void mt76_dma_cleanup(struct mt76_dev *dev) mt76_for_each_q_rx(dev, i) { struct mt76_queue *q = &dev->q_rx[i]; - if (!mt76_queue_is_wed_rro(q)) { - napi_disable(&dev->napi[i]); - netif_napi_del(&dev->napi[i]); - } + netif_napi_del(&dev->napi[i]); mt76_dma_rx_cleanup(dev, q); page_pool_destroy(q->page_pool); From 11058bd3d47d57eb3473935feae53868d6d168b7 Mon Sep 17 00:00:00 2001 From: Jasper Wise Date: Thu, 13 Aug 2026 08:46:18 +0000 Subject: [PATCH 162/171] spi: virtio: mark device ready before registering the controller virtio_spi_probe() registers the SPI controller with devm_spi_register_controller(). spi_register_controller() binds a child inline unless its driver has asked for asynchronous probing, so a peripheral that performs a transfer during its own probe reaches virtio_spi_transfer_one(), which kicks the virtqueue before probe has returned. The driver never calls virtio_device_ready(), so DRIVER_OK is set on its behalf by virtio_dev_probe(), only once probe has returned. The virtio spec is explicit about that ordering in 3.1 Device Initialization: | The driver MUST NOT send any buffer available notifications to the | device before setting DRIVER_OK. A device that waits for DRIVER_OK before servicing the queue therefore leaves the transfer unanswered, and virtio_spi_transfer_one() waits for its completion with no timeout, so probe never returns. Mark the device ready before registering the controller, as done for the same reason in commit f5866db64f34 ("virtio_console: enable VQs early") and commit 1d774589f924 ("i2c: virtio: mark device ready before registering the adapter"). Fixes: f98cabe3f6cf ("SPI: Add virtio SPI driver") Signed-off-by: Jasper Wise Link: https://patch.msgid.link/20260813084618.613172-1-jaspwise@amazon.co.uk Signed-off-by: Mark Brown --- drivers/spi/spi-virtio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-virtio.c b/drivers/spi/spi-virtio.c index fa4c68049eca..2256dfec5407 100644 --- a/drivers/spi/spi-virtio.c +++ b/drivers/spi/spi-virtio.c @@ -358,6 +358,8 @@ static int virtio_spi_probe(struct virtio_device *vdev) if (ret) return dev_err_probe(&vdev->dev, ret, "Cannot register virtqueue cleanup\n"); + virtio_device_ready(vdev); + /* Use devm version to register controller */ ret = devm_spi_register_controller(&vdev->dev, ctrl); if (ret) From b69859204d4db3acd86c1c2dadcef0d77b451933 Mon Sep 17 00:00:00 2001 From: Hui Su Date: Wed, 12 Aug 2026 01:39:03 +0800 Subject: [PATCH 163/171] tracing: Fix NULL pointer dereference in module event cache removal A module-only event filter such as ":mod:foo" is cached with a NULL event_mod->match when foo has not been loaded. If a later write tries to remove a specific match from the same module, remove_cache_mod() passes the NULL cached match to strcmp(), causing a NULL pointer dereference. The issue can be reproduced from userspace: echo ':mod:trace_events_kunit_missing' > /sys/kernel/tracing/set_event echo '!foo_bar:mod:trace_events_kunit_missing' >> /sys/kernel/tracing/set_event The second write must be a concatenation (">>") to not include O_TRUNC as that would cause ftrace_clear_events() to clear the cached modules lines. The crash was reproduced on x86_64 QEMU while KUnit workers contended on the event tracing path: BUG: kernel NULL pointer dereference, address: 0000000000000000 #PF: supervisor read access in kernel mode RIP: 0010:strcmp+0x10/0x30 Call Trace: __ftrace_set_clr_event_nolock+0x373/0x4a0 ftrace_set_clr_event+0xf0/0x180 ftrace_event_write+0xdf/0x110 vfs_write+0xf6/0x440 ksys_write+0x68/0xe0 do_syscall_64+0xf9/0x540 entry_SYSCALL_64_after_hwframe+0x77/0x7f Check event_mod->match before comparing it, consistent with the existing NULL checks for the cached system and event fields. The mismatched removal continues to return -EINVAL; a broad cached module filter is removed with "!:mod:". Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260811173902.1927376-2-sh_def@163.com Fixes: b355247df104 ("tracing: Cache \":mod:\" events for modules not loaded yet") Reported-by: syzbot+4d3143c8e28f6266c636@syzkaller.appspotmail.com Closes: https://lore.kernel.org/lkml/6a7a6b7f.9c11d2ce.289b96.00f8.GAE@google.com/ Signed-off-by: Hui Su Signed-off-by: Steven Rostedt --- kernel/trace/trace_events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index c01b10b99f67..032f741ba616 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -945,7 +945,7 @@ static int remove_cache_mod(struct trace_array *tr, const char *mod, if (strcmp(event_mod->module, mod) != 0) continue; - if (match && strcmp(event_mod->match, match) != 0) + if (match && (!event_mod->match || strcmp(event_mod->match, match) != 0)) continue; if (system && From c3730b8373bb5059d735509b9e6a00d7eb337d7c Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Mon, 10 Aug 2026 14:32:30 +0800 Subject: [PATCH 164/171] tracing: Fix race between update_event_fields and, event_define_fields The following sequence may leads race between event_define_fields() and update_event_fields(): CPU0 (loads module A) CPU1 (loads module B) =============================== =============================== load_module(A) load_module(B) notifier_call_chain notifier_call_chain trace_module_notify trace_module_notify mutex_lock(&event_mutex) trace_event_update_all() trace_module_add_events(A) down_write(&trace_event_sem) __register_event(call_A) __add_event_to_tracers(call_A) event_define_fields(call_A) for each f: list_for_each_entry(field, list_add(&f->link, &class->fields, link) &class->fields) field = class->fields->next; Where access to the class->fields is not protected by the event_mutex in trace_event_update_all(). This produces the following panic: Unable to handle kernel access ... at virtual address 0000000000000018 pc : update_event_fields+0xf8/0x368 Call trace: update_event_fields+0xf8/0x368 trace_event_update_all+0x7c/0x2b4 trace_module_notify+0x4c/0x1dc notifier_call_chain+0x84/0x168 blocking_notifier_call_chain_robust+0x64/0xd4 load_module+0x10c8/0x123c __arm64_sys_finit_module+0x230/0x31c Fix by taking event_mutex in trace_event_update_all() before trace_event_sem. Cc: stable@vger.kernel.org Fixes: b3bc8547d3be ("tracing: Have TRACE_DEFINE_ENUM affect trace event types as well") Link: https://patch.msgid.link/2e5730d2-c631-da41-3a3a-ae35bb4895f3@allwinnertech.com Signed-off-by: Michael Wu Signed-off-by: Steven Rostedt --- kernel/trace/trace_events.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 032f741ba616..3650d84d4f16 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -3566,6 +3566,7 @@ void trace_event_update_all(struct trace_eval_map **map, int len) int last_i; int i; + mutex_lock(&event_mutex); down_write(&trace_event_sem); list_for_each_entry_safe(call, p, &ftrace_events, list) { /* events are usually grouped together with systems */ @@ -3604,6 +3605,7 @@ void trace_event_update_all(struct trace_eval_map **map, int len) cond_resched(); } up_write(&trace_event_sem); + mutex_unlock(&event_mutex); } static bool event_in_systems(struct trace_event_call *call, From 333238da9a193ffc58792995f3e951e4cb87bfd2 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Fri, 14 Aug 2026 15:52:41 +0200 Subject: [PATCH 165/171] sched: Update time before requeueing delayed entities In order to compute the right lag, it is required to update time to 'now'. Without this, the delayed entity might appear younger than it really is and receive less compensation for having waited. Signed-off-by: Vincent Guittot Signed-off-by: Peter Zijlstra (Intel) --- kernel/sched/fair.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index d78467ec6ee1..f15f5764818e 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7779,6 +7779,8 @@ requeue_delayed_entity(struct sched_entity *se) WARN_ON_ONCE(!se->sched_delayed); WARN_ON_ONCE(!se->on_rq); + update_curr(cfs_rq); + if (update_entity_lag(cfs_rq, se)) { cfs_rq->nr_queued--; if (se != cfs_rq->curr) From a64d500b0078e16e9abb25baca4dee1dbc9054fc Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Wed, 12 Aug 2026 21:11:43 +0800 Subject: [PATCH 166/171] erofs: fix EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS on some UP platforms CONFIG_NR_CPUS doesn't define on some UP platforms (e.g. arm), so this can cause make oldconfig to loop indefinitely when CONFIG_SMP=n: $ make ARCH=arm allmodconfig $ sed -i "/CONFIG_SMP=y/d" .config $ sed -i "/CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS.*/d" .config EROFS LZMA default maximum decompression streams (EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS) [0] (NEW) EROFS LZMA default maximum decompression streams (EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS) [0] (NEW) ... Let's guard NR_CPUS with SMP instead of using a hardcoded arbitrary CPU uplimit here, similar to commit a3344078101c ("mm: make SPLIT_PTE_PTLOCKS depend on SMP"). The initial report from SJ Park was for m68k [1] (m68k is the only arch without NR_CPUS in Kconfig), and that got fixed in commit 1fd495ef09ee ("m68k: Define NR_CPUS to 1") Reported-by: SJ Park Link: https://lore.kernel.org/all/anuyFHLUGDjZWY4K@XiangdeMacBook-Pro.local/T/#u [1] Closes: https://lore.kernel.org/r/20260728065447.91511-1-sj@kernel.org Reported-by: Guenter Roeck Closes: https://lore.kernel.org/r/87853c96-cc8f-49e6-81b1-02bfe409e372@roeck-us.net Fixes: c9b47e6b2311 ("erofs: cap LZMA stream pool size") Signed-off-by: Gao Xiang Tested-by: SJ Park Tested-by: Geert Uytterhoeven Signed-off-by: Linus Torvalds --- fs/erofs/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig index 8ca1767dafb6..2dfc313588d2 100644 --- a/fs/erofs/Kconfig +++ b/fs/erofs/Kconfig @@ -134,7 +134,8 @@ config EROFS_FS_ZIP_LZMA config EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS int "EROFS LZMA default maximum decompression streams" depends on EROFS_FS_ZIP_LZMA - range 1 NR_CPUS + range 1 NR_CPUS if SMP + range 1 1 if !SMP default 16 help By default EROFS allocates one LZMA decompression stream per CPU. From 32ef1b30ad736519f7a207bcc2986f3d4129d972 Mon Sep 17 00:00:00 2001 From: Ali Ahmet Memis Date: Fri, 7 Aug 2026 23:42:30 +0000 Subject: [PATCH 167/171] openrisc: signal: do not restore privileged SR bits on sigreturn restore_sigcontext() copies the whole supervision register (SR) from the signal frame and only clears SPR_SR_SM before the value is reloaded into the hardware SR (through ESR and l.rfe) on the return to user space. All other SR bits are left under user control. An unprivileged task can thus return from a signal handler through a crafted sigframe that clears SPR_SR_DME. With the data MMU disabled the CPU performs no translation or protection on data accesses, so the task gains read and write access to arbitrary physical memory, a local privilege escalation. SPR_SR_IME, SPR_SR_SUMRA, SPR_SR_LEE, SPR_SR_EPH and the cache-enable bits are exposed the same way. The ptrace GPR regset already refuses any change to SR for exactly this reason. Restore only the arithmetic flag bits (F, CY, OV) from the signal frame and take every privileged control bit from the SR the kernel saved on signal entry. Verified with qemu-system-or1k -M or1k-sim: before this change an unprivileged PoC clears SPR_SR_DME in rt_sigreturn and writes a marker to physical address 0x03000000 (beyond the kernel's mem=32M); afterwards the same PoC receives SIGSEGV and physical memory is unchanged. Fixes: ac689eb7f9d4 ("OpenRISC: Signal handling") Cc: stable@vger.kernel.org Signed-off-by: Ali Ahmet Memis Signed-off-by: Stafford Horne --- arch/openrisc/include/asm/processor.h | 2 ++ arch/openrisc/kernel/signal.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/openrisc/include/asm/processor.h b/arch/openrisc/include/asm/processor.h index 3ff893a67c13..ae43fe79b2b5 100644 --- a/arch/openrisc/include/asm/processor.h +++ b/arch/openrisc/include/asm/processor.h @@ -26,6 +26,8 @@ | SPR_SR_DCE | SPR_SR_SM) #define USER_SR (SPR_SR_DME | SPR_SR_IME | SPR_SR_ICE \ | SPR_SR_DCE | SPR_SR_IEE | SPR_SR_TEE) +/* SR bits user space may change via sigreturn, the rest stay kernel owned */ +#define SPR_SR_USER_MASK (SPR_SR_F | SPR_SR_CY | SPR_SR_OV) /* * User space process size. This is hardcoded into a few places, diff --git a/arch/openrisc/kernel/signal.c b/arch/openrisc/kernel/signal.c index f70a13ee0593..2be5af3b9832 100644 --- a/arch/openrisc/kernel/signal.c +++ b/arch/openrisc/kernel/signal.c @@ -74,6 +74,7 @@ static long save_fp_state(struct sigcontext __user *sc) static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) { + unsigned long old_sr = regs->sr; int err = 0; /* Always make any pending restarted system calls return -EINTR */ @@ -89,8 +90,8 @@ static int restore_sigcontext(struct pt_regs *regs, err |= __copy_from_user(®s->sr, &sc->regs.sr, sizeof(unsigned long)); err |= restore_fp_state(sc); - /* make sure the SM-bit is cleared so user-mode cannot fool us */ - regs->sr &= ~SPR_SR_SM; + /* keep the privileged SR bits kernel owned, restore only user flags */ + regs->sr = (old_sr & ~SPR_SR_USER_MASK) | (regs->sr & SPR_SR_USER_MASK); regs->orig_gpr11 = -1; /* Avoid syscall restart checks */ From c71bf113dfdf426bdaf106636f573ef87b6613a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=B6hmwalder?= Date: Fri, 14 Aug 2026 17:16:17 +0200 Subject: [PATCH 168/171] drbd: don't leak the shared secret to unprivileged netlink dumps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The conversion to explicit netlink serialization dropped the exclude_sensitive parameter from net_conf_to_skb(), so each caller has to sanitize by hand. Two dump paths were missed: drbd_nl_get_connections_dumpit() and the volume-less connection branch of get_one_status(). Neither op carries GENL_ADMIN_PERM, so any unprivileged local user could read the CRAM-HMAC secret. Add a net_conf_to_skb_sanitized() wrapper and route all three callers through it. Fixes: 8098eeb693c4 ("drbd: replace genl_magic with explicit netlink serialization") Reported-by: Vivek Parikh Signed-off-by: Christoph Böhmwalder Link: https://patch.msgid.link/20260814151617.73752-1-christoph.boehmwalder@linbit.com Signed-off-by: Jens Axboe --- drivers/block/drbd/drbd_nl.c | 39 +++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index f9ffcd67607b..b77f901fc3ef 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -3306,6 +3306,26 @@ static int nla_put_drbd_cfg_context(struct sk_buff *skb, return -EMSGSIZE; } +/* + * net_conf_to_skb() serializes the shared secret verbatim. Any path that can + * answer a request from an unprivileged process must pass exclude_sensitive, + * so the secret is blanked in a private copy before it reaches the skb. + */ +static int net_conf_to_skb_sanitized(struct sk_buff *skb, struct net_conf *nc, + bool exclude_sensitive) +{ + struct net_conf nc_clean; + + if (!exclude_sensitive) + return net_conf_to_skb(skb, nc); + + nc_clean = *nc; + memset(nc_clean.shared_secret, 0, sizeof(nc_clean.shared_secret)); + nc_clean.shared_secret_len = 0; + + return net_conf_to_skb(skb, &nc_clean); +} + /* * The generic netlink dump callbacks are called outside the genl_lock(), so * they cannot use the simple attribute parsing code which uses global @@ -3621,7 +3641,8 @@ int drbd_nl_get_connections_dumpit(struct sk_buff *skb, struct netlink_callback goto out; net_conf = rcu_dereference(connection->net_conf); if (net_conf) { - err = net_conf_to_skb(skb, net_conf); + err = net_conf_to_skb_sanitized(skb, net_conf, + !capable(CAP_SYS_ADMIN)); if (err) goto out; } @@ -3842,18 +3863,8 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device, struct net_conf *nc; nc = rcu_dereference(first_peer_device(device)->connection->net_conf); - if (nc) { - if (exclude_sensitive) { - struct net_conf nc_clean = *nc; - - memset(nc_clean.shared_secret, 0, - sizeof(nc_clean.shared_secret)); - nc_clean.shared_secret_len = 0; - err = net_conf_to_skb(skb, &nc_clean); - } else { - err = net_conf_to_skb(skb, nc); - } - } + if (nc) + err = net_conf_to_skb_sanitized(skb, nc, exclude_sensitive); } rcu_read_unlock(); if (err) @@ -4058,7 +4069,7 @@ static int get_one_status(struct sk_buff *skb, struct netlink_callback *cb) struct net_conf *nc; nc = rcu_dereference(connection->net_conf); - if (nc && net_conf_to_skb(skb, nc) != 0) + if (nc && net_conf_to_skb_sanitized(skb, nc, true) != 0) goto cancel; } goto done; From d5b95e612cde33f9def1a7a6c3242d03d3bbde3a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 16 Aug 2026 10:40:14 -0700 Subject: [PATCH 169/171] Revert "i2c: designware: defer probe if child GpioInt controllers are not bound" This reverts commit 0a4bb2abc3e56d7be6e69b050c88ba52c87e22bf. This was reported to break the touchpad on at least some Thinkpads, and while the revert has hit the i2c tree, it hasn't hit mine. So I'm reverting it directly just to have this resolved for the imminent 7.2 release. Reported-by: Thorsten Leemhuis Link: https://lore.kernel.org/all/b4a4eadb-282f-464c-843a-19d415a34d0c@leemhuis.info/ Cc: Mario Limonciello CC: Hardik Prakash Signed-off-by: Linus Torvalds --- drivers/i2c/busses/i2c-designware-platdrv.c | 80 --------------------- 1 file changed, 80 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index c8a203fff4d1..6d6e81242f74 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -8,14 +8,12 @@ * Copyright (C) 2007 MontaVista Software Inc. * Copyright (C) 2009 Provigent Ltd. */ -#include #include #include #include #include #include #include -#include #include #include #include @@ -132,80 +130,6 @@ static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) return 0; } -#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB) -/* - * Check whether an ACPI GpioInt resource's referenced GPIO controller - * has finished probing. Resources with no named controller (resource - * source string) are skipped, since they can't be resolved to a - * struct device. - */ -static int check_gpioint_resource(struct acpi_resource *ares, void *data) -{ - struct acpi_resource_gpio *agpio; - struct acpi_device *gpio_adev; - struct device *gpio_dev; - acpi_handle handle; - acpi_status status; - - if (!acpi_gpio_get_irq_resource(ares, &agpio)) - return 1; /* not a GpioInt resource, skip */ - - if (!agpio->resource_source.string_length) - return 1; /* no named controller, skip */ - - status = acpi_get_handle(NULL, agpio->resource_source.string_ptr, &handle); - if (ACPI_FAILURE(status)) - return 1; - - gpio_adev = acpi_fetch_acpi_dev(handle); - if (!gpio_adev) - return 1; - - struct gpio_device *gdev __free(gpio_device_put) = - gpio_device_find_by_fwnode(acpi_fwnode_handle(gpio_adev)); - if (!gdev) - return -EPROBE_DEFER; /* controller not registered yet: abort walk */ - - gpio_dev = gpio_device_to_device(gdev)->parent; - - guard(device)(gpio_dev); - if (!device_is_bound(gpio_dev)) - return -EPROBE_DEFER; /* controller not bound yet: abort walk */ - - return 1; /* bound, skip adding to resource list, continue walk */ -} - -static int check_child_gpioint(struct acpi_device *adev, void *data) -{ - LIST_HEAD(res_list); - int ret; - - ret = acpi_dev_get_resources(adev, &res_list, check_gpioint_resource, NULL); - if (ret < 0) - return ret; - - acpi_dev_free_resource_list(&res_list); - - return 0; -} - -static int i2c_dw_check_gpio_dependencies(struct device *dev) -{ - struct acpi_device *adev; - - adev = ACPI_COMPANION(dev); - if (!adev) - return 0; - - return acpi_dev_for_each_child(adev, check_child_gpioint, NULL); -} -#else -static int i2c_dw_check_gpio_dependencies(struct device *dev) -{ - return 0; -} -#endif /* CONFIG_ACPI && CONFIG_GPIOLIB */ - static int dw_i2c_plat_probe(struct platform_device *pdev) { u32 flags = (uintptr_t)device_get_match_data(&pdev->dev); @@ -214,10 +138,6 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) struct dw_i2c_dev *dev; int irq, ret; - ret = i2c_dw_check_gpio_dependencies(device); - if (ret) - return ret; - irq = platform_get_irq_optional(pdev, 0); if (irq == -ENXIO) flags |= ACCESS_POLLING; From d6e7d57ed967def9c964a58328ffba409f7efb64 Mon Sep 17 00:00:00 2001 From: Charlie-cy Wu Date: Mon, 29 Jun 2026 16:35:43 +0800 Subject: [PATCH 170/171] wifi: mt76: mt7921: refactor regd update to fix recursive mutex deadlock Split mt7921_mcu_regd_update() into two functions to prevent recursive mutex acquisition. Introduce __mt7921_mcu_regd_update() as the internal implementation that assumes the mutex is already held by the caller, while mt7921_mcu_regd_update() remains as the external interface that handles mutex acquisition and release. This fixes a deadlock issue when mt7921_regd_set_6ghz_power_type() is called with the device mutex already held. Without this change, calling mt7921_mcu_regd_update() would attempt to acquire the same mutex again, causing a recursive lock deadlock. The __mt7921_mcu_regd_update() function can be safely called when the caller has already acquired the device mutex, avoiding the deadlock while maintaining proper synchronization for regulatory domain updates. Fixes: dc2608cf5224 ("wifi: mt76: mt7921: refactor regulatory notifier flow") Signed-off-by: Charlie-cy Wu Tested-by: Mikhail Gavrilov Tested-by: Devin Wittmayer Signed-off-by: Linus Torvalds --- .../net/wireless/mediatek/mt76/mt7921/main.c | 2 +- .../net/wireless/mediatek/mt76/mt7921/regd.c | 30 ++++++++++++------- .../net/wireless/mediatek/mt76/mt7921/regd.h | 2 ++ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 3480205d5fb9..68a059504e83 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -802,7 +802,7 @@ mt7921_regd_set_6ghz_power_type(struct ieee80211_vif *vif, bool is_add) out: if (vif->bss_conf.chanreq.oper.chan->band == NL80211_BAND_6GHZ) - mt7921_mcu_regd_update(dev, dev->mt76.alpha2, dev->country_ie_env); + __mt7921_mcu_regd_update(dev, dev->mt76.alpha2, dev->country_ie_env); } int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c index c0e2b48a50bf..4722007abafd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c @@ -71,36 +71,44 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) } } -int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, - enum environment_cap country_ie_env) +/* Internal version that assumes mutex is already held by caller */ +int __mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env) { struct mt76_dev *mdev = &dev->mt76; struct ieee80211_hw *hw = mdev->hw; struct wiphy *wiphy = hw->wiphy; int ret = 0; - dev->regd_in_progress = true; - - mt792x_mutex_acquire(dev); if (!dev->regd_change) - goto err; + return 0; ret = mt7921_mcu_set_clc(dev, alpha2, country_ie_env); if (ret < 0) - goto err; + return ret; mt7921_regd_channel_update(wiphy, dev); ret = mt76_connac_mcu_set_channel_domain(hw->priv); if (ret < 0) - goto err; + return ret; ret = mt7921_set_tx_sar_pwr(hw, NULL); - if (ret < 0) - goto err; -err: + return ret; +} + +int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env) +{ + int ret = 0; + + dev->regd_in_progress = true; + + mt792x_mutex_acquire(dev); + ret = __mt7921_mcu_regd_update(dev, alpha2, country_ie_env); mt792x_mutex_release(dev); + dev->regd_change = false; dev->regd_in_progress = false; wake_up(&dev->wait); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h index 571f31629e9e..5b24d0902c36 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h @@ -10,6 +10,8 @@ struct regulatory_request; int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, enum environment_cap country_ie_env); +int __mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env); void mt7921_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); bool mt7921_regd_clc_supported(struct mt792x_dev *dev); From 8d3ae59288f1e7d58d76558a6ee96d533bc5019f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 16 Aug 2026 14:32:26 -0700 Subject: [PATCH 171/171] Linux 7.2 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0e6fc0803775..ddcc6dca1ece 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 2 SUBLEVEL = 0 -EXTRAVERSION = -rc7 +EXTRAVERSION = NAME = Baby Opossum Posse # *DOCUMENTATION*