From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=109.224.244.118 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794181158; b=XUH1p4u4rB/vdDQG/T+Zxv4M3EiikibLI91QUXXvzpSWxZOisu/WV7UbQ/7oPNlmM7x0 XsDW76wH+fsvfALrwO8PWwv5q3XHAArLreWfJHFa116y8igl8GTpjMqF/SKc73uNmKIaz 4se1TIpyEHKCxaylrTZWFmYXt2bR6kgXlQVQiY2j5njknEthBOT9iBApSikRosEC/0Cz0 ERAf+E1VU4eB/I8ZnFlS0AO5eDg41/G4FsPvfgyOfg8gfX4LL9QZK64r1Vk6yiJ/Lnp51 0JoXhakrZ2X81wimdFJAQEBLHP9nMxE6pWkp728W3YI+KqZ+d3qmfiOAmde8d+u5GUQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794181158; h=DMARC-Filter:DKIM-Signature:Date:To:From:Cc:Subject:Message-ID: Feedback-ID:X-Pm-Message-ID:MIME-Version:Content-Type: Content-Transfer-Encoding; bh=Ir3LIg5xY40r450i57J61cS++CiSUUAMG8r460r1ElE=; b=mKBFYHFZjOIiNNpUY+9eROQV942WMKmoPoSics4ThOrRG4w/njhhmVfXAmLZScasY20+ FOWrXuUEvY8l8VsGWEdIBg3fKEqqsb1rOypH/+nSDf4Q+HKip7cuMmiI4071P5j5vmApp QCrHXMVCpDOvAlSOb8FY04lr+pG7S+PKoNg+BUU+DXMnU4STgPXSdXgrPEaTKDajlN2w6 lkL1HCKJ1Kdhj8t+s/H5B50RzxOgyp3RL/y2XsP+MiT81VGhCQU6uETQ6Si31Wt2gDD2K wg40bkfOz2ZTsPhF5c+OIxY4UhOl5fvdW9XhwmLA49MVstItHDbhkdDV+JDw2xUtSLA== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=modelmiser.com; spf=pass smtp.mailfrom=modelmiser.com; dkim=pass header.d=modelmiser.com header.i=@modelmiser.com header.a=rsa-sha256 header.s=protonmail header.b="TT5C/KAx"; arc=none smtp.remote-ip=109.224.244.118 Received: from mail-244118.protonmail.ch (mail-244118.protonmail.ch [109.224.244.118]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9A37B606A31C for ; Sun, 8 Nov 2026 23:39:14 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 9A37B606A31C Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=modelmiser.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=modelmiser.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=modelmiser.com header.i=@modelmiser.com header.a=rsa-sha256 header.s=protonmail header.b=TT5C/KAx DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=modelmiser.com; s=protonmail; t=1794181152; x=1794440352; bh=Ir3LIg5xY40r450i57J61cS++CiSUUAMG8r460r1ElE=; h=Date:To:From:Cc:Subject:Message-ID:Feedback-ID:From:To:Cc:Date: Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=TT5C/KAxygt1mMOtcOxLUZAKJGyE5SRFga6WAtgwLwp7O7UP+rE2kFgQZaWeD50R8 bU9ujX+baU+wU7tc3F5S9N8sSmejStBI2/e9CPq4RTNGbKUTklHx1qGI83znRQdefM UUEYTTGihenrwIVX0Guc+9vaY/Fp+EoB45E7xO9JBAQFV2//iDxzVIslQK0Vr/5AOr cO30syyhCeL/kqGtD5ZH9arOnrEZPxf+kzcQBeVJ2K8amrMZ9xEYke1U5nFpEoXDUQ gtcHczAf4NgKA/p4XtWn++44A2Kl90VZKO4uHTXlSDcBL5GIj8QSnJ4Pd0KYh6KJFp fn6a6W9d4wMNA== Date: Sun, 08 Nov 2026 23:39:06 +0000 To: "Rafael J . Wysocki" From: Chad Aldreda Cc: Len Brown , Chen Yu , linux-acpi@vger.kernel.org, stable@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 1/2] ACPI: pfr_telemetry: keep the state until the last file is closed Message-ID: <20261108233900.4043941-3-ca@modelmiser.com> Feedback-ID: 232753253:user:proton X-Pm-Message-ID: 318ef2ea009b2e267e14cbe7369708688f707be8 X-Mailing-List: linux-acpi@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable struct pfrt_log_device is allocated with devm_kzalloc() and embeds the miscdevice, so it is freed when the driver unbinds. A file that is open on the node outlives the unbind and nothing holds a reference for it: the next ioctl() or mmap() on that file uses freed memory. Unbinding the driver needs root. Allocate the structure with kzalloc() and reference count it: open() takes a reference, under misc_mtx, which misc_deregister() also takes, release() drops it, and a devm action drops the driver's own. The structure also holds a reference on the parent device it uses. A call to ioctl() or mmap() that starts after remove() returns fails with -ENODEV; one already running finishes against state that is still valid. The problem was found with a Coccinelle script that reports device nodes in devm memory [1]. The fix, the reproducer and this message were written with an AI assistant (Claude) and checked by hand. The bug was reproduced and the fix tested in QEMU with KASAN, using an SSDT whose _DSM stands in for the firmware: ioctl() and mmap() worked before an unbind and, on a file held open across it, failed with -ENODEV. A call racing with the unbind was not tested, nor was hardware with this firmware interface. Link: https://lore.kernel.org/r/20261106193517.3267013-2-ca@modelmiser.com = [1] Fixes: b0013e037a8b ("ACPI: Introduce Platform Firmware Runtime Telemetry d= river") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Chad Aldreda --- The Coccinelle script reports a devm_k*() allocation when it, or an address inside it, is passed to misc_register() or a similar registration function in the same function, or when the allocated struct embeds such an object. It reports this driver for the misc_register() of &pfrt_log_dev->miscdev. Reproduced in QEMU 10.2.4 (q35, KASAN, booted with kasan_multi_shot). An SSDT adds a device with _HID INTC1081 and a _DSM that returns valid packages, and memmap=3D reserves memory for the log buffer. A process opens /dev/acpi_pfr_telemetry0, uses SET_INFO, GET_INFO, GET_DATA_INFO and mmap(), and unbinds the driver through sysfs with the file open. Repeating those calls gives, without this patch, 15 KASAN reports in pfrt_log_ioctl(), set_pfrt_log_level() and get_pfrt_log_data_info(). With it there are none. All the calls succeed before the unbind and return -ENODEV after it, a mapping made before the unbind was still readable after it, and the same sequence behaves the same after a bind. Unbinding with the file already closed, or never opened, gives no reports either. No lock is added. A call that passed the removed check can run during the unbind, against state and a parent device that are still valid; the driver has never serialized calls from different files. Mappings of the firmware log buffer outlive the unbind, as they did before. kzalloc() rather than kzalloc_obj(), which first appears in v7.0, for the stable trees. drivers/acpi/pfr_telemetry.c | 55 +++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/pfr_telemetry.c b/drivers/acpi/pfr_telemetry.c index 2387376832a1..5ab8baabcc60 100644 --- a/drivers/acpi/pfr_telemetry.c +++ b/drivers/acpi/pfr_telemetry.c @@ -15,10 +15,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -62,6 +64,8 @@ struct pfrt_log_device { =09struct pfrt_log_info info; =09struct device *parent_dev; =09struct miscdevice miscdev; +=09struct kref kref; +=09bool removed; }; =20 /* pfrt_guid is the parameter for _DSM method */ @@ -247,6 +251,9 @@ static long pfrt_log_ioctl(struct file *file, unsigned = int cmd, unsigned long ar =09void __user *p; =09int ret =3D 0; =20 +=09if (READ_ONCE(pfrt_log_dev->removed)) +=09=09return -ENODEV; + =09p =3D (void __user *)arg; =20 =09switch (cmd) { @@ -303,14 +310,17 @@ pfrt_log_mmap(struct file *file, struct vm_area_struc= t *vma) =09phys_addr_t base_addr; =09int ret; =20 +=09pfrt_log_dev =3D to_pfrt_log_dev(file); + +=09if (READ_ONCE(pfrt_log_dev->removed)) +=09=09return -ENODEV; + =09if (vma->vm_flags & VM_WRITE) =09=09return -EROFS; =20 =09/* changing from read to write with mprotect is not allowed */ =09vm_flags_clear(vma, VM_MAYWRITE); =20 -=09pfrt_log_dev =3D to_pfrt_log_dev(file); - =09ret =3D get_pfrt_log_data_info(&info, pfrt_log_dev); =09if (ret) =09=09return ret; @@ -337,8 +347,39 @@ pfrt_log_mmap(struct file *file, struct vm_area_struct= *vma) =09return 0; } =20 +static void pfrt_log_free(struct kref *kref) +{ +=09struct pfrt_log_device *pfrt_log_dev =3D +=09=09container_of(kref, struct pfrt_log_device, kref); + +=09put_device(pfrt_log_dev->parent_dev); +=09kfree(pfrt_log_dev); +} + +static void pfrt_log_put(void *data) +{ +=09struct pfrt_log_device *pfrt_log_dev =3D data; + +=09kref_put(&pfrt_log_dev->kref, pfrt_log_free); +} + +/* misc_open() holds misc_mtx here, and misc_deregister() takes it. */ +static int pfrt_log_open(struct inode *inode, struct file *file) +{ +=09kref_get(&to_pfrt_log_dev(file)->kref); +=09return 0; +} + +static int pfrt_log_release(struct inode *inode, struct file *file) +{ +=09pfrt_log_put(to_pfrt_log_dev(file)); +=09return 0; +} + static const struct file_operations acpi_pfrt_log_fops =3D { =09.owner=09=09=3D THIS_MODULE, +=09.open=09=09=3D pfrt_log_open, +=09.release=09=3D pfrt_log_release, =09.mmap=09=09=3D pfrt_log_mmap, =09.unlocked_ioctl =3D pfrt_log_ioctl, =09.llseek=09=09=3D noop_llseek, @@ -349,6 +390,7 @@ static void acpi_pfrt_log_remove(struct platform_device= *pdev) =09struct pfrt_log_device *pfrt_log_dev =3D platform_get_drvdata(pdev); =20 =09misc_deregister(&pfrt_log_dev->miscdev); +=09WRITE_ONCE(pfrt_log_dev->removed, true); } =20 static void pfrt_log_put_idx(void *data) @@ -373,10 +415,16 @@ static int acpi_pfrt_log_probe(struct platform_device= *pdev) =09=09return -ENODEV; =09} =20 -=09pfrt_log_dev =3D devm_kzalloc(&pdev->dev, sizeof(*pfrt_log_dev), GFP_KE= RNEL); +=09pfrt_log_dev =3D kzalloc(sizeof(*pfrt_log_dev), GFP_KERNEL); =09if (!pfrt_log_dev) =09=09return -ENOMEM; =20 +=09kref_init(&pfrt_log_dev->kref); +=09pfrt_log_dev->parent_dev =3D get_device(&pdev->dev); +=09ret =3D devm_add_action_or_reset(&pdev->dev, pfrt_log_put, pfrt_log_dev= ); +=09if (ret) +=09=09return ret; + =09ret =3D ida_alloc(&pfrt_log_ida, GFP_KERNEL); =09if (ret < 0) =09=09return ret; @@ -387,7 +435,6 @@ static int acpi_pfrt_log_probe(struct platform_device *= pdev) =09=09return ret; =20 =09pfrt_log_dev->info.log_revid =3D PFRT_DEFAULT_REV_ID; -=09pfrt_log_dev->parent_dev =3D &pdev->dev; =20 =09pfrt_log_dev->miscdev.minor =3D MISC_DYNAMIC_MINOR; =09pfrt_log_dev->miscdev.name =3D devm_kasprintf(&pdev->dev, GFP_KERNEL, --=20 2.43.0