diff options
| author | Wentao Liang <vulab@iscas.ac.cn> | 2026-06-16 15:10:49 +0000 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-07-04 13:45:06 +0200 |
| commit | 73569a44fca2992f0ca4a4c0104069741b9873a0 (patch) | |
| tree | 273d92218e9e5d77b52b1691d90d166b4db7eade | |
| parent | 9d0d5ba20cad661f7f287d4c66d2c19022ce2fd0 (diff) | |
| download | linux-73569a44fca2992f0ca4a4c0104069741b9873a0.tar.gz linux-73569a44fca2992f0ca4a4c0104069741b9873a0.zip | |
pwrseq: core: fix use-after-free in pwrseq_debugfs_seq_next()
commit 257595adf9dac15ae1edd9d07753fbc576a7583d upstream.
pwrseq_debugfs_seq_next() declares 'next' with __free(put_device),
which causes put_device() to be called on the returned pointer when
the variable goes out of scope. This results in a use-after-free
since the seq_file framework receives a pointer whose reference has
already been dropped.
Simply removing __free(put_device) would fix the UAF but would leak
the reference acquired by bus_find_next_device(), as stop() only
calls up_read(&pwrseq_sem) and never releases the device reference.
Fix this by making the reference counting consistent across all
seq_file callbacks, matching the standard pattern used by PCI and
SCSI:
- start(): use get_device() so it returns a referenced pointer.
- next(): explicitly put_device(curr) to release the previous
device's reference (no NULL check needed - the seq_file framework
only calls next() while the previous return was non-NULL).
- stop(): put_device(data) to release the last iterated device's
reference, with a NULL guard since stop() may be called with NULL
when start() returned NULL or next() reached end-of-sequence.
Cc: stable@vger.kernel.org
Fixes: 249ebf3f65f8 ("power: sequencing: implement the pwrseq core")
Signed-off-by: Wentao Liang <vulab@iscas.ac.cn>
Link: https://patch.msgid.link/20260616151049.1705503-1-vulab@iscas.ac.cn
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | drivers/power/sequencing/core.c | 14 |
1 files changed, 9 insertions, 5 deletions
diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/core.c index 4dff71be11b6..9b512a055b5a 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -989,8 +989,9 @@ static void *pwrseq_debugfs_seq_start(struct seq_file *seq, loff_t *pos) ctx.index = *pos; /* - * We're holding the lock for the entire printout so no need to fiddle - * with device reference count. + * Hold the lock for the entire printout to prevent device removal. + * Reference counts are managed by start()/next()/stop() as required + * by the seq_file contract. */ down_read(&pwrseq_sem); @@ -998,7 +999,7 @@ static void *pwrseq_debugfs_seq_start(struct seq_file *seq, loff_t *pos) if (!ctx.index) return NULL; - return ctx.dev; + return get_device(ctx.dev); } static void *pwrseq_debugfs_seq_next(struct seq_file *seq, void *data, @@ -1008,8 +1009,9 @@ static void *pwrseq_debugfs_seq_next(struct seq_file *seq, void *data, ++*pos; - struct device *next __free(put_device) = - bus_find_next_device(&pwrseq_bus, curr); + struct device *next = bus_find_next_device(&pwrseq_bus, curr); + + put_device(curr); return next; } @@ -1058,6 +1060,8 @@ static int pwrseq_debugfs_seq_show(struct seq_file *seq, void *data) static void pwrseq_debugfs_seq_stop(struct seq_file *seq, void *data) { + if (data) + put_device(data); up_read(&pwrseq_sem); } |
