]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
perf kwork: Fix address sanitizer issues
authorIan Rogers <irogers@google.com>
Thu, 21 May 2026 07:24:28 +0000 (00:24 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Fri, 22 May 2026 23:51:01 +0000 (20:51 -0300)
There is a double free in the record array due to how parse_options
will mutate the array. Fix by keeping an array that isn't
mutated. Ensure kwork_usage is freed on all paths.

Signed-off-by: Ian Rogers <irogers@google.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/builtin-kwork.c

index 59a54d11f7fa63ad24754253419e31faab2acf6b..a4604e1520023b0182d54fb2585b1065ceab1922 100644 (file)
@@ -2258,7 +2258,7 @@ static void setup_event_list(struct perf_kwork *kwork,
 static int perf_kwork__record(struct perf_kwork *kwork,
                              int argc, const char **argv)
 {
-       const char **rec_argv;
+       const char **rec_argv, **to_free = NULL;
        unsigned int rec_argc, i, j;
        struct kwork_class *class;
        int ret;
@@ -2295,16 +2295,27 @@ static int perf_kwork__record(struct perf_kwork *kwork,
 
        BUG_ON(i != rec_argc);
 
+       /* Save the pointers as the array will be mutated by cmd_record. */
+       to_free = calloc(rec_argc + 1, sizeof(char *));
+       if (to_free == NULL) {
+               ret = -ENOMEM;
+               goto EXIT;
+       }
+
        pr_debug("record comm: ");
-       for (j = 0; j < rec_argc; j++)
+       for (j = 0; j < rec_argc; j++) {
                pr_debug("%s ", rec_argv[j]);
+               to_free[j] = rec_argv[j];
+       }
        pr_debug("\n");
 
        ret = cmd_record(i, rec_argv);
 
 EXIT:
        for (i = 0; i < rec_argc; i++)
-               free((void *)rec_argv[i]);
+               free((void *)(to_free ? to_free[i] : rec_argv[i]));
+
+       free(to_free);
        free(rec_argv);
        return ret;
 }
@@ -2447,6 +2458,7 @@ int cmd_kwork(int argc, const char **argv)
        const char *const kwork_subcommands[] = {
                "record", "report", "latency", "timehist", "top", NULL
        };
+       int ret = 0;
 
        perf_tool__init(&kwork.tool, /*ordered_events=*/true);
        kwork.tool.mmap   = perf_event__process_mmap;
@@ -2463,7 +2475,7 @@ int cmd_kwork(int argc, const char **argv)
 
        if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) {
                setup_event_list(&kwork, kwork_options, kwork_usage);
-               return perf_kwork__record(&kwork, argc, argv);
+               ret = perf_kwork__record(&kwork, argc, argv);
        } else if (strlen(argv[0]) > 2 && strstarts("report", argv[0])) {
                kwork.sort_order = default_report_sort_order;
                if (argc > 1) {
@@ -2474,7 +2486,7 @@ int cmd_kwork(int argc, const char **argv)
                kwork.report = KWORK_REPORT_RUNTIME;
                setup_sorting(&kwork, report_options, report_usage);
                setup_event_list(&kwork, kwork_options, kwork_usage);
-               return perf_kwork__report(&kwork);
+               ret = perf_kwork__report(&kwork);
        } else if (strlen(argv[0]) > 2 && strstarts("latency", argv[0])) {
                kwork.sort_order = default_latency_sort_order;
                if (argc > 1) {
@@ -2485,7 +2497,7 @@ int cmd_kwork(int argc, const char **argv)
                kwork.report = KWORK_REPORT_LATENCY;
                setup_sorting(&kwork, latency_options, latency_usage);
                setup_event_list(&kwork, kwork_options, kwork_usage);
-               return perf_kwork__report(&kwork);
+               ret = perf_kwork__report(&kwork);
        } else if (strlen(argv[0]) > 2 && strstarts("timehist", argv[0])) {
                if (argc > 1) {
                        argc = parse_options(argc, argv, timehist_options, timehist_usage, 0);
@@ -2494,7 +2506,7 @@ int cmd_kwork(int argc, const char **argv)
                }
                kwork.report = KWORK_REPORT_TIMEHIST;
                setup_event_list(&kwork, kwork_options, kwork_usage);
-               return perf_kwork__timehist(&kwork);
+               ret = perf_kwork__timehist(&kwork);
        } else if (strlen(argv[0]) > 2 && strstarts("top", argv[0])) {
                kwork.sort_order = default_top_sort_order;
                if (argc > 1) {
@@ -2507,12 +2519,12 @@ int cmd_kwork(int argc, const char **argv)
                        kwork.event_list_str = "sched, irq, softirq";
                setup_event_list(&kwork, kwork_options, kwork_usage);
                setup_sorting(&kwork, top_options, top_usage);
-               return perf_kwork__top(&kwork);
+               ret = perf_kwork__top(&kwork);
        } else
                usage_with_options(kwork_usage, kwork_options);
 
        /* free usage string allocated by parse_options_subcommand */
        free((void *)kwork_usage[0]);
 
-       return 0;
+       return ret;
 }