mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Petr Oros <poros@redhat.com>
To: netdev@vger.kernel.org
Cc: ivecera@redhat.com, Petr Oros <poros@redhat.com>,
	Vadim Fedorenko <vadim.fedorenko@linux.dev>,
	Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>,
	Jiri Pirko <jiri@resnulli.us>,
	Milena Olech <milena.olech@intel.com>,
	"David S. Miller" <davem@davemloft.net>,
	Michal Michalik <michal.michalik@intel.com>,
	linux-kernel@vger.kernel.org
Subject: [PATCH net 1/2] dpll: fix NULL deref in dpll_device_ops() during teardown race
Date: Sat, 16 May 2026 21:13:16 +0200	[thread overview]
Message-ID: <20260516191317.1005612-2-poros@redhat.com> (raw)
In-Reply-To: <20260516191317.1005612-1-poros@redhat.com>

When a dpll device driver (zl3073x) is unloaded via rmmod while a
foreign driver (ice) still holds pins on it via
dpll_pin_on_pin_register(), dpll_device_unregister() empties
dpll->registration_list but the dpll object stays alive on ice's
refcount. A subsequent PIN_DELETED
notification on ice's pin reaches dpll_cmd_pin_get_one() which calls
dpll_device_ops() on the still-indexed but half-dead dpll;
dpll_device_registration_first() returns NULL and the accessor
dereferences ops at offset 0x10 (struct dpll_device_registration::ops):

  CPU A (zl3073x rmmod)            CPU B (ice workqueue)
  ---------------------            ---------------------
  dpll_device_unregister():
    drop reg from
    dpll->registration_list
                                   dpll_pin_on_pin_unregister
                                     dpll_pin_delete_ntf
                                       dpll_cmd_pin_get_one
                                         dpll_device_ops()
                                           registration_first() = NULL
                                           NULL deref @ 0x10

  WARNING: CPU: 27 PID: 972 at drivers/dpll/dpll_core.c:1072 dpll_device_ops+0x20/0x30
  Workqueue: ice_dpll_wq ice_dpll_pin_notify_work [ice]
  RIP: 0010:dpll_device_ops+0x20/0x30
  Call Trace:
   <TASK>
   ? __warn+0x84/0x140
   ? dpll_device_ops+0x20/0x30
   ? report_bug+0x16b/0x180
   ? handle_bug+0x3c/0x70
   ? exc_invalid_op+0x14/0x70
   ? asm_exc_invalid_op+0x16/0x20
   ? dpll_device_ops+0x20/0x30
   dpll_cmd_pin_get_one+0x303/0x490
   dpll_pin_event_send+0x93/0x140
   dpll_pin_on_pin_unregister+0x45/0xe0
   ice_dpll_pin_notify_work+0x7b/0x150 [ice]
   process_one_work+0x188/0x3b0
   worker_thread+0x2ef/0x410
   kthread+0x122/0x240
   ret_from_fork+0x28/0x50
   </TASK>
  ---[ end trace 0000000000000000 ]---
  BUG: kernel NULL pointer dereference, address: 0000000000000010
  RIP: 0010:dpll_device_ops+0x24/0x30
  Call Trace:
   dpll_cmd_pin_get_one+0x303/0x490
   dpll_pin_event_send+0x93/0x140
   dpll_pin_on_pin_unregister+0x45/0xe0
   ice_dpll_pin_notify_work+0x7b/0x150 [ice]
   process_one_work+0x188/0x3b0
   worker_thread+0x2ef/0x410
   kthread+0x122/0x240
   ret_from_fork+0x28/0x50

dpll_lock serializes the two paths but cannot reorder them: ice's work
was queued before zl3073x took the lock. "Empty registration_list on a
still-indexed dpll" is a legitimate transient state caused by
peer-driver refcounting; the accessors must tolerate it.

Drop the WARN_ON in dpll_device_registration_first(), make dpll_priv()
and dpll_device_ops() return NULL on the empty-list state, and bail
out cleanly in dpll_cmd_pin_get_one() when the first dpll the pin
references is already gone.

In-tree doit/dumpit callers of dpll_priv() / dpll_device_ops() obtain
their dpll via dpll_pre_doit() -> dpll_device_get_by_id(), which only
returns DPLL_REGISTERED devices; dpll_device_unregister() clears that
mark only after observing list_empty(&dpll->registration_list), so
those callers cannot see the new NULL return. The only path that can
is dpll_cmd_pin_get_one() reached from dpll_pin_event_send(), gated
here.

Fixes: 9431063ad323 ("dpll: core: Add DPLL framework base functions")
Signed-off-by: Petr Oros <poros@redhat.com>
---
 drivers/dpll/dpll_core.c    | 12 ++++++------
 drivers/dpll/dpll_netlink.c |  6 ++++++
 2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index cbb635db43210f..4a058b46c69d4f 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -1060,12 +1060,8 @@ EXPORT_SYMBOL_GPL(dpll_pin_ref_sync_pair_add);
 static struct dpll_device_registration *
 dpll_device_registration_first(struct dpll_device *dpll)
 {
-	struct dpll_device_registration *reg;
-
-	reg = list_first_entry_or_null((struct list_head *)&dpll->registration_list,
-				       struct dpll_device_registration, list);
-	WARN_ON(!reg);
-	return reg;
+	return list_first_entry_or_null((struct list_head *)&dpll->registration_list,
+					struct dpll_device_registration, list);
 }
 
 void *dpll_priv(struct dpll_device *dpll)
@@ -1073,6 +1069,8 @@ void *dpll_priv(struct dpll_device *dpll)
 	struct dpll_device_registration *reg;
 
 	reg = dpll_device_registration_first(dpll);
+	if (!reg)
+		return NULL;
 	return reg->priv;
 }
 
@@ -1081,6 +1079,8 @@ const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll)
 	struct dpll_device_registration *reg;
 
 	reg = dpll_device_registration_first(dpll);
+	if (!reg)
+		return NULL;
 	return reg->ops;
 }
 
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 0ff1658c2dc1ba..8e7e61982b867c 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -682,6 +682,12 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
 	ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
 	ASSERT_NOT_NULL(ref);
 
+	/* The first dpll the pin references may be torn down while still
+	 * pinned by foreign-driver refs; drop the notification cleanly.
+	 */
+	if (!dpll_device_ops(ref->dpll))
+		return -ENODEV;
+
 	ret = dpll_msg_add_pin_handle(msg, pin);
 	if (ret)
 		return ret;
-- 
2.53.0


  reply	other threads:[~2026-05-16 19:13 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-16 19:13 [PATCH net 0/2] dpll: fix two teardown races on E825-C Petr Oros
2026-05-16 19:13 ` Petr Oros [this message]
2026-05-21  2:19   ` [PATCH net 1/2] dpll: fix NULL deref in dpll_device_ops() during teardown race Jakub Kicinski
2026-05-21  2:19   ` Jakub Kicinski
2026-05-16 19:13 ` [PATCH net 2/2] dpll: filter pin->dpll_refs by cookie in dpll_pin_on_pin_unregister Petr Oros
2026-05-21  2:19   ` Jakub Kicinski

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=20260516191317.1005612-2-poros@redhat.com \
    --to=poros@redhat.com \
    --cc=arkadiusz.kubalewski@intel.com \
    --cc=davem@davemloft.net \
    --cc=ivecera@redhat.com \
    --cc=jiri@resnulli.us \
    --cc=linux-kernel@vger.kernel.org \
    --cc=michal.michalik@intel.com \
    --cc=milena.olech@intel.com \
    --cc=netdev@vger.kernel.org \
    --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®