Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf mem: Don't leak mem event names

When preparing the mem events for the argv copies are intentionally
made. These copies are leaked and cause runs of perf using address
sanitizer to fail. Rather than leak the memory allocate a chunk of
memory for the mem event names upfront and build the strings in this -
the storage is sized larger than the previous buffer size. The caller
is then responsible for clearing up this memory. As part of this
change, remove the mem_loads_name and mem_stores_name global buffers
then change the perf_pmu__mem_events_name to write to an out argument
buffer.

Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: Thomas Falcon <thomas.falcon@intel.com>
Reviewed-by: Leo Yan <leo.yan@arm.com>
Link: https://lore.kernel.org/r/20250308012853.1384762-1-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

Ian Rogers and committed by
Namhyung Kim
db5af2e4 6dad43bb

+53 -33
+3 -1
tools/perf/builtin-c2c.c
··· 3239 3239 { 3240 3240 int rec_argc, i = 0, j; 3241 3241 const char **rec_argv; 3242 + char *event_name_storage = NULL; 3242 3243 int ret; 3243 3244 bool all_user = false, all_kernel = false; 3244 3245 bool event_set = false; ··· 3301 3300 rec_argv[i++] = "--phys-data"; 3302 3301 rec_argv[i++] = "--sample-cpu"; 3303 3302 3304 - ret = perf_mem_events__record_args(rec_argv, &i); 3303 + ret = perf_mem_events__record_args(rec_argv, &i, &event_name_storage); 3305 3304 if (ret) 3306 3305 goto out; 3307 3306 ··· 3328 3327 3329 3328 ret = cmd_record(i, rec_argv); 3330 3329 out: 3330 + free(event_name_storage); 3331 3331 free(rec_argv); 3332 3332 return ret; 3333 3333 }
+8 -4
tools/perf/builtin-mem.c
··· 74 74 int rec_argc, i = 0, j; 75 75 int start, end; 76 76 const char **rec_argv; 77 + char *event_name_storage = NULL; 77 78 int ret; 78 79 struct perf_mem_event *e; 79 80 struct perf_pmu *pmu; ··· 141 140 rec_argv[i++] = "--data-page-size"; 142 141 143 142 start = i; 144 - ret = perf_mem_events__record_args(rec_argv, &i); 143 + ret = perf_mem_events__record_args(rec_argv, &i, &event_name_storage); 145 144 if (ret) 146 145 goto out; 147 146 end = i; ··· 171 170 172 171 ret = cmd_record(i, rec_argv); 173 172 out: 173 + free(event_name_storage); 174 174 free(rec_argv); 175 175 return ret; 176 176 } ··· 523 521 NULL, 524 522 NULL 525 523 }; 524 + int ret; 526 525 527 526 argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands, 528 527 mem_usage, PARSE_OPT_STOP_AT_NON_OPTION); ··· 539 536 } 540 537 541 538 if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) 542 - return __cmd_record(argc, argv, &mem, record_options); 539 + ret = __cmd_record(argc, argv, &mem, record_options); 543 540 else if (strlen(argv[0]) > 2 && strstarts("report", argv[0])) 544 - return __cmd_report(argc, argv, &mem, report_options); 541 + ret = __cmd_report(argc, argv, &mem, report_options); 545 542 else 546 543 usage_with_options(mem_usage, mem_options); 547 544 548 545 /* free usage string allocated by parse_options_subcommand */ 549 546 free((void *)mem_usage[0]); 547 + free(sort_order_help); 550 548 551 - return 0; 549 + return ret; 552 550 }
+40 -27
tools/perf/util/mem-events.c
··· 31 31 32 32 bool perf_mem_record[PERF_MEM_EVENTS__MAX] = { 0 }; 33 33 34 - static char mem_loads_name[100]; 35 - static char mem_stores_name[100]; 36 - 37 34 struct perf_mem_event *perf_pmu__mem_events_ptr(struct perf_pmu *pmu, int i) 38 35 { 39 36 if (i >= PERF_MEM_EVENTS__MAX || !pmu) ··· 78 81 return num; 79 82 } 80 83 81 - static const char *perf_pmu__mem_events_name(int i, struct perf_pmu *pmu) 84 + static const char *perf_pmu__mem_events_name(struct perf_pmu *pmu, int i, 85 + char *buf, size_t buf_size) 82 86 { 83 87 struct perf_mem_event *e; 84 88 ··· 94 96 if (e->ldlat) { 95 97 if (!e->aux_event) { 96 98 /* ARM and Most of Intel */ 97 - scnprintf(mem_loads_name, sizeof(mem_loads_name), 99 + scnprintf(buf, buf_size, 98 100 e->name, pmu->name, 99 101 perf_mem_events__loads_ldlat); 100 102 } else { 101 103 /* Intel with mem-loads-aux event */ 102 - scnprintf(mem_loads_name, sizeof(mem_loads_name), 104 + scnprintf(buf, buf_size, 103 105 e->name, pmu->name, pmu->name, 104 106 perf_mem_events__loads_ldlat); 105 107 } 106 108 } else { 107 109 if (!e->aux_event) { 108 110 /* AMD and POWER */ 109 - scnprintf(mem_loads_name, sizeof(mem_loads_name), 111 + scnprintf(buf, buf_size, 110 112 e->name, pmu->name); 111 - } else 113 + } else { 112 114 return NULL; 115 + } 113 116 } 114 - 115 - return mem_loads_name; 117 + return buf; 116 118 } 117 119 118 120 if (i == PERF_MEM_EVENTS__STORE) { 119 - scnprintf(mem_stores_name, sizeof(mem_stores_name), 121 + scnprintf(buf, buf_size, 120 122 e->name, pmu->name); 121 - return mem_stores_name; 123 + return buf; 122 124 } 123 125 124 126 return NULL; ··· 236 238 int j; 237 239 238 240 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) { 241 + char buf[128]; 239 242 struct perf_mem_event *e = perf_pmu__mem_events_ptr(pmu, j); 240 243 241 244 fprintf(stderr, "%-*s%-*s%s", 242 245 e->tag ? 13 : 0, 243 246 e->tag ? : "", 244 247 e->tag && verbose > 0 ? 25 : 0, 245 - e->tag && verbose > 0 ? perf_pmu__mem_events_name(j, pmu) : "", 248 + e->tag && verbose > 0 249 + ? perf_pmu__mem_events_name(pmu, j, buf, sizeof(buf)) 250 + : "", 246 251 e->supported ? ": available\n" : ""); 247 252 } 248 253 } 249 254 250 - int perf_mem_events__record_args(const char **rec_argv, int *argv_nr) 255 + int perf_mem_events__record_args(const char **rec_argv, int *argv_nr, char **event_name_storage_out) 251 256 { 252 257 const char *mnt = sysfs__mount(); 253 258 struct perf_pmu *pmu = NULL; 254 - struct perf_mem_event *e; 255 259 int i = *argv_nr; 256 - const char *s; 257 - char *copy; 258 260 struct perf_cpu_map *cpu_map = NULL; 259 - int ret; 261 + size_t event_name_storage_size = 262 + perf_pmu__mem_events_num_mem_pmus(NULL) * PERF_MEM_EVENTS__MAX * 128; 263 + size_t event_name_storage_remaining = event_name_storage_size; 264 + char *event_name_storage = malloc(event_name_storage_size); 265 + char *event_name_storage_ptr = event_name_storage; 260 266 267 + if (!event_name_storage) 268 + return -ENOMEM; 269 + 270 + *event_name_storage_out = NULL; 261 271 while ((pmu = perf_pmus__scan_mem(pmu)) != NULL) { 262 272 for (int j = 0; j < PERF_MEM_EVENTS__MAX; j++) { 263 - e = perf_pmu__mem_events_ptr(pmu, j); 273 + const char *s; 274 + struct perf_mem_event *e = perf_pmu__mem_events_ptr(pmu, j); 275 + int ret; 264 276 265 277 if (!perf_mem_record[j]) 266 278 continue; 267 279 268 280 if (!e->supported) { 281 + char buf[128]; 282 + 269 283 pr_err("failed: event '%s' not supported\n", 270 - perf_pmu__mem_events_name(j, pmu)); 284 + perf_pmu__mem_events_name(pmu, j, buf, sizeof(buf))); 285 + free(event_name_storage); 271 286 return -1; 272 287 } 273 288 274 - s = perf_pmu__mem_events_name(j, pmu); 289 + s = perf_pmu__mem_events_name(pmu, j, event_name_storage_ptr, 290 + event_name_storage_remaining); 275 291 if (!s || !perf_pmu__mem_events_supported(mnt, pmu, e)) 276 292 continue; 277 293 278 - copy = strdup(s); 279 - if (!copy) 280 - return -1; 281 - 282 294 rec_argv[i++] = "-e"; 283 - rec_argv[i++] = copy; 295 + rec_argv[i++] = event_name_storage_ptr; 296 + event_name_storage_remaining -= strlen(event_name_storage_ptr) + 1; 297 + event_name_storage_ptr += strlen(event_name_storage_ptr) + 1; 284 298 285 299 ret = perf_cpu_map__merge(&cpu_map, pmu->cpus); 286 - if (ret < 0) 300 + if (ret < 0) { 301 + free(event_name_storage); 287 302 return ret; 303 + } 288 304 } 289 305 } 290 306 ··· 313 301 } 314 302 315 303 *argv_nr = i; 304 + *event_name_storage_out = event_name_storage; 316 305 return 0; 317 306 } 318 307
+2 -1
tools/perf/util/mem-events.h
··· 38 38 bool is_mem_loads_aux_event(struct evsel *leader); 39 39 40 40 void perf_pmu__mem_events_list(struct perf_pmu *pmu); 41 - int perf_mem_events__record_args(const char **rec_argv, int *argv_nr); 41 + int perf_mem_events__record_args(const char **rec_argv, int *argv_nr, 42 + char **event_name_storage_out); 42 43 43 44 int perf_mem__tlb_scnprintf(char *out, size_t sz, const struct mem_info *mem_info); 44 45 int perf_mem__lvl_scnprintf(char *out, size_t sz, const struct mem_info *mem_info);