From: Greg Kroah-Hartman Date: Wed, 22 Jul 2026 13:16:14 +0000 (+0200) Subject: drop 6.12 perf patches X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=fd1666a7f54539f64fcefb8764595b8117fb6e8f;p=thirdparty%2Fkernel%2Fstable-queue.git drop 6.12 perf patches --- diff --git a/queue-6.12/perf-arm-spe-add-branch-stack.patch b/queue-6.12/perf-arm-spe-add-branch-stack.patch deleted file mode 100644 index a8cb3c98d6..0000000000 --- a/queue-6.12/perf-arm-spe-add-branch-stack.patch +++ /dev/null @@ -1,221 +0,0 @@ -From 6eb077ed90cb41ccf685ed405d72d9e5a7b8299e Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Tue, 4 Mar 2025 11:12:39 +0000 -Subject: perf arm-spe: Add branch stack - -From: Leo Yan - -[ Upstream commit 73cb57f56f749ac640b889f076d5db96264020f8 ] - -Although Arm SPE cannot generate continuous branch records, this commit -creates a branch stack with only one branch entry. A single branch info -can be used for performance optimization. - -A branch stack structure is dynamically allocated in the decode queue. -The branch stack and stack flags are synthesized based on branch types -and associated events. - -After: - - # perf script --itrace=bl1 -F flags,addr,brstack - - jcc ffffc0fad9c6b214 0xffffc0fad9c6b234/0xffffc0fad9c6b214/P/-/-/7/COND/- - jcc/miss,not_taken/ ffffc0fadaaebb30 0xffffc0fadaaebb2c/0xffffc0fadaaebb30/MN/-/-/7/COND/- - jmp ffffc0fadaaea358 0xffffc0fadaaea5ec/0xffffc0fadaaea358/P/-/-/5//- - jcc/not_taken/ ffffc0fadaae6494 0xffffc0fadaae6490/0xffffc0fadaae6494/PN/-/-/11/COND/- - jcc/not_taken/ ffff7f83ab54 0xffff7f83ab50/0xffff7f83ab54/PN/-/-/13/COND/- - jcc/not_taken/ ffff7f83ab08 0xffff7f83ab04/0xffff7f83ab08/PN/-/-/8/COND/- - jcc ffff7f83aa80 0xffff7f83aa58/0xffff7f83aa80/P/-/-/10/COND/- - jcc ffff7f9a45d0 0xffff7f9a43f0/0xffff7f9a45d0/P/-/-/29/COND/- - jcc/not_taken/ ffffc0fad9ba6db4 0xffffc0fad9ba6db0/0xffffc0fad9ba6db4/PN/-/-/44/COND/- - jcc ffffc0fadaac2964 0xffffc0fadaac2970/0xffffc0fadaac2964/P/-/-/6/COND/- - jcc ffffc0fad99ddc10 0xffffc0fad99ddc04/0xffffc0fad99ddc10/P/-/-/72/COND/- - jcc/not_taken/ ffffc0fad9b3f21c 0xffffc0fad9b3f218/0xffffc0fad9b3f21c/PN/-/-/64/COND/- - jcc ffffc0fad9c3b604 0xffffc0fad9c3b5f8/0xffffc0fad9c3b604/P/-/-/13/COND/- - jcc ffffc0fadaad6048 0xffffc0fadaad5f8c/0xffffc0fadaad6048/P/-/-/5/COND/- - return/miss/ ffff7f84e614 0xffffc0fad98a2274/0xffff7f84e614/M/-/-/13/RET/- - jcc/not_taken/ ffffc0fadaac4eb4 0xffffc0fadaac4eb0/0xffffc0fadaac4eb4/PN/-/-/5/COND/- - jmp ffff7f8e3130 0xffff7f87555c/0xffff7f8e3130/P/-/-/5//- - jcc/not_taken/ ffffc0fad9b3d9b0 0xffffc0fad9b3d9ac/0xffffc0fad9b3d9b0/PN/-/-/14/COND/- - return ffffc0fad9b91950 0xffffc0fad98c3e28/0xffffc0fad9b91950/P/-/-/12/RET/- - -Reviewed-by: Ian Rogers -Reviewed-by: James Clark -Signed-off-by: Leo Yan -Link: https://lore.kernel.org/r/20250304111240.3378214-12-leo.yan@arm.com -Signed-off-by: Namhyung Kim -Stable-dep-of: 059e9100d82a ("perf event: Fix size of synthesized sample with branch stacks") -Signed-off-by: Sasha Levin ---- - tools/perf/util/arm-spe.c | 99 +++++++++++++++++++++++++++++++++++++++ - 1 file changed, 99 insertions(+) - -diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c -index a004e1721a1ff1..80fcb8b10f52e4 100644 ---- a/tools/perf/util/arm-spe.c -+++ b/tools/perf/util/arm-spe.c -@@ -99,6 +99,7 @@ struct arm_spe_queue { - struct thread *thread; - u64 period_instructions; - u32 flags; -+ struct branch_stack *last_branch; - }; - - static void arm_spe_dump(struct arm_spe *spe __maybe_unused, -@@ -217,6 +218,16 @@ static struct arm_spe_queue *arm_spe__alloc_queue(struct arm_spe *spe, - params.get_trace = arm_spe_get_trace; - params.data = speq; - -+ if (spe->synth_opts.last_branch) { -+ size_t sz = sizeof(struct branch_stack); -+ -+ /* Allocate one entry for TGT */ -+ sz += sizeof(struct branch_entry); -+ speq->last_branch = zalloc(sz); -+ if (!speq->last_branch) -+ goto out_free; -+ } -+ - /* create new decoder */ - speq->decoder = arm_spe_decoder_new(¶ms); - if (!speq->decoder) -@@ -226,6 +237,7 @@ static struct arm_spe_queue *arm_spe__alloc_queue(struct arm_spe *spe, - - out_free: - zfree(&speq->event_buf); -+ zfree(&speq->last_branch); - free(speq); - - return NULL; -@@ -332,6 +344,73 @@ static void arm_spe_prep_sample(struct arm_spe *spe, - event->sample.header.size = sizeof(struct perf_event_header); - } - -+static void arm_spe__prep_branch_stack(struct arm_spe_queue *speq) -+{ -+ struct arm_spe_record *record = &speq->decoder->record; -+ struct branch_stack *bstack = speq->last_branch; -+ struct branch_flags *bs_flags; -+ size_t sz = sizeof(struct branch_stack) + -+ sizeof(struct branch_entry) /* TGT */; -+ -+ /* Clean up branch stack */ -+ memset(bstack, 0x0, sz); -+ -+ if (!(speq->flags & PERF_IP_FLAG_BRANCH)) -+ return; -+ -+ bstack->entries[0].from = record->from_ip; -+ bstack->entries[0].to = record->to_ip; -+ -+ bs_flags = &bstack->entries[0].flags; -+ bs_flags->value = 0; -+ -+ if (record->op & ARM_SPE_OP_BR_CR_BL) { -+ if (record->op & ARM_SPE_OP_BR_COND) -+ bs_flags->type |= PERF_BR_COND_CALL; -+ else -+ bs_flags->type |= PERF_BR_CALL; -+ /* -+ * Indirect branch instruction without link (e.g. BR), -+ * take this case as function return. -+ */ -+ } else if (record->op & ARM_SPE_OP_BR_CR_RET || -+ record->op & ARM_SPE_OP_BR_INDIRECT) { -+ if (record->op & ARM_SPE_OP_BR_COND) -+ bs_flags->type |= PERF_BR_COND_RET; -+ else -+ bs_flags->type |= PERF_BR_RET; -+ } else if (record->op & ARM_SPE_OP_BR_CR_NON_BL_RET) { -+ if (record->op & ARM_SPE_OP_BR_COND) -+ bs_flags->type |= PERF_BR_COND; -+ else -+ bs_flags->type |= PERF_BR_UNCOND; -+ } else { -+ if (record->op & ARM_SPE_OP_BR_COND) -+ bs_flags->type |= PERF_BR_COND; -+ else -+ bs_flags->type |= PERF_BR_UNKNOWN; -+ } -+ -+ if (record->type & ARM_SPE_BRANCH_MISS) { -+ bs_flags->mispred = 1; -+ bs_flags->predicted = 0; -+ } else { -+ bs_flags->mispred = 0; -+ bs_flags->predicted = 1; -+ } -+ -+ if (record->type & ARM_SPE_BRANCH_NOT_TAKEN) -+ bs_flags->not_taken = 1; -+ -+ if (record->type & ARM_SPE_IN_TXN) -+ bs_flags->in_tx = 1; -+ -+ bs_flags->cycles = min(record->latency, 0xFFFFU); -+ -+ bstack->nr = 1; -+ bstack->hw_idx = -1ULL; -+} -+ - static int arm_spe__inject_event(union perf_event *event, struct perf_sample *sample, u64 type) - { - event->header.size = perf_event__sample_event_size(sample, type, 0); -@@ -394,6 +473,7 @@ static int arm_spe__synth_branch_sample(struct arm_spe_queue *speq, - sample.addr = record->to_ip; - sample.weight = record->latency; - sample.flags = speq->flags; -+ sample.branch_stack = speq->last_branch; - - return arm_spe_deliver_synth_event(spe, speq, event, &sample); - } -@@ -424,6 +504,7 @@ static int arm_spe__synth_instruction_sample(struct arm_spe_queue *speq, - sample.period = spe->instructions_sample_period; - sample.weight = record->latency; - sample.flags = speq->flags; -+ sample.branch_stack = speq->last_branch; - - return arm_spe_deliver_synth_event(spe, speq, event, &sample); - } -@@ -685,6 +766,10 @@ static int arm_spe_sample(struct arm_spe_queue *speq) - } - } - -+ if (spe->synth_opts.last_branch && -+ (spe->sample_branch || spe->sample_instructions)) -+ arm_spe__prep_branch_stack(speq); -+ - if (spe->sample_branch && (record->op & ARM_SPE_OP_BRANCH_ERET)) { - err = arm_spe__synth_branch_sample(speq, spe->branch_id); - if (err) -@@ -1109,6 +1194,7 @@ static void arm_spe_free_queue(void *priv) - thread__zput(speq->thread); - arm_spe_decoder_free(speq->decoder); - zfree(&speq->event_buf); -+ zfree(&speq->last_branch); - free(speq); - } - -@@ -1283,6 +1369,19 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) - id += 1; - } - -+ if (spe->synth_opts.last_branch) { -+ if (spe->synth_opts.last_branch_sz > 1) -+ pr_debug("Arm SPE supports only one bstack entry (TGT).\n"); -+ -+ attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; -+ /* -+ * We don't use the hardware index, but the sample generation -+ * code uses the new format branch_stack with this field, -+ * so the event attributes must indicate that it's present. -+ */ -+ attr.branch_sample_type |= PERF_SAMPLE_BRANCH_HW_INDEX; -+ } -+ - if (spe->synth_opts.branches) { - spe->sample_branch = true; - --- -2.53.0 - diff --git a/queue-6.12/perf-arm-spe-correctly-set-sample-flags.patch b/queue-6.12/perf-arm-spe-correctly-set-sample-flags.patch deleted file mode 100644 index eb8ac25bfc..0000000000 --- a/queue-6.12/perf-arm-spe-correctly-set-sample-flags.patch +++ /dev/null @@ -1,110 +0,0 @@ -From f86107d0af421f668696ed5516ebc773614aa3b0 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Fri, 25 Oct 2024 15:30:08 +0100 -Subject: perf arm-spe: Correctly set sample flags - -From: Graham Woodward - -[ Upstream commit edff8dad3f9a483259140fb814586b39da430a38 ] - -Set flags on all synthesized instruction and branch samples. - -Signed-off-by: Graham Woodward -Reviewed-by: James Clark -Tested-by: Leo Yan -Cc: nd@arm.com -Cc: mike.leach@linaro.org -Cc: linux-arm-kernel@lists.infradead.org -Link: https://lore.kernel.org/r/20241025143009.25419-4-graham.woodward@arm.com -Signed-off-by: Namhyung Kim -Stable-dep-of: 059e9100d82a ("perf event: Fix size of synthesized sample with branch stacks") -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-script.c | 1 + - tools/perf/util/arm-spe.c | 17 +++++++++++++++++ - tools/perf/util/event.h | 1 + - 3 files changed, 19 insertions(+) - -diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c -index a644787fa9e1dc..6f3db073786f96 100644 ---- a/tools/perf/builtin-script.c -+++ b/tools/perf/builtin-script.c -@@ -1728,6 +1728,7 @@ static struct { - {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "tr end"}, - {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMENTRY, "vmentry"}, - {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMEXIT, "vmexit"}, -+ {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_BRANCH_MISS, "br miss"}, - {0, NULL} - }; - -diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c -index ce34cc5a2e7163..a004e1721a1ff1 100644 ---- a/tools/perf/util/arm-spe.c -+++ b/tools/perf/util/arm-spe.c -@@ -98,6 +98,7 @@ struct arm_spe_queue { - u64 timestamp; - struct thread *thread; - u64 period_instructions; -+ u32 flags; - }; - - static void arm_spe_dump(struct arm_spe *spe __maybe_unused, -@@ -392,6 +393,7 @@ static int arm_spe__synth_branch_sample(struct arm_spe_queue *speq, - sample.stream_id = spe_events_id; - sample.addr = record->to_ip; - sample.weight = record->latency; -+ sample.flags = speq->flags; - - return arm_spe_deliver_synth_event(spe, speq, event, &sample); - } -@@ -421,6 +423,7 @@ static int arm_spe__synth_instruction_sample(struct arm_spe_queue *speq, - sample.data_src = data_src; - sample.period = spe->instructions_sample_period; - sample.weight = record->latency; -+ sample.flags = speq->flags; - - return arm_spe_deliver_synth_event(spe, speq, event, &sample); - } -@@ -432,6 +435,19 @@ static const struct midr_range common_ds_encoding_cpus[] = { - {}, - }; - -+static void arm_spe__sample_flags(struct arm_spe_queue *speq) -+{ -+ const struct arm_spe_record *record = &speq->decoder->record; -+ -+ speq->flags = 0; -+ if (record->op & ARM_SPE_OP_BRANCH_ERET) { -+ speq->flags = PERF_IP_FLAG_BRANCH; -+ -+ if (record->type & ARM_SPE_BRANCH_MISS) -+ speq->flags |= PERF_IP_FLAG_BRANCH_MISS; -+ } -+} -+ - static void arm_spe__synth_data_source_common(const struct arm_spe_record *record, - union perf_mem_data_src *data_src) - { -@@ -618,6 +634,7 @@ static int arm_spe_sample(struct arm_spe_queue *speq) - u64 data_src; - int err; - -+ arm_spe__sample_flags(speq); - data_src = arm_spe__synth_data_source(speq, record); - - if (spe->sample_flc) { -diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h -index f8742e6230a5ab..2744c54f404e7b 100644 ---- a/tools/perf/util/event.h -+++ b/tools/perf/util/event.h -@@ -66,6 +66,7 @@ enum { - PERF_IP_FLAG_VMEXIT = 1ULL << 12, - PERF_IP_FLAG_INTR_DISABLE = 1ULL << 13, - PERF_IP_FLAG_INTR_TOGGLE = 1ULL << 14, -+ PERF_IP_FLAG_BRANCH_MISS = 1ULL << 15, - }; - - #define PERF_IP_FLAG_CHARS "bcrosyiABExghDt" --- -2.53.0 - diff --git a/queue-6.12/perf-arm-spe-use-arm_spe_op_branch_eret-when-synthes.patch b/queue-6.12/perf-arm-spe-use-arm_spe_op_branch_eret-when-synthes.patch deleted file mode 100644 index 2336875744..0000000000 --- a/queue-6.12/perf-arm-spe-use-arm_spe_op_branch_eret-when-synthes.patch +++ /dev/null @@ -1,70 +0,0 @@ -From 979a3078c6ef80cfc382e67d88d4fc01e7c392dd Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Fri, 25 Oct 2024 15:30:07 +0100 -Subject: perf arm-spe: Use ARM_SPE_OP_BRANCH_ERET when synthesizing branches - -From: Graham Woodward - -[ Upstream commit c1b67c85108f99af0a80aa9e59a2b94ad95428d7 ] - -Instead of checking the type for just branch misses, we can instead -check for the OP_BRANCH_ERET and synthesise branches as well as -branch misses. - -Signed-off-by: Graham Woodward -Reviewed-by: James Clark -Tested-by: Leo Yan -Cc: nd@arm.com -Cc: mike.leach@linaro.org -Cc: linux-arm-kernel@lists.infradead.org -Link: https://lore.kernel.org/r/20241025143009.25419-3-graham.woodward@arm.com -Signed-off-by: Namhyung Kim -Stable-dep-of: 059e9100d82a ("perf event: Fix size of synthesized sample with branch stacks") -Signed-off-by: Sasha Levin ---- - tools/perf/util/arm-spe.c | 12 ++++++------ - 1 file changed, 6 insertions(+), 6 deletions(-) - -diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c -index fcacdd87c0d2b4..ce34cc5a2e7163 100644 ---- a/tools/perf/util/arm-spe.c -+++ b/tools/perf/util/arm-spe.c -@@ -71,7 +71,7 @@ struct arm_spe { - u64 llc_access_id; - u64 tlb_miss_id; - u64 tlb_access_id; -- u64 branch_miss_id; -+ u64 branch_id; - u64 remote_access_id; - u64 memory_id; - u64 instructions_id; -@@ -668,8 +668,8 @@ static int arm_spe_sample(struct arm_spe_queue *speq) - } - } - -- if (spe->sample_branch && (record->type & ARM_SPE_BRANCH_MISS)) { -- err = arm_spe__synth_branch_sample(speq, spe->branch_miss_id); -+ if (spe->sample_branch && (record->op & ARM_SPE_OP_BRANCH_ERET)) { -+ err = arm_spe__synth_branch_sample(speq, spe->branch_id); - if (err) - return err; - } -@@ -1269,12 +1269,12 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) - if (spe->synth_opts.branches) { - spe->sample_branch = true; - -- /* Branch miss */ -+ /* Branch */ - err = perf_session__deliver_synth_attr_event(session, &attr, id); - if (err) - return err; -- spe->branch_miss_id = id; -- arm_spe_set_event_name(evlist, id, "branch-miss"); -+ spe->branch_id = id; -+ arm_spe_set_event_name(evlist, id, "branch"); - id += 1; - } - --- -2.53.0 - diff --git a/queue-6.12/perf-arm-spe-use-metadata-to-decide-the-data-source-.patch b/queue-6.12/perf-arm-spe-use-metadata-to-decide-the-data-source-.patch deleted file mode 100644 index c1f82c4518..0000000000 --- a/queue-6.12/perf-arm-spe-use-metadata-to-decide-the-data-source-.patch +++ /dev/null @@ -1,124 +0,0 @@ -From 475db7c808a106211ccfd69a597980a561daf49f Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Thu, 3 Oct 2024 19:53:19 +0100 -Subject: perf arm-spe: Use metadata to decide the data source feature - -From: Leo Yan - -[ Upstream commit ba5e7169e5483a61899497e23fa18f7ef33aa827 ] - -Use the info in the metadata to decide if the data source feature is -supported. The CPU MIDR must be in the CPU list for the common data -source encoding. - -For the metadata version 1, it doesn't include info for MIDR. In this -case, due to absent info for making decision, print out warning to -remind users to upgrade tool and returns false. - -Signed-off-by: Leo Yan -Reviewed-by: James Clark -Link: https://lore.kernel.org/r/20241003185322.192357-5-leo.yan@arm.com -Signed-off-by: Namhyung Kim -Stable-dep-of: 059e9100d82a ("perf event: Fix size of synthesized sample with branch stacks") -Signed-off-by: Sasha Levin ---- - tools/perf/util/arm-spe.c | 67 +++++++++++++++++++++++++++++++++++++-- - 1 file changed, 64 insertions(+), 3 deletions(-) - -diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c -index 9890b17241c34a..fcacdd87c0d2b4 100644 ---- a/tools/perf/util/arm-spe.c -+++ b/tools/perf/util/arm-spe.c -@@ -275,6 +275,20 @@ static int arm_spe_set_tid(struct arm_spe_queue *speq, pid_t tid) - return 0; - } - -+static u64 *arm_spe__get_metadata_by_cpu(struct arm_spe *spe, u64 cpu) -+{ -+ u64 i; -+ -+ if (!spe->metadata) -+ return NULL; -+ -+ for (i = 0; i < spe->metadata_nr_cpu; i++) -+ if (spe->metadata[i][ARM_SPE_CPU] == cpu) -+ return spe->metadata[i]; -+ -+ return NULL; -+} -+ - static struct simd_flags arm_spe__synth_simd_flags(const struct arm_spe_record *record) - { - struct simd_flags simd_flags = {}; -@@ -517,10 +531,57 @@ static void arm_spe__synth_data_source_generic(const struct arm_spe_record *reco - data_src->mem_remote = PERF_MEM_REMOTE_REMOTE; - } - --static u64 arm_spe__synth_data_source(const struct arm_spe_record *record, u64 midr) -+static bool arm_spe__is_common_ds_encoding(struct arm_spe_queue *speq) -+{ -+ struct arm_spe *spe = speq->spe; -+ bool is_in_cpu_list; -+ u64 *metadata = NULL; -+ u64 midr = 0; -+ -+ /* -+ * Metadata version 1 doesn't contain any info for MIDR. -+ * Simply return false in this case. -+ */ -+ if (spe->metadata_ver == 1) { -+ pr_warning_once("The data file contains metadata version 1, " -+ "which is absent the info for data source. " -+ "Please upgrade the tool to record data.\n"); -+ return false; -+ } -+ -+ /* CPU ID is -1 for per-thread mode */ -+ if (speq->cpu < 0) { -+ /* -+ * On the heterogeneous system, due to CPU ID is -1, -+ * cannot confirm the data source packet is supported. -+ */ -+ if (!spe->is_homogeneous) -+ return false; -+ -+ /* In homogeneous system, simply use CPU0's metadata */ -+ if (spe->metadata) -+ metadata = spe->metadata[0]; -+ } else { -+ metadata = arm_spe__get_metadata_by_cpu(spe, speq->cpu); -+ } -+ -+ if (!metadata) -+ return false; -+ -+ midr = metadata[ARM_SPE_CPU_MIDR]; -+ -+ is_in_cpu_list = is_midr_in_range_list(midr, common_ds_encoding_cpus); -+ if (is_in_cpu_list) -+ return true; -+ else -+ return false; -+} -+ -+static u64 arm_spe__synth_data_source(struct arm_spe_queue *speq, -+ const struct arm_spe_record *record) - { - union perf_mem_data_src data_src = { .mem_op = PERF_MEM_OP_NA }; -- bool is_common = is_midr_in_range_list(midr, common_ds_encoding_cpus); -+ bool is_common = arm_spe__is_common_ds_encoding(speq); - - /* Only synthesize data source for LDST operations */ - if (!is_ldst_op(record->op)) -@@ -557,7 +618,7 @@ static int arm_spe_sample(struct arm_spe_queue *speq) - u64 data_src; - int err; - -- data_src = arm_spe__synth_data_source(record, spe->midr); -+ data_src = arm_spe__synth_data_source(speq, record); - - if (spe->sample_flc) { - if (record->type & ARM_SPE_L1D_MISS) { --- -2.53.0 - diff --git a/queue-6.12/perf-arm-spe-use-old-behavior-when-opening-old-spe-files.patch b/queue-6.12/perf-arm-spe-use-old-behavior-when-opening-old-spe-files.patch deleted file mode 100644 index 6701a20230..0000000000 --- a/queue-6.12/perf-arm-spe-use-old-behavior-when-opening-old-spe-files.patch +++ /dev/null @@ -1,100 +0,0 @@ -From ba993e5ada1ddce7a71140dc85ef65bc2cd981bc Mon Sep 17 00:00:00 2001 -From: James Clark -Date: Tue, 29 Oct 2024 14:37:33 +0000 -Subject: perf arm-spe: Use old behavior when opening old SPE files - -From: James Clark - -commit ba993e5ada1ddce7a71140dc85ef65bc2cd981bc upstream. - -Since the linked commit, we stopped interpreting data source if the -perf.data file doesn't have the new metadata version. This means that -perf c2c will show no samples in this case. - -Keep the old behavior so old files can be opened, but also still show -the new warning that updating might improve the decoding. - -Also re-write the warning to be more concise and specific to a user. - -Fixes: ba5e7169e548 ("perf arm-spe: Use metadata to decide the data source feature") -Signed-off-by: James Clark -Reviewed-by: Leo Yan -Cc: Julio.Suarez@arm.com -Cc: Kiel.Friedt@arm.com -Cc: Ryan.Roberts@arm.com -Cc: Will Deacon -Cc: Mike Leach -Cc: linux-arm-kernel@lists.infradead.org -Cc: Besar Wicaksono -Cc: John Garry -Link: https://lore.kernel.org/r/20241029143734.291638-1-james.clark@linaro.org -Signed-off-by: Namhyung Kim -Signed-off-by: Greg Kroah-Hartman ---- - tools/perf/util/arm-spe.c | 50 ++++++++++++++++++++++------------------------ - 1 file changed, 24 insertions(+), 26 deletions(-) - ---- a/tools/perf/util/arm-spe.c -+++ b/tools/perf/util/arm-spe.c -@@ -635,37 +635,35 @@ static bool arm_spe__is_common_ds_encodi - u64 *metadata = NULL; - u64 midr = 0; - -- /* -- * Metadata version 1 doesn't contain any info for MIDR. -- * Simply return false in this case. -- */ -+ /* Metadata version 1 assumes all CPUs are the same (old behavior) */ - if (spe->metadata_ver == 1) { -- pr_warning_once("The data file contains metadata version 1, " -- "which is absent the info for data source. " -- "Please upgrade the tool to record data.\n"); -- return false; -- } -- -- /* CPU ID is -1 for per-thread mode */ -- if (speq->cpu < 0) { -- /* -- * On the heterogeneous system, due to CPU ID is -1, -- * cannot confirm the data source packet is supported. -- */ -- if (!spe->is_homogeneous) -- return false; -+ const char *cpuid; - -- /* In homogeneous system, simply use CPU0's metadata */ -- if (spe->metadata) -- metadata = spe->metadata[0]; -+ pr_warning_once("Old SPE metadata, re-record to improve decode accuracy\n"); -+ cpuid = perf_env__cpuid(spe->session->evlist->env); -+ midr = strtol(cpuid, NULL, 16); - } else { -- metadata = arm_spe__get_metadata_by_cpu(spe, speq->cpu); -- } -+ /* CPU ID is -1 for per-thread mode */ -+ if (speq->cpu < 0) { -+ /* -+ * On the heterogeneous system, due to CPU ID is -1, -+ * cannot confirm the data source packet is supported. -+ */ -+ if (!spe->is_homogeneous) -+ return false; -+ -+ /* In homogeneous system, simply use CPU0's metadata */ -+ if (spe->metadata) -+ metadata = spe->metadata[0]; -+ } else { -+ metadata = arm_spe__get_metadata_by_cpu(spe, speq->cpu); -+ } - -- if (!metadata) -- return false; -+ if (!metadata) -+ return false; - -- midr = metadata[ARM_SPE_CPU_MIDR]; -+ midr = metadata[ARM_SPE_CPU_MIDR]; -+ } - - is_in_cpu_list = is_midr_in_range_list(midr, common_ds_encoding_cpus); - if (is_in_cpu_list) diff --git a/queue-6.12/perf-bpf-add-null-check-for-btf__type_by_id-in-synth.patch b/queue-6.12/perf-bpf-add-null-check-for-btf__type_by_id-in-synth.patch deleted file mode 100644 index 24bc65eec6..0000000000 --- a/queue-6.12/perf-bpf-add-null-check-for-btf__type_by_id-in-synth.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 04779f87f572c359f1d6f02092cb2332481df5eb Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Mon, 8 Jun 2026 08:10:43 -0300 -Subject: perf bpf: Add NULL check for btf__type_by_id() in - synthesize_bpf_prog_name() - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 903b0526dcf86d030c5970b4b0a67f9c227368e2 ] - -synthesize_bpf_prog_name() calls btf__type_by_id() and immediately -dereferences the result via t->name_off without checking for NULL. -btf__type_by_id() returns NULL when the type_id is invalid or out -of range. When processing perf.data files, finfo->type_id comes from -untrusted input, so an invalid ID causes a NULL pointer dereference. - -Fix by checking t for NULL before dereferencing. - -Reported-by: sashiko-bot -Fixes: fc462ac75b36daaa ("perf bpf: Extract logic to create program names from perf_event__synthesize_one_bpf_prog()") -Cc: Song Liu -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/bpf-event.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c -index 8919fc52eecd45..6c9a4a1b80a24c 100644 ---- a/tools/perf/util/bpf-event.c -+++ b/tools/perf/util/bpf-event.c -@@ -138,7 +138,8 @@ static int synthesize_bpf_prog_name(char *buf, int size, - if (btf) { - finfo = func_infos + sub_id * info->func_info_rec_size; - t = btf__type_by_id(btf, finfo->type_id); -- short_name = btf__name_by_offset(btf, t->name_off); -+ if (t) -+ short_name = btf__name_by_offset(btf, t->name_off); - } else if (sub_id == 0 && sub_prog_cnt == 1) { - /* no subprog */ - if (info->name[0]) --- -2.53.0 - diff --git a/queue-6.12/perf-bpf-bounds-check-array-offsets-in-bpil_offs_to_.patch b/queue-6.12/perf-bpf-bounds-check-array-offsets-in-bpil_offs_to_.patch deleted file mode 100644 index 6a0f3eb2ec..0000000000 --- a/queue-6.12/perf-bpf-bounds-check-array-offsets-in-bpil_offs_to_.patch +++ /dev/null @@ -1,69 +0,0 @@ -From d728900c519019491aaf8d451d942733edcf3615 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Wed, 10 Jun 2026 21:03:16 -0300 -Subject: perf bpf: Bounds-check array offsets in bpil_offs_to_addr() - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 033e85edfbf271f92979d2a39aeaf40f8472a795 ] - -bpil_offs_to_addr() converts offsets stored in perf.data's -bpf_prog_info_linear structure into heap pointers by adding the offset -to the data allocation base. The offsets come from untrusted file input -and are not validated against data_len. - -If an offset exceeds data_len, the computed address points outside the -allocated data buffer. Callers like synthesize_bpf_prog_name() then -dereference prog_tags[sub_id] or func_info pointers, reading arbitrary -heap memory. - -Add a bounds check: when an offset exceeds data_len, zero the field -and skip the conversion. This prevents out-of-bounds pointer -construction from crafted perf.data files. - -Reported-by: sashiko-bot -Fixes: 6ac22d036f86c4e2 ("perf bpf: Pull in bpf_program__get_prog_info_linear()") -Cc: Dave Marchevsky -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/bpf-utils.c | 16 ++++++++++++++++ - 1 file changed, 16 insertions(+) - -diff --git a/tools/perf/util/bpf-utils.c b/tools/perf/util/bpf-utils.c -index 5a66dc8594aa88..03a621a0c5dcd5 100644 ---- a/tools/perf/util/bpf-utils.c -+++ b/tools/perf/util/bpf-utils.c -@@ -264,12 +264,28 @@ void bpil_offs_to_addr(struct perf_bpil *info_linear) - for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { - const struct bpil_array_desc *desc = &bpil_array_desc[i]; - __u64 addr, offs; -+ __u32 count, size; - - if ((info_linear->arrays & (1UL << i)) == 0) - continue; - - offs = bpf_prog_info_read_offset_u64(&info_linear->info, - desc->array_offset); -+ count = bpf_prog_info_read_offset_u32(&info_linear->info, -+ desc->count_offset); -+ size = bpf_prog_info_read_offset_u32(&info_linear->info, -+ desc->size_offset); -+ /* offset and extent from perf.data are untrusted — keep within data[] */ -+ if (offs >= info_linear->data_len || -+ (u64)count * size > info_linear->data_len - offs) { -+ bpf_prog_info_set_offset_u64(&info_linear->info, -+ desc->array_offset, 0); -+ bpf_prog_info_set_offset_u32(&info_linear->info, -+ desc->count_offset, 0); -+ /* clear the bit so bpil_addr_to_offs() won't reverse a zeroed address */ -+ info_linear->arrays &= ~(1UL << i); -+ continue; -+ } - addr = offs + ptr_to_u64(info_linear->data); - bpf_prog_info_set_offset_u64(&info_linear->info, - desc->array_offset, addr); --- -2.53.0 - diff --git a/queue-6.12/perf-bpf-use-scnprintf-in-snprintf_hex-and-synthesiz.patch b/queue-6.12/perf-bpf-use-scnprintf-in-snprintf_hex-and-synthesiz.patch deleted file mode 100644 index 054d1f6a8e..0000000000 --- a/queue-6.12/perf-bpf-use-scnprintf-in-snprintf_hex-and-synthesiz.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 8733e9bbace794b8cd131440af75c81186fe1502 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sun, 7 Jun 2026 14:23:15 -0300 -Subject: perf bpf: Use scnprintf() in snprintf_hex() and - synthesize_bpf_prog_name() - -From: Arnaldo Carvalho de Melo - -[ Upstream commit cab3a9331ed0b3f884dd61c8a25b3cf123705982 ] - -Both functions accumulate formatted output via ret += snprintf(buf + ret, -size - ret, ...). If the buffer is too small and snprintf() returns more -than the remaining space, ret exceeds size and the next 'size - ret' -underflows, causing snprintf() to write past the buffer end. - -Switch to scnprintf() which returns the actual number of bytes written, -making the accumulation safe. - -Fixes: 7b612e291a5affb1 ("perf tools: Synthesize PERF_RECORD_* for loaded BPF programs") -Reported-by: sashiko-bot -Reviewed-by: Ian Rogers -Cc: Song Liu -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/bpf-event.c | 11 ++++++----- - 1 file changed, 6 insertions(+), 5 deletions(-) - -diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c -index d9123c9637baaf..8919fc52eecd45 100644 ---- a/tools/perf/util/bpf-event.c -+++ b/tools/perf/util/bpf-event.c -@@ -28,7 +28,7 @@ static int snprintf_hex(char *buf, size_t size, unsigned char *data, size_t len) - size_t i; - - for (i = 0; i < len; i++) -- ret += snprintf(buf + ret, size - ret, "%02x", data[i]); -+ ret += scnprintf(buf + ret, size - ret, "%02x", data[i]); - return ret; - } - -@@ -132,7 +132,7 @@ static int synthesize_bpf_prog_name(char *buf, int size, - const struct btf_type *t; - int name_len; - -- name_len = snprintf(buf, size, "bpf_prog_"); -+ name_len = scnprintf(buf, size, "bpf_prog_"); - name_len += snprintf_hex(buf + name_len, size - name_len, - prog_tags[sub_id], BPF_TAG_SIZE); - if (btf) { -@@ -145,9 +145,10 @@ static int synthesize_bpf_prog_name(char *buf, int size, - short_name = info->name; - } else - short_name = "F"; -- if (short_name) -- name_len += snprintf(buf + name_len, size - name_len, -- "_%s", short_name); -+ if (short_name) { -+ name_len += scnprintf(buf + name_len, size - name_len, -+ "_%s", short_name); -+ } - return name_len; - } - --- -2.53.0 - diff --git a/queue-6.12/perf-bpf-validate-func_info_rec_size-and-sub_id-in-s.patch b/queue-6.12/perf-bpf-validate-func_info_rec_size-and-sub_id-in-s.patch deleted file mode 100644 index 6f464875c5..0000000000 --- a/queue-6.12/perf-bpf-validate-func_info_rec_size-and-sub_id-in-s.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 8a9e06475407592b6ea1cfa70f569f373cdd6a39 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Wed, 10 Jun 2026 21:01:15 -0300 -Subject: perf bpf: Validate func_info_rec_size and sub_id in - synthesize_bpf_prog_name() - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 10b3c3d63ecc17c6acb855bac5f40367f1115765 ] - -synthesize_bpf_prog_name() computes a pointer into the func_info array -using sub_id * info->func_info_rec_size without validating either value. -Both come from perf.data and are untrusted: - -- A func_info_rec_size smaller than sizeof(struct bpf_func_info) means - the finfo pointer would reference a truncated entry, reading past it - into adjacent data. - -- A sub_id >= nr_func_info computes an offset past the func_info buffer, - causing an out-of-bounds read. - -Add bounds checks for both values before computing the pointer offset. -When validation fails, fall through to the non-BTF name path instead -of reading garbage. - -Reported-by: sashiko-bot -Fixes: 7b612e291a5affb1 ("perf tools: Synthesize PERF_RECORD_* for loaded BPF programs") -Cc: Song Liu -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/bpf-event.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c -index 6c9a4a1b80a24c..db89053ce96808 100644 ---- a/tools/perf/util/bpf-event.c -+++ b/tools/perf/util/bpf-event.c -@@ -135,7 +135,9 @@ static int synthesize_bpf_prog_name(char *buf, int size, - name_len = scnprintf(buf, size, "bpf_prog_"); - name_len += snprintf_hex(buf + name_len, size - name_len, - prog_tags[sub_id], BPF_TAG_SIZE); -- if (btf) { -+ if (btf && -+ info->func_info_rec_size >= sizeof(*finfo) && -+ sub_id < info->nr_func_info) { - finfo = func_infos + sub_id * info->func_info_rec_size; - t = btf__type_by_id(btf, finfo->type_id); - if (t) --- -2.53.0 - diff --git a/queue-6.12/perf-build-id-change-sprintf-functions-to-snprintf.patch b/queue-6.12/perf-build-id-change-sprintf-functions-to-snprintf.patch deleted file mode 100644 index daacf1c807..0000000000 --- a/queue-6.12/perf-build-id-change-sprintf-functions-to-snprintf.patch +++ /dev/null @@ -1,429 +0,0 @@ -From b68d22b74b85ac8084e7b59d6a88b4542ec75792 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Thu, 24 Jul 2025 09:32:43 -0700 -Subject: perf build-id: Change sprintf functions to snprintf - -From: Ian Rogers - -[ Upstream commit fccaaf6fbbc59910edcf276f97a5b2ef5778c55e ] - -Pass in a size argument rather than implying all build id strings must -be SBUILD_ID_SIZE. - -Signed-off-by: Ian Rogers -Link: https://lore.kernel.org/r/20250724163302.596743-4-irogers@google.com -[ fixed some build errors ] -Signed-off-by: Namhyung Kim -Stable-dep-of: 2ea64782a428 ("perf tools: Use snprintf() in dso__read_running_kernel_build_id()") -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-buildid-cache.c | 12 +++---- - tools/perf/builtin-buildid-list.c | 6 ++-- - tools/perf/tests/sdt.c | 2 +- - tools/perf/util/build-id.c | 33 ++++++++----------- - tools/perf/util/build-id.h | 6 ++-- - tools/perf/util/disasm.c | 2 +- - tools/perf/util/dso.c | 4 +-- - tools/perf/util/dsos.c | 2 +- - tools/perf/util/event.c | 2 +- - tools/perf/util/header.c | 2 +- - tools/perf/util/map.c | 2 +- - tools/perf/util/probe-event.c | 4 +-- - tools/perf/util/probe-file.c | 4 +-- - tools/perf/util/probe-finder.c | 2 +- - .../scripting-engines/trace-event-python.c | 7 ++-- - tools/perf/util/symbol.c | 2 +- - 16 files changed, 42 insertions(+), 50 deletions(-) - -diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c -index b0511d16aeb6c7..3f7739b21148bd 100644 ---- a/tools/perf/builtin-buildid-cache.c -+++ b/tools/perf/builtin-buildid-cache.c -@@ -31,7 +31,7 @@ - #include - #include - --static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid) -+static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid, size_t sbuildid_size) - { - char root_dir[PATH_MAX]; - char *p; -@@ -42,7 +42,7 @@ static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid) - if (!p) - return -1; - *p = '\0'; -- return sysfs__sprintf_build_id(root_dir, sbuildid); -+ return sysfs__snprintf_build_id(root_dir, sbuildid, sbuildid_size); - } - - static int build_id_cache__kcore_dir(char *dir, size_t sz) -@@ -128,7 +128,7 @@ static int build_id_cache__add_kcore(const char *filename, bool force) - return -1; - *p = '\0'; - -- if (build_id_cache__kcore_buildid(from_dir, sbuildid) < 0) -+ if (build_id_cache__kcore_buildid(from_dir, sbuildid, sizeof(sbuildid)) < 0) - return -1; - - scnprintf(to_dir, sizeof(to_dir), "%s/%s/%s", -@@ -187,7 +187,7 @@ static int build_id_cache__add_file(const char *filename, struct nsinfo *nsi) - return -1; - } - -- build_id__sprintf(&bid, sbuild_id); -+ build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); - err = build_id_cache__add_s(sbuild_id, filename, nsi, - false, false); - pr_debug("Adding %s %s: %s\n", sbuild_id, filename, -@@ -211,7 +211,7 @@ static int build_id_cache__remove_file(const char *filename, struct nsinfo *nsi) - return -1; - } - -- build_id__sprintf(&bid, sbuild_id); -+ build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); - err = build_id_cache__remove_s(sbuild_id); - pr_debug("Removing %s %s: %s\n", sbuild_id, filename, - err ? "FAIL" : "Ok"); -@@ -317,7 +317,7 @@ static int build_id_cache__update_file(const char *filename, struct nsinfo *nsi) - } - err = 0; - -- build_id__sprintf(&bid, sbuild_id); -+ build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); - if (build_id_cache__cached(sbuild_id)) - err = build_id_cache__remove_s(sbuild_id); - -diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c -index 52dfacaff8e39f..ba8ba0303920c5 100644 ---- a/tools/perf/builtin-buildid-list.c -+++ b/tools/perf/builtin-buildid-list.c -@@ -31,7 +31,7 @@ static int buildid__map_cb(struct map *map, void *arg __maybe_unused) - - memset(bid_buf, 0, sizeof(bid_buf)); - if (dso__has_build_id(dso)) -- build_id__sprintf(dso__bid_const(dso), bid_buf); -+ build_id__snprintf(dso__bid_const(dso), bid_buf, sizeof(bid_buf)); - printf("%s %16" PRIx64 " %16" PRIx64, bid_buf, map__start(map), map__end(map)); - if (dso_long_name != NULL) - printf(" %s", dso_long_name); -@@ -57,7 +57,7 @@ static int sysfs__fprintf_build_id(FILE *fp) - char sbuild_id[SBUILD_ID_SIZE]; - int ret; - -- ret = sysfs__sprintf_build_id("/", sbuild_id); -+ ret = sysfs__snprintf_build_id("/", sbuild_id, sizeof(sbuild_id)); - if (ret != sizeof(sbuild_id)) - return ret < 0 ? ret : -EINVAL; - -@@ -69,7 +69,7 @@ static int filename__fprintf_build_id(const char *name, FILE *fp) - char sbuild_id[SBUILD_ID_SIZE]; - int ret; - -- ret = filename__sprintf_build_id(name, sbuild_id); -+ ret = filename__snprintf_build_id(name, sbuild_id, sizeof(sbuild_id)); - if (ret != sizeof(sbuild_id)) - return ret < 0 ? ret : -EINVAL; - -diff --git a/tools/perf/tests/sdt.c b/tools/perf/tests/sdt.c -index 91971289925102..663c8f700069d4 100644 ---- a/tools/perf/tests/sdt.c -+++ b/tools/perf/tests/sdt.c -@@ -37,7 +37,7 @@ static int build_id_cache__add_file(const char *filename) - return err; - } - -- build_id__sprintf(&bid, sbuild_id); -+ build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); - err = build_id_cache__add_s(sbuild_id, filename, NULL, false, false); - if (err < 0) - pr_debug("Failed to add build id cache of %s\n", filename); -diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c -index ee00313d5d7e2a..ac642c758d5a17 100644 ---- a/tools/perf/util/build-id.c -+++ b/tools/perf/util/build-id.c -@@ -67,24 +67,17 @@ int build_id__mark_dso_hit(const struct perf_tool *tool __maybe_unused, - return 0; - } - --int build_id__sprintf(const struct build_id *build_id, char *bf) -+int build_id__snprintf(const struct build_id *build_id, char *bf, size_t bf_size) - { -- char *bid = bf; -- const u8 *raw = build_id->data; -- size_t i; -- -- bf[0] = 0x0; -+ size_t offs = 0; - -- for (i = 0; i < build_id->size; ++i) { -- sprintf(bid, "%02x", *raw); -- ++raw; -- bid += 2; -- } -+ for (size_t i = 0; i < build_id->size && offs < bf_size; ++i) -+ offs += snprintf(bf + offs, bf_size - offs, "%02x", build_id->data[i]); - -- return (bid - bf) + 1; -+ return offs; - } - --int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id) -+int sysfs__snprintf_build_id(const char *root_dir, char *sbuild_id, size_t sbuild_id_size) - { - char notes[PATH_MAX]; - struct build_id bid; -@@ -99,10 +92,10 @@ int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id) - if (ret < 0) - return ret; - -- return build_id__sprintf(&bid, sbuild_id); -+ return build_id__snprintf(&bid, sbuild_id, sbuild_id_size); - } - --int filename__sprintf_build_id(const char *pathname, char *sbuild_id) -+int filename__snprintf_build_id(const char *pathname, char *sbuild_id, size_t sbuild_id_size) - { - struct build_id bid; - int ret; -@@ -111,7 +104,7 @@ int filename__sprintf_build_id(const char *pathname, char *sbuild_id) - if (ret < 0) - return ret; - -- return build_id__sprintf(&bid, sbuild_id); -+ return build_id__snprintf(&bid, sbuild_id, sbuild_id_size); - } - - /* asnprintf consolidates asprintf and snprintf */ -@@ -212,9 +205,9 @@ static bool build_id_cache__valid_id(char *sbuild_id) - return false; - - if (!strcmp(pathname, DSO__NAME_KALLSYMS)) -- ret = sysfs__sprintf_build_id("/", real_sbuild_id); -+ ret = sysfs__snprintf_build_id("/", real_sbuild_id, sizeof(real_sbuild_id)); - else if (pathname[0] == '/') -- ret = filename__sprintf_build_id(pathname, real_sbuild_id); -+ ret = filename__snprintf_build_id(pathname, real_sbuild_id, sizeof(real_sbuild_id)); - else - ret = -EINVAL; /* Should we support other special DSO cache? */ - if (ret >= 0) -@@ -243,7 +236,7 @@ char *__dso__build_id_filename(const struct dso *dso, char *bf, size_t size, - if (!dso__has_build_id(dso)) - return NULL; - -- build_id__sprintf(dso__bid_const(dso), sbuild_id); -+ build_id__snprintf(dso__bid_const(dso), sbuild_id, sizeof(sbuild_id)); - linkname = build_id_cache__linkname(sbuild_id, NULL, 0); - if (!linkname) - return NULL; -@@ -769,7 +762,7 @@ static int build_id_cache__add_b(const struct build_id *bid, - { - char sbuild_id[SBUILD_ID_SIZE]; - -- build_id__sprintf(bid, sbuild_id); -+ build_id__snprintf(bid, sbuild_id, sizeof(sbuild_id)); - - return __build_id_cache__add_s(sbuild_id, name, nsi, is_kallsyms, - is_vdso, proper_name, root_dir); -diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h -index a212497bfdb0a5..37b8f00f8314c3 100644 ---- a/tools/perf/util/build-id.h -+++ b/tools/perf/util/build-id.h -@@ -21,10 +21,10 @@ struct feat_fd; - struct nsinfo; - - void build_id__init(struct build_id *bid, const u8 *data, size_t size); --int build_id__sprintf(const struct build_id *build_id, char *bf); -+int build_id__snprintf(const struct build_id *build_id, char *bf, size_t bf_size); - bool build_id__is_defined(const struct build_id *bid); --int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id); --int filename__sprintf_build_id(const char *pathname, char *sbuild_id); -+int sysfs__snprintf_build_id(const char *root_dir, char *sbuild_id, size_t sbuild_id_size); -+int filename__snprintf_build_id(const char *pathname, char *sbuild_id, size_t sbuild_id_size); - char *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf, - size_t size); - -diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c -index 8f35232f7f22c7..d8c27921332238 100644 ---- a/tools/perf/util/disasm.c -+++ b/tools/perf/util/disasm.c -@@ -1220,7 +1220,7 @@ int symbol__strerror_disassemble(struct map_symbol *ms, int errnum, char *buf, s - char *build_id_msg = NULL; - - if (dso__has_build_id(dso)) { -- build_id__sprintf(dso__bid(dso), bf + 15); -+ build_id__snprintf(dso__bid(dso), bf + 15, sizeof(bf) - 15); - build_id_msg = bf; - } - scnprintf(buf, buflen, -diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c -index 7c12d53656887d..aae754b1cc624a 100644 ---- a/tools/perf/util/dso.c -+++ b/tools/perf/util/dso.c -@@ -215,7 +215,7 @@ int dso__read_binary_type_filename(const struct dso *dso, - break; - } - -- build_id__sprintf(dso__bid_const(dso), build_id_hex); -+ build_id__snprintf(dso__bid_const(dso), build_id_hex, sizeof(build_id_hex)); - len = __symbol__join_symfs(filename, size, "/usr/lib/debug/.build-id/"); - snprintf(filename + len, size - len, "%.2s/%s.debug", - build_id_hex, build_id_hex + 2); -@@ -1694,7 +1694,7 @@ static size_t dso__fprintf_buildid(struct dso *dso, FILE *fp) - { - char sbuild_id[SBUILD_ID_SIZE]; - -- build_id__sprintf(dso__bid(dso), sbuild_id); -+ build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); - return fprintf(fp, "%s", sbuild_id); - } - -diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c -index e0998e2a7c4e75..b2172632b3cdd7 100644 ---- a/tools/perf/util/dsos.c -+++ b/tools/perf/util/dsos.c -@@ -370,7 +370,7 @@ static int dsos__fprintf_buildid_cb(struct dso *dso, void *data) - - if (args->skip && args->skip(dso, args->parm)) - return 0; -- build_id__sprintf(dso__bid(dso), sbuild_id); -+ build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); - args->ret += fprintf(args->fp, "%-40s %s\n", sbuild_id, dso__long_name(dso)); - return 0; - } -diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c -index 1e5085817e8250..ce6c70c1d3ee53 100644 ---- a/tools/perf/util/event.c -+++ b/tools/perf/util/event.c -@@ -330,7 +330,7 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) - - build_id__init(&bid, event->mmap2.build_id, - event->mmap2.build_id_size); -- build_id__sprintf(&bid, sbuild_id); -+ build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); - - return fprintf(fp, " %d/%d: [%#" PRI_lx64 "(%#" PRI_lx64 ") @ %#" PRI_lx64 - " <%s>]: %c%c%c%c %s\n", -diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c -index 8c15ce90466997..2d8eda9b614ef1 100644 ---- a/tools/perf/util/header.c -+++ b/tools/perf/util/header.c -@@ -2374,7 +2374,7 @@ static int __event_process_build_id(struct perf_record_header_build_id *bev, - free(m.name); - } - -- build_id__sprintf(dso__bid(dso), sbuild_id); -+ build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); - pr_debug("build id event received for %s: %s [%zu]\n", - dso__long_name(dso), sbuild_id, size); - dso__put(dso); -diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c -index d729438b7d6573..0f6b185f95896a 100644 ---- a/tools/perf/util/map.c -+++ b/tools/perf/util/map.c -@@ -354,7 +354,7 @@ int map__load(struct map *map) - if (dso__has_build_id(dso)) { - char sbuild_id[SBUILD_ID_SIZE]; - -- build_id__sprintf(dso__bid(dso), sbuild_id); -+ build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); - pr_debug("%s with build id %s not found", name, sbuild_id); - } else - pr_debug("Failed to open %s", name); -diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c -index a17c9b8a7a792d..72a71acf17c8c6 100644 ---- a/tools/perf/util/probe-event.c -+++ b/tools/perf/util/probe-event.c -@@ -498,7 +498,7 @@ static struct debuginfo *open_from_debuginfod(struct dso *dso, struct nsinfo *ns - if (!c) - return NULL; - -- build_id__sprintf(dso__bid(dso), sbuild_id); -+ build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); - fd = debuginfod_find_debuginfo(c, (const unsigned char *)sbuild_id, - 0, &path); - if (fd >= 0) -@@ -1071,7 +1071,7 @@ static int __show_line_range(struct line_range *lr, const char *module, - } - if (dinfo->build_id) { - build_id__init(&bid, dinfo->build_id, BUILD_ID_SIZE); -- build_id__sprintf(&bid, sbuild_id); -+ build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); - } - debuginfo__delete(dinfo); - if (ret == 0 || ret == -ENOENT) { -diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c -index 3d50de3217d50a..9ea0a838f59310 100644 ---- a/tools/perf/util/probe-file.c -+++ b/tools/perf/util/probe-file.c -@@ -467,10 +467,10 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target, - if (!target || !strcmp(target, DSO__NAME_KALLSYMS)) { - target = DSO__NAME_KALLSYMS; - is_kallsyms = true; -- ret = sysfs__sprintf_build_id("/", sbuildid); -+ ret = sysfs__snprintf_build_id("/", sbuildid, sizeof(sbuildid)); - } else { - nsinfo__mountns_enter(nsi, &nsc); -- ret = filename__sprintf_build_id(target, sbuildid); -+ ret = filename__snprintf_build_id(target, sbuildid, sizeof(sbuildid)); - nsinfo__mountns_exit(&nsc); - } - -diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c -index a30f88ed030044..cd6da777c16170 100644 ---- a/tools/perf/util/probe-finder.c -+++ b/tools/perf/util/probe-finder.c -@@ -848,7 +848,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) - comp_dir = cu_get_comp_dir(&pf->cu_die); - if (pf->dbg->build_id) { - build_id__init(&bid, pf->dbg->build_id, BUILD_ID_SIZE); -- build_id__sprintf(&bid, sbuild_id); -+ build_id__snprintf(&bid, sbuild_id, sizeof(sbuild_id)); - } - ret = find_source_path(pf->fname, sbuild_id, comp_dir, &fpath); - if (ret < 0) { -diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c -index d7183134b66993..780f7e720849ea 100644 ---- a/tools/perf/util/scripting-engines/trace-event-python.c -+++ b/tools/perf/util/scripting-engines/trace-event-python.c -@@ -795,14 +795,13 @@ static void set_sym_in_dict(PyObject *dict, struct addr_location *al, - const char *dso_map_start, const char *dso_map_end, - const char *sym_field, const char *symoff_field) - { -- char sbuild_id[SBUILD_ID_SIZE]; -- - if (al->map) { -+ char sbuild_id[SBUILD_ID_SIZE]; - struct dso *dso = map__dso(al->map); - - pydict_set_item_string_decref(dict, dso_field, - _PyUnicode_FromString(dso__name(dso))); -- build_id__sprintf(dso__bid(dso), sbuild_id); -+ build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); - pydict_set_item_string_decref(dict, dso_bid_field, - _PyUnicode_FromString(sbuild_id)); - pydict_set_item_string_decref(dict, dso_map_start, -@@ -1250,7 +1249,7 @@ static int python_export_dso(struct db_export *dbe, struct dso *dso, - char sbuild_id[SBUILD_ID_SIZE]; - PyObject *t; - -- build_id__sprintf(dso__bid(dso), sbuild_id); -+ build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); - - t = tuple_new(5); - -diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c -index 249de806f8e0d3..b1b4a2d4b7e46c 100644 ---- a/tools/perf/util/symbol.c -+++ b/tools/perf/util/symbol.c -@@ -2135,7 +2135,7 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map) - goto proc_kallsyms; - } - -- build_id__sprintf(dso__bid(dso), sbuild_id); -+ build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); - - /* Find kallsyms in build-id cache with kcore */ - scnprintf(path, sizeof(path), "%s/%s/%s", --- -2.53.0 - diff --git a/queue-6.12/perf-build-id-ensure-snprintf-string-is-empty-when-size-is-0.patch b/queue-6.12/perf-build-id-ensure-snprintf-string-is-empty-when-size-is-0.patch deleted file mode 100644 index c1112209da..0000000000 --- a/queue-6.12/perf-build-id-ensure-snprintf-string-is-empty-when-size-is-0.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 0dc96cae063cbf9ebf6631b33b08e9ba02324248 Mon Sep 17 00:00:00 2001 -From: Ian Rogers -Date: Thu, 18 Sep 2025 10:24:16 -0700 -Subject: perf build-id: Ensure snprintf string is empty when size is 0 - -From: Ian Rogers - -commit 0dc96cae063cbf9ebf6631b33b08e9ba02324248 upstream. - -The string result of build_id__snprintf() is unconditionally used in -places like dsos__fprintf_buildid_cb(). If the build id has size 0 then -this creates a use of uninitialized memory. Add null termination for the -size 0 case. - -A similar fix was written by Jiri Olsa in commit 6311951d4f8f28c4 ("perf -tools: Initialize output buffer in build_id__sprintf") but lost in the -transition to snprintf. - -Fixes: fccaaf6fbbc59910 ("perf build-id: Change sprintf functions to snprintf") -Signed-off-by: Ian Rogers -Cc: Adrian Hunter -Cc: Alexander Shishkin -Cc: Howard Chu -Cc: Ingo Molnar -Cc: James Clark -Cc: Jiri Olsa -Cc: Kan Liang -Cc: Mark Rutland -Cc: Namhyung Kim -Cc: Peter Zijlstra -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Greg Kroah-Hartman ---- - tools/perf/util/build-id.c | 7 +++++++ - 1 file changed, 7 insertions(+) - ---- a/tools/perf/util/build-id.c -+++ b/tools/perf/util/build-id.c -@@ -71,6 +71,13 @@ int build_id__snprintf(const struct buil - { - size_t offs = 0; - -+ if (build_id->size == 0) { -+ /* Ensure bf is always \0 terminated. */ -+ if (bf_size > 0) -+ bf[0] = '\0'; -+ return 0; -+ } -+ - for (size_t i = 0; i < build_id->size && offs < bf_size; ++i) - offs += snprintf(bf + offs, bf_size - offs, "%02x", build_id->data[i]); - diff --git a/queue-6.12/perf-build-id-fix-off-by-one-bug-when-printing-kernel-module-build-id.patch b/queue-6.12/perf-build-id-fix-off-by-one-bug-when-printing-kernel-module-build-id.patch deleted file mode 100644 index 4ad7ead505..0000000000 --- a/queue-6.12/perf-build-id-fix-off-by-one-bug-when-printing-kernel-module-build-id.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 017bca78e4d72b1ff027d368c20a1b2c654edaf7 Mon Sep 17 00:00:00 2001 -From: Michael Petlan -Date: Wed, 20 May 2026 00:38:55 +0200 -Subject: perf build-id: Fix off-by-one bug when printing kernel/module build-id - -From: Michael Petlan - -commit 017bca78e4d72b1ff027d368c20a1b2c654edaf7 upstream. - -When changing sprintf functions to snprintf, one byte got lost. Since -snprintf ones do not handle the '\0' terminating character, the number -of printed characters is 40, while sizeof(sbuild_id) is 41, including -the terminating '\0' character. - -This makes the later check fail so that nothing is printed. - -Fix that. - -Before: - - [Michael@Carbon ~]$ perf buildid-list -k - [Michael@Carbon ~]$ - -After: - - [Michael@Carbon ~]$ perf buildid-list -k - a527806324d543c4bc3ff2f9c9519d494fed5f68 - [Michael@Carbon ~]$ - -Fixes: fccaaf6fbbc59910 ("perf build-id: Change sprintf functions to snprintf") -Signed-off-by: Michael Petlan -Tested-by: Ian Rogers -Cc: Ian Rogers -Cc: Namhyung Kim -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Greg Kroah-Hartman ---- - tools/perf/builtin-buildid-list.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/tools/perf/builtin-buildid-list.c -+++ b/tools/perf/builtin-buildid-list.c -@@ -58,7 +58,7 @@ static int sysfs__fprintf_build_id(FILE - int ret; - - ret = sysfs__snprintf_build_id("/", sbuild_id, sizeof(sbuild_id)); -- if (ret != sizeof(sbuild_id)) -+ if (ret + 1 != sizeof(sbuild_id)) - return ret < 0 ? ret : -EINVAL; - - return fprintf(fp, "%s\n", sbuild_id); -@@ -70,7 +70,7 @@ static int filename__fprintf_build_id(co - int ret; - - ret = filename__snprintf_build_id(name, sbuild_id, sizeof(sbuild_id)); -- if (ret != sizeof(sbuild_id)) -+ if (ret + 1 != sizeof(sbuild_id)) - return ret < 0 ? ret : -EINVAL; - - return fprintf(fp, "%s\n", sbuild_id); diff --git a/queue-6.12/perf-c2c-bounds-check-cpu-and-node-ids-before-bitmap.patch b/queue-6.12/perf-c2c-bounds-check-cpu-and-node-ids-before-bitmap.patch deleted file mode 100644 index 8c716f321d..0000000000 --- a/queue-6.12/perf-c2c-bounds-check-cpu-and-node-ids-before-bitmap.patch +++ /dev/null @@ -1,96 +0,0 @@ -From 7f9eb9343a7e7e5e0c62b1a3a1e3299d46cc8b55 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Fri, 5 Jun 2026 11:05:13 -0300 -Subject: perf c2c: Bounds-check CPU and node IDs before bitmap and array - access - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 65117c3da50f749f4c22eb7a7effc53453dff57f ] - -c2c_he__set_cpu() passes sample->cpu directly to __set_bit(cpu, cpuset) -after only checking for the (u32)-1 sentinel. The cpuset bitmap is -allocated with c2c.cpus_cnt bits (from env->nr_cpus_avail), so a crafted -perf.data with CPU IDs exceeding that count causes out-of-bounds heap -writes. - -c2c_he__set_node() similarly passes the node ID from mem2node__node() -to __set_bit(node, nodeset) after only checking for negative values. -The nodeset bitmap is sized to c2c.nodes_cnt (from env->nr_numa_nodes), -so a node ID exceeding that causes OOB writes. - -process_sample_event() indexes c2c.cpu2node[cpu] and -c2c_he->node_stats[node] without bounds checking. Both arrays are -sized to c2c.cpus_cnt and c2c.nodes_cnt respectively. - -Add bounds checks in all three paths: - - c2c_he__set_cpu(): return if sample->cpu >= c2c.cpus_cnt - - c2c_he__set_node(): return if node >= c2c.nodes_cnt - - process_sample_event(): clamp cpu to 0 if >= cpus_cnt, - guard node_stats access with bounds check - -Fixes: 1e181b92a2da ("perf c2c report: Add 'node' sort key") -Reported-by: sashiko-bot -Cc: Jiri Olsa -Cc: Namhyung Kim -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-c2c.c | 19 +++++++++++++++++-- - 1 file changed, 17 insertions(+), 2 deletions(-) - -diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c -index 15e1fce71c7225..8dd371cb94ca5f 100644 ---- a/tools/perf/builtin-c2c.c -+++ b/tools/perf/builtin-c2c.c -@@ -230,6 +230,10 @@ static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, - "WARNING: no sample cpu value")) - return; - -+ /* cpuset bitmap has c2c.cpus_cnt bits from env->nr_cpus_avail */ -+ if (sample->cpu >= (unsigned int)c2c.cpus_cnt) -+ return; -+ - __set_bit(sample->cpu, c2c_he->cpuset); - } - -@@ -247,6 +251,10 @@ static void c2c_he__set_node(struct c2c_hist_entry *c2c_he, - if (WARN_ONCE(node < 0, "WARNING: failed to find node\n")) - return; - -+ /* nodeset bitmap has c2c.nodes_cnt bits from env->nr_numa_nodes */ -+ if (node >= c2c.nodes_cnt) -+ return; -+ - __set_bit(node, c2c_he->nodeset); - - if (c2c_he->paddr != sample->phys_addr) { -@@ -346,7 +354,12 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, - * Doing node stats only for single callchain data. - */ - int cpu = sample->cpu == (unsigned int) -1 ? 0 : sample->cpu; -- int node = c2c.cpu2node[cpu]; -+ int node; -+ -+ /* cpu2node[] has c2c.cpus_cnt entries; large u32 wraps signed negative */ -+ if (cpu < 0 || cpu >= c2c.cpus_cnt) -+ cpu = 0; -+ node = c2c.cpu2node[cpu]; - - mi = mi_dup; - -@@ -363,7 +376,9 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, - c2c_he = container_of(he, struct c2c_hist_entry, he); - c2c_add_stats(&c2c_he->stats, &stats); - c2c_add_stats(&c2c_hists->stats, &stats); -- c2c_add_stats(&c2c_he->node_stats[node], &stats); -+ /* node_stats[] has c2c.nodes_cnt entries */ -+ if (node >= 0 && node < c2c.nodes_cnt) -+ c2c_add_stats(&c2c_he->node_stats[node], &stats); - - compute_stats(c2c_he, &stats, sample->weight); - --- -2.53.0 - diff --git a/queue-6.12/perf-c2c-bounds-check-cpu-ids-in-setup_nodes-topolog.patch b/queue-6.12/perf-c2c-bounds-check-cpu-ids-in-setup_nodes-topolog.patch deleted file mode 100644 index e39161c4fe..0000000000 --- a/queue-6.12/perf-c2c-bounds-check-cpu-ids-in-setup_nodes-topolog.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 516cd92913df6b15f4920ae05f05b51f761786a0 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Fri, 5 Jun 2026 11:06:30 -0300 -Subject: perf c2c: Bounds-check CPU IDs in setup_nodes() topology loop - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 5fb2e6ad8c5d6b3b380f94c7456595511f3731be ] - -setup_nodes() iterates CPU maps from the perf.data topology header and -uses cpu.cpu directly as an array index into cpu2node[] (allocated with -c2c.cpus_cnt = env->nr_cpus_avail entries) and __set_bit(cpu.cpu, set) -(bitmap also sized to c2c.cpus_cnt). - -A crafted perf.data with topology CPU IDs exceeding nr_cpus_avail causes -out-of-bounds heap writes into both the cpu2node array and the per-node -bitmap. - -Add a bounds check to skip CPU IDs that fall outside the valid range. - -Fixes: 1e181b92a2da ("perf c2c report: Add 'node' sort key") -Reported-by: sashiko-bot -Cc: Jiri Olsa -Cc: Namhyung Kim -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-c2c.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c -index 8dd371cb94ca5f..9b1482786d81cd 100644 ---- a/tools/perf/builtin-c2c.c -+++ b/tools/perf/builtin-c2c.c -@@ -2318,6 +2318,10 @@ static int setup_nodes(struct perf_session *session) - nodes[node] = set; - - perf_cpu_map__for_each_cpu_skip_any(cpu, idx, map) { -+ /* topology CPU IDs from perf.data may exceed nr_cpus_avail */ -+ if (cpu.cpu < 0 || cpu.cpu >= c2c.cpus_cnt) -+ continue; -+ - __set_bit(cpu.cpu, set); - - if (WARN_ONCE(cpu2node[cpu.cpu] != -1, "node/cpu topology bug")) --- -2.53.0 - diff --git a/queue-6.12/perf-c2c-fix-use-after-free-in-he__get_c2c_hists-err.patch b/queue-6.12/perf-c2c-fix-use-after-free-in-he__get_c2c_hists-err.patch deleted file mode 100644 index c9abf0d15f..0000000000 --- a/queue-6.12/perf-c2c-fix-use-after-free-in-he__get_c2c_hists-err.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 108a7a9016771f2dd391beece48df21d5761d3ec Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 6 Jun 2026 11:19:10 -0300 -Subject: perf c2c: Fix use-after-free in he__get_c2c_hists() error path - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 5e5e6196d737c5be03d20647428316b36621608d ] - -he__get_c2c_hists() assigns c2c_he->hists before calling -c2c_hists__init(). If init fails, the error path calls free(hists) -but leaves c2c_he->hists pointing to freed memory. On teardown, -c2c_he_free() finds the non-NULL pointer and calls -hists__delete_entries() on it, causing a use-after-free. - -Set c2c_he->hists to NULL before freeing so teardown skips the -already-freed allocation. - -Fixes: b2252ae67b687d2b ("perf c2c report: Decode c2c_stats for hist entries") -Reported-by: sashiko-bot -Cc: Jiri Olsa -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-c2c.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c -index 9b1482786d81cd..b02b41059730f9 100644 ---- a/tools/perf/builtin-c2c.c -+++ b/tools/perf/builtin-c2c.c -@@ -216,6 +216,7 @@ he__get_c2c_hists(struct hist_entry *he, - - ret = c2c_hists__init(hists, sort, nr_header_lines); - if (ret) { -+ c2c_he->hists = NULL; - free(hists); - return NULL; - } --- -2.53.0 - diff --git a/queue-6.12/perf-callchain-handle-multiple-address-spaces.patch b/queue-6.12/perf-callchain-handle-multiple-address-spaces.patch deleted file mode 100644 index 290c854c27..0000000000 --- a/queue-6.12/perf-callchain-handle-multiple-address-spaces.patch +++ /dev/null @@ -1,150 +0,0 @@ -From ae15db3e9b639491007cc1e9e99638e4b6091781 Mon Sep 17 00:00:00 2001 -From: Thomas Richter -Date: Tue, 14 Apr 2026 14:42:41 +0200 -Subject: perf callchain: Handle multiple address spaces - -From: Thomas Richter - -commit ae15db3e9b639491007cc1e9e99638e4b6091781 upstream. - -perf test 'perf inject to convert DWARF callchains to regular ones' -fails on s390. It was introduced with commit 92ea788d2af4e65a ("perf -inject: Add --convert-callchain option") - -The failure comes the difference in output. Without the inject script to -convert DWARF the callchains is: - - # perf record -F 999 --call-graph dwarf -- perf test -w noploop - # perf report -i perf.data --stdio --no-children -q \ - --percent-limit=1 > /tmp/111 - # cat /tmp/111 - 99.30% perf-noploop perf [.] noploop - | - ---noploop - run_workload (inlined) - cmd_test - run_builtin (inlined) - handle_internal_command - run_argv (inlined) - main - __libc_start_call_main - __libc_start_main_impl (inlined) - _start - # - -With the inject script step the output is: - - # perf inject -i perf.data --convert-callchain -o /tmp/perf-inject-1.out - # perf report -i /tmp/perf-inject-1.out --stdio --no-children -q \ - --percent-limit=1 > /tmp/222 - # cat /tmp/222 - 99.40% perf-noploop perf [.] noploop - | - ---noploop - run_workload (inlined) - cmd_test - run_builtin (inlined) - handle_internal_command - run_argv (inlined) - main - _start - # diff /tmp/111 /tmp/222 - 1c1 - < 99.30% perf-noploop perf [.] noploop - --- - > 99.40% perf-noploop perf [.] noploop - 10,11d9 - < __libc_start_call_main - < __libc_start_main_impl (inlined) - # - -The difference are the symbols __libc_start_call_main and -__libc_start_main_impl. - -On x86_64, kernel and user space share a single virtual address space, -with the kernel mapped to the upper end of memory. The instruction -pointer value alone is sufficient to distinguish between user space and -kernel space addresses. - -This is not true for s390, which uses separate address spaces for user -and kernel. - -The same virtual address can be valid in both address spaces, so the -instruction pointer value alone cannot determine whether an address -belongs to the kernel or user space. - -Instead, perf must rely on the cpumode metadata derived from the -processor status word (PSW) at sample time. - -In function perf_event__convert_sample_callchain() the first part -copies a kernel callchain and context entries, if any. - -It then appends additional entries ignoring the address space -architecture. Taking that into account, the symbols at addresses - - 0x3ff970348cb __libc_start_call_main - 0x3ff970349c5 __libc_start_main_impl - -(located after the kernel address space on s390) are now included. - -Output before: - - # perf test 83 - 83: perf inject to convert DWARF callchains to regular ones : FAILED! - -Output after: - # perf test 83 - 83: perf inject to convert DWARF callchains to regular ones : Ok - -Question to Namhyung: - -In function perf_event__convert_sample_callchain() just before the -for() loop this patch modifies, the kernel callchain is copied, -see this comment and the next 5 lines: - - /* copy kernel callchain and context entries */ - -Then why is machine__kernel_ip() needed in the for() loop, when -the kernel entries have been copied just before the loop? - -Note: This patch was tested on x86_64 virtual machine and succeeded. - -Fixes: 92ea788d2af4e65a ("perf inject: Add --convert-callchain option") -Signed-off-by: Thomas Richter -Acked-by: Namhyung Kim -Cc: Alexander Gordeev -Cc: Heiko Carstens -Cc: Jan Polensky -Cc: linux-s390@vger.kernel.org -Cc: Sumanth Korikkar -Cc: Vasily Gorbik -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Greg Kroah-Hartman ---- - tools/perf/arch/common.c | 4 +++- - tools/perf/builtin-inject.c | 3 ++- - 2 files changed, 5 insertions(+), 2 deletions(-) - ---- a/tools/perf/arch/common.c -+++ b/tools/perf/arch/common.c -@@ -237,5 +237,7 @@ int perf_env__lookup_objdump(struct perf - */ - bool perf_env__single_address_space(struct perf_env *env) - { -- return strcmp(perf_env__arch(env), "sparc"); -+ const char *arch = perf_env__arch(env); -+ -+ return strcmp(arch, "s390") && strcmp(arch, "sparc"); - } ---- a/tools/perf/builtin-inject.c -+++ b/tools/perf/builtin-inject.c -@@ -452,7 +452,8 @@ static int perf_event__convert_sample_ca - - node = cursor->first; - for (k = 0; k < cursor->nr && i < PERF_MAX_STACK_DEPTH; k++) { -- if (machine__kernel_ip(machine, node->ip)) -+ if (machine->single_address_space && -+ machine__kernel_ip(machine, node->ip)) - /* kernel IPs were added already */; - else if (node->ms.sym && node->ms.sym->inlined) - /* we can't handle inlined callchains */; diff --git a/queue-6.12/perf-cs-etm-bounds-check-cpu-in-cs_etm__get_queue.patch b/queue-6.12/perf-cs-etm-bounds-check-cpu-in-cs_etm__get_queue.patch deleted file mode 100644 index 352add5eaf..0000000000 --- a/queue-6.12/perf-cs-etm-bounds-check-cpu-in-cs_etm__get_queue.patch +++ /dev/null @@ -1,117 +0,0 @@ -From c2b8092feb71592b928681be3b68c7626c07a38e Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 13 Jun 2026 14:55:32 -0300 -Subject: perf cs-etm: Bounds-check CPU in cs_etm__get_queue() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 9a989e60cc6e29d98aed2087425cba53bf4b392d ] - -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: 77c123f53e97ad4b ("perf: cs-etm: Move traceid_list to each queue") -Reported-by: sashiko-bot -Cc: Adrian Hunter -Cc: James Clark -Cc: Leo Yan -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/cs-etm.c | 25 +++++++++++++++++++++++-- - 1 file changed, 23 insertions(+), 2 deletions(-) - ---- a/tools/perf/util/cs-etm.c -+++ b/tools/perf/util/cs-etm.c -@@ -292,8 +292,11 @@ static struct cs_etm_queue *cs_etm__get_ - { - 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(struc - * 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(struc - 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 - 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 - 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 - } - - 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; -@@ -3128,6 +3146,9 @@ static int cs_etm__queue_aux_fragment(st - aux_offset + aux_size <= auxtrace_event->offset + auxtrace_event->size) { - struct cs_etm_queue *etmq = etm->queues.queue_array[auxtrace_event->idx].priv; - -+ 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. diff --git a/queue-6.12/perf-cs-etm-queue-context-packets-for-frontend.patch b/queue-6.12/perf-cs-etm-queue-context-packets-for-frontend.patch deleted file mode 100644 index cbdf200a1d..0000000000 --- a/queue-6.12/perf-cs-etm-queue-context-packets-for-frontend.patch +++ /dev/null @@ -1,716 +0,0 @@ -From 05f90ec3fd0040a17a2967ff06eb10a9cc543b8f Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Tue, 9 Jun 2026 15:40:06 +0100 -Subject: perf cs-etm: Queue context packets for frontend - -From: James Clark - -[ Upstream commit 68ca50bc0fa64841cd73b8a1538df1d7f7eb4108 ] - -PE_CONTEXT elements update the context ID and exception level, but the -decoder may still have prior packets cached for frontend processing. -Updating the context immediately in the decoder backend can make those -cached packets get consumed with the wrong thread or EL state. - -Add a CS_ETM_CONTEXT packet carrying the TID and EL to the frontend, -this keeps context changes ordered with the rest of the packet stream -and avoids mismatches when synthesizing samples from cached packets. - -Separate the memory access function into one for the frontend and one -for decoding. The frontend also needs memory access to attach the -instruction to samples. Because the frontend does memory access for -both previous and current packets, change all the frontend memory access -function signatures to take both a tidq and packet. But backend always -uses the current backend EL and thread from the tidq. - -Treat context packets as a boundary for branch sample generation and -remove tidq->prev_packet_thread because it's not possible to branch to a -different thread, so only tracking the current thread is required for -sample generation. - -Fixes: e573e978fb12e160 ("perf cs-etm: Inject capabilitity for CoreSight traces") -Reported-by: Amir Ayupov -Closes: https://lore.kernel.org/linux-perf-users/20260515021135.1729028-1-aaupov@meta.com/ -Co-authored-by: James Clark -Signed-off-by: Leo Yan -Cc: Ian Rogers -Cc: Jiri Olsa -Cc: Jonathan Corbet -Cc: linux-doc@vger.kernel.org -Cc: Mike Leach -Cc: Namhyung Kim -Cc: Paschalis Mpeis -Cc: Robert Walker -Cc: Shuah Khan -Cc: Suzuki Poulouse -Signed-off-by: James Clark -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/cs-etm-decoder/cs-etm-decoder.c | 21 +- - tools/perf/util/cs-etm.c | 236 ++++++++++++++---------- - tools/perf/util/cs-etm.h | 8 - 3 files changed, 163 insertions(+), 102 deletions(-) - ---- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c -+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c -@@ -399,6 +399,8 @@ cs_etm_decoder__buffer_packet(struct cs_ - packet_queue->packet_buffer[et].flags = 0; - packet_queue->packet_buffer[et].exception_number = UINT32_MAX; - packet_queue->packet_buffer[et].trace_chan_id = trace_chan_id; -+ packet_queue->packet_buffer[et].el = ocsd_EL_unknown; -+ packet_queue->packet_buffer[et].tid = -1; - - if (packet_queue->packet_count == CS_ETM_PACKET_MAX_BUFFER - 1) - return OCSD_RESP_WAIT; -@@ -446,6 +448,7 @@ cs_etm_decoder__buffer_range(struct cs_e - packet->last_instr_type = elem->last_i_type; - packet->last_instr_subtype = elem->last_i_subtype; - packet->last_instr_cond = elem->last_instr_cond; -+ packet->el = elem->context.exception_level; - - if (elem->last_i_type == OCSD_INSTR_BR || elem->last_i_type == OCSD_INSTR_BR_INDIRECT) - packet->last_instr_taken_branch = elem->last_instr_exec; -@@ -522,7 +525,9 @@ cs_etm_decoder__set_tid(struct cs_etm_qu - const ocsd_generic_trace_elem *elem, - const uint8_t trace_chan_id) - { -+ struct cs_etm_packet *packet; - pid_t tid = -1; -+ int ret; - - /* - * Process the PE_CONTEXT packets if we have a valid contextID or VMID. -@@ -543,12 +548,18 @@ cs_etm_decoder__set_tid(struct cs_etm_qu - break; - } - -- if (cs_etm__etmq_set_tid_el(etmq, tid, trace_chan_id, -- elem->context.exception_level)) -+ if (cs_etm__etmq_update_decode_context(etmq, trace_chan_id, -+ elem->context.exception_level, tid)) - return OCSD_RESP_FATAL_SYS_ERR; - -- if (tid == -1) -- return OCSD_RESP_CONT; -+ ret = cs_etm_decoder__buffer_packet(etmq, packet_queue, trace_chan_id, -+ CS_ETM_CONTEXT); -+ if (ret != OCSD_RESP_CONT && ret != OCSD_RESP_WAIT) -+ return ret; -+ -+ packet = &packet_queue->packet_buffer[packet_queue->tail]; -+ packet->tid = tid; -+ packet->el = elem->context.exception_level; - - /* - * A timestamp is generated after a PE_CONTEXT element so make sure -@@ -556,7 +567,7 @@ cs_etm_decoder__set_tid(struct cs_etm_qu - */ - cs_etm_decoder__reset_timestamp(packet_queue); - -- return OCSD_RESP_CONT; -+ return ret; - } - - static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer( ---- a/tools/perf/util/cs-etm.c -+++ b/tools/perf/util/cs-etm.c -@@ -86,15 +86,22 @@ struct cs_etm_traceid_queue { - u64 period_instructions; - size_t last_branch_pos; - union perf_event *event_buf; -- struct thread *thread; -- struct thread *prev_packet_thread; -- ocsd_ex_level prev_packet_el; -- ocsd_ex_level el; - struct branch_stack *last_branch; - struct branch_stack *last_branch_rb; - struct cs_etm_packet *prev_packet; - struct cs_etm_packet *packet; - struct cs_etm_packet_queue packet_queue; -+ -+ struct thread *decode_thread; -+ ocsd_ex_level decode_el; -+ -+ /* -+ * The frontend accesses the EL from '[prev_]packet' because it needs -+ * previous EL for branch and current EL for instruction samples. It's -+ * not possible to change thread in a single branch sample so no need to -+ * store or access the thread through the packet. -+ */ -+ struct thread *frontend_thread; - }; - - enum cs_etm_format { -@@ -608,10 +615,11 @@ static int cs_etm__init_traceid_queue(st - - queue = &etmq->etm->queues.queue_array[etmq->queue_nr]; - tidq->trace_chan_id = trace_chan_id; -- tidq->el = tidq->prev_packet_el = ocsd_EL_unknown; -- tidq->thread = machine__findnew_thread(&etm->session->machines.host, -1, -+ tidq->decode_el = ocsd_EL_unknown; -+ tidq->frontend_thread = machine__findnew_thread(&etm->session->machines.host, -1, -+ queue->tid); -+ tidq->decode_thread = machine__findnew_thread(&etm->session->machines.host, -1, - queue->tid); -- tidq->prev_packet_thread = machine__idle_thread(&etm->session->machines.host); - - tidq->packet = zalloc(sizeof(struct cs_etm_packet)); - if (!tidq->packet) -@@ -744,21 +752,10 @@ static void cs_etm__packet_swap(struct c - /* - * Swap PACKET with PREV_PACKET: PACKET becomes PREV_PACKET for - * the next incoming packet. -- * -- * Threads and exception levels are also tracked for both the -- * previous and current packets. This is because the previous -- * packet is used for the 'from' IP for branch samples, so the -- * thread at that time must also be assigned to that sample. -- * Across discontinuity packets the thread can change, so by -- * tracking the thread for the previous packet the branch sample -- * will have the correct info. - */ - tmp = tidq->packet; - tidq->packet = tidq->prev_packet; - tidq->prev_packet = tmp; -- tidq->prev_packet_el = tidq->el; -- thread__put(tidq->prev_packet_thread); -- tidq->prev_packet_thread = thread__get(tidq->thread); - } - } - -@@ -931,8 +928,8 @@ static void cs_etm__free_traceid_queues( - - /* Free this traceid_queue from the array */ - tidq = etmq->traceid_queues[idx]; -- thread__zput(tidq->thread); -- thread__zput(tidq->prev_packet_thread); -+ thread__zput(tidq->frontend_thread); -+ thread__zput(tidq->decode_thread); - zfree(&tidq->event_buf); - zfree(&tidq->last_branch); - zfree(&tidq->last_branch_rb); -@@ -1077,47 +1074,43 @@ static u8 cs_etm__cpu_mode(struct cs_etm - } - } - --static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, -- u64 address, size_t size, u8 *buffer, -- const ocsd_mem_space_acc_t mem_space) -+static u32 __cs_etm__mem_access(struct cs_etm_queue *etmq, -+ u64 address, size_t size, u8 *buffer, -+ const ocsd_mem_space_acc_t mem_space, -+ ocsd_ex_level el, struct thread *thread) - { - u8 cpumode; - u64 offset; - int len; - struct addr_location al; - struct dso *dso; -- struct cs_etm_traceid_queue *tidq; - int ret = 0; - - if (!etmq) - return 0; - - addr_location__init(&al); -- tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); -- if (!tidq) -- goto out; - - /* -- * We've already tracked EL along side the PID in cs_etm__set_thread() -- * so double check that it matches what OpenCSD thinks as well. It -- * doesn't distinguish between EL0 and EL1 for this mem access callback -- * so we had to do the extra tracking. Skip validation if it's any of -- * the 'any' values. -+ * We track EL for the frontend and the backend when receiving context -+ * and range packets. OpenCSD doesn't distinguish between EL0 and EL1 -+ * for this mem access callback so we had to do the extra tracking. Skip -+ * validation if it's any of the 'any' values. - */ - if (!(mem_space == OCSD_MEM_SPACE_ANY || - mem_space == OCSD_MEM_SPACE_N || mem_space == OCSD_MEM_SPACE_S)) { - if (mem_space & OCSD_MEM_SPACE_EL1N) { - /* Includes both non secure EL1 and EL0 */ -- assert(tidq->el == ocsd_EL1 || tidq->el == ocsd_EL0); -+ assert(el == ocsd_EL1 || el == ocsd_EL0); - } else if (mem_space & OCSD_MEM_SPACE_EL2) -- assert(tidq->el == ocsd_EL2); -+ assert(el == ocsd_EL2); - else if (mem_space & OCSD_MEM_SPACE_EL3) -- assert(tidq->el == ocsd_EL3); -+ assert(el == ocsd_EL3); - } - -- cpumode = cs_etm__cpu_mode(etmq, address, tidq->el); -+ cpumode = cs_etm__cpu_mode(etmq, address, el); - -- if (!thread__find_map(tidq->thread, cpumode, address, &al)) -+ if (!thread__find_map(thread, cpumode, address, &al)) - goto out; - - dso = map__dso(al.map); -@@ -1132,7 +1125,7 @@ static u32 cs_etm__mem_access(struct cs_ - - map__load(al.map); - -- len = dso__data_read_offset(dso, maps__machine(thread__maps(tidq->thread)), -+ len = dso__data_read_offset(dso, maps__machine(thread__maps(thread)), - offset, buffer, size); - - if (len <= 0) { -@@ -1152,6 +1145,30 @@ out: - return ret; - } - -+static u32 cs_etm__frontend_mem_access(struct cs_etm_queue *etmq, -+ struct cs_etm_traceid_queue *tidq, -+ struct cs_etm_packet *packet, -+ u64 address, size_t size, u8 *buffer) -+{ -+ return __cs_etm__mem_access(etmq, address, size, buffer, 0, packet->el, -+ tidq->frontend_thread); -+} -+ -+static u32 cs_etm__decoder_mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, -+ u64 address, size_t size, u8 *buffer, -+ const ocsd_mem_space_acc_t mem_space) -+{ -+ struct cs_etm_traceid_queue *tidq; -+ -+ tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); -+ if (!tidq) -+ return 0; -+ -+ return __cs_etm__mem_access(etmq, address, size, buffer, -+ mem_space, tidq->decode_el, -+ tidq->decode_thread); -+} -+ - static struct cs_etm_queue *cs_etm__alloc_queue(void) - { - struct cs_etm_queue *etmq = zalloc(sizeof(*etmq)); -@@ -1327,12 +1344,13 @@ void cs_etm__reset_last_branch_rb(struct - } - - static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq, -- u8 trace_chan_id, u64 addr) -+ struct cs_etm_traceid_queue *tidq, -+ struct cs_etm_packet *packet, u64 addr) - { - u8 instrBytes[2]; - -- cs_etm__mem_access(etmq, trace_chan_id, addr, ARRAY_SIZE(instrBytes), -- instrBytes, 0); -+ cs_etm__frontend_mem_access(etmq, tidq, packet, addr, -+ ARRAY_SIZE(instrBytes), instrBytes); - /* - * T32 instruction size is indicated by bits[15:11] of the first - * 16-bit word of the instruction: 0b11101, 0b11110 and 0b11111 -@@ -1365,16 +1383,16 @@ u64 cs_etm__last_executed_instr(const st - } - - static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq, -- u64 trace_chan_id, -- const struct cs_etm_packet *packet, -+ struct cs_etm_traceid_queue *tidq, -+ struct cs_etm_packet *packet, - u64 offset) - { - if (packet->isa == CS_ETM_ISA_T32) { - u64 addr = packet->start_addr; - - while (offset) { -- addr += cs_etm__t32_instr_size(etmq, -- trace_chan_id, addr); -+ addr += cs_etm__t32_instr_size(etmq, tidq, packet, -+ addr); - offset--; - } - return addr; -@@ -1466,34 +1484,51 @@ cs_etm__get_trace(struct cs_etm_queue *e - return etmq->buf_len; - } - --static void cs_etm__set_thread(struct cs_etm_queue *etmq, -- struct cs_etm_traceid_queue *tidq, pid_t tid, -- ocsd_ex_level el) -+/* -+ * Convert a raw thread number to a thread struct and assign it to **thread. -+ */ -+static int cs_etm__etmq_update_thread(struct cs_etm_queue *etmq, -+ ocsd_ex_level el, pid_t tid, -+ struct thread **thread) - { - struct machine *machine = cs_etm__get_machine(etmq, el); - -+ if (!machine || !*thread) -+ return -EINVAL; -+ - if (tid != -1) { -- thread__zput(tidq->thread); -- tidq->thread = machine__find_thread(machine, -1, tid); -+ thread__zput(*thread); -+ *thread = machine__find_thread(machine, -1, tid); - } - - /* Couldn't find a known thread */ -- if (!tidq->thread) -- tidq->thread = machine__idle_thread(machine); -+ if (!*thread) -+ *thread = machine__idle_thread(machine); - -- tidq->el = el; -+ return 0; - } - --int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid, -- u8 trace_chan_id, ocsd_ex_level el) -+/* -+ * Set the thread and EL of the decode context which is ahead in time of the -+ * frontend context. -+ */ -+int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq, -+ u8 trace_chan_id, -+ ocsd_ex_level el, pid_t tid) - { - struct cs_etm_traceid_queue *tidq; -+ int ret; - - tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); - if (!tidq) - return -EINVAL; - -- cs_etm__set_thread(etmq, tidq, tid, el); -+ ret = cs_etm__etmq_update_thread(etmq, el, tid, -+ &tidq->decode_thread); -+ if (ret) -+ return ret; -+ -+ tidq->decode_el = el; - return 0; - } - -@@ -1503,8 +1538,8 @@ bool cs_etm__etmq_is_timeless(struct cs_ - } - - static void cs_etm__copy_insn(struct cs_etm_queue *etmq, -- u64 trace_chan_id, -- const struct cs_etm_packet *packet, -+ struct cs_etm_traceid_queue *tidq, -+ struct cs_etm_packet *packet, - struct perf_sample *sample) - { - /* -@@ -1521,14 +1556,14 @@ static void cs_etm__copy_insn(struct cs_ - * cs_etm__t32_instr_size(). - */ - if (packet->isa == CS_ETM_ISA_T32) -- sample->insn_len = cs_etm__t32_instr_size(etmq, trace_chan_id, -+ sample->insn_len = cs_etm__t32_instr_size(etmq, tidq, packet, - sample->ip); - /* Otherwise, A64 and A32 instruction size are always 32-bit. */ - else - sample->insn_len = 4; - -- cs_etm__mem_access(etmq, trace_chan_id, sample->ip, sample->insn_len, -- (void *)sample->insn, 0); -+ cs_etm__frontend_mem_access(etmq, tidq, packet, sample->ip, -+ sample->insn_len, (void *)sample->insn); - } - - u64 cs_etm__convert_sample_time(struct cs_etm_queue *etmq, u64 cs_timestamp) -@@ -1555,6 +1590,7 @@ static inline u64 cs_etm__resolve_sample - - static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, - struct cs_etm_traceid_queue *tidq, -+ struct cs_etm_packet *packet, - u64 addr, u64 period) - { - int ret = 0; -@@ -1563,23 +1599,23 @@ static int cs_etm__synth_instruction_sam - struct perf_sample sample = {.ip = 0,}; - - event->sample.header.type = PERF_RECORD_SAMPLE; -- event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, tidq->el); -+ event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, packet->el); - event->sample.header.size = sizeof(struct perf_event_header); - - /* Set time field based on etm auxtrace config. */ - sample.time = cs_etm__resolve_sample_time(etmq, tidq); - - sample.ip = addr; -- sample.pid = thread__pid(tidq->thread); -- sample.tid = thread__tid(tidq->thread); -+ sample.pid = thread__pid(tidq->frontend_thread); -+ sample.tid = thread__tid(tidq->frontend_thread); - sample.id = etmq->etm->instructions_id; - sample.stream_id = etmq->etm->instructions_id; - sample.period = period; -- sample.cpu = tidq->packet->cpu; -+ sample.cpu = packet->cpu; - sample.flags = tidq->prev_packet->flags; - sample.cpumode = event->sample.header.misc; - -- cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->packet, &sample); -+ cs_etm__copy_insn(etmq, tidq, packet, &sample); - - if (etm->synth_opts.last_branch) - sample.branch_stack = tidq->last_branch; -@@ -1623,15 +1659,15 @@ static int cs_etm__synth_branch_sample(s - - event->sample.header.type = PERF_RECORD_SAMPLE; - event->sample.header.misc = cs_etm__cpu_mode(etmq, ip, -- tidq->prev_packet_el); -+ tidq->prev_packet->el); - event->sample.header.size = sizeof(struct perf_event_header); - - /* Set time field based on etm auxtrace config. */ - sample.time = cs_etm__resolve_sample_time(etmq, tidq); - - sample.ip = ip; -- sample.pid = thread__pid(tidq->prev_packet_thread); -- sample.tid = thread__tid(tidq->prev_packet_thread); -+ sample.pid = thread__pid(tidq->frontend_thread); -+ sample.tid = thread__tid(tidq->frontend_thread); - sample.addr = cs_etm__first_executed_instr(tidq->packet); - sample.id = etmq->etm->branches_id; - sample.stream_id = etmq->etm->branches_id; -@@ -1640,8 +1676,7 @@ static int cs_etm__synth_branch_sample(s - sample.flags = tidq->prev_packet->flags; - sample.cpumode = event->sample.header.misc; - -- cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->prev_packet, -- &sample); -+ cs_etm__copy_insn(etmq, tidq, tidq->prev_packet, &sample); - - /* - * perf report cannot handle events without a branch stack -@@ -1765,7 +1800,6 @@ static int cs_etm__sample(struct cs_etm_ - { - struct cs_etm_auxtrace *etm = etmq->etm; - int ret; -- u8 trace_chan_id = tidq->trace_chan_id; - u64 instrs_prev; - - /* Get instructions remainder from previous packet */ -@@ -1851,10 +1885,10 @@ static int cs_etm__sample(struct cs_etm_ - * been executed, but PC has not advanced to next - * instruction) - */ -- addr = cs_etm__instr_addr(etmq, trace_chan_id, -- tidq->packet, offset - 1); -+ addr = cs_etm__instr_addr(etmq, tidq, tidq->packet, -+ offset - 1); - ret = cs_etm__synth_instruction_sample( -- etmq, tidq, addr, -+ etmq, tidq, tidq->packet, addr, - etm->instructions_sample_period); - if (ret) - return ret; -@@ -1936,7 +1970,7 @@ static int cs_etm__flush(struct cs_etm_q - addr = cs_etm__last_executed_instr(tidq->prev_packet); - - err = cs_etm__synth_instruction_sample( -- etmq, tidq, addr, -+ etmq, tidq, tidq->prev_packet, addr, - tidq->period_instructions); - if (err) - return err; -@@ -1991,7 +2025,7 @@ static int cs_etm__end_block(struct cs_e - addr = cs_etm__last_executed_instr(tidq->prev_packet); - - err = cs_etm__synth_instruction_sample( -- etmq, tidq, addr, -+ etmq, tidq, tidq->prev_packet, addr, - tidq->period_instructions); - if (err) - return err; -@@ -2028,9 +2062,9 @@ static int cs_etm__get_data_block(struct - return etmq->buf_len; - } - --static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, -- struct cs_etm_packet *packet, -- u64 end_addr) -+static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, -+ struct cs_etm_traceid_queue *tidq, -+ struct cs_etm_packet *packet, u64 end_addr) - { - /* Initialise to keep compiler happy */ - u16 instr16 = 0; -@@ -2052,8 +2086,8 @@ static bool cs_etm__is_svc_instr(struct - * so below only read 2 bytes as instruction size for T32. - */ - addr = end_addr - 2; -- cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr16), -- (u8 *)&instr16, 0); -+ cs_etm__frontend_mem_access(etmq, tidq, packet, addr, -+ sizeof(instr16), (u8 *)&instr16); - if ((instr16 & 0xFF00) == 0xDF00) - return true; - -@@ -2068,8 +2102,8 @@ static bool cs_etm__is_svc_instr(struct - * +---------+---------+-------------------------+ - */ - addr = end_addr - 4; -- cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32), -- (u8 *)&instr32, 0); -+ cs_etm__frontend_mem_access(etmq, tidq, packet, addr, -+ sizeof(instr32), (u8 *)&instr32); - if ((instr32 & 0x0F000000) == 0x0F000000 && - (instr32 & 0xF0000000) != 0xF0000000) - return true; -@@ -2085,8 +2119,8 @@ static bool cs_etm__is_svc_instr(struct - * +-----------------------+---------+-----------+ - */ - addr = end_addr - 4; -- cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32), -- (u8 *)&instr32, 0); -+ cs_etm__frontend_mem_access(etmq, tidq, packet, addr, -+ sizeof(instr32), (u8 *)&instr32); - if ((instr32 & 0xFFE0001F) == 0xd4000001) - return true; - -@@ -2102,7 +2136,6 @@ static bool cs_etm__is_svc_instr(struct - static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, - struct cs_etm_traceid_queue *tidq, u64 magic) - { -- u8 trace_chan_id = tidq->trace_chan_id; - struct cs_etm_packet *packet = tidq->packet; - struct cs_etm_packet *prev_packet = tidq->prev_packet; - -@@ -2117,7 +2150,7 @@ static bool cs_etm__is_syscall(struct cs - */ - if (magic == __perf_cs_etmv4_magic) { - if (packet->exception_number == CS_ETMV4_EXC_CALL && -- cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet, -+ cs_etm__is_svc_instr(etmq, tidq, prev_packet, - prev_packet->end_addr)) - return true; - } -@@ -2155,7 +2188,6 @@ static bool cs_etm__is_sync_exception(st - struct cs_etm_traceid_queue *tidq, - u64 magic) - { -- u8 trace_chan_id = tidq->trace_chan_id; - struct cs_etm_packet *packet = tidq->packet; - struct cs_etm_packet *prev_packet = tidq->prev_packet; - -@@ -2181,7 +2213,7 @@ static bool cs_etm__is_sync_exception(st - * (SMC, HVC) are taken as sync exceptions. - */ - if (packet->exception_number == CS_ETMV4_EXC_CALL && -- !cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet, -+ !cs_etm__is_svc_instr(etmq, tidq, prev_packet, - prev_packet->end_addr)) - return true; - -@@ -2205,7 +2237,6 @@ static int cs_etm__set_sample_flags(stru - { - struct cs_etm_packet *packet = tidq->packet; - struct cs_etm_packet *prev_packet = tidq->prev_packet; -- u8 trace_chan_id = tidq->trace_chan_id; - u64 magic; - int ret; - -@@ -2286,11 +2317,11 @@ static int cs_etm__set_sample_flags(stru - if (prev_packet->flags == (PERF_IP_FLAG_BRANCH | - PERF_IP_FLAG_RETURN | - PERF_IP_FLAG_INTERRUPT) && -- cs_etm__is_svc_instr(etmq, trace_chan_id, -- packet, packet->start_addr)) -+ cs_etm__is_svc_instr(etmq, tidq, packet, packet->start_addr)) { - prev_packet->flags = PERF_IP_FLAG_BRANCH | - PERF_IP_FLAG_RETURN | - PERF_IP_FLAG_SYSCALLRET; -+ } - break; - case CS_ETM_DISCONTINUITY: - /* -@@ -2371,6 +2402,7 @@ static int cs_etm__set_sample_flags(stru - PERF_IP_FLAG_RETURN | - PERF_IP_FLAG_INTERRUPT; - break; -+ case CS_ETM_CONTEXT: - case CS_ETM_EMPTY: - default: - break; -@@ -2446,6 +2478,19 @@ static int cs_etm__process_traceid_queue - */ - cs_etm__sample(etmq, tidq); - break; -+ case CS_ETM_CONTEXT: -+ /* -+ * Update context but don't swap packet. Keep the -+ * previous one for branch source address info, if -+ * tracing the kernel the context packet will be emitted -+ * between two ranges. -+ */ -+ ret = cs_etm__etmq_update_thread(etmq, tidq->packet->el, -+ tidq->packet->tid, -+ &tidq->frontend_thread); -+ if (ret) -+ goto out; -+ break; - case CS_ETM_EXCEPTION: - case CS_ETM_EXCEPTION_RET: - /* -@@ -2474,6 +2519,7 @@ static int cs_etm__process_traceid_queue - } - } - -+out: - return ret; - } - -@@ -2597,7 +2643,7 @@ static int cs_etm__process_timeless_queu - if (!tidq) - continue; - -- if (tid == -1 || thread__tid(tidq->thread) == tid) -+ if (tid == -1 || thread__tid(tidq->frontend_thread) == tid) - cs_etm__run_per_thread_timeless_decoder(etmq); - } else - cs_etm__run_per_cpu_timeless_decoder(etmq); -@@ -3308,7 +3354,7 @@ static int cs_etm__create_queue_decoders - */ - if (cs_etm_decoder__add_mem_access_cb(etmq->decoder, - 0x0L, ((u64) -1L), -- cs_etm__mem_access)) -+ cs_etm__decoder_mem_access)) - goto out_free_decoder; - - zfree(&t_params); ---- a/tools/perf/util/cs-etm.h -+++ b/tools/perf/util/cs-etm.h -@@ -158,6 +158,7 @@ enum cs_etm_sample_type { - CS_ETM_DISCONTINUITY, - CS_ETM_EXCEPTION, - CS_ETM_EXCEPTION_RET, -+ CS_ETM_CONTEXT, - }; - - enum cs_etm_isa { -@@ -184,6 +185,8 @@ struct cs_etm_packet { - u8 last_instr_size; - u8 trace_chan_id; - int cpu; -+ int el; -+ pid_t tid; - }; - - #define CS_ETM_PACKET_MAX_BUFFER 1024 -@@ -244,8 +247,9 @@ enum cs_etm_pid_fmt { - #include - int cs_etm__get_cpu(struct cs_etm_queue *etmq, u8 trace_chan_id, int *cpu); - enum cs_etm_pid_fmt cs_etm__get_pid_fmt(struct cs_etm_queue *etmq); --int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid, -- u8 trace_chan_id, ocsd_ex_level el); -+int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq, -+ u8 trace_chan_id, ocsd_ex_level el, -+ pid_t tid); - bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq); - void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq, - u8 trace_chan_id); diff --git a/queue-6.12/perf-cs-etm-reject-cpu-ids-that-would-overflow-signe.patch b/queue-6.12/perf-cs-etm-reject-cpu-ids-that-would-overflow-signe.patch deleted file mode 100644 index 95db5fa7ee..0000000000 --- a/queue-6.12/perf-cs-etm-reject-cpu-ids-that-would-overflow-signe.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 33793177d52cc3face8cd19a594573df93c3b4c1 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Wed, 10 Jun 2026 22:45:08 -0300 -Subject: perf cs-etm: Reject CPU IDs that would overflow signed comparison - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 542e88a4c6f7b6edd1326ce767d4cb3c2ea9d61d ] - -metadata[j][CS_ETM_CPU] is a u64 from perf.data, but the comparison -with max_cpu casts it to (int). A crafted value like 0xFFFFFFFF becomes --1 after the cast, which compares less than max_cpu (0), so the queue -array is never sized to accommodate it. When the value is later passed -to cs_etm__get_queue(), it indexes queue_array with the original large -value, causing an out-of-bounds access. - -Validate that CS_ETM_CPU fits in an int before using it in the signed -comparison. - -Fixes: 57880a7966be510c ("perf: cs-etm: Allocate queues for all CPUs") -Reported-by: sashiko-bot -Cc: James Clark -Cc: Adrian Hunter -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/cs-etm.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - ---- a/tools/perf/util/cs-etm.c -+++ b/tools/perf/util/cs-etm.c -@@ -6,6 +6,7 @@ - * Author: Mathieu Poirier - */ - -+#include - #include - #include - #include -@@ -3447,7 +3448,13 @@ int cs_etm__process_auxtrace_info_full(u - goto err_free_metadata; - } - -- if ((int) metadata[j][CS_ETM_CPU] > max_cpu) -+ /* CPU id comes from perf.data and must fit max_cpu + 1 without overflow */ -+ if (metadata[j][CS_ETM_CPU] >= INT_MAX) { -+ err = -EINVAL; -+ goto err_free_metadata; -+ } -+ -+ if ((int)metadata[j][CS_ETM_CPU] > max_cpu) - max_cpu = metadata[j][CS_ETM_CPU]; - } - diff --git a/queue-6.12/perf-cs-etm-require-full-global-header-in-auxtrace_i.patch b/queue-6.12/perf-cs-etm-require-full-global-header-in-auxtrace_i.patch deleted file mode 100644 index 75d55142bf..0000000000 --- a/queue-6.12/perf-cs-etm-require-full-global-header-in-auxtrace_i.patch +++ /dev/null @@ -1,49 +0,0 @@ -From a2fbadc4fa90d99ec3c46a07dcf6f52e4f08b2ef Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 13 Jun 2026 14:40:36 -0300 -Subject: perf cs-etm: Require full global header in auxtrace_info size check - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 78d8ba680126f3545e8d0fba667e12d79fd4353b ] - -cs_etm__process_auxtrace_info() checks that header.size covers -event_header_size + INFO_HEADER_SIZE (16 bytes total), but then -accesses ptr[CS_PMU_TYPE_CPUS] at offset 24 from the start of the -event. A crafted 16-byte auxtrace_info event passes the size check -but reads out-of-bounds. - -Include CS_ETM_HEADER_SIZE in the minimum size check so that the -global header entries (version, pmu_type_cpus, snapshot) are -guaranteed to fit within the event. - -Fixes: 55c1de9973d66516 ("perf cs-etm: Print auxtrace info even if OpenCSD isn't linked") -Reported-by: sashiko-bot -Cc: Adrian Hunter -Cc: James Clark -Cc: Leo Yan -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/cs-etm-base.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/tools/perf/util/cs-etm-base.c b/tools/perf/util/cs-etm-base.c -index 4abe416e3febd2..aebef71d3a0a1d 100644 ---- a/tools/perf/util/cs-etm-base.c -+++ b/tools/perf/util/cs-etm-base.c -@@ -170,7 +170,9 @@ int cs_etm__process_auxtrace_info(union perf_event *event, - u64 *ptr = NULL; - u64 hdr_version; - -- if (auxtrace_info->header.size < (event_header_size + INFO_HEADER_SIZE)) -+ /* Ensure priv[] is large enough for the global header entries */ -+ if (auxtrace_info->header.size < (event_header_size + INFO_HEADER_SIZE + -+ CS_ETM_HEADER_SIZE)) - return -EINVAL; - - /* First the global part */ --- -2.53.0 - diff --git a/queue-6.12/perf-cs-etm-validate-num_cpu-before-metadata-allocat.patch b/queue-6.12/perf-cs-etm-validate-num_cpu-before-metadata-allocat.patch deleted file mode 100644 index 93c7f561d7..0000000000 --- a/queue-6.12/perf-cs-etm-validate-num_cpu-before-metadata-allocat.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 3d3a6d2ca005f15e76c8e9761af300db4358bd27 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 13 Jun 2026 14:16:45 -0300 -Subject: perf cs-etm: Validate num_cpu before metadata allocation - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 312d91329b8fc6989a916a3f9a12d0674167b7e4 ] - -cs_etm__process_auxtrace_info_full() reads num_cpu from untrusted -perf.data and uses it to allocate the metadata pointer array: - - metadata = zalloc(sizeof(*metadata) * num_cpu); - -On 32-bit, sizeof(*metadata) is 4, so num_cpu = 0x40000000 overflows -the multiplication to 0, causing zalloc(0) to return a valid zero-sized -allocation followed by out-of-bounds writes in the population loop. - -Fix by computing priv_size early and using it to bound num_cpu: each -CPU needs at least one u64 metadata entry, so num_cpu cannot exceed -the total number of u64 entries in the event's private data area. - -Fixes: cd8bfd8c973eaff8 ("perf tools: Add processing of coresight metadata") -Reported-by: sashiko-bot -Cc: Adrian Hunter -Cc: James Clark -Cc: Leo Yan -Cc: Tor Jeremiassen -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/cs-etm.c | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - ---- a/tools/perf/util/cs-etm.c -+++ b/tools/perf/util/cs-etm.c -@@ -3410,6 +3410,18 @@ int cs_etm__process_auxtrace_info_full(u - /* First the global part */ - ptr = (u64 *) auxtrace_info->priv; - num_cpu = ptr[CS_PMU_TYPE_CPUS] & 0xffffffff; -+ -+ /* -+ * Bound num_cpu by the event size: the global header consumes -+ * CS_ETM_HEADER_SIZE bytes, and each CPU needs at least one u64 -+ * metadata entry after that. -+ */ -+ priv_size = total_size - event_header_size - INFO_HEADER_SIZE - -+ CS_ETM_HEADER_SIZE; -+ if (num_cpu <= 0 || priv_size <= 0 || -+ num_cpu > priv_size / (int)sizeof(u64)) -+ return -EINVAL; -+ - metadata = zalloc(sizeof(*metadata) * num_cpu); - if (!metadata) - return -ENOMEM; diff --git a/queue-6.12/perf-debuginfo-fix-libdw-api-contract-violations.patch b/queue-6.12/perf-debuginfo-fix-libdw-api-contract-violations.patch deleted file mode 100644 index a9fe9a1a1e..0000000000 --- a/queue-6.12/perf-debuginfo-fix-libdw-api-contract-violations.patch +++ /dev/null @@ -1,84 +0,0 @@ -From f327b3e36fc33294281f83af5ad7a99038dcf366 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Mon, 4 May 2026 01:12:27 -0700 -Subject: perf debuginfo: Fix libdw API contract violations - -From: Ian Rogers - -[ Upstream commit 31088ccf0312b1a547046f1f69890ede07834a30 ] - -Check return value of `dwfl_report_end` during offline initialization. -Validate `dwfl_module_relocation_info` result before passing to `strcmp` -to avoid potential segmentation faults. - -Additionally: - - Fix a file descriptor leak in `debuginfo__init_offline_dwarf()` when - `dwfl_report_offline()` or subsequent setup calls fail. - -Fixes: 6f1b6291cf73cb32 ("perf tools: Add util/debuginfo.[ch] files") -Assisted-by: Gemini-CLI:Google Gemini 3 -Signed-off-by: Ian Rogers -Acked-by: Namhyung Kim -Cc: Adrian Hunter -Cc: Ingo Molnar -Cc: James Clark -Cc: Jiri Olsa -Cc: Masami Hiramatsu -Cc: Namhyung Kim -Cc: Peter Zijlstra -Cc: Zecheng Li -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/debuginfo.c | 9 ++++++--- - 1 file changed, 6 insertions(+), 3 deletions(-) - -diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c -index 19acf4775d3587..f5af3b4408b950 100644 ---- a/tools/perf/util/debuginfo.c -+++ b/tools/perf/util/debuginfo.c -@@ -42,6 +42,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, - { - GElf_Addr dummy; - int fd; -+ bool fd_consumed = false; - - fd = open(path, O_RDONLY); - if (fd < 0) -@@ -55,6 +56,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, - dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); - if (!dbg->mod) - goto error; -+ fd_consumed = true; - - dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); - if (!dbg->dbg) -@@ -62,13 +64,14 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, - - dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy); - -- dwfl_report_end(dbg->dwfl, NULL, NULL); -+ if (dwfl_report_end(dbg->dwfl, NULL, NULL) != 0) -+ goto error; - - return 0; - error: - if (dbg->dwfl) - dwfl_end(dbg->dwfl); -- else -+ if (!fd_consumed) - close(fd); - memset(dbg, 0, sizeof(*dbg)); - -@@ -160,7 +163,7 @@ int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, - /* Search the relocation related .text section */ - for (i = 0; i < n; i++) { - p = dwfl_module_relocation_info(dbg->mod, i, &shndx); -- if (strcmp(p, ".text") == 0) { -+ if (p && strcmp(p, ".text") == 0) { - /* OK, get the section header */ - scn = elf_getscn(elf, shndx); - if (!scn) --- -2.53.0 - diff --git a/queue-6.12/perf-dso-add-support-for-reading-the-e_machine-type-.patch b/queue-6.12/perf-dso-add-support-for-reading-the-e_machine-type-.patch deleted file mode 100644 index 779761e102..0000000000 --- a/queue-6.12/perf-dso-add-support-for-reading-the-e_machine-type-.patch +++ /dev/null @@ -1,198 +0,0 @@ -From 38090a76f4e23063a774b9efcb9e3e3a36128721 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Tue, 18 Mar 2025 22:07:33 -0700 -Subject: perf dso: Add support for reading the e_machine type for a dso - -From: Ian Rogers - -[ Upstream commit afffec6f032926f70fc9f43f5d07cdfa09a0a184 ] - -For ELF file dsos read the e_machine from the ELF header. For kernel -types assume the e_machine matches the perf tool. In other cases -return EM_NONE. - -When reading from the ELF header use DSO__SWAP that may need -dso->needs_swap initializing. Factor out dso__swap_init to allow this. - -Signed-off-by: Ian Rogers -Reviewed-by: Namhyung Kim -Acked-by: Arnaldo Carvalho de Melo -Link: https://lore.kernel.org/r/20250319050741.269828-7-irogers@google.com -Signed-off-by: Namhyung Kim -Stable-dep-of: 2ea64782a428 ("perf tools: Use snprintf() in dso__read_running_kernel_build_id()") -Signed-off-by: Sasha Levin ---- - tools/perf/util/dso.c | 89 ++++++++++++++++++++++++++++++++++++ - tools/perf/util/dso.h | 3 ++ - tools/perf/util/symbol-elf.c | 27 ----------- - 3 files changed, 92 insertions(+), 27 deletions(-) - -diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c -index 05a9dc6f41f820..7c12d53656887d 100644 ---- a/tools/perf/util/dso.c -+++ b/tools/perf/util/dso.c -@@ -1191,6 +1191,68 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, - return data_read_write_offset(dso, machine, offset, data, size, true); - } - -+uint16_t dso__e_machine(struct dso *dso, struct machine *machine) -+{ -+ uint16_t e_machine = EM_NONE; -+ int fd; -+ -+ switch (dso__binary_type(dso)) { -+ case DSO_BINARY_TYPE__KALLSYMS: -+ case DSO_BINARY_TYPE__GUEST_KALLSYMS: -+ case DSO_BINARY_TYPE__VMLINUX: -+ case DSO_BINARY_TYPE__GUEST_VMLINUX: -+ case DSO_BINARY_TYPE__GUEST_KMODULE: -+ case DSO_BINARY_TYPE__GUEST_KMODULE_COMP: -+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: -+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: -+ case DSO_BINARY_TYPE__KCORE: -+ case DSO_BINARY_TYPE__GUEST_KCORE: -+ case DSO_BINARY_TYPE__BPF_PROG_INFO: -+ case DSO_BINARY_TYPE__BPF_IMAGE: -+ case DSO_BINARY_TYPE__OOL: -+ case DSO_BINARY_TYPE__JAVA_JIT: -+ return EM_HOST; -+ case DSO_BINARY_TYPE__DEBUGLINK: -+ case DSO_BINARY_TYPE__BUILD_ID_CACHE: -+ case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO: -+ case DSO_BINARY_TYPE__GNU_DEBUGDATA: -+ case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: -+ case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: -+ case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: -+ case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: -+ case DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO: -+ case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: -+ break; -+ case DSO_BINARY_TYPE__NOT_FOUND: -+ default: -+ return EM_NONE; -+ } -+ -+ mutex_lock(dso__data_open_lock()); -+ -+ /* -+ * dso__data(dso)->fd might be closed if other thread opened another -+ * file (dso) due to open file limit (RLIMIT_NOFILE). -+ */ -+ try_to_open_dso(dso, machine); -+ fd = dso__data(dso)->fd; -+ if (fd >= 0) { -+ _Static_assert(offsetof(Elf32_Ehdr, e_machine) == 18, "Unexpected offset"); -+ _Static_assert(offsetof(Elf64_Ehdr, e_machine) == 18, "Unexpected offset"); -+ if (dso__needs_swap(dso) == DSO_SWAP__UNSET) { -+ unsigned char eidata; -+ -+ if (pread(fd, &eidata, sizeof(eidata), EI_DATA) == sizeof(eidata)) -+ dso__swap_init(dso, eidata); -+ } -+ if (dso__needs_swap(dso) != DSO_SWAP__UNSET && -+ pread(fd, &e_machine, sizeof(e_machine), 18) == sizeof(e_machine)) -+ e_machine = DSO__SWAP(dso, uint16_t, e_machine); -+ } -+ mutex_unlock(dso__data_open_lock()); -+ return e_machine; -+} -+ - /** - * dso__data_read_addr - Read data from dso address - * @dso: dso object -@@ -1546,6 +1608,33 @@ void dso__put(struct dso *dso) - RC_CHK_PUT(dso); - } - -+int dso__swap_init(struct dso *dso, unsigned char eidata) -+{ -+ static unsigned int const endian = 1; -+ -+ dso__set_needs_swap(dso, DSO_SWAP__NO); -+ -+ switch (eidata) { -+ case ELFDATA2LSB: -+ /* We are big endian, DSO is little endian. */ -+ if (*(unsigned char const *)&endian != 1) -+ dso__set_needs_swap(dso, DSO_SWAP__YES); -+ break; -+ -+ case ELFDATA2MSB: -+ /* We are little endian, DSO is big endian. */ -+ if (*(unsigned char const *)&endian != 0) -+ dso__set_needs_swap(dso, DSO_SWAP__YES); -+ break; -+ -+ default: -+ pr_err("unrecognized DSO data encoding %d\n", eidata); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ - void dso__set_build_id(struct dso *dso, struct build_id *bid) - { - RC_CHK_ACCESS(dso)->bid = *bid; -diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h -index 53eac48fd0cd81..4bca8a3c8b48e2 100644 ---- a/tools/perf/util/dso.h -+++ b/tools/perf/util/dso.h -@@ -677,6 +677,8 @@ bool dso__sorted_by_name(const struct dso *dso); - void dso__set_sorted_by_name(struct dso *dso); - void dso__sort_by_name(struct dso *dso); - -+int dso__swap_init(struct dso *dso, unsigned char eidata); -+ - void dso__set_build_id(struct dso *dso, struct build_id *bid); - bool dso__build_id_equal(const struct dso *dso, struct build_id *bid); - void dso__read_running_kernel_build_id(struct dso *dso, -@@ -766,6 +768,7 @@ int dso__data_file_size(struct dso *dso, struct machine *machine); - off_t dso__data_size(struct dso *dso, struct machine *machine); - ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, - u64 offset, u8 *data, ssize_t size); -+uint16_t dso__e_machine(struct dso *dso, struct machine *machine); - ssize_t dso__data_read_addr(struct dso *dso, struct map *map, - struct machine *machine, u64 addr, - u8 *data, ssize_t size); -diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c -index c7fcbb8fb644c3..029a1ef9b287b4 100644 ---- a/tools/perf/util/symbol-elf.c -+++ b/tools/perf/util/symbol-elf.c -@@ -1186,33 +1186,6 @@ int filename__read_debuglink(const char *filename, char *debuglink, - - #endif - --static int dso__swap_init(struct dso *dso, unsigned char eidata) --{ -- static unsigned int const endian = 1; -- -- dso__set_needs_swap(dso, DSO_SWAP__NO); -- -- switch (eidata) { -- case ELFDATA2LSB: -- /* We are big endian, DSO is little endian. */ -- if (*(unsigned char const *)&endian != 1) -- dso__set_needs_swap(dso, DSO_SWAP__YES); -- break; -- -- case ELFDATA2MSB: -- /* We are little endian, DSO is big endian. */ -- if (*(unsigned char const *)&endian != 0) -- dso__set_needs_swap(dso, DSO_SWAP__YES); -- break; -- -- default: -- pr_err("unrecognized DSO data encoding %d\n", eidata); -- return -EINVAL; -- } -- -- return 0; --} -- - bool symsrc__possibly_runtime(struct symsrc *ss) - { - return ss->dynsym || ss->opdsec; --- -2.53.0 - diff --git a/queue-6.12/perf-dso-move-build_id-to-dso_id.patch b/queue-6.12/perf-dso-move-build_id-to-dso_id.patch deleted file mode 100644 index 6a6e04f9e2..0000000000 --- a/queue-6.12/perf-dso-move-build_id-to-dso_id.patch +++ /dev/null @@ -1,839 +0,0 @@ -From 295ddd9358516166b64d594f5ca07bc35cbb0a62 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Thu, 24 Jul 2025 09:32:46 -0700 -Subject: perf dso: Move build_id to dso_id - -From: Ian Rogers - -[ Upstream commit d9f2ecbc5e47fca7bda7c13cff3b3534b1467b32 ] - -The dso_id previously contained the major, minor, inode and inode -generation information from a mmap2 event - the inode generation would -be zero when reading from /proc/pid/maps. The build_id was in the -dso. With build ID mmap2 events these fields wouldn't be initialized -which would largely mean the special empty case where any dso would -match for equality. This isn't desirable as if a dso is replaced we -want the comparison to yield a difference. - -To support detecting the difference between DSOs based on build_id, -move the build_id out of the DSO and into the dso_id. The dso_id is -also stored in the DSO so nothing is lost. Capture in the dso_id what -parts have been initialized and rename dso_id__inject to -dso_id__improve_id so that it is clear the dso_id is being improved -upon with additional information. With the build_id in the dso_id, use -memcmp to compare for equality. - -Signed-off-by: Ian Rogers -Link: https://lore.kernel.org/r/20250724163302.596743-7-irogers@google.com -Signed-off-by: Namhyung Kim -Stable-dep-of: 2ea64782a428 ("perf tools: Use snprintf() in dso__read_running_kernel_build_id()") -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-buildid-list.c | 2 - tools/perf/builtin-inject.c | 36 ++++++---- - tools/perf/builtin-report.c | 11 ++- - tools/perf/include/perf/perf_dlfilter.h | 2 - tools/perf/tests/symbols.c | 4 - - tools/perf/util/build-id.c | 4 - - tools/perf/util/dso.c | 109 +++++++++++++++++--------------- - tools/perf/util/dso.h | 75 ++++++++++++---------- - tools/perf/util/dsos.c | 18 ++--- - tools/perf/util/machine.c | 28 ++++---- - tools/perf/util/map.c | 13 +-- - tools/perf/util/map.h | 5 - - tools/perf/util/sort.c | 29 ++++---- - tools/perf/util/synthetic-events.c | 18 +++-- - 14 files changed, 198 insertions(+), 156 deletions(-) - ---- a/tools/perf/builtin-buildid-list.c -+++ b/tools/perf/builtin-buildid-list.c -@@ -31,7 +31,7 @@ static int buildid__map_cb(struct map *m - - memset(bid_buf, 0, sizeof(bid_buf)); - if (dso__has_build_id(dso)) -- build_id__snprintf(dso__bid_const(dso), bid_buf, sizeof(bid_buf)); -+ build_id__snprintf(dso__bid(dso), bid_buf, sizeof(bid_buf)); - printf("%s %16" PRIx64 " %16" PRIx64, bid_buf, map__start(map), map__end(map)); - if (dso_long_name != NULL) - printf(" %s", dso_long_name); ---- a/tools/perf/builtin-inject.c -+++ b/tools/perf/builtin-inject.c -@@ -673,15 +673,17 @@ static int perf_event__repipe_mmap2(cons - struct perf_sample *sample, - struct machine *machine) - { -- struct dso_id id; -- struct dso_id *dso_id = NULL; -+ struct dso_id id = dso_id_empty; - -- if (!(event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)) { -+ if (event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID) { -+ build_id__init(&id.build_id, event->mmap2.build_id, event->mmap2.build_id_size); -+ } else { - id.maj = event->mmap2.maj; - id.min = event->mmap2.min; - id.ino = event->mmap2.ino; - id.ino_generation = event->mmap2.ino_generation; -- dso_id = &id; -+ id.mmap2_valid = true; -+ id.mmap2_ino_generation_valid = true; - } - - return perf_event__repipe_common_mmap( -@@ -689,7 +691,7 @@ static int perf_event__repipe_mmap2(cons - event->mmap2.pid, event->mmap2.tid, - event->mmap2.start, event->mmap2.len, event->mmap2.pgoff, - event->mmap2.flags, event->mmap2.prot, -- event->mmap2.filename, dso_id, -+ event->mmap2.filename, &id, - perf_event__process_mmap2); - } - -@@ -757,19 +759,20 @@ static int perf_event__repipe_tracing_da - static int dso__read_build_id(struct dso *dso) - { - struct nscookie nsc; -+ struct build_id bid = { .size = 0, }; - - if (dso__has_build_id(dso)) - return 0; - - mutex_lock(dso__lock(dso)); - nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); -- if (filename__read_build_id(dso__long_name(dso), dso__bid(dso)) > 0) -- dso__set_has_build_id(dso); -+ if (filename__read_build_id(dso__long_name(dso), &bid) > 0) -+ dso__set_build_id(dso, &bid); - else if (dso__nsinfo(dso)) { - char *new_name = dso__filename_with_chroot(dso, dso__long_name(dso)); - -- if (new_name && filename__read_build_id(new_name, dso__bid(dso)) > 0) -- dso__set_has_build_id(dso); -+ if (new_name && filename__read_build_id(new_name, &bid) > 0) -+ dso__set_build_id(dso, &bid); - free(new_name); - } - nsinfo__mountns_exit(&nsc); -@@ -818,23 +821,26 @@ static bool perf_inject__lookup_known_bu - struct dso *dso) - { - struct str_node *pos; -- int bid_len; - - strlist__for_each_entry(pos, inject->known_build_ids) { -+ struct build_id bid; - const char *build_id, *dso_name; -+ size_t bid_len; - - build_id = skip_spaces(pos->s); - dso_name = strchr(build_id, ' '); - bid_len = dso_name - pos->s; -+ if (bid_len > sizeof(bid.data)) -+ bid_len = sizeof(bid.data); - dso_name = skip_spaces(dso_name); - if (strcmp(dso__long_name(dso), dso_name)) - continue; -- for (int ix = 0; 2 * ix + 1 < bid_len; ++ix) { -- dso__bid(dso)->data[ix] = (hex(build_id[2 * ix]) << 4 | -- hex(build_id[2 * ix + 1])); -+ for (size_t ix = 0; 2 * ix + 1 < bid_len; ++ix) { -+ bid.data[ix] = (hex(build_id[2 * ix]) << 4 | -+ hex(build_id[2 * ix + 1])); - } -- dso__bid(dso)->size = bid_len / 2; -- dso__set_has_build_id(dso); -+ bid.size = bid_len / 2; -+ dso__set_build_id(dso, &bid); - return true; - } - return false; ---- a/tools/perf/builtin-report.c -+++ b/tools/perf/builtin-report.c -@@ -855,17 +855,24 @@ static int maps__fprintf_task_cb(struct - struct maps__fprintf_task_args *args = data; - const struct dso *dso = map__dso(map); - u32 prot = map__prot(map); -+ const struct dso_id *dso_id = dso__id_const(dso); - int ret; -+ char buf[SBUILD_ID_SIZE]; -+ -+ if (dso_id->mmap2_valid) -+ snprintf(buf, sizeof(buf), "%" PRIu64, dso_id->ino); -+ else -+ build_id__snprintf(&dso_id->build_id, buf, sizeof(buf)); - - ret = fprintf(args->fp, -- "%*s %" PRIx64 "-%" PRIx64 " %c%c%c%c %08" PRIx64 " %" PRIu64 " %s\n", -+ "%*s %" PRIx64 "-%" PRIx64 " %c%c%c%c %08" PRIx64 " %s %s\n", - args->indent, "", map__start(map), map__end(map), - prot & PROT_READ ? 'r' : '-', - prot & PROT_WRITE ? 'w' : '-', - prot & PROT_EXEC ? 'x' : '-', - map__flags(map) ? 's' : 'p', - map__pgoff(map), -- dso__id_const(dso)->ino, dso__name(dso)); -+ buf, dso__name(dso)); - - if (ret < 0) - return ret; ---- a/tools/perf/include/perf/perf_dlfilter.h -+++ b/tools/perf/include/perf/perf_dlfilter.h -@@ -87,7 +87,7 @@ struct perf_dlfilter_al { - __u8 is_64_bit; /* Only valid if dso is not NULL */ - __u8 is_kernel_ip; /* True if in kernel space */ - __u32 buildid_size; -- __u8 *buildid; -+ const __u8 *buildid; - /* Below members are only populated by resolve_ip() */ - __u8 filtered; /* True if this sample event will be filtered out */ - const char *comm; ---- a/tools/perf/tests/symbols.c -+++ b/tools/perf/tests/symbols.c -@@ -96,8 +96,8 @@ static int create_map(struct test_info * - dso__put(dso); - - /* Create a dummy map at 0x100000 */ -- *map_p = map__new(ti->machine, 0x100000, 0xffffffff, 0, NULL, -- PROT_EXEC, 0, NULL, filename, ti->thread); -+ *map_p = map__new(ti->machine, 0x100000, 0xffffffff, 0, &dso_id_empty, -+ PROT_EXEC, /*flags=*/0, filename, ti->thread); - if (!*map_p) { - pr_debug("Failed to create map!"); - return TEST_FAIL; ---- a/tools/perf/util/build-id.c -+++ b/tools/perf/util/build-id.c -@@ -236,7 +236,7 @@ char *__dso__build_id_filename(const str - if (!dso__has_build_id(dso)) - return NULL; - -- build_id__snprintf(dso__bid_const(dso), sbuild_id, sizeof(sbuild_id)); -+ build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id)); - linkname = build_id_cache__linkname(sbuild_id, NULL, 0); - if (!linkname) - return NULL; -@@ -319,7 +319,7 @@ static int machine__write_buildid_table_ - } - - in_kernel = dso__kernel(dso) || is_kernel_module(name, PERF_RECORD_MISC_CPUMODE_UNKNOWN); -- return write_buildid(name, name_len, dso__bid(dso), args->machine->pid, -+ return write_buildid(name, name_len, &dso__id(dso)->build_id, args->machine->pid, - in_kernel ? args->kmisc : args->umisc, args->fd); - } - ---- a/tools/perf/util/dso.c -+++ b/tools/perf/util/dso.c -@@ -215,7 +215,7 @@ int dso__read_binary_type_filename(const - break; - } - -- build_id__snprintf(dso__bid_const(dso), build_id_hex, sizeof(build_id_hex)); -+ build_id__snprintf(dso__bid(dso), build_id_hex, sizeof(build_id_hex)); - len = __symbol__join_symfs(filename, size, "/usr/lib/debug/.build-id/"); - snprintf(filename + len, size - len, "%.2s/%s.debug", - build_id_hex, build_id_hex + 2); -@@ -1376,64 +1376,76 @@ static void dso__set_long_name_id(struct - - static int __dso_id__cmp(const struct dso_id *a, const struct dso_id *b) - { -- if (a->maj > b->maj) return -1; -- if (a->maj < b->maj) return 1; -+ if (a->mmap2_valid && b->mmap2_valid) { -+ if (a->maj > b->maj) return -1; -+ if (a->maj < b->maj) return 1; - -- if (a->min > b->min) return -1; -- if (a->min < b->min) return 1; -+ if (a->min > b->min) return -1; -+ if (a->min < b->min) return 1; - -- if (a->ino > b->ino) return -1; -- if (a->ino < b->ino) return 1; -- -- /* -- * Synthesized MMAP events have zero ino_generation, avoid comparing -- * them with MMAP events with actual ino_generation. -- * -- * I found it harmful because the mismatch resulted in a new -- * dso that did not have a build ID whereas the original dso did have a -- * build ID. The build ID was essential because the object was not found -- * otherwise. - Adrian -- */ -- if (a->ino_generation && b->ino_generation) { -+ if (a->ino > b->ino) return -1; -+ if (a->ino < b->ino) return 1; -+ } -+ if (a->mmap2_ino_generation_valid && b->mmap2_ino_generation_valid) { - if (a->ino_generation > b->ino_generation) return -1; - if (a->ino_generation < b->ino_generation) return 1; - } -- -+ if (build_id__is_defined(&a->build_id) && build_id__is_defined(&b->build_id)) { -+ if (a->build_id.size != b->build_id.size) -+ return a->build_id.size < b->build_id.size ? -1 : 1; -+ return memcmp(a->build_id.data, b->build_id.data, a->build_id.size); -+ } - return 0; - } - --bool dso_id__empty(const struct dso_id *id) --{ -- if (!id) -- return true; -- -- return !id->maj && !id->min && !id->ino && !id->ino_generation; --} -+const struct dso_id dso_id_empty = { -+ { -+ .maj = 0, -+ .min = 0, -+ .ino = 0, -+ .ino_generation = 0, -+ }, -+ .mmap2_valid = false, -+ .mmap2_ino_generation_valid = false, -+ { -+ .size = 0, -+ } -+}; - --void __dso__inject_id(struct dso *dso, const struct dso_id *id) -+void __dso__improve_id(struct dso *dso, const struct dso_id *id) - { - struct dsos *dsos = dso__dsos(dso); - struct dso_id *dso_id = dso__id(dso); -+ bool changed = false; - - /* dsos write lock held by caller. */ - -- dso_id->maj = id->maj; -- dso_id->min = id->min; -- dso_id->ino = id->ino; -- dso_id->ino_generation = id->ino_generation; -- -- if (dsos) -+ if (id->mmap2_valid && !dso_id->mmap2_valid) { -+ dso_id->maj = id->maj; -+ dso_id->min = id->min; -+ dso_id->ino = id->ino; -+ dso_id->mmap2_valid = true; -+ changed = true; -+ } -+ if (id->mmap2_ino_generation_valid && !dso_id->mmap2_ino_generation_valid) { -+ dso_id->ino_generation = id->ino_generation; -+ dso_id->mmap2_ino_generation_valid = true; -+ changed = true; -+ } -+ if (build_id__is_defined(&id->build_id) && !build_id__is_defined(&dso_id->build_id)) { -+ dso_id->build_id = id->build_id; -+ changed = true; -+ } -+ if (changed && dsos) - dsos->sorted = false; - } - - int dso_id__cmp(const struct dso_id *a, const struct dso_id *b) - { -- /* -- * The second is always dso->id, so zeroes if not set, assume passing -- * NULL for a means a zeroed id -- */ -- if (dso_id__empty(a) || dso_id__empty(b)) -+ if (a == &dso_id_empty || b == &dso_id_empty) { -+ /* There is no valid data to compare so the comparison always returns identical. */ - return 0; -+ } - - return __dso_id__cmp(a, b); - } -@@ -1530,7 +1542,6 @@ struct dso *dso__new_id(const char *name - dso->loaded = 0; - dso->rel = 0; - dso->sorted_by_name = 0; -- dso->has_build_id = 0; - dso->has_srcline = 1; - dso->a2l_fails = 1; - dso->kernel = DSO_SPACE__USER; -@@ -1635,15 +1646,14 @@ int dso__swap_init(struct dso *dso, unsi - return 0; - } - --void dso__set_build_id(struct dso *dso, struct build_id *bid) -+void dso__set_build_id(struct dso *dso, const struct build_id *bid) - { -- RC_CHK_ACCESS(dso)->bid = *bid; -- RC_CHK_ACCESS(dso)->has_build_id = 1; -+ dso__id(dso)->build_id = *bid; - } - --bool dso__build_id_equal(const struct dso *dso, struct build_id *bid) -+bool dso__build_id_equal(const struct dso *dso, const struct build_id *bid) - { -- const struct build_id *dso_bid = dso__bid_const(dso); -+ const struct build_id *dso_bid = dso__bid(dso); - - if (dso_bid->size > bid->size && dso_bid->size == BUILD_ID_SIZE) { - /* -@@ -1662,18 +1672,20 @@ bool dso__build_id_equal(const struct ds - void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) - { - char path[PATH_MAX]; -+ struct build_id bid = { .size = 0, }; - - if (machine__is_default_guest(machine)) - return; - sprintf(path, "%s/sys/kernel/notes", machine->root_dir); -- if (sysfs__read_build_id(path, dso__bid(dso)) == 0) -- dso__set_has_build_id(dso); -+ sysfs__read_build_id(path, &bid); -+ dso__set_build_id(dso, &bid); - } - - int dso__kernel_module_get_build_id(struct dso *dso, - const char *root_dir) - { - char filename[PATH_MAX]; -+ struct build_id bid = { .size = 0, }; - /* - * kernel module short names are of the form "[module]" and - * we need just "module" here. -@@ -1684,9 +1696,8 @@ int dso__kernel_module_get_build_id(stru - "%s/sys/module/%.*s/notes/.note.gnu.build-id", - root_dir, (int)strlen(name) - 1, name); - -- if (sysfs__read_build_id(filename, dso__bid(dso)) == 0) -- dso__set_has_build_id(dso); -- -+ sysfs__read_build_id(filename, &bid); -+ dso__set_build_id(dso, &bid); - return 0; - } - ---- a/tools/perf/util/dso.h -+++ b/tools/perf/util/dso.h -@@ -127,14 +127,33 @@ enum dso_load_errno { - #define DSO__DATA_CACHE_SIZE 4096 - #define DSO__DATA_CACHE_MASK ~(DSO__DATA_CACHE_SIZE - 1) - --/* -- * Data about backing storage DSO, comes from PERF_RECORD_MMAP2 meta events -+/** -+ * struct dso_id -+ * -+ * Data about backing storage DSO, comes from PERF_RECORD_MMAP2 meta events, -+ * reading from /proc/pid/maps or synthesis of build_ids from DSOs. Possibly -+ * incomplete at any particular use. - */ - struct dso_id { -- u32 maj; -- u32 min; -- u64 ino; -- u64 ino_generation; -+ /* Data related to the mmap2 event or read from /proc/pid/maps. */ -+ struct { -+ u32 maj; -+ u32 min; -+ u64 ino; -+ u64 ino_generation; -+ }; -+ /** @mmap2_valid: Are the maj, min and ino fields valid? */ -+ bool mmap2_valid; -+ /** -+ * @mmap2_ino_generation_valid: Is the ino_generation valid? Generally -+ * false for /proc/pid/maps mmap event. -+ */ -+ bool mmap2_ino_generation_valid; -+ /** -+ * @build_id: A possibly populated build_id. build_id__is_defined checks -+ * whether it is populated. -+ */ -+ struct build_id build_id; - }; - - struct dso_cache { -@@ -183,7 +202,6 @@ DECLARE_RC_STRUCT(dso) { - u64 addr; - struct symbol *symbol; - } last_find_result; -- struct build_id bid; - u64 text_offset; - u64 text_end; - const char *short_name; -@@ -216,7 +234,6 @@ DECLARE_RC_STRUCT(dso) { - enum dso_swap_type needs_swap:2; - bool is_kmod:1; - u8 adjust_symbols:1; -- u8 has_build_id:1; - u8 header_build_id:1; - u8 has_srcline:1; - u8 hit:1; -@@ -232,6 +249,9 @@ DECLARE_RC_STRUCT(dso) { - }; - - extern struct mutex _dso__data_open_lock; -+extern const struct dso_id dso_id_empty; -+ -+int dso_id__cmp(const struct dso_id *a, const struct dso_id *b); - - /* dso__for_each_symbol - iterate over the symbols of given type - * -@@ -302,31 +322,11 @@ static inline void dso__set_auxtrace_cac - RC_CHK_ACCESS(dso)->auxtrace_cache = cache; - } - --static inline struct build_id *dso__bid(struct dso *dso) --{ -- return &RC_CHK_ACCESS(dso)->bid; --} -- --static inline const struct build_id *dso__bid_const(const struct dso *dso) --{ -- return &RC_CHK_ACCESS(dso)->bid; --} -- - static inline struct dso_bpf_prog *dso__bpf_prog(struct dso *dso) - { - return &RC_CHK_ACCESS(dso)->bpf_prog; - } - --static inline bool dso__has_build_id(const struct dso *dso) --{ -- return RC_CHK_ACCESS(dso)->has_build_id; --} -- --static inline void dso__set_has_build_id(struct dso *dso) --{ -- RC_CHK_ACCESS(dso)->has_build_id = true; --} -- - static inline bool dso__has_srcline(const struct dso *dso) - { - return RC_CHK_ACCESS(dso)->has_srcline; -@@ -402,6 +402,16 @@ static inline const struct dso_id *dso__ - return &RC_CHK_ACCESS(dso)->id; - } - -+static inline const struct build_id *dso__bid(const struct dso *dso) -+{ -+ return &dso__id_const(dso)->build_id; -+} -+ -+static inline bool dso__has_build_id(const struct dso *dso) -+{ -+ return build_id__is_defined(dso__bid(dso)); -+} -+ - static inline struct rb_root_cached *dso__inlined_nodes(struct dso *dso) - { - return &RC_CHK_ACCESS(dso)->inlined_nodes; -@@ -639,9 +649,6 @@ static inline void dso__set_text_offset( - RC_CHK_ACCESS(dso)->text_offset = val; - } - --int dso_id__cmp(const struct dso_id *a, const struct dso_id *b); --bool dso_id__empty(const struct dso_id *id); -- - struct dso *dso__new_id(const char *name, const struct dso_id *id); - struct dso *dso__new(const char *name); - void dso__delete(struct dso *dso); -@@ -649,7 +656,7 @@ void dso__delete(struct dso *dso); - int dso__cmp_id(struct dso *a, struct dso *b); - void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated); - void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated); --void __dso__inject_id(struct dso *dso, const struct dso_id *id); -+void __dso__improve_id(struct dso *dso, const struct dso_id *id); - - int dso__name_len(const struct dso *dso); - -@@ -679,8 +686,8 @@ void dso__sort_by_name(struct dso *dso); - - int dso__swap_init(struct dso *dso, unsigned char eidata); - --void dso__set_build_id(struct dso *dso, struct build_id *bid); --bool dso__build_id_equal(const struct dso *dso, struct build_id *bid); -+void dso__set_build_id(struct dso *dso, const struct build_id *bid); -+bool dso__build_id_equal(const struct dso *dso, const struct build_id *bid); - void dso__read_running_kernel_build_id(struct dso *dso, - struct machine *machine); - int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir); ---- a/tools/perf/util/dsos.c -+++ b/tools/perf/util/dsos.c -@@ -72,6 +72,7 @@ static int dsos__read_build_ids_cb(struc - { - struct dsos__read_build_ids_cb_args *args = data; - struct nscookie nsc; -+ struct build_id bid = { .size = 0, }; - - if (args->with_hits && !dso__hit(dso) && !dso__is_vdso(dso)) - return 0; -@@ -80,15 +81,15 @@ static int dsos__read_build_ids_cb(struc - return 0; - } - nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); -- if (filename__read_build_id(dso__long_name(dso), dso__bid(dso)) > 0) { -+ if (filename__read_build_id(dso__long_name(dso), &bid) > 0) { -+ dso__set_build_id(dso, &bid); - args->have_build_id = true; -- dso__set_has_build_id(dso); - } else if (errno == ENOENT && dso__nsinfo(dso)) { - char *new_name = dso__filename_with_chroot(dso, dso__long_name(dso)); - -- if (new_name && filename__read_build_id(new_name, dso__bid(dso)) > 0) { -+ if (new_name && filename__read_build_id(new_name, &bid) > 0) { -+ dso__set_build_id(dso, &bid); - args->have_build_id = true; -- dso__set_has_build_id(dso); - } - free(new_name); - } -@@ -284,7 +285,7 @@ struct dso *dsos__find(struct dsos *dsos - struct dso *res; - - down_read(&dsos->lock); -- res = __dsos__find_id(dsos, name, NULL, cmp_short, /*write_locked=*/false); -+ res = __dsos__find_id(dsos, name, &dso_id_empty, cmp_short, /*write_locked=*/false); - up_read(&dsos->lock); - return res; - } -@@ -341,8 +342,8 @@ static struct dso *__dsos__findnew_id(st - { - struct dso *dso = __dsos__find_id(dsos, name, id, false, /*write_locked=*/true); - -- if (dso && dso_id__empty(dso__id(dso)) && !dso_id__empty(id)) -- __dso__inject_id(dso, id); -+ if (dso) -+ __dso__improve_id(dso, id); - - return dso ? dso : __dsos__addnew_id(dsos, name, id); - } -@@ -433,7 +434,8 @@ struct dso *dsos__findnew_module_dso(str - - down_write(&dsos->lock); - -- dso = __dsos__find_id(dsos, m->name, NULL, /*cmp_short=*/true, /*write_locked=*/true); -+ dso = __dsos__find_id(dsos, m->name, &dso_id_empty, /*cmp_short=*/true, -+ /*write_locked=*/true); - if (dso) { - up_write(&dsos->lock); - return dso; ---- a/tools/perf/util/machine.c -+++ b/tools/perf/util/machine.c -@@ -1696,21 +1696,21 @@ int machine__process_mmap2_event(struct - { - struct thread *thread; - struct map *map; -- struct dso_id dso_id = { -- .maj = event->mmap2.maj, -- .min = event->mmap2.min, -- .ino = event->mmap2.ino, -- .ino_generation = event->mmap2.ino_generation, -- }; -- struct build_id __bid, *bid = NULL; -+ struct dso_id dso_id = dso_id_empty; - int ret = 0; - - if (dump_trace) - perf_event__fprintf_mmap2(event, stdout); - - if (event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID) { -- bid = &__bid; -- build_id__init(bid, event->mmap2.build_id, event->mmap2.build_id_size); -+ build_id__init(&dso_id.build_id, event->mmap2.build_id, event->mmap2.build_id_size); -+ } else { -+ dso_id.maj = event->mmap2.maj; -+ dso_id.min = event->mmap2.min; -+ dso_id.ino = event->mmap2.ino; -+ dso_id.ino_generation = event->mmap2.ino_generation; -+ dso_id.mmap2_valid = true; -+ dso_id.mmap2_ino_generation_valid = true; - } - - if (sample->cpumode == PERF_RECORD_MISC_GUEST_KERNEL || -@@ -1722,7 +1722,7 @@ int machine__process_mmap2_event(struct - }; - - strlcpy(xm.name, event->mmap2.filename, KMAP_NAME_LEN); -- ret = machine__process_kernel_mmap_event(machine, &xm, bid); -+ ret = machine__process_kernel_mmap_event(machine, &xm, &dso_id.build_id); - if (ret < 0) - goto out_problem; - return 0; -@@ -1736,7 +1736,7 @@ int machine__process_mmap2_event(struct - map = map__new(machine, event->mmap2.start, - event->mmap2.len, event->mmap2.pgoff, - &dso_id, event->mmap2.prot, -- event->mmap2.flags, bid, -+ event->mmap2.flags, - event->mmap2.filename, thread); - - if (map == NULL) -@@ -1794,8 +1794,8 @@ int machine__process_mmap_event(struct m - prot = PROT_EXEC; - - map = map__new(machine, event->mmap.start, -- event->mmap.len, event->mmap.pgoff, -- NULL, prot, 0, NULL, event->mmap.filename, thread); -+ event->mmap.len, event->mmap.pgoff, -+ &dso_id_empty, prot, /*flags=*/0, event->mmap.filename, thread); - - if (map == NULL) - goto out_problem_map; -@@ -3155,7 +3155,7 @@ struct dso *machine__findnew_dso_id(stru - - struct dso *machine__findnew_dso(struct machine *machine, const char *filename) - { -- return machine__findnew_dso_id(machine, filename, NULL); -+ return machine__findnew_dso_id(machine, filename, &dso_id_empty); - } - - char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, char **modp) ---- a/tools/perf/util/map.c -+++ b/tools/perf/util/map.c -@@ -120,8 +120,8 @@ static void map__init(struct map *map, u - } - - struct map *map__new(struct machine *machine, u64 start, u64 len, -- u64 pgoff, struct dso_id *id, -- u32 prot, u32 flags, struct build_id *bid, -+ u64 pgoff, const struct dso_id *id, -+ u32 prot, u32 flags, - char *filename, struct thread *thread) - { - struct map *result; -@@ -132,7 +132,7 @@ struct map *map__new(struct machine *mac - map = zalloc(sizeof(*map)); - if (ADD_RC_CHK(result, map)) { - char newfilename[PATH_MAX]; -- struct dso *dso, *header_bid_dso; -+ struct dso *dso; - int anon, no_dso, vdso, android; - - android = is_android_lib(filename); -@@ -189,16 +189,15 @@ struct map *map__new(struct machine *mac - dso__set_nsinfo(dso, nsi); - mutex_unlock(dso__lock(dso)); - -- if (build_id__is_defined(bid)) { -- dso__set_build_id(dso, bid); -- } else { -+ if (!build_id__is_defined(&id->build_id)) { - /* - * If the mmap event had no build ID, search for an existing dso from the - * build ID header by name. Otherwise only the dso loaded at the time of - * reading the header will have the build ID set and all future mmaps will - * have it missing. - */ -- header_bid_dso = dsos__find(&machine->dsos, filename, false); -+ struct dso *header_bid_dso = dsos__find(&machine->dsos, filename, false); -+ - if (header_bid_dso && dso__header_build_id(header_bid_dso)) { - dso__set_build_id(dso, dso__bid(header_bid_dso)); - dso__set_header_build_id(dso, 1); ---- a/tools/perf/util/map.h -+++ b/tools/perf/util/map.h -@@ -173,11 +173,10 @@ struct thread; - __map__for_each_symbol_by_name(map, sym_name, (pos), idx) - - struct dso_id; --struct build_id; - - struct map *map__new(struct machine *machine, u64 start, u64 len, -- u64 pgoff, struct dso_id *id, u32 prot, u32 flags, -- struct build_id *bid, char *filename, struct thread *thread); -+ u64 pgoff, const struct dso_id *id, u32 prot, u32 flags, -+ char *filename, struct thread *thread); - struct map *map__new2(u64 start, struct dso *dso); - void map__delete(struct map *map); - struct map *map__clone(struct map *map); ---- a/tools/perf/util/sort.c -+++ b/tools/perf/util/sort.c -@@ -1579,22 +1579,27 @@ sort__dcacheline_cmp(struct hist_entry * - if (rc) - return rc; - /* -- * Addresses with no major/minor numbers are assumed to be -+ * Addresses with no major/minor numbers or build ID are assumed to be - * anonymous in userspace. Sort those on pid then address. - * - * The kernel and non-zero major/minor mapped areas are - * assumed to be unity mapped. Sort those on address. - */ -+ if (left->cpumode != PERF_RECORD_MISC_KERNEL && (map__flags(l_map) & MAP_SHARED) == 0) { -+ const struct dso_id *dso_id = dso__id_const(l_dso); - -- if ((left->cpumode != PERF_RECORD_MISC_KERNEL) && -- (!(map__flags(l_map) & MAP_SHARED)) && !dso__id(l_dso)->maj && !dso__id(l_dso)->min && -- !dso__id(l_dso)->ino && !dso__id(l_dso)->ino_generation) { -- /* userspace anonymous */ -- -- if (thread__pid(left->thread) > thread__pid(right->thread)) -- return -1; -- if (thread__pid(left->thread) < thread__pid(right->thread)) -- return 1; -+ if (!dso_id->mmap2_valid) -+ dso_id = dso__id_const(r_dso); -+ -+ if (!build_id__is_defined(&dso_id->build_id) && -+ (!dso_id->mmap2_valid || (dso_id->maj == 0 && dso_id->min == 0))) { -+ /* userspace anonymous */ -+ -+ if (thread__pid(left->thread) > thread__pid(right->thread)) -+ return -1; -+ if (thread__pid(left->thread) < thread__pid(right->thread)) -+ return 1; -+ } - } - - addr: -@@ -1619,6 +1624,7 @@ static int hist_entry__dcacheline_snprin - if (he->mem_info) { - struct map *map = mem_info__daddr(he->mem_info)->ms.map; - struct dso *dso = map ? map__dso(map) : NULL; -+ const struct dso_id *dso_id = dso ? dso__id_const(dso) : &dso_id_empty; - - addr = cl_address(mem_info__daddr(he->mem_info)->al_addr, chk_double_cl); - ms = &mem_info__daddr(he->mem_info)->ms; -@@ -1627,8 +1633,7 @@ static int hist_entry__dcacheline_snprin - if ((he->cpumode != PERF_RECORD_MISC_KERNEL) && - map && !(map__prot(map) & PROT_EXEC) && - (map__flags(map) & MAP_SHARED) && -- (dso__id(dso)->maj || dso__id(dso)->min || dso__id(dso)->ino || -- dso__id(dso)->ino_generation)) -+ (!dso_id->mmap2_valid || (dso_id->maj == 0 && dso_id->min == 0))) - level = 's'; - else if (!map) - level = 'X'; ---- a/tools/perf/util/synthetic-events.c -+++ b/tools/perf/util/synthetic-events.c -@@ -371,7 +371,7 @@ static void perf_record_mmap2__read_buil - struct nsinfo *nsi; - struct nscookie nc; - struct dso *dso = NULL; -- struct dso_id id; -+ struct dso_id dso_id = dso_id_empty; - int rc; - - if (is_kernel) { -@@ -379,12 +379,18 @@ static void perf_record_mmap2__read_buil - goto out; - } - -- id.maj = event->maj; -- id.min = event->min; -- id.ino = event->ino; -- id.ino_generation = event->ino_generation; -+ if (event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID) { -+ build_id__init(&dso_id.build_id, event->build_id, event->build_id_size); -+ } else { -+ dso_id.maj = event->maj; -+ dso_id.min = event->min; -+ dso_id.ino = event->ino; -+ dso_id.ino_generation = event->ino_generation; -+ dso_id.mmap2_valid = true; -+ dso_id.mmap2_ino_generation_valid = true; -+ }; - -- dso = dsos__findnew_id(&machine->dsos, event->filename, &id); -+ dso = dsos__findnew_id(&machine->dsos, event->filename, &dso_id); - if (dso && dso__has_build_id(dso)) { - bid = *dso__bid(dso); - rc = 0; diff --git a/queue-6.12/perf-dso-use-lock-annotations-to-fix-asan-deadlock.patch b/queue-6.12/perf-dso-use-lock-annotations-to-fix-asan-deadlock.patch deleted file mode 100644 index b02128aa90..0000000000 --- a/queue-6.12/perf-dso-use-lock-annotations-to-fix-asan-deadlock.patch +++ /dev/null @@ -1,360 +0,0 @@ -From a70398930ce7dc7600103884f6fe27e710d871ea Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Mon, 17 Mar 2025 21:31:50 -0700 -Subject: perf dso: Use lock annotations to fix asan deadlock - -From: Ian Rogers - -[ Upstream commit 5ac22c35aa8519f1fb5522fb3aff915f28a9365e ] - -dso__list_del with address sanitizer and/or reference count checking -will call dso__put that can call dso__data_close reentrantly trying to -lock the dso__data_open_lock and deadlocking. Switch from pthread -mutexes to perf's mutex so that lock checking is performed in debug -builds. Add lock annotations that diagnosed the problem. Release the -dso__data_open_lock around the dso__put to avoid the deadlock. - -Change the declaration of dso__data_get_fd to return a boolean, -indicating the fd is valid and the lock is held, to make it compatible -with the thread safety annotations as a try lock. - -Signed-off-by: Ian Rogers -Link: https://lore.kernel.org/r/20250318043151.137973-3-irogers@google.com -Signed-off-by: Namhyung Kim -Stable-dep-of: 2ea64782a428 ("perf tools: Use snprintf() in dso__read_running_kernel_build_id()") -Signed-off-by: Sasha Levin ---- - tools/perf/tests/dso-data.c | 4 +- - tools/perf/util/dso.c | 74 +++++++++++++++--------- - tools/perf/util/dso.h | 15 +++-- - tools/perf/util/unwind-libunwind-local.c | 16 ++--- - 4 files changed, 66 insertions(+), 43 deletions(-) - -diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c -index 5286ae8bd2d70d..06be7c5d849554 100644 ---- a/tools/perf/tests/dso-data.c -+++ b/tools/perf/tests/dso-data.c -@@ -106,9 +106,9 @@ struct test_data_offset offsets[] = { - /* move it from util/dso.c for compatibility */ - static int dso__data_fd(struct dso *dso, struct machine *machine) - { -- int fd = dso__data_get_fd(dso, machine); -+ int fd = -1; - -- if (fd >= 0) -+ if (dso__data_get_fd(dso, machine, &fd)) - dso__data_put_fd(dso); - - return fd; -diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c -index 5c6e85fdae0de1..05a9dc6f41f820 100644 ---- a/tools/perf/util/dso.c -+++ b/tools/perf/util/dso.c -@@ -490,11 +490,25 @@ void dso__set_module_info(struct dso *dso, struct kmod_path *m, - /* - * Global list of open DSOs and the counter. - */ -+struct mutex _dso__data_open_lock; - static LIST_HEAD(dso__data_open); --static long dso__data_open_cnt; --static pthread_mutex_t dso__data_open_lock = PTHREAD_MUTEX_INITIALIZER; -+static long dso__data_open_cnt GUARDED_BY(_dso__data_open_lock); - --static void dso__list_add(struct dso *dso) -+static void dso__data_open_lock_init(void) -+{ -+ mutex_init(&_dso__data_open_lock); -+} -+ -+static struct mutex *dso__data_open_lock(void) LOCK_RETURNED(_dso__data_open_lock) -+{ -+ static pthread_once_t data_open_lock_once = PTHREAD_ONCE_INIT; -+ -+ pthread_once(&data_open_lock_once, dso__data_open_lock_init); -+ -+ return &_dso__data_open_lock; -+} -+ -+static void dso__list_add(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) - { - list_add_tail(&dso__data(dso)->open_entry, &dso__data_open); - #ifdef REFCNT_CHECKING -@@ -505,11 +519,13 @@ static void dso__list_add(struct dso *dso) - dso__data_open_cnt++; - } - --static void dso__list_del(struct dso *dso) -+static void dso__list_del(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) - { - list_del_init(&dso__data(dso)->open_entry); - #ifdef REFCNT_CHECKING -+ mutex_unlock(dso__data_open_lock()); - dso__put(dso__data(dso)->dso); -+ mutex_lock(dso__data_open_lock()); - #endif - WARN_ONCE(dso__data_open_cnt <= 0, - "DSO data fd counter out of bounds."); -@@ -518,7 +534,7 @@ static void dso__list_del(struct dso *dso) - - static void close_first_dso(void); - --static int do_open(char *name) -+static int do_open(char *name) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) - { - int fd; - char sbuf[STRERR_BUFSIZE]; -@@ -545,6 +561,7 @@ char *dso__filename_with_chroot(const struct dso *dso, const char *filename) - } - - static int __open_dso(struct dso *dso, struct machine *machine) -+ EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) - { - int fd = -EINVAL; - char *root_dir = (char *)""; -@@ -610,6 +627,7 @@ static void check_data_close(void); - * list/count of open DSO objects. - */ - static int open_dso(struct dso *dso, struct machine *machine) -+ EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) - { - int fd; - struct nscookie nsc; -@@ -635,7 +653,7 @@ static int open_dso(struct dso *dso, struct machine *machine) - return fd; - } - --static void close_data_fd(struct dso *dso) -+static void close_data_fd(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) - { - if (dso__data(dso)->fd >= 0) { - close(dso__data(dso)->fd); -@@ -652,12 +670,12 @@ static void close_data_fd(struct dso *dso) - * Close @dso's data file descriptor and updates - * list/count of open DSO objects. - */ --static void close_dso(struct dso *dso) -+static void close_dso(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) - { - close_data_fd(dso); - } - --static void close_first_dso(void) -+static void close_first_dso(void) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) - { - struct dso_data *dso_data; - struct dso *dso; -@@ -702,7 +720,7 @@ void reset_fd_limit(void) - fd_limit = 0; - } - --static bool may_cache_fd(void) -+static bool may_cache_fd(void) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) - { - if (!fd_limit) - fd_limit = get_fd_limit(); -@@ -718,7 +736,7 @@ static bool may_cache_fd(void) - * for opened dso file descriptors. The limit is half - * of the RLIMIT_NOFILE files opened. - */ --static void check_data_close(void) -+static void check_data_close(void) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) - { - bool cache_fd = may_cache_fd(); - -@@ -734,12 +752,13 @@ static void check_data_close(void) - */ - void dso__data_close(struct dso *dso) - { -- pthread_mutex_lock(&dso__data_open_lock); -+ mutex_lock(dso__data_open_lock()); - close_dso(dso); -- pthread_mutex_unlock(&dso__data_open_lock); -+ mutex_unlock(dso__data_open_lock()); - } - - static void try_to_open_dso(struct dso *dso, struct machine *machine) -+ EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) - { - enum dso_binary_type binary_type_data[] = { - DSO_BINARY_TYPE__BUILD_ID_CACHE, -@@ -781,25 +800,27 @@ static void try_to_open_dso(struct dso *dso, struct machine *machine) - * returns file descriptor. It should be paired with - * dso__data_put_fd() if it returns non-negative value. - */ --int dso__data_get_fd(struct dso *dso, struct machine *machine) -+bool dso__data_get_fd(struct dso *dso, struct machine *machine, int *fd) - { -+ *fd = -1; - if (dso__data(dso)->status == DSO_DATA_STATUS_ERROR) -- return -1; -+ return false; - -- if (pthread_mutex_lock(&dso__data_open_lock) < 0) -- return -1; -+ mutex_lock(dso__data_open_lock()); - - try_to_open_dso(dso, machine); - -- if (dso__data(dso)->fd < 0) -- pthread_mutex_unlock(&dso__data_open_lock); -+ *fd = dso__data(dso)->fd; -+ if (*fd >= 0) -+ return true; - -- return dso__data(dso)->fd; -+ mutex_unlock(dso__data_open_lock()); -+ return false; - } - - void dso__data_put_fd(struct dso *dso __maybe_unused) - { -- pthread_mutex_unlock(&dso__data_open_lock); -+ mutex_unlock(dso__data_open_lock()); - } - - bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by) -@@ -951,7 +972,7 @@ static ssize_t file_read(struct dso *dso, struct machine *machine, - { - ssize_t ret; - -- pthread_mutex_lock(&dso__data_open_lock); -+ mutex_lock(dso__data_open_lock()); - - /* - * dso__data(dso)->fd might be closed if other thread opened another -@@ -967,7 +988,7 @@ static ssize_t file_read(struct dso *dso, struct machine *machine, - - ret = pread(dso__data(dso)->fd, data, DSO__DATA_CACHE_SIZE, offset); - out: -- pthread_mutex_unlock(&dso__data_open_lock); -+ mutex_unlock(dso__data_open_lock()); - return ret; - } - -@@ -1075,7 +1096,7 @@ static int file_size(struct dso *dso, struct machine *machine) - struct stat st; - char sbuf[STRERR_BUFSIZE]; - -- pthread_mutex_lock(&dso__data_open_lock); -+ mutex_lock(dso__data_open_lock()); - - /* - * dso__data(dso)->fd might be closed if other thread opened another -@@ -1099,7 +1120,7 @@ static int file_size(struct dso *dso, struct machine *machine) - dso__data(dso)->file_size = st.st_size; - - out: -- pthread_mutex_unlock(&dso__data_open_lock); -+ mutex_unlock(dso__data_open_lock()); - return ret; - } - -@@ -1608,11 +1629,10 @@ size_t dso__fprintf(struct dso *dso, FILE *fp) - - enum dso_type dso__type(struct dso *dso, struct machine *machine) - { -- int fd; -+ int fd = -1; - enum dso_type type = DSO__TYPE_UNKNOWN; - -- fd = dso__data_get_fd(dso, machine); -- if (fd >= 0) { -+ if (dso__data_get_fd(dso, machine, &fd)) { - type = dso__type_fd(fd); - dso__data_put_fd(dso); - } -diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h -index c0472a41147c3c..53eac48fd0cd81 100644 ---- a/tools/perf/util/dso.h -+++ b/tools/perf/util/dso.h -@@ -231,6 +231,8 @@ DECLARE_RC_STRUCT(dso) { - char name[]; - }; - -+extern struct mutex _dso__data_open_lock; -+ - /* dso__for_each_symbol - iterate over the symbols of given type - * - * @dso: the 'struct dso *' in which symbols are iterated -@@ -652,7 +654,7 @@ void __dso__inject_id(struct dso *dso, const struct dso_id *id); - int dso__name_len(const struct dso *dso); - - struct dso *dso__get(struct dso *dso); --void dso__put(struct dso *dso); -+void dso__put(struct dso *dso) LOCKS_EXCLUDED(_dso__data_open_lock); - - static inline void __dso__zput(struct dso **dso) - { -@@ -732,8 +734,8 @@ void dso__set_module_info(struct dso *dso, struct kmod_path *m, - * The current usage of the dso__data_* interface is as follows: - * - * Get DSO's fd: -- * int fd = dso__data_get_fd(dso, machine); -- * if (fd >= 0) { -+ * int fd; -+ * if (dso__data_get_fd(dso, machine, &fd)) { - * USE 'fd' SOMEHOW - * dso__data_put_fd(dso); - * } -@@ -755,9 +757,10 @@ void dso__set_module_info(struct dso *dso, struct kmod_path *m, - * - * TODO - */ --int dso__data_get_fd(struct dso *dso, struct machine *machine); --void dso__data_put_fd(struct dso *dso); --void dso__data_close(struct dso *dso); -+bool dso__data_get_fd(struct dso *dso, struct machine *machine, int *fd) -+ EXCLUSIVE_TRYLOCK_FUNCTION(true, _dso__data_open_lock); -+void dso__data_put_fd(struct dso *dso) UNLOCK_FUNCTION(_dso__data_open_lock); -+void dso__data_close(struct dso *dso) LOCKS_EXCLUDED(_dso__data_open_lock); - - int dso__data_file_size(struct dso *dso, struct machine *machine); - off_t dso__data_size(struct dso *dso, struct machine *machine); -diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c -index 16c2b03831f39a..4c076eba86226c 100644 ---- a/tools/perf/util/unwind-libunwind-local.c -+++ b/tools/perf/util/unwind-libunwind-local.c -@@ -330,8 +330,7 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *ui, - int ret, fd; - - if (dso__data(dso)->eh_frame_hdr_offset == 0) { -- fd = dso__data_get_fd(dso, ui->machine); -- if (fd < 0) -+ if (!dso__data_get_fd(dso, ui->machine, &fd)) - return -EINVAL; - - /* Check the .eh_frame section for unwinding info */ -@@ -372,8 +371,7 @@ static int read_unwind_spec_debug_frame(struct dso *dso, - * has to be pointed by symsrc_filename - */ - if (ofs == 0) { -- fd = dso__data_get_fd(dso, machine); -- if (fd >= 0) { -+ if (dso__data_get_fd(dso, machine, &fd) { - ofs = elf_section_offset(fd, ".debug_frame"); - dso__data_put_fd(dso); - } -@@ -485,14 +483,16 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, - /* Check the .debug_frame section for unwinding info */ - if (ret < 0 && - !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) { -- int fd = dso__data_get_fd(dso, ui->machine); -- int is_exec = elf_is_exec(fd, dso__name(dso)); -+ int fd; - u64 start = map__start(map); -- unw_word_t base = is_exec ? 0 : start; -+ unw_word_t base = start; - const char *symfile; - -- if (fd >= 0) -+ if (dso__data_get_fd(dso, ui->machine, &fd)) { -+ if (elf_is_exec(fd, dso__name(dso))) -+ base = 0; - dso__data_put_fd(dso); -+ } - - symfile = dso__symsrc_filename(dso) ?: dso__name(dso); - --- -2.53.0 - diff --git a/queue-6.12/perf-header-sanity-check-header_event_desc-attr.size.patch b/queue-6.12/perf-header-sanity-check-header_event_desc-attr.size.patch deleted file mode 100644 index 69fbef088b..0000000000 --- a/queue-6.12/perf-header-sanity-check-header_event_desc-attr.size.patch +++ /dev/null @@ -1,140 +0,0 @@ -From 74a5f7a871a93ad8a713e5dc3674dea340ebb385 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 2 May 2026 14:37:39 -0300 -Subject: perf header: Sanity check HEADER_EVENT_DESC attr.size before swap -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 944f65c8b8231d26d4db6be67bcb641142603cb4 ] - -read_event_desc() reads nre (event count), sz (attr size), and nr -(IDs per event) from the file and uses them to control allocations -and loops without validating them against the section size. - -A crafted perf.data could trigger large allocations or many loop -iterations before __do_read() eventually rejects the reads. - -Add bounds checks in read_event_desc(): -- Reject sz smaller than PERF_ATTR_SIZE_VER0. -- Require at least one event (nre > 0). -- Check that nre events fit in the remaining section, using the - minimum per-event footprint of sz + sizeof(u32). -- Pre-swap attr->size to native byte order, then reject values - below PERF_ATTR_SIZE_VER0 or above sz before calling - perf_event__attr_swap() to prevent heap out-of-bounds access. -- Handle ABI0 (attr.size == 0): substitute PERF_ATTR_SIZE_VER0, - and on native-endian files write the value back so - free_event_desc() does not treat the zero as its end-of-array - sentinel (it iterates while attr.size != 0). The swap path - skips the write-back — perf_event__attr_swap() has its own - ABI0 fallback that sets VER0 after swapping. -- Check that nr IDs fit in the remaining section before allocating. - -Fixes: b30b61729246 ("perf tools: Fix a problem when opening old perf.data with different byte order") -Reported-by: sashiko-bot@kernel.org # Running on a local machine -Reviewed-by: Ian Rogers -Cc: Jiri Olsa -Cc: Namhyung Kim -Cc: Wang Nan -Assisted-by: Claude:claude-opus-4.6-1m -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/header.c | 54 ++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 54 insertions(+) - -diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c -index 7b99f58f7bf269..8c15ce90466997 100644 ---- a/tools/perf/util/header.c -+++ b/tools/perf/util/header.c -@@ -1940,9 +1940,28 @@ static struct evsel *read_event_desc(struct feat_fd *ff) - if (do_read_u32(ff, &nre)) - goto error; - -+ /* Size of each of the nre attributes. */ - if (do_read_u32(ff, &sz)) - goto error; - -+ /* -+ * Require at least one event with an attr no smaller than the -+ * first published struct, and reject sz values where -+ * sz + sizeof(u32) would overflow size_t (possible on 32-bit) -+ * or nre == UINT32_MAX where nre + 1 wraps to 0 in the calloc. -+ * -+ * The minimum section footprint per event is sz bytes for the -+ * attr plus a u32 for the id count, check that nre events fit. -+ */ -+ if (!nre || sz < PERF_ATTR_SIZE_VER0 || -+ sz > ff->size || (size_t)sz > SIZE_MAX - sizeof(u32) || -+ nre == UINT32_MAX || -+ nre > (ff->size - ff->offset) / (sz + sizeof(u32))) { -+ pr_err("Invalid HEADER_EVENT_DESC: nre=%u sz=%u (min %d)\n", -+ nre, sz, PERF_ATTR_SIZE_VER0); -+ goto error; -+ } -+ - /* buffer to hold on file attr struct */ - buf = malloc(sz); - if (!buf) -@@ -1958,6 +1977,9 @@ static struct evsel *read_event_desc(struct feat_fd *ff) - msz = sz; - - for (i = 0, evsel = events; i < nre; evsel++, i++) { -+ struct perf_event_attr *attr = buf; -+ u32 attr_size; -+ - evsel->core.idx = i; - - /* -@@ -1967,6 +1989,32 @@ static struct evsel *read_event_desc(struct feat_fd *ff) - if (__do_read(ff, buf, sz)) - goto error; - -+ /* Reject before attr_swap to prevent OOB via bswap_safe() */ -+ attr_size = ff->ph->needs_swap ? bswap_32(attr->size) : attr->size; -+ /* ABI0: size == 0 means the producer didn't set it */ -+ if (!attr_size) { -+ attr_size = PERF_ATTR_SIZE_VER0; -+ /* -+ * Write back so free_event_desc() doesn't -+ * treat this event as the end-of-array sentinel -+ * (it iterates while attr.size != 0). -+ * -+ * Only for native — the swap path must NOT -+ * write native-endian VER0 here because -+ * perf_event__attr_swap() would re-swap it -+ * to 0x40000000, defeating bswap_safe() bounds. -+ * perf_event__attr_swap() has its own ABI0 -+ * fallback that sets VER0 after swapping. -+ */ -+ if (!ff->ph->needs_swap) -+ attr->size = attr_size; -+ } -+ if (attr_size < PERF_ATTR_SIZE_VER0 || attr_size > sz) { -+ pr_err("Event %d attr.size (%u) invalid (min: %d, max: %u)\n", -+ i, attr_size, PERF_ATTR_SIZE_VER0, sz); -+ goto error; -+ } -+ - if (ff->ph->needs_swap) - perf_event__attr_swap(buf); - -@@ -1988,6 +2036,12 @@ static struct evsel *read_event_desc(struct feat_fd *ff) - if (!nr) - continue; - -+ /* Prevent oversized allocation from crafted nr */ -+ if (nr > (ff->size - ff->offset) / sizeof(*id)) { -+ pr_err("Event %d: id count %u exceeds remaining section\n", i, nr); -+ goto error; -+ } -+ - id = calloc(nr, sizeof(*id)); - if (!id) - goto error; --- -2.53.0 - diff --git a/queue-6.12/perf-hists-fix-snprintf-in-hists__scnprintf_title-ui.patch b/queue-6.12/perf-hists-fix-snprintf-in-hists__scnprintf_title-ui.patch deleted file mode 100644 index 1b3c05950f..0000000000 --- a/queue-6.12/perf-hists-fix-snprintf-in-hists__scnprintf_title-ui.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 6da2b221ce3fa75964b2932e94385d3057641443 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sun, 7 Jun 2026 14:35:28 -0300 -Subject: perf hists: Fix snprintf() in hists__scnprintf_title() UID filter - path - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 227a8748742f0263f1fe3131449b44563b77a209 ] - -hists__scnprintf_title() accumulates formatted output into a buffer -using scnprintf() for all filter clauses except the UID filter, which -uses snprintf(). If the buffer fills up and snprintf() returns more -than the remaining space, printed exceeds size and the next 'size - -printed' underflows, causing later scnprintf() calls to write past -the buffer. - -Switch the UID filter clause to scnprintf() to match the rest of the -function. - -Fixes: 25c312dbf88ca402 ("perf hists: Move hists__scnprintf_title() away from the TUI code") -Reported-by: sashiko-bot -Reviewed-by: Ian Rogers -Cc: Arnaldo Carvalho de Melo -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/hist.c | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c -index 694faf405e11cf..299bfca446a698 100644 ---- a/tools/perf/util/hist.c -+++ b/tools/perf/util/hist.c -@@ -2816,9 +2816,10 @@ int __hists__scnprintf_title(struct hists *hists, char *bf, size_t size, bool sh - ev_name, sample_freq_str, enable_ref ? ref : " ", nr_events); - - -- if (hists->uid_filter_str) -- printed += snprintf(bf + printed, size - printed, -- ", UID: %s", hists->uid_filter_str); -+ if (hists->uid_filter_str) { -+ printed += scnprintf(bf + printed, size - printed, -+ ", UID: %s", hists->uid_filter_str); -+ } - if (thread) { - if (hists__has(hists, thread)) { - printed += scnprintf(bf + printed, size - printed, --- -2.53.0 - diff --git a/queue-6.12/perf-inject-add-convert-callchain-option.patch b/queue-6.12/perf-inject-add-convert-callchain-option.patch deleted file mode 100644 index 658a581a1e..0000000000 --- a/queue-6.12/perf-inject-add-convert-callchain-option.patch +++ /dev/null @@ -1,313 +0,0 @@ -From b386345dedddd6b741c541fa75444d7e1ad04de8 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Tue, 13 Jan 2026 15:29:02 -0800 -Subject: perf inject: Add --convert-callchain option - -From: Namhyung Kim - -[ Upstream commit 92ea788d2af4e65ad7a144ccfff50667e9a0d227 ] - -There are applications not built with frame pointers, so DWARF is needed -to get the stack traces. - -`perf record --call-graph dwarf` saves the stack and register data for -each sample to get the stacktrace offline. But sometimes this data may -have sensitive information and we don't want to keep them in the file. - -This new 'perf inject --convert-callchain' option creates the callchains -and discards the stack and register after that. - -This saves storage space and processing time for the new data file. - -Of course, users should remove the original data file to not keep -sensitive data around. :) - -The down side is that it cannot handle inlined callchain entries as they -all have the same IPs. - -Maybe we can add an option to 'perf report' to look up inlined functions -using DWARF - IIUC it doesn't require stack and register data. - -This is an example. - - $ perf record --call-graph dwarf -- perf test -w noploop - - $ perf report --stdio --no-children --percent-limit=0 > output-prev - - $ perf inject -i perf.data --convert-callchain -o perf.data.out - - $ perf report --stdio --no-children --percent-limit=0 -i perf.data.out > output-next - - $ diff -u output-prev output-next - ... - 0.23% perf ld-linux-x86-64.so.2 [.] _dl_relocate_object_no_relro - | - - ---elf_dynamic_do_Rela (inlined) - - _dl_relocate_object_no_relro - + ---_dl_relocate_object_no_relro - _dl_relocate_object - dl_main - _dl_sysdep_start - - _dl_start_final (inlined) - _dl_start - _start - -Reviewed-by: Ian Rogers -Signed-off-by: Namhyung Kim -Cc: Adrian Hunter -Cc: Ingo Molnar -Cc: James Clark -Cc: Jiri Olsa -Cc: Peter Zijlstra -Signed-off-by: Arnaldo Carvalho de Melo -Stable-dep-of: 059e9100d82a ("perf event: Fix size of synthesized sample with branch stacks") -Signed-off-by: Sasha Levin ---- - tools/perf/Documentation/perf-inject.txt | 5 + - tools/perf/builtin-inject.c | 152 +++++++++++++++++++++++ - 2 files changed, 157 insertions(+) - -diff --git a/tools/perf/Documentation/perf-inject.txt b/tools/perf/Documentation/perf-inject.txt -index c972032f4ca0d2..95dfdf39666efe 100644 ---- a/tools/perf/Documentation/perf-inject.txt -+++ b/tools/perf/Documentation/perf-inject.txt -@@ -109,6 +109,11 @@ include::itrace.txt[] - should be used, and also --buildid-all and --switch-events may be - useful. - -+--convert-callchain:: -+ Parse DWARF callchains and convert them to usual callchains. This also -+ discards stack and register data from the samples. This will lose -+ inlined callchain entries. -+ - :GMEXAMPLECMD: inject - :GMEXAMPLESUBCMD: - include::guestmount.txt[] -diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c -index 11e49cafa3af9d..241d7e36914872 100644 ---- a/tools/perf/builtin-inject.c -+++ b/tools/perf/builtin-inject.c -@@ -122,6 +122,7 @@ struct perf_inject { - bool in_place_update; - bool in_place_update_dry_run; - bool copy_kcore_dir; -+ bool convert_callchain; - const char *input_name; - struct perf_data output; - u64 bytes_written; -@@ -133,6 +134,7 @@ struct perf_inject { - struct guest_session guest_session; - struct strlist *known_build_ids; - const struct evsel *mmap_evsel; -+ struct ip_callchain *raw_callchain; - }; - - struct event_entry { -@@ -396,6 +398,90 @@ static int perf_event__repipe_sample(const struct perf_tool *tool, - return perf_event__repipe_synth(tool, event); - } - -+static int perf_event__convert_sample_callchain(const struct perf_tool *tool, -+ union perf_event *event, -+ struct perf_sample *sample, -+ struct evsel *evsel, -+ struct machine *machine) -+{ -+ struct perf_inject *inject = container_of(tool, struct perf_inject, tool); -+ struct callchain_cursor *cursor = get_tls_callchain_cursor(); -+ union perf_event *event_copy = (void *)inject->event_copy; -+ struct callchain_cursor_node *node; -+ struct thread *thread; -+ u64 sample_type = evsel->core.attr.sample_type; -+ u32 sample_size = event->header.size; -+ u64 i, k; -+ int ret; -+ -+ if (event_copy == NULL) { -+ inject->event_copy = malloc(PERF_SAMPLE_MAX_SIZE); -+ if (!inject->event_copy) -+ return -ENOMEM; -+ -+ event_copy = (void *)inject->event_copy; -+ } -+ -+ if (cursor == NULL) -+ return -ENOMEM; -+ -+ callchain_cursor_reset(cursor); -+ -+ thread = machine__find_thread(machine, sample->tid, sample->pid); -+ if (thread == NULL) -+ goto out; -+ -+ /* this will parse DWARF using stack and register data */ -+ ret = thread__resolve_callchain(thread, cursor, evsel, sample, -+ /*parent=*/NULL, /*root_al=*/NULL, -+ PERF_MAX_STACK_DEPTH); -+ thread__put(thread); -+ if (ret != 0) -+ goto out; -+ -+ /* copy kernel callchain and context entries */ -+ for (i = 0; i < sample->callchain->nr; i++) { -+ inject->raw_callchain->ips[i] = sample->callchain->ips[i]; -+ if (sample->callchain->ips[i] == PERF_CONTEXT_USER) { -+ i++; -+ break; -+ } -+ } -+ if (i == 0 || inject->raw_callchain->ips[i - 1] != PERF_CONTEXT_USER) -+ inject->raw_callchain->ips[i++] = PERF_CONTEXT_USER; -+ -+ node = cursor->first; -+ for (k = 0; k < cursor->nr && i < PERF_MAX_STACK_DEPTH; k++) { -+ if (machine__kernel_ip(machine, node->ip)) -+ /* kernel IPs were added already */; -+ else if (node->ms.sym && node->ms.sym->inlined) -+ /* we can't handle inlined callchains */; -+ else -+ inject->raw_callchain->ips[i++] = node->ip; -+ -+ node = node->next; -+ } -+ -+ inject->raw_callchain->nr = i; -+ sample->callchain = inject->raw_callchain; -+ -+out: -+ memcpy(event_copy, event, sizeof(event->header)); -+ -+ /* adjust sample size for stack and regs */ -+ sample_size -= sample->user_stack.size; -+ sample_size -= (hweight64(evsel->core.attr.sample_regs_user) + 1) * sizeof(u64); -+ sample_size += (sample->callchain->nr + 1) * sizeof(u64); -+ event_copy->header.size = sample_size; -+ -+ /* remove sample_type {STACK,REGS}_USER for synthesize */ -+ sample_type &= ~(PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER); -+ -+ perf_event__synthesize_sample(event_copy, sample_type, -+ evsel->core.attr.read_format, sample); -+ return perf_event__repipe_synth(tool, event_copy); -+} -+ - static struct dso *findnew_dso(int pid, int tid, const char *filename, - const struct dso_id *id, struct machine *machine) - { -@@ -2274,6 +2360,15 @@ static int __cmd_inject(struct perf_inject *inject) - /* Allow space in the header for guest attributes */ - output_data_offset += gs->session->header.data_offset; - output_data_offset = roundup(output_data_offset, 4096); -+ } else if (inject->convert_callchain) { -+ inject->tool.sample = perf_event__convert_sample_callchain; -+ inject->tool.fork = perf_event__repipe_fork; -+ inject->tool.comm = perf_event__repipe_comm; -+ inject->tool.exit = perf_event__repipe_exit; -+ inject->tool.mmap = perf_event__repipe_mmap; -+ inject->tool.mmap2 = perf_event__repipe_mmap2; -+ inject->tool.ordered_events = true; -+ inject->tool.ordering_requires_timestamps = true; - } - - if (!inject->itrace_synth_opts.set) -@@ -2326,6 +2421,23 @@ static int __cmd_inject(struct perf_inject *inject) - perf_header__set_feat(&session->header, - HEADER_BRANCH_STACK); - } -+ -+ /* -+ * The converted data file won't have stack and registers. -+ * Update the perf_event_attr to remove them before writing. -+ */ -+ if (inject->convert_callchain) { -+ struct evsel *evsel; -+ -+ evlist__for_each_entry(session->evlist, evsel) { -+ evsel__reset_sample_bit(evsel, REGS_USER); -+ evsel__reset_sample_bit(evsel, STACK_USER); -+ evsel->core.attr.sample_regs_user = 0; -+ evsel->core.attr.sample_stack_user = 0; -+ evsel->core.attr.exclude_callchain_user = 0; -+ } -+ } -+ - session->header.data_offset = output_data_offset; - session->header.data_size = inject->bytes_written; - perf_session__inject_header(session, session->evlist, fd, &inj_fc.fc, -@@ -2350,6 +2462,18 @@ static int __cmd_inject(struct perf_inject *inject) - return ret; - } - -+static bool evsel__has_dwarf_callchain(struct evsel *evsel) -+{ -+ struct perf_event_attr *attr = &evsel->core.attr; -+ const u64 dwarf_callchain_flags = -+ PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_CALLCHAIN; -+ -+ if (!attr->exclude_callchain_user) -+ return false; -+ -+ return (attr->sample_type & dwarf_callchain_flags) == dwarf_callchain_flags; -+} -+ - int cmd_inject(int argc, const char **argv) - { - struct perf_inject inject = { -@@ -2418,6 +2542,8 @@ int cmd_inject(int argc, const char **argv) - OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory", - "guest mount directory under which every guest os" - " instance has a subdir"), -+ OPT_BOOLEAN(0, "convert-callchain", &inject.convert_callchain, -+ "Generate callchains using DWARF and drop register/stack data"), - OPT_END() - }; - const char * const inject_usage[] = { -@@ -2433,6 +2559,9 @@ int cmd_inject(int argc, const char **argv) - - #ifndef HAVE_JITDUMP - set_option_nobuild(options, 'j', "jit", "NO_LIBELF=1", true); -+#endif -+#ifndef HAVE_LIBDW_SUPPORT -+ set_option_nobuild(options, 0, "convert-callchain", "NO_LIBDW=1", true); - #endif - argc = parse_options(argc, argv, options, inject_usage, 0); - -@@ -2589,6 +2718,28 @@ int cmd_inject(int argc, const char **argv) - } - } - -+ if (inject.convert_callchain) { -+ struct evsel *evsel; -+ -+ if (inject.output.is_pipe || inject.session->data->is_pipe) { -+ pr_err("--convert-callchain cannot work with pipe\n"); -+ goto out_delete; -+ } -+ -+ evlist__for_each_entry(inject.session->evlist, evsel) { -+ if (!evsel__has_dwarf_callchain(evsel)) { -+ pr_err("--convert-callchain requires DWARF call graph.\n"); -+ goto out_delete; -+ } -+ } -+ -+ inject.raw_callchain = calloc(PERF_MAX_STACK_DEPTH, sizeof(u64)); -+ if (inject.raw_callchain == NULL) { -+ pr_err("callchain allocation failed\n"); -+ goto out_delete; -+ } -+ } -+ - #ifdef HAVE_JITDUMP - if (inject.jit_mode) { - inject.tool.mmap2 = perf_event__repipe_mmap2; -@@ -2619,5 +2770,6 @@ int cmd_inject(int argc, const char **argv) - free(inject.itrace_synth_opts.vm_tm_corr_args); - free(inject.event_copy); - free(inject.guest_session.ev.event_buf); -+ free(inject.raw_callchain); - return ret; - } --- -2.53.0 - diff --git a/queue-6.12/perf-inject-with-convert-callchain-ignore-the-dummy-event-for-dwarf-stacks.patch b/queue-6.12/perf-inject-with-convert-callchain-ignore-the-dummy-event-for-dwarf-stacks.patch deleted file mode 100644 index a6216de98d..0000000000 --- a/queue-6.12/perf-inject-with-convert-callchain-ignore-the-dummy-event-for-dwarf-stacks.patch +++ /dev/null @@ -1,47 +0,0 @@ -From e786a04b4a5461dd7e2829422314a5a6d5a664d9 Mon Sep 17 00:00:00 2001 -From: Ian Rogers -Date: Thu, 22 Jan 2026 09:58:46 -0800 -Subject: perf inject: With --convert-callchain ignore the dummy event for dwarf stacks - -From: Ian Rogers - -commit e786a04b4a5461dd7e2829422314a5a6d5a664d9 upstream. - -On hybrid systems there is generally >1 event and a dummy event. - -The perf inject --convert-callchain option is failing to convert -perf.data files on such systems reporting "--convert-callchain requires -DWARF call graph." - -The failing event is the dummy event that doesn't need to be set up for -samples. - -As such ignore this event when checking the evsels. - -Fixes: 92ea788d2af4e65a ("perf inject: Add --convert-callchain option") -Signed-off-by: Ian Rogers -Tested-by: Arnaldo Carvalho de Melo -Cc: Adrian Hunter -Cc: Alexander Shishkin -Cc: Ingo Molnar -Cc: James Clark -Cc: Jiri Olsa -Cc: Namhyung Kim -Cc: Peter Zijlstra -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Greg Kroah-Hartman ---- - tools/perf/builtin-inject.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/tools/perf/builtin-inject.c -+++ b/tools/perf/builtin-inject.c -@@ -2733,7 +2733,7 @@ int cmd_inject(int argc, const char **ar - } - - evlist__for_each_entry(inject.session->evlist, evsel) { -- if (!evsel__has_dwarf_callchain(evsel)) { -+ if (!evsel__has_dwarf_callchain(evsel) && !evsel__is_dummy_event(evsel)) { - pr_err("--convert-callchain requires DWARF call graph.\n"); - goto out_delete; - } diff --git a/queue-6.12/perf-intel-pt-fix-snprintf-size-tracking-bug-in-insn.patch b/queue-6.12/perf-intel-pt-fix-snprintf-size-tracking-bug-in-insn.patch deleted file mode 100644 index 41417e8f1d..0000000000 --- a/queue-6.12/perf-intel-pt-fix-snprintf-size-tracking-bug-in-insn.patch +++ /dev/null @@ -1,67 +0,0 @@ -From 552413aaffba1685e9586d523da9030f028f221b Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sun, 7 Jun 2026 21:06:54 -0300 -Subject: perf intel-pt: Fix snprintf size tracking bug in insn decoder - -From: Arnaldo Carvalho de Melo - -[ Upstream commit b6bb3b005dcdd960b8e0b7f9d6869132b3de08d5 ] - -dump_insn() tracks remaining buffer space with a 'left' variable, -but the loop subtracts the cumulative offset 'n' each iteration -instead of just the per-iteration delta: - - n += snprintf(x->out + n, left, "%02x ", inbuf[i]); - left -= n; /* BUG: n is cumulative, not the delta */ - -After two iterations left goes massively negative, wrapping to a -huge value when passed as size_t to snprintf(), disabling all bounds -checking for the rest of the loop. - -Switch to scnprintf() accumulation using sizeof(x->out) - n as the -remaining space, which is always correct and eliminates the separate -'left' variable entirely. - -Fixes: 48d02a1d5c137d36 ("perf script: Add 'brstackinsn' for branch stacks") -Reported-by: sashiko-bot -Cc: Adrian Hunter -Cc: Andi Kleen -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - .../util/intel-pt-decoder/intel-pt-insn-decoder.c | 11 +++-------- - 1 file changed, 3 insertions(+), 8 deletions(-) - -diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c -index 47cf35799a4dfc..8cd0c31e2b936a 100644 ---- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c -+++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c -@@ -223,7 +223,6 @@ const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused, - { - struct insn insn; - int n, i, ret; -- int left; - - ret = insn_decode(&insn, inbuf, inlen, - x->is64bit ? INSN_MODE_64 : INSN_MODE_32); -@@ -232,13 +231,9 @@ const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused, - return ""; - if (lenp) - *lenp = insn.length; -- left = sizeof(x->out); -- n = snprintf(x->out, left, "insn: "); -- left -= n; -- for (i = 0; i < insn.length; i++) { -- n += snprintf(x->out + n, left, "%02x ", inbuf[i]); -- left -= n; -- } -+ n = scnprintf(x->out, sizeof(x->out), "insn: "); -+ for (i = 0; i < insn.length; i++) -+ n += scnprintf(x->out + n, sizeof(x->out) - n, "%02x ", inbuf[i]); - return x->out; - } - --- -2.53.0 - diff --git a/queue-6.12/perf-libunwind-arm64-fix-missing-close-parens-in-an-if-statement.patch b/queue-6.12/perf-libunwind-arm64-fix-missing-close-parens-in-an-if-statement.patch deleted file mode 100644 index 796608ca8e..0000000000 --- a/queue-6.12/perf-libunwind-arm64-fix-missing-close-parens-in-an-if-statement.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 1293dacbbd43ab9848ac4655f6f2ba1dcc5a96ad Mon Sep 17 00:00:00 2001 -From: Arnaldo Carvalho de Melo -Date: Wed, 9 Apr 2025 10:35:31 -0300 -Subject: perf libunwind arm64: Fix missing close parens in an if statement - -From: Arnaldo Carvalho de Melo - -commit 1293dacbbd43ab9848ac4655f6f2ba1dcc5a96ad upstream. - -While testing building with libunwind (using LIBUNWIND=1) in various -arches I noticed a problem on arm64, on an rpi5 system, a missing close -parens in a change related to dso__data_get_fd() usage, fix it. - -Fixes: 5ac22c35aa8519f1 ("perf dso: Use lock annotations to fix asan deadlock") -Signed-off-by: Arnaldo Carvalho de Melo -Link: https://lore.kernel.org/r/Z_Z3o8KvB2i5c6ab@x1 -Signed-off-by: Namhyung Kim -Signed-off-by: Greg Kroah-Hartman ---- - tools/perf/util/unwind-libunwind-local.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/tools/perf/util/unwind-libunwind-local.c -+++ b/tools/perf/util/unwind-libunwind-local.c -@@ -371,7 +371,7 @@ static int read_unwind_spec_debug_frame( - * has to be pointed by symsrc_filename - */ - if (ofs == 0) { -- if (dso__data_get_fd(dso, machine, &fd) { -+ if (dso__data_get_fd(dso, machine, &fd)) { - ofs = elf_section_offset(fd, ".debug_frame"); - dso__data_put_fd(dso); - } diff --git a/queue-6.12/perf-machine-use-snprintf-for-guestmount-path-constr.patch b/queue-6.12/perf-machine-use-snprintf-for-guestmount-path-constr.patch deleted file mode 100644 index 46f760b2fe..0000000000 --- a/queue-6.12/perf-machine-use-snprintf-for-guestmount-path-constr.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 97c215b8b1ecb9d11b3fdf20742eb525b27da551 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 13 Jun 2026 13:59:39 -0300 -Subject: perf machine: Use snprintf() for guestmount path construction - -From: Arnaldo Carvalho de Melo - -[ Upstream commit fe63d3bca288c5bb983304efd5fc3a5ff3183403 ] - -machines__findnew() and machines__create_guest_kernel_maps() use -sprintf() to build paths by prepending symbol_conf.guestmount. -Both write into PATH_MAX stack buffers, but guestmount comes from -user configuration and is not length-checked. A guestmount path -at or near PATH_MAX causes a stack buffer overflow. - -Switch to snprintf() with sizeof() to prevent overflow. The -subsequent access()/fopen() calls will fail on a truncated path. - -Fixes: a1645ce12adb6c9c ("perf: 'perf kvm' tool for monitoring guest performance from host") -Reported-by: sashiko-bot -Cc: Zhang, Yanmin -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/machine.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c -index 71d3d034ba095e..b1817634996964 100644 ---- a/tools/perf/util/machine.c -+++ b/tools/perf/util/machine.c -@@ -289,7 +289,7 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid) - if ((pid != HOST_KERNEL_ID) && - (pid != DEFAULT_GUEST_KERNEL_ID) && - (symbol_conf.guestmount)) { -- sprintf(path, "%s/%d", symbol_conf.guestmount, pid); -+ snprintf(path, sizeof(path), "%s/%d", symbol_conf.guestmount, pid); - if (access(path, R_OK)) { - static struct strlist *seen; - -@@ -1214,9 +1214,9 @@ int machines__create_guest_kernel_maps(struct machines *machines) - namelist[i]->d_name); - continue; - } -- sprintf(path, "%s/%s/proc/kallsyms", -- symbol_conf.guestmount, -- namelist[i]->d_name); -+ snprintf(path, sizeof(path), "%s/%s/proc/kallsyms", -+ symbol_conf.guestmount, -+ namelist[i]->d_name); - ret = access(path, R_OK); - if (ret) { - pr_debug("Can't access file %s\n", path); --- -2.53.0 - diff --git a/queue-6.12/perf-maps-add-maps__mutate_mapping.patch b/queue-6.12/perf-maps-add-maps__mutate_mapping.patch deleted file mode 100644 index 95c0b5d0cb..0000000000 --- a/queue-6.12/perf-maps-add-maps__mutate_mapping.patch +++ /dev/null @@ -1,416 +0,0 @@ -From 2468f12df21a946eec5d1654a289cda9cb90e6b9 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Thu, 11 Jun 2026 09:41:18 -0700 -Subject: perf maps: Add maps__mutate_mapping - -From: Ian Rogers - -[ Upstream commit 75a4888b7029a1f98613aef91f517b2ee1f03d43 ] - -During kernel ELF symbol parsing (dso__process_kernel_symbol), proc -kallsyms image loading (dso__load_kernel_sym, -dso__load_guest_kernel_sym), and dynamic kernel memory map alignment -updates (machine__update_kernel_mmap), the loader directly modifies live -virtual address boundary keys fields on map objects. - -If these boundaries are mutated while the map pointer actively resides -inside the parent maps cache array list (kmaps) outside of any lock -closure, an unsafe concurrent window is exposed where parallel worker -lookup threads (e.g., inside perf top) can mistakenly assume the cache -remains sorted based on stale parameters, executing binary search -queries (bsearch) across an unsorted range and triggering lookup -failures. - -Fix this by introducing maps__mutate_mapping() that explicitly acquires -the parent maps write semaphore lock, executes an incoming mutation -callback block to perform the field updates under lock protection, and -invalidates the sorted tracking flags prior to releasing the write lock. - -This guarantees synchronization invariants, closing the concurrent -lookup race window. The adjacent module alignment pass inside -machine__create_kernel_maps() is safely preserved as a high-performance -lockless pass, as its invocation lifecycle bounds remain strictly -single-threaded by contract during session initialization construction. - -To safely support this unconditional down_write write lock mutator -without recursive read-to-write self-deadlock upgrades during lazy -symbol loading, we introduce a public maps__load_maps() API. - -It copies map pointers under a brief read lock and force-loads all -modules locklessly outside the lock. Callers (such as perf inject) must -pre-load all kernel symbol maps up front at startup using -maps__load_maps(), completely bypassing dynamic runtime mutations. - -Fixes: 39b12f781271 ("perf tools: Make it possible to read object code from vmlinux") - -Assisted-by: Antigravity:gemini-3.1-pro -Signed-off-by: Ian Rogers -Tested-by: James Clark -Cc: Adrian Hunter -Cc: Gabriel Marin -Cc: Ingo Molnar -Cc: Jiri Olsa -Cc: Namhyung Kim -Cc: Peter Zijlstra -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/machine.c | 32 +++++--- - tools/perf/util/maps.c | 148 ++++++++++++++++++++++++++++------- - tools/perf/util/maps.h | 3 + - tools/perf/util/symbol-elf.c | 41 ++++++---- - tools/perf/util/symbol.c | 17 +++- - 5 files changed, 183 insertions(+), 58 deletions(-) - -diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c -index 06a168ce5d9044..557853fcb5479b 100644 ---- a/tools/perf/util/machine.c -+++ b/tools/perf/util/machine.c -@@ -1489,22 +1489,30 @@ static void machine__set_kernel_mmap(struct machine *machine, - map__set_end(machine->vmlinux_map, ~0ULL); - } - --static int machine__update_kernel_mmap(struct machine *machine, -- u64 start, u64 end) -+struct kernel_mmap_mutation_ctx { -+ u64 start; -+ u64 end; -+}; -+ -+static int kernel_mmap_mutate_cb(struct map *map, void *data) - { -- struct map *orig, *updated; -- int err; -+ struct kernel_mmap_mutation_ctx *ctx = data; - -- orig = machine->vmlinux_map; -- updated = map__get(orig); -+ map__set_start(map, ctx->start); -+ map__set_end(map, ctx->end); -+ if (ctx->start == 0 && ctx->end == 0) -+ map__set_end(map, ~0ULL); -+ return 0; -+} - -- machine->vmlinux_map = updated; -- maps__remove(machine__kernel_maps(machine), orig); -- machine__set_kernel_mmap(machine, start, end); -- err = maps__insert(machine__kernel_maps(machine), updated); -- map__put(orig); -+static int machine__update_kernel_mmap(struct machine *machine, -+ u64 start, u64 end) -+{ -+ struct kernel_mmap_mutation_ctx ctx = { .start = start, .end = end }; - -- return err; -+ return maps__mutate_mapping(machine__kernel_maps(machine), -+ machine->vmlinux_map, -+ kernel_mmap_mutate_cb, &ctx); - } - - int machine__create_kernel_maps(struct machine *machine) -diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c -index 7b8f48677c3185..51726d8de2cfd1 100644 ---- a/tools/perf/util/maps.c -+++ b/tools/perf/util/maps.c -@@ -539,6 +539,48 @@ void maps__remove(struct maps *maps, struct map *map) - up_write(maps__lock(maps)); - } - -+/** -+ * maps__mutate_mapping - Apply write-protected mutations to a map. -+ * @maps: The maps collection containing the map. -+ * @map: The map to mutate. -+ * @mutate_cb: Callback function that performs the actual mutations. -+ * @data: Private data passed to the callback. -+ * -+ * This acquires the write lock on the maps semaphore to safely protect -+ * concurrent readers from seeing partially mutated or unsorted map boundaries. -+ * -+ * WARNING: Acquiring down_write() here can trigger a recursive self-deadlock if -+ * the caller already holds the read lock (e.g., during maps__for_each_map() or -+ * maps__find() iteration paths that trigger lazy symbol loading). To completely -+ * avoid this deadlock, all kernel/module maps must be pre-loaded up-front (via -+ * maps__load_maps()) under a clean, single-threaded context before entering -+ * multi-threaded event processing loops. -+ */ -+int maps__mutate_mapping(struct maps *maps, struct map *map, -+ int (*mutate_cb)(struct map *map, void *data), void *data) -+{ -+ int err = 0; -+ -+ if (maps) { -+ down_write(maps__lock(maps)); -+ -+ err = mutate_cb(map, data); -+ -+ RC_CHK_ACCESS(maps)->maps_by_address_sorted = false; -+ RC_CHK_ACCESS(maps)->maps_by_name_sorted = false; -+ -+ up_write(maps__lock(maps)); -+ -+#ifdef HAVE_LIBDW_SUPPORT -+ libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps)); -+#endif -+ } else { -+ err = mutate_cb(map, data); -+ } -+ -+ return err; -+} -+ - bool maps__empty(struct maps *maps) - { - bool res; -@@ -589,6 +631,41 @@ int maps__for_each_map(struct maps *maps, int (*cb)(struct map *map, void *data) - return ret; - } - -+int maps__load_maps(struct maps *maps) -+{ -+ struct map **maps_copy; -+ unsigned int nr_maps; -+ int err = 0; -+ -+ if (!maps) -+ return 0; -+ -+ down_read(maps__lock(maps)); -+ nr_maps = maps__nr_maps(maps); -+ if (nr_maps == 0) { -+ up_read(maps__lock(maps)); -+ return 0; -+ } -+ maps_copy = calloc(nr_maps, sizeof(*maps_copy)); -+ if (!maps_copy) { -+ up_read(maps__lock(maps)); -+ return -ENOMEM; -+ } -+ for (unsigned int i = 0; i < nr_maps; i++) -+ maps_copy[i] = map__get(maps__maps_by_address(maps)[i]); -+ up_read(maps__lock(maps)); -+ -+ for (unsigned int i = 0; i < nr_maps; i++) { -+ if (map__load(maps_copy[i]) < 0) { -+ pr_warning("Failed to load map %s\n", dso__name(map__dso(maps_copy[i]))); -+ err = -1; -+ } -+ map__put(maps_copy[i]); -+ } -+ free(maps_copy); -+ return err; -+} -+ - void maps__remove_maps(struct maps *maps, bool (*cb)(struct map *map, void *data), void *data) - { - struct map **maps_by_address; -@@ -623,40 +700,57 @@ struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp) - return result; - } - --struct maps__find_symbol_by_name_args { -- struct map **mapp; -- const char *name; -- struct symbol *sym; --}; -- --static int maps__find_symbol_by_name_cb(struct map *map, void *data) -+struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp) - { -- struct maps__find_symbol_by_name_args *args = data; -+ struct map **maps_copy; -+ unsigned int nr_maps; -+ struct symbol *sym = NULL; - -- args->sym = map__find_symbol_by_name(map, args->name); -- if (!args->sym) -- return 0; -+ if (!maps) -+ return NULL; - -- if (!map__contains_symbol(map, args->sym)) { -- args->sym = NULL; -- return 0; -+ /* -+ * First, ensure all maps are loaded. We pre-load them outside of any -+ * read-to-write locks to avoid deadlocks. Even if some fail, we proceed. -+ */ -+ maps__load_maps(maps); -+ -+ /* -+ * Create a local snapshot of the maps while holding the read lock. -+ * This prevents deadlocking if iteration triggers further map insertions. -+ */ -+ down_read(maps__lock(maps)); -+ nr_maps = maps__nr_maps(maps); -+ maps_copy = calloc(nr_maps, sizeof(*maps_copy)); -+ if (maps_copy) { -+ for (unsigned int i = 0; i < nr_maps; i++) { -+ struct map *map = maps__maps_by_address(maps)[i]; -+ -+ maps_copy[i] = map__get(map); -+ } - } -+ up_read(maps__lock(maps)); - -- if (args->mapp != NULL) -- *args->mapp = map__get(map); -- return 1; --} -+ if (!maps_copy) -+ return NULL; - --struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp) --{ -- struct maps__find_symbol_by_name_args args = { -- .mapp = mapp, -- .name = name, -- .sym = NULL, -- }; -+ for (unsigned int i = 0; i < nr_maps; i++) { -+ struct map *map = maps_copy[i]; -+ -+ sym = map__find_symbol_by_name(map, name); -+ if (sym && map__contains_symbol(map, sym)) { -+ if (mapp) -+ *mapp = map__get(map); -+ break; -+ } -+ sym = NULL; -+ } -+ -+ for (unsigned int i = 0; i < nr_maps; i++) -+ map__put(maps_copy[i]); - -- maps__for_each_map(maps, maps__find_symbol_by_name_cb, &args); -- return args.sym; -+ free(maps_copy); -+ return sym; - } - - int maps__find_ams(struct maps *maps, struct addr_map_symbol *ams) -diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h -index d9aa62ed968ac4..df22f8c1b32bd1 100644 ---- a/tools/perf/util/maps.h -+++ b/tools/perf/util/maps.h -@@ -55,8 +55,11 @@ void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libun - - size_t maps__fprintf(struct maps *maps, FILE *fp); - -+int maps__load_maps(struct maps *maps); - int maps__insert(struct maps *maps, struct map *map); - void maps__remove(struct maps *maps, struct map *map); -+int maps__mutate_mapping(struct maps *maps, struct map *map, -+ int (*mutate_cb)(struct map *map, void *data), void *data); - - struct map *maps__find(struct maps *maps, u64 addr); - struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp); -diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c -index 90f11a6eb19fea..4ba49975970a9b 100644 ---- a/tools/perf/util/symbol-elf.c -+++ b/tools/perf/util/symbol-elf.c -@@ -1418,6 +1418,24 @@ static u64 ref_reloc(struct kmap *kmap) - void __weak arch__sym_update(struct symbol *s __maybe_unused, - GElf_Sym *sym __maybe_unused) { } - -+struct remap_kernel_ctx { -+ u64 sh_addr; -+ u64 sh_size; -+ u64 sh_offset; -+ struct kmap *kmap; -+}; -+ -+static int remap_kernel_cb(struct map *map, void *data) -+{ -+ struct remap_kernel_ctx *ctx = data; -+ -+ map__set_start(map, ctx->sh_addr + ref_reloc(ctx->kmap)); -+ map__set_end(map, map__start(map) + ctx->sh_size); -+ map__set_pgoff(map, ctx->sh_offset); -+ map__set_mapping_type(map, MAPPING_TYPE__DSO); -+ return 0; -+} -+ - static int dso__process_kernel_symbol(struct dso *dso, struct map *map, - GElf_Sym *sym, GElf_Shdr *shdr, - struct maps *kmaps, struct kmap *kmap, -@@ -1448,22 +1466,15 @@ static int dso__process_kernel_symbol(struct dso *dso, struct map *map, - * map to the kernel dso. - */ - if (*remap_kernel && dso__kernel(dso) && !kmodule) { -+ struct remap_kernel_ctx ctx = { -+ .sh_addr = shdr->sh_addr, -+ .sh_size = shdr->sh_size, -+ .sh_offset = shdr->sh_offset, -+ .kmap = kmap -+ }; -+ - *remap_kernel = false; -- map__set_start(map, shdr->sh_addr + ref_reloc(kmap)); -- map__set_end(map, map__start(map) + shdr->sh_size); -- map__set_pgoff(map, shdr->sh_offset); -- map__set_mapping_type(map, MAPPING_TYPE__DSO); -- /* Ensure maps are correctly ordered */ -- if (kmaps) { -- int err; -- struct map *tmp = map__get(map); -- -- maps__remove(kmaps, map); -- err = maps__insert(kmaps, map); -- map__put(tmp); -- if (err) -- return err; -- } -+ maps__mutate_mapping(kmaps, map, remap_kernel_cb, &ctx); - } - - /* -diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c -index b1b4a2d4b7e46c..e792dd22b54983 100644 ---- a/tools/perf/util/symbol.c -+++ b/tools/perf/util/symbol.c -@@ -42,6 +42,13 @@ - #include - #include - -+static int map_fixup_cb(struct map *map, void *data __maybe_unused) -+{ -+ map__fixup_start(map); -+ map__fixup_end(map); -+ return 0; -+} -+ - static int dso__load_kernel_sym(struct dso *dso, struct map *map); - static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map); - static bool symbol__is_idle(const char *name); -@@ -2227,10 +2234,11 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map) - free(kallsyms_allocated_filename); - - if (err > 0 && !dso__is_kcore(dso)) { -+ struct maps *kmaps = map__kmaps(map); -+ - dso__set_binary_type(dso, DSO_BINARY_TYPE__KALLSYMS); - dso__set_long_name(dso, DSO__NAME_KALLSYMS, false); -- map__fixup_start(map); -- map__fixup_end(map); -+ maps__mutate_mapping(kmaps, map, map_fixup_cb, NULL); - } - - return err; -@@ -2270,10 +2278,11 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map) - if (err > 0) - pr_debug("Using %s for symbols\n", kallsyms_filename); - if (err > 0 && !dso__is_kcore(dso)) { -+ struct maps *kmaps = map__kmaps(map); -+ - dso__set_binary_type(dso, DSO_BINARY_TYPE__GUEST_KALLSYMS); - dso__set_long_name(dso, machine->mmap_name, false); -- map__fixup_start(map); -- map__fixup_end(map); -+ maps__mutate_mapping(kmaps, map, map_fixup_cb, NULL); - } - - return err; --- -2.53.0 - diff --git a/queue-6.12/perf-mmap-fix-null-deref-in-aio-cleanup-on-alloc-fai.patch b/queue-6.12/perf-mmap-fix-null-deref-in-aio-cleanup-on-alloc-fai.patch deleted file mode 100644 index a2b626a8d2..0000000000 --- a/queue-6.12/perf-mmap-fix-null-deref-in-aio-cleanup-on-alloc-fai.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 2ca4d08f1f172fbd844597d300b5af0cf362dbbd Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 6 Jun 2026 11:03:29 -0300 -Subject: perf mmap: Fix NULL deref in aio cleanup on alloc failure - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 25627346b10e6a564610ea2c49dc6dd54812226d ] - -perf_mmap__aio_mmap() sets map->aio.nr_cblocks before allocating the -data array. If calloc() for aiocb or cblocks fails before the data -array is allocated, the return -1 path leads to perf_mmap__aio_munmap() -which loops nr_cblocks times calling perf_mmap__aio_free(). Both -versions of perf_mmap__aio_free() (NUMA and non-NUMA) dereference -map->aio.data[idx] without checking if data is NULL, causing a NULL -pointer dereference. - -Add NULL checks for map->aio.data at the top of both -perf_mmap__aio_free() variants so the cleanup path is safe when -allocation fails partway through perf_mmap__aio_mmap(). - -Fixes: d3d1af6f011a553a ("perf record: Enable asynchronous trace writing") -Reported-by: sashiko-bot -Cc: Alexey Budankov -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/mmap.c | 10 ++++++---- - 1 file changed, 6 insertions(+), 4 deletions(-) - -diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c -index 5d2685d9ab4ea6..d011968c031c03 100644 ---- a/tools/perf/util/mmap.c -+++ b/tools/perf/util/mmap.c -@@ -88,10 +88,10 @@ static int perf_mmap__aio_alloc(struct mmap *map, int idx) - - static void perf_mmap__aio_free(struct mmap *map, int idx) - { -- if (map->aio.data[idx]) { -- munmap(map->aio.data[idx], mmap__mmap_len(map)); -- map->aio.data[idx] = NULL; -- } -+ if (!map->aio.data || !map->aio.data[idx]) -+ return; -+ munmap(map->aio.data[idx], mmap__mmap_len(map)); -+ map->aio.data[idx] = NULL; - } - - static int perf_mmap__aio_bind(struct mmap *map, int idx, struct perf_cpu cpu, int affinity) -@@ -140,6 +140,8 @@ static int perf_mmap__aio_alloc(struct mmap *map, int idx) - - static void perf_mmap__aio_free(struct mmap *map, int idx) - { -+ if (!map->aio.data) -+ return; - zfree(&(map->aio.data[idx])); - } - --- -2.53.0 - diff --git a/queue-6.12/perf-mmap-guard-cpu__get_node-return-in-aio_bind.patch b/queue-6.12/perf-mmap-guard-cpu__get_node-return-in-aio_bind.patch deleted file mode 100644 index 5a3d0cc80d..0000000000 --- a/queue-6.12/perf-mmap-guard-cpu__get_node-return-in-aio_bind.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 1b91e0c3d517cbedcefdb361c7112c078b33d84a Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Fri, 5 Jun 2026 10:56:33 -0300 -Subject: perf mmap: Guard cpu__get_node() return in aio_bind() - -From: Arnaldo Carvalho de Melo - -[ Upstream commit f32dc302a090f48893477ef297a888db109ec0bd ] - -perf_mmap__aio_bind() passes the cpu__get_node() return value directly -to an unsigned long variable (node_index). When cpu__get_node() returns --1 for an unknown CPU, the implicit int-to-unsigned-long conversion -sign-extends it to ULONG_MAX. - -This causes bitmap_zalloc(ULONG_MAX + 1) which wraps to -bitmap_zalloc(0), returning a zero-sized allocation. The subsequent -__set_bit(ULONG_MAX, node_mask) then writes massively out of bounds. - -Check the return value in a signed temporary before assigning to -node_index, and skip the NUMA binding when the node is unknown. - -Fixes: c44a8b44ca9f ("perf record: Bind the AIO user space buffers to nodes") -Reported-by: sashiko-bot -Cc: Alexey Budankov -Cc: Jiri Olsa -Cc: Namhyung Kim -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/mmap.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c -index 43b02293f1d2da..5d2685d9ab4ea6 100644 ---- a/tools/perf/util/mmap.c -+++ b/tools/perf/util/mmap.c -@@ -103,9 +103,15 @@ static int perf_mmap__aio_bind(struct mmap *map, int idx, struct perf_cpu cpu, i - int err = 0; - - if (affinity != PERF_AFFINITY_SYS && cpu__max_node() > 1) { -+ int node; -+ - data = map->aio.data[idx]; - mmap_len = mmap__mmap_len(map); -- node_index = cpu__get_node(cpu); -+ node = cpu__get_node(cpu); -+ /* -1 sign-extends to ULONG_MAX, wrapping bitmap_zalloc(0) and OOB __set_bit */ -+ if (node < 0) -+ return 0; -+ node_index = node; - node_mask = bitmap_zalloc(node_index + 1); - if (!node_mask) { - pr_err("Failed to allocate node mask for mbind: error %m\n"); --- -2.53.0 - diff --git a/queue-6.12/perf-pmu-fix-perf_pmu__parse_scale-unit-oob-access-o.patch b/queue-6.12/perf-pmu-fix-perf_pmu__parse_scale-unit-oob-access-o.patch deleted file mode 100644 index 1f917e9d2f..0000000000 --- a/queue-6.12/perf-pmu-fix-perf_pmu__parse_scale-unit-oob-access-o.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 2be632851d4fccb6813c2dd43629a5af9a01aaa4 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sun, 7 Jun 2026 21:03:13 -0300 -Subject: perf pmu: Fix perf_pmu__parse_scale/unit() OOB access on empty sysfs - file -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 33035f7dd4e49f3f117e70c5e36c8c1ae88d37f2 ] - -perf_pmu__parse_scale() reads a PMU scale file then accesses -scale[sret - 1] to strip a trailing newline. Only sret < 0 is -guarded, so an empty file (sret == 0) causes scale[-1] — a stack -buffer underflow that reads and potentially writes out of bounds. - -perf_pmu__parse_unit() has the same pattern: alias->unit[sret - 1] -with sret == 0 accesses the byte before the struct member, which -may corrupt the adjacent pmu_name pointer field. - -Change both guards from sret < 0 to sret <= 0 so that empty files -are treated as read errors. - -Fixes: 410136f5dd96b601 ("tools/perf/stat: Add event unit and scale support") -Reported-by: sashiko-bot -Cc: Stephane Eranian -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/pmu.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c -index bf9793a995316b..3a3a56f06880ad 100644 ---- a/tools/perf/util/pmu.c -+++ b/tools/perf/util/pmu.c -@@ -314,7 +314,7 @@ static int perf_pmu__parse_scale(struct perf_pmu *pmu, struct perf_pmu_alias *al - goto error; - - sret = read(fd, scale, sizeof(scale)-1); -- if (sret < 0) -+ if (sret <= 0) - goto error; - - if (scale[sret - 1] == '\n') -@@ -346,7 +346,7 @@ static int perf_pmu__parse_unit(struct perf_pmu *pmu, struct perf_pmu_alias *ali - return -1; - - sret = read(fd, alias->unit, UNIT_MAX_LEN); -- if (sret < 0) -+ if (sret <= 0) - goto error; - - close(fd); --- -2.53.0 - diff --git a/queue-6.12/perf-pmu-fix-pmu_id-heap-underwrite-on-empty-identif.patch b/queue-6.12/perf-pmu-fix-pmu_id-heap-underwrite-on-empty-identif.patch deleted file mode 100644 index 671caf2831..0000000000 --- a/queue-6.12/perf-pmu-fix-pmu_id-heap-underwrite-on-empty-identif.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 68de90895a69326c0013eb8251e2f480a39c6520 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sun, 7 Jun 2026 21:01:43 -0300 -Subject: perf pmu: Fix pmu_id() heap underwrite on empty identifier file - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 836455e6dbd34eb3d12eeab5e2d2b9a7f1512459 ] - -pmu_id() calls filename__read_str() then strips the trailing newline -via str[len - 1] = 0. If the PMU identifier file is empty, -filename__read_str() succeeds with len = 0. len - 1 underflows -size_t to SIZE_MAX, writing a null byte before the heap allocation. - -Add a len == 0 check before the newline stripping. - -Fixes: 51d548471510843e ("perf pmu: Add pmu_id()") -Reported-by: sashiko-bot -Cc: John Garry -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/pmu.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c -index 8885998c195308..bf9793a995316b 100644 ---- a/tools/perf/util/pmu.c -+++ b/tools/perf/util/pmu.c -@@ -796,6 +796,12 @@ static char *pmu_id(const char *name) - if (filename__read_str(path, &str, &len) < 0) - return NULL; - -+ /* empty identifier file — nothing useful */ -+ if (len == 0) { -+ free(str); -+ return NULL; -+ } -+ - str[len - 1] = 0; /* remove line feed */ - - return str; --- -2.53.0 - diff --git a/queue-6.12/perf-pmu-skip-test-on-arm64-when-slots-is-zero.patch b/queue-6.12/perf-pmu-skip-test-on-arm64-when-slots-is-zero.patch deleted file mode 100644 index bc07c9c9df..0000000000 --- a/queue-6.12/perf-pmu-skip-test-on-arm64-when-slots-is-zero.patch +++ /dev/null @@ -1,104 +0,0 @@ -From 2e0a291e0c556ad0b6df41ed1eacf4bbc33ea93e Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Fri, 10 Apr 2026 12:13:44 +0100 -Subject: perf pmu: Skip test on Arm64 when #slots is zero - -From: Leo Yan - -[ Upstream commit 2e2ba7d1ea554ee6e9e751a53eebf3e9270b0670 ] - -Some Arm64 PMUs expose 'caps/slots' as 0 when the slot count is not -implemented, tool_pmu__read_event() currently returns false for this, -so metrics that reference #slots are reported as syntax error. - -Since the commit 3a61fd866ef9 ("perf expr: Return -EINVAL for syntax -error in expr__find_ids()"), these syntax errors are populated as -failures and make the PMU metric test fail: - - 9.3: Parsing of PMU event table metrics: - --- start --- - ... - - Found metric 'backend_bound' - metric expr 100 * (stall_slot_backend / (#slots * cpu_cycles)) for backend_bound - parsing metric: 100 * (stall_slot_backend / (#slots * cpu_cycles)) - Failure to read '#slots' - literal: #slots = nan - syntax error - Fail to parse metric or group `backend_bound' - - ... - ---- end(-1) ---- - 9.3: Parsing of PMU event table metrics : FAILED! - -This commit introduces a new function is_expected_broken_metric() to -identify broken metrics, and treats metrics containing "#slots" as -expected broken when #slots == 0 on Arm64 platforms. - -Fixes: 3a61fd866ef9aaa1 ("perf expr: Return -EINVAL for syntax error in expr__find_ids()") -Reviewed-by: Ian Rogers -Reviewed-by: James Clark -Signed-off-by: Leo Yan -Cc: Adrian Hunter -Cc: Alexander Shishkin -Cc: Jiri Olsa -Cc: Mark Rutland -Cc: Namhyung Kim -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/tests/pmu-events.c | 24 ++++++++++++++++++++++-- - 1 file changed, 22 insertions(+), 2 deletions(-) - -diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c -index db004d26fcb005..8c73ab189c5fce 100644 ---- a/tools/perf/tests/pmu-events.c -+++ b/tools/perf/tests/pmu-events.c -@@ -15,6 +15,7 @@ - #include "util/expr.h" - #include "util/hashmap.h" - #include "util/parse-events.h" -+#include "util/tool_pmu.h" - #include "metricgroup.h" - #include "stat.h" - -@@ -864,6 +865,26 @@ struct metric { - struct metric_ref metric_ref; - }; - -+static bool is_expected_broken_metric(const struct pmu_metric *pm) -+{ -+ if (!strcmp(pm->metric_name, "M1") || !strcmp(pm->metric_name, "M2") || -+ !strcmp(pm->metric_name, "M3")) -+ return true; -+ -+#if defined(__aarch64__) -+ /* -+ * Arm64 platforms may return "#slots == 0", which is treated as a -+ * syntax error by the parser. Don't test these metrics when running -+ * on such platforms. -+ */ -+ if (strstr(pm->metric_expr, "#slots") && -+ !tool_pmu__cpu_slots_per_cycle()) -+ return true; -+#endif -+ -+ return false; -+} -+ - static int test__parsing_callback(const struct pmu_metric *pm, - const struct pmu_metrics_table *table, - void *data) -@@ -902,8 +923,7 @@ static int test__parsing_callback(const struct pmu_metric *pm, - - err = metricgroup__parse_groups_test(evlist, table, pm->metric_name, &metric_events); - if (err) { -- if (!strcmp(pm->metric_name, "M1") || !strcmp(pm->metric_name, "M2") || -- !strcmp(pm->metric_name, "M3")) { -+ if (is_expected_broken_metric(pm)) { - (*failures)--; - pr_debug("Expected broken metric %s skipping\n", pm->metric_name); - err = 0; --- -2.53.0 - diff --git a/queue-6.12/perf-remove-redundant-kernel.h-include.patch b/queue-6.12/perf-remove-redundant-kernel.h-include.patch deleted file mode 100644 index 50230664b8..0000000000 --- a/queue-6.12/perf-remove-redundant-kernel.h-include.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 942efef4b7b7d031c09e6719131040dfda8d3aac Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Fri, 23 Jan 2026 13:32:04 +0000 -Subject: perf: Remove redundant kernel.h include - -From: Leo Yan - -[ Upstream commit 7a0ba3891104da77cfd1a16d41699e0fdf45603a ] - -Now that the bitfield dependency is resolved, the explicit inclusion of -kernel.h is no longer needed. - -Remove the redundant include. - -Signed-off-by: Leo Yan -Cc: Ian Rogers -Cc: James Clark -Cc: Namhyung Kim -Signed-off-by: Arnaldo Carvalho de Melo -Stable-dep-of: 542e88a4c6f7 ("perf cs-etm: Reject CPU IDs that would overflow signed comparison") -Signed-off-by: Sasha Levin ---- - tools/perf/arch/arm64/util/header.c | 1 - - tools/perf/util/cs-etm.c | 1 - - 2 files changed, 2 deletions(-) - -diff --git a/tools/perf/arch/arm64/util/header.c b/tools/perf/arch/arm64/util/header.c -index 741df3614a09ac..d0ccc727593ca2 100644 ---- a/tools/perf/arch/arm64/util/header.c -+++ b/tools/perf/arch/arm64/util/header.c -@@ -1,4 +1,3 @@ --#include - #include - #include - #include -diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c -index 3fd669817eb9bf..776bef4e00156e 100644 ---- a/tools/perf/util/cs-etm.c -+++ b/tools/perf/util/cs-etm.c -@@ -6,7 +6,6 @@ - * Author: Mathieu Poirier - */ - --#include - #include - #include - #include --- -2.53.0 - diff --git a/queue-6.12/perf-sched-add-missing-mmap2-handler-in-timehist.patch b/queue-6.12/perf-sched-add-missing-mmap2-handler-in-timehist.patch deleted file mode 100644 index fc0583d43f..0000000000 --- a/queue-6.12/perf-sched-add-missing-mmap2-handler-in-timehist.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 198e08ffc86f5ecfb3818a30c56a395ebafc799b Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Tue, 5 May 2026 17:45:42 -0700 -Subject: perf sched: Add missing mmap2 handler in timehist - -From: Ian Rogers - -[ Upstream commit 91182741369b261c441e63e6678893032a6d7e4c ] - -perf_sched__timehist() registers event handlers for options using the -sched->tool struct. It registers handlers for MMAP, COMM, EXIT, FORK, etc. -but completely omits registering a handler for MMAP2 events. - -Failing to register both MMAP and MMAP2 handlers causes modern systems -(which primarily output MMAP2 records) to silently drop VMA map mappings. -This results in uninitialized machine/thread mapping structures, making it -impossible to resolve shared library instruction pointers (IPs) to dynamic -symbols/DSOs during timehist callchain analysis. - -Fix this by correctly registering perf_event__process_mmap2 in -sched->tool inside perf_sched__timehist(). - -Fixes: 49394a2a24c78ce0 ("perf sched timehist: Introduce timehist command") -Assisted-by: Gemini-CLI:Google Gemini 3 -Signed-off-by: Ian Rogers -Cc: Adrian Hunter -Cc: David Ahern -Cc: Gabriel Marin -Cc: Ingo Molnar -Cc: James Clark -Cc: Jiri Olsa -Cc: Namhyung Kim -Cc: Peter Zijlstra -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-sched.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c -index 64bf3ac237f2ef..fa5f00d47e6277 100644 ---- a/tools/perf/builtin-sched.c -+++ b/tools/perf/builtin-sched.c -@@ -3261,6 +3261,7 @@ static int perf_sched__timehist(struct perf_sched *sched) - */ - sched->tool.sample = perf_timehist__process_sample; - sched->tool.mmap = perf_event__process_mmap; -+ sched->tool.mmap2 = perf_event__process_mmap2; - sched->tool.comm = perf_event__process_comm; - sched->tool.exit = perf_event__process_exit; - sched->tool.fork = perf_event__process_fork; --- -2.53.0 - diff --git a/queue-6.12/perf-sched-bounds-check-prio-before-test_bit-in-time.patch b/queue-6.12/perf-sched-bounds-check-prio-before-test_bit-in-time.patch deleted file mode 100644 index 5dab2b5116..0000000000 --- a/queue-6.12/perf-sched-bounds-check-prio-before-test_bit-in-time.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 6007424409b0a570f8132a408e0f317dd33c8830 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 6 Jun 2026 21:48:13 -0300 -Subject: perf sched: Bounds-check prio before test_bit() in timehist -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 4477dc01fcfc7f404772a67e0c1e056541ceb61d ] - -timehist_skip_sample() reads prio from untrusted tracepoint data via -perf_sample__intval(sample, "prev_prio") without bounds validation. -A crafted perf.data with prev_prio >= MAX_PRIO (140) causes test_bit() -to read past the end of the prio_bitmap, which is only MAX_PRIO bits. - -Add a prio >= 0 guard before the test_bit() call and skip out-of-range -values (>= MAX_PRIO) that can never match the user's filter set. - -The original prio != -1 already let all negatives other than -1 through -(after an undefined-behavior bitmap read); the new prio >= 0 guard -preserves that pass-through behavior — negative means "no priority -info", so the event is shown unfiltered — while fixing the OOB. -Values >= MAX_PRIO are skipped because they cannot be represented in -the filter bitmap. - -Fixes: 9b3a48bbe20d9692 ("perf sched timehist: Add --prio option") -Reported-by: sashiko-bot -Reviewed-by: Ian Rogers -Cc: Yang Jihong -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-sched.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c -index d599ad421df108..7cf56e271a993a 100644 ---- a/tools/perf/builtin-sched.c -+++ b/tools/perf/builtin-sched.c -@@ -2646,7 +2646,9 @@ static bool timehist_skip_sample(struct perf_sched *sched, - else if (evsel__name_is(evsel, "sched:sched_switch")) - prio = evsel__intval(evsel, sample, "prev_prio"); - -- if (prio != -1 && !test_bit(prio, sched->prio_bitmap)) { -+ /* negative prio means no info; out-of-range prio can't match the filter */ -+ if (prio >= 0 && -+ (prio >= MAX_PRIO || !test_bit(prio, sched->prio_bitmap))) { - rc = true; - sched->skipped_samples++; - } --- -2.53.0 - diff --git a/queue-6.12/perf-sched-clean-up-idle_threads-entry-on-init-failu.patch b/queue-6.12/perf-sched-clean-up-idle_threads-entry-on-init-failu.patch deleted file mode 100644 index e9eab6af73..0000000000 --- a/queue-6.12/perf-sched-clean-up-idle_threads-entry-on-init-failu.patch +++ /dev/null @@ -1,54 +0,0 @@ -From f68c59a0bd704d8491bc39474d5f73d19e496808 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Fri, 5 Jun 2026 11:12:08 -0300 -Subject: perf sched: Clean up idle_threads entry on init failure - -From: Arnaldo Carvalho de Melo - -[ Upstream commit cda5a94ad9181cd60cbf04be11d524201bf489a2 ] - -get_idle_thread() allocates a thread via thread__new() and stores it in -idle_threads[cpu], then calls init_idle_thread() to set up the private -data. If init_idle_thread() fails (e.g. OOM for the idle_thread_runtime -struct), the function returns NULL but leaves the partially initialized -thread in idle_threads[cpu]. - -On subsequent calls for the same CPU, get_idle_thread() finds a non-NULL -idle_threads[cpu], skips allocation, and returns thread__get() on a -thread that has no priv data. Callers then get a thread whose -thread__priv() returns NULL, leading to unexpected behavior. - -Release the thread and reset the slot to NULL on init failure so the -entry doesn't persist in a corrupted state. - -Fixes: 49394a2a24c7 ("perf sched timehist: Introduce timehist command") -Reported-by: sashiko-bot -Cc: David Ahern -Cc: Namhyung Kim -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-sched.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c -index 4596037b5763c0..fd93ab11bd37ed 100644 ---- a/tools/perf/builtin-sched.c -+++ b/tools/perf/builtin-sched.c -@@ -2496,8 +2496,11 @@ static struct thread *get_idle_thread(int cpu) - idle_threads[cpu] = thread__new(0, 0); - - if (idle_threads[cpu]) { -- if (init_idle_thread(idle_threads[cpu]) < 0) -+ if (init_idle_thread(idle_threads[cpu]) < 0) { -+ /* clean up so next call doesn't find a half-initialized thread */ -+ thread__zput(idle_threads[cpu]); - return NULL; -+ } - } - } - --- -2.53.0 - diff --git a/queue-6.12/perf-sched-fix-idle-hist-callchain-display-using-wro.patch b/queue-6.12/perf-sched-fix-idle-hist-callchain-display-using-wro.patch deleted file mode 100644 index 8a83f8a870..0000000000 --- a/queue-6.12/perf-sched-fix-idle-hist-callchain-display-using-wro.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 8ca288d57427734595a82345590be1fa3a9ad6c1 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 6 Jun 2026 21:49:16 -0300 -Subject: perf sched: Fix idle-hist callchain display using wrong rb_first - variant -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -From: Arnaldo Carvalho de Melo - -[ Upstream commit d9b99dc8148e0c1f5da3942131b47e0d21187a32 ] - -timehist_print_idlehist_callchain() calls rb_first_cached() on -sorted_root, but the sort function (callchain_param.sort) populates it -via rb_insert_color() on the plain rb_root member — not the cached -variant. This means rb_leftmost is never set, so rb_first_cached() -always returns NULL and the entire callchain summary is silently -dropped from --idle-hist output. - -The original code in ba957ebb54893aca ("perf sched timehist: Show -callchains for idle stat") was correct — it used struct rb_root and -rb_first(). The bug was introduced when sorted_root was converted to -rb_root_cached without converting the sort insertion path to use -rb_insert_color_cached(). - -Use rb_first(&root->rb_root) to match how the tree was populated. - -Fixes: cb4c13a5137766c3 ("perf sched: Use cached rbtrees") -Reported-by: sashiko-bot -Cc: Davidlohr Bueso -Cc: Namhyung Kim -Acked-by: Ian Rogers -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-sched.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c -index 7cf56e271a993a..0741d926590468 100644 ---- a/tools/perf/builtin-sched.c -+++ b/tools/perf/builtin-sched.c -@@ -3126,7 +3126,8 @@ static size_t timehist_print_idlehist_callchain(struct rb_root_cached *root) - size_t ret = 0; - FILE *fp = stdout; - struct callchain_node *chain; -- struct rb_node *rb_node = rb_first_cached(root); -+ /* sort() uses rb_insert_color() on rb_root, not rb_root_cached */ -+ struct rb_node *rb_node = rb_first(&root->rb_root); - - printf(" %16s %8s %s\n", "Idle time (msec)", "Count", "Callchains"); - printf(" %.16s %.8s %.50s\n", graph_dotted_line, graph_dotted_line, --- -2.53.0 - diff --git a/queue-6.12/perf-sched-fix-null-dereference-in-latency_runtime_e.patch b/queue-6.12/perf-sched-fix-null-dereference-in-latency_runtime_e.patch deleted file mode 100644 index a3c7c63d55..0000000000 --- a/queue-6.12/perf-sched-fix-null-dereference-in-latency_runtime_e.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 15f991f106f73950850b3029d6e163b7d32ad579 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Thu, 4 Jun 2026 12:56:02 -0300 -Subject: perf sched: Fix NULL dereference in latency_runtime_event - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 8cbca8a480e15f6326ce94287570993f27a4b2d5 ] - -latency_runtime_event() passes the return value of -machine__findnew_thread() directly to thread_atoms_search() at line -1216, before checking for NULL at line 1220. thread_atoms_search() -calls pid_cmp() which dereferences the thread pointer via -thread__tid(), causing a NULL pointer dereference if the allocation -fails. - -All other callers of thread_atoms_search() in this file -(latency_switch_event, latency_wakeup_event, -latency_migrate_task_event) correctly check for NULL first. - -Move the atoms assignment after the NULL check to match the pattern -used by the other callers. - -Fixes: b91fc39f4ad7 ("perf machine: Protect the machine->threads with a rwlock") -Reported-by: sashiko-bot -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-sched.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c -index 00d50c356e43c1..12db6fe6e05a6a 100644 ---- a/tools/perf/builtin-sched.c -+++ b/tools/perf/builtin-sched.c -@@ -1214,13 +1214,15 @@ static int latency_runtime_event(struct perf_sched *sched, - const u32 pid = evsel__intval(evsel, sample, "pid"); - const u64 runtime = evsel__intval(evsel, sample, "runtime"); - struct thread *thread = machine__findnew_thread(machine, -1, pid); -- struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); -+ struct work_atoms *atoms; - u64 timestamp = sample->time; - int cpu = sample->cpu, err = -1; - - if (thread == NULL) - return -1; - -+ atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); -+ - /* perf.data is untrusted input — CPU may be absent or corrupted */ - if (cpu >= MAX_CPUS || cpu < 0) { - pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", --- -2.53.0 - diff --git a/queue-6.12/perf-sched-fix-register_pid-overflow-strcpy-and-bug_.patch b/queue-6.12/perf-sched-fix-register_pid-overflow-strcpy-and-bug_.patch deleted file mode 100644 index 4ec5ed6a30..0000000000 --- a/queue-6.12/perf-sched-fix-register_pid-overflow-strcpy-and-bug_.patch +++ /dev/null @@ -1,140 +0,0 @@ -From 0d2616e375487df9410dd818229be85ded869f7e Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Thu, 4 Jun 2026 18:25:57 -0300 -Subject: perf sched: Fix register_pid() overflow, strcpy, and BUG_ON - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 5949d339f5ec98752d56dcd4e36f619a59d513a5 ] - -register_pid() has several issues when processing untrusted perf.data: - -1. Integer overflow: (pid + 1) * sizeof(struct task_desc *) can wrap - to a small value on 32-bit systems when pid is large (e.g. - 0x40000000), causing realloc to return a tiny buffer followed by - out-of-bounds writes in the initialization loop. - -2. Heap buffer overflow: strcpy(task->comm, comm) copies the - untrusted comm string into a fixed 20-byte COMM_LEN buffer with - no length check. - -3. BUG_ON on allocation failure: perf.data is untrusted input, so - allocation failures should be handled gracefully rather than - killing the process. - -4. Realloc of sched->tasks assigned directly back, leaking the old - pointer on failure; nr_tasks incremented before the realloc, - leaving corrupted state on failure. - -Cap pid at PID_MAX_LIMIT (4194304, matching the kernel's maximum -on 64-bit), replace strcpy with strlcpy, guard against NULL comm, -replace BUG_ON with NULL returns using safe realloc patterns, and -add NULL checks in callers that dereference the result. - -Fixes: ec156764d424 ("perf sched: Import schedbench.c") -Reported-by: sashiko-bot -Cc: Ingo Molnar -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-sched.c | 40 ++++++++++++++++++++++++++++---------- - 1 file changed, 30 insertions(+), 10 deletions(-) - -diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c -index 12db6fe6e05a6a..4596037b5763c0 100644 ---- a/tools/perf/builtin-sched.c -+++ b/tools/perf/builtin-sched.c -@@ -51,6 +51,7 @@ - #define COMM_LEN 20 - #define SYM_LEN 129 - #define MAX_PID 1024000 -+#define PID_MAX_LIMIT 4194304 /* kernel limit on 64-bit */ - #define MAX_PRIO 140 - - static const char *cpu_list; -@@ -440,17 +441,28 @@ static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *ta - static struct task_desc *register_pid(struct perf_sched *sched, - unsigned long pid, const char *comm) - { -- struct task_desc *task; -+ struct task_desc *task, **tasks_p; - static int pid_max; - -+ /* perf.data is untrusted — cap pid to prevent overflow in size calculations */ -+ if (pid >= PID_MAX_LIMIT) { -+ pr_err("pid %lu exceeds limit %d, skipping\n", pid, PID_MAX_LIMIT); -+ return NULL; -+ } -+ - if (sched->pid_to_task == NULL) { - if (sysctl__read_int("kernel/pid_max", &pid_max) < 0) - pid_max = MAX_PID; -- BUG_ON((sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *))) == NULL); -+ sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *)); -+ if (sched->pid_to_task == NULL) -+ return NULL; - } - if (pid >= (unsigned long)pid_max) { -- BUG_ON((sched->pid_to_task = realloc(sched->pid_to_task, (pid + 1) * -- sizeof(struct task_desc *))) == NULL); -+ void *p = realloc(sched->pid_to_task, (pid + 1) * sizeof(struct task_desc *)); -+ -+ if (p == NULL) -+ return NULL; -+ sched->pid_to_task = p; - while (pid >= (unsigned long)pid_max) - sched->pid_to_task[pid_max++] = NULL; - } -@@ -461,9 +473,11 @@ static struct task_desc *register_pid(struct perf_sched *sched, - return task; - - task = zalloc(sizeof(*task)); -+ if (task == NULL) -+ return NULL; - task->pid = pid; -- task->nr = sched->nr_tasks; -- strcpy(task->comm, comm); -+ if (comm) -+ strlcpy(task->comm, comm, sizeof(task->comm)); - /* - * every task starts in sleeping state - this gets ignored - * if there's no wakeup pointing to this sleep state: -@@ -471,10 +485,12 @@ static struct task_desc *register_pid(struct perf_sched *sched, - add_sched_event_sleep(sched, task, 0, 0); - - sched->pid_to_task[pid] = task; -- sched->nr_tasks++; -- sched->tasks = realloc(sched->tasks, sched->nr_tasks * sizeof(struct task_desc *)); -- BUG_ON(!sched->tasks); -- sched->tasks[task->nr] = task; -+ tasks_p = realloc(sched->tasks, (sched->nr_tasks + 1) * sizeof(struct task_desc *)); -+ if (!tasks_p) -+ return NULL; -+ sched->tasks = tasks_p; -+ sched->tasks[sched->nr_tasks] = task; -+ task->nr = sched->nr_tasks++; - - if (verbose > 0) - printf("registered task #%ld, PID %ld (%s)\n", sched->nr_tasks, pid, comm); -@@ -838,6 +854,8 @@ replay_wakeup_event(struct perf_sched *sched, - - waker = register_pid(sched, sample->tid, ""); - wakee = register_pid(sched, pid, comm); -+ if (waker == NULL || wakee == NULL) -+ return -1; - - add_sched_event_wakeup(sched, waker, sample->time, wakee); - return 0; -@@ -880,6 +898,8 @@ static int replay_switch_event(struct perf_sched *sched, - - prev = register_pid(sched, prev_pid, prev_comm); - next = register_pid(sched, next_pid, next_comm); -+ if (prev == NULL || next == NULL) -+ return -1; - - sched->cpu_last_switched[cpu] = timestamp; - --- -2.53.0 - diff --git a/queue-6.12/perf-sched-fix-thread-leaks-in-perf-sched-timehist.patch b/queue-6.12/perf-sched-fix-thread-leaks-in-perf-sched-timehist.patch deleted file mode 100644 index 5390d2ae52..0000000000 --- a/queue-6.12/perf-sched-fix-thread-leaks-in-perf-sched-timehist.patch +++ /dev/null @@ -1,199 +0,0 @@ -From 7e45435541778380e6b352e5d1d5363b05b61536 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Wed, 2 Jul 2025 18:49:38 -0700 -Subject: perf sched: Fix thread leaks in 'perf sched timehist' - -From: Namhyung Kim - -[ Upstream commit e2eb59260c4f6bac403491d0112891766b8650d1 ] - -Add missing thread__put() after machine__findnew_thread() or -timehist_get_thread(). Also idle threads' last_thread should be -refcounted properly. - -Fixes: 699b5b920db04a6f ("perf sched timehist: Save callchain when entering idle") -Reviewed-by: Ian Rogers -Tested-by: Ian Rogers -Link: https://lore.kernel.org/r/20250703014942.1369397-5-namhyung@kernel.org -Signed-off-by: Namhyung Kim -Stable-dep-of: a99d6394cd48 ("perf sched: Use thread__put() in free_idle_threads()") -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-sched.c | 48 +++++++++++++++++++++++++++++--------- - 1 file changed, 37 insertions(+), 11 deletions(-) - -diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c -index 1203425d2b8a55..f3a03be9471aa0 100644 ---- a/tools/perf/builtin-sched.c -+++ b/tools/perf/builtin-sched.c -@@ -2386,8 +2386,10 @@ static void save_task_callchain(struct perf_sched *sched, - return; - } - -- if (!sched->show_callchain || sample->callchain == NULL) -+ if (!sched->show_callchain || sample->callchain == NULL) { -+ thread__put(thread); - return; -+ } - - cursor = get_tls_callchain_cursor(); - -@@ -2396,10 +2398,12 @@ static void save_task_callchain(struct perf_sched *sched, - if (verbose > 0) - pr_err("Failed to resolve callchain. Skipping\n"); - -+ thread__put(thread); - return; - } - - callchain_cursor_commit(cursor); -+ thread__put(thread); - - while (true) { - struct callchain_cursor_node *node; -@@ -2476,8 +2480,17 @@ static void free_idle_threads(void) - return; - - for (i = 0; i < idle_max_cpu; ++i) { -- if ((idle_threads[i])) -- thread__delete(idle_threads[i]); -+ struct thread *idle = idle_threads[i]; -+ -+ if (idle) { -+ struct idle_thread_runtime *itr; -+ -+ itr = thread__priv(idle); -+ if (itr) -+ thread__put(itr->last_thread); -+ -+ thread__delete(idle); -+ } - } - - free(idle_threads); -@@ -2517,7 +2530,7 @@ static struct thread *get_idle_thread(int cpu) - } - } - -- return idle_threads[cpu]; -+ return thread__get(idle_threads[cpu]); - } - - static void save_idle_callchain(struct perf_sched *sched, -@@ -2572,7 +2585,8 @@ static struct thread *timehist_get_thread(struct perf_sched *sched, - if (itr == NULL) - return NULL; - -- itr->last_thread = thread; -+ thread__put(itr->last_thread); -+ itr->last_thread = thread__get(thread); - - /* copy task callchain when entering to idle */ - if (evsel__intval(evsel, sample, "next_pid") == 0) -@@ -2643,6 +2657,7 @@ static void timehist_print_wakeup_event(struct perf_sched *sched, - /* show wakeup unless both awakee and awaker are filtered */ - if (timehist_skip_sample(sched, thread, evsel, sample) && - timehist_skip_sample(sched, awakened, evsel, sample)) { -+ thread__put(thread); - return; - } - -@@ -2659,6 +2674,8 @@ static void timehist_print_wakeup_event(struct perf_sched *sched, - printf("awakened: %s", timehist_get_commstr(awakened)); - - printf("\n"); -+ -+ thread__put(thread); - } - - static int timehist_sched_wakeup_ignore(const struct perf_tool *tool __maybe_unused, -@@ -2687,8 +2704,10 @@ static int timehist_sched_wakeup_event(const struct perf_tool *tool, - return -1; - - tr = thread__get_runtime(thread); -- if (tr == NULL) -+ if (tr == NULL) { -+ thread__put(thread); - return -1; -+ } - - if (tr->ready_to_run == 0) - tr->ready_to_run = sample->time; -@@ -2698,6 +2717,7 @@ static int timehist_sched_wakeup_event(const struct perf_tool *tool, - !perf_time__skip_sample(&sched->ptime, sample->time)) - timehist_print_wakeup_event(sched, evsel, sample, machine, thread); - -+ thread__put(thread); - return 0; - } - -@@ -2725,6 +2745,7 @@ static void timehist_print_migration_event(struct perf_sched *sched, - - if (timehist_skip_sample(sched, thread, evsel, sample) && - timehist_skip_sample(sched, migrated, evsel, sample)) { -+ thread__put(thread); - return; - } - -@@ -2752,6 +2773,7 @@ static void timehist_print_migration_event(struct perf_sched *sched, - printf(" cpu %d => %d", ocpu, dcpu); - - printf("\n"); -+ thread__put(thread); - } - - static int timehist_migrate_task_event(const struct perf_tool *tool, -@@ -2771,8 +2793,10 @@ static int timehist_migrate_task_event(const struct perf_tool *tool, - return -1; - - tr = thread__get_runtime(thread); -- if (tr == NULL) -+ if (tr == NULL) { -+ thread__put(thread); - return -1; -+ } - - tr->migrations++; - tr->migrated = sample->time; -@@ -2782,6 +2806,7 @@ static int timehist_migrate_task_event(const struct perf_tool *tool, - timehist_print_migration_event(sched, evsel, sample, - machine, thread); - } -+ thread__put(thread); - - return 0; - } -@@ -2804,10 +2829,10 @@ static void timehist_update_task_prio(struct evsel *evsel, - return; - - tr = thread__get_runtime(thread); -- if (tr == NULL) -- return; -+ if (tr != NULL) -+ tr->prio = next_prio; - -- tr->prio = next_prio; -+ thread__put(thread); - } - - static int timehist_sched_change_event(const struct perf_tool *tool, -@@ -2819,7 +2844,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool, - struct perf_sched *sched = container_of(tool, struct perf_sched, tool); - struct perf_time_interval *ptime = &sched->ptime; - struct addr_location al; -- struct thread *thread; -+ struct thread *thread = NULL; - struct thread_runtime *tr = NULL; - u64 tprev, t = sample->time; - int rc = 0; -@@ -2944,6 +2969,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool, - - evsel__save_time(evsel, sample->time, sample->cpu); - -+ thread__put(thread); - addr_location__exit(&al); - return rc; - } --- -2.53.0 - diff --git a/queue-6.12/perf-sched-fix-thread-reference-leak-in-latency_swit.patch b/queue-6.12/perf-sched-fix-thread-reference-leak-in-latency_swit.patch deleted file mode 100644 index 0a97c8f013..0000000000 --- a/queue-6.12/perf-sched-fix-thread-reference-leak-in-latency_swit.patch +++ /dev/null @@ -1,42 +0,0 @@ -From aeff520e1eb2a89c4f61def4ae298cd13e1e340a Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Thu, 4 Jun 2026 12:55:29 -0300 -Subject: perf sched: Fix thread reference leak in latency_switch_event - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 66ea9de60396a4dea5276bc87025884691876c36 ] - -In latency_switch_event(), after acquiring thread references for -sched_out and sched_in via machine__findnew_thread(), the first -add_sched_out_event() failure path does 'return -1', bypassing the -out_put label that calls thread__put() on both references. - -The second and third add_sched_out_event() failures correctly use -'goto out_put'. Fix the first one to match. - -Fixes: b91fc39f4ad7 ("perf machine: Protect the machine->threads with a rwlock") -Reported-by: sashiko-bot -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-sched.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c -index 6d7106fb1c0cbc..c0fd6a8595d0f2 100644 ---- a/tools/perf/builtin-sched.c -+++ b/tools/perf/builtin-sched.c -@@ -1175,7 +1175,7 @@ static int latency_switch_event(struct perf_sched *sched, - } - } - if (add_sched_out_event(out_events, prev_state, timestamp)) -- return -1; -+ goto out_put; - - in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid); - if (!in_events) { --- -2.53.0 - diff --git a/queue-6.12/perf-sched-replace-bug_on-and-add-null-checks-in-rep.patch b/queue-6.12/perf-sched-replace-bug_on-and-add-null-checks-in-rep.patch deleted file mode 100644 index ab4a9afc19..0000000000 --- a/queue-6.12/perf-sched-replace-bug_on-and-add-null-checks-in-rep.patch +++ /dev/null @@ -1,121 +0,0 @@ -From d14d51afef5376933d3142196548558a11e7f96e Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Fri, 5 Jun 2026 11:26:28 -0300 -Subject: perf sched: Replace BUG_ON and add NULL checks in replay event - helpers - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 75eafe4a3a93a0143a20c0cc286bfb9008ac1478 ] - -get_new_event() has three issues: - -1. The zalloc() result is dereferenced without a NULL check, crashing - on allocation failure. - -2. BUG_ON(!task->atoms) kills the process when realloc() fails. - Since perf.data is untrusted input, this should be a graceful error. - -3. The realloc pattern assigns directly to task->atoms, losing the old - pointer on failure. task->nr_events is also incremented before the - realloc, leaving corrupted state on failure. - -Fix get_new_event() to: - - Check the zalloc() result before dereferencing - - Use a temporary for realloc() to avoid losing the old pointer - - Increment nr_events only after successful realloc - - Return NULL instead of calling BUG_ON on failure - -Also fix add_sched_event_wakeup() where zalloc() for wait_sem is -passed to sem_init() without a NULL check. - -Update all callers (add_sched_event_run, add_sched_event_wakeup, -add_sched_event_sleep) to handle NULL returns by returning early. -The replay may produce incomplete output on OOM but will not crash. - -Fixes: ec156764d424 ("perf sched: Import schedbench.c") -Reported-by: sashiko-bot -Cc: Ingo Molnar -Cc: Namhyung Kim -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-sched.c | 28 +++++++++++++++++++++++++--- - 1 file changed, 25 insertions(+), 3 deletions(-) - -diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c -index 8eb5c880a5b605..d599ad421df108 100644 ---- a/tools/perf/builtin-sched.c -+++ b/tools/perf/builtin-sched.c -@@ -360,14 +360,25 @@ get_new_event(struct task_desc *task, u64 timestamp) - struct sched_atom *event = zalloc(sizeof(*event)); - unsigned long idx = task->nr_events; - size_t size; -+ struct sched_atom **atoms_p; -+ -+ if (event == NULL) { -+ pr_err("ERROR: sched: failed to allocate event\n"); -+ return NULL; -+ } - - event->timestamp = timestamp; - event->nr = idx; - -+ size = sizeof(struct sched_atom *) * (task->nr_events + 1); -+ atoms_p = realloc(task->atoms, size); -+ if (!atoms_p) { -+ pr_err("ERROR: sched: failed to grow atoms array\n"); -+ free(event); -+ return NULL; -+ } -+ task->atoms = atoms_p; - task->nr_events++; -- size = sizeof(struct sched_atom *) * task->nr_events; -- task->atoms = realloc(task->atoms, size); -- BUG_ON(!task->atoms); - - task->atoms[idx] = event; - -@@ -398,6 +409,8 @@ static void add_sched_event_run(struct perf_sched *sched, struct task_desc *task - } - - event = get_new_event(task, timestamp); -+ if (event == NULL) -+ return; - - event->type = SCHED_EVENT_RUN; - event->duration = duration; -@@ -411,6 +424,8 @@ static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *t - struct sched_atom *event, *wakee_event; - - event = get_new_event(task, timestamp); -+ if (event == NULL) -+ return; - event->type = SCHED_EVENT_WAKEUP; - event->wakee = wakee; - -@@ -425,6 +440,10 @@ static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *t - } - - wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem)); -+ if (!wakee_event->wait_sem) { -+ pr_err("ERROR: sched: failed to allocate semaphore\n"); -+ return; -+ } - sem_init(wakee_event->wait_sem, 0, 0); - wakee_event->specific_wait = 1; - event->wait_sem = wakee_event->wait_sem; -@@ -437,6 +456,9 @@ static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *ta - { - struct sched_atom *event = get_new_event(task, timestamp); - -+ if (event == NULL) -+ return; -+ - event->type = SCHED_EVENT_SLEEP; - - sched->nr_sleep_events++; --- -2.53.0 - diff --git a/queue-6.12/perf-sched-replace-bug_on-on-invalid-cpu-with-gracef.patch b/queue-6.12/perf-sched-replace-bug_on-on-invalid-cpu-with-gracef.patch deleted file mode 100644 index 2d84490724..0000000000 --- a/queue-6.12/perf-sched-replace-bug_on-on-invalid-cpu-with-gracef.patch +++ /dev/null @@ -1,84 +0,0 @@ -From a12dc855834bb7be2affdb52098a836fc82064e7 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Mon, 1 Jun 2026 19:53:41 -0300 -Subject: perf sched: Replace BUG_ON on invalid CPU with graceful skip - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 1e2c83f732deb329ebce23e26cbc482f4c4bf194 ] - -latency_switch_event(), latency_runtime_event(), and map_switch_event() -use BUG_ON(cpu >= MAX_CPUS || cpu < 0) to validate the sample CPU. -When PERF_SAMPLE_CPU is absent from the sample type, -evsel__parse_sample() initializes sample->cpu to (u32)-1. Casting -this to int yields -1, which triggers the BUG_ON and aborts perf sched. - -The central CPU validation in perf_session__deliver_event() intentionally -preserves the (u32)-1 sentinel for downstream tools like perf script -and perf inject, so leaf callbacks must handle it themselves. - -Replace the three BUG_ON calls with graceful skips using pr_warning(), -matching the existing pattern in process_sched_switch_event() and -process_sched_runtime_event() earlier in the same file. Include the -file offset for cross-referencing with perf report -D. - -Reported-by: sashiko-bot@kernel.org # Running on a local machine -Reviewed-by: Ian Rogers -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Stable-dep-of: 8cbca8a480e1 ("perf sched: Fix NULL dereference in latency_runtime_event") -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-sched.c | 22 +++++++++++++++++++--- - 1 file changed, 19 insertions(+), 3 deletions(-) - -diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c -index c0fd6a8595d0f2..00d50c356e43c1 100644 ---- a/tools/perf/builtin-sched.c -+++ b/tools/perf/builtin-sched.c -@@ -1145,7 +1145,12 @@ static int latency_switch_event(struct perf_sched *sched, - int cpu = sample->cpu, err = -1; - s64 delta; - -- BUG_ON(cpu >= MAX_CPUS || cpu < 0); -+ /* perf.data is untrusted input — CPU may be absent or corrupted */ -+ if (cpu >= MAX_CPUS || cpu < 0) { -+ pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", -+ sample->file_offset, cpu); -+ return 0; -+ } - - timestamp0 = sched->cpu_last_switched[cpu]; - sched->cpu_last_switched[cpu] = timestamp; -@@ -1216,7 +1221,13 @@ static int latency_runtime_event(struct perf_sched *sched, - if (thread == NULL) - return -1; - -- BUG_ON(cpu >= MAX_CPUS || cpu < 0); -+ /* perf.data is untrusted input — CPU may be absent or corrupted */ -+ if (cpu >= MAX_CPUS || cpu < 0) { -+ pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", -+ sample->file_offset, cpu); -+ err = 0; -+ goto out_put; -+ } - if (!atoms) { - if (thread_atoms_insert(sched, thread)) - goto out_put; -@@ -1656,7 +1667,12 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, - const char *str; - int ret = -1; - -- BUG_ON(this_cpu.cpu >= MAX_CPUS || this_cpu.cpu < 0); -+ /* perf.data is untrusted input — CPU may be absent or corrupted */ -+ if (this_cpu.cpu >= MAX_CPUS || this_cpu.cpu < 0) { -+ pr_warning("WARNING: at offset %#" PRIx64 ": out-of-bound sample CPU %d, skipping sample\n", -+ sample->file_offset, this_cpu.cpu); -+ return 0; -+ } - - if (this_cpu.cpu > sched->max_cpu.cpu) - sched->max_cpu = this_cpu; --- -2.53.0 - diff --git a/queue-6.12/perf-sched-timehist-add-pre-migration-wait-time-opti.patch b/queue-6.12/perf-sched-timehist-add-pre-migration-wait-time-opti.patch deleted file mode 100644 index a9f90fc38e..0000000000 --- a/queue-6.12/perf-sched-timehist-add-pre-migration-wait-time-opti.patch +++ /dev/null @@ -1,301 +0,0 @@ -From 638a0bcd5724e3f1c6beecc3c1cf2c34ad5236e4 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Fri, 4 Oct 2024 22:37:56 +0530 -Subject: perf sched timehist: Add pre-migration wait time option - -From: Madadi Vineeth Reddy - -[ Upstream commit cd912ab3b65445df7b95bbdd06ac29f823d4df98 ] - -pre-migration wait time is the time that a task unnecessarily spends -on the runqueue of a CPU but doesn't get switched-in there. In terms -of tracepoints, it is the time between sched:sched_wakeup and -sched:sched_migrate_task. - -Let's say a task woke up on CPU2, then it got migrated to CPU4 and -then it's switched-in to CPU4. So, here pre-migration wait time is -time that it was waiting on runqueue of CPU2 after it is woken up. - -The general pattern for pre-migration to occur is: -sched:sched_wakeup -sched:sched_migrate_task -sched:sched_switch - -The sched:sched_waking event is used to capture the wakeup time, -as it aligns with the existing code and only introduces a negligible -time difference. - -pre-migrations are generally not useful and it increases migrations. -This metric would be helpful in testing patches mainly related to wakeup -and load-balancer code paths as better wakeup logic would choose an -optimal CPU where task would be switched-in and thereby reducing pre- -migrations. - -The sample output(s) when -P or --pre-migrations is used: -================= - time cpu task name wait time sch delay run time pre-mig time - [tid/pid] (msec) (msec) (msec) (msec) ---------------- ------ ------------------------------ --------- --------- --------- --------- - 38456.720806 [0001] schbench[28634/28574] 4.917 4.768 1.004 0.000 - 38456.720810 [0001] rcu_preempt[18] 3.919 0.003 0.004 0.000 - 38456.721800 [0006] schbench[28779/28574] 23.465 23.465 1.999 0.000 - 38456.722800 [0002] schbench[28773/28574] 60.371 60.237 3.955 60.197 - 38456.722806 [0001] schbench[28634/28574] 0.004 0.004 1.996 0.000 - 38456.722811 [0001] rcu_preempt[18] 1.996 0.005 0.005 0.000 - 38456.723800 [0000] schbench[28833/28574] 4.000 4.000 3.999 0.000 - 38456.723800 [0004] schbench[28762/28574] 42.951 42.839 3.999 39.867 - 38456.723802 [0007] schbench[28812/28574] 43.947 43.817 3.999 40.866 - 38456.723804 [0001] schbench[28587/28574] 7.935 7.822 0.993 0.000 - -Signed-off-by: Madadi Vineeth Reddy -Reviewed-by: Tim Chen -Link: https://lore.kernel.org/r/20241004170756.18064-1-vineethr@linux.ibm.com -Signed-off-by: Namhyung Kim -Stable-dep-of: a99d6394cd48 ("perf sched: Use thread__put() in free_idle_threads()") -Signed-off-by: Sasha Levin ---- - tools/perf/Documentation/perf-sched.txt | 8 ++ - tools/perf/builtin-sched.c | 98 +++++++++++++++---------- - 2 files changed, 67 insertions(+), 39 deletions(-) - -diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt -index 3db64954a267a3..6dbbddb6464d01 100644 ---- a/tools/perf/Documentation/perf-sched.txt -+++ b/tools/perf/Documentation/perf-sched.txt -@@ -221,6 +221,14 @@ OPTIONS for 'perf sched timehist' - priorities are specified with -: 120-129. A combination of both can also be - provided: 0,120-129. - -+-P:: -+--pre-migrations:: -+ Show pre-migration wait time. pre-migration wait time is the time spent -+ by a task waiting on a runqueue but not getting the chance to run there -+ and is migrated to a different runqueue where it is finally run. This -+ time between sched_wakeup and migrate_task is the pre-migration wait -+ time. -+ - OPTIONS for 'perf sched replay' - ------------------------------ - -diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c -index fd93ab11bd37ed..1203425d2b8a55 100644 ---- a/tools/perf/builtin-sched.c -+++ b/tools/perf/builtin-sched.c -@@ -229,6 +229,7 @@ struct perf_sched { - bool show_wakeups; - bool show_next; - bool show_migrations; -+ bool pre_migrations; - bool show_state; - bool show_prio; - u64 skipped_samples; -@@ -248,7 +249,9 @@ struct thread_runtime { - u64 dt_iowait; /* time between CPU access by iowait (off cpu) */ - u64 dt_preempt; /* time between CPU access by preempt (off cpu) */ - u64 dt_delay; /* time between wakeup and sched-in */ -+ u64 dt_pre_mig; /* time between migration and wakeup */ - u64 ready_to_run; /* time of wakeup */ -+ u64 migrated; /* time when a thread is migrated */ - - struct stats run_stats; - u64 total_run_time; -@@ -256,6 +259,7 @@ struct thread_runtime { - u64 total_iowait_time; - u64 total_preempt_time; - u64 total_delay_time; -+ u64 total_pre_mig_time; - - char last_state; - -@@ -2148,14 +2152,15 @@ static void timehist_header(struct perf_sched *sched) - printf(" "); - } - -- if (sched->show_prio) { -- printf(" %-*s %-*s %9s %9s %9s", -- comm_width, "task name", MAX_PRIO_STR_LEN, "prio", -- "wait time", "sch delay", "run time"); -- } else { -- printf(" %-*s %9s %9s %9s", comm_width, -- "task name", "wait time", "sch delay", "run time"); -- } -+ printf(" %-*s", comm_width, "task name"); -+ -+ if (sched->show_prio) -+ printf(" %-*s", MAX_PRIO_STR_LEN, "prio"); -+ -+ printf(" %9s %9s %9s", "wait time", "sch delay", "run time"); -+ -+ if (sched->pre_migrations) -+ printf(" %9s", "pre-mig time"); - - if (sched->show_state) - printf(" %s", "state"); -@@ -2170,17 +2175,15 @@ static void timehist_header(struct perf_sched *sched) - if (sched->show_cpu_visual) - printf(" %*s ", ncpus, ""); - -- if (sched->show_prio) { -- printf(" %-*s %-*s %9s %9s %9s", -- comm_width, "[tid/pid]", MAX_PRIO_STR_LEN, "", -- "(msec)", "(msec)", "(msec)"); -- } else { -- printf(" %-*s %9s %9s %9s", comm_width, -- "[tid/pid]", "(msec)", "(msec)", "(msec)"); -- } -+ printf(" %-*s", comm_width, "[tid/pid]"); - -- if (sched->show_state) -- printf(" %5s", ""); -+ if (sched->show_prio) -+ printf(" %-*s", MAX_PRIO_STR_LEN, ""); -+ -+ printf(" %9s %9s %9s", "(msec)", "(msec)", "(msec)"); -+ -+ if (sched->pre_migrations) -+ printf(" %9s", "(msec)"); - - printf("\n"); - -@@ -2192,15 +2195,15 @@ static void timehist_header(struct perf_sched *sched) - if (sched->show_cpu_visual) - printf(" %.*s ", ncpus, graph_dotted_line); - -- if (sched->show_prio) { -- printf(" %.*s %.*s %.9s %.9s %.9s", -- comm_width, graph_dotted_line, MAX_PRIO_STR_LEN, graph_dotted_line, -- graph_dotted_line, graph_dotted_line, graph_dotted_line); -- } else { -- printf(" %.*s %.9s %.9s %.9s", comm_width, -- graph_dotted_line, graph_dotted_line, graph_dotted_line, -- graph_dotted_line); -- } -+ printf(" %.*s", comm_width, graph_dotted_line); -+ -+ if (sched->show_prio) -+ printf(" %.*s", MAX_PRIO_STR_LEN, graph_dotted_line); -+ -+ printf(" %.9s %.9s %.9s", graph_dotted_line, graph_dotted_line, graph_dotted_line); -+ -+ if (sched->pre_migrations) -+ printf(" %.9s", graph_dotted_line); - - if (sched->show_state) - printf(" %.5s", graph_dotted_line); -@@ -2256,6 +2259,8 @@ static void timehist_print_sample(struct perf_sched *sched, - - print_sched_time(tr->dt_delay, 6); - print_sched_time(tr->dt_run, 6); -+ if (sched->pre_migrations) -+ print_sched_time(tr->dt_pre_mig, 6); - - if (sched->show_state) - printf(" %5c ", thread__tid(thread) == 0 ? 'I' : state); -@@ -2293,18 +2298,21 @@ static void timehist_print_sample(struct perf_sched *sched, - * last_time = time of last sched change event for current task - * (i.e, time process was last scheduled out) - * ready_to_run = time of wakeup for current task -+ * migrated = time of task migration to another CPU - * -- * -----|------------|------------|------------|------ -- * last ready tprev t -+ * -----|-------------|-------------|-------------|-------------|----- -+ * last ready migrated tprev t - * time to run - * -- * |-------- dt_wait --------| -- * |- dt_delay -|-- dt_run --| -+ * |---------------- dt_wait ----------------| -+ * |--------- dt_delay ---------|-- dt_run --| -+ * |- dt_pre_mig -| - * -- * dt_run = run time of current task -- * dt_wait = time between last schedule out event for task and tprev -- * represents time spent off the cpu -- * dt_delay = time between wakeup and schedule-in of task -+ * dt_run = run time of current task -+ * dt_wait = time between last schedule out event for task and tprev -+ * represents time spent off the cpu -+ * dt_delay = time between wakeup and schedule-in of task -+ * dt_pre_mig = time between wakeup and migration to another CPU - */ - - static void timehist_update_runtime_stats(struct thread_runtime *r, -@@ -2315,6 +2323,7 @@ static void timehist_update_runtime_stats(struct thread_runtime *r, - r->dt_iowait = 0; - r->dt_preempt = 0; - r->dt_run = 0; -+ r->dt_pre_mig = 0; - - if (tprev) { - r->dt_run = t - tprev; -@@ -2323,6 +2332,9 @@ static void timehist_update_runtime_stats(struct thread_runtime *r, - pr_debug("time travel: wakeup time for task > previous sched_switch event\n"); - else - r->dt_delay = tprev - r->ready_to_run; -+ -+ if ((r->migrated > r->ready_to_run) && (r->migrated < tprev)) -+ r->dt_pre_mig = r->migrated - r->ready_to_run; - } - - if (r->last_time > tprev) -@@ -2346,6 +2358,7 @@ static void timehist_update_runtime_stats(struct thread_runtime *r, - r->total_sleep_time += r->dt_sleep; - r->total_iowait_time += r->dt_iowait; - r->total_preempt_time += r->dt_preempt; -+ r->total_pre_mig_time += r->dt_pre_mig; - } - - static bool is_idle_sample(struct perf_sample *sample, -@@ -2762,9 +2775,13 @@ static int timehist_migrate_task_event(const struct perf_tool *tool, - return -1; - - tr->migrations++; -+ tr->migrated = sample->time; - - /* show migrations if requested */ -- timehist_print_migration_event(sched, evsel, sample, machine, thread); -+ if (sched->show_migrations) { -+ timehist_print_migration_event(sched, evsel, sample, -+ machine, thread); -+ } - - return 0; - } -@@ -2916,11 +2933,13 @@ static int timehist_sched_change_event(const struct perf_tool *tool, - /* last state is used to determine where to account wait time */ - tr->last_state = state; - -- /* sched out event for task so reset ready to run time */ -+ /* sched out event for task so reset ready to run time and migrated time */ - if (state == 'R') - tr->ready_to_run = t; - else - tr->ready_to_run = 0; -+ -+ tr->migrated = 0; - } - - evsel__save_time(evsel, sample->time, sample->cpu); -@@ -3363,8 +3382,8 @@ static int perf_sched__timehist(struct perf_sched *sched) - goto out; - } - -- if (sched->show_migrations && -- perf_session__set_tracepoints_handlers(session, migrate_handlers)) -+ if ((sched->show_migrations || sched->pre_migrations) && -+ perf_session__set_tracepoints_handlers(session, migrate_handlers)) - goto out; - - /* pre-allocate struct for per-CPU idle stats */ -@@ -3919,6 +3938,7 @@ int cmd_sched(int argc, const char **argv) - OPT_BOOLEAN(0, "show-prio", &sched.show_prio, "Show task priority"), - OPT_STRING(0, "prio", &sched.prio_str, "prio", - "analyze events only for given task priority(ies)"), -+ OPT_BOOLEAN('P', "pre-migrations", &sched.pre_migrations, "Show pre-migration wait time"), - OPT_PARENT(sched_options) - }; - --- -2.53.0 - diff --git a/queue-6.12/perf-sched-use-thread__put-in-free_idle_threads.patch b/queue-6.12/perf-sched-use-thread__put-in-free_idle_threads.patch deleted file mode 100644 index ae14c521b1..0000000000 --- a/queue-6.12/perf-sched-use-thread__put-in-free_idle_threads.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 4a601c1324dcd54899d2639c80a9be567ac4ed77 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Fri, 5 Jun 2026 11:17:03 -0300 -Subject: perf sched: Use thread__put() in free_idle_threads() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -From: Arnaldo Carvalho de Melo - -[ Upstream commit a99d6394cd48fed75b1d24733d5afe6837a61a3f ] - -free_idle_threads() calls thread__delete() directly instead of -thread__put(), bypassing the reference counting lifecycle. Under -REFCNT_CHECKING builds, this leaks the pointer handle since -thread__delete() frees the object without going through the refcount -wrapper. - -The idle threads are created via thread__new() (refcount=1) in -get_idle_thread(). Callers get additional references via thread__get() -which they release with thread__put(). free_idle_threads() drops the -base reference — thread__put() is the correct call, matching the -thread__new() acquisition. - -Fixes: 49394a2a24c7 ("perf sched timehist: Introduce timehist command") -Reported-by: sashiko-bot -Cc: David Ahern -Cc: Namhyung Kim -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-sched.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c -index f3a03be9471aa0..8eb5c880a5b605 100644 ---- a/tools/perf/builtin-sched.c -+++ b/tools/perf/builtin-sched.c -@@ -2489,7 +2489,7 @@ static void free_idle_threads(void) - if (itr) - thread__put(itr->last_thread); - -- thread__delete(idle); -+ thread__put(idle); - } - } - --- -2.53.0 - diff --git a/queue-6.12/perf-stat-bounds-check-cpu-index-in-topology-aggrega.patch b/queue-6.12/perf-stat-bounds-check-cpu-index-in-topology-aggrega.patch deleted file mode 100644 index b63f68f031..0000000000 --- a/queue-6.12/perf-stat-bounds-check-cpu-index-in-topology-aggrega.patch +++ /dev/null @@ -1,100 +0,0 @@ -From 5709b7f407201e5c57ad5b40736ae821ac5ab396 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Fri, 5 Jun 2026 10:59:11 -0300 -Subject: perf stat: Bounds-check CPU index in topology aggregation callbacks - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 52e69b1c5b606b513d403dd4addc784c27a0c8e2 ] - -Six perf_env__get_*_aggr_by_cpu() functions access env->cpu[cpu.cpu] -after only checking cpu.cpu != -1. env->cpu[] is allocated with -env->nr_cpus_avail entries, so a CPU index from an untrusted perf.data -file that exceeds that count causes an out-of-bounds heap read. - -Replace the != -1 guard with >= 0 && < env->nr_cpus_avail in all six -functions. The >= 0 check also catches -1 and any other negative values -that could bypass the old check. - -Affected functions: - - perf_env__get_socket_aggr_by_cpu() - - perf_env__get_die_aggr_by_cpu() - - perf_env__get_cache_aggr_by_cpu() - - perf_env__get_cluster_aggr_by_cpu() - - perf_env__get_core_aggr_by_cpu() - - perf_env__get_cpu_aggr_by_cpu() - -Fixes: 68d702f7a120 ("perf stat report: Add support to initialize aggr_map from file") -Reported-by: sashiko-bot -Cc: Ian Rogers -Cc: Jiri Olsa -Cc: Namhyung Kim -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-stat.c | 13 +++++++------ - 1 file changed, 7 insertions(+), 6 deletions(-) - -diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c -index e476598de80836..4331820b199601 100644 ---- a/tools/perf/builtin-stat.c -+++ b/tools/perf/builtin-stat.c -@@ -1561,7 +1561,8 @@ static struct aggr_cpu_id perf_env__get_socket_aggr_by_cpu(struct perf_cpu cpu, - struct perf_env *env = data; - struct aggr_cpu_id id = aggr_cpu_id__empty(); - -- if (cpu.cpu != -1) -+ /* env->cpu[] has env->nr_cpus_avail entries; reject untrusted indices */ -+ if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) - id.socket = env->cpu[cpu.cpu].socket_id; - - return id; -@@ -1572,7 +1573,7 @@ static struct aggr_cpu_id perf_env__get_die_aggr_by_cpu(struct perf_cpu cpu, voi - struct perf_env *env = data; - struct aggr_cpu_id id = aggr_cpu_id__empty(); - -- if (cpu.cpu != -1) { -+ if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { - /* - * die_id is relative to socket, so start - * with the socket ID and then add die to -@@ -1628,7 +1629,7 @@ static struct aggr_cpu_id perf_env__get_cache_aggr_by_cpu(struct perf_cpu cpu, - struct perf_env *env = data; - struct aggr_cpu_id id = aggr_cpu_id__empty(); - -- if (cpu.cpu != -1) { -+ if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { - u32 cache_level = (perf_stat.aggr_level) ?: stat_config.aggr_level; - - id.socket = env->cpu[cpu.cpu].socket_id; -@@ -1645,7 +1646,7 @@ static struct aggr_cpu_id perf_env__get_cluster_aggr_by_cpu(struct perf_cpu cpu, - struct perf_env *env = data; - struct aggr_cpu_id id = aggr_cpu_id__empty(); - -- if (cpu.cpu != -1) { -+ if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; - id.cluster = env->cpu[cpu.cpu].cluster_id; -@@ -1659,7 +1660,7 @@ static struct aggr_cpu_id perf_env__get_core_aggr_by_cpu(struct perf_cpu cpu, vo - struct perf_env *env = data; - struct aggr_cpu_id id = aggr_cpu_id__empty(); - -- if (cpu.cpu != -1) { -+ if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { - /* - * core_id is relative to socket, die and cluster, we need a - * global id. So we set socket, die id, cluster id and core id. -@@ -1678,7 +1679,7 @@ static struct aggr_cpu_id perf_env__get_cpu_aggr_by_cpu(struct perf_cpu cpu, voi - struct perf_env *env = data; - struct aggr_cpu_id id = aggr_cpu_id__empty(); - -- if (cpu.cpu != -1) { -+ if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { - /* - * core_id is relative to socket and die, - * we need a global id. So we set --- -2.53.0 - diff --git a/queue-6.12/perf-stat-introduce-perf_env__get_cpu_topology-to-gu.patch b/queue-6.12/perf-stat-introduce-perf_env__get_cpu_topology-to-gu.patch deleted file mode 100644 index 7e4ecd8568..0000000000 --- a/queue-6.12/perf-stat-introduce-perf_env__get_cpu_topology-to-gu.patch +++ /dev/null @@ -1,180 +0,0 @@ -From dcdee7052257bab063c42acf931aa4e905732e31 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 6 Jun 2026 11:17:45 -0300 -Subject: perf stat: Introduce perf_env__get_cpu_topology() to guard NULL - env->cpu - -From: Arnaldo Carvalho de Melo - -[ Upstream commit afa4363a91a19dff65dceb7fbce7bba689bbc854 ] - -process_cpu_topology() in header.c frees env->cpu on old-format -perf.data files that predate topology information, but leaves -nr_cpus_avail set. The six perf_env__get_*_aggr_by_cpu() functions -in builtin-stat.c pass the bounds check but dereference a NULL -env->cpu pointer, crashing on old recordings. - -Introduce perf_env__get_cpu_topology() as a safe accessor that -validates env->cpu, cpu.cpu >= 0, and cpu.cpu < nr_cpus_avail in -one place, returning a struct cpu_topology_map pointer or NULL. -Convert all six topology aggregation callbacks to use it. - -Fixes: 88031a0de7d68d13 ("perf stat: Switch to cpu version of cpu_map__get()") -Reported-by: sashiko-bot -Cc: Ian Rogers -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-stat.c | 51 +++++++++++++++++++++------------------ - tools/perf/util/env.h | 14 +++++++++++ - 2 files changed, 42 insertions(+), 23 deletions(-) - -diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c -index 4331820b199601..2b8b3354e0cf7b 100644 ---- a/tools/perf/builtin-stat.c -+++ b/tools/perf/builtin-stat.c -@@ -1560,10 +1560,10 @@ static struct aggr_cpu_id perf_env__get_socket_aggr_by_cpu(struct perf_cpu cpu, - { - struct perf_env *env = data; - struct aggr_cpu_id id = aggr_cpu_id__empty(); -+ struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - -- /* env->cpu[] has env->nr_cpus_avail entries; reject untrusted indices */ -- if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) -- id.socket = env->cpu[cpu.cpu].socket_id; -+ if (topo) -+ id.socket = topo->socket_id; - - return id; - } -@@ -1572,15 +1572,16 @@ static struct aggr_cpu_id perf_env__get_die_aggr_by_cpu(struct perf_cpu cpu, voi - { - struct perf_env *env = data; - struct aggr_cpu_id id = aggr_cpu_id__empty(); -+ struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - -- if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { -+ if (topo) { - /* - * die_id is relative to socket, so start - * with the socket ID and then add die to - * make a unique ID. - */ -- id.socket = env->cpu[cpu.cpu].socket_id; -- id.die = env->cpu[cpu.cpu].die_id; -+ id.socket = topo->socket_id; -+ id.die = topo->die_id; - } - - return id; -@@ -1628,12 +1629,13 @@ static struct aggr_cpu_id perf_env__get_cache_aggr_by_cpu(struct perf_cpu cpu, - { - struct perf_env *env = data; - struct aggr_cpu_id id = aggr_cpu_id__empty(); -+ struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - -- if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { -+ if (topo) { - u32 cache_level = (perf_stat.aggr_level) ?: stat_config.aggr_level; - -- id.socket = env->cpu[cpu.cpu].socket_id; -- id.die = env->cpu[cpu.cpu].die_id; -+ id.socket = topo->socket_id; -+ id.die = topo->die_id; - perf_env__get_cache_id_for_cpu(cpu, env, cache_level, &id); - } - -@@ -1645,11 +1647,12 @@ static struct aggr_cpu_id perf_env__get_cluster_aggr_by_cpu(struct perf_cpu cpu, - { - struct perf_env *env = data; - struct aggr_cpu_id id = aggr_cpu_id__empty(); -+ struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - -- if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { -- id.socket = env->cpu[cpu.cpu].socket_id; -- id.die = env->cpu[cpu.cpu].die_id; -- id.cluster = env->cpu[cpu.cpu].cluster_id; -+ if (topo) { -+ id.socket = topo->socket_id; -+ id.die = topo->die_id; -+ id.cluster = topo->cluster_id; - } - - return id; -@@ -1659,16 +1662,17 @@ static struct aggr_cpu_id perf_env__get_core_aggr_by_cpu(struct perf_cpu cpu, vo - { - struct perf_env *env = data; - struct aggr_cpu_id id = aggr_cpu_id__empty(); -+ struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - -- if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { -+ if (topo) { - /* - * core_id is relative to socket, die and cluster, we need a - * global id. So we set socket, die id, cluster id and core id. - */ -- id.socket = env->cpu[cpu.cpu].socket_id; -- id.die = env->cpu[cpu.cpu].die_id; -- id.cluster = env->cpu[cpu.cpu].cluster_id; -- id.core = env->cpu[cpu.cpu].core_id; -+ id.socket = topo->socket_id; -+ id.die = topo->die_id; -+ id.cluster = topo->cluster_id; -+ id.core = topo->core_id; - } - - return id; -@@ -1678,18 +1682,19 @@ static struct aggr_cpu_id perf_env__get_cpu_aggr_by_cpu(struct perf_cpu cpu, voi - { - struct perf_env *env = data; - struct aggr_cpu_id id = aggr_cpu_id__empty(); -+ struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - -- if (cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) { -+ if (topo) { - /* - * core_id is relative to socket and die, - * we need a global id. So we set - * socket, die id and core id - */ -- id.socket = env->cpu[cpu.cpu].socket_id; -- id.die = env->cpu[cpu.cpu].die_id; -- id.core = env->cpu[cpu.cpu].core_id; -- id.cpu = cpu; -+ id.socket = topo->socket_id; -+ id.die = topo->die_id; -+ id.core = topo->core_id; - } -+ id.cpu = cpu; - - return id; - } -diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h -index 38de0af2a68081..0ba95396ecf04d 100644 ---- a/tools/perf/util/env.h -+++ b/tools/perf/util/env.h -@@ -167,6 +167,20 @@ const char *perf_env__pmu_mappings(struct perf_env *env); - - int perf_env__read_cpu_topology_map(struct perf_env *env); - -+/* -+ * Safe accessor for env->cpu[] topology array. env->cpu can be NULL when -+ * reading old-format perf.data that predates topology information — -+ * process_cpu_topology() in header.c frees it while nr_cpus_avail remains -+ * set, so callers must not index env->cpu[] without this check. -+ */ -+static inline struct cpu_topology_map * -+perf_env__get_cpu_topology(struct perf_env *env, struct perf_cpu cpu) -+{ -+ if (env->cpu && cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) -+ return &env->cpu[cpu.cpu]; -+ return NULL; -+} -+ - void cpu_cache_level__free(struct cpu_cache_level *cache); - - const char *perf_env__arch(struct perf_env *env); --- -2.53.0 - diff --git a/queue-6.12/perf-symbols-add-bounds-checks-to-elf_read_build_id-.patch b/queue-6.12/perf-symbols-add-bounds-checks-to-elf_read_build_id-.patch deleted file mode 100644 index e375d3e819..0000000000 --- a/queue-6.12/perf-symbols-add-bounds-checks-to-elf_read_build_id-.patch +++ /dev/null @@ -1,64 +0,0 @@ -From fdedca7203c4d5eb76d80a8d22d49483a19177be Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Mon, 8 Jun 2026 08:12:34 -0300 -Subject: perf symbols: Add bounds checks to elf_read_build_id() note iteration - -From: Arnaldo Carvalho de Melo - -[ Upstream commit acc56d3941fc2997a5a21ea9233a8ac3d87c4f2f ] - -elf_read_build_id() iterates ELF notes using pointer arithmetic -driven by n_namesz and n_descsz from the note headers. Neither -the note header read nor the subsequent name/desc advances are -checked against the section boundary. A malformed ELF file with -oversized note sizes causes out-of-bounds reads past the section -data buffer. - -Add two bounds checks: verify the note header fits within the -remaining section data, and verify that namesz + descsz (after -alignment) fits before advancing the pointer. - -Fixes: fd7a346ea292074e ("perf symbols: Filename__read_build_id should look at .notes section too") -Reported-by: sashiko-bot -Cc: Namhyung Kim -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/symbol-elf.c | 18 ++++++++++++++++-- - 1 file changed, 16 insertions(+), 2 deletions(-) - -diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c -index 029a1ef9b287b4..90f11a6eb19fea 100644 ---- a/tools/perf/util/symbol-elf.c -+++ b/tools/perf/util/symbol-elf.c -@@ -909,10 +909,24 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) - ptr = data->d_buf; - while (ptr < (data->d_buf + data->d_size)) { - GElf_Nhdr *nhdr = ptr; -- size_t namesz = NOTE_ALIGN(nhdr->n_namesz), -- descsz = NOTE_ALIGN(nhdr->n_descsz); -+ size_t namesz, descsz, remaining; - const char *name; - -+ /* ensure the note header fits within the section */ -+ if (ptr + sizeof(*nhdr) > data->d_buf + data->d_size) -+ break; -+ -+ namesz = NOTE_ALIGN(nhdr->n_namesz); -+ descsz = NOTE_ALIGN(nhdr->n_descsz); -+ -+ /* validate individually to avoid size_t overflow on 32-bit */ -+ remaining = data->d_buf + data->d_size - ptr - sizeof(*nhdr); -+ if (namesz > remaining || descsz > remaining - namesz) { -+ pr_warning("%s: oversized note: n_namesz=%u, n_descsz=%u\n", -+ __func__, nhdr->n_namesz, nhdr->n_descsz); -+ break; -+ } -+ - ptr += sizeof(*nhdr); - name = ptr; - ptr += namesz; --- -2.53.0 - diff --git a/queue-6.12/perf-symbols-add-bounds-checks-to-read_build_id-note.patch b/queue-6.12/perf-symbols-add-bounds-checks-to-read_build_id-note.patch deleted file mode 100644 index aed9fce7fe..0000000000 --- a/queue-6.12/perf-symbols-add-bounds-checks-to-read_build_id-note.patch +++ /dev/null @@ -1,67 +0,0 @@ -From 387e0c7eed13e14603281c3399a3cc768a2ebf81 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Wed, 10 Jun 2026 16:09:45 -0300 -Subject: perf symbols: Add bounds checks to read_build_id() note iteration in - minimal build - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 52e582e316c48c53bb3082c29f7862ebc554087e ] - -symbol-minimal.c's read_build_id() iterates ELF notes with the same -pattern as symbol-elf.c's elf_read_build_id(): pointer arithmetic -driven by n_namesz and n_descsz from 32-bit note header fields, -without validating that the name and desc fit within the note section -data. A malformed ELF file with oversized note sizes causes -out-of-bounds reads past the section data buffer. - -Add the same bounds check as the libelf path: validate namesz and -descsz individually against remaining data before advancing the -pointer, avoiding size_t overflow on 32-bit. - -Fixes: b691f64360ecec49 ("perf symbols: Implement poor man's ELF parser") -Reported-by: sashiko-bot -Cc: Namhyung Kim -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/symbol-minimal.c | 11 ++++++++++- - 1 file changed, 10 insertions(+), 1 deletion(-) - -diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c -index f114f75ebeb989..12ef02bbf2eea8 100644 ---- a/tools/perf/util/symbol-minimal.c -+++ b/tools/perf/util/symbol-minimal.c -@@ -1,3 +1,4 @@ -+#include "debug.h" - #include "dso.h" - #include "symbol.h" - #include "symsrc.h" -@@ -44,7 +45,7 @@ static int read_build_id(void *note_data, size_t note_len, struct build_id *bid, - ptr = note_data; - while (ptr < (note_data + note_len)) { - const char *name; -- size_t namesz, descsz; -+ size_t namesz, descsz, remaining; - - nhdr = ptr; - if (need_swap) { -@@ -56,6 +57,14 @@ static int read_build_id(void *note_data, size_t note_len, struct build_id *bid, - namesz = NOTE_ALIGN(nhdr->n_namesz); - descsz = NOTE_ALIGN(nhdr->n_descsz); - -+ /* validate individually to avoid size_t overflow on 32-bit */ -+ remaining = note_data + note_len - ptr - sizeof(*nhdr); -+ if (namesz > remaining || descsz > remaining - namesz) { -+ pr_warning("%s: oversized note: n_namesz=%u, n_descsz=%u\n", -+ __func__, nhdr->n_namesz, nhdr->n_descsz); -+ break; -+ } -+ - ptr += sizeof(*nhdr); - name = ptr; - ptr += namesz; --- -2.53.0 - diff --git a/queue-6.12/perf-symbols-bounds-check-.gnu_debuglink-section-dat.patch b/queue-6.12/perf-symbols-bounds-check-.gnu_debuglink-section-dat.patch deleted file mode 100644 index be137f2f1a..0000000000 --- a/queue-6.12/perf-symbols-bounds-check-.gnu_debuglink-section-dat.patch +++ /dev/null @@ -1,59 +0,0 @@ -From d89e7b0b17c6423161131b145e615a92dedeaa1b Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sun, 7 Jun 2026 21:05:15 -0300 -Subject: perf symbols: Bounds-check .gnu_debuglink section data - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 9c74f0aab398cb32ab250401f323c0fdc9a3a496 ] - -filename__read_debuglink() copies .gnu_debuglink section data into a -caller-provided buffer via: - - strncpy(debuglink, data->d_buf, size); - -where size is PATH_MAX. If the ELF section is smaller than size and -lacks a null terminator, strncpy reads past data->d_buf into adjacent -memory. A malformed ELF file can trigger this, potentially causing a -segfault or leaking heap data. - -Additionally, strncpy does not guarantee null termination when the -source fills the buffer. - -Replace with an explicit memcpy bounded by both the output buffer -size and the actual section data size (data->d_size), followed by -explicit null termination. - -Fixes: e5a1845fc0aeca85 ("perf symbols: Split out util/symbol-elf.c") -Reported-by: sashiko-bot -Cc: Namhyung Kim -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/symbol-elf.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c -index 3968645e1dfa08..923e7d1d8aad8a 100644 ---- a/tools/perf/util/symbol-elf.c -+++ b/tools/perf/util/symbol-elf.c -@@ -1160,7 +1160,14 @@ int filename__read_debuglink(const char *filename, char *debuglink, - goto out_elf_end; - - /* the start of this section is a zero-terminated string */ -- strncpy(debuglink, data->d_buf, size); -+ if (data->d_size > 0) { -+ size_t len = min(size - 1, data->d_size); -+ -+ memcpy(debuglink, data->d_buf, len); -+ debuglink[len] = '\0'; -+ } else { -+ debuglink[0] = '\0'; -+ } - - err = 0; - --- -2.53.0 - diff --git a/queue-6.12/perf-symbols-bounds-check-descsz-in-sysfs__read_buil.patch b/queue-6.12/perf-symbols-bounds-check-descsz-in-sysfs__read_buil.patch deleted file mode 100644 index e8906b3ea1..0000000000 --- a/queue-6.12/perf-symbols-bounds-check-descsz-in-sysfs__read_buil.patch +++ /dev/null @@ -1,57 +0,0 @@ -From f9a65684f7758d8381ec0b0f974618a7020a293b Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sun, 7 Jun 2026 22:40:20 -0300 -Subject: perf symbols: Bounds-check descsz in sysfs__read_build_id() GNU - fallback - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 1b4e9fbdeabc549965e70ac0cd8095d57ff6df06 ] - -When sysfs__read_build_id() matches NT_GNU_BUILD_ID with the right -namesz but the name content is not "GNU", it falls back to reading -descsz bytes into the stack buffer bf[BUFSIZ]: - - } else if (read(fd, bf, descsz) != (ssize_t)descsz) - -Unlike the else branch which validates namesz + descsz against -sizeof(bf), this path passes descsz directly to read() without any -bounds check. A crafted sysfs file with a large n_descsz overflows -the 8192-byte stack buffer. - -Add a descsz > sizeof(bf) check before the read, breaking out of -the loop on oversized values. - -Fixes: e5a1845fc0aeca85 ("perf symbols: Split out util/symbol-elf.c") -Reported-by: sashiko-bot -Cc: Namhyung Kim -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/symbol-elf.c | 9 +++++++-- - 1 file changed, 7 insertions(+), 2 deletions(-) - -diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c -index 923e7d1d8aad8a..c7fcbb8fb644c3 100644 ---- a/tools/perf/util/symbol-elf.c -+++ b/tools/perf/util/symbol-elf.c -@@ -1060,8 +1060,13 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) - err = 0; - break; - } -- } else if (read(fd, bf, descsz) != (ssize_t)descsz) -- break; -+ } else { -+ /* descsz from untrusted file — clamp to buffer */ -+ if (descsz > sizeof(bf)) -+ break; -+ if (read(fd, bf, descsz) != (ssize_t)descsz) -+ break; -+ } - } else { - size_t n; - --- -2.53.0 - diff --git a/queue-6.12/perf-symbols-break-infinite-loop-on-zero-filled-note.patch b/queue-6.12/perf-symbols-break-infinite-loop-on-zero-filled-note.patch deleted file mode 100644 index 8f3e25465b..0000000000 --- a/queue-6.12/perf-symbols-break-infinite-loop-on-zero-filled-note.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 45ed275d8a4e761c4f0a446da25672469f952d5a Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Wed, 10 Jun 2026 19:32:22 -0300 -Subject: perf symbols: Break infinite loop on zero-filled notes in - sysfs__read_build_id() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 063c647b24f640657d6d9e2e90d620ea3ee19ae6 ] - -sysfs__read_build_id() iterates ELF note headers from sysfs files in a -while(1) loop. If the file contains a zero-filled note header (both -n_namesz and n_descsz are 0), the code computes n = namesz + descsz = 0 -and calls read(fd, bf, 0). read() with count 0 returns 0, which -matches the expected (ssize_t)n value, so the error check passes and -the loop repeats — reading the same zero bytes and spinning forever. - -This can happen with corrupted or zero-padded sysfs pseudo-files. - -Add a check for n == 0 before the read, since no valid ELF note has -both name and description of zero length. - -Reported-by: sashiko-bot -Fixes: f1617b40596cb341 ("perf symbols: Record the build_ids of kernel modules too") -Reviewed-by: Ian Rogers -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/symbol-elf.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c -index 4ba49975970a9b..bb3c1b68fe3706 100644 ---- a/tools/perf/util/symbol-elf.c -+++ b/tools/perf/util/symbol-elf.c -@@ -1092,6 +1092,9 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) - } else { - n = namesz + descsz; - } -+ /* no valid note has both namesz and descsz zero */ -+ if (n == 0) -+ break; - if (read(fd, bf, n) != (ssize_t)n) - break; - } --- -2.53.0 - diff --git a/queue-6.12/perf-symbols-fix-bswap-copy-paste-error-for-32-bit-e.patch b/queue-6.12/perf-symbols-fix-bswap-copy-paste-error-for-32-bit-e.patch deleted file mode 100644 index bba07d42f5..0000000000 --- a/queue-6.12/perf-symbols-fix-bswap-copy-paste-error-for-32-bit-e.patch +++ /dev/null @@ -1,51 +0,0 @@ -From e1c9337037614d1a0761570fec1b31035874a2c9 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Wed, 10 Jun 2026 19:28:43 -0300 -Subject: perf symbols: Fix bswap copy-paste error for 32-bit ELF p_filesz - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 081b387c7397498c583b1ba7c2fdaf4c6da6b538 ] - -filename__read_build_id() byte-swaps 32-bit ELF program headers on -cross-endian files, but line 178 passes p_offset to bswap_32() instead -of p_filesz: - - hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_offset); - -This clobbers p_filesz with the already-swapped p_offset value. The -64-bit path on line 182 is correct and swaps p_filesz from p_filesz. - -The consequence is that the PT_NOTE segment read uses the wrong size, -which can cause either a short read (missing the build-id) or an -oversized read (reading past the segment into adjacent data). - -Fix by swapping the correct field. - -Reported-by: sashiko-bot -Fixes: fef8f648bb47726d ("perf symbol: Fix use-after-free in filename__read_build_id") -Reviewed-by: Ian Rogers -Cc: Ian Rogers -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/symbol-minimal.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c -index 12ef02bbf2eea8..68f5a2622bd8bc 100644 ---- a/tools/perf/util/symbol-minimal.c -+++ b/tools/perf/util/symbol-minimal.c -@@ -168,7 +168,7 @@ int filename__read_build_id(const char *filename, struct build_id *bid) - if (elf32) { - hdrs.phdr32[i].p_type = bswap_32(hdrs.phdr32[i].p_type); - hdrs.phdr32[i].p_offset = bswap_32(hdrs.phdr32[i].p_offset); -- hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_offset); -+ hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_filesz); - } else { - hdrs.phdr64[i].p_type = bswap_32(hdrs.phdr64[i].p_type); - hdrs.phdr64[i].p_offset = bswap_64(hdrs.phdr64[i].p_offset); --- -2.53.0 - diff --git a/queue-6.12/perf-symbols-fix-signed-overflow-in-sysfs__read_buil.patch b/queue-6.12/perf-symbols-fix-signed-overflow-in-sysfs__read_buil.patch deleted file mode 100644 index c1b2c6312f..0000000000 --- a/queue-6.12/perf-symbols-fix-signed-overflow-in-sysfs__read_buil.patch +++ /dev/null @@ -1,72 +0,0 @@ -From 2a6cdac63f01f3a6bce30d35b78eb1e730353523 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sun, 7 Jun 2026 21:04:50 -0300 -Subject: perf symbols: Fix signed overflow in sysfs__read_build_id() size - check -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 6eaa8ee3e2abe5112e80e94c27196bb175689469 ] - -sysfs__read_build_id() reads ELF note headers from sysfs files. The -note's namesz and descsz fields are used to compute the skip size: - - int n = namesz + descsz; - if (n > (int)sizeof(bf)) - -Both namesz and descsz are size_t from NOTE_ALIGN() of 32-bit note -header fields. Their sum can exceed INT_MAX, overflowing the signed -int n to a negative value. The check n > sizeof(bf) then evaluates -false (negative < positive in signed comparison), and read(fd, bf, n) -reinterprets the negative n as a huge size_t count — the kernel writes -up to MAX_RW_COUNT bytes into the 8192-byte stack buffer. - -In practice the overflow is bounded by the sysfs file's actual size, -so a real sysfs notes file won't trigger it organically. But crafted -input (e.g. via a mounted debugfs/sysfs image) could. - -Fix by validating namesz and descsz individually against the buffer -size before summing, and change n to size_t to avoid the signed -overflow entirely. - -Fixes: f1617b40596cb341 ("perf symbols: Record the build_ids of kernel modules too") -Reported-by: sashiko-bot -Cc: Namhyung Kim -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/symbol-elf.c | 9 ++++++--- - 1 file changed, 6 insertions(+), 3 deletions(-) - -diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c -index 61f931997f2ecf..3968645e1dfa08 100644 ---- a/tools/perf/util/symbol-elf.c -+++ b/tools/perf/util/symbol-elf.c -@@ -1063,14 +1063,17 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) - } else if (read(fd, bf, descsz) != (ssize_t)descsz) - break; - } else { -- int n = namesz + descsz; -+ size_t n; - -- if (n > (int)sizeof(bf)) { -+ /* int sum of namesz+descsz can overflow negative, bypassing size check */ -+ if (namesz > sizeof(bf) || descsz > sizeof(bf) - namesz) { - n = sizeof(bf); - pr_debug("%s: truncating reading of build id in sysfs file %s: n_namesz=%u, n_descsz=%u.\n", - __func__, filename, nhdr.n_namesz, nhdr.n_descsz); -+ } else { -+ n = namesz + descsz; - } -- if (read(fd, bf, n) != n) -+ if (read(fd, bf, n) != (ssize_t)n) - break; - } - } --- -2.53.0 - diff --git a/queue-6.12/perf-symbols-validate-p_filesz-before-use-in-filenam.patch b/queue-6.12/perf-symbols-validate-p_filesz-before-use-in-filenam.patch deleted file mode 100644 index 5ef4e506d3..0000000000 --- a/queue-6.12/perf-symbols-validate-p_filesz-before-use-in-filenam.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 43fa6a64625d86439ef972c35b5f664d9082158e Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Wed, 10 Jun 2026 19:29:34 -0300 -Subject: perf symbols: Validate p_filesz before use in - filename__read_build_id() - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 2a3716544359d4312c81b0fa909a13301186da17 ] - -filename__read_build_id() stores ELF p_filesz in a ssize_t variable. -A crafted 32-bit ELF with p_filesz = 0xFFFFFFFF produces ssize_t value --1. The comparison `p_filesz > buf_size` evaluates false because signed --1 is less than any non-negative buf_size, so the realloc is skipped and -buf remains NULL. - -The subsequent read(fd, NULL, -1) returns -1, which equals p_filesz, -passing the error check. read_build_id() then dereferences the NULL -buffer. - -Add an explicit check for p_filesz <= 0 before using the value, -catching both zero-length and sign-wrapped negative sizes from crafted -ELF files. - -Reported-by: sashiko-bot -Fixes: ba0b7081f7a521d7 ("perf symbol-minimal: Fix ehdr reading in filename__read_build_id") -Reviewed-by: Ian Rogers -Cc: Ian Rogers -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/symbol-minimal.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c -index 68f5a2622bd8bc..f6bef8fcc75f97 100644 ---- a/tools/perf/util/symbol-minimal.c -+++ b/tools/perf/util/symbol-minimal.c -@@ -179,6 +179,9 @@ int filename__read_build_id(const char *filename, struct build_id *bid) - continue; - - p_filesz = elf32 ? hdrs.phdr32[i].p_filesz : hdrs.phdr64[i].p_filesz; -+ /* ssize_t can go negative with crafted ELF p_filesz values */ -+ if (p_filesz <= 0) -+ continue; - if (p_filesz > buf_size) { - void *tmp; - --- -2.53.0 - diff --git a/queue-6.12/perf-synthetic-events-fix-stale-build-id-in-module-mmap2-records.patch b/queue-6.12/perf-synthetic-events-fix-stale-build-id-in-module-mmap2-records.patch deleted file mode 100644 index ba4a63f43d..0000000000 --- a/queue-6.12/perf-synthetic-events-fix-stale-build-id-in-module-mmap2-records.patch +++ /dev/null @@ -1,74 +0,0 @@ -From 35b16a7a2c4fc458304447128b86514ce9f70f3c Mon Sep 17 00:00:00 2001 -From: Chuck Lever -Date: Tue, 10 Mar 2026 13:59:51 -0400 -Subject: perf synthetic-events: Fix stale build ID in module MMAP2 records - -From: Chuck Lever - -commit 35b16a7a2c4fc458304447128b86514ce9f70f3c upstream. - -perf_event__synthesize_modules() allocates a single union perf_event and -reuses it across every kernel module callback. - -After the first module is processed, perf_record_mmap2__read_build_id() -sets PERF_RECORD_MISC_MMAP_BUILD_ID in header.misc and writes that -module's build ID into the event. - -On subsequent iterations the callback overwrites start, len, pid, and -filename for the next module but never clears the stale build ID fields -or the MMAP_BUILD_ID flag. - -When perf_record_mmap2__read_build_id() runs for the second module it -sees the flag, reads the stale build ID into a dso_id, and -__dso__improve_id() permanently poisons the DSO with the wrong build ID. - -Every module after the first therefore receives the first module's build -ID in its MMAP2 record. - -On a system with the sunrpc and nfsd modules loaded, this causes perf -script and perf report to show [unknown] for all module symbols. - -The latent bug has existed since commit d9f2ecbc5e47fca7 ("perf dso: -Move build_id to dso_id") introduced the PERF_RECORD_MISC_MMAP_BUILD_ID -check in perf_record_mmap2__read_build_id(). - -Commit 53b00ff358dc75b1 ("perf record: Make --buildid-mmap the default") -then exposed it to all users by making the MMAP2-with-build-ID path the -default. Both commits were merged in the same series. - -Clear the MMAP_BUILD_ID flag and zero the build_id union before each -call to perf_record_mmap2__read_build_id() so that every module starts -with a clean slate. - -Fixes: d9f2ecbc5e47fca7 ("perf dso: Move build_id to dso_id") -Reviewed-by: Ian Rogers -Signed-off-by: Chuck Lever -Cc: Adrian Hunter -Cc: Alexander Shishkin -Cc: Ian Rogers -Cc: Ingo Molnar -Cc: James Clark -Cc: Jiri Olsa -Cc: Mark Rutland -Cc: Namhyung Kim -Cc: Peter Zijlstra -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Greg Kroah-Hartman ---- - tools/perf/util/synthetic-events.c | 5 +++++ - 1 file changed, 5 insertions(+) - ---- a/tools/perf/util/synthetic-events.c -+++ b/tools/perf/util/synthetic-events.c -@@ -702,6 +702,11 @@ static int perf_event__synthesize_module - - memcpy(event->mmap2.filename, dso__long_name(dso), dso__long_name_len(dso) + 1); - -+ /* Clear stale build ID from previous module iteration */ -+ event->mmap2.header.misc &= ~PERF_RECORD_MISC_MMAP_BUILD_ID; -+ memset(event->mmap2.build_id, 0, sizeof(event->mmap2.build_id)); -+ event->mmap2.build_id_size = 0; -+ - perf_record_mmap2__read_build_id(&event->mmap2, args->machine, false); - } else { - size = PERF_ALIGN(dso__long_name_len(dso) + 1, sizeof(u64)); diff --git a/queue-6.12/perf-timechart-fix-cpu2y-oob-read-on-untrusted-cpu-i.patch b/queue-6.12/perf-timechart-fix-cpu2y-oob-read-on-untrusted-cpu-i.patch deleted file mode 100644 index 39e0ffdbf3..0000000000 --- a/queue-6.12/perf-timechart-fix-cpu2y-oob-read-on-untrusted-cpu-i.patch +++ /dev/null @@ -1,71 +0,0 @@ -From e04782c639cb25bbc4a3a833c7d1b70f8d5d728e Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 6 Jun 2026 11:21:32 -0300 -Subject: perf timechart: Fix cpu2y() OOB read on untrusted CPU index - -From: Arnaldo Carvalho de Melo - -[ Upstream commit e2496db45bfd8dfb6154ec415798fee330f1cc0a ] - -cpu2y() indexes topology_map[cpu] without bounds checking. The array -is allocated with nr_cpus entries (from env->nr_cpus_online), but -callers pass sample CPU values from perf.data which can exceed that -size with cross-machine recordings. - -Track the topology_map allocation size and bounds-check the CPU -argument in cpu2y() before indexing. Out-of-bounds CPUs fall back -to the identity mapping (cpu2slot(cpu)), which is the same behavior -as when no topology is available. - -Fixes: c507999790438cde ("perf timechart: Add support for topology") -Reported-by: sashiko-bot -Cc: Stanislav Fomichev -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/svghelper.c | 10 ++++++---- - 1 file changed, 6 insertions(+), 4 deletions(-) - -diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c -index 2b04f47f4db0f0..d553a40f2c6add 100644 ---- a/tools/perf/util/svghelper.c -+++ b/tools/perf/util/svghelper.c -@@ -46,13 +46,13 @@ static double cpu2slot(int cpu) - } - - static int *topology_map; -+static int topology_map_size; - - static double cpu2y(int cpu) - { -- if (topology_map) -+ if (topology_map && cpu >= 0 && cpu < topology_map_size) - return cpu2slot(topology_map[cpu]) * SLOT_MULT; -- else -- return cpu2slot(cpu) * SLOT_MULT; -+ return cpu2slot(cpu) * SLOT_MULT; - } - - static double time2pixels(u64 __time) -@@ -734,7 +734,8 @@ static int str_to_bitmap(char *s, cpumask_t *b, int nr_cpus) - return -1; - - perf_cpu_map__for_each_cpu(cpu, idx, map) { -- if (cpu.cpu >= nr_cpus) { -+ /* perf_cpu_map__new("") returns cpu.cpu == -1 */ -+ if (cpu.cpu < 0 || cpu.cpu >= nr_cpus) { - ret = -1; - break; - } -@@ -792,6 +793,7 @@ int svg_build_topology_map(struct perf_env *env) - fprintf(stderr, "topology: no memory\n"); - goto exit; - } -+ topology_map_size = nr_cpus; - - for (i = 0; i < nr_cpus; i++) - topology_map[i] = -1; --- -2.53.0 - diff --git a/queue-6.12/perf-tools-add-bounds-check-to-cpu__get_node.patch b/queue-6.12/perf-tools-add-bounds-check-to-cpu__get_node.patch deleted file mode 100644 index 56aa3e4119..0000000000 --- a/queue-6.12/perf-tools-add-bounds-check-to-cpu__get_node.patch +++ /dev/null @@ -1,47 +0,0 @@ -From ee957ddaa2bc90819907eb28fef12305728eff63 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Thu, 4 Jun 2026 18:14:23 -0300 -Subject: perf tools: Add bounds check to cpu__get_node() - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 1e7921d7227de5da0dfc167943092c823ec7e49b ] - -cpu__get_node() accesses cpunode_map[cpu.cpu] without checking against -max_cpu_num, the allocation size of cpunode_map. Callers such as -builtin-kmem.c:evsel__process_alloc_event() pass sample->cpu from -perf.data events, which may exceed the host's CPU count when analyzing -cross-machine recordings. - -Add a bounds check against max_cpu_num before indexing, returning -1 -for out-of-range values. This is a central fix that protects all -callers. - -Fixes: 86895b480a2f ("perf stat: Add --per-node agregation support") -Reported-by: sashiko-bot -Cc: Jiri Olsa -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/cpumap.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c -index 27094211edd8a8..7cafa6672d4fb4 100644 ---- a/tools/perf/util/cpumap.c -+++ b/tools/perf/util/cpumap.c -@@ -522,6 +522,10 @@ int cpu__get_node(struct perf_cpu cpu) - return -1; - } - -+ /* cpunode_map allocated for max_cpu_num entries; input may be untrusted */ -+ if (cpu.cpu < 0 || cpu.cpu >= max_cpu_num.cpu) -+ return -1; -+ - return cpunode_map[cpu.cpu]; - } - --- -2.53.0 - diff --git a/queue-6.12/perf-tools-fix-get_max_num-size_t-underflow-on-empty.patch b/queue-6.12/perf-tools-fix-get_max_num-size_t-underflow-on-empty.patch deleted file mode 100644 index 098c96e6db..0000000000 --- a/queue-6.12/perf-tools-fix-get_max_num-size_t-underflow-on-empty.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 866c203d6775ec390b55d33780deaadb0efd0c36 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 6 Jun 2026 20:37:52 -0300 -Subject: perf tools: Fix get_max_num() size_t underflow on empty sysfs file - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 0a012113bb3a44482c163f16f4db03ccaa37a339 ] - -get_max_num() reads a sysfs file (cpu/possible, cpu/present, or -node/possible) and scans backward from the end to find the last -number. If the file is empty, filename__read_str() returns num == 0. -The loop `while (--num)` decrements the size_t from 0 to SIZE_MAX, -reading backward across the heap until a comma or hyphen is found -or unmapped memory is hit. - -Add an early return for empty files before the backward scan. - -Fixes: 7780c25bae59fd04 ("perf tools: Allow ability to map cpus to nodes easily") -Reported-by: sashiko-bot -Reviewed-by: Ian Rogers -Cc: Don Zickus -Cc: Ian Rogers -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/cpumap.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c -index 7cafa6672d4fb4..388755a4eda2f2 100644 ---- a/tools/perf/util/cpumap.c -+++ b/tools/perf/util/cpumap.c -@@ -402,6 +402,12 @@ static int get_max_num(char *path, int *max) - - buf[num] = '\0'; - -+ /* empty file — nothing to parse */ -+ if (num == 0) { -+ err = -1; -+ goto out; -+ } -+ - /* start on the right, to find highest node num */ - while (--num) { - if ((buf[num] == ',') || (buf[num] == '-')) { --- -2.53.0 - diff --git a/queue-6.12/perf-tools-fix-thread__set_comm_from_proc-on-empty-c.patch b/queue-6.12/perf-tools-fix-thread__set_comm_from_proc-on-empty-c.patch deleted file mode 100644 index 6d1e2ae900..0000000000 --- a/queue-6.12/perf-tools-fix-thread__set_comm_from_proc-on-empty-c.patch +++ /dev/null @@ -1,55 +0,0 @@ -From d023b81607362e43e1b5a79f05cad140a08029f7 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sun, 7 Jun 2026 22:37:55 -0300 -Subject: perf tools: Fix thread__set_comm_from_proc() on empty comm file -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 31d596054550f793508abe7dd593853ece47d428 ] - -thread__set_comm_from_proc() calls procfs__read_str() then strips -the trailing newline via comm[sz - 1] = '\0'. procfs__read_str() -allocates the buffer before reading, so on an empty /proc/pid/comm -(reachable during late exit teardown) it returns success with sz = 0 -and an unterminated heap buffer. - -The sz - 1 underflow was the original sashiko finding: it writes a -null byte before the allocation. But even with a sz > 0 guard on -the newline strip, the unterminated buffer would still be passed to -thread__set_comm() which calls strlen() — an unbounded heap read. - -Fix by treating sz == 0 as failure: free the buffer and return -1. -This is consistent with pmu.c's perf_pmu__parse_scale/unit which -already treat len == 0 from filename__read_str as an error. - -Fixes: 2f3027ac28bf6bc3 ("perf thread: Introduce method to set comm from /proc/pid/self") -Reported-by: sashiko-bot -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/thread.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c -index 309d573eac9a94..64f29370d59962 100644 ---- a/tools/perf/util/thread.c -+++ b/tools/perf/util/thread.c -@@ -273,6 +273,11 @@ int thread__set_comm_from_proc(struct thread *thread) - if (!(snprintf(path, sizeof(path), "%d/task/%d/comm", - thread__pid(thread), thread__tid(thread)) >= (int)sizeof(path)) && - procfs__read_str(path, &comm, &sz) == 0) { -+ /* sz==0: read got nothing, e.g. race during exit teardown */ -+ if (sz == 0) { -+ free(comm); -+ return -1; -+ } - comm[sz - 1] = '\0'; - err = thread__set_comm(thread, comm, 0); - } --- -2.53.0 - diff --git a/queue-6.12/perf-tools-guard-test_bit-from-out-of-bounds-sample-.patch b/queue-6.12/perf-tools-guard-test_bit-from-out-of-bounds-sample-.patch deleted file mode 100644 index fb999cb60e..0000000000 --- a/queue-6.12/perf-tools-guard-test_bit-from-out-of-bounds-sample-.patch +++ /dev/null @@ -1,102 +0,0 @@ -From af74646eb6980580abfe8fcabf8f83c6102d4225 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Thu, 4 Jun 2026 12:55:06 -0300 -Subject: perf tools: Guard test_bit from out-of-bounds sample CPU - -From: Arnaldo Carvalho de Melo - -[ Upstream commit a5498ccf8079fc91c938f122ff9697b0c526b2fd ] - -When PERF_SAMPLE_CPU is absent from a perf.data file, sample->cpu is -initialized to (u32)-1 by evsel__parse_sample(). Five call sites pass -this value directly to test_bit(sample->cpu, cpu_bitmap), reading -massively out of bounds past the DECLARE_BITMAP(..., MAX_NR_CPUS) -allocation of 4096 bits. - -Add a sample->cpu >= MAX_NR_CPUS guard before each test_bit() call, -matching the existing safe pattern in builtin-kwork.c. This catches -both the (u32)-1 sentinel and any corrupted CPU value exceeding the -bitmap size. - -Fixes: 5d67be97f890 ("perf report/annotate/script: Add option to specify a CPU range") -Cc: Anton Blanchard -Reported-by: sashiko-bot -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/builtin-annotate.c | 3 ++- - tools/perf/builtin-diff.c | 3 ++- - tools/perf/builtin-report.c | 3 ++- - tools/perf/builtin-sched.c | 6 ++++-- - 4 files changed, 10 insertions(+), 5 deletions(-) - -diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c -index 3dc6197ef3fa6d..3882f400065c7c 100644 ---- a/tools/perf/builtin-annotate.c -+++ b/tools/perf/builtin-annotate.c -@@ -298,7 +298,8 @@ static int process_sample_event(const struct perf_tool *tool, - goto out_put; - } - -- if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap)) -+ if (ann->cpu_list && (sample->cpu >= MAX_NR_CPUS || -+ !test_bit(sample->cpu, ann->cpu_bitmap))) - goto out_put; - - if (!al.filtered && -diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c -index 82fb7773e03e62..bf5611e1f5e006 100644 ---- a/tools/perf/builtin-diff.c -+++ b/tools/perf/builtin-diff.c -@@ -417,7 +417,8 @@ static int diff__process_sample_event(const struct perf_tool *tool, - goto out; - } - -- if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) { -+ if (cpu_list && (sample->cpu >= MAX_NR_CPUS || -+ !test_bit(sample->cpu, cpu_bitmap))) { - ret = 0; - goto out; - } -diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c -index 8700c39680662a..86cbca6285a51a 100644 ---- a/tools/perf/builtin-report.c -+++ b/tools/perf/builtin-report.c -@@ -301,7 +301,8 @@ static int process_sample_event(const struct perf_tool *tool, - if (symbol_conf.hide_unresolved && al.sym == NULL) - goto out_put; - -- if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) -+ if (rep->cpu_list && (sample->cpu >= MAX_NR_CPUS || -+ !test_bit(sample->cpu, rep->cpu_bitmap))) - goto out_put; - - if (sort__mode == SORT_MODE__BRANCH) { -diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c -index fa5f00d47e6277..6d7106fb1c0cbc 100644 ---- a/tools/perf/builtin-sched.c -+++ b/tools/perf/builtin-sched.c -@@ -2185,7 +2185,8 @@ static void timehist_print_sample(struct perf_sched *sched, - char nstr[30]; - u64 wait_time; - -- if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) -+ if (cpu_list && (sample->cpu >= MAX_NR_CPUS || -+ !test_bit(sample->cpu, cpu_bitmap))) - return; - - timestamp__scnprintf_usec(t, tstr, sizeof(tstr)); -@@ -2823,7 +2824,8 @@ static int timehist_sched_change_event(const struct perf_tool *tool, - } - - if (!sched->idle_hist || thread__tid(thread) == 0) { -- if (!cpu_list || test_bit(sample->cpu, cpu_bitmap)) -+ if (!cpu_list || (sample->cpu < MAX_NR_CPUS && -+ test_bit(sample->cpu, cpu_bitmap))) - timehist_update_runtime_stats(tr, t, tprev); - - if (sched->idle_hist) { --- -2.53.0 - diff --git a/queue-6.12/perf-tools-use-perf_env__get_cpu_topology-in-machine.patch b/queue-6.12/perf-tools-use-perf_env__get_cpu_topology-in-machine.patch deleted file mode 100644 index 7a76ab150a..0000000000 --- a/queue-6.12/perf-tools-use-perf_env__get_cpu_topology-in-machine.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 6bb248d226457f4db505e74b659d8af2022b8d69 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 6 Jun 2026 20:57:49 -0300 -Subject: perf tools: Use perf_env__get_cpu_topology() in machine__resolve() - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 5484b43a0ec8231c36fba6ead654cb72dbba8b8f ] - -machine__resolve() accesses env->cpu[al->cpu].socket_id after checking -al->cpu >= 0 and env->cpu != NULL, but without validating al->cpu -against env->nr_cpus_avail. Since al->cpu comes from the untrusted -perf.data sample, a crafted file with a large CPU index causes an -out-of-bounds heap read. - -Use perf_env__get_cpu_topology() which validates both NULL and bounds. -Also bounds-check al->cpu before the cast to struct perf_cpu (int16_t): -without this, values like 65536 silently truncate to 0, bypassing the -accessor's internal check and returning CPU 0's topology. - -Fixes: 0c4c4debb0adda4c ("perf tools: Add processor socket info to hist_entry and addr_location") -Reported-by: sashiko-bot -Reviewed-by: Ian Rogers -Cc: Kan Liang -Cc: Ian Rogers -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/event.c | 15 +++++++++++++-- - 1 file changed, 13 insertions(+), 2 deletions(-) - -diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c -index aac96d5d191700..1e5085817e8250 100644 ---- a/tools/perf/util/event.c -+++ b/tools/perf/util/event.c -@@ -12,6 +12,7 @@ - #include - #include "cpumap.h" - #include "dso.h" -+#include "env.h" - #include "event.h" - #include "debug.h" - #include "hist.h" -@@ -763,8 +764,18 @@ int machine__resolve(struct machine *machine, struct addr_location *al, - if (al->cpu >= 0) { - struct perf_env *env = machine->env; - -- if (env && env->cpu) -- al->socket = env->cpu[al->cpu].socket_id; -+ /* -+ * Bounds-check al->cpu (s32) before casting to struct perf_cpu -+ * (int16_t): without this, e.g. 65536 truncates to 0 and silently -+ * returns CPU 0's topology. Can go once perf_cpu.cpu is widened. -+ */ -+ if (env && al->cpu < env->nr_cpus_avail) { -+ struct cpu_topology_map *topo; -+ -+ topo = perf_env__get_cpu_topology(env, (struct perf_cpu){ al->cpu }); -+ if (topo) -+ al->socket = topo->socket_id; -+ } - } - - if (al->map) { --- -2.53.0 - diff --git a/queue-6.12/perf-tools-use-scnprintf-in-cpu_map__snprint-to-prev.patch b/queue-6.12/perf-tools-use-scnprintf-in-cpu_map__snprint-to-prev.patch deleted file mode 100644 index 13fc5d15f2..0000000000 --- a/queue-6.12/perf-tools-use-scnprintf-in-cpu_map__snprint-to-prev.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 6860af79c10d3ed1e8095d743f4ff35e60cfb829 Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Sat, 6 Jun 2026 20:43:52 -0300 -Subject: perf tools: Use scnprintf() in cpu_map__snprint() to prevent overflow - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 7953a3a9b8e02e98c6e6958f291d0ae22393e46a ] - -cpu_map__snprint() accumulates snprintf() return values in ret. -snprintf() returns the number of characters that *would have been -written* on truncation, not the actual count. When a fragmented CPU -list exceeds the buffer, ret grows past size, causing `size - ret` to -underflow (both are size_t), and subsequent snprintf() calls write -past the end of the caller's stack buffer. - -Switch to scnprintf() which returns the actual number of characters -written, making ret accumulation safe by construction. - -Fixes: a24020e6b7cf6eb8 ("perf tools: Change cpu_map__fprintf output") -Reported-by: sashiko-bot -Reviewed-by: Ian Rogers -Cc: Jiri Olsa -Cc: Ian Rogers -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/cpumap.c | 18 +++++++++--------- - 1 file changed, 9 insertions(+), 9 deletions(-) - -diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c -index 388755a4eda2f2..f6a5daffeeb10e 100644 ---- a/tools/perf/util/cpumap.c -+++ b/tools/perf/util/cpumap.c -@@ -625,21 +625,21 @@ size_t cpu_map__snprint(struct perf_cpu_map *map, char *buf, size_t size) - if (start == -1) { - start = i; - if (last) { -- ret += snprintf(buf + ret, size - ret, -- "%s%d", COMMA, -- perf_cpu_map__cpu(map, i).cpu); -+ ret += scnprintf(buf + ret, size - ret, -+ "%s%d", COMMA, -+ perf_cpu_map__cpu(map, i).cpu); - } - } else if (((i - start) != (cpu.cpu - perf_cpu_map__cpu(map, start).cpu)) || last) { - int end = i - 1; - - if (start == end) { -- ret += snprintf(buf + ret, size - ret, -- "%s%d", COMMA, -- perf_cpu_map__cpu(map, start).cpu); -+ ret += scnprintf(buf + ret, size - ret, -+ "%s%d", COMMA, -+ perf_cpu_map__cpu(map, start).cpu); - } else { -- ret += snprintf(buf + ret, size - ret, -- "%s%d-%d", COMMA, -- perf_cpu_map__cpu(map, start).cpu, perf_cpu_map__cpu(map, end).cpu); -+ ret += scnprintf(buf + ret, size - ret, -+ "%s%d-%d", COMMA, -+ perf_cpu_map__cpu(map, start).cpu, perf_cpu_map__cpu(map, end).cpu); - } - first = false; - start = i; --- -2.53.0 - diff --git a/queue-6.12/perf-tools-use-snprintf-for-root_dir-path-constructi.patch b/queue-6.12/perf-tools-use-snprintf-for-root_dir-path-constructi.patch deleted file mode 100644 index 6186ebb6e0..0000000000 --- a/queue-6.12/perf-tools-use-snprintf-for-root_dir-path-constructi.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 5de6778c41c7e0c4eb12f4f80835a6458786ec1e Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Wed, 10 Jun 2026 20:34:38 -0300 -Subject: perf tools: Use snprintf() for root_dir path construction - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 7b0df6f4d498b1608afccfd6dffb264e6da91693 ] - -get_kernel_version() in machine.c and dso__load_guest_kernel_sym() in -symbol.c use sprintf() to construct paths by prepending root_dir to -"/proc/version" and "/proc/kallsyms" respectively. Both write into -PATH_MAX stack buffers, but root_dir comes from --guestmount or KVM -configuration and is not length-checked. A root_dir at or near -PATH_MAX causes a stack buffer overflow. - -Switch to snprintf() with sizeof(path) to prevent overflow. - -Reported-by: sashiko-bot -Fixes: a1645ce12adb6c9c ("perf: 'perf kvm' tool for monitoring guest performance from host") -Cc: Zhang Yanmin -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/machine.c | 2 +- - tools/perf/util/symbol.c | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c -index 557853fcb5479b..71d3d034ba095e 100644 ---- a/tools/perf/util/machine.c -+++ b/tools/perf/util/machine.c -@@ -1294,7 +1294,7 @@ static char *get_kernel_version(const char *root_dir) - char *name, *tmp; - const char *prefix = "Linux version "; - -- sprintf(version, "%s/proc/version", root_dir); -+ snprintf(version, sizeof(version), "%s/proc/version", root_dir); - file = fopen(version, "r"); - if (!file) - return NULL; -diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c -index e792dd22b54983..92b31c1a73a839 100644 ---- a/tools/perf/util/symbol.c -+++ b/tools/perf/util/symbol.c -@@ -2270,7 +2270,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map) - if (!kallsyms_filename) - return -1; - } else { -- sprintf(path, "%s/proc/kallsyms", machine->root_dir); -+ snprintf(path, sizeof(path), "%s/proc/kallsyms", machine->root_dir); - kallsyms_filename = path; - } - --- -2.53.0 - diff --git a/queue-6.12/perf-tools-use-snprintf-in-dso__read_running_kernel_.patch b/queue-6.12/perf-tools-use-snprintf-in-dso__read_running_kernel_.patch deleted file mode 100644 index 7a4f3608fd..0000000000 --- a/queue-6.12/perf-tools-use-snprintf-in-dso__read_running_kernel_.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 40724299dc87fbe1e19146b0004764408ebc6c6c Mon Sep 17 00:00:00 2001 -From: Sasha Levin -Date: Mon, 8 Jun 2026 07:04:25 -0300 -Subject: perf tools: Use snprintf() in dso__read_running_kernel_build_id() - -From: Arnaldo Carvalho de Melo - -[ Upstream commit 2ea64782a428bed74f595961e651ceb8c4c5bf22 ] - -dso__read_running_kernel_build_id() uses sprintf() to format a sysfs -path from machine->root_dir into a PATH_MAX buffer. If root_dir is -close to PATH_MAX in length, appending "/sys/kernel/notes" (18 bytes) -overflows the stack buffer. - -Switch to snprintf() with sizeof(path) to prevent the overflow. - -Reported-by: sashiko-bot -Fixes: cdd059d731eeb466 ("perf tools: Move dso_* related functions into dso object") -Cc: Jiri Olsa -Assisted-by: Claude:claude-opus-4.6 -Signed-off-by: Arnaldo Carvalho de Melo -Signed-off-by: Sasha Levin ---- - tools/perf/util/dso.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c -index 9792c8d38a314b..0c8492ea530fc1 100644 ---- a/tools/perf/util/dso.c -+++ b/tools/perf/util/dso.c -@@ -1676,7 +1676,7 @@ void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) - - if (machine__is_default_guest(machine)) - return; -- sprintf(path, "%s/sys/kernel/notes", machine->root_dir); -+ snprintf(path, sizeof(path), "%s/sys/kernel/notes", machine->root_dir); - sysfs__read_build_id(path, &bid); - dso__set_build_id(dso, &bid); - } --- -2.53.0 - diff --git a/queue-6.12/series b/queue-6.12/series index 00259a3c74..a9b951b34c 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -411,7 +411,6 @@ eventpoll-rename-epi-next-and-txlist-for-clarity.patch eventpoll-fix-epoll_wait-report-false-negative.patch gpiolib-acpi-only-trigger-activeboth-interrupts-on-b.patch staging-nvec-fix-use-after-free-in-nvec_rx_completed.patch -perf-debuginfo-fix-libdw-api-contract-violations.patch coresight-cti-fix-dt-filter-signals-silently-ignored.patch coresight-etm4x-correct-trcvmidcctlr1-save-and-resto.patch pci-aspm-don-t-reconfigure-aspm-entering-low-power-s.patch @@ -419,27 +418,19 @@ pci-introduce-named-defines-for-pci-rom.patch pci-check-rom-header-and-data-structure-addr-before-.patch x86-platform-olpc-xo15-drop-wakeup-source-on-driver-.patch platform-x86-xo15-ebook-fix-wakeup-source-and-gpe-ha.patch -perf-sched-add-missing-mmap2-handler-in-timehist.patch pci-loongson-do-not-ignore-downstream-devices-on-ext.patch rust-alloc-fix-assert-in-vec-reserve-doc-test.patch bus-mhi-ep-fix-potential-deadlock-in-mhi_ep_reset_wo.patch pci-qcom-set-max-opp-before-dbi-access-during-resume.patch phy-phy-can-transceiver-check-driver-match-and-drive.patch -perf-pmu-skip-test-on-arm64-when-slots-is-zero.patch clk-at91-sam9x7-fix-gmac_gclk-clock-definition.patch coresight-fix-source-not-disabled-on-idr_alloc_u32-f.patch mailbox-mtk-adsp-fix-uaf-during-device-teardown.patch -perf-arm-spe-use-metadata-to-decide-the-data-source-.patch -perf-arm-spe-use-arm_spe_op_branch_eret-when-synthes.patch -perf-arm-spe-correctly-set-sample-flags.patch -perf-arm-spe-add-branch-stack.patch -perf-inject-add-convert-callchain-option.patch staging-most-video-avoid-double-free-on-video-regist.patch usb-host-max3421-fix-shift-out-of-bounds-in-max3421_.patch usb-host-max3421-reject-hub-port-requests-for-non-ex.patch char-tlclk-fix-use-after-free-in-tlclk_cleanup.patch pci-qcom-disable-aspm-l0s-for-sa8775p.patch -perf-header-sanity-check-header_event_desc-attr.size.patch iio-light-si1133-reset-counter-to-prevent-race-condi.patch iio-light-si1133-prevent-race-condition-on-timeout.patch iio-magnetometer-ak8975-fix-potential-kernel-stack-m.patch @@ -454,25 +445,6 @@ drm-amd-display-add-missing-kdoc-for-allm-parameters.patch thunderbolt-debugfs-fix-margining-error-counter-buff.patch dmaengine-imx-sdma-refine-spba-bus-searching-in-prob.patch perf-fix-off-by-one-stack-buffer-overflow-in-kallsym.patch -perf-tools-guard-test_bit-from-out-of-bounds-sample-.patch -perf-sched-fix-thread-reference-leak-in-latency_swit.patch -perf-sched-replace-bug_on-on-invalid-cpu-with-gracef.patch -perf-sched-fix-null-dereference-in-latency_runtime_e.patch -perf-tools-add-bounds-check-to-cpu__get_node.patch -perf-sched-fix-register_pid-overflow-strcpy-and-bug_.patch -perf-mmap-guard-cpu__get_node-return-in-aio_bind.patch -perf-stat-bounds-check-cpu-index-in-topology-aggrega.patch -perf-c2c-bounds-check-cpu-and-node-ids-before-bitmap.patch -perf-c2c-bounds-check-cpu-ids-in-setup_nodes-topolog.patch -perf-sched-clean-up-idle_threads-entry-on-init-failu.patch -perf-sched-timehist-add-pre-migration-wait-time-opti.patch -perf-sched-fix-thread-leaks-in-perf-sched-timehist.patch -perf-sched-use-thread__put-in-free_idle_threads.patch -perf-sched-replace-bug_on-and-add-null-checks-in-rep.patch -perf-mmap-fix-null-deref-in-aio-cleanup-on-alloc-fai.patch -perf-stat-introduce-perf_env__get_cpu_topology-to-gu.patch -perf-c2c-fix-use-after-free-in-he__get_c2c_hists-err.patch -perf-timechart-fix-cpu2y-oob-read-on-untrusted-cpu-i.patch dmaengine-qcom-gpi-set-dma_private-capability.patch dmaengine-fix-possible-use-after-free.patch dmaengine-dma-axi-dmac-properly-free-struct-axi_dmac.patch @@ -493,14 +465,7 @@ pci-mediatek-fix-operator-precedence-in-pcie_fts_num.patch pci-meson-propagate-devm_add_action_or_reset-failure.patch pci-meson-add-missing-remove-callback.patch fs-ntfs3-resize-log-one_page_buf-when-adopting-on-di.patch -perf-tools-fix-get_max_num-size_t-underflow-on-empty.patch -perf-tools-use-scnprintf-in-cpu_map__snprint-to-prev.patch -perf-tools-use-perf_env__get_cpu_topology-in-machine.patch pci-rcar-host-remove-unused-list_head-res.patch -perf-sched-bounds-check-prio-before-test_bit-in-time.patch -perf-sched-fix-idle-hist-callchain-display-using-wro.patch -perf-bpf-use-scnprintf-in-snprintf_hex-and-synthesiz.patch -perf-hists-fix-snprintf-in-hists__scnprintf_title-ui.patch xprtrdma-fix-ep-kref-imbalance-on-addr_change.patch xprtrdma-initialize-re_id-before-removal-registratio.patch xprtrdma-check-frwr_wp_create-during-connect.patch @@ -510,25 +475,9 @@ xprtrdma-fix-bcall-rep-leak-and-unbounded-peek.patch xprtrdma-sanitize-the-reply-credit-grant-after-parsi.patch xprtrdma-repost-receive-buffers-for-malformed-replie.patch xprtrdma-return-sendctx-slot-after-send-preparation-.patch -perf-cs-etm-queue-context-packets-for-frontend.patch -perf-pmu-fix-pmu_id-heap-underwrite-on-empty-identif.patch -perf-pmu-fix-perf_pmu__parse_scale-unit-oob-access-o.patch tools-lib-api-fix-missing-null-termination-in-filena.patch -perf-symbols-fix-signed-overflow-in-sysfs__read_buil.patch -perf-symbols-bounds-check-.gnu_debuglink-section-dat.patch -perf-intel-pt-fix-snprintf-size-tracking-bug-in-insn.patch -perf-tools-fix-thread__set_comm_from_proc-on-empty-c.patch -perf-symbols-bounds-check-descsz-in-sysfs__read_buil.patch -perf-dso-use-lock-annotations-to-fix-asan-deadlock.patch -perf-dso-add-support-for-reading-the-e_machine-type-.patch -perf-build-id-change-sprintf-functions-to-snprintf.patch -perf-dso-move-build_id-to-dso_id.patch -perf-tools-use-snprintf-in-dso__read_running_kernel_.patch tools-lib-api-fix-filename__write_int-writing-uninit.patch tools-lib-api-fix-mount_overload-snprintf-truncation.patch -perf-bpf-add-null-check-for-btf__type_by_id-in-synth.patch -perf-symbols-add-bounds-checks-to-elf_read_build_id-.patch -perf-symbols-add-bounds-checks-to-read_build_id-note.patch pci-mediatek-fix-possible-truncation-in-mtk_pcie_par.patch pci-mediatek-use-actual-physical-address-instead-of-.patch revert-pci-msi-unmap-msi-x-region-on-error.patch @@ -547,25 +496,12 @@ i3c-master-make-hot-join-workqueue-freezable-to-bloc.patch i3c-master-prevent-reuse-of-dynamic-address-on-devic.patch apparmor-fix-label-can-not-be-immediately-before-a-d.patch sparc-led-avoid-trimming-a-newline-from-empty-writes.patch -perf-maps-add-maps__mutate_mapping.patch -perf-symbols-fix-bswap-copy-paste-error-for-32-bit-e.patch -perf-symbols-validate-p_filesz-before-use-in-filenam.patch -perf-symbols-break-infinite-loop-on-zero-filled-note.patch -perf-tools-use-snprintf-for-root_dir-path-constructi.patch -perf-bpf-validate-func_info_rec_size-and-sub_id-in-s.patch -perf-bpf-bounds-check-array-offsets-in-bpil_offs_to_.patch -perf-remove-redundant-kernel.h-include.patch -perf-cs-etm-reject-cpu-ids-that-would-overflow-signe.patch gpio-mlxbf3-fail-probe-if-gpiochip-registration-fail.patch drm-i915-clear-crtc-color-blob-pointers-after-droppi.patch spi-dw-fix-wrong-baudr-setting-after-resume.patch xfrm-fix-xfrm-state-cache-insertion-race.patch xfrm-annotate-data-races-around-xfrm_policy_count-an.patch xfrm-validate-selector-family-and-prefixlen-during-m.patch -perf-machine-use-snprintf-for-guestmount-path-constr.patch -perf-cs-etm-validate-num_cpu-before-metadata-allocat.patch -perf-cs-etm-require-full-global-header-in-auxtrace_i.patch -perf-cs-etm-bounds-check-cpu-in-cs_etm__get_queue.patch asoc-tlv320aic3x-restrict-clkdiv-bypass-q-values-in-.patch drm-amdkfd-avoid-double-unpin-of-doorbell-mmio-bos-o.patch drm-amdkfd-fix-list_del-corruption-in-kfd_criu_resum.patch @@ -1245,17 +1181,13 @@ kvm-arm64-fix-propagation-of-tlbi-level-in-kvm_pgtable_stage2_relax_perms.patch bpf-reject-bpf_map_type_inode_storage-creation-if-bpf-lsm-is-uninitialized.patch perf-x86-amd-brs-fix-kernel-address-leakage.patch dibs-loopback-validate-offset-and-size-in-move_data.patch -perf-inject-with-convert-callchain-ignore-the-dummy-event-for-dwarf-stacks.patch seqlock-fix-scoped_seqlock_read-kernel-doc.patch -perf-callchain-handle-multiple-address-spaces.patch ksmbd-fix-durable-reconnect-double-bind-race-in-ksmbd_reopen_durable_fd.patch rtnetlink-make-per-netns-rtnl-dereference-helpers-to-macro.patch -perf-arm-spe-use-old-behavior-when-opening-old-spe-files.patch net-airoha-fix-channel-configuration-for-ets-qdisc.patch jiffies-cast-to-unsigned-long-in-secs_to_jiffies-conversion.patch afs-fix-afs_atcell_get_link-to-check-if-ws_cell-is-unset-first.patch afs-fix-afs_dynroot_readdir-to-not-use-the-rcu-read-lock.patch -perf-libunwind-arm64-fix-missing-close-parens-in-an-if-statement.patch crypto-ccp-fix-__sev_snp_shutdown_locked.patch crypto-ccp-fix-dereferencing-uninitialized-error-pointer.patch crypto-ccp-fix-snp-panic-notifier-unregistration.patch @@ -1263,13 +1195,10 @@ udp_tunnel-fix-deadlock-in-udp_tunnel_nic_set_port_priv.patch bluetooth-hci_core-remove-check-of-bdaddr_any-in-hci_conn_hash_lookup_big_state.patch bluetooth-hci_sync-fix-attempting-to-send-hci_disconnect-to-bis-handle.patch crypto-ccp-always-pass-in-an-error-pointer-to-__sev_platform_shutdown_locked.patch -perf-build-id-ensure-snprintf-string-is-empty-when-size-is-0.patch i40e-drop-udp_tunnel_get_rx_info-call-from-i40e_open.patch ice-drop-udp_tunnel_get_rx_info-call-from-ndo_open.patch crypto-ccp-fix-leaking-the-same-page-twice.patch -perf-synthetic-events-fix-stale-build-id-in-module-mmap2-records.patch bluetooth-l2cap-fix-regressions-caused-by-reusing-ident.patch bluetooth-6lowpan-fix-using-chan-conn-as-indication-to-no-remote-netdev.patch bluetooth-l2cap-fix-tx-ident-leak-for-commands-without-a-response.patch dpll-fix-null-pointer-dereference-in-dpll_msg_add_pin_ref_sync.patch -perf-build-id-fix-off-by-one-bug-when-printing-kernel-module-build-id.patch