CVE-2026-46021
Description
In the Linux kernel, the following vulnerability has been resolved:
thermal: core: Fix thermal zone governor cleanup issues
If thermal_zone_device_register_with_trips() fails after adding a thermal governor to the thermal zone being registered, the governor is not removed from it as appropriate which may lead to a memory leak.
In turn, thermal_zone_device_unregister() calls thermal_set_governor() without acquiring the thermal zone lock beforehand which may race with a governor update via sysfs and may lead to a use-after-free in that case.
Address these issues by adding two thermal_set_governor() calls, one to thermal_release() to remove the governor from the given thermal zone, and one to the thermal zone registration error path to cover failures preceding the thermal zone device registration.
Affected products
1Patches
1037a430a2d4e6thermal: core: Fix thermal zone governor cleanup issues
1 file changed · +4 −4
drivers/thermal/thermal_core.c+4 −4 modifieddiff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 660a8d6f35673b..3efdd2ae6dcb89 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -804,6 +804,7 @@ static void thermal_release(struct device *dev) sizeof("thermal_zone") - 1)) { tz = to_thermal_zone(dev); thermal_zone_destroy_device_groups(tz); + thermal_set_governor(tz, NULL); mutex_destroy(&tz->lock); complete(&tz->removal); } else if (!strncmp(dev_name(dev), "cooling_device", @@ -1325,8 +1326,10 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t /* sys I/F */ /* Add nodes that are always present via .groups */ result = thermal_zone_create_device_groups(tz, mask); - if (result) + if (result) { + thermal_set_governor(tz, NULL); goto remove_id; + } /* A new thermal zone needs to be updated anyway. */ atomic_set(&tz->need_update, 1); @@ -1478,8 +1481,6 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) cancel_delayed_work_sync(&tz->poll_queue); - thermal_set_governor(tz, NULL); - thermal_remove_hwmon_sysfs(tz); ida_free(&thermal_tz_ida, tz->id); ida_destroy(&tz->ida); -- cgit 1.3-korg
f412e541d25athermal: core: Fix thermal zone governor cleanup issues
1 file changed · +4 −4
drivers/thermal/thermal_core.c+4 −4 modifieddiff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 8ce1134e15e567..1eaddce11aedd9 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -917,6 +917,7 @@ static void thermal_release(struct device *dev) sizeof("thermal_zone") - 1)) { tz = to_thermal_zone(dev); thermal_zone_destroy_device_groups(tz); + thermal_set_governor(tz, NULL); mutex_destroy(&tz->lock); complete(&tz->removal); } else if (!strncmp(dev_name(dev), "cooling_device", @@ -1483,8 +1484,10 @@ thermal_zone_device_register_with_trips(const char *type, /* sys I/F */ /* Add nodes that are always present via .groups */ result = thermal_zone_create_device_groups(tz); - if (result) + if (result) { + thermal_set_governor(tz, NULL); goto remove_id; + } /* A new thermal zone needs to be updated anyway. */ atomic_set(&tz->need_update, 1); @@ -1630,8 +1633,6 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) cancel_delayed_work_sync(&tz->poll_queue); - thermal_set_governor(tz, NULL); - thermal_remove_hwmon_sysfs(tz); ida_free(&thermal_tz_ida, tz->id); ida_destroy(&tz->ida); -- cgit 1.3-korg
41ff66baf81cthermal: core: Fix thermal zone governor cleanup issues
1 file changed · +4 −4
drivers/thermal/thermal_core.c+4 −4 modifieddiff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index b6b651cb233173..6e10b2fe29724a 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -964,6 +964,7 @@ static void thermal_release(struct device *dev) sizeof("thermal_zone") - 1)) { tz = to_thermal_zone(dev); thermal_zone_destroy_device_groups(tz); + thermal_set_governor(tz, NULL); mutex_destroy(&tz->lock); complete(&tz->removal); } else if (!strncmp(dev_name(dev), "cooling_device", @@ -1610,8 +1611,10 @@ thermal_zone_device_register_with_trips(const char *type, /* sys I/F */ /* Add nodes that are always present via .groups */ result = thermal_zone_create_device_groups(tz); - if (result) + if (result) { + thermal_set_governor(tz, NULL); goto remove_id; + } result = device_register(&tz->device); if (result) @@ -1724,8 +1727,6 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) cancel_delayed_work_sync(&tz->poll_queue); - thermal_set_governor(tz, NULL); - thermal_thresholds_exit(tz); thermal_remove_hwmon_sysfs(tz); ida_free(&thermal_tz_ida, tz->id); -- cgit 1.3-korg
75f8f3c3e091thermal: core: Fix thermal zone governor cleanup issues
1 file changed · +4 −4
drivers/thermal/thermal_core.c+4 −4 modifieddiff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f28c15dd0b9260..61e6dc81dc923e 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -964,6 +964,7 @@ static void thermal_release(struct device *dev) sizeof("thermal_zone") - 1)) { tz = to_thermal_zone(dev); thermal_zone_destroy_device_groups(tz); + thermal_set_governor(tz, NULL); mutex_destroy(&tz->lock); complete(&tz->removal); } else if (!strncmp(dev_name(dev), "cooling_device", @@ -1607,8 +1608,10 @@ thermal_zone_device_register_with_trips(const char *type, /* sys I/F */ /* Add nodes that are always present via .groups */ result = thermal_zone_create_device_groups(tz); - if (result) + if (result) { + thermal_set_governor(tz, NULL); goto remove_id; + } result = device_register(&tz->device); if (result) @@ -1721,8 +1724,6 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) cancel_delayed_work_sync(&tz->poll_queue); - thermal_set_governor(tz, NULL); - thermal_thresholds_exit(tz); thermal_remove_hwmon_sysfs(tz); ida_free(&thermal_tz_ida, tz->id); -- cgit 1.3-korg
64d4ebf91d08thermal: core: Fix thermal zone governor cleanup issues
1 file changed · +4 −4
drivers/thermal/thermal_core.c+4 −4 modifieddiff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index d1beee9e15f8ef..cf75f7035602fd 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -964,6 +964,7 @@ static void thermal_release(struct device *dev) sizeof("thermal_zone") - 1)) { tz = to_thermal_zone(dev); thermal_zone_destroy_device_groups(tz); + thermal_set_governor(tz, NULL); mutex_destroy(&tz->lock); complete(&tz->removal); } else if (!strncmp(dev_name(dev), "cooling_device", @@ -1611,8 +1612,10 @@ thermal_zone_device_register_with_trips(const char *type, /* sys I/F */ /* Add nodes that are always present via .groups */ result = thermal_zone_create_device_groups(tz); - if (result) + if (result) { + thermal_set_governor(tz, NULL); goto remove_id; + } result = device_register(&tz->device); if (result) @@ -1725,8 +1728,6 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) cancel_delayed_work_sync(&tz->poll_queue); - thermal_set_governor(tz, NULL); - thermal_thresholds_exit(tz); thermal_remove_hwmon_sysfs(tz); ida_free(&thermal_tz_ida, tz->id); -- cgit 1.3-korg
37a430a2d4e6thermal: core: Fix thermal zone governor cleanup issues
1 file changed · +4 −4
drivers/thermal/thermal_core.c+4 −4 modifieddiff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 660a8d6f35673b..3efdd2ae6dcb89 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -804,6 +804,7 @@ static void thermal_release(struct device *dev) sizeof("thermal_zone") - 1)) { tz = to_thermal_zone(dev); thermal_zone_destroy_device_groups(tz); + thermal_set_governor(tz, NULL); mutex_destroy(&tz->lock); complete(&tz->removal); } else if (!strncmp(dev_name(dev), "cooling_device", @@ -1325,8 +1326,10 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t /* sys I/F */ /* Add nodes that are always present via .groups */ result = thermal_zone_create_device_groups(tz, mask); - if (result) + if (result) { + thermal_set_governor(tz, NULL); goto remove_id; + } /* A new thermal zone needs to be updated anyway. */ atomic_set(&tz->need_update, 1); @@ -1478,8 +1481,6 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) cancel_delayed_work_sync(&tz->poll_queue); - thermal_set_governor(tz, NULL); - thermal_remove_hwmon_sysfs(tz); ida_free(&thermal_tz_ida, tz->id); ida_destroy(&tz->ida); -- cgit 1.3-korg
f412e541d25athermal: core: Fix thermal zone governor cleanup issues
1 file changed · +4 −4
drivers/thermal/thermal_core.c+4 −4 modifieddiff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 8ce1134e15e567..1eaddce11aedd9 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -917,6 +917,7 @@ static void thermal_release(struct device *dev) sizeof("thermal_zone") - 1)) { tz = to_thermal_zone(dev); thermal_zone_destroy_device_groups(tz); + thermal_set_governor(tz, NULL); mutex_destroy(&tz->lock); complete(&tz->removal); } else if (!strncmp(dev_name(dev), "cooling_device", @@ -1483,8 +1484,10 @@ thermal_zone_device_register_with_trips(const char *type, /* sys I/F */ /* Add nodes that are always present via .groups */ result = thermal_zone_create_device_groups(tz); - if (result) + if (result) { + thermal_set_governor(tz, NULL); goto remove_id; + } /* A new thermal zone needs to be updated anyway. */ atomic_set(&tz->need_update, 1); @@ -1630,8 +1633,6 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) cancel_delayed_work_sync(&tz->poll_queue); - thermal_set_governor(tz, NULL); - thermal_remove_hwmon_sysfs(tz); ida_free(&thermal_tz_ida, tz->id); ida_destroy(&tz->ida); -- cgit 1.3-korg
41ff66baf81cthermal: core: Fix thermal zone governor cleanup issues
1 file changed · +4 −4
drivers/thermal/thermal_core.c+4 −4 modifieddiff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index b6b651cb233173..6e10b2fe29724a 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -964,6 +964,7 @@ static void thermal_release(struct device *dev) sizeof("thermal_zone") - 1)) { tz = to_thermal_zone(dev); thermal_zone_destroy_device_groups(tz); + thermal_set_governor(tz, NULL); mutex_destroy(&tz->lock); complete(&tz->removal); } else if (!strncmp(dev_name(dev), "cooling_device", @@ -1610,8 +1611,10 @@ thermal_zone_device_register_with_trips(const char *type, /* sys I/F */ /* Add nodes that are always present via .groups */ result = thermal_zone_create_device_groups(tz); - if (result) + if (result) { + thermal_set_governor(tz, NULL); goto remove_id; + } result = device_register(&tz->device); if (result) @@ -1724,8 +1727,6 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) cancel_delayed_work_sync(&tz->poll_queue); - thermal_set_governor(tz, NULL); - thermal_thresholds_exit(tz); thermal_remove_hwmon_sysfs(tz); ida_free(&thermal_tz_ida, tz->id); -- cgit 1.3-korg
64d4ebf91d08thermal: core: Fix thermal zone governor cleanup issues
1 file changed · +4 −4
drivers/thermal/thermal_core.c+4 −4 modifieddiff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index d1beee9e15f8ef..cf75f7035602fd 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -964,6 +964,7 @@ static void thermal_release(struct device *dev) sizeof("thermal_zone") - 1)) { tz = to_thermal_zone(dev); thermal_zone_destroy_device_groups(tz); + thermal_set_governor(tz, NULL); mutex_destroy(&tz->lock); complete(&tz->removal); } else if (!strncmp(dev_name(dev), "cooling_device", @@ -1611,8 +1612,10 @@ thermal_zone_device_register_with_trips(const char *type, /* sys I/F */ /* Add nodes that are always present via .groups */ result = thermal_zone_create_device_groups(tz); - if (result) + if (result) { + thermal_set_governor(tz, NULL); goto remove_id; + } result = device_register(&tz->device); if (result) @@ -1725,8 +1728,6 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) cancel_delayed_work_sync(&tz->poll_queue); - thermal_set_governor(tz, NULL); - thermal_thresholds_exit(tz); thermal_remove_hwmon_sysfs(tz); ida_free(&thermal_tz_ida, tz->id); -- cgit 1.3-korg
75f8f3c3e091thermal: core: Fix thermal zone governor cleanup issues
1 file changed · +4 −4
drivers/thermal/thermal_core.c+4 −4 modifieddiff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f28c15dd0b9260..61e6dc81dc923e 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -964,6 +964,7 @@ static void thermal_release(struct device *dev) sizeof("thermal_zone") - 1)) { tz = to_thermal_zone(dev); thermal_zone_destroy_device_groups(tz); + thermal_set_governor(tz, NULL); mutex_destroy(&tz->lock); complete(&tz->removal); } else if (!strncmp(dev_name(dev), "cooling_device", @@ -1607,8 +1608,10 @@ thermal_zone_device_register_with_trips(const char *type, /* sys I/F */ /* Add nodes that are always present via .groups */ result = thermal_zone_create_device_groups(tz); - if (result) + if (result) { + thermal_set_governor(tz, NULL); goto remove_id; + } result = device_register(&tz->device); if (result) @@ -1721,8 +1724,6 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) cancel_delayed_work_sync(&tz->poll_queue); - thermal_set_governor(tz, NULL); - thermal_thresholds_exit(tz); thermal_remove_hwmon_sysfs(tz); ida_free(&thermal_tz_ida, tz->id); -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing governor cleanup in thermal zone release and registration error paths, and missing lock acquisition when calling thermal_set_governor() during unregistration."
Attack vector
An attacker with local access can trigger the race condition by writing to the thermal zone's governor sysfs attribute concurrently with a thermal zone device being unregistered. Because `thermal_zone_device_unregister()` called `thermal_set_governor()` without acquiring the thermal zone lock [patch_id=2660373], a concurrent sysfs governor update could race with the unregister path, leading to a use-after-free. Additionally, if `thermal_zone_device_register_with_trips()` fails after a governor has been added (e.g., if `thermal_zone_create_device_groups()` fails), the governor is not removed, causing a memory leak.
Affected code
The vulnerability is in `drivers/thermal/thermal_core.c`, specifically in the functions `thermal_release()`, `thermal_zone_device_register_with_trips()`, and `thermal_zone_device_unregister()` [patch_id=2660373]. The registration error path and the release path both failed to call `thermal_set_governor(tz, NULL)` to properly clean up the governor association, and `thermal_zone_device_unregister()` called `thermal_set_governor()` without holding the thermal zone lock.
What the fix does
The patch adds `thermal_set_governor(tz, NULL)` in two places. First, in `thermal_release()`, so that when a thermal zone device is released, the governor is properly detached, preventing a memory leak of governor-private data. Second, in the error path of `thermal_zone_device_register_with_trips()` after `thermal_zone_create_device_groups()` fails, ensuring that if registration fails after a governor was already added, the governor is cleaned up. The patch also removes the unprotected `thermal_set_governor(tz, NULL)` call from `thermal_zone_device_unregister()`, since the release path now handles it under the lock [patch_id=2660373].
Preconditions
- authLocal access to the system to trigger thermal zone registration/unregistration or to write to sysfs governor attributes
- inputAbility to trigger a failure in thermal_zone_device_register_with_trips() after governor attachment (for memory leak)
- inputConcurrent sysfs governor write and thermal zone unregistration (for use-after-free race)
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/37a430a2d4e66ec8238da6c7f7e48809bf265e13nvd
- git.kernel.org/stable/c/41ff66baf81c6541f4f985dd7eac4494d03d9440nvd
- git.kernel.org/stable/c/64d4ebf91d082034bbc5ae3ba2d7fd800bc02d06nvd
- git.kernel.org/stable/c/75f8f3c3e09122270986de9d7aa347d701676761nvd
- git.kernel.org/stable/c/f412e541d25a3dfaf3d53e012ade6ff03cae8a45nvd
News mentions
0No linked articles in our index yet.