mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
perf kwork: Bounds check work->cpu before indexing cpus_runtime[]
work->cpu comes from sample->cpu which is (u32)-1 when PERF_SAMPLE_CPU is absent. Stored as int, this becomes -1 which passes the signed BUG_ON(work->cpu >= MAX_NR_CPUS) but causes an out-of-bounds access on cpus_runtime[-1]. Replace the BUG_ON in top_calc_total_runtime() with an unsigned bounds check that skips entries with invalid CPU values, counting them for a summary warning. Guard the same index in profile_event_match() (bitmap OOB), top_calc_idle_time(), top_calc_irq_runtime(), top_calc_cpu_usage(), and top_calc_load_runtime(). Also guard against division by zero in top_calc_cpu_usage() when no runtime was accumulated. Reported-by: sashiko-bot@kernel.org # Running on a local machine Reviewed-by: Ian Rogers <irogers@google.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Yang Jihong <yangjihong@bytedance.com> Assisted-by: Claude:claude-opus-4.6-1m Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
a2e59fb79f
commit
48d004c074
|
|
@ -467,7 +467,9 @@ static bool profile_event_match(struct perf_kwork *kwork,
|
|||
u64 time = sample->time;
|
||||
struct perf_time_interval *ptime = &kwork->ptime;
|
||||
|
||||
if ((kwork->cpu_list != NULL) && !test_bit(cpu, kwork->cpu_bitmap))
|
||||
/* Guard test_bit: cpu == -1 (absent PERF_SAMPLE_CPU) would index past the bitmap */
|
||||
if ((kwork->cpu_list != NULL) &&
|
||||
((unsigned int)cpu >= MAX_NR_CPUS || !test_bit(cpu, kwork->cpu_bitmap)))
|
||||
return false;
|
||||
|
||||
if (((ptime->start != 0) && (ptime->start > time)) ||
|
||||
|
|
@ -2041,7 +2043,18 @@ static void top_calc_total_runtime(struct perf_kwork *kwork)
|
|||
next = rb_first_cached(&class->work_root);
|
||||
while (next) {
|
||||
work = rb_entry(next, struct kwork_work, node);
|
||||
BUG_ON(work->cpu >= MAX_NR_CPUS);
|
||||
/*
|
||||
* work->cpu comes from sample->cpu which is -1 when
|
||||
* PERF_SAMPLE_CPU is absent. As int that's -1, but as
|
||||
* unsigned it exceeds MAX_NR_CPUS — skip to avoid OOB
|
||||
* on cpus_runtime[].
|
||||
*/
|
||||
/* Counted and reported in perf_kwork__top_report() */
|
||||
if ((unsigned int)work->cpu >= MAX_NR_CPUS) {
|
||||
stat->nr_skipped_cpu++;
|
||||
next = rb_next(next);
|
||||
continue;
|
||||
}
|
||||
stat->cpus_runtime[work->cpu].total += work->total_runtime;
|
||||
stat->cpus_runtime[MAX_NR_CPUS].total += work->total_runtime;
|
||||
next = rb_next(next);
|
||||
|
|
@ -2053,7 +2066,8 @@ static void top_calc_idle_time(struct perf_kwork *kwork,
|
|||
{
|
||||
struct kwork_top_stat *stat = &kwork->top_stat;
|
||||
|
||||
if (work->id == 0) {
|
||||
/* See comment in top_calc_total_runtime() */
|
||||
if (work->id == 0 && (unsigned int)work->cpu < MAX_NR_CPUS) {
|
||||
stat->cpus_runtime[work->cpu].idle += work->total_runtime;
|
||||
stat->cpus_runtime[MAX_NR_CPUS].idle += work->total_runtime;
|
||||
}
|
||||
|
|
@ -2065,6 +2079,10 @@ static void top_calc_irq_runtime(struct perf_kwork *kwork,
|
|||
{
|
||||
struct kwork_top_stat *stat = &kwork->top_stat;
|
||||
|
||||
/* See comment in top_calc_total_runtime() */
|
||||
if ((unsigned int)work->cpu >= MAX_NR_CPUS)
|
||||
return;
|
||||
|
||||
if (type == KWORK_CLASS_IRQ) {
|
||||
stat->cpus_runtime[work->cpu].irq += work->total_runtime;
|
||||
stat->cpus_runtime[MAX_NR_CPUS].irq += work->total_runtime;
|
||||
|
|
@ -2117,12 +2135,19 @@ static void top_calc_cpu_usage(struct perf_kwork *kwork)
|
|||
if (work->total_runtime == 0)
|
||||
goto next;
|
||||
|
||||
/* See comment in top_calc_total_runtime() */
|
||||
if ((unsigned int)work->cpu >= MAX_NR_CPUS)
|
||||
goto next;
|
||||
|
||||
__set_bit(work->cpu, stat->all_cpus_bitmap);
|
||||
|
||||
top_subtract_irq_runtime(kwork, work);
|
||||
|
||||
work->cpu_usage = work->total_runtime * 10000 /
|
||||
stat->cpus_runtime[work->cpu].total;
|
||||
/* Guard against division by zero if no runtime was accumulated */
|
||||
if (stat->cpus_runtime[work->cpu].total) {
|
||||
work->cpu_usage = work->total_runtime * 10000 /
|
||||
stat->cpus_runtime[work->cpu].total;
|
||||
}
|
||||
|
||||
top_calc_idle_time(kwork, work);
|
||||
next:
|
||||
|
|
@ -2135,7 +2160,8 @@ static void top_calc_load_runtime(struct perf_kwork *kwork,
|
|||
{
|
||||
struct kwork_top_stat *stat = &kwork->top_stat;
|
||||
|
||||
if (work->id != 0) {
|
||||
/* See comment in top_calc_total_runtime() */
|
||||
if (work->id != 0 && (unsigned int)work->cpu < MAX_NR_CPUS) {
|
||||
stat->cpus_runtime[work->cpu].load += work->total_runtime;
|
||||
stat->cpus_runtime[MAX_NR_CPUS].load += work->total_runtime;
|
||||
}
|
||||
|
|
@ -2211,6 +2237,13 @@ static void perf_kwork__top_report(struct perf_kwork *kwork)
|
|||
next = rb_next(next);
|
||||
}
|
||||
|
||||
if (kwork->top_stat.nr_skipped_cpu) {
|
||||
printf(" Warning: %u work entries with invalid CPU were excluded from totals.\n"
|
||||
" Task runtimes may appear inflated (IRQ time not subtracted).\n"
|
||||
" Consider re-recording with PERF_SAMPLE_CPU enabled.\n",
|
||||
kwork->top_stat.nr_skipped_cpu);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ struct __top_cpus_runtime {
|
|||
struct kwork_top_stat {
|
||||
DECLARE_BITMAP(all_cpus_bitmap, MAX_NR_CPUS);
|
||||
struct __top_cpus_runtime *cpus_runtime;
|
||||
unsigned int nr_skipped_cpu;
|
||||
};
|
||||
|
||||
struct perf_kwork {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user