[PATCH v2 3/4] perf tools: Fix memory leak in cmd_kvm()

From: Michalis Niarchos via B4 Relay

Date: Mon Aug 03 2026 - 09:28:13 EST


From: Michalis Niarchos <michael.niarchos@xxxxxxxxx>

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@xxxxxxxxx>
---
tools/perf/builtin-kvm.c | 42 +++++++++++++++++++++++-------------------
1 file changed, 23 insertions(+), 19 deletions(-)

diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 45c92ab74fdd..9504c83e2074 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -2142,6 +2142,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;
@@ -2155,34 +2156,37 @@ 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])) {
+ 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 (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 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