mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 01:32:21 +02:00
perf cs-etm: Bounds-check CPU in cs_etm__get_queue()
cs_etm__get_queue() indexes etm->queues.queue_array[cpu] without
validating that cpu is within nr_queues. When processing
AUX_OUTPUT_HW_ID events, the cpu value comes from untrusted perf.data
trace payload and flows through cs_etm__process_trace_id_v0_1() and
cs_etm__queue_aux_fragment() without bounds checking, allowing an
out-of-bounds read with a crafted file.
Add a bounds check in cs_etm__get_queue() and NULL checks in all
callers.
Also add NULL checks for queue_array[i].priv in the queue iteration
loops in cs_etm__map_trace_id_v0() and cs_etm__process_trace_id_v0_1()
— after auxtrace_queues__grow() new entries are zero-initialized so
.priv can be NULL. Add a get_cpu_data() NULL check in
cs_etm__process_trace_id_v0_1(), matching the existing check in
cs_etm__process_trace_id_v0().
Fixes: 77c123f53e ("perf: cs-etm: Move traceid_list to each queue")
Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: James Clark <james.clark@arm.com>
Cc: Leo Yan <leo.yan@linaro.org>
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
78d8ba6801
commit
9a989e60cc
|
|
@ -292,8 +292,11 @@ static struct cs_etm_queue *cs_etm__get_queue(struct cs_etm_auxtrace *etm, int c
|
|||
{
|
||||
if (etm->per_thread_decoding)
|
||||
return etm->queues.queue_array[0].priv;
|
||||
else
|
||||
return etm->queues.queue_array[cpu].priv;
|
||||
|
||||
if (cpu < 0 || cpu >= (int)etm->queues.nr_queues)
|
||||
return NULL;
|
||||
|
||||
return etm->queues.queue_array[cpu].priv;
|
||||
}
|
||||
|
||||
static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id,
|
||||
|
|
@ -306,6 +309,9 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id
|
|||
* queue associated with that CPU so only one decoder is made.
|
||||
*/
|
||||
etmq = cs_etm__get_queue(etm, cpu_metadata[CS_ETM_CPU]);
|
||||
if (!etmq)
|
||||
return -EINVAL;
|
||||
|
||||
if (etmq->format == UNFORMATTED)
|
||||
return cs_etm__insert_trace_id_node(etmq, trace_chan_id,
|
||||
cpu_metadata);
|
||||
|
|
@ -318,6 +324,9 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id
|
|||
int ret;
|
||||
|
||||
etmq = etm->queues.queue_array[i].priv;
|
||||
if (!etmq)
|
||||
continue;
|
||||
|
||||
ret = cs_etm__insert_trace_id_node(etmq, trace_chan_id,
|
||||
cpu_metadata);
|
||||
if (ret)
|
||||
|
|
@ -358,6 +367,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu,
|
|||
u32 sink_id = FIELD_GET(CS_AUX_HW_ID_SINK_ID_MASK, hw_id);
|
||||
u8 trace_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id);
|
||||
|
||||
if (!etmq)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Check sink id hasn't changed in per-cpu mode. In per-thread mode,
|
||||
* let it pass for now until an actual overlapping trace ID is hit. In
|
||||
|
|
@ -375,6 +387,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu,
|
|||
for (unsigned int i = 0; i < etm->queues.nr_queues; ++i) {
|
||||
struct cs_etm_queue *other_etmq = etm->queues.queue_array[i].priv;
|
||||
|
||||
if (!other_etmq)
|
||||
continue;
|
||||
|
||||
/* Different sinks, skip */
|
||||
if (other_etmq->sink_id != etmq->sink_id)
|
||||
continue;
|
||||
|
|
@ -396,6 +411,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu,
|
|||
}
|
||||
|
||||
cpu_data = get_cpu_data(etm, cpu);
|
||||
if (!cpu_data)
|
||||
return -EINVAL;
|
||||
|
||||
ret = cs_etm__insert_trace_id_node(etmq, trace_id, cpu_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -3144,6 +3162,9 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o
|
|||
aux_offset + aux_size <= auxtrace_event->offset + auxtrace_event->size) {
|
||||
struct cs_etm_queue *etmq = cs_etm__get_queue(etm, auxtrace_event->cpu);
|
||||
|
||||
if (!etmq)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* If this AUX event was inside this buffer somewhere, create a new auxtrace event
|
||||
* based on the sizes of the aux event, and queue that fragment.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user