From nobody Sat Jun 13 19:02:41 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 859AF2153D8 for ; Wed, 6 May 2026 00:45:59 +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=1778028360; cv=none; b=SjPw1qJrcccqQ2ww2rFPlt3MAVmlLdgGnzNTzkJjewgClxy7TTdbz1jtZwYmUL8Lx17gVXGAS0Yg35auHYVwaBVSeGtYLWcqi2jIJI2Z899QJLDXXR/YXEJUmbUIqeDS/2462vOhweQVS9uLMVtSiz4HXZMFXVVEGocgB4jbVSo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778028360; c=relaxed/simple; bh=c9ejlYjPiFjsPhz/yehQpWJw1IyWFFw1CL0F74SsqP4=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=QII23IvDQz0gycJK6OxIpeI+1L1+VD1ZqTDzIezCOzj9f4tGITapkW7eTmQCc5pQh1WkgJ7TVrH3EfVIhA4h7tp7kHjHUGCs0FCR8BAHYseM897mDUhv5b1imqLbuD/61zJa71DieTXqdYuRNP1f78NcIRMb73nzrDVqU3hhpOk= 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=b28r90HH; 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="b28r90HH" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-1275c6fc58aso11778200c88.0 for ; Tue, 05 May 2026 17:45:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1778028359; x=1778633159; 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=Vkt4oU+hEVEUN5xjbaGhASHDDjx1y3R0jVDB1yo0F0U=; b=b28r90HHVBPJVxhM9ShzY05Z0qsNsFCFehWaCgnNHn7SdcqZBSj59A65HZgukQNSnR VG40RACZxVwYXt9C4958cCU0xdsfy2nl+sxnuX9e7133RqSOYseExyrZ4u4qV2EAgrtN zng99nHfIFk4DjZq6bFr8IxBe8w1QiJJXYP+u5P2aDGQxCyDdH0/xFbyAnQZfFAyarcC +vLJpNjK4auTs0ZRV3KhBS/QU1R4x3XRPaOZLLjPAMncxQdpqX4D6ZvZeq1nWOZNYyMA x4KrF9qmfm/CxSzxU+KhvjrEtYXbp5oSpN+BBk2R/35dmMqM197+GNZRd/Q+Sf0nmIXz npog== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778028359; x=1778633159; 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=Vkt4oU+hEVEUN5xjbaGhASHDDjx1y3R0jVDB1yo0F0U=; b=flrPrzm4tbt8MvniXKBvn6zcno7oJA90tAcNfnL/ExazxyN3oJfxInYbhiewjJxer4 Xti7hQsJfSjuFYjyY/7h+wlvbKbKX77dRTstlhLRJvEClorF8mYz8zprt8wdOYkHQNwr AdtBQdmFtoztytts2eKNgMyTMqNbdy6MieUroyxosrM04EUz6m8vC8l6eeFOCg2QJB/I ZlAGsJozLn4bqStTPXvqltwXN/7VxO2B3dn5Q8AZQTgZkAvIOSUWgGS09pEa/eBxtGx5 pq4CJlla6D+gJLdrG6oF3gtb+aQoBTp3HYwqaXXu7zW6EYrR6LYDJwz2C1UajQspCmpu VTjA== X-Forwarded-Encrypted: i=1; AFNElJ+D3VKPlnsuMRgJ9X+7FoCygzbF74IiN3ji5iawECR4PwXe60AsPnfIvFkRlDtSud7vBiR6SNpqvCiyv30=@vger.kernel.org X-Gm-Message-State: AOJu0YwOk0CNDQ/2VSE5U0PcOYmptvL9q8HGnj5lTO3FsUuRqTnBfztg +D1vBcyX5WEyMSeLy0mX+hFWS+mOh/mVdoxa9JuryahaVObv2Iux1sPIxpB5mk3Dqn+U81iCB/6 NivVBZKtETw== X-Received: from dlbrs9.prod.google.com ([2002:a05:7022:f689:b0:12b:fba9:5eb0]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:f005:b0:128:d967:4678 with SMTP id a92af1059eb24-1318e812013mr719263c88.23.1778028358417; Tue, 05 May 2026 17:45:58 -0700 (PDT) Date: Tue, 5 May 2026 17:45:42 -0700 In-Reply-To: <20260506004546.3140141-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: <20260504072937.2103453-1-irogers@google.com> <20260506004546.3140141-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260506004546.3140141-2-irogers@google.com> Subject: [PATCH v5 1/5] perf sched: Add missing mmap2 handler in timehist From: Ian Rogers To: irogers@google.com, acme@kernel.org, gmx@google.com, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, 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: 5bbfec0ad93c ("perf sched: Implement timehist option") 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.545.g6539524ca2-goog From nobody Sat Jun 13 19:02:41 2026 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.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 66D1023E320 for ; Wed, 6 May 2026 00:46:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778028363; cv=none; b=KxXGuKJTOyWf6fLIuHXlO3lBi3OUDaBIGvEj9xClgH+Lyz8YyFiV2fhMFx+ySmVv/D3Z7z0FcpQHUn1+b2NHZGO89miSMEq3hre731T/7mOsLpGgsks/MayK8pjocFWdMi1SC7fWCuHPFp7dftoBNTYEsentDwKJTyD241jxzC8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778028363; c=relaxed/simple; bh=oO73p1xRPTCaH7zWY9uPtQL6ZrNpPDEb1CrIWcRHrM0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=AaMliShFbu9rzwPdwHQm4+R6ESMMJ4/6dUlLjGXjQ0LB5TA5olpgEDZSpakAfzPVmuC7fE2oO39BE/cUSZu2uXhrCjy6vdlErwd9/eGpqpyPHJ4aDbRkWqPQRuSDgsqzTj+HGiAM3cMyqKxX5xN45XzpzheCWH8Nr7cknz0T0dM= 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=BNoeC9UY; arc=none smtp.client-ip=209.85.216.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="BNoeC9UY" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-365161a9de4so3669061a91.1 for ; Tue, 05 May 2026 17:46:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1778028361; x=1778633161; 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=hdtzf9eHR5G9u38wQE27Ez1yBHseJb6vJmHAUnBL/S4=; b=BNoeC9UYsmqxQVls5dd55Cya7lspBnfxOQ5ZcO4LrzgZiEvzy4vhF8+uObx9XoFq8W /iHWXrP147wYYFYwOm6bOxbwV27vTVGBMPXUtn55yuo7ipiTJd5n1sxYhg+aYhw5Fb9L Bo56aQE4eRVMuBiF5UDVLTykmZ7fd6xcYdD+4q4w8KzqgEueY0M0z3/rP1T1rQfpYvww T6ThtfUiwIAtZWSmNs4BodD6pWZj09Tj8Vrg3mUE8HK+3VTM0wek7kPnGqoJrGI5ioDi 2zLU/NKsCGM6Ku+sR7vSOLWjbamDuxol+06WOmSIXbHPvVCAQIl7aRqMWd7ljFSGTg94 Ov9A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778028361; x=1778633161; 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=hdtzf9eHR5G9u38wQE27Ez1yBHseJb6vJmHAUnBL/S4=; b=LuxIP+TQpTtztoVy8A/LBaP8KhvFHGeVQhHN5h9wG9wCTvbwVDZ/42vKhGyYWn+hUW ir1txKpatf8MMiNFNjX8Vz75fQ5tNxbjzG6uXD41jUJlqWMAbEEL8T6bTxq5IX/x1b52 Hu1QrF9Z1miNyZsWrifoGBWRM/S3QjsrlyLk44ZkWcUXd1qyxUPaWX3iWqLcOTifU1UY FsvZvmy5Bf6yRnVbXxmxd4NCz9RJyoT3Hw8lxXjSlWKXEu/MnlIjStJmDd2AKyoD2dbm EVfTiKXGmI4rx96cpjV48eVQIN3qR9I/leCnSqB5mzTJzOFyybYwuNTOFsnbRrEhqtFF zGqQ== X-Forwarded-Encrypted: i=1; AFNElJ8jnz3w8GR6mBOx3uwojtrB7BeMYMTi0cLPlHKqmG9RYaz3qYgEq+5JC+j7Bsjv1GUW6lK2r5OyNP/0PwY=@vger.kernel.org X-Gm-Message-State: AOJu0YyTvUHxmHFk4WwGHmRhVN0hkTWVdNK3pI2B3H9F06rxYcAr28n8 OOtW7CNeCuyvjOwEnuADpcT2u3yv2FAcU4KsmhsuxLhK+6cvOLUstUVpreRQRWN49VNFPBlWBg4 9kgsPOYxurw== X-Received: from pjuj12.prod.google.com ([2002:a17:90a:d00c:b0:35f:b7f5:9c7]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:2251:b0:35f:b5df:453 with SMTP id 98e67ed59e1d1-365ac7757c2mr952090a91.22.1778028360556; Tue, 05 May 2026 17:46:00 -0700 (PDT) Date: Tue, 5 May 2026 17:45:43 -0700 In-Reply-To: <20260506004546.3140141-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: <20260504072937.2103453-1-irogers@google.com> <20260506004546.3140141-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260506004546.3140141-3-irogers@google.com> Subject: [PATCH v5 2/5] perf tool: Fix missing schedstat delegates and dont_split_sample_group in delegate_tool From: Ian Rogers To: irogers@google.com, acme@kernel.org, gmx@google.com, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, 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.545.g6539524ca2-goog From nobody Sat Jun 13 19:02:41 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 955D1244675 for ; Wed, 6 May 2026 00:46:03 +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=1778028364; cv=none; b=BIoz26VxphEDf5zGIKIRogsFfGWTOs5tLqIqO3jbQdhZ+Z8lJgwGOz/XoiX4uJzuzqc27K37uvjpnisl0p+1V+xfznuLp7rPYFVzP9f+qDAwi9fpfzRf/I+qgkDgz7WoHS9cdOhm84Ke29UMuUcWfzBGIimV+vR79c9myyHvdbU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778028364; c=relaxed/simple; bh=K0v5k7ohAIGp1ovP6WBrlQf0O1Cj4kRGfYBqP+FfH8c=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=dbkNEB3Lm6+PZTIzn9gaf8aOt9ojxUireRQy7txtlxCt6j5qNL+JwYndv6njw/Ado/hXdOh9MYURG7VWgQLXcTFuAJYonXLcODvgsyphFlPVTOs7UqCp22nbNp5zu9O2r/SDbMldyHyLHGwstv6gCWGg9iwihY9gB1zX1VgriNA= 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=OnSMI6MR; 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="OnSMI6MR" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-1320f2754d0so17364c88.1 for ; Tue, 05 May 2026 17:46:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1778028363; x=1778633163; 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=9l04Nhv68ISYgUcRIyEzfbm9pU+jEiwpVxMxGUMc6pQ=; b=OnSMI6MRwVK2gbfkYW2Uk3IjF1aozLWQ9r4tnrKQo7LHHYFrYSDlyKl5i93zVNTqyH L56MRmZ2ydfbR/XpfaLvYBAFgADDOIjKz6z9Ijzr7q65hp+9KpXHOy9KsGoBwHnrQhSl q7/8Y6rkreGPAT0LrJuGHYtco/M4X5uVy811Jn5/VZIRo/G6ynwNwUQdQt9LyyjU9QJL qurqHqxSIO/P78YWGLhZwqoaXPDlG5PtijRNfmR2s0ksJZfWGKKyBFW/SH5uONPGESan u/prnkiU1GD+biOnKxgn5rgAmeJOyv+jynJT3G1SF47v+7Pherkk1PMvv5bPdxO/HW/t uHTA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778028363; x=1778633163; 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=9l04Nhv68ISYgUcRIyEzfbm9pU+jEiwpVxMxGUMc6pQ=; b=cYDtwmcxCadUQhG8ZitR0N06dRO2bLLCGmY8dirGIOdyV74bq4AjAACvPE23Sb18cu uCff3Uz9ceebY4RmHAk07xeCp7xC5+pnkurXuIC5fV5plqgENDmlGd1YDeGd9gS1UkUU VsYxP6H2X+lNV7CktYhuD+NtNV1UN9LJqmrlmebF7ACRJ3YKh2/saX6OVtiFIp+64m2N xcJIj+Xbx07BtMPNnVvy01PVcKcgsOTtTWaTmCdccPJdYsube/xZKXb+XutRuJXbdT6k 3HMAF4bCck/Jdv2Yfi3gE9IyY7lsZZl8IANRn2QPSFbjTgIN6HwLvhc/Q8hTuZSZ+esI iJEw== X-Forwarded-Encrypted: i=1; AFNElJ99qU7tak3fAv0b3Zv8CFAfgYl0OSFcDZstBuWrZsJmdRMHnWdLhRm6zODLksHLM0TyPpdJj5oHmqClYJk=@vger.kernel.org X-Gm-Message-State: AOJu0YwmM5BJ5VTZSAsdchj5vDWhsOopgUe2szWiY6n93iWrLn1pBr7X n1B5FUtCRgdUMtcntgNC+hZpNK9aCKYHT4o32fjiB/85XjIs5Hx1tzr2dRk9+9DfG4DbZrO3o7R CUQ5PmgYiMg== X-Received: from dleb2-n1.prod.google.com ([2002:a05:701b:4242:10b0:12c:5db:d6d1]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:698b:b0:12d:e126:b7c8 with SMTP id a92af1059eb24-131965c2123mr723864c88.13.1778028362570; Tue, 05 May 2026 17:46:02 -0700 (PDT) Date: Tue, 5 May 2026 17:45:44 -0700 In-Reply-To: <20260506004546.3140141-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: <20260504072937.2103453-1-irogers@google.com> <20260506004546.3140141-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260506004546.3140141-4-irogers@google.com> Subject: [PATCH v5 3/5] perf symbols: Fix map removal sequence inside dso__process_kernel_symbol() From: Ian Rogers To: irogers@google.com, acme@kernel.org, gmx@google.com, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, 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 parsing vmlinux ELF binary symbols, dso__process_kernel_symbol() mutates the map's start address key fields in place before executing maps__remove(). This forces maps__by_address_index() to look up the new mutated address range via strict binary search inside an array interval that was ordered using the old unmutated boundaries, leading to a bsearch() mismatch failure and leaking maps index errors. Fix this natively by executing maps__remove() before mutating the map fields in place, ensuring binary search maps queries always locate and extract target elements flawlessly. Fixes: 39b12f781271 ("perf tools: Make it possible to read object code from= vmlinux") Signed-off-by: Ian Rogers --- tools/perf/util/symbol-elf.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 7afa8a117139..f31d481a8627 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1372,20 +1372,31 @@ static int dso__process_kernel_symbol(struct dso *d= so, struct map *map, */ if (*remap_kernel && dso__kernel(dso) && !kmodule) { *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 the map is tracking inside the kmaps cache list array, we + * MUST remove it before mutating its virtual address key fields + * in place. Otherwise, downstream binary search lookups (bsearch) + * will search for mutated keys inside an array sorted under old + * invariants, causing indexing desynchronization faults. + */ if (kmaps) { int err; struct map *tmp =3D map__get(map); =20 maps__remove(kmaps, map); + 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); err =3D maps__insert(kmaps, map); map__put(tmp); if (err) return err; + } else { + 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); } } =20 --=20 2.54.0.545.g6539524ca2-goog From nobody Sat Jun 13 19:02:41 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 E846E244675 for ; Wed, 6 May 2026 00:46:05 +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=1778028371; cv=none; b=b6dvPr/YXVrjsn4fPiRn/Ihj7324r6hr99DxXyVBvrbpADD6XQ3HJaMJtkx+OKNb1/XQTc1opf130AsrXZeddu6bkZuHu1bB6Mvbj4uffz5iCZsD2GT1z+ORQAgVTP0Ksvq5NcmRDFtFJP3tvbOLVI7gwbra6GuGc4vYD/2SQt8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778028371; c=relaxed/simple; bh=lzrgeseOdn5o9XWRxWzyAlM2GNk8O3HuUeIelxgFfJk=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=qxQejP+oFFVCvUaU+j0ChgcOD+TI9xIPpR2ceuCGpVgHSX7jACPciazOLlqYiNoB4Ym19clXSoOJBrzJhea33iOzbAl1qcHpZ5A1mid1lFJcdniCyXPRS1W2xJe6lgF8YyOsfoR6RjfWNdS1FMLovl//esKAPKfflUzICdZA7Hk= 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=sg+a7C7k; 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="sg+a7C7k" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2efc342ef15so4801299eec.1 for ; Tue, 05 May 2026 17:46:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1778028365; x=1778633165; 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=NMor4flQ1solkSOoj0icE/Ip8zpngf2bIpLwUuV7J8o=; b=sg+a7C7kgjRBsFivIQwiQZTtpAJmxhC5dRNwjzJ8ohpXpkY5bIre989iIR3/p+6dsX g0KkVSwAH0xYRD210iSKAqRN5lyxB6Oh1AQvdQkErJEPJikp3WZL3rfJ/N4mRwWCc3m7 gLcwwN9lPbojSZZ+CQqgwu1nv/DsMq5aOAAXRlnSfoyx7uEQYoepbBiZf16Zi9p0RvCR 3iJAH04lAldW7M+Qwp6OlHscEbjuOOd1eTpYJKyi+qXEZcnWBzKwQHNdRNsYaYMpftzp mswkIclVUg0ED/qskE0c4MGJqh3t53eBV5hy0tM+3GbPE6w+Fqky9MBjpK6Gq6575Dcn UJlA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778028365; x=1778633165; 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=NMor4flQ1solkSOoj0icE/Ip8zpngf2bIpLwUuV7J8o=; b=Aon5kA3jLBvYKZAugsGuk9yxzMHy8SHY/zaA08eQLws1LCNilWVdXOWipkE+/PLc/k XHkM8eBb+ey8PpqRW7mKHKmC20rFGNpNWjVCgF/fzshPVSIAr/GZk0JzVDkN8LtVOlcl QBliyMJK5v8JcJUGoBmMvUNBF2F2o7iIRSqAQsrBSOcHjwtKWsFTigrawnWDuIgM6h6n QMfipxjkYsURHCMolxlaWYtTIS6JSq+TzbNFtx90HSLmKr/soXVDm1cORvhds8ZFCVAU A3TdZulgeoYVDZd5a5b1b/bJ7BwPAZupprrwMjDZQB4kP1z2wPvCzFv7COg6Xq/FplrS pJcg== X-Forwarded-Encrypted: i=1; AFNElJ/zrhV8KKVKTgGKlU8vbsEnHDIH9MtmV0+b2qklQVCP7EOWrYzL9HJKt0fSK/W/tcgMT/f8AWyDtmkFXTE=@vger.kernel.org X-Gm-Message-State: AOJu0YyjNzX6N88BgCoI336RjSR4UaJiNDv0uq4ebEs8Y93cnEe6lwcb JygPpz64PgTC/qM7tAoojirYS3/vGg3foquGziKm1OQbEzI6raqmvbQeed7Ug5XnYJu7xyYi9YN MsQHEkigFNQ== X-Received: from dyaf16-n2.prod.google.com ([2002:a05:693c:8390:20b0:2da:13c6:f288]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:e19:b0:12c:61fe:fb49 with SMTP id a92af1059eb24-131967d7c1cmr744665c88.6.1778028364793; Tue, 05 May 2026 17:46:04 -0700 (PDT) Date: Tue, 5 May 2026 17:45:45 -0700 In-Reply-To: <20260506004546.3140141-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: <20260504072937.2103453-1-irogers@google.com> <20260506004546.3140141-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260506004546.3140141-5-irogers@google.com> Subject: [PATCH v5 4/5] perf inject/aslr: Add aslr tool to remap/obfuscate virtual addresses From: Ian Rogers To: irogers@google.com, acme@kernel.org, gmx@google.com, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, 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. When events are not known/handled by the tool they are dropped. This makes the tool conservative and it should never leak ASLR information, but it means virtual address remapping is needed for cases like auxtrace. 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 layouts are tracked globally per process context 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, and dro= ps unsupported complex payloads (such as user register stacks, raw tracepoints, and hardware AUX tracing frames) to completely eliminate accidental address leakage vectors. Assisted-by: Gemini-CLI:Google Gemini 3 Signed-off-by: Ian Rogers Co-developed-by: Gabriel Marin Signed-off-by: Gabriel Marin --- v5: Fix memory leaks inside aslr_tool__delete destructor by calling standard machines__destroy_kernel_maps() to cleanly free host/guest maps and gue= st machine structures. Introduce the precise 'first_kernel_mapping' tracki= ng guard inside aslr.c to rewrite the core kernel pgoff virtual address wh= ile safely protecting module file offsets from corruption. Harden skipn() pipe I/O stream reader loops against EINTR interruption errors. Clean up breakpoint address (bp_addr) memory scrubbing by executing the scrubbin= g loop directly at core session initialization startup level, natively securin= g both file headers and streaming pipe channels while removing redundant runti= me 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 handli= ng from v3/v4 development. Drop raw auxtrace events. Fix thread reference = leaks in event handlers. Fix 32-bit truncation bug in hashmaps using u64* val= ues. Prevent leaking uninitialized heap memory by zeroing copy buffer. Corre= ct bitmask checks for branch stack flags. Avoid PMU configuration corrupti= on. v2: First review feedback adjustments. --- tools/perf/builtin-inject.c | 31 +- tools/perf/util/Build | 1 + tools/perf/util/aslr.c | 1220 +++++++++++++++++++++++++++++++++++ tools/perf/util/aslr.h | 10 + 4 files changed, 1261 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 f174bc69cec4..8fe479cb4152 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, @@ -2458,6 +2462,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, @@ -2564,6 +2570,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 { @@ -2571,6 +2579,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. */ @@ -2684,18 +2693,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) @@ -2789,6 +2816,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..effdcbec0db0 --- /dev/null +++ b/tools/perf/util/aslr.c @@ -0,0 +1,1220 @@ +// 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 + +static int skipn(int fd, u64 n) +{ + char buf[4096]; + ssize_t ret; + + while (n > 0) { + ret =3D read(fd, buf, (n < (u64)sizeof(buf) ? n : (u64)sizeof(buf))); + if (ret < 0) { + if (errno =3D=3D EINTR) + continue; + return ret; + } + if (ret =3D=3D 0) + return 0; + n -=3D ret; + } + + return 0; +} + +/** + * 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 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]; + /** @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; + /** @first_kernel_mapping: flag indicating if we are still to process any= kernel mapping. */ + bool first_kernel_mapping; +}; + +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->dso ^ key->invariant ^ key->pid; +} + +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 RC_CHK_EQUAL(key1->dso, key2->dso) && + key1->invariant =3D=3D key2->invariant && + key1->pid =3D=3D key2->pid; +} + +static size_t top_addresses__hash(long key, void *ctx __maybe_unused) +{ + return key; +} + +static bool top_addresses__equal(long key1, long key2, void *ctx __maybe_u= nused) +{ + return key1 =3D=3D key2; +} + +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.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 { + if (effective_cpumode =3D=3D PERF_RECORD_MISC_KERNEL) { + struct hashmap_entry *cur; + size_t bkt; + + hashmap__for_each_entry(&aslr->remap_addresses, cur, bkt) { + struct remap_addresses_key *k; + u64 *v; + + k =3D (struct remap_addresses_key *)cur->pkey; + if (k->pid =3D=3D kernel_pid && + k->invariant =3D=3D key.invariant) { + v =3D (u64 *)cur->pvalue; + remap_addr =3D *v + map__pgoff(al.map) + + (addr - map__start(al.map)); + break; + } + } + } + if (remap_addr =3D=3D 0) { + 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__remap_mapping(struct aslr_tool *aslr, + struct thread *aslr_thread, + u8 cpumode, + u64 start, u64 len, u64 pgoff) +{ + struct addr_location al; + struct addr_location prev_al; + struct remap_addresses_key key; + struct remap_addresses_key *new_key =3D NULL; + struct remap_addresses_key *old_key =3D NULL; + u64 remap_addr =3D 0; + u64 *remapped_invariant_ptr =3D NULL; + u64 *max_addr_ptr =3D NULL; + u64 *new_val =3D NULL; + u64 *new_max =3D NULL; + u64 *old_val =3D NULL; + u64 *old_val_remap =3D NULL; + bool is_contiguous =3D false; + bool first_mapping =3D false; + bool key_found =3D false; + int err; + + if (!aslr_thread) + return 0; /* No thread. */ + + addr_location__init(&al); + if (thread__find_map(aslr_thread, cpumode, start, &al)) { + key.dso =3D map__dso(al.map); + key.invariant =3D map__start(al.map) - map__pgoff(al.map); + } else { + key.dso =3D NULL; + key.invariant =3D start - pgoff; + } + key.pid =3D cpumode =3D=3D PERF_RECORD_MISC_KERNEL ? kernel_pid : aslr_th= read->pid_; + + if (hashmap__find(&aslr->remap_addresses, &key, &remapped_invariant_ptr))= { + remap_addr =3D *remapped_invariant_ptr + (al.map ? map__pgoff(al.map) : = pgoff); + key_found =3D true; + } else { + addr_location__init(&prev_al); + if (thread__find_map(aslr_thread, cpumode, start - 1, &prev_al)) { + if (map__start(prev_al.map) + map__size(prev_al.map) =3D=3D start) { + is_contiguous =3D true; + } else { + pr_debug("Previous mmap [%lx, %lx] overlaps current map [%lx, %lx]\n", + map__start(prev_al.map), + map__start(prev_al.map) + map__size(prev_al.map), + start, start+len); + } + } + addr_location__exit(&prev_al); + + if (!hashmap__find(&aslr->top_addresses, key.pid, &max_addr_ptr)) { + first_mapping =3D true; + remap_addr =3D (cpumode =3D=3D PERF_RECORD_MISC_KERNEL ? + kernel_space_start : user_space_start); + } else { + remap_addr =3D *max_addr_ptr; + } + + remap_addr =3D round_up_to_page_size(remap_addr); + if (!is_contiguous && !first_mapping) + remap_addr +=3D page_size; + + new_key =3D malloc(sizeof(*new_key)); + new_val =3D malloc(sizeof(u64)); + + if (!new_key || !new_val) { + free(new_key); + free(new_val); + addr_location__exit(&al); + return 0; + } + *new_key =3D key; + new_key->dso =3D dso__get(key.dso); + *new_val =3D remap_addr - (al.map ? map__pgoff(al.map) : pgoff); + + if (hashmap__add(&aslr->remap_addresses, new_key, new_val) !=3D 0) { + dso__put(new_key->dso); + free(new_key); + free(new_val); + addr_location__exit(&al); + return 0; + } + } + + /* Update top_addresses */ + new_max =3D malloc(sizeof(u64)); + old_val =3D NULL; + + if (!new_max) { + old_key =3D NULL; + old_val_remap =3D NULL; + + if (!key_found) { + hashmap__delete(&aslr->remap_addresses, &key, + &old_key, &old_val_remap); + if (old_key) + dso__put(old_key->dso); + free(old_key); + free(old_val_remap); + } + addr_location__exit(&al); + return 0; + } + *new_max =3D remap_addr + len; + + if (hashmap__find(&aslr->top_addresses, key.pid, &max_addr_ptr)) { + if (*max_addr_ptr > *new_max) + *new_max =3D *max_addr_ptr; + } + + err =3D hashmap__insert(&aslr->top_addresses, key.pid, new_max, + (first_mapping && !key_found) ? + HASHMAP_ADD : HASHMAP_UPDATE, + NULL, &old_val); + if (err) { + old_key =3D NULL; + old_val_remap =3D NULL; + + free(new_max); + if (!key_found) { + hashmap__delete(&aslr->remap_addresses, &key, + &old_key, &old_val_remap); + if (old_key) + dso__put(old_key->dso); + free(old_key); + free(old_val_remap); + } + addr_location__exit(&al); + return 0; + } + free(old_val); + + addr_location__exit(&al); + return remap_addr; +} + +static u64 aslr_tool__remap_ksymbol(struct aslr_tool *aslr, + struct thread *aslr_thread, + u64 addr, u32 len) +{ + struct addr_location al; + struct remap_addresses_key key; + struct hashmap_entry *cur; + struct remap_addresses_key *new_key =3D NULL; + struct remap_addresses_key *old_key =3D NULL; + struct remap_addresses_key *k; + size_t bkt; + u64 remap_addr =3D 0; + u64 *remapped_invariant_ptr =3D NULL; + u64 *max_addr_ptr =3D NULL; + u64 *new_val =3D NULL; + u64 *new_max =3D NULL; + u64 *old_val =3D NULL; + u64 *old_val_remap =3D NULL; + u64 *v; + bool first_mapping =3D false; + int err; + + if (!aslr_thread) + return 0; /* No thread. */ + + addr_location__init(&al); + if (thread__find_map(aslr_thread, PERF_RECORD_MISC_KERNEL, addr, &al)) { + key.dso =3D map__dso(al.map); + key.invariant =3D map__start(al.map) - map__pgoff(al.map); + } else { + key.dso =3D NULL; + key.invariant =3D addr; /* pgoff is 0 for ksymbols */ + } + key.pid =3D aslr_thread->pid_; + + if (hashmap__find(&aslr->remap_addresses, &key, &remapped_invariant_ptr))= { + if (al.map) + remap_addr =3D *remapped_invariant_ptr + map__pgoff(al.map) + + (addr - map__start(al.map)); + else + remap_addr =3D *remapped_invariant_ptr; + addr_location__exit(&al); + return remap_addr; + } + + hashmap__for_each_entry(&aslr->remap_addresses, cur, bkt) { + k =3D (struct remap_addresses_key *)cur->pkey; + if (k->pid =3D=3D kernel_pid && k->invariant =3D=3D key.invariant) { + v =3D (u64 *)cur->pvalue; + + if (al.map) + remap_addr =3D *v + map__pgoff(al.map) + + (addr - map__start(al.map)); + else + remap_addr =3D *v; + addr_location__exit(&al); + return remap_addr; + } + } + + if (!hashmap__find(&aslr->top_addresses, key.pid, &max_addr_ptr)) { + first_mapping =3D true; + remap_addr =3D kernel_space_start; + } else { + remap_addr =3D *max_addr_ptr; + } + + remap_addr =3D round_up_to_page_size(remap_addr) + page_size; + + new_key =3D malloc(sizeof(*new_key)); + new_val =3D malloc(sizeof(u64)); + + if (!new_key || !new_val) { + free(new_key); + free(new_val); + addr_location__exit(&al); + return 0; + } + *new_key =3D key; + new_key->dso =3D dso__get(key.dso); + if (al.map) + *new_val =3D remap_addr - (addr - map__start(al.map)) - map__pgoff(al.ma= p); + else + *new_val =3D remap_addr; + + if (hashmap__add(&aslr->remap_addresses, new_key, new_val) < 0) { + dso__put(new_key->dso); + free(new_key); + free(new_val); + addr_location__exit(&al); + return 0; + } + + new_max =3D malloc(sizeof(u64)); + old_val =3D NULL; + + if (!new_max) { + old_key =3D NULL; + old_val_remap =3D NULL; + + hashmap__delete(&aslr->remap_addresses, &key, &old_key, &old_val_remap); + if (old_key) + dso__put(old_key->dso); + free(old_key); + free(old_val_remap); + addr_location__exit(&al); + return 0; + } + *new_max =3D remap_addr + len; + + if (hashmap__find(&aslr->top_addresses, key.pid, &max_addr_ptr)) { + if (*max_addr_ptr > *new_max) + *new_max =3D *max_addr_ptr; + } + + err =3D hashmap__insert(&aslr->top_addresses, key.pid, new_max, + first_mapping ? + HASHMAP_ADD : HASHMAP_UPDATE, + NULL, &old_val); + if (err) { + old_key =3D NULL; + old_val_remap =3D NULL; + + free(new_max); + hashmap__delete(&aslr->remap_addresses, &key, &old_key, &old_val_remap); + if (old_key) + dso__put(old_key->dso); + free(old_key); + free(old_val_remap); + addr_location__exit(&al); + return 0; + } + free(old_val); + + 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__remap_mapping(aslr, thread, cpumode, + event->mmap.start, + event->mmap.len, + event->mmap.pgoff); + if (aslr->first_kernel_mapping && cpumode =3D=3D PERF_RECORD_MISC_KERNEL)= { + new_event->mmap.pgoff =3D new_event->mmap.start; + aslr->first_kernel_mapping =3D false; + } + 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__remap_mapping(aslr, thread, cpumode, + event->mmap2.start, + event->mmap2.len, + event->mmap2.pgoff); + if (aslr->first_kernel_mapping && cpumode =3D=3D PERF_RECORD_MISC_KERNEL)= { + new_event->mmap2.pgoff =3D new_event->mmap2.start; + aslr->first_kernel_mapping =3D false; + } + 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, + 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; + + thread =3D machine__findnew_thread(aslr_machine, sample->pid, sample->tid= ); + if (!thread) + return -ENOMEM; + memcpy(&new_event->text_poke, &event->text_poke, event->text_poke.header.= size); + new_event->text_poke.addr =3D aslr_tool__remap_address(aslr, thread, cpum= ode, + event->text_poke.addr); + + err =3D delegate->text_poke(delegate, new_event, sample, machine); + + thread__put(thread); + return err; +} + +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__remap_ksymbol(aslr, thread, + event->ksymbol.addr, event->ksymbol.len); + + 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: + /* + * Immediately followed by a 64-bit + * stitching cookie. Skip/Copy it! + */ + CHECK_BOUNDS(1, 1); + out_array[j++] =3D in_array[i++]; + cntr++; + 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 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)) { + 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->first_kernel_mapping =3D true; + + 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->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.545.g6539524ca2-goog From nobody Sat Jun 13 19:02:41 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 D8A7A23E320 for ; Wed, 6 May 2026 00:46:07 +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=1778028371; cv=none; b=Ou43yNRfqVRz19KiCn3xP3zraJiWEN0eLT64BksLkxybplbGIEU9ObzmsFIR0htxCup9Gvb8LzWVXE/7IqWhfxa0qXGY90vW94J5KVzzxl6HVn06NAer/e0jqTR1ufN4rx0Xh0BG1DDFlJfr8CoRrngWmBs3DU+lOvgMPLB8yUs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778028371; c=relaxed/simple; bh=px0/X5fmfuDrmWrZDyUO2HDd+wo0jhLusDC8vLnHSzs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Pwzi5/QATybr2iIqwhlkRz5CZycKyEAW/DeYEg4BdKO4ePrflrQ7lpZnG03L0RWosE+wbQSXl4tRH8SFDUFrbFbj3n87z+9I/Fi6KL8px/HrxbW/R088bQ214gSTEKA6Vzxp50y0CEvgnNbLv+v9QrrNYYk/Rs0r2Zqyx8YSJc4= 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=gaKrkIeZ; 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="gaKrkIeZ" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-1275c6fc58aso11778314c88.0 for ; Tue, 05 May 2026 17:46:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1778028367; x=1778633167; 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=YlEpcQhzskUFWEYWvkt2Bk0tfQ0ZPSI9EJO/tKhv0lY=; b=gaKrkIeZjG7OUtSw5aXaSmleVYhBpkoNsTk53fZ9Q66Vhp+s88Tgxym7ijJumPK2/7 fw3+EGngg6IWDrcDAXaTZG4kLGtlccflEJkKe3yvr+QrCNZz+pr315HQGhjAS/7dnqzG 72HFtNjdhc+L9lDiUr6/cSYJvht41dR6cNADn/HDW3N9n1syG0gjedW1rVOMk3MzRiRM ZqclVGLRWe/2jLiQOKe2Rot6YSAvr71xm7FG8j80UEEYkRt32HVa46LdKYy6rfdpKus/ AXUlWUWMOrgqRhPx7YriKods25MolzovNqJeE8DgV7f9PoVBhidTDTWxqXhs8J+9eFCr 97zw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778028367; x=1778633167; 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=YlEpcQhzskUFWEYWvkt2Bk0tfQ0ZPSI9EJO/tKhv0lY=; b=Oh2vvwA3CCDHAJUaTmbRYATVt1m3gfhohuLrJoX03jUrvEo2LR0XIdmAnTiU0LARsr 6pDTUuJsyVKF1i+NNROzh0uFOyZ+cohMBDZ8Co3uxItA9PeQSOCZijqXpCuUa2zYbLO4 yqFiA/gqE9/jHyck9hR4vQnC2YhBZgGrPS6JBCbTJvaYN0ZPAb2v0oo+werenpFqvwUC /vfE16Dx3VDTIU+W+jys8l6mTreNraB9WE7akwslA5lS7Wc25KW1F126jI24K8FyRzGs AE+5+jjyQW2KW5EhE/CKg3upO/PErcEGwTYNujtW4+VgGoCpIAYv7f45XCi0Z0lYQHkk kpZA== X-Forwarded-Encrypted: i=1; AFNElJ9ybGdtPdn9YB3JybyBvzueDT0QIt16kZGi19WYeeW+bG6N2RH5Ko36kpV5qSCFHXauL3+cxlGXYDNk7pA=@vger.kernel.org X-Gm-Message-State: AOJu0Yw2crtIf680ehx5EuoGRIOBgugeQE9VEHCfUjm8kIM1WTHP5Df8 J308+hjrVgSlbFl8F/9qwwVabJiqsgpYivxVXZ6WIzqlWl/Ot5M/Wtf2SJidYmRC7+gv8lv0ThR iYPclzd/vAg== X-Received: from dlbsi14.prod.google.com ([2002:a05:7022:b88e:b0:12d:b591:4bc6]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:238f:b0:12d:ca31:f1b6 with SMTP id a92af1059eb24-131853e5dcdmr839051c88.18.1778028366861; Tue, 05 May 2026 17:46:06 -0700 (PDT) Date: Tue, 5 May 2026 17:45:46 -0700 In-Reply-To: <20260506004546.3140141-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: <20260504072937.2103453-1-irogers@google.com> <20260506004546.3140141-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260506004546.3140141-6-irogers@google.com> Subject: [PATCH v5 5/5] perf test: Add inject ASLR test From: Ian Rogers To: irogers@google.com, acme@kernel.org, gmx@google.com, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, 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 (skipping gracefully if permissions restrict recording the kernel map). - 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 --- v5: Harden test suite verification pipelines by upgrading report checks to strict sorted line-by-line diff comparisons to accommodate remapped poi= nter 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 filte= rs 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 | 459 ++++++++++++++++++++++++++ 1 file changed, 459 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..cdc3aa94de63 --- /dev/null +++ b/tools/perf/tests/shell/inject_aslr.sh @@ -0,0 +1,459 @@ +#!/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 + + + +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}" ${prog} > "${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.545.g6539524ca2-goog