[PATCH 8/9] w1: w1_therm: lock the list of slaves for the bulk read
From: Greg Kroah-Hartman
Date: Tue Oct 27 2026 - 12:28:16 EST
From: Josef Bacik <josef@xxxxxxxxxxxxxx>
The callbacks of the file therm_bulk_read walk the list of the slaves of
the master, trigger_bulk_read() three times and therm_bulk_read_show()
once, without the list_mutex which protects it. In the meantime the
thread of the master may find out that a sensor has gone:
write to therm_bulk_read w1_process()
trigger_bulk_read() w1_slave_detach()
list_for_each_entry(sl, ...) w1_unref_slave()
bulk_read_slave(sl) list_del()
w1_therm_remove_slave()
kfree(sl->family_data)
kfree(sl)
SLAVE_CONVERT_TRIGGERED(sl) = 1
The walk reads, and writes to, memory which has been freed, and follows
the poisoned list pointers.
Take list_mutex around each of the four walks. No pointer to a slave is
kept from one walk to the next.
This could not be done as long as w1_unref_slave() held the mutex while
it waited, in w1_therm_remove_slave(), for these very callbacks and for
the readers of the sensor, who need the bus_mutex which
trigger_bulk_read() holds around its second walk. Now nothing is waited
for with list_mutex held, and none of the other mutexes of the subsystem
is taken under it, so it can be nested inside bus_mutex.
Fixes: 57c76221d5af ("w1_therm: adding bulk read support to trigger multiple conversion on bus")
Cc: stable <stable@xxxxxxxxxx>
Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
---
drivers/w1/slaves/w1_therm.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
index d96b224e2215..6db0b534b338 100644
--- a/drivers/w1/slaves/w1_therm.c
+++ b/drivers/w1/slaves/w1_therm.c
@@ -1495,6 +1495,7 @@ static int trigger_bulk_read(struct w1_master *dev_master)
* and compute duration of conversion for these devices
* so we can apply a strong pullup if required
*/
+ mutex_lock(&dev_master->list_mutex);
list_for_each_entry(sl, &dev_master->slist, w1_slave_entry) {
if (!sl->family_data)
goto error;
@@ -1508,6 +1509,7 @@ static int trigger_bulk_read(struct w1_master *dev_master)
w1_strong_pullup));
}
}
+ mutex_unlock(&dev_master->list_mutex);
/*
* t_conv is the max conversion time required on the bus
@@ -1534,11 +1536,13 @@ static int trigger_bulk_read(struct w1_master *dev_master)
w1_write_8(dev_master, W1_CONVERT_TEMP);
/* set a flag to instruct that converT pending */
+ mutex_lock(&dev_master->list_mutex);
list_for_each_entry(sl,
&dev_master->slist, w1_slave_entry) {
if (bulk_read_support(sl))
SLAVE_CONVERT_TRIGGERED(sl) = -1;
}
+ mutex_unlock(&dev_master->list_mutex);
if (strong_pullup) { /* some device need pullup */
sleep_rem = msleep_interruptible(t_conv);
@@ -1564,10 +1568,12 @@ static int trigger_bulk_read(struct w1_master *dev_master)
mutex_unlock(&dev_master->bus_mutex);
set_flag:
/* set a flag to register convsersion is done */
+ mutex_lock(&dev_master->list_mutex);
list_for_each_entry(sl, &dev_master->slist, w1_slave_entry) {
if (bulk_read_support(sl))
SLAVE_CONVERT_TRIGGERED(sl) = 1;
}
+ mutex_unlock(&dev_master->list_mutex);
error:
return ret;
}
@@ -1932,6 +1938,7 @@ static ssize_t therm_bulk_read_show(struct device *device,
struct w1_slave *sl = NULL;
int ret = 0;
+ mutex_lock(&dev_master->list_mutex);
list_for_each_entry(sl, &dev_master->slist, w1_slave_entry) {
if (sl->family_data) {
if (bulk_read_support(sl)) {
@@ -1946,6 +1953,7 @@ static ssize_t therm_bulk_read_show(struct device *device,
}
}
show_result:
+ mutex_unlock(&dev_master->list_mutex);
return sprintf(buf, "%d\n", ret);
}
--
2.56.0