From mboxrd@z Thu Jan 1 00:00:00 1970 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 8F3FB3FFAA8 for ; Fri, 5 Jun 2026 06:06:23 +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=1780639585; cv=none; b=MHvkNyHH6Yi99bchl1KzMIpOZP5RO5eKBGlcSJCjePgEhSwkKpS0t+XP4t8+HMKCUCUULlwT3huzK2xWfavCO7I3B9L6stog7zRlu/njklAp4KooT1c/qfQtjOjOFdv/d+CB9myApHLPdR+zyAphb5MPRg8QN5+nkl9D7NXxYPo= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780639585; c=relaxed/simple; bh=Ti7JhinxdfdF0VZlYRiP0WIYtIpfmPWtsX24g8DQCQA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=bch26Upkby9IlXAnR13CC9esVBSf+Hpp3SFIlltdDG54cIfQWgocHEvrxtieD+Pl/F2rBkM4ut6vaTNz1Tp2ASi9zEzvdR9T8NOP9db84ujIs55/j0vaBIoF++wLVaWZQEvy4NvfjgBwmdmSdYG9FxFPD1J+H6+pTKqx8ccdMl4= 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=MN2E6pTW; 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="MN2E6pTW" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-3074d4102a9so1284860eec.0 for ; Thu, 04 Jun 2026 23:06:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1780639583; x=1781244383; 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=0FFKVPcKu6oPvyvRC5MoZY7q7Y3IzL2ALcc/8kXtgMI=; b=MN2E6pTWTS8sRdJ/YnAW1JDycqNRhwJldwIpleNipaT0o4Z+hpogtlslF+IHb/cSyi p6YDKtgV9oZh8WgEmHJjWStIOc+Dxh/PbKyWcQGZX9J2hVDQMIM0iaIIoE4xF1WwVFyZ uUOMYpKmrPHoFtq9Kocu3bGWzOglrtYs3c399gXgg/nm6ktj62AnDj8+Veu+/c/2qsUg hOnCdQ8kYODGhHZ8FwQ9BbS2gKULl7OXYeHS4x8ur+8wjd2q3WHJdzlP/RbJFFbK/4xA 09zXoo8ccKTiV+RQ3k6MFaRjhKTNT7ZI36TEOnrNXnLxAUfWNE6+uoyr371sDIhI47Np OcgQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780639583; x=1781244383; 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=0FFKVPcKu6oPvyvRC5MoZY7q7Y3IzL2ALcc/8kXtgMI=; b=jGGaazfLZzaj7rvifr0c61hhoEUzKgaK0EnLlduSNCpmUpP9Ves4d3e6c0/K4VLE36 MsCX5vc3h2llQL3jVmcPVBEbrshAHupkAMWA5+dWDQpnY2AWdyRJ/I4XWw8fRAKj3k44 WslrfNAF9QAGVWqkseRugnlTr8N1w2UpzXsDumaYQPY/b0e2/k2fadYsc2U6veq1g1Fw dyZ6yVAOnhe0oudv0t5ReDMJFkV3fM6qTcpqWoHV9jfqYbv3pZ3cAlAJq2cR8tA6N+oY arNn0YIe/8rSCNsJpypN/mZjDExhC9T7JzO1ZBqk26uQZ4AefQZEnYuquDcobiCNKtuF 6/vA== X-Forwarded-Encrypted: i=1; AFNElJ/+iJhXwCYKV+SN3awphu79ZvCOTGetkxnud25dYEKZLR+2l3B5l3OOpZkRIQRL9ku3LKDt9lvcog90t9o=@vger.kernel.org X-Gm-Message-State: AOJu0YzMiUZAgwG14VDUPbQBtT2e6+jXp/PcfilbuPscOg0blpDxv8Ld oGeYUPMoWkkFTBmk5qL803l3+T/w9zVARYjyHIFeU0Q0XvjbE8yJzWYfIYRrobC4drvSXaVXyBG ttSL65XKR8g== X-Received: from dyke36.prod.google.com ([2002:a05:7300:4724:b0:304:de31:a7b2]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:caa:b0:2f2:6dde:df53 with SMTP id 5a478bee46e88-3077b181258mr1032664eec.17.1780639582425; Thu, 04 Jun 2026 23:06:22 -0700 (PDT) Date: Thu, 4 Jun 2026 23:06:08 -0700 In-Reply-To: <20260605060610.1529996-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: <20260604172850.683329-1-irogers@google.com> <20260605060610.1529996-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.1032.g2f8565e1d1-goog Message-ID: <20260605060610.1529996-4-irogers@google.com> Subject: [PATCH v10 3/5] perf inject/aslr: Implement sample address remapping From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, gmx@google.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-Type: text/plain; charset="UTF-8" Add the sample address remapping logic to the ASLR tool. This patch implements aslr_tool__process_sample, which parses sample events, remaps IPs, ADDRs, callchains, and branch stacks using the mappings collected from metadata events, and drops potentially leaking raw, register, stack, physical address, and aux samples. Also adds the aslr_tool__remap_address helper function. Co-developed-by: Gabriel Marin Signed-off-by: Gabriel Marin Signed-off-by: Ian Rogers --- tools/perf/util/aslr.c | 454 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 450 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/aslr.c b/tools/perf/util/aslr.c index 375a0355f281..8cfefa23030d 100644 --- a/tools/perf/util/aslr.c +++ b/tools/perf/util/aslr.c @@ -109,6 +109,60 @@ 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.machine = maps__machine(thread__maps(aslr_thread)); + key.dso = map__dso(al.map); + key.invariant = map__start(al.map) - map__pgoff(al.map); + key.pid = (effective_cpumode == PERF_RECORD_MISC_KERNEL || + effective_cpumode == PERF_RECORD_MISC_GUEST_KERNEL) ? + kernel_pid : thread__pid(aslr_thread); + + 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 { + 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; +} + struct aslr_machine_priv { bool kernel_maps_loaded; }; @@ -554,13 +608,405 @@ static int aslr_tool__process_sample(const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine) { - struct delegate_tool *del_tool = container_of(tool, struct delegate_tool, tool); - struct aslr_tool *aslr = container_of(del_tool, struct aslr_tool, tool); - struct perf_tool *delegate = aslr->tool.delegate; + struct evsel *evsel = sample->evsel; + 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; + bool orig_needs_swap; + + del_tool = container_of(tool, struct delegate_tool, tool); + aslr = container_of(del_tool, struct aslr_tool, tool); + delegate = aslr->tool.delegate; + + if (evsel__is_dummy_event(evsel)) + return delegate->sample(delegate, event, sample, machine); + + 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; + if (aslr_tool__preload_kernel_maps(aslr_machine) < 0) + 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; + + 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) \ + (i + (required_i) > max_i || j + (required_j) > max_j) + +#define COPY_U64() \ + do { \ + if (CHECK_BOUNDS(1, 1)) { \ + ret = -EFAULT; \ + goto out_put; \ + } \ + out_array[j++] = in_array[i++]; \ + } while (0) + +#define REMAP_U64(addr_field) \ + do { \ + if (CHECK_BOUNDS(1, 1)) { \ + ret = -EFAULT; \ + goto out_put; \ + } \ + 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; + + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + out_array[j] = in_array[i]; + nr = out_array[j++]; + 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; + + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + out_array[j] = in_array[i]; + nr = out_array[j++]; + i++; + + for (u64 cntr = 0; cntr < nr; cntr++) { + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + 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: + if (cntr + 1 >= nr) { + pr_debug("Truncated callchain deferred cookie context\n"); + ret = 0; + goto out_put; + } + /* + * Immediately followed by a 64-bit + * stitching cookie. Skip/Copy it! + */ + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + out_array[j++] = in_array[i++]; + cntr++; + cpumode = PERF_RECORD_MISC_USER; + 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; + + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + out_array[j] = in_array[i]; + nr = out_array[j++]; + 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) { + if (CHECK_BOUNDS(1, 0)) { + ret = -EFAULT; + goto out_put; + } + /* abi */ + COPY_U64(); + /* TODO: can this be less conservative? */ + pr_debug("Dropping regs user sample as possible ASLR leak\n"); + ret = 0; + goto out_put; + } + if (sample_type & PERF_SAMPLE_STACK_USER) { + u64 size; + + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + out_array[j] = in_array[i]; + size = out_array[j++]; + 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 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) { + if (CHECK_BOUNDS(1, 0)) { + ret = -EFAULT; + goto out_put; + } + /* abi */ + COPY_U64(); + /* 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; + + if (CHECK_BOUNDS(1, 1)) { + ret = -EFAULT; + goto out_put; + } + out_array[j] = in_array[i]; + size = out_array[j++]; + 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; + } + + if (evsel__is_offcpu_event(evsel)) { + /* TODO: can this be less conservative? */ + pr_debug("Dropping off-CPU sample as possible ASLR leak\n"); + ret = 0; + goto out_put; + } + + new_event->sample.header.size = sizeof(struct perf_event_header) + j * sizeof(u64); + + orig_needs_swap = evsel->needs_swap; - return delegate->sample(delegate, event, sample, machine); + perf_sample__init(&new_sample, /*all=*/ true); + evsel->needs_swap = false; + ret = evsel__parse_sample(evsel, new_event, &new_sample); + evsel->needs_swap = orig_needs_swap; + + if (ret) { + perf_sample__exit(&new_sample); + goto out_put; + } + + new_sample.evsel = evsel; + ret = delegate->sample(delegate, new_event, &new_sample, machine); + perf_sample__exit(&new_sample); + +out_put: + thread__put(thread); + return ret; } +#undef CHECK_BOUNDS +#undef COPY_U64 +#undef REMAP_U64 + static int skipn(int fd, off_t n) { char buf[4096]; -- 2.54.0.1032.g2f8565e1d1-goog