From nobody Sun Feb 8 00:26:07 2026 Received: from mail-pl1-f171.google.com (mail-pl1-f171.google.com [209.85.214.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D668D28934F for ; Sat, 31 Jan 2026 10:13:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769854409; cv=none; b=Jje4a8eaZ0RFe9HfuGAE/I2k+ARa9lccV16EI7Fo5Y+wWovmQ1VahdyYxdmvUN+apn1us2yFWb0wDoMzlavg0z4szuWMnt//HItcO6eHE+a25mExP7D0FBgsdheurWclXL2UgPhAQrf8TKllIry1+k5knBT4dIMl0QkwwhaQLgg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769854409; c=relaxed/simple; bh=7HfRm2uQhSA/5klzskc0rfKbKQ/BIFWeaAMsPLMktrU=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=d1BG5+punCu9eVrJd6+arCTAsee8Mj6OQOOVH45lWGYONb6BvLx8//XeicUFvduKE8GCQAOixe6nyAesqh3cHjNCNy0SA+9NLZ26AjFourfPQNSUs2gF7WIlHw6kLvAUxw8G0g6Ysh3KuWgTuJWfKQlU3hEzL+1f60eXLVG1I+8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=URleIlZc; arc=none smtp.client-ip=209.85.214.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="URleIlZc" Received: by mail-pl1-f171.google.com with SMTP id d9443c01a7336-2a07fac8aa1so21725345ad.1 for ; Sat, 31 Jan 2026 02:13:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769854406; x=1770459206; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=uhZUDYbj88T1m+i7y1ui9FHryoXdNDmT3b+H4WcmQeM=; b=URleIlZcZrCxo8OcEW9JPb/Chozzd0GKf3HTgmPb9B1ZZYb4m8ZFrP1V+a4Grg0nCx dzTlZ2Ftq/42WSzX4SFZ5R0okO4OmiGpdnjQIQIVgCFzAkc+R636dyfYexXkWoAgQKMx KYK6qLY7mjgU1ISfiD1Nda4dYNImXOk5nD8JHqNue+NmJIdUcmSv/m3WjM460OpqUPXq Wx/6YbX/hB77K2gv9dukihG7pgc37NW6jkN8UPwpOPQL+sK5SfY/O1UaE8fXEdcpUa/u FfZqiGFS15G20eiQbzPSoi8HWY2gWF+QdysR//BHZ9ur/LfA94yg3HB2yn5aHZiWi2lw PDSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769854406; x=1770459206; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=uhZUDYbj88T1m+i7y1ui9FHryoXdNDmT3b+H4WcmQeM=; b=UH2lOQZjyIbaPcIPxLtvcR87orrNvRmg+JQ4eYttJ/Q4jTlui0IPEoV5hQ5vMiad4+ I/7K71EAPgzv9y3mTdYPbm6mLUu4GtROGGCZLBBC8drGruv1rPXf0TtuEf31TP0XcADy BFeg/zYs2OqQlcLwQMeQrMkfQmdE4V1mxG1x9aEmRBoLh88lkQ+hLUEzAydVa+ypd80h JU+WtroKVXEtu0LuAD2EBbHB2HlVhGPGLgTHDclEQgHxxZf/DcqZtJyWjDcu4eRiZftV ScPMRYthZjW+/Xwvr3/U0yS1+EmWWZ18ikIGbVO7gN8Co9c4QmwCodCLAF4F40YNqeWz MsXg== X-Forwarded-Encrypted: i=1; AJvYcCW7ZikUWlQoYL9J3szrO4sy/C0MnH/ZPgXkwr38oFtr4JEXrzd764mEZqaUr93CZqTk+uB2u85xvPe6fP8=@vger.kernel.org X-Gm-Message-State: AOJu0YxzgE1g8v8Sfe4lsRmCq7eBKsiZlS1zytW0vWzOCNRv1JMuHLe8 9YnPyYKDeaUgRa8Zyp0nAkJcUvY1ROUFtoZDWCyAJe2xWrWXZ4sG3t51IrvWXfHU X-Gm-Gg: AZuq6aJfG6u7sHMFj413Pl695s5zevnm9eDF+Mh3ALeD4XkrgPwMopK/WFeg0U6Y8jV KWnW1dpVcLawFOT8EJ9PNn2It26kXNytWc8cIH3jDzhzkpDAOlDyE1D6gvAUkLgH8xso0mwmLg6 hhzlqCfqD4iO8uLtMkwhfvfPCOlIICHGms6aK4tJJSCUMt3OkbQUEG5zlzgoyUeul5fosSWW5y/ EbEVny7giwssPKNZJiXlz1BtAUbsIDrPiwD9CeHm8JlPl0y8LSYZmTiPDx+cd1uI/wywxhqyFFx LKuPP5wQ6xCKEprxnuQZHICI+smNutWHQ5WFrN3iD9gEYHi7lwySFxiCWCmNy7mI5s1ankWrCEp mja1zYjqwt8G1moQzgsQQO9NUaL3xe1kvItYVxf1eqdv3gpp2yk7gL8Gpwjx4nrjHotEOdm4G7f UPf3/tEPnOCkdQsqOiyidOd00gvXKhNDza5eQnfRztJnSvHn+9Lp7IlCSNRwLr3Q79/yTGXwSRJ 4HFYYlgJgCUSek0VolujfLtDu5qQDqVMwkxKesjkOyMpt+f38XI9N+IYrR58Y4= X-Received: by 2002:a17:902:c412:b0:295:9627:8cbd with SMTP id d9443c01a7336-2a8d8150724mr60494385ad.33.1769854406060; Sat, 31 Jan 2026 02:13:26 -0800 (PST) Received: from 2045D.localdomain (6.sub-75-226-63.myvzw.com. [75.226.63.6]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2a88b5d99eesm95905085ad.78.2026.01.31.02.13.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 31 Jan 2026 02:13:25 -0800 (PST) From: Gui-Dong Han To: rafael@kernel.org, pavel@kernel.org, lenb@kernel.org, gregkh@linuxfoundation.org, dakr@kernel.org Cc: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, baijiaju1990@gmail.com, Gui-Dong Han Subject: [PATCH] PM: sleep: wakeirq: harden dev_pm_clear_wake_irq() against races Date: Sat, 31 Jan 2026 18:12:54 +0800 Message-ID: <20260131101254.56423-1-hanguidong02@gmail.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" dev_pm_clear_wake_irq() currently uses a dangerous pattern where dev->power.wakeirq is read and checked for NULL outside the lock. If two callers invoke this function concurrently, both might see a valid pointer and proceed. This could result in a double-free when the second caller acquires the lock and tries to release the same object. Address this by using double-checked locking. This retains the performance benefit of avoiding the lock when dev->power.wakeirq is NULL, consistent with the original logic, but adds a necessary re-check after acquiring dev->power.lock. Additionally, use READ_ONCE() and WRITE_ONCE() to annotate the shared variable accesses to avoid data races as defined by the kernel documentation. Based on a quick scan of current users, I did not find an actual bug as drivers seem to rely on their own synchronization. However, since asynchronous usage patterns exist (e.g., in drivers/net/wireless/ti/wlcore), I believe a race is theoretically possible if the API is used less carefully in the future. This change hardens the API to be robust against such cases. Fixes: 4990d4fe327b ("PM / Wakeirq: Add automated device wake IRQ handling") Signed-off-by: Gui-Dong Han --- While studying wakeirq, I noticed dev_pm_clear_wake_irq() handles sequential re-entry (via the NULL check) but may lead to a double-free on concurrent calls. I considered whether we should simply document that concurrent calls are forbidden. However, since the double-check locking pattern is straightforward and adds negligible performance overhead (we still skip the lock in the NULL case), I believe hardening the API is the better approach. I also noticed comments for dev_pm_enable_wake_irq_check() and friends appear outdated. They claim "Caller must hold &dev->power.lock" and limit usage to rpm_suspend/resume, yet pm_runtime_force_suspend/resume() now call them lockless. While this usage appears safe due to the specific context, it conflicts with the comments. I can submit a follow-up patch to fix this doc drift but am unsure whether to simply remove the restriction text or complicate it with exceptions. Guidance is welcome. --- drivers/base/power/wakeirq.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c index 8aa28c08b289..acb520626195 100644 --- a/drivers/base/power/wakeirq.c +++ b/drivers/base/power/wakeirq.c @@ -30,7 +30,7 @@ static int dev_pm_attach_wake_irq(struct device *dev, str= uct wake_irq *wirq) return -EEXIST; } =20 - dev->power.wakeirq =3D wirq; + WRITE_ONCE(dev->power.wakeirq, wirq); device_wakeup_attach_irq(dev, wirq); =20 spin_unlock_irqrestore(&dev->power.lock, flags); @@ -83,15 +83,21 @@ EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq); */ void dev_pm_clear_wake_irq(struct device *dev) { - struct wake_irq *wirq =3D dev->power.wakeirq; + struct wake_irq *wirq =3D READ_ONCE(dev->power.wakeirq); unsigned long flags; =20 if (!wirq) return; =20 spin_lock_irqsave(&dev->power.lock, flags); + wirq =3D dev->power.wakeirq; + if (!wirq) { + spin_unlock_irqrestore(&dev->power.lock, flags); + return; + } + device_wakeup_detach_irq(dev); - dev->power.wakeirq =3D NULL; + WRITE_ONCE(dev->power.wakeirq, NULL); spin_unlock_irqrestore(&dev->power.lock, flags); =20 if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED) { --=20 2.43.0