--- /dev/null
+From 5099807f335ce4f783f0578bef7278fffad30b07 Mon Sep 17 00:00:00 2001
+From: Kory Maincent <kory.maincent@bootlin.com>
+Date: Wed, 15 Apr 2026 15:02:59 +0200
+Subject: [PATCH] net: pse-pd: fix out-of-bounds bitmap access in pse_isr() on
+ 32-bit
+
+In pse_isr(), notifs_mask was declared as a single unsigned long on the
+stack (32 bits on 32-bit architectures). For PSE controllers with more
+than 32 ports, this causes two problems:
+
+- map_event callbacks could wrote bit positions >= 32 via
+ *notifs_mask |= BIT(i), which is undefined behaviour on a 32-bit
+ unsigned long and corrupts adjacent stack memory.
+
+- for_each_set_bit(i, ¬ifs_mask, pcdev->nr_lines) treats
+ ¬ifs_mask as a multi-word bitmap and reads beyond the single
+ unsigned long when nr_lines > BITS_PER_LONG.
+
+Fix this by moving notifs_mask out of the stack and into struct pse_irq
+as a dynamically allocated bitmap. It is sized with
+BITS_TO_LONGS(pcdev->nr_lines) words in devm_pse_irq_helper(), so it
+is always wide enough regardless of the host word size.
+
+[Jakub]: No upstream driver currently supports >=32 ports.
+
+Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
+Link: https://patch.msgid.link/20260415130300.806152-1-kory.maincent@bootlin.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/pse-pd/pse_core.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/pse-pd/pse_core.c
++++ b/drivers/net/pse-pd/pse_core.c
+@@ -1170,6 +1170,7 @@ struct pse_irq {
+ struct pse_controller_dev *pcdev;
+ struct pse_irq_desc desc;
+ unsigned long *notifs;
++ unsigned long *notifs_mask;
+ };
+
+ /**
+@@ -1247,7 +1248,6 @@ static int pse_set_config_isr(struct pse
+ static irqreturn_t pse_isr(int irq, void *data)
+ {
+ struct pse_controller_dev *pcdev;
+- unsigned long notifs_mask = 0;
+ struct pse_irq_desc *desc;
+ struct pse_irq *h = data;
+ int ret, i;
+@@ -1257,14 +1257,15 @@ static irqreturn_t pse_isr(int irq, void
+
+ /* Clear notifs mask */
+ memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs));
++ bitmap_zero(h->notifs_mask, pcdev->nr_lines);
+ mutex_lock(&pcdev->lock);
+- ret = desc->map_event(irq, pcdev, h->notifs, ¬ifs_mask);
+- if (ret || !notifs_mask) {
++ ret = desc->map_event(irq, pcdev, h->notifs, h->notifs_mask);
++ if (ret || bitmap_empty(h->notifs_mask, pcdev->nr_lines)) {
+ mutex_unlock(&pcdev->lock);
+ return IRQ_NONE;
+ }
+
+- for_each_set_bit(i, ¬ifs_mask, pcdev->nr_lines) {
++ for_each_set_bit(i, h->notifs_mask, pcdev->nr_lines) {
+ unsigned long notifs, rnotifs;
+ struct pse_ntf ntf = {};
+
+@@ -1340,6 +1341,10 @@ int devm_pse_irq_helper(struct pse_contr
+ if (!h->notifs)
+ return -ENOMEM;
+
++ h->notifs_mask = devm_bitmap_zalloc(dev, pcdev->nr_lines, GFP_KERNEL);
++ if (!h->notifs_mask)
++ return -ENOMEM;
++
+ ret = devm_request_threaded_irq(dev, irq, NULL, pse_isr,
+ IRQF_ONESHOT | irq_flags,
+ irq_name, h);
--- /dev/null
+From a2d81faf75c1661d7a5977725731f481ba8de92d Mon Sep 17 00:00:00 2001
+From: Carlo Szelinsky <github@szelinsky.de>
+Date: Sat, 23 May 2026 21:32:58 +0200
+Subject: [PATCH] net: pse-pd: disable IRQ before freeing PI data in unregister
+
+pse_controller_unregister() frees the PI array via pse_release_pis()
+before disabling the controller IRQ. The threaded IRQ handler pse_isr()
+walks pcdev->pi[] (via pse_set_config_isr() and
+regulator_notifier_call_chain() on pcdev->pi[i].rdev), so an interrupt
+arriving in the window between pse_release_pis() and disable_irq()
+dereferences freed memory.
+
+Disable the IRQ first, then release the PI array. cancel_work_sync()
+for the notification worker stays after pse_release_pis(): the worker
+only touches the kfifo and the pse_control list, not pcdev->pi.
+
+Fixes: ffef61d6d273 ("net: pse-pd: Add support for budget evaluation strategies")
+Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
+---
+ drivers/net/pse-pd/pse_core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/pse-pd/pse_core.c
++++ b/drivers/net/pse-pd/pse_core.c
+@@ -1115,9 +1115,9 @@ EXPORT_SYMBOL_GPL(pse_controller_registe
+ void pse_controller_unregister(struct pse_controller_dev *pcdev)
+ {
+ pse_flush_pw_ds(pcdev);
+- pse_release_pis(pcdev);
+ if (pcdev->irq)
+ disable_irq(pcdev->irq);
++ pse_release_pis(pcdev);
+ cancel_work_sync(&pcdev->ntf_work);
+ kfifo_free(&pcdev->ntf_fifo);
+ mutex_lock(&pse_list_mutex);
--- /dev/null
+From c4bd3afcf943491228e284cc6e68a145fcd07c2b Mon Sep 17 00:00:00 2001
+From: Carlo Szelinsky <github@szelinsky.de>
+Date: Sat, 23 May 2026 21:33:24 +0200
+Subject: [PATCH] net: pse-pd: guard against freed PI data on regulator disable
+
+PSE PI regulators are devm-registered inside pse_controller_register(),
+which runs before devres_add() arms the controller's own release in
+devm_pse_controller_register(). On driver detach, devres unwinds in LIFO
+order, so pse_controller_unregister() runs first and frees pcdev->pi via
+pse_release_pis(); the regulators are torn down afterwards.
+
+When regulator_unregister() flushes a pending disable, the regulator core
+invokes pse_pi_disable(), which dereferences pcdev->pi[id] (directly and
+via _pse_pi_disable() -> pse_pi_deallocate_pw_budget()). At that point the
+PI array is already freed, so this is a use-after-free.
+
+pse_release_pis() now clears pcdev->pi after freeing it, and
+pse_pi_disable() bails out under the lock when pcdev->pi is NULL, so a
+late disable from the regulator core is a no-op once the controller has
+been unregistered.
+
+Fixes: ffef61d6d273 ("net: pse-pd: Add support for budget evaluation strategies")
+Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
+---
+ drivers/net/pse-pd/pse_core.c | 13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/pse-pd/pse_core.c
++++ b/drivers/net/pse-pd/pse_core.c
+@@ -145,6 +145,7 @@ static void pse_release_pis(struct pse_c
+ of_node_put(pcdev->pi[i].np);
+ }
+ kfree(pcdev->pi);
++ pcdev->pi = NULL;
+ }
+
+ /**
+@@ -702,15 +703,21 @@ static int pse_pi_enable(struct regulato
+ static int pse_pi_disable(struct regulator_dev *rdev)
+ {
+ struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
+- struct pse_pi *pi;
+ int id, ret;
+
+ id = rdev_get_id(rdev);
+- pi = &pcdev->pi[id];
+ mutex_lock(&pcdev->lock);
++ /* The controller may already be unregistered (pcdev->pi freed) by the
++ * time the regulator core flushes a deferred disable during
++ * regulator_unregister(). Bail out to avoid touching freed PI data.
++ */
++ if (!pcdev->pi) {
++ mutex_unlock(&pcdev->lock);
++ return 0;
++ }
+ ret = _pse_pi_disable(pcdev, id);
+ if (!ret)
+- pi->admin_state_enabled = 0;
++ pcdev->pi[id].admin_state_enabled = 0;
+
+ mutex_unlock(&pcdev->lock);
+ return 0;
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
-@@ -1138,6 +1138,9 @@ int pse_controller_register(struct pse_c
+@@ -1145,6 +1145,9 @@ int pse_controller_register(struct pse_c
list_add(&pcdev->list, &pse_controller_list);
mutex_unlock(&pse_list_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(pse_controller_register);
-@@ -1148,6 +1151,9 @@ EXPORT_SYMBOL_GPL(pse_controller_registe
+@@ -1155,6 +1158,9 @@ EXPORT_SYMBOL_GPL(pse_controller_registe
*/
void pse_controller_unregister(struct pse_controller_dev *pcdev)
{
+ PSE_UNREGISTERED, pcdev);
+
pse_flush_pw_ds(pcdev);
- pse_release_pis(pcdev);
if (pcdev->irq)
+ disable_irq(pcdev->irq);
dev_err(sfp->dev, "phy_device_register failed: %pe\n",
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
-@@ -2016,3 +2016,17 @@ bool pse_has_c33(struct pse_control *pse
+@@ -2028,3 +2028,17 @@ bool pse_has_c33(struct pse_control *pse
return psec->pcdev->types & ETHTOOL_PSE_C33;
}
EXPORT_SYMBOL_GPL(pse_has_c33);