Skip to content

Commit c7f7c37

Browse files
committed
thermal/debugfs: Fix two locking issues with thermal zone debug
With the current thermal zone locking arrangement in the debugfs code, user space can open the "mitigations" file for a thermal zone before the zone's debugfs pointer is set which will result in a NULL pointer dereference in tze_seq_start(). Moreover, thermal_debug_tz_remove() is not called under the thermal zone lock, so it can run in parallel with the other functions accessing the thermal zone's struct thermal_debugfs object. Then, it may clear tz->debugfs after one of those functions has checked it and the struct thermal_debugfs object may be freed prematurely. To address the first problem, pass a pointer to the thermal zone's struct thermal_debugfs object to debugfs_create_file() in thermal_debug_tz_add() and make tze_seq_start(), tze_seq_next(), tze_seq_stop(), and tze_seq_show() retrieve it from s->private instead of a pointer to the thermal zone object. This will ensure that tz_debugfs will be valid across the "mitigations" file accesses until thermal_debugfs_remove_id() called by thermal_debug_tz_remove() removes that file. To address the second problem, use tz->lock in thermal_debug_tz_remove() around the tz->debugfs value check (in case the same thermal zone is removed at the same time in two different threads) and its reset to NULL. Fixes: 7ef01f2 ("thermal/debugfs: Add thermal debugfs information for mitigation episodes") Cc :6.8+ <stable@vger.kernel.org> # 6.8+ Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
1 parent 72c1aff commit c7f7c37

File tree

1 file changed

+22
-12
lines changed

1 file changed

+22
-12
lines changed

drivers/thermal/thermal_debugfs.c

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,13 @@ struct tz_episode {
139139
* we keep track of the current position in the history array.
140140
*
141141
* @tz_episodes: a list of thermal mitigation episodes
142+
* @tz: thermal zone this object belongs to
142143
* @trips_crossed: an array of trip points crossed by id
143144
* @nr_trips: the number of trip points currently being crossed
144145
*/
145146
struct tz_debugfs {
146147
struct list_head tz_episodes;
148+
struct thermal_zone_device *tz;
147149
int *trips_crossed;
148150
int nr_trips;
149151
};
@@ -716,8 +718,7 @@ void thermal_debug_update_temp(struct thermal_zone_device *tz)
716718

717719
static void *tze_seq_start(struct seq_file *s, loff_t *pos)
718720
{
719-
struct thermal_zone_device *tz = s->private;
720-
struct thermal_debugfs *thermal_dbg = tz->debugfs;
721+
struct thermal_debugfs *thermal_dbg = s->private;
721722
struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg;
722723

723724
mutex_lock(&thermal_dbg->lock);
@@ -727,24 +728,23 @@ static void *tze_seq_start(struct seq_file *s, loff_t *pos)
727728

728729
static void *tze_seq_next(struct seq_file *s, void *v, loff_t *pos)
729730
{
730-
struct thermal_zone_device *tz = s->private;
731-
struct thermal_debugfs *thermal_dbg = tz->debugfs;
731+
struct thermal_debugfs *thermal_dbg = s->private;
732732
struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg;
733733

734734
return seq_list_next(v, &tz_dbg->tz_episodes, pos);
735735
}
736736

737737
static void tze_seq_stop(struct seq_file *s, void *v)
738738
{
739-
struct thermal_zone_device *tz = s->private;
740-
struct thermal_debugfs *thermal_dbg = tz->debugfs;
739+
struct thermal_debugfs *thermal_dbg = s->private;
741740

742741
mutex_unlock(&thermal_dbg->lock);
743742
}
744743

745744
static int tze_seq_show(struct seq_file *s, void *v)
746745
{
747-
struct thermal_zone_device *tz = s->private;
746+
struct thermal_debugfs *thermal_dbg = s->private;
747+
struct thermal_zone_device *tz = thermal_dbg->tz_dbg.tz;
748748
struct thermal_trip *trip;
749749
struct tz_episode *tze;
750750
const char *type;
@@ -810,6 +810,8 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz)
810810

811811
tz_dbg = &thermal_dbg->tz_dbg;
812812

813+
tz_dbg->tz = tz;
814+
813815
tz_dbg->trips_crossed = kzalloc(sizeof(int) * tz->num_trips, GFP_KERNEL);
814816
if (!tz_dbg->trips_crossed) {
815817
thermal_debugfs_remove_id(thermal_dbg);
@@ -818,20 +820,30 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz)
818820

819821
INIT_LIST_HEAD(&tz_dbg->tz_episodes);
820822

821-
debugfs_create_file("mitigations", 0400, thermal_dbg->d_top, tz, &tze_fops);
823+
debugfs_create_file("mitigations", 0400, thermal_dbg->d_top,
824+
thermal_dbg, &tze_fops);
822825

823826
tz->debugfs = thermal_dbg;
824827
}
825828

826829
void thermal_debug_tz_remove(struct thermal_zone_device *tz)
827830
{
828-
struct thermal_debugfs *thermal_dbg = tz->debugfs;
831+
struct thermal_debugfs *thermal_dbg;
829832
struct tz_episode *tze, *tmp;
830833
struct tz_debugfs *tz_dbg;
831834
int *trips_crossed;
832835

833-
if (!thermal_dbg)
836+
mutex_lock(&tz->lock);
837+
838+
thermal_dbg = tz->debugfs;
839+
if (!thermal_dbg) {
840+
mutex_unlock(&tz->lock);
834841
return;
842+
}
843+
844+
tz->debugfs = NULL;
845+
846+
mutex_unlock(&tz->lock);
835847

836848
tz_dbg = &thermal_dbg->tz_dbg;
837849

@@ -844,8 +856,6 @@ void thermal_debug_tz_remove(struct thermal_zone_device *tz)
844856
kfree(tze);
845857
}
846858

847-
tz->debugfs = NULL;
848-
849859
mutex_unlock(&thermal_dbg->lock);
850860

851861
thermal_debugfs_remove_id(thermal_dbg);

0 commit comments

Comments
 (0)