* [PATCH v3 1/3] perf tools: Fix memory leak in cmd_kvm()
2026-08-03 13:51 [PATCH v3 0/3] perf tools: Fix memory issues Michalis Niarchos via B4 Relay
@ 2026-08-03 13:51 ` Michalis Niarchos via B4 Relay
2026-08-03 13:51 ` [PATCH v3 2/3] perf tools: Fix double free and memory leak issues Michalis Niarchos via B4 Relay
2026-08-03 13:51 ` [PATCH v3 3/3] perf tools: Fix memory leak in cmd_kvm() Michalis Niarchos via B4 Relay
2 siblings, 0 replies; 4+ messages in thread
From: Michalis Niarchos via B4 Relay @ 2026-08-03 13:51 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, James Clark
Cc: linux-perf-users, linux-kernel, Michalis Niarchos
From: Michalis Niarchos <michael.niarchos@gmail.com>
filename may get allocated by get_filename_for_perf_kvm(), but is
never freed. Use string literals to remove the need for freeing.
Signed-off-by: Michalis Niarchos <michael.niarchos@gmail.com>
---
tools/perf/builtin-kvm.c | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 394302ebdb16..16cfa7ce7856 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -606,11 +606,11 @@ static const char *get_filename_for_perf_kvm(void)
const char *filename;
if (perf_host && !perf_guest)
- filename = strdup("perf.data.host");
+ filename = "perf.data.host";
else if (!perf_host && perf_guest)
- filename = strdup("perf.data.guest");
+ filename = "perf.data.guest";
else
- filename = strdup("perf.data.kvm");
+ filename = "perf.data.kvm";
return filename;
}
@@ -2158,15 +2158,9 @@ int cmd_kvm(int argc, const char **argv)
if (!perf_host)
perf_guest = 1;
- if (!file_name) {
+ if (!file_name)
file_name = get_filename_for_perf_kvm();
- if (!file_name) {
- pr_err("Failed to allocate memory for filename\n");
- return -ENOMEM;
- }
- }
-
if (strlen(argv[0]) > 2 && strstarts("record", argv[0]))
return __cmd_record(file_name, argc, argv);
else if (strlen(argv[0]) > 2 && strstarts("report", argv[0]))
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH v3 2/3] perf tools: Fix double free and memory leak issues
2026-08-03 13:51 [PATCH v3 0/3] perf tools: Fix memory issues Michalis Niarchos via B4 Relay
2026-08-03 13:51 ` [PATCH v3 1/3] perf tools: Fix memory leak in cmd_kvm() Michalis Niarchos via B4 Relay
@ 2026-08-03 13:51 ` Michalis Niarchos via B4 Relay
2026-08-03 13:51 ` [PATCH v3 3/3] perf tools: Fix memory leak in cmd_kvm() Michalis Niarchos via B4 Relay
2 siblings, 0 replies; 4+ messages in thread
From: Michalis Niarchos via B4 Relay @ 2026-08-03 13:51 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, James Clark
Cc: linux-perf-users, linux-kernel, Michalis Niarchos
From: Michalis Niarchos <michael.niarchos@gmail.com>
parse_options() reorders the contents of the rec_argv pointer array, so
its entry order no longer matches the order in which the caller
originally allocated them. Freeing the contents of rec_argv by
iterating the reordered array results in some pointers being freed
twice and others never freed.
All the entries of rec_argv come from literals or pointers that are
valid for the lifetime of the respective call. Reference them directly
instead of duplicating to remove the need to individually track and free
each entry.
Signed-off-by: Michalis Niarchos <michael.niarchos@gmail.com>
---
tools/perf/builtin-kvm.c | 51 +++++++++---------------
tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c | 4 +-
tools/perf/util/kvm-stat-arch/kvm-stat-x86.c | 10 ++---
3 files changed, 24 insertions(+), 41 deletions(-)
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 16cfa7ce7856..8e14d037d7d9 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -1681,18 +1681,18 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(record_args); i++)
- rec_argv[i] = STRDUP_FAIL_EXIT(record_args[i]);
+ rec_argv[i] = record_args[i];
for (j = 0; j < events_tp_size; j++) {
- rec_argv[i++] = STRDUP_FAIL_EXIT("-e");
- rec_argv[i++] = STRDUP_FAIL_EXIT(kvm_events_tp(e_machine)[j]);
+ rec_argv[i++] = "-e";
+ rec_argv[i++] = kvm_events_tp(e_machine)[j];
}
- rec_argv[i++] = STRDUP_FAIL_EXIT("-o");
- rec_argv[i++] = STRDUP_FAIL_EXIT(kvm->file_name);
+ rec_argv[i++] = "-o";
+ rec_argv[i++] = kvm->file_name;
for (j = 1; j < (unsigned int)argc; j++, i++)
- rec_argv[i] = STRDUP_FAIL_EXIT(argv[j]);
+ rec_argv[i] = argv[j];
set_option_flag(record_options, 'e', "event", PARSE_OPT_HIDDEN);
set_option_flag(record_options, 0, "filter", PARSE_OPT_HIDDEN);
@@ -1717,9 +1717,6 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
record_usage = kvm_stat_record_usage;
ret = cmd_record(i, rec_argv);
-EXIT:
- for (i = 0; i < rec_argc; i++)
- free((void *)rec_argv[i]);
free(rec_argv);
return ret;
}
@@ -2006,11 +2003,11 @@ static int __cmd_record(const char *file_name, int argc, const char **argv)
if (!rec_argv)
return -ENOMEM;
- rec_argv[i++] = STRDUP_FAIL_EXIT("record");
- rec_argv[i++] = STRDUP_FAIL_EXIT("-o");
- rec_argv[i++] = STRDUP_FAIL_EXIT(file_name);
+ rec_argv[i++] = "record";
+ rec_argv[i++] = "-o";
+ rec_argv[i++] = file_name;
for (j = 1; j < argc; j++, i++)
- rec_argv[i] = STRDUP_FAIL_EXIT(argv[j]);
+ rec_argv[i] = argv[j];
BUG_ON(i + 2 != rec_argc);
@@ -2021,8 +2018,6 @@ static int __cmd_record(const char *file_name, int argc, const char **argv)
ret = cmd_record(i, rec_argv);
EXIT:
- for (i = 0; i < rec_argc; i++)
- free((void *)rec_argv[i]);
free(rec_argv);
return ret;
}
@@ -2037,19 +2032,16 @@ static int __cmd_report(const char *file_name, int argc, const char **argv)
if (!rec_argv)
return -ENOMEM;
- rec_argv[i++] = STRDUP_FAIL_EXIT("report");
- rec_argv[i++] = STRDUP_FAIL_EXIT("-i");
- rec_argv[i++] = STRDUP_FAIL_EXIT(file_name);
+ rec_argv[i++] = "report";
+ rec_argv[i++] = "-i";
+ rec_argv[i++] = file_name;
for (j = 1; j < argc; j++, i++)
- rec_argv[i] = STRDUP_FAIL_EXIT(argv[j]);
+ rec_argv[i] = argv[j];
BUG_ON(i != rec_argc);
ret = cmd_report(i, rec_argv);
-EXIT:
- for (i = 0; i < rec_argc; i++)
- free((void *)rec_argv[i]);
free(rec_argv);
return ret;
}
@@ -2065,19 +2057,16 @@ __cmd_buildid_list(const char *file_name, int argc, const char **argv)
if (!rec_argv)
return -ENOMEM;
- rec_argv[i++] = STRDUP_FAIL_EXIT("buildid-list");
- rec_argv[i++] = STRDUP_FAIL_EXIT("-i");
- rec_argv[i++] = STRDUP_FAIL_EXIT(file_name);
+ rec_argv[i++] = "buildid-list";
+ rec_argv[i++] = "-i";
+ rec_argv[i++] = file_name;
for (j = 1; j < argc; j++, i++)
- rec_argv[i] = STRDUP_FAIL_EXIT(argv[j]);
+ rec_argv[i] = argv[j];
BUG_ON(i != rec_argc);
ret = cmd_buildid_list(i, rec_argv);
-EXIT:
- for (i = 0; i < rec_argc; i++)
- free((void *)rec_argv[i]);
free(rec_argv);
return ret;
}
@@ -2097,7 +2086,7 @@ static int __cmd_top(int argc, const char **argv)
return -ENOMEM;
for (i = 0; i < argc; i++)
- rec_argv[i] = STRDUP_FAIL_EXIT(argv[i]);
+ rec_argv[i] = argv[i];
BUG_ON(i != argc);
@@ -2108,8 +2097,6 @@ static int __cmd_top(int argc, const char **argv)
ret = cmd_top(i, rec_argv);
EXIT:
- for (i = 0; i < rec_argc; i++)
- free((void *)rec_argv[i]);
free(rec_argv);
return ret;
}
diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c b/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c
index 96d9c4ae0209..37f36c6bf895 100644
--- a/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c
+++ b/tools/perf/util/kvm-stat-arch/kvm-stat-powerpc.c
@@ -196,8 +196,8 @@ int __kvm_add_default_arch_event_powerpc(int *argc, const char **argv)
parse_options(j, tmp, event_options, NULL, PARSE_OPT_KEEP_UNKNOWN);
if (!event) {
if (perf_pmus__have_event("trace_imc", "trace_cycles")) {
- argv[j++] = strdup("-e");
- argv[j++] = strdup("trace_imc/trace_cycles/");
+ argv[j++] = "-e";
+ argv[j++] = "trace_imc/trace_cycles/";
*argc += 2;
} else {
free(tmp);
diff --git a/tools/perf/util/kvm-stat-arch/kvm-stat-x86.c b/tools/perf/util/kvm-stat-arch/kvm-stat-x86.c
index 788d216f0852..67babdd3daf1 100644
--- a/tools/perf/util/kvm-stat-arch/kvm-stat-x86.c
+++ b/tools/perf/util/kvm-stat-arch/kvm-stat-x86.c
@@ -213,7 +213,7 @@ int __kvm_add_default_arch_event_x86(int *argc, const char **argv)
{
const char **tmp;
bool event = false;
- int ret = 0, i, j = *argc;
+ int i, j = *argc;
const struct option event_options[] = {
OPT_BOOLEAN('e', "event", &event, NULL),
@@ -233,17 +233,13 @@ int __kvm_add_default_arch_event_x86(int *argc, const char **argv)
parse_options(j, tmp, event_options, NULL, PARSE_OPT_KEEP_UNKNOWN);
if (!event) {
- argv[j++] = STRDUP_FAIL_EXIT("-e");
- argv[j++] = STRDUP_FAIL_EXIT("cycles");
+ argv[j++] = "-e";
+ argv[j++] = "cycles";
*argc += 2;
}
free(tmp);
return 0;
-
-EXIT:
- free(tmp);
- return ret;
}
const char * const *__kvm_events_tp_x86(void)
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH v3 3/3] perf tools: Fix memory leak in cmd_kvm()
2026-08-03 13:51 [PATCH v3 0/3] perf tools: Fix memory issues Michalis Niarchos via B4 Relay
2026-08-03 13:51 ` [PATCH v3 1/3] perf tools: Fix memory leak in cmd_kvm() Michalis Niarchos via B4 Relay
2026-08-03 13:51 ` [PATCH v3 2/3] perf tools: Fix double free and memory leak issues Michalis Niarchos via B4 Relay
@ 2026-08-03 13:51 ` Michalis Niarchos via B4 Relay
2 siblings, 0 replies; 4+ messages in thread
From: Michalis Niarchos via B4 Relay @ 2026-08-03 13:51 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Ian Rogers, Adrian Hunter, James Clark
Cc: linux-perf-users, linux-kernel, Michalis Niarchos
From: Michalis Niarchos <michael.niarchos@gmail.com>
The usage string is allocated by parse_options_subcommand() and freed
only on one return path. Using a single return point guarantees it is
freed on all occasions.
Signed-off-by: Michalis Niarchos <michael.niarchos@gmail.com>
---
tools/perf/builtin-kvm.c | 36 +++++++++++++++++++++++-------------
1 file changed, 23 insertions(+), 13 deletions(-)
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 8e14d037d7d9..04cf9bd5b595 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -2132,6 +2132,7 @@ int cmd_kvm(int argc, const char **argv)
const char *const kvm_subcommands[] = { "top", "record", "report", "diff",
"buildid-list", "stat", NULL };
const char *kvm_usage[] = { NULL, NULL };
+ int ret = 0;
exclude_GH_default = true;
perf_host = 0;
@@ -2148,25 +2149,34 @@ int cmd_kvm(int argc, const char **argv)
if (!file_name)
file_name = get_filename_for_perf_kvm();
- if (strlen(argv[0]) > 2 && strstarts("record", argv[0]))
- return __cmd_record(file_name, argc, argv);
- else if (strlen(argv[0]) > 2 && strstarts("report", argv[0]))
- return __cmd_report(file_name, argc, argv);
- else if (strlen(argv[0]) > 2 && strstarts("diff", argv[0]))
- return cmd_diff(argc, argv);
- else if (!strcmp(argv[0], "top"))
- return __cmd_top(argc, argv);
- else if (strlen(argv[0]) > 2 && strstarts("buildid-list", argv[0]))
- return __cmd_buildid_list(file_name, argc, argv);
+ if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) {
+ ret = __cmd_record(file_name, argc, argv);
+ goto exit;
+ } else if (strlen(argv[0]) > 2 && strstarts("report", argv[0])) {
+ ret = __cmd_report(file_name, argc, argv);
+ goto exit;
+ } else if (strlen(argv[0]) > 2 && strstarts("diff", argv[0])) {
+ ret = cmd_diff(argc, argv);
+ goto exit;
+ } else if (!strcmp(argv[0], "top")) {
+ ret = __cmd_top(argc, argv);
+ goto exit;
+ } else if (strlen(argv[0]) > 2 && strstarts("buildid-list", argv[0])) {
+ ret = __cmd_buildid_list(file_name, argc, argv);
+ goto exit;
+ }
#if defined(HAVE_LIBTRACEEVENT)
- else if (strlen(argv[0]) > 2 && strstarts("stat", argv[0]))
- return kvm_cmd_stat(file_name, argc, argv);
+ else if (strlen(argv[0]) > 2 && strstarts("stat", argv[0])) {
+ ret = kvm_cmd_stat(file_name, argc, argv);
+ goto exit;
+ }
#endif
else
usage_with_options(kvm_usage, kvm_options);
+exit:
/* free usage string allocated by parse_options_subcommand */
free((void *)kvm_usage[0]);
- return 0;
+ return ret;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread