From: 徐晗翔 <xuhx@hust.edu.cn>
To: rafael@kernel.org, maciej.wieczor-retman@intel.com,
pawel.chmielewski@intel.com
Cc: linux-acpi@vger.kernel.org, lenb@kernel.org,
acpica-devel@lists.linux.dev, linux-kernel@vger.kernel.org
Subject: ACPI: ACPICA: Reset can delete an event semaphore while another thread is blocked in Wait
Date: Tue, 10 Nov 2026 21:11:57 +0800 (GMT+08:00) [thread overview]
Message-ID: <2a71bbe6.22afa.1a1c5963447.Coremail.xuhx@hust.edu.cn> (raw)
In-Reply-To: <2026102727-data-sequel-ce41@gregkh>
[-- Attachment #1: Type: text/plain, Size: 5981 bytes --]
acpi_ex_system_reset_event() creates a new semaphore, deletes the old
one, and replaces obj_desc->event.os_semaphore without checking whether
a Wait is already blocked on the old pointer. A waiting thread takes
the old semaphore pointer, drops the ACPICA interpreter lock, and
blocks in down_timeout(); Reset can then call
acpi_os_delete_semaphore(), whose BUG_ON(sem->first_waiter) and
kfree(sem) are performed without sem->lock. If the waiter is already
queued the BUG_ON fires; if the check passes before the waiter is
queued, the waiter uses freed memory. The BUG_ON was reproduced on a
KASAN 7.3 guest with a custom SSDT and a module driving concurrent
Wait/Reset, not just inferred from the source.
The detailed description:
* Test environment / affected version
Linux 7.3.0 (mainline 7.3 source, unmodified), KASAN build from
verification/build-kasan-7.3, x86_64 QEMU/KVM guest, 2 vCPU,
CONFIG_KASAN=y, CONFIG_BUG=y. A test SSDT was loaded with QEMU's
-acpitable option and provided a device \_SB.TST0 with _HID
"RAC0001", Event(EVT0), Method(MWAI) executing
Wait(EVT0, 0xFFFFFFFF), and Method(MRST) executing Reset(EVT0).
The module acpi_case5.ko calls MWAI from one kthread, waits one
second for the waiter to block, and calls MRST from the main thread.
Line numbers are from Linux 7.3.
* Mechanism
1. Wait. acpi_ex_system_wait_semaphore()
(drivers/acpi/acpica/exsystem.c:31-59) first tries
acpi_os_wait_semaphore(..., ACPI_DO_NOT_WAIT). On AE_TIME it
calls acpi_ex_exit_interpreter(), which drops
ACPI_MTX_NAMESPACE and ACPI_MTX_INTERPRETER
(drivers/acpi/acpica/exutils.c:93-110), then blocks in
acpi_os_wait_semaphore(semaphore, 1, timeout). The semaphore
argument is the old pointer read before the interpreter lock was
dropped.
2. Reset. acpi_ex_system_reset_event()
(drivers/acpi/acpica/exsystem.c:261-280) does:
status = acpi_os_create_semaphore(..., &temp_semaphore);
if (ACPI_SUCCESS(status)) {
(void)acpi_os_delete_semaphore(obj_desc->event.os_semaphore);
obj_desc->event.os_semaphore = temp_semaphore;
}
It does not take a per-event lock, does not check for waiters,
does not wake or cancel them, and ignores the delete result.
3. Delete. acpi_os_delete_semaphore()
(drivers/acpi/osl.c:1245-1271) contains the kernel's own TODO
about non-synchronized consumers:
BUG_ON(sem->first_waiter);
kfree(sem);
sem = NULL;
The BUG_ON is an unlocked read and kfree() follows immediately.
down_timeout() checks sem->count under sem->lock and, if it must
block, chains the waiter into sem->first_waiter under the same
lock (kernel/locking/semaphore.c:198-211 and 276-284).
acpi_os_delete_semaphore() does not take sem->lock, so "no waiter
at the check" does not imply "no waiter at kfree".
Two interleavings matter:
A. Waiter already queued. Wait has linked its waiter into
first_waiter and is sleeping; Reset deletes the old semaphore;
BUG_ON(first_waiter) fires. With CONFIG_BUG=y this is an oops,
and panic_on_oops=1 turns it into a panic.
B. Delete wins the check. BUG_ON sees NULL, kfree(old_sem) runs,
and only then does the waiter enter __down_timeout() and take
old_sem->lock / write old_sem->first_waiter. This is a UAF and
can corrupt the wait list or the raw spinlock.
Signal is not the primary trigger. acpi_ex_system_signal_event()
(exsystem.c:199-215) also reads obj_desc->event.os_semaphore, but it
runs under the ACPICA interpreter lock, so it cannot overlap Reset's
delete. After Reset replaces the pointer, Signal operates on the new
semaphore and can leave the old waiter without a wakeup; that is an
event-semantics/liveness bug, not the UAF above.
The utdelete path (drivers/acpi/acpica/utdelete.c:153-161) also
deletes an event semaphore, but it requires the operand object's
refcount to reach zero while a Wait is using it; that is a narrower
and less direct path, so it is not the main trigger here.
* Reproducer (attached: acpi_case5.c) and results on 7.3
The attached module is self-contained and uses the test SSDT
described above:
insmod /acpi_case5.ko
It resolves \_SB.TST0 with acpi_get_handle(), starts a kthread that
evaluates MWAI (Wait), sleeps one second, then evaluates MRST
(Reset) from the module's main thread. Actual serial output
(trimmed):
ACPI_CASE5: get_handle st=0
ACPI_CASE5: waiter evaluating MWAI
ACPI_CASE5: evaluating MRST while waiter blocked
kernel BUG at drivers/acpi/osl.c:1266!
Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
RIP: 0010:acpi_os_delete_semaphore+0x90/0xa0
Call Trace:
acpi_ex_system_reset_event+0xcd/0xf0
acpi_ex_opcode_1A_0T_0R
acpi_ps_parse_loop
acpi_ps_execute_method
acpi_ns_evaluate
acpi_evaluate_object
init_module+0xa0/0xff0 [acpi_case5]
This is the "waiter already queued" interleaving, not the UAF
interleaving B. It proves that Reset can reach delete with a
non-empty waiter list and that the BUG_ON is not a theoretical
assertion. It does not prove a user-triggerable path: the custom
SSDT and module are test harnesses.
* Suggested direction
Give the event object a lock or reference that covers Wait, Signal,
Reset, and destruction; make Reset either wait for/cancel all
waiters or replace the semaphore atomically while Wait revalidates
the pointer under the same lock. Do not use BUG_ON(first_waiter) as
a lifetime check: delete should take sem->lock, wake/cancel queued
waiters, and wait for them to leave before freeing. Alternatively,
use a completion or another primitive with a defined destroy
protocol. Report-first, no patch attached; happy to test any
proposal.
Please keep me in Cc; I am not subscribed to the lists.
Regards,
Hanxiang Xu
xuhx@hust.edu.cn
[-- Attachment #2: acpi_case5.c --]
[-- Type: text/plain, Size: 1233 bytes --]
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>
static acpi_handle test_handle;
static int acpi_wait_thread(void *data)
{
acpi_status st;
pr_info("ACPI_CASE5: waiter evaluating MWAI\n");
st = acpi_evaluate_object(test_handle, "MWAI", NULL, NULL);
pr_info("ACPI_CASE5: waiter returned st=%d\n", st);
return 0;
}
static int __init acpi_case5_init(void)
{
struct task_struct *t;
acpi_status st;
st = acpi_get_handle(NULL, "\\_SB.TST0", &test_handle);
pr_info("ACPI_CASE5: get_handle st=%d handle=%px\n", st, test_handle);
if (ACPI_FAILURE(st))
return -ENODEV;
t = kthread_run(acpi_wait_thread, NULL, "acpi_case5_wait");
if (IS_ERR(t))
return PTR_ERR(t);
msleep(1000);
pr_info("ACPI_CASE5: evaluating MRST while waiter blocked\n");
st = acpi_evaluate_object(test_handle, "MRST", NULL, NULL);
pr_info("ACPI_CASE5: MRST returned st=%d\n", st);
msleep(1000);
return 0;
}
static void __exit acpi_case5_exit(void)
{
pr_info("ACPI_CASE5: unload\n");
}
module_init(acpi_case5_init);
module_exit(acpi_case5_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ACPI event Reset-vs-Wait race probe");
next prev parent reply other threads:[~2026-11-10 13:12 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 12:35 usb: devio: usbdev_release() has no barrier against an in-flight async_completed() callback (use-after-free window on struct usb_dev_state)gregkh@linuxfoundation.org 徐晗翔
2026-10-27 14:12 ` Greg KH
2026-11-03 11:21 ` mm/slub: has_pcs_used() reads remote per-CPU sheaf state from flush_all_cpus_locked() without synchronization (data race; KCSAN blind spot: slub.o is uninstrumented) 徐晗翔
2026-11-04 9:41 ` Qing Wang
2026-11-04 10:07 ` Qing Wang
2026-11-03 11:30 ` futex/hrtimer: hrtimer_sleeper::task is read unsynchronized in 16 places on v7.3 -- the accessor series that fixes this is not in 7.3; please make sure it reaches the affected trees (backport nudge + stock-kernel KCSAN evidence) 徐晗翔
2026-11-03 11:48 ` cgroup: fork vs cgroup.procs write race in favordynmods mode -- v7.3 is affected; please make sure bf6fbf46b5ff reaches the stable trees (repro incl. uninstrumented natural hit) 徐晗翔
2026-11-03 14:51 ` Sasha Levin
2026-11-03 11:57 ` net: rtnetlink: rtnl_link_unregister() vs in-flight RTM_GETLINK dump readers -- v7.3 lacks the synchronize_rcu() fix (Fixes: 6b57ff21a310 family); A/B reproduction on stock 7.3 徐晗翔
2026-11-03 15:49 ` Eric Dumazet
2026-11-04 15:03 ` Sasha Levin
2026-11-03 12:07 ` wifi: wilc1000: SDIO/SPI remove destroys the srcu_struct before wiphy_unregister() -- late cfg80211 callbacks write through dangling srcu state (unfixed upstream; controlled + natural Oops) 徐晗翔
2026-11-03 12:10 ` gpio: gpiolib: final put of the gpio_device happens inside the SRCU read critical section on the setup-failure path -- deterministic use-after-free read at guard release (unfixed upstream) 徐晗翔
2026-11-05 8:52 ` Linus Walleij
2026-11-04 12:54 ` soc: qcom: pmic_glink_altmode: fix use-after-free on unbind (no cancel_work_sync for port work and enable_work) 徐晗翔
2026-11-04 13:04 ` Greg KH
2026-11-04 12:57 ` usb: typec: anx7411: unregister partner/altmodes before stopping the IRQ work -- use-after-free, double unregister, orphan partner on remove 徐晗翔
2026-11-04 13:28 ` Greg KH
2026-11-04 12:58 ` usb: typec: ucsi: deadlock on cros_ec unbind -- connector lock held across cancel_delayed_work_sync vs the mode-selection work's unbounded trylock polling 徐晗翔
2026-11-04 13:00 ` usb: typec: stusb160x/rt1719/hd3ss3220: IRQ thread vs remove -- double unregister / use-after-free on the partner and port 徐晗翔
2026-11-04 13:01 ` usb: typec: ucsi: displayport: data race on dp->header/ vdo_data/vdo_size -- work reads them unlocked while writers hold con->lock (VDM mismatch) 徐晗翔
2026-11-04 13:02 ` usb: typec: class: three lifetime/locking gaps around the mux/switch handles and mode_selection (drvdata UAF after provider unbind; no concurrent protocol for set/put; unlocked state_update vs delete) 徐晗翔
2026-11-10 12:56 ` driver core: uevent: netns teardown frees uevent_sock while a tagged uevent is still being broadcast (UAF) 徐晗翔
2026-11-10 13:00 ` 徐晗翔
2026-11-10 13:05 ` ACPI: CPPC: per-CPU cpc_desc is freed on processor unbind without clearing the published pointer, causing UAF in cpufreq accessors 徐晗翔
2026-11-10 13:07 ` ACPI: CPPC: shared PCC subspace data can be freed while another CPU probes, reads, or tears it down 徐晗翔
2026-11-10 13:11 ` 徐晗翔 [this message]
2026-11-10 13:14 ` power: supply: bq25890: pump-express work can call power_supply_changed after the psy is unregistered 徐晗翔
2026-11-10 13:17 ` power: supply: core: get/set property callbacks can run after unregister and read freed driver data 徐晗翔
2026-11-10 13:18 ` power: supply: core: system-battery enumeration can return an unregistered battery 徐晗翔
2026-11-10 13:21 ` power: supply: core: changed work can run after unregister and use freed LED triggers 徐晗翔
2026-11-10 13:23 ` power: supply: bq24190: IRQ handler can call power_supply_changed after the psy is unregistered 徐晗翔
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=2a71bbe6.22afa.1a1c5963447.Coremail.xuhx@hust.edu.cn \
--to=xuhx@hust.edu.cn \
--cc=acpica-devel@lists.linux.dev \
--cc=lenb@kernel.org \
--cc=linux-acpi@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=maciej.wieczor-retman@intel.com \
--cc=pawel.chmielewski@intel.com \
--cc=rafael@kernel.org \
/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®