From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 46F3A31F98C; Tue, 16 Jun 2026 01:09:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781572142; cv=none; b=SwB2qZHPvL020xlwIkpD5+Rfj9Ivl5jB2gZVvnlfNooDUGYmMsaxOsJYv+H3BaenVl65vJtVqo9r+6Qy7SG7N7Ro0lDLCFTEfbkwGvnxG+Vpnq4t4IP2Pudf59a0zkUlIv3V53xqVQvNu65f3hKYoAsKCH3JCxYUjb8laqw53TM= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781572142; c=relaxed/simple; bh=dtzYcHZgG0hqkA9z54QV309PHffUSU2ySymNNbOxs8E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=FtiZX2kv+eOtjCtqiH30XTxw+DlWmqzGj2RN61sE/tkYZ7UWf0nUb+2jEShSGRlS9nyt6nTsL0rVQ6z28ulcoP0yWbWz4EXuLCvVJMgfU2AQL5ciSIPcwdV30anqSU9g2kV41YbTiIsMLWw4o2qGHv4vHv6ZNiT+GnhFBSycpRM= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=iJ96pkwr; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="iJ96pkwr" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9C8051F00A3A; Tue, 16 Jun 2026 01:08:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781572141; bh=hICK0f9EXipK30sHw4w7F3YugcEvbyriZHsVdJLEggc=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=iJ96pkwrgeWaQmHFX58WMuJY0zQNX7eeLfU7CQC0HsZqA9fosSR0O7rrGG1THyL7+ H+p3cV/kSr6yUVdVNFSVKg9xTjlxlyKMd9Lv6oJTcgyiQNIuDyLK7u6FEkYff2Kysi fI7MolTjMOjmKeMctxUnu96Y60Et8lVBWo83a+B3k1hHw5MGLtoLoVFE+LEXQ0Jvma OH6UwEFxcC+nK2hS/FODXvoS6UxaybRmrdEKhk8E1nD99JKdns/BOVuFDv2d+ap4A8 nW+/rg71rO3mIWWpXrRJ/tSsOi1Pk2CaMybXujodBVkpDUfqqqO3cZoPZtRjiu0SVR gzUWXvEaI5/PA== From: Arnaldo Carvalho de Melo To: Namhyung Kim Cc: Ingo Molnar , Thomas Gleixner , James Clark , Jiri Olsa , Ian Rogers , Adrian Hunter , Clark Williams , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, Arnaldo Carvalho de Melo , sashiko-bot , James Clark , Leo Yan , Claude Subject: [PATCH 5/9] perf cs-etm: Bounds-check CPU in cs_etm__get_queue() Date: Mon, 15 Jun 2026 22:08:30 -0300 Message-ID: <20260616010834.37618-6-acme@kernel.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260616010834.37618-1-acme@kernel.org> References: <20260616010834.37618-1-acme@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Arnaldo Carvalho de Melo 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 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cs-etm.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index d121c8f22028d5ba..5d0664ff73b79122 100644 --- 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_queue(struct cs_etm_auxtrace *etm, int c { if (etm->per_thread_decoding) return etm->queues.queue_array[0].priv; - else - return etm->queues.queue_array[cpu].priv; + + if (cpu < 0 || cpu >= (int)etm->queues.nr_queues) + return NULL; + + return etm->queues.queue_array[cpu].priv; } static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id, @@ -306,6 +309,9 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id * queue associated with that CPU so only one decoder is made. */ etmq = cs_etm__get_queue(etm, cpu_metadata[CS_ETM_CPU]); + if (!etmq) + return -EINVAL; + if (etmq->format == UNFORMATTED) return cs_etm__insert_trace_id_node(etmq, trace_chan_id, cpu_metadata); @@ -318,6 +324,9 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id int ret; etmq = etm->queues.queue_array[i].priv; + if (!etmq) + continue; + ret = cs_etm__insert_trace_id_node(etmq, trace_chan_id, cpu_metadata); if (ret) @@ -358,6 +367,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, u32 sink_id = FIELD_GET(CS_AUX_HW_ID_SINK_ID_MASK, hw_id); u8 trace_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id); + if (!etmq) + return -EINVAL; + /* * Check sink id hasn't changed in per-cpu mode. In per-thread mode, * let it pass for now until an actual overlapping trace ID is hit. In @@ -375,6 +387,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, for (unsigned int i = 0; i < etm->queues.nr_queues; ++i) { struct cs_etm_queue *other_etmq = etm->queues.queue_array[i].priv; + if (!other_etmq) + continue; + /* Different sinks, skip */ if (other_etmq->sink_id != etmq->sink_id) continue; @@ -396,6 +411,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, } cpu_data = get_cpu_data(etm, cpu); + if (!cpu_data) + return -EINVAL; + ret = cs_etm__insert_trace_id_node(etmq, trace_id, cpu_data); if (ret) return ret; @@ -3144,6 +3162,9 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o aux_offset + aux_size <= auxtrace_event->offset + auxtrace_event->size) { struct cs_etm_queue *etmq = cs_etm__get_queue(etm, auxtrace_event->cpu); + if (!etmq) + return -EINVAL; + /* * If this AUX event was inside this buffer somewhere, create a new auxtrace event * based on the sizes of the aux event, and queue that fragment. -- 2.54.0