From: Carlo Szelinsky Date: Sat, 18 Jul 2026 13:38:38 +0000 (+0200) Subject: kernel: pse-pd: backport net fixes for unregister/isr X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d060557066e823ed3d6590d8c4eaa5d649563182;p=thirdparty%2Fopenwrt.git kernel: pse-pd: backport net fixes for unregister/isr pse_isr() 32-bit OOB bitmap access, disable IRQ before freeing PI data on unregister, and guard against freed PI data on regulator disable. Pending upstream in the PSE-core teardown UAF fix series [PATCH net v2 0/2] (in review; merge expected soon). Link: https://lore.kernel.org/all/20260711121611.1639086-1-github@szelinsky.de/ Signed-off-by: Carlo Szelinsky Link: https://github.com/openwrt/openwrt/pull/22245 Signed-off-by: Jonas Jelonek --- diff --git a/target/linux/generic/pending-6.18/895-00-net-pse-pd-fix-out-of-bounds-bitmap-access-in-pse_isr-on-32-bit.patch b/target/linux/generic/pending-6.18/895-00-net-pse-pd-fix-out-of-bounds-bitmap-access-in-pse_isr-on-32-bit.patch new file mode 100644 index 00000000000..aa3ee7cb7da --- /dev/null +++ b/target/linux/generic/pending-6.18/895-00-net-pse-pd-fix-out-of-bounds-bitmap-access-in-pse_isr-on-32-bit.patch @@ -0,0 +1,80 @@ +From 5099807f335ce4f783f0578bef7278fffad30b07 Mon Sep 17 00:00:00 2001 +From: Kory Maincent +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 +Link: https://patch.msgid.link/20260415130300.806152-1-kory.maincent@bootlin.com +Signed-off-by: Jakub Kicinski +--- + 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); diff --git a/target/linux/generic/pending-6.18/895-00a-net-pse-pd-disable-IRQ-before-freeing-PI-data-in-unregister.patch b/target/linux/generic/pending-6.18/895-00a-net-pse-pd-disable-IRQ-before-freeing-PI-data-in-unregister.patch new file mode 100644 index 00000000000..4e99d5ef425 --- /dev/null +++ b/target/linux/generic/pending-6.18/895-00a-net-pse-pd-disable-IRQ-before-freeing-PI-data-in-unregister.patch @@ -0,0 +1,35 @@ +From a2d81faf75c1661d7a5977725731f481ba8de92d Mon Sep 17 00:00:00 2001 +From: Carlo Szelinsky +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 +--- + 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); diff --git a/target/linux/generic/pending-6.18/895-00b-net-pse-pd-guard-against-freed-PI-data-on-regulator-disable.patch b/target/linux/generic/pending-6.18/895-00b-net-pse-pd-guard-against-freed-PI-data-on-regulator-disable.patch new file mode 100644 index 00000000000..b11d493bac4 --- /dev/null +++ b/target/linux/generic/pending-6.18/895-00b-net-pse-pd-guard-against-freed-PI-data-on-regulator-disable.patch @@ -0,0 +1,62 @@ +From c4bd3afcf943491228e284cc6e68a145fcd07c2b Mon Sep 17 00:00:00 2001 +From: Carlo Szelinsky +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 +--- + 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; diff --git a/target/linux/generic/pending-6.18/896-02-net-pse-pd-fire-lifecycle-events-on-controller-regis.patch b/target/linux/generic/pending-6.18/896-02-net-pse-pd-fire-lifecycle-events-on-controller-regis.patch index 0a6cbc9ee6e..23676a00001 100644 --- a/target/linux/generic/pending-6.18/896-02-net-pse-pd-fire-lifecycle-events-on-controller-regis.patch +++ b/target/linux/generic/pending-6.18/896-02-net-pse-pd-fire-lifecycle-events-on-controller-regis.patch @@ -20,7 +20,7 @@ Tested-by: Jonas Jelonek --- 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); @@ -30,7 +30,7 @@ Tested-by: Jonas Jelonek 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) { @@ -38,5 +38,5 @@ Tested-by: Jonas Jelonek + PSE_UNREGISTERED, pcdev); + pse_flush_pw_ds(pcdev); - pse_release_pis(pcdev); if (pcdev->irq) + disable_irq(pcdev->irq); diff --git a/target/linux/generic/pending-6.18/896-03-net-phy-own-phydev-psec-via-PSE-notifier-and-remove-.patch b/target/linux/generic/pending-6.18/896-03-net-phy-own-phydev-psec-via-PSE-notifier-and-remove-.patch index b5a5aa2009f..a24c08dcae2 100644 --- a/target/linux/generic/pending-6.18/896-03-net-phy-own-phydev-psec-via-PSE-notifier-and-remove-.patch +++ b/target/linux/generic/pending-6.18/896-03-net-phy-own-phydev-psec-via-PSE-notifier-and-remove-.patch @@ -380,7 +380,7 @@ Tested-by: Jonas Jelonek 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);