Netdev List
 help / color / mirror / Atom feed
From: Simon Horman <horms@kernel.org>
To: github@szelinsky.de
Cc: 'Simon Horman' <horms@kernel.org>,
	o.rempel@pengutronix.de, kory.maincent@bootlin.com,
	andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
	kuba@kernel.org, pabeni@redhat.com, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH net 1/2] net: pse-pd: disable IRQ before freeing PI data in unregister
Date: Wed, 27 May 2026 13:55:53 +0100	[thread overview]
Message-ID: <20260527125551.2425511-3-horms@kernel.org> (raw)
In-Reply-To: <20260524223306.2570676-2-github@szelinsky.de>

From: 'Simon Horman' <horms@kernel.org>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
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.

> diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
> index 69dbdbde9d71a..1f27884dd4d70 100644
> --- 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_register);
>  void pse_controller_unregister(struct pse_controller_dev *pcdev)
>  {
>  	pse_flush_pw_ds(pcdev);
>  	if (pcdev->irq)
>  		disable_irq(pcdev->irq);

Simon says: The issue flagged below seems to warrant investigation
            within the context of this patch.

[Severity: High]
This is a pre-existing issue, but does leaving pse_flush_pw_ds() before
disable_irq() risk a use-after-free of a regulator during an interrupt?

pse_flush_pw_ds() drops references to power domains, which can trigger
__pse_pw_d_release() and free the regulator via regulator_put(pw_d->supply).

If an interrupt arrives after this but before the IRQ is disabled:

pse_isr()
  _pse_pi_disable()
    pse_pw_d_retry_power_delivery()
      regulator_request_power_budget(pw_d->supply, ...)

Could this access the freed regulator? Should disable_irq() be the very
first operation in the teardown sequence?

> +	pse_release_pis(pcdev);
>  	cancel_work_sync(&pcdev->ntf_work);

Simon says: Although this is a pre-exiting issue, it does seem to
            warrant investigation within the context of this patch-set.

[Severity: High]
This isn't a bug introduced by this patch, but can calling
cancel_work_sync() after pse_release_pis() cause an indirect
use-after-free of pcdev->pi?

The worker blocks during cancel_work_sync(), and it can call:

pse_send_ntf_worker()
  pse_control_put(psec)
    __pse_control_release()

Since __pse_control_release() explicitly dereferences
psec->pcdev->pi[psec->id].admin_state_enabled, can the worker execute this
release path while or after pcdev->pi is freed by pse_release_pis()?

>  	kfifo_free(&pcdev->ntf_fifo);
>  	mutex_lock(&pse_list_mutex);
>  	list_del(&pcdev->list);
>  	mutex_unlock(&pse_list_mutex);
>  }

Simon says: I believe the remaining issues should be investigated
            in the context of possible follow-up or separate bug
	    fixes.

[Severity: High]
This is a pre-existing issue, but does removing the device from the global
list at the very end of unregister allow concurrent lookups to access
freed memory?

The device remains discoverable via of_pse_control_get() while its
resources are actively being destroyed. If a concurrent thread calls
of_pse_control_get(), it will find the pcdev and call
pse_control_get_internal(), which accesses pcdev->pi[index].

Because pse_release_pis() has already freed pcdev->pi earlier in this
sequence, could this cause a use-after-free?

[Severity: High]
This is a pre-existing issue, but does the unregistration sequence lack
synchronization with external pse_control consumers?

of_pse_control_get() provides a pse_control struct to consumers (like PHY
drivers), using try_module_get() but without incrementing the device
reference count or creating strict device links.

When a PSE controller is unbound, pse_controller_unregister() synchronously
frees pcdev->pi. However, external clients may still hold struct
pse_control references.

When these clients are later removed, they call:

pse_control_put(psec)
  __pse_control_release()
    dereferences psec->pcdev->pi[psec->id]

Since pcdev->pi was already freed, does this cause a use-after-free on
client teardown?

[Severity: High]
This is a pre-existing issue, but does the devm teardown sequence free
internal structures while the PSE PI regulators remain fully accessible?

pse_controller_register() registers regulators using
devm_regulator_register(). Afterwards, devm_pse_controller_register()
registers its own release callback.

During driver unbind, the LIFO devm sequence means
devm_pse_controller_release() runs first, freeing pcdev->pi via
pse_release_pis().

However, the regulators are still active and visible in sysfs until the
subsequent devm teardown phase unregisters them. A concurrent sysfs access
(e.g., reading regulator state) during this window will invoke regulator
operations like pse_pi_is_enabled(), which dereference the already freed
pcdev->pi[id].

Could this result in a use-after-free?

[Severity: High]
This is a pre-existing issue, but is the globally shared and
reference-counted struct pse_power_domain object incorrectly tied to the
lifetime of the device that allocated it?

devm_pse_alloc_pw_d() allocates pw_d using devm_kzalloc(). The memory
lifetime is therefore bound to the registering PSE controller. This pw_d is
added to the global pse_pw_d_map and uses kref for reference counting.

pse_register_pw_ds() allows other PSE controllers to discover and share
this pw_d by incrementing its kref.

If the device that originally created pw_d unbinds, devm will unilaterally
free the pw_d memory, ignoring the kref counts held by other devices. Any
subsequent access by those other devices will result in a use-after-free.

The __pse_pw_d_release() function seems to omit kfree(pw_d) because it
relies on devm, but doesn't this break the kref requirement?

  reply	other threads:[~2026-05-27 12:59 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-24 22:33 [PATCH net 0/2] net: pse-pd: fix use-after-free of PI array on controller teardown Carlo Szelinsky
2026-05-24 22:33 ` [PATCH net 1/2] net: pse-pd: disable IRQ before freeing PI data in unregister Carlo Szelinsky
2026-05-27 12:55   ` Simon Horman [this message]
2026-05-24 22:33 ` [PATCH net 2/2] net: pse-pd: guard against freed PI data on regulator disable Carlo Szelinsky
2026-05-27 12:24   ` Simon Horman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260527125551.2425511-3-horms@kernel.org \
    --to=horms@kernel.org \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=github@szelinsky.de \
    --cc=kory.maincent@bootlin.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=o.rempel@pengutronix.de \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox