From: Sasha Levin Date: Tue, 4 Aug 2026 23:47:10 +0000 (-0400) Subject: Fixes for all trees X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a3316f12547907b12f04b3d8226484cb58c0afae;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- diff --git a/queue-5.10/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-5.10/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..293c6c052a --- /dev/null +++ b/queue-5.10/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From 745281a66272d2931ea55d12bd01bf916506f005 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:12:54 +0530 +Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ] + +In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98090->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98090.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c +index c7df685be4ea5..531cd172cb5cb 100644 +--- a/sound/soc/codecs/max98090.c ++++ b/sound/soc/codecs/max98090.c +@@ -2393,8 +2393,9 @@ static int max98090_probe(struct snd_soc_component *component) + dev_dbg(component->dev, "max98090_probe\n"); + + max98090->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98090->mclk)) ++ if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + max98090->component = component; + +-- +2.53.0 + diff --git a/queue-5.10/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-5.10/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..ec5217f0f3 --- /dev/null +++ b/queue-5.10/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From c4e521d616202dba019c1f1ce6ec73084a20174b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:09:50 +0530 +Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ] + +In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98095->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98095.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c +index 9bdc6392382a6..c0daa18e7d8f9 100644 +--- a/sound/soc/codecs/max98095.c ++++ b/sound/soc/codecs/max98095.c +@@ -1990,8 +1990,9 @@ static int max98095_probe(struct snd_soc_component *component) + int ret = 0; + + max98095->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98095->mclk)) ++ if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + /* reset the codec, the DSP core, and disable all interrupts */ + max98095_reset(component); +-- +2.53.0 + diff --git a/queue-5.10/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-5.10/assoc_array-trim-the-final-shortcut-word-using-the-c.patch new file mode 100644 index 0000000000..692f5060e1 --- /dev/null +++ b/queue-5.10/assoc_array-trim-the-final-shortcut-word-using-the-c.patch @@ -0,0 +1,66 @@ +From 60838c5fd4de2d701273702903b0f3d45a681b28 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:05 -0400 +Subject: assoc_array: trim the final shortcut word using the current chunk end + +From: Michael Bommarito + +[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ] + +assoc_array_walk() masks off the bits past shortcut->skip_to_level in the +word that contains skip_to_level, gated on +round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level. + +That guard is wrong in two opposite ways: + + - When sc_level is word-aligned (every word after the first) round_up() + is a no-op, so the guard is sc_level > skip_to_level and never fires for + the word that holds skip_to_level. A shortcut that spans more than one + word and ends in the middle of its last word leaves that word untrimmed, + and its stale high bits leak into the dissimilarity word and can steer + the walk down the wrong descendant. + + - When sc_level is unaligned (the first word) and skip_to_level sits on + the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and + fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears + the whole dissimilarity word and makes a differing shortcut compare + equal. + +Use the end of the chunk that contains sc_level instead: + + skip_to_level < round_down(sc_level, CHUNK) + CHUNK + +For an aligned sc_level whose word holds skip_to_level this now fires (the +first bug); for an unaligned sc_level with skip_to_level on the following +boundary it does not, so shift is never 0 when the branch runs and the trim +never clears the whole word. + +Fixes: 3cb989501c26 ("Add a generic associative array implementation.") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + lib/assoc_array.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/assoc_array.c b/lib/assoc_array.c +index b537a83678e11..f700986830190 100644 +--- a/lib/assoc_array.c ++++ b/lib/assoc_array.c +@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array, + sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT]; + dissimilarity = segments ^ sc_segments; + +- if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) { ++ if (shortcut->skip_to_level < round_down(sc_level, ++ ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) { + /* Trim segments that are beyond the shortcut */ + int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK; + dissimilarity &= ~(ULONG_MAX << shift); +-- +2.53.0 + diff --git a/queue-5.10/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-5.10/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch new file mode 100644 index 0000000000..71b9dbf0a5 --- /dev/null +++ b/queue-5.10/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch @@ -0,0 +1,60 @@ +From 8a8ad74d3a3c06c5b31f175052e52dcaa17ffabf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 14:48:45 +0800 +Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp + +From: Jiale Yao + +[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ] + +l2cap_le_connect_rsp() obtains a channel via +__l2cap_get_chan_by_ident() but neither holds a reference nor uses +l2cap_chan_hold_unless_zero() before locking and operating on it. +A concurrent l2cap_chan_del() triggered by a remote disconnect can +free the channel between the lookup and l2cap_chan_lock(), causing +a use-after-free. + +The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler +l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero() +to safely hold a reference, but l2cap_le_connect_rsp() was left +unprotected. + +Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup +and l2cap_chan_put() on the exit path, consistent with other L2CAP +response handlers. + +Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request") +Assisted-by: Claude:deepseek-v4-pro +Signed-off-by: Jiale Yao +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/l2cap_core.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index 6649532712f4a..c9c3bef3ae895 100644 +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -5744,6 +5744,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + goto unlock; + } + ++ chan = l2cap_chan_hold_unless_zero(chan); ++ if (!chan) ++ return -EBADSLT; ++ + err = 0; + + l2cap_chan_lock(chan); +@@ -5789,6 +5793,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + } + + l2cap_chan_unlock(chan); ++ l2cap_chan_put(chan); + + unlock: + mutex_unlock(&conn->chan_lock); +-- +2.53.0 + diff --git a/queue-5.10/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-5.10/can-isotp-check-register_netdevice_notifier-error-in.patch new file mode 100644 index 0000000000..ac75be9dac --- /dev/null +++ b/queue-5.10/can-isotp-check-register_netdevice_notifier-error-in.patch @@ -0,0 +1,57 @@ +From b17c6285bba195e62cb850953701b248badb4bcf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Jul 2026 16:56:56 +0800 +Subject: can: isotp: check register_netdevice_notifier() error in module init + +From: Minhong He + +[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ] + +Register the netdevice notifier before can_proto_register() and check the +return value. If protocol registration fails, unregister the notifier +before returning the error. + +Align isotp_module_init() with the reordering already done for raw.c +(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and +bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization +in bcm_module_init()")). + +Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier") +Signed-off-by: Minhong He +Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn +Signed-off-by: Marc Kleine-Budde +Signed-off-by: Sasha Levin +--- + net/can/isotp.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/net/can/isotp.c b/net/can/isotp.c +index d493b66ae8d29..f23862b465ca6 100644 +--- a/net/can/isotp.c ++++ b/net/can/isotp.c +@@ -1827,13 +1827,18 @@ static __init int isotp_module_init(void) + + pr_info("can: isotp protocol\n"); + ++ err = register_netdevice_notifier(&canisotp_notifier); ++ if (err) ++ return err; ++ + err = can_proto_register(&isotp_can_proto); +- if (err < 0) ++ if (err < 0) { + pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err)); +- else +- register_netdevice_notifier(&canisotp_notifier); ++ unregister_netdevice_notifier(&canisotp_notifier); ++ return err; ++ } + +- return err; ++ return 0; + } + + static __exit void isotp_module_exit(void) +-- +2.53.0 + diff --git a/queue-5.10/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-5.10/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch new file mode 100644 index 0000000000..bbd7d10d68 --- /dev/null +++ b/queue-5.10/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch @@ -0,0 +1,60 @@ +From deb6b2b8a5c22e795066ee0ff958def1efe2e576 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 1 Jul 2026 12:57:33 +0800 +Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA + +From: Hongling Zeng + +[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ] + +When terminating DMA transfers, active descriptors are not properly +reclaimed. Only cyclic descriptors were handled, leaving non-cyclic +descriptors and their LLI chains to be permanently leaked. + +Fix by using vchan_terminate_vdesc() which handles both cyclic and +non-cyclic descriptors by adding them to desc_terminated queue for +proper cleanup. + +Add pchan->desc != pchan->done check to prevent double-adding completed +descriptors, which would corrupt the list. + +Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller") +Signed-off-by: Hongling Zeng +Acked-by: Jernej Skrabec +Suggested-by: Frank Li +Reviewed-by: Frank Li +Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/dma/sun6i-dma.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c +index f5f9c86c50bc2..d846309921a7e 100644 +--- a/drivers/dma/sun6i-dma.c ++++ b/drivers/dma/sun6i-dma.c +@@ -895,16 +895,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan) + + spin_lock_irqsave(&vchan->vc.lock, flags); + +- if (vchan->cyclic) { +- vchan->cyclic = false; +- if (pchan && pchan->desc) { +- struct virt_dma_desc *vd = &pchan->desc->vd; +- struct virt_dma_chan *vc = &vchan->vc; ++ if (pchan && pchan->desc && pchan->desc != pchan->done) { ++ struct virt_dma_desc *vd = &pchan->desc->vd; + +- list_add_tail(&vd->node, &vc->desc_completed); +- } ++ vchan_terminate_vdesc(vd); + } + ++ vchan->cyclic = false; + vchan_get_all_descriptors(&vchan->vc, &head); + + if (pchan) { +-- +2.53.0 + diff --git a/queue-5.10/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-5.10/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch new file mode 100644 index 0000000000..e716c130a2 --- /dev/null +++ b/queue-5.10/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch @@ -0,0 +1,47 @@ +From db8078eedeff5b6c2f1e9aad57a65d9dcffe156f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 17:26:37 +0800 +Subject: forcedeth: fix UAF of txrx_stats in nv_remove + +From: Chenguang Zhao + +[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ] + +nv_remove() frees the per-CPU txrx_stats before unregister_netdev(). +Until unregister completes, ndo_get_stats64, the NAPI/xmit data path, +and nv_close()/drain may still access txrx_stats, leading to a +use-after-free. + +Free the stats only after unregister_netdev(). + +Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Reviewed-by: Zhu Yanjun +Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/nvidia/forcedeth.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c +index e14dd1051e58c..f39b7fcfcd38a 100644 +--- a/drivers/net/ethernet/nvidia/forcedeth.c ++++ b/drivers/net/ethernet/nvidia/forcedeth.c +@@ -6199,10 +6199,10 @@ static void nv_remove(struct pci_dev *pci_dev) + struct net_device *dev = pci_get_drvdata(pci_dev); + struct fe_priv *np = netdev_priv(dev); + +- free_percpu(np->txrx_stats); +- + unregister_netdev(dev); + ++ free_percpu(np->txrx_stats); ++ + nv_restore_mac_addr(pci_dev); + + /* restore any phy related changes */ +-- +2.53.0 + diff --git a/queue-5.10/hwmon-adt7470-convert-to-use-regmap.patch b/queue-5.10/hwmon-adt7470-convert-to-use-regmap.patch new file mode 100644 index 0000000000..eac73540ef --- /dev/null +++ b/queue-5.10/hwmon-adt7470-convert-to-use-regmap.patch @@ -0,0 +1,834 @@ +From 90c75123f9cffaceedd7614a827c214380bd53a6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Aug 2021 14:41:19 +1200 +Subject: hwmon: (adt7470) Convert to use regmap + +From: Chris Packham + +[ Upstream commit ef67959c42539bfece4d7c4335c07656703bb027 ] + +Convert the adt7470 to using regmap which allows better error handling. + +Signed-off-by: Chris Packham +Link: https://lore.kernel.org/r/20210826024121.15665-3-chris.packham@alliedtelesis.co.nz +Signed-off-by: Guenter Roeck +Stable-dep-of: 92413f439d1e ("hwmon: (adt7470) Fix PWM auto temp state array and bounds check") +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 414 +++++++++++++++++++++++----------------- + 1 file changed, 241 insertions(+), 173 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 95c5e219adeff..274d38a9ba589 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -36,7 +37,10 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + #define ADT7470_REG_PWM_MAX_BASE_ADDR 0x38 + #define ADT7470_REG_PWM_MAX_MAX_ADDR 0x3B + #define ADT7470_REG_CFG 0x40 ++#define ADT7470_STRT_MASK 0x01 ++#define ADT7470_TEST_MASK 0x02 + #define ADT7470_FSPD_MASK 0x04 ++#define ADT7470_T05_STB_MASK 0x80 + #define ADT7470_REG_ALARM1 0x41 + #define ADT7470_R1T_ALARM 0x01 + #define ADT7470_R2T_ALARM 0x02 +@@ -138,7 +142,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + #define ADT7470_FREQ_SHIFT 4 + + struct adt7470_data { +- struct i2c_client *client; ++ struct regmap *regmap; + struct mutex lock; + char sensors_valid; + char limits_valid; +@@ -172,52 +176,76 @@ struct adt7470_data { + * 16-bit registers on the ADT7470 are low-byte first. The data sheet says + * that the low byte must be read before the high byte. + */ +-static inline int adt7470_read_word_data(struct i2c_client *client, u8 reg) ++static inline int adt7470_read_word_data(struct adt7470_data *data, unsigned int reg, ++ unsigned int *val) + { +- u16 foo; ++ u8 regval[2]; ++ int err; ++ ++ err = regmap_bulk_read(data->regmap, reg, ®val, 2); ++ if (err < 0) ++ return err; ++ ++ *val = regval[0] | (regval[1] << 8); + +- foo = i2c_smbus_read_byte_data(client, reg); +- foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8); +- return foo; ++ return 0; + } + +-static inline int adt7470_write_word_data(struct i2c_client *client, u8 reg, +- u16 value) ++static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned int reg, ++ unsigned int val) + { +- return i2c_smbus_write_byte_data(client, reg, value & 0xFF) +- || i2c_smbus_write_byte_data(client, reg + 1, value >> 8); ++ u8 regval[2]; ++ ++ regval[0] = val & 0xFF; ++ regval[1] = val >> 8; ++ ++ return regmap_bulk_write(data->regmap, reg, ®val, 2); + } + + /* Probe for temperature sensors. Assumes lock is held */ +-static int adt7470_read_temperatures(struct i2c_client *client, +- struct adt7470_data *data) ++static int adt7470_read_temperatures(struct adt7470_data *data) + { + unsigned long res; ++ unsigned int pwm_cfg[2]; ++ int err; + int i; +- u8 cfg, pwm[4], pwm_cfg[2]; ++ u8 pwm[ADT7470_FAN_COUNT]; + + /* save pwm[1-4] config register */ +- pwm_cfg[0] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(0)); +- pwm_cfg[1] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(2)); ++ err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); ++ if (err < 0) ++ return err; ++ err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(2), &pwm_cfg[1]); ++ if (err < 0) ++ return err; + + /* set manual pwm to whatever it is set to now */ +- for (i = 0; i < ADT7470_FAN_COUNT; i++) +- pwm[i] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM(i)); ++ err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &pwm[0], ++ ADT7470_PWM_COUNT); ++ if (err < 0) ++ return err; + + /* put pwm in manual mode */ +- i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0), +- pwm_cfg[0] & ~(ADT7470_PWM_AUTO_MASK)); +- i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), +- pwm_cfg[1] & ~(ADT7470_PWM_AUTO_MASK)); ++ err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(0), ++ ADT7470_PWM_AUTO_MASK, 0); ++ if (err < 0) ++ return err; ++ err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), ++ ADT7470_PWM_AUTO_MASK, 0); ++ if (err < 0) ++ return err; + + /* write pwm control to whatever it was */ +- for (i = 0; i < ADT7470_FAN_COUNT; i++) +- i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(i), pwm[i]); ++ err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], ++ ADT7470_PWM_COUNT); ++ if (err < 0) ++ return err; + + /* start reading temperature sensors */ +- cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); +- cfg |= 0x80; +- i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg); ++ err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, ++ ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); ++ if (err < 0) ++ return err; + + /* Delay is 200ms * number of temp sensors. */ + res = msleep_interruptible((data->num_temp_sensors >= 0 ? +@@ -225,26 +253,31 @@ static int adt7470_read_temperatures(struct i2c_client *client, + TEMP_COLLECTION_TIME)); + + /* done reading temperature sensors */ +- cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); +- cfg &= ~0x80; +- i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg); ++ err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, ++ ADT7470_T05_STB_MASK, 0); ++ if (err < 0) ++ return err; + + /* restore pwm[1-4] config registers */ +- i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); +- i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); +- +- if (res) { +- pr_err("ha ha, interrupted\n"); ++ err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); ++ if (err < 0) ++ return err; ++ err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); ++ if (err < 0) ++ return err; ++ ++ if (res) + return -EAGAIN; +- } + + /* Only count fans if we have to */ + if (data->num_temp_sensors >= 0) + return 0; + ++ err = regmap_bulk_read(data->regmap, ADT7470_TEMP_REG(0), &data->temp[0], ++ ADT7470_TEMP_COUNT); ++ if (err < 0) ++ return err; + for (i = 0; i < ADT7470_TEMP_COUNT; i++) { +- data->temp[i] = i2c_smbus_read_byte_data(client, +- ADT7470_TEMP_REG(i)); + if (data->temp[i]) + data->num_temp_sensors = i + 1; + } +@@ -259,7 +292,7 @@ static int adt7470_update_thread(void *p) + + while (!kthread_should_stop()) { + mutex_lock(&data->lock); +- adt7470_read_temperatures(client, data); ++ adt7470_read_temperatures(data); + mutex_unlock(&data->lock); + + if (kthread_should_stop()) +@@ -273,89 +306,116 @@ static int adt7470_update_thread(void *p) + + static int adt7470_update_sensors(struct adt7470_data *data) + { +- struct i2c_client *client = data->client; +- u8 cfg; ++ unsigned int val; ++ int err; + int i; + + if (!data->temperatures_probed) +- adt7470_read_temperatures(client, data); ++ err = adt7470_read_temperatures(data); + else +- for (i = 0; i < ADT7470_TEMP_COUNT; i++) +- data->temp[i] = i2c_smbus_read_byte_data(client, +- ADT7470_TEMP_REG(i)); ++ err = regmap_bulk_read(data->regmap, ADT7470_TEMP_REG(0), &data->temp[0], ++ ADT7470_TEMP_COUNT); ++ if (err < 0) ++ return err; + +- for (i = 0; i < ADT7470_FAN_COUNT; i++) +- data->fan[i] = adt7470_read_word_data(client, +- ADT7470_REG_FAN(i)); ++ for (i = 0; i < ADT7470_FAN_COUNT; i++) { ++ err = adt7470_read_word_data(data, ADT7470_REG_FAN(i), &val); ++ if (err < 0) ++ return err; ++ data->fan[i] = val; ++ } + +- for (i = 0; i < ADT7470_PWM_COUNT; i++) { +- int reg; +- int reg_mask; ++ err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &data->pwm[0], ADT7470_PWM_COUNT); ++ if (err < 0) ++ return err; + +- data->pwm[i] = i2c_smbus_read_byte_data(client, +- ADT7470_REG_PWM(i)); ++ for (i = 0; i < ADT7470_PWM_COUNT; i++) { ++ unsigned int mask; + + if (i % 2) +- reg_mask = ADT7470_PWM2_AUTO_MASK; ++ mask = ADT7470_PWM2_AUTO_MASK; + else +- reg_mask = ADT7470_PWM1_AUTO_MASK; ++ mask = ADT7470_PWM1_AUTO_MASK; + +- reg = ADT7470_REG_PWM_CFG(i); +- if (i2c_smbus_read_byte_data(client, reg) & reg_mask) +- data->pwm_automatic[i] = 1; +- else +- data->pwm_automatic[i] = 0; ++ err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(i), &val); ++ if (err < 0) ++ return err; ++ data->pwm_automatic[i] = !!(val & mask); + +- reg = ADT7470_REG_PWM_AUTO_TEMP(i); +- cfg = i2c_smbus_read_byte_data(client, reg); ++ err = regmap_read(data->regmap, ADT7470_REG_PWM_AUTO_TEMP(i), &val); ++ if (err < 0) ++ return err; + if (!(i % 2)) +- data->pwm_auto_temp[i] = cfg >> 4; ++ data->pwm_auto_temp[i] = val >> 4; + else +- data->pwm_auto_temp[i] = cfg & 0xF; ++ data->pwm_auto_temp[i] = val & 0xF; + } + +- if (i2c_smbus_read_byte_data(client, ADT7470_REG_CFG) & +- ADT7470_FSPD_MASK) +- data->force_pwm_max = 1; +- else +- data->force_pwm_max = 0; ++ err = regmap_read(data->regmap, ADT7470_REG_CFG, &val); ++ if (err < 0) ++ return err; ++ data->force_pwm_max = !!(val & ADT7470_FSPD_MASK); ++ ++ err = regmap_read(data->regmap, ADT7470_REG_ALARM1, &val); ++ if (err < 0) ++ return err; ++ data->alarm = val; ++ if (data->alarm & ADT7470_OOL_ALARM) { ++ err = regmap_read(data->regmap, ADT7470_REG_ALARM2, &val); ++ if (err < 0) ++ return err; ++ data->alarm |= ALARM2(val); ++ } + +- data->alarm = i2c_smbus_read_byte_data(client, ADT7470_REG_ALARM1); +- if (data->alarm & ADT7470_OOL_ALARM) +- data->alarm |= ALARM2(i2c_smbus_read_byte_data(client, +- ADT7470_REG_ALARM2)); +- data->alarms_mask = adt7470_read_word_data(client, +- ADT7470_REG_ALARM1_MASK); ++ err = adt7470_read_word_data(data, ADT7470_REG_ALARM1_MASK, &val); ++ if (err < 0) ++ return err; ++ data->alarms_mask = val; + + return 0; + } + + static int adt7470_update_limits(struct adt7470_data *data) + { +- struct i2c_client *client = data->client; ++ unsigned int val; ++ int err; + int i; + + for (i = 0; i < ADT7470_TEMP_COUNT; i++) { +- data->temp_min[i] = i2c_smbus_read_byte_data(client, +- ADT7470_TEMP_MIN_REG(i)); +- data->temp_max[i] = i2c_smbus_read_byte_data(client, +- ADT7470_TEMP_MAX_REG(i)); ++ err = regmap_read(data->regmap, ADT7470_TEMP_MIN_REG(i), &val); ++ if (err < 0) ++ return err; ++ data->temp_min[i] = (s8)val; ++ err = regmap_read(data->regmap, ADT7470_TEMP_MAX_REG(i), &val); ++ if (err < 0) ++ return err; ++ data->temp_max[i] = (s8)val; + } + + for (i = 0; i < ADT7470_FAN_COUNT; i++) { +- data->fan_min[i] = adt7470_read_word_data(client, +- ADT7470_REG_FAN_MIN(i)); +- data->fan_max[i] = adt7470_read_word_data(client, +- ADT7470_REG_FAN_MAX(i)); ++ err = adt7470_read_word_data(data, ADT7470_REG_FAN_MIN(i), &val); ++ if (err < 0) ++ return err; ++ data->fan_min[i] = val; ++ err = adt7470_read_word_data(data, ADT7470_REG_FAN_MAX(i), &val); ++ if (err < 0) ++ return err; ++ data->fan_max[i] = val; + } + + for (i = 0; i < ADT7470_PWM_COUNT; i++) { +- data->pwm_max[i] = i2c_smbus_read_byte_data(client, +- ADT7470_REG_PWM_MAX(i)); +- data->pwm_min[i] = i2c_smbus_read_byte_data(client, +- ADT7470_REG_PWM_MIN(i)); +- data->pwm_tmin[i] = i2c_smbus_read_byte_data(client, +- ADT7470_REG_PWM_TMIN(i)); ++ err = regmap_read(data->regmap, ADT7470_REG_PWM_MAX(i), &val); ++ if (err < 0) ++ return err; ++ data->pwm_max[i] = val; ++ err = regmap_read(data->regmap, ADT7470_REG_PWM_MIN(i), &val); ++ if (err < 0) ++ return err; ++ data->pwm_min[i] = val; ++ err = regmap_read(data->regmap, ADT7470_REG_PWM_TMIN(i), &val); ++ if (err < 0) ++ return err; ++ data->pwm_tmin[i] = (s8)val; + } + + return 0; +@@ -491,8 +551,8 @@ static ssize_t temp_min_store(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = dev_get_drvdata(dev); +- struct i2c_client *client = data->client; + long temp; ++ int err; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; +@@ -502,11 +562,11 @@ static ssize_t temp_min_store(struct device *dev, + + mutex_lock(&data->lock); + data->temp_min[attr->index] = temp; +- i2c_smbus_write_byte_data(client, ADT7470_TEMP_MIN_REG(attr->index), ++ err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(attr->index), + temp); + mutex_unlock(&data->lock); + +- return count; ++ return err < 0 ? err : count; + } + + static ssize_t temp_max_show(struct device *dev, +@@ -527,8 +587,8 @@ static ssize_t temp_max_store(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = dev_get_drvdata(dev); +- struct i2c_client *client = data->client; + long temp; ++ int err; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; +@@ -538,11 +598,10 @@ static ssize_t temp_max_store(struct device *dev, + + mutex_lock(&data->lock); + data->temp_max[attr->index] = temp; +- i2c_smbus_write_byte_data(client, ADT7470_TEMP_MAX_REG(attr->index), +- temp); ++ err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(attr->index), temp); + mutex_unlock(&data->lock); + +- return count; ++ return err < 0 ? err : count; + } + + static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, +@@ -575,6 +634,7 @@ static ssize_t alarm_mask_store(struct device *dev, + { + struct adt7470_data *data = dev_get_drvdata(dev); + long mask; ++ int err; + + if (kstrtoul(buf, 0, &mask)) + return -EINVAL; +@@ -584,10 +644,10 @@ static ssize_t alarm_mask_store(struct device *dev, + + mutex_lock(&data->lock); + data->alarms_mask = mask; +- adt7470_write_word_data(data->client, ADT7470_REG_ALARM1_MASK, mask); ++ err = adt7470_write_word_data(data, ADT7470_REG_ALARM1_MASK, mask); + mutex_unlock(&data->lock); + +- return count; ++ return err < 0 ? err : count; + } + + static ssize_t fan_max_show(struct device *dev, +@@ -612,8 +672,8 @@ static ssize_t fan_max_store(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = dev_get_drvdata(dev); +- struct i2c_client *client = data->client; + long temp; ++ int err; + + if (kstrtol(buf, 10, &temp) || !temp) + return -EINVAL; +@@ -623,10 +683,10 @@ static ssize_t fan_max_store(struct device *dev, + + mutex_lock(&data->lock); + data->fan_max[attr->index] = temp; +- adt7470_write_word_data(client, ADT7470_REG_FAN_MAX(attr->index), temp); ++ err = adt7470_write_word_data(data, ADT7470_REG_FAN_MAX(attr->index), temp); + mutex_unlock(&data->lock); + +- return count; ++ return err < 0 ? err : count; + } + + static ssize_t fan_min_show(struct device *dev, +@@ -651,8 +711,8 @@ static ssize_t fan_min_store(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = dev_get_drvdata(dev); +- struct i2c_client *client = data->client; + long temp; ++ int err; + + if (kstrtol(buf, 10, &temp) || !temp) + return -EINVAL; +@@ -662,10 +722,10 @@ static ssize_t fan_min_store(struct device *dev, + + mutex_lock(&data->lock); + data->fan_min[attr->index] = temp; +- adt7470_write_word_data(client, ADT7470_REG_FAN_MIN(attr->index), temp); ++ err = adt7470_write_word_data(data, ADT7470_REG_FAN_MIN(attr->index), temp); + mutex_unlock(&data->lock); + +- return count; ++ return err < 0 ? err : count; + } + + static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, +@@ -700,24 +760,20 @@ static ssize_t force_pwm_max_store(struct device *dev, + const char *buf, size_t count) + { + struct adt7470_data *data = dev_get_drvdata(dev); +- struct i2c_client *client = data->client; + long temp; +- u8 reg; ++ int err; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + + mutex_lock(&data->lock); + data->force_pwm_max = temp; +- reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); +- if (temp) +- reg |= ADT7470_FSPD_MASK; +- else +- reg &= ~ADT7470_FSPD_MASK; +- i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg); ++ err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, ++ ADT7470_FSPD_MASK, ++ temp ? ADT7470_FSPD_MASK : 0); + mutex_unlock(&data->lock); + +- return count; ++ return err < 0 ? err : count; + } + + static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr, +@@ -737,8 +793,8 @@ static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = dev_get_drvdata(dev); +- struct i2c_client *client = data->client; + long temp; ++ int err; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; +@@ -747,10 +803,10 @@ static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr, + + mutex_lock(&data->lock); + data->pwm[attr->index] = temp; +- i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(attr->index), temp); ++ err = regmap_write(data->regmap, ADT7470_REG_PWM(attr->index), temp); + mutex_unlock(&data->lock); + +- return count; ++ return err < 0 ? err : count; + } + + /* These are the valid PWM frequencies to the nearest Hz */ +@@ -762,13 +818,20 @@ static ssize_t pwm1_freq_show(struct device *dev, + struct device_attribute *devattr, char *buf) + { + struct adt7470_data *data = adt7470_update_device(dev); +- unsigned char cfg_reg_1; +- unsigned char cfg_reg_2; ++ unsigned int cfg_reg_1, cfg_reg_2; + int index; ++ int err; ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); + + mutex_lock(&data->lock); +- cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG); +- cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2); ++ err = regmap_read(data->regmap, ADT7470_REG_CFG, &cfg_reg_1); ++ if (err < 0) ++ goto out; ++ err = regmap_read(data->regmap, ADT7470_REG_CFG_2, &cfg_reg_2); ++ if (err < 0) ++ goto out; + mutex_unlock(&data->lock); + + index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; +@@ -778,6 +841,10 @@ static ssize_t pwm1_freq_show(struct device *dev, + index = ARRAY_SIZE(adt7470_freq_map) - 1; + + return scnprintf(buf, PAGE_SIZE, "%d\n", adt7470_freq_map[index]); ++ ++out: ++ mutex_unlock(&data->lock); ++ return err; + } + + static ssize_t pwm1_freq_store(struct device *dev, +@@ -785,11 +852,10 @@ static ssize_t pwm1_freq_store(struct device *dev, + const char *buf, size_t count) + { + struct adt7470_data *data = dev_get_drvdata(dev); +- struct i2c_client *client = data->client; + long freq; + int index; + int low_freq = ADT7470_CFG_LF; +- unsigned char val; ++ int err; + + if (kstrtol(buf, 10, &freq)) + return -EINVAL; +@@ -805,16 +871,19 @@ static ssize_t pwm1_freq_store(struct device *dev, + + mutex_lock(&data->lock); + /* Configuration Register 1 */ +- val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); +- i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, +- (val & ~ADT7470_CFG_LF) | low_freq); ++ err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, ++ ADT7470_CFG_LF, low_freq); ++ if (err < 0) ++ goto out; ++ + /* Configuration Register 2 */ +- val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2); +- i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2, +- (val & ~ADT7470_FREQ_MASK) | (index << ADT7470_FREQ_SHIFT)); ++ err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, ++ ADT7470_FREQ_MASK, ++ index << ADT7470_FREQ_SHIFT); ++out: + mutex_unlock(&data->lock); + +- return count; ++ return err < 0 ? err : count; + } + + static ssize_t pwm_max_show(struct device *dev, +@@ -835,8 +904,8 @@ static ssize_t pwm_max_store(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = dev_get_drvdata(dev); +- struct i2c_client *client = data->client; + long temp; ++ int err; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; +@@ -845,11 +914,11 @@ static ssize_t pwm_max_store(struct device *dev, + + mutex_lock(&data->lock); + data->pwm_max[attr->index] = temp; +- i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MAX(attr->index), +- temp); ++ err = regmap_write(data->regmap, ADT7470_REG_PWM_MAX(attr->index), ++ temp); + mutex_unlock(&data->lock); + +- return count; ++ return err < 0 ? err : count; + } + + static ssize_t pwm_min_show(struct device *dev, +@@ -870,8 +939,8 @@ static ssize_t pwm_min_store(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = dev_get_drvdata(dev); +- struct i2c_client *client = data->client; + long temp; ++ int err; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; +@@ -880,11 +949,11 @@ static ssize_t pwm_min_store(struct device *dev, + + mutex_lock(&data->lock); + data->pwm_min[attr->index] = temp; +- i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MIN(attr->index), +- temp); ++ err = regmap_write(data->regmap, ADT7470_REG_PWM_MIN(attr->index), ++ temp); + mutex_unlock(&data->lock); + +- return count; ++ return err < 0 ? err : count; + } + + static ssize_t pwm_tmax_show(struct device *dev, +@@ -918,8 +987,8 @@ static ssize_t pwm_tmin_store(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = dev_get_drvdata(dev); +- struct i2c_client *client = data->client; + long temp; ++ int err; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; +@@ -929,11 +998,11 @@ static ssize_t pwm_tmin_store(struct device *dev, + + mutex_lock(&data->lock); + data->pwm_tmin[attr->index] = temp; +- i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_TMIN(attr->index), +- temp); ++ err = regmap_write(data->regmap, ADT7470_REG_PWM_TMIN(attr->index), ++ temp); + mutex_unlock(&data->lock); + +- return count; ++ return err < 0 ? err : count; + } + + static ssize_t pwm_auto_show(struct device *dev, +@@ -954,11 +1023,9 @@ static ssize_t pwm_auto_store(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = dev_get_drvdata(dev); +- struct i2c_client *client = data->client; +- int pwm_auto_reg = ADT7470_REG_PWM_CFG(attr->index); + int pwm_auto_reg_mask; + long temp; +- u8 reg; ++ int err; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; +@@ -974,15 +1041,12 @@ static ssize_t pwm_auto_store(struct device *dev, + + mutex_lock(&data->lock); + data->pwm_automatic[attr->index] = temp; +- reg = i2c_smbus_read_byte_data(client, pwm_auto_reg); +- if (temp) +- reg |= pwm_auto_reg_mask; +- else +- reg &= ~pwm_auto_reg_mask; +- i2c_smbus_write_byte_data(client, pwm_auto_reg, reg); ++ err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(attr->index), ++ pwm_auto_reg_mask, ++ temp ? pwm_auto_reg_mask : 0); + mutex_unlock(&data->lock); + +- return count; ++ return err < 0 ? err : count; + } + + static ssize_t pwm_auto_temp_show(struct device *dev, +@@ -1017,10 +1081,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = dev_get_drvdata(dev); +- struct i2c_client *client = data->client; + int pwm_auto_reg = ADT7470_REG_PWM_AUTO_TEMP(attr->index); ++ unsigned int mask, val; + long temp; +- u8 reg; ++ int err; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; +@@ -1031,20 +1095,19 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + + mutex_lock(&data->lock); + data->pwm_automatic[attr->index] = temp; +- reg = i2c_smbus_read_byte_data(client, pwm_auto_reg); + + if (!(attr->index % 2)) { +- reg &= 0xF; +- reg |= (temp << 4) & 0xF0; ++ mask = 0xF0; ++ val = (temp << 4) & 0xF0; + } else { +- reg &= 0xF0; +- reg |= temp & 0xF; ++ mask = 0x0F; ++ val = temp & 0x0F; + } + +- i2c_smbus_write_byte_data(client, pwm_auto_reg, reg); ++ err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); + mutex_unlock(&data->lock); + +- return count; ++ return err < 0 ? err : count; + } + + static ssize_t alarm_show(struct device *dev, +@@ -1053,6 +1116,9 @@ static ssize_t alarm_show(struct device *dev, + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + if (data->alarm & attr->index) + return sprintf(buf, "1\n"); + else +@@ -1288,23 +1354,19 @@ static int adt7470_detect(struct i2c_client *client, + return 0; + } + +-static void adt7470_init_client(struct i2c_client *client) +-{ +- int reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); +- +- if (reg < 0) { +- dev_err(&client->dev, "cannot read configuration register\n"); +- } else { +- /* start monitoring (and do a self-test) */ +- i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg | 3); +- } +-} ++static const struct regmap_config adt7470_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .use_single_read = true, ++ .use_single_write = true, ++}; + + static int adt7470_probe(struct i2c_client *client) + { + struct device *dev = &client->dev; + struct adt7470_data *data; + struct device *hwmon_dev; ++ int err; + + data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); + if (!data) +@@ -1312,15 +1374,21 @@ static int adt7470_probe(struct i2c_client *client) + + data->num_temp_sensors = -1; + data->auto_update_interval = AUTO_UPDATE_INTERVAL; ++ data->regmap = devm_regmap_init_i2c(client, &adt7470_regmap_config); ++ if (IS_ERR(data->regmap)) ++ return PTR_ERR(data->regmap); + + i2c_set_clientdata(client, data); +- data->client = client; + mutex_init(&data->lock); + + dev_info(&client->dev, "%s chip found\n", client->name); + + /* Initialize the ADT7470 chip */ +- adt7470_init_client(client); ++ err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, ++ ADT7470_STRT_MASK | ADT7470_TEST_MASK, ++ ADT7470_STRT_MASK | ADT7470_TEST_MASK); ++ if (err < 0) ++ return err; + + /* Register sysfs hooks */ + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, +-- +2.53.0 + diff --git a/queue-5.10/hwmon-adt7470-create-functions-for-updating-readings.patch b/queue-5.10/hwmon-adt7470-create-functions-for-updating-readings.patch new file mode 100644 index 0000000000..ce3fd0f4f7 --- /dev/null +++ b/queue-5.10/hwmon-adt7470-create-functions-for-updating-readings.patch @@ -0,0 +1,333 @@ +From 12f0b6dc7d732afd9f7b6e4570fe31bcfd6423d9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 20 Oct 2020 11:34:22 +1300 +Subject: hwmon: (adt7470) Create functions for updating readings and limits + +From: Chris Packham + +[ Upstream commit ad00a02e34b481396938c5fa62ee642bff7fbb08 ] + +Split the body of adt7470_update_device() into two helper functions +adt7470_update_sensors() and adt7470_update_limits(). Although neither +of the new helpers returns an error yet lay the groundwork for +propagating failures through to the sysfs readers. + +Signed-off-by: Chris Packham +Link: https://lore.kernel.org/r/20201019223423.31488-2-chris.packham@alliedtelesis.co.nz +Signed-off-by: Guenter Roeck +Stable-dep-of: 92413f439d1e ("hwmon: (adt7470) Fix PWM auto temp state array and bounds check") +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 154 ++++++++++++++++++++++++++++++---------- + 1 file changed, 118 insertions(+), 36 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 23a447c1f0cf0..b8697975d8b6c 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -270,37 +270,11 @@ static int adt7470_update_thread(void *p) + return 0; + } + +-static struct adt7470_data *adt7470_update_device(struct device *dev) ++static int adt7470_update_sensors(struct adt7470_data *data) + { +- struct adt7470_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; +- unsigned long local_jiffies = jiffies; + u8 cfg; + int i; +- int need_sensors = 1; +- int need_limits = 1; +- +- /* +- * Figure out if we need to update the shadow registers. +- * Lockless means that we may occasionally report out of +- * date data. +- */ +- if (time_before(local_jiffies, data->sensors_last_updated + +- SENSOR_REFRESH_INTERVAL) && +- data->sensors_valid) +- need_sensors = 0; +- +- if (time_before(local_jiffies, data->limits_last_updated + +- LIMIT_REFRESH_INTERVAL) && +- data->limits_valid) +- need_limits = 0; +- +- if (!need_sensors && !need_limits) +- return data; +- +- mutex_lock(&data->lock); +- if (!need_sensors) +- goto no_sensor_update; + + if (!data->temperatures_probed) + adt7470_read_temperatures(client, data); +@@ -352,12 +326,13 @@ static struct adt7470_data *adt7470_update_device(struct device *dev) + data->alarms_mask = adt7470_read_word_data(client, + ADT7470_REG_ALARM1_MASK); + +- data->sensors_last_updated = local_jiffies; +- data->sensors_valid = 1; ++ return 0; ++} + +-no_sensor_update: +- if (!need_limits) +- goto out; ++static int adt7470_update_limits(struct adt7470_data *data) ++{ ++ struct i2c_client *client = data->client; ++ int i; + + for (i = 0; i < ADT7470_TEMP_COUNT; i++) { + data->temp_min[i] = i2c_smbus_read_byte_data(client, +@@ -382,12 +357,55 @@ static struct adt7470_data *adt7470_update_device(struct device *dev) + ADT7470_REG_PWM_TMIN(i)); + } + +- data->limits_last_updated = local_jiffies; +- data->limits_valid = 1; ++ return 0; ++} + ++static struct adt7470_data *adt7470_update_device(struct device *dev) ++{ ++ struct adt7470_data *data = dev_get_drvdata(dev); ++ unsigned long local_jiffies = jiffies; ++ int need_sensors = 1; ++ int need_limits = 1; ++ int err; ++ ++ /* ++ * Figure out if we need to update the shadow registers. ++ * Lockless means that we may occasionally report out of ++ * date data. ++ */ ++ if (time_before(local_jiffies, data->sensors_last_updated + ++ SENSOR_REFRESH_INTERVAL) && ++ data->sensors_valid) ++ need_sensors = 0; ++ ++ if (time_before(local_jiffies, data->limits_last_updated + ++ LIMIT_REFRESH_INTERVAL) && ++ data->limits_valid) ++ need_limits = 0; ++ ++ if (!need_sensors && !need_limits) ++ return data; ++ ++ mutex_lock(&data->lock); ++ if (need_sensors) { ++ err = adt7470_update_sensors(data); ++ if (err < 0) ++ goto out; ++ data->sensors_last_updated = local_jiffies; ++ data->sensors_valid = 1; ++ } ++ ++ if (need_limits) { ++ err = adt7470_update_limits(data); ++ if (err < 0) ++ goto out; ++ data->limits_last_updated = local_jiffies; ++ data->limits_valid = 1; ++ } + out: + mutex_unlock(&data->lock); +- return data; ++ ++ return err < 0 ? ERR_PTR(err) : data; + } + + static ssize_t auto_update_interval_show(struct device *dev, +@@ -395,6 +413,10 @@ static ssize_t auto_update_interval_show(struct device *dev, + char *buf) + { + struct adt7470_data *data = adt7470_update_device(dev); ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + return sprintf(buf, "%d\n", data->auto_update_interval); + } + +@@ -422,6 +444,10 @@ static ssize_t num_temp_sensors_show(struct device *dev, + char *buf) + { + struct adt7470_data *data = adt7470_update_device(dev); ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + return sprintf(buf, "%d\n", data->num_temp_sensors); + } + +@@ -451,6 +477,10 @@ static ssize_t temp_min_show(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + return sprintf(buf, "%d\n", 1000 * data->temp_min[attr->index]); + } + +@@ -483,6 +513,10 @@ static ssize_t temp_max_show(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + return sprintf(buf, "%d\n", 1000 * data->temp_max[attr->index]); + } + +@@ -515,6 +549,10 @@ static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + return sprintf(buf, "%d\n", 1000 * data->temp[attr->index]); + } + +@@ -524,6 +562,9 @@ static ssize_t alarm_mask_show(struct device *dev, + { + struct adt7470_data *data = adt7470_update_device(dev); + ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + return sprintf(buf, "%x\n", data->alarms_mask); + } + +@@ -554,6 +595,9 @@ static ssize_t fan_max_show(struct device *dev, + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + if (FAN_DATA_VALID(data->fan_max[attr->index])) + return sprintf(buf, "%d\n", + FAN_PERIOD_TO_RPM(data->fan_max[attr->index])); +@@ -590,6 +634,9 @@ static ssize_t fan_min_show(struct device *dev, + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + if (FAN_DATA_VALID(data->fan_min[attr->index])) + return sprintf(buf, "%d\n", + FAN_PERIOD_TO_RPM(data->fan_min[attr->index])); +@@ -626,6 +673,9 @@ static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); + ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + if (FAN_DATA_VALID(data->fan[attr->index])) + return sprintf(buf, "%d\n", + FAN_PERIOD_TO_RPM(data->fan[attr->index])); +@@ -637,6 +687,10 @@ static ssize_t force_pwm_max_show(struct device *dev, + struct device_attribute *devattr, char *buf) + { + struct adt7470_data *data = adt7470_update_device(dev); ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + return sprintf(buf, "%d\n", data->force_pwm_max); + } + +@@ -670,6 +724,10 @@ static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + return sprintf(buf, "%d\n", data->pwm[attr->index]); + } + +@@ -763,6 +821,10 @@ static ssize_t pwm_max_show(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + return sprintf(buf, "%d\n", data->pwm_max[attr->index]); + } + +@@ -794,6 +856,10 @@ static ssize_t pwm_min_show(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + return sprintf(buf, "%d\n", data->pwm_min[attr->index]); + } + +@@ -825,6 +891,10 @@ static ssize_t pwm_tmax_show(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + /* the datasheet says that tmax = tmin + 20C */ + return sprintf(buf, "%d\n", 1000 * (20 + data->pwm_tmin[attr->index])); + } +@@ -834,6 +904,10 @@ static ssize_t pwm_tmin_show(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + return sprintf(buf, "%d\n", 1000 * data->pwm_tmin[attr->index]); + } + +@@ -866,6 +940,10 @@ static ssize_t pwm_auto_show(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ + return sprintf(buf, "%d\n", 1 + data->pwm_automatic[attr->index]); + } + +@@ -911,8 +989,12 @@ static ssize_t pwm_auto_temp_show(struct device *dev, + { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7470_data *data = adt7470_update_device(dev); +- u8 ctrl = data->pwm_auto_temp[attr->index]; ++ u8 ctrl; ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); + ++ ctrl = data->pwm_auto_temp[attr->index]; + if (ctrl) + return sprintf(buf, "%d\n", 1 << (ctrl - 1)); + else +-- +2.53.0 + diff --git a/queue-5.10/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-5.10/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch new file mode 100644 index 0000000000..a9c9277783 --- /dev/null +++ b/queue-5.10/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch @@ -0,0 +1,51 @@ +From addfcd4619cb8eb128cee5d4f3f4bf95a875ed98 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:19 -0300 +Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread + +From: Luiz Angelo Daros de Luca + +[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ] + +When userspace configures 'auto_update_interval' to 0 via sysfs, the +background kthread executes schedule_timeout_interruptible(0), which +returns immediately. + +If 'num_temp_sensors' is concurrently or previously set to 0, the +msleep_interruptible() delay inside adt7470_read_temperatures() also +becomes 0. This combination forces the background thread into a tight, +unbounded busy-loop, hogging the CPU and flooding the I2C bus with a +continuous stream of transactions. + +Fix this vulnerability by raising the lower limit of the clamp_val in +auto_update_interval_store() from 0 to 500 milliseconds. This guarantees +a reasonable minimum sleep window between sensor updates, protecting the +system from intentional or accidental I2C bus denial of service. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org +Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 71e357956ce4c..8450737dd4bde 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -408,7 +408,7 @@ static ssize_t auto_update_interval_store(struct device *dev, + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + +- temp = clamp_val(temp, 0, 60000); ++ temp = clamp_val(temp, 500, 60000); + + mutex_lock(&data->lock); + data->auto_update_interval = temp; +-- +2.53.0 + diff --git a/queue-5.10/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-5.10/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch new file mode 100644 index 0000000000..38b4bb4a1e --- /dev/null +++ b/queue-5.10/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch @@ -0,0 +1,57 @@ +From cbc8ebee5894b413583fdb7ab18f8f86fcfcea7b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:24 -0300 +Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ] + +In pwm_auto_temp_store(), the parsed user input was missing bounds +checks, allowing values > 0xF to overflow into the adjacent channel's +bits. Furthermore, the value was being incorrectly written to the +pwm_automatic state array instead of pwm_auto_temp. + +Fix this by rejecting values > 0xF with -EINVAL, and assigning the +value to the correct array only after a successful I2C write. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 274d38a9ba589..c8f6b9af0f139 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -1093,8 +1093,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + if (temp < 0) + return temp; + ++ if (temp > 0xF) ++ return -EINVAL; ++ + mutex_lock(&data->lock); +- data->pwm_automatic[attr->index] = temp; + + if (!(attr->index % 2)) { + mask = 0xF0; +@@ -1105,6 +1107,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + } + + err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); ++ if (!err) ++ data->pwm_auto_temp[attr->index] = temp; ++ + mutex_unlock(&data->lock); + + return err < 0 ? err : count; +-- +2.53.0 + diff --git a/queue-5.10/hwmon-adt7470-fix-some-style-issues.patch b/queue-5.10/hwmon-adt7470-fix-some-style-issues.patch new file mode 100644 index 0000000000..4780985e27 --- /dev/null +++ b/queue-5.10/hwmon-adt7470-fix-some-style-issues.patch @@ -0,0 +1,60 @@ +From 61ea7b201743520391ebebfb2e420b7e822d4104 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Aug 2021 14:41:18 +1200 +Subject: hwmon: (adt7470) Fix some style issues + +From: Chris Packham + +[ Upstream commit 23bd022aa6182367e1add7b4d05777cdba283756 ] + +In preparation for the changes that follow fix up some existing style +issues. +Specifically: +- add blank line between variable declaration and code +- use strscpy instead of strlcpy +- remove unnecessary braces + +Signed-off-by: Chris Packham +Link: https://lore.kernel.org/r/20210826024121.15665-2-chris.packham@alliedtelesis.co.nz +Signed-off-by: Guenter Roeck +Stable-dep-of: 92413f439d1e ("hwmon: (adt7470) Fix PWM auto temp state array and bounds check") +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index b8697975d8b6c..95c5e219adeff 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -175,6 +175,7 @@ struct adt7470_data { + static inline int adt7470_read_word_data(struct i2c_client *client, u8 reg) + { + u16 foo; ++ + foo = i2c_smbus_read_byte_data(client, reg); + foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8); + return foo; +@@ -1282,7 +1283,7 @@ static int adt7470_detect(struct i2c_client *client, + if (revision != ADT7470_REVISION) + return -ENODEV; + +- strlcpy(info->type, "adt7470", I2C_NAME_SIZE); ++ strscpy(info->type, "adt7470", I2C_NAME_SIZE); + + return 0; + } +@@ -1331,9 +1332,8 @@ static int adt7470_probe(struct i2c_client *client) + + data->auto_update = kthread_run(adt7470_update_thread, client, "%s", + dev_name(hwmon_dev)); +- if (IS_ERR(data->auto_update)) { ++ if (IS_ERR(data->auto_update)) + return PTR_ERR(data->auto_update); +- } + + return 0; + } +-- +2.53.0 + diff --git a/queue-5.10/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-5.10/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch new file mode 100644 index 0000000000..a66e4598e0 --- /dev/null +++ b/queue-5.10/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch @@ -0,0 +1,62 @@ +From 77f9c298cedb2037a7139f64841c847c9fc5340d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:20 -0300 +Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks + +From: Luiz Angelo Daros de Luca + +[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ] + +The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are +currently defined with swapped bit values. + +According to Table 22 of the ADT7470 datasheet, the Fan Control Mode +Configuration for register 0x69 follows the exact same bit position +layout as register 0x68: +- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80 +- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40 +- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80 +- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40 + +Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40. + +This typo did not cause any functional bugs because these specific +macros are never referenced in the driver code. Instead, the driver +correctly applies the configuration by relying on the modulo parity of +the channel index (e.g., `channel % 2`) to selectively apply either +ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40). +Since the bit layout is identical between the two configuration +registers, the hardware is currently configured correctly. + +Fix the macro definitions to reflect the datasheet accurately and +prevent future bugs or confusion during code review and refactoring. +As this is a purely cosmetic fix with no functional impact, a backport +to stable kernels is not necessary. + +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 8450737dd4bde..23a447c1f0cf0 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -66,8 +66,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + #define ADT7470_PWM1_AUTO_MASK 0x80 + #define ADT7470_PWM_AUTO_MASK 0xC0 + #define ADT7470_REG_PWM34_CFG 0x69 +-#define ADT7470_PWM3_AUTO_MASK 0x40 +-#define ADT7470_PWM4_AUTO_MASK 0x80 ++#define ADT7470_PWM4_AUTO_MASK 0x40 ++#define ADT7470_PWM3_AUTO_MASK 0x80 + #define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A + #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D + #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E +-- +2.53.0 + diff --git a/queue-5.10/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-5.10/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch new file mode 100644 index 0000000000..4da0acf0cf --- /dev/null +++ b/queue-5.10/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch @@ -0,0 +1,74 @@ +From 4165624dc46d0e8573d210b58f79a5e4a44cc43c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 13:35:37 -0700 +Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guenter Roeck + +[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ] + +Sashiko reports: + +During initialization of the nct6116 chip, the driver sets data->pwm_num +to 5. However, it assigns several NCT6106 register arrays (such as +NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and +NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP. +These arrays only contain 3 elements. + +In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If +data->has_pwm has bits 3 or 4 set (which is structurally possible for +nct6116), the loop attempts to read elements at index 3 and 4 from these +3-element arrays. This results in a global out-of-bounds read, which can +be caught by KASAN. + +Furthermore, the driver uses these garbage out-of-bounds values as +hardware register addresses for subsequent read and write operations. This +leads to invalid hardware register access, potentially causing hardware +misconfiguration or system crashes. + +The underlying problem is that the chip does support up to five fan +control channels, but only the first three support weight control. +Fix the problem by extending the affected weight register arrays with +zeroed fields. The driver uses zeroed register addresses to determine +if a register is supported or not, and skips accesses for unsupported +registers. + +Reported-by: Sashiko +Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") +Cc: Björn Gerhart +Cc: Florian Bezdeka +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c +index 71cfc1c5bd12e..5428b5b4043da 100644 +--- a/drivers/hwmon/nct6775.c ++++ b/drivers/hwmon/nct6775.c +@@ -851,12 +851,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 }; + + static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 }; + +-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 }; + + static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 }; + static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 }; +-- +2.53.0 + diff --git a/queue-5.10/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-5.10/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch new file mode 100644 index 0000000000..f76948c5a4 --- /dev/null +++ b/queue-5.10/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch @@ -0,0 +1,39 @@ +From fc52a806f4f5ff6392f7baaae82053db9284b82c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 08:41:40 -0700 +Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data() + +From: Guenter Roeck + +[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ] + +pmbus_update_byte_data() is supposed to return a negative error code or 0. +However, if no change is made to the register, it actually returns the +register value. This can result in problems if the calling code explicitly +expects to see an error code or 0. + +Fix it to return 0 on success or the error code as expected. + +Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/pmbus/pmbus_core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c +index e90ab980b836f..ddd8399a7feea 100644 +--- a/drivers/hwmon/pmbus/pmbus_core.c ++++ b/drivers/hwmon/pmbus/pmbus_core.c +@@ -408,7 +408,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + if (tmp != rv) + rv = _pmbus_write_byte_data(client, page, reg, tmp); + +- return rv; ++ return rv < 0 ? rv : 0; + } + EXPORT_SYMBOL_GPL(pmbus_update_byte_data); + +-- +2.53.0 + diff --git a/queue-5.10/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-5.10/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch new file mode 100644 index 0000000000..6b6680ca7b --- /dev/null +++ b/queue-5.10/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch @@ -0,0 +1,64 @@ +From 6f0d805c38b085095225fb879ae394b417a69afa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:03 -0400 +Subject: keys: fix out-of-bounds read in keyring_get_key_chunk() + +From: Michael Bommarito + +[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ] + +For description-level chunks keyring_get_key_chunk() advances the read +pointer by level * sizeof(long) past the inline prefix but only +bounds-checks the prefix, so a long enough key description is read past +its kmemdup(desc, desc_len + 1) allocation. Compute the full byte +offset and bounds-check the description against it before reading. + +The walk only reaches a description-level chunk when two keys collide +through the hash, x, type and domain_tag chunks, so this is reached from +an unprivileged add_key(2) with a crafted pair of same-type keys whose +index hashes collide; KASAN reports a slab-out-of-bounds read. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index 3f609316a096e..165ac0d38bcfc 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + unsigned long chunk = 0; + const u8 *d; + int desc_len = index_key->desc_len, n = sizeof(chunk); ++ unsigned int offset; + + level /= ASSOC_ARRAY_KEY_CHUNK_SIZE; + switch (level) { +@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + return (unsigned long)index_key->domain_tag; + default: + level -= 4; +- if (desc_len <= sizeof(index_key->desc)) ++ offset = sizeof(index_key->desc) + level * sizeof(long); ++ if (desc_len <= offset) + return 0; + +- d = index_key->description + sizeof(index_key->desc); +- d += level * sizeof(long); +- desc_len -= sizeof(index_key->desc); ++ d = index_key->description + offset; ++ desc_len -= offset; + if (desc_len > n) + desc_len = n; + do { +-- +2.53.0 + diff --git a/queue-5.10/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-5.10/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch new file mode 100644 index 0000000000..62eb82c0ad --- /dev/null +++ b/queue-5.10/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch @@ -0,0 +1,63 @@ +From d3aad9586cfc1a34b9ac977be4476efeed9577b4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:04 -0400 +Subject: keys: make keyring key-chunk byte order agree with + keyring_diff_objects() + +From: Michael Bommarito + +[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ] + +keyring_get_key_chunk() loads description bytes into the index chunk low +address first, while keyring_diff_objects() numbers the first differing +bit from the low end and folds the absolute byte index into the level +without removing the inline-prefix offset the level already carries. +The two disagree on byte order and bit position, so the array can be +told two keys first differ at a bit that does not differ in the chunk +the walker uses, letting crafted descriptions collide into one node. + +Load the chunk in the order keyring_diff_objects() assumes and drop the +inline-prefix length when folding the byte index into the level. This +only changes the in-memory ordering used to place keys within a keyring; +add, search and read of non-colliding keys are unaffected. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index 165ac0d38bcfc..8b938e61ecd8d 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + desc_len -= offset; + if (desc_len > n) + desc_len = n; ++ d += desc_len; + do { + chunk <<= 8; +- chunk |= *d++; ++ chunk |= *--d; + } while (--desc_len > 0); + return chunk; + } +@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data) + return -1; + + differ_plus_i: +- level += i; ++ level += i - (int)sizeof(a->desc); + differ: + i = level * 8 + __ffs(seg_a ^ seg_b); + return i; +-- +2.53.0 + diff --git a/queue-5.10/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-5.10/net-phylink-put-link_gpio-if-phylink_create-fails.patch new file mode 100644 index 0000000000..b4a6061997 --- /dev/null +++ b/queue-5.10/net-phylink-put-link_gpio-if-phylink_create-fails.patch @@ -0,0 +1,92 @@ +From 16e0b628990a88d35eb7346bc54b05b323ff445d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 17:08:05 +0200 +Subject: net: phylink: put link_gpio if phylink_create fails + +From: Christian Marangi + +[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ] + +In phylink_create() if phylink_register_sfp() returns an error, link_gpio +obtained by phylink_parse_fixedlink() is never released. While this is a +very unlikely scenario, it's worth to fix/handle this. + +This was present from the very first implementation of phylink but got +relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to +bridge between network devices and sfp cages") where additional function +were added after phylink_parse_fixedlink() making the release of link_gpio +needed if such additional function errored out. + +While at it, restructure the exit condition of phylink_create() with the +goto pattern to reduce code duplication on handling error conditions. + +Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages") +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/phy/phylink.c | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index cbdd01f311625..012c750d49874 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -842,8 +842,8 @@ struct phylink *phylink_create(struct phylink_config *config, + } else if (config->type == PHYLINK_DEV) { + pl->dev = config->dev; + } else { +- kfree(pl); +- return ERR_PTR(-EINVAL); ++ ret = -EINVAL; ++ goto free_pl; + } + + pl->phy_state.interface = iface; +@@ -866,28 +866,29 @@ struct phylink *phylink_create(struct phylink_config *config, + phylink_validate(pl, pl->supported, &pl->link_config); + + ret = phylink_parse_mode(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto free_pl; + + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { + ret = phylink_parse_fixedlink(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + } + + pl->cur_link_an_mode = pl->cfg_link_an_mode; + + ret = phylink_register_sfp(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + + return pl; ++ ++release_link_gpio: ++ if (pl->link_gpio) ++ gpiod_put(pl->link_gpio); ++free_pl: ++ kfree(pl); ++ return ERR_PTR(ret); + } + EXPORT_SYMBOL_GPL(phylink_create); + +-- +2.53.0 + diff --git a/queue-5.10/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-5.10/net-sxgbe-check-descriptor-ring-allocation-failures.patch new file mode 100644 index 0000000000..0e1ea8179f --- /dev/null +++ b/queue-5.10/net-sxgbe-check-descriptor-ring-allocation-failures.patch @@ -0,0 +1,48 @@ +From cb06d222dcac8d9a8cc143b29a5c4df9a8748513 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:20 +0800 +Subject: net: sxgbe: check descriptor ring allocation failures + +From: Chenguang Zhao + +[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ] + +sxgbe_open() ignores the return value of init_dma_desc_rings() and +continues to program DMA with invalid ring addresses when allocation +fails. Check the return value and disconnect the PHY on failure. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index 22405744b2a36..c2333b8224c61 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -1077,7 +1077,9 @@ static int sxgbe_open(struct net_device *dev) + priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE); + priv->tx_tc = TC_DEFAULT; + priv->rx_tc = TC_DEFAULT; +- init_dma_desc_rings(dev); ++ ret = init_dma_desc_rings(dev); ++ if (ret) ++ goto init_phy_error; + + /* DMA initialization and SW reset */ + ret = sxgbe_init_dma_engine(priv); +@@ -1186,6 +1188,7 @@ static int sxgbe_open(struct net_device *dev) + + init_error: + free_dma_desc_resources(priv); ++init_phy_error: + if (dev->phydev) + phy_disconnect(dev->phydev); + phy_error: +-- +2.53.0 + diff --git a/queue-5.10/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-5.10/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch new file mode 100644 index 0000000000..a1181e8612 --- /dev/null +++ b/queue-5.10/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch @@ -0,0 +1,50 @@ +From 322a6f59d892d532399635617039790d41be6fc3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:19 +0800 +Subject: net: sxgbe: free TX rings on RX allocation failure + +From: Chenguang Zhao + +[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ] + +When RX descriptor ring allocation fails, init_dma_desc_rings() only +frees the partially allocated RX rings and returns. The TX rings that +were allocated earlier in the same function are leaked. + +Rearrange error labels to clean up TX rings upon RX failures. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index 9c745d48f54b0..22405744b2a36 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -599,14 +599,13 @@ static int init_dma_desc_rings(struct net_device *netd) + + return 0; + +-txalloc_err: +- while (queue_num--) +- free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); +- return ret; +- + rxalloc_err: + while (queue_num--) + free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize); ++ queue_num = SXGBE_TX_QUEUES; ++txalloc_err: ++ while (queue_num--) ++ free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); + return ret; + } + +-- +2.53.0 + diff --git a/queue-5.10/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-5.10/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch new file mode 100644 index 0000000000..d75579e1d5 --- /dev/null +++ b/queue-5.10/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch @@ -0,0 +1,96 @@ +From 9f1d44aad084db4481eafed4c7404e2616617767 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 16:42:01 -0700 +Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in + sip_help_tcp() + +From: Xiang Mei + +[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ] + +sip_help_tcp() stores the size change of each NAT-rewritten SIP message +in s16 diff and accumulates it in s16 tdiff, but a single message can +grow by more than S16_MAX while the packet stays under the 65535 +enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long +Contact list expands the message by tens of kilobytes. diff then wraps, +and "datalen = datalen + diff - msglen" yields a huge unsigned datalen, +so the next iteration's ct_sip_get_header() reads past the linearized skb +tail. + +Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the +65535 byte packet limit, and the seqadj core is already s32 +(nf_ct_seqadj_set() takes s32), so no previously accepted input is +rejected. + + BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25 + ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694) + nf_confirm (net/netfilter/nf_conntrack_proto.c:183) + nf_hook_slow (net/netfilter/core.c:619) + ip6_output (net/ipv6/ip6_output.c:246) + ip6_forward (net/ipv6/ip6_output.c:690) + ipv6_rcv (net/ipv6/ip6_input.c:351) + __netif_receive_skb_one_core (net/core/dev.c:6212) + process_backlog (net/core/dev.c:6676) + __napi_poll (net/core/dev.c:7735) + net_rx_action (net/core/dev.c:7955) + handle_softirqs (kernel/softirq.c:622) + run_ksoftirqd (kernel/softirq.c:1076) + ... + +Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support") +Reported-by: Weiming Shi +Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Xiang Mei +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/nf_conntrack_sip.h | 2 +- + net/netfilter/nf_conntrack_sip.c | 2 +- + net/netfilter/nf_nat_sip.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h +index c620521c42bc6..dfb9708f53404 100644 +--- a/include/linux/netfilter/nf_conntrack_sip.h ++++ b/include/linux/netfilter/nf_conntrack_sip.h +@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks { + unsigned int *datalen); + + void (*seq_adjust)(struct sk_buff *skb, +- unsigned int protoff, s16 off); ++ unsigned int protoff, s32 off); + + unsigned int (*expect)(struct sk_buff *skb, + unsigned int protoff, +diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c +index 4326d5ea0400d..0f6147ea9e67b 100644 +--- a/net/netfilter/nf_conntrack_sip.c ++++ b/net/netfilter/nf_conntrack_sip.c +@@ -1622,7 +1622,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, + unsigned int matchoff, matchlen; + unsigned int msglen, origlen; + const char *dptr, *end; +- s16 diff, tdiff = 0; ++ s32 diff, tdiff = 0; + int ret = NF_ACCEPT; + unsigned long clen; + bool term; +diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c +index 76a8bbb44951c..7687d3d15df22 100644 +--- a/net/netfilter/nf_nat_sip.c ++++ b/net/netfilter/nf_nat_sip.c +@@ -313,7 +313,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, + } + + static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, +- s16 off) ++ s32 off) + { + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); +-- +2.53.0 + diff --git a/queue-5.10/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-5.10/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch new file mode 100644 index 0000000000..8acd993150 --- /dev/null +++ b/queue-5.10/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch @@ -0,0 +1,69 @@ +From 50499df170a23d43820095c5781123f1d061ddda Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:15:23 +0000 +Subject: netfilter: nft_payload: fix mask build for partial field offload + +From: Xiang Mei (Microsoft) + +[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ] + +nft_payload_offload_mask() builds the offload match mask for a payload +expression that covers only part of a header field. For a partial IPv6 +address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which +is undefined on the 32-bit int operand. It also trims only one word, so +the remaining words stay 0xffffffff (and when priv_len is a multiple of 4 +the trim is skipped entirely), leaving the mask covering more bytes than +the rule matches. + + UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20 + shift exponent 120 is too large for 32-bit type 'int' + ... + +The match is byte-granular and struct nft_data is zero-initialised, so the +correct mask is simply the first priv_len bytes set to 0xff. Set those +bytes directly and drop the word/shift trimming; this removes the undefined +shift and no longer over-masks the trailing bytes. + +Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes") +Reported-by: AutonomousCodeSecurity@microsoft.com +Signed-off-by: Xiang Mei (Microsoft) +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/nft_payload.c | 12 +----------- + 1 file changed, 1 insertion(+), 11 deletions(-) + +diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c +index ae0c4cd2dd1c5..df296551ba6f2 100644 +--- a/net/netfilter/nft_payload.c ++++ b/net/netfilter/nft_payload.c +@@ -159,9 +159,7 @@ static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr) + static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + u32 priv_len, u32 field_len) + { +- unsigned int remainder, delta, k; + struct nft_data mask = {}; +- __be32 remainder_mask; + + if (priv_len == field_len) { + memset(®->mask, 0xff, priv_len); +@@ -170,15 +168,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + return false; + } + +- memset(&mask, 0xff, field_len); +- remainder = priv_len % sizeof(u32); +- if (remainder) { +- k = priv_len / sizeof(u32); +- delta = field_len - priv_len; +- remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1)); +- mask.data[k] = (__force u32)remainder_mask; +- } +- ++ memset(&mask, 0xff, priv_len); + memcpy(®->mask, &mask, field_len); + + return true; +-- +2.53.0 + diff --git a/queue-5.10/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-5.10/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch new file mode 100644 index 0000000000..d02f203c59 --- /dev/null +++ b/queue-5.10/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch @@ -0,0 +1,83 @@ +From e9a89d5243f31521d7447907fe0317643d2a7715 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 22:02:46 +0200 +Subject: netfilter: xt_hashlimit: validate hashtable supports + XT_HASHLIMIT_RATE_MATCH + +From: Pablo Neira Ayuso + +[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ] + +The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the +dsthash_ent structure which represents an entry in the hashtable. There +is a union area which uses a different layout to express the rate match +mode. + +Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode +flag is requested by two or more different rules that refer to the same +hashtable. Otherwise, uninitialized access to the burst field in the +union is possible. + +Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by +revision less than 3 too. + +Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode") +Reported-and-tested-by: Talha Berk Arslan +Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/ +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/xt_hashlimit.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c +index 9c5cfd74a0ee4..26918fa575afe 100644 +--- a/net/netfilter/xt_hashlimit.c ++++ b/net/netfilter/xt_hashlimit.c +@@ -118,6 +118,7 @@ struct xt_hashlimit_htable { + refcount_t use; + u_int8_t family; + bool rnd_initialized; ++ bool ratematch; + + struct hashlimit_cfg3 cfg; /* config */ + +@@ -325,6 +326,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg, + vfree(hinfo); + return -ENOMEM; + } ++ hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH); + spin_lock_init(&hinfo->lock); + + switch (revision) { +@@ -868,7 +870,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + } + + /* Check for overflow. */ +- if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (revision < 3) ++ return -EINVAL; ++ + if (cfg->avg == 0 || cfg->avg > U32_MAX) { + pr_info_ratelimited("invalid rate\n"); + return -ERANGE; +@@ -901,6 +906,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + mutex_unlock(&hashlimit_mutex); + return ret; + } ++ } else { ++ if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH && ++ !(*hinfo)->ratematch) || ++ (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) && ++ (*hinfo)->ratematch)) { ++ mutex_unlock(&hashlimit_mutex); ++ htable_put(*hinfo); ++ return -EINVAL; ++ } + } + mutex_unlock(&hashlimit_mutex); + +-- +2.53.0 + diff --git a/queue-5.10/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-5.10/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch new file mode 100644 index 0000000000..0b8aa168cc --- /dev/null +++ b/queue-5.10/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch @@ -0,0 +1,42 @@ +From 11e44d9e9ba01497ec3f895cdad38c7ee15c3849 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 12:58:31 +0530 +Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop + +From: Suman Ghosh + +[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ] + +During link down event, we were doing netif_tx_stop_all_queues() first +and then netif_carrier_off(). This can cause a potential race since +carrier is still on during down event. This patch reverse the calling +order to fix the issue. + +Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications") +Signed-off-by: Suman Ghosh +Signed-off-by: Ratheesh Kannoth +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +index 50a36639e0ced..ca2b8ae98bd26 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +@@ -840,8 +840,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf) + netif_carrier_on(netdev); + netif_tx_start_all_queues(netdev); + } else { +- netif_tx_stop_all_queues(netdev); + netif_carrier_off(netdev); ++ netif_tx_stop_all_queues(netdev); + } + } + +-- +2.53.0 + diff --git a/queue-5.10/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-5.10/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch new file mode 100644 index 0000000000..29a21162bf --- /dev/null +++ b/queue-5.10/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch @@ -0,0 +1,38 @@ +From 0288d2e5dc143d90e2e1d52f25b0c2fc91bf8502 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:55 +0200 +Subject: powerpc/boot: Fix simpleboot CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/simpleboot.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c +index c80691d83880b..27591df41e9e8 100644 +--- a/arch/powerpc/boot/simpleboot.c ++++ b/arch/powerpc/boot/simpleboot.c +@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + /* finally, setup the timebase */ + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-5.10/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-5.10/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch new file mode 100644 index 0000000000..ec8adc4e0e --- /dev/null +++ b/queue-5.10/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch @@ -0,0 +1,38 @@ +From 86e0c60425f0883fd9421be225d2c0b2aa5e6055 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:57 +0200 +Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-akebono.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c +index e3cc2599869cc..1b529037480fb 100644 +--- a/arch/powerpc/boot/treeboot-akebono.c ++++ b/arch/powerpc/boot/treeboot-akebono.c +@@ -146,7 +146,7 @@ void platform_init(char *userdata) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-5.10/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-5.10/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch new file mode 100644 index 0000000000..22210fa629 --- /dev/null +++ b/queue-5.10/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch @@ -0,0 +1,38 @@ +From 933b9bdfe5a4833a40672b17a638b85f251b1cc0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:56 +0200 +Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-currituck.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c +index d53e8a592f816..5b5363b74f9f3 100644 +--- a/arch/powerpc/boot/treeboot-currituck.c ++++ b/arch/powerpc/boot/treeboot-currituck.c +@@ -102,7 +102,7 @@ void platform_init(void) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-5.10/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-5.10/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch new file mode 100644 index 0000000000..ce539b3597 --- /dev/null +++ b/queue-5.10/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch @@ -0,0 +1,169 @@ +From 16c00fd5110a14cce11da9ca1596418f31d80c6e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 12:43:11 +0200 +Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path + +From: Denis V. Lunev + +[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ] + +A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports +configured wedges the rtnetlink control plane of the whole machine: + + NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms + [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2! + [qede_recovery_handler:2665(ens6f0)]Starting a recovery process + +The recovery path deadlocks on the driver's own mutex: + + qede_sp_task + rtnl_lock() + mutex_lock(&edev->qede_lock) <- taken + qede_recovery_handler + qede_load + udp_tunnel_nic_reset_ntf + __udp_tunnel_nic_device_sync + info->sync_table == qede_udp_tunnel_sync + mutex_lock(&edev->qede_lock) <- same task: deadlock + +The mutex is not recursive, so the kworker blocks on itself with +rtnl_lock held, and neither lock is ever released. Every task that +calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6 +addrconf, sshd) blocks forever while the node still answers ping. +In a vmcore from an affected production node rtnl_mutex.owner +decodes to the very kworker blocked at the innermost mutex_lock() +above. + +Re-sync the tunnel ports from qede_sp_task() after the internal lock +is dropped, still under rtnl_lock as the udp_tunnel API requires. +This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf() +under rtnl without the internal lock. + +qede_recovery_handler() now returns whether it has successfully +reloaded an open device, and the caller re-syncs the ports only in +that case. This keeps the old gating exactly: a device that was down +or a failed recovery returns false, as those paths never reached the +udp_tunnel_nic_reset_ntf() call before either. + +This was the only user of the qede_lock()/qede_unlock() helpers, so +remove them. + +Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra") +Signed-off-by: Denis V. Lunev +CC: Andrew Lunn +CC: "David S. Miller" +CC: Eric Dumazet +CC: Jakub Kicinski +CC: Paolo Abeni +Reviewed-by: Jacob Keller +Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++---------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c +index 16c3f32e5ca73..a3103b2ed734f 100644 +--- a/drivers/net/ethernet/qlogic/qede/qede_main.c ++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c +@@ -113,7 +113,7 @@ static void qede_remove(struct pci_dev *pdev); + static void qede_shutdown(struct pci_dev *pdev); + static void qede_link_update(void *dev, struct qed_link_output *link); + static void qede_schedule_recovery_handler(void *dev); +-static void qede_recovery_handler(struct qede_dev *edev); ++static bool qede_recovery_handler(struct qede_dev *edev); + static void qede_schedule_hw_err_handler(void *dev, + enum qed_hw_err_type err_type); + static void qede_get_eth_tlv_data(void *edev, void *data); +@@ -993,21 +993,6 @@ void __qede_unlock(struct qede_dev *edev) + mutex_unlock(&edev->qede_lock); + } + +-/* This version of the lock should be used when acquiring the RTNL lock is also +- * needed in addition to the internal qede lock. +- */ +-static void qede_lock(struct qede_dev *edev) +-{ +- rtnl_lock(); +- __qede_lock(edev); +-} +- +-static void qede_unlock(struct qede_dev *edev) +-{ +- __qede_unlock(edev); +- rtnl_unlock(); +-} +- + static void qede_periodic_task(struct work_struct *work) + { + struct qede_dev *edev = container_of(work, struct qede_dev, +@@ -1044,6 +1029,8 @@ static void qede_sp_task(struct work_struct *work) + */ + + if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) { ++ bool reloaded; ++ + cancel_delayed_work_sync(&edev->periodic_task); + #ifdef CONFIG_QED_SRIOV + /* SRIOV must be disabled outside the lock to avoid a deadlock. +@@ -1052,9 +1039,17 @@ static void qede_sp_task(struct work_struct *work) + if (pci_num_vf(edev->pdev)) + qede_sriov_configure(edev->pdev, 0); + #endif +- qede_lock(edev); +- qede_recovery_handler(edev); +- qede_unlock(edev); ++ rtnl_lock(); ++ __qede_lock(edev); ++ reloaded = qede_recovery_handler(edev); ++ __qede_unlock(edev); ++ ++ /* The udp_tunnel core synchronously calls back into ++ * qede_udp_tunnel_sync(), which takes the qede lock. ++ */ ++ if (reloaded) ++ udp_tunnel_nic_reset_ntf(edev->ndev); ++ rtnl_unlock(); + } + + __qede_lock(edev); +@@ -2573,9 +2568,13 @@ static void qede_recovery_failed(struct qede_dev *edev) + edev->ops->common->set_power_state(edev->cdev, PCI_D3hot); + } + +-static void qede_recovery_handler(struct qede_dev *edev) ++/* Returns true if an open device was successfully reloaded and its ++ * udp_tunnel ports need to be re-synced by the caller. ++ */ ++static bool qede_recovery_handler(struct qede_dev *edev) + { + u32 curr_state = edev->state; ++ bool reloaded = false; + int rc; + + DP_NOTICE(edev, "Starting a recovery process\n"); +@@ -2605,17 +2604,18 @@ static void qede_recovery_handler(struct qede_dev *edev) + goto err; + + qede_config_rx_mode(edev->ndev); +- udp_tunnel_nic_reset_ntf(edev->ndev); ++ reloaded = true; + } + + edev->state = curr_state; + + DP_NOTICE(edev, "Recovery handling is done\n"); + +- return; ++ return reloaded; + + err: + qede_recovery_failed(edev); ++ return false; + } + + static void qede_atomic_hw_err_handler(struct qede_dev *edev) +-- +2.53.0 + diff --git a/queue-5.10/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch b/queue-5.10/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch new file mode 100644 index 0000000000..7f91d8331d --- /dev/null +++ b/queue-5.10/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch @@ -0,0 +1,118 @@ +From 2a62ca0ffd50c306beca64ec3891bdd4cdcc2f59 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 9 Jul 2026 16:27:54 +0000 +Subject: rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled + +From: Ilia Gavrilov + +[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ] + +When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst +is never initialized because inet6_init() exits before addrconf_init() +is called to initialize it. An attempt to bind an RDS socket to +an ipv6 address results in a crash in __ipv6_chk_addr_and_flags() + +KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] +RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0 +Call Trace: + + ipv6_chk_addr+0x3b/0x50 + rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp] + rds_trans_get_preferred+0x15d/0x2d0 [rds] + ? trace_hardirqs_on+0x2d/0x110 + rds_bind+0x1433/0x1d60 [rds] + ? rds_remove_bound+0xd50/0xd50 [rds] + ? aa_af_perm+0x250/0x250 + ? __might_fault+0xde/0x190 + ? __sys_bind+0x1dc/0x210 + __sys_bind+0x1dc/0x210 + ? __ia32_sys_socketpair+0x100/0x100 + ? restore_fpregs_from_fpstate+0x53/0x100 + __x64_sys_bind+0x73/0xb0 + ? syscall_enter_from_user_mode+0x1c/0x50 + do_syscall_64+0x34/0x80 + entry_SYSCALL_64_after_hwframe+0x6e/0xd8 +RIP: 0033:0x7f47f8269ea9 + + +The following code reproduces the issue: + +struct sockaddr_in6 addr; +s = socket(PF_RDS, SOCK_SEQPACKET, 0); + +memset(&addr, 0, sizeof(addr)); +inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr); +addr.sin6_family = AF_INET6; +addr.sin6_port = htons(PORT); + +bind(s, &addr, sizeof(addr)); + +Found by InfoTeCS on behalf of Linux Verification Center +(linuxtesting.org) with Syzkaller. + +Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") +Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support") +Signed-off-by: Ilia Gavrilov +Reviewed-by: Allison Henderson +Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru +Signed-off-by: Jakub Kicinski +Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()") +Signed-off-by: Sasha Levin +--- + net/rds/ib.c | 4 ++++ + net/rds/ib_cm.c | 4 ++++ + net/rds/tcp.c | 8 +++++--- + 3 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/net/rds/ib.c b/net/rds/ib.c +index ec45664f38767..810a9b76101f5 100644 +--- a/net/rds/ib.c ++++ b/net/rds/ib.c +@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr, + sa = (struct sockaddr *)&sin; + } else { + #if IS_ENABLED(CONFIG_IPV6) ++ if (!ipv6_mod_enabled()) { ++ ret = -EADDRNOTAVAIL; ++ goto out; ++ } + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = *addr; +diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c +index f643d1f59c3be..f23233e0a53c7 100644 +--- a/net/rds/ib_cm.c ++++ b/net/rds/ib_cm.c +@@ -844,6 +844,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, + dp = event->param.conn.private_data; + if (isv6) { + #if IS_ENABLED(CONFIG_IPV6) ++ if (!ipv6_mod_enabled()) { ++ err = -EOPNOTSUPP; ++ goto out; ++ } + dp_cmn = &dp->ricp_v6.dp_cmn; + saddr6 = &dp->ricp_v6.dp_saddr; + daddr6 = &dp->ricp_v6.dp_daddr; +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index b560d06e6d96d..071f2a2f514ff 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -341,9 +341,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, + rcu_read_unlock(); + } + #if IS_ENABLED(CONFIG_IPV6) +- ret = ipv6_chk_addr(net, addr, dev, 0); +- if (ret) +- return 0; ++ if (ipv6_mod_enabled()) { ++ ret = ipv6_chk_addr(net, addr, dev, 0); ++ if (ret) ++ return 0; ++ } + #endif + return -EADDRNOTAVAIL; + } +-- +2.53.0 + diff --git a/queue-5.10/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-5.10/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch new file mode 100644 index 0000000000..a13916425e --- /dev/null +++ b/queue-5.10/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch @@ -0,0 +1,85 @@ +From 992c9437cd6093ed71784188ff32040dd3874bdf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 14:02:03 -0700 +Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in + rds_tcp_laddr_check() + +From: Xiang Mei + +[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ] + +rds_tcp_laddr_check() looks up a scoped IPv6 interface with +dev_get_by_index_rcu(), drops the RCU read-side lock, and only then +passes the bare struct net_device * into ipv6_chk_addr(). + +dev_get_by_index_rcu() only keeps the device alive within the same RCU +read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can +free the net_device; ipv6_chk_addr() then dereferences the stale pointer +in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading +freed memory. + +Keep the RCU read-side lock held across the ipv6_chk_addr() call instead +of dropping it right after the lookup, so the device cannot be freed +while it is in use. + + BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + Read of size 8 at addr ffff8880106ec000 by task exploit/153 + Call Trace: + ... + kasan_report (mm/kasan/report.c:595) + __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972) + rds_tcp_laddr_check (net/rds/tcp.c:370) + rds_bind (net/rds/bind.c:248) + __sys_bind (net/socket.c:1920) + __x64_sys_bind (net/socket.c:1956) + do_syscall_64 (arch/x86/entry/syscall_64.c:63) + entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) + +Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") +Reported-by: Weiming Shi +Signed-off-by: Xiang Mei +Reviewed-by: Allison Henderson +Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/rds/tcp.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index 071f2a2f514ff..f66cbf0b9895f 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -330,23 +330,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, + /* If the scope_id is specified, check only those addresses + * hosted on the specified interface. + */ ++ rcu_read_lock(); + if (scope_id != 0) { +- rcu_read_lock(); + dev = dev_get_by_index_rcu(net, scope_id); + /* scope_id is not valid... */ + if (!dev) { + rcu_read_unlock(); + return -EADDRNOTAVAIL; + } +- rcu_read_unlock(); + } + #if IS_ENABLED(CONFIG_IPV6) + if (ipv6_mod_enabled()) { + ret = ipv6_chk_addr(net, addr, dev, 0); +- if (ret) ++ if (ret) { ++ rcu_read_unlock(); + return 0; ++ } + } + #endif ++ rcu_read_unlock(); + return -EADDRNOTAVAIL; + } + +-- +2.53.0 + diff --git a/queue-5.10/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-5.10/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch new file mode 100644 index 0000000000..b02d1b5e82 --- /dev/null +++ b/queue-5.10/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch @@ -0,0 +1,56 @@ +From 9eff8256421689e36ac2f38c56ac26940d1c16c8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 14 Jul 2026 19:49:34 +0900 +Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer + +From: HyeongJun An + +[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ] + +iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the +target-supplied data segment. The segment carries a 2-byte sense length +followed by the sense bytes, so it must hold 2 + senselen bytes, but the +bounds check only requires datalen >= senselen: + + senselen = get_unaligned_be16(data); + if (datalen < senselen) + goto invalid_datalen; + memcpy(sc->sense_buffer, data + 2, + min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); + +A target that returns a SCSI Response whose datalen equals senselen +(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data + +2 read up to two bytes past the received data. Those bytes are stale +conn->data contents and end up in the command's sense buffer, which is +returned to userspace. + +Account for the 2-byte sense length prefix in the check. + +Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi") +Suggested-by: Sashiko AI +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c +index 7e82ddce5031e..8cd20cc9809f1 100644 +--- a/drivers/scsi/libiscsi.c ++++ b/drivers/scsi/libiscsi.c +@@ -840,7 +840,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, + } + + senselen = get_unaligned_be16(data); +- if (datalen < senselen) ++ if (datalen < senselen + 2) + goto invalid_datalen; + + memcpy(sc->sense_buffer, data + 2, +-- +2.53.0 + diff --git a/queue-5.10/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-5.10/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch new file mode 100644 index 0000000000..2f7dd65f79 --- /dev/null +++ b/queue-5.10/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch @@ -0,0 +1,71 @@ +From 3b9bdcf73657f9eda520bf6a9b8a7a8240528626 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 15:58:48 +0900 +Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the + connection buffer + +From: HyeongJun An + +[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ] + +iscsi_tcp_hdr_dissect() receives the data segment of several PDU types +into the fixed-size conn->data buffer, which is allocated for +ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes. For the LOGIN_RSP, TEXT_RSP, +REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU +whose DataSegmentLength exceeds that buffer. + +The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its +data segment (sense/response data) into conn->data via +iscsi_tcp_data_recv_prep(), but it does so without the same check. The +only upstream bound on in.datalen is conn->max_recv_dlength, the +initiator's advertised MaxRecvDataSegmentLength, which is commonly +negotiated well above 8192 (open-iscsi defaults to 262144). A target +that returns a SCSI Response with a DataSegmentLength between 8193 and +max_recv_dlength therefore overflows the 8192-byte conn->data buffer. + +Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly +like those responses: bound the data segment, receive it into conn->data +when present, and otherwise complete the PDU with no data. Fold the +opcode into that case group rather than duplicating the check. + +Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld") +Suggested-by: Chris Leech +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi_tcp.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c +index 83f14b2c8804b..0aa883ab6ff02 100644 +--- a/drivers/scsi/libiscsi_tcp.c ++++ b/drivers/scsi/libiscsi_tcp.c +@@ -722,13 +722,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); + spin_unlock(&conn->session->back_lock); + break; +- case ISCSI_OP_SCSI_CMD_RSP: +- if (tcp_conn->in.datalen) { +- iscsi_tcp_data_recv_prep(tcp_conn); +- return 0; +- } +- rc = iscsi_complete_pdu(conn, hdr, NULL, 0); +- break; + case ISCSI_OP_R2T: + spin_lock(&conn->session->back_lock); + task = iscsi_itt_to_ctask(conn, hdr->itt); +@@ -745,6 +738,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + } else + rc = ISCSI_ERR_PROTO; + break; ++ case ISCSI_OP_SCSI_CMD_RSP: + case ISCSI_OP_LOGIN_RSP: + case ISCSI_OP_TEXT_RSP: + case ISCSI_OP_REJECT: +-- +2.53.0 + diff --git a/queue-5.10/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-5.10/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch new file mode 100644 index 0000000000..6c3e11934d --- /dev/null +++ b/queue-5.10/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch @@ -0,0 +1,66 @@ +From 1febe617f816473534ed9ebd292b1b4e3670c4a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 09:27:36 +0200 +Subject: scsi: zfcp: Fix memory leak during adapter release by destroying + gid_pn_req + +From: Benjamin Block + +[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ] + +When releasing an adapter we don't free the mempool 'gid_pn_req' that is +allocated during the enqueue. This leaks memory: + + unreferenced object 0xd8d29297de700 (size 256): + comm "(udev-worker)", pid 2105, jiffies 4294945794 + hex dump (first 32 bytes): + 00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00 ......N......... + ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0 ..........._g... + backtrace (crc 4a5b5da2): + [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0 + [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0 + [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150 + [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp] + [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp] + [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp] + [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80 + [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390 + [<000dc45f643d8238>] online_store+0x298/0x5b0 + [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480 + [<000dc45f62c81150>] new_sync_write+0x370/0x4b0 + [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0 + [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0 + [<000dc45f621c4a16>] do_syscall+0x2f6/0x430 + [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0 + [<000dc45f64dc2224>] system_call+0x74/0xa0 + +Fix this by destroying the mempool during the adapter's release. + +Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp") +Signed-off-by: Benjamin Block +Tested-by: M Nikhil +Acked-by: M Nikhil +Reviewed-by: Chinmaya Kajagar +Reviewed-by: Nihar Panda +Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/s390/scsi/zfcp_aux.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c +index 36c2bd2016f22..d472d0ea7957b 100644 +--- a/drivers/s390/scsi/zfcp_aux.c ++++ b/drivers/s390/scsi/zfcp_aux.c +@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) + static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) + { + mempool_destroy(adapter->pool.erp_req); ++ mempool_destroy(adapter->pool.gid_pn_req); + mempool_destroy(adapter->pool.scsi_req); + mempool_destroy(adapter->pool.scsi_abort); + mempool_destroy(adapter->pool.qtcb_pool); +-- +2.53.0 + diff --git a/queue-5.10/series b/queue-5.10/series index 2e649fc059..0e4690b6c6 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -200,3 +200,38 @@ hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch net-qrtr-ns-limit-the-maximum-server-registration-pe.patch net-qrtr-ns-raise-node-count-limit-to-512.patch tls-separate-no-async-decryption-request-handling-fr.patch +dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch +asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch +asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch +netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch +keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch +keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch +assoc_array-trim-the-final-shortcut-word-using-the-c.patch +netfilter-xt_hashlimit-validate-hashtable-supports-x.patch +netfilter-nft_payload-fix-mask-build-for-partial-fie.patch +rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch +rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch +scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch +scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch +smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch +hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch +forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch +hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch +hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch +hwmon-adt7470-create-functions-for-updating-readings.patch +hwmon-adt7470-fix-some-style-issues.patch +hwmon-adt7470-convert-to-use-regmap.patch +hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch +powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch +powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch +powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch +hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch +bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch +net-phylink-put-link_gpio-if-phylink_create-fails.patch +scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch +net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch +net-sxgbe-check-descriptor-ring-allocation-failures.patch +can-isotp-check-register_netdevice_notifier-error-in.patch +tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch +octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch +qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch diff --git a/queue-5.10/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-5.10/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch new file mode 100644 index 0000000000..705f2535ed --- /dev/null +++ b/queue-5.10/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch @@ -0,0 +1,85 @@ +From 6c7562e9bf0ea8d714c5c2cace7110d2d71d068a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 28 Jun 2026 14:59:09 +0800 +Subject: smb: client: fix buffer leaks in SMB1 read and write + +From: Dawei Feng + +[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ] + +CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request +buffer before checking whether tcon->ses->server is NULL. If that +defensive check ever fails, the helper returns -ECONNABORTED without +releasing the request buffer. + +Fix these leaks by releasing the allocated request buffer before +returning from these error paths. Use cifs_small_buf_release() for the +buffers allocated by small_smb_init() and cifs_buf_release() for the +buffer allocated by smb_init(). + +The bug was first flagged by an experimental analysis tool we are +developing for kernel memory-management bugs while analyzing +v6.13-rc1. The tool is still under development and is not yet publicly +available. Manual inspection confirms that the bug is still +present in v7.1.1. + +An x86_64 allyesconfig build showed no new warnings. + +Runtime validation used a temporary fault-injection hook to force +tcon->ses->server to NULL after request-buffer initialization. On the +unfixed kernel, the harness observed two leaked small request buffers and +one leaked large request buffer, with directed kmemleak dumps confirming +the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer +deltas remained. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Signed-off-by: Dawei Feng +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/cifs/cifssmb.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c +index a19e5e7c7d0f4..67ff1669cab2a 100644 +--- a/fs/cifs/cifssmb.c ++++ b/fs/cifs/cifssmb.c +@@ -1774,8 +1774,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -1887,8 +1889,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -2317,8 +2321,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +-- +2.53.0 + diff --git a/queue-5.10/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-5.10/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch new file mode 100644 index 0000000000..be09cc3781 --- /dev/null +++ b/queue-5.10/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch @@ -0,0 +1,43 @@ +From bf1ada81c1e671b72ddd48aa962e6319fefdc330 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 21:49:51 +0900 +Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data() + +From: Masami Hiramatsu (Google) + +[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ] + +mmio_reset_data() is called during tracer initialization, reset, and +start. While it resets overrun_detected and prev_overruns, it neglects +to reset dropped_count. Consequently, dropped event counts from prior +tracing sessions persist in dropped_count and corrupt overrun reports +in subsequent runs. + +Fix this by explicitly calling atomic_set(&dropped_count, 0) in +mmio_reset_data(). + +Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2 +Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording") +Assisted-by: Antigravity:gemini-3.6-flash +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_mmiotrace.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c +index da2231ac13dfc..e8ae2d92a6787 100644 +--- a/kernel/trace/trace_mmiotrace.c ++++ b/kernel/trace/trace_mmiotrace.c +@@ -31,6 +31,7 @@ static void mmio_reset_data(struct trace_array *tr) + { + overrun_detected = false; + prev_overruns = 0; ++ atomic_set(&dropped_count, 0); + + tracing_reset_online_cpus(&tr->array_buffer); + } +-- +2.53.0 + diff --git a/queue-5.15/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-5.15/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..76bbb34a33 --- /dev/null +++ b/queue-5.15/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From c1fae0f093b25893b0403c41b3e46344f922bf1b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:12:54 +0530 +Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ] + +In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98090->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98090.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c +index 3cf41870978da..4f173f86e157e 100644 +--- a/sound/soc/codecs/max98090.c ++++ b/sound/soc/codecs/max98090.c +@@ -2391,8 +2391,9 @@ static int max98090_probe(struct snd_soc_component *component) + dev_dbg(component->dev, "max98090_probe\n"); + + max98090->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98090->mclk)) ++ if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + max98090->component = component; + +-- +2.53.0 + diff --git a/queue-5.15/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-5.15/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..e707083e97 --- /dev/null +++ b/queue-5.15/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From 5e47c6da7e8d3c92b880f0270d99e3d8f8f8d955 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:09:50 +0530 +Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ] + +In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98095->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98095.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c +index 736cd70be7255..909f2e2f18910 100644 +--- a/sound/soc/codecs/max98095.c ++++ b/sound/soc/codecs/max98095.c +@@ -1990,8 +1990,9 @@ static int max98095_probe(struct snd_soc_component *component) + int ret = 0; + + max98095->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98095->mclk)) ++ if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + /* reset the codec, the DSP core, and disable all interrupts */ + max98095_reset(component); +-- +2.53.0 + diff --git a/queue-5.15/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-5.15/assoc_array-trim-the-final-shortcut-word-using-the-c.patch new file mode 100644 index 0000000000..ecb9a03ace --- /dev/null +++ b/queue-5.15/assoc_array-trim-the-final-shortcut-word-using-the-c.patch @@ -0,0 +1,66 @@ +From 574af16c048482cf6920dbb7946e20de22733517 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:05 -0400 +Subject: assoc_array: trim the final shortcut word using the current chunk end + +From: Michael Bommarito + +[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ] + +assoc_array_walk() masks off the bits past shortcut->skip_to_level in the +word that contains skip_to_level, gated on +round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level. + +That guard is wrong in two opposite ways: + + - When sc_level is word-aligned (every word after the first) round_up() + is a no-op, so the guard is sc_level > skip_to_level and never fires for + the word that holds skip_to_level. A shortcut that spans more than one + word and ends in the middle of its last word leaves that word untrimmed, + and its stale high bits leak into the dissimilarity word and can steer + the walk down the wrong descendant. + + - When sc_level is unaligned (the first word) and skip_to_level sits on + the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and + fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears + the whole dissimilarity word and makes a differing shortcut compare + equal. + +Use the end of the chunk that contains sc_level instead: + + skip_to_level < round_down(sc_level, CHUNK) + CHUNK + +For an aligned sc_level whose word holds skip_to_level this now fires (the +first bug); for an unaligned sc_level with skip_to_level on the following +boundary it does not, so shift is never 0 when the branch runs and the trim +never clears the whole word. + +Fixes: 3cb989501c26 ("Add a generic associative array implementation.") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + lib/assoc_array.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/assoc_array.c b/lib/assoc_array.c +index 70304b8f15ace..861965af48b9c 100644 +--- a/lib/assoc_array.c ++++ b/lib/assoc_array.c +@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array, + sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT]; + dissimilarity = segments ^ sc_segments; + +- if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) { ++ if (shortcut->skip_to_level < round_down(sc_level, ++ ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) { + /* Trim segments that are beyond the shortcut */ + int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK; + dissimilarity &= ~(ULONG_MAX << shift); +-- +2.53.0 + diff --git a/queue-5.15/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-5.15/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch new file mode 100644 index 0000000000..d2c674ba02 --- /dev/null +++ b/queue-5.15/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch @@ -0,0 +1,60 @@ +From c6076f6b0131ef728ba13108eb7b366a087ca2f7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 14:48:45 +0800 +Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp + +From: Jiale Yao + +[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ] + +l2cap_le_connect_rsp() obtains a channel via +__l2cap_get_chan_by_ident() but neither holds a reference nor uses +l2cap_chan_hold_unless_zero() before locking and operating on it. +A concurrent l2cap_chan_del() triggered by a remote disconnect can +free the channel between the lookup and l2cap_chan_lock(), causing +a use-after-free. + +The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler +l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero() +to safely hold a reference, but l2cap_le_connect_rsp() was left +unprotected. + +Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup +and l2cap_chan_put() on the exit path, consistent with other L2CAP +response handlers. + +Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request") +Assisted-by: Claude:deepseek-v4-pro +Signed-off-by: Jiale Yao +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/l2cap_core.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index 196b3bdca2644..913c0d04a481a 100644 +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -5734,6 +5734,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + goto unlock; + } + ++ chan = l2cap_chan_hold_unless_zero(chan); ++ if (!chan) ++ return -EBADSLT; ++ + err = 0; + + l2cap_chan_lock(chan); +@@ -5779,6 +5783,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + } + + l2cap_chan_unlock(chan); ++ l2cap_chan_put(chan); + + unlock: + mutex_unlock(&conn->chan_lock); +-- +2.53.0 + diff --git a/queue-5.15/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-5.15/can-isotp-check-register_netdevice_notifier-error-in.patch new file mode 100644 index 0000000000..fdc5c66a61 --- /dev/null +++ b/queue-5.15/can-isotp-check-register_netdevice_notifier-error-in.patch @@ -0,0 +1,57 @@ +From 1cfc9e18c6a4b6f370a9fb8619d5e009b990c649 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Jul 2026 16:56:56 +0800 +Subject: can: isotp: check register_netdevice_notifier() error in module init + +From: Minhong He + +[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ] + +Register the netdevice notifier before can_proto_register() and check the +return value. If protocol registration fails, unregister the notifier +before returning the error. + +Align isotp_module_init() with the reordering already done for raw.c +(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and +bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization +in bcm_module_init()")). + +Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier") +Signed-off-by: Minhong He +Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn +Signed-off-by: Marc Kleine-Budde +Signed-off-by: Sasha Levin +--- + net/can/isotp.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/net/can/isotp.c b/net/can/isotp.c +index de6992580c38c..34939744dd412 100644 +--- a/net/can/isotp.c ++++ b/net/can/isotp.c +@@ -1827,13 +1827,18 @@ static __init int isotp_module_init(void) + + pr_info("can: isotp protocol\n"); + ++ err = register_netdevice_notifier(&canisotp_notifier); ++ if (err) ++ return err; ++ + err = can_proto_register(&isotp_can_proto); +- if (err < 0) ++ if (err < 0) { + pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err)); +- else +- register_netdevice_notifier(&canisotp_notifier); ++ unregister_netdevice_notifier(&canisotp_notifier); ++ return err; ++ } + +- return err; ++ return 0; + } + + static __exit void isotp_module_exit(void) +-- +2.53.0 + diff --git a/queue-5.15/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-5.15/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch new file mode 100644 index 0000000000..0b3b60ff5a --- /dev/null +++ b/queue-5.15/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch @@ -0,0 +1,60 @@ +From cc2b45f30c13cf436bfa67f01b3e4aaca0a0020c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 1 Jul 2026 12:57:33 +0800 +Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA + +From: Hongling Zeng + +[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ] + +When terminating DMA transfers, active descriptors are not properly +reclaimed. Only cyclic descriptors were handled, leaving non-cyclic +descriptors and their LLI chains to be permanently leaked. + +Fix by using vchan_terminate_vdesc() which handles both cyclic and +non-cyclic descriptors by adding them to desc_terminated queue for +proper cleanup. + +Add pchan->desc != pchan->done check to prevent double-adding completed +descriptors, which would corrupt the list. + +Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller") +Signed-off-by: Hongling Zeng +Acked-by: Jernej Skrabec +Suggested-by: Frank Li +Reviewed-by: Frank Li +Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/dma/sun6i-dma.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c +index 5cadd4d2b8246..fa216ef645d58 100644 +--- a/drivers/dma/sun6i-dma.c ++++ b/drivers/dma/sun6i-dma.c +@@ -895,16 +895,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan) + + spin_lock_irqsave(&vchan->vc.lock, flags); + +- if (vchan->cyclic) { +- vchan->cyclic = false; +- if (pchan && pchan->desc) { +- struct virt_dma_desc *vd = &pchan->desc->vd; +- struct virt_dma_chan *vc = &vchan->vc; ++ if (pchan && pchan->desc && pchan->desc != pchan->done) { ++ struct virt_dma_desc *vd = &pchan->desc->vd; + +- list_add_tail(&vd->node, &vc->desc_completed); +- } ++ vchan_terminate_vdesc(vd); + } + ++ vchan->cyclic = false; + vchan_get_all_descriptors(&vchan->vc, &head); + + if (pchan) { +-- +2.53.0 + diff --git a/queue-5.15/drm-mediatek-check-crtc-state-before-freeing.patch b/queue-5.15/drm-mediatek-check-crtc-state-before-freeing.patch new file mode 100644 index 0000000000..e89df03580 --- /dev/null +++ b/queue-5.15/drm-mediatek-check-crtc-state-before-freeing.patch @@ -0,0 +1,51 @@ +From 0f6a792fbfc21e024f8b1e762cf923bce1b9964c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 23:05:28 +0800 +Subject: drm/mediatek: Check CRTC state before freeing + +From: Ruoyu Wang + +[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ] + +mtk_crtc_reset() destroys the current CRTC state only when crtc->state +is non-NULL, but it always converts crtc->state to struct mtk_crtc_state +and passes the result to kfree(). + +When reset is called without an existing state, container_of(NULL, ...) +does not produce NULL. Keep the mtk state free in the same crtc->state +guard as the helper state destruction. + +This issue was found by a static analysis checker and confirmed by +manual source review. + +Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset") +Signed-off-by: Ruoyu Wang +Reviewed-by: CK Hu +Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/ +Signed-off-by: Chun-Kuang Hu +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +index 27f3e91425580..993c5743192b3 100644 +--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c ++++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +@@ -172,10 +172,10 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc) + { + struct mtk_crtc_state *state; + +- if (crtc->state) ++ if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); +- +- kfree(to_mtk_crtc_state(crtc->state)); ++ kfree(to_mtk_crtc_state(crtc->state)); ++ } + crtc->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-5.15/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-5.15/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch new file mode 100644 index 0000000000..61a59432a0 --- /dev/null +++ b/queue-5.15/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch @@ -0,0 +1,47 @@ +From 7c62323d58e38460269df77a947ec69e10983d19 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 17:26:37 +0800 +Subject: forcedeth: fix UAF of txrx_stats in nv_remove + +From: Chenguang Zhao + +[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ] + +nv_remove() frees the per-CPU txrx_stats before unregister_netdev(). +Until unregister completes, ndo_get_stats64, the NAPI/xmit data path, +and nv_close()/drain may still access txrx_stats, leading to a +use-after-free. + +Free the stats only after unregister_netdev(). + +Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Reviewed-by: Zhu Yanjun +Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/nvidia/forcedeth.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c +index e7be0710220e5..dcde28af8a0dc 100644 +--- a/drivers/net/ethernet/nvidia/forcedeth.c ++++ b/drivers/net/ethernet/nvidia/forcedeth.c +@@ -6190,10 +6190,10 @@ static void nv_remove(struct pci_dev *pci_dev) + struct net_device *dev = pci_get_drvdata(pci_dev); + struct fe_priv *np = netdev_priv(dev); + +- free_percpu(np->txrx_stats); +- + unregister_netdev(dev); + ++ free_percpu(np->txrx_stats); ++ + nv_restore_mac_addr(pci_dev); + + /* restore any phy related changes */ +-- +2.53.0 + diff --git a/queue-5.15/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-5.15/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch new file mode 100644 index 0000000000..868e5fcb90 --- /dev/null +++ b/queue-5.15/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch @@ -0,0 +1,51 @@ +From ef9bb63f5d8760e0cf7f2569f20e51a8e6683281 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:19 -0300 +Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread + +From: Luiz Angelo Daros de Luca + +[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ] + +When userspace configures 'auto_update_interval' to 0 via sysfs, the +background kthread executes schedule_timeout_interruptible(0), which +returns immediately. + +If 'num_temp_sensors' is concurrently or previously set to 0, the +msleep_interruptible() delay inside adt7470_read_temperatures() also +becomes 0. This combination forces the background thread into a tight, +unbounded busy-loop, hogging the CPU and flooding the I2C bus with a +continuous stream of transactions. + +Fix this vulnerability by raising the lower limit of the clamp_val in +auto_update_interval_store() from 0 to 500 milliseconds. This guarantees +a reasonable minimum sleep window between sensor updates, protecting the +system from intentional or accidental I2C bus denial of service. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org +Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 6aa6152e00337..0874d3684fbce 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev, + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + +- temp = clamp_val(temp, 0, 60000); ++ temp = clamp_val(temp, 500, 60000); + + mutex_lock(&data->lock); + data->auto_update_interval = temp; +-- +2.53.0 + diff --git a/queue-5.15/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch b/queue-5.15/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch new file mode 100644 index 0000000000..b66acc064c --- /dev/null +++ b/queue-5.15/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch @@ -0,0 +1,83 @@ +From 916313c30a9a6dfce670ca0754a10166fb012322 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:18 -0300 +Subject: hwmon: (adt7470) Fix cache updated before hardware write on I2C error + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ] + +adt7470_temp_write() and adt7470_pwm_write() update the driver's +cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing +the corresponding regmap_write(), and never check whether the write +succeeded before committing that update. If the I2C transaction fails, +the function correctly propagates the error to the caller, but the cache +silently keeps the new value, which was never actually applied to the +hardware. Subsequent reads then report a value that does not match the +device state. + +Reorder both write paths to update the cache only after a successful +regmap_write(), so the cache always reflects what was actually +written to the hardware. + +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 57c78e82a5f27..6aa6152e00337 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va + switch (attr) { + case hwmon_temp_min: + mutex_lock(&data->lock); +- data->temp_min[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val); ++ if (!err) ++ data->temp_min[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_temp_max: + mutex_lock(&data->lock); +- data->temp_max[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val); ++ if (!err) ++ data->temp_max[channel] = val; + mutex_unlock(&data->lock); + break; + default: +@@ -839,9 +841,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + case hwmon_pwm_input: + val = clamp_val(val, 0, 255); + mutex_lock(&data->lock); +- data->pwm[channel] = val; + err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), +- data->pwm[channel]); ++ val); ++ if (!err) ++ data->pwm[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_pwm_enable: +@@ -855,10 +858,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + val--; + + mutex_lock(&data->lock); +- data->pwm_automatic[channel] = val; + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel), + pwm_auto_reg_mask, + val ? pwm_auto_reg_mask : 0); ++ if (!err) ++ data->pwm_automatic[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_pwm_freq: +-- +2.53.0 + diff --git a/queue-5.15/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch b/queue-5.15/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch new file mode 100644 index 0000000000..e35da3c52a --- /dev/null +++ b/queue-5.15/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch @@ -0,0 +1,80 @@ +From 5d68d9a82b697d1871b2d53bc1a215a03068b5ad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:23 -0300 +Subject: hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ] + +If the fan data becomes 0 between the FAN_DATA_VALID() check and the +FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash +due to a race with a concurrent update of the cached fan value. + +Fix a TOCTOU issue by reading fan data once. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/ +Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 23 ++++++++++------------- + 1 file changed, 10 insertions(+), 13 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 3fcc10d0d3639..394f0aba3493a 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev, + static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val) + { + struct adt7470_data *data = adt7470_update_device(dev); ++ u16 fan_data; + + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (attr) { + case hwmon_fan_input: +- if (FAN_DATA_VALID(data->fan[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan[channel]); + break; + case hwmon_fan_min: +- if (FAN_DATA_VALID(data->fan_min[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan_min[channel]); + break; + case hwmon_fan_max: +- if (FAN_DATA_VALID(data->fan_max[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan_max[channel]); + break; + case hwmon_fan_alarm: + *val = !!(data->alarm & FAN_ALARM_BIT(channel)); +- break; ++ return 0; + default: + return -EOPNOTSUPP; + } + ++ if (FAN_DATA_VALID(fan_data)) ++ *val = FAN_PERIOD_TO_RPM(fan_data); ++ else ++ *val = 0; ++ + return 0; + } + +-- +2.53.0 + diff --git a/queue-5.15/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch b/queue-5.15/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch new file mode 100644 index 0000000000..4d2fde0910 --- /dev/null +++ b/queue-5.15/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch @@ -0,0 +1,115 @@ +From e10e44ad1dfc794e4473e26ed5d4e9b2f6004d2d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:17 -0300 +Subject: hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ] + +During adt7470_read_temperatures(), the driver temporarily switches +the PWM channels to manual mode, performs the temperature collection, +and then restores the original configuration registers. + +However, if an I2C transaction fails at any point after entering manual +mode, the function aborts and returns immediately. This leaves the +configuration registers un-restored, permanently trapping the fans in +manual mode. + +Introduce a recovery path to ensure that the original PWM configuration +registers are always restored, even when intermediate I2C operations +fail. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++----------- + 1 file changed, 29 insertions(+), 11 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index c67cd037a93fd..57c78e82a5f27 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in + /* Probe for temperature sensors. Assumes lock is held */ + static int adt7470_read_temperatures(struct adt7470_data *data) + { +- unsigned long res; ++ struct device *dev = regmap_get_device(data->regmap); ++ u8 pwm[ADT7470_FAN_COUNT]; + unsigned int pwm_cfg[2]; +- int err; ++ unsigned long res; ++ int err, err2; + int i; +- u8 pwm[ADT7470_FAN_COUNT]; + + /* save pwm[1-4] config register */ + err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); +@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data) + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), + ADT7470_PWM_AUTO_MASK, 0); + if (err < 0) +- return err; ++ goto out_restore; + + /* write pwm control to whatever it was */ + err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], + ADT7470_PWM_COUNT); + if (err < 0) +- return err; ++ goto out_restore; + + /* start reading temperature sensors */ + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); + if (err < 0) +- return err; ++ goto out_restore; + + /* Delay is 200ms * number of temp sensors. */ + res = msleep_interruptible((data->num_temp_sensors >= 0 ? +@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data) + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, 0); + if (err < 0) +- return err; ++ goto out_restore; + ++out_restore: + /* restore pwm[1-4] config registers */ +- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); +- if (err < 0) +- return err; +- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); ++ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); ++ if (err2 < 0) { ++ dev_warn_ratelimited(dev, ++ "failed to restore PWM{1,2} config (%d)\n", ++ err2); ++ ++ if (!err) ++ err = err2; ++ } ++ ++ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); ++ if (err2 < 0) { ++ dev_warn_ratelimited(dev, ++ "failed to restore PWM{3,4} config (%d)\n", ++ err2); ++ ++ if (!err) ++ err = err2; ++ } ++ + if (err < 0) + return err; + +-- +2.53.0 + diff --git a/queue-5.15/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-5.15/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch new file mode 100644 index 0000000000..9131f2131d --- /dev/null +++ b/queue-5.15/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch @@ -0,0 +1,57 @@ +From 678bec18a9226b2e37b8f0df11b04809b8ed4972 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:24 -0300 +Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ] + +In pwm_auto_temp_store(), the parsed user input was missing bounds +checks, allowing values > 0xF to overflow into the adjacent channel's +bits. Furthermore, the value was being incorrectly written to the +pwm_automatic state array instead of pwm_auto_temp. + +Fix this by rejecting values > 0xF with -EINVAL, and assigning the +value to the correct array only after a successful I2C write. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 394f0aba3493a..e4be1750d9b60 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -1057,8 +1057,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + if (temp < 0) + return temp; + ++ if (temp > 0xF) ++ return -EINVAL; ++ + mutex_lock(&data->lock); +- data->pwm_automatic[attr->index] = temp; + + if (!(attr->index % 2)) { + mask = 0xF0; +@@ -1069,6 +1071,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + } + + err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); ++ if (!err) ++ data->pwm_auto_temp[attr->index] = temp; ++ + mutex_unlock(&data->lock); + + return err < 0 ? err : count; +-- +2.53.0 + diff --git a/queue-5.15/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-5.15/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch new file mode 100644 index 0000000000..22732211d8 --- /dev/null +++ b/queue-5.15/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch @@ -0,0 +1,62 @@ +From de570a25c13e2da344bd92a0f2ec1c266713b2fa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:20 -0300 +Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks + +From: Luiz Angelo Daros de Luca + +[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ] + +The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are +currently defined with swapped bit values. + +According to Table 22 of the ADT7470 datasheet, the Fan Control Mode +Configuration for register 0x69 follows the exact same bit position +layout as register 0x68: +- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80 +- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40 +- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80 +- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40 + +Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40. + +This typo did not cause any functional bugs because these specific +macros are never referenced in the driver code. Instead, the driver +correctly applies the configuration by relying on the modulo parity of +the channel index (e.g., `channel % 2`) to selectively apply either +ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40). +Since the bit layout is identical between the two configuration +registers, the hardware is currently configured correctly. + +Fix the macro definitions to reflect the datasheet accurately and +prevent future bugs or confusion during code review and refactoring. +As this is a purely cosmetic fix with no functional impact, a backport +to stable kernels is not necessary. + +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 68658cb566a1a..8226213f768d5 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + #define ADT7470_PWM1_AUTO_MASK 0x80 + #define ADT7470_PWM_AUTO_MASK 0xC0 + #define ADT7470_REG_PWM34_CFG 0x69 +-#define ADT7470_PWM3_AUTO_MASK 0x40 +-#define ADT7470_PWM4_AUTO_MASK 0x80 ++#define ADT7470_PWM4_AUTO_MASK 0x40 ++#define ADT7470_PWM3_AUTO_MASK 0x80 + #define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A + #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D + #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E +-- +2.53.0 + diff --git a/queue-5.15/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch b/queue-5.15/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch new file mode 100644 index 0000000000..89a3a28daa --- /dev/null +++ b/queue-5.15/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch @@ -0,0 +1,71 @@ +From cf20052fa4b1f94fe50968b1a2b69fdd96ee3454 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:21 -0300 +Subject: hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read() + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ] + +During the conversion the alarm callback started interpreting the +channel index as an alarm bitmask, resulting in incorrect alarm +reporting. Compute the proper alarm bit instead. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org +Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 19 +++++++++++++++++-- + 1 file changed, 17 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 0874d3684fbce..68658cb566a1a 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + + #define ALARM2(x) ((x) << 8) + ++/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and ++ * 0x72, or BIT(0)..BIT(6) of data->alarm. ++ * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and ++ * 0x73, or BIT(8)..BIT(10) of data->alarm. ++ */ ++#define TEMP_ALARM_BIT(ch) ({ \ ++ typeof(ch) _ch = (ch); \ ++ (1 << (_ch < 7 ? _ch : _ch + 1)); \ ++}) ++ ++/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in ++ * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm. ++ */ ++#define FAN_ALARM_BIT(ch) (1 << (12 + (ch))) ++ + #define ADT7470_VENDOR 0x41 + #define ADT7470_DEVICE 0x70 + /* datasheet only mentions a revision 2 */ +@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va + *val = 1000 * data->temp_max[channel]; + break; + case hwmon_temp_alarm: +- *val = !!(data->alarm & channel); ++ *val = !!(data->alarm & TEMP_ALARM_BIT(channel)); + break; + default: + return -EOPNOTSUPP; +@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val + *val = 0; + break; + case hwmon_fan_alarm: +- *val = !!(data->alarm & (1 << (12 + channel))); ++ *val = !!(data->alarm & FAN_ALARM_BIT(channel)); + break; + default: + return -EOPNOTSUPP; +-- +2.53.0 + diff --git a/queue-5.15/hwmon-adt7470-use-cached-pwm-frequency-value.patch b/queue-5.15/hwmon-adt7470-use-cached-pwm-frequency-value.patch new file mode 100644 index 0000000000..eda88e9f8a --- /dev/null +++ b/queue-5.15/hwmon-adt7470-use-cached-pwm-frequency-value.patch @@ -0,0 +1,111 @@ +From 34e6cc94a320c2d27679f99f01275b1822d35ece Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:22 -0300 +Subject: hwmon: (adt7470) Use cached PWM frequency value + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ] + +adt7470_pwm_read() currently ignores failures returned by +pwm1_freq_get(). If the register read fails, the negative error code is +returned through *val while the function itself reports success, +potentially exposing a negative PWM frequency through sysfs. + +Fix this by using the cached PWM frequency maintained by the driver, +eliminating the register access from the read path. + +Apart from the corrected error propagation and using the cached value, +no functional change is intended. + +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 8226213f768d5..3fcc10d0d3639 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -182,6 +182,7 @@ struct adt7470_data { + u8 pwm_min[ADT7470_PWM_COUNT]; + s8 pwm_tmin[ADT7470_PWM_COUNT]; + u8 pwm_auto_temp[ADT7470_PWM_COUNT]; ++ u32 pwm_freq; + + struct task_struct *auto_update; + unsigned int auto_update_interval; +@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev, + } + + /* These are the valid PWM frequencies to the nearest Hz */ +-static const int adt7470_freq_map[] = { ++static const u32 adt7470_freq_map[] = { + 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 + }; + +@@ -804,7 +805,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val + *val = 1 + data->pwm_automatic[channel]; + break; + case hwmon_pwm_freq: +- *val = pwm1_freq_get(dev); ++ *val = data->pwm_freq; + break; + default: + return -EOPNOTSUPP; +@@ -817,12 +818,14 @@ static int pwm1_freq_set(struct device *dev, long freq) + { + struct adt7470_data *data = dev_get_drvdata(dev); + unsigned int low_freq = ADT7470_CFG_LF; ++ u32 closest_freq; + int index; + int err; + + /* Round the user value given to the closest available frequency */ + index = find_closest(freq, adt7470_freq_map, + ARRAY_SIZE(adt7470_freq_map)); ++ closest_freq = adt7470_freq_map[index]; + + if (index >= 8) { + index -= 8; +@@ -840,6 +843,10 @@ static int pwm1_freq_set(struct device *dev, long freq) + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, + ADT7470_FREQ_MASK, + index << ADT7470_FREQ_SHIFT); ++ if (err < 0) ++ goto out; ++ ++ data->pwm_freq = closest_freq; + out: + mutex_unlock(&data->lock); + +@@ -1293,6 +1300,7 @@ static int adt7470_probe(struct i2c_client *client) + struct device *dev = &client->dev; + struct adt7470_data *data; + struct device *hwmon_dev; ++ int freq_val; + int err; + + data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); +@@ -1317,6 +1325,14 @@ static int adt7470_probe(struct i2c_client *client) + if (err < 0) + return err; + ++ freq_val = pwm1_freq_get(dev); ++ if (freq_val <= 0) { ++ err = freq_val < 0 ? freq_val : -EINVAL; ++ return err; ++ } ++ ++ data->pwm_freq = (u32)freq_val; ++ + /* Register sysfs hooks */ + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &adt7470_chip_info, +-- +2.53.0 + diff --git a/queue-5.15/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-5.15/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch new file mode 100644 index 0000000000..f1def2785c --- /dev/null +++ b/queue-5.15/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch @@ -0,0 +1,74 @@ +From 6469377cc6c7c1301d15058cc066d01f7537cd17 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 13:35:37 -0700 +Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guenter Roeck + +[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ] + +Sashiko reports: + +During initialization of the nct6116 chip, the driver sets data->pwm_num +to 5. However, it assigns several NCT6106 register arrays (such as +NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and +NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP. +These arrays only contain 3 elements. + +In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If +data->has_pwm has bits 3 or 4 set (which is structurally possible for +nct6116), the loop attempts to read elements at index 3 and 4 from these +3-element arrays. This results in a global out-of-bounds read, which can +be caught by KASAN. + +Furthermore, the driver uses these garbage out-of-bounds values as +hardware register addresses for subsequent read and write operations. This +leads to invalid hardware register access, potentially causing hardware +misconfiguration or system crashes. + +The underlying problem is that the chip does support up to five fan +control channels, but only the first three support weight control. +Fix the problem by extending the affected weight register arrays with +zeroed fields. The driver uses zeroed register addresses to determine +if a register is supported or not, and skips accesses for unsupported +registers. + +Reported-by: Sashiko +Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") +Cc: Björn Gerhart +Cc: Florian Bezdeka +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c +index 71cfc1c5bd12e..5428b5b4043da 100644 +--- a/drivers/hwmon/nct6775.c ++++ b/drivers/hwmon/nct6775.c +@@ -851,12 +851,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 }; + + static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 }; + +-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 }; + + static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 }; + static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 }; +-- +2.53.0 + diff --git a/queue-5.15/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-5.15/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch new file mode 100644 index 0000000000..7016625515 --- /dev/null +++ b/queue-5.15/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch @@ -0,0 +1,39 @@ +From ec2bf7ef570fa7c797519f2d377fa458eeb707af Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 08:41:40 -0700 +Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data() + +From: Guenter Roeck + +[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ] + +pmbus_update_byte_data() is supposed to return a negative error code or 0. +However, if no change is made to the register, it actually returns the +register value. This can result in problems if the calling code explicitly +expects to see an error code or 0. + +Fix it to return 0 on success or the error code as expected. + +Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/pmbus/pmbus_core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c +index 1ef214a8a01b7..84497fd177d81 100644 +--- a/drivers/hwmon/pmbus/pmbus_core.c ++++ b/drivers/hwmon/pmbus/pmbus_core.c +@@ -419,7 +419,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + if (tmp != rv) + rv = _pmbus_write_byte_data(client, page, reg, tmp); + +- return rv; ++ return rv < 0 ? rv : 0; + } + EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS); + +-- +2.53.0 + diff --git a/queue-5.15/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-5.15/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch new file mode 100644 index 0000000000..c13409db9e --- /dev/null +++ b/queue-5.15/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch @@ -0,0 +1,64 @@ +From 4e49148fbf44d1669b79907236b050274ebb0552 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:03 -0400 +Subject: keys: fix out-of-bounds read in keyring_get_key_chunk() + +From: Michael Bommarito + +[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ] + +For description-level chunks keyring_get_key_chunk() advances the read +pointer by level * sizeof(long) past the inline prefix but only +bounds-checks the prefix, so a long enough key description is read past +its kmemdup(desc, desc_len + 1) allocation. Compute the full byte +offset and bounds-check the description against it before reading. + +The walk only reaches a description-level chunk when two keys collide +through the hash, x, type and domain_tag chunks, so this is reached from +an unprivileged add_key(2) with a crafted pair of same-type keys whose +index hashes collide; KASAN reports a slab-out-of-bounds read. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index 08aaa9429ab36..85791380ca724 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + unsigned long chunk = 0; + const u8 *d; + int desc_len = index_key->desc_len, n = sizeof(chunk); ++ unsigned int offset; + + level /= ASSOC_ARRAY_KEY_CHUNK_SIZE; + switch (level) { +@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + return (unsigned long)index_key->domain_tag; + default: + level -= 4; +- if (desc_len <= sizeof(index_key->desc)) ++ offset = sizeof(index_key->desc) + level * sizeof(long); ++ if (desc_len <= offset) + return 0; + +- d = index_key->description + sizeof(index_key->desc); +- d += level * sizeof(long); +- desc_len -= sizeof(index_key->desc); ++ d = index_key->description + offset; ++ desc_len -= offset; + if (desc_len > n) + desc_len = n; + do { +-- +2.53.0 + diff --git a/queue-5.15/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-5.15/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch new file mode 100644 index 0000000000..3ba161d959 --- /dev/null +++ b/queue-5.15/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch @@ -0,0 +1,63 @@ +From e7305369ef84b059d231c848d13215cdeb30d8be Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:04 -0400 +Subject: keys: make keyring key-chunk byte order agree with + keyring_diff_objects() + +From: Michael Bommarito + +[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ] + +keyring_get_key_chunk() loads description bytes into the index chunk low +address first, while keyring_diff_objects() numbers the first differing +bit from the low end and folds the absolute byte index into the level +without removing the inline-prefix offset the level already carries. +The two disagree on byte order and bit position, so the array can be +told two keys first differ at a bit that does not differ in the chunk +the walker uses, letting crafted descriptions collide into one node. + +Load the chunk in the order keyring_diff_objects() assumes and drop the +inline-prefix length when folding the byte index into the level. This +only changes the in-memory ordering used to place keys within a keyring; +add, search and read of non-colliding keys are unaffected. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index 85791380ca724..fc26d339a962b 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + desc_len -= offset; + if (desc_len > n) + desc_len = n; ++ d += desc_len; + do { + chunk <<= 8; +- chunk |= *d++; ++ chunk |= *--d; + } while (--desc_len > 0); + return chunk; + } +@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data) + return -1; + + differ_plus_i: +- level += i; ++ level += i - (int)sizeof(a->desc); + differ: + i = level * 8 + __ffs(seg_a ^ seg_b); + return i; +-- +2.53.0 + diff --git a/queue-5.15/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch b/queue-5.15/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch new file mode 100644 index 0000000000..e2fbbf8d34 --- /dev/null +++ b/queue-5.15/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch @@ -0,0 +1,40 @@ +From fdfb9825660ec246a21e0acdf6b7d51ffcbda561 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 06:26:05 +0000 +Subject: net: bridge: mrp: fix Option TLV length in MRP_Test frames + +From: David Corvaglia + +[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ] + +oui is a pointer, so sizeof(oui) is the pointer size. The MRA +Option TLV thus advertises a wrong length (15 vs 10 on x86_64), +causing misparsing of the frame on peers. Fix is to replace +with sizeof(*oui). + +Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA") +Signed-off-by: David Corvaglia +Acked-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_mrp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c +index fd2de35ffb3cf..5fd22bb4f5b60 100644 +--- a/net/bridge/br_mrp.c ++++ b/net/bridge/br_mrp.c +@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp, + struct br_mrp_oui_hdr *oui = NULL; + u8 length; + +- length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) + ++ length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) + + MRP_OPT_PADDING; + br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length); + +-- +2.53.0 + diff --git a/queue-5.15/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-5.15/net-phylink-put-link_gpio-if-phylink_create-fails.patch new file mode 100644 index 0000000000..63184447c7 --- /dev/null +++ b/queue-5.15/net-phylink-put-link_gpio-if-phylink_create-fails.patch @@ -0,0 +1,92 @@ +From 80637c0564dba3ad8b2ba714b96359f185d5cbfa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 17:08:05 +0200 +Subject: net: phylink: put link_gpio if phylink_create fails + +From: Christian Marangi + +[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ] + +In phylink_create() if phylink_register_sfp() returns an error, link_gpio +obtained by phylink_parse_fixedlink() is never released. While this is a +very unlikely scenario, it's worth to fix/handle this. + +This was present from the very first implementation of phylink but got +relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to +bridge between network devices and sfp cages") where additional function +were added after phylink_parse_fixedlink() making the release of link_gpio +needed if such additional function errored out. + +While at it, restructure the exit condition of phylink_create() with the +goto pattern to reduce code duplication on handling error conditions. + +Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages") +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/phy/phylink.c | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 514e7f9e0339c..84e3953090bee 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -857,8 +857,8 @@ struct phylink *phylink_create(struct phylink_config *config, + } else if (config->type == PHYLINK_DEV) { + pl->dev = config->dev; + } else { +- kfree(pl); +- return ERR_PTR(-EINVAL); ++ ret = -EINVAL; ++ goto free_pl; + } + + pl->phy_state.interface = iface; +@@ -881,28 +881,29 @@ struct phylink *phylink_create(struct phylink_config *config, + phylink_validate(pl, pl->supported, &pl->link_config); + + ret = phylink_parse_mode(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto free_pl; + + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { + ret = phylink_parse_fixedlink(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + } + + pl->cur_link_an_mode = pl->cfg_link_an_mode; + + ret = phylink_register_sfp(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + + return pl; ++ ++release_link_gpio: ++ if (pl->link_gpio) ++ gpiod_put(pl->link_gpio); ++free_pl: ++ kfree(pl); ++ return ERR_PTR(ret); + } + EXPORT_SYMBOL_GPL(phylink_create); + +-- +2.53.0 + diff --git a/queue-5.15/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-5.15/net-sxgbe-check-descriptor-ring-allocation-failures.patch new file mode 100644 index 0000000000..cd830e141e --- /dev/null +++ b/queue-5.15/net-sxgbe-check-descriptor-ring-allocation-failures.patch @@ -0,0 +1,48 @@ +From 6e99d03d3acea63552d7409d0cdca705a49df26f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:20 +0800 +Subject: net: sxgbe: check descriptor ring allocation failures + +From: Chenguang Zhao + +[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ] + +sxgbe_open() ignores the return value of init_dma_desc_rings() and +continues to program DMA with invalid ring addresses when allocation +fails. Check the return value and disconnect the PHY on failure. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index 8c3aad051c2e5..f453c546478a0 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -1077,7 +1077,9 @@ static int sxgbe_open(struct net_device *dev) + priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE); + priv->tx_tc = TC_DEFAULT; + priv->rx_tc = TC_DEFAULT; +- init_dma_desc_rings(dev); ++ ret = init_dma_desc_rings(dev); ++ if (ret) ++ goto init_phy_error; + + /* DMA initialization and SW reset */ + ret = sxgbe_init_dma_engine(priv); +@@ -1186,6 +1188,7 @@ static int sxgbe_open(struct net_device *dev) + + init_error: + free_dma_desc_resources(priv); ++init_phy_error: + if (dev->phydev) + phy_disconnect(dev->phydev); + phy_error: +-- +2.53.0 + diff --git a/queue-5.15/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-5.15/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch new file mode 100644 index 0000000000..28ddb27569 --- /dev/null +++ b/queue-5.15/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch @@ -0,0 +1,50 @@ +From e24071c4bf4c3d5709b9ce082c242647b5f9bf6e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:19 +0800 +Subject: net: sxgbe: free TX rings on RX allocation failure + +From: Chenguang Zhao + +[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ] + +When RX descriptor ring allocation fails, init_dma_desc_rings() only +frees the partially allocated RX rings and returns. The TX rings that +were allocated earlier in the same function are leaked. + +Rearrange error labels to clean up TX rings upon RX failures. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index 1fe687b594f9d..8c3aad051c2e5 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -599,14 +599,13 @@ static int init_dma_desc_rings(struct net_device *netd) + + return 0; + +-txalloc_err: +- while (queue_num--) +- free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); +- return ret; +- + rxalloc_err: + while (queue_num--) + free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize); ++ queue_num = SXGBE_TX_QUEUES; ++txalloc_err: ++ while (queue_num--) ++ free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); + return ret; + } + +-- +2.53.0 + diff --git a/queue-5.15/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-5.15/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch new file mode 100644 index 0000000000..6f4303d654 --- /dev/null +++ b/queue-5.15/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch @@ -0,0 +1,96 @@ +From 6c8d56edaeff059eba36ee76f727da192e545ccd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 16:42:01 -0700 +Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in + sip_help_tcp() + +From: Xiang Mei + +[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ] + +sip_help_tcp() stores the size change of each NAT-rewritten SIP message +in s16 diff and accumulates it in s16 tdiff, but a single message can +grow by more than S16_MAX while the packet stays under the 65535 +enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long +Contact list expands the message by tens of kilobytes. diff then wraps, +and "datalen = datalen + diff - msglen" yields a huge unsigned datalen, +so the next iteration's ct_sip_get_header() reads past the linearized skb +tail. + +Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the +65535 byte packet limit, and the seqadj core is already s32 +(nf_ct_seqadj_set() takes s32), so no previously accepted input is +rejected. + + BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25 + ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694) + nf_confirm (net/netfilter/nf_conntrack_proto.c:183) + nf_hook_slow (net/netfilter/core.c:619) + ip6_output (net/ipv6/ip6_output.c:246) + ip6_forward (net/ipv6/ip6_output.c:690) + ipv6_rcv (net/ipv6/ip6_input.c:351) + __netif_receive_skb_one_core (net/core/dev.c:6212) + process_backlog (net/core/dev.c:6676) + __napi_poll (net/core/dev.c:7735) + net_rx_action (net/core/dev.c:7955) + handle_softirqs (kernel/softirq.c:622) + run_ksoftirqd (kernel/softirq.c:1076) + ... + +Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support") +Reported-by: Weiming Shi +Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Xiang Mei +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/nf_conntrack_sip.h | 2 +- + net/netfilter/nf_conntrack_sip.c | 2 +- + net/netfilter/nf_nat_sip.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h +index c620521c42bc6..dfb9708f53404 100644 +--- a/include/linux/netfilter/nf_conntrack_sip.h ++++ b/include/linux/netfilter/nf_conntrack_sip.h +@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks { + unsigned int *datalen); + + void (*seq_adjust)(struct sk_buff *skb, +- unsigned int protoff, s16 off); ++ unsigned int protoff, s32 off); + + unsigned int (*expect)(struct sk_buff *skb, + unsigned int protoff, +diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c +index 4326d5ea0400d..0f6147ea9e67b 100644 +--- a/net/netfilter/nf_conntrack_sip.c ++++ b/net/netfilter/nf_conntrack_sip.c +@@ -1622,7 +1622,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, + unsigned int matchoff, matchlen; + unsigned int msglen, origlen; + const char *dptr, *end; +- s16 diff, tdiff = 0; ++ s32 diff, tdiff = 0; + int ret = NF_ACCEPT; + unsigned long clen; + bool term; +diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c +index 76a8bbb44951c..7687d3d15df22 100644 +--- a/net/netfilter/nf_nat_sip.c ++++ b/net/netfilter/nf_nat_sip.c +@@ -313,7 +313,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, + } + + static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, +- s16 off) ++ s32 off) + { + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); +-- +2.53.0 + diff --git a/queue-5.15/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-5.15/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch new file mode 100644 index 0000000000..b2cec62f90 --- /dev/null +++ b/queue-5.15/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch @@ -0,0 +1,69 @@ +From 954ede6d2dd9f83fd38dc5fa820388d016281e72 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:15:23 +0000 +Subject: netfilter: nft_payload: fix mask build for partial field offload + +From: Xiang Mei (Microsoft) + +[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ] + +nft_payload_offload_mask() builds the offload match mask for a payload +expression that covers only part of a header field. For a partial IPv6 +address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which +is undefined on the 32-bit int operand. It also trims only one word, so +the remaining words stay 0xffffffff (and when priv_len is a multiple of 4 +the trim is skipped entirely), leaving the mask covering more bytes than +the rule matches. + + UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20 + shift exponent 120 is too large for 32-bit type 'int' + ... + +The match is byte-granular and struct nft_data is zero-initialised, so the +correct mask is simply the first priv_len bytes set to 0xff. Set those +bytes directly and drop the word/shift trimming; this removes the undefined +shift and no longer over-masks the trailing bytes. + +Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes") +Reported-by: AutonomousCodeSecurity@microsoft.com +Signed-off-by: Xiang Mei (Microsoft) +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/nft_payload.c | 12 +----------- + 1 file changed, 1 insertion(+), 11 deletions(-) + +diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c +index dbafb964bd706..f24d1276f5315 100644 +--- a/net/netfilter/nft_payload.c ++++ b/net/netfilter/nft_payload.c +@@ -216,9 +216,7 @@ static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr) + static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + u32 priv_len, u32 field_len) + { +- unsigned int remainder, delta, k; + struct nft_data mask = {}; +- __be32 remainder_mask; + + if (priv_len == field_len) { + memset(®->mask, 0xff, priv_len); +@@ -227,15 +225,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + return false; + } + +- memset(&mask, 0xff, field_len); +- remainder = priv_len % sizeof(u32); +- if (remainder) { +- k = priv_len / sizeof(u32); +- delta = field_len - priv_len; +- remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1)); +- mask.data[k] = (__force u32)remainder_mask; +- } +- ++ memset(&mask, 0xff, priv_len); + memcpy(®->mask, &mask, field_len); + + return true; +-- +2.53.0 + diff --git a/queue-5.15/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-5.15/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch new file mode 100644 index 0000000000..8c19461a33 --- /dev/null +++ b/queue-5.15/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch @@ -0,0 +1,83 @@ +From e96475015fdad126bead7f247760addf327575c9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 22:02:46 +0200 +Subject: netfilter: xt_hashlimit: validate hashtable supports + XT_HASHLIMIT_RATE_MATCH + +From: Pablo Neira Ayuso + +[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ] + +The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the +dsthash_ent structure which represents an entry in the hashtable. There +is a union area which uses a different layout to express the rate match +mode. + +Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode +flag is requested by two or more different rules that refer to the same +hashtable. Otherwise, uninitialized access to the burst field in the +union is possible. + +Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by +revision less than 3 too. + +Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode") +Reported-and-tested-by: Talha Berk Arslan +Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/ +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/xt_hashlimit.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c +index 9c5cfd74a0ee4..26918fa575afe 100644 +--- a/net/netfilter/xt_hashlimit.c ++++ b/net/netfilter/xt_hashlimit.c +@@ -118,6 +118,7 @@ struct xt_hashlimit_htable { + refcount_t use; + u_int8_t family; + bool rnd_initialized; ++ bool ratematch; + + struct hashlimit_cfg3 cfg; /* config */ + +@@ -325,6 +326,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg, + vfree(hinfo); + return -ENOMEM; + } ++ hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH); + spin_lock_init(&hinfo->lock); + + switch (revision) { +@@ -868,7 +870,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + } + + /* Check for overflow. */ +- if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (revision < 3) ++ return -EINVAL; ++ + if (cfg->avg == 0 || cfg->avg > U32_MAX) { + pr_info_ratelimited("invalid rate\n"); + return -ERANGE; +@@ -901,6 +906,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + mutex_unlock(&hashlimit_mutex); + return ret; + } ++ } else { ++ if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH && ++ !(*hinfo)->ratematch) || ++ (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) && ++ (*hinfo)->ratematch)) { ++ mutex_unlock(&hashlimit_mutex); ++ htable_put(*hinfo); ++ return -EINVAL; ++ } + } + mutex_unlock(&hashlimit_mutex); + +-- +2.53.0 + diff --git a/queue-5.15/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-5.15/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch new file mode 100644 index 0000000000..04faa8d3b9 --- /dev/null +++ b/queue-5.15/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch @@ -0,0 +1,42 @@ +From 416e84a3b59dfdf8cbf124d9a25b32268dc117c5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 12:58:31 +0530 +Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop + +From: Suman Ghosh + +[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ] + +During link down event, we were doing netif_tx_stop_all_queues() first +and then netif_carrier_off(). This can cause a potential race since +carrier is still on during down event. This patch reverse the calling +order to fix the issue. + +Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications") +Signed-off-by: Suman Ghosh +Signed-off-by: Ratheesh Kannoth +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +index 9d39b9fc2f6a8..a8a97197d6b76 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +@@ -849,8 +849,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf) + netif_carrier_on(netdev); + netif_tx_start_all_queues(netdev); + } else { +- netif_tx_stop_all_queues(netdev); + netif_carrier_off(netdev); ++ netif_tx_stop_all_queues(netdev); + } + } + +-- +2.53.0 + diff --git a/queue-5.15/phy-zynqmp-allow-variation-in-refclk-rate.patch b/queue-5.15/phy-zynqmp-allow-variation-in-refclk-rate.patch new file mode 100644 index 0000000000..68eddbb0c3 --- /dev/null +++ b/queue-5.15/phy-zynqmp-allow-variation-in-refclk-rate.patch @@ -0,0 +1,41 @@ +From d4ff91fda4a620b8a36d73e156c2bff19d9df866 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 11 Jul 2023 15:45:39 -0400 +Subject: phy: zynqmp: Allow variation in refclk rate + +From: Sean Anderson + +[ Upstream commit 76009ee76e05e30e29aade02e788aebe9ce9ffd2 ] + +Due to limited available frequency ratios, the reference clock rate may +not be exactly the same as the required rate. Allow a small (100 ppm) +deviation. + +Signed-off-by: Sean Anderson +Link: https://lore.kernel.org/r/20230711194542.898230-1-sean.anderson@seco.com +Signed-off-by: Vinod Koul +Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()") +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index ac9a9124a36de..fb67db31e94a3 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -957,7 +957,10 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) + rate = clk_get_rate(clk); + + for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { +- if (rate == ssc_lookup[i].refclk_rate) { ++ /* Allow an error of 100 ppm */ ++ unsigned long error = ssc_lookup[i].refclk_rate / 10000; ++ ++ if (abs(rate - ssc_lookup[i].refclk_rate) < error) { + gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i]; + break; + } +-- +2.53.0 + diff --git a/queue-5.15/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch b/queue-5.15/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch new file mode 100644 index 0000000000..38cdfc7b9e --- /dev/null +++ b/queue-5.15/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch @@ -0,0 +1,76 @@ +From e4ba857f57005ae7d58b46ac82c88318c88ec01d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 21:08:30 +0530 +Subject: phy: zynqmp: fix clock error handling in xpsgtr_phy_init() + +From: Radhey Shyam Pandey + +[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ] + +Propagate clk_prepare_enable() failures to the caller instead of +returning success, and disable the reference clock on initialization +error paths to avoid leaking clock references when phy_exit() is not +called. + +Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save") +Signed-off-by: Radhey Shyam Pandey +Reviewed-by: Michal Simek +Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index a476f1343253f..4048aff0798ec 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -644,12 +644,13 @@ static int xpsgtr_phy_init(struct phy *phy) + { + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; +- int ret = 0; ++ int ret; + + mutex_lock(>r_dev->gtr_mutex); + + /* Configure and enable the clock when peripheral phy_init call */ +- if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk])) ++ ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]); ++ if (ret) + goto out; + + /* Skip initialization if not required. */ +@@ -659,7 +660,7 @@ static int xpsgtr_phy_init(struct phy *phy) + if (gtr_dev->tx_term_fix) { + ret = xpsgtr_phy_tx_term_fix(gtr_phy); + if (ret < 0) +- goto out; ++ goto out_disable_clk; + + gtr_dev->tx_term_fix = false; + } +@@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy) + */ + ret = xpsgtr_configure_pll(gtr_phy); + if (ret) +- goto out; ++ goto out_disable_clk; + + xpsgtr_lane_set_protocol(gtr_phy); + +@@ -691,6 +692,10 @@ static int xpsgtr_phy_init(struct phy *phy) + break; + } + ++ goto out; ++ ++out_disable_clk: ++ clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]); + out: + mutex_unlock(>r_dev->gtr_mutex); + return ret; +-- +2.53.0 + diff --git a/queue-5.15/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch b/queue-5.15/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch new file mode 100644 index 0000000000..a4cef0f22d --- /dev/null +++ b/queue-5.15/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch @@ -0,0 +1,56 @@ +From ae588d51e2503f8cfebd8672ea6240d4f3c81fdc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 21:08:31 +0530 +Subject: phy: zynqmp: fix runtime PM leak on probe allocation failure + +From: Radhey Shyam Pandey + +[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ] + +Allocate saved_regs before pm_runtime_resume_and_get() so a +devm_kmalloc() failure does not leave an unreleased runtime PM usage +counter. + +Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume") +Signed-off-by: Radhey Shyam Pandey +Reviewed-by: Michal Simek +Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index 4048aff0798ec..b1bbc859b6ccb 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -1045,6 +1045,12 @@ static int xpsgtr_probe(struct platform_device *pdev) + return PTR_ERR(provider); + } + ++ gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, ++ sizeof(save_reg_address), ++ GFP_KERNEL); ++ if (!gtr_dev->saved_regs) ++ return -ENOMEM; ++ + pm_runtime_set_active(gtr_dev->dev); + pm_runtime_enable(gtr_dev->dev); + +@@ -1054,12 +1060,6 @@ static int xpsgtr_probe(struct platform_device *pdev) + return ret; + } + +- gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, +- sizeof(save_reg_address), +- GFP_KERNEL); +- if (!gtr_dev->saved_regs) +- return -ENOMEM; +- + return 0; + } + +-- +2.53.0 + diff --git a/queue-5.15/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch b/queue-5.15/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch new file mode 100644 index 0000000000..d8203dab66 --- /dev/null +++ b/queue-5.15/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch @@ -0,0 +1,172 @@ +From 8bf4e5988c55d0e8f7a44bce228658a583f85527 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 28 Apr 2025 08:35:47 +0200 +Subject: phy-zynqmp: Postpone getting clock rate until actually needed + +From: Mike Looijmans + +[ Upstream commit 065d5885f6180c534b7b176847b3e008f4e11850 ] + +At probe time the driver would display the following error and abort: + xilinx-psgtr fd400000.phy: Invalid rate 0 for reference clock 0 + +At probe time, the associated GTR driver (e.g. SATA or PCIe) hasn't +initialized the clock yet, so clk_get_rate() likely returns 0 if the clock +is programmable. So this driver only works if the clock is fixed. + +The PHY driver doesn't need to know the clock frequency at probe yet, so +wait until the associated driver initializes the lane before requesting the +clock rate setting. + +In addition to allowing the driver to be used with programmable clocks, +this also reduces the driver's runtime memory footprint by removing an +array of pointers from struct xpsgtr_phy. + +Signed-off-by: Mike Looijmans +Acked-by: Michal Simek +Link: https://lore.kernel.org/r/20250428063648.22034-1-mike.looijmans@topic.nl +Signed-off-by: Vinod Koul +Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()") +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 70 +++++++++++++++++---------------- + 1 file changed, 37 insertions(+), 33 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index fb67db31e94a3..a476f1343253f 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -228,7 +228,6 @@ struct xpsgtr_phy { + * @siou: siou base address + * @gtr_mutex: mutex for locking + * @phys: PHY lanes +- * @refclk_sscs: spread spectrum settings for the reference clocks + * @clk: reference clocks + * @tx_term_fix: fix for GT issue + * @saved_icm_cfg0: stored value of ICM CFG0 register +@@ -241,7 +240,6 @@ struct xpsgtr_dev { + void __iomem *siou; + struct mutex gtr_mutex; /* mutex for locking */ + struct xpsgtr_phy phys[NUM_LANES]; +- const struct xpsgtr_ssc *refclk_sscs[NUM_LANES]; + struct clk *clk[NUM_LANES]; + bool tx_term_fix; + unsigned int saved_icm_cfg0; +@@ -384,13 +382,40 @@ static int xpsgtr_wait_pll_lock(struct phy *phy) + return ret; + } + ++/* Get the spread spectrum (SSC) settings for the reference clock rate */ ++static const struct xpsgtr_ssc *xpsgtr_find_sscs(struct xpsgtr_phy *gtr_phy) ++{ ++ unsigned long rate; ++ struct clk *clk; ++ unsigned int i; ++ ++ clk = gtr_phy->dev->clk[gtr_phy->refclk]; ++ rate = clk_get_rate(clk); ++ ++ for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { ++ /* Allow an error of 100 ppm */ ++ unsigned long error = ssc_lookup[i].refclk_rate / 10000; ++ ++ if (abs(rate - ssc_lookup[i].refclk_rate) < error) ++ return &ssc_lookup[i]; ++ } ++ ++ dev_err(gtr_phy->dev->dev, "Invalid rate %lu for reference clock %u\n", ++ rate, gtr_phy->refclk); ++ ++ return NULL; ++} ++ + /* Configure PLL and spread-sprectrum clock. */ +-static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) ++static int xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) + { + const struct xpsgtr_ssc *ssc; + u32 step_size; + +- ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk]; ++ ssc = xpsgtr_find_sscs(gtr_phy); ++ if (!ssc) ++ return -EINVAL; ++ + step_size = ssc->step_size; + + xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane), +@@ -432,6 +457,8 @@ static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB, + STEP_SIZE_3_MASK, (step_size & STEP_SIZE_3_MASK) | + FORCE_STEP_SIZE | FORCE_STEPS); ++ ++ return 0; + } + + /* Configure the lane protocol. */ +@@ -644,7 +671,10 @@ static int xpsgtr_phy_init(struct phy *phy) + * Configure the PLL, the lane protocol, and perform protocol-specific + * initialization. + */ +- xpsgtr_configure_pll(gtr_phy); ++ ret = xpsgtr_configure_pll(gtr_phy); ++ if (ret) ++ goto out; ++ + xpsgtr_lane_set_protocol(gtr_phy); + + switch (gtr_phy->protocol) { +@@ -854,8 +884,7 @@ static struct phy *xpsgtr_xlate(struct device *dev, + } + + refclk = args->args[3]; +- if (refclk >= ARRAY_SIZE(gtr_dev->refclk_sscs) || +- !gtr_dev->refclk_sscs[refclk]) { ++ if (refclk >= ARRAY_SIZE(gtr_dev->clk)) { + dev_err(dev, "Invalid reference clock number %u\n", refclk); + return ERR_PTR(-EINVAL); + } +@@ -931,9 +960,7 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) + { + unsigned int refclk; + +- for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) { +- unsigned long rate; +- unsigned int i; ++ for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->clk); ++refclk) { + struct clk *clk; + char name[8]; + +@@ -949,29 +976,6 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) + continue; + + gtr_dev->clk[refclk] = clk; +- +- /* +- * Get the spread spectrum (SSC) settings for the reference +- * clock rate. +- */ +- rate = clk_get_rate(clk); +- +- for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { +- /* Allow an error of 100 ppm */ +- unsigned long error = ssc_lookup[i].refclk_rate / 10000; +- +- if (abs(rate - ssc_lookup[i].refclk_rate) < error) { +- gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i]; +- break; +- } +- } +- +- if (i == ARRAY_SIZE(ssc_lookup)) { +- dev_err(gtr_dev->dev, +- "Invalid rate %lu for reference clock %u\n", +- rate, refclk); +- return -EINVAL; +- } + } + + return 0; +-- +2.53.0 + diff --git a/queue-5.15/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-5.15/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch new file mode 100644 index 0000000000..64353e940c --- /dev/null +++ b/queue-5.15/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch @@ -0,0 +1,38 @@ +From fe23e4b247477dc691ed24a0eb29352414e48f35 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:55 +0200 +Subject: powerpc/boot: Fix simpleboot CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/simpleboot.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c +index c80691d83880b..27591df41e9e8 100644 +--- a/arch/powerpc/boot/simpleboot.c ++++ b/arch/powerpc/boot/simpleboot.c +@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + /* finally, setup the timebase */ + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-5.15/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-5.15/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch new file mode 100644 index 0000000000..583c88f0d6 --- /dev/null +++ b/queue-5.15/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch @@ -0,0 +1,38 @@ +From d7184cc622ca674be9e21e5abe1aa2bc48e1fcfa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:57 +0200 +Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-akebono.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c +index e3cc2599869cc..1b529037480fb 100644 +--- a/arch/powerpc/boot/treeboot-akebono.c ++++ b/arch/powerpc/boot/treeboot-akebono.c +@@ -146,7 +146,7 @@ void platform_init(char *userdata) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-5.15/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-5.15/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch new file mode 100644 index 0000000000..b00eda4437 --- /dev/null +++ b/queue-5.15/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch @@ -0,0 +1,38 @@ +From 015067e812966a6339e3de7b6db79975cf0652d0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:56 +0200 +Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-currituck.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c +index d53e8a592f816..5b5363b74f9f3 100644 +--- a/arch/powerpc/boot/treeboot-currituck.c ++++ b/arch/powerpc/boot/treeboot-currituck.c +@@ -102,7 +102,7 @@ void platform_init(void) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-5.15/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-5.15/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch new file mode 100644 index 0000000000..559d894166 --- /dev/null +++ b/queue-5.15/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch @@ -0,0 +1,169 @@ +From f9fcfc96045136f83953029679f05810d406e35b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 12:43:11 +0200 +Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path + +From: Denis V. Lunev + +[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ] + +A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports +configured wedges the rtnetlink control plane of the whole machine: + + NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms + [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2! + [qede_recovery_handler:2665(ens6f0)]Starting a recovery process + +The recovery path deadlocks on the driver's own mutex: + + qede_sp_task + rtnl_lock() + mutex_lock(&edev->qede_lock) <- taken + qede_recovery_handler + qede_load + udp_tunnel_nic_reset_ntf + __udp_tunnel_nic_device_sync + info->sync_table == qede_udp_tunnel_sync + mutex_lock(&edev->qede_lock) <- same task: deadlock + +The mutex is not recursive, so the kworker blocks on itself with +rtnl_lock held, and neither lock is ever released. Every task that +calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6 +addrconf, sshd) blocks forever while the node still answers ping. +In a vmcore from an affected production node rtnl_mutex.owner +decodes to the very kworker blocked at the innermost mutex_lock() +above. + +Re-sync the tunnel ports from qede_sp_task() after the internal lock +is dropped, still under rtnl_lock as the udp_tunnel API requires. +This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf() +under rtnl without the internal lock. + +qede_recovery_handler() now returns whether it has successfully +reloaded an open device, and the caller re-syncs the ports only in +that case. This keeps the old gating exactly: a device that was down +or a failed recovery returns false, as those paths never reached the +udp_tunnel_nic_reset_ntf() call before either. + +This was the only user of the qede_lock()/qede_unlock() helpers, so +remove them. + +Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra") +Signed-off-by: Denis V. Lunev +CC: Andrew Lunn +CC: "David S. Miller" +CC: Eric Dumazet +CC: Jakub Kicinski +CC: Paolo Abeni +Reviewed-by: Jacob Keller +Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++---------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c +index 0d5e96a3b9a21..5dd642647071a 100644 +--- a/drivers/net/ethernet/qlogic/qede/qede_main.c ++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c +@@ -108,7 +108,7 @@ static void qede_remove(struct pci_dev *pdev); + static void qede_shutdown(struct pci_dev *pdev); + static void qede_link_update(void *dev, struct qed_link_output *link); + static void qede_schedule_recovery_handler(void *dev); +-static void qede_recovery_handler(struct qede_dev *edev); ++static bool qede_recovery_handler(struct qede_dev *edev); + static void qede_schedule_hw_err_handler(void *dev, + enum qed_hw_err_type err_type); + static void qede_get_eth_tlv_data(void *edev, void *data); +@@ -992,21 +992,6 @@ void __qede_unlock(struct qede_dev *edev) + mutex_unlock(&edev->qede_lock); + } + +-/* This version of the lock should be used when acquiring the RTNL lock is also +- * needed in addition to the internal qede lock. +- */ +-static void qede_lock(struct qede_dev *edev) +-{ +- rtnl_lock(); +- __qede_lock(edev); +-} +- +-static void qede_unlock(struct qede_dev *edev) +-{ +- __qede_unlock(edev); +- rtnl_unlock(); +-} +- + static void qede_periodic_task(struct work_struct *work) + { + struct qede_dev *edev = container_of(work, struct qede_dev, +@@ -1043,6 +1028,8 @@ static void qede_sp_task(struct work_struct *work) + */ + + if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) { ++ bool reloaded; ++ + cancel_delayed_work_sync(&edev->periodic_task); + #ifdef CONFIG_QED_SRIOV + /* SRIOV must be disabled outside the lock to avoid a deadlock. +@@ -1051,9 +1038,17 @@ static void qede_sp_task(struct work_struct *work) + if (pci_num_vf(edev->pdev)) + qede_sriov_configure(edev->pdev, 0); + #endif +- qede_lock(edev); +- qede_recovery_handler(edev); +- qede_unlock(edev); ++ rtnl_lock(); ++ __qede_lock(edev); ++ reloaded = qede_recovery_handler(edev); ++ __qede_unlock(edev); ++ ++ /* The udp_tunnel core synchronously calls back into ++ * qede_udp_tunnel_sync(), which takes the qede lock. ++ */ ++ if (reloaded) ++ udp_tunnel_nic_reset_ntf(edev->ndev); ++ rtnl_unlock(); + } + + __qede_lock(edev); +@@ -2596,9 +2591,13 @@ static void qede_recovery_failed(struct qede_dev *edev) + edev->ops->common->set_power_state(edev->cdev, PCI_D3hot); + } + +-static void qede_recovery_handler(struct qede_dev *edev) ++/* Returns true if an open device was successfully reloaded and its ++ * udp_tunnel ports need to be re-synced by the caller. ++ */ ++static bool qede_recovery_handler(struct qede_dev *edev) + { + u32 curr_state = edev->state; ++ bool reloaded = false; + int rc; + + DP_NOTICE(edev, "Starting a recovery process\n"); +@@ -2628,17 +2627,18 @@ static void qede_recovery_handler(struct qede_dev *edev) + goto err; + + qede_config_rx_mode(edev->ndev); +- udp_tunnel_nic_reset_ntf(edev->ndev); ++ reloaded = true; + } + + edev->state = curr_state; + + DP_NOTICE(edev, "Recovery handling is done\n"); + +- return; ++ return reloaded; + + err: + qede_recovery_failed(edev); ++ return false; + } + + static void qede_atomic_hw_err_handler(struct qede_dev *edev) +-- +2.53.0 + diff --git a/queue-5.15/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch b/queue-5.15/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch new file mode 100644 index 0000000000..4a2c1609a8 --- /dev/null +++ b/queue-5.15/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch @@ -0,0 +1,118 @@ +From da0e4f6ff80f0bb0d8352693c60fbbccf758b39a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 9 Jul 2026 16:27:54 +0000 +Subject: rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled + +From: Ilia Gavrilov + +[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ] + +When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst +is never initialized because inet6_init() exits before addrconf_init() +is called to initialize it. An attempt to bind an RDS socket to +an ipv6 address results in a crash in __ipv6_chk_addr_and_flags() + +KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] +RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0 +Call Trace: + + ipv6_chk_addr+0x3b/0x50 + rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp] + rds_trans_get_preferred+0x15d/0x2d0 [rds] + ? trace_hardirqs_on+0x2d/0x110 + rds_bind+0x1433/0x1d60 [rds] + ? rds_remove_bound+0xd50/0xd50 [rds] + ? aa_af_perm+0x250/0x250 + ? __might_fault+0xde/0x190 + ? __sys_bind+0x1dc/0x210 + __sys_bind+0x1dc/0x210 + ? __ia32_sys_socketpair+0x100/0x100 + ? restore_fpregs_from_fpstate+0x53/0x100 + __x64_sys_bind+0x73/0xb0 + ? syscall_enter_from_user_mode+0x1c/0x50 + do_syscall_64+0x34/0x80 + entry_SYSCALL_64_after_hwframe+0x6e/0xd8 +RIP: 0033:0x7f47f8269ea9 + + +The following code reproduces the issue: + +struct sockaddr_in6 addr; +s = socket(PF_RDS, SOCK_SEQPACKET, 0); + +memset(&addr, 0, sizeof(addr)); +inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr); +addr.sin6_family = AF_INET6; +addr.sin6_port = htons(PORT); + +bind(s, &addr, sizeof(addr)); + +Found by InfoTeCS on behalf of Linux Verification Center +(linuxtesting.org) with Syzkaller. + +Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") +Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support") +Signed-off-by: Ilia Gavrilov +Reviewed-by: Allison Henderson +Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru +Signed-off-by: Jakub Kicinski +Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()") +Signed-off-by: Sasha Levin +--- + net/rds/ib.c | 4 ++++ + net/rds/ib_cm.c | 4 ++++ + net/rds/tcp.c | 8 +++++--- + 3 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/net/rds/ib.c b/net/rds/ib.c +index ec45664f38767..810a9b76101f5 100644 +--- a/net/rds/ib.c ++++ b/net/rds/ib.c +@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr, + sa = (struct sockaddr *)&sin; + } else { + #if IS_ENABLED(CONFIG_IPV6) ++ if (!ipv6_mod_enabled()) { ++ ret = -EADDRNOTAVAIL; ++ goto out; ++ } + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = *addr; +diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c +index 5289afbb61aa7..e50e01abb0799 100644 +--- a/net/rds/ib_cm.c ++++ b/net/rds/ib_cm.c +@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, + dp = event->param.conn.private_data; + if (isv6) { + #if IS_ENABLED(CONFIG_IPV6) ++ if (!ipv6_mod_enabled()) { ++ err = -EOPNOTSUPP; ++ goto out; ++ } + dp_cmn = &dp->ricp_v6.dp_cmn; + saddr6 = &dp->ricp_v6.dp_saddr; + daddr6 = &dp->ricp_v6.dp_daddr; +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index b560d06e6d96d..071f2a2f514ff 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -341,9 +341,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, + rcu_read_unlock(); + } + #if IS_ENABLED(CONFIG_IPV6) +- ret = ipv6_chk_addr(net, addr, dev, 0); +- if (ret) +- return 0; ++ if (ipv6_mod_enabled()) { ++ ret = ipv6_chk_addr(net, addr, dev, 0); ++ if (ret) ++ return 0; ++ } + #endif + return -EADDRNOTAVAIL; + } +-- +2.53.0 + diff --git a/queue-5.15/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-5.15/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch new file mode 100644 index 0000000000..960be6fa11 --- /dev/null +++ b/queue-5.15/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch @@ -0,0 +1,85 @@ +From 625bebb9887b095d8b3dc7ee23f3a5d21c58ed01 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 14:02:03 -0700 +Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in + rds_tcp_laddr_check() + +From: Xiang Mei + +[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ] + +rds_tcp_laddr_check() looks up a scoped IPv6 interface with +dev_get_by_index_rcu(), drops the RCU read-side lock, and only then +passes the bare struct net_device * into ipv6_chk_addr(). + +dev_get_by_index_rcu() only keeps the device alive within the same RCU +read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can +free the net_device; ipv6_chk_addr() then dereferences the stale pointer +in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading +freed memory. + +Keep the RCU read-side lock held across the ipv6_chk_addr() call instead +of dropping it right after the lookup, so the device cannot be freed +while it is in use. + + BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + Read of size 8 at addr ffff8880106ec000 by task exploit/153 + Call Trace: + ... + kasan_report (mm/kasan/report.c:595) + __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972) + rds_tcp_laddr_check (net/rds/tcp.c:370) + rds_bind (net/rds/bind.c:248) + __sys_bind (net/socket.c:1920) + __x64_sys_bind (net/socket.c:1956) + do_syscall_64 (arch/x86/entry/syscall_64.c:63) + entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) + +Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") +Reported-by: Weiming Shi +Signed-off-by: Xiang Mei +Reviewed-by: Allison Henderson +Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/rds/tcp.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index 071f2a2f514ff..f66cbf0b9895f 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -330,23 +330,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, + /* If the scope_id is specified, check only those addresses + * hosted on the specified interface. + */ ++ rcu_read_lock(); + if (scope_id != 0) { +- rcu_read_lock(); + dev = dev_get_by_index_rcu(net, scope_id); + /* scope_id is not valid... */ + if (!dev) { + rcu_read_unlock(); + return -EADDRNOTAVAIL; + } +- rcu_read_unlock(); + } + #if IS_ENABLED(CONFIG_IPV6) + if (ipv6_mod_enabled()) { + ret = ipv6_chk_addr(net, addr, dev, 0); +- if (ret) ++ if (ret) { ++ rcu_read_unlock(); + return 0; ++ } + } + #endif ++ rcu_read_unlock(); + return -EADDRNOTAVAIL; + } + +-- +2.53.0 + diff --git a/queue-5.15/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-5.15/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch new file mode 100644 index 0000000000..3da870168c --- /dev/null +++ b/queue-5.15/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch @@ -0,0 +1,56 @@ +From 795a3615ec51039fa64c8fb8dba49f3b5ecfebf4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 14 Jul 2026 19:49:34 +0900 +Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer + +From: HyeongJun An + +[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ] + +iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the +target-supplied data segment. The segment carries a 2-byte sense length +followed by the sense bytes, so it must hold 2 + senselen bytes, but the +bounds check only requires datalen >= senselen: + + senselen = get_unaligned_be16(data); + if (datalen < senselen) + goto invalid_datalen; + memcpy(sc->sense_buffer, data + 2, + min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); + +A target that returns a SCSI Response whose datalen equals senselen +(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data + +2 read up to two bytes past the received data. Those bytes are stale +conn->data contents and end up in the command's sense buffer, which is +returned to userspace. + +Account for the 2-byte sense length prefix in the check. + +Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi") +Suggested-by: Sashiko AI +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c +index 225aa82799609..fbb5eecbb7caa 100644 +--- a/drivers/scsi/libiscsi.c ++++ b/drivers/scsi/libiscsi.c +@@ -866,7 +866,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, + } + + senselen = get_unaligned_be16(data); +- if (datalen < senselen) ++ if (datalen < senselen + 2) + goto invalid_datalen; + + memcpy(sc->sense_buffer, data + 2, +-- +2.53.0 + diff --git a/queue-5.15/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-5.15/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch new file mode 100644 index 0000000000..6b1cf6966d --- /dev/null +++ b/queue-5.15/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch @@ -0,0 +1,71 @@ +From 69265c316159969194cd87940bc48f08cb1b3209 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 15:58:48 +0900 +Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the + connection buffer + +From: HyeongJun An + +[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ] + +iscsi_tcp_hdr_dissect() receives the data segment of several PDU types +into the fixed-size conn->data buffer, which is allocated for +ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes. For the LOGIN_RSP, TEXT_RSP, +REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU +whose DataSegmentLength exceeds that buffer. + +The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its +data segment (sense/response data) into conn->data via +iscsi_tcp_data_recv_prep(), but it does so without the same check. The +only upstream bound on in.datalen is conn->max_recv_dlength, the +initiator's advertised MaxRecvDataSegmentLength, which is commonly +negotiated well above 8192 (open-iscsi defaults to 262144). A target +that returns a SCSI Response with a DataSegmentLength between 8193 and +max_recv_dlength therefore overflows the 8192-byte conn->data buffer. + +Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly +like those responses: bound the data segment, receive it into conn->data +when present, and otherwise complete the PDU with no data. Fold the +opcode into that case group rather than duplicating the check. + +Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld") +Suggested-by: Chris Leech +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi_tcp.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c +index 883005757ddb8..8e9f6e14c6e5a 100644 +--- a/drivers/scsi/libiscsi_tcp.c ++++ b/drivers/scsi/libiscsi_tcp.c +@@ -759,13 +759,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); + spin_unlock(&conn->session->back_lock); + break; +- case ISCSI_OP_SCSI_CMD_RSP: +- if (tcp_conn->in.datalen) { +- iscsi_tcp_data_recv_prep(tcp_conn); +- return 0; +- } +- rc = iscsi_complete_pdu(conn, hdr, NULL, 0); +- break; + case ISCSI_OP_R2T: + if (ahslen) { + rc = ISCSI_ERR_AHSLEN; +@@ -773,6 +766,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + } + rc = iscsi_tcp_r2t_rsp(conn, hdr); + break; ++ case ISCSI_OP_SCSI_CMD_RSP: + case ISCSI_OP_LOGIN_RSP: + case ISCSI_OP_TEXT_RSP: + case ISCSI_OP_REJECT: +-- +2.53.0 + diff --git a/queue-5.15/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-5.15/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch new file mode 100644 index 0000000000..929a13d7aa --- /dev/null +++ b/queue-5.15/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch @@ -0,0 +1,66 @@ +From 6d3cabdb5d961e69c7ee6acd72c3346b8c5ffce9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 09:27:36 +0200 +Subject: scsi: zfcp: Fix memory leak during adapter release by destroying + gid_pn_req + +From: Benjamin Block + +[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ] + +When releasing an adapter we don't free the mempool 'gid_pn_req' that is +allocated during the enqueue. This leaks memory: + + unreferenced object 0xd8d29297de700 (size 256): + comm "(udev-worker)", pid 2105, jiffies 4294945794 + hex dump (first 32 bytes): + 00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00 ......N......... + ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0 ..........._g... + backtrace (crc 4a5b5da2): + [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0 + [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0 + [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150 + [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp] + [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp] + [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp] + [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80 + [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390 + [<000dc45f643d8238>] online_store+0x298/0x5b0 + [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480 + [<000dc45f62c81150>] new_sync_write+0x370/0x4b0 + [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0 + [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0 + [<000dc45f621c4a16>] do_syscall+0x2f6/0x430 + [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0 + [<000dc45f64dc2224>] system_call+0x74/0xa0 + +Fix this by destroying the mempool during the adapter's release. + +Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp") +Signed-off-by: Benjamin Block +Tested-by: M Nikhil +Acked-by: M Nikhil +Reviewed-by: Chinmaya Kajagar +Reviewed-by: Nihar Panda +Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/s390/scsi/zfcp_aux.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c +index 2e29121f96fa3..c92f9b13d8ecc 100644 +--- a/drivers/s390/scsi/zfcp_aux.c ++++ b/drivers/s390/scsi/zfcp_aux.c +@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) + static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) + { + mempool_destroy(adapter->pool.erp_req); ++ mempool_destroy(adapter->pool.gid_pn_req); + mempool_destroy(adapter->pool.scsi_req); + mempool_destroy(adapter->pool.scsi_abort); + mempool_destroy(adapter->pool.qtcb_pool); +-- +2.53.0 + diff --git a/queue-5.15/series b/queue-5.15/series index f2c8434b1c..818da74543 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -243,3 +243,47 @@ hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch net-qrtr-ns-limit-the-maximum-server-registration-pe.patch net-qrtr-ns-raise-node-count-limit-to-512.patch tls-separate-no-async-decryption-request-handling-fr.patch +dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch +asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch +asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch +phy-zynqmp-allow-variation-in-refclk-rate.patch +phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch +phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch +phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch +netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch +drm-mediatek-check-crtc-state-before-freeing.patch +keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch +keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch +assoc_array-trim-the-final-shortcut-word-using-the-c.patch +netfilter-xt_hashlimit-validate-hashtable-supports-x.patch +netfilter-nft_payload-fix-mask-build-for-partial-fie.patch +rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch +rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch +scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch +scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch +smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch +hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch +net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch +forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch +hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch +hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch +hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch +hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch +hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch +hwmon-adt7470-use-cached-pwm-frequency-value.patch +hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch +hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch +powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch +powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch +powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch +wifi-mac80211-validate-individual-twt-params-before-.patch +hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch +bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch +net-phylink-put-link_gpio-if-phylink_create-fails.patch +scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch +net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch +net-sxgbe-check-descriptor-ring-allocation-failures.patch +can-isotp-check-register_netdevice_notifier-error-in.patch +tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch +octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch +qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch diff --git a/queue-5.15/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-5.15/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch new file mode 100644 index 0000000000..36ea5a0c25 --- /dev/null +++ b/queue-5.15/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch @@ -0,0 +1,85 @@ +From 0a10de74295e0e153fcd2de3d60faee837d7f0b2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 28 Jun 2026 14:59:09 +0800 +Subject: smb: client: fix buffer leaks in SMB1 read and write + +From: Dawei Feng + +[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ] + +CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request +buffer before checking whether tcon->ses->server is NULL. If that +defensive check ever fails, the helper returns -ECONNABORTED without +releasing the request buffer. + +Fix these leaks by releasing the allocated request buffer before +returning from these error paths. Use cifs_small_buf_release() for the +buffers allocated by small_smb_init() and cifs_buf_release() for the +buffer allocated by smb_init(). + +The bug was first flagged by an experimental analysis tool we are +developing for kernel memory-management bugs while analyzing +v6.13-rc1. The tool is still under development and is not yet publicly +available. Manual inspection confirms that the bug is still +present in v7.1.1. + +An x86_64 allyesconfig build showed no new warnings. + +Runtime validation used a temporary fault-injection hook to force +tcon->ses->server to NULL after request-buffer initialization. On the +unfixed kernel, the harness observed two leaked small request buffers and +one leaked large request buffer, with directed kmemleak dumps confirming +the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer +deltas remained. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Signed-off-by: Dawei Feng +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/cifs/cifssmb.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c +index e6541bd5c63df..11a400663b9f2 100644 +--- a/fs/cifs/cifssmb.c ++++ b/fs/cifs/cifssmb.c +@@ -1661,8 +1661,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -1774,8 +1776,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -2205,8 +2209,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +-- +2.53.0 + diff --git a/queue-5.15/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-5.15/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch new file mode 100644 index 0000000000..347ee033cd --- /dev/null +++ b/queue-5.15/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch @@ -0,0 +1,43 @@ +From 8a2f4eedc80b92ae7883c15b8cc230dc54bd71f3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 21:49:51 +0900 +Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data() + +From: Masami Hiramatsu (Google) + +[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ] + +mmio_reset_data() is called during tracer initialization, reset, and +start. While it resets overrun_detected and prev_overruns, it neglects +to reset dropped_count. Consequently, dropped event counts from prior +tracing sessions persist in dropped_count and corrupt overrun reports +in subsequent runs. + +Fix this by explicitly calling atomic_set(&dropped_count, 0) in +mmio_reset_data(). + +Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2 +Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording") +Assisted-by: Antigravity:gemini-3.6-flash +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_mmiotrace.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c +index 4d9e5c830dbe1..c523ce5aa4958 100644 +--- a/kernel/trace/trace_mmiotrace.c ++++ b/kernel/trace/trace_mmiotrace.c +@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr) + { + overrun_detected = false; + prev_overruns = 0; ++ atomic_set(&dropped_count, 0); + + tracing_reset_online_cpus(&tr->array_buffer); + } +-- +2.53.0 + diff --git a/queue-5.15/wifi-mac80211-validate-individual-twt-params-before-.patch b/queue-5.15/wifi-mac80211-validate-individual-twt-params-before-.patch new file mode 100644 index 0000000000..5b5cd4387c --- /dev/null +++ b/queue-5.15/wifi-mac80211-validate-individual-twt-params-before-.patch @@ -0,0 +1,52 @@ +From 8c74f3f245216af830df6e0bc4404bca5e47e1ef Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 09:09:28 +0800 +Subject: wifi: mac80211: validate individual TWT params before driver setup + +From: Zhao Li + +[ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ] + +ieee80211_process_rx_twt_action() only partially validates a received +S1G TWT setup frame before queueing it. + +An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup() +with twt->length too short for the full struct ieee80211_twt_params. + +The individual path passes twt to drv_add_twt_setup(). Both the tracepoint +and the driver callback consume the complete parameters block, not merely +req_type. Do not pass a short individual agreement to the driver. +Broadcast agreements remain unchanged because they are rejected locally +after accessing only req_type. + +Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode") +Assisted-by: Codex:gpt-5 +Assisted-by: Claude:opus-4.8 +Signed-off-by: Zhao Li +Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com +[edit commit message to not overclaim lack of validation nor + understate driver impact] +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/s1g.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c +index 10b34bc4b67d4..e41fd43f6a6ec 100644 +--- a/net/mac80211/s1g.c ++++ b/net/mac80211/s1g.c +@@ -100,6 +100,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata, + struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable; + struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + ++ if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) && ++ twt->length < sizeof(twt->control) + sizeof(*twt_agrt)) ++ return; ++ + twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST); + + /* broadcast TWT not supported yet */ +-- +2.53.0 + diff --git a/queue-6.1/ahci-introduce-ahci_ignore_port-helper.patch b/queue-6.1/ahci-introduce-ahci_ignore_port-helper.patch new file mode 100644 index 0000000000..0e3793b8ac --- /dev/null +++ b/queue-6.1/ahci-introduce-ahci_ignore_port-helper.patch @@ -0,0 +1,135 @@ +From 4abaeb6e5740613ef47db455e8d605ba77465ee3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 6 Jan 2025 14:14:47 +0900 +Subject: ahci: Introduce ahci_ignore_port() helper + +From: Damien Le Moal + +[ Upstream commit c9b5be909e6595547ed5d45aef39fd65948aa342 ] + +libahci and AHCI drivers may ignore some ports if the port is invalid +(its ID does not correspond to a valid physical port) or if the user +explicitly requested the port to be ignored with the mask_port_map +ahci module parameter. Such port that shall be ignored can be identified +by checking that the bit corresponding to the port ID is not set in the +mask_port_map field of struct ahci_host_priv. E.g. code such as: +"if (!(hpriv->mask_port_map & (1 << portid)))". + +Replace all direct use of the mask_port_map field to detect such port +with the new helper inline function ahci_ignore_port() to make the code +more readable/easier to understand. + +The comment describing the mask_port_map field of struct ahci_host_priv +is also updated to be more accurate. + +Signed-off-by: Damien Le Moal +Reviewed-by: Niklas Cassel +Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()") +Signed-off-by: Sasha Levin +--- + drivers/ata/ahci.h | 13 ++++++++++++- + drivers/ata/ahci_brcm.c | 2 +- + drivers/ata/ahci_ceva.c | 4 ++-- + drivers/ata/libahci_platform.c | 6 +++--- + 4 files changed, 18 insertions(+), 7 deletions(-) + +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index f9c5906a8afa8..9864427e5c234 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -330,7 +330,7 @@ struct ahci_port_priv { + struct ahci_host_priv { + /* Input fields */ + unsigned int flags; /* AHCI_HFLAG_* */ +- u32 mask_port_map; /* mask out particular bits */ ++ u32 mask_port_map; /* Mask of valid ports */ + + void __iomem * mmio; /* bus-independent mem map */ + u32 cap; /* cap to use */ +@@ -381,6 +381,17 @@ struct ahci_host_priv { + int port); + }; + ++/* ++ * Return true if a port should be ignored because it is excluded from ++ * the host port map. ++ */ ++static inline bool ahci_ignore_port(struct ahci_host_priv *hpriv, ++ unsigned int portid) ++{ ++ return portid >= hpriv->nports || ++ !(hpriv->mask_port_map & (1 << portid)); ++} ++ + extern int ahci_ignore_sss; + + extern const struct attribute_group *ahci_shost_groups[]; +diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c +index cb5d550d6b7ed..ee708dafd08d1 100644 +--- a/drivers/ata/ahci_brcm.c ++++ b/drivers/ata/ahci_brcm.c +@@ -288,7 +288,7 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev, + + /* Re-initialize and calibrate the PHY */ + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + rc = phy_init(hpriv->phys[i]); +diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c +index c179b8b328587..738e0da50d5b4 100644 +--- a/drivers/ata/ahci_ceva.c ++++ b/drivers/ata/ahci_ceva.c +@@ -206,7 +206,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + goto disable_clks; + + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + rc = phy_init(hpriv->phys[i]); +@@ -218,7 +218,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + ahci_platform_deassert_rsts(hpriv); + + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + rc = phy_power_on(hpriv->phys[i]); +diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c +index 155ef2aa90a6c..45144dbc252ba 100644 +--- a/drivers/ata/libahci_platform.c ++++ b/drivers/ata/libahci_platform.c +@@ -48,7 +48,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) + int rc, i; + + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + rc = phy_init(hpriv->phys[i]); +@@ -72,7 +72,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) + + disable_phys: + while (--i >= 0) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + phy_power_off(hpriv->phys[i]); +@@ -93,7 +93,7 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) + int i; + + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + phy_power_off(hpriv->phys[i]); +-- +2.53.0 + diff --git a/queue-6.1/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.1/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..861f2f8532 --- /dev/null +++ b/queue-6.1/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From e5eecc03eda5e1a405f0b208a295ee4f765eea03 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:12:54 +0530 +Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ] + +In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98090->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98090.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c +index f5acf84ee20cc..e64e2352c6aa4 100644 +--- a/sound/soc/codecs/max98090.c ++++ b/sound/soc/codecs/max98090.c +@@ -2391,8 +2391,9 @@ static int max98090_probe(struct snd_soc_component *component) + dev_dbg(component->dev, "max98090_probe\n"); + + max98090->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98090->mclk)) ++ if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + max98090->component = component; + +-- +2.53.0 + diff --git a/queue-6.1/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.1/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..350ef7c669 --- /dev/null +++ b/queue-6.1/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From aad8f396d0b7b4f1ee6e1b459ac3af485444671f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:09:50 +0530 +Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ] + +In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98095->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98095.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c +index 44aa58fcc23f8..7b399564785c4 100644 +--- a/sound/soc/codecs/max98095.c ++++ b/sound/soc/codecs/max98095.c +@@ -1984,8 +1984,9 @@ static int max98095_probe(struct snd_soc_component *component) + int ret = 0; + + max98095->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98095->mclk)) ++ if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + /* reset the codec, the DSP core, and disable all interrupts */ + max98095_reset(component); +-- +2.53.0 + diff --git a/queue-6.1/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-6.1/assoc_array-trim-the-final-shortcut-word-using-the-c.patch new file mode 100644 index 0000000000..829994379f --- /dev/null +++ b/queue-6.1/assoc_array-trim-the-final-shortcut-word-using-the-c.patch @@ -0,0 +1,66 @@ +From 564abe5bc165af524d05a33eefde9192f7f83ed7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:05 -0400 +Subject: assoc_array: trim the final shortcut word using the current chunk end + +From: Michael Bommarito + +[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ] + +assoc_array_walk() masks off the bits past shortcut->skip_to_level in the +word that contains skip_to_level, gated on +round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level. + +That guard is wrong in two opposite ways: + + - When sc_level is word-aligned (every word after the first) round_up() + is a no-op, so the guard is sc_level > skip_to_level and never fires for + the word that holds skip_to_level. A shortcut that spans more than one + word and ends in the middle of its last word leaves that word untrimmed, + and its stale high bits leak into the dissimilarity word and can steer + the walk down the wrong descendant. + + - When sc_level is unaligned (the first word) and skip_to_level sits on + the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and + fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears + the whole dissimilarity word and makes a differing shortcut compare + equal. + +Use the end of the chunk that contains sc_level instead: + + skip_to_level < round_down(sc_level, CHUNK) + CHUNK + +For an aligned sc_level whose word holds skip_to_level this now fires (the +first bug); for an unaligned sc_level with skip_to_level on the following +boundary it does not, so shift is never 0 when the branch runs and the trim +never clears the whole word. + +Fixes: 3cb989501c26 ("Add a generic associative array implementation.") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + lib/assoc_array.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/assoc_array.c b/lib/assoc_array.c +index ca0b4f360c1a0..65409f0d2e0e5 100644 +--- a/lib/assoc_array.c ++++ b/lib/assoc_array.c +@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array, + sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT]; + dissimilarity = segments ^ sc_segments; + +- if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) { ++ if (shortcut->skip_to_level < round_down(sc_level, ++ ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) { + /* Trim segments that are beyond the shortcut */ + int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK; + dissimilarity &= ~(ULONG_MAX << shift); +-- +2.53.0 + diff --git a/queue-6.1/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch b/queue-6.1/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch new file mode 100644 index 0000000000..5f63ff2e08 --- /dev/null +++ b/queue-6.1/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch @@ -0,0 +1,76 @@ +From 86e3adfe2d29dbf00883b4f2386a2c93aa29b141 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 17 Jul 2026 23:55:26 +0530 +Subject: ata: ahci_ceva: fix error paths in + ceva_ahci_platform_enable_resources() + +From: Radhey Shyam Pandey + +[ Upstream commit 4d99a91574c420decab56cc880fad0dc15b8a7a3 ] + +On phy_init() failure the error path fallsthrough to disable_rsts, which +deasserts the controller reset and then enters disable_phys calling +phy_power_off() on PHYs that were never powered on. That corrupts the PHY +power_count and triggers an extra runtime PM put. + +Use a separate exit_phys path that unwinds with phy_exit() only and falls +through to disable_clks while the controller remains in reset. Reserve +phy_power_off() for the phy_power_on() failure path only, and skip +masked-out ports in both unwind loops. + +On phy_power_on() failure re-assert the controller reset before disabling +clocks and regulators, matching the teardown order used by +ahci_platform_enable_resources() and ahci_platform_disable_resources(). + +Fixes: 26c8404e162b ("ata: ahci_ceva: fix error handling for Xilinx GT PHY support") +Signed-off-by: Radhey Shyam Pandey +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + drivers/ata/ahci_ceva.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c +index 738e0da50d5b4..0651d4065f7f7 100644 +--- a/drivers/ata/ahci_ceva.c ++++ b/drivers/ata/ahci_ceva.c +@@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + + rc = phy_init(hpriv->phys[i]); + if (rc) +- goto disable_rsts; ++ goto exit_phys; + } + + /* De-assert the controller reset */ +@@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + + return 0; + +-disable_rsts: +- ahci_platform_deassert_rsts(hpriv); +- + disable_phys: + while (--i >= 0) { ++ if (ahci_ignore_port(hpriv, i)) ++ continue; ++ + phy_power_off(hpriv->phys[i]); + phy_exit(hpriv->phys[i]); + } ++ ahci_platform_assert_rsts(hpriv); ++ goto disable_clks; ++ ++exit_phys: ++ while (--i >= 0) { ++ if (ahci_ignore_port(hpriv, i)) ++ continue; ++ ++ phy_exit(hpriv->phys[i]); ++ } + + disable_clks: + ahci_platform_disable_clks(hpriv); +-- +2.53.0 + diff --git a/queue-6.1/ata-libahci_platform-support-non-consecutive-port-nu.patch b/queue-6.1/ata-libahci_platform-support-non-consecutive-port-nu.patch new file mode 100644 index 0000000000..029d5eeb50 --- /dev/null +++ b/queue-6.1/ata-libahci_platform-support-non-consecutive-port-nu.patch @@ -0,0 +1,178 @@ +From 675ba85d4bc97bc934a2ac66c655f36fecbbdd21 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 1 Jan 2025 13:13:33 +0100 +Subject: ata: libahci_platform: support non-consecutive port numbers + +From: Josua Mayer + +[ Upstream commit 8c87215dd3a2c814dcffc0bafe8c80c8f98f2574 ] + +So far ahci_platform relied on number of child nodes in firmware to +allocate arrays and expected port numbers to start from 0 without holes. +This number of ports is then set in private structure for use when +configuring phys and regulators. + +Some platforms may not use every port of an ahci controller. +E.g. SolidRUN CN9130 Clearfog uses only port 1 but not port 0, leading +to the following errors during boot: +[ 1.719476] ahci f2540000.sata: invalid port number 1 +[ 1.724562] ahci f2540000.sata: No port enabled + +Update all accessesors of ahci_host_priv phys and target_pwrs arrays to +support holes. Access is gated by hpriv->mask_port_map which has a bit +set for each enabled port. + +Update ahci_platform_get_resources to ignore holes in the port numbers +and enable ports defined in firmware by their reg property only. + +When firmware does not define children it is assumed that there is +exactly one port, using index 0. + +Signed-off-by: Josua Mayer +Reviewed-by: Hans de Goede +Signed-off-by: Damien Le Moal +Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()") +Signed-off-by: Sasha Levin +--- + drivers/ata/ahci_brcm.c | 3 +++ + drivers/ata/ahci_ceva.c | 6 +++++ + drivers/ata/libahci_platform.c | 40 +++++++++++++++++++++++++++++----- + 3 files changed, 43 insertions(+), 6 deletions(-) + +diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c +index 6f216eb256100..cb5d550d6b7ed 100644 +--- a/drivers/ata/ahci_brcm.c ++++ b/drivers/ata/ahci_brcm.c +@@ -288,6 +288,9 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev, + + /* Re-initialize and calibrate the PHY */ + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + rc = phy_init(hpriv->phys[i]); + if (rc) + goto disable_phys; +diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c +index 50e07ea60e45c..c179b8b328587 100644 +--- a/drivers/ata/ahci_ceva.c ++++ b/drivers/ata/ahci_ceva.c +@@ -206,6 +206,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + goto disable_clks; + + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + rc = phy_init(hpriv->phys[i]); + if (rc) + goto disable_rsts; +@@ -215,6 +218,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + ahci_platform_deassert_rsts(hpriv); + + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + rc = phy_power_on(hpriv->phys[i]); + if (rc) { + phy_exit(hpriv->phys[i]); +diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c +index b9e336bacf179..155ef2aa90a6c 100644 +--- a/drivers/ata/libahci_platform.c ++++ b/drivers/ata/libahci_platform.c +@@ -48,6 +48,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) + int rc, i; + + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + rc = phy_init(hpriv->phys[i]); + if (rc) + goto disable_phys; +@@ -69,6 +72,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) + + disable_phys: + while (--i >= 0) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + phy_power_off(hpriv->phys[i]); + phy_exit(hpriv->phys[i]); + } +@@ -87,6 +93,9 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) + int i; + + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + phy_power_off(hpriv->phys[i]); + phy_exit(hpriv->phys[i]); + } +@@ -434,6 +443,20 @@ static int ahci_platform_get_firmware(struct ahci_host_priv *hpriv, + return 0; + } + ++static u32 ahci_platform_find_max_port_id(struct device *dev) ++{ ++ u32 max_port = 0; ++ ++ for_each_child_of_node_scoped(dev->of_node, child) { ++ u32 port; ++ ++ if (!of_property_read_u32(child, "reg", &port)) ++ max_port = max(max_port, port); ++ } ++ ++ return max_port; ++} ++ + /** + * ahci_platform_get_resources - Get platform resources + * @pdev: platform device to get resources for +@@ -461,6 +484,7 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, + struct ahci_host_priv *hpriv; + struct device_node *child; + u32 mask_port_map = 0; ++ u32 max_port; + + if (!devres_open_group(dev, NULL, GFP_KERNEL)) + return ERR_PTR(-ENOMEM); +@@ -552,15 +576,17 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, + goto err_out; + } + ++ /* find maximum port id for allocating structures */ ++ max_port = ahci_platform_find_max_port_id(dev); + /* +- * If no sub-node was found, we still need to set nports to +- * one in order to be able to use the ++ * Set nports according to maximum port id. Clamp at ++ * AHCI_MAX_PORTS, warning message for invalid port id ++ * is generated later. ++ * When DT has no sub-nodes max_port is 0, nports is 1, ++ * in order to be able to use the + * ahci_platform_[en|dis]able_[phys|regulators] functions. + */ +- if (child_nodes) +- hpriv->nports = child_nodes; +- else +- hpriv->nports = 1; ++ hpriv->nports = min(AHCI_MAX_PORTS, max_port + 1); + + hpriv->phys = devm_kcalloc(dev, hpriv->nports, sizeof(*hpriv->phys), GFP_KERNEL); + if (!hpriv->phys) { +@@ -633,6 +659,8 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, + * If no sub-node was found, keep this for device tree + * compatibility + */ ++ hpriv->mask_port_map |= BIT(0); ++ + rc = ahci_platform_get_phy(hpriv, 0, dev, dev->of_node); + if (rc) + goto err_out; +-- +2.53.0 + diff --git a/queue-6.1/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch b/queue-6.1/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch new file mode 100644 index 0000000000..bb66602753 --- /dev/null +++ b/queue-6.1/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch @@ -0,0 +1,44 @@ +From b05da1c6b2969569d52e61c5c8c7f5bb356b5705 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 15:31:37 -0700 +Subject: ata: sata_mv: accept 1 or 2 resources in platform probe + +From: Rosen Penev + +[ Upstream commit ef19a9cf037957fe3a35df8355c76ff0a63a0436 ] + +Board files in arch/arm/plat-orion, arch/arm/mach-dove, +arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the +"sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ). +Those devices are rejected with -EINVAL, so SATA no longer probes on +legacy Marvell Orion/Kirkwood-style boards. + +Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2 +resources (legacy, IRQ supplied as a second resource) so both probing +paths work. + +Fixes: b3b2bec9646e ("ata: sata_mv: Fixes expected number of resources now IRQs are gone") +Assisted-by: opencode:big-pickle +Signed-off-by: Rosen Penev +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + drivers/ata/sata_mv.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c +index 9cf540017a5e5..298bf6330de57 100644 +--- a/drivers/ata/sata_mv.c ++++ b/drivers/ata/sata_mv.c +@@ -4026,7 +4026,7 @@ static int mv_platform_probe(struct platform_device *pdev) + /* + * Simple resource validation .. + */ +- if (unlikely(pdev->num_resources != 1)) { ++ if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) { + dev_err(&pdev->dev, "invalid number of resources\n"); + return -EINVAL; + } +-- +2.53.0 + diff --git a/queue-6.1/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-6.1/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch new file mode 100644 index 0000000000..f505879af9 --- /dev/null +++ b/queue-6.1/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch @@ -0,0 +1,60 @@ +From ec2fe9e5559611c175e09fc1cedeb5c8c7765028 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 14:48:45 +0800 +Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp + +From: Jiale Yao + +[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ] + +l2cap_le_connect_rsp() obtains a channel via +__l2cap_get_chan_by_ident() but neither holds a reference nor uses +l2cap_chan_hold_unless_zero() before locking and operating on it. +A concurrent l2cap_chan_del() triggered by a remote disconnect can +free the channel between the lookup and l2cap_chan_lock(), causing +a use-after-free. + +The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler +l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero() +to safely hold a reference, but l2cap_le_connect_rsp() was left +unprotected. + +Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup +and l2cap_chan_put() on the exit path, consistent with other L2CAP +response handlers. + +Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request") +Assisted-by: Claude:deepseek-v4-pro +Signed-off-by: Jiale Yao +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/l2cap_core.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index 1da637f81db34..b5603531a9d60 100644 +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -4831,6 +4831,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + goto unlock; + } + ++ chan = l2cap_chan_hold_unless_zero(chan); ++ if (!chan) ++ return -EBADSLT; ++ + err = 0; + + l2cap_chan_lock(chan); +@@ -4876,6 +4880,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + } + + l2cap_chan_unlock(chan); ++ l2cap_chan_put(chan); + + unlock: + mutex_unlock(&conn->chan_lock); +-- +2.53.0 + diff --git a/queue-6.1/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-6.1/can-isotp-check-register_netdevice_notifier-error-in.patch new file mode 100644 index 0000000000..828fc82c26 --- /dev/null +++ b/queue-6.1/can-isotp-check-register_netdevice_notifier-error-in.patch @@ -0,0 +1,57 @@ +From b24eb3cfc813e3d42bdbb0a980f823fdb73cd316 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Jul 2026 16:56:56 +0800 +Subject: can: isotp: check register_netdevice_notifier() error in module init + +From: Minhong He + +[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ] + +Register the netdevice notifier before can_proto_register() and check the +return value. If protocol registration fails, unregister the notifier +before returning the error. + +Align isotp_module_init() with the reordering already done for raw.c +(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and +bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization +in bcm_module_init()")). + +Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier") +Signed-off-by: Minhong He +Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn +Signed-off-by: Marc Kleine-Budde +Signed-off-by: Sasha Levin +--- + net/can/isotp.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/net/can/isotp.c b/net/can/isotp.c +index 6ee4849e4b789..645e0bd16782d 100644 +--- a/net/can/isotp.c ++++ b/net/can/isotp.c +@@ -1825,13 +1825,18 @@ static __init int isotp_module_init(void) + + pr_info("can: isotp protocol\n"); + ++ err = register_netdevice_notifier(&canisotp_notifier); ++ if (err) ++ return err; ++ + err = can_proto_register(&isotp_can_proto); +- if (err < 0) ++ if (err < 0) { + pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err)); +- else +- register_netdevice_notifier(&canisotp_notifier); ++ unregister_netdevice_notifier(&canisotp_notifier); ++ return err; ++ } + +- return err; ++ return 0; + } + + static __exit void isotp_module_exit(void) +-- +2.53.0 + diff --git a/queue-6.1/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-6.1/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch new file mode 100644 index 0000000000..82d518daff --- /dev/null +++ b/queue-6.1/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch @@ -0,0 +1,60 @@ +From ac76dc9e4b2bbdd75070e36a0ba001ab6bb786e7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 1 Jul 2026 12:57:33 +0800 +Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA + +From: Hongling Zeng + +[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ] + +When terminating DMA transfers, active descriptors are not properly +reclaimed. Only cyclic descriptors were handled, leaving non-cyclic +descriptors and their LLI chains to be permanently leaked. + +Fix by using vchan_terminate_vdesc() which handles both cyclic and +non-cyclic descriptors by adding them to desc_terminated queue for +proper cleanup. + +Add pchan->desc != pchan->done check to prevent double-adding completed +descriptors, which would corrupt the list. + +Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller") +Signed-off-by: Hongling Zeng +Acked-by: Jernej Skrabec +Suggested-by: Frank Li +Reviewed-by: Frank Li +Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/dma/sun6i-dma.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c +index 7ca0c26f9e872..f6b5ad7683734 100644 +--- a/drivers/dma/sun6i-dma.c ++++ b/drivers/dma/sun6i-dma.c +@@ -933,16 +933,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan) + + spin_lock_irqsave(&vchan->vc.lock, flags); + +- if (vchan->cyclic) { +- vchan->cyclic = false; +- if (pchan && pchan->desc) { +- struct virt_dma_desc *vd = &pchan->desc->vd; +- struct virt_dma_chan *vc = &vchan->vc; ++ if (pchan && pchan->desc && pchan->desc != pchan->done) { ++ struct virt_dma_desc *vd = &pchan->desc->vd; + +- list_add_tail(&vd->node, &vc->desc_completed); +- } ++ vchan_terminate_vdesc(vd); + } + ++ vchan->cyclic = false; + vchan_get_all_descriptors(&vchan->vc, &head); + + if (pchan) { +-- +2.53.0 + diff --git a/queue-6.1/drm-mediatek-check-crtc-state-before-freeing.patch b/queue-6.1/drm-mediatek-check-crtc-state-before-freeing.patch new file mode 100644 index 0000000000..43dc8050c9 --- /dev/null +++ b/queue-6.1/drm-mediatek-check-crtc-state-before-freeing.patch @@ -0,0 +1,51 @@ +From f7ff63535b0c95c9c81fe23e17587a90216161da Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 23:05:28 +0800 +Subject: drm/mediatek: Check CRTC state before freeing + +From: Ruoyu Wang + +[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ] + +mtk_crtc_reset() destroys the current CRTC state only when crtc->state +is non-NULL, but it always converts crtc->state to struct mtk_crtc_state +and passes the result to kfree(). + +When reset is called without an existing state, container_of(NULL, ...) +does not produce NULL. Keep the mtk state free in the same crtc->state +guard as the helper state destruction. + +This issue was found by a static analysis checker and confirmed by +manual source review. + +Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset") +Signed-off-by: Ruoyu Wang +Reviewed-by: CK Hu +Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/ +Signed-off-by: Chun-Kuang Hu +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +index aba26ec9a1425..59b310f7327c8 100644 +--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c ++++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +@@ -183,10 +183,10 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc) + { + struct mtk_crtc_state *state; + +- if (crtc->state) ++ if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); +- +- kfree(to_mtk_crtc_state(crtc->state)); ++ kfree(to_mtk_crtc_state(crtc->state)); ++ } + crtc->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-6.1/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-6.1/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch new file mode 100644 index 0000000000..6ce606c494 --- /dev/null +++ b/queue-6.1/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch @@ -0,0 +1,47 @@ +From 8f1406262fb0e7ed599305cc7dbf3de72edac618 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 17:26:37 +0800 +Subject: forcedeth: fix UAF of txrx_stats in nv_remove + +From: Chenguang Zhao + +[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ] + +nv_remove() frees the per-CPU txrx_stats before unregister_netdev(). +Until unregister completes, ndo_get_stats64, the NAPI/xmit data path, +and nv_close()/drain may still access txrx_stats, leading to a +use-after-free. + +Free the stats only after unregister_netdev(). + +Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Reviewed-by: Zhu Yanjun +Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/nvidia/forcedeth.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c +index 486cbc8ab2242..866267df1f2c9 100644 +--- a/drivers/net/ethernet/nvidia/forcedeth.c ++++ b/drivers/net/ethernet/nvidia/forcedeth.c +@@ -6199,10 +6199,10 @@ static void nv_remove(struct pci_dev *pci_dev) + struct net_device *dev = pci_get_drvdata(pci_dev); + struct fe_priv *np = netdev_priv(dev); + +- free_percpu(np->txrx_stats); +- + unregister_netdev(dev); + ++ free_percpu(np->txrx_stats); ++ + nv_restore_mac_addr(pci_dev); + + /* restore any phy related changes */ +-- +2.53.0 + diff --git a/queue-6.1/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-6.1/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch new file mode 100644 index 0000000000..a0666b8c6f --- /dev/null +++ b/queue-6.1/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch @@ -0,0 +1,51 @@ +From 824e0ca38497802996e38fc03b22f1b9581ca420 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:19 -0300 +Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread + +From: Luiz Angelo Daros de Luca + +[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ] + +When userspace configures 'auto_update_interval' to 0 via sysfs, the +background kthread executes schedule_timeout_interruptible(0), which +returns immediately. + +If 'num_temp_sensors' is concurrently or previously set to 0, the +msleep_interruptible() delay inside adt7470_read_temperatures() also +becomes 0. This combination forces the background thread into a tight, +unbounded busy-loop, hogging the CPU and flooding the I2C bus with a +continuous stream of transactions. + +Fix this vulnerability by raising the lower limit of the clamp_val in +auto_update_interval_store() from 0 to 500 milliseconds. This guarantees +a reasonable minimum sleep window between sensor updates, protecting the +system from intentional or accidental I2C bus denial of service. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org +Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 1e837760c55db..9569f1faadeb4 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev, + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + +- temp = clamp_val(temp, 0, 60000); ++ temp = clamp_val(temp, 500, 60000); + + mutex_lock(&data->lock); + data->auto_update_interval = temp; +-- +2.53.0 + diff --git a/queue-6.1/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch b/queue-6.1/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch new file mode 100644 index 0000000000..2efe098739 --- /dev/null +++ b/queue-6.1/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch @@ -0,0 +1,83 @@ +From ec06c2c30a5c1fef13916b81fde2a4df5079e37f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:18 -0300 +Subject: hwmon: (adt7470) Fix cache updated before hardware write on I2C error + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ] + +adt7470_temp_write() and adt7470_pwm_write() update the driver's +cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing +the corresponding regmap_write(), and never check whether the write +succeeded before committing that update. If the I2C transaction fails, +the function correctly propagates the error to the caller, but the cache +silently keeps the new value, which was never actually applied to the +hardware. Subsequent reads then report a value that does not match the +device state. + +Reorder both write paths to update the cache only after a successful +regmap_write(), so the cache always reflects what was actually +written to the hardware. + +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 81d7c4ec06b0d..1e837760c55db 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va + switch (attr) { + case hwmon_temp_min: + mutex_lock(&data->lock); +- data->temp_min[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val); ++ if (!err) ++ data->temp_min[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_temp_max: + mutex_lock(&data->lock); +- data->temp_max[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val); ++ if (!err) ++ data->temp_max[channel] = val; + mutex_unlock(&data->lock); + break; + default: +@@ -839,9 +841,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + case hwmon_pwm_input: + val = clamp_val(val, 0, 255); + mutex_lock(&data->lock); +- data->pwm[channel] = val; + err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), +- data->pwm[channel]); ++ val); ++ if (!err) ++ data->pwm[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_pwm_enable: +@@ -855,10 +858,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + val--; + + mutex_lock(&data->lock); +- data->pwm_automatic[channel] = val; + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel), + pwm_auto_reg_mask, + val ? pwm_auto_reg_mask : 0); ++ if (!err) ++ data->pwm_automatic[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_pwm_freq: +-- +2.53.0 + diff --git a/queue-6.1/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch b/queue-6.1/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch new file mode 100644 index 0000000000..36b0ae88a2 --- /dev/null +++ b/queue-6.1/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch @@ -0,0 +1,80 @@ +From c83640a6d4aae7e786c317570c7f5d9b97def2ac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:23 -0300 +Subject: hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ] + +If the fan data becomes 0 between the FAN_DATA_VALID() check and the +FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash +due to a race with a concurrent update of the cached fan value. + +Fix a TOCTOU issue by reading fan data once. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/ +Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 23 ++++++++++------------- + 1 file changed, 10 insertions(+), 13 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 1ac26a510bfe2..8010ed6f86a18 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev, + static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val) + { + struct adt7470_data *data = adt7470_update_device(dev); ++ u16 fan_data; + + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (attr) { + case hwmon_fan_input: +- if (FAN_DATA_VALID(data->fan[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan[channel]); + break; + case hwmon_fan_min: +- if (FAN_DATA_VALID(data->fan_min[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan_min[channel]); + break; + case hwmon_fan_max: +- if (FAN_DATA_VALID(data->fan_max[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan_max[channel]); + break; + case hwmon_fan_alarm: + *val = !!(data->alarm & FAN_ALARM_BIT(channel)); +- break; ++ return 0; + default: + return -EOPNOTSUPP; + } + ++ if (FAN_DATA_VALID(fan_data)) ++ *val = FAN_PERIOD_TO_RPM(fan_data); ++ else ++ *val = 0; ++ + return 0; + } + +-- +2.53.0 + diff --git a/queue-6.1/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch b/queue-6.1/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch new file mode 100644 index 0000000000..34c2ec53cc --- /dev/null +++ b/queue-6.1/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch @@ -0,0 +1,115 @@ +From de3892e07ddbb1d81ee5b958e4f13956bb0bd1e9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:17 -0300 +Subject: hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ] + +During adt7470_read_temperatures(), the driver temporarily switches +the PWM channels to manual mode, performs the temperature collection, +and then restores the original configuration registers. + +However, if an I2C transaction fails at any point after entering manual +mode, the function aborts and returns immediately. This leaves the +configuration registers un-restored, permanently trapping the fans in +manual mode. + +Introduce a recovery path to ensure that the original PWM configuration +registers are always restored, even when intermediate I2C operations +fail. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++----------- + 1 file changed, 29 insertions(+), 11 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 927f8df05b7c9..81d7c4ec06b0d 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in + /* Probe for temperature sensors. Assumes lock is held */ + static int adt7470_read_temperatures(struct adt7470_data *data) + { +- unsigned long res; ++ struct device *dev = regmap_get_device(data->regmap); ++ u8 pwm[ADT7470_FAN_COUNT]; + unsigned int pwm_cfg[2]; +- int err; ++ unsigned long res; ++ int err, err2; + int i; +- u8 pwm[ADT7470_FAN_COUNT]; + + /* save pwm[1-4] config register */ + err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); +@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data) + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), + ADT7470_PWM_AUTO_MASK, 0); + if (err < 0) +- return err; ++ goto out_restore; + + /* write pwm control to whatever it was */ + err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], + ADT7470_PWM_COUNT); + if (err < 0) +- return err; ++ goto out_restore; + + /* start reading temperature sensors */ + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); + if (err < 0) +- return err; ++ goto out_restore; + + /* Delay is 200ms * number of temp sensors. */ + res = msleep_interruptible((data->num_temp_sensors >= 0 ? +@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data) + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, 0); + if (err < 0) +- return err; ++ goto out_restore; + ++out_restore: + /* restore pwm[1-4] config registers */ +- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); +- if (err < 0) +- return err; +- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); ++ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); ++ if (err2 < 0) { ++ dev_warn_ratelimited(dev, ++ "failed to restore PWM{1,2} config (%d)\n", ++ err2); ++ ++ if (!err) ++ err = err2; ++ } ++ ++ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); ++ if (err2 < 0) { ++ dev_warn_ratelimited(dev, ++ "failed to restore PWM{3,4} config (%d)\n", ++ err2); ++ ++ if (!err) ++ err = err2; ++ } ++ + if (err < 0) + return err; + +-- +2.53.0 + diff --git a/queue-6.1/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-6.1/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch new file mode 100644 index 0000000000..d41fa247bd --- /dev/null +++ b/queue-6.1/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch @@ -0,0 +1,57 @@ +From 10861844d2aa4eee21ae050b0bdf6c2bef008a6f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:24 -0300 +Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ] + +In pwm_auto_temp_store(), the parsed user input was missing bounds +checks, allowing values > 0xF to overflow into the adjacent channel's +bits. Furthermore, the value was being incorrectly written to the +pwm_automatic state array instead of pwm_auto_temp. + +Fix this by rejecting values > 0xF with -EINVAL, and assigning the +value to the correct array only after a successful I2C write. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 8010ed6f86a18..9783593a1de76 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -1057,8 +1057,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + if (temp < 0) + return temp; + ++ if (temp > 0xF) ++ return -EINVAL; ++ + mutex_lock(&data->lock); +- data->pwm_automatic[attr->index] = temp; + + if (!(attr->index % 2)) { + mask = 0xF0; +@@ -1069,6 +1071,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + } + + err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); ++ if (!err) ++ data->pwm_auto_temp[attr->index] = temp; ++ + mutex_unlock(&data->lock); + + return err < 0 ? err : count; +-- +2.53.0 + diff --git a/queue-6.1/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-6.1/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch new file mode 100644 index 0000000000..6acc8aee80 --- /dev/null +++ b/queue-6.1/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch @@ -0,0 +1,62 @@ +From 436e22790126d22d947e66f3e954962a3344c177 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:20 -0300 +Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks + +From: Luiz Angelo Daros de Luca + +[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ] + +The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are +currently defined with swapped bit values. + +According to Table 22 of the ADT7470 datasheet, the Fan Control Mode +Configuration for register 0x69 follows the exact same bit position +layout as register 0x68: +- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80 +- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40 +- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80 +- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40 + +Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40. + +This typo did not cause any functional bugs because these specific +macros are never referenced in the driver code. Instead, the driver +correctly applies the configuration by relying on the modulo parity of +the channel index (e.g., `channel % 2`) to selectively apply either +ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40). +Since the bit layout is identical between the two configuration +registers, the hardware is currently configured correctly. + +Fix the macro definitions to reflect the datasheet accurately and +prevent future bugs or confusion during code review and refactoring. +As this is a purely cosmetic fix with no functional impact, a backport +to stable kernels is not necessary. + +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 7fae4a2b3dea0..a5bef97c8e0d3 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + #define ADT7470_PWM1_AUTO_MASK 0x80 + #define ADT7470_PWM_AUTO_MASK 0xC0 + #define ADT7470_REG_PWM34_CFG 0x69 +-#define ADT7470_PWM3_AUTO_MASK 0x40 +-#define ADT7470_PWM4_AUTO_MASK 0x80 ++#define ADT7470_PWM4_AUTO_MASK 0x40 ++#define ADT7470_PWM3_AUTO_MASK 0x80 + #define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A + #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D + #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E +-- +2.53.0 + diff --git a/queue-6.1/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch b/queue-6.1/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch new file mode 100644 index 0000000000..fae72fd302 --- /dev/null +++ b/queue-6.1/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch @@ -0,0 +1,71 @@ +From 0705359c0aca88b0ed47f4276ff6edf9ff88c703 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:21 -0300 +Subject: hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read() + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ] + +During the conversion the alarm callback started interpreting the +channel index as an alarm bitmask, resulting in incorrect alarm +reporting. Compute the proper alarm bit instead. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org +Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 19 +++++++++++++++++-- + 1 file changed, 17 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 9569f1faadeb4..7fae4a2b3dea0 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + + #define ALARM2(x) ((x) << 8) + ++/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and ++ * 0x72, or BIT(0)..BIT(6) of data->alarm. ++ * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and ++ * 0x73, or BIT(8)..BIT(10) of data->alarm. ++ */ ++#define TEMP_ALARM_BIT(ch) ({ \ ++ typeof(ch) _ch = (ch); \ ++ (1 << (_ch < 7 ? _ch : _ch + 1)); \ ++}) ++ ++/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in ++ * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm. ++ */ ++#define FAN_ALARM_BIT(ch) (1 << (12 + (ch))) ++ + #define ADT7470_VENDOR 0x41 + #define ADT7470_DEVICE 0x70 + /* datasheet only mentions a revision 2 */ +@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va + *val = 1000 * data->temp_max[channel]; + break; + case hwmon_temp_alarm: +- *val = !!(data->alarm & channel); ++ *val = !!(data->alarm & TEMP_ALARM_BIT(channel)); + break; + default: + return -EOPNOTSUPP; +@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val + *val = 0; + break; + case hwmon_fan_alarm: +- *val = !!(data->alarm & (1 << (12 + channel))); ++ *val = !!(data->alarm & FAN_ALARM_BIT(channel)); + break; + default: + return -EOPNOTSUPP; +-- +2.53.0 + diff --git a/queue-6.1/hwmon-adt7470-use-cached-pwm-frequency-value.patch b/queue-6.1/hwmon-adt7470-use-cached-pwm-frequency-value.patch new file mode 100644 index 0000000000..281aa815b9 --- /dev/null +++ b/queue-6.1/hwmon-adt7470-use-cached-pwm-frequency-value.patch @@ -0,0 +1,111 @@ +From 866b14dcce4f298b2eb1c788d6ec6214fb84d8ea Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:22 -0300 +Subject: hwmon: (adt7470) Use cached PWM frequency value + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ] + +adt7470_pwm_read() currently ignores failures returned by +pwm1_freq_get(). If the register read fails, the negative error code is +returned through *val while the function itself reports success, +potentially exposing a negative PWM frequency through sysfs. + +Fix this by using the cached PWM frequency maintained by the driver, +eliminating the register access from the read path. + +Apart from the corrected error propagation and using the cached value, +no functional change is intended. + +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index a5bef97c8e0d3..1ac26a510bfe2 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -182,6 +182,7 @@ struct adt7470_data { + u8 pwm_min[ADT7470_PWM_COUNT]; + s8 pwm_tmin[ADT7470_PWM_COUNT]; + u8 pwm_auto_temp[ADT7470_PWM_COUNT]; ++ u32 pwm_freq; + + struct task_struct *auto_update; + unsigned int auto_update_interval; +@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev, + } + + /* These are the valid PWM frequencies to the nearest Hz */ +-static const int adt7470_freq_map[] = { ++static const u32 adt7470_freq_map[] = { + 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 + }; + +@@ -804,7 +805,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val + *val = 1 + data->pwm_automatic[channel]; + break; + case hwmon_pwm_freq: +- *val = pwm1_freq_get(dev); ++ *val = data->pwm_freq; + break; + default: + return -EOPNOTSUPP; +@@ -817,12 +818,14 @@ static int pwm1_freq_set(struct device *dev, long freq) + { + struct adt7470_data *data = dev_get_drvdata(dev); + unsigned int low_freq = ADT7470_CFG_LF; ++ u32 closest_freq; + int index; + int err; + + /* Round the user value given to the closest available frequency */ + index = find_closest(freq, adt7470_freq_map, + ARRAY_SIZE(adt7470_freq_map)); ++ closest_freq = adt7470_freq_map[index]; + + if (index >= 8) { + index -= 8; +@@ -840,6 +843,10 @@ static int pwm1_freq_set(struct device *dev, long freq) + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, + ADT7470_FREQ_MASK, + index << ADT7470_FREQ_SHIFT); ++ if (err < 0) ++ goto out; ++ ++ data->pwm_freq = closest_freq; + out: + mutex_unlock(&data->lock); + +@@ -1293,6 +1300,7 @@ static int adt7470_probe(struct i2c_client *client) + struct device *dev = &client->dev; + struct adt7470_data *data; + struct device *hwmon_dev; ++ int freq_val; + int err; + + data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); +@@ -1317,6 +1325,14 @@ static int adt7470_probe(struct i2c_client *client) + if (err < 0) + return err; + ++ freq_val = pwm1_freq_get(dev); ++ if (freq_val <= 0) { ++ err = freq_val < 0 ? freq_val : -EINVAL; ++ return err; ++ } ++ ++ data->pwm_freq = (u32)freq_val; ++ + /* Register sysfs hooks */ + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &adt7470_chip_info, +-- +2.53.0 + diff --git a/queue-6.1/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch b/queue-6.1/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch new file mode 100644 index 0000000000..97bfe1af2b --- /dev/null +++ b/queue-6.1/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch @@ -0,0 +1,54 @@ +From 7f896c56a62a1f8f13299e9cdcba12a8f900b544 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 15:27:28 -0700 +Subject: hwmon: (lm90) Only report alarms if driver is ready + +From: Guenter Roeck + +[ Upstream commit aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 ] + +Userspace can read sysfs attributes before driver registration is complete, +immediately after devm_hwmon_device_register_with_info() has been called. +At that time, data->hwmon_dev is not yet initialized. This can trigger +a NULL pointer access since lm90_update_device() and with it +lm90_update_alarms_locked() will be called. This call schedules +report_work and lm90_report_alarms(), which passes the still-NULL +data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer +dereference. + +Fix the problem by only scheduling the report and alert workers +data->hwmon_dev is set. + +Reported-by: Sashiko +Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/lm90.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c +index db595f7d01f8a..7ce75e64cc3ca 100644 +--- a/drivers/hwmon/lm90.c ++++ b/drivers/hwmon/lm90.c +@@ -1148,7 +1148,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) + check_enable = (client->irq || !(data->config_orig & 0x80)) && + (data->config & 0x80); + +- if (force || check_enable) ++ if (data->hwmon_dev && (force || check_enable)) + schedule_work(&data->report_work); + + /* +@@ -1156,7 +1156,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) + * alarms are all clear, and alerts are currently disabled. + * Otherwise (re)schedule worker if needed. + */ +- if (check_enable) { ++ if (check_enable && data->hwmon_dev) { + if (!(data->current_alarms & data->alert_alarms)) { + dev_dbg(&client->dev, "Re-enabling ALERT#\n"); + lm90_update_confreg(data, data->config & ~0x80); +-- +2.53.0 + diff --git a/queue-6.1/hwmon-nct6755-add-support-for-nct6799d.patch b/queue-6.1/hwmon-nct6755-add-support-for-nct6799d.patch new file mode 100644 index 0000000000..28089d762b --- /dev/null +++ b/queue-6.1/hwmon-nct6755-add-support-for-nct6799d.patch @@ -0,0 +1,320 @@ +From 55ab5ac3aa6ed41ac6a7c9ff4af08b4de844dff7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 28 Dec 2022 05:57:44 -0800 +Subject: hwmon: (nct6755) Add support for NCT6799D + +From: Guenter Roeck + +[ Upstream commit aee395bb190564a3fa22aa65c60812c25410e94a ] + +NCT6799D is mostly compatible to NCT6798D, with minor variations. + +Note that NCT6798D and NCT6799D have a new means to select temperature +sources, and to report temperatures from those sources. This is not +currently implemented, meaning that most likely not all temperatures +are reported. + +Cc: Sebastian Arnhold +Cc: Ahmad Khalifa +Signed-off-by: Guenter Roeck +Tested-by: Sebastian Arnhold +Tested-by: Corentin Labbe +Link: https://lore.kernel.org/r/20221228135744.281752-1-linux@roeck-us.net +Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116") +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 55 ++++++++++++++++++++++++++++++-- + drivers/hwmon/nct6775-i2c.c | 2 ++ + drivers/hwmon/nct6775-platform.c | 41 ++++++++++++++++++++++-- + drivers/hwmon/nct6775.h | 2 +- + 4 files changed, 94 insertions(+), 6 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index ec3ff4e9a9abd..3a61acec7a345 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -33,6 +33,7 @@ + * (0xd451) + * nct6798d 14 7 7 2+6 0xd428 0xc1 0x5ca3 + * (0xd429) ++ * nct6799d 14 7 7 2+6 0xd802 0xc1 0x5ca3 + * + * #temp lists the number of monitored temperature sources (first value) plus + * the number of directly connectable temperature sensors (second value). +@@ -73,6 +74,7 @@ static const char * const nct6775_device_names[] = { + "nct6796", + "nct6797", + "nct6798", ++ "nct6799", + }; + + /* Common and NCT6775 specific data */ +@@ -381,7 +383,7 @@ static const u16 NCT6779_REG_TEMP_OVER[ARRAY_SIZE(NCT6779_REG_TEMP)] = { + 0x39, 0x155 }; + + static const u16 NCT6779_REG_TEMP_OFFSET[] = { +- 0x454, 0x455, 0x456, 0x44a, 0x44b, 0x44c }; ++ 0x454, 0x455, 0x456, 0x44a, 0x44b, 0x44c, 0x44d, 0x449 }; + + static const char *const nct6779_temp_label[] = { + "", +@@ -654,6 +656,44 @@ static const char *const nct6798_temp_label[] = { + #define NCT6798_TEMP_MASK 0xbfff0ffe + #define NCT6798_VIRT_TEMP_MASK 0x80000c00 + ++static const char *const nct6799_temp_label[] = { ++ "", ++ "SYSTIN", ++ "CPUTIN", ++ "AUXTIN0", ++ "AUXTIN1", ++ "AUXTIN2", ++ "AUXTIN3", ++ "AUXTIN4", ++ "SMBUSMASTER 0", ++ "SMBUSMASTER 1", ++ "Virtual_TEMP", ++ "Virtual_TEMP", ++ "", ++ "AUXTIN5", ++ "", ++ "", ++ "PECI Agent 0", ++ "PECI Agent 1", ++ "PCH_CHIP_CPU_MAX_TEMP", ++ "PCH_CHIP_TEMP", ++ "PCH_CPU_TEMP", ++ "PCH_MCH_TEMP", ++ "Agent0 Dimm0", ++ "Agent0 Dimm1", ++ "Agent1 Dimm0", ++ "Agent1 Dimm1", ++ "BYTE_TEMP0", ++ "BYTE_TEMP1", ++ "PECI Agent 0 Calibration", /* undocumented */ ++ "PECI Agent 1 Calibration", /* undocumented */ ++ "", ++ "Virtual_TEMP" ++}; ++ ++#define NCT6799_TEMP_MASK 0xbfff2ffe ++#define NCT6799_VIRT_TEMP_MASK 0x80000c00 ++ + /* NCT6102D/NCT6106D specific data */ + + #define NCT6106_REG_VBAT 0x318 +@@ -1109,6 +1149,7 @@ bool nct6775_reg_is_word_sized(struct nct6775_data *data, u16 reg) + case nct6796: + case nct6797: + case nct6798: ++ case nct6799: + return reg == 0x150 || reg == 0x153 || reg == 0x155 || + (reg & 0xfff0) == 0x4c0 || + reg == 0x402 || +@@ -1462,6 +1503,7 @@ static int nct6775_update_pwm_limits(struct device *dev) + case nct6796: + case nct6797: + case nct6798: ++ case nct6799: + err = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i], ®); + if (err) + return err; +@@ -3119,6 +3161,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, + case nct6796: + case nct6797: + case nct6798: ++ case nct6799: + err = nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val); + if (err) + break; +@@ -3817,10 +3860,12 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + case nct6796: + case nct6797: + case nct6798: ++ case nct6799: + data->in_num = 15; + data->pwm_num = (data->kind == nct6796 || + data->kind == nct6797 || +- data->kind == nct6798) ? 7 : 6; ++ data->kind == nct6798 || ++ data->kind == nct6799) ? 7 : 6; + data->auto_pwm_num = 4; + data->has_fan_div = false; + data->temp_fixed_num = 6; +@@ -3869,6 +3914,11 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + data->temp_mask = NCT6798_TEMP_MASK; + data->virt_temp_mask = NCT6798_VIRT_TEMP_MASK; + break; ++ case nct6799: ++ data->temp_label = nct6799_temp_label; ++ data->temp_mask = NCT6799_TEMP_MASK; ++ data->virt_temp_mask = NCT6799_VIRT_TEMP_MASK; ++ break; + } + + data->REG_CONFIG = NCT6775_REG_CONFIG; +@@ -3928,6 +3978,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + case nct6796: + case nct6797: + case nct6798: ++ case nct6799: + data->REG_TSI_TEMP = NCT6796_REG_TSI_TEMP; + num_reg_tsi_temp = ARRAY_SIZE(NCT6796_REG_TSI_TEMP); + break; +diff --git a/drivers/hwmon/nct6775-i2c.c b/drivers/hwmon/nct6775-i2c.c +index e1bcd11461913..779ce65db1a15 100644 +--- a/drivers/hwmon/nct6775-i2c.c ++++ b/drivers/hwmon/nct6775-i2c.c +@@ -87,6 +87,7 @@ static const struct of_device_id __maybe_unused nct6775_i2c_of_match[] = { + { .compatible = "nuvoton,nct6796", .data = (void *)nct6796, }, + { .compatible = "nuvoton,nct6797", .data = (void *)nct6797, }, + { .compatible = "nuvoton,nct6798", .data = (void *)nct6798, }, ++ { .compatible = "nuvoton,nct6799", .data = (void *)nct6799, }, + { }, + }; + MODULE_DEVICE_TABLE(of, nct6775_i2c_of_match); +@@ -104,6 +105,7 @@ static const struct i2c_device_id nct6775_i2c_id[] = { + { "nct6796", nct6796 }, + { "nct6797", nct6797 }, + { "nct6798", nct6798 }, ++ { "nct6799", nct6799 }, + { } + }; + MODULE_DEVICE_TABLE(i2c, nct6775_i2c_id); +diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c +index 76c6b564d7fc4..5ca6f42c35747 100644 +--- a/drivers/hwmon/nct6775-platform.c ++++ b/drivers/hwmon/nct6775-platform.c +@@ -35,6 +35,7 @@ static const char * const nct6775_sio_names[] __initconst = { + "NCT6796D", + "NCT6797D", + "NCT6798D", ++ "NCT6799D", + }; + + static unsigned short force_id; +@@ -85,6 +86,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); + #define SIO_NCT6796_ID 0xd420 + #define SIO_NCT6797_ID 0xd450 + #define SIO_NCT6798_ID 0xd428 ++#define SIO_NCT6799_ID 0xd800 + #define SIO_ID_MASK 0xFFF8 + + /* +@@ -418,7 +420,7 @@ static int nct6775_resume(struct device *dev) + if (data->kind == nct6791 || data->kind == nct6792 || + data->kind == nct6793 || data->kind == nct6795 || + data->kind == nct6796 || data->kind == nct6797 || +- data->kind == nct6798) ++ data->kind == nct6798 || data->kind == nct6799) + nct6791_enable_io_mapping(sio_data); + + sio_data->sio_exit(sio_data); +@@ -565,7 +567,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio + } else { + /* + * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, +- * NCT6797D, NCT6798D ++ * NCT6797D, NCT6798D, NCT6799D + */ + int cr1a = sio_data->sio_inb(sio_data, 0x1a); + int cr1b = sio_data->sio_inb(sio_data, 0x1b); +@@ -575,12 +577,17 @@ nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio + int cr2b = sio_data->sio_inb(sio_data, 0x2b); + int cr2d = sio_data->sio_inb(sio_data, 0x2d); + int cr2f = sio_data->sio_inb(sio_data, 0x2f); ++ bool vsb_ctl_en = cr2f & BIT(0); + bool dsw_en = cr2f & BIT(3); + bool ddr4_en = cr2f & BIT(4); ++ bool as_seq1_en = cr2f & BIT(7); + int cre0; ++ int cre6; + int creb; + int cred; + ++ cre6 = sio_data->sio_inb(sio_data, 0xe0); ++ + sio_data->sio_select(sio_data, NCT6775_LD_12); + cre0 = sio_data->sio_inb(sio_data, 0xe0); + creb = sio_data->sio_inb(sio_data, 0xeb); +@@ -683,6 +690,29 @@ nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio + pwm7pin = !(cr1d & (BIT(2) | BIT(3))); + pwm7pin |= cr2d & BIT(7); + pwm7pin |= creb & BIT(2); ++ break; ++ case nct6799: ++ fan4pin = cr1c & BIT(6); ++ fan5pin = cr1c & BIT(7); ++ ++ fan6pin = !(cr1b & BIT(0)) && (cre0 & BIT(3)); ++ fan6pin |= cre6 & BIT(5); ++ fan6pin |= creb & BIT(5); ++ fan6pin |= !as_seq1_en && (cr2a & BIT(4)); ++ ++ fan7pin = cr1b & BIT(5); ++ fan7pin |= !vsb_ctl_en && !(cr2b & BIT(2)); ++ fan7pin |= creb & BIT(3); ++ ++ pwm6pin = !(cr1b & BIT(0)) && (cre0 & BIT(4)); ++ pwm6pin |= !as_seq1_en && !(cred & BIT(2)) && (cr2a & BIT(3)); ++ pwm6pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); ++ pwm6pin |= cre6 & BIT(3); ++ ++ pwm7pin = !vsb_ctl_en && !(cr1d & (BIT(2) | BIT(3))); ++ pwm7pin |= creb & BIT(2); ++ pwm7pin |= cr2d & BIT(7); ++ + break; + default: /* NCT6779D */ + break; +@@ -838,6 +868,7 @@ static int nct6775_platform_probe_init(struct nct6775_data *data) + case nct6796: + case nct6797: + case nct6798: ++ case nct6799: + break; + } + +@@ -876,6 +907,7 @@ static int nct6775_platform_probe_init(struct nct6775_data *data) + case nct6796: + case nct6797: + case nct6798: ++ case nct6799: + tmp |= 0x7e; + break; + } +@@ -1005,6 +1037,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) + case SIO_NCT6798_ID: + sio_data->kind = nct6798; + break; ++ case SIO_NCT6799_ID: ++ sio_data->kind = nct6799; ++ break; + default: + if (val != 0xffff) + pr_debug("unsupported chip ID: 0x%04x\n", val); +@@ -1033,7 +1068,7 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) + if (sio_data->kind == nct6791 || sio_data->kind == nct6792 || + sio_data->kind == nct6793 || sio_data->kind == nct6795 || + sio_data->kind == nct6796 || sio_data->kind == nct6797 || +- sio_data->kind == nct6798) ++ sio_data->kind == nct6798 || sio_data->kind == nct6799) + nct6791_enable_io_mapping(sio_data); + + sio_data->sio_exit(sio_data); +diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h +index be41848c3cd29..44f79c5726a9c 100644 +--- a/drivers/hwmon/nct6775.h ++++ b/drivers/hwmon/nct6775.h +@@ -5,7 +5,7 @@ + #include + + enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792, +- nct6793, nct6795, nct6796, nct6797, nct6798 }; ++ nct6793, nct6795, nct6796, nct6797, nct6798, nct6799 }; + enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; + + #define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/ +-- +2.53.0 + diff --git a/queue-6.1/hwmon-nct6775-add-support-for-18-in-readings-for-nct.patch b/queue-6.1/hwmon-nct6775-add-support-for-18-in-readings-for-nct.patch new file mode 100644 index 0000000000..e4bc82218f --- /dev/null +++ b/queue-6.1/hwmon-nct6775-add-support-for-18-in-readings-for-nct.patch @@ -0,0 +1,166 @@ +From d6f39722469e686eb36439c30033627afd8fe80e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 19 Jul 2023 23:41:42 +0100 +Subject: hwmon: (nct6775) Add support for 18 IN readings for nct6799 + +From: Ahmad Khalifa + +[ Upstream commit 4f65c15cf70eb22c074889af60b9d2bcffbb375a ] + +* Add additional VIN/IN_MIN/IN_MAX register values +* Separate ALARM/BEEP bits for nct6799 +* Update scaling factors for nct6799 + +Registers/alarms match for NCT6796D-S and NCT6799D-R +Tested on NCT6799D-R for new IN/MIN/MAX and ALARMS + +Signed-off-by: Ahmad Khalifa +Link: https://lore.kernel.org/r/20230719224142.411237-1-ahmad@khalifa.ws +Signed-off-by: Guenter Roeck +Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116") +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 51 +++++++++++++++++++++++++++++------- + drivers/hwmon/nct6775.h | 5 ++-- + 2 files changed, 45 insertions(+), 11 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index 34b13194c91c5..c2deff486ef3c 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -79,14 +79,17 @@ static const char * const nct6775_device_names[] = { + + /* Common and NCT6775 specific data */ + +-/* Voltage min/max registers for nr=7..14 are in bank 5 */ ++/* ++ * Voltage min/max registers for nr=7..14 are in bank 5 ++ * min/max: 15-17 for NCT6799 only ++ */ + + static const u16 NCT6775_REG_IN_MAX[] = { + 0x2b, 0x2d, 0x2f, 0x31, 0x33, 0x35, 0x37, 0x554, 0x556, 0x558, 0x55a, +- 0x55c, 0x55e, 0x560, 0x562 }; ++ 0x55c, 0x55e, 0x560, 0x562, 0x564, 0x570, 0x572 }; + static const u16 NCT6775_REG_IN_MIN[] = { + 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x555, 0x557, 0x559, 0x55b, +- 0x55d, 0x55f, 0x561, 0x563 }; ++ 0x55d, 0x55f, 0x561, 0x563, 0x565, 0x571, 0x573 }; + static const u16 NCT6775_REG_IN[] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x550, 0x551, 0x552 + }; +@@ -255,7 +258,8 @@ static const s8 NCT6776_ALARM_BITS[NUM_ALARM_BITS] = { + 12, 9, /* intr0-intr1 */ + }; + +-static const u16 NCT6776_REG_BEEP[NUM_REG_BEEP] = { 0xb2, 0xb3, 0xb4, 0xb5 }; ++/* 0xbf: nct6799 only */ ++static const u16 NCT6776_REG_BEEP[NUM_REG_BEEP] = { 0xb2, 0xb3, 0xb4, 0xb5, 0xbf }; + + static const s8 NCT6776_BEEP_BITS[NUM_BEEP_BITS] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, /* in0-in11 */ +@@ -327,9 +331,16 @@ static const u16 NCT6776_REG_TSI_TEMP[] = { + + /* NCT6779 specific data */ + ++/* ++ * 15-17 for NCT6799 only, register labels are: ++ * CPUVC, VIN1, AVSB, 3VCC, VIN0, VIN8, VIN4, 3VSB ++ * VBAT, VTT, VIN5, VIN6, VIN2, VIN3, VIN7, VIN9 ++ * VHIF, VIN10 ++ */ + static const u16 NCT6779_REG_IN[] = { + 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487, +- 0x488, 0x489, 0x48a, 0x48b, 0x48c, 0x48d, 0x48e }; ++ 0x488, 0x489, 0x48a, 0x48b, 0x48c, 0x48d, 0x48e, 0x48f, ++ 0x470, 0x471}; + + static const u16 NCT6779_REG_ALARM[NUM_REG_ALARM] = { + 0x459, 0x45A, 0x45B, 0x568 }; +@@ -643,6 +654,22 @@ static const char *const nct6798_temp_label[] = { + #define NCT6798_TEMP_MASK 0xbfff0ffe + #define NCT6798_VIRT_TEMP_MASK 0x80000c00 + ++static const s8 NCT6799_ALARM_BITS[NUM_ALARM_BITS] = { ++ 0, 1, 2, 3, 8, -1, 20, 16, 17, 24, 25, 26, /* in0-in11 */ ++ 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */ ++ 6, 7, 11, 10, 23, 33, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ ++ 4, 5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 12, 9, /* intr0-intr1 */ ++}; ++ ++static const s8 NCT6799_BEEP_BITS[NUM_BEEP_BITS] = { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, /* in0-in11 */ ++ 12, 13, 14, 15, 34, 35, -1, -1, -1, -1, -1, -1, /* in12-in23 */ ++ 25, 26, 27, 28, 29, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ ++ 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 30, 31, 24 /* intr0-intr1, beep_en */ ++}; ++ + static const char *const nct6799_temp_label[] = { + "", + "SYSTIN", +@@ -937,12 +964,12 @@ static const u16 scale_in[15] = { + /* + * NCT6798 scaling: + * CPUVC, IN1, AVSB, 3VCC, IN0, IN8, IN4, 3VSB, VBAT, VTT, IN5, IN6, IN2, +- * IN3, IN7 +- * Additional scales to be added later: IN9 (800), VHIF (1600) ++ * IN3, IN7, IN9, VHIF, IN10 ++ * 15-17 for NCT6799 only + */ +-static const u16 scale_in_6798[15] = { ++static const u16 scale_in_6798[NUM_IN] = { + 800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 1600, 1600, 1600, 800, +- 800, 800 ++ 800, 800, 800, 1600, 800 + }; + + static inline long in_from_reg(u8 reg, u8 nr, const u16 *scales) +@@ -3970,7 +3997,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + case nct6796: + case nct6797: + case nct6798: ++ data->REG_TSI_TEMP = NCT6796_REG_TSI_TEMP; ++ num_reg_tsi_temp = ARRAY_SIZE(NCT6796_REG_TSI_TEMP); ++ break; + case nct6799: ++ data->in_num = 18; ++ data->ALARM_BITS = NCT6799_ALARM_BITS; ++ data->BEEP_BITS = NCT6799_BEEP_BITS; + data->REG_TSI_TEMP = NCT6796_REG_TSI_TEMP; + num_reg_tsi_temp = ARRAY_SIZE(NCT6796_REG_TSI_TEMP); + break; +diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h +index c752bc7bbe722..edcde39c47915 100644 +--- a/drivers/hwmon/nct6775.h ++++ b/drivers/hwmon/nct6775.h +@@ -16,6 +16,7 @@ enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; + #define NUM_REG_BEEP 5 /* Max number of beep registers */ + + #define NUM_FAN 7 ++#define NUM_IN 18 + + struct nct6775_data { + int addr; /* IO base of hw monitor block */ +@@ -97,7 +98,7 @@ struct nct6775_data { + /* Register values */ + u8 bank; /* current register bank */ + u8 in_num; /* number of in inputs we have */ +- u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */ ++ u8 in[NUM_IN][3]; /* [0]=in, [1]=in_max, [2]=in_min */ + const u16 *scale_in; /* internal scaling factors */ + unsigned int rpm[NUM_FAN]; + u16 fan_min[NUM_FAN]; +@@ -166,7 +167,7 @@ struct nct6775_data { + u16 have_temp; + u16 have_temp_fixed; + u16 have_tsi_temp; +- u16 have_in; ++ u32 have_in; + + /* Remember extra register values over suspend/resume */ + u8 vbat; +-- +2.53.0 + diff --git a/queue-6.1/hwmon-nct6775-additional-temp-registers-for-nct6799.patch b/queue-6.1/hwmon-nct6775-additional-temp-registers-for-nct6799.patch new file mode 100644 index 0000000000..dd2bc79e66 --- /dev/null +++ b/queue-6.1/hwmon-nct6775-additional-temp-registers-for-nct6799.patch @@ -0,0 +1,276 @@ +From 43ac06f9c6b8920c589dfe82fa3b6051c8f0335e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 2 Aug 2023 19:58:21 +0100 +Subject: hwmon: (nct6775) Additional TEMP registers for nct6799 + +From: Ahmad Khalifa + +[ Upstream commit b7f1f7b2523a6a4382f12fe953380b847b80e09d ] + +Additional TEMP registers for nct6798d, nct6799d-r and nct6796d-s +This allows the max/max_hyst/crit attributes to be shown/stored + +* Increase NUM_TEMP from 10 to 12 +* Separate TEMP/MON_TEMP/OVER/HYST/CRIT registers +* Rename "PECI Calibration" to include "TSI" too +* Update ALARM/BEEP bits for temps for 6799 +* For 6799, keep temp_fixed_num at 6, but increase + num_temp_alarms/num_temp_beeps to 7/8 + +Tested with NCT6799D-R showing additional sysfs attributes: +* temp3-temp8: max/max_hyst/beep/alarm +* temp3-temp6: crit/offset + +Signed-off-by: Ahmad Khalifa +Link: https://lore.kernel.org/r/20230802185820.3642399-1-ahmad@khalifa.ws +[groeck: Addressed cosmetic checkpatch complaints] +Signed-off-by: Guenter Roeck +Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116") +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 150 ++++++++++++++++++++++++++++------- + drivers/hwmon/nct6775.h | 2 +- + 2 files changed, 121 insertions(+), 31 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index c2deff486ef3c..acb0fbb0eabca 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -616,6 +616,28 @@ static const char *const nct6796_temp_label[] = { + + static const u16 NCT6796_REG_TSI_TEMP[] = { 0x409, 0x40b }; + ++static const u16 NCT6798_REG_TEMP[] = { ++ 0x27, 0x150, 0x670, 0x672, 0x674, 0x676, 0x678, 0x67a}; ++ ++static const u16 NCT6798_REG_TEMP_SOURCE[] = { ++ 0x621, 0x622, 0xc26, 0xc27, 0xc28, 0xc29, 0xc2a, 0xc2b }; ++ ++static const u16 NCT6798_REG_TEMP_MON[] = { ++ 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d, 0x4a0 }; ++static const u16 NCT6798_REG_TEMP_OVER[] = { ++ 0x39, 0x155, 0xc1a, 0xc1b, 0xc1c, 0xc1d, 0xc1e, 0xc1f }; ++static const u16 NCT6798_REG_TEMP_HYST[] = { ++ 0x3a, 0x153, 0xc20, 0xc21, 0xc22, 0xc23, 0xc24, 0xc25 }; ++ ++static const u16 NCT6798_REG_TEMP_CRIT[32] = { ++ 0x135, 0x235, 0x335, 0x835, 0x935, 0xa35, 0xb35, 0 }; ++ ++static const u16 NCT6798_REG_TEMP_ALTERNATE[32] = { ++ 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0x496, 0, ++ 0, 0, 0, 0, 0x4a2, 0, 0, 0, ++ 0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407, ++ 0x408, 0x419, 0x41a, 0x4f4, 0x4f5 }; ++ + static const char *const nct6798_temp_label[] = { + "", + "SYSTIN", +@@ -654,11 +676,14 @@ static const char *const nct6798_temp_label[] = { + #define NCT6798_TEMP_MASK 0xbfff0ffe + #define NCT6798_VIRT_TEMP_MASK 0x80000c00 + ++static const u16 NCT6799_REG_ALARM[NUM_REG_ALARM] = { ++ 0x459, 0x45A, 0x45B, 0x568, 0x45D, 0xc01 }; ++ + static const s8 NCT6799_ALARM_BITS[NUM_ALARM_BITS] = { + 0, 1, 2, 3, 8, -1, 20, 16, 17, 24, 25, 26, /* in0-in11 */ + 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */ + 6, 7, 11, 10, 23, 33, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ +- 4, 5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 4, 5, 40, 41, 42, 43, 44, -1, -1, -1, -1, -1, /* temp1-temp12 */ + 12, 9, /* intr0-intr1 */ + }; + +@@ -666,10 +691,11 @@ static const s8 NCT6799_BEEP_BITS[NUM_BEEP_BITS] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, /* in0-in11 */ + 12, 13, 14, 15, 34, 35, -1, -1, -1, -1, -1, -1, /* in12-in23 */ + 25, 26, 27, 28, 29, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ +- 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 16, 17, 18, 19, 20, 21, 22, 23, -1, -1, -1, -1, /* temp1-temp12 */ + 30, 31, 24 /* intr0-intr1, beep_en */ + }; + ++/* PECI Calibration only for NCT6799D, not NCT6796D-S */ + static const char *const nct6799_temp_label[] = { + "", + "SYSTIN", +@@ -699,8 +725,8 @@ static const char *const nct6799_temp_label[] = { + "Agent1 Dimm1", + "BYTE_TEMP0", + "BYTE_TEMP1", +- "PECI Agent 0 Calibration", /* undocumented */ +- "PECI Agent 1 Calibration", /* undocumented */ ++ "PECI/TSI Agent 0 Calibration", ++ "PECI/TSI Agent 1 Calibration", + "", + "Virtual_TEMP" + }; +@@ -3878,13 +3904,9 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + case nct6795: + case nct6796: + case nct6797: +- case nct6798: +- case nct6799: + data->in_num = 15; + data->pwm_num = (data->kind == nct6796 || +- data->kind == nct6797 || +- data->kind == nct6798 || +- data->kind == nct6799) ? 7 : 6; ++ data->kind == nct6797) ? 7 : 6; + data->auto_pwm_num = 4; + data->has_fan_div = false; + data->temp_fixed_num = 6; +@@ -3928,16 +3950,6 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + data->temp_mask = NCT6796_TEMP_MASK; + data->virt_temp_mask = NCT6796_VIRT_TEMP_MASK; + break; +- case nct6798: +- data->temp_label = nct6798_temp_label; +- data->temp_mask = NCT6798_TEMP_MASK; +- data->virt_temp_mask = NCT6798_VIRT_TEMP_MASK; +- break; +- case nct6799: +- data->temp_label = nct6799_temp_label; +- data->temp_mask = NCT6799_TEMP_MASK; +- data->virt_temp_mask = NCT6799_VIRT_TEMP_MASK; +- break; + } + + data->REG_CONFIG = NCT6775_REG_CONFIG; +@@ -3996,14 +4008,6 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + case nct6795: + case nct6796: + case nct6797: +- case nct6798: +- data->REG_TSI_TEMP = NCT6796_REG_TSI_TEMP; +- num_reg_tsi_temp = ARRAY_SIZE(NCT6796_REG_TSI_TEMP); +- break; +- case nct6799: +- data->in_num = 18; +- data->ALARM_BITS = NCT6799_ALARM_BITS; +- data->BEEP_BITS = NCT6799_BEEP_BITS; + data->REG_TSI_TEMP = NCT6796_REG_TSI_TEMP; + num_reg_tsi_temp = ARRAY_SIZE(NCT6796_REG_TSI_TEMP); + break; +@@ -4012,9 +4016,6 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + break; + } + +- if (data->kind == nct6798 || data->kind == nct6799) +- data->scale_in = scale_in_6798; +- + reg_temp = NCT6779_REG_TEMP; + num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); + if (data->kind == nct6791) { +@@ -4030,6 +4031,95 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6779_REG_TEMP_CRIT; + ++ break; ++ case nct6798: ++ case nct6799: ++ data->in_num = data->kind == nct6799 ? 18 : 15; ++ data->scale_in = scale_in_6798; ++ data->pwm_num = 7; ++ data->auto_pwm_num = 4; ++ data->has_fan_div = false; ++ data->temp_fixed_num = 6; ++ data->num_temp_alarms = 7; ++ data->num_temp_beeps = 8; ++ ++ data->ALARM_BITS = NCT6799_ALARM_BITS; ++ data->BEEP_BITS = NCT6799_BEEP_BITS; ++ ++ data->fan_from_reg = fan_from_reg_rpm; ++ data->fan_from_reg_min = fan_from_reg13; ++ data->target_temp_mask = 0xff; ++ data->tolerance_mask = 0x07; ++ data->speed_tolerance_limit = 63; ++ ++ switch (data->kind) { ++ default: ++ case nct6798: ++ data->temp_label = nct6798_temp_label; ++ data->temp_mask = NCT6798_TEMP_MASK; ++ data->virt_temp_mask = NCT6798_VIRT_TEMP_MASK; ++ break; ++ case nct6799: ++ data->temp_label = nct6799_temp_label; ++ data->temp_mask = NCT6799_TEMP_MASK; ++ data->virt_temp_mask = NCT6799_VIRT_TEMP_MASK; ++ break; ++ } ++ ++ data->REG_CONFIG = NCT6775_REG_CONFIG; ++ data->REG_VBAT = NCT6775_REG_VBAT; ++ data->REG_DIODE = NCT6775_REG_DIODE; ++ data->DIODE_MASK = NCT6775_DIODE_MASK; ++ data->REG_VIN = NCT6779_REG_IN; ++ data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; ++ data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; ++ data->REG_TARGET = NCT6775_REG_TARGET; ++ data->REG_FAN = NCT6779_REG_FAN; ++ data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; ++ data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; ++ data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; ++ data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; ++ data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; ++ data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME; ++ data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME; ++ data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; ++ data->REG_PWM[0] = NCT6775_REG_PWM; ++ data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; ++ data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; ++ data->REG_PWM[5] = NCT6791_REG_WEIGHT_DUTY_STEP; ++ data->REG_PWM[6] = NCT6791_REG_WEIGHT_DUTY_BASE; ++ data->REG_PWM_READ = NCT6775_REG_PWM_READ; ++ data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; ++ data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; ++ data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP; ++ data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM; ++ data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP; ++ data->REG_CRITICAL_TEMP_TOLERANCE = NCT6775_REG_CRITICAL_TEMP_TOLERANCE; ++ data->REG_CRITICAL_PWM_ENABLE = NCT6779_REG_CRITICAL_PWM_ENABLE; ++ data->CRITICAL_PWM_ENABLE_MASK = NCT6779_CRITICAL_PWM_ENABLE_MASK; ++ data->REG_CRITICAL_PWM = NCT6779_REG_CRITICAL_PWM; ++ data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; ++ data->REG_TEMP_SOURCE = NCT6798_REG_TEMP_SOURCE; ++ data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; ++ data->REG_WEIGHT_TEMP_SEL = NCT6791_REG_WEIGHT_TEMP_SEL; ++ data->REG_WEIGHT_TEMP[0] = NCT6791_REG_WEIGHT_TEMP_STEP; ++ data->REG_WEIGHT_TEMP[1] = NCT6791_REG_WEIGHT_TEMP_STEP_TOL; ++ data->REG_WEIGHT_TEMP[2] = NCT6791_REG_WEIGHT_TEMP_BASE; ++ data->REG_ALARM = NCT6799_REG_ALARM; ++ data->REG_BEEP = NCT6792_REG_BEEP; ++ data->REG_TSI_TEMP = NCT6796_REG_TSI_TEMP; ++ num_reg_tsi_temp = ARRAY_SIZE(NCT6796_REG_TSI_TEMP); ++ ++ reg_temp = NCT6798_REG_TEMP; ++ num_reg_temp = ARRAY_SIZE(NCT6798_REG_TEMP); ++ reg_temp_mon = NCT6798_REG_TEMP_MON; ++ num_reg_temp_mon = ARRAY_SIZE(NCT6798_REG_TEMP_MON); ++ reg_temp_over = NCT6798_REG_TEMP_OVER; ++ reg_temp_hyst = NCT6798_REG_TEMP_HYST; ++ reg_temp_config = NCT6779_REG_TEMP_CONFIG; ++ reg_temp_alternate = NCT6798_REG_TEMP_ALTERNATE; ++ reg_temp_crit = NCT6798_REG_TEMP_CRIT; ++ + break; + default: + return -ENODEV; +diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h +index edcde39c47915..296eff99d0038 100644 +--- a/drivers/hwmon/nct6775.h ++++ b/drivers/hwmon/nct6775.h +@@ -8,7 +8,7 @@ enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792, + nct6793, nct6795, nct6796, nct6797, nct6798, nct6799 }; + enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; + +-#define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/ ++#define NUM_TEMP 12 /* Max number of temp attribute sets w/ limits*/ + #define NUM_TEMP_FIXED 6 /* Max number of fixed temp attribute sets */ + #define NUM_TSI_TEMP 8 /* Max number of TSI temp register pairs */ + +-- +2.53.0 + diff --git a/queue-6.1/hwmon-nct6775-core-fix-number-of-temperature-registe.patch b/queue-6.1/hwmon-nct6775-core-fix-number-of-temperature-registe.patch new file mode 100644 index 0000000000..52fdfd85d0 --- /dev/null +++ b/queue-6.1/hwmon-nct6775-core-fix-number-of-temperature-registe.patch @@ -0,0 +1,90 @@ +From d5f1f62f560b852a3156175f7731ebf80f5c9f88 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 07:14:36 -0700 +Subject: hwmon: (nct6775-core) Fix number of temperature registers for NCT6116 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guenter Roeck + +[ Upstream commit b0e8adb2ccb43009796897ced09f91636685c9d3 ] + +Unlike NCT6106, NCT6116 only has three temperature registers, and with +it only three temperature source and temperature source configuration +registers. The register addresses match those of NCT6106 and can be +re-used. + +The code used a separate array to list the temperature source registers +for NCT6116, but used the size of the NCT6106 register array to set +the number of registers. The NCT6106 register array provides six addresses, +while the temperature source register array for NCT6116 only provides three +addresses. This causes a KASAN report. + +BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775] +Read of size 2 at addr ffffffffc19561a6 by task modprobe/954 +... +Call Trace: + dump_stack+0x7d/0xa7 + print_address_description.constprop.0+0x1c/0x220 + ? __kasan_kmalloc.constprop.0+0xc9/0xd0 + ? __kmalloc_node_track_caller+0x194/0x5b0 + ? nct6775_probe+0x936/0x46f0 [nct6775] + ? nct6775_probe+0x936/0x46f0 [nct6775] +... + +Fix the problem by hard-coding the number of temperature and temperature +configuration registers to three for NCT6116. Drop the unnecessary +NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE. + +Reported-by: Florian Bezdeka +Closes: https://lore.kernel.org/linux-hwmon/57cfc3fa-d4e9-4c10-8aa7-4ad0af7ebebe@roeck-us.net/T/#t +Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") +Cc: Björn Gerhart +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index c7b3961e189ce..887102e51067a 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -845,8 +845,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 }; + static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 }; + static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 }; + static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 }; +-static const u16 NCT6116_REG_TEMP_SOURCE[] = { +- 0xb0, 0xb1, 0xb2 }; + + static const u16 NCT6116_REG_CRITICAL_TEMP[] = { + 0x11a, 0x12a, 0x13a, 0x19a, 0x1aa }; +@@ -3645,7 +3643,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + = NCT6106_CRITICAL_PWM_ENABLE_MASK; + data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM; + data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; +- data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE; ++ data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; +@@ -3659,13 +3657,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + + reg_temp = NCT6106_REG_TEMP; + reg_temp_mon = NCT6106_REG_TEMP_MON; +- num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); ++ num_reg_temp = 3; + num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON); + num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP); + reg_temp_over = NCT6106_REG_TEMP_OVER; + reg_temp_hyst = NCT6106_REG_TEMP_HYST; + reg_temp_config = NCT6106_REG_TEMP_CONFIG; +- num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG); ++ num_reg_temp_config = 3; + reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6106_REG_TEMP_CRIT; + reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; +-- +2.53.0 + diff --git a/queue-6.1/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-6.1/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch new file mode 100644 index 0000000000..0eeca0264c --- /dev/null +++ b/queue-6.1/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch @@ -0,0 +1,74 @@ +From cda70bf9296a12fa70a95ee4fccf71f7d215c0ab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 13:35:37 -0700 +Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guenter Roeck + +[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ] + +Sashiko reports: + +During initialization of the nct6116 chip, the driver sets data->pwm_num +to 5. However, it assigns several NCT6106 register arrays (such as +NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and +NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP. +These arrays only contain 3 elements. + +In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If +data->has_pwm has bits 3 or 4 set (which is structurally possible for +nct6116), the loop attempts to read elements at index 3 and 4 from these +3-element arrays. This results in a global out-of-bounds read, which can +be caught by KASAN. + +Furthermore, the driver uses these garbage out-of-bounds values as +hardware register addresses for subsequent read and write operations. This +leads to invalid hardware register access, potentially causing hardware +misconfiguration or system crashes. + +The underlying problem is that the chip does support up to five fan +control channels, but only the first three support weight control. +Fix the problem by extending the affected weight register arrays with +zeroed fields. The driver uses zeroed register addresses to determine +if a register is supported or not, and skips accesses for unsupported +registers. + +Reported-by: Sashiko +Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") +Cc: Björn Gerhart +Cc: Florian Bezdeka +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index 887102e51067a..7c62cc35c5a17 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -790,12 +790,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 }; + + static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 }; + +-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 }; + + static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 }; + static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 }; +-- +2.53.0 + diff --git a/queue-6.1/hwmon-nct6775-fix-access-to-temperature-configuratio.patch b/queue-6.1/hwmon-nct6775-fix-access-to-temperature-configuratio.patch new file mode 100644 index 0000000000..724a513c1d --- /dev/null +++ b/queue-6.1/hwmon-nct6775-fix-access-to-temperature-configuratio.patch @@ -0,0 +1,118 @@ +From 538c44adbf70f8b571b8c8c4cd8b76ec6224931d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 21 Feb 2024 06:01:20 -0800 +Subject: hwmon: (nct6775) Fix access to temperature configuration registers + +From: Guenter Roeck + +[ Upstream commit d56e460e19ea8382f813eb489730248ec8d7eb73 ] + +The number of temperature configuration registers does +not always match the total number of temperature registers. +This can result in access errors reported if KASAN is enabled. + +BUG: KASAN: global-out-of-bounds in nct6775_probe+0x5654/0x6fe9 nct6775_core + +Reported-by: Erhard Furtner +Closes: https://lore.kernel.org/linux-hwmon/d51181d1-d26b-42b2-b002-3f5a4037721f@roeck-us.net/ +Fixes: b7f1f7b2523a ("hwmon: (nct6775) Additional TEMP registers for nct6799") +Cc: Ahmad Khalifa +Tested-by: Ahmad Khalifa +Signed-off-by: Guenter Roeck +Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116") +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index acb0fbb0eabca..c7b3961e189ce 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -3506,6 +3506,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + const u16 *reg_temp_mon, *reg_temp_alternate, *reg_temp_crit; + const u16 *reg_temp_crit_l = NULL, *reg_temp_crit_h = NULL; + int num_reg_temp, num_reg_temp_mon, num_reg_tsi_temp; ++ int num_reg_temp_config; + struct device *hwmon_dev; + struct sensor_template_group tsi_temp_tg; + +@@ -3588,6 +3589,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + reg_temp_over = NCT6106_REG_TEMP_OVER; + reg_temp_hyst = NCT6106_REG_TEMP_HYST; + reg_temp_config = NCT6106_REG_TEMP_CONFIG; ++ num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG); + reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6106_REG_TEMP_CRIT; + reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; +@@ -3663,6 +3665,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + reg_temp_over = NCT6106_REG_TEMP_OVER; + reg_temp_hyst = NCT6106_REG_TEMP_HYST; + reg_temp_config = NCT6106_REG_TEMP_CONFIG; ++ num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG); + reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6106_REG_TEMP_CRIT; + reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; +@@ -3740,6 +3743,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + reg_temp_over = NCT6775_REG_TEMP_OVER; + reg_temp_hyst = NCT6775_REG_TEMP_HYST; + reg_temp_config = NCT6775_REG_TEMP_CONFIG; ++ num_reg_temp_config = ARRAY_SIZE(NCT6775_REG_TEMP_CONFIG); + reg_temp_alternate = NCT6775_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6775_REG_TEMP_CRIT; + +@@ -3815,6 +3819,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + reg_temp_over = NCT6775_REG_TEMP_OVER; + reg_temp_hyst = NCT6775_REG_TEMP_HYST; + reg_temp_config = NCT6776_REG_TEMP_CONFIG; ++ num_reg_temp_config = ARRAY_SIZE(NCT6776_REG_TEMP_CONFIG); + reg_temp_alternate = NCT6776_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6776_REG_TEMP_CRIT; + +@@ -3894,6 +3899,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + reg_temp_over = NCT6779_REG_TEMP_OVER; + reg_temp_hyst = NCT6779_REG_TEMP_HYST; + reg_temp_config = NCT6779_REG_TEMP_CONFIG; ++ num_reg_temp_config = ARRAY_SIZE(NCT6779_REG_TEMP_CONFIG); + reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6779_REG_TEMP_CRIT; + +@@ -4028,6 +4034,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + reg_temp_over = NCT6779_REG_TEMP_OVER; + reg_temp_hyst = NCT6779_REG_TEMP_HYST; + reg_temp_config = NCT6779_REG_TEMP_CONFIG; ++ num_reg_temp_config = ARRAY_SIZE(NCT6779_REG_TEMP_CONFIG); + reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6779_REG_TEMP_CRIT; + +@@ -4117,6 +4124,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + reg_temp_over = NCT6798_REG_TEMP_OVER; + reg_temp_hyst = NCT6798_REG_TEMP_HYST; + reg_temp_config = NCT6779_REG_TEMP_CONFIG; ++ num_reg_temp_config = ARRAY_SIZE(NCT6779_REG_TEMP_CONFIG); + reg_temp_alternate = NCT6798_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6798_REG_TEMP_CRIT; + +@@ -4198,7 +4206,8 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + = reg_temp_crit[src - 1]; + if (reg_temp_crit_l && reg_temp_crit_l[i]) + data->reg_temp[4][src - 1] = reg_temp_crit_l[i]; +- data->reg_temp_config[src - 1] = reg_temp_config[i]; ++ if (i < num_reg_temp_config) ++ data->reg_temp_config[src - 1] = reg_temp_config[i]; + data->temp_src[src - 1] = src; + continue; + } +@@ -4211,7 +4220,8 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + data->reg_temp[0][s] = reg_temp[i]; + data->reg_temp[1][s] = reg_temp_over[i]; + data->reg_temp[2][s] = reg_temp_hyst[i]; +- data->reg_temp_config[s] = reg_temp_config[i]; ++ if (i < num_reg_temp_config) ++ data->reg_temp_config[s] = reg_temp_config[i]; + if (reg_temp_crit_h && reg_temp_crit_h[i]) + data->reg_temp[3][s] = reg_temp_crit_h[i]; + else if (reg_temp_crit[src - 1]) +-- +2.53.0 + diff --git a/queue-6.1/hwmon-nct6775-fix-in-scaling-factors-for-6798-6799.patch b/queue-6.1/hwmon-nct6775-fix-in-scaling-factors-for-6798-6799.patch new file mode 100644 index 0000000000..d1006a71bd --- /dev/null +++ b/queue-6.1/hwmon-nct6775-fix-in-scaling-factors-for-6798-6799.patch @@ -0,0 +1,112 @@ +From 7169928c943edac27d9136d40b0cb86330f202a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 19 Jul 2023 20:28:48 +0100 +Subject: hwmon: (nct6775) Fix IN scaling factors for 6798/6799 + +From: Ahmad Khalifa + +[ Upstream commit 13558a2e6341d1ba6dff9f8e2febf97877067885 ] + +Scaling for VTT/VIN5/VIN6 registers were based on prior chips +* Split scaling factors for 6798/6799 and assign at probe() +* Pass them through driver data to sysfs functions + +Tested on nct6799 with old/new input/min/max + +Fixes: 0599682b826f ("hwmon: (nct6775) Add support for NCT6798D") +Signed-off-by: Ahmad Khalifa +Link: https://lore.kernel.org/r/20230719192848.337508-1-ahmad@khalifa.ws +Signed-off-by: Guenter Roeck +Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116") +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 28 ++++++++++++++++++++++------ + drivers/hwmon/nct6775.h | 1 + + 2 files changed, 23 insertions(+), 6 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index 3a61acec7a345..a7e5faf8d0278 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -955,14 +955,25 @@ static const u16 scale_in[15] = { + 800, 800 + }; + +-static inline long in_from_reg(u8 reg, u8 nr) ++/* ++ * NCT6798 scaling: ++ * CPUVC, IN1, AVSB, 3VCC, IN0, IN8, IN4, 3VSB, VBAT, VTT, IN5, IN6, IN2, ++ * IN3, IN7 ++ * Additional scales to be added later: IN9 (800), VHIF (1600) ++ */ ++static const u16 scale_in_6798[15] = { ++ 800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 1600, 1600, 1600, 800, ++ 800, 800 ++}; ++ ++static inline long in_from_reg(u8 reg, u8 nr, const u16 *scales) + { +- return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100); ++ return DIV_ROUND_CLOSEST(reg * scales[nr], 100); + } + +-static inline u8 in_to_reg(u32 val, u8 nr) ++static inline u8 in_to_reg(u32 val, u8 nr, const u16 *scales) + { +- return clamp_val(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0, 255); ++ return clamp_val(DIV_ROUND_CLOSEST(val * 100, scales[nr]), 0, 255); + } + + /* TSI temperatures are in 8.3 format */ +@@ -1677,7 +1688,8 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf) + if (IS_ERR(data)) + return PTR_ERR(data); + +- return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr)); ++ return sprintf(buf, "%ld\n", ++ in_from_reg(data->in[nr][index], nr, data->scale_in)); + } + + static ssize_t +@@ -1695,7 +1707,7 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf, + if (err < 0) + return err; + mutex_lock(&data->update_lock); +- data->in[nr][index] = in_to_reg(val, nr); ++ data->in[nr][index] = in_to_reg(val, nr, data->scale_in); + err = nct6775_write_value(data, data->REG_IN_MINMAX[index - 1][nr], data->in[nr][index]); + mutex_unlock(&data->update_lock); + return err ? : count; +@@ -3472,6 +3484,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + mutex_init(&data->update_lock); + data->name = nct6775_device_names[data->kind]; + data->bank = 0xff; /* Force initial bank selection */ ++ data->scale_in = scale_in; + + switch (data->kind) { + case nct6106: +@@ -3987,6 +4000,9 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + break; + } + ++ if (data->kind == nct6798 || data->kind == nct6799) ++ data->scale_in = scale_in_6798; ++ + reg_temp = NCT6779_REG_TEMP; + num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); + if (data->kind == nct6791) { +diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h +index 44f79c5726a9c..a84c6ce7275de 100644 +--- a/drivers/hwmon/nct6775.h ++++ b/drivers/hwmon/nct6775.h +@@ -98,6 +98,7 @@ struct nct6775_data { + u8 bank; /* current register bank */ + u8 in_num; /* number of in inputs we have */ + u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */ ++ const u16 *scale_in; /* internal scaling factors */ + unsigned int rpm[NUM_FAN]; + u16 fan_min[NUM_FAN]; + u8 fan_pulses[NUM_FAN]; +-- +2.53.0 + diff --git a/queue-6.1/hwmon-nct6775-increase-and-reorder-alarm-beep-bits.patch b/queue-6.1/hwmon-nct6775-increase-and-reorder-alarm-beep-bits.patch new file mode 100644 index 0000000000..5131f0852e --- /dev/null +++ b/queue-6.1/hwmon-nct6775-increase-and-reorder-alarm-beep-bits.patch @@ -0,0 +1,302 @@ +From afdc8c886cda6204472a14b543f8e0016f6253fa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 17 Jul 2023 21:10:51 +0100 +Subject: hwmon: (nct6775) Increase and reorder ALARM/BEEP bits + +From: Ahmad Khalifa + +[ Upstream commit 3b7f4bde06daaff391a374fc27c8163b2847de34 ] + +* Increase available bits, IN: 16 to 24, FAN: 8 to 12, + TEMP: 6 to 12 +* Reorder alarm/beep definitions to match in order to allow + additional inputs in the future +* Remove comments about 'unused' bits as probe() is a better + reference + +Testing note: +* Tested on nct6799 with IN/FAN/TEMP, and changing min/max/high/hyst, + that triggers the corresponding alarms correctly. Good confirmation + on the original mapping of the registers and masks. + As to be expected, only 4 fans and 2 temps (fixed) have limits + currently on nct6799 on my board. +* Trouble with testing intrusion alarms and beeps, no way to confirm + those. As I understand now, intrusion/caseopen is probably not + connected on my board. + And I haven't seen a buzzer on a board in ages. + +Signed-off-by: Ahmad Khalifa +Link: https://lore.kernel.org/r/20230717201050.1657809-1-ahmad@khalifa.ws +Signed-off-by: Guenter Roeck +Stable-dep-of: b0e8adb2ccb4 ("hwmon: (nct6775-core) Fix number of temperature registers for NCT6116") +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 169 +++++++++++++++-------------------- + drivers/hwmon/nct6775.h | 23 ++++- + 2 files changed, 93 insertions(+), 99 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index a7e5faf8d0278..34b13194c91c5 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -97,31 +97,23 @@ static const u16 NCT6775_REG_IN[] = { + + static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B }; + +-/* 0..15 voltages, 16..23 fans, 24..29 temperatures, 30..31 intrusion */ +- +-static const s8 NCT6775_ALARM_BITS[] = { +- 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ +- 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ +- -1, /* unused */ +- 6, 7, 11, -1, -1, /* fan1..fan5 */ +- -1, -1, -1, /* unused */ +- 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ +- 12, -1 }; /* intrusion0, intrusion1 */ ++static const s8 NCT6775_ALARM_BITS[NUM_ALARM_BITS] = { ++ 0, 1, 2, 3, 8, 21, 20, 16, 17, -1, -1, -1, /* in0-in11 */ ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */ ++ 6, 7, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ ++ 4, 5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 12, -1, /* intr0-intr1 */ ++}; + + static const u16 NCT6775_REG_BEEP[NUM_REG_BEEP] = { 0x56, 0x57, 0x453, 0x4e }; + +-/* +- * 0..14 voltages, 15 global beep enable, 16..23 fans, 24..29 temperatures, +- * 30..31 intrusion +- */ +-static const s8 NCT6775_BEEP_BITS[] = { +- 0, 1, 2, 3, 8, 9, 10, 16, /* in0.. in7 */ +- 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ +- 21, /* global beep enable */ +- 6, 7, 11, 28, -1, /* fan1..fan5 */ +- -1, -1, -1, /* unused */ +- 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ +- 12, -1 }; /* intrusion0, intrusion1 */ ++static const s8 NCT6775_BEEP_BITS[NUM_BEEP_BITS] = { ++ 0, 1, 2, 3, 8, 9, 10, 16, 17, -1, -1, -1, /* in0-in11 */ ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */ ++ 6, 7, 11, 28, -1, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ ++ 4, 5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 12, -1, 21 /* intr0-intr1, beep_en */ ++}; + + /* DC or PWM output fan configuration */ + static const u8 NCT6775_REG_PWM_MODE[] = { 0x04, 0x04, 0x12 }; +@@ -255,25 +247,23 @@ static const u16 NCT6775_REG_TSI_TEMP[] = { 0x669 }; + #define NCT6776_REG_FAN_STEP_UP_TIME NCT6775_REG_FAN_STEP_DOWN_TIME + #define NCT6776_REG_FAN_STEP_DOWN_TIME NCT6775_REG_FAN_STEP_UP_TIME + +-static const s8 NCT6776_ALARM_BITS[] = { +- 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ +- 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ +- -1, /* unused */ +- 6, 7, 11, 10, 23, /* fan1..fan5 */ +- -1, -1, -1, /* unused */ +- 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ +- 12, 9 }; /* intrusion0, intrusion1 */ ++static const s8 NCT6776_ALARM_BITS[NUM_ALARM_BITS] = { ++ 0, 1, 2, 3, 8, 21, 20, 16, 17, -1, -1, -1, /* in0-in11 */ ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */ ++ 6, 7, 11, 10, 23, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ ++ 4, 5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 12, 9, /* intr0-intr1 */ ++}; + + static const u16 NCT6776_REG_BEEP[NUM_REG_BEEP] = { 0xb2, 0xb3, 0xb4, 0xb5 }; + +-static const s8 NCT6776_BEEP_BITS[] = { +- 0, 1, 2, 3, 4, 5, 6, 7, /* in0.. in7 */ +- 8, -1, -1, -1, -1, -1, -1, /* in8..in14 */ +- 24, /* global beep enable */ +- 25, 26, 27, 28, 29, /* fan1..fan5 */ +- -1, -1, -1, /* unused */ +- 16, 17, 18, 19, 20, 21, /* temp1..temp6 */ +- 30, 31 }; /* intrusion0, intrusion1 */ ++static const s8 NCT6776_BEEP_BITS[NUM_BEEP_BITS] = { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, /* in0-in11 */ ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */ ++ 25, 26, 27, 28, 29, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ ++ 16, 17, 18, 19, 20, 21, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 30, 31, 24 /* intr0-intr1, beep_en */ ++}; + + static const u16 NCT6776_REG_TOLERANCE_H[] = { + 0x10c, 0x20c, 0x30c, 0x80c, 0x90c, 0xa0c, 0xb0c }; +@@ -344,23 +334,21 @@ static const u16 NCT6779_REG_IN[] = { + static const u16 NCT6779_REG_ALARM[NUM_REG_ALARM] = { + 0x459, 0x45A, 0x45B, 0x568 }; + +-static const s8 NCT6779_ALARM_BITS[] = { +- 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ +- 17, 24, 25, 26, 27, 28, 29, /* in8..in14 */ +- -1, /* unused */ +- 6, 7, 11, 10, 23, /* fan1..fan5 */ +- -1, -1, -1, /* unused */ +- 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ +- 12, 9 }; /* intrusion0, intrusion1 */ +- +-static const s8 NCT6779_BEEP_BITS[] = { +- 0, 1, 2, 3, 4, 5, 6, 7, /* in0.. in7 */ +- 8, 9, 10, 11, 12, 13, 14, /* in8..in14 */ +- 24, /* global beep enable */ +- 25, 26, 27, 28, 29, /* fan1..fan5 */ +- -1, -1, -1, /* unused */ +- 16, 17, -1, -1, -1, -1, /* temp1..temp6 */ +- 30, 31 }; /* intrusion0, intrusion1 */ ++static const s8 NCT6779_ALARM_BITS[NUM_ALARM_BITS] = { ++ 0, 1, 2, 3, 8, 21, 20, 16, 17, 24, 25, 26, /* in0-in11 */ ++ 27, 28, 29, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */ ++ 6, 7, 11, 10, 23, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ ++ 4, 5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 12, 9, /* intr0-intr1 */ ++}; ++ ++static const s8 NCT6779_BEEP_BITS[NUM_BEEP_BITS] = { ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, /* in0-in11 */ ++ 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */ ++ 25, 26, 27, 28, 29, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ ++ 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 30, 31, 24 /* intr0-intr1, beep_en */ ++}; + + static const u16 NCT6779_REG_FAN[] = { + 0x4c0, 0x4c2, 0x4c4, 0x4c6, 0x4c8, 0x4ca, 0x4ce }; +@@ -448,14 +436,13 @@ static const u16 NCT6791_REG_WEIGHT_DUTY_BASE[NUM_FAN] = { 0, 0x23e }; + static const u16 NCT6791_REG_ALARM[NUM_REG_ALARM] = { + 0x459, 0x45A, 0x45B, 0x568, 0x45D }; + +-static const s8 NCT6791_ALARM_BITS[] = { +- 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ +- 17, 24, 25, 26, 27, 28, 29, /* in8..in14 */ +- -1, /* unused */ +- 6, 7, 11, 10, 23, 33, /* fan1..fan6 */ +- -1, -1, /* unused */ +- 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ +- 12, 9 }; /* intrusion0, intrusion1 */ ++static const s8 NCT6791_ALARM_BITS[NUM_ALARM_BITS] = { ++ 0, 1, 2, 3, 8, 21, 20, 16, 17, 24, 25, 26, /* in0-in11 */ ++ 27, 28, 29, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */ ++ 6, 7, 11, 10, 23, 33, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ ++ 4, 5, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 12, 9, /* intr0-intr1 */ ++}; + + /* NCT6792/NCT6793 specific data */ + +@@ -763,27 +750,23 @@ static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 }; + static const u16 NCT6106_REG_ALARM[NUM_REG_ALARM] = { + 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d }; + +-static const s8 NCT6106_ALARM_BITS[] = { +- 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */ +- 9, -1, -1, -1, -1, -1, -1, /* in8..in14 */ +- -1, /* unused */ +- 32, 33, 34, -1, -1, /* fan1..fan5 */ +- -1, -1, -1, /* unused */ +- 16, 17, 18, 19, 20, 21, /* temp1..temp6 */ +- 48, -1 /* intrusion0, intrusion1 */ ++static const s8 NCT6106_ALARM_BITS[NUM_ALARM_BITS] = { ++ 0, 1, 2, 3, 4, 5, 7, 8, 9, -1, -1, -1, /* in0-in11 */ ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */ ++ 32, 33, 34, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ ++ 16, 17, 18, 19, 20, 21, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 48, -1, /* intr0-intr1 */ + }; + + static const u16 NCT6106_REG_BEEP[NUM_REG_BEEP] = { + 0x3c0, 0x3c1, 0x3c2, 0x3c3, 0x3c4 }; + +-static const s8 NCT6106_BEEP_BITS[] = { +- 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */ +- 9, 10, 11, 12, -1, -1, -1, /* in8..in14 */ +- 32, /* global beep enable */ +- 24, 25, 26, 27, 28, /* fan1..fan5 */ +- -1, -1, -1, /* unused */ +- 16, 17, 18, 19, 20, 21, /* temp1..temp6 */ +- 34, -1 /* intrusion0, intrusion1 */ ++static const s8 NCT6106_BEEP_BITS[NUM_BEEP_BITS] = { ++ 0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, /* in0-in11 */ ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */ ++ 24, 25, 26, 27, 28, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ ++ 16, 17, 18, 19, 20, 21, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 34, -1, 32 /* intr0-intr1, beep_en */ + }; + + static const u16 NCT6106_REG_TEMP_ALTERNATE[32] = { +@@ -843,24 +826,20 @@ static const u16 NCT6116_REG_AUTO_TEMP[] = { + static const u16 NCT6116_REG_AUTO_PWM[] = { + 0x164, 0x174, 0x184, 0x1d4, 0x1e4 }; + +-static const s8 NCT6116_ALARM_BITS[] = { +- 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */ +- 9, -1, -1, -1, -1, -1, -1, /* in8..in9 */ +- -1, /* unused */ +- 32, 33, 34, 35, 36, /* fan1..fan5 */ +- -1, -1, -1, /* unused */ +- 16, 17, 18, -1, -1, -1, /* temp1..temp6 */ +- 48, -1 /* intrusion0, intrusion1 */ ++static const s8 NCT6116_ALARM_BITS[NUM_ALARM_BITS] = { ++ 0, 1, 2, 3, 4, 5, 7, 8, 9, -1, -1, -1, /* in0-in11 */ ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */ ++ 32, 33, 34, 35, 36, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ ++ 16, 17, 18, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 48, -1, /* intr0-intr1 */ + }; + +-static const s8 NCT6116_BEEP_BITS[] = { +- 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */ +- 9, 10, 11, 12, -1, -1, -1, /* in8..in14 */ +- 32, /* global beep enable */ +- 24, 25, 26, 27, 28, /* fan1..fan5 */ +- -1, -1, -1, /* unused */ +- 16, 17, 18, -1, -1, -1, /* temp1..temp6 */ +- 34, -1 /* intrusion0, intrusion1 */ ++static const s8 NCT6116_BEEP_BITS[NUM_BEEP_BITS] = { ++ 0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, /* in0-in11 */ ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */ ++ 24, 25, 26, 27, 28, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */ ++ 16, 17, 18, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */ ++ 34, -1, 32 /* intr0-intr1, beep_en */ + }; + + static const u16 NCT6116_REG_TSI_TEMP[] = { 0x59, 0x5b }; +diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h +index a84c6ce7275de..c752bc7bbe722 100644 +--- a/drivers/hwmon/nct6775.h ++++ b/drivers/hwmon/nct6775.h +@@ -239,10 +239,25 @@ nct6775_add_attr_group(struct nct6775_data *data, const struct attribute_group * + + #define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE 0x28 + +-#define FAN_ALARM_BASE 16 +-#define TEMP_ALARM_BASE 24 +-#define INTRUSION_ALARM_BASE 30 +-#define BEEP_ENABLE_BASE 15 ++/* ++ * ALARM_BITS and BEEP_BITS store bit-index for the mask of the registers ++ * loaded into data->alarm and data->beep. ++ * ++ * Every input register (IN/TEMP/FAN) must have a corresponding ++ * ALARM/BEEP bit at the same index BITS[BASE + index] ++ * Set value to -1 to disable the visibility of that '*_alarm' attribute and ++ * to pad the bits until the next BASE ++ * ++ * Beep has an additional GLOBAL_BEEP_ENABLE bit ++ */ ++#define VIN_ALARM_BASE 0 ++#define FAN_ALARM_BASE 24 ++#define TEMP_ALARM_BASE 36 ++#define INTRUSION_ALARM_BASE 48 ++#define BEEP_ENABLE_BASE 50 ++ ++#define NUM_ALARM_BITS (INTRUSION_ALARM_BASE + 4) ++#define NUM_BEEP_BITS (BEEP_ENABLE_BASE + 1) + + /* + * Not currently used: +-- +2.53.0 + diff --git a/queue-6.1/hwmon-nzxt-smart2-dma-align-output-buffer.patch b/queue-6.1/hwmon-nzxt-smart2-dma-align-output-buffer.patch new file mode 100644 index 0000000000..718e93b6db --- /dev/null +++ b/queue-6.1/hwmon-nzxt-smart2-dma-align-output-buffer.patch @@ -0,0 +1,53 @@ +From de48d3a067c9c1a4878fbc5eb19bdbad879b177d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 09:54:23 -0700 +Subject: hwmon: (nzxt-smart2) DMA-align output buffer + +From: Guenter Roeck + +[ Upstream commit 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 ] + +Sashiko reports: + +When send_output_report() calls hid_hw_output_report(), the underlying USB +HID core calls usb_interrupt_msg() which maps this buffer directly for DMA. + +When the DMA mapping flushes or invalidates the cacheline, it will corrupt +the adjacent variables (mutex, update_interval) that were modified +concurrently by the CPU. This causes memory corruption due to cacheline +sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA +API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings +for this violation. + +Any operation that triggers send_output_report() (like setting a fan speed +or updating the interval) causes the USB DMA mapping. On systems with +non-coherent caches, this structural bug causes immediate and deterministic +memory corruption. + +Align the output buffer to ARCH_DMA_MINALIGN to fix the problem. + +Reported-by: Sashiko +Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.") +Cc: Aleksandr Mezin +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nzxt-smart2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c +index 02816538d18eb..90df6a7fc88d7 100644 +--- a/drivers/hwmon/nzxt-smart2.c ++++ b/drivers/hwmon/nzxt-smart2.c +@@ -203,7 +203,7 @@ struct drvdata { + */ + struct mutex mutex; + long update_interval; +- u8 output_buffer[OUTPUT_REPORT_SIZE]; ++ u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN); + }; + + static long scale_pwm_value(long val, long orig_max, long new_max) +-- +2.53.0 + diff --git a/queue-6.1/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-6.1/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch new file mode 100644 index 0000000000..71ed0b8316 --- /dev/null +++ b/queue-6.1/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch @@ -0,0 +1,39 @@ +From d60fb056a397aa0b45a4adec4fef9348dd17649e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 08:41:40 -0700 +Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data() + +From: Guenter Roeck + +[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ] + +pmbus_update_byte_data() is supposed to return a negative error code or 0. +However, if no change is made to the register, it actually returns the +register value. This can result in problems if the calling code explicitly +expects to see an error code or 0. + +Fix it to return 0 on success or the error code as expected. + +Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/pmbus/pmbus_core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c +index 1715fafc4152f..d69fd8ca66eb5 100644 +--- a/drivers/hwmon/pmbus/pmbus_core.c ++++ b/drivers/hwmon/pmbus/pmbus_core.c +@@ -442,7 +442,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + if (tmp != rv) + rv = _pmbus_write_byte_data(client, page, reg, tmp); + +- return rv; ++ return rv < 0 ? rv : 0; + } + EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS); + +-- +2.53.0 + diff --git a/queue-6.1/ipv6-introduce-dst_rt6_info-helper.patch b/queue-6.1/ipv6-introduce-dst_rt6_info-helper.patch new file mode 100644 index 0000000000..b739fecb9a --- /dev/null +++ b/queue-6.1/ipv6-introduce-dst_rt6_info-helper.patch @@ -0,0 +1,841 @@ +From e7b3f889b679a6bcfa8027fb950c463a917c0ab8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Apr 2024 15:19:52 +0000 +Subject: ipv6: introduce dst_rt6_info() helper + +From: Eric Dumazet + +[ Upstream commit e8dfd42c17faf183415323db1ef0c977be0d6489 ] + +Instead of (struct rt6_info *)dst casts, we can use : + + #define dst_rt6_info(_ptr) \ + container_of_const(_ptr, struct rt6_info, dst) + +Some places needed missing const qualifiers : + +ip6_confirm_neigh(), ipv6_anycast_destination(), +ipv6_unicast_destination(), has_gateway() + +v2: added missing parts (David Ahern) + +Signed-off-by: Eric Dumazet +Reviewed-by: David Ahern +Signed-off-by: David S. Miller +Stable-dep-of: e876b75b9020 ("ipvs: fix the checksum validations") +Signed-off-by: Sasha Levin +--- + drivers/infiniband/core/addr.c | 6 ++-- + .../ethernet/mellanox/mlxsw/spectrum_span.c | 2 +- + drivers/net/vrf.c | 2 +- + drivers/net/vxlan/vxlan_core.c | 2 +- + drivers/s390/net/qeth_core.h | 4 +-- + include/net/ip6_fib.h | 6 ++-- + include/net/ip6_route.h | 11 ++++---- + net/bluetooth/6lowpan.c | 2 +- + net/core/dst_cache.c | 2 +- + net/core/filter.c | 2 +- + net/ipv4/ip_tunnel.c | 2 +- + net/ipv6/icmp.c | 8 +++--- + net/ipv6/ila/ila_lwt.c | 4 +-- + net/ipv6/ip6_output.c | 18 ++++++------ + net/ipv6/ip6mr.c | 2 +- + net/ipv6/ndisc.c | 2 +- + net/ipv6/ping.c | 2 +- + net/ipv6/raw.c | 4 +-- + net/ipv6/route.c | 28 +++++++++---------- + net/ipv6/tcp_ipv6.c | 4 +-- + net/ipv6/udp.c | 11 +++----- + net/ipv6/xfrm6_policy.c | 2 +- + net/l2tp/l2tp_ip6.c | 2 +- + net/mpls/mpls_iptunnel.c | 2 +- + net/netfilter/ipvs/ip_vs_xmit.c | 14 +++++----- + net/netfilter/nf_flow_table_core.c | 8 ++---- + net/netfilter/nf_flow_table_ip.c | 4 +-- + net/netfilter/nft_rt.c | 2 +- + net/sctp/ipv6.c | 2 +- + net/xfrm/xfrm_policy.c | 3 +- + 30 files changed, 77 insertions(+), 86 deletions(-) + +diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c +index 3e87e92e9993e..579fd879716dc 100644 +--- a/drivers/infiniband/core/addr.c ++++ b/drivers/infiniband/core/addr.c +@@ -338,15 +338,15 @@ static int dst_fetch_ha(const struct dst_entry *dst, + + static bool has_gateway(const struct dst_entry *dst, sa_family_t family) + { +- struct rtable *rt; +- struct rt6_info *rt6; ++ const struct rtable *rt; ++ const struct rt6_info *rt6; + + if (family == AF_INET) { + rt = container_of(dst, struct rtable, dst); + return rt->rt_uses_gateway; + } + +- rt6 = container_of(dst, struct rt6_info, dst); ++ rt6 = dst_rt6_info(dst); + return rt6->rt6i_flags & RTF_GATEWAY; + } + +diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c +index 8883ef0127477..fa3fef2b74db0 100644 +--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c ++++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c +@@ -538,7 +538,7 @@ mlxsw_sp_span_gretap6_route(const struct net_device *to_dev, + if (!dst || dst->error) + goto out; + +- rt6 = container_of(dst, struct rt6_info, dst); ++ rt6 = dst_rt6_info(dst); + + dev = dst->dev; + *saddrp = fl6.saddr; +diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c +index 51b34882827e9..65668113f715e 100644 +--- a/drivers/net/vrf.c ++++ b/drivers/net/vrf.c +@@ -655,7 +655,7 @@ static int vrf_finish_output6(struct net *net, struct sock *sk, + skb->dev = dev; + + rcu_read_lock(); +- nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr); ++ nexthop = rt6_nexthop(dst_rt6_info(dst), &ipv6_hdr(skb)->daddr); + neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop); + if (unlikely(!neigh)) + neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false); +diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c +index 8e3e8a7cad1c9..5a709566e5a28 100644 +--- a/drivers/net/vxlan/vxlan_core.c ++++ b/drivers/net/vxlan/vxlan_core.c +@@ -2715,7 +2715,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, + } + + if (!info) { +- u32 rt6i_flags = ((struct rt6_info *)ndst)->rt6i_flags; ++ u32 rt6i_flags = dst_rt6_info(ndst)->rt6i_flags; + + err = encap_bypass_if_local(skb, dev, vxlan, dst, + dst_port, ifindex, vni, +diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h +index 613eab7297046..5f17a2a5d0e33 100644 +--- a/drivers/s390/net/qeth_core.h ++++ b/drivers/s390/net/qeth_core.h +@@ -956,7 +956,7 @@ static inline struct dst_entry *qeth_dst_check_rcu(struct sk_buff *skb, + struct dst_entry *dst = skb_dst(skb); + struct rt6_info *rt; + +- rt = (struct rt6_info *) dst; ++ rt = dst_rt6_info(dst); + if (dst) { + if (proto == htons(ETH_P_IPV6)) + dst = dst_check(dst, rt6_get_cookie(rt)); +@@ -978,7 +978,7 @@ static inline __be32 qeth_next_hop_v4_rcu(struct sk_buff *skb, + static inline struct in6_addr *qeth_next_hop_v6_rcu(struct sk_buff *skb, + struct dst_entry *dst) + { +- struct rt6_info *rt = (struct rt6_info *) dst; ++ struct rt6_info *rt = dst_rt6_info(dst); + + if (rt && !ipv6_addr_any(&rt->rt6i_gateway)) + return &rt->rt6i_gateway; +diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h +index fa4e6af382e2a..ef38ee0912e1a 100644 +--- a/include/net/ip6_fib.h ++++ b/include/net/ip6_fib.h +@@ -240,9 +240,11 @@ struct fib6_result { + for (rt = (w)->leaf; rt; \ + rt = rcu_dereference_protected(rt->fib6_next, 1)) + +-static inline struct inet6_dev *ip6_dst_idev(struct dst_entry *dst) ++#define dst_rt6_info(_ptr) container_of_const(_ptr, struct rt6_info, dst) ++ ++static inline struct inet6_dev *ip6_dst_idev(const struct dst_entry *dst) + { +- return ((struct rt6_info *)dst)->rt6i_idev; ++ return dst_rt6_info(dst)->rt6i_idev; + } + + static inline bool fib6_requires_src(const struct fib6_info *rt) +diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h +index 4cd0839c86c92..7c0184c09392f 100644 +--- a/include/net/ip6_route.h ++++ b/include/net/ip6_route.h +@@ -222,12 +222,11 @@ void rt6_uncached_list_del(struct rt6_info *rt); + static inline const struct rt6_info *skb_rt6_info(const struct sk_buff *skb) + { + const struct dst_entry *dst = skb_dst(skb); +- const struct rt6_info *rt6 = NULL; + + if (dst) +- rt6 = container_of(dst, struct rt6_info, dst); ++ return dst_rt6_info(dst); + +- return rt6; ++ return NULL; + } + + /* +@@ -239,7 +238,7 @@ static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst, + { + struct ipv6_pinfo *np = inet6_sk(sk); + +- np->dst_cookie = rt6_get_cookie((struct rt6_info *)dst); ++ np->dst_cookie = rt6_get_cookie(dst_rt6_info(dst)); + sk_setup_caps(sk, dst); + np->daddr_cache = daddr; + #ifdef CONFIG_IPV6_SUBTREES +@@ -252,7 +251,7 @@ void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst, + + static inline bool ipv6_unicast_destination(const struct sk_buff *skb) + { +- struct rt6_info *rt = (struct rt6_info *) skb_dst(skb); ++ const struct rt6_info *rt = dst_rt6_info(skb_dst(skb)); + + return rt->rt6i_flags & RTF_LOCAL; + } +@@ -260,7 +259,7 @@ static inline bool ipv6_unicast_destination(const struct sk_buff *skb) + static inline bool ipv6_anycast_destination(const struct dst_entry *dst, + const struct in6_addr *daddr) + { +- struct rt6_info *rt = (struct rt6_info *)dst; ++ const struct rt6_info *rt = dst_rt6_info(dst); + + return rt->rt6i_flags & RTF_ANYCAST || + (rt->rt6i_dst.plen < 127 && +diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c +index c94e59b1f7133..e2196cf4f6f70 100644 +--- a/net/bluetooth/6lowpan.c ++++ b/net/bluetooth/6lowpan.c +@@ -140,7 +140,7 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev, + struct in6_addr *daddr, + struct sk_buff *skb) + { +- struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); ++ struct rt6_info *rt = dst_rt6_info(skb_dst(skb)); + int count = atomic_read(&dev->peer_count); + const struct in6_addr *nexthop; + struct lowpan_peer *peer; +diff --git a/net/core/dst_cache.c b/net/core/dst_cache.c +index 0ccfd5fa5cb9b..b17171345d649 100644 +--- a/net/core/dst_cache.c ++++ b/net/core/dst_cache.c +@@ -112,7 +112,7 @@ void dst_cache_set_ip6(struct dst_cache *dst_cache, struct dst_entry *dst, + + idst = this_cpu_ptr(dst_cache->cache); + dst_cache_per_cpu_dst_set(this_cpu_ptr(dst_cache->cache), dst, +- rt6_get_cookie((struct rt6_info *)dst)); ++ rt6_get_cookie(dst_rt6_info(dst))); + idst->in6_saddr = *saddr; + } + EXPORT_SYMBOL_GPL(dst_cache_set_ip6); +diff --git a/net/core/filter.c b/net/core/filter.c +index ce9f079d46e3b..91d337252fc90 100644 +--- a/net/core/filter.c ++++ b/net/core/filter.c +@@ -2226,7 +2226,7 @@ static int bpf_out_neigh_v6(struct net *net, struct sk_buff *skb, + rcu_read_lock(); + if (!nh) { + dst = skb_dst(skb); +- nexthop = rt6_nexthop(container_of(dst, struct rt6_info, dst), ++ nexthop = rt6_nexthop(dst_rt6_info(dst), + &ipv6_hdr(skb)->daddr); + } else { + nexthop = &nh->ipv6_nh; +diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c +index dcf9e9c52a22a..5dfb007f7792d 100644 +--- a/net/ipv4/ip_tunnel.c ++++ b/net/ipv4/ip_tunnel.c +@@ -544,7 +544,7 @@ static int tnl_update_pmtu(struct net_device *dev, struct sk_buff *skb, + struct rt6_info *rt6; + __be32 daddr; + +- rt6 = skb_valid_dst(skb) ? (struct rt6_info *)skb_dst(skb) : ++ rt6 = skb_valid_dst(skb) ? dst_rt6_info(skb_dst(skb)) : + NULL; + daddr = md ? dst : tunnel->parms.iph.daddr; + +diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c +index 877cb5e8ded7b..f8f7c1246f43b 100644 +--- a/net/ipv6/icmp.c ++++ b/net/ipv6/icmp.c +@@ -214,7 +214,7 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type, + } else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) { + res = true; + } else { +- struct rt6_info *rt = (struct rt6_info *)dst; ++ struct rt6_info *rt = dst_rt6_info(dst); + int tmo = net->ipv6.sysctl.icmpv6_time; + struct inet_peer *peer; + +@@ -245,7 +245,7 @@ static bool icmpv6_rt_has_prefsrc(struct sock *sk, u8 type, + + dst = ip6_route_output(net, sk, fl6); + if (!dst->error) { +- struct rt6_info *rt = (struct rt6_info *)dst; ++ struct rt6_info *rt = dst_rt6_info(dst); + struct in6_addr prefsrc; + + rt6_get_prefsrc(rt, &prefsrc); +@@ -624,7 +624,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, + if (ip6_append_data(sk, icmpv6_getfrag, &msg, + len + sizeof(struct icmp6hdr), + sizeof(struct icmp6hdr), +- &ipc6, &fl6, (struct rt6_info *)dst, ++ &ipc6, &fl6, dst_rt6_info(dst), + MSG_DONTWAIT)) { + ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS); + ip6_flush_pending_frames(sk); +@@ -817,7 +817,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) + if (ip6_append_data(sk, icmpv6_getfrag, &msg, + skb->len + sizeof(struct icmp6hdr), + sizeof(struct icmp6hdr), &ipc6, &fl6, +- (struct rt6_info *)dst, MSG_DONTWAIT)) { ++ dst_rt6_info(dst), MSG_DONTWAIT)) { + __ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS); + ip6_flush_pending_frames(sk); + } else { +diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c +index 7397f764c66cc..7d574f5132e2f 100644 +--- a/net/ipv6/ila/ila_lwt.c ++++ b/net/ipv6/ila/ila_lwt.c +@@ -38,7 +38,7 @@ static inline struct ila_params *ila_params_lwtunnel( + static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) + { + struct dst_entry *orig_dst = skb_dst(skb); +- struct rt6_info *rt = (struct rt6_info *)orig_dst; ++ struct rt6_info *rt = dst_rt6_info(orig_dst); + struct ila_lwt *ilwt = ila_lwt_lwtunnel(orig_dst->lwtstate); + struct dst_entry *dst; + int err = -EINVAL; +@@ -72,7 +72,7 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) + memset(&fl6, 0, sizeof(fl6)); + fl6.flowi6_oif = orig_dst->dev->ifindex; + fl6.flowi6_iif = LOOPBACK_IFINDEX; +- fl6.daddr = *rt6_nexthop((struct rt6_info *)orig_dst, ++ fl6.daddr = *rt6_nexthop(dst_rt6_info(orig_dst), + &ip6h->daddr); + + dst = ip6_route_output(net, NULL, &fl6); +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index d8ce708fcb3c4..ad821d362656f 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -121,7 +121,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff * + } + + rcu_read_lock(); +- nexthop = rt6_nexthop((struct rt6_info *)dst, daddr); ++ nexthop = rt6_nexthop(dst_rt6_info(dst), daddr); + neigh = __ipv6_neigh_lookup_noref(dev, nexthop); + + if (unlikely(IS_ERR_OR_NULL(neigh))) { +@@ -611,7 +611,7 @@ int ip6_forward(struct sk_buff *skb) + * send a redirect. + */ + +- rt = (struct rt6_info *) dst; ++ rt = dst_rt6_info(dst); + if (rt->rt6i_flags & RTF_GATEWAY) + target = &rt->rt6i_gateway; + else +@@ -866,7 +866,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, + int (*output)(struct net *, struct sock *, struct sk_buff *)) + { + struct sk_buff *frag; +- struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); ++ struct rt6_info *rt = dst_rt6_info(skb_dst(skb)); + struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ? + inet6_sk(skb->sk) : NULL; + bool mono_delivery_time = skb->mono_delivery_time; +@@ -1074,7 +1074,7 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk, + return NULL; + } + +- rt = (struct rt6_info *)dst; ++ rt = dst_rt6_info(dst); + /* Yes, checking route validity in not connected + * case is not very simple. Take into account, + * that we do not support routing by source, TOS, +@@ -1129,7 +1129,7 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk, + struct rt6_info *rt; + + *dst = ip6_route_output(net, sk, fl6); +- rt = (*dst)->error ? NULL : (struct rt6_info *)*dst; ++ rt = (*dst)->error ? NULL : dst_rt6_info(*dst); + + rcu_read_lock(); + from = rt ? rcu_dereference(rt->from) : NULL; +@@ -1171,7 +1171,7 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk, + * dst entry and replace it instead with the + * dst entry of the nexthop router + */ +- rt = (struct rt6_info *) *dst; ++ rt = dst_rt6_info(*dst); + rcu_read_lock(); + n = __ipv6_neigh_lookup_noref(rt->dst.dev, + rt6_nexthop(rt, &fl6->daddr)); +@@ -1437,7 +1437,7 @@ static int __ip6_append_data(struct sock *sk, + int offset = 0; + bool zc = false; + u32 tskey = 0; +- struct rt6_info *rt = (struct rt6_info *)cork->dst; ++ struct rt6_info *rt = dst_rt6_info(cork->dst); + bool paged, hold_tskey, extra_uref = false; + struct ipv6_txoptions *opt = v6_cork->opt; + int csummode = CHECKSUM_NONE; +@@ -1869,7 +1869,7 @@ struct sk_buff *__ip6_make_skb(struct sock *sk, + struct net *net = sock_net(sk); + struct ipv6hdr *hdr; + struct ipv6_txoptions *opt = v6_cork->opt; +- struct rt6_info *rt = (struct rt6_info *)cork->base.dst; ++ struct rt6_info *rt = dst_rt6_info(cork->base.dst); + struct flowi6 *fl6 = &cork->fl.u.ip6; + unsigned char proto = fl6->flowi6_proto; + +@@ -1941,7 +1941,7 @@ struct sk_buff *__ip6_make_skb(struct sock *sk, + int ip6_send_skb(struct sk_buff *skb) + { + struct net *net = sock_net(skb->sk); +- struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); ++ struct rt6_info *rt = dst_rt6_info(skb_dst(skb)); + int err; + + rcu_read_lock(); +diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c +index 06f66531628fe..00774cfa011af 100644 +--- a/net/ipv6/ip6mr.c ++++ b/net/ipv6/ip6mr.c +@@ -2301,7 +2301,7 @@ int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm, + int err; + struct mr_table *mrt; + struct mfc6_cache *cache; +- struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); ++ struct rt6_info *rt = dst_rt6_info(skb_dst(skb)); + + rcu_read_lock(); + mrt = __ip6mr_get_table(net, RT6_TABLE_DFLT); +diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c +index 85f7798d3e55c..a53d2a6a99f85 100644 +--- a/net/ipv6/ndisc.c ++++ b/net/ipv6/ndisc.c +@@ -1717,7 +1717,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) + if (IS_ERR(dst)) + return; + +- rt = (struct rt6_info *) dst; ++ rt = dst_rt6_info(dst); + + if (rt->rt6i_flags & RTF_GATEWAY) { + ND_PRINTK(2, warn, +diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c +index a5d7d1915ba7e..c9550fddc6b3d 100644 +--- a/net/ipv6/ping.c ++++ b/net/ipv6/ping.c +@@ -154,7 +154,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) + dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, false); + if (IS_ERR(dst)) + return PTR_ERR(dst); +- rt = (struct rt6_info *) dst; ++ rt = dst_rt6_info(dst); + + if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) + fl6.flowi6_oif = np->mcast_oif; +diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c +index f6f18196ed6e4..7b17f1b2a6128 100644 +--- a/net/ipv6/raw.c ++++ b/net/ipv6/raw.c +@@ -591,7 +591,7 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, + struct ipv6hdr *iph; + struct sk_buff *skb; + int err; +- struct rt6_info *rt = (struct rt6_info *)*dstp; ++ struct rt6_info *rt = dst_rt6_info(*dstp); + int hlen = LL_RESERVED_SPACE(rt->dst.dev); + int tlen = rt->dst.dev->needed_tailroom; + +@@ -915,7 +915,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) + ipc6.opt = opt; + lock_sock(sk); + err = ip6_append_data(sk, raw6_getfrag, &rfv, +- len, 0, &ipc6, &fl6, (struct rt6_info *)dst, ++ len, 0, &ipc6, &fl6, dst_rt6_info(dst), + msg->msg_flags); + + if (err) +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index f047000e2c55c..ea703c569d4b4 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -227,7 +227,7 @@ static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst, + struct sk_buff *skb, + const void *daddr) + { +- const struct rt6_info *rt = container_of(dst, struct rt6_info, dst); ++ const struct rt6_info *rt = dst_rt6_info(dst); + + return ip6_neigh_lookup(rt6_nexthop(rt, &in6addr_any), + dst->dev, skb, daddr); +@@ -235,8 +235,8 @@ static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst, + + static void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr) + { ++ const struct rt6_info *rt = dst_rt6_info(dst); + struct net_device *dev = dst->dev; +- struct rt6_info *rt = (struct rt6_info *)dst; + + daddr = choose_neigh_daddr(rt6_nexthop(rt, &in6addr_any), NULL, daddr); + if (!daddr) +@@ -356,7 +356,7 @@ EXPORT_SYMBOL(ip6_dst_alloc); + + static void ip6_dst_destroy(struct dst_entry *dst) + { +- struct rt6_info *rt = (struct rt6_info *)dst; ++ struct rt6_info *rt = dst_rt6_info(dst); + struct fib6_info *from; + struct inet6_dev *idev; + +@@ -376,7 +376,7 @@ static void ip6_dst_destroy(struct dst_entry *dst) + static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, + int how) + { +- struct rt6_info *rt = (struct rt6_info *)dst; ++ struct rt6_info *rt = dst_rt6_info(dst); + struct inet6_dev *idev = rt->rt6i_idev; + struct fib6_info *from; + +@@ -1324,7 +1324,7 @@ struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, + + dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup); + if (dst->error == 0) +- return (struct rt6_info *) dst; ++ return dst_rt6_info(dst); + + dst_release(dst); + +@@ -2681,7 +2681,7 @@ struct dst_entry *ip6_route_output_flags(struct net *net, + + rcu_read_lock(); + dst = ip6_route_output_flags_noref(net, sk, fl6, flags); +- rt6 = (struct rt6_info *)dst; ++ rt6 = dst_rt6_info(dst); + /* For dst cached in uncached_list, refcnt is already taken. */ + if (list_empty(&rt6->rt6i_uncached) && !dst_hold_safe(dst)) { + dst = &net->ipv6.ip6_null_entry->dst; +@@ -2695,7 +2695,7 @@ EXPORT_SYMBOL_GPL(ip6_route_output_flags); + + struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) + { +- struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; ++ struct rt6_info *rt, *ort = dst_rt6_info(dst_orig); + struct net_device *loopback_dev = net->loopback_dev; + struct dst_entry *new = NULL; + +@@ -2778,7 +2778,7 @@ INDIRECT_CALLABLE_SCOPE struct dst_entry *ip6_dst_check(struct dst_entry *dst, + struct fib6_info *from; + struct rt6_info *rt; + +- rt = container_of(dst, struct rt6_info, dst); ++ rt = dst_rt6_info(dst); + + if (rt->sernum) + return rt6_is_valid(rt) ? dst : NULL; +@@ -2807,7 +2807,7 @@ EXPORT_INDIRECT_CALLABLE(ip6_dst_check); + static void ip6_negative_advice(struct sock *sk, + struct dst_entry *dst) + { +- struct rt6_info *rt = (struct rt6_info *) dst; ++ struct rt6_info *rt = dst_rt6_info(dst); + + if (rt->rt6i_flags & RTF_CACHE) { + rcu_read_lock(); +@@ -2830,7 +2830,7 @@ static void ip6_link_failure(struct sk_buff *skb) + + icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); + +- rt = (struct rt6_info *) skb_dst(skb); ++ rt = dst_rt6_info(skb_dst(skb)); + if (rt) { + rcu_read_lock(); + if (rt->rt6i_flags & RTF_CACHE) { +@@ -2886,7 +2886,7 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, + bool confirm_neigh) + { + const struct in6_addr *daddr, *saddr; +- struct rt6_info *rt6 = (struct rt6_info *)dst; ++ struct rt6_info *rt6 = dst_rt6_info(dst); + + /* Note: do *NOT* check dst_metric_locked(dst, RTAX_MTU) + * IPv6 pmtu discovery isn't optional, so 'mtu lock' cannot disable it. +@@ -4214,7 +4214,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu + } + } + +- rt = (struct rt6_info *) dst; ++ rt = dst_rt6_info(dst); + if (rt->rt6i_flags & RTF_REJECT) { + net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); + return; +@@ -5665,7 +5665,7 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb, + int iif, int type, u32 portid, u32 seq, + unsigned int flags) + { +- struct rt6_info *rt6 = (struct rt6_info *)dst; ++ struct rt6_info *rt6 = dst_rt6_info(dst); + struct rt6key *rt6_dst, *rt6_src; + u32 *pmetrics, table, rt6_flags; + unsigned char nh_flags = 0; +@@ -6182,7 +6182,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, + } + + +- rt = container_of(dst, struct rt6_info, dst); ++ rt = dst_rt6_info(dst); + if (rt->dst.error) { + err = rt->dst.error; + ip6_rt_put(rt); +diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c +index a1e31fe596708..3d909982d8187 100644 +--- a/net/ipv6/tcp_ipv6.c ++++ b/net/ipv6/tcp_ipv6.c +@@ -106,11 +106,9 @@ static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) + struct dst_entry *dst = skb_dst(skb); + + if (dst && dst_hold_safe(dst)) { +- const struct rt6_info *rt = (const struct rt6_info *)dst; +- + rcu_assign_pointer(sk->sk_rx_dst, dst); + sk->sk_rx_dst_ifindex = skb->skb_iif; +- sk->sk_rx_dst_cookie = rt6_get_cookie(rt); ++ sk->sk_rx_dst_cookie = rt6_get_cookie(dst_rt6_info(dst)); + } + } + +diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c +index 184fc7a5e4d17..96a46dc473360 100644 +--- a/net/ipv6/udp.c ++++ b/net/ipv6/udp.c +@@ -925,11 +925,8 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, + + static void udp6_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst) + { +- if (udp_sk_rx_dst_set(sk, dst)) { +- const struct rt6_info *rt = (const struct rt6_info *)dst; +- +- sk->sk_rx_dst_cookie = rt6_get_cookie(rt); +- } ++ if (udp_sk_rx_dst_set(sk, dst)) ++ sk->sk_rx_dst_cookie = rt6_get_cookie(dst_rt6_info(dst)); + } + + /* wrapper for udp_queue_rcv_skb tacking care of csum conversion and +@@ -1593,7 +1590,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) + + skb = ip6_make_skb(sk, getfrag, msg, ulen, + sizeof(struct udphdr), &ipc6, +- (struct rt6_info *)dst, ++ dst_rt6_info(dst), + msg->msg_flags, &cork); + err = PTR_ERR(skb); + if (!IS_ERR_OR_NULL(skb)) +@@ -1620,7 +1617,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) + ipc6.dontfrag = np->dontfrag; + up->len += ulen; + err = ip6_append_data(sk, getfrag, msg, ulen, sizeof(struct udphdr), +- &ipc6, fl6, (struct rt6_info *)dst, ++ &ipc6, fl6, dst_rt6_info(dst), + corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); + if (err) + udp_v6_flush_pending_frames(sk); +diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c +index c945473f6e5fe..0342efd0a0b0a 100644 +--- a/net/ipv6/xfrm6_policy.c ++++ b/net/ipv6/xfrm6_policy.c +@@ -80,7 +80,7 @@ static int xfrm6_get_saddr(xfrm_address_t *saddr, + static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, + const struct flowi *fl) + { +- struct rt6_info *rt = (struct rt6_info *)xdst->route; ++ struct rt6_info *rt = dst_rt6_info(xdst->route); + + xdst->u.dst.dev = dev; + netdev_hold(dev, &xdst->u.dst.dev_tracker, GFP_ATOMIC); +diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c +index bb92dc8b82f39..e282b91b396c7 100644 +--- a/net/l2tp/l2tp_ip6.c ++++ b/net/l2tp/l2tp_ip6.c +@@ -633,7 +633,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) + ulen = len + (skb_queue_empty(&sk->sk_write_queue) ? transhdrlen : 0); + err = ip6_append_data(sk, ip_generic_getfrag, msg, + ulen, transhdrlen, &ipc6, +- &fl6, (struct rt6_info *)dst, ++ &fl6, dst_rt6_info(dst), + msg->msg_flags); + if (err) + ip6_flush_pending_frames(sk); +diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c +index ef59e25dc4827..8985abcb7a058 100644 +--- a/net/mpls/mpls_iptunnel.c ++++ b/net/mpls/mpls_iptunnel.c +@@ -92,7 +92,7 @@ static int mpls_xmit(struct sk_buff *skb) + ttl = net->mpls.default_ttl; + else + ttl = ipv6_hdr(skb)->hop_limit; +- rt6 = (struct rt6_info *)dst; ++ rt6 = dst_rt6_info(dst); + } else { + goto drop; + } +diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c +index 9793eb8884373..0fc8cd2d4859e 100644 +--- a/net/netfilter/ipvs/ip_vs_xmit.c ++++ b/net/netfilter/ipvs/ip_vs_xmit.c +@@ -184,7 +184,7 @@ static inline bool crosses_local_route_boundary(int skb_af, struct sk_buff *skb, + (!skb->dev || skb->dev->flags & IFF_LOOPBACK) && + (addr_type & IPV6_ADDR_LOOPBACK); + old_rt_is_local = __ip_vs_is_local_route6( +- (struct rt6_info *)skb_dst(skb)); ++ dst_rt6_info(skb_dst(skb))); + } else + #endif + { +@@ -484,7 +484,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, + if (dest) { + dest_dst = __ip_vs_dst_check(dest); + if (likely(dest_dst)) +- rt = (struct rt6_info *) dest_dst->dst_cache; ++ rt = dst_rt6_info(dest_dst->dst_cache); + else { + u32 cookie; + +@@ -504,7 +504,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, + ip_vs_dest_dst_free(dest_dst); + goto err_unreach; + } +- rt = (struct rt6_info *) dst; ++ rt = dst_rt6_info(dst); + cookie = rt6_get_cookie(rt); + __ip_vs_dst_set(dest, dest_dst, &rt->dst, cookie); + spin_unlock_bh(&dest->dst_lock); +@@ -520,7 +520,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, + rt_mode); + if (!dst) + goto err_unreach; +- rt = (struct rt6_info *) dst; ++ rt = dst_rt6_info(dst); + } + + local = __ip_vs_is_local_route6(rt); +@@ -879,7 +879,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + IP_VS_RT_MODE_RDR); + if (local < 0) + goto tx_error; +- rt = (struct rt6_info *) skb_dst(skb); ++ rt = dst_rt6_info(skb_dst(skb)); + /* + * Avoid duplicate tuple in reply direction for NAT traffic + * to local address when connection is sync-ed +@@ -1315,7 +1315,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + if (local) + return ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 1); + +- rt = (struct rt6_info *) skb_dst(skb); ++ rt = dst_rt6_info(skb_dst(skb)); + tdev = rt->dst.dev; + + /* +@@ -1636,7 +1636,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + &cp->daddr.in6, NULL, ipvsh, 0, rt_mode); + if (local < 0) + goto tx_error; +- rt = (struct rt6_info *) skb_dst(skb); ++ rt = dst_rt6_info(skb_dst(skb)); + /* + * Avoid duplicate tuple in reply direction for NAT traffic + * to local address when connection is sync-ed +diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c +index 99195cf6b2657..9edc627d94b9e 100644 +--- a/net/netfilter/nf_flow_table_core.c ++++ b/net/netfilter/nf_flow_table_core.c +@@ -77,12 +77,8 @@ EXPORT_SYMBOL_GPL(flow_offload_alloc); + + static u32 flow_offload_dst_cookie(struct flow_offload_tuple *flow_tuple) + { +- const struct rt6_info *rt; +- +- if (flow_tuple->l3proto == NFPROTO_IPV6) { +- rt = (const struct rt6_info *)flow_tuple->dst_cache; +- return rt6_get_cookie(rt); +- } ++ if (flow_tuple->l3proto == NFPROTO_IPV6) ++ return rt6_get_cookie(dst_rt6_info(flow_tuple->dst_cache)); + + return 0; + } +diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c +index 34be2c9bc39d8..523228e969ab4 100644 +--- a/net/netfilter/nf_flow_table_ip.c ++++ b/net/netfilter/nf_flow_table_ip.c +@@ -665,7 +665,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, + nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len); + + if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) { +- rt = (struct rt6_info *)tuplehash->tuple.dst_cache; ++ rt = dst_rt6_info(tuplehash->tuple.dst_cache); + memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); + IP6CB(skb)->iif = skb->dev->ifindex; + IP6CB(skb)->flags = IP6SKB_FORWARDED; +@@ -674,7 +674,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, + + switch (tuplehash->tuple.xmit_type) { + case FLOW_OFFLOAD_XMIT_NEIGH: +- rt = (struct rt6_info *)tuplehash->tuple.dst_cache; ++ rt = dst_rt6_info(tuplehash->tuple.dst_cache); + outdev = rt->dst.dev; + skb->dev = outdev; + nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6); +diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c +index 7d21e16499bfa..eea3ee809c47d 100644 +--- a/net/netfilter/nft_rt.c ++++ b/net/netfilter/nft_rt.c +@@ -80,7 +80,7 @@ void nft_rt_get_eval(const struct nft_expr *expr, + if (nft_pf(pkt) != NFPROTO_IPV6) + goto err; + +- memcpy(dest, rt6_nexthop((struct rt6_info *)dst, ++ memcpy(dest, rt6_nexthop(dst_rt6_info(dst), + &ipv6_hdr(skb)->daddr), + sizeof(struct in6_addr)); + break; +diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c +index a1cb8ac0408af..be190f5696d88 100644 +--- a/net/sctp/ipv6.c ++++ b/net/sctp/ipv6.c +@@ -416,7 +416,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + if (!IS_ERR_OR_NULL(dst)) { + struct rt6_info *rt; + +- rt = (struct rt6_info *)dst; ++ rt = dst_rt6_info(dst); + t->dst_cookie = rt6_get_cookie(rt); + pr_debug("rt6_dst:%pI6/%d rt6_src:%pI6\n", + &rt->rt6i_dst.addr, rt->rt6i_dst.plen, +diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c +index dbaef78f43c21..2a47f5b5776e8 100644 +--- a/net/xfrm/xfrm_policy.c ++++ b/net/xfrm/xfrm_policy.c +@@ -2526,8 +2526,7 @@ static void xfrm_init_path(struct xfrm_dst *path, struct dst_entry *dst, + int nfheader_len) + { + if (dst->ops->family == AF_INET6) { +- struct rt6_info *rt = (struct rt6_info *)dst; +- path->path_cookie = rt6_get_cookie(rt); ++ path->path_cookie = rt6_get_cookie(dst_rt6_info(dst)); + path->u.rt6.rt6i_nfheader_len = nfheader_len; + } + } +-- +2.53.0 + diff --git a/queue-6.1/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch b/queue-6.1/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch new file mode 100644 index 0000000000..e35a8d488f --- /dev/null +++ b/queue-6.1/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch @@ -0,0 +1,335 @@ +From e0442e13a6c31b6c234b61213ee4c237290c796d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:17 +0300 +Subject: ipvs: do not mangle ICMP replies for non-first fragments + +From: Julian Anastasov + +[ Upstream commit 342e24a339b90e8e339a0f8c151ca479b8565661 ] + +Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the +payload for embedded non-first IPv4 fragments. The problem is +in the very old inverted pp->dont_defrag check which should not +continue when embedded is a non-first TCP/UDP/SCTP fragment. + +Check for embedded non-first fragment is also missing from +ip_vs_out_icmp_v6(), it is needed before any connection +lookups that expect ports after the network headers. + +Drop the blocking code from ip_vs_in_icmp_v6() which prevents +ICMPv6 from local clients to use non-MASQ forwarding. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Link: https://sashiko.dev/#/patchset/20260720201122.79882-1-ja%40ssi.bg +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 11 +++--- + net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++--------------------- + net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++---- + 3 files changed, 48 insertions(+), 52 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index db5e3832ccd46..344f0082d3041 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -1497,8 +1497,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph); ++ unsigned int hooknum, struct ip_vs_iphdr *ciph); + void ip_vs_dest_dst_rcu_free(struct rcu_head *head); + + #ifdef CONFIG_IP_VS_IPV6 +@@ -1512,8 +1511,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph); ++ unsigned int hooknum, struct ip_vs_iphdr *ciph); + #endif + + #ifdef CONFIG_SYSCTL +@@ -1578,12 +1576,13 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) + } + + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir, unsigned int toff); ++ struct ip_vs_conn *cp, int dir, unsigned int toff, ++ bool has_ports); + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int dir, unsigned int toff, +- struct ip_vs_iphdr *ciph); ++ bool has_ports, struct ip_vs_iphdr *ciph); + #endif + + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index 01a87530515d5..27c096b070774 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -746,7 +746,8 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, + * - inout: 1=in->out, 0=out->in + */ + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout, unsigned int toff) ++ struct ip_vs_conn *cp, int inout, unsigned int toff, ++ bool has_ports) + { + struct iphdr *iph = ip_hdr(skb); + struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); +@@ -766,8 +767,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + } + + /* the TCP/UDP/SCTP port */ +- if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol || +- IPPROTO_SCTP == ciph->protocol) { ++ if (has_ports) { + __be16 *ports = (void *)ciph + ciph->ihl*4; + + if (inout) +@@ -792,18 +792,15 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int inout, unsigned int toff, +- struct ip_vs_iphdr *ciph) ++ bool has_ports, struct ip_vs_iphdr *ciph) + { + struct ipv6hdr *iph = ipv6_hdr(skb); +- int protocol; + struct icmp6hdr *icmph; + struct ipv6hdr *cih; + + icmph = (struct icmp6hdr *)(skb->data + toff); + cih = (struct ipv6hdr *)(skb->data + ciph->off); + +- protocol = ciph->protocol; +- + if (inout) { + iph->saddr = cp->vaddr.in6; + cih->daddr = cp->vaddr.in6; +@@ -813,9 +810,7 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + } + + /* the TCP/UDP/SCTP port */ +- if (!ciph->fragoffs && +- (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || +- protocol == IPPROTO_SCTP)) { ++ if (has_ports) { + __be16 *ports = (void *)(skb->data + ciph->len); + + IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, +@@ -857,6 +852,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; + unsigned int ctoff = ciph->len; ++ bool has_ports = false; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; +@@ -870,17 +866,19 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + } + + if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || +- ciph->protocol == IPPROTO_SCTP) ++ ciph->protocol == IPPROTO_SCTP) { + ctoff += 2 * sizeof(__u16); ++ has_ports = true; ++ } + if (skb_ensure_writable(skb, ctoff)) + goto out; + + #ifdef CONFIG_IP_VS_IPV6 + if (af == AF_INET6) +- ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph); + else + #endif +- ip_vs_nat_icmp(skb, pp, cp, 1, toff); ++ ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports); + + if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) + goto out; +@@ -964,8 +962,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + + /* Is the embedded protocol header present? */ +- if (unlikely(cih->frag_off & htons(IP_OFFSET) && +- pp->dont_defrag)) ++ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, +@@ -1029,6 +1026,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (!pp) + return NF_ACCEPT; + ++ /* Is the embedded protocol header present? */ ++ if (unlikely(ciph.fragoffs && !pp->dont_defrag)) ++ return NF_ACCEPT; ++ + /* The embedded headers contain source and dest in reverse order */ + cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto, + ipvs, AF_INET6, skb, &ciph); +@@ -1692,8 +1693,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + pp = pd->pp; + + /* Is the embedded protocol header present? */ +- if (unlikely(cih->frag_off & htons(IP_OFFSET) && +- pp->dont_defrag)) ++ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, +@@ -1701,7 +1701,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + + offset2 = offset; + ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph); +- offset = ciph.len; + + /* The embedded headers contain source and dest in reverse order. + * For IPIP/UDP/GRE tunnel this is error for request, not for reply. +@@ -1795,11 +1794,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); +- if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || +- IPPROTO_SCTP == cih->protocol) +- offset += 2 * sizeof(__u16); +- verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, +- &ciph); ++ verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph); + + out: + if (likely(!new_cp)) +@@ -1859,8 +1854,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + pp = pd->pp; + +- /* Cannot handle fragmented embedded protocol */ +- if (ciph.fragoffs) ++ /* Is the embedded protocol header present? */ ++ if (ciph.fragoffs && !pp->dont_defrag) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset, +@@ -1884,13 +1879,6 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + new_cp = true; + } + +- /* VS/TUN, VS/DR and LOCALNODE just let it go */ +- if ((hooknum == NF_INET_LOCAL_OUT) && +- (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) { +- verdict = NF_ACCEPT; +- goto out; +- } +- + verdict = NF_DROP; + + /* Ensure the checksum is correct */ +@@ -1906,14 +1894,7 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); + +- /* Need to mangle contained IPv6 header in ICMPv6 packet */ +- offset = ciph.len; +- if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol || +- IPPROTO_SCTP == ciph.protocol) +- offset += 2 * sizeof(__u16); /* Also mangle ports */ +- +- verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, +- &ciph); ++ verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph); + + out: + if (likely(!new_cp)) +diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c +index 06179804b0da6..db2f090e2f8f9 100644 +--- a/net/netfilter/ipvs/ip_vs_xmit.c ++++ b/net/netfilter/ipvs/ip_vs_xmit.c +@@ -1512,13 +1512,14 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + int + ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph) ++ unsigned int hooknum, struct ip_vs_iphdr *ciph) + { + struct rtable *rt; /* Route to the other host */ + int rc; + int local; + int rt_mode, was_input; ++ bool has_ports = false; ++ unsigned int wlen; + + EnterFunction(10); + +@@ -1576,6 +1577,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + goto tx_error; + } + ++ wlen = ciph->len; ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) { ++ wlen += 2 * sizeof(__u16); /* Also mangle ports */ ++ has_ports = true; ++ } ++ + /* copy-on-write the packet before mangling it */ + if (skb_ensure_writable(skb, wlen)) + goto tx_error; +@@ -1583,7 +1591,7 @@ 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); ++ ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +@@ -1603,10 +1611,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + int + ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph) ++ unsigned int hooknum, struct ip_vs_iphdr *ciph) + { ++ bool has_ports = false; + struct rt6_info *rt; /* Route to the other host */ ++ unsigned int wlen; + int rc; + int local; + int rt_mode; +@@ -1666,6 +1675,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + goto tx_error; + } + ++ wlen = ciph->len; ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) { ++ wlen += 2 * sizeof(__u16); /* Also mangle ports */ ++ has_ports = true; ++ } ++ + /* copy-on-write the packet before mangling it */ + if (skb_ensure_writable(skb, wlen)) + goto tx_error; +@@ -1673,7 +1689,7 @@ ip_vs_icmp_xmit_v6(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_v6(skb, pp, cp, 0, toff, ciph); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +-- +2.53.0 + diff --git a/queue-6.1/ipvs-fix-places-with-wrong-packet-offsets.patch b/queue-6.1/ipvs-fix-places-with-wrong-packet-offsets.patch new file mode 100644 index 0000000000..98c3437c56 --- /dev/null +++ b/queue-6.1/ipvs-fix-places-with-wrong-packet-offsets.patch @@ -0,0 +1,624 @@ +From c53e670b99be3aa6818a773f600de70bf4ed972c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:16 +0300 +Subject: ipvs: fix places with wrong packet offsets + +From: Julian Anastasov + +[ Upstream commit 15cab31a3730e05f0767b922a7450e5d784b2607 ] + +The offsets we use to packet headers and payloads should be +based on skb->data. We even already respect non-zero +network offset in ip_vs_fill_iph_skb() but some places +do it wrongly and support only zero offset which is expected +for the IP layer where IPVS has hooks. + +Change all places that instead of skb->data use offsets based +on the network header (skb_network_header, ip_hdr, etc) because +this doubles the network offset as noted by Sashiko. + +For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header +parsing done by the caller. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Link: https://sashiko.dev/#/patchset/20260710143733.29741-2-fw%40strlen.de +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 15 +-- + net/netfilter/ipvs/ip_vs_app.c | 4 +- + net/netfilter/ipvs/ip_vs_core.c | 133 +++++++++++++------------- + net/netfilter/ipvs/ip_vs_proto_sctp.c | 4 +- + net/netfilter/ipvs/ip_vs_proto_tcp.c | 4 +- + net/netfilter/ipvs/ip_vs_proto_udp.c | 4 +- + net/netfilter/ipvs/ip_vs_xmit.c | 26 ++--- + 7 files changed, 97 insertions(+), 93 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index 22793d64a1295..db5e3832ccd46 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -1496,8 +1496,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, +- unsigned int hooknum, struct ip_vs_iphdr *iph); ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph); + void ip_vs_dest_dst_rcu_free(struct rcu_head *head); + + #ifdef CONFIG_IP_VS_IPV6 +@@ -1510,8 +1511,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, +- unsigned int hooknum, struct ip_vs_iphdr *iph); ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph); + #endif + + #ifdef CONFIG_SYSCTL +@@ -1576,11 +1578,12 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) + } + + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir); ++ struct ip_vs_conn *cp, int dir, unsigned int toff); + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir); ++ struct ip_vs_conn *cp, int dir, unsigned int toff, ++ struct ip_vs_iphdr *ciph); + #endif + + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) +diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c +index f9132b359f0c6..0c690a30a85dc 100644 +--- a/net/netfilter/ipvs/ip_vs_app.c ++++ b/net/netfilter/ipvs/ip_vs_app.c +@@ -368,7 +368,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb, + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) + return 0; + +- th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); ++ th = (struct tcphdr *)(skb->data + ipvsh->len); + + /* + * Remember seq number in case this pkt gets resized +@@ -444,7 +444,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb, + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) + return 0; + +- th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); ++ th = (struct tcphdr *)(skb->data + ipvsh->len); + + /* + * Remember seq number in case this pkt gets resized +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index 4ab62ed6e4333..01a87530515d5 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -746,13 +746,12 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, + * - inout: 1=in->out, 0=out->in + */ + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout) ++ struct ip_vs_conn *cp, int inout, unsigned int toff) + { + struct iphdr *iph = ip_hdr(skb); +- unsigned int icmp_offset = iph->ihl*4; +- struct icmphdr *icmph = (struct icmphdr *)(skb_network_header(skb) + +- icmp_offset); ++ struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); + struct iphdr *ciph = (struct iphdr *)(icmph + 1); ++ unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr); + + if (inout) { + iph->saddr = cp->vaddr.ip; +@@ -779,48 +778,45 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + + /* And finally the ICMP checksum */ + icmph->checksum = 0; +- icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset); ++ icmph->checksum = ip_vs_checksum_complete(skb, toff); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (inout) +- IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, +- "Forwarding altered outgoing ICMP"); ++ IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, ++ "Forwarding altered outgoing ICMP"); + else +- IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, +- "Forwarding altered incoming ICMP"); ++ IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, ++ "Forwarding altered incoming ICMP"); + } + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout) ++ struct ip_vs_conn *cp, int inout, unsigned int toff, ++ struct ip_vs_iphdr *ciph) + { + struct ipv6hdr *iph = ipv6_hdr(skb); +- unsigned int icmp_offset = 0; +- unsigned int offs = 0; /* header offset*/ + int protocol; + struct icmp6hdr *icmph; +- struct ipv6hdr *ciph; +- unsigned short fragoffs; ++ struct ipv6hdr *cih; + +- ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL); +- icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset); +- offs = icmp_offset + sizeof(struct icmp6hdr); +- ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs); ++ icmph = (struct icmp6hdr *)(skb->data + toff); ++ cih = (struct ipv6hdr *)(skb->data + ciph->off); + +- protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL); ++ protocol = ciph->protocol; + + if (inout) { + iph->saddr = cp->vaddr.in6; +- ciph->daddr = cp->vaddr.in6; ++ cih->daddr = cp->vaddr.in6; + } else { + iph->daddr = cp->daddr.in6; +- ciph->saddr = cp->daddr.in6; ++ cih->saddr = cp->daddr.in6; + } + + /* the TCP/UDP/SCTP port */ +- if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || +- IPPROTO_SCTP == protocol)) { +- __be16 *ports = (void *)(skb_network_header(skb) + offs); ++ if (!ciph->fragoffs && ++ (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || ++ protocol == IPPROTO_SCTP)) { ++ __be16 *ports = (void *)(skb->data + ciph->len); + + IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, + ntohs(inout ? ports[1] : ports[0]), +@@ -833,19 +829,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + + /* And finally the ICMP checksum */ + icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, +- skb->len - icmp_offset, ++ skb->len - toff, + IPPROTO_ICMPV6, 0); +- skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset; ++ skb->csum_start = skb_headroom(skb) + toff; + skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum); + skb->ip_summed = CHECKSUM_PARTIAL; + + if (inout) +- IP_VS_DBG_PKT(11, AF_INET6, pp, skb, +- (void *)ciph - (void *)iph, ++ IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, + "Forwarding altered outgoing ICMPv6"); + else +- IP_VS_DBG_PKT(11, AF_INET6, pp, skb, +- (void *)ciph - (void *)iph, ++ IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, + "Forwarding altered incoming ICMPv6"); + } + #endif +@@ -855,37 +849,38 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + */ + static int handle_response_icmp(int af, struct sk_buff *skb, + union nf_inet_addr *snet, +- __u8 protocol, struct ip_vs_conn *cp, ++ struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, +- unsigned int offset, unsigned int ihl, +- unsigned int hooknum) ++ struct ip_vs_iphdr *ciph, ++ unsigned int toff, unsigned int hooknum) + { + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; ++ unsigned int ctoff = ciph->len; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; + + /* Ensure the checksum is correct */ +- if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { ++ if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) { + /* Failed checksum! */ + IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", + IP_VS_DBG_ADDR(af, snet)); + goto out; + } + +- if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || +- IPPROTO_SCTP == protocol) +- offset += 2 * sizeof(__u16); +- if (skb_ensure_writable(skb, offset)) ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) ++ ctoff += 2 * sizeof(__u16); ++ if (skb_ensure_writable(skb, ctoff)) + goto out; + + #ifdef CONFIG_IP_VS_IPV6 + if (af == AF_INET6) +- ip_vs_nat_icmp_v6(skb, pp, cp, 1); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); + else + #endif +- ip_vs_nat_icmp(skb, pp, cp, 1); ++ ip_vs_nat_icmp(skb, pp, cp, 1, toff); + + if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) + goto out; +@@ -913,9 +908,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + * Currently handles error types - unreachable, quench, ttl exceeded. + */ + static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, +- int *related, unsigned int hooknum) ++ int *related, unsigned int hooknum, ++ struct ip_vs_iphdr *ipvsh) + { +- struct iphdr *iph; + struct icmphdr _icmph, *ic; + struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ + struct ip_vs_iphdr ciph; +@@ -930,17 +925,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (ip_is_fragment(ip_hdr(skb))) { + if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) + return NF_STOLEN; ++ if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh)) ++ return NF_ACCEPT; + } + +- iph = ip_hdr(skb); +- offset = ihl = iph->ihl * 4; ++ ihl = ipvsh->len; ++ offset = ipvsh->len; + ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); + if (ic == NULL) + return NF_DROP; + + IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n", + ic->type, ntohs(icmp_id(ic)), +- &iph->saddr, &iph->daddr); ++ &ipvsh->saddr.ip, &ipvsh->daddr.ip); + + /* + * Work through seeing if this is for us. +@@ -959,7 +956,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + /* Now find the contained IP header */ + offset += sizeof(_icmph); + cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); +- if (cih == NULL) ++ if (!(cih && cih->version == 4 && cih->ihl >= 5)) + return NF_ACCEPT; /* The packet looks wrong, ignore */ + + pp = ip_vs_proto_get(cih->protocol); +@@ -982,9 +979,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (!cp) + return NF_ACCEPT; + +- snet.ip = iph->saddr; +- return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp, +- pp, ciph.len, ihl, hooknum); ++ snet.ip = ipvsh->saddr.ip; ++ return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl, ++ hooknum); + } + + #ifdef CONFIG_IP_VS_IPV6 +@@ -997,7 +994,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + struct ip_vs_conn *cp; + struct ip_vs_protocol *pp; + union nf_inet_addr snet; +- unsigned int offset; + + *related = 1; + ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph); +@@ -1040,9 +1036,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + + snet.in6 = ciph.saddr.in6; +- offset = ciph.len; +- return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp, +- pp, offset, ipvsh->len, hooknum); ++ return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph, ++ ipvsh->len, hooknum); + } + #endif + +@@ -1377,7 +1372,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat + #endif + if (unlikely(iph.protocol == IPPROTO_ICMP)) { + int related; +- int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum); ++ int verdict = ip_vs_out_icmp(ipvs, skb, &related, ++ hooknum, &iph); + + if (related) + return verdict; +@@ -1581,9 +1577,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb, + */ + static int + ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, +- unsigned int hooknum) ++ unsigned int hooknum, struct ip_vs_iphdr *iph) + { +- struct iphdr *iph; + struct icmphdr _icmph, *ic; + struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ + struct ip_vs_iphdr ciph; +@@ -1593,7 +1588,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + unsigned int offset, offset2, ihl, verdict; + bool tunnel, new_cp = false; + union nf_inet_addr *raddr; +- char *outer_proto = "IPIP"; ++ char *outer_proto __maybe_unused = "IPIP"; + unsigned int hlen_ipip; + int ulen = 0; + +@@ -1603,17 +1598,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + if (ip_is_fragment(ip_hdr(skb))) { + if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) + return NF_STOLEN; ++ if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph)) ++ return NF_ACCEPT; + } + +- iph = ip_hdr(skb); +- offset = ihl = iph->ihl * 4; ++ ihl = iph->len; ++ offset = iph->len; + ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); + if (ic == NULL) + return NF_DROP; + + IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n", + ic->type, ntohs(icmp_id(ic)), +- &iph->saddr, &iph->daddr); ++ &iph->saddr.ip, &iph->daddr.ip); + + /* + * Work through seeing if this is for us. +@@ -1730,7 +1727,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { + /* Failed checksum! */ + IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", +- &iph->saddr); ++ &iph->saddr.ip); + goto out; + } + +@@ -1801,7 +1798,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || + IPPROTO_SCTP == cih->protocol) + offset += 2 * sizeof(__u16); +- verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph); ++ verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, ++ &ciph); + + out: + if (likely(!new_cp)) +@@ -1914,7 +1912,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + IPPROTO_SCTP == ciph.protocol) + offset += 2 * sizeof(__u16); /* Also mangle ports */ + +- verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph); ++ verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, ++ &ciph); + + out: + if (likely(!new_cp)) +@@ -1993,7 +1992,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state + if (unlikely(iph.protocol == IPPROTO_ICMP)) { + int related; + int verdict = ip_vs_in_icmp(ipvs, skb, &related, +- hooknum); ++ hooknum, &iph); + + if (related) + return verdict; +@@ -2129,6 +2128,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) + { + struct netns_ipvs *ipvs = net_ipvs(state->net); ++ struct ip_vs_iphdr iphdr; + int r; + + /* ipvs enabled in this netns ? */ +@@ -2138,10 +2138,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + if (state->pf == NFPROTO_IPV4) { + if (ip_hdr(skb)->protocol != IPPROTO_ICMP) + return NF_ACCEPT; ++ ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr); + #ifdef CONFIG_IP_VS_IPV6 + } else { +- struct ip_vs_iphdr iphdr; +- + ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr); + + if (iphdr.protocol != IPPROTO_ICMPV6) +@@ -2151,7 +2150,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + #endif + } + +- return ip_vs_in_icmp(ipvs, skb, &r, state->hook); ++ return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr); + } + + static const struct nf_hook_ops ip_vs_ops4[] = { +diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c +index f6f732b7dfa86..3dbd3096e1637 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c +@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- sctph = (void *) skb_network_header(skb) + sctphoff; ++ sctph = (void *)skb->data + sctphoff; + + /* Only update csum if we really have to */ + if (sctph->source != cp->vport || payload_csum || +@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- sctph = (void *) skb_network_header(skb) + sctphoff; ++ sctph = (void *)skb->data + sctphoff; + + /* Only update csum if we really have to */ + if (sctph->dest != cp->dport || payload_csum || +diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c +index bf31127338aa0..1ac9c233537d3 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c +@@ -180,7 +180,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- tcph = (void *)skb_network_header(skb) + tcphoff; ++ tcph = (void *)skb->data + tcphoff; + tcph->source = cp->vport; + + /* Adjust TCP checksums */ +@@ -261,7 +261,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- tcph = (void *)skb_network_header(skb) + tcphoff; ++ tcph = (void *)skb->data + tcphoff; + tcph->dest = cp->dport; + + /* +diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c +index 40d30649b3048..96ac882df15c1 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_udp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c +@@ -171,7 +171,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- udph = (void *)skb_network_header(skb) + udphoff; ++ udph = (void *)skb->data + udphoff; + udph->source = cp->vport; + + /* +@@ -255,7 +255,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- udph = (void *)skb_network_header(skb) + udphoff; ++ udph = (void *)skb->data + udphoff; + udph->dest = cp->dport; + + /* +diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c +index 0fc8cd2d4859e..06179804b0da6 100644 +--- a/net/netfilter/ipvs/ip_vs_xmit.c ++++ b/net/netfilter/ipvs/ip_vs_xmit.c +@@ -1511,8 +1511,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + */ + int + ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, unsigned int hooknum, +- struct ip_vs_iphdr *iph) ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph) + { + struct rtable *rt; /* Route to the other host */ + int rc; +@@ -1526,7 +1527,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + translate address/port back */ + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { + if (cp->packet_xmit) +- rc = cp->packet_xmit(skb, cp, pp, iph); ++ rc = cp->packet_xmit(skb, cp, pp, ciph); + else + rc = NF_ACCEPT; + /* do not touch skb anymore */ +@@ -1544,7 +1545,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; + local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode, +- NULL, iph); ++ NULL, ciph); + if (local < 0) + goto tx_error; + rt = skb_rtable(skb); +@@ -1576,13 +1577,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + } + + /* copy-on-write the packet before mangling it */ +- if (skb_ensure_writable(skb, offset)) ++ if (skb_ensure_writable(skb, wlen)) + goto tx_error; + + if (skb_cow(skb, rt->dst.dev->hard_header_len)) + goto tx_error; + +- ip_vs_nat_icmp(skb, pp, cp, 0); ++ ip_vs_nat_icmp(skb, pp, cp, 0, toff); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +@@ -1601,8 +1602,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + #ifdef CONFIG_IP_VS_IPV6 + int + ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, unsigned int hooknum, +- struct ip_vs_iphdr *ipvsh) ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph) + { + struct rt6_info *rt; /* Route to the other host */ + int rc; +@@ -1616,7 +1618,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + translate address/port back */ + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { + if (cp->packet_xmit) +- rc = cp->packet_xmit(skb, cp, pp, ipvsh); ++ rc = cp->packet_xmit(skb, cp, pp, ciph); + else + rc = NF_ACCEPT; + /* do not touch skb anymore */ +@@ -1633,7 +1635,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; + local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest, +- &cp->daddr.in6, NULL, ipvsh, 0, rt_mode); ++ &cp->daddr.in6, NULL, ciph, 0, rt_mode); + if (local < 0) + goto tx_error; + rt = dst_rt6_info(skb_dst(skb)); +@@ -1665,13 +1667,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + } + + /* copy-on-write the packet before mangling it */ +- if (skb_ensure_writable(skb, offset)) ++ if (skb_ensure_writable(skb, wlen)) + goto tx_error; + + if (skb_cow(skb, rt->dst.dev->hard_header_len)) + goto tx_error; + +- ip_vs_nat_icmp_v6(skb, pp, cp, 0); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +-- +2.53.0 + diff --git a/queue-6.1/ipvs-fix-the-checksum-validations.patch b/queue-6.1/ipvs-fix-the-checksum-validations.patch new file mode 100644 index 0000000000..f217309f76 --- /dev/null +++ b/queue-6.1/ipvs-fix-the-checksum-validations.patch @@ -0,0 +1,389 @@ +From 2bdcbfe93c48958393a87764b9b1f310fcc0c1c9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:15 +0300 +Subject: ipvs: fix the checksum validations + +From: Julian Anastasov + +[ Upstream commit e876b75b9020a97bbdc79721e7fc749024891c65 ] + +ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6 +packets from clients. In fact, as for TCP/UDP we should +validate the checksum for ICMP packets only when we +mangle the packets on MASQ or on reply for tunnel. + +Also, Sashiko points out that handle_response_icmp() being +common for IPv4 and IPv6 is missing the pseudo-header +calculation while validating ICMPv6 messages from real +servers which is a problem if checksum is not validated +by the hardware. + +Fix the problems by creating ip_vs_checksum_common_check() +helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6. +Rely on the nf_checksum() for validating the ICMP messages +but use it also for TCP and UDP. + +Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP. + +IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum +validation on LOCAL_OUT (local clients or local real +servers) and on FORWARD (traffic from servers on LAN). +Do it only on LOCAL_IN, in case nf_checksum() is not +called on PRE_ROUTING. + +Also, ip_vs_checksum_complete() can be marked static. + +Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6") +Link: https://sashiko.dev/#/patchset/20260708180315.77413-1-ja%40ssi.bg +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 31 +++++++++++++++-- + net/netfilter/ipvs/ip_vs_core.c | 20 +++++++++-- + net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++---- + net/netfilter/ipvs/ip_vs_proto_tcp.c | 44 +++++------------------ + net/netfilter/ipvs/ip_vs_proto_udp.c | 50 ++++++--------------------- + 5 files changed, 74 insertions(+), 86 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index 35961160ebdec..22793d64a1295 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -24,7 +24,9 @@ + #include /* for union nf_inet_addr */ + #include + #include /* for struct ipv6hdr */ ++#include + #include ++#include + #if IS_ENABLED(CONFIG_NF_CONNTRACK) + #include + #endif +@@ -1581,8 +1583,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int dir); + #endif + +-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset); +- + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) + { + __be32 diff[2] = { ~old, new }; +@@ -1608,6 +1608,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum) + return csum_partial(diff, sizeof(diff), oldsum); + } + ++static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af) ++{ ++ /* Checksum unnecessary or already validated? */ ++ if (skb_csum_unnecessary(skb)) ++ return false; ++ /* LOCAL_OUT ? */ ++ if (!skb->dev || skb->dev->flags & IFF_LOOPBACK) ++ return false; ++ /* !LOCAL_IN (FORWARD) ? */ ++ if (af == AF_INET6) { ++ if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL)) ++ return false; ++ } else { ++ if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL)) ++ return false; ++ } ++ return true; ++} ++ ++static inline bool ip_vs_checksum_common_check(struct sk_buff *skb, ++ int offset, int proto, int af) ++{ ++ if (!ip_vs_checksum_needed(skb, af)) ++ return true; ++ return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af); ++} ++ + /* Forget current conntrack (unconfirmed) and attach notrack entry */ + static inline void ip_vs_notrack(struct sk_buff *skb) + { +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index 4b03857e41d77..4ab62ed6e4333 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -689,7 +689,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; } + + #endif + +-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) ++static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) + { + return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0)); + } +@@ -860,13 +860,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + unsigned int offset, unsigned int ihl, + unsigned int hooknum) + { ++ int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; + + /* Ensure the checksum is correct */ +- if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { ++ if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { + /* Failed checksum! */ + IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", + IP_VS_DBG_ADDR(af, snet)); +@@ -1725,7 +1726,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + verdict = NF_DROP; + + /* Ensure the checksum is correct */ +- if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { ++ if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) && ++ !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { + /* Failed checksum! */ + IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", + &iph->saddr); +@@ -1891,6 +1893,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + goto out; + } + ++ verdict = NF_DROP; ++ ++ /* Ensure the checksum is correct */ ++ if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ && ++ !ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6, ++ AF_INET6)) { ++ /* Failed checksum! */ ++ IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n", ++ &iph->saddr); ++ goto out; ++ } ++ + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); + +diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c +index c67317be17dfa..f6f732b7dfa86 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c +@@ -11,7 +11,7 @@ + + static int + sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int sctphoff); ++ struct ip_vs_iphdr *iph); + + static int + sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) ++ if (!sctp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) ++ if (!sctp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int sctphoff) ++ struct ip_vs_iphdr *iph) + { ++ unsigned int sctphoff = iph->len; + struct sctphdr *sh; + __le32 cmp, val; + ++ if (!ip_vs_checksum_needed(skb, af)) ++ return 1; + sh = (struct sctphdr *)(skb->data + sctphoff); + cmp = sh->checksum; + val = sctp_compute_cksum(skb, sctphoff); + + if (val != cmp) { + /* CRC failure, dump it. */ +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); + return 0; + } + return 1; +diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c +index b382810156b2c..bf31127338aa0 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c +@@ -30,7 +30,7 @@ + + static int + tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int tcphoff); ++ struct ip_vs_iphdr *iph); + + static int + tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -167,7 +167,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) ++ if (!tcp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -245,7 +245,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) ++ if (!tcp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -303,41 +303,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int tcphoff) ++ struct ip_vs_iphdr *iph) + { +- switch (skb->ip_summed) { +- case CHECKSUM_NONE: +- skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); +- fallthrough; +- case CHECKSUM_COMPLETE: +-#ifdef CONFIG_IP_VS_IPV6 +- if (af == AF_INET6) { +- if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, +- &ipv6_hdr(skb)->daddr, +- skb->len - tcphoff, +- IPPROTO_TCP, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- } else +-#endif +- if (csum_tcpudp_magic(ip_hdr(skb)->saddr, +- ip_hdr(skb)->daddr, +- skb->len - tcphoff, +- ip_hdr(skb)->protocol, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- break; +- default: +- /* No need to checksum. */ +- break; ++ if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) { ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); ++ return 0; + } +- + return 1; + } + +diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c +index dbd4155bb0752..40d30649b3048 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_udp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c +@@ -26,7 +26,7 @@ + + static int + udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int udphoff); ++ struct ip_vs_iphdr *iph); + + static int + udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -156,7 +156,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!udp_csum_check(cp->af, skb, pp, udphoff)) ++ if (!udp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -239,7 +239,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!udp_csum_check(cp->af, skb, pp, udphoff)) ++ if (!udp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -299,48 +299,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int udphoff) ++ struct ip_vs_iphdr *iph) + { + struct udphdr _udph, *uh; + +- uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph); ++ uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph); + if (uh == NULL) + return 0; + +- if (uh->check != 0) { +- switch (skb->ip_summed) { +- case CHECKSUM_NONE: +- skb->csum = skb_checksum(skb, udphoff, +- skb->len - udphoff, 0); +- fallthrough; +- case CHECKSUM_COMPLETE: +-#ifdef CONFIG_IP_VS_IPV6 +- if (af == AF_INET6) { +- if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, +- &ipv6_hdr(skb)->daddr, +- skb->len - udphoff, +- IPPROTO_UDP, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- } else +-#endif +- if (csum_tcpudp_magic(ip_hdr(skb)->saddr, +- ip_hdr(skb)->daddr, +- skb->len - udphoff, +- ip_hdr(skb)->protocol, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- break; +- default: +- /* No need to checksum. */ +- break; +- } ++ if (!uh->check) ++ return 1; ++ if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) { ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); ++ return 0; + } + return 1; + } +-- +2.53.0 + diff --git a/queue-6.1/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-6.1/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch new file mode 100644 index 0000000000..3f0dcd578a --- /dev/null +++ b/queue-6.1/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch @@ -0,0 +1,64 @@ +From 6bf2c6df208b9fccf96544bf7136f7e45429e817 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:03 -0400 +Subject: keys: fix out-of-bounds read in keyring_get_key_chunk() + +From: Michael Bommarito + +[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ] + +For description-level chunks keyring_get_key_chunk() advances the read +pointer by level * sizeof(long) past the inline prefix but only +bounds-checks the prefix, so a long enough key description is read past +its kmemdup(desc, desc_len + 1) allocation. Compute the full byte +offset and bounds-check the description against it before reading. + +The walk only reaches a description-level chunk when two keys collide +through the hash, x, type and domain_tag chunks, so this is reached from +an unprivileged add_key(2) with a crafted pair of same-type keys whose +index hashes collide; KASAN reports a slab-out-of-bounds read. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index e105349794f23..b9ca9dc9b0c3f 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + unsigned long chunk = 0; + const u8 *d; + int desc_len = index_key->desc_len, n = sizeof(chunk); ++ unsigned int offset; + + level /= ASSOC_ARRAY_KEY_CHUNK_SIZE; + switch (level) { +@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + return (unsigned long)index_key->domain_tag; + default: + level -= 4; +- if (desc_len <= sizeof(index_key->desc)) ++ offset = sizeof(index_key->desc) + level * sizeof(long); ++ if (desc_len <= offset) + return 0; + +- d = index_key->description + sizeof(index_key->desc); +- d += level * sizeof(long); +- desc_len -= sizeof(index_key->desc); ++ d = index_key->description + offset; ++ desc_len -= offset; + if (desc_len > n) + desc_len = n; + do { +-- +2.53.0 + diff --git a/queue-6.1/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-6.1/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch new file mode 100644 index 0000000000..9f353085be --- /dev/null +++ b/queue-6.1/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch @@ -0,0 +1,63 @@ +From 5937f29f40bd1e2f1f15fe88ef645138aadc9dc5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:04 -0400 +Subject: keys: make keyring key-chunk byte order agree with + keyring_diff_objects() + +From: Michael Bommarito + +[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ] + +keyring_get_key_chunk() loads description bytes into the index chunk low +address first, while keyring_diff_objects() numbers the first differing +bit from the low end and folds the absolute byte index into the level +without removing the inline-prefix offset the level already carries. +The two disagree on byte order and bit position, so the array can be +told two keys first differ at a bit that does not differ in the chunk +the walker uses, letting crafted descriptions collide into one node. + +Load the chunk in the order keyring_diff_objects() assumes and drop the +inline-prefix length when folding the byte index into the level. This +only changes the in-memory ordering used to place keys within a keyring; +add, search and read of non-colliding keys are unaffected. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index b9ca9dc9b0c3f..fd95a0eb7a466 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + desc_len -= offset; + if (desc_len > n) + desc_len = n; ++ d += desc_len; + do { + chunk <<= 8; +- chunk |= *d++; ++ chunk |= *--d; + } while (--desc_len > 0); + return chunk; + } +@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data) + return -1; + + differ_plus_i: +- level += i; ++ level += i - (int)sizeof(a->desc); + differ: + i = level * 8 + __ffs(seg_a ^ seg_b); + return i; +-- +2.53.0 + diff --git a/queue-6.1/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch b/queue-6.1/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch new file mode 100644 index 0000000000..d9faf822be --- /dev/null +++ b/queue-6.1/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch @@ -0,0 +1,50 @@ +From a47fb4926d7f4ff507423b99d8754adb3b635dac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 10:04:19 +0900 +Subject: ksmbd: fix use-after-free in __close_file_table_ids() + +From: Namjae Jeon + +[ Upstream commit e7188199eff46a636f3436356f0aae039be6dd66 ] + +A ksmbd_file can remain alive after logical close while another session +holds a temporary reference obtained through ksmbd_lookup_fd_inode(). +ksmbd_close_fd() currently marks the file closed and drops the idr-owned +reference, but leaves the pointer published in the closing session's idr +until the final reference is dropped. + +If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final() +supplies the foreign session's file table to __ksmbd_close_fd(). The object +is then freed without being removed from its owner's idr, and the owner +session later dereferences the stale pointer during file-table teardown. + +Remove the volatile id from the owner's idr while ksmbd_close_fd() still +holds that table's lock, and clear volatile_id before dropping +the idr-owned reference. A later foreign final put then only performs +physical destruction and cannot remove the object from the wrong table. + +Fixes: 8510a043d334 ("ksmbd: increment reference count of parent fp") +Reported-by: Yunseong Kim +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs_cache.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index e67b055b8eb3b..5b3a55bd700dc 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -409,6 +409,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + fp = NULL; + else { + fp->f_state = FP_CLOSED; ++ idr_remove(ft->idr, id); ++ fp->volatile_id = KSMBD_NO_FID; + closed = true; + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; +-- +2.53.0 + diff --git a/queue-6.1/ksmbd-return-success-for-deferred-final-close.patch b/queue-6.1/ksmbd-return-success-for-deferred-final-close.patch new file mode 100644 index 0000000000..9cd62c129c --- /dev/null +++ b/queue-6.1/ksmbd-return-success-for-deferred-final-close.patch @@ -0,0 +1,64 @@ +From fc95f0677f0a5750e3392bbd78e0bc9295f60e8e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 21 Jun 2026 19:41:08 +0900 +Subject: ksmbd: return success for deferred final close + +From: Namjae Jeon + +[ Upstream commit c5db4de8988f1a621556ca5c4537f77b766ca07d ] + +ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table +reference. If another in-flight request still holds a reference, the final +close is deferred until that request drops its reference. + +The function currently returns -EINVAL in that deferred-final-close case +because fp is cleared when the reference count does not reach zero. That +turns a valid close into STATUS_FILE_CLOSED. + +smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then +closes the same directory handle before receiving the find response. +The query holds a reference while it builds the response, so close must +mark the handle closed and return success even though final teardown is +delayed. Track whether the handle was successfully transitioned to +FP_CLOSED and return success when only the final close is deferred. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: e7188199eff4 ("ksmbd: fix use-after-free in __close_file_table_ids()") +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs_cache.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index 4911d1e325cd2..e67b055b8eb3b 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -394,6 +394,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + { + struct ksmbd_file *fp; + struct ksmbd_file_table *ft; ++ bool closed = false; + + if (!has_file_id(id)) + return 0; +@@ -408,6 +409,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + fp = NULL; + else { + fp->f_state = FP_CLOSED; ++ closed = true; + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; + } +@@ -415,7 +417,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + write_unlock(&ft->lock); + + if (!fp) +- return -EINVAL; ++ return closed ? 0 : -EINVAL; + + __put_fd_final(work, fp); + return 0; +-- +2.53.0 + diff --git a/queue-6.1/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch b/queue-6.1/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch new file mode 100644 index 0000000000..5980f592ae --- /dev/null +++ b/queue-6.1/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch @@ -0,0 +1,40 @@ +From 075661608f82c79d4a9c49cf43d861e6f14e06ab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 06:26:05 +0000 +Subject: net: bridge: mrp: fix Option TLV length in MRP_Test frames + +From: David Corvaglia + +[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ] + +oui is a pointer, so sizeof(oui) is the pointer size. The MRA +Option TLV thus advertises a wrong length (15 vs 10 on x86_64), +causing misparsing of the frame on peers. Fix is to replace +with sizeof(*oui). + +Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA") +Signed-off-by: David Corvaglia +Acked-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_mrp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c +index fd2de35ffb3cf..5fd22bb4f5b60 100644 +--- a/net/bridge/br_mrp.c ++++ b/net/bridge/br_mrp.c +@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp, + struct br_mrp_oui_hdr *oui = NULL; + u8 length; + +- length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) + ++ length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) + + MRP_OPT_PADDING; + br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length); + +-- +2.53.0 + diff --git a/queue-6.1/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch b/queue-6.1/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch new file mode 100644 index 0000000000..ab4372f188 --- /dev/null +++ b/queue-6.1/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch @@ -0,0 +1,78 @@ +From 2b3de6306e9168671a469977eb0c68eac43422bc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 07:29:01 +0000 +Subject: net: do not send ICMP/NDISC Redirects when peer allocation fails + +From: Eric Dumazet + +[ Upstream commit dbc3791e3b2472e1ccc08947e0f83b443470ff4f ] + +When inet_getpeer_v4() or inet_getpeer_v6() fails to allocate a peer entry +under memory pressure or tree size caps, redirect handlers previously fell +back to sending un-rate-limited ICMP/NDISC Redirect messages. + +In IPv4, ip_rt_send_redirect() called icmp_send() directly when peer == NULL. +In IPv6, ip6_forward() and ndisc_send_redirect() passed a NULL peer into +inet_peer_xrlim_allow(), which returned true when peer == NULL. + +Because ICMP/NDISC Redirects are not part of the default global rate limit +mask (sysctl_icmp_ratemask), sending redirects when peer == NULL creates +an un-rate-limited ICMP packet storm. + +Fix this by failing closed in ip_rt_send_redirect(), ip6_forward(), and +ndisc_send_redirect() when peer is NULL. + +Fixes: 92d868292634 ("inetpeer: Move ICMP rate limiting state into inet_peer entries.") +Signed-off-by: Eric Dumazet +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260724072901.1633601-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/route.c | 2 -- + net/ipv6/ip6_output.c | 2 +- + net/ipv6/ndisc.c | 2 ++ + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/net/ipv4/route.c b/net/ipv4/route.c +index 60516c6ae62e0..f9d2d4268c616 100644 +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -902,8 +902,6 @@ void ip_rt_send_redirect(struct sk_buff *skb) + peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif); + if (!peer) { + rcu_read_unlock(); +- icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, +- rt_nexthop(rt, ip_hdr(skb)->daddr)); + return; + } + +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index ad821d362656f..93e55ab7e0f6d 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -623,7 +623,7 @@ int ip6_forward(struct sk_buff *skb) + /* Limit redirects both by destination (here) + and by source (inside ndisc_send_redirect) + */ +- if (inet_peer_xrlim_allow(peer, 1*HZ)) ++ if (peer && inet_peer_xrlim_allow(peer, 1*HZ)) + ndisc_send_redirect(skb, target); + rcu_read_unlock(); + } else { +diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c +index a53d2a6a99f85..585a9135cf267 100644 +--- a/net/ipv6/ndisc.c ++++ b/net/ipv6/ndisc.c +@@ -1727,6 +1727,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) + + rcu_read_lock(); + peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr); ++ if (!peer) ++ goto release; + ret = inet_peer_xrlim_allow(peer, 1*HZ); + rcu_read_unlock(); + +-- +2.53.0 + diff --git a/queue-6.1/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-6.1/net-phylink-put-link_gpio-if-phylink_create-fails.patch new file mode 100644 index 0000000000..0ceed4079e --- /dev/null +++ b/queue-6.1/net-phylink-put-link_gpio-if-phylink_create-fails.patch @@ -0,0 +1,92 @@ +From 7fc42cd434f53cc61615a2148211f01557ad874f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 17:08:05 +0200 +Subject: net: phylink: put link_gpio if phylink_create fails + +From: Christian Marangi + +[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ] + +In phylink_create() if phylink_register_sfp() returns an error, link_gpio +obtained by phylink_parse_fixedlink() is never released. While this is a +very unlikely scenario, it's worth to fix/handle this. + +This was present from the very first implementation of phylink but got +relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to +bridge between network devices and sfp cages") where additional function +were added after phylink_parse_fixedlink() making the release of link_gpio +needed if such additional function errored out. + +While at it, restructure the exit condition of phylink_create() with the +goto pattern to reduce code duplication on handling error conditions. + +Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages") +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/phy/phylink.c | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 3069a7df25d3f..8109a049a74e4 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1483,8 +1483,8 @@ struct phylink *phylink_create(struct phylink_config *config, + } else if (config->type == PHYLINK_DEV) { + pl->dev = config->dev; + } else { +- kfree(pl); +- return ERR_PTR(-EINVAL); ++ ret = -EINVAL; ++ goto free_pl; + } + + pl->using_mac_select_pcs = using_mac_select_pcs; +@@ -1508,28 +1508,29 @@ struct phylink *phylink_create(struct phylink_config *config, + phylink_validate(pl, pl->supported, &pl->link_config); + + ret = phylink_parse_mode(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto free_pl; + + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { + ret = phylink_parse_fixedlink(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + } + + pl->cur_link_an_mode = pl->cfg_link_an_mode; + + ret = phylink_register_sfp(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + + return pl; ++ ++release_link_gpio: ++ if (pl->link_gpio) ++ gpiod_put(pl->link_gpio); ++free_pl: ++ kfree(pl); ++ return ERR_PTR(ret); + } + EXPORT_SYMBOL_GPL(phylink_create); + +-- +2.53.0 + diff --git a/queue-6.1/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-6.1/net-sxgbe-check-descriptor-ring-allocation-failures.patch new file mode 100644 index 0000000000..c58b458df0 --- /dev/null +++ b/queue-6.1/net-sxgbe-check-descriptor-ring-allocation-failures.patch @@ -0,0 +1,48 @@ +From 07febae2ed86e0bea36138a05cd45ad88584f4e2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:20 +0800 +Subject: net: sxgbe: check descriptor ring allocation failures + +From: Chenguang Zhao + +[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ] + +sxgbe_open() ignores the return value of init_dma_desc_rings() and +continues to program DMA with invalid ring addresses when allocation +fails. Check the return value and disconnect the PHY on failure. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index 2801526f75962..342faeb6c8afa 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -1080,7 +1080,9 @@ static int sxgbe_open(struct net_device *dev) + priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE); + priv->tx_tc = TC_DEFAULT; + priv->rx_tc = TC_DEFAULT; +- init_dma_desc_rings(dev); ++ ret = init_dma_desc_rings(dev); ++ if (ret) ++ goto init_phy_error; + + /* DMA initialization and SW reset */ + ret = sxgbe_init_dma_engine(priv); +@@ -1189,6 +1191,7 @@ static int sxgbe_open(struct net_device *dev) + + init_error: + free_dma_desc_resources(priv); ++init_phy_error: + if (dev->phydev) + phy_disconnect(dev->phydev); + phy_error: +-- +2.53.0 + diff --git a/queue-6.1/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-6.1/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch new file mode 100644 index 0000000000..96d6713549 --- /dev/null +++ b/queue-6.1/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch @@ -0,0 +1,50 @@ +From bb1a3b7c01a6cfcb026a7cad494248af422e9b13 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:19 +0800 +Subject: net: sxgbe: free TX rings on RX allocation failure + +From: Chenguang Zhao + +[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ] + +When RX descriptor ring allocation fails, init_dma_desc_rings() only +frees the partially allocated RX rings and returns. The TX rings that +were allocated earlier in the same function are leaked. + +Rearrange error labels to clean up TX rings upon RX failures. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index f6f99712d562e..2801526f75962 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -599,14 +599,13 @@ static int init_dma_desc_rings(struct net_device *netd) + + return 0; + +-txalloc_err: +- while (queue_num--) +- free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); +- return ret; +- + rxalloc_err: + while (queue_num--) + free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize); ++ queue_num = SXGBE_TX_QUEUES; ++txalloc_err: ++ while (queue_num--) ++ free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.1/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-6.1/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch new file mode 100644 index 0000000000..2c7b7dfd6e --- /dev/null +++ b/queue-6.1/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch @@ -0,0 +1,96 @@ +From 071b0458e8569c694fa65a278994bd1983920b6d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 16:42:01 -0700 +Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in + sip_help_tcp() + +From: Xiang Mei + +[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ] + +sip_help_tcp() stores the size change of each NAT-rewritten SIP message +in s16 diff and accumulates it in s16 tdiff, but a single message can +grow by more than S16_MAX while the packet stays under the 65535 +enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long +Contact list expands the message by tens of kilobytes. diff then wraps, +and "datalen = datalen + diff - msglen" yields a huge unsigned datalen, +so the next iteration's ct_sip_get_header() reads past the linearized skb +tail. + +Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the +65535 byte packet limit, and the seqadj core is already s32 +(nf_ct_seqadj_set() takes s32), so no previously accepted input is +rejected. + + BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25 + ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694) + nf_confirm (net/netfilter/nf_conntrack_proto.c:183) + nf_hook_slow (net/netfilter/core.c:619) + ip6_output (net/ipv6/ip6_output.c:246) + ip6_forward (net/ipv6/ip6_output.c:690) + ipv6_rcv (net/ipv6/ip6_input.c:351) + __netif_receive_skb_one_core (net/core/dev.c:6212) + process_backlog (net/core/dev.c:6676) + __napi_poll (net/core/dev.c:7735) + net_rx_action (net/core/dev.c:7955) + handle_softirqs (kernel/softirq.c:622) + run_ksoftirqd (kernel/softirq.c:1076) + ... + +Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support") +Reported-by: Weiming Shi +Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Xiang Mei +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/nf_conntrack_sip.h | 2 +- + net/netfilter/nf_conntrack_sip.c | 2 +- + net/netfilter/nf_nat_sip.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h +index dbc614dfe0d56..aafa0c04f917e 100644 +--- a/include/linux/netfilter/nf_conntrack_sip.h ++++ b/include/linux/netfilter/nf_conntrack_sip.h +@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks { + unsigned int *datalen); + + void (*seq_adjust)(struct sk_buff *skb, +- unsigned int protoff, s16 off); ++ unsigned int protoff, s32 off); + + unsigned int (*expect)(struct sk_buff *skb, + unsigned int protoff, +diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c +index 0af6ede4b92c1..6e8ad849d14c4 100644 +--- a/net/netfilter/nf_conntrack_sip.c ++++ b/net/netfilter/nf_conntrack_sip.c +@@ -1627,7 +1627,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, + unsigned int matchoff, matchlen; + unsigned int msglen, origlen; + const char *dptr, *end; +- s16 diff, tdiff = 0; ++ s32 diff, tdiff = 0; + int ret = NF_ACCEPT; + unsigned long clen; + bool term; +diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c +index f7be30c69b5c8..a1c41defaf22d 100644 +--- a/net/netfilter/nf_nat_sip.c ++++ b/net/netfilter/nf_nat_sip.c +@@ -315,7 +315,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, + } + + static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, +- s16 off) ++ s32 off) + { + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); +-- +2.53.0 + diff --git a/queue-6.1/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-6.1/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch new file mode 100644 index 0000000000..94f2604035 --- /dev/null +++ b/queue-6.1/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch @@ -0,0 +1,69 @@ +From 16b83b2d8326665e16b9f81f03cd6f0564d7b99a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:15:23 +0000 +Subject: netfilter: nft_payload: fix mask build for partial field offload + +From: Xiang Mei (Microsoft) + +[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ] + +nft_payload_offload_mask() builds the offload match mask for a payload +expression that covers only part of a header field. For a partial IPv6 +address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which +is undefined on the 32-bit int operand. It also trims only one word, so +the remaining words stay 0xffffffff (and when priv_len is a multiple of 4 +the trim is skipped entirely), leaving the mask covering more bytes than +the rule matches. + + UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20 + shift exponent 120 is too large for 32-bit type 'int' + ... + +The match is byte-granular and struct nft_data is zero-initialised, so the +correct mask is simply the first priv_len bytes set to 0xff. Set those +bytes directly and drop the word/shift trimming; this removes the undefined +shift and no longer over-masks the trailing bytes. + +Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes") +Reported-by: AutonomousCodeSecurity@microsoft.com +Signed-off-by: Xiang Mei (Microsoft) +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/nft_payload.c | 12 +----------- + 1 file changed, 1 insertion(+), 11 deletions(-) + +diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c +index ae3277424b839..2a58a81ed13cf 100644 +--- a/net/netfilter/nft_payload.c ++++ b/net/netfilter/nft_payload.c +@@ -241,9 +241,7 @@ static bool nft_payload_reduce(struct nft_regs_track *track, + static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + u32 priv_len, u32 field_len) + { +- unsigned int remainder, delta, k; + struct nft_data mask = {}; +- __be32 remainder_mask; + + if (priv_len == field_len) { + memset(®->mask, 0xff, priv_len); +@@ -252,15 +250,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + return false; + } + +- memset(&mask, 0xff, field_len); +- remainder = priv_len % sizeof(u32); +- if (remainder) { +- k = priv_len / sizeof(u32); +- delta = field_len - priv_len; +- remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1)); +- mask.data[k] = (__force u32)remainder_mask; +- } +- ++ memset(&mask, 0xff, priv_len); + memcpy(®->mask, &mask, field_len); + + return true; +-- +2.53.0 + diff --git a/queue-6.1/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-6.1/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch new file mode 100644 index 0000000000..0e1f209cf8 --- /dev/null +++ b/queue-6.1/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch @@ -0,0 +1,83 @@ +From 8858755ef9de2bc34fb26103d888deee5a5cab04 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 22:02:46 +0200 +Subject: netfilter: xt_hashlimit: validate hashtable supports + XT_HASHLIMIT_RATE_MATCH + +From: Pablo Neira Ayuso + +[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ] + +The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the +dsthash_ent structure which represents an entry in the hashtable. There +is a union area which uses a different layout to express the rate match +mode. + +Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode +flag is requested by two or more different rules that refer to the same +hashtable. Otherwise, uninitialized access to the burst field in the +union is possible. + +Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by +revision less than 3 too. + +Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode") +Reported-and-tested-by: Talha Berk Arslan +Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/ +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/xt_hashlimit.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c +index 0859b8f767645..61813010cd319 100644 +--- a/net/netfilter/xt_hashlimit.c ++++ b/net/netfilter/xt_hashlimit.c +@@ -118,6 +118,7 @@ struct xt_hashlimit_htable { + refcount_t use; + u_int8_t family; + bool rnd_initialized; ++ bool ratematch; + + struct hashlimit_cfg3 cfg; /* config */ + +@@ -325,6 +326,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg, + vfree(hinfo); + return -ENOMEM; + } ++ hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH); + spin_lock_init(&hinfo->lock); + + switch (revision) { +@@ -868,7 +870,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + } + + /* Check for overflow. */ +- if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (revision < 3) ++ return -EINVAL; ++ + if (cfg->avg == 0 || cfg->avg > U32_MAX) { + pr_info_ratelimited("invalid rate\n"); + return -ERANGE; +@@ -901,6 +906,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + mutex_unlock(&hashlimit_mutex); + return ret; + } ++ } else { ++ if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH && ++ !(*hinfo)->ratematch) || ++ (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) && ++ (*hinfo)->ratematch)) { ++ mutex_unlock(&hashlimit_mutex); ++ htable_put(*hinfo); ++ return -EINVAL; ++ } + } + mutex_unlock(&hashlimit_mutex); + +-- +2.53.0 + diff --git a/queue-6.1/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-6.1/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch new file mode 100644 index 0000000000..961ec35154 --- /dev/null +++ b/queue-6.1/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch @@ -0,0 +1,42 @@ +From 63d9716c8eb67fc385f672b5042ecbc5e5086da7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 12:58:31 +0530 +Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop + +From: Suman Ghosh + +[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ] + +During link down event, we were doing netif_tx_stop_all_queues() first +and then netif_carrier_off(). This can cause a potential race since +carrier is still on during down event. This patch reverse the calling +order to fix the issue. + +Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications") +Signed-off-by: Suman Ghosh +Signed-off-by: Ratheesh Kannoth +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +index 49f21c7f5c1fd..187d0a71e64ad 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +@@ -842,8 +842,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf) + netif_carrier_on(netdev); + netif_tx_start_all_queues(netdev); + } else { +- netif_tx_stop_all_queues(netdev); + netif_carrier_off(netdev); ++ netif_tx_stop_all_queues(netdev); + } + } + +-- +2.53.0 + diff --git a/queue-6.1/phy-zynqmp-allow-variation-in-refclk-rate.patch b/queue-6.1/phy-zynqmp-allow-variation-in-refclk-rate.patch new file mode 100644 index 0000000000..75bed41412 --- /dev/null +++ b/queue-6.1/phy-zynqmp-allow-variation-in-refclk-rate.patch @@ -0,0 +1,41 @@ +From 455c546f2c09cd36fc21118820c90b97ac315785 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 11 Jul 2023 15:45:39 -0400 +Subject: phy: zynqmp: Allow variation in refclk rate + +From: Sean Anderson + +[ Upstream commit 76009ee76e05e30e29aade02e788aebe9ce9ffd2 ] + +Due to limited available frequency ratios, the reference clock rate may +not be exactly the same as the required rate. Allow a small (100 ppm) +deviation. + +Signed-off-by: Sean Anderson +Link: https://lore.kernel.org/r/20230711194542.898230-1-sean.anderson@seco.com +Signed-off-by: Vinod Koul +Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()") +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index cc36fb7616ae4..00ceeca2a395f 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -958,7 +958,10 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) + rate = clk_get_rate(clk); + + for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { +- if (rate == ssc_lookup[i].refclk_rate) { ++ /* Allow an error of 100 ppm */ ++ unsigned long error = ssc_lookup[i].refclk_rate / 10000; ++ ++ if (abs(rate - ssc_lookup[i].refclk_rate) < error) { + gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i]; + break; + } +-- +2.53.0 + diff --git a/queue-6.1/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch b/queue-6.1/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch new file mode 100644 index 0000000000..631b285ab3 --- /dev/null +++ b/queue-6.1/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch @@ -0,0 +1,76 @@ +From 97b964f5294762ee02e4fbfa9242f3f52531be62 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 21:08:30 +0530 +Subject: phy: zynqmp: fix clock error handling in xpsgtr_phy_init() + +From: Radhey Shyam Pandey + +[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ] + +Propagate clk_prepare_enable() failures to the caller instead of +returning success, and disable the reference clock on initialization +error paths to avoid leaking clock references when phy_exit() is not +called. + +Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save") +Signed-off-by: Radhey Shyam Pandey +Reviewed-by: Michal Simek +Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index 61dc831f778dd..517855fea8920 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -644,12 +644,13 @@ static int xpsgtr_phy_init(struct phy *phy) + { + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; +- int ret = 0; ++ int ret; + + mutex_lock(>r_dev->gtr_mutex); + + /* Configure and enable the clock when peripheral phy_init call */ +- if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk])) ++ ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]); ++ if (ret) + goto out; + + /* Skip initialization if not required. */ +@@ -659,7 +660,7 @@ static int xpsgtr_phy_init(struct phy *phy) + if (gtr_dev->tx_term_fix) { + ret = xpsgtr_phy_tx_term_fix(gtr_phy); + if (ret < 0) +- goto out; ++ goto out_disable_clk; + + gtr_dev->tx_term_fix = false; + } +@@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy) + */ + ret = xpsgtr_configure_pll(gtr_phy); + if (ret) +- goto out; ++ goto out_disable_clk; + + xpsgtr_lane_set_protocol(gtr_phy); + +@@ -691,6 +692,10 @@ static int xpsgtr_phy_init(struct phy *phy) + break; + } + ++ goto out; ++ ++out_disable_clk: ++ clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]); + out: + mutex_unlock(>r_dev->gtr_mutex); + return ret; +-- +2.53.0 + diff --git a/queue-6.1/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch b/queue-6.1/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch new file mode 100644 index 0000000000..cb5b5a8ed7 --- /dev/null +++ b/queue-6.1/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch @@ -0,0 +1,56 @@ +From ac05064893ce2ff7729ba5aefd7b33257ddcb67a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 21:08:31 +0530 +Subject: phy: zynqmp: fix runtime PM leak on probe allocation failure + +From: Radhey Shyam Pandey + +[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ] + +Allocate saved_regs before pm_runtime_resume_and_get() so a +devm_kmalloc() failure does not leave an unreleased runtime PM usage +counter. + +Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume") +Signed-off-by: Radhey Shyam Pandey +Reviewed-by: Michal Simek +Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index 517855fea8920..f1c2006dda602 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -1046,6 +1046,12 @@ static int xpsgtr_probe(struct platform_device *pdev) + return PTR_ERR(provider); + } + ++ gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, ++ sizeof(save_reg_address), ++ GFP_KERNEL); ++ if (!gtr_dev->saved_regs) ++ return -ENOMEM; ++ + pm_runtime_set_active(gtr_dev->dev); + pm_runtime_enable(gtr_dev->dev); + +@@ -1055,12 +1061,6 @@ static int xpsgtr_probe(struct platform_device *pdev) + return ret; + } + +- gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, +- sizeof(save_reg_address), +- GFP_KERNEL); +- if (!gtr_dev->saved_regs) +- return -ENOMEM; +- + return 0; + } + +-- +2.53.0 + diff --git a/queue-6.1/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch b/queue-6.1/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch new file mode 100644 index 0000000000..ec8444fdbe --- /dev/null +++ b/queue-6.1/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch @@ -0,0 +1,172 @@ +From cfa67e135a4ee1eaf20861665de5b8f52d1b7969 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 28 Apr 2025 08:35:47 +0200 +Subject: phy-zynqmp: Postpone getting clock rate until actually needed + +From: Mike Looijmans + +[ Upstream commit 065d5885f6180c534b7b176847b3e008f4e11850 ] + +At probe time the driver would display the following error and abort: + xilinx-psgtr fd400000.phy: Invalid rate 0 for reference clock 0 + +At probe time, the associated GTR driver (e.g. SATA or PCIe) hasn't +initialized the clock yet, so clk_get_rate() likely returns 0 if the clock +is programmable. So this driver only works if the clock is fixed. + +The PHY driver doesn't need to know the clock frequency at probe yet, so +wait until the associated driver initializes the lane before requesting the +clock rate setting. + +In addition to allowing the driver to be used with programmable clocks, +this also reduces the driver's runtime memory footprint by removing an +array of pointers from struct xpsgtr_phy. + +Signed-off-by: Mike Looijmans +Acked-by: Michal Simek +Link: https://lore.kernel.org/r/20250428063648.22034-1-mike.looijmans@topic.nl +Signed-off-by: Vinod Koul +Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()") +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 70 +++++++++++++++++---------------- + 1 file changed, 37 insertions(+), 33 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index 00ceeca2a395f..61dc831f778dd 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -228,7 +228,6 @@ struct xpsgtr_phy { + * @siou: siou base address + * @gtr_mutex: mutex for locking + * @phys: PHY lanes +- * @refclk_sscs: spread spectrum settings for the reference clocks + * @clk: reference clocks + * @tx_term_fix: fix for GT issue + * @saved_icm_cfg0: stored value of ICM CFG0 register +@@ -241,7 +240,6 @@ struct xpsgtr_dev { + void __iomem *siou; + struct mutex gtr_mutex; /* mutex for locking */ + struct xpsgtr_phy phys[NUM_LANES]; +- const struct xpsgtr_ssc *refclk_sscs[NUM_LANES]; + struct clk *clk[NUM_LANES]; + bool tx_term_fix; + unsigned int saved_icm_cfg0; +@@ -384,13 +382,40 @@ static int xpsgtr_wait_pll_lock(struct phy *phy) + return ret; + } + ++/* Get the spread spectrum (SSC) settings for the reference clock rate */ ++static const struct xpsgtr_ssc *xpsgtr_find_sscs(struct xpsgtr_phy *gtr_phy) ++{ ++ unsigned long rate; ++ struct clk *clk; ++ unsigned int i; ++ ++ clk = gtr_phy->dev->clk[gtr_phy->refclk]; ++ rate = clk_get_rate(clk); ++ ++ for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { ++ /* Allow an error of 100 ppm */ ++ unsigned long error = ssc_lookup[i].refclk_rate / 10000; ++ ++ if (abs(rate - ssc_lookup[i].refclk_rate) < error) ++ return &ssc_lookup[i]; ++ } ++ ++ dev_err(gtr_phy->dev->dev, "Invalid rate %lu for reference clock %u\n", ++ rate, gtr_phy->refclk); ++ ++ return NULL; ++} ++ + /* Configure PLL and spread-sprectrum clock. */ +-static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) ++static int xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) + { + const struct xpsgtr_ssc *ssc; + u32 step_size; + +- ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk]; ++ ssc = xpsgtr_find_sscs(gtr_phy); ++ if (!ssc) ++ return -EINVAL; ++ + step_size = ssc->step_size; + + xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane), +@@ -432,6 +457,8 @@ static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB, + STEP_SIZE_3_MASK, (step_size & STEP_SIZE_3_MASK) | + FORCE_STEP_SIZE | FORCE_STEPS); ++ ++ return 0; + } + + /* Configure the lane protocol. */ +@@ -644,7 +671,10 @@ static int xpsgtr_phy_init(struct phy *phy) + * Configure the PLL, the lane protocol, and perform protocol-specific + * initialization. + */ +- xpsgtr_configure_pll(gtr_phy); ++ ret = xpsgtr_configure_pll(gtr_phy); ++ if (ret) ++ goto out; ++ + xpsgtr_lane_set_protocol(gtr_phy); + + switch (gtr_phy->protocol) { +@@ -855,8 +885,7 @@ static struct phy *xpsgtr_xlate(struct device *dev, + } + + refclk = args->args[3]; +- if (refclk >= ARRAY_SIZE(gtr_dev->refclk_sscs) || +- !gtr_dev->refclk_sscs[refclk]) { ++ if (refclk >= ARRAY_SIZE(gtr_dev->clk)) { + dev_err(dev, "Invalid reference clock number %u\n", refclk); + return ERR_PTR(-EINVAL); + } +@@ -932,9 +961,7 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) + { + unsigned int refclk; + +- for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) { +- unsigned long rate; +- unsigned int i; ++ for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->clk); ++refclk) { + struct clk *clk; + char name[8]; + +@@ -950,29 +977,6 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) + continue; + + gtr_dev->clk[refclk] = clk; +- +- /* +- * Get the spread spectrum (SSC) settings for the reference +- * clock rate. +- */ +- rate = clk_get_rate(clk); +- +- for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { +- /* Allow an error of 100 ppm */ +- unsigned long error = ssc_lookup[i].refclk_rate / 10000; +- +- if (abs(rate - ssc_lookup[i].refclk_rate) < error) { +- gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i]; +- break; +- } +- } +- +- if (i == ARRAY_SIZE(ssc_lookup)) { +- dev_err(gtr_dev->dev, +- "Invalid rate %lu for reference clock %u\n", +- rate, refclk); +- return -EINVAL; +- } + } + + return 0; +-- +2.53.0 + diff --git a/queue-6.1/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch b/queue-6.1/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch new file mode 100644 index 0000000000..c57fa94d5b --- /dev/null +++ b/queue-6.1/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch @@ -0,0 +1,58 @@ +From 697fe758fd37a93960926189a9002e32ae6401db Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 11:28:44 -0500 +Subject: pinctrl-amd: Don't clear S4 wake bits at probe + +From: Mario Limonciello + +[ Upstream commit ffe8a0c6b55285ceaf2f42fc20c3a0594d14f1e9 ] + +commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") +introduced a regression where Wake-on-LAN no longer works after suspend +or shutdown on some AMD platforms. + +Firmware-programmed S4 wake bits for devices like PCIe NICs using PCI +PME are cleared at probe, but nothing restores them. Unlike S0i3/S3 wake +sources that use enable_irq_wake() -> amd_gpio_irq_set_wake(), PCIe PME +does not use GPIO IRQ infrastructure and relies on firmware configuration. + +The original intent of commit 6bc3462a0f5e ("pinctrl: amd: Mask wake +bits on probe again") was to clear spurious wake bits left by firmware +to prevent unwanted wakeups. However, S4 wake bits are used for +hardware-level wake sources like WoL that bypass the kernel's IRQ wake +API. + +Fix by preserving S4 wake bits at probe and only clearing S0i3/S3 bits: +- Firmware-configured S4 wake sources (WoL) continue working +- Kernel maintains control of S3/S0i3 wake policy via set_wake() +- S3-only wake sources work correctly per commit f31f33dbb3ba ("pinctrl: + amd: Take suspend type into consideration which pins are non-wake") + +The trade-off is that firmware-programmed spurious S4 wake bits remain +set, but this is less problematic than breaking WoL. + +Fixes: 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") +Signed-off-by: Mario Limonciello +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/pinctrl-amd.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c +index 2b6d996e393e0..51506492302ef 100644 +--- a/drivers/pinctrl/pinctrl-amd.c ++++ b/drivers/pinctrl/pinctrl-amd.c +@@ -869,8 +869,7 @@ static void amd_gpio_irq_init(struct amd_gpio *gpio_dev) + u32 pin_reg, mask; + int i; + +- mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) | +- BIT(WAKE_CNTRL_OFF_S4); ++ mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3); + + for (i = 0; i < desc->npins; i++) { + int pin = desc->pins[i].number; +-- +2.53.0 + diff --git a/queue-6.1/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch b/queue-6.1/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch new file mode 100644 index 0000000000..11f574757d --- /dev/null +++ b/queue-6.1/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch @@ -0,0 +1,58 @@ +From c4342884dff8c3f1ebd7a909c1d35c00381b5631 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 15:08:05 +0200 +Subject: pinctrl: qcom: sc8280xp: Add missing wakeup entries for GPIO143/151 + +From: Konrad Dybcio + +[ Upstream commit 437a8d2aa1aa442c4a176fdf4700a9b3bb0c8794 ] + +Pins 143 and 151 were not included in the PDC wakeup map. They are +normally used for PCIe2A and PCIe3a PERST# respectively, so they're +unlikely to be excercised in practice, but still add them for the sake +of completeness. + +Fixes: c0e4c71a9e7c ("pinctrl: qcom: Introduce sc8280xp TLMM driver") +Signed-off-by: Konrad Dybcio +Link: https://patch.msgid.link/20260626-topic-8280_pinctrl_wakeup-v1-1-2ccb267148f5@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-sc8280xp.c | 21 +++++++++++---------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +index e96c00686a25b..aeeb40f4aa3da 100644 +--- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c ++++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +@@ -1892,16 +1892,17 @@ static const struct msm_gpio_wakeirq_map sc8280xp_pdc_map[] = { + { 126, 200 }, { 127, 225 }, { 128, 262 }, { 129, 201 }, + { 130, 209 }, { 131, 173 }, { 132, 202 }, { 136, 210 }, + { 138, 171 }, { 139, 226 }, { 140, 227 }, { 142, 228 }, +- { 144, 229 }, { 145, 230 }, { 146, 231 }, { 148, 232 }, +- { 149, 233 }, { 150, 234 }, { 152, 235 }, { 154, 212 }, +- { 157, 213 }, { 161, 219 }, { 170, 236 }, { 171, 221 }, +- { 174, 222 }, { 175, 237 }, { 176, 223 }, { 177, 170 }, +- { 180, 238 }, { 181, 239 }, { 182, 240 }, { 183, 241 }, +- { 184, 242 }, { 185, 243 }, { 190, 178 }, { 193, 184 }, +- { 196, 185 }, { 198, 186 }, { 200, 174 }, { 201, 175 }, +- { 205, 176 }, { 206, 177 }, { 208, 187 }, { 210, 198 }, +- { 211, 199 }, { 212, 204 }, { 215, 205 }, { 220, 188 }, +- { 221, 194 }, { 223, 195 }, { 225, 196 }, { 227, 197 }, ++ { 143, 261 }, { 144, 229 }, { 145, 230 }, { 146, 231 }, ++ { 148, 232 }, { 149, 233 }, { 150, 234 }, { 151, 264 }, ++ { 152, 235 }, { 154, 212 }, { 157, 213 }, { 161, 219 }, ++ { 170, 236 }, { 171, 221 }, { 174, 222 }, { 175, 237 }, ++ { 176, 223 }, { 177, 170 }, { 180, 238 }, { 181, 239 }, ++ { 182, 240 }, { 183, 241 }, { 184, 242 }, { 185, 243 }, ++ { 190, 178 }, { 193, 184 }, { 196, 185 }, { 198, 186 }, ++ { 200, 174 }, { 201, 175 }, { 205, 176 }, { 206, 177 }, ++ { 208, 187 }, { 210, 198 }, { 211, 199 }, { 212, 204 }, ++ { 215, 205 }, { 220, 188 }, { 221, 194 }, { 223, 195 }, ++ { 225, 196 }, { 227, 197 }, + }; + + static struct msm_pinctrl_soc_data sc8280xp_pinctrl = { +-- +2.53.0 + diff --git a/queue-6.1/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-6.1/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch new file mode 100644 index 0000000000..6049ce2986 --- /dev/null +++ b/queue-6.1/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch @@ -0,0 +1,38 @@ +From 9c70098a953b5f3355d66bfe0f37b71e089a86c7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:55 +0200 +Subject: powerpc/boot: Fix simpleboot CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/simpleboot.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c +index c80691d83880b..27591df41e9e8 100644 +--- a/arch/powerpc/boot/simpleboot.c ++++ b/arch/powerpc/boot/simpleboot.c +@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + /* finally, setup the timebase */ + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-6.1/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-6.1/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch new file mode 100644 index 0000000000..bf7978ed71 --- /dev/null +++ b/queue-6.1/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch @@ -0,0 +1,38 @@ +From 630f398a572bcdab90d5a72f43c5a9634443880d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:57 +0200 +Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-akebono.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c +index e3cc2599869cc..1b529037480fb 100644 +--- a/arch/powerpc/boot/treeboot-akebono.c ++++ b/arch/powerpc/boot/treeboot-akebono.c +@@ -146,7 +146,7 @@ void platform_init(char *userdata) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-6.1/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-6.1/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch new file mode 100644 index 0000000000..fdfbe7d28d --- /dev/null +++ b/queue-6.1/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch @@ -0,0 +1,38 @@ +From d7ea3104628ad040818c8948b094f3a2298e5c4d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:56 +0200 +Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-currituck.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c +index d53e8a592f816..5b5363b74f9f3 100644 +--- a/arch/powerpc/boot/treeboot-currituck.c ++++ b/arch/powerpc/boot/treeboot-currituck.c +@@ -102,7 +102,7 @@ void platform_init(void) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-6.1/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-6.1/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch new file mode 100644 index 0000000000..f9aca20e67 --- /dev/null +++ b/queue-6.1/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch @@ -0,0 +1,169 @@ +From 44cb067d2d174573cdccfbcfe91c7b94df589dc3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 12:43:11 +0200 +Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path + +From: Denis V. Lunev + +[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ] + +A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports +configured wedges the rtnetlink control plane of the whole machine: + + NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms + [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2! + [qede_recovery_handler:2665(ens6f0)]Starting a recovery process + +The recovery path deadlocks on the driver's own mutex: + + qede_sp_task + rtnl_lock() + mutex_lock(&edev->qede_lock) <- taken + qede_recovery_handler + qede_load + udp_tunnel_nic_reset_ntf + __udp_tunnel_nic_device_sync + info->sync_table == qede_udp_tunnel_sync + mutex_lock(&edev->qede_lock) <- same task: deadlock + +The mutex is not recursive, so the kworker blocks on itself with +rtnl_lock held, and neither lock is ever released. Every task that +calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6 +addrconf, sshd) blocks forever while the node still answers ping. +In a vmcore from an affected production node rtnl_mutex.owner +decodes to the very kworker blocked at the innermost mutex_lock() +above. + +Re-sync the tunnel ports from qede_sp_task() after the internal lock +is dropped, still under rtnl_lock as the udp_tunnel API requires. +This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf() +under rtnl without the internal lock. + +qede_recovery_handler() now returns whether it has successfully +reloaded an open device, and the caller re-syncs the ports only in +that case. This keeps the old gating exactly: a device that was down +or a failed recovery returns false, as those paths never reached the +udp_tunnel_nic_reset_ntf() call before either. + +This was the only user of the qede_lock()/qede_unlock() helpers, so +remove them. + +Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra") +Signed-off-by: Denis V. Lunev +CC: Andrew Lunn +CC: "David S. Miller" +CC: Eric Dumazet +CC: Jakub Kicinski +CC: Paolo Abeni +Reviewed-by: Jacob Keller +Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++---------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c +index 4bc950d366073..8148c16c851ea 100644 +--- a/drivers/net/ethernet/qlogic/qede/qede_main.c ++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c +@@ -108,7 +108,7 @@ static void qede_remove(struct pci_dev *pdev); + static void qede_shutdown(struct pci_dev *pdev); + static void qede_link_update(void *dev, struct qed_link_output *link); + static void qede_schedule_recovery_handler(void *dev); +-static void qede_recovery_handler(struct qede_dev *edev); ++static bool qede_recovery_handler(struct qede_dev *edev); + static void qede_schedule_hw_err_handler(void *dev, + enum qed_hw_err_type err_type); + static void qede_get_eth_tlv_data(void *edev, void *data); +@@ -1063,21 +1063,6 @@ void __qede_unlock(struct qede_dev *edev) + mutex_unlock(&edev->qede_lock); + } + +-/* This version of the lock should be used when acquiring the RTNL lock is also +- * needed in addition to the internal qede lock. +- */ +-static void qede_lock(struct qede_dev *edev) +-{ +- rtnl_lock(); +- __qede_lock(edev); +-} +- +-static void qede_unlock(struct qede_dev *edev) +-{ +- __qede_unlock(edev); +- rtnl_unlock(); +-} +- + static void qede_periodic_task(struct work_struct *work) + { + struct qede_dev *edev = container_of(work, struct qede_dev, +@@ -1114,6 +1099,8 @@ static void qede_sp_task(struct work_struct *work) + */ + + if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) { ++ bool reloaded; ++ + cancel_delayed_work_sync(&edev->periodic_task); + #ifdef CONFIG_QED_SRIOV + /* SRIOV must be disabled outside the lock to avoid a deadlock. +@@ -1122,9 +1109,17 @@ static void qede_sp_task(struct work_struct *work) + if (pci_num_vf(edev->pdev)) + qede_sriov_configure(edev->pdev, 0); + #endif +- qede_lock(edev); +- qede_recovery_handler(edev); +- qede_unlock(edev); ++ rtnl_lock(); ++ __qede_lock(edev); ++ reloaded = qede_recovery_handler(edev); ++ __qede_unlock(edev); ++ ++ /* The udp_tunnel core synchronously calls back into ++ * qede_udp_tunnel_sync(), which takes the qede lock. ++ */ ++ if (reloaded) ++ udp_tunnel_nic_reset_ntf(edev->ndev); ++ rtnl_unlock(); + } + + __qede_lock(edev); +@@ -2665,9 +2660,13 @@ static void qede_recovery_failed(struct qede_dev *edev) + edev->ops->common->set_power_state(edev->cdev, PCI_D3hot); + } + +-static void qede_recovery_handler(struct qede_dev *edev) ++/* Returns true if an open device was successfully reloaded and its ++ * udp_tunnel ports need to be re-synced by the caller. ++ */ ++static bool qede_recovery_handler(struct qede_dev *edev) + { + u32 curr_state = edev->state; ++ bool reloaded = false; + int rc; + + DP_NOTICE(edev, "Starting a recovery process\n"); +@@ -2697,17 +2696,18 @@ static void qede_recovery_handler(struct qede_dev *edev) + goto err; + + qede_config_rx_mode(edev->ndev); +- udp_tunnel_nic_reset_ntf(edev->ndev); ++ reloaded = true; + } + + edev->state = curr_state; + + DP_NOTICE(edev, "Recovery handling is done\n"); + +- return; ++ return reloaded; + + err: + qede_recovery_failed(edev); ++ return false; + } + + static void qede_atomic_hw_err_handler(struct qede_dev *edev) +-- +2.53.0 + diff --git a/queue-6.1/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch b/queue-6.1/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch new file mode 100644 index 0000000000..b3e72acda5 --- /dev/null +++ b/queue-6.1/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch @@ -0,0 +1,118 @@ +From f22be27c65882d29ef66aee7e05e7a02acf49b46 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 9 Jul 2026 16:27:54 +0000 +Subject: rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled + +From: Ilia Gavrilov + +[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ] + +When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst +is never initialized because inet6_init() exits before addrconf_init() +is called to initialize it. An attempt to bind an RDS socket to +an ipv6 address results in a crash in __ipv6_chk_addr_and_flags() + +KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] +RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0 +Call Trace: + + ipv6_chk_addr+0x3b/0x50 + rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp] + rds_trans_get_preferred+0x15d/0x2d0 [rds] + ? trace_hardirqs_on+0x2d/0x110 + rds_bind+0x1433/0x1d60 [rds] + ? rds_remove_bound+0xd50/0xd50 [rds] + ? aa_af_perm+0x250/0x250 + ? __might_fault+0xde/0x190 + ? __sys_bind+0x1dc/0x210 + __sys_bind+0x1dc/0x210 + ? __ia32_sys_socketpair+0x100/0x100 + ? restore_fpregs_from_fpstate+0x53/0x100 + __x64_sys_bind+0x73/0xb0 + ? syscall_enter_from_user_mode+0x1c/0x50 + do_syscall_64+0x34/0x80 + entry_SYSCALL_64_after_hwframe+0x6e/0xd8 +RIP: 0033:0x7f47f8269ea9 + + +The following code reproduces the issue: + +struct sockaddr_in6 addr; +s = socket(PF_RDS, SOCK_SEQPACKET, 0); + +memset(&addr, 0, sizeof(addr)); +inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr); +addr.sin6_family = AF_INET6; +addr.sin6_port = htons(PORT); + +bind(s, &addr, sizeof(addr)); + +Found by InfoTeCS on behalf of Linux Verification Center +(linuxtesting.org) with Syzkaller. + +Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") +Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support") +Signed-off-by: Ilia Gavrilov +Reviewed-by: Allison Henderson +Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru +Signed-off-by: Jakub Kicinski +Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()") +Signed-off-by: Sasha Levin +--- + net/rds/ib.c | 4 ++++ + net/rds/ib_cm.c | 4 ++++ + net/rds/tcp.c | 8 +++++--- + 3 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/net/rds/ib.c b/net/rds/ib.c +index ce5be43c5fbac..1061bcf7d1315 100644 +--- a/net/rds/ib.c ++++ b/net/rds/ib.c +@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr, + sa = (struct sockaddr *)&sin; + } else { + #if IS_ENABLED(CONFIG_IPV6) ++ if (!ipv6_mod_enabled()) { ++ ret = -EADDRNOTAVAIL; ++ goto out; ++ } + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = *addr; +diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c +index 5289afbb61aa7..e50e01abb0799 100644 +--- a/net/rds/ib_cm.c ++++ b/net/rds/ib_cm.c +@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, + dp = event->param.conn.private_data; + if (isv6) { + #if IS_ENABLED(CONFIG_IPV6) ++ if (!ipv6_mod_enabled()) { ++ err = -EOPNOTSUPP; ++ goto out; ++ } + dp_cmn = &dp->ricp_v6.dp_cmn; + saddr6 = &dp->ricp_v6.dp_saddr; + daddr6 = &dp->ricp_v6.dp_daddr; +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index 4444fd82b66df..e717c42b39353 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -341,9 +341,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, + rcu_read_unlock(); + } + #if IS_ENABLED(CONFIG_IPV6) +- ret = ipv6_chk_addr(net, addr, dev, 0); +- if (ret) +- return 0; ++ if (ipv6_mod_enabled()) { ++ ret = ipv6_chk_addr(net, addr, dev, 0); ++ if (ret) ++ return 0; ++ } + #endif + return -EADDRNOTAVAIL; + } +-- +2.53.0 + diff --git a/queue-6.1/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-6.1/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch new file mode 100644 index 0000000000..b2bba26648 --- /dev/null +++ b/queue-6.1/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch @@ -0,0 +1,85 @@ +From 575e6e28e23407fd2594a0fc1d6de609e883b534 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 14:02:03 -0700 +Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in + rds_tcp_laddr_check() + +From: Xiang Mei + +[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ] + +rds_tcp_laddr_check() looks up a scoped IPv6 interface with +dev_get_by_index_rcu(), drops the RCU read-side lock, and only then +passes the bare struct net_device * into ipv6_chk_addr(). + +dev_get_by_index_rcu() only keeps the device alive within the same RCU +read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can +free the net_device; ipv6_chk_addr() then dereferences the stale pointer +in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading +freed memory. + +Keep the RCU read-side lock held across the ipv6_chk_addr() call instead +of dropping it right after the lookup, so the device cannot be freed +while it is in use. + + BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + Read of size 8 at addr ffff8880106ec000 by task exploit/153 + Call Trace: + ... + kasan_report (mm/kasan/report.c:595) + __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972) + rds_tcp_laddr_check (net/rds/tcp.c:370) + rds_bind (net/rds/bind.c:248) + __sys_bind (net/socket.c:1920) + __x64_sys_bind (net/socket.c:1956) + do_syscall_64 (arch/x86/entry/syscall_64.c:63) + entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) + +Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") +Reported-by: Weiming Shi +Signed-off-by: Xiang Mei +Reviewed-by: Allison Henderson +Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/rds/tcp.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index e717c42b39353..323fa5ed5c3ea 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -330,23 +330,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, + /* If the scope_id is specified, check only those addresses + * hosted on the specified interface. + */ ++ rcu_read_lock(); + if (scope_id != 0) { +- rcu_read_lock(); + dev = dev_get_by_index_rcu(net, scope_id); + /* scope_id is not valid... */ + if (!dev) { + rcu_read_unlock(); + return -EADDRNOTAVAIL; + } +- rcu_read_unlock(); + } + #if IS_ENABLED(CONFIG_IPV6) + if (ipv6_mod_enabled()) { + ret = ipv6_chk_addr(net, addr, dev, 0); +- if (ret) ++ if (ret) { ++ rcu_read_unlock(); + return 0; ++ } + } + #endif ++ rcu_read_unlock(); + return -EADDRNOTAVAIL; + } + +-- +2.53.0 + diff --git a/queue-6.1/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-6.1/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch new file mode 100644 index 0000000000..ca2b8a0f94 --- /dev/null +++ b/queue-6.1/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch @@ -0,0 +1,56 @@ +From 81fdfcf12a08498a0fceb27524a7e6ba6fb5dc6c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 14 Jul 2026 19:49:34 +0900 +Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer + +From: HyeongJun An + +[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ] + +iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the +target-supplied data segment. The segment carries a 2-byte sense length +followed by the sense bytes, so it must hold 2 + senselen bytes, but the +bounds check only requires datalen >= senselen: + + senselen = get_unaligned_be16(data); + if (datalen < senselen) + goto invalid_datalen; + memcpy(sc->sense_buffer, data + 2, + min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); + +A target that returns a SCSI Response whose datalen equals senselen +(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data + +2 read up to two bytes past the received data. Those bytes are stale +conn->data contents and end up in the command's sense buffer, which is +returned to userspace. + +Account for the 2-byte sense length prefix in the check. + +Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi") +Suggested-by: Sashiko AI +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c +index ee4e3feedd10b..b858efe3972e9 100644 +--- a/drivers/scsi/libiscsi.c ++++ b/drivers/scsi/libiscsi.c +@@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, + } + + senselen = get_unaligned_be16(data); +- if (datalen < senselen) ++ if (datalen < senselen + 2) + goto invalid_datalen; + + memcpy(sc->sense_buffer, data + 2, +-- +2.53.0 + diff --git a/queue-6.1/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-6.1/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch new file mode 100644 index 0000000000..c102f0ae14 --- /dev/null +++ b/queue-6.1/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch @@ -0,0 +1,71 @@ +From a12ae65e91e581e7b929964d30be51e5fcefc12c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 15:58:48 +0900 +Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the + connection buffer + +From: HyeongJun An + +[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ] + +iscsi_tcp_hdr_dissect() receives the data segment of several PDU types +into the fixed-size conn->data buffer, which is allocated for +ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes. For the LOGIN_RSP, TEXT_RSP, +REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU +whose DataSegmentLength exceeds that buffer. + +The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its +data segment (sense/response data) into conn->data via +iscsi_tcp_data_recv_prep(), but it does so without the same check. The +only upstream bound on in.datalen is conn->max_recv_dlength, the +initiator's advertised MaxRecvDataSegmentLength, which is commonly +negotiated well above 8192 (open-iscsi defaults to 262144). A target +that returns a SCSI Response with a DataSegmentLength between 8193 and +max_recv_dlength therefore overflows the 8192-byte conn->data buffer. + +Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly +like those responses: bound the data segment, receive it into conn->data +when present, and otherwise complete the PDU with no data. Fold the +opcode into that case group rather than duplicating the check. + +Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld") +Suggested-by: Chris Leech +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi_tcp.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c +index c182aa83f2c93..4d23205129432 100644 +--- a/drivers/scsi/libiscsi_tcp.c ++++ b/drivers/scsi/libiscsi_tcp.c +@@ -763,13 +763,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); + spin_unlock(&conn->session->back_lock); + break; +- case ISCSI_OP_SCSI_CMD_RSP: +- if (tcp_conn->in.datalen) { +- iscsi_tcp_data_recv_prep(tcp_conn); +- return 0; +- } +- rc = iscsi_complete_pdu(conn, hdr, NULL, 0); +- break; + case ISCSI_OP_R2T: + if (ahslen) { + rc = ISCSI_ERR_AHSLEN; +@@ -777,6 +770,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + } + rc = iscsi_tcp_r2t_rsp(conn, hdr); + break; ++ case ISCSI_OP_SCSI_CMD_RSP: + case ISCSI_OP_LOGIN_RSP: + case ISCSI_OP_TEXT_RSP: + case ISCSI_OP_REJECT: +-- +2.53.0 + diff --git a/queue-6.1/scsi-libsas-abort-all-in-flight-requests-when-device.patch b/queue-6.1/scsi-libsas-abort-all-in-flight-requests-when-device.patch new file mode 100644 index 0000000000..eae1693c6d --- /dev/null +++ b/queue-6.1/scsi-libsas-abort-all-in-flight-requests-when-device.patch @@ -0,0 +1,83 @@ +From 1421c0c238aedf8aecd62359ab5279674e8884db Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 30 Mar 2023 19:09:30 +0800 +Subject: scsi: libsas: Abort all in-flight requests when device is gone + +From: Jason Yan + +[ Upstream commit 0e4b1791d9b192ac263a03707d876132eb0f8dab ] + +When a disk is removed with in-flight I/O, the application needs to wait +for 30 seconds (depending on the timeout configuration) to hear back from +the kernel. Xingui tried to fix this issue by aborting the ATA link for +SATA devices[1], however this approach left the SAS devices unresolved. + +Try to fix this issue by aborting all in-flight requests when the device is +gone. This is implemented by iterating over the tagset. + +[1] https://lore.kernel.org/lkml/234e04db-7539-07e4-a6b8-c6b05f78193d@opensource.wdc.com/T/ + +Cc: Xingui Yang +Cc: John Garry +Cc: Damien Le Moal +Cc: Hannes Reinecke +Signed-off-by: Jason Yan +Link: https://lore.kernel.org/r/20230330110930.175539-1-yanaijie@huawei.com +Reviewed-by: John Garry +Signed-off-by: Martin K. Petersen +Stable-dep-of: 3dbbbf656b85 ("scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race") +Signed-off-by: Sasha Levin +--- + drivers/scsi/libsas/sas_discover.c | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c +index d5bc1314c3415..49dea3c597778 100644 +--- a/drivers/scsi/libsas/sas_discover.c ++++ b/drivers/scsi/libsas/sas_discover.c +@@ -360,6 +360,33 @@ static void sas_destruct_ports(struct asd_sas_port *port) + } + } + ++static bool sas_abort_cmd(struct request *req, void *data) ++{ ++ struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req); ++ struct domain_device *dev = data; ++ ++ if (dev == cmd_to_domain_dev(cmd)) ++ blk_abort_request(req); ++ return true; ++} ++ ++static void sas_abort_device_scsi_cmds(struct domain_device *dev) ++{ ++ struct sas_ha_struct *sas_ha = dev->port->ha; ++ struct Scsi_Host *shost = sas_ha->core.shost; ++ ++ if (dev_is_expander(dev->dev_type)) ++ return; ++ ++ /* ++ * For removed device with active IOs, the user space applications have ++ * to spend very long time waiting for the timeout. This is not ++ * necessary because a removed device will not return the IOs. ++ * Abort the inflight IOs here so that EH can be quickly kicked in. ++ */ ++ blk_mq_tagset_busy_iter(&shost->tag_set, sas_abort_cmd, dev); ++} ++ + void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) + { + if (!test_bit(SAS_DEV_DESTROY, &dev->state) && +@@ -372,6 +399,8 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) + } + + if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) { ++ if (test_bit(SAS_DEV_GONE, &dev->state)) ++ sas_abort_device_scsi_cmds(dev); + sas_rphy_unlink(dev->rphy); + list_move_tail(&dev->disco_list_node, &port->destroy_list); + } +-- +2.53.0 + diff --git a/queue-6.1/scsi-libsas-delete-struct-scsi_core.patch b/queue-6.1/scsi-libsas-delete-struct-scsi_core.patch new file mode 100644 index 0000000000..b4f18511b9 --- /dev/null +++ b/queue-6.1/scsi-libsas-delete-struct-scsi_core.patch @@ -0,0 +1,563 @@ +From cb30320aa74525f6642c05eddfbf1726b32d6fa7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 15 Aug 2023 11:51:50 +0000 +Subject: scsi: libsas: Delete struct scsi_core + +From: John Garry + +[ Upstream commit 1136a0225d0582c4464fa37e3a91ed4b19b8745e ] + +Since commit 79855d178557 ("libsas: remove task_collector mode"), struct +scsi_core only contains a reference to the shost. struct scsi_core is only +used in sas_ha_struct.core, so delete scsi_core and replace with a +reference to the shost there. + +Signed-off-by: John Garry +Link: https://lore.kernel.org/r/20230815115156.343535-5-john.g.garry@oracle.com +Reviewed-by: Jason Yan +Reviewed-by: Damien Le Moal +Signed-off-by: Martin K. Petersen +Stable-dep-of: 3dbbbf656b85 ("scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race") +Signed-off-by: Sasha Levin +--- + drivers/scsi/aic94xx/aic94xx_hwi.c | 2 +- + drivers/scsi/aic94xx/aic94xx_init.c | 6 +++--- + drivers/scsi/hisi_sas/hisi_sas_main.c | 6 +++--- + drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 6 +++--- + drivers/scsi/isci/host.h | 2 +- + drivers/scsi/isci/init.c | 4 ++-- + drivers/scsi/libsas/sas_ata.c | 8 ++++---- + drivers/scsi/libsas/sas_discover.c | 8 ++++---- + drivers/scsi/libsas/sas_expander.c | 2 +- + drivers/scsi/libsas/sas_host_smp.c | 4 ++-- + drivers/scsi/libsas/sas_init.c | 16 ++++++++-------- + drivers/scsi/libsas/sas_phy.c | 8 ++++---- + drivers/scsi/libsas/sas_port.c | 6 +++--- + drivers/scsi/libsas/sas_scsi_host.c | 14 +++++++------- + drivers/scsi/mvsas/mv_init.c | 4 ++-- + drivers/scsi/pm8001/pm8001_init.c | 2 +- + include/scsi/libsas.h | 7 +------ + 17 files changed, 50 insertions(+), 55 deletions(-) + +diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c +index 3dd1101434715..8f515aae0a8dd 100644 +--- a/drivers/scsi/aic94xx/aic94xx_hwi.c ++++ b/drivers/scsi/aic94xx/aic94xx_hwi.c +@@ -28,7 +28,7 @@ static int asd_get_user_sas_addr(struct asd_ha_struct *asd_ha) + if (asd_ha->hw_prof.sas_addr[0]) + return 0; + +- return sas_request_addr(asd_ha->sas_ha.core.shost, ++ return sas_request_addr(asd_ha->sas_ha.shost, + asd_ha->hw_prof.sas_addr); + } + +diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c +index 1766302053da6..f204714ad7536 100644 +--- a/drivers/scsi/aic94xx/aic94xx_init.c ++++ b/drivers/scsi/aic94xx/aic94xx_init.c +@@ -688,8 +688,8 @@ static int asd_unregister_sas_ha(struct asd_ha_struct *asd_ha) + + err = sas_unregister_ha(&asd_ha->sas_ha); + +- sas_remove_host(asd_ha->sas_ha.core.shost); +- scsi_host_put(asd_ha->sas_ha.core.shost); ++ sas_remove_host(asd_ha->sas_ha.shost); ++ scsi_host_put(asd_ha->sas_ha.shost); + + kfree(asd_ha->sas_ha.sas_phy); + kfree(asd_ha->sas_ha.sas_port); +@@ -739,7 +739,7 @@ static int asd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) + asd_printk("found %s, device %s\n", asd_ha->name, pci_name(dev)); + + SHOST_TO_SAS_HA(shost) = &asd_ha->sas_ha; +- asd_ha->sas_ha.core.shost = shost; ++ asd_ha->sas_ha.shost = shost; + shost->transportt = aic94xx_transport_template; + shost->max_id = ~0; + shost->max_lun = ~0; +diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c +index 360f2799f2a13..10ea1d434c48d 100644 +--- a/drivers/scsi/hisi_sas/hisi_sas_main.c ++++ b/drivers/scsi/hisi_sas/hisi_sas_main.c +@@ -2458,7 +2458,7 @@ int hisi_sas_probe(struct platform_device *pdev, + sha->lldd_module = THIS_MODULE; + sha->sas_addr = &hisi_hba->sas_addr[0]; + sha->num_phys = hisi_hba->n_phy; +- sha->core.shost = hisi_hba->shost; ++ sha->shost = hisi_hba->shost; + + for (i = 0; i < hisi_hba->n_phy; i++) { + sha->sas_phy[i] = &hisi_hba->phy[i].sas_phy; +@@ -2500,12 +2500,12 @@ int hisi_sas_remove(struct platform_device *pdev) + { + struct sas_ha_struct *sha = platform_get_drvdata(pdev); + struct hisi_hba *hisi_hba = sha->lldd_ha; +- struct Scsi_Host *shost = sha->core.shost; ++ struct Scsi_Host *shost = sha->shost; + + del_timer_sync(&hisi_hba->timer); + + sas_unregister_ha(sha); +- sas_remove_host(sha->core.shost); ++ sas_remove_host(shost); + + hisi_sas_free(hisi_hba); + scsi_host_put(shost); +diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +index 20b4d76e07149..a3b408962a861 100644 +--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c ++++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +@@ -4820,7 +4820,7 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id) + + sha->sas_phy = arr_phy; + sha->sas_port = arr_port; +- sha->core.shost = shost; ++ sha->shost = shost; + sha->lldd_ha = hisi_hba; + + shost->transportt = hisi_sas_stt; +@@ -4921,14 +4921,14 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev) + struct device *dev = &pdev->dev; + struct sas_ha_struct *sha = dev_get_drvdata(dev); + struct hisi_hba *hisi_hba = sha->lldd_ha; +- struct Scsi_Host *shost = sha->core.shost; ++ struct Scsi_Host *shost = sha->shost; + + pm_runtime_get_noresume(dev); + del_timer_sync(&hisi_hba->timer); + + sas_unregister_ha(sha); + flush_workqueue(hisi_hba->wq); +- sas_remove_host(sha->core.shost); ++ sas_remove_host(shost); + + hisi_sas_v3_destroy_irqs(pdev, hisi_hba); + hisi_sas_free(hisi_hba); +diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h +index 6bc3f022630a2..52388374cf315 100644 +--- a/drivers/scsi/isci/host.h ++++ b/drivers/scsi/isci/host.h +@@ -306,7 +306,7 @@ static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev) + + static inline struct Scsi_Host *to_shost(struct isci_host *ihost) + { +- return ihost->sas_ha.core.shost; ++ return ihost->sas_ha.shost; + } + + #define for_each_isci_host(id, ihost, pdev) \ +diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c +index 012cd2dade862..ee8c87dbe5ef3 100644 +--- a/drivers/scsi/isci/init.c ++++ b/drivers/scsi/isci/init.c +@@ -571,7 +571,7 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) + goto err_shost; + + SHOST_TO_SAS_HA(shost) = &ihost->sas_ha; +- ihost->sas_ha.core.shost = shost; ++ ihost->sas_ha.shost = shost; + shost->transportt = isci_transport_template; + + shost->max_id = ~0; +@@ -726,7 +726,7 @@ static int isci_resume(struct device *dev) + sas_prep_resume_ha(&ihost->sas_ha); + + isci_host_init(ihost); +- isci_host_start(ihost->sas_ha.core.shost); ++ isci_host_start(ihost->sas_ha.shost); + wait_for_start(ihost); + + sas_resume_ha(&ihost->sas_ha); +diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c +index 6b045be947b14..1d06daac1d927 100644 +--- a/drivers/scsi/libsas/sas_ata.c ++++ b/drivers/scsi/libsas/sas_ata.c +@@ -162,7 +162,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) + struct ata_port *ap = qc->ap; + struct domain_device *dev = ap->private_data; + struct sas_ha_struct *sas_ha = dev->port->ha; +- struct Scsi_Host *host = sas_ha->core.shost; ++ struct Scsi_Host *host = sas_ha->shost; + struct sas_internal *i = to_sas_internal(host->transportt); + + /* TODO: we should try to remove that unlock */ +@@ -236,7 +236,7 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) + + static struct sas_internal *dev_to_sas_internal(struct domain_device *dev) + { +- return to_sas_internal(dev->port->ha->core.shost->transportt); ++ return to_sas_internal(dev->port->ha->shost->transportt); + } + + static int sas_get_ata_command_set(struct domain_device *dev); +@@ -576,7 +576,7 @@ static struct ata_port_info sata_port_info = { + int sas_ata_init(struct domain_device *found_dev) + { + struct sas_ha_struct *ha = found_dev->port->ha; +- struct Scsi_Host *shost = ha->core.shost; ++ struct Scsi_Host *shost = ha->shost; + struct ata_host *ata_host; + struct ata_port *ap; + int rc; +@@ -766,7 +766,7 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) + struct sas_ha_struct *ha = dev->port->ha; + + sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n"); +- ata_scsi_port_error_handler(ha->core.shost, ap); ++ ata_scsi_port_error_handler(ha->shost, ap); + sas_put_device(dev); + } + +diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c +index 49dea3c597778..4e8cdd3ae5ab9 100644 +--- a/drivers/scsi/libsas/sas_discover.c ++++ b/drivers/scsi/libsas/sas_discover.c +@@ -170,7 +170,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) + { + int res = 0; + struct sas_ha_struct *sas_ha = dev->port->ha; +- struct Scsi_Host *shost = sas_ha->core.shost; ++ struct Scsi_Host *shost = sas_ha->shost; + struct sas_internal *i = to_sas_internal(shost->transportt); + + if (!i->dft->lldd_dev_found) +@@ -192,7 +192,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) + void sas_notify_lldd_dev_gone(struct domain_device *dev) + { + struct sas_ha_struct *sas_ha = dev->port->ha; +- struct Scsi_Host *shost = sas_ha->core.shost; ++ struct Scsi_Host *shost = sas_ha->shost; + struct sas_internal *i = to_sas_internal(shost->transportt); + + if (!i->dft->lldd_dev_gone) +@@ -234,7 +234,7 @@ static void sas_suspend_devices(struct work_struct *work) + struct domain_device *dev; + struct sas_discovery_event *ev = to_sas_discovery_event(work); + struct asd_sas_port *port = ev->port; +- struct Scsi_Host *shost = port->ha->core.shost; ++ struct Scsi_Host *shost = port->ha->shost; + struct sas_internal *si = to_sas_internal(shost->transportt); + + clear_bit(DISCE_SUSPEND, &port->disc.pending); +@@ -373,7 +373,7 @@ static bool sas_abort_cmd(struct request *req, void *data) + static void sas_abort_device_scsi_cmds(struct domain_device *dev) + { + struct sas_ha_struct *sas_ha = dev->port->ha; +- struct Scsi_Host *shost = sas_ha->core.shost; ++ struct Scsi_Host *shost = sas_ha->shost; + + if (dev_is_expander(dev->dev_type)) + return; +diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c +index ffec7f0e51fcd..03d367c2f0a7a 100644 +--- a/drivers/scsi/libsas/sas_expander.c ++++ b/drivers/scsi/libsas/sas_expander.c +@@ -37,7 +37,7 @@ static int smp_execute_task_sg(struct domain_device *dev, + int res, retry; + struct sas_task *task = NULL; + struct sas_internal *i = +- to_sas_internal(dev->port->ha->core.shost->transportt); ++ to_sas_internal(dev->port->ha->shost->transportt); + struct sas_ha_struct *ha = dev->port->ha; + + pm_runtime_get_sync(ha->dev); +diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c +index 32cdc969b736a..2ecb8535634c1 100644 +--- a/drivers/scsi/libsas/sas_host_smp.c ++++ b/drivers/scsi/libsas/sas_host_smp.c +@@ -114,7 +114,7 @@ static int sas_host_smp_write_gpio(struct sas_ha_struct *sas_ha, u8 *resp_data, + u8 reg_type, u8 reg_index, u8 reg_count, + u8 *req_data) + { +- struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); ++ struct sas_internal *i = to_sas_internal(sas_ha->shost->transportt); + int written; + + if (i->dft->lldd_write_gpio == NULL) { +@@ -182,7 +182,7 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id, + enum sas_linkrate max, u8 *resp_data) + { + struct sas_internal *i = +- to_sas_internal(sas_ha->core.shost->transportt); ++ to_sas_internal(sas_ha->shost->transportt); + struct sas_phy_linkrates rates; + struct asd_sas_phy *asd_phy; + +diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c +index e4f77072a58d2..d514cf3f85ed9 100644 +--- a/drivers/scsi/libsas/sas_init.c ++++ b/drivers/scsi/libsas/sas_init.c +@@ -186,7 +186,7 @@ static int sas_get_linkerrors(struct sas_phy *phy) + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; + struct sas_internal *i = +- to_sas_internal(sas_ha->core.shost->transportt); ++ to_sas_internal(sas_ha->shost->transportt); + + return i->dft->lldd_control_phy(asd_phy, PHY_FUNC_GET_EVENTS, NULL); + } +@@ -235,7 +235,7 @@ static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset) + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; + struct sas_internal *i = +- to_sas_internal(sas_ha->core.shost->transportt); ++ to_sas_internal(sas_ha->shost->transportt); + + if (!hard_reset && sas_try_ata_reset(asd_phy) == 0) + return 0; +@@ -269,7 +269,7 @@ int sas_phy_enable(struct sas_phy *phy, int enable) + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; + struct sas_internal *i = +- to_sas_internal(sas_ha->core.shost->transportt); ++ to_sas_internal(sas_ha->shost->transportt); + + if (enable) + ret = transport_sas_phy_reset(phy, 0); +@@ -306,7 +306,7 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset) + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; + struct sas_internal *i = +- to_sas_internal(sas_ha->core.shost->transportt); ++ to_sas_internal(sas_ha->shost->transportt); + + ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL); + } else { +@@ -342,7 +342,7 @@ int sas_set_phy_speed(struct sas_phy *phy, + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; + struct sas_internal *i = +- to_sas_internal(sas_ha->core.shost->transportt); ++ to_sas_internal(sas_ha->shost->transportt); + + ret = i->dft->lldd_control_phy(asd_phy, PHY_FUNC_SET_LINK_RATE, + rates); +@@ -441,7 +441,7 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + /* all phys are back up or timed out, turn on i/o so we can + * flush out disks that did not return + */ +- scsi_unblock_requests(ha->core.shost); ++ scsi_unblock_requests(ha->shost); + if (drain) + sas_drain_work(ha); + clear_bit(SAS_HA_RESUMING, &ha->state); +@@ -471,7 +471,7 @@ void sas_suspend_ha(struct sas_ha_struct *ha) + int i; + + sas_disable_events(ha); +- scsi_block_requests(ha->core.shost); ++ scsi_block_requests(ha->shost); + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_port *port = ha->sas_port[i]; + +@@ -644,7 +644,7 @@ struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy, + struct asd_sas_event *event; + struct sas_ha_struct *sas_ha = phy->ha; + struct sas_internal *i = +- to_sas_internal(sas_ha->core.shost->transportt); ++ to_sas_internal(sas_ha->shost->transportt); + + event = kmem_cache_zalloc(sas_event_cache, gfp_flags); + if (!event) +diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c +index a0d592d11dfb1..57494ac97076d 100644 +--- a/drivers/scsi/libsas/sas_phy.c ++++ b/drivers/scsi/libsas/sas_phy.c +@@ -38,7 +38,7 @@ static void sas_phye_oob_error(struct work_struct *work) + struct sas_ha_struct *sas_ha = phy->ha; + struct asd_sas_port *port = phy->port; + struct sas_internal *i = +- to_sas_internal(sas_ha->core.shost->transportt); ++ to_sas_internal(sas_ha->shost->transportt); + + sas_deform_port(phy, 1); + +@@ -66,7 +66,7 @@ static void sas_phye_spinup_hold(struct work_struct *work) + struct asd_sas_phy *phy = ev->phy; + struct sas_ha_struct *sas_ha = phy->ha; + struct sas_internal *i = +- to_sas_internal(sas_ha->core.shost->transportt); ++ to_sas_internal(sas_ha->shost->transportt); + + phy->error = 0; + i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL); +@@ -95,7 +95,7 @@ static void sas_phye_shutdown(struct work_struct *work) + struct asd_sas_phy *phy = ev->phy; + struct sas_ha_struct *sas_ha = phy->ha; + struct sas_internal *i = +- to_sas_internal(sas_ha->core.shost->transportt); ++ to_sas_internal(sas_ha->shost->transportt); + + if (phy->enabled) { + int ret; +@@ -131,7 +131,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) + spin_lock_init(&phy->sas_prim_lock); + phy->frame_rcvd_size = 0; + +- phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i); ++ phy->phy = sas_phy_alloc(&sas_ha->shost->shost_gendev, i); + if (!phy->phy) + return -ENOMEM; + +diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c +index 11599c0e3fc34..60ad1486d15c2 100644 +--- a/drivers/scsi/libsas/sas_port.c ++++ b/drivers/scsi/libsas/sas_port.c +@@ -28,7 +28,7 @@ static void sas_resume_port(struct asd_sas_phy *phy) + struct domain_device *dev, *n; + struct asd_sas_port *port = phy->port; + struct sas_ha_struct *sas_ha = phy->ha; +- struct sas_internal *si = to_sas_internal(sas_ha->core.shost->transportt); ++ struct sas_internal *si = to_sas_internal(sas_ha->shost->transportt); + + if (si->dft->lldd_port_formed) + si->dft->lldd_port_formed(phy); +@@ -109,7 +109,7 @@ static void sas_form_port(struct asd_sas_phy *phy) + struct asd_sas_port *port = phy->port; + struct domain_device *port_dev = NULL; + struct sas_internal *si = +- to_sas_internal(sas_ha->core.shost->transportt); ++ to_sas_internal(sas_ha->shost->transportt); + unsigned long flags; + + if (port) { +@@ -212,7 +212,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) + struct sas_ha_struct *sas_ha = phy->ha; + struct asd_sas_port *port = phy->port; + struct sas_internal *si = +- to_sas_internal(sas_ha->core.shost->transportt); ++ to_sas_internal(sas_ha->shost->transportt); + struct domain_device *dev; + unsigned long flags; + +diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c +index a36fa1c128a84..83ea14ce2330a 100644 +--- a/drivers/scsi/libsas/sas_scsi_host.c ++++ b/drivers/scsi/libsas/sas_scsi_host.c +@@ -279,7 +279,7 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task) + unsigned long flags; + int i, res; + struct sas_internal *si = +- to_sas_internal(task->dev->port->ha->core.shost->transportt); ++ to_sas_internal(task->dev->port->ha->shost->transportt); + + for (i = 0; i < 5; i++) { + pr_notice("%s: aborting task 0x%p\n", __func__, task); +@@ -327,7 +327,7 @@ static int sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd) + int res = TMF_RESP_FUNC_FAILED; + struct scsi_lun lun; + struct sas_internal *i = +- to_sas_internal(dev->port->ha->core.shost->transportt); ++ to_sas_internal(dev->port->ha->shost->transportt); + + int_to_scsilun(cmd->device->lun, &lun); + +@@ -355,7 +355,7 @@ static int sas_recover_I_T(struct domain_device *dev) + { + int res = TMF_RESP_FUNC_FAILED; + struct sas_internal *i = +- to_sas_internal(dev->port->ha->core.shost->transportt); ++ to_sas_internal(dev->port->ha->shost->transportt); + + pr_notice("I_T nexus reset for dev %016llx\n", + SAS_ADDR(dev->sas_addr)); +@@ -410,7 +410,7 @@ static void sas_wait_eh(struct domain_device *dev) + spin_unlock_irq(&ha->lock); + + /* make sure SCSI EH is complete */ +- if (scsi_host_in_recovery(ha->core.shost)) { ++ if (scsi_host_in_recovery(ha->shost)) { + msleep(10); + goto retry; + } +@@ -440,7 +440,7 @@ static int sas_queue_reset(struct domain_device *dev, int reset_type, + set_bit(SAS_DEV_EH_PENDING, &dev->state); + set_bit(reset_type, &dev->state); + int_to_scsilun(lun, &dev->ssp_dev.reset_lun); +- scsi_schedule_eh(ha->core.shost); ++ scsi_schedule_eh(ha->shost); + } + spin_unlock_irq(&ha->lock); + +@@ -926,7 +926,7 @@ static int sas_execute_internal_abort(struct domain_device *device, + unsigned int qid, void *data) + { + struct sas_ha_struct *ha = device->port->ha; +- struct sas_internal *i = to_sas_internal(ha->core.shost->transportt); ++ struct sas_internal *i = to_sas_internal(ha->shost->transportt); + struct sas_task *task = NULL; + int res, retry; + +@@ -1016,7 +1016,7 @@ int sas_execute_tmf(struct domain_device *device, void *parameter, + { + struct sas_task *task; + struct sas_internal *i = +- to_sas_internal(device->port->ha->core.shost->transportt); ++ to_sas_internal(device->port->ha->shost->transportt); + int res, retry; + + for (retry = 0; retry < TASK_RETRY; retry++) { +diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c +index b500c343cad75..4182e004d775f 100644 +--- a/drivers/scsi/mvsas/mv_init.c ++++ b/drivers/scsi/mvsas/mv_init.c +@@ -416,7 +416,7 @@ static int mvs_prep_sas_ha_init(struct Scsi_Host *shost, + + sha->sas_phy = arr_phy; + sha->sas_port = arr_port; +- sha->core.shost = shost; ++ sha->shost = shost; + + sha->lldd_ha = kzalloc(sizeof(struct mvs_prv_info), GFP_KERNEL); + if (!sha->lldd_ha) +@@ -473,7 +473,7 @@ static void mvs_post_sas_ha_init(struct Scsi_Host *shost, + shost->sg_tablesize = min_t(u16, SG_ALL, MVS_MAX_SG); + shost->can_queue = can_queue; + mvi->shost->cmd_per_lun = MVS_QUEUE_SIZE; +- sha->core.shost = mvi->shost; ++ sha->shost = mvi->shost; + } + + static void mvs_init_sas_add(struct mvs_info *mvi) +diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c +index 60b477e65a66a..914a30b3dfc2e 100644 +--- a/drivers/scsi/pm8001/pm8001_init.c ++++ b/drivers/scsi/pm8001/pm8001_init.c +@@ -653,7 +653,7 @@ static void pm8001_post_sas_ha_init(struct Scsi_Host *shost, + sha->lldd_module = THIS_MODULE; + sha->sas_addr = &pm8001_ha->sas_addr[0]; + sha->num_phys = chip_info->n_phy; +- sha->core.shost = shost; ++ sha->shost = shost; + } + + /** +diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h +index 9e9dff75a02bc..1be8aa6f53933 100644 +--- a/include/scsi/libsas.h ++++ b/include/scsi/libsas.h +@@ -346,11 +346,6 @@ struct asd_sas_phy { + void *lldd_phy; /* not touched by the sas_class_code */ + }; + +-struct scsi_core { +- struct Scsi_Host *shost; +- +-}; +- + enum sas_ha_state { + SAS_HA_REGISTERED, + SAS_HA_DRAINING, +@@ -371,7 +366,7 @@ struct sas_ha_struct { + + struct mutex disco_mutex; + +- struct scsi_core core; ++ struct Scsi_Host *shost; + + /* public: */ + char *sas_ha_name; +-- +2.53.0 + diff --git a/queue-6.1/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch b/queue-6.1/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch new file mode 100644 index 0000000000..dc39774e36 --- /dev/null +++ b/queue-6.1/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch @@ -0,0 +1,165 @@ +From 5a161a846c1b83be31990833cdb08f15af1c94b7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 16:11:45 +0800 +Subject: scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race + +From: Xingui Yang + +[ Upstream commit 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 ] + +Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue +for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock: +the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue, +calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI +devices and waits for the host to become runtime-active. But the host +cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the +drain is blocked on that very handler. + +However skipping the drain reintroduces a race: hisi_sas returns from +resume before all PHY UP work and libsas discovery work finish. The +controller may then autosuspend while disks are still waking up. The +disks issue IO to a suspended controller, the IO fails, and the disks +get disabled. + +Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT +notification to after sas_drain_work(). By then the host resume is about +to complete, so device removal through device_link no longer blocks on +the resume and the cycle is broken. + +With the deadlock gone, restore sas_resume_ha() (the draining variant) +in hisi_sas and remove sas_resume_ha_no_sync(). + +The reorder is safe for the other libsas consumers (isci, pm8001, +aic94xx, mvsas). During suspend, sas_suspend_devices() calls +sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to +NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a +timed-out phy's disk is immediately rejected by the LLDD before reaching +hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET), +and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both +complete directly via scsi_done() without entering SCSI EH. This is +identical in both the old and new ordering since lldd_dev_gone runs +during suspend, before resume. The reorder only affects when the +PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work() +vs. asynchronous after resume returns), not whether I/O can reach the +device. aic94xx and mvsas do not register any PM ops and never reach +this code path. + +Fixes: fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume") +Signed-off-by: Xingui Yang +Reviewed-by: John Garry +Link: https://patch.msgid.link/20260716081145.3950172-1-yangxingui@huawei.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +------ + drivers/scsi/libsas/sas_init.c | 37 +++++++++++++------------- + include/scsi/libsas.h | 1 - + 3 files changed, 19 insertions(+), 29 deletions(-) + +diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +index a3b408962a861..ccd52fc7d34a2 100644 +--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c ++++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +@@ -5047,15 +5047,7 @@ static int _resume_v3_hw(struct device *device) + return rc; + } + phys_init_v3_hw(hisi_hba); +- +- /* +- * If a directly-attached disk is removed during suspend, a deadlock +- * may occur, as the PHYE_RESUME_TIMEOUT processing will require the +- * hisi_hba->device to be active, which can only happen when resume +- * completes. So don't wait for the HA event workqueue to drain upon +- * resume. +- */ +- sas_resume_ha_no_sync(sha); ++ sas_resume_ha(sha); + clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags); + + dev_warn(dev, "end of resuming controller\n"); +diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c +index d514cf3f85ed9..f1e24534d12fe 100644 +--- a/drivers/scsi/libsas/sas_init.c ++++ b/drivers/scsi/libsas/sas_init.c +@@ -412,7 +412,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha) + } + } + +-static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) ++void sas_resume_ha(struct sas_ha_struct *ha) + { + const unsigned long tmo = msecs_to_jiffies(25000); + int i; +@@ -428,6 +428,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n", + i, i > 1 ? "s" : ""); + wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo); ++ ++ /* ++ * All phys are back up or timed out. Turn on I/O and drain ++ * pending work. ++ */ ++ scsi_unblock_requests(ha->shost); ++ sas_drain_work(ha); ++ ++ /* ++ * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler ++ * calls sas_deform_port() -> sas_destruct_devices(), which removes ++ * SCSI devices and, for LLDDs using device_link() PM sync, waits ++ * for the host to be runtime-active. Sending it before the drain ++ * would deadlock: the drain waits for the handler, the handler ++ * waits for host resume, and host resume waits for the drain to ++ * finish. ++ */ + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_phy *phy = ha->sas_phy[i]; + +@@ -438,12 +455,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + } + } + +- /* all phys are back up or timed out, turn on i/o so we can +- * flush out disks that did not return +- */ +- scsi_unblock_requests(ha->shost); +- if (drain) +- sas_drain_work(ha); + clear_bit(SAS_HA_RESUMING, &ha->state); + + sas_queue_deferred_work(ha); +@@ -452,20 +463,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + */ + sas_resume_insert_broadcast_ha(ha); + } +- +-void sas_resume_ha(struct sas_ha_struct *ha) +-{ +- _sas_resume_ha(ha, true); +-} + EXPORT_SYMBOL(sas_resume_ha); + +-/* A no-sync variant, which does not call sas_drain_ha(). */ +-void sas_resume_ha_no_sync(struct sas_ha_struct *ha) +-{ +- _sas_resume_ha(ha, false); +-} +-EXPORT_SYMBOL(sas_resume_ha_no_sync); +- + void sas_suspend_ha(struct sas_ha_struct *ha) + { + int i; +diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h +index 1be8aa6f53933..7165d3aee1176 100644 +--- a/include/scsi/libsas.h ++++ b/include/scsi/libsas.h +@@ -703,7 +703,6 @@ extern int sas_register_ha(struct sas_ha_struct *); + extern int sas_unregister_ha(struct sas_ha_struct *); + extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha); + extern void sas_resume_ha(struct sas_ha_struct *sas_ha); +-extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha); + extern void sas_suspend_ha(struct sas_ha_struct *sas_ha); + + int sas_set_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates); +-- +2.53.0 + diff --git a/queue-6.1/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch b/queue-6.1/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch new file mode 100644 index 0000000000..595a2ec689 --- /dev/null +++ b/queue-6.1/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch @@ -0,0 +1,80 @@ +From 5ba734f5ce2bf27a8d13716a478cadb7100a2ccd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 09:30:10 +0300 +Subject: scsi: target: Clear cmd_cnt when initial counter enrollment fails + +From: Leon Romanovsky + +[ Upstream commit a8ddfd2425bbbafadae8700d63ed8a61a4109878 ] + +When target_get_sess_cmd() fails during session shutdown because +percpu_ref_tryget_live() returns false, the command keeps the +se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier +without owning a reference. Final release through +target_release_cmd_kref() then issues an unmatched percpu_ref_put(). + +Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during +cmd setup") moved the cmd_cnt assignment ahead of the reference +acquisition. Clear se_cmd->cmd_cnt whenever the initial +target_get_sess_cmd() fails in target_init_cmd() and +target_submit_tmr(), so release performs exactly one matching put per +acquired reference. + +Fixes: 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup") +Signed-off-by: Leon Romanovsky +Reviewed-by: Mike Christie +Link: https://patch.msgid.link/20260722-reference-count-underflow-in-target-v1-1-63ab664f12fd@nvidia.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/target/target_core_transport.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c +index b9a144a59dff3..f4e3ba173dbe3 100644 +--- a/drivers/target/target_core_transport.c ++++ b/drivers/target/target_core_transport.c +@@ -1670,6 +1670,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, + u32 data_length, int task_attr, int data_dir, int flags) + { + struct se_portal_group *se_tpg; ++ int ret; + + se_tpg = se_sess->se_tpg; + BUG_ON(!se_tpg); +@@ -1699,7 +1700,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, + * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second + * kref_put() to happen during fabric packet acknowledgement. + */ +- return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); ++ ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); ++ if (ret) ++ se_cmd->cmd_cnt = NULL; ++ ++ return ret; + } + EXPORT_SYMBOL_GPL(target_init_cmd); + +@@ -1994,8 +1999,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, + * allocation failure. + */ + ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp); +- if (ret < 0) ++ if (ret < 0) { ++ se_cmd->cmd_cnt = NULL; + return -ENOMEM; ++ } + + if (tm_type == TMR_ABORT_TASK) + se_cmd->se_tmr_req->ref_task_tag = tag; +@@ -2003,6 +2010,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, + /* See target_submit_cmd for commentary */ + ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); + if (ret) { ++ se_cmd->cmd_cnt = NULL; + core_tmr_release_req(se_cmd->se_tmr_req); + return ret; + } +-- +2.53.0 + diff --git a/queue-6.1/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-6.1/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch new file mode 100644 index 0000000000..49a5c987a6 --- /dev/null +++ b/queue-6.1/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch @@ -0,0 +1,66 @@ +From 47b376780daae6b805aaa2f7f66610f2fbf77b23 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 09:27:36 +0200 +Subject: scsi: zfcp: Fix memory leak during adapter release by destroying + gid_pn_req + +From: Benjamin Block + +[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ] + +When releasing an adapter we don't free the mempool 'gid_pn_req' that is +allocated during the enqueue. This leaks memory: + + unreferenced object 0xd8d29297de700 (size 256): + comm "(udev-worker)", pid 2105, jiffies 4294945794 + hex dump (first 32 bytes): + 00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00 ......N......... + ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0 ..........._g... + backtrace (crc 4a5b5da2): + [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0 + [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0 + [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150 + [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp] + [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp] + [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp] + [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80 + [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390 + [<000dc45f643d8238>] online_store+0x298/0x5b0 + [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480 + [<000dc45f62c81150>] new_sync_write+0x370/0x4b0 + [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0 + [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0 + [<000dc45f621c4a16>] do_syscall+0x2f6/0x430 + [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0 + [<000dc45f64dc2224>] system_call+0x74/0xa0 + +Fix this by destroying the mempool during the adapter's release. + +Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp") +Signed-off-by: Benjamin Block +Tested-by: M Nikhil +Acked-by: M Nikhil +Reviewed-by: Chinmaya Kajagar +Reviewed-by: Nihar Panda +Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/s390/scsi/zfcp_aux.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c +index ab2f35bc294da..d3cc884ccd599 100644 +--- a/drivers/s390/scsi/zfcp_aux.c ++++ b/drivers/s390/scsi/zfcp_aux.c +@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) + static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) + { + mempool_destroy(adapter->pool.erp_req); ++ mempool_destroy(adapter->pool.gid_pn_req); + mempool_destroy(adapter->pool.scsi_req); + mempool_destroy(adapter->pool.scsi_abort); + mempool_destroy(adapter->pool.qtcb_pool); +-- +2.53.0 + diff --git a/queue-6.1/series b/queue-6.1/series index 8124209bec..f5e3f3595c 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -306,3 +306,73 @@ hid-logitech-dj-prevent-report_id_dj_short-related-u.patch hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch net-qrtr-ns-limit-the-maximum-server-registration-pe.patch net-qrtr-ns-raise-node-count-limit-to-512.patch +pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch +dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch +ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch +ata-libahci_platform-support-non-consecutive-port-nu.patch +ahci-introduce-ahci_ignore_port-helper.patch +ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch +asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch +asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch +phy-zynqmp-allow-variation-in-refclk-rate.patch +phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch +phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch +phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch +netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch +drm-mediatek-check-crtc-state-before-freeing.patch +keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch +keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch +assoc_array-trim-the-final-shortcut-word-using-the-c.patch +netfilter-xt_hashlimit-validate-hashtable-supports-x.patch +ipv6-introduce-dst_rt6_info-helper.patch +ipvs-fix-the-checksum-validations.patch +ipvs-fix-places-with-wrong-packet-offsets.patch +ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch +netfilter-nft_payload-fix-mask-build-for-partial-fie.patch +rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch +rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch +pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch +scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch +scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch +scsi-libsas-abort-all-in-flight-requests-when-device.patch +scsi-libsas-delete-struct-scsi_core.patch +scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch +smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch +hwmon-nct6755-add-support-for-nct6799d.patch +hwmon-nct6775-fix-in-scaling-factors-for-6798-6799.patch +hwmon-nct6775-increase-and-reorder-alarm-beep-bits.patch +hwmon-nct6775-add-support-for-18-in-readings-for-nct.patch +hwmon-nct6775-additional-temp-registers-for-nct6799.patch +hwmon-nct6775-fix-access-to-temperature-configuratio.patch +hwmon-nct6775-core-fix-number-of-temperature-registe.patch +hwmon-lm90-only-report-alarms-if-driver-is-ready.patch +hwmon-nzxt-smart2-dma-align-output-buffer.patch +net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch +hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch +net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch +forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch +hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch +hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch +hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch +hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch +hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch +hwmon-adt7470-use-cached-pwm-frequency-value.patch +hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch +hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch +powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch +powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch +powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch +wifi-mac80211-validate-individual-twt-params-before-.patch +hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch +bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch +net-phylink-put-link_gpio-if-phylink_create-fails.patch +scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch +scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch +net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch +net-sxgbe-check-descriptor-ring-allocation-failures.patch +can-isotp-check-register_netdevice_notifier-error-in.patch +tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch +octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch +qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch +ksmbd-return-success-for-deferred-final-close.patch +ksmbd-fix-use-after-free-in-__close_file_table_ids.patch diff --git a/queue-6.1/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-6.1/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch new file mode 100644 index 0000000000..829eae832f --- /dev/null +++ b/queue-6.1/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch @@ -0,0 +1,85 @@ +From 4d90846b856f769eb2adf0051871143d6d45d837 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 28 Jun 2026 14:59:09 +0800 +Subject: smb: client: fix buffer leaks in SMB1 read and write + +From: Dawei Feng + +[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ] + +CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request +buffer before checking whether tcon->ses->server is NULL. If that +defensive check ever fails, the helper returns -ECONNABORTED without +releasing the request buffer. + +Fix these leaks by releasing the allocated request buffer before +returning from these error paths. Use cifs_small_buf_release() for the +buffers allocated by small_smb_init() and cifs_buf_release() for the +buffer allocated by smb_init(). + +The bug was first flagged by an experimental analysis tool we are +developing for kernel memory-management bugs while analyzing +v6.13-rc1. The tool is still under development and is not yet publicly +available. Manual inspection confirms that the bug is still +present in v7.1.1. + +An x86_64 allyesconfig build showed no new warnings. + +Runtime validation used a temporary fault-injection hook to force +tcon->ses->server to NULL after request-buffer initialization. On the +unfixed kernel, the harness observed two leaked small request buffers and +one leaked large request buffer, with directed kmemleak dumps confirming +the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer +deltas remained. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Signed-off-by: Dawei Feng +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/client/cifssmb.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c +index 49d7726830044..95cf7e69a411d 100644 +--- a/fs/smb/client/cifssmb.c ++++ b/fs/smb/client/cifssmb.c +@@ -1409,8 +1409,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -1522,8 +1524,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -1776,8 +1780,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +-- +2.53.0 + diff --git a/queue-6.1/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-6.1/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch new file mode 100644 index 0000000000..804e899c2b --- /dev/null +++ b/queue-6.1/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch @@ -0,0 +1,43 @@ +From a3a309d3e4aaa622f57220b9243a2ddf0ed052e5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 21:49:51 +0900 +Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data() + +From: Masami Hiramatsu (Google) + +[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ] + +mmio_reset_data() is called during tracer initialization, reset, and +start. While it resets overrun_detected and prev_overruns, it neglects +to reset dropped_count. Consequently, dropped event counts from prior +tracing sessions persist in dropped_count and corrupt overrun reports +in subsequent runs. + +Fix this by explicitly calling atomic_set(&dropped_count, 0) in +mmio_reset_data(). + +Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2 +Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording") +Assisted-by: Antigravity:gemini-3.6-flash +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_mmiotrace.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c +index 4d9e5c830dbe1..c523ce5aa4958 100644 +--- a/kernel/trace/trace_mmiotrace.c ++++ b/kernel/trace/trace_mmiotrace.c +@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr) + { + overrun_detected = false; + prev_overruns = 0; ++ atomic_set(&dropped_count, 0); + + tracing_reset_online_cpus(&tr->array_buffer); + } +-- +2.53.0 + diff --git a/queue-6.1/wifi-mac80211-validate-individual-twt-params-before-.patch b/queue-6.1/wifi-mac80211-validate-individual-twt-params-before-.patch new file mode 100644 index 0000000000..78f86160d0 --- /dev/null +++ b/queue-6.1/wifi-mac80211-validate-individual-twt-params-before-.patch @@ -0,0 +1,52 @@ +From cb17cce204cc5dff81bd572e26374c6cb2913cb7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 09:09:28 +0800 +Subject: wifi: mac80211: validate individual TWT params before driver setup + +From: Zhao Li + +[ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ] + +ieee80211_process_rx_twt_action() only partially validates a received +S1G TWT setup frame before queueing it. + +An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup() +with twt->length too short for the full struct ieee80211_twt_params. + +The individual path passes twt to drv_add_twt_setup(). Both the tracepoint +and the driver callback consume the complete parameters block, not merely +req_type. Do not pass a short individual agreement to the driver. +Broadcast agreements remain unchanged because they are rejected locally +after accessing only req_type. + +Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode") +Assisted-by: Codex:gpt-5 +Assisted-by: Claude:opus-4.8 +Signed-off-by: Zhao Li +Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com +[edit commit message to not overclaim lack of validation nor + understate driver impact] +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/s1g.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c +index c1f964e9991cd..9914390ff31ff 100644 +--- a/net/mac80211/s1g.c ++++ b/net/mac80211/s1g.c +@@ -100,6 +100,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata, + struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable; + struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + ++ if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) && ++ twt->length < sizeof(twt->control) + sizeof(*twt_agrt)) ++ return; ++ + twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST); + + /* broadcast TWT not supported yet */ +-- +2.53.0 + diff --git a/queue-6.12/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch b/queue-6.12/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch new file mode 100644 index 0000000000..ca5e297e63 --- /dev/null +++ b/queue-6.12/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch @@ -0,0 +1,49 @@ +From 10128405b927c7ca4c11361ec0c5e9e59dce26fc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 02:25:20 +0500 +Subject: accel/qaic: use sizeof(*trans_hdr) for transaction length check + +From: Muhammad Bilal + +[ Upstream commit d6c075f797a672a6e3bd2fd44aee713801698ec2 ] + +In encode_message() the per-transaction lower-bound check compares +trans_hdr->len against sizeof(trans_hdr), i.e. the size of the pointer, +instead of sizeof(*trans_hdr), the size of struct qaic_manage_trans_hdr. + +Every other length check in this file (encode_message() at the loop +guard, decode_message(), etc.) correctly uses sizeof(*trans_hdr), so +this is an inconsistency. On 64-bit builds the pointer and the struct +are both 8 bytes, so the check is correct by coincidence and there is +no behavioural change. On 32-bit builds the pointer is 4 bytes, which +weakens the minimum-length check below the 8-byte header size. + +Use sizeof(*trans_hdr) so the check validates against the actual +transaction header size on all builds. + +Fixes: ea33cb6fc278 ("accel/qaic: tighten bounds checking in encode_message()") +Signed-off-by: Muhammad Bilal +Reviewed-by: Jeff Hugo +Signed-off-by: Jeff Hugo +Link: https://patch.msgid.link/20260617212520.59801-1-meatuni001@gmail.com +Signed-off-by: Sasha Levin +--- + drivers/accel/qaic/qaic_control.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c +index 8eae30fe14f98..8759b53ba38eb 100644 +--- a/drivers/accel/qaic/qaic_control.c ++++ b/drivers/accel/qaic/qaic_control.c +@@ -782,7 +782,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg, + break; + } + trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len); +- if (trans_hdr->len < sizeof(trans_hdr) || ++ if (trans_hdr->len < sizeof(*trans_hdr) || + size_add(user_len, trans_hdr->len) > user_msg->len) { + ret = -EINVAL; + break; +-- +2.53.0 + diff --git a/queue-6.12/ahci-introduce-ahci_ignore_port-helper.patch b/queue-6.12/ahci-introduce-ahci_ignore_port-helper.patch new file mode 100644 index 0000000000..6b90192a77 --- /dev/null +++ b/queue-6.12/ahci-introduce-ahci_ignore_port-helper.patch @@ -0,0 +1,135 @@ +From ef8858a7a9e525f32ce14f8dbe9016d3c8cc94c8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 6 Jan 2025 14:14:47 +0900 +Subject: ahci: Introduce ahci_ignore_port() helper + +From: Damien Le Moal + +[ Upstream commit c9b5be909e6595547ed5d45aef39fd65948aa342 ] + +libahci and AHCI drivers may ignore some ports if the port is invalid +(its ID does not correspond to a valid physical port) or if the user +explicitly requested the port to be ignored with the mask_port_map +ahci module parameter. Such port that shall be ignored can be identified +by checking that the bit corresponding to the port ID is not set in the +mask_port_map field of struct ahci_host_priv. E.g. code such as: +"if (!(hpriv->mask_port_map & (1 << portid)))". + +Replace all direct use of the mask_port_map field to detect such port +with the new helper inline function ahci_ignore_port() to make the code +more readable/easier to understand. + +The comment describing the mask_port_map field of struct ahci_host_priv +is also updated to be more accurate. + +Signed-off-by: Damien Le Moal +Reviewed-by: Niklas Cassel +Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()") +Signed-off-by: Sasha Levin +--- + drivers/ata/ahci.h | 13 ++++++++++++- + drivers/ata/ahci_brcm.c | 2 +- + drivers/ata/ahci_ceva.c | 4 ++-- + drivers/ata/libahci_platform.c | 6 +++--- + 4 files changed, 18 insertions(+), 7 deletions(-) + +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 10a5fe02f0a45..5814245e43ceb 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -329,7 +329,7 @@ struct ahci_port_priv { + struct ahci_host_priv { + /* Input fields */ + unsigned int flags; /* AHCI_HFLAG_* */ +- u32 mask_port_map; /* mask out particular bits */ ++ u32 mask_port_map; /* Mask of valid ports */ + + void __iomem * mmio; /* bus-independent mem map */ + u32 cap; /* cap to use */ +@@ -380,6 +380,17 @@ struct ahci_host_priv { + int port); + }; + ++/* ++ * Return true if a port should be ignored because it is excluded from ++ * the host port map. ++ */ ++static inline bool ahci_ignore_port(struct ahci_host_priv *hpriv, ++ unsigned int portid) ++{ ++ return portid >= hpriv->nports || ++ !(hpriv->mask_port_map & (1 << portid)); ++} ++ + extern int ahci_ignore_sss; + + extern const struct attribute_group *ahci_shost_groups[]; +diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c +index 60505e1e247ab..12bb0c2f914e7 100644 +--- a/drivers/ata/ahci_brcm.c ++++ b/drivers/ata/ahci_brcm.c +@@ -288,7 +288,7 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev, + + /* Re-initialize and calibrate the PHY */ + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + rc = phy_init(hpriv->phys[i]); +diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c +index 6e115da23d3f0..93275b1b48898 100644 +--- a/drivers/ata/ahci_ceva.c ++++ b/drivers/ata/ahci_ceva.c +@@ -206,7 +206,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + goto disable_clks; + + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + rc = phy_init(hpriv->phys[i]); +@@ -218,7 +218,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + ahci_platform_deassert_rsts(hpriv); + + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + rc = phy_power_on(hpriv->phys[i]); +diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c +index b68777841f7a5..53b2c7719dc51 100644 +--- a/drivers/ata/libahci_platform.c ++++ b/drivers/ata/libahci_platform.c +@@ -49,7 +49,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) + int rc, i; + + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + rc = phy_init(hpriv->phys[i]); +@@ -73,7 +73,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) + + disable_phys: + while (--i >= 0) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + phy_power_off(hpriv->phys[i]); +@@ -94,7 +94,7 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) + int i; + + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + phy_power_off(hpriv->phys[i]); +-- +2.53.0 + diff --git a/queue-6.12/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.12/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..e3d0d64c79 --- /dev/null +++ b/queue-6.12/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From 4acbf5f317d386e793f5d49b47188f1f2567e246 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:12:54 +0530 +Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ] + +In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98090->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98090.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c +index 4023b88e7bc13..9253707bccde9 100644 +--- a/sound/soc/codecs/max98090.c ++++ b/sound/soc/codecs/max98090.c +@@ -2388,8 +2388,9 @@ static int max98090_probe(struct snd_soc_component *component) + dev_dbg(component->dev, "max98090_probe\n"); + + max98090->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98090->mclk)) ++ if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + max98090->component = component; + +-- +2.53.0 + diff --git a/queue-6.12/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.12/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..813167a0d8 --- /dev/null +++ b/queue-6.12/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From 5a06924b764743850c3fa90b5262eeabd4e030ef Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:09:50 +0530 +Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ] + +In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98095->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98095.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c +index 7e525d49328d2..3b37f4c4235e6 100644 +--- a/sound/soc/codecs/max98095.c ++++ b/sound/soc/codecs/max98095.c +@@ -1984,8 +1984,9 @@ static int max98095_probe(struct snd_soc_component *component) + int ret = 0; + + max98095->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98095->mclk)) ++ if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + /* reset the codec, the DSP core, and disable all interrupts */ + max98095_reset(component); +-- +2.53.0 + diff --git a/queue-6.12/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-6.12/assoc_array-trim-the-final-shortcut-word-using-the-c.patch new file mode 100644 index 0000000000..ac3503c390 --- /dev/null +++ b/queue-6.12/assoc_array-trim-the-final-shortcut-word-using-the-c.patch @@ -0,0 +1,66 @@ +From 854c51871ececb198789159d2f1fda17ed7d0378 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:05 -0400 +Subject: assoc_array: trim the final shortcut word using the current chunk end + +From: Michael Bommarito + +[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ] + +assoc_array_walk() masks off the bits past shortcut->skip_to_level in the +word that contains skip_to_level, gated on +round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level. + +That guard is wrong in two opposite ways: + + - When sc_level is word-aligned (every word after the first) round_up() + is a no-op, so the guard is sc_level > skip_to_level and never fires for + the word that holds skip_to_level. A shortcut that spans more than one + word and ends in the middle of its last word leaves that word untrimmed, + and its stale high bits leak into the dissimilarity word and can steer + the walk down the wrong descendant. + + - When sc_level is unaligned (the first word) and skip_to_level sits on + the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and + fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears + the whole dissimilarity word and makes a differing shortcut compare + equal. + +Use the end of the chunk that contains sc_level instead: + + skip_to_level < round_down(sc_level, CHUNK) + CHUNK + +For an aligned sc_level whose word holds skip_to_level this now fires (the +first bug); for an unaligned sc_level with skip_to_level on the following +boundary it does not, so shift is never 0 when the branch runs and the trim +never clears the whole word. + +Fixes: 3cb989501c26 ("Add a generic associative array implementation.") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + lib/assoc_array.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/assoc_array.c b/lib/assoc_array.c +index 388e656ac9743..01619e88a52c9 100644 +--- a/lib/assoc_array.c ++++ b/lib/assoc_array.c +@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array, + sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT]; + dissimilarity = segments ^ sc_segments; + +- if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) { ++ if (shortcut->skip_to_level < round_down(sc_level, ++ ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) { + /* Trim segments that are beyond the shortcut */ + int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK; + dissimilarity &= ~(ULONG_MAX << shift); +-- +2.53.0 + diff --git a/queue-6.12/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch b/queue-6.12/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch new file mode 100644 index 0000000000..3ab5e06850 --- /dev/null +++ b/queue-6.12/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch @@ -0,0 +1,76 @@ +From 262563e6066841b74104405bdadc288f265b5bf5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 17 Jul 2026 23:55:26 +0530 +Subject: ata: ahci_ceva: fix error paths in + ceva_ahci_platform_enable_resources() + +From: Radhey Shyam Pandey + +[ Upstream commit 4d99a91574c420decab56cc880fad0dc15b8a7a3 ] + +On phy_init() failure the error path fallsthrough to disable_rsts, which +deasserts the controller reset and then enters disable_phys calling +phy_power_off() on PHYs that were never powered on. That corrupts the PHY +power_count and triggers an extra runtime PM put. + +Use a separate exit_phys path that unwinds with phy_exit() only and falls +through to disable_clks while the controller remains in reset. Reserve +phy_power_off() for the phy_power_on() failure path only, and skip +masked-out ports in both unwind loops. + +On phy_power_on() failure re-assert the controller reset before disabling +clocks and regulators, matching the teardown order used by +ahci_platform_enable_resources() and ahci_platform_disable_resources(). + +Fixes: 26c8404e162b ("ata: ahci_ceva: fix error handling for Xilinx GT PHY support") +Signed-off-by: Radhey Shyam Pandey +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + drivers/ata/ahci_ceva.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c +index 93275b1b48898..b2918ad8e7e05 100644 +--- a/drivers/ata/ahci_ceva.c ++++ b/drivers/ata/ahci_ceva.c +@@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + + rc = phy_init(hpriv->phys[i]); + if (rc) +- goto disable_rsts; ++ goto exit_phys; + } + + /* De-assert the controller reset */ +@@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + + return 0; + +-disable_rsts: +- ahci_platform_deassert_rsts(hpriv); +- + disable_phys: + while (--i >= 0) { ++ if (ahci_ignore_port(hpriv, i)) ++ continue; ++ + phy_power_off(hpriv->phys[i]); + phy_exit(hpriv->phys[i]); + } ++ ahci_platform_assert_rsts(hpriv); ++ goto disable_clks; ++ ++exit_phys: ++ while (--i >= 0) { ++ if (ahci_ignore_port(hpriv, i)) ++ continue; ++ ++ phy_exit(hpriv->phys[i]); ++ } + + disable_clks: + ahci_platform_disable_clks(hpriv); +-- +2.53.0 + diff --git a/queue-6.12/ata-libahci_platform-support-non-consecutive-port-nu.patch b/queue-6.12/ata-libahci_platform-support-non-consecutive-port-nu.patch new file mode 100644 index 0000000000..ff1cde43ca --- /dev/null +++ b/queue-6.12/ata-libahci_platform-support-non-consecutive-port-nu.patch @@ -0,0 +1,178 @@ +From c48c3345edb3c1140ed0a9e764f5412674e5ac86 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 1 Jan 2025 13:13:33 +0100 +Subject: ata: libahci_platform: support non-consecutive port numbers + +From: Josua Mayer + +[ Upstream commit 8c87215dd3a2c814dcffc0bafe8c80c8f98f2574 ] + +So far ahci_platform relied on number of child nodes in firmware to +allocate arrays and expected port numbers to start from 0 without holes. +This number of ports is then set in private structure for use when +configuring phys and regulators. + +Some platforms may not use every port of an ahci controller. +E.g. SolidRUN CN9130 Clearfog uses only port 1 but not port 0, leading +to the following errors during boot: +[ 1.719476] ahci f2540000.sata: invalid port number 1 +[ 1.724562] ahci f2540000.sata: No port enabled + +Update all accessesors of ahci_host_priv phys and target_pwrs arrays to +support holes. Access is gated by hpriv->mask_port_map which has a bit +set for each enabled port. + +Update ahci_platform_get_resources to ignore holes in the port numbers +and enable ports defined in firmware by their reg property only. + +When firmware does not define children it is assumed that there is +exactly one port, using index 0. + +Signed-off-by: Josua Mayer +Reviewed-by: Hans de Goede +Signed-off-by: Damien Le Moal +Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()") +Signed-off-by: Sasha Levin +--- + drivers/ata/ahci_brcm.c | 3 +++ + drivers/ata/ahci_ceva.c | 6 +++++ + drivers/ata/libahci_platform.c | 40 +++++++++++++++++++++++++++++----- + 3 files changed, 43 insertions(+), 6 deletions(-) + +diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c +index 2f16524c25262..60505e1e247ab 100644 +--- a/drivers/ata/ahci_brcm.c ++++ b/drivers/ata/ahci_brcm.c +@@ -288,6 +288,9 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev, + + /* Re-initialize and calibrate the PHY */ + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + rc = phy_init(hpriv->phys[i]); + if (rc) + goto disable_phys; +diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c +index 11a2c199a7c24..6e115da23d3f0 100644 +--- a/drivers/ata/ahci_ceva.c ++++ b/drivers/ata/ahci_ceva.c +@@ -206,6 +206,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + goto disable_clks; + + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + rc = phy_init(hpriv->phys[i]); + if (rc) + goto disable_rsts; +@@ -215,6 +218,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + ahci_platform_deassert_rsts(hpriv); + + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + rc = phy_power_on(hpriv->phys[i]); + if (rc) { + phy_exit(hpriv->phys[i]); +diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c +index 7a8064520a35b..b68777841f7a5 100644 +--- a/drivers/ata/libahci_platform.c ++++ b/drivers/ata/libahci_platform.c +@@ -49,6 +49,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) + int rc, i; + + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + rc = phy_init(hpriv->phys[i]); + if (rc) + goto disable_phys; +@@ -70,6 +73,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) + + disable_phys: + while (--i >= 0) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + phy_power_off(hpriv->phys[i]); + phy_exit(hpriv->phys[i]); + } +@@ -88,6 +94,9 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) + int i; + + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + phy_power_off(hpriv->phys[i]); + phy_exit(hpriv->phys[i]); + } +@@ -432,6 +441,20 @@ static int ahci_platform_get_firmware(struct ahci_host_priv *hpriv, + return 0; + } + ++static u32 ahci_platform_find_max_port_id(struct device *dev) ++{ ++ u32 max_port = 0; ++ ++ for_each_child_of_node_scoped(dev->of_node, child) { ++ u32 port; ++ ++ if (!of_property_read_u32(child, "reg", &port)) ++ max_port = max(max_port, port); ++ } ++ ++ return max_port; ++} ++ + /** + * ahci_platform_get_resources - Get platform resources + * @pdev: platform device to get resources for +@@ -458,6 +481,7 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; + u32 mask_port_map = 0; ++ u32 max_port; + + if (!devres_open_group(dev, NULL, GFP_KERNEL)) + return ERR_PTR(-ENOMEM); +@@ -549,15 +573,17 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, + goto err_out; + } + ++ /* find maximum port id for allocating structures */ ++ max_port = ahci_platform_find_max_port_id(dev); + /* +- * If no sub-node was found, we still need to set nports to +- * one in order to be able to use the ++ * Set nports according to maximum port id. Clamp at ++ * AHCI_MAX_PORTS, warning message for invalid port id ++ * is generated later. ++ * When DT has no sub-nodes max_port is 0, nports is 1, ++ * in order to be able to use the + * ahci_platform_[en|dis]able_[phys|regulators] functions. + */ +- if (child_nodes) +- hpriv->nports = child_nodes; +- else +- hpriv->nports = 1; ++ hpriv->nports = min(AHCI_MAX_PORTS, max_port + 1); + + hpriv->phys = devm_kcalloc(dev, hpriv->nports, sizeof(*hpriv->phys), GFP_KERNEL); + if (!hpriv->phys) { +@@ -625,6 +651,8 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, + * If no sub-node was found, keep this for device tree + * compatibility + */ ++ hpriv->mask_port_map |= BIT(0); ++ + rc = ahci_platform_get_phy(hpriv, 0, dev, dev->of_node); + if (rc) + goto err_out; +-- +2.53.0 + diff --git a/queue-6.12/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch b/queue-6.12/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch new file mode 100644 index 0000000000..8d46017d4b --- /dev/null +++ b/queue-6.12/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch @@ -0,0 +1,44 @@ +From 422ddceb81d0e95e86b7bcf735d87f39f932944b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 15:31:37 -0700 +Subject: ata: sata_mv: accept 1 or 2 resources in platform probe + +From: Rosen Penev + +[ Upstream commit ef19a9cf037957fe3a35df8355c76ff0a63a0436 ] + +Board files in arch/arm/plat-orion, arch/arm/mach-dove, +arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the +"sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ). +Those devices are rejected with -EINVAL, so SATA no longer probes on +legacy Marvell Orion/Kirkwood-style boards. + +Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2 +resources (legacy, IRQ supplied as a second resource) so both probing +paths work. + +Fixes: b3b2bec9646e ("ata: sata_mv: Fixes expected number of resources now IRQs are gone") +Assisted-by: opencode:big-pickle +Signed-off-by: Rosen Penev +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + drivers/ata/sata_mv.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c +index 05c905827dc5c..40fdc422940dd 100644 +--- a/drivers/ata/sata_mv.c ++++ b/drivers/ata/sata_mv.c +@@ -4026,7 +4026,7 @@ static int mv_platform_probe(struct platform_device *pdev) + /* + * Simple resource validation .. + */ +- if (unlikely(pdev->num_resources != 1)) { ++ if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) { + dev_err(&pdev->dev, "invalid number of resources\n"); + return -EINVAL; + } +-- +2.53.0 + diff --git a/queue-6.12/bluetooth-btintel-validate-length-before-parsing-dia.patch b/queue-6.12/bluetooth-btintel-validate-length-before-parsing-dia.patch new file mode 100644 index 0000000000..3a31c2be45 --- /dev/null +++ b/queue-6.12/bluetooth-btintel-validate-length-before-parsing-dia.patch @@ -0,0 +1,40 @@ +From 6f8809af7f3a2ebcd59dba21ea5632021e3be7b9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 01:54:40 -0700 +Subject: Bluetooth: btintel: Validate length before parsing diagnostics TLV + +From: Zijun Hu + +[ Upstream commit b640ff9af3c809ff5ea2077fbba17df1594ec1e4 ] + +btintel_diagnostics() accesses tlv->val[0] without first validating +that the diagnostics VSE is long enough to contain that field, so +may cause reading data beyond the received frame. + +Fix by validating the length before access. + +Fixes: af395330abed ("Bluetooth: btintel: Add Intel devcoredump support") +Signed-off-by: Zijun Hu +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btintel.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c +index 6e3e4a817e727..cb948423a9bd8 100644 +--- a/drivers/bluetooth/btintel.c ++++ b/drivers/bluetooth/btintel.c +@@ -3335,6 +3335,9 @@ int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb) + { + struct intel_tlv *tlv = (void *)&skb->data[5]; + ++ if (skb->len < 5 + sizeof(*tlv) + sizeof(tlv->val[0])) ++ goto recv_frame; ++ + /* The first event is always an event type TLV */ + if (tlv->type != INTEL_TLV_TYPE_ID) + goto recv_frame; +-- +2.53.0 + diff --git a/queue-6.12/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch b/queue-6.12/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch new file mode 100644 index 0000000000..905477d72c --- /dev/null +++ b/queue-6.12/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch @@ -0,0 +1,54 @@ +From d6bc576906b88c8281ddf03cf3ec9d912e81939f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:17 +0300 +Subject: Bluetooth: hci_conn: hold conn reference in abort_conn_sync() + +From: Pauli Virtanen + +[ Upstream commit 5761d003daa987ac81463f570713ce9c9dd204e5 ] + +There is theoretical UAF if the conn is freed while the hci_sync task is +running. + +Hold refcount to avoid that. + +Fixes: 227a0cdf4a02 ("Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_conn.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c +index 93c14e2d66c8a..c5b006d9d974a 100644 +--- a/net/bluetooth/hci_conn.c ++++ b/net/bluetooth/hci_conn.c +@@ -2925,6 +2925,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data) + return hci_abort_conn_sync(hdev, conn, conn->abort_reason); + } + ++static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err) ++{ ++ struct hci_conn *conn = data; ++ ++ hci_conn_put(conn); ++} ++ + int hci_abort_conn(struct hci_conn *conn, u8 reason) + { + struct hci_dev *hdev = conn->hdev; +@@ -2950,6 +2957,9 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) + * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does + * already queue its callback on cmd_sync_work. + */ +- err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); ++ err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn), ++ abort_conn_destroy); ++ if (err) ++ hci_conn_put(conn); + return (err == -EEXIST) ? 0 : err; + } +-- +2.53.0 + diff --git a/queue-6.12/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch b/queue-6.12/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch new file mode 100644 index 0000000000..a33f3323b5 --- /dev/null +++ b/queue-6.12/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch @@ -0,0 +1,43 @@ +From b46e5385dfa1cf84f94da8d1072c1567aa36032a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:22 +0300 +Subject: Bluetooth: hci_sync: fix hci_conn_del() use in + hci_le_create_conn_sync + +From: Pauli Virtanen + +[ Upstream commit 2c1e4e00613dfd105f978be2276e5e265801ec9f ] + +hci_conn_del() caller must hold hdev->lock, check the conn was not +concurrently deleted, and usually inform socket the conn is going to be +deleted. + +Use hci_abort_conn_sync() instead of calling hci_conn_del() without +locks etc. + +Fixes: 8e8b92ee60de5 ("Bluetooth: hci_sync: Add hci_le_create_conn_sync") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_sync.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 111b8f3c77296..8c48d459a5415 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -6593,7 +6593,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + if (hci_dev_test_flag(hdev, HCI_LE_SCAN) && + hdev->le_scan_type == LE_SCAN_ACTIVE && + !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) { +- hci_conn_del(conn); ++ conn->state = BT_OPEN; ++ hci_abort_conn_sync(hdev, conn, ++ HCI_ERROR_REJ_LIMITED_RESOURCES); + hci_conn_put(conn); + return -EBUSY; + } +-- +2.53.0 + diff --git a/queue-6.12/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch b/queue-6.12/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch new file mode 100644 index 0000000000..bd49bf2014 --- /dev/null +++ b/queue-6.12/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch @@ -0,0 +1,64 @@ +From c5e57578c6b08d8c9a779685fff2d85c153f3b54 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 25 Mar 2026 21:07:45 +0200 +Subject: Bluetooth: hci_sync: make hci_cmd_sync_run_once return -EEXIST if + exists + +From: Pauli Virtanen + +[ Upstream commit d288f4db0909c22342eb50cd1632b4d850517281 ] + +hci_cmd_sync_run_once() needs to indicate whether a queue item was +added, so caller can know if callbacks are called, so it can avoid +leaking resources. + +Change the function to return -EEXIST if queue item already exists. + +Modify all callsites vs. the changes. The only callsite is +hci_abort_conn(). + +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: 5761d003daa9 ("Bluetooth: hci_conn: hold conn reference in abort_conn_sync()") +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_conn.c | 4 +++- + net/bluetooth/hci_sync.c | 2 +- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c +index ac98e3f2344e2..93c14e2d66c8a 100644 +--- a/net/bluetooth/hci_conn.c ++++ b/net/bluetooth/hci_conn.c +@@ -2928,6 +2928,7 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data) + int hci_abort_conn(struct hci_conn *conn, u8 reason) + { + struct hci_dev *hdev = conn->hdev; ++ int err; + + /* If abort_reason has already been set it means the connection is + * already being aborted so don't attempt to overwrite it. +@@ -2949,5 +2950,6 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) + * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does + * already queue its callback on cmd_sync_work. + */ +- return hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); ++ err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); ++ return (err == -EEXIST) ? 0 : err; + } +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index d2e8543b5a51e..111b8f3c77296 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -825,7 +825,7 @@ int hci_cmd_sync_run_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, + void *data, hci_cmd_sync_work_destroy_t destroy) + { + if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy)) +- return 0; ++ return -EEXIST; + + return hci_cmd_sync_run(hdev, func, data, destroy); + } +-- +2.53.0 + diff --git a/queue-6.12/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch b/queue-6.12/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch new file mode 100644 index 0000000000..2260403089 --- /dev/null +++ b/queue-6.12/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch @@ -0,0 +1,80 @@ +From bf0aa0cc8c7feb530fd50e157eec3f0a361bafbd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:23 +0300 +Subject: Bluetooth: hci_sync: remove unnecessary hci_conn_get in + create_conn_sync + +From: Pauli Virtanen + +[ Upstream commit c0a9dcd2be398eee505d4b254ec3a845aa8ab189 ] + +hci_conn_get() without already held reference is data race against +concurrent deletion. + +In previous patches, the refcount has been changed to be taken before +starting the hci_sync task, so remove these extra get() + put() as they +are not needed. + +Fixes: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_sync.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 8c48d459a5415..03bbf4c1d6e5a 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -6577,11 +6577,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + + bt_dev_dbg(hdev, "conn %p", conn); + +- /* Hold a reference so conn stays valid for the HCI_CONN_CREATE +- * clear_bit() at done. +- */ +- hci_conn_get(conn); +- + clear_bit(HCI_CONN_SCANNING, &conn->flags); + conn->state = BT_CONNECT; + +@@ -6596,7 +6591,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + conn->state = BT_OPEN; + hci_abort_conn_sync(hdev, conn, + HCI_ERROR_REJ_LIMITED_RESOURCES); +- hci_conn_put(conn); + return -EBUSY; + } + +@@ -6690,7 +6684,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + + /* Re-enable advertising after the connection attempt is finished. */ + hci_resume_advertising_sync(hdev); +- hci_conn_put(conn); + return err; + } + +@@ -6965,11 +6958,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) + else + cp.role_switch = 0x00; + +- /* Hold a reference so conn stays valid for the HCI_CONN_CREATE +- * clear_bit() below. +- */ +- hci_conn_get(conn); +- + /* Mark create connection in flight so hci_cancel_connect_sync() can + * cancel it while blocking on the connection complete event. + */ +@@ -6981,7 +6969,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) + conn->conn_timeout, NULL); + + clear_bit(HCI_CONN_CREATE, &conn->flags); +- hci_conn_put(conn); + + return err; + } +-- +2.53.0 + diff --git a/queue-6.12/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch b/queue-6.12/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch new file mode 100644 index 0000000000..3d1e4d4981 --- /dev/null +++ b/queue-6.12/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch @@ -0,0 +1,188 @@ +From dc376bb410f214396c5ba9c4919a4623527482bd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:31 +0300 +Subject: Bluetooth: ISO: avoid deadlocks in iso_sock_timeout + +From: Pauli Virtanen + +[ Upstream commit 200fa1629c57a3ca2b03d3ca63fd3a9bfd910c43 ] + +iso_sock_timeout() takes lock_sock, so sync disabling the timer while +holding that lock may deadlock. + +iso_sock_timeout() may also run concurrently with iso_conn_del(), which +leads to UAF + + [Task 1] [Task hdev->workqueue] + iso_sock_timeout iso_conn_del + iso_conn_hold_unless_zero iso_chan_del + `------------> iso_conn_put + caller frees hcon + iso_conn_put + iso_conn_free + conn->hcon->iso_data = NULL; /* UAF */ + +Fix the deadlock by removing the disable from the lock_sock sections. +Move the timer from iso_conn to iso_pinfo to decouple it from iso_conn +which may need to be freed in lock_sock section. Convert some of the +clear_timer to disable_timer. + +Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 60 ++++++++++++++++++++++----------------------- + 1 file changed, 29 insertions(+), 31 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index a1c350defcb3a..5b53de4589471 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -30,8 +30,6 @@ struct iso_conn { + spinlock_t lock; + struct sock *sk; + +- struct delayed_work timeout_work; +- + struct sk_buff *rx_skb; + __u32 rx_len; + __u16 tx_sn; +@@ -74,6 +72,7 @@ struct iso_pinfo { + __u8 base_len; + __u8 base[BASE_MAX_LENGTH]; + struct iso_conn *conn; ++ struct delayed_work timeout_work; + }; + + static struct bt_iso_qos default_qos; +@@ -109,9 +108,6 @@ static void iso_conn_free(struct kref *ref) + hci_conn_drop(conn->hcon); + } + +- /* Ensure no more work items will run since hci_conn has been dropped */ +- disable_delayed_work_sync(&conn->timeout_work); +- + kfree_skb(conn->rx_skb); + + kfree(conn); +@@ -152,48 +148,45 @@ static struct sock *iso_sock_hold(struct iso_conn *conn) + + static void iso_sock_timeout(struct work_struct *work) + { +- struct iso_conn *conn = container_of(work, struct iso_conn, +- timeout_work.work); +- struct sock *sk; +- +- conn = iso_conn_hold_unless_zero(conn); +- if (!conn) +- return; +- +- iso_conn_lock(conn); +- sk = iso_sock_hold(conn); +- iso_conn_unlock(conn); +- iso_conn_put(conn); +- +- if (!sk) +- return; ++ struct iso_pinfo *pi = container_of(work, struct iso_pinfo, ++ timeout_work.work); ++ struct sock *sk = &pi->bt.sk; + + BT_DBG("sock %p state %d", sk, sk->sk_state); + + lock_sock(sk); +- sk->sk_err = ETIMEDOUT; +- sk->sk_state_change(sk); ++ if (!sock_flag(sk, SOCK_ZAPPED)) { ++ sk->sk_err = ETIMEDOUT; ++ sk->sk_state_change(sk); ++ } + release_sock(sk); +- sock_put(sk); + } + + static void iso_sock_set_timer(struct sock *sk, long timeout) + { ++ lockdep_assert(lockdep_sock_is_held(sk)); ++ ++ cancel_delayed_work(&iso_pi(sk)->timeout_work); ++ + if (!iso_pi(sk)->conn) + return; + + BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout); +- cancel_delayed_work(&iso_pi(sk)->conn->timeout_work); +- schedule_delayed_work(&iso_pi(sk)->conn->timeout_work, timeout); ++ schedule_delayed_work(&iso_pi(sk)->timeout_work, timeout); + } + + static void iso_sock_clear_timer(struct sock *sk) + { +- if (!iso_pi(sk)->conn) +- return; ++ BT_DBG("sock %p state %d", sk, sk->sk_state); ++ cancel_delayed_work(&iso_pi(sk)->timeout_work); ++} ++ ++static void iso_sock_disable_timer(struct sock *sk) ++{ ++ lockdep_assert(!lockdep_sock_is_held(sk)); + + BT_DBG("sock %p state %d", sk, sk->sk_state); +- cancel_delayed_work(&iso_pi(sk)->conn->timeout_work); ++ disable_delayed_work_sync(&iso_pi(sk)->timeout_work); + } + + /* ---- ISO connections ---- */ +@@ -218,7 +211,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon) + + kref_init(&conn->ref); + spin_lock_init(&conn->lock); +- INIT_DELAYED_WORK(&conn->timeout_work, iso_sock_timeout); + + hcon->iso_data = conn; + conn->hcon = hcon; +@@ -283,8 +275,9 @@ static void iso_conn_del(struct hci_conn *hcon, int err) + return; + } + ++ iso_sock_disable_timer(sk); ++ + lock_sock(sk); +- iso_sock_clear_timer(sk); + iso_chan_del(sk, err); + release_sock(sk); + iso_sock_kill(sk); +@@ -766,6 +759,8 @@ static void iso_sock_cleanup_listen(struct sock *parent) + */ + static void iso_sock_kill(struct sock *sk) + { ++ iso_sock_disable_timer(sk); ++ + lock_sock(sk); + + if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || +@@ -864,8 +859,9 @@ static void __iso_sock_close(struct sock *sk) + /* Must be called on unlocked socket. */ + static void iso_sock_close(struct sock *sk) + { ++ iso_sock_disable_timer(sk); ++ + lock_sock(sk); +- iso_sock_clear_timer(sk); + __iso_sock_close(sk); + release_sock(sk); + } +@@ -934,6 +930,8 @@ static struct sock *iso_sock_alloc(struct net *net, struct socket *sock, + iso_pi(sk)->qos = default_qos; + iso_pi(sk)->sync_handle = -1; + ++ INIT_DELAYED_WORK(&iso_pi(sk)->timeout_work, iso_sock_timeout); ++ + bt_sock_link(&iso_sk_list, sk); + return sk; + } +-- +2.53.0 + diff --git a/queue-6.12/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch b/queue-6.12/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch new file mode 100644 index 0000000000..a9fef244ce --- /dev/null +++ b/queue-6.12/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch @@ -0,0 +1,40 @@ +From d47c5fc619ff386de38abc5e94517921f5f9bbf5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 17:53:33 +0300 +Subject: Bluetooth: ISO: clear iso_data always when detaching conn from hcon + +From: Pauli Virtanen + +[ Upstream commit d57e506f6a1e3929611340fae87c1e4823f4d85c ] + +When setting conn->hcon = NULL, also conn->hcon->iso_data = NULL is +necessary, otherwise later iso_conn_free() will UAF. + +Fix clearing of iso_data in iso_sock_disconn() + +Fixes KASAN: slab-use-after-free in iso_conn_hold_unless_zero on +iso_sock_release() followed by hci_abort_conn_sync(). + +Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 12a01dc6dbde3..1d3e4616ee700 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -811,6 +811,7 @@ static void iso_sock_disconn(struct sock *sk) + sk->sk_state = BT_DISCONN; + iso_conn_lock(iso_pi(sk)->conn); + hci_conn_drop(iso_pi(sk)->conn->hcon); ++ iso_pi(sk)->conn->hcon->iso_data = NULL; + iso_pi(sk)->conn->hcon = NULL; + iso_conn_unlock(iso_pi(sk)->conn); + } +-- +2.53.0 + diff --git a/queue-6.12/bluetooth-iso-fix-leaking-sk-after-socket-release.patch b/queue-6.12/bluetooth-iso-fix-leaking-sk-after-socket-release.patch new file mode 100644 index 0000000000..ab7e531dd1 --- /dev/null +++ b/queue-6.12/bluetooth-iso-fix-leaking-sk-after-socket-release.patch @@ -0,0 +1,108 @@ +From 649540b07cdaa3de1345775782ecd9c926b7870d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:30 +0300 +Subject: Bluetooth: ISO: fix leaking sk after socket release + +From: Pauli Virtanen + +[ Upstream commit ce57442a379212fe3fda59c9437ee8217eceb5b1 ] + +iso_sock_kill() tests !sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || +sock_flag(sk, SOCK_DEAD) for early return, but this is always true since +sock_orphan(sk) sets SOCK_DEAD, so the sk reference released by socket +always leaks, iso_sock_destruct is never called. + +The socket reference also leaks when __iso_sock_close() does not set +SOCK_ZAPPED, since iso_conn_del() does not call iso_sock_kill() after +zapping. + +Fix by replacing SOCK_DEAD by BT_SK_KILLED flag that is not used for +something else, and lock_sock to ensure iso_sock_kill() puts sk only +after socket release only once. Release and iso_conn_del may run +concurrently. Call iso_sock_kill() from iso_conn_del() to clean sk up +after zapping. + +Remove call to iso_sock_kill() from iso_sock_close(), as it's generally +no-op there. + +Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 06c68e053b423..a1c350defcb3a 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -55,6 +55,7 @@ static void iso_sock_kill(struct sock *sk); + enum { + BT_SK_BIG_SYNC, + BT_SK_PA_SYNC, ++ BT_SK_KILLED, + }; + + struct iso_pinfo { +@@ -286,6 +287,7 @@ static void iso_conn_del(struct hci_conn *hcon, int err) + iso_sock_clear_timer(sk); + iso_chan_del(sk, err); + release_sock(sk); ++ iso_sock_kill(sk); + sock_put(sk); + } + +@@ -764,9 +766,13 @@ static void iso_sock_cleanup_listen(struct sock *parent) + */ + static void iso_sock_kill(struct sock *sk) + { ++ lock_sock(sk); ++ + if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || +- sock_flag(sk, SOCK_DEAD)) ++ test_bit(BT_SK_KILLED, &iso_pi(sk)->flags)) { ++ release_sock(sk); + return; ++ } + + BT_DBG("sk %p state %d", sk, sk->sk_state); + +@@ -780,6 +786,9 @@ static void iso_sock_kill(struct sock *sk) + /* Kill poor orphan */ + bt_sock_unlink(&iso_sk_list, sk); + sock_set_flag(sk, SOCK_DEAD); ++ set_bit(BT_SK_KILLED, &iso_pi(sk)->flags); ++ ++ release_sock(sk); + sock_put(sk); + } + +@@ -859,7 +868,6 @@ static void iso_sock_close(struct sock *sk) + iso_sock_clear_timer(sk); + __iso_sock_close(sk); + release_sock(sk); +- iso_sock_kill(sk); + } + + static void iso_sock_init(struct sock *sk, struct sock *parent) +@@ -1867,8 +1875,16 @@ static int iso_sock_release(struct socket *sock) + release_sock(sk); + } + ++ /* Make sure sk is valid even if iso_conn_del() is concurrent */ ++ sock_hold(sk); ++ ++ lock_sock(sk); + sock_orphan(sk); ++ release_sock(sk); ++ + iso_sock_kill(sk); ++ ++ sock_put(sk); + return err; + } + +-- +2.53.0 + diff --git a/queue-6.12/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch b/queue-6.12/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch new file mode 100644 index 0000000000..c9256fc04e --- /dev/null +++ b/queue-6.12/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch @@ -0,0 +1,38 @@ +From 654412fcc7691609b382b27bd510dde83c7868f9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:27 +0300 +Subject: Bluetooth: ISO: fix timeout vs sync_timeout typo in check_bcast_qos + +From: Pauli Virtanen + +[ Upstream commit e9cb51813d79fc9aae4a2098aab3ab6ebd7fb6c8 ] + +In iso.c check_bcast_qos(), missing bcast.timeout is not set to its +default value, and appears typoed as bcast.sync_timeout. + +Fix the typo. + +Fixes: b37cab587aa3 ("Bluetooth: ISO: Don't reject BT_ISO_QOS if parameters are unset") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 1d3e4616ee700..c54e42ab6947b 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -1636,7 +1636,7 @@ static bool check_bcast_qos(struct bt_iso_qos *qos) + return false; + + if (!qos->bcast.timeout) +- qos->bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT; ++ qos->bcast.timeout = BT_ISO_SYNC_TIMEOUT; + + if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000) + return false; +-- +2.53.0 + diff --git a/queue-6.12/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch b/queue-6.12/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch new file mode 100644 index 0000000000..0467fc6c55 --- /dev/null +++ b/queue-6.12/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch @@ -0,0 +1,49 @@ +From a2c3af294a2640b163fd89a77365084dbf3a6404 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:28 +0300 +Subject: Bluetooth: ISO: validate sockaddr_iso first in iso_sock_rebind_bis() + +From: Pauli Virtanen + +[ Upstream commit 4e20192d46a685d73e590a60a4a2419a0a8afcbf ] + +iso_sock_rebind_bis() updates socket iso_pi(sk)->bc_num_bis before +validating the BIS values, so it's possible to end up with bc_num_bis +inconsistent. + +Assign to iso_pi(sk)->bc_num_bis only after validation. + +Fixes: 80837140c1f2 ("Bluetooth: ISO: Allow binding a PA sync socket") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index c54e42ab6947b..06c68e053b423 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -1014,15 +1014,15 @@ static int iso_sock_bind_pa_sk(struct sock *sk, struct sockaddr_iso *sa, + goto done; + } + +- iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis; +- +- for (int i = 0; i < iso_pi(sk)->bc_num_bis; i++) ++ for (int i = 0; i < sa->iso_bc->bc_num_bis; i++) + if (sa->iso_bc->bc_bis[i] < 0x01 || + sa->iso_bc->bc_bis[i] > 0x1f) { + err = -EINVAL; + goto done; + } + ++ iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis; ++ + memcpy(iso_pi(sk)->bc_bis, sa->iso_bc->bc_bis, + iso_pi(sk)->bc_num_bis); + +-- +2.53.0 + diff --git a/queue-6.12/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-6.12/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch new file mode 100644 index 0000000000..5b6733fbdf --- /dev/null +++ b/queue-6.12/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch @@ -0,0 +1,60 @@ +From 134062bd43a64988de24fb821be663dce7e7c831 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 14:48:45 +0800 +Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp + +From: Jiale Yao + +[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ] + +l2cap_le_connect_rsp() obtains a channel via +__l2cap_get_chan_by_ident() but neither holds a reference nor uses +l2cap_chan_hold_unless_zero() before locking and operating on it. +A concurrent l2cap_chan_del() triggered by a remote disconnect can +free the channel between the lookup and l2cap_chan_lock(), causing +a use-after-free. + +The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler +l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero() +to safely hold a reference, but l2cap_le_connect_rsp() was left +unprotected. + +Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup +and l2cap_chan_put() on the exit path, consistent with other L2CAP +response handlers. + +Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request") +Assisted-by: Claude:deepseek-v4-pro +Signed-off-by: Jiale Yao +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/l2cap_core.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index 56337f164d306..c876f986b27f5 100644 +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -4796,6 +4796,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + if (!chan) + return -EBADSLT; + ++ chan = l2cap_chan_hold_unless_zero(chan); ++ if (!chan) ++ return -EBADSLT; ++ + err = 0; + + l2cap_chan_lock(chan); +@@ -4841,6 +4845,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + } + + l2cap_chan_unlock(chan); ++ l2cap_chan_put(chan); + + return err; + } +-- +2.53.0 + diff --git a/queue-6.12/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch b/queue-6.12/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch new file mode 100644 index 0000000000..c5a696371a --- /dev/null +++ b/queue-6.12/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch @@ -0,0 +1,79 @@ +From 620aa39fa2a26a8505e75294d0039299c7e999e2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 4 Jul 2026 17:58:56 +0930 +Subject: btrfs: fix leaking BTRFS_FS_STATE_REMOUNTING flag + +From: Qu Wenruo + +[ Upstream commit 6881f45d0eb541f2cee8c37c84b3860a23823bb3 ] + +[BUG] +The following script can lead to unexpected qgroup rescan failure: + + # mkfs.btrfs -f -O quota $dev + # mount $dev $mnt + # mount -o remount,rescue=ibadroots $mnt + ^^^^^ This above command is expected to fail + + # btrfs quota rescan -w $mnt + ^^^^^ The above qgroup rescan is not expected to fail + + # btrfs qgroup show $mnt + WARNING: qgroup data inconsistent, rescan recommended + Qgroupid Referenced Exclusive Path + -------- ---------- --------- ---- + 0/5 16.00KiB 16.00KiB + +The above short script will be converted to a proper fstests case. + +[CAUSE] +Inside btrfs_reconfigure(), if either btrfs_check_options() or +btrfs_check_features() failed, we will always have +BTRFS_FS_STATE_REMOUNTING set for the fs until the next successful +remount. + +That BTRFS_FS_STATE_REMOUNTING flag will interrupt several operations, +including: + +- Qgroup rescan +- Auto defrag +- Space reclaim + +[FIX] +Change the error handling of btrfs_check_options() and +btrfs_check_features() to goto restore label. + +Fixes: eddb1a433f26 ("btrfs: add reconfigure callback for fs_context") +Reviewed-by: Johannes Thumshirn +Signed-off-by: Qu Wenruo +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/super.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c +index 21419bc33863b..c8ad43f94db0d 100644 +--- a/fs/btrfs/super.c ++++ b/fs/btrfs/super.c +@@ -1509,12 +1509,14 @@ static int btrfs_reconfigure(struct fs_context *fc) + sync_filesystem(sb); + set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state); + +- if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) +- return -EINVAL; ++ if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) { ++ ret = -EINVAL; ++ goto restore; ++ } + + ret = btrfs_check_features(fs_info, !(fc->sb_flags & SB_RDONLY)); + if (ret < 0) +- return ret; ++ goto restore; + + btrfs_ctx_to_info(fs_info, ctx); + btrfs_remount_begin(fs_info, old_ctx.mount_opt, fc->sb_flags); +-- +2.53.0 + diff --git a/queue-6.12/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch b/queue-6.12/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch new file mode 100644 index 0000000000..ccd9112c51 --- /dev/null +++ b/queue-6.12/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch @@ -0,0 +1,77 @@ +From fa601d17fc8d2cc253ad5a7477c23ca0bce3553a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 3 Jul 2026 07:54:40 +0200 +Subject: btrfs: zoned: fix deadlock between metadata writeback and transaction + commit + +From: Johannes Thumshirn + +[ Upstream commit 1ebe51c29fa9755d5b2fea28727c051117907cf8 ] + +When writing out metadata extent buffers in a zoned filesystem, +btree_writepages() holds fs_info->zoned_meta_io_lock across the whole +writeback loop, including the call to btrfs_check_meta_write_pointer() -> +check_bg_is_active(). + +For the tree-log block group, check_bg_is_active() may fail to activate +the zone and fall back to btrfs_zone_finish_one_bg() to free an active +zone. That path waits for the running transaction to commit while still +holding zoned_meta_io_lock, but the committer needs that same lock to +write out the tree extents, so the two tasks deadlock: + + Task A (kworker, metadata writeback) Task B (fsstress, transaction commit) + ------------------------------------ ------------------------------------- + wb_workfn() btrfs_commit_transaction(T) + btree_writepages() btrfs_write_and_wait_transaction() + btrfs_zoned_meta_io_lock() btrfs_write_marked_extents() + btrfs_check_meta_write_pointer() btree_writepages() + check_bg_is_active() [treelog_bg] btrfs_zoned_meta_io_lock() + btrfs_zone_finish_one_bg() + do_zone_finish() + btrfs_inc_block_group_ro() + btrfs_wait_for_commit() + + +The sibling branch in check_bg_is_active() already drops zoned_meta_io_lock +around do_zone_finish() for this exact reason. Do the same in the tree-log +branch: release the lock around btrfs_zone_finish_one_bg() and re-acquire +it afterwards. The lock only protects fs_info->active_{meta,system}_bg, +which this branch does not touch, and ctx->zoned_bg keeps a reference to +the block group across the unlock, so nothing is lost while the lock +is dropped. + +This hang occasionally reproduces with fstests generic/475 on a zoned +btrfs filesystem. + +Fixes: 13bb483d32ab ("btrfs: zoned: activate metadata block group on write time") +Reviewed-by: Naohiro Aota +Signed-off-by: Johannes Thumshirn +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/zoned.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c +index d9c26f4be6634..c64840b155974 100644 +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -2122,7 +2122,11 @@ static bool check_bg_is_active(struct btrfs_eb_write_context *ctx, + + if (fs_info->treelog_bg == block_group->start) { + if (!btrfs_zone_activate(block_group)) { +- int ret_fin = btrfs_zone_finish_one_bg(fs_info); ++ int ret_fin; ++ ++ btrfs_zoned_meta_io_unlock(fs_info); ++ ret_fin = btrfs_zone_finish_one_bg(fs_info); ++ btrfs_zoned_meta_io_lock(fs_info); + + if (ret_fin != 1 || !btrfs_zone_activate(block_group)) + return false; +-- +2.53.0 + diff --git a/queue-6.12/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-6.12/can-isotp-check-register_netdevice_notifier-error-in.patch new file mode 100644 index 0000000000..7c3948b9e4 --- /dev/null +++ b/queue-6.12/can-isotp-check-register_netdevice_notifier-error-in.patch @@ -0,0 +1,57 @@ +From 4d89225fb4ee31005481671fc696246d8131cbda Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Jul 2026 16:56:56 +0800 +Subject: can: isotp: check register_netdevice_notifier() error in module init + +From: Minhong He + +[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ] + +Register the netdevice notifier before can_proto_register() and check the +return value. If protocol registration fails, unregister the notifier +before returning the error. + +Align isotp_module_init() with the reordering already done for raw.c +(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and +bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization +in bcm_module_init()")). + +Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier") +Signed-off-by: Minhong He +Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn +Signed-off-by: Marc Kleine-Budde +Signed-off-by: Sasha Levin +--- + net/can/isotp.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/net/can/isotp.c b/net/can/isotp.c +index b2622c881aa32..7e3d6afbb24f9 100644 +--- a/net/can/isotp.c ++++ b/net/can/isotp.c +@@ -1885,13 +1885,18 @@ static __init int isotp_module_init(void) + + pr_info("can: isotp protocol (max_pdu_size %d)\n", max_pdu_size); + ++ err = register_netdevice_notifier(&canisotp_notifier); ++ if (err) ++ return err; ++ + err = can_proto_register(&isotp_can_proto); +- if (err < 0) ++ if (err < 0) { + pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err)); +- else +- register_netdevice_notifier(&canisotp_notifier); ++ unregister_netdevice_notifier(&canisotp_notifier); ++ return err; ++ } + +- return err; ++ return 0; + } + + static __exit void isotp_module_exit(void) +-- +2.53.0 + diff --git a/queue-6.12/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch b/queue-6.12/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch new file mode 100644 index 0000000000..9c9e4fdea1 --- /dev/null +++ b/queue-6.12/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch @@ -0,0 +1,65 @@ +From e1a2c6a57346c8c06923b8dc8509cd107d91b73d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 25 May 2026 10:15:50 -0400 +Subject: dmaengine: idxd: fix fdev setup failure cleanup in idxd_cdev_open() + +From: Yuho Choi + +[ Upstream commit ee1d7274102285d78a53161fc705a8d8cd40b066 ] + +The failed_dev_add and failed_dev_name paths drop the file-device +reference while wq->wq_lock is still held. If put_device(fdev) drops the +last reference, idxd_file_dev_release() runs synchronously and tries to +take wq->wq_lock again, deadlocking. + +Those paths also fall through into the later ctx cleanup labels even +though idxd_file_dev_release() owns that cleanup and frees ctx. This can +make idxd_xa_pasid_remove(ctx) and kfree(ctx) operate on a freed context. + +Move idxd_wq_get() before file-device setup can fail, since the release +callback always calls idxd_wq_put(). Then unlock wq->wq_lock before +put_device(fdev) and return directly from the file-device setup failure +path, leaving ctx cleanup to the release callback. + +Fixes: e6fd6d7e5f0fe ("dmaengine: idxd: add a device to represent the file opened") +Signed-off-by: Yuho Choi +Reviewed-by: Dave Jiang +Acked-by: Vinicius Costa Gomes +Link: https://patch.msgid.link/20260525141550.1385581-1-dbgh9129@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/dma/idxd/cdev.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c +index 8dcd2331bb1ac..14b322ac6e6c6 100644 +--- a/drivers/dma/idxd/cdev.c ++++ b/drivers/dma/idxd/cdev.c +@@ -293,6 +293,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) + fdev->parent = cdev_dev(idxd_cdev); + fdev->bus = &dsa_bus_type; + fdev->type = &idxd_cdev_file_type; ++ idxd_wq_get(wq); + + rc = dev_set_name(fdev, "file%d", ctx->id); + if (rc < 0) { +@@ -306,13 +307,14 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) + goto failed_dev_add; + } + +- idxd_wq_get(wq); + mutex_unlock(&wq->wq_lock); + return 0; + + failed_dev_add: + failed_dev_name: ++ mutex_unlock(&wq->wq_lock); + put_device(fdev); ++ return rc; + failed_ida: + failed_set_pasid: + if (device_user_pasid_enabled(idxd)) +-- +2.53.0 + diff --git a/queue-6.12/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-6.12/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch new file mode 100644 index 0000000000..4ccc4319ff --- /dev/null +++ b/queue-6.12/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch @@ -0,0 +1,60 @@ +From cefa1bd14be4e30d76167c7fd3bb7ac129653ba0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 1 Jul 2026 12:57:33 +0800 +Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA + +From: Hongling Zeng + +[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ] + +When terminating DMA transfers, active descriptors are not properly +reclaimed. Only cyclic descriptors were handled, leaving non-cyclic +descriptors and their LLI chains to be permanently leaked. + +Fix by using vchan_terminate_vdesc() which handles both cyclic and +non-cyclic descriptors by adding them to desc_terminated queue for +proper cleanup. + +Add pchan->desc != pchan->done check to prevent double-adding completed +descriptors, which would corrupt the list. + +Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller") +Signed-off-by: Hongling Zeng +Acked-by: Jernej Skrabec +Suggested-by: Frank Li +Reviewed-by: Frank Li +Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/dma/sun6i-dma.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c +index c97027379263d..1f1e21d48ad76 100644 +--- a/drivers/dma/sun6i-dma.c ++++ b/drivers/dma/sun6i-dma.c +@@ -934,16 +934,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan) + + spin_lock_irqsave(&vchan->vc.lock, flags); + +- if (vchan->cyclic) { +- vchan->cyclic = false; +- if (pchan && pchan->desc) { +- struct virt_dma_desc *vd = &pchan->desc->vd; +- struct virt_dma_chan *vc = &vchan->vc; ++ if (pchan && pchan->desc && pchan->desc != pchan->done) { ++ struct virt_dma_desc *vd = &pchan->desc->vd; + +- list_add_tail(&vd->node, &vc->desc_completed); +- } ++ vchan_terminate_vdesc(vd); + } + ++ vchan->cyclic = false; + vchan_get_all_descriptors(&vchan->vc, &head); + + if (pchan) { +-- +2.53.0 + diff --git a/queue-6.12/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch b/queue-6.12/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch new file mode 100644 index 0000000000..9daa089875 --- /dev/null +++ b/queue-6.12/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch @@ -0,0 +1,55 @@ +From 6ae2dec37a9c63d3a8edd417463590c505cfd547 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 17:32:15 +0200 +Subject: Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep + annotation + +From: Sebastian Andrzej Siewior + +[ Upstream commit 8c7ab779c8850f4dab8473463cca9a7d52fdaecc ] + +lockdep_hardirq_threaded() is supposed to be used within IRQ core code +and not within drivers. It is not obvious from within the driver, that +this is the only interrupt service routing and that it is not shared +handler. + +Replace lockdep_hardirq_threaded() with a lockdep annotation limiting +threaded context on PREEMPT_RT to __vmbus_isr(). + +Fixes: f8e6343b7a89c ("Drivers: hv: vmbus: Use kthread for vmbus interrupts on PREEMPT_RT") +Signed-off-by: Sebastian Andrzej Siewior +Reviewed-by: Michael Kelley +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/vmbus_drv.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c +index 83f3215bc5bd5..db1c71832f2a8 100644 +--- a/drivers/hv/vmbus_drv.c ++++ b/drivers/hv/vmbus_drv.c +@@ -1320,8 +1320,19 @@ static void vmbus_isr(void) + if (IS_ENABLED(CONFIG_PREEMPT_RT)) { + vmbus_irqd_wake(); + } else { +- lockdep_hardirq_threaded(); ++ static DEFINE_WAIT_OVERRIDE_MAP(vmbus_map, LD_WAIT_CONFIG); ++ ++ /* ++ * vmbus_isr is never force-threaded and always invoked at hard ++ * IRQ level. __vmbus_isr() below can acquire a spinlock_t ++ * which becomes a sleeping lock and must not be acquired in ++ * this context. Therefore on PREEMPT_RT this will be threaded ++ * via vmbus_irqd_wake(). On non-PREEMPT the annotation lets ++ * lockdep know that acquiring a spinlock_t is not an issue. ++ */ ++ lock_map_acquire_try(&vmbus_map); + __vmbus_isr(); ++ lock_map_release(&vmbus_map); + } + } + +-- +2.53.0 + diff --git a/queue-6.12/drm-mediatek-check-crtc-state-before-freeing.patch b/queue-6.12/drm-mediatek-check-crtc-state-before-freeing.patch new file mode 100644 index 0000000000..4da5843e8f --- /dev/null +++ b/queue-6.12/drm-mediatek-check-crtc-state-before-freeing.patch @@ -0,0 +1,51 @@ +From afaf46be6995ffa18203a22b5b2dcf836b63ab48 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 23:05:28 +0800 +Subject: drm/mediatek: Check CRTC state before freeing + +From: Ruoyu Wang + +[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ] + +mtk_crtc_reset() destroys the current CRTC state only when crtc->state +is non-NULL, but it always converts crtc->state to struct mtk_crtc_state +and passes the result to kfree(). + +When reset is called without an existing state, container_of(NULL, ...) +does not produce NULL. Keep the mtk state free in the same crtc->state +guard as the helper state destruction. + +This issue was found by a static analysis checker and confirmed by +manual source review. + +Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset") +Signed-off-by: Ruoyu Wang +Reviewed-by: CK Hu +Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/ +Signed-off-by: Chun-Kuang Hu +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/mediatek/mtk_crtc.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c +index c4c6d0249df56..aa8e6f6ddcd32 100644 +--- a/drivers/gpu/drm/mediatek/mtk_crtc.c ++++ b/drivers/gpu/drm/mediatek/mtk_crtc.c +@@ -153,10 +153,10 @@ static void mtk_crtc_reset(struct drm_crtc *crtc) + { + struct mtk_crtc_state *state; + +- if (crtc->state) ++ if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); +- +- kfree(to_mtk_crtc_state(crtc->state)); ++ kfree(to_mtk_crtc_state(crtc->state)); ++ } + crtc->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-6.12/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-6.12/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch new file mode 100644 index 0000000000..840877d36b --- /dev/null +++ b/queue-6.12/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch @@ -0,0 +1,47 @@ +From 802f89fdfd20e58b4b66a34eb84c8f28a3191e9b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 17:26:37 +0800 +Subject: forcedeth: fix UAF of txrx_stats in nv_remove + +From: Chenguang Zhao + +[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ] + +nv_remove() frees the per-CPU txrx_stats before unregister_netdev(). +Until unregister completes, ndo_get_stats64, the NAPI/xmit data path, +and nv_close()/drain may still access txrx_stats, leading to a +use-after-free. + +Free the stats only after unregister_netdev(). + +Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Reviewed-by: Zhu Yanjun +Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/nvidia/forcedeth.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c +index 720f577929dbf..11eb3751ad27b 100644 +--- a/drivers/net/ethernet/nvidia/forcedeth.c ++++ b/drivers/net/ethernet/nvidia/forcedeth.c +@@ -6199,10 +6199,10 @@ static void nv_remove(struct pci_dev *pci_dev) + struct net_device *dev = pci_get_drvdata(pci_dev); + struct fe_priv *np = netdev_priv(dev); + +- free_percpu(np->txrx_stats); +- + unregister_netdev(dev); + ++ free_percpu(np->txrx_stats); ++ + nv_restore_mac_addr(pci_dev); + + /* restore any phy related changes */ +-- +2.53.0 + diff --git a/queue-6.12/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch b/queue-6.12/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch new file mode 100644 index 0000000000..030f58b9a3 --- /dev/null +++ b/queue-6.12/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch @@ -0,0 +1,55 @@ +From bcca6a0d8f2e509af85196c5f0aed86896b16d25 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 13:23:08 +0530 +Subject: gpio: sloppy-logic-analyzer: Fix memory leak in gpio_la_poll_probe() + +From: Abdun Nihaal + +[ Upstream commit 7a7baebd9f23ba4f24796775472b2fd00dcd95d9 ] + +The memory allocated for priv->blob.data is not freed in the error paths +that follow the fops_buf_size_set() call in gpio_la_poll_probe(), as +well as in the remove function. Fix that by using device managed action +to free the memory on remove. + +Fixes: 7828b7bbbf20 ("gpio: add sloppy logic analyzer using polling") +Signed-off-by: Abdun Nihaal +Reviewed-by: Wolfram Sang +Link: https://patch.msgid.link/20260715075311.527753-1-nihaal@cse.iitm.ac.in +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-sloppy-logic-analyzer.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c +index 59a8f3a5c4e48..9c526aeadeba5 100644 +--- a/drivers/gpio/gpio-sloppy-logic-analyzer.c ++++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c +@@ -161,6 +161,13 @@ static int fops_buf_size_get(void *data, u64 *val) + return 0; + } + ++static void fops_buf_release(void *data) ++{ ++ struct gpio_la_poll_priv *priv = data; ++ ++ vfree(priv->blob.data); ++} ++ + static int fops_buf_size_set(void *data, u64 val) + { + struct gpio_la_poll_priv *priv = data; +@@ -239,6 +246,9 @@ static int gpio_la_poll_probe(struct platform_device *pdev) + return ret; + + fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE); ++ ret = devm_add_action_or_reset(dev, fops_buf_release, priv); ++ if (ret) ++ return ret; + + priv->descs = devm_gpiod_get_array(dev, "probe", GPIOD_IN); + if (IS_ERR(priv->descs)) +-- +2.53.0 + diff --git a/queue-6.12/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-6.12/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch new file mode 100644 index 0000000000..3abd48cf89 --- /dev/null +++ b/queue-6.12/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch @@ -0,0 +1,51 @@ +From 7def045476cbcdccffe6ed6d4b309ca6df7e9479 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:19 -0300 +Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread + +From: Luiz Angelo Daros de Luca + +[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ] + +When userspace configures 'auto_update_interval' to 0 via sysfs, the +background kthread executes schedule_timeout_interruptible(0), which +returns immediately. + +If 'num_temp_sensors' is concurrently or previously set to 0, the +msleep_interruptible() delay inside adt7470_read_temperatures() also +becomes 0. This combination forces the background thread into a tight, +unbounded busy-loop, hogging the CPU and flooding the I2C bus with a +continuous stream of transactions. + +Fix this vulnerability by raising the lower limit of the clamp_val in +auto_update_interval_store() from 0 to 500 milliseconds. This guarantees +a reasonable minimum sleep window between sensor updates, protecting the +system from intentional or accidental I2C bus denial of service. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org +Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 06b19fd382457..a927e0b6d3319 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev, + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + +- temp = clamp_val(temp, 0, 60000); ++ temp = clamp_val(temp, 500, 60000); + + mutex_lock(&data->lock); + data->auto_update_interval = temp; +-- +2.53.0 + diff --git a/queue-6.12/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch b/queue-6.12/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch new file mode 100644 index 0000000000..7f39f62500 --- /dev/null +++ b/queue-6.12/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch @@ -0,0 +1,83 @@ +From 6304effb41b2403128474055a83ca1b901ae8ff8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:18 -0300 +Subject: hwmon: (adt7470) Fix cache updated before hardware write on I2C error + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ] + +adt7470_temp_write() and adt7470_pwm_write() update the driver's +cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing +the corresponding regmap_write(), and never check whether the write +succeeded before committing that update. If the I2C transaction fails, +the function correctly propagates the error to the caller, but the cache +silently keeps the new value, which was never actually applied to the +hardware. Subsequent reads then report a value that does not match the +device state. + +Reorder both write paths to update the cache only after a successful +regmap_write(), so the cache always reflects what was actually +written to the hardware. + +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index ef8f411df61be..06b19fd382457 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va + switch (attr) { + case hwmon_temp_min: + mutex_lock(&data->lock); +- data->temp_min[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val); ++ if (!err) ++ data->temp_min[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_temp_max: + mutex_lock(&data->lock); +- data->temp_max[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val); ++ if (!err) ++ data->temp_max[channel] = val; + mutex_unlock(&data->lock); + break; + default: +@@ -831,9 +833,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + case hwmon_pwm_input: + val = clamp_val(val, 0, 255); + mutex_lock(&data->lock); +- data->pwm[channel] = val; + err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), +- data->pwm[channel]); ++ val); ++ if (!err) ++ data->pwm[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_pwm_enable: +@@ -847,10 +850,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + val--; + + mutex_lock(&data->lock); +- data->pwm_automatic[channel] = val; + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel), + pwm_auto_reg_mask, + val ? pwm_auto_reg_mask : 0); ++ if (!err) ++ data->pwm_automatic[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_pwm_freq: +-- +2.53.0 + diff --git a/queue-6.12/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch b/queue-6.12/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch new file mode 100644 index 0000000000..5c221b4ad4 --- /dev/null +++ b/queue-6.12/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch @@ -0,0 +1,80 @@ +From bb83793a4f35c487fe921041d8fa49b7eb388a83 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:23 -0300 +Subject: hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ] + +If the fan data becomes 0 between the FAN_DATA_VALID() check and the +FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash +due to a race with a concurrent update of the cached fan value. + +Fix a TOCTOU issue by reading fan data once. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/ +Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 23 ++++++++++------------- + 1 file changed, 10 insertions(+), 13 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 43c0130342622..3a2d408a45be8 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev, + static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val) + { + struct adt7470_data *data = adt7470_update_device(dev); ++ u16 fan_data; + + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (attr) { + case hwmon_fan_input: +- if (FAN_DATA_VALID(data->fan[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan[channel]); + break; + case hwmon_fan_min: +- if (FAN_DATA_VALID(data->fan_min[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan_min[channel]); + break; + case hwmon_fan_max: +- if (FAN_DATA_VALID(data->fan_max[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan_max[channel]); + break; + case hwmon_fan_alarm: + *val = !!(data->alarm & FAN_ALARM_BIT(channel)); +- break; ++ return 0; + default: + return -EOPNOTSUPP; + } + ++ if (FAN_DATA_VALID(fan_data)) ++ *val = FAN_PERIOD_TO_RPM(fan_data); ++ else ++ *val = 0; ++ + return 0; + } + +-- +2.53.0 + diff --git a/queue-6.12/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch b/queue-6.12/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch new file mode 100644 index 0000000000..db37a30cb3 --- /dev/null +++ b/queue-6.12/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch @@ -0,0 +1,115 @@ +From dc9f40e665408656450242c83fff4c80a92bcd74 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:17 -0300 +Subject: hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ] + +During adt7470_read_temperatures(), the driver temporarily switches +the PWM channels to manual mode, performs the temperature collection, +and then restores the original configuration registers. + +However, if an I2C transaction fails at any point after entering manual +mode, the function aborts and returns immediately. This leaves the +configuration registers un-restored, permanently trapping the fans in +manual mode. + +Introduce a recovery path to ensure that the original PWM configuration +registers are always restored, even when intermediate I2C operations +fail. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++----------- + 1 file changed, 29 insertions(+), 11 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index dbee6926fa055..ef8f411df61be 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in + /* Probe for temperature sensors. Assumes lock is held */ + static int adt7470_read_temperatures(struct adt7470_data *data) + { +- unsigned long res; ++ struct device *dev = regmap_get_device(data->regmap); ++ u8 pwm[ADT7470_FAN_COUNT]; + unsigned int pwm_cfg[2]; +- int err; ++ unsigned long res; ++ int err, err2; + int i; +- u8 pwm[ADT7470_FAN_COUNT]; + + /* save pwm[1-4] config register */ + err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); +@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data) + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), + ADT7470_PWM_AUTO_MASK, 0); + if (err < 0) +- return err; ++ goto out_restore; + + /* write pwm control to whatever it was */ + err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], + ADT7470_PWM_COUNT); + if (err < 0) +- return err; ++ goto out_restore; + + /* start reading temperature sensors */ + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); + if (err < 0) +- return err; ++ goto out_restore; + + /* Delay is 200ms * number of temp sensors. */ + res = msleep_interruptible((data->num_temp_sensors >= 0 ? +@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data) + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, 0); + if (err < 0) +- return err; ++ goto out_restore; + ++out_restore: + /* restore pwm[1-4] config registers */ +- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); +- if (err < 0) +- return err; +- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); ++ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); ++ if (err2 < 0) { ++ dev_warn_ratelimited(dev, ++ "failed to restore PWM{1,2} config (%d)\n", ++ err2); ++ ++ if (!err) ++ err = err2; ++ } ++ ++ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); ++ if (err2 < 0) { ++ dev_warn_ratelimited(dev, ++ "failed to restore PWM{3,4} config (%d)\n", ++ err2); ++ ++ if (!err) ++ err = err2; ++ } ++ + if (err < 0) + return err; + +-- +2.53.0 + diff --git a/queue-6.12/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-6.12/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch new file mode 100644 index 0000000000..366a71d6b3 --- /dev/null +++ b/queue-6.12/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch @@ -0,0 +1,57 @@ +From 7e42adb13766ac36489ac5b17d728329a71128e0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:24 -0300 +Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ] + +In pwm_auto_temp_store(), the parsed user input was missing bounds +checks, allowing values > 0xF to overflow into the adjacent channel's +bits. Furthermore, the value was being incorrectly written to the +pwm_automatic state array instead of pwm_auto_temp. + +Fix this by rejecting values > 0xF with -EINVAL, and assigning the +value to the correct array only after a successful I2C write. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 3a2d408a45be8..0fad4bfe53df6 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -1049,8 +1049,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + if (temp < 0) + return temp; + ++ if (temp > 0xF) ++ return -EINVAL; ++ + mutex_lock(&data->lock); +- data->pwm_automatic[attr->index] = temp; + + if (!(attr->index % 2)) { + mask = 0xF0; +@@ -1061,6 +1063,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + } + + err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); ++ if (!err) ++ data->pwm_auto_temp[attr->index] = temp; ++ + mutex_unlock(&data->lock); + + return err < 0 ? err : count; +-- +2.53.0 + diff --git a/queue-6.12/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-6.12/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch new file mode 100644 index 0000000000..0808b98e72 --- /dev/null +++ b/queue-6.12/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch @@ -0,0 +1,62 @@ +From 3cd57f54c689f2a5c26fc723ca6ae56dc7144b09 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:20 -0300 +Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks + +From: Luiz Angelo Daros de Luca + +[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ] + +The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are +currently defined with swapped bit values. + +According to Table 22 of the ADT7470 datasheet, the Fan Control Mode +Configuration for register 0x69 follows the exact same bit position +layout as register 0x68: +- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80 +- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40 +- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80 +- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40 + +Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40. + +This typo did not cause any functional bugs because these specific +macros are never referenced in the driver code. Instead, the driver +correctly applies the configuration by relying on the modulo parity of +the channel index (e.g., `channel % 2`) to selectively apply either +ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40). +Since the bit layout is identical between the two configuration +registers, the hardware is currently configured correctly. + +Fix the macro definitions to reflect the datasheet accurately and +prevent future bugs or confusion during code review and refactoring. +As this is a purely cosmetic fix with no functional impact, a backport +to stable kernels is not necessary. + +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 41fa6a0e8b937..f0a45538ded6d 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + #define ADT7470_PWM1_AUTO_MASK 0x80 + #define ADT7470_PWM_AUTO_MASK 0xC0 + #define ADT7470_REG_PWM34_CFG 0x69 +-#define ADT7470_PWM3_AUTO_MASK 0x40 +-#define ADT7470_PWM4_AUTO_MASK 0x80 ++#define ADT7470_PWM4_AUTO_MASK 0x40 ++#define ADT7470_PWM3_AUTO_MASK 0x80 + #define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A + #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D + #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E +-- +2.53.0 + diff --git a/queue-6.12/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch b/queue-6.12/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch new file mode 100644 index 0000000000..442d8e5f20 --- /dev/null +++ b/queue-6.12/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch @@ -0,0 +1,71 @@ +From 803427a90180b4ee8f889e29494c29fb88efe2e0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:21 -0300 +Subject: hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read() + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ] + +During the conversion the alarm callback started interpreting the +channel index as an alarm bitmask, resulting in incorrect alarm +reporting. Compute the proper alarm bit instead. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org +Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 19 +++++++++++++++++-- + 1 file changed, 17 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index a927e0b6d3319..41fa6a0e8b937 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + + #define ALARM2(x) ((x) << 8) + ++/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and ++ * 0x72, or BIT(0)..BIT(6) of data->alarm. ++ * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and ++ * 0x73, or BIT(8)..BIT(10) of data->alarm. ++ */ ++#define TEMP_ALARM_BIT(ch) ({ \ ++ typeof(ch) _ch = (ch); \ ++ (1 << (_ch < 7 ? _ch : _ch + 1)); \ ++}) ++ ++/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in ++ * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm. ++ */ ++#define FAN_ALARM_BIT(ch) (1 << (12 + (ch))) ++ + #define ADT7470_VENDOR 0x41 + #define ADT7470_DEVICE 0x70 + /* datasheet only mentions a revision 2 */ +@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va + *val = 1000 * data->temp_max[channel]; + break; + case hwmon_temp_alarm: +- *val = !!(data->alarm & channel); ++ *val = !!(data->alarm & TEMP_ALARM_BIT(channel)); + break; + default: + return -EOPNOTSUPP; +@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val + *val = 0; + break; + case hwmon_fan_alarm: +- *val = !!(data->alarm & (1 << (12 + channel))); ++ *val = !!(data->alarm & FAN_ALARM_BIT(channel)); + break; + default: + return -EOPNOTSUPP; +-- +2.53.0 + diff --git a/queue-6.12/hwmon-adt7470-use-cached-pwm-frequency-value.patch b/queue-6.12/hwmon-adt7470-use-cached-pwm-frequency-value.patch new file mode 100644 index 0000000000..1f7cfce78f --- /dev/null +++ b/queue-6.12/hwmon-adt7470-use-cached-pwm-frequency-value.patch @@ -0,0 +1,111 @@ +From 21c7cb69dd5567b8b8b2940b14283c7d99cea692 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:22 -0300 +Subject: hwmon: (adt7470) Use cached PWM frequency value + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ] + +adt7470_pwm_read() currently ignores failures returned by +pwm1_freq_get(). If the register read fails, the negative error code is +returned through *val while the function itself reports success, +potentially exposing a negative PWM frequency through sysfs. + +Fix this by using the cached PWM frequency maintained by the driver, +eliminating the register access from the read path. + +Apart from the corrected error propagation and using the cached value, +no functional change is intended. + +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index f0a45538ded6d..43c0130342622 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -182,6 +182,7 @@ struct adt7470_data { + u8 pwm_min[ADT7470_PWM_COUNT]; + s8 pwm_tmin[ADT7470_PWM_COUNT]; + u8 pwm_auto_temp[ADT7470_PWM_COUNT]; ++ u32 pwm_freq; + + struct task_struct *auto_update; + unsigned int auto_update_interval; +@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev, + } + + /* These are the valid PWM frequencies to the nearest Hz */ +-static const int adt7470_freq_map[] = { ++static const u32 adt7470_freq_map[] = { + 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 + }; + +@@ -796,7 +797,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val + *val = 1 + data->pwm_automatic[channel]; + break; + case hwmon_pwm_freq: +- *val = pwm1_freq_get(dev); ++ *val = data->pwm_freq; + break; + default: + return -EOPNOTSUPP; +@@ -809,12 +810,14 @@ static int pwm1_freq_set(struct device *dev, long freq) + { + struct adt7470_data *data = dev_get_drvdata(dev); + unsigned int low_freq = ADT7470_CFG_LF; ++ u32 closest_freq; + int index; + int err; + + /* Round the user value given to the closest available frequency */ + index = find_closest(freq, adt7470_freq_map, + ARRAY_SIZE(adt7470_freq_map)); ++ closest_freq = adt7470_freq_map[index]; + + if (index >= 8) { + index -= 8; +@@ -832,6 +835,10 @@ static int pwm1_freq_set(struct device *dev, long freq) + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, + ADT7470_FREQ_MASK, + index << ADT7470_FREQ_SHIFT); ++ if (err < 0) ++ goto out; ++ ++ data->pwm_freq = closest_freq; + out: + mutex_unlock(&data->lock); + +@@ -1285,6 +1292,7 @@ static int adt7470_probe(struct i2c_client *client) + struct device *dev = &client->dev; + struct adt7470_data *data; + struct device *hwmon_dev; ++ int freq_val; + int err; + + data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); +@@ -1309,6 +1317,14 @@ static int adt7470_probe(struct i2c_client *client) + if (err < 0) + return err; + ++ freq_val = pwm1_freq_get(dev); ++ if (freq_val <= 0) { ++ err = freq_val < 0 ? freq_val : -EINVAL; ++ return err; ++ } ++ ++ data->pwm_freq = (u32)freq_val; ++ + /* Register sysfs hooks */ + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &adt7470_chip_info, +-- +2.53.0 + diff --git a/queue-6.12/hwmon-ina226-add-support-for-sy24655.patch b/queue-6.12/hwmon-ina226-add-support-for-sy24655.patch new file mode 100644 index 0000000000..3129ebc840 --- /dev/null +++ b/queue-6.12/hwmon-ina226-add-support-for-sy24655.patch @@ -0,0 +1,326 @@ +From 9fb0ae89106658b3fcceafd5df7aae95866432da Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 Nov 2024 10:05:46 -0500 +Subject: hwmon: (ina226) Add support for SY24655 + +From: Wenliang Yan + +[ Upstream commit 52172ad87a22ed6e687ca678da21d3c949bc89a1 ] + +SY24655: Support for current and voltage detection as well as +power calculation. + +Signed-off-by: Wenliang Yan +Message-ID: <20241106150547.2538-1-wenliang202407@163.com> +[groeck: Changed order of compatible entries; + dropped spurious extra return statement in is_visible(); + fixed code problems] +Signed-off-by: Guenter Roeck +Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues") +Signed-off-by: Sasha Levin +--- + Documentation/hwmon/ina2xx.rst | 27 +++++++++- + drivers/hwmon/Kconfig | 2 +- + drivers/hwmon/ina2xx.c | 94 ++++++++++++++++++++++++++++++++-- + 3 files changed, 116 insertions(+), 7 deletions(-) + +diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst +index 1ce161e6c0bf0..a3860aae444c0 100644 +--- a/Documentation/hwmon/ina2xx.rst ++++ b/Documentation/hwmon/ina2xx.rst +@@ -63,6 +63,17 @@ Supported chips: + + https://www.ti.com/ + ++ * Silergy SY24655 ++ ++ Prefix: 'sy24655' ++ ++ Addresses: I2C 0x40 - 0x4f ++ ++ Datasheet: Publicly available at the Silergy website ++ ++ https://us1.silergy.com/ ++ ++ + Author: Lothar Felten + + Description +@@ -85,6 +96,11 @@ bus supply voltage. + INA260 is a high or low side current and power monitor with integrated shunt + resistor. + ++The SY24655 is a high- and low-side current shunt and power monitor with an I2C ++interface. The SY24655 supports both shunt drop and supply voltage, with ++programmable calibration value and conversion times. The SY24655 can also ++calculate average power for use in energy conversion. ++ + The shunt value in micro-ohms can be set via platform data or device tree at + compile-time or via the shunt_resistor attribute in sysfs at run-time. Please + refer to the Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for bindings +@@ -108,8 +124,8 @@ power1_input Power(uW) measurement channel + shunt_resistor Shunt resistance(uOhm) channel (not for ina260) + ======================= =============================================== + +-Additional sysfs entries for ina226, ina230, ina231, and ina260 +---------------------------------------------------------------- ++Additional sysfs entries for ina226, ina230, ina231, ina260, and sy24655 ++------------------------------------------------------------------------ + + ======================= ==================================================== + curr1_lcrit Critical low current +@@ -130,6 +146,13 @@ update_interval data conversion time; affects number of samples used + to average results for shunt and bus voltages. + ======================= ==================================================== + ++Sysfs entries for sy24655 only ++------------------------------ ++ ++======================= ==================================================== ++power1_average average power from last reading to the present. ++======================= ==================================================== ++ + .. note:: + + - Configure `shunt_resistor` before configure `power1_crit`, because power +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +index 681722626bc48..195c7b505a853 100644 +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -2170,7 +2170,7 @@ config SENSORS_INA2XX + select REGMAP_I2C + help + If you say yes here you get support for INA219, INA220, INA226, +- INA230, INA231, and INA260 power monitor chips. ++ INA230, INA231, INA260, and SY24655 power monitor chips. + + The INA2xx driver is configured for the default configuration of + the part as described in the datasheet. +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +index cecc80a41a974..345fe7db9de94 100644 +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -51,12 +51,19 @@ + #define INA226_ALERT_LIMIT 0x07 + #define INA226_DIE_ID 0xFF + +-#define INA2XX_MAX_REGISTERS 8 ++/* SY24655 register definitions */ ++#define SY24655_EIN 0x0A ++#define SY24655_ACCUM_CONFIG 0x0D ++#define INA2XX_MAX_REGISTERS 0x0D + + /* settings - depend on use case */ + #define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ + #define INA226_CONFIG_DEFAULT 0x4527 /* averages=16 */ + #define INA260_CONFIG_DEFAULT 0x6527 /* averages=16 */ ++#define SY24655_CONFIG_DEFAULT 0x4527 /* averages=16 */ ++ ++/* (only for sy24655) */ ++#define SY24655_ACCUM_CONFIG_DEFAULT 0x044C /* continuous mode, clear after read*/ + + /* worst case is 68.10 ms (~14.6Hz, ina219) */ + #define INA2XX_CONVERSION_RATE 15 +@@ -97,6 +104,7 @@ static bool ina2xx_writeable_reg(struct device *dev, unsigned int reg) + case INA2XX_CALIBRATION: + case INA226_MASK_ENABLE: + case INA226_ALERT_LIMIT: ++ case SY24655_ACCUM_CONFIG: + return true; + default: + return false; +@@ -127,12 +135,13 @@ static const struct regmap_config ina2xx_regmap_config = { + .writeable_reg = ina2xx_writeable_reg, + }; + +-enum ina2xx_ids { ina219, ina226, ina260 }; ++enum ina2xx_ids { ina219, ina226, ina260, sy24655 }; + + struct ina2xx_config { + u16 config_default; + bool has_alerts; /* chip supports alerts and limits */ + bool has_ishunt; /* chip has internal shunt resistor */ ++ bool has_power_average; /* chip has internal shunt resistor */ + int calibration_value; + int shunt_div; + int bus_voltage_shift; +@@ -149,6 +158,7 @@ struct ina2xx_data { + long power_lsb_uW; + struct mutex config_lock; + struct regmap *regmap; ++ struct i2c_client *client; + }; + + static const struct ina2xx_config ina2xx_config[] = { +@@ -161,6 +171,7 @@ static const struct ina2xx_config ina2xx_config[] = { + .power_lsb_factor = 20, + .has_alerts = false, + .has_ishunt = false, ++ .has_power_average = false, + }, + [ina226] = { + .config_default = INA226_CONFIG_DEFAULT, +@@ -171,6 +182,7 @@ static const struct ina2xx_config ina2xx_config[] = { + .power_lsb_factor = 25, + .has_alerts = true, + .has_ishunt = false, ++ .has_power_average = false, + }, + [ina260] = { + .config_default = INA260_CONFIG_DEFAULT, +@@ -180,6 +192,18 @@ static const struct ina2xx_config ina2xx_config[] = { + .power_lsb_factor = 8, + .has_alerts = true, + .has_ishunt = true, ++ .has_power_average = false, ++ }, ++ [sy24655] = { ++ .config_default = SY24655_CONFIG_DEFAULT, ++ .calibration_value = 4096, ++ .shunt_div = 400, ++ .bus_voltage_shift = 0, ++ .bus_voltage_lsb = 1250, ++ .power_lsb_factor = 25, ++ .has_alerts = true, ++ .has_ishunt = false, ++ .has_power_average = true, + }, + }; + +@@ -485,6 +509,41 @@ static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val) + return 0; + } + ++/* ++ * Configuring the READ_EIN (bit 10) of the ACCUM_CONFIG register to 1 ++ * can clear accumulator and sample_count after reading the EIN register. ++ * This way, the average power between the last read and the current ++ * read can be obtained. By combining with accurate time data from ++ * outside, the energy consumption during that period can be calculated. ++ */ ++static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *val) ++{ ++ u8 template[6]; ++ int ret; ++ long accumulator_24, sample_count; ++ ++ /* 48-bit register read */ ++ ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template); ++ if (ret < 0) ++ return ret; ++ if (ret != 6) ++ return -EIO; ++ accumulator_24 = ((template[3] << 16) | ++ (template[4] << 8) | ++ template[5]); ++ sample_count = ((template[0] << 16) | ++ (template[1] << 8) | ++ template[2]); ++ if (sample_count <= 0) { ++ *val = 0; ++ return 0; ++ } ++ ++ *val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; ++ ++ return 0; ++} ++ + static int ina2xx_power_read(struct device *dev, u32 attr, long *val) + { + struct ina2xx_data *data = dev_get_drvdata(dev); +@@ -492,6 +551,8 @@ static int ina2xx_power_read(struct device *dev, u32 attr, long *val) + switch (attr) { + case hwmon_power_input: + return ina2xx_read_init(dev, INA2XX_POWER, val); ++ case hwmon_power_average: ++ return sy24655_average_power_read(data, SY24655_EIN, val); + case hwmon_power_crit: + return ina226_alert_limit_read(data, INA226_POWER_OVER_LIMIT_MASK, + INA2XX_POWER, val); +@@ -651,6 +712,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type + { + const struct ina2xx_data *data = _data; + bool has_alerts = data->config->has_alerts; ++ bool has_power_average = data->config->has_power_average; + enum ina2xx_ids chip = data->chip; + + switch (type) { +@@ -702,6 +764,10 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type + if (has_alerts) + return 0444; + break; ++ case hwmon_power_average: ++ if (has_power_average) ++ return 0444; ++ break; + default: + break; + } +@@ -734,7 +800,8 @@ static const struct hwmon_channel_info * const ina2xx_info[] = { + HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_CRIT | HWMON_C_CRIT_ALARM | + HWMON_C_LCRIT | HWMON_C_LCRIT_ALARM), + HWMON_CHANNEL_INFO(power, +- HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM), ++ HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM | ++ HWMON_P_AVERAGE), + NULL + }; + +@@ -839,6 +906,19 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data) + INA226_ALERT_LATCH_ENABLE | + FIELD_PREP(INA226_ALERT_POLARITY, active_high)); + } ++ if (data->config->has_power_average) { ++ if (data->chip == sy24655) { ++ /* ++ * Initialize the power accumulation method to continuous ++ * mode and clear the EIN register after each read of the ++ * EIN register ++ */ ++ ret = regmap_write(regmap, SY24655_ACCUM_CONFIG, ++ SY24655_ACCUM_CONFIG_DEFAULT); ++ if (ret < 0) ++ return ret; ++ } ++ } + + if (data->config->has_ishunt) + return 0; +@@ -868,6 +948,7 @@ static int ina2xx_probe(struct i2c_client *client) + return -ENOMEM; + + /* set the device type */ ++ data->client = client; + data->config = &ina2xx_config[chip]; + data->chip = chip; + mutex_init(&data->config_lock); +@@ -906,11 +987,16 @@ static const struct i2c_device_id ina2xx_id[] = { + { "ina230", ina226 }, + { "ina231", ina226 }, + { "ina260", ina260 }, ++ { "sy24655", sy24655 }, + { } + }; + MODULE_DEVICE_TABLE(i2c, ina2xx_id); + + static const struct of_device_id __maybe_unused ina2xx_of_match[] = { ++ { ++ .compatible = "silergy,sy24655", ++ .data = (void *)sy24655 ++ }, + { + .compatible = "ti,ina219", + .data = (void *)ina219 +@@ -935,7 +1021,7 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = { + .compatible = "ti,ina260", + .data = (void *)ina260 + }, +- { }, ++ { } + }; + MODULE_DEVICE_TABLE(of, ina2xx_of_match); + +-- +2.53.0 + diff --git a/queue-6.12/hwmon-ina2xx-add-support-for-has_alerts-configuratio.patch b/queue-6.12/hwmon-ina2xx-add-support-for-has_alerts-configuratio.patch new file mode 100644 index 0000000000..6afc87f50a --- /dev/null +++ b/queue-6.12/hwmon-ina2xx-add-support-for-has_alerts-configuratio.patch @@ -0,0 +1,112 @@ +From efcdd27d43125883ee26a01f0edbd130f3df70ac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 27 Aug 2024 12:57:10 -0700 +Subject: hwmon: (ina2xx) Add support for has_alerts configuration flag + +From: Guenter Roeck + +[ Upstream commit de0da6ae1908b43b23782d64b4564b5ca3119f7f ] + +Add configuration flag indicating if the chip supports alerts and limits +to prepare for adding INA260 support. + +Reviewed-by: Tzung-Bi Shih +Signed-off-by: Guenter Roeck +Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues") +Signed-off-by: Sasha Levin +--- + drivers/hwmon/ina2xx.c | 18 +++++++++++------- + 1 file changed, 11 insertions(+), 7 deletions(-) + +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +index f0fa6d073627f..03a011c9b73da 100644 +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -129,6 +129,7 @@ enum ina2xx_ids { ina219, ina226 }; + + struct ina2xx_config { + u16 config_default; ++ bool has_alerts; /* chip supports alerts and limits */ + int calibration_value; + int shunt_div; + int bus_voltage_shift; +@@ -155,6 +156,7 @@ static const struct ina2xx_config ina2xx_config[] = { + .bus_voltage_shift = 3, + .bus_voltage_lsb = 4000, + .power_lsb_factor = 20, ++ .has_alerts = false, + }, + [ina226] = { + .config_default = INA226_CONFIG_DEFAULT, +@@ -163,6 +165,7 @@ static const struct ina2xx_config ina2xx_config[] = { + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb_factor = 25, ++ .has_alerts = true, + }, + }; + +@@ -624,6 +627,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type + u32 attr, int channel) + { + const struct ina2xx_data *data = _data; ++ bool has_alerts = data->config->has_alerts; + enum ina2xx_ids chip = data->chip; + + switch (type) { +@@ -633,12 +637,12 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type + return 0444; + case hwmon_in_lcrit: + case hwmon_in_crit: +- if (chip == ina226) ++ if (has_alerts) + return 0644; + break; + case hwmon_in_lcrit_alarm: + case hwmon_in_crit_alarm: +- if (chip == ina226) ++ if (has_alerts) + return 0444; + break; + default: +@@ -651,12 +655,12 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type + return 0444; + case hwmon_curr_lcrit: + case hwmon_curr_crit: +- if (chip == ina226) ++ if (has_alerts) + return 0644; + break; + case hwmon_curr_lcrit_alarm: + case hwmon_curr_crit_alarm: +- if (chip == ina226) ++ if (has_alerts) + return 0444; + break; + default: +@@ -668,11 +672,11 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type + case hwmon_power_input: + return 0444; + case hwmon_power_crit: +- if (chip == ina226) ++ if (has_alerts) + return 0644; + break; + case hwmon_power_crit_alarm: +- if (chip == ina226) ++ if (has_alerts) + return 0444; + break; + default: +@@ -802,7 +806,7 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data) + if (ret < 0) + return ret; + +- if (data->chip == ina226) { ++ if (data->config->has_alerts) { + bool active_high = device_property_read_bool(dev, "ti,alert-polarity-active-high"); + + regmap_update_bits(regmap, INA226_MASK_ENABLE, +-- +2.53.0 + diff --git a/queue-6.12/hwmon-ina2xx-add-support-for-ina234.patch b/queue-6.12/hwmon-ina2xx-add-support-for-ina234.patch new file mode 100644 index 0000000000..501d580b77 --- /dev/null +++ b/queue-6.12/hwmon-ina2xx-add-support-for-ina234.patch @@ -0,0 +1,133 @@ +From 3fb13e14f450d96e80e99770c4d894f926a6c413 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 20 Feb 2026 13:20:22 +0200 +Subject: hwmon: (ina2xx) Add support for INA234 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Ian Ray + +[ Upstream commit 88a928eebccdc5445d874ddcbf1683b76c9f1431 ] + +INA234 is register compatible to INA226 (excepting manufacturer and die +or device id registers) but has different scaling. + +Signed-off-by: Ian Ray +Reviewed-by: Bence Csókás # v2 +Tested-by: Jens Almer +Tested-by: Jonas Rebmann +Link: https://lore.kernel.org/r/20260220112024.97446-4-ian.ray@gehealthcare.com +Signed-off-by: Guenter Roeck +Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues") +Signed-off-by: Sasha Levin +--- + Documentation/hwmon/ina2xx.rst | 13 ++++++++++++- + drivers/hwmon/Kconfig | 2 +- + drivers/hwmon/ina2xx.c | 18 ++++++++++++++++++ + 3 files changed, 31 insertions(+), 2 deletions(-) + +diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst +index a4ddf4bd2b081..d64e7af46a124 100644 +--- a/Documentation/hwmon/ina2xx.rst ++++ b/Documentation/hwmon/ina2xx.rst +@@ -74,6 +74,16 @@ Supported chips: + https://us1.silergy.com/ + + ++ * Texas Instruments INA234 ++ ++ Prefix: 'ina234' ++ ++ Addresses: I2C 0x40 - 0x43 ++ ++ Datasheet: Publicly available at the Texas Instruments website ++ ++ https://www.ti.com/ ++ + Author: Lothar Felten + + Description +@@ -89,7 +99,7 @@ interface. The INA220 monitors both shunt drop and supply voltage. + The INA226 is a current shunt and power monitor with an I2C interface. + The INA226 monitors both a shunt voltage drop and bus supply voltage. + +-INA230 and INA231 are high or low side current shunt and power monitors ++INA230, INA231, and INA234 are high or low side current shunt and power monitors + with an I2C interface. The chips monitor both a shunt voltage drop and + bus supply voltage. + +@@ -132,6 +142,7 @@ Additional entries are available for the following chips: + * ina226 + * ina230 + * ina231 ++ * ina234 + * ina260 + * sy24655 + +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +index 195c7b505a853..848b9cfcb1d87 100644 +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -2170,7 +2170,7 @@ config SENSORS_INA2XX + select REGMAP_I2C + help + If you say yes here you get support for INA219, INA220, INA226, +- INA230, INA231, INA260, and SY24655 power monitor chips. ++ INA230, INA231, INA234, INA260, and SY24655 power monitor chips. + + The INA2xx driver is configured for the default configuration of + the part as described in the datasheet. +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +index 97ec0e0218307..be6e214fae21f 100644 +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -138,6 +138,7 @@ static const struct regmap_config ina2xx_regmap_config = { + enum ina2xx_ids { + ina219, + ina226, ++ ina234, + ina260, + sy24655 + }; +@@ -192,6 +193,18 @@ static const struct ina2xx_config ina2xx_config[] = { + .has_power_average = false, + .has_update_interval = true, + }, ++ [ina234] = { ++ .config_default = INA226_CONFIG_DEFAULT, ++ .calibration_value = 2048, ++ .shunt_div = 400, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */ ++ .bus_voltage_shift = 4, ++ .bus_voltage_lsb = 25600, ++ .power_lsb_factor = 32, ++ .has_alerts = true, ++ .has_ishunt = false, ++ .has_power_average = false, ++ .has_update_interval = true, ++ }, + [ina260] = { + .config_default = INA260_CONFIG_DEFAULT, + .shunt_div = 400, +@@ -996,6 +1009,7 @@ static const struct i2c_device_id ina2xx_id[] = { + { "ina226", ina226 }, + { "ina230", ina226 }, + { "ina231", ina226 }, ++ { "ina234", ina234 }, + { "ina260", ina260 }, + { "sy24655", sy24655 }, + { } +@@ -1027,6 +1041,10 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = { + .compatible = "ti,ina231", + .data = (void *)ina226 + }, ++ { ++ .compatible = "ti,ina234", ++ .data = (void *)ina234 ++ }, + { + .compatible = "ti,ina260", + .data = (void *)ina260 +-- +2.53.0 + diff --git a/queue-6.12/hwmon-ina2xx-add-support-for-ina260.patch b/queue-6.12/hwmon-ina2xx-add-support-for-ina260.patch new file mode 100644 index 0000000000..922f33195a --- /dev/null +++ b/queue-6.12/hwmon-ina2xx-add-support-for-ina260.patch @@ -0,0 +1,234 @@ +From 055a70df0cdec3010cedd2565a6fffd258d04c56 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 27 Aug 2024 10:23:10 -0700 +Subject: hwmon: (ina2xx) Add support for INA260 + +From: Guenter Roeck + +[ Upstream commit 70fb84a109c639637f0636281dbdb21ed8ffb000 ] + +INA260 is similar to other chips of the series, except it has an internal +shunt resistor. The calibration register is therefore not present. Also, +the current register address was changed, though that does not matter for +the driver since the shunt voltage register (which is now the current +register) value is already used to read the current. + +Cc: Loic Guegan +Reviewed-by: Tzung-Bi Shih +Signed-off-by: Guenter Roeck +Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues") +Signed-off-by: Sasha Levin +--- + Documentation/hwmon/ina2xx.rst | 23 +++++++++++++++---- + drivers/hwmon/Kconfig | 5 ++-- + drivers/hwmon/ina2xx.c | 42 ++++++++++++++++++++++++++++++---- + 3 files changed, 59 insertions(+), 11 deletions(-) + +diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst +index 7f1939b40f74f..1ce161e6c0bf0 100644 +--- a/Documentation/hwmon/ina2xx.rst ++++ b/Documentation/hwmon/ina2xx.rst +@@ -53,6 +53,16 @@ Supported chips: + + https://www.ti.com/ + ++ * Texas Instruments INA260 ++ ++ Prefix: 'ina260' ++ ++ Addresses: I2C 0x40 - 0x4f ++ ++ Datasheet: Publicly available at the Texas Instruments website ++ ++ https://www.ti.com/ ++ + Author: Lothar Felten + + Description +@@ -72,6 +82,9 @@ INA230 and INA231 are high or low side current shunt and power monitors + with an I2C interface. The chips monitor both a shunt voltage drop and + bus supply voltage. + ++INA260 is a high or low side current and power monitor with integrated shunt ++resistor. ++ + The shunt value in micro-ohms can be set via platform data or device tree at + compile-time or via the shunt_resistor attribute in sysfs at run-time. Please + refer to the Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for bindings +@@ -87,16 +100,16 @@ The actual programmed interval may vary from the desired value. + General sysfs entries + --------------------- + +-======================= =============================== ++======================= =============================================== + in0_input Shunt voltage(mV) channel + in1_input Bus voltage(mV) channel + curr1_input Current(mA) measurement channel + power1_input Power(uW) measurement channel +-shunt_resistor Shunt resistance(uOhm) channel +-======================= =============================== ++shunt_resistor Shunt resistance(uOhm) channel (not for ina260) ++======================= =============================================== + +-Sysfs entries for ina226, ina230 and ina231 only +------------------------------------------------- ++Additional sysfs entries for ina226, ina230, ina231, and ina260 ++--------------------------------------------------------------- + + ======================= ==================================================== + curr1_lcrit Critical low current +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +index acd0f86cb9b38..681722626bc48 100644 +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -2170,11 +2170,12 @@ config SENSORS_INA2XX + select REGMAP_I2C + help + If you say yes here you get support for INA219, INA220, INA226, +- INA230, and INA231 power monitor chips. ++ INA230, INA231, and INA260 power monitor chips. + + The INA2xx driver is configured for the default configuration of + the part as described in the datasheet. +- Default value for Rshunt is 10 mOhms. ++ Default value for Rshunt is 10 mOhms except for INA260 which has an ++ internal 2 mOhm shunt resistor. + This driver can also be built as a module. If so, the module + will be called ina2xx. + +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +index 03a011c9b73da..cecc80a41a974 100644 +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -56,12 +56,14 @@ + /* settings - depend on use case */ + #define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ + #define INA226_CONFIG_DEFAULT 0x4527 /* averages=16 */ ++#define INA260_CONFIG_DEFAULT 0x6527 /* averages=16 */ + + /* worst case is 68.10 ms (~14.6Hz, ina219) */ + #define INA2XX_CONVERSION_RATE 15 + #define INA2XX_MAX_DELAY 69 /* worst case delay in ms */ + + #define INA2XX_RSHUNT_DEFAULT 10000 ++#define INA260_RSHUNT 2000 + + /* bit mask for reading the averaging setting in the configuration register */ + #define INA226_AVG_RD_MASK GENMASK(11, 9) +@@ -125,11 +127,12 @@ static const struct regmap_config ina2xx_regmap_config = { + .writeable_reg = ina2xx_writeable_reg, + }; + +-enum ina2xx_ids { ina219, ina226 }; ++enum ina2xx_ids { ina219, ina226, ina260 }; + + struct ina2xx_config { + u16 config_default; + bool has_alerts; /* chip supports alerts and limits */ ++ bool has_ishunt; /* chip has internal shunt resistor */ + int calibration_value; + int shunt_div; + int bus_voltage_shift; +@@ -157,6 +160,7 @@ static const struct ina2xx_config ina2xx_config[] = { + .bus_voltage_lsb = 4000, + .power_lsb_factor = 20, + .has_alerts = false, ++ .has_ishunt = false, + }, + [ina226] = { + .config_default = INA226_CONFIG_DEFAULT, +@@ -166,6 +170,16 @@ static const struct ina2xx_config ina2xx_config[] = { + .bus_voltage_lsb = 1250, + .power_lsb_factor = 25, + .has_alerts = true, ++ .has_ishunt = false, ++ }, ++ [ina260] = { ++ .config_default = INA260_CONFIG_DEFAULT, ++ .shunt_div = 400, ++ .bus_voltage_shift = 0, ++ .bus_voltage_lsb = 1250, ++ .power_lsb_factor = 8, ++ .has_alerts = true, ++ .has_ishunt = true, + }, + }; + +@@ -257,6 +271,15 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val) + unsigned int regval; + int ret, retry; + ++ if (data->config->has_ishunt) { ++ /* No calibration needed */ ++ ret = regmap_read(regmap, reg, ®val); ++ if (ret < 0) ++ return ret; ++ *val = ina2xx_get_value(data, reg, regval); ++ return 0; ++ } ++ + for (retry = 5; retry; retry--) { + ret = regmap_read(regmap, reg, ®val); + if (ret < 0) +@@ -686,7 +709,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: +- if (chip == ina226) ++ if (chip == ina226 || chip == ina260) + return 0644; + break; + default: +@@ -795,7 +818,9 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data) + u32 shunt; + int ret; + +- if (device_property_read_u32(dev, "shunt-resistor", &shunt) < 0) ++ if (data->config->has_ishunt) ++ shunt = INA260_RSHUNT; ++ else if (device_property_read_u32(dev, "shunt-resistor", &shunt) < 0) + shunt = INA2XX_RSHUNT_DEFAULT; + + ret = ina2xx_set_shunt(data, shunt); +@@ -815,6 +840,9 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data) + FIELD_PREP(INA226_ALERT_POLARITY, active_high)); + } + ++ if (data->config->has_ishunt) ++ return 0; ++ + /* + * Calibration register is set to the best value, which eliminates + * truncation errors on calculating current register in hardware. +@@ -860,7 +888,8 @@ static int ina2xx_probe(struct i2c_client *client) + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &ina2xx_chip_info, +- ina2xx_groups); ++ data->config->has_ishunt ? ++ NULL : ina2xx_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + +@@ -876,6 +905,7 @@ static const struct i2c_device_id ina2xx_id[] = { + { "ina226", ina226 }, + { "ina230", ina226 }, + { "ina231", ina226 }, ++ { "ina260", ina260 }, + { } + }; + MODULE_DEVICE_TABLE(i2c, ina2xx_id); +@@ -901,6 +931,10 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = { + .compatible = "ti,ina231", + .data = (void *)ina226 + }, ++ { ++ .compatible = "ti,ina260", ++ .data = (void *)ina260 ++ }, + { }, + }; + MODULE_DEVICE_TABLE(of, ina2xx_of_match); +-- +2.53.0 + diff --git a/queue-6.12/hwmon-ina2xx-fix-various-overflow-issues.patch b/queue-6.12/hwmon-ina2xx-fix-various-overflow-issues.patch new file mode 100644 index 0000000000..4e53ca4844 --- /dev/null +++ b/queue-6.12/hwmon-ina2xx-fix-various-overflow-issues.patch @@ -0,0 +1,156 @@ +From 226e9dd2665d0eb346202f427f3c2a7e5ae815da Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 10 Jun 2026 07:46:16 -0700 +Subject: hwmon: (ina2xx) Fix various overflow issues + +From: Guenter Roeck + +[ Upstream commit e6c80061ca239f45c0eaf7e47a91d6d6df9bd636 ] + +Sashiko reports several integer overflow problems in the ina2xx driver +caused by unbounded multiplications and inadequate types for intermediate +calculations. + +Specifically: +- In ina2xx_get_value(), the return type is changed from int to long. + Intermediate calculations for current are now performed using 64-bit + types to prevent 32-bit integer overflow before the division by 1000. +- When calculating power in ina2xx_get_value() and + sy24655_average_power_read(), interim values are cast to u64 and clamped + to LONG_MAX. This prevents overflow when regval or accumulator_24 is + multiplied by power_lsb_uW. +- In ina226_alert_to_reg(), the clamping logic is rewritten using min_t(). + This safely avoids integer overflows when scaling user-provided values + for shunt voltage, bus voltage, power, and current limits. + +Cc: Loic Poulain +Fixes: ab7fbee452be ("hwmon: (ina2xx) Fix various overflow issues") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/ina2xx.c | 61 ++++++++++++++++++++++++------------------ + 1 file changed, 35 insertions(+), 26 deletions(-) + +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +index d671d546b2054..c504bb43a2ca3 100644 +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -283,30 +284,34 @@ static u16 ina226_interval_to_reg(long interval) + return FIELD_PREP(INA226_AVG_RD_MASK, avg_bits); + } + +-static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, +- unsigned int regval) ++static long ina2xx_get_value(struct ina2xx_data *data, u8 reg, ++ unsigned int regval) + { +- int val; ++ s64 val64; ++ long val; + + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: + /* signed register */ +- val = (s16)regval >> data->config->shunt_voltage_shift; +- val = DIV_ROUND_CLOSEST(val, data->config->shunt_div); ++ val = DIV_ROUND_CLOSEST((s16)regval >> data->config->shunt_voltage_shift, ++ data->config->shunt_div); + break; + case INA2XX_BUS_VOLTAGE: +- val = (regval >> data->config->bus_voltage_shift) * +- data->config->bus_voltage_lsb; +- val = DIV_ROUND_CLOSEST(val, 1000); ++ val = DIV_ROUND_CLOSEST((regval >> data->config->bus_voltage_shift) * ++ data->config->bus_voltage_lsb, 1000); + break; + case INA2XX_POWER: +- val = regval * data->power_lsb_uW; ++ val = min_t(u64, (u64)regval * data->power_lsb_uW, LONG_MAX); + break; + case INA2XX_CURRENT: + /* signed register, result in mA */ +- val = ((s16)regval >> data->config->current_shift) * ++ val64 = (s64)((s16)regval >> data->config->current_shift) * + data->current_lsb_uA; +- val = DIV_ROUND_CLOSEST(val, 1000); ++ if (val64 < 0) ++ val64 = -DIV_ROUND_CLOSEST_ULL(-val64, 1000); ++ else ++ val64 = DIV_ROUND_CLOSEST_ULL(val64, 1000); ++ val = clamp_val(val64, LONG_MIN, LONG_MAX); + break; + case INA2XX_CALIBRATION: + val = regval; +@@ -395,27 +400,29 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val) + */ + static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) + { ++ long limit; ++ + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: +- val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div); +- val *= data->config->shunt_div; +- val <<= data->config->shunt_voltage_shift; +- return clamp_val(val, 0, SHRT_MAX); ++ val = min_t(long, val, DIV_ROUND_CLOSEST(SHRT_MAX, data->config->shunt_div)); ++ return min_t(long, (val * data->config->shunt_div) << data->config->shunt_voltage_shift, ++ SHRT_MAX); + case INA2XX_BUS_VOLTAGE: +- val = clamp_val(val, 0, 200000); +- val = (val * 1000) << data->config->bus_voltage_shift; +- val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb); +- return clamp_val(val, 0, USHRT_MAX); ++ val = min_t(long, val, 130000); ++ return min_t(long, ++ DIV_ROUND_CLOSEST((val * 1000) << data->config->bus_voltage_shift, ++ data->config->bus_voltage_lsb), ++ USHRT_MAX); + case INA2XX_POWER: +- val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW); +- val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW); +- return clamp_val(val, 0, USHRT_MAX); ++ val = min_t(long, val, LONG_MAX - data->power_lsb_uW); ++ return min_t(long, DIV_ROUND_CLOSEST(val, data->power_lsb_uW), USHRT_MAX); + case INA2XX_CURRENT: +- val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000); ++ limit = (LONG_MAX - (data->current_lsb_uA / 2)) / 1000; ++ val = min_t(long, val, limit); + /* signed register, result in mA */ + val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA); +- val <<= data->config->current_shift; +- return clamp_val(val, SHRT_MIN, SHRT_MAX); ++ limit = SHRT_MAX >> data->config->current_shift; ++ return (u16)(min_t(long, val, limit) << data->config->current_shift); + default: + /* programmer goofed */ + WARN_ON_ONCE(1); +@@ -560,6 +567,7 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va + u8 template[6]; + int ret; + long accumulator_24, sample_count; ++ u64 val64; + + /* 48-bit register read */ + ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template); +@@ -578,7 +586,8 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va + return 0; + } + +- *val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; ++ val64 = (u64)DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; ++ *val = min_t(u64, val64, LONG_MAX); + + return 0; + } +-- +2.53.0 + diff --git a/queue-6.12/hwmon-ina2xx-make-it-easier-to-add-more-devices.patch b/queue-6.12/hwmon-ina2xx-make-it-easier-to-add-more-devices.patch new file mode 100644 index 0000000000..0a1eca178b --- /dev/null +++ b/queue-6.12/hwmon-ina2xx-make-it-easier-to-add-more-devices.patch @@ -0,0 +1,133 @@ +From 9d2d906f9d043d546ebdef2e4ca5fed76482a3ce Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 20 Feb 2026 13:20:21 +0200 +Subject: hwmon: (ina2xx) Make it easier to add more devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Ian Ray + +[ Upstream commit f6e14b5bcabf4ee97a2d535c3c2d7e72c8da4c15 ] + +* Make sysfs entries documentation easier to maintain. +* Use multi-line enum. +* Correct "has_power_average" comment. + +Create a new "has_update_interval" member for chips which support +averaging. + +Signed-off-by: Ian Ray +Reviewed-by: Bence Csókás # v2 +Tested-by: Jens Almer +Link: https://lore.kernel.org/r/20260220112024.97446-3-ian.ray@gehealthcare.com +Signed-off-by: Guenter Roeck +Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues") +Signed-off-by: Sasha Levin +--- + Documentation/hwmon/ina2xx.rst | 12 ++++++++++-- + drivers/hwmon/ina2xx.c | 18 ++++++++++++++---- + 2 files changed, 24 insertions(+), 6 deletions(-) + +diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst +index a3860aae444c0..a4ddf4bd2b081 100644 +--- a/Documentation/hwmon/ina2xx.rst ++++ b/Documentation/hwmon/ina2xx.rst +@@ -124,8 +124,16 @@ power1_input Power(uW) measurement channel + shunt_resistor Shunt resistance(uOhm) channel (not for ina260) + ======================= =============================================== + +-Additional sysfs entries for ina226, ina230, ina231, ina260, and sy24655 +------------------------------------------------------------------------- ++Additional sysfs entries ++------------------------ ++ ++Additional entries are available for the following chips: ++ ++ * ina226 ++ * ina230 ++ * ina231 ++ * ina260 ++ * sy24655 + + ======================= ==================================================== + curr1_lcrit Critical low current +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +index 345fe7db9de94..97ec0e0218307 100644 +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -135,13 +135,19 @@ static const struct regmap_config ina2xx_regmap_config = { + .writeable_reg = ina2xx_writeable_reg, + }; + +-enum ina2xx_ids { ina219, ina226, ina260, sy24655 }; ++enum ina2xx_ids { ++ ina219, ++ ina226, ++ ina260, ++ sy24655 ++}; + + struct ina2xx_config { + u16 config_default; + bool has_alerts; /* chip supports alerts and limits */ + bool has_ishunt; /* chip has internal shunt resistor */ +- bool has_power_average; /* chip has internal shunt resistor */ ++ bool has_power_average; /* chip supports average power */ ++ bool has_update_interval; + int calibration_value; + int shunt_div; + int bus_voltage_shift; +@@ -172,6 +178,7 @@ static const struct ina2xx_config ina2xx_config[] = { + .has_alerts = false, + .has_ishunt = false, + .has_power_average = false, ++ .has_update_interval = false, + }, + [ina226] = { + .config_default = INA226_CONFIG_DEFAULT, +@@ -183,6 +190,7 @@ static const struct ina2xx_config ina2xx_config[] = { + .has_alerts = true, + .has_ishunt = false, + .has_power_average = false, ++ .has_update_interval = true, + }, + [ina260] = { + .config_default = INA260_CONFIG_DEFAULT, +@@ -193,6 +201,7 @@ static const struct ina2xx_config ina2xx_config[] = { + .has_alerts = true, + .has_ishunt = true, + .has_power_average = false, ++ .has_update_interval = true, + }, + [sy24655] = { + .config_default = SY24655_CONFIG_DEFAULT, +@@ -204,6 +213,7 @@ static const struct ina2xx_config ina2xx_config[] = { + .has_alerts = true, + .has_ishunt = false, + .has_power_average = true, ++ .has_update_interval = false, + }, + }; + +@@ -713,7 +723,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type + const struct ina2xx_data *data = _data; + bool has_alerts = data->config->has_alerts; + bool has_power_average = data->config->has_power_average; +- enum ina2xx_ids chip = data->chip; ++ bool has_update_interval = data->config->has_update_interval; + + switch (type) { + case hwmon_in: +@@ -775,7 +785,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: +- if (chip == ina226 || chip == ina260) ++ if (has_update_interval) + return 0644; + break; + default: +-- +2.53.0 + diff --git a/queue-6.12/hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch b/queue-6.12/hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch new file mode 100644 index 0000000000..26e9258c2c --- /dev/null +++ b/queue-6.12/hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch @@ -0,0 +1,163 @@ +From 3f968fafb2850c79db10b056447970d401a4b4dd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 3 Mar 2026 12:07:02 +0100 +Subject: hwmon: (ina2xx) Shift INA234 shunt and current registers + +From: Jonas Rebmann + +[ Upstream commit eeca1114d1e2cc0eacaebb80f3f2afbaebfc60be ] + +The INA219 has the lowest three bits of the bus voltage register +zero-reserved, the bus_voltage_shift ina2xx_config field was introduced +to accommodate for that. + +The INA234 has four bits of the bus voltage, of the shunt voltage, and +of the current registers zero-reserved but the latter two were +implemented by choosing a 16x higher shunt_div instead of a separate +field specifying a bit shift. + +This is possible because shunt voltage and current are divided by +shunt_div, hence a 16x higher shunt_div results in a 16x smaller LSB for +both the shunt voltage and the current register, perfectly accounting +for the missing bit shift. + +For consistency and correctness, account for the reserved bits via +shunt_voltage_shift and current_shift configuration fields as already +done for voltage registers and use the conversion constants given in the +INA234 datasheet. + +Signed-off-by: Jonas Rebmann +Link: https://lore.kernel.org/r/20260303-ina234-shift-v1-2-318c33ac4480@pengutronix.de +Signed-off-by: Guenter Roeck +Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues") +Signed-off-by: Sasha Levin +--- + drivers/hwmon/ina2xx.c | 22 +++++++++++++++++++--- + 1 file changed, 19 insertions(+), 3 deletions(-) + +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +index be6e214fae21f..d671d546b2054 100644 +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -151,9 +151,11 @@ struct ina2xx_config { + bool has_update_interval; + int calibration_value; + int shunt_div; ++ int shunt_voltage_shift; + int bus_voltage_shift; + int bus_voltage_lsb; /* uV */ + int power_lsb_factor; ++ int current_shift; + }; + + struct ina2xx_data { +@@ -173,59 +175,69 @@ static const struct ina2xx_config ina2xx_config[] = { + .config_default = INA219_CONFIG_DEFAULT, + .calibration_value = 4096, + .shunt_div = 100, ++ .shunt_voltage_shift = 0, + .bus_voltage_shift = 3, + .bus_voltage_lsb = 4000, + .power_lsb_factor = 20, + .has_alerts = false, + .has_ishunt = false, + .has_power_average = false, ++ .current_shift = 0, + .has_update_interval = false, + }, + [ina226] = { + .config_default = INA226_CONFIG_DEFAULT, + .calibration_value = 2048, + .shunt_div = 400, ++ .shunt_voltage_shift = 0, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb_factor = 25, + .has_alerts = true, + .has_ishunt = false, + .has_power_average = false, ++ .current_shift = 0, + .has_update_interval = true, + }, + [ina234] = { + .config_default = INA226_CONFIG_DEFAULT, + .calibration_value = 2048, +- .shunt_div = 400, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */ ++ .shunt_div = 25, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */ ++ .shunt_voltage_shift = 4, + .bus_voltage_shift = 4, + .bus_voltage_lsb = 25600, + .power_lsb_factor = 32, + .has_alerts = true, + .has_ishunt = false, + .has_power_average = false, ++ .current_shift = 4, + .has_update_interval = true, + }, + [ina260] = { + .config_default = INA260_CONFIG_DEFAULT, + .shunt_div = 400, ++ .shunt_voltage_shift = 0, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb_factor = 8, + .has_alerts = true, + .has_ishunt = true, + .has_power_average = false, ++ .current_shift = 0, + .has_update_interval = true, + }, + [sy24655] = { + .config_default = SY24655_CONFIG_DEFAULT, + .calibration_value = 4096, + .shunt_div = 400, ++ .shunt_voltage_shift = 0, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb_factor = 25, + .has_alerts = true, + .has_ishunt = false, + .has_power_average = true, ++ .current_shift = 0, + .has_update_interval = false, + }, + }; +@@ -279,7 +291,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: + /* signed register */ +- val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div); ++ val = (s16)regval >> data->config->shunt_voltage_shift; ++ val = DIV_ROUND_CLOSEST(val, data->config->shunt_div); + break; + case INA2XX_BUS_VOLTAGE: + val = (regval >> data->config->bus_voltage_shift) * +@@ -291,7 +304,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, + break; + case INA2XX_CURRENT: + /* signed register, result in mA */ +- val = (s16)regval * data->current_lsb_uA; ++ val = ((s16)regval >> data->config->current_shift) * ++ data->current_lsb_uA; + val = DIV_ROUND_CLOSEST(val, 1000); + break; + case INA2XX_CALIBRATION: +@@ -385,6 +399,7 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) + case INA2XX_SHUNT_VOLTAGE: + val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div); + val *= data->config->shunt_div; ++ val <<= data->config->shunt_voltage_shift; + return clamp_val(val, 0, SHRT_MAX); + case INA2XX_BUS_VOLTAGE: + val = clamp_val(val, 0, 200000); +@@ -399,6 +414,7 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) + val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000); + /* signed register, result in mA */ + val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA); ++ val <<= data->config->current_shift; + return clamp_val(val, SHRT_MIN, SHRT_MAX); + default: + /* programmer goofed */ +-- +2.53.0 + diff --git a/queue-6.12/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch b/queue-6.12/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch new file mode 100644 index 0000000000..6d5450fead --- /dev/null +++ b/queue-6.12/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch @@ -0,0 +1,54 @@ +From e9211fde6e941ad0a19805b93ef9f0bf815f82b8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 15:27:28 -0700 +Subject: hwmon: (lm90) Only report alarms if driver is ready + +From: Guenter Roeck + +[ Upstream commit aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 ] + +Userspace can read sysfs attributes before driver registration is complete, +immediately after devm_hwmon_device_register_with_info() has been called. +At that time, data->hwmon_dev is not yet initialized. This can trigger +a NULL pointer access since lm90_update_device() and with it +lm90_update_alarms_locked() will be called. This call schedules +report_work and lm90_report_alarms(), which passes the still-NULL +data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer +dereference. + +Fix the problem by only scheduling the report and alert workers +data->hwmon_dev is set. + +Reported-by: Sashiko +Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/lm90.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c +index 511d95a0efb36..45d5a2427dcb1 100644 +--- a/drivers/hwmon/lm90.c ++++ b/drivers/hwmon/lm90.c +@@ -1149,7 +1149,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) + check_enable = (client->irq || !(data->config_orig & 0x80)) && + (data->config & 0x80); + +- if (force || check_enable) ++ if (data->hwmon_dev && (force || check_enable)) + schedule_work(&data->report_work); + + /* +@@ -1157,7 +1157,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) + * alarms are all clear, and alerts are currently disabled. + * Otherwise (re)schedule worker if needed. + */ +- if (check_enable) { ++ if (check_enable && data->hwmon_dev) { + if (!(data->current_alarms & data->alert_alarms)) { + dev_dbg(&client->dev, "Re-enabling ALERT#\n"); + lm90_update_confreg(data, data->config & ~0x80); +-- +2.53.0 + diff --git a/queue-6.12/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch b/queue-6.12/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch new file mode 100644 index 0000000000..b44e92a9aa --- /dev/null +++ b/queue-6.12/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch @@ -0,0 +1,39 @@ +From fb8048d2ea8b827805d2fcc4a07e8d7229086ef1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 5 Feb 2025 12:27:15 -0800 +Subject: hwmon: (ltc4282) Fix reading the minimum alarm voltage + +From: Guenter Roeck + +[ Upstream commit 00feb1cce93dab948a299b69753d99c681d45a0b ] + +Coverity reports an out-of-bounds access when reading the minimum alarm +voltage for the VGPIO channel. Add the missing return statement to fix +the problem. + +Fixes: cbc29538dbf7 ("hwmon: Add driver for LTC4282") +Cc: Nuno Sa +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/ltc4282.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c +index d98c57918ce33..e1c0d02b564b7 100644 +--- a/drivers/hwmon/ltc4282.c ++++ b/drivers/hwmon/ltc4282.c +@@ -382,8 +382,8 @@ static int ltc4282_read_in(struct ltc4282_state *st, u32 attr, long *val, + channel, val); + case hwmon_in_min_alarm: + if (channel == LTC4282_CHAN_VGPIO) +- ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG, +- LTC4282_GPIO_ALARM_L_MASK, val); ++ return ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG, ++ LTC4282_GPIO_ALARM_L_MASK, val); + + return ltc4282_vdd_source_read_alm(st, + LTC4282_VSOURCE_ALARM_L_MASK, +-- +2.53.0 + diff --git a/queue-6.12/hwmon-nct6775-core-fix-number-of-temperature-registe.patch b/queue-6.12/hwmon-nct6775-core-fix-number-of-temperature-registe.patch new file mode 100644 index 0000000000..37a1574405 --- /dev/null +++ b/queue-6.12/hwmon-nct6775-core-fix-number-of-temperature-registe.patch @@ -0,0 +1,90 @@ +From 602b41a909121226b0a7dd11597a5c375a4c800c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 07:14:36 -0700 +Subject: hwmon: (nct6775-core) Fix number of temperature registers for NCT6116 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guenter Roeck + +[ Upstream commit b0e8adb2ccb43009796897ced09f91636685c9d3 ] + +Unlike NCT6106, NCT6116 only has three temperature registers, and with +it only three temperature source and temperature source configuration +registers. The register addresses match those of NCT6106 and can be +re-used. + +The code used a separate array to list the temperature source registers +for NCT6116, but used the size of the NCT6106 register array to set +the number of registers. The NCT6106 register array provides six addresses, +while the temperature source register array for NCT6116 only provides three +addresses. This causes a KASAN report. + +BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775] +Read of size 2 at addr ffffffffc19561a6 by task modprobe/954 +... +Call Trace: + dump_stack+0x7d/0xa7 + print_address_description.constprop.0+0x1c/0x220 + ? __kasan_kmalloc.constprop.0+0xc9/0xd0 + ? __kmalloc_node_track_caller+0x194/0x5b0 + ? nct6775_probe+0x936/0x46f0 [nct6775] + ? nct6775_probe+0x936/0x46f0 [nct6775] +... + +Fix the problem by hard-coding the number of temperature and temperature +configuration registers to three for NCT6116. Drop the unnecessary +NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE. + +Reported-by: Florian Bezdeka +Closes: https://lore.kernel.org/linux-hwmon/57cfc3fa-d4e9-4c10-8aa7-4ad0af7ebebe@roeck-us.net/T/#t +Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") +Cc: Björn Gerhart +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index 79bc67ffb9986..506d57025c3bd 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -846,8 +846,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 }; + static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 }; + static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 }; + static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 }; +-static const u16 NCT6116_REG_TEMP_SOURCE[] = { +- 0xb0, 0xb1, 0xb2 }; + + static const u16 NCT6116_REG_CRITICAL_TEMP[] = { + 0x11a, 0x12a, 0x13a, 0x19a, 0x1aa }; +@@ -3650,7 +3648,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + = NCT6106_CRITICAL_PWM_ENABLE_MASK; + data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM; + data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; +- data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE; ++ data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; +@@ -3664,13 +3662,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + + reg_temp = NCT6106_REG_TEMP; + reg_temp_mon = NCT6106_REG_TEMP_MON; +- num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); ++ num_reg_temp = 3; + num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON); + num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP); + reg_temp_over = NCT6106_REG_TEMP_OVER; + reg_temp_hyst = NCT6106_REG_TEMP_HYST; + reg_temp_config = NCT6106_REG_TEMP_CONFIG; +- num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG); ++ num_reg_temp_config = 3; + reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6106_REG_TEMP_CRIT; + reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; +-- +2.53.0 + diff --git a/queue-6.12/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-6.12/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch new file mode 100644 index 0000000000..a2309faa87 --- /dev/null +++ b/queue-6.12/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch @@ -0,0 +1,74 @@ +From 14d8e3a4e9a76e7ee6f06d2cfd4121f5626f4706 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 13:35:37 -0700 +Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guenter Roeck + +[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ] + +Sashiko reports: + +During initialization of the nct6116 chip, the driver sets data->pwm_num +to 5. However, it assigns several NCT6106 register arrays (such as +NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and +NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP. +These arrays only contain 3 elements. + +In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If +data->has_pwm has bits 3 or 4 set (which is structurally possible for +nct6116), the loop attempts to read elements at index 3 and 4 from these +3-element arrays. This results in a global out-of-bounds read, which can +be caught by KASAN. + +Furthermore, the driver uses these garbage out-of-bounds values as +hardware register addresses for subsequent read and write operations. This +leads to invalid hardware register access, potentially causing hardware +misconfiguration or system crashes. + +The underlying problem is that the chip does support up to five fan +control channels, but only the first three support weight control. +Fix the problem by extending the affected weight register arrays with +zeroed fields. The driver uses zeroed register addresses to determine +if a register is supported or not, and skips accesses for unsupported +registers. + +Reported-by: Sashiko +Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") +Cc: Björn Gerhart +Cc: Florian Bezdeka +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index 506d57025c3bd..ce2cf1f229004 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -791,12 +791,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 }; + + static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 }; + +-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 }; + + static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 }; + static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 }; +-- +2.53.0 + diff --git a/queue-6.12/hwmon-nzxt-smart2-dma-align-output-buffer.patch b/queue-6.12/hwmon-nzxt-smart2-dma-align-output-buffer.patch new file mode 100644 index 0000000000..6c3ac15b7d --- /dev/null +++ b/queue-6.12/hwmon-nzxt-smart2-dma-align-output-buffer.patch @@ -0,0 +1,53 @@ +From 0501320f32578722239a07e67369590ed2c3ff49 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 09:54:23 -0700 +Subject: hwmon: (nzxt-smart2) DMA-align output buffer + +From: Guenter Roeck + +[ Upstream commit 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 ] + +Sashiko reports: + +When send_output_report() calls hid_hw_output_report(), the underlying USB +HID core calls usb_interrupt_msg() which maps this buffer directly for DMA. + +When the DMA mapping flushes or invalidates the cacheline, it will corrupt +the adjacent variables (mutex, update_interval) that were modified +concurrently by the CPU. This causes memory corruption due to cacheline +sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA +API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings +for this violation. + +Any operation that triggers send_output_report() (like setting a fan speed +or updating the interval) causes the USB DMA mapping. On systems with +non-coherent caches, this structural bug causes immediate and deterministic +memory corruption. + +Align the output buffer to ARCH_DMA_MINALIGN to fix the problem. + +Reported-by: Sashiko +Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.") +Cc: Aleksandr Mezin +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nzxt-smart2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c +index 6f8febda4277c..66c5886f411a6 100644 +--- a/drivers/hwmon/nzxt-smart2.c ++++ b/drivers/hwmon/nzxt-smart2.c +@@ -203,7 +203,7 @@ struct drvdata { + */ + struct mutex mutex; + long update_interval; +- u8 output_buffer[OUTPUT_REPORT_SIZE]; ++ u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN); + }; + + static long scale_pwm_value(long val, long orig_max, long new_max) +-- +2.53.0 + diff --git a/queue-6.12/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-6.12/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch new file mode 100644 index 0000000000..7f471f010f --- /dev/null +++ b/queue-6.12/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch @@ -0,0 +1,39 @@ +From 8858dde4f59016376548bf60728a01cfcf232f8b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 08:41:40 -0700 +Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data() + +From: Guenter Roeck + +[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ] + +pmbus_update_byte_data() is supposed to return a negative error code or 0. +However, if no change is made to the register, it actually returns the +register value. This can result in problems if the calling code explicitly +expects to see an error code or 0. + +Fix it to return 0 on success or the error code as expected. + +Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/pmbus/pmbus_core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c +index a431cce336034..e08e30f288130 100644 +--- a/drivers/hwmon/pmbus/pmbus_core.c ++++ b/drivers/hwmon/pmbus/pmbus_core.c +@@ -508,7 +508,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + if (tmp != rv) + rv = _pmbus_write_byte_data(client, page, reg, tmp); + +- return rv; ++ return rv < 0 ? rv : 0; + } + EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS); + +-- +2.53.0 + diff --git a/queue-6.12/hwmon-sht3x-fix-unaligned-accesses.patch b/queue-6.12/hwmon-sht3x-fix-unaligned-accesses.patch new file mode 100644 index 0000000000..bfd8beee3e --- /dev/null +++ b/queue-6.12/hwmon-sht3x-fix-unaligned-accesses.patch @@ -0,0 +1,76 @@ +From e523910a26393a5ab5d75cf8ebd7e40fd70252aa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 09:34:46 -0700 +Subject: hwmon: (sht3x) Fix unaligned accesses + +From: Guenter Roeck + +[ Upstream commit f46d5ab43a572b84773015a76966f5da56fc1748 ] + +Sashiko reports: + +In sht3x_update_client(), the 16-bit temperature and humidity values are +extracted from a stack-allocated byte array using be16_to_cpup(). The +pointers passed to this function are calculated as buf and buf + 3. Since +the difference between the two pointers is an odd number of bytes, at +least one of them is guaranteed to be at an unaligned offset. + +This will trigger an alignment fault on strict-alignment architectures +such as ARMv5 or SPARC, resulting in a kernel panic. + +Fix the problem by using get_unaligned_be16() instead of be16_to_cpup(), +and put_unaligned_be16() instead of cpu_to_be16(). + +Fixes: 7c84f7f80d6f ("hwmon: add support for Sensirion SHT3x sensors") +Reported-by: Sashiko +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/sht3x.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c +index 94466e28dc56f..1f63c07cfb085 100644 +--- a/drivers/hwmon/sht3x.c ++++ b/drivers/hwmon/sht3x.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + /* commands (high repeatability mode) */ + static const unsigned char sht3x_cmd_measure_single_hpm[] = { 0x24, 0x00 }; +@@ -279,9 +280,9 @@ static struct sht3x_data *sht3x_update_client(struct device *dev) + if (ret) + goto out; + +- val = be16_to_cpup((__be16 *)buf); ++ val = get_unaligned_be16(buf); + data->temperature = sht3x_extract_temperature(val); +- val = be16_to_cpup((__be16 *)(buf + 3)); ++ val = get_unaligned_be16(buf + 3); + data->humidity = sht3x_extract_humidity(val); + data->last_update = jiffies; + } +@@ -339,7 +340,7 @@ static int limits_update(struct sht3x_data *data) + if (ret) + return ret; + +- raw = be16_to_cpup((__be16 *)buffer); ++ raw = get_unaligned_be16(buffer); + temperature = sht3x_extract_temperature((raw & 0x01ff) << 7); + humidity = sht3x_extract_humidity(raw & 0xfe00); + data->temperature_limits[index] = temperature; +@@ -392,7 +393,7 @@ static size_t limit_write(struct device *dev, + raw = ((u32)(temperature + 45000) * 24543) >> (16 + 7); + raw |= ((humidity * 42950) >> 16) & 0xfe00; + +- *((__be16 *)position) = cpu_to_be16(raw); ++ put_unaligned_be16(raw, position); + position += SHT3X_WORD_LEN; + *position = crc8(sht3x_crc8_table, + position - SHT3X_WORD_LEN, +-- +2.53.0 + diff --git a/queue-6.12/idpf-adjust-txq-ring-count-minimum.patch b/queue-6.12/idpf-adjust-txq-ring-count-minimum.patch new file mode 100644 index 0000000000..f4f49666ba --- /dev/null +++ b/queue-6.12/idpf-adjust-txq-ring-count-minimum.patch @@ -0,0 +1,72 @@ +From 414320955368289fd160f8d78e3f62a6c497babc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 30 Jun 2026 16:56:19 -0700 +Subject: idpf: adjust TxQ ring count minimum + +From: Joshua Hay + +[ Upstream commit bef152db47debcd14cbacefc5767f6f026c4bc89 ] + +Set the TxQ ring count minimum to 128 descriptors. Any lower than this, +and the queue will stall and trigger Tx timeouts in flow based +scheduling mode. This is because next_to_clean might never be updated. + +In flow based scheduling mode, next_to_clean is only updated after a +descriptor completion is processed, i.e. after the RE bit is set in the +last descriptor of a Tx packet. This will never happen with a ring size +of 64 and an IDPF_TX_SPLITQ_RE_MIN_GAP of 64. No matter what the value +of last_re is initialized/set to, the calculated gap will be at most 63 +and never trigger the RE bit. + +Even a ring size of 96 does not solve this. Because of how infrequent +next_to_clean is updated and how small the ring is, IDPF_DESC_UNUSED +will be much smaller on average. This increases the chance the queue +will be stopped because a multi-descriptor packet, e.g. a large LSO +packet, does not see enough resources on the ring. In this case, the +queue will trigger the stop logic. The queue permanently stalls because +there is no chance for a descriptor completion to update next_to_clean +since it is dependent on a packet being sent. + +Fixes: 5f417d551324 ("idpf: replace flow scheduling buffer ring with buffer pool") +Signed-off-by: Joshua Hay +Reviewed-by: Aleksandr Loktionov +Tested-by: Samuel Salin +Signed-off-by: Tony Nguyen +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/idpf/idpf_txrx.c | 5 +---- + drivers/net/ethernet/intel/idpf/idpf_txrx.h | 2 +- + 2 files changed, 2 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c +index 82a927265b9cf..afb922e8704d0 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c ++++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c +@@ -2507,10 +2507,7 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb, + + tx_params.dtype = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE; + tx_params.eop_cmd = IDPF_TXD_FLEX_FLOW_CMD_EOP; +- /* Set the RE bit to periodically "clean" the descriptor ring. +- * MIN_GAP is set to MIN_RING size to ensure it will be set at +- * least once each time around the ring. +- */ ++ /* Set the RE bit periodically to "clean" the descriptor ring */ + if (idpf_tx_splitq_need_re(tx_q)) { + tx_params.eop_cmd |= IDPF_TXD_FLEX_FLOW_CMD_RE; + tx_q->txq_grp->num_completions_pending++; +diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h +index a34c791c46088..2d9960dd51ad5 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h ++++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h +@@ -19,7 +19,7 @@ + /* Mailbox Queue */ + #define IDPF_MAX_MBXQ 1 + +-#define IDPF_MIN_TXQ_DESC 64 ++#define IDPF_MIN_TXQ_DESC 128 + #define IDPF_MIN_RXQ_DESC 64 + #define IDPF_MIN_TXQ_COMPLQ_DESC 256 + #define IDPF_MAX_QIDS 256 +-- +2.53.0 + diff --git a/queue-6.12/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch b/queue-6.12/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch new file mode 100644 index 0000000000..32b2d555a0 --- /dev/null +++ b/queue-6.12/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch @@ -0,0 +1,41 @@ +From 44c5cfb9678052340d09a3955baeb004408e7fac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 3 Jul 2026 01:03:32 -0400 +Subject: idpf: Fix mailbox IRQ name leak on request failure + +From: Yuho Choi + +[ Upstream commit 9bff30482c10f70d9e56c0633a6616e07140e217 ] + +idpf_mb_intr_req_irq() allocates the mailbox IRQ name before calling +request_irq(). On success, the name is released later through +kfree(free_irq()), but request_irq() failure returns without freeing it. + +Free the allocated name on the request_irq() failure path. + +Fixes: 4930fbf419a7 ("idpf: add core init and interrupt request") +Signed-off-by: Yuho Choi +Reviewed-by: Aleksandr Loktionov +Tested-by: Samuel Salin +Signed-off-by: Tony Nguyen +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/idpf/idpf_lib.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c +index 4973135c346a2..c2101f7f6e1bf 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c ++++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c +@@ -134,7 +134,7 @@ static int idpf_mb_intr_req_irq(struct idpf_adapter *adapter) + if (err) { + dev_err(&adapter->pdev->dev, + "IRQ request for mailbox failed, error: %d\n", err); +- ++ kfree(name); + return err; + } + +-- +2.53.0 + diff --git a/queue-6.12/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch b/queue-6.12/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch new file mode 100644 index 0000000000..78c6e23015 --- /dev/null +++ b/queue-6.12/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch @@ -0,0 +1,335 @@ +From 8439b7f8a9a0140b8345812c4a27fe9a744e59aa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:17 +0300 +Subject: ipvs: do not mangle ICMP replies for non-first fragments + +From: Julian Anastasov + +[ Upstream commit 342e24a339b90e8e339a0f8c151ca479b8565661 ] + +Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the +payload for embedded non-first IPv4 fragments. The problem is +in the very old inverted pp->dont_defrag check which should not +continue when embedded is a non-first TCP/UDP/SCTP fragment. + +Check for embedded non-first fragment is also missing from +ip_vs_out_icmp_v6(), it is needed before any connection +lookups that expect ports after the network headers. + +Drop the blocking code from ip_vs_in_icmp_v6() which prevents +ICMPv6 from local clients to use non-MASQ forwarding. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Link: https://sashiko.dev/#/patchset/20260720201122.79882-1-ja%40ssi.bg +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 11 +++--- + net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++--------------------- + net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++---- + 3 files changed, 48 insertions(+), 52 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index a44979db80134..6935ec09af24d 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -1627,8 +1627,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph); ++ unsigned int hooknum, struct ip_vs_iphdr *ciph); + void ip_vs_dest_dst_rcu_free(struct rcu_head *head); + + #ifdef CONFIG_IP_VS_IPV6 +@@ -1642,8 +1641,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph); ++ unsigned int hooknum, struct ip_vs_iphdr *ciph); + #endif + + #ifdef CONFIG_SYSCTL +@@ -1708,12 +1706,13 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) + } + + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir, unsigned int toff); ++ struct ip_vs_conn *cp, int dir, unsigned int toff, ++ bool has_ports); + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int dir, unsigned int toff, +- struct ip_vs_iphdr *ciph); ++ bool has_ports, struct ip_vs_iphdr *ciph); + #endif + + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index 784c00ec01ef7..6207a91e93f3b 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -746,7 +746,8 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, + * - inout: 1=in->out, 0=out->in + */ + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout, unsigned int toff) ++ struct ip_vs_conn *cp, int inout, unsigned int toff, ++ bool has_ports) + { + struct iphdr *iph = ip_hdr(skb); + struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); +@@ -766,8 +767,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + } + + /* the TCP/UDP/SCTP port */ +- if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol || +- IPPROTO_SCTP == ciph->protocol) { ++ if (has_ports) { + __be16 *ports = (void *)ciph + ciph->ihl*4; + + if (inout) +@@ -792,18 +792,15 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int inout, unsigned int toff, +- struct ip_vs_iphdr *ciph) ++ bool has_ports, struct ip_vs_iphdr *ciph) + { + struct ipv6hdr *iph = ipv6_hdr(skb); +- int protocol; + struct icmp6hdr *icmph; + struct ipv6hdr *cih; + + icmph = (struct icmp6hdr *)(skb->data + toff); + cih = (struct ipv6hdr *)(skb->data + ciph->off); + +- protocol = ciph->protocol; +- + if (inout) { + iph->saddr = cp->vaddr.in6; + cih->daddr = cp->vaddr.in6; +@@ -813,9 +810,7 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + } + + /* the TCP/UDP/SCTP port */ +- if (!ciph->fragoffs && +- (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || +- protocol == IPPROTO_SCTP)) { ++ if (has_ports) { + __be16 *ports = (void *)(skb->data + ciph->len); + + IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, +@@ -857,6 +852,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; + unsigned int ctoff = ciph->len; ++ bool has_ports = false; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; +@@ -870,17 +866,19 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + } + + if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || +- ciph->protocol == IPPROTO_SCTP) ++ ciph->protocol == IPPROTO_SCTP) { + ctoff += 2 * sizeof(__u16); ++ has_ports = true; ++ } + if (skb_ensure_writable(skb, ctoff)) + goto out; + + #ifdef CONFIG_IP_VS_IPV6 + if (af == AF_INET6) +- ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph); + else + #endif +- ip_vs_nat_icmp(skb, pp, cp, 1, toff); ++ ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports); + + if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) + goto out; +@@ -964,8 +962,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + + /* Is the embedded protocol header present? */ +- if (unlikely(cih->frag_off & htons(IP_OFFSET) && +- pp->dont_defrag)) ++ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, +@@ -1029,6 +1026,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (!pp) + return NF_ACCEPT; + ++ /* Is the embedded protocol header present? */ ++ if (unlikely(ciph.fragoffs && !pp->dont_defrag)) ++ return NF_ACCEPT; ++ + /* The embedded headers contain source and dest in reverse order */ + cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto, + ipvs, AF_INET6, skb, &ciph); +@@ -1687,8 +1688,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + pp = pd->pp; + + /* Is the embedded protocol header present? */ +- if (unlikely(cih->frag_off & htons(IP_OFFSET) && +- pp->dont_defrag)) ++ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, +@@ -1696,7 +1696,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + + offset2 = offset; + ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph); +- offset = ciph.len; + + /* The embedded headers contain source and dest in reverse order. + * For IPIP/UDP/GRE tunnel this is error for request, not for reply. +@@ -1790,11 +1789,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); +- if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || +- IPPROTO_SCTP == cih->protocol) +- offset += 2 * sizeof(__u16); +- verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, +- &ciph); ++ verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph); + + out: + if (likely(!new_cp)) +@@ -1854,8 +1849,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + pp = pd->pp; + +- /* Cannot handle fragmented embedded protocol */ +- if (ciph.fragoffs) ++ /* Is the embedded protocol header present? */ ++ if (ciph.fragoffs && !pp->dont_defrag) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset, +@@ -1879,13 +1874,6 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + new_cp = true; + } + +- /* VS/TUN, VS/DR and LOCALNODE just let it go */ +- if ((hooknum == NF_INET_LOCAL_OUT) && +- (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) { +- verdict = NF_ACCEPT; +- goto out; +- } +- + verdict = NF_DROP; + + /* Ensure the checksum is correct */ +@@ -1901,14 +1889,7 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); + +- /* Need to mangle contained IPv6 header in ICMPv6 packet */ +- offset = ciph.len; +- if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol || +- IPPROTO_SCTP == ciph.protocol) +- offset += 2 * sizeof(__u16); /* Also mangle ports */ +- +- verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, +- &ciph); ++ verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph); + + out: + if (likely(!new_cp)) +diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c +index 05e6164c45039..b85420a3a14ce 100644 +--- a/net/netfilter/ipvs/ip_vs_xmit.c ++++ b/net/netfilter/ipvs/ip_vs_xmit.c +@@ -1500,13 +1500,14 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + int + ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph) ++ unsigned int hooknum, struct ip_vs_iphdr *ciph) + { + struct rtable *rt; /* Route to the other host */ + int rc; + int local; + int rt_mode, was_input; ++ bool has_ports = false; ++ unsigned int wlen; + + /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be + forwarded directly here, because there is no need to +@@ -1562,6 +1563,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + goto tx_error; + } + ++ wlen = ciph->len; ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) { ++ wlen += 2 * sizeof(__u16); /* Also mangle ports */ ++ has_ports = true; ++ } ++ + /* copy-on-write the packet before mangling it */ + if (skb_ensure_writable(skb, wlen)) + goto tx_error; +@@ -1569,7 +1577,7 @@ 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); ++ ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +@@ -1586,10 +1594,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + int + ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph) ++ unsigned int hooknum, struct ip_vs_iphdr *ciph) + { ++ bool has_ports = false; + struct rt6_info *rt; /* Route to the other host */ ++ unsigned int wlen; + int rc; + int local; + int rt_mode; +@@ -1647,6 +1656,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + goto tx_error; + } + ++ wlen = ciph->len; ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) { ++ wlen += 2 * sizeof(__u16); /* Also mangle ports */ ++ has_ports = true; ++ } ++ + /* copy-on-write the packet before mangling it */ + if (skb_ensure_writable(skb, wlen)) + goto tx_error; +@@ -1654,7 +1670,7 @@ ip_vs_icmp_xmit_v6(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_v6(skb, pp, cp, 0, toff, ciph); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +-- +2.53.0 + diff --git a/queue-6.12/ipvs-fix-places-with-wrong-packet-offsets.patch b/queue-6.12/ipvs-fix-places-with-wrong-packet-offsets.patch new file mode 100644 index 0000000000..6390487ec3 --- /dev/null +++ b/queue-6.12/ipvs-fix-places-with-wrong-packet-offsets.patch @@ -0,0 +1,624 @@ +From b03fe65a4291e243bb661439174eb0aae4b2756f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:16 +0300 +Subject: ipvs: fix places with wrong packet offsets + +From: Julian Anastasov + +[ Upstream commit 15cab31a3730e05f0767b922a7450e5d784b2607 ] + +The offsets we use to packet headers and payloads should be +based on skb->data. We even already respect non-zero +network offset in ip_vs_fill_iph_skb() but some places +do it wrongly and support only zero offset which is expected +for the IP layer where IPVS has hooks. + +Change all places that instead of skb->data use offsets based +on the network header (skb_network_header, ip_hdr, etc) because +this doubles the network offset as noted by Sashiko. + +For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header +parsing done by the caller. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Link: https://sashiko.dev/#/patchset/20260710143733.29741-2-fw%40strlen.de +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 15 +-- + net/netfilter/ipvs/ip_vs_app.c | 4 +- + net/netfilter/ipvs/ip_vs_core.c | 133 +++++++++++++------------- + net/netfilter/ipvs/ip_vs_proto_sctp.c | 4 +- + net/netfilter/ipvs/ip_vs_proto_tcp.c | 4 +- + net/netfilter/ipvs/ip_vs_proto_udp.c | 4 +- + net/netfilter/ipvs/ip_vs_xmit.c | 26 ++--- + 7 files changed, 97 insertions(+), 93 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index aef042039cb00..a44979db80134 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -1626,8 +1626,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, +- unsigned int hooknum, struct ip_vs_iphdr *iph); ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph); + void ip_vs_dest_dst_rcu_free(struct rcu_head *head); + + #ifdef CONFIG_IP_VS_IPV6 +@@ -1640,8 +1641,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, +- unsigned int hooknum, struct ip_vs_iphdr *iph); ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph); + #endif + + #ifdef CONFIG_SYSCTL +@@ -1706,11 +1708,12 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) + } + + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir); ++ struct ip_vs_conn *cp, int dir, unsigned int toff); + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir); ++ struct ip_vs_conn *cp, int dir, unsigned int toff, ++ struct ip_vs_iphdr *ciph); + #endif + + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) +diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c +index f9132b359f0c6..0c690a30a85dc 100644 +--- a/net/netfilter/ipvs/ip_vs_app.c ++++ b/net/netfilter/ipvs/ip_vs_app.c +@@ -368,7 +368,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb, + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) + return 0; + +- th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); ++ th = (struct tcphdr *)(skb->data + ipvsh->len); + + /* + * Remember seq number in case this pkt gets resized +@@ -444,7 +444,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb, + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) + return 0; + +- th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); ++ th = (struct tcphdr *)(skb->data + ipvsh->len); + + /* + * Remember seq number in case this pkt gets resized +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index 306a8227d300e..784c00ec01ef7 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -746,13 +746,12 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, + * - inout: 1=in->out, 0=out->in + */ + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout) ++ struct ip_vs_conn *cp, int inout, unsigned int toff) + { + struct iphdr *iph = ip_hdr(skb); +- unsigned int icmp_offset = iph->ihl*4; +- struct icmphdr *icmph = (struct icmphdr *)(skb_network_header(skb) + +- icmp_offset); ++ struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); + struct iphdr *ciph = (struct iphdr *)(icmph + 1); ++ unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr); + + if (inout) { + iph->saddr = cp->vaddr.ip; +@@ -779,48 +778,45 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + + /* And finally the ICMP checksum */ + icmph->checksum = 0; +- icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset); ++ icmph->checksum = ip_vs_checksum_complete(skb, toff); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (inout) +- IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, +- "Forwarding altered outgoing ICMP"); ++ IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, ++ "Forwarding altered outgoing ICMP"); + else +- IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, +- "Forwarding altered incoming ICMP"); ++ IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, ++ "Forwarding altered incoming ICMP"); + } + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout) ++ struct ip_vs_conn *cp, int inout, unsigned int toff, ++ struct ip_vs_iphdr *ciph) + { + struct ipv6hdr *iph = ipv6_hdr(skb); +- unsigned int icmp_offset = 0; +- unsigned int offs = 0; /* header offset*/ + int protocol; + struct icmp6hdr *icmph; +- struct ipv6hdr *ciph; +- unsigned short fragoffs; ++ struct ipv6hdr *cih; + +- ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL); +- icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset); +- offs = icmp_offset + sizeof(struct icmp6hdr); +- ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs); ++ icmph = (struct icmp6hdr *)(skb->data + toff); ++ cih = (struct ipv6hdr *)(skb->data + ciph->off); + +- protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL); ++ protocol = ciph->protocol; + + if (inout) { + iph->saddr = cp->vaddr.in6; +- ciph->daddr = cp->vaddr.in6; ++ cih->daddr = cp->vaddr.in6; + } else { + iph->daddr = cp->daddr.in6; +- ciph->saddr = cp->daddr.in6; ++ cih->saddr = cp->daddr.in6; + } + + /* the TCP/UDP/SCTP port */ +- if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || +- IPPROTO_SCTP == protocol)) { +- __be16 *ports = (void *)(skb_network_header(skb) + offs); ++ if (!ciph->fragoffs && ++ (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || ++ protocol == IPPROTO_SCTP)) { ++ __be16 *ports = (void *)(skb->data + ciph->len); + + IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, + ntohs(inout ? ports[1] : ports[0]), +@@ -833,19 +829,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + + /* And finally the ICMP checksum */ + icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, +- skb->len - icmp_offset, ++ skb->len - toff, + IPPROTO_ICMPV6, 0); +- skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset; ++ skb->csum_start = skb_headroom(skb) + toff; + skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum); + skb->ip_summed = CHECKSUM_PARTIAL; + + if (inout) +- IP_VS_DBG_PKT(11, AF_INET6, pp, skb, +- (void *)ciph - (void *)iph, ++ IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, + "Forwarding altered outgoing ICMPv6"); + else +- IP_VS_DBG_PKT(11, AF_INET6, pp, skb, +- (void *)ciph - (void *)iph, ++ IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, + "Forwarding altered incoming ICMPv6"); + } + #endif +@@ -855,37 +849,38 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + */ + static int handle_response_icmp(int af, struct sk_buff *skb, + union nf_inet_addr *snet, +- __u8 protocol, struct ip_vs_conn *cp, ++ struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, +- unsigned int offset, unsigned int ihl, +- unsigned int hooknum) ++ struct ip_vs_iphdr *ciph, ++ unsigned int toff, unsigned int hooknum) + { + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; ++ unsigned int ctoff = ciph->len; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; + + /* Ensure the checksum is correct */ +- if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { ++ if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) { + /* Failed checksum! */ + IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", + IP_VS_DBG_ADDR(af, snet)); + goto out; + } + +- if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || +- IPPROTO_SCTP == protocol) +- offset += 2 * sizeof(__u16); +- if (skb_ensure_writable(skb, offset)) ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) ++ ctoff += 2 * sizeof(__u16); ++ if (skb_ensure_writable(skb, ctoff)) + goto out; + + #ifdef CONFIG_IP_VS_IPV6 + if (af == AF_INET6) +- ip_vs_nat_icmp_v6(skb, pp, cp, 1); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); + else + #endif +- ip_vs_nat_icmp(skb, pp, cp, 1); ++ ip_vs_nat_icmp(skb, pp, cp, 1, toff); + + if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) + goto out; +@@ -913,9 +908,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + * Currently handles error types - unreachable, quench, ttl exceeded. + */ + static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, +- int *related, unsigned int hooknum) ++ int *related, unsigned int hooknum, ++ struct ip_vs_iphdr *ipvsh) + { +- struct iphdr *iph; + struct icmphdr _icmph, *ic; + struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ + struct ip_vs_iphdr ciph; +@@ -930,17 +925,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (ip_is_fragment(ip_hdr(skb))) { + if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) + return NF_STOLEN; ++ if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh)) ++ return NF_ACCEPT; + } + +- iph = ip_hdr(skb); +- offset = ihl = iph->ihl * 4; ++ ihl = ipvsh->len; ++ offset = ipvsh->len; + ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); + if (ic == NULL) + return NF_DROP; + + IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n", + ic->type, ntohs(icmp_id(ic)), +- &iph->saddr, &iph->daddr); ++ &ipvsh->saddr.ip, &ipvsh->daddr.ip); + + /* + * Work through seeing if this is for us. +@@ -959,7 +956,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + /* Now find the contained IP header */ + offset += sizeof(_icmph); + cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); +- if (cih == NULL) ++ if (!(cih && cih->version == 4 && cih->ihl >= 5)) + return NF_ACCEPT; /* The packet looks wrong, ignore */ + + pp = ip_vs_proto_get(cih->protocol); +@@ -982,9 +979,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (!cp) + return NF_ACCEPT; + +- snet.ip = iph->saddr; +- return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp, +- pp, ciph.len, ihl, hooknum); ++ snet.ip = ipvsh->saddr.ip; ++ return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl, ++ hooknum); + } + + #ifdef CONFIG_IP_VS_IPV6 +@@ -997,7 +994,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + struct ip_vs_conn *cp; + struct ip_vs_protocol *pp; + union nf_inet_addr snet; +- unsigned int offset; + + *related = 1; + ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph); +@@ -1040,9 +1036,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + + snet.in6 = ciph.saddr.in6; +- offset = ciph.len; +- return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp, +- pp, offset, ipvsh->len, hooknum); ++ return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph, ++ ipvsh->len, hooknum); + } + #endif + +@@ -1368,7 +1363,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat + #endif + if (unlikely(iph.protocol == IPPROTO_ICMP)) { + int related; +- int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum); ++ int verdict = ip_vs_out_icmp(ipvs, skb, &related, ++ hooknum, &iph); + + if (related) + return verdict; +@@ -1576,9 +1572,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb, + */ + static int + ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, +- unsigned int hooknum) ++ unsigned int hooknum, struct ip_vs_iphdr *iph) + { +- struct iphdr *iph; + struct icmphdr _icmph, *ic; + struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ + struct ip_vs_iphdr ciph; +@@ -1588,7 +1583,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + unsigned int offset, offset2, ihl, verdict; + bool tunnel, new_cp = false; + union nf_inet_addr *raddr; +- char *outer_proto = "IPIP"; ++ char *outer_proto __maybe_unused = "IPIP"; + unsigned int hlen_ipip; + int ulen = 0; + +@@ -1598,17 +1593,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + if (ip_is_fragment(ip_hdr(skb))) { + if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) + return NF_STOLEN; ++ if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph)) ++ return NF_ACCEPT; + } + +- iph = ip_hdr(skb); +- offset = ihl = iph->ihl * 4; ++ ihl = iph->len; ++ offset = iph->len; + ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); + if (ic == NULL) + return NF_DROP; + + IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n", + ic->type, ntohs(icmp_id(ic)), +- &iph->saddr, &iph->daddr); ++ &iph->saddr.ip, &iph->daddr.ip); + + /* + * Work through seeing if this is for us. +@@ -1725,7 +1722,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { + /* Failed checksum! */ + IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", +- &iph->saddr); ++ &iph->saddr.ip); + goto out; + } + +@@ -1796,7 +1793,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || + IPPROTO_SCTP == cih->protocol) + offset += 2 * sizeof(__u16); +- verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph); ++ verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, ++ &ciph); + + out: + if (likely(!new_cp)) +@@ -1909,7 +1907,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + IPPROTO_SCTP == ciph.protocol) + offset += 2 * sizeof(__u16); /* Also mangle ports */ + +- verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph); ++ verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, ++ &ciph); + + out: + if (likely(!new_cp)) +@@ -1988,7 +1987,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state + if (unlikely(iph.protocol == IPPROTO_ICMP)) { + int related; + int verdict = ip_vs_in_icmp(ipvs, skb, &related, +- hooknum); ++ hooknum, &iph); + + if (related) + return verdict; +@@ -2124,6 +2123,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) + { + struct netns_ipvs *ipvs = net_ipvs(state->net); ++ struct ip_vs_iphdr iphdr; + int r; + + /* ipvs enabled in this netns ? */ +@@ -2133,10 +2133,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + if (state->pf == NFPROTO_IPV4) { + if (ip_hdr(skb)->protocol != IPPROTO_ICMP) + return NF_ACCEPT; ++ ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr); + #ifdef CONFIG_IP_VS_IPV6 + } else { +- struct ip_vs_iphdr iphdr; +- + ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr); + + if (iphdr.protocol != IPPROTO_ICMPV6) +@@ -2146,7 +2145,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + #endif + } + +- return ip_vs_in_icmp(ipvs, skb, &r, state->hook); ++ return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr); + } + + static const struct nf_hook_ops ip_vs_ops4[] = { +diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c +index f6f732b7dfa86..3dbd3096e1637 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c +@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- sctph = (void *) skb_network_header(skb) + sctphoff; ++ sctph = (void *)skb->data + sctphoff; + + /* Only update csum if we really have to */ + if (sctph->source != cp->vport || payload_csum || +@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- sctph = (void *) skb_network_header(skb) + sctphoff; ++ sctph = (void *)skb->data + sctphoff; + + /* Only update csum if we really have to */ + if (sctph->dest != cp->dport || payload_csum || +diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c +index bf31127338aa0..1ac9c233537d3 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c +@@ -180,7 +180,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- tcph = (void *)skb_network_header(skb) + tcphoff; ++ tcph = (void *)skb->data + tcphoff; + tcph->source = cp->vport; + + /* Adjust TCP checksums */ +@@ -261,7 +261,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- tcph = (void *)skb_network_header(skb) + tcphoff; ++ tcph = (void *)skb->data + tcphoff; + tcph->dest = cp->dport; + + /* +diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c +index 40d30649b3048..96ac882df15c1 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_udp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c +@@ -171,7 +171,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- udph = (void *)skb_network_header(skb) + udphoff; ++ udph = (void *)skb->data + udphoff; + udph->source = cp->vport; + + /* +@@ -255,7 +255,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- udph = (void *)skb_network_header(skb) + udphoff; ++ udph = (void *)skb->data + udphoff; + udph->dest = cp->dport; + + /* +diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c +index fa1b03c3201f1..05e6164c45039 100644 +--- a/net/netfilter/ipvs/ip_vs_xmit.c ++++ b/net/netfilter/ipvs/ip_vs_xmit.c +@@ -1499,8 +1499,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + */ + int + ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, unsigned int hooknum, +- struct ip_vs_iphdr *iph) ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph) + { + struct rtable *rt; /* Route to the other host */ + int rc; +@@ -1512,7 +1513,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + translate address/port back */ + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { + if (cp->packet_xmit) +- rc = cp->packet_xmit(skb, cp, pp, iph); ++ rc = cp->packet_xmit(skb, cp, pp, ciph); + else + rc = NF_ACCEPT; + /* do not touch skb anymore */ +@@ -1530,7 +1531,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; + local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode, +- NULL, iph); ++ NULL, ciph); + if (local < 0) + goto tx_error; + rt = skb_rtable(skb); +@@ -1562,13 +1563,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + } + + /* copy-on-write the packet before mangling it */ +- if (skb_ensure_writable(skb, offset)) ++ if (skb_ensure_writable(skb, wlen)) + goto tx_error; + + if (skb_cow(skb, rt->dst.dev->hard_header_len)) + goto tx_error; + +- ip_vs_nat_icmp(skb, pp, cp, 0); ++ ip_vs_nat_icmp(skb, pp, cp, 0, toff); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +@@ -1584,8 +1585,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + #ifdef CONFIG_IP_VS_IPV6 + int + ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, unsigned int hooknum, +- struct ip_vs_iphdr *ipvsh) ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph) + { + struct rt6_info *rt; /* Route to the other host */ + int rc; +@@ -1597,7 +1599,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + translate address/port back */ + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { + if (cp->packet_xmit) +- rc = cp->packet_xmit(skb, cp, pp, ipvsh); ++ rc = cp->packet_xmit(skb, cp, pp, ciph); + else + rc = NF_ACCEPT; + /* do not touch skb anymore */ +@@ -1614,7 +1616,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; + local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest, +- &cp->daddr.in6, NULL, ipvsh, 0, rt_mode); ++ &cp->daddr.in6, NULL, ciph, 0, rt_mode); + if (local < 0) + goto tx_error; + rt = dst_rt6_info(skb_dst(skb)); +@@ -1646,13 +1648,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + } + + /* copy-on-write the packet before mangling it */ +- if (skb_ensure_writable(skb, offset)) ++ if (skb_ensure_writable(skb, wlen)) + goto tx_error; + + if (skb_cow(skb, rt->dst.dev->hard_header_len)) + goto tx_error; + +- ip_vs_nat_icmp_v6(skb, pp, cp, 0); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +-- +2.53.0 + diff --git a/queue-6.12/ipvs-fix-the-checksum-validations.patch b/queue-6.12/ipvs-fix-the-checksum-validations.patch new file mode 100644 index 0000000000..7c03fbff79 --- /dev/null +++ b/queue-6.12/ipvs-fix-the-checksum-validations.patch @@ -0,0 +1,389 @@ +From e99237f82626252ac5667bb2361b842346ce77b1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:15 +0300 +Subject: ipvs: fix the checksum validations + +From: Julian Anastasov + +[ Upstream commit e876b75b9020a97bbdc79721e7fc749024891c65 ] + +ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6 +packets from clients. In fact, as for TCP/UDP we should +validate the checksum for ICMP packets only when we +mangle the packets on MASQ or on reply for tunnel. + +Also, Sashiko points out that handle_response_icmp() being +common for IPv4 and IPv6 is missing the pseudo-header +calculation while validating ICMPv6 messages from real +servers which is a problem if checksum is not validated +by the hardware. + +Fix the problems by creating ip_vs_checksum_common_check() +helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6. +Rely on the nf_checksum() for validating the ICMP messages +but use it also for TCP and UDP. + +Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP. + +IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum +validation on LOCAL_OUT (local clients or local real +servers) and on FORWARD (traffic from servers on LAN). +Do it only on LOCAL_IN, in case nf_checksum() is not +called on PRE_ROUTING. + +Also, ip_vs_checksum_complete() can be marked static. + +Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6") +Link: https://sashiko.dev/#/patchset/20260708180315.77413-1-ja%40ssi.bg +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 31 +++++++++++++++-- + net/netfilter/ipvs/ip_vs_core.c | 20 +++++++++-- + net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++---- + net/netfilter/ipvs/ip_vs_proto_tcp.c | 44 +++++------------------ + net/netfilter/ipvs/ip_vs_proto_udp.c | 50 ++++++--------------------- + 5 files changed, 74 insertions(+), 86 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index 08b90d33acdc6..aef042039cb00 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -24,7 +24,9 @@ + #include /* for union nf_inet_addr */ + #include + #include /* for struct ipv6hdr */ ++#include + #include ++#include + #if IS_ENABLED(CONFIG_NF_CONNTRACK) + #include + #endif +@@ -1711,8 +1713,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int dir); + #endif + +-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset); +- + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) + { + __be32 diff[2] = { ~old, new }; +@@ -1738,6 +1738,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum) + return csum_partial(diff, sizeof(diff), oldsum); + } + ++static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af) ++{ ++ /* Checksum unnecessary or already validated? */ ++ if (skb_csum_unnecessary(skb)) ++ return false; ++ /* LOCAL_OUT ? */ ++ if (!skb->dev || skb->dev->flags & IFF_LOOPBACK) ++ return false; ++ /* !LOCAL_IN (FORWARD) ? */ ++ if (af == AF_INET6) { ++ if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL)) ++ return false; ++ } else { ++ if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL)) ++ return false; ++ } ++ return true; ++} ++ ++static inline bool ip_vs_checksum_common_check(struct sk_buff *skb, ++ int offset, int proto, int af) ++{ ++ if (!ip_vs_checksum_needed(skb, af)) ++ return true; ++ return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af); ++} ++ + /* Forget current conntrack (unconfirmed) and attach notrack entry */ + static inline void ip_vs_notrack(struct sk_buff *skb) + { +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index 90619453cb6f0..306a8227d300e 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -689,7 +689,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; } + + #endif + +-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) ++static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) + { + return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0)); + } +@@ -860,13 +860,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + unsigned int offset, unsigned int ihl, + unsigned int hooknum) + { ++ int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; + + /* Ensure the checksum is correct */ +- if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { ++ if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { + /* Failed checksum! */ + IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", + IP_VS_DBG_ADDR(af, snet)); +@@ -1720,7 +1721,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + verdict = NF_DROP; + + /* Ensure the checksum is correct */ +- if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { ++ if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) && ++ !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { + /* Failed checksum! */ + IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", + &iph->saddr); +@@ -1886,6 +1888,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + goto out; + } + ++ verdict = NF_DROP; ++ ++ /* Ensure the checksum is correct */ ++ if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ && ++ !ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6, ++ AF_INET6)) { ++ /* Failed checksum! */ ++ IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n", ++ &iph->saddr); ++ goto out; ++ } ++ + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); + +diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c +index c67317be17dfa..f6f732b7dfa86 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c +@@ -11,7 +11,7 @@ + + static int + sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int sctphoff); ++ struct ip_vs_iphdr *iph); + + static int + sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) ++ if (!sctp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) ++ if (!sctp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int sctphoff) ++ struct ip_vs_iphdr *iph) + { ++ unsigned int sctphoff = iph->len; + struct sctphdr *sh; + __le32 cmp, val; + ++ if (!ip_vs_checksum_needed(skb, af)) ++ return 1; + sh = (struct sctphdr *)(skb->data + sctphoff); + cmp = sh->checksum; + val = sctp_compute_cksum(skb, sctphoff); + + if (val != cmp) { + /* CRC failure, dump it. */ +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); + return 0; + } + return 1; +diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c +index b382810156b2c..bf31127338aa0 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c +@@ -30,7 +30,7 @@ + + static int + tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int tcphoff); ++ struct ip_vs_iphdr *iph); + + static int + tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -167,7 +167,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) ++ if (!tcp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -245,7 +245,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) ++ if (!tcp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -303,41 +303,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int tcphoff) ++ struct ip_vs_iphdr *iph) + { +- switch (skb->ip_summed) { +- case CHECKSUM_NONE: +- skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); +- fallthrough; +- case CHECKSUM_COMPLETE: +-#ifdef CONFIG_IP_VS_IPV6 +- if (af == AF_INET6) { +- if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, +- &ipv6_hdr(skb)->daddr, +- skb->len - tcphoff, +- IPPROTO_TCP, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- } else +-#endif +- if (csum_tcpudp_magic(ip_hdr(skb)->saddr, +- ip_hdr(skb)->daddr, +- skb->len - tcphoff, +- ip_hdr(skb)->protocol, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- break; +- default: +- /* No need to checksum. */ +- break; ++ if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) { ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); ++ return 0; + } +- + return 1; + } + +diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c +index dbd4155bb0752..40d30649b3048 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_udp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c +@@ -26,7 +26,7 @@ + + static int + udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int udphoff); ++ struct ip_vs_iphdr *iph); + + static int + udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -156,7 +156,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!udp_csum_check(cp->af, skb, pp, udphoff)) ++ if (!udp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -239,7 +239,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!udp_csum_check(cp->af, skb, pp, udphoff)) ++ if (!udp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -299,48 +299,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int udphoff) ++ struct ip_vs_iphdr *iph) + { + struct udphdr _udph, *uh; + +- uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph); ++ uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph); + if (uh == NULL) + return 0; + +- if (uh->check != 0) { +- switch (skb->ip_summed) { +- case CHECKSUM_NONE: +- skb->csum = skb_checksum(skb, udphoff, +- skb->len - udphoff, 0); +- fallthrough; +- case CHECKSUM_COMPLETE: +-#ifdef CONFIG_IP_VS_IPV6 +- if (af == AF_INET6) { +- if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, +- &ipv6_hdr(skb)->daddr, +- skb->len - udphoff, +- IPPROTO_UDP, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- } else +-#endif +- if (csum_tcpudp_magic(ip_hdr(skb)->saddr, +- ip_hdr(skb)->daddr, +- skb->len - udphoff, +- ip_hdr(skb)->protocol, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- break; +- default: +- /* No need to checksum. */ +- break; +- } ++ if (!uh->check) ++ return 1; ++ if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) { ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); ++ return 0; + } + return 1; + } +-- +2.53.0 + diff --git a/queue-6.12/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-6.12/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch new file mode 100644 index 0000000000..42b096feef --- /dev/null +++ b/queue-6.12/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch @@ -0,0 +1,64 @@ +From 86de0810a334b7f3ddc8ad8ae261367c91ed8242 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:03 -0400 +Subject: keys: fix out-of-bounds read in keyring_get_key_chunk() + +From: Michael Bommarito + +[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ] + +For description-level chunks keyring_get_key_chunk() advances the read +pointer by level * sizeof(long) past the inline prefix but only +bounds-checks the prefix, so a long enough key description is read past +its kmemdup(desc, desc_len + 1) allocation. Compute the full byte +offset and bounds-check the description against it before reading. + +The walk only reaches a description-level chunk when two keys collide +through the hash, x, type and domain_tag chunks, so this is reached from +an unprivileged add_key(2) with a crafted pair of same-type keys whose +index hashes collide; KASAN reports a slab-out-of-bounds read. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index e105349794f23..b9ca9dc9b0c3f 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + unsigned long chunk = 0; + const u8 *d; + int desc_len = index_key->desc_len, n = sizeof(chunk); ++ unsigned int offset; + + level /= ASSOC_ARRAY_KEY_CHUNK_SIZE; + switch (level) { +@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + return (unsigned long)index_key->domain_tag; + default: + level -= 4; +- if (desc_len <= sizeof(index_key->desc)) ++ offset = sizeof(index_key->desc) + level * sizeof(long); ++ if (desc_len <= offset) + return 0; + +- d = index_key->description + sizeof(index_key->desc); +- d += level * sizeof(long); +- desc_len -= sizeof(index_key->desc); ++ d = index_key->description + offset; ++ desc_len -= offset; + if (desc_len > n) + desc_len = n; + do { +-- +2.53.0 + diff --git a/queue-6.12/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-6.12/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch new file mode 100644 index 0000000000..20e6a17c94 --- /dev/null +++ b/queue-6.12/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch @@ -0,0 +1,63 @@ +From e0e353acd4b00a4225cce078294db8f65d62bbf5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:04 -0400 +Subject: keys: make keyring key-chunk byte order agree with + keyring_diff_objects() + +From: Michael Bommarito + +[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ] + +keyring_get_key_chunk() loads description bytes into the index chunk low +address first, while keyring_diff_objects() numbers the first differing +bit from the low end and folds the absolute byte index into the level +without removing the inline-prefix offset the level already carries. +The two disagree on byte order and bit position, so the array can be +told two keys first differ at a bit that does not differ in the chunk +the walker uses, letting crafted descriptions collide into one node. + +Load the chunk in the order keyring_diff_objects() assumes and drop the +inline-prefix length when folding the byte index into the level. This +only changes the in-memory ordering used to place keys within a keyring; +add, search and read of non-colliding keys are unaffected. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index b9ca9dc9b0c3f..fd95a0eb7a466 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + desc_len -= offset; + if (desc_len > n) + desc_len = n; ++ d += desc_len; + do { + chunk <<= 8; +- chunk |= *d++; ++ chunk |= *--d; + } while (--desc_len > 0); + return chunk; + } +@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data) + return -1; + + differ_plus_i: +- level += i; ++ level += i - (int)sizeof(a->desc); + differ: + i = level * 8 + __ffs(seg_a ^ seg_b); + return i; +-- +2.53.0 + diff --git a/queue-6.12/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch b/queue-6.12/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch new file mode 100644 index 0000000000..7bda3e5d81 --- /dev/null +++ b/queue-6.12/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch @@ -0,0 +1,96 @@ +From eb51779d0f9000cee36481b510c16961e1f78c65 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 19:22:30 +0300 +Subject: KEYS: trusted: dcp: fix key_len validation and calc_blob_len() return + type + +From: Fabrice Derepas + +[ Upstream commit 35d661c98fe4733490f20b4311616a3c2c30abc0 ] + +Two correctness and type-hygiene issues exist in the DCP trusted keys +implementation. + +First, trusted_dcp_unseal() reads p->key_len from a user-supplied blob +without checking if it exceeds MAX_KEY_SIZE. If a crafted blob provides a +payload_len larger than 128, the subsequent do_aead_crypto() call writes +past the end of the p->key array into the adjacent p->blob buffer within +the same struct trusted_key_payload -- the caller's own input, not +unrelated kernel memory. While not exploitable, this violates strict array +bounds and triggers static analyzers. Fix this by adding a validation +check against MIN_KEY_SIZE and MAX_KEY_SIZE immediately after reading the +length, matching the checks already done in trusted_core.c. + +Second, calc_blob_len() calculates a sum in size_t that truncates to +unsigned int on 64-bit platforms. Because the DCP hardware is only present +on 32-bit i.MX SoC platforms, size_t and unsigned int are functionally +equivalent in production, making this truncation harmless in practice. +Nevertheless, updating the return type to size_t (and subsequently updating +'blen' in the seal/unseal paths) resolves type-narrowing warnings and +improves overall code hygiene. + +Fixes: 2e8a0f40a39c ("KEYS: trusted: Introduce NXP DCP-backed trusted keys") +Signed-off-by: Fabrice Derepas +Reviewed-by: David Gstir +Reviewed-by: Richard Weinberger +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719163939.3624767-1-fabrice.derepas@canonical.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/trusted-keys/trusted_dcp.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +diff --git a/security/keys/trusted-keys/trusted_dcp.c b/security/keys/trusted-keys/trusted_dcp.c +index 7b6eb655df0cb..c078adebe190e 100644 +--- a/security/keys/trusted-keys/trusted_dcp.c ++++ b/security/keys/trusted-keys/trusted_dcp.c +@@ -69,7 +69,7 @@ static bool skip_zk_test; + module_param_named(dcp_skip_zk_test, skip_zk_test, bool, 0); + MODULE_PARM_DESC(dcp_skip_zk_test, "Don't test whether device keys are zero'ed"); + +-static unsigned int calc_blob_len(unsigned int payload_len) ++static size_t calc_blob_len(unsigned int payload_len) + { + return sizeof(struct dcp_blob_fmt) + payload_len + DCP_BLOB_AUTHLEN; + } +@@ -200,7 +200,8 @@ static int encrypt_blob_key(u8 *plain_key, u8 *encrypted_key) + static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob) + { + struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob; +- int blen, ret; ++ size_t blen; ++ int ret; + u8 *plain_blob_key; + + blen = calc_blob_len(p->key_len); +@@ -242,7 +243,8 @@ static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob) + static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob) + { + struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob; +- int blen, ret; ++ size_t blen; ++ int ret; + u8 *plain_blob_key = NULL; + + if (b->fmt_version != DCP_BLOB_VERSION) { +@@ -253,9 +255,14 @@ static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob) + } + + p->key_len = le32_to_cpu(b->payload_len); ++ if (p->key_len < MIN_KEY_SIZE || p->key_len > MAX_KEY_SIZE) { ++ ret = -EINVAL; ++ goto out; ++ } ++ + blen = calc_blob_len(p->key_len); + if (blen != p->blob_len) { +- pr_err("DCP blob has bad length: %i != %i\n", blen, ++ pr_err("DCP blob has bad length: %zu != %u\n", blen, + p->blob_len); + ret = -EINVAL; + goto out; +-- +2.53.0 + diff --git a/queue-6.12/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch b/queue-6.12/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch new file mode 100644 index 0000000000..bcbee4703a --- /dev/null +++ b/queue-6.12/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch @@ -0,0 +1,50 @@ +From a3d326cbb0897e38c4a8e87b4e15fd5bdc1d3ce2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 10:04:19 +0900 +Subject: ksmbd: fix use-after-free in __close_file_table_ids() + +From: Namjae Jeon + +[ Upstream commit e7188199eff46a636f3436356f0aae039be6dd66 ] + +A ksmbd_file can remain alive after logical close while another session +holds a temporary reference obtained through ksmbd_lookup_fd_inode(). +ksmbd_close_fd() currently marks the file closed and drops the idr-owned +reference, but leaves the pointer published in the closing session's idr +until the final reference is dropped. + +If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final() +supplies the foreign session's file table to __ksmbd_close_fd(). The object +is then freed without being removed from its owner's idr, and the owner +session later dereferences the stale pointer during file-table teardown. + +Remove the volatile id from the owner's idr while ksmbd_close_fd() still +holds that table's lock, and clear volatile_id before dropping +the idr-owned reference. A later foreign final put then only performs +physical destruction and cannot remove the object from the wrong table. + +Fixes: 8510a043d334 ("ksmbd: increment reference count of parent fp") +Reported-by: Yunseong Kim +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs_cache.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index 0d3341927b483..51e37e89d1aa5 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -502,6 +502,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + fp = NULL; + else { + fp->f_state = FP_CLOSED; ++ idr_remove(ft->idr, id); ++ fp->volatile_id = KSMBD_NO_FID; + closed = true; + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; +-- +2.53.0 + diff --git a/queue-6.12/ksmbd-return-success-for-deferred-final-close.patch b/queue-6.12/ksmbd-return-success-for-deferred-final-close.patch new file mode 100644 index 0000000000..d9ceec7c8d --- /dev/null +++ b/queue-6.12/ksmbd-return-success-for-deferred-final-close.patch @@ -0,0 +1,64 @@ +From 6b210ed24a4bf09d9f448ca02e135991891c8227 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 21 Jun 2026 19:41:08 +0900 +Subject: ksmbd: return success for deferred final close + +From: Namjae Jeon + +[ Upstream commit c5db4de8988f1a621556ca5c4537f77b766ca07d ] + +ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table +reference. If another in-flight request still holds a reference, the final +close is deferred until that request drops its reference. + +The function currently returns -EINVAL in that deferred-final-close case +because fp is cleared when the reference count does not reach zero. That +turns a valid close into STATUS_FILE_CLOSED. + +smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then +closes the same directory handle before receiving the find response. +The query holds a reference while it builds the response, so close must +mark the handle closed and return success even though final teardown is +delayed. Track whether the handle was successfully transitioned to +FP_CLOSED and return success when only the final close is deferred. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: e7188199eff4 ("ksmbd: fix use-after-free in __close_file_table_ids()") +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs_cache.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index 5b6d8bb8edb27..0d3341927b483 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -487,6 +487,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + { + struct ksmbd_file *fp; + struct ksmbd_file_table *ft; ++ bool closed = false; + + if (!has_file_id(id)) + return 0; +@@ -501,6 +502,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + fp = NULL; + else { + fp->f_state = FP_CLOSED; ++ closed = true; + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; + } +@@ -508,7 +510,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + write_unlock(&ft->lock); + + if (!fp) +- return -EINVAL; ++ return closed ? 0 : -EINVAL; + + __put_fd_final(work, fp); + return 0; +-- +2.53.0 + diff --git a/queue-6.12/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch b/queue-6.12/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch new file mode 100644 index 0000000000..19358b9d69 --- /dev/null +++ b/queue-6.12/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch @@ -0,0 +1,40 @@ +From 2cc5cfa6ad5193f19e0c8c4976b9835a8571eb1d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 06:26:05 +0000 +Subject: net: bridge: mrp: fix Option TLV length in MRP_Test frames + +From: David Corvaglia + +[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ] + +oui is a pointer, so sizeof(oui) is the pointer size. The MRA +Option TLV thus advertises a wrong length (15 vs 10 on x86_64), +causing misparsing of the frame on peers. Fix is to replace +with sizeof(*oui). + +Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA") +Signed-off-by: David Corvaglia +Acked-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_mrp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c +index fd2de35ffb3cf..5fd22bb4f5b60 100644 +--- a/net/bridge/br_mrp.c ++++ b/net/bridge/br_mrp.c +@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp, + struct br_mrp_oui_hdr *oui = NULL; + u8 length; + +- length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) + ++ length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) + + MRP_OPT_PADDING; + br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length); + +-- +2.53.0 + diff --git a/queue-6.12/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch b/queue-6.12/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch new file mode 100644 index 0000000000..d7d3f6594b --- /dev/null +++ b/queue-6.12/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch @@ -0,0 +1,78 @@ +From dfed353957f7e41f1943bc9434eeaf2aa1a26244 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 07:29:01 +0000 +Subject: net: do not send ICMP/NDISC Redirects when peer allocation fails + +From: Eric Dumazet + +[ Upstream commit dbc3791e3b2472e1ccc08947e0f83b443470ff4f ] + +When inet_getpeer_v4() or inet_getpeer_v6() fails to allocate a peer entry +under memory pressure or tree size caps, redirect handlers previously fell +back to sending un-rate-limited ICMP/NDISC Redirect messages. + +In IPv4, ip_rt_send_redirect() called icmp_send() directly when peer == NULL. +In IPv6, ip6_forward() and ndisc_send_redirect() passed a NULL peer into +inet_peer_xrlim_allow(), which returned true when peer == NULL. + +Because ICMP/NDISC Redirects are not part of the default global rate limit +mask (sysctl_icmp_ratemask), sending redirects when peer == NULL creates +an un-rate-limited ICMP packet storm. + +Fix this by failing closed in ip_rt_send_redirect(), ip6_forward(), and +ndisc_send_redirect() when peer is NULL. + +Fixes: 92d868292634 ("inetpeer: Move ICMP rate limiting state into inet_peer entries.") +Signed-off-by: Eric Dumazet +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260724072901.1633601-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/route.c | 2 -- + net/ipv6/ip6_output.c | 2 +- + net/ipv6/ndisc.c | 2 ++ + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/net/ipv4/route.c b/net/ipv4/route.c +index 4dce0de6ab898..2b8c29a29c4ae 100644 +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -890,8 +890,6 @@ void ip_rt_send_redirect(struct sk_buff *skb) + peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif); + if (!peer) { + rcu_read_unlock(); +- icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, +- rt_nexthop(rt, ip_hdr(skb)->daddr)); + return; + } + +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index 50b41be5d38d0..5d17982cb9946 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -627,7 +627,7 @@ int ip6_forward(struct sk_buff *skb) + /* Limit redirects both by destination (here) + and by source (inside ndisc_send_redirect) + */ +- if (inet_peer_xrlim_allow(peer, 1*HZ)) ++ if (peer && inet_peer_xrlim_allow(peer, 1*HZ)) + ndisc_send_redirect(skb, target); + rcu_read_unlock(); + } else { +diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c +index 73e2e457e30be..1a759519a90f9 100644 +--- a/net/ipv6/ndisc.c ++++ b/net/ipv6/ndisc.c +@@ -1739,6 +1739,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) + + rcu_read_lock(); + peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr); ++ if (!peer) ++ goto release; + ret = inet_peer_xrlim_allow(peer, 1*HZ); + rcu_read_unlock(); + +-- +2.53.0 + diff --git a/queue-6.12/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch b/queue-6.12/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch new file mode 100644 index 0000000000..89df8fbba1 --- /dev/null +++ b/queue-6.12/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch @@ -0,0 +1,53 @@ +From a4ded6efb318cc70c212d43718073b5d3cb7ad68 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 05:52:14 +0100 +Subject: net: dsa: mt7530: check bus->read() errors in the MDIO regmap backend + +From: Daniel Golle + +[ Upstream commit b4ce102b2cd88424c5860fbbb20b9eb343a93bf4 ] + +bus->read() returns a negative errno on failure, but +mt7530_regmap_read() assigns it to a u16, truncating e.g. -ETIMEDOUT +into 0xff92, and returns success. The garbage word is then consumed as +register data, and read-modify-write cycles write it back to the +switch. Check both reads and propagate their errors. + +The same defect existed in mt7530_mii_read() since the driver was +introduced and moved into the regmap backend unchanged. + +Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch") +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/3c628e48276c2e5522c8795a6be60d11c7a76a7d.1785213071.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530-mdio.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/dsa/mt7530-mdio.c b/drivers/net/dsa/mt7530-mdio.c +index 51df42ccdbe62..e19b46449ffe6 100644 +--- a/drivers/net/dsa/mt7530-mdio.c ++++ b/drivers/net/dsa/mt7530-mdio.c +@@ -55,8 +55,15 @@ mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) + if (ret < 0) + return ret; + +- lo = bus->read(bus, priv->mdiodev->addr, r); +- hi = bus->read(bus, priv->mdiodev->addr, 0x10); ++ ret = bus->read(bus, priv->mdiodev->addr, r); ++ if (ret < 0) ++ return ret; ++ lo = ret; ++ ++ ret = bus->read(bus, priv->mdiodev->addr, 0x10); ++ if (ret < 0) ++ return ret; ++ hi = ret; + + *val = (hi << 16) | (lo & 0xffff); + +-- +2.53.0 + diff --git a/queue-6.12/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch b/queue-6.12/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch new file mode 100644 index 0000000000..ec0c9029c8 --- /dev/null +++ b/queue-6.12/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch @@ -0,0 +1,191 @@ +From de36809ff405c07fa438f892e09c8073df37d37e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 05:52:29 +0100 +Subject: net: dsa: mt7530: error out on failed reads in MT7531 PHY polling + +From: Daniel Golle + +[ Upstream commit 77a9ebe8818cf6dd1699bd6728cb5d66307801d7 ] + +The MT7531 indirect PHY access functions poll MT7531_PHY_IAC through +a helper which returns 0 when the underlying read fails, so a failed +bus transaction clears MT7531_PHY_ACS_ST and the access carries on, +returning garbage PHY register data to phylib. + +Poll using regmap_read_poll_timeout(), which stops on read errors and +propagates them. These functions hold the MDIO bus lock across the +whole sequence, so the unlocked regmap accesses remain correct. Remove +the now-unused _mt7530_unlocked_read(). + +Fixes: c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch") +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/79e85d68d210cc37342978171aa6432aa2954333.1785213071.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 58 ++++++++++++++-------------------------- + 1 file changed, 20 insertions(+), 38 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 21437ce57b64f..b13e905833c3f 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -219,12 +219,6 @@ mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val) + mt7530_mutex_unlock(priv); + } + +-static u32 +-_mt7530_unlocked_read(struct mt7530_dummy_poll *p) +-{ +- return mt7530_mii_read(p->priv, p->reg); +-} +- + static u32 + _mt7530_read(struct mt7530_dummy_poll *p) + { +@@ -577,16 +571,13 @@ static int + mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + int regnum) + { +- struct mt7530_dummy_poll p; + u32 reg, val; + int ret; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -596,8 +587,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad) | regnum; + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -607,8 +598,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad); + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -625,16 +616,13 @@ static int + mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + int regnum, u16 data) + { +- struct mt7530_dummy_poll p; + u32 val, reg; + int ret; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -644,8 +632,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad) | regnum; + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -655,8 +643,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad) | data; + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -671,16 +659,13 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + static int + mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) + { +- struct mt7530_dummy_poll p; + int ret; + u32 val; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -691,8 +676,8 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) + + mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -709,16 +694,13 @@ static int + mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, + u16 data) + { +- struct mt7530_dummy_poll p; + int ret; + u32 reg; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, +- !(reg & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg, ++ !(reg & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -729,8 +711,8 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, + + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, +- !(reg & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg, ++ !(reg & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +-- +2.53.0 + diff --git a/queue-6.12/net-ethernet-mtk_eth_soc-add-consts-for-irq-index.patch b/queue-6.12/net-ethernet-mtk_eth_soc-add-consts-for-irq-index.patch new file mode 100644 index 0000000000..c03be12a75 --- /dev/null +++ b/queue-6.12/net-ethernet-mtk_eth_soc-add-consts-for-irq-index.patch @@ -0,0 +1,128 @@ +From 010927541f2e61737e68ea7420488d3936dc16c1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 19 Jun 2025 15:21:22 +0200 +Subject: net: ethernet: mtk_eth_soc: add consts for irq index + +From: Frank Wunderlich + +[ Upstream commit 4981901009923c8889a635a928d55b8f7f8b31ec ] + +Use consts instead of fixed integers for accessing IRQ array. + +Signed-off-by: Frank Wunderlich +Reviewed-by: Simon Horman +Reviewed-by: Daniel Golle +Link: https://patch.msgid.link/20250619132125.78368-3-linux@fw-web.de +Signed-off-by: Jakub Kicinski +Stable-dep-of: e095f249e220 ("net: ethernet: mtk_eth_soc: pass eth to mtk_handle_irq_rx in poll_controller") +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 22 ++++++++++----------- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 7 ++++++- + 2 files changed, 17 insertions(+), 12 deletions(-) + +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +index 4305779459efd..c18d4a143aac9 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3215,9 +3215,9 @@ static int mtk_get_irqs(struct platform_device *pdev, struct mtk_eth *eth) + int i; + + /* future SoCs beginning with MT7988 should use named IRQs in dts */ +- eth->irq[1] = platform_get_irq_byname(pdev, "fe1"); +- eth->irq[2] = platform_get_irq_byname(pdev, "fe2"); +- if (eth->irq[1] >= 0 && eth->irq[2] >= 0) ++ eth->irq[MTK_FE_IRQ_TX] = platform_get_irq_byname(pdev, "fe1"); ++ eth->irq[MTK_FE_IRQ_RX] = platform_get_irq_byname(pdev, "fe2"); ++ if (eth->irq[MTK_FE_IRQ_TX] >= 0 && eth->irq[MTK_FE_IRQ_RX] >= 0) + return 0; + + /* legacy way: +@@ -3226,9 +3226,9 @@ static int mtk_get_irqs(struct platform_device *pdev, struct mtk_eth *eth) + * On SoCs with non-shared IRQs the first entry is not used, + * the second is for TX, and the third is for RX. + */ +- for (i = 0; i < 3; i++) { ++ for (i = 0; i < MTK_FE_IRQ_NUM; i++) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT) && i > 0) +- eth->irq[i] = eth->irq[0]; ++ eth->irq[i] = eth->irq[MTK_FE_IRQ_SHARED]; + else + eth->irq[i] = platform_get_irq(pdev, i); + +@@ -3294,7 +3294,7 @@ static void mtk_poll_controller(struct net_device *dev) + + mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); + mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask); +- mtk_handle_irq_rx(eth->irq[2], dev); ++ mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], dev); + mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); + mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask); + } +@@ -4787,7 +4787,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) + eth->netdev[id]->features |= eth->soc->hw_features; + eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops; + +- eth->netdev[id]->irq = eth->irq[0]; ++ eth->netdev[id]->irq = eth->irq[MTK_FE_IRQ_SHARED]; + eth->netdev[id]->dev.of_node = np; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) +@@ -5064,17 +5064,17 @@ static int mtk_probe(struct platform_device *pdev) + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT)) { +- err = devm_request_irq(eth->dev, eth->irq[0], ++ err = devm_request_irq(eth->dev, eth->irq[MTK_FE_IRQ_SHARED], + mtk_handle_irq, 0, + dev_name(eth->dev), eth); + } else { +- err = devm_request_irq(eth->dev, eth->irq[1], ++ err = devm_request_irq(eth->dev, eth->irq[MTK_FE_IRQ_TX], + mtk_handle_irq_tx, 0, + dev_name(eth->dev), eth); + if (err) + goto err_free_dev; + +- err = devm_request_irq(eth->dev, eth->irq[2], ++ err = devm_request_irq(eth->dev, eth->irq[MTK_FE_IRQ_RX], + mtk_handle_irq_rx, 0, + dev_name(eth->dev), eth); + } +@@ -5120,7 +5120,7 @@ static int mtk_probe(struct platform_device *pdev) + } else + netif_info(eth, probe, eth->netdev[i], + "mediatek frame engine at 0x%08lx, irq %d\n", +- eth->netdev[i]->base_addr, eth->irq[0]); ++ eth->netdev[i]->base_addr, eth->irq[MTK_FE_IRQ_SHARED]); + } + + /* we run 2 devices on the same DMA ring so we need a dummy device +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +index 0570623e569d5..76d60b6a23b2b 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -611,6 +611,11 @@ + + #define MTK_MAC_FSM(x) (0x1010C + ((x) * 0x100)) + ++#define MTK_FE_IRQ_SHARED 0 ++#define MTK_FE_IRQ_TX 1 ++#define MTK_FE_IRQ_RX 2 ++#define MTK_FE_IRQ_NUM (MTK_FE_IRQ_RX + 1) ++ + struct mtk_rx_dma { + unsigned int rxd1; + unsigned int rxd2; +@@ -1248,7 +1253,7 @@ struct mtk_eth { + struct net_device *dummy_dev; + struct net_device *netdev[MTK_MAX_DEVS]; + struct mtk_mac *mac[MTK_MAX_DEVS]; +- int irq[3]; ++ int irq[MTK_FE_IRQ_NUM]; + u32 msg_enable; + unsigned long sysclk; + struct regmap *ethsys; +-- +2.53.0 + diff --git a/queue-6.12/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch b/queue-6.12/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch new file mode 100644 index 0000000000..d5990d9a2b --- /dev/null +++ b/queue-6.12/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch @@ -0,0 +1,40 @@ +From 1281cc3f7c0ce7c5d2a641d26aed314d3e8abd6e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 13:57:35 +0800 +Subject: net: ethernet: mtk_eth_soc: pass eth to mtk_handle_irq_rx in + poll_controller + +From: Chenguang Zhao + +[ Upstream commit e095f249e2209674f6366f6db0383a2b96e19239 ] + +mtk_handle_irq_rx expects a struct mtk_eth * (matching the request_irq +cookie), but mtk_poll_controller incorrectly passed the net_device *. +Calling ndo_poll_controller with CONFIG_NET_POLL_CONTROLLER enabled +would then crash. + +Fixes: 8186f6e382d8 ("net-next: mediatek: fix compile error inside mtk_poll_controller()") +Signed-off-by: Chenguang Zhao +Link: https://patch.msgid.link/20260723055735.885112-1-chenguang.zhao@linux.dev +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +index c18d4a143aac9..0d3346baa84e7 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3294,7 +3294,7 @@ static void mtk_poll_controller(struct net_device *dev) + + mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); + mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask); +- mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], dev); ++ mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], eth); + mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); + mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask); + } +-- +2.53.0 + diff --git a/queue-6.12/net-ethernet-mtk_eth_soc-support-named-irqs.patch b/queue-6.12/net-ethernet-mtk_eth_soc-support-named-irqs.patch new file mode 100644 index 0000000000..5311526419 --- /dev/null +++ b/queue-6.12/net-ethernet-mtk_eth_soc-support-named-irqs.patch @@ -0,0 +1,92 @@ +From 94ae289b86b49c92289d214af6f13945c9573071 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 19 Jun 2025 15:21:21 +0200 +Subject: net: ethernet: mtk_eth_soc: support named IRQs + +From: Frank Wunderlich + +[ Upstream commit ee85b483fefbbebe1e6e61c53169f864bbdd8f13 ] + +Add named interrupts and keep index based fallback for existing +devicetrees. + +Currently only rx and tx IRQs are defined to be used with mt7988, but +later extended with RSS/LRO support. + +Signed-off-by: Frank Wunderlich +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250619132125.78368-2-linux@fw-web.de +Signed-off-by: Jakub Kicinski +Stable-dep-of: e095f249e220 ("net: ethernet: mtk_eth_soc: pass eth to mtk_handle_irq_rx in poll_controller") +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 46 ++++++++++++++++----- + 1 file changed, 35 insertions(+), 11 deletions(-) + +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +index ebf5432cb328d..4305779459efd 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3210,6 +3210,37 @@ static void mtk_tx_timeout(struct net_device *dev, unsigned int txqueue) + schedule_work(ð->pending_work); + } + ++static int mtk_get_irqs(struct platform_device *pdev, struct mtk_eth *eth) ++{ ++ int i; ++ ++ /* future SoCs beginning with MT7988 should use named IRQs in dts */ ++ eth->irq[1] = platform_get_irq_byname(pdev, "fe1"); ++ eth->irq[2] = platform_get_irq_byname(pdev, "fe2"); ++ if (eth->irq[1] >= 0 && eth->irq[2] >= 0) ++ return 0; ++ ++ /* legacy way: ++ * On MTK_SHARED_INT SoCs (MT7621 + MT7628) the first IRQ is taken ++ * from devicetree and used for both RX and TX - it is shared. ++ * On SoCs with non-shared IRQs the first entry is not used, ++ * the second is for TX, and the third is for RX. ++ */ ++ for (i = 0; i < 3; i++) { ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT) && i > 0) ++ eth->irq[i] = eth->irq[0]; ++ else ++ eth->irq[i] = platform_get_irq(pdev, i); ++ ++ if (eth->irq[i] < 0) { ++ dev_err(&pdev->dev, "no IRQ%d resource found\n", i); ++ return -ENXIO; ++ } ++ } ++ ++ return 0; ++} ++ + static irqreturn_t mtk_handle_irq_rx(int irq, void *_eth) + { + struct mtk_eth *eth = _eth; +@@ -4986,17 +5017,10 @@ static int mtk_probe(struct platform_device *pdev) + } + } + +- for (i = 0; i < 3; i++) { +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT) && i > 0) +- eth->irq[i] = eth->irq[0]; +- else +- eth->irq[i] = platform_get_irq(pdev, i); +- if (eth->irq[i] < 0) { +- dev_err(&pdev->dev, "no IRQ%d resource found\n", i); +- err = -ENXIO; +- goto err_wed_exit; +- } +- } ++ err = mtk_get_irqs(pdev, eth); ++ if (err) ++ goto err_wed_exit; ++ + for (i = 0; i < ARRAY_SIZE(eth->clks); i++) { + eth->clks[i] = devm_clk_get(eth->dev, + mtk_clks_source_name[i]); +-- +2.53.0 + diff --git a/queue-6.12/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch b/queue-6.12/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch new file mode 100644 index 0000000000..584c6dd577 --- /dev/null +++ b/queue-6.12/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch @@ -0,0 +1,48 @@ +From 8fd1171e50f3aa1c766a4550bc84c8900d40ef16 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 15:46:57 +0800 +Subject: net: libwx: fix FDIR ATR queue mismatch for software VLAN packets + +From: Jiawen Wu + +[ Upstream commit 732ed8f75ce583d115716f668dc80d730f3ad610 ] + +When TX VLAN hardware offload is disabled, VLAN tags are embedded in +the packet payload (software VLAN). Previously, the driver failed to +set the WX_TX_FLAGS_SW_VLAN flag for these packets during transmission. + +This missing flag caused the txgbe FDIR ATR logic to fall through to the +default hash calculation path. This resulted in asymmetric hash values +for Tx and Rx flows, preventing return packets from being steered to the +same queue as the transmit packets. + +Fix this by detecting software VLANs via eth_type_vlan(skb->protocol) +and setting WX_TX_FLAGS_SW_VLAN. This ensures the ATR feature selects +the correct hashing algorithm to maintain Tx/Rx queue symmetry. + +Fixes: b501d261a5b3 ("net: txgbe: add FDIR ATR support") +Signed-off-by: Jiawen Wu +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/0879DA38A8E32701+20260724074657.10773-1-jiawenwu@trustnetic.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/wangxun/libwx/wx_lib.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c +index 4c203f4afd689..3666302e3401b 100644 +--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c ++++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c +@@ -1481,6 +1481,8 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb, + if (skb_vlan_tag_present(skb)) { + tx_flags |= skb_vlan_tag_get(skb) << WX_TX_FLAGS_VLAN_SHIFT; + tx_flags |= WX_TX_FLAGS_HW_VLAN; ++ } else if (eth_type_vlan(skb->protocol)) { ++ tx_flags |= WX_TX_FLAGS_SW_VLAN; + } + + /* record initial flags and protocol */ +-- +2.53.0 + diff --git a/queue-6.12/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-6.12/net-phylink-put-link_gpio-if-phylink_create-fails.patch new file mode 100644 index 0000000000..a2fc862e0c --- /dev/null +++ b/queue-6.12/net-phylink-put-link_gpio-if-phylink_create-fails.patch @@ -0,0 +1,92 @@ +From a98569dec6e8514076ea3bfb6a6d262091d3ef4a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 17:08:05 +0200 +Subject: net: phylink: put link_gpio if phylink_create fails + +From: Christian Marangi + +[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ] + +In phylink_create() if phylink_register_sfp() returns an error, link_gpio +obtained by phylink_parse_fixedlink() is never released. While this is a +very unlikely scenario, it's worth to fix/handle this. + +This was present from the very first implementation of phylink but got +relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to +bridge between network devices and sfp cages") where additional function +were added after phylink_parse_fixedlink() making the release of link_gpio +needed if such additional function errored out. + +While at it, restructure the exit condition of phylink_create() with the +goto pattern to reduce code duplication on handling error conditions. + +Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages") +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/phy/phylink.c | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index b78dfcbec936c..e6f3963739ad6 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1728,8 +1728,8 @@ struct phylink *phylink_create(struct phylink_config *config, + } else if (config->type == PHYLINK_DEV) { + pl->dev = config->dev; + } else { +- kfree(pl); +- return ERR_PTR(-EINVAL); ++ ret = -EINVAL; ++ goto free_pl; + } + + pl->using_mac_select_pcs = using_mac_select_pcs; +@@ -1753,28 +1753,29 @@ struct phylink *phylink_create(struct phylink_config *config, + phylink_validate(pl, pl->supported, &pl->link_config); + + ret = phylink_parse_mode(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto free_pl; + + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { + ret = phylink_parse_fixedlink(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + } + + pl->cur_link_an_mode = pl->cfg_link_an_mode; + + ret = phylink_register_sfp(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + + return pl; ++ ++release_link_gpio: ++ if (pl->link_gpio) ++ gpiod_put(pl->link_gpio); ++free_pl: ++ kfree(pl); ++ return ERR_PTR(ret); + } + EXPORT_SYMBOL_GPL(phylink_create); + +-- +2.53.0 + diff --git a/queue-6.12/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-6.12/net-sxgbe-check-descriptor-ring-allocation-failures.patch new file mode 100644 index 0000000000..7c5be1abd4 --- /dev/null +++ b/queue-6.12/net-sxgbe-check-descriptor-ring-allocation-failures.patch @@ -0,0 +1,48 @@ +From 1be983093a4dbc670959463006db37ce11d773ee Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:20 +0800 +Subject: net: sxgbe: check descriptor ring allocation failures + +From: Chenguang Zhao + +[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ] + +sxgbe_open() ignores the return value of init_dma_desc_rings() and +continues to program DMA with invalid ring addresses when allocation +fails. Check the return value and disconnect the PHY on failure. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index a4f457fa570d9..4afa2b2c67b47 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -1079,7 +1079,9 @@ static int sxgbe_open(struct net_device *dev) + priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE); + priv->tx_tc = TC_DEFAULT; + priv->rx_tc = TC_DEFAULT; +- init_dma_desc_rings(dev); ++ ret = init_dma_desc_rings(dev); ++ if (ret) ++ goto init_phy_error; + + /* DMA initialization and SW reset */ + ret = sxgbe_init_dma_engine(priv); +@@ -1188,6 +1190,7 @@ static int sxgbe_open(struct net_device *dev) + + init_error: + free_dma_desc_resources(priv); ++init_phy_error: + if (dev->phydev) + phy_disconnect(dev->phydev); + phy_error: +-- +2.53.0 + diff --git a/queue-6.12/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-6.12/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch new file mode 100644 index 0000000000..20c5ebc5a3 --- /dev/null +++ b/queue-6.12/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch @@ -0,0 +1,50 @@ +From 37c5d45f45d538c6e8e7549d527c7c40cc383f2b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:19 +0800 +Subject: net: sxgbe: free TX rings on RX allocation failure + +From: Chenguang Zhao + +[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ] + +When RX descriptor ring allocation fails, init_dma_desc_rings() only +frees the partially allocated RX rings and returns. The TX rings that +were allocated earlier in the same function are leaked. + +Rearrange error labels to clean up TX rings upon RX failures. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index d2ba283f6123f..a4f457fa570d9 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -598,14 +598,13 @@ static int init_dma_desc_rings(struct net_device *netd) + + return 0; + +-txalloc_err: +- while (queue_num--) +- free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); +- return ret; +- + rxalloc_err: + while (queue_num--) + free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize); ++ queue_num = SXGBE_TX_QUEUES; ++txalloc_err: ++ while (queue_num--) ++ free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.12/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch b/queue-6.12/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch new file mode 100644 index 0000000000..dd544d414b --- /dev/null +++ b/queue-6.12/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch @@ -0,0 +1,150 @@ +From 5c0b63ef8637f1a69c80dfc5285ab874c146d1fb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 09:11:37 +0000 +Subject: net: udp_tunnel: fix memory leak in udp_tunnel_nic_unregister() + +From: Eric Dumazet + +[ Upstream commit 080695e6f005e2396f1207fd69d24c442cb230c6 ] + +syzbot reported a memory leak [1] in the UDP tunnel NIC offload code. + +When device registration fails (e.g. in register_netdevice()), netdev core +unwinds by sending a single NETDEV_UNREGISTER notification. If work was queued +during NETDEV_REGISTER (utn->work_pending is set), udp_tunnel_nic_unregister() +returns early: + + if (utn->work_pending) + return; + +Because failed registrations do not enter netdev_wait_allrefs_any(), no +subsequent NETDEV_UNREGISTER rebroadcast will ever occur. As a result, the +struct udp_tunnel_nic allocated in udp_tunnel_nic_alloc() is leaked +permanently. + +Fix this by removing the early return. Instead, synchronously cancel any +pending work with cancel_delayed_work_sync() before freeing @utn. + +To be able to call cancel_delayed_work_sync() while holding RTNL (the work also +needs RTNL), switch udp_tunnel_nic_device_sync_work() to rtnl_trylock(). If RTNL +is contended, requeue the work with a 1 jiffy delay (via queue_delayed_work()) +to prevent high CPU contention while waiting for RTNL lock. + +The utn->work_pending bookkeeping is no longer needed and is removed, as +the workqueue core already tracks the pending/running state of the work. + +[1] +BUG: memory leak +unreferenced object 0xffff888127d5f840 (size 96): + comm "syz-executor", pid 5806, jiffies 4294942188 + backtrace (crc 99fdb6c8): + __kmalloc_noprof+0x3bf/0x550 + udp_tunnel_nic_alloc net/ipv4/udp_tunnel_nic.c:756 [inline] + udp_tunnel_nic_register net/ipv4/udp_tunnel_nic.c:833 [inline] + udp_tunnel_nic_netdevice_event+0x804/0xab0 net/ipv4/udp_tunnel_nic.c:931 + notifier_call_chain+0x59/0x160 kernel/notifier.c:85 + call_netdevice_notifiers_info+0x7d/0xb0 net/core/dev.c:2250 + register_netdevice+0xc10/0xeb0 net/core/dev.c:11478 + +Fixes: cc4e3835eff4 ("udp_tunnel: add central NIC RX port offload infrastructure") +Reported-by: syzbot+eca845fb8c18dd6b44c1@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/netdev/6a632b15.dde6c935.cf6c8.0011.GAE@google.com/T/#u +Signed-off-by: Eric Dumazet +Link: https://patch.msgid.link/20260724091137.1792543-1-edumazet@google.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/ipv4/udp_tunnel_nic.c | 32 +++++++++++++++++--------------- + 1 file changed, 17 insertions(+), 15 deletions(-) + +diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c +index b13e8f7092f46..4360d159ee8a7 100644 +--- a/net/ipv4/udp_tunnel_nic.c ++++ b/net/ipv4/udp_tunnel_nic.c +@@ -32,13 +32,12 @@ struct udp_tunnel_nic_table_entry { + * @lock: protects all fields + * @need_sync: at least one port start changed + * @need_replay: space was freed, we need a replay of all ports +- * @work_pending: @work is currently scheduled + * @n_tables: number of tables under @entries + * @missed: bitmap of tables which overflown + * @entries: table of tables of ports currently offloaded + */ + struct udp_tunnel_nic { +- struct work_struct work; ++ struct delayed_work work; + + struct net_device *dev; + +@@ -46,7 +45,6 @@ struct udp_tunnel_nic { + + u8 need_sync:1; + u8 need_replay:1; +- u8 work_pending:1; + + unsigned int n_tables; + unsigned long missed; +@@ -301,11 +299,10 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) + static void + udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) + { +- if (!utn->need_sync || utn->work_pending) ++ if (!utn->need_sync) + return; + +- queue_work(udp_tunnel_nic_workqueue, &utn->work); +- utn->work_pending = 1; ++ queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 0); + } + + static bool +@@ -731,12 +728,17 @@ udp_tunnel_nic_replay(struct net_device *dev, struct udp_tunnel_nic *utn) + static void udp_tunnel_nic_device_sync_work(struct work_struct *work) + { + struct udp_tunnel_nic *utn = +- container_of(work, struct udp_tunnel_nic, work); ++ container_of(work, struct udp_tunnel_nic, work.work); + +- rtnl_lock(); ++ /* We cannot block on RTNL here, otherwise we would deadlock with ++ * udp_tunnel_nic_unregister() calling cancel_delayed_work_sync() ++ * while holding RTNL. Requeue with 1 jiffy delay if RTNL is contended. ++ */ ++ if (!rtnl_trylock()) { ++ queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 1); ++ return; ++ } + mutex_lock(&utn->lock); +- +- utn->work_pending = 0; + __udp_tunnel_nic_device_sync(utn->dev, utn); + + if (utn->need_replay) +@@ -757,7 +759,7 @@ udp_tunnel_nic_alloc(const struct udp_tunnel_nic_info *info, + if (!utn) + return NULL; + utn->n_tables = n_tables; +- INIT_WORK(&utn->work, udp_tunnel_nic_device_sync_work); ++ INIT_DELAYED_WORK(&utn->work, udp_tunnel_nic_device_sync_work); + mutex_init(&utn->lock); + + for (i = 0; i < n_tables; i++) { +@@ -901,11 +903,11 @@ udp_tunnel_nic_unregister(struct net_device *dev, struct udp_tunnel_nic *utn) + udp_tunnel_nic_flush(dev, utn); + udp_tunnel_nic_unlock(dev); + +- /* Wait for the work to be done using the state, netdev core will +- * retry unregister until we give up our reference on this device. ++ /* Make sure no work is running or queued before freeing @utn. ++ * The work handler uses rtnl_trylock(), so it will not deadlock ++ * against the RTNL we are holding here. + */ +- if (utn->work_pending) +- return; ++ cancel_delayed_work_sync(&utn->work); + + udp_tunnel_nic_free(utn); + release_dev: +-- +2.53.0 + diff --git a/queue-6.12/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-6.12/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch new file mode 100644 index 0000000000..3816e085a2 --- /dev/null +++ b/queue-6.12/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch @@ -0,0 +1,96 @@ +From 55bff37447197c397f6b4157deb88aed732e3cfc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 16:42:01 -0700 +Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in + sip_help_tcp() + +From: Xiang Mei + +[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ] + +sip_help_tcp() stores the size change of each NAT-rewritten SIP message +in s16 diff and accumulates it in s16 tdiff, but a single message can +grow by more than S16_MAX while the packet stays under the 65535 +enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long +Contact list expands the message by tens of kilobytes. diff then wraps, +and "datalen = datalen + diff - msglen" yields a huge unsigned datalen, +so the next iteration's ct_sip_get_header() reads past the linearized skb +tail. + +Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the +65535 byte packet limit, and the seqadj core is already s32 +(nf_ct_seqadj_set() takes s32), so no previously accepted input is +rejected. + + BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25 + ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694) + nf_confirm (net/netfilter/nf_conntrack_proto.c:183) + nf_hook_slow (net/netfilter/core.c:619) + ip6_output (net/ipv6/ip6_output.c:246) + ip6_forward (net/ipv6/ip6_output.c:690) + ipv6_rcv (net/ipv6/ip6_input.c:351) + __netif_receive_skb_one_core (net/core/dev.c:6212) + process_backlog (net/core/dev.c:6676) + __napi_poll (net/core/dev.c:7735) + net_rx_action (net/core/dev.c:7955) + handle_softirqs (kernel/softirq.c:622) + run_ksoftirqd (kernel/softirq.c:1076) + ... + +Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support") +Reported-by: Weiming Shi +Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Xiang Mei +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/nf_conntrack_sip.h | 2 +- + net/netfilter/nf_conntrack_sip.c | 2 +- + net/netfilter/nf_nat_sip.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h +index dbc614dfe0d56..aafa0c04f917e 100644 +--- a/include/linux/netfilter/nf_conntrack_sip.h ++++ b/include/linux/netfilter/nf_conntrack_sip.h +@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks { + unsigned int *datalen); + + void (*seq_adjust)(struct sk_buff *skb, +- unsigned int protoff, s16 off); ++ unsigned int protoff, s32 off); + + unsigned int (*expect)(struct sk_buff *skb, + unsigned int protoff, +diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c +index 852c0b74b8a77..4f975b83c84f6 100644 +--- a/net/netfilter/nf_conntrack_sip.c ++++ b/net/netfilter/nf_conntrack_sip.c +@@ -1629,7 +1629,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, + unsigned int matchoff, matchlen; + unsigned int msglen, origlen; + const char *dptr, *end; +- s16 diff, tdiff = 0; ++ s32 diff, tdiff = 0; + int ret = NF_ACCEPT; + unsigned long clen; + bool term; +diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c +index f7be30c69b5c8..a1c41defaf22d 100644 +--- a/net/netfilter/nf_nat_sip.c ++++ b/net/netfilter/nf_nat_sip.c +@@ -315,7 +315,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, + } + + static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, +- s16 off) ++ s32 off) + { + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); +-- +2.53.0 + diff --git a/queue-6.12/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch b/queue-6.12/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch new file mode 100644 index 0000000000..9e81d497af --- /dev/null +++ b/queue-6.12/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch @@ -0,0 +1,205 @@ +From d4f35e73fd5e45276ef1d5957e83359fd582b7b4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 10:13:37 +0200 +Subject: netfilter: nf_tables: make nft_object rhltable per table + +From: Pablo Neira Ayuso + +[ Upstream commit f4f699790590bd0896c48a71e9232a65198f92f0 ] + +The nft_object rhltable is global, this allows for accessing objects +that are being dismangled from lookup path by other existing netns. +Given the nft_obj_destroy() releases the object inmediately, this might +lead to use-after-free of these objects that are being released. +Make the existing rhltable per table to address this issue to deal with +with the nft_rcv_nl_event() path too. + +Update nft_obj_lookup() to take the table as non-const, otherwise, +compiler complains when passing the objname_ht to rhltable_lookup(). + +Fixes: 4d44175aa5bb ("netfilter: nf_tables: handle nft_object lookups via rhltable") +Suggested-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/netfilter/nf_tables.h | 4 +++- + net/netfilter/nf_tables_api.c | 34 +++++++++++++++---------------- + 2 files changed, 19 insertions(+), 19 deletions(-) + +diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h +index 032b649658be9..f4b59915a49c5 100644 +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -1274,6 +1274,7 @@ static inline void nft_use_inc_restore(u32 *use) + * @sets: sets in the table + * @objects: stateful objects in the table + * @flowtables: flow tables in the table ++ * @objname_ht: hashtable for objects lookup by name + * @hgenerator: handle generator state + * @handle: table handle + * @use: number of chain references to this table +@@ -1293,6 +1294,7 @@ struct nft_table { + struct list_head sets; + struct list_head objects; + struct list_head flowtables; ++ struct rhltable objname_ht; + u64 hgenerator; + u64 handle; + u32 use; +@@ -1380,7 +1382,7 @@ static inline void *nft_obj_data(const struct nft_object *obj) + #define nft_expr_obj(expr) *((struct nft_object **)nft_expr_priv(expr)) + + struct nft_object *nft_obj_lookup(const struct net *net, +- const struct nft_table *table, ++ struct nft_table *table, + const struct nlattr *nla, u32 objtype, + u8 genmask); + +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index 4cf565686a51f..03dd9d1db12fc 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -41,8 +41,6 @@ enum { + NFT_VALIDATE_DO, + }; + +-static struct rhltable nft_objname_ht; +- + static u32 nft_chain_hash(const void *data, u32 len, u32 seed); + static u32 nft_chain_hash_obj(const void *data, u32 len, u32 seed); + static int nft_chain_hash_cmp(struct rhashtable_compare_arg *, const void *); +@@ -1457,6 +1455,10 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info, + if (err) + goto err_chain_ht; + ++ err = rhltable_init(&table->objname_ht, &nft_objname_ht_params); ++ if (err < 0) ++ goto err_obj_ht; ++ + INIT_LIST_HEAD(&table->chains); + INIT_LIST_HEAD(&table->sets); + INIT_LIST_HEAD(&table->objects); +@@ -1475,6 +1477,8 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info, + list_add_tail_rcu(&table->list, &nft_net->tables); + return 0; + err_trans: ++ rhltable_destroy(&table->objname_ht); ++err_obj_ht: + rhltable_destroy(&table->chains_ht); + err_chain_ht: + kfree(table->udata); +@@ -1641,6 +1645,7 @@ static void nf_tables_table_destroy(struct nft_table *table) + return; + + rhltable_destroy(&table->chains_ht); ++ rhltable_destroy(&table->objname_ht); + kfree(table->name); + kfree(table->udata); + kfree(table); +@@ -7578,7 +7583,7 @@ void nft_unregister_obj(struct nft_object_type *obj_type) + EXPORT_SYMBOL_GPL(nft_unregister_obj); + + struct nft_object *nft_obj_lookup(const struct net *net, +- const struct nft_table *table, ++ struct nft_table *table, + const struct nlattr *nla, u32 objtype, + u8 genmask) + { +@@ -7594,7 +7599,7 @@ struct nft_object *nft_obj_lookup(const struct net *net, + !lockdep_commit_lock_is_held(net)); + + rcu_read_lock(); +- list = rhltable_lookup(&nft_objname_ht, &k, nft_objname_ht_params); ++ list = rhltable_lookup(&table->objname_ht, &k, nft_objname_ht_params); + if (!list) + goto out; + +@@ -7873,7 +7878,7 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info, + if (err < 0) + goto err_trans; + +- err = rhltable_insert(&nft_objname_ht, &obj->rhlhead, ++ err = rhltable_insert(&table->objname_ht, &obj->rhlhead, + nft_objname_ht_params); + if (err < 0) + goto err_obj_ht; +@@ -8052,8 +8057,8 @@ nf_tables_getobj_single(u32 portid, const struct nfnl_info *info, + struct netlink_ext_ack *extack = info->extack; + u8 genmask = nft_genmask_cur(info->net); + u8 family = info->nfmsg->nfgen_family; +- const struct nft_table *table; + struct net *net = info->net; ++ struct nft_table *table; + struct nft_object *obj; + struct sk_buff *skb2; + u32 objtype; +@@ -9782,9 +9787,9 @@ static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain) + nf_tables_commit_chain_free_rules_old(g0); + } + +-static void nft_obj_del(struct nft_object *obj) ++static void nft_obj_del(struct nft_table *table, struct nft_object *obj) + { +- rhltable_remove(&nft_objname_ht, &obj->rhlhead, nft_objname_ht_params); ++ rhltable_remove(&table->objname_ht, &obj->rhlhead, nft_objname_ht_params); + list_del_rcu(&obj->list); + } + +@@ -10480,7 +10485,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) + break; + case NFT_MSG_DELOBJ: + case NFT_MSG_DESTROYOBJ: +- nft_obj_del(nft_trans_obj(trans)); ++ nft_obj_del(table, nft_trans_obj(trans)); + nf_tables_obj_notify(&ctx, nft_trans_obj(trans), + trans->msg_type); + break; +@@ -10780,7 +10785,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) + nft_trans_destroy(trans); + } else { + nft_use_dec_restore(&table->use); +- nft_obj_del(nft_trans_obj(trans)); ++ nft_obj_del(table, nft_trans_obj(trans)); + } + break; + case NFT_MSG_DELOBJ: +@@ -11447,7 +11452,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table) + nft_set_destroy(&ctx, set); + } + list_for_each_entry_safe(obj, ne, &table->objects, list) { +- nft_obj_del(obj); ++ nft_obj_del(table, obj); + nft_use_dec(&table->use); + nft_obj_destroy(&ctx, obj); + } +@@ -11629,10 +11634,6 @@ static int __init nf_tables_module_init(void) + if (err < 0) + goto err_netdev_notifier; + +- err = rhltable_init(&nft_objname_ht, &nft_objname_ht_params); +- if (err < 0) +- goto err_rht_objname; +- + err = nft_offload_init(); + if (err < 0) + goto err_offload; +@@ -11655,8 +11656,6 @@ static int __init nf_tables_module_init(void) + err_netlink_notifier: + nft_offload_exit(); + err_offload: +- rhltable_destroy(&nft_objname_ht); +-err_rht_objname: + unregister_netdevice_notifier(&nf_tables_flowtable_notifier); + err_netdev_notifier: + nf_tables_core_module_exit(); +@@ -11678,7 +11677,6 @@ static void __exit nf_tables_module_exit(void) + unregister_pernet_subsys(&nf_tables_net_ops); + cancel_work_sync(&trans_gc_work); + rcu_barrier(); +- rhltable_destroy(&nft_objname_ht); + nf_tables_core_module_exit(); + } + +-- +2.53.0 + diff --git a/queue-6.12/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-6.12/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch new file mode 100644 index 0000000000..0d7553ccba --- /dev/null +++ b/queue-6.12/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch @@ -0,0 +1,69 @@ +From 69e893dcc766e8eed72794bee079601f3a9f913c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:15:23 +0000 +Subject: netfilter: nft_payload: fix mask build for partial field offload + +From: Xiang Mei (Microsoft) + +[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ] + +nft_payload_offload_mask() builds the offload match mask for a payload +expression that covers only part of a header field. For a partial IPv6 +address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which +is undefined on the 32-bit int operand. It also trims only one word, so +the remaining words stay 0xffffffff (and when priv_len is a multiple of 4 +the trim is skipped entirely), leaving the mask covering more bytes than +the rule matches. + + UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20 + shift exponent 120 is too large for 32-bit type 'int' + ... + +The match is byte-granular and struct nft_data is zero-initialised, so the +correct mask is simply the first priv_len bytes set to 0xff. Set those +bytes directly and drop the word/shift trimming; this removes the undefined +shift and no longer over-masks the trailing bytes. + +Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes") +Reported-by: AutonomousCodeSecurity@microsoft.com +Signed-off-by: Xiang Mei (Microsoft) +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/nft_payload.c | 12 +----------- + 1 file changed, 1 insertion(+), 11 deletions(-) + +diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c +index 36c31ad2d64c1..15feb49e0b663 100644 +--- a/net/netfilter/nft_payload.c ++++ b/net/netfilter/nft_payload.c +@@ -253,9 +253,7 @@ static int nft_payload_dump(struct sk_buff *skb, + static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + u32 priv_len, u32 field_len) + { +- unsigned int remainder, delta, k; + struct nft_data mask = {}; +- __be32 remainder_mask; + + if (priv_len == field_len) { + memset(®->mask, 0xff, priv_len); +@@ -264,15 +262,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + return false; + } + +- memset(&mask, 0xff, field_len); +- remainder = priv_len % sizeof(u32); +- if (remainder) { +- k = priv_len / sizeof(u32); +- delta = field_len - priv_len; +- remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1)); +- mask.data[k] = (__force u32)remainder_mask; +- } +- ++ memset(&mask, 0xff, priv_len); + memcpy(®->mask, &mask, field_len); + + return true; +-- +2.53.0 + diff --git a/queue-6.12/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-6.12/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch new file mode 100644 index 0000000000..7a917478f8 --- /dev/null +++ b/queue-6.12/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch @@ -0,0 +1,83 @@ +From 760266bf78d62908739895c61356665a0b35ce8a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 22:02:46 +0200 +Subject: netfilter: xt_hashlimit: validate hashtable supports + XT_HASHLIMIT_RATE_MATCH + +From: Pablo Neira Ayuso + +[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ] + +The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the +dsthash_ent structure which represents an entry in the hashtable. There +is a union area which uses a different layout to express the rate match +mode. + +Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode +flag is requested by two or more different rules that refer to the same +hashtable. Otherwise, uninitialized access to the burst field in the +union is possible. + +Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by +revision less than 3 too. + +Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode") +Reported-and-tested-by: Talha Berk Arslan +Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/ +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/xt_hashlimit.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c +index 0859b8f767645..61813010cd319 100644 +--- a/net/netfilter/xt_hashlimit.c ++++ b/net/netfilter/xt_hashlimit.c +@@ -118,6 +118,7 @@ struct xt_hashlimit_htable { + refcount_t use; + u_int8_t family; + bool rnd_initialized; ++ bool ratematch; + + struct hashlimit_cfg3 cfg; /* config */ + +@@ -325,6 +326,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg, + vfree(hinfo); + return -ENOMEM; + } ++ hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH); + spin_lock_init(&hinfo->lock); + + switch (revision) { +@@ -868,7 +870,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + } + + /* Check for overflow. */ +- if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (revision < 3) ++ return -EINVAL; ++ + if (cfg->avg == 0 || cfg->avg > U32_MAX) { + pr_info_ratelimited("invalid rate\n"); + return -ERANGE; +@@ -901,6 +906,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + mutex_unlock(&hashlimit_mutex); + return ret; + } ++ } else { ++ if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH && ++ !(*hinfo)->ratematch) || ++ (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) && ++ (*hinfo)->ratematch)) { ++ mutex_unlock(&hashlimit_mutex); ++ htable_put(*hinfo); ++ return -EINVAL; ++ } + } + mutex_unlock(&hashlimit_mutex); + +-- +2.53.0 + diff --git a/queue-6.12/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-6.12/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch new file mode 100644 index 0000000000..ea28691406 --- /dev/null +++ b/queue-6.12/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch @@ -0,0 +1,42 @@ +From ef058dddb0af9d5d84b4d35cd72c761b3669e250 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 12:58:31 +0530 +Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop + +From: Suman Ghosh + +[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ] + +During link down event, we were doing netif_tx_stop_all_queues() first +and then netif_carrier_off(). This can cause a potential race since +carrier is still on during down event. This patch reverse the calling +order to fix the issue. + +Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications") +Signed-off-by: Suman Ghosh +Signed-off-by: Ratheesh Kannoth +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +index c314139e0d781..d03399585d01d 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +@@ -838,8 +838,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf) + netif_carrier_on(netdev); + netif_tx_start_all_queues(netdev); + } else { +- netif_tx_stop_all_queues(netdev); + netif_carrier_off(netdev); ++ netif_tx_stop_all_queues(netdev); + } + } + +-- +2.53.0 + diff --git a/queue-6.12/of-reserved_mem-add-code-to-dynamically-allocate-res.patch b/queue-6.12/of-reserved_mem-add-code-to-dynamically-allocate-res.patch new file mode 100644 index 0000000000..c82a46df7b --- /dev/null +++ b/queue-6.12/of-reserved_mem-add-code-to-dynamically-allocate-res.patch @@ -0,0 +1,172 @@ +From 1db32f155c82eedaa9404e509fb07e9ec05eaa96 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 8 Oct 2024 15:06:24 -0700 +Subject: of: reserved_mem: Add code to dynamically allocate reserved_mem array + +From: Oreoluwa Babatunde + +[ Upstream commit 00c9a452a235c61f099504783badd9a7675ff5a5 ] + +The reserved_mem array is statically allocated with a size of +MAX_RESERVED_REGIONS(64). Therefore, if the number of reserved_mem +regions exceeds this size, there will not be enough space to store +all the data. + +Hence, extend the use of the static array by introducing a +dynamically allocated array based on the number of reserved memory +regions specified in the DT. + +On architectures such as arm64, memblock allocated memory is not +writable until after the page tables have been setup. Hence, the +dynamic allocation of the reserved_mem array will need to be done only +after the page tables have been setup. + +As a result, a temporary static array is still needed in the initial +stages to store the information of the dynamically-placed reserved +memory regions because the start address is selected only at run-time +and is not stored anywhere else. +It is not possible to wait until the reserved_mem array is allocated +because this is done after the page tables are setup and the reserved +memory regions need to be initialized before then. + +After the reserved_mem array is allocated, all entries from the static +array is copied over to the new array, and the rest of the information +for the statically-placed reserved memory regions are read in from the +DT and stored in the new array as well. + +Once the init process is completed, the temporary static array is +released back to the system because it is no longer needed. This is +achieved by marking it as __initdata. + +Signed-off-by: Oreoluwa Babatunde +Link: https://lore.kernel.org/r/20241008220624.551309-3-quic_obabatun@quicinc.com +Signed-off-by: Rob Herring (Arm) +Stable-dep-of: db3dbdfea1b8 ("of: reserved_mem: prevent OOB when too many dynamic regions are defined") +Signed-off-by: Sasha Levin +--- + drivers/of/of_reserved_mem.c | 63 +++++++++++++++++++++++++++++++++--- + 1 file changed, 59 insertions(+), 4 deletions(-) + +diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c +index 7b5d6562fe4a0..02d3c3284f886 100644 +--- a/drivers/of/of_reserved_mem.c ++++ b/drivers/of/of_reserved_mem.c +@@ -28,7 +28,9 @@ + + #include "of_private.h" + +-static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; ++static struct reserved_mem reserved_mem_array[MAX_RESERVED_REGIONS] __initdata; ++static struct reserved_mem *reserved_mem __refdata = reserved_mem_array; ++static int total_reserved_mem_cnt = MAX_RESERVED_REGIONS; + static int reserved_mem_count; + + static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, +@@ -57,6 +59,50 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, + return err; + } + ++/* ++ * alloc_reserved_mem_array() - allocate memory for the reserved_mem ++ * array using memblock ++ * ++ * This function is used to allocate memory for the reserved_mem ++ * array according to the total number of reserved memory regions ++ * defined in the DT. ++ * After the new array is allocated, the information stored in ++ * the initial static array is copied over to this new array and ++ * the new array is used from this point on. ++ */ ++static void __init alloc_reserved_mem_array(void) ++{ ++ struct reserved_mem *new_array; ++ size_t alloc_size, copy_size, memset_size; ++ ++ alloc_size = array_size(total_reserved_mem_cnt, sizeof(*new_array)); ++ if (alloc_size == SIZE_MAX) { ++ pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW); ++ return; ++ } ++ ++ new_array = memblock_alloc(alloc_size, SMP_CACHE_BYTES); ++ if (!new_array) { ++ pr_err("Failed to allocate memory for reserved_mem array with err: %d", -ENOMEM); ++ return; ++ } ++ ++ copy_size = array_size(reserved_mem_count, sizeof(*new_array)); ++ if (copy_size == SIZE_MAX) { ++ memblock_free(new_array, alloc_size); ++ total_reserved_mem_cnt = MAX_RESERVED_REGIONS; ++ pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW); ++ return; ++ } ++ ++ memset_size = alloc_size - copy_size; ++ ++ memcpy(new_array, reserved_mem, copy_size); ++ memset(new_array + reserved_mem_count, 0, memset_size); ++ ++ reserved_mem = new_array; ++} ++ + static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem); + /* + * fdt_reserved_mem_save_node() - save fdt node for second pass initialization +@@ -66,7 +112,7 @@ static void __init fdt_reserved_mem_save_node(unsigned long node, const char *un + { + struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; + +- if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) { ++ if (reserved_mem_count == total_reserved_mem_cnt) { + pr_err("not enough space for all defined regions.\n"); + return; + } +@@ -199,6 +245,9 @@ void __init fdt_scan_reserved_mem_reg_nodes(void) + return; + } + ++ /* Attempt dynamic allocation of a new reserved_mem array */ ++ alloc_reserved_mem_array(); ++ + if (__reserved_mem_check_root(node)) { + pr_err("Reserved memory: unsupported node format, ignoring\n"); + return; +@@ -243,7 +292,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam + int __init fdt_scan_reserved_mem(void) + { + int node, child; +- int dynamic_nodes_cnt = 0; ++ int dynamic_nodes_cnt = 0, count = 0; + int dynamic_nodes[MAX_RESERVED_REGIONS]; + const void *fdt = initial_boot_params; + +@@ -266,6 +315,8 @@ int __init fdt_scan_reserved_mem(void) + uname = fdt_get_name(fdt, child, NULL); + + err = __reserved_mem_reserve_reg(child, uname); ++ if (!err) ++ count++; + /* + * Save the nodes for the dynamically-placed regions + * into an array which will be used for allocation right +@@ -280,11 +331,15 @@ int __init fdt_scan_reserved_mem(void) + } + for (int i = 0; i < dynamic_nodes_cnt; i++) { + const char *uname; ++ int err; + + child = dynamic_nodes[i]; + uname = fdt_get_name(fdt, child, NULL); +- __reserved_mem_alloc_size(child, uname); ++ err = __reserved_mem_alloc_size(child, uname); ++ if (!err) ++ count++; + } ++ total_reserved_mem_cnt = count; + return 0; + } + +-- +2.53.0 + diff --git a/queue-6.12/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch b/queue-6.12/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch new file mode 100644 index 0000000000..d3d6989e9b --- /dev/null +++ b/queue-6.12/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch @@ -0,0 +1,66 @@ +From 976f97a7ca25112f6377d28751d9d73d23f2d3e8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 14 Jun 2026 22:38:06 +0900 +Subject: of: reserved_mem: prevent OOB when too many dynamic regions are + defined + +From: Sang-Heon Jeon + +[ Upstream commit db3dbdfea1b8f38774419c5c2c14e4b81c48708d ] + +On boot, fdt_scan_reserved_mem() saves each dynamically-placed +/reserved-memory subnode into a local array of size +MAX_RESERVED_REGIONS. + +If the device tree defines more than MAX_RESERVED_REGIONS +dynamically-placed regions, fdt_scan_reserved_mem() writes past the +end of the local array. + +Add a bounds check that logs an error and skips the excess regions, +restoring the original behavior. + +Fixes: 8a6e02d0c00e ("of: reserved_mem: Restructure how the reserved memory regions are processed") +Signed-off-by: Sang-Heon Jeon +Link: https://patch.msgid.link/20260614133807.2165124-2-ekffu200098@gmail.com +Signed-off-by: Rob Herring (Arm) +Signed-off-by: Sasha Levin +--- + drivers/of/of_reserved_mem.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c +index 02d3c3284f886..9f4f0306b834f 100644 +--- a/drivers/of/of_reserved_mem.c ++++ b/drivers/of/of_reserved_mem.c +@@ -317,6 +317,7 @@ int __init fdt_scan_reserved_mem(void) + err = __reserved_mem_reserve_reg(child, uname); + if (!err) + count++; ++ + /* + * Save the nodes for the dynamically-placed regions + * into an array which will be used for allocation right +@@ -324,10 +325,17 @@ int __init fdt_scan_reserved_mem(void) + * or marked as no-map. This is done to avoid dynamically + * allocating from one of the statically-placed regions. + */ +- if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) { +- dynamic_nodes[dynamic_nodes_cnt] = child; +- dynamic_nodes_cnt++; ++ if (err != -ENOENT || !of_get_flat_dt_prop(child, "size", NULL)) ++ continue; ++ ++ if (dynamic_nodes_cnt == MAX_RESERVED_REGIONS) { ++ pr_err("too many defined dynamic regions, skip '%s'\n", ++ uname); ++ continue; + } ++ ++ dynamic_nodes[dynamic_nodes_cnt] = child; ++ dynamic_nodes_cnt++; + } + for (int i = 0; i < dynamic_nodes_cnt; i++) { + const char *uname; +-- +2.53.0 + diff --git a/queue-6.12/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch b/queue-6.12/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch new file mode 100644 index 0000000000..afe4fb6b82 --- /dev/null +++ b/queue-6.12/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch @@ -0,0 +1,76 @@ +From 0f9e0e34ef1da4fe32c09ac9f068d0b34b8ce4b3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 21:08:30 +0530 +Subject: phy: zynqmp: fix clock error handling in xpsgtr_phy_init() + +From: Radhey Shyam Pandey + +[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ] + +Propagate clk_prepare_enable() failures to the caller instead of +returning success, and disable the reference clock on initialization +error paths to avoid leaking clock references when phy_exit() is not +called. + +Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save") +Signed-off-by: Radhey Shyam Pandey +Reviewed-by: Michal Simek +Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index 6fcbdff760b32..5039b0e89cba4 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -658,12 +658,13 @@ static int xpsgtr_phy_init(struct phy *phy) + { + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; +- int ret = 0; ++ int ret; + + mutex_lock(>r_dev->gtr_mutex); + + /* Configure and enable the clock when peripheral phy_init call */ +- if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk])) ++ ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]); ++ if (ret) + goto out; + + /* Skip initialization if not required. */ +@@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy) + if (gtr_dev->tx_term_fix) { + ret = xpsgtr_phy_tx_term_fix(gtr_phy); + if (ret < 0) +- goto out; ++ goto out_disable_clk; + + gtr_dev->tx_term_fix = false; + } +@@ -687,7 +688,7 @@ static int xpsgtr_phy_init(struct phy *phy) + */ + ret = xpsgtr_configure_pll(gtr_phy); + if (ret) +- goto out; ++ goto out_disable_clk; + + xpsgtr_lane_set_protocol(gtr_phy); + +@@ -705,6 +706,10 @@ static int xpsgtr_phy_init(struct phy *phy) + break; + } + ++ goto out; ++ ++out_disable_clk: ++ clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]); + out: + mutex_unlock(>r_dev->gtr_mutex); + return ret; +-- +2.53.0 + diff --git a/queue-6.12/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch b/queue-6.12/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch new file mode 100644 index 0000000000..2f62e86616 --- /dev/null +++ b/queue-6.12/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch @@ -0,0 +1,56 @@ +From 612585a8e6c42e5d9f56c9afc5e5a6696dc78811 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 21:08:31 +0530 +Subject: phy: zynqmp: fix runtime PM leak on probe allocation failure + +From: Radhey Shyam Pandey + +[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ] + +Allocate saved_regs before pm_runtime_resume_and_get() so a +devm_kmalloc() failure does not leave an unreleased runtime PM usage +counter. + +Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume") +Signed-off-by: Radhey Shyam Pandey +Reviewed-by: Michal Simek +Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index 5039b0e89cba4..1ec5722ac427c 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -1044,6 +1044,12 @@ static int xpsgtr_probe(struct platform_device *pdev) + return PTR_ERR(provider); + } + ++ gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, ++ sizeof(save_reg_address), ++ GFP_KERNEL); ++ if (!gtr_dev->saved_regs) ++ return -ENOMEM; ++ + pm_runtime_set_active(gtr_dev->dev); + pm_runtime_enable(gtr_dev->dev); + +@@ -1053,12 +1059,6 @@ static int xpsgtr_probe(struct platform_device *pdev) + return ret; + } + +- gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, +- sizeof(save_reg_address), +- GFP_KERNEL); +- if (!gtr_dev->saved_regs) +- return -ENOMEM; +- + return 0; + } + +-- +2.53.0 + diff --git a/queue-6.12/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch b/queue-6.12/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch new file mode 100644 index 0000000000..d6ac9b089f --- /dev/null +++ b/queue-6.12/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch @@ -0,0 +1,172 @@ +From 198948f768a687cbbf9fed22a3327a9a19e48a55 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 28 Apr 2025 08:35:47 +0200 +Subject: phy-zynqmp: Postpone getting clock rate until actually needed + +From: Mike Looijmans + +[ Upstream commit 065d5885f6180c534b7b176847b3e008f4e11850 ] + +At probe time the driver would display the following error and abort: + xilinx-psgtr fd400000.phy: Invalid rate 0 for reference clock 0 + +At probe time, the associated GTR driver (e.g. SATA or PCIe) hasn't +initialized the clock yet, so clk_get_rate() likely returns 0 if the clock +is programmable. So this driver only works if the clock is fixed. + +The PHY driver doesn't need to know the clock frequency at probe yet, so +wait until the associated driver initializes the lane before requesting the +clock rate setting. + +In addition to allowing the driver to be used with programmable clocks, +this also reduces the driver's runtime memory footprint by removing an +array of pointers from struct xpsgtr_phy. + +Signed-off-by: Mike Looijmans +Acked-by: Michal Simek +Link: https://lore.kernel.org/r/20250428063648.22034-1-mike.looijmans@topic.nl +Signed-off-by: Vinod Koul +Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()") +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 70 +++++++++++++++++---------------- + 1 file changed, 37 insertions(+), 33 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index e6579002f1146..6fcbdff760b32 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -222,7 +222,6 @@ struct xpsgtr_phy { + * @siou: siou base address + * @gtr_mutex: mutex for locking + * @phys: PHY lanes +- * @refclk_sscs: spread spectrum settings for the reference clocks + * @clk: reference clocks + * @tx_term_fix: fix for GT issue + * @saved_icm_cfg0: stored value of ICM CFG0 register +@@ -235,7 +234,6 @@ struct xpsgtr_dev { + void __iomem *siou; + struct mutex gtr_mutex; /* mutex for locking */ + struct xpsgtr_phy phys[NUM_LANES]; +- const struct xpsgtr_ssc *refclk_sscs[NUM_LANES]; + struct clk *clk[NUM_LANES]; + bool tx_term_fix; + unsigned int saved_icm_cfg0; +@@ -398,13 +396,40 @@ static int xpsgtr_wait_pll_lock(struct phy *phy) + return ret; + } + ++/* Get the spread spectrum (SSC) settings for the reference clock rate */ ++static const struct xpsgtr_ssc *xpsgtr_find_sscs(struct xpsgtr_phy *gtr_phy) ++{ ++ unsigned long rate; ++ struct clk *clk; ++ unsigned int i; ++ ++ clk = gtr_phy->dev->clk[gtr_phy->refclk]; ++ rate = clk_get_rate(clk); ++ ++ for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { ++ /* Allow an error of 100 ppm */ ++ unsigned long error = ssc_lookup[i].refclk_rate / 10000; ++ ++ if (abs(rate - ssc_lookup[i].refclk_rate) < error) ++ return &ssc_lookup[i]; ++ } ++ ++ dev_err(gtr_phy->dev->dev, "Invalid rate %lu for reference clock %u\n", ++ rate, gtr_phy->refclk); ++ ++ return NULL; ++} ++ + /* Configure PLL and spread-sprectrum clock. */ +-static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) ++static int xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) + { + const struct xpsgtr_ssc *ssc; + u32 step_size; + +- ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk]; ++ ssc = xpsgtr_find_sscs(gtr_phy); ++ if (!ssc) ++ return -EINVAL; ++ + step_size = ssc->step_size; + + xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane), +@@ -446,6 +471,8 @@ static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB, + STEP_SIZE_3_MASK, (step_size & STEP_SIZE_3_MASK) | + FORCE_STEP_SIZE | FORCE_STEPS); ++ ++ return 0; + } + + /* Configure the lane protocol. */ +@@ -658,7 +685,10 @@ static int xpsgtr_phy_init(struct phy *phy) + * Configure the PLL, the lane protocol, and perform protocol-specific + * initialization. + */ +- xpsgtr_configure_pll(gtr_phy); ++ ret = xpsgtr_configure_pll(gtr_phy); ++ if (ret) ++ goto out; ++ + xpsgtr_lane_set_protocol(gtr_phy); + + switch (gtr_phy->protocol) { +@@ -823,8 +853,7 @@ static struct phy *xpsgtr_xlate(struct device *dev, + } + + refclk = args->args[3]; +- if (refclk >= ARRAY_SIZE(gtr_dev->refclk_sscs) || +- !gtr_dev->refclk_sscs[refclk]) { ++ if (refclk >= ARRAY_SIZE(gtr_dev->clk)) { + dev_err(dev, "Invalid reference clock number %u\n", refclk); + return ERR_PTR(-EINVAL); + } +@@ -928,9 +957,7 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) + { + unsigned int refclk; + +- for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) { +- unsigned long rate; +- unsigned int i; ++ for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->clk); ++refclk) { + struct clk *clk; + char name[8]; + +@@ -946,29 +973,6 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) + continue; + + gtr_dev->clk[refclk] = clk; +- +- /* +- * Get the spread spectrum (SSC) settings for the reference +- * clock rate. +- */ +- rate = clk_get_rate(clk); +- +- for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { +- /* Allow an error of 100 ppm */ +- unsigned long error = ssc_lookup[i].refclk_rate / 10000; +- +- if (abs(rate - ssc_lookup[i].refclk_rate) < error) { +- gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i]; +- break; +- } +- } +- +- if (i == ARRAY_SIZE(ssc_lookup)) { +- dev_err(gtr_dev->dev, +- "Invalid rate %lu for reference clock %u\n", +- rate, refclk); +- return -EINVAL; +- } + } + + return 0; +-- +2.53.0 + diff --git a/queue-6.12/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch b/queue-6.12/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch new file mode 100644 index 0000000000..34b3d232b0 --- /dev/null +++ b/queue-6.12/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch @@ -0,0 +1,58 @@ +From adf1864247d1f95271535b2668b5878a1b4e71f1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 11:28:44 -0500 +Subject: pinctrl-amd: Don't clear S4 wake bits at probe + +From: Mario Limonciello + +[ Upstream commit ffe8a0c6b55285ceaf2f42fc20c3a0594d14f1e9 ] + +commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") +introduced a regression where Wake-on-LAN no longer works after suspend +or shutdown on some AMD platforms. + +Firmware-programmed S4 wake bits for devices like PCIe NICs using PCI +PME are cleared at probe, but nothing restores them. Unlike S0i3/S3 wake +sources that use enable_irq_wake() -> amd_gpio_irq_set_wake(), PCIe PME +does not use GPIO IRQ infrastructure and relies on firmware configuration. + +The original intent of commit 6bc3462a0f5e ("pinctrl: amd: Mask wake +bits on probe again") was to clear spurious wake bits left by firmware +to prevent unwanted wakeups. However, S4 wake bits are used for +hardware-level wake sources like WoL that bypass the kernel's IRQ wake +API. + +Fix by preserving S4 wake bits at probe and only clearing S0i3/S3 bits: +- Firmware-configured S4 wake sources (WoL) continue working +- Kernel maintains control of S3/S0i3 wake policy via set_wake() +- S3-only wake sources work correctly per commit f31f33dbb3ba ("pinctrl: + amd: Take suspend type into consideration which pins are non-wake") + +The trade-off is that firmware-programmed spurious S4 wake bits remain +set, but this is less problematic than breaking WoL. + +Fixes: 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") +Signed-off-by: Mario Limonciello +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/pinctrl-amd.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c +index debf36ce57857..65006eceaf399 100644 +--- a/drivers/pinctrl/pinctrl-amd.c ++++ b/drivers/pinctrl/pinctrl-amd.c +@@ -869,8 +869,7 @@ static void amd_gpio_irq_init(struct amd_gpio *gpio_dev) + u32 pin_reg, mask; + int i; + +- mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) | +- BIT(WAKE_CNTRL_OFF_S4); ++ mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3); + + for (i = 0; i < desc->npins; i++) { + int pin = desc->pins[i].number; +-- +2.53.0 + diff --git a/queue-6.12/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch b/queue-6.12/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch new file mode 100644 index 0000000000..d44d3ad77b --- /dev/null +++ b/queue-6.12/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch @@ -0,0 +1,58 @@ +From 693f0f227a1aac6f3bae30da8e3ea7871e9faa82 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 15:08:05 +0200 +Subject: pinctrl: qcom: sc8280xp: Add missing wakeup entries for GPIO143/151 + +From: Konrad Dybcio + +[ Upstream commit 437a8d2aa1aa442c4a176fdf4700a9b3bb0c8794 ] + +Pins 143 and 151 were not included in the PDC wakeup map. They are +normally used for PCIe2A and PCIe3a PERST# respectively, so they're +unlikely to be excercised in practice, but still add them for the sake +of completeness. + +Fixes: c0e4c71a9e7c ("pinctrl: qcom: Introduce sc8280xp TLMM driver") +Signed-off-by: Konrad Dybcio +Link: https://patch.msgid.link/20260626-topic-8280_pinctrl_wakeup-v1-1-2ccb267148f5@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-sc8280xp.c | 21 +++++++++++---------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +index 4b1c49697698d..67945ce867fa1 100644 +--- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c ++++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +@@ -1884,16 +1884,17 @@ static const struct msm_gpio_wakeirq_map sc8280xp_pdc_map[] = { + { 126, 200 }, { 127, 225 }, { 128, 262 }, { 129, 201 }, + { 130, 209 }, { 131, 173 }, { 132, 202 }, { 136, 210 }, + { 138, 171 }, { 139, 226 }, { 140, 227 }, { 142, 228 }, +- { 144, 229 }, { 145, 230 }, { 146, 231 }, { 148, 232 }, +- { 149, 233 }, { 150, 234 }, { 152, 235 }, { 154, 212 }, +- { 157, 213 }, { 161, 219 }, { 170, 236 }, { 171, 221 }, +- { 174, 222 }, { 175, 237 }, { 176, 223 }, { 177, 170 }, +- { 180, 238 }, { 181, 239 }, { 182, 240 }, { 183, 241 }, +- { 184, 242 }, { 185, 243 }, { 190, 178 }, { 193, 184 }, +- { 196, 185 }, { 198, 186 }, { 200, 174 }, { 201, 175 }, +- { 205, 176 }, { 206, 177 }, { 208, 187 }, { 210, 198 }, +- { 211, 199 }, { 212, 204 }, { 215, 205 }, { 220, 188 }, +- { 221, 194 }, { 223, 195 }, { 225, 196 }, { 227, 197 }, ++ { 143, 261 }, { 144, 229 }, { 145, 230 }, { 146, 231 }, ++ { 148, 232 }, { 149, 233 }, { 150, 234 }, { 151, 264 }, ++ { 152, 235 }, { 154, 212 }, { 157, 213 }, { 161, 219 }, ++ { 170, 236 }, { 171, 221 }, { 174, 222 }, { 175, 237 }, ++ { 176, 223 }, { 177, 170 }, { 180, 238 }, { 181, 239 }, ++ { 182, 240 }, { 183, 241 }, { 184, 242 }, { 185, 243 }, ++ { 190, 178 }, { 193, 184 }, { 196, 185 }, { 198, 186 }, ++ { 200, 174 }, { 201, 175 }, { 205, 176 }, { 206, 177 }, ++ { 208, 187 }, { 210, 198 }, { 211, 199 }, { 212, 204 }, ++ { 215, 205 }, { 220, 188 }, { 221, 194 }, { 223, 195 }, ++ { 225, 196 }, { 227, 197 }, + }; + + static struct msm_pinctrl_soc_data sc8280xp_pinctrl = { +-- +2.53.0 + diff --git a/queue-6.12/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch b/queue-6.12/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch new file mode 100644 index 0000000000..1225d50dc6 --- /dev/null +++ b/queue-6.12/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch @@ -0,0 +1,70 @@ +From 2d9245bc8e7d31a26100bda1096511c7177c2490 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 16 Jun 2026 17:24:53 +0530 +Subject: pinctrl: qcom: Unconditionally mark gpio as wakeup enable + +From: Sneh Mankad + +[ Upstream commit 859e02a369ab328a77dfcabf59562100e55f9c5c ] + +GPIO interrupts that are wakeup capable need to be forwarded to wakeup +capable parent irqchip. This is done via writing to it's wakeup_enable bit. + +Currently the bit is set only for PDC irqchip by checking skip_wake_irqs. +skip_wake_irqs is set to differentiate between parent irqchips MPM and +PDC. It is set when the parent irqchip is PDC to inform pinctrl about +skipping the IRQ setting up at TLMM. + +However, the functionality to forward GPIO interrupts during SoC low +power mode is needed regardless of which parent irqchip it is. +Without the functionality it is impossible for MPM irqchip to detect the +GPIO interrupt during SoC low power mode since for MPM irqchip the +skip_wake_irqs is always false. + +Remove skip_wake_irqs condition when setting wakeup enable bit to allow +forwarding GPIO interrupts for SoCs using MPM irqchip too. + +Fixes: 76b446f5b86e ("pinctrl: qcom: handle intr_target_reg wakeup_present/enable bits") +Signed-off-by: Sneh Mankad +Reviewed-by: Maulik Shah +Reviewed-by: Linus Walleij +Reviewed-by: Konrad Dybcio +Link: https://patch.msgid.link/20260616-enable_wakeup_capable_gpios-v3-1-fb59647d89cb@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-msm.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c +index 27eb585bf42df..994f46c4c3b6b 100644 +--- a/drivers/pinctrl/qcom/pinctrl-msm.c ++++ b/drivers/pinctrl/qcom/pinctrl-msm.c +@@ -1247,12 +1247,12 @@ static int msm_gpio_irq_reqres(struct irq_data *d) + /* + * If the wakeup_enable bit is present and marked as available for the + * requested GPIO, it should be enabled when the GPIO is marked as +- * wake irq in order to allow the interrupt event to be transfered to +- * the PDC HW. ++ * wake irq in order to allow the interrupt event to be transferred to ++ * the PDC/MPM HW. + * While the name implies only the wakeup event, it's also required for + * the interrupt event. + */ +- if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) { ++ if (g->intr_wakeup_present_bit) { + u32 intr_cfg; + + raw_spin_lock_irqsave(&pctrl->lock, flags); +@@ -1280,7 +1280,7 @@ static void msm_gpio_irq_relres(struct irq_data *d) + unsigned long flags; + + /* Disable the wakeup_enable bit if it has been set in msm_gpio_irq_reqres() */ +- if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) { ++ if (g->intr_wakeup_present_bit) { + u32 intr_cfg; + + raw_spin_lock_irqsave(&pctrl->lock, flags); +-- +2.53.0 + diff --git a/queue-6.12/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-6.12/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch new file mode 100644 index 0000000000..b13d00dda4 --- /dev/null +++ b/queue-6.12/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch @@ -0,0 +1,38 @@ +From 100b184d93359398d2ca9684086d6c40b18f09fa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:55 +0200 +Subject: powerpc/boot: Fix simpleboot CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/simpleboot.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c +index c80691d83880b..27591df41e9e8 100644 +--- a/arch/powerpc/boot/simpleboot.c ++++ b/arch/powerpc/boot/simpleboot.c +@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + /* finally, setup the timebase */ + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-6.12/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-6.12/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch new file mode 100644 index 0000000000..a36620d55e --- /dev/null +++ b/queue-6.12/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch @@ -0,0 +1,38 @@ +From ca7a601e5a6d6de7f80b47f6b53717136ecf605a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:57 +0200 +Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-akebono.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c +index e3cc2599869cc..1b529037480fb 100644 +--- a/arch/powerpc/boot/treeboot-akebono.c ++++ b/arch/powerpc/boot/treeboot-akebono.c +@@ -146,7 +146,7 @@ void platform_init(char *userdata) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-6.12/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-6.12/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch new file mode 100644 index 0000000000..ff7140459a --- /dev/null +++ b/queue-6.12/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch @@ -0,0 +1,38 @@ +From 8de7e4472ca96b99b2253a5d8d73d3b0baf46eb0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:56 +0200 +Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-currituck.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c +index d53e8a592f816..5b5363b74f9f3 100644 +--- a/arch/powerpc/boot/treeboot-currituck.c ++++ b/arch/powerpc/boot/treeboot-currituck.c +@@ -102,7 +102,7 @@ void platform_init(void) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-6.12/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-6.12/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch new file mode 100644 index 0000000000..4e99d935a6 --- /dev/null +++ b/queue-6.12/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch @@ -0,0 +1,169 @@ +From b1a47e1e2ceef35feb092684adc110b04cd023bd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 12:43:11 +0200 +Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path + +From: Denis V. Lunev + +[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ] + +A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports +configured wedges the rtnetlink control plane of the whole machine: + + NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms + [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2! + [qede_recovery_handler:2665(ens6f0)]Starting a recovery process + +The recovery path deadlocks on the driver's own mutex: + + qede_sp_task + rtnl_lock() + mutex_lock(&edev->qede_lock) <- taken + qede_recovery_handler + qede_load + udp_tunnel_nic_reset_ntf + __udp_tunnel_nic_device_sync + info->sync_table == qede_udp_tunnel_sync + mutex_lock(&edev->qede_lock) <- same task: deadlock + +The mutex is not recursive, so the kworker blocks on itself with +rtnl_lock held, and neither lock is ever released. Every task that +calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6 +addrconf, sshd) blocks forever while the node still answers ping. +In a vmcore from an affected production node rtnl_mutex.owner +decodes to the very kworker blocked at the innermost mutex_lock() +above. + +Re-sync the tunnel ports from qede_sp_task() after the internal lock +is dropped, still under rtnl_lock as the udp_tunnel API requires. +This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf() +under rtnl without the internal lock. + +qede_recovery_handler() now returns whether it has successfully +reloaded an open device, and the caller re-syncs the ports only in +that case. This keeps the old gating exactly: a device that was down +or a failed recovery returns false, as those paths never reached the +udp_tunnel_nic_reset_ntf() call before either. + +This was the only user of the qede_lock()/qede_unlock() helpers, so +remove them. + +Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra") +Signed-off-by: Denis V. Lunev +CC: Andrew Lunn +CC: "David S. Miller" +CC: Eric Dumazet +CC: Jakub Kicinski +CC: Paolo Abeni +Reviewed-by: Jacob Keller +Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++---------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c +index b5d744d2586f7..59a80f7193723 100644 +--- a/drivers/net/ethernet/qlogic/qede/qede_main.c ++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c +@@ -107,7 +107,7 @@ static void qede_remove(struct pci_dev *pdev); + static void qede_shutdown(struct pci_dev *pdev); + static void qede_link_update(void *dev, struct qed_link_output *link); + static void qede_schedule_recovery_handler(void *dev); +-static void qede_recovery_handler(struct qede_dev *edev); ++static bool qede_recovery_handler(struct qede_dev *edev); + static void qede_schedule_hw_err_handler(void *dev, + enum qed_hw_err_type err_type); + static void qede_get_eth_tlv_data(void *edev, void *data); +@@ -1065,21 +1065,6 @@ void __qede_unlock(struct qede_dev *edev) + mutex_unlock(&edev->qede_lock); + } + +-/* This version of the lock should be used when acquiring the RTNL lock is also +- * needed in addition to the internal qede lock. +- */ +-static void qede_lock(struct qede_dev *edev) +-{ +- rtnl_lock(); +- __qede_lock(edev); +-} +- +-static void qede_unlock(struct qede_dev *edev) +-{ +- __qede_unlock(edev); +- rtnl_unlock(); +-} +- + static void qede_periodic_task(struct work_struct *work) + { + struct qede_dev *edev = container_of(work, struct qede_dev, +@@ -1116,6 +1101,8 @@ static void qede_sp_task(struct work_struct *work) + */ + + if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) { ++ bool reloaded; ++ + cancel_delayed_work_sync(&edev->periodic_task); + #ifdef CONFIG_QED_SRIOV + /* SRIOV must be disabled outside the lock to avoid a deadlock. +@@ -1124,9 +1111,17 @@ static void qede_sp_task(struct work_struct *work) + if (pci_num_vf(edev->pdev)) + qede_sriov_configure(edev->pdev, 0); + #endif +- qede_lock(edev); +- qede_recovery_handler(edev); +- qede_unlock(edev); ++ rtnl_lock(); ++ __qede_lock(edev); ++ reloaded = qede_recovery_handler(edev); ++ __qede_unlock(edev); ++ ++ /* The udp_tunnel core synchronously calls back into ++ * qede_udp_tunnel_sync(), which takes the qede lock. ++ */ ++ if (reloaded) ++ udp_tunnel_nic_reset_ntf(edev->ndev); ++ rtnl_unlock(); + } + + __qede_lock(edev); +@@ -2667,9 +2662,13 @@ static void qede_recovery_failed(struct qede_dev *edev) + edev->ops->common->set_power_state(edev->cdev, PCI_D3hot); + } + +-static void qede_recovery_handler(struct qede_dev *edev) ++/* Returns true if an open device was successfully reloaded and its ++ * udp_tunnel ports need to be re-synced by the caller. ++ */ ++static bool qede_recovery_handler(struct qede_dev *edev) + { + u32 curr_state = edev->state; ++ bool reloaded = false; + int rc; + + DP_NOTICE(edev, "Starting a recovery process\n"); +@@ -2699,17 +2698,18 @@ static void qede_recovery_handler(struct qede_dev *edev) + goto err; + + qede_config_rx_mode(edev->ndev); +- udp_tunnel_nic_reset_ntf(edev->ndev); ++ reloaded = true; + } + + edev->state = curr_state; + + DP_NOTICE(edev, "Recovery handling is done\n"); + +- return; ++ return reloaded; + + err: + qede_recovery_failed(edev); ++ return false; + } + + static void qede_atomic_hw_err_handler(struct qede_dev *edev) +-- +2.53.0 + diff --git a/queue-6.12/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch b/queue-6.12/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch new file mode 100644 index 0000000000..098cb46685 --- /dev/null +++ b/queue-6.12/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch @@ -0,0 +1,118 @@ +From b07c1b0529437fd76d0dac6262b5d4c454eea574 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 9 Jul 2026 16:27:54 +0000 +Subject: rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled + +From: Ilia Gavrilov + +[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ] + +When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst +is never initialized because inet6_init() exits before addrconf_init() +is called to initialize it. An attempt to bind an RDS socket to +an ipv6 address results in a crash in __ipv6_chk_addr_and_flags() + +KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] +RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0 +Call Trace: + + ipv6_chk_addr+0x3b/0x50 + rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp] + rds_trans_get_preferred+0x15d/0x2d0 [rds] + ? trace_hardirqs_on+0x2d/0x110 + rds_bind+0x1433/0x1d60 [rds] + ? rds_remove_bound+0xd50/0xd50 [rds] + ? aa_af_perm+0x250/0x250 + ? __might_fault+0xde/0x190 + ? __sys_bind+0x1dc/0x210 + __sys_bind+0x1dc/0x210 + ? __ia32_sys_socketpair+0x100/0x100 + ? restore_fpregs_from_fpstate+0x53/0x100 + __x64_sys_bind+0x73/0xb0 + ? syscall_enter_from_user_mode+0x1c/0x50 + do_syscall_64+0x34/0x80 + entry_SYSCALL_64_after_hwframe+0x6e/0xd8 +RIP: 0033:0x7f47f8269ea9 + + +The following code reproduces the issue: + +struct sockaddr_in6 addr; +s = socket(PF_RDS, SOCK_SEQPACKET, 0); + +memset(&addr, 0, sizeof(addr)); +inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr); +addr.sin6_family = AF_INET6; +addr.sin6_port = htons(PORT); + +bind(s, &addr, sizeof(addr)); + +Found by InfoTeCS on behalf of Linux Verification Center +(linuxtesting.org) with Syzkaller. + +Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") +Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support") +Signed-off-by: Ilia Gavrilov +Reviewed-by: Allison Henderson +Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru +Signed-off-by: Jakub Kicinski +Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()") +Signed-off-by: Sasha Levin +--- + net/rds/ib.c | 4 ++++ + net/rds/ib_cm.c | 4 ++++ + net/rds/tcp.c | 8 +++++--- + 3 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/net/rds/ib.c b/net/rds/ib.c +index ce5be43c5fbac..1061bcf7d1315 100644 +--- a/net/rds/ib.c ++++ b/net/rds/ib.c +@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr, + sa = (struct sockaddr *)&sin; + } else { + #if IS_ENABLED(CONFIG_IPV6) ++ if (!ipv6_mod_enabled()) { ++ ret = -EADDRNOTAVAIL; ++ goto out; ++ } + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = *addr; +diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c +index 5289afbb61aa7..e50e01abb0799 100644 +--- a/net/rds/ib_cm.c ++++ b/net/rds/ib_cm.c +@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, + dp = event->param.conn.private_data; + if (isv6) { + #if IS_ENABLED(CONFIG_IPV6) ++ if (!ipv6_mod_enabled()) { ++ err = -EOPNOTSUPP; ++ goto out; ++ } + dp_cmn = &dp->ricp_v6.dp_cmn; + saddr6 = &dp->ricp_v6.dp_saddr; + daddr6 = &dp->ricp_v6.dp_daddr; +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index 06a2d8d48bbac..87e6ab0e93b71 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -342,9 +342,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, + rcu_read_unlock(); + } + #if IS_ENABLED(CONFIG_IPV6) +- ret = ipv6_chk_addr(net, addr, dev, 0); +- if (ret) +- return 0; ++ if (ipv6_mod_enabled()) { ++ ret = ipv6_chk_addr(net, addr, dev, 0); ++ if (ret) ++ return 0; ++ } + #endif + return -EADDRNOTAVAIL; + } +-- +2.53.0 + diff --git a/queue-6.12/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-6.12/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch new file mode 100644 index 0000000000..3746175d43 --- /dev/null +++ b/queue-6.12/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch @@ -0,0 +1,85 @@ +From c8c24b98870d434162cdc9ec3f16d3517ad1755e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 14:02:03 -0700 +Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in + rds_tcp_laddr_check() + +From: Xiang Mei + +[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ] + +rds_tcp_laddr_check() looks up a scoped IPv6 interface with +dev_get_by_index_rcu(), drops the RCU read-side lock, and only then +passes the bare struct net_device * into ipv6_chk_addr(). + +dev_get_by_index_rcu() only keeps the device alive within the same RCU +read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can +free the net_device; ipv6_chk_addr() then dereferences the stale pointer +in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading +freed memory. + +Keep the RCU read-side lock held across the ipv6_chk_addr() call instead +of dropping it right after the lookup, so the device cannot be freed +while it is in use. + + BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + Read of size 8 at addr ffff8880106ec000 by task exploit/153 + Call Trace: + ... + kasan_report (mm/kasan/report.c:595) + __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972) + rds_tcp_laddr_check (net/rds/tcp.c:370) + rds_bind (net/rds/bind.c:248) + __sys_bind (net/socket.c:1920) + __x64_sys_bind (net/socket.c:1956) + do_syscall_64 (arch/x86/entry/syscall_64.c:63) + entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) + +Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") +Reported-by: Weiming Shi +Signed-off-by: Xiang Mei +Reviewed-by: Allison Henderson +Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/rds/tcp.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index 87e6ab0e93b71..1980a197034ba 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -331,23 +331,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, + /* If the scope_id is specified, check only those addresses + * hosted on the specified interface. + */ ++ rcu_read_lock(); + if (scope_id != 0) { +- rcu_read_lock(); + dev = dev_get_by_index_rcu(net, scope_id); + /* scope_id is not valid... */ + if (!dev) { + rcu_read_unlock(); + return -EADDRNOTAVAIL; + } +- rcu_read_unlock(); + } + #if IS_ENABLED(CONFIG_IPV6) + if (ipv6_mod_enabled()) { + ret = ipv6_chk_addr(net, addr, dev, 0); +- if (ret) ++ if (ret) { ++ rcu_read_unlock(); + return 0; ++ } + } + #endif ++ rcu_read_unlock(); + return -EADDRNOTAVAIL; + } + +-- +2.53.0 + diff --git a/queue-6.12/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch b/queue-6.12/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch new file mode 100644 index 0000000000..369ae4c285 --- /dev/null +++ b/queue-6.12/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch @@ -0,0 +1,63 @@ +From cc3ad3e21450c97cc3273d01c564d5ac19966831 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Jul 2026 03:21:32 +0200 +Subject: riscv: mm: Fix out-of-bounds page-table walk during memory hot-remove + +From: Karl Mehltretter + +[ Upstream commit a0188cc133696627857d16054e43f9ebc7efc821 ] + +remove_pud_mapping() and remove_p4d_mapping() obtain a child table base +with pud_offset(p4dp, 0) and p4d_offset(pgd, 0), then add the index for +addr. + +RISC-V folds page-table levels at runtime. When a level is folded, its +offset helper returns the parent entry itself, but the index can still be +nonzero. Adding it walks past the parent table. Sv48 folds P4D, while Sv39 +folds both P4D and PUD, so memory hot-remove can descend into unrelated +memory and pass an invalid page to __free_pages(). This can trigger: + + kernel BUG at include/linux/mm.h:1810! + VM_BUG_ON_PAGE(page_ref_count(page) == 0) + arch_remove_memory+0x1e/0x5c + try_remove_memory+0x15e/0x200 + remove_memory+0x24/0x3c + +Only add the index when the corresponding page-table level is enabled, +matching p4d_offset() and pud_offset(). + +Fixes: c75a74f4ba19 ("riscv: mm: Add memory hotplugging support") +Assisted-by: Claude:claude-fable-5 +Signed-off-by: Karl Mehltretter +Link: https://patch.msgid.link/20260729012132.24882-1-kmehltretter@gmail.com +Signed-off-by: Paul Walmsley +Signed-off-by: Sasha Levin +--- + arch/riscv/mm/init.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c +index 4594f77a06ee3..e52f7dabb370c 100644 +--- a/arch/riscv/mm/init.c ++++ b/arch/riscv/mm/init.c +@@ -1726,7 +1726,7 @@ static void __meminit remove_pud_mapping(pud_t *pud_base, unsigned long addr, un + + for (; addr < end; addr = next) { + next = pud_addr_end(addr, end); +- pudp = pud_base + pud_index(addr); ++ pudp = pgtable_l4_enabled ? pud_base + pud_index(addr) : pud_base; + pud = pudp_get(pudp); + if (!pud_present(pud)) + continue; +@@ -1757,7 +1757,7 @@ static void __meminit remove_p4d_mapping(p4d_t *p4d_base, unsigned long addr, un + + for (; addr < end; addr = next) { + next = p4d_addr_end(addr, end); +- p4dp = p4d_base + p4d_index(addr); ++ p4dp = pgtable_l5_enabled ? p4d_base + p4d_index(addr) : p4d_base; + p4d = p4dp_get(p4dp); + if (!p4d_present(p4d)) + continue; +-- +2.53.0 + diff --git a/queue-6.12/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch b/queue-6.12/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch new file mode 100644 index 0000000000..3730d7e591 --- /dev/null +++ b/queue-6.12/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch @@ -0,0 +1,47 @@ +From 031806e8efdce8dc46b7960fd9cf119c358776a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 10:38:36 +0800 +Subject: rtase: fix double free of multi-frag skb on DMA map failure + +From: Yun Lu + +[ Upstream commit 6fb7b769d6ed6d1d2e02af4a80e57a2477f35086 ] + +In rtase_start_xmit(), when the head buffer DMA mapping fails after +rtase_xmit_frags() has mapped all fragments, the error path clears +the fragment descriptors with rtase_tx_clear_range(), which frees +the skb through the last-frag slot and accounts tx_dropped. Control +then falls through to the common error label, which frees the same +skb a second time and counts it again. + +Return right after clearing the fragments when the skb owns frags; +the no-frag case still drops through and frees the head skb once. + +Fixes: d6e882b89fdf ("rtase: Implement .ndo_start_xmit function") +Signed-off-by: Yun Lu +Reviewed-by: Jacob Keller +Reviewed-by: Justin Lai +Link: https://patch.msgid.link/20260721023836.6691-1-luyun_611@163.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/realtek/rtase/rtase_main.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c +index a565d5fb6b85c..afae19cc99314 100644 +--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c ++++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c +@@ -1609,6 +1609,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb, + err_dma_1: + ring->skbuff[entry] = NULL; + rtase_tx_clear_range(ring, ring->cur_idx + 1, frags); ++ if (frags) ++ /* the frags were cleared above, along with the skb */ ++ return NETDEV_TX_OK; + + err_dma_0: + tp->stats.tx_dropped++; +-- +2.53.0 + diff --git a/queue-6.12/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch b/queue-6.12/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch new file mode 100644 index 0000000000..233f00fc7b --- /dev/null +++ b/queue-6.12/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch @@ -0,0 +1,46 @@ +From b2f48d7f8ae232fd1c0fe52975bf18b0807bb5b5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 May 2026 14:58:33 +0200 +Subject: sched/deadline: Use revised wakeup rule only for running dl_server + +From: Gabriele Monaco + +[ Upstream commit 1842bf97af109f5ebf830175c9725bf81ebb78b1 ] + +Commit 14a857056466 ("sched/deadline: Use revised wakeup rule for +dl_server") applies the revised wakeup rule to any server, as a result +servers that are not running (dl_defer_running == 0) and start with a +deadline overflow get enqueued and can boost tasks as if they were +running, invalidating the defer rule and the documented state model. + +Apply the revised wakeup rule only for deferrable servers that are +marked as running. + +Fixes: 14a857056466 ("sched/deadline: Use revised wakeup rule for dl_server") +Signed-off-by: Gabriele Monaco +Signed-off-by: Peter Zijlstra (Intel) +Acked-by: Juri Lelli +Tested-by: Andrea Righi +Link: https://patch.msgid.link/20260522125833.264145-1-gmonaco@redhat.com +Signed-off-by: Sasha Levin +--- + kernel/sched/deadline.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c +index cb8eff0ebd228..f5c64d1a019d5 100644 +--- a/kernel/sched/deadline.c ++++ b/kernel/sched/deadline.c +@@ -1079,7 +1079,8 @@ static void update_dl_entity(struct sched_dl_entity *dl_se) + if (dl_time_before(dl_se->deadline, rq_clock(rq)) || + dl_entity_overflow(dl_se, rq_clock(rq))) { + +- if (unlikely((!dl_is_implicit(dl_se) || dl_se->dl_defer) && ++ if (unlikely((!dl_is_implicit(dl_se) || ++ (dl_se->dl_defer && dl_se->dl_defer_running)) && + !dl_time_before(dl_se->deadline, rq_clock(rq)) && + !is_dl_boosted(dl_se))) { + update_dl_revised_wakeup(dl_se, rq); +-- +2.53.0 + diff --git a/queue-6.12/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-6.12/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch new file mode 100644 index 0000000000..d2d3c856c0 --- /dev/null +++ b/queue-6.12/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch @@ -0,0 +1,56 @@ +From 004182c7ca3985b5f820a5be6f1c50c3f2e787ec Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 14 Jul 2026 19:49:34 +0900 +Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer + +From: HyeongJun An + +[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ] + +iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the +target-supplied data segment. The segment carries a 2-byte sense length +followed by the sense bytes, so it must hold 2 + senselen bytes, but the +bounds check only requires datalen >= senselen: + + senselen = get_unaligned_be16(data); + if (datalen < senselen) + goto invalid_datalen; + memcpy(sc->sense_buffer, data + 2, + min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); + +A target that returns a SCSI Response whose datalen equals senselen +(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data + +2 read up to two bytes past the received data. Those bytes are stale +conn->data contents and end up in the command's sense buffer, which is +returned to userspace. + +Account for the 2-byte sense length prefix in the check. + +Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi") +Suggested-by: Sashiko AI +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c +index 29af3722ea220..0f169f243475f 100644 +--- a/drivers/scsi/libiscsi.c ++++ b/drivers/scsi/libiscsi.c +@@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, + } + + senselen = get_unaligned_be16(data); +- if (datalen < senselen) ++ if (datalen < senselen + 2) + goto invalid_datalen; + + memcpy(sc->sense_buffer, data + 2, +-- +2.53.0 + diff --git a/queue-6.12/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-6.12/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch new file mode 100644 index 0000000000..d1a0197124 --- /dev/null +++ b/queue-6.12/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch @@ -0,0 +1,71 @@ +From ac067b3373db984e47ccc4e54974bdff16b2d148 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 15:58:48 +0900 +Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the + connection buffer + +From: HyeongJun An + +[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ] + +iscsi_tcp_hdr_dissect() receives the data segment of several PDU types +into the fixed-size conn->data buffer, which is allocated for +ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes. For the LOGIN_RSP, TEXT_RSP, +REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU +whose DataSegmentLength exceeds that buffer. + +The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its +data segment (sense/response data) into conn->data via +iscsi_tcp_data_recv_prep(), but it does so without the same check. The +only upstream bound on in.datalen is conn->max_recv_dlength, the +initiator's advertised MaxRecvDataSegmentLength, which is commonly +negotiated well above 8192 (open-iscsi defaults to 262144). A target +that returns a SCSI Response with a DataSegmentLength between 8193 and +max_recv_dlength therefore overflows the 8192-byte conn->data buffer. + +Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly +like those responses: bound the data segment, receive it into conn->data +when present, and otherwise complete the PDU with no data. Fold the +opcode into that case group rather than duplicating the check. + +Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld") +Suggested-by: Chris Leech +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi_tcp.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c +index c182aa83f2c93..4d23205129432 100644 +--- a/drivers/scsi/libiscsi_tcp.c ++++ b/drivers/scsi/libiscsi_tcp.c +@@ -763,13 +763,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); + spin_unlock(&conn->session->back_lock); + break; +- case ISCSI_OP_SCSI_CMD_RSP: +- if (tcp_conn->in.datalen) { +- iscsi_tcp_data_recv_prep(tcp_conn); +- return 0; +- } +- rc = iscsi_complete_pdu(conn, hdr, NULL, 0); +- break; + case ISCSI_OP_R2T: + if (ahslen) { + rc = ISCSI_ERR_AHSLEN; +@@ -777,6 +770,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + } + rc = iscsi_tcp_r2t_rsp(conn, hdr); + break; ++ case ISCSI_OP_SCSI_CMD_RSP: + case ISCSI_OP_LOGIN_RSP: + case ISCSI_OP_TEXT_RSP: + case ISCSI_OP_REJECT: +-- +2.53.0 + diff --git a/queue-6.12/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch b/queue-6.12/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch new file mode 100644 index 0000000000..621895dd5c --- /dev/null +++ b/queue-6.12/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch @@ -0,0 +1,165 @@ +From 9b990e6b9cd199725b6f82198729aa0754c6b31e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 16:11:45 +0800 +Subject: scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race + +From: Xingui Yang + +[ Upstream commit 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 ] + +Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue +for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock: +the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue, +calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI +devices and waits for the host to become runtime-active. But the host +cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the +drain is blocked on that very handler. + +However skipping the drain reintroduces a race: hisi_sas returns from +resume before all PHY UP work and libsas discovery work finish. The +controller may then autosuspend while disks are still waking up. The +disks issue IO to a suspended controller, the IO fails, and the disks +get disabled. + +Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT +notification to after sas_drain_work(). By then the host resume is about +to complete, so device removal through device_link no longer blocks on +the resume and the cycle is broken. + +With the deadlock gone, restore sas_resume_ha() (the draining variant) +in hisi_sas and remove sas_resume_ha_no_sync(). + +The reorder is safe for the other libsas consumers (isci, pm8001, +aic94xx, mvsas). During suspend, sas_suspend_devices() calls +sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to +NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a +timed-out phy's disk is immediately rejected by the LLDD before reaching +hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET), +and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both +complete directly via scsi_done() without entering SCSI EH. This is +identical in both the old and new ordering since lldd_dev_gone runs +during suspend, before resume. The reorder only affects when the +PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work() +vs. asynchronous after resume returns), not whether I/O can reach the +device. aic94xx and mvsas do not register any PM ops and never reach +this code path. + +Fixes: fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume") +Signed-off-by: Xingui Yang +Reviewed-by: John Garry +Link: https://patch.msgid.link/20260716081145.3950172-1-yangxingui@huawei.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +------ + drivers/scsi/libsas/sas_init.c | 37 +++++++++++++------------- + include/scsi/libsas.h | 1 - + 3 files changed, 19 insertions(+), 29 deletions(-) + +diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +index e958b588d078f..b7dd4efca0c7f 100644 +--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c ++++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +@@ -5219,15 +5219,7 @@ static int _resume_v3_hw(struct device *device) + return rc; + } + phys_init_v3_hw(hisi_hba); +- +- /* +- * If a directly-attached disk is removed during suspend, a deadlock +- * may occur, as the PHYE_RESUME_TIMEOUT processing will require the +- * hisi_hba->device to be active, which can only happen when resume +- * completes. So don't wait for the HA event workqueue to drain upon +- * resume. +- */ +- sas_resume_ha_no_sync(sha); ++ sas_resume_ha(sha); + clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags); + + dev_warn(dev, "end of resuming controller\n"); +diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c +index 8566bb1208a05..ac157ab6a3011 100644 +--- a/drivers/scsi/libsas/sas_init.c ++++ b/drivers/scsi/libsas/sas_init.c +@@ -409,7 +409,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha) + } + } + +-static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) ++void sas_resume_ha(struct sas_ha_struct *ha) + { + const unsigned long tmo = msecs_to_jiffies(25000); + int i; +@@ -425,6 +425,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n", + i, i > 1 ? "s" : ""); + wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo); ++ ++ /* ++ * All phys are back up or timed out. Turn on I/O and drain ++ * pending work. ++ */ ++ scsi_unblock_requests(ha->shost); ++ sas_drain_work(ha); ++ ++ /* ++ * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler ++ * calls sas_deform_port() -> sas_destruct_devices(), which removes ++ * SCSI devices and, for LLDDs using device_link() PM sync, waits ++ * for the host to be runtime-active. Sending it before the drain ++ * would deadlock: the drain waits for the handler, the handler ++ * waits for host resume, and host resume waits for the drain to ++ * finish. ++ */ + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_phy *phy = ha->sas_phy[i]; + +@@ -435,12 +452,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + } + } + +- /* all phys are back up or timed out, turn on i/o so we can +- * flush out disks that did not return +- */ +- scsi_unblock_requests(ha->shost); +- if (drain) +- sas_drain_work(ha); + clear_bit(SAS_HA_RESUMING, &ha->state); + + sas_queue_deferred_work(ha); +@@ -449,20 +460,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + */ + sas_resume_insert_broadcast_ha(ha); + } +- +-void sas_resume_ha(struct sas_ha_struct *ha) +-{ +- _sas_resume_ha(ha, true); +-} + EXPORT_SYMBOL(sas_resume_ha); + +-/* A no-sync variant, which does not call sas_drain_ha(). */ +-void sas_resume_ha_no_sync(struct sas_ha_struct *ha) +-{ +- _sas_resume_ha(ha, false); +-} +-EXPORT_SYMBOL(sas_resume_ha_no_sync); +- + void sas_suspend_ha(struct sas_ha_struct *ha) + { + int i; +diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h +index 1324068dd950f..2e3fcae62e27e 100644 +--- a/include/scsi/libsas.h ++++ b/include/scsi/libsas.h +@@ -676,7 +676,6 @@ extern int sas_register_ha(struct sas_ha_struct *); + extern int sas_unregister_ha(struct sas_ha_struct *); + extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha); + extern void sas_resume_ha(struct sas_ha_struct *sas_ha); +-extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha); + extern void sas_suspend_ha(struct sas_ha_struct *sas_ha); + + int sas_phy_reset(struct sas_phy *phy, int hard_reset); +-- +2.53.0 + diff --git a/queue-6.12/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch b/queue-6.12/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch new file mode 100644 index 0000000000..fff978c114 --- /dev/null +++ b/queue-6.12/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch @@ -0,0 +1,80 @@ +From 33688352daae6c90f41e537458d7a2cabe2f0dab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 09:30:10 +0300 +Subject: scsi: target: Clear cmd_cnt when initial counter enrollment fails + +From: Leon Romanovsky + +[ Upstream commit a8ddfd2425bbbafadae8700d63ed8a61a4109878 ] + +When target_get_sess_cmd() fails during session shutdown because +percpu_ref_tryget_live() returns false, the command keeps the +se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier +without owning a reference. Final release through +target_release_cmd_kref() then issues an unmatched percpu_ref_put(). + +Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during +cmd setup") moved the cmd_cnt assignment ahead of the reference +acquisition. Clear se_cmd->cmd_cnt whenever the initial +target_get_sess_cmd() fails in target_init_cmd() and +target_submit_tmr(), so release performs exactly one matching put per +acquired reference. + +Fixes: 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup") +Signed-off-by: Leon Romanovsky +Reviewed-by: Mike Christie +Link: https://patch.msgid.link/20260722-reference-count-underflow-in-target-v1-1-63ab664f12fd@nvidia.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/target/target_core_transport.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c +index cf9834f958c9c..3dc49f6c7832e 100644 +--- a/drivers/target/target_core_transport.c ++++ b/drivers/target/target_core_transport.c +@@ -1691,6 +1691,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, + u32 data_length, int task_attr, int data_dir, int flags) + { + struct se_portal_group *se_tpg; ++ int ret; + + se_tpg = se_sess->se_tpg; + BUG_ON(!se_tpg); +@@ -1720,7 +1721,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, + * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second + * kref_put() to happen during fabric packet acknowledgement. + */ +- return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); ++ ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); ++ if (ret) ++ se_cmd->cmd_cnt = NULL; ++ ++ return ret; + } + EXPORT_SYMBOL_GPL(target_init_cmd); + +@@ -1996,8 +2001,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, + * allocation failure. + */ + ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp); +- if (ret < 0) ++ if (ret < 0) { ++ se_cmd->cmd_cnt = NULL; + return -ENOMEM; ++ } + + if (tm_type == TMR_ABORT_TASK) + se_cmd->se_tmr_req->ref_task_tag = tag; +@@ -2005,6 +2012,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, + /* See target_submit_cmd for commentary */ + ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); + if (ret) { ++ se_cmd->cmd_cnt = NULL; + core_tmr_release_req(se_cmd->se_tmr_req); + return ret; + } +-- +2.53.0 + diff --git a/queue-6.12/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch b/queue-6.12/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch new file mode 100644 index 0000000000..253161fa8c --- /dev/null +++ b/queue-6.12/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch @@ -0,0 +1,54 @@ +From 3ab9e4cee8b8671f58da2137d6e74525c04cd9a0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 15:58:50 +0800 +Subject: scsi: target: iblock: Fix wrong PR ops NULL check for PREEMPT/RELEASE + +From: TanZheng + +[ Upstream commit 9c33222bd387312874fbe36ca8002e5c945b9653 ] + +In the iblock_execute_pr_out() function, PRO_PREEMPT, +PRO_PREEMPT_AND_ABORT, and PRO_RELEASE all perform callback capability +checks through ops->pr_clear. The error check allows unimplemented hooks +to pass through the gate, resulting dereferencing a NULL function +pointer. + +Check whether the hooks that need to be called are supported. + +Fixes: 394f81184882 ("scsi: target: Add block PR support to iblock") +Signed-off-by: TanZheng +Reviewed-by: Mike Christie +Link: https://patch.msgid.link/20260724075850.280699-1-kensanya@163.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/target/target_core_iblock.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c +index c8dc92a7d63e6..0a232abee8f58 100644 +--- a/drivers/target/target_core_iblock.c ++++ b/drivers/target/target_core_iblock.c +@@ -891,7 +891,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, + break; + case PRO_PREEMPT: + case PRO_PREEMPT_AND_ABORT: +- if (!ops->pr_clear) { ++ if (!ops->pr_preempt) { + pr_err("block_device does not support pr_preempt.\n"); + return TCM_UNSUPPORTED_SCSI_OPCODE; + } +@@ -901,8 +901,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, + sa == PRO_PREEMPT_AND_ABORT); + break; + case PRO_RELEASE: +- if (!ops->pr_clear) { +- pr_err("block_device does not support pr_pclear.\n"); ++ if (!ops->pr_release) { ++ pr_err("block_device does not support pr_release.\n"); + return TCM_UNSUPPORTED_SCSI_OPCODE; + } + +-- +2.53.0 + diff --git a/queue-6.12/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch b/queue-6.12/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch new file mode 100644 index 0000000000..863e67d0cb --- /dev/null +++ b/queue-6.12/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch @@ -0,0 +1,70 @@ +From eef9d12d9caa5c8800203920ead61cbafe19e69f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 01:27:26 +0800 +Subject: scsi: ufs: core: Cancel RTC work in active-active suspend + +From: Guangshuo Li + +[ Upstream commit f71b4a30983b846b4075bf544e835121e70e6a43 ] + +UFS RTC support schedules ufs_rtc_update_work to periodically update the +device RTC. The work can issue query commands and access the UFS host +controller. + +A previous change moved the RTC work cancellation before the PRE_CHANGE +vendor suspend callback to close a race in the common suspend path. +However, the active-active path jumps directly to vops_suspend after +flushing exception handling work and therefore bypasses the +cancellation. + +If the RTC work runs while the vendor suspend callback is gating or +otherwise changing hardware state, it can access the controller during +suspend and trigger an SError. + +Cancel the RTC work before entering the vendor suspend callback in the +active-active path. Since this path now cancels the work, move the RTC +work scheduling outside the device and link state restoration block in +the resume path. This restarts RTC updates after an active-active +suspend and resume cycle. + +Fixes: b0bd84c39289 ("scsi: ufs: core: Fix SError in ufshcd_rtc_work() during UFS suspend") +Signed-off-by: Guangshuo Li +Reviewed-by: Peter Wang +Reviewed-by: Bean Huo +Reviewed-by: Bart Van Assche +Link: https://patch.msgid.link/20260714172726.1736967-1-lgs201920130244@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/ufs/core/ufshcd.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c +index 205427f6c4651..98bf5bc6f383e 100644 +--- a/drivers/ufs/core/ufshcd.c ++++ b/drivers/ufs/core/ufshcd.c +@@ -9761,6 +9761,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) + req_link_state == UIC_LINK_ACTIVE_STATE) { + ufshcd_disable_auto_bkops(hba); + flush_work(&hba->eeh_work); ++ cancel_delayed_work_sync(&hba->ufs_rtc_update_work); + goto vops_suspend; + } + +@@ -9970,10 +9971,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) + if (ret) + goto set_old_link_state; + ufshcd_set_timestamp_attr(hba); +- schedule_delayed_work(&hba->ufs_rtc_update_work, +- msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); + } + ++ schedule_delayed_work(&hba->ufs_rtc_update_work, ++ msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); ++ + if (ufshcd_keep_autobkops_enabled_except_suspend(hba)) + ufshcd_enable_auto_bkops(hba); + else +-- +2.53.0 + diff --git a/queue-6.12/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-6.12/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch new file mode 100644 index 0000000000..ab80c3ccc2 --- /dev/null +++ b/queue-6.12/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch @@ -0,0 +1,66 @@ +From 27db9be62504ea2a3ef85d6be276161d43ba0ed8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 09:27:36 +0200 +Subject: scsi: zfcp: Fix memory leak during adapter release by destroying + gid_pn_req + +From: Benjamin Block + +[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ] + +When releasing an adapter we don't free the mempool 'gid_pn_req' that is +allocated during the enqueue. This leaks memory: + + unreferenced object 0xd8d29297de700 (size 256): + comm "(udev-worker)", pid 2105, jiffies 4294945794 + hex dump (first 32 bytes): + 00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00 ......N......... + ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0 ..........._g... + backtrace (crc 4a5b5da2): + [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0 + [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0 + [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150 + [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp] + [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp] + [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp] + [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80 + [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390 + [<000dc45f643d8238>] online_store+0x298/0x5b0 + [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480 + [<000dc45f62c81150>] new_sync_write+0x370/0x4b0 + [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0 + [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0 + [<000dc45f621c4a16>] do_syscall+0x2f6/0x430 + [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0 + [<000dc45f64dc2224>] system_call+0x74/0xa0 + +Fix this by destroying the mempool during the adapter's release. + +Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp") +Signed-off-by: Benjamin Block +Tested-by: M Nikhil +Acked-by: M Nikhil +Reviewed-by: Chinmaya Kajagar +Reviewed-by: Nihar Panda +Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/s390/scsi/zfcp_aux.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c +index ab2f35bc294da..d3cc884ccd599 100644 +--- a/drivers/s390/scsi/zfcp_aux.c ++++ b/drivers/s390/scsi/zfcp_aux.c +@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) + static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) + { + mempool_destroy(adapter->pool.erp_req); ++ mempool_destroy(adapter->pool.gid_pn_req); + mempool_destroy(adapter->pool.scsi_req); + mempool_destroy(adapter->pool.scsi_abort); + mempool_destroy(adapter->pool.qtcb_pool); +-- +2.53.0 + diff --git a/queue-6.12/series b/queue-6.12/series index 85365a67ae..d33c8c5b54 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -17,3 +17,111 @@ hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch bpf-reset-register-bounds-before-narrowing-retval-ra.patch netconsole-avoid-oob-reads-msg-is-not-nul-terminated.patch thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch +pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch +pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch +dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch +dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch +gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch +ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch +ata-libahci_platform-support-non-consecutive-port-nu.patch +ahci-introduce-ahci_ignore_port-helper.patch +ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch +asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch +asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch +of-reserved_mem-add-code-to-dynamically-allocate-res.patch +of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch +btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch +btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch +phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch +phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch +phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch +netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch +drm-mediatek-check-crtc-state-before-freeing.patch +drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch +keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch +keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch +keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch +assoc_array-trim-the-final-shortcut-word-using-the-c.patch +netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch +netfilter-xt_hashlimit-validate-hashtable-supports-x.patch +ipvs-fix-the-checksum-validations.patch +ipvs-fix-places-with-wrong-packet-offsets.patch +ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch +netfilter-nft_payload-fix-mask-build-for-partial-fie.patch +rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch +rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch +pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch +scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch +scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch +scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch +smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch +spi-spi-cadence-supports-transmission-with-bits_per_.patch +spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch +hwmon-nct6775-core-fix-number-of-temperature-registe.patch +hwmon-ina2xx-add-support-for-has_alerts-configuratio.patch +hwmon-ina2xx-add-support-for-ina260.patch +hwmon-ina226-add-support-for-sy24655.patch +hwmon-ina2xx-make-it-easier-to-add-more-devices.patch +hwmon-ina2xx-add-support-for-ina234.patch +hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch +hwmon-ina2xx-fix-various-overflow-issues.patch +hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch +hwmon-sht3x-fix-unaligned-accesses.patch +hwmon-lm90-only-report-alarms-if-driver-is-ready.patch +hwmon-nzxt-smart2-dma-align-output-buffer.patch +net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch +hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch +net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch +forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch +hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch +hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch +hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch +hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch +hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch +hwmon-adt7470-use-cached-pwm-frequency-value.patch +hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch +hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch +rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch +powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch +powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch +powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch +net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch +wifi-mac80211-validate-individual-twt-params-before-.patch +net-ethernet-mtk_eth_soc-support-named-irqs.patch +net-ethernet-mtk_eth_soc-add-consts-for-irq-index.patch +net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch +hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch +idpf-adjust-txq-ring-count-minimum.patch +idpf-fix-mailbox-irq-name-leak-on-request-failure.patch +bluetooth-iso-clear-iso_data-always-when-detaching-c.patch +bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch +bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch +bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch +bluetooth-iso-fix-leaking-sk-after-socket-release.patch +bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch +bluetooth-btintel-validate-length-before-parsing-dia.patch +bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch +bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch +bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch +bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch +net-phylink-put-link_gpio-if-phylink_create-fails.patch +scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch +scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch +scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch +scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch +net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch +net-sxgbe-check-descriptor-ring-allocation-failures.patch +can-isotp-check-register_netdevice_notifier-error-in.patch +tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch +tracing-remove-trace_event_fl_filtered-logic.patch +tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch +accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch +riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch +net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch +net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch +net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch +octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch +sched-deadline-use-revised-wakeup-rule-only-for-runn.patch +qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch +ksmbd-return-success-for-deferred-final-close.patch +ksmbd-fix-use-after-free-in-__close_file_table_ids.patch diff --git a/queue-6.12/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-6.12/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch new file mode 100644 index 0000000000..71c1abfcdc --- /dev/null +++ b/queue-6.12/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch @@ -0,0 +1,85 @@ +From f2b7fc73c4ed556a8930d0158ea463ad1413eb9c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 28 Jun 2026 14:59:09 +0800 +Subject: smb: client: fix buffer leaks in SMB1 read and write + +From: Dawei Feng + +[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ] + +CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request +buffer before checking whether tcon->ses->server is NULL. If that +defensive check ever fails, the helper returns -ECONNABORTED without +releasing the request buffer. + +Fix these leaks by releasing the allocated request buffer before +returning from these error paths. Use cifs_small_buf_release() for the +buffers allocated by small_smb_init() and cifs_buf_release() for the +buffer allocated by smb_init(). + +The bug was first flagged by an experimental analysis tool we are +developing for kernel memory-management bugs while analyzing +v6.13-rc1. The tool is still under development and is not yet publicly +available. Manual inspection confirms that the bug is still +present in v7.1.1. + +An x86_64 allyesconfig build showed no new warnings. + +Runtime validation used a temporary fault-injection hook to force +tcon->ses->server to NULL after request-buffer initialization. On the +unfixed kernel, the harness observed two leaked small request buffers and +one leaked large request buffer, with directed kmemleak dumps confirming +the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer +deltas remained. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Signed-off-by: Dawei Feng +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/client/cifssmb.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c +index 81a1c116e9031..882d535d17a4c 100644 +--- a/fs/smb/client/cifssmb.c ++++ b/fs/smb/client/cifssmb.c +@@ -1436,8 +1436,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -1549,8 +1551,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -1816,8 +1820,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +-- +2.53.0 + diff --git a/queue-6.12/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch b/queue-6.12/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch new file mode 100644 index 0000000000..e276d67786 --- /dev/null +++ b/queue-6.12/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch @@ -0,0 +1,111 @@ +From 40c1378b7f32f28037e77a77683bf27333ed5ccd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 18:25:10 +0530 +Subject: spi: spi-cadence: Move TX FIFO full busy-wait into FIFO +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Srikanth Boyapally + +[ Upstream commit d9eadfce2fac49445db40808fe4d8259f20a9d2b ] + +SPI host transfers could intermittently stall with spi_transfer timeouts. +The TXFULL condition was checked only once in cdns_transfer_one() before +cdns_spi_process_fifo(), so if the FIFO became full again during refill, +writes could be dropped and the transfer would never complete. + +Move the TXFULL busy-wait into the TX path of cdns_spi_process_fifo() so +the 10µs back-off is applied per FIFO entry during filling, ensuring +forward progress and eliminating spurious timeouts. + +Restrict the delay to host mode using spi_controller_is_target(), the +controller is passed into cdns_spi_process_fifo() so the check is made at +the point of use. In target mode this delay must not run as it causes the +target to miss its transfer window and corrupt data. + +Fixes: 49530e641178 ("spi: cadence: Add usleep_range() for cdns_spi_fill_tx_fifo()") +Signed-off-by: Srikanth Boyapally +Reviewed-by: Radhey Shyam Pandey +Link: https://patch.msgid.link/20260720125510.60166-1-srikanth.boyapally@amd.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-cadence.c | 26 ++++++++++++++++---------- + 1 file changed, 16 insertions(+), 10 deletions(-) + +diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c +index fee908b7f327e..015dce7fc9d81 100644 +--- a/drivers/spi/spi-cadence.c ++++ b/drivers/spi/spi-cadence.c +@@ -388,11 +388,13 @@ static inline void cdns_spi_writer(struct cdns_spi *xspi) + + /** + * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO ++ * @ctlr: Pointer to the spi_controller structure + * @xspi: Pointer to the cdns_spi structure + * @ntx: Number of bytes to pack into the TX FIFO + * @nrx: Number of bytes to drain from the RX FIFO + */ +-static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) ++static void cdns_spi_process_fifo(struct spi_controller *ctlr, ++ struct cdns_spi *xspi, int ntx, int nrx) + { + ntx = clamp(ntx, 0, xspi->tx_bytes); + nrx = clamp(nrx, 0, xspi->rx_bytes); +@@ -407,6 +409,16 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) + } + + if (ntx) { ++ /* When xspi in busy condition, bytes may send failed, ++ * then spi control didn't work thoroughly, add one byte ++ * delay. Only in host mode; in target mode this delay ++ * causes data corruption as the target fails to prepare ++ * data in time. ++ */ ++ if (!spi_controller_is_target(ctlr) && ++ (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)) ++ udelay(10); ++ + cdns_spi_writer(xspi); + ntx--; + } +@@ -460,14 +472,14 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id) + cdns_spi_write(xspi, CDNS_SPI_THLD, 1); + + if (xspi->tx_bytes) { +- cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt); ++ cdns_spi_process_fifo(ctlr, xspi, trans_cnt, trans_cnt); + } else { + /* Fixed delay due to controller limitation with + * RX_NEMPTY incorrect status + * Xilinx AR:65885 contains more details + */ + udelay(10); +- cdns_spi_process_fifo(xspi, 0, trans_cnt); ++ cdns_spi_process_fifo(ctlr, xspi, 0, trans_cnt); + cdns_spi_write(xspi, CDNS_SPI_IDR, + CDNS_SPI_IXR_DEFAULT); + spi_finalize_current_transfer(ctlr); +@@ -520,17 +532,11 @@ static int cdns_transfer_one(struct spi_controller *ctlr, + cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1); + } + +- /* When xspi in busy condition, bytes may send failed, +- * then spi control didn't work thoroughly, add one byte delay +- */ +- if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) +- udelay(10); +- + xspi->n_bytes = cdns_spi_n_bytes(transfer); + xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes); + xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes); + +- cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0); ++ cdns_spi_process_fifo(ctlr, xspi, xspi->tx_fifo_depth, 0); + + cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); + return transfer->len; +-- +2.53.0 + diff --git a/queue-6.12/spi-spi-cadence-supports-transmission-with-bits_per_.patch b/queue-6.12/spi-spi-cadence-supports-transmission-with-bits_per_.patch new file mode 100644 index 0000000000..274a8094a3 --- /dev/null +++ b/queue-6.12/spi-spi-cadence-supports-transmission-with-bits_per_.patch @@ -0,0 +1,200 @@ +From 18e80139530cb1ca620d7e0812c6f6fc9f0b4a89 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 31 Oct 2025 15:30:02 +0800 +Subject: spi: spi-cadence: supports transmission with bits_per_word of 16 and + 32 + +From: Jun Guo + +[ Upstream commit 4e00135b2dd1d7924a58bffa551b6ceb3bd836f2 ] + +The default FIFO data width of the Cadence SPI IP is 8 bits, but +the hardware supports configurations of 16 bits and 32 bits. +This patch enhances the driver to support communication with both +16-bits and 32-bits FIFO data widths. + +Signed-off-by: Jun Guo +Link: https://patch.msgid.link/20251031073003.3289573-3-jun.guo@cixtech.com +Signed-off-by: Mark Brown +Stable-dep-of: d9eadfce2fac ("spi: spi-cadence: Move TX FIFO full busy-wait into FIFO") +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-cadence.c | 106 +++++++++++++++++++++++++++++++++----- + 1 file changed, 93 insertions(+), 13 deletions(-) + +diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c +index 914384348448d..fee908b7f327e 100644 +--- a/drivers/spi/spi-cadence.c ++++ b/drivers/spi/spi-cadence.c +@@ -109,6 +109,7 @@ + * @rxbuf: Pointer to the RX buffer + * @tx_bytes: Number of bytes left to transfer + * @rx_bytes: Number of bytes requested ++ * @n_bytes: Number of bytes per word + * @dev_busy: Device busy flag + * @is_decoded_cs: Flag for decoder property set or not + * @tx_fifo_depth: Depth of the TX FIFO +@@ -120,16 +121,24 @@ struct cdns_spi { + struct clk *pclk; + unsigned int clk_rate; + u32 speed_hz; +- const u8 *txbuf; +- u8 *rxbuf; ++ const void *txbuf; ++ void *rxbuf; + int tx_bytes; + int rx_bytes; ++ u8 n_bytes; + u8 dev_busy; + u32 is_decoded_cs; + unsigned int tx_fifo_depth; + struct reset_control *rstc; + }; + ++enum cdns_spi_frame_n_bytes { ++ CDNS_SPI_N_BYTES_NULL = 0, ++ CDNS_SPI_N_BYTES_U8 = 1, ++ CDNS_SPI_N_BYTES_U16 = 2, ++ CDNS_SPI_N_BYTES_U32 = 4 ++}; ++ + /* Macros for the SPI controller read/write */ + static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset) + { +@@ -305,6 +314,78 @@ static int cdns_spi_setup_transfer(struct spi_device *spi, + return 0; + } + ++static u8 cdns_spi_n_bytes(struct spi_transfer *transfer) ++{ ++ if (transfer->bits_per_word <= 8) ++ return CDNS_SPI_N_BYTES_U8; ++ else if (transfer->bits_per_word <= 16) ++ return CDNS_SPI_N_BYTES_U16; ++ else ++ return CDNS_SPI_N_BYTES_U32; ++} ++ ++static inline void cdns_spi_reader(struct cdns_spi *xspi) ++{ ++ u32 rxw = 0; ++ ++ if (xspi->rxbuf && !IS_ALIGNED((uintptr_t)xspi->rxbuf, xspi->n_bytes)) { ++ pr_err("%s: rxbuf address is not aligned for %d bytes\n", ++ __func__, xspi->n_bytes); ++ return; ++ } ++ ++ rxw = cdns_spi_read(xspi, CDNS_SPI_RXD); ++ if (xspi->rxbuf) { ++ switch (xspi->n_bytes) { ++ case CDNS_SPI_N_BYTES_U8: ++ *(u8 *)xspi->rxbuf = rxw; ++ break; ++ case CDNS_SPI_N_BYTES_U16: ++ *(u16 *)xspi->rxbuf = rxw; ++ break; ++ case CDNS_SPI_N_BYTES_U32: ++ *(u32 *)xspi->rxbuf = rxw; ++ break; ++ default: ++ pr_err("%s invalid n_bytes %d\n", __func__, ++ xspi->n_bytes); ++ return; ++ } ++ xspi->rxbuf = (u8 *)xspi->rxbuf + xspi->n_bytes; ++ } ++} ++ ++static inline void cdns_spi_writer(struct cdns_spi *xspi) ++{ ++ u32 txw = 0; ++ ++ if (xspi->txbuf && !IS_ALIGNED((uintptr_t)xspi->txbuf, xspi->n_bytes)) { ++ pr_err("%s: txbuf address is not aligned for %d bytes\n", ++ __func__, xspi->n_bytes); ++ return; ++ } ++ ++ if (xspi->txbuf) { ++ switch (xspi->n_bytes) { ++ case CDNS_SPI_N_BYTES_U8: ++ txw = *(u8 *)xspi->txbuf; ++ break; ++ case CDNS_SPI_N_BYTES_U16: ++ txw = *(u16 *)xspi->txbuf; ++ break; ++ case CDNS_SPI_N_BYTES_U32: ++ txw = *(u32 *)xspi->txbuf; ++ break; ++ default: ++ pr_err("%s invalid n_bytes %d\n", __func__, ++ xspi->n_bytes); ++ return; ++ } ++ cdns_spi_write(xspi, CDNS_SPI_TXD, txw); ++ xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes; ++ } ++} ++ + /** + * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO + * @xspi: Pointer to the cdns_spi structure +@@ -321,23 +402,14 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) + + while (ntx || nrx) { + if (nrx) { +- u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD); +- +- if (xspi->rxbuf) +- *xspi->rxbuf++ = data; +- ++ cdns_spi_reader(xspi); + nrx--; + } + + if (ntx) { +- if (xspi->txbuf) +- cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++); +- else +- cdns_spi_write(xspi, CDNS_SPI_TXD, 0); +- ++ cdns_spi_writer(xspi); + ntx--; + } +- + } + } + +@@ -454,6 +526,10 @@ static int cdns_transfer_one(struct spi_controller *ctlr, + if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) + udelay(10); + ++ xspi->n_bytes = cdns_spi_n_bytes(transfer); ++ xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes); ++ xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes); ++ + cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0); + + cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); +@@ -654,6 +730,9 @@ static int cdns_spi_probe(struct platform_device *pdev) + ctlr->mode_bits = SPI_CPOL | SPI_CPHA; + ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + ++ if (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6")) ++ ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32); ++ + if (!spi_controller_is_target(ctlr)) { + ctlr->mode_bits |= SPI_CS_HIGH; + ctlr->set_cs = cdns_spi_chipselect; +@@ -809,6 +888,7 @@ static const struct dev_pm_ops cdns_spi_dev_pm_ops = { + + static const struct of_device_id cdns_spi_of_match[] = { + { .compatible = "xlnx,zynq-spi-r1p6" }, ++ { .compatible = "cix,sky1-spi-r1p6" }, + { .compatible = "cdns,spi-r1p6" }, + { /* end of table */ } + }; +-- +2.53.0 + diff --git a/queue-6.12/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch b/queue-6.12/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch new file mode 100644 index 0000000000..501c826bd2 --- /dev/null +++ b/queue-6.12/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch @@ -0,0 +1,70 @@ +From 89b5ec1d7728a1cac731adc0766b3c55c489469a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 21:50:00 +0900 +Subject: tracing/mmiotrace: Add NULL check for mmio_trace_array in logging + functions + +From: Masami Hiramatsu (Google) + +[ Upstream commit 12b80cdbc54cf615b4717a4e8180063408091ea2 ] + +mmio_trace_rw() and mmio_trace_mapping() retrieve mmio_trace_array into +tr and pass it to __trace_mmiotrace_rw() and __trace_mmiotrace_map(). +If these functions are invoked while mmio_trace_array is NULL (e.g. before +initialization or after disabled), accessing tr->array_buffer.buffer will +result in a NULL pointer dereference crash. + +Fix this by adding an explicit NULL check for tr at the beginning of +__trace_mmiotrace_rw() and __trace_mmiotrace_map(). + +Link: https://patch.msgid.link/178524300062.56416.8362487250709962380.stgit@devnote2 +Fixes: f984b51e0779 ("ftrace: add mmiotrace plugin") +Assisted-by: Antigravity:gemini-3.6-flash +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_mmiotrace.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c +index 6b964471265e1..251ed7051e409 100644 +--- a/kernel/trace/trace_mmiotrace.c ++++ b/kernel/trace/trace_mmiotrace.c +@@ -295,11 +295,15 @@ static void __trace_mmiotrace_rw(struct trace_array *tr, + struct trace_array_cpu *data, + struct mmiotrace_rw *rw) + { +- struct trace_buffer *buffer = tr->array_buffer.buffer; ++ struct trace_buffer *buffer; + struct ring_buffer_event *event; + struct trace_mmiotrace_rw *entry; + unsigned int trace_ctx; + ++ if (!tr) ++ return; ++ ++ buffer = tr->array_buffer.buffer; + trace_ctx = tracing_gen_ctx_flags(0); + event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW, + sizeof(*entry), trace_ctx); +@@ -324,11 +328,15 @@ static void __trace_mmiotrace_map(struct trace_array *tr, + struct trace_array_cpu *data, + struct mmiotrace_map *map) + { +- struct trace_buffer *buffer = tr->array_buffer.buffer; ++ struct trace_buffer *buffer; + struct ring_buffer_event *event; + struct trace_mmiotrace_map *entry; + unsigned int trace_ctx; + ++ if (!tr) ++ return; ++ ++ buffer = tr->array_buffer.buffer; + trace_ctx = tracing_gen_ctx_flags(0); + event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP, + sizeof(*entry), trace_ctx); +-- +2.53.0 + diff --git a/queue-6.12/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-6.12/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch new file mode 100644 index 0000000000..1353b438c0 --- /dev/null +++ b/queue-6.12/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch @@ -0,0 +1,43 @@ +From d0156f120da26130fc6529e3d96a49a89b06164a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 21:49:51 +0900 +Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data() + +From: Masami Hiramatsu (Google) + +[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ] + +mmio_reset_data() is called during tracer initialization, reset, and +start. While it resets overrun_detected and prev_overruns, it neglects +to reset dropped_count. Consequently, dropped event counts from prior +tracing sessions persist in dropped_count and corrupt overrun reports +in subsequent runs. + +Fix this by explicitly calling atomic_set(&dropped_count, 0) in +mmio_reset_data(). + +Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2 +Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording") +Assisted-by: Antigravity:gemini-3.6-flash +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_mmiotrace.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c +index 4d9e5c830dbe1..c523ce5aa4958 100644 +--- a/kernel/trace/trace_mmiotrace.c ++++ b/kernel/trace/trace_mmiotrace.c +@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr) + { + overrun_detected = false; + prev_overruns = 0; ++ atomic_set(&dropped_count, 0); + + tracing_reset_online_cpus(&tr->array_buffer); + } +-- +2.53.0 + diff --git a/queue-6.12/tracing-remove-trace_event_fl_filtered-logic.patch b/queue-6.12/tracing-remove-trace_event_fl_filtered-logic.patch new file mode 100644 index 0000000000..7bbae9b77b --- /dev/null +++ b/queue-6.12/tracing-remove-trace_event_fl_filtered-logic.patch @@ -0,0 +1,445 @@ +From 626ccd985bae0c38165439fece6201e049cb2cb5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 11 Sep 2024 09:00:26 +0800 +Subject: tracing: Remove TRACE_EVENT_FL_FILTERED logic + +From: Zheng Yejian + +[ Upstream commit 49e4154f4b16345da5e219b23ed9737a6e735bc1 ] + +After commit dcb0b5575d24 ("tracing: Remove TRACE_EVENT_FL_USE_CALL_FILTER + logic"), no one's going to set the TRACE_EVENT_FL_FILTERED or change the +call->filter, so remove related logic. + +Link: https://lore.kernel.org/20240911010026.2302849-1-zhengyejian@huaweicloud.com +Signed-off-by: Zheng Yejian +Signed-off-by: Steven Rostedt (Google) +Stable-dep-of: 12b80cdbc54c ("tracing/mmiotrace: Add NULL check for mmio_trace_array in logging functions") +Signed-off-by: Sasha Levin +--- + include/linux/trace_events.h | 4 --- + kernel/trace/trace.c | 44 ++++++---------------------- + kernel/trace/trace.h | 4 --- + kernel/trace/trace_branch.c | 4 +-- + kernel/trace/trace_events.c | 2 -- + kernel/trace/trace_functions_graph.c | 8 ++--- + kernel/trace/trace_hwlat.c | 4 +-- + kernel/trace/trace_mmiotrace.c | 8 ++--- + kernel/trace/trace_osnoise.c | 12 ++------ + kernel/trace/trace_sched_wakeup.c | 8 ++--- + 10 files changed, 20 insertions(+), 78 deletions(-) + +diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h +index 54ba231ded519..98ee10b417e67 100644 +--- a/include/linux/trace_events.h ++++ b/include/linux/trace_events.h +@@ -327,7 +327,6 @@ void *trace_event_buffer_reserve(struct trace_event_buffer *fbuffer, + void trace_event_buffer_commit(struct trace_event_buffer *fbuffer); + + enum { +- TRACE_EVENT_FL_FILTERED_BIT, + TRACE_EVENT_FL_CAP_ANY_BIT, + TRACE_EVENT_FL_NO_SET_FILTER_BIT, + TRACE_EVENT_FL_IGNORE_ENABLE_BIT, +@@ -343,7 +342,6 @@ enum { + + /* + * Event flags: +- * FILTERED - The event has a filter attached + * CAP_ANY - Any user can enable for perf + * NO_SET_FILTER - Set when filter has error and is to be ignored + * IGNORE_ENABLE - For trace internal events, do not enable with debugfs file +@@ -359,7 +357,6 @@ enum { + * TEST_STR - The event has a "%s" that points to a string outside the event + */ + enum { +- TRACE_EVENT_FL_FILTERED = (1 << TRACE_EVENT_FL_FILTERED_BIT), + TRACE_EVENT_FL_CAP_ANY = (1 << TRACE_EVENT_FL_CAP_ANY_BIT), + TRACE_EVENT_FL_NO_SET_FILTER = (1 << TRACE_EVENT_FL_NO_SET_FILTER_BIT), + TRACE_EVENT_FL_IGNORE_ENABLE = (1 << TRACE_EVENT_FL_IGNORE_ENABLE_BIT), +@@ -385,7 +382,6 @@ struct trace_event_call { + }; + struct trace_event event; + char *print_fmt; +- struct event_filter *filter; + /* + * Static events can disappear with modules, + * where as dynamic ones need their own ref count. +diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c +index 9e5aa468d7e48..fcbeda360df3e 100644 +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -591,19 +591,6 @@ int tracing_check_open_get_tr(struct trace_array *tr) + return 0; + } + +-int call_filter_check_discard(struct trace_event_call *call, void *rec, +- struct trace_buffer *buffer, +- struct ring_buffer_event *event) +-{ +- if (unlikely(call->flags & TRACE_EVENT_FL_FILTERED) && +- !filter_match_preds(call->filter, rec)) { +- __trace_event_discard_commit(buffer, event); +- return 1; +- } +- +- return 0; +-} +- + /** + * trace_find_filtered_pid - check if a pid exists in a filtered_pid list + * @filtered_pids: The list of pids to check +@@ -2894,7 +2881,6 @@ void + trace_function(struct trace_array *tr, unsigned long ip, unsigned long + parent_ip, unsigned int trace_ctx) + { +- struct trace_event_call *call = &event_function; + struct trace_buffer *buffer = tr->array_buffer.buffer; + struct ring_buffer_event *event; + struct ftrace_entry *entry; +@@ -2907,11 +2893,9 @@ trace_function(struct trace_array *tr, unsigned long ip, unsigned long + entry->ip = ip; + entry->parent_ip = parent_ip; + +- if (!call_filter_check_discard(call, entry, buffer, event)) { +- if (static_branch_unlikely(&trace_function_exports_enabled)) +- ftrace_exports(event, TRACE_EXPORT_FUNCTION); +- __buffer_unlock_commit(buffer, event); +- } ++ if (static_branch_unlikely(&trace_function_exports_enabled)) ++ ftrace_exports(event, TRACE_EXPORT_FUNCTION); ++ __buffer_unlock_commit(buffer, event); + } + + #ifdef CONFIG_STACKTRACE +@@ -2938,7 +2922,6 @@ static void __ftrace_trace_stack(struct trace_array *tr, + unsigned int trace_ctx, + int skip, struct pt_regs *regs) + { +- struct trace_event_call *call = &event_kernel_stack; + struct ring_buffer_event *event; + unsigned int size, nr_entries; + struct ftrace_stack *fstack; +@@ -3011,8 +2994,7 @@ static void __ftrace_trace_stack(struct trace_array *tr, + memcpy(&entry->caller, fstack->calls, + flex_array_size(entry, caller, nr_entries)); + +- if (!call_filter_check_discard(call, entry, buffer, event)) +- __buffer_unlock_commit(buffer, event); ++ __buffer_unlock_commit(buffer, event); + + out: + /* Again, don't let gcc optimize things here */ +@@ -3086,7 +3068,6 @@ static void + ftrace_trace_userstack(struct trace_array *tr, + struct trace_buffer *buffer, unsigned int trace_ctx) + { +- struct trace_event_call *call = &event_user_stack; + struct ring_buffer_event *event; + struct userstack_entry *entry; + +@@ -3120,8 +3101,7 @@ ftrace_trace_userstack(struct trace_array *tr, + memset(&entry->caller, 0, sizeof(entry->caller)); + + stack_trace_save_user(entry->caller, FTRACE_STACK_ENTRIES); +- if (!call_filter_check_discard(call, entry, buffer, event)) +- __buffer_unlock_commit(buffer, event); ++ __buffer_unlock_commit(buffer, event); + + out_drop_count: + __this_cpu_dec(user_stack_count); +@@ -3290,7 +3270,6 @@ static void trace_printk_start_stop_comm(int enabled) + */ + int trace_vbprintk(unsigned long ip, const char *fmt, va_list args) + { +- struct trace_event_call *call = &event_bprint; + struct ring_buffer_event *event; + struct trace_buffer *buffer; + struct trace_array *tr = READ_ONCE(printk_trace); +@@ -3334,10 +3313,8 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args) + entry->fmt = fmt; + + memcpy(entry->buf, tbuffer, sizeof(u32) * len); +- if (!call_filter_check_discard(call, entry, buffer, event)) { +- __buffer_unlock_commit(buffer, event); +- ftrace_trace_stack(tr, buffer, trace_ctx, 6, NULL); +- } ++ __buffer_unlock_commit(buffer, event); ++ ftrace_trace_stack(tr, buffer, trace_ctx, 6, NULL); + + out: + ring_buffer_nest_end(buffer); +@@ -3356,7 +3333,6 @@ static __printf(3, 0) + int __trace_array_vprintk(struct trace_buffer *buffer, + unsigned long ip, const char *fmt, va_list args) + { +- struct trace_event_call *call = &event_print; + struct ring_buffer_event *event; + int len = 0, size; + struct print_entry *entry; +@@ -3391,10 +3367,8 @@ int __trace_array_vprintk(struct trace_buffer *buffer, + entry->ip = ip; + + memcpy(&entry->buf, tbuffer, len + 1); +- if (!call_filter_check_discard(call, entry, buffer, event)) { +- __buffer_unlock_commit(buffer, event); +- ftrace_trace_stack(printk_trace, buffer, trace_ctx, 6, NULL); +- } ++ __buffer_unlock_commit(buffer, event); ++ ftrace_trace_stack(printk_trace, buffer, trace_ctx, 6, NULL); + + out: + ring_buffer_nest_end(buffer); +diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h +index ed4fcc438a0bf..baa39009a25d1 100644 +--- a/kernel/trace/trace.h ++++ b/kernel/trace/trace.h +@@ -1440,10 +1440,6 @@ struct trace_subsystem_dir { + int nr_events; + }; + +-extern int call_filter_check_discard(struct trace_event_call *call, void *rec, +- struct trace_buffer *buffer, +- struct ring_buffer_event *event); +- + void trace_buffer_unlock_commit_regs(struct trace_array *tr, + struct trace_buffer *buffer, + struct ring_buffer_event *event, +diff --git a/kernel/trace/trace_branch.c b/kernel/trace/trace_branch.c +index 30f72e0ecb5d4..138f69521eb4a 100644 +--- a/kernel/trace/trace_branch.c ++++ b/kernel/trace/trace_branch.c +@@ -30,7 +30,6 @@ static struct trace_array *branch_tracer; + static void + probe_likely_condition(struct ftrace_likely_data *f, int val, int expect) + { +- struct trace_event_call *call = &event_branch; + struct trace_array *tr = branch_tracer; + struct trace_buffer *buffer; + struct trace_array_cpu *data; +@@ -82,8 +81,7 @@ probe_likely_condition(struct ftrace_likely_data *f, int val, int expect) + entry->line = f->data.line; + entry->correct = val == expect; + +- if (!call_filter_check_discard(call, entry, buffer, event)) +- trace_buffer_unlock_commit_nostack(buffer, event); ++ trace_buffer_unlock_commit_nostack(buffer, event); + + out: + current->trace_recursion &= ~TRACE_BRANCH_BIT; +diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c +index d07b8062ded30..14feebea4f000 100644 +--- a/kernel/trace/trace_events.c ++++ b/kernel/trace/trace_events.c +@@ -3319,8 +3319,6 @@ static void __trace_remove_event_call(struct trace_event_call *call) + { + event_remove(call); + trace_destroy_fields(call); +- free_event_filter(call->filter); +- call->filter = NULL; + } + + static int probe_remove_event_call(struct trace_event_call *call) +diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c +index 47ea114ca9f46..04d403a38f04f 100644 +--- a/kernel/trace/trace_functions_graph.c ++++ b/kernel/trace/trace_functions_graph.c +@@ -102,7 +102,6 @@ int __trace_graph_entry(struct trace_array *tr, + struct ftrace_graph_ent *trace, + unsigned int trace_ctx) + { +- struct trace_event_call *call = &event_funcgraph_entry; + struct ring_buffer_event *event; + struct trace_buffer *buffer = tr->array_buffer.buffer; + struct ftrace_graph_ent_entry *entry; +@@ -113,8 +112,7 @@ int __trace_graph_entry(struct trace_array *tr, + return 0; + entry = ring_buffer_event_data(event); + entry->graph_ent = *trace; +- if (!call_filter_check_discard(call, entry, buffer, event)) +- trace_buffer_unlock_commit_nostack(buffer, event); ++ trace_buffer_unlock_commit_nostack(buffer, event); + + return 1; + } +@@ -223,7 +221,6 @@ void __trace_graph_return(struct trace_array *tr, + struct ftrace_graph_ret *trace, + unsigned int trace_ctx) + { +- struct trace_event_call *call = &event_funcgraph_exit; + struct ring_buffer_event *event; + struct trace_buffer *buffer = tr->array_buffer.buffer; + struct ftrace_graph_ret_entry *entry; +@@ -234,8 +231,7 @@ void __trace_graph_return(struct trace_array *tr, + return; + entry = ring_buffer_event_data(event); + entry->ret = *trace; +- if (!call_filter_check_discard(call, entry, buffer, event)) +- trace_buffer_unlock_commit_nostack(buffer, event); ++ trace_buffer_unlock_commit_nostack(buffer, event); + } + + void trace_graph_return(struct ftrace_graph_ret *trace, +diff --git a/kernel/trace/trace_hwlat.c b/kernel/trace/trace_hwlat.c +index bc437b6ce8969..8703eb86a20c8 100644 +--- a/kernel/trace/trace_hwlat.c ++++ b/kernel/trace/trace_hwlat.c +@@ -130,7 +130,6 @@ static bool hwlat_busy; + static void trace_hwlat_sample(struct hwlat_sample *sample) + { + struct trace_array *tr = hwlat_trace; +- struct trace_event_call *call = &event_hwlat; + struct trace_buffer *buffer = tr->array_buffer.buffer; + struct ring_buffer_event *event; + struct hwlat_entry *entry; +@@ -148,8 +147,7 @@ static void trace_hwlat_sample(struct hwlat_sample *sample) + entry->nmi_count = sample->nmi_count; + entry->count = sample->count; + +- if (!call_filter_check_discard(call, entry, buffer, event)) +- trace_buffer_unlock_commit_nostack(buffer, event); ++ trace_buffer_unlock_commit_nostack(buffer, event); + } + + /* Macros to encapsulate the time capturing infrastructure */ +diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c +index c523ce5aa4958..6b964471265e1 100644 +--- a/kernel/trace/trace_mmiotrace.c ++++ b/kernel/trace/trace_mmiotrace.c +@@ -295,7 +295,6 @@ static void __trace_mmiotrace_rw(struct trace_array *tr, + struct trace_array_cpu *data, + struct mmiotrace_rw *rw) + { +- struct trace_event_call *call = &event_mmiotrace_rw; + struct trace_buffer *buffer = tr->array_buffer.buffer; + struct ring_buffer_event *event; + struct trace_mmiotrace_rw *entry; +@@ -311,8 +310,7 @@ static void __trace_mmiotrace_rw(struct trace_array *tr, + entry = ring_buffer_event_data(event); + entry->rw = *rw; + +- if (!call_filter_check_discard(call, entry, buffer, event)) +- trace_buffer_unlock_commit(tr, buffer, event, trace_ctx); ++ trace_buffer_unlock_commit(tr, buffer, event, trace_ctx); + } + + void mmio_trace_rw(struct mmiotrace_rw *rw) +@@ -326,7 +324,6 @@ static void __trace_mmiotrace_map(struct trace_array *tr, + struct trace_array_cpu *data, + struct mmiotrace_map *map) + { +- struct trace_event_call *call = &event_mmiotrace_map; + struct trace_buffer *buffer = tr->array_buffer.buffer; + struct ring_buffer_event *event; + struct trace_mmiotrace_map *entry; +@@ -342,8 +339,7 @@ static void __trace_mmiotrace_map(struct trace_array *tr, + entry = ring_buffer_event_data(event); + entry->map = *map; + +- if (!call_filter_check_discard(call, entry, buffer, event)) +- trace_buffer_unlock_commit(tr, buffer, event, trace_ctx); ++ trace_buffer_unlock_commit(tr, buffer, event, trace_ctx); + } + + void mmio_trace_mapping(struct mmiotrace_map *map) +diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c +index 4daedf8debdc5..549ac1916dc39 100644 +--- a/kernel/trace/trace_osnoise.c ++++ b/kernel/trace/trace_osnoise.c +@@ -501,7 +501,6 @@ static void print_osnoise_headers(struct seq_file *s) + static void + __trace_osnoise_sample(struct osnoise_sample *sample, struct trace_buffer *buffer) + { +- struct trace_event_call *call = &event_osnoise; + struct ring_buffer_event *event; + struct osnoise_entry *entry; + +@@ -519,8 +518,7 @@ __trace_osnoise_sample(struct osnoise_sample *sample, struct trace_buffer *buffe + entry->softirq_count = sample->softirq_count; + entry->thread_count = sample->thread_count; + +- if (!call_filter_check_discard(call, entry, buffer, event)) +- trace_buffer_unlock_commit_nostack(buffer, event); ++ trace_buffer_unlock_commit_nostack(buffer, event); + } + + /* +@@ -580,7 +578,6 @@ static void print_timerlat_headers(struct seq_file *s) + static void + __trace_timerlat_sample(struct timerlat_sample *sample, struct trace_buffer *buffer) + { +- struct trace_event_call *call = &event_osnoise; + struct ring_buffer_event *event; + struct timerlat_entry *entry; + +@@ -593,8 +590,7 @@ __trace_timerlat_sample(struct timerlat_sample *sample, struct trace_buffer *buf + entry->context = sample->context; + entry->timer_latency = sample->timer_latency; + +- if (!call_filter_check_discard(call, entry, buffer, event)) +- trace_buffer_unlock_commit_nostack(buffer, event); ++ trace_buffer_unlock_commit_nostack(buffer, event); + } + + /* +@@ -656,7 +652,6 @@ static void timerlat_save_stack(int skip) + static void + __timerlat_dump_stack(struct trace_buffer *buffer, struct trace_stack *fstack, unsigned int size) + { +- struct trace_event_call *call = &event_osnoise; + struct ring_buffer_event *event; + struct stack_entry *entry; + +@@ -670,8 +665,7 @@ __timerlat_dump_stack(struct trace_buffer *buffer, struct trace_stack *fstack, u + entry->size = fstack->nr_entries; + memcpy(&entry->caller, fstack->calls, size); + +- if (!call_filter_check_discard(call, entry, buffer, event)) +- trace_buffer_unlock_commit_nostack(buffer, event); ++ trace_buffer_unlock_commit_nostack(buffer, event); + } + + /* +diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c +index 039382576bc16..59e30d897a9da 100644 +--- a/kernel/trace/trace_sched_wakeup.c ++++ b/kernel/trace/trace_sched_wakeup.c +@@ -376,7 +376,6 @@ tracing_sched_switch_trace(struct trace_array *tr, + struct task_struct *next, + unsigned int trace_ctx) + { +- struct trace_event_call *call = &event_context_switch; + struct trace_buffer *buffer = tr->array_buffer.buffer; + struct ring_buffer_event *event; + struct ctx_switch_entry *entry; +@@ -394,8 +393,7 @@ tracing_sched_switch_trace(struct trace_array *tr, + entry->next_state = task_state_index(next); + entry->next_cpu = task_cpu(next); + +- if (!call_filter_check_discard(call, entry, buffer, event)) +- trace_buffer_unlock_commit(tr, buffer, event, trace_ctx); ++ trace_buffer_unlock_commit(tr, buffer, event, trace_ctx); + } + + static void +@@ -404,7 +402,6 @@ tracing_sched_wakeup_trace(struct trace_array *tr, + struct task_struct *curr, + unsigned int trace_ctx) + { +- struct trace_event_call *call = &event_wakeup; + struct ring_buffer_event *event; + struct ctx_switch_entry *entry; + struct trace_buffer *buffer = tr->array_buffer.buffer; +@@ -422,8 +419,7 @@ tracing_sched_wakeup_trace(struct trace_array *tr, + entry->next_state = task_state_index(wakee); + entry->next_cpu = task_cpu(wakee); + +- if (!call_filter_check_discard(call, entry, buffer, event)) +- trace_buffer_unlock_commit(tr, buffer, event, trace_ctx); ++ trace_buffer_unlock_commit(tr, buffer, event, trace_ctx); + } + + static void notrace +-- +2.53.0 + diff --git a/queue-6.12/wifi-mac80211-validate-individual-twt-params-before-.patch b/queue-6.12/wifi-mac80211-validate-individual-twt-params-before-.patch new file mode 100644 index 0000000000..df94b8904e --- /dev/null +++ b/queue-6.12/wifi-mac80211-validate-individual-twt-params-before-.patch @@ -0,0 +1,52 @@ +From aec0e05ca4f9d13307fdf2e76c7343353887cf28 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 09:09:28 +0800 +Subject: wifi: mac80211: validate individual TWT params before driver setup + +From: Zhao Li + +[ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ] + +ieee80211_process_rx_twt_action() only partially validates a received +S1G TWT setup frame before queueing it. + +An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup() +with twt->length too short for the full struct ieee80211_twt_params. + +The individual path passes twt to drv_add_twt_setup(). Both the tracepoint +and the driver callback consume the complete parameters block, not merely +req_type. Do not pass a short individual agreement to the driver. +Broadcast agreements remain unchanged because they are rejected locally +after accessing only req_type. + +Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode") +Assisted-by: Codex:gpt-5 +Assisted-by: Claude:opus-4.8 +Signed-off-by: Zhao Li +Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com +[edit commit message to not overclaim lack of validation nor + understate driver impact] +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/s1g.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c +index d4ed0c0a335ca..2a23e431cbb5a 100644 +--- a/net/mac80211/s1g.c ++++ b/net/mac80211/s1g.c +@@ -101,6 +101,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata, + struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable; + struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + ++ if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) && ++ twt->length < sizeof(twt->control) + sizeof(*twt_agrt)) ++ return; ++ + twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST); + + /* broadcast TWT not supported yet */ +-- +2.53.0 + diff --git a/queue-6.18/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch b/queue-6.18/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch new file mode 100644 index 0000000000..91463d8b0f --- /dev/null +++ b/queue-6.18/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch @@ -0,0 +1,49 @@ +From a9a9b37003fa6a6b173974e4b0540bcbffa959a9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 02:25:20 +0500 +Subject: accel/qaic: use sizeof(*trans_hdr) for transaction length check + +From: Muhammad Bilal + +[ Upstream commit d6c075f797a672a6e3bd2fd44aee713801698ec2 ] + +In encode_message() the per-transaction lower-bound check compares +trans_hdr->len against sizeof(trans_hdr), i.e. the size of the pointer, +instead of sizeof(*trans_hdr), the size of struct qaic_manage_trans_hdr. + +Every other length check in this file (encode_message() at the loop +guard, decode_message(), etc.) correctly uses sizeof(*trans_hdr), so +this is an inconsistency. On 64-bit builds the pointer and the struct +are both 8 bytes, so the check is correct by coincidence and there is +no behavioural change. On 32-bit builds the pointer is 4 bytes, which +weakens the minimum-length check below the 8-byte header size. + +Use sizeof(*trans_hdr) so the check validates against the actual +transaction header size on all builds. + +Fixes: ea33cb6fc278 ("accel/qaic: tighten bounds checking in encode_message()") +Signed-off-by: Muhammad Bilal +Reviewed-by: Jeff Hugo +Signed-off-by: Jeff Hugo +Link: https://patch.msgid.link/20260617212520.59801-1-meatuni001@gmail.com +Signed-off-by: Sasha Levin +--- + drivers/accel/qaic/qaic_control.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c +index 8eae30fe14f98..8759b53ba38eb 100644 +--- a/drivers/accel/qaic/qaic_control.c ++++ b/drivers/accel/qaic/qaic_control.c +@@ -782,7 +782,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg, + break; + } + trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len); +- if (trans_hdr->len < sizeof(trans_hdr) || ++ if (trans_hdr->len < sizeof(*trans_hdr) || + size_add(user_len, trans_hdr->len) > user_msg->len) { + ret = -EINVAL; + break; +-- +2.53.0 + diff --git a/queue-6.18/af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch b/queue-6.18/af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch new file mode 100644 index 0000000000..c16bf3cd71 --- /dev/null +++ b/queue-6.18/af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch @@ -0,0 +1,41 @@ +From d93886858d1051c551d395f77757e9f34a7ea295 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 18 Jul 2026 14:29:01 -0400 +Subject: af_unix: fix listen() succeeding on sockets in the wrong state + +From: John Ericson + +[ Upstream commit f0d9c3ffc2b5fc2ffacb56b3036155ce7a940a12 ] + +Commit fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for +reaped sk->sk_peer_pid") inserted a prepare_peercred() call between err += -EINVAL and the socket-state check in unix_listen(). Since +prepare_peercred() leaves err at 0 on success, listen() on an AF_UNIX +socket that is not in TCP_CLOSE or TCP_LISTEN state (e.g. one that is +already connected) now silently returns success without doing anything, +instead of failing with EINVAL as it did before. + +Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid") +Signed-off-by: John Ericson +Link: https://patch.msgid.link/20260718182903.2295560-1-John.Ericson@Obsidian.Systems +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/unix/af_unix.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c +index b65479a738f4c..52b17331e9d51 100644 +--- a/net/unix/af_unix.c ++++ b/net/unix/af_unix.c +@@ -835,6 +835,7 @@ static int unix_listen(struct socket *sock, int backlog) + if (err) + goto out; + unix_state_lock(sk); ++ err = -EINVAL; + if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN) + goto out_unlock; + if (backlog > sk->sk_max_ack_backlog) +-- +2.53.0 + diff --git a/queue-6.18/arch-x86-mshyperv-discover-confidential-vmbus-availa.patch b/queue-6.18/arch-x86-mshyperv-discover-confidential-vmbus-availa.patch new file mode 100644 index 0000000000..9226a28349 --- /dev/null +++ b/queue-6.18/arch-x86-mshyperv-discover-confidential-vmbus-availa.patch @@ -0,0 +1,104 @@ +From 93d8ad7629a4fa7bfe80c812af43c9abc771faa3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Oct 2025 16:34:05 -0700 +Subject: arch/x86: mshyperv: Discover Confidential VMBus availability + +From: Roman Kisel + +[ Upstream commit 7c8b6c326d830ca5c6b95f390c703966e14167e6 ] + +Confidential VMBus requires enabling paravisor SynIC, and +the x86_64 guest has to inspect the Virtualization Stack (VS) +CPUID leaf to see if Confidential VMBus is available. If it is, +the guest shall enable the paravisor SynIC. + +Read the relevant data from the VS CPUID leaf. Refactor the +code to avoid repeating CPUID and add flags to the struct +ms_hyperv_info. For ARM64, the flag for Confidential VMBus +is not set which provides the desired behaviour for now as +it is not available on ARM64 just yet. Once ARM64 CCA guests +are supported, this flag will be set unconditionally when +running such a guest. + +Signed-off-by: Roman Kisel +Reviewed-by: Michael Kelley +Signed-off-by: Wei Liu +Stable-dep-of: 8c7ab779c885 ("Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep annotation") +Signed-off-by: Sasha Levin +--- + arch/x86/kernel/cpu/mshyperv.c | 28 +++++++++++++++------------- + include/asm-generic/mshyperv.h | 2 ++ + 2 files changed, 17 insertions(+), 13 deletions(-) + +diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c +index c4febdbcfe4d8..cae0dcd9006bd 100644 +--- a/arch/x86/kernel/cpu/mshyperv.c ++++ b/arch/x86/kernel/cpu/mshyperv.c +@@ -440,7 +440,7 @@ EXPORT_SYMBOL_GPL(hv_get_hypervisor_version); + + static void __init ms_hyperv_init_platform(void) + { +- int hv_max_functions_eax; ++ int hv_max_functions_eax, eax; + + #ifdef CONFIG_PARAVIRT + pv_info.name = "Hyper-V"; +@@ -475,6 +475,19 @@ static void __init ms_hyperv_init_platform(void) + pr_info("Hyper-V: running on a nested hypervisor\n"); + } + ++ /* ++ * There is no check against the max function for HYPERV_CPUID_VIRT_STACK_* CPUID ++ * leaves as the hypervisor doesn't handle them. Even a nested root partition (L2 ++ * root) will not get them because the nested (L1) hypervisor filters them out. ++ * These are handled through intercept processing by the Windows Hyper-V stack ++ * or the paravisor. ++ */ ++ eax = cpuid_eax(HYPERV_CPUID_VIRT_STACK_PROPERTIES); ++ ms_hyperv.confidential_vmbus_available = ++ eax & HYPERV_VS_PROPERTIES_EAX_CONFIDENTIAL_VMBUS_AVAILABLE; ++ ms_hyperv.msi_ext_dest_id = ++ eax & HYPERV_VS_PROPERTIES_EAX_EXTENDED_IOAPIC_RTE; ++ + if (ms_hyperv.features & HV_ACCESS_FREQUENCY_MSRS && + ms_hyperv.misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE) { + x86_platform.calibrate_tsc = hv_get_tsc_khz; +@@ -675,21 +688,10 @@ static bool __init ms_hyperv_x2apic_available(void) + * pci-hyperv host bridge. + * + * Note: for a Hyper-V root partition, this will always return false. +- * The hypervisor doesn't expose these HYPERV_CPUID_VIRT_STACK_* cpuids by +- * default, they are implemented as intercepts by the Windows Hyper-V stack. +- * Even a nested root partition (L2 root) will not get them because the +- * nested (L1) hypervisor filters them out. + */ + static bool __init ms_hyperv_msi_ext_dest_id(void) + { +- u32 eax; +- +- eax = cpuid_eax(HYPERV_CPUID_VIRT_STACK_INTERFACE); +- if (eax != HYPERV_VS_INTERFACE_EAX_SIGNATURE) +- return false; +- +- eax = cpuid_eax(HYPERV_CPUID_VIRT_STACK_PROPERTIES); +- return eax & HYPERV_VS_PROPERTIES_EAX_EXTENDED_IOAPIC_RTE; ++ return ms_hyperv.msi_ext_dest_id; + } + + #ifdef CONFIG_AMD_MEM_ENCRYPT +diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h +index b89c7e3a20474..8862f77241e6c 100644 +--- a/include/asm-generic/mshyperv.h ++++ b/include/asm-generic/mshyperv.h +@@ -62,6 +62,8 @@ struct ms_hyperv_info { + }; + }; + u64 shared_gpa_boundary; ++ bool msi_ext_dest_id; ++ bool confidential_vmbus_available; + }; + extern struct ms_hyperv_info ms_hyperv; + extern bool hv_nested; +-- +2.53.0 + diff --git a/queue-6.18/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.18/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..8720eb679c --- /dev/null +++ b/queue-6.18/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From 024311e871e29dfd371fc72bff1deb4cd403b792 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:12:54 +0530 +Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ] + +In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98090->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98090.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c +index 5aff5a459a433..830caf62d0fa8 100644 +--- a/sound/soc/codecs/max98090.c ++++ b/sound/soc/codecs/max98090.c +@@ -2390,8 +2390,9 @@ static int max98090_probe(struct snd_soc_component *component) + dev_dbg(component->dev, "max98090_probe\n"); + + max98090->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98090->mclk)) ++ if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + max98090->component = component; + +-- +2.53.0 + diff --git a/queue-6.18/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.18/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..c4e2a77dce --- /dev/null +++ b/queue-6.18/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From d110519c67fc766c93f2d053a5f3977d1aea0122 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:09:50 +0530 +Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ] + +In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98095->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98095.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c +index cfb63fe69267b..ad2587d2df819 100644 +--- a/sound/soc/codecs/max98095.c ++++ b/sound/soc/codecs/max98095.c +@@ -1984,8 +1984,9 @@ static int max98095_probe(struct snd_soc_component *component) + int ret = 0; + + max98095->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98095->mclk)) ++ if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + /* reset the codec, the DSP core, and disable all interrupts */ + max98095_reset(component); +-- +2.53.0 + diff --git a/queue-6.18/asoc-sdca-ensure-that-control-range-is-large-enough-.patch b/queue-6.18/asoc-sdca-ensure-that-control-range-is-large-enough-.patch new file mode 100644 index 0000000000..e7419a2b5a --- /dev/null +++ b/queue-6.18/asoc-sdca-ensure-that-control-range-is-large-enough-.patch @@ -0,0 +1,39 @@ +From 52c7c4ba5d8a3de9528fa5862ab4d0417eb8d6a5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 11:35:00 +0100 +Subject: ASoC: SDCA: Ensure that Control Range is large enough for header + +From: Charles Keepax + +[ Upstream commit 951e921b039b793bef7050eaf5c5fb1a4a5341d1 ] + +When reading the Ranges structure from an SDCA Control, ensure that the +read data is large enough to encompass the required header before +accessing it. + +Fixes: 64fb5af1d1bb ("ASoC: SDCA: Add parsing for Control range structures") +Signed-off-by: Charles Keepax +Reviewed-by: Pierre-Louis Bossart +Link: https://patch.msgid.link/20260722103500.872714-5-ckeepax@opensource.cirrus.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/sdca/sdca_functions.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c +index 4417278e39bb1..e0cae47641ba5 100644 +--- a/sound/soc/sdca/sdca_functions.c ++++ b/sound/soc/sdca/sdca_functions.c +@@ -849,6 +849,8 @@ static int find_sdca_control_range(struct device *dev, + return 0; + else if (num_range < 0) + return num_range; ++ else if (num_range < 2 * sizeof(*limits)) ++ return -EINVAL; + + range_list = devm_kcalloc(dev, num_range, sizeof(*range_list), GFP_KERNEL); + if (!range_list) +-- +2.53.0 + diff --git a/queue-6.18/asoc-tas2781-use-correct-calibration-data-for-sinega.patch b/queue-6.18/asoc-tas2781-use-correct-calibration-data-for-sinega.patch new file mode 100644 index 0000000000..bf70b4577e --- /dev/null +++ b/queue-6.18/asoc-tas2781-use-correct-calibration-data-for-sinega.patch @@ -0,0 +1,44 @@ +From d81792e8dd5f471d3ad6bc74803c0b214676347f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:16:16 +0800 +Subject: ASoC: tas2781: Use correct calibration data for SINEGAIN2 register + +From: wangdicheng + +[ Upstream commit dd88cf6273de61f2f7206c2066af97798dbb38b0 ] + +The SINEGAIN2_REG case in cali_reg_update() references t->sin_gn[] +rather than t->sin_gn2[], causing the second pilot tone gain +calibration to be programmed with the wrong register address. + +These are distinct fields in struct fct_param_address and are +populated from separate firmware parameters by the parser in +tas2781-fmwlib.c. + +Fixes: 84d6a465f211 ("ASoC: tas2781: Support dsp firmware Alpha and Beta seaies") +Signed-off-by: wangdicheng +Link: https://patch.msgid.link/20260720081616.631413-1-wangdich9700@163.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/tas2781-i2c.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c +index a3b4d2c3b4789..0b83446da45ea 100644 +--- a/sound/soc/codecs/tas2781-i2c.c ++++ b/sound/soc/codecs/tas2781-i2c.c +@@ -1303,8 +1303,8 @@ static void cali_reg_update(struct bulk_reg_val *p, + t->sin_gn[2]); + break; + case TAS2781_PRM_SINEGAIN2_REG: +- reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1], +- t->sin_gn[2]); ++ reg = TASDEVICE_REG(t->sin_gn2[0], t->sin_gn2[1], ++ t->sin_gn2[2]); + break; + default: + reg = 0; +-- +2.53.0 + diff --git a/queue-6.18/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-6.18/assoc_array-trim-the-final-shortcut-word-using-the-c.patch new file mode 100644 index 0000000000..0bf5589bab --- /dev/null +++ b/queue-6.18/assoc_array-trim-the-final-shortcut-word-using-the-c.patch @@ -0,0 +1,66 @@ +From 20413f4c0b0c345697994a9dbbc5801db6a4de59 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:05 -0400 +Subject: assoc_array: trim the final shortcut word using the current chunk end + +From: Michael Bommarito + +[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ] + +assoc_array_walk() masks off the bits past shortcut->skip_to_level in the +word that contains skip_to_level, gated on +round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level. + +That guard is wrong in two opposite ways: + + - When sc_level is word-aligned (every word after the first) round_up() + is a no-op, so the guard is sc_level > skip_to_level and never fires for + the word that holds skip_to_level. A shortcut that spans more than one + word and ends in the middle of its last word leaves that word untrimmed, + and its stale high bits leak into the dissimilarity word and can steer + the walk down the wrong descendant. + + - When sc_level is unaligned (the first word) and skip_to_level sits on + the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and + fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears + the whole dissimilarity word and makes a differing shortcut compare + equal. + +Use the end of the chunk that contains sc_level instead: + + skip_to_level < round_down(sc_level, CHUNK) + CHUNK + +For an aligned sc_level whose word holds skip_to_level this now fires (the +first bug); for an unaligned sc_level with skip_to_level on the following +boundary it does not, so shift is never 0 when the branch runs and the trim +never clears the whole word. + +Fixes: 3cb989501c26 ("Add a generic associative array implementation.") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + lib/assoc_array.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/assoc_array.c b/lib/assoc_array.c +index 388e656ac9743..01619e88a52c9 100644 +--- a/lib/assoc_array.c ++++ b/lib/assoc_array.c +@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array, + sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT]; + dissimilarity = segments ^ sc_segments; + +- if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) { ++ if (shortcut->skip_to_level < round_down(sc_level, ++ ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) { + /* Trim segments that are beyond the shortcut */ + int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK; + dissimilarity &= ~(ULONG_MAX << shift); +-- +2.53.0 + diff --git a/queue-6.18/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch b/queue-6.18/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch new file mode 100644 index 0000000000..acfdb7f658 --- /dev/null +++ b/queue-6.18/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch @@ -0,0 +1,76 @@ +From 96cb7b4470fdcafe5b7be1243f34b401a2f086d7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 17 Jul 2026 23:55:26 +0530 +Subject: ata: ahci_ceva: fix error paths in + ceva_ahci_platform_enable_resources() + +From: Radhey Shyam Pandey + +[ Upstream commit 4d99a91574c420decab56cc880fad0dc15b8a7a3 ] + +On phy_init() failure the error path fallsthrough to disable_rsts, which +deasserts the controller reset and then enters disable_phys calling +phy_power_off() on PHYs that were never powered on. That corrupts the PHY +power_count and triggers an extra runtime PM put. + +Use a separate exit_phys path that unwinds with phy_exit() only and falls +through to disable_clks while the controller remains in reset. Reserve +phy_power_off() for the phy_power_on() failure path only, and skip +masked-out ports in both unwind loops. + +On phy_power_on() failure re-assert the controller reset before disabling +clocks and regulators, matching the teardown order used by +ahci_platform_enable_resources() and ahci_platform_disable_resources(). + +Fixes: 26c8404e162b ("ata: ahci_ceva: fix error handling for Xilinx GT PHY support") +Signed-off-by: Radhey Shyam Pandey +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + drivers/ata/ahci_ceva.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c +index 2d6a08c23d6ad..2961e53288f42 100644 +--- a/drivers/ata/ahci_ceva.c ++++ b/drivers/ata/ahci_ceva.c +@@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + + rc = phy_init(hpriv->phys[i]); + if (rc) +- goto disable_rsts; ++ goto exit_phys; + } + + /* De-assert the controller reset */ +@@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + + return 0; + +-disable_rsts: +- ahci_platform_deassert_rsts(hpriv); +- + disable_phys: + while (--i >= 0) { ++ if (ahci_ignore_port(hpriv, i)) ++ continue; ++ + phy_power_off(hpriv->phys[i]); + phy_exit(hpriv->phys[i]); + } ++ ahci_platform_assert_rsts(hpriv); ++ goto disable_clks; ++ ++exit_phys: ++ while (--i >= 0) { ++ if (ahci_ignore_port(hpriv, i)) ++ continue; ++ ++ phy_exit(hpriv->phys[i]); ++ } + + disable_clks: + ahci_platform_disable_clks(hpriv); +-- +2.53.0 + diff --git a/queue-6.18/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch b/queue-6.18/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch new file mode 100644 index 0000000000..8fdea02949 --- /dev/null +++ b/queue-6.18/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch @@ -0,0 +1,44 @@ +From 32285b5e143e6b1f6875414ec702cde179e5b961 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 15:31:37 -0700 +Subject: ata: sata_mv: accept 1 or 2 resources in platform probe + +From: Rosen Penev + +[ Upstream commit ef19a9cf037957fe3a35df8355c76ff0a63a0436 ] + +Board files in arch/arm/plat-orion, arch/arm/mach-dove, +arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the +"sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ). +Those devices are rejected with -EINVAL, so SATA no longer probes on +legacy Marvell Orion/Kirkwood-style boards. + +Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2 +resources (legacy, IRQ supplied as a second resource) so both probing +paths work. + +Fixes: b3b2bec9646e ("ata: sata_mv: Fixes expected number of resources now IRQs are gone") +Assisted-by: opencode:big-pickle +Signed-off-by: Rosen Penev +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + drivers/ata/sata_mv.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c +index ffb396f61731f..1d8ca95fb7328 100644 +--- a/drivers/ata/sata_mv.c ++++ b/drivers/ata/sata_mv.c +@@ -4026,7 +4026,7 @@ static int mv_platform_probe(struct platform_device *pdev) + /* + * Simple resource validation .. + */ +- if (unlikely(pdev->num_resources != 1)) { ++ if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) { + dev_err(&pdev->dev, "invalid number of resources\n"); + return -EINVAL; + } +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-btintel-validate-length-before-parsing-dia.patch b/queue-6.18/bluetooth-btintel-validate-length-before-parsing-dia.patch new file mode 100644 index 0000000000..216e5df05f --- /dev/null +++ b/queue-6.18/bluetooth-btintel-validate-length-before-parsing-dia.patch @@ -0,0 +1,40 @@ +From b3d9264ff081068e5cc9f047108cfbbbce389adc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 01:54:40 -0700 +Subject: Bluetooth: btintel: Validate length before parsing diagnostics TLV + +From: Zijun Hu + +[ Upstream commit b640ff9af3c809ff5ea2077fbba17df1594ec1e4 ] + +btintel_diagnostics() accesses tlv->val[0] without first validating +that the diagnostics VSE is long enough to contain that field, so +may cause reading data beyond the received frame. + +Fix by validating the length before access. + +Fixes: af395330abed ("Bluetooth: btintel: Add Intel devcoredump support") +Signed-off-by: Zijun Hu +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btintel.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c +index 5e0a05edcbfd1..4d6b6bbb4267e 100644 +--- a/drivers/bluetooth/btintel.c ++++ b/drivers/bluetooth/btintel.c +@@ -3694,6 +3694,9 @@ static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb) + { + struct intel_tlv *tlv = (void *)&skb->data[5]; + ++ if (skb->len < 5 + sizeof(*tlv) + sizeof(tlv->val[0])) ++ goto recv_frame; ++ + /* The first event is always an event type TLV */ + if (tlv->type != INTEL_TLV_TYPE_ID) + goto recv_frame; +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-hci-add-initial-support-for-past.patch b/queue-6.18/bluetooth-hci-add-initial-support-for-past.patch new file mode 100644 index 0000000000..0f8b4529c4 --- /dev/null +++ b/queue-6.18/bluetooth-hci-add-initial-support-for-past.patch @@ -0,0 +1,360 @@ +From 29c7fe784270a2206168b289853a2a4c630ad1e9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Sep 2025 11:11:40 -0400 +Subject: Bluetooth: HCI: Add initial support for PAST + +From: Luiz Augusto von Dentz + +[ Upstream commit 33b2835f0b7e2a458473b0e3a23b54b92108b6b0 ] + +This adds PAST related commands (HCI_OP_LE_PAST, +HCI_OP_LE_PAST_SET_INFO and HCI_OP_LE_PAST_PARAMS) and events +(HCI_EV_LE_PAST_RECEIVED) along with handling of PAST sender and +receiver features bits including new MGMG settings ( +HCI_EV_LE_PAST_RECEIVED and MGMT_SETTING_PAST_RECEIVER) which +userspace can use to determine if PAST is supported by the +controller. + +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: 4311fd6f4290 ("Bluetooth: ISO: lock sk in iso_connect_ind") +Signed-off-by: Sasha Levin +--- + include/net/bluetooth/hci.h | 54 ++++++++++++++++++++++ + include/net/bluetooth/hci_core.h | 12 +++++ + include/net/bluetooth/mgmt.h | 2 + + net/bluetooth/hci_event.c | 79 ++++++++++++++++++++++++++++---- + net/bluetooth/hci_sync.c | 3 ++ + net/bluetooth/iso.c | 25 ++++++++++ + net/bluetooth/mgmt.c | 12 +++++ + 7 files changed, 177 insertions(+), 10 deletions(-) + +diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h +index 2613cfe5b8a39..87b3f99e6ebf1 100644 +--- a/include/net/bluetooth/hci.h ++++ b/include/net/bluetooth/hci.h +@@ -647,6 +647,8 @@ enum { + #define HCI_LE_EXT_ADV 0x10 + #define HCI_LE_PERIODIC_ADV 0x20 + #define HCI_LE_CHAN_SEL_ALG2 0x40 ++#define HCI_LE_PAST_SENDER 0x01 ++#define HCI_LE_PAST_RECEIVER 0x02 + #define HCI_LE_CIS_CENTRAL 0x10 + #define HCI_LE_CIS_PERIPHERAL 0x20 + #define HCI_LE_ISO_BROADCASTER 0x40 +@@ -2068,6 +2070,44 @@ struct hci_cp_le_set_privacy_mode { + __u8 mode; + } __packed; + ++#define HCI_OP_LE_PAST 0x205a ++struct hci_cp_le_past { ++ __le16 handle; ++ __le16 service_data; ++ __le16 sync_handle; ++} __packed; ++ ++struct hci_rp_le_past { ++ __u8 status; ++ __le16 handle; ++} __packed; ++ ++#define HCI_OP_LE_PAST_SET_INFO 0x205b ++struct hci_cp_le_past_set_info { ++ __le16 handle; ++ __le16 service_data; ++ __u8 adv_handle; ++} __packed; ++ ++struct hci_rp_le_past_set_info { ++ __u8 status; ++ __le16 handle; ++} __packed; ++ ++#define HCI_OP_LE_PAST_PARAMS 0x205c ++struct hci_cp_le_past_params { ++ __le16 handle; ++ __u8 mode; ++ __le16 skip; ++ __le16 sync_timeout; ++ __u8 cte_type; ++} __packed; ++ ++struct hci_rp_le_past_params { ++ __u8 status; ++ __le16 handle; ++} __packed; ++ + #define HCI_OP_LE_READ_BUFFER_SIZE_V2 0x2060 + struct hci_rp_le_read_buffer_size_v2 { + __u8 status; +@@ -2800,6 +2840,20 @@ struct hci_evt_le_ext_adv_set_term { + __u8 num_evts; + } __packed; + ++#define HCI_EV_LE_PAST_RECEIVED 0x18 ++struct hci_ev_le_past_received { ++ __u8 status; ++ __le16 handle; ++ __le16 service_data; ++ __le16 sync_handle; ++ __u8 sid; ++ __u8 bdaddr_type; ++ bdaddr_t bdaddr; ++ __u8 phy; ++ __le16 interval; ++ __u8 clock_accuracy; ++} __packed; ++ + #define HCI_EVT_LE_CIS_ESTABLISHED 0x19 + struct hci_evt_le_cis_established { + __u8 status; +diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h +index b5640980fbf4e..a08d8910ba265 100644 +--- a/include/net/bluetooth/hci_core.h ++++ b/include/net/bluetooth/hci_core.h +@@ -2054,6 +2054,18 @@ void hci_conn_del_sysfs(struct hci_conn *conn); + #define sync_recv_capable(dev) \ + ((dev)->le_features[3] & HCI_LE_ISO_SYNC_RECEIVER) + #define sync_recv_enabled(dev) (le_enabled(dev) && sync_recv_capable(dev)) ++#define past_sender_capable(dev) \ ++ ((dev)->le_features[3] & HCI_LE_PAST_SENDER) ++#define past_receiver_capable(dev) \ ++ ((dev)->le_features[3] & HCI_LE_PAST_RECEIVER) ++#define past_capable(dev) \ ++ (past_sender_capable(dev) || past_receiver_capable(dev)) ++#define past_sender_enabled(dev) \ ++ (le_enabled(dev) && past_sender_capable(dev)) ++#define past_receiver_enabled(dev) \ ++ (le_enabled(dev) && past_receiver_capable(dev)) ++#define past_enabled(dev) \ ++ (past_sender_enabled(dev) || past_receiver_enabled(dev)) + + #define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \ + (!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG))) +diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h +index f5be96f08b9d9..8234915854b68 100644 +--- a/include/net/bluetooth/mgmt.h ++++ b/include/net/bluetooth/mgmt.h +@@ -119,6 +119,8 @@ struct mgmt_rp_read_index_list { + #define MGMT_SETTING_ISO_BROADCASTER BIT(20) + #define MGMT_SETTING_ISO_SYNC_RECEIVER BIT(21) + #define MGMT_SETTING_LL_PRIVACY BIT(22) ++#define MGMT_SETTING_PAST_SENDER BIT(23) ++#define MGMT_SETTING_PAST_RECEIVER BIT(24) + + #define MGMT_OP_READ_INFO 0x0004 + #define MGMT_READ_INFO_SIZE 0 +diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c +index 396aafa609c9a..ce89f7cbebc7d 100644 +--- a/net/bluetooth/hci_event.c ++++ b/net/bluetooth/hci_event.c +@@ -5950,6 +5950,71 @@ static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, void *data, + hci_dev_unlock(hdev); + } + ++static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle) ++{ ++ struct hci_cp_le_pa_term_sync cp; ++ ++ memset(&cp, 0, sizeof(cp)); ++ cp.handle = handle; ++ ++ return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp); ++} ++ ++static void hci_le_past_received_evt(struct hci_dev *hdev, void *data, ++ struct sk_buff *skb) ++{ ++ struct hci_ev_le_past_received *ev = data; ++ int mask = hdev->link_mode; ++ __u8 flags = 0; ++ struct hci_conn *pa_sync, *conn; ++ ++ bt_dev_dbg(hdev, "status 0x%2.2x", ev->status); ++ ++ hci_dev_lock(hdev); ++ ++ hci_dev_clear_flag(hdev, HCI_PA_SYNC); ++ ++ conn = hci_conn_hash_lookup_create_pa_sync(hdev); ++ if (!conn) { ++ bt_dev_err(hdev, ++ "Unable to find connection for dst %pMR sid 0x%2.2x", ++ &ev->bdaddr, ev->sid); ++ goto unlock; ++ } ++ ++ conn->sync_handle = le16_to_cpu(ev->sync_handle); ++ conn->sid = HCI_SID_INVALID; ++ ++ mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, PA_LINK, ++ &flags); ++ if (!(mask & HCI_LM_ACCEPT)) { ++ hci_le_pa_term_sync(hdev, ev->sync_handle); ++ goto unlock; ++ } ++ ++ if (!(flags & HCI_PROTO_DEFER)) ++ goto unlock; ++ ++ /* Add connection to indicate PA sync event */ ++ pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY, ++ HCI_ROLE_SLAVE); ++ ++ if (IS_ERR(pa_sync)) ++ goto unlock; ++ ++ pa_sync->sync_handle = le16_to_cpu(ev->sync_handle); ++ ++ if (ev->status) { ++ set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags); ++ ++ /* Notify iso layer */ ++ hci_connect_cfm(pa_sync, ev->status); ++ } ++ ++unlock: ++ hci_dev_unlock(hdev); ++} ++ + static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, void *data, + struct sk_buff *skb) + { +@@ -6426,16 +6491,6 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data, + hci_dev_unlock(hdev); + } + +-static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle) +-{ +- struct hci_cp_le_pa_term_sync cp; +- +- memset(&cp, 0, sizeof(cp)); +- cp.handle = handle; +- +- return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp); +-} +- + static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data, + struct sk_buff *skb) + { +@@ -7250,6 +7305,10 @@ static const struct hci_le_ev { + /* [0x12 = HCI_EV_LE_EXT_ADV_SET_TERM] */ + HCI_LE_EV(HCI_EV_LE_EXT_ADV_SET_TERM, hci_le_ext_adv_term_evt, + sizeof(struct hci_evt_le_ext_adv_set_term)), ++ /* [0x18 = HCI_EVT_LE_PAST_RECEIVED] */ ++ HCI_LE_EV(HCI_EV_LE_PAST_RECEIVED, ++ hci_le_past_received_evt, ++ sizeof(struct hci_ev_le_past_received)), + /* [0x19 = HCI_EVT_LE_CIS_ESTABLISHED] */ + HCI_LE_EV(HCI_EVT_LE_CIS_ESTABLISHED, hci_le_cis_established_evt, + sizeof(struct hci_evt_le_cis_established)), +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 65c8a490c5553..87a2502fccd6e 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -4450,6 +4450,9 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev) + if (ext_adv_capable(hdev)) + events[2] |= 0x02; /* LE Advertising Set Terminated */ + ++ if (past_receiver_capable(hdev)) ++ events[2] |= 0x80; /* LE PAST Received */ ++ + if (cis_capable(hdev)) { + events[3] |= 0x01; /* LE CIS Established */ + if (cis_peripheral_capable(hdev)) +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 4ea41b093ac65..f65e77f7e0349 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -80,6 +80,7 @@ static struct bt_iso_qos default_qos; + static bool check_ucast_qos(struct bt_iso_qos *qos); + static bool check_bcast_qos(struct bt_iso_qos *qos); + static bool iso_match_sid(struct sock *sk, void *data); ++static bool iso_match_sid_past(struct sock *sk, void *data); + static bool iso_match_sync_handle(struct sock *sk, void *data); + static bool iso_match_sync_handle_pa_report(struct sock *sk, void *data); + static void iso_sock_disconn(struct sock *sk); +@@ -2147,6 +2148,16 @@ static bool iso_match_sid(struct sock *sk, void *data) + return ev->sid == iso_pi(sk)->bc_sid; + } + ++static bool iso_match_sid_past(struct sock *sk, void *data) ++{ ++ struct hci_ev_le_past_received *ev = data; ++ ++ if (iso_pi(sk)->bc_sid == HCI_SID_INVALID) ++ return true; ++ ++ return ev->sid == iso_pi(sk)->bc_sid; ++} ++ + static bool iso_match_sync_handle(struct sock *sk, void *data) + { + struct hci_evt_le_big_info_adv_report *ev = data; +@@ -2166,6 +2177,7 @@ static bool iso_match_sync_handle_pa_report(struct sock *sk, void *data) + int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + { + struct hci_ev_le_pa_sync_established *ev1; ++ struct hci_ev_le_past_received *ev1a; + struct hci_evt_le_big_info_adv_report *ev2; + struct hci_ev_le_per_adv_report *ev3; + struct sock *sk; +@@ -2179,6 +2191,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + * SID to listen to and once sync is established its handle needs to + * be stored in iso_pi(sk)->sync_handle so it can be matched once + * receiving the BIG Info. ++ * 1a. HCI_EV_LE_PAST_RECEIVED: alternative to 1. + * 2. HCI_EVT_LE_BIG_INFO_ADV_REPORT: When connect_ind is triggered by a + * a BIG Info it attempts to check if there any listening socket with + * the same sync_handle and if it does then attempt to create a sync. +@@ -2198,6 +2211,18 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + goto done; + } + ++ ev1a = hci_recv_event_data(hdev, HCI_EV_LE_PAST_RECEIVED); ++ if (ev1a) { ++ sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN, ++ iso_match_sid_past, ev1a); ++ if (sk && !ev1a->status) { ++ iso_pi(sk)->sync_handle = le16_to_cpu(ev1a->sync_handle); ++ iso_pi(sk)->bc_sid = ev1a->sid; ++ } ++ ++ goto done; ++ } ++ + ev2 = hci_recv_event_data(hdev, HCI_EVT_LE_BIG_INFO_ADV_REPORT); + if (ev2) { + /* Check if BIGInfo report has already been handled */ +diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c +index 79eb605be2804..424e7980f32e1 100644 +--- a/net/bluetooth/mgmt.c ++++ b/net/bluetooth/mgmt.c +@@ -858,6 +858,12 @@ static u32 get_supported_settings(struct hci_dev *hdev) + if (ll_privacy_capable(hdev)) + settings |= MGMT_SETTING_LL_PRIVACY; + ++ if (past_sender_capable(hdev)) ++ settings |= MGMT_SETTING_PAST_SENDER; ++ ++ if (past_receiver_capable(hdev)) ++ settings |= MGMT_SETTING_PAST_RECEIVER; ++ + settings |= MGMT_SETTING_PHY_CONFIGURATION; + + return settings; +@@ -943,6 +949,12 @@ static u32 get_current_settings(struct hci_dev *hdev) + if (ll_privacy_enabled(hdev)) + settings |= MGMT_SETTING_LL_PRIVACY; + ++ if (past_sender_enabled(hdev)) ++ settings |= MGMT_SETTING_PAST_SENDER; ++ ++ if (past_receiver_enabled(hdev)) ++ settings |= MGMT_SETTING_PAST_RECEIVER; ++ + return settings; + } + +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch b/queue-6.18/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch new file mode 100644 index 0000000000..c7375c885d --- /dev/null +++ b/queue-6.18/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch @@ -0,0 +1,55 @@ +From 6e4b00bb5110ee13409ae2b9380b26a0fc0293e2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:17 +0300 +Subject: Bluetooth: hci_conn: hold conn reference in abort_conn_sync() + +From: Pauli Virtanen + +[ Upstream commit 5761d003daa987ac81463f570713ce9c9dd204e5 ] + +There is theoretical UAF if the conn is freed while the hci_sync task is +running. + +Hold refcount to avoid that. + +Fixes: 227a0cdf4a02 ("Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_conn.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c +index f485cffe35735..e630d6369a0c3 100644 +--- a/net/bluetooth/hci_conn.c ++++ b/net/bluetooth/hci_conn.c +@@ -2965,6 +2965,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data) + return hci_abort_conn_sync(hdev, conn, conn->abort_reason); + } + ++static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err) ++{ ++ struct hci_conn *conn = data; ++ ++ hci_conn_put(conn); ++} ++ + int hci_abort_conn(struct hci_conn *conn, u8 reason) + { + struct hci_dev *hdev = conn->hdev; +@@ -2990,7 +2997,10 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) + * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does + * already queue its callback on cmd_sync_work. + */ +- err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); ++ err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn), ++ abort_conn_destroy); ++ if (err) ++ hci_conn_put(conn); + return (err == -EEXIST) ? 0 : err; + } + +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch b/queue-6.18/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch new file mode 100644 index 0000000000..e2d93f4b45 --- /dev/null +++ b/queue-6.18/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch @@ -0,0 +1,43 @@ +From a41d539ab4ccff78007239708e9726dadd71e06d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:22 +0300 +Subject: Bluetooth: hci_sync: fix hci_conn_del() use in + hci_le_create_conn_sync + +From: Pauli Virtanen + +[ Upstream commit 2c1e4e00613dfd105f978be2276e5e265801ec9f ] + +hci_conn_del() caller must hold hdev->lock, check the conn was not +concurrently deleted, and usually inform socket the conn is going to be +deleted. + +Use hci_abort_conn_sync() instead of calling hci_conn_del() without +locks etc. + +Fixes: 8e8b92ee60de5 ("Bluetooth: hci_sync: Add hci_le_create_conn_sync") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_sync.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index ca002269306c4..27c5cc1457e65 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -6620,7 +6620,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + if (hci_dev_test_flag(hdev, HCI_LE_SCAN) && + hdev->le_scan_type == LE_SCAN_ACTIVE && + !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) { +- hci_conn_del(conn); ++ conn->state = BT_OPEN; ++ hci_abort_conn_sync(hdev, conn, ++ HCI_ERROR_REJ_LIMITED_RESOURCES); + hci_conn_put(conn); + return -EBUSY; + } +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch b/queue-6.18/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch new file mode 100644 index 0000000000..c86e89818c --- /dev/null +++ b/queue-6.18/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch @@ -0,0 +1,66 @@ +From c2de8c52056eba0decf5453412579e887198315a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 25 Mar 2026 21:07:45 +0200 +Subject: Bluetooth: hci_sync: make hci_cmd_sync_run_once return -EEXIST if + exists + +From: Pauli Virtanen + +[ Upstream commit d288f4db0909c22342eb50cd1632b4d850517281 ] + +hci_cmd_sync_run_once() needs to indicate whether a queue item was +added, so caller can know if callbacks are called, so it can avoid +leaking resources. + +Change the function to return -EEXIST if queue item already exists. + +Modify all callsites vs. the changes. The only callsite is +hci_abort_conn(). + +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: 5761d003daa9 ("Bluetooth: hci_conn: hold conn reference in abort_conn_sync()") +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_conn.c | 4 +++- + net/bluetooth/hci_sync.c | 2 +- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c +index ceeb19158a36c..f485cffe35735 100644 +--- a/net/bluetooth/hci_conn.c ++++ b/net/bluetooth/hci_conn.c +@@ -2968,6 +2968,7 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data) + int hci_abort_conn(struct hci_conn *conn, u8 reason) + { + struct hci_dev *hdev = conn->hdev; ++ int err; + + /* If abort_reason has already been set it means the connection is + * already being aborted so don't attempt to overwrite it. +@@ -2989,7 +2990,8 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) + * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does + * already queue its callback on cmd_sync_work. + */ +- return hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); ++ err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); ++ return (err == -EEXIST) ? 0 : err; + } + + void hci_setup_tx_timestamp(struct sk_buff *skb, size_t key_offset, +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 87a2502fccd6e..ca002269306c4 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -825,7 +825,7 @@ int hci_cmd_sync_run_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, + void *data, hci_cmd_sync_work_destroy_t destroy) + { + if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy)) +- return 0; ++ return -EEXIST; + + return hci_cmd_sync_run(hdev, func, data, destroy); + } +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch b/queue-6.18/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch new file mode 100644 index 0000000000..6494bb307c --- /dev/null +++ b/queue-6.18/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch @@ -0,0 +1,80 @@ +From d2824fa429d31c0fe8d1ceb8d348496cc4bd95da Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:23 +0300 +Subject: Bluetooth: hci_sync: remove unnecessary hci_conn_get in + create_conn_sync + +From: Pauli Virtanen + +[ Upstream commit c0a9dcd2be398eee505d4b254ec3a845aa8ab189 ] + +hci_conn_get() without already held reference is data race against +concurrent deletion. + +In previous patches, the refcount has been changed to be taken before +starting the hci_sync task, so remove these extra get() + put() as they +are not needed. + +Fixes: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_sync.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 27c5cc1457e65..1ba41e82c406e 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -6604,11 +6604,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + + bt_dev_dbg(hdev, "conn %p", conn); + +- /* Hold a reference so conn stays valid for the HCI_CONN_CREATE +- * clear_bit() at done. +- */ +- hci_conn_get(conn); +- + clear_bit(HCI_CONN_SCANNING, &conn->flags); + conn->state = BT_CONNECT; + +@@ -6623,7 +6618,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + conn->state = BT_OPEN; + hci_abort_conn_sync(hdev, conn, + HCI_ERROR_REJ_LIMITED_RESOURCES); +- hci_conn_put(conn); + return -EBUSY; + } + +@@ -6717,7 +6711,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + + /* Re-enable advertising after the connection attempt is finished. */ + hci_resume_advertising_sync(hdev); +- hci_conn_put(conn); + return err; + } + +@@ -6992,11 +6985,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) + else + cp.role_switch = 0x00; + +- /* Hold a reference so conn stays valid for the HCI_CONN_CREATE +- * clear_bit() below. +- */ +- hci_conn_get(conn); +- + /* Mark create connection in flight so hci_cancel_connect_sync() can + * cancel it while blocking on the connection complete event. + */ +@@ -7008,7 +6996,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) + conn->conn_timeout, NULL); + + clear_bit(HCI_CONN_CREATE, &conn->flags); +- hci_conn_put(conn); + + return err; + } +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch b/queue-6.18/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch new file mode 100644 index 0000000000..ec263a4962 --- /dev/null +++ b/queue-6.18/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch @@ -0,0 +1,188 @@ +From 3b60a2030df61f582406ed93bc0efa1445cb74ed Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:31 +0300 +Subject: Bluetooth: ISO: avoid deadlocks in iso_sock_timeout + +From: Pauli Virtanen + +[ Upstream commit 200fa1629c57a3ca2b03d3ca63fd3a9bfd910c43 ] + +iso_sock_timeout() takes lock_sock, so sync disabling the timer while +holding that lock may deadlock. + +iso_sock_timeout() may also run concurrently with iso_conn_del(), which +leads to UAF + + [Task 1] [Task hdev->workqueue] + iso_sock_timeout iso_conn_del + iso_conn_hold_unless_zero iso_chan_del + `------------> iso_conn_put + caller frees hcon + iso_conn_put + iso_conn_free + conn->hcon->iso_data = NULL; /* UAF */ + +Fix the deadlock by removing the disable from the lock_sock sections. +Move the timer from iso_conn to iso_pinfo to decouple it from iso_conn +which may need to be freed in lock_sock section. Convert some of the +clear_timer to disable_timer. + +Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 60 ++++++++++++++++++++++----------------------- + 1 file changed, 29 insertions(+), 31 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 001fb12ccee3b..3320be2d66b55 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -36,8 +36,6 @@ struct iso_conn { + spinlock_t lock; + struct sock *sk; + +- struct delayed_work timeout_work; +- + struct sk_buff *rx_skb; + __u32 rx_len; + __u16 tx_sn; +@@ -80,6 +78,7 @@ struct iso_pinfo { + __u8 base_len; + __u8 base[BASE_MAX_LENGTH]; + struct iso_conn *conn; ++ struct delayed_work timeout_work; + }; + + static struct bt_iso_qos default_qos; +@@ -117,9 +116,6 @@ static void iso_conn_free(struct kref *ref) + hci_conn_drop(conn->hcon); + } + +- /* Ensure no more work items will run since hci_conn has been dropped */ +- disable_delayed_work_sync(&conn->timeout_work); +- + kfree_skb(conn->rx_skb); + + kfree(conn); +@@ -160,48 +156,45 @@ static struct sock *iso_sock_hold(struct iso_conn *conn) + + static void iso_sock_timeout(struct work_struct *work) + { +- struct iso_conn *conn = container_of(work, struct iso_conn, +- timeout_work.work); +- struct sock *sk; +- +- conn = iso_conn_hold_unless_zero(conn); +- if (!conn) +- return; +- +- iso_conn_lock(conn); +- sk = iso_sock_hold(conn); +- iso_conn_unlock(conn); +- iso_conn_put(conn); +- +- if (!sk) +- return; ++ struct iso_pinfo *pi = container_of(work, struct iso_pinfo, ++ timeout_work.work); ++ struct sock *sk = &pi->bt.sk; + + BT_DBG("sock %p state %d", sk, sk->sk_state); + + lock_sock(sk); +- sk->sk_err = ETIMEDOUT; +- sk->sk_state_change(sk); ++ if (!sock_flag(sk, SOCK_ZAPPED)) { ++ sk->sk_err = ETIMEDOUT; ++ sk->sk_state_change(sk); ++ } + release_sock(sk); +- sock_put(sk); + } + + static void iso_sock_set_timer(struct sock *sk, long timeout) + { ++ lockdep_assert(lockdep_sock_is_held(sk)); ++ ++ cancel_delayed_work(&iso_pi(sk)->timeout_work); ++ + if (!iso_pi(sk)->conn) + return; + + BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout); +- cancel_delayed_work(&iso_pi(sk)->conn->timeout_work); +- schedule_delayed_work(&iso_pi(sk)->conn->timeout_work, timeout); ++ schedule_delayed_work(&iso_pi(sk)->timeout_work, timeout); + } + + static void iso_sock_clear_timer(struct sock *sk) + { +- if (!iso_pi(sk)->conn) +- return; ++ BT_DBG("sock %p state %d", sk, sk->sk_state); ++ cancel_delayed_work(&iso_pi(sk)->timeout_work); ++} ++ ++static void iso_sock_disable_timer(struct sock *sk) ++{ ++ lockdep_assert(!lockdep_sock_is_held(sk)); + + BT_DBG("sock %p state %d", sk, sk->sk_state); +- cancel_delayed_work(&iso_pi(sk)->conn->timeout_work); ++ disable_delayed_work_sync(&iso_pi(sk)->timeout_work); + } + + /* ---- ISO connections ---- */ +@@ -226,7 +219,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon) + + kref_init(&conn->ref); + spin_lock_init(&conn->lock); +- INIT_DELAYED_WORK(&conn->timeout_work, iso_sock_timeout); + + hcon->iso_data = conn; + conn->hcon = hcon; +@@ -291,8 +283,9 @@ static void iso_conn_del(struct hci_conn *hcon, int err) + return; + } + ++ iso_sock_disable_timer(sk); ++ + lock_sock(sk); +- iso_sock_clear_timer(sk); + iso_chan_del(sk, err); + release_sock(sk); + iso_sock_kill(sk); +@@ -782,6 +775,8 @@ static void iso_sock_cleanup_listen(struct sock *parent) + */ + static void iso_sock_kill(struct sock *sk) + { ++ iso_sock_disable_timer(sk); ++ + lock_sock(sk); + + if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || +@@ -877,8 +872,9 @@ static void __iso_sock_close(struct sock *sk) + /* Must be called on unlocked socket. */ + static void iso_sock_close(struct sock *sk) + { ++ iso_sock_disable_timer(sk); ++ + lock_sock(sk); +- iso_sock_clear_timer(sk); + __iso_sock_close(sk); + release_sock(sk); + } +@@ -947,6 +943,8 @@ static struct sock *iso_sock_alloc(struct net *net, struct socket *sock, + iso_pi(sk)->qos = default_qos; + iso_pi(sk)->sync_handle = -1; + ++ INIT_DELAYED_WORK(&iso_pi(sk)->timeout_work, iso_sock_timeout); ++ + bt_sock_link(&iso_sk_list, sk); + return sk; + } +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch b/queue-6.18/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch new file mode 100644 index 0000000000..609bfdd95e --- /dev/null +++ b/queue-6.18/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch @@ -0,0 +1,40 @@ +From 243515e681f93ae1ac3bd47fcc6f090284080f66 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 17:53:33 +0300 +Subject: Bluetooth: ISO: clear iso_data always when detaching conn from hcon + +From: Pauli Virtanen + +[ Upstream commit d57e506f6a1e3929611340fae87c1e4823f4d85c ] + +When setting conn->hcon = NULL, also conn->hcon->iso_data = NULL is +necessary, otherwise later iso_conn_free() will UAF. + +Fix clearing of iso_data in iso_sock_disconn() + +Fixes KASAN: slab-use-after-free in iso_conn_hold_unless_zero on +iso_sock_release() followed by hci_abort_conn_sync(). + +Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index f2ff10ae76e22..5868ad1f7128e 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -826,6 +826,7 @@ static void iso_sock_disconn(struct sock *sk) + sk->sk_state = BT_DISCONN; + iso_conn_lock(iso_pi(sk)->conn); + hci_conn_drop(iso_pi(sk)->conn->hcon); ++ iso_pi(sk)->conn->hcon->iso_data = NULL; + iso_pi(sk)->conn->hcon = NULL; + iso_conn_unlock(iso_pi(sk)->conn); + } +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch b/queue-6.18/bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch new file mode 100644 index 0000000000..4d7d1bf692 --- /dev/null +++ b/queue-6.18/bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch @@ -0,0 +1,109 @@ +From 1a5b060db1b91ead2015af719ea79075f4dabd7b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:32 +0300 +Subject: Bluetooth: ISO: ensure no dangling hcon references in iso_conn + +From: Pauli Virtanen + +[ Upstream commit aa9f7cb2bd3a2be998ceb739fc9a2f986eba43eb ] + +After iso_conn_del(), ISO sockets should not dereference the hcon any +more. Currently, clearing iso_conn::hcon relies on iso_conn_del() +releasing the last reference to the iso_conn. + +Simplify this by explicitly clearing conn->hcon in iso_conn_del(), to +avoid more complex reasoning on races about who holds the last +reference. + +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: fdfde532ab1c ("Bluetooth: ISO: fix refcounting of iso_conn") +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 24 +++++++++++++++++++++--- + 1 file changed, 21 insertions(+), 3 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 3320be2d66b55..b21627b8e958b 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -262,6 +262,7 @@ static void iso_chan_del(struct sock *sk, int err) + } + + static void iso_conn_del(struct hci_conn *hcon, int err) ++ __must_hold(&hcon->hdev->lock) + { + struct iso_conn *conn = hcon->iso_data; + struct sock *sk; +@@ -276,11 +277,10 @@ static void iso_conn_del(struct hci_conn *hcon, int err) + iso_conn_lock(conn); + sk = iso_sock_hold(conn); + iso_conn_unlock(conn); +- iso_conn_put(conn); + + if (!sk) { + iso_conn_put(conn); +- return; ++ goto done; + } + + iso_sock_disable_timer(sk); +@@ -290,6 +290,15 @@ static void iso_conn_del(struct hci_conn *hcon, int err) + release_sock(sk); + iso_sock_kill(sk); + sock_put(sk); ++ ++done: ++ /* No sk access to conn->hcon any more (lock_sock + hdev->lock) */ ++ iso_conn_lock(conn); ++ conn->hcon = NULL; ++ hcon->iso_data = NULL; ++ iso_conn_unlock(conn); ++ ++ iso_conn_put(conn); + } + + static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, +@@ -305,6 +314,11 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, + return -EBUSY; + } + ++ if (!conn->hcon) { ++ BT_ERR("conn->hcon missing"); ++ return -EIO; ++ } ++ + iso_pi(sk)->conn = conn; + conn->sk = sk; + clear_bit(ISO_CONN_DROPPED, conn->flags); +@@ -2410,6 +2424,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + } + + static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) ++ __must_hold(&hcon->hdev->lock) + { + if (hcon->type != CIS_LINK && hcon->type != BIS_LINK && + hcon->type != PA_LINK) { +@@ -2421,8 +2436,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) + struct hci_link *link, *t; + + list_for_each_entry_safe(link, t, &hcon->link_list, +- list) ++ list) { ++ lockdep_assert_held(&link->conn->hdev->lock); + iso_conn_del(link->conn, bt_to_errno(status)); ++ } + + return; + } +@@ -2452,6 +2469,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) + } + + static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason) ++ __must_hold(&hcon->hdev->lock) + { + if (hcon->type != CIS_LINK && hcon->type != BIS_LINK && + hcon->type != PA_LINK) +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-iso-fix-connected-closed-transition-on-shu.patch b/queue-6.18/bluetooth-iso-fix-connected-closed-transition-on-shu.patch new file mode 100644 index 0000000000..7fcd139530 --- /dev/null +++ b/queue-6.18/bluetooth-iso-fix-connected-closed-transition-on-shu.patch @@ -0,0 +1,108 @@ +From 4721856be86871cdf6640d7ef4dfa82c7e0e95ea Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:24 +0300 +Subject: Bluetooth: ISO: fix CONNECTED -> CLOSED transition on + shutdown/release + +From: Pauli Virtanen + +[ Upstream commit 0786469ee242952008628ed0e2d386098e2065ab ] + +Commit d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon") +merged a version of the UAF fix that breaks releasing connected +ISO sockets. Since hci_conn::iso_data is set to NULL, iso_chan_del() won't +be called when the hci_conn disconnects, and the ISO socket does not emit +POLLHUP correctly. + +Fix by retaining full hci_conn <-> iso_conn association while in +BT_DISCONNECT state, so that local disconnect via shutdown() follows +similar ISO socket code path as remote disconnect. Use a separate flag +to track whether hci_conn_drop() is needed, instead of setting +iso_conn::hcon = NULL + +In iso_sock_ready(), disallow disconnecting socket going BT_CONNECTED, +in case hcon connects while its drop is pending. + +Fixes: d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon") +Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: 0d255e63fcf3 ("Bluetooth: ISO: hold sk properly in iso_conn_ready") +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 26 ++++++++++++++++++++------ + 1 file changed, 20 insertions(+), 6 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 0d2d9c07bf66b..fb77a7e22a9dd 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -23,8 +23,14 @@ static struct bt_sock_list iso_sk_list = { + }; + + /* ---- ISO connections ---- */ ++enum { ++ ISO_CONN_DROPPED, ++ __ISO_CONN_NUM_FLAGS ++}; ++ + struct iso_conn { + struct hci_conn *hcon; ++ DECLARE_BITMAP(flags, __ISO_CONN_NUM_FLAGS); + + /* @lock: spinlock protecting changes to iso_conn fields */ + spinlock_t lock; +@@ -106,7 +112,8 @@ static void iso_conn_free(struct kref *ref) + + if (conn->hcon) { + conn->hcon->iso_data = NULL; +- hci_conn_drop(conn->hcon); ++ if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags)) ++ hci_conn_drop(conn->hcon); + } + + /* Ensure no more work items will run since hci_conn has been dropped */ +@@ -305,6 +312,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, + + iso_pi(sk)->conn = conn; + conn->sk = sk; ++ clear_bit(ISO_CONN_DROPPED, conn->flags); + + if (parent) + bt_accept_enqueue(parent, sk, true); +@@ -817,11 +825,8 @@ static void iso_sock_disconn(struct sock *sk) + } + + sk->sk_state = BT_DISCONN; +- iso_conn_lock(iso_pi(sk)->conn); +- hci_conn_drop(iso_pi(sk)->conn->hcon); +- iso_pi(sk)->conn->hcon->iso_data = NULL; +- iso_pi(sk)->conn->hcon = NULL; +- iso_conn_unlock(iso_pi(sk)->conn); ++ if (!test_and_set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags)) ++ hci_conn_drop(iso_pi(sk)->conn->hcon); + } + + static void __iso_sock_close(struct sock *sk) +@@ -1964,9 +1969,18 @@ static void iso_sock_ready(struct sock *sk) + return; + + lock_sock(sk); ++ ++ switch (sk->sk_state) { ++ case BT_DISCONN: ++ case BT_CLOSED: ++ release_sock(sk); ++ return; ++ } ++ + iso_sock_clear_timer(sk); + sk->sk_state = BT_CONNECTED; + sk->sk_state_change(sk); ++ + release_sock(sk); + } + +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-iso-fix-data-race-on-iso_pi-sk-in-socket-a.patch b/queue-6.18/bluetooth-iso-fix-data-race-on-iso_pi-sk-in-socket-a.patch new file mode 100644 index 0000000000..ad5f03f7ca --- /dev/null +++ b/queue-6.18/bluetooth-iso-fix-data-race-on-iso_pi-sk-in-socket-a.patch @@ -0,0 +1,227 @@ +From d6b822f0cbc1d4af1b4dc35de9a7012fca6d7837 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Apr 2026 11:51:22 +0900 +Subject: Bluetooth: ISO: Fix data-race on iso_pi(sk) in socket and HCI event + paths + +From: SeungJu Cheon + +[ Upstream commit f958c7805b18e9d69f6b322b231ecee46ec6f331 ] + +Several iso_pi(sk) fields (qos, qos_user_set, bc_sid, base, base_len, +sync_handle, bc_num_bis) are written under lock_sock in +iso_sock_setsockopt() and iso_sock_bind(), but read and written under +hci_dev_lock only in two other paths: + + - iso_connect_bis() / iso_connect_cis(), invoked from connect(2), + read qos/base/bc_sid and reset qos to default_qos on the + qos_user_set validation failure -- all without lock_sock. + + - iso_connect_ind(), invoked from hci_rx_work, writes sync_handle, + bc_sid, qos.bcast.encryption, bc_num_bis, base and base_len on + PA_SYNC_ESTABLISHED / PAST_RECEIVED / BIG_INFO_ADV_REPORT / + PER_ADV_REPORT events. The BIG_INFO handler additionally passes + &iso_pi(sk)->qos together with sync_handle / bc_num_bis / bc_bis + to hci_conn_big_create_sync() while setsockopt may be mutating + them. + +Acquire lock_sock around the affected accesses in both paths. + +The locking order hci_dev_lock -> lock_sock matches the existing +iso_conn_big_sync() precedent, whose comment documents the same +requirement for hci_conn_big_create_sync(). The HCI connect/bind +helpers do not wait for command completion -- they enqueue work via +hci_cmd_sync_queue{,_once}() / hci_le_create_cis_pending() and +return -- so the added hold time is comparable to iso_conn_big_sync(). + +KCSAN report: + +BUG: KCSAN: data-race in iso_connect_cis / iso_sock_setsockopt + +read to 0xffffa3ae8ce3cdc8 of 1 bytes by task 335 on cpu 0: + iso_connect_cis+0x49f/0xa20 + iso_sock_connect+0x60e/0xb40 + __sys_connect_file+0xbd/0xe0 + __sys_connect+0xe0/0x110 + __x64_sys_connect+0x40/0x50 + x64_sys_call+0xcad/0x1c60 + do_syscall_64+0x133/0x590 + entry_SYSCALL_64_after_hwframe+0x77/0x7f + +write to 0xffffa3ae8ce3cdc8 of 60 bytes by task 334 on cpu 1: + iso_sock_setsockopt+0x69a/0x930 + do_sock_setsockopt+0xc3/0x170 + __sys_setsockopt+0xd1/0x130 + __x64_sys_setsockopt+0x64/0x80 + x64_sys_call+0x1547/0x1c60 + do_syscall_64+0x133/0x590 + entry_SYSCALL_64_after_hwframe+0x77/0x7f + +Reported by Kernel Concurrency Sanitizer on: +CPU: 1 UID: 0 PID: 334 Comm: iso_setup_race Not tainted 7.0.0-10949-g8541d8f725c6 #44 PREEMPT(lazy) + +The iso_connect_ind() races were found by inspection. + +Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type") +Signed-off-by: SeungJu Cheon +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: 4311fd6f4290 ("Bluetooth: ISO: lock sk in iso_connect_ind") +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 54 +++++++++++++++++++++++++-------------------- + 1 file changed, 30 insertions(+), 24 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index f65e77f7e0349..4377fe6cf14df 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -355,6 +355,7 @@ static int iso_connect_bis(struct sock *sk) + return -EHOSTUNREACH; + + hci_dev_lock(hdev); ++ lock_sock(sk); + + if (!bis_capable(hdev)) { + err = -EOPNOTSUPP; +@@ -407,13 +408,9 @@ static int iso_connect_bis(struct sock *sk) + goto unlock; + } + +- lock_sock(sk); +- + err = iso_chan_add(conn, sk, NULL); +- if (err) { +- release_sock(sk); ++ if (err) + goto unlock; +- } + + /* Update source addr of the socket */ + bacpy(&iso_pi(sk)->src, &hcon->src); +@@ -429,9 +426,8 @@ static int iso_connect_bis(struct sock *sk) + iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo)); + } + +- release_sock(sk); +- + unlock: ++ release_sock(sk); + hci_dev_unlock(hdev); + hci_dev_put(hdev); + return err; +@@ -459,6 +455,7 @@ static int iso_connect_cis(struct sock *sk) + return -EHOSTUNREACH; + + hci_dev_lock(hdev); ++ lock_sock(sk); + + if (!cis_central_capable(hdev)) { + err = -EOPNOTSUPP; +@@ -513,13 +510,9 @@ static int iso_connect_cis(struct sock *sk) + goto unlock; + } + +- lock_sock(sk); +- + err = iso_chan_add(conn, sk, NULL); +- if (err) { +- release_sock(sk); ++ if (err) + goto unlock; +- } + + /* Update source addr of the socket */ + bacpy(&iso_pi(sk)->src, &hcon->src); +@@ -535,9 +528,8 @@ static int iso_connect_cis(struct sock *sk) + iso_sock_set_timer(sk, READ_ONCE(sk->sk_sndtimeo)); + } + +- release_sock(sk); +- + unlock: ++ release_sock(sk); + hci_dev_unlock(hdev); + hci_dev_put(hdev); + return err; +@@ -2204,8 +2196,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN, + iso_match_sid, ev1); + if (sk && !ev1->status) { ++ lock_sock(sk); + iso_pi(sk)->sync_handle = le16_to_cpu(ev1->handle); + iso_pi(sk)->bc_sid = ev1->sid; ++ release_sock(sk); + } + + goto done; +@@ -2216,8 +2210,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN, + iso_match_sid_past, ev1a); + if (sk && !ev1a->status) { ++ lock_sock(sk); + iso_pi(sk)->sync_handle = le16_to_cpu(ev1a->sync_handle); + iso_pi(sk)->bc_sid = ev1a->sid; ++ release_sock(sk); + } + + goto done; +@@ -2244,27 +2240,35 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + ev2); + + if (sk) { +- int err; +- struct hci_conn *hcon = iso_pi(sk)->conn->hcon; ++ int err = 0; ++ bool big_sync; ++ struct hci_conn *hcon; + ++ lock_sock(sk); ++ ++ hcon = iso_pi(sk)->conn->hcon; + iso_pi(sk)->qos.bcast.encryption = ev2->encryption; + + if (ev2->num_bis < iso_pi(sk)->bc_num_bis) + iso_pi(sk)->bc_num_bis = ev2->num_bis; + +- if (!test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) && +- !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) { ++ big_sync = !test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags) && ++ !test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags); ++ ++ if (big_sync) + err = hci_conn_big_create_sync(hdev, hcon, + &iso_pi(sk)->qos, + iso_pi(sk)->sync_handle, + iso_pi(sk)->bc_num_bis, + iso_pi(sk)->bc_bis); +- if (err) { +- bt_dev_err(hdev, "hci_le_big_create_sync: %d", +- err); +- sock_put(sk); +- sk = NULL; +- } ++ ++ release_sock(sk); ++ ++ if (big_sync && err) { ++ bt_dev_err(hdev, "hci_le_big_create_sync: %d", ++ err); ++ sock_put(sk); ++ sk = NULL; + } + } + +@@ -2318,8 +2322,10 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + if (!base || base_len > BASE_MAX_LENGTH) + goto done; + ++ lock_sock(sk); + memcpy(iso_pi(sk)->base, base, base_len); + iso_pi(sk)->base_len = base_len; ++ release_sock(sk); + } else { + /* This is a PA data fragment. Keep pa_data_len set to 0 + * until all data has been reassembled. +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-iso-fix-leaking-sk-after-socket-release.patch b/queue-6.18/bluetooth-iso-fix-leaking-sk-after-socket-release.patch new file mode 100644 index 0000000000..6bbb1e6afa --- /dev/null +++ b/queue-6.18/bluetooth-iso-fix-leaking-sk-after-socket-release.patch @@ -0,0 +1,116 @@ +From b52a0101085ee70fb5dac9dca65c38f4352e777a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:30 +0300 +Subject: Bluetooth: ISO: fix leaking sk after socket release + +From: Pauli Virtanen + +[ Upstream commit ce57442a379212fe3fda59c9437ee8217eceb5b1 ] + +iso_sock_kill() tests !sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || +sock_flag(sk, SOCK_DEAD) for early return, but this is always true since +sock_orphan(sk) sets SOCK_DEAD, so the sk reference released by socket +always leaks, iso_sock_destruct is never called. + +The socket reference also leaks when __iso_sock_close() does not set +SOCK_ZAPPED, since iso_conn_del() does not call iso_sock_kill() after +zapping. + +Fix by replacing SOCK_DEAD by BT_SK_KILLED flag that is not used for +something else, and lock_sock to ensure iso_sock_kill() puts sk only +after socket release only once. Release and iso_conn_del may run +concurrently. Call iso_sock_kill() from iso_conn_del() to clean sk up +after zapping. + +Remove call to iso_sock_kill() from iso_sock_close(), as it's generally +no-op there. + +Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 22 ++++++++++++++++++---- + 1 file changed, 18 insertions(+), 4 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index cb629734062a5..001fb12ccee3b 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -61,6 +61,7 @@ static void iso_sock_kill(struct sock *sk); + enum { + BT_SK_BIG_SYNC, + BT_SK_PA_SYNC, ++ BT_SK_KILLED, + }; + + struct iso_pinfo { +@@ -294,6 +295,7 @@ static void iso_conn_del(struct hci_conn *hcon, int err) + iso_sock_clear_timer(sk); + iso_chan_del(sk, err); + release_sock(sk); ++ iso_sock_kill(sk); + sock_put(sk); + } + +@@ -780,24 +782,29 @@ static void iso_sock_cleanup_listen(struct sock *parent) + */ + static void iso_sock_kill(struct sock *sk) + { ++ lock_sock(sk); ++ + if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || +- sock_flag(sk, SOCK_DEAD)) ++ test_bit(BT_SK_KILLED, &iso_pi(sk)->flags)) { ++ release_sock(sk); + return; ++ } + + BT_DBG("sk %p state %d", sk, sk->sk_state); + + /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */ +- lock_sock(sk); + if (iso_pi(sk)->conn) { + iso_conn_lock(iso_pi(sk)->conn); + iso_pi(sk)->conn->sk = NULL; + iso_conn_unlock(iso_pi(sk)->conn); + } +- release_sock(sk); + + /* Kill poor orphan */ + bt_sock_unlink(&iso_sk_list, sk); + sock_set_flag(sk, SOCK_DEAD); ++ set_bit(BT_SK_KILLED, &iso_pi(sk)->flags); ++ ++ release_sock(sk); + sock_put(sk); + } + +@@ -874,7 +881,6 @@ static void iso_sock_close(struct sock *sk) + iso_sock_clear_timer(sk); + __iso_sock_close(sk); + release_sock(sk); +- iso_sock_kill(sk); + } + + static void iso_sock_init(struct sock *sk, struct sock *parent) +@@ -1958,8 +1964,16 @@ static int iso_sock_release(struct socket *sock) + release_sock(sk); + } + ++ /* Make sure sk is valid even if iso_conn_del() is concurrent */ ++ sock_hold(sk); ++ ++ lock_sock(sk); + sock_orphan(sk); ++ release_sock(sk); ++ + iso_sock_kill(sk); ++ ++ sock_put(sk); + return err; + } + +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-iso-fix-not-updating-bis-sender-source-add.patch b/queue-6.18/bluetooth-iso-fix-not-updating-bis-sender-source-add.patch new file mode 100644 index 0000000000..e09cabb1a7 --- /dev/null +++ b/queue-6.18/bluetooth-iso-fix-not-updating-bis-sender-source-add.patch @@ -0,0 +1,53 @@ +From fa25ef28917ca79979482d1bb9e9a99f7d4bf592 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 6 Oct 2025 16:53:35 -0400 +Subject: Bluetooth: ISO: Fix not updating BIS sender source address + +From: Luiz Augusto von Dentz + +[ Upstream commit 577cf4c0a1e8471a0d6c0f36bb3716285e27ad5e ] + +The source address for a BIS sender/Broadcast Source shall be updated +with the advertisement address since in case privacy is enabled it may +use an RPA rather than an identity address. + +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: 0d255e63fcf3 ("Bluetooth: ISO: hold sk properly in iso_conn_ready") +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 98fa94644b4a6..0d2d9c07bf66b 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -2001,6 +2001,25 @@ static void iso_conn_ready(struct iso_conn *conn) + BT_DBG("conn %p", conn); + + if (sk) { ++ /* Attempt to update source address in case of BIS Sender if ++ * the advertisement is using a random address. ++ */ ++ if (conn->hcon->type == BIS_LINK && ++ conn->hcon->role == HCI_ROLE_MASTER && ++ !bacmp(&conn->hcon->dst, BDADDR_ANY)) { ++ struct hci_conn *bis = conn->hcon; ++ struct adv_info *adv; ++ ++ adv = hci_find_adv_instance(bis->hdev, ++ bis->iso_qos.bcast.bis); ++ if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) { ++ lock_sock(sk); ++ iso_pi(sk)->src_type = BDADDR_LE_RANDOM; ++ bacpy(&iso_pi(sk)->src, &adv->random_addr); ++ release_sock(sk); ++ } ++ } ++ + iso_sock_ready(conn->sk); + } else { + hcon = conn->hcon; +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-iso-fix-refcounting-of-iso_conn.patch b/queue-6.18/bluetooth-iso-fix-refcounting-of-iso_conn.patch new file mode 100644 index 0000000000..14f137888c --- /dev/null +++ b/queue-6.18/bluetooth-iso-fix-refcounting-of-iso_conn.patch @@ -0,0 +1,142 @@ +From ef216fe405fd74f09003255b94ddc009af2786ed Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:33 +0300 +Subject: Bluetooth: ISO: fix refcounting of iso_conn +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Pauli Virtanen + +[ Upstream commit fdfde532ab1caa165fcd8985001157ac8b4db365 ] + +iso_conn_del() and iso_chan_del() have a race that results to double-put +of iso_conn: + + [Task hdev->workqueue] [Task 2] + iso_conn_del iso_chan_del + iso_conn_hold_unless_zero iso_conn_lock + iso_conn_lock conn->sk = NULL + iso_conn_unlock + sk = iso_sock_hold(conn) <---------´ + if (!sk) iso_conn_put iso_conn_put + iso_conn_put /* UAF */ + +The extra put for !sk in iso_conn_del() is currently required since +failing iso_chan_add() may leave iso_conn not associated with any sk. + +Fix by having iso_pi(sk)->conn own refcount when non-NULL, so +iso_conn_del does not need to put it. Adjust the iso_conn_add() +refcounting so that conn is put if it does not get associated with an +sk. + +Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 29 +++++++++++++++++------------ + 1 file changed, 17 insertions(+), 12 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index b21627b8e958b..fcf4fd78c7cc9 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -107,9 +107,6 @@ static void iso_conn_free(struct kref *ref) + + BT_DBG("conn %p", conn); + +- if (conn->sk) +- iso_pi(conn->sk)->conn = NULL; +- + if (conn->hcon) { + conn->hcon->iso_data = NULL; + if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags)) +@@ -144,6 +141,14 @@ static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn) + return conn; + } + ++static struct iso_conn *iso_conn_hold(struct iso_conn *conn) ++{ ++ BT_DBG("conn %p refcnt %u", conn, kref_read(&conn->ref)); ++ ++ kref_get(&conn->ref); ++ return conn; ++} ++ + static struct sock *iso_sock_hold(struct iso_conn *conn) + { + if (!conn || !bt_sock_linked(&iso_sk_list, conn->sk)) +@@ -209,7 +214,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon) + conn->hcon = hcon; + iso_conn_unlock(conn); + } +- iso_conn_put(conn); + return conn; + } + +@@ -278,10 +282,8 @@ static void iso_conn_del(struct hci_conn *hcon, int err) + sk = iso_sock_hold(conn); + iso_conn_unlock(conn); + +- if (!sk) { +- iso_conn_put(conn); ++ if (!sk) + goto done; +- } + + iso_sock_disable_timer(sk); + +@@ -319,7 +321,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, + return -EIO; + } + +- iso_pi(sk)->conn = conn; ++ iso_pi(sk)->conn = iso_conn_hold(conn); + conn->sk = sk; + clear_bit(ISO_CONN_DROPPED, conn->flags); + +@@ -426,6 +428,7 @@ static int iso_connect_bis(struct sock *sk) + } + + err = iso_chan_add(conn, sk, NULL); ++ iso_conn_put(conn); + if (err) + goto unlock; + +@@ -528,6 +531,7 @@ static int iso_connect_cis(struct sock *sk) + } + + err = iso_chan_add(conn, sk, NULL); ++ iso_conn_put(conn); + if (err) + goto unlock; + +@@ -1228,10 +1232,9 @@ static int iso_listen_bis(struct sock *sk) + } + + err = iso_chan_add(conn, sk, NULL); +- if (err) { +- hci_conn_drop(hcon); ++ iso_conn_put(conn); ++ if (err) + goto unlock; +- } + + unlock: + release_sock(sk); +@@ -2461,8 +2464,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) + struct iso_conn *conn; + + conn = iso_conn_add(hcon); +- if (conn) ++ if (conn) { + iso_conn_ready(conn); ++ iso_conn_put(conn); ++ } + } else { + iso_conn_del(hcon, bt_to_errno(status)); + } +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch b/queue-6.18/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch new file mode 100644 index 0000000000..d6681883db --- /dev/null +++ b/queue-6.18/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch @@ -0,0 +1,38 @@ +From 7ad37a986d0565e8ccae79a05148c96297b4bc1f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:27 +0300 +Subject: Bluetooth: ISO: fix timeout vs sync_timeout typo in check_bcast_qos + +From: Pauli Virtanen + +[ Upstream commit e9cb51813d79fc9aae4a2098aab3ab6ebd7fb6c8 ] + +In iso.c check_bcast_qos(), missing bcast.timeout is not set to its +default value, and appears typoed as bcast.sync_timeout. + +Fix the typo. + +Fixes: b37cab587aa3 ("Bluetooth: ISO: Don't reject BT_ISO_QOS if parameters are unset") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index d667cbf8a1392..a0ef76ecb80b8 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -1709,7 +1709,7 @@ static bool check_bcast_qos(struct bt_iso_qos *qos) + return false; + + if (!qos->bcast.timeout) +- qos->bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT; ++ qos->bcast.timeout = BT_ISO_SYNC_TIMEOUT; + + if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000) + return false; +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch b/queue-6.18/bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch new file mode 100644 index 0000000000..1d8047176c --- /dev/null +++ b/queue-6.18/bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch @@ -0,0 +1,133 @@ +From 8f6f297417ae19cd148b14ecef9eb5d094c7afbe Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:29 +0300 +Subject: Bluetooth: ISO: hold sk properly in iso_conn_ready + +From: Pauli Virtanen + +[ Upstream commit 0d255e63fcf3f13a570d7ac11678fa1164ac015c ] + +sk deref in iso_conn_ready must be done either under conn->lock, or +holding a refcount, to avoid concurrent close. conn->sk is currently +accessed without either: + + [Task 1] [Task 2] + iso_sock_release + iso_conn_ready + sk = conn->sk + lock_sock(sk) + conn->sk = NULL + lock_sock(sk) + release_sock(sk) + iso_sock_kill(sk) + UAF on sk deref + +Fix possible UAF by holding sk refcount in iso_conn_ready(). Also +recheck after lock_sock that the socket is still valid. Adjust locking +so conn->sk is cleared only under lock_sock. + +Fixes: 27c24fda62b60 ("Bluetooth: switch to lock_sock in SCO") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 34 +++++++++++++++++++++++----------- + 1 file changed, 23 insertions(+), 11 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index fb77a7e22a9dd..cb629734062a5 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -787,11 +787,13 @@ static void iso_sock_kill(struct sock *sk) + BT_DBG("sk %p state %d", sk, sk->sk_state); + + /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */ ++ lock_sock(sk); + if (iso_pi(sk)->conn) { + iso_conn_lock(iso_pi(sk)->conn); + iso_pi(sk)->conn->sk = NULL; + iso_conn_unlock(iso_pi(sk)->conn); + } ++ release_sock(sk); + + /* Kill poor orphan */ + bt_sock_unlink(&iso_sk_list, sk); +@@ -1965,23 +1967,17 @@ static void iso_sock_ready(struct sock *sk) + { + BT_DBG("sk %p", sk); + +- if (!sk) +- return; +- +- lock_sock(sk); ++ lockdep_assert(lockdep_sock_is_held(sk)); + + switch (sk->sk_state) { + case BT_DISCONN: + case BT_CLOSED: +- release_sock(sk); + return; + } + + iso_sock_clear_timer(sk); + sk->sk_state = BT_CONNECTED; + sk->sk_state_change(sk); +- +- release_sock(sk); + } + + static bool iso_match_big(struct sock *sk, void *data) +@@ -2006,7 +2002,7 @@ static bool iso_match_pa_sync_flag(struct sock *sk, void *data) + static void iso_conn_ready(struct iso_conn *conn) + { + struct sock *parent = NULL; +- struct sock *sk = conn->sk; ++ struct sock *sk; + struct hci_ev_le_big_sync_established *ev = NULL; + struct hci_ev_le_pa_sync_established *ev2 = NULL; + struct hci_ev_le_per_adv_report *ev3 = NULL; +@@ -2014,7 +2010,22 @@ static void iso_conn_ready(struct iso_conn *conn) + + BT_DBG("conn %p", conn); + ++ iso_conn_lock(conn); ++ sk = iso_sock_hold(conn); ++ iso_conn_unlock(conn); ++ + if (sk) { ++ lock_sock(sk); ++ ++ /* conn->sk may have become NULL if racing with sk close, but ++ * due to held hdev->lock, it can't become different sk. ++ */ ++ if (!conn->sk) { ++ release_sock(sk); ++ sock_put(sk); ++ return; ++ } ++ + /* Attempt to update source address in case of BIS Sender if + * the advertisement is using a random address. + */ +@@ -2027,14 +2038,15 @@ static void iso_conn_ready(struct iso_conn *conn) + adv = hci_find_adv_instance(bis->hdev, + bis->iso_qos.bcast.bis); + if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) { +- lock_sock(sk); + iso_pi(sk)->src_type = BDADDR_LE_RANDOM; + bacpy(&iso_pi(sk)->src, &adv->random_addr); +- release_sock(sk); + } + } + +- iso_sock_ready(conn->sk); ++ iso_sock_ready(sk); ++ ++ release_sock(sk); ++ sock_put(sk); + } else { + hcon = conn->hcon; + if (!hcon) +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-iso-lock-sk-in-iso_connect_ind.patch b/queue-6.18/bluetooth-iso-lock-sk-in-iso_connect_ind.patch new file mode 100644 index 0000000000..57d97c9828 --- /dev/null +++ b/queue-6.18/bluetooth-iso-lock-sk-in-iso_connect_ind.patch @@ -0,0 +1,92 @@ +From 877218b6c830a40e3810c93260d3ab930db59d95 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:26 +0300 +Subject: Bluetooth: ISO: lock sk in iso_connect_ind + +From: Pauli Virtanen + +[ Upstream commit 4311fd6f429065a8ba208660360a895627a00cf3 ] + +Accessing iso_pi(sk)->conn requires lock_sock, which is not taken in the +"ev3" part of iso_connect_ind. It may also be NULL if socket has +transitioned away from the LISTEN/CONNECT states before locking. + +Fix by adding lock/release. Recheck hcon is valid after lock acquire +where needed. + +Fixes: 168d9bf9c7f0 ("Bluetooth: ISO: Reassemble PA data for bcast sink") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 19 +++++++++++-------- + 1 file changed, 11 insertions(+), 8 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 4377fe6cf14df..d667cbf8a1392 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -2246,7 +2246,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + + lock_sock(sk); + +- hcon = iso_pi(sk)->conn->hcon; ++ hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL; + iso_pi(sk)->qos.bcast.encryption = ev2->encryption; + + if (ev2->num_bis < iso_pi(sk)->bc_num_bis) +@@ -2286,9 +2286,11 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + if (!sk) + goto done; + +- hcon = iso_pi(sk)->conn->hcon; ++ lock_sock(sk); ++ ++ hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL; + if (!hcon) +- goto done; ++ goto release3; + + if (ev3->data_status == LE_PA_DATA_TRUNCATED) { + /* The controller was unable to retrieve PA data. */ +@@ -2296,12 +2298,12 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + HCI_MAX_PER_AD_TOT_LEN); + hcon->le_per_adv_data_len = 0; + hcon->le_per_adv_data_offset = 0; +- goto done; ++ goto release3; + } + + if (hcon->le_per_adv_data_offset + ev3->length > + HCI_MAX_PER_AD_TOT_LEN) +- goto done; ++ goto release3; + + memcpy(hcon->le_per_adv_data + hcon->le_per_adv_data_offset, + ev3->data, ev3->length); +@@ -2320,18 +2322,19 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + &base_len); + + if (!base || base_len > BASE_MAX_LENGTH) +- goto done; ++ goto release3; + +- lock_sock(sk); + memcpy(iso_pi(sk)->base, base, base_len); + iso_pi(sk)->base_len = base_len; +- release_sock(sk); + } else { + /* This is a PA data fragment. Keep pa_data_len set to 0 + * until all data has been reassembled. + */ + hcon->le_per_adv_data_len = 0; + } ++ ++release3: ++ release_sock(sk); + } else { + sk = iso_get_sock(&hdev->bdaddr, BDADDR_ANY, + BT_LISTEN, NULL, NULL); +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-iso-lock-sk-in-iso_sock_getname.patch b/queue-6.18/bluetooth-iso-lock-sk-in-iso_sock_getname.patch new file mode 100644 index 0000000000..45a0519c46 --- /dev/null +++ b/queue-6.18/bluetooth-iso-lock-sk-in-iso_sock_getname.patch @@ -0,0 +1,46 @@ +From 0ab8ce0a50cb6b44e4629f82860e3f0d8685d403 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:25 +0300 +Subject: Bluetooth: ISO: lock sk in iso_sock_getname + +From: Pauli Virtanen + +[ Upstream commit 89cf154d7c18e6e94a3da83051f3cf2bac317ae2 ] + +Accessing iso_pi(sk)->conn requires lock_sock, which is not held here. + +Fix by adding the lock/release. + +Fixes: 2df108c227b2 ("Bluetooth: ISO: Fix using BT_SK_PA_SYNC to detect BIS sockets") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 5868ad1f7128e..4ea41b093ac65 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -1392,6 +1392,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr, + + BT_DBG("sock %p, sk %p", sock, sk); + ++ lock_sock(sk); ++ + addr->sa_family = AF_BLUETOOTH; + + if (peer) { +@@ -1413,6 +1415,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr, + sa->iso_bdaddr_type = iso_pi(sk)->src_type; + } + ++ release_sock(sk); ++ + return len; + } + +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch b/queue-6.18/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch new file mode 100644 index 0000000000..6ef13f363e --- /dev/null +++ b/queue-6.18/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch @@ -0,0 +1,49 @@ +From 6eb20cb8cfe3cec217e149eae5e54030e9152fdb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:28 +0300 +Subject: Bluetooth: ISO: validate sockaddr_iso first in iso_sock_rebind_bis() + +From: Pauli Virtanen + +[ Upstream commit 4e20192d46a685d73e590a60a4a2419a0a8afcbf ] + +iso_sock_rebind_bis() updates socket iso_pi(sk)->bc_num_bis before +validating the BIS values, so it's possible to end up with bc_num_bis +inconsistent. + +Assign to iso_pi(sk)->bc_num_bis only after validation. + +Fixes: 80837140c1f2 ("Bluetooth: ISO: Allow binding a PA sync socket") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index a0ef76ecb80b8..98fa94644b4a6 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -1022,15 +1022,15 @@ static int iso_sock_bind_pa_sk(struct sock *sk, struct sockaddr_iso *sa, + goto done; + } + +- iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis; +- +- for (int i = 0; i < iso_pi(sk)->bc_num_bis; i++) ++ for (int i = 0; i < sa->iso_bc->bc_num_bis; i++) + if (sa->iso_bc->bc_bis[i] < 0x01 || + sa->iso_bc->bc_bis[i] > 0x1f) { + err = -EINVAL; + goto done; + } + ++ iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis; ++ + memcpy(iso_pi(sk)->bc_bis, sa->iso_bc->bc_bis, + iso_pi(sk)->bc_num_bis); + +-- +2.53.0 + diff --git a/queue-6.18/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-6.18/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch new file mode 100644 index 0000000000..b4df872b37 --- /dev/null +++ b/queue-6.18/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch @@ -0,0 +1,60 @@ +From 16ceb79c28e11cf333f580f97fd034fb326949bb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 14:48:45 +0800 +Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp + +From: Jiale Yao + +[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ] + +l2cap_le_connect_rsp() obtains a channel via +__l2cap_get_chan_by_ident() but neither holds a reference nor uses +l2cap_chan_hold_unless_zero() before locking and operating on it. +A concurrent l2cap_chan_del() triggered by a remote disconnect can +free the channel between the lookup and l2cap_chan_lock(), causing +a use-after-free. + +The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler +l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero() +to safely hold a reference, but l2cap_le_connect_rsp() was left +unprotected. + +Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup +and l2cap_chan_put() on the exit path, consistent with other L2CAP +response handlers. + +Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request") +Assisted-by: Claude:deepseek-v4-pro +Signed-off-by: Jiale Yao +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/l2cap_core.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index e04948fb6832e..6133c65b20172 100644 +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -4831,6 +4831,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + if (!chan) + return -EBADSLT; + ++ chan = l2cap_chan_hold_unless_zero(chan); ++ if (!chan) ++ return -EBADSLT; ++ + err = 0; + + l2cap_chan_lock(chan); +@@ -4876,6 +4880,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + } + + l2cap_chan_unlock(chan); ++ l2cap_chan_put(chan); + + return err; + } +-- +2.53.0 + diff --git a/queue-6.18/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch b/queue-6.18/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch new file mode 100644 index 0000000000..ee5295adfe --- /dev/null +++ b/queue-6.18/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch @@ -0,0 +1,79 @@ +From b3799c578e81fc664a568b218b9bcb355e6f1847 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 4 Jul 2026 17:58:56 +0930 +Subject: btrfs: fix leaking BTRFS_FS_STATE_REMOUNTING flag + +From: Qu Wenruo + +[ Upstream commit 6881f45d0eb541f2cee8c37c84b3860a23823bb3 ] + +[BUG] +The following script can lead to unexpected qgroup rescan failure: + + # mkfs.btrfs -f -O quota $dev + # mount $dev $mnt + # mount -o remount,rescue=ibadroots $mnt + ^^^^^ This above command is expected to fail + + # btrfs quota rescan -w $mnt + ^^^^^ The above qgroup rescan is not expected to fail + + # btrfs qgroup show $mnt + WARNING: qgroup data inconsistent, rescan recommended + Qgroupid Referenced Exclusive Path + -------- ---------- --------- ---- + 0/5 16.00KiB 16.00KiB + +The above short script will be converted to a proper fstests case. + +[CAUSE] +Inside btrfs_reconfigure(), if either btrfs_check_options() or +btrfs_check_features() failed, we will always have +BTRFS_FS_STATE_REMOUNTING set for the fs until the next successful +remount. + +That BTRFS_FS_STATE_REMOUNTING flag will interrupt several operations, +including: + +- Qgroup rescan +- Auto defrag +- Space reclaim + +[FIX] +Change the error handling of btrfs_check_options() and +btrfs_check_features() to goto restore label. + +Fixes: eddb1a433f26 ("btrfs: add reconfigure callback for fs_context") +Reviewed-by: Johannes Thumshirn +Signed-off-by: Qu Wenruo +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/super.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c +index 736575f925179..157d551344707 100644 +--- a/fs/btrfs/super.c ++++ b/fs/btrfs/super.c +@@ -1518,12 +1518,14 @@ static int btrfs_reconfigure(struct fs_context *fc) + sync_filesystem(sb); + set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state); + +- if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) +- return -EINVAL; ++ if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) { ++ ret = -EINVAL; ++ goto restore; ++ } + + ret = btrfs_check_features(fs_info, !(fc->sb_flags & SB_RDONLY)); + if (ret < 0) +- return ret; ++ goto restore; + + btrfs_ctx_to_info(fs_info, ctx); + btrfs_remount_begin(fs_info, old_ctx.mount_opt, fc->sb_flags); +-- +2.53.0 + diff --git a/queue-6.18/btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch b/queue-6.18/btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch new file mode 100644 index 0000000000..87d28de645 --- /dev/null +++ b/queue-6.18/btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch @@ -0,0 +1,58 @@ +From c347a08ac8f96c358e64b5aa8daaae6787004d66 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 13:12:51 +0930 +Subject: btrfs: raid56: fix an incorrect csum skip during scrub + +From: Qu Wenruo + +[ Upstream commit 330dcc553f282e8dc0b88c9495b4c296465364e1 ] + +Commit 7425a2894019 ("btrfs: introduce btrfs_bio_for_each_block_all() +helper") uses the new helper to replace the nested loop inside +verify_bio_data_sectors(), which simplifies the code. + +However that also changed the behavior of "continue" when a block has no +data checksum. + +Previously the "continue" would skip the old for() loop, which would also +increase @total_sector_nr. + +Now the "continue" will skip the new btrfs_bio_for_each_block_all() +loop, which doesn't update @total_sector_nr. + +This means if we hit a block that has no data checksum, we will skip all +the remaining blocks no matter if they have data checksum. +As @total_sector_nr will never be updated, and that test_bit() will +always return false. + +Fix it by increasing @total_sector_nr before calling "continue". + +Fixes: 7425a2894019 ("btrfs: introduce btrfs_bio_for_each_block_all() helper") +Reviewed-by: Daniel Vacek +Signed-off-by: Qu Wenruo +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/raid56.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c +index 0135dceb7baaa..4d4852f2ba7eb 100644 +--- a/fs/btrfs/raid56.c ++++ b/fs/btrfs/raid56.c +@@ -1583,8 +1583,10 @@ static void verify_bio_data_sectors(struct btrfs_raid_bio *rbio, + int ret; + + /* No csum for this sector, skip to the next sector. */ +- if (!test_bit(total_sector_nr, rbio->csum_bitmap)) ++ if (!test_bit(total_sector_nr, rbio->csum_bitmap)) { ++ total_sector_nr++; + continue; ++ } + + ret = btrfs_check_block_csum(fs_info, paddr, + csum_buf, expected_csum); +-- +2.53.0 + diff --git a/queue-6.18/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch b/queue-6.18/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch new file mode 100644 index 0000000000..be545b39f1 --- /dev/null +++ b/queue-6.18/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch @@ -0,0 +1,77 @@ +From b88703a9e4fd0f112d4e59c5882646ddc4035b17 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 3 Jul 2026 07:54:40 +0200 +Subject: btrfs: zoned: fix deadlock between metadata writeback and transaction + commit + +From: Johannes Thumshirn + +[ Upstream commit 1ebe51c29fa9755d5b2fea28727c051117907cf8 ] + +When writing out metadata extent buffers in a zoned filesystem, +btree_writepages() holds fs_info->zoned_meta_io_lock across the whole +writeback loop, including the call to btrfs_check_meta_write_pointer() -> +check_bg_is_active(). + +For the tree-log block group, check_bg_is_active() may fail to activate +the zone and fall back to btrfs_zone_finish_one_bg() to free an active +zone. That path waits for the running transaction to commit while still +holding zoned_meta_io_lock, but the committer needs that same lock to +write out the tree extents, so the two tasks deadlock: + + Task A (kworker, metadata writeback) Task B (fsstress, transaction commit) + ------------------------------------ ------------------------------------- + wb_workfn() btrfs_commit_transaction(T) + btree_writepages() btrfs_write_and_wait_transaction() + btrfs_zoned_meta_io_lock() btrfs_write_marked_extents() + btrfs_check_meta_write_pointer() btree_writepages() + check_bg_is_active() [treelog_bg] btrfs_zoned_meta_io_lock() + btrfs_zone_finish_one_bg() + do_zone_finish() + btrfs_inc_block_group_ro() + btrfs_wait_for_commit() + + +The sibling branch in check_bg_is_active() already drops zoned_meta_io_lock +around do_zone_finish() for this exact reason. Do the same in the tree-log +branch: release the lock around btrfs_zone_finish_one_bg() and re-acquire +it afterwards. The lock only protects fs_info->active_{meta,system}_bg, +which this branch does not touch, and ctx->zoned_bg keeps a reference to +the block group across the unlock, so nothing is lost while the lock +is dropped. + +This hang occasionally reproduces with fstests generic/475 on a zoned +btrfs filesystem. + +Fixes: 13bb483d32ab ("btrfs: zoned: activate metadata block group on write time") +Reviewed-by: Naohiro Aota +Signed-off-by: Johannes Thumshirn +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/zoned.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c +index b8fc97e33009b..340380dbe81d1 100644 +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -2167,7 +2167,11 @@ static bool check_bg_is_active(struct btrfs_eb_write_context *ctx, + + if (fs_info->treelog_bg == block_group->start) { + if (!btrfs_zone_activate(block_group)) { +- int ret_fin = btrfs_zone_finish_one_bg(fs_info); ++ int ret_fin; ++ ++ btrfs_zoned_meta_io_unlock(fs_info); ++ ret_fin = btrfs_zone_finish_one_bg(fs_info); ++ btrfs_zoned_meta_io_lock(fs_info); + + if (ret_fin != 1 || !btrfs_zone_activate(block_group)) + return false; +-- +2.53.0 + diff --git a/queue-6.18/btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch b/queue-6.18/btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch new file mode 100644 index 0000000000..7d9701acb7 --- /dev/null +++ b/queue-6.18/btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch @@ -0,0 +1,56 @@ +From 80ecdcb40ee175cb44c7fec1ea97a7702ad2c510 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 3 Jul 2026 07:54:45 +0200 +Subject: btrfs: zoned: reset meta_write_pointer on zone reset + +From: Johannes Thumshirn + +[ Upstream commit 5fabb1cf25d723274009d7b759545fd59f230c9d ] + +btrfs_reset_unused_block_groups() resets a block group's zone and sets +alloc_offset back to 0 so the space can be reused, but it leaves +meta_write_pointer pointing at the previous end of the zone. + +Once the block group is reactivated and reused for metadata, newly +allocated tree blocks live before that stale write pointer. +btrfs_check_meta_write_pointer() then sees them behind the write pointer, +so they can never be written out in sequential order: the dirty extent +buffers are stranded and pin their btree_inode folios until unmount. + +Reset meta_write_pointer back to the start of the block group for +metadata and system block groups. + +Fixes: 453a73c3069a ("btrfs: zoned: reclaim unused zone by zone resetting") +Reviewed-by: Naohiro Aota +Signed-off-by: Johannes Thumshirn +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/zoned.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c +index 340380dbe81d1..0dfbb28b7445c 100644 +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -3171,6 +3171,17 @@ int btrfs_reset_unused_block_groups(struct btrfs_space_info *space_info, u64 num + reclaimed = bg->alloc_offset; + bg->zone_unusable = bg->length - bg->zone_capacity; + bg->alloc_offset = 0; ++ /* ++ * The zone was just reset to empty, so alloc_offset went back to ++ * the start of the zone. For metadata/system block groups the ++ * write pointer must follow it back to the start of the zone; ++ * otherwise it stays stale at the previous (finished) zone end, ++ * and metadata written into the reused zone would sit behind the ++ * write pointer, could never be written out in sequential order, ++ * and would be stranded (pinning its folio) until unmount. ++ */ ++ if (bg->flags & (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_SYSTEM)) ++ bg->meta_write_pointer = bg->start; + /* + * This holds because we currently reset fully used then freed + * block group. +-- +2.53.0 + diff --git a/queue-6.18/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-6.18/can-isotp-check-register_netdevice_notifier-error-in.patch new file mode 100644 index 0000000000..c6b8eaed0f --- /dev/null +++ b/queue-6.18/can-isotp-check-register_netdevice_notifier-error-in.patch @@ -0,0 +1,57 @@ +From 22a858da2c2c2adcc7e4a0e478a1c87f2d2e6411 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Jul 2026 16:56:56 +0800 +Subject: can: isotp: check register_netdevice_notifier() error in module init + +From: Minhong He + +[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ] + +Register the netdevice notifier before can_proto_register() and check the +return value. If protocol registration fails, unregister the notifier +before returning the error. + +Align isotp_module_init() with the reordering already done for raw.c +(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and +bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization +in bcm_module_init()")). + +Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier") +Signed-off-by: Minhong He +Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn +Signed-off-by: Marc Kleine-Budde +Signed-off-by: Sasha Levin +--- + net/can/isotp.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/net/can/isotp.c b/net/can/isotp.c +index c21d816a52747..95e14631a1eb9 100644 +--- a/net/can/isotp.c ++++ b/net/can/isotp.c +@@ -1888,13 +1888,18 @@ static __init int isotp_module_init(void) + + pr_info("can: isotp protocol (max_pdu_size %d)\n", max_pdu_size); + ++ err = register_netdevice_notifier(&canisotp_notifier); ++ if (err) ++ return err; ++ + err = can_proto_register(&isotp_can_proto); +- if (err < 0) ++ if (err < 0) { + pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err)); +- else +- register_netdevice_notifier(&canisotp_notifier); ++ unregister_netdevice_notifier(&canisotp_notifier); ++ return err; ++ } + +- return err; ++ return 0; + } + + static __exit void isotp_module_exit(void) +-- +2.53.0 + diff --git a/queue-6.18/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch b/queue-6.18/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch new file mode 100644 index 0000000000..6e2832153e --- /dev/null +++ b/queue-6.18/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch @@ -0,0 +1,65 @@ +From d5fa93acbdda5bb1d9e98ae4db0b22f95f812a64 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 25 May 2026 10:15:50 -0400 +Subject: dmaengine: idxd: fix fdev setup failure cleanup in idxd_cdev_open() + +From: Yuho Choi + +[ Upstream commit ee1d7274102285d78a53161fc705a8d8cd40b066 ] + +The failed_dev_add and failed_dev_name paths drop the file-device +reference while wq->wq_lock is still held. If put_device(fdev) drops the +last reference, idxd_file_dev_release() runs synchronously and tries to +take wq->wq_lock again, deadlocking. + +Those paths also fall through into the later ctx cleanup labels even +though idxd_file_dev_release() owns that cleanup and frees ctx. This can +make idxd_xa_pasid_remove(ctx) and kfree(ctx) operate on a freed context. + +Move idxd_wq_get() before file-device setup can fail, since the release +callback always calls idxd_wq_put(). Then unlock wq->wq_lock before +put_device(fdev) and return directly from the file-device setup failure +path, leaving ctx cleanup to the release callback. + +Fixes: e6fd6d7e5f0fe ("dmaengine: idxd: add a device to represent the file opened") +Signed-off-by: Yuho Choi +Reviewed-by: Dave Jiang +Acked-by: Vinicius Costa Gomes +Link: https://patch.msgid.link/20260525141550.1385581-1-dbgh9129@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/dma/idxd/cdev.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c +index 4105688cf3f06..37addcdc10bc2 100644 +--- a/drivers/dma/idxd/cdev.c ++++ b/drivers/dma/idxd/cdev.c +@@ -288,6 +288,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) + fdev->parent = cdev_dev(idxd_cdev); + fdev->bus = &dsa_bus_type; + fdev->type = &idxd_cdev_file_type; ++ idxd_wq_get(wq); + + rc = dev_set_name(fdev, "file%d", ctx->id); + if (rc < 0) { +@@ -301,13 +302,14 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) + goto failed_dev_add; + } + +- idxd_wq_get(wq); + mutex_unlock(&wq->wq_lock); + return 0; + + failed_dev_add: + failed_dev_name: ++ mutex_unlock(&wq->wq_lock); + put_device(fdev); ++ return rc; + failed_ida: + failed_set_pasid: + if (device_user_pasid_enabled(idxd)) +-- +2.53.0 + diff --git a/queue-6.18/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-6.18/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch new file mode 100644 index 0000000000..afaba587a4 --- /dev/null +++ b/queue-6.18/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch @@ -0,0 +1,60 @@ +From 64f10f396db74f269ec45dfb831bfa0541e5248b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 1 Jul 2026 12:57:33 +0800 +Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA + +From: Hongling Zeng + +[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ] + +When terminating DMA transfers, active descriptors are not properly +reclaimed. Only cyclic descriptors were handled, leaving non-cyclic +descriptors and their LLI chains to be permanently leaked. + +Fix by using vchan_terminate_vdesc() which handles both cyclic and +non-cyclic descriptors by adding them to desc_terminated queue for +proper cleanup. + +Add pchan->desc != pchan->done check to prevent double-adding completed +descriptors, which would corrupt the list. + +Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller") +Signed-off-by: Hongling Zeng +Acked-by: Jernej Skrabec +Suggested-by: Frank Li +Reviewed-by: Frank Li +Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/dma/sun6i-dma.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c +index f9d876deb1f05..ae362a2f7c40f 100644 +--- a/drivers/dma/sun6i-dma.c ++++ b/drivers/dma/sun6i-dma.c +@@ -935,16 +935,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan) + + spin_lock_irqsave(&vchan->vc.lock, flags); + +- if (vchan->cyclic) { +- vchan->cyclic = false; +- if (pchan && pchan->desc) { +- struct virt_dma_desc *vd = &pchan->desc->vd; +- struct virt_dma_chan *vc = &vchan->vc; ++ if (pchan && pchan->desc && pchan->desc != pchan->done) { ++ struct virt_dma_desc *vd = &pchan->desc->vd; + +- list_add_tail(&vd->node, &vc->desc_completed); +- } ++ vchan_terminate_vdesc(vd); + } + ++ vchan->cyclic = false; + vchan_get_all_descriptors(&vchan->vc, &head); + + if (pchan) { +-- +2.53.0 + diff --git a/queue-6.18/drivers-hv-allocate-the-paravisor-synic-pages-when-r.patch b/queue-6.18/drivers-hv-allocate-the-paravisor-synic-pages-when-r.patch new file mode 100644 index 0000000000..df00e5c34f --- /dev/null +++ b/queue-6.18/drivers-hv-allocate-the-paravisor-synic-pages-when-r.patch @@ -0,0 +1,294 @@ +From 216aef528319180a957576357360b5871594836d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Oct 2025 16:34:09 -0700 +Subject: Drivers: hv: Allocate the paravisor SynIC pages when required + +From: Roman Kisel + +[ Upstream commit 226494e5ee4eb0bae4fc7b525505828271d5047e ] + +Confidential VMBus requires interacting with two SynICs -- one +provided by the host hypervisor, and one provided by the paravisor. +Each SynIC requires its own message and event pages. + +Refactor and extend the existing code to add allocating and freeing +the message and event pages for the paravisor SynIC when it is +present. + +Signed-off-by: Roman Kisel +Reviewed-by: Tianyu Lan +Reviewed-by: Michael Kelley +Signed-off-by: Wei Liu +Stable-dep-of: 8c7ab779c885 ("Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep annotation") +Signed-off-by: Sasha Levin +--- + drivers/hv/hv.c | 184 +++++++++++++++++++------------------- + drivers/hv/hyperv_vmbus.h | 18 ++++ + 2 files changed, 112 insertions(+), 90 deletions(-) + +diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c +index b7419d0fad1d3..90db1e17582d8 100644 +--- a/drivers/hv/hv.c ++++ b/drivers/hv/hv.c +@@ -96,10 +96,70 @@ int hv_post_message(union hv_connection_id connection_id, + return hv_result(status); + } + ++static int hv_alloc_page(void **page, bool decrypt, const char *note) ++{ ++ int ret = 0; ++ ++ /* ++ * After the page changes its encryption status, its contents might ++ * appear scrambled on some hardware. Thus `get_zeroed_page` would ++ * zero the page out in vain, so do that explicitly exactly once. ++ * ++ * By default, the page is allocated encrypted in a CoCo VM. ++ */ ++ *page = (void *)__get_free_page(GFP_KERNEL); ++ if (!*page) ++ return -ENOMEM; ++ ++ if (decrypt) ++ ret = set_memory_decrypted((unsigned long)*page, 1); ++ if (ret) ++ goto failed; ++ ++ memset(*page, 0, PAGE_SIZE); ++ return 0; ++ ++failed: ++ /* ++ * Report the failure but don't put the page back on the free list as ++ * its encryption status is unknown. ++ */ ++ pr_err("allocation failed for %s page, error %d, decrypted %d\n", ++ note, ret, decrypt); ++ *page = NULL; ++ return ret; ++} ++ ++static int hv_free_page(void **page, bool encrypt, const char *note) ++{ ++ int ret = 0; ++ ++ if (!*page) ++ return 0; ++ ++ if (encrypt) ++ ret = set_memory_encrypted((unsigned long)*page, 1); ++ ++ /* ++ * In the case of the failure, the page is leaked. Something is wrong, ++ * prefer to lose the page with the unknown encryption status and stay afloat. ++ */ ++ if (ret) ++ pr_err("deallocation failed for %s page, error %d, encrypt %d\n", ++ note, ret, encrypt); ++ else ++ free_page((unsigned long)*page); ++ ++ *page = NULL; ++ ++ return ret; ++} ++ + int hv_synic_alloc(void) + { + int cpu, ret = -ENOMEM; + struct hv_per_cpu_context *hv_cpu; ++ const bool decrypt = !vmbus_is_confidential(); + + /* + * First, zero all per-cpu memory areas so hv_synic_free() can +@@ -125,73 +185,37 @@ int hv_synic_alloc(void) + vmbus_on_msg_dpc, (unsigned long)hv_cpu); + + if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) { +- hv_cpu->post_msg_page = (void *)get_zeroed_page(GFP_ATOMIC); +- if (!hv_cpu->post_msg_page) { +- pr_err("Unable to allocate post msg page\n"); ++ ret = hv_alloc_page(&hv_cpu->post_msg_page, ++ decrypt, "post msg"); ++ if (ret) + goto err; +- } +- +- ret = set_memory_decrypted((unsigned long)hv_cpu->post_msg_page, 1); +- if (ret) { +- pr_err("Failed to decrypt post msg page: %d\n", ret); +- /* Just leak the page, as it's unsafe to free the page. */ +- hv_cpu->post_msg_page = NULL; +- goto err; +- } +- +- memset(hv_cpu->post_msg_page, 0, PAGE_SIZE); + } + + /* +- * Synic message and event pages are allocated by paravisor. +- * Skip these pages allocation here. ++ * If these SynIC pages are not allocated, SIEF and SIM pages ++ * are configured using what the root partition or the paravisor ++ * provides upon reading the SIEFP and SIMP registers. + */ + if (!ms_hyperv.paravisor_present && !hv_root_partition()) { +- hv_cpu->hyp_synic_message_page = +- (void *)get_zeroed_page(GFP_ATOMIC); +- if (!hv_cpu->hyp_synic_message_page) { +- pr_err("Unable to allocate SYNIC message page\n"); ++ ret = hv_alloc_page(&hv_cpu->hyp_synic_message_page, ++ decrypt, "hypervisor SynIC msg"); ++ if (ret) + goto err; +- } +- +- hv_cpu->hyp_synic_event_page = +- (void *)get_zeroed_page(GFP_ATOMIC); +- if (!hv_cpu->hyp_synic_event_page) { +- pr_err("Unable to allocate SYNIC event page\n"); +- +- free_page((unsigned long)hv_cpu->hyp_synic_message_page); +- hv_cpu->hyp_synic_message_page = NULL; ++ ret = hv_alloc_page(&hv_cpu->hyp_synic_event_page, ++ decrypt, "hypervisor SynIC event"); ++ if (ret) + goto err; +- } + } + +- if (!ms_hyperv.paravisor_present && +- (hv_isolation_type_snp() || hv_isolation_type_tdx())) { +- ret = set_memory_decrypted((unsigned long) +- hv_cpu->hyp_synic_message_page, 1); +- if (ret) { +- pr_err("Failed to decrypt SYNIC msg page: %d\n", ret); +- hv_cpu->hyp_synic_message_page = NULL; +- +- /* +- * Free the event page here so that hv_synic_free() +- * won't later try to re-encrypt it. +- */ +- free_page((unsigned long)hv_cpu->hyp_synic_event_page); +- hv_cpu->hyp_synic_event_page = NULL; ++ if (vmbus_is_confidential()) { ++ ret = hv_alloc_page(&hv_cpu->para_synic_message_page, ++ false, "paravisor SynIC msg"); ++ if (ret) + goto err; +- } +- +- ret = set_memory_decrypted((unsigned long) +- hv_cpu->hyp_synic_event_page, 1); +- if (ret) { +- pr_err("Failed to decrypt SYNIC event page: %d\n", ret); +- hv_cpu->hyp_synic_event_page = NULL; ++ ret = hv_alloc_page(&hv_cpu->para_synic_event_page, ++ false, "paravisor SynIC event"); ++ if (ret) + goto err; +- } +- +- memset(hv_cpu->hyp_synic_message_page, 0, PAGE_SIZE); +- memset(hv_cpu->hyp_synic_event_page, 0, PAGE_SIZE); + } + } + +@@ -207,48 +231,28 @@ int hv_synic_alloc(void) + + void hv_synic_free(void) + { +- int cpu, ret; ++ int cpu; ++ const bool encrypt = !vmbus_is_confidential(); + + for_each_present_cpu(cpu) { + struct hv_per_cpu_context *hv_cpu = + per_cpu_ptr(hv_context.cpu_context, cpu); + +- /* It's better to leak the page if the encryption fails. */ +- if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) { +- if (hv_cpu->post_msg_page) { +- ret = set_memory_encrypted((unsigned long) +- hv_cpu->post_msg_page, 1); +- if (ret) { +- pr_err("Failed to encrypt post msg page: %d\n", ret); +- hv_cpu->post_msg_page = NULL; +- } +- } ++ if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) ++ hv_free_page(&hv_cpu->post_msg_page, ++ encrypt, "post msg"); ++ if (!ms_hyperv.paravisor_present && !hv_root_partition()) { ++ hv_free_page(&hv_cpu->hyp_synic_event_page, ++ encrypt, "hypervisor SynIC event"); ++ hv_free_page(&hv_cpu->hyp_synic_message_page, ++ encrypt, "hypervisor SynIC msg"); + } +- +- if (!ms_hyperv.paravisor_present && +- (hv_isolation_type_snp() || hv_isolation_type_tdx())) { +- if (hv_cpu->hyp_synic_message_page) { +- ret = set_memory_encrypted((unsigned long) +- hv_cpu->hyp_synic_message_page, 1); +- if (ret) { +- pr_err("Failed to encrypt SYNIC msg page: %d\n", ret); +- hv_cpu->hyp_synic_message_page = NULL; +- } +- } +- +- if (hv_cpu->hyp_synic_event_page) { +- ret = set_memory_encrypted((unsigned long) +- hv_cpu->hyp_synic_event_page, 1); +- if (ret) { +- pr_err("Failed to encrypt SYNIC event page: %d\n", ret); +- hv_cpu->hyp_synic_event_page = NULL; +- } +- } ++ if (vmbus_is_confidential()) { ++ hv_free_page(&hv_cpu->para_synic_event_page, ++ false, "paravisor SynIC event"); ++ hv_free_page(&hv_cpu->para_synic_message_page, ++ false, "paravisor SynIC msg"); + } +- +- free_page((unsigned long)hv_cpu->post_msg_page); +- free_page((unsigned long)hv_cpu->hyp_synic_event_page); +- free_page((unsigned long)hv_cpu->hyp_synic_message_page); + } + + kfree(hv_context.hv_numa_map); +diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h +index 1b05a57286101..39cdded062e8b 100644 +--- a/drivers/hv/hyperv_vmbus.h ++++ b/drivers/hv/hyperv_vmbus.h +@@ -120,8 +120,26 @@ enum { + * Per cpu state for channel handling + */ + struct hv_per_cpu_context { ++ /* ++ * SynIC pages for communicating with the host. ++ * ++ * These pages are accessible to the host partition and the hypervisor. ++ * They may be used for exchanging data with the host partition and the ++ * hypervisor even when they aren't trusted yet the guest partition ++ * must be prepared to handle the malicious behavior. ++ */ + void *hyp_synic_message_page; + void *hyp_synic_event_page; ++ /* ++ * SynIC pages for communicating with the paravisor. ++ * ++ * These pages may be accessed from within the guest partition only in ++ * CoCo VMs. Neither the host partition nor the hypervisor can access ++ * these pages in that case; they are used for exchanging data with the ++ * paravisor. ++ */ ++ void *para_synic_message_page; ++ void *para_synic_event_page; + + /* + * The page is only used in hv_post_message() for a TDX VM (with the +-- +2.53.0 + diff --git a/queue-6.18/drivers-hv-rename-fields-for-synic-message-and-event.patch b/queue-6.18/drivers-hv-rename-fields-for-synic-message-and-event.patch new file mode 100644 index 0000000000..7b9256d72b --- /dev/null +++ b/queue-6.18/drivers-hv-rename-fields-for-synic-message-and-event.patch @@ -0,0 +1,327 @@ +From d35dad7522434863a8d053f83909a07237c91c30 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Oct 2025 16:34:08 -0700 +Subject: Drivers: hv: Rename fields for SynIC message and event pages + +From: Roman Kisel + +[ Upstream commit 163224c189e8b679ce919aa64ccabb7a992ca2d1 ] + +Confidential VMBus requires interacting with two SynICs -- one +provided by the host hypervisor, and one provided by the paravisor. +Each SynIC requires its own message and event pages. + +Rename the existing host-accessible SynIC message and event pages +with the "hyp_" prefix to clearly distinguish them from the paravisor +ones. The field name is also changed in mshv_root.* for consistency. + +No functional changes. + +Signed-off-by: Roman Kisel +Reviewed-by: Tianyu Lan +Reviewed-by: Michael Kelley +Signed-off-by: Wei Liu +Stable-dep-of: 8c7ab779c885 ("Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep annotation") +Signed-off-by: Sasha Levin +--- + drivers/hv/channel_mgmt.c | 6 ++-- + drivers/hv/hv.c | 66 +++++++++++++++++++-------------------- + drivers/hv/hyperv_vmbus.h | 4 +-- + drivers/hv/mshv_root.h | 2 +- + drivers/hv/mshv_synic.c | 6 ++-- + drivers/hv/vmbus_drv.c | 6 ++-- + 6 files changed, 45 insertions(+), 45 deletions(-) + +diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c +index fd1d675ae37a4..d5348195e4fb0 100644 +--- a/drivers/hv/channel_mgmt.c ++++ b/drivers/hv/channel_mgmt.c +@@ -844,14 +844,14 @@ static void vmbus_wait_for_unload(void) + = per_cpu_ptr(hv_context.cpu_context, cpu); + + /* +- * In a CoCo VM the synic_message_page is not allocated ++ * In a CoCo VM the hyp_synic_message_page is not allocated + * in hv_synic_alloc(). Instead it is set/cleared in + * hv_synic_enable_regs() and hv_synic_disable_regs() + * such that it is set only when the CPU is online. If + * not all present CPUs are online, the message page + * might be NULL, so skip such CPUs. + */ +- page_addr = hv_cpu->synic_message_page; ++ page_addr = hv_cpu->hyp_synic_message_page; + if (!page_addr) + continue; + +@@ -892,7 +892,7 @@ static void vmbus_wait_for_unload(void) + struct hv_per_cpu_context *hv_cpu + = per_cpu_ptr(hv_context.cpu_context, cpu); + +- page_addr = hv_cpu->synic_message_page; ++ page_addr = hv_cpu->hyp_synic_message_page; + if (!page_addr) + continue; + +diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c +index b14c5f9e0ef29..b7419d0fad1d3 100644 +--- a/drivers/hv/hv.c ++++ b/drivers/hv/hv.c +@@ -147,20 +147,20 @@ int hv_synic_alloc(void) + * Skip these pages allocation here. + */ + if (!ms_hyperv.paravisor_present && !hv_root_partition()) { +- hv_cpu->synic_message_page = ++ hv_cpu->hyp_synic_message_page = + (void *)get_zeroed_page(GFP_ATOMIC); +- if (!hv_cpu->synic_message_page) { ++ if (!hv_cpu->hyp_synic_message_page) { + pr_err("Unable to allocate SYNIC message page\n"); + goto err; + } + +- hv_cpu->synic_event_page = ++ hv_cpu->hyp_synic_event_page = + (void *)get_zeroed_page(GFP_ATOMIC); +- if (!hv_cpu->synic_event_page) { ++ if (!hv_cpu->hyp_synic_event_page) { + pr_err("Unable to allocate SYNIC event page\n"); + +- free_page((unsigned long)hv_cpu->synic_message_page); +- hv_cpu->synic_message_page = NULL; ++ free_page((unsigned long)hv_cpu->hyp_synic_message_page); ++ hv_cpu->hyp_synic_message_page = NULL; + goto err; + } + } +@@ -168,30 +168,30 @@ int hv_synic_alloc(void) + if (!ms_hyperv.paravisor_present && + (hv_isolation_type_snp() || hv_isolation_type_tdx())) { + ret = set_memory_decrypted((unsigned long) +- hv_cpu->synic_message_page, 1); ++ hv_cpu->hyp_synic_message_page, 1); + if (ret) { + pr_err("Failed to decrypt SYNIC msg page: %d\n", ret); +- hv_cpu->synic_message_page = NULL; ++ hv_cpu->hyp_synic_message_page = NULL; + + /* + * Free the event page here so that hv_synic_free() + * won't later try to re-encrypt it. + */ +- free_page((unsigned long)hv_cpu->synic_event_page); +- hv_cpu->synic_event_page = NULL; ++ free_page((unsigned long)hv_cpu->hyp_synic_event_page); ++ hv_cpu->hyp_synic_event_page = NULL; + goto err; + } + + ret = set_memory_decrypted((unsigned long) +- hv_cpu->synic_event_page, 1); ++ hv_cpu->hyp_synic_event_page, 1); + if (ret) { + pr_err("Failed to decrypt SYNIC event page: %d\n", ret); +- hv_cpu->synic_event_page = NULL; ++ hv_cpu->hyp_synic_event_page = NULL; + goto err; + } + +- memset(hv_cpu->synic_message_page, 0, PAGE_SIZE); +- memset(hv_cpu->synic_event_page, 0, PAGE_SIZE); ++ memset(hv_cpu->hyp_synic_message_page, 0, PAGE_SIZE); ++ memset(hv_cpu->hyp_synic_event_page, 0, PAGE_SIZE); + } + } + +@@ -227,28 +227,28 @@ void hv_synic_free(void) + + if (!ms_hyperv.paravisor_present && + (hv_isolation_type_snp() || hv_isolation_type_tdx())) { +- if (hv_cpu->synic_message_page) { ++ if (hv_cpu->hyp_synic_message_page) { + ret = set_memory_encrypted((unsigned long) +- hv_cpu->synic_message_page, 1); ++ hv_cpu->hyp_synic_message_page, 1); + if (ret) { + pr_err("Failed to encrypt SYNIC msg page: %d\n", ret); +- hv_cpu->synic_message_page = NULL; ++ hv_cpu->hyp_synic_message_page = NULL; + } + } + +- if (hv_cpu->synic_event_page) { ++ if (hv_cpu->hyp_synic_event_page) { + ret = set_memory_encrypted((unsigned long) +- hv_cpu->synic_event_page, 1); ++ hv_cpu->hyp_synic_event_page, 1); + if (ret) { + pr_err("Failed to encrypt SYNIC event page: %d\n", ret); +- hv_cpu->synic_event_page = NULL; ++ hv_cpu->hyp_synic_event_page = NULL; + } + } + } + + free_page((unsigned long)hv_cpu->post_msg_page); +- free_page((unsigned long)hv_cpu->synic_event_page); +- free_page((unsigned long)hv_cpu->synic_message_page); ++ free_page((unsigned long)hv_cpu->hyp_synic_event_page); ++ free_page((unsigned long)hv_cpu->hyp_synic_message_page); + } + + kfree(hv_context.hv_numa_map); +@@ -278,12 +278,12 @@ void hv_synic_enable_regs(unsigned int cpu) + /* Mask out vTOM bit. ioremap_cache() maps decrypted */ + u64 base = (simp.base_simp_gpa << HV_HYP_PAGE_SHIFT) & + ~ms_hyperv.shared_gpa_boundary; +- hv_cpu->synic_message_page = ++ hv_cpu->hyp_synic_message_page = + (void *)ioremap_cache(base, HV_HYP_PAGE_SIZE); +- if (!hv_cpu->synic_message_page) ++ if (!hv_cpu->hyp_synic_message_page) + pr_err("Fail to map synic message page.\n"); + } else { +- simp.base_simp_gpa = virt_to_phys(hv_cpu->synic_message_page) ++ simp.base_simp_gpa = virt_to_phys(hv_cpu->hyp_synic_message_page) + >> HV_HYP_PAGE_SHIFT; + } + +@@ -297,12 +297,12 @@ void hv_synic_enable_regs(unsigned int cpu) + /* Mask out vTOM bit. ioremap_cache() maps decrypted */ + u64 base = (siefp.base_siefp_gpa << HV_HYP_PAGE_SHIFT) & + ~ms_hyperv.shared_gpa_boundary; +- hv_cpu->synic_event_page = ++ hv_cpu->hyp_synic_event_page = + (void *)ioremap_cache(base, HV_HYP_PAGE_SIZE); +- if (!hv_cpu->synic_event_page) ++ if (!hv_cpu->hyp_synic_event_page) + pr_err("Fail to map synic event page.\n"); + } else { +- siefp.base_siefp_gpa = virt_to_phys(hv_cpu->synic_event_page) ++ siefp.base_siefp_gpa = virt_to_phys(hv_cpu->hyp_synic_event_page) + >> HV_HYP_PAGE_SHIFT; + } + +@@ -360,8 +360,8 @@ void hv_synic_disable_regs(unsigned int cpu) + */ + simp.simp_enabled = 0; + if (ms_hyperv.paravisor_present || hv_root_partition()) { +- iounmap(hv_cpu->synic_message_page); +- hv_cpu->synic_message_page = NULL; ++ iounmap(hv_cpu->hyp_synic_message_page); ++ hv_cpu->hyp_synic_message_page = NULL; + } else { + simp.base_simp_gpa = 0; + } +@@ -372,8 +372,8 @@ void hv_synic_disable_regs(unsigned int cpu) + siefp.siefp_enabled = 0; + + if (ms_hyperv.paravisor_present || hv_root_partition()) { +- iounmap(hv_cpu->synic_event_page); +- hv_cpu->synic_event_page = NULL; ++ iounmap(hv_cpu->hyp_synic_event_page); ++ hv_cpu->hyp_synic_event_page = NULL; + } else { + siefp.base_siefp_gpa = 0; + } +@@ -403,7 +403,7 @@ static bool hv_synic_event_pending(void) + { + struct hv_per_cpu_context *hv_cpu = this_cpu_ptr(hv_context.cpu_context); + union hv_synic_event_flags *event = +- (union hv_synic_event_flags *)hv_cpu->synic_event_page + VMBUS_MESSAGE_SINT; ++ (union hv_synic_event_flags *)hv_cpu->hyp_synic_event_page + VMBUS_MESSAGE_SINT; + unsigned long *recv_int_page = event->flags; /* assumes VMBus version >= VERSION_WIN8 */ + bool pending; + u32 relid; +diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h +index 34943de7d6ac4..1b05a57286101 100644 +--- a/drivers/hv/hyperv_vmbus.h ++++ b/drivers/hv/hyperv_vmbus.h +@@ -120,8 +120,8 @@ enum { + * Per cpu state for channel handling + */ + struct hv_per_cpu_context { +- void *synic_message_page; +- void *synic_event_page; ++ void *hyp_synic_message_page; ++ void *hyp_synic_event_page; + + /* + * The page is only used in hv_post_message() for a TDX VM (with the +diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h +index e3931b0f12693..db6b42db2fdc6 100644 +--- a/drivers/hv/mshv_root.h ++++ b/drivers/hv/mshv_root.h +@@ -169,7 +169,7 @@ struct mshv_girq_routing_table { + }; + + struct hv_synic_pages { +- struct hv_message_page *synic_message_page; ++ struct hv_message_page *hyp_synic_message_page; + struct hv_synic_event_flags_page *synic_event_flags_page; + struct hv_synic_event_ring_page *synic_event_ring_page; + }; +diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c +index 1a822beae2390..3c65b1c4abbe3 100644 +--- a/drivers/hv/mshv_synic.c ++++ b/drivers/hv/mshv_synic.c +@@ -399,7 +399,7 @@ mshv_intercept_isr(struct hv_message *msg) + void mshv_isr(void) + { + struct hv_synic_pages *spages = this_cpu_ptr(mshv_root.synic_pages); +- struct hv_message_page **msg_page = &spages->synic_message_page; ++ struct hv_message_page **msg_page = &spages->hyp_synic_message_page; + struct hv_message *msg; + bool handled; + +@@ -461,7 +461,7 @@ int mshv_synic_init(unsigned int cpu) + #endif + union hv_synic_scontrol sctrl; + struct hv_synic_pages *spages = this_cpu_ptr(mshv_root.synic_pages); +- struct hv_message_page **msg_page = &spages->synic_message_page; ++ struct hv_message_page **msg_page = &spages->hyp_synic_message_page; + struct hv_synic_event_flags_page **event_flags_page = + &spages->synic_event_flags_page; + struct hv_synic_event_ring_page **event_ring_page = +@@ -555,7 +555,7 @@ int mshv_synic_cleanup(unsigned int cpu) + union hv_synic_sirbp sirbp; + union hv_synic_scontrol sctrl; + struct hv_synic_pages *spages = this_cpu_ptr(mshv_root.synic_pages); +- struct hv_message_page **msg_page = &spages->synic_message_page; ++ struct hv_message_page **msg_page = &spages->hyp_synic_message_page; + struct hv_synic_event_flags_page **event_flags_page = + &spages->synic_event_flags_page; + struct hv_synic_event_ring_page **event_ring_page = +diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c +index 1a0e350c19404..0788281b28ded 100644 +--- a/drivers/hv/vmbus_drv.c ++++ b/drivers/hv/vmbus_drv.c +@@ -1048,7 +1048,7 @@ static void vmbus_onmessage_work(struct work_struct *work) + void vmbus_on_msg_dpc(unsigned long data) + { + struct hv_per_cpu_context *hv_cpu = (void *)data; +- void *page_addr = hv_cpu->synic_message_page; ++ void *page_addr = hv_cpu->hyp_synic_message_page; + struct hv_message msg_copy, *msg = (struct hv_message *)page_addr + + VMBUS_MESSAGE_SINT; + struct vmbus_channel_message_header *hdr; +@@ -1232,7 +1232,7 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu) + * The event page can be directly checked to get the id of + * the channel that has the interrupt pending. + */ +- void *page_addr = hv_cpu->synic_event_page; ++ void *page_addr = hv_cpu->hyp_synic_event_page; + union hv_synic_event_flags *event + = (union hv_synic_event_flags *)page_addr + + VMBUS_MESSAGE_SINT; +@@ -1315,7 +1315,7 @@ static void __vmbus_isr(void) + + vmbus_chan_sched(hv_cpu); + +- page_addr = hv_cpu->synic_message_page; ++ page_addr = hv_cpu->hyp_synic_message_page; + msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; + + /* Check if there are actual msgs to be processed */ +-- +2.53.0 + diff --git a/queue-6.18/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch b/queue-6.18/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch new file mode 100644 index 0000000000..615ebdd3ce --- /dev/null +++ b/queue-6.18/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch @@ -0,0 +1,55 @@ +From ff46847f417fd3960b611bf8edec254ba5495234 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 17:32:15 +0200 +Subject: Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep + annotation + +From: Sebastian Andrzej Siewior + +[ Upstream commit 8c7ab779c8850f4dab8473463cca9a7d52fdaecc ] + +lockdep_hardirq_threaded() is supposed to be used within IRQ core code +and not within drivers. It is not obvious from within the driver, that +this is the only interrupt service routing and that it is not shared +handler. + +Replace lockdep_hardirq_threaded() with a lockdep annotation limiting +threaded context on PREEMPT_RT to __vmbus_isr(). + +Fixes: f8e6343b7a89c ("Drivers: hv: vmbus: Use kthread for vmbus interrupts on PREEMPT_RT") +Signed-off-by: Sebastian Andrzej Siewior +Reviewed-by: Michael Kelley +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/vmbus_drv.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c +index 0788281b28ded..4f1f5f294a735 100644 +--- a/drivers/hv/vmbus_drv.c ++++ b/drivers/hv/vmbus_drv.c +@@ -1372,8 +1372,19 @@ static void vmbus_isr(void) + if (IS_ENABLED(CONFIG_PREEMPT_RT)) { + vmbus_irqd_wake(); + } else { +- lockdep_hardirq_threaded(); ++ static DEFINE_WAIT_OVERRIDE_MAP(vmbus_map, LD_WAIT_CONFIG); ++ ++ /* ++ * vmbus_isr is never force-threaded and always invoked at hard ++ * IRQ level. __vmbus_isr() below can acquire a spinlock_t ++ * which becomes a sleeping lock and must not be acquired in ++ * this context. Therefore on PREEMPT_RT this will be threaded ++ * via vmbus_irqd_wake(). On non-PREEMPT the annotation lets ++ * lockdep know that acquiring a spinlock_t is not an issue. ++ */ ++ lock_map_acquire_try(&vmbus_map); + __vmbus_isr(); ++ lock_map_release(&vmbus_map); + } + } + +-- +2.53.0 + diff --git a/queue-6.18/drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch b/queue-6.18/drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch new file mode 100644 index 0000000000..cad8b81e30 --- /dev/null +++ b/queue-6.18/drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch @@ -0,0 +1,80 @@ +From 752c1d48925da00682511d8f744174f5b0b12e60 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 18 Jul 2026 12:52:07 +0200 +Subject: drm/i915/dp: Ignore the sink's DSC max FRL rate without a PCON DSC + encoder +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Alexander Kaplan + +[ Upstream commit 8891e39e89042e285fd82fdde325d1311ec750a1 ] + +intel_dp_hdmi_sink_max_frl() limits the sink's max FRL rate by its +DSC max FRL rate whenever the sink supports DSC 1.2. +However, the DSC max FRL rate (HF-VSDB DSC_Max_FRL_Rate) only applies +to compressed video transport, which requires a DSC 1.2 encoder in +the PCON (configured via intel_dp_pcon_dsc_configure()). +Without such an encoder the HDMI link always carries uncompressed +video, for which the regular Max_FRL_Rate is the correct limit. + +Applying the DSC limit unconditionally trains the FRL link at a lower +rate than both the PCON and the sink support. +E.g. an LG OLED G4 (Max_FRL_Rate 48 Gbps, DSC_Max_FRL_Rate 24 Gbps) +behind a Synaptics VMM7100 PCON (PCON max FRL bw 48 Gbps, no DSC +encoder): + + Sink max rate from EDID = 24 Gbps + FRL trained with : 24 Gbps + +while Windows/macOS train the same hardware at 40/48 Gbps. +The too low FRL rate needlessly constrains the formats available to +the sink. + +Only apply the sink's DSC max FRL rate if the PCON has a DSC 1.2 +encoder, matching the gate in intel_dp_pcon_dsc_configure(). +PCONs with a DSC encoder keep the current conservative behavior, +since the link is trained once and compressed transport may be used +for any subsequent mode. +With this the setup above trains at 48 Gbps. + +Tested on PTL (xe) with the above PCON/sink combo. + +Fixes: 10fec80b48c5 ("drm/i915/display: Configure PCON for DSC1.1 to DSC1.2 encoding") +Cc: Ankit Nautiyal +Cc: Ville Syrjälä +Reviewed-by: Ankit Nautiyal +Signed-off-by: Alexander Kaplan +Signed-off-by: Ankit Nautiyal +Link: https://patch.msgid.link/20260718105207.5565-3-alexander.kaplan@sms-medipool.de +(cherry picked from commit 71b57dd92f94569dca4bdf883fbd8ca5d4ed4bae) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_dp.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c +index 2a0750acf2053..84fca860c93ee 100644 +--- a/drivers/gpu/drm/i915/display/intel_dp.c ++++ b/drivers/gpu/drm/i915/display/intel_dp.c +@@ -3859,7 +3859,14 @@ static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp) + rate_per_lane = info->hdmi.max_frl_rate_per_lane; + max_frl_rate = max_lanes * rate_per_lane; + +- if (info->hdmi.dsc_cap.v_1p2) { ++ /* ++ * The sink's DSC max FRL rate only applies to compressed video ++ * transport, which requires a DSC 1.2 encoder in the PCON. Without ++ * one the HDMI link always carries uncompressed video, for which ++ * the regular max FRL rate is the limit. ++ */ ++ if (drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd) && ++ info->hdmi.dsc_cap.v_1p2) { + max_dsc_lanes = info->hdmi.dsc_cap.max_lanes; + dsc_rate_per_lane = info->hdmi.dsc_cap.max_frl_rate_per_lane; + if (max_dsc_lanes && dsc_rate_per_lane) +-- +2.53.0 + diff --git a/queue-6.18/drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch b/queue-6.18/drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch new file mode 100644 index 0000000000..74db704985 --- /dev/null +++ b/queue-6.18/drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch @@ -0,0 +1,117 @@ +From 65b2fa379c96e8f2a20c9c02eae57dc4e22e52d7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 07:55:44 +0530 +Subject: drm/i915/hdmi: Poll for 200 msec for TMDS_Scrambler_Status + +From: Jerome Tollet + +[ Upstream commit 1afb8eaeec44fd011f2b93ccd9fd426d753d963b ] + +HDMI 2.0 section 6.1.3.1 specifies that after enabling +Scrambling_Enable and starting scrambled video transmission, the source +should poll Scrambling_Status until it reads 1 or until a timeout of +200 ms expires. + +Add a polling step after enabling the HDMI port to check the scrambling +status when HDMI scrambling is enabled. + +On some HDMI 2.0 sinks, omitting this check can result in 4K@60Hz +(594 MHz) failing to come up correctly because the sink has not yet +finished its scrambling setup. In practice, waiting for the scrambling +status here fixes such sinks. + +While this synchronous polling is not itself explicitly required for +correct modeset sequencing, HDMI 2.0 section 6.1.3.1 does recommend it +as the way for the source to verify that the TMDS link is functioning +correctly with scrambling enabled. + +v3: + - Add explicit HDMI 2.0 section reference in code comment + - Clarify commit message around the observed sink fix + +v2: + - Poll TMDS_Scrambler_Status for up to 200 ms instead of using a fixed + delay + +Reported-by: Jerome Tollet +Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/6868 +Link: https://lore.kernel.org/dri-devel/20251230091037.5603-1-jerome.tollet@gmail.com/ +Signed-off-by: Jerome Tollet +Signed-off-by: Ankit Nautiyal +Reviewed-by: Arun R Murthy +Link: https://patch.msgid.link/20260520022544.3097252-1-ankit.k.nautiyal@intel.com +(cherry picked from commit b7d51d65e4f12a48392d260613108ec262bc7774) +Fixes: 15953637886d ("drm/i915: enable scrambling") +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_ddi.c | 2 ++ + drivers/gpu/drm/i915/display/intel_hdmi.c | 26 +++++++++++++++++++++++ + drivers/gpu/drm/i915/display/intel_hdmi.h | 2 ++ + 3 files changed, 30 insertions(+) + +diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c +index c09aa759f4d4f..3de3e9167d0d6 100644 +--- a/drivers/gpu/drm/i915/display/intel_ddi.c ++++ b/drivers/gpu/drm/i915/display/intel_ddi.c +@@ -3502,6 +3502,8 @@ static void intel_ddi_enable_hdmi(struct intel_atomic_state *state, + } + + intel_ddi_buf_enable(encoder, buf_ctl); ++ ++ intel_hdmi_poll_for_scrambling_enable(crtc_state, connector); + } + + static void intel_ddi_enable(struct intel_atomic_state *state, +diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c +index 4ab7e2e3bfd42..e77c6422b001f 100644 +--- a/drivers/gpu/drm/i915/display/intel_hdmi.c ++++ b/drivers/gpu/drm/i915/display/intel_hdmi.c +@@ -2680,6 +2680,32 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *_ + drm_connector_attach_max_bpc_property(&connector->base, 8, 12); + } + ++/* ++ * HDMI 2.0 spec, section 6.1.3.1 (Scrambling Control): after ++ * enabling Scrambling_Enable and starting scrambled video ++ * transmission, poll Scrambling_Status for up to 200 ms. ++ */ ++void ++intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, ++ struct drm_connector *_connector) ++{ ++ struct intel_connector *connector = to_intel_connector(_connector); ++ struct intel_display *display = to_intel_display(crtc_state); ++ bool scrambling_enabled = false; ++ int ret; ++ ++ if (!crtc_state->hdmi_scrambling) ++ return; ++ ++ /* Poll for a max of 200 msec as per HDMI spec */ ++ ret = poll_timeout_us(scrambling_enabled = drm_scdc_get_scrambling_status(&connector->base), ++ scrambling_enabled, 1000, 200 * 1000, false); ++ if (ret) ++ drm_dbg_kms(display->drm, ++ "[CONNECTOR:%d:%s] Timed out waiting for scrambling enable\n", ++ connector->base.base.id, connector->base.name); ++} ++ + /* + * intel_hdmi_handle_sink_scrambling: handle sink scrambling/clock ratio setup + * @encoder: intel_encoder +diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h +index dec2ad7dd8a22..2de9056367916 100644 +--- a/drivers/gpu/drm/i915/display/intel_hdmi.h ++++ b/drivers/gpu/drm/i915/display/intel_hdmi.h +@@ -69,5 +69,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + unsigned int type, + void *frame, ssize_t len); ++void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, ++ struct drm_connector *_connector); + + #endif /* __INTEL_HDMI_H__ */ +-- +2.53.0 + diff --git a/queue-6.18/drm-mediatek-check-crtc-state-before-freeing.patch b/queue-6.18/drm-mediatek-check-crtc-state-before-freeing.patch new file mode 100644 index 0000000000..5234aa0a02 --- /dev/null +++ b/queue-6.18/drm-mediatek-check-crtc-state-before-freeing.patch @@ -0,0 +1,51 @@ +From 0361283dc1e85b03c5d9ff3ec2e091e9f6bcf5fd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 23:05:28 +0800 +Subject: drm/mediatek: Check CRTC state before freeing + +From: Ruoyu Wang + +[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ] + +mtk_crtc_reset() destroys the current CRTC state only when crtc->state +is non-NULL, but it always converts crtc->state to struct mtk_crtc_state +and passes the result to kfree(). + +When reset is called without an existing state, container_of(NULL, ...) +does not produce NULL. Keep the mtk state free in the same crtc->state +guard as the helper state destruction. + +This issue was found by a static analysis checker and confirmed by +manual source review. + +Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset") +Signed-off-by: Ruoyu Wang +Reviewed-by: CK Hu +Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/ +Signed-off-by: Chun-Kuang Hu +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/mediatek/mtk_crtc.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c +index c4c6d0249df56..aa8e6f6ddcd32 100644 +--- a/drivers/gpu/drm/mediatek/mtk_crtc.c ++++ b/drivers/gpu/drm/mediatek/mtk_crtc.c +@@ -153,10 +153,10 @@ static void mtk_crtc_reset(struct drm_crtc *crtc) + { + struct mtk_crtc_state *state; + +- if (crtc->state) ++ if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); +- +- kfree(to_mtk_crtc_state(crtc->state)); ++ kfree(to_mtk_crtc_state(crtc->state)); ++ } + crtc->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-6.18/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-6.18/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch new file mode 100644 index 0000000000..9fe0189e0e --- /dev/null +++ b/queue-6.18/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch @@ -0,0 +1,47 @@ +From d1275db875ad85f1f620ae0efa4746ae5edf5ff6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 17:26:37 +0800 +Subject: forcedeth: fix UAF of txrx_stats in nv_remove + +From: Chenguang Zhao + +[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ] + +nv_remove() frees the per-CPU txrx_stats before unregister_netdev(). +Until unregister completes, ndo_get_stats64, the NAPI/xmit data path, +and nv_close()/drain may still access txrx_stats, leading to a +use-after-free. + +Free the stats only after unregister_netdev(). + +Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Reviewed-by: Zhu Yanjun +Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/nvidia/forcedeth.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c +index 19aa1f1538aa3..c09fb7694facb 100644 +--- a/drivers/net/ethernet/nvidia/forcedeth.c ++++ b/drivers/net/ethernet/nvidia/forcedeth.c +@@ -6187,10 +6187,10 @@ static void nv_remove(struct pci_dev *pci_dev) + struct net_device *dev = pci_get_drvdata(pci_dev); + struct fe_priv *np = netdev_priv(dev); + +- free_percpu(np->txrx_stats); +- + unregister_netdev(dev); + ++ free_percpu(np->txrx_stats); ++ + nv_restore_mac_addr(pci_dev); + + /* restore any phy related changes */ +-- +2.53.0 + diff --git a/queue-6.18/fprobe-fix-module-reference-count-leak-on-error-in-r.patch b/queue-6.18/fprobe-fix-module-reference-count-leak-on-error-in-r.patch new file mode 100644 index 0000000000..cfe05fbf46 --- /dev/null +++ b/queue-6.18/fprobe-fix-module-reference-count-leak-on-error-in-r.patch @@ -0,0 +1,48 @@ +From 39de6e32b86aec489bfa25fea7e3785f3294bc49 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Jul 2026 08:27:33 +0900 +Subject: fprobe: Fix module reference count leak on error in register_fprobe() + +From: Masami Hiramatsu (Google) + +[ Upstream commit 8cf2f40ceb85047ad8a84dffae3bebc9fed18216 ] + +In register_fprobe(), get_ips_from_filter() resolves target function +addresses and increments module reference counts via try_module_get() for +symbols in kernel modules. If get_ips_from_filter() fails on the second +pass and returns an error, register_fprobe() returned directly without +releasing module references acquired up to that point. + +Fix this by ensuring the cleanup loop executing module_put() runs even when +get_ips_from_filter() returns a negative error. + +Link: https://lore.kernel.org/all/178528125360.101985.4144133640239273153.stgit@devnote2/ + +Fixes: d24fa977eec5 ("tracing: fprobe: Fix to lock module while registering fprobe") +Assisted-by: Antigravity:gemini-3.6-flash +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Sasha Levin +--- + kernel/trace/fprobe.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c +index 01f98f8a86e60..1c9ae332afad1 100644 +--- a/kernel/trace/fprobe.c ++++ b/kernel/trace/fprobe.c +@@ -868,10 +868,8 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter + return -ENOMEM; + + ret = get_ips_from_filter(filter, notfilter, addrs, mods, num); +- if (ret < 0) +- return ret; +- +- ret = register_fprobe_ips(fp, addrs, ret); ++ if (ret >= 0) ++ ret = register_fprobe_ips(fp, addrs, ret); + + for (int i = 0; i < num; i++) { + if (mods[i]) +-- +2.53.0 + diff --git a/queue-6.18/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch b/queue-6.18/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch new file mode 100644 index 0000000000..ff3b89b1e8 --- /dev/null +++ b/queue-6.18/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch @@ -0,0 +1,55 @@ +From f111ea9d0a481626e0340cb96bb6c55165832321 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 13:23:08 +0530 +Subject: gpio: sloppy-logic-analyzer: Fix memory leak in gpio_la_poll_probe() + +From: Abdun Nihaal + +[ Upstream commit 7a7baebd9f23ba4f24796775472b2fd00dcd95d9 ] + +The memory allocated for priv->blob.data is not freed in the error paths +that follow the fops_buf_size_set() call in gpio_la_poll_probe(), as +well as in the remove function. Fix that by using device managed action +to free the memory on remove. + +Fixes: 7828b7bbbf20 ("gpio: add sloppy logic analyzer using polling") +Signed-off-by: Abdun Nihaal +Reviewed-by: Wolfram Sang +Link: https://patch.msgid.link/20260715075311.527753-1-nihaal@cse.iitm.ac.in +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-sloppy-logic-analyzer.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c +index 969dddd3d6faf..0f4a6228a7488 100644 +--- a/drivers/gpio/gpio-sloppy-logic-analyzer.c ++++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c +@@ -161,6 +161,13 @@ static int fops_buf_size_get(void *data, u64 *val) + return 0; + } + ++static void fops_buf_release(void *data) ++{ ++ struct gpio_la_poll_priv *priv = data; ++ ++ vfree(priv->blob.data); ++} ++ + static int fops_buf_size_set(void *data, u64 val) + { + struct gpio_la_poll_priv *priv = data; +@@ -239,6 +246,9 @@ static int gpio_la_poll_probe(struct platform_device *pdev) + return ret; + + fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE); ++ ret = devm_add_action_or_reset(dev, fops_buf_release, priv); ++ if (ret) ++ return ret; + + priv->descs = devm_gpiod_get_array(dev, "probe", GPIOD_IN); + if (IS_ERR(priv->descs)) +-- +2.53.0 + diff --git a/queue-6.18/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-6.18/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch new file mode 100644 index 0000000000..3e174e2806 --- /dev/null +++ b/queue-6.18/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch @@ -0,0 +1,51 @@ +From 7e867e200107fe0decd1b85a45ba67cfb2e35a90 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:19 -0300 +Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread + +From: Luiz Angelo Daros de Luca + +[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ] + +When userspace configures 'auto_update_interval' to 0 via sysfs, the +background kthread executes schedule_timeout_interruptible(0), which +returns immediately. + +If 'num_temp_sensors' is concurrently or previously set to 0, the +msleep_interruptible() delay inside adt7470_read_temperatures() also +becomes 0. This combination forces the background thread into a tight, +unbounded busy-loop, hogging the CPU and flooding the I2C bus with a +continuous stream of transactions. + +Fix this vulnerability by raising the lower limit of the clamp_val in +auto_update_interval_store() from 0 to 500 milliseconds. This guarantees +a reasonable minimum sleep window between sensor updates, protecting the +system from intentional or accidental I2C bus denial of service. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org +Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 06b19fd382457..a927e0b6d3319 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev, + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + +- temp = clamp_val(temp, 0, 60000); ++ temp = clamp_val(temp, 500, 60000); + + mutex_lock(&data->lock); + data->auto_update_interval = temp; +-- +2.53.0 + diff --git a/queue-6.18/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch b/queue-6.18/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch new file mode 100644 index 0000000000..cd5476cb0c --- /dev/null +++ b/queue-6.18/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch @@ -0,0 +1,83 @@ +From 6439755e104baf4c587e6c45d0f512369f7a53f6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:18 -0300 +Subject: hwmon: (adt7470) Fix cache updated before hardware write on I2C error + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ] + +adt7470_temp_write() and adt7470_pwm_write() update the driver's +cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing +the corresponding regmap_write(), and never check whether the write +succeeded before committing that update. If the I2C transaction fails, +the function correctly propagates the error to the caller, but the cache +silently keeps the new value, which was never actually applied to the +hardware. Subsequent reads then report a value that does not match the +device state. + +Reorder both write paths to update the cache only after a successful +regmap_write(), so the cache always reflects what was actually +written to the hardware. + +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index ef8f411df61be..06b19fd382457 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va + switch (attr) { + case hwmon_temp_min: + mutex_lock(&data->lock); +- data->temp_min[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val); ++ if (!err) ++ data->temp_min[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_temp_max: + mutex_lock(&data->lock); +- data->temp_max[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val); ++ if (!err) ++ data->temp_max[channel] = val; + mutex_unlock(&data->lock); + break; + default: +@@ -831,9 +833,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + case hwmon_pwm_input: + val = clamp_val(val, 0, 255); + mutex_lock(&data->lock); +- data->pwm[channel] = val; + err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), +- data->pwm[channel]); ++ val); ++ if (!err) ++ data->pwm[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_pwm_enable: +@@ -847,10 +850,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + val--; + + mutex_lock(&data->lock); +- data->pwm_automatic[channel] = val; + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel), + pwm_auto_reg_mask, + val ? pwm_auto_reg_mask : 0); ++ if (!err) ++ data->pwm_automatic[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_pwm_freq: +-- +2.53.0 + diff --git a/queue-6.18/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch b/queue-6.18/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch new file mode 100644 index 0000000000..7d7b418ba0 --- /dev/null +++ b/queue-6.18/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch @@ -0,0 +1,80 @@ +From 22178441dc45a1187c78cb3656088a5f83654abf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:23 -0300 +Subject: hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ] + +If the fan data becomes 0 between the FAN_DATA_VALID() check and the +FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash +due to a race with a concurrent update of the cached fan value. + +Fix a TOCTOU issue by reading fan data once. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/ +Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 23 ++++++++++------------- + 1 file changed, 10 insertions(+), 13 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 43c0130342622..3a2d408a45be8 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev, + static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val) + { + struct adt7470_data *data = adt7470_update_device(dev); ++ u16 fan_data; + + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (attr) { + case hwmon_fan_input: +- if (FAN_DATA_VALID(data->fan[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan[channel]); + break; + case hwmon_fan_min: +- if (FAN_DATA_VALID(data->fan_min[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan_min[channel]); + break; + case hwmon_fan_max: +- if (FAN_DATA_VALID(data->fan_max[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan_max[channel]); + break; + case hwmon_fan_alarm: + *val = !!(data->alarm & FAN_ALARM_BIT(channel)); +- break; ++ return 0; + default: + return -EOPNOTSUPP; + } + ++ if (FAN_DATA_VALID(fan_data)) ++ *val = FAN_PERIOD_TO_RPM(fan_data); ++ else ++ *val = 0; ++ + return 0; + } + +-- +2.53.0 + diff --git a/queue-6.18/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch b/queue-6.18/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch new file mode 100644 index 0000000000..1b990c2626 --- /dev/null +++ b/queue-6.18/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch @@ -0,0 +1,115 @@ +From 9f44cde625696d9e37a4948a8a7eff45b2202334 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:17 -0300 +Subject: hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ] + +During adt7470_read_temperatures(), the driver temporarily switches +the PWM channels to manual mode, performs the temperature collection, +and then restores the original configuration registers. + +However, if an I2C transaction fails at any point after entering manual +mode, the function aborts and returns immediately. This leaves the +configuration registers un-restored, permanently trapping the fans in +manual mode. + +Introduce a recovery path to ensure that the original PWM configuration +registers are always restored, even when intermediate I2C operations +fail. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++----------- + 1 file changed, 29 insertions(+), 11 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index dbee6926fa055..ef8f411df61be 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in + /* Probe for temperature sensors. Assumes lock is held */ + static int adt7470_read_temperatures(struct adt7470_data *data) + { +- unsigned long res; ++ struct device *dev = regmap_get_device(data->regmap); ++ u8 pwm[ADT7470_FAN_COUNT]; + unsigned int pwm_cfg[2]; +- int err; ++ unsigned long res; ++ int err, err2; + int i; +- u8 pwm[ADT7470_FAN_COUNT]; + + /* save pwm[1-4] config register */ + err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); +@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data) + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), + ADT7470_PWM_AUTO_MASK, 0); + if (err < 0) +- return err; ++ goto out_restore; + + /* write pwm control to whatever it was */ + err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], + ADT7470_PWM_COUNT); + if (err < 0) +- return err; ++ goto out_restore; + + /* start reading temperature sensors */ + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); + if (err < 0) +- return err; ++ goto out_restore; + + /* Delay is 200ms * number of temp sensors. */ + res = msleep_interruptible((data->num_temp_sensors >= 0 ? +@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data) + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, 0); + if (err < 0) +- return err; ++ goto out_restore; + ++out_restore: + /* restore pwm[1-4] config registers */ +- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); +- if (err < 0) +- return err; +- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); ++ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); ++ if (err2 < 0) { ++ dev_warn_ratelimited(dev, ++ "failed to restore PWM{1,2} config (%d)\n", ++ err2); ++ ++ if (!err) ++ err = err2; ++ } ++ ++ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); ++ if (err2 < 0) { ++ dev_warn_ratelimited(dev, ++ "failed to restore PWM{3,4} config (%d)\n", ++ err2); ++ ++ if (!err) ++ err = err2; ++ } ++ + if (err < 0) + return err; + +-- +2.53.0 + diff --git a/queue-6.18/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-6.18/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch new file mode 100644 index 0000000000..70ee6d4b1c --- /dev/null +++ b/queue-6.18/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch @@ -0,0 +1,57 @@ +From 6a1b038e5eef578c5d9101695f4b921e9d773421 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:24 -0300 +Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ] + +In pwm_auto_temp_store(), the parsed user input was missing bounds +checks, allowing values > 0xF to overflow into the adjacent channel's +bits. Furthermore, the value was being incorrectly written to the +pwm_automatic state array instead of pwm_auto_temp. + +Fix this by rejecting values > 0xF with -EINVAL, and assigning the +value to the correct array only after a successful I2C write. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 3a2d408a45be8..0fad4bfe53df6 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -1049,8 +1049,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + if (temp < 0) + return temp; + ++ if (temp > 0xF) ++ return -EINVAL; ++ + mutex_lock(&data->lock); +- data->pwm_automatic[attr->index] = temp; + + if (!(attr->index % 2)) { + mask = 0xF0; +@@ -1061,6 +1063,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + } + + err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); ++ if (!err) ++ data->pwm_auto_temp[attr->index] = temp; ++ + mutex_unlock(&data->lock); + + return err < 0 ? err : count; +-- +2.53.0 + diff --git a/queue-6.18/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-6.18/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch new file mode 100644 index 0000000000..71efc9f3a1 --- /dev/null +++ b/queue-6.18/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch @@ -0,0 +1,62 @@ +From 6b0c80e1551f71a1560fceddabf47735e351d7cb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:20 -0300 +Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks + +From: Luiz Angelo Daros de Luca + +[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ] + +The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are +currently defined with swapped bit values. + +According to Table 22 of the ADT7470 datasheet, the Fan Control Mode +Configuration for register 0x69 follows the exact same bit position +layout as register 0x68: +- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80 +- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40 +- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80 +- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40 + +Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40. + +This typo did not cause any functional bugs because these specific +macros are never referenced in the driver code. Instead, the driver +correctly applies the configuration by relying on the modulo parity of +the channel index (e.g., `channel % 2`) to selectively apply either +ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40). +Since the bit layout is identical between the two configuration +registers, the hardware is currently configured correctly. + +Fix the macro definitions to reflect the datasheet accurately and +prevent future bugs or confusion during code review and refactoring. +As this is a purely cosmetic fix with no functional impact, a backport +to stable kernels is not necessary. + +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 41fa6a0e8b937..f0a45538ded6d 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + #define ADT7470_PWM1_AUTO_MASK 0x80 + #define ADT7470_PWM_AUTO_MASK 0xC0 + #define ADT7470_REG_PWM34_CFG 0x69 +-#define ADT7470_PWM3_AUTO_MASK 0x40 +-#define ADT7470_PWM4_AUTO_MASK 0x80 ++#define ADT7470_PWM4_AUTO_MASK 0x40 ++#define ADT7470_PWM3_AUTO_MASK 0x80 + #define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A + #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D + #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E +-- +2.53.0 + diff --git a/queue-6.18/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch b/queue-6.18/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch new file mode 100644 index 0000000000..f679da1ad9 --- /dev/null +++ b/queue-6.18/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch @@ -0,0 +1,71 @@ +From 534115e7f4e2fce9d5ec1b0b2b79b10d6fc9b303 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:21 -0300 +Subject: hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read() + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ] + +During the conversion the alarm callback started interpreting the +channel index as an alarm bitmask, resulting in incorrect alarm +reporting. Compute the proper alarm bit instead. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org +Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 19 +++++++++++++++++-- + 1 file changed, 17 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index a927e0b6d3319..41fa6a0e8b937 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + + #define ALARM2(x) ((x) << 8) + ++/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and ++ * 0x72, or BIT(0)..BIT(6) of data->alarm. ++ * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and ++ * 0x73, or BIT(8)..BIT(10) of data->alarm. ++ */ ++#define TEMP_ALARM_BIT(ch) ({ \ ++ typeof(ch) _ch = (ch); \ ++ (1 << (_ch < 7 ? _ch : _ch + 1)); \ ++}) ++ ++/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in ++ * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm. ++ */ ++#define FAN_ALARM_BIT(ch) (1 << (12 + (ch))) ++ + #define ADT7470_VENDOR 0x41 + #define ADT7470_DEVICE 0x70 + /* datasheet only mentions a revision 2 */ +@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va + *val = 1000 * data->temp_max[channel]; + break; + case hwmon_temp_alarm: +- *val = !!(data->alarm & channel); ++ *val = !!(data->alarm & TEMP_ALARM_BIT(channel)); + break; + default: + return -EOPNOTSUPP; +@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val + *val = 0; + break; + case hwmon_fan_alarm: +- *val = !!(data->alarm & (1 << (12 + channel))); ++ *val = !!(data->alarm & FAN_ALARM_BIT(channel)); + break; + default: + return -EOPNOTSUPP; +-- +2.53.0 + diff --git a/queue-6.18/hwmon-adt7470-use-cached-pwm-frequency-value.patch b/queue-6.18/hwmon-adt7470-use-cached-pwm-frequency-value.patch new file mode 100644 index 0000000000..4c374453b8 --- /dev/null +++ b/queue-6.18/hwmon-adt7470-use-cached-pwm-frequency-value.patch @@ -0,0 +1,111 @@ +From b46d6418cb50c31c30b14d112388a9588a3d31a7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:22 -0300 +Subject: hwmon: (adt7470) Use cached PWM frequency value + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ] + +adt7470_pwm_read() currently ignores failures returned by +pwm1_freq_get(). If the register read fails, the negative error code is +returned through *val while the function itself reports success, +potentially exposing a negative PWM frequency through sysfs. + +Fix this by using the cached PWM frequency maintained by the driver, +eliminating the register access from the read path. + +Apart from the corrected error propagation and using the cached value, +no functional change is intended. + +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index f0a45538ded6d..43c0130342622 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -182,6 +182,7 @@ struct adt7470_data { + u8 pwm_min[ADT7470_PWM_COUNT]; + s8 pwm_tmin[ADT7470_PWM_COUNT]; + u8 pwm_auto_temp[ADT7470_PWM_COUNT]; ++ u32 pwm_freq; + + struct task_struct *auto_update; + unsigned int auto_update_interval; +@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev, + } + + /* These are the valid PWM frequencies to the nearest Hz */ +-static const int adt7470_freq_map[] = { ++static const u32 adt7470_freq_map[] = { + 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 + }; + +@@ -796,7 +797,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val + *val = 1 + data->pwm_automatic[channel]; + break; + case hwmon_pwm_freq: +- *val = pwm1_freq_get(dev); ++ *val = data->pwm_freq; + break; + default: + return -EOPNOTSUPP; +@@ -809,12 +810,14 @@ static int pwm1_freq_set(struct device *dev, long freq) + { + struct adt7470_data *data = dev_get_drvdata(dev); + unsigned int low_freq = ADT7470_CFG_LF; ++ u32 closest_freq; + int index; + int err; + + /* Round the user value given to the closest available frequency */ + index = find_closest(freq, adt7470_freq_map, + ARRAY_SIZE(adt7470_freq_map)); ++ closest_freq = adt7470_freq_map[index]; + + if (index >= 8) { + index -= 8; +@@ -832,6 +835,10 @@ static int pwm1_freq_set(struct device *dev, long freq) + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, + ADT7470_FREQ_MASK, + index << ADT7470_FREQ_SHIFT); ++ if (err < 0) ++ goto out; ++ ++ data->pwm_freq = closest_freq; + out: + mutex_unlock(&data->lock); + +@@ -1285,6 +1292,7 @@ static int adt7470_probe(struct i2c_client *client) + struct device *dev = &client->dev; + struct adt7470_data *data; + struct device *hwmon_dev; ++ int freq_val; + int err; + + data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); +@@ -1309,6 +1317,14 @@ static int adt7470_probe(struct i2c_client *client) + if (err < 0) + return err; + ++ freq_val = pwm1_freq_get(dev); ++ if (freq_val <= 0) { ++ err = freq_val < 0 ? freq_val : -EINVAL; ++ return err; ++ } ++ ++ data->pwm_freq = (u32)freq_val; ++ + /* Register sysfs hooks */ + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &adt7470_chip_info, +-- +2.53.0 + diff --git a/queue-6.18/hwmon-ina2xx-add-support-for-ina234.patch b/queue-6.18/hwmon-ina2xx-add-support-for-ina234.patch new file mode 100644 index 0000000000..664abb87dc --- /dev/null +++ b/queue-6.18/hwmon-ina2xx-add-support-for-ina234.patch @@ -0,0 +1,133 @@ +From f7ca6478d5aa2bc1b480fd5ae4074b7f151cd49c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 20 Feb 2026 13:20:22 +0200 +Subject: hwmon: (ina2xx) Add support for INA234 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Ian Ray + +[ Upstream commit 88a928eebccdc5445d874ddcbf1683b76c9f1431 ] + +INA234 is register compatible to INA226 (excepting manufacturer and die +or device id registers) but has different scaling. + +Signed-off-by: Ian Ray +Reviewed-by: Bence Csókás # v2 +Tested-by: Jens Almer +Tested-by: Jonas Rebmann +Link: https://lore.kernel.org/r/20260220112024.97446-4-ian.ray@gehealthcare.com +Signed-off-by: Guenter Roeck +Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues") +Signed-off-by: Sasha Levin +--- + Documentation/hwmon/ina2xx.rst | 13 ++++++++++++- + drivers/hwmon/Kconfig | 2 +- + drivers/hwmon/ina2xx.c | 18 ++++++++++++++++++ + 3 files changed, 31 insertions(+), 2 deletions(-) + +diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst +index a4ddf4bd2b081..d64e7af46a124 100644 +--- a/Documentation/hwmon/ina2xx.rst ++++ b/Documentation/hwmon/ina2xx.rst +@@ -74,6 +74,16 @@ Supported chips: + https://us1.silergy.com/ + + ++ * Texas Instruments INA234 ++ ++ Prefix: 'ina234' ++ ++ Addresses: I2C 0x40 - 0x43 ++ ++ Datasheet: Publicly available at the Texas Instruments website ++ ++ https://www.ti.com/ ++ + Author: Lothar Felten + + Description +@@ -89,7 +99,7 @@ interface. The INA220 monitors both shunt drop and supply voltage. + The INA226 is a current shunt and power monitor with an I2C interface. + The INA226 monitors both a shunt voltage drop and bus supply voltage. + +-INA230 and INA231 are high or low side current shunt and power monitors ++INA230, INA231, and INA234 are high or low side current shunt and power monitors + with an I2C interface. The chips monitor both a shunt voltage drop and + bus supply voltage. + +@@ -132,6 +142,7 @@ Additional entries are available for the following chips: + * ina226 + * ina230 + * ina231 ++ * ina234 + * ina260 + * sy24655 + +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +index c8a7e45e8006c..4d852e68b4749 100644 +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -2275,7 +2275,7 @@ config SENSORS_INA2XX + select REGMAP_I2C + help + If you say yes here you get support for INA219, INA220, INA226, +- INA230, INA231, INA260, and SY24655 power monitor chips. ++ INA230, INA231, INA234, INA260, and SY24655 power monitor chips. + + The INA2xx driver is configured for the default configuration of + the part as described in the datasheet. +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +index afd0018a17e03..e07f24901bfbb 100644 +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -138,6 +138,7 @@ static const struct regmap_config ina2xx_regmap_config = { + enum ina2xx_ids { + ina219, + ina226, ++ ina234, + ina260, + sy24655 + }; +@@ -192,6 +193,18 @@ static const struct ina2xx_config ina2xx_config[] = { + .has_power_average = false, + .has_update_interval = true, + }, ++ [ina234] = { ++ .config_default = INA226_CONFIG_DEFAULT, ++ .calibration_value = 2048, ++ .shunt_div = 400, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */ ++ .bus_voltage_shift = 4, ++ .bus_voltage_lsb = 25600, ++ .power_lsb_factor = 32, ++ .has_alerts = true, ++ .has_ishunt = false, ++ .has_power_average = false, ++ .has_update_interval = true, ++ }, + [ina260] = { + .config_default = INA260_CONFIG_DEFAULT, + .shunt_div = 400, +@@ -1000,6 +1013,7 @@ static const struct i2c_device_id ina2xx_id[] = { + { "ina226", ina226 }, + { "ina230", ina226 }, + { "ina231", ina226 }, ++ { "ina234", ina234 }, + { "ina260", ina260 }, + { "sy24655", sy24655 }, + { } +@@ -1031,6 +1045,10 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = { + .compatible = "ti,ina231", + .data = (void *)ina226 + }, ++ { ++ .compatible = "ti,ina234", ++ .data = (void *)ina234 ++ }, + { + .compatible = "ti,ina260", + .data = (void *)ina260 +-- +2.53.0 + diff --git a/queue-6.18/hwmon-ina2xx-fix-various-overflow-issues.patch b/queue-6.18/hwmon-ina2xx-fix-various-overflow-issues.patch new file mode 100644 index 0000000000..356072e6b9 --- /dev/null +++ b/queue-6.18/hwmon-ina2xx-fix-various-overflow-issues.patch @@ -0,0 +1,156 @@ +From 3415ad3f4779b000b072760c670c3d4f9cb61373 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 10 Jun 2026 07:46:16 -0700 +Subject: hwmon: (ina2xx) Fix various overflow issues + +From: Guenter Roeck + +[ Upstream commit e6c80061ca239f45c0eaf7e47a91d6d6df9bd636 ] + +Sashiko reports several integer overflow problems in the ina2xx driver +caused by unbounded multiplications and inadequate types for intermediate +calculations. + +Specifically: +- In ina2xx_get_value(), the return type is changed from int to long. + Intermediate calculations for current are now performed using 64-bit + types to prevent 32-bit integer overflow before the division by 1000. +- When calculating power in ina2xx_get_value() and + sy24655_average_power_read(), interim values are cast to u64 and clamped + to LONG_MAX. This prevents overflow when regval or accumulator_24 is + multiplied by power_lsb_uW. +- In ina226_alert_to_reg(), the clamping logic is rewritten using min_t(). + This safely avoids integer overflows when scaling user-provided values + for shunt voltage, bus voltage, power, and current limits. + +Cc: Loic Poulain +Fixes: ab7fbee452be ("hwmon: (ina2xx) Fix various overflow issues") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/ina2xx.c | 61 ++++++++++++++++++++++++------------------ + 1 file changed, 35 insertions(+), 26 deletions(-) + +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +index a46a118406726..9c2dc9c0fd4d5 100644 +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -283,30 +284,34 @@ static u16 ina226_interval_to_reg(long interval) + return FIELD_PREP(INA226_AVG_RD_MASK, avg_bits); + } + +-static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, +- unsigned int regval) ++static long ina2xx_get_value(struct ina2xx_data *data, u8 reg, ++ unsigned int regval) + { +- int val; ++ s64 val64; ++ long val; + + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: + /* signed register */ +- val = (s16)regval >> data->config->shunt_voltage_shift; +- val = DIV_ROUND_CLOSEST(val, data->config->shunt_div); ++ val = DIV_ROUND_CLOSEST((s16)regval >> data->config->shunt_voltage_shift, ++ data->config->shunt_div); + break; + case INA2XX_BUS_VOLTAGE: +- val = (regval >> data->config->bus_voltage_shift) * +- data->config->bus_voltage_lsb; +- val = DIV_ROUND_CLOSEST(val, 1000); ++ val = DIV_ROUND_CLOSEST((regval >> data->config->bus_voltage_shift) * ++ data->config->bus_voltage_lsb, 1000); + break; + case INA2XX_POWER: +- val = regval * data->power_lsb_uW; ++ val = min_t(u64, (u64)regval * data->power_lsb_uW, LONG_MAX); + break; + case INA2XX_CURRENT: + /* signed register, result in mA */ +- val = ((s16)regval >> data->config->current_shift) * ++ val64 = (s64)((s16)regval >> data->config->current_shift) * + data->current_lsb_uA; +- val = DIV_ROUND_CLOSEST(val, 1000); ++ if (val64 < 0) ++ val64 = -DIV_ROUND_CLOSEST_ULL(-val64, 1000); ++ else ++ val64 = DIV_ROUND_CLOSEST_ULL(val64, 1000); ++ val = clamp_val(val64, LONG_MIN, LONG_MAX); + break; + case INA2XX_CALIBRATION: + val = regval; +@@ -395,27 +400,29 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val) + */ + static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) + { ++ long limit; ++ + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: +- val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div); +- val *= data->config->shunt_div; +- val <<= data->config->shunt_voltage_shift; +- return clamp_val(val, 0, SHRT_MAX); ++ val = min_t(long, val, DIV_ROUND_CLOSEST(SHRT_MAX, data->config->shunt_div)); ++ return min_t(long, (val * data->config->shunt_div) << data->config->shunt_voltage_shift, ++ SHRT_MAX); + case INA2XX_BUS_VOLTAGE: +- val = clamp_val(val, 0, 200000); +- val = (val * 1000) << data->config->bus_voltage_shift; +- val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb); +- return clamp_val(val, 0, USHRT_MAX); ++ val = min_t(long, val, 130000); ++ return min_t(long, ++ DIV_ROUND_CLOSEST((val * 1000) << data->config->bus_voltage_shift, ++ data->config->bus_voltage_lsb), ++ USHRT_MAX); + case INA2XX_POWER: +- val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW); +- val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW); +- return clamp_val(val, 0, USHRT_MAX); ++ val = min_t(long, val, LONG_MAX - data->power_lsb_uW); ++ return min_t(long, DIV_ROUND_CLOSEST(val, data->power_lsb_uW), USHRT_MAX); + case INA2XX_CURRENT: +- val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000); ++ limit = (LONG_MAX - (data->current_lsb_uA / 2)) / 1000; ++ val = min_t(long, val, limit); + /* signed register, result in mA */ + val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA); +- val <<= data->config->current_shift; +- return clamp_val(val, SHRT_MIN, SHRT_MAX); ++ limit = SHRT_MAX >> data->config->current_shift; ++ return (u16)(min_t(long, val, limit) << data->config->current_shift); + default: + /* programmer goofed */ + WARN_ON_ONCE(1); +@@ -560,6 +567,7 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va + u8 template[6]; + int ret; + long accumulator_24, sample_count; ++ u64 val64; + + /* 48-bit register read */ + ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template); +@@ -578,7 +586,8 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va + return 0; + } + +- *val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; ++ val64 = (u64)DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; ++ *val = min_t(u64, val64, LONG_MAX); + + return 0; + } +-- +2.53.0 + diff --git a/queue-6.18/hwmon-ina2xx-make-it-easier-to-add-more-devices.patch b/queue-6.18/hwmon-ina2xx-make-it-easier-to-add-more-devices.patch new file mode 100644 index 0000000000..5c7bc01e5b --- /dev/null +++ b/queue-6.18/hwmon-ina2xx-make-it-easier-to-add-more-devices.patch @@ -0,0 +1,133 @@ +From 7f3e8e729a6da94a8648b206710546b8a79f10b1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 20 Feb 2026 13:20:21 +0200 +Subject: hwmon: (ina2xx) Make it easier to add more devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Ian Ray + +[ Upstream commit f6e14b5bcabf4ee97a2d535c3c2d7e72c8da4c15 ] + +* Make sysfs entries documentation easier to maintain. +* Use multi-line enum. +* Correct "has_power_average" comment. + +Create a new "has_update_interval" member for chips which support +averaging. + +Signed-off-by: Ian Ray +Reviewed-by: Bence Csókás # v2 +Tested-by: Jens Almer +Link: https://lore.kernel.org/r/20260220112024.97446-3-ian.ray@gehealthcare.com +Signed-off-by: Guenter Roeck +Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues") +Signed-off-by: Sasha Levin +--- + Documentation/hwmon/ina2xx.rst | 12 ++++++++++-- + drivers/hwmon/ina2xx.c | 18 ++++++++++++++---- + 2 files changed, 24 insertions(+), 6 deletions(-) + +diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst +index a3860aae444c0..a4ddf4bd2b081 100644 +--- a/Documentation/hwmon/ina2xx.rst ++++ b/Documentation/hwmon/ina2xx.rst +@@ -124,8 +124,16 @@ power1_input Power(uW) measurement channel + shunt_resistor Shunt resistance(uOhm) channel (not for ina260) + ======================= =============================================== + +-Additional sysfs entries for ina226, ina230, ina231, ina260, and sy24655 +------------------------------------------------------------------------- ++Additional sysfs entries ++------------------------ ++ ++Additional entries are available for the following chips: ++ ++ * ina226 ++ * ina230 ++ * ina231 ++ * ina260 ++ * sy24655 + + ======================= ==================================================== + curr1_lcrit Critical low current +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +index bc3c1f7314b3e..afd0018a17e03 100644 +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -135,13 +135,19 @@ static const struct regmap_config ina2xx_regmap_config = { + .writeable_reg = ina2xx_writeable_reg, + }; + +-enum ina2xx_ids { ina219, ina226, ina260, sy24655 }; ++enum ina2xx_ids { ++ ina219, ++ ina226, ++ ina260, ++ sy24655 ++}; + + struct ina2xx_config { + u16 config_default; + bool has_alerts; /* chip supports alerts and limits */ + bool has_ishunt; /* chip has internal shunt resistor */ +- bool has_power_average; /* chip has internal shunt resistor */ ++ bool has_power_average; /* chip supports average power */ ++ bool has_update_interval; + int calibration_value; + int shunt_div; + int bus_voltage_shift; +@@ -172,6 +178,7 @@ static const struct ina2xx_config ina2xx_config[] = { + .has_alerts = false, + .has_ishunt = false, + .has_power_average = false, ++ .has_update_interval = false, + }, + [ina226] = { + .config_default = INA226_CONFIG_DEFAULT, +@@ -183,6 +190,7 @@ static const struct ina2xx_config ina2xx_config[] = { + .has_alerts = true, + .has_ishunt = false, + .has_power_average = false, ++ .has_update_interval = true, + }, + [ina260] = { + .config_default = INA260_CONFIG_DEFAULT, +@@ -193,6 +201,7 @@ static const struct ina2xx_config ina2xx_config[] = { + .has_alerts = true, + .has_ishunt = true, + .has_power_average = false, ++ .has_update_interval = true, + }, + [sy24655] = { + .config_default = SY24655_CONFIG_DEFAULT, +@@ -204,6 +213,7 @@ static const struct ina2xx_config ina2xx_config[] = { + .has_alerts = true, + .has_ishunt = false, + .has_power_average = true, ++ .has_update_interval = false, + }, + }; + +@@ -713,7 +723,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type + const struct ina2xx_data *data = _data; + bool has_alerts = data->config->has_alerts; + bool has_power_average = data->config->has_power_average; +- enum ina2xx_ids chip = data->chip; ++ bool has_update_interval = data->config->has_update_interval; + + switch (type) { + case hwmon_in: +@@ -775,7 +785,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: +- if (chip == ina226 || chip == ina260) ++ if (has_update_interval) + return 0644; + break; + default: +-- +2.53.0 + diff --git a/queue-6.18/hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch b/queue-6.18/hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch new file mode 100644 index 0000000000..9e45aa3b30 --- /dev/null +++ b/queue-6.18/hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch @@ -0,0 +1,163 @@ +From bee38cf8d68ebf9122d1605337c846fe9956fc08 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 3 Mar 2026 12:07:02 +0100 +Subject: hwmon: (ina2xx) Shift INA234 shunt and current registers + +From: Jonas Rebmann + +[ Upstream commit eeca1114d1e2cc0eacaebb80f3f2afbaebfc60be ] + +The INA219 has the lowest three bits of the bus voltage register +zero-reserved, the bus_voltage_shift ina2xx_config field was introduced +to accommodate for that. + +The INA234 has four bits of the bus voltage, of the shunt voltage, and +of the current registers zero-reserved but the latter two were +implemented by choosing a 16x higher shunt_div instead of a separate +field specifying a bit shift. + +This is possible because shunt voltage and current are divided by +shunt_div, hence a 16x higher shunt_div results in a 16x smaller LSB for +both the shunt voltage and the current register, perfectly accounting +for the missing bit shift. + +For consistency and correctness, account for the reserved bits via +shunt_voltage_shift and current_shift configuration fields as already +done for voltage registers and use the conversion constants given in the +INA234 datasheet. + +Signed-off-by: Jonas Rebmann +Link: https://lore.kernel.org/r/20260303-ina234-shift-v1-2-318c33ac4480@pengutronix.de +Signed-off-by: Guenter Roeck +Stable-dep-of: e6c80061ca23 ("hwmon: (ina2xx) Fix various overflow issues") +Signed-off-by: Sasha Levin +--- + drivers/hwmon/ina2xx.c | 22 +++++++++++++++++++--- + 1 file changed, 19 insertions(+), 3 deletions(-) + +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +index e07f24901bfbb..a46a118406726 100644 +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -151,9 +151,11 @@ struct ina2xx_config { + bool has_update_interval; + int calibration_value; + int shunt_div; ++ int shunt_voltage_shift; + int bus_voltage_shift; + int bus_voltage_lsb; /* uV */ + int power_lsb_factor; ++ int current_shift; + }; + + struct ina2xx_data { +@@ -173,59 +175,69 @@ static const struct ina2xx_config ina2xx_config[] = { + .config_default = INA219_CONFIG_DEFAULT, + .calibration_value = 4096, + .shunt_div = 100, ++ .shunt_voltage_shift = 0, + .bus_voltage_shift = 3, + .bus_voltage_lsb = 4000, + .power_lsb_factor = 20, + .has_alerts = false, + .has_ishunt = false, + .has_power_average = false, ++ .current_shift = 0, + .has_update_interval = false, + }, + [ina226] = { + .config_default = INA226_CONFIG_DEFAULT, + .calibration_value = 2048, + .shunt_div = 400, ++ .shunt_voltage_shift = 0, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb_factor = 25, + .has_alerts = true, + .has_ishunt = false, + .has_power_average = false, ++ .current_shift = 0, + .has_update_interval = true, + }, + [ina234] = { + .config_default = INA226_CONFIG_DEFAULT, + .calibration_value = 2048, +- .shunt_div = 400, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */ ++ .shunt_div = 25, /* 2.5 µV/LSB raw ADC reading from INA2XX_SHUNT_VOLTAGE */ ++ .shunt_voltage_shift = 4, + .bus_voltage_shift = 4, + .bus_voltage_lsb = 25600, + .power_lsb_factor = 32, + .has_alerts = true, + .has_ishunt = false, + .has_power_average = false, ++ .current_shift = 4, + .has_update_interval = true, + }, + [ina260] = { + .config_default = INA260_CONFIG_DEFAULT, + .shunt_div = 400, ++ .shunt_voltage_shift = 0, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb_factor = 8, + .has_alerts = true, + .has_ishunt = true, + .has_power_average = false, ++ .current_shift = 0, + .has_update_interval = true, + }, + [sy24655] = { + .config_default = SY24655_CONFIG_DEFAULT, + .calibration_value = 4096, + .shunt_div = 400, ++ .shunt_voltage_shift = 0, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb_factor = 25, + .has_alerts = true, + .has_ishunt = false, + .has_power_average = true, ++ .current_shift = 0, + .has_update_interval = false, + }, + }; +@@ -279,7 +291,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: + /* signed register */ +- val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div); ++ val = (s16)regval >> data->config->shunt_voltage_shift; ++ val = DIV_ROUND_CLOSEST(val, data->config->shunt_div); + break; + case INA2XX_BUS_VOLTAGE: + val = (regval >> data->config->bus_voltage_shift) * +@@ -291,7 +304,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, + break; + case INA2XX_CURRENT: + /* signed register, result in mA */ +- val = (s16)regval * data->current_lsb_uA; ++ val = ((s16)regval >> data->config->current_shift) * ++ data->current_lsb_uA; + val = DIV_ROUND_CLOSEST(val, 1000); + break; + case INA2XX_CALIBRATION: +@@ -385,6 +399,7 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) + case INA2XX_SHUNT_VOLTAGE: + val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div); + val *= data->config->shunt_div; ++ val <<= data->config->shunt_voltage_shift; + return clamp_val(val, 0, SHRT_MAX); + case INA2XX_BUS_VOLTAGE: + val = clamp_val(val, 0, 200000); +@@ -399,6 +414,7 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) + val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000); + /* signed register, result in mA */ + val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA); ++ val <<= data->config->current_shift; + return clamp_val(val, SHRT_MIN, SHRT_MAX); + default: + /* programmer goofed */ +-- +2.53.0 + diff --git a/queue-6.18/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch b/queue-6.18/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch new file mode 100644 index 0000000000..e8d3eb0498 --- /dev/null +++ b/queue-6.18/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch @@ -0,0 +1,54 @@ +From 94fd25f1dffd0f1ba4ecbec547b8730509fdd697 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 15:27:28 -0700 +Subject: hwmon: (lm90) Only report alarms if driver is ready + +From: Guenter Roeck + +[ Upstream commit aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 ] + +Userspace can read sysfs attributes before driver registration is complete, +immediately after devm_hwmon_device_register_with_info() has been called. +At that time, data->hwmon_dev is not yet initialized. This can trigger +a NULL pointer access since lm90_update_device() and with it +lm90_update_alarms_locked() will be called. This call schedules +report_work and lm90_report_alarms(), which passes the still-NULL +data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer +dereference. + +Fix the problem by only scheduling the report and alert workers +data->hwmon_dev is set. + +Reported-by: Sashiko +Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/lm90.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c +index a465a8a7ef5af..e1795ccde876c 100644 +--- a/drivers/hwmon/lm90.c ++++ b/drivers/hwmon/lm90.c +@@ -1196,7 +1196,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) + check_enable = (client->irq || !(data->config_orig & 0x80)) && + (data->config & 0x80); + +- if (force || check_enable) ++ if (data->hwmon_dev && (force || check_enable)) + schedule_work(&data->report_work); + + /* +@@ -1204,7 +1204,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) + * alarms are all clear, and alerts are currently disabled. + * Otherwise (re)schedule worker if needed. + */ +- if (check_enable) { ++ if (check_enable && data->hwmon_dev) { + if (!(data->current_alarms & data->alert_alarms)) { + dev_dbg(&client->dev, "Re-enabling ALERT#\n"); + lm90_update_confreg(data, data->config & ~0x80); +-- +2.53.0 + diff --git a/queue-6.18/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch b/queue-6.18/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch new file mode 100644 index 0000000000..6ef2d36473 --- /dev/null +++ b/queue-6.18/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch @@ -0,0 +1,39 @@ +From 15cc8d5c81072885cd703c97ff0de71e5f8cc49e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 5 Feb 2025 12:27:15 -0800 +Subject: hwmon: (ltc4282) Fix reading the minimum alarm voltage + +From: Guenter Roeck + +[ Upstream commit 00feb1cce93dab948a299b69753d99c681d45a0b ] + +Coverity reports an out-of-bounds access when reading the minimum alarm +voltage for the VGPIO channel. Add the missing return statement to fix +the problem. + +Fixes: cbc29538dbf7 ("hwmon: Add driver for LTC4282") +Cc: Nuno Sa +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/ltc4282.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c +index 58c2d3a624329..4000bcbc7353d 100644 +--- a/drivers/hwmon/ltc4282.c ++++ b/drivers/hwmon/ltc4282.c +@@ -384,8 +384,8 @@ static int ltc4282_read_in(struct ltc4282_state *st, u32 attr, long *val, + channel, val); + case hwmon_in_min_alarm: + if (channel == LTC4282_CHAN_VGPIO) +- ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG, +- LTC4282_GPIO_ALARM_L_MASK, val); ++ return ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG, ++ LTC4282_GPIO_ALARM_L_MASK, val); + + return ltc4282_vdd_source_read_alm(st, + LTC4282_VSOURCE_ALARM_L_MASK, +-- +2.53.0 + diff --git a/queue-6.18/hwmon-nct6775-core-fix-number-of-temperature-registe.patch b/queue-6.18/hwmon-nct6775-core-fix-number-of-temperature-registe.patch new file mode 100644 index 0000000000..9cd6050196 --- /dev/null +++ b/queue-6.18/hwmon-nct6775-core-fix-number-of-temperature-registe.patch @@ -0,0 +1,90 @@ +From 9d79fec841fa41cb393c2140c67165598e831bfa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 07:14:36 -0700 +Subject: hwmon: (nct6775-core) Fix number of temperature registers for NCT6116 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guenter Roeck + +[ Upstream commit b0e8adb2ccb43009796897ced09f91636685c9d3 ] + +Unlike NCT6106, NCT6116 only has three temperature registers, and with +it only three temperature source and temperature source configuration +registers. The register addresses match those of NCT6106 and can be +re-used. + +The code used a separate array to list the temperature source registers +for NCT6116, but used the size of the NCT6106 register array to set +the number of registers. The NCT6106 register array provides six addresses, +while the temperature source register array for NCT6116 only provides three +addresses. This causes a KASAN report. + +BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775] +Read of size 2 at addr ffffffffc19561a6 by task modprobe/954 +... +Call Trace: + dump_stack+0x7d/0xa7 + print_address_description.constprop.0+0x1c/0x220 + ? __kasan_kmalloc.constprop.0+0xc9/0xd0 + ? __kmalloc_node_track_caller+0x194/0x5b0 + ? nct6775_probe+0x936/0x46f0 [nct6775] + ? nct6775_probe+0x936/0x46f0 [nct6775] +... + +Fix the problem by hard-coding the number of temperature and temperature +configuration registers to three for NCT6116. Drop the unnecessary +NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE. + +Reported-by: Florian Bezdeka +Closes: https://lore.kernel.org/linux-hwmon/57cfc3fa-d4e9-4c10-8aa7-4ad0af7ebebe@roeck-us.net/T/#t +Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") +Cc: Björn Gerhart +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index 79bc67ffb9986..506d57025c3bd 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -846,8 +846,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 }; + static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 }; + static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 }; + static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 }; +-static const u16 NCT6116_REG_TEMP_SOURCE[] = { +- 0xb0, 0xb1, 0xb2 }; + + static const u16 NCT6116_REG_CRITICAL_TEMP[] = { + 0x11a, 0x12a, 0x13a, 0x19a, 0x1aa }; +@@ -3650,7 +3648,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + = NCT6106_CRITICAL_PWM_ENABLE_MASK; + data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM; + data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; +- data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE; ++ data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; +@@ -3664,13 +3662,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + + reg_temp = NCT6106_REG_TEMP; + reg_temp_mon = NCT6106_REG_TEMP_MON; +- num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); ++ num_reg_temp = 3; + num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON); + num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP); + reg_temp_over = NCT6106_REG_TEMP_OVER; + reg_temp_hyst = NCT6106_REG_TEMP_HYST; + reg_temp_config = NCT6106_REG_TEMP_CONFIG; +- num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG); ++ num_reg_temp_config = 3; + reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6106_REG_TEMP_CRIT; + reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; +-- +2.53.0 + diff --git a/queue-6.18/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-6.18/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch new file mode 100644 index 0000000000..56e55abe43 --- /dev/null +++ b/queue-6.18/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch @@ -0,0 +1,74 @@ +From b0702ea6ff50e80d8f03cc4ca06c9b622988c86c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 13:35:37 -0700 +Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guenter Roeck + +[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ] + +Sashiko reports: + +During initialization of the nct6116 chip, the driver sets data->pwm_num +to 5. However, it assigns several NCT6106 register arrays (such as +NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and +NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP. +These arrays only contain 3 elements. + +In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If +data->has_pwm has bits 3 or 4 set (which is structurally possible for +nct6116), the loop attempts to read elements at index 3 and 4 from these +3-element arrays. This results in a global out-of-bounds read, which can +be caught by KASAN. + +Furthermore, the driver uses these garbage out-of-bounds values as +hardware register addresses for subsequent read and write operations. This +leads to invalid hardware register access, potentially causing hardware +misconfiguration or system crashes. + +The underlying problem is that the chip does support up to five fan +control channels, but only the first three support weight control. +Fix the problem by extending the affected weight register arrays with +zeroed fields. The driver uses zeroed register addresses to determine +if a register is supported or not, and skips accesses for unsupported +registers. + +Reported-by: Sashiko +Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") +Cc: Björn Gerhart +Cc: Florian Bezdeka +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index 506d57025c3bd..ce2cf1f229004 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -791,12 +791,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 }; + + static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 }; + +-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 }; + + static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 }; + static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 }; +-- +2.53.0 + diff --git a/queue-6.18/hwmon-nzxt-smart2-dma-align-output-buffer.patch b/queue-6.18/hwmon-nzxt-smart2-dma-align-output-buffer.patch new file mode 100644 index 0000000000..d9a77ad572 --- /dev/null +++ b/queue-6.18/hwmon-nzxt-smart2-dma-align-output-buffer.patch @@ -0,0 +1,53 @@ +From ed6d42a723fa25929814e8f31f16d397de5e8eee Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 09:54:23 -0700 +Subject: hwmon: (nzxt-smart2) DMA-align output buffer + +From: Guenter Roeck + +[ Upstream commit 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 ] + +Sashiko reports: + +When send_output_report() calls hid_hw_output_report(), the underlying USB +HID core calls usb_interrupt_msg() which maps this buffer directly for DMA. + +When the DMA mapping flushes or invalidates the cacheline, it will corrupt +the adjacent variables (mutex, update_interval) that were modified +concurrently by the CPU. This causes memory corruption due to cacheline +sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA +API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings +for this violation. + +Any operation that triggers send_output_report() (like setting a fan speed +or updating the interval) causes the USB DMA mapping. On systems with +non-coherent caches, this structural bug causes immediate and deterministic +memory corruption. + +Align the output buffer to ARCH_DMA_MINALIGN to fix the problem. + +Reported-by: Sashiko +Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.") +Cc: Aleksandr Mezin +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nzxt-smart2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c +index e2316c46629d6..ff0c0bee0e839 100644 +--- a/drivers/hwmon/nzxt-smart2.c ++++ b/drivers/hwmon/nzxt-smart2.c +@@ -203,7 +203,7 @@ struct drvdata { + */ + struct mutex mutex; + long update_interval; +- u8 output_buffer[OUTPUT_REPORT_SIZE]; ++ u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN); + }; + + static long scale_pwm_value(long val, long orig_max, long new_max) +-- +2.53.0 + diff --git a/queue-6.18/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-6.18/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch new file mode 100644 index 0000000000..b36ef1c8cc --- /dev/null +++ b/queue-6.18/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch @@ -0,0 +1,39 @@ +From de1b7bb75fcd9820ae9324fba910c7b663ab8262 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 08:41:40 -0700 +Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data() + +From: Guenter Roeck + +[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ] + +pmbus_update_byte_data() is supposed to return a negative error code or 0. +However, if no change is made to the register, it actually returns the +register value. This can result in problems if the calling code explicitly +expects to see an error code or 0. + +Fix it to return 0 on success or the error code as expected. + +Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/pmbus/pmbus_core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c +index ddf64e72751cc..4e1c66670ead9 100644 +--- a/drivers/hwmon/pmbus/pmbus_core.c ++++ b/drivers/hwmon/pmbus/pmbus_core.c +@@ -512,7 +512,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + if (tmp != rv) + rv = _pmbus_write_byte_data(client, page, reg, tmp); + +- return rv; ++ return rv < 0 ? rv : 0; + } + EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS"); + +-- +2.53.0 + diff --git a/queue-6.18/hwmon-sht3x-fix-unaligned-accesses.patch b/queue-6.18/hwmon-sht3x-fix-unaligned-accesses.patch new file mode 100644 index 0000000000..fd06a21b8b --- /dev/null +++ b/queue-6.18/hwmon-sht3x-fix-unaligned-accesses.patch @@ -0,0 +1,76 @@ +From bd795d62339f6bf7835052bc44bed452d8dd010d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 09:34:46 -0700 +Subject: hwmon: (sht3x) Fix unaligned accesses + +From: Guenter Roeck + +[ Upstream commit f46d5ab43a572b84773015a76966f5da56fc1748 ] + +Sashiko reports: + +In sht3x_update_client(), the 16-bit temperature and humidity values are +extracted from a stack-allocated byte array using be16_to_cpup(). The +pointers passed to this function are calculated as buf and buf + 3. Since +the difference between the two pointers is an odd number of bytes, at +least one of them is guaranteed to be at an unaligned offset. + +This will trigger an alignment fault on strict-alignment architectures +such as ARMv5 or SPARC, resulting in a kernel panic. + +Fix the problem by using get_unaligned_be16() instead of be16_to_cpup(), +and put_unaligned_be16() instead of cpu_to_be16(). + +Fixes: 7c84f7f80d6f ("hwmon: add support for Sensirion SHT3x sensors") +Reported-by: Sashiko +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/sht3x.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c +index f36c0229328fa..5d04013efb522 100644 +--- a/drivers/hwmon/sht3x.c ++++ b/drivers/hwmon/sht3x.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + /* commands (high repeatability mode) */ + static const unsigned char sht3x_cmd_measure_single_hpm[] = { 0x24, 0x00 }; +@@ -276,9 +277,9 @@ static struct sht3x_data *sht3x_update_client(struct device *dev) + if (ret) + goto out; + +- val = be16_to_cpup((__be16 *)buf); ++ val = get_unaligned_be16(buf); + data->temperature = sht3x_extract_temperature(val); +- val = be16_to_cpup((__be16 *)(buf + 3)); ++ val = get_unaligned_be16(buf + 3); + data->humidity = sht3x_extract_humidity(val); + data->last_update = jiffies; + } +@@ -336,7 +337,7 @@ static int limits_update(struct sht3x_data *data) + if (ret) + return ret; + +- raw = be16_to_cpup((__be16 *)buffer); ++ raw = get_unaligned_be16(buffer); + temperature = sht3x_extract_temperature((raw & 0x01ff) << 7); + humidity = sht3x_extract_humidity(raw & 0xfe00); + data->temperature_limits[index] = temperature; +@@ -389,7 +390,7 @@ static size_t limit_write(struct device *dev, + raw = ((u32)(temperature + 45000) * 24543) >> (16 + 7); + raw |= ((humidity * 42950) >> 16) & 0xfe00; + +- *((__be16 *)position) = cpu_to_be16(raw); ++ put_unaligned_be16(raw, position); + position += SHT3X_WORD_LEN; + *position = crc8(sht3x_crc8_table, + position - SHT3X_WORD_LEN, +-- +2.53.0 + diff --git a/queue-6.18/ice-suppress-dpll-errors-during-reset-recovery.patch b/queue-6.18/ice-suppress-dpll-errors-during-reset-recovery.patch new file mode 100644 index 0000000000..e95841a59a --- /dev/null +++ b/queue-6.18/ice-suppress-dpll-errors-during-reset-recovery.patch @@ -0,0 +1,89 @@ +From 6a496d074d0b106a771d2c2f3ed680f821ae008a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 13:50:06 +0200 +Subject: ice: suppress DPLL errors during reset recovery + +From: Przemyslaw Korba + +[ Upstream commit b00be7c6b4bd7da3d510753b27ff6cb7ec647d07 ] + +During reset recovery, the admin queue returns EBUSY which is expected +behavior. However, the DPLL subsystem was logging these as errors and +incrementing the error counter, potentially leading to unnecessary +warnings and even disabling the DPLL periodic worker if the threshold +was reached. + +Suppress error logging and error counter increments when the admin +queue returns EBUSY, as this is expected during reset recovery and +not a real failure condition. + +test case: +- ethtool --reset eth3 irq-shared dma-shared filter-shared offload-shared +mac-shared phy-shared ram-shared +- observe if dmesg EBUSY errors are gone + +Fixes: d7999f5ea64b ("ice: implement dpll interface to control cgu") +Signed-off-by: Przemyslaw Korba +Reviewed-by: Simon Horman +Tested-by: Rinitha S (A Contingent worker at Intel) +Reviewed-by: Aleksandr Loktionov +Signed-off-by: Tony Nguyen +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_dpll.c | 19 ++++++++++++------- + 1 file changed, 12 insertions(+), 7 deletions(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c +index 023d0de12670c..2a47d9f3cf7ef 100644 +--- a/drivers/net/ethernet/intel/ice/ice_dpll.c ++++ b/drivers/net/ethernet/intel/ice/ice_dpll.c +@@ -703,7 +703,7 @@ ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin, + ret, + libie_aq_str(pf->hw.adminq.sq_last_status), + pin_type_name[pin_type], pin->idx); +- else ++ else if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY) + dev_err_ratelimited(ice_pf_to_dev(pf), + "err:%d %s failed to update %s pin:%u\n", + ret, +@@ -2669,7 +2669,8 @@ static int ice_dpll_pps_update_phase_offsets(struct ice_pf *pf, + *phase_offset_pins_updated = 0; + ret = ice_aq_get_cgu_input_pin_measure(&pf->hw, DPLL_TYPE_PPS, meas, + ARRAY_SIZE(meas)); +- if (ret && pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN) { ++ if (ret && (pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN || ++ pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EBUSY)) { + return 0; + } else if (ret) { + dev_err(ice_pf_to_dev(pf), +@@ -2731,10 +2732,12 @@ ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init) + d->dpll_idx, d->prev_input_idx, d->input_idx, + d->dpll_state, d->prev_dpll_state, d->mode); + if (ret) { +- dev_err(ice_pf_to_dev(pf), +- "update dpll=%d state failed, ret=%d %s\n", +- d->dpll_idx, ret, +- libie_aq_str(pf->hw.adminq.sq_last_status)); ++ /* EBUSY is expected during reset recovery, don't log error */ ++ if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY) ++ dev_err(ice_pf_to_dev(pf), ++ "update dpll=%d state failed, ret=%d %s\n", ++ d->dpll_idx, ret, ++ libie_aq_str(pf->hw.adminq.sq_last_status)); + return ret; + } + if (init) { +@@ -2803,7 +2806,9 @@ static void ice_dpll_periodic_work(struct kthread_work *work) + d->periodic_counter % dp->phase_offset_monitor_period == 0) + ret = ice_dpll_pps_update_phase_offsets(pf, &phase_offset_ntf); + if (ret) { +- d->cgu_state_acq_err_num++; ++ /* EBUSY is expected during reset recovery */ ++ if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY) ++ d->cgu_state_acq_err_num++; + /* stop rescheduling this worker */ + if (d->cgu_state_acq_err_num > + ICE_CGU_STATE_ACQ_ERR_THRESHOLD) { +-- +2.53.0 + diff --git a/queue-6.18/idpf-adjust-txq-ring-count-minimum.patch b/queue-6.18/idpf-adjust-txq-ring-count-minimum.patch new file mode 100644 index 0000000000..a2a2e4682c --- /dev/null +++ b/queue-6.18/idpf-adjust-txq-ring-count-minimum.patch @@ -0,0 +1,72 @@ +From bb171e0f25224ae957c75eab6cbc9136187cd33a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 30 Jun 2026 16:56:19 -0700 +Subject: idpf: adjust TxQ ring count minimum + +From: Joshua Hay + +[ Upstream commit bef152db47debcd14cbacefc5767f6f026c4bc89 ] + +Set the TxQ ring count minimum to 128 descriptors. Any lower than this, +and the queue will stall and trigger Tx timeouts in flow based +scheduling mode. This is because next_to_clean might never be updated. + +In flow based scheduling mode, next_to_clean is only updated after a +descriptor completion is processed, i.e. after the RE bit is set in the +last descriptor of a Tx packet. This will never happen with a ring size +of 64 and an IDPF_TX_SPLITQ_RE_MIN_GAP of 64. No matter what the value +of last_re is initialized/set to, the calculated gap will be at most 63 +and never trigger the RE bit. + +Even a ring size of 96 does not solve this. Because of how infrequent +next_to_clean is updated and how small the ring is, IDPF_DESC_UNUSED +will be much smaller on average. This increases the chance the queue +will be stopped because a multi-descriptor packet, e.g. a large LSO +packet, does not see enough resources on the ring. In this case, the +queue will trigger the stop logic. The queue permanently stalls because +there is no chance for a descriptor completion to update next_to_clean +since it is dependent on a packet being sent. + +Fixes: 5f417d551324 ("idpf: replace flow scheduling buffer ring with buffer pool") +Signed-off-by: Joshua Hay +Reviewed-by: Aleksandr Loktionov +Tested-by: Samuel Salin +Signed-off-by: Tony Nguyen +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/idpf/idpf_txrx.c | 5 +---- + drivers/net/ethernet/intel/idpf/idpf_txrx.h | 2 +- + 2 files changed, 2 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c +index c859665b2dc89..e6563ad31f5ca 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c ++++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c +@@ -3075,10 +3075,7 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb, + + tx_params.dtype = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE; + tx_params.eop_cmd = IDPF_TXD_FLEX_FLOW_CMD_EOP; +- /* Set the RE bit to periodically "clean" the descriptor ring. +- * MIN_GAP is set to MIN_RING size to ensure it will be set at +- * least once each time around the ring. +- */ ++ /* Set the RE bit periodically to "clean" the descriptor ring */ + if (idpf_tx_splitq_need_re(tx_q)) { + tx_params.eop_cmd |= IDPF_TXD_FLEX_FLOW_CMD_RE; + tx_q->txq_grp->num_completions_pending++; +diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h +index 423cc9486dce7..aa0b93b5c8599 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h ++++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h +@@ -21,7 +21,7 @@ + /* Mailbox Queue */ + #define IDPF_MAX_MBXQ 1 + +-#define IDPF_MIN_TXQ_DESC 64 ++#define IDPF_MIN_TXQ_DESC 128 + #define IDPF_MIN_RXQ_DESC 64 + #define IDPF_MIN_TXQ_COMPLQ_DESC 256 + #define IDPF_MAX_QIDS 256 +-- +2.53.0 + diff --git a/queue-6.18/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch b/queue-6.18/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch new file mode 100644 index 0000000000..b252cfc462 --- /dev/null +++ b/queue-6.18/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch @@ -0,0 +1,41 @@ +From 9821cd97f88033c77bcff5e72555b328f724d958 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 3 Jul 2026 01:03:32 -0400 +Subject: idpf: Fix mailbox IRQ name leak on request failure + +From: Yuho Choi + +[ Upstream commit 9bff30482c10f70d9e56c0633a6616e07140e217 ] + +idpf_mb_intr_req_irq() allocates the mailbox IRQ name before calling +request_irq(). On success, the name is released later through +kfree(free_irq()), but request_irq() failure returns without freeing it. + +Free the allocated name on the request_irq() failure path. + +Fixes: 4930fbf419a7 ("idpf: add core init and interrupt request") +Signed-off-by: Yuho Choi +Reviewed-by: Aleksandr Loktionov +Tested-by: Samuel Salin +Signed-off-by: Tony Nguyen +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/idpf/idpf_lib.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c +index 131a8121839bd..590119f4097d0 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c ++++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c +@@ -139,7 +139,7 @@ static int idpf_mb_intr_req_irq(struct idpf_adapter *adapter) + if (err) { + dev_err(&adapter->pdev->dev, + "IRQ request for mailbox failed, error: %d\n", err); +- ++ kfree(name); + return err; + } + +-- +2.53.0 + diff --git a/queue-6.18/iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch b/queue-6.18/iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch new file mode 100644 index 0000000000..c0443f2d3f --- /dev/null +++ b/queue-6.18/iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch @@ -0,0 +1,77 @@ +From d40e86fb49d7934842fd144aa5c51aa18740ed28 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 29 Jun 2026 14:52:29 +0200 +Subject: iomap: add a separate bio_set for iomap_split_ioend + +From: Christoph Hellwig + +[ Upstream commit c679ce3be6cb63763d68ab9b5d9d73ddc0a40762 ] + +iomap_split_ioend can split bios that already come from +iomap_ioend_bioset and thus deadlock when the bioset is exhausted. + +Add a separate bio_set to avoid this deadlock. + +Christian Brauner says: +Mark iomap_ioend_split_bioset static as it is only used in ioend.c, +fixing the sparse warning reported by the kernel test robot. + +Fixes: 5fcbd555d483 ("iomap: split bios to zone append limits in the submission handlers") +Signed-off-by: Christoph Hellwig +Link: https://patch.msgid.link/20260629125229.3400726-1-hch@lst.de +Signed-off-by: Christian Brauner (Amutable) +Signed-off-by: Sasha Levin +--- + fs/iomap/ioend.c | 21 +++++++++++++++++++-- + 1 file changed, 19 insertions(+), 2 deletions(-) + +diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c +index 6c75fbf4d5af4..2a86b05c88efc 100644 +--- a/fs/iomap/ioend.c ++++ b/fs/iomap/ioend.c +@@ -11,6 +11,7 @@ + + struct bio_set iomap_ioend_bioset; + EXPORT_SYMBOL_GPL(iomap_ioend_bioset); ++static struct bio_set iomap_ioend_split_bioset; + + struct iomap_ioend *iomap_init_ioend(struct inode *inode, + struct bio *bio, loff_t file_offset, u16 ioend_flags) +@@ -409,7 +410,8 @@ struct iomap_ioend *iomap_split_ioend(struct iomap_ioend *ioend, + sector_offset = ALIGN_DOWN(sector_offset << SECTOR_SHIFT, + i_blocksize(ioend->io_inode)) >> SECTOR_SHIFT; + +- split = bio_split(bio, sector_offset, GFP_NOFS, &iomap_ioend_bioset); ++ split = bio_split(bio, sector_offset, GFP_NOFS, ++ &iomap_ioend_split_bioset); + if (IS_ERR(split)) + return ERR_CAST(split); + split->bi_private = bio->bi_private; +@@ -432,8 +434,23 @@ EXPORT_SYMBOL_GPL(iomap_split_ioend); + + static int __init iomap_ioend_init(void) + { +- return bioset_init(&iomap_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE), ++ const unsigned int nr_mempool_entries = 4 * (PAGE_SIZE / SECTOR_SIZE); ++ int error; ++ ++ error = bioset_init(&iomap_ioend_bioset, nr_mempool_entries, + offsetof(struct iomap_ioend, io_bio), + BIOSET_NEED_BVECS); ++ if (error) ++ return error; ++ error = bioset_init(&iomap_ioend_split_bioset, nr_mempool_entries, ++ offsetof(struct iomap_ioend, io_bio), ++ BIOSET_NEED_BVECS); ++ if (error) ++ goto out_exit_ioend_bioset; ++ return 0; ++ ++out_exit_ioend_bioset: ++ bioset_exit(&iomap_ioend_bioset); ++ return error; + } + fs_initcall(iomap_ioend_init); +-- +2.53.0 + diff --git a/queue-6.18/iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch b/queue-6.18/iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch new file mode 100644 index 0000000000..6c85388dd4 --- /dev/null +++ b/queue-6.18/iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch @@ -0,0 +1,67 @@ +From 7e29c6e0d6bb9e37597bc9f9276b825b6525587a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 5 Jul 2026 22:36:11 -0700 +Subject: iommu/arm-smmu-v3-iommufd: Require exactly one Stream ID for a + vDEVICE + +From: Nicolin Chen + +[ Upstream commit c3b8ee84a965058b41275069d4696f37a8b14bf6 ] + +arm_vsmmu_vsid_to_sid() maps a guest's vSID to a single physical Stream ID +taken from master->streams[0], assuming a device has exactly one stream. A +device with several streams gets only its first one mapped, so a guest vSID +invalidation cannot reach the others' ATC and IOTLB entries; a device with +none makes master->streams a ZERO_SIZE_PTR, read out of bounds. + +Add an arm_vsmmu_vdevice_init() op to reject the vDEVICE with -EOPNOTSUPP +when master->num_streams is not one, rather than mapping it silently. + +Fixes: d68beb276ba26 ("iommu/arm-smmu-v3: Support IOMMU_HWPT_INVALIDATE using a VIOMMU object") +Link: https://patch.msgid.link/r/b15f2b73520f389f3f57881da2f040e7bdc18876.1783311134.git.nicolinc@nvidia.com +Reviewed-by: Kevin Tian +Assisted-by: Claude:claude-opus-4-8 +Reviewed-by: Pranjal Shrivastava +Signed-off-by: Nicolin Chen +Signed-off-by: Jason Gunthorpe +Signed-off-by: Sasha Levin +--- + .../iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +index 9ec5591565083..a52f86454f77d 100644 +--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c ++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +@@ -285,6 +285,20 @@ static int arm_vsmmu_vsid_to_sid(struct arm_vsmmu *vsmmu, u32 vsid, u32 *sid) + return ret; + } + ++static int arm_vsmmu_vdevice_init(struct iommufd_vdevice *vdev) ++{ ++ struct device *dev = iommufd_vdevice_to_device(vdev); ++ struct arm_smmu_master *master = dev_iommu_priv_get(dev); ++ ++ /* ++ * arm_vsmmu_vsid_to_sid() maps a vSID to master->streams[0] alone, so ++ * more streams would leave the rest stale and none reads out of bounds. ++ */ ++ if (master->num_streams != 1) ++ return -EOPNOTSUPP; ++ return 0; ++} ++ + /* This is basically iommu_viommu_arm_smmuv3_invalidate in u64 for conversion */ + struct arm_vsmmu_invalidation_cmd { + union { +@@ -391,6 +405,7 @@ int arm_vsmmu_cache_invalidate(struct iommufd_viommu *viommu, + static const struct iommufd_viommu_ops arm_vsmmu_ops = { + .alloc_domain_nested = arm_vsmmu_alloc_domain_nested, + .cache_invalidate = arm_vsmmu_cache_invalidate, ++ .vdevice_init = arm_vsmmu_vdevice_init, + }; + + size_t arm_smmu_get_viommu_size(struct device *dev, +-- +2.53.0 + diff --git a/queue-6.18/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch b/queue-6.18/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch new file mode 100644 index 0000000000..188683df5c --- /dev/null +++ b/queue-6.18/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch @@ -0,0 +1,335 @@ +From 3ae1bd04c685d0fcec46430b7bc5205c1c4f20b6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:17 +0300 +Subject: ipvs: do not mangle ICMP replies for non-first fragments + +From: Julian Anastasov + +[ Upstream commit 342e24a339b90e8e339a0f8c151ca479b8565661 ] + +Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the +payload for embedded non-first IPv4 fragments. The problem is +in the very old inverted pp->dont_defrag check which should not +continue when embedded is a non-first TCP/UDP/SCTP fragment. + +Check for embedded non-first fragment is also missing from +ip_vs_out_icmp_v6(), it is needed before any connection +lookups that expect ports after the network headers. + +Drop the blocking code from ip_vs_in_icmp_v6() which prevents +ICMPv6 from local clients to use non-MASQ forwarding. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Link: https://sashiko.dev/#/patchset/20260720201122.79882-1-ja%40ssi.bg +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 11 +++--- + net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++--------------------- + net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++---- + 3 files changed, 48 insertions(+), 52 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index 71ffbba542464..17a97086b294f 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -1640,8 +1640,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph); ++ unsigned int hooknum, struct ip_vs_iphdr *ciph); + void ip_vs_dest_dst_rcu_free(struct rcu_head *head); + + #ifdef CONFIG_IP_VS_IPV6 +@@ -1655,8 +1654,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph); ++ unsigned int hooknum, struct ip_vs_iphdr *ciph); + #endif + + #ifdef CONFIG_SYSCTL +@@ -1721,12 +1719,13 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) + } + + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir, unsigned int toff); ++ struct ip_vs_conn *cp, int dir, unsigned int toff, ++ bool has_ports); + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int dir, unsigned int toff, +- struct ip_vs_iphdr *ciph); ++ bool has_ports, struct ip_vs_iphdr *ciph); + #endif + + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index 784c00ec01ef7..6207a91e93f3b 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -746,7 +746,8 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, + * - inout: 1=in->out, 0=out->in + */ + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout, unsigned int toff) ++ struct ip_vs_conn *cp, int inout, unsigned int toff, ++ bool has_ports) + { + struct iphdr *iph = ip_hdr(skb); + struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); +@@ -766,8 +767,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + } + + /* the TCP/UDP/SCTP port */ +- if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol || +- IPPROTO_SCTP == ciph->protocol) { ++ if (has_ports) { + __be16 *ports = (void *)ciph + ciph->ihl*4; + + if (inout) +@@ -792,18 +792,15 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int inout, unsigned int toff, +- struct ip_vs_iphdr *ciph) ++ bool has_ports, struct ip_vs_iphdr *ciph) + { + struct ipv6hdr *iph = ipv6_hdr(skb); +- int protocol; + struct icmp6hdr *icmph; + struct ipv6hdr *cih; + + icmph = (struct icmp6hdr *)(skb->data + toff); + cih = (struct ipv6hdr *)(skb->data + ciph->off); + +- protocol = ciph->protocol; +- + if (inout) { + iph->saddr = cp->vaddr.in6; + cih->daddr = cp->vaddr.in6; +@@ -813,9 +810,7 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + } + + /* the TCP/UDP/SCTP port */ +- if (!ciph->fragoffs && +- (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || +- protocol == IPPROTO_SCTP)) { ++ if (has_ports) { + __be16 *ports = (void *)(skb->data + ciph->len); + + IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, +@@ -857,6 +852,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; + unsigned int ctoff = ciph->len; ++ bool has_ports = false; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; +@@ -870,17 +866,19 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + } + + if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || +- ciph->protocol == IPPROTO_SCTP) ++ ciph->protocol == IPPROTO_SCTP) { + ctoff += 2 * sizeof(__u16); ++ has_ports = true; ++ } + if (skb_ensure_writable(skb, ctoff)) + goto out; + + #ifdef CONFIG_IP_VS_IPV6 + if (af == AF_INET6) +- ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph); + else + #endif +- ip_vs_nat_icmp(skb, pp, cp, 1, toff); ++ ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports); + + if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) + goto out; +@@ -964,8 +962,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + + /* Is the embedded protocol header present? */ +- if (unlikely(cih->frag_off & htons(IP_OFFSET) && +- pp->dont_defrag)) ++ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, +@@ -1029,6 +1026,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (!pp) + return NF_ACCEPT; + ++ /* Is the embedded protocol header present? */ ++ if (unlikely(ciph.fragoffs && !pp->dont_defrag)) ++ return NF_ACCEPT; ++ + /* The embedded headers contain source and dest in reverse order */ + cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto, + ipvs, AF_INET6, skb, &ciph); +@@ -1687,8 +1688,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + pp = pd->pp; + + /* Is the embedded protocol header present? */ +- if (unlikely(cih->frag_off & htons(IP_OFFSET) && +- pp->dont_defrag)) ++ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, +@@ -1696,7 +1696,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + + offset2 = offset; + ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph); +- offset = ciph.len; + + /* The embedded headers contain source and dest in reverse order. + * For IPIP/UDP/GRE tunnel this is error for request, not for reply. +@@ -1790,11 +1789,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); +- if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || +- IPPROTO_SCTP == cih->protocol) +- offset += 2 * sizeof(__u16); +- verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, +- &ciph); ++ verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph); + + out: + if (likely(!new_cp)) +@@ -1854,8 +1849,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + pp = pd->pp; + +- /* Cannot handle fragmented embedded protocol */ +- if (ciph.fragoffs) ++ /* Is the embedded protocol header present? */ ++ if (ciph.fragoffs && !pp->dont_defrag) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset, +@@ -1879,13 +1874,6 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + new_cp = true; + } + +- /* VS/TUN, VS/DR and LOCALNODE just let it go */ +- if ((hooknum == NF_INET_LOCAL_OUT) && +- (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) { +- verdict = NF_ACCEPT; +- goto out; +- } +- + verdict = NF_DROP; + + /* Ensure the checksum is correct */ +@@ -1901,14 +1889,7 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); + +- /* Need to mangle contained IPv6 header in ICMPv6 packet */ +- offset = ciph.len; +- if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol || +- IPPROTO_SCTP == ciph.protocol) +- offset += 2 * sizeof(__u16); /* Also mangle ports */ +- +- verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, +- &ciph); ++ verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph); + + out: + if (likely(!new_cp)) +diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c +index fba791d140e70..ac1827ef78949 100644 +--- a/net/netfilter/ipvs/ip_vs_xmit.c ++++ b/net/netfilter/ipvs/ip_vs_xmit.c +@@ -1500,13 +1500,14 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + int + ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph) ++ unsigned int hooknum, struct ip_vs_iphdr *ciph) + { + struct rtable *rt; /* Route to the other host */ + int rc; + int local; + int rt_mode, was_input; ++ bool has_ports = false; ++ unsigned int wlen; + + /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be + forwarded directly here, because there is no need to +@@ -1562,6 +1563,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + goto tx_error; + } + ++ wlen = ciph->len; ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) { ++ wlen += 2 * sizeof(__u16); /* Also mangle ports */ ++ has_ports = true; ++ } ++ + /* copy-on-write the packet before mangling it */ + if (skb_ensure_writable(skb, wlen)) + goto tx_error; +@@ -1569,7 +1577,7 @@ 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); ++ ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +@@ -1586,10 +1594,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + int + ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph) ++ unsigned int hooknum, struct ip_vs_iphdr *ciph) + { ++ bool has_ports = false; + struct rt6_info *rt; /* Route to the other host */ ++ unsigned int wlen; + int rc; + int local; + int rt_mode; +@@ -1647,6 +1656,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + goto tx_error; + } + ++ wlen = ciph->len; ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) { ++ wlen += 2 * sizeof(__u16); /* Also mangle ports */ ++ has_ports = true; ++ } ++ + /* copy-on-write the packet before mangling it */ + if (skb_ensure_writable(skb, wlen)) + goto tx_error; +@@ -1654,7 +1670,7 @@ ip_vs_icmp_xmit_v6(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_v6(skb, pp, cp, 0, toff, ciph); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +-- +2.53.0 + diff --git a/queue-6.18/ipvs-fix-places-with-wrong-packet-offsets.patch b/queue-6.18/ipvs-fix-places-with-wrong-packet-offsets.patch new file mode 100644 index 0000000000..3de0c95588 --- /dev/null +++ b/queue-6.18/ipvs-fix-places-with-wrong-packet-offsets.patch @@ -0,0 +1,624 @@ +From 5c1dacf7ce4465603548c912f753021ed444ddca Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:16 +0300 +Subject: ipvs: fix places with wrong packet offsets + +From: Julian Anastasov + +[ Upstream commit 15cab31a3730e05f0767b922a7450e5d784b2607 ] + +The offsets we use to packet headers and payloads should be +based on skb->data. We even already respect non-zero +network offset in ip_vs_fill_iph_skb() but some places +do it wrongly and support only zero offset which is expected +for the IP layer where IPVS has hooks. + +Change all places that instead of skb->data use offsets based +on the network header (skb_network_header, ip_hdr, etc) because +this doubles the network offset as noted by Sashiko. + +For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header +parsing done by the caller. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Link: https://sashiko.dev/#/patchset/20260710143733.29741-2-fw%40strlen.de +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 15 +-- + net/netfilter/ipvs/ip_vs_app.c | 4 +- + net/netfilter/ipvs/ip_vs_core.c | 133 +++++++++++++------------- + net/netfilter/ipvs/ip_vs_proto_sctp.c | 4 +- + net/netfilter/ipvs/ip_vs_proto_tcp.c | 4 +- + net/netfilter/ipvs/ip_vs_proto_udp.c | 4 +- + net/netfilter/ipvs/ip_vs_xmit.c | 26 ++--- + 7 files changed, 97 insertions(+), 93 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index 7dcaadc32a76f..71ffbba542464 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -1639,8 +1639,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, +- unsigned int hooknum, struct ip_vs_iphdr *iph); ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph); + void ip_vs_dest_dst_rcu_free(struct rcu_head *head); + + #ifdef CONFIG_IP_VS_IPV6 +@@ -1653,8 +1654,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, +- unsigned int hooknum, struct ip_vs_iphdr *iph); ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph); + #endif + + #ifdef CONFIG_SYSCTL +@@ -1719,11 +1721,12 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) + } + + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir); ++ struct ip_vs_conn *cp, int dir, unsigned int toff); + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir); ++ struct ip_vs_conn *cp, int dir, unsigned int toff, ++ struct ip_vs_iphdr *ciph); + #endif + + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) +diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c +index f9132b359f0c6..0c690a30a85dc 100644 +--- a/net/netfilter/ipvs/ip_vs_app.c ++++ b/net/netfilter/ipvs/ip_vs_app.c +@@ -368,7 +368,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb, + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) + return 0; + +- th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); ++ th = (struct tcphdr *)(skb->data + ipvsh->len); + + /* + * Remember seq number in case this pkt gets resized +@@ -444,7 +444,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb, + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) + return 0; + +- th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); ++ th = (struct tcphdr *)(skb->data + ipvsh->len); + + /* + * Remember seq number in case this pkt gets resized +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index 306a8227d300e..784c00ec01ef7 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -746,13 +746,12 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, + * - inout: 1=in->out, 0=out->in + */ + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout) ++ struct ip_vs_conn *cp, int inout, unsigned int toff) + { + struct iphdr *iph = ip_hdr(skb); +- unsigned int icmp_offset = iph->ihl*4; +- struct icmphdr *icmph = (struct icmphdr *)(skb_network_header(skb) + +- icmp_offset); ++ struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); + struct iphdr *ciph = (struct iphdr *)(icmph + 1); ++ unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr); + + if (inout) { + iph->saddr = cp->vaddr.ip; +@@ -779,48 +778,45 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + + /* And finally the ICMP checksum */ + icmph->checksum = 0; +- icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset); ++ icmph->checksum = ip_vs_checksum_complete(skb, toff); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (inout) +- IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, +- "Forwarding altered outgoing ICMP"); ++ IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, ++ "Forwarding altered outgoing ICMP"); + else +- IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, +- "Forwarding altered incoming ICMP"); ++ IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, ++ "Forwarding altered incoming ICMP"); + } + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout) ++ struct ip_vs_conn *cp, int inout, unsigned int toff, ++ struct ip_vs_iphdr *ciph) + { + struct ipv6hdr *iph = ipv6_hdr(skb); +- unsigned int icmp_offset = 0; +- unsigned int offs = 0; /* header offset*/ + int protocol; + struct icmp6hdr *icmph; +- struct ipv6hdr *ciph; +- unsigned short fragoffs; ++ struct ipv6hdr *cih; + +- ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL); +- icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset); +- offs = icmp_offset + sizeof(struct icmp6hdr); +- ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs); ++ icmph = (struct icmp6hdr *)(skb->data + toff); ++ cih = (struct ipv6hdr *)(skb->data + ciph->off); + +- protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL); ++ protocol = ciph->protocol; + + if (inout) { + iph->saddr = cp->vaddr.in6; +- ciph->daddr = cp->vaddr.in6; ++ cih->daddr = cp->vaddr.in6; + } else { + iph->daddr = cp->daddr.in6; +- ciph->saddr = cp->daddr.in6; ++ cih->saddr = cp->daddr.in6; + } + + /* the TCP/UDP/SCTP port */ +- if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || +- IPPROTO_SCTP == protocol)) { +- __be16 *ports = (void *)(skb_network_header(skb) + offs); ++ if (!ciph->fragoffs && ++ (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || ++ protocol == IPPROTO_SCTP)) { ++ __be16 *ports = (void *)(skb->data + ciph->len); + + IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, + ntohs(inout ? ports[1] : ports[0]), +@@ -833,19 +829,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + + /* And finally the ICMP checksum */ + icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, +- skb->len - icmp_offset, ++ skb->len - toff, + IPPROTO_ICMPV6, 0); +- skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset; ++ skb->csum_start = skb_headroom(skb) + toff; + skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum); + skb->ip_summed = CHECKSUM_PARTIAL; + + if (inout) +- IP_VS_DBG_PKT(11, AF_INET6, pp, skb, +- (void *)ciph - (void *)iph, ++ IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, + "Forwarding altered outgoing ICMPv6"); + else +- IP_VS_DBG_PKT(11, AF_INET6, pp, skb, +- (void *)ciph - (void *)iph, ++ IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, + "Forwarding altered incoming ICMPv6"); + } + #endif +@@ -855,37 +849,38 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + */ + static int handle_response_icmp(int af, struct sk_buff *skb, + union nf_inet_addr *snet, +- __u8 protocol, struct ip_vs_conn *cp, ++ struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, +- unsigned int offset, unsigned int ihl, +- unsigned int hooknum) ++ struct ip_vs_iphdr *ciph, ++ unsigned int toff, unsigned int hooknum) + { + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; ++ unsigned int ctoff = ciph->len; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; + + /* Ensure the checksum is correct */ +- if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { ++ if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) { + /* Failed checksum! */ + IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", + IP_VS_DBG_ADDR(af, snet)); + goto out; + } + +- if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || +- IPPROTO_SCTP == protocol) +- offset += 2 * sizeof(__u16); +- if (skb_ensure_writable(skb, offset)) ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) ++ ctoff += 2 * sizeof(__u16); ++ if (skb_ensure_writable(skb, ctoff)) + goto out; + + #ifdef CONFIG_IP_VS_IPV6 + if (af == AF_INET6) +- ip_vs_nat_icmp_v6(skb, pp, cp, 1); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); + else + #endif +- ip_vs_nat_icmp(skb, pp, cp, 1); ++ ip_vs_nat_icmp(skb, pp, cp, 1, toff); + + if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) + goto out; +@@ -913,9 +908,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + * Currently handles error types - unreachable, quench, ttl exceeded. + */ + static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, +- int *related, unsigned int hooknum) ++ int *related, unsigned int hooknum, ++ struct ip_vs_iphdr *ipvsh) + { +- struct iphdr *iph; + struct icmphdr _icmph, *ic; + struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ + struct ip_vs_iphdr ciph; +@@ -930,17 +925,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (ip_is_fragment(ip_hdr(skb))) { + if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) + return NF_STOLEN; ++ if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh)) ++ return NF_ACCEPT; + } + +- iph = ip_hdr(skb); +- offset = ihl = iph->ihl * 4; ++ ihl = ipvsh->len; ++ offset = ipvsh->len; + ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); + if (ic == NULL) + return NF_DROP; + + IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n", + ic->type, ntohs(icmp_id(ic)), +- &iph->saddr, &iph->daddr); ++ &ipvsh->saddr.ip, &ipvsh->daddr.ip); + + /* + * Work through seeing if this is for us. +@@ -959,7 +956,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + /* Now find the contained IP header */ + offset += sizeof(_icmph); + cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); +- if (cih == NULL) ++ if (!(cih && cih->version == 4 && cih->ihl >= 5)) + return NF_ACCEPT; /* The packet looks wrong, ignore */ + + pp = ip_vs_proto_get(cih->protocol); +@@ -982,9 +979,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (!cp) + return NF_ACCEPT; + +- snet.ip = iph->saddr; +- return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp, +- pp, ciph.len, ihl, hooknum); ++ snet.ip = ipvsh->saddr.ip; ++ return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl, ++ hooknum); + } + + #ifdef CONFIG_IP_VS_IPV6 +@@ -997,7 +994,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + struct ip_vs_conn *cp; + struct ip_vs_protocol *pp; + union nf_inet_addr snet; +- unsigned int offset; + + *related = 1; + ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph); +@@ -1040,9 +1036,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + + snet.in6 = ciph.saddr.in6; +- offset = ciph.len; +- return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp, +- pp, offset, ipvsh->len, hooknum); ++ return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph, ++ ipvsh->len, hooknum); + } + #endif + +@@ -1368,7 +1363,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat + #endif + if (unlikely(iph.protocol == IPPROTO_ICMP)) { + int related; +- int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum); ++ int verdict = ip_vs_out_icmp(ipvs, skb, &related, ++ hooknum, &iph); + + if (related) + return verdict; +@@ -1576,9 +1572,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb, + */ + static int + ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, +- unsigned int hooknum) ++ unsigned int hooknum, struct ip_vs_iphdr *iph) + { +- struct iphdr *iph; + struct icmphdr _icmph, *ic; + struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ + struct ip_vs_iphdr ciph; +@@ -1588,7 +1583,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + unsigned int offset, offset2, ihl, verdict; + bool tunnel, new_cp = false; + union nf_inet_addr *raddr; +- char *outer_proto = "IPIP"; ++ char *outer_proto __maybe_unused = "IPIP"; + unsigned int hlen_ipip; + int ulen = 0; + +@@ -1598,17 +1593,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + if (ip_is_fragment(ip_hdr(skb))) { + if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) + return NF_STOLEN; ++ if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph)) ++ return NF_ACCEPT; + } + +- iph = ip_hdr(skb); +- offset = ihl = iph->ihl * 4; ++ ihl = iph->len; ++ offset = iph->len; + ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); + if (ic == NULL) + return NF_DROP; + + IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n", + ic->type, ntohs(icmp_id(ic)), +- &iph->saddr, &iph->daddr); ++ &iph->saddr.ip, &iph->daddr.ip); + + /* + * Work through seeing if this is for us. +@@ -1725,7 +1722,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { + /* Failed checksum! */ + IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", +- &iph->saddr); ++ &iph->saddr.ip); + goto out; + } + +@@ -1796,7 +1793,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || + IPPROTO_SCTP == cih->protocol) + offset += 2 * sizeof(__u16); +- verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph); ++ verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, ++ &ciph); + + out: + if (likely(!new_cp)) +@@ -1909,7 +1907,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + IPPROTO_SCTP == ciph.protocol) + offset += 2 * sizeof(__u16); /* Also mangle ports */ + +- verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph); ++ verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, ++ &ciph); + + out: + if (likely(!new_cp)) +@@ -1988,7 +1987,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state + if (unlikely(iph.protocol == IPPROTO_ICMP)) { + int related; + int verdict = ip_vs_in_icmp(ipvs, skb, &related, +- hooknum); ++ hooknum, &iph); + + if (related) + return verdict; +@@ -2124,6 +2123,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) + { + struct netns_ipvs *ipvs = net_ipvs(state->net); ++ struct ip_vs_iphdr iphdr; + int r; + + /* ipvs enabled in this netns ? */ +@@ -2133,10 +2133,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + if (state->pf == NFPROTO_IPV4) { + if (ip_hdr(skb)->protocol != IPPROTO_ICMP) + return NF_ACCEPT; ++ ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr); + #ifdef CONFIG_IP_VS_IPV6 + } else { +- struct ip_vs_iphdr iphdr; +- + ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr); + + if (iphdr.protocol != IPPROTO_ICMPV6) +@@ -2146,7 +2145,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + #endif + } + +- return ip_vs_in_icmp(ipvs, skb, &r, state->hook); ++ return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr); + } + + static const struct nf_hook_ops ip_vs_ops4[] = { +diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c +index f6f732b7dfa86..3dbd3096e1637 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c +@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- sctph = (void *) skb_network_header(skb) + sctphoff; ++ sctph = (void *)skb->data + sctphoff; + + /* Only update csum if we really have to */ + if (sctph->source != cp->vport || payload_csum || +@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- sctph = (void *) skb_network_header(skb) + sctphoff; ++ sctph = (void *)skb->data + sctphoff; + + /* Only update csum if we really have to */ + if (sctph->dest != cp->dport || payload_csum || +diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c +index bf31127338aa0..1ac9c233537d3 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c +@@ -180,7 +180,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- tcph = (void *)skb_network_header(skb) + tcphoff; ++ tcph = (void *)skb->data + tcphoff; + tcph->source = cp->vport; + + /* Adjust TCP checksums */ +@@ -261,7 +261,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- tcph = (void *)skb_network_header(skb) + tcphoff; ++ tcph = (void *)skb->data + tcphoff; + tcph->dest = cp->dport; + + /* +diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c +index 40d30649b3048..96ac882df15c1 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_udp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c +@@ -171,7 +171,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- udph = (void *)skb_network_header(skb) + udphoff; ++ udph = (void *)skb->data + udphoff; + udph->source = cp->vport; + + /* +@@ -255,7 +255,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- udph = (void *)skb_network_header(skb) + udphoff; ++ udph = (void *)skb->data + udphoff; + udph->dest = cp->dport; + + /* +diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c +index f564b4197cee9..fba791d140e70 100644 +--- a/net/netfilter/ipvs/ip_vs_xmit.c ++++ b/net/netfilter/ipvs/ip_vs_xmit.c +@@ -1499,8 +1499,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + */ + int + ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, unsigned int hooknum, +- struct ip_vs_iphdr *iph) ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph) + { + struct rtable *rt; /* Route to the other host */ + int rc; +@@ -1512,7 +1513,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + translate address/port back */ + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { + if (cp->packet_xmit) +- rc = cp->packet_xmit(skb, cp, pp, iph); ++ rc = cp->packet_xmit(skb, cp, pp, ciph); + else + rc = NF_ACCEPT; + /* do not touch skb anymore */ +@@ -1530,7 +1531,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; + local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode, +- NULL, iph); ++ NULL, ciph); + if (local < 0) + goto tx_error; + rt = skb_rtable(skb); +@@ -1562,13 +1563,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + } + + /* copy-on-write the packet before mangling it */ +- if (skb_ensure_writable(skb, offset)) ++ if (skb_ensure_writable(skb, wlen)) + goto tx_error; + + if (skb_cow(skb, rt->dst.dev->hard_header_len)) + goto tx_error; + +- ip_vs_nat_icmp(skb, pp, cp, 0); ++ ip_vs_nat_icmp(skb, pp, cp, 0, toff); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +@@ -1584,8 +1585,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + #ifdef CONFIG_IP_VS_IPV6 + int + ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, unsigned int hooknum, +- struct ip_vs_iphdr *ipvsh) ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph) + { + struct rt6_info *rt; /* Route to the other host */ + int rc; +@@ -1597,7 +1599,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + translate address/port back */ + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { + if (cp->packet_xmit) +- rc = cp->packet_xmit(skb, cp, pp, ipvsh); ++ rc = cp->packet_xmit(skb, cp, pp, ciph); + else + rc = NF_ACCEPT; + /* do not touch skb anymore */ +@@ -1614,7 +1616,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; + local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest, +- &cp->daddr.in6, NULL, ipvsh, 0, rt_mode); ++ &cp->daddr.in6, NULL, ciph, 0, rt_mode); + if (local < 0) + goto tx_error; + rt = dst_rt6_info(skb_dst(skb)); +@@ -1646,13 +1648,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + } + + /* copy-on-write the packet before mangling it */ +- if (skb_ensure_writable(skb, offset)) ++ if (skb_ensure_writable(skb, wlen)) + goto tx_error; + + if (skb_cow(skb, rt->dst.dev->hard_header_len)) + goto tx_error; + +- ip_vs_nat_icmp_v6(skb, pp, cp, 0); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +-- +2.53.0 + diff --git a/queue-6.18/ipvs-fix-the-checksum-validations.patch b/queue-6.18/ipvs-fix-the-checksum-validations.patch new file mode 100644 index 0000000000..b796e9c13a --- /dev/null +++ b/queue-6.18/ipvs-fix-the-checksum-validations.patch @@ -0,0 +1,389 @@ +From 5340f7071f1f09d1c6066b1be5e9c6e2ee477daa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:15 +0300 +Subject: ipvs: fix the checksum validations + +From: Julian Anastasov + +[ Upstream commit e876b75b9020a97bbdc79721e7fc749024891c65 ] + +ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6 +packets from clients. In fact, as for TCP/UDP we should +validate the checksum for ICMP packets only when we +mangle the packets on MASQ or on reply for tunnel. + +Also, Sashiko points out that handle_response_icmp() being +common for IPv4 and IPv6 is missing the pseudo-header +calculation while validating ICMPv6 messages from real +servers which is a problem if checksum is not validated +by the hardware. + +Fix the problems by creating ip_vs_checksum_common_check() +helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6. +Rely on the nf_checksum() for validating the ICMP messages +but use it also for TCP and UDP. + +Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP. + +IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum +validation on LOCAL_OUT (local clients or local real +servers) and on FORWARD (traffic from servers on LAN). +Do it only on LOCAL_IN, in case nf_checksum() is not +called on PRE_ROUTING. + +Also, ip_vs_checksum_complete() can be marked static. + +Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6") +Link: https://sashiko.dev/#/patchset/20260708180315.77413-1-ja%40ssi.bg +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 31 +++++++++++++++-- + net/netfilter/ipvs/ip_vs_core.c | 20 +++++++++-- + net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++---- + net/netfilter/ipvs/ip_vs_proto_tcp.c | 44 +++++------------------ + net/netfilter/ipvs/ip_vs_proto_udp.c | 50 ++++++--------------------- + 5 files changed, 74 insertions(+), 86 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index 2be04ca409d1f..7dcaadc32a76f 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -24,7 +24,9 @@ + #include /* for union nf_inet_addr */ + #include + #include /* for struct ipv6hdr */ ++#include + #include ++#include + #if IS_ENABLED(CONFIG_NF_CONNTRACK) + #include + #endif +@@ -1724,8 +1726,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int dir); + #endif + +-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset); +- + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) + { + __be32 diff[2] = { ~old, new }; +@@ -1751,6 +1751,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum) + return csum_partial(diff, sizeof(diff), oldsum); + } + ++static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af) ++{ ++ /* Checksum unnecessary or already validated? */ ++ if (skb_csum_unnecessary(skb)) ++ return false; ++ /* LOCAL_OUT ? */ ++ if (!skb->dev || skb->dev->flags & IFF_LOOPBACK) ++ return false; ++ /* !LOCAL_IN (FORWARD) ? */ ++ if (af == AF_INET6) { ++ if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL)) ++ return false; ++ } else { ++ if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL)) ++ return false; ++ } ++ return true; ++} ++ ++static inline bool ip_vs_checksum_common_check(struct sk_buff *skb, ++ int offset, int proto, int af) ++{ ++ if (!ip_vs_checksum_needed(skb, af)) ++ return true; ++ return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af); ++} ++ + /* Forget current conntrack (unconfirmed) and attach notrack entry */ + static inline void ip_vs_notrack(struct sk_buff *skb) + { +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index 90619453cb6f0..306a8227d300e 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -689,7 +689,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; } + + #endif + +-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) ++static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) + { + return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0)); + } +@@ -860,13 +860,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + unsigned int offset, unsigned int ihl, + unsigned int hooknum) + { ++ int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; + + /* Ensure the checksum is correct */ +- if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { ++ if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { + /* Failed checksum! */ + IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", + IP_VS_DBG_ADDR(af, snet)); +@@ -1720,7 +1721,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + verdict = NF_DROP; + + /* Ensure the checksum is correct */ +- if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { ++ if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) && ++ !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { + /* Failed checksum! */ + IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", + &iph->saddr); +@@ -1886,6 +1888,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + goto out; + } + ++ verdict = NF_DROP; ++ ++ /* Ensure the checksum is correct */ ++ if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ && ++ !ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6, ++ AF_INET6)) { ++ /* Failed checksum! */ ++ IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n", ++ &iph->saddr); ++ goto out; ++ } ++ + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); + +diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c +index c67317be17dfa..f6f732b7dfa86 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c +@@ -11,7 +11,7 @@ + + static int + sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int sctphoff); ++ struct ip_vs_iphdr *iph); + + static int + sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) ++ if (!sctp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) ++ if (!sctp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int sctphoff) ++ struct ip_vs_iphdr *iph) + { ++ unsigned int sctphoff = iph->len; + struct sctphdr *sh; + __le32 cmp, val; + ++ if (!ip_vs_checksum_needed(skb, af)) ++ return 1; + sh = (struct sctphdr *)(skb->data + sctphoff); + cmp = sh->checksum; + val = sctp_compute_cksum(skb, sctphoff); + + if (val != cmp) { + /* CRC failure, dump it. */ +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); + return 0; + } + return 1; +diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c +index b382810156b2c..bf31127338aa0 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c +@@ -30,7 +30,7 @@ + + static int + tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int tcphoff); ++ struct ip_vs_iphdr *iph); + + static int + tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -167,7 +167,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) ++ if (!tcp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -245,7 +245,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) ++ if (!tcp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -303,41 +303,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int tcphoff) ++ struct ip_vs_iphdr *iph) + { +- switch (skb->ip_summed) { +- case CHECKSUM_NONE: +- skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); +- fallthrough; +- case CHECKSUM_COMPLETE: +-#ifdef CONFIG_IP_VS_IPV6 +- if (af == AF_INET6) { +- if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, +- &ipv6_hdr(skb)->daddr, +- skb->len - tcphoff, +- IPPROTO_TCP, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- } else +-#endif +- if (csum_tcpudp_magic(ip_hdr(skb)->saddr, +- ip_hdr(skb)->daddr, +- skb->len - tcphoff, +- ip_hdr(skb)->protocol, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- break; +- default: +- /* No need to checksum. */ +- break; ++ if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) { ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); ++ return 0; + } +- + return 1; + } + +diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c +index dbd4155bb0752..40d30649b3048 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_udp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c +@@ -26,7 +26,7 @@ + + static int + udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int udphoff); ++ struct ip_vs_iphdr *iph); + + static int + udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -156,7 +156,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!udp_csum_check(cp->af, skb, pp, udphoff)) ++ if (!udp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -239,7 +239,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!udp_csum_check(cp->af, skb, pp, udphoff)) ++ if (!udp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -299,48 +299,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int udphoff) ++ struct ip_vs_iphdr *iph) + { + struct udphdr _udph, *uh; + +- uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph); ++ uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph); + if (uh == NULL) + return 0; + +- if (uh->check != 0) { +- switch (skb->ip_summed) { +- case CHECKSUM_NONE: +- skb->csum = skb_checksum(skb, udphoff, +- skb->len - udphoff, 0); +- fallthrough; +- case CHECKSUM_COMPLETE: +-#ifdef CONFIG_IP_VS_IPV6 +- if (af == AF_INET6) { +- if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, +- &ipv6_hdr(skb)->daddr, +- skb->len - udphoff, +- IPPROTO_UDP, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- } else +-#endif +- if (csum_tcpudp_magic(ip_hdr(skb)->saddr, +- ip_hdr(skb)->daddr, +- skb->len - udphoff, +- ip_hdr(skb)->protocol, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- break; +- default: +- /* No need to checksum. */ +- break; +- } ++ if (!uh->check) ++ return 1; ++ if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) { ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); ++ return 0; + } + return 1; + } +-- +2.53.0 + diff --git a/queue-6.18/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-6.18/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch new file mode 100644 index 0000000000..756425ff06 --- /dev/null +++ b/queue-6.18/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch @@ -0,0 +1,64 @@ +From 8625d8c6fcbd177a4e1a31ed588d3dcea9563325 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:03 -0400 +Subject: keys: fix out-of-bounds read in keyring_get_key_chunk() + +From: Michael Bommarito + +[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ] + +For description-level chunks keyring_get_key_chunk() advances the read +pointer by level * sizeof(long) past the inline prefix but only +bounds-checks the prefix, so a long enough key description is read past +its kmemdup(desc, desc_len + 1) allocation. Compute the full byte +offset and bounds-check the description against it before reading. + +The walk only reaches a description-level chunk when two keys collide +through the hash, x, type and domain_tag chunks, so this is reached from +an unprivileged add_key(2) with a crafted pair of same-type keys whose +index hashes collide; KASAN reports a slab-out-of-bounds read. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index e105349794f23..b9ca9dc9b0c3f 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + unsigned long chunk = 0; + const u8 *d; + int desc_len = index_key->desc_len, n = sizeof(chunk); ++ unsigned int offset; + + level /= ASSOC_ARRAY_KEY_CHUNK_SIZE; + switch (level) { +@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + return (unsigned long)index_key->domain_tag; + default: + level -= 4; +- if (desc_len <= sizeof(index_key->desc)) ++ offset = sizeof(index_key->desc) + level * sizeof(long); ++ if (desc_len <= offset) + return 0; + +- d = index_key->description + sizeof(index_key->desc); +- d += level * sizeof(long); +- desc_len -= sizeof(index_key->desc); ++ d = index_key->description + offset; ++ desc_len -= offset; + if (desc_len > n) + desc_len = n; + do { +-- +2.53.0 + diff --git a/queue-6.18/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-6.18/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch new file mode 100644 index 0000000000..17cc8b179f --- /dev/null +++ b/queue-6.18/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch @@ -0,0 +1,63 @@ +From 4e42560edb7a328b70f5775632698cef77cced0c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:04 -0400 +Subject: keys: make keyring key-chunk byte order agree with + keyring_diff_objects() + +From: Michael Bommarito + +[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ] + +keyring_get_key_chunk() loads description bytes into the index chunk low +address first, while keyring_diff_objects() numbers the first differing +bit from the low end and folds the absolute byte index into the level +without removing the inline-prefix offset the level already carries. +The two disagree on byte order and bit position, so the array can be +told two keys first differ at a bit that does not differ in the chunk +the walker uses, letting crafted descriptions collide into one node. + +Load the chunk in the order keyring_diff_objects() assumes and drop the +inline-prefix length when folding the byte index into the level. This +only changes the in-memory ordering used to place keys within a keyring; +add, search and read of non-colliding keys are unaffected. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index b9ca9dc9b0c3f..fd95a0eb7a466 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + desc_len -= offset; + if (desc_len > n) + desc_len = n; ++ d += desc_len; + do { + chunk <<= 8; +- chunk |= *d++; ++ chunk |= *--d; + } while (--desc_len > 0); + return chunk; + } +@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data) + return -1; + + differ_plus_i: +- level += i; ++ level += i - (int)sizeof(a->desc); + differ: + i = level * 8 + __ffs(seg_a ^ seg_b); + return i; +-- +2.53.0 + diff --git a/queue-6.18/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch b/queue-6.18/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch new file mode 100644 index 0000000000..18ab8ef51f --- /dev/null +++ b/queue-6.18/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch @@ -0,0 +1,96 @@ +From 42edd53e1e1eef7e9058e3f6d6a25278ceb11b2a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 19:22:30 +0300 +Subject: KEYS: trusted: dcp: fix key_len validation and calc_blob_len() return + type + +From: Fabrice Derepas + +[ Upstream commit 35d661c98fe4733490f20b4311616a3c2c30abc0 ] + +Two correctness and type-hygiene issues exist in the DCP trusted keys +implementation. + +First, trusted_dcp_unseal() reads p->key_len from a user-supplied blob +without checking if it exceeds MAX_KEY_SIZE. If a crafted blob provides a +payload_len larger than 128, the subsequent do_aead_crypto() call writes +past the end of the p->key array into the adjacent p->blob buffer within +the same struct trusted_key_payload -- the caller's own input, not +unrelated kernel memory. While not exploitable, this violates strict array +bounds and triggers static analyzers. Fix this by adding a validation +check against MIN_KEY_SIZE and MAX_KEY_SIZE immediately after reading the +length, matching the checks already done in trusted_core.c. + +Second, calc_blob_len() calculates a sum in size_t that truncates to +unsigned int on 64-bit platforms. Because the DCP hardware is only present +on 32-bit i.MX SoC platforms, size_t and unsigned int are functionally +equivalent in production, making this truncation harmless in practice. +Nevertheless, updating the return type to size_t (and subsequently updating +'blen' in the seal/unseal paths) resolves type-narrowing warnings and +improves overall code hygiene. + +Fixes: 2e8a0f40a39c ("KEYS: trusted: Introduce NXP DCP-backed trusted keys") +Signed-off-by: Fabrice Derepas +Reviewed-by: David Gstir +Reviewed-by: Richard Weinberger +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719163939.3624767-1-fabrice.derepas@canonical.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/trusted-keys/trusted_dcp.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +diff --git a/security/keys/trusted-keys/trusted_dcp.c b/security/keys/trusted-keys/trusted_dcp.c +index 7b6eb655df0cb..c078adebe190e 100644 +--- a/security/keys/trusted-keys/trusted_dcp.c ++++ b/security/keys/trusted-keys/trusted_dcp.c +@@ -69,7 +69,7 @@ static bool skip_zk_test; + module_param_named(dcp_skip_zk_test, skip_zk_test, bool, 0); + MODULE_PARM_DESC(dcp_skip_zk_test, "Don't test whether device keys are zero'ed"); + +-static unsigned int calc_blob_len(unsigned int payload_len) ++static size_t calc_blob_len(unsigned int payload_len) + { + return sizeof(struct dcp_blob_fmt) + payload_len + DCP_BLOB_AUTHLEN; + } +@@ -200,7 +200,8 @@ static int encrypt_blob_key(u8 *plain_key, u8 *encrypted_key) + static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob) + { + struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob; +- int blen, ret; ++ size_t blen; ++ int ret; + u8 *plain_blob_key; + + blen = calc_blob_len(p->key_len); +@@ -242,7 +243,8 @@ static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob) + static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob) + { + struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob; +- int blen, ret; ++ size_t blen; ++ int ret; + u8 *plain_blob_key = NULL; + + if (b->fmt_version != DCP_BLOB_VERSION) { +@@ -253,9 +255,14 @@ static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob) + } + + p->key_len = le32_to_cpu(b->payload_len); ++ if (p->key_len < MIN_KEY_SIZE || p->key_len > MAX_KEY_SIZE) { ++ ret = -EINVAL; ++ goto out; ++ } ++ + blen = calc_blob_len(p->key_len); + if (blen != p->blob_len) { +- pr_err("DCP blob has bad length: %i != %i\n", blen, ++ pr_err("DCP blob has bad length: %zu != %u\n", blen, + p->blob_len); + ret = -EINVAL; + goto out; +-- +2.53.0 + diff --git a/queue-6.18/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch b/queue-6.18/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch new file mode 100644 index 0000000000..d71124a3c7 --- /dev/null +++ b/queue-6.18/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch @@ -0,0 +1,50 @@ +From df74112f5a6d213976cefe20c9ef930f2d59bcfc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 10:04:19 +0900 +Subject: ksmbd: fix use-after-free in __close_file_table_ids() + +From: Namjae Jeon + +[ Upstream commit e7188199eff46a636f3436356f0aae039be6dd66 ] + +A ksmbd_file can remain alive after logical close while another session +holds a temporary reference obtained through ksmbd_lookup_fd_inode(). +ksmbd_close_fd() currently marks the file closed and drops the idr-owned +reference, but leaves the pointer published in the closing session's idr +until the final reference is dropped. + +If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final() +supplies the foreign session's file table to __ksmbd_close_fd(). The object +is then freed without being removed from its owner's idr, and the owner +session later dereferences the stale pointer during file-table teardown. + +Remove the volatile id from the owner's idr while ksmbd_close_fd() still +holds that table's lock, and clear volatile_id before dropping +the idr-owned reference. A later foreign final put then only performs +physical destruction and cannot remove the object from the wrong table. + +Fixes: 8510a043d334 ("ksmbd: increment reference count of parent fp") +Reported-by: Yunseong Kim +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs_cache.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index 0d3341927b483..51e37e89d1aa5 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -502,6 +502,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + fp = NULL; + else { + fp->f_state = FP_CLOSED; ++ idr_remove(ft->idr, id); ++ fp->volatile_id = KSMBD_NO_FID; + closed = true; + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; +-- +2.53.0 + diff --git a/queue-6.18/ksmbd-return-success-for-deferred-final-close.patch b/queue-6.18/ksmbd-return-success-for-deferred-final-close.patch new file mode 100644 index 0000000000..d51ae4f014 --- /dev/null +++ b/queue-6.18/ksmbd-return-success-for-deferred-final-close.patch @@ -0,0 +1,64 @@ +From 255944d9c251b74e758c3643bae5c9957265b972 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 21 Jun 2026 19:41:08 +0900 +Subject: ksmbd: return success for deferred final close + +From: Namjae Jeon + +[ Upstream commit c5db4de8988f1a621556ca5c4537f77b766ca07d ] + +ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table +reference. If another in-flight request still holds a reference, the final +close is deferred until that request drops its reference. + +The function currently returns -EINVAL in that deferred-final-close case +because fp is cleared when the reference count does not reach zero. That +turns a valid close into STATUS_FILE_CLOSED. + +smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then +closes the same directory handle before receiving the find response. +The query holds a reference while it builds the response, so close must +mark the handle closed and return success even though final teardown is +delayed. Track whether the handle was successfully transitioned to +FP_CLOSED and return success when only the final close is deferred. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: e7188199eff4 ("ksmbd: fix use-after-free in __close_file_table_ids()") +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs_cache.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index 5b6d8bb8edb27..0d3341927b483 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -487,6 +487,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + { + struct ksmbd_file *fp; + struct ksmbd_file_table *ft; ++ bool closed = false; + + if (!has_file_id(id)) + return 0; +@@ -501,6 +502,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + fp = NULL; + else { + fp->f_state = FP_CLOSED; ++ closed = true; + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; + } +@@ -508,7 +510,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + write_unlock(&ft->lock); + + if (!fp) +- return -EINVAL; ++ return closed ? 0 : -EINVAL; + + __put_fd_final(work, fp); + return 0; +-- +2.53.0 + diff --git a/queue-6.18/kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch b/queue-6.18/kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch new file mode 100644 index 0000000000..b49ae5c166 --- /dev/null +++ b/queue-6.18/kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch @@ -0,0 +1,121 @@ +From 31608e5104db439ca7e1ad0631b51a11950c3b6f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 10:03:54 +0100 +Subject: KVM: arm64: Reject guest_memfd memslots when the VM has MTE + +From: Alexandru Elisei + +[ Upstream commit 679d7201c1f09e37fa1c12ce28d84079c17fc87f ] + +The user cannot use MTE on VMAs created by mapping a guest_memfd file, +as arch_calc_vm_flag_bits() does not set VM_MTE_ALLOWED. + +When creating a guest_memfd backed memslot, +kvm_arch_prepare_memory_region() rejects the memslot if MTE is enabled for +the VM and if guest_memfd has been mapped in a VMA that intersects the +memslot. + +However, the documentation for KVM_SET_USER_MEMORY_REGION2 explicitly +states that the only condition for userspace_addr is for it to be a legal +userspace address, but the mapping is not required to be valid nor +populated at memslot creation. + +If userspace sets userspace_addr to an address that hasn't been mapped, or +if userspace_addr belongs to a VMA that isn't backed by the guest_memfd +file, or if the VMA doesn't intersect the memslot, memslot creation is +successful and KVM ends up with a VM with MTE and guest_memfd-backed +memslots. + +The same happens if the order is reversed: when userspace enables MTE, KVM +does not check if memslots backed by guest_memfd are already present. + +Fix both issues by rejecting guest_memfd-backed memslots when MTE is +enabled, and by rejecting MTE when guest_memfd-backed memslots are already +present. + +Fixes: 32e200bd6e44 ("KVM: arm64: Enable support for guest_memfd backed memory") +Tested-by: Fuad Tabba +Reviewed-by: Fuad Tabba +Signed-off-by: Alexandru Elisei +Link: https://patch.msgid.link/20260722090354.94245-1-alexandru.elisei@arm.com +Signed-off-by: Marc Zyngier +Signed-off-by: Sasha Levin +--- + Documentation/virt/kvm/api.rst | 6 ++++++ + arch/arm64/kvm/arm.c | 25 +++++++++++++++++++------ + arch/arm64/kvm/mmu.c | 4 ++++ + 3 files changed, 29 insertions(+), 6 deletions(-) + +diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst +index 9f835f68b4fb9..10d8a366b2072 100644 +--- a/Documentation/virt/kvm/api.rst ++++ b/Documentation/virt/kvm/api.rst +@@ -8259,6 +8259,12 @@ When this capability is enabled all memory in memslots must be mapped as + attempts to create a memslot with an invalid mmap will result in an + -EINVAL return. + ++``guest_memfd``, even though it is an anonymous file, is not supported with MTE. ++Attempting to create a memslot backed by ``guest_memfd`` when the MTE capability ++is enabled, or attempting to enable the MTE capability after ++``guest_memfd``-backed memslots have been created, will result in an -EINVAL ++return. ++ + When enabled the VMM may make use of the ``KVM_ARM_MTE_COPY_TAGS`` ioctl to + perform a bulk copy of tags to/from the guest. + +diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c +index 10fc6783cd608..5d2435dd76f35 100644 +--- a/arch/arm64/kvm/arm.c ++++ b/arch/arm64/kvm/arm.c +@@ -96,14 +96,27 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, + set_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER, + &kvm->arch.flags); + break; +- case KVM_CAP_ARM_MTE: +- mutex_lock(&kvm->lock); +- if (system_supports_mte() && !kvm->created_vcpus) { +- r = 0; +- set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags); ++ case KVM_CAP_ARM_MTE: { ++ struct kvm_memory_slot *memslot; ++ int bkt; ++ ++ guard(mutex)(&kvm->lock); ++ if (!system_supports_mte() || kvm->created_vcpus) ++ break; ++ ++ r = 0; ++ guard(mutex)(&kvm->slots_lock); ++ kvm_for_each_memslot(memslot, bkt, kvm_memslots(kvm)) { ++ if (kvm_slot_has_gmem(memslot)) { ++ r = -EINVAL; ++ break; ++ } + } +- mutex_unlock(&kvm->lock); ++ if (r == 0) ++ set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags); + break; ++ ++ } + case KVM_CAP_ARM_SYSTEM_SUSPEND: + r = 0; + set_bit(KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED, &kvm->arch.flags); +diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c +index 403a635483513..b4237699705b4 100644 +--- a/arch/arm64/kvm/mmu.c ++++ b/arch/arm64/kvm/mmu.c +@@ -2323,6 +2323,10 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, + if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new)) + return -EINVAL; + ++ /* guest_memfd is incompatible with MTE. */ ++ if (kvm_slot_has_gmem(new) && kvm_has_mte(kvm)) ++ return -EINVAL; ++ + hva = new->userspace_addr; + reg_end = hva + (new->npages << PAGE_SHIFT); + +-- +2.53.0 + diff --git a/queue-6.18/mshv-adjust-interrupt-control-structure-for-arm64.patch b/queue-6.18/mshv-adjust-interrupt-control-structure-for-arm64.patch new file mode 100644 index 0000000000..1ac0c28b46 --- /dev/null +++ b/queue-6.18/mshv-adjust-interrupt-control-structure-for-arm64.patch @@ -0,0 +1,124 @@ +From 619cc5b0e4ab1838b4307a76d03d30ad1cf81d93 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 24 Nov 2025 14:25:59 +0000 +Subject: mshv: adjust interrupt control structure for ARM64 + +From: Jinank Jain + +[ Upstream commit 9d70ef7a18e0ec1653ac63020a13a5d4dda7cc0d ] + +Interrupt control structure (union hv_interupt_control) has different +fields when it comes to x86 vs ARM64. Bring in the correct structure +from HyperV header files and adjust the existing interrupt routing +code accordingly. + +Signed-off-by: Jinank Jain +Signed-off-by: Anirudh Rayabharam (Microsoft) +Signed-off-by: Wei Liu +Stable-dep-of: 0289a67cd70b ("mshv: Fix level-triggered check on uninitialized data") +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_eventfd.c | 6 ++++++ + drivers/hv/mshv_irq.c | 4 ++++ + drivers/hv/mshv_root_hv_call.c | 6 ++++++ + include/hyperv/hvhdk.h | 6 ++++++ + 4 files changed, 22 insertions(+) + +diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c +index 586a9f99b3405..311aa9fecbc48 100644 +--- a/drivers/hv/mshv_eventfd.c ++++ b/drivers/hv/mshv_eventfd.c +@@ -164,8 +164,10 @@ static int mshv_try_assert_irq_fast(struct mshv_irqfd *irqfd) + if (hv_scheduler_type != HV_SCHEDULER_TYPE_ROOT) + return -EOPNOTSUPP; + ++#if IS_ENABLED(CONFIG_X86) + if (irq->lapic_control.logical_dest_mode) + return -EOPNOTSUPP; ++#endif + + vp = partition->pt_vp_array[irq->lapic_apic_id]; + +@@ -197,8 +199,10 @@ static void mshv_assert_irq_slow(struct mshv_irqfd *irqfd) + unsigned int seq; + int idx; + ++#if IS_ENABLED(CONFIG_X86) + WARN_ON(irqfd->irqfd_resampler && + !irq->lapic_control.level_triggered); ++#endif + + idx = srcu_read_lock(&partition->pt_irq_srcu); + if (irqfd->irqfd_girq_ent.guest_irq_num) { +@@ -469,6 +473,7 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, + init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc); + + spin_lock_irq(&pt->pt_irqfds_lock); ++#if IS_ENABLED(CONFIG_X86) + if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) && + !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) { + /* +@@ -479,6 +484,7 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, + ret = -EINVAL; + goto fail; + } ++#endif + ret = 0; + hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) { + if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx) +diff --git a/drivers/hv/mshv_irq.c b/drivers/hv/mshv_irq.c +index 743777b4886d3..7422a36199b40 100644 +--- a/drivers/hv/mshv_irq.c ++++ b/drivers/hv/mshv_irq.c +@@ -119,6 +119,10 @@ void mshv_copy_girq_info(struct mshv_guest_irq_ent *ent, + lirq->lapic_vector = ent->girq_irq_data & 0xFF; + lirq->lapic_apic_id = (ent->girq_addr_lo >> 12) & 0xFF; + lirq->lapic_control.interrupt_type = (ent->girq_irq_data & 0x700) >> 8; ++#if IS_ENABLED(CONFIG_X86) + lirq->lapic_control.level_triggered = (ent->girq_irq_data >> 15) & 0x1; + lirq->lapic_control.logical_dest_mode = (ent->girq_addr_lo >> 2) & 0x1; ++#elif IS_ENABLED(CONFIG_ARM64) ++ lirq->lapic_control.asserted = 1; ++#endif + } +diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c +index c9c274f29c3c6..a23b5ba09fb40 100644 +--- a/drivers/hv/mshv_root_hv_call.c ++++ b/drivers/hv/mshv_root_hv_call.c +@@ -388,7 +388,13 @@ int hv_call_assert_virtual_interrupt(u64 partition_id, u32 vector, + memset(input, 0, sizeof(*input)); + input->partition_id = partition_id; + input->vector = vector; ++ /* ++ * NOTE: dest_addr only needs to be provided while asserting an ++ * interrupt on x86 platform ++ */ ++#if IS_ENABLED(CONFIG_X86) + input->dest_addr = dest_addr; ++#endif + input->control = control; + status = hv_do_hypercall(HVCALL_ASSERT_VIRTUAL_INTERRUPT, input, NULL); + local_irq_restore(flags); +diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h +index 1057455b84f28..cdc74199ba3fb 100644 +--- a/include/hyperv/hvhdk.h ++++ b/include/hyperv/hvhdk.h +@@ -540,9 +540,15 @@ union hv_interrupt_control { + u64 as_uint64; + struct { + u32 interrupt_type; /* enum hv_interrupt_type */ ++#if IS_ENABLED(CONFIG_X86) + u32 level_triggered : 1; + u32 logical_dest_mode : 1; + u32 rsvd : 30; ++#elif IS_ENABLED(CONFIG_ARM64) ++ u32 rsvd1 : 2; ++ u32 asserted : 1; ++ u32 rsvd2 : 29; ++#endif + } __packed; + }; + +-- +2.53.0 + diff --git a/queue-6.18/mshv-fix-duplicate-gsi-detection-for-gsi-0.patch b/queue-6.18/mshv-fix-duplicate-gsi-detection-for-gsi-0.patch new file mode 100644 index 0000000000..67662cd0d5 --- /dev/null +++ b/queue-6.18/mshv-fix-duplicate-gsi-detection-for-gsi-0.patch @@ -0,0 +1,51 @@ +From a4b2b99effa4d7a31472d88ca325d24abfd1fcd6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 15:43:49 +0000 +Subject: mshv: Fix duplicate GSI detection for GSI 0 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Stanislav Kinsburskii + +[ Upstream commit 649dd135491945afa544351e3e6d4a727de87020 ] + +The duplicate routing entry check in mshv_update_routing_table() uses +guest_irq_num != 0 to detect whether a GSI slot is already occupied. +This fails for GSI 0 because its guest_irq_num is 0 both when the slot +is unused (zero-initialized) and when legitimately assigned. As a +result, duplicate entries for GSI 0 are silently accepted, with the +second entry overwriting the first — corrupting the routing table +without any error reported to userspace. + +While GSI 0 (legacy timer) is unlikely to appear in MSI-based routing +in practice, the check is semantically wrong — it conflates +"uninitialized" with "GSI number 0." Use girq_entry_valid instead, +which is explicitly set to true when an entry is populated and remains +zero for unused slots regardless of the GSI number. + +Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") +Signed-off-by: Stanislav Kinsburskii +Reviewed-by: Anirudh Rayabharam (Microsoft) +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_irq.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hv/mshv_irq.c b/drivers/hv/mshv_irq.c +index d0fb9ef734f42..743777b4886d3 100644 +--- a/drivers/hv/mshv_irq.c ++++ b/drivers/hv/mshv_irq.c +@@ -51,7 +51,7 @@ int mshv_update_routing_table(struct mshv_partition *partition, + /* + * Allow only one to one mapping between GSI and MSI routing. + */ +- if (girq->guest_irq_num != 0) { ++ if (girq->girq_entry_valid) { + r = -EINVAL; + goto out; + } +-- +2.53.0 + diff --git a/queue-6.18/mshv-fix-level-triggered-check-on-uninitialized-data.patch b/queue-6.18/mshv-fix-level-triggered-check-on-uninitialized-data.patch new file mode 100644 index 0000000000..bd9a1de3f8 --- /dev/null +++ b/queue-6.18/mshv-fix-level-triggered-check-on-uninitialized-data.patch @@ -0,0 +1,84 @@ +From f1567ca264d3562fa0b14b588223a75d4b92b823 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 15:43:43 +0000 +Subject: mshv: Fix level-triggered check on uninitialized data +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Stanislav Kinsburskii + +[ Upstream commit 0289a67cd70bf9d3807e289f4efd643e16a6c6b4 ] + +In mshv_irqfd_assign(), the level-triggered validation for resample +irqfds checks irqfd_lapic_irq.lapic_control.level_triggered before +mshv_irqfd_update() has populated the field. Since the irqfd struct is +zero-allocated, level_triggered is always 0 at that point, causing the +check to always reject resample irqfds with -EINVAL. This makes +level-triggered interrupt resampling — used to avoid interrupt storms +with assigned devices — completely non-functional. + +Move the check after the mshv_irqfd_update() call, which resolves the +IRQ routing entry and populates irqfd_lapic_irq with the actual trigger +mode. + +Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") +Signed-off-by: Stanislav Kinsburskii +Reviewed-by: Anirudh Rayabharam (Microsoft) +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_eventfd.c | 25 ++++++++++++++----------- + 1 file changed, 14 insertions(+), 11 deletions(-) + +diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c +index 311aa9fecbc48..65b969b82fb97 100644 +--- a/drivers/hv/mshv_eventfd.c ++++ b/drivers/hv/mshv_eventfd.c +@@ -473,6 +473,19 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, + init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc); + + spin_lock_irq(&pt->pt_irqfds_lock); ++ ret = 0; ++ hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) { ++ if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx) ++ continue; ++ /* This fd is used for another irq already. */ ++ ret = -EBUSY; ++ spin_unlock_irq(&pt->pt_irqfds_lock); ++ goto fail; ++ } ++ ++ idx = srcu_read_lock(&pt->pt_irq_srcu); ++ mshv_irqfd_update(pt, irqfd); ++ + #if IS_ENABLED(CONFIG_X86) + if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) && + !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) { +@@ -481,22 +494,12 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, + * Otherwise return with failure + */ + spin_unlock_irq(&pt->pt_irqfds_lock); ++ srcu_read_unlock(&pt->pt_irq_srcu, idx); + ret = -EINVAL; + goto fail; + } + #endif +- ret = 0; +- hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) { +- if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx) +- continue; +- /* This fd is used for another irq already. */ +- ret = -EBUSY; +- spin_unlock_irq(&pt->pt_irqfds_lock); +- goto fail; +- } + +- idx = srcu_read_lock(&pt->pt_irq_srcu); +- mshv_irqfd_update(pt, irqfd); + hlist_add_head(&irqfd->irqfd_hnode, &pt->pt_irqfds_list); + spin_unlock_irq(&pt->pt_irqfds_lock); + +-- +2.53.0 + diff --git a/queue-6.18/mshv-fix-race-in-mshv_irqfd_deassign.patch b/queue-6.18/mshv-fix-race-in-mshv_irqfd_deassign.patch new file mode 100644 index 0000000000..42206c9d5a --- /dev/null +++ b/queue-6.18/mshv-fix-race-in-mshv_irqfd_deassign.patch @@ -0,0 +1,68 @@ +From b166e91ba1baf83ffc256893f58015988b0e0227 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 15:43:15 +0000 +Subject: mshv: Fix race in mshv_irqfd_deassign + +From: Stanislav Kinsburskii + +[ Upstream commit 0762262ac3e70f65b3bb843fe892f8bac1562d08 ] + +mshv_irqfd_deactivate() and the hlist traversal of pt_irqfds_list +require pt->pt_irqfds_lock to be held, but mshv_irqfd_deassign() +omits it. This races with the EPOLLHUP path in mshv_irqfd_wakeup(), +which does take the lock before calling mshv_irqfd_deactivate(). + +Additionally, mshv_irqfd_deactivate() uses hlist_del() which poisons +the node pointers rather than resetting them. Since +mshv_irqfd_is_active() relies on hlist_unhashed() (checks pprev == +NULL), a poisoned node still appears active. If a concurrent path calls +mshv_irqfd_deactivate() again on the same irqfd, the guard fails to +prevent a double hlist_del() on poisoned pointers. + +Fix both issues: +- Add the missing spin_lock_irq/spin_unlock_irq around the list + traversal in mshv_irqfd_deassign(), matching mshv_irqfd_release(). +- Use hlist_del_init() instead of hlist_del() so the node is properly + marked as unhashed after removal, making the is_active guard reliable. + +Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") +Signed-off-by: Stanislav Kinsburskii +Reviewed-by: Anirudh Rayabharam (Microsoft) +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_eventfd.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c +index 40b7179fee805..586a9f99b3405 100644 +--- a/drivers/hv/mshv_eventfd.c ++++ b/drivers/hv/mshv_eventfd.c +@@ -280,7 +280,7 @@ static void mshv_irqfd_deactivate(struct mshv_irqfd *irqfd) + if (!mshv_irqfd_is_active(irqfd)) + return; + +- hlist_del(&irqfd->irqfd_hnode); ++ hlist_del_init(&irqfd->irqfd_hnode); + + queue_work(irqfd_cleanup_wq, &irqfd->irqfd_shutdown); + } +@@ -535,13 +535,14 @@ static int mshv_irqfd_deassign(struct mshv_partition *pt, + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + ++ spin_lock_irq(&pt->pt_irqfds_lock); + hlist_for_each_entry_safe(irqfd, n, &pt->pt_irqfds_list, + irqfd_hnode) { + if (irqfd->irqfd_eventfd_ctx == eventfd && + irqfd->irqfd_irqnum == args->gsi) +- + mshv_irqfd_deactivate(irqfd); + } ++ spin_unlock_irq(&pt->pt_irqfds_lock); + + eventfd_ctx_put(eventfd); + +-- +2.53.0 + diff --git a/queue-6.18/mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch b/queue-6.18/mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch new file mode 100644 index 0000000000..3143fa7eff --- /dev/null +++ b/queue-6.18/mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch @@ -0,0 +1,50 @@ +From 563e09c11ebb1d0e028a256f4cf5ec6752fe5e43 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 15:43:59 +0000 +Subject: mshv: Fix sleeping under spinlock in mshv_portid_alloc + +From: Stanislav Kinsburskii + +[ Upstream commit a9708e550d11a53b22d932cdbfaa10c26346a2d0 ] + +idr_alloc() is called with GFP_KERNEL inside idr_lock(), which holds a +spinlock. GFP_KERNEL allows the allocator to sleep, triggering a +sleeping-while-atomic bug. + +Fix by using idr_preload(GFP_KERNEL) before taking the lock to +pre-allocate memory in a sleepable context, then idr_alloc() with +GFP_NOWAIT inside the spinlock-protected section. + +Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") +Signed-off-by: Stanislav Kinsburskii +Reviewed-by: Anirudh Rayabharam (Microsoft) +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_portid_table.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/hv/mshv_portid_table.c b/drivers/hv/mshv_portid_table.c +index c349af1f0aaac..6f59b3e376247 100644 +--- a/drivers/hv/mshv_portid_table.c ++++ b/drivers/hv/mshv_portid_table.c +@@ -40,12 +40,14 @@ mshv_port_table_fini(void) + int + mshv_portid_alloc(struct port_table_info *info) + { +- int ret = 0; ++ int ret; + ++ idr_preload(GFP_KERNEL); + idr_lock(&port_table_idr); + ret = idr_alloc(&port_table_idr, info, PORTID_MIN, +- PORTID_MAX, GFP_KERNEL); ++ PORTID_MAX, GFP_NOWAIT); + idr_unlock(&port_table_idr); ++ idr_preload_end(); + + return ret; + } +-- +2.53.0 + diff --git a/queue-6.18/mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch b/queue-6.18/mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch new file mode 100644 index 0000000000..06e42a019d --- /dev/null +++ b/queue-6.18/mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch @@ -0,0 +1,94 @@ +From 786f40bff5e01cd7c5313ab9ce3462380c3602c6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 00:28:11 +0000 +Subject: mshv: Order pt_vp_array publish against irqfd assertion path + +From: Stanislav Kinsburskii + +[ Upstream commit b098dc869219c15dc49bf9cf63fb5fc1481d3373 ] + +mshv_partition_ioctl_create_vp() initialises a VP struct (allocations, +mutex_init, init_waitqueue_head, page mappings) and then publishes the +pointer into partition->pt_vp_array. Several ISR paths read this array +locklessly: the intercept ISR, the two scheduler ISRs, and +mshv_try_assert_irq_fast() on the irqfd fast path. + +Of these, only mshv_try_assert_irq_fast() can structurally race the +publish. It runs from an eventfd waker without holding pt_mutex, and +MSHV_IRQFD does not require the target lapic_apic_id (== vp_index) to +refer to an existing VP at registration time. A user can therefore +register an irqfd targeting a yet-to-be-created VP, then trigger +mshv_try_assert_irq_fast() concurrently with MSHV_CREATE_VP for the +same index. On weakly-ordered architectures the reader can observe a +non-NULL pointer in pt_vp_array before the initialising stores to the +VP struct become visible, leading to use of partially-initialised +fields (e.g. vp_register_page). + +The other ISR readers cannot reach this race: the hypervisor will not +generate intercept or scheduler messages for a VP that has never been +told to run, and the user can only call MSHV_RUN_VP on the VP fd +returned by MSHV_CREATE_VP, which by construction is returned after +the publish. Leave those readers as plain loads. + +Use smp_store_release() in mshv_partition_ioctl_create_vp() to publish +the pointer, and pair it with smp_load_acquire() in +mshv_try_assert_irq_fast(). On x86 these compile to plain accesses +under TSO; on ARM64 they emit one-instruction acquire/release barriers, +acceptable on this fast path. + +The destroy-side path (destroy_partition() clearing pt_vp_array[i] to +NULL after kfree(vp)) has a separate ordering and lifetime concern +that is out of scope here. + +Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") +Signed-off-by: Stanislav Kinsburskii +Reviewed-by: Anirudh Rayabharam (Microsoft) +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_eventfd.c | 9 ++++++++- + drivers/hv/mshv_root_main.c | 8 +++++++- + 2 files changed, 15 insertions(+), 2 deletions(-) + +diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c +index 65b969b82fb97..161a9655aa0b1 100644 +--- a/drivers/hv/mshv_eventfd.c ++++ b/drivers/hv/mshv_eventfd.c +@@ -169,7 +169,14 @@ static int mshv_try_assert_irq_fast(struct mshv_irqfd *irqfd) + return -EOPNOTSUPP; + #endif + +- vp = partition->pt_vp_array[irq->lapic_apic_id]; ++ /* ++ * Pairs with smp_store_release() in mshv_partition_ioctl_create_vp(). ++ * MSHV_IRQFD does not require the target lapic_apic_id to refer to an ++ * existing VP, so this read can race a concurrent VP creation; the ++ * acquire ensures that a non-NULL pointer implies the VP's ++ * initialising stores are visible. ++ */ ++ vp = smp_load_acquire(&partition->pt_vp_array[irq->lapic_apic_id]); + + if (!vp->vp_register_page) + return -EOPNOTSUPP; +diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c +index 4e04bef544379..3a356b34774bd 100644 +--- a/drivers/hv/mshv_root_main.c ++++ b/drivers/hv/mshv_root_main.c +@@ -979,7 +979,13 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, + + /* already exclusive with the partition mutex for all ioctls */ + partition->pt_vp_count++; +- partition->pt_vp_array[args.vp_index] = vp; ++ /* ++ * Pairs with smp_load_acquire() in mshv_try_assert_irq_fast(), which ++ * can run concurrently from an irqfd waker without holding pt_mutex. ++ * The release ensures the VP's initialising stores are visible to any ++ * reader that observes a non-NULL pointer in pt_vp_array. ++ */ ++ smp_store_release(&partition->pt_vp_array[args.vp_index], vp); + + return ret; + +-- +2.53.0 + diff --git a/queue-6.18/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch b/queue-6.18/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch new file mode 100644 index 0000000000..392216b39f --- /dev/null +++ b/queue-6.18/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch @@ -0,0 +1,40 @@ +From afe1126da59a312be82e7804770a3630884f43a6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 06:26:05 +0000 +Subject: net: bridge: mrp: fix Option TLV length in MRP_Test frames + +From: David Corvaglia + +[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ] + +oui is a pointer, so sizeof(oui) is the pointer size. The MRA +Option TLV thus advertises a wrong length (15 vs 10 on x86_64), +causing misparsing of the frame on peers. Fix is to replace +with sizeof(*oui). + +Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA") +Signed-off-by: David Corvaglia +Acked-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_mrp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c +index 3c36fa24bc05a..92a0debf4a4a6 100644 +--- a/net/bridge/br_mrp.c ++++ b/net/bridge/br_mrp.c +@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp, + struct br_mrp_oui_hdr *oui = NULL; + u8 length; + +- length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) + ++ length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) + + MRP_OPT_PADDING; + br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length); + +-- +2.53.0 + diff --git a/queue-6.18/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch b/queue-6.18/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch new file mode 100644 index 0000000000..4257d617dc --- /dev/null +++ b/queue-6.18/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch @@ -0,0 +1,78 @@ +From da68af48919f93c0c3c2d3bec22dfcacbc22ecd7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 07:29:01 +0000 +Subject: net: do not send ICMP/NDISC Redirects when peer allocation fails + +From: Eric Dumazet + +[ Upstream commit dbc3791e3b2472e1ccc08947e0f83b443470ff4f ] + +When inet_getpeer_v4() or inet_getpeer_v6() fails to allocate a peer entry +under memory pressure or tree size caps, redirect handlers previously fell +back to sending un-rate-limited ICMP/NDISC Redirect messages. + +In IPv4, ip_rt_send_redirect() called icmp_send() directly when peer == NULL. +In IPv6, ip6_forward() and ndisc_send_redirect() passed a NULL peer into +inet_peer_xrlim_allow(), which returned true when peer == NULL. + +Because ICMP/NDISC Redirects are not part of the default global rate limit +mask (sysctl_icmp_ratemask), sending redirects when peer == NULL creates +an un-rate-limited ICMP packet storm. + +Fix this by failing closed in ip_rt_send_redirect(), ip6_forward(), and +ndisc_send_redirect() when peer is NULL. + +Fixes: 92d868292634 ("inetpeer: Move ICMP rate limiting state into inet_peer entries.") +Signed-off-by: Eric Dumazet +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260724072901.1633601-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/route.c | 2 -- + net/ipv6/ip6_output.c | 2 +- + net/ipv6/ndisc.c | 2 ++ + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/net/ipv4/route.c b/net/ipv4/route.c +index 11d990703d31a..778467bea476a 100644 +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -892,8 +892,6 @@ void ip_rt_send_redirect(struct sk_buff *skb) + peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif); + if (!peer) { + rcu_read_unlock(); +- icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, +- rt_nexthop(rt, ip_hdr(skb)->daddr)); + return; + } + +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index 8f37c9cc868b0..9ef6581168f0f 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -630,7 +630,7 @@ int ip6_forward(struct sk_buff *skb) + /* Limit redirects both by destination (here) + and by source (inside ndisc_send_redirect) + */ +- if (inet_peer_xrlim_allow(peer, 1*HZ)) ++ if (peer && inet_peer_xrlim_allow(peer, 1*HZ)) + ndisc_send_redirect(skb, target); + rcu_read_unlock(); + } else { +diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c +index 9df1fba967c6e..7262356de10d9 100644 +--- a/net/ipv6/ndisc.c ++++ b/net/ipv6/ndisc.c +@@ -1706,6 +1706,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) + } + + peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr); ++ if (!peer) ++ goto release; + ret = inet_peer_xrlim_allow(peer, 1*HZ); + + if (!ret) +-- +2.53.0 + diff --git a/queue-6.18/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch b/queue-6.18/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch new file mode 100644 index 0000000000..794c4d348f --- /dev/null +++ b/queue-6.18/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch @@ -0,0 +1,53 @@ +From e72cae329e8d92865c4428c997e168428cd2e473 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 05:52:14 +0100 +Subject: net: dsa: mt7530: check bus->read() errors in the MDIO regmap backend + +From: Daniel Golle + +[ Upstream commit b4ce102b2cd88424c5860fbbb20b9eb343a93bf4 ] + +bus->read() returns a negative errno on failure, but +mt7530_regmap_read() assigns it to a u16, truncating e.g. -ETIMEDOUT +into 0xff92, and returns success. The garbage word is then consumed as +register data, and read-modify-write cycles write it back to the +switch. Check both reads and propagate their errors. + +The same defect existed in mt7530_mii_read() since the driver was +introduced and moved into the regmap backend unchanged. + +Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch") +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/3c628e48276c2e5522c8795a6be60d11c7a76a7d.1785213071.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530-mdio.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/dsa/mt7530-mdio.c b/drivers/net/dsa/mt7530-mdio.c +index 0286a6cecb6f5..6cc2c8975af66 100644 +--- a/drivers/net/dsa/mt7530-mdio.c ++++ b/drivers/net/dsa/mt7530-mdio.c +@@ -55,8 +55,15 @@ mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) + if (ret < 0) + return ret; + +- lo = bus->read(bus, priv->mdiodev->addr, r); +- hi = bus->read(bus, priv->mdiodev->addr, 0x10); ++ ret = bus->read(bus, priv->mdiodev->addr, r); ++ if (ret < 0) ++ return ret; ++ lo = ret; ++ ++ ret = bus->read(bus, priv->mdiodev->addr, 0x10); ++ if (ret < 0) ++ return ret; ++ hi = ret; + + *val = (hi << 16) | (lo & 0xffff); + +-- +2.53.0 + diff --git a/queue-6.18/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch b/queue-6.18/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch new file mode 100644 index 0000000000..a4b2d765be --- /dev/null +++ b/queue-6.18/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch @@ -0,0 +1,191 @@ +From 6bd91df9e53d5482d28a24bd7e2615abf7225073 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 05:52:29 +0100 +Subject: net: dsa: mt7530: error out on failed reads in MT7531 PHY polling + +From: Daniel Golle + +[ Upstream commit 77a9ebe8818cf6dd1699bd6728cb5d66307801d7 ] + +The MT7531 indirect PHY access functions poll MT7531_PHY_IAC through +a helper which returns 0 when the underlying read fails, so a failed +bus transaction clears MT7531_PHY_ACS_ST and the access carries on, +returning garbage PHY register data to phylib. + +Poll using regmap_read_poll_timeout(), which stops on read errors and +propagates them. These functions hold the MDIO bus lock across the +whole sequence, so the unlocked regmap accesses remain correct. Remove +the now-unused _mt7530_unlocked_read(). + +Fixes: c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch") +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/79e85d68d210cc37342978171aa6432aa2954333.1785213071.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 58 ++++++++++++++-------------------------- + 1 file changed, 20 insertions(+), 38 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 4571da0d7a8f6..2a70fa4b5fa3e 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -188,12 +188,6 @@ mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val) + mt7530_mutex_unlock(priv); + } + +-static u32 +-_mt7530_unlocked_read(struct mt7530_dummy_poll *p) +-{ +- return mt7530_mii_read(p->priv, p->reg); +-} +- + static u32 + _mt7530_read(struct mt7530_dummy_poll *p) + { +@@ -546,16 +540,13 @@ static int + mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + int regnum) + { +- struct mt7530_dummy_poll p; + u32 reg, val; + int ret; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -565,8 +556,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad) | regnum; + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -576,8 +567,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad); + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -594,16 +585,13 @@ static int + mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + int regnum, u16 data) + { +- struct mt7530_dummy_poll p; + u32 val, reg; + int ret; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -613,8 +601,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad) | regnum; + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -624,8 +612,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad) | data; + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -640,16 +628,13 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + static int + mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) + { +- struct mt7530_dummy_poll p; + int ret; + u32 val; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -660,8 +645,8 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) + + mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -678,16 +663,13 @@ static int + mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, + u16 data) + { +- struct mt7530_dummy_poll p; + int ret; + u32 reg; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, +- !(reg & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg, ++ !(reg & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -698,8 +680,8 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, + + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, +- !(reg & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg, ++ !(reg & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +-- +2.53.0 + diff --git a/queue-6.18/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch b/queue-6.18/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch new file mode 100644 index 0000000000..233e6714c8 --- /dev/null +++ b/queue-6.18/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch @@ -0,0 +1,40 @@ +From 3a8c733299c67ed5d305a65c72450b345cdf9283 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 13:57:35 +0800 +Subject: net: ethernet: mtk_eth_soc: pass eth to mtk_handle_irq_rx in + poll_controller + +From: Chenguang Zhao + +[ Upstream commit e095f249e2209674f6366f6db0383a2b96e19239 ] + +mtk_handle_irq_rx expects a struct mtk_eth * (matching the request_irq +cookie), but mtk_poll_controller incorrectly passed the net_device *. +Calling ndo_poll_controller with CONFIG_NET_POLL_CONTROLLER enabled +would then crash. + +Fixes: 8186f6e382d8 ("net-next: mediatek: fix compile error inside mtk_poll_controller()") +Signed-off-by: Chenguang Zhao +Link: https://patch.msgid.link/20260723055735.885112-1-chenguang.zhao@linux.dev +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +index 065f969ee44ef..98cf11a706e14 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3446,7 +3446,7 @@ static void mtk_poll_controller(struct net_device *dev) + + mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); + mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask); +- mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], dev); ++ mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], eth); + mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); + mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask); + } +-- +2.53.0 + diff --git a/queue-6.18/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch b/queue-6.18/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch new file mode 100644 index 0000000000..9670893d8f --- /dev/null +++ b/queue-6.18/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch @@ -0,0 +1,48 @@ +From 7c5591882aeafab6b5e8612fcb70df57fb1aca09 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 15:46:57 +0800 +Subject: net: libwx: fix FDIR ATR queue mismatch for software VLAN packets + +From: Jiawen Wu + +[ Upstream commit 732ed8f75ce583d115716f668dc80d730f3ad610 ] + +When TX VLAN hardware offload is disabled, VLAN tags are embedded in +the packet payload (software VLAN). Previously, the driver failed to +set the WX_TX_FLAGS_SW_VLAN flag for these packets during transmission. + +This missing flag caused the txgbe FDIR ATR logic to fall through to the +default hash calculation path. This resulted in asymmetric hash values +for Tx and Rx flows, preventing return packets from being steered to the +same queue as the transmit packets. + +Fix this by detecting software VLANs via eth_type_vlan(skb->protocol) +and setting WX_TX_FLAGS_SW_VLAN. This ensures the ATR feature selects +the correct hashing algorithm to maintain Tx/Rx queue symmetry. + +Fixes: b501d261a5b3 ("net: txgbe: add FDIR ATR support") +Signed-off-by: Jiawen Wu +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/0879DA38A8E32701+20260724074657.10773-1-jiawenwu@trustnetic.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/wangxun/libwx/wx_lib.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c +index 678dce480f156..380edc64e45e4 100644 +--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c ++++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c +@@ -1549,6 +1549,8 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb, + if (skb_vlan_tag_present(skb)) { + tx_flags |= skb_vlan_tag_get(skb) << WX_TX_FLAGS_VLAN_SHIFT; + tx_flags |= WX_TX_FLAGS_HW_VLAN; ++ } else if (eth_type_vlan(skb->protocol)) { ++ tx_flags |= WX_TX_FLAGS_SW_VLAN; + } + + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && +-- +2.53.0 + diff --git a/queue-6.18/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-6.18/net-phylink-put-link_gpio-if-phylink_create-fails.patch new file mode 100644 index 0000000000..eb1e088f20 --- /dev/null +++ b/queue-6.18/net-phylink-put-link_gpio-if-phylink_create-fails.patch @@ -0,0 +1,92 @@ +From a9b24e2fb20c4e59c61267471f7f1bcbd75b3209 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 17:08:05 +0200 +Subject: net: phylink: put link_gpio if phylink_create fails + +From: Christian Marangi + +[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ] + +In phylink_create() if phylink_register_sfp() returns an error, link_gpio +obtained by phylink_parse_fixedlink() is never released. While this is a +very unlikely scenario, it's worth to fix/handle this. + +This was present from the very first implementation of phylink but got +relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to +bridge between network devices and sfp cages") where additional function +were added after phylink_parse_fixedlink() making the release of link_gpio +needed if such additional function errored out. + +While at it, restructure the exit condition of phylink_create() with the +goto pattern to reduce code duplication on handling error conditions. + +Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages") +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/phy/phylink.c | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 9182443082158..1b7fc17bf3919 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1861,8 +1861,8 @@ struct phylink *phylink_create(struct phylink_config *config, + } else if (config->type == PHYLINK_DEV) { + pl->dev = config->dev; + } else { +- kfree(pl); +- return ERR_PTR(-EINVAL); ++ ret = -EINVAL; ++ goto free_pl; + } + + pl->mac_supports_eee_ops = phylink_mac_implements_lpi(mac_ops); +@@ -1895,28 +1895,29 @@ struct phylink *phylink_create(struct phylink_config *config, + phylink_validate(pl, pl->supported, &pl->link_config); + + ret = phylink_parse_mode(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto free_pl; + + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { + ret = phylink_parse_fixedlink(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + } + + pl->req_link_an_mode = pl->cfg_link_an_mode; + + ret = phylink_register_sfp(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + + return pl; ++ ++release_link_gpio: ++ if (pl->link_gpio) ++ gpiod_put(pl->link_gpio); ++free_pl: ++ kfree(pl); ++ return ERR_PTR(ret); + } + EXPORT_SYMBOL_GPL(phylink_create); + +-- +2.53.0 + diff --git a/queue-6.18/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-6.18/net-sxgbe-check-descriptor-ring-allocation-failures.patch new file mode 100644 index 0000000000..77d98ef8c9 --- /dev/null +++ b/queue-6.18/net-sxgbe-check-descriptor-ring-allocation-failures.patch @@ -0,0 +1,48 @@ +From 8d473c088527dda530755b0249415c293e09a77d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:20 +0800 +Subject: net: sxgbe: check descriptor ring allocation failures + +From: Chenguang Zhao + +[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ] + +sxgbe_open() ignores the return value of init_dma_desc_rings() and +continues to program DMA with invalid ring addresses when allocation +fails. Check the return value and disconnect the PHY on failure. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index 5eb8e7c232f8b..848a0578cea1f 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -1080,7 +1080,9 @@ static int sxgbe_open(struct net_device *dev) + priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE); + priv->tx_tc = TC_DEFAULT; + priv->rx_tc = TC_DEFAULT; +- init_dma_desc_rings(dev); ++ ret = init_dma_desc_rings(dev); ++ if (ret) ++ goto init_phy_error; + + /* DMA initialization and SW reset */ + ret = sxgbe_init_dma_engine(priv); +@@ -1189,6 +1191,7 @@ static int sxgbe_open(struct net_device *dev) + + init_error: + free_dma_desc_resources(priv); ++init_phy_error: + if (dev->phydev) + phy_disconnect(dev->phydev); + phy_error: +-- +2.53.0 + diff --git a/queue-6.18/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-6.18/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch new file mode 100644 index 0000000000..3f766290dc --- /dev/null +++ b/queue-6.18/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch @@ -0,0 +1,50 @@ +From 59ab621edb1cef2e59580d2e0b5686232d736581 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:19 +0800 +Subject: net: sxgbe: free TX rings on RX allocation failure + +From: Chenguang Zhao + +[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ] + +When RX descriptor ring allocation fails, init_dma_desc_rings() only +frees the partially allocated RX rings and returns. The TX rings that +were allocated earlier in the same function are leaked. + +Rearrange error labels to clean up TX rings upon RX failures. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index 849c5a6c2af1e..5eb8e7c232f8b 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -599,14 +599,13 @@ static int init_dma_desc_rings(struct net_device *netd) + + return 0; + +-txalloc_err: +- while (queue_num--) +- free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); +- return ret; +- + rxalloc_err: + while (queue_num--) + free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize); ++ queue_num = SXGBE_TX_QUEUES; ++txalloc_err: ++ while (queue_num--) ++ free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.18/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch b/queue-6.18/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch new file mode 100644 index 0000000000..780c6672e8 --- /dev/null +++ b/queue-6.18/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch @@ -0,0 +1,150 @@ +From 847afdba1315ad16fbd4747aed21f9a661d1441a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 09:11:37 +0000 +Subject: net: udp_tunnel: fix memory leak in udp_tunnel_nic_unregister() + +From: Eric Dumazet + +[ Upstream commit 080695e6f005e2396f1207fd69d24c442cb230c6 ] + +syzbot reported a memory leak [1] in the UDP tunnel NIC offload code. + +When device registration fails (e.g. in register_netdevice()), netdev core +unwinds by sending a single NETDEV_UNREGISTER notification. If work was queued +during NETDEV_REGISTER (utn->work_pending is set), udp_tunnel_nic_unregister() +returns early: + + if (utn->work_pending) + return; + +Because failed registrations do not enter netdev_wait_allrefs_any(), no +subsequent NETDEV_UNREGISTER rebroadcast will ever occur. As a result, the +struct udp_tunnel_nic allocated in udp_tunnel_nic_alloc() is leaked +permanently. + +Fix this by removing the early return. Instead, synchronously cancel any +pending work with cancel_delayed_work_sync() before freeing @utn. + +To be able to call cancel_delayed_work_sync() while holding RTNL (the work also +needs RTNL), switch udp_tunnel_nic_device_sync_work() to rtnl_trylock(). If RTNL +is contended, requeue the work with a 1 jiffy delay (via queue_delayed_work()) +to prevent high CPU contention while waiting for RTNL lock. + +The utn->work_pending bookkeeping is no longer needed and is removed, as +the workqueue core already tracks the pending/running state of the work. + +[1] +BUG: memory leak +unreferenced object 0xffff888127d5f840 (size 96): + comm "syz-executor", pid 5806, jiffies 4294942188 + backtrace (crc 99fdb6c8): + __kmalloc_noprof+0x3bf/0x550 + udp_tunnel_nic_alloc net/ipv4/udp_tunnel_nic.c:756 [inline] + udp_tunnel_nic_register net/ipv4/udp_tunnel_nic.c:833 [inline] + udp_tunnel_nic_netdevice_event+0x804/0xab0 net/ipv4/udp_tunnel_nic.c:931 + notifier_call_chain+0x59/0x160 kernel/notifier.c:85 + call_netdevice_notifiers_info+0x7d/0xb0 net/core/dev.c:2250 + register_netdevice+0xc10/0xeb0 net/core/dev.c:11478 + +Fixes: cc4e3835eff4 ("udp_tunnel: add central NIC RX port offload infrastructure") +Reported-by: syzbot+eca845fb8c18dd6b44c1@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/netdev/6a632b15.dde6c935.cf6c8.0011.GAE@google.com/T/#u +Signed-off-by: Eric Dumazet +Link: https://patch.msgid.link/20260724091137.1792543-1-edumazet@google.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/ipv4/udp_tunnel_nic.c | 32 +++++++++++++++++--------------- + 1 file changed, 17 insertions(+), 15 deletions(-) + +diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c +index b13e8f7092f46..4360d159ee8a7 100644 +--- a/net/ipv4/udp_tunnel_nic.c ++++ b/net/ipv4/udp_tunnel_nic.c +@@ -32,13 +32,12 @@ struct udp_tunnel_nic_table_entry { + * @lock: protects all fields + * @need_sync: at least one port start changed + * @need_replay: space was freed, we need a replay of all ports +- * @work_pending: @work is currently scheduled + * @n_tables: number of tables under @entries + * @missed: bitmap of tables which overflown + * @entries: table of tables of ports currently offloaded + */ + struct udp_tunnel_nic { +- struct work_struct work; ++ struct delayed_work work; + + struct net_device *dev; + +@@ -46,7 +45,6 @@ struct udp_tunnel_nic { + + u8 need_sync:1; + u8 need_replay:1; +- u8 work_pending:1; + + unsigned int n_tables; + unsigned long missed; +@@ -301,11 +299,10 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) + static void + udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) + { +- if (!utn->need_sync || utn->work_pending) ++ if (!utn->need_sync) + return; + +- queue_work(udp_tunnel_nic_workqueue, &utn->work); +- utn->work_pending = 1; ++ queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 0); + } + + static bool +@@ -731,12 +728,17 @@ udp_tunnel_nic_replay(struct net_device *dev, struct udp_tunnel_nic *utn) + static void udp_tunnel_nic_device_sync_work(struct work_struct *work) + { + struct udp_tunnel_nic *utn = +- container_of(work, struct udp_tunnel_nic, work); ++ container_of(work, struct udp_tunnel_nic, work.work); + +- rtnl_lock(); ++ /* We cannot block on RTNL here, otherwise we would deadlock with ++ * udp_tunnel_nic_unregister() calling cancel_delayed_work_sync() ++ * while holding RTNL. Requeue with 1 jiffy delay if RTNL is contended. ++ */ ++ if (!rtnl_trylock()) { ++ queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 1); ++ return; ++ } + mutex_lock(&utn->lock); +- +- utn->work_pending = 0; + __udp_tunnel_nic_device_sync(utn->dev, utn); + + if (utn->need_replay) +@@ -757,7 +759,7 @@ udp_tunnel_nic_alloc(const struct udp_tunnel_nic_info *info, + if (!utn) + return NULL; + utn->n_tables = n_tables; +- INIT_WORK(&utn->work, udp_tunnel_nic_device_sync_work); ++ INIT_DELAYED_WORK(&utn->work, udp_tunnel_nic_device_sync_work); + mutex_init(&utn->lock); + + for (i = 0; i < n_tables; i++) { +@@ -901,11 +903,11 @@ udp_tunnel_nic_unregister(struct net_device *dev, struct udp_tunnel_nic *utn) + udp_tunnel_nic_flush(dev, utn); + udp_tunnel_nic_unlock(dev); + +- /* Wait for the work to be done using the state, netdev core will +- * retry unregister until we give up our reference on this device. ++ /* Make sure no work is running or queued before freeing @utn. ++ * The work handler uses rtnl_trylock(), so it will not deadlock ++ * against the RTNL we are holding here. + */ +- if (utn->work_pending) +- return; ++ cancel_delayed_work_sync(&utn->work); + + udp_tunnel_nic_free(utn); + release_dev: +-- +2.53.0 + diff --git a/queue-6.18/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-6.18/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch new file mode 100644 index 0000000000..113df09f51 --- /dev/null +++ b/queue-6.18/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch @@ -0,0 +1,96 @@ +From fb13068c94e776a46cbfbc6b248b97cc4fa39d4c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 16:42:01 -0700 +Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in + sip_help_tcp() + +From: Xiang Mei + +[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ] + +sip_help_tcp() stores the size change of each NAT-rewritten SIP message +in s16 diff and accumulates it in s16 tdiff, but a single message can +grow by more than S16_MAX while the packet stays under the 65535 +enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long +Contact list expands the message by tens of kilobytes. diff then wraps, +and "datalen = datalen + diff - msglen" yields a huge unsigned datalen, +so the next iteration's ct_sip_get_header() reads past the linearized skb +tail. + +Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the +65535 byte packet limit, and the seqadj core is already s32 +(nf_ct_seqadj_set() takes s32), so no previously accepted input is +rejected. + + BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25 + ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694) + nf_confirm (net/netfilter/nf_conntrack_proto.c:183) + nf_hook_slow (net/netfilter/core.c:619) + ip6_output (net/ipv6/ip6_output.c:246) + ip6_forward (net/ipv6/ip6_output.c:690) + ipv6_rcv (net/ipv6/ip6_input.c:351) + __netif_receive_skb_one_core (net/core/dev.c:6212) + process_backlog (net/core/dev.c:6676) + __napi_poll (net/core/dev.c:7735) + net_rx_action (net/core/dev.c:7955) + handle_softirqs (kernel/softirq.c:622) + run_ksoftirqd (kernel/softirq.c:1076) + ... + +Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support") +Reported-by: Weiming Shi +Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Xiang Mei +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/nf_conntrack_sip.h | 2 +- + net/netfilter/nf_conntrack_sip.c | 2 +- + net/netfilter/nf_nat_sip.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h +index dbc614dfe0d56..aafa0c04f917e 100644 +--- a/include/linux/netfilter/nf_conntrack_sip.h ++++ b/include/linux/netfilter/nf_conntrack_sip.h +@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks { + unsigned int *datalen); + + void (*seq_adjust)(struct sk_buff *skb, +- unsigned int protoff, s16 off); ++ unsigned int protoff, s32 off); + + unsigned int (*expect)(struct sk_buff *skb, + unsigned int protoff, +diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c +index 4bebf66410ea3..f5f9bd47d2889 100644 +--- a/net/netfilter/nf_conntrack_sip.c ++++ b/net/netfilter/nf_conntrack_sip.c +@@ -1629,7 +1629,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, + unsigned int matchoff, matchlen; + unsigned int msglen, origlen; + const char *dptr, *end; +- s16 diff, tdiff = 0; ++ s32 diff, tdiff = 0; + int ret = NF_ACCEPT; + unsigned long clen; + bool term; +diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c +index f7be30c69b5c8..a1c41defaf22d 100644 +--- a/net/netfilter/nf_nat_sip.c ++++ b/net/netfilter/nf_nat_sip.c +@@ -315,7 +315,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, + } + + static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, +- s16 off) ++ s32 off) + { + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); +-- +2.53.0 + diff --git a/queue-6.18/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch b/queue-6.18/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch new file mode 100644 index 0000000000..1266d2b5d5 --- /dev/null +++ b/queue-6.18/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch @@ -0,0 +1,205 @@ +From 0ae320e7ef723790d764d23f55f17809406889de Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 10:13:37 +0200 +Subject: netfilter: nf_tables: make nft_object rhltable per table + +From: Pablo Neira Ayuso + +[ Upstream commit f4f699790590bd0896c48a71e9232a65198f92f0 ] + +The nft_object rhltable is global, this allows for accessing objects +that are being dismangled from lookup path by other existing netns. +Given the nft_obj_destroy() releases the object inmediately, this might +lead to use-after-free of these objects that are being released. +Make the existing rhltable per table to address this issue to deal with +with the nft_rcv_nl_event() path too. + +Update nft_obj_lookup() to take the table as non-const, otherwise, +compiler complains when passing the objname_ht to rhltable_lookup(). + +Fixes: 4d44175aa5bb ("netfilter: nf_tables: handle nft_object lookups via rhltable") +Suggested-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/netfilter/nf_tables.h | 4 +++- + net/netfilter/nf_tables_api.c | 34 +++++++++++++++---------------- + 2 files changed, 19 insertions(+), 19 deletions(-) + +diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h +index fda5bc82dd504..e6b31aa9d6b7d 100644 +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -1288,6 +1288,7 @@ static inline void nft_use_inc_restore(u32 *use) + * @sets: sets in the table + * @objects: stateful objects in the table + * @flowtables: flow tables in the table ++ * @objname_ht: hashtable for objects lookup by name + * @hgenerator: handle generator state + * @handle: table handle + * @use: number of chain references to this table +@@ -1307,6 +1308,7 @@ struct nft_table { + struct list_head sets; + struct list_head objects; + struct list_head flowtables; ++ struct rhltable objname_ht; + u64 hgenerator; + u64 handle; + u32 use; +@@ -1394,7 +1396,7 @@ static inline void *nft_obj_data(const struct nft_object *obj) + #define nft_expr_obj(expr) *((struct nft_object **)nft_expr_priv(expr)) + + struct nft_object *nft_obj_lookup(const struct net *net, +- const struct nft_table *table, ++ struct nft_table *table, + const struct nlattr *nla, u32 objtype, + u8 genmask); + +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index 51d3d4d44918c..ca6d2041eee66 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -44,8 +44,6 @@ enum { + NFT_VALIDATE_DO, + }; + +-static struct rhltable nft_objname_ht; +- + static u32 nft_chain_hash(const void *data, u32 len, u32 seed); + static u32 nft_chain_hash_obj(const void *data, u32 len, u32 seed); + static int nft_chain_hash_cmp(struct rhashtable_compare_arg *, const void *); +@@ -1600,6 +1598,10 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info, + if (err) + goto err_chain_ht; + ++ err = rhltable_init(&table->objname_ht, &nft_objname_ht_params); ++ if (err < 0) ++ goto err_obj_ht; ++ + INIT_LIST_HEAD(&table->chains); + INIT_LIST_HEAD(&table->sets); + INIT_LIST_HEAD(&table->objects); +@@ -1618,6 +1620,8 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info, + list_add_tail_rcu(&table->list, &nft_net->tables); + return 0; + err_trans: ++ rhltable_destroy(&table->objname_ht); ++err_obj_ht: + rhltable_destroy(&table->chains_ht); + err_chain_ht: + kfree(table->udata); +@@ -1784,6 +1788,7 @@ static void nf_tables_table_destroy(struct nft_table *table) + return; + + rhltable_destroy(&table->chains_ht); ++ rhltable_destroy(&table->objname_ht); + kfree(table->name); + kfree(table->udata); + kfree(table); +@@ -7957,7 +7962,7 @@ void nft_unregister_obj(struct nft_object_type *obj_type) + EXPORT_SYMBOL_GPL(nft_unregister_obj); + + struct nft_object *nft_obj_lookup(const struct net *net, +- const struct nft_table *table, ++ struct nft_table *table, + const struct nlattr *nla, u32 objtype, + u8 genmask) + { +@@ -7973,7 +7978,7 @@ struct nft_object *nft_obj_lookup(const struct net *net, + !lockdep_commit_lock_is_held(net)); + + rcu_read_lock(); +- list = rhltable_lookup(&nft_objname_ht, &k, nft_objname_ht_params); ++ list = rhltable_lookup(&table->objname_ht, &k, nft_objname_ht_params); + if (!list) + goto out; + +@@ -8252,7 +8257,7 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info, + if (err < 0) + goto err_trans; + +- err = rhltable_insert(&nft_objname_ht, &obj->rhlhead, ++ err = rhltable_insert(&table->objname_ht, &obj->rhlhead, + nft_objname_ht_params); + if (err < 0) + goto err_obj_ht; +@@ -8437,8 +8442,8 @@ nf_tables_getobj_single(u32 portid, const struct nfnl_info *info, + struct netlink_ext_ack *extack = info->extack; + u8 genmask = nft_genmask_cur(info->net); + u8 family = info->nfmsg->nfgen_family; +- const struct nft_table *table; + struct net *net = info->net; ++ struct nft_table *table; + struct nft_object *obj; + struct sk_buff *skb2; + u32 objtype; +@@ -10270,9 +10275,9 @@ static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain) + nf_tables_commit_chain_free_rules_old(g0); + } + +-static void nft_obj_del(struct nft_object *obj) ++static void nft_obj_del(struct nft_table *table, struct nft_object *obj) + { +- rhltable_remove(&nft_objname_ht, &obj->rhlhead, nft_objname_ht_params); ++ rhltable_remove(&table->objname_ht, &obj->rhlhead, nft_objname_ht_params); + list_del_rcu(&obj->list); + } + +@@ -10960,7 +10965,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) + break; + case NFT_MSG_DELOBJ: + case NFT_MSG_DESTROYOBJ: +- nft_obj_del(nft_trans_obj(trans)); ++ nft_obj_del(table, nft_trans_obj(trans)); + nf_tables_obj_notify(&ctx, nft_trans_obj(trans), + trans->msg_type); + break; +@@ -11251,7 +11256,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) + nft_trans_destroy(trans); + } else { + nft_use_dec_restore(&table->use); +- nft_obj_del(nft_trans_obj(trans)); ++ nft_obj_del(table, nft_trans_obj(trans)); + } + break; + case NFT_MSG_DELOBJ: +@@ -11877,7 +11882,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table) + nft_set_destroy(&ctx, set); + } + list_for_each_entry_safe(obj, ne, &table->objects, list) { +- nft_obj_del(obj); ++ nft_obj_del(table, obj); + nft_use_dec(&table->use); + nft_obj_destroy(&ctx, obj); + } +@@ -12059,10 +12064,6 @@ static int __init nf_tables_module_init(void) + if (err < 0) + goto err_netdev_notifier; + +- err = rhltable_init(&nft_objname_ht, &nft_objname_ht_params); +- if (err < 0) +- goto err_rht_objname; +- + err = nft_offload_init(); + if (err < 0) + goto err_offload; +@@ -12085,8 +12086,6 @@ static int __init nf_tables_module_init(void) + err_netlink_notifier: + nft_offload_exit(); + err_offload: +- rhltable_destroy(&nft_objname_ht); +-err_rht_objname: + unregister_netdevice_notifier(&nf_tables_flowtable_notifier); + err_netdev_notifier: + nf_tables_core_module_exit(); +@@ -12108,7 +12107,6 @@ static void __exit nf_tables_module_exit(void) + unregister_pernet_subsys(&nf_tables_net_ops); + cancel_work_sync(&trans_gc_work); + rcu_barrier(); +- rhltable_destroy(&nft_objname_ht); + nf_tables_core_module_exit(); + } + +-- +2.53.0 + diff --git a/queue-6.18/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-6.18/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch new file mode 100644 index 0000000000..d7b3aab8f2 --- /dev/null +++ b/queue-6.18/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch @@ -0,0 +1,69 @@ +From 71464e314457c57d016aa259a4045340b8d5b58d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:15:23 +0000 +Subject: netfilter: nft_payload: fix mask build for partial field offload + +From: Xiang Mei (Microsoft) + +[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ] + +nft_payload_offload_mask() builds the offload match mask for a payload +expression that covers only part of a header field. For a partial IPv6 +address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which +is undefined on the 32-bit int operand. It also trims only one word, so +the remaining words stay 0xffffffff (and when priv_len is a multiple of 4 +the trim is skipped entirely), leaving the mask covering more bytes than +the rule matches. + + UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20 + shift exponent 120 is too large for 32-bit type 'int' + ... + +The match is byte-granular and struct nft_data is zero-initialised, so the +correct mask is simply the first priv_len bytes set to 0xff. Set those +bytes directly and drop the word/shift trimming; this removes the undefined +shift and no longer over-masks the trailing bytes. + +Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes") +Reported-by: AutonomousCodeSecurity@microsoft.com +Signed-off-by: Xiang Mei (Microsoft) +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/nft_payload.c | 12 +----------- + 1 file changed, 1 insertion(+), 11 deletions(-) + +diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c +index 86a21c2b954f8..e07888aaf1475 100644 +--- a/net/netfilter/nft_payload.c ++++ b/net/netfilter/nft_payload.c +@@ -259,9 +259,7 @@ static int nft_payload_dump(struct sk_buff *skb, + static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + u32 priv_len, u32 field_len) + { +- unsigned int remainder, delta, k; + struct nft_data mask = {}; +- __be32 remainder_mask; + + if (priv_len == field_len) { + memset(®->mask, 0xff, priv_len); +@@ -270,15 +268,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + return false; + } + +- memset(&mask, 0xff, field_len); +- remainder = priv_len % sizeof(u32); +- if (remainder) { +- k = priv_len / sizeof(u32); +- delta = field_len - priv_len; +- remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1)); +- mask.data[k] = (__force u32)remainder_mask; +- } +- ++ memset(&mask, 0xff, priv_len); + memcpy(®->mask, &mask, field_len); + + return true; +-- +2.53.0 + diff --git a/queue-6.18/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-6.18/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch new file mode 100644 index 0000000000..b79ff0e8c2 --- /dev/null +++ b/queue-6.18/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch @@ -0,0 +1,83 @@ +From 09cb2046366a3e1eea86254be066e7a0a59a9ac6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 22:02:46 +0200 +Subject: netfilter: xt_hashlimit: validate hashtable supports + XT_HASHLIMIT_RATE_MATCH + +From: Pablo Neira Ayuso + +[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ] + +The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the +dsthash_ent structure which represents an entry in the hashtable. There +is a union area which uses a different layout to express the rate match +mode. + +Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode +flag is requested by two or more different rules that refer to the same +hashtable. Otherwise, uninitialized access to the burst field in the +union is possible. + +Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by +revision less than 3 too. + +Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode") +Reported-and-tested-by: Talha Berk Arslan +Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/ +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/xt_hashlimit.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c +index 3b507694e81e5..b3f15bbf259e2 100644 +--- a/net/netfilter/xt_hashlimit.c ++++ b/net/netfilter/xt_hashlimit.c +@@ -117,6 +117,7 @@ struct xt_hashlimit_htable { + refcount_t use; + u_int8_t family; + bool rnd_initialized; ++ bool ratematch; + + struct hashlimit_cfg3 cfg; /* config */ + +@@ -323,6 +324,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg, + kvfree(hinfo); + return -ENOMEM; + } ++ hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH); + spin_lock_init(&hinfo->lock); + + switch (revision) { +@@ -870,7 +872,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + } + + /* Check for overflow. */ +- if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (revision < 3) ++ return -EINVAL; ++ + if (cfg->avg == 0 || cfg->avg > U32_MAX) { + pr_info_ratelimited("invalid rate\n"); + return -ERANGE; +@@ -903,6 +908,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + mutex_unlock(&hashlimit_mutex); + return ret; + } ++ } else { ++ if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH && ++ !(*hinfo)->ratematch) || ++ (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) && ++ (*hinfo)->ratematch)) { ++ mutex_unlock(&hashlimit_mutex); ++ htable_put(*hinfo); ++ return -EINVAL; ++ } + } + mutex_unlock(&hashlimit_mutex); + +-- +2.53.0 + diff --git a/queue-6.18/netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch b/queue-6.18/netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch new file mode 100644 index 0000000000..ab64831baf --- /dev/null +++ b/queue-6.18/netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch @@ -0,0 +1,44 @@ +From 078a2717a0c231166e88977aafffefbae3d8ec86 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 14:07:12 +0100 +Subject: netfs: clear PG_private_2 on copy-to-cache append failure + +From: Yichong Chen + +[ Upstream commit a81fc9266e1c5fef9ccf675a9b44b2f4ab464923 ] + +netfs_pgpriv2_copy_to_cache() marks the folio with PG_private_2 before +netfs_pgpriv2_copy_folio() appends it to the copy-to-cache rolling +buffer. + +If the append fails, the folio is not queued for cache writeback, so +the PG_private_2 state and its reference must be released immediately. + +Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item") +Signed-off-by: Yichong Chen +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260727130716.1099906-2-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner (Amutable) +Signed-off-by: Sasha Levin +--- + fs/netfs/read_pgpriv2.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c +index a1489aa29f782..7eacc58abadb7 100644 +--- a/fs/netfs/read_pgpriv2.c ++++ b/fs/netfs/read_pgpriv2.c +@@ -54,6 +54,7 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio + + /* Attach the folio to the rolling buffer. */ + if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) { ++ folio_end_private_2(folio); + clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags); + return; + } +-- +2.53.0 + diff --git a/queue-6.18/netfs-handle-single-writeback-rolling-buffer-allocat.patch b/queue-6.18/netfs-handle-single-writeback-rolling-buffer-allocat.patch new file mode 100644 index 0000000000..28cb9ad2a9 --- /dev/null +++ b/queue-6.18/netfs-handle-single-writeback-rolling-buffer-allocat.patch @@ -0,0 +1,57 @@ +From 3f432e9f8e6a2aca4e81eaad02b29c402e7b64e4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 14:07:13 +0100 +Subject: netfs: handle single writeback rolling buffer allocation failure + +From: Yichong Chen + +[ Upstream commit 37a1c535c80c67d98668d190c7432f9ebda43310 ] + +netfs_write_folio_single() takes an extra folio reference before +appending the folio to the rolling buffer. + +rolling_buffer_append() can fail if it cannot allocate another +folio_queue. Check the return value and drop the extra folio reference +before returning the error. + +Fixes: 49866ce7ea8d ("netfs: Add support for caching single monolithic objects such as AFS dirs") +Signed-off-by: Yichong Chen +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260727130716.1099906-3-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner (Amutable) +Signed-off-by: Sasha Levin +--- + fs/netfs/write_issue.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c +index 76614339554ea..5f216907b1e50 100644 +--- a/fs/netfs/write_issue.c ++++ b/fs/netfs/write_issue.c +@@ -730,6 +730,7 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, + size_t iter_off = 0; + size_t fsize = folio_size(folio), flen; + loff_t fpos = folio_pos(folio); ++ ssize_t ret; + bool to_eof = false; + bool no_debug = false; + +@@ -758,7 +759,11 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, + + /* Attach the folio to the rolling buffer. */ + folio_get(folio); +- rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK); ++ ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK); ++ if (ret < 0) { ++ folio_put(folio); ++ return ret; ++ } + + /* Move the submission point forward to allow for write-streaming data + * not starting at the front of the page. We don't do write-streaming +-- +2.53.0 + diff --git a/queue-6.18/netfs-release-readahead-folios-on-iterator-preparati.patch b/queue-6.18/netfs-release-readahead-folios-on-iterator-preparati.patch new file mode 100644 index 0000000000..436c56389a --- /dev/null +++ b/queue-6.18/netfs-release-readahead-folios-on-iterator-preparati.patch @@ -0,0 +1,49 @@ +From 890f89db290ed7245e3a9cb54b4b29b6c587073c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 14:07:14 +0100 +Subject: netfs: release readahead folios on iterator preparation failure + +From: Yichong Chen + +[ Upstream commit 87eb3d272dcbcbbfe5c1576c10e5dc72810cf1f6 ] + +netfs_prepare_read_iterator() batches readahead folios in put_batch so that +the folio references can be dropped after the I/O iterator has been +prepared. + +If rolling_buffer_load_from_ra() fails after earlier folios have been +batched, the function returns immediately and leaves those references held. +Release the batch before returning the error. + +Fixes: 06fa229ceb36 ("netfs: Abstract out a rolling folio buffer implementation") +Signed-off-by: Yichong Chen +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260727130716.1099906-4-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner (Amutable) +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index fab3181c7f869..221287f8925a9 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -102,8 +102,10 @@ static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq, + + added = rolling_buffer_load_from_ra(&rreq->buffer, ractl, + &put_batch); +- if (added < 0) ++ if (added < 0) { ++ folio_batch_release(&put_batch); + return added; ++ } + rreq->submitted += added; + } + folio_batch_release(&put_batch); +-- +2.53.0 + diff --git a/queue-6.18/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-6.18/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch new file mode 100644 index 0000000000..d31d704e2f --- /dev/null +++ b/queue-6.18/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch @@ -0,0 +1,42 @@ +From 691cda9c02a80909a051d37dc1bc7579c9d90169 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 12:58:31 +0530 +Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop + +From: Suman Ghosh + +[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ] + +During link down event, we were doing netif_tx_stop_all_queues() first +and then netif_carrier_off(). This can cause a potential race since +carrier is still on during down event. This patch reverse the calling +order to fix the issue. + +Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications") +Signed-off-by: Suman Ghosh +Signed-off-by: Ratheesh Kannoth +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +index ae7bded777688..7e42d3291a9fd 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +@@ -889,8 +889,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf) + netif_carrier_on(netdev); + netif_tx_start_all_queues(netdev); + } else { +- netif_tx_stop_all_queues(netdev); + netif_carrier_off(netdev); ++ netif_tx_stop_all_queues(netdev); + } + } + +-- +2.53.0 + diff --git a/queue-6.18/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch b/queue-6.18/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch new file mode 100644 index 0000000000..7d59188b6a --- /dev/null +++ b/queue-6.18/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch @@ -0,0 +1,66 @@ +From 8c30ff3fa549b112dbc795fd1cf5cad1db888ca5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 14 Jun 2026 22:38:06 +0900 +Subject: of: reserved_mem: prevent OOB when too many dynamic regions are + defined + +From: Sang-Heon Jeon + +[ Upstream commit db3dbdfea1b8f38774419c5c2c14e4b81c48708d ] + +On boot, fdt_scan_reserved_mem() saves each dynamically-placed +/reserved-memory subnode into a local array of size +MAX_RESERVED_REGIONS. + +If the device tree defines more than MAX_RESERVED_REGIONS +dynamically-placed regions, fdt_scan_reserved_mem() writes past the +end of the local array. + +Add a bounds check that logs an error and skips the excess regions, +restoring the original behavior. + +Fixes: 8a6e02d0c00e ("of: reserved_mem: Restructure how the reserved memory regions are processed") +Signed-off-by: Sang-Heon Jeon +Link: https://patch.msgid.link/20260614133807.2165124-2-ekffu200098@gmail.com +Signed-off-by: Rob Herring (Arm) +Signed-off-by: Sasha Levin +--- + drivers/of/of_reserved_mem.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c +index 39d8305a16800..56c2a1e4e86c1 100644 +--- a/drivers/of/of_reserved_mem.c ++++ b/drivers/of/of_reserved_mem.c +@@ -326,6 +326,7 @@ int __init fdt_scan_reserved_mem(void) + err = __reserved_mem_reserve_reg(child, uname); + if (!err) + count++; ++ + /* + * Save the nodes for the dynamically-placed regions + * into an array which will be used for allocation right +@@ -333,10 +334,17 @@ int __init fdt_scan_reserved_mem(void) + * or marked as no-map. This is done to avoid dynamically + * allocating from one of the statically-placed regions. + */ +- if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) { +- dynamic_nodes[dynamic_nodes_cnt] = child; +- dynamic_nodes_cnt++; ++ if (err != -ENOENT || !of_get_flat_dt_prop(child, "size", NULL)) ++ continue; ++ ++ if (dynamic_nodes_cnt == MAX_RESERVED_REGIONS) { ++ pr_err("too many defined dynamic regions, skip '%s'\n", ++ uname); ++ continue; + } ++ ++ dynamic_nodes[dynamic_nodes_cnt] = child; ++ dynamic_nodes_cnt++; + } + for (int i = 0; i < dynamic_nodes_cnt; i++) { + const char *uname; +-- +2.53.0 + diff --git a/queue-6.18/phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch b/queue-6.18/phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch new file mode 100644 index 0000000000..01c66669a3 --- /dev/null +++ b/queue-6.18/phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch @@ -0,0 +1,38 @@ +From 143fbde27d9979f9eba76c7b7b4dae3137431886 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 18 Jul 2026 15:01:31 +0530 +Subject: phy: qcom: m31-eusb2: Fix return value of init call + +From: Krishna Kurapati + +[ Upstream commit 361f533a2dce2c2841fe4dc0c9d85a67117edf95 ] + +The init call currently returns success irrespective of any failures +during repeater init or clock enablement. Return appropriate error value +in the init call failure path. + +Fixes: 9c8504861cc4 ("phy: qcom: Add M31 based eUSB2 PHY driver") +Signed-off-by: Krishna Kurapati +Link: https://patch.msgid.link/20260718-m31-eusb2-fix-v1-1-8588a1b94d76@oss.qualcomm.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/qualcomm/phy-qcom-m31-eusb2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c +index 68f1ba8fec4ad..9434bd22ef32d 100644 +--- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c ++++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c +@@ -221,7 +221,7 @@ static int m31eusb2_phy_init(struct phy *uphy) + disable_vreg: + regulator_bulk_disable(M31_EUSB_NUM_VREGS, phy->vregs); + +- return 0; ++ return ret; + } + + static int m31eusb2_phy_exit(struct phy *uphy) +-- +2.53.0 + diff --git a/queue-6.18/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch b/queue-6.18/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch new file mode 100644 index 0000000000..03761662a6 --- /dev/null +++ b/queue-6.18/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch @@ -0,0 +1,76 @@ +From 242e8f806b680aaea46cceb10a41de2eb3a47329 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 21:08:30 +0530 +Subject: phy: zynqmp: fix clock error handling in xpsgtr_phy_init() + +From: Radhey Shyam Pandey + +[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ] + +Propagate clk_prepare_enable() failures to the caller instead of +returning success, and disable the reference clock on initialization +error paths to avoid leaking clock references when phy_exit() is not +called. + +Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save") +Signed-off-by: Radhey Shyam Pandey +Reviewed-by: Michal Simek +Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index fe6b4925d1662..c8230f2bda629 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -658,12 +658,13 @@ static int xpsgtr_phy_init(struct phy *phy) + { + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; +- int ret = 0; ++ int ret; + + mutex_lock(>r_dev->gtr_mutex); + + /* Configure and enable the clock when peripheral phy_init call */ +- if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk])) ++ ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]); ++ if (ret) + goto out; + + /* Skip initialization if not required. */ +@@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy) + if (gtr_dev->tx_term_fix) { + ret = xpsgtr_phy_tx_term_fix(gtr_phy); + if (ret < 0) +- goto out; ++ goto out_disable_clk; + + gtr_dev->tx_term_fix = false; + } +@@ -687,7 +688,7 @@ static int xpsgtr_phy_init(struct phy *phy) + */ + ret = xpsgtr_configure_pll(gtr_phy); + if (ret) +- goto out; ++ goto out_disable_clk; + + xpsgtr_lane_set_protocol(gtr_phy); + +@@ -705,6 +706,10 @@ static int xpsgtr_phy_init(struct phy *phy) + break; + } + ++ goto out; ++ ++out_disable_clk: ++ clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]); + out: + mutex_unlock(>r_dev->gtr_mutex); + return ret; +-- +2.53.0 + diff --git a/queue-6.18/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch b/queue-6.18/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch new file mode 100644 index 0000000000..4adfa601c1 --- /dev/null +++ b/queue-6.18/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch @@ -0,0 +1,56 @@ +From a2241cf6da5da1651013c97323ddf20fc0f428e9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 21:08:31 +0530 +Subject: phy: zynqmp: fix runtime PM leak on probe allocation failure + +From: Radhey Shyam Pandey + +[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ] + +Allocate saved_regs before pm_runtime_resume_and_get() so a +devm_kmalloc() failure does not leave an unreleased runtime PM usage +counter. + +Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume") +Signed-off-by: Radhey Shyam Pandey +Reviewed-by: Michal Simek +Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index c8230f2bda629..2138f5399821a 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -1044,6 +1044,12 @@ static int xpsgtr_probe(struct platform_device *pdev) + return PTR_ERR(provider); + } + ++ gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, ++ sizeof(save_reg_address), ++ GFP_KERNEL); ++ if (!gtr_dev->saved_regs) ++ return -ENOMEM; ++ + pm_runtime_set_active(gtr_dev->dev); + pm_runtime_enable(gtr_dev->dev); + +@@ -1053,12 +1059,6 @@ static int xpsgtr_probe(struct platform_device *pdev) + return ret; + } + +- gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, +- sizeof(save_reg_address), +- GFP_KERNEL); +- if (!gtr_dev->saved_regs) +- return -ENOMEM; +- + return 0; + } + +-- +2.53.0 + diff --git a/queue-6.18/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch b/queue-6.18/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch new file mode 100644 index 0000000000..35ea7b6ce3 --- /dev/null +++ b/queue-6.18/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch @@ -0,0 +1,58 @@ +From a5ada19dc297b2ccd33f967454364e0b60b73ff1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 11:28:44 -0500 +Subject: pinctrl-amd: Don't clear S4 wake bits at probe + +From: Mario Limonciello + +[ Upstream commit ffe8a0c6b55285ceaf2f42fc20c3a0594d14f1e9 ] + +commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") +introduced a regression where Wake-on-LAN no longer works after suspend +or shutdown on some AMD platforms. + +Firmware-programmed S4 wake bits for devices like PCIe NICs using PCI +PME are cleared at probe, but nothing restores them. Unlike S0i3/S3 wake +sources that use enable_irq_wake() -> amd_gpio_irq_set_wake(), PCIe PME +does not use GPIO IRQ infrastructure and relies on firmware configuration. + +The original intent of commit 6bc3462a0f5e ("pinctrl: amd: Mask wake +bits on probe again") was to clear spurious wake bits left by firmware +to prevent unwanted wakeups. However, S4 wake bits are used for +hardware-level wake sources like WoL that bypass the kernel's IRQ wake +API. + +Fix by preserving S4 wake bits at probe and only clearing S0i3/S3 bits: +- Firmware-configured S4 wake sources (WoL) continue working +- Kernel maintains control of S3/S0i3 wake policy via set_wake() +- S3-only wake sources work correctly per commit f31f33dbb3ba ("pinctrl: + amd: Take suspend type into consideration which pins are non-wake") + +The trade-off is that firmware-programmed spurious S4 wake bits remain +set, but this is less problematic than breaking WoL. + +Fixes: 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") +Signed-off-by: Mario Limonciello +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/pinctrl-amd.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c +index 2dac5c71eb008..d962f2e1d2acd 100644 +--- a/drivers/pinctrl/pinctrl-amd.c ++++ b/drivers/pinctrl/pinctrl-amd.c +@@ -886,8 +886,7 @@ static void amd_gpio_irq_init(struct amd_gpio *gpio_dev) + u32 pin_reg, mask; + int i; + +- mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) | +- BIT(WAKE_CNTRL_OFF_S4); ++ mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3); + + for (i = 0; i < desc->npins; i++) { + int pin = desc->pins[i].number; +-- +2.53.0 + diff --git a/queue-6.18/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch b/queue-6.18/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch new file mode 100644 index 0000000000..a2ba3ad7ea --- /dev/null +++ b/queue-6.18/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch @@ -0,0 +1,58 @@ +From 694588d90c9094e7fdd8e9733e8e5d86bda0d1b4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 15:08:05 +0200 +Subject: pinctrl: qcom: sc8280xp: Add missing wakeup entries for GPIO143/151 + +From: Konrad Dybcio + +[ Upstream commit 437a8d2aa1aa442c4a176fdf4700a9b3bb0c8794 ] + +Pins 143 and 151 were not included in the PDC wakeup map. They are +normally used for PCIe2A and PCIe3a PERST# respectively, so they're +unlikely to be excercised in practice, but still add them for the sake +of completeness. + +Fixes: c0e4c71a9e7c ("pinctrl: qcom: Introduce sc8280xp TLMM driver") +Signed-off-by: Konrad Dybcio +Link: https://patch.msgid.link/20260626-topic-8280_pinctrl_wakeup-v1-1-2ccb267148f5@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-sc8280xp.c | 21 +++++++++++---------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +index cf8297e8b8f8c..d6e31b00e1c5e 100644 +--- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c ++++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +@@ -1884,16 +1884,17 @@ static const struct msm_gpio_wakeirq_map sc8280xp_pdc_map[] = { + { 126, 200 }, { 127, 225 }, { 128, 262 }, { 129, 201 }, + { 130, 209 }, { 131, 173 }, { 132, 202 }, { 136, 210 }, + { 138, 171 }, { 139, 226 }, { 140, 227 }, { 142, 228 }, +- { 144, 229 }, { 145, 230 }, { 146, 231 }, { 148, 232 }, +- { 149, 233 }, { 150, 234 }, { 152, 235 }, { 154, 212 }, +- { 157, 213 }, { 161, 219 }, { 170, 236 }, { 171, 221 }, +- { 174, 222 }, { 175, 237 }, { 176, 223 }, { 177, 170 }, +- { 180, 238 }, { 181, 239 }, { 182, 240 }, { 183, 241 }, +- { 184, 242 }, { 185, 243 }, { 190, 178 }, { 193, 184 }, +- { 196, 185 }, { 198, 186 }, { 200, 174 }, { 201, 175 }, +- { 205, 176 }, { 206, 177 }, { 208, 187 }, { 210, 198 }, +- { 211, 199 }, { 212, 204 }, { 215, 205 }, { 220, 188 }, +- { 221, 194 }, { 223, 195 }, { 225, 196 }, { 227, 197 }, ++ { 143, 261 }, { 144, 229 }, { 145, 230 }, { 146, 231 }, ++ { 148, 232 }, { 149, 233 }, { 150, 234 }, { 151, 264 }, ++ { 152, 235 }, { 154, 212 }, { 157, 213 }, { 161, 219 }, ++ { 170, 236 }, { 171, 221 }, { 174, 222 }, { 175, 237 }, ++ { 176, 223 }, { 177, 170 }, { 180, 238 }, { 181, 239 }, ++ { 182, 240 }, { 183, 241 }, { 184, 242 }, { 185, 243 }, ++ { 190, 178 }, { 193, 184 }, { 196, 185 }, { 198, 186 }, ++ { 200, 174 }, { 201, 175 }, { 205, 176 }, { 206, 177 }, ++ { 208, 187 }, { 210, 198 }, { 211, 199 }, { 212, 204 }, ++ { 215, 205 }, { 220, 188 }, { 221, 194 }, { 223, 195 }, ++ { 225, 196 }, { 227, 197 }, + }; + + static struct msm_pinctrl_soc_data sc8280xp_pinctrl = { +-- +2.53.0 + diff --git a/queue-6.18/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch b/queue-6.18/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch new file mode 100644 index 0000000000..8891655f47 --- /dev/null +++ b/queue-6.18/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch @@ -0,0 +1,70 @@ +From 2481796581b3b549aafaa53065afc55653ea1dd4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 16 Jun 2026 17:24:53 +0530 +Subject: pinctrl: qcom: Unconditionally mark gpio as wakeup enable + +From: Sneh Mankad + +[ Upstream commit 859e02a369ab328a77dfcabf59562100e55f9c5c ] + +GPIO interrupts that are wakeup capable need to be forwarded to wakeup +capable parent irqchip. This is done via writing to it's wakeup_enable bit. + +Currently the bit is set only for PDC irqchip by checking skip_wake_irqs. +skip_wake_irqs is set to differentiate between parent irqchips MPM and +PDC. It is set when the parent irqchip is PDC to inform pinctrl about +skipping the IRQ setting up at TLMM. + +However, the functionality to forward GPIO interrupts during SoC low +power mode is needed regardless of which parent irqchip it is. +Without the functionality it is impossible for MPM irqchip to detect the +GPIO interrupt during SoC low power mode since for MPM irqchip the +skip_wake_irqs is always false. + +Remove skip_wake_irqs condition when setting wakeup enable bit to allow +forwarding GPIO interrupts for SoCs using MPM irqchip too. + +Fixes: 76b446f5b86e ("pinctrl: qcom: handle intr_target_reg wakeup_present/enable bits") +Signed-off-by: Sneh Mankad +Reviewed-by: Maulik Shah +Reviewed-by: Linus Walleij +Reviewed-by: Konrad Dybcio +Link: https://patch.msgid.link/20260616-enable_wakeup_capable_gpios-v3-1-fb59647d89cb@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-msm.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c +index e99871b90ab9c..424b14bc29e96 100644 +--- a/drivers/pinctrl/qcom/pinctrl-msm.c ++++ b/drivers/pinctrl/qcom/pinctrl-msm.c +@@ -1226,12 +1226,12 @@ static int msm_gpio_irq_reqres(struct irq_data *d) + /* + * If the wakeup_enable bit is present and marked as available for the + * requested GPIO, it should be enabled when the GPIO is marked as +- * wake irq in order to allow the interrupt event to be transfered to +- * the PDC HW. ++ * wake irq in order to allow the interrupt event to be transferred to ++ * the PDC/MPM HW. + * While the name implies only the wakeup event, it's also required for + * the interrupt event. + */ +- if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) { ++ if (g->intr_wakeup_present_bit) { + u32 intr_cfg; + + raw_spin_lock_irqsave(&pctrl->lock, flags); +@@ -1259,7 +1259,7 @@ static void msm_gpio_irq_relres(struct irq_data *d) + unsigned long flags; + + /* Disable the wakeup_enable bit if it has been set in msm_gpio_irq_reqres() */ +- if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) { ++ if (g->intr_wakeup_present_bit) { + u32 intr_cfg; + + raw_spin_lock_irqsave(&pctrl->lock, flags); +-- +2.53.0 + diff --git a/queue-6.18/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-6.18/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch new file mode 100644 index 0000000000..c7098837cb --- /dev/null +++ b/queue-6.18/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch @@ -0,0 +1,38 @@ +From 96245c6a61e7b219c9389df07b3d4abccac6a66b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:55 +0200 +Subject: powerpc/boot: Fix simpleboot CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/simpleboot.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c +index c80691d83880b..27591df41e9e8 100644 +--- a/arch/powerpc/boot/simpleboot.c ++++ b/arch/powerpc/boot/simpleboot.c +@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + /* finally, setup the timebase */ + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-6.18/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-6.18/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch new file mode 100644 index 0000000000..5d380f8589 --- /dev/null +++ b/queue-6.18/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch @@ -0,0 +1,38 @@ +From 95ebe6555c4b94cffb4394710dc6414e7b20952f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:57 +0200 +Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-akebono.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c +index e3cc2599869cc..1b529037480fb 100644 +--- a/arch/powerpc/boot/treeboot-akebono.c ++++ b/arch/powerpc/boot/treeboot-akebono.c +@@ -146,7 +146,7 @@ void platform_init(char *userdata) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-6.18/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-6.18/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch new file mode 100644 index 0000000000..085376f275 --- /dev/null +++ b/queue-6.18/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch @@ -0,0 +1,38 @@ +From 49cece124bd19e2b42bb8221dd506db7dfaebc33 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:56 +0200 +Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-currituck.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c +index d53e8a592f816..5b5363b74f9f3 100644 +--- a/arch/powerpc/boot/treeboot-currituck.c ++++ b/arch/powerpc/boot/treeboot-currituck.c +@@ -102,7 +102,7 @@ void platform_init(void) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-6.18/ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch b/queue-6.18/ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch new file mode 100644 index 0000000000..aea5e5e2fb --- /dev/null +++ b/queue-6.18/ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch @@ -0,0 +1,121 @@ +From 585cae9201bc3cd2d48e3da5d3c30ff0009e9823 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 14:03:48 +0800 +Subject: ptp: netc: fix potential interrupt storm caused by incorrect unbind + order + +From: Wei Fang + +[ Upstream commit 54ad7ea45d63146a8e3c57375f8a269d4cf7ecea ] + +In netc_timer_remove(), hardware interrupts are disabled by clearing +TMR_TEMASK before ptp_clock_unregister() is called. This may cause a +race condition during driver unbind that could leave hardware interrupts +active. For example, a concurrent PTP_CLK_REQ_EXTTS ioctl can re-enable +TMR_TEMASK after it has been cleared, leaving a pending hardware +interrupt when the driver unbinds. + +Since the NETC Timer does not support PCIe FLR, hardware state is not +reset during probe. When the driver is rebound and the IRQ is registered, +the pending interrupt fires immediately. At that point priv->tmr_emask +is still zero, so netc_timer_isr() does not clear the interrupt status +and unconditionally returns IRQ_HANDLED, resulting in an uninterruptible +infinite interrupt storm. + +Fix this in several ways. First, request the IRQ with IRQF_NO_AUTOEN so +it is not enabled when request_irq() runs, and clear TMR_TEMASK in +netc_timer_init() before enabling it. The IRQ is only enabled at the end +of probe once the timer has been reprogrammed and the PTP clock has been +registered. This ensures a stale pending interrupt from a previous unbind +or an unclean shutdown cannot be delivered before the driver is fully +initialized. + +Second, in netc_timer_remove() call disable_irq() before +ptp_clock_unregister() and move the TMR_TEMASK/TMR_CTRL clearing after +it. disable_irq() masks the line and waits for any in-flight +netc_timer_isr() to finish, so no ISR can dereference priv->clock after +ptp_clock_unregister() has freed it. Unregistering the PTP clock before +clearing the mask also guarantees that no in-flight or concurrent ioctl +can re-enable hardware interrupts. + +Finally, return IRQ_NONE from netc_timer_isr() when the masked event +status is zero, so the kernel's spurious interrupt detection can disable +a stuck line instead of looping forever. + +Fixes: 671e266835b8 ("ptp: netc: add periodic pulse output support") +Reported-by: Sashiko +Closes: https://sashiko.dev/#/patchset/20260720012508.23227-1-wei.fang%40oss.nxp.com +Signed-off-by: Wei Fang +Link: https://patch.msgid.link/20260727060348.1887464-1-wei.fang@oss.nxp.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/ptp/ptp_netc.c | 15 ++++++++++++--- + 1 file changed, 12 insertions(+), 3 deletions(-) + +diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c +index 5e381c354d746..1c20d7efab929 100644 +--- a/drivers/ptp/ptp_netc.c ++++ b/drivers/ptp/ptp_netc.c +@@ -769,6 +769,7 @@ static void netc_timer_init(struct netc_timer *priv) + TMR_CTRL_TE | TMR_CTRL_FS; + netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl); + netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc); ++ netc_timer_wr(priv, NETC_TMR_TEMASK, 0); + + /* Disable FIPER by default */ + fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL); +@@ -901,6 +902,11 @@ static irqreturn_t netc_timer_isr(int irq, void *data) + /* Clear interrupts status */ + netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event); + ++ if (!tmr_event) { ++ spin_unlock(&priv->lock); ++ return IRQ_NONE; ++ } ++ + if (tmr_event & TMR_TEVENT_ALMEN(0)) + netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0); + +@@ -936,7 +942,8 @@ static int netc_timer_init_msix_irq(struct netc_timer *priv) + } + + priv->irq = pci_irq_vector(pdev, 0); +- err = request_irq(priv->irq, netc_timer_isr, 0, priv->irq_name, priv); ++ err = request_irq(priv->irq, netc_timer_isr, IRQF_NO_AUTOEN, ++ priv->irq_name, priv); + if (err) { + dev_err(&pdev->dev, "request_irq() failed\n"); + pci_free_irq_vectors(pdev); +@@ -951,7 +958,6 @@ static void netc_timer_free_msix_irq(struct netc_timer *priv) + { + struct pci_dev *pdev = priv->pdev; + +- disable_irq(priv->irq); + free_irq(priv->irq, priv); + pci_free_irq_vectors(pdev); + } +@@ -1005,6 +1011,8 @@ static int netc_timer_probe(struct pci_dev *pdev, + goto free_msix_irq; + } + ++ enable_irq(priv->irq); ++ + return 0; + + free_msix_irq: +@@ -1019,9 +1027,10 @@ static void netc_timer_remove(struct pci_dev *pdev) + { + struct netc_timer *priv = pci_get_drvdata(pdev); + ++ disable_irq(priv->irq); ++ ptp_clock_unregister(priv->clock); + netc_timer_wr(priv, NETC_TMR_TEMASK, 0); + netc_timer_wr(priv, NETC_TMR_CTRL, 0); +- ptp_clock_unregister(priv->clock); + netc_timer_free_msix_irq(priv); + netc_timer_pci_remove(pdev); + } +-- +2.53.0 + diff --git a/queue-6.18/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-6.18/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch new file mode 100644 index 0000000000..a7b8953737 --- /dev/null +++ b/queue-6.18/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch @@ -0,0 +1,169 @@ +From dd31f5d9a636880f7194230895c8f1a09fa4f58f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 12:43:11 +0200 +Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path + +From: Denis V. Lunev + +[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ] + +A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports +configured wedges the rtnetlink control plane of the whole machine: + + NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms + [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2! + [qede_recovery_handler:2665(ens6f0)]Starting a recovery process + +The recovery path deadlocks on the driver's own mutex: + + qede_sp_task + rtnl_lock() + mutex_lock(&edev->qede_lock) <- taken + qede_recovery_handler + qede_load + udp_tunnel_nic_reset_ntf + __udp_tunnel_nic_device_sync + info->sync_table == qede_udp_tunnel_sync + mutex_lock(&edev->qede_lock) <- same task: deadlock + +The mutex is not recursive, so the kworker blocks on itself with +rtnl_lock held, and neither lock is ever released. Every task that +calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6 +addrconf, sshd) blocks forever while the node still answers ping. +In a vmcore from an affected production node rtnl_mutex.owner +decodes to the very kworker blocked at the innermost mutex_lock() +above. + +Re-sync the tunnel ports from qede_sp_task() after the internal lock +is dropped, still under rtnl_lock as the udp_tunnel API requires. +This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf() +under rtnl without the internal lock. + +qede_recovery_handler() now returns whether it has successfully +reloaded an open device, and the caller re-syncs the ports only in +that case. This keeps the old gating exactly: a device that was down +or a failed recovery returns false, as those paths never reached the +udp_tunnel_nic_reset_ntf() call before either. + +This was the only user of the qede_lock()/qede_unlock() helpers, so +remove them. + +Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra") +Signed-off-by: Denis V. Lunev +CC: Andrew Lunn +CC: "David S. Miller" +CC: Eric Dumazet +CC: Jakub Kicinski +CC: Paolo Abeni +Reviewed-by: Jacob Keller +Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++---------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c +index b5d744d2586f7..59a80f7193723 100644 +--- a/drivers/net/ethernet/qlogic/qede/qede_main.c ++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c +@@ -107,7 +107,7 @@ static void qede_remove(struct pci_dev *pdev); + static void qede_shutdown(struct pci_dev *pdev); + static void qede_link_update(void *dev, struct qed_link_output *link); + static void qede_schedule_recovery_handler(void *dev); +-static void qede_recovery_handler(struct qede_dev *edev); ++static bool qede_recovery_handler(struct qede_dev *edev); + static void qede_schedule_hw_err_handler(void *dev, + enum qed_hw_err_type err_type); + static void qede_get_eth_tlv_data(void *edev, void *data); +@@ -1065,21 +1065,6 @@ void __qede_unlock(struct qede_dev *edev) + mutex_unlock(&edev->qede_lock); + } + +-/* This version of the lock should be used when acquiring the RTNL lock is also +- * needed in addition to the internal qede lock. +- */ +-static void qede_lock(struct qede_dev *edev) +-{ +- rtnl_lock(); +- __qede_lock(edev); +-} +- +-static void qede_unlock(struct qede_dev *edev) +-{ +- __qede_unlock(edev); +- rtnl_unlock(); +-} +- + static void qede_periodic_task(struct work_struct *work) + { + struct qede_dev *edev = container_of(work, struct qede_dev, +@@ -1116,6 +1101,8 @@ static void qede_sp_task(struct work_struct *work) + */ + + if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) { ++ bool reloaded; ++ + cancel_delayed_work_sync(&edev->periodic_task); + #ifdef CONFIG_QED_SRIOV + /* SRIOV must be disabled outside the lock to avoid a deadlock. +@@ -1124,9 +1111,17 @@ static void qede_sp_task(struct work_struct *work) + if (pci_num_vf(edev->pdev)) + qede_sriov_configure(edev->pdev, 0); + #endif +- qede_lock(edev); +- qede_recovery_handler(edev); +- qede_unlock(edev); ++ rtnl_lock(); ++ __qede_lock(edev); ++ reloaded = qede_recovery_handler(edev); ++ __qede_unlock(edev); ++ ++ /* The udp_tunnel core synchronously calls back into ++ * qede_udp_tunnel_sync(), which takes the qede lock. ++ */ ++ if (reloaded) ++ udp_tunnel_nic_reset_ntf(edev->ndev); ++ rtnl_unlock(); + } + + __qede_lock(edev); +@@ -2667,9 +2662,13 @@ static void qede_recovery_failed(struct qede_dev *edev) + edev->ops->common->set_power_state(edev->cdev, PCI_D3hot); + } + +-static void qede_recovery_handler(struct qede_dev *edev) ++/* Returns true if an open device was successfully reloaded and its ++ * udp_tunnel ports need to be re-synced by the caller. ++ */ ++static bool qede_recovery_handler(struct qede_dev *edev) + { + u32 curr_state = edev->state; ++ bool reloaded = false; + int rc; + + DP_NOTICE(edev, "Starting a recovery process\n"); +@@ -2699,17 +2698,18 @@ static void qede_recovery_handler(struct qede_dev *edev) + goto err; + + qede_config_rx_mode(edev->ndev); +- udp_tunnel_nic_reset_ntf(edev->ndev); ++ reloaded = true; + } + + edev->state = curr_state; + + DP_NOTICE(edev, "Recovery handling is done\n"); + +- return; ++ return reloaded; + + err: + qede_recovery_failed(edev); ++ return false; + } + + static void qede_atomic_hw_err_handler(struct qede_dev *edev) +-- +2.53.0 + diff --git a/queue-6.18/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch b/queue-6.18/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch new file mode 100644 index 0000000000..929c1a7ef1 --- /dev/null +++ b/queue-6.18/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch @@ -0,0 +1,118 @@ +From 87ad45ccf6060d3c5c38dfa9459fe75c3f9bccd0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 9 Jul 2026 16:27:54 +0000 +Subject: rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled + +From: Ilia Gavrilov + +[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ] + +When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst +is never initialized because inet6_init() exits before addrconf_init() +is called to initialize it. An attempt to bind an RDS socket to +an ipv6 address results in a crash in __ipv6_chk_addr_and_flags() + +KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] +RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0 +Call Trace: + + ipv6_chk_addr+0x3b/0x50 + rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp] + rds_trans_get_preferred+0x15d/0x2d0 [rds] + ? trace_hardirqs_on+0x2d/0x110 + rds_bind+0x1433/0x1d60 [rds] + ? rds_remove_bound+0xd50/0xd50 [rds] + ? aa_af_perm+0x250/0x250 + ? __might_fault+0xde/0x190 + ? __sys_bind+0x1dc/0x210 + __sys_bind+0x1dc/0x210 + ? __ia32_sys_socketpair+0x100/0x100 + ? restore_fpregs_from_fpstate+0x53/0x100 + __x64_sys_bind+0x73/0xb0 + ? syscall_enter_from_user_mode+0x1c/0x50 + do_syscall_64+0x34/0x80 + entry_SYSCALL_64_after_hwframe+0x6e/0xd8 +RIP: 0033:0x7f47f8269ea9 + + +The following code reproduces the issue: + +struct sockaddr_in6 addr; +s = socket(PF_RDS, SOCK_SEQPACKET, 0); + +memset(&addr, 0, sizeof(addr)); +inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr); +addr.sin6_family = AF_INET6; +addr.sin6_port = htons(PORT); + +bind(s, &addr, sizeof(addr)); + +Found by InfoTeCS on behalf of Linux Verification Center +(linuxtesting.org) with Syzkaller. + +Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") +Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support") +Signed-off-by: Ilia Gavrilov +Reviewed-by: Allison Henderson +Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru +Signed-off-by: Jakub Kicinski +Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()") +Signed-off-by: Sasha Levin +--- + net/rds/ib.c | 4 ++++ + net/rds/ib_cm.c | 4 ++++ + net/rds/tcp.c | 8 +++++--- + 3 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/net/rds/ib.c b/net/rds/ib.c +index ce5be43c5fbac..1061bcf7d1315 100644 +--- a/net/rds/ib.c ++++ b/net/rds/ib.c +@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr, + sa = (struct sockaddr *)&sin; + } else { + #if IS_ENABLED(CONFIG_IPV6) ++ if (!ipv6_mod_enabled()) { ++ ret = -EADDRNOTAVAIL; ++ goto out; ++ } + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = *addr; +diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c +index 5289afbb61aa7..e50e01abb0799 100644 +--- a/net/rds/ib_cm.c ++++ b/net/rds/ib_cm.c +@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, + dp = event->param.conn.private_data; + if (isv6) { + #if IS_ENABLED(CONFIG_IPV6) ++ if (!ipv6_mod_enabled()) { ++ err = -EOPNOTSUPP; ++ goto out; ++ } + dp_cmn = &dp->ricp_v6.dp_cmn; + saddr6 = &dp->ricp_v6.dp_saddr; + daddr6 = &dp->ricp_v6.dp_daddr; +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index 06a2d8d48bbac..87e6ab0e93b71 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -342,9 +342,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, + rcu_read_unlock(); + } + #if IS_ENABLED(CONFIG_IPV6) +- ret = ipv6_chk_addr(net, addr, dev, 0); +- if (ret) +- return 0; ++ if (ipv6_mod_enabled()) { ++ ret = ipv6_chk_addr(net, addr, dev, 0); ++ if (ret) ++ return 0; ++ } + #endif + return -EADDRNOTAVAIL; + } +-- +2.53.0 + diff --git a/queue-6.18/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-6.18/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch new file mode 100644 index 0000000000..5e34294779 --- /dev/null +++ b/queue-6.18/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch @@ -0,0 +1,85 @@ +From 42edbed84b0e91de66949cbf61e0c7063e4c4aa1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 14:02:03 -0700 +Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in + rds_tcp_laddr_check() + +From: Xiang Mei + +[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ] + +rds_tcp_laddr_check() looks up a scoped IPv6 interface with +dev_get_by_index_rcu(), drops the RCU read-side lock, and only then +passes the bare struct net_device * into ipv6_chk_addr(). + +dev_get_by_index_rcu() only keeps the device alive within the same RCU +read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can +free the net_device; ipv6_chk_addr() then dereferences the stale pointer +in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading +freed memory. + +Keep the RCU read-side lock held across the ipv6_chk_addr() call instead +of dropping it right after the lookup, so the device cannot be freed +while it is in use. + + BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + Read of size 8 at addr ffff8880106ec000 by task exploit/153 + Call Trace: + ... + kasan_report (mm/kasan/report.c:595) + __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972) + rds_tcp_laddr_check (net/rds/tcp.c:370) + rds_bind (net/rds/bind.c:248) + __sys_bind (net/socket.c:1920) + __x64_sys_bind (net/socket.c:1956) + do_syscall_64 (arch/x86/entry/syscall_64.c:63) + entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) + +Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") +Reported-by: Weiming Shi +Signed-off-by: Xiang Mei +Reviewed-by: Allison Henderson +Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/rds/tcp.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index 87e6ab0e93b71..1980a197034ba 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -331,23 +331,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, + /* If the scope_id is specified, check only those addresses + * hosted on the specified interface. + */ ++ rcu_read_lock(); + if (scope_id != 0) { +- rcu_read_lock(); + dev = dev_get_by_index_rcu(net, scope_id); + /* scope_id is not valid... */ + if (!dev) { + rcu_read_unlock(); + return -EADDRNOTAVAIL; + } +- rcu_read_unlock(); + } + #if IS_ENABLED(CONFIG_IPV6) + if (ipv6_mod_enabled()) { + ret = ipv6_chk_addr(net, addr, dev, 0); +- if (ret) ++ if (ret) { ++ rcu_read_unlock(); + return 0; ++ } + } + #endif ++ rcu_read_unlock(); + return -EADDRNOTAVAIL; + } + +-- +2.53.0 + diff --git a/queue-6.18/riscv-drop-__init-from-vec_check_unaligned_access_sp.patch b/queue-6.18/riscv-drop-__init-from-vec_check_unaligned_access_sp.patch new file mode 100644 index 0000000000..d1ee924c7c --- /dev/null +++ b/queue-6.18/riscv-drop-__init-from-vec_check_unaligned_access_sp.patch @@ -0,0 +1,80 @@ +From cb8b9accfa9d14551459440c7a46d1f2bccc03f3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 12 Jun 2026 11:24:43 -0500 +Subject: riscv: drop __init from vec_check_unaligned_access_speed_all_cpus + +From: Anirudh Srinivasan + +[ Upstream commit f51fed61eea0daba2f95f1a6074085e4cd513c7b ] + +This function runs within a kthread and need not necessarily finish +before system finishes boot and free_initmem() unmaps the .init.text +section. This function makes calls to SBI for probing unaligned access +speed, and if this is slow for some reason (say some debug prints were +added to SBI), the kthread can still be running at this point and result +in an instruction page fault when trying to fetch from the freed region. + +[ 25.642087] Unable to handle kernel paging request at virtual address ffffffff80a04ef8 +[ 25.646694] Current vec_check_unali pgtable: 4K pagesize, 48-bit VAs, pgdp=0x00004000316e9000 +[ 25.653170] [ffffffff80a04ef8] pgd=000010004be7e401, p4d=000010004be7e401, pud=000010004be7e001, pmd=000010000c3000e3 +[ 25.661244] Oops [#1] +[ 25.662997] Modules linked in: +[ 25.665357] CPU: 3 UID: 0 PID: 42 Comm: vec_check_unali Not tainted 7.0.0-tt-blackhole-asrinivasan-00007-g30ff73f18211 #570 PREEMPTLAZY +[ 25.674669] Hardware name: Tenstorrent Blackhole (DT) +[ 25.678545] epc : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c +[ 25.683458] ra : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c +[ 25.688372] epc : ffffffff80a04ef8 ra : ffffffff80a04ef8 sp : ffff8f8000203e20 +[ 25.693874] gp : ffffffff814dc168 tp : ffffaf8001ad9900 t0 : 0000000000000000 +[ 25.699401] t1 : fffffffffffffff0 t2 : ffffaf8001ad9a10 s0 : ffff8f8000203e30 +[ 25.704912] s1 : ffffaf80018dc780 a0 : 0000000000000000 a1 : 0000000000000002 +[ 25.710407] a2 : 00000000000001f0 a3 : 0000000000000018 a4 : 0000000000000000 +[ 25.715917] a5 : 0000000000000000 a6 : ffffaf8001c03d98 a7 : ffffaf8001c03e30 +[ 25.721419] s2 : ffff8f8000023c98 s3 : ffffaf8001aa1240 s4 : ffffffff80a04ee0 +[ 25.726937] s5 : 0000000000000000 s6 : 0000000000000000 s7 : 0000000000000000 +[ 25.732450] s8 : 0000000000000000 s9 : 0000000000000000 s10: 0000000000000000 +[ 25.737944] s11: 0000000000000000 t3 : 0000000000000002 t4 : 0000000000000402 +[ 25.743481] t5 : 0000000000000040 t6 : 0000000000000004 ssp : 0000000000000000 +[ 25.749024] status: 0000000200000120 badaddr: ffffffff80a04ef8 cause: 000000000000000c +[ 25.755060] [] vec_check_unaligned_access_speed_all_cpus+0x18/0x2c +[ 25.760964] [] kthread+0xd8/0xfc +[ 25.764660] [] ret_from_fork_kernel+0x18/0x1c4 +[ 25.769220] [] ret_from_fork_kernel_asm+0x16/0x18 +[ 25.774018] Code: cccc cccc cccc cccc cccc cccc cccc cccc cccc cccc (cccc) cccc + +Drop __init from its signature so that this doesn't happen. + +Fixes: a00e022be531 ("riscv: Annotate unaligned access init functions") +Signed-off-by: Anirudh Srinivasan +Assisted-by: Claude:claude-opus-4-6 +Link: https://patch.msgid.link/20260612-vec_unaligned_drop_init-v1-1-df969210ae34@oss.tenstorrent.com +Signed-off-by: Paul Walmsley +Signed-off-by: Sasha Levin +--- + arch/riscv/kernel/unaligned_access_speed.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c +index 70b5e69276209..78fc74a416787 100644 +--- a/arch/riscv/kernel/unaligned_access_speed.c ++++ b/arch/riscv/kernel/unaligned_access_speed.c +@@ -376,7 +376,7 @@ static void check_vector_unaligned_access(struct work_struct *work __always_unus + } + + /* Measure unaligned access speed on all CPUs present at boot in parallel. */ +-static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) ++static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) + { + schedule_on_each_cpu(check_vector_unaligned_access); + riscv_hwprobe_complete_async_probe(); +@@ -384,7 +384,7 @@ static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __alway + return 0; + } + #else /* CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS */ +-static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) ++static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) + { + return 0; + } +-- +2.53.0 + diff --git a/queue-6.18/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch b/queue-6.18/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch new file mode 100644 index 0000000000..179f91d7cd --- /dev/null +++ b/queue-6.18/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch @@ -0,0 +1,63 @@ +From c07e09a36240b899ff9fe9954cb1c6fbc243ee7e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Jul 2026 03:21:32 +0200 +Subject: riscv: mm: Fix out-of-bounds page-table walk during memory hot-remove + +From: Karl Mehltretter + +[ Upstream commit a0188cc133696627857d16054e43f9ebc7efc821 ] + +remove_pud_mapping() and remove_p4d_mapping() obtain a child table base +with pud_offset(p4dp, 0) and p4d_offset(pgd, 0), then add the index for +addr. + +RISC-V folds page-table levels at runtime. When a level is folded, its +offset helper returns the parent entry itself, but the index can still be +nonzero. Adding it walks past the parent table. Sv48 folds P4D, while Sv39 +folds both P4D and PUD, so memory hot-remove can descend into unrelated +memory and pass an invalid page to __free_pages(). This can trigger: + + kernel BUG at include/linux/mm.h:1810! + VM_BUG_ON_PAGE(page_ref_count(page) == 0) + arch_remove_memory+0x1e/0x5c + try_remove_memory+0x15e/0x200 + remove_memory+0x24/0x3c + +Only add the index when the corresponding page-table level is enabled, +matching p4d_offset() and pud_offset(). + +Fixes: c75a74f4ba19 ("riscv: mm: Add memory hotplugging support") +Assisted-by: Claude:claude-fable-5 +Signed-off-by: Karl Mehltretter +Link: https://patch.msgid.link/20260729012132.24882-1-kmehltretter@gmail.com +Signed-off-by: Paul Walmsley +Signed-off-by: Sasha Levin +--- + arch/riscv/mm/init.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c +index ee40ca01ac663..557bf160b503a 100644 +--- a/arch/riscv/mm/init.c ++++ b/arch/riscv/mm/init.c +@@ -1738,7 +1738,7 @@ static void __meminit remove_pud_mapping(pud_t *pud_base, unsigned long addr, un + + for (; addr < end; addr = next) { + next = pud_addr_end(addr, end); +- pudp = pud_base + pud_index(addr); ++ pudp = pgtable_l4_enabled ? pud_base + pud_index(addr) : pud_base; + pud = pudp_get(pudp); + if (!pud_present(pud)) + continue; +@@ -1769,7 +1769,7 @@ static void __meminit remove_p4d_mapping(p4d_t *p4d_base, unsigned long addr, un + + for (; addr < end; addr = next) { + next = p4d_addr_end(addr, end); +- p4dp = p4d_base + p4d_index(addr); ++ p4dp = pgtable_l5_enabled ? p4d_base + p4d_index(addr) : p4d_base; + p4d = p4dp_get(p4dp); + if (!p4d_present(p4d)) + continue; +-- +2.53.0 + diff --git a/queue-6.18/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch b/queue-6.18/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch new file mode 100644 index 0000000000..7671075898 --- /dev/null +++ b/queue-6.18/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch @@ -0,0 +1,47 @@ +From d7d06feda0304a17cd659c4eba74c10c004ed8e1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 10:38:36 +0800 +Subject: rtase: fix double free of multi-frag skb on DMA map failure + +From: Yun Lu + +[ Upstream commit 6fb7b769d6ed6d1d2e02af4a80e57a2477f35086 ] + +In rtase_start_xmit(), when the head buffer DMA mapping fails after +rtase_xmit_frags() has mapped all fragments, the error path clears +the fragment descriptors with rtase_tx_clear_range(), which frees +the skb through the last-frag slot and accounts tx_dropped. Control +then falls through to the common error label, which frees the same +skb a second time and counts it again. + +Return right after clearing the fragments when the skb owns frags; +the no-frag case still drops through and frees the head skb once. + +Fixes: d6e882b89fdf ("rtase: Implement .ndo_start_xmit function") +Signed-off-by: Yun Lu +Reviewed-by: Jacob Keller +Reviewed-by: Justin Lai +Link: https://patch.msgid.link/20260721023836.6691-1-luyun_611@163.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/realtek/rtase/rtase_main.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c +index a57a525327a3b..bc9b14614f7a7 100644 +--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c ++++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c +@@ -1620,6 +1620,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb, + err_dma_1: + ring->skbuff[entry] = NULL; + rtase_tx_clear_range(ring, ring->cur_idx + 1, frags); ++ if (frags) ++ /* the frags were cleared above, along with the skb */ ++ return NETDEV_TX_OK; + + err_dma_0: + tp->stats.tx_dropped++; +-- +2.53.0 + diff --git a/queue-6.18/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch b/queue-6.18/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch new file mode 100644 index 0000000000..99ab2fb68c --- /dev/null +++ b/queue-6.18/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch @@ -0,0 +1,46 @@ +From ff8e24ef41809e5ccb090b1366134142919dedf1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 May 2026 14:58:33 +0200 +Subject: sched/deadline: Use revised wakeup rule only for running dl_server + +From: Gabriele Monaco + +[ Upstream commit 1842bf97af109f5ebf830175c9725bf81ebb78b1 ] + +Commit 14a857056466 ("sched/deadline: Use revised wakeup rule for +dl_server") applies the revised wakeup rule to any server, as a result +servers that are not running (dl_defer_running == 0) and start with a +deadline overflow get enqueued and can boost tasks as if they were +running, invalidating the defer rule and the documented state model. + +Apply the revised wakeup rule only for deferrable servers that are +marked as running. + +Fixes: 14a857056466 ("sched/deadline: Use revised wakeup rule for dl_server") +Signed-off-by: Gabriele Monaco +Signed-off-by: Peter Zijlstra (Intel) +Acked-by: Juri Lelli +Tested-by: Andrea Righi +Link: https://patch.msgid.link/20260522125833.264145-1-gmonaco@redhat.com +Signed-off-by: Sasha Levin +--- + kernel/sched/deadline.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c +index d5052f238adf7..e8b9cf0767b27 100644 +--- a/kernel/sched/deadline.c ++++ b/kernel/sched/deadline.c +@@ -1036,7 +1036,8 @@ static void update_dl_entity(struct sched_dl_entity *dl_se) + if (dl_time_before(dl_se->deadline, rq_clock(rq)) || + dl_entity_overflow(dl_se, rq_clock(rq))) { + +- if (unlikely((!dl_is_implicit(dl_se) || dl_se->dl_defer) && ++ if (unlikely((!dl_is_implicit(dl_se) || ++ (dl_se->dl_defer && dl_se->dl_defer_running)) && + !dl_time_before(dl_se->deadline, rq_clock(rq)) && + !is_dl_boosted(dl_se))) { + update_dl_revised_wakeup(dl_se, rq); +-- +2.53.0 + diff --git a/queue-6.18/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-6.18/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch new file mode 100644 index 0000000000..3a710454e3 --- /dev/null +++ b/queue-6.18/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch @@ -0,0 +1,56 @@ +From aeee4f59a3ae7570d82f87b4fe6cf54a6a696572 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 14 Jul 2026 19:49:34 +0900 +Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer + +From: HyeongJun An + +[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ] + +iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the +target-supplied data segment. The segment carries a 2-byte sense length +followed by the sense bytes, so it must hold 2 + senselen bytes, but the +bounds check only requires datalen >= senselen: + + senselen = get_unaligned_be16(data); + if (datalen < senselen) + goto invalid_datalen; + memcpy(sc->sense_buffer, data + 2, + min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); + +A target that returns a SCSI Response whose datalen equals senselen +(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data + +2 read up to two bytes past the received data. Those bytes are stale +conn->data contents and end up in the command's sense buffer, which is +returned to userspace. + +Account for the 2-byte sense length prefix in the check. + +Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi") +Suggested-by: Sashiko AI +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c +index c9f410c509783..e2adebc80b3a9 100644 +--- a/drivers/scsi/libiscsi.c ++++ b/drivers/scsi/libiscsi.c +@@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, + } + + senselen = get_unaligned_be16(data); +- if (datalen < senselen) ++ if (datalen < senselen + 2) + goto invalid_datalen; + + memcpy(sc->sense_buffer, data + 2, +-- +2.53.0 + diff --git a/queue-6.18/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-6.18/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch new file mode 100644 index 0000000000..21a9861b7a --- /dev/null +++ b/queue-6.18/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch @@ -0,0 +1,71 @@ +From 342da4b6b3e693585633d0320efdc1598cc3902d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 15:58:48 +0900 +Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the + connection buffer + +From: HyeongJun An + +[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ] + +iscsi_tcp_hdr_dissect() receives the data segment of several PDU types +into the fixed-size conn->data buffer, which is allocated for +ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes. For the LOGIN_RSP, TEXT_RSP, +REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU +whose DataSegmentLength exceeds that buffer. + +The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its +data segment (sense/response data) into conn->data via +iscsi_tcp_data_recv_prep(), but it does so without the same check. The +only upstream bound on in.datalen is conn->max_recv_dlength, the +initiator's advertised MaxRecvDataSegmentLength, which is commonly +negotiated well above 8192 (open-iscsi defaults to 262144). A target +that returns a SCSI Response with a DataSegmentLength between 8193 and +max_recv_dlength therefore overflows the 8192-byte conn->data buffer. + +Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly +like those responses: bound the data segment, receive it into conn->data +when present, and otherwise complete the PDU with no data. Fold the +opcode into that case group rather than duplicating the check. + +Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld") +Suggested-by: Chris Leech +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi_tcp.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c +index e90805ba868fb..7223bb18b0480 100644 +--- a/drivers/scsi/libiscsi_tcp.c ++++ b/drivers/scsi/libiscsi_tcp.c +@@ -752,13 +752,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); + spin_unlock(&conn->session->back_lock); + break; +- case ISCSI_OP_SCSI_CMD_RSP: +- if (tcp_conn->in.datalen) { +- iscsi_tcp_data_recv_prep(tcp_conn); +- return 0; +- } +- rc = iscsi_complete_pdu(conn, hdr, NULL, 0); +- break; + case ISCSI_OP_R2T: + if (ahslen) { + rc = ISCSI_ERR_AHSLEN; +@@ -766,6 +759,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + } + rc = iscsi_tcp_r2t_rsp(conn, hdr); + break; ++ case ISCSI_OP_SCSI_CMD_RSP: + case ISCSI_OP_LOGIN_RSP: + case ISCSI_OP_TEXT_RSP: + case ISCSI_OP_REJECT: +-- +2.53.0 + diff --git a/queue-6.18/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch b/queue-6.18/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch new file mode 100644 index 0000000000..5f0c24fb05 --- /dev/null +++ b/queue-6.18/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch @@ -0,0 +1,165 @@ +From 8f63a0f98cdc2c5f72399be4cc55262c9ddb0891 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 16:11:45 +0800 +Subject: scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race + +From: Xingui Yang + +[ Upstream commit 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 ] + +Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue +for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock: +the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue, +calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI +devices and waits for the host to become runtime-active. But the host +cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the +drain is blocked on that very handler. + +However skipping the drain reintroduces a race: hisi_sas returns from +resume before all PHY UP work and libsas discovery work finish. The +controller may then autosuspend while disks are still waking up. The +disks issue IO to a suspended controller, the IO fails, and the disks +get disabled. + +Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT +notification to after sas_drain_work(). By then the host resume is about +to complete, so device removal through device_link no longer blocks on +the resume and the cycle is broken. + +With the deadlock gone, restore sas_resume_ha() (the draining variant) +in hisi_sas and remove sas_resume_ha_no_sync(). + +The reorder is safe for the other libsas consumers (isci, pm8001, +aic94xx, mvsas). During suspend, sas_suspend_devices() calls +sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to +NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a +timed-out phy's disk is immediately rejected by the LLDD before reaching +hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET), +and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both +complete directly via scsi_done() without entering SCSI EH. This is +identical in both the old and new ordering since lldd_dev_gone runs +during suspend, before resume. The reorder only affects when the +PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work() +vs. asynchronous after resume returns), not whether I/O can reach the +device. aic94xx and mvsas do not register any PM ops and never reach +this code path. + +Fixes: fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume") +Signed-off-by: Xingui Yang +Reviewed-by: John Garry +Link: https://patch.msgid.link/20260716081145.3950172-1-yangxingui@huawei.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +------ + drivers/scsi/libsas/sas_init.c | 37 +++++++++++++------------- + include/scsi/libsas.h | 1 - + 3 files changed, 19 insertions(+), 29 deletions(-) + +diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +index f30d4a58d7ab5..16256a0529253 100644 +--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c ++++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +@@ -5261,15 +5261,7 @@ static int _resume_v3_hw(struct device *device) + return rc; + } + phys_init_v3_hw(hisi_hba); +- +- /* +- * If a directly-attached disk is removed during suspend, a deadlock +- * may occur, as the PHYE_RESUME_TIMEOUT processing will require the +- * hisi_hba->device to be active, which can only happen when resume +- * completes. So don't wait for the HA event workqueue to drain upon +- * resume. +- */ +- sas_resume_ha_no_sync(sha); ++ sas_resume_ha(sha); + clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags); + + dev_warn(dev, "end of resuming controller\n"); +diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c +index 8566bb1208a05..ac157ab6a3011 100644 +--- a/drivers/scsi/libsas/sas_init.c ++++ b/drivers/scsi/libsas/sas_init.c +@@ -409,7 +409,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha) + } + } + +-static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) ++void sas_resume_ha(struct sas_ha_struct *ha) + { + const unsigned long tmo = msecs_to_jiffies(25000); + int i; +@@ -425,6 +425,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n", + i, i > 1 ? "s" : ""); + wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo); ++ ++ /* ++ * All phys are back up or timed out. Turn on I/O and drain ++ * pending work. ++ */ ++ scsi_unblock_requests(ha->shost); ++ sas_drain_work(ha); ++ ++ /* ++ * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler ++ * calls sas_deform_port() -> sas_destruct_devices(), which removes ++ * SCSI devices and, for LLDDs using device_link() PM sync, waits ++ * for the host to be runtime-active. Sending it before the drain ++ * would deadlock: the drain waits for the handler, the handler ++ * waits for host resume, and host resume waits for the drain to ++ * finish. ++ */ + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_phy *phy = ha->sas_phy[i]; + +@@ -435,12 +452,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + } + } + +- /* all phys are back up or timed out, turn on i/o so we can +- * flush out disks that did not return +- */ +- scsi_unblock_requests(ha->shost); +- if (drain) +- sas_drain_work(ha); + clear_bit(SAS_HA_RESUMING, &ha->state); + + sas_queue_deferred_work(ha); +@@ -449,20 +460,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + */ + sas_resume_insert_broadcast_ha(ha); + } +- +-void sas_resume_ha(struct sas_ha_struct *ha) +-{ +- _sas_resume_ha(ha, true); +-} + EXPORT_SYMBOL(sas_resume_ha); + +-/* A no-sync variant, which does not call sas_drain_ha(). */ +-void sas_resume_ha_no_sync(struct sas_ha_struct *ha) +-{ +- _sas_resume_ha(ha, false); +-} +-EXPORT_SYMBOL(sas_resume_ha_no_sync); +- + void sas_suspend_ha(struct sas_ha_struct *ha) + { + int i; +diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h +index a0635b128d7ab..cc0a1f320f17d 100644 +--- a/include/scsi/libsas.h ++++ b/include/scsi/libsas.h +@@ -684,7 +684,6 @@ extern int sas_register_ha(struct sas_ha_struct *); + extern int sas_unregister_ha(struct sas_ha_struct *); + extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha); + extern void sas_resume_ha(struct sas_ha_struct *sas_ha); +-extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha); + extern void sas_suspend_ha(struct sas_ha_struct *sas_ha); + + int sas_phy_reset(struct sas_phy *phy, int hard_reset); +-- +2.53.0 + diff --git a/queue-6.18/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch b/queue-6.18/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch new file mode 100644 index 0000000000..72bb5f1b06 --- /dev/null +++ b/queue-6.18/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch @@ -0,0 +1,80 @@ +From 34c67fc4eacbb0b3c7eb3f2d389e6cd87250a89b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 09:30:10 +0300 +Subject: scsi: target: Clear cmd_cnt when initial counter enrollment fails + +From: Leon Romanovsky + +[ Upstream commit a8ddfd2425bbbafadae8700d63ed8a61a4109878 ] + +When target_get_sess_cmd() fails during session shutdown because +percpu_ref_tryget_live() returns false, the command keeps the +se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier +without owning a reference. Final release through +target_release_cmd_kref() then issues an unmatched percpu_ref_put(). + +Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during +cmd setup") moved the cmd_cnt assignment ahead of the reference +acquisition. Clear se_cmd->cmd_cnt whenever the initial +target_get_sess_cmd() fails in target_init_cmd() and +target_submit_tmr(), so release performs exactly one matching put per +acquired reference. + +Fixes: 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup") +Signed-off-by: Leon Romanovsky +Reviewed-by: Mike Christie +Link: https://patch.msgid.link/20260722-reference-count-underflow-in-target-v1-1-63ab664f12fd@nvidia.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/target/target_core_transport.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c +index 88544c911949c..5ad2dcf730cd2 100644 +--- a/drivers/target/target_core_transport.c ++++ b/drivers/target/target_core_transport.c +@@ -1691,6 +1691,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, + u32 data_length, int task_attr, int data_dir, int flags) + { + struct se_portal_group *se_tpg; ++ int ret; + + se_tpg = se_sess->se_tpg; + BUG_ON(!se_tpg); +@@ -1720,7 +1721,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, + * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second + * kref_put() to happen during fabric packet acknowledgement. + */ +- return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); ++ ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); ++ if (ret) ++ se_cmd->cmd_cnt = NULL; ++ ++ return ret; + } + EXPORT_SYMBOL_GPL(target_init_cmd); + +@@ -1996,8 +2001,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, + * allocation failure. + */ + ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp); +- if (ret < 0) ++ if (ret < 0) { ++ se_cmd->cmd_cnt = NULL; + return -ENOMEM; ++ } + + if (tm_type == TMR_ABORT_TASK) + se_cmd->se_tmr_req->ref_task_tag = tag; +@@ -2005,6 +2012,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, + /* See target_submit_cmd for commentary */ + ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); + if (ret) { ++ se_cmd->cmd_cnt = NULL; + core_tmr_release_req(se_cmd->se_tmr_req); + return ret; + } +-- +2.53.0 + diff --git a/queue-6.18/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch b/queue-6.18/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch new file mode 100644 index 0000000000..06602bc2e6 --- /dev/null +++ b/queue-6.18/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch @@ -0,0 +1,54 @@ +From 0ed6afaf6955668266d6e051f8e7cac8c223b1e8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 15:58:50 +0800 +Subject: scsi: target: iblock: Fix wrong PR ops NULL check for PREEMPT/RELEASE + +From: TanZheng + +[ Upstream commit 9c33222bd387312874fbe36ca8002e5c945b9653 ] + +In the iblock_execute_pr_out() function, PRO_PREEMPT, +PRO_PREEMPT_AND_ABORT, and PRO_RELEASE all perform callback capability +checks through ops->pr_clear. The error check allows unimplemented hooks +to pass through the gate, resulting dereferencing a NULL function +pointer. + +Check whether the hooks that need to be called are supported. + +Fixes: 394f81184882 ("scsi: target: Add block PR support to iblock") +Signed-off-by: TanZheng +Reviewed-by: Mike Christie +Link: https://patch.msgid.link/20260724075850.280699-1-kensanya@163.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/target/target_core_iblock.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c +index 66c292b7d74bc..4ec322f371bc2 100644 +--- a/drivers/target/target_core_iblock.c ++++ b/drivers/target/target_core_iblock.c +@@ -902,7 +902,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, + break; + case PRO_PREEMPT: + case PRO_PREEMPT_AND_ABORT: +- if (!ops->pr_clear) { ++ if (!ops->pr_preempt) { + pr_err("block_device does not support pr_preempt.\n"); + return TCM_UNSUPPORTED_SCSI_OPCODE; + } +@@ -912,8 +912,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, + sa == PRO_PREEMPT_AND_ABORT); + break; + case PRO_RELEASE: +- if (!ops->pr_clear) { +- pr_err("block_device does not support pr_pclear.\n"); ++ if (!ops->pr_release) { ++ pr_err("block_device does not support pr_release.\n"); + return TCM_UNSUPPORTED_SCSI_OPCODE; + } + +-- +2.53.0 + diff --git a/queue-6.18/scsi-ufs-core-avoid-irq-thread-wakeup-during-active-.patch b/queue-6.18/scsi-ufs-core-avoid-irq-thread-wakeup-during-active-.patch new file mode 100644 index 0000000000..f5cd074e8f --- /dev/null +++ b/queue-6.18/scsi-ufs-core-avoid-irq-thread-wakeup-during-active-.patch @@ -0,0 +1,46 @@ +From 25b22498d927209842878d3199fe52c395e8ca25 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 6 Mar 2026 13:43:02 +0800 +Subject: scsi: ufs: core: Avoid IRQ thread wakeup during active UIC command + +From: Peter Wang + +[ Upstream commit 6475cfb81fc4f6175b6d15d1c205a5168dc10b46 ] + +Only return IRQ_WAKE_THREAD when MCQ and ESI are not enabled and no UIC +command is active. The default UIC command timeout is 500ms, Using threaded +IRQs during an active UIC command increases the risk of timeout due to +possible preemption by other system IRQs. + +Signed-off-by: Peter Wang +Reviewed-by: Bart Van Assche +Link: https://patch.msgid.link/20260306054419.3816557-1-peter.wang@mediatek.com +Signed-off-by: Martin K. Petersen +Stable-dep-of: 8a309036f557 ("scsi: ufs: core: Revert "Delegate the interrupt service routine to a threaded IRQ handler"") +Signed-off-by: Sasha Levin +--- + drivers/ufs/core/ufshcd.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c +index 2545f7a51211b..482a9299a5894 100644 +--- a/drivers/ufs/core/ufshcd.c ++++ b/drivers/ufs/core/ufshcd.c +@@ -7190,8 +7190,12 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba) + struct ufs_hba *hba = __hba; + u32 intr_status, enabled_intr_status; + +- /* Move interrupt handling to thread when MCQ & ESI are not enabled */ +- if (!hba->mcq_enabled || !hba->mcq_esi_enabled) ++ /* ++ * Handle interrupt in thread if MCQ or ESI is disabled, ++ * and no active UIC command. ++ */ ++ if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) && ++ !hba->active_uic_cmd) + return IRQ_WAKE_THREAD; + + intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); +-- +2.53.0 + diff --git a/queue-6.18/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch b/queue-6.18/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch new file mode 100644 index 0000000000..ab07520b0d --- /dev/null +++ b/queue-6.18/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch @@ -0,0 +1,70 @@ +From 552ad6257264e1eeb52f2ea4471d1742a906810f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 01:27:26 +0800 +Subject: scsi: ufs: core: Cancel RTC work in active-active suspend + +From: Guangshuo Li + +[ Upstream commit f71b4a30983b846b4075bf544e835121e70e6a43 ] + +UFS RTC support schedules ufs_rtc_update_work to periodically update the +device RTC. The work can issue query commands and access the UFS host +controller. + +A previous change moved the RTC work cancellation before the PRE_CHANGE +vendor suspend callback to close a race in the common suspend path. +However, the active-active path jumps directly to vops_suspend after +flushing exception handling work and therefore bypasses the +cancellation. + +If the RTC work runs while the vendor suspend callback is gating or +otherwise changing hardware state, it can access the controller during +suspend and trigger an SError. + +Cancel the RTC work before entering the vendor suspend callback in the +active-active path. Since this path now cancels the work, move the RTC +work scheduling outside the device and link state restoration block in +the resume path. This restarts RTC updates after an active-active +suspend and resume cycle. + +Fixes: b0bd84c39289 ("scsi: ufs: core: Fix SError in ufshcd_rtc_work() during UFS suspend") +Signed-off-by: Guangshuo Li +Reviewed-by: Peter Wang +Reviewed-by: Bean Huo +Reviewed-by: Bart Van Assche +Link: https://patch.msgid.link/20260714172726.1736967-1-lgs201920130244@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/ufs/core/ufshcd.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c +index 8de3d0886a121..2545f7a51211b 100644 +--- a/drivers/ufs/core/ufshcd.c ++++ b/drivers/ufs/core/ufshcd.c +@@ -9873,6 +9873,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) + req_link_state == UIC_LINK_ACTIVE_STATE) { + ufshcd_disable_auto_bkops(hba); + flush_work(&hba->eeh_work); ++ cancel_delayed_work_sync(&hba->ufs_rtc_update_work); + goto vops_suspend; + } + +@@ -10082,10 +10083,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) + if (ret) + goto set_old_link_state; + ufshcd_set_timestamp_attr(hba); +- schedule_delayed_work(&hba->ufs_rtc_update_work, +- msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); + } + ++ schedule_delayed_work(&hba->ufs_rtc_update_work, ++ msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); ++ + if (ufshcd_keep_autobkops_enabled_except_suspend(hba)) + ufshcd_enable_auto_bkops(hba); + else +-- +2.53.0 + diff --git a/queue-6.18/scsi-ufs-core-revert-delegate-the-interrupt-service-.patch b/queue-6.18/scsi-ufs-core-revert-delegate-the-interrupt-service-.patch new file mode 100644 index 0000000000..5b47eaf85a --- /dev/null +++ b/queue-6.18/scsi-ufs-core-revert-delegate-the-interrupt-service-.patch @@ -0,0 +1,123 @@ +From aabb7f1b10c169074903a3a36c37702c73ab3f58 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 10:12:28 -0700 +Subject: scsi: ufs: core: Revert "Delegate the interrupt service routine to a + threaded IRQ handler" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Bart Van Assche + +[ Upstream commit 8a309036f557d3ff4efb2beea5132ba91172d934 ] + +There have been multiple reports of performance regressions caused by +commit 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service +routine to a threaded IRQ handler"). Hence this revert. + +This patch reverts most of the following commits: + + * 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service + routine to a threaded IRQ handler") + + * 6475cfb81fc4 ("scsi: ufs: core: Avoid IRQ thread wakeup during active + UIC command") + +This patch preserves the following commits: + + * 034d319c8899 ("scsi: ufs: core: Fix interrupt handling for MCQ Mode") + + * eabcac808ca3 ("scsi: ufs: core: Fix IRQ lock inversion for the SCSI + host lock") + +Cc: Neil Armstrong +Cc: 孙魁 (Kui Sun) +Cc: André Draszik +Cc: Gregory CLEMENT +Cc: Sebastian Andrzej Siewior +Fixes: 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler") +Signed-off-by: Bart Van Assche +Reviewed-by: Sebastian Andrzej Siewior +Tested-by: André Draszik # on Pixel 6 +Reviewed-by: André Draszik +Link: https://patch.msgid.link/b70eb60a01f971bed68c42c5b555929db5f835df.1784135511.git.bvanassche@acm.org +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/ufs/core/ufshcd.c | 39 +++------------------------------------ + 1 file changed, 3 insertions(+), 36 deletions(-) + +diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c +index 482a9299a5894..504600f1e08cd 100644 +--- a/drivers/ufs/core/ufshcd.c ++++ b/drivers/ufs/core/ufshcd.c +@@ -7128,7 +7128,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) + } + + /** +- * ufshcd_threaded_intr - Threaded interrupt service routine ++ * ufshcd_intr - Main interrupt service routine + * @irq: irq number + * @__hba: pointer to adapter instance + * +@@ -7136,7 +7136,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) + * IRQ_HANDLED - If interrupt is valid + * IRQ_NONE - If invalid interrupt + */ +-static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba) ++static irqreturn_t ufshcd_intr(int irq, void *__hba) + { + u32 last_intr_status, intr_status, enabled_intr_status = 0; + irqreturn_t retval = IRQ_NONE; +@@ -7175,38 +7175,6 @@ static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba) + return retval; + } + +-/** +- * ufshcd_intr - Main interrupt service routine +- * @irq: irq number +- * @__hba: pointer to adapter instance +- * +- * Return: +- * IRQ_HANDLED - If interrupt is valid +- * IRQ_WAKE_THREAD - If handling is moved to threaded handled +- * IRQ_NONE - If invalid interrupt +- */ +-static irqreturn_t ufshcd_intr(int irq, void *__hba) +-{ +- struct ufs_hba *hba = __hba; +- u32 intr_status, enabled_intr_status; +- +- /* +- * Handle interrupt in thread if MCQ or ESI is disabled, +- * and no active UIC command. +- */ +- if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) && +- !hba->active_uic_cmd) +- return IRQ_WAKE_THREAD; +- +- intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); +- enabled_intr_status = intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); +- +- ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); +- +- /* Directly handle interrupts since MCQ ESI handlers does the hard job */ +- return ufshcd_sl_intr(hba, enabled_intr_status); +-} +- + static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag) + { + int err = 0; +@@ -10835,8 +10803,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) + ufshcd_readl(hba, REG_INTERRUPT_ENABLE); + + /* IRQ registration */ +- err = devm_request_threaded_irq(dev, irq, ufshcd_intr, ufshcd_threaded_intr, +- IRQF_ONESHOT | IRQF_SHARED, UFSHCD, hba); ++ err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba); + if (err) { + dev_err(hba->dev, "request irq failed\n"); + goto out_disable; +-- +2.53.0 + diff --git a/queue-6.18/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-6.18/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch new file mode 100644 index 0000000000..2925de129c --- /dev/null +++ b/queue-6.18/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch @@ -0,0 +1,66 @@ +From 1b1a0106d20fecf8a2d2ee0a2211e3793296ab6d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 09:27:36 +0200 +Subject: scsi: zfcp: Fix memory leak during adapter release by destroying + gid_pn_req + +From: Benjamin Block + +[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ] + +When releasing an adapter we don't free the mempool 'gid_pn_req' that is +allocated during the enqueue. This leaks memory: + + unreferenced object 0xd8d29297de700 (size 256): + comm "(udev-worker)", pid 2105, jiffies 4294945794 + hex dump (first 32 bytes): + 00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00 ......N......... + ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0 ..........._g... + backtrace (crc 4a5b5da2): + [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0 + [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0 + [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150 + [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp] + [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp] + [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp] + [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80 + [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390 + [<000dc45f643d8238>] online_store+0x298/0x5b0 + [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480 + [<000dc45f62c81150>] new_sync_write+0x370/0x4b0 + [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0 + [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0 + [<000dc45f621c4a16>] do_syscall+0x2f6/0x430 + [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0 + [<000dc45f64dc2224>] system_call+0x74/0xa0 + +Fix this by destroying the mempool during the adapter's release. + +Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp") +Signed-off-by: Benjamin Block +Tested-by: M Nikhil +Acked-by: M Nikhil +Reviewed-by: Chinmaya Kajagar +Reviewed-by: Nihar Panda +Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/s390/scsi/zfcp_aux.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c +index dc2265ebb11b8..03f723042434b 100644 +--- a/drivers/s390/scsi/zfcp_aux.c ++++ b/drivers/s390/scsi/zfcp_aux.c +@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) + static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) + { + mempool_destroy(adapter->pool.erp_req); ++ mempool_destroy(adapter->pool.gid_pn_req); + mempool_destroy(adapter->pool.scsi_req); + mempool_destroy(adapter->pool.scsi_abort); + mempool_destroy(adapter->pool.qtcb_pool); +-- +2.53.0 + diff --git a/queue-6.18/selftest-af_unix-create-its-own-.gitignore.patch b/queue-6.18/selftest-af_unix-create-its-own-.gitignore.patch new file mode 100644 index 0000000000..6de704b4aa --- /dev/null +++ b/queue-6.18/selftest-af_unix-create-its-own-.gitignore.patch @@ -0,0 +1,86 @@ +From 4d0fbcf75936d884dad94ab2b62aedee97dbb0dd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 24 Nov 2025 21:26:39 +0000 +Subject: selftest: af_unix: Create its own .gitignore. + +From: Kuniyuki Iwashima + +[ Upstream commit adb6b68c50604f3113d62758e839cc0186b94ce8 ] + +Somehow AF_UNIX tests have reused ../.gitignore, +but now NIPA warns about it. + +Let's create .gitignore under af_unix/. + +Signed-off-by: Kuniyuki Iwashima +Link: https://patch.msgid.link/20251124212805.486235-2-kuniyu@google.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: 7f57c650d08b ("selftests/net/af_unix: test listen() rejects wrong socket states") +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/net/.gitignore | 8 -------- + tools/testing/selftests/net/af_unix/.gitignore | 8 ++++++++ + 2 files changed, 8 insertions(+), 8 deletions(-) + create mode 100644 tools/testing/selftests/net/af_unix/.gitignore + +diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore +index f627f1a2a2b8c..e2e2063399675 100644 +--- a/tools/testing/selftests/net/.gitignore ++++ b/tools/testing/selftests/net/.gitignore +@@ -4,7 +4,6 @@ bind_timewait + bind_wildcard + busy_poller + cmsg_sender +-diag_uid + epoll_busy_poll + fin_ack_lat + gro +@@ -18,7 +17,6 @@ ipv6_flowlabel + ipv6_flowlabel_mgr + ipv6_fragmentation + log.txt +-msg_oob + msg_zerocopy + netlink-dumps + nettest +@@ -35,9 +33,6 @@ reuseport_bpf_numa + reuseport_dualstack + rxtimestamp + sctp_hello +-scm_inq +-scm_pidfd +-scm_rights + sk_bind_sendto_listen + sk_connect_zero_addr + sk_so_peek_off +@@ -45,7 +40,6 @@ skf_net_off + socket + so_incoming_cpu + so_netns_cookie +-so_peek_off + so_txtime + so_rcv_listener + stress_reuseport_listen +@@ -65,5 +59,3 @@ txtimestamp + udpgso + udpgso_bench_rx + udpgso_bench_tx +-unix_connect +-unix_connreset +diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore +new file mode 100644 +index 0000000000000..240b26740c9e0 +--- /dev/null ++++ b/tools/testing/selftests/net/af_unix/.gitignore +@@ -0,0 +1,8 @@ ++diag_uid ++msg_oob ++scm_inq ++scm_pidfd ++scm_rights ++so_peek_off ++unix_connect ++unix_connreset +-- +2.53.0 + diff --git a/queue-6.18/selftests-af_unix-add-tests-for-econnreset-and-eof-s.patch b/queue-6.18/selftests-af_unix-add-tests-for-econnreset-and-eof-s.patch new file mode 100644 index 0000000000..695fe1bd70 --- /dev/null +++ b/queue-6.18/selftests-af_unix-add-tests-for-econnreset-and-eof-s.patch @@ -0,0 +1,245 @@ +From 908104d927810c5929f96d2d265b840b9d09f00c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 13 Nov 2025 12:28:02 +0100 +Subject: selftests: af_unix: Add tests for ECONNRESET and EOF semantics +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Sunday Adelodun + +[ Upstream commit 45a1cd8346ca245a1ca475b26eb6ceb9d8b7c6f0 ] + +Add selftests to verify and document Linux’s intended behaviour for +UNIX domain sockets (SOCK_STREAM and SOCK_DGRAM) when a peer closes. +The tests verify that: + + 1. SOCK_STREAM returns EOF when the peer closes normally. + 2. SOCK_STREAM returns ECONNRESET if the peer closes with unread data. + 3. SOCK_SEQPACKET returns EOF when the peer closes normally. + 4. SOCK_SEQPACKET returns ECONNRESET if the peer closes with unread data. + 5. SOCK_DGRAM does not return ECONNRESET when the peer closes. + +This follows up on review feedback suggesting a selftest to clarify +Linux’s semantics. + +Suggested-by: Kuniyuki Iwashima +Signed-off-by: Sunday Adelodun +Link: https://patch.msgid.link/20251113112802.44657-1-adelodunolaoluwa@yahoo.com +Signed-off-by: Paolo Abeni +Stable-dep-of: 7f57c650d08b ("selftests/net/af_unix: test listen() rejects wrong socket states") +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/net/.gitignore | 1 + + tools/testing/selftests/net/af_unix/Makefile | 1 + + .../selftests/net/af_unix/unix_connreset.c | 177 ++++++++++++++++++ + 3 files changed, 179 insertions(+) + create mode 100644 tools/testing/selftests/net/af_unix/unix_connreset.c + +diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore +index 8f9850a71f542..f627f1a2a2b8c 100644 +--- a/tools/testing/selftests/net/.gitignore ++++ b/tools/testing/selftests/net/.gitignore +@@ -66,3 +66,4 @@ udpgso + udpgso_bench_rx + udpgso_bench_tx + unix_connect ++unix_connreset +diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile +index 2889403e35468..4c0375e28bbee 100644 +--- a/tools/testing/selftests/net/af_unix/Makefile ++++ b/tools/testing/selftests/net/af_unix/Makefile +@@ -13,6 +13,7 @@ TEST_GEN_PROGS := \ + scm_rights \ + so_peek_off \ + unix_connect \ ++ unix_connreset \ + # end of TEST_GEN_PROGS + + include ../../lib.mk +diff --git a/tools/testing/selftests/net/af_unix/unix_connreset.c b/tools/testing/selftests/net/af_unix/unix_connreset.c +new file mode 100644 +index 0000000000000..bffef2b54bfd1 +--- /dev/null ++++ b/tools/testing/selftests/net/af_unix/unix_connreset.c +@@ -0,0 +1,177 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Selftest for AF_UNIX socket close and ECONNRESET behaviour. ++ * ++ * This test verifies: ++ * 1. SOCK_STREAM returns EOF when the peer closes normally. ++ * 2. SOCK_STREAM returns ECONNRESET if peer closes with unread data. ++ * 3. SOCK_SEQPACKET returns EOF when the peer closes normally. ++ * 4. SOCK_SEQPACKET returns ECONNRESET if the peer closes with unread data. ++ * 5. SOCK_DGRAM does not return ECONNRESET when the peer closes. ++ * ++ * These tests document the intended Linux behaviour. ++ * ++ */ ++ ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../../kselftest_harness.h" ++ ++#define SOCK_PATH "/tmp/af_unix_connreset.sock" ++ ++static void remove_socket_file(void) ++{ ++ unlink(SOCK_PATH); ++} ++ ++FIXTURE(unix_sock) ++{ ++ int server; ++ int client; ++ int child; ++}; ++ ++FIXTURE_VARIANT(unix_sock) ++{ ++ int socket_type; ++ const char *name; ++}; ++ ++FIXTURE_VARIANT_ADD(unix_sock, stream) { ++ .socket_type = SOCK_STREAM, ++ .name = "SOCK_STREAM", ++}; ++ ++FIXTURE_VARIANT_ADD(unix_sock, dgram) { ++ .socket_type = SOCK_DGRAM, ++ .name = "SOCK_DGRAM", ++}; ++ ++FIXTURE_VARIANT_ADD(unix_sock, seqpacket) { ++ .socket_type = SOCK_SEQPACKET, ++ .name = "SOCK_SEQPACKET", ++}; ++ ++FIXTURE_SETUP(unix_sock) ++{ ++ struct sockaddr_un addr = {}; ++ int err; ++ ++ addr.sun_family = AF_UNIX; ++ strcpy(addr.sun_path, SOCK_PATH); ++ remove_socket_file(); ++ ++ self->server = socket(AF_UNIX, variant->socket_type, 0); ++ ASSERT_LT(-1, self->server); ++ ++ err = bind(self->server, (struct sockaddr *)&addr, sizeof(addr)); ++ ASSERT_EQ(0, err); ++ ++ if (variant->socket_type == SOCK_STREAM || ++ variant->socket_type == SOCK_SEQPACKET) { ++ err = listen(self->server, 1); ++ ASSERT_EQ(0, err); ++ } ++ ++ self->client = socket(AF_UNIX, variant->socket_type | SOCK_NONBLOCK, 0); ++ ASSERT_LT(-1, self->client); ++ ++ err = connect(self->client, (struct sockaddr *)&addr, sizeof(addr)); ++ ASSERT_EQ(0, err); ++} ++ ++FIXTURE_TEARDOWN(unix_sock) ++{ ++ if (variant->socket_type == SOCK_STREAM || ++ variant->socket_type == SOCK_SEQPACKET) ++ close(self->child); ++ ++ close(self->client); ++ close(self->server); ++ remove_socket_file(); ++} ++ ++/* Test 1: peer closes normally */ ++TEST_F(unix_sock, eof) ++{ ++ char buf[16] = {}; ++ ssize_t n; ++ ++ if (variant->socket_type == SOCK_STREAM || ++ variant->socket_type == SOCK_SEQPACKET) { ++ self->child = accept(self->server, NULL, NULL); ++ ASSERT_LT(-1, self->child); ++ ++ close(self->child); ++ } else { ++ close(self->server); ++ } ++ ++ n = recv(self->client, buf, sizeof(buf), 0); ++ ++ if (variant->socket_type == SOCK_STREAM || ++ variant->socket_type == SOCK_SEQPACKET) { ++ ASSERT_EQ(0, n); ++ } else { ++ ASSERT_EQ(-1, n); ++ ASSERT_EQ(EAGAIN, errno); ++ } ++} ++ ++/* Test 2: peer closes with unread data */ ++TEST_F(unix_sock, reset_unread_behavior) ++{ ++ char buf[16] = {}; ++ ssize_t n; ++ ++ /* Send data that will remain unread */ ++ send(self->client, "hello", 5, 0); ++ ++ if (variant->socket_type == SOCK_DGRAM) { ++ /* No real connection, just close the server */ ++ close(self->server); ++ } else { ++ self->child = accept(self->server, NULL, NULL); ++ ASSERT_LT(-1, self->child); ++ ++ /* Peer closes before client reads */ ++ close(self->child); ++ } ++ ++ n = recv(self->client, buf, sizeof(buf), 0); ++ ASSERT_EQ(-1, n); ++ ++ if (variant->socket_type == SOCK_STREAM || ++ variant->socket_type == SOCK_SEQPACKET) { ++ ASSERT_EQ(ECONNRESET, errno); ++ } else { ++ ASSERT_EQ(EAGAIN, errno); ++ } ++} ++ ++/* Test 3: closing unaccepted (embryo) server socket should reset client. */ ++TEST_F(unix_sock, reset_closed_embryo) ++{ ++ char buf[16] = {}; ++ ssize_t n; ++ ++ if (variant->socket_type == SOCK_DGRAM) ++ SKIP(return, "This test only applies to SOCK_STREAM and SOCK_SEQPACKET"); ++ ++ /* Close server without accept()ing */ ++ close(self->server); ++ ++ n = recv(self->client, buf, sizeof(buf), 0); ++ ++ ASSERT_EQ(-1, n); ++ ASSERT_EQ(ECONNRESET, errno); ++} ++ ++TEST_HARNESS_MAIN ++ +-- +2.53.0 + diff --git a/queue-6.18/selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch b/queue-6.18/selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch new file mode 100644 index 0000000000..a844fb3810 --- /dev/null +++ b/queue-6.18/selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch @@ -0,0 +1,42 @@ +From 27e2c8d668078157378a0c4f8f8bf0b53f15c8c5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 16:02:10 +0800 +Subject: selftests/lkdtm: rename STACKLEAK_ERASING to KSTACK_ERASE + +From: Haofeng Li + +[ Upstream commit b3a7aa9c0020ae549a0d4964867ff66d2bd61709 ] + +Commit 57fbad15c2ee ("stackleak: Rename STACKLEAK to KSTACK_ERASE") +renamed the LKDTM crash type and selftest configuration but missed the +entry in tests.txt. + +As a result, the selftest generates STACKLEAK_ERASING.sh, which run.sh +skips because the LKDTM DIRECT trigger only exposes KSTACK_ERASE. Rename +the test entry so the generated runner uses the registered crash type. + +Fixes: 57fbad15c2ee ("stackleak: Rename STACKLEAK to KSTACK_ERASE") +Signed-off-by: Haofeng Li +Link: https://patch.msgid.link/tencent_CD80B5F746B6AABD68AF3F1097AD02C96F05@qq.com +Signed-off-by: Kees Cook +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/lkdtm/tests.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt +index cff124c1eddd3..665a13a37363c 100644 +--- a/tools/testing/selftests/lkdtm/tests.txt ++++ b/tools/testing/selftests/lkdtm/tests.txt +@@ -74,7 +74,7 @@ USERCOPY_STACK_FRAME_TO + USERCOPY_STACK_FRAME_FROM + USERCOPY_STACK_BEYOND + USERCOPY_KERNEL +-STACKLEAK_ERASING OK: the rest of the thread stack is properly erased ++KSTACK_ERASE OK: the rest of the thread stack is properly erased + CFI_FORWARD_PROTO + CFI_BACKWARD call trace:|ok: control flow unchanged + FORTIFY_STRSCPY detected buffer overflow +-- +2.53.0 + diff --git a/queue-6.18/selftests-net-af_unix-test-listen-rejects-wrong-sock.patch b/queue-6.18/selftests-net-af_unix-test-listen-rejects-wrong-sock.patch new file mode 100644 index 0000000000..0cf3f3c7d7 --- /dev/null +++ b/queue-6.18/selftests-net-af_unix-test-listen-rejects-wrong-sock.patch @@ -0,0 +1,253 @@ +From e6e933ffdc705984f642563ddeba5a58068d20e0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 18 Jul 2026 14:29:02 -0400 +Subject: selftests/net/af_unix: test listen() rejects wrong socket states + +From: John Ericson + +[ Upstream commit 7f57c650d08b8793bb551bdb33ad876535ef9fe8 ] + +Add a regression test for the unix_listen() state check. The key case is +listen() on a bound socket that has already been connected: it is no +longer in TCP_CLOSE or TCP_LISTEN, so it must fail with EINVAL. A +prepare_peercred() call slipped in ahead of that check once left err at 0 +and made listen() silently succeed there instead; this guards against a +repeat. + +The neighbouring outcomes are covered too so they cannot regress the same +way: a bound socket in TCP_CLOSE listens fine, calling listen() again on a +socket already in TCP_LISTEN is allowed, and an unbound socket fails with +EINVAL. + +Each case runs for both listenable socket types (SOCK_STREAM and +SOCK_SEQPACKET) and both pathname and abstract addresses. + +Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid") +Signed-off-by: John Ericson +Link: https://patch.msgid.link/20260718182903.2295560-2-John.Ericson@Obsidian.Systems +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + .../testing/selftests/net/af_unix/.gitignore | 1 + + tools/testing/selftests/net/af_unix/Makefile | 1 + + .../selftests/net/af_unix/unix_listen.c | 187 ++++++++++++++++++ + 3 files changed, 189 insertions(+) + create mode 100644 tools/testing/selftests/net/af_unix/unix_listen.c + +diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore +index 240b26740c9e0..9731766441036 100644 +--- a/tools/testing/selftests/net/af_unix/.gitignore ++++ b/tools/testing/selftests/net/af_unix/.gitignore +@@ -6,3 +6,4 @@ scm_rights + so_peek_off + unix_connect + unix_connreset ++unix_listen +diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile +index 4c0375e28bbee..57d159803a3ab 100644 +--- a/tools/testing/selftests/net/af_unix/Makefile ++++ b/tools/testing/selftests/net/af_unix/Makefile +@@ -14,6 +14,7 @@ TEST_GEN_PROGS := \ + so_peek_off \ + unix_connect \ + unix_connreset \ ++ unix_listen \ + # end of TEST_GEN_PROGS + + include ../../lib.mk +diff --git a/tools/testing/selftests/net/af_unix/unix_listen.c b/tools/testing/selftests/net/af_unix/unix_listen.c +new file mode 100644 +index 0000000000000..416fa3e5bfe9b +--- /dev/null ++++ b/tools/testing/selftests/net/af_unix/unix_listen.c +@@ -0,0 +1,187 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Tests for the state checks in AF_UNIX listen(). ++ * ++ * The central case is a regression test: listen() on a bound socket that ++ * is already connected (i.e. not in TCP_CLOSE or TCP_LISTEN state) must ++ * fail with EINVAL. A prior change accidentally let it return success ++ * without doing anything, because a helper called in between reset the ++ * error code to 0. The neighbouring checks (unbound, already listening) ++ * are tested too so they cannot silently regress the same way. ++ * ++ * Every case runs for both listenable socket types (SOCK_STREAM and ++ * SOCK_SEQPACKET) and both pathname and abstract addresses. ++ */ ++#define _GNU_SOURCE ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "kselftest_harness.h" ++ ++#define SK_NAME "unix_listen_sk" ++#define SRV_NAME "unix_listen_srv" ++ ++FIXTURE(unix_listen) ++{ ++ int sk; /* socket under test */ ++ int server; /* a listening peer, when a test needs one */ ++ struct sockaddr_un addr, srv_addr; ++ socklen_t addrlen, srv_addrlen; ++}; ++ ++FIXTURE_VARIANT(unix_listen) ++{ ++ int type; ++ int abstract; ++}; ++ ++FIXTURE_VARIANT_ADD(unix_listen, stream_pathname) ++{ ++ .type = SOCK_STREAM, ++ .abstract = 0, ++}; ++ ++FIXTURE_VARIANT_ADD(unix_listen, stream_abstract) ++{ ++ .type = SOCK_STREAM, ++ .abstract = 1, ++}; ++ ++FIXTURE_VARIANT_ADD(unix_listen, seqpacket_pathname) ++{ ++ .type = SOCK_SEQPACKET, ++ .abstract = 0, ++}; ++ ++FIXTURE_VARIANT_ADD(unix_listen, seqpacket_abstract) ++{ ++ .type = SOCK_SEQPACKET, ++ .abstract = 1, ++}; ++ ++/* Fill @addr with a pathname or abstract address named @name. */ ++static socklen_t unix_set_addr(struct sockaddr_un *addr, const char *name, ++ int abstract) ++{ ++ size_t len = strlen(name); ++ ++ memset(addr, 0, sizeof(*addr)); ++ addr->sun_family = AF_UNIX; ++ /* An abstract address leads with a NUL and has no filesystem entry. */ ++ memcpy(addr->sun_path + (abstract ? 1 : 0), name, len); ++ ++ return offsetof(struct sockaddr_un, sun_path) + len + 1; ++} ++ ++FIXTURE_SETUP(unix_listen) ++{ ++ self->sk = -1; ++ self->server = -1; ++ self->addrlen = unix_set_addr(&self->addr, SK_NAME, variant->abstract); ++ self->srv_addrlen = unix_set_addr(&self->srv_addr, SRV_NAME, ++ variant->abstract); ++} ++ ++FIXTURE_TEARDOWN(unix_listen) ++{ ++ if (self->sk >= 0) ++ close(self->sk); ++ if (self->server >= 0) ++ close(self->server); ++ ++ /* Pathname sockets leave a filesystem entry behind; abstract ones do not. */ ++ if (!variant->abstract) { ++ remove(SK_NAME); ++ remove(SRV_NAME); ++ } ++} ++ ++/* A bound socket in TCP_CLOSE is the normal, allowed case. */ ++TEST_F(unix_listen, bound_is_ok) ++{ ++ int err; ++ ++ self->sk = socket(AF_UNIX, variant->type, 0); ++ ASSERT_LE(0, self->sk); ++ ++ err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); ++ ASSERT_EQ(0, err); ++ ++ err = listen(self->sk, 8); ++ EXPECT_EQ(0, err); ++} ++ ++/* Listening again on an already-listening socket (TCP_LISTEN) is allowed. */ ++TEST_F(unix_listen, relisten_is_ok) ++{ ++ int err; ++ ++ self->sk = socket(AF_UNIX, variant->type, 0); ++ ASSERT_LE(0, self->sk); ++ ++ err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); ++ ASSERT_EQ(0, err); ++ ++ err = listen(self->sk, 8); ++ ASSERT_EQ(0, err); ++ ++ err = listen(self->sk, 16); ++ EXPECT_EQ(0, err); ++} ++ ++/* listen() on an unbound socket fails: there is nothing to listen on. */ ++TEST_F(unix_listen, unbound_is_einval) ++{ ++ int err; ++ ++ self->sk = socket(AF_UNIX, variant->type, 0); ++ ASSERT_LE(0, self->sk); ++ ++ err = listen(self->sk, 8); ++ EXPECT_EQ(-1, err); ++ EXPECT_EQ(EINVAL, errno); ++} ++ ++/* ++ * The regression: a bound socket that has already been connected is not in ++ * TCP_CLOSE or TCP_LISTEN, so listen() must reject it with EINVAL rather ++ * than quietly succeeding. ++ */ ++TEST_F(unix_listen, connected_is_einval) ++{ ++ int err; ++ ++ self->server = socket(AF_UNIX, variant->type, 0); ++ ASSERT_LE(0, self->server); ++ ++ err = bind(self->server, (struct sockaddr *)&self->srv_addr, ++ self->srv_addrlen); ++ ASSERT_EQ(0, err); ++ ++ err = listen(self->server, 8); ++ ASSERT_EQ(0, err); ++ ++ self->sk = socket(AF_UNIX, variant->type, 0); ++ ASSERT_LE(0, self->sk); ++ ++ /* Bind first so the unbound check does not mask the state check. */ ++ err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); ++ ASSERT_EQ(0, err); ++ ++ err = connect(self->sk, (struct sockaddr *)&self->srv_addr, ++ self->srv_addrlen); ++ ASSERT_EQ(0, err); ++ ++ err = listen(self->sk, 8); ++ EXPECT_EQ(-1, err); ++ EXPECT_EQ(EINVAL, errno); ++} ++ ++TEST_HARNESS_MAIN +-- +2.53.0 + diff --git a/queue-6.18/selftests-seccomp-fix-pointer-type-mismatch-build-er.patch b/queue-6.18/selftests-seccomp-fix-pointer-type-mismatch-build-er.patch new file mode 100644 index 0000000000..a133fbdebc --- /dev/null +++ b/queue-6.18/selftests-seccomp-fix-pointer-type-mismatch-build-er.patch @@ -0,0 +1,57 @@ +From 8b280c9c66aa3ed4f83a67be337338f81641bf97 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 13:35:52 +0800 +Subject: selftests/seccomp: Fix pointer type mismatch build error +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Kuan-Ying Lee + +[ Upstream commit 3421b9b056a6576d0ebac1030eafb48ad0544092 ] + +We hit the following build error while running the seccomp selftests in +our testing. + +CC seccomp_bpf +seccomp_bpf.c: In function ‘UPROBE_setup’: +seccomp_bpf.c:5175:74: error: pointer type mismatch in conditional expression [-Wincompatible-pointer-types] +5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); +| ^ +seccomp_bpf.c:5175:57: note: first expression has type ‘int (*)(void)’ +5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); +| ^~~~~~~~~~~~~~~~ +seccomp_bpf.c:5175:76: note: second expression has type ‘int (__attribute__((nocf_check)) *)(void)’ +5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); +| ^~~~~~~~~~~~~ + +get_uprobe_offset() takes a 'const void *' argument, so cast both +operands to 'void *'. + +Fixes: 9ffc7a635c35 ("selftests/seccomp: validate uprobe syscall passes through seccomp") +Signed-off-by: Kuan-Ying Lee +Acked-by: Jiri Olsa +Link: https://patch.msgid.link/20260715053559.28535-1-kuan-ying.lee@canonical.com +Signed-off-by: Kees Cook +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/seccomp/seccomp_bpf.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c +index 874f17763536b..19c9afa0be719 100644 +--- a/tools/testing/selftests/seccomp/seccomp_bpf.c ++++ b/tools/testing/selftests/seccomp/seccomp_bpf.c +@@ -5172,7 +5172,8 @@ FIXTURE_SETUP(UPROBE) + ASSERT_GE(bit, 0); + } + +- offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); ++ offset = get_uprobe_offset(variant->uretprobe ? (void *)probed_uretprobe ++ : (void *)probed_uprobe); + ASSERT_GE(offset, 0); + + if (variant->uretprobe) +-- +2.53.0 + diff --git a/queue-6.18/series b/queue-6.18/series index aca90a1fee..9969c904ff 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -11,3 +11,149 @@ hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch +pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch +pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch +dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch +dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch +iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch +gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch +selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch +selftests-seccomp-fix-pointer-type-mismatch-build-er.patch +ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch +ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch +phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch +asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch +asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch +of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch +btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch +btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch +btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch +btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch +phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch +phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch +netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch +drm-mediatek-check-crtc-state-before-freeing.patch +arch-x86-mshyperv-discover-confidential-vmbus-availa.patch +drivers-hv-rename-fields-for-synic-message-and-event.patch +drivers-hv-allocate-the-paravisor-synic-pages-when-r.patch +drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch +mshv-fix-duplicate-gsi-detection-for-gsi-0.patch +mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch +kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch +keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch +keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch +keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch +assoc_array-trim-the-final-shortcut-word-using-the-c.patch +netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch +netfilter-xt_hashlimit-validate-hashtable-supports-x.patch +ipvs-fix-the-checksum-validations.patch +ipvs-fix-places-with-wrong-packet-offsets.patch +ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch +netfilter-nft_payload-fix-mask-build-for-partial-fie.patch +asoc-sdca-ensure-that-control-range-is-large-enough-.patch +rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch +rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch +af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch +selftests-af_unix-add-tests-for-econnreset-and-eof-s.patch +selftest-af_unix-create-its-own-.gitignore.patch +selftests-net-af_unix-test-listen-rejects-wrong-sock.patch +xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch +xsk-use-a-smaller-new-lock-for-shared-pool-case.patch +xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch +pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch +scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch +scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch +scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch +smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch +asoc-tas2781-use-correct-calibration-data-for-sinega.patch +spi-spi-cadence-supports-transmission-with-bits_per_.patch +spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch +hwmon-nct6775-core-fix-number-of-temperature-registe.patch +hwmon-ina2xx-make-it-easier-to-add-more-devices.patch +hwmon-ina2xx-add-support-for-ina234.patch +hwmon-ina2xx-shift-ina234-shunt-and-current-register.patch +hwmon-ina2xx-fix-various-overflow-issues.patch +hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch +hwmon-sht3x-fix-unaligned-accesses.patch +hwmon-lm90-only-report-alarms-if-driver-is-ready.patch +hwmon-nzxt-smart2-dma-align-output-buffer.patch +net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch +hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch +net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch +forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch +hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch +hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch +hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch +hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch +hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch +hwmon-adt7470-use-cached-pwm-frequency-value.patch +hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch +hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch +rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch +powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch +powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch +powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch +net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch +wifi-mac80211-validate-individual-twt-params-before-.patch +netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch +netfs-handle-single-writeback-rolling-buffer-allocat.patch +netfs-release-readahead-folios-on-iterator-preparati.patch +net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch +hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch +idpf-adjust-txq-ring-count-minimum.patch +idpf-fix-mailbox-irq-name-leak-on-request-failure.patch +ice-suppress-dpll-errors-during-reset-recovery.patch +bluetooth-iso-clear-iso_data-always-when-detaching-c.patch +bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch +bluetooth-iso-lock-sk-in-iso_sock_getname.patch +bluetooth-hci-add-initial-support-for-past.patch +bluetooth-iso-fix-data-race-on-iso_pi-sk-in-socket-a.patch +bluetooth-iso-lock-sk-in-iso_connect_ind.patch +bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch +bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch +bluetooth-iso-fix-not-updating-bis-sender-source-add.patch +bluetooth-iso-fix-connected-closed-transition-on-shu.patch +bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch +bluetooth-iso-fix-leaking-sk-after-socket-release.patch +bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch +bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch +bluetooth-iso-fix-refcounting-of-iso_conn.patch +bluetooth-btintel-validate-length-before-parsing-dia.patch +bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch +bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch +bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch +bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch +x86-boot-add-volatile-clobbers-and-zero-length-test-.patch +net-phylink-put-link_gpio-if-phylink_create-fails.patch +scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch +scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch +scsi-ufs-core-avoid-irq-thread-wakeup-during-active-.patch +scsi-ufs-core-revert-delegate-the-interrupt-service-.patch +scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch +scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch +net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch +net-sxgbe-check-descriptor-ring-allocation-failures.patch +can-isotp-check-register_netdevice_notifier-error-in.patch +drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch +fprobe-fix-module-reference-count-leak-on-error-in-r.patch +tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch +tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch +riscv-drop-__init-from-vec_check_unaligned_access_sp.patch +accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch +riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch +net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch +net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch +ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch +net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch +octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch +sched-deadline-use-revised-wakeup-rule-only-for-runn.patch +spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch +qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch +drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch +ksmbd-return-success-for-deferred-final-close.patch +ksmbd-fix-use-after-free-in-__close_file_table_ids.patch +iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch +mshv-fix-race-in-mshv_irqfd_deassign.patch +mshv-adjust-interrupt-control-structure-for-arm64.patch +mshv-fix-level-triggered-check-on-uninitialized-data.patch +mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch diff --git a/queue-6.18/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-6.18/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch new file mode 100644 index 0000000000..9c2fb6f7d7 --- /dev/null +++ b/queue-6.18/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch @@ -0,0 +1,85 @@ +From 3dd1d2ed8c1807dfb94dd90e2fd4600664e39e4b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 28 Jun 2026 14:59:09 +0800 +Subject: smb: client: fix buffer leaks in SMB1 read and write + +From: Dawei Feng + +[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ] + +CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request +buffer before checking whether tcon->ses->server is NULL. If that +defensive check ever fails, the helper returns -ECONNABORTED without +releasing the request buffer. + +Fix these leaks by releasing the allocated request buffer before +returning from these error paths. Use cifs_small_buf_release() for the +buffers allocated by small_smb_init() and cifs_buf_release() for the +buffer allocated by smb_init(). + +The bug was first flagged by an experimental analysis tool we are +developing for kernel memory-management bugs while analyzing +v6.13-rc1. The tool is still under development and is not yet publicly +available. Manual inspection confirms that the bug is still +present in v7.1.1. + +An x86_64 allyesconfig build showed no new warnings. + +Runtime validation used a temporary fault-injection hook to force +tcon->ses->server to NULL after request-buffer initialization. On the +unfixed kernel, the harness observed two leaked small request buffers and +one leaked large request buffer, with directed kmemleak dumps confirming +the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer +deltas remained. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Signed-off-by: Dawei Feng +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/client/cifssmb.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c +index 7694ca283fdc3..db9d9a1983c73 100644 +--- a/fs/smb/client/cifssmb.c ++++ b/fs/smb/client/cifssmb.c +@@ -1515,8 +1515,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -1628,8 +1630,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -1908,8 +1912,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +-- +2.53.0 + diff --git a/queue-6.18/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch b/queue-6.18/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch new file mode 100644 index 0000000000..48dbfe22b5 --- /dev/null +++ b/queue-6.18/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch @@ -0,0 +1,111 @@ +From 7eb6f327258307f67000105d044d953e68bb7d4c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 18:25:10 +0530 +Subject: spi: spi-cadence: Move TX FIFO full busy-wait into FIFO +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Srikanth Boyapally + +[ Upstream commit d9eadfce2fac49445db40808fe4d8259f20a9d2b ] + +SPI host transfers could intermittently stall with spi_transfer timeouts. +The TXFULL condition was checked only once in cdns_transfer_one() before +cdns_spi_process_fifo(), so if the FIFO became full again during refill, +writes could be dropped and the transfer would never complete. + +Move the TXFULL busy-wait into the TX path of cdns_spi_process_fifo() so +the 10µs back-off is applied per FIFO entry during filling, ensuring +forward progress and eliminating spurious timeouts. + +Restrict the delay to host mode using spi_controller_is_target(), the +controller is passed into cdns_spi_process_fifo() so the check is made at +the point of use. In target mode this delay must not run as it causes the +target to miss its transfer window and corrupt data. + +Fixes: 49530e641178 ("spi: cadence: Add usleep_range() for cdns_spi_fill_tx_fifo()") +Signed-off-by: Srikanth Boyapally +Reviewed-by: Radhey Shyam Pandey +Link: https://patch.msgid.link/20260720125510.60166-1-srikanth.boyapally@amd.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-cadence.c | 26 ++++++++++++++++---------- + 1 file changed, 16 insertions(+), 10 deletions(-) + +diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c +index 75803a7f4cb11..403e8141dff89 100644 +--- a/drivers/spi/spi-cadence.c ++++ b/drivers/spi/spi-cadence.c +@@ -388,11 +388,13 @@ static inline void cdns_spi_writer(struct cdns_spi *xspi) + + /** + * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO ++ * @ctlr: Pointer to the spi_controller structure + * @xspi: Pointer to the cdns_spi structure + * @ntx: Number of bytes to pack into the TX FIFO + * @nrx: Number of bytes to drain from the RX FIFO + */ +-static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) ++static void cdns_spi_process_fifo(struct spi_controller *ctlr, ++ struct cdns_spi *xspi, int ntx, int nrx) + { + ntx = clamp(ntx, 0, xspi->tx_bytes); + nrx = clamp(nrx, 0, xspi->rx_bytes); +@@ -407,6 +409,16 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) + } + + if (ntx) { ++ /* When xspi in busy condition, bytes may send failed, ++ * then spi control didn't work thoroughly, add one byte ++ * delay. Only in host mode; in target mode this delay ++ * causes data corruption as the target fails to prepare ++ * data in time. ++ */ ++ if (!spi_controller_is_target(ctlr) && ++ (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)) ++ udelay(10); ++ + cdns_spi_writer(xspi); + ntx--; + } +@@ -460,14 +472,14 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id) + cdns_spi_write(xspi, CDNS_SPI_THLD, 1); + + if (xspi->tx_bytes) { +- cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt); ++ cdns_spi_process_fifo(ctlr, xspi, trans_cnt, trans_cnt); + } else { + /* Fixed delay due to controller limitation with + * RX_NEMPTY incorrect status + * Xilinx AR:65885 contains more details + */ + udelay(10); +- cdns_spi_process_fifo(xspi, 0, trans_cnt); ++ cdns_spi_process_fifo(ctlr, xspi, 0, trans_cnt); + cdns_spi_write(xspi, CDNS_SPI_IDR, + CDNS_SPI_IXR_DEFAULT); + spi_finalize_current_transfer(ctlr); +@@ -520,17 +532,11 @@ static int cdns_transfer_one(struct spi_controller *ctlr, + cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1); + } + +- /* When xspi in busy condition, bytes may send failed, +- * then spi control didn't work thoroughly, add one byte delay +- */ +- if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) +- udelay(10); +- + xspi->n_bytes = cdns_spi_n_bytes(transfer); + xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes); + xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes); + +- cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0); ++ cdns_spi_process_fifo(ctlr, xspi, xspi->tx_fifo_depth, 0); + + cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); + return transfer->len; +-- +2.53.0 + diff --git a/queue-6.18/spi-spi-cadence-supports-transmission-with-bits_per_.patch b/queue-6.18/spi-spi-cadence-supports-transmission-with-bits_per_.patch new file mode 100644 index 0000000000..1e24cd78d0 --- /dev/null +++ b/queue-6.18/spi-spi-cadence-supports-transmission-with-bits_per_.patch @@ -0,0 +1,200 @@ +From 3a6c0b176e5b4cf6ab7a71262a06ccf9850722b9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 31 Oct 2025 15:30:02 +0800 +Subject: spi: spi-cadence: supports transmission with bits_per_word of 16 and + 32 + +From: Jun Guo + +[ Upstream commit 4e00135b2dd1d7924a58bffa551b6ceb3bd836f2 ] + +The default FIFO data width of the Cadence SPI IP is 8 bits, but +the hardware supports configurations of 16 bits and 32 bits. +This patch enhances the driver to support communication with both +16-bits and 32-bits FIFO data widths. + +Signed-off-by: Jun Guo +Link: https://patch.msgid.link/20251031073003.3289573-3-jun.guo@cixtech.com +Signed-off-by: Mark Brown +Stable-dep-of: d9eadfce2fac ("spi: spi-cadence: Move TX FIFO full busy-wait into FIFO") +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-cadence.c | 106 +++++++++++++++++++++++++++++++++----- + 1 file changed, 93 insertions(+), 13 deletions(-) + +diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c +index 7a745c2e0892c..75803a7f4cb11 100644 +--- a/drivers/spi/spi-cadence.c ++++ b/drivers/spi/spi-cadence.c +@@ -109,6 +109,7 @@ + * @rxbuf: Pointer to the RX buffer + * @tx_bytes: Number of bytes left to transfer + * @rx_bytes: Number of bytes requested ++ * @n_bytes: Number of bytes per word + * @dev_busy: Device busy flag + * @is_decoded_cs: Flag for decoder property set or not + * @tx_fifo_depth: Depth of the TX FIFO +@@ -120,16 +121,24 @@ struct cdns_spi { + struct clk *pclk; + unsigned int clk_rate; + u32 speed_hz; +- const u8 *txbuf; +- u8 *rxbuf; ++ const void *txbuf; ++ void *rxbuf; + int tx_bytes; + int rx_bytes; ++ u8 n_bytes; + u8 dev_busy; + u32 is_decoded_cs; + unsigned int tx_fifo_depth; + struct reset_control *rstc; + }; + ++enum cdns_spi_frame_n_bytes { ++ CDNS_SPI_N_BYTES_NULL = 0, ++ CDNS_SPI_N_BYTES_U8 = 1, ++ CDNS_SPI_N_BYTES_U16 = 2, ++ CDNS_SPI_N_BYTES_U32 = 4 ++}; ++ + /* Macros for the SPI controller read/write */ + static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset) + { +@@ -305,6 +314,78 @@ static int cdns_spi_setup_transfer(struct spi_device *spi, + return 0; + } + ++static u8 cdns_spi_n_bytes(struct spi_transfer *transfer) ++{ ++ if (transfer->bits_per_word <= 8) ++ return CDNS_SPI_N_BYTES_U8; ++ else if (transfer->bits_per_word <= 16) ++ return CDNS_SPI_N_BYTES_U16; ++ else ++ return CDNS_SPI_N_BYTES_U32; ++} ++ ++static inline void cdns_spi_reader(struct cdns_spi *xspi) ++{ ++ u32 rxw = 0; ++ ++ if (xspi->rxbuf && !IS_ALIGNED((uintptr_t)xspi->rxbuf, xspi->n_bytes)) { ++ pr_err("%s: rxbuf address is not aligned for %d bytes\n", ++ __func__, xspi->n_bytes); ++ return; ++ } ++ ++ rxw = cdns_spi_read(xspi, CDNS_SPI_RXD); ++ if (xspi->rxbuf) { ++ switch (xspi->n_bytes) { ++ case CDNS_SPI_N_BYTES_U8: ++ *(u8 *)xspi->rxbuf = rxw; ++ break; ++ case CDNS_SPI_N_BYTES_U16: ++ *(u16 *)xspi->rxbuf = rxw; ++ break; ++ case CDNS_SPI_N_BYTES_U32: ++ *(u32 *)xspi->rxbuf = rxw; ++ break; ++ default: ++ pr_err("%s invalid n_bytes %d\n", __func__, ++ xspi->n_bytes); ++ return; ++ } ++ xspi->rxbuf = (u8 *)xspi->rxbuf + xspi->n_bytes; ++ } ++} ++ ++static inline void cdns_spi_writer(struct cdns_spi *xspi) ++{ ++ u32 txw = 0; ++ ++ if (xspi->txbuf && !IS_ALIGNED((uintptr_t)xspi->txbuf, xspi->n_bytes)) { ++ pr_err("%s: txbuf address is not aligned for %d bytes\n", ++ __func__, xspi->n_bytes); ++ return; ++ } ++ ++ if (xspi->txbuf) { ++ switch (xspi->n_bytes) { ++ case CDNS_SPI_N_BYTES_U8: ++ txw = *(u8 *)xspi->txbuf; ++ break; ++ case CDNS_SPI_N_BYTES_U16: ++ txw = *(u16 *)xspi->txbuf; ++ break; ++ case CDNS_SPI_N_BYTES_U32: ++ txw = *(u32 *)xspi->txbuf; ++ break; ++ default: ++ pr_err("%s invalid n_bytes %d\n", __func__, ++ xspi->n_bytes); ++ return; ++ } ++ cdns_spi_write(xspi, CDNS_SPI_TXD, txw); ++ xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes; ++ } ++} ++ + /** + * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO + * @xspi: Pointer to the cdns_spi structure +@@ -321,23 +402,14 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) + + while (ntx || nrx) { + if (nrx) { +- u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD); +- +- if (xspi->rxbuf) +- *xspi->rxbuf++ = data; +- ++ cdns_spi_reader(xspi); + nrx--; + } + + if (ntx) { +- if (xspi->txbuf) +- cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++); +- else +- cdns_spi_write(xspi, CDNS_SPI_TXD, 0); +- ++ cdns_spi_writer(xspi); + ntx--; + } +- + } + } + +@@ -454,6 +526,10 @@ static int cdns_transfer_one(struct spi_controller *ctlr, + if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) + udelay(10); + ++ xspi->n_bytes = cdns_spi_n_bytes(transfer); ++ xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes); ++ xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes); ++ + cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0); + + cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); +@@ -654,6 +730,9 @@ static int cdns_spi_probe(struct platform_device *pdev) + ctlr->mode_bits = SPI_CPOL | SPI_CPHA; + ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + ++ if (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6")) ++ ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32); ++ + if (!spi_controller_is_target(ctlr)) { + ctlr->mode_bits |= SPI_CS_HIGH; + ctlr->set_cs = cdns_spi_chipselect; +@@ -812,6 +891,7 @@ static const struct dev_pm_ops cdns_spi_dev_pm_ops = { + + static const struct of_device_id cdns_spi_of_match[] = { + { .compatible = "xlnx,zynq-spi-r1p6" }, ++ { .compatible = "cix,sky1-spi-r1p6" }, + { .compatible = "cdns,spi-r1p6" }, + { /* end of table */ } + }; +-- +2.53.0 + diff --git a/queue-6.18/spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch b/queue-6.18/spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch new file mode 100644 index 0000000000..f334418d6b --- /dev/null +++ b/queue-6.18/spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch @@ -0,0 +1,197 @@ +From 6abf8c5ac564f93d22f22f1ea594ac49f17a300d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 18:18:08 +0800 +Subject: spi: spi-nxp-fspi: add per-SoC SDR/DTR clock rate limits for all + supported SoCs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Haibo Chen + +[ Upstream commit 9c19d60fea9f46ed0c3394653ef4941f1a459968 ] + +The commit f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for +different sample clock source selection") introduced a global 166MHz +cap for DTR mode (RXCLKSRC=3), based on the i.MX8MN datasheet timing +specification (Section 3.9.9, page 65). + +After reviewing the FlexSPI timing parameters in the datasheets for all +supported SoCs, the following corrections and additions are needed: + +1. SDR mode (RXCLKSRC=0) limits vary per SoC: + - i.MX8MN/MM/MP/95: 66MHz (IMX8MNCEC §3.9.9, IMX8MMCEC §3.9.10, + IMX8MPCEC, IMX95CEC Rev.8 §4.11.7) + - i.MX8QXP/QM/DXL/ULP: 60MHz (IMX8QXPCEC, IMX8QMCEC, IMX8DXLCEC, + IMX8ULPCEC §7.3.1 ND mode) + - LX2160A: 100MHz (LX2160ACEC FlexSPI timing parameters) + +2. DTR mode (RXCLKSRC=3) limits vary per SoC: + - i.MX8MN/MM/MP/ULP: 166MHz + - i.MX8QXP/QM/DXL: 200MHz (same FlexSPI IP across this family) + - i.MX95: 200MHz (IMX95CEC §4.11.7.3.2.3 Table 106) + - LX2160A: DTR disabled (FSPI_QUIRK_DISABLE_DTR) + +Update related platform data with correct speed limation according +to datasheet. + +Fixes: f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for different sample clock source selection") +Signed-off-by: Haibo Chen +Link: https://patch.msgid.link/20260728-fspi-clock-v2-1-dbe786a4a6eb@nxp.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-nxp-fspi.c | 83 ++++++++++++++++++++++++++++++++++++-- + 1 file changed, 80 insertions(+), 3 deletions(-) + +diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c +index 95ccb1f7dfafa..b504a5c86692a 100644 +--- a/drivers/spi/spi-nxp-fspi.c ++++ b/drivers/spi/spi-nxp-fspi.c +@@ -340,6 +340,18 @@ struct nxp_fspi_devtype_data { + unsigned int quirks; + unsigned int lut_num; + bool little_endian; ++ /* ++ * The max clock rate (Hz) that FlexSPI can output to the device ++ * in SDR mode (RXCLKSRC=0). Defaults to 66MHz if zero. ++ * Some SoCs (e.g. LX2160A) support up to 100MHz in SDR mode. ++ */ ++ unsigned long max_sdr_rate; ++ /* ++ * The max clock rate (Hz) that FlexSPI can output to the device ++ * in DTR mode (RXCLKSRC=3). Defaults to 166MHz if zero. ++ * Some SoCs (e.g. i.MX95, i.MX8QM, i.MX8DXL) support up to 200MHz. ++ */ ++ unsigned long max_dtr_rate; + }; + + static struct nxp_fspi_devtype_data lx2160a_data = { +@@ -349,6 +361,10 @@ static struct nxp_fspi_devtype_data lx2160a_data = { + .quirks = FSPI_QUIRK_DISABLE_DTR, + .lut_num = 32, + .little_endian = true, /* little-endian */ ++ /* ++ * LX2160ACEC: SDR RXCLKSRC=0 max 100MHz, DTR disabled via quirk. ++ */ ++ .max_sdr_rate = 100000000, + }; + + static struct nxp_fspi_devtype_data imx8mm_data = { +@@ -358,6 +374,21 @@ static struct nxp_fspi_devtype_data imx8mm_data = { + .quirks = 0, + .lut_num = 32, + .little_endian = true, /* little-endian */ ++ /* IMX8MMCEC §3.9.10: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */ ++ .max_sdr_rate = 66000000, ++ .max_dtr_rate = 166000000, ++}; ++ ++static struct nxp_fspi_devtype_data imx8mp_data = { ++ .rxfifo = SZ_512, /* (64 * 64 bits) */ ++ .txfifo = SZ_1K, /* (128 * 64 bits) */ ++ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ ++ .quirks = 0, ++ .lut_num = 32, ++ .little_endian = true, /* little-endian */ ++ /* IMX8MPCEC: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */ ++ .max_sdr_rate = 66000000, ++ .max_dtr_rate = 166000000, + }; + + static struct nxp_fspi_devtype_data imx8qxp_data = { +@@ -367,6 +398,12 @@ static struct nxp_fspi_devtype_data imx8qxp_data = { + .quirks = 0, + .lut_num = 32, + .little_endian = true, /* little-endian */ ++ /* ++ * IMX8QXPCEC: SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 200MHz. ++ * i.MX8QM and i.MX8DXL share the same FlexSPI IP and limits. ++ */ ++ .max_sdr_rate = 60000000, ++ .max_dtr_rate = 200000000, + }; + + static struct nxp_fspi_devtype_data imx8dxl_data = { +@@ -376,6 +413,12 @@ static struct nxp_fspi_devtype_data imx8dxl_data = { + .quirks = FSPI_QUIRK_USE_IP_ONLY, + .lut_num = 32, + .little_endian = true, /* little-endian */ ++ /* ++ * IMX8DXLCEC (i.MX 8XLite): SDR RXCLKSRC=0 max 60MHz, ++ * DDR RXCLKSRC=3 max 200MHz. ++ */ ++ .max_sdr_rate = 60000000, ++ .max_dtr_rate = 200000000, + }; + + static struct nxp_fspi_devtype_data imx8ulp_data = { +@@ -385,6 +428,29 @@ static struct nxp_fspi_devtype_data imx8ulp_data = { + .quirks = 0, + .lut_num = 16, + .little_endian = true, /* little-endian */ ++ /* ++ * IMX8ULPCEC §7.3.1, Normal Drive (ND, 1.0V) mode: ++ * SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 166MHz. ++ * Note: Overdrive (OD, 1.05V) allows up to 180MHz DTR ++ * but is not the default use case. ++ */ ++ .max_sdr_rate = 60000000, ++ .max_dtr_rate = 166000000, ++}; ++ ++static struct nxp_fspi_devtype_data imx95_data = { ++ .rxfifo = SZ_512, /* (64 * 64 bits) */ ++ .txfifo = SZ_1K, /* (128 * 64 bits) */ ++ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ ++ .quirks = 0, ++ .lut_num = 32, ++ .little_endian = true, /* little-endian */ ++ /* ++ * IMX95CEC Rev.8 §4.11.7: SDR RXCLKSRC=0 max 66MHz, ++ * DDR RXCLKSRC=3 max 200MHz (Nominal/Overdrive mode). ++ */ ++ .max_sdr_rate = 66000000, ++ .max_dtr_rate = 200000000, + }; + + struct nxp_fspi { +@@ -691,10 +757,20 @@ static void nxp_fspi_select_rx_sample_clk_source(struct nxp_fspi *f, + reg = fspi_readl(f, f->iobase + FSPI_MCR0); + if (op_is_dtr) { + reg |= FSPI_MCR0_RXCLKSRC(3); +- f->max_rate = 166000000; ++ /* ++ * Use the SoC-specific DTR max rate if provided, otherwise ++ * fall back to 166MHz (limit from IMX8MN datasheet §3.9.9). ++ */ ++ f->max_rate = f->devtype_data->max_dtr_rate ? ++ f->devtype_data->max_dtr_rate : 166000000; + } else { /*select mode 0 */ + reg &= ~FSPI_MCR0_RXCLKSRC(3); +- f->max_rate = 66000000; ++ /* ++ * Use the SoC-specific SDR max rate if provided, otherwise ++ * fall back to 66MHz (limit from IMX8MN datasheet §3.9.9). ++ */ ++ f->max_rate = f->devtype_data->max_sdr_rate ? ++ f->devtype_data->max_sdr_rate : 66000000; + } + fspi_writel(f, reg, f->iobase + FSPI_MCR0); + } +@@ -1446,10 +1522,11 @@ static const struct dev_pm_ops nxp_fspi_pm_ops = { + static const struct of_device_id nxp_fspi_dt_ids[] = { + { .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, }, + { .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, }, +- { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mm_data, }, ++ { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mp_data, }, + { .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, }, + { .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, }, + { .compatible = "nxp,imx8ulp-fspi", .data = (void *)&imx8ulp_data, }, ++ { .compatible = "nxp,imx95-fspi", .data = (void *)&imx95_data, }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids); +-- +2.53.0 + diff --git a/queue-6.18/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch b/queue-6.18/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch new file mode 100644 index 0000000000..51c7be01aa --- /dev/null +++ b/queue-6.18/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch @@ -0,0 +1,70 @@ +From e15976c4b381bb1ff44850bd2027ac8c7b5e9e49 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 21:50:00 +0900 +Subject: tracing/mmiotrace: Add NULL check for mmio_trace_array in logging + functions + +From: Masami Hiramatsu (Google) + +[ Upstream commit 12b80cdbc54cf615b4717a4e8180063408091ea2 ] + +mmio_trace_rw() and mmio_trace_mapping() retrieve mmio_trace_array into +tr and pass it to __trace_mmiotrace_rw() and __trace_mmiotrace_map(). +If these functions are invoked while mmio_trace_array is NULL (e.g. before +initialization or after disabled), accessing tr->array_buffer.buffer will +result in a NULL pointer dereference crash. + +Fix this by adding an explicit NULL check for tr at the beginning of +__trace_mmiotrace_rw() and __trace_mmiotrace_map(). + +Link: https://patch.msgid.link/178524300062.56416.8362487250709962380.stgit@devnote2 +Fixes: f984b51e0779 ("ftrace: add mmiotrace plugin") +Assisted-by: Antigravity:gemini-3.6-flash +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_mmiotrace.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c +index e16b15d7c84aa..32b7e1d972a47 100644 +--- a/kernel/trace/trace_mmiotrace.c ++++ b/kernel/trace/trace_mmiotrace.c +@@ -294,11 +294,15 @@ device_initcall(init_mmio_trace); + static void __trace_mmiotrace_rw(struct trace_array *tr, + struct mmiotrace_rw *rw) + { +- struct trace_buffer *buffer = tr->array_buffer.buffer; ++ struct trace_buffer *buffer; + struct ring_buffer_event *event; + struct trace_mmiotrace_rw *entry; + unsigned int trace_ctx; + ++ if (!tr) ++ return; ++ ++ buffer = tr->array_buffer.buffer; + trace_ctx = tracing_gen_ctx_flags(0); + event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW, + sizeof(*entry), trace_ctx); +@@ -321,11 +325,15 @@ void mmio_trace_rw(struct mmiotrace_rw *rw) + static void __trace_mmiotrace_map(struct trace_array *tr, + struct mmiotrace_map *map) + { +- struct trace_buffer *buffer = tr->array_buffer.buffer; ++ struct trace_buffer *buffer; + struct ring_buffer_event *event; + struct trace_mmiotrace_map *entry; + unsigned int trace_ctx; + ++ if (!tr) ++ return; ++ ++ buffer = tr->array_buffer.buffer; + trace_ctx = tracing_gen_ctx_flags(0); + event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP, + sizeof(*entry), trace_ctx); +-- +2.53.0 + diff --git a/queue-6.18/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-6.18/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch new file mode 100644 index 0000000000..a13c06129d --- /dev/null +++ b/queue-6.18/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch @@ -0,0 +1,43 @@ +From 1a6fa17180dc1c6fef68d5ed07fcdf40e414860a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 21:49:51 +0900 +Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data() + +From: Masami Hiramatsu (Google) + +[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ] + +mmio_reset_data() is called during tracer initialization, reset, and +start. While it resets overrun_detected and prev_overruns, it neglects +to reset dropped_count. Consequently, dropped event counts from prior +tracing sessions persist in dropped_count and corrupt overrun reports +in subsequent runs. + +Fix this by explicitly calling atomic_set(&dropped_count, 0) in +mmio_reset_data(). + +Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2 +Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording") +Assisted-by: Antigravity:gemini-3.6-flash +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_mmiotrace.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c +index dc8a1eafbe574..e16b15d7c84aa 100644 +--- a/kernel/trace/trace_mmiotrace.c ++++ b/kernel/trace/trace_mmiotrace.c +@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr) + { + overrun_detected = false; + prev_overruns = 0; ++ atomic_set(&dropped_count, 0); + + tracing_reset_online_cpus(&tr->array_buffer); + } +-- +2.53.0 + diff --git a/queue-6.18/wifi-mac80211-validate-individual-twt-params-before-.patch b/queue-6.18/wifi-mac80211-validate-individual-twt-params-before-.patch new file mode 100644 index 0000000000..b14ff47660 --- /dev/null +++ b/queue-6.18/wifi-mac80211-validate-individual-twt-params-before-.patch @@ -0,0 +1,52 @@ +From b40b87bee9c2ab1cdd77f70d9b37631b432b93fc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 09:09:28 +0800 +Subject: wifi: mac80211: validate individual TWT params before driver setup + +From: Zhao Li + +[ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ] + +ieee80211_process_rx_twt_action() only partially validates a received +S1G TWT setup frame before queueing it. + +An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup() +with twt->length too short for the full struct ieee80211_twt_params. + +The individual path passes twt to drv_add_twt_setup(). Both the tracepoint +and the driver callback consume the complete parameters block, not merely +req_type. Do not pass a short individual agreement to the driver. +Broadcast agreements remain unchanged because they are rejected locally +after accessing only req_type. + +Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode") +Assisted-by: Codex:gpt-5 +Assisted-by: Claude:opus-4.8 +Signed-off-by: Zhao Li +Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com +[edit commit message to not overclaim lack of validation nor + understate driver impact] +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/s1g.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c +index 1f68df6e80670..7702cff84d546 100644 +--- a/net/mac80211/s1g.c ++++ b/net/mac80211/s1g.c +@@ -101,6 +101,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata, + struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable; + struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + ++ if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) && ++ twt->length < sizeof(twt->control) + sizeof(*twt_agrt)) ++ return; ++ + twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST); + + /* broadcast TWT not supported yet */ +-- +2.53.0 + diff --git a/queue-6.18/x86-boot-add-volatile-clobbers-and-zero-length-test-.patch b/queue-6.18/x86-boot-add-volatile-clobbers-and-zero-length-test-.patch new file mode 100644 index 0000000000..3c0a06b76d --- /dev/null +++ b/queue-6.18/x86-boot-add-volatile-clobbers-and-zero-length-test-.patch @@ -0,0 +1,54 @@ +From 0b77e35c536802d17ecf8a832c5072ebb9745796 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 20:08:04 -0300 +Subject: x86/boot: Add volatile, clobbers and zero-length test in memcmp() + +From: Mauricio Faria de Oliveira + +[ Upstream commit a8c171c107c0b61a5e7e10cedab0fb72aeaf640d ] + +Add the volatile qualifier and clobbers parameter to prevent bugs with +instruction reordering and optimization. + +Also add TEST for the zero-length case to set ZF, as, if the count register +is zero, the REPE prefix does not run the CMPSB instruction, leaving the ZF +flag undetermined. + + [ bp: Add a comment about the len==0 case. ] + +Fixes: 62bd0337d0c4 ("Top header file for new x86 setup code") +Closes: https://sashiko.dev/#/patchset/20260701-pvh-kasan-inline-v6-0-ba99045dfa9f%40igalia.com +Suggested-by: Borislav Petkov +Signed-off-by: Mauricio Faria de Oliveira +Signed-off-by: Borislav Petkov (AMD) +Link: https://lore.kernel.org/all/20260721-pvh-kasan-inline-v7-2-38979a50cef0@igalia.com +Signed-off-by: Sasha Levin +--- + arch/x86/boot/string.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c +index b25c6a9303b73..3a2bba7c25e9d 100644 +--- a/arch/x86/boot/string.c ++++ b/arch/x86/boot/string.c +@@ -32,8 +32,15 @@ + int memcmp(const void *s1, const void *s2, size_t len) + { + bool diff; +- asm("repe cmpsb" +- : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len)); ++ ++ /* ++ * Make sure ZF is properly set in the len==0 case because in it, ++ * RCX==0 and the REPE; CMPSB won't get executed. ++ */ ++ asm volatile("test %3, %3\n\t" ++ "repe cmpsb" ++ : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len) ++ : : "cc", "memory"); + return diff; + } + +-- +2.53.0 + diff --git a/queue-6.18/xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch b/queue-6.18/xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch new file mode 100644 index 0000000000..fe0610a667 --- /dev/null +++ b/queue-6.18/xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch @@ -0,0 +1,144 @@ +From 151f8bb7e7ef893b15830d5e8e9fed4c947ae401 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 15:56:05 +0200 +Subject: xsk: drain continuation descs after overflow in xsk_build_skb() + +From: Jason Xing + +[ Upstream commit bd44a6dcd4248883de90f5dad53ae80066e27096 ] + +Fix generic xmit path multi-buffer logic when packets are either too big +(count of descriptors exceed MAX_SKB_FRAGS) or an invalid descriptor is +included in fragmented packet. Introduce xdp_sock::drain_cont and act +upon this flag - when it is set, keep on consuming descriptors from +AF_XDP Tx ring and put them directly onto Cq. Previously these +descriptors were silently lost and could never be reached again. + +Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path") +Closes: https://lore.kernel.org/all/20260425041726.85FB3C2BCB2@smtp.kernel.org/ +Reviewed-by: Jason Xing +Co-developed-by: Maciej Fijalkowski # wrapped cq addr submission onto routine +Signed-off-by: Maciej Fijalkowski +Signed-off-by: Jason Xing +Acked-by: Stanislav Fomichev +Link: https://patch.msgid.link/20260719135609.147823-3-maciej.fijalkowski@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + include/net/xdp_sock.h | 1 + + net/xdp/xsk.c | 45 +++++++++++++++++++++++++++++++++++++++--- + 2 files changed, 43 insertions(+), 3 deletions(-) + +diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h +index 7c2bc46c67050..8a0967af8f0c4 100644 +--- a/include/net/xdp_sock.h ++++ b/include/net/xdp_sock.h +@@ -80,6 +80,7 @@ struct xdp_sock { + * call of __xsk_generic_xmit(). + */ + struct sk_buff *skb; ++ bool drain_cont; + + struct list_head map_list; + /* Protects map_list */ +diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c +index 72d83b671f844..70c57c5d8e1da 100644 +--- a/net/xdp/xsk.c ++++ b/net/xdp/xsk.c +@@ -623,6 +623,19 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool, + spin_unlock_irqrestore(&pool->cq_prod_lock, flags); + } + ++static void xsk_cq_submit_addr_single_locked(struct xsk_buff_pool *pool, ++ struct xdp_desc *desc) ++{ ++ unsigned long flags; ++ u32 idx; ++ ++ spin_lock_irqsave(&pool->cq_prod_lock, flags); ++ idx = xskq_get_prod(pool->cq); ++ xskq_prod_write_addr(pool->cq, idx, desc->addr); ++ xskq_prod_submit_n(pool->cq, 1); ++ spin_unlock_irqrestore(&pool->cq_prod_lock, flags); ++} ++ + static void xsk_cq_cancel_locked(struct xsk_buff_pool *pool, u32 n) + { + spin_lock(&pool->cq_cached_prod_lock); +@@ -913,13 +926,14 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, + static int __xsk_generic_xmit(struct sock *sk) + { + struct xdp_sock *xs = xdp_sk(sk); +- bool sent_frame = false; + struct xdp_desc desc; + struct sk_buff *skb; ++ u32 cached_cons; + u32 max_batch; + int err = 0; + + mutex_lock(&xs->mutex); ++ cached_cons = xs->tx->cached_cons; + + /* Since we dropped the RCU read lock, the socket state might have changed. */ + if (unlikely(!xsk_is_bound(xs))) { +@@ -948,11 +962,21 @@ static int __xsk_generic_xmit(struct sock *sk) + goto out; + } + ++ if (unlikely(xs->drain_cont)) { ++ xsk_cq_submit_addr_single_locked(xs->pool, &desc); ++ xs->tx->invalid_descs++; ++ xskq_cons_release(xs->tx); ++ xs->drain_cont = xp_mb_desc(&desc); ++ continue; ++ } ++ + skb = xsk_build_skb(xs, &desc); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + if (err != -EOVERFLOW) + goto out; ++ if (xp_mb_desc(&desc)) ++ xs->drain_cont = true; + err = 0; + continue; + } +@@ -981,18 +1005,33 @@ static int __xsk_generic_xmit(struct sock *sk) + goto out; + } + +- sent_frame = true; + xs->skb = NULL; + } + + if (xskq_has_descs(xs->tx)) { ++ bool drain = xs->skb || xs->drain_cont || xp_mb_desc(&desc); ++ ++ err = xsk_cq_reserve_locked(xs->pool); ++ if (err) { ++ xs->tx->invalid_descs--; ++ if (xs->skb) ++ xsk_drop_skb(xs->skb); ++ xs->drain_cont = drain; ++ err = -EAGAIN; ++ goto out; ++ } ++ + if (xs->skb) + xsk_drop_skb(xs->skb); ++ ++ xsk_cq_submit_addr_single_locked(xs->pool, &desc); ++ + xskq_cons_release(xs->tx); ++ xs->drain_cont = xp_mb_desc(&desc); + } + + out: +- if (sent_frame) ++ if (xs->tx->cached_cons != cached_cons) + __xsk_tx_release(xs); + + mutex_unlock(&xs->mutex); +-- +2.53.0 + diff --git a/queue-6.18/xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch b/queue-6.18/xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch new file mode 100644 index 0000000000..f4f1982733 --- /dev/null +++ b/queue-6.18/xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch @@ -0,0 +1,106 @@ +From 60b806e3ba83daad331ac31bc6f2db88c211accb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 15:56:04 +0200 +Subject: xsk: fix buffer leak in xsk_drop_skb() for AF_XDP multi-buffer Tx + +From: Jason Xing + +[ Upstream commit a3c8382ebce4780c6b3ace2c09bc342313ac0186 ] + +This patch is inspired by the check[1] from sashiko. It says when +overflow happens, the address of cq to be published is invalid. +Actually the severer thing is the whole process of publishing the +address of cq in this particular case is not right: it should truely +publish the address and advance the cached_prod in cq as long as it +reads descriptors from txq. + +The following is the full analysis. +xsk_drop_skb() is called in three places, which all discard a partially +built multi-buffer skb: +1) xsk_build_skb() -EOVERFLOW error path: packet exceeds MAX_SKB_FRAGS +2) __xsk_generic_xmit() post-loop cleanup: an invalid descriptor in + the TX ring prevents the partial packet from completing +3) xsk_release(): socket close while xs->skb holds an incomplete packet + +In all three cases, the TX descriptors for the already-processed frags +have been consumed from the TX ring (xskq_cons_release), and CQ slots +have been reserved. However, xsk_drop_skb() calls xsk_consume_skb() +which cancels the CQ reservations via xsk_cq_cancel_locked(). Since +the buffer addresses never appear in the completion queue, userspace +permanently loses track of these buffers. + +Fix this by letting consume_skb() trigger the existing xsk_destruct_skb +destructor, which already submits buffer addresses to the CQ via +xsk_cq_submit_addr_locked(). + +Note that cancelling the descriptors back to the TX ring (via +xskq_cons_cancel_n) is not a appropriate option because an oversized +packet that always exceeds MAX_SKB_FRAGS would be retried indefinitely, +which is an obviously deadlock bug in the TX path. + +Also move the desc->addr assignment in xsk_build_skb() above the +overflow check so that the current descriptor's address is recorded +before a potential -EOVERFLOW jump to free_err, consistent with the +zerocopy path in xsk_build_skb_zerocopy(). + +[1]: https://lore.kernel.org/all/20260425041726.85FB3C2BCB2@smtp.kernel.org/ + +Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path") +Acked-by: Maciej Fijalkowski +Signed-off-by: Jason Xing +Acked-by: Stanislav Fomichev +Link: https://patch.msgid.link/20260719135609.147823-2-maciej.fijalkowski@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/xdp/xsk.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c +index 9e0a486d54fb3..024e23c08b8db 100644 +--- a/net/xdp/xsk.c ++++ b/net/xdp/xsk.c +@@ -676,8 +676,11 @@ static void xsk_consume_skb(struct sk_buff *skb) + + static void xsk_drop_skb(struct sk_buff *skb) + { +- xdp_sk(skb->sk)->tx->invalid_descs += xsk_get_num_desc(skb); +- xsk_consume_skb(skb); ++ struct xdp_sock *xs = xdp_sk(skb->sk); ++ ++ xs->tx->invalid_descs += xsk_get_num_desc(skb); ++ consume_skb(skb); ++ xs->skb = NULL; + } + + static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, +@@ -768,7 +771,7 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, + } + + /* in case of -EOVERFLOW that could happen below, +- * xsk_consume_skb() will release this node as whole skb ++ * xsk_drop_skb() will release this node as whole skb + * would be dropped, which implies freeing all list elements + */ + xsk_addr->addrs[xsk_addr->num_descs] = desc->addr; +@@ -867,6 +870,8 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, + xsk_addr = (struct xsk_addrs *)skb_shinfo(skb)->destructor_arg; + } + ++ xsk_addr->addrs[xsk_addr->num_descs] = desc->addr; ++ + if (unlikely(nr_frags == (MAX_SKB_FRAGS - 1) && xp_mb_desc(desc))) { + err = -EOVERFLOW; + goto free_err; +@@ -884,8 +889,6 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, + + skb_add_rx_frag(skb, nr_frags, page, 0, len, PAGE_SIZE); + refcount_add(PAGE_SIZE, &xs->sk.sk_wmem_alloc); +- +- xsk_addr->addrs[xsk_addr->num_descs] = desc->addr; + } + } + +-- +2.53.0 + diff --git a/queue-6.18/xsk-use-a-smaller-new-lock-for-shared-pool-case.patch b/queue-6.18/xsk-use-a-smaller-new-lock-for-shared-pool-case.patch new file mode 100644 index 0000000000..17fc36beaf --- /dev/null +++ b/queue-6.18/xsk-use-a-smaller-new-lock-for-shared-pool-case.patch @@ -0,0 +1,140 @@ +From 97d412bcd0e47617f99fa79c6da94ce616b18c02 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 30 Oct 2025 08:06:46 +0800 +Subject: xsk: use a smaller new lock for shared pool case + +From: Jason Xing + +[ Upstream commit 30ed05adca4a05c50594384cff18910858dd1d35 ] + +- Split cq_lock into two smaller locks: cq_prod_lock and + cq_cached_prod_lock +- Avoid disabling/enabling interrupts in the hot xmit path + +In either xsk_cq_cancel_locked() or xsk_cq_reserve_locked() function, +the race condition is only between multiple xsks sharing the same +pool. They are all in the process context rather than interrupt context, +so now the small lock named cq_cached_prod_lock can be used without +handling interrupts. + +While cq_cached_prod_lock ensures the exclusive modification of +@cached_prod, cq_prod_lock in xsk_cq_submit_addr_locked() only cares +about @producer and corresponding @desc. Both of them don't necessarily +be consistent with @cached_prod protected by cq_cached_prod_lock. +That's the reason why the previous big lock can be split into two +smaller ones. Please note that SPSC rule is all about the global state +of producer and consumer that can affect both layers instead of local +or cached ones. + +Frequently disabling and enabling interrupt are very time consuming +in some cases, especially in a per-descriptor granularity, which now +can be avoided after this optimization, even when the pool is shared by +multiple xsks. + +With this patch, the performance number[1] could go from 1,872,565 pps +to 1,961,009 pps. It's a minor rise of around 5%. + +[1]: taskset -c 1 ./xdpsock -i enp2s0f1 -q 0 -t -S -s 64 + +Signed-off-by: Jason Xing +Acked-by: Maciej Fijalkowski +Link: https://patch.msgid.link/20251030000646.18859-3-kerneljasonxing@gmail.com +Signed-off-by: Paolo Abeni +Stable-dep-of: bd44a6dcd424 ("xsk: drain continuation descs after overflow in xsk_build_skb()") +Signed-off-by: Sasha Levin +--- + include/net/xsk_buff_pool.h | 13 +++++++++---- + net/xdp/xsk.c | 15 ++++++--------- + net/xdp/xsk_buff_pool.c | 3 ++- + 3 files changed, 17 insertions(+), 14 deletions(-) + +diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h +index cac56e6b0869b..92a2358c6ce34 100644 +--- a/include/net/xsk_buff_pool.h ++++ b/include/net/xsk_buff_pool.h +@@ -85,11 +85,16 @@ struct xsk_buff_pool { + bool unaligned; + bool tx_sw_csum; + void *addrs; +- /* Mutual exclusion of the completion ring in the SKB mode. Two cases to protect: +- * NAPI TX thread and sendmsg error paths in the SKB destructor callback and when +- * sockets share a single cq when the same netdev and queue id is shared. ++ /* Mutual exclusion of the completion ring in the SKB mode. ++ * Protect: NAPI TX thread and sendmsg error paths in the SKB ++ * destructor callback. + */ +- spinlock_t cq_lock; ++ spinlock_t cq_prod_lock; ++ /* Mutual exclusion of the completion ring in the SKB mode. ++ * Protect: when sockets share a single cq when the same netdev ++ * and queue id is shared. ++ */ ++ spinlock_t cq_cached_prod_lock; + struct xdp_buff_xsk *free_heads[]; + }; + +diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c +index 024e23c08b8db..72d83b671f844 100644 +--- a/net/xdp/xsk.c ++++ b/net/xdp/xsk.c +@@ -547,12 +547,11 @@ static int xsk_wakeup(struct xdp_sock *xs, u8 flags) + + static int xsk_cq_reserve_locked(struct xsk_buff_pool *pool) + { +- unsigned long flags; + int ret; + +- spin_lock_irqsave(&pool->cq_lock, flags); ++ spin_lock(&pool->cq_cached_prod_lock); + ret = xskq_prod_reserve(pool->cq); +- spin_unlock_irqrestore(&pool->cq_lock, flags); ++ spin_unlock(&pool->cq_cached_prod_lock); + + return ret; + } +@@ -603,7 +602,7 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool, + unsigned long flags; + u32 idx, i; + +- spin_lock_irqsave(&pool->cq_lock, flags); ++ spin_lock_irqsave(&pool->cq_prod_lock, flags); + idx = xskq_get_prod(pool->cq); + + if (unlikely(num_descs > 1)) { +@@ -621,16 +620,14 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool, + descs_processed++; + } + xskq_prod_submit_n(pool->cq, descs_processed); +- spin_unlock_irqrestore(&pool->cq_lock, flags); ++ spin_unlock_irqrestore(&pool->cq_prod_lock, flags); + } + + static void xsk_cq_cancel_locked(struct xsk_buff_pool *pool, u32 n) + { +- unsigned long flags; +- +- spin_lock_irqsave(&pool->cq_lock, flags); ++ spin_lock(&pool->cq_cached_prod_lock); + xskq_prod_cancel_n(pool->cq, n); +- spin_unlock_irqrestore(&pool->cq_lock, flags); ++ spin_unlock(&pool->cq_cached_prod_lock); + } + + static void xsk_destruct_skb(struct sk_buff *skb) +diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c +index a129ce6f1c25f..c9688735b643c 100644 +--- a/net/xdp/xsk_buff_pool.c ++++ b/net/xdp/xsk_buff_pool.c +@@ -96,7 +96,8 @@ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs, + INIT_LIST_HEAD(&pool->xskb_list); + INIT_LIST_HEAD(&pool->xsk_tx_list); + spin_lock_init(&pool->xsk_tx_list_lock); +- spin_lock_init(&pool->cq_lock); ++ spin_lock_init(&pool->cq_prod_lock); ++ spin_lock_init(&pool->cq_cached_prod_lock); + refcount_set(&pool->users, 1); + + pool->fq = xs->fq_tmp; +-- +2.53.0 + diff --git a/queue-6.6/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch b/queue-6.6/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch new file mode 100644 index 0000000000..1eae6d0c2a --- /dev/null +++ b/queue-6.6/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch @@ -0,0 +1,49 @@ +From e303128a63a05229222959e84ca65a3b46047b52 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 02:25:20 +0500 +Subject: accel/qaic: use sizeof(*trans_hdr) for transaction length check + +From: Muhammad Bilal + +[ Upstream commit d6c075f797a672a6e3bd2fd44aee713801698ec2 ] + +In encode_message() the per-transaction lower-bound check compares +trans_hdr->len against sizeof(trans_hdr), i.e. the size of the pointer, +instead of sizeof(*trans_hdr), the size of struct qaic_manage_trans_hdr. + +Every other length check in this file (encode_message() at the loop +guard, decode_message(), etc.) correctly uses sizeof(*trans_hdr), so +this is an inconsistency. On 64-bit builds the pointer and the struct +are both 8 bytes, so the check is correct by coincidence and there is +no behavioural change. On 32-bit builds the pointer is 4 bytes, which +weakens the minimum-length check below the 8-byte header size. + +Use sizeof(*trans_hdr) so the check validates against the actual +transaction header size on all builds. + +Fixes: ea33cb6fc278 ("accel/qaic: tighten bounds checking in encode_message()") +Signed-off-by: Muhammad Bilal +Reviewed-by: Jeff Hugo +Signed-off-by: Jeff Hugo +Link: https://patch.msgid.link/20260617212520.59801-1-meatuni001@gmail.com +Signed-off-by: Sasha Levin +--- + drivers/accel/qaic/qaic_control.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c +index de8b17e2b29e1..e59acc39c3fa5 100644 +--- a/drivers/accel/qaic/qaic_control.c ++++ b/drivers/accel/qaic/qaic_control.c +@@ -782,7 +782,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg, + break; + } + trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len); +- if (trans_hdr->len < sizeof(trans_hdr) || ++ if (trans_hdr->len < sizeof(*trans_hdr) || + size_add(user_len, trans_hdr->len) > user_msg->len) { + ret = -EINVAL; + break; +-- +2.53.0 + diff --git a/queue-6.6/ahci-introduce-ahci_ignore_port-helper.patch b/queue-6.6/ahci-introduce-ahci_ignore_port-helper.patch new file mode 100644 index 0000000000..ab66ff3bae --- /dev/null +++ b/queue-6.6/ahci-introduce-ahci_ignore_port-helper.patch @@ -0,0 +1,135 @@ +From 020404ef0cef2393357e64e59ce97f0a0d519ae8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 6 Jan 2025 14:14:47 +0900 +Subject: ahci: Introduce ahci_ignore_port() helper + +From: Damien Le Moal + +[ Upstream commit c9b5be909e6595547ed5d45aef39fd65948aa342 ] + +libahci and AHCI drivers may ignore some ports if the port is invalid +(its ID does not correspond to a valid physical port) or if the user +explicitly requested the port to be ignored with the mask_port_map +ahci module parameter. Such port that shall be ignored can be identified +by checking that the bit corresponding to the port ID is not set in the +mask_port_map field of struct ahci_host_priv. E.g. code such as: +"if (!(hpriv->mask_port_map & (1 << portid)))". + +Replace all direct use of the mask_port_map field to detect such port +with the new helper inline function ahci_ignore_port() to make the code +more readable/easier to understand. + +The comment describing the mask_port_map field of struct ahci_host_priv +is also updated to be more accurate. + +Signed-off-by: Damien Le Moal +Reviewed-by: Niklas Cassel +Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()") +Signed-off-by: Sasha Levin +--- + drivers/ata/ahci.h | 13 ++++++++++++- + drivers/ata/ahci_brcm.c | 2 +- + drivers/ata/ahci_ceva.c | 4 ++-- + drivers/ata/libahci_platform.c | 6 +++--- + 4 files changed, 18 insertions(+), 7 deletions(-) + +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index df8f8a1a3a34c..ef791ab8d9d11 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -330,7 +330,7 @@ struct ahci_port_priv { + struct ahci_host_priv { + /* Input fields */ + unsigned int flags; /* AHCI_HFLAG_* */ +- u32 mask_port_map; /* mask out particular bits */ ++ u32 mask_port_map; /* Mask of valid ports */ + + void __iomem * mmio; /* bus-independent mem map */ + u32 cap; /* cap to use */ +@@ -381,6 +381,17 @@ struct ahci_host_priv { + int port); + }; + ++/* ++ * Return true if a port should be ignored because it is excluded from ++ * the host port map. ++ */ ++static inline bool ahci_ignore_port(struct ahci_host_priv *hpriv, ++ unsigned int portid) ++{ ++ return portid >= hpriv->nports || ++ !(hpriv->mask_port_map & (1 << portid)); ++} ++ + extern int ahci_ignore_sss; + + extern const struct attribute_group *ahci_shost_groups[]; +diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c +index 0411bad231475..481aab4d9b96f 100644 +--- a/drivers/ata/ahci_brcm.c ++++ b/drivers/ata/ahci_brcm.c +@@ -288,7 +288,7 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev, + + /* Re-initialize and calibrate the PHY */ + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + rc = phy_init(hpriv->phys[i]); +diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c +index 6e115da23d3f0..93275b1b48898 100644 +--- a/drivers/ata/ahci_ceva.c ++++ b/drivers/ata/ahci_ceva.c +@@ -206,7 +206,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + goto disable_clks; + + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + rc = phy_init(hpriv->phys[i]); +@@ -218,7 +218,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + ahci_platform_deassert_rsts(hpriv); + + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + rc = phy_power_on(hpriv->phys[i]); +diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c +index 2174a3e6e9574..bfb103ca610dd 100644 +--- a/drivers/ata/libahci_platform.c ++++ b/drivers/ata/libahci_platform.c +@@ -49,7 +49,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) + int rc, i; + + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + rc = phy_init(hpriv->phys[i]); +@@ -73,7 +73,7 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) + + disable_phys: + while (--i >= 0) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + phy_power_off(hpriv->phys[i]); +@@ -94,7 +94,7 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) + int i; + + for (i = 0; i < hpriv->nports; i++) { +- if (!(hpriv->mask_port_map & (1 << i))) ++ if (ahci_ignore_port(hpriv, i)) + continue; + + phy_power_off(hpriv->phys[i]); +-- +2.53.0 + diff --git a/queue-6.6/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.6/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..a01e8f58ee --- /dev/null +++ b/queue-6.6/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From 7dc8420bef992376983b564a243fe97b60f2936a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:12:54 +0530 +Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ] + +In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98090->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98090.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c +index 4023b88e7bc13..9253707bccde9 100644 +--- a/sound/soc/codecs/max98090.c ++++ b/sound/soc/codecs/max98090.c +@@ -2388,8 +2388,9 @@ static int max98090_probe(struct snd_soc_component *component) + dev_dbg(component->dev, "max98090_probe\n"); + + max98090->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98090->mclk)) ++ if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + max98090->component = component; + +-- +2.53.0 + diff --git a/queue-6.6/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-6.6/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..bccd663440 --- /dev/null +++ b/queue-6.6/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From 01d7b44388959bf1e15cdcb0680cc3ace21fa593 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:09:50 +0530 +Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ] + +In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98095->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98095.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c +index 7e525d49328d2..3b37f4c4235e6 100644 +--- a/sound/soc/codecs/max98095.c ++++ b/sound/soc/codecs/max98095.c +@@ -1984,8 +1984,9 @@ static int max98095_probe(struct snd_soc_component *component) + int ret = 0; + + max98095->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98095->mclk)) ++ if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + /* reset the codec, the DSP core, and disable all interrupts */ + max98095_reset(component); +-- +2.53.0 + diff --git a/queue-6.6/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-6.6/assoc_array-trim-the-final-shortcut-word-using-the-c.patch new file mode 100644 index 0000000000..4993fb31ed --- /dev/null +++ b/queue-6.6/assoc_array-trim-the-final-shortcut-word-using-the-c.patch @@ -0,0 +1,66 @@ +From a16964aa125016e60dfdd822d48b5713aa678e3a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:05 -0400 +Subject: assoc_array: trim the final shortcut word using the current chunk end + +From: Michael Bommarito + +[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ] + +assoc_array_walk() masks off the bits past shortcut->skip_to_level in the +word that contains skip_to_level, gated on +round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level. + +That guard is wrong in two opposite ways: + + - When sc_level is word-aligned (every word after the first) round_up() + is a no-op, so the guard is sc_level > skip_to_level and never fires for + the word that holds skip_to_level. A shortcut that spans more than one + word and ends in the middle of its last word leaves that word untrimmed, + and its stale high bits leak into the dissimilarity word and can steer + the walk down the wrong descendant. + + - When sc_level is unaligned (the first word) and skip_to_level sits on + the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and + fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears + the whole dissimilarity word and makes a differing shortcut compare + equal. + +Use the end of the chunk that contains sc_level instead: + + skip_to_level < round_down(sc_level, CHUNK) + CHUNK + +For an aligned sc_level whose word holds skip_to_level this now fires (the +first bug); for an unaligned sc_level with skip_to_level on the following +boundary it does not, so shift is never 0 when the branch runs and the trim +never clears the whole word. + +Fixes: 3cb989501c26 ("Add a generic associative array implementation.") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + lib/assoc_array.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/assoc_array.c b/lib/assoc_array.c +index ca0b4f360c1a0..65409f0d2e0e5 100644 +--- a/lib/assoc_array.c ++++ b/lib/assoc_array.c +@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array, + sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT]; + dissimilarity = segments ^ sc_segments; + +- if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) { ++ if (shortcut->skip_to_level < round_down(sc_level, ++ ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) { + /* Trim segments that are beyond the shortcut */ + int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK; + dissimilarity &= ~(ULONG_MAX << shift); +-- +2.53.0 + diff --git a/queue-6.6/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch b/queue-6.6/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch new file mode 100644 index 0000000000..3e3f21cefb --- /dev/null +++ b/queue-6.6/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch @@ -0,0 +1,76 @@ +From 41c03d77ca79d75abab06d59392fb01bf1ad648d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 17 Jul 2026 23:55:26 +0530 +Subject: ata: ahci_ceva: fix error paths in + ceva_ahci_platform_enable_resources() + +From: Radhey Shyam Pandey + +[ Upstream commit 4d99a91574c420decab56cc880fad0dc15b8a7a3 ] + +On phy_init() failure the error path fallsthrough to disable_rsts, which +deasserts the controller reset and then enters disable_phys calling +phy_power_off() on PHYs that were never powered on. That corrupts the PHY +power_count and triggers an extra runtime PM put. + +Use a separate exit_phys path that unwinds with phy_exit() only and falls +through to disable_clks while the controller remains in reset. Reserve +phy_power_off() for the phy_power_on() failure path only, and skip +masked-out ports in both unwind loops. + +On phy_power_on() failure re-assert the controller reset before disabling +clocks and regulators, matching the teardown order used by +ahci_platform_enable_resources() and ahci_platform_disable_resources(). + +Fixes: 26c8404e162b ("ata: ahci_ceva: fix error handling for Xilinx GT PHY support") +Signed-off-by: Radhey Shyam Pandey +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + drivers/ata/ahci_ceva.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c +index 93275b1b48898..b2918ad8e7e05 100644 +--- a/drivers/ata/ahci_ceva.c ++++ b/drivers/ata/ahci_ceva.c +@@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + + rc = phy_init(hpriv->phys[i]); + if (rc) +- goto disable_rsts; ++ goto exit_phys; + } + + /* De-assert the controller reset */ +@@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + + return 0; + +-disable_rsts: +- ahci_platform_deassert_rsts(hpriv); +- + disable_phys: + while (--i >= 0) { ++ if (ahci_ignore_port(hpriv, i)) ++ continue; ++ + phy_power_off(hpriv->phys[i]); + phy_exit(hpriv->phys[i]); + } ++ ahci_platform_assert_rsts(hpriv); ++ goto disable_clks; ++ ++exit_phys: ++ while (--i >= 0) { ++ if (ahci_ignore_port(hpriv, i)) ++ continue; ++ ++ phy_exit(hpriv->phys[i]); ++ } + + disable_clks: + ahci_platform_disable_clks(hpriv); +-- +2.53.0 + diff --git a/queue-6.6/ata-libahci_platform-support-non-consecutive-port-nu.patch b/queue-6.6/ata-libahci_platform-support-non-consecutive-port-nu.patch new file mode 100644 index 0000000000..ea92c3d7c3 --- /dev/null +++ b/queue-6.6/ata-libahci_platform-support-non-consecutive-port-nu.patch @@ -0,0 +1,178 @@ +From 6d5e9f4c9e355bd9e8d066eefcd6f5ffb2543f2e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 1 Jan 2025 13:13:33 +0100 +Subject: ata: libahci_platform: support non-consecutive port numbers + +From: Josua Mayer + +[ Upstream commit 8c87215dd3a2c814dcffc0bafe8c80c8f98f2574 ] + +So far ahci_platform relied on number of child nodes in firmware to +allocate arrays and expected port numbers to start from 0 without holes. +This number of ports is then set in private structure for use when +configuring phys and regulators. + +Some platforms may not use every port of an ahci controller. +E.g. SolidRUN CN9130 Clearfog uses only port 1 but not port 0, leading +to the following errors during boot: +[ 1.719476] ahci f2540000.sata: invalid port number 1 +[ 1.724562] ahci f2540000.sata: No port enabled + +Update all accessesors of ahci_host_priv phys and target_pwrs arrays to +support holes. Access is gated by hpriv->mask_port_map which has a bit +set for each enabled port. + +Update ahci_platform_get_resources to ignore holes in the port numbers +and enable ports defined in firmware by their reg property only. + +When firmware does not define children it is assumed that there is +exactly one port, using index 0. + +Signed-off-by: Josua Mayer +Reviewed-by: Hans de Goede +Signed-off-by: Damien Le Moal +Stable-dep-of: 4d99a91574c4 ("ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources()") +Signed-off-by: Sasha Levin +--- + drivers/ata/ahci_brcm.c | 3 +++ + drivers/ata/ahci_ceva.c | 6 +++++ + drivers/ata/libahci_platform.c | 40 +++++++++++++++++++++++++++++----- + 3 files changed, 43 insertions(+), 6 deletions(-) + +diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c +index 70c3a33eee6f2..0411bad231475 100644 +--- a/drivers/ata/ahci_brcm.c ++++ b/drivers/ata/ahci_brcm.c +@@ -288,6 +288,9 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev, + + /* Re-initialize and calibrate the PHY */ + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + rc = phy_init(hpriv->phys[i]); + if (rc) + goto disable_phys; +diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c +index 11a2c199a7c24..6e115da23d3f0 100644 +--- a/drivers/ata/ahci_ceva.c ++++ b/drivers/ata/ahci_ceva.c +@@ -206,6 +206,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + goto disable_clks; + + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + rc = phy_init(hpriv->phys[i]); + if (rc) + goto disable_rsts; +@@ -215,6 +218,9 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + ahci_platform_deassert_rsts(hpriv); + + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + rc = phy_power_on(hpriv->phys[i]); + if (rc) { + phy_exit(hpriv->phys[i]); +diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c +index 581704e61f286..2174a3e6e9574 100644 +--- a/drivers/ata/libahci_platform.c ++++ b/drivers/ata/libahci_platform.c +@@ -49,6 +49,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) + int rc, i; + + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + rc = phy_init(hpriv->phys[i]); + if (rc) + goto disable_phys; +@@ -70,6 +73,9 @@ int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) + + disable_phys: + while (--i >= 0) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + phy_power_off(hpriv->phys[i]); + phy_exit(hpriv->phys[i]); + } +@@ -88,6 +94,9 @@ void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) + int i; + + for (i = 0; i < hpriv->nports; i++) { ++ if (!(hpriv->mask_port_map & (1 << i))) ++ continue; ++ + phy_power_off(hpriv->phys[i]); + phy_exit(hpriv->phys[i]); + } +@@ -435,6 +444,20 @@ static int ahci_platform_get_firmware(struct ahci_host_priv *hpriv, + return 0; + } + ++static u32 ahci_platform_find_max_port_id(struct device *dev) ++{ ++ u32 max_port = 0; ++ ++ for_each_child_of_node_scoped(dev->of_node, child) { ++ u32 port; ++ ++ if (!of_property_read_u32(child, "reg", &port)) ++ max_port = max(max_port, port); ++ } ++ ++ return max_port; ++} ++ + /** + * ahci_platform_get_resources - Get platform resources + * @pdev: platform device to get resources for +@@ -462,6 +485,7 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, + struct ahci_host_priv *hpriv; + struct device_node *child; + u32 mask_port_map = 0; ++ u32 max_port; + + if (!devres_open_group(dev, NULL, GFP_KERNEL)) + return ERR_PTR(-ENOMEM); +@@ -553,15 +577,17 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, + goto err_out; + } + ++ /* find maximum port id for allocating structures */ ++ max_port = ahci_platform_find_max_port_id(dev); + /* +- * If no sub-node was found, we still need to set nports to +- * one in order to be able to use the ++ * Set nports according to maximum port id. Clamp at ++ * AHCI_MAX_PORTS, warning message for invalid port id ++ * is generated later. ++ * When DT has no sub-nodes max_port is 0, nports is 1, ++ * in order to be able to use the + * ahci_platform_[en|dis]able_[phys|regulators] functions. + */ +- if (child_nodes) +- hpriv->nports = child_nodes; +- else +- hpriv->nports = 1; ++ hpriv->nports = min(AHCI_MAX_PORTS, max_port + 1); + + hpriv->phys = devm_kcalloc(dev, hpriv->nports, sizeof(*hpriv->phys), GFP_KERNEL); + if (!hpriv->phys) { +@@ -634,6 +660,8 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, + * If no sub-node was found, keep this for device tree + * compatibility + */ ++ hpriv->mask_port_map |= BIT(0); ++ + rc = ahci_platform_get_phy(hpriv, 0, dev, dev->of_node); + if (rc) + goto err_out; +-- +2.53.0 + diff --git a/queue-6.6/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch b/queue-6.6/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch new file mode 100644 index 0000000000..8809de45c2 --- /dev/null +++ b/queue-6.6/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch @@ -0,0 +1,44 @@ +From 3633682b60e01fbc5549b6e99687e6e3a7cb82c2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 15:31:37 -0700 +Subject: ata: sata_mv: accept 1 or 2 resources in platform probe + +From: Rosen Penev + +[ Upstream commit ef19a9cf037957fe3a35df8355c76ff0a63a0436 ] + +Board files in arch/arm/plat-orion, arch/arm/mach-dove, +arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the +"sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ). +Those devices are rejected with -EINVAL, so SATA no longer probes on +legacy Marvell Orion/Kirkwood-style boards. + +Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2 +resources (legacy, IRQ supplied as a second resource) so both probing +paths work. + +Fixes: b3b2bec9646e ("ata: sata_mv: Fixes expected number of resources now IRQs are gone") +Assisted-by: opencode:big-pickle +Signed-off-by: Rosen Penev +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + drivers/ata/sata_mv.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c +index 80a45e11fb5b6..8700bc2fdade8 100644 +--- a/drivers/ata/sata_mv.c ++++ b/drivers/ata/sata_mv.c +@@ -4026,7 +4026,7 @@ static int mv_platform_probe(struct platform_device *pdev) + /* + * Simple resource validation .. + */ +- if (unlikely(pdev->num_resources != 1)) { ++ if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) { + dev_err(&pdev->dev, "invalid number of resources\n"); + return -EINVAL; + } +-- +2.53.0 + diff --git a/queue-6.6/bluetooth-btintel-validate-length-before-parsing-dia.patch b/queue-6.6/bluetooth-btintel-validate-length-before-parsing-dia.patch new file mode 100644 index 0000000000..80a7a0aecb --- /dev/null +++ b/queue-6.6/bluetooth-btintel-validate-length-before-parsing-dia.patch @@ -0,0 +1,40 @@ +From 20fd548bd4024dfbe4ea532a6f898a95b724098c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 01:54:40 -0700 +Subject: Bluetooth: btintel: Validate length before parsing diagnostics TLV + +From: Zijun Hu + +[ Upstream commit b640ff9af3c809ff5ea2077fbba17df1594ec1e4 ] + +btintel_diagnostics() accesses tlv->val[0] without first validating +that the diagnostics VSE is long enough to contain that field, so +may cause reading data beyond the received frame. + +Fix by validating the length before access. + +Fixes: af395330abed ("Bluetooth: btintel: Add Intel devcoredump support") +Signed-off-by: Zijun Hu +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btintel.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c +index 25fcd00c6a174..4784da07dd8a1 100644 +--- a/drivers/bluetooth/btintel.c ++++ b/drivers/bluetooth/btintel.c +@@ -2922,6 +2922,9 @@ static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb) + { + struct intel_tlv *tlv = (void *)&skb->data[5]; + ++ if (skb->len < 5 + sizeof(*tlv) + sizeof(tlv->val[0])) ++ goto recv_frame; ++ + /* The first event is always an event type TLV */ + if (tlv->type != INTEL_TLV_TYPE_ID) + goto recv_frame; +-- +2.53.0 + diff --git a/queue-6.6/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch b/queue-6.6/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch new file mode 100644 index 0000000000..2584e2553e --- /dev/null +++ b/queue-6.6/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch @@ -0,0 +1,54 @@ +From cee5b7df719747e459fc919044ed844e9fad1776 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:17 +0300 +Subject: Bluetooth: hci_conn: hold conn reference in abort_conn_sync() + +From: Pauli Virtanen + +[ Upstream commit 5761d003daa987ac81463f570713ce9c9dd204e5 ] + +There is theoretical UAF if the conn is freed while the hci_sync task is +running. + +Hold refcount to avoid that. + +Fixes: 227a0cdf4a02 ("Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_conn.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c +index 7625461dff7ae..e4364a64ffd57 100644 +--- a/net/bluetooth/hci_conn.c ++++ b/net/bluetooth/hci_conn.c +@@ -2896,6 +2896,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data) + return hci_abort_conn_sync(hdev, conn, conn->abort_reason); + } + ++static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err) ++{ ++ struct hci_conn *conn = data; ++ ++ hci_conn_put(conn); ++} ++ + int hci_abort_conn(struct hci_conn *conn, u8 reason) + { + struct hci_dev *hdev = conn->hdev; +@@ -2921,6 +2928,9 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) + * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does + * already queue its callback on cmd_sync_work. + */ +- err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); ++ err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn), ++ abort_conn_destroy); ++ if (err) ++ hci_conn_put(conn); + return (err == -EEXIST) ? 0 : err; + } +-- +2.53.0 + diff --git a/queue-6.6/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch b/queue-6.6/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch new file mode 100644 index 0000000000..68f450f69a --- /dev/null +++ b/queue-6.6/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch @@ -0,0 +1,43 @@ +From 7a8dfa508bb97798acfa63f4d55353d18121e819 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:22 +0300 +Subject: Bluetooth: hci_sync: fix hci_conn_del() use in + hci_le_create_conn_sync + +From: Pauli Virtanen + +[ Upstream commit 2c1e4e00613dfd105f978be2276e5e265801ec9f ] + +hci_conn_del() caller must hold hdev->lock, check the conn was not +concurrently deleted, and usually inform socket the conn is going to be +deleted. + +Use hci_abort_conn_sync() instead of calling hci_conn_del() without +locks etc. + +Fixes: 8e8b92ee60de5 ("Bluetooth: hci_sync: Add hci_le_create_conn_sync") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_sync.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index df207c3f1c5c3..6a5ec74eaf25a 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -6582,7 +6582,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + if (hci_dev_test_flag(hdev, HCI_LE_SCAN) && + hdev->le_scan_type == LE_SCAN_ACTIVE && + !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) { +- hci_conn_del(conn); ++ conn->state = BT_OPEN; ++ hci_abort_conn_sync(hdev, conn, ++ HCI_ERROR_REJ_LIMITED_RESOURCES); + hci_conn_put(conn); + return -EBUSY; + } +-- +2.53.0 + diff --git a/queue-6.6/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch b/queue-6.6/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch new file mode 100644 index 0000000000..bd4f2e90f5 --- /dev/null +++ b/queue-6.6/bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch @@ -0,0 +1,64 @@ +From c0abf50a5452f2f358fb287c1590da4b615af584 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 25 Mar 2026 21:07:45 +0200 +Subject: Bluetooth: hci_sync: make hci_cmd_sync_run_once return -EEXIST if + exists + +From: Pauli Virtanen + +[ Upstream commit d288f4db0909c22342eb50cd1632b4d850517281 ] + +hci_cmd_sync_run_once() needs to indicate whether a queue item was +added, so caller can know if callbacks are called, so it can avoid +leaking resources. + +Change the function to return -EEXIST if queue item already exists. + +Modify all callsites vs. the changes. The only callsite is +hci_abort_conn(). + +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: 5761d003daa9 ("Bluetooth: hci_conn: hold conn reference in abort_conn_sync()") +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_conn.c | 4 +++- + net/bluetooth/hci_sync.c | 2 +- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c +index 19ca5016372ac..7625461dff7ae 100644 +--- a/net/bluetooth/hci_conn.c ++++ b/net/bluetooth/hci_conn.c +@@ -2899,6 +2899,7 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data) + int hci_abort_conn(struct hci_conn *conn, u8 reason) + { + struct hci_dev *hdev = conn->hdev; ++ int err; + + /* If abort_reason has already been set it means the connection is + * already being aborted so don't attempt to overwrite it. +@@ -2920,5 +2921,6 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) + * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does + * already queue its callback on cmd_sync_work. + */ +- return hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); ++ err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); ++ return (err == -EEXIST) ? 0 : err; + } +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 4b1fcea37941f..df207c3f1c5c3 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -849,7 +849,7 @@ int hci_cmd_sync_run_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, + void *data, hci_cmd_sync_work_destroy_t destroy) + { + if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy)) +- return 0; ++ return -EEXIST; + + return hci_cmd_sync_run(hdev, func, data, destroy); + } +-- +2.53.0 + diff --git a/queue-6.6/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch b/queue-6.6/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch new file mode 100644 index 0000000000..2091373bb8 --- /dev/null +++ b/queue-6.6/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch @@ -0,0 +1,80 @@ +From 3d33c799bf91a0c2937cfcb3b384c76745a9159c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:23 +0300 +Subject: Bluetooth: hci_sync: remove unnecessary hci_conn_get in + create_conn_sync + +From: Pauli Virtanen + +[ Upstream commit c0a9dcd2be398eee505d4b254ec3a845aa8ab189 ] + +hci_conn_get() without already held reference is data race against +concurrent deletion. + +In previous patches, the refcount has been changed to be taken before +starting the hci_sync task, so remove these extra get() + put() as they +are not needed. + +Fixes: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_sync.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 6a5ec74eaf25a..9dec36467fead 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -6566,11 +6566,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + + bt_dev_dbg(hdev, "conn %p", conn); + +- /* Hold a reference so conn stays valid for the HCI_CONN_CREATE +- * clear_bit() at done. +- */ +- hci_conn_get(conn); +- + clear_bit(HCI_CONN_SCANNING, &conn->flags); + conn->state = BT_CONNECT; + +@@ -6585,7 +6580,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + conn->state = BT_OPEN; + hci_abort_conn_sync(hdev, conn, + HCI_ERROR_REJ_LIMITED_RESOURCES); +- hci_conn_put(conn); + return -EBUSY; + } + +@@ -6679,7 +6673,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + + /* Re-enable advertising after the connection attempt is finished. */ + hci_resume_advertising_sync(hdev); +- hci_conn_put(conn); + return err; + } + +@@ -6958,11 +6951,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) + else + cp.role_switch = 0x00; + +- /* Hold a reference so conn stays valid for the HCI_CONN_CREATE +- * clear_bit() below. +- */ +- hci_conn_get(conn); +- + /* Mark create connection in flight so hci_cancel_connect_sync() can + * cancel it while blocking on the connection complete event. + */ +@@ -6974,7 +6962,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) + conn->conn_timeout, NULL); + + clear_bit(HCI_CONN_CREATE, &conn->flags); +- hci_conn_put(conn); + + return err; + } +-- +2.53.0 + diff --git a/queue-6.6/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch b/queue-6.6/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch new file mode 100644 index 0000000000..08edadc22d --- /dev/null +++ b/queue-6.6/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch @@ -0,0 +1,40 @@ +From db8ac8c0eebfb786bd49cff126637cd2295982b7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 17:53:33 +0300 +Subject: Bluetooth: ISO: clear iso_data always when detaching conn from hcon + +From: Pauli Virtanen + +[ Upstream commit d57e506f6a1e3929611340fae87c1e4823f4d85c ] + +When setting conn->hcon = NULL, also conn->hcon->iso_data = NULL is +necessary, otherwise later iso_conn_free() will UAF. + +Fix clearing of iso_data in iso_sock_disconn() + +Fixes KASAN: slab-use-after-free in iso_conn_hold_unless_zero on +iso_sock_release() followed by hci_abort_conn_sync(). + +Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 189b719669924..51d0065f058c1 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -767,6 +767,7 @@ static void iso_sock_disconn(struct sock *sk) + iso_sock_set_timer(sk, ISO_DISCONN_TIMEOUT); + iso_conn_lock(iso_pi(sk)->conn); + hci_conn_drop(iso_pi(sk)->conn->hcon); ++ iso_pi(sk)->conn->hcon->iso_data = NULL; + iso_pi(sk)->conn->hcon = NULL; + iso_conn_unlock(iso_pi(sk)->conn); + } +-- +2.53.0 + diff --git a/queue-6.6/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch b/queue-6.6/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch new file mode 100644 index 0000000000..1d046284ac --- /dev/null +++ b/queue-6.6/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch @@ -0,0 +1,38 @@ +From ed11b1deae5a73cc00a26663dd0bcbe373a3dd36 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:27 +0300 +Subject: Bluetooth: ISO: fix timeout vs sync_timeout typo in check_bcast_qos + +From: Pauli Virtanen + +[ Upstream commit e9cb51813d79fc9aae4a2098aab3ab6ebd7fb6c8 ] + +In iso.c check_bcast_qos(), missing bcast.timeout is not set to its +default value, and appears typoed as bcast.sync_timeout. + +Fix the typo. + +Fixes: b37cab587aa3 ("Bluetooth: ISO: Don't reject BT_ISO_QOS if parameters are unset") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 51d0065f058c1..a67bce151aaff 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -1439,7 +1439,7 @@ static bool check_bcast_qos(struct bt_iso_qos *qos) + return false; + + if (!qos->bcast.timeout) +- qos->bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT; ++ qos->bcast.timeout = BT_ISO_SYNC_TIMEOUT; + + if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000) + return false; +-- +2.53.0 + diff --git a/queue-6.6/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-6.6/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch new file mode 100644 index 0000000000..73aa7b2a16 --- /dev/null +++ b/queue-6.6/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch @@ -0,0 +1,60 @@ +From 117e5f41e9f3f83cf7eb9c4e285c145e5f78b2c3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 14:48:45 +0800 +Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp + +From: Jiale Yao + +[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ] + +l2cap_le_connect_rsp() obtains a channel via +__l2cap_get_chan_by_ident() but neither holds a reference nor uses +l2cap_chan_hold_unless_zero() before locking and operating on it. +A concurrent l2cap_chan_del() triggered by a remote disconnect can +free the channel between the lookup and l2cap_chan_lock(), causing +a use-after-free. + +The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler +l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero() +to safely hold a reference, but l2cap_le_connect_rsp() was left +unprotected. + +Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup +and l2cap_chan_put() on the exit path, consistent with other L2CAP +response handlers. + +Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request") +Assisted-by: Claude:deepseek-v4-pro +Signed-off-by: Jiale Yao +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/l2cap_core.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index 792974eef47c0..9643a8aacb96b 100644 +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -4779,6 +4779,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + if (!chan) + return -EBADSLT; + ++ chan = l2cap_chan_hold_unless_zero(chan); ++ if (!chan) ++ return -EBADSLT; ++ + err = 0; + + l2cap_chan_lock(chan); +@@ -4824,6 +4828,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + } + + l2cap_chan_unlock(chan); ++ l2cap_chan_put(chan); + + return err; + } +-- +2.53.0 + diff --git a/queue-6.6/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch b/queue-6.6/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch new file mode 100644 index 0000000000..3bc53fe7d2 --- /dev/null +++ b/queue-6.6/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch @@ -0,0 +1,77 @@ +From 8b0763779df5af54d34312f51e3589b567165f7c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 3 Jul 2026 07:54:40 +0200 +Subject: btrfs: zoned: fix deadlock between metadata writeback and transaction + commit + +From: Johannes Thumshirn + +[ Upstream commit 1ebe51c29fa9755d5b2fea28727c051117907cf8 ] + +When writing out metadata extent buffers in a zoned filesystem, +btree_writepages() holds fs_info->zoned_meta_io_lock across the whole +writeback loop, including the call to btrfs_check_meta_write_pointer() -> +check_bg_is_active(). + +For the tree-log block group, check_bg_is_active() may fail to activate +the zone and fall back to btrfs_zone_finish_one_bg() to free an active +zone. That path waits for the running transaction to commit while still +holding zoned_meta_io_lock, but the committer needs that same lock to +write out the tree extents, so the two tasks deadlock: + + Task A (kworker, metadata writeback) Task B (fsstress, transaction commit) + ------------------------------------ ------------------------------------- + wb_workfn() btrfs_commit_transaction(T) + btree_writepages() btrfs_write_and_wait_transaction() + btrfs_zoned_meta_io_lock() btrfs_write_marked_extents() + btrfs_check_meta_write_pointer() btree_writepages() + check_bg_is_active() [treelog_bg] btrfs_zoned_meta_io_lock() + btrfs_zone_finish_one_bg() + do_zone_finish() + btrfs_inc_block_group_ro() + btrfs_wait_for_commit() + + +The sibling branch in check_bg_is_active() already drops zoned_meta_io_lock +around do_zone_finish() for this exact reason. Do the same in the tree-log +branch: release the lock around btrfs_zone_finish_one_bg() and re-acquire +it afterwards. The lock only protects fs_info->active_{meta,system}_bg, +which this branch does not touch, and ctx->zoned_bg keeps a reference to +the block group across the unlock, so nothing is lost while the lock +is dropped. + +This hang occasionally reproduces with fstests generic/475 on a zoned +btrfs filesystem. + +Fixes: 13bb483d32ab ("btrfs: zoned: activate metadata block group on write time") +Reviewed-by: Naohiro Aota +Signed-off-by: Johannes Thumshirn +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/zoned.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c +index dabbfc3b7d776..9962ae292b128 100644 +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -1775,7 +1775,11 @@ static bool check_bg_is_active(struct btrfs_eb_write_context *ctx, + + if (fs_info->treelog_bg == block_group->start) { + if (!btrfs_zone_activate(block_group)) { +- int ret_fin = btrfs_zone_finish_one_bg(fs_info); ++ int ret_fin; ++ ++ btrfs_zoned_meta_io_unlock(fs_info); ++ ret_fin = btrfs_zone_finish_one_bg(fs_info); ++ btrfs_zoned_meta_io_lock(fs_info); + + if (ret_fin != 1 || !btrfs_zone_activate(block_group)) + return false; +-- +2.53.0 + diff --git a/queue-6.6/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-6.6/can-isotp-check-register_netdevice_notifier-error-in.patch new file mode 100644 index 0000000000..a1572d0855 --- /dev/null +++ b/queue-6.6/can-isotp-check-register_netdevice_notifier-error-in.patch @@ -0,0 +1,57 @@ +From fb80dcd526d3eed4c6890dd7d5d05a413ac51504 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Jul 2026 16:56:56 +0800 +Subject: can: isotp: check register_netdevice_notifier() error in module init + +From: Minhong He + +[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ] + +Register the netdevice notifier before can_proto_register() and check the +return value. If protocol registration fails, unregister the notifier +before returning the error. + +Align isotp_module_init() with the reordering already done for raw.c +(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and +bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization +in bcm_module_init()")). + +Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier") +Signed-off-by: Minhong He +Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn +Signed-off-by: Marc Kleine-Budde +Signed-off-by: Sasha Levin +--- + net/can/isotp.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/net/can/isotp.c b/net/can/isotp.c +index efc5eeac7c886..062b7bbf1b098 100644 +--- a/net/can/isotp.c ++++ b/net/can/isotp.c +@@ -1883,13 +1883,18 @@ static __init int isotp_module_init(void) + + pr_info("can: isotp protocol (max_pdu_size %d)\n", max_pdu_size); + ++ err = register_netdevice_notifier(&canisotp_notifier); ++ if (err) ++ return err; ++ + err = can_proto_register(&isotp_can_proto); +- if (err < 0) ++ if (err < 0) { + pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err)); +- else +- register_netdevice_notifier(&canisotp_notifier); ++ unregister_netdevice_notifier(&canisotp_notifier); ++ return err; ++ } + +- return err; ++ return 0; + } + + static __exit void isotp_module_exit(void) +-- +2.53.0 + diff --git a/queue-6.6/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch b/queue-6.6/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch new file mode 100644 index 0000000000..5a89e28b1a --- /dev/null +++ b/queue-6.6/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch @@ -0,0 +1,65 @@ +From 5533a5654a25ef9b1932837db1f355164faabf99 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 25 May 2026 10:15:50 -0400 +Subject: dmaengine: idxd: fix fdev setup failure cleanup in idxd_cdev_open() + +From: Yuho Choi + +[ Upstream commit ee1d7274102285d78a53161fc705a8d8cd40b066 ] + +The failed_dev_add and failed_dev_name paths drop the file-device +reference while wq->wq_lock is still held. If put_device(fdev) drops the +last reference, idxd_file_dev_release() runs synchronously and tries to +take wq->wq_lock again, deadlocking. + +Those paths also fall through into the later ctx cleanup labels even +though idxd_file_dev_release() owns that cleanup and frees ctx. This can +make idxd_xa_pasid_remove(ctx) and kfree(ctx) operate on a freed context. + +Move idxd_wq_get() before file-device setup can fail, since the release +callback always calls idxd_wq_put(). Then unlock wq->wq_lock before +put_device(fdev) and return directly from the file-device setup failure +path, leaving ctx cleanup to the release callback. + +Fixes: e6fd6d7e5f0fe ("dmaengine: idxd: add a device to represent the file opened") +Signed-off-by: Yuho Choi +Reviewed-by: Dave Jiang +Acked-by: Vinicius Costa Gomes +Link: https://patch.msgid.link/20260525141550.1385581-1-dbgh9129@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/dma/idxd/cdev.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c +index 5ded4a0887bc8..e885d5af485b2 100644 +--- a/drivers/dma/idxd/cdev.c ++++ b/drivers/dma/idxd/cdev.c +@@ -293,6 +293,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) + fdev->parent = cdev_dev(idxd_cdev); + fdev->bus = &dsa_bus_type; + fdev->type = &idxd_cdev_file_type; ++ idxd_wq_get(wq); + + rc = dev_set_name(fdev, "file%d", ctx->id); + if (rc < 0) { +@@ -306,13 +307,14 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) + goto failed_dev_add; + } + +- idxd_wq_get(wq); + mutex_unlock(&wq->wq_lock); + return 0; + + failed_dev_add: + failed_dev_name: ++ mutex_unlock(&wq->wq_lock); + put_device(fdev); ++ return rc; + failed_ida: + failed_set_pasid: + if (device_user_pasid_enabled(idxd)) +-- +2.53.0 + diff --git a/queue-6.6/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-6.6/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch new file mode 100644 index 0000000000..4e77072345 --- /dev/null +++ b/queue-6.6/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch @@ -0,0 +1,60 @@ +From 8d45d1a2e5401bb9a6f2ea98c7fcfc3d536e0c68 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 1 Jul 2026 12:57:33 +0800 +Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA + +From: Hongling Zeng + +[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ] + +When terminating DMA transfers, active descriptors are not properly +reclaimed. Only cyclic descriptors were handled, leaving non-cyclic +descriptors and their LLI chains to be permanently leaked. + +Fix by using vchan_terminate_vdesc() which handles both cyclic and +non-cyclic descriptors by adding them to desc_terminated queue for +proper cleanup. + +Add pchan->desc != pchan->done check to prevent double-adding completed +descriptors, which would corrupt the list. + +Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller") +Signed-off-by: Hongling Zeng +Acked-by: Jernej Skrabec +Suggested-by: Frank Li +Reviewed-by: Frank Li +Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/dma/sun6i-dma.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c +index 6a384bd469528..f1d5af20363e9 100644 +--- a/drivers/dma/sun6i-dma.c ++++ b/drivers/dma/sun6i-dma.c +@@ -934,16 +934,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan) + + spin_lock_irqsave(&vchan->vc.lock, flags); + +- if (vchan->cyclic) { +- vchan->cyclic = false; +- if (pchan && pchan->desc) { +- struct virt_dma_desc *vd = &pchan->desc->vd; +- struct virt_dma_chan *vc = &vchan->vc; ++ if (pchan && pchan->desc && pchan->desc != pchan->done) { ++ struct virt_dma_desc *vd = &pchan->desc->vd; + +- list_add_tail(&vd->node, &vc->desc_completed); +- } ++ vchan_terminate_vdesc(vd); + } + ++ vchan->cyclic = false; + vchan_get_all_descriptors(&vchan->vc, &head); + + if (pchan) { +-- +2.53.0 + diff --git a/queue-6.6/drm-mediatek-check-crtc-state-before-freeing.patch b/queue-6.6/drm-mediatek-check-crtc-state-before-freeing.patch new file mode 100644 index 0000000000..0206406d2e --- /dev/null +++ b/queue-6.6/drm-mediatek-check-crtc-state-before-freeing.patch @@ -0,0 +1,51 @@ +From 78be7ac2127f21a78ee5a4322856fa8d5b2d98d0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 23:05:28 +0800 +Subject: drm/mediatek: Check CRTC state before freeing + +From: Ruoyu Wang + +[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ] + +mtk_crtc_reset() destroys the current CRTC state only when crtc->state +is non-NULL, but it always converts crtc->state to struct mtk_crtc_state +and passes the result to kfree(). + +When reset is called without an existing state, container_of(NULL, ...) +does not produce NULL. Keep the mtk state free in the same crtc->state +guard as the helper state destruction. + +This issue was found by a static analysis checker and confirmed by +manual source review. + +Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset") +Signed-off-by: Ruoyu Wang +Reviewed-by: CK Hu +Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/ +Signed-off-by: Chun-Kuang Hu +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +index 659112da47b69..859dffe451372 100644 +--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c ++++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +@@ -185,10 +185,10 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc) + { + struct mtk_crtc_state *state; + +- if (crtc->state) ++ if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); +- +- kfree(to_mtk_crtc_state(crtc->state)); ++ kfree(to_mtk_crtc_state(crtc->state)); ++ } + crtc->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); +-- +2.53.0 + diff --git a/queue-6.6/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-6.6/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch new file mode 100644 index 0000000000..0f172af140 --- /dev/null +++ b/queue-6.6/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch @@ -0,0 +1,47 @@ +From 41f1c155659740af4169e5862cba532b3c773310 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 17:26:37 +0800 +Subject: forcedeth: fix UAF of txrx_stats in nv_remove + +From: Chenguang Zhao + +[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ] + +nv_remove() frees the per-CPU txrx_stats before unregister_netdev(). +Until unregister completes, ndo_get_stats64, the NAPI/xmit data path, +and nv_close()/drain may still access txrx_stats, leading to a +use-after-free. + +Free the stats only after unregister_netdev(). + +Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Reviewed-by: Zhu Yanjun +Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/nvidia/forcedeth.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c +index 7a549b834e970..0e9afeb070ba1 100644 +--- a/drivers/net/ethernet/nvidia/forcedeth.c ++++ b/drivers/net/ethernet/nvidia/forcedeth.c +@@ -6199,10 +6199,10 @@ static void nv_remove(struct pci_dev *pci_dev) + struct net_device *dev = pci_get_drvdata(pci_dev); + struct fe_priv *np = netdev_priv(dev); + +- free_percpu(np->txrx_stats); +- + unregister_netdev(dev); + ++ free_percpu(np->txrx_stats); ++ + nv_restore_mac_addr(pci_dev); + + /* restore any phy related changes */ +-- +2.53.0 + diff --git a/queue-6.6/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-6.6/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch new file mode 100644 index 0000000000..2a8677fb1a --- /dev/null +++ b/queue-6.6/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch @@ -0,0 +1,51 @@ +From 0eb162fce060e96fc6bc0a324f3f7acc1fca548b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:19 -0300 +Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread + +From: Luiz Angelo Daros de Luca + +[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ] + +When userspace configures 'auto_update_interval' to 0 via sysfs, the +background kthread executes schedule_timeout_interruptible(0), which +returns immediately. + +If 'num_temp_sensors' is concurrently or previously set to 0, the +msleep_interruptible() delay inside adt7470_read_temperatures() also +becomes 0. This combination forces the background thread into a tight, +unbounded busy-loop, hogging the CPU and flooding the I2C bus with a +continuous stream of transactions. + +Fix this vulnerability by raising the lower limit of the clamp_val in +auto_update_interval_store() from 0 to 500 milliseconds. This guarantees +a reasonable minimum sleep window between sensor updates, protecting the +system from intentional or accidental I2C bus denial of service. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org +Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 3d4b4b6bd47fd..06d60f76cabbd 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev, + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + +- temp = clamp_val(temp, 0, 60000); ++ temp = clamp_val(temp, 500, 60000); + + mutex_lock(&data->lock); + data->auto_update_interval = temp; +-- +2.53.0 + diff --git a/queue-6.6/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch b/queue-6.6/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch new file mode 100644 index 0000000000..03f9dbbe8f --- /dev/null +++ b/queue-6.6/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch @@ -0,0 +1,83 @@ +From 6f6fd689ec260a24bcbde113fc94acb5f48c327d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:18 -0300 +Subject: hwmon: (adt7470) Fix cache updated before hardware write on I2C error + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ] + +adt7470_temp_write() and adt7470_pwm_write() update the driver's +cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing +the corresponding regmap_write(), and never check whether the write +succeeded before committing that update. If the I2C transaction fails, +the function correctly propagates the error to the caller, but the cache +silently keeps the new value, which was never actually applied to the +hardware. Subsequent reads then report a value that does not match the +device state. + +Reorder both write paths to update the cache only after a successful +regmap_write(), so the cache always reflects what was actually +written to the hardware. + +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 0e0d2236dc536..3d4b4b6bd47fd 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va + switch (attr) { + case hwmon_temp_min: + mutex_lock(&data->lock); +- data->temp_min[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val); ++ if (!err) ++ data->temp_min[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_temp_max: + mutex_lock(&data->lock); +- data->temp_max[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val); ++ if (!err) ++ data->temp_max[channel] = val; + mutex_unlock(&data->lock); + break; + default: +@@ -839,9 +841,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + case hwmon_pwm_input: + val = clamp_val(val, 0, 255); + mutex_lock(&data->lock); +- data->pwm[channel] = val; + err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), +- data->pwm[channel]); ++ val); ++ if (!err) ++ data->pwm[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_pwm_enable: +@@ -855,10 +858,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + val--; + + mutex_lock(&data->lock); +- data->pwm_automatic[channel] = val; + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel), + pwm_auto_reg_mask, + val ? pwm_auto_reg_mask : 0); ++ if (!err) ++ data->pwm_automatic[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_pwm_freq: +-- +2.53.0 + diff --git a/queue-6.6/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch b/queue-6.6/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch new file mode 100644 index 0000000000..6782d3a1cc --- /dev/null +++ b/queue-6.6/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch @@ -0,0 +1,80 @@ +From 187f348030c9fd91015fdd52e5e6a45f7ad2e7a5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:23 -0300 +Subject: hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ] + +If the fan data becomes 0 between the FAN_DATA_VALID() check and the +FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash +due to a race with a concurrent update of the cached fan value. + +Fix a TOCTOU issue by reading fan data once. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/ +Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 23 ++++++++++------------- + 1 file changed, 10 insertions(+), 13 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 0ec0f1e4dcbc9..7b9cb44749d95 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev, + static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val) + { + struct adt7470_data *data = adt7470_update_device(dev); ++ u16 fan_data; + + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (attr) { + case hwmon_fan_input: +- if (FAN_DATA_VALID(data->fan[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan[channel]); + break; + case hwmon_fan_min: +- if (FAN_DATA_VALID(data->fan_min[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan_min[channel]); + break; + case hwmon_fan_max: +- if (FAN_DATA_VALID(data->fan_max[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan_max[channel]); + break; + case hwmon_fan_alarm: + *val = !!(data->alarm & FAN_ALARM_BIT(channel)); +- break; ++ return 0; + default: + return -EOPNOTSUPP; + } + ++ if (FAN_DATA_VALID(fan_data)) ++ *val = FAN_PERIOD_TO_RPM(fan_data); ++ else ++ *val = 0; ++ + return 0; + } + +-- +2.53.0 + diff --git a/queue-6.6/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch b/queue-6.6/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch new file mode 100644 index 0000000000..987234cfe8 --- /dev/null +++ b/queue-6.6/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch @@ -0,0 +1,115 @@ +From e8e3ff132e8ebb3f2b5fdd49ef3ff7a0677db9bc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:17 -0300 +Subject: hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ] + +During adt7470_read_temperatures(), the driver temporarily switches +the PWM channels to manual mode, performs the temperature collection, +and then restores the original configuration registers. + +However, if an I2C transaction fails at any point after entering manual +mode, the function aborts and returns immediately. This leaves the +configuration registers un-restored, permanently trapping the fans in +manual mode. + +Introduce a recovery path to ensure that the original PWM configuration +registers are always restored, even when intermediate I2C operations +fail. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++----------- + 1 file changed, 29 insertions(+), 11 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index c4b3a4a18670c..0e0d2236dc536 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in + /* Probe for temperature sensors. Assumes lock is held */ + static int adt7470_read_temperatures(struct adt7470_data *data) + { +- unsigned long res; ++ struct device *dev = regmap_get_device(data->regmap); ++ u8 pwm[ADT7470_FAN_COUNT]; + unsigned int pwm_cfg[2]; +- int err; ++ unsigned long res; ++ int err, err2; + int i; +- u8 pwm[ADT7470_FAN_COUNT]; + + /* save pwm[1-4] config register */ + err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); +@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data) + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), + ADT7470_PWM_AUTO_MASK, 0); + if (err < 0) +- return err; ++ goto out_restore; + + /* write pwm control to whatever it was */ + err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], + ADT7470_PWM_COUNT); + if (err < 0) +- return err; ++ goto out_restore; + + /* start reading temperature sensors */ + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); + if (err < 0) +- return err; ++ goto out_restore; + + /* Delay is 200ms * number of temp sensors. */ + res = msleep_interruptible((data->num_temp_sensors >= 0 ? +@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data) + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, 0); + if (err < 0) +- return err; ++ goto out_restore; + ++out_restore: + /* restore pwm[1-4] config registers */ +- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); +- if (err < 0) +- return err; +- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); ++ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); ++ if (err2 < 0) { ++ dev_warn_ratelimited(dev, ++ "failed to restore PWM{1,2} config (%d)\n", ++ err2); ++ ++ if (!err) ++ err = err2; ++ } ++ ++ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); ++ if (err2 < 0) { ++ dev_warn_ratelimited(dev, ++ "failed to restore PWM{3,4} config (%d)\n", ++ err2); ++ ++ if (!err) ++ err = err2; ++ } ++ + if (err < 0) + return err; + +-- +2.53.0 + diff --git a/queue-6.6/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-6.6/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch new file mode 100644 index 0000000000..f0dc6fe9e0 --- /dev/null +++ b/queue-6.6/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch @@ -0,0 +1,57 @@ +From 530af8388256bccec097091207b04260ef11e6a8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:24 -0300 +Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ] + +In pwm_auto_temp_store(), the parsed user input was missing bounds +checks, allowing values > 0xF to overflow into the adjacent channel's +bits. Furthermore, the value was being incorrectly written to the +pwm_automatic state array instead of pwm_auto_temp. + +Fix this by rejecting values > 0xF with -EINVAL, and assigning the +value to the correct array only after a successful I2C write. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 7b9cb44749d95..3ced54988f549 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -1057,8 +1057,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + if (temp < 0) + return temp; + ++ if (temp > 0xF) ++ return -EINVAL; ++ + mutex_lock(&data->lock); +- data->pwm_automatic[attr->index] = temp; + + if (!(attr->index % 2)) { + mask = 0xF0; +@@ -1069,6 +1071,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + } + + err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); ++ if (!err) ++ data->pwm_auto_temp[attr->index] = temp; ++ + mutex_unlock(&data->lock); + + return err < 0 ? err : count; +-- +2.53.0 + diff --git a/queue-6.6/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-6.6/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch new file mode 100644 index 0000000000..229f6d8b86 --- /dev/null +++ b/queue-6.6/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch @@ -0,0 +1,62 @@ +From 1ea0f1e6ba308752077742be6223781f14ffb5ab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:20 -0300 +Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks + +From: Luiz Angelo Daros de Luca + +[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ] + +The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are +currently defined with swapped bit values. + +According to Table 22 of the ADT7470 datasheet, the Fan Control Mode +Configuration for register 0x69 follows the exact same bit position +layout as register 0x68: +- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80 +- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40 +- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80 +- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40 + +Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40. + +This typo did not cause any functional bugs because these specific +macros are never referenced in the driver code. Instead, the driver +correctly applies the configuration by relying on the modulo parity of +the channel index (e.g., `channel % 2`) to selectively apply either +ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40). +Since the bit layout is identical between the two configuration +registers, the hardware is currently configured correctly. + +Fix the macro definitions to reflect the datasheet accurately and +prevent future bugs or confusion during code review and refactoring. +As this is a purely cosmetic fix with no functional impact, a backport +to stable kernels is not necessary. + +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index b63e3a0f6c135..76f7efae54e07 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + #define ADT7470_PWM1_AUTO_MASK 0x80 + #define ADT7470_PWM_AUTO_MASK 0xC0 + #define ADT7470_REG_PWM34_CFG 0x69 +-#define ADT7470_PWM3_AUTO_MASK 0x40 +-#define ADT7470_PWM4_AUTO_MASK 0x80 ++#define ADT7470_PWM4_AUTO_MASK 0x40 ++#define ADT7470_PWM3_AUTO_MASK 0x80 + #define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A + #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D + #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E +-- +2.53.0 + diff --git a/queue-6.6/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch b/queue-6.6/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch new file mode 100644 index 0000000000..4f6b494f22 --- /dev/null +++ b/queue-6.6/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch @@ -0,0 +1,71 @@ +From 934a7c9959731da6f687f20b247c65d6201e2452 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:21 -0300 +Subject: hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read() + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ] + +During the conversion the alarm callback started interpreting the +channel index as an alarm bitmask, resulting in incorrect alarm +reporting. Compute the proper alarm bit instead. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org +Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 19 +++++++++++++++++-- + 1 file changed, 17 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 06d60f76cabbd..b63e3a0f6c135 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + + #define ALARM2(x) ((x) << 8) + ++/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and ++ * 0x72, or BIT(0)..BIT(6) of data->alarm. ++ * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and ++ * 0x73, or BIT(8)..BIT(10) of data->alarm. ++ */ ++#define TEMP_ALARM_BIT(ch) ({ \ ++ typeof(ch) _ch = (ch); \ ++ (1 << (_ch < 7 ? _ch : _ch + 1)); \ ++}) ++ ++/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in ++ * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm. ++ */ ++#define FAN_ALARM_BIT(ch) (1 << (12 + (ch))) ++ + #define ADT7470_VENDOR 0x41 + #define ADT7470_DEVICE 0x70 + /* datasheet only mentions a revision 2 */ +@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va + *val = 1000 * data->temp_max[channel]; + break; + case hwmon_temp_alarm: +- *val = !!(data->alarm & channel); ++ *val = !!(data->alarm & TEMP_ALARM_BIT(channel)); + break; + default: + return -EOPNOTSUPP; +@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val + *val = 0; + break; + case hwmon_fan_alarm: +- *val = !!(data->alarm & (1 << (12 + channel))); ++ *val = !!(data->alarm & FAN_ALARM_BIT(channel)); + break; + default: + return -EOPNOTSUPP; +-- +2.53.0 + diff --git a/queue-6.6/hwmon-adt7470-use-cached-pwm-frequency-value.patch b/queue-6.6/hwmon-adt7470-use-cached-pwm-frequency-value.patch new file mode 100644 index 0000000000..03c0916646 --- /dev/null +++ b/queue-6.6/hwmon-adt7470-use-cached-pwm-frequency-value.patch @@ -0,0 +1,111 @@ +From c7f278a739940757df68f7a6638aec10c3601c4e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:22 -0300 +Subject: hwmon: (adt7470) Use cached PWM frequency value + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ] + +adt7470_pwm_read() currently ignores failures returned by +pwm1_freq_get(). If the register read fails, the negative error code is +returned through *val while the function itself reports success, +potentially exposing a negative PWM frequency through sysfs. + +Fix this by using the cached PWM frequency maintained by the driver, +eliminating the register access from the read path. + +Apart from the corrected error propagation and using the cached value, +no functional change is intended. + +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 76f7efae54e07..0ec0f1e4dcbc9 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -182,6 +182,7 @@ struct adt7470_data { + u8 pwm_min[ADT7470_PWM_COUNT]; + s8 pwm_tmin[ADT7470_PWM_COUNT]; + u8 pwm_auto_temp[ADT7470_PWM_COUNT]; ++ u32 pwm_freq; + + struct task_struct *auto_update; + unsigned int auto_update_interval; +@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev, + } + + /* These are the valid PWM frequencies to the nearest Hz */ +-static const int adt7470_freq_map[] = { ++static const u32 adt7470_freq_map[] = { + 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 + }; + +@@ -804,7 +805,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val + *val = 1 + data->pwm_automatic[channel]; + break; + case hwmon_pwm_freq: +- *val = pwm1_freq_get(dev); ++ *val = data->pwm_freq; + break; + default: + return -EOPNOTSUPP; +@@ -817,12 +818,14 @@ static int pwm1_freq_set(struct device *dev, long freq) + { + struct adt7470_data *data = dev_get_drvdata(dev); + unsigned int low_freq = ADT7470_CFG_LF; ++ u32 closest_freq; + int index; + int err; + + /* Round the user value given to the closest available frequency */ + index = find_closest(freq, adt7470_freq_map, + ARRAY_SIZE(adt7470_freq_map)); ++ closest_freq = adt7470_freq_map[index]; + + if (index >= 8) { + index -= 8; +@@ -840,6 +843,10 @@ static int pwm1_freq_set(struct device *dev, long freq) + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, + ADT7470_FREQ_MASK, + index << ADT7470_FREQ_SHIFT); ++ if (err < 0) ++ goto out; ++ ++ data->pwm_freq = closest_freq; + out: + mutex_unlock(&data->lock); + +@@ -1293,6 +1300,7 @@ static int adt7470_probe(struct i2c_client *client) + struct device *dev = &client->dev; + struct adt7470_data *data; + struct device *hwmon_dev; ++ int freq_val; + int err; + + data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); +@@ -1317,6 +1325,14 @@ static int adt7470_probe(struct i2c_client *client) + if (err < 0) + return err; + ++ freq_val = pwm1_freq_get(dev); ++ if (freq_val <= 0) { ++ err = freq_val < 0 ? freq_val : -EINVAL; ++ return err; ++ } ++ ++ data->pwm_freq = (u32)freq_val; ++ + /* Register sysfs hooks */ + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &adt7470_chip_info, +-- +2.53.0 + diff --git a/queue-6.6/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch b/queue-6.6/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch new file mode 100644 index 0000000000..7b2b0e5853 --- /dev/null +++ b/queue-6.6/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch @@ -0,0 +1,54 @@ +From 278ba8f3d6dbe09eca15240515e6937021d5ba9d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 15:27:28 -0700 +Subject: hwmon: (lm90) Only report alarms if driver is ready + +From: Guenter Roeck + +[ Upstream commit aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 ] + +Userspace can read sysfs attributes before driver registration is complete, +immediately after devm_hwmon_device_register_with_info() has been called. +At that time, data->hwmon_dev is not yet initialized. This can trigger +a NULL pointer access since lm90_update_device() and with it +lm90_update_alarms_locked() will be called. This call schedules +report_work and lm90_report_alarms(), which passes the still-NULL +data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer +dereference. + +Fix the problem by only scheduling the report and alert workers +data->hwmon_dev is set. + +Reported-by: Sashiko +Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/lm90.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c +index e0d7454a301cf..7be9874b3dfb1 100644 +--- a/drivers/hwmon/lm90.c ++++ b/drivers/hwmon/lm90.c +@@ -1149,7 +1149,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) + check_enable = (client->irq || !(data->config_orig & 0x80)) && + (data->config & 0x80); + +- if (force || check_enable) ++ if (data->hwmon_dev && (force || check_enable)) + schedule_work(&data->report_work); + + /* +@@ -1157,7 +1157,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) + * alarms are all clear, and alerts are currently disabled. + * Otherwise (re)schedule worker if needed. + */ +- if (check_enable) { ++ if (check_enable && data->hwmon_dev) { + if (!(data->current_alarms & data->alert_alarms)) { + dev_dbg(&client->dev, "Re-enabling ALERT#\n"); + lm90_update_confreg(data, data->config & ~0x80); +-- +2.53.0 + diff --git a/queue-6.6/hwmon-nct6775-core-fix-number-of-temperature-registe.patch b/queue-6.6/hwmon-nct6775-core-fix-number-of-temperature-registe.patch new file mode 100644 index 0000000000..296417cd28 --- /dev/null +++ b/queue-6.6/hwmon-nct6775-core-fix-number-of-temperature-registe.patch @@ -0,0 +1,90 @@ +From 54df4a21e0e9511f0cc77cd48bef097a405382b2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 07:14:36 -0700 +Subject: hwmon: (nct6775-core) Fix number of temperature registers for NCT6116 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guenter Roeck + +[ Upstream commit b0e8adb2ccb43009796897ced09f91636685c9d3 ] + +Unlike NCT6106, NCT6116 only has three temperature registers, and with +it only three temperature source and temperature source configuration +registers. The register addresses match those of NCT6106 and can be +re-used. + +The code used a separate array to list the temperature source registers +for NCT6116, but used the size of the NCT6106 register array to set +the number of registers. The NCT6106 register array provides six addresses, +while the temperature source register array for NCT6116 only provides three +addresses. This causes a KASAN report. + +BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775] +Read of size 2 at addr ffffffffc19561a6 by task modprobe/954 +... +Call Trace: + dump_stack+0x7d/0xa7 + print_address_description.constprop.0+0x1c/0x220 + ? __kasan_kmalloc.constprop.0+0xc9/0xd0 + ? __kmalloc_node_track_caller+0x194/0x5b0 + ? nct6775_probe+0x936/0x46f0 [nct6775] + ? nct6775_probe+0x936/0x46f0 [nct6775] +... + +Fix the problem by hard-coding the number of temperature and temperature +configuration registers to three for NCT6116. Drop the unnecessary +NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE. + +Reported-by: Florian Bezdeka +Closes: https://lore.kernel.org/linux-hwmon/57cfc3fa-d4e9-4c10-8aa7-4ad0af7ebebe@roeck-us.net/T/#t +Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") +Cc: Björn Gerhart +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index da4c3425d2d1d..d47ba7cd97727 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -846,8 +846,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 }; + static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 }; + static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 }; + static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 }; +-static const u16 NCT6116_REG_TEMP_SOURCE[] = { +- 0xb0, 0xb1, 0xb2 }; + + static const u16 NCT6116_REG_CRITICAL_TEMP[] = { + 0x11a, 0x12a, 0x13a, 0x19a, 0x1aa }; +@@ -3650,7 +3648,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + = NCT6106_CRITICAL_PWM_ENABLE_MASK; + data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM; + data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; +- data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE; ++ data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; +@@ -3664,13 +3662,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + + reg_temp = NCT6106_REG_TEMP; + reg_temp_mon = NCT6106_REG_TEMP_MON; +- num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); ++ num_reg_temp = 3; + num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON); + num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP); + reg_temp_over = NCT6106_REG_TEMP_OVER; + reg_temp_hyst = NCT6106_REG_TEMP_HYST; + reg_temp_config = NCT6106_REG_TEMP_CONFIG; +- num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG); ++ num_reg_temp_config = 3; + reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6106_REG_TEMP_CRIT; + reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; +-- +2.53.0 + diff --git a/queue-6.6/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-6.6/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch new file mode 100644 index 0000000000..071d0635e9 --- /dev/null +++ b/queue-6.6/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch @@ -0,0 +1,74 @@ +From d005dd7e5147d0c9f30ce487b55425b8227abb86 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 13:35:37 -0700 +Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guenter Roeck + +[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ] + +Sashiko reports: + +During initialization of the nct6116 chip, the driver sets data->pwm_num +to 5. However, it assigns several NCT6106 register arrays (such as +NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and +NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP. +These arrays only contain 3 elements. + +In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If +data->has_pwm has bits 3 or 4 set (which is structurally possible for +nct6116), the loop attempts to read elements at index 3 and 4 from these +3-element arrays. This results in a global out-of-bounds read, which can +be caught by KASAN. + +Furthermore, the driver uses these garbage out-of-bounds values as +hardware register addresses for subsequent read and write operations. This +leads to invalid hardware register access, potentially causing hardware +misconfiguration or system crashes. + +The underlying problem is that the chip does support up to five fan +control channels, but only the first three support weight control. +Fix the problem by extending the affected weight register arrays with +zeroed fields. The driver uses zeroed register addresses to determine +if a register is supported or not, and skips accesses for unsupported +registers. + +Reported-by: Sashiko +Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") +Cc: Björn Gerhart +Cc: Florian Bezdeka +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index d47ba7cd97727..6d28cf101f5f1 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -791,12 +791,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 }; + + static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 }; + +-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 }; + + static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 }; + static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 }; +-- +2.53.0 + diff --git a/queue-6.6/hwmon-nzxt-smart2-dma-align-output-buffer.patch b/queue-6.6/hwmon-nzxt-smart2-dma-align-output-buffer.patch new file mode 100644 index 0000000000..b54e6ae26d --- /dev/null +++ b/queue-6.6/hwmon-nzxt-smart2-dma-align-output-buffer.patch @@ -0,0 +1,53 @@ +From 160559e5a589db174ad0747414aeac0955fe9946 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 09:54:23 -0700 +Subject: hwmon: (nzxt-smart2) DMA-align output buffer + +From: Guenter Roeck + +[ Upstream commit 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 ] + +Sashiko reports: + +When send_output_report() calls hid_hw_output_report(), the underlying USB +HID core calls usb_interrupt_msg() which maps this buffer directly for DMA. + +When the DMA mapping flushes or invalidates the cacheline, it will corrupt +the adjacent variables (mutex, update_interval) that were modified +concurrently by the CPU. This causes memory corruption due to cacheline +sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA +API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings +for this violation. + +Any operation that triggers send_output_report() (like setting a fan speed +or updating the interval) causes the USB DMA mapping. On systems with +non-coherent caches, this structural bug causes immediate and deterministic +memory corruption. + +Align the output buffer to ARCH_DMA_MINALIGN to fix the problem. + +Reported-by: Sashiko +Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.") +Cc: Aleksandr Mezin +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nzxt-smart2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c +index 0ce956954378d..5bbe6f3f8af48 100644 +--- a/drivers/hwmon/nzxt-smart2.c ++++ b/drivers/hwmon/nzxt-smart2.c +@@ -203,7 +203,7 @@ struct drvdata { + */ + struct mutex mutex; + long update_interval; +- u8 output_buffer[OUTPUT_REPORT_SIZE]; ++ u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN); + }; + + static long scale_pwm_value(long val, long orig_max, long new_max) +-- +2.53.0 + diff --git a/queue-6.6/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-6.6/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch new file mode 100644 index 0000000000..48d742bcdf --- /dev/null +++ b/queue-6.6/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch @@ -0,0 +1,39 @@ +From 8383be2aca2eec42e2e8aee9948c2ad4bcda61ef Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 08:41:40 -0700 +Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data() + +From: Guenter Roeck + +[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ] + +pmbus_update_byte_data() is supposed to return a negative error code or 0. +However, if no change is made to the register, it actually returns the +register value. This can result in problems if the calling code explicitly +expects to see an error code or 0. + +Fix it to return 0 on success or the error code as expected. + +Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/pmbus/pmbus_core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c +index eafeabfd93d77..2cf5f1fe8886c 100644 +--- a/drivers/hwmon/pmbus/pmbus_core.c ++++ b/drivers/hwmon/pmbus/pmbus_core.c +@@ -449,7 +449,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + if (tmp != rv) + rv = _pmbus_write_byte_data(client, page, reg, tmp); + +- return rv; ++ return rv < 0 ? rv : 0; + } + EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS); + +-- +2.53.0 + diff --git a/queue-6.6/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch b/queue-6.6/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch new file mode 100644 index 0000000000..9b67cdd25e --- /dev/null +++ b/queue-6.6/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch @@ -0,0 +1,335 @@ +From ff41a18936804d7d41f102b8e82ed388ebe979c0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:17 +0300 +Subject: ipvs: do not mangle ICMP replies for non-first fragments + +From: Julian Anastasov + +[ Upstream commit 342e24a339b90e8e339a0f8c151ca479b8565661 ] + +Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the +payload for embedded non-first IPv4 fragments. The problem is +in the very old inverted pp->dont_defrag check which should not +continue when embedded is a non-first TCP/UDP/SCTP fragment. + +Check for embedded non-first fragment is also missing from +ip_vs_out_icmp_v6(), it is needed before any connection +lookups that expect ports after the network headers. + +Drop the blocking code from ip_vs_in_icmp_v6() which prevents +ICMPv6 from local clients to use non-MASQ forwarding. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Link: https://sashiko.dev/#/patchset/20260720201122.79882-1-ja%40ssi.bg +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 11 +++--- + net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++--------------------- + net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++---- + 3 files changed, 48 insertions(+), 52 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index a44979db80134..6935ec09af24d 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -1627,8 +1627,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph); ++ unsigned int hooknum, struct ip_vs_iphdr *ciph); + void ip_vs_dest_dst_rcu_free(struct rcu_head *head); + + #ifdef CONFIG_IP_VS_IPV6 +@@ -1642,8 +1641,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph); ++ unsigned int hooknum, struct ip_vs_iphdr *ciph); + #endif + + #ifdef CONFIG_SYSCTL +@@ -1708,12 +1706,13 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) + } + + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir, unsigned int toff); ++ struct ip_vs_conn *cp, int dir, unsigned int toff, ++ bool has_ports); + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int dir, unsigned int toff, +- struct ip_vs_iphdr *ciph); ++ bool has_ports, struct ip_vs_iphdr *ciph); + #endif + + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index d5b886ded5f11..e49a4840effb3 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -746,7 +746,8 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, + * - inout: 1=in->out, 0=out->in + */ + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout, unsigned int toff) ++ struct ip_vs_conn *cp, int inout, unsigned int toff, ++ bool has_ports) + { + struct iphdr *iph = ip_hdr(skb); + struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); +@@ -766,8 +767,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + } + + /* the TCP/UDP/SCTP port */ +- if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol || +- IPPROTO_SCTP == ciph->protocol) { ++ if (has_ports) { + __be16 *ports = (void *)ciph + ciph->ihl*4; + + if (inout) +@@ -792,18 +792,15 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int inout, unsigned int toff, +- struct ip_vs_iphdr *ciph) ++ bool has_ports, struct ip_vs_iphdr *ciph) + { + struct ipv6hdr *iph = ipv6_hdr(skb); +- int protocol; + struct icmp6hdr *icmph; + struct ipv6hdr *cih; + + icmph = (struct icmp6hdr *)(skb->data + toff); + cih = (struct ipv6hdr *)(skb->data + ciph->off); + +- protocol = ciph->protocol; +- + if (inout) { + iph->saddr = cp->vaddr.in6; + cih->daddr = cp->vaddr.in6; +@@ -813,9 +810,7 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + } + + /* the TCP/UDP/SCTP port */ +- if (!ciph->fragoffs && +- (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || +- protocol == IPPROTO_SCTP)) { ++ if (has_ports) { + __be16 *ports = (void *)(skb->data + ciph->len); + + IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, +@@ -857,6 +852,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; + unsigned int ctoff = ciph->len; ++ bool has_ports = false; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; +@@ -870,17 +866,19 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + } + + if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || +- ciph->protocol == IPPROTO_SCTP) ++ ciph->protocol == IPPROTO_SCTP) { + ctoff += 2 * sizeof(__u16); ++ has_ports = true; ++ } + if (skb_ensure_writable(skb, ctoff)) + goto out; + + #ifdef CONFIG_IP_VS_IPV6 + if (af == AF_INET6) +- ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph); + else + #endif +- ip_vs_nat_icmp(skb, pp, cp, 1, toff); ++ ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports); + + if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) + goto out; +@@ -964,8 +962,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + + /* Is the embedded protocol header present? */ +- if (unlikely(cih->frag_off & htons(IP_OFFSET) && +- pp->dont_defrag)) ++ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, +@@ -1029,6 +1026,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (!pp) + return NF_ACCEPT; + ++ /* Is the embedded protocol header present? */ ++ if (unlikely(ciph.fragoffs && !pp->dont_defrag)) ++ return NF_ACCEPT; ++ + /* The embedded headers contain source and dest in reverse order */ + cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto, + ipvs, AF_INET6, skb, &ciph); +@@ -1683,8 +1684,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + pp = pd->pp; + + /* Is the embedded protocol header present? */ +- if (unlikely(cih->frag_off & htons(IP_OFFSET) && +- pp->dont_defrag)) ++ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, +@@ -1692,7 +1692,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + + offset2 = offset; + ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph); +- offset = ciph.len; + + /* The embedded headers contain source and dest in reverse order. + * For IPIP/UDP/GRE tunnel this is error for request, not for reply. +@@ -1786,11 +1785,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); +- if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || +- IPPROTO_SCTP == cih->protocol) +- offset += 2 * sizeof(__u16); +- verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, +- &ciph); ++ verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph); + + out: + if (likely(!new_cp)) +@@ -1850,8 +1845,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + pp = pd->pp; + +- /* Cannot handle fragmented embedded protocol */ +- if (ciph.fragoffs) ++ /* Is the embedded protocol header present? */ ++ if (ciph.fragoffs && !pp->dont_defrag) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset, +@@ -1875,13 +1870,6 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + new_cp = true; + } + +- /* VS/TUN, VS/DR and LOCALNODE just let it go */ +- if ((hooknum == NF_INET_LOCAL_OUT) && +- (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) { +- verdict = NF_ACCEPT; +- goto out; +- } +- + verdict = NF_DROP; + + /* Ensure the checksum is correct */ +@@ -1897,14 +1885,7 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); + +- /* Need to mangle contained IPv6 header in ICMPv6 packet */ +- offset = ciph.len; +- if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol || +- IPPROTO_SCTP == ciph.protocol) +- offset += 2 * sizeof(__u16); /* Also mangle ports */ +- +- verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, +- &ciph); ++ verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph); + + out: + if (likely(!new_cp)) +diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c +index db562dac31c72..c214e5d05524c 100644 +--- a/net/netfilter/ipvs/ip_vs_xmit.c ++++ b/net/netfilter/ipvs/ip_vs_xmit.c +@@ -1474,13 +1474,14 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + int + ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph) ++ unsigned int hooknum, struct ip_vs_iphdr *ciph) + { + struct rtable *rt; /* Route to the other host */ + int rc; + int local; + int rt_mode, was_input; ++ bool has_ports = false; ++ unsigned int wlen; + + /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be + forwarded directly here, because there is no need to +@@ -1536,6 +1537,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + goto tx_error; + } + ++ wlen = ciph->len; ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) { ++ wlen += 2 * sizeof(__u16); /* Also mangle ports */ ++ has_ports = true; ++ } ++ + /* copy-on-write the packet before mangling it */ + if (skb_ensure_writable(skb, wlen)) + goto tx_error; +@@ -1543,7 +1551,7 @@ 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); ++ ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +@@ -1560,10 +1568,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + int + ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph) ++ unsigned int hooknum, struct ip_vs_iphdr *ciph) + { ++ bool has_ports = false; + struct rt6_info *rt; /* Route to the other host */ ++ unsigned int wlen; + int rc; + int local; + int rt_mode; +@@ -1621,6 +1630,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + goto tx_error; + } + ++ wlen = ciph->len; ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) { ++ wlen += 2 * sizeof(__u16); /* Also mangle ports */ ++ has_ports = true; ++ } ++ + /* copy-on-write the packet before mangling it */ + if (skb_ensure_writable(skb, wlen)) + goto tx_error; +@@ -1628,7 +1644,7 @@ ip_vs_icmp_xmit_v6(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_v6(skb, pp, cp, 0, toff, ciph); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +-- +2.53.0 + diff --git a/queue-6.6/ipvs-fix-places-with-wrong-packet-offsets.patch b/queue-6.6/ipvs-fix-places-with-wrong-packet-offsets.patch new file mode 100644 index 0000000000..d2c9bc5f9d --- /dev/null +++ b/queue-6.6/ipvs-fix-places-with-wrong-packet-offsets.patch @@ -0,0 +1,624 @@ +From 6e9c0a6f87c89b1136ecabe2987f0384709b16e8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:16 +0300 +Subject: ipvs: fix places with wrong packet offsets + +From: Julian Anastasov + +[ Upstream commit 15cab31a3730e05f0767b922a7450e5d784b2607 ] + +The offsets we use to packet headers and payloads should be +based on skb->data. We even already respect non-zero +network offset in ip_vs_fill_iph_skb() but some places +do it wrongly and support only zero offset which is expected +for the IP layer where IPVS has hooks. + +Change all places that instead of skb->data use offsets based +on the network header (skb_network_header, ip_hdr, etc) because +this doubles the network offset as noted by Sashiko. + +For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header +parsing done by the caller. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Link: https://sashiko.dev/#/patchset/20260710143733.29741-2-fw%40strlen.de +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 15 +-- + net/netfilter/ipvs/ip_vs_app.c | 4 +- + net/netfilter/ipvs/ip_vs_core.c | 133 +++++++++++++------------- + net/netfilter/ipvs/ip_vs_proto_sctp.c | 4 +- + net/netfilter/ipvs/ip_vs_proto_tcp.c | 4 +- + net/netfilter/ipvs/ip_vs_proto_udp.c | 4 +- + net/netfilter/ipvs/ip_vs_xmit.c | 26 ++--- + 7 files changed, 97 insertions(+), 93 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index aef042039cb00..a44979db80134 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -1626,8 +1626,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, +- unsigned int hooknum, struct ip_vs_iphdr *iph); ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph); + void ip_vs_dest_dst_rcu_free(struct rcu_head *head); + + #ifdef CONFIG_IP_VS_IPV6 +@@ -1640,8 +1641,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, +- unsigned int hooknum, struct ip_vs_iphdr *iph); ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph); + #endif + + #ifdef CONFIG_SYSCTL +@@ -1706,11 +1708,12 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) + } + + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir); ++ struct ip_vs_conn *cp, int dir, unsigned int toff); + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir); ++ struct ip_vs_conn *cp, int dir, unsigned int toff, ++ struct ip_vs_iphdr *ciph); + #endif + + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) +diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c +index f9132b359f0c6..0c690a30a85dc 100644 +--- a/net/netfilter/ipvs/ip_vs_app.c ++++ b/net/netfilter/ipvs/ip_vs_app.c +@@ -368,7 +368,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb, + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) + return 0; + +- th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); ++ th = (struct tcphdr *)(skb->data + ipvsh->len); + + /* + * Remember seq number in case this pkt gets resized +@@ -444,7 +444,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb, + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) + return 0; + +- th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); ++ th = (struct tcphdr *)(skb->data + ipvsh->len); + + /* + * Remember seq number in case this pkt gets resized +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index 99b0090bdfadb..d5b886ded5f11 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -746,13 +746,12 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, + * - inout: 1=in->out, 0=out->in + */ + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout) ++ struct ip_vs_conn *cp, int inout, unsigned int toff) + { + struct iphdr *iph = ip_hdr(skb); +- unsigned int icmp_offset = iph->ihl*4; +- struct icmphdr *icmph = (struct icmphdr *)(skb_network_header(skb) + +- icmp_offset); ++ struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); + struct iphdr *ciph = (struct iphdr *)(icmph + 1); ++ unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr); + + if (inout) { + iph->saddr = cp->vaddr.ip; +@@ -779,48 +778,45 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + + /* And finally the ICMP checksum */ + icmph->checksum = 0; +- icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset); ++ icmph->checksum = ip_vs_checksum_complete(skb, toff); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (inout) +- IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, +- "Forwarding altered outgoing ICMP"); ++ IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, ++ "Forwarding altered outgoing ICMP"); + else +- IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, +- "Forwarding altered incoming ICMP"); ++ IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, ++ "Forwarding altered incoming ICMP"); + } + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout) ++ struct ip_vs_conn *cp, int inout, unsigned int toff, ++ struct ip_vs_iphdr *ciph) + { + struct ipv6hdr *iph = ipv6_hdr(skb); +- unsigned int icmp_offset = 0; +- unsigned int offs = 0; /* header offset*/ + int protocol; + struct icmp6hdr *icmph; +- struct ipv6hdr *ciph; +- unsigned short fragoffs; ++ struct ipv6hdr *cih; + +- ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL); +- icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset); +- offs = icmp_offset + sizeof(struct icmp6hdr); +- ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs); ++ icmph = (struct icmp6hdr *)(skb->data + toff); ++ cih = (struct ipv6hdr *)(skb->data + ciph->off); + +- protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL); ++ protocol = ciph->protocol; + + if (inout) { + iph->saddr = cp->vaddr.in6; +- ciph->daddr = cp->vaddr.in6; ++ cih->daddr = cp->vaddr.in6; + } else { + iph->daddr = cp->daddr.in6; +- ciph->saddr = cp->daddr.in6; ++ cih->saddr = cp->daddr.in6; + } + + /* the TCP/UDP/SCTP port */ +- if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || +- IPPROTO_SCTP == protocol)) { +- __be16 *ports = (void *)(skb_network_header(skb) + offs); ++ if (!ciph->fragoffs && ++ (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || ++ protocol == IPPROTO_SCTP)) { ++ __be16 *ports = (void *)(skb->data + ciph->len); + + IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, + ntohs(inout ? ports[1] : ports[0]), +@@ -833,19 +829,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + + /* And finally the ICMP checksum */ + icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, +- skb->len - icmp_offset, ++ skb->len - toff, + IPPROTO_ICMPV6, 0); +- skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset; ++ skb->csum_start = skb_headroom(skb) + toff; + skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum); + skb->ip_summed = CHECKSUM_PARTIAL; + + if (inout) +- IP_VS_DBG_PKT(11, AF_INET6, pp, skb, +- (void *)ciph - (void *)iph, ++ IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, + "Forwarding altered outgoing ICMPv6"); + else +- IP_VS_DBG_PKT(11, AF_INET6, pp, skb, +- (void *)ciph - (void *)iph, ++ IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, + "Forwarding altered incoming ICMPv6"); + } + #endif +@@ -855,37 +849,38 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + */ + static int handle_response_icmp(int af, struct sk_buff *skb, + union nf_inet_addr *snet, +- __u8 protocol, struct ip_vs_conn *cp, ++ struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, +- unsigned int offset, unsigned int ihl, +- unsigned int hooknum) ++ struct ip_vs_iphdr *ciph, ++ unsigned int toff, unsigned int hooknum) + { + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; ++ unsigned int ctoff = ciph->len; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; + + /* Ensure the checksum is correct */ +- if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { ++ if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) { + /* Failed checksum! */ + IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", + IP_VS_DBG_ADDR(af, snet)); + goto out; + } + +- if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || +- IPPROTO_SCTP == protocol) +- offset += 2 * sizeof(__u16); +- if (skb_ensure_writable(skb, offset)) ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) ++ ctoff += 2 * sizeof(__u16); ++ if (skb_ensure_writable(skb, ctoff)) + goto out; + + #ifdef CONFIG_IP_VS_IPV6 + if (af == AF_INET6) +- ip_vs_nat_icmp_v6(skb, pp, cp, 1); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); + else + #endif +- ip_vs_nat_icmp(skb, pp, cp, 1); ++ ip_vs_nat_icmp(skb, pp, cp, 1, toff); + + if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) + goto out; +@@ -913,9 +908,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + * Currently handles error types - unreachable, quench, ttl exceeded. + */ + static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, +- int *related, unsigned int hooknum) ++ int *related, unsigned int hooknum, ++ struct ip_vs_iphdr *ipvsh) + { +- struct iphdr *iph; + struct icmphdr _icmph, *ic; + struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ + struct ip_vs_iphdr ciph; +@@ -930,17 +925,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (ip_is_fragment(ip_hdr(skb))) { + if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) + return NF_STOLEN; ++ if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh)) ++ return NF_ACCEPT; + } + +- iph = ip_hdr(skb); +- offset = ihl = iph->ihl * 4; ++ ihl = ipvsh->len; ++ offset = ipvsh->len; + ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); + if (ic == NULL) + return NF_DROP; + + IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n", + ic->type, ntohs(icmp_id(ic)), +- &iph->saddr, &iph->daddr); ++ &ipvsh->saddr.ip, &ipvsh->daddr.ip); + + /* + * Work through seeing if this is for us. +@@ -959,7 +956,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + /* Now find the contained IP header */ + offset += sizeof(_icmph); + cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); +- if (cih == NULL) ++ if (!(cih && cih->version == 4 && cih->ihl >= 5)) + return NF_ACCEPT; /* The packet looks wrong, ignore */ + + pp = ip_vs_proto_get(cih->protocol); +@@ -982,9 +979,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (!cp) + return NF_ACCEPT; + +- snet.ip = iph->saddr; +- return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp, +- pp, ciph.len, ihl, hooknum); ++ snet.ip = ipvsh->saddr.ip; ++ return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl, ++ hooknum); + } + + #ifdef CONFIG_IP_VS_IPV6 +@@ -997,7 +994,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + struct ip_vs_conn *cp; + struct ip_vs_protocol *pp; + union nf_inet_addr snet; +- unsigned int offset; + + *related = 1; + ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph); +@@ -1040,9 +1036,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + + snet.in6 = ciph.saddr.in6; +- offset = ciph.len; +- return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp, +- pp, offset, ipvsh->len, hooknum); ++ return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph, ++ ipvsh->len, hooknum); + } + #endif + +@@ -1368,7 +1363,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat + #endif + if (unlikely(iph.protocol == IPPROTO_ICMP)) { + int related; +- int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum); ++ int verdict = ip_vs_out_icmp(ipvs, skb, &related, ++ hooknum, &iph); + + if (related) + return verdict; +@@ -1572,9 +1568,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb, + */ + static int + ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, +- unsigned int hooknum) ++ unsigned int hooknum, struct ip_vs_iphdr *iph) + { +- struct iphdr *iph; + struct icmphdr _icmph, *ic; + struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ + struct ip_vs_iphdr ciph; +@@ -1584,7 +1579,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + unsigned int offset, offset2, ihl, verdict; + bool tunnel, new_cp = false; + union nf_inet_addr *raddr; +- char *outer_proto = "IPIP"; ++ char *outer_proto __maybe_unused = "IPIP"; + unsigned int hlen_ipip; + int ulen = 0; + +@@ -1594,17 +1589,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + if (ip_is_fragment(ip_hdr(skb))) { + if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) + return NF_STOLEN; ++ if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph)) ++ return NF_ACCEPT; + } + +- iph = ip_hdr(skb); +- offset = ihl = iph->ihl * 4; ++ ihl = iph->len; ++ offset = iph->len; + ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); + if (ic == NULL) + return NF_DROP; + + IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n", + ic->type, ntohs(icmp_id(ic)), +- &iph->saddr, &iph->daddr); ++ &iph->saddr.ip, &iph->daddr.ip); + + /* + * Work through seeing if this is for us. +@@ -1721,7 +1718,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { + /* Failed checksum! */ + IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", +- &iph->saddr); ++ &iph->saddr.ip); + goto out; + } + +@@ -1792,7 +1789,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || + IPPROTO_SCTP == cih->protocol) + offset += 2 * sizeof(__u16); +- verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph); ++ verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, ++ &ciph); + + out: + if (likely(!new_cp)) +@@ -1905,7 +1903,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + IPPROTO_SCTP == ciph.protocol) + offset += 2 * sizeof(__u16); /* Also mangle ports */ + +- verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph); ++ verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, ++ &ciph); + + out: + if (likely(!new_cp)) +@@ -1984,7 +1983,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state + if (unlikely(iph.protocol == IPPROTO_ICMP)) { + int related; + int verdict = ip_vs_in_icmp(ipvs, skb, &related, +- hooknum); ++ hooknum, &iph); + + if (related) + return verdict; +@@ -2120,6 +2119,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) + { + struct netns_ipvs *ipvs = net_ipvs(state->net); ++ struct ip_vs_iphdr iphdr; + int r; + + /* ipvs enabled in this netns ? */ +@@ -2129,10 +2129,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + if (state->pf == NFPROTO_IPV4) { + if (ip_hdr(skb)->protocol != IPPROTO_ICMP) + return NF_ACCEPT; ++ ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr); + #ifdef CONFIG_IP_VS_IPV6 + } else { +- struct ip_vs_iphdr iphdr; +- + ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr); + + if (iphdr.protocol != IPPROTO_ICMPV6) +@@ -2142,7 +2141,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + #endif + } + +- return ip_vs_in_icmp(ipvs, skb, &r, state->hook); ++ return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr); + } + + static const struct nf_hook_ops ip_vs_ops4[] = { +diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c +index f6f732b7dfa86..3dbd3096e1637 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c +@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- sctph = (void *) skb_network_header(skb) + sctphoff; ++ sctph = (void *)skb->data + sctphoff; + + /* Only update csum if we really have to */ + if (sctph->source != cp->vport || payload_csum || +@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- sctph = (void *) skb_network_header(skb) + sctphoff; ++ sctph = (void *)skb->data + sctphoff; + + /* Only update csum if we really have to */ + if (sctph->dest != cp->dport || payload_csum || +diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c +index bf31127338aa0..1ac9c233537d3 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c +@@ -180,7 +180,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- tcph = (void *)skb_network_header(skb) + tcphoff; ++ tcph = (void *)skb->data + tcphoff; + tcph->source = cp->vport; + + /* Adjust TCP checksums */ +@@ -261,7 +261,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- tcph = (void *)skb_network_header(skb) + tcphoff; ++ tcph = (void *)skb->data + tcphoff; + tcph->dest = cp->dport; + + /* +diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c +index 40d30649b3048..96ac882df15c1 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_udp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c +@@ -171,7 +171,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- udph = (void *)skb_network_header(skb) + udphoff; ++ udph = (void *)skb->data + udphoff; + udph->source = cp->vport; + + /* +@@ -255,7 +255,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- udph = (void *)skb_network_header(skb) + udphoff; ++ udph = (void *)skb->data + udphoff; + udph->dest = cp->dport; + + /* +diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c +index 02a2a966911ee..db562dac31c72 100644 +--- a/net/netfilter/ipvs/ip_vs_xmit.c ++++ b/net/netfilter/ipvs/ip_vs_xmit.c +@@ -1473,8 +1473,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + */ + int + ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, unsigned int hooknum, +- struct ip_vs_iphdr *iph) ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph) + { + struct rtable *rt; /* Route to the other host */ + int rc; +@@ -1486,7 +1487,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + translate address/port back */ + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { + if (cp->packet_xmit) +- rc = cp->packet_xmit(skb, cp, pp, iph); ++ rc = cp->packet_xmit(skb, cp, pp, ciph); + else + rc = NF_ACCEPT; + /* do not touch skb anymore */ +@@ -1504,7 +1505,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; + local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode, +- NULL, iph); ++ NULL, ciph); + if (local < 0) + goto tx_error; + rt = skb_rtable(skb); +@@ -1536,13 +1537,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + } + + /* copy-on-write the packet before mangling it */ +- if (skb_ensure_writable(skb, offset)) ++ if (skb_ensure_writable(skb, wlen)) + goto tx_error; + + if (skb_cow(skb, rt->dst.dev->hard_header_len)) + goto tx_error; + +- ip_vs_nat_icmp(skb, pp, cp, 0); ++ ip_vs_nat_icmp(skb, pp, cp, 0, toff); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +@@ -1558,8 +1559,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + #ifdef CONFIG_IP_VS_IPV6 + int + ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, unsigned int hooknum, +- struct ip_vs_iphdr *ipvsh) ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph) + { + struct rt6_info *rt; /* Route to the other host */ + int rc; +@@ -1571,7 +1573,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + translate address/port back */ + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { + if (cp->packet_xmit) +- rc = cp->packet_xmit(skb, cp, pp, ipvsh); ++ rc = cp->packet_xmit(skb, cp, pp, ciph); + else + rc = NF_ACCEPT; + /* do not touch skb anymore */ +@@ -1588,7 +1590,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; + local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest, +- &cp->daddr.in6, NULL, ipvsh, 0, rt_mode); ++ &cp->daddr.in6, NULL, ciph, 0, rt_mode); + if (local < 0) + goto tx_error; + rt = dst_rt6_info(skb_dst(skb)); +@@ -1620,13 +1622,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + } + + /* copy-on-write the packet before mangling it */ +- if (skb_ensure_writable(skb, offset)) ++ if (skb_ensure_writable(skb, wlen)) + goto tx_error; + + if (skb_cow(skb, rt->dst.dev->hard_header_len)) + goto tx_error; + +- ip_vs_nat_icmp_v6(skb, pp, cp, 0); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +-- +2.53.0 + diff --git a/queue-6.6/ipvs-fix-the-checksum-validations.patch b/queue-6.6/ipvs-fix-the-checksum-validations.patch new file mode 100644 index 0000000000..70f000c1c5 --- /dev/null +++ b/queue-6.6/ipvs-fix-the-checksum-validations.patch @@ -0,0 +1,389 @@ +From 1b61d76c6b229826c754294fbeee6ffd009c255b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:15 +0300 +Subject: ipvs: fix the checksum validations + +From: Julian Anastasov + +[ Upstream commit e876b75b9020a97bbdc79721e7fc749024891c65 ] + +ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6 +packets from clients. In fact, as for TCP/UDP we should +validate the checksum for ICMP packets only when we +mangle the packets on MASQ or on reply for tunnel. + +Also, Sashiko points out that handle_response_icmp() being +common for IPv4 and IPv6 is missing the pseudo-header +calculation while validating ICMPv6 messages from real +servers which is a problem if checksum is not validated +by the hardware. + +Fix the problems by creating ip_vs_checksum_common_check() +helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6. +Rely on the nf_checksum() for validating the ICMP messages +but use it also for TCP and UDP. + +Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP. + +IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum +validation on LOCAL_OUT (local clients or local real +servers) and on FORWARD (traffic from servers on LAN). +Do it only on LOCAL_IN, in case nf_checksum() is not +called on PRE_ROUTING. + +Also, ip_vs_checksum_complete() can be marked static. + +Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6") +Link: https://sashiko.dev/#/patchset/20260708180315.77413-1-ja%40ssi.bg +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 31 +++++++++++++++-- + net/netfilter/ipvs/ip_vs_core.c | 20 +++++++++-- + net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++---- + net/netfilter/ipvs/ip_vs_proto_tcp.c | 44 +++++------------------ + net/netfilter/ipvs/ip_vs_proto_udp.c | 50 ++++++--------------------- + 5 files changed, 74 insertions(+), 86 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index 08b90d33acdc6..aef042039cb00 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -24,7 +24,9 @@ + #include /* for union nf_inet_addr */ + #include + #include /* for struct ipv6hdr */ ++#include + #include ++#include + #if IS_ENABLED(CONFIG_NF_CONNTRACK) + #include + #endif +@@ -1711,8 +1713,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int dir); + #endif + +-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset); +- + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) + { + __be32 diff[2] = { ~old, new }; +@@ -1738,6 +1738,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum) + return csum_partial(diff, sizeof(diff), oldsum); + } + ++static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af) ++{ ++ /* Checksum unnecessary or already validated? */ ++ if (skb_csum_unnecessary(skb)) ++ return false; ++ /* LOCAL_OUT ? */ ++ if (!skb->dev || skb->dev->flags & IFF_LOOPBACK) ++ return false; ++ /* !LOCAL_IN (FORWARD) ? */ ++ if (af == AF_INET6) { ++ if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL)) ++ return false; ++ } else { ++ if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL)) ++ return false; ++ } ++ return true; ++} ++ ++static inline bool ip_vs_checksum_common_check(struct sk_buff *skb, ++ int offset, int proto, int af) ++{ ++ if (!ip_vs_checksum_needed(skb, af)) ++ return true; ++ return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af); ++} ++ + /* Forget current conntrack (unconfirmed) and attach notrack entry */ + static inline void ip_vs_notrack(struct sk_buff *skb) + { +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index fcd8e0857de2a..99b0090bdfadb 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -689,7 +689,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; } + + #endif + +-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) ++static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) + { + return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0)); + } +@@ -860,13 +860,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + unsigned int offset, unsigned int ihl, + unsigned int hooknum) + { ++ int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; + + /* Ensure the checksum is correct */ +- if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { ++ if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { + /* Failed checksum! */ + IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", + IP_VS_DBG_ADDR(af, snet)); +@@ -1716,7 +1717,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + verdict = NF_DROP; + + /* Ensure the checksum is correct */ +- if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { ++ if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) && ++ !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { + /* Failed checksum! */ + IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", + &iph->saddr); +@@ -1882,6 +1884,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + goto out; + } + ++ verdict = NF_DROP; ++ ++ /* Ensure the checksum is correct */ ++ if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ && ++ !ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6, ++ AF_INET6)) { ++ /* Failed checksum! */ ++ IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n", ++ &iph->saddr); ++ goto out; ++ } ++ + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); + +diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c +index c67317be17dfa..f6f732b7dfa86 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c +@@ -11,7 +11,7 @@ + + static int + sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int sctphoff); ++ struct ip_vs_iphdr *iph); + + static int + sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) ++ if (!sctp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) ++ if (!sctp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int sctphoff) ++ struct ip_vs_iphdr *iph) + { ++ unsigned int sctphoff = iph->len; + struct sctphdr *sh; + __le32 cmp, val; + ++ if (!ip_vs_checksum_needed(skb, af)) ++ return 1; + sh = (struct sctphdr *)(skb->data + sctphoff); + cmp = sh->checksum; + val = sctp_compute_cksum(skb, sctphoff); + + if (val != cmp) { + /* CRC failure, dump it. */ +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); + return 0; + } + return 1; +diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c +index b382810156b2c..bf31127338aa0 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c +@@ -30,7 +30,7 @@ + + static int + tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int tcphoff); ++ struct ip_vs_iphdr *iph); + + static int + tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -167,7 +167,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) ++ if (!tcp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -245,7 +245,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) ++ if (!tcp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -303,41 +303,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int tcphoff) ++ struct ip_vs_iphdr *iph) + { +- switch (skb->ip_summed) { +- case CHECKSUM_NONE: +- skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); +- fallthrough; +- case CHECKSUM_COMPLETE: +-#ifdef CONFIG_IP_VS_IPV6 +- if (af == AF_INET6) { +- if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, +- &ipv6_hdr(skb)->daddr, +- skb->len - tcphoff, +- IPPROTO_TCP, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- } else +-#endif +- if (csum_tcpudp_magic(ip_hdr(skb)->saddr, +- ip_hdr(skb)->daddr, +- skb->len - tcphoff, +- ip_hdr(skb)->protocol, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- break; +- default: +- /* No need to checksum. */ +- break; ++ if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) { ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); ++ return 0; + } +- + return 1; + } + +diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c +index dbd4155bb0752..40d30649b3048 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_udp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c +@@ -26,7 +26,7 @@ + + static int + udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int udphoff); ++ struct ip_vs_iphdr *iph); + + static int + udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -156,7 +156,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!udp_csum_check(cp->af, skb, pp, udphoff)) ++ if (!udp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -239,7 +239,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!udp_csum_check(cp->af, skb, pp, udphoff)) ++ if (!udp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -299,48 +299,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int udphoff) ++ struct ip_vs_iphdr *iph) + { + struct udphdr _udph, *uh; + +- uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph); ++ uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph); + if (uh == NULL) + return 0; + +- if (uh->check != 0) { +- switch (skb->ip_summed) { +- case CHECKSUM_NONE: +- skb->csum = skb_checksum(skb, udphoff, +- skb->len - udphoff, 0); +- fallthrough; +- case CHECKSUM_COMPLETE: +-#ifdef CONFIG_IP_VS_IPV6 +- if (af == AF_INET6) { +- if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, +- &ipv6_hdr(skb)->daddr, +- skb->len - udphoff, +- IPPROTO_UDP, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- } else +-#endif +- if (csum_tcpudp_magic(ip_hdr(skb)->saddr, +- ip_hdr(skb)->daddr, +- skb->len - udphoff, +- ip_hdr(skb)->protocol, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- break; +- default: +- /* No need to checksum. */ +- break; +- } ++ if (!uh->check) ++ return 1; ++ if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) { ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); ++ return 0; + } + return 1; + } +-- +2.53.0 + diff --git a/queue-6.6/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-6.6/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch new file mode 100644 index 0000000000..e3c9eeb4e8 --- /dev/null +++ b/queue-6.6/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch @@ -0,0 +1,64 @@ +From dbd84695e12b063eeaf61a57e8aa16bd62743ddc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:03 -0400 +Subject: keys: fix out-of-bounds read in keyring_get_key_chunk() + +From: Michael Bommarito + +[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ] + +For description-level chunks keyring_get_key_chunk() advances the read +pointer by level * sizeof(long) past the inline prefix but only +bounds-checks the prefix, so a long enough key description is read past +its kmemdup(desc, desc_len + 1) allocation. Compute the full byte +offset and bounds-check the description against it before reading. + +The walk only reaches a description-level chunk when two keys collide +through the hash, x, type and domain_tag chunks, so this is reached from +an unprivileged add_key(2) with a crafted pair of same-type keys whose +index hashes collide; KASAN reports a slab-out-of-bounds read. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index e105349794f23..b9ca9dc9b0c3f 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + unsigned long chunk = 0; + const u8 *d; + int desc_len = index_key->desc_len, n = sizeof(chunk); ++ unsigned int offset; + + level /= ASSOC_ARRAY_KEY_CHUNK_SIZE; + switch (level) { +@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + return (unsigned long)index_key->domain_tag; + default: + level -= 4; +- if (desc_len <= sizeof(index_key->desc)) ++ offset = sizeof(index_key->desc) + level * sizeof(long); ++ if (desc_len <= offset) + return 0; + +- d = index_key->description + sizeof(index_key->desc); +- d += level * sizeof(long); +- desc_len -= sizeof(index_key->desc); ++ d = index_key->description + offset; ++ desc_len -= offset; + if (desc_len > n) + desc_len = n; + do { +-- +2.53.0 + diff --git a/queue-6.6/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-6.6/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch new file mode 100644 index 0000000000..00e07a2ead --- /dev/null +++ b/queue-6.6/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch @@ -0,0 +1,63 @@ +From 1d73c3eba682af319c50411905aa41a3bfbbda80 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:04 -0400 +Subject: keys: make keyring key-chunk byte order agree with + keyring_diff_objects() + +From: Michael Bommarito + +[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ] + +keyring_get_key_chunk() loads description bytes into the index chunk low +address first, while keyring_diff_objects() numbers the first differing +bit from the low end and folds the absolute byte index into the level +without removing the inline-prefix offset the level already carries. +The two disagree on byte order and bit position, so the array can be +told two keys first differ at a bit that does not differ in the chunk +the walker uses, letting crafted descriptions collide into one node. + +Load the chunk in the order keyring_diff_objects() assumes and drop the +inline-prefix length when folding the byte index into the level. This +only changes the in-memory ordering used to place keys within a keyring; +add, search and read of non-colliding keys are unaffected. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index b9ca9dc9b0c3f..fd95a0eb7a466 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + desc_len -= offset; + if (desc_len > n) + desc_len = n; ++ d += desc_len; + do { + chunk <<= 8; +- chunk |= *d++; ++ chunk |= *--d; + } while (--desc_len > 0); + return chunk; + } +@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data) + return -1; + + differ_plus_i: +- level += i; ++ level += i - (int)sizeof(a->desc); + differ: + i = level * 8 + __ffs(seg_a ^ seg_b); + return i; +-- +2.53.0 + diff --git a/queue-6.6/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch b/queue-6.6/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch new file mode 100644 index 0000000000..45fbdf838e --- /dev/null +++ b/queue-6.6/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch @@ -0,0 +1,50 @@ +From 08a57f25ee0bc05a8223a704c8b65de90a88f96a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 10:04:19 +0900 +Subject: ksmbd: fix use-after-free in __close_file_table_ids() + +From: Namjae Jeon + +[ Upstream commit e7188199eff46a636f3436356f0aae039be6dd66 ] + +A ksmbd_file can remain alive after logical close while another session +holds a temporary reference obtained through ksmbd_lookup_fd_inode(). +ksmbd_close_fd() currently marks the file closed and drops the idr-owned +reference, but leaves the pointer published in the closing session's idr +until the final reference is dropped. + +If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final() +supplies the foreign session's file table to __ksmbd_close_fd(). The object +is then freed without being removed from its owner's idr, and the owner +session later dereferences the stale pointer during file-table teardown. + +Remove the volatile id from the owner's idr while ksmbd_close_fd() still +holds that table's lock, and clear volatile_id before dropping +the idr-owned reference. A later foreign final put then only performs +physical destruction and cannot remove the object from the wrong table. + +Fixes: 8510a043d334 ("ksmbd: increment reference count of parent fp") +Reported-by: Yunseong Kim +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs_cache.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index 320d467d46ca8..a15c427248494 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -502,6 +502,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + fp = NULL; + else { + fp->f_state = FP_CLOSED; ++ idr_remove(ft->idr, id); ++ fp->volatile_id = KSMBD_NO_FID; + closed = true; + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; +-- +2.53.0 + diff --git a/queue-6.6/ksmbd-return-success-for-deferred-final-close.patch b/queue-6.6/ksmbd-return-success-for-deferred-final-close.patch new file mode 100644 index 0000000000..07827cca21 --- /dev/null +++ b/queue-6.6/ksmbd-return-success-for-deferred-final-close.patch @@ -0,0 +1,64 @@ +From ab59b591c611b18bc9a60eced2edd9d8dcbff87b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 21 Jun 2026 19:41:08 +0900 +Subject: ksmbd: return success for deferred final close + +From: Namjae Jeon + +[ Upstream commit c5db4de8988f1a621556ca5c4537f77b766ca07d ] + +ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table +reference. If another in-flight request still holds a reference, the final +close is deferred until that request drops its reference. + +The function currently returns -EINVAL in that deferred-final-close case +because fp is cleared when the reference count does not reach zero. That +turns a valid close into STATUS_FILE_CLOSED. + +smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then +closes the same directory handle before receiving the find response. +The query holds a reference while it builds the response, so close must +mark the handle closed and return success even though final teardown is +delayed. Track whether the handle was successfully transitioned to +FP_CLOSED and return success when only the final close is deferred. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: e7188199eff4 ("ksmbd: fix use-after-free in __close_file_table_ids()") +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs_cache.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index d15641007d4e3..320d467d46ca8 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -487,6 +487,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + { + struct ksmbd_file *fp; + struct ksmbd_file_table *ft; ++ bool closed = false; + + if (!has_file_id(id)) + return 0; +@@ -501,6 +502,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + fp = NULL; + else { + fp->f_state = FP_CLOSED; ++ closed = true; + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; + } +@@ -508,7 +510,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + write_unlock(&ft->lock); + + if (!fp) +- return -EINVAL; ++ return closed ? 0 : -EINVAL; + + __put_fd_final(work, fp); + return 0; +-- +2.53.0 + diff --git a/queue-6.6/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch b/queue-6.6/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch new file mode 100644 index 0000000000..4ac9ee8387 --- /dev/null +++ b/queue-6.6/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch @@ -0,0 +1,40 @@ +From 8fc9a3a3a03c5a9430c95a60d92fec6908cd278d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 06:26:05 +0000 +Subject: net: bridge: mrp: fix Option TLV length in MRP_Test frames + +From: David Corvaglia + +[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ] + +oui is a pointer, so sizeof(oui) is the pointer size. The MRA +Option TLV thus advertises a wrong length (15 vs 10 on x86_64), +causing misparsing of the frame on peers. Fix is to replace +with sizeof(*oui). + +Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA") +Signed-off-by: David Corvaglia +Acked-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_mrp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c +index fd2de35ffb3cf..5fd22bb4f5b60 100644 +--- a/net/bridge/br_mrp.c ++++ b/net/bridge/br_mrp.c +@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp, + struct br_mrp_oui_hdr *oui = NULL; + u8 length; + +- length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) + ++ length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) + + MRP_OPT_PADDING; + br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length); + +-- +2.53.0 + diff --git a/queue-6.6/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch b/queue-6.6/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch new file mode 100644 index 0000000000..389bf5dbaa --- /dev/null +++ b/queue-6.6/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch @@ -0,0 +1,78 @@ +From 5cd1a88ad1020ff51ca2a2e7cec1a77b39e66cc4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 07:29:01 +0000 +Subject: net: do not send ICMP/NDISC Redirects when peer allocation fails + +From: Eric Dumazet + +[ Upstream commit dbc3791e3b2472e1ccc08947e0f83b443470ff4f ] + +When inet_getpeer_v4() or inet_getpeer_v6() fails to allocate a peer entry +under memory pressure or tree size caps, redirect handlers previously fell +back to sending un-rate-limited ICMP/NDISC Redirect messages. + +In IPv4, ip_rt_send_redirect() called icmp_send() directly when peer == NULL. +In IPv6, ip6_forward() and ndisc_send_redirect() passed a NULL peer into +inet_peer_xrlim_allow(), which returned true when peer == NULL. + +Because ICMP/NDISC Redirects are not part of the default global rate limit +mask (sysctl_icmp_ratemask), sending redirects when peer == NULL creates +an un-rate-limited ICMP packet storm. + +Fix this by failing closed in ip_rt_send_redirect(), ip6_forward(), and +ndisc_send_redirect() when peer is NULL. + +Fixes: 92d868292634 ("inetpeer: Move ICMP rate limiting state into inet_peer entries.") +Signed-off-by: Eric Dumazet +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260724072901.1633601-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/route.c | 2 -- + net/ipv6/ip6_output.c | 2 +- + net/ipv6/ndisc.c | 2 ++ + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/net/ipv4/route.c b/net/ipv4/route.c +index f134c59f839e2..834ec1f34e506 100644 +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -902,8 +902,6 @@ void ip_rt_send_redirect(struct sk_buff *skb) + peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif); + if (!peer) { + rcu_read_unlock(); +- icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, +- rt_nexthop(rt, ip_hdr(skb)->daddr)); + return; + } + +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index 2e2cd0e0d48a5..5ee625d81942a 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -625,7 +625,7 @@ int ip6_forward(struct sk_buff *skb) + /* Limit redirects both by destination (here) + and by source (inside ndisc_send_redirect) + */ +- if (inet_peer_xrlim_allow(peer, 1*HZ)) ++ if (peer && inet_peer_xrlim_allow(peer, 1*HZ)) + ndisc_send_redirect(skb, target); + rcu_read_unlock(); + } else { +diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c +index 342e7066f765f..8adb31804f01b 100644 +--- a/net/ipv6/ndisc.c ++++ b/net/ipv6/ndisc.c +@@ -1731,6 +1731,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) + + rcu_read_lock(); + peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr); ++ if (!peer) ++ goto release; + ret = inet_peer_xrlim_allow(peer, 1*HZ); + rcu_read_unlock(); + +-- +2.53.0 + diff --git a/queue-6.6/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch b/queue-6.6/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch new file mode 100644 index 0000000000..e43984558f --- /dev/null +++ b/queue-6.6/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch @@ -0,0 +1,191 @@ +From 19744227ad6dad7c7118e148ee491e342553d940 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 05:52:29 +0100 +Subject: net: dsa: mt7530: error out on failed reads in MT7531 PHY polling + +From: Daniel Golle + +[ Upstream commit 77a9ebe8818cf6dd1699bd6728cb5d66307801d7 ] + +The MT7531 indirect PHY access functions poll MT7531_PHY_IAC through +a helper which returns 0 when the underlying read fails, so a failed +bus transaction clears MT7531_PHY_ACS_ST and the access carries on, +returning garbage PHY register data to phylib. + +Poll using regmap_read_poll_timeout(), which stops on read errors and +propagates them. These functions hold the MDIO bus lock across the +whole sequence, so the unlocked regmap accesses remain correct. Remove +the now-unused _mt7530_unlocked_read(). + +Fixes: c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch") +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/79e85d68d210cc37342978171aa6432aa2954333.1785213071.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 58 ++++++++++++++-------------------------- + 1 file changed, 20 insertions(+), 38 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 8d889bc5f3d1b..aaa5640ad0f1c 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -233,12 +233,6 @@ mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val) + mt7530_mutex_unlock(priv); + } + +-static u32 +-_mt7530_unlocked_read(struct mt7530_dummy_poll *p) +-{ +- return mt7530_mii_read(p->priv, p->reg); +-} +- + static u32 + _mt7530_read(struct mt7530_dummy_poll *p) + { +@@ -628,16 +622,13 @@ static int + mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + int regnum) + { +- struct mt7530_dummy_poll p; + u32 reg, val; + int ret; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -647,8 +638,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad) | regnum; + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -658,8 +649,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad); + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -676,16 +667,13 @@ static int + mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + int regnum, u16 data) + { +- struct mt7530_dummy_poll p; + u32 val, reg; + int ret; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -695,8 +683,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad) | regnum; + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -706,8 +694,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad) | data; + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -722,16 +710,13 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + static int + mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) + { +- struct mt7530_dummy_poll p; + int ret; + u32 val; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -742,8 +727,8 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) + + mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -760,16 +745,13 @@ static int + mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, + u16 data) + { +- struct mt7530_dummy_poll p; + int ret; + u32 reg; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, +- !(reg & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg, ++ !(reg & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -780,8 +762,8 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, + + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, +- !(reg & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg, ++ !(reg & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +-- +2.53.0 + diff --git a/queue-6.6/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-6.6/net-phylink-put-link_gpio-if-phylink_create-fails.patch new file mode 100644 index 0000000000..37335be3bf --- /dev/null +++ b/queue-6.6/net-phylink-put-link_gpio-if-phylink_create-fails.patch @@ -0,0 +1,92 @@ +From 66dc33f68fd79fe30282ac061912ea9a9a285319 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 17:08:05 +0200 +Subject: net: phylink: put link_gpio if phylink_create fails + +From: Christian Marangi + +[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ] + +In phylink_create() if phylink_register_sfp() returns an error, link_gpio +obtained by phylink_parse_fixedlink() is never released. While this is a +very unlikely scenario, it's worth to fix/handle this. + +This was present from the very first implementation of phylink but got +relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to +bridge between network devices and sfp cages") where additional function +were added after phylink_parse_fixedlink() making the release of link_gpio +needed if such additional function errored out. + +While at it, restructure the exit condition of phylink_create() with the +goto pattern to reduce code duplication on handling error conditions. + +Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages") +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/phy/phylink.c | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 800e8b9eb4532..6d8ff73a7ebae 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1635,8 +1635,8 @@ struct phylink *phylink_create(struct phylink_config *config, + } else if (config->type == PHYLINK_DEV) { + pl->dev = config->dev; + } else { +- kfree(pl); +- return ERR_PTR(-EINVAL); ++ ret = -EINVAL; ++ goto free_pl; + } + + pl->using_mac_select_pcs = using_mac_select_pcs; +@@ -1660,28 +1660,29 @@ struct phylink *phylink_create(struct phylink_config *config, + phylink_validate(pl, pl->supported, &pl->link_config); + + ret = phylink_parse_mode(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto free_pl; + + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { + ret = phylink_parse_fixedlink(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + } + + pl->cur_link_an_mode = pl->cfg_link_an_mode; + + ret = phylink_register_sfp(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + + return pl; ++ ++release_link_gpio: ++ if (pl->link_gpio) ++ gpiod_put(pl->link_gpio); ++free_pl: ++ kfree(pl); ++ return ERR_PTR(ret); + } + EXPORT_SYMBOL_GPL(phylink_create); + +-- +2.53.0 + diff --git a/queue-6.6/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-6.6/net-sxgbe-check-descriptor-ring-allocation-failures.patch new file mode 100644 index 0000000000..9e5e510813 --- /dev/null +++ b/queue-6.6/net-sxgbe-check-descriptor-ring-allocation-failures.patch @@ -0,0 +1,48 @@ +From ba3f0ec1e1da255e7b7b7fad8a0de17cf86db153 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:20 +0800 +Subject: net: sxgbe: check descriptor ring allocation failures + +From: Chenguang Zhao + +[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ] + +sxgbe_open() ignores the return value of init_dma_desc_rings() and +continues to program DMA with invalid ring addresses when allocation +fails. Check the return value and disconnect the PHY on failure. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index fc5750396a3f0..df300493a8d9b 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -1080,7 +1080,9 @@ static int sxgbe_open(struct net_device *dev) + priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE); + priv->tx_tc = TC_DEFAULT; + priv->rx_tc = TC_DEFAULT; +- init_dma_desc_rings(dev); ++ ret = init_dma_desc_rings(dev); ++ if (ret) ++ goto init_phy_error; + + /* DMA initialization and SW reset */ + ret = sxgbe_init_dma_engine(priv); +@@ -1189,6 +1191,7 @@ static int sxgbe_open(struct net_device *dev) + + init_error: + free_dma_desc_resources(priv); ++init_phy_error: + if (dev->phydev) + phy_disconnect(dev->phydev); + phy_error: +-- +2.53.0 + diff --git a/queue-6.6/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-6.6/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch new file mode 100644 index 0000000000..bf94700937 --- /dev/null +++ b/queue-6.6/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch @@ -0,0 +1,50 @@ +From 4a1578f38ccfc187c9d70bf83c2a0f13bfdda291 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:19 +0800 +Subject: net: sxgbe: free TX rings on RX allocation failure + +From: Chenguang Zhao + +[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ] + +When RX descriptor ring allocation fails, init_dma_desc_rings() only +frees the partially allocated RX rings and returns. The TX rings that +were allocated earlier in the same function are leaked. + +Rearrange error labels to clean up TX rings upon RX failures. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index d662679c29832..fc5750396a3f0 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -599,14 +599,13 @@ static int init_dma_desc_rings(struct net_device *netd) + + return 0; + +-txalloc_err: +- while (queue_num--) +- free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); +- return ret; +- + rxalloc_err: + while (queue_num--) + free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize); ++ queue_num = SXGBE_TX_QUEUES; ++txalloc_err: ++ while (queue_num--) ++ free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); + return ret; + } + +-- +2.53.0 + diff --git a/queue-6.6/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-6.6/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch new file mode 100644 index 0000000000..d5162fead8 --- /dev/null +++ b/queue-6.6/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch @@ -0,0 +1,96 @@ +From 8d26964170957133ee4568cf983ba69f9321cd2a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 16:42:01 -0700 +Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in + sip_help_tcp() + +From: Xiang Mei + +[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ] + +sip_help_tcp() stores the size change of each NAT-rewritten SIP message +in s16 diff and accumulates it in s16 tdiff, but a single message can +grow by more than S16_MAX while the packet stays under the 65535 +enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long +Contact list expands the message by tens of kilobytes. diff then wraps, +and "datalen = datalen + diff - msglen" yields a huge unsigned datalen, +so the next iteration's ct_sip_get_header() reads past the linearized skb +tail. + +Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the +65535 byte packet limit, and the seqadj core is already s32 +(nf_ct_seqadj_set() takes s32), so no previously accepted input is +rejected. + + BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25 + ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694) + nf_confirm (net/netfilter/nf_conntrack_proto.c:183) + nf_hook_slow (net/netfilter/core.c:619) + ip6_output (net/ipv6/ip6_output.c:246) + ip6_forward (net/ipv6/ip6_output.c:690) + ipv6_rcv (net/ipv6/ip6_input.c:351) + __netif_receive_skb_one_core (net/core/dev.c:6212) + process_backlog (net/core/dev.c:6676) + __napi_poll (net/core/dev.c:7735) + net_rx_action (net/core/dev.c:7955) + handle_softirqs (kernel/softirq.c:622) + run_ksoftirqd (kernel/softirq.c:1076) + ... + +Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support") +Reported-by: Weiming Shi +Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Xiang Mei +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/nf_conntrack_sip.h | 2 +- + net/netfilter/nf_conntrack_sip.c | 2 +- + net/netfilter/nf_nat_sip.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h +index dbc614dfe0d56..aafa0c04f917e 100644 +--- a/include/linux/netfilter/nf_conntrack_sip.h ++++ b/include/linux/netfilter/nf_conntrack_sip.h +@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks { + unsigned int *datalen); + + void (*seq_adjust)(struct sk_buff *skb, +- unsigned int protoff, s16 off); ++ unsigned int protoff, s32 off); + + unsigned int (*expect)(struct sk_buff *skb, + unsigned int protoff, +diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c +index 852c0b74b8a77..4f975b83c84f6 100644 +--- a/net/netfilter/nf_conntrack_sip.c ++++ b/net/netfilter/nf_conntrack_sip.c +@@ -1629,7 +1629,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, + unsigned int matchoff, matchlen; + unsigned int msglen, origlen; + const char *dptr, *end; +- s16 diff, tdiff = 0; ++ s32 diff, tdiff = 0; + int ret = NF_ACCEPT; + unsigned long clen; + bool term; +diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c +index f7be30c69b5c8..a1c41defaf22d 100644 +--- a/net/netfilter/nf_nat_sip.c ++++ b/net/netfilter/nf_nat_sip.c +@@ -315,7 +315,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, + } + + static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, +- s16 off) ++ s32 off) + { + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); +-- +2.53.0 + diff --git a/queue-6.6/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-6.6/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch new file mode 100644 index 0000000000..7286377ecd --- /dev/null +++ b/queue-6.6/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch @@ -0,0 +1,69 @@ +From fe95b45dd43052347fcbce979e9a2c6fe49852ce Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:15:23 +0000 +Subject: netfilter: nft_payload: fix mask build for partial field offload + +From: Xiang Mei (Microsoft) + +[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ] + +nft_payload_offload_mask() builds the offload match mask for a payload +expression that covers only part of a header field. For a partial IPv6 +address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which +is undefined on the 32-bit int operand. It also trims only one word, so +the remaining words stay 0xffffffff (and when priv_len is a multiple of 4 +the trim is skipped entirely), leaving the mask covering more bytes than +the rule matches. + + UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20 + shift exponent 120 is too large for 32-bit type 'int' + ... + +The match is byte-granular and struct nft_data is zero-initialised, so the +correct mask is simply the first priv_len bytes set to 0xff. Set those +bytes directly and drop the word/shift trimming; this removes the undefined +shift and no longer over-masks the trailing bytes. + +Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes") +Reported-by: AutonomousCodeSecurity@microsoft.com +Signed-off-by: Xiang Mei (Microsoft) +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/nft_payload.c | 12 +----------- + 1 file changed, 1 insertion(+), 11 deletions(-) + +diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c +index 36c31ad2d64c1..15feb49e0b663 100644 +--- a/net/netfilter/nft_payload.c ++++ b/net/netfilter/nft_payload.c +@@ -253,9 +253,7 @@ static int nft_payload_dump(struct sk_buff *skb, + static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + u32 priv_len, u32 field_len) + { +- unsigned int remainder, delta, k; + struct nft_data mask = {}; +- __be32 remainder_mask; + + if (priv_len == field_len) { + memset(®->mask, 0xff, priv_len); +@@ -264,15 +262,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + return false; + } + +- memset(&mask, 0xff, field_len); +- remainder = priv_len % sizeof(u32); +- if (remainder) { +- k = priv_len / sizeof(u32); +- delta = field_len - priv_len; +- remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1)); +- mask.data[k] = (__force u32)remainder_mask; +- } +- ++ memset(&mask, 0xff, priv_len); + memcpy(®->mask, &mask, field_len); + + return true; +-- +2.53.0 + diff --git a/queue-6.6/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-6.6/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch new file mode 100644 index 0000000000..182eedb7c5 --- /dev/null +++ b/queue-6.6/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch @@ -0,0 +1,83 @@ +From 5b944fa0a1e9e36b7a3f3029115f0ee4f035ac3e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 22:02:46 +0200 +Subject: netfilter: xt_hashlimit: validate hashtable supports + XT_HASHLIMIT_RATE_MATCH + +From: Pablo Neira Ayuso + +[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ] + +The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the +dsthash_ent structure which represents an entry in the hashtable. There +is a union area which uses a different layout to express the rate match +mode. + +Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode +flag is requested by two or more different rules that refer to the same +hashtable. Otherwise, uninitialized access to the burst field in the +union is possible. + +Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by +revision less than 3 too. + +Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode") +Reported-and-tested-by: Talha Berk Arslan +Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/ +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/xt_hashlimit.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c +index 0859b8f767645..61813010cd319 100644 +--- a/net/netfilter/xt_hashlimit.c ++++ b/net/netfilter/xt_hashlimit.c +@@ -118,6 +118,7 @@ struct xt_hashlimit_htable { + refcount_t use; + u_int8_t family; + bool rnd_initialized; ++ bool ratematch; + + struct hashlimit_cfg3 cfg; /* config */ + +@@ -325,6 +326,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg, + vfree(hinfo); + return -ENOMEM; + } ++ hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH); + spin_lock_init(&hinfo->lock); + + switch (revision) { +@@ -868,7 +870,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + } + + /* Check for overflow. */ +- if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (revision < 3) ++ return -EINVAL; ++ + if (cfg->avg == 0 || cfg->avg > U32_MAX) { + pr_info_ratelimited("invalid rate\n"); + return -ERANGE; +@@ -901,6 +906,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + mutex_unlock(&hashlimit_mutex); + return ret; + } ++ } else { ++ if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH && ++ !(*hinfo)->ratematch) || ++ (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) && ++ (*hinfo)->ratematch)) { ++ mutex_unlock(&hashlimit_mutex); ++ htable_put(*hinfo); ++ return -EINVAL; ++ } + } + mutex_unlock(&hashlimit_mutex); + +-- +2.53.0 + diff --git a/queue-6.6/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-6.6/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch new file mode 100644 index 0000000000..f6a9ded944 --- /dev/null +++ b/queue-6.6/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch @@ -0,0 +1,42 @@ +From 3f5794c432321382c87a8b27628d6016568597e9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 12:58:31 +0530 +Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop + +From: Suman Ghosh + +[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ] + +During link down event, we were doing netif_tx_stop_all_queues() first +and then netif_carrier_off(). This can cause a potential race since +carrier is still on during down event. This patch reverse the calling +order to fix the issue. + +Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications") +Signed-off-by: Suman Ghosh +Signed-off-by: Ratheesh Kannoth +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +index caea58f8fd86a..eb7ad5b3b629b 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +@@ -842,8 +842,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf) + netif_carrier_on(netdev); + netif_tx_start_all_queues(netdev); + } else { +- netif_tx_stop_all_queues(netdev); + netif_carrier_off(netdev); ++ netif_tx_stop_all_queues(netdev); + } + } + +-- +2.53.0 + diff --git a/queue-6.6/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch b/queue-6.6/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch new file mode 100644 index 0000000000..9ea42fb635 --- /dev/null +++ b/queue-6.6/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch @@ -0,0 +1,76 @@ +From 2a3894dab9e76ac15ec72a111bc352658a328f48 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 21:08:30 +0530 +Subject: phy: zynqmp: fix clock error handling in xpsgtr_phy_init() + +From: Radhey Shyam Pandey + +[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ] + +Propagate clk_prepare_enable() failures to the caller instead of +returning success, and disable the reference clock on initialization +error paths to avoid leaking clock references when phy_exit() is not +called. + +Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save") +Signed-off-by: Radhey Shyam Pandey +Reviewed-by: Michal Simek +Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index 559a9c26a0040..bf2a06a7f606f 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -643,12 +643,13 @@ static int xpsgtr_phy_init(struct phy *phy) + { + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; +- int ret = 0; ++ int ret; + + mutex_lock(>r_dev->gtr_mutex); + + /* Configure and enable the clock when peripheral phy_init call */ +- if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk])) ++ ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]); ++ if (ret) + goto out; + + /* Skip initialization if not required. */ +@@ -658,7 +659,7 @@ static int xpsgtr_phy_init(struct phy *phy) + if (gtr_dev->tx_term_fix) { + ret = xpsgtr_phy_tx_term_fix(gtr_phy); + if (ret < 0) +- goto out; ++ goto out_disable_clk; + + gtr_dev->tx_term_fix = false; + } +@@ -672,7 +673,7 @@ static int xpsgtr_phy_init(struct phy *phy) + */ + ret = xpsgtr_configure_pll(gtr_phy); + if (ret) +- goto out; ++ goto out_disable_clk; + + xpsgtr_lane_set_protocol(gtr_phy); + +@@ -690,6 +691,10 @@ static int xpsgtr_phy_init(struct phy *phy) + break; + } + ++ goto out; ++ ++out_disable_clk: ++ clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]); + out: + mutex_unlock(>r_dev->gtr_mutex); + return ret; +-- +2.53.0 + diff --git a/queue-6.6/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch b/queue-6.6/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch new file mode 100644 index 0000000000..a85e8602c9 --- /dev/null +++ b/queue-6.6/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch @@ -0,0 +1,56 @@ +From 1d5886533a4923c436ada2549d1343a823fd5037 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 21:08:31 +0530 +Subject: phy: zynqmp: fix runtime PM leak on probe allocation failure + +From: Radhey Shyam Pandey + +[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ] + +Allocate saved_regs before pm_runtime_resume_and_get() so a +devm_kmalloc() failure does not leave an unreleased runtime PM usage +counter. + +Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume") +Signed-off-by: Radhey Shyam Pandey +Reviewed-by: Michal Simek +Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index bf2a06a7f606f..b6fc72287cd99 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -1045,6 +1045,12 @@ static int xpsgtr_probe(struct platform_device *pdev) + return PTR_ERR(provider); + } + ++ gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, ++ sizeof(save_reg_address), ++ GFP_KERNEL); ++ if (!gtr_dev->saved_regs) ++ return -ENOMEM; ++ + pm_runtime_set_active(gtr_dev->dev); + pm_runtime_enable(gtr_dev->dev); + +@@ -1054,12 +1060,6 @@ static int xpsgtr_probe(struct platform_device *pdev) + return ret; + } + +- gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, +- sizeof(save_reg_address), +- GFP_KERNEL); +- if (!gtr_dev->saved_regs) +- return -ENOMEM; +- + return 0; + } + +-- +2.53.0 + diff --git a/queue-6.6/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch b/queue-6.6/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch new file mode 100644 index 0000000000..2a5c917d63 --- /dev/null +++ b/queue-6.6/phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch @@ -0,0 +1,172 @@ +From 7f5a3187e9c9037a0478b98057e353d84d53f714 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 28 Apr 2025 08:35:47 +0200 +Subject: phy-zynqmp: Postpone getting clock rate until actually needed + +From: Mike Looijmans + +[ Upstream commit 065d5885f6180c534b7b176847b3e008f4e11850 ] + +At probe time the driver would display the following error and abort: + xilinx-psgtr fd400000.phy: Invalid rate 0 for reference clock 0 + +At probe time, the associated GTR driver (e.g. SATA or PCIe) hasn't +initialized the clock yet, so clk_get_rate() likely returns 0 if the clock +is programmable. So this driver only works if the clock is fixed. + +The PHY driver doesn't need to know the clock frequency at probe yet, so +wait until the associated driver initializes the lane before requesting the +clock rate setting. + +In addition to allowing the driver to be used with programmable clocks, +this also reduces the driver's runtime memory footprint by removing an +array of pointers from struct xpsgtr_phy. + +Signed-off-by: Mike Looijmans +Acked-by: Michal Simek +Link: https://lore.kernel.org/r/20250428063648.22034-1-mike.looijmans@topic.nl +Signed-off-by: Vinod Koul +Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()") +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 70 +++++++++++++++++---------------- + 1 file changed, 37 insertions(+), 33 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index c72b52955a867..559a9c26a0040 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -227,7 +227,6 @@ struct xpsgtr_phy { + * @siou: siou base address + * @gtr_mutex: mutex for locking + * @phys: PHY lanes +- * @refclk_sscs: spread spectrum settings for the reference clocks + * @clk: reference clocks + * @tx_term_fix: fix for GT issue + * @saved_icm_cfg0: stored value of ICM CFG0 register +@@ -240,7 +239,6 @@ struct xpsgtr_dev { + void __iomem *siou; + struct mutex gtr_mutex; /* mutex for locking */ + struct xpsgtr_phy phys[NUM_LANES]; +- const struct xpsgtr_ssc *refclk_sscs[NUM_LANES]; + struct clk *clk[NUM_LANES]; + bool tx_term_fix; + unsigned int saved_icm_cfg0; +@@ -383,13 +381,40 @@ static int xpsgtr_wait_pll_lock(struct phy *phy) + return ret; + } + ++/* Get the spread spectrum (SSC) settings for the reference clock rate */ ++static const struct xpsgtr_ssc *xpsgtr_find_sscs(struct xpsgtr_phy *gtr_phy) ++{ ++ unsigned long rate; ++ struct clk *clk; ++ unsigned int i; ++ ++ clk = gtr_phy->dev->clk[gtr_phy->refclk]; ++ rate = clk_get_rate(clk); ++ ++ for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { ++ /* Allow an error of 100 ppm */ ++ unsigned long error = ssc_lookup[i].refclk_rate / 10000; ++ ++ if (abs(rate - ssc_lookup[i].refclk_rate) < error) ++ return &ssc_lookup[i]; ++ } ++ ++ dev_err(gtr_phy->dev->dev, "Invalid rate %lu for reference clock %u\n", ++ rate, gtr_phy->refclk); ++ ++ return NULL; ++} ++ + /* Configure PLL and spread-sprectrum clock. */ +-static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) ++static int xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) + { + const struct xpsgtr_ssc *ssc; + u32 step_size; + +- ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk]; ++ ssc = xpsgtr_find_sscs(gtr_phy); ++ if (!ssc) ++ return -EINVAL; ++ + step_size = ssc->step_size; + + xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane), +@@ -431,6 +456,8 @@ static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB, + STEP_SIZE_3_MASK, (step_size & STEP_SIZE_3_MASK) | + FORCE_STEP_SIZE | FORCE_STEPS); ++ ++ return 0; + } + + /* Configure the lane protocol. */ +@@ -643,7 +670,10 @@ static int xpsgtr_phy_init(struct phy *phy) + * Configure the PLL, the lane protocol, and perform protocol-specific + * initialization. + */ +- xpsgtr_configure_pll(gtr_phy); ++ ret = xpsgtr_configure_pll(gtr_phy); ++ if (ret) ++ goto out; ++ + xpsgtr_lane_set_protocol(gtr_phy); + + switch (gtr_phy->protocol) { +@@ -854,8 +884,7 @@ static struct phy *xpsgtr_xlate(struct device *dev, + } + + refclk = args->args[3]; +- if (refclk >= ARRAY_SIZE(gtr_dev->refclk_sscs) || +- !gtr_dev->refclk_sscs[refclk]) { ++ if (refclk >= ARRAY_SIZE(gtr_dev->clk)) { + dev_err(dev, "Invalid reference clock number %u\n", refclk); + return ERR_PTR(-EINVAL); + } +@@ -931,9 +960,7 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) + { + unsigned int refclk; + +- for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) { +- unsigned long rate; +- unsigned int i; ++ for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->clk); ++refclk) { + struct clk *clk; + char name[8]; + +@@ -949,29 +976,6 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) + continue; + + gtr_dev->clk[refclk] = clk; +- +- /* +- * Get the spread spectrum (SSC) settings for the reference +- * clock rate. +- */ +- rate = clk_get_rate(clk); +- +- for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { +- /* Allow an error of 100 ppm */ +- unsigned long error = ssc_lookup[i].refclk_rate / 10000; +- +- if (abs(rate - ssc_lookup[i].refclk_rate) < error) { +- gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i]; +- break; +- } +- } +- +- if (i == ARRAY_SIZE(ssc_lookup)) { +- dev_err(gtr_dev->dev, +- "Invalid rate %lu for reference clock %u\n", +- rate, refclk); +- return -EINVAL; +- } + } + + return 0; +-- +2.53.0 + diff --git a/queue-6.6/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch b/queue-6.6/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch new file mode 100644 index 0000000000..827a3ed633 --- /dev/null +++ b/queue-6.6/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch @@ -0,0 +1,58 @@ +From 13a22cfc40544c1590c407f08a3955c8b05c24d7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 11:28:44 -0500 +Subject: pinctrl-amd: Don't clear S4 wake bits at probe + +From: Mario Limonciello + +[ Upstream commit ffe8a0c6b55285ceaf2f42fc20c3a0594d14f1e9 ] + +commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") +introduced a regression where Wake-on-LAN no longer works after suspend +or shutdown on some AMD platforms. + +Firmware-programmed S4 wake bits for devices like PCIe NICs using PCI +PME are cleared at probe, but nothing restores them. Unlike S0i3/S3 wake +sources that use enable_irq_wake() -> amd_gpio_irq_set_wake(), PCIe PME +does not use GPIO IRQ infrastructure and relies on firmware configuration. + +The original intent of commit 6bc3462a0f5e ("pinctrl: amd: Mask wake +bits on probe again") was to clear spurious wake bits left by firmware +to prevent unwanted wakeups. However, S4 wake bits are used for +hardware-level wake sources like WoL that bypass the kernel's IRQ wake +API. + +Fix by preserving S4 wake bits at probe and only clearing S0i3/S3 bits: +- Firmware-configured S4 wake sources (WoL) continue working +- Kernel maintains control of S3/S0i3 wake policy via set_wake() +- S3-only wake sources work correctly per commit f31f33dbb3ba ("pinctrl: + amd: Take suspend type into consideration which pins are non-wake") + +The trade-off is that firmware-programmed spurious S4 wake bits remain +set, but this is less problematic than breaking WoL. + +Fixes: 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") +Signed-off-by: Mario Limonciello +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/pinctrl-amd.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c +index ba38173d3ed3c..9c937f6cf525c 100644 +--- a/drivers/pinctrl/pinctrl-amd.c ++++ b/drivers/pinctrl/pinctrl-amd.c +@@ -869,8 +869,7 @@ static void amd_gpio_irq_init(struct amd_gpio *gpio_dev) + u32 pin_reg, mask; + int i; + +- mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) | +- BIT(WAKE_CNTRL_OFF_S4); ++ mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3); + + for (i = 0; i < desc->npins; i++) { + int pin = desc->pins[i].number; +-- +2.53.0 + diff --git a/queue-6.6/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch b/queue-6.6/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch new file mode 100644 index 0000000000..a226408427 --- /dev/null +++ b/queue-6.6/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch @@ -0,0 +1,58 @@ +From ab3b68d14d087d6f6b1b74334c82ac2d4b1b748b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 15:08:05 +0200 +Subject: pinctrl: qcom: sc8280xp: Add missing wakeup entries for GPIO143/151 + +From: Konrad Dybcio + +[ Upstream commit 437a8d2aa1aa442c4a176fdf4700a9b3bb0c8794 ] + +Pins 143 and 151 were not included in the PDC wakeup map. They are +normally used for PCIe2A and PCIe3a PERST# respectively, so they're +unlikely to be excercised in practice, but still add them for the sake +of completeness. + +Fixes: c0e4c71a9e7c ("pinctrl: qcom: Introduce sc8280xp TLMM driver") +Signed-off-by: Konrad Dybcio +Link: https://patch.msgid.link/20260626-topic-8280_pinctrl_wakeup-v1-1-2ccb267148f5@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-sc8280xp.c | 21 +++++++++++---------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +index 4b1c49697698d..67945ce867fa1 100644 +--- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c ++++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +@@ -1884,16 +1884,17 @@ static const struct msm_gpio_wakeirq_map sc8280xp_pdc_map[] = { + { 126, 200 }, { 127, 225 }, { 128, 262 }, { 129, 201 }, + { 130, 209 }, { 131, 173 }, { 132, 202 }, { 136, 210 }, + { 138, 171 }, { 139, 226 }, { 140, 227 }, { 142, 228 }, +- { 144, 229 }, { 145, 230 }, { 146, 231 }, { 148, 232 }, +- { 149, 233 }, { 150, 234 }, { 152, 235 }, { 154, 212 }, +- { 157, 213 }, { 161, 219 }, { 170, 236 }, { 171, 221 }, +- { 174, 222 }, { 175, 237 }, { 176, 223 }, { 177, 170 }, +- { 180, 238 }, { 181, 239 }, { 182, 240 }, { 183, 241 }, +- { 184, 242 }, { 185, 243 }, { 190, 178 }, { 193, 184 }, +- { 196, 185 }, { 198, 186 }, { 200, 174 }, { 201, 175 }, +- { 205, 176 }, { 206, 177 }, { 208, 187 }, { 210, 198 }, +- { 211, 199 }, { 212, 204 }, { 215, 205 }, { 220, 188 }, +- { 221, 194 }, { 223, 195 }, { 225, 196 }, { 227, 197 }, ++ { 143, 261 }, { 144, 229 }, { 145, 230 }, { 146, 231 }, ++ { 148, 232 }, { 149, 233 }, { 150, 234 }, { 151, 264 }, ++ { 152, 235 }, { 154, 212 }, { 157, 213 }, { 161, 219 }, ++ { 170, 236 }, { 171, 221 }, { 174, 222 }, { 175, 237 }, ++ { 176, 223 }, { 177, 170 }, { 180, 238 }, { 181, 239 }, ++ { 182, 240 }, { 183, 241 }, { 184, 242 }, { 185, 243 }, ++ { 190, 178 }, { 193, 184 }, { 196, 185 }, { 198, 186 }, ++ { 200, 174 }, { 201, 175 }, { 205, 176 }, { 206, 177 }, ++ { 208, 187 }, { 210, 198 }, { 211, 199 }, { 212, 204 }, ++ { 215, 205 }, { 220, 188 }, { 221, 194 }, { 223, 195 }, ++ { 225, 196 }, { 227, 197 }, + }; + + static struct msm_pinctrl_soc_data sc8280xp_pinctrl = { +-- +2.53.0 + diff --git a/queue-6.6/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-6.6/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch new file mode 100644 index 0000000000..5da39c7c95 --- /dev/null +++ b/queue-6.6/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch @@ -0,0 +1,38 @@ +From 86b58d7766eb874c5ea87469efd29e1b6d37452c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:55 +0200 +Subject: powerpc/boot: Fix simpleboot CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/simpleboot.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c +index c80691d83880b..27591df41e9e8 100644 +--- a/arch/powerpc/boot/simpleboot.c ++++ b/arch/powerpc/boot/simpleboot.c +@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + /* finally, setup the timebase */ + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-6.6/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-6.6/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch new file mode 100644 index 0000000000..8a66701f4c --- /dev/null +++ b/queue-6.6/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch @@ -0,0 +1,38 @@ +From db54d6ba5ed1cf63e86a7d6a04fc36dd2dc257cd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:57 +0200 +Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-akebono.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c +index e3cc2599869cc..1b529037480fb 100644 +--- a/arch/powerpc/boot/treeboot-akebono.c ++++ b/arch/powerpc/boot/treeboot-akebono.c +@@ -146,7 +146,7 @@ void platform_init(char *userdata) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-6.6/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-6.6/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch new file mode 100644 index 0000000000..bc50aded41 --- /dev/null +++ b/queue-6.6/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch @@ -0,0 +1,38 @@ +From 4d4c715751398a91487cb9a930e9b4f2d367f30b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:56 +0200 +Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-currituck.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c +index d53e8a592f816..5b5363b74f9f3 100644 +--- a/arch/powerpc/boot/treeboot-currituck.c ++++ b/arch/powerpc/boot/treeboot-currituck.c +@@ -102,7 +102,7 @@ void platform_init(void) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-6.6/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-6.6/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch new file mode 100644 index 0000000000..83eb184622 --- /dev/null +++ b/queue-6.6/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch @@ -0,0 +1,169 @@ +From 27e9f8ca828160b1021657a80472939985d835d5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 12:43:11 +0200 +Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path + +From: Denis V. Lunev + +[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ] + +A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports +configured wedges the rtnetlink control plane of the whole machine: + + NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms + [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2! + [qede_recovery_handler:2665(ens6f0)]Starting a recovery process + +The recovery path deadlocks on the driver's own mutex: + + qede_sp_task + rtnl_lock() + mutex_lock(&edev->qede_lock) <- taken + qede_recovery_handler + qede_load + udp_tunnel_nic_reset_ntf + __udp_tunnel_nic_device_sync + info->sync_table == qede_udp_tunnel_sync + mutex_lock(&edev->qede_lock) <- same task: deadlock + +The mutex is not recursive, so the kworker blocks on itself with +rtnl_lock held, and neither lock is ever released. Every task that +calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6 +addrconf, sshd) blocks forever while the node still answers ping. +In a vmcore from an affected production node rtnl_mutex.owner +decodes to the very kworker blocked at the innermost mutex_lock() +above. + +Re-sync the tunnel ports from qede_sp_task() after the internal lock +is dropped, still under rtnl_lock as the udp_tunnel API requires. +This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf() +under rtnl without the internal lock. + +qede_recovery_handler() now returns whether it has successfully +reloaded an open device, and the caller re-syncs the ports only in +that case. This keeps the old gating exactly: a device that was down +or a failed recovery returns false, as those paths never reached the +udp_tunnel_nic_reset_ntf() call before either. + +This was the only user of the qede_lock()/qede_unlock() helpers, so +remove them. + +Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra") +Signed-off-by: Denis V. Lunev +CC: Andrew Lunn +CC: "David S. Miller" +CC: Eric Dumazet +CC: Jakub Kicinski +CC: Paolo Abeni +Reviewed-by: Jacob Keller +Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++---------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c +index b5d744d2586f7..59a80f7193723 100644 +--- a/drivers/net/ethernet/qlogic/qede/qede_main.c ++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c +@@ -107,7 +107,7 @@ static void qede_remove(struct pci_dev *pdev); + static void qede_shutdown(struct pci_dev *pdev); + static void qede_link_update(void *dev, struct qed_link_output *link); + static void qede_schedule_recovery_handler(void *dev); +-static void qede_recovery_handler(struct qede_dev *edev); ++static bool qede_recovery_handler(struct qede_dev *edev); + static void qede_schedule_hw_err_handler(void *dev, + enum qed_hw_err_type err_type); + static void qede_get_eth_tlv_data(void *edev, void *data); +@@ -1065,21 +1065,6 @@ void __qede_unlock(struct qede_dev *edev) + mutex_unlock(&edev->qede_lock); + } + +-/* This version of the lock should be used when acquiring the RTNL lock is also +- * needed in addition to the internal qede lock. +- */ +-static void qede_lock(struct qede_dev *edev) +-{ +- rtnl_lock(); +- __qede_lock(edev); +-} +- +-static void qede_unlock(struct qede_dev *edev) +-{ +- __qede_unlock(edev); +- rtnl_unlock(); +-} +- + static void qede_periodic_task(struct work_struct *work) + { + struct qede_dev *edev = container_of(work, struct qede_dev, +@@ -1116,6 +1101,8 @@ static void qede_sp_task(struct work_struct *work) + */ + + if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) { ++ bool reloaded; ++ + cancel_delayed_work_sync(&edev->periodic_task); + #ifdef CONFIG_QED_SRIOV + /* SRIOV must be disabled outside the lock to avoid a deadlock. +@@ -1124,9 +1111,17 @@ static void qede_sp_task(struct work_struct *work) + if (pci_num_vf(edev->pdev)) + qede_sriov_configure(edev->pdev, 0); + #endif +- qede_lock(edev); +- qede_recovery_handler(edev); +- qede_unlock(edev); ++ rtnl_lock(); ++ __qede_lock(edev); ++ reloaded = qede_recovery_handler(edev); ++ __qede_unlock(edev); ++ ++ /* The udp_tunnel core synchronously calls back into ++ * qede_udp_tunnel_sync(), which takes the qede lock. ++ */ ++ if (reloaded) ++ udp_tunnel_nic_reset_ntf(edev->ndev); ++ rtnl_unlock(); + } + + __qede_lock(edev); +@@ -2667,9 +2662,13 @@ static void qede_recovery_failed(struct qede_dev *edev) + edev->ops->common->set_power_state(edev->cdev, PCI_D3hot); + } + +-static void qede_recovery_handler(struct qede_dev *edev) ++/* Returns true if an open device was successfully reloaded and its ++ * udp_tunnel ports need to be re-synced by the caller. ++ */ ++static bool qede_recovery_handler(struct qede_dev *edev) + { + u32 curr_state = edev->state; ++ bool reloaded = false; + int rc; + + DP_NOTICE(edev, "Starting a recovery process\n"); +@@ -2699,17 +2698,18 @@ static void qede_recovery_handler(struct qede_dev *edev) + goto err; + + qede_config_rx_mode(edev->ndev); +- udp_tunnel_nic_reset_ntf(edev->ndev); ++ reloaded = true; + } + + edev->state = curr_state; + + DP_NOTICE(edev, "Recovery handling is done\n"); + +- return; ++ return reloaded; + + err: + qede_recovery_failed(edev); ++ return false; + } + + static void qede_atomic_hw_err_handler(struct qede_dev *edev) +-- +2.53.0 + diff --git a/queue-6.6/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch b/queue-6.6/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch new file mode 100644 index 0000000000..70157a9d84 --- /dev/null +++ b/queue-6.6/rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch @@ -0,0 +1,118 @@ +From 46d0fe91023eefbaf1a63f9bc6849c732e02abf5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 9 Jul 2026 16:27:54 +0000 +Subject: rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled + +From: Ilia Gavrilov + +[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ] + +When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst +is never initialized because inet6_init() exits before addrconf_init() +is called to initialize it. An attempt to bind an RDS socket to +an ipv6 address results in a crash in __ipv6_chk_addr_and_flags() + +KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] +RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0 +Call Trace: + + ipv6_chk_addr+0x3b/0x50 + rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp] + rds_trans_get_preferred+0x15d/0x2d0 [rds] + ? trace_hardirqs_on+0x2d/0x110 + rds_bind+0x1433/0x1d60 [rds] + ? rds_remove_bound+0xd50/0xd50 [rds] + ? aa_af_perm+0x250/0x250 + ? __might_fault+0xde/0x190 + ? __sys_bind+0x1dc/0x210 + __sys_bind+0x1dc/0x210 + ? __ia32_sys_socketpair+0x100/0x100 + ? restore_fpregs_from_fpstate+0x53/0x100 + __x64_sys_bind+0x73/0xb0 + ? syscall_enter_from_user_mode+0x1c/0x50 + do_syscall_64+0x34/0x80 + entry_SYSCALL_64_after_hwframe+0x6e/0xd8 +RIP: 0033:0x7f47f8269ea9 + + +The following code reproduces the issue: + +struct sockaddr_in6 addr; +s = socket(PF_RDS, SOCK_SEQPACKET, 0); + +memset(&addr, 0, sizeof(addr)); +inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr); +addr.sin6_family = AF_INET6; +addr.sin6_port = htons(PORT); + +bind(s, &addr, sizeof(addr)); + +Found by InfoTeCS on behalf of Linux Verification Center +(linuxtesting.org) with Syzkaller. + +Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") +Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support") +Signed-off-by: Ilia Gavrilov +Reviewed-by: Allison Henderson +Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru +Signed-off-by: Jakub Kicinski +Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()") +Signed-off-by: Sasha Levin +--- + net/rds/ib.c | 4 ++++ + net/rds/ib_cm.c | 4 ++++ + net/rds/tcp.c | 8 +++++--- + 3 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/net/rds/ib.c b/net/rds/ib.c +index ce5be43c5fbac..1061bcf7d1315 100644 +--- a/net/rds/ib.c ++++ b/net/rds/ib.c +@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr, + sa = (struct sockaddr *)&sin; + } else { + #if IS_ENABLED(CONFIG_IPV6) ++ if (!ipv6_mod_enabled()) { ++ ret = -EADDRNOTAVAIL; ++ goto out; ++ } + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = *addr; +diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c +index 5289afbb61aa7..e50e01abb0799 100644 +--- a/net/rds/ib_cm.c ++++ b/net/rds/ib_cm.c +@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, + dp = event->param.conn.private_data; + if (isv6) { + #if IS_ENABLED(CONFIG_IPV6) ++ if (!ipv6_mod_enabled()) { ++ err = -EOPNOTSUPP; ++ goto out; ++ } + dp_cmn = &dp->ricp_v6.dp_cmn; + saddr6 = &dp->ricp_v6.dp_saddr; + daddr6 = &dp->ricp_v6.dp_daddr; +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index dee18da64a322..09dd862d7e9f1 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -341,9 +341,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, + rcu_read_unlock(); + } + #if IS_ENABLED(CONFIG_IPV6) +- ret = ipv6_chk_addr(net, addr, dev, 0); +- if (ret) +- return 0; ++ if (ipv6_mod_enabled()) { ++ ret = ipv6_chk_addr(net, addr, dev, 0); ++ if (ret) ++ return 0; ++ } + #endif + return -EADDRNOTAVAIL; + } +-- +2.53.0 + diff --git a/queue-6.6/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-6.6/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch new file mode 100644 index 0000000000..cd8bca2f41 --- /dev/null +++ b/queue-6.6/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch @@ -0,0 +1,85 @@ +From 57bff294ec9ea4f27ee3ec813a0686b867f77add Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 14:02:03 -0700 +Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in + rds_tcp_laddr_check() + +From: Xiang Mei + +[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ] + +rds_tcp_laddr_check() looks up a scoped IPv6 interface with +dev_get_by_index_rcu(), drops the RCU read-side lock, and only then +passes the bare struct net_device * into ipv6_chk_addr(). + +dev_get_by_index_rcu() only keeps the device alive within the same RCU +read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can +free the net_device; ipv6_chk_addr() then dereferences the stale pointer +in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading +freed memory. + +Keep the RCU read-side lock held across the ipv6_chk_addr() call instead +of dropping it right after the lookup, so the device cannot be freed +while it is in use. + + BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + Read of size 8 at addr ffff8880106ec000 by task exploit/153 + Call Trace: + ... + kasan_report (mm/kasan/report.c:595) + __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972) + rds_tcp_laddr_check (net/rds/tcp.c:370) + rds_bind (net/rds/bind.c:248) + __sys_bind (net/socket.c:1920) + __x64_sys_bind (net/socket.c:1956) + do_syscall_64 (arch/x86/entry/syscall_64.c:63) + entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) + +Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") +Reported-by: Weiming Shi +Signed-off-by: Xiang Mei +Reviewed-by: Allison Henderson +Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/rds/tcp.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index 09dd862d7e9f1..997aee359025e 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -330,23 +330,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, + /* If the scope_id is specified, check only those addresses + * hosted on the specified interface. + */ ++ rcu_read_lock(); + if (scope_id != 0) { +- rcu_read_lock(); + dev = dev_get_by_index_rcu(net, scope_id); + /* scope_id is not valid... */ + if (!dev) { + rcu_read_unlock(); + return -EADDRNOTAVAIL; + } +- rcu_read_unlock(); + } + #if IS_ENABLED(CONFIG_IPV6) + if (ipv6_mod_enabled()) { + ret = ipv6_chk_addr(net, addr, dev, 0); +- if (ret) ++ if (ret) { ++ rcu_read_unlock(); + return 0; ++ } + } + #endif ++ rcu_read_unlock(); + return -EADDRNOTAVAIL; + } + +-- +2.53.0 + diff --git a/queue-6.6/sched-add-task_struct-faults_disabled_mapping.patch b/queue-6.6/sched-add-task_struct-faults_disabled_mapping.patch new file mode 100644 index 0000000000..fba2bcfa0e --- /dev/null +++ b/queue-6.6/sched-add-task_struct-faults_disabled_mapping.patch @@ -0,0 +1,107 @@ +From 382f8130e3beb066598255f04254fffd343ce27e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 16 Oct 2019 15:03:50 -0400 +Subject: sched: Add task_struct->faults_disabled_mapping +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Kent Overstreet + +[ Upstream commit 2b69987be575b92adb6c177679f3c559134f0d8f ] + +There has been a long standing page cache coherence bug with direct IO. +This provides part of a mechanism to fix it, currently just used by +bcachefs but potentially worth promoting to the VFS. + +Direct IO evicts the range of the pagecache being read or written to. + +For reads, we need dirty pages to be written to disk, so that the read +doesn't return stale data. For writes, we need to evict that range of +the pagecache so that it's not stale after the write completes. + +However, without a locking mechanism to prevent those pages from being +re-added to the pagecache - by a buffered read or page fault - page +cache inconsistency is still possible. + +This isn't necessarily just an issue for userspace when they're playing +games; filesystems may hang arbitrary state off the pagecache, and so +page cache inconsistency may cause real filesystem bugs, depending on +the filesystem. This is less of an issue for iomap based filesystems, +but e.g. buffer heads caches disk block mappings (!) and attaches them +to the pagecache, and bcachefs attaches disk reservations to pagecache +pages. + +This issue has been hard to fix, because + - we need to add a lock (henceforth called pagecache_add_lock), which + would be held for the duration of the direct IO + - page faults add pages to the page cache, thus need to take the same + lock + - dio -> gup -> page fault thus can deadlock + +And we cannot enforce a lock ordering with this lock, since userspace +will be controlling the lock ordering (via the fd and buffer arguments +to direct IOs), so we need a different method of deadlock avoidance. + +We need to tell the page fault handler that we're already holding a +pagecache_add_lock, and since plumbing it through the entire gup() path +would be highly impractical this adds a field to task_struct. + +Then the full method is: + - in the dio path, when we first take the pagecache_add_lock, note the + mapping in the current task_struct + - in the page fault handler, if faults_disabled_mapping is set, we + check if it's the same mapping as the one we're taking a page fault + for, and if so return an error. + + Then we check lock ordering: if there's a lock ordering violation and + trylock fails, we'll have to cycle the locks and return an error that + tells the DIO path to retry: faults_disabled_mapping is also used for + signalling "locks were dropped, please retry". + +Also relevant to this patch: mapping->invalidate_lock. +mapping->invalidate_lock provides most of the required semantics - it's +used by truncate/fallocate to block pages being added to the pagecache. +However, since it's a rwsem, direct IOs would need to take the write +side in order to block page cache adds, and would then be exclusive with +each other - we'll need a new type of lock to pair with this approach. + +Signed-off-by: Kent Overstreet +Cc: Jan Kara +Cc: Darrick J. Wong +Cc: linux-fsdevel@vger.kernel.org +Cc: Andreas Grünbacher +Stable-dep-of: e876b75b9020 ("ipvs: fix the checksum validations") +Signed-off-by: Sasha Levin +--- + include/linux/sched.h | 1 + + init/init_task.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/include/linux/sched.h b/include/linux/sched.h +index fad3aad97c7b0..c5e90874096e9 100644 +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -878,6 +878,7 @@ struct task_struct { + + struct mm_struct *mm; + struct mm_struct *active_mm; ++ struct address_space *faults_disabled_mapping; + + int exit_state; + int exit_code; +diff --git a/init/init_task.c b/init/init_task.c +index fd9e27185e23a..9b363c986f824 100644 +--- a/init/init_task.c ++++ b/init/init_task.c +@@ -85,6 +85,7 @@ struct task_struct init_task + .nr_cpus_allowed= NR_CPUS, + .mm = NULL, + .active_mm = &init_mm, ++ .faults_disabled_mapping = NULL, + .restart_block = { + .fn = do_no_restart_syscall, + }, +-- +2.53.0 + diff --git a/queue-6.6/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-6.6/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch new file mode 100644 index 0000000000..c4de17fa6e --- /dev/null +++ b/queue-6.6/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch @@ -0,0 +1,56 @@ +From 275739a23e132d0ba096e834af9db5372dcc83c0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 14 Jul 2026 19:49:34 +0900 +Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer + +From: HyeongJun An + +[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ] + +iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the +target-supplied data segment. The segment carries a 2-byte sense length +followed by the sense bytes, so it must hold 2 + senselen bytes, but the +bounds check only requires datalen >= senselen: + + senselen = get_unaligned_be16(data); + if (datalen < senselen) + goto invalid_datalen; + memcpy(sc->sense_buffer, data + 2, + min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); + +A target that returns a SCSI Response whose datalen equals senselen +(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data + +2 read up to two bytes past the received data. Those bytes are stale +conn->data contents and end up in the command's sense buffer, which is +returned to userspace. + +Account for the 2-byte sense length prefix in the check. + +Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi") +Suggested-by: Sashiko AI +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c +index 916c076484608..213aea93120d9 100644 +--- a/drivers/scsi/libiscsi.c ++++ b/drivers/scsi/libiscsi.c +@@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, + } + + senselen = get_unaligned_be16(data); +- if (datalen < senselen) ++ if (datalen < senselen + 2) + goto invalid_datalen; + + memcpy(sc->sense_buffer, data + 2, +-- +2.53.0 + diff --git a/queue-6.6/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-6.6/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch new file mode 100644 index 0000000000..aa7e2116b4 --- /dev/null +++ b/queue-6.6/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch @@ -0,0 +1,71 @@ +From a7572438acbf05c8a9a5cf7b861f90a3ce176ed5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 15:58:48 +0900 +Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the + connection buffer + +From: HyeongJun An + +[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ] + +iscsi_tcp_hdr_dissect() receives the data segment of several PDU types +into the fixed-size conn->data buffer, which is allocated for +ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes. For the LOGIN_RSP, TEXT_RSP, +REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU +whose DataSegmentLength exceeds that buffer. + +The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its +data segment (sense/response data) into conn->data via +iscsi_tcp_data_recv_prep(), but it does so without the same check. The +only upstream bound on in.datalen is conn->max_recv_dlength, the +initiator's advertised MaxRecvDataSegmentLength, which is commonly +negotiated well above 8192 (open-iscsi defaults to 262144). A target +that returns a SCSI Response with a DataSegmentLength between 8193 and +max_recv_dlength therefore overflows the 8192-byte conn->data buffer. + +Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly +like those responses: bound the data segment, receive it into conn->data +when present, and otherwise complete the PDU with no data. Fold the +opcode into that case group rather than duplicating the check. + +Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld") +Suggested-by: Chris Leech +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi_tcp.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c +index c182aa83f2c93..4d23205129432 100644 +--- a/drivers/scsi/libiscsi_tcp.c ++++ b/drivers/scsi/libiscsi_tcp.c +@@ -763,13 +763,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); + spin_unlock(&conn->session->back_lock); + break; +- case ISCSI_OP_SCSI_CMD_RSP: +- if (tcp_conn->in.datalen) { +- iscsi_tcp_data_recv_prep(tcp_conn); +- return 0; +- } +- rc = iscsi_complete_pdu(conn, hdr, NULL, 0); +- break; + case ISCSI_OP_R2T: + if (ahslen) { + rc = ISCSI_ERR_AHSLEN; +@@ -777,6 +770,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + } + rc = iscsi_tcp_r2t_rsp(conn, hdr); + break; ++ case ISCSI_OP_SCSI_CMD_RSP: + case ISCSI_OP_LOGIN_RSP: + case ISCSI_OP_TEXT_RSP: + case ISCSI_OP_REJECT: +-- +2.53.0 + diff --git a/queue-6.6/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch b/queue-6.6/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch new file mode 100644 index 0000000000..180a18f7d9 --- /dev/null +++ b/queue-6.6/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch @@ -0,0 +1,165 @@ +From 339fa9d2fc339dabd04fb527dc560d34b5a6e43c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 16:11:45 +0800 +Subject: scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race + +From: Xingui Yang + +[ Upstream commit 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 ] + +Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue +for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock: +the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue, +calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI +devices and waits for the host to become runtime-active. But the host +cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the +drain is blocked on that very handler. + +However skipping the drain reintroduces a race: hisi_sas returns from +resume before all PHY UP work and libsas discovery work finish. The +controller may then autosuspend while disks are still waking up. The +disks issue IO to a suspended controller, the IO fails, and the disks +get disabled. + +Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT +notification to after sas_drain_work(). By then the host resume is about +to complete, so device removal through device_link no longer blocks on +the resume and the cycle is broken. + +With the deadlock gone, restore sas_resume_ha() (the draining variant) +in hisi_sas and remove sas_resume_ha_no_sync(). + +The reorder is safe for the other libsas consumers (isci, pm8001, +aic94xx, mvsas). During suspend, sas_suspend_devices() calls +sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to +NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a +timed-out phy's disk is immediately rejected by the LLDD before reaching +hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET), +and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both +complete directly via scsi_done() without entering SCSI EH. This is +identical in both the old and new ordering since lldd_dev_gone runs +during suspend, before resume. The reorder only affects when the +PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work() +vs. asynchronous after resume returns), not whether I/O can reach the +device. aic94xx and mvsas do not register any PM ops and never reach +this code path. + +Fixes: fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume") +Signed-off-by: Xingui Yang +Reviewed-by: John Garry +Link: https://patch.msgid.link/20260716081145.3950172-1-yangxingui@huawei.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +------ + drivers/scsi/libsas/sas_init.c | 37 +++++++++++++------------- + include/scsi/libsas.h | 1 - + 3 files changed, 19 insertions(+), 29 deletions(-) + +diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +index 7075dde4584db..d386672d59e4a 100644 +--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c ++++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +@@ -5345,15 +5345,7 @@ static int _resume_v3_hw(struct device *device) + return rc; + } + phys_init_v3_hw(hisi_hba); +- +- /* +- * If a directly-attached disk is removed during suspend, a deadlock +- * may occur, as the PHYE_RESUME_TIMEOUT processing will require the +- * hisi_hba->device to be active, which can only happen when resume +- * completes. So don't wait for the HA event workqueue to drain upon +- * resume. +- */ +- sas_resume_ha_no_sync(sha); ++ sas_resume_ha(sha); + clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags); + + dev_warn(dev, "end of resuming controller\n"); +diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c +index 8586dc79f2a0b..9fcdad2b55a8f 100644 +--- a/drivers/scsi/libsas/sas_init.c ++++ b/drivers/scsi/libsas/sas_init.c +@@ -409,7 +409,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha) + } + } + +-static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) ++void sas_resume_ha(struct sas_ha_struct *ha) + { + const unsigned long tmo = msecs_to_jiffies(25000); + int i; +@@ -425,6 +425,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n", + i, i > 1 ? "s" : ""); + wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo); ++ ++ /* ++ * All phys are back up or timed out. Turn on I/O and drain ++ * pending work. ++ */ ++ scsi_unblock_requests(ha->shost); ++ sas_drain_work(ha); ++ ++ /* ++ * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler ++ * calls sas_deform_port() -> sas_destruct_devices(), which removes ++ * SCSI devices and, for LLDDs using device_link() PM sync, waits ++ * for the host to be runtime-active. Sending it before the drain ++ * would deadlock: the drain waits for the handler, the handler ++ * waits for host resume, and host resume waits for the drain to ++ * finish. ++ */ + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_phy *phy = ha->sas_phy[i]; + +@@ -435,12 +452,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + } + } + +- /* all phys are back up or timed out, turn on i/o so we can +- * flush out disks that did not return +- */ +- scsi_unblock_requests(ha->shost); +- if (drain) +- sas_drain_work(ha); + clear_bit(SAS_HA_RESUMING, &ha->state); + + sas_queue_deferred_work(ha); +@@ -449,20 +460,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + */ + sas_resume_insert_broadcast_ha(ha); + } +- +-void sas_resume_ha(struct sas_ha_struct *ha) +-{ +- _sas_resume_ha(ha, true); +-} + EXPORT_SYMBOL(sas_resume_ha); + +-/* A no-sync variant, which does not call sas_drain_ha(). */ +-void sas_resume_ha_no_sync(struct sas_ha_struct *ha) +-{ +- _sas_resume_ha(ha, false); +-} +-EXPORT_SYMBOL(sas_resume_ha_no_sync); +- + void sas_suspend_ha(struct sas_ha_struct *ha) + { + int i; +diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h +index 8a43534eea5cb..367b06fe5fcb8 100644 +--- a/include/scsi/libsas.h ++++ b/include/scsi/libsas.h +@@ -678,7 +678,6 @@ extern int sas_register_ha(struct sas_ha_struct *); + extern int sas_unregister_ha(struct sas_ha_struct *); + extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha); + extern void sas_resume_ha(struct sas_ha_struct *sas_ha); +-extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha); + extern void sas_suspend_ha(struct sas_ha_struct *sas_ha); + + int sas_set_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates); +-- +2.53.0 + diff --git a/queue-6.6/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch b/queue-6.6/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch new file mode 100644 index 0000000000..9cb17652ac --- /dev/null +++ b/queue-6.6/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch @@ -0,0 +1,80 @@ +From 722836f34972b805dab74c38cc89fc7daff93d95 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 09:30:10 +0300 +Subject: scsi: target: Clear cmd_cnt when initial counter enrollment fails + +From: Leon Romanovsky + +[ Upstream commit a8ddfd2425bbbafadae8700d63ed8a61a4109878 ] + +When target_get_sess_cmd() fails during session shutdown because +percpu_ref_tryget_live() returns false, the command keeps the +se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier +without owning a reference. Final release through +target_release_cmd_kref() then issues an unmatched percpu_ref_put(). + +Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during +cmd setup") moved the cmd_cnt assignment ahead of the reference +acquisition. Clear se_cmd->cmd_cnt whenever the initial +target_get_sess_cmd() fails in target_init_cmd() and +target_submit_tmr(), so release performs exactly one matching put per +acquired reference. + +Fixes: 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup") +Signed-off-by: Leon Romanovsky +Reviewed-by: Mike Christie +Link: https://patch.msgid.link/20260722-reference-count-underflow-in-target-v1-1-63ab664f12fd@nvidia.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/target/target_core_transport.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c +index b9a144a59dff3..f4e3ba173dbe3 100644 +--- a/drivers/target/target_core_transport.c ++++ b/drivers/target/target_core_transport.c +@@ -1670,6 +1670,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, + u32 data_length, int task_attr, int data_dir, int flags) + { + struct se_portal_group *se_tpg; ++ int ret; + + se_tpg = se_sess->se_tpg; + BUG_ON(!se_tpg); +@@ -1699,7 +1700,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, + * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second + * kref_put() to happen during fabric packet acknowledgement. + */ +- return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); ++ ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); ++ if (ret) ++ se_cmd->cmd_cnt = NULL; ++ ++ return ret; + } + EXPORT_SYMBOL_GPL(target_init_cmd); + +@@ -1994,8 +1999,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, + * allocation failure. + */ + ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp); +- if (ret < 0) ++ if (ret < 0) { ++ se_cmd->cmd_cnt = NULL; + return -ENOMEM; ++ } + + if (tm_type == TMR_ABORT_TASK) + se_cmd->se_tmr_req->ref_task_tag = tag; +@@ -2003,6 +2010,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, + /* See target_submit_cmd for commentary */ + ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); + if (ret) { ++ se_cmd->cmd_cnt = NULL; + core_tmr_release_req(se_cmd->se_tmr_req); + return ret; + } +-- +2.53.0 + diff --git a/queue-6.6/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch b/queue-6.6/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch new file mode 100644 index 0000000000..32d0996dff --- /dev/null +++ b/queue-6.6/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch @@ -0,0 +1,54 @@ +From 06804e3b28ece015323f30a57d38f45bc86e5a42 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 15:58:50 +0800 +Subject: scsi: target: iblock: Fix wrong PR ops NULL check for PREEMPT/RELEASE + +From: TanZheng + +[ Upstream commit 9c33222bd387312874fbe36ca8002e5c945b9653 ] + +In the iblock_execute_pr_out() function, PRO_PREEMPT, +PRO_PREEMPT_AND_ABORT, and PRO_RELEASE all perform callback capability +checks through ops->pr_clear. The error check allows unimplemented hooks +to pass through the gate, resulting dereferencing a NULL function +pointer. + +Check whether the hooks that need to be called are supported. + +Fixes: 394f81184882 ("scsi: target: Add block PR support to iblock") +Signed-off-by: TanZheng +Reviewed-by: Mike Christie +Link: https://patch.msgid.link/20260724075850.280699-1-kensanya@163.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/target/target_core_iblock.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c +index a6a06a5f74834..4d025163569ac 100644 +--- a/drivers/target/target_core_iblock.c ++++ b/drivers/target/target_core_iblock.c +@@ -885,7 +885,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, + break; + case PRO_PREEMPT: + case PRO_PREEMPT_AND_ABORT: +- if (!ops->pr_clear) { ++ if (!ops->pr_preempt) { + pr_err("block_device does not support pr_preempt.\n"); + return TCM_UNSUPPORTED_SCSI_OPCODE; + } +@@ -895,8 +895,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, + sa == PRO_PREEMPT_AND_ABORT); + break; + case PRO_RELEASE: +- if (!ops->pr_clear) { +- pr_err("block_device does not support pr_pclear.\n"); ++ if (!ops->pr_release) { ++ pr_err("block_device does not support pr_release.\n"); + return TCM_UNSUPPORTED_SCSI_OPCODE; + } + +-- +2.53.0 + diff --git a/queue-6.6/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch b/queue-6.6/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch new file mode 100644 index 0000000000..cb3409aa7c --- /dev/null +++ b/queue-6.6/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch @@ -0,0 +1,70 @@ +From 7c17ca95b3ce8a19de1970c7f0b7e7b08dd12763 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 01:27:26 +0800 +Subject: scsi: ufs: core: Cancel RTC work in active-active suspend + +From: Guangshuo Li + +[ Upstream commit f71b4a30983b846b4075bf544e835121e70e6a43 ] + +UFS RTC support schedules ufs_rtc_update_work to periodically update the +device RTC. The work can issue query commands and access the UFS host +controller. + +A previous change moved the RTC work cancellation before the PRE_CHANGE +vendor suspend callback to close a race in the common suspend path. +However, the active-active path jumps directly to vops_suspend after +flushing exception handling work and therefore bypasses the +cancellation. + +If the RTC work runs while the vendor suspend callback is gating or +otherwise changing hardware state, it can access the controller during +suspend and trigger an SError. + +Cancel the RTC work before entering the vendor suspend callback in the +active-active path. Since this path now cancels the work, move the RTC +work scheduling outside the device and link state restoration block in +the resume path. This restarts RTC updates after an active-active +suspend and resume cycle. + +Fixes: b0bd84c39289 ("scsi: ufs: core: Fix SError in ufshcd_rtc_work() during UFS suspend") +Signed-off-by: Guangshuo Li +Reviewed-by: Peter Wang +Reviewed-by: Bean Huo +Reviewed-by: Bart Van Assche +Link: https://patch.msgid.link/20260714172726.1736967-1-lgs201920130244@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/ufs/core/ufshcd.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c +index 142abff301220..3bbe1088b7e49 100644 +--- a/drivers/ufs/core/ufshcd.c ++++ b/drivers/ufs/core/ufshcd.c +@@ -9830,6 +9830,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) + req_link_state == UIC_LINK_ACTIVE_STATE) { + ufshcd_disable_auto_bkops(hba); + flush_work(&hba->eeh_work); ++ cancel_delayed_work_sync(&hba->ufs_rtc_update_work); + goto vops_suspend; + } + +@@ -10039,10 +10040,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) + if (ret) + goto set_old_link_state; + ufshcd_set_timestamp_attr(hba); +- schedule_delayed_work(&hba->ufs_rtc_update_work, +- msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); + } + ++ schedule_delayed_work(&hba->ufs_rtc_update_work, ++ msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); ++ + if (ufshcd_keep_autobkops_enabled_except_suspend(hba)) + ufshcd_enable_auto_bkops(hba); + else +-- +2.53.0 + diff --git a/queue-6.6/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-6.6/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch new file mode 100644 index 0000000000..9377ad0688 --- /dev/null +++ b/queue-6.6/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch @@ -0,0 +1,66 @@ +From 36a9acded91f5004274061670e1f885ba40880e6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 09:27:36 +0200 +Subject: scsi: zfcp: Fix memory leak during adapter release by destroying + gid_pn_req + +From: Benjamin Block + +[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ] + +When releasing an adapter we don't free the mempool 'gid_pn_req' that is +allocated during the enqueue. This leaks memory: + + unreferenced object 0xd8d29297de700 (size 256): + comm "(udev-worker)", pid 2105, jiffies 4294945794 + hex dump (first 32 bytes): + 00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00 ......N......... + ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0 ..........._g... + backtrace (crc 4a5b5da2): + [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0 + [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0 + [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150 + [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp] + [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp] + [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp] + [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80 + [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390 + [<000dc45f643d8238>] online_store+0x298/0x5b0 + [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480 + [<000dc45f62c81150>] new_sync_write+0x370/0x4b0 + [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0 + [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0 + [<000dc45f621c4a16>] do_syscall+0x2f6/0x430 + [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0 + [<000dc45f64dc2224>] system_call+0x74/0xa0 + +Fix this by destroying the mempool during the adapter's release. + +Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp") +Signed-off-by: Benjamin Block +Tested-by: M Nikhil +Acked-by: M Nikhil +Reviewed-by: Chinmaya Kajagar +Reviewed-by: Nihar Panda +Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/s390/scsi/zfcp_aux.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c +index ab2f35bc294da..d3cc884ccd599 100644 +--- a/drivers/s390/scsi/zfcp_aux.c ++++ b/drivers/s390/scsi/zfcp_aux.c +@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) + static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) + { + mempool_destroy(adapter->pool.erp_req); ++ mempool_destroy(adapter->pool.gid_pn_req); + mempool_destroy(adapter->pool.scsi_req); + mempool_destroy(adapter->pool.scsi_abort); + mempool_destroy(adapter->pool.qtcb_pool); +-- +2.53.0 + diff --git a/queue-6.6/series b/queue-6.6/series index 441fddf862..e9d5e7c094 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -7,3 +7,79 @@ hid-logitech-dj-prevent-report_id_dj_short-related-u.patch hid-logitech-dj-fix-wrong-detection-of-bad-dj_short-.patch soc-qcom-ice-allow-explicit-votes-on-iface-clock-for.patch thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch +pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch +dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch +dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch +ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch +ata-libahci_platform-support-non-consecutive-port-nu.patch +ahci-introduce-ahci_ignore_port-helper.patch +ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch +asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch +asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch +btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch +phy-zynqmp-postpone-getting-clock-rate-until-actuall.patch +phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch +phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch +netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch +drm-mediatek-check-crtc-state-before-freeing.patch +keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch +keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch +assoc_array-trim-the-final-shortcut-word-using-the-c.patch +netfilter-xt_hashlimit-validate-hashtable-supports-x.patch +sched-add-task_struct-faults_disabled_mapping.patch +ipvs-fix-the-checksum-validations.patch +ipvs-fix-places-with-wrong-packet-offsets.patch +ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch +netfilter-nft_payload-fix-mask-build-for-partial-fie.patch +rds-fix-inet6_addr_lst-null-dereference-when-ipv6-is.patch +rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch +pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch +scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch +scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch +scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch +smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch +spi-spi-cadence-supports-transmission-with-bits_per_.patch +spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch +hwmon-nct6775-core-fix-number-of-temperature-registe.patch +hwmon-lm90-only-report-alarms-if-driver-is-ready.patch +hwmon-nzxt-smart2-dma-align-output-buffer.patch +net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch +hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch +net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch +forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch +hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch +hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch +hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch +hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch +hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch +hwmon-adt7470-use-cached-pwm-frequency-value.patch +hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch +hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch +powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch +powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch +powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch +wifi-mac80211-validate-individual-twt-params-before-.patch +hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch +bluetooth-iso-clear-iso_data-always-when-detaching-c.patch +bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch +bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch +bluetooth-btintel-validate-length-before-parsing-dia.patch +bluetooth-hci_sync-make-hci_cmd_sync_run_once-return.patch +bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch +bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch +bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch +net-phylink-put-link_gpio-if-phylink_create-fails.patch +scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch +scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch +scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch +scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch +net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch +net-sxgbe-check-descriptor-ring-allocation-failures.patch +can-isotp-check-register_netdevice_notifier-error-in.patch +tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch +accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch +net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch +octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch +qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch +ksmbd-return-success-for-deferred-final-close.patch +ksmbd-fix-use-after-free-in-__close_file_table_ids.patch diff --git a/queue-6.6/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-6.6/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch new file mode 100644 index 0000000000..71a09149dc --- /dev/null +++ b/queue-6.6/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch @@ -0,0 +1,85 @@ +From a778b6aacf61fa25feee9825af8db6e2bae7b87f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 28 Jun 2026 14:59:09 +0800 +Subject: smb: client: fix buffer leaks in SMB1 read and write + +From: Dawei Feng + +[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ] + +CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request +buffer before checking whether tcon->ses->server is NULL. If that +defensive check ever fails, the helper returns -ECONNABORTED without +releasing the request buffer. + +Fix these leaks by releasing the allocated request buffer before +returning from these error paths. Use cifs_small_buf_release() for the +buffers allocated by small_smb_init() and cifs_buf_release() for the +buffer allocated by smb_init(). + +The bug was first flagged by an experimental analysis tool we are +developing for kernel memory-management bugs while analyzing +v6.13-rc1. The tool is still under development and is not yet publicly +available. Manual inspection confirms that the bug is still +present in v7.1.1. + +An x86_64 allyesconfig build showed no new warnings. + +Runtime validation used a temporary fault-injection hook to force +tcon->ses->server to NULL after request-buffer initialization. On the +unfixed kernel, the harness observed two leaked small request buffers and +one leaked large request buffer, with directed kmemleak dumps confirming +the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer +deltas remained. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Signed-off-by: Dawei Feng +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/client/cifssmb.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c +index 91f4e50af1e94..877b4ad03fe9c 100644 +--- a/fs/smb/client/cifssmb.c ++++ b/fs/smb/client/cifssmb.c +@@ -1416,8 +1416,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -1529,8 +1531,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -1780,8 +1784,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +-- +2.53.0 + diff --git a/queue-6.6/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch b/queue-6.6/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch new file mode 100644 index 0000000000..ccd48c46bc --- /dev/null +++ b/queue-6.6/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch @@ -0,0 +1,111 @@ +From 307298393a127d324ea21cf7433e0fd2181f647a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 18:25:10 +0530 +Subject: spi: spi-cadence: Move TX FIFO full busy-wait into FIFO +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Srikanth Boyapally + +[ Upstream commit d9eadfce2fac49445db40808fe4d8259f20a9d2b ] + +SPI host transfers could intermittently stall with spi_transfer timeouts. +The TXFULL condition was checked only once in cdns_transfer_one() before +cdns_spi_process_fifo(), so if the FIFO became full again during refill, +writes could be dropped and the transfer would never complete. + +Move the TXFULL busy-wait into the TX path of cdns_spi_process_fifo() so +the 10µs back-off is applied per FIFO entry during filling, ensuring +forward progress and eliminating spurious timeouts. + +Restrict the delay to host mode using spi_controller_is_target(), the +controller is passed into cdns_spi_process_fifo() so the check is made at +the point of use. In target mode this delay must not run as it causes the +target to miss its transfer window and corrupt data. + +Fixes: 49530e641178 ("spi: cadence: Add usleep_range() for cdns_spi_fill_tx_fifo()") +Signed-off-by: Srikanth Boyapally +Reviewed-by: Radhey Shyam Pandey +Link: https://patch.msgid.link/20260720125510.60166-1-srikanth.boyapally@amd.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-cadence.c | 26 ++++++++++++++++---------- + 1 file changed, 16 insertions(+), 10 deletions(-) + +diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c +index 80ec9bbf84187..01b386878ee3a 100644 +--- a/drivers/spi/spi-cadence.c ++++ b/drivers/spi/spi-cadence.c +@@ -385,11 +385,13 @@ static inline void cdns_spi_writer(struct cdns_spi *xspi) + + /** + * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO ++ * @ctlr: Pointer to the spi_controller structure + * @xspi: Pointer to the cdns_spi structure + * @ntx: Number of bytes to pack into the TX FIFO + * @nrx: Number of bytes to drain from the RX FIFO + */ +-static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) ++static void cdns_spi_process_fifo(struct spi_controller *ctlr, ++ struct cdns_spi *xspi, int ntx, int nrx) + { + ntx = clamp(ntx, 0, xspi->tx_bytes); + nrx = clamp(nrx, 0, xspi->rx_bytes); +@@ -404,6 +406,16 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) + } + + if (ntx) { ++ /* When xspi in busy condition, bytes may send failed, ++ * then spi control didn't work thoroughly, add one byte ++ * delay. Only in host mode; in target mode this delay ++ * causes data corruption as the target fails to prepare ++ * data in time. ++ */ ++ if (!spi_controller_is_target(ctlr) && ++ (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)) ++ udelay(10); ++ + cdns_spi_writer(xspi); + ntx--; + } +@@ -457,14 +469,14 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id) + cdns_spi_write(xspi, CDNS_SPI_THLD, 1); + + if (xspi->tx_bytes) { +- cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt); ++ cdns_spi_process_fifo(ctlr, xspi, trans_cnt, trans_cnt); + } else { + /* Fixed delay due to controller limitation with + * RX_NEMPTY incorrect status + * Xilinx AR:65885 contains more details + */ + udelay(10); +- cdns_spi_process_fifo(xspi, 0, trans_cnt); ++ cdns_spi_process_fifo(ctlr, xspi, 0, trans_cnt); + cdns_spi_write(xspi, CDNS_SPI_IDR, + CDNS_SPI_IXR_DEFAULT); + spi_finalize_current_transfer(ctlr); +@@ -517,17 +529,11 @@ static int cdns_transfer_one(struct spi_controller *ctlr, + cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1); + } + +- /* When xspi in busy condition, bytes may send failed, +- * then spi control didn't work thoroughly, add one byte delay +- */ +- if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) +- udelay(10); +- + xspi->n_bytes = cdns_spi_n_bytes(transfer); + xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes); + xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes); + +- cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0); ++ cdns_spi_process_fifo(ctlr, xspi, xspi->tx_fifo_depth, 0); + + cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); + return transfer->len; +-- +2.53.0 + diff --git a/queue-6.6/spi-spi-cadence-supports-transmission-with-bits_per_.patch b/queue-6.6/spi-spi-cadence-supports-transmission-with-bits_per_.patch new file mode 100644 index 0000000000..0bd88b1a76 --- /dev/null +++ b/queue-6.6/spi-spi-cadence-supports-transmission-with-bits_per_.patch @@ -0,0 +1,199 @@ +From 6b49174a8e4a95adf14bc7ae721160fd333d2701 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 31 Oct 2025 15:30:02 +0800 +Subject: spi: spi-cadence: supports transmission with bits_per_word of 16 and + 32 + +From: Jun Guo + +[ Upstream commit 4e00135b2dd1d7924a58bffa551b6ceb3bd836f2 ] + +The default FIFO data width of the Cadence SPI IP is 8 bits, but +the hardware supports configurations of 16 bits and 32 bits. +This patch enhances the driver to support communication with both +16-bits and 32-bits FIFO data widths. + +Signed-off-by: Jun Guo +Link: https://patch.msgid.link/20251031073003.3289573-3-jun.guo@cixtech.com +Signed-off-by: Mark Brown +Stable-dep-of: d9eadfce2fac ("spi: spi-cadence: Move TX FIFO full busy-wait into FIFO") +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-cadence.c | 106 +++++++++++++++++++++++++++++++++----- + 1 file changed, 93 insertions(+), 13 deletions(-) + +diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c +index c6579db42eff3..80ec9bbf84187 100644 +--- a/drivers/spi/spi-cadence.c ++++ b/drivers/spi/spi-cadence.c +@@ -108,6 +108,7 @@ + * @rxbuf: Pointer to the RX buffer + * @tx_bytes: Number of bytes left to transfer + * @rx_bytes: Number of bytes requested ++ * @n_bytes: Number of bytes per word + * @dev_busy: Device busy flag + * @is_decoded_cs: Flag for decoder property set or not + * @tx_fifo_depth: Depth of the TX FIFO +@@ -118,15 +119,23 @@ struct cdns_spi { + struct clk *pclk; + unsigned int clk_rate; + u32 speed_hz; +- const u8 *txbuf; +- u8 *rxbuf; ++ const void *txbuf; ++ void *rxbuf; + int tx_bytes; + int rx_bytes; ++ u8 n_bytes; + u8 dev_busy; + u32 is_decoded_cs; + unsigned int tx_fifo_depth; + }; + ++enum cdns_spi_frame_n_bytes { ++ CDNS_SPI_N_BYTES_NULL = 0, ++ CDNS_SPI_N_BYTES_U8 = 1, ++ CDNS_SPI_N_BYTES_U16 = 2, ++ CDNS_SPI_N_BYTES_U32 = 4 ++}; ++ + /* Macros for the SPI controller read/write */ + static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset) + { +@@ -302,6 +311,78 @@ static int cdns_spi_setup_transfer(struct spi_device *spi, + return 0; + } + ++static u8 cdns_spi_n_bytes(struct spi_transfer *transfer) ++{ ++ if (transfer->bits_per_word <= 8) ++ return CDNS_SPI_N_BYTES_U8; ++ else if (transfer->bits_per_word <= 16) ++ return CDNS_SPI_N_BYTES_U16; ++ else ++ return CDNS_SPI_N_BYTES_U32; ++} ++ ++static inline void cdns_spi_reader(struct cdns_spi *xspi) ++{ ++ u32 rxw = 0; ++ ++ if (xspi->rxbuf && !IS_ALIGNED((uintptr_t)xspi->rxbuf, xspi->n_bytes)) { ++ pr_err("%s: rxbuf address is not aligned for %d bytes\n", ++ __func__, xspi->n_bytes); ++ return; ++ } ++ ++ rxw = cdns_spi_read(xspi, CDNS_SPI_RXD); ++ if (xspi->rxbuf) { ++ switch (xspi->n_bytes) { ++ case CDNS_SPI_N_BYTES_U8: ++ *(u8 *)xspi->rxbuf = rxw; ++ break; ++ case CDNS_SPI_N_BYTES_U16: ++ *(u16 *)xspi->rxbuf = rxw; ++ break; ++ case CDNS_SPI_N_BYTES_U32: ++ *(u32 *)xspi->rxbuf = rxw; ++ break; ++ default: ++ pr_err("%s invalid n_bytes %d\n", __func__, ++ xspi->n_bytes); ++ return; ++ } ++ xspi->rxbuf = (u8 *)xspi->rxbuf + xspi->n_bytes; ++ } ++} ++ ++static inline void cdns_spi_writer(struct cdns_spi *xspi) ++{ ++ u32 txw = 0; ++ ++ if (xspi->txbuf && !IS_ALIGNED((uintptr_t)xspi->txbuf, xspi->n_bytes)) { ++ pr_err("%s: txbuf address is not aligned for %d bytes\n", ++ __func__, xspi->n_bytes); ++ return; ++ } ++ ++ if (xspi->txbuf) { ++ switch (xspi->n_bytes) { ++ case CDNS_SPI_N_BYTES_U8: ++ txw = *(u8 *)xspi->txbuf; ++ break; ++ case CDNS_SPI_N_BYTES_U16: ++ txw = *(u16 *)xspi->txbuf; ++ break; ++ case CDNS_SPI_N_BYTES_U32: ++ txw = *(u32 *)xspi->txbuf; ++ break; ++ default: ++ pr_err("%s invalid n_bytes %d\n", __func__, ++ xspi->n_bytes); ++ return; ++ } ++ cdns_spi_write(xspi, CDNS_SPI_TXD, txw); ++ xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes; ++ } ++} ++ + /** + * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO + * @xspi: Pointer to the cdns_spi structure +@@ -318,23 +399,14 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) + + while (ntx || nrx) { + if (nrx) { +- u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD); +- +- if (xspi->rxbuf) +- *xspi->rxbuf++ = data; +- ++ cdns_spi_reader(xspi); + nrx--; + } + + if (ntx) { +- if (xspi->txbuf) +- cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++); +- else +- cdns_spi_write(xspi, CDNS_SPI_TXD, 0); +- ++ cdns_spi_writer(xspi); + ntx--; + } +- + } + } + +@@ -451,6 +523,10 @@ static int cdns_transfer_one(struct spi_controller *ctlr, + if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) + udelay(10); + ++ xspi->n_bytes = cdns_spi_n_bytes(transfer); ++ xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes); ++ xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes); ++ + cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0); + + cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); +@@ -641,6 +717,9 @@ static int cdns_spi_probe(struct platform_device *pdev) + ctlr->mode_bits = SPI_CPOL | SPI_CPHA; + ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + ++ if (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6")) ++ ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32); ++ + if (!spi_controller_is_target(ctlr)) { + ctlr->mode_bits |= SPI_CS_HIGH; + ctlr->set_cs = cdns_spi_chipselect; +@@ -796,6 +875,7 @@ static const struct dev_pm_ops cdns_spi_dev_pm_ops = { + + static const struct of_device_id cdns_spi_of_match[] = { + { .compatible = "xlnx,zynq-spi-r1p6" }, ++ { .compatible = "cix,sky1-spi-r1p6" }, + { .compatible = "cdns,spi-r1p6" }, + { /* end of table */ } + }; +-- +2.53.0 + diff --git a/queue-6.6/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-6.6/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch new file mode 100644 index 0000000000..67e3dd4ba1 --- /dev/null +++ b/queue-6.6/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch @@ -0,0 +1,43 @@ +From 74a639dada7fbdb3cf56c7f978717661ff34d667 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 21:49:51 +0900 +Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data() + +From: Masami Hiramatsu (Google) + +[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ] + +mmio_reset_data() is called during tracer initialization, reset, and +start. While it resets overrun_detected and prev_overruns, it neglects +to reset dropped_count. Consequently, dropped event counts from prior +tracing sessions persist in dropped_count and corrupt overrun reports +in subsequent runs. + +Fix this by explicitly calling atomic_set(&dropped_count, 0) in +mmio_reset_data(). + +Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2 +Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording") +Assisted-by: Antigravity:gemini-3.6-flash +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_mmiotrace.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c +index 4d9e5c830dbe1..c523ce5aa4958 100644 +--- a/kernel/trace/trace_mmiotrace.c ++++ b/kernel/trace/trace_mmiotrace.c +@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr) + { + overrun_detected = false; + prev_overruns = 0; ++ atomic_set(&dropped_count, 0); + + tracing_reset_online_cpus(&tr->array_buffer); + } +-- +2.53.0 + diff --git a/queue-6.6/wifi-mac80211-validate-individual-twt-params-before-.patch b/queue-6.6/wifi-mac80211-validate-individual-twt-params-before-.patch new file mode 100644 index 0000000000..cbb4360a8e --- /dev/null +++ b/queue-6.6/wifi-mac80211-validate-individual-twt-params-before-.patch @@ -0,0 +1,52 @@ +From 835a8edbac2cce177c4e73f4f3b1c5a6ebc813cd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 09:09:28 +0800 +Subject: wifi: mac80211: validate individual TWT params before driver setup + +From: Zhao Li + +[ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ] + +ieee80211_process_rx_twt_action() only partially validates a received +S1G TWT setup frame before queueing it. + +An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup() +with twt->length too short for the full struct ieee80211_twt_params. + +The individual path passes twt to drv_add_twt_setup(). Both the tracepoint +and the driver callback consume the complete parameters block, not merely +req_type. Do not pass a short individual agreement to the driver. +Broadcast agreements remain unchanged because they are rejected locally +after accessing only req_type. + +Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode") +Assisted-by: Codex:gpt-5 +Assisted-by: Claude:opus-4.8 +Signed-off-by: Zhao Li +Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com +[edit commit message to not overclaim lack of validation nor + understate driver impact] +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/s1g.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c +index c1f964e9991cd..9914390ff31ff 100644 +--- a/net/mac80211/s1g.c ++++ b/net/mac80211/s1g.c +@@ -100,6 +100,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata, + struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable; + struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + ++ if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) && ++ twt->length < sizeof(twt->control) + sizeof(*twt_agrt)) ++ return; ++ + twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST); + + /* broadcast TWT not supported yet */ +-- +2.53.0 + diff --git a/queue-7.1/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch b/queue-7.1/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch new file mode 100644 index 0000000000..4982666e68 --- /dev/null +++ b/queue-7.1/accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch @@ -0,0 +1,49 @@ +From ad4ff7d5d60cbf44cb845beeda20f54ffa421a58 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Jun 2026 02:25:20 +0500 +Subject: accel/qaic: use sizeof(*trans_hdr) for transaction length check + +From: Muhammad Bilal + +[ Upstream commit d6c075f797a672a6e3bd2fd44aee713801698ec2 ] + +In encode_message() the per-transaction lower-bound check compares +trans_hdr->len against sizeof(trans_hdr), i.e. the size of the pointer, +instead of sizeof(*trans_hdr), the size of struct qaic_manage_trans_hdr. + +Every other length check in this file (encode_message() at the loop +guard, decode_message(), etc.) correctly uses sizeof(*trans_hdr), so +this is an inconsistency. On 64-bit builds the pointer and the struct +are both 8 bytes, so the check is correct by coincidence and there is +no behavioural change. On 32-bit builds the pointer is 4 bytes, which +weakens the minimum-length check below the 8-byte header size. + +Use sizeof(*trans_hdr) so the check validates against the actual +transaction header size on all builds. + +Fixes: ea33cb6fc278 ("accel/qaic: tighten bounds checking in encode_message()") +Signed-off-by: Muhammad Bilal +Reviewed-by: Jeff Hugo +Signed-off-by: Jeff Hugo +Link: https://patch.msgid.link/20260617212520.59801-1-meatuni001@gmail.com +Signed-off-by: Sasha Levin +--- + drivers/accel/qaic/qaic_control.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c +index 43f84d4389602..4d4e789d5fcb8 100644 +--- a/drivers/accel/qaic/qaic_control.c ++++ b/drivers/accel/qaic/qaic_control.c +@@ -786,7 +786,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg, + break; + } + trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len); +- if (trans_hdr->len < sizeof(trans_hdr) || ++ if (trans_hdr->len < sizeof(*trans_hdr) || + size_add(user_len, trans_hdr->len) > user_msg->len) { + ret = -EINVAL; + break; +-- +2.53.0 + diff --git a/queue-7.1/acpi-cppc-skip-writes-to-unsupported-performance-con.patch b/queue-7.1/acpi-cppc-skip-writes-to-unsupported-performance-con.patch new file mode 100644 index 0000000000..a9a4835208 --- /dev/null +++ b/queue-7.1/acpi-cppc-skip-writes-to-unsupported-performance-con.patch @@ -0,0 +1,61 @@ +From a1c2472d5fda092f0f4d7a202f57a138995c459c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 11:40:42 +0100 +Subject: ACPI: CPPC: Skip writes to unsupported performance controls + +From: Christian Loehle + +[ Upstream commit 47d4e945dff8139050473be4ab263a32e1da910c ] + +MIN_PERF and MAX_PERF are optional CPPC controls. DESIRED_PERF is also +optional with CPPC2 when autonomous selection is supported. + +The cppc-cpufreq target callbacks populate both limits for every request +without checking whether the controls are implemented. cppc_set_perf() +consequently passes NULL register descriptors to cpc_write(). The writes +fail width validation and their return values are ignored, so the failed +access paths are repeated on every target request. An autonomous-only +platform can take the same path for DESIRED_PERF. + +Check that each performance control is supported before calling +cpc_write(). + +Fixes: ea3db45ae476 ("cpufreq: cppc: Update MIN_PERF/MAX_PERF in target callbacks") +Reviewed-by: Sumit Gupta +Signed-off-by: Christian Loehle +Reviewed-by: Lifeng Zheng +Link: https://patch.msgid.link/20260724104042.1481804-1-christian.loehle@arm.com +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Sasha Levin +--- + drivers/acpi/cppc_acpi.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c +index 34edec0f2bde2..aa4017be960a0 100644 +--- a/drivers/acpi/cppc_acpi.c ++++ b/drivers/acpi/cppc_acpi.c +@@ -1940,16 +1940,17 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) + cpc_desc->write_cmd_status = 0; + } + +- cpc_write(cpu, desired_reg, perf_ctrls->desired_perf); ++ if (CPC_SUPPORTED(desired_reg)) ++ cpc_write(cpu, desired_reg, perf_ctrls->desired_perf); + + /* + * Only write if min_perf and max_perf not zero. Some drivers pass zero + * value to min and max perf, but they don't mean to set the zero value, + * they just don't want to write to those registers. + */ +- if (perf_ctrls->min_perf) ++ if (perf_ctrls->min_perf && CPC_SUPPORTED(min_perf_reg)) + cpc_write(cpu, min_perf_reg, perf_ctrls->min_perf); +- if (perf_ctrls->max_perf) ++ if (perf_ctrls->max_perf && CPC_SUPPORTED(max_perf_reg)) + cpc_write(cpu, max_perf_reg, perf_ctrls->max_perf); + + if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg)) +-- +2.53.0 + diff --git a/queue-7.1/af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch b/queue-7.1/af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch new file mode 100644 index 0000000000..e8ec81ae9f --- /dev/null +++ b/queue-7.1/af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch @@ -0,0 +1,41 @@ +From 841abc906f2843e538fa657f3a5617484a53f88a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 18 Jul 2026 14:29:01 -0400 +Subject: af_unix: fix listen() succeeding on sockets in the wrong state + +From: John Ericson + +[ Upstream commit f0d9c3ffc2b5fc2ffacb56b3036155ce7a940a12 ] + +Commit fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for +reaped sk->sk_peer_pid") inserted a prepare_peercred() call between err += -EINVAL and the socket-state check in unix_listen(). Since +prepare_peercred() leaves err at 0 on success, listen() on an AF_UNIX +socket that is not in TCP_CLOSE or TCP_LISTEN state (e.g. one that is +already connected) now silently returns success without doing anything, +instead of failing with EINVAL as it did before. + +Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid") +Signed-off-by: John Ericson +Link: https://patch.msgid.link/20260718182903.2295560-1-John.Ericson@Obsidian.Systems +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/unix/af_unix.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c +index 0d9cd977c7b78..bb01d5371dc85 100644 +--- a/net/unix/af_unix.c ++++ b/net/unix/af_unix.c +@@ -823,6 +823,7 @@ static int unix_listen(struct socket *sock, int backlog) + if (err) + goto out; + unix_state_lock(sk); ++ err = -EINVAL; + if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN) + goto out_unlock; + if (backlog > sk->sk_max_ack_backlog) +-- +2.53.0 + diff --git a/queue-7.1/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-7.1/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..2508e3b691 --- /dev/null +++ b/queue-7.1/asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From 2183c56240c4328b35f328977144f7c460278018 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:12:54 +0530 +Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ] + +In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98090->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98090.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c +index 13a15459040fc..595efd84b62b3 100644 +--- a/sound/soc/codecs/max98090.c ++++ b/sound/soc/codecs/max98090.c +@@ -2424,8 +2424,9 @@ static int max98090_probe(struct snd_soc_component *component) + dev_dbg(component->dev, "max98090_probe\n"); + + max98090->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98090->mclk)) ++ if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + max98090->component = component; + +-- +2.53.0 + diff --git a/queue-7.1/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch b/queue-7.1/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch new file mode 100644 index 0000000000..057caa4536 --- /dev/null +++ b/queue-7.1/asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch @@ -0,0 +1,61 @@ +From 373bb5657533fa799b4e9990109b626f541062ef Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:09:50 +0530 +Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup + +From: Uday Khare + +[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ] + +In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is +broken due to a missing IS_ERR() guard. + +The code intends to return -EPROBE_DEFER only when the clock lookup +fails with that specific error. However, without IS_ERR() the check: + + if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) + +is called unconditionally, including when devm_clk_get() succeeds and +returns a valid pointer. Calling PTR_ERR() on a valid pointer +reinterprets its address as a signed long; the result is arbitrary +and is almost never equal to -EPROBE_DEFER, so the check silently +does nothing in the success case. When devm_clk_get() fails with +any error other than -EPROBE_DEFER the check is also skipped, leaving +max98095->mclk holding an error pointer with no indication to the caller. + +This means a deferred probe will never actually be triggered for this +device, and any non-EPROBE_DEFER clock error is silently swallowed with +the error pointer left in the mclk field. + +Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, +matching the pattern already used in the sibling max98088 and wm8960 +drivers. + +Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling") +Signed-off-by: Uday Khare +Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/max98095.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c +index aae6423156e16..207374e451810 100644 +--- a/sound/soc/codecs/max98095.c ++++ b/sound/soc/codecs/max98095.c +@@ -1987,8 +1987,9 @@ static int max98095_probe(struct snd_soc_component *component) + int ret = 0; + + max98095->mclk = devm_clk_get(component->dev, "mclk"); +- if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) +- return -EPROBE_DEFER; ++ if (IS_ERR(max98095->mclk)) ++ if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + + /* reset the codec, the DSP core, and disable all interrupts */ + max98095_reset(component); +-- +2.53.0 + diff --git a/queue-7.1/asoc-sdca-always-free-firmware-in-fdl-path.patch b/queue-7.1/asoc-sdca-always-free-firmware-in-fdl-path.patch new file mode 100644 index 0000000000..5beb95fab0 --- /dev/null +++ b/queue-7.1/asoc-sdca-always-free-firmware-in-fdl-path.patch @@ -0,0 +1,50 @@ +From a5a48fd2539aa85000227c8a2416946955a962f5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 11:34:58 +0100 +Subject: ASoC: SDCA: Always free firmware in FDL path + +From: Charles Keepax + +[ Upstream commit 7f64ccc374b2fe1f6a169182e95ab8e104cae406 ] + +In the case a disk firmware exists but is invalid and no SWFT firmware +exists fdl_load_file() will return without calling release_firmware(). +Update the code to call this to ensure the firmware is released on the +error path. + +Fixes: 71f7990a34cd ("ASoC: SDCA: Add FDL library for XU entities") +Signed-off-by: Charles Keepax +Reviewed-by: Pierre-Louis Bossart +Link: https://patch.msgid.link/20260722103500.872714-3-ckeepax@opensource.cirrus.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/sdca/sdca_fdl.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c +index 994821a6df617..60fdd406220d4 100644 +--- a/sound/soc/sdca/sdca_fdl.c ++++ b/sound/soc/sdca/sdca_fdl.c +@@ -258,7 +258,8 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, + + if (!swf) { + dev_err(dev, "failed to locate SWF\n"); +- return -ENOENT; ++ ret = -ENOENT; ++ goto error; + } + + dev_info(dev, "loading SWF: %x-%x-%x\n", +@@ -270,6 +271,8 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, + SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset, + SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data, + swf->file_length - offsetof(struct acpi_sw_file, data)); ++ ++error: + release_firmware(firmware); + return ret; + } +-- +2.53.0 + diff --git a/queue-7.1/asoc-sdca-correct-pointer-passed-to-devm_acpi_table_.patch b/queue-7.1/asoc-sdca-correct-pointer-passed-to-devm_acpi_table_.patch new file mode 100644 index 0000000000..c8da204855 --- /dev/null +++ b/queue-7.1/asoc-sdca-correct-pointer-passed-to-devm_acpi_table_.patch @@ -0,0 +1,41 @@ +From 09c00944ef8abd28ceb4c089d4cdb026e1b3ecb7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 11:34:57 +0100 +Subject: ASoC: SDCA: Correct pointer passed to devm_acpi_table_put + +From: Charles Keepax + +[ Upstream commit 6a50332e194f58b562d396ab772c34e8c9890c0f ] + +devm_acpi_table_put() takes a struct acpi_table_header * but the value +passed in is struct acpi_table_header ** so the value passed to +acpi_put_table() is actually the pointer not the table itself. + +Remove the extra reference to correct the passed value. + +Fixes: c4d096c3ca42 ("ASoC: SDCA: Add SDCA FDL data parsing") +Signed-off-by: Charles Keepax +Reviewed-by: Pierre-Louis Bossart +Link: https://patch.msgid.link/20260722103500.872714-2-ckeepax@opensource.cirrus.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/sdca/sdca_device.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/sdca/sdca_device.c b/sound/soc/sdca/sdca_device.c +index 405e80b979de8..4bcd8d1fdff82 100644 +--- a/sound/soc/sdca/sdca_device.c ++++ b/sound/soc/sdca/sdca_device.c +@@ -43,7 +43,7 @@ void sdca_lookup_swft(struct sdw_slave *slave) + dev_info(&slave->dev, "SWFT not available\n"); + else + devm_add_action_or_reset(&slave->dev, devm_acpi_table_put, +- &slave->sdca_data.swft); ++ slave->sdca_data.swft); + } + EXPORT_SYMBOL_NS(sdca_lookup_swft, "SND_SOC_SDCA"); + +-- +2.53.0 + diff --git a/queue-7.1/asoc-sdca-ensure-that-control-range-is-large-enough-.patch b/queue-7.1/asoc-sdca-ensure-that-control-range-is-large-enough-.patch new file mode 100644 index 0000000000..104f11039c --- /dev/null +++ b/queue-7.1/asoc-sdca-ensure-that-control-range-is-large-enough-.patch @@ -0,0 +1,39 @@ +From a6590b364b86db91b2d5986984dcf9cbe5933851 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 11:35:00 +0100 +Subject: ASoC: SDCA: Ensure that Control Range is large enough for header + +From: Charles Keepax + +[ Upstream commit 951e921b039b793bef7050eaf5c5fb1a4a5341d1 ] + +When reading the Ranges structure from an SDCA Control, ensure that the +read data is large enough to encompass the required header before +accessing it. + +Fixes: 64fb5af1d1bb ("ASoC: SDCA: Add parsing for Control range structures") +Signed-off-by: Charles Keepax +Reviewed-by: Pierre-Louis Bossart +Link: https://patch.msgid.link/20260722103500.872714-5-ckeepax@opensource.cirrus.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/sdca/sdca_functions.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c +index 196bade11ab5d..ecc372a397573 100644 +--- a/sound/soc/sdca/sdca_functions.c ++++ b/sound/soc/sdca/sdca_functions.c +@@ -847,6 +847,8 @@ static int find_sdca_control_range(struct device *dev, + return 0; + else if (num_range < 0) + return num_range; ++ else if (num_range < 2 * sizeof(*limits)) ++ return -EINVAL; + + range_list = devm_kcalloc(dev, num_range, sizeof(*range_list), GFP_KERNEL); + if (!range_list) +-- +2.53.0 + diff --git a/queue-7.1/asoc-sdca-make-ump-message-size-check-more-robust.patch b/queue-7.1/asoc-sdca-make-ump-message-size-check-more-robust.patch new file mode 100644 index 0000000000..74ef5dc010 --- /dev/null +++ b/queue-7.1/asoc-sdca-make-ump-message-size-check-more-robust.patch @@ -0,0 +1,48 @@ +From 392aca6ce74c6e3de1e44b12d79b0c2eb0106f2b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 11:34:59 +0100 +Subject: ASoC: SDCA: Make UMP message size check more robust + +From: Charles Keepax + +[ Upstream commit 556d872e7c2a0b570c5b0974813847ef0d0cd637 ] + +If message offset was larger than the buffer length the size +check will pass incorrectly. Refactor the check such that it is +more robust to invalid sizes. + +Fixes: daab108504be ("ASoC: SDCA: Add UMP buffer helper functions") +Signed-off-by: Charles Keepax +Reviewed-by: Pierre-Louis Bossart +Link: https://patch.msgid.link/20260722103500.872714-4-ckeepax@opensource.cirrus.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/sdca/sdca_ump.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/sdca/sdca_ump.c b/sound/soc/sdca/sdca_ump.c +index a86bb28c6d0ad..82a8bf75bbca5 100644 +--- a/sound/soc/sdca/sdca_ump.c ++++ b/sound/soc/sdca/sdca_ump.c +@@ -141,7 +141,7 @@ int sdca_ump_read_message(struct device *dev, + return ret; + } + +- if (msg_len > buf_len - msg_offset) { ++ if (msg_offset + msg_len > buf_len) { + dev_err(dev, "%s: message too big for UMP buffer: %d\n", + entity->label, msg_len); + return -EINVAL; +@@ -207,7 +207,7 @@ int sdca_ump_write_message(struct device *dev, + buf_len = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_LENGTH, 0); + ump_mode = sdca_range(range, SDCA_MESSAGEOFFSET_UMP_MODE, 0); + +- if (msg_len > buf_len - msg_offset) { ++ if (msg_offset + msg_len > buf_len) { + dev_err(dev, "%s: message too big for UMP buffer: %d\n", + entity->label, msg_len); + return -EINVAL; +-- +2.53.0 + diff --git a/queue-7.1/asoc-sophgo-return-1-on-volume-change-in-cv1800b_adc.patch b/queue-7.1/asoc-sophgo-return-1-on-volume-change-in-cv1800b_adc.patch new file mode 100644 index 0000000000..582171f2d3 --- /dev/null +++ b/queue-7.1/asoc-sophgo-return-1-on-volume-change-in-cv1800b_adc.patch @@ -0,0 +1,63 @@ +From 0646487e2f1c1806c5c6f907566ee563ac5025fa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 11:08:04 +0530 +Subject: ASoC: sophgo: return 1 on volume change in cv1800b_adc_volume_set() + +From: Surendra Singh Chouhan + +[ Upstream commit f47064c970d3e920a27435454c02df310695f6e1 ] + +cv1800b_adc_volume_set() serves as the .put callback for the "Internal +I2S Capture Volume" control. + +ALSA mixer control callbacks must return 1 when the register value is +modified, 0 if unchanged, or a negative error code on failure. Returning +0 unconditionally causes ALSA core to assume the value was unchanged, +suppressing SNDRV_CTL_EVENT_MASK_VALUE change notifications to userspace +sound servers (e.g. PipeWire/PulseAudio). + +Fix this by comparing the new register value with the existing register +value. If unchanged, return 0; otherwise, write the updated value and +return 1. + +Fixes: 4cf8752a03e6 ("ASoC: sophgo: add CV1800B internal ADC codec driver") +Signed-off-by: Surendra Singh Chouhan +Link: https://patch.msgid.link/20260727053804.25599-1-kr494167@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/sophgo/cv1800b-sound-adc.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/sophgo/cv1800b-sound-adc.c b/sound/soc/sophgo/cv1800b-sound-adc.c +index b66761156b993..bd93e261bdd1d 100644 +--- a/sound/soc/sophgo/cv1800b-sound-adc.c ++++ b/sound/soc/sophgo/cv1800b-sound-adc.c +@@ -251,16 +251,22 @@ static int cv1800b_adc_volume_set(struct snd_kcontrol *kcontrol, + + u32 v_left = clamp_t(u32, ucontrol->value.integer.value[0], 0, 24); + u32 v_right = clamp_t(u32, ucontrol->value.integer.value[1], 0, 24); +- u32 val; ++ u32 val, old_val; + + val = readl(priv->regs + CV1800B_RXADC_ANA0); ++ old_val = val; ++ + val = u32_replace_bits(val, cv1800b_gains[v_left], + REG_COMB_LEFT_VOLUME); + val = u32_replace_bits(val, cv1800b_gains[v_right], + REG_COMB_RIGHT_VOLUME); ++ ++ if (val == old_val) ++ return 0; ++ + writel(val, priv->regs + CV1800B_RXADC_ANA0); + +- return 0; ++ return 1; + } + + static DECLARE_TLV_DB_SCALE(cv1800b_volume_tlv, 0, 200, 0); +-- +2.53.0 + diff --git a/queue-7.1/asoc-tas2781-use-correct-calibration-data-for-sinega.patch b/queue-7.1/asoc-tas2781-use-correct-calibration-data-for-sinega.patch new file mode 100644 index 0000000000..0ed468c273 --- /dev/null +++ b/queue-7.1/asoc-tas2781-use-correct-calibration-data-for-sinega.patch @@ -0,0 +1,44 @@ +From 8b830e9af599911cb9fb72639a594f06accbcbbd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:16:16 +0800 +Subject: ASoC: tas2781: Use correct calibration data for SINEGAIN2 register + +From: wangdicheng + +[ Upstream commit dd88cf6273de61f2f7206c2066af97798dbb38b0 ] + +The SINEGAIN2_REG case in cali_reg_update() references t->sin_gn[] +rather than t->sin_gn2[], causing the second pilot tone gain +calibration to be programmed with the wrong register address. + +These are distinct fields in struct fct_param_address and are +populated from separate firmware parameters by the parser in +tas2781-fmwlib.c. + +Fixes: 84d6a465f211 ("ASoC: tas2781: Support dsp firmware Alpha and Beta seaies") +Signed-off-by: wangdicheng +Link: https://patch.msgid.link/20260720081616.631413-1-wangdich9700@163.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/tas2781-i2c.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c +index a78a8f9b98338..775c3767bf3a8 100644 +--- a/sound/soc/codecs/tas2781-i2c.c ++++ b/sound/soc/codecs/tas2781-i2c.c +@@ -1308,8 +1308,8 @@ static void cali_reg_update(struct bulk_reg_val *p, + t->sin_gn[2]); + break; + case TAS2781_PRM_SINEGAIN2_REG: +- reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1], +- t->sin_gn[2]); ++ reg = TASDEVICE_REG(t->sin_gn2[0], t->sin_gn2[1], ++ t->sin_gn2[2]); + break; + default: + reg = 0; +-- +2.53.0 + diff --git a/queue-7.1/assoc_array-trim-the-final-shortcut-word-using-the-c.patch b/queue-7.1/assoc_array-trim-the-final-shortcut-word-using-the-c.patch new file mode 100644 index 0000000000..15bdd3fee1 --- /dev/null +++ b/queue-7.1/assoc_array-trim-the-final-shortcut-word-using-the-c.patch @@ -0,0 +1,66 @@ +From 1037e819f922161518086b14489e5777cc8be47d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:05 -0400 +Subject: assoc_array: trim the final shortcut word using the current chunk end + +From: Michael Bommarito + +[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ] + +assoc_array_walk() masks off the bits past shortcut->skip_to_level in the +word that contains skip_to_level, gated on +round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level. + +That guard is wrong in two opposite ways: + + - When sc_level is word-aligned (every word after the first) round_up() + is a no-op, so the guard is sc_level > skip_to_level and never fires for + the word that holds skip_to_level. A shortcut that spans more than one + word and ends in the middle of its last word leaves that word untrimmed, + and its stale high bits leak into the dissimilarity word and can steer + the walk down the wrong descendant. + + - When sc_level is unaligned (the first word) and skip_to_level sits on + the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and + fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears + the whole dissimilarity word and makes a differing shortcut compare + equal. + +Use the end of the chunk that contains sc_level instead: + + skip_to_level < round_down(sc_level, CHUNK) + CHUNK + +For an aligned sc_level whose word holds skip_to_level this now fires (the +first bug); for an unaligned sc_level with skip_to_level on the following +boundary it does not, so shift is never 0 when the branch runs and the trim +never clears the whole word. + +Fixes: 3cb989501c26 ("Add a generic associative array implementation.") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + lib/assoc_array.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/assoc_array.c b/lib/assoc_array.c +index bcc6e0a013eb8..b6c9723e12ced 100644 +--- a/lib/assoc_array.c ++++ b/lib/assoc_array.c +@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array, + sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT]; + dissimilarity = segments ^ sc_segments; + +- if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) { ++ if (shortcut->skip_to_level < round_down(sc_level, ++ ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) { + /* Trim segments that are beyond the shortcut */ + int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK; + dissimilarity &= ~(ULONG_MAX << shift); +-- +2.53.0 + diff --git a/queue-7.1/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch b/queue-7.1/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch new file mode 100644 index 0000000000..f27d3c0c3f --- /dev/null +++ b/queue-7.1/ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch @@ -0,0 +1,76 @@ +From 1638affa848653f9fcac82d18a7a7e83ea24f923 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 17 Jul 2026 23:55:26 +0530 +Subject: ata: ahci_ceva: fix error paths in + ceva_ahci_platform_enable_resources() + +From: Radhey Shyam Pandey + +[ Upstream commit 4d99a91574c420decab56cc880fad0dc15b8a7a3 ] + +On phy_init() failure the error path fallsthrough to disable_rsts, which +deasserts the controller reset and then enters disable_phys calling +phy_power_off() on PHYs that were never powered on. That corrupts the PHY +power_count and triggers an extra runtime PM put. + +Use a separate exit_phys path that unwinds with phy_exit() only and falls +through to disable_clks while the controller remains in reset. Reserve +phy_power_off() for the phy_power_on() failure path only, and skip +masked-out ports in both unwind loops. + +On phy_power_on() failure re-assert the controller reset before disabling +clocks and regulators, matching the teardown order used by +ahci_platform_enable_resources() and ahci_platform_disable_resources(). + +Fixes: 26c8404e162b ("ata: ahci_ceva: fix error handling for Xilinx GT PHY support") +Signed-off-by: Radhey Shyam Pandey +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + drivers/ata/ahci_ceva.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c +index 2d6a08c23d6ad..2961e53288f42 100644 +--- a/drivers/ata/ahci_ceva.c ++++ b/drivers/ata/ahci_ceva.c +@@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + + rc = phy_init(hpriv->phys[i]); + if (rc) +- goto disable_rsts; ++ goto exit_phys; + } + + /* De-assert the controller reset */ +@@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + + return 0; + +-disable_rsts: +- ahci_platform_deassert_rsts(hpriv); +- + disable_phys: + while (--i >= 0) { ++ if (ahci_ignore_port(hpriv, i)) ++ continue; ++ + phy_power_off(hpriv->phys[i]); + phy_exit(hpriv->phys[i]); + } ++ ahci_platform_assert_rsts(hpriv); ++ goto disable_clks; ++ ++exit_phys: ++ while (--i >= 0) { ++ if (ahci_ignore_port(hpriv, i)) ++ continue; ++ ++ phy_exit(hpriv->phys[i]); ++ } + + disable_clks: + ahci_platform_disable_clks(hpriv); +-- +2.53.0 + diff --git a/queue-7.1/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch b/queue-7.1/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch new file mode 100644 index 0000000000..2bf04b3ea7 --- /dev/null +++ b/queue-7.1/ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch @@ -0,0 +1,44 @@ +From a44c8a2214cdbdb81d48ed2ff7473077dbc96dd9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 15:31:37 -0700 +Subject: ata: sata_mv: accept 1 or 2 resources in platform probe + +From: Rosen Penev + +[ Upstream commit ef19a9cf037957fe3a35df8355c76ff0a63a0436 ] + +Board files in arch/arm/plat-orion, arch/arm/mach-dove, +arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the +"sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ). +Those devices are rejected with -EINVAL, so SATA no longer probes on +legacy Marvell Orion/Kirkwood-style boards. + +Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2 +resources (legacy, IRQ supplied as a second resource) so both probing +paths work. + +Fixes: b3b2bec9646e ("ata: sata_mv: Fixes expected number of resources now IRQs are gone") +Assisted-by: opencode:big-pickle +Signed-off-by: Rosen Penev +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + drivers/ata/sata_mv.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c +index ffb396f61731f..1d8ca95fb7328 100644 +--- a/drivers/ata/sata_mv.c ++++ b/drivers/ata/sata_mv.c +@@ -4026,7 +4026,7 @@ static int mv_platform_probe(struct platform_device *pdev) + /* + * Simple resource validation .. + */ +- if (unlikely(pdev->num_resources != 1)) { ++ if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) { + dev_err(&pdev->dev, "invalid number of resources\n"); + return -EINVAL; + } +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-btintel-validate-length-before-parsing-dia.patch b/queue-7.1/bluetooth-btintel-validate-length-before-parsing-dia.patch new file mode 100644 index 0000000000..67c97d7d62 --- /dev/null +++ b/queue-7.1/bluetooth-btintel-validate-length-before-parsing-dia.patch @@ -0,0 +1,40 @@ +From cda0a3385cf2f3a0e7a3bd0d9304d9e3c8396b74 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 01:54:40 -0700 +Subject: Bluetooth: btintel: Validate length before parsing diagnostics TLV + +From: Zijun Hu + +[ Upstream commit b640ff9af3c809ff5ea2077fbba17df1594ec1e4 ] + +btintel_diagnostics() accesses tlv->val[0] without first validating +that the diagnostics VSE is long enough to contain that field, so +may cause reading data beyond the received frame. + +Fix by validating the length before access. + +Fixes: af395330abed ("Bluetooth: btintel: Add Intel devcoredump support") +Signed-off-by: Zijun Hu +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + drivers/bluetooth/btintel.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c +index 5e9cac090bd8f..bf567b7c5f00b 100644 +--- a/drivers/bluetooth/btintel.c ++++ b/drivers/bluetooth/btintel.c +@@ -3771,6 +3771,9 @@ static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb) + { + struct intel_tlv *tlv = (void *)&skb->data[5]; + ++ if (skb->len < 5 + sizeof(*tlv) + sizeof(tlv->val[0])) ++ goto recv_frame; ++ + /* The first event is always an event type TLV */ + if (tlv->type != INTEL_TLV_TYPE_ID) + goto recv_frame; +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch b/queue-7.1/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch new file mode 100644 index 0000000000..36a810e547 --- /dev/null +++ b/queue-7.1/bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch @@ -0,0 +1,55 @@ +From c373769b704e3b579c2247073d2521f9bd5d190c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:17 +0300 +Subject: Bluetooth: hci_conn: hold conn reference in abort_conn_sync() + +From: Pauli Virtanen + +[ Upstream commit 5761d003daa987ac81463f570713ce9c9dd204e5 ] + +There is theoretical UAF if the conn is freed while the hci_sync task is +running. + +Hold refcount to avoid that. + +Fixes: 227a0cdf4a02 ("Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_conn.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c +index 924c7795e368a..fb96d019cf856 100644 +--- a/net/bluetooth/hci_conn.c ++++ b/net/bluetooth/hci_conn.c +@@ -3168,6 +3168,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data) + return hci_abort_conn_sync(hdev, conn, conn->abort_reason); + } + ++static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err) ++{ ++ struct hci_conn *conn = data; ++ ++ hci_conn_put(conn); ++} ++ + int hci_abort_conn(struct hci_conn *conn, u8 reason) + { + struct hci_dev *hdev = conn->hdev; +@@ -3193,7 +3200,10 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) + * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does + * already queue its callback on cmd_sync_work. + */ +- err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); ++ err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn), ++ abort_conn_destroy); ++ if (err) ++ hci_conn_put(conn); + return (err == -EEXIST) ? 0 : err; + } + +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch b/queue-7.1/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch new file mode 100644 index 0000000000..91e26127d6 --- /dev/null +++ b/queue-7.1/bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch @@ -0,0 +1,43 @@ +From 89a5fe25ec8b9da48236eef63dd03031d14dd758 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:22 +0300 +Subject: Bluetooth: hci_sync: fix hci_conn_del() use in + hci_le_create_conn_sync + +From: Pauli Virtanen + +[ Upstream commit 2c1e4e00613dfd105f978be2276e5e265801ec9f ] + +hci_conn_del() caller must hold hdev->lock, check the conn was not +concurrently deleted, and usually inform socket the conn is going to be +deleted. + +Use hci_abort_conn_sync() instead of calling hci_conn_del() without +locks etc. + +Fixes: 8e8b92ee60de5 ("Bluetooth: hci_sync: Add hci_le_create_conn_sync") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_sync.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 70ac18fe74448..0bb015da6f88c 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -6672,7 +6672,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + if (hci_dev_test_flag(hdev, HCI_LE_SCAN) && + hdev->le_scan_type == LE_SCAN_ACTIVE && + !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) { +- hci_conn_del(conn); ++ conn->state = BT_OPEN; ++ hci_abort_conn_sync(hdev, conn, ++ HCI_ERROR_REJ_LIMITED_RESOURCES); + hci_conn_put(conn); + return -EBUSY; + } +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_acl-le_s.patch b/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_acl-le_s.patch new file mode 100644 index 0000000000..796020bda0 --- /dev/null +++ b/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_acl-le_s.patch @@ -0,0 +1,103 @@ +From ea5fa13433e9bd5edcfef8a682619b6892bccaf3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:18 +0300 +Subject: Bluetooth: hci_sync: hold conn in hci_connect_acl/le_sync() callbacks + +From: Pauli Virtanen + +[ Upstream commit 2f5d635ad5906b0235bc0c870e8beba3116e1e98 ] + +There is theoretical UAF if the conn is freed while the hci_sync task +is running. + +Hold refcount to avoid that. + +Fixes: 881559af5f5c ("Bluetooth: hci_sync: Attempt to dequeue connection attempt") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_sync.c | 32 ++++++++++++++++++++++++-------- + 1 file changed, 24 insertions(+), 8 deletions(-) + +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 42bbffba2172e..2cac69922353a 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -7067,12 +7067,23 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) + return err; + } + ++static void hci_acl_create_conn_sync_complete(struct hci_dev *hdev, void *data, ++ int err) ++{ ++ struct hci_conn *conn = data; ++ ++ hci_conn_put(conn); ++} ++ + int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn) + { + int err; + +- err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn, +- NULL); ++ err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, ++ hci_conn_get(conn), ++ hci_acl_create_conn_sync_complete); ++ if (err) ++ hci_conn_put(conn); + return (err == -EEXIST) ? 0 : err; + } + +@@ -7083,36 +7094,41 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err) + bt_dev_dbg(hdev, "err %d", err); + + if (err == -ECANCELED) +- return; ++ goto done; + + hci_dev_lock(hdev); + + if (!hci_conn_valid(hdev, conn)) +- goto done; ++ goto unlock; + + if (!err) { + hci_connect_le_scan_cleanup(conn, 0x00); +- goto done; ++ goto unlock; + } + + /* Check if connection is still pending */ + if (conn != hci_lookup_le_connect(hdev)) +- goto done; ++ goto unlock; + + /* Flush to make sure we send create conn cancel command if needed */ + flush_delayed_work(&conn->le_conn_timeout); + hci_conn_failed(conn, bt_status(err)); + +-done: ++unlock: + hci_dev_unlock(hdev); ++done: ++ hci_conn_put(conn); + } + + int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn) + { + int err; + +- err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn, ++ err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, ++ hci_conn_get(conn), + create_le_conn_complete); ++ if (err) ++ hci_conn_put(conn); + return (err == -EEXIST) ? 0 : err; + } + +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_big_sync.patch b/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_big_sync.patch new file mode 100644 index 0000000000..626dc6da16 --- /dev/null +++ b/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_big_sync.patch @@ -0,0 +1,62 @@ +From 6e7a89d05dbeab9afe15db80ef89a4dd9377de14 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:19 +0300 +Subject: Bluetooth: hci_sync: hold conn in hci_connect_big_sync() callback + +From: Pauli Virtanen + +[ Upstream commit 56e78b670356caab0b607e8aad4cf819a1909d07 ] + +There is theoretical UAF if the conn is freed while the hci_sync task is +running. + +Hold refcount to avoid that. Handle NULL hcon, return 0 + do nothing to +match the previous behavior. + +Fixes: 024421cf3992 ("Bluetooth: hci_conn: Fix not setting timeout for BIG Create Sync") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_sync.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 2cac69922353a..2860779e6a791 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -7437,10 +7437,12 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) + bt_dev_dbg(hdev, "err %d", err); + + if (err == -ECANCELED) +- return; ++ goto done; + +- if (hci_conn_valid(hdev, conn)) +- clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags); ++ clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags); ++ ++done: ++ hci_conn_put(conn); + } + + static int hci_le_big_create_sync(struct hci_dev *hdev, void *data) +@@ -7492,8 +7494,14 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn) + { + int err; + +- err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn, ++ if (!conn) ++ return 0; ++ ++ err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, ++ hci_conn_get(conn), + create_big_complete); ++ if (err) ++ hci_conn_put(conn); + return (err == -EEXIST) ? 0 : err; + } + +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_pa_sync-.patch b/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_pa_sync-.patch new file mode 100644 index 0000000000..ada431b5ad --- /dev/null +++ b/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_connect_pa_sync-.patch @@ -0,0 +1,60 @@ +From 5bd6218bb74a63f1a00a0852a279dc084d840b7a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:20 +0300 +Subject: Bluetooth: hci_sync: hold conn in hci_connect_pa_sync() callback + +From: Pauli Virtanen + +[ Upstream commit 44fc74069d8988f2825246f9401218e29de2c0ab ] + +There is theoretical UAF if the conn is freed while the hci_sync task is +running. + +Hold refcount to avoid that. + +Fixes: 6d0417e4e1cf ("Bluetooth: hci_conn: Fix not setting conn_timeout for Broadcast Receiver") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_sync.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 2860779e6a791..6c3dc910fdeba 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -7251,7 +7251,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err) + bt_dev_dbg(hdev, "err %d", err); + + if (err == -ECANCELED) +- return; ++ goto done; + + hci_dev_lock(hdev); + +@@ -7275,6 +7275,8 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err) + + unlock: + hci_dev_unlock(hdev); ++done: ++ hci_conn_put(conn); + } + + static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn, +@@ -7425,8 +7427,11 @@ int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn) + { + int err; + +- err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn, ++ err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, ++ hci_conn_get(conn), + create_pa_complete); ++ if (err) ++ hci_conn_put(conn); + return (err == -EEXIST) ? 0 : err; + } + +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_past_sync-callba.patch b/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_past_sync-callba.patch new file mode 100644 index 0000000000..16e65ffe6b --- /dev/null +++ b/queue-7.1/bluetooth-hci_sync-hold-conn-in-hci_past_sync-callba.patch @@ -0,0 +1,62 @@ +From 44cf060da682e227d36059cd9384ec2ab138ab3f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:21 +0300 +Subject: Bluetooth: hci_sync: hold conn in hci_past_sync() callback + +From: Pauli Virtanen + +[ Upstream commit abf9753edf3f88282c44a605f3945d8d4f8dd86c ] + +Avoids giving freed pointers to hci_conn_valid(), which kmalloc may have +reused. + +Hold refcount to avoid that. + +Fixes: d3413703d5f8 ("Bluetooth: ISO: Add support to bind to trigger PAST") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_sync.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 6c3dc910fdeba..70ac18fe74448 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -7521,6 +7521,8 @@ static void past_complete(struct hci_dev *hdev, void *data, int err) + + bt_dev_dbg(hdev, "err %d", err); + ++ hci_conn_put(past->conn); ++ hci_conn_put(past->le); + kfree(past); + } + +@@ -7585,8 +7587,8 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le) + if (!data) + return -ENOMEM; + +- data->conn = conn; +- data->le = le; ++ data->conn = hci_conn_get(conn); ++ data->le = hci_conn_get(le); + + if (conn->role == HCI_ROLE_MASTER) + err = hci_cmd_sync_queue_once(conn->hdev, +@@ -7596,8 +7598,11 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le) + err = hci_cmd_sync_queue_once(conn->hdev, hci_le_past_sync, + data, past_complete); + +- if (err) ++ if (err) { ++ hci_conn_put(data->conn); ++ hci_conn_put(data->le); + kfree(data); ++ } + + return (err == -EEXIST) ? 0 : err; + } +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch b/queue-7.1/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch new file mode 100644 index 0000000000..016162d745 --- /dev/null +++ b/queue-7.1/bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch @@ -0,0 +1,80 @@ +From 10974998169f87f40f6b734c795d3c75ba4fb469 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 12:59:23 +0300 +Subject: Bluetooth: hci_sync: remove unnecessary hci_conn_get in + create_conn_sync + +From: Pauli Virtanen + +[ Upstream commit c0a9dcd2be398eee505d4b254ec3a845aa8ab189 ] + +hci_conn_get() without already held reference is data race against +concurrent deletion. + +In previous patches, the refcount has been changed to be taken before +starting the hci_sync task, so remove these extra get() + put() as they +are not needed. + +Fixes: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/hci_sync.c | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c +index 0bb015da6f88c..540da127ecea6 100644 +--- a/net/bluetooth/hci_sync.c ++++ b/net/bluetooth/hci_sync.c +@@ -6656,11 +6656,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + + bt_dev_dbg(hdev, "conn %p", conn); + +- /* Hold a reference so conn stays valid for the HCI_CONN_CREATE +- * clear_bit() at done. +- */ +- hci_conn_get(conn); +- + clear_bit(HCI_CONN_SCANNING, &conn->flags); + conn->state = BT_CONNECT; + +@@ -6675,7 +6670,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + conn->state = BT_OPEN; + hci_abort_conn_sync(hdev, conn, + HCI_ERROR_REJ_LIMITED_RESOURCES); +- hci_conn_put(conn); + return -EBUSY; + } + +@@ -6773,7 +6767,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) + + /* Re-enable advertising after the connection attempt is finished. */ + hci_resume_advertising_sync(hdev); +- hci_conn_put(conn); + return err; + } + +@@ -7048,11 +7041,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) + else + cp.role_switch = 0x00; + +- /* Hold a reference so conn stays valid for the HCI_CONN_CREATE +- * clear_bit() below. +- */ +- hci_conn_get(conn); +- + /* Mark create connection in flight so hci_cancel_connect_sync() can + * cancel it while blocking on the connection complete event. + */ +@@ -7064,7 +7052,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) + conn->conn_timeout, NULL); + + clear_bit(HCI_CONN_CREATE, &conn->flags); +- hci_conn_put(conn); + + return err; + } +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch b/queue-7.1/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch new file mode 100644 index 0000000000..c9335e0e69 --- /dev/null +++ b/queue-7.1/bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch @@ -0,0 +1,188 @@ +From 6b764e6ec0f66c5302a4f397c3f3a992b99af25d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:31 +0300 +Subject: Bluetooth: ISO: avoid deadlocks in iso_sock_timeout + +From: Pauli Virtanen + +[ Upstream commit 200fa1629c57a3ca2b03d3ca63fd3a9bfd910c43 ] + +iso_sock_timeout() takes lock_sock, so sync disabling the timer while +holding that lock may deadlock. + +iso_sock_timeout() may also run concurrently with iso_conn_del(), which +leads to UAF + + [Task 1] [Task hdev->workqueue] + iso_sock_timeout iso_conn_del + iso_conn_hold_unless_zero iso_chan_del + `------------> iso_conn_put + caller frees hcon + iso_conn_put + iso_conn_free + conn->hcon->iso_data = NULL; /* UAF */ + +Fix the deadlock by removing the disable from the lock_sock sections. +Move the timer from iso_conn to iso_pinfo to decouple it from iso_conn +which may need to be freed in lock_sock section. Convert some of the +clear_timer to disable_timer. + +Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 60 ++++++++++++++++++++++----------------------- + 1 file changed, 29 insertions(+), 31 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 1ca3576357006..7e1dcaa22a5b2 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -36,8 +36,6 @@ struct iso_conn { + spinlock_t lock; + struct sock *sk; + +- struct delayed_work timeout_work; +- + struct sk_buff *rx_skb; + __u32 rx_len; + __u16 tx_sn; +@@ -80,6 +78,7 @@ struct iso_pinfo { + __u8 base_len; + __u8 base[BASE_MAX_LENGTH]; + struct iso_conn *conn; ++ struct delayed_work timeout_work; + }; + + static struct bt_iso_qos default_qos; +@@ -117,9 +116,6 @@ static void iso_conn_free(struct kref *ref) + hci_conn_drop(conn->hcon); + } + +- /* Ensure no more work items will run since hci_conn has been dropped */ +- disable_delayed_work_sync(&conn->timeout_work); +- + kfree_skb(conn->rx_skb); + + kfree(conn); +@@ -160,48 +156,45 @@ static struct sock *iso_sock_hold(struct iso_conn *conn) + + static void iso_sock_timeout(struct work_struct *work) + { +- struct iso_conn *conn = container_of(work, struct iso_conn, +- timeout_work.work); +- struct sock *sk; +- +- conn = iso_conn_hold_unless_zero(conn); +- if (!conn) +- return; +- +- iso_conn_lock(conn); +- sk = iso_sock_hold(conn); +- iso_conn_unlock(conn); +- iso_conn_put(conn); +- +- if (!sk) +- return; ++ struct iso_pinfo *pi = container_of(work, struct iso_pinfo, ++ timeout_work.work); ++ struct sock *sk = &pi->bt.sk; + + BT_DBG("sock %p state %d", sk, sk->sk_state); + + lock_sock(sk); +- sk->sk_err = ETIMEDOUT; +- sk->sk_state_change(sk); ++ if (!sock_flag(sk, SOCK_ZAPPED)) { ++ sk->sk_err = ETIMEDOUT; ++ sk->sk_state_change(sk); ++ } + release_sock(sk); +- sock_put(sk); + } + + static void iso_sock_set_timer(struct sock *sk, long timeout) + { ++ lockdep_assert(lockdep_sock_is_held(sk)); ++ ++ cancel_delayed_work(&iso_pi(sk)->timeout_work); ++ + if (!iso_pi(sk)->conn) + return; + + BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout); +- cancel_delayed_work(&iso_pi(sk)->conn->timeout_work); +- schedule_delayed_work(&iso_pi(sk)->conn->timeout_work, timeout); ++ schedule_delayed_work(&iso_pi(sk)->timeout_work, timeout); + } + + static void iso_sock_clear_timer(struct sock *sk) + { +- if (!iso_pi(sk)->conn) +- return; ++ BT_DBG("sock %p state %d", sk, sk->sk_state); ++ cancel_delayed_work(&iso_pi(sk)->timeout_work); ++} ++ ++static void iso_sock_disable_timer(struct sock *sk) ++{ ++ lockdep_assert(!lockdep_sock_is_held(sk)); + + BT_DBG("sock %p state %d", sk, sk->sk_state); +- cancel_delayed_work(&iso_pi(sk)->conn->timeout_work); ++ disable_delayed_work_sync(&iso_pi(sk)->timeout_work); + } + + /* ---- ISO connections ---- */ +@@ -226,7 +219,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon) + + kref_init(&conn->ref); + spin_lock_init(&conn->lock); +- INIT_DELAYED_WORK(&conn->timeout_work, iso_sock_timeout); + + hcon->iso_data = conn; + conn->hcon = hcon; +@@ -291,8 +283,9 @@ static void iso_conn_del(struct hci_conn *hcon, int err) + return; + } + ++ iso_sock_disable_timer(sk); ++ + lock_sock(sk); +- iso_sock_clear_timer(sk); + iso_chan_del(sk, err); + release_sock(sk); + iso_sock_kill(sk); +@@ -799,6 +792,8 @@ static void iso_sock_cleanup_listen(struct sock *parent) + */ + static void iso_sock_kill(struct sock *sk) + { ++ iso_sock_disable_timer(sk); ++ + lock_sock(sk); + + if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || +@@ -894,8 +889,9 @@ static void __iso_sock_close(struct sock *sk) + /* Must be called on unlocked socket. */ + static void iso_sock_close(struct sock *sk) + { ++ iso_sock_disable_timer(sk); ++ + lock_sock(sk); +- iso_sock_clear_timer(sk); + __iso_sock_close(sk); + release_sock(sk); + } +@@ -964,6 +960,8 @@ static struct sock *iso_sock_alloc(struct net *net, struct socket *sock, + iso_pi(sk)->qos = default_qos; + iso_pi(sk)->sync_handle = -1; + ++ INIT_DELAYED_WORK(&iso_pi(sk)->timeout_work, iso_sock_timeout); ++ + bt_sock_link(&iso_sk_list, sk); + return sk; + } +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch b/queue-7.1/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch new file mode 100644 index 0000000000..ad65d3ae03 --- /dev/null +++ b/queue-7.1/bluetooth-iso-clear-iso_data-always-when-detaching-c.patch @@ -0,0 +1,40 @@ +From 3d0a5282240734913f27319a3318db5bbcf77579 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 17:53:33 +0300 +Subject: Bluetooth: ISO: clear iso_data always when detaching conn from hcon + +From: Pauli Virtanen + +[ Upstream commit d57e506f6a1e3929611340fae87c1e4823f4d85c ] + +When setting conn->hcon = NULL, also conn->hcon->iso_data = NULL is +necessary, otherwise later iso_conn_free() will UAF. + +Fix clearing of iso_data in iso_sock_disconn() + +Fixes KASAN: slab-use-after-free in iso_conn_hold_unless_zero on +iso_sock_release() followed by hci_abort_conn_sync(). + +Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 1d5ce87e6496e..6bbbbce4c5f27 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -836,6 +836,7 @@ static void iso_sock_disconn(struct sock *sk) + sk->sk_state = BT_DISCONN; + iso_conn_lock(iso_pi(sk)->conn); + hci_conn_drop(iso_pi(sk)->conn->hcon); ++ iso_pi(sk)->conn->hcon->iso_data = NULL; + iso_pi(sk)->conn->hcon = NULL; + iso_conn_unlock(iso_pi(sk)->conn); + } +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch b/queue-7.1/bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch new file mode 100644 index 0000000000..96bcee23c7 --- /dev/null +++ b/queue-7.1/bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch @@ -0,0 +1,109 @@ +From 795a94bfe3665cf2b4e908e27546ca8515f8ea2d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:32 +0300 +Subject: Bluetooth: ISO: ensure no dangling hcon references in iso_conn + +From: Pauli Virtanen + +[ Upstream commit aa9f7cb2bd3a2be998ceb739fc9a2f986eba43eb ] + +After iso_conn_del(), ISO sockets should not dereference the hcon any +more. Currently, clearing iso_conn::hcon relies on iso_conn_del() +releasing the last reference to the iso_conn. + +Simplify this by explicitly clearing conn->hcon in iso_conn_del(), to +avoid more complex reasoning on races about who holds the last +reference. + +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: fdfde532ab1c ("Bluetooth: ISO: fix refcounting of iso_conn") +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 24 +++++++++++++++++++++--- + 1 file changed, 21 insertions(+), 3 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 7e1dcaa22a5b2..31ceb1338dc23 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -262,6 +262,7 @@ static void iso_chan_del(struct sock *sk, int err) + } + + static void iso_conn_del(struct hci_conn *hcon, int err) ++ __must_hold(&hcon->hdev->lock) + { + struct iso_conn *conn = hcon->iso_data; + struct sock *sk; +@@ -276,11 +277,10 @@ static void iso_conn_del(struct hci_conn *hcon, int err) + iso_conn_lock(conn); + sk = iso_sock_hold(conn); + iso_conn_unlock(conn); +- iso_conn_put(conn); + + if (!sk) { + iso_conn_put(conn); +- return; ++ goto done; + } + + iso_sock_disable_timer(sk); +@@ -290,6 +290,15 @@ static void iso_conn_del(struct hci_conn *hcon, int err) + release_sock(sk); + iso_sock_kill(sk); + sock_put(sk); ++ ++done: ++ /* No sk access to conn->hcon any more (lock_sock + hdev->lock) */ ++ iso_conn_lock(conn); ++ conn->hcon = NULL; ++ hcon->iso_data = NULL; ++ iso_conn_unlock(conn); ++ ++ iso_conn_put(conn); + } + + static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, +@@ -305,6 +314,11 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, + return -EBUSY; + } + ++ if (!conn->hcon) { ++ BT_ERR("conn->hcon missing"); ++ return -EIO; ++ } ++ + iso_pi(sk)->conn = conn; + conn->sk = sk; + clear_bit(ISO_CONN_DROPPED, conn->flags); +@@ -2499,6 +2513,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + } + + static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) ++ __must_hold(&hcon->hdev->lock) + { + if (hcon->type != CIS_LINK && hcon->type != BIS_LINK && + hcon->type != PA_LINK) { +@@ -2510,8 +2525,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) + struct hci_link *link, *t; + + list_for_each_entry_safe(link, t, &hcon->link_list, +- list) ++ list) { ++ lockdep_assert_held(&link->conn->hdev->lock); + iso_conn_del(link->conn, bt_to_errno(status)); ++ } + + return; + } +@@ -2541,6 +2558,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) + } + + static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason) ++ __must_hold(&hcon->hdev->lock) + { + if (hcon->type != CIS_LINK && hcon->type != BIS_LINK && + hcon->type != PA_LINK) +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-iso-fix-connected-closed-transition-on-shu.patch b/queue-7.1/bluetooth-iso-fix-connected-closed-transition-on-shu.patch new file mode 100644 index 0000000000..c75a2ec772 --- /dev/null +++ b/queue-7.1/bluetooth-iso-fix-connected-closed-transition-on-shu.patch @@ -0,0 +1,107 @@ +From 43d82ac82a722b68053915c4407fb60de16d73ed Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:24 +0300 +Subject: Bluetooth: ISO: fix CONNECTED -> CLOSED transition on + shutdown/release + +From: Pauli Virtanen + +[ Upstream commit 0786469ee242952008628ed0e2d386098e2065ab ] + +Commit d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon") +merged a version of the UAF fix that breaks releasing connected +ISO sockets. Since hci_conn::iso_data is set to NULL, iso_chan_del() won't +be called when the hci_conn disconnects, and the ISO socket does not emit +POLLHUP correctly. + +Fix by retaining full hci_conn <-> iso_conn association while in +BT_DISCONNECT state, so that local disconnect via shutdown() follows +similar ISO socket code path as remote disconnect. Use a separate flag +to track whether hci_conn_drop() is needed, instead of setting +iso_conn::hcon = NULL + +In iso_sock_ready(), disallow disconnecting socket going BT_CONNECTED, +in case hcon connects while its drop is pending. + +Fixes: d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon") +Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 26 ++++++++++++++++++++------ + 1 file changed, 20 insertions(+), 6 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 6bbbbce4c5f27..f1399ddd6ad05 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -23,8 +23,14 @@ static struct bt_sock_list iso_sk_list = { + }; + + /* ---- ISO connections ---- */ ++enum { ++ ISO_CONN_DROPPED, ++ __ISO_CONN_NUM_FLAGS ++}; ++ + struct iso_conn { + struct hci_conn *hcon; ++ DECLARE_BITMAP(flags, __ISO_CONN_NUM_FLAGS); + + /* @lock: spinlock protecting changes to iso_conn fields */ + spinlock_t lock; +@@ -106,7 +112,8 @@ static void iso_conn_free(struct kref *ref) + + if (conn->hcon) { + conn->hcon->iso_data = NULL; +- hci_conn_drop(conn->hcon); ++ if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags)) ++ hci_conn_drop(conn->hcon); + } + + /* Ensure no more work items will run since hci_conn has been dropped */ +@@ -305,6 +312,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, + + iso_pi(sk)->conn = conn; + conn->sk = sk; ++ clear_bit(ISO_CONN_DROPPED, conn->flags); + + if (parent) + bt_accept_enqueue(parent, sk, true); +@@ -834,11 +842,8 @@ static void iso_sock_disconn(struct sock *sk) + } + + sk->sk_state = BT_DISCONN; +- iso_conn_lock(iso_pi(sk)->conn); +- hci_conn_drop(iso_pi(sk)->conn->hcon); +- iso_pi(sk)->conn->hcon->iso_data = NULL; +- iso_pi(sk)->conn->hcon = NULL; +- iso_conn_unlock(iso_pi(sk)->conn); ++ if (!test_and_set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags)) ++ hci_conn_drop(iso_pi(sk)->conn->hcon); + } + + static void __iso_sock_close(struct sock *sk) +@@ -2041,9 +2046,18 @@ static void iso_sock_ready(struct sock *sk) + return; + + lock_sock(sk); ++ ++ switch (sk->sk_state) { ++ case BT_DISCONN: ++ case BT_CLOSED: ++ release_sock(sk); ++ return; ++ } ++ + iso_sock_clear_timer(sk); + sk->sk_state = BT_CONNECTED; + sk->sk_state_change(sk); ++ + release_sock(sk); + } + +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-iso-fix-leaking-sk-after-socket-release.patch b/queue-7.1/bluetooth-iso-fix-leaking-sk-after-socket-release.patch new file mode 100644 index 0000000000..11cd5f26b6 --- /dev/null +++ b/queue-7.1/bluetooth-iso-fix-leaking-sk-after-socket-release.patch @@ -0,0 +1,116 @@ +From 605ffa56c247cdfe98e6e225ea4ede1281d9a7f3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:30 +0300 +Subject: Bluetooth: ISO: fix leaking sk after socket release + +From: Pauli Virtanen + +[ Upstream commit ce57442a379212fe3fda59c9437ee8217eceb5b1 ] + +iso_sock_kill() tests !sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || +sock_flag(sk, SOCK_DEAD) for early return, but this is always true since +sock_orphan(sk) sets SOCK_DEAD, so the sk reference released by socket +always leaks, iso_sock_destruct is never called. + +The socket reference also leaks when __iso_sock_close() does not set +SOCK_ZAPPED, since iso_conn_del() does not call iso_sock_kill() after +zapping. + +Fix by replacing SOCK_DEAD by BT_SK_KILLED flag that is not used for +something else, and lock_sock to ensure iso_sock_kill() puts sk only +after socket release only once. Release and iso_conn_del may run +concurrently. Call iso_sock_kill() from iso_conn_del() to clean sk up +after zapping. + +Remove call to iso_sock_kill() from iso_sock_close(), as it's generally +no-op there. + +Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 22 ++++++++++++++++++---- + 1 file changed, 18 insertions(+), 4 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 9d1ac27202664..1ca3576357006 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -61,6 +61,7 @@ static void iso_sock_kill(struct sock *sk); + enum { + BT_SK_BIG_SYNC, + BT_SK_PA_SYNC, ++ BT_SK_KILLED, + }; + + struct iso_pinfo { +@@ -294,6 +295,7 @@ static void iso_conn_del(struct hci_conn *hcon, int err) + iso_sock_clear_timer(sk); + iso_chan_del(sk, err); + release_sock(sk); ++ iso_sock_kill(sk); + sock_put(sk); + } + +@@ -797,24 +799,29 @@ static void iso_sock_cleanup_listen(struct sock *parent) + */ + static void iso_sock_kill(struct sock *sk) + { ++ lock_sock(sk); ++ + if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || +- sock_flag(sk, SOCK_DEAD)) ++ test_bit(BT_SK_KILLED, &iso_pi(sk)->flags)) { ++ release_sock(sk); + return; ++ } + + BT_DBG("sk %p state %d", sk, sk->sk_state); + + /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */ +- lock_sock(sk); + if (iso_pi(sk)->conn) { + iso_conn_lock(iso_pi(sk)->conn); + iso_pi(sk)->conn->sk = NULL; + iso_conn_unlock(iso_pi(sk)->conn); + } +- release_sock(sk); + + /* Kill poor orphan */ + bt_sock_unlink(&iso_sk_list, sk); + sock_set_flag(sk, SOCK_DEAD); ++ set_bit(BT_SK_KILLED, &iso_pi(sk)->flags); ++ ++ release_sock(sk); + sock_put(sk); + } + +@@ -891,7 +898,6 @@ static void iso_sock_close(struct sock *sk) + iso_sock_clear_timer(sk); + __iso_sock_close(sk); + release_sock(sk); +- iso_sock_kill(sk); + } + + static void iso_sock_init(struct sock *sk, struct sock *parent) +@@ -2039,8 +2045,16 @@ static int iso_sock_release(struct socket *sock) + release_sock(sk); + } + ++ /* Make sure sk is valid even if iso_conn_del() is concurrent */ ++ sock_hold(sk); ++ ++ lock_sock(sk); + sock_orphan(sk); ++ release_sock(sk); ++ + iso_sock_kill(sk); ++ ++ sock_put(sk); + return err; + } + +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-iso-fix-race-of-kfree-vs-kref_get_unless_z.patch b/queue-7.1/bluetooth-iso-fix-race-of-kfree-vs-kref_get_unless_z.patch new file mode 100644 index 0000000000..f7863d44de --- /dev/null +++ b/queue-7.1/bluetooth-iso-fix-race-of-kfree-vs-kref_get_unless_z.patch @@ -0,0 +1,232 @@ +From 4b971e237928d41cdb2aa7e4c4906703f7da43f7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:34 +0300 +Subject: Bluetooth: ISO: fix race of kfree vs kref_get_unless_zero + +From: Pauli Virtanen + +[ Upstream commit af24e338bf5dafb80f42baa9a0b9e9b57b1c5d9c ] + +hci_conn::iso_data is accessed and modified without lock or RCU. +This leads to a race + + [Task hdev->workqueue] [Task 2] + iso_recv iso_conn_put(conn) + conn = LOAD hcon->iso_data iso_conn_free(conn) + iso_conn_hold_unless_zero(conn) hcon->iso_data = NULL + kfree(conn) + kref_get_unless_zero(&conn->ref) /* UAF */ + +and also to races in iso_conn_add() vs. iso_conn_free(). + +Fix by adding spinlock hci_conn::proto_lock and using it to guard +hci_conn::iso_data. + +Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + include/net/bluetooth/hci_core.h | 4 +- + net/bluetooth/hci_conn.c | 2 + + net/bluetooth/iso.c | 64 ++++++++++++++++++++++++++------ + 3 files changed, 58 insertions(+), 12 deletions(-) + +diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h +index 02ba1cba6b236..2c15c9d8dbf74 100644 +--- a/include/net/bluetooth/hci_core.h ++++ b/include/net/bluetooth/hci_core.h +@@ -770,9 +770,11 @@ struct hci_conn { + struct dentry *debugfs; + + struct hci_dev *hdev; ++ ++ spinlock_t proto_lock; /* lock guarding protocol data */ + void *l2cap_data; + void *sco_data; +- void *iso_data; ++ void *iso_data __guarded_by(&proto_lock); + + struct list_head link_list; + struct hci_conn *parent; +diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c +index eba4a548bef52..924c7795e368a 100644 +--- a/net/bluetooth/hci_conn.c ++++ b/net/bluetooth/hci_conn.c +@@ -1126,6 +1126,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, + INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle); + INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout); + ++ spin_lock_init(&conn->proto_lock); ++ + atomic_set(&conn->refcnt, 0); + + hci_dev_hold(hdev); +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 3b22193a9ec32..f946ef5f37b37 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -108,9 +108,16 @@ static void iso_conn_free(struct kref *ref) + BT_DBG("conn %p", conn); + + if (conn->hcon) { +- conn->hcon->iso_data = NULL; +- if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags)) +- hci_conn_drop(conn->hcon); ++ spin_lock(&conn->hcon->proto_lock); ++ ++ /* Check we are not racing with iso_conn_add */ ++ if (conn->hcon->iso_data == conn) { ++ conn->hcon->iso_data = NULL; ++ if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags)) ++ hci_conn_drop(conn->hcon); ++ } ++ ++ spin_unlock(&conn->hcon->proto_lock); + } + + kfree_skb(conn->rx_skb); +@@ -125,7 +132,21 @@ static void iso_conn_put(struct iso_conn *conn) + + BT_DBG("conn %p refcnt %d", conn, kref_read(&conn->ref)); + ++ /* The following race vs. iso_conn_del() is possible: ++ * ++ * 1. conn->hcon != NULL here ++ * 2. kref_put puts the last reference ++ * 3. concurrent iso_conn_del() gets iso_conn_hold_unless_zero() -> NULL ++ * and returns immediately, so conn->hcon is not cleared ++ * 4. iso_conn_free() dereferences conn->hcon ++ * ++ * To avoid UAF in step 4, take RCU before decrementing the refcount. ++ */ ++ rcu_read_lock(); ++ + kref_put(&conn->ref, iso_conn_free); ++ ++ rcu_read_unlock(); + } + + static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn) +@@ -204,22 +225,28 @@ static void iso_sock_disable_timer(struct sock *sk) + + /* ---- ISO connections ---- */ + static struct iso_conn *iso_conn_add(struct hci_conn *hcon) ++ __must_hold(&hcon->hdev->lock) + { +- struct iso_conn *conn = hcon->iso_data; ++ struct iso_conn *conn; ++ ++ spin_lock(&hcon->proto_lock); + +- conn = iso_conn_hold_unless_zero(conn); ++ conn = iso_conn_hold_unless_zero(hcon->iso_data); + if (conn) { + if (!conn->hcon) { + iso_conn_lock(conn); + conn->hcon = hcon; + iso_conn_unlock(conn); + } ++ spin_unlock(&hcon->proto_lock); + return conn; + } + +- conn = kzalloc_obj(*conn); +- if (!conn) ++ conn = kzalloc_obj(*conn, GFP_ATOMIC); ++ if (!conn) { ++ spin_unlock(&hcon->proto_lock); + return NULL; ++ } + + kref_init(&conn->ref); + spin_lock_init(&conn->lock); +@@ -228,6 +255,8 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon) + conn->hcon = hcon; + conn->tx_sn = 0; + ++ spin_unlock(&hcon->proto_lock); ++ + BT_DBG("hcon %p conn %p", hcon, conn); + + return conn; +@@ -268,10 +297,12 @@ static void iso_chan_del(struct sock *sk, int err) + static void iso_conn_del(struct hci_conn *hcon, int err) + __must_hold(&hcon->hdev->lock) + { +- struct iso_conn *conn = hcon->iso_data; ++ struct iso_conn *conn; + struct sock *sk; + +- conn = iso_conn_hold_unless_zero(conn); ++ spin_lock(&hcon->proto_lock); ++ conn = iso_conn_hold_unless_zero(hcon->iso_data); ++ spin_unlock(&hcon->proto_lock); + if (!conn) + return; + +@@ -295,10 +326,12 @@ static void iso_conn_del(struct hci_conn *hcon, int err) + + done: + /* No sk access to conn->hcon any more (lock_sock + hdev->lock) */ ++ spin_lock(&hcon->proto_lock); + iso_conn_lock(conn); + conn->hcon = NULL; + hcon->iso_data = NULL; + iso_conn_unlock(conn); ++ spin_unlock(&hcon->proto_lock); + + iso_conn_put(conn); + } +@@ -420,6 +453,8 @@ static int iso_connect_bis(struct sock *sk) + iso_pi(sk)->bc_sid = hcon->sid; + } + ++ lockdep_assert_held(&hcon->hdev->lock); ++ + conn = iso_conn_add(hcon); + if (!conn) { + hci_conn_drop(hcon); +@@ -523,6 +558,8 @@ static int iso_connect_cis(struct sock *sk) + } + } + ++ lockdep_assert_held(&hcon->hdev->lock); ++ + conn = iso_conn_add(hcon); + if (!conn) { + hci_conn_drop(hcon); +@@ -854,8 +891,8 @@ static void iso_sock_disconn(struct sock *sk) + */ + if (bis_sk) { + hcon->state = BT_OPEN; +- hcon->iso_data = NULL; +- iso_pi(sk)->conn->hcon = NULL; ++ set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags); ++ + iso_sock_clear_timer(sk); + iso_chan_del(sk, bt_to_errno(hcon->abort_reason)); + sock_put(bis_sk); +@@ -1305,6 +1342,8 @@ static int iso_listen_bis(struct sock *sk) + goto unlock; + } + ++ lockdep_assert_held(&hcon->hdev->lock); ++ + conn = iso_conn_add(hcon); + if (!conn) { + hci_conn_drop(hcon); +@@ -2590,7 +2629,10 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) + return -ENOENT; + } + ++ spin_lock(&hcon->proto_lock); + conn = iso_conn_hold_unless_zero(hcon->iso_data); ++ spin_unlock(&hcon->proto_lock); ++ + hcon = NULL; + + hci_dev_unlock(hdev); +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-iso-fix-refcounting-of-iso_conn.patch b/queue-7.1/bluetooth-iso-fix-refcounting-of-iso_conn.patch new file mode 100644 index 0000000000..f16daf51d9 --- /dev/null +++ b/queue-7.1/bluetooth-iso-fix-refcounting-of-iso_conn.patch @@ -0,0 +1,142 @@ +From b8bb6172e3523e8c28da6c36ef420092e094c238 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:33 +0300 +Subject: Bluetooth: ISO: fix refcounting of iso_conn +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Pauli Virtanen + +[ Upstream commit fdfde532ab1caa165fcd8985001157ac8b4db365 ] + +iso_conn_del() and iso_chan_del() have a race that results to double-put +of iso_conn: + + [Task hdev->workqueue] [Task 2] + iso_conn_del iso_chan_del + iso_conn_hold_unless_zero iso_conn_lock + iso_conn_lock conn->sk = NULL + iso_conn_unlock + sk = iso_sock_hold(conn) <---------´ + if (!sk) iso_conn_put iso_conn_put + iso_conn_put /* UAF */ + +The extra put for !sk in iso_conn_del() is currently required since +failing iso_chan_add() may leave iso_conn not associated with any sk. + +Fix by having iso_pi(sk)->conn own refcount when non-NULL, so +iso_conn_del does not need to put it. Adjust the iso_conn_add() +refcounting so that conn is put if it does not get associated with an +sk. + +Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 29 +++++++++++++++++------------ + 1 file changed, 17 insertions(+), 12 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 31ceb1338dc23..3b22193a9ec32 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -107,9 +107,6 @@ static void iso_conn_free(struct kref *ref) + + BT_DBG("conn %p", conn); + +- if (conn->sk) +- iso_pi(conn->sk)->conn = NULL; +- + if (conn->hcon) { + conn->hcon->iso_data = NULL; + if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags)) +@@ -144,6 +141,14 @@ static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn) + return conn; + } + ++static struct iso_conn *iso_conn_hold(struct iso_conn *conn) ++{ ++ BT_DBG("conn %p refcnt %u", conn, kref_read(&conn->ref)); ++ ++ kref_get(&conn->ref); ++ return conn; ++} ++ + static struct sock *iso_sock_hold(struct iso_conn *conn) + { + if (!conn || !bt_sock_linked(&iso_sk_list, conn->sk)) +@@ -209,7 +214,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon) + conn->hcon = hcon; + iso_conn_unlock(conn); + } +- iso_conn_put(conn); + return conn; + } + +@@ -278,10 +282,8 @@ static void iso_conn_del(struct hci_conn *hcon, int err) + sk = iso_sock_hold(conn); + iso_conn_unlock(conn); + +- if (!sk) { +- iso_conn_put(conn); ++ if (!sk) + goto done; +- } + + iso_sock_disable_timer(sk); + +@@ -319,7 +321,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, + return -EIO; + } + +- iso_pi(sk)->conn = conn; ++ iso_pi(sk)->conn = iso_conn_hold(conn); + conn->sk = sk; + clear_bit(ISO_CONN_DROPPED, conn->flags); + +@@ -426,6 +428,7 @@ static int iso_connect_bis(struct sock *sk) + } + + err = iso_chan_add(conn, sk, NULL); ++ iso_conn_put(conn); + if (err) + goto unlock; + +@@ -528,6 +531,7 @@ static int iso_connect_cis(struct sock *sk) + } + + err = iso_chan_add(conn, sk, NULL); ++ iso_conn_put(conn); + if (err) + goto unlock; + +@@ -1309,10 +1313,9 @@ static int iso_listen_bis(struct sock *sk) + } + + err = iso_chan_add(conn, sk, NULL); +- if (err) { +- hci_conn_drop(hcon); ++ iso_conn_put(conn); ++ if (err) + goto unlock; +- } + + unlock: + release_sock(sk); +@@ -2550,8 +2553,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) + struct iso_conn *conn; + + conn = iso_conn_add(hcon); +- if (conn) ++ if (conn) { + iso_conn_ready(conn); ++ iso_conn_put(conn); ++ } + } else { + iso_conn_del(hcon, bt_to_errno(status)); + } +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch b/queue-7.1/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch new file mode 100644 index 0000000000..b9f7e59c52 --- /dev/null +++ b/queue-7.1/bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch @@ -0,0 +1,38 @@ +From 4e44b3f29c80b7d07c36501d14536e1e989e264d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:27 +0300 +Subject: Bluetooth: ISO: fix timeout vs sync_timeout typo in check_bcast_qos + +From: Pauli Virtanen + +[ Upstream commit e9cb51813d79fc9aae4a2098aab3ab6ebd7fb6c8 ] + +In iso.c check_bcast_qos(), missing bcast.timeout is not set to its +default value, and appears typoed as bcast.sync_timeout. + +Fix the typo. + +Fixes: b37cab587aa3 ("Bluetooth: ISO: Don't reject BT_ISO_QOS if parameters are unset") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 6fadaeb1f7e96..7e8f8a51eebd2 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -1795,7 +1795,7 @@ static bool check_bcast_qos(struct bt_iso_qos *qos) + return false; + + if (!qos->bcast.timeout) +- qos->bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT; ++ qos->bcast.timeout = BT_ISO_SYNC_TIMEOUT; + + if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000) + return false; +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch b/queue-7.1/bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch new file mode 100644 index 0000000000..9f3bc0f385 --- /dev/null +++ b/queue-7.1/bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch @@ -0,0 +1,133 @@ +From e4294eeffd2afea156c18cb817c6511e32a6b11a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:29 +0300 +Subject: Bluetooth: ISO: hold sk properly in iso_conn_ready + +From: Pauli Virtanen + +[ Upstream commit 0d255e63fcf3f13a570d7ac11678fa1164ac015c ] + +sk deref in iso_conn_ready must be done either under conn->lock, or +holding a refcount, to avoid concurrent close. conn->sk is currently +accessed without either: + + [Task 1] [Task 2] + iso_sock_release + iso_conn_ready + sk = conn->sk + lock_sock(sk) + conn->sk = NULL + lock_sock(sk) + release_sock(sk) + iso_sock_kill(sk) + UAF on sk deref + +Fix possible UAF by holding sk refcount in iso_conn_ready(). Also +recheck after lock_sock that the socket is still valid. Adjust locking +so conn->sk is cleared only under lock_sock. + +Fixes: 27c24fda62b60 ("Bluetooth: switch to lock_sock in SCO") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 34 +++++++++++++++++++++++----------- + 1 file changed, 23 insertions(+), 11 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 06b6fec364485..9d1ac27202664 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -804,11 +804,13 @@ static void iso_sock_kill(struct sock *sk) + BT_DBG("sk %p state %d", sk, sk->sk_state); + + /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */ ++ lock_sock(sk); + if (iso_pi(sk)->conn) { + iso_conn_lock(iso_pi(sk)->conn); + iso_pi(sk)->conn->sk = NULL; + iso_conn_unlock(iso_pi(sk)->conn); + } ++ release_sock(sk); + + /* Kill poor orphan */ + bt_sock_unlink(&iso_sk_list, sk); +@@ -2046,23 +2048,17 @@ static void iso_sock_ready(struct sock *sk) + { + BT_DBG("sk %p", sk); + +- if (!sk) +- return; +- +- lock_sock(sk); ++ lockdep_assert(lockdep_sock_is_held(sk)); + + switch (sk->sk_state) { + case BT_DISCONN: + case BT_CLOSED: +- release_sock(sk); + return; + } + + iso_sock_clear_timer(sk); + sk->sk_state = BT_CONNECTED; + sk->sk_state_change(sk); +- +- release_sock(sk); + } + + static bool iso_match_big(struct sock *sk, void *data) +@@ -2092,7 +2088,7 @@ static bool iso_match_dst(struct sock *sk, void *data) + static void iso_conn_ready(struct iso_conn *conn) + { + struct sock *parent = NULL; +- struct sock *sk = conn->sk; ++ struct sock *sk; + struct hci_ev_le_big_sync_established *ev = NULL; + struct hci_ev_le_pa_sync_established *ev2 = NULL; + struct hci_ev_le_per_adv_report *ev3 = NULL; +@@ -2101,7 +2097,22 @@ static void iso_conn_ready(struct iso_conn *conn) + + BT_DBG("conn %p", conn); + ++ iso_conn_lock(conn); ++ sk = iso_sock_hold(conn); ++ iso_conn_unlock(conn); ++ + if (sk) { ++ lock_sock(sk); ++ ++ /* conn->sk may have become NULL if racing with sk close, but ++ * due to held hdev->lock, it can't become different sk. ++ */ ++ if (!conn->sk) { ++ release_sock(sk); ++ sock_put(sk); ++ return; ++ } ++ + /* Attempt to update source address in case of BIS Sender if + * the advertisement is using a random address. + */ +@@ -2114,14 +2125,15 @@ static void iso_conn_ready(struct iso_conn *conn) + adv = hci_find_adv_instance(bis->hdev, + bis->iso_qos.bcast.bis); + if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) { +- lock_sock(sk); + iso_pi(sk)->src_type = BDADDR_LE_RANDOM; + bacpy(&iso_pi(sk)->src, &adv->random_addr); +- release_sock(sk); + } + } + +- iso_sock_ready(conn->sk); ++ iso_sock_ready(sk); ++ ++ release_sock(sk); ++ sock_put(sk); + } else { + hcon = conn->hcon; + if (!hcon) +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-iso-lock-sk-in-iso_connect_ind.patch b/queue-7.1/bluetooth-iso-lock-sk-in-iso_connect_ind.patch new file mode 100644 index 0000000000..32c5ae6936 --- /dev/null +++ b/queue-7.1/bluetooth-iso-lock-sk-in-iso_connect_ind.patch @@ -0,0 +1,92 @@ +From 39a81e7836829a3f79218884ff9461a7aa6158d2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:26 +0300 +Subject: Bluetooth: ISO: lock sk in iso_connect_ind + +From: Pauli Virtanen + +[ Upstream commit 4311fd6f429065a8ba208660360a895627a00cf3 ] + +Accessing iso_pi(sk)->conn requires lock_sock, which is not taken in the +"ev3" part of iso_connect_ind. It may also be NULL if socket has +transitioned away from the LISTEN/CONNECT states before locking. + +Fix by adding lock/release. Recheck hcon is valid after lock acquire +where needed. + +Fixes: 168d9bf9c7f0 ("Bluetooth: ISO: Reassemble PA data for bcast sink") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 19 +++++++++++-------- + 1 file changed, 11 insertions(+), 8 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 476c679791234..6fadaeb1f7e96 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -2368,7 +2368,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + + lock_sock(sk); + +- hcon = iso_pi(sk)->conn->hcon; ++ hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL; + iso_pi(sk)->qos.bcast.encryption = ev2->encryption; + + if (ev2->num_bis < iso_pi(sk)->bc_num_bis) +@@ -2408,9 +2408,11 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + if (!sk) + goto done; + +- hcon = iso_pi(sk)->conn->hcon; ++ lock_sock(sk); ++ ++ hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL; + if (!hcon) +- goto done; ++ goto release3; + + if (ev3->data_status == LE_PA_DATA_TRUNCATED) { + /* The controller was unable to retrieve PA data. */ +@@ -2418,12 +2420,12 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + HCI_MAX_PER_AD_TOT_LEN); + hcon->le_per_adv_data_len = 0; + hcon->le_per_adv_data_offset = 0; +- goto done; ++ goto release3; + } + + if (hcon->le_per_adv_data_offset + ev3->length > + HCI_MAX_PER_AD_TOT_LEN) +- goto done; ++ goto release3; + + memcpy(hcon->le_per_adv_data + hcon->le_per_adv_data_offset, + ev3->data, ev3->length); +@@ -2442,18 +2444,19 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) + &base_len); + + if (!base || base_len > BASE_MAX_LENGTH) +- goto done; ++ goto release3; + +- lock_sock(sk); + memcpy(iso_pi(sk)->base, base, base_len); + iso_pi(sk)->base_len = base_len; +- release_sock(sk); + } else { + /* This is a PA data fragment. Keep pa_data_len set to 0 + * until all data has been reassembled. + */ + hcon->le_per_adv_data_len = 0; + } ++ ++release3: ++ release_sock(sk); + } else { + sk = iso_get_sock(hdev, &hdev->bdaddr, BDADDR_ANY, + BT_LISTEN, iso_match_dst, BDADDR_ANY); +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-iso-lock-sk-in-iso_sock_getname.patch b/queue-7.1/bluetooth-iso-lock-sk-in-iso_sock_getname.patch new file mode 100644 index 0000000000..9b0a3e398c --- /dev/null +++ b/queue-7.1/bluetooth-iso-lock-sk-in-iso_sock_getname.patch @@ -0,0 +1,46 @@ +From c8e998290bdf0f8f2baa32c4daa67d90a6d0710f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:25 +0300 +Subject: Bluetooth: ISO: lock sk in iso_sock_getname + +From: Pauli Virtanen + +[ Upstream commit 89cf154d7c18e6e94a3da83051f3cf2bac317ae2 ] + +Accessing iso_pi(sk)->conn requires lock_sock, which is not held here. + +Fix by adding the lock/release. + +Fixes: 2df108c227b2 ("Bluetooth: ISO: Fix using BT_SK_PA_SYNC to detect BIS sockets") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index f1399ddd6ad05..476c679791234 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -1471,6 +1471,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr, + + BT_DBG("sock %p, sk %p", sock, sk); + ++ lock_sock(sk); ++ + addr->sa_family = AF_BLUETOOTH; + + if (peer) { +@@ -1492,6 +1494,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr, + sa->iso_bdaddr_type = iso_pi(sk)->src_type; + } + ++ release_sock(sk); ++ + return len; + } + +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch b/queue-7.1/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch new file mode 100644 index 0000000000..c4c8ecbb08 --- /dev/null +++ b/queue-7.1/bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch @@ -0,0 +1,49 @@ +From 5f8c62de1c694e3969e15fec3e02462ad770bc4b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:20:28 +0300 +Subject: Bluetooth: ISO: validate sockaddr_iso first in iso_sock_rebind_bis() + +From: Pauli Virtanen + +[ Upstream commit 4e20192d46a685d73e590a60a4a2419a0a8afcbf ] + +iso_sock_rebind_bis() updates socket iso_pi(sk)->bc_num_bis before +validating the BIS values, so it's possible to end up with bc_num_bis +inconsistent. + +Assign to iso_pi(sk)->bc_num_bis only after validation. + +Fixes: 80837140c1f2 ("Bluetooth: ISO: Allow binding a PA sync socket") +Signed-off-by: Pauli Virtanen +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/iso.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c +index 7e8f8a51eebd2..06b6fec364485 100644 +--- a/net/bluetooth/iso.c ++++ b/net/bluetooth/iso.c +@@ -1038,15 +1038,15 @@ static int iso_sock_rebind_bis(struct sock *sk, struct sockaddr_iso *sa, + goto done; + } + +- iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis; +- +- for (int i = 0; i < iso_pi(sk)->bc_num_bis; i++) ++ for (int i = 0; i < sa->iso_bc->bc_num_bis; i++) + if (sa->iso_bc->bc_bis[i] < 0x01 || + sa->iso_bc->bc_bis[i] > 0x1f) { + err = -EINVAL; + goto done; + } + ++ iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis; ++ + memcpy(iso_pi(sk)->bc_bis, sa->iso_bc->bc_bis, + iso_pi(sk)->bc_num_bis); + +-- +2.53.0 + diff --git a/queue-7.1/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch b/queue-7.1/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch new file mode 100644 index 0000000000..0d4960b409 --- /dev/null +++ b/queue-7.1/bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch @@ -0,0 +1,60 @@ +From 78f19c8e1d362e9e1d59241fc88312272412db5f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 14:48:45 +0800 +Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp + +From: Jiale Yao + +[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ] + +l2cap_le_connect_rsp() obtains a channel via +__l2cap_get_chan_by_ident() but neither holds a reference nor uses +l2cap_chan_hold_unless_zero() before locking and operating on it. +A concurrent l2cap_chan_del() triggered by a remote disconnect can +free the channel between the lookup and l2cap_chan_lock(), causing +a use-after-free. + +The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler +l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero() +to safely hold a reference, but l2cap_le_connect_rsp() was left +unprotected. + +Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup +and l2cap_chan_put() on the exit path, consistent with other L2CAP +response handlers. + +Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request") +Assisted-by: Claude:deepseek-v4-pro +Signed-off-by: Jiale Yao +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +--- + net/bluetooth/l2cap_core.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index 189085000c73b..6585f08fe4a14 100644 +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -4823,6 +4823,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + if (!chan) + return -EBADSLT; + ++ chan = l2cap_chan_hold_unless_zero(chan); ++ if (!chan) ++ return -EBADSLT; ++ + err = 0; + + l2cap_chan_lock(chan); +@@ -4868,6 +4872,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, + } + + l2cap_chan_unlock(chan); ++ l2cap_chan_put(chan); + + return err; + } +-- +2.53.0 + diff --git a/queue-7.1/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch b/queue-7.1/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch new file mode 100644 index 0000000000..602f237d8d --- /dev/null +++ b/queue-7.1/btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch @@ -0,0 +1,79 @@ +From ad790c7713dc3bb4767b9da9a3146690a78347c3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 4 Jul 2026 17:58:56 +0930 +Subject: btrfs: fix leaking BTRFS_FS_STATE_REMOUNTING flag + +From: Qu Wenruo + +[ Upstream commit 6881f45d0eb541f2cee8c37c84b3860a23823bb3 ] + +[BUG] +The following script can lead to unexpected qgroup rescan failure: + + # mkfs.btrfs -f -O quota $dev + # mount $dev $mnt + # mount -o remount,rescue=ibadroots $mnt + ^^^^^ This above command is expected to fail + + # btrfs quota rescan -w $mnt + ^^^^^ The above qgroup rescan is not expected to fail + + # btrfs qgroup show $mnt + WARNING: qgroup data inconsistent, rescan recommended + Qgroupid Referenced Exclusive Path + -------- ---------- --------- ---- + 0/5 16.00KiB 16.00KiB + +The above short script will be converted to a proper fstests case. + +[CAUSE] +Inside btrfs_reconfigure(), if either btrfs_check_options() or +btrfs_check_features() failed, we will always have +BTRFS_FS_STATE_REMOUNTING set for the fs until the next successful +remount. + +That BTRFS_FS_STATE_REMOUNTING flag will interrupt several operations, +including: + +- Qgroup rescan +- Auto defrag +- Space reclaim + +[FIX] +Change the error handling of btrfs_check_options() and +btrfs_check_features() to goto restore label. + +Fixes: eddb1a433f26 ("btrfs: add reconfigure callback for fs_context") +Reviewed-by: Johannes Thumshirn +Signed-off-by: Qu Wenruo +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/super.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c +index ba70d727622e8..ba0b31e5816b6 100644 +--- a/fs/btrfs/super.c ++++ b/fs/btrfs/super.c +@@ -1518,12 +1518,14 @@ static int btrfs_reconfigure(struct fs_context *fc) + sync_filesystem(sb); + set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state); + +- if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) +- return -EINVAL; ++ if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) { ++ ret = -EINVAL; ++ goto restore; ++ } + + ret = btrfs_check_features(fs_info, !(fc->sb_flags & SB_RDONLY)); + if (ret < 0) +- return ret; ++ goto restore; + + btrfs_ctx_to_info(fs_info, ctx); + btrfs_remount_begin(fs_info, old_ctx.mount_opt, fc->sb_flags); +-- +2.53.0 + diff --git a/queue-7.1/btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch b/queue-7.1/btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch new file mode 100644 index 0000000000..e6e09bc176 --- /dev/null +++ b/queue-7.1/btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch @@ -0,0 +1,58 @@ +From 95c2f099645aa86ffcf3df794dc209a38496a39e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 13:12:51 +0930 +Subject: btrfs: raid56: fix an incorrect csum skip during scrub + +From: Qu Wenruo + +[ Upstream commit 330dcc553f282e8dc0b88c9495b4c296465364e1 ] + +Commit 7425a2894019 ("btrfs: introduce btrfs_bio_for_each_block_all() +helper") uses the new helper to replace the nested loop inside +verify_bio_data_sectors(), which simplifies the code. + +However that also changed the behavior of "continue" when a block has no +data checksum. + +Previously the "continue" would skip the old for() loop, which would also +increase @total_sector_nr. + +Now the "continue" will skip the new btrfs_bio_for_each_block_all() +loop, which doesn't update @total_sector_nr. + +This means if we hit a block that has no data checksum, we will skip all +the remaining blocks no matter if they have data checksum. +As @total_sector_nr will never be updated, and that test_bit() will +always return false. + +Fix it by increasing @total_sector_nr before calling "continue". + +Fixes: 7425a2894019 ("btrfs: introduce btrfs_bio_for_each_block_all() helper") +Reviewed-by: Daniel Vacek +Signed-off-by: Qu Wenruo +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/raid56.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c +index 08ee8f316d96d..515ffa4b02114 100644 +--- a/fs/btrfs/raid56.c ++++ b/fs/btrfs/raid56.c +@@ -1679,8 +1679,10 @@ static void verify_bio_data_sectors(struct btrfs_raid_bio *rbio, + continue; + + /* No csum for this sector, skip to the next sector. */ +- if (!test_bit(total_sector_nr, rbio->csum_bitmap)) ++ if (!test_bit(total_sector_nr, rbio->csum_bitmap)) { ++ total_sector_nr++; + continue; ++ } + + expected_csum = rbio->csum_buf + total_sector_nr * fs_info->csum_size; + btrfs_calculate_block_csum_pages(fs_info, paddrs, csum_buf); +-- +2.53.0 + diff --git a/queue-7.1/btrfs-skip-global-block-reserve-accounting-for-rescu.patch b/queue-7.1/btrfs-skip-global-block-reserve-accounting-for-rescu.patch new file mode 100644 index 0000000000..e460487ac1 --- /dev/null +++ b/queue-7.1/btrfs-skip-global-block-reserve-accounting-for-rescu.patch @@ -0,0 +1,137 @@ +From cf46fbbbf8d6b556d621d4e55a188ddce22677bb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 16:50:08 +0800 +Subject: btrfs: skip global block reserve accounting for rescue mounts + +From: Dongjiang Zhu + +[ Upstream commit 51a0e8399858621442807a26057bcd1cd3ced046 ] + +[BUG] +Mounting with rescue=ibadroots after corrupting the block group tree +root triggers a NULL pointer dereference: + + BUG: kernel NULL pointer dereference, address: 0000000000000100 + RIP: 0010:btrfs_update_global_block_rsv+0x9d/0x1c0 [btrfs] + Call Trace: + fill_dummy_bgs+0xd4/0x120 [btrfs] + open_ctree+0xc6e/0x1ca0 [btrfs] + btrfs_get_tree+0x50d/0xa40 [btrfs] + +The same crash occurs with a corrupted raid stripe tree root, via +btrfs_read_block_groups() instead of fill_dummy_bgs(). + +[CAUSE] +With rescue=ibadroots, btrfs_read_roots() allows the mount to continue +when either root cannot be read, leaving the corresponding root pointer +NULL while its on-disk feature bit remains set. + +btrfs_update_global_block_rsv() then dereferences the missing root based +on the feature bit alone. + +[FIX] +Rescue mounts are fully read-only and cannot start transactions, so the +global reserve is never consumed. Under btrfs_is_full_ro(), mark the +reserve as full and return before performing the accounting. + +And since we need to check if the fs is mount fully RO, export +fs_is_full_ro() as btrfs_is_full_ro(), and move it to fs.h. + +Fixes: 8dbfc14fc736 ("btrfs: account block group tree when calculating global reserve size") +Fixes: 515020900d44 ("btrfs: read raid stripe tree from disk") +Suggested-by: Qu Wenruo +Signed-off-by: Dongjiang Zhu +[ Squash the fs_is_full_ro() export commit into this one. ] +Reviewed-by: Qu Wenruo +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/block-rsv.c | 19 +++++++++++++++++-- + fs/btrfs/disk-io.c | 11 +---------- + fs/btrfs/fs.h | 9 +++++++++ + 3 files changed, 27 insertions(+), 12 deletions(-) + +diff --git a/fs/btrfs/block-rsv.c b/fs/btrfs/block-rsv.c +index 9efb3016ef116..c68a8f4b7d19c 100644 +--- a/fs/btrfs/block-rsv.c ++++ b/fs/btrfs/block-rsv.c +@@ -322,10 +322,25 @@ void btrfs_block_rsv_add_bytes(struct btrfs_block_rsv *block_rsv, + void btrfs_update_global_block_rsv(struct btrfs_fs_info *fs_info) + { + struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv; +- struct btrfs_space_info *sinfo = block_rsv->space_info; ++ struct btrfs_space_info *sinfo; + struct btrfs_root *root, *tmp; +- u64 num_bytes = btrfs_root_used(&fs_info->tree_root->root_item); + unsigned int min_items = 1; ++ u64 num_bytes; ++ ++ /* ++ * A full read-only mount (rescue options) cannot start transactions, ++ * so the global reserve is never consumed. Mark it as full and skip ++ * the accounting. ++ */ ++ if (btrfs_is_full_ro(fs_info)) { ++ spin_lock(&block_rsv->lock); ++ block_rsv->full = true; ++ spin_unlock(&block_rsv->lock); ++ return; ++ } ++ ++ sinfo = block_rsv->space_info; ++ num_bytes = btrfs_root_used(&fs_info->tree_root->root_item); + + /* + * The global block rsv is based on the size of the extent tree, the +diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c +index 833965b06f90e..7ca64f79451cb 100644 +--- a/fs/btrfs/disk-io.c ++++ b/fs/btrfs/disk-io.c +@@ -3267,15 +3267,6 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount) + return 0; + } + +-static bool fs_is_full_ro(const struct btrfs_fs_info *fs_info) +-{ +- if (!sb_rdonly(fs_info->sb)) +- return false; +- if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK)) +- return true; +- return false; +-} +- + /* + * Try to wait for any metadata readahead, and invalidate all btree folios. + * +@@ -3432,7 +3423,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device + WRITE_ONCE(fs_info->fs_error, -EUCLEAN); + + /* If the fs has any rescue options, no transaction is allowed. */ +- if (fs_is_full_ro(fs_info)) ++ if (btrfs_is_full_ro(fs_info)) + WRITE_ONCE(fs_info->fs_error, -EROFS); + + /* Set up fs_info before parsing mount options */ +diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h +index a8aa086a4df86..0292246db614d 100644 +--- a/fs/btrfs/fs.h ++++ b/fs/btrfs/fs.h +@@ -1146,6 +1146,15 @@ void __btrfs_clear_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag, + #define btrfs_test_opt(fs_info, opt) ((fs_info)->mount_opt & \ + BTRFS_MOUNT_##opt) + ++static inline bool btrfs_is_full_ro(const struct btrfs_fs_info *fs_info) ++{ ++ if (!sb_rdonly(fs_info->sb)) ++ return false; ++ if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK)) ++ return true; ++ return false; ++} ++ + static inline bool btrfs_fs_closing(const struct btrfs_fs_info *fs_info) + { + return unlikely(test_bit(BTRFS_FS_CLOSING_START, &fs_info->flags)); +-- +2.53.0 + diff --git a/queue-7.1/btrfs-warn-about-extent-buffer-that-can-not-be-relea.patch b/queue-7.1/btrfs-warn-about-extent-buffer-that-can-not-be-relea.patch new file mode 100644 index 0000000000..5a6e894332 --- /dev/null +++ b/queue-7.1/btrfs-warn-about-extent-buffer-that-can-not-be-relea.patch @@ -0,0 +1,183 @@ +From 8d847284b77c6a7befc6ee27925fcb1fff7a2062 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 30 Apr 2026 10:37:23 +0930 +Subject: btrfs: warn about extent buffer that can not be released + +From: Qu Wenruo + +[ Upstream commit 83f7e52b7ed1c3e03b79123e20b6f6adf8d886bb ] + +When we unmount the fs or during mount failures, btrfs will call +invalidate_inode_pages() to release all btree inode folios. + +However that function can return -EBUSY if any folios can not be +invalidated. +This can be caused by: + +- Some extent buffers are still held by btrfs + This is a logic error, as we should release all tree root nodes + during unmount and mount failure handling. + +- Some extent buffers are under readahead and haven't yet finished + These are much rarer but valid cases. + In that case we should wait for those extent buffers. + +Introduce a new helper invalidate_and_check_btree_folios() which will: + +- Call invalidate_inode_pages2() and catch its return value + If it returned 0 as expected, that's great and we can call it a day. + +- Otherwise go through each extent buffer in buffer_tree + Increase the ref by one first for the eb we're checking. + This is to ensure the eb won't be freed after the readahead is + finished. + + For ebs that still have EXTENT_BUFFER_READING flag, wait for them to + finish first. + + After waiting for the readahead, check the refs of the eb and if it's + still dirty. + + If the eb ref count is greater than 2 (one for the buffer tree, one + held by us), it means we are still holding the extent buffer somewhere + else, which is a code bug. + + If the eb is still dirty, it means a bug in transaction handling, e.g. + the bug fixed by patch "btrfs: only release the dirty pages io tree + after successful writes". + + For either case, show a warning message about the eb, including its + bytenr, owner, refs and flags. + And if it's a debug build, also trigger WARN_ON_ONCE() so that fstests + can properly catch such situation. + +Link: https://bugzilla.kernel.org/show_bug.cgi?id=221270 +Reported-by: AHN SEOK-YOUNG +CC: Teng Liu <27rabbitlt@gmail.com> +Tested-by: Teng Liu <27rabbitlt@gmail.com> +Reviewed-by: Filipe Manana +Signed-off-by: Qu Wenruo +Signed-off-by: David Sterba +Stable-dep-of: 51a0e8399858 ("btrfs: skip global block reserve accounting for rescue mounts") +Signed-off-by: Sasha Levin +--- + fs/btrfs/disk-io.c | 53 ++++++++++++++++++++++++++++++++++++++++++-- + fs/btrfs/extent_io.c | 6 ----- + fs/btrfs/extent_io.h | 6 +++++ + 3 files changed, 57 insertions(+), 8 deletions(-) + +diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c +index ab92b35fa3cc1..833965b06f90e 100644 +--- a/fs/btrfs/disk-io.c ++++ b/fs/btrfs/disk-io.c +@@ -3276,6 +3276,55 @@ static bool fs_is_full_ro(const struct btrfs_fs_info *fs_info) + return false; + } + ++/* ++ * Try to wait for any metadata readahead, and invalidate all btree folios. ++ * ++ * If the invalidation failed, report any dirty/held extent buffers. ++ */ ++static void invalidate_and_check_btree_folios(struct btrfs_fs_info *fs_info) ++{ ++ unsigned long index = 0; ++ struct extent_buffer *eb; ++ int ret; ++ ++ ret = invalidate_inode_pages2(fs_info->btree_inode->i_mapping); ++ if (likely(ret == 0)) ++ return; ++ ++ /* ++ * Some btree pages can not be invalidated, this happens when some tree ++ * blocks are still held (either by readahead or some task is holding a ref). ++ */ ++ rcu_read_lock(); ++ xa_for_each(&fs_info->buffer_tree, index, eb) { ++ /* Increase the ref so that the eb won't disappear. */ ++ if (!refcount_inc_not_zero(&eb->refs)) ++ continue; ++ rcu_read_unlock(); ++ ++ /* Wait for any readahead first. */ ++ if (test_bit(EXTENT_BUFFER_READING, &eb->bflags)) ++ wait_on_bit_io(&eb->bflags, EXTENT_BUFFER_READING, ++ TASK_UNINTERRUPTIBLE); ++ /* ++ * The refs threshold is 2, one held by us at the beginning ++ * of the loop, one for the ownership in the buffer tree. ++ */ ++ if (unlikely(refcount_read(&eb->refs) > 2 || extent_buffer_under_io(eb))) { ++ WARN_ON_ONCE(IS_ENABLED(CONFIG_BTRFS_DEBUG)); ++ btrfs_warn(fs_info, ++ "unable to release extent buffer %llu owner %llu gen %llu refs %u flags 0x%lx", ++ eb->start, btrfs_header_owner(eb), ++ btrfs_header_generation(eb), ++ refcount_read(&eb->refs), eb->bflags); ++ } ++ free_extent_buffer(eb); ++ rcu_read_lock(); ++ } ++ rcu_read_unlock(); ++ invalidate_inode_pages2(fs_info->btree_inode->i_mapping); ++} ++ + int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_devices) + { + u32 sectorsize; +@@ -3706,7 +3755,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device + if (fs_info->data_reloc_root) + btrfs_drop_and_free_fs_root(fs_info, fs_info->data_reloc_root); + free_root_pointers(fs_info, true); +- invalidate_inode_pages2(fs_info->btree_inode->i_mapping); ++ invalidate_and_check_btree_folios(fs_info); + + fail_sb_buffer: + btrfs_stop_all_workers(fs_info); +@@ -4445,7 +4494,7 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) + * We must make sure there is not any read request to + * submit after we stop all workers. + */ +- invalidate_inode_pages2(fs_info->btree_inode->i_mapping); ++ invalidate_and_check_btree_folios(fs_info); + btrfs_stop_all_workers(fs_info); + + /* +diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c +index f0bfa8a6218a7..741fd1d4ab2ea 100644 +--- a/fs/btrfs/extent_io.c ++++ b/fs/btrfs/extent_io.c +@@ -2877,12 +2877,6 @@ bool try_release_extent_mapping(struct folio *folio, gfp_t mask) + return try_release_extent_state(io_tree, folio); + } + +-static int extent_buffer_under_io(const struct extent_buffer *eb) +-{ +- return (test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags) || +- test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)); +-} +- + static bool folio_range_has_eb(struct folio *folio) + { + struct btrfs_folio_state *bfs; +diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h +index b310a5145cf69..7b4152387d886 100644 +--- a/fs/btrfs/extent_io.h ++++ b/fs/btrfs/extent_io.h +@@ -327,6 +327,12 @@ static inline bool extent_buffer_uptodate(const struct extent_buffer *eb) + return test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags); + } + ++static inline bool extent_buffer_under_io(const struct extent_buffer *eb) ++{ ++ return (test_bit(EXTENT_BUFFER_WRITEBACK, &eb->bflags) || ++ test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)); ++} ++ + int memcmp_extent_buffer(const struct extent_buffer *eb, const void *ptrv, + unsigned long start, unsigned long len); + void read_extent_buffer(const struct extent_buffer *eb, void *dst, +-- +2.53.0 + diff --git a/queue-7.1/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch b/queue-7.1/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch new file mode 100644 index 0000000000..483abdec0a --- /dev/null +++ b/queue-7.1/btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch @@ -0,0 +1,77 @@ +From b8aa38554846e42a97d81e559ff05990ba129129 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 3 Jul 2026 07:54:40 +0200 +Subject: btrfs: zoned: fix deadlock between metadata writeback and transaction + commit + +From: Johannes Thumshirn + +[ Upstream commit 1ebe51c29fa9755d5b2fea28727c051117907cf8 ] + +When writing out metadata extent buffers in a zoned filesystem, +btree_writepages() holds fs_info->zoned_meta_io_lock across the whole +writeback loop, including the call to btrfs_check_meta_write_pointer() -> +check_bg_is_active(). + +For the tree-log block group, check_bg_is_active() may fail to activate +the zone and fall back to btrfs_zone_finish_one_bg() to free an active +zone. That path waits for the running transaction to commit while still +holding zoned_meta_io_lock, but the committer needs that same lock to +write out the tree extents, so the two tasks deadlock: + + Task A (kworker, metadata writeback) Task B (fsstress, transaction commit) + ------------------------------------ ------------------------------------- + wb_workfn() btrfs_commit_transaction(T) + btree_writepages() btrfs_write_and_wait_transaction() + btrfs_zoned_meta_io_lock() btrfs_write_marked_extents() + btrfs_check_meta_write_pointer() btree_writepages() + check_bg_is_active() [treelog_bg] btrfs_zoned_meta_io_lock() + btrfs_zone_finish_one_bg() + do_zone_finish() + btrfs_inc_block_group_ro() + btrfs_wait_for_commit() + + +The sibling branch in check_bg_is_active() already drops zoned_meta_io_lock +around do_zone_finish() for this exact reason. Do the same in the tree-log +branch: release the lock around btrfs_zone_finish_one_bg() and re-acquire +it afterwards. The lock only protects fs_info->active_{meta,system}_bg, +which this branch does not touch, and ctx->zoned_bg keeps a reference to +the block group across the unlock, so nothing is lost while the lock +is dropped. + +This hang occasionally reproduces with fstests generic/475 on a zoned +btrfs filesystem. + +Fixes: 13bb483d32ab ("btrfs: zoned: activate metadata block group on write time") +Reviewed-by: Naohiro Aota +Signed-off-by: Johannes Thumshirn +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/zoned.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c +index 0d590e81f3259..a85fc150c97b5 100644 +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -2188,7 +2188,11 @@ static bool check_bg_is_active(struct btrfs_eb_write_context *ctx, + + if (fs_info->treelog_bg == block_group->start) { + if (!btrfs_zone_activate(block_group)) { +- int ret_fin = btrfs_zone_finish_one_bg(fs_info); ++ int ret_fin; ++ ++ btrfs_zoned_meta_io_unlock(fs_info); ++ ret_fin = btrfs_zone_finish_one_bg(fs_info); ++ btrfs_zoned_meta_io_lock(fs_info); + + if (ret_fin != 1 || !btrfs_zone_activate(block_group)) + return false; +-- +2.53.0 + diff --git a/queue-7.1/btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch b/queue-7.1/btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch new file mode 100644 index 0000000000..98f88b8e80 --- /dev/null +++ b/queue-7.1/btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch @@ -0,0 +1,56 @@ +From c95574819aef191e8285da4bd2a8b1f28dd5d974 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 3 Jul 2026 07:54:45 +0200 +Subject: btrfs: zoned: reset meta_write_pointer on zone reset + +From: Johannes Thumshirn + +[ Upstream commit 5fabb1cf25d723274009d7b759545fd59f230c9d ] + +btrfs_reset_unused_block_groups() resets a block group's zone and sets +alloc_offset back to 0 so the space can be reused, but it leaves +meta_write_pointer pointing at the previous end of the zone. + +Once the block group is reactivated and reused for metadata, newly +allocated tree blocks live before that stale write pointer. +btrfs_check_meta_write_pointer() then sees them behind the write pointer, +so they can never be written out in sequential order: the dirty extent +buffers are stranded and pin their btree_inode folios until unmount. + +Reset meta_write_pointer back to the start of the block group for +metadata and system block groups. + +Fixes: 453a73c3069a ("btrfs: zoned: reclaim unused zone by zone resetting") +Reviewed-by: Naohiro Aota +Signed-off-by: Johannes Thumshirn +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/zoned.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c +index a85fc150c97b5..6963e9068a565 100644 +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -3196,6 +3196,17 @@ int btrfs_reset_unused_block_groups(struct btrfs_space_info *space_info, u64 num + reclaimed = bg->alloc_offset; + bg->zone_unusable = bg->length - bg->zone_capacity; + bg->alloc_offset = 0; ++ /* ++ * The zone was just reset to empty, so alloc_offset went back to ++ * the start of the zone. For metadata/system block groups the ++ * write pointer must follow it back to the start of the zone; ++ * otherwise it stays stale at the previous (finished) zone end, ++ * and metadata written into the reused zone would sit behind the ++ * write pointer, could never be written out in sequential order, ++ * and would be stranded (pinning its folio) until unmount. ++ */ ++ if (bg->flags & (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_SYSTEM)) ++ bg->meta_write_pointer = bg->start; + /* + * This holds because we currently reset fully used then freed + * block group. +-- +2.53.0 + diff --git a/queue-7.1/btrfs-zoned-skip-fully-truncated-ordered-extents-at-.patch b/queue-7.1/btrfs-zoned-skip-fully-truncated-ordered-extents-at-.patch new file mode 100644 index 0000000000..3a6675bb6c --- /dev/null +++ b/queue-7.1/btrfs-zoned-skip-fully-truncated-ordered-extents-at-.patch @@ -0,0 +1,56 @@ +From ac03bea00a34bb7e3c31f5a2558ebe32c331538c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 10:23:12 +0200 +Subject: btrfs: zoned: skip fully truncated ordered extents at zone finish + +From: Johannes Thumshirn + +[ Upstream commit ab602da96a915d42dcb1b0b322e8daea0f71b51f ] + +A fully truncated ordered extent (truncated_len == 0) wrote no data, so its +->csum_list is empty and btrfs_finish_ordered_zoned() trips: + + assertion failed: !list_empty(&ordered->csum_list), in fs/btrfs/zoned.c:2141 + +Since commit 66ff4d366e7e a short or cancelled direct IO write finishes the +unsubmitted ordered extent as truncated with uptodate = true instead of +setting BTRFS_ORDERED_IOERR, so it now reaches btrfs_finish_ordered_zoned() +rather than being skipped by the IOERR check in btrfs_finish_ordered_io(). +generic/208 hits this on a zoned filesystem. + +Return early for these, like the BTRFS_ORDERED_PREALLOC case; there is no +zone append result to record and btrfs_finish_one_ordered() skips them too. + +Fixes: 66ff4d366e7e ("btrfs: fix false IO failure after falling back to buffered write") +Reviewed-by: Qu Wenruo +Signed-off-by: Johannes Thumshirn +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/zoned.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c +index 6963e9068a565..e9d72401d328f 100644 +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -2136,6 +2136,16 @@ void btrfs_finish_ordered_zoned(struct btrfs_ordered_extent *ordered) + if (test_bit(BTRFS_ORDERED_PREALLOC, &ordered->flags)) + return; + ++ /* ++ * A fully truncated ordered extent wrote no data and so has ++ * no zone append result to record. ++ */ ++ if (test_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags) && ++ ordered->truncated_len == 0) { ++ ASSERT(list_empty(&ordered->csum_list)); ++ return; ++ } ++ + ASSERT(!list_empty(&ordered->csum_list)); + sum = list_first_entry(&ordered->csum_list, struct btrfs_ordered_sum, list); + logical = sum->logical; +-- +2.53.0 + diff --git a/queue-7.1/can-isotp-check-register_netdevice_notifier-error-in.patch b/queue-7.1/can-isotp-check-register_netdevice_notifier-error-in.patch new file mode 100644 index 0000000000..caf970894e --- /dev/null +++ b/queue-7.1/can-isotp-check-register_netdevice_notifier-error-in.patch @@ -0,0 +1,57 @@ +From f76297ebfeddac4fa6236e472524cc0ba3d673c3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Jul 2026 16:56:56 +0800 +Subject: can: isotp: check register_netdevice_notifier() error in module init + +From: Minhong He + +[ Upstream commit ef09a13c5afac41a3c4b5f22b8572820d9e7518c ] + +Register the netdevice notifier before can_proto_register() and check the +return value. If protocol registration fails, unregister the notifier +before returning the error. + +Align isotp_module_init() with the reordering already done for raw.c +(commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and +bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization +in bcm_module_init()")). + +Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier") +Signed-off-by: Minhong He +Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn +Signed-off-by: Marc Kleine-Budde +Signed-off-by: Sasha Levin +--- + net/can/isotp.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/net/can/isotp.c b/net/can/isotp.c +index 54becaf6898f1..ae6260e98a7a6 100644 +--- a/net/can/isotp.c ++++ b/net/can/isotp.c +@@ -1907,13 +1907,18 @@ static __init int isotp_module_init(void) + + pr_info("can: isotp protocol (max_pdu_size %d)\n", max_pdu_size); + ++ err = register_netdevice_notifier(&canisotp_notifier); ++ if (err) ++ return err; ++ + err = can_proto_register(&isotp_can_proto); +- if (err < 0) ++ if (err < 0) { + pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err)); +- else +- register_netdevice_notifier(&canisotp_notifier); ++ unregister_netdevice_notifier(&canisotp_notifier); ++ return err; ++ } + +- return err; ++ return 0; + } + + static __exit void isotp_module_exit(void) +-- +2.53.0 + diff --git a/queue-7.1/dmaengine-idxd-fix-double-free-of-wq-engine-and-grou.patch b/queue-7.1/dmaengine-idxd-fix-double-free-of-wq-engine-and-grou.patch new file mode 100644 index 0000000000..22b16cbd43 --- /dev/null +++ b/queue-7.1/dmaengine-idxd-fix-double-free-of-wq-engine-and-grou.patch @@ -0,0 +1,178 @@ +From c7cc6621c2c76b74ac60980ad27a81a938df674f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Apr 2026 16:54:52 -0400 +Subject: dmaengine: idxd: fix double free of wq, engine, and group structs + +From: Yuho Choi + +[ Upstream commit ec2d428b2e32dd157de8f86a86dd85c5b2c8f45c ] + +The release callbacks for wq, engine, and group devices +(idxd_conf_wq_release, idxd_conf_engine_release, +idxd_conf_group_release) each call kfree() on the enclosing struct. +The setup error paths and cleanup functions also call kfree() +explicitly after put_device(), producing a double free whenever +put_device() drops the reference count to zero and fires the release. + +In the setup functions, device_initialize() is called before +device_add(), so the reference count is exactly 1 at the error sites. +put_device() unconditionally fires the release, which frees the struct; +the subsequent explicit kfree() then operates on freed memory. + +For idxd_setup_wqs(), the wq release callback also owns opcap_bmap +and wqcfg. The error unwind additionally freed those fields explicitly +before calling put_device(), causing further double frees on both. + +Remove the redundant explicit kfree() calls from all setup error paths +and cleanup functions for wq, engine, and group structs, delegating +sole ownership of those allocations to the release callbacks. + +Fixes: 7c5dd23e57c1 ("dmaengine: idxd: fix wq conf_dev 'struct device' lifetime") +Fixes: 75b911309060 ("dmaengine: idxd: fix engine conf_dev lifetime") +Fixes: defe49f96012 ("dmaengine: idxd: fix group conf_dev lifetime") +Signed-off-by: Yuho Choi +Acked-by: Vinicius Costa Gomes +Reviewed-by: Frank Li +Link: https://patch.msgid.link/20260415205452.67155-1-dbgh9129@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/dma/idxd/init.c | 36 +++++------------------------------- + 1 file changed, 5 insertions(+), 31 deletions(-) + +diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c +index f1cfc7790d950..4b827a3297564 100644 +--- a/drivers/dma/idxd/init.c ++++ b/drivers/dma/idxd/init.c +@@ -159,18 +159,12 @@ static void idxd_cleanup_interrupts(struct idxd_device *idxd) + + static void idxd_clean_wqs(struct idxd_device *idxd) + { +- struct idxd_wq *wq; + struct device *conf_dev; + int i; + + for (i = 0; i < idxd->max_wqs; i++) { +- wq = idxd->wqs[i]; +- if (idxd->hw.wq_cap.op_config) +- bitmap_free(wq->opcap_bmap); +- kfree(wq->wqcfg); +- conf_dev = wq_confdev(wq); ++ conf_dev = wq_confdev(idxd->wqs[i]); + put_device(conf_dev); +- kfree(wq); + } + bitmap_free(idxd->wq_enable_map); + kfree(idxd->wqs); +@@ -212,7 +206,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd) + rc = dev_set_name(conf_dev, "wq%d.%d", idxd->id, wq->id); + if (rc < 0) { + put_device(conf_dev); +- kfree(wq); + goto err_unwind; + } + +@@ -227,7 +220,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd) + wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev)); + if (!wq->wqcfg) { + put_device(conf_dev); +- kfree(wq); + rc = -ENOMEM; + goto err_unwind; + } +@@ -235,9 +227,7 @@ static int idxd_setup_wqs(struct idxd_device *idxd) + if (idxd->hw.wq_cap.op_config) { + wq->opcap_bmap = bitmap_zalloc(IDXD_MAX_OPCAP_BITS, GFP_KERNEL); + if (!wq->opcap_bmap) { +- kfree(wq->wqcfg); + put_device(conf_dev); +- kfree(wq); + rc = -ENOMEM; + goto err_unwind; + } +@@ -252,13 +242,8 @@ static int idxd_setup_wqs(struct idxd_device *idxd) + + err_unwind: + while (--i >= 0) { +- wq = idxd->wqs[i]; +- if (idxd->hw.wq_cap.op_config) +- bitmap_free(wq->opcap_bmap); +- kfree(wq->wqcfg); +- conf_dev = wq_confdev(wq); ++ conf_dev = wq_confdev(idxd->wqs[i]); + put_device(conf_dev); +- kfree(wq); + } + bitmap_free(idxd->wq_enable_map); + +@@ -270,15 +255,12 @@ static int idxd_setup_wqs(struct idxd_device *idxd) + + static void idxd_clean_engines(struct idxd_device *idxd) + { +- struct idxd_engine *engine; + struct device *conf_dev; + int i; + + for (i = 0; i < idxd->max_engines; i++) { +- engine = idxd->engines[i]; +- conf_dev = engine_confdev(engine); ++ conf_dev = engine_confdev(idxd->engines[i]); + put_device(conf_dev); +- kfree(engine); + } + kfree(idxd->engines); + } +@@ -313,7 +295,6 @@ static int idxd_setup_engines(struct idxd_device *idxd) + rc = dev_set_name(conf_dev, "engine%d.%d", idxd->id, engine->id); + if (rc < 0) { + put_device(conf_dev); +- kfree(engine); + goto err; + } + +@@ -324,10 +305,8 @@ static int idxd_setup_engines(struct idxd_device *idxd) + + err: + while (--i >= 0) { +- engine = idxd->engines[i]; +- conf_dev = engine_confdev(engine); ++ conf_dev = engine_confdev(idxd->engines[i]); + put_device(conf_dev); +- kfree(engine); + } + kfree(idxd->engines); + +@@ -336,13 +315,10 @@ static int idxd_setup_engines(struct idxd_device *idxd) + + static void idxd_clean_groups(struct idxd_device *idxd) + { +- struct idxd_group *group; + int i; + + for (i = 0; i < idxd->max_groups; i++) { +- group = idxd->groups[i]; +- put_device(group_confdev(group)); +- kfree(group); ++ put_device(group_confdev(idxd->groups[i])); + } + kfree(idxd->groups); + } +@@ -377,7 +353,6 @@ static int idxd_setup_groups(struct idxd_device *idxd) + rc = dev_set_name(conf_dev, "group%d.%d", idxd->id, group->id); + if (rc < 0) { + put_device(conf_dev); +- kfree(group); + goto err; + } + +@@ -402,7 +377,6 @@ static int idxd_setup_groups(struct idxd_device *idxd) + while (--i >= 0) { + group = idxd->groups[i]; + put_device(group_confdev(group)); +- kfree(group); + } + kfree(idxd->groups); + +-- +2.53.0 + diff --git a/queue-7.1/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch b/queue-7.1/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch new file mode 100644 index 0000000000..d7c6e51638 --- /dev/null +++ b/queue-7.1/dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch @@ -0,0 +1,65 @@ +From 07e4b21dfc4e87f46404ff66c17c49267710f5ac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 25 May 2026 10:15:50 -0400 +Subject: dmaengine: idxd: fix fdev setup failure cleanup in idxd_cdev_open() + +From: Yuho Choi + +[ Upstream commit ee1d7274102285d78a53161fc705a8d8cd40b066 ] + +The failed_dev_add and failed_dev_name paths drop the file-device +reference while wq->wq_lock is still held. If put_device(fdev) drops the +last reference, idxd_file_dev_release() runs synchronously and tries to +take wq->wq_lock again, deadlocking. + +Those paths also fall through into the later ctx cleanup labels even +though idxd_file_dev_release() owns that cleanup and frees ctx. This can +make idxd_xa_pasid_remove(ctx) and kfree(ctx) operate on a freed context. + +Move idxd_wq_get() before file-device setup can fail, since the release +callback always calls idxd_wq_put(). Then unlock wq->wq_lock before +put_device(fdev) and return directly from the file-device setup failure +path, leaving ctx cleanup to the release callback. + +Fixes: e6fd6d7e5f0fe ("dmaengine: idxd: add a device to represent the file opened") +Signed-off-by: Yuho Choi +Reviewed-by: Dave Jiang +Acked-by: Vinicius Costa Gomes +Link: https://patch.msgid.link/20260525141550.1385581-1-dbgh9129@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/dma/idxd/cdev.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c +index 0366c7cf35020..82b07cf942ef8 100644 +--- a/drivers/dma/idxd/cdev.c ++++ b/drivers/dma/idxd/cdev.c +@@ -288,6 +288,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) + fdev->parent = cdev_dev(idxd_cdev); + fdev->bus = &dsa_bus_type; + fdev->type = &idxd_cdev_file_type; ++ idxd_wq_get(wq); + + rc = dev_set_name(fdev, "file%d", ctx->id); + if (rc < 0) { +@@ -301,13 +302,14 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) + goto failed_dev_add; + } + +- idxd_wq_get(wq); + mutex_unlock(&wq->wq_lock); + return 0; + + failed_dev_add: + failed_dev_name: ++ mutex_unlock(&wq->wq_lock); + put_device(fdev); ++ return rc; + failed_ida: + failed_set_pasid: + if (device_user_pasid_enabled(idxd)) +-- +2.53.0 + diff --git a/queue-7.1/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch b/queue-7.1/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch new file mode 100644 index 0000000000..0658c0730f --- /dev/null +++ b/queue-7.1/dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch @@ -0,0 +1,60 @@ +From cc3a8ba039b951dbd8adb0b1282bb5f7a69034b3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 1 Jul 2026 12:57:33 +0800 +Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA + +From: Hongling Zeng + +[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ] + +When terminating DMA transfers, active descriptors are not properly +reclaimed. Only cyclic descriptors were handled, leaving non-cyclic +descriptors and their LLI chains to be permanently leaked. + +Fix by using vchan_terminate_vdesc() which handles both cyclic and +non-cyclic descriptors by adding them to desc_terminated queue for +proper cleanup. + +Add pchan->desc != pchan->done check to prevent double-adding completed +descriptors, which would corrupt the list. + +Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller") +Signed-off-by: Hongling Zeng +Acked-by: Jernej Skrabec +Suggested-by: Frank Li +Reviewed-by: Frank Li +Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/dma/sun6i-dma.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c +index a9a254dbf8cb9..f47a326dd7ffa 100644 +--- a/drivers/dma/sun6i-dma.c ++++ b/drivers/dma/sun6i-dma.c +@@ -945,16 +945,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan) + + spin_lock_irqsave(&vchan->vc.lock, flags); + +- if (vchan->cyclic) { +- vchan->cyclic = false; +- if (pchan && pchan->desc) { +- struct virt_dma_desc *vd = &pchan->desc->vd; +- struct virt_dma_chan *vc = &vchan->vc; ++ if (pchan && pchan->desc && pchan->desc != pchan->done) { ++ struct virt_dma_desc *vd = &pchan->desc->vd; + +- list_add_tail(&vd->node, &vc->desc_completed); +- } ++ vchan_terminate_vdesc(vd); + } + ++ vchan->cyclic = false; + vchan_get_all_descriptors(&vchan->vc, &head); + + if (pchan) { +-- +2.53.0 + diff --git a/queue-7.1/dmaengine-switchtec-dma-fix-field_get-misuse-when-pr.patch b/queue-7.1/dmaengine-switchtec-dma-fix-field_get-misuse-when-pr.patch new file mode 100644 index 0000000000..ecfbc79c48 --- /dev/null +++ b/queue-7.1/dmaengine-switchtec-dma-fix-field_get-misuse-when-pr.patch @@ -0,0 +1,47 @@ +From 5f5576dc7be79bef833c320479a285634d0c9e4d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 17 Mar 2026 08:32:52 +0000 +Subject: dmaengine: switchtec-dma: fix FIELD_GET misuse when programming SE + threshold + +From: David Carlier + +[ Upstream commit 9d12eb98582fec2578d17e025b13740dcfb57d8e ] + +FIELD_GET(SE_THRESH_MASK, thresh) extracts bits [31:23] from thresh and +right-shifts them, which is the inverse of the intended operation. Since +thresh is derived from se_buf_len / 2 (at most 255), bits [31:23] are +always zero, so the SE threshold is never actually programmed into the +register. + +Use FIELD_PREP() instead to correctly left-shift thresh into bits [31:23] +of the valid_en_se register, consistent with the FIELD_PREP usage for +the perf tuner config just above. + +Fixes: 30eba9df76ad ("dmaengine: switchtec-dma: Implement hardware initialization and cleanup") +Signed-off-by: David Carlier +Review-by: Logan Gunthorpe +Reviewed-by: Frank Li +Link: https://patch.msgid.link/20260317083252.13224-1-devnexen@gmail.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/dma/switchtec_dma.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/dma/switchtec_dma.c b/drivers/dma/switchtec_dma.c +index 3ef9286406159..71d9868ce613e 100644 +--- a/drivers/dma/switchtec_dma.c ++++ b/drivers/dma/switchtec_dma.c +@@ -1099,7 +1099,7 @@ static int switchtec_dma_chan_init(struct switchtec_dma_dev *swdma_dev, + dev_dbg(&pdev->dev, "Channel %d: SE buffer count %d\n", i, se_buf_len); + + thresh = se_buf_len / 2; +- valid_en_se |= FIELD_GET(SE_THRESH_MASK, thresh); ++ valid_en_se |= FIELD_PREP(SE_THRESH_MASK, thresh); + writel(valid_en_se, &swdma_chan->mmio_chan_fw->valid_en_se); + + /* request irqs */ +-- +2.53.0 + diff --git a/queue-7.1/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch b/queue-7.1/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch new file mode 100644 index 0000000000..42070955fa --- /dev/null +++ b/queue-7.1/drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch @@ -0,0 +1,55 @@ +From 07287edbbb8a5a79309122484769132dd2bd2a78 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 17:32:15 +0200 +Subject: Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep + annotation + +From: Sebastian Andrzej Siewior + +[ Upstream commit 8c7ab779c8850f4dab8473463cca9a7d52fdaecc ] + +lockdep_hardirq_threaded() is supposed to be used within IRQ core code +and not within drivers. It is not obvious from within the driver, that +this is the only interrupt service routing and that it is not shared +handler. + +Replace lockdep_hardirq_threaded() with a lockdep annotation limiting +threaded context on PREEMPT_RT to __vmbus_isr(). + +Fixes: f8e6343b7a89c ("Drivers: hv: vmbus: Use kthread for vmbus interrupts on PREEMPT_RT") +Signed-off-by: Sebastian Andrzej Siewior +Reviewed-by: Michael Kelley +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/vmbus_drv.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c +index 23206640c6139..44877664d9d08 100644 +--- a/drivers/hv/vmbus_drv.c ++++ b/drivers/hv/vmbus_drv.c +@@ -1384,8 +1384,19 @@ void vmbus_isr(void) + if (IS_ENABLED(CONFIG_PREEMPT_RT)) { + vmbus_irqd_wake(); + } else { +- lockdep_hardirq_threaded(); ++ static DEFINE_WAIT_OVERRIDE_MAP(vmbus_map, LD_WAIT_CONFIG); ++ ++ /* ++ * vmbus_isr is never force-threaded and always invoked at hard ++ * IRQ level. __vmbus_isr() below can acquire a spinlock_t ++ * which becomes a sleeping lock and must not be acquired in ++ * this context. Therefore on PREEMPT_RT this will be threaded ++ * via vmbus_irqd_wake(). On non-PREEMPT the annotation lets ++ * lockdep know that acquiring a spinlock_t is not an issue. ++ */ ++ lock_map_acquire_try(&vmbus_map); + __vmbus_isr(); ++ lock_map_release(&vmbus_map); + } + } + EXPORT_SYMBOL_FOR_MODULES(vmbus_isr, "mshv_vtl"); +-- +2.53.0 + diff --git a/queue-7.1/drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch b/queue-7.1/drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch new file mode 100644 index 0000000000..0c2cbda2ba --- /dev/null +++ b/queue-7.1/drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch @@ -0,0 +1,80 @@ +From df1013221306f65150f4d2fc35e4a95523586b64 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 18 Jul 2026 12:52:07 +0200 +Subject: drm/i915/dp: Ignore the sink's DSC max FRL rate without a PCON DSC + encoder +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Alexander Kaplan + +[ Upstream commit 8891e39e89042e285fd82fdde325d1311ec750a1 ] + +intel_dp_hdmi_sink_max_frl() limits the sink's max FRL rate by its +DSC max FRL rate whenever the sink supports DSC 1.2. +However, the DSC max FRL rate (HF-VSDB DSC_Max_FRL_Rate) only applies +to compressed video transport, which requires a DSC 1.2 encoder in +the PCON (configured via intel_dp_pcon_dsc_configure()). +Without such an encoder the HDMI link always carries uncompressed +video, for which the regular Max_FRL_Rate is the correct limit. + +Applying the DSC limit unconditionally trains the FRL link at a lower +rate than both the PCON and the sink support. +E.g. an LG OLED G4 (Max_FRL_Rate 48 Gbps, DSC_Max_FRL_Rate 24 Gbps) +behind a Synaptics VMM7100 PCON (PCON max FRL bw 48 Gbps, no DSC +encoder): + + Sink max rate from EDID = 24 Gbps + FRL trained with : 24 Gbps + +while Windows/macOS train the same hardware at 40/48 Gbps. +The too low FRL rate needlessly constrains the formats available to +the sink. + +Only apply the sink's DSC max FRL rate if the PCON has a DSC 1.2 +encoder, matching the gate in intel_dp_pcon_dsc_configure(). +PCONs with a DSC encoder keep the current conservative behavior, +since the link is trained once and compressed transport may be used +for any subsequent mode. +With this the setup above trains at 48 Gbps. + +Tested on PTL (xe) with the above PCON/sink combo. + +Fixes: 10fec80b48c5 ("drm/i915/display: Configure PCON for DSC1.1 to DSC1.2 encoding") +Cc: Ankit Nautiyal +Cc: Ville Syrjälä +Reviewed-by: Ankit Nautiyal +Signed-off-by: Alexander Kaplan +Signed-off-by: Ankit Nautiyal +Link: https://patch.msgid.link/20260718105207.5565-3-alexander.kaplan@sms-medipool.de +(cherry picked from commit 71b57dd92f94569dca4bdf883fbd8ca5d4ed4bae) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_dp.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c +index f34da1a055dd5..c12a8ee846d74 100644 +--- a/drivers/gpu/drm/i915/display/intel_dp.c ++++ b/drivers/gpu/drm/i915/display/intel_dp.c +@@ -4109,7 +4109,14 @@ static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp) + rate_per_lane = info->hdmi.max_frl_rate_per_lane; + max_frl_rate = max_lanes * rate_per_lane; + +- if (info->hdmi.dsc_cap.v_1p2) { ++ /* ++ * The sink's DSC max FRL rate only applies to compressed video ++ * transport, which requires a DSC 1.2 encoder in the PCON. Without ++ * one the HDMI link always carries uncompressed video, for which ++ * the regular max FRL rate is the limit. ++ */ ++ if (drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd) && ++ info->hdmi.dsc_cap.v_1p2) { + max_dsc_lanes = info->hdmi.dsc_cap.max_lanes; + dsc_rate_per_lane = info->hdmi.dsc_cap.max_frl_rate_per_lane; + if (max_dsc_lanes && dsc_rate_per_lane) +-- +2.53.0 + diff --git a/queue-7.1/drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch b/queue-7.1/drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch new file mode 100644 index 0000000000..2000e8fdce --- /dev/null +++ b/queue-7.1/drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch @@ -0,0 +1,117 @@ +From 5ba54986036fb36a0b4f2acea6d95fcb85bac771 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 07:55:44 +0530 +Subject: drm/i915/hdmi: Poll for 200 msec for TMDS_Scrambler_Status + +From: Jerome Tollet + +[ Upstream commit 1afb8eaeec44fd011f2b93ccd9fd426d753d963b ] + +HDMI 2.0 section 6.1.3.1 specifies that after enabling +Scrambling_Enable and starting scrambled video transmission, the source +should poll Scrambling_Status until it reads 1 or until a timeout of +200 ms expires. + +Add a polling step after enabling the HDMI port to check the scrambling +status when HDMI scrambling is enabled. + +On some HDMI 2.0 sinks, omitting this check can result in 4K@60Hz +(594 MHz) failing to come up correctly because the sink has not yet +finished its scrambling setup. In practice, waiting for the scrambling +status here fixes such sinks. + +While this synchronous polling is not itself explicitly required for +correct modeset sequencing, HDMI 2.0 section 6.1.3.1 does recommend it +as the way for the source to verify that the TMDS link is functioning +correctly with scrambling enabled. + +v3: + - Add explicit HDMI 2.0 section reference in code comment + - Clarify commit message around the observed sink fix + +v2: + - Poll TMDS_Scrambler_Status for up to 200 ms instead of using a fixed + delay + +Reported-by: Jerome Tollet +Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/6868 +Link: https://lore.kernel.org/dri-devel/20251230091037.5603-1-jerome.tollet@gmail.com/ +Signed-off-by: Jerome Tollet +Signed-off-by: Ankit Nautiyal +Reviewed-by: Arun R Murthy +Link: https://patch.msgid.link/20260520022544.3097252-1-ankit.k.nautiyal@intel.com +(cherry picked from commit b7d51d65e4f12a48392d260613108ec262bc7774) +Fixes: 15953637886d ("drm/i915: enable scrambling") +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/i915/display/intel_ddi.c | 2 ++ + drivers/gpu/drm/i915/display/intel_hdmi.c | 26 +++++++++++++++++++++++ + drivers/gpu/drm/i915/display/intel_hdmi.h | 2 ++ + 3 files changed, 30 insertions(+) + +diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c +index 0987eee38bd13..a3bfb1be51de8 100644 +--- a/drivers/gpu/drm/i915/display/intel_ddi.c ++++ b/drivers/gpu/drm/i915/display/intel_ddi.c +@@ -3502,6 +3502,8 @@ static void intel_ddi_enable_hdmi(struct intel_atomic_state *state, + } + + intel_ddi_buf_enable(encoder, buf_ctl); ++ ++ intel_hdmi_poll_for_scrambling_enable(crtc_state, connector); + } + + static void intel_ddi_enable(struct intel_atomic_state *state, +diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c +index 05e898d10a2be..234866310c14d 100644 +--- a/drivers/gpu/drm/i915/display/intel_hdmi.c ++++ b/drivers/gpu/drm/i915/display/intel_hdmi.c +@@ -2693,6 +2693,32 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *_ + drm_connector_attach_max_bpc_property(&connector->base, 8, 12); + } + ++/* ++ * HDMI 2.0 spec, section 6.1.3.1 (Scrambling Control): after ++ * enabling Scrambling_Enable and starting scrambled video ++ * transmission, poll Scrambling_Status for up to 200 ms. ++ */ ++void ++intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, ++ struct drm_connector *_connector) ++{ ++ struct intel_connector *connector = to_intel_connector(_connector); ++ struct intel_display *display = to_intel_display(crtc_state); ++ bool scrambling_enabled = false; ++ int ret; ++ ++ if (!crtc_state->hdmi_scrambling) ++ return; ++ ++ /* Poll for a max of 200 msec as per HDMI spec */ ++ ret = poll_timeout_us(scrambling_enabled = drm_scdc_get_scrambling_status(&connector->base), ++ scrambling_enabled, 1000, 200 * 1000, false); ++ if (ret) ++ drm_dbg_kms(display->drm, ++ "[CONNECTOR:%d:%s] Timed out waiting for scrambling enable\n", ++ connector->base.base.id, connector->base.name); ++} ++ + /* + * intel_hdmi_handle_sink_scrambling: handle sink scrambling/clock ratio setup + * @encoder: intel_encoder +diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h +index be2fad57e4ad0..0fa3661568e86 100644 +--- a/drivers/gpu/drm/i915/display/intel_hdmi.h ++++ b/drivers/gpu/drm/i915/display/intel_hdmi.h +@@ -70,5 +70,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + unsigned int type, + void *frame, ssize_t len); ++void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, ++ struct drm_connector *_connector); + + #endif /* __INTEL_HDMI_H__ */ +-- +2.53.0 + diff --git a/queue-7.1/drm-mediatek-check-crtc-state-before-freeing.patch b/queue-7.1/drm-mediatek-check-crtc-state-before-freeing.patch new file mode 100644 index 0000000000..e0b609f990 --- /dev/null +++ b/queue-7.1/drm-mediatek-check-crtc-state-before-freeing.patch @@ -0,0 +1,51 @@ +From df7d12ade4820ae52e31524de02d22e495310c3f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Jul 2026 23:05:28 +0800 +Subject: drm/mediatek: Check CRTC state before freeing + +From: Ruoyu Wang + +[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ] + +mtk_crtc_reset() destroys the current CRTC state only when crtc->state +is non-NULL, but it always converts crtc->state to struct mtk_crtc_state +and passes the result to kfree(). + +When reset is called without an existing state, container_of(NULL, ...) +does not produce NULL. Keep the mtk state free in the same crtc->state +guard as the helper state destruction. + +This issue was found by a static analysis checker and confirmed by +manual source review. + +Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset") +Signed-off-by: Ruoyu Wang +Reviewed-by: CK Hu +Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/ +Signed-off-by: Chun-Kuang Hu +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/mediatek/mtk_crtc.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c +index fcb16f3f7b23b..b01162a7df7fa 100644 +--- a/drivers/gpu/drm/mediatek/mtk_crtc.c ++++ b/drivers/gpu/drm/mediatek/mtk_crtc.c +@@ -153,10 +153,10 @@ static void mtk_crtc_reset(struct drm_crtc *crtc) + { + struct mtk_crtc_state *state; + +- if (crtc->state) ++ if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); +- +- kfree(to_mtk_crtc_state(crtc->state)); ++ kfree(to_mtk_crtc_state(crtc->state)); ++ } + crtc->state = NULL; + + state = kzalloc_obj(*state); +-- +2.53.0 + diff --git a/queue-7.1/drm-xe-pt-check-no-dma-huge-pte-cases-before-dma-seg.patch b/queue-7.1/drm-xe-pt-check-no-dma-huge-pte-cases-before-dma-seg.patch new file mode 100644 index 0000000000..870a8552c9 --- /dev/null +++ b/queue-7.1/drm-xe-pt-check-no-dma-huge-pte-cases-before-dma-seg.patch @@ -0,0 +1,78 @@ +From b84082dd4337ff1210d6c7ad9742175c9b6d39ed Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 11:29:17 +0530 +Subject: drm/xe/pt: check no-DMA huge-pte cases before DMA segment test + +From: Himal Prasad Ghimiray + +[ Upstream commit d94f82d57e7a86def6946f22b34eb53f96628f8a ] + +On a non-range clear, curs.size is never set, so the segment test +(next - va_curs_start > curs->size) returns false for every level > 0 +before the clear_pt short-circuit is reached. The clear then descends to +level 0 instead of forming a huge zero-leaf, wasting page tables and +risking -ENOMEM on unbind. + +Move the null-VMA, purged-BO and clear_pt short-circuits above the +curs->size test. The bind path always sets curs.size, so it is unaffected. + +v2 +- Also set curs.size on the clear path so the cursor stays meaningful +during the walk. clear_pt is only reached with range == NULL, so assert +that invariant. (Matthew Brost) + +Cc: Matthew Brost +Fixes: 5b658b7e89c3 ("drm/xe: Clear scratch page on vm_bind") +Reported-by: Sashiko +Reviewed-by: Matthew Brost +Link: https://patch.msgid.link/20260728055916.593707-2-himal.prasad.ghimiray@intel.com +Signed-off-by: Himal Prasad Ghimiray +(cherry picked from commit 04eeeb45cb61b8a3e9d785003457e550c920ba49) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/xe/xe_pt.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c +index cf371c0d7b2bb..a4d0d9bf0c8eb 100644 +--- a/drivers/gpu/drm/xe/xe_pt.c ++++ b/drivers/gpu/drm/xe/xe_pt.c +@@ -442,10 +442,6 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, + if (!xe_pt_covers(addr, next, level, &xe_walk->base)) + return false; + +- /* Does the DMA segment cover the whole pte? */ +- if (next - xe_walk->va_curs_start > xe_walk->curs->size) +- return false; +- + /* null VMA's and purged BO's do not have dma addresses */ + if (xe_vma_is_null(xe_walk->vma) || (bo && xe_bo_is_purged(bo))) + return true; +@@ -454,6 +450,10 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, + if (xe_walk->clear_pt) + return true; + ++ /* Does the DMA segment cover the whole pte? */ ++ if (next - xe_walk->va_curs_start > xe_walk->curs->size) ++ return false; ++ + /* Is the DMA address huge PTE size aligned? */ + size = next - addr; + dma = addr - xe_walk->va_curs_start + xe_res_dma(xe_walk->curs); +@@ -774,8 +774,11 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma, + } + + xe_walk.needs_64K = (vm->flags & XE_VM_FLAG_64K); +- if (clear_pt) ++ if (clear_pt) { ++ xe_assert(xe, !range); ++ curs.size = xe_vma_size(vma); + goto walk_pt; ++ } + + if (vma->gpuva.flags & XE_VMA_ATOMIC_PTE_BIT) { + xe_walk.default_vram_pte = xe_atomic_for_vram(vm, vma) ? XE_USM_PPGTT_PTE_AE : 0; +-- +2.53.0 + diff --git a/queue-7.1/erofs-clean-up-erofs_ishare_fill_inode.patch b/queue-7.1/erofs-clean-up-erofs_ishare_fill_inode.patch new file mode 100644 index 0000000000..a905e2b51a --- /dev/null +++ b/queue-7.1/erofs-clean-up-erofs_ishare_fill_inode.patch @@ -0,0 +1,116 @@ +From 815917a26cc807296207f876250c390fd6317ff4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 8 Jun 2026 01:21:32 +0800 +Subject: erofs: clean up erofs_ishare_fill_inode() + +From: Gao Xiang + +[ Upstream commit 1ccc75909ca7c3b52163b408a3e1eb5453db013f ] + + - Use the shorthand `si` to replace the overly long `sharedinode`; + + - Introduce erofs_warn() and get rid of barely-used _erofs_printk(); + + - Get rid of the variable `hash`; + + - Simplify error paths. + +Reviewed-by: Hongbo Li +Reviewed-by: Chao Yu +Signed-off-by: Gao Xiang +Stable-dep-of: 96b2dbbe58a1 ("erofs: ensure valid f_path for page cache sharing") +Signed-off-by: Sasha Levin +--- + fs/erofs/internal.h | 2 ++ + fs/erofs/ishare.c | 45 +++++++++++++++++++-------------------------- + 2 files changed, 21 insertions(+), 26 deletions(-) + +diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h +index 4792490161ec9..9e2ae7b619773 100644 +--- a/fs/erofs/internal.h ++++ b/fs/erofs/internal.h +@@ -23,6 +23,8 @@ + __printf(2, 3) void _erofs_printk(struct super_block *sb, const char *fmt, ...); + #define erofs_err(sb, fmt, ...) \ + _erofs_printk(sb, KERN_ERR fmt "\n", ##__VA_ARGS__) ++#define erofs_warn(sb, fmt, ...) \ ++ _erofs_printk(sb, KERN_WARNING fmt "\n", ##__VA_ARGS__) + #define erofs_info(sb, fmt, ...) \ + _erofs_printk(sb, KERN_INFO fmt "\n", ##__VA_ARGS__) + +diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c +index 6ed66b17359ba..35cbd0bc04d7d 100644 +--- a/fs/erofs/ishare.c ++++ b/fs/erofs/ishare.c +@@ -40,49 +40,42 @@ static int erofs_ishare_iget5_set(struct inode *inode, void *data) + bool erofs_ishare_fill_inode(struct inode *inode) + { + struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb); +- struct erofs_inode *vi = EROFS_I(inode); + const struct address_space_operations *aops; ++ struct erofs_inode *vi = EROFS_I(inode); + struct erofs_inode_fingerprint fp; +- struct inode *sharedinode; +- unsigned long hash; ++ struct inode *si; + + aops = erofs_get_aops(inode, true); + if (IS_ERR(aops)) + return false; + if (erofs_xattr_fill_inode_fingerprint(&fp, inode, sbi->domain_id)) + return false; +- hash = xxh32(fp.opaque, fp.size, 0); +- sharedinode = iget5_locked(erofs_ishare_mnt->mnt_sb, hash, +- erofs_ishare_iget5_eq, erofs_ishare_iget5_set, +- &fp); +- if (!sharedinode) { +- kfree(fp.opaque); +- return false; +- } + +- if (inode_state_read_once(sharedinode) & I_NEW) { +- sharedinode->i_mapping->a_ops = aops; +- sharedinode->i_size = vi->vfs_inode.i_size; +- unlock_new_inode(sharedinode); ++ si = iget5_locked(erofs_ishare_mnt->mnt_sb, ++ xxh32(fp.opaque, fp.size, 0), ++ erofs_ishare_iget5_eq, erofs_ishare_iget5_set, &fp); ++ if (si && (inode_state_read_once(si) & I_NEW)) { ++ si->i_mapping->a_ops = aops; ++ si->i_size = inode->i_size; ++ unlock_new_inode(si); + } else { + kfree(fp.opaque); +- if (aops != sharedinode->i_mapping->a_ops) { +- iput(sharedinode); ++ if (!si || aops != si->i_mapping->a_ops) { ++ iput(si); + return false; + } +- if (sharedinode->i_size != vi->vfs_inode.i_size) { +- _erofs_printk(inode->i_sb, KERN_WARNING +- "size(%lld:%lld) not matches for the same fingerprint\n", +- vi->vfs_inode.i_size, sharedinode->i_size); +- iput(sharedinode); ++ if (si->i_size != inode->i_size) { ++ erofs_warn(inode->i_sb, "i_size mismatch (%lld != %lld) for the same fingerprint", ++ inode->i_size, si->i_size); ++ iput(si); + return false; + } + } +- vi->sharedinode = sharedinode; ++ vi->sharedinode = si; + INIT_LIST_HEAD(&vi->ishare_list); +- spin_lock(&EROFS_I(sharedinode)->ishare_lock); +- list_add(&vi->ishare_list, &EROFS_I(sharedinode)->ishare_list); +- spin_unlock(&EROFS_I(sharedinode)->ishare_lock); ++ spin_lock(&EROFS_I(si)->ishare_lock); ++ list_add(&vi->ishare_list, &EROFS_I(si)->ishare_list); ++ spin_unlock(&EROFS_I(si)->ishare_lock); + return true; + } + +-- +2.53.0 + diff --git a/queue-7.1/erofs-ensure-valid-f_path-for-page-cache-sharing.patch b/queue-7.1/erofs-ensure-valid-f_path-for-page-cache-sharing.patch new file mode 100644 index 0000000000..b05bfad9c6 --- /dev/null +++ b/queue-7.1/erofs-ensure-valid-f_path-for-page-cache-sharing.patch @@ -0,0 +1,192 @@ +From c73a6cf0ae38ea3c1cda98c6aa86017c6e1e8129 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 12:27:39 +0800 +Subject: erofs: ensure valid f_path for page cache sharing + +From: Gao Xiang + +[ Upstream commit 96b2dbbe58a1ea5df8d29c2fe24b5f04715f4443 ] + +Previously, backing files for page cache sharing were set up with +f_path left as NULL (only f_inode was valid). It worked, but a recent +mincore fix relies on f_path.mnt and crashes (found by "erofs/028" on +7.2-rc4): + + BUG: kernel NULL pointer dereference, address: 0000000000000018 + #PF: supervisor read access in kernel mode + #PF: error_code(0x0000) - not-present page + PGD 0 P4D 0 + Oops: Oops: 0000 [#1] SMP PTI + CPU: 3 UID: 0 PID: 675528 Comm: fincore Not tainted 7.2.0-rc4-00002-g[]-dirty #1 PREEMPT(lazy) + Hardware name: Red Hat KVM, BIOS 1.16.0-4.al8 04/01/2014 + RIP: 0010:__do_sys_mincore+0xc0/0x2c0 + ... + +Specify valid paths using valid disconnected dentries together with +erofs_ishare_mnt instead of leaving f_path empty, so they are more +like real backing files in a pseudo filesystem and standard +backing_file_open() can be used directly. + +Fixes: e187bc02f8fa ("mm: do file ownership checks with the proper mount idmap") +Acked-by: Hongbo Li +Signed-off-by: Gao Xiang +Signed-off-by: Sasha Levin +--- + fs/erofs/Kconfig | 1 + + fs/erofs/internal.h | 4 ++-- + fs/erofs/ishare.c | 58 ++++++++++++++++++++++----------------------- + 3 files changed, 31 insertions(+), 32 deletions(-) + +diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig +index 4789b1077d8ce..1feb28cfe557d 100644 +--- a/fs/erofs/Kconfig ++++ b/fs/erofs/Kconfig +@@ -189,6 +189,7 @@ config EROFS_FS_PCPU_KTHREAD_HIPRI + config EROFS_FS_PAGE_CACHE_SHARE + bool "EROFS page cache share support (experimental)" + depends on EROFS_FS && EROFS_FS_XATTR ++ select FS_STACK + help + This enables page cache sharing among inodes with identical + content fingerprints on the same machine. +diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h +index 580f8d9f14e7f..57bd21859c65d 100644 +--- a/fs/erofs/internal.h ++++ b/fs/erofs/internal.h +@@ -288,8 +288,8 @@ struct erofs_inode { + struct erofs_inode_fingerprint fingerprint; + spinlock_t ishare_lock; + }; +- /* for each real inode */ +- struct inode *sharedinode; ++ /* for each real filesystem inode */ ++ struct dentry *sharedentry; + }; + #endif + /* the corresponding vfs inode */ +diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c +index 0868c12fc15b4..25558df98a389 100644 +--- a/fs/erofs/ishare.c ++++ b/fs/erofs/ishare.c +@@ -2,14 +2,13 @@ + /* + * Copyright (C) 2024, Alibaba Cloud + */ ++#include + #include + #include + #include + #include "internal.h" + #include "xattr.h" + +-#include "../internal.h" +- + static struct vfsmount *erofs_ishare_mnt; + + static inline bool erofs_is_ishare_inode(struct inode *inode) +@@ -39,10 +38,12 @@ static int erofs_ishare_iget5_set(struct inode *inode, void *data) + + bool erofs_ishare_fill_inode(struct inode *inode) + { ++ static const struct file_operations empty_fops = {}; + struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb); + const struct address_space_operations *aops; + struct erofs_inode *vi = EROFS_I(inode); + struct erofs_inode_fingerprint fp; ++ struct dentry *sd; + struct inode *si; + + aops = erofs_get_aops(inode); +@@ -55,7 +56,9 @@ bool erofs_ishare_fill_inode(struct inode *inode) + xxh32(fp.opaque, fp.size, 0), + erofs_ishare_iget5_eq, erofs_ishare_iget5_set, &fp); + if (si && (inode_state_read_once(si) & I_NEW)) { ++ si->i_fop = &empty_fops; + si->i_mapping->a_ops = aops; ++ si->i_mode = 0444 | S_IFREG; + si->i_size = inode->i_size; + unlock_new_inode(si); + } else { +@@ -71,7 +74,10 @@ bool erofs_ishare_fill_inode(struct inode *inode) + return false; + } + } +- vi->sharedinode = si; ++ sd = d_obtain_alias(si); /* disconnected denties for sharedinodes */ ++ if (IS_ERR(sd)) ++ return false; ++ vi->sharedentry = sd; + INIT_LIST_HEAD(&vi->ishare_list); + spin_lock(&EROFS_I(si)->ishare_lock); + list_add(&vi->ishare_list, &EROFS_I(si)->ishare_list); +@@ -81,48 +87,40 @@ bool erofs_ishare_fill_inode(struct inode *inode) + + void erofs_ishare_free_inode(struct inode *inode) + { +- struct erofs_inode *vi = EROFS_I(inode); +- struct inode *sharedinode = vi->sharedinode; ++ struct erofs_inode *vi = EROFS_I(inode), *svi; + +- if (!sharedinode) ++ if (!vi->sharedentry) + return; +- spin_lock(&EROFS_I(sharedinode)->ishare_lock); ++ svi = EROFS_I(d_inode(vi->sharedentry)); ++ spin_lock(&svi->ishare_lock); + list_del(&vi->ishare_list); +- spin_unlock(&EROFS_I(sharedinode)->ishare_lock); +- iput(sharedinode); +- vi->sharedinode = NULL; ++ spin_unlock(&svi->ishare_lock); ++ dput(vi->sharedentry); ++ vi->sharedentry = NULL; + } + + static int erofs_ishare_file_open(struct inode *inode, struct file *file) + { +- struct inode *sharedinode = EROFS_I(inode)->sharedinode; +- struct file *realfile; ++ struct path sharedpath = { ++ .mnt = erofs_ishare_mnt, ++ .dentry = EROFS_I(inode)->sharedentry, ++ }; ++ struct file *rf; + + if (file->f_flags & O_DIRECT) + return -EINVAL; +- realfile = alloc_empty_backing_file(O_RDONLY|O_NOATIME, current_cred(), +- file); +- if (IS_ERR(realfile)) +- return PTR_ERR(realfile); +- ihold(sharedinode); +- realfile->f_op = &erofs_file_fops; +- realfile->f_inode = sharedinode; +- realfile->f_mapping = sharedinode->i_mapping; +- path_get(&file->f_path); +- backing_file_set_user_path(realfile, &file->f_path); +- +- file_ra_state_init(&realfile->f_ra, file->f_mapping); +- realfile->private_data = EROFS_I(inode); +- file->private_data = realfile; ++ ++ rf = backing_file_open(file, file->f_flags | O_NOATIME, ++ &sharedpath, current_cred()); ++ if (IS_ERR(rf)) ++ return PTR_ERR(rf); ++ file->private_data = rf; + return 0; + } + + static int erofs_ishare_file_release(struct inode *inode, struct file *file) + { +- struct file *realfile = file->private_data; +- +- iput(realfile->f_inode); +- fput(realfile); ++ fput(file->private_data); + file->private_data = NULL; + return 0; + } +-- +2.53.0 + diff --git a/queue-7.1/erofs-remove-fscache-backend-entirely.patch b/queue-7.1/erofs-remove-fscache-backend-entirely.patch new file mode 100644 index 0000000000..197f1e5285 --- /dev/null +++ b/queue-7.1/erofs-remove-fscache-backend-entirely.patch @@ -0,0 +1,1275 @@ +From e63a500e59ec32aa2329f001f08f60a7e760daf2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 22 Jun 2026 09:36:22 +0800 +Subject: erofs: remove fscache backend entirely + +From: Gao Xiang + +[ Upstream commit c37460cd9b2fcb61ec66b7eb4fde737e65ec2a56 ] + +EROFS over fscache was introduced to provide image lazy pulling +functionality. After the feature landed, the fscache subsystem made +netfs a new hard dependency, which is unexpected for a local filesystem +and has an kernel-defined caching hierarchy which could be inflexible +compared to the fanotify pre-content hooks. Therefore, this feature has +been deprecated for almost two years. + +As EROFS file-backed mounts and fanotify pre-content hooks both upstream +for a while and already providing equivalent functionality (erofs-utils +has supported fanotify pre-content hooks), let's remove the fscache +backend now. + +The main application of this feature is Nydus [1], and they plan to move +to use fanotify pre-content hooks in the near future too. + +I hope this patch can be merged into Linux 7.2, which is also motivated +by newly found implementation issues [2][3] that are not worth +investigating given the deprecation and limited development resources. +The associated fscache/cachefiles cleanup patch will follow separately +through the vfs tree (netfs) later: it seems fine since the codebase is +isolated by CONFIG_CACHEFILES_ONDEMAND. + +[1] https://github.com/dragonflyoss/nydus/blob/v2.1.0/docs/nydus-fscache.md +[2] https://github.com/dragonflyoss/nydus/pull/1824 +[3] https://lore.kernel.org/r/20260619135800.1594811-1-michael.bommarito@gmail.com + +Acked-by: Jingbo Xu +Signed-off-by: Gao Xiang +Stable-dep-of: 96b2dbbe58a1 ("erofs: ensure valid f_path for page cache sharing") +Signed-off-by: Sasha Levin +--- + Documentation/filesystems/erofs.rst | 9 +- + fs/erofs/Kconfig | 21 +- + fs/erofs/Makefile | 1 - + fs/erofs/data.c | 4 +- + fs/erofs/fscache.c | 664 ---------------------------- + fs/erofs/inode.c | 2 +- + fs/erofs/internal.h | 70 +-- + fs/erofs/ishare.c | 2 +- + fs/erofs/super.c | 94 +--- + fs/erofs/zdata.c | 6 - + 10 files changed, 26 insertions(+), 847 deletions(-) + delete mode 100644 fs/erofs/fscache.c + +diff --git a/Documentation/filesystems/erofs.rst b/Documentation/filesystems/erofs.rst +index fe06308e546c1..4a82e1e31e5fa 100644 +--- a/Documentation/filesystems/erofs.rst ++++ b/Documentation/filesystems/erofs.rst +@@ -127,12 +127,9 @@ dax A legacy option which is an alias for ``dax=always``. + device=%s Specify a path to an extra device to be used together. + directio (For file-backed mounts) Use direct I/O to access backing + files, and asynchronous I/O will be enabled if supported. +-fsid=%s Specify a filesystem image ID for Fscache back-end. +-domain_id=%s Specify a trusted domain ID for fscache mode so that +- different images with the same blobs, identified by blob IDs, +- can share storage within the same trusted domain. +- Also used for different filesystems with inode page sharing +- enabled to share page cache within the trusted domain. ++domain_id=%s Specify a trusted domain ID. Filesystems sharing the same ++ domain ID can share page cache across mounts when inode ++ page sharing is enabled. (not shown in mountinfo output) + fsoffset=%llu Specify block-aligned filesystem offset for the primary device. + inode_share Enable inode page sharing for this filesystem. Inodes with + identical content within the same domain ID can share the +diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig +index 97c48ebe84584..4789b1077d8ce 100644 +--- a/fs/erofs/Kconfig ++++ b/fs/erofs/Kconfig +@@ -3,13 +3,11 @@ + config EROFS_FS + tristate "EROFS filesystem support" + depends on BLOCK +- select CACHEFILES if EROFS_FS_ONDEMAND + select CRC32 + select CRYPTO if EROFS_FS_ZIP_ACCEL + select CRYPTO_DEFLATE if EROFS_FS_ZIP_ACCEL + select FS_IOMAP + select LZ4_DECOMPRESS if EROFS_FS_ZIP +- select NETFS_SUPPORT if EROFS_FS_ONDEMAND + select XXHASH if EROFS_FS_XATTR + select XZ_DEC if EROFS_FS_ZIP_LZMA + select XZ_DEC_MICROLZMA if EROFS_FS_ZIP_LZMA +@@ -109,9 +107,6 @@ config EROFS_FS_BACKED_BY_FILE + be used to simplify error-prone lifetime management of unnecessary + virtual block devices. + +- Note that this feature, along with ongoing fanotify pre-content +- hooks, will eventually replace "EROFS over fscache." +- + If you don't want to enable this feature, say N. + + config EROFS_FS_ZIP +@@ -172,20 +167,6 @@ config EROFS_FS_ZIP_ACCEL + + If unsure, say N. + +-config EROFS_FS_ONDEMAND +- bool "EROFS fscache-based on-demand read support (deprecated)" +- depends on EROFS_FS +- select FSCACHE +- select CACHEFILES_ONDEMAND +- help +- This permits EROFS to use fscache-backed data blobs with on-demand +- read support. +- +- It is now deprecated and scheduled to be removed from the kernel +- after fanotify pre-content hooks are landed. +- +- If unsure, say N. +- + config EROFS_FS_PCPU_KTHREAD + bool "EROFS per-cpu decompression kthread workers" + depends on EROFS_FS_ZIP +@@ -207,7 +188,7 @@ config EROFS_FS_PCPU_KTHREAD_HIPRI + + config EROFS_FS_PAGE_CACHE_SHARE + bool "EROFS page cache share support (experimental)" +- depends on EROFS_FS && EROFS_FS_XATTR && !EROFS_FS_ONDEMAND ++ depends on EROFS_FS && EROFS_FS_XATTR + help + This enables page cache sharing among inodes with identical + content fingerprints on the same machine. +diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile +index a80e1762b6079..30423496786fe 100644 +--- a/fs/erofs/Makefile ++++ b/fs/erofs/Makefile +@@ -9,5 +9,4 @@ erofs-$(CONFIG_EROFS_FS_ZIP_DEFLATE) += decompressor_deflate.o + erofs-$(CONFIG_EROFS_FS_ZIP_ZSTD) += decompressor_zstd.o + erofs-$(CONFIG_EROFS_FS_ZIP_ACCEL) += decompressor_crypto.o + erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o +-erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o + erofs-$(CONFIG_EROFS_FS_PAGE_CACHE_SHARE) += ishare.o +diff --git a/fs/erofs/data.c b/fs/erofs/data.c +index 44da21c9d7776..af846bab1368c 100644 +--- a/fs/erofs/data.c ++++ b/fs/erofs/data.c +@@ -80,9 +80,7 @@ int erofs_init_metabuf(struct erofs_buf *buf, struct super_block *sb, + if (erofs_is_fileio_mode(sbi)) { + buf->file = sbi->dif0.file; /* some fs like FUSE needs it */ + buf->mapping = buf->file->f_mapping; +- } else if (erofs_is_fscache_mode(sb)) +- buf->mapping = sbi->dif0.fscache->inode->i_mapping; +- else ++ } else + buf->mapping = sb->s_bdev->bd_mapping; + return 0; + } +diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c +deleted file mode 100644 +index 685c68774379b..0000000000000 +--- a/fs/erofs/fscache.c ++++ /dev/null +@@ -1,664 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0-or-later +-/* +- * Copyright (C) 2022, Alibaba Cloud +- * Copyright (C) 2022, Bytedance Inc. All rights reserved. +- */ +-#include +-#include "internal.h" +- +-static DEFINE_MUTEX(erofs_domain_list_lock); +-static DEFINE_MUTEX(erofs_domain_cookies_lock); +-static LIST_HEAD(erofs_domain_list); +-static LIST_HEAD(erofs_domain_cookies_list); +-static struct vfsmount *erofs_pseudo_mnt; +- +-struct erofs_fscache_io { +- struct netfs_cache_resources cres; +- struct iov_iter iter; +- netfs_io_terminated_t end_io; +- void *private; +- refcount_t ref; +-}; +- +-struct erofs_fscache_rq { +- struct address_space *mapping; /* The mapping being accessed */ +- loff_t start; /* Start position */ +- size_t len; /* Length of the request */ +- size_t submitted; /* Length of submitted */ +- short error; /* 0 or error that occurred */ +- refcount_t ref; +-}; +- +-static bool erofs_fscache_io_put(struct erofs_fscache_io *io) +-{ +- if (!refcount_dec_and_test(&io->ref)) +- return false; +- if (io->cres.ops) +- io->cres.ops->end_operation(&io->cres); +- kfree(io); +- return true; +-} +- +-static void erofs_fscache_req_complete(struct erofs_fscache_rq *req) +-{ +- struct folio *folio; +- bool failed = req->error; +- pgoff_t start_page = req->start / PAGE_SIZE; +- pgoff_t last_page = ((req->start + req->len) / PAGE_SIZE) - 1; +- +- XA_STATE(xas, &req->mapping->i_pages, start_page); +- +- rcu_read_lock(); +- xas_for_each(&xas, folio, last_page) { +- if (xas_retry(&xas, folio)) +- continue; +- if (!failed) +- folio_mark_uptodate(folio); +- folio_unlock(folio); +- } +- rcu_read_unlock(); +-} +- +-static void erofs_fscache_req_put(struct erofs_fscache_rq *req) +-{ +- if (!refcount_dec_and_test(&req->ref)) +- return; +- erofs_fscache_req_complete(req); +- kfree(req); +-} +- +-static struct erofs_fscache_rq *erofs_fscache_req_alloc(struct address_space *mapping, +- loff_t start, size_t len) +-{ +- struct erofs_fscache_rq *req = kzalloc_obj(*req); +- +- if (!req) +- return NULL; +- req->mapping = mapping; +- req->start = start; +- req->len = len; +- refcount_set(&req->ref, 1); +- return req; +-} +- +-static void erofs_fscache_req_io_put(struct erofs_fscache_io *io) +-{ +- struct erofs_fscache_rq *req = io->private; +- +- if (erofs_fscache_io_put(io)) +- erofs_fscache_req_put(req); +-} +- +-static void erofs_fscache_req_end_io(void *priv, ssize_t transferred_or_error) +-{ +- struct erofs_fscache_io *io = priv; +- struct erofs_fscache_rq *req = io->private; +- +- if (IS_ERR_VALUE(transferred_or_error)) +- req->error = transferred_or_error; +- erofs_fscache_req_io_put(io); +-} +- +-static struct erofs_fscache_io *erofs_fscache_req_io_alloc(struct erofs_fscache_rq *req) +-{ +- struct erofs_fscache_io *io = kzalloc_obj(*io); +- +- if (!io) +- return NULL; +- io->end_io = erofs_fscache_req_end_io; +- io->private = req; +- refcount_inc(&req->ref); +- refcount_set(&io->ref, 1); +- return io; +-} +- +-/* +- * Read data from fscache described by cookie at pstart physical address +- * offset, and fill the read data into buffer described by io->iter. +- */ +-static int erofs_fscache_read_io_async(struct fscache_cookie *cookie, +- loff_t pstart, struct erofs_fscache_io *io) +-{ +- enum netfs_io_source source; +- struct netfs_cache_resources *cres = &io->cres; +- struct iov_iter *iter = &io->iter; +- int ret; +- +- ret = fscache_begin_read_operation(cres, cookie); +- if (ret) +- return ret; +- +- while (iov_iter_count(iter)) { +- size_t orig_count = iov_iter_count(iter), len = orig_count; +- unsigned long flags = 1 << NETFS_SREQ_ONDEMAND; +- +- source = cres->ops->prepare_ondemand_read(cres, +- pstart, &len, LLONG_MAX, &flags, 0); +- if (WARN_ON(len == 0)) +- source = NETFS_INVALID_READ; +- if (source != NETFS_READ_FROM_CACHE) { +- erofs_err(NULL, "prepare_ondemand_read failed (source %d)", source); +- return -EIO; +- } +- +- iov_iter_truncate(iter, len); +- refcount_inc(&io->ref); +- ret = fscache_read(cres, pstart, iter, NETFS_READ_HOLE_FAIL, +- io->end_io, io); +- if (ret == -EIOCBQUEUED) +- ret = 0; +- if (ret) { +- erofs_err(NULL, "fscache_read failed (ret %d)", ret); +- return ret; +- } +- if (WARN_ON(iov_iter_count(iter))) +- return -EIO; +- +- iov_iter_reexpand(iter, orig_count - len); +- pstart += len; +- } +- return 0; +-} +- +-struct erofs_fscache_bio { +- struct erofs_fscache_io io; +- struct bio bio; /* w/o bdev to share bio_add_page/endio() */ +- struct bio_vec bvecs[BIO_MAX_VECS]; +-}; +- +-static void erofs_fscache_bio_endio(void *priv, ssize_t transferred_or_error) +-{ +- struct erofs_fscache_bio *io = priv; +- +- if (IS_ERR_VALUE(transferred_or_error)) +- io->bio.bi_status = errno_to_blk_status(transferred_or_error); +- bio_endio(&io->bio); +- BUILD_BUG_ON(offsetof(struct erofs_fscache_bio, io) != 0); +- erofs_fscache_io_put(&io->io); +-} +- +-struct bio *erofs_fscache_bio_alloc(struct erofs_map_dev *mdev) +-{ +- struct erofs_fscache_bio *io; +- +- io = kmalloc_obj(*io, GFP_KERNEL | __GFP_NOFAIL); +- bio_init(&io->bio, NULL, io->bvecs, BIO_MAX_VECS, REQ_OP_READ); +- io->io.private = mdev->m_dif->fscache->cookie; +- io->io.end_io = erofs_fscache_bio_endio; +- refcount_set(&io->io.ref, 1); +- return &io->bio; +-} +- +-void erofs_fscache_submit_bio(struct bio *bio) +-{ +- struct erofs_fscache_bio *io = container_of(bio, +- struct erofs_fscache_bio, bio); +- int ret; +- +- iov_iter_bvec(&io->io.iter, ITER_DEST, io->bvecs, bio->bi_vcnt, +- bio->bi_iter.bi_size); +- ret = erofs_fscache_read_io_async(io->io.private, +- bio->bi_iter.bi_sector << 9, &io->io); +- erofs_fscache_io_put(&io->io); +- if (!ret) +- return; +- bio->bi_status = errno_to_blk_status(ret); +- bio_endio(bio); +-} +- +-static int erofs_fscache_meta_read_folio(struct file *data, struct folio *folio) +-{ +- struct erofs_fscache *ctx = folio->mapping->host->i_private; +- int ret = -ENOMEM; +- struct erofs_fscache_rq *req; +- struct erofs_fscache_io *io; +- +- req = erofs_fscache_req_alloc(folio->mapping, +- folio_pos(folio), folio_size(folio)); +- if (!req) { +- folio_unlock(folio); +- return ret; +- } +- +- io = erofs_fscache_req_io_alloc(req); +- if (!io) { +- req->error = ret; +- goto out; +- } +- iov_iter_xarray(&io->iter, ITER_DEST, &folio->mapping->i_pages, +- folio_pos(folio), folio_size(folio)); +- +- ret = erofs_fscache_read_io_async(ctx->cookie, folio_pos(folio), io); +- if (ret) +- req->error = ret; +- +- erofs_fscache_req_io_put(io); +-out: +- erofs_fscache_req_put(req); +- return ret; +-} +- +-static int erofs_fscache_data_read_slice(struct erofs_fscache_rq *req) +-{ +- struct address_space *mapping = req->mapping; +- struct inode *inode = mapping->host; +- struct super_block *sb = inode->i_sb; +- struct erofs_fscache_io *io; +- struct erofs_map_blocks map; +- struct erofs_map_dev mdev; +- loff_t pos = req->start + req->submitted; +- size_t count; +- int ret; +- +- map.m_la = pos; +- ret = erofs_map_blocks(inode, &map); +- if (ret) +- return ret; +- +- if (map.m_flags & EROFS_MAP_META) { +- struct erofs_buf buf = __EROFS_BUF_INITIALIZER; +- struct iov_iter iter; +- size_t size = map.m_llen; +- void *src; +- +- src = erofs_read_metabuf(&buf, sb, map.m_pa, +- erofs_inode_in_metabox(inode)); +- if (IS_ERR(src)) +- return PTR_ERR(src); +- +- iov_iter_xarray(&iter, ITER_DEST, &mapping->i_pages, pos, PAGE_SIZE); +- if (copy_to_iter(src, size, &iter) != size) { +- erofs_put_metabuf(&buf); +- return -EFAULT; +- } +- iov_iter_zero(PAGE_SIZE - size, &iter); +- erofs_put_metabuf(&buf); +- req->submitted += PAGE_SIZE; +- return 0; +- } +- +- count = req->len - req->submitted; +- if (!(map.m_flags & EROFS_MAP_MAPPED)) { +- struct iov_iter iter; +- +- iov_iter_xarray(&iter, ITER_DEST, &mapping->i_pages, pos, count); +- iov_iter_zero(count, &iter); +- req->submitted += count; +- return 0; +- } +- +- count = min_t(size_t, map.m_llen - (pos - map.m_la), count); +- DBG_BUGON(!count || count % PAGE_SIZE); +- +- mdev = (struct erofs_map_dev) { +- .m_deviceid = map.m_deviceid, +- .m_pa = map.m_pa, +- }; +- ret = erofs_map_dev(sb, &mdev); +- if (ret) +- return ret; +- +- io = erofs_fscache_req_io_alloc(req); +- if (!io) +- return -ENOMEM; +- iov_iter_xarray(&io->iter, ITER_DEST, &mapping->i_pages, pos, count); +- ret = erofs_fscache_read_io_async(mdev.m_dif->fscache->cookie, +- mdev.m_pa + (pos - map.m_la), io); +- erofs_fscache_req_io_put(io); +- +- req->submitted += count; +- return ret; +-} +- +-static int erofs_fscache_data_read(struct erofs_fscache_rq *req) +-{ +- int ret; +- +- do { +- ret = erofs_fscache_data_read_slice(req); +- if (ret) +- req->error = ret; +- } while (!ret && req->submitted < req->len); +- return ret; +-} +- +-static int erofs_fscache_read_folio(struct file *file, struct folio *folio) +-{ +- struct erofs_fscache_rq *req; +- int ret; +- +- req = erofs_fscache_req_alloc(folio->mapping, +- folio_pos(folio), folio_size(folio)); +- if (!req) { +- folio_unlock(folio); +- return -ENOMEM; +- } +- +- ret = erofs_fscache_data_read(req); +- erofs_fscache_req_put(req); +- return ret; +-} +- +-static void erofs_fscache_readahead(struct readahead_control *rac) +-{ +- struct erofs_fscache_rq *req; +- +- if (!readahead_count(rac)) +- return; +- +- req = erofs_fscache_req_alloc(rac->mapping, +- readahead_pos(rac), readahead_length(rac)); +- if (!req) +- return; +- +- /* The request completion will drop refs on the folios. */ +- while (readahead_folio(rac)) +- ; +- +- erofs_fscache_data_read(req); +- erofs_fscache_req_put(req); +-} +- +-static const struct address_space_operations erofs_fscache_meta_aops = { +- .read_folio = erofs_fscache_meta_read_folio, +-}; +- +-const struct address_space_operations erofs_fscache_access_aops = { +- .read_folio = erofs_fscache_read_folio, +- .readahead = erofs_fscache_readahead, +-}; +- +-static void erofs_fscache_domain_put(struct erofs_domain *domain) +-{ +- mutex_lock(&erofs_domain_list_lock); +- if (refcount_dec_and_test(&domain->ref)) { +- list_del(&domain->list); +- if (list_empty(&erofs_domain_list)) { +- kern_unmount(erofs_pseudo_mnt); +- erofs_pseudo_mnt = NULL; +- } +- fscache_relinquish_volume(domain->volume, NULL, false); +- mutex_unlock(&erofs_domain_list_lock); +- kfree_sensitive(domain->domain_id); +- kfree(domain); +- return; +- } +- mutex_unlock(&erofs_domain_list_lock); +-} +- +-static int erofs_fscache_register_volume(struct super_block *sb) +-{ +- struct erofs_sb_info *sbi = EROFS_SB(sb); +- char *domain_id = sbi->domain_id; +- struct fscache_volume *volume; +- char *name; +- int ret = 0; +- +- name = kasprintf(GFP_KERNEL, "erofs,%s", +- domain_id ? domain_id : sbi->fsid); +- if (!name) +- return -ENOMEM; +- +- volume = fscache_acquire_volume(name, NULL, NULL, 0); +- if (IS_ERR_OR_NULL(volume)) { +- erofs_err(sb, "failed to register volume for %s", name); +- ret = volume ? PTR_ERR(volume) : -EOPNOTSUPP; +- volume = NULL; +- } +- +- sbi->volume = volume; +- kfree(name); +- return ret; +-} +- +-static int erofs_fscache_init_domain(struct super_block *sb) +-{ +- int err; +- struct erofs_domain *domain; +- struct erofs_sb_info *sbi = EROFS_SB(sb); +- +- domain = kzalloc_obj(struct erofs_domain); +- if (!domain) +- return -ENOMEM; +- +- domain->domain_id = kstrdup(sbi->domain_id, GFP_KERNEL); +- if (!domain->domain_id) { +- kfree(domain); +- return -ENOMEM; +- } +- +- err = erofs_fscache_register_volume(sb); +- if (err) +- goto out; +- +- if (!erofs_pseudo_mnt) { +- struct vfsmount *mnt = kern_mount(&erofs_anon_fs_type); +- if (IS_ERR(mnt)) { +- err = PTR_ERR(mnt); +- goto out; +- } +- erofs_pseudo_mnt = mnt; +- } +- +- domain->volume = sbi->volume; +- refcount_set(&domain->ref, 1); +- list_add(&domain->list, &erofs_domain_list); +- sbi->domain = domain; +- return 0; +-out: +- kfree_sensitive(domain->domain_id); +- kfree(domain); +- return err; +-} +- +-static int erofs_fscache_register_domain(struct super_block *sb) +-{ +- int err; +- struct erofs_domain *domain; +- struct erofs_sb_info *sbi = EROFS_SB(sb); +- +- mutex_lock(&erofs_domain_list_lock); +- list_for_each_entry(domain, &erofs_domain_list, list) { +- if (!strcmp(domain->domain_id, sbi->domain_id)) { +- sbi->domain = domain; +- sbi->volume = domain->volume; +- refcount_inc(&domain->ref); +- mutex_unlock(&erofs_domain_list_lock); +- return 0; +- } +- } +- err = erofs_fscache_init_domain(sb); +- mutex_unlock(&erofs_domain_list_lock); +- return err; +-} +- +-static struct erofs_fscache *erofs_fscache_acquire_cookie(struct super_block *sb, +- char *name, unsigned int flags) +-{ +- struct fscache_volume *volume = EROFS_SB(sb)->volume; +- struct erofs_fscache *ctx; +- struct fscache_cookie *cookie; +- struct super_block *isb; +- struct inode *inode; +- int ret; +- +- ctx = kzalloc_obj(*ctx); +- if (!ctx) +- return ERR_PTR(-ENOMEM); +- INIT_LIST_HEAD(&ctx->node); +- refcount_set(&ctx->ref, 1); +- +- cookie = fscache_acquire_cookie(volume, FSCACHE_ADV_WANT_CACHE_SIZE, +- name, strlen(name), NULL, 0, 0); +- if (!cookie) { +- erofs_err(sb, "failed to get cookie for %s", name); +- ret = -EINVAL; +- goto err; +- } +- fscache_use_cookie(cookie, false); +- +- /* +- * Allocate anonymous inode in global pseudo mount for shareable blobs, +- * so that they are accessible among erofs fs instances. +- */ +- isb = flags & EROFS_REG_COOKIE_SHARE ? erofs_pseudo_mnt->mnt_sb : sb; +- inode = new_inode(isb); +- if (!inode) { +- erofs_err(sb, "failed to get anon inode for %s", name); +- ret = -ENOMEM; +- goto err_cookie; +- } +- +- inode->i_size = OFFSET_MAX; +- inode->i_mapping->a_ops = &erofs_fscache_meta_aops; +- mapping_set_gfp_mask(inode->i_mapping, GFP_KERNEL); +- inode->i_blkbits = EROFS_SB(sb)->blkszbits; +- inode->i_private = ctx; +- +- ctx->cookie = cookie; +- ctx->inode = inode; +- return ctx; +- +-err_cookie: +- fscache_unuse_cookie(cookie, NULL, NULL); +- fscache_relinquish_cookie(cookie, false); +-err: +- kfree(ctx); +- return ERR_PTR(ret); +-} +- +-static void erofs_fscache_relinquish_cookie(struct erofs_fscache *ctx) +-{ +- fscache_unuse_cookie(ctx->cookie, NULL, NULL); +- fscache_relinquish_cookie(ctx->cookie, false); +- iput(ctx->inode); +- kfree(ctx->name); +- kfree(ctx); +-} +- +-static struct erofs_fscache *erofs_domain_init_cookie(struct super_block *sb, +- char *name, unsigned int flags) +-{ +- struct erofs_fscache *ctx; +- struct erofs_domain *domain = EROFS_SB(sb)->domain; +- +- ctx = erofs_fscache_acquire_cookie(sb, name, flags); +- if (IS_ERR(ctx)) +- return ctx; +- +- ctx->name = kstrdup(name, GFP_KERNEL); +- if (!ctx->name) { +- erofs_fscache_relinquish_cookie(ctx); +- return ERR_PTR(-ENOMEM); +- } +- +- refcount_inc(&domain->ref); +- ctx->domain = domain; +- list_add(&ctx->node, &erofs_domain_cookies_list); +- return ctx; +-} +- +-static struct erofs_fscache *erofs_domain_register_cookie(struct super_block *sb, +- char *name, unsigned int flags) +-{ +- struct erofs_fscache *ctx; +- struct erofs_domain *domain = EROFS_SB(sb)->domain; +- +- flags |= EROFS_REG_COOKIE_SHARE; +- mutex_lock(&erofs_domain_cookies_lock); +- list_for_each_entry(ctx, &erofs_domain_cookies_list, node) { +- if (ctx->domain != domain || strcmp(ctx->name, name)) +- continue; +- if (!(flags & EROFS_REG_COOKIE_NEED_NOEXIST)) { +- refcount_inc(&ctx->ref); +- } else { +- erofs_err(sb, "%s already exists in domain %s", name, +- domain->domain_id); +- ctx = ERR_PTR(-EEXIST); +- } +- mutex_unlock(&erofs_domain_cookies_lock); +- return ctx; +- } +- ctx = erofs_domain_init_cookie(sb, name, flags); +- mutex_unlock(&erofs_domain_cookies_lock); +- return ctx; +-} +- +-struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, +- char *name, +- unsigned int flags) +-{ +- if (EROFS_SB(sb)->domain_id) +- return erofs_domain_register_cookie(sb, name, flags); +- return erofs_fscache_acquire_cookie(sb, name, flags); +-} +- +-void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx) +-{ +- struct erofs_domain *domain = NULL; +- +- if (!ctx) +- return; +- if (!ctx->domain) +- return erofs_fscache_relinquish_cookie(ctx); +- +- mutex_lock(&erofs_domain_cookies_lock); +- if (refcount_dec_and_test(&ctx->ref)) { +- domain = ctx->domain; +- list_del(&ctx->node); +- erofs_fscache_relinquish_cookie(ctx); +- } +- mutex_unlock(&erofs_domain_cookies_lock); +- if (domain) +- erofs_fscache_domain_put(domain); +-} +- +-int erofs_fscache_register_fs(struct super_block *sb) +-{ +- int ret; +- struct erofs_sb_info *sbi = EROFS_SB(sb); +- struct erofs_fscache *fscache; +- unsigned int flags = 0; +- +- if (sbi->domain_id) +- ret = erofs_fscache_register_domain(sb); +- else +- ret = erofs_fscache_register_volume(sb); +- if (ret) +- return ret; +- +- /* +- * When shared domain is enabled, using NEED_NOEXIST to guarantee +- * the primary data blob (aka fsid) is unique in the shared domain. +- * +- * For non-shared-domain case, fscache_acquire_volume() invoked by +- * erofs_fscache_register_volume() has already guaranteed +- * the uniqueness of primary data blob. +- * +- * Acquired domain/volume will be relinquished in kill_sb() on error. +- */ +- if (sbi->domain_id) +- flags |= EROFS_REG_COOKIE_NEED_NOEXIST; +- fscache = erofs_fscache_register_cookie(sb, sbi->fsid, flags); +- if (IS_ERR(fscache)) +- return PTR_ERR(fscache); +- +- sbi->dif0.fscache = fscache; +- return 0; +-} +- +-void erofs_fscache_unregister_fs(struct super_block *sb) +-{ +- struct erofs_sb_info *sbi = EROFS_SB(sb); +- +- erofs_fscache_unregister_cookie(sbi->dif0.fscache); +- +- if (sbi->domain) +- erofs_fscache_domain_put(sbi->domain); +- else +- fscache_relinquish_volume(sbi->volume, NULL, false); +- +- sbi->dif0.fscache = NULL; +- sbi->volume = NULL; +- sbi->domain = NULL; +-} +diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c +index e0c47da4f09e0..45afe5c50de83 100644 +--- a/fs/erofs/inode.c ++++ b/fs/erofs/inode.c +@@ -256,7 +256,7 @@ static int erofs_fill_inode(struct inode *inode) + } + + mapping_set_large_folios(inode->i_mapping); +- aops = erofs_get_aops(inode, false); ++ aops = erofs_get_aops(inode); + if (IS_ERR(aops)) + return PTR_ERR(aops); + inode->i_mapping->a_ops = aops; +diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h +index 9e2ae7b619773..580f8d9f14e7f 100644 +--- a/fs/erofs/internal.h ++++ b/fs/erofs/internal.h +@@ -43,7 +43,6 @@ typedef u64 erofs_blk_t; + + struct erofs_device_info { + char *path; +- struct erofs_fscache *fscache; + struct file *file; + struct dax_device *dax_dev; + u64 fsoff, dax_part_off; +@@ -80,24 +79,6 @@ struct erofs_sb_lz4_info { + u16 max_pclusterblks; + }; + +-struct erofs_domain { +- refcount_t ref; +- struct list_head list; +- struct fscache_volume *volume; +- char *domain_id; +-}; +- +-struct erofs_fscache { +- struct fscache_cookie *cookie; +- struct inode *inode; /* anonymous inode for the blob */ +- +- /* used for share domain mode */ +- struct erofs_domain *domain; +- struct list_head node; +- refcount_t ref; +- char *name; +-}; +- + struct erofs_xattr_prefix_item { + struct erofs_xattr_long_prefix *prefix; + u8 infix_len; +@@ -162,10 +143,6 @@ struct erofs_sb_info { + struct completion s_kobj_unregister; + erofs_off_t dir_ra_bytes; + +- /* fscache support */ +- struct fscache_volume *volume; +- struct erofs_domain *domain; +- char *fsid; + char *domain_id; + }; + +@@ -191,12 +168,6 @@ static inline bool erofs_is_fileio_mode(struct erofs_sb_info *sbi) + + extern struct file_system_type erofs_anon_fs_type; + +-static inline bool erofs_is_fscache_mode(struct super_block *sb) +-{ +- return IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && +- !erofs_is_fileio_mode(EROFS_SB(sb)) && !sb->s_bdev; +-} +- + enum { + EROFS_ZIP_CACHE_DISABLED, + EROFS_ZIP_CACHE_READAHEAD, +@@ -413,11 +384,9 @@ struct erofs_map_dev { + }; + + extern const struct super_operations erofs_sops; +- + extern const struct address_space_operations erofs_aops; + extern const struct address_space_operations erofs_fileio_aops; + extern const struct address_space_operations z_erofs_aops; +-extern const struct address_space_operations erofs_fscache_access_aops; + + extern const struct inode_operations erofs_generic_iops; + extern const struct inode_operations erofs_symlink_iops; +@@ -430,10 +399,6 @@ extern const struct file_operations erofs_ishare_fops; + + extern const struct iomap_ops z_erofs_iomap_report_ops; + +-/* flags for erofs_fscache_register_cookie() */ +-#define EROFS_REG_COOKIE_SHARE 0x0001 +-#define EROFS_REG_COOKIE_NEED_NOEXIST 0x0002 +- + void *erofs_read_metadata(struct super_block *sb, struct erofs_buf *buf, + erofs_off_t *offset, int *lengthp); + void erofs_unmap_metabuf(struct erofs_buf *buf); +@@ -473,7 +438,7 @@ static inline void *erofs_vm_map_ram(struct page **pages, unsigned int count) + } + + static inline const struct address_space_operations * +-erofs_get_aops(struct inode *realinode, bool no_fscache) ++erofs_get_aops(struct inode *realinode) + { + if (erofs_inode_is_data_compressed(EROFS_I(realinode)->datalayout)) { + if (!IS_ENABLED(CONFIG_EROFS_FS_ZIP)) +@@ -483,9 +448,6 @@ erofs_get_aops(struct inode *realinode, bool no_fscache) + "EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!"); + return &z_erofs_aops; + } +- if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && !no_fscache && +- erofs_is_fscache_mode(realinode->i_sb)) +- return &erofs_fscache_access_aops; + if (IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) && + erofs_is_fileio_mode(EROFS_SB(realinode->i_sb))) + return &erofs_fileio_aops; +@@ -548,36 +510,6 @@ static inline struct bio *erofs_fileio_bio_alloc(struct erofs_map_dev *mdev) { r + static inline void erofs_fileio_submit_bio(struct bio *bio) {} + #endif + +-#ifdef CONFIG_EROFS_FS_ONDEMAND +-int erofs_fscache_register_fs(struct super_block *sb); +-void erofs_fscache_unregister_fs(struct super_block *sb); +- +-struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, +- char *name, unsigned int flags); +-void erofs_fscache_unregister_cookie(struct erofs_fscache *fscache); +-struct bio *erofs_fscache_bio_alloc(struct erofs_map_dev *mdev); +-void erofs_fscache_submit_bio(struct bio *bio); +-#else +-static inline int erofs_fscache_register_fs(struct super_block *sb) +-{ +- return -EOPNOTSUPP; +-} +-static inline void erofs_fscache_unregister_fs(struct super_block *sb) {} +- +-static inline +-struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, +- char *name, unsigned int flags) +-{ +- return ERR_PTR(-EOPNOTSUPP); +-} +- +-static inline void erofs_fscache_unregister_cookie(struct erofs_fscache *fscache) +-{ +-} +-static inline struct bio *erofs_fscache_bio_alloc(struct erofs_map_dev *mdev) { return NULL; } +-static inline void erofs_fscache_submit_bio(struct bio *bio) {} +-#endif +- + #ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE + int __init erofs_init_ishare(void); + void erofs_exit_ishare(void); +diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c +index 35cbd0bc04d7d..0868c12fc15b4 100644 +--- a/fs/erofs/ishare.c ++++ b/fs/erofs/ishare.c +@@ -45,7 +45,7 @@ bool erofs_ishare_fill_inode(struct inode *inode) + struct erofs_inode_fingerprint fp; + struct inode *si; + +- aops = erofs_get_aops(inode, true); ++ aops = erofs_get_aops(inode); + if (IS_ERR(aops)) + return false; + if (erofs_xattr_fill_inode_fingerprint(&fp, inode, sbi->domain_id)) +diff --git a/fs/erofs/super.c b/fs/erofs/super.c +index 579443e6acfeb..86fa5c6a0c708 100644 +--- a/fs/erofs/super.c ++++ b/fs/erofs/super.c +@@ -126,7 +126,6 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb, + struct erofs_device_info *dif, erofs_off_t *pos) + { + struct erofs_sb_info *sbi = EROFS_SB(sb); +- struct erofs_fscache *fscache; + struct erofs_deviceslot *dis; + struct file *file; + bool _48bit; +@@ -145,12 +144,7 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb, + return -ENOMEM; + } + +- if (erofs_is_fscache_mode(sb)) { +- fscache = erofs_fscache_register_cookie(sb, dif->path, 0); +- if (IS_ERR(fscache)) +- return PTR_ERR(fscache); +- dif->fscache = fscache; +- } else if (!sbi->devs->flatdev) { ++ if (!sbi->devs->flatdev) { + file = erofs_is_fileio_mode(sbi) ? + filp_open(dif->path, O_RDONLY | O_LARGEFILE, 0) : + bdev_file_open_by_path(dif->path, +@@ -216,7 +210,7 @@ static int erofs_scan_devices(struct super_block *sb, + if (!ondisk_extradevs) + return 0; + +- if (!sbi->devs->extra_devices && !erofs_is_fscache_mode(sb)) ++ if (!sbi->devs->extra_devices) + sbi->devs->flatdev = true; + + sbi->device_id_mask = roundup_pow_of_two(ondisk_extradevs + 1) - 1; +@@ -372,8 +366,6 @@ static int erofs_read_superblock(struct super_block *sb) + erofs_info(sb, "EXPERIMENTAL 48-bit layout support in use. Use at your own risk!"); + if (erofs_sb_has_metabox(sbi)) + erofs_info(sb, "EXPERIMENTAL metadata compression support in use. Use at your own risk!"); +- if (erofs_is_fscache_mode(sb)) +- erofs_info(sb, "[deprecated] fscache-based on-demand read feature in use. Use at your own risk!"); + out: + erofs_put_metabuf(&buf); + return ret; +@@ -393,8 +385,7 @@ static void erofs_default_options(struct erofs_sb_info *sbi) + + enum { + Opt_user_xattr, Opt_acl, Opt_cache_strategy, Opt_dax, Opt_dax_enum, +- Opt_device, Opt_fsid, Opt_domain_id, Opt_directio, Opt_fsoffset, +- Opt_inode_share, ++ Opt_device, Opt_domain_id, Opt_directio, Opt_fsoffset, Opt_inode_share, + }; + + static const struct constant_table erofs_param_cache_strategy[] = { +@@ -418,7 +409,6 @@ static const struct fs_parameter_spec erofs_fs_parameters[] = { + fsparam_flag("dax", Opt_dax), + fsparam_enum("dax", Opt_dax_enum, erofs_dax_param_enums), + fsparam_string("device", Opt_device), +- fsparam_string("fsid", Opt_fsid), + fsparam_string("domain_id", Opt_domain_id), + fsparam_flag_no("directio", Opt_directio), + fsparam_u64("fsoffset", Opt_fsoffset), +@@ -509,25 +499,14 @@ static int erofs_fc_parse_param(struct fs_context *fc, + } + ++sbi->devs->extra_devices; + break; +-#ifdef CONFIG_EROFS_FS_ONDEMAND +- case Opt_fsid: +- kfree(sbi->fsid); +- sbi->fsid = kstrdup(param->string, GFP_KERNEL); +- if (!sbi->fsid) +- return -ENOMEM; +- break; +-#endif +-#if defined(CONFIG_EROFS_FS_ONDEMAND) || defined(CONFIG_EROFS_FS_PAGE_CACHE_SHARE) + case Opt_domain_id: +- kfree_sensitive(sbi->domain_id); +- sbi->domain_id = no_free_ptr(param->string); +- break; +-#else +- case Opt_fsid: +- case Opt_domain_id: +- errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name); ++ if (!IS_ENABLED(CONFIG_EROFS_FS_PAGE_CACHE_SHARE)) { ++ errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name); ++ } else { ++ kfree_sensitive(sbi->domain_id); ++ sbi->domain_id = no_free_ptr(param->string); ++ } + break; +-#endif + case Opt_directio: + if (!IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE)) + errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name); +@@ -620,12 +599,7 @@ static void erofs_set_sysfs_name(struct super_block *sb) + { + struct erofs_sb_info *sbi = EROFS_SB(sb); + +- if (sbi->domain_id && sbi->fsid) +- super_set_sysfs_name_generic(sb, "%s,%s", sbi->domain_id, +- sbi->fsid); +- else if (sbi->fsid) +- super_set_sysfs_name_generic(sb, "%s", sbi->fsid); +- else if (erofs_is_fileio_mode(sbi)) ++ if (erofs_is_fileio_mode(sbi)) + super_set_sysfs_name_generic(sb, "%s", + bdi_dev_name(sb->s_bdi)); + else +@@ -680,11 +654,6 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) + sb->s_blocksize = PAGE_SIZE; + sb->s_blocksize_bits = PAGE_SHIFT; + +- if (erofs_is_fscache_mode(sb)) { +- err = erofs_fscache_register_fs(sb); +- if (err) +- return err; +- } + err = super_setup_bdi(sb); + if (err) + return err; +@@ -703,11 +672,6 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) + return err; + + if (sb->s_blocksize_bits != sbi->blkszbits) { +- if (erofs_is_fscache_mode(sb)) { +- errorfc(fc, "unsupported blksize for fscache mode"); +- return -EINVAL; +- } +- + if (erofs_is_fileio_mode(sbi)) { + sb->s_blocksize = 1 << sbi->blkszbits; + sb->s_blocksize_bits = sbi->blkszbits; +@@ -716,14 +680,9 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) + return -EINVAL; + } + } +- +- if (sbi->dif0.fsoff) { +- if (sbi->dif0.fsoff & (sb->s_blocksize - 1)) +- return invalfc(fc, "fsoffset %llu is not aligned to block size %lu", +- sbi->dif0.fsoff, sb->s_blocksize); +- if (erofs_is_fscache_mode(sb)) +- return invalfc(fc, "cannot use fsoffset in fscache mode"); +- } ++ if (sbi->dif0.fsoff & (sb->s_blocksize - 1)) ++ return invalfc(fc, "fsoffset %llu is not aligned to block size %lu", ++ sbi->dif0.fsoff, sb->s_blocksize); + + if (test_opt(&sbi->opt, DAX_ALWAYS) && sbi->blkszbits != PAGE_SHIFT) { + erofs_info(sb, "unsupported blocksize for DAX"); +@@ -793,16 +752,13 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) + + static int erofs_fc_get_tree(struct fs_context *fc) + { +- struct erofs_sb_info *sbi = fc->s_fs_info; + int ret; + +- if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid) +- return get_tree_nodev(fc, erofs_fc_fill_super); +- + ret = get_tree_bdev_flags(fc, erofs_fc_fill_super, + IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) ? + GET_TREE_BDEV_QUIET_LOOKUP : 0); + if (IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) && ret == -ENOTBLK) { ++ struct erofs_sb_info *sbi = fc->s_fs_info; + struct file *file; + + if (!fc->source) +@@ -827,8 +783,8 @@ static int erofs_fc_reconfigure(struct fs_context *fc) + + DBG_BUGON(!sb_rdonly(sb)); + +- if (new_sbi->fsid || new_sbi->domain_id) +- erofs_info(sb, "ignoring reconfiguration for fsid|domain_id."); ++ if (new_sbi->domain_id) ++ erofs_info(sb, "ignoring reconfiguration for domain_id."); + + if (test_opt(&new_sbi->opt, POSIX_ACL)) + fc->sb_flags |= SB_POSIXACL; +@@ -848,8 +804,6 @@ static int erofs_release_device_info(int id, void *ptr, void *data) + fs_put_dax(dif->dax_dev, NULL); + if (dif->file) + fput(dif->file); +- erofs_fscache_unregister_cookie(dif->fscache); +- dif->fscache = NULL; + kfree(dif->path); + kfree(dif); + return 0; +@@ -867,7 +821,6 @@ static void erofs_free_dev_context(struct erofs_dev_context *devs) + static void erofs_sb_free(struct erofs_sb_info *sbi) + { + erofs_free_dev_context(sbi->devs); +- kfree(sbi->fsid); + kfree_sensitive(sbi->domain_id); + if (sbi->dif0.file) + fput(sbi->dif0.file); +@@ -928,14 +881,12 @@ static void erofs_kill_sb(struct super_block *sb) + { + struct erofs_sb_info *sbi = EROFS_SB(sb); + +- if ((IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && sbi->fsid) || +- sbi->dif0.file) ++ if (sbi->dif0.file) + kill_anon_super(sb); + else + kill_block_super(sb); + erofs_drop_internal_inodes(sbi); + fs_put_dax(sbi->dif0.dax_dev, NULL); +- erofs_fscache_unregister_fs(sb); + erofs_sb_free(sbi); + sb->s_fs_info = NULL; + } +@@ -950,7 +901,6 @@ static void erofs_put_super(struct super_block *sb) + erofs_drop_internal_inodes(sbi); + erofs_free_dev_context(sbi->devs); + sbi->devs = NULL; +- erofs_fscache_unregister_fs(sb); + } + + static struct file_system_type erofs_fs_type = { +@@ -962,14 +912,12 @@ static struct file_system_type erofs_fs_type = { + }; + MODULE_ALIAS_FS("erofs"); + +-#if defined(CONFIG_EROFS_FS_ONDEMAND) || defined(CONFIG_EROFS_FS_PAGE_CACHE_SHARE) ++#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE + static void erofs_free_anon_inode(struct inode *inode) + { + struct erofs_inode *vi = EROFS_I(inode); + +-#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE + kfree(vi->fingerprint.opaque); +-#endif + kmem_cache_free(erofs_inode_cachep, vi); + } + +@@ -1099,12 +1047,6 @@ static int erofs_show_options(struct seq_file *seq, struct dentry *root) + seq_puts(seq, ",dax=never"); + if (erofs_is_fileio_mode(sbi) && test_opt(opt, DIRECT_IO)) + seq_puts(seq, ",directio"); +- if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND)) { +- if (sbi->fsid) +- seq_printf(seq, ",fsid=%s", sbi->fsid); +- if (sbi->domain_id) +- seq_printf(seq, ",domain_id=%s", sbi->domain_id); +- } + if (sbi->dif0.fsoff) + seq_printf(seq, ",fsoffset=%llu", sbi->dif0.fsoff); + if (test_opt(opt, INODE_SHARE)) +diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c +index c6240dccbb0f0..5fa6651147eaa 100644 +--- a/fs/erofs/zdata.c ++++ b/fs/erofs/zdata.c +@@ -1714,8 +1714,6 @@ static void z_erofs_submit_queue(struct z_erofs_frontend *f, + drain_io: + if (erofs_is_fileio_mode(EROFS_SB(sb))) + erofs_fileio_submit_bio(bio); +- else if (erofs_is_fscache_mode(sb)) +- erofs_fscache_submit_bio(bio); + else + submit_bio(bio); + +@@ -1744,8 +1742,6 @@ static void z_erofs_submit_queue(struct z_erofs_frontend *f, + if (!bio) { + if (erofs_is_fileio_mode(EROFS_SB(sb))) + bio = erofs_fileio_bio_alloc(&mdev); +- else if (erofs_is_fscache_mode(sb)) +- bio = erofs_fscache_bio_alloc(&mdev); + else + bio = bio_alloc(mdev.m_bdev, BIO_MAX_VECS, + REQ_OP_READ, GFP_NOIO); +@@ -1774,8 +1770,6 @@ static void z_erofs_submit_queue(struct z_erofs_frontend *f, + if (bio) { + if (erofs_is_fileio_mode(EROFS_SB(sb))) + erofs_fileio_submit_bio(bio); +- else if (erofs_is_fscache_mode(sb)) +- erofs_fscache_submit_bio(bio); + else + submit_bio(bio); + } +-- +2.53.0 + diff --git a/queue-7.1/ethtool-embed-fec-hist-ranges-as-buffer-in-struct.patch b/queue-7.1/ethtool-embed-fec-hist-ranges-as-buffer-in-struct.patch new file mode 100644 index 0000000000..89cddaf95f --- /dev/null +++ b/queue-7.1/ethtool-embed-fec-hist-ranges-as-buffer-in-struct.patch @@ -0,0 +1,147 @@ +From 47723c3eef971d69af7904cd0e1b3d3282f4f1c0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 21:13:42 -0700 +Subject: ethtool: Embed FEC hist ranges as buffer in struct + +From: Eric Joyner + +[ Upstream commit 97ac08560d236ca17f6606d9e671118e5eae5721 ] + +When a driver's .get_fec_stats() handler is called and the driver +supports FEC histogram stats, the driver supplies the histogram bin +ranges via a pointer. This pointer is assigned while under the netdev +ops lock in fec_prepare_data(), but the actual data is only read after +the lock is released; so this allows the driver to change the ranges +(e.g. from another .get_fec_stats() call) while the current call chain +is reading them in fec_fill_reply(). + +Fix this by adding an ethtool core-owned buffer, ranges_buf, to struct +ethtool_fec_hist. Drivers whose ranges are built dynamically (currently +just mlx5) fill ranges_buf and then point the existing ranges pointer at +it, giving ethtool a consistent copy that stays valid after the netdev +ops lock is dropped and later in fec_fill_reply(). Drivers whose ranges +are compile-time constants (bnxt, netdevsim) are unaffected by the +potential race and keep setting the existing ranges pointer to their +constant array, without making copies. + +Fixes: cc2f08129925 ("ethtool: add FEC bins histogram report") +Signed-off-by: Eric Joyner +Reviewed-by: Vadim Fedorenko +Link: https://patch.msgid.link/20260723041342.39238-1-eric.joyner@amd.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mellanox/mlx5/core/en.h | 1 - + .../net/ethernet/mellanox/mlx5/core/en_main.c | 7 ------- + .../ethernet/mellanox/mlx5/core/en_stats.c | 19 +++++++++---------- + include/linux/ethtool.h | 1 + + 4 files changed, 10 insertions(+), 18 deletions(-) + +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h +index d507289096c20..6867a5aed42c0 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h +@@ -984,7 +984,6 @@ struct mlx5e_priv { + struct mlx5e_mqprio_rl *mqprio_rl; + struct dentry *dfs_root; + struct mlx5_devcom_comp_dev *devcom; +- struct ethtool_fec_hist_range *fec_ranges; + }; + + static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv) +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index fd442109aea8c..7d1063c7bf649 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -6374,14 +6374,8 @@ int mlx5e_priv_init(struct mlx5e_priv *priv, + if (!priv->channel_stats) + goto err_free_tx_rates; + +- priv->fec_ranges = kzalloc_objs(*priv->fec_ranges, ETHTOOL_FEC_HIST_MAX); +- if (!priv->fec_ranges) +- goto err_free_channel_stats; +- + return 0; + +-err_free_channel_stats: +- kfree(priv->channel_stats); + err_free_tx_rates: + kfree(priv->tx_rates); + err_free_txq2sq_stats: +@@ -6406,7 +6400,6 @@ void mlx5e_priv_cleanup(struct mlx5e_priv *priv) + if (!priv->mdev) + return; + +- kfree(priv->fec_ranges); + for (i = 0; i < priv->stats_nch; i++) + kvfree(priv->channel_stats[i]); + kfree(priv->channel_stats); +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +index 8632b73179cbc..bba51e198d731 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +@@ -1551,7 +1551,7 @@ static bool fec_rs_validate_hist_type(int mode, int hist_type) + + static u8 + fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode, +- const struct ethtool_fec_hist_range **ranges) ++ struct ethtool_fec_hist_range *ranges) + { + struct mlx5_core_dev *mdev = priv->mdev; + u32 out[MLX5_ST_SZ_DW(pphcr_reg)] = {0}; +@@ -1559,8 +1559,6 @@ fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode, + int sz = MLX5_ST_SZ_BYTES(pphcr_reg); + u8 hist_type, num_of_bins; + +- memset(priv->fec_ranges, 0, +- ETHTOOL_FEC_HIST_MAX * sizeof(*priv->fec_ranges)); + MLX5_SET(pphcr_reg, in, local_port, 1); + if (mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPHCR, 0, 0)) + return 0; +@@ -1576,12 +1574,11 @@ fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode, + for (int i = 0; i < num_of_bins; i++) { + void *bin_range = MLX5_ADDR_OF(pphcr_reg, out, bin_range[i]); + +- priv->fec_ranges[i].high = MLX5_GET(bin_range_layout, bin_range, +- high_val); +- priv->fec_ranges[i].low = MLX5_GET(bin_range_layout, bin_range, +- low_val); ++ ranges[i].high = MLX5_GET(bin_range_layout, bin_range, ++ high_val); ++ ranges[i].low = MLX5_GET(bin_range_layout, bin_range, ++ low_val); + } +- *ranges = priv->fec_ranges; + + return num_of_bins; + } +@@ -1623,10 +1620,12 @@ static void fec_set_histograms_stats(struct mlx5e_priv *priv, int mode, + case MLX5E_FEC_LLRS_272_257_1: + case MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD: + num_of_bins = +- fec_rs_histogram_fill_ranges(priv, mode, &hist->ranges); +- if (num_of_bins) ++ fec_rs_histogram_fill_ranges(priv, mode, hist->ranges_buf); ++ if (num_of_bins) { ++ hist->ranges = hist->ranges_buf; + return fec_rs_histogram_fill_stats(priv, num_of_bins, + hist); ++ } + break; + default: + return; +diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h +index 1cb0740ba3310..091d382f0a2d3 100644 +--- a/include/linux/ethtool.h ++++ b/include/linux/ethtool.h +@@ -560,6 +560,7 @@ struct ethtool_fec_hist { + u64 per_lane[ETHTOOL_MAX_LANES]; + } values[ETHTOOL_FEC_HIST_MAX]; + const struct ethtool_fec_hist_range *ranges; ++ struct ethtool_fec_hist_range ranges_buf[ETHTOOL_FEC_HIST_MAX]; + }; + /** + * struct ethtool_fec_stats - statistics for IEEE 802.3 FEC +-- +2.53.0 + diff --git a/queue-7.1/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch b/queue-7.1/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch new file mode 100644 index 0000000000..f0d94b9a1e --- /dev/null +++ b/queue-7.1/forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch @@ -0,0 +1,47 @@ +From abb54503a418273ce0e6033ced93bd075c4764c4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 17:26:37 +0800 +Subject: forcedeth: fix UAF of txrx_stats in nv_remove + +From: Chenguang Zhao + +[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ] + +nv_remove() frees the per-CPU txrx_stats before unregister_netdev(). +Until unregister completes, ndo_get_stats64, the NAPI/xmit data path, +and nv_close()/drain may still access txrx_stats, leading to a +use-after-free. + +Free the stats only after unregister_netdev(). + +Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Reviewed-by: Zhu Yanjun +Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/nvidia/forcedeth.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c +index 5b0435d7bc395..58d3e55def486 100644 +--- a/drivers/net/ethernet/nvidia/forcedeth.c ++++ b/drivers/net/ethernet/nvidia/forcedeth.c +@@ -6187,10 +6187,10 @@ static void nv_remove(struct pci_dev *pci_dev) + struct net_device *dev = pci_get_drvdata(pci_dev); + struct fe_priv *np = netdev_priv(dev); + +- free_percpu(np->txrx_stats); +- + unregister_netdev(dev); + ++ free_percpu(np->txrx_stats); ++ + nv_restore_mac_addr(pci_dev); + + /* restore any phy related changes */ +-- +2.53.0 + diff --git a/queue-7.1/fprobe-fix-module-reference-count-leak-on-error-in-r.patch b/queue-7.1/fprobe-fix-module-reference-count-leak-on-error-in-r.patch new file mode 100644 index 0000000000..a4f8d165a2 --- /dev/null +++ b/queue-7.1/fprobe-fix-module-reference-count-leak-on-error-in-r.patch @@ -0,0 +1,48 @@ +From d26d8ce659b53dc06a32d5dc7a6e90c4985d2f0b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Jul 2026 08:27:33 +0900 +Subject: fprobe: Fix module reference count leak on error in register_fprobe() + +From: Masami Hiramatsu (Google) + +[ Upstream commit 8cf2f40ceb85047ad8a84dffae3bebc9fed18216 ] + +In register_fprobe(), get_ips_from_filter() resolves target function +addresses and increments module reference counts via try_module_get() for +symbols in kernel modules. If get_ips_from_filter() fails on the second +pass and returns an error, register_fprobe() returned directly without +releasing module references acquired up to that point. + +Fix this by ensuring the cleanup loop executing module_put() runs even when +get_ips_from_filter() returns a negative error. + +Link: https://lore.kernel.org/all/178528125360.101985.4144133640239273153.stgit@devnote2/ + +Fixes: d24fa977eec5 ("tracing: fprobe: Fix to lock module while registering fprobe") +Assisted-by: Antigravity:gemini-3.6-flash +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Sasha Levin +--- + kernel/trace/fprobe.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c +index f215990b90613..f681015413b83 100644 +--- a/kernel/trace/fprobe.c ++++ b/kernel/trace/fprobe.c +@@ -961,10 +961,8 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter + return -ENOMEM; + + ret = get_ips_from_filter(filter, notfilter, addrs, mods, num); +- if (ret < 0) +- return ret; +- +- ret = register_fprobe_ips(fp, addrs, ret); ++ if (ret >= 0) ++ ret = register_fprobe_ips(fp, addrs, ret); + + for (int i = 0; i < num; i++) { + if (mods[i]) +-- +2.53.0 + diff --git a/queue-7.1/gpio-gpio-by-pinctrl-apply-initial-value-in-directio.patch b/queue-7.1/gpio-gpio-by-pinctrl-apply-initial-value-in-directio.patch new file mode 100644 index 0000000000..b13d41861e --- /dev/null +++ b/queue-7.1/gpio-gpio-by-pinctrl-apply-initial-value-in-directio.patch @@ -0,0 +1,63 @@ +From 983162d1ae405a51c6f5a177af70320dab9613da Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 09:42:28 -0700 +Subject: gpio: gpio-by-pinctrl: Apply initial value in direction output + wrapper + +From: Alex Tran + +[ Upstream commit 67ff4bf723c8bd1f1b10450fa3e8f55762418104 ] + +After successfully configuring gpio pin as output, set the +requested initial output value via the existing gpio set +wrapper, so that the pin is not left at its previous level. + +Fixes: 7671f4949a6c ("gpio: gpio-by-pinctrl: add pinctrl based generic GPIO driver") +Signed-off-by: Alex Tran +Reviewed-by: Linus Walleij +Link: https://patch.msgid.link/20260724-gpio-pinctrl-output-set-val-v2-1-cad55d025636@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-by-pinctrl.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +diff --git a/drivers/gpio/gpio-by-pinctrl.c b/drivers/gpio/gpio-by-pinctrl.c +index ddfdc479d38a8..b3b3c11376172 100644 +--- a/drivers/gpio/gpio-by-pinctrl.c ++++ b/drivers/gpio/gpio-by-pinctrl.c +@@ -28,12 +28,6 @@ static int pin_control_gpio_get_direction(struct gpio_chip *gc, unsigned int off + return GPIO_LINE_DIRECTION_IN; + } + +-static int pin_control_gpio_direction_output(struct gpio_chip *chip, +- unsigned int offset, int val) +-{ +- return pinctrl_gpio_direction_output(chip, offset); +-} +- + static int pin_control_gpio_get(struct gpio_chip *chip, unsigned int offset) + { + unsigned long config; +@@ -56,6 +50,18 @@ static int pin_control_gpio_set(struct gpio_chip *chip, unsigned int offset, + return pinctrl_gpio_set_config(chip, offset, config); + } + ++static int pin_control_gpio_direction_output(struct gpio_chip *chip, ++ unsigned int offset, int val) ++{ ++ int ret; ++ ++ ret = pinctrl_gpio_direction_output(chip, offset); ++ if (ret) ++ return ret; ++ ++ return pin_control_gpio_set(chip, offset, val); ++} ++ + static int pin_control_gpio_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +-- +2.53.0 + diff --git a/queue-7.1/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch b/queue-7.1/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch new file mode 100644 index 0000000000..16a46aeafe --- /dev/null +++ b/queue-7.1/gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch @@ -0,0 +1,55 @@ +From 8c3d87466efaed739b0b93492e0fd130a505f5ec Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 13:23:08 +0530 +Subject: gpio: sloppy-logic-analyzer: Fix memory leak in gpio_la_poll_probe() + +From: Abdun Nihaal + +[ Upstream commit 7a7baebd9f23ba4f24796775472b2fd00dcd95d9 ] + +The memory allocated for priv->blob.data is not freed in the error paths +that follow the fops_buf_size_set() call in gpio_la_poll_probe(), as +well as in the remove function. Fix that by using device managed action +to free the memory on remove. + +Fixes: 7828b7bbbf20 ("gpio: add sloppy logic analyzer using polling") +Signed-off-by: Abdun Nihaal +Reviewed-by: Wolfram Sang +Link: https://patch.msgid.link/20260715075311.527753-1-nihaal@cse.iitm.ac.in +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/gpio/gpio-sloppy-logic-analyzer.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c +index 969dddd3d6faf..0f4a6228a7488 100644 +--- a/drivers/gpio/gpio-sloppy-logic-analyzer.c ++++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c +@@ -161,6 +161,13 @@ static int fops_buf_size_get(void *data, u64 *val) + return 0; + } + ++static void fops_buf_release(void *data) ++{ ++ struct gpio_la_poll_priv *priv = data; ++ ++ vfree(priv->blob.data); ++} ++ + static int fops_buf_size_set(void *data, u64 val) + { + struct gpio_la_poll_priv *priv = data; +@@ -239,6 +246,9 @@ static int gpio_la_poll_probe(struct platform_device *pdev) + return ret; + + fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE); ++ ret = devm_add_action_or_reset(dev, fops_buf_release, priv); ++ if (ret) ++ return ret; + + priv->descs = devm_gpiod_get_array(dev, "probe", GPIOD_IN); + if (IS_ERR(priv->descs)) +-- +2.53.0 + diff --git a/queue-7.1/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch b/queue-7.1/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch new file mode 100644 index 0000000000..34b852b65b --- /dev/null +++ b/queue-7.1/hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch @@ -0,0 +1,51 @@ +From 5e9064825c76c2d287c39df92a010fc9baa062e3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:19 -0300 +Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread + +From: Luiz Angelo Daros de Luca + +[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ] + +When userspace configures 'auto_update_interval' to 0 via sysfs, the +background kthread executes schedule_timeout_interruptible(0), which +returns immediately. + +If 'num_temp_sensors' is concurrently or previously set to 0, the +msleep_interruptible() delay inside adt7470_read_temperatures() also +becomes 0. This combination forces the background thread into a tight, +unbounded busy-loop, hogging the CPU and flooding the I2C bus with a +continuous stream of transactions. + +Fix this vulnerability by raising the lower limit of the clamp_val in +auto_update_interval_store() from 0 to 500 milliseconds. This guarantees +a reasonable minimum sleep window between sensor updates, protecting the +system from intentional or accidental I2C bus denial of service. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org +Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 62ec68ea0a406..0b19b0925d1c7 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev, + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + +- temp = clamp_val(temp, 0, 60000); ++ temp = clamp_val(temp, 500, 60000); + + mutex_lock(&data->lock); + data->auto_update_interval = temp; +-- +2.53.0 + diff --git a/queue-7.1/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch b/queue-7.1/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch new file mode 100644 index 0000000000..c54a362884 --- /dev/null +++ b/queue-7.1/hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch @@ -0,0 +1,83 @@ +From b495e61d1c5cb59962d524a066c1e8569977d99b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:18 -0300 +Subject: hwmon: (adt7470) Fix cache updated before hardware write on I2C error + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ] + +adt7470_temp_write() and adt7470_pwm_write() update the driver's +cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing +the corresponding regmap_write(), and never check whether the write +succeeded before committing that update. If the I2C transaction fails, +the function correctly propagates the error to the caller, but the cache +silently keeps the new value, which was never actually applied to the +hardware. Subsequent reads then report a value that does not match the +device state. + +Reorder both write paths to update the cache only after a successful +regmap_write(), so the cache always reflects what was actually +written to the hardware. + +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 481d51617f4be..62ec68ea0a406 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va + switch (attr) { + case hwmon_temp_min: + mutex_lock(&data->lock); +- data->temp_min[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val); ++ if (!err) ++ data->temp_min[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_temp_max: + mutex_lock(&data->lock); +- data->temp_max[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val); ++ if (!err) ++ data->temp_max[channel] = val; + mutex_unlock(&data->lock); + break; + default: +@@ -831,9 +833,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + case hwmon_pwm_input: + val = clamp_val(val, 0, 255); + mutex_lock(&data->lock); +- data->pwm[channel] = val; + err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), +- data->pwm[channel]); ++ val); ++ if (!err) ++ data->pwm[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_pwm_enable: +@@ -847,10 +850,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val + val--; + + mutex_lock(&data->lock); +- data->pwm_automatic[channel] = val; + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel), + pwm_auto_reg_mask, + val ? pwm_auto_reg_mask : 0); ++ if (!err) ++ data->pwm_automatic[channel] = val; + mutex_unlock(&data->lock); + break; + case hwmon_pwm_freq: +-- +2.53.0 + diff --git a/queue-7.1/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch b/queue-7.1/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch new file mode 100644 index 0000000000..888dee28ec --- /dev/null +++ b/queue-7.1/hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch @@ -0,0 +1,80 @@ +From d2f4c36417bf591c232f3328f1cffda306e5001d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:23 -0300 +Subject: hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ] + +If the fan data becomes 0 between the FAN_DATA_VALID() check and the +FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash +due to a race with a concurrent update of the cached fan value. + +Fix a TOCTOU issue by reading fan data once. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/ +Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 23 ++++++++++------------- + 1 file changed, 10 insertions(+), 13 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 1fbca4869b7b6..772d2a409bb5c 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev, + static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val) + { + struct adt7470_data *data = adt7470_update_device(dev); ++ u16 fan_data; + + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (attr) { + case hwmon_fan_input: +- if (FAN_DATA_VALID(data->fan[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan[channel]); + break; + case hwmon_fan_min: +- if (FAN_DATA_VALID(data->fan_min[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan_min[channel]); + break; + case hwmon_fan_max: +- if (FAN_DATA_VALID(data->fan_max[channel])) +- *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]); +- else +- *val = 0; ++ fan_data = READ_ONCE(data->fan_max[channel]); + break; + case hwmon_fan_alarm: + *val = !!(data->alarm & FAN_ALARM_BIT(channel)); +- break; ++ return 0; + default: + return -EOPNOTSUPP; + } + ++ if (FAN_DATA_VALID(fan_data)) ++ *val = FAN_PERIOD_TO_RPM(fan_data); ++ else ++ *val = 0; ++ + return 0; + } + +-- +2.53.0 + diff --git a/queue-7.1/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch b/queue-7.1/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch new file mode 100644 index 0000000000..e7961a3537 --- /dev/null +++ b/queue-7.1/hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch @@ -0,0 +1,115 @@ +From 1e256e05a1ce75b5947fbebcf5e19d05c0d3b8a6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:17 -0300 +Subject: hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ] + +During adt7470_read_temperatures(), the driver temporarily switches +the PWM channels to manual mode, performs the temperature collection, +and then restores the original configuration registers. + +However, if an I2C transaction fails at any point after entering manual +mode, the function aborts and returns immediately. This leaves the +configuration registers un-restored, permanently trapping the fans in +manual mode. + +Introduce a recovery path to ensure that the original PWM configuration +registers are always restored, even when intermediate I2C operations +fail. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++----------- + 1 file changed, 29 insertions(+), 11 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 664349756dc2b..481d51617f4be 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in + /* Probe for temperature sensors. Assumes lock is held */ + static int adt7470_read_temperatures(struct adt7470_data *data) + { +- unsigned long res; ++ struct device *dev = regmap_get_device(data->regmap); ++ u8 pwm[ADT7470_FAN_COUNT]; + unsigned int pwm_cfg[2]; +- int err; ++ unsigned long res; ++ int err, err2; + int i; +- u8 pwm[ADT7470_FAN_COUNT]; + + /* save pwm[1-4] config register */ + err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); +@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data) + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), + ADT7470_PWM_AUTO_MASK, 0); + if (err < 0) +- return err; ++ goto out_restore; + + /* write pwm control to whatever it was */ + err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], + ADT7470_PWM_COUNT); + if (err < 0) +- return err; ++ goto out_restore; + + /* start reading temperature sensors */ + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); + if (err < 0) +- return err; ++ goto out_restore; + + /* Delay is 200ms * number of temp sensors. */ + res = msleep_interruptible((data->num_temp_sensors >= 0 ? +@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data) + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, 0); + if (err < 0) +- return err; ++ goto out_restore; + ++out_restore: + /* restore pwm[1-4] config registers */ +- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); +- if (err < 0) +- return err; +- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); ++ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); ++ if (err2 < 0) { ++ dev_warn_ratelimited(dev, ++ "failed to restore PWM{1,2} config (%d)\n", ++ err2); ++ ++ if (!err) ++ err = err2; ++ } ++ ++ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); ++ if (err2 < 0) { ++ dev_warn_ratelimited(dev, ++ "failed to restore PWM{3,4} config (%d)\n", ++ err2); ++ ++ if (!err) ++ err = err2; ++ } ++ + if (err < 0) + return err; + +-- +2.53.0 + diff --git a/queue-7.1/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch b/queue-7.1/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch new file mode 100644 index 0000000000..3f05376cd5 --- /dev/null +++ b/queue-7.1/hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch @@ -0,0 +1,57 @@ +From 9884c3f4849e2bbfb9835e43ad5944183eb9f4be Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:24 -0300 +Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ] + +In pwm_auto_temp_store(), the parsed user input was missing bounds +checks, allowing values > 0xF to overflow into the adjacent channel's +bits. Furthermore, the value was being incorrectly written to the +pwm_automatic state array instead of pwm_auto_temp. + +Fix this by rejecting values > 0xF with -EINVAL, and assigning the +value to the correct array only after a successful I2C write. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 772d2a409bb5c..c45b984c02e6b 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -1049,8 +1049,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + if (temp < 0) + return temp; + ++ if (temp > 0xF) ++ return -EINVAL; ++ + mutex_lock(&data->lock); +- data->pwm_automatic[attr->index] = temp; + + if (!(attr->index % 2)) { + mask = 0xF0; +@@ -1061,6 +1063,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev, + } + + err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); ++ if (!err) ++ data->pwm_auto_temp[attr->index] = temp; ++ + mutex_unlock(&data->lock); + + return err < 0 ? err : count; +-- +2.53.0 + diff --git a/queue-7.1/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch b/queue-7.1/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch new file mode 100644 index 0000000000..abaf65c593 --- /dev/null +++ b/queue-7.1/hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch @@ -0,0 +1,62 @@ +From 0e02b15889d2e78f613b1763c34d70007c4d25ad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:20 -0300 +Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks + +From: Luiz Angelo Daros de Luca + +[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ] + +The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are +currently defined with swapped bit values. + +According to Table 22 of the ADT7470 datasheet, the Fan Control Mode +Configuration for register 0x69 follows the exact same bit position +layout as register 0x68: +- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80 +- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40 +- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80 +- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40 + +Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40. + +This typo did not cause any functional bugs because these specific +macros are never referenced in the driver code. Instead, the driver +correctly applies the configuration by relying on the modulo parity of +the channel index (e.g., `channel % 2`) to selectively apply either +ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40). +Since the bit layout is identical between the two configuration +registers, the hardware is currently configured correctly. + +Fix the macro definitions to reflect the datasheet accurately and +prevent future bugs or confusion during code review and refactoring. +As this is a purely cosmetic fix with no functional impact, a backport +to stable kernels is not necessary. + +Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 428bd1d91e700..c6fc7d38d698c 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + #define ADT7470_PWM1_AUTO_MASK 0x80 + #define ADT7470_PWM_AUTO_MASK 0xC0 + #define ADT7470_REG_PWM34_CFG 0x69 +-#define ADT7470_PWM3_AUTO_MASK 0x40 +-#define ADT7470_PWM4_AUTO_MASK 0x80 ++#define ADT7470_PWM4_AUTO_MASK 0x40 ++#define ADT7470_PWM3_AUTO_MASK 0x80 + #define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A + #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D + #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E +-- +2.53.0 + diff --git a/queue-7.1/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch b/queue-7.1/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch new file mode 100644 index 0000000000..72543b4f83 --- /dev/null +++ b/queue-7.1/hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch @@ -0,0 +1,71 @@ +From 3b972ceb36a2c659fe13f2793021211495a41b49 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:21 -0300 +Subject: hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read() + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ] + +During the conversion the alarm callback started interpreting the +channel index as an alarm bitmask, resulting in incorrect alarm +reporting. Compute the proper alarm bit instead. + +Reported-by: sashiko-bot@kernel.org +Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org +Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 19 +++++++++++++++++-- + 1 file changed, 17 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index 0b19b0925d1c7..428bd1d91e700 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; + + #define ALARM2(x) ((x) << 8) + ++/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and ++ * 0x72, or BIT(0)..BIT(6) of data->alarm. ++ * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and ++ * 0x73, or BIT(8)..BIT(10) of data->alarm. ++ */ ++#define TEMP_ALARM_BIT(ch) ({ \ ++ typeof(ch) _ch = (ch); \ ++ (1 << (_ch < 7 ? _ch : _ch + 1)); \ ++}) ++ ++/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in ++ * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm. ++ */ ++#define FAN_ALARM_BIT(ch) (1 << (12 + (ch))) ++ + #define ADT7470_VENDOR 0x41 + #define ADT7470_DEVICE 0x70 + /* datasheet only mentions a revision 2 */ +@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va + *val = 1000 * data->temp_max[channel]; + break; + case hwmon_temp_alarm: +- *val = !!(data->alarm & channel); ++ *val = !!(data->alarm & TEMP_ALARM_BIT(channel)); + break; + default: + return -EOPNOTSUPP; +@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val + *val = 0; + break; + case hwmon_fan_alarm: +- *val = !!(data->alarm & (1 << (12 + channel))); ++ *val = !!(data->alarm & FAN_ALARM_BIT(channel)); + break; + default: + return -EOPNOTSUPP; +-- +2.53.0 + diff --git a/queue-7.1/hwmon-adt7470-use-cached-pwm-frequency-value.patch b/queue-7.1/hwmon-adt7470-use-cached-pwm-frequency-value.patch new file mode 100644 index 0000000000..8a1ec0ceb7 --- /dev/null +++ b/queue-7.1/hwmon-adt7470-use-cached-pwm-frequency-value.patch @@ -0,0 +1,111 @@ +From bc6f271c5b91470fc8246c077c6d8d21a9539e24 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 21:22:22 -0300 +Subject: hwmon: (adt7470) Use cached PWM frequency value + +From: Luiz Angelo Daros de Luca + +[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ] + +adt7470_pwm_read() currently ignores failures returned by +pwm1_freq_get(). If the register read fails, the negative error code is +returned through *val while the function itself reports success, +potentially exposing a negative PWM frequency through sysfs. + +Fix this by using the cached PWM frequency maintained by the driver, +eliminating the register access from the read path. + +Apart from the corrected error propagation and using the cached value, +no functional change is intended. + +Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") +Signed-off-by: Luiz Angelo Daros de Luca +Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/adt7470.c | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c +index c6fc7d38d698c..1fbca4869b7b6 100644 +--- a/drivers/hwmon/adt7470.c ++++ b/drivers/hwmon/adt7470.c +@@ -182,6 +182,7 @@ struct adt7470_data { + u8 pwm_min[ADT7470_PWM_COUNT]; + s8 pwm_tmin[ADT7470_PWM_COUNT]; + u8 pwm_auto_temp[ADT7470_PWM_COUNT]; ++ u32 pwm_freq; + + struct task_struct *auto_update; + unsigned int auto_update_interval; +@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev, + } + + /* These are the valid PWM frequencies to the nearest Hz */ +-static const int adt7470_freq_map[] = { ++static const u32 adt7470_freq_map[] = { + 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 + }; + +@@ -796,7 +797,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val + *val = 1 + data->pwm_automatic[channel]; + break; + case hwmon_pwm_freq: +- *val = pwm1_freq_get(dev); ++ *val = data->pwm_freq; + break; + default: + return -EOPNOTSUPP; +@@ -809,12 +810,14 @@ static int pwm1_freq_set(struct device *dev, long freq) + { + struct adt7470_data *data = dev_get_drvdata(dev); + unsigned int low_freq = ADT7470_CFG_LF; ++ u32 closest_freq; + int index; + int err; + + /* Round the user value given to the closest available frequency */ + index = find_closest(freq, adt7470_freq_map, + ARRAY_SIZE(adt7470_freq_map)); ++ closest_freq = adt7470_freq_map[index]; + + if (index >= 8) { + index -= 8; +@@ -832,6 +835,10 @@ static int pwm1_freq_set(struct device *dev, long freq) + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, + ADT7470_FREQ_MASK, + index << ADT7470_FREQ_SHIFT); ++ if (err < 0) ++ goto out; ++ ++ data->pwm_freq = closest_freq; + out: + mutex_unlock(&data->lock); + +@@ -1285,6 +1292,7 @@ static int adt7470_probe(struct i2c_client *client) + struct device *dev = &client->dev; + struct adt7470_data *data; + struct device *hwmon_dev; ++ int freq_val; + int err; + + data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); +@@ -1309,6 +1317,14 @@ static int adt7470_probe(struct i2c_client *client) + if (err < 0) + return err; + ++ freq_val = pwm1_freq_get(dev); ++ if (freq_val <= 0) { ++ err = freq_val < 0 ? freq_val : -EINVAL; ++ return err; ++ } ++ ++ data->pwm_freq = (u32)freq_val; ++ + /* Register sysfs hooks */ + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &adt7470_chip_info, +-- +2.53.0 + diff --git a/queue-7.1/hwmon-ina2xx-fix-various-overflow-issues.patch b/queue-7.1/hwmon-ina2xx-fix-various-overflow-issues.patch new file mode 100644 index 0000000000..757d3060fe --- /dev/null +++ b/queue-7.1/hwmon-ina2xx-fix-various-overflow-issues.patch @@ -0,0 +1,156 @@ +From 0368266b8f4cad2076e56c352d9c176f0529b85f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 10 Jun 2026 07:46:16 -0700 +Subject: hwmon: (ina2xx) Fix various overflow issues + +From: Guenter Roeck + +[ Upstream commit e6c80061ca239f45c0eaf7e47a91d6d6df9bd636 ] + +Sashiko reports several integer overflow problems in the ina2xx driver +caused by unbounded multiplications and inadequate types for intermediate +calculations. + +Specifically: +- In ina2xx_get_value(), the return type is changed from int to long. + Intermediate calculations for current are now performed using 64-bit + types to prevent 32-bit integer overflow before the division by 1000. +- When calculating power in ina2xx_get_value() and + sy24655_average_power_read(), interim values are cast to u64 and clamped + to LONG_MAX. This prevents overflow when regval or accumulator_24 is + multiplied by power_lsb_uW. +- In ina226_alert_to_reg(), the clamping logic is rewritten using min_t(). + This safely avoids integer overflows when scaling user-provided values + for shunt voltage, bus voltage, power, and current limits. + +Cc: Loic Poulain +Fixes: ab7fbee452be ("hwmon: (ina2xx) Fix various overflow issues") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/ina2xx.c | 61 ++++++++++++++++++++++++------------------ + 1 file changed, 35 insertions(+), 26 deletions(-) + +diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c +index 32bf4595bcb48..789eeb718b1ca 100644 +--- a/drivers/hwmon/ina2xx.c ++++ b/drivers/hwmon/ina2xx.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -266,30 +267,34 @@ static u16 ina226_interval_to_reg(long interval) + return FIELD_PREP(INA226_AVG_RD_MASK, avg_bits); + } + +-static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, +- unsigned int regval) ++static long ina2xx_get_value(struct ina2xx_data *data, u8 reg, ++ unsigned int regval) + { +- int val; ++ s64 val64; ++ long val; + + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: + /* signed register */ +- val = (s16)regval >> data->config->shunt_voltage_shift; +- val = DIV_ROUND_CLOSEST(val, data->config->shunt_div); ++ val = DIV_ROUND_CLOSEST((s16)regval >> data->config->shunt_voltage_shift, ++ data->config->shunt_div); + break; + case INA2XX_BUS_VOLTAGE: +- val = (regval >> data->config->bus_voltage_shift) * +- data->config->bus_voltage_lsb; +- val = DIV_ROUND_CLOSEST(val, 1000); ++ val = DIV_ROUND_CLOSEST((regval >> data->config->bus_voltage_shift) * ++ data->config->bus_voltage_lsb, 1000); + break; + case INA2XX_POWER: +- val = regval * data->power_lsb_uW; ++ val = min_t(u64, (u64)regval * data->power_lsb_uW, LONG_MAX); + break; + case INA2XX_CURRENT: + /* signed register, result in mA */ +- val = ((s16)regval >> data->config->current_shift) * ++ val64 = (s64)((s16)regval >> data->config->current_shift) * + data->current_lsb_uA; +- val = DIV_ROUND_CLOSEST(val, 1000); ++ if (val64 < 0) ++ val64 = -DIV_ROUND_CLOSEST_ULL(-val64, 1000); ++ else ++ val64 = DIV_ROUND_CLOSEST_ULL(val64, 1000); ++ val = clamp_val(val64, LONG_MIN, LONG_MAX); + break; + case INA2XX_CALIBRATION: + val = regval; +@@ -378,27 +383,29 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val) + */ + static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) + { ++ long limit; ++ + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: +- val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div); +- val *= data->config->shunt_div; +- val <<= data->config->shunt_voltage_shift; +- return clamp_val(val, 0, SHRT_MAX); ++ val = min_t(long, val, DIV_ROUND_CLOSEST(SHRT_MAX, data->config->shunt_div)); ++ return min_t(long, (val * data->config->shunt_div) << data->config->shunt_voltage_shift, ++ SHRT_MAX); + case INA2XX_BUS_VOLTAGE: +- val = clamp_val(val, 0, 200000); +- val = (val * 1000) << data->config->bus_voltage_shift; +- val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb); +- return clamp_val(val, 0, USHRT_MAX); ++ val = min_t(long, val, 130000); ++ return min_t(long, ++ DIV_ROUND_CLOSEST((val * 1000) << data->config->bus_voltage_shift, ++ data->config->bus_voltage_lsb), ++ USHRT_MAX); + case INA2XX_POWER: +- val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW); +- val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW); +- return clamp_val(val, 0, USHRT_MAX); ++ val = min_t(long, val, LONG_MAX - data->power_lsb_uW); ++ return min_t(long, DIV_ROUND_CLOSEST(val, data->power_lsb_uW), USHRT_MAX); + case INA2XX_CURRENT: +- val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000); ++ limit = (LONG_MAX - (data->current_lsb_uA / 2)) / 1000; ++ val = min_t(long, val, limit); + /* signed register, result in mA */ + val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA); +- val <<= data->config->current_shift; +- return clamp_val(val, SHRT_MIN, SHRT_MAX); ++ limit = SHRT_MAX >> data->config->current_shift; ++ return (u16)(min_t(long, val, limit) << data->config->current_shift); + default: + /* programmer goofed */ + WARN_ON_ONCE(1); +@@ -537,6 +544,7 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va + u8 template[6]; + int ret; + long accumulator_24, sample_count; ++ u64 val64; + + /* 48-bit register read */ + ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template); +@@ -555,7 +563,8 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va + return 0; + } + +- *val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; ++ val64 = (u64)DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; ++ *val = min_t(u64, val64, LONG_MAX); + + return 0; + } +-- +2.53.0 + diff --git a/queue-7.1/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch b/queue-7.1/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch new file mode 100644 index 0000000000..bfaaeb2088 --- /dev/null +++ b/queue-7.1/hwmon-lm90-only-report-alarms-if-driver-is-ready.patch @@ -0,0 +1,54 @@ +From 3a4ff32d52308f94fbe743a863b0d59fd9d9f0fe Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 15:27:28 -0700 +Subject: hwmon: (lm90) Only report alarms if driver is ready + +From: Guenter Roeck + +[ Upstream commit aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 ] + +Userspace can read sysfs attributes before driver registration is complete, +immediately after devm_hwmon_device_register_with_info() has been called. +At that time, data->hwmon_dev is not yet initialized. This can trigger +a NULL pointer access since lm90_update_device() and with it +lm90_update_alarms_locked() will be called. This call schedules +report_work and lm90_report_alarms(), which passes the still-NULL +data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer +dereference. + +Fix the problem by only scheduling the report and alert workers +data->hwmon_dev is set. + +Reported-by: Sashiko +Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/lm90.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c +index c78c96e1bd83f..c56dd958429be 100644 +--- a/drivers/hwmon/lm90.c ++++ b/drivers/hwmon/lm90.c +@@ -1194,7 +1194,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) + check_enable = (client->irq || !(data->config_orig & 0x80)) && + (data->config & 0x80); + +- if (force || check_enable) ++ if (data->hwmon_dev && (force || check_enable)) + schedule_work(&data->report_work); + + /* +@@ -1202,7 +1202,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) + * alarms are all clear, and alerts are currently disabled. + * Otherwise (re)schedule worker if needed. + */ +- if (check_enable) { ++ if (check_enable && data->hwmon_dev) { + if (!(data->current_alarms & data->alert_alarms)) { + dev_dbg(&client->dev, "Re-enabling ALERT#\n"); + lm90_update_confreg(data, data->config & ~0x80); +-- +2.53.0 + diff --git a/queue-7.1/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch b/queue-7.1/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch new file mode 100644 index 0000000000..3c6961c7c7 --- /dev/null +++ b/queue-7.1/hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch @@ -0,0 +1,39 @@ +From 6c3e00e85e2bd6942e57a227fdfa0cee5effa45b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 5 Feb 2025 12:27:15 -0800 +Subject: hwmon: (ltc4282) Fix reading the minimum alarm voltage + +From: Guenter Roeck + +[ Upstream commit 00feb1cce93dab948a299b69753d99c681d45a0b ] + +Coverity reports an out-of-bounds access when reading the minimum alarm +voltage for the VGPIO channel. Add the missing return statement to fix +the problem. + +Fixes: cbc29538dbf7 ("hwmon: Add driver for LTC4282") +Cc: Nuno Sa +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/ltc4282.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c +index b9084424160d6..bdbf370233d71 100644 +--- a/drivers/hwmon/ltc4282.c ++++ b/drivers/hwmon/ltc4282.c +@@ -375,8 +375,8 @@ static int ltc4282_read_in(struct ltc4282_state *st, u32 attr, long *val, + channel, val); + case hwmon_in_min_alarm: + if (channel == LTC4282_CHAN_VGPIO) +- ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG, +- LTC4282_GPIO_ALARM_L_MASK, val); ++ return ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG, ++ LTC4282_GPIO_ALARM_L_MASK, val); + + return ltc4282_vdd_source_read_alm(st, + LTC4282_VSOURCE_ALARM_L_MASK, +-- +2.53.0 + diff --git a/queue-7.1/hwmon-nct6775-core-fix-number-of-temperature-registe.patch b/queue-7.1/hwmon-nct6775-core-fix-number-of-temperature-registe.patch new file mode 100644 index 0000000000..b0f86b583c --- /dev/null +++ b/queue-7.1/hwmon-nct6775-core-fix-number-of-temperature-registe.patch @@ -0,0 +1,90 @@ +From 667c8562186f9731a74dd5e1f52f6c7080af03d9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 07:14:36 -0700 +Subject: hwmon: (nct6775-core) Fix number of temperature registers for NCT6116 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guenter Roeck + +[ Upstream commit b0e8adb2ccb43009796897ced09f91636685c9d3 ] + +Unlike NCT6106, NCT6116 only has three temperature registers, and with +it only three temperature source and temperature source configuration +registers. The register addresses match those of NCT6106 and can be +re-used. + +The code used a separate array to list the temperature source registers +for NCT6116, but used the size of the NCT6106 register array to set +the number of registers. The NCT6106 register array provides six addresses, +while the temperature source register array for NCT6116 only provides three +addresses. This causes a KASAN report. + +BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775] +Read of size 2 at addr ffffffffc19561a6 by task modprobe/954 +... +Call Trace: + dump_stack+0x7d/0xa7 + print_address_description.constprop.0+0x1c/0x220 + ? __kasan_kmalloc.constprop.0+0xc9/0xd0 + ? __kmalloc_node_track_caller+0x194/0x5b0 + ? nct6775_probe+0x936/0x46f0 [nct6775] + ? nct6775_probe+0x936/0x46f0 [nct6775] +... + +Fix the problem by hard-coding the number of temperature and temperature +configuration registers to three for NCT6116. Drop the unnecessary +NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE. + +Reported-by: Florian Bezdeka +Closes: https://lore.kernel.org/linux-hwmon/57cfc3fa-d4e9-4c10-8aa7-4ad0af7ebebe@roeck-us.net/T/#t +Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") +Cc: Björn Gerhart +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index d668dc390def8..51253acff4b06 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -846,8 +846,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 }; + static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 }; + static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 }; + static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 }; +-static const u16 NCT6116_REG_TEMP_SOURCE[] = { +- 0xb0, 0xb1, 0xb2 }; + + static const u16 NCT6116_REG_CRITICAL_TEMP[] = { + 0x11a, 0x12a, 0x13a, 0x19a, 0x1aa }; +@@ -3652,7 +3650,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + = NCT6106_CRITICAL_PWM_ENABLE_MASK; + data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM; + data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; +- data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE; ++ data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; +@@ -3666,13 +3664,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, + + reg_temp = NCT6106_REG_TEMP; + reg_temp_mon = NCT6106_REG_TEMP_MON; +- num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); ++ num_reg_temp = 3; + num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON); + num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP); + reg_temp_over = NCT6106_REG_TEMP_OVER; + reg_temp_hyst = NCT6106_REG_TEMP_HYST; + reg_temp_config = NCT6106_REG_TEMP_CONFIG; +- num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG); ++ num_reg_temp_config = 3; + reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6106_REG_TEMP_CRIT; + reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; +-- +2.53.0 + diff --git a/queue-7.1/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch b/queue-7.1/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch new file mode 100644 index 0000000000..787cfc2ca1 --- /dev/null +++ b/queue-7.1/hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch @@ -0,0 +1,74 @@ +From 82222d5d52e27681bfca9a1e955a0c5c304f32da Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 13:35:37 -0700 +Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Guenter Roeck + +[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ] + +Sashiko reports: + +During initialization of the nct6116 chip, the driver sets data->pwm_num +to 5. However, it assigns several NCT6106 register arrays (such as +NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and +NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP. +These arrays only contain 3 elements. + +In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If +data->has_pwm has bits 3 or 4 set (which is structurally possible for +nct6116), the loop attempts to read elements at index 3 and 4 from these +3-element arrays. This results in a global out-of-bounds read, which can +be caught by KASAN. + +Furthermore, the driver uses these garbage out-of-bounds values as +hardware register addresses for subsequent read and write operations. This +leads to invalid hardware register access, potentially causing hardware +misconfiguration or system crashes. + +The underlying problem is that the chip does support up to five fan +control channels, but only the first three support weight control. +Fix the problem by extending the affected weight register arrays with +zeroed fields. The driver uses zeroed register addresses to determine +if a register is supported or not, and skips accesses for unsupported +registers. + +Reported-by: Sashiko +Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") +Cc: Björn Gerhart +Cc: Florian Bezdeka +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nct6775-core.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c +index 51253acff4b06..94482c8092cda 100644 +--- a/drivers/hwmon/nct6775-core.c ++++ b/drivers/hwmon/nct6775-core.c +@@ -791,12 +791,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 }; + + static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 }; + +-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b }; +-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c }; +-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 }; ++static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 }; + + static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 }; + static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 }; +-- +2.53.0 + diff --git a/queue-7.1/hwmon-nzxt-smart2-dma-align-output-buffer.patch b/queue-7.1/hwmon-nzxt-smart2-dma-align-output-buffer.patch new file mode 100644 index 0000000000..c2d015fcce --- /dev/null +++ b/queue-7.1/hwmon-nzxt-smart2-dma-align-output-buffer.patch @@ -0,0 +1,53 @@ +From f8e0db1c876661a31709f95e1d6de5ca3d30d162 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 09:54:23 -0700 +Subject: hwmon: (nzxt-smart2) DMA-align output buffer + +From: Guenter Roeck + +[ Upstream commit 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 ] + +Sashiko reports: + +When send_output_report() calls hid_hw_output_report(), the underlying USB +HID core calls usb_interrupt_msg() which maps this buffer directly for DMA. + +When the DMA mapping flushes or invalidates the cacheline, it will corrupt +the adjacent variables (mutex, update_interval) that were modified +concurrently by the CPU. This causes memory corruption due to cacheline +sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA +API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings +for this violation. + +Any operation that triggers send_output_report() (like setting a fan speed +or updating the interval) causes the USB DMA mapping. On systems with +non-coherent caches, this structural bug causes immediate and deterministic +memory corruption. + +Align the output buffer to ARCH_DMA_MINALIGN to fix the problem. + +Reported-by: Sashiko +Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.") +Cc: Aleksandr Mezin +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/nzxt-smart2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c +index e2316c46629d6..ff0c0bee0e839 100644 +--- a/drivers/hwmon/nzxt-smart2.c ++++ b/drivers/hwmon/nzxt-smart2.c +@@ -203,7 +203,7 @@ struct drvdata { + */ + struct mutex mutex; + long update_interval; +- u8 output_buffer[OUTPUT_REPORT_SIZE]; ++ u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN); + }; + + static long scale_pwm_value(long val, long orig_max, long new_max) +-- +2.53.0 + diff --git a/queue-7.1/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch b/queue-7.1/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch new file mode 100644 index 0000000000..1797fd01f6 --- /dev/null +++ b/queue-7.1/hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch @@ -0,0 +1,39 @@ +From 171284e3b2dcdf17662a1aedd30e27a4df159ada Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 08:41:40 -0700 +Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data() + +From: Guenter Roeck + +[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ] + +pmbus_update_byte_data() is supposed to return a negative error code or 0. +However, if no change is made to the register, it actually returns the +register value. This can result in problems if the calling code explicitly +expects to see an error code or 0. + +Fix it to return 0 on success or the error code as expected. + +Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write") +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/pmbus/pmbus_core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c +index 3143b9e0316c4..7f2d28e41d816 100644 +--- a/drivers/hwmon/pmbus/pmbus_core.c ++++ b/drivers/hwmon/pmbus/pmbus_core.c +@@ -513,7 +513,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + if (tmp != rv) + rv = _pmbus_write_byte_data(client, page, reg, tmp); + +- return rv; ++ return rv < 0 ? rv : 0; + } + EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS"); + +-- +2.53.0 + diff --git a/queue-7.1/hwmon-sht3x-fix-unaligned-accesses.patch b/queue-7.1/hwmon-sht3x-fix-unaligned-accesses.patch new file mode 100644 index 0000000000..b803c1308d --- /dev/null +++ b/queue-7.1/hwmon-sht3x-fix-unaligned-accesses.patch @@ -0,0 +1,76 @@ +From f8c7ec24da62e28f911e248170720123d1b1829b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 25 Jul 2026 09:34:46 -0700 +Subject: hwmon: (sht3x) Fix unaligned accesses + +From: Guenter Roeck + +[ Upstream commit f46d5ab43a572b84773015a76966f5da56fc1748 ] + +Sashiko reports: + +In sht3x_update_client(), the 16-bit temperature and humidity values are +extracted from a stack-allocated byte array using be16_to_cpup(). The +pointers passed to this function are calculated as buf and buf + 3. Since +the difference between the two pointers is an odd number of bytes, at +least one of them is guaranteed to be at an unaligned offset. + +This will trigger an alignment fault on strict-alignment architectures +such as ARMv5 or SPARC, resulting in a kernel panic. + +Fix the problem by using get_unaligned_be16() instead of be16_to_cpup(), +and put_unaligned_be16() instead of cpu_to_be16(). + +Fixes: 7c84f7f80d6f ("hwmon: add support for Sensirion SHT3x sensors") +Reported-by: Sashiko +Signed-off-by: Guenter Roeck +Signed-off-by: Sasha Levin +--- + drivers/hwmon/sht3x.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c +index c2f6b73aa7f34..4d90f89a99297 100644 +--- a/drivers/hwmon/sht3x.c ++++ b/drivers/hwmon/sht3x.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + /* commands (high repeatability mode) */ + static const unsigned char sht3x_cmd_measure_single_hpm[] = { 0x24, 0x00 }; +@@ -276,9 +277,9 @@ static struct sht3x_data *sht3x_update_client(struct device *dev) + if (ret) + goto out; + +- val = be16_to_cpup((__be16 *)buf); ++ val = get_unaligned_be16(buf); + data->temperature = sht3x_extract_temperature(val); +- val = be16_to_cpup((__be16 *)(buf + 3)); ++ val = get_unaligned_be16(buf + 3); + data->humidity = sht3x_extract_humidity(val); + data->last_update = jiffies; + } +@@ -336,7 +337,7 @@ static int limits_update(struct sht3x_data *data) + if (ret) + return ret; + +- raw = be16_to_cpup((__be16 *)buffer); ++ raw = get_unaligned_be16(buffer); + temperature = sht3x_extract_temperature((raw & 0x01ff) << 7); + humidity = sht3x_extract_humidity(raw & 0xfe00); + data->temperature_limits[index] = temperature; +@@ -389,7 +390,7 @@ static size_t limit_write(struct device *dev, + raw = ((u32)(temperature + 45000) * 24543) >> (16 + 7); + raw |= ((humidity * 42950) >> 16) & 0xfe00; + +- *((__be16 *)position) = cpu_to_be16(raw); ++ put_unaligned_be16(raw, position); + position += SHT3X_WORD_LEN; + *position = crc8(sht3x_crc8_table, + position - SHT3X_WORD_LEN, +-- +2.53.0 + diff --git a/queue-7.1/ice-suppress-dpll-errors-during-reset-recovery.patch b/queue-7.1/ice-suppress-dpll-errors-during-reset-recovery.patch new file mode 100644 index 0000000000..754a847922 --- /dev/null +++ b/queue-7.1/ice-suppress-dpll-errors-during-reset-recovery.patch @@ -0,0 +1,89 @@ +From 3e1349816fe6957aba6600df6470dc803192835e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 May 2026 13:50:06 +0200 +Subject: ice: suppress DPLL errors during reset recovery + +From: Przemyslaw Korba + +[ Upstream commit b00be7c6b4bd7da3d510753b27ff6cb7ec647d07 ] + +During reset recovery, the admin queue returns EBUSY which is expected +behavior. However, the DPLL subsystem was logging these as errors and +incrementing the error counter, potentially leading to unnecessary +warnings and even disabling the DPLL periodic worker if the threshold +was reached. + +Suppress error logging and error counter increments when the admin +queue returns EBUSY, as this is expected during reset recovery and +not a real failure condition. + +test case: +- ethtool --reset eth3 irq-shared dma-shared filter-shared offload-shared +mac-shared phy-shared ram-shared +- observe if dmesg EBUSY errors are gone + +Fixes: d7999f5ea64b ("ice: implement dpll interface to control cgu") +Signed-off-by: Przemyslaw Korba +Reviewed-by: Simon Horman +Tested-by: Rinitha S (A Contingent worker at Intel) +Reviewed-by: Aleksandr Loktionov +Signed-off-by: Tony Nguyen +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/ice/ice_dpll.c | 19 ++++++++++++------- + 1 file changed, 12 insertions(+), 7 deletions(-) + +diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c +index 5f6f29142e301..14dfddc85d1c0 100644 +--- a/drivers/net/ethernet/intel/ice/ice_dpll.c ++++ b/drivers/net/ethernet/intel/ice/ice_dpll.c +@@ -784,7 +784,7 @@ ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin, + ret, + libie_aq_str(pf->hw.adminq.sq_last_status), + pin_type_name[pin_type], pin->idx); +- else ++ else if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY) + dev_err_ratelimited(ice_pf_to_dev(pf), + "err:%d %s failed to update %s pin:%u\n", + ret, +@@ -2821,7 +2821,8 @@ static int ice_dpll_pps_update_phase_offsets(struct ice_pf *pf, + *phase_offset_pins_updated = 0; + ret = ice_aq_get_cgu_input_pin_measure(&pf->hw, DPLL_TYPE_PPS, meas, + ARRAY_SIZE(meas)); +- if (ret && pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN) { ++ if (ret && (pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN || ++ pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EBUSY)) { + return 0; + } else if (ret) { + dev_err(ice_pf_to_dev(pf), +@@ -2883,10 +2884,12 @@ ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init) + d->dpll_idx, d->prev_input_idx, d->input_idx, + d->dpll_state, d->prev_dpll_state, d->mode); + if (ret) { +- dev_err(ice_pf_to_dev(pf), +- "update dpll=%d state failed, ret=%d %s\n", +- d->dpll_idx, ret, +- libie_aq_str(pf->hw.adminq.sq_last_status)); ++ /* EBUSY is expected during reset recovery, don't log error */ ++ if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY) ++ dev_err(ice_pf_to_dev(pf), ++ "update dpll=%d state failed, ret=%d %s\n", ++ d->dpll_idx, ret, ++ libie_aq_str(pf->hw.adminq.sq_last_status)); + return ret; + } + if (init) { +@@ -2955,7 +2958,9 @@ static void ice_dpll_periodic_work(struct kthread_work *work) + d->periodic_counter % dp->phase_offset_monitor_period == 0) + ret = ice_dpll_pps_update_phase_offsets(pf, &phase_offset_ntf); + if (ret) { +- d->cgu_state_acq_err_num++; ++ /* EBUSY is expected during reset recovery */ ++ if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY) ++ d->cgu_state_acq_err_num++; + /* stop rescheduling this worker */ + if (d->cgu_state_acq_err_num > + ICE_CGU_STATE_ACQ_ERR_THRESHOLD) { +-- +2.53.0 + diff --git a/queue-7.1/idpf-adjust-txq-ring-count-minimum.patch b/queue-7.1/idpf-adjust-txq-ring-count-minimum.patch new file mode 100644 index 0000000000..30a83c6534 --- /dev/null +++ b/queue-7.1/idpf-adjust-txq-ring-count-minimum.patch @@ -0,0 +1,72 @@ +From 27140040be288f073f72f0d7b28ffcfeb3b40e26 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 30 Jun 2026 16:56:19 -0700 +Subject: idpf: adjust TxQ ring count minimum + +From: Joshua Hay + +[ Upstream commit bef152db47debcd14cbacefc5767f6f026c4bc89 ] + +Set the TxQ ring count minimum to 128 descriptors. Any lower than this, +and the queue will stall and trigger Tx timeouts in flow based +scheduling mode. This is because next_to_clean might never be updated. + +In flow based scheduling mode, next_to_clean is only updated after a +descriptor completion is processed, i.e. after the RE bit is set in the +last descriptor of a Tx packet. This will never happen with a ring size +of 64 and an IDPF_TX_SPLITQ_RE_MIN_GAP of 64. No matter what the value +of last_re is initialized/set to, the calculated gap will be at most 63 +and never trigger the RE bit. + +Even a ring size of 96 does not solve this. Because of how infrequent +next_to_clean is updated and how small the ring is, IDPF_DESC_UNUSED +will be much smaller on average. This increases the chance the queue +will be stopped because a multi-descriptor packet, e.g. a large LSO +packet, does not see enough resources on the ring. In this case, the +queue will trigger the stop logic. The queue permanently stalls because +there is no chance for a descriptor completion to update next_to_clean +since it is dependent on a packet being sent. + +Fixes: 5f417d551324 ("idpf: replace flow scheduling buffer ring with buffer pool") +Signed-off-by: Joshua Hay +Reviewed-by: Aleksandr Loktionov +Tested-by: Samuel Salin +Signed-off-by: Tony Nguyen +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/idpf/idpf_txrx.c | 5 +---- + drivers/net/ethernet/intel/idpf/idpf_txrx.h | 2 +- + 2 files changed, 2 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c +index f6b3b15364ff6..bddb601ee411c 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c ++++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c +@@ -3097,10 +3097,7 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb, + + tx_params.dtype = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE; + tx_params.eop_cmd = IDPF_TXD_FLEX_FLOW_CMD_EOP; +- /* Set the RE bit to periodically "clean" the descriptor ring. +- * MIN_GAP is set to MIN_RING size to ensure it will be set at +- * least once each time around the ring. +- */ ++ /* Set the RE bit periodically to "clean" the descriptor ring */ + if (idpf_tx_splitq_need_re(tx_q)) { + tx_params.eop_cmd |= IDPF_TXD_FLEX_FLOW_CMD_RE; + tx_q->txq_grp->num_completions_pending++; +diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h +index 4be5b3b6d3ed2..908dfa28674eb 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h ++++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h +@@ -21,7 +21,7 @@ + /* Mailbox Queue */ + #define IDPF_MAX_MBXQ 1 + +-#define IDPF_MIN_TXQ_DESC 64 ++#define IDPF_MIN_TXQ_DESC 128 + #define IDPF_MIN_RXQ_DESC 64 + #define IDPF_MIN_TXQ_COMPLQ_DESC 256 + #define IDPF_MAX_QIDS 256 +-- +2.53.0 + diff --git a/queue-7.1/idpf-bound-interrupt-vector-register-fill-to-the-all.patch b/queue-7.1/idpf-bound-interrupt-vector-register-fill-to-the-all.patch new file mode 100644 index 0000000000..3ea39448e7 --- /dev/null +++ b/queue-7.1/idpf-bound-interrupt-vector-register-fill-to-the-all.patch @@ -0,0 +1,112 @@ +From faf5ffbd80fc25fa6558db830c2c6b61c9760a15 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 17 Jun 2026 17:57:54 -0400 +Subject: idpf: bound interrupt-vector register fill to the allocated array + +From: Michael Bommarito + +[ Upstream commit 9f7007ee9858c99aa43101bc8352c672fee85644 ] + +idpf_get_reg_intr_vecs() fills the caller-allocated reg_vals[] array from +the VIRTCHNL2_OP_ALLOC_VECTORS reply in adapter->req_vec_chunks, bounding +its inner loop only by the per-chunk num_vectors. The array is sized +separately: idpf_intr_reg_init() allocates +kzalloc_objs(struct idpf_vec_regs, total_vecs) from +caps.num_allocated_vectors and only checks the returned count after the +fill. The sum of per-chunk num_vectors is never reconciled against +total_vecs, so a reply with a small num_allocated_vectors but chunks +summing higher writes past the end of reg_vals[]. + +Impact: a control plane (a PF or hypervisor device model) that returns a +VIRTCHNL2_OP_ALLOC_VECTORS reply whose per-chunk num_vectors sum exceeds +num_allocated_vectors writes struct idpf_vec_regs entries past the end of +the reg_vals kmalloc allocation (KASAN slab-out-of-bounds write). + +Bound the fill loop to the array capacity passed in by the callers, +mirroring the sibling idpf_vport_get_q_reg(). The existing +num_regs < num_vecs check then rejects an undersized reply without the +out-of-bounds write happening first. + +Fixes: d4d558718266 ("idpf: initialize interrupts and enable vport") +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Michael Bommarito +Reviewed-by: Aleksandr Loktionov +Tested-by: Samuel Salin +Signed-off-by: Tony Nguyen +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/idpf/idpf_dev.c | 2 +- + drivers/net/ethernet/intel/idpf/idpf_vf_dev.c | 2 +- + drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 5 +++-- + drivers/net/ethernet/intel/idpf/idpf_virtchnl.h | 2 +- + 4 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/intel/idpf/idpf_dev.c b/drivers/net/ethernet/intel/idpf/idpf_dev.c +index 1a0c71c95ef12..4079a787657f1 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_dev.c ++++ b/drivers/net/ethernet/intel/idpf/idpf_dev.c +@@ -87,7 +87,7 @@ static int idpf_intr_reg_init(struct idpf_vport *vport, + if (!reg_vals) + return -ENOMEM; + +- num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals); ++ num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals, total_vecs); + if (num_regs < num_vecs) { + err = -EINVAL; + goto free_reg_vals; +diff --git a/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c b/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c +index a07d7e808ca9b..6726084f6cfa0 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c ++++ b/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c +@@ -86,7 +86,7 @@ static int idpf_vf_intr_reg_init(struct idpf_vport *vport, + if (!reg_vals) + return -ENOMEM; + +- num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals); ++ num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals, total_vecs); + if (num_regs < num_vecs) { + err = -EINVAL; + goto free_reg_vals; +diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c +index dc5ad784f456f..8bd6cca64c9bf 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c ++++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c +@@ -1318,11 +1318,12 @@ idpf_vport_init_queue_reg_chunks(struct idpf_vport_config *vport_config, + * idpf_get_reg_intr_vecs - Get vector queue register offset + * @adapter: adapter structure to get the vector chunks + * @reg_vals: Register offsets to store in ++ * @num_vecs: number of entries the @reg_vals array can hold + * + * Return: number of registers that got populated + */ + int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter, +- struct idpf_vec_regs *reg_vals) ++ struct idpf_vec_regs *reg_vals, int num_vecs) + { + struct virtchnl2_vector_chunks *chunks; + struct idpf_vec_regs reg_val; +@@ -1346,7 +1347,7 @@ int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter, + dynctl_reg_spacing = le32_to_cpu(chunk->dynctl_reg_spacing); + itrn_reg_spacing = le32_to_cpu(chunk->itrn_reg_spacing); + +- for (i = 0; i < num_vec; i++) { ++ for (i = 0; i < num_vec && num_regs < num_vecs; i++) { + reg_vals[num_regs].dyn_ctl_reg = reg_val.dyn_ctl_reg; + reg_vals[num_regs].itrn_reg = reg_val.itrn_reg; + reg_vals[num_regs].itrn_index_spacing = +diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h +index 6876e3ed9d1be..9b1c9c86f6eac 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h ++++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h +@@ -104,7 +104,7 @@ int idpf_vc_core_init(struct idpf_adapter *adapter); + void idpf_vc_core_deinit(struct idpf_adapter *adapter); + + int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter, +- struct idpf_vec_regs *reg_vals); ++ struct idpf_vec_regs *reg_vals, int num_vecs); + int idpf_queue_reg_init(struct idpf_vport *vport, + struct idpf_q_vec_rsrc *rsrc, + struct idpf_queue_id_reg_info *chunks); +-- +2.53.0 + diff --git a/queue-7.1/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch b/queue-7.1/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch new file mode 100644 index 0000000000..3daf3f2efb --- /dev/null +++ b/queue-7.1/idpf-fix-mailbox-irq-name-leak-on-request-failure.patch @@ -0,0 +1,41 @@ +From bf598cd402f521bd4a3671078351c1a74a67217c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 3 Jul 2026 01:03:32 -0400 +Subject: idpf: Fix mailbox IRQ name leak on request failure + +From: Yuho Choi + +[ Upstream commit 9bff30482c10f70d9e56c0633a6616e07140e217 ] + +idpf_mb_intr_req_irq() allocates the mailbox IRQ name before calling +request_irq(). On success, the name is released later through +kfree(free_irq()), but request_irq() failure returns without freeing it. + +Free the allocated name on the request_irq() failure path. + +Fixes: 4930fbf419a7 ("idpf: add core init and interrupt request") +Signed-off-by: Yuho Choi +Reviewed-by: Aleksandr Loktionov +Tested-by: Samuel Salin +Signed-off-by: Tony Nguyen +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/intel/idpf/idpf_lib.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c +index cf966fe6c759c..bb81e620c5c87 100644 +--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c ++++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c +@@ -139,7 +139,7 @@ static int idpf_mb_intr_req_irq(struct idpf_adapter *adapter) + if (err) { + dev_err(&adapter->pdev->dev, + "IRQ request for mailbox failed, error: %d\n", err); +- ++ kfree(name); + return err; + } + +-- +2.53.0 + diff --git a/queue-7.1/iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch b/queue-7.1/iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch new file mode 100644 index 0000000000..4ef9730529 --- /dev/null +++ b/queue-7.1/iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch @@ -0,0 +1,77 @@ +From 94e591b29b40b36555d34bd8cc685f3e6a63ee37 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 29 Jun 2026 14:52:29 +0200 +Subject: iomap: add a separate bio_set for iomap_split_ioend + +From: Christoph Hellwig + +[ Upstream commit c679ce3be6cb63763d68ab9b5d9d73ddc0a40762 ] + +iomap_split_ioend can split bios that already come from +iomap_ioend_bioset and thus deadlock when the bioset is exhausted. + +Add a separate bio_set to avoid this deadlock. + +Christian Brauner says: +Mark iomap_ioend_split_bioset static as it is only used in ioend.c, +fixing the sparse warning reported by the kernel test robot. + +Fixes: 5fcbd555d483 ("iomap: split bios to zone append limits in the submission handlers") +Signed-off-by: Christoph Hellwig +Link: https://patch.msgid.link/20260629125229.3400726-1-hch@lst.de +Signed-off-by: Christian Brauner (Amutable) +Signed-off-by: Sasha Levin +--- + fs/iomap/ioend.c | 21 +++++++++++++++++++-- + 1 file changed, 19 insertions(+), 2 deletions(-) + +diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c +index 2d5611f6cc57d..9fe0aea8e8d9b 100644 +--- a/fs/iomap/ioend.c ++++ b/fs/iomap/ioend.c +@@ -13,6 +13,7 @@ + + struct bio_set iomap_ioend_bioset; + EXPORT_SYMBOL_GPL(iomap_ioend_bioset); ++static struct bio_set iomap_ioend_split_bioset; + + struct iomap_ioend *iomap_init_ioend(struct inode *inode, + struct bio *bio, loff_t file_offset, u16 ioend_flags) +@@ -485,7 +486,8 @@ struct iomap_ioend *iomap_split_ioend(struct iomap_ioend *ioend, + sector_offset = ALIGN_DOWN(sector_offset << SECTOR_SHIFT, + i_blocksize(ioend->io_inode)) >> SECTOR_SHIFT; + +- split = bio_split(bio, sector_offset, GFP_NOFS, &iomap_ioend_bioset); ++ split = bio_split(bio, sector_offset, GFP_NOFS, ++ &iomap_ioend_split_bioset); + if (IS_ERR(split)) + return ERR_CAST(split); + split->bi_private = bio->bi_private; +@@ -508,8 +510,23 @@ EXPORT_SYMBOL_GPL(iomap_split_ioend); + + static int __init iomap_ioend_init(void) + { +- return bioset_init(&iomap_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE), ++ const unsigned int nr_mempool_entries = 4 * (PAGE_SIZE / SECTOR_SIZE); ++ int error; ++ ++ error = bioset_init(&iomap_ioend_bioset, nr_mempool_entries, + offsetof(struct iomap_ioend, io_bio), + BIOSET_NEED_BVECS); ++ if (error) ++ return error; ++ error = bioset_init(&iomap_ioend_split_bioset, nr_mempool_entries, ++ offsetof(struct iomap_ioend, io_bio), ++ BIOSET_NEED_BVECS); ++ if (error) ++ goto out_exit_ioend_bioset; ++ return 0; ++ ++out_exit_ioend_bioset: ++ bioset_exit(&iomap_ioend_bioset); ++ return error; + } + fs_initcall(iomap_ioend_init); +-- +2.53.0 + diff --git a/queue-7.1/iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch b/queue-7.1/iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch new file mode 100644 index 0000000000..079a44acb8 --- /dev/null +++ b/queue-7.1/iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch @@ -0,0 +1,67 @@ +From 544dc386a40d83f3012437d4f25d678ebcce2967 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 5 Jul 2026 22:36:11 -0700 +Subject: iommu/arm-smmu-v3-iommufd: Require exactly one Stream ID for a + vDEVICE + +From: Nicolin Chen + +[ Upstream commit c3b8ee84a965058b41275069d4696f37a8b14bf6 ] + +arm_vsmmu_vsid_to_sid() maps a guest's vSID to a single physical Stream ID +taken from master->streams[0], assuming a device has exactly one stream. A +device with several streams gets only its first one mapped, so a guest vSID +invalidation cannot reach the others' ATC and IOTLB entries; a device with +none makes master->streams a ZERO_SIZE_PTR, read out of bounds. + +Add an arm_vsmmu_vdevice_init() op to reject the vDEVICE with -EOPNOTSUPP +when master->num_streams is not one, rather than mapping it silently. + +Fixes: d68beb276ba26 ("iommu/arm-smmu-v3: Support IOMMU_HWPT_INVALIDATE using a VIOMMU object") +Link: https://patch.msgid.link/r/b15f2b73520f389f3f57881da2f040e7bdc18876.1783311134.git.nicolinc@nvidia.com +Reviewed-by: Kevin Tian +Assisted-by: Claude:claude-opus-4-8 +Reviewed-by: Pranjal Shrivastava +Signed-off-by: Nicolin Chen +Signed-off-by: Jason Gunthorpe +Signed-off-by: Sasha Levin +--- + .../iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +index ddae0b07c76b5..b7a4a1b5eddd5 100644 +--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c ++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +@@ -297,6 +297,20 @@ static int arm_vsmmu_vsid_to_sid(struct arm_vsmmu *vsmmu, u32 vsid, u32 *sid) + return ret; + } + ++static int arm_vsmmu_vdevice_init(struct iommufd_vdevice *vdev) ++{ ++ struct device *dev = iommufd_vdevice_to_device(vdev); ++ struct arm_smmu_master *master = dev_iommu_priv_get(dev); ++ ++ /* ++ * arm_vsmmu_vsid_to_sid() maps a vSID to master->streams[0] alone, so ++ * more streams would leave the rest stale and none reads out of bounds. ++ */ ++ if (master->num_streams != 1) ++ return -EOPNOTSUPP; ++ return 0; ++} ++ + /* This is basically iommu_viommu_arm_smmuv3_invalidate in u64 for conversion */ + struct arm_vsmmu_invalidation_cmd { + union { +@@ -403,6 +417,7 @@ int arm_vsmmu_cache_invalidate(struct iommufd_viommu *viommu, + static const struct iommufd_viommu_ops arm_vsmmu_ops = { + .alloc_domain_nested = arm_vsmmu_alloc_domain_nested, + .cache_invalidate = arm_vsmmu_cache_invalidate, ++ .vdevice_init = arm_vsmmu_vdevice_init, + }; + + size_t arm_smmu_get_viommu_size(struct device *dev, +-- +2.53.0 + diff --git a/queue-7.1/ipv6-release-fib6_null_entry-on-subtree-failure.patch b/queue-7.1/ipv6-release-fib6_null_entry-on-subtree-failure.patch new file mode 100644 index 0000000000..50111f5dd0 --- /dev/null +++ b/queue-7.1/ipv6-release-fib6_null_entry-on-subtree-failure.patch @@ -0,0 +1,47 @@ +From 4ab88db6bdf5a427193904847ac8b6d47c146fa6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 14:53:39 -0400 +Subject: ipv6: release fib6_null_entry on subtree failure + +From: Shuangpeng Bai + +[ Upstream commit 93cad1f6bd1e27c75c4a5ab000c2a2fc01181ccf ] + +When adding a source-specific route creates a new subtree, fib6_add() +installs fib6_null_entry as the temporary leaf of the new subtree root +and takes a fib6_info reference for that holder. + +If adding the first source leaf fails, the code frees the just allocated +subtree root but leaves that hold behind. fib6_null_entry is a per-netns +sentinel and is freed directly at netns teardown, so this does not keep +the object alive. However, it leaves its visible refcount permanently +elevated and can eventually saturate the refcount on repeated failures. + +Drop the null-entry reference before freeing the unlinked subtree root. + +Fixes: 5ea715289af6 ("ipv6: broadly use fib6_info_hold() helper") +Signed-off-by: Shuangpeng Bai +Reviewed-by: Ido Schimmel +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Link: https://patch.msgid.link/20260727185339.1545169-1-shuangpeng.kernel@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv6/ip6_fib.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c +index 414fc3c567360..b4641bfffdf3f 100644 +--- a/net/ipv6/ip6_fib.c ++++ b/net/ipv6/ip6_fib.c +@@ -1495,6 +1495,7 @@ int fib6_add(struct fib6_node *root, struct fib6_info *rt, + root, and then (in failure) stale node + in main tree. + */ ++ fib6_info_release(info->nl_net->ipv6.fib6_null_entry); + node_free_immediate(info->nl_net, sfn); + err = PTR_ERR(sn); + goto failure; +-- +2.53.0 + diff --git a/queue-7.1/ipvs-adjust-double-hashing-when-fwd-method-changes.patch b/queue-7.1/ipvs-adjust-double-hashing-when-fwd-method-changes.patch new file mode 100644 index 0000000000..f153dcc840 --- /dev/null +++ b/queue-7.1/ipvs-adjust-double-hashing-when-fwd-method-changes.patch @@ -0,0 +1,344 @@ +From 731ea8a6eacc3a11e5144d0f10b86307d4e49bb7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 19:52:33 +0800 +Subject: ipvs: adjust double hashing when fwd method changes + +From: Julian Anastasov + +[ Upstream commit 712d2993bea555f1f09cd53cbdb25714f28e85db ] + +Synced conns can be created with one forwarding method +and later updated with different one after the dest +server is configured. This needs adjusting the hashing +for node hn1 because only MASQ supports double hashing. + +Modify conn_tab_lock() to support seeking for hash node +hn0 together with adding for hn1. By this way we can +safely modify the forwarding method and hn1.hash_key +under bucket lock for the first node hn0. The forwarding +method is also protected by cp->lock as it is part of +cp->flags. + +Fix the usage of stale idx/idx2 values in conn_tab_lock +after jumping to the retry label. Instead, use idx/idx2 +values just to order the locking for the old/new tables. + +Reported-by: Zhiling Zou +Link: https://patch.msgid.link/1b914f41d725bc064c9ba9830dc8169329737270.1782540466.git.roxy520tt@gmail.com/ +Link: https://sashiko.dev/#/patchset/CALMqdkR704S2BG_QD_bgHTFp2%2B1QCi7n0T4zoZyTo8mDZevYSA%40mail.gmail.com +Fixes: f20c73b0460d ("ipvs: use more keys for connection hashing") +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/ipvs/ip_vs_conn.c | 189 +++++++++++++++++++++++++------- + 1 file changed, 147 insertions(+), 42 deletions(-) + +diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c +index b457dd2f6bc89..26f2233b34b04 100644 +--- a/net/netfilter/ipvs/ip_vs_conn.c ++++ b/net/netfilter/ipvs/ip_vs_conn.c +@@ -70,25 +70,45 @@ static struct kmem_cache *ip_vs_conn_cachep __read_mostly; + * bucket or hash table + * - hash table resize works like rehash but always rehashes into new table + * - bit lock on bucket serializes all operations that modify the chain ++ * - on resize, bucket from the old table is locked before bucket from the ++ * new table + * - cp->lock protects conn fields like cp->flags, cp->dest + */ + +-/* Lock conn_tab bucket for conn hash/unhash, not for rehash */ ++/** ++ * conn_tab_lock - Lock conn_tab buckets for conn hash/unhash, not for rehash ++ * @t: hash table for hn0, new_tbl when new_hash=true ++ * @t2: hash table for hn1, new_tbl when new_hash2=true ++ * @cp: connection ++ * @hash_key: hash key for hn0 ++ * @hash_key2: hash key for hn1 ++ * @use2: using hn1 (double hashing) based on the forwarding method ++ * @new_hash: mode for hn0, hash node (true) or seek node (false) ++ * @new_hash2: mode for hn1, hash node (true) or seek node (false) ++ * @head_ret: returned head for hn0 ++ * @head2_ret: returned head for hn1 ++ * ++ * We support 3 modes: ++ * - seek mode for both nodes, used for unhashing ++ * - hash mode for both nodes, used for hashing ++ * - seek hn0 and hash hn1, used when forwarding method is changed ++ */ + static __always_inline void +-conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key, +- u32 hash_key2, bool use2, bool new_hash, +- struct hlist_bl_head **head_ret, struct hlist_bl_head **head2_ret) ++conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_rht *t2, struct ip_vs_conn *cp, ++ u32 hash_key, u32 hash_key2, bool use2, bool new_hash, ++ bool new_hash2, struct hlist_bl_head **head_ret, ++ struct hlist_bl_head **head2_ret) + { + struct hlist_bl_head *head, *head2; + u32 hash_key_new, hash_key_new2; +- struct ip_vs_rht *t2 = t; +- u32 idx, idx2; ++ int idx = 0, idx2 = 0; ++ ++ /* Advance idx2 when new_hash is not set but hash_key2 ++ * is for new table ++ */ ++ if (new_hash2 && use2 && t != t2) ++ idx2++; + +- idx = hash_key & t->mask; +- if (use2) +- idx2 = hash_key2 & t->mask; +- else +- idx2 = idx; + if (!new_hash) { + /* We need to lock the bucket in the right table */ + +@@ -100,46 +120,45 @@ conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key, + * both nodes in different tables, use idx/idx2 + * for proper lock ordering for heads. + */ +- idx = hash_key & t->mask; +- idx |= IP_VS_RHT_TABLE_ID_MASK; +- } +- if (use2) { +- if (!ip_vs_rht_same_table(t2, hash_key2)) { +- /* It is already moved to new table */ +- t2 = rcu_dereference(t2->new_tbl); +- idx2 = hash_key2 & t2->mask; +- idx2 |= IP_VS_RHT_TABLE_ID_MASK; +- } +- } else { +- idx2 = idx; ++ idx++; + } + } ++ if (use2 && !new_hash2 && !ip_vs_rht_same_table(t2, hash_key2)) { ++ /* It is already moved to new table */ ++ t2 = rcu_dereference(t2->new_tbl); ++ idx2++; ++ } + ++ if (!use2) ++ idx2 = idx; + head = t->buckets + (hash_key & t->mask); + head2 = use2 ? t2->buckets + (hash_key2 & t2->mask) : head; + +- local_bh_disable(); +- /* Do not touch seqcount, this is a safe operation */ +- +- if (idx <= idx2) { ++ if (idx > idx2 || (head > head2 && idx == idx2)) { ++ hlist_bl_lock(head2); + hlist_bl_lock(head); +- if (head != head2) +- hlist_bl_lock(head2); + } else { +- hlist_bl_lock(head2); + hlist_bl_lock(head); ++ if (head != head2) ++ hlist_bl_lock(head2); + } + if (!new_hash) { ++ bool changed; ++ + /* Ensure hash_key is read under lock */ + hash_key_new = READ_ONCE(cp->hn0.hash_key); +- hash_key_new2 = READ_ONCE(cp->hn1.hash_key); ++ changed = hash_key != hash_key_new; ++ if (use2 && !new_hash2) { ++ hash_key_new2 = READ_ONCE(cp->hn1.hash_key); ++ changed |= hash_key2 != hash_key_new2; ++ } else { ++ hash_key_new2 = hash_key2; ++ } + /* Hash changed ? */ +- if (hash_key != hash_key_new || +- (hash_key2 != hash_key_new2 && use2)) { ++ if (changed) { + if (head != head2) + hlist_bl_unlock(head2); + hlist_bl_unlock(head); +- local_bh_enable(); + hash_key = hash_key_new; + hash_key2 = hash_key_new2; + goto retry; +@@ -155,7 +174,6 @@ static inline void conn_tab_unlock(struct hlist_bl_head *head, + if (head != head2) + hlist_bl_unlock(head2); + hlist_bl_unlock(head); +- local_bh_enable(); + } + + static void ip_vs_conn_expire(struct timer_list *t); +@@ -268,8 +286,9 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) + use2 = false; + } + +- conn_tab_lock(t, cp, hash_key, hash_key2, use2, true /* new_hash */, +- &head, &head2); ++ local_bh_disable(); ++ conn_tab_lock(t, t, cp, hash_key, hash_key2, use2, true /* new_hash */, ++ true /* new_hash2 */, &head, &head2); + + cp->flags |= IP_VS_CONN_F_HASHED; + WRITE_ONCE(cp->hn0.hash_key, hash_key); +@@ -280,6 +299,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) + hlist_bl_add_head_rcu(&cp->hn1.node, head2); + + conn_tab_unlock(head, head2); ++ local_bh_enable(); + ret = 1; + + /* Schedule resizing if load increases */ +@@ -306,18 +326,20 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp) + return refcount_dec_if_one(&cp->refcnt); + + rcu_read_lock(); ++ local_bh_disable(); + + t = rcu_dereference(ipvs->conn_tab); + hash_key = READ_ONCE(cp->hn0.hash_key); + hash_key2 = READ_ONCE(cp->hn1.hash_key); + use2 = ip_vs_conn_use_hash2(cp); + +- conn_tab_lock(t, cp, hash_key, hash_key2, use2, false /* new_hash */, +- &head, &head2); ++ conn_tab_lock(t, t, cp, hash_key, hash_key2, use2, false /* new_hash */, ++ false /* new_hash2 */, &head, &head2); + + if (cp->flags & IP_VS_CONN_F_HASHED) { + /* Decrease refcnt and unlink conn only if we are last user */ +- if (refcount_dec_if_one(&cp->refcnt)) { ++ if (use2 == ip_vs_conn_use_hash2(cp) && ++ refcount_dec_if_one(&cp->refcnt)) { + hlist_bl_del_rcu(&cp->hn0.node); + if (use2) + hlist_bl_del_rcu(&cp->hn1.node); +@@ -328,6 +350,7 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp) + + conn_tab_unlock(head, head2); + ++ local_bh_enable(); + rcu_read_unlock(); + + return ret; +@@ -632,6 +655,7 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport) + int ntbl; + int dir; + ++restart: + /* No packets from inside, so we can do it in 2 steps. */ + dir = use2 ? 1 : 0; + +@@ -686,6 +710,23 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport) + /* Protect the cp->flags modification */ + spin_lock_bh(&cp->lock); + ++ /* Recheck the forwarding method under lock */ ++ if (use2 != ip_vs_conn_use_hash2(cp)) { ++ use2 = !use2; ++ if (use2) { ++ spin_unlock_bh(&cp->lock); ++ /* Restart with new use2 value */ ++ goto restart; ++ } ++ if (dir) { ++ /* Not started yet, so just skip dir 1 */ ++ spin_unlock_bh(&cp->lock); ++ dir--; ++ goto next_dir; ++ } ++ /* Just finish dir 0 */ ++ } ++ + /* Lock seqcount only for the old bucket, even if we are on new table + * because it affects the del operation, not the adding. + */ +@@ -752,6 +793,61 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport) + goto next_dir; + } + ++/* Change forwarding method for hashed conn */ ++static void ip_vs_conn_change_fwd_mask(struct ip_vs_conn *cp, u32 new_flags) ++{ ++ struct netns_ipvs *ipvs = cp->ipvs; ++ struct hlist_bl_head *head, *head2; ++ u32 hash2, hash_key, hash_key2; ++ struct ip_vs_rht *t, *t2; ++ ++ /* See ip_vs_conn_use_hash2() for reference */ ++ if ((cp->flags & IP_VS_CONN_F_TEMPLATE) || ++ /* No change in double hashing ? */ ++ (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ) == ++ ((new_flags & IP_VS_CONN_F_FWD_MASK) == IP_VS_CONN_F_MASQ)) { ++ cp->flags = new_flags; ++ return; ++ } ++ t = rcu_dereference(ipvs->conn_tab); ++ if (ip_vs_conn_use_hash2(cp)) { ++ /* Stop double hashing */ ++ hash_key = READ_ONCE(cp->hn0.hash_key); ++ hash_key2 = READ_ONCE(cp->hn1.hash_key); ++ ++ conn_tab_lock(t, t, cp, hash_key, hash_key2, true /* use2 */, ++ false /* new_hash */, false /* new_hash2 */, ++ &head, &head2); ++ ++ /* Keep both hash keys in same table */ ++ hash_key = READ_ONCE(cp->hn0.hash_key); ++ WRITE_ONCE(cp->hn1.hash_key, hash_key); ++ hlist_bl_del_rcu(&cp->hn1.node); ++ cp->flags = new_flags; ++ ++ conn_tab_unlock(head, head2); ++ } else { ++ /* Start double hashing */ ++ ++ hash_key = READ_ONCE(cp->hn0.hash_key); ++ ++ t2 = rcu_dereference(t->new_tbl); ++ hash2 = ip_vs_conn_hashkey_conn(t2, cp, true); ++ hash_key2 = ip_vs_rht_build_hash_key(t2, hash2); ++ ++ /* Change the forwarding method under locked hn0 */ ++ conn_tab_lock(t, t2, cp, hash_key, hash_key2, true /* use2 */, ++ false /* new_hash */, true /* new_hash2 */, ++ &head, &head2); ++ ++ WRITE_ONCE(cp->hn1.hash_key, hash_key2); ++ cp->flags = new_flags; ++ hlist_bl_add_head_rcu(&cp->hn1.node, head2); ++ ++ conn_tab_unlock(head, head2); ++ } ++} ++ + /* Get default load factor to map conn_count/u_thresh to t->size */ + static int ip_vs_conn_default_load_factor(struct netns_ipvs *ipvs) + { +@@ -1021,9 +1117,18 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) + conn_flags &= ~IP_VS_CONN_F_INACTIVE; + /* connections inherit forwarding method from dest */ + flags &= ~(IP_VS_CONN_F_FWD_MASK | IP_VS_CONN_F_NOOUTPUT); ++ flags |= conn_flags; ++ /* Changing forwarding method for hashed conn can ++ * happen only under locks ++ */ ++ if (cp->flags & IP_VS_CONN_F_HASHED) ++ ip_vs_conn_change_fwd_mask(cp, flags); ++ else ++ cp->flags = flags; ++ } else { ++ flags |= conn_flags; ++ cp->flags = flags; + } +- flags |= conn_flags; +- cp->flags = flags; + cp->dest = dest; + + IP_VS_DBG_BUF(7, "Bind-dest %s c:%s:%d v:%s:%d " +-- +2.53.0 + diff --git a/queue-7.1/ipvs-clear-the-nfct-flag-under-lock.patch b/queue-7.1/ipvs-clear-the-nfct-flag-under-lock.patch new file mode 100644 index 0000000000..5509917f31 --- /dev/null +++ b/queue-7.1/ipvs-clear-the-nfct-flag-under-lock.patch @@ -0,0 +1,54 @@ +From 94cf5f0855f58eb6a447178567f3c55ca5e080a5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:25:39 +0300 +Subject: ipvs: clear the nfct flag under lock + +From: Julian Anastasov + +[ Upstream commit da7d894c41d5910daae2b8ffa024c52ff0a4df6a ] + +Sashiko warns that cp->flags should be changed under cp->lock + +Fixes: 35dfb013149f ("ipvs: queue delayed work to expire no destination connections if expire_nodest_conn=1") +Fixes: f0a5e4d7a594 ("ipvs: allow connection reuse for unconfirmed conntrack") +Link: https://sashiko.dev/#/patchset/CALMqdkR704S2BG_QD_bgHTFp2%2B1QCi7n0T4zoZyTo8mDZevYSA%40mail.gmail.com +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/ipvs/ip_vs_core.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index 7efa209a517b1..6b79e0c4d9e28 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -2194,8 +2194,11 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state + } + + if (resched) { +- if (!old_ct) ++ if (!old_ct) { ++ spin_lock_bh(&cp->lock); + cp->flags &= ~IP_VS_CONN_F_NFCT; ++ spin_unlock_bh(&cp->lock); ++ } + if (!atomic_read(&cp->n_control)) + ip_vs_conn_expire_now(cp); + __ip_vs_conn_put(cp); +@@ -2211,8 +2214,11 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state + if (sysctl_expire_nodest_conn(ipvs)) { + bool old_ct = ip_vs_conn_uses_old_conntrack(cp, skb); + +- if (!old_ct) ++ if (!old_ct) { ++ spin_lock_bh(&cp->lock); + cp->flags &= ~IP_VS_CONN_F_NFCT; ++ spin_unlock_bh(&cp->lock); ++ } + + ip_vs_conn_expire_now(cp); + __ip_vs_conn_put(cp); +-- +2.53.0 + diff --git a/queue-7.1/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch b/queue-7.1/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch new file mode 100644 index 0000000000..f324119493 --- /dev/null +++ b/queue-7.1/ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch @@ -0,0 +1,335 @@ +From 68ebdd27d3132fe39091ad9e9b0e9cda545bfb3e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:17 +0300 +Subject: ipvs: do not mangle ICMP replies for non-first fragments + +From: Julian Anastasov + +[ Upstream commit 342e24a339b90e8e339a0f8c151ca479b8565661 ] + +Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the +payload for embedded non-first IPv4 fragments. The problem is +in the very old inverted pp->dont_defrag check which should not +continue when embedded is a non-first TCP/UDP/SCTP fragment. + +Check for embedded non-first fragment is also missing from +ip_vs_out_icmp_v6(), it is needed before any connection +lookups that expect ports after the network headers. + +Drop the blocking code from ip_vs_in_icmp_v6() which prevents +ICMPv6 from local clients to use non-MASQ forwarding. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Link: https://sashiko.dev/#/patchset/20260720201122.79882-1-ja%40ssi.bg +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 11 +++--- + net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++--------------------- + net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++---- + 3 files changed, 48 insertions(+), 52 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index 7b556ce03602e..8d98f7e0a9fb2 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -1953,8 +1953,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph); ++ unsigned int hooknum, struct ip_vs_iphdr *ciph); + void ip_vs_dest_dst_rcu_free(struct rcu_head *head); + + #ifdef CONFIG_IP_VS_IPV6 +@@ -1968,8 +1967,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph); ++ unsigned int hooknum, struct ip_vs_iphdr *ciph); + #endif + + #ifdef CONFIG_SYSCTL +@@ -2041,12 +2039,13 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp) + } + + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir, unsigned int toff); ++ struct ip_vs_conn *cp, int dir, unsigned int toff, ++ bool has_ports); + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int dir, unsigned int toff, +- struct ip_vs_iphdr *ciph); ++ bool has_ports, struct ip_vs_iphdr *ciph); + #endif + + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index cd5eb71543ec8..7efa209a517b1 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -924,7 +924,8 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, + * - inout: 1=in->out, 0=out->in + */ + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout, unsigned int toff) ++ struct ip_vs_conn *cp, int inout, unsigned int toff, ++ bool has_ports) + { + struct iphdr *iph = ip_hdr(skb); + struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); +@@ -944,8 +945,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + } + + /* the TCP/UDP/SCTP port */ +- if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol || +- IPPROTO_SCTP == ciph->protocol) { ++ if (has_ports) { + __be16 *ports = (void *)ciph + ciph->ihl*4; + + if (inout) +@@ -970,18 +970,15 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int inout, unsigned int toff, +- struct ip_vs_iphdr *ciph) ++ bool has_ports, struct ip_vs_iphdr *ciph) + { + struct ipv6hdr *iph = ipv6_hdr(skb); +- int protocol; + struct icmp6hdr *icmph; + struct ipv6hdr *cih; + + icmph = (struct icmp6hdr *)(skb->data + toff); + cih = (struct ipv6hdr *)(skb->data + ciph->off); + +- protocol = ciph->protocol; +- + if (inout) { + iph->saddr = cp->vaddr.in6; + cih->daddr = cp->vaddr.in6; +@@ -991,9 +988,7 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + } + + /* the TCP/UDP/SCTP port */ +- if (!ciph->fragoffs && +- (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || +- protocol == IPPROTO_SCTP)) { ++ if (has_ports) { + __be16 *ports = (void *)(skb->data + ciph->len); + + IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, +@@ -1035,6 +1030,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; + unsigned int ctoff = ciph->len; ++ bool has_ports = false; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; +@@ -1048,17 +1044,19 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + } + + if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || +- ciph->protocol == IPPROTO_SCTP) ++ ciph->protocol == IPPROTO_SCTP) { + ctoff += 2 * sizeof(__u16); ++ has_ports = true; ++ } + if (skb_ensure_writable(skb, ctoff)) + goto out; + + #ifdef CONFIG_IP_VS_IPV6 + if (af == AF_INET6) +- ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph); + else + #endif +- ip_vs_nat_icmp(skb, pp, cp, 1, toff); ++ ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports); + + if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) + goto out; +@@ -1142,8 +1140,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + + /* Is the embedded protocol header present? */ +- if (unlikely(cih->frag_off & htons(IP_OFFSET) && +- pp->dont_defrag)) ++ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, +@@ -1207,6 +1204,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (!pp) + return NF_ACCEPT; + ++ /* Is the embedded protocol header present? */ ++ if (unlikely(ciph.fragoffs && !pp->dont_defrag)) ++ return NF_ACCEPT; ++ + /* The embedded headers contain source and dest in reverse order */ + cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto, + ipvs, AF_INET6, skb, &ciph); +@@ -1865,8 +1866,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + pp = pd->pp; + + /* Is the embedded protocol header present? */ +- if (unlikely(cih->frag_off & htons(IP_OFFSET) && +- pp->dont_defrag)) ++ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, +@@ -1874,7 +1874,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + + offset2 = offset; + ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph); +- offset = ciph.len; + + /* The embedded headers contain source and dest in reverse order. + * For IPIP/UDP/GRE tunnel this is error for request, not for reply. +@@ -1968,11 +1967,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); +- if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || +- IPPROTO_SCTP == cih->protocol) +- offset += 2 * sizeof(__u16); +- verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, +- &ciph); ++ verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph); + + out: + if (likely(!new_cp)) +@@ -2032,8 +2027,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + pp = pd->pp; + +- /* Cannot handle fragmented embedded protocol */ +- if (ciph.fragoffs) ++ /* Is the embedded protocol header present? */ ++ if (ciph.fragoffs && !pp->dont_defrag) + return NF_ACCEPT; + + IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset, +@@ -2057,13 +2052,6 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + new_cp = true; + } + +- /* VS/TUN, VS/DR and LOCALNODE just let it go */ +- if ((hooknum == NF_INET_LOCAL_OUT) && +- (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) { +- verdict = NF_ACCEPT; +- goto out; +- } +- + verdict = NF_DROP; + + /* Ensure the checksum is correct */ +@@ -2079,14 +2067,7 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); + +- /* Need to mangle contained IPv6 header in ICMPv6 packet */ +- offset = ciph.len; +- if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol || +- IPPROTO_SCTP == ciph.protocol) +- offset += 2 * sizeof(__u16); /* Also mangle ports */ +- +- verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, +- &ciph); ++ verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph); + + out: + if (likely(!new_cp)) +diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c +index c23401c789de3..0b0c5304993a9 100644 +--- a/net/netfilter/ipvs/ip_vs_xmit.c ++++ b/net/netfilter/ipvs/ip_vs_xmit.c +@@ -1503,13 +1503,14 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + int + ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph) ++ unsigned int hooknum, struct ip_vs_iphdr *ciph) + { + struct rtable *rt; /* Route to the other host */ + int rc; + int local; + int rt_mode, was_input; ++ bool has_ports = false; ++ unsigned int wlen; + + /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be + forwarded directly here, because there is no need to +@@ -1565,6 +1566,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + goto tx_error; + } + ++ wlen = ciph->len; ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) { ++ wlen += 2 * sizeof(__u16); /* Also mangle ports */ ++ has_ports = true; ++ } ++ + /* copy-on-write the packet before mangling it */ + if (skb_ensure_writable(skb, wlen)) + goto tx_error; +@@ -1572,7 +1580,7 @@ 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); ++ ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +@@ -1589,10 +1597,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + int + ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, unsigned int toff, +- unsigned int wlen, unsigned int hooknum, +- struct ip_vs_iphdr *ciph) ++ unsigned int hooknum, struct ip_vs_iphdr *ciph) + { ++ bool has_ports = false; + struct rt6_info *rt; /* Route to the other host */ ++ unsigned int wlen; + int rc; + int local; + int rt_mode; +@@ -1650,6 +1659,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + goto tx_error; + } + ++ wlen = ciph->len; ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) { ++ wlen += 2 * sizeof(__u16); /* Also mangle ports */ ++ has_ports = true; ++ } ++ + /* copy-on-write the packet before mangling it */ + if (skb_ensure_writable(skb, wlen)) + goto tx_error; +@@ -1657,7 +1673,7 @@ ip_vs_icmp_xmit_v6(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_v6(skb, pp, cp, 0, toff, ciph); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +-- +2.53.0 + diff --git a/queue-7.1/ipvs-fix-places-with-wrong-packet-offsets.patch b/queue-7.1/ipvs-fix-places-with-wrong-packet-offsets.patch new file mode 100644 index 0000000000..ecb81b68fe --- /dev/null +++ b/queue-7.1/ipvs-fix-places-with-wrong-packet-offsets.patch @@ -0,0 +1,624 @@ +From 839381e040ed167c0eb45aab70c834d12ddcebf0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:16 +0300 +Subject: ipvs: fix places with wrong packet offsets + +From: Julian Anastasov + +[ Upstream commit 15cab31a3730e05f0767b922a7450e5d784b2607 ] + +The offsets we use to packet headers and payloads should be +based on skb->data. We even already respect non-zero +network offset in ip_vs_fill_iph_skb() but some places +do it wrongly and support only zero offset which is expected +for the IP layer where IPVS has hooks. + +Change all places that instead of skb->data use offsets based +on the network header (skb_network_header, ip_hdr, etc) because +this doubles the network offset as noted by Sashiko. + +For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header +parsing done by the caller. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Link: https://sashiko.dev/#/patchset/20260710143733.29741-2-fw%40strlen.de +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 15 +-- + net/netfilter/ipvs/ip_vs_app.c | 4 +- + net/netfilter/ipvs/ip_vs_core.c | 133 +++++++++++++------------- + net/netfilter/ipvs/ip_vs_proto_sctp.c | 4 +- + net/netfilter/ipvs/ip_vs_proto_tcp.c | 4 +- + net/netfilter/ipvs/ip_vs_proto_udp.c | 4 +- + net/netfilter/ipvs/ip_vs_xmit.c | 26 ++--- + 7 files changed, 97 insertions(+), 93 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index 591bf5b4b894f..7b556ce03602e 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -1952,8 +1952,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, +- unsigned int hooknum, struct ip_vs_iphdr *iph); ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph); + void ip_vs_dest_dst_rcu_free(struct rcu_head *head); + + #ifdef CONFIG_IP_VS_IPV6 +@@ -1966,8 +1967,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); + int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, +- unsigned int hooknum, struct ip_vs_iphdr *iph); ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph); + #endif + + #ifdef CONFIG_SYSCTL +@@ -2039,11 +2041,12 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp) + } + + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir); ++ struct ip_vs_conn *cp, int dir, unsigned int toff); + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int dir); ++ struct ip_vs_conn *cp, int dir, unsigned int toff, ++ struct ip_vs_iphdr *ciph); + #endif + + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) +diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c +index b0e00be85cb12..11cbdbaf561da 100644 +--- a/net/netfilter/ipvs/ip_vs_app.c ++++ b/net/netfilter/ipvs/ip_vs_app.c +@@ -367,7 +367,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb, + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) + return 0; + +- th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); ++ th = (struct tcphdr *)(skb->data + ipvsh->len); + + /* + * Remember seq number in case this pkt gets resized +@@ -443,7 +443,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb, + if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) + return 0; + +- th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); ++ th = (struct tcphdr *)(skb->data + ipvsh->len); + + /* + * Remember seq number in case this pkt gets resized +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index c8b512725e6e0..cd5eb71543ec8 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -924,13 +924,12 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, + * - inout: 1=in->out, 0=out->in + */ + void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout) ++ struct ip_vs_conn *cp, int inout, unsigned int toff) + { + struct iphdr *iph = ip_hdr(skb); +- unsigned int icmp_offset = iph->ihl*4; +- struct icmphdr *icmph = (struct icmphdr *)(skb_network_header(skb) + +- icmp_offset); ++ struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); + struct iphdr *ciph = (struct iphdr *)(icmph + 1); ++ unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr); + + if (inout) { + iph->saddr = cp->vaddr.ip; +@@ -957,48 +956,45 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + + /* And finally the ICMP checksum */ + icmph->checksum = 0; +- icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset); ++ icmph->checksum = ip_vs_checksum_complete(skb, toff); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (inout) +- IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, +- "Forwarding altered outgoing ICMP"); ++ IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, ++ "Forwarding altered outgoing ICMP"); + else +- IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, +- "Forwarding altered incoming ICMP"); ++ IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, ++ "Forwarding altered incoming ICMP"); + } + + #ifdef CONFIG_IP_VS_IPV6 + void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, +- struct ip_vs_conn *cp, int inout) ++ struct ip_vs_conn *cp, int inout, unsigned int toff, ++ struct ip_vs_iphdr *ciph) + { + struct ipv6hdr *iph = ipv6_hdr(skb); +- unsigned int icmp_offset = 0; +- unsigned int offs = 0; /* header offset*/ + int protocol; + struct icmp6hdr *icmph; +- struct ipv6hdr *ciph; +- unsigned short fragoffs; ++ struct ipv6hdr *cih; + +- ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL); +- icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset); +- offs = icmp_offset + sizeof(struct icmp6hdr); +- ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs); ++ icmph = (struct icmp6hdr *)(skb->data + toff); ++ cih = (struct ipv6hdr *)(skb->data + ciph->off); + +- protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL); ++ protocol = ciph->protocol; + + if (inout) { + iph->saddr = cp->vaddr.in6; +- ciph->daddr = cp->vaddr.in6; ++ cih->daddr = cp->vaddr.in6; + } else { + iph->daddr = cp->daddr.in6; +- ciph->saddr = cp->daddr.in6; ++ cih->saddr = cp->daddr.in6; + } + + /* the TCP/UDP/SCTP port */ +- if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || +- IPPROTO_SCTP == protocol)) { +- __be16 *ports = (void *)(skb_network_header(skb) + offs); ++ if (!ciph->fragoffs && ++ (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || ++ protocol == IPPROTO_SCTP)) { ++ __be16 *ports = (void *)(skb->data + ciph->len); + + IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, + ntohs(inout ? ports[1] : ports[0]), +@@ -1011,19 +1007,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + + /* And finally the ICMP checksum */ + icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, +- skb->len - icmp_offset, ++ skb->len - toff, + IPPROTO_ICMPV6, 0); +- skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset; ++ skb->csum_start = skb_headroom(skb) + toff; + skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum); + skb->ip_summed = CHECKSUM_PARTIAL; + + if (inout) +- IP_VS_DBG_PKT(11, AF_INET6, pp, skb, +- (void *)ciph - (void *)iph, ++ IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, + "Forwarding altered outgoing ICMPv6"); + else +- IP_VS_DBG_PKT(11, AF_INET6, pp, skb, +- (void *)ciph - (void *)iph, ++ IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, + "Forwarding altered incoming ICMPv6"); + } + #endif +@@ -1033,37 +1027,38 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + */ + static int handle_response_icmp(int af, struct sk_buff *skb, + union nf_inet_addr *snet, +- __u8 protocol, struct ip_vs_conn *cp, ++ struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, +- unsigned int offset, unsigned int ihl, +- unsigned int hooknum) ++ struct ip_vs_iphdr *ciph, ++ unsigned int toff, unsigned int hooknum) + { + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; ++ unsigned int ctoff = ciph->len; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; + + /* Ensure the checksum is correct */ +- if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { ++ if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) { + /* Failed checksum! */ + IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", + IP_VS_DBG_ADDR(af, snet)); + goto out; + } + +- if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || +- IPPROTO_SCTP == protocol) +- offset += 2 * sizeof(__u16); +- if (skb_ensure_writable(skb, offset)) ++ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || ++ ciph->protocol == IPPROTO_SCTP) ++ ctoff += 2 * sizeof(__u16); ++ if (skb_ensure_writable(skb, ctoff)) + goto out; + + #ifdef CONFIG_IP_VS_IPV6 + if (af == AF_INET6) +- ip_vs_nat_icmp_v6(skb, pp, cp, 1); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); + else + #endif +- ip_vs_nat_icmp(skb, pp, cp, 1); ++ ip_vs_nat_icmp(skb, pp, cp, 1, toff); + + if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) + goto out; +@@ -1091,9 +1086,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + * Currently handles error types - unreachable, quench, ttl exceeded. + */ + static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, +- int *related, unsigned int hooknum) ++ int *related, unsigned int hooknum, ++ struct ip_vs_iphdr *ipvsh) + { +- struct iphdr *iph; + struct icmphdr _icmph, *ic; + struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ + struct ip_vs_iphdr ciph; +@@ -1108,17 +1103,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (ip_is_fragment(ip_hdr(skb))) { + if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) + return NF_STOLEN; ++ if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh)) ++ return NF_ACCEPT; + } + +- iph = ip_hdr(skb); +- offset = ihl = iph->ihl * 4; ++ ihl = ipvsh->len; ++ offset = ipvsh->len; + ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); + if (ic == NULL) + return NF_DROP; + + IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n", + ic->type, ntohs(icmp_id(ic)), +- &iph->saddr, &iph->daddr); ++ &ipvsh->saddr.ip, &ipvsh->daddr.ip); + + /* + * Work through seeing if this is for us. +@@ -1137,7 +1134,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + /* Now find the contained IP header */ + offset += sizeof(_icmph); + cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); +- if (cih == NULL) ++ if (!(cih && cih->version == 4 && cih->ihl >= 5)) + return NF_ACCEPT; /* The packet looks wrong, ignore */ + + pp = ip_vs_proto_get(cih->protocol); +@@ -1160,9 +1157,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, + if (!cp) + return NF_ACCEPT; + +- snet.ip = iph->saddr; +- return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp, +- pp, ciph.len, ihl, hooknum); ++ snet.ip = ipvsh->saddr.ip; ++ return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl, ++ hooknum); + } + + #ifdef CONFIG_IP_VS_IPV6 +@@ -1175,7 +1172,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + struct ip_vs_conn *cp; + struct ip_vs_protocol *pp; + union nf_inet_addr snet; +- unsigned int offset; + + *related = 1; + ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph); +@@ -1218,9 +1214,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + return NF_ACCEPT; + + snet.in6 = ciph.saddr.in6; +- offset = ciph.len; +- return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp, +- pp, offset, ipvsh->len, hooknum); ++ return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph, ++ ipvsh->len, hooknum); + } + #endif + +@@ -1546,7 +1541,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat + #endif + if (unlikely(iph.protocol == IPPROTO_ICMP)) { + int related; +- int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum); ++ int verdict = ip_vs_out_icmp(ipvs, skb, &related, ++ hooknum, &iph); + + if (related) + return verdict; +@@ -1754,9 +1750,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb, + */ + static int + ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, +- unsigned int hooknum) ++ unsigned int hooknum, struct ip_vs_iphdr *iph) + { +- struct iphdr *iph; + struct icmphdr _icmph, *ic; + struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ + struct ip_vs_iphdr ciph; +@@ -1766,7 +1761,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + unsigned int offset, offset2, ihl, verdict; + bool tunnel, new_cp = false; + union nf_inet_addr *raddr; +- char *outer_proto = "IPIP"; ++ char *outer_proto __maybe_unused = "IPIP"; + unsigned int hlen_ipip; + int ulen = 0; + +@@ -1776,17 +1771,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + if (ip_is_fragment(ip_hdr(skb))) { + if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) + return NF_STOLEN; ++ if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph)) ++ return NF_ACCEPT; + } + +- iph = ip_hdr(skb); +- offset = ihl = iph->ihl * 4; ++ ihl = iph->len; ++ offset = iph->len; + ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); + if (ic == NULL) + return NF_DROP; + + IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n", + ic->type, ntohs(icmp_id(ic)), +- &iph->saddr, &iph->daddr); ++ &iph->saddr.ip, &iph->daddr.ip); + + /* + * Work through seeing if this is for us. +@@ -1903,7 +1900,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { + /* Failed checksum! */ + IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", +- &iph->saddr); ++ &iph->saddr.ip); + goto out; + } + +@@ -1974,7 +1971,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || + IPPROTO_SCTP == cih->protocol) + offset += 2 * sizeof(__u16); +- verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph); ++ verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, ++ &ciph); + + out: + if (likely(!new_cp)) +@@ -2087,7 +2085,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + IPPROTO_SCTP == ciph.protocol) + offset += 2 * sizeof(__u16); /* Also mangle ports */ + +- verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph); ++ verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, ++ &ciph); + + out: + if (likely(!new_cp)) +@@ -2166,7 +2165,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state + if (unlikely(iph.protocol == IPPROTO_ICMP)) { + int related; + int verdict = ip_vs_in_icmp(ipvs, skb, &related, +- hooknum); ++ hooknum, &iph); + + if (related) + return verdict; +@@ -2302,6 +2301,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) + { + struct netns_ipvs *ipvs = net_ipvs(state->net); ++ struct ip_vs_iphdr iphdr; + int r; + + /* ipvs enabled in this netns ? */ +@@ -2311,10 +2311,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + if (state->pf == NFPROTO_IPV4) { + if (ip_hdr(skb)->protocol != IPPROTO_ICMP) + return NF_ACCEPT; ++ ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr); + #ifdef CONFIG_IP_VS_IPV6 + } else { +- struct ip_vs_iphdr iphdr; +- + ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr); + + if (iphdr.protocol != IPPROTO_ICMPV6) +@@ -2324,7 +2323,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, + #endif + } + +- return ip_vs_in_icmp(ipvs, skb, &r, state->hook); ++ return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr); + } + + static const struct nf_hook_ops ip_vs_ops4[] = { +diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c +index f6f732b7dfa86..3dbd3096e1637 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c +@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- sctph = (void *) skb_network_header(skb) + sctphoff; ++ sctph = (void *)skb->data + sctphoff; + + /* Only update csum if we really have to */ + if (sctph->source != cp->vport || payload_csum || +@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- sctph = (void *) skb_network_header(skb) + sctphoff; ++ sctph = (void *)skb->data + sctphoff; + + /* Only update csum if we really have to */ + if (sctph->dest != cp->dport || payload_csum || +diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c +index 533fce3e5e4e4..99a286fdc90c6 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c +@@ -179,7 +179,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- tcph = (void *)skb_network_header(skb) + tcphoff; ++ tcph = (void *)skb->data + tcphoff; + tcph->source = cp->vport; + + /* Adjust TCP checksums */ +@@ -260,7 +260,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- tcph = (void *)skb_network_header(skb) + tcphoff; ++ tcph = (void *)skb->data + tcphoff; + tcph->dest = cp->dport; + + /* +diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c +index de3597347542e..f32785682402d 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_udp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c +@@ -170,7 +170,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- udph = (void *)skb_network_header(skb) + udphoff; ++ udph = (void *)skb->data + udphoff; + udph->source = cp->vport; + + /* +@@ -254,7 +254,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + payload_csum = true; + } + +- udph = (void *)skb_network_header(skb) + udphoff; ++ udph = (void *)skb->data + udphoff; + udph->dest = cp->dport; + + /* +diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c +index 9fef4335da13f..c23401c789de3 100644 +--- a/net/netfilter/ipvs/ip_vs_xmit.c ++++ b/net/netfilter/ipvs/ip_vs_xmit.c +@@ -1502,8 +1502,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + */ + int + ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, unsigned int hooknum, +- struct ip_vs_iphdr *iph) ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph) + { + struct rtable *rt; /* Route to the other host */ + int rc; +@@ -1515,7 +1516,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + translate address/port back */ + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { + if (cp->packet_xmit) +- rc = cp->packet_xmit(skb, cp, pp, iph); ++ rc = cp->packet_xmit(skb, cp, pp, ciph); + else + rc = NF_ACCEPT; + /* do not touch skb anymore */ +@@ -1533,7 +1534,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; + local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode, +- NULL, iph); ++ NULL, ciph); + if (local < 0) + goto tx_error; + rt = skb_rtable(skb); +@@ -1565,13 +1566,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + } + + /* copy-on-write the packet before mangling it */ +- if (skb_ensure_writable(skb, offset)) ++ if (skb_ensure_writable(skb, wlen)) + goto tx_error; + + if (skb_cow(skb, rt->dst.dev->hard_header_len)) + goto tx_error; + +- ip_vs_nat_icmp(skb, pp, cp, 0); ++ ip_vs_nat_icmp(skb, pp, cp, 0, toff); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +@@ -1587,8 +1588,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + #ifdef CONFIG_IP_VS_IPV6 + int + ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, +- struct ip_vs_protocol *pp, int offset, unsigned int hooknum, +- struct ip_vs_iphdr *ipvsh) ++ struct ip_vs_protocol *pp, unsigned int toff, ++ unsigned int wlen, unsigned int hooknum, ++ struct ip_vs_iphdr *ciph) + { + struct rt6_info *rt; /* Route to the other host */ + int rc; +@@ -1600,7 +1602,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + translate address/port back */ + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { + if (cp->packet_xmit) +- rc = cp->packet_xmit(skb, cp, pp, ipvsh); ++ rc = cp->packet_xmit(skb, cp, pp, ciph); + else + rc = NF_ACCEPT; + /* do not touch skb anymore */ +@@ -1617,7 +1619,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; + local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest, +- &cp->daddr.in6, NULL, ipvsh, 0, rt_mode); ++ &cp->daddr.in6, NULL, ciph, 0, rt_mode); + if (local < 0) + goto tx_error; + rt = dst_rt6_info(skb_dst(skb)); +@@ -1649,13 +1651,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + } + + /* copy-on-write the packet before mangling it */ +- if (skb_ensure_writable(skb, offset)) ++ if (skb_ensure_writable(skb, wlen)) + goto tx_error; + + if (skb_cow(skb, rt->dst.dev->hard_header_len)) + goto tx_error; + +- ip_vs_nat_icmp_v6(skb, pp, cp, 0); ++ ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph); + + /* Another hack: avoid icmp_send in ip_fragment */ + skb->ignore_df = 1; +-- +2.53.0 + diff --git a/queue-7.1/ipvs-fix-the-checksum-validations.patch b/queue-7.1/ipvs-fix-the-checksum-validations.patch new file mode 100644 index 0000000000..f43dbfe761 --- /dev/null +++ b/queue-7.1/ipvs-fix-the-checksum-validations.patch @@ -0,0 +1,389 @@ +From 4392d1ee25269fc2546185750de904197af9b07b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:15:15 +0300 +Subject: ipvs: fix the checksum validations + +From: Julian Anastasov + +[ Upstream commit e876b75b9020a97bbdc79721e7fc749024891c65 ] + +ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6 +packets from clients. In fact, as for TCP/UDP we should +validate the checksum for ICMP packets only when we +mangle the packets on MASQ or on reply for tunnel. + +Also, Sashiko points out that handle_response_icmp() being +common for IPv4 and IPv6 is missing the pseudo-header +calculation while validating ICMPv6 messages from real +servers which is a problem if checksum is not validated +by the hardware. + +Fix the problems by creating ip_vs_checksum_common_check() +helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6. +Rely on the nf_checksum() for validating the ICMP messages +but use it also for TCP and UDP. + +Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP. + +IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum +validation on LOCAL_OUT (local clients or local real +servers) and on FORWARD (traffic from servers on LAN). +Do it only on LOCAL_IN, in case nf_checksum() is not +called on PRE_ROUTING. + +Also, ip_vs_checksum_complete() can be marked static. + +Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6") +Link: https://sashiko.dev/#/patchset/20260708180315.77413-1-ja%40ssi.bg +Signed-off-by: Julian Anastasov +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/ip_vs.h | 31 +++++++++++++++-- + net/netfilter/ipvs/ip_vs_core.c | 20 +++++++++-- + net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++---- + net/netfilter/ipvs/ip_vs_proto_tcp.c | 44 +++++------------------ + net/netfilter/ipvs/ip_vs_proto_udp.c | 50 ++++++--------------------- + 5 files changed, 74 insertions(+), 86 deletions(-) + +diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h +index 62433e48b1f9a..591bf5b4b894f 100644 +--- a/include/net/ip_vs.h ++++ b/include/net/ip_vs.h +@@ -25,7 +25,9 @@ + #include /* for union nf_inet_addr */ + #include + #include /* for struct ipv6hdr */ ++#include + #include ++#include + #if IS_ENABLED(CONFIG_NF_CONNTRACK) + #include + #endif +@@ -2044,8 +2046,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int dir); + #endif + +-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset); +- + static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) + { + __be32 diff[2] = { ~old, new }; +@@ -2071,6 +2071,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum) + return csum_partial(diff, sizeof(diff), oldsum); + } + ++static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af) ++{ ++ /* Checksum unnecessary or already validated? */ ++ if (skb_csum_unnecessary(skb)) ++ return false; ++ /* LOCAL_OUT ? */ ++ if (!skb->dev || skb->dev->flags & IFF_LOOPBACK) ++ return false; ++ /* !LOCAL_IN (FORWARD) ? */ ++ if (af == AF_INET6) { ++ if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL)) ++ return false; ++ } else { ++ if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL)) ++ return false; ++ } ++ return true; ++} ++ ++static inline bool ip_vs_checksum_common_check(struct sk_buff *skb, ++ int offset, int proto, int af) ++{ ++ if (!ip_vs_checksum_needed(skb, af)) ++ return true; ++ return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af); ++} ++ + /* Forget current conntrack (unconfirmed) and attach notrack entry */ + static inline void ip_vs_notrack(struct sk_buff *skb) + { +diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c +index bafab93451d03..c8b512725e6e0 100644 +--- a/net/netfilter/ipvs/ip_vs_core.c ++++ b/net/netfilter/ipvs/ip_vs_core.c +@@ -867,7 +867,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; } + + #endif + +-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) ++static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) + { + return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0)); + } +@@ -1038,13 +1038,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb, + unsigned int offset, unsigned int ihl, + unsigned int hooknum) + { ++ int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; + unsigned int verdict = NF_DROP; + + if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) + goto after_nat; + + /* Ensure the checksum is correct */ +- if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { ++ if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { + /* Failed checksum! */ + IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", + IP_VS_DBG_ADDR(af, snet)); +@@ -1898,7 +1899,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, + verdict = NF_DROP; + + /* Ensure the checksum is correct */ +- if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { ++ if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) && ++ !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { + /* Failed checksum! */ + IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", + &iph->saddr); +@@ -2064,6 +2066,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, + goto out; + } + ++ verdict = NF_DROP; ++ ++ /* Ensure the checksum is correct */ ++ if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ && ++ !ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6, ++ AF_INET6)) { ++ /* Failed checksum! */ ++ IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n", ++ &iph->saddr); ++ goto out; ++ } ++ + /* do the statistics and put it back */ + ip_vs_in_stats(cp, skb); + +diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c +index c67317be17dfa..f6f732b7dfa86 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c +@@ -11,7 +11,7 @@ + + static int + sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int sctphoff); ++ struct ip_vs_iphdr *iph); + + static int + sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) ++ if (!sctp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) ++ if (!sctp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int sctphoff) ++ struct ip_vs_iphdr *iph) + { ++ unsigned int sctphoff = iph->len; + struct sctphdr *sh; + __le32 cmp, val; + ++ if (!ip_vs_checksum_needed(skb, af)) ++ return 1; + sh = (struct sctphdr *)(skb->data + sctphoff); + cmp = sh->checksum; + val = sctp_compute_cksum(skb, sctphoff); + + if (val != cmp) { + /* CRC failure, dump it. */ +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); + return 0; + } + return 1; +diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c +index f86b763efcc4d..533fce3e5e4e4 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c +@@ -29,7 +29,7 @@ + + static int + tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int tcphoff); ++ struct ip_vs_iphdr *iph); + + static int + tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -166,7 +166,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) ++ if (!tcp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* Call application helper if needed */ +@@ -244,7 +244,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) ++ if (!tcp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -302,41 +302,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int tcphoff) ++ struct ip_vs_iphdr *iph) + { +- switch (skb->ip_summed) { +- case CHECKSUM_NONE: +- skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); +- fallthrough; +- case CHECKSUM_COMPLETE: +-#ifdef CONFIG_IP_VS_IPV6 +- if (af == AF_INET6) { +- if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, +- &ipv6_hdr(skb)->daddr, +- skb->len - tcphoff, +- IPPROTO_TCP, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- } else +-#endif +- if (csum_tcpudp_magic(ip_hdr(skb)->saddr, +- ip_hdr(skb)->daddr, +- skb->len - tcphoff, +- ip_hdr(skb)->protocol, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- break; +- default: +- /* No need to checksum. */ +- break; ++ if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) { ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); ++ return 0; + } +- + return 1; + } + +diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c +index 58f9e255927e2..de3597347542e 100644 +--- a/net/netfilter/ipvs/ip_vs_proto_udp.c ++++ b/net/netfilter/ipvs/ip_vs_proto_udp.c +@@ -25,7 +25,7 @@ + + static int + udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int udphoff); ++ struct ip_vs_iphdr *iph); + + static int + udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, +@@ -155,7 +155,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!udp_csum_check(cp->af, skb, pp, udphoff)) ++ if (!udp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -238,7 +238,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + int ret; + + /* Some checks before mangling */ +- if (!udp_csum_check(cp->af, skb, pp, udphoff)) ++ if (!udp_csum_check(cp->af, skb, pp, iph)) + return 0; + + /* +@@ -298,48 +298,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, + + static int + udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, +- unsigned int udphoff) ++ struct ip_vs_iphdr *iph) + { + struct udphdr _udph, *uh; + +- uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph); ++ uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph); + if (uh == NULL) + return 0; + +- if (uh->check != 0) { +- switch (skb->ip_summed) { +- case CHECKSUM_NONE: +- skb->csum = skb_checksum(skb, udphoff, +- skb->len - udphoff, 0); +- fallthrough; +- case CHECKSUM_COMPLETE: +-#ifdef CONFIG_IP_VS_IPV6 +- if (af == AF_INET6) { +- if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, +- &ipv6_hdr(skb)->daddr, +- skb->len - udphoff, +- IPPROTO_UDP, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- } else +-#endif +- if (csum_tcpudp_magic(ip_hdr(skb)->saddr, +- ip_hdr(skb)->daddr, +- skb->len - udphoff, +- ip_hdr(skb)->protocol, +- skb->csum)) { +- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, +- "Failed checksum for"); +- return 0; +- } +- break; +- default: +- /* No need to checksum. */ +- break; +- } ++ if (!uh->check) ++ return 1; ++ if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) { ++ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, ++ "Failed checksum for"); ++ return 0; + } + return 1; + } +-- +2.53.0 + diff --git a/queue-7.1/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch b/queue-7.1/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch new file mode 100644 index 0000000000..3181c694e7 --- /dev/null +++ b/queue-7.1/keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch @@ -0,0 +1,64 @@ +From a03473866411d7e8c2a1b1d742b366131655cb1f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:03 -0400 +Subject: keys: fix out-of-bounds read in keyring_get_key_chunk() + +From: Michael Bommarito + +[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ] + +For description-level chunks keyring_get_key_chunk() advances the read +pointer by level * sizeof(long) past the inline prefix but only +bounds-checks the prefix, so a long enough key description is read past +its kmemdup(desc, desc_len + 1) allocation. Compute the full byte +offset and bounds-check the description against it before reading. + +The walk only reaches a description-level chunk when two keys collide +through the hash, x, type and domain_tag chunks, so this is reached from +an unprivileged add_key(2) with a crafted pair of same-type keys whose +index hashes collide; KASAN reports a slab-out-of-bounds read. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index 7a2ee0ded7c93..085f7a743354c 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + unsigned long chunk = 0; + const u8 *d; + int desc_len = index_key->desc_len, n = sizeof(chunk); ++ unsigned int offset; + + level /= ASSOC_ARRAY_KEY_CHUNK_SIZE; + switch (level) { +@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + return (unsigned long)index_key->domain_tag; + default: + level -= 4; +- if (desc_len <= sizeof(index_key->desc)) ++ offset = sizeof(index_key->desc) + level * sizeof(long); ++ if (desc_len <= offset) + return 0; + +- d = index_key->description + sizeof(index_key->desc); +- d += level * sizeof(long); +- desc_len -= sizeof(index_key->desc); ++ d = index_key->description + offset; ++ desc_len -= offset; + if (desc_len > n) + desc_len = n; + do { +-- +2.53.0 + diff --git a/queue-7.1/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch b/queue-7.1/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch new file mode 100644 index 0000000000..aa65c3e268 --- /dev/null +++ b/queue-7.1/keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch @@ -0,0 +1,63 @@ +From 9b04ac2309139aa32d2d9a7a7d49729383c674e0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:15:04 -0400 +Subject: keys: make keyring key-chunk byte order agree with + keyring_diff_objects() + +From: Michael Bommarito + +[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ] + +keyring_get_key_chunk() loads description bytes into the index chunk low +address first, while keyring_diff_objects() numbers the first differing +bit from the low end and folds the absolute byte index into the level +without removing the inline-prefix offset the level already carries. +The two disagree on byte order and bit position, so the array can be +told two keys first differ at a bit that does not differ in the chunk +the walker uses, letting crafted descriptions collide into one node. + +Load the chunk in the order keyring_diff_objects() assumes and drop the +inline-prefix length when folding the byte index into the level. This +only changes the in-memory ordering used to place keys within a keyring; +add, search and read of non-colliding keys are unaffected. + +Fixes: f771fde82051 ("keys: Simplify key description management") +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Michael Bommarito +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/keyring.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index 085f7a743354c..15bf4af8f2821 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) + desc_len -= offset; + if (desc_len > n) + desc_len = n; ++ d += desc_len; + do { + chunk <<= 8; +- chunk |= *d++; ++ chunk |= *--d; + } while (--desc_len > 0); + return chunk; + } +@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data) + return -1; + + differ_plus_i: +- level += i; ++ level += i - (int)sizeof(a->desc); + differ: + i = level * 8 + __ffs(seg_a ^ seg_b); + return i; +-- +2.53.0 + diff --git a/queue-7.1/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch b/queue-7.1/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch new file mode 100644 index 0000000000..297ab3a4a7 --- /dev/null +++ b/queue-7.1/keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch @@ -0,0 +1,96 @@ +From 45f7ac188ea3e0393e7447921593f3c98d9c8f65 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 19:22:30 +0300 +Subject: KEYS: trusted: dcp: fix key_len validation and calc_blob_len() return + type + +From: Fabrice Derepas + +[ Upstream commit 35d661c98fe4733490f20b4311616a3c2c30abc0 ] + +Two correctness and type-hygiene issues exist in the DCP trusted keys +implementation. + +First, trusted_dcp_unseal() reads p->key_len from a user-supplied blob +without checking if it exceeds MAX_KEY_SIZE. If a crafted blob provides a +payload_len larger than 128, the subsequent do_aead_crypto() call writes +past the end of the p->key array into the adjacent p->blob buffer within +the same struct trusted_key_payload -- the caller's own input, not +unrelated kernel memory. While not exploitable, this violates strict array +bounds and triggers static analyzers. Fix this by adding a validation +check against MIN_KEY_SIZE and MAX_KEY_SIZE immediately after reading the +length, matching the checks already done in trusted_core.c. + +Second, calc_blob_len() calculates a sum in size_t that truncates to +unsigned int on 64-bit platforms. Because the DCP hardware is only present +on 32-bit i.MX SoC platforms, size_t and unsigned int are functionally +equivalent in production, making this truncation harmless in practice. +Nevertheless, updating the return type to size_t (and subsequently updating +'blen' in the seal/unseal paths) resolves type-narrowing warnings and +improves overall code hygiene. + +Fixes: 2e8a0f40a39c ("KEYS: trusted: Introduce NXP DCP-backed trusted keys") +Signed-off-by: Fabrice Derepas +Reviewed-by: David Gstir +Reviewed-by: Richard Weinberger +Reviewed-by: Jarkko Sakkinen +Tested-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/20260719163939.3624767-1-fabrice.derepas@canonical.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: Sasha Levin +--- + security/keys/trusted-keys/trusted_dcp.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +diff --git a/security/keys/trusted-keys/trusted_dcp.c b/security/keys/trusted-keys/trusted_dcp.c +index 7b6eb655df0cb..c078adebe190e 100644 +--- a/security/keys/trusted-keys/trusted_dcp.c ++++ b/security/keys/trusted-keys/trusted_dcp.c +@@ -69,7 +69,7 @@ static bool skip_zk_test; + module_param_named(dcp_skip_zk_test, skip_zk_test, bool, 0); + MODULE_PARM_DESC(dcp_skip_zk_test, "Don't test whether device keys are zero'ed"); + +-static unsigned int calc_blob_len(unsigned int payload_len) ++static size_t calc_blob_len(unsigned int payload_len) + { + return sizeof(struct dcp_blob_fmt) + payload_len + DCP_BLOB_AUTHLEN; + } +@@ -200,7 +200,8 @@ static int encrypt_blob_key(u8 *plain_key, u8 *encrypted_key) + static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob) + { + struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob; +- int blen, ret; ++ size_t blen; ++ int ret; + u8 *plain_blob_key; + + blen = calc_blob_len(p->key_len); +@@ -242,7 +243,8 @@ static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob) + static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob) + { + struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob; +- int blen, ret; ++ size_t blen; ++ int ret; + u8 *plain_blob_key = NULL; + + if (b->fmt_version != DCP_BLOB_VERSION) { +@@ -253,9 +255,14 @@ static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob) + } + + p->key_len = le32_to_cpu(b->payload_len); ++ if (p->key_len < MIN_KEY_SIZE || p->key_len > MAX_KEY_SIZE) { ++ ret = -EINVAL; ++ goto out; ++ } ++ + blen = calc_blob_len(p->key_len); + if (blen != p->blob_len) { +- pr_err("DCP blob has bad length: %i != %i\n", blen, ++ pr_err("DCP blob has bad length: %zu != %u\n", blen, + p->blob_len); + ret = -EINVAL; + goto out; +-- +2.53.0 + diff --git a/queue-7.1/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch b/queue-7.1/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch new file mode 100644 index 0000000000..c0ba9e381a --- /dev/null +++ b/queue-7.1/ksmbd-fix-use-after-free-in-__close_file_table_ids.patch @@ -0,0 +1,50 @@ +From 9e912bb30ff00c1a612395a63dae37630fd570f8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 10:04:19 +0900 +Subject: ksmbd: fix use-after-free in __close_file_table_ids() + +From: Namjae Jeon + +[ Upstream commit e7188199eff46a636f3436356f0aae039be6dd66 ] + +A ksmbd_file can remain alive after logical close while another session +holds a temporary reference obtained through ksmbd_lookup_fd_inode(). +ksmbd_close_fd() currently marks the file closed and drops the idr-owned +reference, but leaves the pointer published in the closing session's idr +until the final reference is dropped. + +If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final() +supplies the foreign session's file table to __ksmbd_close_fd(). The object +is then freed without being removed from its owner's idr, and the owner +session later dereferences the stale pointer during file-table teardown. + +Remove the volatile id from the owner's idr while ksmbd_close_fd() still +holds that table's lock, and clear volatile_id before dropping +the idr-owned reference. A later foreign final put then only performs +physical destruction and cannot remove the object from the wrong table. + +Fixes: 8510a043d334 ("ksmbd: increment reference count of parent fp") +Reported-by: Yunseong Kim +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs_cache.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index b12c0bb527e41..c7d49aac7470a 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -595,6 +595,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + fp = NULL; + else { + fp->f_state = FP_CLOSED; ++ idr_remove(ft->idr, id); ++ fp->volatile_id = KSMBD_NO_FID; + closed = true; + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; +-- +2.53.0 + diff --git a/queue-7.1/ksmbd-return-success-for-deferred-final-close.patch b/queue-7.1/ksmbd-return-success-for-deferred-final-close.patch new file mode 100644 index 0000000000..f38fb40207 --- /dev/null +++ b/queue-7.1/ksmbd-return-success-for-deferred-final-close.patch @@ -0,0 +1,64 @@ +From d48c0eb66c502f69fd3d102841ac4fabd122dd04 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 21 Jun 2026 19:41:08 +0900 +Subject: ksmbd: return success for deferred final close + +From: Namjae Jeon + +[ Upstream commit c5db4de8988f1a621556ca5c4537f77b766ca07d ] + +ksmbd_close_fd() marks an open file as FP_CLOSED and drops the file table +reference. If another in-flight request still holds a reference, the final +close is deferred until that request drops its reference. + +The function currently returns -EINVAL in that deferred-final-close case +because fp is cleared when the reference count does not reach zero. That +turns a valid close into STATUS_FILE_CLOSED. + +smb2.compound_find.compound_find_close sends QUERY_DIRECTORY and then +closes the same directory handle before receiving the find response. +The query holds a reference while it builds the response, so close must +mark the handle closed and return success even though final teardown is +delayed. Track whether the handle was successfully transitioned to +FP_CLOSED and return success when only the final close is deferred. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: e7188199eff4 ("ksmbd: fix use-after-free in __close_file_table_ids()") +Signed-off-by: Sasha Levin +--- + fs/smb/server/vfs_cache.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index aa0924c9fdf96..b12c0bb527e41 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -580,6 +580,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + { + struct ksmbd_file *fp; + struct ksmbd_file_table *ft; ++ bool closed = false; + + if (!has_file_id(id)) + return 0; +@@ -594,6 +595,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + fp = NULL; + else { + fp->f_state = FP_CLOSED; ++ closed = true; + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; + } +@@ -601,7 +603,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + write_unlock(&ft->lock); + + if (!fp) +- return -EINVAL; ++ return closed ? 0 : -EINVAL; + + __put_fd_final(work, fp); + return 0; +-- +2.53.0 + diff --git a/queue-7.1/ksmbd-use-memcmp-to-compare-clientguids.patch b/queue-7.1/ksmbd-use-memcmp-to-compare-clientguids.patch new file mode 100644 index 0000000000..0d5d8022d3 --- /dev/null +++ b/queue-7.1/ksmbd-use-memcmp-to-compare-clientguids.patch @@ -0,0 +1,53 @@ +From b7a07da076fa181a9ef91d75a9365cb147f87528 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 10:32:01 +0900 +Subject: ksmbd: use memcmp() to compare ClientGUIDs + +From: Namjae Jeon + +[ Upstream commit e8bb506e6ef749ac0336f3e579d8d02396b7d832 ] + +ClientGUID is a fixed-size binary value and can contain embedded NUL +bytes. strncmp() stops comparing at the first NUL byte, so different +ClientGUID values can incorrectly be treated as equal. + +Use memcmp() in SMB3 multichannel session binding and +FSCTL_VALIDATE_NEGOTIATE_INFO to compare all SMB2_CLIENT_GUID_SIZE +bytes. + +Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel") +Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") +Reported-by: Samu +Suggested-by: Samu +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/server/smb2pdu.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index c29850170b315..5df4a8cc4d2e6 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -1792,7 +1792,7 @@ int smb2_sess_setup(struct ksmbd_work *work) + goto out_err; + } + +- if (strncmp(conn->ClientGUID, sess->ClientGUID, ++ if (memcmp(conn->ClientGUID, sess->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) { + rc = -ENOENT; + goto out_err; +@@ -8186,7 +8186,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, + goto err_out; + } + +- if (strncmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { ++ if (memcmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { + ret = -EINVAL; + goto err_out; + } +-- +2.53.0 + diff --git a/queue-7.1/kvm-arm64-add-missing-hyp_enter-when-trapping-sysreg.patch b/queue-7.1/kvm-arm64-add-missing-hyp_enter-when-trapping-sysreg.patch new file mode 100644 index 0000000000..2ad3b3d98e --- /dev/null +++ b/queue-7.1/kvm-arm64-add-missing-hyp_enter-when-trapping-sysreg.patch @@ -0,0 +1,67 @@ +From c53677d86b6c396043d8d594206430cf7249e7db Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 17 Jun 2026 10:52:38 +0100 +Subject: KVM: arm64: Add missing hyp_enter when trapping sysreg + +From: Vincent Donnefort + +[ Upstream commit e7821048e8d72a94b1fb7422f8b45aa374ff076f ] + +Add a missing hypervisor event call for hyp_enter on sysreg trapping, +causing an unbalanced hyp_enter/hyp_exit. + +The enum hyp_enter_exit_reason is not ABI, so we can keep the ERET +reasons at the end for clarity. + +Fixes: 696dfec22b8e ("KVM: arm64: Add hyp_enter/hyp_exit events to nVHE/pKVM hyp") +Signed-off-by: Vincent Donnefort +Reviewed-by: Fuad Tabba +Tested-by: Fuad Tabba +Link: https://patch.msgid.link/20260617095238.1530121-1-vdonnefort@google.com +Signed-off-by: Marc Zyngier +Signed-off-by: Sasha Levin +--- + arch/arm64/include/asm/kvm_hypevents.h | 1 + + arch/arm64/kvm/hyp/nvhe/hyp-main.c | 1 + + arch/arm64/kvm/hyp_trace.c | 1 + + 3 files changed, 3 insertions(+) + +diff --git a/arch/arm64/include/asm/kvm_hypevents.h b/arch/arm64/include/asm/kvm_hypevents.h +index 743c49bd878f7..5f6e6789d1211 100644 +--- a/arch/arm64/include/asm/kvm_hypevents.h ++++ b/arch/arm64/include/asm/kvm_hypevents.h +@@ -12,6 +12,7 @@ + enum hyp_enter_exit_reason { + HYP_REASON_SMC, + HYP_REASON_HVC, ++ HYP_REASON_SYS, + HYP_REASON_PSCI, + HYP_REASON_HOST_ABORT, + HYP_REASON_GUEST_EXIT, +diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c +index a0da08caa6c27..4cd321c27d042 100644 +--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c ++++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c +@@ -925,6 +925,7 @@ void handle_trap(struct kvm_cpu_context *host_ctxt) + handle_host_mem_abort(host_ctxt); + break; + case ESR_ELx_EC_SYS64: ++ trace_hyp_enter(host_ctxt, HYP_REASON_SYS); + if (handle_host_mte(esr)) + break; + fallthrough; +diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c +index 3f4ba2034a155..210807edacf7f 100644 +--- a/arch/arm64/kvm/hyp_trace.c ++++ b/arch/arm64/kvm/hyp_trace.c +@@ -409,6 +409,7 @@ static const char *__hyp_enter_exit_reason_str(u8 reason) + static const char strs[][12] = { + "smc", + "hvc", ++ "sys", + "psci", + "host_abort", + "guest_exit", +-- +2.53.0 + diff --git a/queue-7.1/kvm-arm64-fix-hyp_trace-clock-disabling.patch b/queue-7.1/kvm-arm64-fix-hyp_trace-clock-disabling.patch new file mode 100644 index 0000000000..b16507c16a --- /dev/null +++ b/queue-7.1/kvm-arm64-fix-hyp_trace-clock-disabling.patch @@ -0,0 +1,85 @@ +From bbdbf62ecec70e4f922b63542c838f1cf8fbc5b5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 11:51:00 +0100 +Subject: KVM: arm64: Fix hyp_trace clock disabling +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Vincent Donnefort + +[ Upstream commit bbece712cfc7f286b2908ac120dcf700279d87eb ] + +Fix the disable path in hyp_trace_clock_enable(), which fell through to +re-initialize and reschedule the clock after cancelling the work. Return +early instead. + +While at it, cleanup hyp_trace_clock::lock which is unused and +hyp_trace_clock::running which is redundant: the trace_remote framework +already serializes calls to the callback enable_tracing. + +Fixes: b22888917fa4 ("KVM: arm64: Sync boot clock with the nVHE/pKVM hyp") +Signed-off-by: Vincent Donnefort +Reviewed-by: Fuad Tabba (✓ DKIM/linux.dev) +Link: https://patch.msgid.link/20260715105100.3178255-1-vdonnefort@google.com +Signed-off-by: Marc Zyngier +Signed-off-by: Sasha Levin +--- + arch/arm64/kvm/hyp_trace.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c +index c4b3ee5521313..ac6a2db317323 100644 +--- a/arch/arm64/kvm/hyp_trace.c ++++ b/arch/arm64/kvm/hyp_trace.c +@@ -37,8 +37,6 @@ static struct hyp_trace_clock { + u32 shift; + struct delayed_work work; + struct completion ready; +- struct mutex lock; +- bool running; + } hyp_clock; + + static void __hyp_clock_work(struct work_struct *work) +@@ -110,12 +108,9 @@ static void hyp_trace_clock_enable(struct hyp_trace_clock *hyp_clock, bool enabl + { + struct system_time_snapshot snap; + +- if (hyp_clock->running == enable) +- return; +- + if (!enable) { + cancel_delayed_work_sync(&hyp_clock->work); +- hyp_clock->running = false; ++ return; + } + + ktime_get_snapshot(&snap); +@@ -128,7 +123,6 @@ static void hyp_trace_clock_enable(struct hyp_trace_clock *hyp_clock, bool enabl + INIT_DELAYED_WORK(&hyp_clock->work, __hyp_clock_work); + schedule_delayed_work(&hyp_clock->work, msecs_to_jiffies(CLOCK_INIT_MS)); + wait_for_completion(&hyp_clock->ready); +- hyp_clock->running = true; + } + + /* Access to this struct within the trace_remote_callbacks are protected by the trace_remote lock */ +@@ -304,9 +298,15 @@ static void hyp_trace_unload(struct trace_buffer_desc *desc, void *priv) + + static int hyp_trace_enable_tracing(bool enable, void *priv) + { ++ int ret; ++ + hyp_trace_clock_enable(&hyp_clock, enable); + +- return kvm_call_hyp_nvhe(__tracing_enable, enable); ++ ret = kvm_call_hyp_nvhe(__tracing_enable, enable); ++ if (ret) ++ hyp_trace_clock_enable(&hyp_clock, !enable); ++ ++ return ret; + } + + static int hyp_trace_swap_reader_page(unsigned int cpu, void *priv) +-- +2.53.0 + diff --git a/queue-7.1/kvm-arm64-fix-hyp_trace_desc-allocation-size-in-hyp_.patch b/queue-7.1/kvm-arm64-fix-hyp_trace_desc-allocation-size-in-hyp_.patch new file mode 100644 index 0000000000..f773580b51 --- /dev/null +++ b/queue-7.1/kvm-arm64-fix-hyp_trace_desc-allocation-size-in-hyp_.patch @@ -0,0 +1,69 @@ +From 58adc51661cb8bba4fd7dba4fc699a0aae6e6524 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Jul 2026 12:48:19 +0100 +Subject: KVM: arm64: Fix hyp_trace_desc allocation size in hyp_trace_load() + +From: Vincent Donnefort + +[ Upstream commit ca28278d10ec234592989ad370d58294b9d43e0f ] + +The footprint calculated for struct hyp_trace_desc sizes only +trace_buffer_desc and do not take into account the other fields. It +worked so far thanks to the follow-up PAGE_ALIGN(). + +Fix the descriptor size and while at it, enforce an overflow check after +PAGE_ALIGN(). + +Reported-by: Sashiko +Fixes: 3aed038aac8d ("KVM: arm64: Add trace remote for the nVHE/pKVM hyp") +Signed-off-by: Vincent Donnefort +Reviewed-by: Fuad Tabba +Tested-by: Fuad Tabba +Link: https://patch.msgid.link/20260710114819.2689386-3-vdonnefort@google.com +Signed-off-by: Marc Zyngier +Signed-off-by: Sasha Levin +--- + arch/arm64/kvm/hyp_trace.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c +index 73c1731392c74..3f4ba2034a155 100644 +--- a/arch/arm64/kvm/hyp_trace.c ++++ b/arch/arm64/kvm/hyp_trace.c +@@ -229,18 +229,22 @@ static int hyp_trace_buffer_share_hyp(struct hyp_trace_buffer *trace_buffer) + static struct trace_buffer_desc *hyp_trace_load(unsigned long size, void *priv) + { + struct hyp_trace_buffer *trace_buffer = priv; ++ size_t desc_size, tb_desc_size; + struct hyp_trace_desc *desc; +- size_t desc_size; + int ret; + + if (WARN_ON(trace_buffer->desc)) + return ERR_PTR(-EINVAL); + +- desc_size = trace_buffer_desc_size(size, num_possible_cpus()); ++ tb_desc_size = trace_buffer_desc_size(size, num_possible_cpus()); ++ desc_size = size_add(tb_desc_size, offsetof(struct hyp_trace_desc, trace_buffer_desc)); + if (desc_size == SIZE_MAX) + return ERR_PTR(-E2BIG); + + desc_size = PAGE_ALIGN(desc_size); ++ if (!desc_size) ++ return ERR_PTR(-E2BIG); ++ + desc = (struct hyp_trace_desc *)alloc_pages_exact(desc_size, GFP_KERNEL); + if (!desc) + return ERR_PTR(-ENOMEM); +@@ -256,7 +260,7 @@ static struct trace_buffer_desc *hyp_trace_load(unsigned long size, void *priv) + if (ret) + goto err_free_desc; + +- ret = trace_remote_alloc_buffer(&desc->trace_buffer_desc, desc_size, size, ++ ret = trace_remote_alloc_buffer(&desc->trace_buffer_desc, tb_desc_size, size, + cpu_possible_mask); + if (ret) + goto err_free_backing; +-- +2.53.0 + diff --git a/queue-7.1/kvm-arm64-fix-potential-leak-in-hyp_trace_buffer_all.patch b/queue-7.1/kvm-arm64-fix-potential-leak-in-hyp_trace_buffer_all.patch new file mode 100644 index 0000000000..9d934f1009 --- /dev/null +++ b/queue-7.1/kvm-arm64-fix-potential-leak-in-hyp_trace_buffer_all.patch @@ -0,0 +1,59 @@ +From d3c78e1994556d8dcb2e0c86400e831a966bcfec Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Jul 2026 12:48:18 +0100 +Subject: KVM: arm64: Fix potential leak in + hyp_trace_buffer_alloc_bpages_backing + +From: Vincent Donnefort + +[ Upstream commit df7a9d376f7a388ecfacf8dfe0b5819ddfba6972 ] + +In the very unlikely event of a failure in __map_hyp, the allocated +backing pages are leaked in hyp_trace_buffer_alloc_bpages_backing(). Fix +this by freeing the pages on error. + +Fixes: 3aed038aac8d ("KVM: arm64: Add trace remote for the nVHE/pKVM hyp") +Reported-by: Sashiko +Reviewed-by: Fuad Tabba +Tested-by: Fuad Tabba +Signed-off-by: Vincent Donnefort +Link: https://patch.msgid.link/20260710114819.2689386-2-vdonnefort@google.com +Signed-off-by: Marc Zyngier +Signed-off-by: Sasha Levin +--- + arch/arm64/kvm/hyp_trace.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c +index ac6a2db317323..73c1731392c74 100644 +--- a/arch/arm64/kvm/hyp_trace.c ++++ b/arch/arm64/kvm/hyp_trace.c +@@ -154,6 +154,7 @@ static int hyp_trace_buffer_alloc_bpages_backing(struct hyp_trace_buffer *trace_ + int nr_bpages = (PAGE_ALIGN(size) / PAGE_SIZE) + 1; + size_t backing_size; + void *start; ++ int ret; + + backing_size = PAGE_ALIGN(sizeof(struct simple_buffer_page) * nr_bpages * + num_possible_cpus()); +@@ -162,10 +163,16 @@ static int hyp_trace_buffer_alloc_bpages_backing(struct hyp_trace_buffer *trace_ + if (!start) + return -ENOMEM; + ++ ret = __map_hyp(start, backing_size); ++ if (ret) { ++ free_pages_exact(start, backing_size); ++ return ret; ++ } ++ + trace_buffer->desc->bpages_backing_start = (unsigned long)start; + trace_buffer->desc->bpages_backing_size = backing_size; + +- return __map_hyp(start, backing_size); ++ return ret; + } + + static void hyp_trace_buffer_free_bpages_backing(struct hyp_trace_buffer *trace_buffer) +-- +2.53.0 + diff --git a/queue-7.1/kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch b/queue-7.1/kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch new file mode 100644 index 0000000000..f5e082aaea --- /dev/null +++ b/queue-7.1/kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch @@ -0,0 +1,121 @@ +From d39715e84a55145c825661d4c02a445d95ab0512 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 10:03:54 +0100 +Subject: KVM: arm64: Reject guest_memfd memslots when the VM has MTE + +From: Alexandru Elisei + +[ Upstream commit 679d7201c1f09e37fa1c12ce28d84079c17fc87f ] + +The user cannot use MTE on VMAs created by mapping a guest_memfd file, +as arch_calc_vm_flag_bits() does not set VM_MTE_ALLOWED. + +When creating a guest_memfd backed memslot, +kvm_arch_prepare_memory_region() rejects the memslot if MTE is enabled for +the VM and if guest_memfd has been mapped in a VMA that intersects the +memslot. + +However, the documentation for KVM_SET_USER_MEMORY_REGION2 explicitly +states that the only condition for userspace_addr is for it to be a legal +userspace address, but the mapping is not required to be valid nor +populated at memslot creation. + +If userspace sets userspace_addr to an address that hasn't been mapped, or +if userspace_addr belongs to a VMA that isn't backed by the guest_memfd +file, or if the VMA doesn't intersect the memslot, memslot creation is +successful and KVM ends up with a VM with MTE and guest_memfd-backed +memslots. + +The same happens if the order is reversed: when userspace enables MTE, KVM +does not check if memslots backed by guest_memfd are already present. + +Fix both issues by rejecting guest_memfd-backed memslots when MTE is +enabled, and by rejecting MTE when guest_memfd-backed memslots are already +present. + +Fixes: 32e200bd6e44 ("KVM: arm64: Enable support for guest_memfd backed memory") +Tested-by: Fuad Tabba +Reviewed-by: Fuad Tabba +Signed-off-by: Alexandru Elisei +Link: https://patch.msgid.link/20260722090354.94245-1-alexandru.elisei@arm.com +Signed-off-by: Marc Zyngier +Signed-off-by: Sasha Levin +--- + Documentation/virt/kvm/api.rst | 6 ++++++ + arch/arm64/kvm/arm.c | 25 +++++++++++++++++++------ + arch/arm64/kvm/mmu.c | 4 ++++ + 3 files changed, 29 insertions(+), 6 deletions(-) + +diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst +index 52bbbb553ce10..934ec29093e36 100644 +--- a/Documentation/virt/kvm/api.rst ++++ b/Documentation/virt/kvm/api.rst +@@ -8401,6 +8401,12 @@ When this capability is enabled all memory in memslots must be mapped as + attempts to create a memslot with an invalid mmap will result in an + -EINVAL return. + ++``guest_memfd``, even though it is an anonymous file, is not supported with MTE. ++Attempting to create a memslot backed by ``guest_memfd`` when the MTE capability ++is enabled, or attempting to enable the MTE capability after ++``guest_memfd``-backed memslots have been created, will result in an -EINVAL ++return. ++ + When enabled the VMM may make use of the ``KVM_ARM_MTE_COPY_TAGS`` ioctl to + perform a bulk copy of tags to/from the guest. + +diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c +index 9ffd5d4079e6c..00df8783c30df 100644 +--- a/arch/arm64/kvm/arm.c ++++ b/arch/arm64/kvm/arm.c +@@ -148,14 +148,27 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, + set_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER, + &kvm->arch.flags); + break; +- case KVM_CAP_ARM_MTE: +- mutex_lock(&kvm->lock); +- if (system_supports_mte() && !kvm->created_vcpus) { +- r = 0; +- set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags); ++ case KVM_CAP_ARM_MTE: { ++ struct kvm_memory_slot *memslot; ++ int bkt; ++ ++ guard(mutex)(&kvm->lock); ++ if (!system_supports_mte() || kvm->created_vcpus) ++ break; ++ ++ r = 0; ++ guard(mutex)(&kvm->slots_lock); ++ kvm_for_each_memslot(memslot, bkt, kvm_memslots(kvm)) { ++ if (kvm_slot_has_gmem(memslot)) { ++ r = -EINVAL; ++ break; ++ } + } +- mutex_unlock(&kvm->lock); ++ if (r == 0) ++ set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags); + break; ++ ++ } + case KVM_CAP_ARM_SYSTEM_SUSPEND: + r = 0; + set_bit(KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED, &kvm->arch.flags); +diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c +index 6e95514d5a76e..726827c85c732 100644 +--- a/arch/arm64/kvm/mmu.c ++++ b/arch/arm64/kvm/mmu.c +@@ -2625,6 +2625,10 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, + if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new)) + return -EINVAL; + ++ /* guest_memfd is incompatible with MTE. */ ++ if (kvm_slot_has_gmem(new) && kvm_has_mte(kvm)) ++ return -EINVAL; ++ + hva = new->userspace_addr; + reg_end = hva + (new->npages << PAGE_SHIFT); + +-- +2.53.0 + diff --git a/queue-7.1/kvm-arm64-vgic-fix-race-between-lpi-release-and-re-r.patch b/queue-7.1/kvm-arm64-vgic-fix-race-between-lpi-release-and-re-r.patch new file mode 100644 index 0000000000..8b0fa10c1e --- /dev/null +++ b/queue-7.1/kvm-arm64-vgic-fix-race-between-lpi-release-and-re-r.patch @@ -0,0 +1,196 @@ +From fb73c581f2842790299234ab782624a60383c734 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 12:51:37 +0200 +Subject: KVM: arm64: vgic: Fix race between LPI release and re-registration +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Carlos López + +[ Upstream commit cbfe2b24a1ea9de35032dbdd100fdc700f5be92d ] + +Fix a potential race between decrementing an LPI's reference count and +evicting that structure from the LPI xarray. + +LPI structures are maintained in the VGIC LPI xarray (dist->lpi_xa). +When the reference count of an LPI structure drops to zero, +vgic_release_lpi_locked() removes the structure from the xarray and +frees it under the xarray lock. + +However, the release of an LPI can race with a concurrent LPI +re-registration with the same INTID via vgic_add_lpi() on another CPU, +since the reference count drop and the xarray eviction are not performed +in a single atomic step. This can happen e.g. if the guest issues a +DISCARD while the LPI is still referenced from a vCPU's active-pending +list (ap_list), and the same INTID is re-mapped via MAPTI. + +Particularly, vgic_release_lpi_locked() is called from two distinct +paths: direct release via vgic_put_irq(), and deferred release via +vgic_release_deleted_lpis(). During direct release, the issue can result +in deleting a newly registered LPI from the xarray: + + CPU0 (Releasing LPI) CPU1 (Adding new LPI) + ==================== ===================== + vgic_put_irq() + __vgic_put_irq() + refcount_dec_and_test() + vgic_add_lpi() + xa_lock_irqsave() + old_irq = xa_load(.., intid) + vgic_try_get_irq_ref(old_irq) == false + new IRQ inserted --> __xa_store(.., intid, ..) + xa_unlock_irqrestore() + xa_lock_irqsave(); + vgic_release_lpi_locked() + __xa_erase(.., irq->intid) <-- BUG: new IRQ is erased + kfree_rcu(old_irq) + +During the deferred release path, the old IRQ can be leaked: + + CPU0 (Releasing LPI) CPU1 (Adding new LPI) + ==================== ===================== + vgic_put_irq_norelease() + __vgic_put_irq() + refcount_dec_and_test() + irq->pending_release = true + vgic_add_lpi() + xa_lock_irqsave() + old_irq = xa_load(.., intid) + vgic_try_get_irq_ref(oldirq) == false + BUG: old IRQ overwritten --> __xa_store(.., intid, ..) + xa_unlock_irqrestore() + + vgic_release_deleted_lpis() + xa_lock_irqsave() + xa_for_each() { .. } <-- old IRQ with pending_release = true + is gone, so it cannot be released + +To fix the direct release path, move the reference count drop inside +the xarray lock, making sure that vgic_add_lpi() never encounters the +to-be-released LPI. + +In the deferred release path, the refcount drop must happen under a raw +spinlock, so the xarray lock cannot be grabbed, and the same solution +does not work. Instead, update vgic_add_lpi(), so that if it evicts +an LPI from the xarray, it takes on the responsibility of freeing it. +Consequently, an LPI may now be freed concurrently after a deferred +release drops the refcount, so accessing the pending_release field is no +longer safe from use-after-free. Delete all uses of the flag, and update +vgic_release_deleted_lpis() to identify orphaned LPIs purely based on +their refcount. + +Reported-by: Claude:claude-opus-4-6 +Fixes: 3a08a6ca7c37 ("KVM: arm64: vgic-v3: Use bare refcount for VGIC LPIs") +Fixes: d54594accf73 ("KVM: arm64: vgic-v3: Erase LPIs from xarray outside of raw spinlocks") +Signed-off-by: Carlos López +Link: https://patch.msgid.link/20260715105137.3973823-4-clopez@suse.de +Signed-off-by: Marc Zyngier +Signed-off-by: Sasha Levin +--- + arch/arm64/kvm/vgic/vgic-its.c | 24 ++++++++++++++++-------- + arch/arm64/kvm/vgic/vgic.c | 18 ++++++++---------- + include/kvm/arm_vgic.h | 3 --- + 3 files changed, 24 insertions(+), 21 deletions(-) + +diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c +index 7abb685c65465..e9ef7686a9524 100644 +--- a/arch/arm64/kvm/vgic/vgic-its.c ++++ b/arch/arm64/kvm/vgic/vgic-its.c +@@ -116,18 +116,26 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid, + kfree(irq); + irq = oldirq; + } else { +- ret = xa_err(__xa_store(&dist->lpi_xa, intid, irq, 0)); +- } +- +- xa_unlock_irqrestore(&dist->lpi_xa, flags); ++ /* ++ * The entry is either empty or contains a dead LPI (refcount=0) ++ * from the deferred release path, pending cleanup by ++ * vgic_release_deleted_lpis(). Evict and free it if present. ++ */ ++ oldirq = __xa_store(&dist->lpi_xa, intid, irq, 0); ++ ret = xa_err(oldirq); ++ if (ret) { ++ xa_unlock_irqrestore(&dist->lpi_xa, flags); ++ kfree(irq); + +- if (ret) { +- xa_release(&dist->lpi_xa, intid); +- kfree(irq); ++ return ERR_PTR(ret); ++ } + +- return ERR_PTR(ret); ++ if (oldirq && !WARN_ON_ONCE(refcount_read(&oldirq->refcount))) ++ kfree_rcu(oldirq, rcu); + } + ++ xa_unlock_irqrestore(&dist->lpi_xa, flags); ++ + /* + * We "cache" the configuration table entries in our struct vgic_irq's. + * However we only have those structs for mapped IRQs, so we read in +diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c +index 4cd818ce90933..7fb9057fdd7ee 100644 +--- a/arch/arm64/kvm/vgic/vgic.c ++++ b/arch/arm64/kvm/vgic/vgic.c +@@ -147,11 +147,7 @@ static __must_check bool __vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq) + + static __must_check bool vgic_put_irq_norelease(struct kvm *kvm, struct vgic_irq *irq) + { +- if (!__vgic_put_irq(kvm, irq)) +- return false; +- +- irq->pending_release = true; +- return true; ++ return __vgic_put_irq(kvm, irq); + } + + void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq) +@@ -168,12 +164,14 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq) + guard(spinlock_irqsave)(&dist->lpi_xa.xa_lock); + } + +- if (!__vgic_put_irq(kvm, irq)) ++ if (!irq_is_lpi(kvm, irq->intid)) + return; + +- xa_lock_irqsave(&dist->lpi_xa, flags); +- vgic_release_lpi_locked(dist, irq); +- xa_unlock_irqrestore(&dist->lpi_xa, flags); ++ if (refcount_dec_and_lock_irqsave(&irq->refcount, ++ &dist->lpi_xa.xa_lock, &flags)) { ++ vgic_release_lpi_locked(dist, irq); ++ xa_unlock_irqrestore(&dist->lpi_xa, flags); ++ } + } + + static void vgic_release_deleted_lpis(struct kvm *kvm) +@@ -185,7 +183,7 @@ static void vgic_release_deleted_lpis(struct kvm *kvm) + xa_lock_irqsave(&dist->lpi_xa, flags); + + xa_for_each(&dist->lpi_xa, intid, irq) { +- if (irq->pending_release) ++ if (!refcount_read(&irq->refcount)) + vgic_release_lpi_locked(dist, irq); + } + +diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h +index 1388dc6028a9a..700d6c519d133 100644 +--- a/include/kvm/arm_vgic.h ++++ b/include/kvm/arm_vgic.h +@@ -242,9 +242,6 @@ struct vgic_irq { + * affinity reg (v3). + */ + +- bool pending_release:1; /* Used for LPIs only, unreferenced IRQ +- * pending a release */ +- + bool pending_latch:1; /* The pending latch state used to calculate + * the pending state for both level + * and edge triggered IRQs. */ +-- +2.53.0 + diff --git a/queue-7.1/kvm-arm64-vgic-mitigate-potential-lpi-registration-f.patch b/queue-7.1/kvm-arm64-vgic-mitigate-potential-lpi-registration-f.patch new file mode 100644 index 0000000000..59e0eef8f6 --- /dev/null +++ b/queue-7.1/kvm-arm64-vgic-mitigate-potential-lpi-registration-f.patch @@ -0,0 +1,76 @@ +From a5e52309cb95c494c782e3c9979dc9583a279cdb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 12:51:38 +0200 +Subject: KVM: arm64: vgic: Mitigate potential LPI registration failure +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Carlos López + +[ Upstream commit 21f12496fdd357ad4e1fcdd07dc80ab7378f7d24 ] + +Mitigate a potential failure when inserting a new LPI into the VGIC LPI +xarray. + +When vgic_add_lpi() is preparing to register a new LPI, it pre-allocates +an xarray entry using xa_reserve_irq(), so that it can later perform the +insertion under the xarray lock without allocating. + +However, since xa_reserve_irq() is called before acquiring such lock, +there is a potential race where xa_reserve_irq() observes a populated +entry, thus not performing the allocation, and another CPU removes that +entry before the xarray lock is grabbed to perform the insertion. + + CPU0 (Adding new LPI) CPU1 (Releasing LPI) + ===================== =================== + vgic_add_lpi() + /* Entry populated, does not allocate */ + xa_reserve_irq(.., intid, ..) + vgic_release_deleted_lpis() + xa_lock_irqsave() + vgic_release_lpi_locked() + xarray node freed --> __xa_erase(.., intid) + xa_unlock_irqrestore() + xa_lock_irqsave() + xa_load(.., intid) == NULL + vgic_try_get_irq_ref(NULL) == false + __xa_store(.., intid, irq, 0) <-- xarray node was freed, gfp=0 + cannot allocate, returns -ENOMEM + +This can happen e.g. if the guest issues a DISCARD while the LPI is +still referenced from a vCPU's active-pending list (ap_list), and the +same INTID is re-mapped via MAPTI. + +Mitigate this by passing GFP_NOWAIT to __xa_store(), so that the +allocation can happen under the lock in the rare case that this +condition is hit. Add __GFP_ACCOUNT as well to match xa_reserve_irq()'s +flags. + +Reported-by: Sashiko +Fixes: 1d6f83f60f79 ("KVM: arm64: vgic: Store LPIs in an xarray") +Signed-off-by: Carlos López +Link: https://patch.msgid.link/20260715105137.3973823-5-clopez@suse.de +Signed-off-by: Marc Zyngier +Signed-off-by: Sasha Levin +--- + arch/arm64/kvm/vgic/vgic-its.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c +index e9ef7686a9524..c15f1867adc20 100644 +--- a/arch/arm64/kvm/vgic/vgic-its.c ++++ b/arch/arm64/kvm/vgic/vgic-its.c +@@ -121,7 +121,8 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid, + * from the deferred release path, pending cleanup by + * vgic_release_deleted_lpis(). Evict and free it if present. + */ +- oldirq = __xa_store(&dist->lpi_xa, intid, irq, 0); ++ oldirq = __xa_store(&dist->lpi_xa, intid, irq, ++ GFP_NOWAIT | __GFP_ACCOUNT); + ret = xa_err(oldirq); + if (ret) { + xa_unlock_irqrestore(&dist->lpi_xa, flags); +-- +2.53.0 + diff --git a/queue-7.1/mshv-fix-duplicate-gsi-detection-for-gsi-0.patch b/queue-7.1/mshv-fix-duplicate-gsi-detection-for-gsi-0.patch new file mode 100644 index 0000000000..7b78ba7bd6 --- /dev/null +++ b/queue-7.1/mshv-fix-duplicate-gsi-detection-for-gsi-0.patch @@ -0,0 +1,51 @@ +From 64b50107e8669e2205e470bf25aec71d4bf94157 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 15:43:49 +0000 +Subject: mshv: Fix duplicate GSI detection for GSI 0 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Stanislav Kinsburskii + +[ Upstream commit 649dd135491945afa544351e3e6d4a727de87020 ] + +The duplicate routing entry check in mshv_update_routing_table() uses +guest_irq_num != 0 to detect whether a GSI slot is already occupied. +This fails for GSI 0 because its guest_irq_num is 0 both when the slot +is unused (zero-initialized) and when legitimately assigned. As a +result, duplicate entries for GSI 0 are silently accepted, with the +second entry overwriting the first — corrupting the routing table +without any error reported to userspace. + +While GSI 0 (legacy timer) is unlikely to appear in MSI-based routing +in practice, the check is semantically wrong — it conflates +"uninitialized" with "GSI number 0." Use girq_entry_valid instead, +which is explicitly set to true when an entry is populated and remains +zero for unused slots regardless of the GSI number. + +Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") +Signed-off-by: Stanislav Kinsburskii +Reviewed-by: Anirudh Rayabharam (Microsoft) +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_irq.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/hv/mshv_irq.c b/drivers/hv/mshv_irq.c +index b3142c84dcbc2..65a4ffc82d566 100644 +--- a/drivers/hv/mshv_irq.c ++++ b/drivers/hv/mshv_irq.c +@@ -51,7 +51,7 @@ int mshv_update_routing_table(struct mshv_partition *partition, + /* + * Allow only one to one mapping between GSI and MSI routing. + */ +- if (girq->guest_irq_num != 0) { ++ if (girq->girq_entry_valid) { + r = -EINVAL; + goto out; + } +-- +2.53.0 + diff --git a/queue-7.1/mshv-fix-level-triggered-check-on-uninitialized-data.patch b/queue-7.1/mshv-fix-level-triggered-check-on-uninitialized-data.patch new file mode 100644 index 0000000000..bd9d553edd --- /dev/null +++ b/queue-7.1/mshv-fix-level-triggered-check-on-uninitialized-data.patch @@ -0,0 +1,84 @@ +From 72c2bb4bfe716ba75f8a6af16acb8354c109a95f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 15:43:43 +0000 +Subject: mshv: Fix level-triggered check on uninitialized data +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Stanislav Kinsburskii + +[ Upstream commit 0289a67cd70bf9d3807e289f4efd643e16a6c6b4 ] + +In mshv_irqfd_assign(), the level-triggered validation for resample +irqfds checks irqfd_lapic_irq.lapic_control.level_triggered before +mshv_irqfd_update() has populated the field. Since the irqfd struct is +zero-allocated, level_triggered is always 0 at that point, causing the +check to always reject resample irqfds with -EINVAL. This makes +level-triggered interrupt resampling — used to avoid interrupt storms +with assigned devices — completely non-functional. + +Move the check after the mshv_irqfd_update() call, which resolves the +IRQ routing entry and populates irqfd_lapic_irq with the actual trigger +mode. + +Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") +Signed-off-by: Stanislav Kinsburskii +Reviewed-by: Anirudh Rayabharam (Microsoft) +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_eventfd.c | 25 ++++++++++++++----------- + 1 file changed, 14 insertions(+), 11 deletions(-) + +diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c +index 5995a62aff8d8..047e5bd432381 100644 +--- a/drivers/hv/mshv_eventfd.c ++++ b/drivers/hv/mshv_eventfd.c +@@ -473,6 +473,19 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, + init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc); + + spin_lock_irq(&pt->pt_irqfds_lock); ++ ret = 0; ++ hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) { ++ if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx) ++ continue; ++ /* This fd is used for another irq already. */ ++ ret = -EBUSY; ++ spin_unlock_irq(&pt->pt_irqfds_lock); ++ goto fail; ++ } ++ ++ idx = srcu_read_lock(&pt->pt_irq_srcu); ++ mshv_irqfd_update(pt, irqfd); ++ + #if IS_ENABLED(CONFIG_X86) + if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) && + !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) { +@@ -481,22 +494,12 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, + * Otherwise return with failure + */ + spin_unlock_irq(&pt->pt_irqfds_lock); ++ srcu_read_unlock(&pt->pt_irq_srcu, idx); + ret = -EINVAL; + goto fail; + } + #endif +- ret = 0; +- hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) { +- if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx) +- continue; +- /* This fd is used for another irq already. */ +- ret = -EBUSY; +- spin_unlock_irq(&pt->pt_irqfds_lock); +- goto fail; +- } + +- idx = srcu_read_lock(&pt->pt_irq_srcu); +- mshv_irqfd_update(pt, irqfd); + hlist_add_head(&irqfd->irqfd_hnode, &pt->pt_irqfds_list); + spin_unlock_irq(&pt->pt_irqfds_lock); + +-- +2.53.0 + diff --git a/queue-7.1/mshv-fix-missing-error-code-on-vp-allocation-failure.patch b/queue-7.1/mshv-fix-missing-error-code-on-vp-allocation-failure.patch new file mode 100644 index 0000000000..ef90b72f69 --- /dev/null +++ b/queue-7.1/mshv-fix-missing-error-code-on-vp-allocation-failure.patch @@ -0,0 +1,46 @@ +From 0965d8b05892ab74ed324cba10f11d810ccabeae Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 15:44:37 +0000 +Subject: mshv: Fix missing error code on VP allocation failure + +From: Stanislav Kinsburskii + +[ Upstream commit f546be6a19d24d02be576d8617cb26c7acb61594 ] + +In mshv_partition_ioctl_create_vp(), when kzalloc for the VP struct +fails, the code jumps to the cleanup path without setting ret. At that +point ret is 0 from the preceding successful mshv_vp_stats_map() call, +so the function returns success to userspace despite having failed to +create the VP. No fd is installed and no VP is registered in pt_vp_array, +but userspace has no way to know the operation failed. + +Set ret to -ENOMEM before jumping to the cleanup path. + +Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") +Signed-off-by: Stanislav Kinsburskii +Reviewed-by: Anirudh Rayabharam (Microsoft) +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_root_main.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c +index 146726cc4e9ba..644f9b10cbba4 100644 +--- a/drivers/hv/mshv_root_main.c ++++ b/drivers/hv/mshv_root_main.c +@@ -1117,8 +1117,10 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, + goto unmap_ghcb_page; + + vp = kzalloc_obj(*vp); +- if (!vp) ++ if (!vp) { ++ ret = -ENOMEM; + goto unmap_stats_pages; ++ } + + vp->vp_partition = mshv_partition_get(partition); + if (!vp->vp_partition) { +-- +2.53.0 + diff --git a/queue-7.1/mshv-fix-race-in-mshv_irqfd_deassign.patch b/queue-7.1/mshv-fix-race-in-mshv_irqfd_deassign.patch new file mode 100644 index 0000000000..ca584edaa6 --- /dev/null +++ b/queue-7.1/mshv-fix-race-in-mshv_irqfd_deassign.patch @@ -0,0 +1,68 @@ +From 406196c1969de2558a7686241b941cd8e887e78f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 15:43:15 +0000 +Subject: mshv: Fix race in mshv_irqfd_deassign + +From: Stanislav Kinsburskii + +[ Upstream commit 0762262ac3e70f65b3bb843fe892f8bac1562d08 ] + +mshv_irqfd_deactivate() and the hlist traversal of pt_irqfds_list +require pt->pt_irqfds_lock to be held, but mshv_irqfd_deassign() +omits it. This races with the EPOLLHUP path in mshv_irqfd_wakeup(), +which does take the lock before calling mshv_irqfd_deactivate(). + +Additionally, mshv_irqfd_deactivate() uses hlist_del() which poisons +the node pointers rather than resetting them. Since +mshv_irqfd_is_active() relies on hlist_unhashed() (checks pprev == +NULL), a poisoned node still appears active. If a concurrent path calls +mshv_irqfd_deactivate() again on the same irqfd, the guard fails to +prevent a double hlist_del() on poisoned pointers. + +Fix both issues: +- Add the missing spin_lock_irq/spin_unlock_irq around the list + traversal in mshv_irqfd_deassign(), matching mshv_irqfd_release(). +- Use hlist_del_init() instead of hlist_del() so the node is properly + marked as unhashed after removal, making the is_active guard reliable. + +Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") +Signed-off-by: Stanislav Kinsburskii +Reviewed-by: Anirudh Rayabharam (Microsoft) +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_eventfd.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c +index 90959f639dc32..5995a62aff8d8 100644 +--- a/drivers/hv/mshv_eventfd.c ++++ b/drivers/hv/mshv_eventfd.c +@@ -284,7 +284,7 @@ static void mshv_irqfd_deactivate(struct mshv_irqfd *irqfd) + if (!mshv_irqfd_is_active(irqfd)) + return; + +- hlist_del(&irqfd->irqfd_hnode); ++ hlist_del_init(&irqfd->irqfd_hnode); + + queue_work(irqfd_cleanup_wq, &irqfd->irqfd_shutdown); + } +@@ -541,13 +541,14 @@ static int mshv_irqfd_deassign(struct mshv_partition *pt, + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + ++ spin_lock_irq(&pt->pt_irqfds_lock); + hlist_for_each_entry_safe(irqfd, n, &pt->pt_irqfds_list, + irqfd_hnode) { + if (irqfd->irqfd_eventfd_ctx == eventfd && + irqfd->irqfd_irqnum == args->gsi) +- + mshv_irqfd_deactivate(irqfd); + } ++ spin_unlock_irq(&pt->pt_irqfds_lock); + + eventfd_ctx_put(eventfd); + +-- +2.53.0 + diff --git a/queue-7.1/mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch b/queue-7.1/mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch new file mode 100644 index 0000000000..b44653d04b --- /dev/null +++ b/queue-7.1/mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch @@ -0,0 +1,50 @@ +From 4315ec4df9a385b17c7c611c7b6ce74899701742 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 15:43:59 +0000 +Subject: mshv: Fix sleeping under spinlock in mshv_portid_alloc + +From: Stanislav Kinsburskii + +[ Upstream commit a9708e550d11a53b22d932cdbfaa10c26346a2d0 ] + +idr_alloc() is called with GFP_KERNEL inside idr_lock(), which holds a +spinlock. GFP_KERNEL allows the allocator to sleep, triggering a +sleeping-while-atomic bug. + +Fix by using idr_preload(GFP_KERNEL) before taking the lock to +pre-allocate memory in a sleepable context, then idr_alloc() with +GFP_NOWAIT inside the spinlock-protected section. + +Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") +Signed-off-by: Stanislav Kinsburskii +Reviewed-by: Anirudh Rayabharam (Microsoft) +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_portid_table.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/hv/mshv_portid_table.c b/drivers/hv/mshv_portid_table.c +index c349af1f0aaac..6f59b3e376247 100644 +--- a/drivers/hv/mshv_portid_table.c ++++ b/drivers/hv/mshv_portid_table.c +@@ -40,12 +40,14 @@ mshv_port_table_fini(void) + int + mshv_portid_alloc(struct port_table_info *info) + { +- int ret = 0; ++ int ret; + ++ idr_preload(GFP_KERNEL); + idr_lock(&port_table_idr); + ret = idr_alloc(&port_table_idr, info, PORTID_MIN, +- PORTID_MAX, GFP_KERNEL); ++ PORTID_MAX, GFP_NOWAIT); + idr_unlock(&port_table_idr); ++ idr_preload_end(); + + return ret; + } +-- +2.53.0 + diff --git a/queue-7.1/mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch b/queue-7.1/mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch new file mode 100644 index 0000000000..95013aca6e --- /dev/null +++ b/queue-7.1/mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch @@ -0,0 +1,94 @@ +From d8e8579dc9e92dcadcc554905b3693591d41b507 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 00:28:11 +0000 +Subject: mshv: Order pt_vp_array publish against irqfd assertion path + +From: Stanislav Kinsburskii + +[ Upstream commit b098dc869219c15dc49bf9cf63fb5fc1481d3373 ] + +mshv_partition_ioctl_create_vp() initialises a VP struct (allocations, +mutex_init, init_waitqueue_head, page mappings) and then publishes the +pointer into partition->pt_vp_array. Several ISR paths read this array +locklessly: the intercept ISR, the two scheduler ISRs, and +mshv_try_assert_irq_fast() on the irqfd fast path. + +Of these, only mshv_try_assert_irq_fast() can structurally race the +publish. It runs from an eventfd waker without holding pt_mutex, and +MSHV_IRQFD does not require the target lapic_apic_id (== vp_index) to +refer to an existing VP at registration time. A user can therefore +register an irqfd targeting a yet-to-be-created VP, then trigger +mshv_try_assert_irq_fast() concurrently with MSHV_CREATE_VP for the +same index. On weakly-ordered architectures the reader can observe a +non-NULL pointer in pt_vp_array before the initialising stores to the +VP struct become visible, leading to use of partially-initialised +fields (e.g. vp_register_page). + +The other ISR readers cannot reach this race: the hypervisor will not +generate intercept or scheduler messages for a VP that has never been +told to run, and the user can only call MSHV_RUN_VP on the VP fd +returned by MSHV_CREATE_VP, which by construction is returned after +the publish. Leave those readers as plain loads. + +Use smp_store_release() in mshv_partition_ioctl_create_vp() to publish +the pointer, and pair it with smp_load_acquire() in +mshv_try_assert_irq_fast(). On x86 these compile to plain accesses +under TSO; on ARM64 they emit one-instruction acquire/release barriers, +acceptable on this fast path. + +The destroy-side path (destroy_partition() clearing pt_vp_array[i] to +NULL after kfree(vp)) has a separate ordering and lifetime concern +that is out of scope here. + +Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") +Signed-off-by: Stanislav Kinsburskii +Reviewed-by: Anirudh Rayabharam (Microsoft) +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_eventfd.c | 9 ++++++++- + drivers/hv/mshv_root_main.c | 8 +++++++- + 2 files changed, 15 insertions(+), 2 deletions(-) + +diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c +index 047e5bd432381..06aef99c8298d 100644 +--- a/drivers/hv/mshv_eventfd.c ++++ b/drivers/hv/mshv_eventfd.c +@@ -169,7 +169,14 @@ static int mshv_try_assert_irq_fast(struct mshv_irqfd *irqfd) + return -EOPNOTSUPP; + #endif + +- vp = partition->pt_vp_array[irq->lapic_apic_id]; ++ /* ++ * Pairs with smp_store_release() in mshv_partition_ioctl_create_vp(). ++ * MSHV_IRQFD does not require the target lapic_apic_id to refer to an ++ * existing VP, so this read can race a concurrent VP creation; the ++ * acquire ensures that a non-NULL pointer implies the VP's ++ * initialising stores are visible. ++ */ ++ vp = smp_load_acquire(&partition->pt_vp_array[irq->lapic_apic_id]); + + if (!vp->vp_register_page) + return -EOPNOTSUPP; +diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c +index 644f9b10cbba4..8a15448e2ace9 100644 +--- a/drivers/hv/mshv_root_main.c ++++ b/drivers/hv/mshv_root_main.c +@@ -1157,7 +1157,13 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, + + /* already exclusive with the partition mutex for all ioctls */ + partition->pt_vp_count++; +- partition->pt_vp_array[args.vp_index] = vp; ++ /* ++ * Pairs with smp_load_acquire() in mshv_try_assert_irq_fast(), which ++ * can run concurrently from an irqfd waker without holding pt_mutex. ++ * The release ensures the VP's initialising stores are visible to any ++ * reader that observes a non-NULL pointer in pt_vp_array. ++ */ ++ smp_store_release(&partition->pt_vp_array[args.vp_index], vp); + + goto out; + +-- +2.53.0 + diff --git a/queue-7.1/mshv-publish-vp-to-pt_vp_array-before-installing-the.patch b/queue-7.1/mshv-publish-vp-to-pt_vp_array-before-installing-the.patch new file mode 100644 index 0000000000..a7c9e20bbd --- /dev/null +++ b/queue-7.1/mshv-publish-vp-to-pt_vp_array-before-installing-the.patch @@ -0,0 +1,96 @@ +From f68291f633c5200f9d42e71ce955bf50d5207226 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 May 2026 15:44:32 +0000 +Subject: mshv: Publish VP to pt_vp_array before installing the file descriptor +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Stanislav Kinsburskii + +[ Upstream commit 72e3b0311aa90568a4b42a64445d1d3e1dc5a34d ] + +mshv_partition_ioctl_create_vp() called anon_inode_getfd() before +publishing the new VP into partition->pt_vp_array. anon_inode_getfd() +includes fd_install(), so the fd was live in current->files before the +publish ran. + +A concurrent MSHV_RUN_VP ioctl on that fd does not serialise against the +in-progress MSHV_CREATE_VP — it takes vp->vp_mutex, not the partition +mutex. Once the VP starts running and traps, mshv_intercept_isr() can look +up partition->pt_vp_array[vp_index] and observe NULL, silently dropping the +intercept message. + +Split the fd creation: reserve an fd with get_unused_fd_flags(), create the +file with anon_inode_getfile(), publish the VP via smp_store_release(), and +finally call fd_install() as the userspace-visibility commit point. + +Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") +Signed-off-by: Stanislav Kinsburskii +Reviewed-by: Anirudh Rayabharam (Microsoft) +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_root_main.c | 29 ++++++++++++++++++++++------- + 1 file changed, 22 insertions(+), 7 deletions(-) + +diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c +index 8a15448e2ace9..cc2cfce2aefdb 100644 +--- a/drivers/hv/mshv_root_main.c ++++ b/drivers/hv/mshv_root_main.c +@@ -1072,6 +1072,8 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, + struct mshv_vp *vp; + struct page *intercept_msg_page, *register_page, *ghcb_page; + struct hv_stats_page *stats_pages[2]; ++ struct file *file; ++ int fd; + long ret; + + if (copy_from_user(&args, arg, sizeof(args))) +@@ -1146,14 +1148,18 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, + if (ret) + goto put_partition; + +- /* +- * Keep anon_inode_getfd last: it installs fd in the file struct and +- * thus makes the state accessible in user space. +- */ +- ret = anon_inode_getfd("mshv_vp", &mshv_vp_fops, vp, +- O_RDWR | O_CLOEXEC); +- if (ret < 0) ++ fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC); ++ if (fd < 0) { ++ ret = fd; + goto remove_debugfs_vp; ++ } ++ ++ file = anon_inode_getfile("mshv_vp", &mshv_vp_fops, vp, ++ O_RDWR | O_CLOEXEC); ++ if (IS_ERR(file)) { ++ ret = PTR_ERR(file); ++ goto put_unused_vp_fd; ++ } + + /* already exclusive with the partition mutex for all ioctls */ + partition->pt_vp_count++; +@@ -1165,8 +1171,17 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, + */ + smp_store_release(&partition->pt_vp_array[args.vp_index], vp); + ++ /* ++ * fd_install() is the userspace-visibility commit point. Must be the ++ * last operation that can fail or be observed. ++ */ ++ fd_install(fd, file); ++ ret = fd; ++ + goto out; + ++put_unused_vp_fd: ++ put_unused_fd(fd); + remove_debugfs_vp: + mshv_debugfs_vp_remove(vp); + put_partition: +-- +2.53.0 + diff --git a/queue-7.1/mshv_vtl-fix-fd-leak-in-mshv_ioctl_create_vtl.patch b/queue-7.1/mshv_vtl-fix-fd-leak-in-mshv_ioctl_create_vtl.patch new file mode 100644 index 0000000000..01c2f953e2 --- /dev/null +++ b/queue-7.1/mshv_vtl-fix-fd-leak-in-mshv_ioctl_create_vtl.patch @@ -0,0 +1,35 @@ +From b984fce105ff09d0a680c3ec7dae788d3789283b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Jul 2026 09:28:52 +0800 +Subject: mshv_vtl: fix fd leak in mshv_ioctl_create_vtl() + +From: Yi Xie + +[ Upstream commit ec08dd5b9ffa52980908805bdc8a55a8f1bc6667 ] + +put_unused_fd() if anon_inode_getfile() fails. + +Fixes: 7bfe3b8ea6e30 ("Drivers: hv: Introduce mshv_vtl driver") +Signed-off-by: Yi Xie +Reviewed-by: Hamza Mahfooz +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + drivers/hv/mshv_vtl_main.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c +index c194007014678..3ce4b4d6c11eb 100644 +--- a/drivers/hv/mshv_vtl_main.c ++++ b/drivers/hv/mshv_vtl_main.c +@@ -129,6 +129,7 @@ mshv_ioctl_create_vtl(void __user *user_arg, struct device *module_dev) + file = anon_inode_getfile("mshv_vtl", &mshv_vtl_fops, + vtl, O_RDWR); + if (IS_ERR(file)) { ++ put_unused_fd(fd); + kfree(vtl); + return PTR_ERR(file); + } +-- +2.53.0 + diff --git a/queue-7.1/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch b/queue-7.1/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch new file mode 100644 index 0000000000..fc45b63983 --- /dev/null +++ b/queue-7.1/net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch @@ -0,0 +1,40 @@ +From 804a670dea2e3fe8ff24d9123b9655541084a2f7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 06:26:05 +0000 +Subject: net: bridge: mrp: fix Option TLV length in MRP_Test frames + +From: David Corvaglia + +[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ] + +oui is a pointer, so sizeof(oui) is the pointer size. The MRA +Option TLV thus advertises a wrong length (15 vs 10 on x86_64), +causing misparsing of the frame on peers. Fix is to replace +with sizeof(*oui). + +Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA") +Signed-off-by: David Corvaglia +Acked-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/bridge/br_mrp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c +index f1aa67f7a0510..460be392af477 100644 +--- a/net/bridge/br_mrp.c ++++ b/net/bridge/br_mrp.c +@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp, + struct br_mrp_oui_hdr *oui = NULL; + u8 length; + +- length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) + ++ length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) + + MRP_OPT_PADDING; + br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length); + +-- +2.53.0 + diff --git a/queue-7.1/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch b/queue-7.1/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch new file mode 100644 index 0000000000..fac869a1df --- /dev/null +++ b/queue-7.1/net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch @@ -0,0 +1,78 @@ +From c83e25c52e059d9ebe6dc9ac65e02fe9c986daab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 07:29:01 +0000 +Subject: net: do not send ICMP/NDISC Redirects when peer allocation fails + +From: Eric Dumazet + +[ Upstream commit dbc3791e3b2472e1ccc08947e0f83b443470ff4f ] + +When inet_getpeer_v4() or inet_getpeer_v6() fails to allocate a peer entry +under memory pressure or tree size caps, redirect handlers previously fell +back to sending un-rate-limited ICMP/NDISC Redirect messages. + +In IPv4, ip_rt_send_redirect() called icmp_send() directly when peer == NULL. +In IPv6, ip6_forward() and ndisc_send_redirect() passed a NULL peer into +inet_peer_xrlim_allow(), which returned true when peer == NULL. + +Because ICMP/NDISC Redirects are not part of the default global rate limit +mask (sysctl_icmp_ratemask), sending redirects when peer == NULL creates +an un-rate-limited ICMP packet storm. + +Fix this by failing closed in ip_rt_send_redirect(), ip6_forward(), and +ndisc_send_redirect() when peer is NULL. + +Fixes: 92d868292634 ("inetpeer: Move ICMP rate limiting state into inet_peer entries.") +Signed-off-by: Eric Dumazet +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260724072901.1633601-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/route.c | 2 -- + net/ipv6/ip6_output.c | 2 +- + net/ipv6/ndisc.c | 2 ++ + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/net/ipv4/route.c b/net/ipv4/route.c +index 3d62d45d84bda..7cc9a7336c30a 100644 +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -892,8 +892,6 @@ void ip_rt_send_redirect(struct sk_buff *skb) + peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif); + if (!peer) { + rcu_read_unlock(); +- icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, +- rt_nexthop(rt, ip_hdr(skb)->daddr)); + return; + } + +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index b5d9c57fe439f..634f581e17114 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -641,7 +641,7 @@ int ip6_forward(struct sk_buff *skb) + /* Limit redirects both by destination (here) + and by source (inside ndisc_send_redirect) + */ +- if (inet_peer_xrlim_allow(peer, 1*HZ)) ++ if (peer && inet_peer_xrlim_allow(peer, 1*HZ)) + ndisc_send_redirect(skb, target); + rcu_read_unlock(); + } else { +diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c +index f867ec8d3d905..fe36b3f512850 100644 +--- a/net/ipv6/ndisc.c ++++ b/net/ipv6/ndisc.c +@@ -1707,6 +1707,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) + } + + peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr); ++ if (!peer) ++ goto release; + ret = inet_peer_xrlim_allow(peer, 1*HZ); + + if (!ret) +-- +2.53.0 + diff --git a/queue-7.1/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch b/queue-7.1/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch new file mode 100644 index 0000000000..9d95278c24 --- /dev/null +++ b/queue-7.1/net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch @@ -0,0 +1,53 @@ +From 279e8507cf6d5ca3ac5617a7dcef628cb0ac3243 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 05:52:14 +0100 +Subject: net: dsa: mt7530: check bus->read() errors in the MDIO regmap backend + +From: Daniel Golle + +[ Upstream commit b4ce102b2cd88424c5860fbbb20b9eb343a93bf4 ] + +bus->read() returns a negative errno on failure, but +mt7530_regmap_read() assigns it to a u16, truncating e.g. -ETIMEDOUT +into 0xff92, and returns success. The garbage word is then consumed as +register data, and read-modify-write cycles write it back to the +switch. Check both reads and propagate their errors. + +The same defect existed in mt7530_mii_read() since the driver was +introduced and moved into the regmap backend unchanged. + +Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch") +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/3c628e48276c2e5522c8795a6be60d11c7a76a7d.1785213071.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530-mdio.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/dsa/mt7530-mdio.c b/drivers/net/dsa/mt7530-mdio.c +index 11ea924a9f357..784dd58a71589 100644 +--- a/drivers/net/dsa/mt7530-mdio.c ++++ b/drivers/net/dsa/mt7530-mdio.c +@@ -55,8 +55,15 @@ mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) + if (ret < 0) + return ret; + +- lo = bus->read(bus, priv->mdiodev->addr, r); +- hi = bus->read(bus, priv->mdiodev->addr, 0x10); ++ ret = bus->read(bus, priv->mdiodev->addr, r); ++ if (ret < 0) ++ return ret; ++ lo = ret; ++ ++ ret = bus->read(bus, priv->mdiodev->addr, 0x10); ++ if (ret < 0) ++ return ret; ++ hi = ret; + + *val = (hi << 16) | (lo & 0xffff); + +-- +2.53.0 + diff --git a/queue-7.1/net-dsa-mt7530-error-out-on-failed-reads-in-atc-vtcr.patch b/queue-7.1/net-dsa-mt7530-error-out-on-failed-reads-in-atc-vtcr.patch new file mode 100644 index 0000000000..bacca5b5b8 --- /dev/null +++ b/queue-7.1/net-dsa-mt7530-error-out-on-failed-reads-in-atc-vtcr.patch @@ -0,0 +1,104 @@ +From 8a7afb22ba457cf399ecca41c6a194610e24d6f9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 05:52:21 +0100 +Subject: net: dsa: mt7530: error out on failed reads in ATC/VTCR command + polling + +From: Daniel Golle + +[ Upstream commit ed9adac35b8fac635f40e28461505e2e5b6c8fcc ] + +mt7530_fdb_cmd() and mt7530_vlan_cmd() poll the command register +through a helper which returns 0 when the underlying read fails. A +failed bus transaction thus clears ATC_BUSY/VTCR_BUSY and is treated +as successful command completion, and the subsequent ATC_INVALID and +VTCR_INVALID checks are defeated the same way. + +Poll using regmap_read_poll_timeout(), which stops on read errors and +propagates them, and check the completion status read as well. Take +the MDIO bus lock across the sequence as the switch regmap is set up +with locking disabled. + +Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch") +Fixes: 83163f7dca56 ("net: dsa: mediatek: add VLAN support for MT7530") +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/eea1d8f15c54375b3770c23e09fb3217df487169.1785213071.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 28 ++++++++++++++++++---------- + 1 file changed, 18 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 3c2a3029b10cf..292cde961f1af 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -248,15 +248,20 @@ mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp) + { + u32 val; + int ret; +- struct mt7530_dummy_poll p; + + /* Set the command operating upon the MAC address entries */ + val = ATC_BUSY | ATC_MAT(0) | cmd; + mt7530_write(priv, MT7530_ATC, val); + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_ATC); +- ret = readx_poll_timeout(_mt7530_read, &p, val, +- !(val & ATC_BUSY), 20, 20000); ++ mt7530_mutex_lock(priv); ++ ++ ret = regmap_read_poll_timeout(priv->regmap, MT7530_ATC, val, ++ !(val & ATC_BUSY), 20, 20000); ++ if (!ret) ++ ret = regmap_read(priv->regmap, MT7530_ATC, &val); ++ ++ mt7530_mutex_unlock(priv); ++ + if (ret < 0) { + dev_err(priv->dev, "reset timeout\n"); + return ret; +@@ -265,7 +270,6 @@ mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp) + /* Additional sanity for read command if the specified + * entry is invalid + */ +- val = mt7530_read(priv, MT7530_ATC); + if ((cmd == MT7530_FDB_READ) && (val & ATC_INVALID)) + return -EINVAL; + +@@ -1626,22 +1630,26 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port, + static int + mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid) + { +- struct mt7530_dummy_poll p; + u32 val; + int ret; + + val = VTCR_BUSY | VTCR_FUNC(cmd) | vid; + mt7530_write(priv, MT7530_VTCR, val); + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_VTCR); +- ret = readx_poll_timeout(_mt7530_read, &p, val, +- !(val & VTCR_BUSY), 20, 20000); ++ mt7530_mutex_lock(priv); ++ ++ ret = regmap_read_poll_timeout(priv->regmap, MT7530_VTCR, val, ++ !(val & VTCR_BUSY), 20, 20000); ++ if (!ret) ++ ret = regmap_read(priv->regmap, MT7530_VTCR, &val); ++ ++ mt7530_mutex_unlock(priv); ++ + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + return ret; + } + +- val = mt7530_read(priv, MT7530_VTCR); + if (val & VTCR_INVALID) { + dev_err(priv->dev, "read VTCR invalid\n"); + return -EINVAL; +-- +2.53.0 + diff --git a/queue-7.1/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch b/queue-7.1/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch new file mode 100644 index 0000000000..66b29a6815 --- /dev/null +++ b/queue-7.1/net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch @@ -0,0 +1,191 @@ +From e6df9521a62ccf179d4d3f0d3684b475e9d8cf95 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 05:52:29 +0100 +Subject: net: dsa: mt7530: error out on failed reads in MT7531 PHY polling + +From: Daniel Golle + +[ Upstream commit 77a9ebe8818cf6dd1699bd6728cb5d66307801d7 ] + +The MT7531 indirect PHY access functions poll MT7531_PHY_IAC through +a helper which returns 0 when the underlying read fails, so a failed +bus transaction clears MT7531_PHY_ACS_ST and the access carries on, +returning garbage PHY register data to phylib. + +Poll using regmap_read_poll_timeout(), which stops on read errors and +propagates them. These functions hold the MDIO bus lock across the +whole sequence, so the unlocked regmap accesses remain correct. Remove +the now-unused _mt7530_unlocked_read(). + +Fixes: c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch") +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/79e85d68d210cc37342978171aa6432aa2954333.1785213071.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/dsa/mt7530.c | 58 ++++++++++++++-------------------------- + 1 file changed, 20 insertions(+), 38 deletions(-) + +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 292cde961f1af..aa33d94e11b5f 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -191,12 +191,6 @@ mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val) + mt7530_mutex_unlock(priv); + } + +-static u32 +-_mt7530_unlocked_read(struct mt7530_dummy_poll *p) +-{ +- return mt7530_mii_read(p->priv, p->reg); +-} +- + static u32 + _mt7530_read(struct mt7530_dummy_poll *p) + { +@@ -553,16 +547,13 @@ static int + mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + int regnum) + { +- struct mt7530_dummy_poll p; + u32 reg, val; + int ret; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -572,8 +563,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad) | regnum; + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -583,8 +574,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad); + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -601,16 +592,13 @@ static int + mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + int regnum, u16 data) + { +- struct mt7530_dummy_poll p; + u32 val, reg; + int ret; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -620,8 +608,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad) | regnum; + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -631,8 +619,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + MT7531_MDIO_DEV_ADDR(devad) | data; + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -647,16 +635,13 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, + static int + mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) + { +- struct mt7530_dummy_poll p; + int ret; + u32 val; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -667,8 +652,8 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) + + mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +- !(val & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, ++ !(val & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -685,16 +670,13 @@ static int + mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, + u16 data) + { +- struct mt7530_dummy_poll p; + int ret; + u32 reg; + +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); +- + mt7530_mutex_lock(priv); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, +- !(reg & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg, ++ !(reg & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +@@ -705,8 +687,8 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, + + mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +- ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, +- !(reg & MT7531_PHY_ACS_ST), 20, 100000); ++ ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg, ++ !(reg & MT7531_PHY_ACS_ST), 20, 100000); + if (ret < 0) { + dev_err(priv->dev, "poll timeout\n"); + goto out; +-- +2.53.0 + diff --git a/queue-7.1/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch b/queue-7.1/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch new file mode 100644 index 0000000000..c755e4cb52 --- /dev/null +++ b/queue-7.1/net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch @@ -0,0 +1,40 @@ +From 212d8f91595abb774fe22c749ac838eeb5777ed4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 13:57:35 +0800 +Subject: net: ethernet: mtk_eth_soc: pass eth to mtk_handle_irq_rx in + poll_controller + +From: Chenguang Zhao + +[ Upstream commit e095f249e2209674f6366f6db0383a2b96e19239 ] + +mtk_handle_irq_rx expects a struct mtk_eth * (matching the request_irq +cookie), but mtk_poll_controller incorrectly passed the net_device *. +Calling ndo_poll_controller with CONFIG_NET_POLL_CONTROLLER enabled +would then crash. + +Fixes: 8186f6e382d8 ("net-next: mediatek: fix compile error inside mtk_poll_controller()") +Signed-off-by: Chenguang Zhao +Link: https://patch.msgid.link/20260723055735.885112-1-chenguang.zhao@linux.dev +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +index 5d291e50a47bb..351444fb48716 100644 +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3467,7 +3467,7 @@ static void mtk_poll_controller(struct net_device *dev) + + mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); + mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask); +- mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], dev); ++ mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], eth); + mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); + mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask); + } +-- +2.53.0 + diff --git a/queue-7.1/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch b/queue-7.1/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch new file mode 100644 index 0000000000..00a786d283 --- /dev/null +++ b/queue-7.1/net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch @@ -0,0 +1,48 @@ +From 3041b203a27af72da213522eb1df9196babb0f4e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 15:46:57 +0800 +Subject: net: libwx: fix FDIR ATR queue mismatch for software VLAN packets + +From: Jiawen Wu + +[ Upstream commit 732ed8f75ce583d115716f668dc80d730f3ad610 ] + +When TX VLAN hardware offload is disabled, VLAN tags are embedded in +the packet payload (software VLAN). Previously, the driver failed to +set the WX_TX_FLAGS_SW_VLAN flag for these packets during transmission. + +This missing flag caused the txgbe FDIR ATR logic to fall through to the +default hash calculation path. This resulted in asymmetric hash values +for Tx and Rx flows, preventing return packets from being steered to the +same queue as the transmit packets. + +Fix this by detecting software VLANs via eth_type_vlan(skb->protocol) +and setting WX_TX_FLAGS_SW_VLAN. This ensures the ATR feature selects +the correct hashing algorithm to maintain Tx/Rx queue symmetry. + +Fixes: b501d261a5b3 ("net: txgbe: add FDIR ATR support") +Signed-off-by: Jiawen Wu +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/0879DA38A8E32701+20260724074657.10773-1-jiawenwu@trustnetic.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/wangxun/libwx/wx_lib.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c +index 5c2cd5756f8a9..a553365b69943 100644 +--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c ++++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c +@@ -1606,6 +1606,8 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb, + if (skb_vlan_tag_present(skb)) { + tx_flags |= skb_vlan_tag_get(skb) << WX_TX_FLAGS_VLAN_SHIFT; + tx_flags |= WX_TX_FLAGS_HW_VLAN; ++ } else if (eth_type_vlan(skb->protocol)) { ++ tx_flags |= WX_TX_FLAGS_SW_VLAN; + } + + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && +-- +2.53.0 + diff --git a/queue-7.1/net-mana-create-separate-eqs-for-each-vport.patch b/queue-7.1/net-mana-create-separate-eqs-for-each-vport.patch new file mode 100644 index 0000000000..6aa7d6296b --- /dev/null +++ b/queue-7.1/net-mana-create-separate-eqs-for-each-vport.patch @@ -0,0 +1,763 @@ +From 49271e7baabbdf4f4b581ad306738f0f2a50bdaa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 4 Jun 2026 17:57:10 -0700 +Subject: net: mana: Create separate EQs for each vPort + +From: Long Li + +[ Upstream commit fa1a3b7bcd161028e038025c1a4a8963b2f56a95 ] + +To prepare for assigning vPorts to dedicated MSI-X vectors, remove EQ +sharing among the vPorts and create dedicated EQs for each vPort. + +Move the EQ definition from struct mana_context to struct mana_port_context +and update related support functions. Export mana_create_eq() and +mana_destroy_eq() for use by the MANA RDMA driver. + +RSS QPs now take a vport reference via pd->vport_use_count to ensure +EQs outlive all QP consumers. The vport must already be configured by +a raw QP before an RSS QP can be created. EQs are only destroyed when +the last QP (raw or RSS) on the PD releases its reference. + +Restrict each vport to a single RSS QP. The hardware only supports one +steering configuration (indirection table / hash key) per vport, and +mana_disable_vport_rx() on QP destroy disables RX globally for the +vport. Previously, creating a second RSS QP would silently overwrite +the first QP's steering config and destroy would blackhole all traffic. +This is now explicitly rejected with -EBUSY. Existing applications +(DPDK being the primary RDMA consumer) always create one RSS QP per +vport, so no real-world flows are affected. + +Reject cross-port PD sharing for both raw and RSS QPs. Since EQs and +vport configuration are per-port, a PD is bound to the port used by +its first raw QP. Subsequent QPs on the same PD must use the same +port or the creation fails with -EINVAL. Previously this was silently +broken: with shared EQs it appeared to work, but with per-vPort EQs +a cross-port PD would cause wrong-port EQ teardown and corruption. +DPDK creates one PD per port so no existing flows are affected. + +Serialize mana_set_channels() and the async per-port queue reset +handler against RDMA vport configuration to prevent RDMA from claiming +the vport during the detach/attach window. A channel_changing flag is +set under apc->vport_mutex before detach and checked by +mana_cfg_vport() when called from the RDMA path, blocking RDMA from +grabbing the vport during the entire window. When the port is down +and RDMA already holds the vport, the channel change is rejected with +-EBUSY. + +Signed-off-by: Long Li +Link: https://patch.msgid.link/20260605005717.2059954-2-longli@microsoft.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: e67cc80b50f5 ("net: mana: Return error code from mana_create_rxq()") +Signed-off-by: Sasha Levin +--- + drivers/infiniband/hw/mana/main.c | 40 ++++-- + drivers/infiniband/hw/mana/mana_ib.h | 14 ++ + drivers/infiniband/hw/mana/qp.c | 68 ++++++++- + drivers/net/ethernet/microsoft/mana/mana_en.c | 135 +++++++++++------- + .../ethernet/microsoft/mana/mana_ethtool.c | 23 ++- + include/net/mana/mana.h | 15 +- + 6 files changed, 228 insertions(+), 67 deletions(-) + +diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c +index 307ae01bf26f3..0e1727c53d5d2 100644 +--- a/drivers/infiniband/hw/mana/main.c ++++ b/drivers/infiniband/hw/mana/main.c +@@ -20,8 +20,10 @@ void mana_ib_uncfg_vport(struct mana_ib_dev *dev, struct mana_ib_pd *pd, + pd->vport_use_count--; + WARN_ON(pd->vport_use_count < 0); + +- if (!pd->vport_use_count) ++ if (!pd->vport_use_count) { ++ mana_destroy_eq(mpc); + mana_uncfg_vport(mpc); ++ } + + mutex_unlock(&pd->vport_mutex); + } +@@ -40,13 +42,27 @@ int mana_ib_cfg_vport(struct mana_ib_dev *dev, u32 port, struct mana_ib_pd *pd, + + pd->vport_use_count++; + if (pd->vport_use_count > 1) { ++ /* Reject cross-port PD sharing. EQs and vport config ++ * are per-port, so the PD must stay bound to the port ++ * that was configured on the first raw QP creation. ++ */ ++ if (pd->vport_port != port) { ++ pd->vport_use_count--; ++ mutex_unlock(&pd->vport_mutex); ++ ibdev_dbg(&dev->ib_dev, ++ "PD already bound to port %u\n", ++ pd->vport_port); ++ return -EINVAL; ++ } + ibdev_dbg(&dev->ib_dev, + "Skip as this PD is already configured vport\n"); + mutex_unlock(&pd->vport_mutex); + return 0; + } + +- err = mana_cfg_vport(mpc, pd->pdn, doorbell_id); ++ pd->vport_port = port; ++ ++ err = mana_cfg_vport(mpc, pd->pdn, doorbell_id, true); + if (err) { + pd->vport_use_count--; + mutex_unlock(&pd->vport_mutex); +@@ -55,15 +71,23 @@ int mana_ib_cfg_vport(struct mana_ib_dev *dev, u32 port, struct mana_ib_pd *pd, + return err; + } + +- mutex_unlock(&pd->vport_mutex); + +- pd->tx_shortform_allowed = mpc->tx_shortform_allowed; +- pd->tx_vp_offset = mpc->tx_vp_offset; ++ err = mana_create_eq(mpc); ++ if (err) { ++ mana_uncfg_vport(mpc); ++ pd->vport_use_count--; ++ } else { ++ pd->tx_shortform_allowed = mpc->tx_shortform_allowed; ++ pd->tx_vp_offset = mpc->tx_vp_offset; ++ } ++ ++ mutex_unlock(&pd->vport_mutex); + +- ibdev_dbg(&dev->ib_dev, "vport handle %llx pdid %x doorbell_id %x\n", +- mpc->port_handle, pd->pdn, doorbell_id); ++ if (!err) ++ ibdev_dbg(&dev->ib_dev, "vport handle %llx pdid %x doorbell_id %x\n", ++ mpc->port_handle, pd->pdn, doorbell_id); + +- return 0; ++ return err; + } + + int mana_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) +diff --git a/drivers/infiniband/hw/mana/mana_ib.h b/drivers/infiniband/hw/mana/mana_ib.h +index c9c94e86a72bd..da05966aff191 100644 +--- a/drivers/infiniband/hw/mana/mana_ib.h ++++ b/drivers/infiniband/hw/mana/mana_ib.h +@@ -102,6 +102,20 @@ struct mana_ib_pd { + struct mutex vport_mutex; + int vport_use_count; + ++ /* Port bound to this PD for raw QP usage. Only valid when ++ * vport_use_count > 0. A PD can only be associated with a ++ * single physical port because per-port EQs and vport ++ * configuration are tied to the PD's refcount. ++ */ ++ u32 vport_port; ++ ++ /* Only one RSS QP is allowed per vport because each RSS QP ++ * overwrites the vport steering config (indirection table / ++ * hash key) and mana_disable_vport_rx() on destroy would ++ * blackhole traffic for any other RSS QP on the same vport. ++ */ ++ bool has_rss_qp; ++ + bool tx_shortform_allowed; + u32 tx_vp_offset; + }; +diff --git a/drivers/infiniband/hw/mana/qp.c b/drivers/infiniband/hw/mana/qp.c +index 0fbcf449c134b..d3ee30b64f53a 100644 +--- a/drivers/infiniband/hw/mana/qp.c ++++ b/drivers/infiniband/hw/mana/qp.c +@@ -79,6 +79,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, + struct ib_qp_init_attr *attr, + struct ib_udata *udata) + { ++ struct mana_ib_pd *mana_pd = container_of(pd, struct mana_ib_pd, ibpd); + struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp); + struct mana_ib_dev *mdev = + container_of(pd->device, struct mana_ib_dev, ib_dev); +@@ -155,6 +156,30 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, + + qp->port = port; + ++ /* Take a reference on the vport to ensure EQs outlive this QP. ++ * The vport must already be configured by a raw QP on the ++ * same port — cross-port PD sharing is not supported. ++ * Only one RSS QP per vport is allowed because each one ++ * overwrites the steering config and destroy disables RX ++ * globally. ++ */ ++ mutex_lock(&mana_pd->vport_mutex); ++ if (!mana_pd->vport_use_count || mana_pd->vport_port != port) { ++ mutex_unlock(&mana_pd->vport_mutex); ++ ret = -EINVAL; ++ goto fail; ++ } ++ if (mana_pd->has_rss_qp) { ++ mutex_unlock(&mana_pd->vport_mutex); ++ ibdev_dbg(&mdev->ib_dev, ++ "Only one RSS QP per vport is supported\n"); ++ ret = -EBUSY; ++ goto fail; ++ } ++ mana_pd->vport_use_count++; ++ mana_pd->has_rss_qp = true; ++ mutex_unlock(&mana_pd->vport_mutex); ++ + for (i = 0; i < ind_tbl_size; i++) { + struct mana_obj_spec wq_spec = {}; + struct mana_obj_spec cq_spec = {}; +@@ -171,13 +196,19 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, + cq_spec.gdma_region = cq->queue.gdma_region; + cq_spec.queue_size = cq->cqe * COMP_ENTRY_SIZE; + cq_spec.modr_ctx_id = 0; +- eq = &mpc->ac->eqs[cq->comp_vector]; ++ /* Map comp_vector to a per-vPort EQ. The modulo handles ++ * the case where the RDMA-advertised num_comp_vectors ++ * exceeds this port's num_queues (e.g. after ethtool -L ++ * reduces it), remapping to an available EQ rather than ++ * failing the QP creation. ++ */ ++ eq = &mpc->eqs[cq->comp_vector % mpc->num_queues]; + cq_spec.attached_eq = eq->eq->id; + + ret = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_RQ, + &wq_spec, &cq_spec, &wq->rx_object); + if (ret) +- goto fail; ++ goto free_vport; + + /* The GDMA regions are now owned by the WQ object */ + wq->queue.gdma_region = GDMA_INVALID_DMA_REGION; +@@ -199,7 +230,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, + ret = mana_ib_install_cq_cb(mdev, cq); + if (ret) { + mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object); +- goto fail; ++ goto free_vport; + } + } + resp.num_entries = i; +@@ -210,7 +241,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, + ucmd.rx_hash_key_len, + ucmd.rx_hash_key); + if (ret) +- goto fail; ++ goto free_vport; + + ret = ib_copy_to_udata(udata, &resp, sizeof(resp)); + if (ret) { +@@ -226,7 +257,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, + + err_disable_vport_rx: + mana_disable_vport_rx(mpc); +-fail: ++free_vport: + while (i-- > 0) { + ibwq = ind_tbl->ind_tbl[i]; + ibcq = ibwq->cq; +@@ -237,6 +268,13 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd, + mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object); + } + ++ mutex_lock(&mana_pd->vport_mutex); ++ mana_pd->has_rss_qp = false; ++ mutex_unlock(&mana_pd->vport_mutex); ++ ++ mana_ib_uncfg_vport(mdev, mana_pd, port); ++ ++fail: + kfree(mana_ind_table); + + return ret; +@@ -299,7 +337,7 @@ static int mana_ib_create_qp_raw(struct ib_qp *ibqp, struct ib_pd *ibpd, + + err = mana_ib_cfg_vport(mdev, port, pd, mana_ucontext->doorbell); + if (err) +- return -ENODEV; ++ return err; + + qp->port = port; + +@@ -321,7 +359,14 @@ static int mana_ib_create_qp_raw(struct ib_qp *ibqp, struct ib_pd *ibpd, + cq_spec.queue_size = send_cq->cqe * COMP_ENTRY_SIZE; + cq_spec.modr_ctx_id = 0; + eq_vec = send_cq->comp_vector; +- eq = &mpc->ac->eqs[eq_vec]; ++ if (!mpc->eqs) { ++ err = -EINVAL; ++ goto err_destroy_queue; ++ } ++ /* Map comp_vector to a per-vPort EQ. See comment in ++ * mana_ib_create_qp_rss() for the modulo rationale. ++ */ ++ eq = &mpc->eqs[eq_vec % mpc->num_queues]; + cq_spec.attached_eq = eq->eq->id; + + err = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_SQ, &wq_spec, +@@ -785,14 +830,17 @@ static int mana_ib_destroy_qp_rss(struct mana_ib_qp *qp, + { + struct mana_ib_dev *mdev = + container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev); ++ struct ib_pd *ibpd = qp->ibqp.pd; + struct mana_port_context *mpc; + struct net_device *ndev; ++ struct mana_ib_pd *pd; + struct mana_ib_wq *wq; + struct ib_wq *ibwq; + int i; + + ndev = mana_ib_get_netdev(qp->ibqp.device, qp->port); + mpc = netdev_priv(ndev); ++ pd = container_of(ibpd, struct mana_ib_pd, ibpd); + + /* Disable vPort RX steering before destroying RX WQ objects. + * Otherwise firmware still routes traffic to the destroyed queues, +@@ -817,6 +865,12 @@ static int mana_ib_destroy_qp_rss(struct mana_ib_qp *qp, + mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object); + } + ++ mutex_lock(&pd->vport_mutex); ++ pd->has_rss_qp = false; ++ mutex_unlock(&pd->vport_mutex); ++ ++ mana_ib_uncfg_vport(mdev, pd, qp->port); ++ + return 0; + } + +diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c +index 352398ae0376e..9d8c91d36b3bc 100644 +--- a/drivers/net/ethernet/microsoft/mana/mana_en.c ++++ b/drivers/net/ethernet/microsoft/mana/mana_en.c +@@ -309,11 +309,18 @@ static void mana_per_port_queue_reset_work_handler(struct work_struct *work) + + rtnl_lock(); + ++ /* Block RDMA from grabbing the vport during the detach/attach ++ * window, same as mana_set_channels(). ++ */ ++ mutex_lock(&apc->vport_mutex); ++ apc->channel_changing = true; ++ mutex_unlock(&apc->vport_mutex); ++ + /* Pre-allocate buffers to prevent failure in mana_attach later */ + err = mana_pre_alloc_rxbufs(apc, ndev->mtu, apc->num_queues); + if (err) { + netdev_err(ndev, "Insufficient memory for reset post tx stall detection\n"); +- goto out; ++ goto clear_flag; + } + + err = mana_detach(ndev, false); +@@ -328,7 +335,11 @@ static void mana_per_port_queue_reset_work_handler(struct work_struct *work) + + dealloc_pre_rxbufs: + mana_pre_dealloc_rxbufs(apc); +-out: ++clear_flag: ++ mutex_lock(&apc->vport_mutex); ++ apc->channel_changing = false; ++ mutex_unlock(&apc->vport_mutex); ++ + rtnl_unlock(); + } + +@@ -1301,7 +1312,7 @@ void mana_uncfg_vport(struct mana_port_context *apc) + EXPORT_SYMBOL_NS(mana_uncfg_vport, "NET_MANA"); + + int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id, +- u32 doorbell_pg_id) ++ u32 doorbell_pg_id, bool check_channel_changing) + { + struct mana_config_vport_resp resp = {}; + struct mana_config_vport_req req = {}; +@@ -1326,7 +1337,8 @@ int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id, + * Ethernet usage on the same port. + */ + mutex_lock(&apc->vport_mutex); +- if (apc->vport_use_count > 0) { ++ if (apc->vport_use_count > 0 || ++ (check_channel_changing && apc->channel_changing)) { + mutex_unlock(&apc->vport_mutex); + return -EBUSY; + } +@@ -1621,78 +1633,84 @@ void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type, + } + EXPORT_SYMBOL_NS(mana_destroy_wq_obj, "NET_MANA"); + +-static void mana_destroy_eq(struct mana_context *ac) ++void mana_destroy_eq(struct mana_port_context *apc) + { ++ struct mana_context *ac = apc->ac; + struct gdma_context *gc = ac->gdma_dev->gdma_context; + struct gdma_queue *eq; + int i; + +- if (!ac->eqs) ++ if (!apc->eqs) + return; + +- debugfs_remove_recursive(ac->mana_eqs_debugfs); +- ac->mana_eqs_debugfs = NULL; ++ debugfs_remove_recursive(apc->mana_eqs_debugfs); ++ apc->mana_eqs_debugfs = NULL; + +- for (i = 0; i < gc->max_num_queues; i++) { +- eq = ac->eqs[i].eq; ++ for (i = 0; i < apc->num_queues; i++) { ++ eq = apc->eqs[i].eq; + if (!eq) + continue; + + mana_gd_destroy_queue(gc, eq); + } + +- kfree(ac->eqs); +- ac->eqs = NULL; ++ kfree(apc->eqs); ++ apc->eqs = NULL; + } ++EXPORT_SYMBOL_NS(mana_destroy_eq, "NET_MANA"); + +-static void mana_create_eq_debugfs(struct mana_context *ac, int i) ++static void mana_create_eq_debugfs(struct mana_port_context *apc, int i) + { +- struct mana_eq eq = ac->eqs[i]; ++ struct mana_eq eq = apc->eqs[i]; + char eqnum[32]; + + sprintf(eqnum, "eq%d", i); +- eq.mana_eq_debugfs = debugfs_create_dir(eqnum, ac->mana_eqs_debugfs); ++ eq.mana_eq_debugfs = debugfs_create_dir(eqnum, apc->mana_eqs_debugfs); + debugfs_create_u32("head", 0400, eq.mana_eq_debugfs, &eq.eq->head); + debugfs_create_u32("tail", 0400, eq.mana_eq_debugfs, &eq.eq->tail); + debugfs_create_file("eq_dump", 0400, eq.mana_eq_debugfs, eq.eq, &mana_dbg_q_fops); + } + +-static int mana_create_eq(struct mana_context *ac) ++int mana_create_eq(struct mana_port_context *apc) + { +- struct gdma_dev *gd = ac->gdma_dev; ++ struct gdma_dev *gd = apc->ac->gdma_dev; + struct gdma_context *gc = gd->gdma_context; + struct gdma_queue_spec spec = {}; + int err; + int i; + +- ac->eqs = kzalloc_objs(struct mana_eq, gc->max_num_queues); +- if (!ac->eqs) ++ if (WARN_ON(apc->eqs)) ++ return -EEXIST; ++ apc->eqs = kzalloc_objs(struct mana_eq, apc->num_queues); ++ if (!apc->eqs) + return -ENOMEM; + + spec.type = GDMA_EQ; + spec.monitor_avl_buf = false; + spec.queue_size = EQ_SIZE; + spec.eq.callback = NULL; +- spec.eq.context = ac->eqs; ++ spec.eq.context = apc->eqs; + spec.eq.log2_throttle_limit = LOG2_EQ_THROTTLE; + +- ac->mana_eqs_debugfs = debugfs_create_dir("EQs", gc->mana_pci_debugfs); ++ apc->mana_eqs_debugfs = ++ debugfs_create_dir("EQs", apc->mana_port_debugfs); + +- for (i = 0; i < gc->max_num_queues; i++) { ++ for (i = 0; i < apc->num_queues; i++) { + spec.eq.msix_index = (i + 1) % gc->num_msix_usable; +- err = mana_gd_create_mana_eq(gd, &spec, &ac->eqs[i].eq); ++ err = mana_gd_create_mana_eq(gd, &spec, &apc->eqs[i].eq); + if (err) { + dev_err(gc->dev, "Failed to create EQ %d : %d\n", i, err); + goto out; + } +- mana_create_eq_debugfs(ac, i); ++ mana_create_eq_debugfs(apc, i); + } + + return 0; + out: +- mana_destroy_eq(ac); ++ mana_destroy_eq(apc); + return err; + } ++EXPORT_SYMBOL_NS(mana_create_eq, "NET_MANA"); + + static int mana_fence_rq(struct mana_port_context *apc, struct mana_rxq *rxq) + { +@@ -2484,7 +2502,7 @@ static int mana_create_txq(struct mana_port_context *apc, + spec.monitor_avl_buf = false; + spec.queue_size = cq_size; + spec.cq.callback = mana_schedule_napi; +- spec.cq.parent_eq = ac->eqs[i].eq; ++ spec.cq.parent_eq = apc->eqs[i].eq; + spec.cq.context = cq; + err = mana_gd_create_mana_wq_cq(gd, &spec, &cq->gdma_cq); + if (err) +@@ -2882,13 +2900,12 @@ static void mana_create_rxq_debugfs(struct mana_port_context *apc, int idx) + static int mana_add_rx_queues(struct mana_port_context *apc, + struct net_device *ndev) + { +- struct mana_context *ac = apc->ac; + struct mana_rxq *rxq; + int err = 0; + int i; + + for (i = 0; i < apc->num_queues; i++) { +- rxq = mana_create_rxq(apc, i, &ac->eqs[i], ndev); ++ rxq = mana_create_rxq(apc, i, &apc->eqs[i], ndev); + if (!rxq) { + err = -ENOMEM; + netdev_err(ndev, "Failed to create rxq %d : %d\n", i, err); +@@ -2907,9 +2924,8 @@ static int mana_add_rx_queues(struct mana_port_context *apc, + return err; + } + +-static void mana_destroy_vport(struct mana_port_context *apc) ++static void mana_destroy_rxqs(struct mana_port_context *apc) + { +- struct gdma_dev *gd = apc->ac->gdma_dev; + struct mana_rxq *rxq; + u32 rxq_idx; + +@@ -2924,8 +2940,12 @@ static void mana_destroy_vport(struct mana_port_context *apc) + apc->rxqs[rxq_idx] = NULL; + } + } ++} ++ ++static void mana_destroy_vport(struct mana_port_context *apc) ++{ ++ struct gdma_dev *gd = apc->ac->gdma_dev; + +- mana_destroy_txq(apc); + mana_uncfg_vport(apc); + + if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode) +@@ -2946,11 +2966,14 @@ static int mana_create_vport(struct mana_port_context *apc, + return err; + } + +- err = mana_cfg_vport(apc, gd->pdid, gd->doorbell); +- if (err) ++ err = mana_cfg_vport(apc, gd->pdid, gd->doorbell, false); ++ if (err) { ++ if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode) ++ mana_pf_deregister_hw_vport(apc); + return err; ++ } + +- return mana_create_txq(apc, net); ++ return 0; + } + + static int mana_rss_table_alloc(struct mana_port_context *apc) +@@ -3236,21 +3259,36 @@ int mana_alloc_queues(struct net_device *ndev) + + err = mana_create_vport(apc, ndev); + if (err) { +- netdev_err(ndev, "Failed to create vPort %u : %d\n", apc->port_idx, err); ++ netdev_err(ndev, "Failed to create vPort %u : %d\n", ++ apc->port_idx, err); + return err; + } + ++ err = mana_create_eq(apc); ++ if (err) { ++ netdev_err(ndev, "Failed to create EQ on vPort %u: %d\n", ++ apc->port_idx, err); ++ goto destroy_vport; ++ } ++ ++ err = mana_create_txq(apc, ndev); ++ if (err) { ++ netdev_err(ndev, "Failed to create TXQ on vPort %u: %d\n", ++ apc->port_idx, err); ++ goto destroy_eq; ++ } ++ + err = netif_set_real_num_tx_queues(ndev, apc->num_queues); + if (err) { + netdev_err(ndev, + "netif_set_real_num_tx_queues () failed for ndev with num_queues %u : %d\n", + apc->num_queues, err); +- goto destroy_vport; ++ goto destroy_txq; + } + + err = mana_add_rx_queues(apc, ndev); + if (err) +- goto destroy_vport; ++ goto destroy_rxq; + + apc->rss_state = apc->num_queues > 1 ? TRI_STATE_TRUE : TRI_STATE_FALSE; + +@@ -3259,7 +3297,7 @@ int mana_alloc_queues(struct net_device *ndev) + netdev_err(ndev, + "netif_set_real_num_rx_queues () failed for ndev with num_queues %u : %d\n", + apc->num_queues, err); +- goto destroy_vport; ++ goto destroy_rxq; + } + + mana_rss_table_init(apc); +@@ -3267,19 +3305,25 @@ int mana_alloc_queues(struct net_device *ndev) + err = mana_config_rss(apc, TRI_STATE_TRUE, true, true); + if (err) { + netdev_err(ndev, "Failed to configure RSS table: %d\n", err); +- goto destroy_vport; ++ goto destroy_rxq; + } + + if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode) { + err = mana_pf_register_filter(apc); + if (err) +- goto destroy_vport; ++ goto destroy_rxq; + } + + mana_chn_setxdp(apc, mana_xdp_get(apc)); + + return 0; + ++destroy_rxq: ++ mana_destroy_rxqs(apc); ++destroy_txq: ++ mana_destroy_txq(apc); ++destroy_eq: ++ mana_destroy_eq(apc); + destroy_vport: + mana_destroy_vport(apc); + return err; +@@ -3390,6 +3434,9 @@ static int mana_dealloc_queues(struct net_device *ndev) + mana_fence_rqs(apc); + + /* Even in err case, still need to cleanup the vPort */ ++ mana_destroy_rxqs(apc); ++ mana_destroy_txq(apc); ++ mana_destroy_eq(apc); + mana_destroy_vport(apc); + + return 0; +@@ -3716,12 +3763,6 @@ int mana_probe(struct gdma_dev *gd, bool resuming) + + INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler); + +- err = mana_create_eq(ac); +- if (err) { +- dev_err(dev, "Failed to create EQs: %d\n", err); +- goto out; +- } +- + err = mana_query_device_cfg(ac, MANA_MAJOR_VERSION, MANA_MINOR_VERSION, + MANA_MICRO_VERSION, &num_ports, &bm_hostmode); + if (err) +@@ -3861,8 +3902,6 @@ void mana_remove(struct gdma_dev *gd, bool suspending) + free_netdev(ndev); + } + +- mana_destroy_eq(ac); +- + if (ac->per_port_queue_reset_wq) { + destroy_workqueue(ac->per_port_queue_reset_wq); + ac->per_port_queue_reset_wq = NULL; +diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c +index 6a4b42fe09445..a9440c4b8825b 100644 +--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c ++++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c +@@ -454,6 +454,11 @@ static int mana_set_coalesce(struct net_device *ndev, + return err; + } + ++/* mana_set_channels - change the number of queues on a port ++ * ++ * Returns -EBUSY if RDMA holds the vport with EQs sized to the ++ * current num_queues. ++ */ + static int mana_set_channels(struct net_device *ndev, + struct ethtool_channels *channels) + { +@@ -462,10 +467,22 @@ static int mana_set_channels(struct net_device *ndev, + unsigned int old_count = apc->num_queues; + int err; + ++ /* Set channel_changing to block RDMA from grabbing the vport ++ * during the detach/attach window. mana_cfg_vport() checks ++ * this flag under vport_mutex and returns -EBUSY if set. ++ */ ++ mutex_lock(&apc->vport_mutex); ++ if (!apc->port_is_up && apc->vport_use_count) { ++ mutex_unlock(&apc->vport_mutex); ++ return -EBUSY; ++ } ++ apc->channel_changing = true; ++ mutex_unlock(&apc->vport_mutex); ++ + err = mana_pre_alloc_rxbufs(apc, ndev->mtu, new_count); + if (err) { + netdev_err(ndev, "Insufficient memory for new allocations"); +- return err; ++ goto clear_flag; + } + + err = mana_detach(ndev, false); +@@ -483,6 +500,10 @@ static int mana_set_channels(struct net_device *ndev, + + out: + mana_pre_dealloc_rxbufs(apc); ++clear_flag: ++ mutex_lock(&apc->vport_mutex); ++ apc->channel_changing = false; ++ mutex_unlock(&apc->vport_mutex); + return err; + } + +diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h +index 4111b93169d2f..f8e05456888e4 100644 +--- a/include/net/mana/mana.h ++++ b/include/net/mana/mana.h +@@ -488,8 +488,6 @@ struct mana_context { + u8 bm_hostmode; + + struct mana_ethtool_hc_stats hc_stats; +- struct mana_eq *eqs; +- struct dentry *mana_eqs_debugfs; + struct workqueue_struct *per_port_queue_reset_wq; + /* Workqueue for querying hardware stats */ + struct delayed_work gf_stats_work; +@@ -509,6 +507,9 @@ struct mana_port_context { + + u8 mac_addr[ETH_ALEN]; + ++ struct mana_eq *eqs; ++ struct dentry *mana_eqs_debugfs; ++ + enum TRI_STATE rss_state; + + mana_handle_t default_rxobj; +@@ -555,6 +556,12 @@ struct mana_port_context { + struct mutex vport_mutex; + int vport_use_count; + ++ /* Set by mana_set_channels() under vport_mutex to block RDMA ++ * from grabbing the vport during the detach/attach window. ++ * Checked by mana_cfg_vport() when called from the RDMA path. ++ */ ++ bool channel_changing; ++ + /* Net shaper handle*/ + struct net_shaper_handle handle; + +@@ -1040,8 +1047,10 @@ void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type, + mana_handle_t wq_obj); + + int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id, +- u32 doorbell_pg_id); ++ u32 doorbell_pg_id, bool check_channel_changing); + void mana_uncfg_vport(struct mana_port_context *apc); ++int mana_create_eq(struct mana_port_context *apc); ++void mana_destroy_eq(struct mana_port_context *apc); + + struct net_device *mana_get_primary_netdev(struct mana_context *ac, + u32 port_index, +-- +2.53.0 + diff --git a/queue-7.1/net-mana-return-error-code-from-mana_create_rxq.patch b/queue-7.1/net-mana-return-error-code-from-mana_create_rxq.patch new file mode 100644 index 0000000000..9e3e8d2ff2 --- /dev/null +++ b/queue-7.1/net-mana-return-error-code-from-mana_create_rxq.patch @@ -0,0 +1,66 @@ +From 62455be4b0fb6ff972e7b9be1ca107ac193f762a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 04:37:59 -0700 +Subject: net: mana: Return error code from mana_create_rxq() + +From: Aditya Garg + +[ Upstream commit e67cc80b50f587cd1d8ffc8989dcec3291720bc3 ] + +mana_create_rxq() returns a struct mana_rxq pointer and returns NULL on +any failure. The caller, mana_add_rx_queues(), cannot tell what went +wrong and hardcodes the error as -ENOMEM. As a result the actual failure +reported by the lower layers (for example -EPROTO from a failed HW +request) is masked and every RX queue creation failure looks like an +out-of-memory error. + +Return an ERR_PTR() encoded error code from mana_create_rxq() on failure +instead of NULL. The caller now propagates the returned error code +directly instead of substituting -ENOMEM. + +Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") +Signed-off-by: Aditya Garg +Reviewed-by: Joe Damato +Link: https://patch.msgid.link/20260727113759.2881500-1-gargaditya@linux.microsoft.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/microsoft/mana/mana_en.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c +index 9d8c91d36b3bc..7e6bc6e5ff232 100644 +--- a/drivers/net/ethernet/microsoft/mana/mana_en.c ++++ b/drivers/net/ethernet/microsoft/mana/mana_en.c +@@ -2771,7 +2771,7 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc, + + rxq = kzalloc_flex(*rxq, rx_oobs, apc->rx_queue_size); + if (!rxq) +- return NULL; ++ return ERR_PTR(-ENOMEM); + + rxq->ndev = ndev; + rxq->num_rx_buf = apc->rx_queue_size; +@@ -2872,7 +2872,7 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc, + + mana_destroy_rxq(apc, rxq, false); + +- return NULL; ++ return ERR_PTR(err); + } + + static void mana_create_rxq_debugfs(struct mana_port_context *apc, int idx) +@@ -2906,8 +2906,8 @@ static int mana_add_rx_queues(struct mana_port_context *apc, + + for (i = 0; i < apc->num_queues; i++) { + rxq = mana_create_rxq(apc, i, &apc->eqs[i], ndev); +- if (!rxq) { +- err = -ENOMEM; ++ if (IS_ERR(rxq)) { ++ err = PTR_ERR(rxq); + netdev_err(ndev, "Failed to create rxq %d : %d\n", i, err); + goto out; + } +-- +2.53.0 + diff --git a/queue-7.1/net-phylink-put-link_gpio-if-phylink_create-fails.patch b/queue-7.1/net-phylink-put-link_gpio-if-phylink_create-fails.patch new file mode 100644 index 0000000000..5d5dcc3e78 --- /dev/null +++ b/queue-7.1/net-phylink-put-link_gpio-if-phylink_create-fails.patch @@ -0,0 +1,92 @@ +From 91bde6ea8b2feacb21ee2d7681a0ee5cb5091ed8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 17:08:05 +0200 +Subject: net: phylink: put link_gpio if phylink_create fails + +From: Christian Marangi + +[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ] + +In phylink_create() if phylink_register_sfp() returns an error, link_gpio +obtained by phylink_parse_fixedlink() is never released. While this is a +very unlikely scenario, it's worth to fix/handle this. + +This was present from the very first implementation of phylink but got +relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to +bridge between network devices and sfp cages") where additional function +were added after phylink_parse_fixedlink() making the release of link_gpio +needed if such additional function errored out. + +While at it, restructure the exit condition of phylink_create() with the +goto pattern to reduce code duplication on handling error conditions. + +Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages") +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/phy/phylink.c | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 087ac63f9193d..18d2ead97aa54 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1875,8 +1875,8 @@ struct phylink *phylink_create(struct phylink_config *config, + } else if (config->type == PHYLINK_DEV) { + pl->dev = config->dev; + } else { +- kfree(pl); +- return ERR_PTR(-EINVAL); ++ ret = -EINVAL; ++ goto free_pl; + } + + pl->mac_supports_eee_ops = phylink_mac_implements_lpi(mac_ops); +@@ -1909,28 +1909,29 @@ struct phylink *phylink_create(struct phylink_config *config, + phylink_validate(pl, pl->supported, &pl->link_config); + + ret = phylink_parse_mode(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto free_pl; + + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { + ret = phylink_parse_fixedlink(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + } + + pl->req_link_an_mode = pl->cfg_link_an_mode; + + ret = phylink_register_sfp(pl, fwnode); +- if (ret < 0) { +- kfree(pl); +- return ERR_PTR(ret); +- } ++ if (ret < 0) ++ goto release_link_gpio; + + return pl; ++ ++release_link_gpio: ++ if (pl->link_gpio) ++ gpiod_put(pl->link_gpio); ++free_pl: ++ kfree(pl); ++ return ERR_PTR(ret); + } + EXPORT_SYMBOL_GPL(phylink_create); + +-- +2.53.0 + diff --git a/queue-7.1/net-sched-cls_u32-validate-offshift-to-prevent-shift.patch b/queue-7.1/net-sched-cls_u32-validate-offshift-to-prevent-shift.patch new file mode 100644 index 0000000000..1957e8e910 --- /dev/null +++ b/queue-7.1/net-sched-cls_u32-validate-offshift-to-prevent-shift.patch @@ -0,0 +1,58 @@ +From 6dea08400f790d131efd1917d1edb4f49f469523 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 00:49:55 -0400 +Subject: net/sched: cls_u32: validate offshift to prevent shift-out-of-bounds + +From: Cen Zhang (Microsoft) + +[ Upstream commit aef96eead2860cbfa371e4471d4f04412213b958 ] + +u32_change() copies the user-provided tc_u32_sel.offshift (unsigned char, +0-255) into the kernel knode object without bounds validation. When a +packet later hits u32_classify() with TC_U32_VAROFFSET set, it evaluates +`ntohs(offmask & *data) >> offshift` where the left operand is a 16-bit +value promoted to a 32-bit int. Any offshift >= 32 is undefined behavior +per C11 6.5.7p3, triggerable by an unprivileged user via user/network +namespaces. + +UBSAN: shift-out-of-bounds in net/sched/cls_u32.c:236:43 +shift exponent 32 is too large for 32-bit type int + +Fix this by rejecting offshift >= 16 during filter creation in +u32_change(). + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Reported-by: AutonomousCodeSecurity@microsoft.com +Link: https://lore.kernel.org/all/20260720034514.23053-1-blbllhy@gmail.com +Signed-off-by: Cen Zhang (Microsoft) +Acked-by: Jamal Hadi Salim +Tested-by: Jamal Hadi Salim +Tested-by: Victor Nogueira +Link: https://patch.msgid.link/20260723044955.89471-1-blbllhy@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/sched/cls_u32.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c +index 8f30cc82181d9..ac98b1c2144a7 100644 +--- a/net/sched/cls_u32.c ++++ b/net/sched/cls_u32.c +@@ -1107,6 +1107,13 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, + goto erridr; + } + ++ if (s->offshift >= 16) { ++ NL_SET_ERR_MSG_MOD(extack, ++ "offshift must be less than 16"); ++ err = -EINVAL; ++ goto erridr; ++ } ++ + n = kzalloc_flex(*n, sel.keys, s->nkeys); + if (n == NULL) { + err = -ENOBUFS; +-- +2.53.0 + diff --git a/queue-7.1/net-sched-sch_cake-skip-clearing-unused-tins-during-.patch b/queue-7.1/net-sched-sch_cake-skip-clearing-unused-tins-during-.patch new file mode 100644 index 0000000000..38dc16f287 --- /dev/null +++ b/queue-7.1/net-sched-sch_cake-skip-clearing-unused-tins-during-.patch @@ -0,0 +1,87 @@ +From f5df8a781071580119e416fb33ff93b5e0c9affb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 23:14:52 +0200 +Subject: net/sched: sch_cake: skip clearing unused tins during rate adjustment +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Jonas Köppeler + +[ Upstream commit a3729e0df005a936ceb3c2b0d167f01a2b03f970 ] + +When cake_configure_rates() is called from the dequeue path with +rate_adjust=true, it only needs to update the rate parameters. The +loop that clears the unused tins is both unnecessary and harmful in +this path: + + - cake_clear_tin() overwrites q->cur_tin and q->cur_flow, which are + actively used by cake_dequeue(), corrupting the dequeue state. + - iterating over the unused tins and their internal queues to purge + packets adds needless overhead to the hot path. + +Skip the entire loop when rate_adjust is set, as neither +cake_clear_tin() nor the mtu_time update are needed when only the +rate changes. + +The clearing loop runs on every rate adjustment from the dequeue path, +clearing (max_tins - cur_tins) tins each time, so the cost grows the +fewer tins the configured mode actually uses. Testing cake_mq over veth +(8 rx/tx queues, 2 Gbit limit) with flent's [1] rrul and tcp_nup tests and +32 TCP upstreams shows a large drop in loaded latency and a throughput +gain, restoring behaviour to pre-15c2715a5264 levels: + + +------------+------+------+-------+-------+---------+ + | kernel | mode | test | base | load | tput | + | | | | (ms) | (ms) | (Mbit) | + +------------+------+------+-------+-------+---------+ + | net-next | be | rrul | 0.810 | 11.78 | 1469.67 | + | net-next | be | nup | 0.637 | 85.71 | 1243.15 | + | net-next | ds3 | rrul | 0.397 | 15.28 | 1770.06 | + | net-next | ds3 | nup | 0.351 | 15.98 | 1799.39 | + +------------+------+------+-------+-------+---------+ + | patched | be | rrul | 0.092 | 0.56 | 1873.40 | + | patched | be | nup | 0.109 | 1.82 | 1869.12 | + | patched | ds3 | rrul | 0.097 | 0.98 | 1866.10 | + | patched | ds3 | nup | 0.101 | 0.51 | 1861.79 | + +------------+------+------+-------+-------+---------+ + +The same trend holds on real hardware (IPQ8074A, 4 rx/tx queues, +OpenWrt): in besteffort mode the tcp_nup loaded latency drops from +~470 ms to ~4 ms. + +[1] https://flent.org + +Fixes: 15c2715a5264 ("net/sched: sch_cake: fixup cake_mq rate adjustment for diffserv config") +Signed-off-by: Jonas Köppeler +Tested-by: Mike Pham +Acked-by: Toke Høiland-Jørgensen +Link: https://patch.msgid.link/20260720-sch_cake-skip-clearing-tins-v2-1-e6a8b0275c73@tu-berlin.de +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/sched/sch_cake.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c +index b967b7153ad34..8965fe252471c 100644 +--- a/net/sched/sch_cake.c ++++ b/net/sched/sch_cake.c +@@ -2609,9 +2609,11 @@ static void cake_configure_rates(struct Qdisc *sch, u64 rate, bool rate_adjust) + break; + } + +- for (c = qd->tin_cnt; c < CAKE_MAX_TINS; c++) { +- cake_clear_tin(sch, c); +- qd->tins[c].cparams.mtu_time = qd->tins[ft].cparams.mtu_time; ++ if (!rate_adjust) { ++ for (c = qd->tin_cnt; c < CAKE_MAX_TINS; c++) { ++ cake_clear_tin(sch, c); ++ qd->tins[c].cparams.mtu_time = qd->tins[ft].cparams.mtu_time; ++ } + } + + qd->rate_ns = qd->tins[ft].tin_rate_ns; +-- +2.53.0 + diff --git a/queue-7.1/net-stmmac-fix-e2e-delay-mechanism.patch b/queue-7.1/net-stmmac-fix-e2e-delay-mechanism.patch new file mode 100644 index 0000000000..3ccdcb68b7 --- /dev/null +++ b/queue-7.1/net-stmmac-fix-e2e-delay-mechanism.patch @@ -0,0 +1,53 @@ +From fff9e4cfacc128b213abe7fb410327440ed12961 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 23:09:04 -0700 +Subject: net: stmmac: Fix E2E delay mechanism + +From: Nazim Amirul + +[ Upstream commit b041ed62aa6e3b2d7d36127e0e5d7bf2701f8231 ] + +For E2E delay mechanism, "received DELAY_REQ without timestamp" error +messages show up for dwmac v3.70+ and dwxgmac IPs. + +This issue affects socfpga platforms, Agilex7 (dwmac 3.70) and +Agilex5 (dwxgmac). According to the databook, to enable timestamping +for all events, the SNAPTYPSEL bits in the MAC_Timestamp_Control +register must be set to 2'b01, and the TSEVNTENA bit must be cleared +to 0'b0. + +Commit 3cb958027cb8 ("net: stmmac: Fix E2E delay mechanism") already +addresses this problem for all dwmacs above version v4.10. However, +same holds true for v3.70 and above, as well as for dwxgmac. Updates +the check accordingly. + +Fixes: 14f347334bf2 ("net: stmmac: Correctly take timestamp for PTPv2") +Fixes: f2fb6b6275eb ("net: stmmac: enable timestamp snapshot for required PTP packets in dwmac v5.10a") +Fixes: 3cb958027cb8 ("net: stmmac: Fix E2E delay mechanism") +Reviewed-by: Maxime Chevallier +Signed-off-by: Rohan G Thomas +Signed-off-by: Nazim Amirul +Link: https://patch.msgid.link/20260728060904.31993-1-muhammad.nazim.amirul.nazle.asmade@altera.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 755f48a34314b..f28367fbcaf81 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -755,7 +755,8 @@ static int stmmac_hwtstamp_set(struct net_device *dev, + config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + ptp_v2 = PTP_TCR_TSVER2ENA; + snap_type_sel = PTP_TCR_SNAPTYPSEL_1; +- if (priv->synopsys_id < DWMAC_CORE_4_10) ++ if (priv->synopsys_id < DWMAC_CORE_3_70 && ++ priv->plat->core_type != DWMAC_CORE_XGMAC) + ts_event_en = PTP_TCR_TSEVNTENA; + ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; + ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; +-- +2.53.0 + diff --git a/queue-7.1/net-sxgbe-check-descriptor-ring-allocation-failures.patch b/queue-7.1/net-sxgbe-check-descriptor-ring-allocation-failures.patch new file mode 100644 index 0000000000..35e2c1f886 --- /dev/null +++ b/queue-7.1/net-sxgbe-check-descriptor-ring-allocation-failures.patch @@ -0,0 +1,48 @@ +From 505286c76c6a8230226c8fbc1fc17b60d24b1726 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:20 +0800 +Subject: net: sxgbe: check descriptor ring allocation failures + +From: Chenguang Zhao + +[ Upstream commit 51b093a7ba27476e1f639455f005e8d2e75390e4 ] + +sxgbe_open() ignores the return value of init_dma_desc_rings() and +continues to program DMA with invalid ring addresses when allocation +fails. Check the return value and disconnect the PHY on failure. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index 9b48a587d5c2e..70cf3619555f9 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -1078,7 +1078,9 @@ static int sxgbe_open(struct net_device *dev) + priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE); + priv->tx_tc = TC_DEFAULT; + priv->rx_tc = TC_DEFAULT; +- init_dma_desc_rings(dev); ++ ret = init_dma_desc_rings(dev); ++ if (ret) ++ goto init_phy_error; + + /* DMA initialization and SW reset */ + ret = sxgbe_init_dma_engine(priv); +@@ -1187,6 +1189,7 @@ static int sxgbe_open(struct net_device *dev) + + init_error: + free_dma_desc_resources(priv); ++init_phy_error: + if (dev->phydev) + phy_disconnect(dev->phydev); + phy_error: +-- +2.53.0 + diff --git a/queue-7.1/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch b/queue-7.1/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch new file mode 100644 index 0000000000..cb82f15c4b --- /dev/null +++ b/queue-7.1/net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch @@ -0,0 +1,50 @@ +From b765ab3c94d70a7f1ea49d77e9014906f96d5770 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 10:18:19 +0800 +Subject: net: sxgbe: free TX rings on RX allocation failure + +From: Chenguang Zhao + +[ Upstream commit c870f7e2890b9f78ac84515a9809cc5c183c975e ] + +When RX descriptor ring allocation fails, init_dma_desc_rings() only +frees the partially allocated RX rings and returns. The TX rings that +were allocated earlier in the same function are leaked. + +Rearrange error labels to clean up TX rings upon RX failures. + +Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") +Signed-off-by: Chenguang Zhao +Reviewed-by: Vadim Fedorenko +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +index 5051ada43d2fa..9b48a587d5c2e 100644 +--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c ++++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +@@ -597,14 +597,13 @@ static int init_dma_desc_rings(struct net_device *netd) + + return 0; + +-txalloc_err: +- while (queue_num--) +- free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); +- return ret; +- + rxalloc_err: + while (queue_num--) + free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize); ++ queue_num = SXGBE_TX_QUEUES; ++txalloc_err: ++ while (queue_num--) ++ free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); + return ret; + } + +-- +2.53.0 + diff --git a/queue-7.1/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch b/queue-7.1/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch new file mode 100644 index 0000000000..4e68cb53e1 --- /dev/null +++ b/queue-7.1/net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch @@ -0,0 +1,150 @@ +From 730b3d9cff353b00522ca168e0878f2b9466dfd9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 09:11:37 +0000 +Subject: net: udp_tunnel: fix memory leak in udp_tunnel_nic_unregister() + +From: Eric Dumazet + +[ Upstream commit 080695e6f005e2396f1207fd69d24c442cb230c6 ] + +syzbot reported a memory leak [1] in the UDP tunnel NIC offload code. + +When device registration fails (e.g. in register_netdevice()), netdev core +unwinds by sending a single NETDEV_UNREGISTER notification. If work was queued +during NETDEV_REGISTER (utn->work_pending is set), udp_tunnel_nic_unregister() +returns early: + + if (utn->work_pending) + return; + +Because failed registrations do not enter netdev_wait_allrefs_any(), no +subsequent NETDEV_UNREGISTER rebroadcast will ever occur. As a result, the +struct udp_tunnel_nic allocated in udp_tunnel_nic_alloc() is leaked +permanently. + +Fix this by removing the early return. Instead, synchronously cancel any +pending work with cancel_delayed_work_sync() before freeing @utn. + +To be able to call cancel_delayed_work_sync() while holding RTNL (the work also +needs RTNL), switch udp_tunnel_nic_device_sync_work() to rtnl_trylock(). If RTNL +is contended, requeue the work with a 1 jiffy delay (via queue_delayed_work()) +to prevent high CPU contention while waiting for RTNL lock. + +The utn->work_pending bookkeeping is no longer needed and is removed, as +the workqueue core already tracks the pending/running state of the work. + +[1] +BUG: memory leak +unreferenced object 0xffff888127d5f840 (size 96): + comm "syz-executor", pid 5806, jiffies 4294942188 + backtrace (crc 99fdb6c8): + __kmalloc_noprof+0x3bf/0x550 + udp_tunnel_nic_alloc net/ipv4/udp_tunnel_nic.c:756 [inline] + udp_tunnel_nic_register net/ipv4/udp_tunnel_nic.c:833 [inline] + udp_tunnel_nic_netdevice_event+0x804/0xab0 net/ipv4/udp_tunnel_nic.c:931 + notifier_call_chain+0x59/0x160 kernel/notifier.c:85 + call_netdevice_notifiers_info+0x7d/0xb0 net/core/dev.c:2250 + register_netdevice+0xc10/0xeb0 net/core/dev.c:11478 + +Fixes: cc4e3835eff4 ("udp_tunnel: add central NIC RX port offload infrastructure") +Reported-by: syzbot+eca845fb8c18dd6b44c1@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/netdev/6a632b15.dde6c935.cf6c8.0011.GAE@google.com/T/#u +Signed-off-by: Eric Dumazet +Link: https://patch.msgid.link/20260724091137.1792543-1-edumazet@google.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + net/ipv4/udp_tunnel_nic.c | 32 +++++++++++++++++--------------- + 1 file changed, 17 insertions(+), 15 deletions(-) + +diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c +index 3b32a0afa9798..53a1a9c1f8bff 100644 +--- a/net/ipv4/udp_tunnel_nic.c ++++ b/net/ipv4/udp_tunnel_nic.c +@@ -32,13 +32,12 @@ struct udp_tunnel_nic_table_entry { + * @lock: protects all fields + * @need_sync: at least one port start changed + * @need_replay: space was freed, we need a replay of all ports +- * @work_pending: @work is currently scheduled + * @n_tables: number of tables under @entries + * @missed: bitmap of tables which overflown + * @entries: table of tables of ports currently offloaded + */ + struct udp_tunnel_nic { +- struct work_struct work; ++ struct delayed_work work; + + struct net_device *dev; + +@@ -46,7 +45,6 @@ struct udp_tunnel_nic { + + u8 need_sync:1; + u8 need_replay:1; +- u8 work_pending:1; + + unsigned int n_tables; + unsigned long missed; +@@ -301,11 +299,10 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) + static void + udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) + { +- if (!utn->need_sync || utn->work_pending) ++ if (!utn->need_sync) + return; + +- queue_work(udp_tunnel_nic_workqueue, &utn->work); +- utn->work_pending = 1; ++ queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 0); + } + + static bool +@@ -731,12 +728,17 @@ udp_tunnel_nic_replay(struct net_device *dev, struct udp_tunnel_nic *utn) + static void udp_tunnel_nic_device_sync_work(struct work_struct *work) + { + struct udp_tunnel_nic *utn = +- container_of(work, struct udp_tunnel_nic, work); ++ container_of(work, struct udp_tunnel_nic, work.work); + +- rtnl_lock(); ++ /* We cannot block on RTNL here, otherwise we would deadlock with ++ * udp_tunnel_nic_unregister() calling cancel_delayed_work_sync() ++ * while holding RTNL. Requeue with 1 jiffy delay if RTNL is contended. ++ */ ++ if (!rtnl_trylock()) { ++ queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 1); ++ return; ++ } + mutex_lock(&utn->lock); +- +- utn->work_pending = 0; + __udp_tunnel_nic_device_sync(utn->dev, utn); + + if (utn->need_replay) +@@ -757,7 +759,7 @@ udp_tunnel_nic_alloc(const struct udp_tunnel_nic_info *info, + if (!utn) + return NULL; + utn->n_tables = n_tables; +- INIT_WORK(&utn->work, udp_tunnel_nic_device_sync_work); ++ INIT_DELAYED_WORK(&utn->work, udp_tunnel_nic_device_sync_work); + mutex_init(&utn->lock); + + for (i = 0; i < n_tables; i++) { +@@ -901,11 +903,11 @@ udp_tunnel_nic_unregister(struct net_device *dev, struct udp_tunnel_nic *utn) + udp_tunnel_nic_flush(dev, utn); + udp_tunnel_nic_unlock(dev); + +- /* Wait for the work to be done using the state, netdev core will +- * retry unregister until we give up our reference on this device. ++ /* Make sure no work is running or queued before freeing @utn. ++ * The work handler uses rtnl_trylock(), so it will not deadlock ++ * against the RTNL we are holding here. + */ +- if (utn->work_pending) +- return; ++ cancel_delayed_work_sync(&utn->work); + + udp_tunnel_nic_free(utn); + release_dev: +-- +2.53.0 + diff --git a/queue-7.1/netfilter-nf_conntrack_expect-add-and-use-nf_ct_expe.patch b/queue-7.1/netfilter-nf_conntrack_expect-add-and-use-nf_ct_expe.patch new file mode 100644 index 0000000000..71c8eb4738 --- /dev/null +++ b/queue-7.1/netfilter-nf_conntrack_expect-add-and-use-nf_ct_expe.patch @@ -0,0 +1,195 @@ +From 50dbe9ade28f59635d89f631781c623ff49e1406 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 00:26:04 +0200 +Subject: netfilter: nf_conntrack_expect: add and use + nf_ct_expect_related_pair() + +From: Pablo Neira Ayuso + +[ Upstream commit 4aa63842fc92de1bce59d4709a0d32e718890bb2 ] + +Add a new function to insert a pair of expectations, this is required by +the SIP and H323 NAT helpers. The spinlock is held to check if there is +a slot for both expectations, in such case, insert them. + +This removes the need for nf_ct_unexpect_related() inside the loop to +find a pair of consecutive ports, otherwise inserting expectations whose +dead flag is already set on can happen. + +Bump master_help->expecting for the expectation class after checking if +the expectation fits in the master expectation list, which is needed for +this new _pair() function variant to run the eviction routine including +the preallocated slot for the first expectation in the pair. + +Fixes: b8b09dc2bf35 ("netfilter: nf_conntrack_expect: use conntrack GC to reap expectations") +Reported-by: Jaeyeong Lee +Link: https://patch.msgid.link/178377968720.33756.12204817361601593230@proton.me/ +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/netfilter/nf_conntrack_expect.h | 3 ++ + net/ipv4/netfilter/nf_nat_h323.c | 22 +++++-------- + net/netfilter/nf_conntrack_expect.c | 35 ++++++++++++++++++++- + net/netfilter/nf_nat_sip.c | 20 ++++-------- + 4 files changed, 50 insertions(+), 30 deletions(-) + +diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h +index c024345c9bd86..26d6babd92fcd 100644 +--- a/include/net/netfilter/nf_conntrack_expect.h ++++ b/include/net/netfilter/nf_conntrack_expect.h +@@ -161,6 +161,9 @@ static inline int nf_ct_expect_related(struct nf_conntrack_expect *expect, + return nf_ct_expect_related_report(expect, 0, 0, flags); + } + ++int nf_ct_expect_related_pair(struct nf_conntrack_expect *expect[], ++ unsigned int flag); ++ + struct nf_conn_help; + void nf_ct_expectation_gc(struct nf_conn_help *master_help); + +diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c +index 183e8a3ff2bab..6bcd6734769b5 100644 +--- a/net/ipv4/netfilter/nf_nat_h323.c ++++ b/net/ipv4/netfilter/nf_nat_h323.c +@@ -182,6 +182,7 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, + struct nf_conntrack_expect *rtp_exp, + struct nf_conntrack_expect *rtcp_exp) + { ++ struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp }; + struct nf_ct_h323_master *info = nfct_help_data(ct); + int dir = CTINFO2DIR(ctinfo); + int i; +@@ -227,22 +228,13 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, + int ret; + + rtp_exp->tuple.dst.u.udp.port = htons(nated_port); +- ret = nf_ct_expect_related(rtp_exp, 0); ++ rtcp_exp->tuple.dst.u.udp.port = htons(nated_port + 1); ++ ret = nf_ct_expect_related_pair(rtp_pair, 0); + if (ret == 0) { +- rtcp_exp->tuple.dst.u.udp.port = +- htons(nated_port + 1); +- ret = nf_ct_expect_related(rtcp_exp, 0); +- if (ret == 0) +- break; +- else if (ret == -EBUSY) { +- nf_ct_unexpect_related(rtp_exp); +- continue; +- } else if (ret < 0) { +- nf_ct_unexpect_related(rtp_exp); +- nated_port = 0; +- break; +- } +- } else if (ret != -EBUSY) { ++ break; ++ } else if (ret == -EBUSY) { ++ continue; ++ } else if (ret < 0) { + nated_port = 0; + break; + } +diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c +index 38630c5e006f0..16520a41fe5f0 100644 +--- a/net/netfilter/nf_conntrack_expect.c ++++ b/net/netfilter/nf_conntrack_expect.c +@@ -426,7 +426,6 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp, + exp->timeout += helper->expect_policy[exp->class].timeout * HZ; + + hlist_add_head_rcu(&exp->lnode, &master_help->expectations); +- master_help->expecting[exp->class]++; + + hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]); + cnet = nf_ct_pernet(net); +@@ -533,6 +532,7 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, + if (ret < 0) + goto out; + ++ master_help->expecting[expect->class]++; + nf_ct_expect_insert(expect, master_help); + + nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report); +@@ -545,6 +545,39 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, + } + EXPORT_SYMBOL_GPL(nf_ct_expect_related_report); + ++int nf_ct_expect_related_pair(struct nf_conntrack_expect *expect[], ++ unsigned int flags) ++{ ++ struct nf_conn_help *master_help; ++ int i, ret; ++ ++ spin_lock_bh(&nf_conntrack_expect_lock); ++ master_help = nfct_help(expect[0]->master); ++ if (!master_help || master_help != nfct_help(expect[1]->master)) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ for (i = 0; i < 2; i++) { ++ ret = __nf_ct_expect_check(expect[i], master_help, flags); ++ if (ret < 0) { ++ if (i == 1) ++ master_help->expecting[expect[0]->class]--; ++ goto out; ++ } ++ master_help->expecting[expect[i]->class]++; ++ } ++ ++ for (i = 0; i < 2; i++) { ++ nf_ct_expect_insert(expect[i], master_help); ++ nf_ct_expect_event_report(IPEXP_NEW, expect[i], 0, 0); ++ } ++out: ++ spin_unlock_bh(&nf_conntrack_expect_lock); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(nf_ct_expect_related_pair); ++ + void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, void *data), + void *data) + { +diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c +index a93eaf0f7d305..133bd713fe0c2 100644 +--- a/net/netfilter/nf_nat_sip.c ++++ b/net/netfilter/nf_nat_sip.c +@@ -592,6 +592,7 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff, + unsigned int medialen, + union nf_inet_addr *rtp_addr) + { ++ struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp }; + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); +@@ -622,24 +623,15 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff, + int ret; + + rtp_exp->tuple.dst.u.udp.port = htons(port); +- ret = nf_ct_expect_related(rtp_exp, +- NF_CT_EXP_F_SKIP_MASTER); +- if (ret == -EBUSY) +- continue; +- else if (ret < 0) { +- port = 0; +- break; +- } + rtcp_exp->tuple.dst.u.udp.port = htons(port + 1); +- ret = nf_ct_expect_related(rtcp_exp, +- NF_CT_EXP_F_SKIP_MASTER); ++ ++ ret = nf_ct_expect_related_pair(rtp_pair, ++ NF_CT_EXP_F_SKIP_MASTER); + if (ret == 0) + break; +- else if (ret == -EBUSY) { +- nf_ct_unexpect_related(rtp_exp); ++ else if (ret == -EBUSY) + continue; +- } else if (ret < 0) { +- nf_ct_unexpect_related(rtp_exp); ++ else if (ret < 0) { + port = 0; + break; + } +-- +2.53.0 + diff --git a/queue-7.1/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch b/queue-7.1/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch new file mode 100644 index 0000000000..db1246fde5 --- /dev/null +++ b/queue-7.1/netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch @@ -0,0 +1,96 @@ +From 9a7d0eb6ebdedcfcfd2c1509b5a305c5cb88ef59 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jul 2026 16:42:01 -0700 +Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in + sip_help_tcp() + +From: Xiang Mei + +[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ] + +sip_help_tcp() stores the size change of each NAT-rewritten SIP message +in s16 diff and accumulates it in s16 tdiff, but a single message can +grow by more than S16_MAX while the packet stays under the 65535 +enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long +Contact list expands the message by tens of kilobytes. diff then wraps, +and "datalen = datalen + diff - msglen" yields a huge unsigned datalen, +so the next iteration's ct_sip_get_header() reads past the linearized skb +tail. + +Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the +65535 byte packet limit, and the seqadj core is already s32 +(nf_ct_seqadj_set() takes s32), so no previously accepted input is +rejected. + + BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25 + ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) + sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694) + nf_confirm (net/netfilter/nf_conntrack_proto.c:183) + nf_hook_slow (net/netfilter/core.c:619) + ip6_output (net/ipv6/ip6_output.c:246) + ip6_forward (net/ipv6/ip6_output.c:690) + ipv6_rcv (net/ipv6/ip6_input.c:351) + __netif_receive_skb_one_core (net/core/dev.c:6212) + process_backlog (net/core/dev.c:6676) + __napi_poll (net/core/dev.c:7735) + net_rx_action (net/core/dev.c:7955) + handle_softirqs (kernel/softirq.c:622) + run_ksoftirqd (kernel/softirq.c:1076) + ... + +Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support") +Reported-by: Weiming Shi +Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Xiang Mei +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/linux/netfilter/nf_conntrack_sip.h | 2 +- + net/netfilter/nf_conntrack_sip.c | 2 +- + net/netfilter/nf_nat_sip.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h +index dbc614dfe0d56..aafa0c04f917e 100644 +--- a/include/linux/netfilter/nf_conntrack_sip.h ++++ b/include/linux/netfilter/nf_conntrack_sip.h +@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks { + unsigned int *datalen); + + void (*seq_adjust)(struct sk_buff *skb, +- unsigned int protoff, s16 off); ++ unsigned int protoff, s32 off); + + unsigned int (*expect)(struct sk_buff *skb, + unsigned int protoff, +diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c +index f3f90a8663389..e4a70d1d77b0b 100644 +--- a/net/netfilter/nf_conntrack_sip.c ++++ b/net/netfilter/nf_conntrack_sip.c +@@ -1663,7 +1663,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, + unsigned int matchoff, matchlen; + unsigned int msglen, origlen; + const char *dptr, *end; +- s16 diff, tdiff = 0; ++ s32 diff, tdiff = 0; + int ret = NF_ACCEPT; + unsigned long clen; + bool term; +diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c +index aea02f6aff092..a93eaf0f7d305 100644 +--- a/net/netfilter/nf_nat_sip.c ++++ b/net/netfilter/nf_nat_sip.c +@@ -321,7 +321,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, + } + + static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, +- s16 off) ++ s32 off) + { + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); +-- +2.53.0 + diff --git a/queue-7.1/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch b/queue-7.1/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch new file mode 100644 index 0000000000..44e482952f --- /dev/null +++ b/queue-7.1/netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch @@ -0,0 +1,205 @@ +From d215d6ce24f26945944eafa143acbe96fee7905a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 10:13:37 +0200 +Subject: netfilter: nf_tables: make nft_object rhltable per table + +From: Pablo Neira Ayuso + +[ Upstream commit f4f699790590bd0896c48a71e9232a65198f92f0 ] + +The nft_object rhltable is global, this allows for accessing objects +that are being dismangled from lookup path by other existing netns. +Given the nft_obj_destroy() releases the object inmediately, this might +lead to use-after-free of these objects that are being released. +Make the existing rhltable per table to address this issue to deal with +with the nft_rcv_nl_event() path too. + +Update nft_obj_lookup() to take the table as non-const, otherwise, +compiler complains when passing the objname_ht to rhltable_lookup(). + +Fixes: 4d44175aa5bb ("netfilter: nf_tables: handle nft_object lookups via rhltable") +Suggested-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + include/net/netfilter/nf_tables.h | 4 +++- + net/netfilter/nf_tables_api.c | 34 +++++++++++++++---------------- + 2 files changed, 19 insertions(+), 19 deletions(-) + +diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h +index 9d844354c4d95..3be612145c130 100644 +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -1294,6 +1294,7 @@ static inline void nft_use_inc_restore(u32 *use) + * @sets: sets in the table + * @objects: stateful objects in the table + * @flowtables: flow tables in the table ++ * @objname_ht: hashtable for objects lookup by name + * @hgenerator: handle generator state + * @handle: table handle + * @use: number of chain references to this table +@@ -1313,6 +1314,7 @@ struct nft_table { + struct list_head sets; + struct list_head objects; + struct list_head flowtables; ++ struct rhltable objname_ht; + u64 hgenerator; + u64 handle; + u32 use; +@@ -1400,7 +1402,7 @@ static inline void *nft_obj_data(const struct nft_object *obj) + #define nft_expr_obj(expr) *((struct nft_object **)nft_expr_priv(expr)) + + struct nft_object *nft_obj_lookup(const struct net *net, +- const struct nft_table *table, ++ struct nft_table *table, + const struct nlattr *nla, u32 objtype, + u8 genmask); + +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index e1db3e678656d..c89e55ffc77d1 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -45,8 +45,6 @@ enum { + NFT_VALIDATE_DO, + }; + +-static struct rhltable nft_objname_ht; +- + static u32 nft_chain_hash(const void *data, u32 len, u32 seed); + static u32 nft_chain_hash_obj(const void *data, u32 len, u32 seed); + static int nft_chain_hash_cmp(struct rhashtable_compare_arg *, const void *); +@@ -1635,6 +1633,10 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info, + if (err) + goto err_chain_ht; + ++ err = rhltable_init(&table->objname_ht, &nft_objname_ht_params); ++ if (err < 0) ++ goto err_obj_ht; ++ + INIT_LIST_HEAD(&table->chains); + INIT_LIST_HEAD(&table->sets); + INIT_LIST_HEAD(&table->objects); +@@ -1653,6 +1655,8 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info, + list_add_tail_rcu(&table->list, &nft_net->tables); + return 0; + err_trans: ++ rhltable_destroy(&table->objname_ht); ++err_obj_ht: + rhltable_destroy(&table->chains_ht); + err_chain_ht: + kfree(table->udata); +@@ -1819,6 +1823,7 @@ static void nf_tables_table_destroy(struct nft_table *table) + return; + + rhltable_destroy(&table->chains_ht); ++ rhltable_destroy(&table->objname_ht); + kfree(table->name); + kfree(table->udata); + kfree(table); +@@ -8082,7 +8087,7 @@ void nft_unregister_obj(struct nft_object_type *obj_type) + EXPORT_SYMBOL_GPL(nft_unregister_obj); + + struct nft_object *nft_obj_lookup(const struct net *net, +- const struct nft_table *table, ++ struct nft_table *table, + const struct nlattr *nla, u32 objtype, + u8 genmask) + { +@@ -8098,7 +8103,7 @@ struct nft_object *nft_obj_lookup(const struct net *net, + !lockdep_commit_lock_is_held(net)); + + rcu_read_lock(); +- list = rhltable_lookup(&nft_objname_ht, &k, nft_objname_ht_params); ++ list = rhltable_lookup(&table->objname_ht, &k, nft_objname_ht_params); + if (!list) + goto out; + +@@ -8376,7 +8381,7 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info, + if (err < 0) + goto err_trans; + +- err = rhltable_insert(&nft_objname_ht, &obj->rhlhead, ++ err = rhltable_insert(&table->objname_ht, &obj->rhlhead, + nft_objname_ht_params); + if (err < 0) + goto err_obj_ht; +@@ -8561,8 +8566,8 @@ nf_tables_getobj_single(u32 portid, const struct nfnl_info *info, + struct netlink_ext_ack *extack = info->extack; + u8 genmask = nft_genmask_cur(info->net); + u8 family = info->nfmsg->nfgen_family; +- const struct nft_table *table; + struct net *net = info->net; ++ struct nft_table *table; + struct nft_object *obj; + struct sk_buff *skb2; + u32 objtype; +@@ -10423,9 +10428,9 @@ static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain) + nf_tables_commit_chain_free_rules_old(g0); + } + +-static void nft_obj_del(struct nft_object *obj) ++static void nft_obj_del(struct nft_table *table, struct nft_object *obj) + { +- rhltable_remove(&nft_objname_ht, &obj->rhlhead, nft_objname_ht_params); ++ rhltable_remove(&table->objname_ht, &obj->rhlhead, nft_objname_ht_params); + list_del_rcu(&obj->list); + } + +@@ -11110,7 +11115,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) + break; + case NFT_MSG_DELOBJ: + case NFT_MSG_DESTROYOBJ: +- nft_obj_del(nft_trans_obj(trans)); ++ nft_obj_del(table, nft_trans_obj(trans)); + nf_tables_obj_notify(&ctx, nft_trans_obj(trans), + trans->msg_type); + break; +@@ -11402,7 +11407,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) + nft_trans_destroy(trans); + } else { + nft_use_dec_restore(&table->use); +- nft_obj_del(nft_trans_obj(trans)); ++ nft_obj_del(table, nft_trans_obj(trans)); + } + break; + case NFT_MSG_DELOBJ: +@@ -12025,7 +12030,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table) + nft_set_destroy(&ctx, set); + } + list_for_each_entry_safe(obj, ne, &table->objects, list) { +- nft_obj_del(obj); ++ nft_obj_del(table, obj); + nft_use_dec(&table->use); + nft_obj_destroy(&ctx, obj); + } +@@ -12207,10 +12212,6 @@ static int __init nf_tables_module_init(void) + if (err < 0) + goto err_netdev_notifier; + +- err = rhltable_init(&nft_objname_ht, &nft_objname_ht_params); +- if (err < 0) +- goto err_rht_objname; +- + err = nft_offload_init(); + if (err < 0) + goto err_offload; +@@ -12233,8 +12234,6 @@ static int __init nf_tables_module_init(void) + err_netlink_notifier: + nft_offload_exit(); + err_offload: +- rhltable_destroy(&nft_objname_ht); +-err_rht_objname: + unregister_netdevice_notifier(&nf_tables_flowtable_notifier); + err_netdev_notifier: + nf_tables_core_module_exit(); +@@ -12256,7 +12255,6 @@ static void __exit nf_tables_module_exit(void) + unregister_pernet_subsys(&nf_tables_net_ops); + cancel_work_sync(&trans_gc_work); + rcu_barrier(); +- rhltable_destroy(&nft_objname_ht); + nf_tables_core_module_exit(); + } + +-- +2.53.0 + diff --git a/queue-7.1/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch b/queue-7.1/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch new file mode 100644 index 0000000000..9f484a6d65 --- /dev/null +++ b/queue-7.1/netfilter-nft_payload-fix-mask-build-for-partial-fie.patch @@ -0,0 +1,69 @@ +From aa1c09461375c2b3b60e74721bf98aa0eea53f5b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:15:23 +0000 +Subject: netfilter: nft_payload: fix mask build for partial field offload + +From: Xiang Mei (Microsoft) + +[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ] + +nft_payload_offload_mask() builds the offload match mask for a payload +expression that covers only part of a header field. For a partial IPv6 +address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which +is undefined on the 32-bit int operand. It also trims only one word, so +the remaining words stay 0xffffffff (and when priv_len is a multiple of 4 +the trim is skipped entirely), leaving the mask covering more bytes than +the rule matches. + + UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20 + shift exponent 120 is too large for 32-bit type 'int' + ... + +The match is byte-granular and struct nft_data is zero-initialised, so the +correct mask is simply the first priv_len bytes set to 0xff. Set those +bytes directly and drop the word/shift trimming; this removes the undefined +shift and no longer over-masks the trailing bytes. + +Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes") +Reported-by: AutonomousCodeSecurity@microsoft.com +Signed-off-by: Xiang Mei (Microsoft) +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/nft_payload.c | 12 +----------- + 1 file changed, 1 insertion(+), 11 deletions(-) + +diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c +index 4438d05f8018c..8ffa864a2195a 100644 +--- a/net/netfilter/nft_payload.c ++++ b/net/netfilter/nft_payload.c +@@ -259,9 +259,7 @@ static int nft_payload_dump(struct sk_buff *skb, + static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + u32 priv_len, u32 field_len) + { +- unsigned int remainder, delta, k; + struct nft_data mask = {}; +- __be32 remainder_mask; + + if (priv_len == field_len) { + memset(®->mask, 0xff, priv_len); +@@ -270,15 +268,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg, + return false; + } + +- memset(&mask, 0xff, field_len); +- remainder = priv_len % sizeof(u32); +- if (remainder) { +- k = priv_len / sizeof(u32); +- delta = field_len - priv_len; +- remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1)); +- mask.data[k] = (__force u32)remainder_mask; +- } +- ++ memset(&mask, 0xff, priv_len); + memcpy(®->mask, &mask, field_len); + + return true; +-- +2.53.0 + diff --git a/queue-7.1/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch b/queue-7.1/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch new file mode 100644 index 0000000000..f54594a113 --- /dev/null +++ b/queue-7.1/netfilter-xt_hashlimit-validate-hashtable-supports-x.patch @@ -0,0 +1,83 @@ +From 3721c015d8cd0904795fdfe5a1bc0477bfc5144a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 22:02:46 +0200 +Subject: netfilter: xt_hashlimit: validate hashtable supports + XT_HASHLIMIT_RATE_MATCH + +From: Pablo Neira Ayuso + +[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ] + +The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the +dsthash_ent structure which represents an entry in the hashtable. There +is a union area which uses a different layout to express the rate match +mode. + +Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode +flag is requested by two or more different rules that refer to the same +hashtable. Otherwise, uninitialized access to the burst field in the +union is possible. + +Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by +revision less than 3 too. + +Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode") +Reported-and-tested-by: Talha Berk Arslan +Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/ +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + net/netfilter/xt_hashlimit.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c +index 2704b4b60d1e0..9af0fa895f730 100644 +--- a/net/netfilter/xt_hashlimit.c ++++ b/net/netfilter/xt_hashlimit.c +@@ -117,6 +117,7 @@ struct xt_hashlimit_htable { + refcount_t use; + u_int8_t family; + bool rnd_initialized; ++ bool ratematch; + + struct hashlimit_cfg3 cfg; /* config */ + +@@ -323,6 +324,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg, + kvfree(hinfo); + return -ENOMEM; + } ++ hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH); + spin_lock_init(&hinfo->lock); + + switch (revision) { +@@ -872,7 +874,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + } + + /* Check for overflow. */ +- if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) { ++ if (revision < 3) ++ return -EINVAL; ++ + if (cfg->avg == 0 || cfg->avg > U32_MAX) { + pr_info_ratelimited("invalid rate\n"); + return -ERANGE; +@@ -905,6 +910,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, + mutex_unlock(&hashlimit_mutex); + return ret; + } ++ } else { ++ if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH && ++ !(*hinfo)->ratematch) || ++ (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) && ++ (*hinfo)->ratematch)) { ++ mutex_unlock(&hashlimit_mutex); ++ htable_put(*hinfo); ++ return -EINVAL; ++ } + } + mutex_unlock(&hashlimit_mutex); + +-- +2.53.0 + diff --git a/queue-7.1/netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch b/queue-7.1/netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch new file mode 100644 index 0000000000..da86520e72 --- /dev/null +++ b/queue-7.1/netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch @@ -0,0 +1,44 @@ +From bc8fc6340236115402b5c926dc2deeab57608aba Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 14:07:12 +0100 +Subject: netfs: clear PG_private_2 on copy-to-cache append failure + +From: Yichong Chen + +[ Upstream commit a81fc9266e1c5fef9ccf675a9b44b2f4ab464923 ] + +netfs_pgpriv2_copy_to_cache() marks the folio with PG_private_2 before +netfs_pgpriv2_copy_folio() appends it to the copy-to-cache rolling +buffer. + +If the append fails, the folio is not queued for cache writeback, so +the PG_private_2 state and its reference must be released immediately. + +Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item") +Signed-off-by: Yichong Chen +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260727130716.1099906-2-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner (Amutable) +Signed-off-by: Sasha Levin +--- + fs/netfs/read_pgpriv2.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c +index a1489aa29f782..7eacc58abadb7 100644 +--- a/fs/netfs/read_pgpriv2.c ++++ b/fs/netfs/read_pgpriv2.c +@@ -54,6 +54,7 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio + + /* Attach the folio to the rolling buffer. */ + if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) { ++ folio_end_private_2(folio); + clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags); + return; + } +-- +2.53.0 + diff --git a/queue-7.1/netfs-fix-folio_queue-enomem-in-writeback-by-adding-.patch b/queue-7.1/netfs-fix-folio_queue-enomem-in-writeback-by-adding-.patch new file mode 100644 index 0000000000..b5422baf2d --- /dev/null +++ b/queue-7.1/netfs-fix-folio_queue-enomem-in-writeback-by-adding-.patch @@ -0,0 +1,371 @@ +From 5ffea70555f4f3c9469436ad614c450f679ae5cb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 14:07:15 +0100 +Subject: netfs: Fix folio_queue ENOMEM in writeback by adding a mempool + +From: David Howells + +[ Upstream commit 1d78d56c43ef3768183e8370e7367b162700e049 ] + +Fix the handling of folio_queue allocation failure in writeback by adding a +mempool and passing in gfp_t flags to the rolling buffer functions that +allocate memory, using the mempool if gfp != GFP_KERNEL. + +This is then extended upwards and the gfp to be used for a request is stored +in the netfs_io_request struct and is then used for both requests and +subrequests, eliminating the sleeping loops there. + +The failure caused: + + folio != NULL + WARNING: fs/netfs/write_issue.c:603 at netfs_writepages+0x883/0xa10 fs/netfs/write_issue.c:603, CPU#3: syz.0.17/5919 + +Fixes: cd0277ed0c18 ("netfs: Use new folio_queue data type and iterator instead of xarray iter") +Reported-by: syzbot+0da43efa72f88bd3a8af@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=0da43efa72f88bd3a8af +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260727130716.1099906-5-dhowells@redhat.com +Tested-by: syzbot+0da43efa72f88bd3a8af@syzkaller.appspotmail.com +cc: Paulo Alcantara +cc: Yun Zhou +cc: Matthew Wilcox +cc: Christoph Hellwig +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner (Amutable) +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 6 +++--- + fs/netfs/internal.h | 1 + + fs/netfs/main.c | 7 +++++++ + fs/netfs/objects.c | 30 +++++++++++++++++------------- + fs/netfs/read_pgpriv2.c | 2 +- + fs/netfs/rolling_buffer.c | 22 +++++++++++++--------- + fs/netfs/write_issue.c | 10 +++++----- + include/linux/netfs.h | 1 + + include/linux/rolling_buffer.h | 6 +++--- + 9 files changed, 51 insertions(+), 34 deletions(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index 68534cef37cd2..6c2192bb49cfc 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -361,7 +361,7 @@ void netfs_readahead(struct readahead_control *ractl) + netfs_rreq_expand(rreq, ractl); + + rreq->submitted = rreq->start; +- if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0) ++ if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0) + goto cleanup_free; + netfs_read_to_pagecache(rreq, ractl); + +@@ -380,10 +380,10 @@ static int netfs_create_singular_buffer(struct netfs_io_request *rreq, struct fo + { + ssize_t added; + +- if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0) ++ if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0) + return -ENOMEM; + +- added = rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags); ++ added = rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags, rreq->gfp); + if (added < 0) + return added; + rreq->submitted = rreq->start + added; +diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h +index 645996ecfc803..acdd543349915 100644 +--- a/fs/netfs/internal.h ++++ b/fs/netfs/internal.h +@@ -43,6 +43,7 @@ extern struct list_head netfs_io_requests; + extern spinlock_t netfs_proc_lock; + extern mempool_t netfs_request_pool; + extern mempool_t netfs_subrequest_pool; ++extern mempool_t netfs_folioq_pool; + + #ifdef CONFIG_PROC_FS + static inline void netfs_proc_add_rreq(struct netfs_io_request *rreq) +diff --git a/fs/netfs/main.c b/fs/netfs/main.c +index 73da6c9f5777c..927badf3989db 100644 +--- a/fs/netfs/main.c ++++ b/fs/netfs/main.c +@@ -28,6 +28,7 @@ static struct kmem_cache *netfs_request_slab; + static struct kmem_cache *netfs_subrequest_slab; + mempool_t netfs_request_pool; + mempool_t netfs_subrequest_pool; ++mempool_t netfs_folioq_pool; + + #ifdef CONFIG_PROC_FS + LIST_HEAD(netfs_io_requests); +@@ -108,6 +109,9 @@ static int __init netfs_init(void) + { + int ret = -ENOMEM; + ++ if (mempool_init_kmalloc_pool(&netfs_folioq_pool, 100, sizeof(struct folio_queue)) < 0) ++ goto error_folioq_pool; ++ + netfs_request_slab = kmem_cache_create("netfs_request", + sizeof(struct netfs_io_request), 0, + SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, +@@ -160,6 +164,8 @@ static int __init netfs_init(void) + error_reqpool: + kmem_cache_destroy(netfs_request_slab); + error_req: ++ mempool_exit(&netfs_folioq_pool); ++error_folioq_pool: + return ret; + } + fs_initcall(netfs_init); +@@ -172,5 +178,6 @@ static void __exit netfs_exit(void) + kmem_cache_destroy(netfs_subrequest_slab); + mempool_exit(&netfs_request_pool); + kmem_cache_destroy(netfs_request_slab); ++ mempool_exit(&netfs_folioq_pool); + } + module_exit(netfs_exit); +diff --git a/fs/netfs/objects.c b/fs/netfs/objects.c +index b8c4918d3dcda..01461a74642d6 100644 +--- a/fs/netfs/objects.c ++++ b/fs/netfs/objects.c +@@ -7,7 +7,6 @@ + + #include + #include +-#include + #include "internal.h" + + static void netfs_free_request(struct work_struct *work); +@@ -26,17 +25,23 @@ struct netfs_io_request *netfs_alloc_request(struct address_space *mapping, + struct netfs_io_request *rreq; + mempool_t *mempool = ctx->ops->request_pool ?: &netfs_request_pool; + struct kmem_cache *cache = mempool->pool_data; ++ gfp_t gfp = GFP_KERNEL; + int ret; + +- for (;;) { +- rreq = mempool_alloc(mempool, GFP_KERNEL); +- if (rreq) +- break; +- msleep(10); ++ /* Writeback is part of memory reclaim and must not fail due to ENOMEM. */ ++ if (origin == NETFS_WRITEBACK || origin == NETFS_WRITEBACK_SINGLE) { ++ gfp = GFP_NOFS; /* Allows use of mempools. */ ++ ++ rreq = mempool_alloc(mempool, gfp); ++ } else { ++ rreq = mempool->alloc(gfp, mempool->pool_data); ++ if (!rreq) ++ return ERR_PTR(-ENOMEM); + } + + memset(rreq, 0, kmem_cache_size(cache)); + INIT_WORK(&rreq->cleanup_work, netfs_free_request); ++ rreq->gfp = gfp; + rreq->start = start; + rreq->len = len; + rreq->origin = origin; +@@ -200,13 +205,12 @@ struct netfs_io_subrequest *netfs_alloc_subrequest(struct netfs_io_request *rreq + mempool_t *mempool = rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool; + struct kmem_cache *cache = mempool->pool_data; + +- for (;;) { +- subreq = mempool_alloc(rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool, +- GFP_KERNEL); +- if (subreq) +- break; +- msleep(10); +- } ++ if (rreq->gfp == GFP_KERNEL) ++ subreq = mempool->alloc(rreq->gfp, mempool->pool_data); ++ else ++ subreq = mempool_alloc(mempool, rreq->gfp); ++ if (!subreq) ++ return NULL; + + memset(subreq, 0, kmem_cache_size(cache)); + INIT_WORK(&subreq->work, NULL); +diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c +index 7eacc58abadb7..c31190993b762 100644 +--- a/fs/netfs/read_pgpriv2.c ++++ b/fs/netfs/read_pgpriv2.c +@@ -53,7 +53,7 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio + trace_netfs_folio(folio, netfs_folio_trace_store_copy); + + /* Attach the folio to the rolling buffer. */ +- if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) { ++ if (rolling_buffer_append(&creq->buffer, folio, 0, creq->gfp) < 0) { + folio_end_private_2(folio); + clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags); + return; +diff --git a/fs/netfs/rolling_buffer.c b/fs/netfs/rolling_buffer.c +index a17fbf9853a44..8c0026836f9c1 100644 +--- a/fs/netfs/rolling_buffer.c ++++ b/fs/netfs/rolling_buffer.c +@@ -6,6 +6,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -27,7 +28,10 @@ struct folio_queue *netfs_folioq_alloc(unsigned int rreq_id, gfp_t gfp, + { + struct folio_queue *fq; + +- fq = kmalloc_obj(*fq, gfp); ++ if (gfp == GFP_KERNEL) ++ fq = netfs_folioq_pool.alloc(gfp, netfs_folioq_pool.pool_data); ++ else ++ fq = mempool_alloc(&netfs_folioq_pool, gfp); + if (fq) { + netfs_stat(&netfs_n_folioq); + folioq_init(fq, rreq_id); +@@ -50,7 +54,7 @@ void netfs_folioq_free(struct folio_queue *folioq, + { + trace_netfs_folioq(folioq, trace); + netfs_stat_d(&netfs_n_folioq); +- kfree(folioq); ++ mempool_free(folioq, &netfs_folioq_pool); + } + EXPORT_SYMBOL(netfs_folioq_free); + +@@ -60,11 +64,11 @@ EXPORT_SYMBOL(netfs_folioq_free); + * consumer. + */ + int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id, +- unsigned int direction) ++ unsigned int direction, gfp_t gfp) + { + struct folio_queue *fq; + +- fq = netfs_folioq_alloc(rreq_id, GFP_NOFS, netfs_trace_folioq_rollbuf_init); ++ fq = netfs_folioq_alloc(rreq_id, gfp, netfs_trace_folioq_rollbuf_init); + if (!fq) + return -ENOMEM; + +@@ -77,14 +81,14 @@ int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id, + /* + * Add another folio_queue to a rolling buffer if there's no space left. + */ +-int rolling_buffer_make_space(struct rolling_buffer *roll) ++int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp) + { + struct folio_queue *fq, *head = roll->head; + + if (!folioq_full(head)) + return 0; + +- fq = netfs_folioq_alloc(head->rreq_id, GFP_NOFS, netfs_trace_folioq_make_space); ++ fq = netfs_folioq_alloc(head->rreq_id, gfp, netfs_trace_folioq_make_space); + if (!fq) + return -ENOMEM; + fq->prev = head; +@@ -122,7 +126,7 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, + int nr, ix, to; + ssize_t size = 0; + +- if (rolling_buffer_make_space(roll) < 0) ++ if (rolling_buffer_make_space(roll, GFP_KERNEL) < 0) + return -ENOMEM; + + fq = roll->head; +@@ -153,12 +157,12 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, + * Append a folio to the rolling buffer. + */ + ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio, +- unsigned int flags) ++ unsigned int flags, gfp_t gfp) + { + ssize_t size = folio_size(folio); + int slot; + +- if (rolling_buffer_make_space(roll) < 0) ++ if (rolling_buffer_make_space(roll, gfp) < 0) + return -ENOMEM; + + slot = folioq_append(roll->head, folio); +diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c +index 8d3a6cad42d5e..0e5f0f259bd05 100644 +--- a/fs/netfs/write_issue.c ++++ b/fs/netfs/write_issue.c +@@ -108,7 +108,7 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping, + ictx = netfs_inode(wreq->inode); + if (is_cacheable) + fscache_begin_write_operation(&wreq->cache_resources, netfs_i_cookie(ictx)); +- if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE) < 0) ++ if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE, wreq->gfp) < 0) + goto nomem; + + wreq->cleaned_to = wreq->start; +@@ -167,7 +167,7 @@ void netfs_prepare_write(struct netfs_io_request *wreq, + */ + if (iov_iter_is_folioq(wreq_iter) && + wreq_iter->folioq_slot >= folioq_nr_slots(wreq_iter->folioq)) +- rolling_buffer_make_space(&wreq->buffer); ++ rolling_buffer_make_space(&wreq->buffer, wreq->gfp); + + subreq = netfs_alloc_subrequest(wreq); + subreq->source = stream->source; +@@ -334,7 +334,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, + + _enter(""); + +- if (rolling_buffer_make_space(&wreq->buffer) < 0) ++ if (rolling_buffer_make_space(&wreq->buffer, wreq->gfp) < 0) + return -ENOMEM; + + /* netfs_perform_write() may shift i_size around the page or from out +@@ -436,7 +436,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, + } + + /* Attach the folio to the rolling buffer. */ +- rolling_buffer_append(&wreq->buffer, folio, 0); ++ rolling_buffer_append(&wreq->buffer, folio, 0, wreq->gfp); + + /* Move the submission point forward to allow for write-streaming data + * not starting at the front of the page. We don't do write-streaming +@@ -760,7 +760,7 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, + + /* Attach the folio to the rolling buffer. */ + folio_get(folio); +- ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK); ++ ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK, wreq->gfp); + if (ret < 0) { + folio_put(folio); + return ret; +diff --git a/include/linux/netfs.h b/include/linux/netfs.h +index 243c0f7379388..7b2daadffe198 100644 +--- a/include/linux/netfs.h ++++ b/include/linux/netfs.h +@@ -253,6 +253,7 @@ struct netfs_io_request { + unsigned long long cleaned_to; /* Position we've cleaned folios to */ + unsigned long long abandon_to; /* Position to abandon folios to */ + const struct folio *no_unlock_folio; /* Don't unlock this folio after read */ ++ gfp_t gfp; /* GFP flags to use */ + unsigned int direct_bv_count; /* Number of elements in direct_bv[] */ + unsigned int debug_id; + unsigned int rsize; /* Maximum read size (0 for none) */ +diff --git a/include/linux/rolling_buffer.h b/include/linux/rolling_buffer.h +index ac15b1ffdd831..9e5dad29669cf 100644 +--- a/include/linux/rolling_buffer.h ++++ b/include/linux/rolling_buffer.h +@@ -43,13 +43,13 @@ struct rolling_buffer_snapshot { + #define ROLLBUF_MARK_2 BIT(1) + + int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id, +- unsigned int direction); +-int rolling_buffer_make_space(struct rolling_buffer *roll); ++ unsigned int direction, gfp_t gfp); ++int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp); + ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, + struct readahead_control *ractl, + struct folio_batch *put_batch); + ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio, +- unsigned int flags); ++ unsigned int flags, gfp_t gfp); + struct folio_queue *rolling_buffer_delete_spent(struct rolling_buffer *roll); + void rolling_buffer_clear(struct rolling_buffer *roll); + +-- +2.53.0 + diff --git a/queue-7.1/netfs-handle-single-writeback-rolling-buffer-allocat.patch b/queue-7.1/netfs-handle-single-writeback-rolling-buffer-allocat.patch new file mode 100644 index 0000000000..88dea52f76 --- /dev/null +++ b/queue-7.1/netfs-handle-single-writeback-rolling-buffer-allocat.patch @@ -0,0 +1,57 @@ +From 8ede923dec1edaca1266043400e475d0fc565399 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 14:07:13 +0100 +Subject: netfs: handle single writeback rolling buffer allocation failure + +From: Yichong Chen + +[ Upstream commit 37a1c535c80c67d98668d190c7432f9ebda43310 ] + +netfs_write_folio_single() takes an extra folio reference before +appending the folio to the rolling buffer. + +rolling_buffer_append() can fail if it cannot allocate another +folio_queue. Check the return value and drop the extra folio reference +before returning the error. + +Fixes: 49866ce7ea8d ("netfs: Add support for caching single monolithic objects such as AFS dirs") +Signed-off-by: Yichong Chen +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260727130716.1099906-3-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner (Amutable) +Signed-off-by: Sasha Levin +--- + fs/netfs/write_issue.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c +index d0d884731dc5d..8d3a6cad42d5e 100644 +--- a/fs/netfs/write_issue.c ++++ b/fs/netfs/write_issue.c +@@ -731,6 +731,7 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, + size_t iter_off = 0; + size_t fsize = folio_size(folio), flen; + loff_t fpos = folio_pos(folio); ++ ssize_t ret; + bool to_eof = false; + bool no_debug = false; + +@@ -759,7 +760,11 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, + + /* Attach the folio to the rolling buffer. */ + folio_get(folio); +- rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK); ++ ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK); ++ if (ret < 0) { ++ folio_put(folio); ++ return ret; ++ } + + /* Move the submission point forward to allow for write-streaming data + * not starting at the front of the page. We don't do write-streaming +-- +2.53.0 + diff --git a/queue-7.1/netfs-release-readahead-folios-on-iterator-preparati.patch b/queue-7.1/netfs-release-readahead-folios-on-iterator-preparati.patch new file mode 100644 index 0000000000..ec80d8a7c1 --- /dev/null +++ b/queue-7.1/netfs-release-readahead-folios-on-iterator-preparati.patch @@ -0,0 +1,49 @@ +From afab3556fd2ceb4069540f0245a09ed716235b01 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 14:07:14 +0100 +Subject: netfs: release readahead folios on iterator preparation failure + +From: Yichong Chen + +[ Upstream commit 87eb3d272dcbcbbfe5c1576c10e5dc72810cf1f6 ] + +netfs_prepare_read_iterator() batches readahead folios in put_batch so that +the folio references can be dropped after the I/O iterator has been +prepared. + +If rolling_buffer_load_from_ra() fails after earlier folios have been +batched, the function returns immediately and leaves those references held. +Release the batch before returning the error. + +Fixes: 06fa229ceb36 ("netfs: Abstract out a rolling folio buffer implementation") +Signed-off-by: Yichong Chen +Signed-off-by: David Howells +Link: https://patch.msgid.link/20260727130716.1099906-4-dhowells@redhat.com +cc: Paulo Alcantara +cc: netfs@lists.linux.dev +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Christian Brauner (Amutable) +Signed-off-by: Sasha Levin +--- + fs/netfs/buffered_read.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c +index 76d0f6a29abab..68534cef37cd2 100644 +--- a/fs/netfs/buffered_read.c ++++ b/fs/netfs/buffered_read.c +@@ -102,8 +102,10 @@ static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq, + + added = rolling_buffer_load_from_ra(&rreq->buffer, ractl, + &put_batch); +- if (added < 0) ++ if (added < 0) { ++ folio_batch_release(&put_batch); + return added; ++ } + rreq->submitted += added; + } + folio_batch_release(&put_batch); +-- +2.53.0 + diff --git a/queue-7.1/nexthop-avoid-unlocked-f6i_list-walk-in-nh_rt_cache_.patch b/queue-7.1/nexthop-avoid-unlocked-f6i_list-walk-in-nh_rt_cache_.patch new file mode 100644 index 0000000000..746e965938 --- /dev/null +++ b/queue-7.1/nexthop-avoid-unlocked-f6i_list-walk-in-nh_rt_cache_.patch @@ -0,0 +1,71 @@ +From 69a403460e87b06cc66f12076d7e78189fdf36e8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 00:29:51 +0000 +Subject: nexthop: avoid unlocked f6i_list walk in nh_rt_cache_flush + +From: Xiang Mei (Microsoft) + +[ Upstream commit 4787a6d2629b4e8c0b6bacab1f75c1660eca44d9 ] + +nh_rt_cache_flush() walks nh->f6i_list during an RTNL-serialized nexthop +replace without holding nh->lock, racing the unlocked IPv6 route +add/delete that mutate the list under nh->lock and free fib6_info +entries (nh_rt_cache_flush() is inlined into rtm_new_nexthop()): + + BUG: KASAN: slab-use-after-free in nh_rt_cache_flush (net/ipv4/nexthop.c:2243) + Read of size 8 at addr ffff888012953e18 by task exploit/146 + nh_rt_cache_flush (net/ipv4/nexthop.c:2243) + replace_nexthop (net/ipv4/nexthop.c:2610) + rtm_new_nexthop (net/ipv4/nexthop.c:3323) + rtnetlink_rcv_msg (net/core/rtnetlink.c:7076) + +Unlike the other f6i_list walks, this one bumps each route's sernum via +fib6_update_sernum_upto_root(), which needs tb6_lock; taking nh->lock +around it would invert the established tb6_lock -> nh->lock order and +deadlock. As the only purpose is to invalidate cached dsts, bump the +IPv6 sernum for the whole netns with rt_genid_bump_ipv6() instead, +mirroring the rt_cache_flush() already done for IPv4 just above. + +Fixes: 081efd18326e ("ipv6: Protect nh->f6i_list with spinlock and flag.") +Reported-by: AutonomousCodeSecurity@microsoft.com +Signed-off-by: Xiang Mei (Microsoft) +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260722002951.2614721-2-xmei5@asu.edu +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/nexthop.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c +index 8b1f3382a998a..1bfc429fc9607 100644 +--- a/net/ipv4/nexthop.c ++++ b/net/ipv4/nexthop.c +@@ -2223,18 +2223,18 @@ static void remove_nexthop(struct net *net, struct nexthop *nh, + static void nh_rt_cache_flush(struct net *net, struct nexthop *nh, + struct nexthop *replaced_nh) + { +- struct fib6_info *f6i; + struct nh_group *nhg; ++ bool have_f6i; + int i; + + if (!list_empty(&nh->fi_list)) + rt_cache_flush(net); + +- list_for_each_entry(f6i, &nh->f6i_list, nh_list) { +- spin_lock_bh(&f6i->fib6_table->tb6_lock); +- fib6_update_sernum_upto_root(net, f6i); +- spin_unlock_bh(&f6i->fib6_table->tb6_lock); +- } ++ spin_lock_bh(&nh->lock); ++ have_f6i = !list_empty(&nh->f6i_list); ++ spin_unlock_bh(&nh->lock); ++ if (have_f6i) ++ rt_genid_bump_ipv6(net); + + /* if an IPv6 group was replaced, we have to release all old + * dsts to make sure all refcounts are released +-- +2.53.0 + diff --git a/queue-7.1/nexthop-take-nh-lock-for-f6i_list-walks-in-replace-c.patch b/queue-7.1/nexthop-take-nh-lock-for-f6i_list-walks-in-replace-c.patch new file mode 100644 index 0000000000..2f0bc0acd4 --- /dev/null +++ b/queue-7.1/nexthop-take-nh-lock-for-f6i_list-walks-in-replace-c.patch @@ -0,0 +1,86 @@ +From 9b0956956136a72831574cb8a105b7e580ab8cfb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 00:29:50 +0000 +Subject: nexthop: take nh->lock for f6i_list walks in replace check and notify + +From: Xiang Mei (Microsoft) + +[ Upstream commit 072cd1f21819dedd2252e704d255de3b0cfc61a7 ] + +fib6_check_nh_list() and __nexthop_replace_notify() walk nh->f6i_list +during an RTNL-serialized nexthop replace without holding nh->lock. IPv6 +RTM_NEWROUTE/RTM_DELROUTE run without RTNL and mutate that list under +nh->lock (fib6_add_rt2node_nh(), fib6_purge_rt()), so both walks race a +concurrent route delete that unlinks and frees a fib6_info: + + BUG: KASAN: slab-use-after-free in rt6_fill_node.isra.0 (net/ipv6/route.c:5799) + Read of size 4 at addr ffff888014607e64 by task exploit/143 + rt6_fill_node.isra.0 (net/ipv6/route.c:5799) + fib6_rt_update (net/ipv6/route.c:6412) + __nexthop_replace_notify (net/ipv4/nexthop.c:2542) + rtm_new_nexthop (net/ipv4/nexthop.c:2554) + rtnetlink_rcv_msg (net/core/rtnetlink.c:7076) + + BUG: KASAN: slab-use-after-free in fib6_check_nh_list (net/ipv4/nexthop.c:1605) + Read of size 8 at addr ffff888014a7d068 by task exploit/142 + fib6_check_nh_list (net/ipv4/nexthop.c:1605) + rtm_new_nexthop (net/ipv4/nexthop.c:2575) + rtnetlink_rcv_msg (net/core/rtnetlink.c:7076) + +Both walks only read the entries and take no tb6_lock, so protect them +with nh->lock; fib6_rt_update() uses gfp_any(), which returns GFP_ATOMIC +under the lock. + +Fixes: 081efd18326e ("ipv6: Protect nh->f6i_list with spinlock and flag.") +Reported-by: AutonomousCodeSecurity@microsoft.com +Signed-off-by: Xiang Mei (Microsoft) +Reviewed-by: Ido Schimmel +Link: https://patch.msgid.link/20260722002951.2614721-1-xmei5@asu.edu +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/ipv4/nexthop.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c +index dc95a3d6e899e..8b1f3382a998a 100644 +--- a/net/ipv4/nexthop.c ++++ b/net/ipv4/nexthop.c +@@ -1597,14 +1597,21 @@ static int fib6_check_nh_list(struct nexthop *old, struct nexthop *new, + struct netlink_ext_ack *extack) + { + struct fib6_info *f6i; ++ int err = 0; + + if (list_empty(&old->f6i_list)) + return 0; + ++ spin_lock_bh(&old->lock); + list_for_each_entry(f6i, &old->f6i_list, nh_list) { +- if (check_src_addr(&f6i->fib6_src.addr, extack) < 0) +- return -EINVAL; ++ err = check_src_addr(&f6i->fib6_src.addr, extack); ++ if (err) ++ break; + } ++ spin_unlock_bh(&old->lock); ++ ++ if (err) ++ return err; + + return fib6_check_nexthop(new, NULL, extack); + } +@@ -2521,8 +2528,10 @@ static void __nexthop_replace_notify(struct net *net, struct nexthop *nh, + fi->nh_updated = false; + } + ++ spin_lock_bh(&nh->lock); + list_for_each_entry(f6i, &nh->f6i_list, nh_list) + fib6_rt_update(net, f6i, info); ++ spin_unlock_bh(&nh->lock); + } + + /* send RTM_NEWROUTE with REPLACE flag set for all FIB entries +-- +2.53.0 + diff --git a/queue-7.1/ntfs-drop-stale-page-cache-when-shrinking-a-non-resi.patch b/queue-7.1/ntfs-drop-stale-page-cache-when-shrinking-a-non-resi.patch new file mode 100644 index 0000000000..af7964fba0 --- /dev/null +++ b/queue-7.1/ntfs-drop-stale-page-cache-when-shrinking-a-non-resi.patch @@ -0,0 +1,48 @@ +From 24a4dcae24dd9a5b3ec3433ae81d60eeed4f3587 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 16:49:57 +0900 +Subject: ntfs: drop stale page-cache when shrinking a non-resident attr + +From: Namjae Jeon + +[ Upstream commit 4e646ecd44759e552b0b9ccd995f3f608daab414 ] + +ntfs_non_resident_attr_shrink() shrinks attribute sizes but fails to +trim the page cache. This leaves orphaned dirty folios beyond the new +end of the attribute, leading to writeback failures (-ENOENT), data +loss, and $EA chain corruption. + +Fix this by truncating the page cache to the new size immediately after +updating the sizes, preventing writeback from flushing out-of-range folios. + +Fixes: 495e90fa3348 ("ntfs: update attrib operations") +Signed-off-by: Namjae Jeon +Signed-off-by: Sasha Levin +--- + fs/ntfs/attrib.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c +index 0f1d0b54cfb52..22338ad541279 100644 +--- a/fs/ntfs/attrib.c ++++ b/fs/ntfs/attrib.c +@@ -4292,6 +4292,16 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz + ni->initialized_size = newsize; + ctx->attr->data.non_resident.initialized_size = cpu_to_le64(newsize); + } ++ ++ /* ++ * Drop any page-cache folios that now lie beyond the shrunk ++ * attribute. The clusters backing them have just been freed and the ++ * runlist truncated, so leaving stale dirty folios around makes a ++ * later writeback map a vcn past the new allocation, which fails with ++ * -ENOENT and loses the write. ++ */ ++ truncate_inode_pages(VFS_I(ni)->i_mapping, newsize); ++ + /* Update data size in the index. */ + if (ni->type == AT_DATA && ni->name == AT_UNNAMED) + NInoSetFileNameDirty(ni); +-- +2.53.0 + diff --git a/queue-7.1/ntfs-harden-runlist-realloc-size-calculations.patch b/queue-7.1/ntfs-harden-runlist-realloc-size-calculations.patch new file mode 100644 index 0000000000..5c7e0510ed --- /dev/null +++ b/queue-7.1/ntfs-harden-runlist-realloc-size-calculations.patch @@ -0,0 +1,109 @@ +From 61ec872dfc62a72c2694c7797a4758ba15252d39 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Jul 2026 14:22:57 +0900 +Subject: ntfs: harden runlist realloc size calculations + +From: Namjae Jeon + +[ Upstream commit 8bed376124ab4505b70083a2b91f2c7ef6d51e24 ] + +Add a shared helper to safely convert runlist element counts to byte sizes +using overflow checks, and use it in both ntfs_rl_realloc() and +ntfs_rl_realloc_nofail(). + +Fixes: 11ccc9107dc4 ("ntfs: update runlist handling and cluster allocator") +Co-developed-by: Alper Mudar +Signed-off-by: Alper Mudar +Tested-by: Alper Mudar +Signed-off-by: Namjae Jeon +Signed-off-by: Sasha Levin +--- + fs/ntfs/runlist.c | 50 +++++++++++++++++++++++++---------------------- + 1 file changed, 27 insertions(+), 23 deletions(-) + +diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c +index 15f1ae530ae13..8d2740cfddd73 100644 +--- a/fs/ntfs/runlist.c ++++ b/fs/ntfs/runlist.c +@@ -71,29 +71,46 @@ static inline void ntfs_rl_mc(struct runlist_element *dstbase, int dst, + * On success, return a pointer to the newly allocated, or recycled, memory. + * On error, return -errno. + */ +-struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl, +- int old_size, int new_size) ++static inline struct runlist_element *ntfs_rl_realloc_gfp(struct runlist_element *rl, ++ int old_size, int new_size, gfp_t gfp) + { + struct runlist_element *new_rl; ++ size_t new_bytes; ++ ++ if (old_size < 0 || new_size < 0) ++ return ERR_PTR(-EINVAL); + +- old_size = old_size * sizeof(*rl); +- new_size = new_size * sizeof(*rl); + if (old_size == new_size) + return rl; + +- new_rl = kvzalloc(new_size, GFP_NOFS); ++ if (check_mul_overflow(new_size, sizeof(*rl), &new_bytes)) ++ return ERR_PTR(-EINVAL); ++ ++ new_rl = kvzalloc(new_bytes, gfp); + if (unlikely(!new_rl)) + return ERR_PTR(-ENOMEM); + + if (likely(rl != NULL)) { +- if (unlikely(old_size > new_size)) +- old_size = new_size; +- memcpy(new_rl, rl, old_size); ++ size_t old_bytes; ++ ++ if (check_mul_overflow(old_size, sizeof(*rl), &old_bytes)) { ++ kvfree(new_rl); ++ return ERR_PTR(-EINVAL); ++ } ++ if (unlikely(old_bytes > new_bytes)) ++ old_bytes = new_bytes; ++ memcpy(new_rl, rl, old_bytes); + kvfree(rl); + } + return new_rl; + } + ++struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl, ++ int old_size, int new_size) ++{ ++ return ntfs_rl_realloc_gfp(rl, old_size, new_size, GFP_NOFS); ++} ++ + /* + * ntfs_rl_realloc_nofail - Reallocate memory for runlists + * @rl: original runlist +@@ -118,21 +135,8 @@ struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl, + static inline struct runlist_element *ntfs_rl_realloc_nofail(struct runlist_element *rl, + int old_size, int new_size) + { +- struct runlist_element *new_rl; +- +- old_size = old_size * sizeof(*rl); +- new_size = new_size * sizeof(*rl); +- if (old_size == new_size) +- return rl; +- +- new_rl = kvmalloc(new_size, GFP_NOFS | __GFP_NOFAIL); +- if (likely(rl != NULL)) { +- if (unlikely(old_size > new_size)) +- old_size = new_size; +- memcpy(new_rl, rl, old_size); +- kvfree(rl); +- } +- return new_rl; ++ return ntfs_rl_realloc_gfp(rl, old_size, new_size, ++ GFP_NOFS | __GFP_NOFAIL); + } + + /* +-- +2.53.0 + diff --git a/queue-7.1/ntfs-preserve-recall_on_open-on-wsl-special-file-rep.patch b/queue-7.1/ntfs-preserve-recall_on_open-on-wsl-special-file-rep.patch new file mode 100644 index 0000000000..363ba929b1 --- /dev/null +++ b/queue-7.1/ntfs-preserve-recall_on_open-on-wsl-special-file-rep.patch @@ -0,0 +1,48 @@ +From ff8886849d9036c0d3bedc1591f11d811ed5a2bf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 11 Jul 2026 16:36:55 +0900 +Subject: ntfs: preserve RECALL_ON_OPEN on WSL special-file reparse points + +From: Namjae Jeon + +[ Upstream commit 523307b8516fc740895238af8473aa0630b3e088 ] + +When creating a WSL special file (socket, fifo, character or block +device), __ntfs_create() sets FILE_ATTRIBUTE_RECALL_ON_OPEN in ni->flags +as valid_reparse_data() requires for these tags. This flag is +intentionally absent from $FILE_NAME, so the subsequent reload + + ni->flags = fn->file_attributes; + +drops it from ni->flags, the authoritative copy written back to +$STANDARD_INFORMATION. The on-disk file_attributes becomes 0x00000404 +instead of 0x00040404, and after a remount valid_reparse_data() rejects +the reparse point while fsck reports "$REPARSE_POINT data is corrupted". + +Preserve the RECALL_ON_OPEN bit across the reload. Symlinks do not set +that bit, so they are unaffected. + +Fixes: af0db57d4293 ("ntfs: update inode operations") +Signed-off-by: Namjae Jeon +Signed-off-by: Sasha Levin +--- + fs/ntfs/namei.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c +index a20ef06087368..712c9ccc8b254 100644 +--- a/fs/ntfs/namei.c ++++ b/fs/ntfs/namei.c +@@ -683,7 +683,8 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d + mutex_unlock(&dir_ni->mrec_lock); + mutex_unlock(&ni->mrec_lock); + +- ni->flags = fn->file_attributes; ++ ni->flags = fn->file_attributes | ++ (ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN); + /* Set the sequence number. */ + vi->i_generation = ni->seq_no; + set_nlink(vi, 1); +-- +2.53.0 + diff --git a/queue-7.1/octeontx2-af-block-vfs-from-clobbering-special-cgx-p.patch b/queue-7.1/octeontx2-af-block-vfs-from-clobbering-special-cgx-p.patch new file mode 100644 index 0000000000..d2d36268d8 --- /dev/null +++ b/queue-7.1/octeontx2-af-block-vfs-from-clobbering-special-cgx-p.patch @@ -0,0 +1,288 @@ +From b29087c17956e53822413e11b3815137416755c8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 13:42:29 +0530 +Subject: octeontx2-af: Block VFs from clobbering special CGX PKIND state + +From: Hariprasad Kelam + +[ Upstream commit 3bd438a58e910db5dc369aa25dfed1fc95f1b596 ] + +PF and VF NIX LFs that share a CGX LMAC reuse the same hardware PKIND +programming. When HiGig2 or EDSA parsing is enabled, a VF NIX LF alloc must +not reset the LMAC RX PKIND or default TX parse config over the PF setup. + +Add cgx_get_pkind() and rvu_cgx_is_pkind_config_permitted() so VFs skip +cgx_set_pkind(), rvu_npc_set_pkind(), and NIX_AF_LFX_TX_PARSE_CFG updates +when the LMAC is using NPC_RX_HIGIG_PKIND or NPC_RX_EDSA_PKIND. + +Fixes: 94d942c5fb97 ("octeontx2-af: Config pkind for CGX mapped PFs") +Cc: Geetha sowjanya +Signed-off-by: Hariprasad Kelam +Signed-off-by: Ratheesh Kannoth +Link: https://patch.msgid.link/20260722081229.1653619-1-rkannoth@marvell.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + .../net/ethernet/marvell/octeontx2/af/cgx.c | 12 +++ + .../net/ethernet/marvell/octeontx2/af/cgx.h | 1 + + .../net/ethernet/marvell/octeontx2/af/rvu.h | 2 + + .../ethernet/marvell/octeontx2/af/rvu_cgx.c | 79 +++++++++++++++++++ + .../ethernet/marvell/octeontx2/af/rvu_nix.c | 22 +++++- + .../ethernet/marvell/octeontx2/af/rvu_npc.c | 29 ++++--- + 6 files changed, 131 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +index 2e94d5105016b..f5fd6138c352f 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +@@ -518,6 +518,18 @@ int cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind) + return 0; + } + ++int cgx_get_pkind(void *cgxd, u8 lmac_id, int *pkind) ++{ ++ struct cgx *cgx = cgxd; ++ ++ if (!is_lmac_valid(cgx, lmac_id)) ++ return -ENODEV; ++ ++ *pkind = cgx_read(cgx, lmac_id, cgx->mac_ops->rxid_map_offset); ++ *pkind = *pkind & 0x3F; ++ return 0; ++} ++ + static u8 cgx_get_lmac_type(void *cgxd, int lmac_id) + { + struct cgx *cgx = cgxd; +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h +index 92ccf343dfe04..8411a75dd723f 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h ++++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h +@@ -141,6 +141,7 @@ int cgx_get_cgxid(void *cgxd); + int cgx_get_lmac_cnt(void *cgxd); + void *cgx_get_pdata(int cgx_id); + int cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind); ++int cgx_get_pkind(void *cgxd, u8 lmac_id, int *pkind); + int cgx_lmac_evh_register(struct cgx_event_cb *cb, void *cgxd, int lmac_id); + int cgx_lmac_evh_unregister(void *cgxd, int lmac_id); + int cgx_get_tx_stats(void *cgxd, int lmac_id, int idx, u64 *tx_stat); +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +index 65397daae4c2f..635a7630238e6 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +@@ -1113,6 +1113,8 @@ void npc_read_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, + u8 *intf, u8 *ena); + int npc_config_cntr_default_entries(struct rvu *rvu, bool enable); + bool is_cgx_config_permitted(struct rvu *rvu, u16 pcifunc); ++bool rvu_cgx_check_permission_and_set_pkind(struct rvu *rvu, u16 pcifunc, int pkind); ++bool rvu_cgx_is_pkind_config_permitted(struct rvu *rvu, u16 pcifunc); + bool is_mac_feature_supported(struct rvu *rvu, int pf, int feature); + u32 rvu_cgx_get_fifolen(struct rvu *rvu); + void *rvu_first_cgx_pdata(struct rvu *rvu); +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +index 4ff3935ed3fe8..87d21889dc49e 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +@@ -1355,3 +1355,82 @@ void rvu_mac_reset(struct rvu *rvu, u16 pcifunc) + if (mac_ops->mac_reset(cgxd, lmac, !is_vf(pcifunc))) + dev_err(rvu->dev, "Failed to reset MAC\n"); + } ++ ++/* Do not allow CGX-mapped VFs to overwrite PKIND when special parse kinds ++ * (HiGig, EDSA, etc.) are in use on the shared LMAC. VFs must not program ++ * NPC_TX_DEF_PKIND on NIX_AF_LFX_TX_PARSE_CFG in that case: the PF owns ++ * parse mode and no separate NPC_TX_HIGIG_PKIND is installed on the VF LF. ++ * TX-parse callers skip the write when denied; rvu_lf_reset() clears each LF ++ * before alloc so the next permitted owner programs NPC_TX_DEF_PKIND. ++ */ ++bool rvu_cgx_is_pkind_config_permitted(struct rvu *rvu, u16 pcifunc) ++{ ++ int pf, err, rxpkind; ++ u8 cgx_id, lmac_id; ++ void *cgxd; ++ ++ pf = rvu_get_pf(rvu->pdev, pcifunc); ++ ++ if (!(pcifunc & RVU_PFVF_FUNC_MASK)) ++ return true; ++ ++ if (!is_pf_cgxmapped(rvu, pf)) ++ return true; ++ ++ rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); ++ cgxd = rvu_cgx_pdata(cgx_id, rvu); ++ err = cgx_get_pkind(cgxd, lmac_id, &rxpkind); ++ if (err) ++ return false; ++ ++ switch (rxpkind) { ++ case NPC_RX_HIGIG_PKIND: ++ case NPC_RX_EDSA_PKIND: ++ return false; ++ default: ++ return true; ++ } ++} ++ ++/* Do not allow CGX-mapped VFs to overwrite PKIND when special parse kinds ++ * (HiGig, EDSA, etc.) are in use on the shared LMAC. ++ */ ++bool rvu_cgx_check_permission_and_set_pkind(struct rvu *rvu, u16 pcifunc, int pkind) ++{ ++ int pf, err, rxpkind; ++ u8 cgx_id, lmac_id; ++ struct cgx *cgxd; ++ ++ pf = rvu_get_pf(rvu->pdev, pcifunc); ++ ++ if (!is_pf_cgxmapped(rvu, pf)) ++ return false; ++ ++ rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); ++ cgxd = rvu_cgx_pdata(cgx_id, rvu); ++ ++ mutex_lock(&cgxd->lock); ++ if (!is_vf(pcifunc)) ++ goto set; ++ ++ err = cgx_get_pkind(cgxd, lmac_id, &rxpkind); ++ if (err) ++ goto err; ++ ++ switch (rxpkind) { ++ case NPC_RX_HIGIG_PKIND: ++ case NPC_RX_EDSA_PKIND: ++ goto err; ++ default: ++ break; ++ } ++ ++set: ++ cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, pkind); ++ mutex_unlock(&cgxd->lock); ++ return true; ++ ++err: ++ mutex_unlock(&cgxd->lock); ++ return false; ++} +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +index 6a0ce2665031d..964bcaae098e2 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +@@ -363,8 +363,8 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf, + pfvf->tx_chan_cnt = 1; + rsp->tx_link = cgx_id * hw->lmac_per_cgx + lmac_id; + +- cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, pkind); +- rvu_npc_set_pkind(rvu, pkind, pfvf); ++ if (rvu_cgx_check_permission_and_set_pkind(rvu, pcifunc, pkind)) ++ rvu_npc_set_pkind(rvu, pkind, pfvf); + break; + case NIX_INTF_TYPE_LBK: + vf = (pcifunc & RVU_PFVF_FUNC_MASK) - 1; +@@ -1505,13 +1505,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, + struct nix_lf_alloc_req *req, + struct nix_lf_alloc_rsp *rsp) + { +- int nixlf, qints, hwctx_size, intf, rc = 0; ++ int nixlf, qints, hwctx_size, intf, rc = 0, pf; + u16 bcast, mcast, promisc, ucast; + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; ++ u8 cgx_id = 0, lmac_id = 0; + bool rules_created = false; + struct rvu_block *block; + struct rvu_pfvf *pfvf; ++ struct cgx *cgxd; + u64 cfg, ctx_cfg; + int blkaddr; + +@@ -1685,8 +1687,20 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, + rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_CFG(nixlf), req->rx_cfg); + + /* Configure pkind for TX parse config */ ++ ++ pf = rvu_get_pf(rvu->pdev, pcifunc); + cfg = NPC_TX_DEF_PKIND; +- rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg); ++ ++ if (is_pf_cgxmapped(rvu, pf) && is_vf(pcifunc)) { ++ rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); ++ cgxd = rvu_cgx_pdata(cgx_id, rvu); ++ mutex_lock(&cgxd->lock); ++ if (rvu_cgx_is_pkind_config_permitted(rvu, pcifunc)) ++ rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg); ++ mutex_unlock(&cgxd->lock); ++ } else { ++ rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg); ++ } + + if (is_rep_dev(rvu, pcifunc)) { + pfvf->tx_chan_base = RVU_SWITCH_LBK_CHAN; +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +index 41d92eb652af9..1e090508f958d 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +@@ -19,6 +19,7 @@ + #include "cn20k/npc.h" + #include "rvu_npc.h" + #include "cn20k/reg.h" ++#include "lmac_common.h" + + #define RSVD_MCAM_ENTRIES_PER_PF 3 /* Broadcast, Promisc and AllMulticast */ + #define RSVD_MCAM_ENTRIES_PER_NIXLF 1 /* Ucast for LFs */ +@@ -3912,10 +3913,11 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir, + + { + struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc); +- int blkaddr, nixlf, rc, intf_mode; + int pf = rvu_get_pf(rvu->pdev, pcifunc); ++ int blkaddr, nixlf, rc, intf_mode; ++ u8 cgx_id = 0, lmac_id = 0; + u64 rxpkind, txpkind; +- u8 cgx_id, lmac_id; ++ struct cgx *cgxd; + + /* use default pkind to disable edsa/higig */ + rxpkind = rvu_npc_get_pkind(rvu, pf); +@@ -3939,12 +3941,8 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir, + /* rx pkind set req valid only for cgx mapped PFs */ + if (!is_cgx_config_permitted(rvu, pcifunc)) + return 0; +- rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); +- +- rc = cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, +- rxpkind); +- if (rc) +- return rc; ++ if (!rvu_cgx_check_permission_and_set_pkind(rvu, pcifunc, rxpkind)) ++ return -EINVAL; + } + + if (dir & PKIND_TX) { +@@ -3953,8 +3951,19 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir, + if (rc) + return rc; + +- rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), +- txpkind); ++ if (is_pf_cgxmapped(rvu, pf) && is_vf(pcifunc)) { ++ rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, ++ &lmac_id); ++ cgxd = rvu_cgx_pdata(cgx_id, rvu); ++ mutex_lock(&cgxd->lock); ++ if (rvu_cgx_is_pkind_config_permitted(rvu, pcifunc)) ++ rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), ++ txpkind); ++ mutex_unlock(&cgxd->lock); ++ } else { ++ rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), ++ txpkind); ++ } + } + + pfvf->intf_mode = intf_mode; +-- +2.53.0 + diff --git a/queue-7.1/octeontx2-cn20k-coordinate-default-rules-with-nix-lf.patch b/queue-7.1/octeontx2-cn20k-coordinate-default-rules-with-nix-lf.patch new file mode 100644 index 0000000000..eaf0ca4bd2 --- /dev/null +++ b/queue-7.1/octeontx2-cn20k-coordinate-default-rules-with-nix-lf.patch @@ -0,0 +1,319 @@ +From f6606f107ec6dc5f7ee22ed259bacc3879ca87e9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 9 Jun 2026 09:34:50 +0530 +Subject: octeontx2: cn20k: Coordinate default rules with NIX LF lifecycle + +From: Ratheesh Kannoth + +[ Upstream commit aac055dbc0fadf64c9d6fbcfc066b8ba33216dc4 ] + +Add NIX_LF_DONT_FREE_DFT_IDXS so the PF can send NIX LF free during hw +reinit or teardown without the AF freeing CN20K default NPC rule indexes +while the driver still owns that state (otx2_init_hw_resources and +otx2_free_hw_resources). + +On CN20K, allocate default NPC rules from NIX LF alloc before +nix_interface_init, roll back with npc_cn20k_dft_rules_free on failure, +and free from NIX LF free when the new flag is not set. Tighten +rvu_mbox_handler_nix_lf_alloc error handling: use a single rc, propagate +qmem_alloc and other errors, and set -ENOMEM only when kcalloc fails +(remove the blanket -ENOMEM at the free_mem path). + +Signed-off-by: Ratheesh Kannoth +Link: https://patch.msgid.link/20260609040453.711932-7-rkannoth@marvell.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: 3bd438a58e91 ("octeontx2-af: Block VFs from clobbering special CGX PKIND state") +Signed-off-by: Sasha Levin +--- + .../net/ethernet/marvell/octeontx2/af/mbox.h | 1 + + .../ethernet/marvell/octeontx2/af/rvu_nix.c | 77 +++++++++++++------ + .../ethernet/marvell/octeontx2/af/rvu_npc.c | 20 +++-- + .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 6 +- + 4 files changed, 69 insertions(+), 35 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +index 44fdd6ba7307c..714e47f68d932 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h ++++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +@@ -1008,6 +1008,7 @@ struct nix_lf_free_req { + struct mbox_msghdr hdr; + #define NIX_LF_DISABLE_FLOWS BIT_ULL(0) + #define NIX_LF_DONT_FREE_TX_VTAG BIT_ULL(1) ++#define NIX_LF_DONT_FREE_DFT_IDXS BIT_ULL(2) + u64 flags; + }; + +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +index 219fc44ab68d4..6a0ce2665031d 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +@@ -16,6 +16,7 @@ + #include "cgx.h" + #include "lmac_common.h" + #include "rvu_npc_hash.h" ++#include "cn20k/npc.h" + + static void nix_free_tx_vtag_entries(struct rvu *rvu, u16 pcifunc); + static int rvu_nix_get_bpid(struct rvu *rvu, struct nix_bp_cfg_req *req, +@@ -1504,9 +1505,11 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, + struct nix_lf_alloc_req *req, + struct nix_lf_alloc_rsp *rsp) + { +- int nixlf, qints, hwctx_size, intf, err, rc = 0; ++ int nixlf, qints, hwctx_size, intf, rc = 0; ++ u16 bcast, mcast, promisc, ucast; + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; ++ bool rules_created = false; + struct rvu_block *block; + struct rvu_pfvf *pfvf; + u64 cfg, ctx_cfg; +@@ -1560,8 +1563,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, + return NIX_AF_ERR_RSS_GRPS_INVALID; + + /* Reset this NIX LF */ +- err = rvu_lf_reset(rvu, block, nixlf); +- if (err) { ++ rc = rvu_lf_reset(rvu, block, nixlf); ++ if (rc) { + dev_err(rvu->dev, "Failed to reset NIX%d LF%d\n", + block->addr - BLKADDR_NIX0, nixlf); + return NIX_AF_ERR_LF_RESET; +@@ -1571,13 +1574,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, + + /* Alloc NIX RQ HW context memory and config the base */ + hwctx_size = 1UL << ((ctx_cfg >> 4) & 0xF); +- err = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size); +- if (err) ++ rc = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size); ++ if (rc) + goto free_mem; + + pfvf->rq_bmap = kcalloc(req->rq_cnt, sizeof(long), GFP_KERNEL); +- if (!pfvf->rq_bmap) ++ if (!pfvf->rq_bmap) { ++ rc = -ENOMEM; + goto free_mem; ++ } + + rvu_write64(rvu, blkaddr, NIX_AF_LFX_RQS_BASE(nixlf), + (u64)pfvf->rq_ctx->iova); +@@ -1588,13 +1593,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, + + /* Alloc NIX SQ HW context memory and config the base */ + hwctx_size = 1UL << (ctx_cfg & 0xF); +- err = qmem_alloc(rvu->dev, &pfvf->sq_ctx, req->sq_cnt, hwctx_size); +- if (err) ++ rc = qmem_alloc(rvu->dev, &pfvf->sq_ctx, req->sq_cnt, hwctx_size); ++ if (rc) + goto free_mem; + + pfvf->sq_bmap = kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL); +- if (!pfvf->sq_bmap) ++ if (!pfvf->sq_bmap) { ++ rc = -ENOMEM; + goto free_mem; ++ } + + rvu_write64(rvu, blkaddr, NIX_AF_LFX_SQS_BASE(nixlf), + (u64)pfvf->sq_ctx->iova); +@@ -1604,13 +1611,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, + + /* Alloc NIX CQ HW context memory and config the base */ + hwctx_size = 1UL << ((ctx_cfg >> 8) & 0xF); +- err = qmem_alloc(rvu->dev, &pfvf->cq_ctx, req->cq_cnt, hwctx_size); +- if (err) ++ rc = qmem_alloc(rvu->dev, &pfvf->cq_ctx, req->cq_cnt, hwctx_size); ++ if (rc) + goto free_mem; + + pfvf->cq_bmap = kcalloc(req->cq_cnt, sizeof(long), GFP_KERNEL); +- if (!pfvf->cq_bmap) ++ if (!pfvf->cq_bmap) { ++ rc = -ENOMEM; + goto free_mem; ++ } + + rvu_write64(rvu, blkaddr, NIX_AF_LFX_CQS_BASE(nixlf), + (u64)pfvf->cq_ctx->iova); +@@ -1620,18 +1629,18 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, + + /* Initialize receive side scaling (RSS) */ + hwctx_size = 1UL << ((ctx_cfg >> 12) & 0xF); +- err = nixlf_rss_ctx_init(rvu, blkaddr, pfvf, nixlf, req->rss_sz, +- req->rss_grps, hwctx_size, req->way_mask, +- !!(req->flags & NIX_LF_RSS_TAG_LSB_AS_ADDER)); +- if (err) ++ rc = nixlf_rss_ctx_init(rvu, blkaddr, pfvf, nixlf, req->rss_sz, ++ req->rss_grps, hwctx_size, req->way_mask, ++ !!(req->flags & NIX_LF_RSS_TAG_LSB_AS_ADDER)); ++ if (rc) + goto free_mem; + + /* Alloc memory for CQINT's HW contexts */ + cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2); + qints = (cfg >> 24) & 0xFFF; + hwctx_size = 1UL << ((ctx_cfg >> 24) & 0xF); +- err = qmem_alloc(rvu->dev, &pfvf->cq_ints_ctx, qints, hwctx_size); +- if (err) ++ rc = qmem_alloc(rvu->dev, &pfvf->cq_ints_ctx, qints, hwctx_size); ++ if (rc) + goto free_mem; + + rvu_write64(rvu, blkaddr, NIX_AF_LFX_CINTS_BASE(nixlf), +@@ -1644,8 +1653,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, + cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2); + qints = (cfg >> 12) & 0xFFF; + hwctx_size = 1UL << ((ctx_cfg >> 20) & 0xF); +- err = qmem_alloc(rvu->dev, &pfvf->nix_qints_ctx, qints, hwctx_size); +- if (err) ++ rc = qmem_alloc(rvu->dev, &pfvf->nix_qints_ctx, qints, hwctx_size); ++ if (rc) + goto free_mem; + + rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_BASE(nixlf), +@@ -1689,10 +1698,22 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, + if (is_sdp_pfvf(rvu, pcifunc)) + intf = NIX_INTF_TYPE_SDP; + +- err = nix_interface_init(rvu, pcifunc, intf, nixlf, rsp, +- !!(req->flags & NIX_LF_LBK_BLK_SEL)); +- if (err) +- goto free_mem; ++ if (is_cn20k(rvu->pdev)) { ++ rc = npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &bcast, &mcast, ++ &promisc, &ucast); ++ if (rc) { ++ rc = npc_cn20k_dft_rules_alloc(rvu, pcifunc); ++ if (rc) ++ goto free_mem; ++ ++ rules_created = true; ++ } ++ } ++ ++ rc = nix_interface_init(rvu, pcifunc, intf, nixlf, rsp, ++ !!(req->flags & NIX_LF_LBK_BLK_SEL)); ++ if (rc) ++ goto free_dft; + + /* Disable NPC entries as NIXLF's contexts are not initialized yet */ + rvu_npc_disable_default_entries(rvu, pcifunc, nixlf); +@@ -1704,9 +1725,12 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, + + goto exit; + ++free_dft: ++ if (is_cn20k(rvu->pdev) && rules_created) ++ npc_cn20k_dft_rules_free(rvu, pcifunc); ++ + free_mem: + nix_ctx_free(rvu, pfvf); +- rc = -ENOMEM; + + exit: + /* Set macaddr of this PF/VF */ +@@ -1780,6 +1804,9 @@ int rvu_mbox_handler_nix_lf_free(struct rvu *rvu, struct nix_lf_free_req *req, + + nix_ctx_free(rvu, pfvf); + ++ if (is_cn20k(rvu->pdev) && !(req->flags & NIX_LF_DONT_FREE_DFT_IDXS)) ++ npc_cn20k_dft_rules_free(rvu, pcifunc); ++ + return 0; + } + +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +index 4994385a822b7..41d92eb652af9 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +@@ -1285,11 +1285,18 @@ void npc_enadis_default_mce_entry(struct rvu *rvu, u16 pcifunc, + struct nix_mce_list *mce_list; + int index, blkaddr, mce_idx; + struct rvu_pfvf *pfvf; ++ u16 ptr[4]; + + /* multicast pkt replication is not enabled for AF's VFs & SDP links */ + if (is_lbk_vf(rvu, pcifunc) || is_sdp_pfvf(rvu, pcifunc)) + return; + ++ /* In cn20k, only CGX mapped devices have default MCAST entry */ ++ if (is_cn20k(rvu->pdev) && ++ npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &ptr[0], &ptr[1], ++ &ptr[2], &ptr[3])) ++ return; ++ + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) + return; +@@ -1329,9 +1336,12 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc, + struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc); + struct npc_mcam *mcam = &rvu->hw->mcam; + int index, blkaddr; ++ u16 ptr[4]; + + /* only CGX or LBK interfaces have default entries */ +- if (is_cn20k(rvu->pdev) && !npc_is_cgx_or_lbk(rvu, pcifunc)) ++ if (is_cn20k(rvu->pdev) && ++ npc_cn20k_dft_rules_idx_get(rvu, pcifunc, &ptr[0], &ptr[1], ++ &ptr[2], &ptr[3])) + return; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); +@@ -4075,12 +4085,10 @@ void rvu_npc_clear_ucast_entry(struct rvu *rvu, int pcifunc, int nixlf) + + ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc, + nixlf, NIXLF_UCAST_ENTRY); +- if (ucast_idx < 0) { +- dev_err(rvu->dev, +- "%s: Error to get ucast entry for pcifunc=%#x\n", +- __func__, pcifunc); ++ ++ /* In cn20k, default rules are freed before detach rsrc */ ++ if (ucast_idx < 0) + return; +- } + + npc_enable_mcam_entry(rvu, mcam, blkaddr, ucast_idx, false); + +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +index 617a0db97e804..2e33b33ec9934 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +@@ -1053,7 +1053,6 @@ irqreturn_t otx2_pfaf_mbox_intr_handler(int irq, void *pf_irq) + /* Clear the IRQ */ + otx2_write64(pf, RVU_PF_INT, BIT_ULL(0)); + +- + mbox_data = otx2_read64(pf, RVU_PF_PFAF_MBOX0); + + if (mbox_data & MBOX_UP_MSG) { +@@ -1727,7 +1726,7 @@ int otx2_init_hw_resources(struct otx2_nic *pf) + mutex_lock(&mbox->lock); + free_req = otx2_mbox_alloc_msg_nix_lf_free(mbox); + if (free_req) { +- free_req->flags = NIX_LF_DISABLE_FLOWS; ++ free_req->flags = NIX_LF_DISABLE_FLOWS | NIX_LF_DONT_FREE_DFT_IDXS; + if (otx2_sync_mbox_msg(mbox)) + dev_err(pf->dev, "%s failed to free nixlf\n", __func__); + } +@@ -1801,7 +1800,7 @@ void otx2_free_hw_resources(struct otx2_nic *pf) + /* Reset NIX LF */ + free_req = otx2_mbox_alloc_msg_nix_lf_free(mbox); + if (free_req) { +- free_req->flags = NIX_LF_DISABLE_FLOWS; ++ free_req->flags = NIX_LF_DISABLE_FLOWS | NIX_LF_DONT_FREE_DFT_IDXS; + if (!(pf->flags & OTX2_FLAG_PF_SHUTDOWN)) + free_req->flags |= NIX_LF_DONT_FREE_TX_VTAG; + if (otx2_sync_mbox_msg(mbox)) +@@ -1924,7 +1923,6 @@ int otx2_alloc_queue_mem(struct otx2_nic *pf) + struct otx2_qset *qset = &pf->qset; + struct otx2_cq_poll *cq_poll; + +- + /* RQ and SQs are mapped to different CQs, + * so find out max CQ IRQs (i.e CINTs) needed. + */ +-- +2.53.0 + diff --git a/queue-7.1/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch b/queue-7.1/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch new file mode 100644 index 0000000000..4ced0655f2 --- /dev/null +++ b/queue-7.1/octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch @@ -0,0 +1,42 @@ +From 07eddcd95fb9d7a6456662c41c6d90343ddf9913 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 12:58:31 +0530 +Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop + +From: Suman Ghosh + +[ Upstream commit 16809472409d998afcda402e32b8229b389337c4 ] + +During link down event, we were doing netif_tx_stop_all_queues() first +and then netif_carrier_off(). This can cause a potential race since +carrier is still on during down event. This patch reverse the calling +order to fix the issue. + +Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications") +Signed-off-by: Suman Ghosh +Signed-off-by: Ratheesh Kannoth +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +index 2e33b33ec9934..c995f29008590 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +@@ -889,8 +889,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf) + netif_carrier_on(netdev); + netif_tx_start_all_queues(netdev); + } else { +- netif_tx_stop_all_queues(netdev); + netif_carrier_off(netdev); ++ netif_tx_stop_all_queues(netdev); + } + } + +-- +2.53.0 + diff --git a/queue-7.1/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch b/queue-7.1/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch new file mode 100644 index 0000000000..18bdfe2011 --- /dev/null +++ b/queue-7.1/of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch @@ -0,0 +1,66 @@ +From 8c3cd3851fa580efeb109dbd65c770991befc956 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 14 Jun 2026 22:38:06 +0900 +Subject: of: reserved_mem: prevent OOB when too many dynamic regions are + defined + +From: Sang-Heon Jeon + +[ Upstream commit db3dbdfea1b8f38774419c5c2c14e4b81c48708d ] + +On boot, fdt_scan_reserved_mem() saves each dynamically-placed +/reserved-memory subnode into a local array of size +MAX_RESERVED_REGIONS. + +If the device tree defines more than MAX_RESERVED_REGIONS +dynamically-placed regions, fdt_scan_reserved_mem() writes past the +end of the local array. + +Add a bounds check that logs an error and skips the excess regions, +restoring the original behavior. + +Fixes: 8a6e02d0c00e ("of: reserved_mem: Restructure how the reserved memory regions are processed") +Signed-off-by: Sang-Heon Jeon +Link: https://patch.msgid.link/20260614133807.2165124-2-ekffu200098@gmail.com +Signed-off-by: Rob Herring (Arm) +Signed-off-by: Sasha Levin +--- + drivers/of/of_reserved_mem.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c +index deaea58c74f2a..47041d195dff8 100644 +--- a/drivers/of/of_reserved_mem.c ++++ b/drivers/of/of_reserved_mem.c +@@ -351,6 +351,7 @@ int __init fdt_scan_reserved_mem(void) + err = __reserved_mem_reserve_reg(child, uname); + if (!err) + count++; ++ + /* + * Save the nodes for the dynamically-placed regions + * into an array which will be used for allocation right +@@ -358,10 +359,17 @@ int __init fdt_scan_reserved_mem(void) + * or marked as no-map. This is done to avoid dynamically + * allocating from one of the statically-placed regions. + */ +- if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) { +- dynamic_nodes[dynamic_nodes_cnt] = child; +- dynamic_nodes_cnt++; ++ if (err != -ENOENT || !of_get_flat_dt_prop(child, "size", NULL)) ++ continue; ++ ++ if (dynamic_nodes_cnt == MAX_RESERVED_REGIONS) { ++ pr_err("too many defined dynamic regions, skip '%s'\n", ++ uname); ++ continue; + } ++ ++ dynamic_nodes[dynamic_nodes_cnt] = child; ++ dynamic_nodes_cnt++; + } + for (int i = 0; i < dynamic_nodes_cnt; i++) { + const char *uname; +-- +2.53.0 + diff --git a/queue-7.1/phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch b/queue-7.1/phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch new file mode 100644 index 0000000000..dd3d437055 --- /dev/null +++ b/queue-7.1/phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch @@ -0,0 +1,38 @@ +From 1f21100d61aa0a9c0badf56ca531da3621d834ac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 18 Jul 2026 15:01:31 +0530 +Subject: phy: qcom: m31-eusb2: Fix return value of init call + +From: Krishna Kurapati + +[ Upstream commit 361f533a2dce2c2841fe4dc0c9d85a67117edf95 ] + +The init call currently returns success irrespective of any failures +during repeater init or clock enablement. Return appropriate error value +in the init call failure path. + +Fixes: 9c8504861cc4 ("phy: qcom: Add M31 based eUSB2 PHY driver") +Signed-off-by: Krishna Kurapati +Link: https://patch.msgid.link/20260718-m31-eusb2-fix-v1-1-8588a1b94d76@oss.qualcomm.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/qualcomm/phy-qcom-m31-eusb2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c +index 68f1ba8fec4ad..9434bd22ef32d 100644 +--- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c ++++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c +@@ -221,7 +221,7 @@ static int m31eusb2_phy_init(struct phy *uphy) + disable_vreg: + regulator_bulk_disable(M31_EUSB_NUM_VREGS, phy->vregs); + +- return 0; ++ return ret; + } + + static int m31eusb2_phy_exit(struct phy *uphy) +-- +2.53.0 + diff --git a/queue-7.1/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch b/queue-7.1/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch new file mode 100644 index 0000000000..769e8d3993 --- /dev/null +++ b/queue-7.1/phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch @@ -0,0 +1,76 @@ +From 1fc66ed79f32976ce0c360d6584272b9e5621452 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 21:08:30 +0530 +Subject: phy: zynqmp: fix clock error handling in xpsgtr_phy_init() + +From: Radhey Shyam Pandey + +[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ] + +Propagate clk_prepare_enable() failures to the caller instead of +returning success, and disable the reference clock on initialization +error paths to avoid leaking clock references when phy_exit() is not +called. + +Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save") +Signed-off-by: Radhey Shyam Pandey +Reviewed-by: Michal Simek +Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index fe6b4925d1662..c8230f2bda629 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -658,12 +658,13 @@ static int xpsgtr_phy_init(struct phy *phy) + { + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; +- int ret = 0; ++ int ret; + + mutex_lock(>r_dev->gtr_mutex); + + /* Configure and enable the clock when peripheral phy_init call */ +- if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk])) ++ ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]); ++ if (ret) + goto out; + + /* Skip initialization if not required. */ +@@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy) + if (gtr_dev->tx_term_fix) { + ret = xpsgtr_phy_tx_term_fix(gtr_phy); + if (ret < 0) +- goto out; ++ goto out_disable_clk; + + gtr_dev->tx_term_fix = false; + } +@@ -687,7 +688,7 @@ static int xpsgtr_phy_init(struct phy *phy) + */ + ret = xpsgtr_configure_pll(gtr_phy); + if (ret) +- goto out; ++ goto out_disable_clk; + + xpsgtr_lane_set_protocol(gtr_phy); + +@@ -705,6 +706,10 @@ static int xpsgtr_phy_init(struct phy *phy) + break; + } + ++ goto out; ++ ++out_disable_clk: ++ clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]); + out: + mutex_unlock(>r_dev->gtr_mutex); + return ret; +-- +2.53.0 + diff --git a/queue-7.1/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch b/queue-7.1/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch new file mode 100644 index 0000000000..43f7559b8c --- /dev/null +++ b/queue-7.1/phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch @@ -0,0 +1,56 @@ +From 192e9e138d4eaf6ffbe3f6fcd591f6320429e4dd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 21:08:31 +0530 +Subject: phy: zynqmp: fix runtime PM leak on probe allocation failure + +From: Radhey Shyam Pandey + +[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ] + +Allocate saved_regs before pm_runtime_resume_and_get() so a +devm_kmalloc() failure does not leave an unreleased runtime PM usage +counter. + +Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume") +Signed-off-by: Radhey Shyam Pandey +Reviewed-by: Michal Simek +Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +--- + drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c +index c8230f2bda629..2138f5399821a 100644 +--- a/drivers/phy/xilinx/phy-zynqmp.c ++++ b/drivers/phy/xilinx/phy-zynqmp.c +@@ -1044,6 +1044,12 @@ static int xpsgtr_probe(struct platform_device *pdev) + return PTR_ERR(provider); + } + ++ gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, ++ sizeof(save_reg_address), ++ GFP_KERNEL); ++ if (!gtr_dev->saved_regs) ++ return -ENOMEM; ++ + pm_runtime_set_active(gtr_dev->dev); + pm_runtime_enable(gtr_dev->dev); + +@@ -1053,12 +1059,6 @@ static int xpsgtr_probe(struct platform_device *pdev) + return ret; + } + +- gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, +- sizeof(save_reg_address), +- GFP_KERNEL); +- if (!gtr_dev->saved_regs) +- return -ENOMEM; +- + return 0; + } + +-- +2.53.0 + diff --git a/queue-7.1/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch b/queue-7.1/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch new file mode 100644 index 0000000000..e02e736885 --- /dev/null +++ b/queue-7.1/pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch @@ -0,0 +1,58 @@ +From 713235a971bc50bfd57e6cce4e1ccd54ad9f19a5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 11:28:44 -0500 +Subject: pinctrl-amd: Don't clear S4 wake bits at probe + +From: Mario Limonciello + +[ Upstream commit ffe8a0c6b55285ceaf2f42fc20c3a0594d14f1e9 ] + +commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") +introduced a regression where Wake-on-LAN no longer works after suspend +or shutdown on some AMD platforms. + +Firmware-programmed S4 wake bits for devices like PCIe NICs using PCI +PME are cleared at probe, but nothing restores them. Unlike S0i3/S3 wake +sources that use enable_irq_wake() -> amd_gpio_irq_set_wake(), PCIe PME +does not use GPIO IRQ infrastructure and relies on firmware configuration. + +The original intent of commit 6bc3462a0f5e ("pinctrl: amd: Mask wake +bits on probe again") was to clear spurious wake bits left by firmware +to prevent unwanted wakeups. However, S4 wake bits are used for +hardware-level wake sources like WoL that bypass the kernel's IRQ wake +API. + +Fix by preserving S4 wake bits at probe and only clearing S0i3/S3 bits: +- Firmware-configured S4 wake sources (WoL) continue working +- Kernel maintains control of S3/S0i3 wake policy via set_wake() +- S3-only wake sources work correctly per commit f31f33dbb3ba ("pinctrl: + amd: Take suspend type into consideration which pins are non-wake") + +The trade-off is that firmware-programmed spurious S4 wake bits remain +set, but this is less problematic than breaking WoL. + +Fixes: 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") +Signed-off-by: Mario Limonciello +Signed-off-by: Linus Walleij +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/pinctrl-amd.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c +index e3128b0045d22..15a398bb3be23 100644 +--- a/drivers/pinctrl/pinctrl-amd.c ++++ b/drivers/pinctrl/pinctrl-amd.c +@@ -884,8 +884,7 @@ static void amd_gpio_irq_init(struct amd_gpio *gpio_dev) + u32 pin_reg, mask; + int i; + +- mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) | +- BIT(WAKE_CNTRL_OFF_S4); ++ mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3); + + for (i = 0; i < desc->npins; i++) { + int pin = desc->pins[i].number; +-- +2.53.0 + diff --git a/queue-7.1/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch b/queue-7.1/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch new file mode 100644 index 0000000000..4935a666bb --- /dev/null +++ b/queue-7.1/pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch @@ -0,0 +1,58 @@ +From 780b8c510367bc77bd0c14a8f76f8766c0014178 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 26 Jun 2026 15:08:05 +0200 +Subject: pinctrl: qcom: sc8280xp: Add missing wakeup entries for GPIO143/151 + +From: Konrad Dybcio + +[ Upstream commit 437a8d2aa1aa442c4a176fdf4700a9b3bb0c8794 ] + +Pins 143 and 151 were not included in the PDC wakeup map. They are +normally used for PCIe2A and PCIe3a PERST# respectively, so they're +unlikely to be excercised in practice, but still add them for the sake +of completeness. + +Fixes: c0e4c71a9e7c ("pinctrl: qcom: Introduce sc8280xp TLMM driver") +Signed-off-by: Konrad Dybcio +Link: https://patch.msgid.link/20260626-topic-8280_pinctrl_wakeup-v1-1-2ccb267148f5@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-sc8280xp.c | 21 +++++++++++---------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +index 4056b9fa32f8c..e018bd11626ca 100644 +--- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c ++++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +@@ -1881,16 +1881,17 @@ static const struct msm_gpio_wakeirq_map sc8280xp_pdc_map[] = { + { 126, 200 }, { 127, 225 }, { 128, 262 }, { 129, 201 }, + { 130, 209 }, { 131, 173 }, { 132, 202 }, { 136, 210 }, + { 138, 171 }, { 139, 226 }, { 140, 227 }, { 142, 228 }, +- { 144, 229 }, { 145, 230 }, { 146, 231 }, { 148, 232 }, +- { 149, 233 }, { 150, 234 }, { 152, 235 }, { 154, 212 }, +- { 157, 213 }, { 161, 219 }, { 170, 236 }, { 171, 221 }, +- { 174, 222 }, { 175, 237 }, { 176, 223 }, { 177, 170 }, +- { 180, 238 }, { 181, 239 }, { 182, 240 }, { 183, 241 }, +- { 184, 242 }, { 185, 243 }, { 190, 178 }, { 193, 184 }, +- { 196, 185 }, { 198, 186 }, { 200, 174 }, { 201, 175 }, +- { 205, 176 }, { 206, 177 }, { 208, 187 }, { 210, 198 }, +- { 211, 199 }, { 212, 204 }, { 215, 205 }, { 220, 188 }, +- { 221, 194 }, { 223, 195 }, { 225, 196 }, { 227, 197 }, ++ { 143, 261 }, { 144, 229 }, { 145, 230 }, { 146, 231 }, ++ { 148, 232 }, { 149, 233 }, { 150, 234 }, { 151, 264 }, ++ { 152, 235 }, { 154, 212 }, { 157, 213 }, { 161, 219 }, ++ { 170, 236 }, { 171, 221 }, { 174, 222 }, { 175, 237 }, ++ { 176, 223 }, { 177, 170 }, { 180, 238 }, { 181, 239 }, ++ { 182, 240 }, { 183, 241 }, { 184, 242 }, { 185, 243 }, ++ { 190, 178 }, { 193, 184 }, { 196, 185 }, { 198, 186 }, ++ { 200, 174 }, { 201, 175 }, { 205, 176 }, { 206, 177 }, ++ { 208, 187 }, { 210, 198 }, { 211, 199 }, { 212, 204 }, ++ { 215, 205 }, { 220, 188 }, { 221, 194 }, { 223, 195 }, ++ { 225, 196 }, { 227, 197 }, + }; + + static struct msm_pinctrl_soc_data sc8280xp_pinctrl = { +-- +2.53.0 + diff --git a/queue-7.1/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch b/queue-7.1/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch new file mode 100644 index 0000000000..c3fd090c84 --- /dev/null +++ b/queue-7.1/pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch @@ -0,0 +1,70 @@ +From bfb457d9a75fea41516001316227400ed9fa87e5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 16 Jun 2026 17:24:53 +0530 +Subject: pinctrl: qcom: Unconditionally mark gpio as wakeup enable + +From: Sneh Mankad + +[ Upstream commit 859e02a369ab328a77dfcabf59562100e55f9c5c ] + +GPIO interrupts that are wakeup capable need to be forwarded to wakeup +capable parent irqchip. This is done via writing to it's wakeup_enable bit. + +Currently the bit is set only for PDC irqchip by checking skip_wake_irqs. +skip_wake_irqs is set to differentiate between parent irqchips MPM and +PDC. It is set when the parent irqchip is PDC to inform pinctrl about +skipping the IRQ setting up at TLMM. + +However, the functionality to forward GPIO interrupts during SoC low +power mode is needed regardless of which parent irqchip it is. +Without the functionality it is impossible for MPM irqchip to detect the +GPIO interrupt during SoC low power mode since for MPM irqchip the +skip_wake_irqs is always false. + +Remove skip_wake_irqs condition when setting wakeup enable bit to allow +forwarding GPIO interrupts for SoCs using MPM irqchip too. + +Fixes: 76b446f5b86e ("pinctrl: qcom: handle intr_target_reg wakeup_present/enable bits") +Signed-off-by: Sneh Mankad +Reviewed-by: Maulik Shah +Reviewed-by: Linus Walleij +Reviewed-by: Konrad Dybcio +Link: https://patch.msgid.link/20260616-enable_wakeup_capable_gpios-v3-1-fb59647d89cb@oss.qualcomm.com +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +--- + drivers/pinctrl/qcom/pinctrl-msm.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c +index 45b3a2763eb85..6a24f9b5e4a97 100644 +--- a/drivers/pinctrl/qcom/pinctrl-msm.c ++++ b/drivers/pinctrl/qcom/pinctrl-msm.c +@@ -1242,12 +1242,12 @@ static int msm_gpio_irq_reqres(struct irq_data *d) + /* + * If the wakeup_enable bit is present and marked as available for the + * requested GPIO, it should be enabled when the GPIO is marked as +- * wake irq in order to allow the interrupt event to be transfered to +- * the PDC HW. ++ * wake irq in order to allow the interrupt event to be transferred to ++ * the PDC/MPM HW. + * While the name implies only the wakeup event, it's also required for + * the interrupt event. + */ +- if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) { ++ if (g->intr_wakeup_present_bit) { + u32 intr_cfg; + + raw_spin_lock_irqsave(&pctrl->lock, flags); +@@ -1275,7 +1275,7 @@ static void msm_gpio_irq_relres(struct irq_data *d) + unsigned long flags; + + /* Disable the wakeup_enable bit if it has been set in msm_gpio_irq_reqres() */ +- if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) { ++ if (g->intr_wakeup_present_bit) { + u32 intr_cfg; + + raw_spin_lock_irqsave(&pctrl->lock, flags); +-- +2.53.0 + diff --git a/queue-7.1/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch b/queue-7.1/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch new file mode 100644 index 0000000000..550992e07c --- /dev/null +++ b/queue-7.1/powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch @@ -0,0 +1,38 @@ +From eb20d069aeef686a828030cae92152366da6d6bb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:55 +0200 +Subject: powerpc/boot: Fix simpleboot CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/simpleboot.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c +index c80691d83880b..27591df41e9e8 100644 +--- a/arch/powerpc/boot/simpleboot.c ++++ b/arch/powerpc/boot/simpleboot.c +@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + /* finally, setup the timebase */ + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-7.1/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch b/queue-7.1/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch new file mode 100644 index 0000000000..e58ee86e1e --- /dev/null +++ b/queue-7.1/powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch @@ -0,0 +1,38 @@ +From 919eff34cfecf66bfbb55123308e0dc08ad50e77 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:57 +0200 +Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-akebono.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c +index e3cc2599869cc..1b529037480fb 100644 +--- a/arch/powerpc/boot/treeboot-akebono.c ++++ b/arch/powerpc/boot/treeboot-akebono.c +@@ -146,7 +146,7 @@ void platform_init(char *userdata) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-7.1/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch b/queue-7.1/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch new file mode 100644 index 0000000000..f905ab867a --- /dev/null +++ b/queue-7.1/powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch @@ -0,0 +1,38 @@ +From 2ce0e670c80cf4b19e979c1f5832b15f300cd9ea Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 23:15:56 +0200 +Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check + +From: Thorsten Blum + +[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ] + +fdt_node_offset_by_prop_value() returns a negative error code on +failure - fix the check accordingly. + +Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform") +Signed-off-by: Thorsten Blum +Reviewed-by: Ritesh Harjani (IBM) +Signed-off-by: Madhavan Srinivasan +Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev +Signed-off-by: Sasha Levin +--- + arch/powerpc/boot/treeboot-currituck.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c +index d53e8a592f816..5b5363b74f9f3 100644 +--- a/arch/powerpc/boot/treeboot-currituck.c ++++ b/arch/powerpc/boot/treeboot-currituck.c +@@ -102,7 +102,7 @@ void platform_init(void) + + node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", + "cpu", sizeof("cpu")); +- if (!node) ++ if (node < 0) + fatal("Cannot find cpu node\n"); + timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); + if (timebase && (size == 4)) +-- +2.53.0 + diff --git a/queue-7.1/ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch b/queue-7.1/ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch new file mode 100644 index 0000000000..da83bb6c21 --- /dev/null +++ b/queue-7.1/ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch @@ -0,0 +1,121 @@ +From 56e27b4f5b22e1faa973747c169326205eb0d0d7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 27 Jul 2026 14:03:48 +0800 +Subject: ptp: netc: fix potential interrupt storm caused by incorrect unbind + order + +From: Wei Fang + +[ Upstream commit 54ad7ea45d63146a8e3c57375f8a269d4cf7ecea ] + +In netc_timer_remove(), hardware interrupts are disabled by clearing +TMR_TEMASK before ptp_clock_unregister() is called. This may cause a +race condition during driver unbind that could leave hardware interrupts +active. For example, a concurrent PTP_CLK_REQ_EXTTS ioctl can re-enable +TMR_TEMASK after it has been cleared, leaving a pending hardware +interrupt when the driver unbinds. + +Since the NETC Timer does not support PCIe FLR, hardware state is not +reset during probe. When the driver is rebound and the IRQ is registered, +the pending interrupt fires immediately. At that point priv->tmr_emask +is still zero, so netc_timer_isr() does not clear the interrupt status +and unconditionally returns IRQ_HANDLED, resulting in an uninterruptible +infinite interrupt storm. + +Fix this in several ways. First, request the IRQ with IRQF_NO_AUTOEN so +it is not enabled when request_irq() runs, and clear TMR_TEMASK in +netc_timer_init() before enabling it. The IRQ is only enabled at the end +of probe once the timer has been reprogrammed and the PTP clock has been +registered. This ensures a stale pending interrupt from a previous unbind +or an unclean shutdown cannot be delivered before the driver is fully +initialized. + +Second, in netc_timer_remove() call disable_irq() before +ptp_clock_unregister() and move the TMR_TEMASK/TMR_CTRL clearing after +it. disable_irq() masks the line and waits for any in-flight +netc_timer_isr() to finish, so no ISR can dereference priv->clock after +ptp_clock_unregister() has freed it. Unregistering the PTP clock before +clearing the mask also guarantees that no in-flight or concurrent ioctl +can re-enable hardware interrupts. + +Finally, return IRQ_NONE from netc_timer_isr() when the masked event +status is zero, so the kernel's spurious interrupt detection can disable +a stuck line instead of looping forever. + +Fixes: 671e266835b8 ("ptp: netc: add periodic pulse output support") +Reported-by: Sashiko +Closes: https://sashiko.dev/#/patchset/20260720012508.23227-1-wei.fang%40oss.nxp.com +Signed-off-by: Wei Fang +Link: https://patch.msgid.link/20260727060348.1887464-1-wei.fang@oss.nxp.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/ptp/ptp_netc.c | 15 ++++++++++++--- + 1 file changed, 12 insertions(+), 3 deletions(-) + +diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c +index 5e381c354d746..1c20d7efab929 100644 +--- a/drivers/ptp/ptp_netc.c ++++ b/drivers/ptp/ptp_netc.c +@@ -769,6 +769,7 @@ static void netc_timer_init(struct netc_timer *priv) + TMR_CTRL_TE | TMR_CTRL_FS; + netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl); + netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc); ++ netc_timer_wr(priv, NETC_TMR_TEMASK, 0); + + /* Disable FIPER by default */ + fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL); +@@ -901,6 +902,11 @@ static irqreturn_t netc_timer_isr(int irq, void *data) + /* Clear interrupts status */ + netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event); + ++ if (!tmr_event) { ++ spin_unlock(&priv->lock); ++ return IRQ_NONE; ++ } ++ + if (tmr_event & TMR_TEVENT_ALMEN(0)) + netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0); + +@@ -936,7 +942,8 @@ static int netc_timer_init_msix_irq(struct netc_timer *priv) + } + + priv->irq = pci_irq_vector(pdev, 0); +- err = request_irq(priv->irq, netc_timer_isr, 0, priv->irq_name, priv); ++ err = request_irq(priv->irq, netc_timer_isr, IRQF_NO_AUTOEN, ++ priv->irq_name, priv); + if (err) { + dev_err(&pdev->dev, "request_irq() failed\n"); + pci_free_irq_vectors(pdev); +@@ -951,7 +958,6 @@ static void netc_timer_free_msix_irq(struct netc_timer *priv) + { + struct pci_dev *pdev = priv->pdev; + +- disable_irq(priv->irq); + free_irq(priv->irq, priv); + pci_free_irq_vectors(pdev); + } +@@ -1005,6 +1011,8 @@ static int netc_timer_probe(struct pci_dev *pdev, + goto free_msix_irq; + } + ++ enable_irq(priv->irq); ++ + return 0; + + free_msix_irq: +@@ -1019,9 +1027,10 @@ static void netc_timer_remove(struct pci_dev *pdev) + { + struct netc_timer *priv = pci_get_drvdata(pdev); + ++ disable_irq(priv->irq); ++ ptp_clock_unregister(priv->clock); + netc_timer_wr(priv, NETC_TMR_TEMASK, 0); + netc_timer_wr(priv, NETC_TMR_CTRL, 0); +- ptp_clock_unregister(priv->clock); + netc_timer_free_msix_irq(priv); + netc_timer_pci_remove(pdev); + } +-- +2.53.0 + diff --git a/queue-7.1/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch b/queue-7.1/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch new file mode 100644 index 0000000000..8d8ecb6d2e --- /dev/null +++ b/queue-7.1/qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch @@ -0,0 +1,169 @@ +From a807f3e3345e7f1d055b7ae31e48f7bd5d1c6a99 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Jul 2026 12:43:11 +0200 +Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path + +From: Denis V. Lunev + +[ Upstream commit 451c9075d6c53f2438d110addbeeeea6fac18567 ] + +A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports +configured wedges the rtnetlink control plane of the whole machine: + + NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms + [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2! + [qede_recovery_handler:2665(ens6f0)]Starting a recovery process + +The recovery path deadlocks on the driver's own mutex: + + qede_sp_task + rtnl_lock() + mutex_lock(&edev->qede_lock) <- taken + qede_recovery_handler + qede_load + udp_tunnel_nic_reset_ntf + __udp_tunnel_nic_device_sync + info->sync_table == qede_udp_tunnel_sync + mutex_lock(&edev->qede_lock) <- same task: deadlock + +The mutex is not recursive, so the kworker blocks on itself with +rtnl_lock held, and neither lock is ever released. Every task that +calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6 +addrconf, sshd) blocks forever while the node still answers ping. +In a vmcore from an affected production node rtnl_mutex.owner +decodes to the very kworker blocked at the innermost mutex_lock() +above. + +Re-sync the tunnel ports from qede_sp_task() after the internal lock +is dropped, still under rtnl_lock as the udp_tunnel API requires. +This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf() +under rtnl without the internal lock. + +qede_recovery_handler() now returns whether it has successfully +reloaded an open device, and the caller re-syncs the ports only in +that case. This keeps the old gating exactly: a device that was down +or a failed recovery returns false, as those paths never reached the +udp_tunnel_nic_reset_ntf() call before either. + +This was the only user of the qede_lock()/qede_unlock() helpers, so +remove them. + +Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra") +Signed-off-by: Denis V. Lunev +CC: Andrew Lunn +CC: "David S. Miller" +CC: Eric Dumazet +CC: Jakub Kicinski +CC: Paolo Abeni +Reviewed-by: Jacob Keller +Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++---------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c +index 39842eb73bc33..da92c580a3dcf 100644 +--- a/drivers/net/ethernet/qlogic/qede/qede_main.c ++++ b/drivers/net/ethernet/qlogic/qede/qede_main.c +@@ -107,7 +107,7 @@ static void qede_remove(struct pci_dev *pdev); + static void qede_shutdown(struct pci_dev *pdev); + static void qede_link_update(void *dev, struct qed_link_output *link); + static void qede_schedule_recovery_handler(void *dev); +-static void qede_recovery_handler(struct qede_dev *edev); ++static bool qede_recovery_handler(struct qede_dev *edev); + static void qede_schedule_hw_err_handler(void *dev, + enum qed_hw_err_type err_type); + static void qede_get_eth_tlv_data(void *edev, void *data); +@@ -1043,21 +1043,6 @@ void __qede_unlock(struct qede_dev *edev) + mutex_unlock(&edev->qede_lock); + } + +-/* This version of the lock should be used when acquiring the RTNL lock is also +- * needed in addition to the internal qede lock. +- */ +-static void qede_lock(struct qede_dev *edev) +-{ +- rtnl_lock(); +- __qede_lock(edev); +-} +- +-static void qede_unlock(struct qede_dev *edev) +-{ +- __qede_unlock(edev); +- rtnl_unlock(); +-} +- + static void qede_periodic_task(struct work_struct *work) + { + struct qede_dev *edev = container_of(work, struct qede_dev, +@@ -1094,6 +1079,8 @@ static void qede_sp_task(struct work_struct *work) + */ + + if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) { ++ bool reloaded; ++ + cancel_delayed_work_sync(&edev->periodic_task); + #ifdef CONFIG_QED_SRIOV + /* SRIOV must be disabled outside the lock to avoid a deadlock. +@@ -1102,9 +1089,17 @@ static void qede_sp_task(struct work_struct *work) + if (pci_num_vf(edev->pdev)) + qede_sriov_configure(edev->pdev, 0); + #endif +- qede_lock(edev); +- qede_recovery_handler(edev); +- qede_unlock(edev); ++ rtnl_lock(); ++ __qede_lock(edev); ++ reloaded = qede_recovery_handler(edev); ++ __qede_unlock(edev); ++ ++ /* The udp_tunnel core synchronously calls back into ++ * qede_udp_tunnel_sync(), which takes the qede lock. ++ */ ++ if (reloaded) ++ udp_tunnel_nic_reset_ntf(edev->ndev); ++ rtnl_unlock(); + } + + __qede_lock(edev); +@@ -2645,9 +2640,13 @@ static void qede_recovery_failed(struct qede_dev *edev) + edev->ops->common->set_power_state(edev->cdev, PCI_D3hot); + } + +-static void qede_recovery_handler(struct qede_dev *edev) ++/* Returns true if an open device was successfully reloaded and its ++ * udp_tunnel ports need to be re-synced by the caller. ++ */ ++static bool qede_recovery_handler(struct qede_dev *edev) + { + u32 curr_state = edev->state; ++ bool reloaded = false; + int rc; + + DP_NOTICE(edev, "Starting a recovery process\n"); +@@ -2677,17 +2676,18 @@ static void qede_recovery_handler(struct qede_dev *edev) + goto err; + + qede_config_rx_mode(edev->ndev); +- udp_tunnel_nic_reset_ntf(edev->ndev); ++ reloaded = true; + } + + edev->state = curr_state; + + DP_NOTICE(edev, "Recovery handling is done\n"); + +- return; ++ return reloaded; + + err: + qede_recovery_failed(edev); ++ return false; + } + + static void qede_atomic_hw_err_handler(struct qede_dev *edev) +-- +2.53.0 + diff --git a/queue-7.1/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch b/queue-7.1/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch new file mode 100644 index 0000000000..7aabea0981 --- /dev/null +++ b/queue-7.1/rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch @@ -0,0 +1,85 @@ +From 718ae7acacf5cb69516e50da118ca88ba7b98796 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 14:02:03 -0700 +Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in + rds_tcp_laddr_check() + +From: Xiang Mei + +[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ] + +rds_tcp_laddr_check() looks up a scoped IPv6 interface with +dev_get_by_index_rcu(), drops the RCU read-side lock, and only then +passes the bare struct net_device * into ipv6_chk_addr(). + +dev_get_by_index_rcu() only keeps the device alive within the same RCU +read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can +free the net_device; ipv6_chk_addr() then dereferences the stale pointer +in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading +freed memory. + +Keep the RCU read-side lock held across the ipv6_chk_addr() call instead +of dropping it right after the lookup, so the device cannot be freed +while it is in use. + + BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + Read of size 8 at addr ffff8880106ec000 by task exploit/153 + Call Trace: + ... + kasan_report (mm/kasan/report.c:595) + __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) + ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972) + rds_tcp_laddr_check (net/rds/tcp.c:370) + rds_bind (net/rds/bind.c:248) + __sys_bind (net/socket.c:1920) + __x64_sys_bind (net/socket.c:1956) + do_syscall_64 (arch/x86/entry/syscall_64.c:63) + entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) + +Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") +Reported-by: Weiming Shi +Signed-off-by: Xiang Mei +Reviewed-by: Allison Henderson +Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/rds/tcp.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index 76f441cbf50e6..c20dc6d5ca65d 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -336,23 +336,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, + /* If the scope_id is specified, check only those addresses + * hosted on the specified interface. + */ ++ rcu_read_lock(); + if (scope_id != 0) { +- rcu_read_lock(); + dev = dev_get_by_index_rcu(net, scope_id); + /* scope_id is not valid... */ + if (!dev) { + rcu_read_unlock(); + return -EADDRNOTAVAIL; + } +- rcu_read_unlock(); + } + #if IS_ENABLED(CONFIG_IPV6) + if (ipv6_mod_enabled()) { + ret = ipv6_chk_addr(net, addr, dev, 0); +- if (ret) ++ if (ret) { ++ rcu_read_unlock(); + return 0; ++ } + } + #endif ++ rcu_read_unlock(); + return -EADDRNOTAVAIL; + } + +-- +2.53.0 + diff --git a/queue-7.1/ring-buffer-fix-reader-page-read-offset-for-remote-b.patch b/queue-7.1/ring-buffer-fix-reader-page-read-offset-for-remote-b.patch new file mode 100644 index 0000000000..014db153c9 --- /dev/null +++ b/queue-7.1/ring-buffer-fix-reader-page-read-offset-for-remote-b.patch @@ -0,0 +1,39 @@ +From 69cea598c41edc75956f0032e3012026ce95fccc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Jul 2026 14:36:09 +0100 +Subject: ring-buffer: Fix reader page read offset for remote buffers + +From: Vincent Donnefort + +[ Upstream commit 78cd56c2a9d2e8da763cea3b06b636266ca66911 ] + +A page swapped in by __rb_get_reader_page_from_remote() retains its +stale read offset, causing subsequent reads to skip events or read +past valid data. Fix it. + +Link: https://patch.msgid.link/20260729133609.4022734-1-vdonnefort@google.com +Fixes: fbd1743ecba1 ("ring-buffer: Add non-consuming read for ring-buffer remotes") +Signed-off-by: Vincent Donnefort +Reviewed-by: Keir Fraser +Tested-by: Keir Fraser +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/ring_buffer.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c +index da9339466bddd..d896fb25de702 100644 +--- a/kernel/trace/ring_buffer.c ++++ b/kernel/trace/ring_buffer.c +@@ -5624,6 +5624,7 @@ __rb_get_reader_page_from_remote(struct ring_buffer_per_cpu *cpu_buffer) + + cpu_buffer->head_page = new_head; + cpu_buffer->reader_page = new_reader; ++ cpu_buffer->reader_page->read = 0; + cpu_buffer->pages = &new_head->list; + cpu_buffer->read_stamp = new_reader->page->time_stamp; + cpu_buffer->lost_events = cpu_buffer->meta_page->reader.lost_events; +-- +2.53.0 + diff --git a/queue-7.1/ring-buffer-fix-subbuf_ids-memory-leak-in-rb_allocat.patch b/queue-7.1/ring-buffer-fix-subbuf_ids-memory-leak-in-rb_allocat.patch new file mode 100644 index 0000000000..5d66dce399 --- /dev/null +++ b/queue-7.1/ring-buffer-fix-subbuf_ids-memory-leak-in-rb_allocat.patch @@ -0,0 +1,48 @@ +From 4de2b9b360f19037d347b95263da544234291391 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 31 Jul 2026 23:16:46 +0900 +Subject: ring-buffer: Fix subbuf_ids memory leak in rb_allocate_cpu_buffer() + error path + +From: Masami Hiramatsu (Google) + +[ Upstream commit 260b20d9b78bf002f89088fb62d60e8dee98f6f8 ] + +In rb_allocate_cpu_buffer(), cpu_buffer->subbuf_ids is allocated using +kcalloc() when buffer->remote is non-NULL. If a subsequent page allocation +fails (e.g., ring_buffer_desc_page() returns NULL or rb_allocate_pages() +fails), execution jumps to fail_free_reader. + +While __free(kfree) automatically frees the outer cpu_buffer structure +at scope exit, kfree(cpu_buffer) does not recursively free nested heap +pointers such as cpu_buffer->subbuf_ids, resulting in a memory leak. + +Fix this by explicitly freeing cpu_buffer->subbuf_ids in the +fail_free_reader error unwinding path when cpu_buffer->remote is set. + +Link: https://patch.msgid.link/178550740672.380917.6067449683620196150.stgit@devnote2 +Fixes: 2e67fabd8b77 ("ring-buffer: Introduce ring-buffer remotes") +Assisted-by: Antigravity:gemini-3.6-flash +Signed-off-by: Masami Hiramatsu (Google) +Reviewed-by: Vincent Donnefort +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/ring_buffer.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c +index d896fb25de702..d2e2cf18bfdbe 100644 +--- a/kernel/trace/ring_buffer.c ++++ b/kernel/trace/ring_buffer.c +@@ -2489,6 +2489,7 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) + return_ptr(cpu_buffer); + + fail_free_reader: ++ kfree(cpu_buffer->subbuf_ids); + free_buffer_page(cpu_buffer->reader_page); + + return NULL; +-- +2.53.0 + diff --git a/queue-7.1/riscv-drop-__init-from-vec_check_unaligned_access_sp.patch b/queue-7.1/riscv-drop-__init-from-vec_check_unaligned_access_sp.patch new file mode 100644 index 0000000000..eba848a335 --- /dev/null +++ b/queue-7.1/riscv-drop-__init-from-vec_check_unaligned_access_sp.patch @@ -0,0 +1,80 @@ +From e6e24bcae268e85f8a367ded25a9d731d7f2a493 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 12 Jun 2026 11:24:43 -0500 +Subject: riscv: drop __init from vec_check_unaligned_access_speed_all_cpus + +From: Anirudh Srinivasan + +[ Upstream commit f51fed61eea0daba2f95f1a6074085e4cd513c7b ] + +This function runs within a kthread and need not necessarily finish +before system finishes boot and free_initmem() unmaps the .init.text +section. This function makes calls to SBI for probing unaligned access +speed, and if this is slow for some reason (say some debug prints were +added to SBI), the kthread can still be running at this point and result +in an instruction page fault when trying to fetch from the freed region. + +[ 25.642087] Unable to handle kernel paging request at virtual address ffffffff80a04ef8 +[ 25.646694] Current vec_check_unali pgtable: 4K pagesize, 48-bit VAs, pgdp=0x00004000316e9000 +[ 25.653170] [ffffffff80a04ef8] pgd=000010004be7e401, p4d=000010004be7e401, pud=000010004be7e001, pmd=000010000c3000e3 +[ 25.661244] Oops [#1] +[ 25.662997] Modules linked in: +[ 25.665357] CPU: 3 UID: 0 PID: 42 Comm: vec_check_unali Not tainted 7.0.0-tt-blackhole-asrinivasan-00007-g30ff73f18211 #570 PREEMPTLAZY +[ 25.674669] Hardware name: Tenstorrent Blackhole (DT) +[ 25.678545] epc : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c +[ 25.683458] ra : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c +[ 25.688372] epc : ffffffff80a04ef8 ra : ffffffff80a04ef8 sp : ffff8f8000203e20 +[ 25.693874] gp : ffffffff814dc168 tp : ffffaf8001ad9900 t0 : 0000000000000000 +[ 25.699401] t1 : fffffffffffffff0 t2 : ffffaf8001ad9a10 s0 : ffff8f8000203e30 +[ 25.704912] s1 : ffffaf80018dc780 a0 : 0000000000000000 a1 : 0000000000000002 +[ 25.710407] a2 : 00000000000001f0 a3 : 0000000000000018 a4 : 0000000000000000 +[ 25.715917] a5 : 0000000000000000 a6 : ffffaf8001c03d98 a7 : ffffaf8001c03e30 +[ 25.721419] s2 : ffff8f8000023c98 s3 : ffffaf8001aa1240 s4 : ffffffff80a04ee0 +[ 25.726937] s5 : 0000000000000000 s6 : 0000000000000000 s7 : 0000000000000000 +[ 25.732450] s8 : 0000000000000000 s9 : 0000000000000000 s10: 0000000000000000 +[ 25.737944] s11: 0000000000000000 t3 : 0000000000000002 t4 : 0000000000000402 +[ 25.743481] t5 : 0000000000000040 t6 : 0000000000000004 ssp : 0000000000000000 +[ 25.749024] status: 0000000200000120 badaddr: ffffffff80a04ef8 cause: 000000000000000c +[ 25.755060] [] vec_check_unaligned_access_speed_all_cpus+0x18/0x2c +[ 25.760964] [] kthread+0xd8/0xfc +[ 25.764660] [] ret_from_fork_kernel+0x18/0x1c4 +[ 25.769220] [] ret_from_fork_kernel_asm+0x16/0x18 +[ 25.774018] Code: cccc cccc cccc cccc cccc cccc cccc cccc cccc cccc (cccc) cccc + +Drop __init from its signature so that this doesn't happen. + +Fixes: a00e022be531 ("riscv: Annotate unaligned access init functions") +Signed-off-by: Anirudh Srinivasan +Assisted-by: Claude:claude-opus-4-6 +Link: https://patch.msgid.link/20260612-vec_unaligned_drop_init-v1-1-df969210ae34@oss.tenstorrent.com +Signed-off-by: Paul Walmsley +Signed-off-by: Sasha Levin +--- + arch/riscv/kernel/unaligned_access_speed.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c +index 11c781a4de733..557d661364ac5 100644 +--- a/arch/riscv/kernel/unaligned_access_speed.c ++++ b/arch/riscv/kernel/unaligned_access_speed.c +@@ -314,7 +314,7 @@ static void check_vector_unaligned_access(struct work_struct *work __always_unus + } + + /* Measure unaligned access speed on all CPUs present at boot in parallel. */ +-static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) ++static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) + { + schedule_on_each_cpu(check_vector_unaligned_access); + riscv_hwprobe_complete_async_probe(); +@@ -322,7 +322,7 @@ static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __alway + return 0; + } + #else /* CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS */ +-static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) ++static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) + { + return 0; + } +-- +2.53.0 + diff --git a/queue-7.1/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch b/queue-7.1/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch new file mode 100644 index 0000000000..e04a292c67 --- /dev/null +++ b/queue-7.1/riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch @@ -0,0 +1,63 @@ +From 4a30c54a465f8eafe4609ad9e8484a4a35ef0bcf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 29 Jul 2026 03:21:32 +0200 +Subject: riscv: mm: Fix out-of-bounds page-table walk during memory hot-remove + +From: Karl Mehltretter + +[ Upstream commit a0188cc133696627857d16054e43f9ebc7efc821 ] + +remove_pud_mapping() and remove_p4d_mapping() obtain a child table base +with pud_offset(p4dp, 0) and p4d_offset(pgd, 0), then add the index for +addr. + +RISC-V folds page-table levels at runtime. When a level is folded, its +offset helper returns the parent entry itself, but the index can still be +nonzero. Adding it walks past the parent table. Sv48 folds P4D, while Sv39 +folds both P4D and PUD, so memory hot-remove can descend into unrelated +memory and pass an invalid page to __free_pages(). This can trigger: + + kernel BUG at include/linux/mm.h:1810! + VM_BUG_ON_PAGE(page_ref_count(page) == 0) + arch_remove_memory+0x1e/0x5c + try_remove_memory+0x15e/0x200 + remove_memory+0x24/0x3c + +Only add the index when the corresponding page-table level is enabled, +matching p4d_offset() and pud_offset(). + +Fixes: c75a74f4ba19 ("riscv: mm: Add memory hotplugging support") +Assisted-by: Claude:claude-fable-5 +Signed-off-by: Karl Mehltretter +Link: https://patch.msgid.link/20260729012132.24882-1-kmehltretter@gmail.com +Signed-off-by: Paul Walmsley +Signed-off-by: Sasha Levin +--- + arch/riscv/mm/init.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c +index 885f1db4e9bfd..6aae507d8e818 100644 +--- a/arch/riscv/mm/init.c ++++ b/arch/riscv/mm/init.c +@@ -1629,7 +1629,7 @@ static void __meminit remove_pud_mapping(pud_t *pud_base, unsigned long addr, un + + for (; addr < end; addr = next) { + next = pud_addr_end(addr, end); +- pudp = pud_base + pud_index(addr); ++ pudp = pgtable_l4_enabled ? pud_base + pud_index(addr) : pud_base; + pud = pudp_get(pudp); + if (!pud_present(pud)) + continue; +@@ -1660,7 +1660,7 @@ static void __meminit remove_p4d_mapping(p4d_t *p4d_base, unsigned long addr, un + + for (; addr < end; addr = next) { + next = p4d_addr_end(addr, end); +- p4dp = p4d_base + p4d_index(addr); ++ p4dp = pgtable_l5_enabled ? p4d_base + p4d_index(addr) : p4d_base; + p4d = p4dp_get(p4dp); + if (!p4d_present(p4d)) + continue; +-- +2.53.0 + diff --git a/queue-7.1/riscv-vdso-only-try-to-install-vdso-when-present.patch b/queue-7.1/riscv-vdso-only-try-to-install-vdso-when-present.patch new file mode 100644 index 0000000000..5fd245a03f --- /dev/null +++ b/queue-7.1/riscv-vdso-only-try-to-install-vdso-when-present.patch @@ -0,0 +1,42 @@ +From b7bcca815bdd22b9259bc1f0a0756f6f41b66d80 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 9 Jul 2026 08:49:00 +0200 +Subject: riscv: vdso: Only try to install vDSO when present +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Thomas Weißschuh + +[ Upstream commit c052927905710de1ab7364bf1925efbd12517ea3 ] + +vdso.so.dbg is only built with CONFIG_MMU. + +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202607090258.iSAUYlO1-lkp@intel.com/ +Fixes: f157d411a9eb ("riscv: add missing vdso_install target") +Fixes: 3edf39916977 ("vDSO, kbuild: Provide vDSO debug variants at runtime") +Signed-off-by: Thomas Weißschuh +Link: https://patch.msgid.link/20260709-riscv-install-vdso-v1-1-0ba4345419ca@linutronix.de +Signed-off-by: Paul Walmsley +Signed-off-by: Sasha Levin +--- + arch/riscv/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile +index ce0cc737f8709..1363e5bef35cd 100644 +--- a/arch/riscv/Makefile ++++ b/arch/riscv/Makefile +@@ -168,7 +168,7 @@ vdso_prepare: prepare0 + endif + endif + +-vdso-install-y += arch/riscv/kernel/vdso/vdso.so.dbg ++vdso-install-$(CONFIG_MMU) += arch/riscv/kernel/vdso/vdso.so.dbg + vdso-install-$(CONFIG_RISCV_USER_CFI) += arch/riscv/kernel/vdso_cfi/vdso-cfi.so.dbg + vdso-install-$(CONFIG_COMPAT) += arch/riscv/kernel/compat_vdso/compat_vdso.so.dbg + +-- +2.53.0 + diff --git a/queue-7.1/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch b/queue-7.1/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch new file mode 100644 index 0000000000..d60e92dc54 --- /dev/null +++ b/queue-7.1/rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch @@ -0,0 +1,47 @@ +From eddaf4a1dc2ec8491987c0a83d31c1a07bcfb172 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 21 Jul 2026 10:38:36 +0800 +Subject: rtase: fix double free of multi-frag skb on DMA map failure + +From: Yun Lu + +[ Upstream commit 6fb7b769d6ed6d1d2e02af4a80e57a2477f35086 ] + +In rtase_start_xmit(), when the head buffer DMA mapping fails after +rtase_xmit_frags() has mapped all fragments, the error path clears +the fragment descriptors with rtase_tx_clear_range(), which frees +the skb through the last-frag slot and accounts tx_dropped. Control +then falls through to the common error label, which frees the same +skb a second time and counts it again. + +Return right after clearing the fragments when the skb owns frags; +the no-frag case still drops through and frees the head skb once. + +Fixes: d6e882b89fdf ("rtase: Implement .ndo_start_xmit function") +Signed-off-by: Yun Lu +Reviewed-by: Jacob Keller +Reviewed-by: Justin Lai +Link: https://patch.msgid.link/20260721023836.6691-1-luyun_611@163.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/realtek/rtase/rtase_main.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c +index a57a525327a3b..bc9b14614f7a7 100644 +--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c ++++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c +@@ -1620,6 +1620,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb, + err_dma_1: + ring->skbuff[entry] = NULL; + rtase_tx_clear_range(ring, ring->cur_idx + 1, frags); ++ if (frags) ++ /* the frags were cleared above, along with the skb */ ++ return NETDEV_TX_OK; + + err_dma_0: + tp->stats.tx_dropped++; +-- +2.53.0 + diff --git a/queue-7.1/rtla-timerlat_top-fix-on-threshold-actions-firing-on.patch b/queue-7.1/rtla-timerlat_top-fix-on-threshold-actions-firing-on.patch new file mode 100644 index 0000000000..71ba2dfbd5 --- /dev/null +++ b/queue-7.1/rtla-timerlat_top-fix-on-threshold-actions-firing-on.patch @@ -0,0 +1,62 @@ +From 77ced2c70a3982e202d59b9171f3bc55b41d7057 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 16:10:47 +0200 +Subject: rtla/timerlat_top: Fix on-threshold actions firing on signal + +From: Tomas Glozar + +[ Upstream commit fafb66e5903c2bcfc7b7e259042a8282f18a6faa ] + +A bug was reported when rtla-timerlat-top tool performs on-threshold +actions, even though no threshold was hit. This is reproduced even if no +threshold is set at all: + +$ rtla timerlat top -q -c 0 --on-threshold shell,command='echo BAD' +BAD + Timer Latency +... + +The bug is due to incorrect logic in timerlat_top_bpf_main_loop(). +The loop uses timerlat_bpf_wait(), the return values of which are: + +- > 0 (number of ringbuffer entries): at least 1 CPU hit threshold +- = 0: time out +- < 0: wait was interrupted by a signal + +Commit 3138df6f0cd0 ("rtla/timerlat: Exit top main loop on any non-zero +wait_retval") changed the condition for "threshold hit" from +"wait_reval == 1" (exactly 1 CPU hit threshold) to "wait_retval != 0", +to fix a race where multiple CPUs hit the threshold at the same time. + +That also made it incorrectly include a signal (< 0), coming from either +duration expired (SIGALRM) or user interrupt (SIGINT). + +Check for wait_retval greater than zero in the if condition to cover all +return values correctly. + +Fixes: 3138df6f0cd0 ("rtla/timerlat: Exit top main loop on any non-zero wait_retval") +Reported-by: Attila Fazekas +Reviewed-by: Wander Lairson Costa +Link: https://lore.kernel.org/r/20260713141047.687877-1-tglozar@redhat.com +Signed-off-by: Tomas Glozar +Signed-off-by: Sasha Levin +--- + tools/tracing/rtla/src/timerlat_top.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c +index 035abf01dbe63..d625b76a31679 100644 +--- a/tools/tracing/rtla/src/timerlat_top.c ++++ b/tools/tracing/rtla/src/timerlat_top.c +@@ -821,7 +821,7 @@ timerlat_top_bpf_main_loop(struct osnoise_tool *tool) + if (!params->quiet) + timerlat_print_stats(tool); + +- if (wait_retval != 0) { ++ if (wait_retval > 0) { + /* Stopping requested by tracer */ + retval = common_threshold_handler(tool); + if (retval) +-- +2.53.0 + diff --git a/queue-7.1/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch b/queue-7.1/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch new file mode 100644 index 0000000000..43cd699a65 --- /dev/null +++ b/queue-7.1/sched-deadline-use-revised-wakeup-rule-only-for-runn.patch @@ -0,0 +1,46 @@ +From eced13eaad18f35ea16531c0e8e4de10dcd05a99 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 May 2026 14:58:33 +0200 +Subject: sched/deadline: Use revised wakeup rule only for running dl_server + +From: Gabriele Monaco + +[ Upstream commit 1842bf97af109f5ebf830175c9725bf81ebb78b1 ] + +Commit 14a857056466 ("sched/deadline: Use revised wakeup rule for +dl_server") applies the revised wakeup rule to any server, as a result +servers that are not running (dl_defer_running == 0) and start with a +deadline overflow get enqueued and can boost tasks as if they were +running, invalidating the defer rule and the documented state model. + +Apply the revised wakeup rule only for deferrable servers that are +marked as running. + +Fixes: 14a857056466 ("sched/deadline: Use revised wakeup rule for dl_server") +Signed-off-by: Gabriele Monaco +Signed-off-by: Peter Zijlstra (Intel) +Acked-by: Juri Lelli +Tested-by: Andrea Righi +Link: https://patch.msgid.link/20260522125833.264145-1-gmonaco@redhat.com +Signed-off-by: Sasha Levin +--- + kernel/sched/deadline.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c +index 7db4c87df83b0..e5a7701a8af71 100644 +--- a/kernel/sched/deadline.c ++++ b/kernel/sched/deadline.c +@@ -1017,7 +1017,8 @@ static void update_dl_entity(struct sched_dl_entity *dl_se) + if (dl_time_before(dl_se->deadline, rq_clock(rq)) || + dl_entity_overflow(dl_se, rq_clock(rq))) { + +- if (unlikely((!dl_is_implicit(dl_se) || dl_se->dl_defer) && ++ if (unlikely((!dl_is_implicit(dl_se) || ++ (dl_se->dl_defer && dl_se->dl_defer_running)) && + !dl_time_before(dl_se->deadline, rq_clock(rq)) && + !is_dl_boosted(dl_se))) { + update_dl_revised_wakeup(dl_se, rq); +-- +2.53.0 + diff --git a/queue-7.1/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch b/queue-7.1/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch new file mode 100644 index 0000000000..2d299d476e --- /dev/null +++ b/queue-7.1/scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch @@ -0,0 +1,56 @@ +From 70103f0fe2173bcc5792f189a2b91b2a9821d445 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 14 Jul 2026 19:49:34 +0900 +Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer + +From: HyeongJun An + +[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ] + +iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the +target-supplied data segment. The segment carries a 2-byte sense length +followed by the sense bytes, so it must hold 2 + senselen bytes, but the +bounds check only requires datalen >= senselen: + + senselen = get_unaligned_be16(data); + if (datalen < senselen) + goto invalid_datalen; + memcpy(sc->sense_buffer, data + 2, + min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); + +A target that returns a SCSI Response whose datalen equals senselen +(with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data + +2 read up to two bytes past the received data. Those bytes are stale +conn->data contents and end up in the command's sense buffer, which is +returned to userspace. + +Account for the 2-byte sense length prefix in the check. + +Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi") +Suggested-by: Sashiko AI +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c +index 25857d6ed6e84..1b4e6af37c927 100644 +--- a/drivers/scsi/libiscsi.c ++++ b/drivers/scsi/libiscsi.c +@@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, + } + + senselen = get_unaligned_be16(data); +- if (datalen < senselen) ++ if (datalen < senselen + 2) + goto invalid_datalen; + + memcpy(sc->sense_buffer, data + 2, +-- +2.53.0 + diff --git a/queue-7.1/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch b/queue-7.1/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch new file mode 100644 index 0000000000..968a091c49 --- /dev/null +++ b/queue-7.1/scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch @@ -0,0 +1,71 @@ +From 8251e1f36c006a6972806878cd0389edc1ad3e23 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 15:58:48 +0900 +Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the + connection buffer + +From: HyeongJun An + +[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ] + +iscsi_tcp_hdr_dissect() receives the data segment of several PDU types +into the fixed-size conn->data buffer, which is allocated for +ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes. For the LOGIN_RSP, TEXT_RSP, +REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU +whose DataSegmentLength exceeds that buffer. + +The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its +data segment (sense/response data) into conn->data via +iscsi_tcp_data_recv_prep(), but it does so without the same check. The +only upstream bound on in.datalen is conn->max_recv_dlength, the +initiator's advertised MaxRecvDataSegmentLength, which is commonly +negotiated well above 8192 (open-iscsi defaults to 262144). A target +that returns a SCSI Response with a DataSegmentLength between 8193 and +max_recv_dlength therefore overflows the 8192-byte conn->data buffer. + +Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly +like those responses: bound the data segment, receive it into conn->data +when present, and otherwise complete the PDU with no data. Fold the +opcode into that case group rather than duplicating the check. + +Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld") +Suggested-by: Chris Leech +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: HyeongJun An +Acked-by: Chris Leech +Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/libiscsi_tcp.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c +index e90805ba868fb..7223bb18b0480 100644 +--- a/drivers/scsi/libiscsi_tcp.c ++++ b/drivers/scsi/libiscsi_tcp.c +@@ -752,13 +752,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); + spin_unlock(&conn->session->back_lock); + break; +- case ISCSI_OP_SCSI_CMD_RSP: +- if (tcp_conn->in.datalen) { +- iscsi_tcp_data_recv_prep(tcp_conn); +- return 0; +- } +- rc = iscsi_complete_pdu(conn, hdr, NULL, 0); +- break; + case ISCSI_OP_R2T: + if (ahslen) { + rc = ISCSI_ERR_AHSLEN; +@@ -766,6 +759,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) + } + rc = iscsi_tcp_r2t_rsp(conn, hdr); + break; ++ case ISCSI_OP_SCSI_CMD_RSP: + case ISCSI_OP_LOGIN_RSP: + case ISCSI_OP_TEXT_RSP: + case ISCSI_OP_REJECT: +-- +2.53.0 + diff --git a/queue-7.1/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch b/queue-7.1/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch new file mode 100644 index 0000000000..1b656b8262 --- /dev/null +++ b/queue-7.1/scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch @@ -0,0 +1,165 @@ +From ecedd21d2cd35a5d3ea5e8f447c54d75613e10bd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 16 Jul 2026 16:11:45 +0800 +Subject: scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race + +From: Xingui Yang + +[ Upstream commit 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 ] + +Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue +for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock: +the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue, +calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI +devices and waits for the host to become runtime-active. But the host +cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the +drain is blocked on that very handler. + +However skipping the drain reintroduces a race: hisi_sas returns from +resume before all PHY UP work and libsas discovery work finish. The +controller may then autosuspend while disks are still waking up. The +disks issue IO to a suspended controller, the IO fails, and the disks +get disabled. + +Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT +notification to after sas_drain_work(). By then the host resume is about +to complete, so device removal through device_link no longer blocks on +the resume and the cycle is broken. + +With the deadlock gone, restore sas_resume_ha() (the draining variant) +in hisi_sas and remove sas_resume_ha_no_sync(). + +The reorder is safe for the other libsas consumers (isci, pm8001, +aic94xx, mvsas). During suspend, sas_suspend_devices() calls +sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to +NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a +timed-out phy's disk is immediately rejected by the LLDD before reaching +hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET), +and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both +complete directly via scsi_done() without entering SCSI EH. This is +identical in both the old and new ordering since lldd_dev_gone runs +during suspend, before resume. The reorder only affects when the +PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work() +vs. asynchronous after resume returns), not whether I/O can reach the +device. aic94xx and mvsas do not register any PM ops and never reach +this code path. + +Fixes: fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume") +Signed-off-by: Xingui Yang +Reviewed-by: John Garry +Link: https://patch.msgid.link/20260716081145.3950172-1-yangxingui@huawei.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +------ + drivers/scsi/libsas/sas_init.c | 37 +++++++++++++------------- + include/scsi/libsas.h | 1 - + 3 files changed, 19 insertions(+), 29 deletions(-) + +diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +index 213d5b5dea94f..8a2500993e19d 100644 +--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c ++++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +@@ -5261,15 +5261,7 @@ static int _resume_v3_hw(struct device *device) + return rc; + } + phys_init_v3_hw(hisi_hba); +- +- /* +- * If a directly-attached disk is removed during suspend, a deadlock +- * may occur, as the PHYE_RESUME_TIMEOUT processing will require the +- * hisi_hba->device to be active, which can only happen when resume +- * completes. So don't wait for the HA event workqueue to drain upon +- * resume. +- */ +- sas_resume_ha_no_sync(sha); ++ sas_resume_ha(sha); + clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags); + + dev_warn(dev, "end of resuming controller\n"); +diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c +index 0bec236f0fb59..c3f3d05b46dea 100644 +--- a/drivers/scsi/libsas/sas_init.c ++++ b/drivers/scsi/libsas/sas_init.c +@@ -410,7 +410,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha) + } + } + +-static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) ++void sas_resume_ha(struct sas_ha_struct *ha) + { + const unsigned long tmo = msecs_to_jiffies(25000); + int i; +@@ -426,6 +426,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n", + i, i > 1 ? "s" : ""); + wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo); ++ ++ /* ++ * All phys are back up or timed out. Turn on I/O and drain ++ * pending work. ++ */ ++ scsi_unblock_requests(ha->shost); ++ sas_drain_work(ha); ++ ++ /* ++ * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler ++ * calls sas_deform_port() -> sas_destruct_devices(), which removes ++ * SCSI devices and, for LLDDs using device_link() PM sync, waits ++ * for the host to be runtime-active. Sending it before the drain ++ * would deadlock: the drain waits for the handler, the handler ++ * waits for host resume, and host resume waits for the drain to ++ * finish. ++ */ + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_phy *phy = ha->sas_phy[i]; + +@@ -436,12 +453,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + } + } + +- /* all phys are back up or timed out, turn on i/o so we can +- * flush out disks that did not return +- */ +- scsi_unblock_requests(ha->shost); +- if (drain) +- sas_drain_work(ha); + clear_bit(SAS_HA_RESUMING, &ha->state); + + sas_queue_deferred_work(ha); +@@ -450,20 +461,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) + */ + sas_resume_insert_broadcast_ha(ha); + } +- +-void sas_resume_ha(struct sas_ha_struct *ha) +-{ +- _sas_resume_ha(ha, true); +-} + EXPORT_SYMBOL(sas_resume_ha); + +-/* A no-sync variant, which does not call sas_drain_ha(). */ +-void sas_resume_ha_no_sync(struct sas_ha_struct *ha) +-{ +- _sas_resume_ha(ha, false); +-} +-EXPORT_SYMBOL(sas_resume_ha_no_sync); +- + void sas_suspend_ha(struct sas_ha_struct *ha) + { + int i; +diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h +index 163f23c92b411..36d4cb567837c 100644 +--- a/include/scsi/libsas.h ++++ b/include/scsi/libsas.h +@@ -680,7 +680,6 @@ extern int sas_register_ha(struct sas_ha_struct *); + extern int sas_unregister_ha(struct sas_ha_struct *); + extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha); + extern void sas_resume_ha(struct sas_ha_struct *sas_ha); +-extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha); + extern void sas_suspend_ha(struct sas_ha_struct *sas_ha); + + int sas_phy_reset(struct sas_phy *phy, int hard_reset); +-- +2.53.0 + diff --git a/queue-7.1/scsi-mpi3mr-fix-potential-deadlock-in-mpi3mr_fault_u.patch b/queue-7.1/scsi-mpi3mr-fix-potential-deadlock-in-mpi3mr_fault_u.patch new file mode 100644 index 0000000000..067f7cff4f --- /dev/null +++ b/queue-7.1/scsi-mpi3mr-fix-potential-deadlock-in-mpi3mr_fault_u.patch @@ -0,0 +1,69 @@ +From 38a239845c788d6851838a0ff1939f8f281ee1c7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 23:22:31 +0530 +Subject: scsi: mpi3mr: Fix potential deadlock in mpi3mr_fault_uevent_emit + +From: Chandrakanth Patil + +[ Upstream commit ccff8c92571500fcfed21281e33daaf645bf692f ] + +mpi3mr_fault_uevent_emit() runs from the fault watchdog and reset paths +where host I/O may already be blocked. GFP_KERNEL allocations here, both +the local kzalloc_obj() and the ones inside kobject_uevent_env() itself, +can trigger reclaim that waits on that blocked I/O and deadlock. + +Use memalloc_noio_save()/restore() to cover the whole call instead of +just the local allocation. + +Fixes: ec54b348f274 ("scsi: mpi3mr: Record and report controller firmware faults") +Reported-by: sashiko-bot +Closes: https://sashiko.dev/#/patchset/20260724164630.924288-1-chandrakanth.patil%40broadcom.com +Signed-off-by: Chandrakanth Patil +Link: https://patch.msgid.link/20260724175231.935192-1-chandrakanth.patil@broadcom.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/mpi3mr/mpi3mr_fw.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c +index 31b19ed1528e5..681868716ebdb 100644 +--- a/drivers/scsi/mpi3mr/mpi3mr_fw.c ++++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c +@@ -9,6 +9,7 @@ + + #include "mpi3mr.h" + #include ++#include + + static int + mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type, u16 reset_reason); +@@ -1287,11 +1288,14 @@ static void mpi3mr_alloc_ioctl_dma_memory(struct mpi3mr_ioc *mrioc) + static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc) + { + struct kobj_uevent_env *env; ++ unsigned int noio_flag; + int ret; + ++ noio_flag = memalloc_noio_save(); ++ + env = kzalloc_obj(*env); + if (!env) +- return; ++ goto out_restore; + + ret = add_uevent_var(env, "DRIVER=%s", mrioc->driver_name); + if (ret) +@@ -1326,7 +1330,8 @@ static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc) + + out_free: + kfree(env); +- ++out_restore: ++ memalloc_noio_restore(noio_flag); + } + + /** +-- +2.53.0 + diff --git a/queue-7.1/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch b/queue-7.1/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch new file mode 100644 index 0000000000..b13ab243bb --- /dev/null +++ b/queue-7.1/scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch @@ -0,0 +1,80 @@ +From 2e7efe7903b25d2c5e09b1bfd1c961b424c8d5d0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jul 2026 09:30:10 +0300 +Subject: scsi: target: Clear cmd_cnt when initial counter enrollment fails + +From: Leon Romanovsky + +[ Upstream commit a8ddfd2425bbbafadae8700d63ed8a61a4109878 ] + +When target_get_sess_cmd() fails during session shutdown because +percpu_ref_tryget_live() returns false, the command keeps the +se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier +without owning a reference. Final release through +target_release_cmd_kref() then issues an unmatched percpu_ref_put(). + +Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during +cmd setup") moved the cmd_cnt assignment ahead of the reference +acquisition. Clear se_cmd->cmd_cnt whenever the initial +target_get_sess_cmd() fails in target_init_cmd() and +target_submit_tmr(), so release performs exactly one matching put per +acquired reference. + +Fixes: 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup") +Signed-off-by: Leon Romanovsky +Reviewed-by: Mike Christie +Link: https://patch.msgid.link/20260722-reference-count-underflow-in-target-v1-1-63ab664f12fd@nvidia.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/target/target_core_transport.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c +index fad03a15c969e..dcfe945949167 100644 +--- a/drivers/target/target_core_transport.c ++++ b/drivers/target/target_core_transport.c +@@ -1734,6 +1734,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, + u32 data_length, int task_attr, int data_dir, int flags) + { + struct se_portal_group *se_tpg; ++ int ret; + + se_tpg = se_sess->se_tpg; + BUG_ON(!se_tpg); +@@ -1763,7 +1764,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, + * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second + * kref_put() to happen during fabric packet acknowledgement. + */ +- return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); ++ ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); ++ if (ret) ++ se_cmd->cmd_cnt = NULL; ++ ++ return ret; + } + EXPORT_SYMBOL_GPL(target_init_cmd); + +@@ -2039,8 +2044,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, + * allocation failure. + */ + ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp); +- if (ret < 0) ++ if (ret < 0) { ++ se_cmd->cmd_cnt = NULL; + return -ENOMEM; ++ } + + if (tm_type == TMR_ABORT_TASK) + se_cmd->se_tmr_req->ref_task_tag = tag; +@@ -2048,6 +2055,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, + /* See target_submit_cmd for commentary */ + ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); + if (ret) { ++ se_cmd->cmd_cnt = NULL; + core_tmr_release_req(se_cmd->se_tmr_req); + return ret; + } +-- +2.53.0 + diff --git a/queue-7.1/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch b/queue-7.1/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch new file mode 100644 index 0000000000..40d1eae0b1 --- /dev/null +++ b/queue-7.1/scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch @@ -0,0 +1,54 @@ +From bf930fa9efd8fb40efdfb366b4470ad46a671fab Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 24 Jul 2026 15:58:50 +0800 +Subject: scsi: target: iblock: Fix wrong PR ops NULL check for PREEMPT/RELEASE + +From: TanZheng + +[ Upstream commit 9c33222bd387312874fbe36ca8002e5c945b9653 ] + +In the iblock_execute_pr_out() function, PRO_PREEMPT, +PRO_PREEMPT_AND_ABORT, and PRO_RELEASE all perform callback capability +checks through ops->pr_clear. The error check allows unimplemented hooks +to pass through the gate, resulting dereferencing a NULL function +pointer. + +Check whether the hooks that need to be called are supported. + +Fixes: 394f81184882 ("scsi: target: Add block PR support to iblock") +Signed-off-by: TanZheng +Reviewed-by: Mike Christie +Link: https://patch.msgid.link/20260724075850.280699-1-kensanya@163.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/target/target_core_iblock.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c +index 1087d1d17c36d..ea65d39fef884 100644 +--- a/drivers/target/target_core_iblock.c ++++ b/drivers/target/target_core_iblock.c +@@ -906,7 +906,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, + break; + case PRO_PREEMPT: + case PRO_PREEMPT_AND_ABORT: +- if (!ops->pr_clear) { ++ if (!ops->pr_preempt) { + pr_err("block_device does not support pr_preempt.\n"); + return TCM_UNSUPPORTED_SCSI_OPCODE; + } +@@ -916,8 +916,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, + sa == PRO_PREEMPT_AND_ABORT); + break; + case PRO_RELEASE: +- if (!ops->pr_clear) { +- pr_err("block_device does not support pr_pclear.\n"); ++ if (!ops->pr_release) { ++ pr_err("block_device does not support pr_release.\n"); + return TCM_UNSUPPORTED_SCSI_OPCODE; + } + +-- +2.53.0 + diff --git a/queue-7.1/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch b/queue-7.1/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch new file mode 100644 index 0000000000..fb85aef14a --- /dev/null +++ b/queue-7.1/scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch @@ -0,0 +1,70 @@ +From eb9bd94a48c6a718b60abe97abe8127f42d7d519 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 01:27:26 +0800 +Subject: scsi: ufs: core: Cancel RTC work in active-active suspend + +From: Guangshuo Li + +[ Upstream commit f71b4a30983b846b4075bf544e835121e70e6a43 ] + +UFS RTC support schedules ufs_rtc_update_work to periodically update the +device RTC. The work can issue query commands and access the UFS host +controller. + +A previous change moved the RTC work cancellation before the PRE_CHANGE +vendor suspend callback to close a race in the common suspend path. +However, the active-active path jumps directly to vops_suspend after +flushing exception handling work and therefore bypasses the +cancellation. + +If the RTC work runs while the vendor suspend callback is gating or +otherwise changing hardware state, it can access the controller during +suspend and trigger an SError. + +Cancel the RTC work before entering the vendor suspend callback in the +active-active path. Since this path now cancels the work, move the RTC +work scheduling outside the device and link state restoration block in +the resume path. This restarts RTC updates after an active-active +suspend and resume cycle. + +Fixes: b0bd84c39289 ("scsi: ufs: core: Fix SError in ufshcd_rtc_work() during UFS suspend") +Signed-off-by: Guangshuo Li +Reviewed-by: Peter Wang +Reviewed-by: Bean Huo +Reviewed-by: Bart Van Assche +Link: https://patch.msgid.link/20260714172726.1736967-1-lgs201920130244@gmail.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/ufs/core/ufshcd.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c +index 84e32957c332d..d6361e42ba553 100644 +--- a/drivers/ufs/core/ufshcd.c ++++ b/drivers/ufs/core/ufshcd.c +@@ -10238,6 +10238,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) + req_link_state == UIC_LINK_ACTIVE_STATE) { + ufshcd_disable_auto_bkops(hba); + flush_work(&hba->eeh_work); ++ cancel_delayed_work_sync(&hba->ufs_rtc_update_work); + goto vops_suspend; + } + +@@ -10447,10 +10448,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) + if (ret) + goto set_old_link_state; + ufshcd_set_timestamp_attr(hba); +- schedule_delayed_work(&hba->ufs_rtc_update_work, +- msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); + } + ++ schedule_delayed_work(&hba->ufs_rtc_update_work, ++ msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); ++ + if (ufshcd_keep_autobkops_enabled_except_suspend(hba)) + ufshcd_enable_auto_bkops(hba); + else +-- +2.53.0 + diff --git a/queue-7.1/scsi-ufs-core-initialize-hba-rpmbs-list-in-ufshcd.patch b/queue-7.1/scsi-ufs-core-initialize-hba-rpmbs-list-in-ufshcd.patch new file mode 100644 index 0000000000..f54127eab6 --- /dev/null +++ b/queue-7.1/scsi-ufs-core-initialize-hba-rpmbs-list-in-ufshcd.patch @@ -0,0 +1,54 @@ +From 5c73e51b26824dd3b389f8b2037e226e2248c8c1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 03:45:30 +0000 +Subject: scsi: ufs: core: Initialize hba->rpmbs list in ufshcd + +From: Ao Sun + +[ Upstream commit 0279fd451a9971c0d5b959fc59f3e11b55e1694e ] + +Initialize the hba->rpmbs list in ufshcd_alloc_host() to prevent NULL +pointer dereference in the device teardown path if ufs_rpmb_probe() +fails. + +Fixes: b06b8c421485 ("scsi: ufs: core: Add OP-TEE based RPMB driver for UFS devices") +Co-developed-by: Jiazi Li +Signed-off-by: Jiazi Li +Signed-off-by: Ao Sun +Reviewed-by: Bean Huo +Link: https://patch.msgid.link/20260723034440.217-1-ao.sun@transsion.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/ufs/core/ufs-rpmb.c | 2 -- + drivers/ufs/core/ufshcd.c | 1 + + 2 files changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/ufs/core/ufs-rpmb.c b/drivers/ufs/core/ufs-rpmb.c +index ffad049872b9a..62120dc2e9da7 100644 +--- a/drivers/ufs/core/ufs-rpmb.c ++++ b/drivers/ufs/core/ufs-rpmb.c +@@ -152,8 +152,6 @@ int ufs_rpmb_probe(struct ufs_hba *hba) + return -EINVAL; + } + +- INIT_LIST_HEAD(&hba->rpmbs); +- + struct rpmb_descr descr = { + .type = RPMB_TYPE_UFS, + .route_frames = ufs_rpmb_route_frames, +diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c +index 082eac1e13463..d6444b3c2ef16 100644 +--- a/drivers/ufs/core/ufshcd.c ++++ b/drivers/ufs/core/ufshcd.c +@@ -10924,6 +10924,7 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle) + hba->nop_out_timeout = NOP_OUT_TIMEOUT; + ufshcd_set_sg_entry_size(hba, sizeof(struct ufshcd_sg_entry)); + INIT_LIST_HEAD(&hba->clk_list_head); ++ INIT_LIST_HEAD(&hba->rpmbs); + spin_lock_init(&hba->outstanding_lock); + + *hba_handle = hba; +-- +2.53.0 + diff --git a/queue-7.1/scsi-ufs-core-revert-delegate-the-interrupt-service-.patch b/queue-7.1/scsi-ufs-core-revert-delegate-the-interrupt-service-.patch new file mode 100644 index 0000000000..d9a3de8e05 --- /dev/null +++ b/queue-7.1/scsi-ufs-core-revert-delegate-the-interrupt-service-.patch @@ -0,0 +1,123 @@ +From 2cd1cb8848d17a5b3fd8bb21df378703ae179456 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 10:12:28 -0700 +Subject: scsi: ufs: core: Revert "Delegate the interrupt service routine to a + threaded IRQ handler" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Bart Van Assche + +[ Upstream commit 8a309036f557d3ff4efb2beea5132ba91172d934 ] + +There have been multiple reports of performance regressions caused by +commit 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service +routine to a threaded IRQ handler"). Hence this revert. + +This patch reverts most of the following commits: + + * 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service + routine to a threaded IRQ handler") + + * 6475cfb81fc4 ("scsi: ufs: core: Avoid IRQ thread wakeup during active + UIC command") + +This patch preserves the following commits: + + * 034d319c8899 ("scsi: ufs: core: Fix interrupt handling for MCQ Mode") + + * eabcac808ca3 ("scsi: ufs: core: Fix IRQ lock inversion for the SCSI + host lock") + +Cc: Neil Armstrong +Cc: 孙魁 (Kui Sun) +Cc: André Draszik +Cc: Gregory CLEMENT +Cc: Sebastian Andrzej Siewior +Fixes: 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler") +Signed-off-by: Bart Van Assche +Reviewed-by: Sebastian Andrzej Siewior +Tested-by: André Draszik # on Pixel 6 +Reviewed-by: André Draszik +Link: https://patch.msgid.link/b70eb60a01f971bed68c42c5b555929db5f835df.1784135511.git.bvanassche@acm.org +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/ufs/core/ufshcd.c | 39 +++------------------------------------ + 1 file changed, 3 insertions(+), 36 deletions(-) + +diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c +index d6361e42ba553..082eac1e13463 100644 +--- a/drivers/ufs/core/ufshcd.c ++++ b/drivers/ufs/core/ufshcd.c +@@ -7309,7 +7309,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) + } + + /** +- * ufshcd_threaded_intr - Threaded interrupt service routine ++ * ufshcd_intr - Main interrupt service routine + * @irq: irq number + * @__hba: pointer to adapter instance + * +@@ -7317,7 +7317,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) + * IRQ_HANDLED - If interrupt is valid + * IRQ_NONE - If invalid interrupt + */ +-static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba) ++static irqreturn_t ufshcd_intr(int irq, void *__hba) + { + u32 last_intr_status, intr_status, enabled_intr_status = 0; + irqreturn_t retval = IRQ_NONE; +@@ -7356,38 +7356,6 @@ static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba) + return retval; + } + +-/** +- * ufshcd_intr - Main interrupt service routine +- * @irq: irq number +- * @__hba: pointer to adapter instance +- * +- * Return: +- * IRQ_HANDLED - If interrupt is valid +- * IRQ_WAKE_THREAD - If handling is moved to threaded handled +- * IRQ_NONE - If invalid interrupt +- */ +-static irqreturn_t ufshcd_intr(int irq, void *__hba) +-{ +- struct ufs_hba *hba = __hba; +- u32 intr_status, enabled_intr_status; +- +- /* +- * Handle interrupt in thread if MCQ or ESI is disabled, +- * and no active UIC command. +- */ +- if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) && +- !hba->active_uic_cmd) +- return IRQ_WAKE_THREAD; +- +- intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); +- enabled_intr_status = intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); +- +- ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); +- +- /* Directly handle interrupts since MCQ ESI handlers does the hard job */ +- return ufshcd_sl_intr(hba, enabled_intr_status); +-} +- + static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag) + { + int err = 0; +@@ -11204,8 +11172,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) + ufshcd_readl(hba, REG_INTERRUPT_ENABLE); + + /* IRQ registration */ +- err = devm_request_threaded_irq(dev, irq, ufshcd_intr, ufshcd_threaded_intr, +- IRQF_ONESHOT | IRQF_SHARED, UFSHCD, hba); ++ err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba); + if (err) { + dev_err(hba->dev, "request irq failed\n"); + goto out_disable; +-- +2.53.0 + diff --git a/queue-7.1/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch b/queue-7.1/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch new file mode 100644 index 0000000000..cd3c7db666 --- /dev/null +++ b/queue-7.1/scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch @@ -0,0 +1,66 @@ +From 90a0aebcef3f45bcde4a95cf79a6f749acfb9674 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 09:27:36 +0200 +Subject: scsi: zfcp: Fix memory leak during adapter release by destroying + gid_pn_req + +From: Benjamin Block + +[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ] + +When releasing an adapter we don't free the mempool 'gid_pn_req' that is +allocated during the enqueue. This leaks memory: + + unreferenced object 0xd8d29297de700 (size 256): + comm "(udev-worker)", pid 2105, jiffies 4294945794 + hex dump (first 32 bytes): + 00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00 ......N......... + ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0 ..........._g... + backtrace (crc 4a5b5da2): + [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0 + [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0 + [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150 + [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp] + [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp] + [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp] + [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80 + [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390 + [<000dc45f643d8238>] online_store+0x298/0x5b0 + [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480 + [<000dc45f62c81150>] new_sync_write+0x370/0x4b0 + [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0 + [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0 + [<000dc45f621c4a16>] do_syscall+0x2f6/0x430 + [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0 + [<000dc45f64dc2224>] system_call+0x74/0xa0 + +Fix this by destroying the mempool during the adapter's release. + +Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp") +Signed-off-by: Benjamin Block +Tested-by: M Nikhil +Acked-by: M Nikhil +Reviewed-by: Chinmaya Kajagar +Reviewed-by: Nihar Panda +Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/s390/scsi/zfcp_aux.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c +index 8ff7db7921b5a..fea573e00f9df 100644 +--- a/drivers/s390/scsi/zfcp_aux.c ++++ b/drivers/s390/scsi/zfcp_aux.c +@@ -253,6 +253,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) + static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) + { + mempool_destroy(adapter->pool.erp_req); ++ mempool_destroy(adapter->pool.gid_pn_req); + mempool_destroy(adapter->pool.scsi_req); + mempool_destroy(adapter->pool.scsi_abort); + mempool_destroy(adapter->pool.qtcb_pool); +-- +2.53.0 + diff --git a/queue-7.1/selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch b/queue-7.1/selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch new file mode 100644 index 0000000000..5390573067 --- /dev/null +++ b/queue-7.1/selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch @@ -0,0 +1,42 @@ +From 8309659d709933d5d6098ac9b150fafa9fcd6519 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 16:02:10 +0800 +Subject: selftests/lkdtm: rename STACKLEAK_ERASING to KSTACK_ERASE + +From: Haofeng Li + +[ Upstream commit b3a7aa9c0020ae549a0d4964867ff66d2bd61709 ] + +Commit 57fbad15c2ee ("stackleak: Rename STACKLEAK to KSTACK_ERASE") +renamed the LKDTM crash type and selftest configuration but missed the +entry in tests.txt. + +As a result, the selftest generates STACKLEAK_ERASING.sh, which run.sh +skips because the LKDTM DIRECT trigger only exposes KSTACK_ERASE. Rename +the test entry so the generated runner uses the registered crash type. + +Fixes: 57fbad15c2ee ("stackleak: Rename STACKLEAK to KSTACK_ERASE") +Signed-off-by: Haofeng Li +Link: https://patch.msgid.link/tencent_CD80B5F746B6AABD68AF3F1097AD02C96F05@qq.com +Signed-off-by: Kees Cook +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/lkdtm/tests.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt +index 3245032db34d3..444040ca51689 100644 +--- a/tools/testing/selftests/lkdtm/tests.txt ++++ b/tools/testing/selftests/lkdtm/tests.txt +@@ -78,7 +78,7 @@ USERCOPY_STACK_FRAME_TO + USERCOPY_STACK_FRAME_FROM + USERCOPY_STACK_BEYOND + USERCOPY_KERNEL +-STACKLEAK_ERASING OK: the rest of the thread stack is properly erased ++KSTACK_ERASE OK: the rest of the thread stack is properly erased + CFI_FORWARD_PROTO + CFI_BACKWARD call trace:|ok: control flow unchanged + FORTIFY_STRSCPY detected buffer overflow +-- +2.53.0 + diff --git a/queue-7.1/selftests-net-af_unix-test-listen-rejects-wrong-sock.patch b/queue-7.1/selftests-net-af_unix-test-listen-rejects-wrong-sock.patch new file mode 100644 index 0000000000..a9fe525a42 --- /dev/null +++ b/queue-7.1/selftests-net-af_unix-test-listen-rejects-wrong-sock.patch @@ -0,0 +1,253 @@ +From f736457503efdb1390931ecfc2061d2690e47a07 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 18 Jul 2026 14:29:02 -0400 +Subject: selftests/net/af_unix: test listen() rejects wrong socket states + +From: John Ericson + +[ Upstream commit 7f57c650d08b8793bb551bdb33ad876535ef9fe8 ] + +Add a regression test for the unix_listen() state check. The key case is +listen() on a bound socket that has already been connected: it is no +longer in TCP_CLOSE or TCP_LISTEN, so it must fail with EINVAL. A +prepare_peercred() call slipped in ahead of that check once left err at 0 +and made listen() silently succeed there instead; this guards against a +repeat. + +The neighbouring outcomes are covered too so they cannot regress the same +way: a bound socket in TCP_CLOSE listens fine, calling listen() again on a +socket already in TCP_LISTEN is allowed, and an unbound socket fails with +EINVAL. + +Each case runs for both listenable socket types (SOCK_STREAM and +SOCK_SEQPACKET) and both pathname and abstract addresses. + +Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid") +Signed-off-by: John Ericson +Link: https://patch.msgid.link/20260718182903.2295560-2-John.Ericson@Obsidian.Systems +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + .../testing/selftests/net/af_unix/.gitignore | 1 + + tools/testing/selftests/net/af_unix/Makefile | 1 + + .../selftests/net/af_unix/unix_listen.c | 187 ++++++++++++++++++ + 3 files changed, 189 insertions(+) + create mode 100644 tools/testing/selftests/net/af_unix/unix_listen.c + +diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore +index 240b26740c9e0..9731766441036 100644 +--- a/tools/testing/selftests/net/af_unix/.gitignore ++++ b/tools/testing/selftests/net/af_unix/.gitignore +@@ -6,3 +6,4 @@ scm_rights + so_peek_off + unix_connect + unix_connreset ++unix_listen +diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile +index 4c0375e28bbee..57d159803a3ab 100644 +--- a/tools/testing/selftests/net/af_unix/Makefile ++++ b/tools/testing/selftests/net/af_unix/Makefile +@@ -14,6 +14,7 @@ TEST_GEN_PROGS := \ + so_peek_off \ + unix_connect \ + unix_connreset \ ++ unix_listen \ + # end of TEST_GEN_PROGS + + include ../../lib.mk +diff --git a/tools/testing/selftests/net/af_unix/unix_listen.c b/tools/testing/selftests/net/af_unix/unix_listen.c +new file mode 100644 +index 0000000000000..416fa3e5bfe9b +--- /dev/null ++++ b/tools/testing/selftests/net/af_unix/unix_listen.c +@@ -0,0 +1,187 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Tests for the state checks in AF_UNIX listen(). ++ * ++ * The central case is a regression test: listen() on a bound socket that ++ * is already connected (i.e. not in TCP_CLOSE or TCP_LISTEN state) must ++ * fail with EINVAL. A prior change accidentally let it return success ++ * without doing anything, because a helper called in between reset the ++ * error code to 0. The neighbouring checks (unbound, already listening) ++ * are tested too so they cannot silently regress the same way. ++ * ++ * Every case runs for both listenable socket types (SOCK_STREAM and ++ * SOCK_SEQPACKET) and both pathname and abstract addresses. ++ */ ++#define _GNU_SOURCE ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "kselftest_harness.h" ++ ++#define SK_NAME "unix_listen_sk" ++#define SRV_NAME "unix_listen_srv" ++ ++FIXTURE(unix_listen) ++{ ++ int sk; /* socket under test */ ++ int server; /* a listening peer, when a test needs one */ ++ struct sockaddr_un addr, srv_addr; ++ socklen_t addrlen, srv_addrlen; ++}; ++ ++FIXTURE_VARIANT(unix_listen) ++{ ++ int type; ++ int abstract; ++}; ++ ++FIXTURE_VARIANT_ADD(unix_listen, stream_pathname) ++{ ++ .type = SOCK_STREAM, ++ .abstract = 0, ++}; ++ ++FIXTURE_VARIANT_ADD(unix_listen, stream_abstract) ++{ ++ .type = SOCK_STREAM, ++ .abstract = 1, ++}; ++ ++FIXTURE_VARIANT_ADD(unix_listen, seqpacket_pathname) ++{ ++ .type = SOCK_SEQPACKET, ++ .abstract = 0, ++}; ++ ++FIXTURE_VARIANT_ADD(unix_listen, seqpacket_abstract) ++{ ++ .type = SOCK_SEQPACKET, ++ .abstract = 1, ++}; ++ ++/* Fill @addr with a pathname or abstract address named @name. */ ++static socklen_t unix_set_addr(struct sockaddr_un *addr, const char *name, ++ int abstract) ++{ ++ size_t len = strlen(name); ++ ++ memset(addr, 0, sizeof(*addr)); ++ addr->sun_family = AF_UNIX; ++ /* An abstract address leads with a NUL and has no filesystem entry. */ ++ memcpy(addr->sun_path + (abstract ? 1 : 0), name, len); ++ ++ return offsetof(struct sockaddr_un, sun_path) + len + 1; ++} ++ ++FIXTURE_SETUP(unix_listen) ++{ ++ self->sk = -1; ++ self->server = -1; ++ self->addrlen = unix_set_addr(&self->addr, SK_NAME, variant->abstract); ++ self->srv_addrlen = unix_set_addr(&self->srv_addr, SRV_NAME, ++ variant->abstract); ++} ++ ++FIXTURE_TEARDOWN(unix_listen) ++{ ++ if (self->sk >= 0) ++ close(self->sk); ++ if (self->server >= 0) ++ close(self->server); ++ ++ /* Pathname sockets leave a filesystem entry behind; abstract ones do not. */ ++ if (!variant->abstract) { ++ remove(SK_NAME); ++ remove(SRV_NAME); ++ } ++} ++ ++/* A bound socket in TCP_CLOSE is the normal, allowed case. */ ++TEST_F(unix_listen, bound_is_ok) ++{ ++ int err; ++ ++ self->sk = socket(AF_UNIX, variant->type, 0); ++ ASSERT_LE(0, self->sk); ++ ++ err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); ++ ASSERT_EQ(0, err); ++ ++ err = listen(self->sk, 8); ++ EXPECT_EQ(0, err); ++} ++ ++/* Listening again on an already-listening socket (TCP_LISTEN) is allowed. */ ++TEST_F(unix_listen, relisten_is_ok) ++{ ++ int err; ++ ++ self->sk = socket(AF_UNIX, variant->type, 0); ++ ASSERT_LE(0, self->sk); ++ ++ err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); ++ ASSERT_EQ(0, err); ++ ++ err = listen(self->sk, 8); ++ ASSERT_EQ(0, err); ++ ++ err = listen(self->sk, 16); ++ EXPECT_EQ(0, err); ++} ++ ++/* listen() on an unbound socket fails: there is nothing to listen on. */ ++TEST_F(unix_listen, unbound_is_einval) ++{ ++ int err; ++ ++ self->sk = socket(AF_UNIX, variant->type, 0); ++ ASSERT_LE(0, self->sk); ++ ++ err = listen(self->sk, 8); ++ EXPECT_EQ(-1, err); ++ EXPECT_EQ(EINVAL, errno); ++} ++ ++/* ++ * The regression: a bound socket that has already been connected is not in ++ * TCP_CLOSE or TCP_LISTEN, so listen() must reject it with EINVAL rather ++ * than quietly succeeding. ++ */ ++TEST_F(unix_listen, connected_is_einval) ++{ ++ int err; ++ ++ self->server = socket(AF_UNIX, variant->type, 0); ++ ASSERT_LE(0, self->server); ++ ++ err = bind(self->server, (struct sockaddr *)&self->srv_addr, ++ self->srv_addrlen); ++ ASSERT_EQ(0, err); ++ ++ err = listen(self->server, 8); ++ ASSERT_EQ(0, err); ++ ++ self->sk = socket(AF_UNIX, variant->type, 0); ++ ASSERT_LE(0, self->sk); ++ ++ /* Bind first so the unbound check does not mask the state check. */ ++ err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); ++ ASSERT_EQ(0, err); ++ ++ err = connect(self->sk, (struct sockaddr *)&self->srv_addr, ++ self->srv_addrlen); ++ ASSERT_EQ(0, err); ++ ++ err = listen(self->sk, 8); ++ EXPECT_EQ(-1, err); ++ EXPECT_EQ(EINVAL, errno); ++} ++ ++TEST_HARNESS_MAIN +-- +2.53.0 + diff --git a/queue-7.1/selftests-netfilter-nft_flowtable.sh-fix-offload-cou.patch b/queue-7.1/selftests-netfilter-nft_flowtable.sh-fix-offload-cou.patch new file mode 100644 index 0000000000..86cb1dfbd4 --- /dev/null +++ b/queue-7.1/selftests-netfilter-nft_flowtable.sh-fix-offload-cou.patch @@ -0,0 +1,101 @@ +From 83cf4b8bed05611a4f28b947ca79c51342972d3b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 14:53:22 +0200 +Subject: selftests: netfilter: nft_flowtable.sh: fix offload counter + verification for tunnel tests +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Lorenzo Bianconi + +[ Upstream commit 1d6123f87eebb5148844cd43045c6e598799720b ] + +The IPIP and IP6IP6 tunnel tests call check_counters() to verify +flowtable offloading occurred, but the flow-add rule only matches +meta oif "veth1". When traffic is routed through a tunnel device, +oif is the tunnel interface (tun0, tun6, etc.), not veth1, so +the flow-add rule never fires, no flowtable entry is created, +and counters stay at zero — producing a silent false pass. +Fix by adding tunnel-specific flow-add rules for each tunnel +interface. These match TCP dport 12345 traffic before the bare +accept rule, set ct mark, add the flow to the flowtable, and +increment routed_orig. The existing routed_repl rule on veth0 +already handles the reply direction since decapsulated reply +packets exit through the physical interface. +Also add check_counters() for the IP6IP6 non-VLAN and +IP6IP6-over-VLAN tests which previously used a bare PASS message. + +Fixes: fe8313316eaf ("selftests: netfilter: nft_flowtable.sh: Add IPIP flowtable selftest") +Fixes: 5e5180352193 ("selftests: netfilter: nft_flowtable.sh: Add IP6IP6 flowtable selftest") +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +--- + .../selftests/net/netfilter/nft_flowtable.sh | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh +index 08ad07500e8a7..d9a21ca8ed2cb 100755 +--- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh ++++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh +@@ -617,7 +617,11 @@ ip -6 -net "$nsr2" route add default via fee1:3::1 + ip -net "$ns2" route add default via 10.0.2.1 + ip -6 -net "$ns2" route add default via dead:2::1 + ++ip netns exec "$nsr1" nft -a insert rule inet filter forward \ ++ 'meta oif tun0 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' + ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0 accept' ++ip netns exec "$nsr1" nft -a insert rule inet filter forward \ ++ 'meta oif tun6 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' + ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun6 accept' + ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif "veth0" tcp sport 12345 ct mark set 1 flow add @f1 counter name routed_repl accept' +@@ -629,7 +633,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel"; then + fi + + if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then +- echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel" ++ check_counters "flow offload for ns1/ns2 IP6IP6 tunnel" + else + echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel" 1>&2 + ip netns exec "$nsr1" nft list ruleset +@@ -642,6 +646,8 @@ ip -net "$nsr1" link set veth1.10 up + ip -net "$nsr1" addr add 192.168.20.1/24 dev veth1.10 + ip -net "$nsr1" addr add fee1:4::1/64 dev veth1.10 nodad + ip netns exec "$nsr1" sysctl net.ipv4.conf.veth1/10.forwarding=1 > /dev/null ++ip netns exec "$nsr1" nft -a insert rule inet filter forward \ ++ 'meta oif veth1.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' + ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif veth1.10 accept' + + ip -net "$nsr1" link add name tun0.10 type ipip local 192.168.20.1 remote 192.168.20.2 +@@ -649,6 +655,8 @@ ip -net "$nsr1" link set tun0.10 up + ip -net "$nsr1" addr add 192.168.200.1/24 dev tun0.10 + ip -net "$nsr1" route change default via 192.168.200.2 + ip netns exec "$nsr1" sysctl net.ipv4.conf.tun0/10.forwarding=1 > /dev/null ++ip netns exec "$nsr1" nft -a insert rule inet filter forward \ ++ 'meta oif tun0.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' + ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0.10 accept' + + ip -net "$nsr1" link add name tun6.10 type ip6tnl local fee1:4::1 remote fee1:4::2 encaplimit none +@@ -656,6 +664,8 @@ ip -net "$nsr1" link set tun6.10 up + ip -net "$nsr1" addr add fee1:5::1/64 dev tun6.10 nodad + ip -6 -net "$nsr1" route delete default + ip -6 -net "$nsr1" route add default via fee1:5::2 ++ip netns exec "$nsr1" nft -a insert rule inet filter forward \ ++ 'meta oif tun6.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' + ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun6.10 accept' + + ip -net "$nsr2" link add link veth0 name veth0.10 type vlan id 10 +@@ -683,7 +693,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel over vlan"; then + fi + + if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then +- echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel over vlan" ++ check_counters "flow offload for ns1/ns2 IP6IP6 tunnel over vlan" + else + echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel over vlan" 1>&2 + ip netns exec "$nsr1" nft list ruleset +-- +2.53.0 + diff --git a/queue-7.1/selftests-seccomp-fix-pointer-type-mismatch-build-er.patch b/queue-7.1/selftests-seccomp-fix-pointer-type-mismatch-build-er.patch new file mode 100644 index 0000000000..b7960598dc --- /dev/null +++ b/queue-7.1/selftests-seccomp-fix-pointer-type-mismatch-build-er.patch @@ -0,0 +1,57 @@ +From 88812d591559a652371feb0a592ac479e985cfac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Jul 2026 13:35:52 +0800 +Subject: selftests/seccomp: Fix pointer type mismatch build error +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Kuan-Ying Lee + +[ Upstream commit 3421b9b056a6576d0ebac1030eafb48ad0544092 ] + +We hit the following build error while running the seccomp selftests in +our testing. + +CC seccomp_bpf +seccomp_bpf.c: In function ‘UPROBE_setup’: +seccomp_bpf.c:5175:74: error: pointer type mismatch in conditional expression [-Wincompatible-pointer-types] +5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); +| ^ +seccomp_bpf.c:5175:57: note: first expression has type ‘int (*)(void)’ +5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); +| ^~~~~~~~~~~~~~~~ +seccomp_bpf.c:5175:76: note: second expression has type ‘int (__attribute__((nocf_check)) *)(void)’ +5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); +| ^~~~~~~~~~~~~ + +get_uprobe_offset() takes a 'const void *' argument, so cast both +operands to 'void *'. + +Fixes: 9ffc7a635c35 ("selftests/seccomp: validate uprobe syscall passes through seccomp") +Signed-off-by: Kuan-Ying Lee +Acked-by: Jiri Olsa +Link: https://patch.msgid.link/20260715053559.28535-1-kuan-ying.lee@canonical.com +Signed-off-by: Kees Cook +Signed-off-by: Sasha Levin +--- + tools/testing/selftests/seccomp/seccomp_bpf.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c +index 358b6c65e120e..0622bc2acad40 100644 +--- a/tools/testing/selftests/seccomp/seccomp_bpf.c ++++ b/tools/testing/selftests/seccomp/seccomp_bpf.c +@@ -5178,7 +5178,8 @@ FIXTURE_SETUP(UPROBE) + ASSERT_GE(bit, 0); + } + +- offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); ++ offset = get_uprobe_offset(variant->uretprobe ? (void *)probed_uretprobe ++ : (void *)probed_uprobe); + ASSERT_GE(offset, 0); + + if (variant->uretprobe) +-- +2.53.0 + diff --git a/queue-7.1/series b/queue-7.1/series index c1b4db8867..2c17f6cba6 100644 --- a/queue-7.1/series +++ b/queue-7.1/series @@ -4,3 +4,192 @@ lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch alsa-hda-realtek-add-quirk-for-hp-dragonfly-folio-g3.patch thunderbolt-prevent-xdomain-delayed-work-use-after-f.patch +dmaengine-switchtec-dma-fix-field_get-misuse-when-pr.patch +pinctrl-qcom-unconditionally-mark-gpio-as-wakeup-ena.patch +pinctrl-qcom-sc8280xp-add-missing-wakeup-entries-for.patch +dmaengine-sun6i-dma-fix-reclaim-descriptors-while-te.patch +dmaengine-idxd-fix-double-free-of-wq-engine-and-grou.patch +dmaengine-idxd-fix-fdev-setup-failure-cleanup-in-idx.patch +iommu-arm-smmu-v3-iommufd-require-exactly-one-stream.patch +gpio-sloppy-logic-analyzer-fix-memory-leak-in-gpio_l.patch +selftests-lkdtm-rename-stackleak_erasing-to-kstack_e.patch +selftests-seccomp-fix-pointer-type-mismatch-build-er.patch +ntfs-preserve-recall_on_open-on-wsl-special-file-rep.patch +ata-sata_mv-accept-1-or-2-resources-in-platform-prob.patch +ata-ahci_ceva-fix-error-paths-in-ceva_ahci_platform_.patch +phy-qcom-m31-eusb2-fix-return-value-of-init-call.patch +asoc-max98095-fix-missing-is_err-before-ptr_err-on-m.patch +asoc-max98090-fix-missing-is_err-before-ptr_err-on-m.patch +of-reserved_mem-prevent-oob-when-too-many-dynamic-re.patch +btrfs-fix-leaking-btrfs_fs_state_remounting-flag.patch +btrfs-zoned-fix-deadlock-between-metadata-writeback-.patch +btrfs-zoned-reset-meta_write_pointer-on-zone-reset.patch +btrfs-warn-about-extent-buffer-that-can-not-be-relea.patch +btrfs-skip-global-block-reserve-accounting-for-rescu.patch +btrfs-raid56-fix-an-incorrect-csum-skip-during-scrub.patch +btrfs-zoned-skip-fully-truncated-ordered-extents-at-.patch +ntfs-harden-runlist-realloc-size-calculations.patch +ntfs-drop-stale-page-cache-when-shrinking-a-non-resi.patch +rtla-timerlat_top-fix-on-threshold-actions-firing-on.patch +phy-zynqmp-fix-clock-error-handling-in-xpsgtr_phy_in.patch +phy-zynqmp-fix-runtime-pm-leak-on-probe-allocation-f.patch +netfilter-nf_conntrack_sip-widen-nat-rewrite-delta-t.patch +selftests-netfilter-nft_flowtable.sh-fix-offload-cou.patch +netfilter-nf_conntrack_expect-add-and-use-nf_ct_expe.patch +drm-mediatek-check-crtc-state-before-freeing.patch +mshv_vtl-fix-fd-leak-in-mshv_ioctl_create_vtl.patch +drivers-hv-vmbus-replace-lockdep_hardirq_threaded-wi.patch +mshv-fix-duplicate-gsi-detection-for-gsi-0.patch +mshv-fix-sleeping-under-spinlock-in-mshv_portid_allo.patch +kvm-arm64-vgic-fix-race-between-lpi-release-and-re-r.patch +kvm-arm64-vgic-mitigate-potential-lpi-registration-f.patch +kvm-arm64-fix-hyp_trace-clock-disabling.patch +kvm-arm64-fix-potential-leak-in-hyp_trace_buffer_all.patch +kvm-arm64-fix-hyp_trace_desc-allocation-size-in-hyp_.patch +kvm-arm64-add-missing-hyp_enter-when-trapping-sysreg.patch +kvm-arm64-reject-guest_memfd-memslots-when-the-vm-ha.patch +keys-trusted-dcp-fix-key_len-validation-and-calc_blo.patch +keys-fix-out-of-bounds-read-in-keyring_get_key_chunk.patch +keys-make-keyring-key-chunk-byte-order-agree-with-ke.patch +assoc_array-trim-the-final-shortcut-word-using-the-c.patch +ipvs-adjust-double-hashing-when-fwd-method-changes.patch +netfilter-nf_tables-make-nft_object-rhltable-per-tab.patch +netfilter-xt_hashlimit-validate-hashtable-supports-x.patch +ipvs-fix-the-checksum-validations.patch +ipvs-fix-places-with-wrong-packet-offsets.patch +ipvs-do-not-mangle-icmp-replies-for-non-first-fragme.patch +ipvs-clear-the-nfct-flag-under-lock.patch +netfilter-nft_payload-fix-mask-build-for-partial-fie.patch +asoc-sdca-correct-pointer-passed-to-devm_acpi_table_.patch +asoc-sdca-always-free-firmware-in-fdl-path.patch +asoc-sdca-make-ump-message-size-check-more-robust.patch +asoc-sdca-ensure-that-control-range-is-large-enough-.patch +rds-tcp-hold-the-rcu-lock-across-ipv6_chk_addr-in-rd.patch +nexthop-take-nh-lock-for-f6i_list-walks-in-replace-c.patch +nexthop-avoid-unlocked-f6i_list-walk-in-nh_rt_cache_.patch +af_unix-fix-listen-succeeding-on-sockets-in-the-wron.patch +selftests-net-af_unix-test-listen-rejects-wrong-sock.patch +xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch +xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch +xsk-provide-sufficient-space-in-pool-tx_descs.patch +xsk-reclaim-invalid-tx-descriptors-in-zc-batch-path.patch +net-sched-sch_cake-skip-clearing-unused-tins-during-.patch +pinctrl-amd-don-t-clear-s4-wake-bits-at-probe.patch +scsi-libiscsi-fix-stale-data-leak-into-the-scsi-sens.patch +scsi-libiscsi_tcp-bound-scsi-response-data-segment-t.patch +scsi-libsas-fix-ha-resume-deadlock-and-hisi_sas-disk.patch +smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch +erofs-clean-up-erofs_ishare_fill_inode.patch +erofs-remove-fscache-backend-entirely.patch +erofs-ensure-valid-f_path-for-page-cache-sharing.patch +gpio-gpio-by-pinctrl-apply-initial-value-in-directio.patch +acpi-cppc-skip-writes-to-unsupported-performance-con.patch +wifi-ath12k-fix-out-of-bounds-clear_bit-in-ath12k_ma.patch +asoc-tas2781-use-correct-calibration-data-for-sinega.patch +spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch +hwmon-nct6775-core-fix-number-of-temperature-registe.patch +hwmon-ina2xx-fix-various-overflow-issues.patch +hwmon-ltc4282-fix-reading-the-minimum-alarm-voltage.patch +hwmon-sht3x-fix-unaligned-accesses.patch +hwmon-lm90-only-report-alarms-if-driver-is-ready.patch +hwmon-nzxt-smart2-dma-align-output-buffer.patch +net-do-not-send-icmp-ndisc-redirects-when-peer-alloc.patch +hwmon-nct6775-core-prevent-access-to-unsupported-wei.patch +net-bridge-mrp-fix-option-tlv-length-in-mrp_test-fra.patch +asoc-sophgo-return-1-on-volume-change-in-cv1800b_adc.patch +forcedeth-fix-uaf-of-txrx_stats-in-nv_remove.patch +hwmon-adt7470-fix-fans-stuck-in-manual-mode-on-i2c-e.patch +hwmon-adt7470-fix-cache-updated-before-hardware-writ.patch +hwmon-adt7470-fix-busy-loop-and-i2c-flooding-in-upda.patch +hwmon-adt7470-fix-temperature-alarm-logic-in-hwmon_t.patch +hwmon-adt7470-fix-swapped-pwm3-and-pwm4-auto-mode-ma.patch +hwmon-adt7470-use-cached-pwm-frequency-value.patch +hwmon-adt7470-fix-divide-by-zero-toctou-crash-in-fan.patch +hwmon-adt7470-fix-pwm-auto-temp-state-array-and-boun.patch +rtase-fix-double-free-of-multi-frag-skb-on-dma-map-f.patch +ethtool-embed-fec-hist-ranges-as-buffer-in-struct.patch +powerpc-boot-fix-simpleboot-cpu-node-lookup-check.patch +powerpc-boot-fix-treeboot-currituck-cpu-node-lookup-.patch +powerpc-boot-fix-treeboot-akebono-cpu-node-lookup-ch.patch +net-sched-cls_u32-validate-offshift-to-prevent-shift.patch +net-udp_tunnel-fix-memory-leak-in-udp_tunnel_nic_unr.patch +wifi-mac80211-validate-individual-twt-params-before-.patch +netfs-clear-pg_private_2-on-copy-to-cache-append-fai.patch +netfs-handle-single-writeback-rolling-buffer-allocat.patch +netfs-release-readahead-folios-on-iterator-preparati.patch +netfs-fix-folio_queue-enomem-in-writeback-by-adding-.patch +net-ethernet-mtk_eth_soc-pass-eth-to-mtk_handle_irq_.patch +hwmon-pmbus-fix-return-value-from-pmbus_update_byte_.patch +idpf-bound-interrupt-vector-register-fill-to-the-all.patch +idpf-adjust-txq-ring-count-minimum.patch +idpf-fix-mailbox-irq-name-leak-on-request-failure.patch +ice-suppress-dpll-errors-during-reset-recovery.patch +bluetooth-iso-clear-iso_data-always-when-detaching-c.patch +bluetooth-l2cap-fix-uaf-in-l2cap_le_connect_rsp.patch +bluetooth-iso-fix-connected-closed-transition-on-shu.patch +bluetooth-iso-lock-sk-in-iso_sock_getname.patch +bluetooth-iso-lock-sk-in-iso_connect_ind.patch +bluetooth-iso-fix-timeout-vs-sync_timeout-typo-in-ch.patch +bluetooth-iso-validate-sockaddr_iso-first-in-iso_soc.patch +bluetooth-iso-hold-sk-properly-in-iso_conn_ready.patch +bluetooth-iso-fix-leaking-sk-after-socket-release.patch +bluetooth-iso-avoid-deadlocks-in-iso_sock_timeout.patch +bluetooth-iso-ensure-no-dangling-hcon-references-in-.patch +bluetooth-iso-fix-refcounting-of-iso_conn.patch +bluetooth-iso-fix-race-of-kfree-vs-kref_get_unless_z.patch +bluetooth-btintel-validate-length-before-parsing-dia.patch +bluetooth-hci_conn-hold-conn-reference-in-abort_conn.patch +bluetooth-hci_sync-hold-conn-in-hci_connect_acl-le_s.patch +bluetooth-hci_sync-hold-conn-in-hci_connect_big_sync.patch +bluetooth-hci_sync-hold-conn-in-hci_connect_pa_sync-.patch +bluetooth-hci_sync-hold-conn-in-hci_past_sync-callba.patch +bluetooth-hci_sync-fix-hci_conn_del-use-in-hci_le_cr.patch +bluetooth-hci_sync-remove-unnecessary-hci_conn_get-i.patch +x86-boot-add-volatile-clobbers-and-zero-length-test-.patch +net-phylink-put-link_gpio-if-phylink_create-fails.patch +scsi-target-iblock-fix-wrong-pr-ops-null-check-for-p.patch +scsi-ufs-core-cancel-rtc-work-in-active-active-suspe.patch +scsi-ufs-core-revert-delegate-the-interrupt-service-.patch +scsi-zfcp-fix-memory-leak-during-adapter-release-by-.patch +scsi-target-clear-cmd_cnt-when-initial-counter-enrol.patch +octeontx2-cn20k-coordinate-default-rules-with-nix-lf.patch +octeontx2-af-block-vfs-from-clobbering-special-cgx-p.patch +scsi-mpi3mr-fix-potential-deadlock-in-mpi3mr_fault_u.patch +scsi-ufs-core-initialize-hba-rpmbs-list-in-ufshcd.patch +net-sxgbe-free-tx-rings-on-rx-allocation-failure.patch +net-sxgbe-check-descriptor-ring-allocation-failures.patch +can-isotp-check-register_netdevice_notifier-error-in.patch +drm-i915-dp-ignore-the-sink-s-dsc-max-frl-rate-witho.patch +drm-xe-pt-check-no-dma-huge-pte-cases-before-dma-seg.patch +fprobe-fix-module-reference-count-leak-on-error-in-r.patch +tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch +tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch +riscv-drop-__init-from-vec_check_unaligned_access_sp.patch +accel-qaic-use-sizeof-trans_hdr-for-transaction-leng.patch +riscv-mm-fix-out-of-bounds-page-table-walk-during-me.patch +ring-buffer-fix-reader-page-read-offset-for-remote-b.patch +ipv6-release-fib6_null_entry-on-subtree-failure.patch +riscv-vdso-only-try-to-install-vdso-when-present.patch +net-dsa-mt7530-check-bus-read-errors-in-the-mdio-reg.patch +net-dsa-mt7530-error-out-on-failed-reads-in-atc-vtcr.patch +net-dsa-mt7530-error-out-on-failed-reads-in-mt7531-p.patch +net-stmmac-fix-e2e-delay-mechanism.patch +net-mana-create-separate-eqs-for-each-vport.patch +net-mana-return-error-code-from-mana_create_rxq.patch +ptp-netc-fix-potential-interrupt-storm-caused-by-inc.patch +net-libwx-fix-fdir-atr-queue-mismatch-for-software-v.patch +octeontx2-pf-set-correct-sequence-for-carrier-off-an.patch +sched-deadline-use-revised-wakeup-rule-only-for-runn.patch +spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch +qede-sync-udp_tunnel-ports-outside-qede_lock-in-the-.patch +drm-i915-hdmi-poll-for-200-msec-for-tmds_scrambler_s.patch +ksmbd-return-success-for-deferred-final-close.patch +ksmbd-fix-use-after-free-in-__close_file_table_ids.patch +ksmbd-use-memcmp-to-compare-clientguids.patch +iomap-add-a-separate-bio_set-for-iomap_split_ioend.patch +mshv-fix-race-in-mshv_irqfd_deassign.patch +mshv-fix-level-triggered-check-on-uninitialized-data.patch +mshv-fix-missing-error-code-on-vp-allocation-failure.patch +mshv-order-pt_vp_array-publish-against-irqfd-asserti.patch +mshv-publish-vp-to-pt_vp_array-before-installing-the.patch +ring-buffer-fix-subbuf_ids-memory-leak-in-rb_allocat.patch diff --git a/queue-7.1/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch b/queue-7.1/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch new file mode 100644 index 0000000000..c9506d595a --- /dev/null +++ b/queue-7.1/smb-client-fix-buffer-leaks-in-smb1-read-and-write.patch @@ -0,0 +1,85 @@ +From b6d9477020fdc02547966c10ef80cd1e7c5ed39f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 28 Jun 2026 14:59:09 +0800 +Subject: smb: client: fix buffer leaks in SMB1 read and write + +From: Dawei Feng + +[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ] + +CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request +buffer before checking whether tcon->ses->server is NULL. If that +defensive check ever fails, the helper returns -ECONNABORTED without +releasing the request buffer. + +Fix these leaks by releasing the allocated request buffer before +returning from these error paths. Use cifs_small_buf_release() for the +buffers allocated by small_smb_init() and cifs_buf_release() for the +buffer allocated by smb_init(). + +The bug was first flagged by an experimental analysis tool we are +developing for kernel memory-management bugs while analyzing +v6.13-rc1. The tool is still under development and is not yet publicly +available. Manual inspection confirms that the bug is still +present in v7.1.1. + +An x86_64 allyesconfig build showed no new warnings. + +Runtime validation used a temporary fault-injection hook to force +tcon->ses->server to NULL after request-buffer initialization. On the +unfixed kernel, the harness observed two leaked small request buffers and +one leaked large request buffer, with directed kmemleak dumps confirming +the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer +deltas remained. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Signed-off-by: Dawei Feng +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/client/cifssmb.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c +index 9e27bfa7376b1..024c5d462424d 100644 +--- a/fs/smb/client/cifssmb.c ++++ b/fs/smb/client/cifssmb.c +@@ -1681,8 +1681,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -1796,8 +1798,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +@@ -2077,8 +2081,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ +- if (tcon->ses->server == NULL) ++ if (!tcon->ses->server) { ++ cifs_small_buf_release(pSMB); + return -ECONNABORTED; ++ } + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; +-- +2.53.0 + diff --git a/queue-7.1/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch b/queue-7.1/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch new file mode 100644 index 0000000000..363034df49 --- /dev/null +++ b/queue-7.1/spi-spi-cadence-move-tx-fifo-full-busy-wait-into-fif.patch @@ -0,0 +1,111 @@ +From ead9658f107b27afd8cd1d29cfd265fcd4a78c20 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 18:25:10 +0530 +Subject: spi: spi-cadence: Move TX FIFO full busy-wait into FIFO +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Srikanth Boyapally + +[ Upstream commit d9eadfce2fac49445db40808fe4d8259f20a9d2b ] + +SPI host transfers could intermittently stall with spi_transfer timeouts. +The TXFULL condition was checked only once in cdns_transfer_one() before +cdns_spi_process_fifo(), so if the FIFO became full again during refill, +writes could be dropped and the transfer would never complete. + +Move the TXFULL busy-wait into the TX path of cdns_spi_process_fifo() so +the 10µs back-off is applied per FIFO entry during filling, ensuring +forward progress and eliminating spurious timeouts. + +Restrict the delay to host mode using spi_controller_is_target(), the +controller is passed into cdns_spi_process_fifo() so the check is made at +the point of use. In target mode this delay must not run as it causes the +target to miss its transfer window and corrupt data. + +Fixes: 49530e641178 ("spi: cadence: Add usleep_range() for cdns_spi_fill_tx_fifo()") +Signed-off-by: Srikanth Boyapally +Reviewed-by: Radhey Shyam Pandey +Link: https://patch.msgid.link/20260720125510.60166-1-srikanth.boyapally@amd.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-cadence.c | 26 ++++++++++++++++---------- + 1 file changed, 16 insertions(+), 10 deletions(-) + +diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c +index 891e2ba369584..2f549e668ae6a 100644 +--- a/drivers/spi/spi-cadence.c ++++ b/drivers/spi/spi-cadence.c +@@ -388,11 +388,13 @@ static inline void cdns_spi_writer(struct cdns_spi *xspi) + + /** + * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO ++ * @ctlr: Pointer to the spi_controller structure + * @xspi: Pointer to the cdns_spi structure + * @ntx: Number of bytes to pack into the TX FIFO + * @nrx: Number of bytes to drain from the RX FIFO + */ +-static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) ++static void cdns_spi_process_fifo(struct spi_controller *ctlr, ++ struct cdns_spi *xspi, int ntx, int nrx) + { + ntx = clamp(ntx, 0, xspi->tx_bytes); + nrx = clamp(nrx, 0, xspi->rx_bytes); +@@ -407,6 +409,16 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) + } + + if (ntx) { ++ /* When xspi in busy condition, bytes may send failed, ++ * then spi control didn't work thoroughly, add one byte ++ * delay. Only in host mode; in target mode this delay ++ * causes data corruption as the target fails to prepare ++ * data in time. ++ */ ++ if (!spi_controller_is_target(ctlr) && ++ (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)) ++ udelay(10); ++ + cdns_spi_writer(xspi); + ntx--; + } +@@ -460,14 +472,14 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id) + cdns_spi_write(xspi, CDNS_SPI_THLD, 1); + + if (xspi->tx_bytes) { +- cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt); ++ cdns_spi_process_fifo(ctlr, xspi, trans_cnt, trans_cnt); + } else { + /* Fixed delay due to controller limitation with + * RX_NEMPTY incorrect status + * Xilinx AR:65885 contains more details + */ + udelay(10); +- cdns_spi_process_fifo(xspi, 0, trans_cnt); ++ cdns_spi_process_fifo(ctlr, xspi, 0, trans_cnt); + cdns_spi_write(xspi, CDNS_SPI_IDR, + CDNS_SPI_IXR_DEFAULT); + spi_finalize_current_transfer(ctlr); +@@ -520,17 +532,11 @@ static int cdns_transfer_one(struct spi_controller *ctlr, + cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1); + } + +- /* When xspi in busy condition, bytes may send failed, +- * then spi control didn't work thoroughly, add one byte delay +- */ +- if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) +- udelay(10); +- + xspi->n_bytes = cdns_spi_n_bytes(transfer); + xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes); + xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes); + +- cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0); ++ cdns_spi_process_fifo(ctlr, xspi, xspi->tx_fifo_depth, 0); + + cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); + return transfer->len; +-- +2.53.0 + diff --git a/queue-7.1/spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch b/queue-7.1/spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch new file mode 100644 index 0000000000..74031654d5 --- /dev/null +++ b/queue-7.1/spi-spi-nxp-fspi-add-per-soc-sdr-dtr-clock-rate-limi.patch @@ -0,0 +1,197 @@ +From 2e71772b73995db2899c5ba23c858afda15d5950 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 18:18:08 +0800 +Subject: spi: spi-nxp-fspi: add per-SoC SDR/DTR clock rate limits for all + supported SoCs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Haibo Chen + +[ Upstream commit 9c19d60fea9f46ed0c3394653ef4941f1a459968 ] + +The commit f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for +different sample clock source selection") introduced a global 166MHz +cap for DTR mode (RXCLKSRC=3), based on the i.MX8MN datasheet timing +specification (Section 3.9.9, page 65). + +After reviewing the FlexSPI timing parameters in the datasheets for all +supported SoCs, the following corrections and additions are needed: + +1. SDR mode (RXCLKSRC=0) limits vary per SoC: + - i.MX8MN/MM/MP/95: 66MHz (IMX8MNCEC §3.9.9, IMX8MMCEC §3.9.10, + IMX8MPCEC, IMX95CEC Rev.8 §4.11.7) + - i.MX8QXP/QM/DXL/ULP: 60MHz (IMX8QXPCEC, IMX8QMCEC, IMX8DXLCEC, + IMX8ULPCEC §7.3.1 ND mode) + - LX2160A: 100MHz (LX2160ACEC FlexSPI timing parameters) + +2. DTR mode (RXCLKSRC=3) limits vary per SoC: + - i.MX8MN/MM/MP/ULP: 166MHz + - i.MX8QXP/QM/DXL: 200MHz (same FlexSPI IP across this family) + - i.MX95: 200MHz (IMX95CEC §4.11.7.3.2.3 Table 106) + - LX2160A: DTR disabled (FSPI_QUIRK_DISABLE_DTR) + +Update related platform data with correct speed limation according +to datasheet. + +Fixes: f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for different sample clock source selection") +Signed-off-by: Haibo Chen +Link: https://patch.msgid.link/20260728-fspi-clock-v2-1-dbe786a4a6eb@nxp.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-nxp-fspi.c | 83 ++++++++++++++++++++++++++++++++++++-- + 1 file changed, 80 insertions(+), 3 deletions(-) + +diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c +index 1e36ae084dd86..39c1eaaf9e0ae 100644 +--- a/drivers/spi/spi-nxp-fspi.c ++++ b/drivers/spi/spi-nxp-fspi.c +@@ -340,6 +340,18 @@ struct nxp_fspi_devtype_data { + unsigned int quirks; + unsigned int lut_num; + bool little_endian; ++ /* ++ * The max clock rate (Hz) that FlexSPI can output to the device ++ * in SDR mode (RXCLKSRC=0). Defaults to 66MHz if zero. ++ * Some SoCs (e.g. LX2160A) support up to 100MHz in SDR mode. ++ */ ++ unsigned long max_sdr_rate; ++ /* ++ * The max clock rate (Hz) that FlexSPI can output to the device ++ * in DTR mode (RXCLKSRC=3). Defaults to 166MHz if zero. ++ * Some SoCs (e.g. i.MX95, i.MX8QM, i.MX8DXL) support up to 200MHz. ++ */ ++ unsigned long max_dtr_rate; + }; + + static struct nxp_fspi_devtype_data lx2160a_data = { +@@ -349,6 +361,10 @@ static struct nxp_fspi_devtype_data lx2160a_data = { + .quirks = FSPI_QUIRK_DISABLE_DTR, + .lut_num = 32, + .little_endian = true, /* little-endian */ ++ /* ++ * LX2160ACEC: SDR RXCLKSRC=0 max 100MHz, DTR disabled via quirk. ++ */ ++ .max_sdr_rate = 100000000, + }; + + static struct nxp_fspi_devtype_data imx8mm_data = { +@@ -358,6 +374,21 @@ static struct nxp_fspi_devtype_data imx8mm_data = { + .quirks = 0, + .lut_num = 32, + .little_endian = true, /* little-endian */ ++ /* IMX8MMCEC §3.9.10: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */ ++ .max_sdr_rate = 66000000, ++ .max_dtr_rate = 166000000, ++}; ++ ++static struct nxp_fspi_devtype_data imx8mp_data = { ++ .rxfifo = SZ_512, /* (64 * 64 bits) */ ++ .txfifo = SZ_1K, /* (128 * 64 bits) */ ++ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ ++ .quirks = 0, ++ .lut_num = 32, ++ .little_endian = true, /* little-endian */ ++ /* IMX8MPCEC: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */ ++ .max_sdr_rate = 66000000, ++ .max_dtr_rate = 166000000, + }; + + static struct nxp_fspi_devtype_data imx8qxp_data = { +@@ -367,6 +398,12 @@ static struct nxp_fspi_devtype_data imx8qxp_data = { + .quirks = 0, + .lut_num = 32, + .little_endian = true, /* little-endian */ ++ /* ++ * IMX8QXPCEC: SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 200MHz. ++ * i.MX8QM and i.MX8DXL share the same FlexSPI IP and limits. ++ */ ++ .max_sdr_rate = 60000000, ++ .max_dtr_rate = 200000000, + }; + + static struct nxp_fspi_devtype_data imx8dxl_data = { +@@ -376,6 +413,12 @@ static struct nxp_fspi_devtype_data imx8dxl_data = { + .quirks = FSPI_QUIRK_USE_IP_ONLY, + .lut_num = 32, + .little_endian = true, /* little-endian */ ++ /* ++ * IMX8DXLCEC (i.MX 8XLite): SDR RXCLKSRC=0 max 60MHz, ++ * DDR RXCLKSRC=3 max 200MHz. ++ */ ++ .max_sdr_rate = 60000000, ++ .max_dtr_rate = 200000000, + }; + + static struct nxp_fspi_devtype_data imx8ulp_data = { +@@ -385,6 +428,29 @@ static struct nxp_fspi_devtype_data imx8ulp_data = { + .quirks = 0, + .lut_num = 16, + .little_endian = true, /* little-endian */ ++ /* ++ * IMX8ULPCEC §7.3.1, Normal Drive (ND, 1.0V) mode: ++ * SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 166MHz. ++ * Note: Overdrive (OD, 1.05V) allows up to 180MHz DTR ++ * but is not the default use case. ++ */ ++ .max_sdr_rate = 60000000, ++ .max_dtr_rate = 166000000, ++}; ++ ++static struct nxp_fspi_devtype_data imx95_data = { ++ .rxfifo = SZ_512, /* (64 * 64 bits) */ ++ .txfifo = SZ_1K, /* (128 * 64 bits) */ ++ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ ++ .quirks = 0, ++ .lut_num = 32, ++ .little_endian = true, /* little-endian */ ++ /* ++ * IMX95CEC Rev.8 §4.11.7: SDR RXCLKSRC=0 max 66MHz, ++ * DDR RXCLKSRC=3 max 200MHz (Nominal/Overdrive mode). ++ */ ++ .max_sdr_rate = 66000000, ++ .max_dtr_rate = 200000000, + }; + + struct nxp_fspi { +@@ -691,10 +757,20 @@ static void nxp_fspi_select_rx_sample_clk_source(struct nxp_fspi *f, + reg = fspi_readl(f, f->iobase + FSPI_MCR0); + if (op_is_dtr) { + reg |= FSPI_MCR0_RXCLKSRC(3); +- f->max_rate = 166000000; ++ /* ++ * Use the SoC-specific DTR max rate if provided, otherwise ++ * fall back to 166MHz (limit from IMX8MN datasheet §3.9.9). ++ */ ++ f->max_rate = f->devtype_data->max_dtr_rate ? ++ f->devtype_data->max_dtr_rate : 166000000; + } else { /*select mode 0 */ + reg &= ~FSPI_MCR0_RXCLKSRC(3); +- f->max_rate = 66000000; ++ /* ++ * Use the SoC-specific SDR max rate if provided, otherwise ++ * fall back to 66MHz (limit from IMX8MN datasheet §3.9.9). ++ */ ++ f->max_rate = f->devtype_data->max_sdr_rate ? ++ f->devtype_data->max_sdr_rate : 66000000; + } + fspi_writel(f, reg, f->iobase + FSPI_MCR0); + } +@@ -1444,10 +1520,11 @@ static const struct dev_pm_ops nxp_fspi_pm_ops = { + static const struct of_device_id nxp_fspi_dt_ids[] = { + { .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, }, + { .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, }, +- { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mm_data, }, ++ { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mp_data, }, + { .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, }, + { .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, }, + { .compatible = "nxp,imx8ulp-fspi", .data = (void *)&imx8ulp_data, }, ++ { .compatible = "nxp,imx95-fspi", .data = (void *)&imx95_data, }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids); +-- +2.53.0 + diff --git a/queue-7.1/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch b/queue-7.1/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch new file mode 100644 index 0000000000..17a34cd10e --- /dev/null +++ b/queue-7.1/tracing-mmiotrace-add-null-check-for-mmio_trace_arra.patch @@ -0,0 +1,70 @@ +From e119c0928d8d829ee951e14a236c4859c9ad5259 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 21:50:00 +0900 +Subject: tracing/mmiotrace: Add NULL check for mmio_trace_array in logging + functions + +From: Masami Hiramatsu (Google) + +[ Upstream commit 12b80cdbc54cf615b4717a4e8180063408091ea2 ] + +mmio_trace_rw() and mmio_trace_mapping() retrieve mmio_trace_array into +tr and pass it to __trace_mmiotrace_rw() and __trace_mmiotrace_map(). +If these functions are invoked while mmio_trace_array is NULL (e.g. before +initialization or after disabled), accessing tr->array_buffer.buffer will +result in a NULL pointer dereference crash. + +Fix this by adding an explicit NULL check for tr at the beginning of +__trace_mmiotrace_rw() and __trace_mmiotrace_map(). + +Link: https://patch.msgid.link/178524300062.56416.8362487250709962380.stgit@devnote2 +Fixes: f984b51e0779 ("ftrace: add mmiotrace plugin") +Assisted-by: Antigravity:gemini-3.6-flash +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_mmiotrace.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c +index e064ba3f28cb9..df8692c2dea8a 100644 +--- a/kernel/trace/trace_mmiotrace.c ++++ b/kernel/trace/trace_mmiotrace.c +@@ -294,11 +294,15 @@ device_initcall(init_mmio_trace); + static void __trace_mmiotrace_rw(struct trace_array *tr, + struct mmiotrace_rw *rw) + { +- struct trace_buffer *buffer = tr->array_buffer.buffer; ++ struct trace_buffer *buffer; + struct ring_buffer_event *event; + struct trace_mmiotrace_rw *entry; + unsigned int trace_ctx; + ++ if (!tr) ++ return; ++ ++ buffer = tr->array_buffer.buffer; + trace_ctx = tracing_gen_ctx_flags(0); + event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW, + sizeof(*entry), trace_ctx); +@@ -321,11 +325,15 @@ void mmio_trace_rw(struct mmiotrace_rw *rw) + static void __trace_mmiotrace_map(struct trace_array *tr, + struct mmiotrace_map *map) + { +- struct trace_buffer *buffer = tr->array_buffer.buffer; ++ struct trace_buffer *buffer; + struct ring_buffer_event *event; + struct trace_mmiotrace_map *entry; + unsigned int trace_ctx; + ++ if (!tr) ++ return; ++ ++ buffer = tr->array_buffer.buffer; + trace_ctx = tracing_gen_ctx_flags(0); + event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP, + sizeof(*entry), trace_ctx); +-- +2.53.0 + diff --git a/queue-7.1/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch b/queue-7.1/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch new file mode 100644 index 0000000000..71f23cb3e0 --- /dev/null +++ b/queue-7.1/tracing-mmiotrace-reset-dropped_count-in-mmio_reset_.patch @@ -0,0 +1,43 @@ +From e586c4e22d55a294830d49859e782dfe319eb514 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 28 Jul 2026 21:49:51 +0900 +Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data() + +From: Masami Hiramatsu (Google) + +[ Upstream commit c786d2bdf1f3964deee192ad942dee2a741c1e2c ] + +mmio_reset_data() is called during tracer initialization, reset, and +start. While it resets overrun_detected and prev_overruns, it neglects +to reset dropped_count. Consequently, dropped event counts from prior +tracing sessions persist in dropped_count and corrupt overrun reports +in subsequent runs. + +Fix this by explicitly calling atomic_set(&dropped_count, 0) in +mmio_reset_data(). + +Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2 +Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording") +Assisted-by: Antigravity:gemini-3.6-flash +Signed-off-by: Masami Hiramatsu (Google) +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +--- + kernel/trace/trace_mmiotrace.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c +index b88b8d9923adb..e064ba3f28cb9 100644 +--- a/kernel/trace/trace_mmiotrace.c ++++ b/kernel/trace/trace_mmiotrace.c +@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr) + { + overrun_detected = false; + prev_overruns = 0; ++ atomic_set(&dropped_count, 0); + + tracing_reset_online_cpus(&tr->array_buffer); + } +-- +2.53.0 + diff --git a/queue-7.1/wifi-ath12k-fix-out-of-bounds-clear_bit-in-ath12k_ma.patch b/queue-7.1/wifi-ath12k-fix-out-of-bounds-clear_bit-in-ath12k_ma.patch new file mode 100644 index 0000000000..948f208d43 --- /dev/null +++ b/queue-7.1/wifi-ath12k-fix-out-of-bounds-clear_bit-in-ath12k_ma.patch @@ -0,0 +1,57 @@ +From d135f59e005549ade412c44e0c7501de83892473 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jul 2026 14:43:22 +0800 +Subject: wifi: ath12k: fix out-of-bounds clear_bit in + ath12k_mac_dp_peer_cleanup() + +From: Baochen Qiang + +[ Upstream commit 47abd2ca281531deee38a3b3770d885e270e9fc9 ] + +ath12k_mac_dp_peer_cleanup() clears the ML peer ID slot on the +free_ml_peer_id_map bitmap by indexing it with dp_peer->peer_id. That is +wrong: dp_peer->peer_id for an MLO peer always carries the +ATH12K_PEER_ML_ID_VALID bit (BIT(13)), so clear_bit() is invoked with +index >= 0x2000, which is far outside the bitmap of ATH12K_MAX_MLO_PEERS +(256) bits and corrupts memory adjacent to ah->free_ml_peer_id_map. The +intended bitmap entry also never gets cleared, so subsequent +ath12k_peer_ml_alloc() calls eventually run out of IDs. + +The ID without the VALID bit is what ath12k_peer_ml_alloc() returned and +is stored in ahsta->ml_peer_id. Use that instead. + +While there, also reset ahsta->ml_peer_id to ATH12K_MLO_PEER_ID_INVALID so +the bitmap and ahsta->ml_peer_id stay in sync. + +Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 + +Fixes: ee16dcf573d5 ("wifi: ath12k: Define ath12k_dp_peer structure & APIs for create & delete") +Signed-off-by: Baochen Qiang +Reviewed-by: Rameshkumar Sundaram +Link: https://patch.msgid.link/20260720-ath12k-fw-allocated-ml-peer-id-v2-1-630632758a80@oss.qualcomm.com +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath12k/mac.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c +index 7408c2577dc2e..d13fb31d13014 100644 +--- a/drivers/net/wireless/ath/ath12k/mac.c ++++ b/drivers/net/wireless/ath/ath12k/mac.c +@@ -1283,8 +1283,11 @@ void ath12k_mac_dp_peer_cleanup(struct ath12k_hw *ah) + spin_lock_bh(&dp_hw->peer_lock); + list_for_each_entry_safe(dp_peer, tmp, &dp_hw->dp_peers_list, list) { + if (dp_peer->is_mlo) { ++ struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(dp_peer->sta); ++ + rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL); +- clear_bit(dp_peer->peer_id, ah->free_ml_peer_id_map); ++ clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map); ++ ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID; + } + + list_move(&dp_peer->list, &peers); +-- +2.53.0 + diff --git a/queue-7.1/wifi-mac80211-validate-individual-twt-params-before-.patch b/queue-7.1/wifi-mac80211-validate-individual-twt-params-before-.patch new file mode 100644 index 0000000000..7f68a35053 --- /dev/null +++ b/queue-7.1/wifi-mac80211-validate-individual-twt-params-before-.patch @@ -0,0 +1,52 @@ +From 6e3d95caf6d624fa77a4d4cfd1fc6a96c2787a48 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 09:09:28 +0800 +Subject: wifi: mac80211: validate individual TWT params before driver setup + +From: Zhao Li + +[ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ] + +ieee80211_process_rx_twt_action() only partially validates a received +S1G TWT setup frame before queueing it. + +An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup() +with twt->length too short for the full struct ieee80211_twt_params. + +The individual path passes twt to drv_add_twt_setup(). Both the tracepoint +and the driver callback consume the complete parameters block, not merely +req_type. Do not pass a short individual agreement to the driver. +Broadcast agreements remain unchanged because they are rejected locally +after accessing only req_type. + +Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode") +Assisted-by: Codex:gpt-5 +Assisted-by: Claude:opus-4.8 +Signed-off-by: Zhao Li +Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com +[edit commit message to not overclaim lack of validation nor + understate driver impact] +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/s1g.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c +index 5af4a0c6c6424..abc338e22e59c 100644 +--- a/net/mac80211/s1g.c ++++ b/net/mac80211/s1g.c +@@ -101,6 +101,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata, + struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.s1g.variable; + struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + ++ if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) && ++ twt->length < sizeof(twt->control) + sizeof(*twt_agrt)) ++ return; ++ + twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST); + + /* broadcast TWT not supported yet */ +-- +2.53.0 + diff --git a/queue-7.1/x86-boot-add-volatile-clobbers-and-zero-length-test-.patch b/queue-7.1/x86-boot-add-volatile-clobbers-and-zero-length-test-.patch new file mode 100644 index 0000000000..27e3872df3 --- /dev/null +++ b/queue-7.1/x86-boot-add-volatile-clobbers-and-zero-length-test-.patch @@ -0,0 +1,54 @@ +From 40ac3e5e75bad5acf1a1b85db3a84cb6a17efa97 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 23 Jul 2026 20:08:04 -0300 +Subject: x86/boot: Add volatile, clobbers and zero-length test in memcmp() + +From: Mauricio Faria de Oliveira + +[ Upstream commit a8c171c107c0b61a5e7e10cedab0fb72aeaf640d ] + +Add the volatile qualifier and clobbers parameter to prevent bugs with +instruction reordering and optimization. + +Also add TEST for the zero-length case to set ZF, as, if the count register +is zero, the REPE prefix does not run the CMPSB instruction, leaving the ZF +flag undetermined. + + [ bp: Add a comment about the len==0 case. ] + +Fixes: 62bd0337d0c4 ("Top header file for new x86 setup code") +Closes: https://sashiko.dev/#/patchset/20260701-pvh-kasan-inline-v6-0-ba99045dfa9f%40igalia.com +Suggested-by: Borislav Petkov +Signed-off-by: Mauricio Faria de Oliveira +Signed-off-by: Borislav Petkov (AMD) +Link: https://lore.kernel.org/all/20260721-pvh-kasan-inline-v7-2-38979a50cef0@igalia.com +Signed-off-by: Sasha Levin +--- + arch/x86/boot/string.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c +index b25c6a9303b73..3a2bba7c25e9d 100644 +--- a/arch/x86/boot/string.c ++++ b/arch/x86/boot/string.c +@@ -32,8 +32,15 @@ + int memcmp(const void *s1, const void *s2, size_t len) + { + bool diff; +- asm("repe cmpsb" +- : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len)); ++ ++ /* ++ * Make sure ZF is properly set in the len==0 case because in it, ++ * RCX==0 and the REPE; CMPSB won't get executed. ++ */ ++ asm volatile("test %3, %3\n\t" ++ "repe cmpsb" ++ : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len) ++ : : "cc", "memory"); + return diff; + } + +-- +2.53.0 + diff --git a/queue-7.1/xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch b/queue-7.1/xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch new file mode 100644 index 0000000000..4fb9683c22 --- /dev/null +++ b/queue-7.1/xsk-drain-continuation-descs-after-overflow-in-xsk_b.patch @@ -0,0 +1,144 @@ +From 18d3d050b25025fb9eb3bd9e21df259f44e56345 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 15:56:05 +0200 +Subject: xsk: drain continuation descs after overflow in xsk_build_skb() + +From: Jason Xing + +[ Upstream commit bd44a6dcd4248883de90f5dad53ae80066e27096 ] + +Fix generic xmit path multi-buffer logic when packets are either too big +(count of descriptors exceed MAX_SKB_FRAGS) or an invalid descriptor is +included in fragmented packet. Introduce xdp_sock::drain_cont and act +upon this flag - when it is set, keep on consuming descriptors from +AF_XDP Tx ring and put them directly onto Cq. Previously these +descriptors were silently lost and could never be reached again. + +Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path") +Closes: https://lore.kernel.org/all/20260425041726.85FB3C2BCB2@smtp.kernel.org/ +Reviewed-by: Jason Xing +Co-developed-by: Maciej Fijalkowski # wrapped cq addr submission onto routine +Signed-off-by: Maciej Fijalkowski +Signed-off-by: Jason Xing +Acked-by: Stanislav Fomichev +Link: https://patch.msgid.link/20260719135609.147823-3-maciej.fijalkowski@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + include/net/xdp_sock.h | 1 + + net/xdp/xsk.c | 45 +++++++++++++++++++++++++++++++++++++++--- + 2 files changed, 43 insertions(+), 3 deletions(-) + +diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h +index ebac60a3d8a17..8b51876efbed1 100644 +--- a/include/net/xdp_sock.h ++++ b/include/net/xdp_sock.h +@@ -80,6 +80,7 @@ struct xdp_sock { + * call of __xsk_generic_xmit(). + */ + struct sk_buff *skb; ++ bool drain_cont; + + struct list_head map_list; + /* Protects map_list */ +diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c +index 0a6203c425766..8dadc39ca81f3 100644 +--- a/net/xdp/xsk.c ++++ b/net/xdp/xsk.c +@@ -736,6 +736,19 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool, + spin_unlock_irqrestore(&pool->cq_prod_lock, flags); + } + ++static void xsk_cq_submit_addr_single_locked(struct xsk_buff_pool *pool, ++ struct xdp_desc *desc) ++{ ++ unsigned long flags; ++ u32 idx; ++ ++ spin_lock_irqsave(&pool->cq_prod_lock, flags); ++ idx = xskq_get_prod(pool->cq); ++ xskq_prod_write_addr(pool->cq, idx, desc->addr); ++ xskq_prod_submit_n(pool->cq, 1); ++ spin_unlock_irqrestore(&pool->cq_prod_lock, flags); ++} ++ + static void xsk_cq_cancel_locked(struct xsk_buff_pool *pool, u32 n) + { + spin_lock(&pool->cq->cq_cached_prod_lock); +@@ -1027,13 +1040,14 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, + static int __xsk_generic_xmit(struct sock *sk) + { + struct xdp_sock *xs = xdp_sk(sk); +- bool sent_frame = false; + struct xdp_desc desc; + struct sk_buff *skb; ++ u32 cached_cons; + u32 max_batch; + int err = 0; + + mutex_lock(&xs->mutex); ++ cached_cons = xs->tx->cached_cons; + + /* Since we dropped the RCU read lock, the socket state might have changed. */ + if (unlikely(!xsk_is_bound(xs))) { +@@ -1062,11 +1076,21 @@ static int __xsk_generic_xmit(struct sock *sk) + goto out; + } + ++ if (unlikely(xs->drain_cont)) { ++ xsk_cq_submit_addr_single_locked(xs->pool, &desc); ++ xs->tx->invalid_descs++; ++ xskq_cons_release(xs->tx); ++ xs->drain_cont = xp_mb_desc(&desc); ++ continue; ++ } ++ + skb = xsk_build_skb(xs, &desc); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + if (err != -EOVERFLOW) + goto out; ++ if (xp_mb_desc(&desc)) ++ xs->drain_cont = true; + err = 0; + continue; + } +@@ -1095,18 +1119,33 @@ static int __xsk_generic_xmit(struct sock *sk) + goto out; + } + +- sent_frame = true; + xs->skb = NULL; + } + + if (xskq_has_descs(xs->tx)) { ++ bool drain = xs->skb || xs->drain_cont || xp_mb_desc(&desc); ++ ++ err = xsk_cq_reserve_locked(xs->pool); ++ if (err) { ++ xs->tx->invalid_descs--; ++ if (xs->skb) ++ xsk_drop_skb(xs->skb); ++ xs->drain_cont = drain; ++ err = -EAGAIN; ++ goto out; ++ } ++ + if (xs->skb) + xsk_drop_skb(xs->skb); ++ ++ xsk_cq_submit_addr_single_locked(xs->pool, &desc); ++ + xskq_cons_release(xs->tx); ++ xs->drain_cont = xp_mb_desc(&desc); + } + + out: +- if (sent_frame) ++ if (xs->tx->cached_cons != cached_cons) + __xsk_tx_release(xs); + + mutex_unlock(&xs->mutex); +-- +2.53.0 + diff --git a/queue-7.1/xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch b/queue-7.1/xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch new file mode 100644 index 0000000000..961fcf181e --- /dev/null +++ b/queue-7.1/xsk-fix-buffer-leak-in-xsk_drop_skb-for-af_xdp-multi.patch @@ -0,0 +1,106 @@ +From cdcda7c54b36ee4bda5141c242e2a5d28c774ee8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 15:56:04 +0200 +Subject: xsk: fix buffer leak in xsk_drop_skb() for AF_XDP multi-buffer Tx + +From: Jason Xing + +[ Upstream commit a3c8382ebce4780c6b3ace2c09bc342313ac0186 ] + +This patch is inspired by the check[1] from sashiko. It says when +overflow happens, the address of cq to be published is invalid. +Actually the severer thing is the whole process of publishing the +address of cq in this particular case is not right: it should truely +publish the address and advance the cached_prod in cq as long as it +reads descriptors from txq. + +The following is the full analysis. +xsk_drop_skb() is called in three places, which all discard a partially +built multi-buffer skb: +1) xsk_build_skb() -EOVERFLOW error path: packet exceeds MAX_SKB_FRAGS +2) __xsk_generic_xmit() post-loop cleanup: an invalid descriptor in + the TX ring prevents the partial packet from completing +3) xsk_release(): socket close while xs->skb holds an incomplete packet + +In all three cases, the TX descriptors for the already-processed frags +have been consumed from the TX ring (xskq_cons_release), and CQ slots +have been reserved. However, xsk_drop_skb() calls xsk_consume_skb() +which cancels the CQ reservations via xsk_cq_cancel_locked(). Since +the buffer addresses never appear in the completion queue, userspace +permanently loses track of these buffers. + +Fix this by letting consume_skb() trigger the existing xsk_destruct_skb +destructor, which already submits buffer addresses to the CQ via +xsk_cq_submit_addr_locked(). + +Note that cancelling the descriptors back to the TX ring (via +xskq_cons_cancel_n) is not a appropriate option because an oversized +packet that always exceeds MAX_SKB_FRAGS would be retried indefinitely, +which is an obviously deadlock bug in the TX path. + +Also move the desc->addr assignment in xsk_build_skb() above the +overflow check so that the current descriptor's address is recorded +before a potential -EOVERFLOW jump to free_err, consistent with the +zerocopy path in xsk_build_skb_zerocopy(). + +[1]: https://lore.kernel.org/all/20260425041726.85FB3C2BCB2@smtp.kernel.org/ + +Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path") +Acked-by: Maciej Fijalkowski +Signed-off-by: Jason Xing +Acked-by: Stanislav Fomichev +Link: https://patch.msgid.link/20260719135609.147823-2-maciej.fijalkowski@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/xdp/xsk.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c +index f8c8a8c9dfba5..0a6203c425766 100644 +--- a/net/xdp/xsk.c ++++ b/net/xdp/xsk.c +@@ -793,8 +793,11 @@ static void xsk_consume_skb(struct sk_buff *skb) + + static void xsk_drop_skb(struct sk_buff *skb) + { +- xdp_sk(skb->sk)->tx->invalid_descs += xsk_get_num_desc(skb); +- xsk_consume_skb(skb); ++ struct xdp_sock *xs = xdp_sk(skb->sk); ++ ++ xs->tx->invalid_descs += xsk_get_num_desc(skb); ++ consume_skb(skb); ++ xs->skb = NULL; + } + + static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, +@@ -876,7 +879,7 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, + return ERR_PTR(-ENOMEM); + + /* in case of -EOVERFLOW that could happen below, +- * xsk_consume_skb() will release this node as whole skb ++ * xsk_drop_skb() will release this node as whole skb + * would be dropped, which implies freeing all list elements + */ + xsk_addr->addrs[xsk_addr->num_descs] = desc->addr; +@@ -968,6 +971,8 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, + goto free_err; + } + ++ xsk_addr->addrs[xsk_addr->num_descs] = desc->addr; ++ + if (unlikely(nr_frags == (MAX_SKB_FRAGS - 1) && xp_mb_desc(desc))) { + err = -EOVERFLOW; + goto free_err; +@@ -985,8 +990,6 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, + + skb_add_rx_frag(skb, nr_frags, page, 0, len, PAGE_SIZE); + refcount_add(PAGE_SIZE, &xs->sk.sk_wmem_alloc); +- +- xsk_addr->addrs[xsk_addr->num_descs] = desc->addr; + } + } + +-- +2.53.0 + diff --git a/queue-7.1/xsk-provide-sufficient-space-in-pool-tx_descs.patch b/queue-7.1/xsk-provide-sufficient-space-in-pool-tx_descs.patch new file mode 100644 index 0000000000..7ee4fcc415 --- /dev/null +++ b/queue-7.1/xsk-provide-sufficient-space-in-pool-tx_descs.patch @@ -0,0 +1,140 @@ +From 22d91d590e32e553428a11d0da6301eb3b67d577 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 15:56:06 +0200 +Subject: xsk: provide sufficient space in pool->tx_descs + +From: Maciej Fijalkowski + +[ Upstream commit 08c9a8e794b4694c100dafcb80e069e29ad81b64 ] + +The temporary Tx descriptor array in an XSK buffer pool is currently +sized from the Tx ring of the socket that creates the pool. + +This is insufficient for shared-UMEM Tx. A later socket may have a +larger Tx ring and submit a valid multi-buffer packet containing more +descriptors than the first socket's ring, while still remaining within +the device's xdp_zc_max_segs limit. + +A packet-framed batch parser bounded by the temporary array cannot reach +the end-of-packet descriptor in that case. It leaves the packet on the +Tx ring and encounters the same packet on every subsequent attempt, +stalling Tx processing for that socket. + +Size the temporary descriptor array to the larger of the first Tx ring +and the device's xdp_zc_max_segs capability. This keeps the array large +enough to inspect one maximum-sized valid packet. Larger shared Tx rings +do not require further resizing, as they can be processed over multiple +batches. + +Following commit will actually address the data path side. + +Fixes: d5581966040f ("xsk: support ZC Tx multi-buffer in batch API") +Reviewed-by: Jason Xing +Signed-off-by: Maciej Fijalkowski +Acked-by: Stanislav Fomichev +Link: https://patch.msgid.link/20260719135609.147823-4-maciej.fijalkowski@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + include/net/xsk_buff_pool.h | 6 ++++-- + net/xdp/xsk.c | 10 +++++++--- + net/xdp/xsk_buff_pool.c | 12 ++++++++---- + 3 files changed, 19 insertions(+), 9 deletions(-) + +diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h +index ccb3b350001f2..f5e737a830559 100644 +--- a/include/net/xsk_buff_pool.h ++++ b/include/net/xsk_buff_pool.h +@@ -102,12 +102,14 @@ struct xsk_buff_pool { + + /* AF_XDP core. */ + struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs, +- struct xdp_umem *umem); ++ struct xdp_umem *umem, ++ u32 max_segs); + int xp_assign_dev(struct xsk_buff_pool *pool, struct net_device *dev, + u16 queue_id, u16 flags); + int xp_assign_dev_shared(struct xsk_buff_pool *pool, struct xdp_sock *umem_xs, + struct net_device *dev, u16 queue_id); +-int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs); ++int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs, ++ u32 max_segs); + void xp_destroy(struct xsk_buff_pool *pool); + void xp_get_pool(struct xsk_buff_pool *pool); + bool xp_put_pool(struct xsk_buff_pool *pool); +diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c +index 8dadc39ca81f3..161ec3d47f053 100644 +--- a/net/xdp/xsk.c ++++ b/net/xdp/xsk.c +@@ -1524,7 +1524,8 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr + * and/or device. + */ + xs->pool = xp_create_and_assign_umem(xs, +- umem_xs->umem); ++ umem_xs->umem, ++ dev->xdp_zc_max_segs); + if (!xs->pool) { + err = -ENOMEM; + sockfd_put(sock); +@@ -1556,7 +1557,8 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr + * utilizes + */ + if (xs->tx && !xs->pool->tx_descs) { +- err = xp_alloc_tx_descs(xs->pool, xs); ++ err = xp_alloc_tx_descs(xs->pool, xs, ++ dev->xdp_zc_max_segs); + if (err) { + xp_put_pool(xs->pool); + xs->pool = NULL; +@@ -1574,7 +1576,9 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr + goto out_unlock; + } else { + /* This xsk has its own umem. */ +- xs->pool = xp_create_and_assign_umem(xs, xs->umem); ++ xs->pool = xp_create_and_assign_umem(xs, xs->umem, ++ dev->xdp_zc_max_segs); ++ + if (!xs->pool) { + err = -ENOMEM; + goto out_unlock; +diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c +index d981cfdd85357..419dc0ffbf7ef 100644 +--- a/net/xdp/xsk_buff_pool.c ++++ b/net/xdp/xsk_buff_pool.c +@@ -42,9 +42,12 @@ void xp_destroy(struct xsk_buff_pool *pool) + kvfree(pool); + } + +-int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs) ++int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs, ++ u32 max_segs) + { +- pool->tx_descs = kvzalloc_objs(*pool->tx_descs, xs->tx->nentries); ++ u32 nentries = max(xs->tx->nentries, max_segs); ++ ++ pool->tx_descs = kvzalloc_objs(*pool->tx_descs, nentries); + if (!pool->tx_descs) + return -ENOMEM; + +@@ -52,7 +55,8 @@ int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs) + } + + struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs, +- struct xdp_umem *umem) ++ struct xdp_umem *umem, ++ u32 max_segs) + { + bool unaligned = umem->flags & XDP_UMEM_UNALIGNED_CHUNK_FLAG; + struct xsk_buff_pool *pool; +@@ -69,7 +73,7 @@ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs, + goto out; + + if (xs->tx) +- if (xp_alloc_tx_descs(pool, xs)) ++ if (xp_alloc_tx_descs(pool, xs, max_segs)) + goto out; + + pool->chunk_mask = ~((u64)umem->chunk_size - 1); +-- +2.53.0 + diff --git a/queue-7.1/xsk-reclaim-invalid-tx-descriptors-in-zc-batch-path.patch b/queue-7.1/xsk-reclaim-invalid-tx-descriptors-in-zc-batch-path.patch new file mode 100644 index 0000000000..cd7b6989fc --- /dev/null +++ b/queue-7.1/xsk-reclaim-invalid-tx-descriptors-in-zc-batch-path.patch @@ -0,0 +1,516 @@ +From a90131b8674f9b2600302b4c6ae084011afb4a17 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 19 Jul 2026 15:56:07 +0200 +Subject: xsk: reclaim invalid Tx descriptors in ZC batch path + +From: Maciej Fijalkowski + +[ Upstream commit 72f2b4516faf55d4dfac2414649d3cffa5fd2c5e ] + +The zero-copy Tx batch parser stops when it encounters an invalid +descriptor. If this happens after one or more continuation descriptors, +the Tx consumer can be advanced past fragments that are neither submitted +to the driver nor returned to userspace through the completion ring. + +A similar problem occurs when a packet exceeds xdp_zc_max_segs. The +descriptors consumed up to the limit are released without completion, and +the remaining continuation descriptors can subsequently be interpreted +as the beginning of another packet. + +Parse Tx batches in packet units and distinguish descriptors belonging to +complete valid packets from descriptors consumed while draining an +invalid or oversized packet. Return the former to the driver and append +the latter to the CQ address area so userspace can reclaim their UMEM +frames. + +Treat a standalone invalid descriptor as a one-descriptor reclaim-only +packet. Advancing the Tx-ring consumer releases the ring slot, but does +not by itself return ownership of the referenced UMEM frame to userspace. + +Once draining starts, continue until the packet's end-of-packet +descriptor is consumed. Preserve the drain state on the socket when EOP +has not yet been supplied, so draining can continue during a later call. +Leave incomplete but otherwise valid packets on the Tx ring. + +Shared-UMEM pools using multi-buffer Tx also need packet-framed parsing. +Walk their Tx sockets one packet at a time, preserving the existing +per-socket fairness scheme, instead of using the legacy one-descriptor +fallback. Keep that fallback for shared pools that do not use +multi-buffer Tx. Since the drain state is maintained per socket and both +the singular and shared paths can resume an interrupted drain, changing +the socket list from singular to shared requires no special bind-time +transition. + +CQ entries are positional, and drivers may complete only part of the Tx +work returned by xsk_tx_peek_release_desc_batch(). Therefore, reclaim-only +entries cannot be published immediately when earlier driver-visible +descriptors are still outstanding. + +Track the number of driver-visible CQ entries preceding the reclaim +entries. Let xsk_tx_completed() publish partial hardware Tx completions, +and publish the reclaim entries only after every earlier Tx descriptor +has completed. Complete a reclaim-only batch immediately when there is no +driver-visible work in front of it, and prevent another Tx batch from +being appended while reclaim entries remain pending. + +Also cap batch processing by the size of the pool's temporary descriptor +array, as Tx rings belonging to sockets sharing a UMEM may have different +sizes. + +This ensures that every invalid Tx descriptor consumed by the ZC batch +path is either submitted to the driver as part of a valid packet or +returned to userspace without violating CQ completion ordering. + +Reviewed-by: Jason Xing +Signed-off-by: Maciej Fijalkowski +Acked-by: Stanislav Fomichev +Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path") +Link: https://patch.msgid.link/20260719135609.147823-5-maciej.fijalkowski@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + Documentation/networking/af_xdp.rst | 54 ++++---- + include/net/xsk_buff_pool.h | 3 + + net/xdp/xsk.c | 187 +++++++++++++++++++++++++--- + net/xdp/xsk_buff_pool.c | 1 + + net/xdp/xsk_queue.h | 65 +++++++--- + 5 files changed, 248 insertions(+), 62 deletions(-) + +diff --git a/Documentation/networking/af_xdp.rst b/Documentation/networking/af_xdp.rst +index 50d92084a49c6..cc3f0d16b28fb 100644 +--- a/Documentation/networking/af_xdp.rst ++++ b/Documentation/networking/af_xdp.rst +@@ -43,12 +43,13 @@ UMEM also has two rings: the FILL ring and the COMPLETION ring. The + FILL ring is used by the application to send down addr for the kernel + to fill in with RX packet data. References to these frames will then + appear in the RX ring once each packet has been received. The +-COMPLETION ring, on the other hand, contains frame addr that the +-kernel has transmitted completely and can now be used again by user +-space, for either TX or RX. Thus, the frame addrs appearing in the +-COMPLETION ring are addrs that were previously transmitted using the +-TX ring. In summary, the RX and FILL rings are used for the RX path +-and the TX and COMPLETION rings are used for the TX path. ++COMPLETION ring, on the other hand, contains frame addresses from Tx ++descriptors that the kernel has finished processing and that can now be ++used again by user space, for either Tx or Rx. This includes frames whose ++transmission has completed as well as frames referenced by invalid Tx ++descriptors rejected by the kernel. A completion therefore returns ++ownership of a frame to user space, but does not by itself guarantee that ++the packet was successfully transmitted. + + The socket is then finally bound with a bind() call to a device and a + specific queue id on that device, and it is not until bind is +@@ -169,14 +170,15 @@ chunks mode, then the incoming addr will be left untouched. + UMEM Completion Ring + ~~~~~~~~~~~~~~~~~~~~ + +-The COMPLETION Ring is used transfer ownership of UMEM frames from ++The COMPLETION Ring is used to transfer ownership of UMEM frames from + kernel-space to user-space. Just like the FILL ring, UMEM indices are +-used. +- +-Frames passed from the kernel to user-space are frames that has been +-sent (TX ring) and can be used by user-space again. +- +-The user application consumes UMEM addrs from this ring. ++used. Frames passed from the kernel to user-space are frames referenced ++by Tx descriptors that the kernel has finished processing and can be ++used by user-space again. This includes both frames whose transmission ++has completed and frames referenced by invalid Tx descriptors that were ++rejected and reclaimed by the kernel. A completion entry does not ++guarantee successful packet transmission. The user application consumes ++UMEM addrs from this ring. + + + RX Ring +@@ -504,21 +506,25 @@ will be treated as an invalid descriptor. + These are the semantics for producing packets onto AF_XDP Tx ring + consisting of multiple frames: + +-* When an invalid descriptor is found, all the other +- descriptors/frames of this packet are marked as invalid and not +- completed. The next descriptor is treated as the start of a new +- packet, even if this was not the intent (because we cannot guess +- the intent). As before, if your program is producing invalid +- descriptors you have a bug that must be fixed. ++* When an invalid descriptor is found, the complete packet is treated as ++ invalid. The kernel consumes descriptors through the descriptor marking ++ the end of the packet and returns all their frame addresses through the ++ COMPLETION ring. A standalone invalid descriptor is treated as a ++ one-descriptor invalid packet. The descriptor following the end of the ++ invalid packet is treated as the start of a new packet. As before, if ++ your program is producing invalid descriptors you have a bug that must ++ be fixed. Rejected descriptors are reported in the ``tx_invalid_descs`` ++ statistic. + + * Zero length descriptors are treated as invalid descriptors. + + * For copy mode, the maximum supported number of frames in a packet is +- equal to CONFIG_MAX_SKB_FRAGS + 1. If it is exceeded, all +- descriptors accumulated so far are dropped and treated as +- invalid. To produce an application that will work on any system +- regardless of this config setting, limit the number of frags to 18, +- as the minimum value of the config is 17. ++ equal to CONFIG_MAX_SKB_FRAGS + 1. If it is exceeded, all descriptors ++ through the end of the oversized packet are consumed, treated as invalid, ++ and their frame addresses are returned through the COMPLETION ring. To ++ produce an application that will work on any system regardless of this ++ config setting, limit the number of frags to 18, as the minimum value of ++ the config is 17. + + * For zero-copy mode, the limit is up to what the NIC HW + supports. Usually at least five on the NICs we have checked. We +diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h +index f5e737a830559..2bb1d122b1bc6 100644 +--- a/include/net/xsk_buff_pool.h ++++ b/include/net/xsk_buff_pool.h +@@ -78,6 +78,9 @@ struct xsk_buff_pool { + u32 chunk_size; + u32 chunk_shift; + u32 frame_len; ++ u32 tx_descs_nentries; ++ u32 reclaim_descs; ++ u32 tx_zc_pending_descs; + u32 xdp_zc_max_segs; + u8 tx_metadata_len; /* inherited from umem */ + u8 cached_need_wakeup; +diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c +index 161ec3d47f053..855fe92358bac 100644 +--- a/net/xdp/xsk.c ++++ b/net/xdp/xsk.c +@@ -498,6 +498,23 @@ void __xsk_map_flush(struct list_head *flush_list) + + void xsk_tx_completed(struct xsk_buff_pool *pool, u32 nb_entries) + { ++ u32 reclaim_descs = READ_ONCE(pool->reclaim_descs); ++ ++ if (unlikely(reclaim_descs)) { ++ u32 pending_descs = READ_ONCE(pool->tx_zc_pending_descs); ++ ++ if (nb_entries < pending_descs) { ++ WRITE_ONCE(pool->tx_zc_pending_descs, ++ pending_descs - nb_entries); ++ xskq_prod_submit_n(pool->cq, nb_entries); ++ return; ++ } ++ ++ WRITE_ONCE(pool->tx_zc_pending_descs, 0); ++ nb_entries += reclaim_descs; ++ WRITE_ONCE(pool->reclaim_descs, 0); ++ } ++ + xskq_prod_submit_n(pool->cq, nb_entries); + } + EXPORT_SYMBOL(xsk_tx_completed); +@@ -573,24 +590,157 @@ static u32 xsk_tx_peek_release_fallback(struct xsk_buff_pool *pool, u32 max_entr + return nb_pkts; + } + ++static void xsk_tx_commit_batch(struct xsk_buff_pool *pool, ++ struct xsk_tx_batch *batch) ++{ ++ u32 nb_descs = xsk_tx_batch_cq_descs(batch); ++ u32 cq_cached_prod; ++ ++ if (!nb_descs) ++ return; ++ ++ cq_cached_prod = pool->cq->cached_prod; ++ xskq_prod_write_addr_batch(pool->cq, pool->tx_descs, nb_descs); ++ ++ if (unlikely(batch->reclaim_descs)) { ++ u32 cq_pending_descs; ++ ++ /* CQ is positional. Descriptors already written but not ++ * submitted must complete before any reclaim-only descriptors ++ * appended below. ++ */ ++ cq_pending_descs = cq_cached_prod - xskq_get_prod(pool->cq); ++ ++ WRITE_ONCE(pool->tx_zc_pending_descs, ++ batch->tx_descs + cq_pending_descs); ++ WRITE_ONCE(pool->reclaim_descs, batch->reclaim_descs); ++ if (unlikely(!pool->tx_zc_pending_descs)) ++ xsk_tx_completed(pool, 0); ++ } ++} ++ ++static struct xsk_tx_batch ++__xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, struct xdp_sock *xs, ++ struct xdp_desc *descs, u32 max_descs) ++{ ++ struct xsk_tx_batch batch = {}; ++ u32 entries; ++ ++ entries = xskq_cons_nb_entries(xs->tx, max_descs); ++ if (!entries) ++ return batch; ++ ++ batch = xskq_cons_read_desc_batch(xs, pool, descs, max_descs); ++ if (!xsk_tx_batch_cq_descs(&batch)) { ++ xs->tx->queue_empty_descs++; ++ } else { ++ __xskq_cons_release(xs->tx); ++ xs->sk.sk_write_space(&xs->sk); ++ } ++ return batch; ++} ++ ++static struct xsk_tx_batch ++xsk_tx_peek_release_shared_desc_batch(struct xsk_buff_pool *pool, u32 max_descs) ++{ ++ u32 cq_descs_before, cq_descs_after; ++ struct xsk_tx_batch sum_batch = {}; ++ bool budget_exhausted; ++ u32 per_socket_budget; ++ struct xdp_sock *xs; ++ ++ /* The fairness quota must allow one maximum-sized valid packet. */ ++ per_socket_budget = max_t(u32, MAX_PER_SOCKET_BUDGET, ++ pool->xdp_zc_max_segs); ++ ++again: ++ budget_exhausted = false; ++ cq_descs_before = xsk_tx_batch_cq_descs(&sum_batch); ++ list_for_each_entry_rcu(xs, &pool->xsk_tx_list, tx_list) { ++ u32 budget, budget_left, offset, remaining, used; ++ struct xsk_tx_batch curr_batch; ++ ++ /* Once reclaim-only descriptors have been appended to the CQ ++ * address area, do not append driver-visible Tx descriptors ++ * from another socket after them. xsk_tx_completed() relies on ++ * all driver-visible descriptors preceding all reclaim-only ++ * descriptors in CQ order. ++ */ ++ if (sum_batch.reclaim_descs) ++ break; ++ ++ /* be gentle when playing with pool->tx_descs */ ++ offset = xsk_tx_batch_cq_descs(&sum_batch); ++ if (offset >= max_descs) ++ break; ++ ++ if (xs->tx_budget_spent >= per_socket_budget) { ++ if (xskq_cons_nb_entries(xs->tx, 1)) ++ budget_exhausted = true; ++ continue; ++ } ++ ++ budget_left = per_socket_budget - xs->tx_budget_spent; ++ remaining = max_descs - offset; ++ budget = min(remaining, budget_left); ++ ++ curr_batch = __xsk_tx_peek_release_desc_batch(pool, xs, ++ pool->tx_descs + offset, ++ budget); ++ used = xsk_tx_batch_cq_descs(&curr_batch); ++ if (!used) { ++ if (curr_batch.budget_limited && budget_left < remaining) ++ budget_exhausted = true; ++ continue; ++ } ++ ++ xs->tx_budget_spent += used; ++ sum_batch.tx_descs += curr_batch.tx_descs; ++ sum_batch.reclaim_descs = curr_batch.reclaim_descs; ++ } ++ ++ cq_descs_after = xsk_tx_batch_cq_descs(&sum_batch); ++ ++ if (sum_batch.reclaim_descs || cq_descs_after >= max_descs) ++ return sum_batch; ++ ++ /* Continue filling the batch while this pass made progress */ ++ if (cq_descs_before != cq_descs_after) ++ goto again; ++ ++ if (!budget_exhausted) ++ return sum_batch; ++ ++ list_for_each_entry_rcu(xs, &pool->xsk_tx_list, tx_list) ++ xs->tx_budget_spent = 0; ++ goto again; ++} ++ + u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 nb_pkts) + { ++ struct xsk_tx_batch batch = {}; + struct xdp_sock *xs; ++ bool umem_shared; + + rcu_read_lock(); +- if (!list_is_singular(&pool->xsk_tx_list)) { +- /* Fallback to the non-batched version */ +- rcu_read_unlock(); +- return xsk_tx_peek_release_fallback(pool, nb_pkts); +- } ++ if (unlikely(READ_ONCE(pool->reclaim_descs))) ++ goto out; + +- xs = list_first_or_null_rcu(&pool->xsk_tx_list, struct xdp_sock, tx_list); +- if (!xs) { +- nb_pkts = 0; ++ xs = list_first_or_null_rcu(&pool->xsk_tx_list, struct xdp_sock, ++ tx_list); ++ if (!xs) + goto out; +- } + +- nb_pkts = xskq_cons_nb_entries(xs->tx, nb_pkts); ++ nb_pkts = min(nb_pkts, pool->tx_descs_nentries); ++ if (!nb_pkts) ++ goto out; ++ ++ umem_shared = !list_is_singular(&pool->xsk_tx_list); ++ ++ if (umem_shared && !(pool->umem->flags & XDP_UMEM_SG_FLAG)) { ++ rcu_read_unlock(); ++ return xsk_tx_peek_release_fallback(pool, nb_pkts); ++ } + + /* This is the backpressure mechanism for the Tx path. Try to + * reserve space in the completion queue for all packets, but +@@ -602,19 +752,16 @@ u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 nb_pkts) + if (!nb_pkts) + goto out; + +- nb_pkts = xskq_cons_read_desc_batch(xs->tx, pool, nb_pkts); +- if (!nb_pkts) { +- xs->tx->queue_empty_descs++; +- goto out; +- } +- +- __xskq_cons_release(xs->tx); +- xskq_prod_write_addr_batch(pool->cq, pool->tx_descs, nb_pkts); +- xs->sk.sk_write_space(&xs->sk); ++ batch = umem_shared ? ++ xsk_tx_peek_release_shared_desc_batch(pool, nb_pkts) : ++ __xsk_tx_peek_release_desc_batch(pool, xs, ++ pool->tx_descs, ++ nb_pkts); ++ xsk_tx_commit_batch(pool, &batch); + + out: + rcu_read_unlock(); +- return nb_pkts; ++ return batch.tx_descs; + } + EXPORT_SYMBOL(xsk_tx_peek_release_desc_batch); + +diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c +index 419dc0ffbf7ef..4538223f44082 100644 +--- a/net/xdp/xsk_buff_pool.c ++++ b/net/xdp/xsk_buff_pool.c +@@ -51,6 +51,7 @@ int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs, + if (!pool->tx_descs) + return -ENOMEM; + ++ pool->tx_descs_nentries = nentries; + return 0; + } + +diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h +index 3e3fbb73d23e2..1bc42c8902f4b 100644 +--- a/net/xdp/xsk_queue.h ++++ b/net/xdp/xsk_queue.h +@@ -58,6 +58,17 @@ struct parsed_desc { + u32 valid; + }; + ++struct xsk_tx_batch { ++ u32 tx_descs; ++ u32 reclaim_descs; ++ bool budget_limited; ++}; ++ ++static inline u32 xsk_tx_batch_cq_descs(const struct xsk_tx_batch *batch) ++{ ++ return batch->tx_descs + batch->reclaim_descs; ++} ++ + /* The structure of the shared state of the rings are a simple + * circular buffer, as outlined in + * Documentation/core-api/circular-buffers.rst. For the Rx and +@@ -263,17 +274,18 @@ static inline void parse_desc(struct xsk_queue *q, struct xsk_buff_pool *pool, + parsed->mb = xp_mb_desc(desc); + } + +-static inline +-u32 xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool, +- u32 max) ++static inline struct xsk_tx_batch ++xskq_cons_read_desc_batch(struct xdp_sock *xs, struct xsk_buff_pool *pool, ++ struct xdp_desc *descs, u32 max) + { +- u32 cached_cons = q->cached_cons, nb_entries = 0; +- struct xdp_desc *descs = pool->tx_descs; +- u32 total_descs = 0, nr_frags = 0; ++ bool drain = READ_ONCE(xs->drain_cont); ++ u32 cached_cons, nb_entries = 0; ++ struct xsk_tx_batch batch = {}; ++ struct xsk_queue *q = xs->tx; ++ u32 nr_frags = 0; ++ ++ cached_cons = q->cached_cons; + +- /* track first entry, if stumble upon *any* invalid descriptor, rewind +- * current packet that consists of frags and stop the processing +- */ + while (cached_cons != q->cached_prod && nb_entries < max) { + struct xdp_rxtx_ring *ring = (struct xdp_rxtx_ring *)q->ring; + u32 idx = cached_cons & q->ring_mask; +@@ -283,25 +295,42 @@ u32 xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool, + cached_cons++; + parse_desc(q, pool, &descs[nb_entries], &parsed); + if (unlikely(!parsed.valid)) +- break; ++ drain = true; ++ ++ nr_frags++; ++ nb_entries++; + + if (likely(!parsed.mb)) { +- total_descs += (nr_frags + 1); +- nr_frags = 0; +- } else { +- nr_frags++; +- if (nr_frags == pool->xdp_zc_max_segs) { ++ if (unlikely(drain)) { ++ batch.reclaim_descs = nr_frags; ++ WRITE_ONCE(xs->drain_cont, false); + nr_frags = 0; + break; + } ++ ++ batch.tx_descs += nr_frags; ++ nr_frags = 0; ++ continue; ++ } ++ ++ if (nr_frags == pool->xdp_zc_max_segs) ++ drain = true; ++ } ++ ++ if (nr_frags) { ++ if (drain) { ++ batch.reclaim_descs = nr_frags; ++ WRITE_ONCE(xs->drain_cont, true); ++ } else { ++ if (nb_entries == max) ++ batch.budget_limited = true; ++ cached_cons -= nr_frags; + } +- nb_entries++; + } + +- cached_cons -= nr_frags; + /* Release valid plus any invalid entries */ + xskq_cons_release_n(q, cached_cons - q->cached_cons); +- return total_descs; ++ return batch; + } + + /* Functions for consumers */ +-- +2.53.0 +