perf machine: Add session back pointer to fix out of bounds read

thread__e_machine_endian() decides whether a thread is live, and so
whether to read /proc/pid/exe rather than fall back to the perf.data
environment, by recovering the session from the machine:

	session = container_of(machine->machines, struct perf_session,
			       machines);
	is_live = !session->data;

That only holds when the struct machines is the one embedded in a struct
perf_session. It is not the only one. aslr_tool keeps its own in struct
aslr_tool, and hists_cumulate, hists_filter, hists_link, hists_output
and thread-maps-share each put one on the stack. For those the
container_of subtracts offsetof(struct perf_session, machines) from an
address that was never inside a session, and the following load of
session->data reads outside the object, which is what ASan reports.

Replace the derivation with an explicit link. machines__init() takes the
owning session, or NULL when the machines is standalone, and
thread__e_machine_endian() reads it back through machines__session().

A NULL session is treated as live. The tests use synthetic pids that
have no /proc entry, so the read fails there and the e_machine falls
back to EM_HOST as before, rather than being decided by whatever the
out of bounds read happened to return. perf inject registers its
session with the aslr tool once the session exists, before any event is
processed, so that tool keeps reporting file based threads as not live.

Fixes: 70351029b5 ("perf thread: Add support for reading the e_machine type for a thread")
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@google.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Ian Rogers 2026-09-17 23:26:20 -07:00 committed by Arnaldo Carvalho de Melo
parent 7e27239d22
commit d227b45fc1
12 changed files with 35 additions and 14 deletions

View File

@ -2881,6 +2881,9 @@ int cmd_inject(int argc, const char **argv)
goto out_close_output;
}
if (inject.aslr)
aslr_tool__register_session(tool, inject.session);
if (zstd_init(&(inject.session->zstd_data), 0) < 0)
pr_warning("Decompression initialization failed.\n");

View File

@ -723,7 +723,7 @@ static int test__hists_cumulate(struct test_suite *test __maybe_unused, int subt
goto out;
err = TEST_FAIL;
if (machines__init(&machines))
if (machines__init(&machines, /*session=*/NULL))
goto out;
/* setup threads/dso/map/symbols also */

View File

@ -131,7 +131,7 @@ static int test__hists_filter(struct test_suite *test __maybe_unused, int subtes
goto out;
err = TEST_FAIL;
if (machines__init(&machines))
if (machines__init(&machines, /*session=*/NULL))
goto out;
/* setup threads/dso/map/symbols also */

View File

@ -303,7 +303,7 @@ static int test__hists_link(struct test_suite *test __maybe_unused, int subtest
goto out;
err = TEST_FAIL;
if (machines__init(&machines))
if (machines__init(&machines, /*session=*/NULL))
goto out;
/* setup threads/dso/map/symbols also */

View File

@ -610,7 +610,7 @@ static int test__hists_output(struct test_suite *test __maybe_unused, int subtes
goto out;
err = TEST_FAIL;
if (machines__init(&machines))
if (machines__init(&machines, /*session=*/NULL))
goto out;
/* setup threads/dso/map/symbols also */

View File

@ -27,7 +27,8 @@ static int test__thread_maps_share(struct test_suite *test __maybe_unused, int s
* other group (pid: 4, tids: 4, 5)
*/
TEST_ASSERT_VAL("failed to init machines", machines__init(&machines) == 0);
TEST_ASSERT_VAL("failed to init machines",
machines__init(&machines, /*session=*/NULL) == 0);
machine = &machines.host;
/* create process with 4 threads */

View File

@ -1274,7 +1274,7 @@ static int aslr_tool__init(struct aslr_tool *aslr, struct perf_tool *delegate)
delegate_tool__init(&aslr->tool, delegate);
aslr->tool.tool.ordered_events = true;
if (machines__init(&aslr->machines))
if (machines__init(&aslr->machines, /*session=*/NULL))
return -ENOMEM;
hashmap__init(&aslr->remap_addresses,
@ -1374,6 +1374,14 @@ void aslr_tool__delete(struct perf_tool *tool)
free(aslr);
}
void aslr_tool__register_session(struct perf_tool *tool, struct perf_session *session)
{
struct delegate_tool *del_tool = container_of(tool, struct delegate_tool, tool);
struct aslr_tool *aslr = container_of(del_tool, struct aslr_tool, tool);
aslr->machines.session = session;
}
int aslr_tool__cache_orig_attrs(struct perf_tool *tool, struct evsel *evsel)
{
struct delegate_tool *del_tool = container_of(tool, struct delegate_tool, tool);

View File

@ -28,6 +28,7 @@
PERF_SAMPLE_CODE_PAGE_SIZE | \
PERF_SAMPLE_AUX)
struct perf_session;
struct perf_tool;
struct evsel;
struct evlist;
@ -36,6 +37,7 @@ union perf_event;
struct perf_tool *aslr_tool__new(struct perf_tool *delegate);
void aslr_tool__delete(struct perf_tool *tool);
void aslr_tool__register_session(struct perf_tool *tool, struct perf_session *session);
void aslr_tool__strip_attr_event(union perf_event *event, struct evlist *evlist);
int aslr_tool__cache_orig_attrs(struct perf_tool *tool, struct evsel *evsel);
void aslr_tool__strip_evlist(const struct perf_tool *tool, struct evlist *evlist);

View File

@ -233,10 +233,11 @@ void machine__delete(struct machine *machine)
}
}
int machines__init(struct machines *machines)
int machines__init(struct machines *machines, struct perf_session *session)
{
int err = machine__init(&machines->host, "", HOST_KERNEL_ID);
machines->session = session;
machines->host.machines = machines;
machines->guests = RB_ROOT_CACHED;
return err;

View File

@ -15,6 +15,7 @@ struct dso;
struct dso_id;
struct evsel;
struct perf_sample;
struct perf_session;
struct symbol;
struct target;
struct thread;
@ -148,13 +149,20 @@ int machine__process_event(struct machine *machine, union perf_event *event,
typedef void (*machine__process_t)(struct machine *machine, void *data);
struct machines {
/** @session: back link to owning session if there is one. */
struct perf_session *session;
struct machine host;
struct rb_root_cached guests;
};
int machines__init(struct machines *machines);
int machines__init(struct machines *machines, struct perf_session *session);
void machines__exit(struct machines *machines);
static inline struct perf_session *machines__session(struct machines *machines)
{
return machines->session;
}
void machines__process_guests(struct machines *machines,
machine__process_t process, void *data);

View File

@ -166,7 +166,7 @@ struct perf_session *__perf_session__new(struct perf_data *data,
session->active_decomp = &session->decomp_data;
INIT_LIST_HEAD(&session->auxtrace_index);
perf_env__init(&session->header.env);
if (machines__init(&session->machines))
if (machines__init(&session->machines, session))
goto out_delete;
ordered_events__init(&session->ordered_events,

View File

@ -531,12 +531,10 @@ uint16_t thread__e_machine_endian(struct thread *thread, struct machine *machine
bool is_live = machine->machines == NULL;
if (!is_live) {
/* Check if the session has a data file. */
struct perf_session *session = container_of(machine->machines,
struct perf_session,
machines);
/* Check if the session has a data file (assume no session is a test). */
struct perf_session *session = machines__session(machine->machines);
is_live = !session->data;
is_live = !session || !session->data;
}
/* Read from /proc/pid/exe if live. */
if (is_live) {