Re: [PATCH net v2] dpll: fix NULL deref in dpll_device_ops() during teardown race

Vadim Fedorenko <[email protected]>
Newsgroups gmane.linux.network,gmane.linux.kernel
Message-ID <[email protected]>
On 13/08/2026 15:08, Petr Oros wrote:
> When the last owner of a dpll device unregisters while a foreign driver
> still holds a pin on it via dpll_pin_on_pin_register(), the dpll object
> stays alive with an empty registration list. A pin notification queued
> before the unregister (e.g. ice reacting to zl3073x_i2c removal) then
> walks pin->dpll_refs into dpll_device_ops(), which trips the WARN_ON and
> dereferences the missing registration. dpll_lock cannot help because the
> notification work was queued before the unregistering driver took the
> lock.
> 
> Treat the empty registration list as a legitimate transient state. Make
> dpll_priv() and dpll_device_ops() return NULL in that case and make
> every pin netlink path that resolves a device from a pin skip such
> dplls. dpll_cmd_pin_get_one() picks a ref with a live registration and
> returns -ENODEV when there is none, the pin dumpit skips such a pin
> instead of aborting the dump, dpll_msg_add_pin_dplls() and the
> frequency, esync, reference sync and phase adjust set paths skip dead
> refs, and dpll_pin_parent_device_set() validates the parent with
> dpll_device_get_by_id(). dpll_pin_register() is the last caller that
> dereferenced the device ops without a check, so move its frequency
> monitor validation under dpll_lock and tolerate a missing registration
> there as well.
> 
> The empty registration list is equivalent to a cleared DPLL_REGISTERED
> mark, both transitions happen under dpll_lock in dpll_device_register()
> and dpll_device_unregister(). A pin notification for a pin whose dplls
> are all gone is now dropped with -ENODEV instead of crashing, all
> callers in the core ignore that return value.
> 
>   WARNING: drivers/dpll/dpll_core.c:1092 at dpll_device_ops+0x24/0x40,
>   CPU#83: kworker/u576:3/23471
>   Modules linked in: ... ice ... zl3073x_i2c(-) ... zl3073x ...
>   Workqueue: ice_dpll_wq ice_dpll_pin_notify_work [ice]
>   RIP: 0010:dpll_device_ops+0x24/0x40
>   Call Trace:
>    <TASK>
>    dpll_cmd_pin_get_one+0x336/0x520
>    dpll_pin_event_send+0x82/0x140
>    dpll_pin_on_pin_unregister+0xbb/0x160
>    ice_dpll_pin_notify_work+0x1bc/0x1f0 [ice]
>    process_one_work+0x19e/0x370
>    worker_thread+0x1a6/0x310
>    kthread+0xe4/0x120
>    ret_from_fork+0x1a1/0x270
>    ret_from_fork_asm+0x1a/0x30
>    </TASK>
>   ---[ end trace 0000000000000000 ]---
>   BUG: kernel NULL pointer dereference, address: 0000000000000010
>   #PF: supervisor read access in kernel mode
>   #PF: error_code(0x0000) - not-present page
> 
> Fixes: 9431063ad323 ("dpll: core: Add DPLL framework base functions")
> Signed-off-by: Petr Oros <[email protected]>
> ---
> v2:
> - guard every path that resolves a device from a pin, not only the
>    first ref in dpll_cmd_pin_get_one(); skip half-dead refs in
>    dpll_msg_add_pin_dplls() and the set paths, select a live
>    representative ref and turn the pin dumpit -ENODEV into a per pin
>    skip (Jakub)
> - validate the parent device in dpll_pin_parent_device_set() via
>    dpll_device_get_by_id()
> - guard the frequency monitor validation in dpll_pin_register() and
>    perform it under dpll_lock, it was the only remaining unchecked
>    dereference of the device ops
> - drop patch 2/2, superseded by commit 32239d600236 ("dpll: fix stale
>    iteration in dpll_pin_on_pin_unregister()")
> 
> v1: https://lore.kernel.org/all/[email protected]/
> ---
>   drivers/dpll/dpll_core.c    | 24 +++++++++------
>   drivers/dpll/dpll_netlink.c | 59 ++++++++++++++++++++++++++++++++-----
>   2 files changed, 67 insertions(+), 16 deletions(-)
> 

Reviewed-by: Vadim Fedorenko <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.