mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Ivan Vecera <ivecera@redhat.com>
To: netdev-bot+sashiko@kernel.org, Jakub Kicinski <kuba@kernel.org>
Cc: netdev@vger.kernel.org, Chris.duQuesnay@microchip.com,
	arkadiusz.kubalewski@intel.com, jiri@resnulli.us,
	min.li@microchip.com, pabeni@redhat.com, poros@redhat.com,
	richardcochran@gmail.com, vadim.fedorenko@linux.dev,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH net-next 1/6] dpll: zl3073x: notify sibling pin when shared output config changes
Date: Wed, 30 Sep 2026 16:24:40 +0200	[thread overview]
Message-ID: <cd768cab-78b7-4269-9e86-2bd49d261e1c@redhat.com> (raw)
In-Reply-To: <d631d2ed-63b5-4785-80ae-b16deb3edd1a@redhat.com>

On 9/30/26 9:49 AM, Ivan Vecera wrote:
>> [Severity: High]
>> Can sibling->dpll_pin be NULL, or the sibling already freed, by the time
>> it is dereferenced here?
>>
>> zl3073x_dpll_output_pin_sibling_get() returns any output pin on
>> zldpll->pins with the matching id. It does not check that
>> sibling->dpll_pin is still set. The lookup runs under zldpll->lock, but
>> the dereference happens after mutex_unlock().
>>
>> The teardown path does not take zldpll->lock:
>>
>> drivers/dpll/zl3073x/dpll.c:zl3073x_dpll_pins_unregister() {
>>     list_for_each_entry_safe(pin, next, &zldpll->pins, list) {
>>         zl3073x_dpll_pin_unregister(pin);
>>         list_del(&pin->list);
>>         zl3073x_dpll_pin_free(pin);
>>     }
>> }
>>
>> zl3073x_dpll_pin_unregister() ends with:
>>
>>     dpll_pin_put(pin->dpll_pin, &pin->tracker);
>>     pin->dpll_pin = NULL;
>>
>> and dpll_pin_put() has already dropped dpll_lock at that point.
>>
>> zl3073x_dpll_pins_register() uses list_add(), so the N pin (2k+1) sits
>> before its P pin (2k) on the list. That means N is torn down while P is
>> still registered and reachable over netlink.
>>
>> One possible interleaving:
>>
>> CPU1 (devlink reload, flash_prepare or unbind)
>> zl3073x_dev_stop()
>>    zl3073x_dpll_unregister()
>>      zl3073x_dpll_pins_unregister()
>>        dpll_pin_put(N)          /* dpll_lock released */
>>
>> CPU0 (DPLL_CMD_PIN_SET on P, dpll_lock held)
>> dpll_pin_freq_set()
>>    zl3073x_dpll_output_pin_frequency_set(P)
>>      mutex_lock(&zldpll->lock)
>>      sibling = zl3073x_dpll_output_pin_sibling_get(P)  /* finds N */
>>      mutex_unlock(&zldpll->lock)
>>
>> CPU1
>>        N->dpll_pin = NULL
>>        list_del(&N->list)
>>        zl3073x_dpll_pin_free(N)
>>
>> CPU0
>>      __dpll_pin_change_ntf(sibling->dpll_pin)
>>
>> In that case, would __dpll_pin_change_ntf() get NULL and oops on
>> pin->id and pin->clock_id in dpll_pin_notify()? Or would CPU0 read the
>> freed zl3073x_dpll_pin?
>>
>> The list walk in zl3073x_dpll_output_pin_sibling_get() can also run
>> while list_del() is in progress, because the writer never takes
>> zldpll->lock.
>>
>> zl3073x_dpll_output_pin_frequency_set() and
>> zl3073x_dpll_output_pin_phase_adjust_set() have the same pattern.
>>
>> The later patch "dpll: zl3073x: add PTP periodic output support" also
>> calls zl3073x_dpll_output_pin_sibling_get() from
>> zl3073x_dpll_ptp_enable() without holding zldpll->lock at all.
> 
> This cannot happen; the notification is already serialized against pin
> teardown.
> 
> The output setters (esync_set / frequency_set / phase_adjust_set) run
> with dpll_lock held: the dpll core invokes the pin ops under dpll_lock,
> and they use __dpll_pin_change_ntf(), whose contract is exactly "caller
> must hold dpll_lock" (dpll_netlink.c: lockdep_assert_held(&dpll_lock),
> "suitable for use inside pin callbacks which are already invoked under
> dpll_lock"). So sibling_get() and the __dpll_pin_change_ntf() call - even
> though it runs after mutex_unlock(&zldpll->lock) - are all covered by
> dpll_lock.
> 
> Pin teardown frees the sibling via zl3073x_dpll_pin_unregister() ->
> dpll_pin_unregister(), which takes dpll_lock. While a setter holds
> dpll_lock for the whole callback, teardown cannot even reach
> dpll_pin_unregister(N), let alone the following list_del()/kfree(). The
> proposed interleaving (CPU1 freeing N while CPU0 notifies) is therefore
> impossible: CPU0 holds dpll_lock throughout.
> 
> The PTP enable() path (zl3073x_dpll_ptp_enable(), added in patch 6) does
> call sibling_get() outside dpll_lock, but it is serialized differently:
> zl3073x_dpll_unregister() unregisters the PTP clock *before* the pins
> (zl3073x_dpll_ptp_unregister() then zl3073x_dpll_pins_unregister()), and
> ptp_clock_unregister() quiesces in-flight enable() callbacks. No
> enable() can run while the pins are being freed.
> 

More thinking about it... and yes this can happen :-(
The pin removal from the list must be performed prior its unregistration
and also the list management has to be protected by zldpll->lock.

Will fix and send as bugfix to net branch with proper Fixes: tags.

Thanks,
Ivan


  reply	other threads:[~2026-09-30 14:24 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-28 18:55 [PATCH net-next 0/6] dpll: zl3073x: add PTP periodic output support Ivan Vecera
2026-09-28 18:55 ` [PATCH net-next 1/6] dpll: zl3073x: notify sibling pin when shared output config changes Ivan Vecera
2026-09-30  6:57   ` netdev-bot+sashiko
2026-09-30  7:49     ` Ivan Vecera
2026-09-30 14:24       ` Ivan Vecera [this message]
2026-09-28 18:55 ` [PATCH net-next 2/6] dpll: zl3073x: add GPO support for output pins Ivan Vecera
2026-09-30  6:57   ` netdev-bot+sashiko
2026-09-30  8:28     ` Ivan Vecera
2026-09-28 18:55 ` [PATCH net-next 3/6] dpll: zl3073x: allow enabling/disabling " Ivan Vecera
2026-09-30  6:57   ` netdev-bot+sashiko
2026-09-30 10:13     ` Ivan Vecera
2026-09-28 18:55 ` [PATCH net-next 4/6] dpll: zl3073x: consolidate pin capabilities into bitmap Ivan Vecera
2026-09-28 18:55 ` [PATCH net-next 5/6] dpll: zl3073x: factor out output pin frequency helper Ivan Vecera
2026-09-30  6:57   ` netdev-bot+sashiko
2026-09-30 10:43     ` Ivan Vecera
2026-09-28 18:55 ` [PATCH net-next 6/6] dpll: zl3073x: add PTP periodic output support Ivan Vecera
2026-09-30  6:57   ` netdev-bot+sashiko

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=cd768cab-78b7-4269-9e86-2bd49d261e1c@redhat.com \
    --to=ivecera@redhat.com \
    --cc=Chris.duQuesnay@microchip.com \
    --cc=arkadiusz.kubalewski@intel.com \
    --cc=jiri@resnulli.us \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=min.li@microchip.com \
    --cc=netdev-bot+sashiko@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=poros@redhat.com \
    --cc=richardcochran@gmail.com \
    --cc=vadim.fedorenko@linux.dev \
    /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

all inboxes | Powered by JetHome®