]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
kernel: pse-pd: backport net fixes for unregister/isr
authorCarlo Szelinsky <github@szelinsky.de>
Sat, 18 Jul 2026 13:38:38 +0000 (15:38 +0200)
committerJonas Jelonek <jelonek.jonas@gmail.com>
Tue, 21 Jul 2026 16:59:25 +0000 (18:59 +0200)
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 <github@szelinsky.de>
Link: https://github.com/openwrt/openwrt/pull/22245
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
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 with mode: 0644]
target/linux/generic/pending-6.18/895-00a-net-pse-pd-disable-IRQ-before-freeing-PI-data-in-unregister.patch [new file with mode: 0644]
target/linux/generic/pending-6.18/895-00b-net-pse-pd-guard-against-freed-PI-data-on-regulator-disable.patch [new file with mode: 0644]
target/linux/generic/pending-6.18/896-02-net-pse-pd-fire-lifecycle-events-on-controller-regis.patch
target/linux/generic/pending-6.18/896-03-net-phy-own-phydev-psec-via-PSE-notifier-and-remove-.patch

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 (file)
index 0000000..aa3ee7c
--- /dev/null
@@ -0,0 +1,80 @@
+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, &notifs_mask, pcdev->nr_lines) treats
+  &notifs_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, &notifs_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, &notifs_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 (file)
index 0000000..4e99d5e
--- /dev/null
@@ -0,0 +1,35 @@
+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);
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 (file)
index 0000000..b11d493
--- /dev/null
@@ -0,0 +1,62 @@
+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;
index 0a6cbc9ee6e0e2d54ac4cbaf86f9eeb73b4ceffb..23676a00001ed0824bc1530eafb8678cb9a75519 100644 (file)
@@ -20,7 +20,7 @@ Tested-by: Jonas Jelonek <jelonek.jonas@gmail.com>
 
 --- 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 <jelonek.jonas@gmail.com>
        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 <jelonek.jonas@gmail.com>
 +                                   PSE_UNREGISTERED, pcdev);
 +
        pse_flush_pw_ds(pcdev);
-       pse_release_pis(pcdev);
        if (pcdev->irq)
+               disable_irq(pcdev->irq);
index b5a5aa2009fc2a13140de065590cd242684fb802..a24c08dcae2c688225b81367ca8c055fe080213d 100644 (file)
@@ -380,7 +380,7 @@ Tested-by: Jonas Jelonek <jelonek.jonas@gmail.com>
                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);