public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Namhyung Kim <namhyung@kernel.org>
To: Ian Rogers <irogers@google.com>
Cc: acme@kernel.org, gmx@google.com, 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
Subject: Re: [PATCH v5 4/5] perf inject/aslr: Add aslr tool to remap/obfuscate virtual addresses
Date: Wed, 6 May 2026 11:52:13 -0700	[thread overview]
Message-ID: <afuN3cN2EFR0S28o@google.com> (raw)
In-Reply-To: <20260506004546.3140141-5-irogers@google.com>

On Tue, May 05, 2026 at 05:45:45PM -0700, Ian Rogers wrote:
> 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 guest
> 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 drops
> 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 <irogers@google.com>
> Co-developed-by: Gabriel Marin <gmx@google.com>
> Signed-off-by: Gabriel Marin <gmx@google.com>
> ---
> v5: Fix memory leaks inside aslr_tool__delete destructor by calling standard
>     machines__destroy_kernel_maps() to cleanly free host/guest maps and guest
>     machine structures. Introduce the precise 'first_kernel_mapping' tracking
>     guard inside aslr.c to rewrite the core kernel pgoff virtual address while
>     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 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 |   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"
>  
> +#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 *tool,
>  	return perf_event__repipe_synth(tool, event);
>  }
>  
> +
> +
>  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)
>  			}
>  		}
>  
> +
> +

Unnessary whitespace changes here and the above.


>  		session->header.data_offset = output_data_offset;
>  		session->header.data_size = inject->bytes_written;
>  		perf_session__inject_header(session, session->evlist, fd, &inj_fc.fc,
> @@ -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[] = {
> @@ -2571,6 +2579,7 @@ int cmd_inject(int argc, const char **argv)
>  		NULL
>  	};
>  	bool ordered_events;
> +	struct perf_tool *tool = &inject.tool;
>  
>  	if (!inject.itrace_synth_opts.set) {
>  		/* Disable eager loading of kernel symbols that adds overhead to perf inject. */
> @@ -2684,18 +2693,36 @@ int cmd_inject(int argc, const char **argv)
>  	inject.tool.schedstat_domain	= perf_event__repipe_op2_synth;
>  	inject.tool.dont_split_sample_group = true;
>  	inject.tool.merge_deferred_callchains = false;
> -	inject.session = __perf_session__new(&data, &inject.tool,
> +	if (inject.aslr) {
> +		tool = aslr_tool__new(&inject.tool);
> +		if (!tool) {
> +			ret = -ENOMEM;
> +			goto out_close_output;
> +		}
> +	}
> +	inject.session = __perf_session__new(&data, tool,
>  					     /*trace_event_repipe=*/inject.output.is_pipe,
>  					     /*host_env=*/NULL);
>  
>  	if (IS_ERR(inject.session)) {
>  		ret = PTR_ERR(inject.session);
> +		if (inject.aslr)
> +			aslr_tool__delete(tool);
>  		goto out_close_output;
>  	}
>  
>  	if (zstd_init(&(inject.session->zstd_data), 0) < 0)
>  		pr_warning("Decompression initialization failed.\n");
>  
> +	if (inject.aslr) {
> +		struct evsel *evsel;
> +
> +		evlist__for_each_entry(inject.session->evlist, evsel) {
> +			if (evsel->core.attr.type == PERF_TYPE_BREAKPOINT)
> +				evsel->core.attr.bp_addr = 0;
> +		}
> +	}
> +
>  	/* Save original section info before feature bits change */
>  	ret = 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 += arm64-frame-pointer-unwind-support.o
>  perf-util-y += addr2line.o
>  perf-util-y += addr_location.o
>  perf-util-y += annotate.o
> +perf-util-y += aslr.o
>  perf-util-y += blake2s.o
>  perf-util-y += block-info.o
>  perf-util-y += 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 <internal/lib.h>  /* page_size */
> +#include <linux/compiler.h>
> +#include <linux/zalloc.h>
> +#include <errno.h>
> +#include <inttypes.h>
> +#include <unistd.h>
> +
> +static int skipn(int fd, u64 n)
> +{
> +	char buf[4096];
> +	ssize_t ret;
> +
> +	while (n > 0) {
> +		ret = read(fd, buf, (n < (u64)sizeof(buf) ? n : (u64)sizeof(buf)));
> +		if (ret < 0) {
> +			if (errno == EINTR)
> +				continue;
> +			return ret;
> +		}
> +		if (ret == 0)
> +			return 0;
> +		n -= ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * struct remap_addresses_key - Key for mapping original addresses to remapped ones.
> + * @dso: Pointer to the DSO (Dynamic Shared Object) associated with the mapping.
> + * @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 map into
> + *             fragmented VMA pieces due to overlapping events, allowing us to
> + *             resolve split maps consistently back to the original VMA.

I'm curious if it's guaranteed to be unique within a process.


> + * @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 process 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 address. */
> +	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 = -1;
> +
> +/* Start remapping user processes from a small non-zero offset. */
> +static const u64 user_space_start = 0x200000;
> +static const u64 kernel_space_start = 0xffff800010000000;
> +
> +static size_t remap_addresses__hash(long _key, void *ctx __maybe_unused)
> +{
> +	struct remap_addresses_key *key = (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 __maybe_unused)
> +{
> +	struct remap_addresses_key *key1 = (struct remap_addresses_key *)_key1;
> +	struct remap_addresses_key *key2 = (struct remap_addresses_key *)_key2;
> +
> +	return RC_CHK_EQUAL(key1->dso, key2->dso) &&
> +	       key1->invariant == key2->invariant &&
> +	       key1->pid == 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_unused)
> +{
> +	return key1 == 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 = NULL;
> +	u64 remap_addr = 0;
> +	u8 effective_cpumode = 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 space
> +		 * to be robust against bad cpumode in samples.
> +		 */
> +		if (cpumode == PERF_RECORD_MISC_KERNEL)
> +			effective_cpumode = PERF_RECORD_MISC_USER;
> +		else if (cpumode == PERF_RECORD_MISC_USER)
> +			effective_cpumode = PERF_RECORD_MISC_KERNEL;
> +		else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL)
> +			effective_cpumode = PERF_RECORD_MISC_GUEST_USER;
> +		else if (cpumode == PERF_RECORD_MISC_GUEST_USER)
> +			effective_cpumode = 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 = map__dso(al.map);
> +	key.invariant = map__start(al.map) - map__pgoff(al.map);
> +	key.pid = effective_cpumode == PERF_RECORD_MISC_KERNEL ? kernel_pid : aslr_thread->pid_;
> +
> +	if (hashmap__find(&aslr->remap_addresses, &key, &remapped_invariant_ptr)) {
> +		remap_addr = *remapped_invariant_ptr + map__pgoff(al.map) +
> +			     (addr - map__start(al.map));
> +	} else {
> +		if (effective_cpumode == 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 = (struct remap_addresses_key *)cur->pkey;
> +				if (k->pid == kernel_pid &&
> +				    k->invariant == key.invariant) {
> +					v = (u64 *)cur->pvalue;
> +					remap_addr = *v + map__pgoff(al.map) +
> +						     (addr - map__start(al.map));
> +					break;
> +				}
> +			}
> +		}
> +		if (remap_addr == 0) {
> +			pr_debug("Cannot find a remapped entry for address %lx in mapping %lx(%lx) for pid=%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 = NULL;
> +	struct remap_addresses_key *old_key = NULL;
> +	u64 remap_addr = 0;
> +	u64 *remapped_invariant_ptr = NULL;
> +	u64 *max_addr_ptr = NULL;
> +	u64 *new_val = NULL;
> +	u64 *new_max = NULL;
> +	u64 *old_val = NULL;
> +	u64 *old_val_remap = NULL;
> +	bool is_contiguous = false;
> +	bool first_mapping = false;
> +	bool key_found = 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 = map__dso(al.map);
> +		key.invariant = map__start(al.map) - map__pgoff(al.map);
> +	} else {
> +		key.dso = NULL;
> +		key.invariant = start - pgoff;
> +	}
> +	key.pid = cpumode == PERF_RECORD_MISC_KERNEL ? kernel_pid : aslr_thread->pid_;
> +
> +	if (hashmap__find(&aslr->remap_addresses, &key, &remapped_invariant_ptr)) {
> +		remap_addr = *remapped_invariant_ptr + (al.map ? map__pgoff(al.map) : pgoff);
> +		key_found = 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) == start) {
> +				is_contiguous = 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 = true;
> +			remap_addr = (cpumode == PERF_RECORD_MISC_KERNEL ?
> +				      kernel_space_start : user_space_start);
> +		} else {
> +			remap_addr = *max_addr_ptr;
> +		}
> +
> +		remap_addr = round_up_to_page_size(remap_addr);
> +		if (!is_contiguous && !first_mapping)
> +			remap_addr += page_size;
> +
> +		new_key = malloc(sizeof(*new_key));
> +		new_val = malloc(sizeof(u64));

I think the value of hashmap can be passed as value if it's u64.. well
on 64-bit systems.


> +
> +		if (!new_key || !new_val) {
> +			free(new_key);
> +			free(new_val);
> +			addr_location__exit(&al);
> +			return 0;
> +		}
> +		*new_key = key;
> +		new_key->dso = dso__get(key.dso);
> +		*new_val = remap_addr - (al.map ? map__pgoff(al.map) : pgoff);
> +
> +		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;
> +		}
> +	}
> +
> +	/* Update top_addresses */
> +	new_max = malloc(sizeof(u64));
> +	old_val = NULL;
> +
> +	if (!new_max) {
> +		old_key = NULL;
> +		old_val_remap = 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 = remap_addr + len;
> +
> +	if (hashmap__find(&aslr->top_addresses, key.pid, &max_addr_ptr)) {
> +		if (*max_addr_ptr > *new_max)
> +			*new_max = *max_addr_ptr;
> +	}
> +
> +	err = hashmap__insert(&aslr->top_addresses, key.pid, new_max,
> +				  (first_mapping && !key_found) ?
> +					HASHMAP_ADD : HASHMAP_UPDATE,
> +				  NULL, &old_val);
> +	if (err) {
> +		old_key = NULL;
> +		old_val_remap = 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)

Any chance you can share the code with the above function?


> +{
> +	struct addr_location al;
> +	struct remap_addresses_key key;
> +	struct hashmap_entry *cur;
> +	struct remap_addresses_key *new_key = NULL;
> +	struct remap_addresses_key *old_key = NULL;
> +	struct remap_addresses_key *k;
> +	size_t bkt;
> +	u64 remap_addr = 0;
> +	u64 *remapped_invariant_ptr = NULL;
> +	u64 *max_addr_ptr = NULL;
> +	u64 *new_val = NULL;
> +	u64 *new_max = NULL;
> +	u64 *old_val = NULL;
> +	u64 *old_val_remap = NULL;
> +	u64 *v;
> +	bool first_mapping = 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 = map__dso(al.map);
> +		key.invariant = map__start(al.map) - map__pgoff(al.map);
> +	} else {
> +		key.dso = NULL;
> +		key.invariant = addr; /* pgoff is 0 for ksymbols */
> +	}
> +	key.pid = aslr_thread->pid_;
> +
> +	if (hashmap__find(&aslr->remap_addresses, &key, &remapped_invariant_ptr)) {
> +		if (al.map)
> +			remap_addr = *remapped_invariant_ptr + map__pgoff(al.map) +
> +				     (addr - map__start(al.map));
> +		else
> +			remap_addr = *remapped_invariant_ptr;
> +		addr_location__exit(&al);
> +		return remap_addr;
> +	}
> +
> +	hashmap__for_each_entry(&aslr->remap_addresses, cur, bkt) {
> +		k = (struct remap_addresses_key *)cur->pkey;
> +		if (k->pid == kernel_pid && k->invariant == key.invariant) {
> +			v = (u64 *)cur->pvalue;
> +
> +			if (al.map)
> +				remap_addr = *v + map__pgoff(al.map) +
> +					     (addr - map__start(al.map));
> +			else
> +				remap_addr = *v;
> +			addr_location__exit(&al);
> +			return remap_addr;
> +		}
> +	}
> +
> +	if (!hashmap__find(&aslr->top_addresses, key.pid, &max_addr_ptr)) {
> +		first_mapping = true;
> +		remap_addr = kernel_space_start;
> +	} else {
> +		remap_addr = *max_addr_ptr;
> +	}
> +
> +	remap_addr = round_up_to_page_size(remap_addr) + page_size;
> +
> +	new_key = malloc(sizeof(*new_key));
> +	new_val = malloc(sizeof(u64));
> +
> +	if (!new_key || !new_val) {
> +		free(new_key);
> +		free(new_val);
> +		addr_location__exit(&al);
> +		return 0;
> +	}
> +	*new_key = key;
> +	new_key->dso = dso__get(key.dso);
> +	if (al.map)
> +		*new_val = remap_addr - (addr - map__start(al.map)) - map__pgoff(al.map);
> +	else
> +		*new_val = 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 = malloc(sizeof(u64));
> +	old_val = NULL;
> +
> +	if (!new_max) {
> +		old_key = NULL;
> +		old_val_remap = 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 = remap_addr + len;
> +
> +	if (hashmap__find(&aslr->top_addresses, key.pid, &max_addr_ptr)) {
> +		if (*max_addr_ptr > *new_max)
> +			*new_max = *max_addr_ptr;
> +	}
> +
> +	err = hashmap__insert(&aslr->top_addresses, key.pid, new_max,
> +				  first_mapping ?
> +					HASHMAP_ADD : HASHMAP_UPDATE,
> +				  NULL, &old_val);
> +	if (err) {
> +		old_key = NULL;
> +		old_val_remap = 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 = container_of(tool, struct delegate_tool, tool);
> +	aslr = container_of(del_tool, struct aslr_tool, tool);
> +	delegate = aslr->tool.delegate;
> +	new_event = (union perf_event *)aslr->event_copy;
> +	cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
> +
> +	aslr_machine = machines__findnew(&aslr->machines, machine->pid);
> +	if (!aslr_machine)
> +		return -ENOMEM;
> +
> +	/* Create the thread, map, etc. in the ASLR before virtual address space. */
> +	err = perf_event__process_mmap(tool, event, sample, aslr_machine);
> +	if (err)
> +		return err;
> +
> +	thread = 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 = aslr_tool__remap_mapping(aslr, thread, cpumode,
> +							   event->mmap.start,
> +							   event->mmap.len,
> +							   event->mmap.pgoff);
> +	if (aslr->first_kernel_mapping && cpumode == PERF_RECORD_MISC_KERNEL) {
> +		new_event->mmap.pgoff = new_event->mmap.start;
> +		aslr->first_kernel_mapping = false;
> +	}
> +	err = 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 = container_of(tool, struct delegate_tool, tool);
> +	aslr = container_of(del_tool, struct aslr_tool, tool);
> +	delegate = aslr->tool.delegate;
> +	new_event = (union perf_event *)aslr->event_copy;
> +	cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
> +
> +	aslr_machine = machines__findnew(&aslr->machines, machine->pid);
> +	if (!aslr_machine)
> +		return -ENOMEM;
> +
> +	/* Create the thread, map, etc. in the ASLR before virtual address space. */
> +	err = perf_event__process_mmap2(tool, event, sample, aslr_machine);
> +	if (err)
> +		return err;
> +
> +	thread = 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 = aslr_tool__remap_mapping(aslr, thread, cpumode,
> +							   event->mmap2.start,
> +							   event->mmap2.len,
> +							   event->mmap2.pgoff);
> +	if (aslr->first_kernel_mapping && cpumode == PERF_RECORD_MISC_KERNEL) {
> +		new_event->mmap2.pgoff = new_event->mmap2.start;
> +		aslr->first_kernel_mapping = false;
> +	}
> +	err = 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 = container_of(tool, struct delegate_tool, tool);
> +	aslr = container_of(del_tool, struct aslr_tool, tool);
> +	delegate = aslr->tool.delegate;
> +
> +	aslr_machine = machines__findnew(&aslr->machines, machine->pid);
> +	if (!aslr_machine)
> +		return -ENOMEM;
> +
> +	/* Create the thread, map, etc. in the ASLR before virtual address space. */
> +	err = 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 = container_of(tool, struct delegate_tool, tool);
> +	aslr = container_of(del_tool, struct aslr_tool, tool);
> +	delegate = aslr->tool.delegate;
> +
> +	aslr_machine = machines__findnew(&aslr->machines, machine->pid);
> +	if (!aslr_machine)
> +		return -ENOMEM;
> +
> +	/* Create the thread, map, etc. in the ASLR before virtual address space. */
> +	err = 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 = container_of(tool, struct delegate_tool, tool);
> +	aslr = container_of(del_tool, struct aslr_tool, tool);
> +	delegate = aslr->tool.delegate;
> +
> +	aslr_machine = machines__findnew(&aslr->machines, machine->pid);
> +	if (!aslr_machine)
> +		return -ENOMEM;
> +
> +	/* Create the thread, map, etc. in the ASLR before virtual address space. */
> +	err = 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 = container_of(tool, struct delegate_tool, tool);
> +	aslr = container_of(del_tool, struct aslr_tool, tool);
> +	delegate = aslr->tool.delegate;
> +	new_event = (union perf_event *)aslr->event_copy;
> +	cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
> +
> +	aslr_machine = machines__findnew(&aslr->machines, machine->pid);
> +	if (!aslr_machine)
> +		return -ENOMEM;
> +
> +	thread = 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 = aslr_tool__remap_address(aslr, thread, cpumode,
> +							     event->text_poke.addr);
> +
> +	err = 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 = container_of(tool, struct delegate_tool, tool);
> +	aslr = container_of(del_tool, struct aslr_tool, tool);
> +	delegate = aslr->tool.delegate;
> +	new_event = (union perf_event *)aslr->event_copy;
> +
> +	aslr_machine = machines__findnew(&aslr->machines, machine->pid);
> +	if (!aslr_machine)
> +		return -ENOMEM;
> +
> +	err = perf_event__process_ksymbol(tool, event, sample, aslr_machine);
> +	if (err)
> +		return err;
> +
> +	thread = 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 = aslr_tool__remap_ksymbol(aslr, thread,
> +							   event->ksymbol.addr, event->ksymbol.len);
> +
> +	err = delegate->ksymbol(delegate, new_event, sample, machine);
> +	thread__put(thread);
> +	return err;
> +}
> +
> +static int aslr_tool__process_sample(const struct perf_tool *tool, union perf_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 = container_of(tool, struct delegate_tool, tool);
> +	aslr = container_of(del_tool, struct aslr_tool, tool);
> +	delegate = aslr->tool.delegate;
> +	ret = -EFAULT;
> +	sample_type = evsel->core.attr.sample_type;
> +	max_i = (event->header.size - sizeof(struct perf_event_header)) / sizeof(__u64);
> +	max_j = (PERF_SAMPLE_MAX_SIZE - sizeof(struct perf_event_header)) / sizeof(__u64);
> +	new_event = (union perf_event *)aslr->event_copy;
> +	cpumode = sample->cpumode;
> +	i = 0;
> +	j = 0;
> +
> +	aslr_machine = machines__findnew(&aslr->machines, machine->pid);
> +	if (!aslr_machine)
> +		return -ENOMEM;
> +
> +	thread = 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;
> +
> +
> +

Excessive blank lines.


> +	new_event->sample.header = event->sample.header;
> +
> +	in_array = &event->sample.array[0];
> +	out_array = &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 = -EFAULT; \
> +			goto out_put; \
> +		} \
> +	} while (0)
> +
> +#define COPY_U64() \
> +	do { \
> +		CHECK_BOUNDS(1, 1); \
> +		out_array[j++] = in_array[i++]; \
> +	} while (0)
> +
> +#define REMAP_U64(addr_field) \
> +	do { \
> +		CHECK_BOUNDS(1, 1); \
> +		out_array[j++] = 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) == 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 = out_array[j++] = 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 = 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 = out_array[j++] = in_array[i++];
> +
> +		for (u64 cntr = 0; cntr < nr; cntr++) {
> +			CHECK_BOUNDS(1, 1);
> +			addr = in_array[i++];
> +			if (addr >= PERF_CONTEXT_MAX) {
> +				out_array[j++] = addr;
> +				switch (addr) {
> +				case PERF_CONTEXT_HV:
> +					cpumode = PERF_RECORD_MISC_HYPERVISOR;
> +					break;
> +				case PERF_CONTEXT_KERNEL:
> +					cpumode = PERF_RECORD_MISC_KERNEL;
> +					break;
> +				case PERF_CONTEXT_USER:
> +					cpumode = PERF_RECORD_MISC_USER;
> +					break;
> +				case PERF_CONTEXT_GUEST:
> +					cpumode = PERF_RECORD_MISC_GUEST_KERNEL;
> +					break;
> +				case PERF_CONTEXT_GUEST_KERNEL:
> +					cpumode = PERF_RECORD_MISC_GUEST_KERNEL;
> +					break;
> +				case PERF_CONTEXT_GUEST_USER:
> +					cpumode = 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++] = in_array[i++];
> +					cntr++;
> +					break;
> +				default:
> +					pr_debug("invalid callchain context: %"PRIx64"\n", addr);
> +					ret = 0;
> +					goto out_put;
> +				}
> +				continue;
> +			}
> +			out_array[j++] = aslr_tool__remap_address(aslr, thread, cpumode, addr);
> +		}
> +	}
> +	if (sample_type & PERF_SAMPLE_RAW) {
> +		size_t bytes = sizeof(u32) + sample->raw_size;
> +		size_t u64_words = (bytes + 7) / 8;
> +
> +		if (i + u64_words > max_i || j + u64_words > max_j) {
> +			ret = -EFAULT;
> +			goto out_put;
> +		}
> +		memcpy(&out_array[j], &in_array[i], bytes);
> +		i += u64_words;
> +		j += 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 = 0;
> +		goto out_put;
> +	}
> +	if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
> +		u64 nr;
> +
> +		CHECK_BOUNDS(1, 1);
> +		nr = out_array[j++] = 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 = -EFAULT;
> +			goto out_put;
> +		}
> +		if (nr * 3 > max_i - i || nr * 3 > max_j - j) {
> +			ret = -EFAULT;
> +			goto out_put;
> +		}
> +		for (u64 cntr = 0; cntr < nr; cntr++) {
> +			out_array[j++] = aslr_tool__remap_address(aslr, thread,
> +								  sample->cpumode,
> +								  in_array[i++]); /* from */
> +			out_array[j++] = aslr_tool__remap_address(aslr, thread,
> +								  sample->cpumode,
> +								  in_array[i++]); /* to */
> +			out_array[j++] = 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 = -EFAULT;
> +				goto out_put;
> +			}
> +			memcpy(&out_array[j], &in_array[i], nr * sizeof(u64));
> +			i += nr;
> +			j += nr;
> +			/* TODO: confirm branch counters don't leak ASLR information. */
> +			pr_debug("Dropping sample branch counters as possible ASLR leak\n");
> +			ret = 0;
> +			goto out_put;
> +		}
> +	}
> +	if (sample_type & PERF_SAMPLE_REGS_USER) {
> +		u64 abi;
> +
> +		COPY_U64(); /* abi */
> +		abi = out_array[j-1];
> +		if (abi != PERF_SAMPLE_REGS_ABI_NONE) {
> +			u64 nr = hweight64(evsel->core.attr.sample_regs_user);
> +
> +			if (nr > max_i - i || nr > max_j - j) {
> +				ret = -EFAULT;
> +				goto out_put;
> +			}
> +			memcpy(&out_array[j], &in_array[i], nr * sizeof(u64));
> +			i += nr;
> +			j += nr;
> +		}
> +		/* TODO: can this be less conservative? */
> +		pr_debug("Dropping regs user sample as possible ASLR leak\n");
> +		ret = 0;
> +		goto out_put;

Is this mean you drop samples if it contains registers?


> +	}
> +	if (sample_type & PERF_SAMPLE_STACK_USER) {
> +		u64 size;
> +
> +		CHECK_BOUNDS(1, 1);
> +		size = out_array[j++] = in_array[i++];
> +		if (size > 0) {
> +			size_t u64_words = size / 8 + (size % 8 ? 1 : 0);
> +
> +			if (u64_words > max_i - i || u64_words > max_j - j) {
> +				ret = -EFAULT;
> +				goto out_put;
> +			}
> +			memcpy(&out_array[j], &in_array[i], size);
> +			if (size % 8) {
> +				size_t pad = 8 - (size % 8);
> +
> +				memset(((char *)&out_array[j]) + size, 0, pad);
> +			}
> +			i += u64_words;
> +			j += u64_words;
> +
> +			COPY_U64(); /* dyn_size */
> +		}
> +		/* TODO: can this be less conservative? */
> +		pr_debug("Dropping stack user sample as possible ASLR leak\n");
> +		ret = 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 = out_array[j-1];
> +		if (abi != PERF_SAMPLE_REGS_ABI_NONE) {
> +			u64 nr = hweight64(evsel->core.attr.sample_regs_intr);
> +
> +			if (nr > max_i - i || nr > max_j - j) {
> +				ret = -EFAULT;
> +				goto out_put;
> +			}
> +			memcpy(&out_array[j], &in_array[i], nr * sizeof(u64));
> +			i += nr;
> +			j += nr;
> +		}
> +		/* TODO: can this be less conservative? */
> +		pr_debug("Dropping interrupt register sample as possible ASLR leak\n");
> +		ret = 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 = 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 = out_array[j++] = in_array[i++];
> +		if (size > 0) {
> +			size_t u64_words = size / 8 + (size % 8 ? 1 : 0);
> +
> +			if (u64_words > max_i - i || u64_words > max_j - j) {
> +				ret = -EFAULT;
> +				goto out_put;
> +			}
> +			memcpy(&out_array[j], &in_array[i], size);
> +			if (size % 8) {
> +				size_t pad = 8 - (size % 8);
> +
> +				memset(((char *)&out_array[j]) + size, 0, pad);
> +			}
> +			i += u64_words;
> +			j += u64_words;
> +		}
> +		/* TODO: can this be less conservative? */
> +		pr_debug("Dropping aux sample as possible ASLR leak\n");
> +		ret = 0;
> +		goto out_put;
> +	}

Can you use perf_event__synthesize_sample()?


> +
> +	if (evsel__is_offcpu_event(evsel)) {
> +		/* TODO: can this be less conservative? */
> +		pr_debug("Dropping off-CPU sample as possible ASLR leak\n");

Why not remap the address?

Thanks,
Namhyung

> +		ret = 0;
> +		goto out_put;
> +	}
> +
> +	new_event->sample.header.size = sizeof(struct perf_event_header) + j * sizeof(u64);
> +
> +	perf_sample__init(&new_sample, /*all=*/ true);
> +	ret = evsel__parse_sample(evsel, new_event, &new_sample);
> +	if (ret) {
> +		perf_sample__exit(&new_sample);
> +		goto out_put;
> +	}
> +
> +	ret = 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 = container_of(tool, struct delegate_tool, tool);
> +	aslr = container_of(del_tool, struct aslr_tool, tool);
> +	delegate = aslr->tool.delegate;
> +	new_event = (union perf_event *)aslr->event_copy;
> +
> +	memcpy(&new_event->attr, &event->attr, event->attr.header.size);
> +	if (new_event->attr.attr.type == PERF_TYPE_BREAKPOINT)
> +		new_event->attr.attr.bp_addr = 0;  /* Conservatively remove addresses. */
> +
> +	return delegate->attr(delegate, new_event, pevlist);
> +}
> +
> +static s64 aslr_tool__process_auxtrace(const struct perf_tool *tool __maybe_unused,
> +				       struct perf_session *session,
> +				       union perf_event *event)
> +{
> +	if (perf_data__is_pipe(session->data)) {
> +		int err = 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 *delegate)
> +{
> +	delegate_tool__init(&aslr->tool, delegate);
> +	aslr->tool.tool.ordered_events = true;
> +
> +	machines__init(&aslr->machines);
> +
> +	hashmap__init(&aslr->remap_addresses,
> +		      remap_addresses__hash, remap_addresses__equal,
> +		      /*ctx=*/NULL);
> +	hashmap__init(&aslr->top_addresses,
> +		      top_addresses__hash, top_addresses__equal,
> +		      /*ctx=*/NULL);
> +	aslr->first_kernel_mapping = true;
> +
> +	aslr->tool.tool.sample	= aslr_tool__process_sample;
> +	/* read - reads a counter, okay to delegate. */
> +	aslr->tool.tool.mmap	= aslr_tool__process_mmap;
> +	aslr->tool.tool.mmap2	= aslr_tool__process_mmap2;
> +	aslr->tool.tool.comm	= aslr_tool__process_comm;
> +	aslr->tool.tool.fork	= aslr_tool__process_fork;
> +	aslr->tool.tool.exit	= 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	= aslr_tool__process_ksymbol;
> +	/* bpf - no virtual address. */
> +	aslr->tool.tool.text_poke = aslr_tool__process_text_poke;
> +	aslr->tool.tool.attr = 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 = aslr_tool__process_auxtrace;
> +	aslr->tool.tool.auxtrace_info = aslr_tool__process_auxtrace_info;
> +	aslr->tool.tool.auxtrace_error = aslr_tool__process_auxtrace_error;
> +}
> +
> +struct perf_tool *aslr_tool__new(struct perf_tool *delegate)
> +{
> +	struct aslr_tool *aslr = 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 = container_of(tool, struct delegate_tool, tool);
> +	aslr = container_of(del_tool, struct aslr_tool, tool);
> +
> +	hashmap__for_each_entry(&aslr->remap_addresses, cur, bkt) {
> +		struct remap_addresses_key *key = (struct remap_addresses_key *)cur->pkey;
> +
> +		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 */
> -- 
> 2.54.0.545.g6539524ca2-goog
> 

  reply	other threads:[~2026-05-06 18:52 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-24 22:05 [PATCH v1 1/2] perf inject/aslr: Add aslr tool to remap/obfuscate virtual addresses Ian Rogers
2026-04-24 22:05 ` [PATCH v1 2/2] perf test: Add inject ASLR test Ian Rogers
2026-04-25  2:05 ` [PATCH v2 1/2] perf inject/aslr: Add aslr tool to remap/obfuscate virtual addresses Ian Rogers
2026-04-25  2:05   ` [PATCH v2 2/2] perf test: Add inject ASLR test Ian Rogers
2026-05-04  3:51   ` [PATCH v3 0/4] perf tools: Add inject --aslr feature and prerequisite robustness fixes Ian Rogers
2026-05-04  3:51     ` [PATCH v3 1/4] perf sched: Add missing mmap2 handler in timehist Ian Rogers
2026-05-04  3:51     ` [PATCH v3 2/4] perf tool: Fix missing schedstat delegates and dont_split_sample_group in delegate_tool Ian Rogers
2026-05-04  3:51     ` [PATCH v3 3/4] perf inject/aslr: Add aslr tool to remap/obfuscate virtual addresses Ian Rogers
2026-05-04  3:51     ` [PATCH v3 4/4] perf test: Add inject ASLR test Ian Rogers
2026-05-04  7:29     ` [PATCH v4 0/4] perf tools: Add inject --aslr feature and prerequisite robustness fixes Ian Rogers
2026-05-04  7:29       ` [PATCH v4 1/4] perf sched: Add missing mmap2 handler in timehist Ian Rogers
2026-05-04  7:29       ` [PATCH v4 2/4] perf tool: Fix missing schedstat delegates and dont_split_sample_group in delegate_tool Ian Rogers
2026-05-04  7:29       ` [PATCH v4 3/4] perf inject/aslr: Add aslr tool to remap/obfuscate virtual addresses Ian Rogers
2026-05-04  7:29       ` [PATCH v4 4/4] perf test: Add inject ASLR test Ian Rogers
2026-05-04  8:23       ` [PATCH v4 0/4] perf tools: Add inject --aslr feature and prerequisite robustness fixes Ian Rogers
2026-05-06  0:45       ` [PATCH v5 0/5] " Ian Rogers
2026-05-06  0:45         ` [PATCH v5 1/5] perf sched: Add missing mmap2 handler in timehist Ian Rogers
2026-05-06 13:22           ` Arnaldo Carvalho de Melo
2026-05-06 16:16             ` Ian Rogers
2026-05-06  0:45         ` [PATCH v5 2/5] perf tool: Fix missing schedstat delegates and dont_split_sample_group in delegate_tool Ian Rogers
2026-05-06  0:45         ` [PATCH v5 3/5] perf symbols: Fix map removal sequence inside dso__process_kernel_symbol() Ian Rogers
2026-05-06  0:45         ` [PATCH v5 4/5] perf inject/aslr: Add aslr tool to remap/obfuscate virtual addresses Ian Rogers
2026-05-06 18:52           ` Namhyung Kim [this message]
2026-05-06 20:01             ` Ian Rogers
2026-05-06  0:45         ` [PATCH v5 5/5] perf test: Add inject ASLR test Ian Rogers

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=afuN3cN2EFR0S28o@google.com \
    --to=namhyung@kernel.org \
    --cc=acme@kernel.org \
    --cc=adrian.hunter@intel.com \
    --cc=gmx@google.com \
    --cc=irogers@google.com \
    --cc=james.clark@linaro.org \
    --cc=jolsa@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=peterz@infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox