* [PATCH 0/11] hwmon: Various fixes
@ 2026-10-30 18:52 Guenter Roeck
2026-10-30 18:52 ` [PATCH 01/11] hwmon: (aquacomputer_d5next) Check maxcollection when probing AQUAERO devices Guenter Roeck
` (10 more replies)
0 siblings, 11 replies; 12+ messages in thread
From: Guenter Roeck @ 2026-10-30 18:52 UTC (permalink / raw)
To: Hardware Monitoring
Cc: Guenter Roeck, Aleksa Savic, Jack Doan, Ed Brindley, Denis Pauk,
James Seo, Iwona Winiarska
The patches in this series fix a number of issues reported by Sashiko.
All but the peci/dimmtemp patch were written the old fashioned way,
not using LLM. I used gemini-4-carbon to implement the peci/dimmtemp
patch.
All patches have been build tested and reviewed by Sashiko.
----------------------------------------------------------------
Guenter Roeck (11):
hwmon: (aquacomputer_d5next) Check maxcollection when probing AQUAERO devices
hwmon: (asus_wmi_sensors) Avoid global struct mutation
hwmon: (hp-wmi-sensors) Avoid global struct mutation
hwmon: (hp-wmi-sensors) Fix unbounded loops in scale_numeric_sensor()
hwmon: (lm87) Fix drvdata type confusion in vrm_show() and vrm_store()
hwmon: (nct6683) Fix incorrect struct cast in show_pwm() and store_pwm()
hwmon: (peci/dimmtemp) Fix devres leak and UAF on concurrent unbind
hwmon: (pmbus/q54sj108a2) Add missing NULL pointer check
hwmon: (pmbus) Check return value from pmbus_thermal_add_sensor()
hwmon: (pmbus) Release devres allocated memory after devm_thermal_of_zone_register error
hwmon: (pmbus) Fix return value check from thermal zone registration
drivers/hwmon/aquacomputer_d5next.c | 2 +-
drivers/hwmon/asus_wmi_sensors.c | 13 ++++------
drivers/hwmon/hp-wmi-sensors.c | 29 +++++++++++----------
drivers/hwmon/lm87.c | 7 +++--
drivers/hwmon/nct6683.c | 4 +--
drivers/hwmon/peci/dimmtemp.c | 21 ++++++++++-----
drivers/hwmon/pmbus/pmbus_core.c | 51 ++++++++++++++++++++++---------------
drivers/hwmon/pmbus/q54sj108a2.c | 2 ++
8 files changed, 75 insertions(+), 54 deletions(-)
Cc: Aleksa Savic <savicaleksa83@gmail.com>
Cc: Jack Doan <me@jackdoan.com>
Cc: Ed Brindley <kernel@maidavale.org>
Cc: Denis Pauk <pauk.denis@gmail.com>
Cc: James Seo <james@equiv.tech>
Cc: Iwona Winiarska <iwona.winiarska@intel.com>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 01/11] hwmon: (aquacomputer_d5next) Check maxcollection when probing AQUAERO devices
2026-10-30 18:52 [PATCH 0/11] hwmon: Various fixes Guenter Roeck
@ 2026-10-30 18:52 ` Guenter Roeck
2026-10-30 18:52 ` [PATCH 02/11] hwmon: (asus_wmi_sensors) Avoid global struct mutation Guenter Roeck
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Guenter Roeck @ 2026-10-30 18:52 UTC (permalink / raw)
To: Hardware Monitoring; +Cc: Guenter Roeck, Aleksa Savic, Jack Doan
When a USB HID device matching USB_PRODUCT_ID_AQUAERO presents a
malformed report descriptor, an out-of-bounds array access occurs in
aqc_probe() due to an unchecked index into the parsed collections array.
During device initialization, the driver distinguishes between the
multiple logical interfaces exposed by the Aquaero device by evaluating
the type of the second element in the collection array. The driver
unconditionally accesses hdev->collection[1] without verifying that
hdev->maxcollection is greater than 1. Because the collection array is
dynamically allocated by the HID core based on the number of collections
found in the report descriptor, a crafted device exposing fewer than two
collections causes an immediate heap out-of-bounds read or NULL pointer
dereference. This leads to a kernel panic during the device probe phase.
Add the missing check to fix the problem.
Fixes: 2c55211104b4 ("hwmon: (aquacomputer_d5next) Support sensors for Aquacomputer Aquaero")
Cc: Aleksa Savic <savicaleksa83@gmail.com>
Cc: Jack Doan <me@jackdoan.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/aquacomputer_d5next.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c
index 341cce4347e2..6565731b0e71 100644
--- a/drivers/hwmon/aquacomputer_d5next.c
+++ b/drivers/hwmon/aquacomputer_d5next.c
@@ -1570,7 +1570,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
* they present. The two other devices have the type of the second element in
* their respective collections set to 1, while the real device has it set to 0.
*/
- if (hdev->collection[1].type != 0) {
+ if (hdev->maxcollection < 2 || hdev->collection[1].type != 0) {
ret = -ENODEV;
goto fail_and_close;
}
--
2.45.2
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 02/11] hwmon: (asus_wmi_sensors) Avoid global struct mutation
2026-10-30 18:52 [PATCH 0/11] hwmon: Various fixes Guenter Roeck
2026-10-30 18:52 ` [PATCH 01/11] hwmon: (aquacomputer_d5next) Check maxcollection when probing AQUAERO devices Guenter Roeck
@ 2026-10-30 18:52 ` Guenter Roeck
2026-10-30 18:52 ` [PATCH 03/11] hwmon: (hp-wmi-sensors) " Guenter Roeck
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Guenter Roeck @ 2026-10-30 18:52 UTC (permalink / raw)
To: Hardware Monitoring; +Cc: Guenter Roeck, Ed Brindley, Denis Pauk
In the asus_wmi_sensors driver, device-managed memory allocated in
asus_wmi_configure_sensor_setup() is assigned to a global static structure,
leading to a potential use-after-free when the device is unbound.
The driver defines a single global asus_wmi_chip_info structure to register
with the hwmon core. During probe, it dynamically allocates the .info
channel array using devm_kcalloc() and assigns it directly to this global
structure. When a device is unbound, its device-managed memory is
automatically freed, leaving asus_wmi_chip_info.info as a dangling pointer.
Furthermore, if multiple WMI devices are probed sequentially, each probe
overwrites the same global pointer, forcing all registered hwmon instances
to reference the channel information of the most recently probed device.
While it is unlikely if not impossible that multiple WMI devices of the same
kind exist, modifying a global static data structure is undesirable. Remove
the static data structure and allocate it as part of struct asus_wmi_sensors
to avoid the potential problem.
Fixes: 548820e21ce1 ("hwmon: (asus_wmi_sensors) Support X370 Asus WMI.")
Cc: Ed Brindley <kernel@maidavale.org>
Cc: Denis Pauk <pauk.denis@gmail.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/asus_wmi_sensors.c | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/drivers/hwmon/asus_wmi_sensors.c b/drivers/hwmon/asus_wmi_sensors.c
index 621e3506972d..0feab3f62496 100644
--- a/drivers/hwmon/asus_wmi_sensors.c
+++ b/drivers/hwmon/asus_wmi_sensors.c
@@ -164,6 +164,7 @@ struct asus_wmi_wmi_info {
struct asus_wmi_sensors {
struct asus_wmi_wmi_info wmi;
+ struct hwmon_chip_info chip_info;
/* lock access to internal cache */
struct mutex lock;
};
@@ -496,11 +497,6 @@ static const struct hwmon_ops asus_wmi_hwmon_ops = {
.read_string = asus_wmi_hwmon_read_string,
};
-static struct hwmon_chip_info asus_wmi_chip_info = {
- .ops = &asus_wmi_hwmon_ops,
- .info = NULL,
-};
-
static int asus_wmi_configure_sensor_setup(struct device *dev,
struct asus_wmi_sensors *sensor_data)
{
@@ -508,7 +504,7 @@ static int asus_wmi_configure_sensor_setup(struct device *dev,
struct hwmon_channel_info *asus_wmi_hwmon_chan;
int nr_count[hwmon_max] = {}, nr_types = 0;
struct asus_wmi_sensor_info *temp_sensor;
- const struct hwmon_chip_info *chip_info;
+ struct hwmon_chip_info *chip_info;
enum hwmon_sensor_types type;
struct device *hwdev;
int i, idx;
@@ -549,8 +545,9 @@ static int asus_wmi_configure_sensor_setup(struct device *dev,
if (!ptr_asus_wmi_ci)
return -ENOMEM;
- asus_wmi_chip_info.info = ptr_asus_wmi_ci;
- chip_info = &asus_wmi_chip_info;
+ chip_info = &sensor_data->chip_info;
+ chip_info->ops = &asus_wmi_hwmon_ops;
+ chip_info->info = ptr_asus_wmi_ci;
sensor_data->wmi.info_by_id = devm_kcalloc(dev, sensor_data->wmi.sensor_count,
sizeof(*sensor_data->wmi.info_by_id),
--
2.45.2
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 03/11] hwmon: (hp-wmi-sensors) Avoid global struct mutation
2026-10-30 18:52 [PATCH 0/11] hwmon: Various fixes Guenter Roeck
2026-10-30 18:52 ` [PATCH 01/11] hwmon: (aquacomputer_d5next) Check maxcollection when probing AQUAERO devices Guenter Roeck
2026-10-30 18:52 ` [PATCH 02/11] hwmon: (asus_wmi_sensors) Avoid global struct mutation Guenter Roeck
@ 2026-10-30 18:52 ` Guenter Roeck
2026-10-30 18:52 ` [PATCH 04/11] hwmon: (hp-wmi-sensors) Fix unbounded loops in scale_numeric_sensor() Guenter Roeck
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Guenter Roeck @ 2026-10-30 18:52 UTC (permalink / raw)
To: Hardware Monitoring; +Cc: Guenter Roeck, James Seo
In the HP WMI sensors driver, make_chip_info() assigns a device-managed
memory allocation to a static global structure, resulting in a
use-after-free when the device unbinds.
During hp_wmi_sensors_probe(), make_chip_info() dynamically allocates a
hwmon_channel_info array using devm_kcalloc() and assigns it to the
static global hp_wmi_chip_info.info pointer.
If multiple instances probe sequentially or concurrently, the shared
global pointer is overwritten by each new device. When any device unbinds,
its devm-managed memory is automatically freed, leaving the global
hp_wmi_chip_info struct and any other active device instances with a
dangling pointer. Subsequent accesses by the hwmon core (such as thermal
zone reads) will dereference this freed memory, causing a use-after-free.
While this is not supposed to happen in the real world (there should only
be one instance of the driver in any system), modifying global structures
is undesirable and should be avoided whenever possible. Allocate struct
hwmon_chip_info as part of struct hp_wmi_sensors instead and initialize it
in make_chip_info() to avoid mutating the global structure.
Fixes: 23902f98f8d4 ("hwmon: add HP WMI Sensors driver")
Cc: James Seo <james@equiv.tech>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/hp-wmi-sensors.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/drivers/hwmon/hp-wmi-sensors.c b/drivers/hwmon/hp-wmi-sensors.c
index d8bcfeb8d5b2..a859752c8387 100644
--- a/drivers/hwmon/hp-wmi-sensors.c
+++ b/drivers/hwmon/hp-wmi-sensors.c
@@ -390,6 +390,7 @@ struct hp_wmi_info {
*/
struct hp_wmi_sensors {
struct wmi_device *wdev;
+ struct hwmon_chip_info chip_info;
struct hp_wmi_info **info_map[hwmon_max];
u8 channel_count[hwmon_max];
bool has_intrusion;
@@ -1531,11 +1532,6 @@ static const struct hwmon_ops hp_wmi_hwmon_ops = {
.write = hp_wmi_hwmon_write,
};
-static struct hwmon_chip_info hp_wmi_chip_info = {
- .ops = &hp_wmi_hwmon_ops,
- .info = NULL,
-};
-
static struct hp_wmi_info *match_fan_event(struct hp_wmi_sensors *state,
const char *event_description)
{
@@ -1962,7 +1958,8 @@ static int make_chip_info(struct hp_wmi_sensors *state, bool has_events)
if (!ptr_channel_info)
return -ENOMEM;
- hp_wmi_chip_info.info = ptr_channel_info;
+ state->chip_info.ops = &hp_wmi_hwmon_ops;
+ state->chip_info.info = ptr_channel_info;
for (type = hwmon_chip; type < hwmon_max; type++) {
count = channel_count[type];
@@ -2053,7 +2050,7 @@ static int hp_wmi_sensors_init(struct hp_wmi_sensors *state)
return err;
hwdev = devm_hwmon_device_register_with_info(dev, "hp_wmi_sensors",
- state, &hp_wmi_chip_info,
+ state, &state->chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwdev);
}
--
2.45.2
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 04/11] hwmon: (hp-wmi-sensors) Fix unbounded loops in scale_numeric_sensor()
2026-10-30 18:52 [PATCH 0/11] hwmon: Various fixes Guenter Roeck
` (2 preceding siblings ...)
2026-10-30 18:52 ` [PATCH 03/11] hwmon: (hp-wmi-sensors) " Guenter Roeck
@ 2026-10-30 18:52 ` Guenter Roeck
2026-10-30 18:52 ` [PATCH 05/11] hwmon: (lm87) Fix drvdata type confusion in vrm_show() and vrm_store() Guenter Roeck
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Guenter Roeck @ 2026-10-30 18:52 UTC (permalink / raw)
To: Hardware Monitoring; +Cc: Guenter Roeck, James Seo
In scale_numeric_sensor() in the hp-wmi-sensors hwmon driver, an unbounded
loop occurs if the ACPI WMI implementation provides a highly anomalous or
malformed UnitModifier value for a numeric sensor.
The UnitModifier property is extracted from a WMI ACPI object without
bounds checking and cast to a signed 32-bit integer. This value directly
controls the iteration count of two scaling loops. If the provided
unit_modifier is extremely negative (e.g., INT_MIN), the first loop
executes billions of times before reaching target_modifier. Conversely,
if unit_modifier is extremely large (e.g., INT_MAX) and the current
sensor reading (val) is exactly 0, the second loop's overflow safeguard
(val > LONG_MAX / 10) never evaluates to true, resulting in billions of
unnecessary iterations. This prolonged execution causes severe CPU stalls
and thread soft lockups during driver initialization or whenever a
user-space process reads the sensor attribute via sysfs, resulting in a
denial of service on the affected CPU core.
Fix the problem in the first loop by exiting it as soon as val is zero.
Fix the problem with the second loop by only entering it if val is not
zero. Also add a check for val < LONG_MIN / 10 and exit the loop if that
is the case.
Fixes: 23902f98f8d4 ("hwmon: add HP WMI Sensors driver")
Cc: James Seo <james@equiv.tech>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/hp-wmi-sensors.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/hwmon/hp-wmi-sensors.c b/drivers/hwmon/hp-wmi-sensors.c
index a859752c8387..1953d672edaf 100644
--- a/drivers/hwmon/hp-wmi-sensors.c
+++ b/drivers/hwmon/hp-wmi-sensors.c
@@ -739,15 +739,21 @@ static long scale_numeric_sensor(const struct hp_wmi_numeric_sensor *nsensor)
val = current_reading;
- for (; unit_modifier < target_modifier; unit_modifier++)
+ for (; val && unit_modifier < target_modifier; unit_modifier++)
val = DIV_ROUND_CLOSEST(val, 10);
- for (; unit_modifier > target_modifier; unit_modifier--) {
- if (val > LONG_MAX / 10) {
- val = LONG_MAX;
- break;
+ if (val) {
+ for (; unit_modifier > target_modifier; unit_modifier--) {
+ if (val > LONG_MAX / 10) {
+ val = LONG_MAX;
+ break;
+ }
+ if (val < LONG_MIN / 10) {
+ val = LONG_MIN;
+ break;
+ }
+ val *= 10;
}
- val *= 10;
}
if (sensor_type == HP_WMI_TYPE_TEMPERATURE) {
--
2.45.2
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 05/11] hwmon: (lm87) Fix drvdata type confusion in vrm_show() and vrm_store()
2026-10-30 18:52 [PATCH 0/11] hwmon: Various fixes Guenter Roeck
` (3 preceding siblings ...)
2026-10-30 18:52 ` [PATCH 04/11] hwmon: (hp-wmi-sensors) Fix unbounded loops in scale_numeric_sensor() Guenter Roeck
@ 2026-10-30 18:52 ` Guenter Roeck
2026-10-30 18:52 ` [PATCH 06/11] hwmon: (nct6683) Fix incorrect struct cast in show_pwm() and store_pwm() Guenter Roeck
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Guenter Roeck @ 2026-10-30 18:52 UTC (permalink / raw)
To: Hardware Monitoring; +Cc: Guenter Roeck
When an LM87 or ADM1024 device is probed with VID support enabled, reading
from or writing to the vrm sysfs attribute triggers a driver data type
confusion in vrm_show() and vrm_store() in drivers/hwmon/lm87.c.
In lm87_probe(), the driver stores the struct lm87_data pointer on the I2C
client via i2c_set_clientdata() and passes the struct i2c_client pointer as
the drvdata argument to devm_hwmon_device_register_with_groups(), which
attaches the attribute groups to the registered hwmon device with client as
its driver_data. Unlike the other sysfs callbacks that retrieve client via
dev_get_drvdata() and then obtain data via i2c_get_clientdata(), vrm_show()
and vrm_store() assign dev_get_drvdata(dev) directly to struct lm87_data *.
Dereferencing data->vrm therefore accesses the struct i2c_client instance
at offsetof(struct lm87_data, vrm), causing vrm_show() to leak an unrelated
byte from struct i2c_client to userspace and vrm_store() to corrupt a byte
within struct i2c_client while failing to update the actual VRM value.
Fix the problem by assigning the return value from dev_get_drvdata(dev) to
struct i2c_client *client and using i2c_get_clientdata() to get the actual
client data.
Fixes: 00a0c905ab4b ("hwmon: (lm87) Use hwmon to create the sysfs groups")
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/lm87.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index c09074f44708..bb4e15b66355 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -606,13 +606,16 @@ static DEVICE_ATTR_RO(cpu0_vid);
static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct lm87_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = dev_get_drvdata(dev);
+ struct lm87_data *data = i2c_get_clientdata(client);
+
return sprintf(buf, "%d\n", data->vrm);
}
static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct lm87_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = dev_get_drvdata(dev);
+ struct lm87_data *data = i2c_get_clientdata(client);
unsigned long val;
int err;
--
2.45.2
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 06/11] hwmon: (nct6683) Fix incorrect struct cast in show_pwm() and store_pwm()
2026-10-30 18:52 [PATCH 0/11] hwmon: Various fixes Guenter Roeck
` (4 preceding siblings ...)
2026-10-30 18:52 ` [PATCH 05/11] hwmon: (lm87) Fix drvdata type confusion in vrm_show() and vrm_store() Guenter Roeck
@ 2026-10-30 18:52 ` Guenter Roeck
2026-10-30 18:52 ` [PATCH 07/11] hwmon: (peci/dimmtemp) Fix devres leak and UAF on concurrent unbind Guenter Roeck
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Guenter Roeck @ 2026-10-30 18:52 UTC (permalink / raw)
To: Hardware Monitoring; +Cc: Guenter Roeck
On big-endian architectures, reading or writing the pwm[1-8] sysfs
attributes in the nct6683 hardware monitoring driver always redirects to
channel 0 due to an incorrect struct downcast in show_pwm() and
store_pwm().
The driver defines the PWM sysfs attributes using SENSOR_TEMPLATE(),
which initializes them as struct sensor_device_attribute (where the index
is a 32-bit int). However, the sysfs callbacks show_pwm() and store_pwm()
incorrectly downcast the attribute to a struct sensor_device_attribute_2
(where the index is an 8-bit u8). Because both structs start with struct
device_attribute, container_of() resolves to the same base address. On
big-endian architectures, reading the 8-bit index field from the memory
layout of a 32-bit int fetches the most significant byte (which is 0),
rather than the intended channel number.
This type mismatch causes all PWM reads to return data for channel 0, and
any writes intended to configure other channels will silently overwrite the
duty cycle of PWM channel 0 instead.
Fix the type casts in show_pwm() and store_pwm() to solve the problem.
Fixes: 41082d66bfd6 ("hwmon: Driver for NCT6683D")
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/nct6683.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c
index e1c36c95affb..f34cb62305be 100644
--- a/drivers/hwmon/nct6683.c
+++ b/drivers/hwmon/nct6683.c
@@ -920,7 +920,7 @@ static ssize_t
show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
{
struct nct6683_data *data = nct6683_update_device(dev);
- struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int index = sattr->index;
return sprintf(buf, "%d\n", data->pwm[index]);
@@ -930,7 +930,7 @@ static ssize_t
store_pwm(struct device *dev, struct device_attribute *attr, const char *buf,
size_t count)
{
- struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
struct nct6683_data *data = dev_get_drvdata(dev);
int index = sattr->index;
unsigned long val;
--
2.45.2
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 07/11] hwmon: (peci/dimmtemp) Fix devres leak and UAF on concurrent unbind
2026-10-30 18:52 [PATCH 0/11] hwmon: Various fixes Guenter Roeck
` (5 preceding siblings ...)
2026-10-30 18:52 ` [PATCH 06/11] hwmon: (nct6683) Fix incorrect struct cast in show_pwm() and store_pwm() Guenter Roeck
@ 2026-10-30 18:52 ` Guenter Roeck
2026-10-30 18:52 ` [PATCH 08/11] hwmon: (pmbus/q54sj108a2) Add missing NULL pointer check Guenter Roeck
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Guenter Roeck @ 2026-10-30 18:52 UTC (permalink / raw)
To: Hardware Monitoring; +Cc: Guenter Roeck, Iwona Winiarska
When a peci_dimmtemp device is unbound concurrently with its delayed
initialization work (detect_work), newly allocated devm resources and
the hwmon device are leaked, leading to a use-after-free when reading
the lingering hwmon sysfs attributes.
In peci_dimmtemp_probe(), detect_work is registered for cancellation via
devm_delayed_work_autocancel(). During driver detach, devres_release_all()
first moves all existing entries from dev->devres_head onto a local todo
list before invoking their release callbacks in reverse order. If
create_dimm_temp_info_delayed() is already running when
devm_delayed_work_drop() calls cancel_delayed_work_sync(),
create_dimm_temp_info() can allocate dimmtemp_label strings via
devm_kzalloc()/devm_kasprintf() and register the hwmon device via
devm_hwmon_device_register_with_info(), appending new devres entries to
the now-empty dev->devres_head. Once the work finishes,
devres_release_all() frees priv and priv->name from the local todo list
without re-evaluating dev->devres_head, leaving the late devres
allocations and registered hwmon device behind with a dangling priv
pointer.
Cancel detect_work synchronously in a driver .remove() callback instead
of relying on devm_delayed_work_autocancel() so that the work is fully
drained before devres_release_all() detaches dev->devres_head, allowing
any devm resources registered by the delayed work to be released in
proper reverse order. Also only schedule detect_work when
check_populated_dimms() returns -EAGAIN so that probe failure paths do
not leave delayed work pending.
Fixes: 73bc1b885dae ("hwmon: peci: Add dimmtemp driver")
Cc: Iwona Winiarska <iwona.winiarska@intel.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/peci/dimmtemp.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c
index bd3e8715dfec..617a4cc0f65c 100644
--- a/drivers/hwmon/peci/dimmtemp.c
+++ b/drivers/hwmon/peci/dimmtemp.c
@@ -4,7 +4,6 @@
#include <linux/auxiliary_bus.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
-#include <linux/devm-helpers.h>
#include <linux/hwmon.h>
#include <linux/jiffies.h>
#include <linux/module.h>
@@ -326,11 +325,13 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv)
if (ret == -ENODEV) {
dev_dbg(priv->dev, "No DIMMs found\n");
return 0;
- } else if (ret) {
+ }
+ if (ret == -EAGAIN) {
schedule_delayed_work(&priv->detect_work, DIMM_MASK_CHECK_DELAY_JIFFIES);
dev_dbg(priv->dev, "Deferred populating DIMM temp info\n");
- return ret;
}
+ if (ret)
+ return ret;
channels = priv->gen_info->chan_rank_max * priv->gen_info->dimm_idx_max;
@@ -398,10 +399,8 @@ static int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct auxil
"Unexpected PECI revision %#x, some features may be unavailable\n",
peci_dev->info.peci_revision);
- ret = devm_delayed_work_autocancel(priv->dev, &priv->detect_work,
- create_dimm_temp_info_delayed);
- if (ret)
- return ret;
+ auxiliary_set_drvdata(adev, priv);
+ INIT_DELAYED_WORK(&priv->detect_work, create_dimm_temp_info_delayed);
ret = create_dimm_temp_info(priv);
if (ret && ret != -EAGAIN) {
@@ -412,6 +411,13 @@ static int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct auxil
return 0;
}
+static void peci_dimmtemp_remove(struct auxiliary_device *adev)
+{
+ struct peci_dimmtemp *priv = auxiliary_get_drvdata(adev);
+
+ cancel_delayed_work_sync(&priv->detect_work);
+}
+
static int
read_thresholds_hsx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data)
{
@@ -665,6 +671,7 @@ MODULE_DEVICE_TABLE(auxiliary, peci_dimmtemp_ids);
static struct auxiliary_driver peci_dimmtemp_driver = {
.probe = peci_dimmtemp_probe,
+ .remove = peci_dimmtemp_remove,
.id_table = peci_dimmtemp_ids,
};
--
2.45.2
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 08/11] hwmon: (pmbus/q54sj108a2) Add missing NULL pointer check
2026-10-30 18:52 [PATCH 0/11] hwmon: Various fixes Guenter Roeck
` (6 preceding siblings ...)
2026-10-30 18:52 ` [PATCH 07/11] hwmon: (peci/dimmtemp) Fix devres leak and UAF on concurrent unbind Guenter Roeck
@ 2026-10-30 18:52 ` Guenter Roeck
2026-10-30 18:52 ` [PATCH 09/11] hwmon: (pmbus) Check return value from pmbus_thermal_add_sensor() Guenter Roeck
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Guenter Roeck @ 2026-10-30 18:52 UTC (permalink / raw)
To: Hardware Monitoring; +Cc: Guenter Roeck
When CONFIG_DEBUG_FS is enabled and per-client PMBus debugfs directory
creation fails during pmbus_do_probe(), q54sj108a2_probe() passes the NULL
pointer returned by pmbus_get_debugfs_dir() to debugfs_create_dir()
without validation, creating its debugfs directory at the debugfs root
instead of under the client directory.
If pmbus_init_debugfs() fails to create the client directory, data->debugfs
is left as NULL or an ERR_PTR, which pmbus_get_debugfs_dir() converts to
NULL. Passing a NULL parent to debugfs_create_dir() places q54sj108a2_dir
in the root of the debugfs filesystem and populates it with attribute files
whose private data pointers reference psu->debugfs_entries. Because this
root-level directory is outside the client debugfs hierarchy, PMBus core
cleanup does not remove it on device unbind, leaking the debugfs entries
and causing a use-after-free of the devm-allocated struct q54sj108a2_data
if the orphaned files are accessed via q54sj108a2_debugfs_read() or
q54sj108a2_debugfs_write() after device removal.
Fix the problem by adding the missing NULL pointer check.
Fixes: d014538aa385 ("hwmon: (pmbus) Driver for Delta power supplies Q54SJ108A2")
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/pmbus/q54sj108a2.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c
index ecac405d7536..b018a611d9b6 100644
--- a/drivers/hwmon/pmbus/q54sj108a2.c
+++ b/drivers/hwmon/pmbus/q54sj108a2.c
@@ -351,6 +351,8 @@ static int q54sj108a2_probe(struct i2c_client *client)
psu->client = client;
debugfs = pmbus_get_debugfs_dir(client);
+ if (!debugfs)
+ return 0;
q54sj108a2_dir = debugfs_create_dir(client->name, debugfs);
--
2.45.2
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 09/11] hwmon: (pmbus) Check return value from pmbus_thermal_add_sensor()
2026-10-30 18:52 [PATCH 0/11] hwmon: Various fixes Guenter Roeck
` (7 preceding siblings ...)
2026-10-30 18:52 ` [PATCH 08/11] hwmon: (pmbus/q54sj108a2) Add missing NULL pointer check Guenter Roeck
@ 2026-10-30 18:52 ` Guenter Roeck
2026-10-30 18:52 ` [PATCH 10/11] hwmon: (pmbus) Release devres allocated memory after devm_thermal_of_zone_register error Guenter Roeck
2026-10-30 18:52 ` [PATCH 11/11] hwmon: (pmbus) Fix return value check from thermal zone registration Guenter Roeck
10 siblings, 0 replies; 12+ messages in thread
From: Guenter Roeck @ 2026-10-30 18:52 UTC (permalink / raw)
To: Hardware Monitoring; +Cc: Guenter Roeck
During PMBus device probe, if registering a temperature sensor with the
thermal subsystem fails, pmbus_add_sensor() silently ignores the error and
returns success.
When a valid temperature sensor is detected (class PSC_TEMPERATURE and type
"input"), pmbus_add_sensor() invokes pmbus_thermal_add_sensor() to hook it
into the kernel's thermal framework. However, the return value of this call
is completely discarded. If thermal registration fails, such as from an
-ENOMEM allocation failure or an invalid device tree configuration like a
malformed #thermal-sensor-cells property, no error is propagated.
Because the hardware monitor probe falsely succeeds despite the failure to
register the thermal zone, the system is left without thermal mitigation
for the affected PMBus device. This silently blinds the system to actual
PMIC temperatures, preventing the execution of necessary cooling mechanisms
or emergency shutdowns and risking localized overheating.
Pass the return value from pmbus_thermal_add_sensor() to the calling code
to fix the problem. To enable passing the actual error, change the error
return from pmbus_add_sensor() from NULL to ERR_PTR() and extract the error
from in the calling code.
Fixes: 3aa74796cfd0 ("hwmon: (pmbus) Register with thermal for PSC_TEMPERATURE")
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/pmbus/pmbus_core.c | 42 ++++++++++++++++++--------------
1 file changed, 24 insertions(+), 18 deletions(-)
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index af13408c62f9..9bccbf41c884 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -1551,10 +1551,11 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
{
struct sensor_device_attribute *a;
struct pmbus_sensor *sensor;
+ int err;
sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor)
- return NULL;
+ return ERR_PTR(-ENOMEM);
a = &sensor->attribute;
if (type)
@@ -1579,14 +1580,17 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
pmbus_set_sensor, -1);
if (pmbus_add_attribute(data, &a->dev_attr.attr))
- return NULL;
+ return ERR_PTR(-ENOMEM);
sensor->next = data->sensors;
data->sensors = sensor;
/* temperature sensors with _input values are registered with thermal */
- if (class == PSC_TEMPERATURE && strcmp(type, "input") == 0)
- pmbus_thermal_add_sensor(data, sensor, seq);
+ if (class == PSC_TEMPERATURE && strcmp(type, "input") == 0) {
+ err = pmbus_thermal_add_sensor(data, sensor, seq);
+ if (err)
+ return ERR_PTR(err);
+ }
return sensor;
}
@@ -1687,8 +1691,8 @@ static int pmbus_add_limit_attrs(struct i2c_client *client,
page, 0xff, l->reg, attr->class,
attr->update || l->update,
l->readonly, l->writeonly, true);
- if (!curr)
- return -ENOMEM;
+ if (IS_ERR(curr))
+ return PTR_ERR(curr);
if (l->sbit && (info->func[page] & attr->sfunc)) {
ret = pmbus_add_boolean(data, name,
l->alarm, index,
@@ -1727,8 +1731,8 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client,
}
base = pmbus_add_sensor(data, name, "input", index, page, phase,
attr->reg, attr->class, true, true, false, true);
- if (!base)
- return -ENOMEM;
+ if (IS_ERR(base))
+ return PTR_ERR(base);
/* No limit and alarm attributes for phase specific sensors */
if (attr->sfunc && phase == 0xff) {
ret = pmbus_add_limit_attrs(client, data, info, name,
@@ -2401,8 +2405,8 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client,
0xff, PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN,
false, false, false, true);
- if (!sensor)
- return -ENOMEM;
+ if (IS_ERR(sensor))
+ return PTR_ERR(sensor);
if (!((data->info->func[page] & PMBUS_HAVE_PWM12) ||
(data->info->func[page] & PMBUS_HAVE_PWM34)))
@@ -2412,15 +2416,15 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client,
0xff, PMBUS_VIRT_PWM_1 + id, PSC_PWM,
false, false, false, true);
- if (!sensor)
- return -ENOMEM;
+ if (IS_ERR(sensor))
+ return PTR_ERR(sensor);
sensor = pmbus_add_sensor(data, "pwm", "enable", index, page,
0xff, PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM,
true, false, false, false);
- if (!sensor)
- return -ENOMEM;
+ if (IS_ERR(sensor))
+ return PTR_ERR(sensor);
return 0;
}
@@ -2437,6 +2441,7 @@ static int pmbus_add_fan_attributes(struct i2c_client *client,
int f;
for (f = 0; f < ARRAY_SIZE(pmbus_fan_registers); f++) {
+ struct pmbus_sensor *sensor;
int regval;
if (!(info->func[page] & pmbus_fan_flags[f]))
@@ -2457,10 +2462,11 @@ static int pmbus_add_fan_attributes(struct i2c_client *client,
(!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4)))))
continue;
- if (pmbus_add_sensor(data, "fan", "input", index,
- page, 0xff, pmbus_fan_registers[f],
- PSC_FAN, true, true, false, true) == NULL)
- return -ENOMEM;
+ sensor = pmbus_add_sensor(data, "fan", "input", index,
+ page, 0xff, pmbus_fan_registers[f],
+ PSC_FAN, true, true, false, true);
+ if (IS_ERR(sensor))
+ return PTR_ERR(sensor);
/* Fan control */
if (pmbus_check_word_register(client, page,
--
2.45.2
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 10/11] hwmon: (pmbus) Release devres allocated memory after devm_thermal_of_zone_register error
2026-10-30 18:52 [PATCH 0/11] hwmon: Various fixes Guenter Roeck
` (8 preceding siblings ...)
2026-10-30 18:52 ` [PATCH 09/11] hwmon: (pmbus) Check return value from pmbus_thermal_add_sensor() Guenter Roeck
@ 2026-10-30 18:52 ` Guenter Roeck
2026-10-30 18:52 ` [PATCH 11/11] hwmon: (pmbus) Fix return value check from thermal zone registration Guenter Roeck
10 siblings, 0 replies; 12+ messages in thread
From: Guenter Roeck @ 2026-10-30 18:52 UTC (permalink / raw)
To: Hardware Monitoring; +Cc: Guenter Roeck
In pmbus_thermal_add_sensor(), a devres memory leak occurs when a PMBus
temperature sensor is initialized but no thermal zone is configured for
it.
The function allocates a pmbus_thermal_data structure using
devm_kzalloc(). It then attempts to register the sensor with the thermal
subsystem via devm_thermal_of_zone_register(). If this registration
returns -ENODEV (e.g., when no thermal zone is mapped in the Device Tree
or CONFIG_THERMAL_OF is disabled), the function intentionally catches the
error and returns 0 to allow device probing to continue.
Because devm_kfree() is not called on the -ENODEV path, the unused tdata
structure remains allocated on the device's managed resource list. Since
most PMBus temperature sensors are not attached to thermal zones, this
results in an accumulation of unused objects that needlessly consume kernel
memory for the lifetime of the PMBus device.
Add the missing call to devm_kfree() to fix the problem.
Fixes: 3aa74796cfd0 ("hwmon: (pmbus) Register with thermal for PSC_TEMPERATURE")
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/pmbus/pmbus_core.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 9bccbf41c884..574d9879bdcf 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -1535,8 +1535,10 @@ static int pmbus_thermal_add_sensor(struct pmbus_data *pmbus_data,
* If CONFIG_THERMAL_OF is disabled, this returns -ENODEV,
* so ignore that error but forward any other error.
*/
- if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV))
+ if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV)) {
+ devm_kfree(dev, tdata);
return PTR_ERR(tzd);
+ }
return 0;
}
--
2.45.2
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 11/11] hwmon: (pmbus) Fix return value check from thermal zone registration
2026-10-30 18:52 [PATCH 0/11] hwmon: Various fixes Guenter Roeck
` (9 preceding siblings ...)
2026-10-30 18:52 ` [PATCH 10/11] hwmon: (pmbus) Release devres allocated memory after devm_thermal_of_zone_register error Guenter Roeck
@ 2026-10-30 18:52 ` Guenter Roeck
10 siblings, 0 replies; 12+ messages in thread
From: Guenter Roeck @ 2026-10-30 18:52 UTC (permalink / raw)
To: Hardware Monitoring; +Cc: Guenter Roeck
The PMBus core used to call devm_thermal_zone_of_sensor_register()
to register thermal zones. Commit 613ed3f676092 ("hwmon: pm_bus: core:
Switch to new of thermal API") changed this to call
devm_thermal_of_zone_register() instead. The code subsequently checks if
the call failed because the thermal subsystem is not configured.
devm_thermal_zone_of_sensor_register() used to return -ENODEV in this
case. devm_thermal_of_zone_register() returns -ENOTSUPP. The code currently
still checks for -ENOVEV. This results in driver instantiation failures if
the thermal subsystem is disabled.
Check for -ENOTSUPP to fix the problem. Keep checking for -ENODEV because
this indicates that no thermal zone is configured for the sensor, which is
acceptable for the driver and must not cause a registration failure.
Fixes: 613ed3f676092 ("hwmon: pm_bus: core: Switch to new of thermal API")
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/pmbus/pmbus_core.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 574d9879bdcf..8fa379d04cd1 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -1532,10 +1532,11 @@ static int pmbus_thermal_add_sensor(struct pmbus_data *pmbus_data,
tzd = devm_thermal_of_zone_register(dev, index - 1, tdata,
&pmbus_thermal_ops);
/*
- * If CONFIG_THERMAL_OF is disabled, this returns -ENODEV,
- * so ignore that error but forward any other error.
+ * If CONFIG_THERMAL_OF is disabled, devm_thermal_of_zone_register()
+ * returns -ENOTSUPP. If no thermal zone is configured for the sensor,
+ * it returns -ENODEV. Ignore those errors but forward any other error.
*/
- if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV)) {
+ if (IS_ERR(tzd) && PTR_ERR(tzd) != -ENODEV && PTR_ERR(tzd) != -ENOTSUPP) {
devm_kfree(dev, tdata);
return PTR_ERR(tzd);
}
--
2.45.2
^ permalink raw reply related [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-10-30 18:53 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-30 18:52 [PATCH 0/11] hwmon: Various fixes Guenter Roeck
2026-10-30 18:52 ` [PATCH 01/11] hwmon: (aquacomputer_d5next) Check maxcollection when probing AQUAERO devices Guenter Roeck
2026-10-30 18:52 ` [PATCH 02/11] hwmon: (asus_wmi_sensors) Avoid global struct mutation Guenter Roeck
2026-10-30 18:52 ` [PATCH 03/11] hwmon: (hp-wmi-sensors) " Guenter Roeck
2026-10-30 18:52 ` [PATCH 04/11] hwmon: (hp-wmi-sensors) Fix unbounded loops in scale_numeric_sensor() Guenter Roeck
2026-10-30 18:52 ` [PATCH 05/11] hwmon: (lm87) Fix drvdata type confusion in vrm_show() and vrm_store() Guenter Roeck
2026-10-30 18:52 ` [PATCH 06/11] hwmon: (nct6683) Fix incorrect struct cast in show_pwm() and store_pwm() Guenter Roeck
2026-10-30 18:52 ` [PATCH 07/11] hwmon: (peci/dimmtemp) Fix devres leak and UAF on concurrent unbind Guenter Roeck
2026-10-30 18:52 ` [PATCH 08/11] hwmon: (pmbus/q54sj108a2) Add missing NULL pointer check Guenter Roeck
2026-10-30 18:52 ` [PATCH 09/11] hwmon: (pmbus) Check return value from pmbus_thermal_add_sensor() Guenter Roeck
2026-10-30 18:52 ` [PATCH 10/11] hwmon: (pmbus) Release devres allocated memory after devm_thermal_of_zone_register error Guenter Roeck
2026-10-30 18:52 ` [PATCH 11/11] hwmon: (pmbus) Fix return value check from thermal zone registration Guenter Roeck
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox