CVE-2026-31731
Severity CVSS v4.0:
Pending analysis
Type:
CWE-416
Use After Free
Publication date:
01/05/2026
Last modified:
08/05/2026
Description
In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
thermal: core: Address thermal zone removal races with resume<br />
<br />
Since thermal_zone_pm_complete() and thermal_zone_device_resume()<br />
re-initialize the poll_queue delayed work for the given thermal zone,<br />
the cancel_delayed_work_sync() in thermal_zone_device_unregister()<br />
may miss some already running work items and the thermal zone may<br />
be freed prematurely [1].<br />
<br />
There are two failing scenarios that both start with<br />
running thermal_pm_notify_complete() right before invoking<br />
thermal_zone_device_unregister() for one of the thermal zones.<br />
<br />
In the first scenario, there is a work item already running for<br />
the given thermal zone when thermal_pm_notify_complete() calls<br />
thermal_zone_pm_complete() for that thermal zone and it continues to<br />
run when thermal_zone_device_unregister() starts. Since the poll_queue<br />
delayed work has been re-initialized by thermal_pm_notify_complete(), the<br />
running work item will be missed by the cancel_delayed_work_sync() in<br />
thermal_zone_device_unregister() and if it continues to run past the<br />
freeing of the thermal zone object, a use-after-free will occur.<br />
<br />
In the second scenario, thermal_zone_device_resume() queued up by<br />
thermal_pm_notify_complete() runs right after the thermal_zone_exit()<br />
called by thermal_zone_device_unregister() has returned. The poll_queue<br />
delayed work is re-initialized by it before cancel_delayed_work_sync() is<br />
called by thermal_zone_device_unregister(), so it may continue to run<br />
after the freeing of the thermal zone object, which also leads to a<br />
use-after-free.<br />
<br />
Address the first failing scenario by ensuring that no thermal work<br />
items will be running when thermal_pm_notify_complete() is called.<br />
For this purpose, first move the cancel_delayed_work() call from<br />
thermal_zone_pm_complete() to thermal_zone_pm_prepare() to prevent<br />
new work from entering the workqueue going forward. Next, switch<br />
over to using a dedicated workqueue for thermal events and update<br />
the code in thermal_pm_notify() to flush that workqueue after<br />
thermal_pm_notify_prepare() has returned which will take care of<br />
all leftover thermal work already on the workqueue (that leftover<br />
work would do nothing useful anyway because all of the thermal zones<br />
have been flagged as suspended).<br />
<br />
The second failing scenario is addressed by adding a tz->state check<br />
to thermal_zone_device_resume() to prevent it from re-initializing<br />
the poll_queue delayed work if the thermal zone is going away.<br />
<br />
Note that the above changes will also facilitate relocating the suspend<br />
and resume of thermal zones closer to the suspend and resume of devices,<br />
respectively.
Impact
Base Score 3.x
7.80
Severity 3.x
HIGH
Vulnerable products and versions
| CPE | From | Up to |
|---|---|---|
| cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* | 6.8 (including) | 6.12.83 (excluding) |
| cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* | 6.13 (including) | 6.18.22 (excluding) |
| cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* | 6.19 (including) | 6.19.12 (excluding) |
| cpe:2.3:o:linux:linux_kernel:7.0:rc1:*:*:*:*:*:* | ||
| cpe:2.3:o:linux:linux_kernel:7.0:rc2:*:*:*:*:*:* | ||
| cpe:2.3:o:linux:linux_kernel:7.0:rc3:*:*:*:*:*:* | ||
| cpe:2.3:o:linux:linux_kernel:7.0:rc4:*:*:*:*:*:* | ||
| cpe:2.3:o:linux:linux_kernel:7.0:rc5:*:*:*:*:*:* | ||
| cpe:2.3:o:linux:linux_kernel:7.0:rc6:*:*:*:*:*:* |
To consult the complete list of CPE names with products and versions, see this page



