From nobody Sat Jun 13 09:18:38 2026 Received: from mail-dl1-f74.google.com (mail-dl1-f74.google.com [74.125.82.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E538637BE7F for ; Fri, 8 May 2026 08:27:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778228858; cv=none; b=iKcLa0myje0U97uvt0Db+z+EkZDLrOHok02DN07lHpvkKt8Jmzggx7OrozCtD+6xOjjk/eK9Is9HOciAhNuM9+3FYS2yWYf6XZZVzlWFFX3FiPrdUswJer0mCkCkpA0Fmexim6TsmhhGszBCRjw8ya2lvisfzgPl5U5MnxrlWBU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778228858; c=relaxed/simple; bh=CylnfDQq2sYXt33B4obV12HlAnESF/qI9YpYRVRk2HM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=KW/9b0RG99wTkcyiZT89O1+S/mmgbAIZPth6PP1vgwyaRvm2fRm55LHvDWLTdhdJAWDjcw2BAZoDsCSvU0kBTtH5/vdQnsp4XgOTyBWshcx4aT4nN2zrMVJpi7k8rc24JiYk1pBNZusAXXaANuCLXJwko6pt9l5CPRLOt5W6Abc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=SlJ6B3rZ; arc=none smtp.client-ip=74.125.82.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="SlJ6B3rZ" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-131371497a1so2366061c88.0 for ; Fri, 08 May 2026 01:27:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1778228854; x=1778833654; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=53zBMY0hxmigdLuQch54NlIbiL3paA9gL4y5SjWANoI=; b=SlJ6B3rZb/sXHtGH0gUeuxM/RVxNBstd9UmI3ed6jwQkfzQfoDkF95AgYx/+4JDpMn ZVLSVFL/0VfAVa0exerL/WdZ6Ro2IgnNLLLeLtMz2cyXuj6zRwOqs/ghmCuq7nLo71jO HTw30ceB4HTuhDmIuo2F0SMMVHFJbg+WdJ4oVcvXcgwhfsTGdoVUnv4Iw+dBTN5D3p4M E4b3YBzamyH5l1EUWYaDmuQ6fr04WnFwD7Q4B9nB+C+1dHGOZFGXOFZyCJWWE0oX4ojh yfJRTjItRvDFA9mTzVLFVLq5sDXq2cMwkit3CQPpeVjX44HY4OcP8Jv6gg/emc2Pjmxh An/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778228854; x=1778833654; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=53zBMY0hxmigdLuQch54NlIbiL3paA9gL4y5SjWANoI=; b=DF+wIXHmhcE3cr/BeCIQXmjMXsI0iTxcE+kJpfyX+iRpLaemkn7khSdVTZuuOAkZ5+ MrGCanxZ3OYDy1QBlC4ZcNuSNU77GjXS1Aeqg+AQaWUCuxBxCHwmiS/Q0EgG0cD23IeB jHq0LqvwC11wbz6Ym9wuq4J9o6El4i03RMn+y9PIW6Qysm/agxi0J27tg4kL53w55SQt dQFzpX/w42QMz/HPy9Q0nXmiG9CiCf7I9tA9nPfTbULIDqdMWM6+KVKaF+xPTWM/S19y +2JqyjGkI0l1Db2S4jfB7/CivvEx2MkZdZ2iVROwF4b2CHoXxMzHzejmIK1JOzqAYGeL 6EVg== X-Forwarded-Encrypted: i=1; AFNElJ/WAboFhl+URUp7pZldt0AlO9yuWbcGnwyYm/0GCv2i538ImTRqg/bOnnBNB5KIRDlkngpF9wKWKFVJUIw=@vger.kernel.org X-Gm-Message-State: AOJu0YxdFbLatdLnn/YwfQ9rYa+OsTrYmGVDPUJtBFlpyXWjy0Pcz6BQ q2VuPqka77+Wt05wTGTFMsY+ZeDHxGAVEapr8uM+9OHjkFteoWU8wIw91euVjjtDy16e/jXFh05 SknxREqKi7g== X-Received: from dlbuy21.prod.google.com ([2002:a05:7022:1e15:b0:12d:e9ff:7062]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:924:b0:12a:713b:8958 with SMTP id a92af1059eb24-13271271429mr850207c88.10.1778228853609; Fri, 08 May 2026 01:27:33 -0700 (PDT) Date: Fri, 8 May 2026 01:27:21 -0700 In-Reply-To: <20260508082726.2795191-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260506004546.3140141-1-irogers@google.com> <20260508082726.2795191-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.563.g4f69b47b94-goog Message-ID: <20260508082726.2795191-2-irogers@google.com> Subject: [PATCH v6 1/6] perf sched: Add missing mmap2 handler in timehist From: Ian Rogers To: irogers@google.com, acme@kernel.org, gmx@google.com, james.clark@linaro.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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(). Assisted-by: Gemini-CLI:Google Gemini 3 Fixes: 49394a2a24c78ce0 ("perf sched timehist: Introduce timehist command") Signed-off-by: Ian Rogers --- 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 555247568e7a..241c2f808f7b 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3299,6 +3299,7 @@ static int perf_sched__timehist(struct perf_sched *sc= hed) */ sched->tool.sample =3D perf_timehist__process_sample; sched->tool.mmap =3D perf_event__process_mmap; + sched->tool.mmap2 =3D perf_event__process_mmap2; sched->tool.comm =3D perf_event__process_comm; sched->tool.exit =3D perf_event__process_exit; sched->tool.fork =3D perf_event__process_fork; --=20 2.54.0.563.g4f69b47b94-goog From nobody Sat Jun 13 09:18:38 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CBE0535F60F for ; Fri, 8 May 2026 08:27:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778228858; cv=none; b=km9UvNovTLyG+L7oPm+D56uJOD55UpD3gmuylNlg9XP+w/45yLKL88dxixjLP28LmyuYklfcEUCceK9wYwbY/bEAYbodZgSp+1DDGyjyEewZlUELxNACBjDimjeG2jZPnbKUp+K5K5+cG0m/63/jDHnezKXXvSB9Kb9xegrcMDc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778228858; c=relaxed/simple; bh=61XWGaFKOmn+SNuFop6s61N38Tmp6A9UBKhOpH6vCUk=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=mZ9jLLBAkrv1AvV5UD1ENnry2aj/bIcqBvb2Fo37TJHA4eudDwFoQkpvZE0iER/q7QcD9IhAT7/mEFSEGt7bNCNsDN/yYd10167DDktC2LVC7imTU1MyLumgl1nwhF98LDZVDKPiOO85xjAg5y4p4MOGShdK2Dx+I0hCTq5cnJo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Nt/2POv0; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Nt/2POv0" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-1325c685526so2194807c88.0 for ; Fri, 08 May 2026 01:27:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1778228856; x=1778833656; darn=vger.kernel.org; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=F0ui433QL46rA01gquLwWXH1KntcAtkrT2sKr2CD4mc=; b=Nt/2POv02juOYRKmxnTr4JyWjFii2D+rsCXWLaHHGAYKQR9vAt8WbVMixNlpPWXgfI sY4Xl1eVSI8j+lEPpwwboGGQf9Ubre945UrshD8nbXazQMwqFW6Eyxf8Eid0A0kTZRvl 4Kk003hahtjMg9dZOuYxm7yGKKoWmSYnKkj2EhB/VjGfwRj+1IZ/hzxhMrrRcITCySHp k46E8ZV6xHc2b6Hh0Qy9WaI5TltHrgQFKA6c/Wpj43eDgA6gHzi6aIqjkN9H9rzq16ix pBfzS8gZwIa/hW8NJece6YqWDcsC+Ky7RC3PJoNwPQ2SIoyZj2tck8AyzBgTWgbEaVea 4xhw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778228856; x=1778833656; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=F0ui433QL46rA01gquLwWXH1KntcAtkrT2sKr2CD4mc=; b=ouEygvxxgI/pZSFdSiIaNGNHfeg+MgdtYzFehpr7JJJgRUG5Dgx5vJj7uypVXEq+qe Jp2rteaZ+WNtUhZgwaxbK2sUXtd+ZUCs4NJ73BxDUI+NwS84XnP11Enz4R4s3E2cn3QD uigAe/JODY5w5IQ+Ny6ZWhsek543+JCN5AR9AQfYZ2W8eBlauPIUUx5sam/jZ/0YaA9f RNPGeQ6zbjO0vQTEHwccowptXgCqx/TlUYSLDo8A6GS/I9DneJzNoKBKiW9orbcrKW33 e1JiRrNtwzVGHmQ5pBYfMtUBnhZ7jr5+W6qWwWjlkGgdpfKAa6lHKOtAix3eiq1Y5G/U 5Y7g== X-Forwarded-Encrypted: i=1; AFNElJ8My758NmXaDvjydrJqOTDdchBfv0f2Zslu5f96fR+XSjwphBFU96Tb+hlABzYHGMZPkZX7cOcdEHqJ/Ws=@vger.kernel.org X-Gm-Message-State: AOJu0Yxov8wJvjydaiIVtYBpZ26IoFqv7MmoiDGEOsqDbXcw1krIs/jL aFGl/KIiuXmivQfLTEVkJttbRW46BCEY/8XDtNDmHFb/HKFvT5SnwszdplVn2iCkvlNto9hg1hX pHcHmJz0rRQ== X-Received: from dlbsn2.prod.google.com ([2002:a05:7022:b902:b0:130:c9dc:1b88]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:221a:b0:128:cedb:33c6 with SMTP id a92af1059eb24-131851d7c36mr5807637c88.16.1778228855651; Fri, 08 May 2026 01:27:35 -0700 (PDT) Date: Fri, 8 May 2026 01:27:22 -0700 In-Reply-To: <20260508082726.2795191-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260506004546.3140141-1-irogers@google.com> <20260508082726.2795191-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.563.g4f69b47b94-goog Message-ID: <20260508082726.2795191-3-irogers@google.com> Subject: [PATCH v6 2/6] perf tool: Missing delegate_tool schedstat delegates and dont_split_sample_group From: Ian Rogers To: irogers@google.com, acme@kernel.org, gmx@google.com, james.clark@linaro.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" delegate_tool was missing the delegate overrides for schedstat_cpu and schedstat_domain. As a result, when allocated with zalloc, these callbacks defaulted to NULL, causing a segmentation fault crash if any schedstat events were delivered during event processing. Fix this by adding delegate_schedstat_cpu and delegate_schedstat_domain via the CREATE_DELEGATE_OP2 macro, and ensuring delegate_tool__init correctly registers them. Additionally, delegate_tool__init completely omitted copying the dont_split_sample_group property from the delegate. This would cause wrapper tools to default the flag to false, which corrupts piped event processing (e.g., in perf inject) by triggering duplicate event deliveries on split sample values in deliver_sample_group(). Similarly, perf_tool__init() omitted the initialization of this boolean field. On stack-allocated tools that rely on this initializer (like intel-tpebs or __cmd_evlist), this could result in uninitialized stack garbage evaluating to true=E2=80=94silently dropping non-leader event members in deliver_sample_group(). Fix both issues by properly copying the field in delegate_tool__init and initializing it to false in perf_tool__init. Assisted-by: Gemini-CLI:Google Gemini 3 Fixes: 6331b2669359 ("perf tool: Add a delegate_tool that just delegates ac= tions to another tool") Fixes: 79bcd34e0f3d ("perf inject: Fix leader sampling inserting additional= samples") Signed-off-by: Ian Rogers --- tools/perf/util/tool.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c index 013c7839e2cf..ff2150517b75 100644 --- a/tools/perf/util/tool.c +++ b/tools/perf/util/tool.c @@ -285,6 +285,7 @@ void perf_tool__init(struct perf_tool *tool, bool order= ed_events) tool->no_warn =3D false; tool->show_feat_hdr =3D SHOW_FEAT_NO_HEADER; tool->merge_deferred_callchains =3D true; + tool->dont_split_sample_group =3D false; =20 tool->sample =3D process_event_sample_stub; tool->mmap =3D process_event_stub; @@ -433,6 +434,8 @@ CREATE_DELEGATE_OP2(stat_config); CREATE_DELEGATE_OP2(stat_round); CREATE_DELEGATE_OP2(thread_map); CREATE_DELEGATE_OP2(time_conv); +CREATE_DELEGATE_OP2(schedstat_cpu); +CREATE_DELEGATE_OP2(schedstat_domain); CREATE_DELEGATE_OP2(tracing_data); =20 #define CREATE_DELEGATE_OP3(name) \ @@ -470,6 +473,7 @@ void delegate_tool__init(struct delegate_tool *tool, st= ruct perf_tool *delegate) tool->tool.no_warn =3D delegate->no_warn; tool->tool.show_feat_hdr =3D delegate->show_feat_hdr; tool->tool.merge_deferred_callchains =3D delegate->merge_deferred_callcha= ins; + tool->tool.dont_split_sample_group =3D delegate->dont_split_sample_group; =20 tool->tool.sample =3D delegate_sample; tool->tool.read =3D delegate_read; @@ -516,4 +520,6 @@ void delegate_tool__init(struct delegate_tool *tool, st= ruct perf_tool *delegate) tool->tool.bpf_metadata =3D delegate_bpf_metadata; tool->tool.compressed =3D delegate_compressed; tool->tool.auxtrace =3D delegate_auxtrace; + tool->tool.schedstat_cpu =3D delegate_schedstat_cpu; + tool->tool.schedstat_domain =3D delegate_schedstat_domain; } --=20 2.54.0.563.g4f69b47b94-goog From nobody Sat Jun 13 09:18:38 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9AF2A37BE7C for ; Fri, 8 May 2026 08:27:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778228861; cv=none; b=rUTAD9w0lKfPPX1EmDbp8P6dkm0zJLx7oYgAdSUGFpJ4EEpEgfEnTuT7+fHbahN1Z2JmpHVmxeNcRy0A4JBA3d74z5/mtoABXdmQjGp28rqnHf1STRLrqsM0tqJRDzuvQXvUtgMfg61inEwzNwSfbeQN601WqqNP4V/ozIaJOus= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778228861; c=relaxed/simple; bh=qTheYH5mSiqC5/15dWn3M7Rofu4TnqK1Ab3iyySLOEw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=VNy6lK8zIJKyKVO2rnklQON6WpPlJvdRS0ZNZ5ycMsR3haGf8jVubMpLYk4Ns4f+KUwd6WgkxrspldKMBaSkZXxB6kK/vueCMZFor+FMdIqn8c/i4L0pO4jlCTlf2cjodtPBdGdzgaAZQIdLzsd5vSKQx+k6zNznS5GDEJ0jtIE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=BcrmsfDC; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="BcrmsfDC" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2ba9a744f7dso2270882eec.0 for ; Fri, 08 May 2026 01:27:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1778228858; x=1778833658; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=QikZcJA/YWUXCpBcR7ioWB0paLrJN6gmFF/oWuLh2Wk=; b=BcrmsfDCPQmFa69S9EKCQYyGODpuFg8v7oUSy2ruUjfj1qMP7bYKIa23RPYx8Y8tSK fBN6W5YDIHrnJv4MAlRs6Y8zPEjW13NsVQBAqq21Z5OwecmtdZ8679zKxA2yRiAp9Fxn cM3b+Gc9QWCURygpz5oSIQMlQ5rJCBSFCTg36WAliyTQG+pku9oeMdGtFhqLlhgnnKKs LmLDYJuVq5szHBpms/o2nYe+J3D+HJQQwHE+dbXhdJUhQyQaLSvXS2TkJeWrguPXUPX7 95WLKoScz14vvp3P+8j731qKSddW4WOHOn4wrJVXF7AYCGzjeK58KlRe1HKk14ywLw0L hwLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778228858; x=1778833658; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=QikZcJA/YWUXCpBcR7ioWB0paLrJN6gmFF/oWuLh2Wk=; b=RTT+yDGCa4UjMYzgM/fIg4+6Xo87fhydvH5gjeqOcOyzqs0aNUQVETeJL8LVxXyDF3 9PQI3/RaTzOrK4naAXOwtvfLyAzZWYYabXgDFh6SEOaoxGynPAf+RqPcUaMTkwe7c4tz OrJisD5u6NaCgIUdmaS9ijvBxsN5eQiyJFu5DCFa0mcG4NoduxHMDHdjCrkf1/VoIskm qwJEXyrK7sCJOl8Mkc6bxOuolKhPwCYCsmCzwwN+nq7eCbJXn/3tNYwsLKDRiQo0gzHl G+LCJMPxfTFi3I/emUCBDLC9vDVWa74E61W1kCkg7ZZ4oWSPwZeqHwImxZBQc+eCEA2Q pdtw== X-Forwarded-Encrypted: i=1; AFNElJ8USf+QIQhOaeG/iO0NGm+yiTxU3Ea4IBxKtz+8WhArTfX00oa7eR9Mnp3SoW19QFnfjXqwg6Dlx1yopQ8=@vger.kernel.org X-Gm-Message-State: AOJu0YzmYwaaOSIMp3jRY1QmaOpA3V3U5nZYzRyPm4GouP9W+x2YVH6z j/TT3mGEb6YbOrBh5KK3AGhWtvF+BcVUMeLr5Fw28vqp6o0zyngibyeBOMrXBQ+PnexN8k1bz5M AepnetmpY/A== X-Received: from dlbcm15.prod.google.com ([2002:a05:7022:688f:b0:12a:9ef0:93ed]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:701b:271a:b0:132:7ab5:6ca8 with SMTP id a92af1059eb24-1327ab56e23mr266276c88.22.1778228857583; Fri, 08 May 2026 01:27:37 -0700 (PDT) Date: Fri, 8 May 2026 01:27:23 -0700 In-Reply-To: <20260508082726.2795191-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260506004546.3140141-1-irogers@google.com> <20260508082726.2795191-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.563.g4f69b47b94-goog Message-ID: <20260508082726.2795191-4-irogers@google.com> Subject: [PATCH v6 3/6] perf maps: Add maps__mutate_mapping From: Ian Rogers To: irogers@google.com, acme@kernel.org, gmx@google.com, james.clark@linaro.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 lookups failure. Fix this by introducing a thread-safe, atomic transactional framework routine maps__mutate_mapping() that explicitly acquires the parent maps write semaphore lock, executes an incoming mutation callback block to perform the field updates under full lock protection, and invalidates the sorted tracking flags prior to releasing the write lock. This guarantees absolute atomic synchronization invariants, completely 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. There is a potential for self deadlock if maps__mutate_mapping is called with the lock held, such as with maps__for_each_map but this problem also existed with the previous remove and insert approaches. Fixes: 39b12f781271 ("perf tools: Make it possible to read object code from= vmlinux") Signed-off-by: Ian Rogers --- tools/perf/util/machine.c | 32 +++++++++++++++++----------- tools/perf/util/maps.c | 26 +++++++++++++++++++++++ tools/perf/util/maps.h | 2 ++ tools/perf/util/symbol-elf.c | 41 +++++++++++++++++++++++------------- tools/perf/util/symbol.c | 17 +++++++++++---- 5 files changed, 87 insertions(+), 31 deletions(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index e76f8c86e62a..8d4452c70cb5 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1522,22 +1522,30 @@ static void machine__set_kernel_mmap(struct machine= *machine, map__set_end(machine->vmlinux_map, ~0ULL); } =20 -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 =3D data; =20 - orig =3D machine->vmlinux_map; - updated =3D map__get(orig); + map__set_start(map, ctx->start); + map__set_end(map, ctx->end); + if (ctx->start =3D=3D 0 && ctx->end =3D=3D 0) + map__set_end(map, ~0ULL); + return 0; +} =20 - machine->vmlinux_map =3D updated; - maps__remove(machine__kernel_maps(machine), orig); - machine__set_kernel_mmap(machine, start, end); - err =3D 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 =3D { .start =3D start, .end =3D end = }; =20 - return err; + return maps__mutate_mapping(machine__kernel_maps(machine), + machine->vmlinux_map, + kernel_mmap_mutate_cb, &ctx); } =20 int machine__create_kernel_maps(struct machine *machine) diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c index 81a97ac34077..91345a773aa2 100644 --- a/tools/perf/util/maps.c +++ b/tools/perf/util/maps.c @@ -575,6 +575,32 @@ void maps__remove(struct maps *maps, struct map *map) #endif } =20 +int maps__mutate_mapping(struct maps *maps, struct map *map, + int (*mutate_cb)(struct map *map, void *data), void *data) +{ + int err =3D 0; + + if (maps) + down_write(maps__lock(maps)); + + err =3D mutate_cb(map, data); + + if (maps) { + RC_CHK_ACCESS(maps)->maps_by_address_sorted =3D false; + RC_CHK_ACCESS(maps)->maps_by_name_sorted =3D false; + } + + if (maps) + up_write(maps__lock(maps)); + +#ifdef HAVE_LIBDW_SUPPORT + if (maps) + libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps)); +#endif + + return err; +} + bool maps__empty(struct maps *maps) { bool res; diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h index 20c52084ba9e..de74ccbb8a12 100644 --- a/tools/perf/util/maps.h +++ b/tools/perf/util/maps.h @@ -61,6 +61,8 @@ size_t maps__fprintf(struct maps *maps, FILE *fp); =20 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); =20 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 7afa8a117139..dc4ab58857b3 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1341,6 +1341,24 @@ static u64 ref_reloc(struct kmap *kmap) void __weak arch__sym_update(struct symbol *s __maybe_unused, GElf_Sym *sym __maybe_unused) { } =20 +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 =3D 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, @@ -1371,22 +1389,15 @@ static int dso__process_kernel_symbol(struct dso *d= so, struct map *map, * map to the kernel dso. */ if (*remap_kernel && dso__kernel(dso) && !kmodule) { + struct remap_kernel_ctx ctx =3D { + .sh_addr =3D shdr->sh_addr, + .sh_size =3D shdr->sh_size, + .sh_offset =3D shdr->sh_offset, + .kmap =3D kmap + }; + *remap_kernel =3D 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 =3D map__get(map); - - maps__remove(kmaps, map); - err =3D maps__insert(kmaps, map); - map__put(tmp); - if (err) - return err; - } + maps__mutate_mapping(kmaps, map, remap_kernel_cb, &ctx); } =20 /* diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index fcaeeddbbb6b..09b93e844887 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -48,6 +48,13 @@ #include #include =20 +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); @@ -2121,10 +2128,11 @@ static int dso__load_kernel_sym(struct dso *dso, st= ruct map *map) free(kallsyms_allocated_filename); =20 if (err > 0 && !dso__is_kcore(dso)) { + struct maps *kmaps =3D 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); } =20 return err; @@ -2164,10 +2172,11 @@ static int dso__load_guest_kernel_sym(struct dso *d= so, 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 =3D 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); } =20 return err; --=20 2.54.0.563.g4f69b47b94-goog From nobody Sat Jun 13 09:18:38 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B16D73845D5 for ; Fri, 8 May 2026 08:27:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778228864; cv=none; b=NXcmo/Hnb2cClsjA459SDpSr5CIuQe4U07yL8dSAmAYIQm3LSXrfsASgkLpLjAJdvQ+ych2m4EPDFyQkwost4f3uYLU/t0xW/sSA+MfN4i1wZGSiDM2CgVscSIoIdjPds0sPjJB0LBbU2EgW6hw2b/IF77kueD96NDCQeWNMv+o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778228864; c=relaxed/simple; bh=btNhtWjQzRurF0KKMZcLJp03dGSzw+UMhnjbUnxLhhI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=P8izhE83uownYQseZ4sFyNTz9lZhk+koJvsE4H8/D+9O17YXcaf5kr0aiVQpGuQMECTVoWKimZJ7wo+183XvSnmMpcTEG5ZAow+JV/9IxvtZ067fEL+GBs5NjyQET6hi/oFHGziP/e7UBANKwBn8rvK1nL5VYTieY4vxCB6zmgM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=NSxMsv02; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="NSxMsv02" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-12dece274b1so961384c88.1 for ; Fri, 08 May 2026 01:27:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1778228860; x=1778833660; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=lqXhhYaZyvF5x5ppVIMVAeRHPGmfx71eikLdldXjhMo=; b=NSxMsv02WBj3rE3SgZ2yaC+zSUd7K+bNROv3exJFoEcdECeX8nJH3doZ53TLE0Rw1U pt2mLakKcMXfs2RdDj8HgUsSPjzCC9BU3hh1ZiQLMkHyr34oVHpl/6uHvfMblw3QMejP zE7FMpyppscHC1Q2m2RnfzHvOBUEH3bPvyaSO9BWmkgh38VXANXjL/anq/o0KW4oKgrz b+QWoxkG+2eQEwsB3nzo6AXJmYeyAFKPqJfOKtYyQi79zCYboR96golKsi2yVeeoyUxF MdTzRn/NfXbQX8E8BksobGCJmYuBid+4T26I+FY1IfMpakwHQUvXk3dqAIBYhrZRICbr Sqjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778228860; x=1778833660; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=lqXhhYaZyvF5x5ppVIMVAeRHPGmfx71eikLdldXjhMo=; b=tRnDVJq4ZlGaG0OEbt/EHZswLABA19nCklTgKz+IU+R0q0hIqobyyTNmL7nUH2rgbK xcYZNs0GetTc0+OQYNcwUfK5NRJffmduKH9CAxjYScvHHe3HF4k3chSxaPc/t2QXufxw 6cTU7z2n/haXPbQ8cldeFHOXxXzub5nSX+DE+sbaiIhqI8XI1qU/mRDouj9wcC2Oes2T B6BpJ5qcET+zhRKJ6bJBrSMQzNEs8XCo/t12wOopT7YYHdz2sU/WeXAnSQq/ctKKyUul pTSNDW37p7WKd0A08BTRxgvvLAomT9N6b1WV2vQ9DN8iWsOjADbapDhnz5sZZOnZp8pK 2zEQ== X-Forwarded-Encrypted: i=1; AFNElJ8nPI9OCKvC0VnzALmrPLJ+Ub4bRBY0MwIvVlJ9OTYZoD0NobcUSo1WjN6ilg27o2HZSxJjEBFk7ZTaxE8=@vger.kernel.org X-Gm-Message-State: AOJu0Yw16+Ri126XcQf581iwskJaVD9Cq/RkH1WdaNo96D9Hvv0ngk+E 9lCYUqwXIj9qpyUPiXWb/5GiZuCb3sH5Vok4+afxTRXAbsryj4yeKPfsp1qBux1/2ZZ/2xBL+40 GiE7L7OsSBQ== X-Received: from dycnr21-n1.prod.google.com ([2002:a05:7300:e9d5:10b0:2d8:f479:9a89]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:1a83:b0:128:d23d:81aa with SMTP id a92af1059eb24-1318e8136c8mr5488610c88.27.1778228859533; Fri, 08 May 2026 01:27:39 -0700 (PDT) Date: Fri, 8 May 2026 01:27:24 -0700 In-Reply-To: <20260508082726.2795191-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260506004546.3140141-1-irogers@google.com> <20260508082726.2795191-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.563.g4f69b47b94-goog Message-ID: <20260508082726.2795191-5-irogers@google.com> Subject: [PATCH v6 4/6] perf inject/aslr: Add aslr tool to remap/obfuscate virtual addresses From: Ian Rogers To: irogers@google.com, acme@kernel.org, gmx@google.com, james.clark@linaro.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" If perf.data files are taken from one machine to another they may leak virtual addresses and so weaken ASLR on the machine they are coming from. Add an aslr option for perf inject that remaps all virtual addresses, or drops data/events, so that the virtual address information isn't leaked. Events carrying virtual memory layouts are conservatively remap-processed or dropped, while zero-address-risk lifecycle metadata records (such as namespaces, cgroups, and BPF program info) are intentionally delegated to preserve comprehensive downstream trace tool analysis compatibility. The ASLR tracking tool virtualizes process and machine namespaces using 'struct machines' to safely isolate host mappings from unprivileged KVM gue= st address spaces. Memory space layouts are tracked globally per process conte= xt to ensure linear, continuous space allocations across successive mapping runs. To remain strictly conservative and guarantee security, the tool scrubs breakpoint addresses (bp_addr) from all synthesized stream headers, complet= ely drops PERF_RECORD_TEXT_POKE events to prevent absolute immediate pointer operands leaks, and drops unsupported complex payloads (such as user regist= er stacks, raw tracepoints, and hardware AUX tracing frames). Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Co-developed-by: Gabriel Marin Signed-off-by: Gabriel Marin --- v6: Enforce strict command-line validation mutual exclusivity between --aslr and --convert-callchain to prevent silent unwind failures. Secure mmap.pgoff unconditionally for all host and guest kernel text mapping regions to completely prevent active KASLR load deltas leakage. Conservatively drop PERF_RECORD_TEXT_POKE events completely via a local static drop stub to prevent absolute 64-bit kernel virtual pointer imme= diate operands leaks. Inject explicit array-end bounds validation check blocks before consuming trailing PERF_CONTEXT_USER_DEFERRED callchain cookies to eliminate out-of-bounds reads and parser desynchronization faults. Simplify ASLR mapping remap logic. Ensure that encountering a PERF_CONTEXT_USER_DEFERRED context marker explicitly updates cpumode. v5: Add machine to remap addresss key so that it is guest/host safe. Add 'first_kernel_mapping' tracking guard inside aslr.c to rewrite the core kernel pgoff virtual address while safely protecting module file offsets from corruption. Clean up breakpoint address (bp_addr) memory scrubbing by executing the scrubbing loop directly at core session initialization startup level, natively securing both file headers and streaming pipe channels while removing redundant runtime tool wrapper interception hooks layers. v4: Scrub bp_addr from headers/pipe synthesis attributes. Remove kernel mmap pgoff mathematical delta adjustment leaks to maintain secure base obfuscation bounds. Harden guest space contexts mapping loops, correct ksymbol map base invariants tracking, and plug tail-word padding heap leakage vectors in user stacks and AUX payloads. v3: Combine split-map fixes, guest namespaces, bounds checks, OOM rollbacks, hot path optimization, safe dso references, and I/O stream error handling from v3/v4 development. Drop raw auxtrace events. Fix thread reference leaks in event handlers. Fix 32-bit truncation bug in hashmaps using u64* values. Prevent leaking uninitialized heap memory by zeroing copy buffer. Correct bitmask checks for branch stack flags. Avoid PMU configuration corruption. v2: First review feedback adjustments. --- tools/perf/builtin-inject.c | 36 +- tools/perf/util/Build | 1 + tools/perf/util/aslr.c | 1036 +++++++++++++++++++++++++++++++++++ tools/perf/util/aslr.h | 10 + 4 files changed, 1082 insertions(+), 1 deletion(-) create mode 100644 tools/perf/util/aslr.c create mode 100644 tools/perf/util/aslr.h diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 6ab20df358c4..51dcf248b653 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -8,6 +8,7 @@ */ #include "builtin.h" =20 +#include "util/aslr.h" #include "util/color.h" #include "util/dso.h" #include "util/vdso.h" @@ -123,6 +124,7 @@ struct perf_inject { bool in_place_update_dry_run; bool copy_kcore_dir; bool convert_callchain; + bool aslr; const char *input_name; struct perf_data output; u64 bytes_written; @@ -304,6 +306,8 @@ static int perf_event__repipe(const struct perf_tool *t= ool, return perf_event__repipe_synth(tool, event); } =20 + + static int perf_event__drop(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, @@ -2459,6 +2463,8 @@ static int __cmd_inject(struct perf_inject *inject) } } =20 + + session->header.data_offset =3D output_data_offset; session->header.data_size =3D inject->bytes_written; perf_session__inject_header(session, session->evlist, fd, &inj_fc.fc, @@ -2565,6 +2571,8 @@ int cmd_inject(int argc, const char **argv) " instance has a subdir"), OPT_BOOLEAN(0, "convert-callchain", &inject.convert_callchain, "Generate callchains using DWARF and drop register/stack data"), + OPT_BOOLEAN(0, "aslr", &inject.aslr, + "Remap virtual memory addresses similar to ASLR"), OPT_END() }; const char * const inject_usage[] =3D { @@ -2572,6 +2580,7 @@ int cmd_inject(int argc, const char **argv) NULL }; bool ordered_events; + struct perf_tool *tool =3D &inject.tool; =20 if (!inject.itrace_synth_opts.set) { /* Disable eager loading of kernel symbols that adds overhead to perf in= ject. */ @@ -2592,6 +2601,11 @@ int cmd_inject(int argc, const char **argv) if (argc) usage_with_options(inject_usage, options); =20 + if (inject.aslr && inject.convert_callchain) { + pr_err("Error: --aslr and --convert-callchain are mutually exclusive fea= tures.\n"); + return -EINVAL; + } + if (inject.strip && !inject.itrace_synth_opts.set) { pr_err("--strip option requires --itrace option\n"); return -1; @@ -2685,18 +2699,36 @@ int cmd_inject(int argc, const char **argv) inject.tool.schedstat_domain =3D perf_event__repipe_op2_synth; inject.tool.dont_split_sample_group =3D true; inject.tool.merge_deferred_callchains =3D false; - inject.session =3D __perf_session__new(&data, &inject.tool, + if (inject.aslr) { + tool =3D aslr_tool__new(&inject.tool); + if (!tool) { + ret =3D -ENOMEM; + goto out_close_output; + } + } + inject.session =3D __perf_session__new(&data, tool, /*trace_event_repipe=3D*/inject.output.is_pipe, /*host_env=3D*/NULL); =20 if (IS_ERR(inject.session)) { ret =3D PTR_ERR(inject.session); + if (inject.aslr) + aslr_tool__delete(tool); goto out_close_output; } =20 if (zstd_init(&(inject.session->zstd_data), 0) < 0) pr_warning("Decompression initialization failed.\n"); =20 + if (inject.aslr) { + struct evsel *evsel; + + evlist__for_each_entry(inject.session->evlist, evsel) { + if (evsel->core.attr.type =3D=3D PERF_TYPE_BREAKPOINT) + evsel->core.attr.bp_addr =3D 0; + } + } + /* Save original section info before feature bits change */ ret =3D save_section_info(&inject); if (ret) @@ -2790,6 +2822,8 @@ int cmd_inject(int argc, const char **argv) strlist__delete(inject.known_build_ids); zstd_fini(&(inject.session->zstd_data)); perf_session__delete(inject.session); + if (inject.aslr) + aslr_tool__delete(tool); out_close_output: if (!inject.in_place_update) perf_data__close(&inject.output); diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 70cc91d00804..65b96f3b87e2 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -6,6 +6,7 @@ perf-util-y +=3D arm64-frame-pointer-unwind-support.o perf-util-y +=3D addr2line.o perf-util-y +=3D addr_location.o perf-util-y +=3D annotate.o +perf-util-y +=3D aslr.o perf-util-y +=3D blake2s.o perf-util-y +=3D block-info.o perf-util-y +=3D block-range.o diff --git a/tools/perf/util/aslr.c b/tools/perf/util/aslr.c new file mode 100644 index 000000000000..09b7f2f8fb85 --- /dev/null +++ b/tools/perf/util/aslr.c @@ -0,0 +1,1036 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "aslr.h" + +#include "addr_location.h" +#include "debug.h" +#include "event.h" +#include "evsel.h" +#include "machine.h" +#include "map.h" +#include "thread.h" +#include "tool.h" +#include "session.h" +#include "data.h" +#include "dso.h" + +#include /* page_size */ +#include +#include +#include +#include +#include + +/** + * struct remap_addresses_key - Key for mapping original addresses to rema= pped ones. + * @dso: Pointer to the DSO (Dynamic Shared Object) associated with the ma= pping. + * @invariant: Unique offset invariant within the VMA (Virtual Memory Area= ). + * Calculated as `start - pgoff`. This value remains constant = when + * perf's internal `maps__fixup_overlap_and_insert` splits a m= ap into + * fragmented VMA pieces due to overlapping events, allowing u= s to + * resolve split maps consistently back to the original VMA. + * @pid: Process ID associated with the mapping. + */ +struct remap_addresses_key { + struct machine *machine; + struct dso *dso; + u64 invariant; + pid_t pid; +}; + +struct aslr_mapping { + struct list_head node; + u64 orig_start; + u64 len; + u64 remap_start; +}; + +struct aslr_tool { + /** @tool: The tool implemented here and a pointer to a delegate to proce= ss the data. */ + struct delegate_tool tool; + /** @machines: The machines with the input, not remapped, virtual address= layout. */ + struct machines machines; + /** @event_copy: Buffer used to create an event to pass to the delegate. = */ + char event_copy[PERF_SAMPLE_MAX_SIZE] __aligned(8); + /** @remap_addresses: mapping from remap_addresses_key to remapped addres= s. */ + struct hashmap remap_addresses; + /** @top_addresses: mapping from process to max remapped address. */ + struct hashmap top_addresses; +}; + +static const pid_t kernel_pid =3D -1; + +/* Start remapping user processes from a small non-zero offset. */ +static const u64 user_space_start =3D 0x200000; +static const u64 kernel_space_start =3D 0xffff800010000000; + +static size_t remap_addresses__hash(long _key, void *ctx __maybe_unused) +{ + struct remap_addresses_key *key =3D (struct remap_addresses_key *)_key; + + return (size_t)key->machine ^ (size_t)key->dso ^ key->invariant ^ key->pi= d; +} + +static bool remap_addresses__equal(long _key1, long _key2, void *ctx __may= be_unused) +{ + struct remap_addresses_key *key1 =3D (struct remap_addresses_key *)_key1; + struct remap_addresses_key *key2 =3D (struct remap_addresses_key *)_key2; + + return key1->machine =3D=3D key2->machine && + RC_CHK_EQUAL(key1->dso, key2->dso) && + key1->invariant =3D=3D key2->invariant && + key1->pid =3D=3D key2->pid; +} + +struct top_addresses_key { + struct machine *machine; + pid_t pid; +}; + +static size_t top_addresses__hash(long _key, void *ctx __maybe_unused) +{ + struct top_addresses_key *key =3D (struct top_addresses_key *)_key; + + return (size_t)key->machine ^ key->pid; +} + +static bool top_addresses__equal(long _key1, long _key2, void *ctx __maybe= _unused) +{ + struct top_addresses_key *key1 =3D (struct top_addresses_key *)_key1; + struct top_addresses_key *key2 =3D (struct top_addresses_key *)_key2; + + return key1->machine =3D=3D key2->machine && key1->pid =3D=3D key2->pid; +} + +static u64 round_up_to_page_size(u64 addr) +{ + return (addr + page_size - 1) & ~((u64)page_size - 1); +} + +static u64 aslr_tool__remap_address(struct aslr_tool *aslr, + struct thread *aslr_thread, + u8 cpumode, + u64 addr) +{ + struct addr_location al; + struct remap_addresses_key key; + u64 *remapped_invariant_ptr =3D NULL; + u64 remap_addr =3D 0; + u8 effective_cpumode =3D cpumode; + + if (!aslr_thread) + return 0; /* No thread. */ + + addr_location__init(&al); + if (!thread__find_map(aslr_thread, cpumode, addr, &al)) { + /* + * If lookup fails with specified cpumode, try fallback to the other spa= ce + * to be robust against bad cpumode in samples. + */ + if (cpumode =3D=3D PERF_RECORD_MISC_KERNEL) + effective_cpumode =3D PERF_RECORD_MISC_USER; + else if (cpumode =3D=3D PERF_RECORD_MISC_USER) + effective_cpumode =3D PERF_RECORD_MISC_KERNEL; + else if (cpumode =3D=3D PERF_RECORD_MISC_GUEST_KERNEL) + effective_cpumode =3D PERF_RECORD_MISC_GUEST_USER; + else if (cpumode =3D=3D PERF_RECORD_MISC_GUEST_USER) + effective_cpumode =3D PERF_RECORD_MISC_GUEST_KERNEL; + + if (!thread__find_map(aslr_thread, effective_cpumode, addr, &al)) { + addr_location__exit(&al); + return 0; /* No mmap. */ + } + } + + key.machine =3D maps__machine(aslr_thread->maps); + key.dso =3D map__dso(al.map); + key.invariant =3D map__start(al.map) - map__pgoff(al.map); + key.pid =3D effective_cpumode =3D=3D PERF_RECORD_MISC_KERNEL ? kernel_pid= : aslr_thread->pid_; + + if (hashmap__find(&aslr->remap_addresses, &key, &remapped_invariant_ptr))= { + remap_addr =3D *remapped_invariant_ptr + map__pgoff(al.map) + + (addr - map__start(al.map)); + } else { + pr_debug("Cannot find a remapped entry for address %lx " + "in mapping %lx(%lx) for pid=3D%d\n", + addr, map__start(al.map), map__size(al.map), key.pid); + } + + addr_location__exit(&al); + return remap_addr; +} + +static u64 aslr_tool__findnew_mapping(struct aslr_tool *aslr, + struct thread *aslr_thread, + u8 cpumode, u64 start, + u64 len, u64 pgoff) +{ + /* Address location for dso lookup. */ + struct addr_location al; + /* Original ASLR address based key for the remap table. */ + struct remap_addresses_key remap_key; + /* The address in the ASLR sanitized address space less pg_off. */ + u64 *remapped_invariant_ptr; + /* Key for the maximum address in a process. */ + struct top_addresses_key top_addr_key; + /* Value in top address table. */ + u64 *pmax =3D NULL; + /* Address in ASLR sanitized address space. */ + u64 remap_addr; + /* Potentially allocated remap table key. */ + struct remap_addresses_key *new_remap_key =3D NULL; + /* + * Potentially allocated remap table key. + * TODO: Avoid allocation necessary for perf 32-bit binary support. + */ + u64 *new_remap_val =3D NULL; + int err; + + if (!aslr_thread) + return 0; + + /* The key to look up an incoming address to the outgoing value. */ + addr_location__init(&al); + remap_key.machine =3D maps__machine(aslr_thread->maps); + remap_key.pid =3D (cpumode =3D=3D PERF_RECORD_MISC_KERNEL) ? kernel_pid := aslr_thread->pid_; + if (thread__find_map(aslr_thread, cpumode, start, &al)) { + remap_key.dso =3D map__dso(al.map); + remap_key.invariant =3D map__start(al.map) - map__pgoff(al.map); + } else { + remap_key.dso =3D NULL; + remap_key.invariant =3D start - pgoff; + } + + /* The key to look up top allocated address. */ + top_addr_key.machine =3D remap_key.machine; + top_addr_key.pid =3D remap_key.pid; + + if (hashmap__find(&aslr->remap_addresses, &remap_key, &remapped_invariant= _ptr)) { + /* Mmap already exists. */ + u64 calculated_max; + + remap_addr =3D *remapped_invariant_ptr + (al.map ? map__pgoff(al.map) : = pgoff); + calculated_max =3D remap_addr + len; + + /* See if top mapping was expanded. */ + if (hashmap__find(&aslr->top_addresses, &top_addr_key, &pmax)) { + if (calculated_max > *pmax) + *pmax =3D calculated_max; + } + addr_location__exit(&al); + return remap_addr; + } + /* No mmap, create an entry from the top address. */ + if (hashmap__find(&aslr->top_addresses, &top_addr_key, &pmax)) { + /* Current max allocated mmap address within the process. */ + remap_addr =3D *pmax; + + /* Give 1 page gap from current max page. */ + remap_addr =3D round_up_to_page_size(remap_addr); + remap_addr +=3D page_size; + if (remap_addr + len > *pmax) + *pmax =3D remap_addr + len; + } else { + /* First address of the process, allocate key and first top address. */ + struct top_addresses_key *tk; + + remap_addr =3D (cpumode =3D=3D PERF_RECORD_MISC_KERNEL) ? + kernel_space_start : user_space_start; + remap_addr =3D round_up_to_page_size(remap_addr); + + tk =3D malloc(sizeof(*tk)); + pmax =3D malloc(sizeof(u64)); + if (!tk || !pmax) { + err =3D -ENOMEM; + } else { + *tk =3D top_addr_key; + *pmax =3D remap_addr + len; + err =3D hashmap__insert(&aslr->top_addresses, tk, pmax, HASHMAP_ADD, NU= LL, NULL); + } + if (err) { + errno =3D -err; + pr_err("Failure to add ASLR process top address %m\n"); + free(tk); + free(pmax); + addr_location__exit(&al); + return 0; + } + } + /* Create rmeapping entry. */ + new_remap_key =3D malloc(sizeof(*new_remap_key)); + new_remap_val =3D malloc(sizeof(u64)); + if (!new_remap_key || !new_remap_val) { + err =3D -ENOMEM; + } else { + *new_remap_key =3D remap_key; + new_remap_key->dso =3D dso__get(remap_key.dso); + if (cpumode =3D=3D PERF_RECORD_MISC_KERNEL) { + if (al.map) + *new_remap_val =3D remap_addr - (start - map__start(al.map)) - map__pg= off(al.map); + else + *new_remap_val =3D remap_addr; + } else { + *new_remap_val =3D remap_addr - (al.map ? map__pgoff(al.map) : pgoff); + } + err =3D hashmap__add(&aslr->remap_addresses, new_remap_key, new_remap_va= l); + if (err) + dso__put(new_remap_key->dso); + } + if (err) { + errno =3D -err; + pr_err("Failure to add ASLR remapping %m\n"); + free(new_remap_key); + free(new_remap_val); + addr_location__exit(&al); + return 0; + } + addr_location__exit(&al); + return remap_addr; +} + +static int aslr_tool__process_mmap(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + union perf_event *new_event; + u8 cpumode; + struct thread *thread; + struct machine *aslr_machine; + int err; + + del_tool =3D container_of(tool, struct delegate_tool, tool); + aslr =3D container_of(del_tool, struct aslr_tool, tool); + delegate =3D aslr->tool.delegate; + new_event =3D (union perf_event *)aslr->event_copy; + cpumode =3D event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + + aslr_machine =3D machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + + /* Create the thread, map, etc. in the ASLR before virtual address space.= */ + err =3D perf_event__process_mmap(tool, event, sample, aslr_machine); + if (err) + return err; + + thread =3D machine__findnew_thread(aslr_machine, event->mmap.pid, event->= mmap.tid); + if (!thread) + return -ENOMEM; + memcpy(&new_event->mmap, &event->mmap, event->mmap.header.size); + /* Remaps the mmap.start. */ + new_event->mmap.start =3D aslr_tool__findnew_mapping(aslr, thread, cpumod= e, + event->mmap.start, + event->mmap.len, + event->mmap.pgoff); + if (cpumode =3D=3D PERF_RECORD_MISC_KERNEL || cpumode =3D=3D PERF_RECORD_= MISC_GUEST_KERNEL) + new_event->mmap.pgoff =3D new_event->mmap.start; + err =3D delegate->mmap(delegate, new_event, sample, machine); + thread__put(thread); + return err; +} + +static int aslr_tool__process_mmap2(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + union perf_event *new_event; + u8 cpumode; + struct thread *thread; + struct machine *aslr_machine; + int err; + + del_tool =3D container_of(tool, struct delegate_tool, tool); + aslr =3D container_of(del_tool, struct aslr_tool, tool); + delegate =3D aslr->tool.delegate; + new_event =3D (union perf_event *)aslr->event_copy; + cpumode =3D event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + + aslr_machine =3D machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + + /* Create the thread, map, etc. in the ASLR before virtual address space.= */ + err =3D perf_event__process_mmap2(tool, event, sample, aslr_machine); + if (err) + return err; + + thread =3D machine__findnew_thread(aslr_machine, event->mmap2.pid, event-= >mmap2.tid); + if (!thread) + return -ENOMEM; + memcpy(&new_event->mmap2, &event->mmap2, event->mmap2.header.size); + /* Remaps the mmap.start. */ + new_event->mmap2.start =3D aslr_tool__findnew_mapping(aslr, thread, cpumo= de, + event->mmap2.start, + event->mmap2.len, + event->mmap2.pgoff); + if (cpumode =3D=3D PERF_RECORD_MISC_KERNEL || cpumode =3D=3D PERF_RECORD_= MISC_GUEST_KERNEL) + new_event->mmap2.pgoff =3D new_event->mmap2.start; + err =3D delegate->mmap2(delegate, new_event, sample, machine); + thread__put(thread); + return err; +} + +static int aslr_tool__process_comm(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + struct machine *aslr_machine; + int err; + + del_tool =3D container_of(tool, struct delegate_tool, tool); + aslr =3D container_of(del_tool, struct aslr_tool, tool); + delegate =3D aslr->tool.delegate; + + aslr_machine =3D machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + + /* Create the thread, map, etc. in the ASLR before virtual address space.= */ + err =3D perf_event__process_comm(tool, event, sample, aslr_machine); + if (err) + return err; + + return delegate->comm(delegate, event, sample, machine); +} + +static int aslr_tool__process_fork(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + struct machine *aslr_machine; + int err; + + del_tool =3D container_of(tool, struct delegate_tool, tool); + aslr =3D container_of(del_tool, struct aslr_tool, tool); + delegate =3D aslr->tool.delegate; + + aslr_machine =3D machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + + /* Create the thread, map, etc. in the ASLR before virtual address space.= */ + err =3D perf_event__process_fork(tool, event, sample, aslr_machine); + if (err) + return err; + + return delegate->fork(delegate, event, sample, machine); +} + +static int aslr_tool__process_exit(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + struct machine *aslr_machine; + int err; + + del_tool =3D container_of(tool, struct delegate_tool, tool); + aslr =3D container_of(del_tool, struct aslr_tool, tool); + delegate =3D aslr->tool.delegate; + + aslr_machine =3D machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + + /* Create the thread, map, etc. in the ASLR before virtual address space.= */ + err =3D perf_event__process_exit(tool, event, sample, aslr_machine); + if (err) + return err; + + return delegate->exit(delegate, event, sample, machine); +} + +static int aslr_tool__process_text_poke(const struct perf_tool *tool __may= be_unused, + union perf_event *event __maybe_unused, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + /* Drop in case the instruction encodes an ASLR revealing address. */ + return 0; +} + +static int aslr_tool__process_ksymbol(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + union perf_event *new_event; + struct thread *thread; + struct machine *aslr_machine; + int err; + + del_tool =3D container_of(tool, struct delegate_tool, tool); + aslr =3D container_of(del_tool, struct aslr_tool, tool); + delegate =3D aslr->tool.delegate; + new_event =3D (union perf_event *)aslr->event_copy; + + aslr_machine =3D machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + + err =3D perf_event__process_ksymbol(tool, event, sample, aslr_machine); + if (err) + return err; + + thread =3D machine__findnew_thread(aslr_machine, kernel_pid, 0); + if (!thread) + return -ENOMEM; + memcpy(&new_event->ksymbol, &event->ksymbol, event->ksymbol.header.size); + /* Remaps the ksymbol.start */ + new_event->ksymbol.addr =3D aslr_tool__findnew_mapping(aslr, thread, + PERF_RECORD_MISC_KERNEL, + event->ksymbol.addr, + event->ksymbol.len, + /*pgoff=3D*/0); + + err =3D delegate->ksymbol(delegate, new_event, sample, machine); + thread__put(thread); + return err; +} + +static int aslr_tool__process_sample(const struct perf_tool *tool, union p= erf_event *event, + struct perf_sample *sample, + struct evsel *evsel, struct machine *machine) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + int ret; + u64 sample_type; + struct thread *thread; + struct machine *aslr_machine; + __u64 max_i; + __u64 max_j; + union perf_event *new_event; + struct perf_sample new_sample; + __u64 *in_array, *out_array; + u8 cpumode; + u64 addr; + size_t i; + size_t j; + + del_tool =3D container_of(tool, struct delegate_tool, tool); + aslr =3D container_of(del_tool, struct aslr_tool, tool); + delegate =3D aslr->tool.delegate; + ret =3D -EFAULT; + sample_type =3D evsel->core.attr.sample_type; + max_i =3D (event->header.size - sizeof(struct perf_event_header)) / sizeo= f(__u64); + max_j =3D (PERF_SAMPLE_MAX_SIZE - sizeof(struct perf_event_header)) / siz= eof(__u64); + new_event =3D (union perf_event *)aslr->event_copy; + cpumode =3D sample->cpumode; + i =3D 0; + j =3D 0; + + aslr_machine =3D machines__findnew(&aslr->machines, machine->pid); + if (!aslr_machine) + return -ENOMEM; + + thread =3D machine__findnew_thread(aslr_machine, sample->pid, sample->tid= ); + + if (!thread) + return -ENOMEM; + + if (max_i > PERF_SAMPLE_MAX_SIZE / sizeof(u64)) + goto out_put; + + + + new_event->sample.header =3D event->sample.header; + + in_array =3D &event->sample.array[0]; + out_array =3D &new_event->sample.array[0]; + +#define CHECK_BOUNDS(required_i, required_j) \ + do { \ + if (i + (required_i) > max_i || j + (required_j) > max_j) { \ + ret =3D -EFAULT; \ + goto out_put; \ + } \ + } while (0) + +#define COPY_U64() \ + do { \ + CHECK_BOUNDS(1, 1); \ + out_array[j++] =3D in_array[i++]; \ + } while (0) + +#define REMAP_U64(addr_field) \ + do { \ + CHECK_BOUNDS(1, 1); \ + out_array[j++] =3D aslr_tool__remap_address(aslr, thread, cpumode, addr_= field); \ + i++; \ + } while (0) + + if (sample_type & PERF_SAMPLE_IDENTIFIER) + COPY_U64(); /* id */ + if (sample_type & PERF_SAMPLE_IP) + REMAP_U64(sample->ip); + if (sample_type & PERF_SAMPLE_TID) + COPY_U64(); /* pid, tid */ + if (sample_type & PERF_SAMPLE_TIME) + COPY_U64(); /* time */ + if (sample_type & PERF_SAMPLE_ADDR) + REMAP_U64(sample->addr); + if (sample_type & PERF_SAMPLE_ID) + COPY_U64(); /* id */ + if (sample_type & PERF_SAMPLE_STREAM_ID) + COPY_U64(); /* stream_id */ + if (sample_type & PERF_SAMPLE_CPU) + COPY_U64(); /* cpu, res */ + if (sample_type & PERF_SAMPLE_PERIOD) + COPY_U64(); /* period */ + if (sample_type & PERF_SAMPLE_READ) { + if ((evsel->core.attr.read_format & PERF_FORMAT_GROUP) =3D=3D 0) { + COPY_U64(); /* value */ + if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) + COPY_U64(); /* time_enabled */ + if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) + COPY_U64(); /* time_running */ + if (evsel->core.attr.read_format & PERF_FORMAT_ID) + COPY_U64(); /* id */ + if (evsel->core.attr.read_format & PERF_FORMAT_LOST) + COPY_U64(); /* lost */ + } else { + u64 nr; + + CHECK_BOUNDS(1, 1); + nr =3D out_array[j++] =3D in_array[i++]; + if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) + COPY_U64(); /* time_enabled */ + if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) + COPY_U64(); /* time_running */ + for (u64 cntr =3D 0; cntr < nr; cntr++) { + COPY_U64(); /* value */ + if (evsel->core.attr.read_format & PERF_FORMAT_ID) + COPY_U64(); /* id */ + if (evsel->core.attr.read_format & PERF_FORMAT_LOST) + COPY_U64(); /* lost */ + } + } + } + if (sample_type & PERF_SAMPLE_CALLCHAIN) { + u64 nr; + + CHECK_BOUNDS(1, 1); + nr =3D out_array[j++] =3D in_array[i++]; + + for (u64 cntr =3D 0; cntr < nr; cntr++) { + CHECK_BOUNDS(1, 1); + addr =3D in_array[i++]; + if (addr >=3D PERF_CONTEXT_MAX) { + out_array[j++] =3D addr; + switch (addr) { + case PERF_CONTEXT_HV: + cpumode =3D PERF_RECORD_MISC_HYPERVISOR; + break; + case PERF_CONTEXT_KERNEL: + cpumode =3D PERF_RECORD_MISC_KERNEL; + break; + case PERF_CONTEXT_USER: + cpumode =3D PERF_RECORD_MISC_USER; + break; + case PERF_CONTEXT_GUEST: + cpumode =3D PERF_RECORD_MISC_GUEST_KERNEL; + break; + case PERF_CONTEXT_GUEST_KERNEL: + cpumode =3D PERF_RECORD_MISC_GUEST_KERNEL; + break; + case PERF_CONTEXT_GUEST_USER: + cpumode =3D PERF_RECORD_MISC_GUEST_USER; + break; + case PERF_CONTEXT_USER_DEFERRED: + if (cntr + 1 >=3D nr) { + pr_debug("Truncated callchain deferred cookie context\n"); + ret =3D 0; + goto out_put; + } + /* + * Immediately followed by a 64-bit + * stitching cookie. Skip/Copy it! + */ + CHECK_BOUNDS(1, 1); + out_array[j++] =3D in_array[i++]; + cntr++; + cpumode =3D PERF_RECORD_MISC_USER; + break; + default: + pr_debug("invalid callchain context: %"PRIx64"\n", addr); + ret =3D 0; + goto out_put; + } + continue; + } + out_array[j++] =3D aslr_tool__remap_address(aslr, thread, cpumode, addr= ); + } + } + if (sample_type & PERF_SAMPLE_RAW) { + size_t bytes =3D sizeof(u32) + sample->raw_size; + size_t u64_words =3D (bytes + 7) / 8; + + if (i + u64_words > max_i || j + u64_words > max_j) { + ret =3D -EFAULT; + goto out_put; + } + memcpy(&out_array[j], &in_array[i], bytes); + i +=3D u64_words; + j +=3D u64_words; + /* + * TODO: certain raw samples can be remapped, such as + * tracepoints by examining their fields. + */ + pr_debug("Dropping raw samples as possible ASLR leak\n"); + ret =3D 0; + goto out_put; + } + if (sample_type & PERF_SAMPLE_BRANCH_STACK) { + u64 nr; + + CHECK_BOUNDS(1, 1); + nr =3D out_array[j++] =3D in_array[i++]; + + if (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX) + COPY_U64(); /* hw_idx */ + + if (nr > (ULLONG_MAX / 3)) { + ret =3D -EFAULT; + goto out_put; + } + if (nr * 3 > max_i - i || nr * 3 > max_j - j) { + ret =3D -EFAULT; + goto out_put; + } + for (u64 cntr =3D 0; cntr < nr; cntr++) { + out_array[j++] =3D aslr_tool__remap_address(aslr, thread, + sample->cpumode, + in_array[i++]); /* from */ + out_array[j++] =3D aslr_tool__remap_address(aslr, thread, + sample->cpumode, + in_array[i++]); /* to */ + out_array[j++] =3D in_array[i++]; /* flags */ + } + if (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS) { + if (nr > max_i - i || nr > max_j - j) { + ret =3D -EFAULT; + goto out_put; + } + memcpy(&out_array[j], &in_array[i], nr * sizeof(u64)); + i +=3D nr; + j +=3D nr; + /* TODO: confirm branch counters don't leak ASLR information. */ + pr_debug("Dropping sample branch counters as possible ASLR leak\n"); + ret =3D 0; + goto out_put; + } + } + if (sample_type & PERF_SAMPLE_REGS_USER) { + u64 abi; + + COPY_U64(); /* abi */ + abi =3D out_array[j-1]; + if (abi !=3D PERF_SAMPLE_REGS_ABI_NONE) { + u64 nr =3D hweight64(evsel->core.attr.sample_regs_user); + + if (nr > max_i - i || nr > max_j - j) { + ret =3D -EFAULT; + goto out_put; + } + memcpy(&out_array[j], &in_array[i], nr * sizeof(u64)); + i +=3D nr; + j +=3D nr; + } + /* TODO: can this be less conservative? */ + pr_debug("Dropping regs user sample as possible ASLR leak\n"); + ret =3D 0; + goto out_put; + } + if (sample_type & PERF_SAMPLE_STACK_USER) { + u64 size; + + CHECK_BOUNDS(1, 1); + size =3D out_array[j++] =3D in_array[i++]; + if (size > 0) { + size_t u64_words =3D size / 8 + (size % 8 ? 1 : 0); + + if (u64_words > max_i - i || u64_words > max_j - j) { + ret =3D -EFAULT; + goto out_put; + } + memcpy(&out_array[j], &in_array[i], size); + if (size % 8) { + size_t pad =3D 8 - (size % 8); + + memset(((char *)&out_array[j]) + size, 0, pad); + } + i +=3D u64_words; + j +=3D u64_words; + + COPY_U64(); /* dyn_size */ + } + /* TODO: can this be less conservative? */ + pr_debug("Dropping stack user sample as possible ASLR leak\n"); + ret =3D 0; + goto out_put; + } + if (sample_type & PERF_SAMPLE_WEIGHT_TYPE) + COPY_U64(); /* perf_sample_weight */ + if (sample_type & PERF_SAMPLE_DATA_SRC) + COPY_U64(); /* data_src */ + if (sample_type & PERF_SAMPLE_TRANSACTION) + COPY_U64(); /* transaction */ + if (sample_type & PERF_SAMPLE_REGS_INTR) { + u64 abi; + + COPY_U64(); /* abi */ + abi =3D out_array[j-1]; + if (abi !=3D PERF_SAMPLE_REGS_ABI_NONE) { + u64 nr =3D hweight64(evsel->core.attr.sample_regs_intr); + + if (nr > max_i - i || nr > max_j - j) { + ret =3D -EFAULT; + goto out_put; + } + memcpy(&out_array[j], &in_array[i], nr * sizeof(u64)); + i +=3D nr; + j +=3D nr; + } + /* TODO: can this be less conservative? */ + pr_debug("Dropping interrupt register sample as possible ASLR leak\n"); + ret =3D 0; + goto out_put; + } + if (sample_type & PERF_SAMPLE_PHYS_ADDR) { + COPY_U64(); /* phys_addr */ + /* TODO: can this be less conservative? */ + pr_debug("Dropping physical address sample as possible ASLR leak\n"); + ret =3D 0; + goto out_put; + } + if (sample_type & PERF_SAMPLE_CGROUP) + COPY_U64(); /* cgroup */ + if (sample_type & PERF_SAMPLE_DATA_PAGE_SIZE) + COPY_U64(); /* data_page_size */ + if (sample_type & PERF_SAMPLE_CODE_PAGE_SIZE) + COPY_U64(); /* code_page_size */ + + if (sample_type & PERF_SAMPLE_AUX) { + u64 size; + + CHECK_BOUNDS(1, 1); + size =3D out_array[j++] =3D in_array[i++]; + if (size > 0) { + size_t u64_words =3D size / 8 + (size % 8 ? 1 : 0); + + if (u64_words > max_i - i || u64_words > max_j - j) { + ret =3D -EFAULT; + goto out_put; + } + memcpy(&out_array[j], &in_array[i], size); + if (size % 8) { + size_t pad =3D 8 - (size % 8); + + memset(((char *)&out_array[j]) + size, 0, pad); + } + i +=3D u64_words; + j +=3D u64_words; + } + /* TODO: can this be less conservative? */ + pr_debug("Dropping aux sample as possible ASLR leak\n"); + ret =3D 0; + goto out_put; + } + + if (evsel__is_offcpu_event(evsel)) { + /* TODO: can this be less conservative? */ + pr_debug("Dropping off-CPU sample as possible ASLR leak\n"); + ret =3D 0; + goto out_put; + } + + new_event->sample.header.size =3D sizeof(struct perf_event_header) + j * = sizeof(u64); + + perf_sample__init(&new_sample, /*all=3D*/ true); + ret =3D evsel__parse_sample(evsel, new_event, &new_sample); + if (ret) { + perf_sample__exit(&new_sample); + goto out_put; + } + + ret =3D delegate->sample(delegate, new_event, &new_sample, evsel, machine= ); + perf_sample__exit(&new_sample); + +out_put: + thread__put(thread); + return ret; +} + +#undef CHECK_BOUNDS +#undef COPY_U64 +#undef REMAP_U64 + + +static int aslr_tool__process_attr(const struct perf_tool *tool, + union perf_event *event, + struct evlist **pevlist) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct perf_tool *delegate; + union perf_event *new_event; + + del_tool =3D container_of(tool, struct delegate_tool, tool); + aslr =3D container_of(del_tool, struct aslr_tool, tool); + delegate =3D aslr->tool.delegate; + new_event =3D (union perf_event *)aslr->event_copy; + + memcpy(&new_event->attr, &event->attr, event->attr.header.size); + if (new_event->attr.attr.type =3D=3D PERF_TYPE_BREAKPOINT) + new_event->attr.attr.bp_addr =3D 0; /* Conservatively remove addresses.= */ + + return delegate->attr(delegate, new_event, pevlist); +} + +static int skipn(int fd, off_t n) +{ + char buf[4096]; + ssize_t ret; + + while (n > 0) { + ret =3D read(fd, buf, min(n, (off_t)sizeof(buf))); + if (ret <=3D 0) + return ret; + n -=3D ret; + } + + return 0; +} + +static s64 aslr_tool__process_auxtrace(const struct perf_tool *tool __mayb= e_unused, + struct perf_session *session, + union perf_event *event) +{ + if (perf_data__is_pipe(session->data)) { + /* Copy behavior of the stub by reading all pipe data. */ + int err =3D skipn(perf_data__fd(session->data), event->auxtrace.size); + + if (err < 0) + return err; + } + return event->auxtrace.size; +} + +static int aslr_tool__process_auxtrace_info(const struct perf_tool *tool _= _maybe_unused, + struct perf_session *session __maybe_unused, + union perf_event *event __maybe_unused) +{ + return 0; +} + +static int aslr_tool__process_auxtrace_error(const struct perf_tool *tool = __maybe_unused, + struct perf_session *session __maybe_unused, + union perf_event *event __maybe_unused) +{ + return 0; +} + +static void aslr_tool__init(struct aslr_tool *aslr, struct perf_tool *dele= gate) +{ + delegate_tool__init(&aslr->tool, delegate); + aslr->tool.tool.ordered_events =3D true; + + machines__init(&aslr->machines); + + hashmap__init(&aslr->remap_addresses, + remap_addresses__hash, remap_addresses__equal, + /*ctx=3D*/NULL); + hashmap__init(&aslr->top_addresses, + top_addresses__hash, top_addresses__equal, + /*ctx=3D*/NULL); + + aslr->tool.tool.sample =3D aslr_tool__process_sample; + /* read - reads a counter, okay to delegate. */ + aslr->tool.tool.mmap =3D aslr_tool__process_mmap; + aslr->tool.tool.mmap2 =3D aslr_tool__process_mmap2; + aslr->tool.tool.comm =3D aslr_tool__process_comm; + aslr->tool.tool.fork =3D aslr_tool__process_fork; + aslr->tool.tool.exit =3D aslr_tool__process_exit; + /* namesspaces, cgroup, lost, lost_sample, aux, */ + /* itrace_start, aux_output_hw_id, context_switch, throttle, unthrottle */ + /* - no virtual addresses. */ + aslr->tool.tool.ksymbol =3D aslr_tool__process_ksymbol; + /* bpf - no virtual address. */ + aslr->tool.tool.text_poke =3D aslr_tool__process_text_poke; + aslr->tool.tool.attr =3D aslr_tool__process_attr; + /* event_update, tracing_data, finished_round, build_id, id_index, */ + /* event_update, tracing_data, finished_round, build_id, id_index, */ + /* auxtrace_info, auxtrace_error, time_conv, thread_map, cpu_map, */ + /* stat_config, stat, feature, finished_init, bpf_metadata, compressed, */ + /* auxtrace - no virtual addresses. */ + aslr->tool.tool.auxtrace =3D aslr_tool__process_auxtrace; + aslr->tool.tool.auxtrace_info =3D aslr_tool__process_auxtrace_info; + aslr->tool.tool.auxtrace_error =3D aslr_tool__process_auxtrace_error; +} + +struct perf_tool *aslr_tool__new(struct perf_tool *delegate) +{ + struct aslr_tool *aslr =3D zalloc(sizeof(*aslr)); + + if (!aslr) + return NULL; + + aslr_tool__init(aslr, delegate); + return &aslr->tool.tool; +} + +void aslr_tool__delete(struct perf_tool *tool) +{ + struct delegate_tool *del_tool; + struct aslr_tool *aslr; + struct hashmap_entry *cur; + size_t bkt; + + if (!tool) + return; + + del_tool =3D container_of(tool, struct delegate_tool, tool); + aslr =3D container_of(del_tool, struct aslr_tool, tool); + + hashmap__for_each_entry(&aslr->remap_addresses, cur, bkt) { + struct remap_addresses_key *key =3D (struct remap_addresses_key *)cur->p= key; + + if (key) + dso__put(key->dso); + zfree(&cur->pkey); + zfree(&cur->pvalue); + } + hashmap__for_each_entry(&aslr->top_addresses, cur, bkt) { + zfree(&cur->pkey); + zfree(&cur->pvalue); + } + + hashmap__clear(&aslr->remap_addresses); + hashmap__clear(&aslr->top_addresses); + machines__destroy_kernel_maps(&aslr->machines); + machines__exit(&aslr->machines); + free(aslr); +} diff --git a/tools/perf/util/aslr.h b/tools/perf/util/aslr.h new file mode 100644 index 000000000000..ea984d82681f --- /dev/null +++ b/tools/perf/util/aslr.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PERF_ASLR_H +#define __PERF_ASLR_H + +struct perf_tool; + +struct perf_tool *aslr_tool__new(struct perf_tool *delegate); +void aslr_tool__delete(struct perf_tool *aslr); + +#endif /* __PERF_ASLR_H */ --=20 2.54.0.563.g4f69b47b94-goog From nobody Sat Jun 13 09:18:38 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1A4A7382397 for ; Fri, 8 May 2026 08:27:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778228868; cv=none; b=oNRYkWWtrBWzksJVuzo0brXbt4RPiJoytulXXAa4lEHtQoIpx/k+3ZrKf/qg0QYpYR5FbgfLUs1m/emFEBk2nVZjoCt/s8//stgHkBDRjgsjv42jclUbHP9vmR02NwMxNBMjLhmLMJqE7/WBAWFJAuPbXBEWx1+Jm461rZE/IZI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778228868; c=relaxed/simple; bh=7HirJ5qVLpmiuMokh9RUjY1fMBiUDXEHVgWcYLXfZYg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=UPR7lEI3pNxFkgOQ2xxcLejE/bhmzOb0RLZeMWQ/HYUSQG8xffjbtrZECMqbL8sa9aUOhAnHidszirIszRzRLFoJAqCpJF/5kAyC/48CuLAHpAmBKWG2C/YLYUhII1J1M+2N7cX+iKZLgI3D6/BwvrDeYthgbfHRDZMeUc2FbMw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=amkR2y0Q; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="amkR2y0Q" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2ba8013a9e3so3018897eec.0 for ; Fri, 08 May 2026 01:27:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1778228862; x=1778833662; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=d3Q+Xfl919I3XIbYdD+dydaVK24AehCNTI3/JjAsIrU=; b=amkR2y0Q4i6ZGPiUV/nVDkZ/NdH9qJlN092siBwPJQpTVqul8Y5aHILKte78dObDtb zKK/DEWo9a1KH+KRFWsEjGUf6/iHViBoqDsS/Xg8ORkgfyU4dr+vsEKvckW0r9EoMCG+ A8FHIsI9P3Ej+HqlGeLkimEi13lHtqFSY+M/RGbx45MyNA4/S/H9cBcgeKLE8ziQagZj 3qib7iXIZp06isNAAJ2OfJ22r/CUN7A53YtJQ8ic7oI/NQdmQORbsnhzPoVQw594Uhmu oxtRatAcgI0+u8AfIjSRY+Sb6dcO2e5qbgTRMuQy4PlTyBmK3mxTVv9krF7ljop9CSaC Sc9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778228862; x=1778833662; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=d3Q+Xfl919I3XIbYdD+dydaVK24AehCNTI3/JjAsIrU=; b=RLwZWniMA8wtYP2iq35bl/McVC6xrWZtF7hMFUmyOWHPiPknVFsLTMazsXDHOowOpm Semr3bfR1dO/eMD5MisXpFGwyZ8dU9b+5PxRN+KDUvbUGKM87wG8XL/B7wuJcjJehI5H ryLXK19pmlfmK3rxu3yz7rurU/hixGATBiHR9aInultVutTcyOgpepv134LPkWXpu19j 4m65Kr7FTxg0KaQjMXPEan9Y88p8NeSTeTvlPYJJPhPwWSuEizuBHjMGGyNOcN+3aoHZ oPAKdtTOn6NJ6/Me5U4PlWL+6p+H16qYJsnbCXhJJTpCHtOlKDcr2Xy7P9JZx30oKEq/ j5uA== X-Forwarded-Encrypted: i=1; AFNElJ8N9tyL8BUYtLbOeWI/O4lmPT2OOLhjpwY4rCQlFaQfT8EbD4pG0t11O9l+I5sAgCxRLRRFTdmYJYqHKk0=@vger.kernel.org X-Gm-Message-State: AOJu0Yzt+nbP0VUNN83qDJqkj+2Hz5rA2QI3bwvgvt+ZxPFapD5+7V/t C1MyKsmo9rkK5sKVvF5Rzh1uSSODQCbJ9pJlXM3Vrs0ELxjiSP2DJ39rzKMNJLLoOHUteoe4v2R KYlDWtFLOTQ== X-Received: from dlbrx17.prod.google.com ([2002:a05:7022:1711:b0:12c:8ccc:748c]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:f212:b0:129:fe5:117e with SMTP id a92af1059eb24-1319cf5953fmr5721412c88.26.1778228861718; Fri, 08 May 2026 01:27:41 -0700 (PDT) Date: Fri, 8 May 2026 01:27:25 -0700 In-Reply-To: <20260508082726.2795191-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260506004546.3140141-1-irogers@google.com> <20260508082726.2795191-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.563.g4f69b47b94-goog Message-ID: <20260508082726.2795191-6-irogers@google.com> Subject: [PATCH v6 5/6] perf test: Add inject ASLR test From: Ian Rogers To: irogers@google.com, acme@kernel.org, gmx@google.com, james.clark@linaro.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a new shell test `inject_aslr.sh` to verify the `perf inject --aslr` feature. The test covers: - Basic address remapping for user space samples. - Pipe mode coverage for `perf record` piped into `perf inject --aslr`. - Callchain address remapping. - Consistency of `perf report` output before and after injection. - Pipe mode report consistency. - Dropping of samples that leak ASLR info (physical addresses). - Kernel address remapping (utilizing a dedicated kernel-intensive VFS dd w= orkload to guarantee continuous timer interrupts sampling flow inside kernel priv= ilege states). - Kernel report consistency with address normalization. The test suite is hardened with global 'set -o pipefail' assertions to catch pipeline failures, stream-consuming awk processors to handle SIGPIPE signal= s, and a dedicated pipe output scenario validating raw 'perf inject -o -' stdo= ut streams. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers --- v6: Refactor kernel-space sampling test cases to utilize a dedicated system-call intensive VFS dd workload (kprog) instead of purely userspace-bound tight loops, guaranteeing high-density kernel privilege state sampling streams and eliminating intermittent execution flakiness dropouts. v5: Harden test suite verification pipelines by upgrading report checks to strict sorted line-by-line diff comparisons to accommodate remapped pointer shifts. Append || true fallback operators to grep-v filtering pipelines to prevent the shell test from spuriously aborting under set -o pipefail on empty inputs, ensuring graceful failure checks trigger correctly. v4: Reorder set -e/pipefail to prevent temp file leakage in root directory on unprivileged record failures when run as root. Ensure grep report filters have || true suffixes to avoid aborts under pipefail. Add comprehensive pipe stdout injection attributes validation case. v3: Harden script with pipefail, SIGPIPE awk pipeline fixes, callchain empty data asserts, baseline sample verification, and grep report abort protections. Reorder set -e/pipefail to prevent stack leaks in mktemp failures. v2: Add sum comparison for kernel overhead and 32-bit math corrections. Add awk with gsub for trailing dots and brackets normalizations. Trap EXIT, prevent race conditions and avoid hardcoded perf binary. --- tools/perf/tests/shell/inject_aslr.sh | 460 ++++++++++++++++++++++++++ 1 file changed, 460 insertions(+) create mode 100755 tools/perf/tests/shell/inject_aslr.sh diff --git a/tools/perf/tests/shell/inject_aslr.sh b/tools/perf/tests/shell= /inject_aslr.sh new file mode 100755 index 000000000000..6363a0f69d2b --- /dev/null +++ b/tools/perf/tests/shell/inject_aslr.sh @@ -0,0 +1,460 @@ +#!/bin/bash +# perf inject --aslr test +# SPDX-License-Identifier: GPL-2.0 + +set -e +set -o pipefail + +shelldir=3D$(dirname "$0") +# shellcheck source=3Dlib/perf_has_symbol.sh +. "${shelldir}"/lib/perf_has_symbol.sh + +sym=3D"noploop" + +skip_test_missing_symbol ${sym} + +# Create global temp directory +temp_dir=3D$(mktemp -d /tmp/perf-test-aslr.XXXXXXXXXX) +data=3D"${temp_dir}/perf.data" +data2=3D"${temp_dir}/perf.data2" + +prog=3D"perf test -w noploop" +[ "$(uname -m)" =3D "s390x" ] && prog=3D"$prog 3" +err=3D0 +kprog=3D"dd if=3D/dev/zero of=3D/dev/null bs=3D1M count=3D500" + +cleanup() { + # Check if temp_dir is set and looks sane before removing + if [[ "${temp_dir}" =3D~ ^/tmp/perf-test-aslr\. ]]; then + rm -rf "${temp_dir}" + fi +} + +trap_cleanup() { + cleanup + exit 1 +} + +trap cleanup EXIT +trap trap_cleanup TERM INT + +get_noploop_addr() { + local file=3D$1 + perf script -i "$file" | awk ' + BEGIN { found=3D0 } + { + for (i=3D1; i<=3DNF; i++) { + if ($i ~ /noploop\+/) { + if (!found) { + print $(i-1) + found=3D1 + } + } + } + }' +} + +test_basic_aslr() { + echo "Test basic ASLR remapping" + local data + data=3D$(mktemp "${temp_dir}/perf.data.basic.XXXXXX") + local data2 + data2=3D$(mktemp "${temp_dir}/perf.data2.basic.XXXXXX") + + perf record -e task-clock:u -o "${data}" ${prog} + perf inject -v --aslr -i "${data}" -o "${data2}" + + orig_addr=3D$(get_noploop_addr "${data}") + new_addr=3D$(get_noploop_addr "${data2}") + + echo "Basic ASLR: orig_addr=3D$orig_addr, new_addr=3D$new_addr" + + if [ -z "$orig_addr" ]; then + echo "Basic ASLR test [Failed - no noploop samples in original file]" + err=3D1 + elif [ -z "$new_addr" ]; then + echo "Basic ASLR test [Failed - could not find remapped address]" + err=3D1 + elif [ "$orig_addr" =3D "$new_addr" ]; then + echo "Basic ASLR test [Failed - addresses are not remapped]" + err=3D1 + else + echo "Basic ASLR test [Success]" + fi +} + +test_pipe_aslr() { + echo "Test pipe mode ASLR remapping" + local data + data=3D$(mktemp "${temp_dir}/perf.data.pipe.XXXXXX") + local data2 + data2=3D$(mktemp "${temp_dir}/perf.data2.pipe.XXXXXX") + + # Use tee to save the original pipe data for comparison + perf record -e task-clock:u -o - ${prog} | tee "${data}" | perf inject -= -aslr -o "${data2}" + + orig_addr=3D$(get_noploop_addr "${data}") + new_addr=3D$(get_noploop_addr "${data2}") + + echo "Pipe ASLR: orig_addr=3D$orig_addr, new_addr=3D$new_addr" + + if [ -z "$orig_addr" ]; then + echo "Pipe ASLR test [Failed - no noploop samples in original file]" + err=3D1 + elif [ -z "$new_addr" ]; then + echo "Pipe ASLR test [Failed - could not find remapped address]" + err=3D1 + elif [ "$orig_addr" =3D "$new_addr" ]; then + echo "Pipe ASLR test [Failed - addresses are not remapped]" + err=3D1 + else + echo "Pipe ASLR test [Success]" + fi +} + +test_callchain_aslr() { + echo "Test Callchain ASLR remapping" + local data + data=3D$(mktemp "${temp_dir}/perf.data.callchain.XXXXXX") + local data2 + data2=3D$(mktemp "${temp_dir}/perf.data2.callchain.XXXXXX") + + perf record -g -e task-clock:u -o "${data}" ${prog} + perf inject --aslr -i "${data}" -o "${data2}" + + orig_addr=3D$(get_noploop_addr "${data}") + new_addr=3D$(get_noploop_addr "${data2}") + + echo "Callchain ASLR: orig_addr=3D$orig_addr, new_addr=3D$new_addr" + + if [ -z "$orig_addr" ]; then + echo "Callchain ASLR test [Failed - no noploop samples in original fil= e]" + err=3D1 + elif [ -z "$new_addr" ]; then + echo "Callchain ASLR test [Failed - could not find remapped address]" + err=3D1 + elif [ "$orig_addr" =3D "$new_addr" ]; then + echo "Callchain ASLR test [Failed - addresses are not remapped]" + err=3D1 + else + # Extract callchain addresses (indented lines starting with hex addres= ses) + orig_callchain=3D$(perf script -i "${data}" | awk '/^[[:space:]]+[0-9a= -f]+/ {print $1}') + new_callchain=3D$(perf script -i "${data2}" | awk '/^[[:space:]]+[0-9a= -f]+/ {print $1}') + + if [ -z "$orig_callchain" ]; then + echo "Callchain ASLR test [Failed - no callchain samples in original= file]" + err=3D1 + elif [ -z "$new_callchain" ]; then + echo "Callchain ASLR test [Failed - callchain data was dropped]" + err=3D1 + elif [ "$orig_callchain" =3D "$new_callchain" ]; then + echo "Callchain ASLR test [Failed - callchain addresses were not rem= apped]" + err=3D1 + else + echo "Callchain ASLR test [Success]" + fi + fi +} + +test_report_aslr() { + echo "Test perf report consistency" + local data + data=3D$(mktemp "${temp_dir}/perf.data.report.XXXXXX") + local data2 + data2=3D$(mktemp "${temp_dir}/perf.data2.report.XXXXXX") + local data_clean + data_clean=3D$(mktemp "${temp_dir}/perf.data.clean.XXXXXX") + + perf record -e task-clock:u -o "${data}" ${prog} + # Use -b to inject build-ids and force ordered events processing in both + perf inject -b -i "${data}" -o "${data_clean}" + perf inject -v -b --aslr -i "${data}" -o "${data2}" + + local report1=3D"${temp_dir}/report1" + local report2=3D"${temp_dir}/report2" + local report1_clean=3D"${temp_dir}/report1.clean" + local report2_clean=3D"${temp_dir}/report2.clean" + local diff_file=3D"${temp_dir}/diff" + + perf report -i "${data_clean}" --stdio > "${report1}" + perf report -i "${data2}" --stdio > "${report2}" + + # Strip headers and compare lines with percentages + grep '%' "${report1}" | grep -v '^#' | sort > "${report1_clean}" || true + grep '%' "${report2}" | grep -v '^#' | sort > "${report2_clean}" || true + + diff -u -w "${report1_clean}" "${report2_clean}" > "${diff_file}" || true + + if [ ! -s "${report1_clean}" ]; then + echo "Report ASLR test [Failed - no samples captured]" + err=3D1 + elif [ -s "${diff_file}" ]; then + echo "Report ASLR test [Failed - reports differ]" + echo "Showing first 20 lines of diff:" + head -n 20 "${diff_file}" + err=3D1 + else + echo "Report ASLR test [Success]" + fi +} + +test_pipe_report_aslr() { + echo "Test pipe mode perf report consistency" + local data + data=3D$(mktemp "${temp_dir}/perf.data.pipe_report.XXXXXX") + local data2 + data2=3D$(mktemp "${temp_dir}/perf.data2.pipe_report.XXXXXX") + local data_clean + data_clean=3D$(mktemp "${temp_dir}/perf.data.clean.XXXXXX") + + # Use tee to save the original pipe data, then process it with inject -b + perf record -e task-clock:u -o - ${prog} | \ + tee "${data}" | \ + perf inject -b --aslr -o "${data2}" + perf inject -b -i "${data}" -o "${data_clean}" + + local report1=3D"${temp_dir}/report1" + local report2=3D"${temp_dir}/report2" + local report1_clean=3D"${temp_dir}/report1.clean" + local report2_clean=3D"${temp_dir}/report2.clean" + local diff_file=3D"${temp_dir}/diff" + + perf report -i "${data_clean}" --stdio > "${report1}" + perf report -i "${data2}" --stdio > "${report2}" + + # Strip headers and compare lines with percentages + grep '%' "${report1}" | grep -v '^#' | sort > "${report1_clean}" || true + grep '%' "${report2}" | grep -v '^#' | sort > "${report2_clean}" || true + + diff -u -w "${report1_clean}" "${report2_clean}" > "${diff_file}" || true + + if [ ! -s "${report1_clean}" ]; then + echo "Pipe Report ASLR test [Failed - no samples captured]" + err=3D1 + elif [ -s "${diff_file}" ]; then + echo "Pipe Report ASLR test [Failed - reports differ]" + echo "Showing first 20 lines of diff:" + head -n 20 "${diff_file}" + err=3D1 + else + echo "Pipe Report ASLR test [Success]" + fi +} + +test_pipe_out_report_aslr() { + echo "Test pipe output mode perf report consistency" + local data + data=3D$(mktemp "${temp_dir}/perf.data.pipe_out_report.XXXXXX") + local data_clean + data_clean=3D$(mktemp "${temp_dir}/perf.data.clean.XXXXXX") + + perf record -e task-clock:u -o "${data}" ${prog} + perf inject -b -i "${data}" -o "${data_clean}" + + local report1=3D"${temp_dir}/report1" + local report2=3D"${temp_dir}/report2" + local report1_clean=3D"${temp_dir}/report1.clean" + local report2_clean=3D"${temp_dir}/report2.clean" + local diff_file=3D"${temp_dir}/diff" + + perf report -i "${data_clean}" --stdio > "${report1}" + perf inject -b --aslr -i "${data}" -o - | perf report -i - --stdio > "${= report2}" + + # Strip headers and compare lines with percentages + grep '%' "${report1}" | grep -v '^#' | sort > "${report1_clean}" || true + grep '%' "${report2}" | grep -v '^#' | sort > "${report2_clean}" || true + + diff -u -w "${report1_clean}" "${report2_clean}" > "${diff_file}" || true + + if [ ! -s "${report1_clean}" ]; then + echo "Pipe Output Report ASLR test [Failed - no samples captured]" + err=3D1 + elif [ -s "${diff_file}" ]; then + echo "Pipe Output Report ASLR test [Failed - reports differ]" + echo "Showing first 20 lines of diff:" + head -n 20 "${diff_file}" + err=3D1 + else + echo "Pipe Output Report ASLR test [Success]" + fi +} + +test_dropped_samples() { + echo "Test dropped samples (phys-data)" + local data + data=3D$(mktemp "${temp_dir}/perf.data.dropped.XXXXXX") + local data2 + data2=3D$(mktemp "${temp_dir}/perf.data2.dropped.XXXXXX") + + # Check if --phys-data is supported by recording a short run + if ! perf record -e task-clock:u --phys-data -o "${data}" -- sleep 0.1 >= /dev/null 2>&1; then + echo "Skipping dropped samples test as --phys-data is not supported" + return + fi + + perf record -e task-clock:u --phys-data -o "${data}" ${prog} + perf inject --aslr -i "${data}" -o "${data2}" + + # Verify that the original file actually contained samples! + orig_samples=3D$(perf script -i "${data}" | wc -l) + if [ "$orig_samples" -eq 0 ]; then + echo "Dropped samples test [Failed - no samples in original file]" + err=3D1 + else + # Verify that samples are dropped. + samples_count=3D$(perf script -i "${data2}" | wc -l) + + if [ "$samples_count" -gt 0 ]; then + echo "Dropped samples test [Failed - samples were not dropped]" + err=3D1 + else + echo "Dropped samples test [Success]" + fi + fi +} + +test_kernel_aslr() { + echo "Test kernel ASLR remapping" + local kdata + kdata=3D$(mktemp "${temp_dir}/perf.data.kernel.XXXXXX") + local kdata2 + kdata2=3D$(mktemp "${temp_dir}/perf.data2.kernel.XXXXXX") + local log_file + log_file=3D$(mktemp "${temp_dir}/kernel_record.log.XXXXXX") + + # Try to record kernel samples + if ! perf record -e task-clock:k -o "${kdata}" ${kprog} > "${log_file}" = 2>&1; then + echo "Skipping kernel ASLR test as recording failed (maybe no permissi= ons)" + return + fi + + # Check for warning about kernel map restriction + if grep -q "Couldn't record kernel reference relocation symbol" "${log_f= ile}"; then + echo "Skipping kernel ASLR test as kernel map could not be recorded (p= ermissions restricted)" + return + fi + + perf inject -v --aslr -i "${kdata}" -o "${kdata2}" + + # Check if kernel addresses are remapped. + # Find the field that ends with :k: (the event name) and take the next f= ield! + orig_addr=3D$(perf script -i "${kdata}" | awk ' + BEGIN { found=3D0 } + { + for (i=3D1; i "${log_file}" = 2>&1; then + echo "Skipping kernel report test as recording failed (maybe no permis= sions)" + return + fi + + # Check for warning about kernel map restriction + if grep -q "Couldn't record kernel reference relocation symbol" "${log_f= ile}"; then + echo "Skipping kernel report test as kernel map could not be recorded = (permissions restricted)" + return + fi + + # Use -b to inject build-ids and force ordered events processing in both + perf inject -b -i "${kdata}" -o "${data_clean}" + perf inject -v -b --aslr -i "${kdata}" -o "${kdata2}" + + local report1=3D"${temp_dir}/report_kernel1" + local report2=3D"${temp_dir}/report_kernel2" + local report1_clean=3D"${temp_dir}/report_kernel1.clean" + local report2_clean=3D"${temp_dir}/report_kernel2.clean" + + perf report -i "${data_clean}" --stdio > "${report1}" + perf report -i "${kdata2}" --stdio > "${report2}" + + # Strip headers and compare lines with percentages + grep '%' "${report1}" | grep -v '^#' > "${report1_clean}" || true + grep '%' "${report2}" | grep -v '^#' > "${report2_clean}" || true + + # Normalize kernel DSOs and addresses in clean reports + # This allows kernel modules to be either a module or kernel.kallsyms + local report1_norm=3D"${temp_dir}/report_kernel1.norm" + local report2_norm=3D"${temp_dir}/report_kernel2.norm" + local diff_file=3D"${temp_dir}/diff_kernel" + + grep -v -E '0x[0-9a-f]{8,}|0000000000000000' "${report1_clean}" | \ + awk '{gsub(/\[[a-zA-Z0-9_.-]{2,}\](\.[a-zA-Z0-9_]+)?/, "[kernel]", $0)= ; print}' | \ + sort > "${report1_norm}" || true + grep -v -E '0x[0-9a-f]{8,}|0000000000000000' "${report2_clean}" | \ + awk '{gsub(/\[[a-zA-Z0-9_.-]{2,}\](\.[a-zA-Z0-9_]+)?/, "[kernel]", $0)= ; print}' | \ + sort > "${report2_norm}" || true + + diff -u -w "${report1_norm}" "${report2_norm}" > "${diff_file}" || true + + if [ ! -s "${report1_norm}" ]; then + echo "Kernel Report ASLR test [Failed - no samples captured]" + err=3D1 + elif [ -s "${diff_file}" ]; then + echo "Kernel Report ASLR test [Failed - reports differ]" + echo "Showing first 20 lines of diff:" + head -n 20 "${diff_file}" + err=3D1 + else + echo "Kernel Report ASLR test [Success]" + fi +} + +test_basic_aslr +test_pipe_aslr +test_callchain_aslr +test_report_aslr +test_pipe_report_aslr +test_pipe_out_report_aslr +test_dropped_samples +test_kernel_aslr +test_kernel_report_aslr + +cleanup +exit $err --=20 2.54.0.563.g4f69b47b94-goog From nobody Sat Jun 13 09:18:38 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E2078384233 for ; Fri, 8 May 2026 08:27:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778228876; cv=none; b=allrQn7kuivZkficaQwPNKRfR2NB9NxYNpKnQRDq9Z7gM/aNkJJIJVuqaq4RG51oZa5zwRYzwaEavMmQKDPXU1OL70tJXhhtYm1eqirQSJfgomLzcNO6x4vP9yC0d1zzE9xWRF+KA+h5kon7q2qw1DcDyfsy9fRYDcHnZuHYIoM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778228876; c=relaxed/simple; bh=YV+jQTzr5U8wqJiMkFv23YvtpmRbZfl0p0/Y4dsUoP0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=nikSQ2Q5bH/nKiZBpPtCEVxUdzQCorJhrjLxTgX129LZsE8nl+NWkJ+CF6noDCWr0j1fHonjFsi73UwFSB3oLp+//aWIo+8GWP2JaeOP0mYi43H/Q3Jh/ARdQEPsy+nBqUJxkJxCzh3ohln050y9QZy65ir2YG+i43yOJUdGna4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Y8tU9AL2; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Y8tU9AL2" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2ee34588671so2509871eec.0 for ; Fri, 08 May 2026 01:27:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1778228864; x=1778833664; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=TaCzln3oP7GCJBDx8P3UztqbyaF0B2xh0/jsYh7hZZo=; b=Y8tU9AL2IqY4y8PuLr3kL8/t6uBLCJ3M1BqbodF4WUfjP72Xpp95CJDU/j/Rw4ab2q XLTpzm8EDI8P1B3cyr5b2Ef1Wm5Cpq0X5gJj7mU3fovEupaNpF9iFTEYL8Lh6AjIQqsq +/IOyGtjqNUFK/lCCJcY1JeAmcRy0F1HkpR7JgDpaGrQVB6893NVOxDiHDwmxrDjxki6 BB0gIqYPzPSQmfMcsO0a4cfz6oY73ex//8c/bbkGWmK2/8bnq4AaqjR0B3Kq13ZJzasr IV80Gx6/Fh6vQfHAjqwNa9YClKDbQwUapqzT7pjTNHSauo/mtPaqQ225ZCSN/t6og+kC UoLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778228864; x=1778833664; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=TaCzln3oP7GCJBDx8P3UztqbyaF0B2xh0/jsYh7hZZo=; b=Fts7EryVn0djY0Ic+WDSapT0Z+kBUCZozrta78r3kOxkNzCLWUhb8sxbeCi2b3RocP JbFbdXet5woTMWkB0/iomCHwAUR2nMLAdM4zyjS+f/W04xKYn1wGPNvJyXCjVKK5B56m ePx+f+ALidP663LNbnU8K1/MlIkPITCc7pmw7kAfk2qjiP6gTkjM4FE0YSVtWX+i4vTA S8rfjSUyBxv0gluMDa64o8DegaAH2LkndY3tMLZ84FVrHWSQyaG4jxs4x2+3wPSr4qGB yQ+ORvGMiDfX/1sqh0176RmF3UBY6RDlycEm/xFTTsH1T1rkWVC7UjuGPWl8BjZHorqy Y8tw== X-Forwarded-Encrypted: i=1; AFNElJ863n/6glqjzXNI2QrnvXCkx5DHqP9xFoV6twZUdPhZue5EFPy5AaD/0YNfOSGaMiR8JrCxw8kfMQ9Np2s=@vger.kernel.org X-Gm-Message-State: AOJu0YyRyDxAdlBCIRoWgwetJkEz8StRCcNPG9AcesXTKTpwFydF7NEc eqdmvEB+dBQP0vGY3PwxwBlu8y9Mxl9p4giIcE2tQmjoyZO0f8bZsuwPyx09P0DPS2pDi4v2YtW /SuEUWGiyZg== X-Received: from dybml13.prod.google.com ([2002:a05:7301:150d:b0:2d9:8c75:b19e]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:6d03:b0:2de:3022:a459 with SMTP id 5a478bee46e88-2f549f7c266mr5651619eec.21.1778228863854; Fri, 08 May 2026 01:27:43 -0700 (PDT) Date: Fri, 8 May 2026 01:27:26 -0700 In-Reply-To: <20260508082726.2795191-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260506004546.3140141-1-irogers@google.com> <20260508082726.2795191-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.563.g4f69b47b94-goog Message-ID: <20260508082726.2795191-7-irogers@google.com> Subject: [PATCH v6 6/6] perf aslr: Strip sample registers From: Ian Rogers To: irogers@google.com, acme@kernel.org, gmx@google.com, james.clark@linaro.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When the ASLR tracking tool encounters sample events containing user or interrupt register dumps (PERF_SAMPLE_REGS_USER / PERF_SAMPLE_REGS_INTR), it previously dropped the entire sample event conservatively to prevent absolute virtual memory pointers leakage embedded inside raw register frames. If a trace session was recorded with register collection flags enabled, this resulted in 100% sample drop rates, and this happened by default for ARM64. Refactor the ASLR tool to strip out obly the register dump payload words from PERF_RECORD_SAMPLE event streams, automatically shrinking the output sample header size. Incoming PERF_RECORD_ATTR events are scrubbed up front to clear the register dump bit selection flags and masks, and output sample ABI words are safely overwritten to PERF_SAMPLE_REGS_ABI_NONE. This keeps downstream evsel parsers perfectly synchronized while retaining full, comprehensive sample profiles completely clear of secret register data frames. Verification parity is established inside inject_aslr.sh via a dedicated sorted report diff comparison validation case proving zero starvation and absolute secrecy. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers --- tools/perf/builtin-inject.c | 11 ++++++ tools/perf/tests/shell/inject_aslr.sh | 51 +++++++++++++++++++++++++++ tools/perf/util/aslr.c | 27 +++++++------- 3 files changed, 75 insertions(+), 14 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 51dcf248b653..7a17ce019657 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -2463,6 +2463,17 @@ static int __cmd_inject(struct perf_inject *inject) } } =20 + if (inject->aslr) { + struct evsel *evsel; + + evlist__for_each_entry(session->evlist, evsel) { + evsel__reset_sample_bit(evsel, REGS_USER); + evsel__reset_sample_bit(evsel, REGS_INTR); + evsel->core.attr.sample_regs_user =3D 0; + evsel->core.attr.sample_regs_intr =3D 0; + } + } + =20 =20 session->header.data_offset =3D output_data_offset; diff --git a/tools/perf/tests/shell/inject_aslr.sh b/tools/perf/tests/shell= /inject_aslr.sh index 6363a0f69d2b..323782c3802d 100755 --- a/tools/perf/tests/shell/inject_aslr.sh +++ b/tools/perf/tests/shell/inject_aslr.sh @@ -446,6 +446,56 @@ test_kernel_report_aslr() { fi } =20 +test_regs_stripping() { + echo "Test user register stripping" + local rdata=3D"${temp_dir}/perf.data.regs" + local rdata2=3D"${temp_dir}/perf.data.regs.injected" + local rdata_clean=3D"${temp_dir}/perf.data.regs.clean" + + if ! perf record --user-regs -o "${rdata}" ${prog} > /dev/null 2>&1; then + echo "Skipping user registers test as recording failed (unsupported fl= ag/platform)" + return + fi + + perf inject -b -i "${rdata}" -o "${rdata_clean}" + perf inject -v -b --aslr -i "${rdata}" -o "${rdata2}" + + local report1=3D"${temp_dir}/report_regs1" + local report2=3D"${temp_dir}/report_regs2" + local report1_clean=3D"${temp_dir}/report_regs1.clean" + local report2_clean=3D"${temp_dir}/report_regs2.clean" + local diff_file=3D"${temp_dir}/diff_regs" + + perf report -i "${rdata_clean}" --stdio > "${report1}" 2>/dev/null || tr= ue + perf report -i "${rdata2}" --stdio > "${report2}" 2>/dev/null || true + + grep '%' "${report1}" | grep -v '^#' | grep -v -E '0x[0-9a-f]{8,}|000000= 0000000000' | sort > "${report1_clean}" || true + grep '%' "${report2}" | grep -v '^#' | grep -v -E '0x[0-9a-f]{8,}|000000= 0000000000' | sort > "${report2_clean}" || true + + diff -u -w "${report1_clean}" "${report2_clean}" > "${diff_file}" || true + + if [ ! -s "${report1_clean}" ]; then + echo "User registers stripping test [Failed - profile trace starved/em= pty]" + err=3D1 + return + elif [ -s "${diff_file}" ]; then + echo "User registers stripping test [Failed - report parsing differs]" + echo "Showing first 20 lines of diff:" + head -n 20 "${diff_file}" + err=3D1 + return + fi + + local script_dump=3D"${temp_dir}/script_regs_dump" + perf script -D -i "${rdata2}" > "${script_dump}" 2>/dev/null || true + if grep -q "PERF_SAMPLE_REGS_USER" "${script_dump}"; then + echo "User registers stripping test [Failed - register dumps still pre= sent]" + err=3D1 + else + echo "User registers stripping test [Success]" + fi +} + test_basic_aslr test_pipe_aslr test_callchain_aslr @@ -455,6 +505,7 @@ test_pipe_out_report_aslr test_dropped_samples test_kernel_aslr test_kernel_report_aslr +test_regs_stripping =20 cleanup exit $err diff --git a/tools/perf/util/aslr.c b/tools/perf/util/aslr.c index 09b7f2f8fb85..e5369589a733 100644 --- a/tools/perf/util/aslr.c +++ b/tools/perf/util/aslr.c @@ -751,18 +751,13 @@ static int aslr_tool__process_sample(const struct per= f_tool *tool, union perf_ev if (abi !=3D PERF_SAMPLE_REGS_ABI_NONE) { u64 nr =3D hweight64(evsel->core.attr.sample_regs_user); =20 - if (nr > max_i - i || nr > max_j - j) { + if (nr > max_i - i) { ret =3D -EFAULT; goto out_put; } - memcpy(&out_array[j], &in_array[i], nr * sizeof(u64)); i +=3D nr; - j +=3D nr; + out_array[j-1] =3D PERF_SAMPLE_REGS_ABI_NONE; } - /* TODO: can this be less conservative? */ - pr_debug("Dropping regs user sample as possible ASLR leak\n"); - ret =3D 0; - goto out_put; } if (sample_type & PERF_SAMPLE_STACK_USER) { u64 size; @@ -806,18 +801,13 @@ static int aslr_tool__process_sample(const struct per= f_tool *tool, union perf_ev if (abi !=3D PERF_SAMPLE_REGS_ABI_NONE) { u64 nr =3D hweight64(evsel->core.attr.sample_regs_intr); =20 - if (nr > max_i - i || nr > max_j - j) { + if (nr > max_i - i) { ret =3D -EFAULT; goto out_put; } - memcpy(&out_array[j], &in_array[i], nr * sizeof(u64)); i +=3D nr; - j +=3D nr; + out_array[j-1] =3D PERF_SAMPLE_REGS_ABI_NONE; } - /* TODO: can this be less conservative? */ - pr_debug("Dropping interrupt register sample as possible ASLR leak\n"); - ret =3D 0; - goto out_put; } if (sample_type & PERF_SAMPLE_PHYS_ADDR) { COPY_U64(); /* phys_addr */ @@ -907,6 +897,15 @@ static int aslr_tool__process_attr(const struct perf_t= ool *tool, if (new_event->attr.attr.type =3D=3D PERF_TYPE_BREAKPOINT) new_event->attr.attr.bp_addr =3D 0; /* Conservatively remove addresses.= */ =20 + if (new_event->attr.attr.sample_type & PERF_SAMPLE_REGS_USER) { + new_event->attr.attr.sample_type &=3D ~PERF_SAMPLE_REGS_USER; + new_event->attr.attr.sample_regs_user =3D 0; + } + if (new_event->attr.attr.sample_type & PERF_SAMPLE_REGS_INTR) { + new_event->attr.attr.sample_type &=3D ~PERF_SAMPLE_REGS_INTR; + new_event->attr.attr.sample_regs_intr =3D 0; + } + return delegate->attr(delegate, new_event, pevlist); } =20 --=20 2.54.0.563.g4f69b47b94-goog