Re: [PATCH] tools/power turbostat: Read complete CPU lists
From: Len Brown
Date: Thu Oct 01 2026 - 10:55:33 EST
Applied -- thanks!
-Len Brown
Intel Open Source Technology Center
On Wed, Jul 29, 2026 at 11:00 PM Ali Ahmet Memis <ali@xxxxxxxxxxxxxx> wrote:
>
> CPU_SUBSET_MAXCPUS accepts CPU IDs through 8191, but the sysfs and
> cgroup CPU-list readers stop at 1024 bytes. A fragmented cpuset or
> hybrid PMU CPU list can exceed that size and be truncated.
> initialize_cpu_set_from_sysfs() can also pass a full, non-NUL-terminated
> buffer to parse_cpu_str(), causing an out-of-bounds read.
>
> Additionally, initialize_cpu_set_from_sysfs() returns with the file
> still open on success. It is now called once per CPU for
> thread_siblings_list, so large systems can leak thousands of descriptors
> during topology discovery.
>
> Use getline() so both readers receive a complete, NUL-terminated line.
> Close and free the sysfs CPU-list resources on every path, and report
> malformed input from the correct buffer.
>
> Fixes: eb187540d13a ("tools/power turbostat: Increase CPU_SUBSET_MAXCPUS to 8192")
> Fixes: 58839fdbd441 ("tools/power turbostat: Process HT siblings in CPU order")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Ali Ahmet Memis <ali@xxxxxxxxxxxxxx>
> ---
> tools/power/x86/turbostat/turbostat.c | 44 +++++++++++++++------------
> 1 file changed, 25 insertions(+), 19 deletions(-)
>
> diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
> index 920694c3c..cec3f2fbb 100644
> --- a/tools/power/x86/turbostat/turbostat.c
> +++ b/tools/power/x86/turbostat/turbostat.c
> @@ -6311,33 +6311,35 @@ int for_all_proc_cpus(int (func) (int))
>
> #define PATH_EFFECTIVE_CPUS "/sys/fs/cgroup/cpuset.cpus.effective"
>
> -static char cpu_effective_str[1024];
> +static char *cpu_effective_str;
>
> static int update_effective_str(bool startup)
> {
> FILE *fp;
> - char *pos;
> - char buf[1024];
> + char *buf = NULL;
> + size_t size = 0;
> int ret;
>
> - if (cpu_effective_str[0] == '\0' && !startup)
> + if (!cpu_effective_str && !startup)
> return 0;
>
> fp = fopen(PATH_EFFECTIVE_CPUS, "r");
> if (!fp)
> return 0;
>
> - pos = fgets(buf, 1024, fp);
> - if (!pos)
> + if (getline(&buf, &size, fp) < 0)
> err(1, "%s: file read failed", PATH_EFFECTIVE_CPUS);
>
> fclose(fp);
>
> - ret = strncmp(cpu_effective_str, buf, 1024);
> - if (!ret)
> + ret = cpu_effective_str ? strcmp(cpu_effective_str, buf) : 1;
> + if (!ret) {
> + free(buf);
> return 0;
> + }
>
> - strncpy(cpu_effective_str, buf, 1024);
> + free(cpu_effective_str);
> + cpu_effective_str = buf;
> return 1;
> }
>
> @@ -8529,13 +8531,16 @@ int add_rapl_perf_counter(int cpu, struct rapl_counter_info_t *rci, const struct
> return ret;
> }
>
> -char cpuset_buf[1024];
> -int initialize_cpu_set_from_sysfs(cpu_set_t *cpu_set, char *sysfs_path, char *sysfs_file)
> +int initialize_cpu_set_from_sysfs(cpu_set_t *cpu_set, const char *sysfs_path,
> + const char *sysfs_file)
> {
> FILE *fp;
> + char *cpuset_buf = NULL;
> + size_t size = 0;
> char path[128];
> + int ret = -1;
>
> - if (snprintf(path, 128, "%s/%s", sysfs_path, sysfs_file) > 128)
> + if (snprintf(path, sizeof(path), "%s/%s", sysfs_path, sysfs_file) >= (int)sizeof(path))
> err(-1, "%s %s", sysfs_path, sysfs_file);
>
> fp = fopen(path, "r");
> @@ -8543,19 +8548,20 @@ int initialize_cpu_set_from_sysfs(cpu_set_t *cpu_set, char *sysfs_path, char *sy
> warn("open %s", path);
> return -1;
> }
> - if (fread(cpuset_buf, sizeof(char), 1024, fp) == 0) {
> + if (getline(&cpuset_buf, &size, fp) < 0) {
> warn("read %s", sysfs_path);
> - goto err;
> + goto out;
> }
> if (parse_cpu_str(cpuset_buf, cpu_set, cpu_possible_setsize)) {
> - warnx("%s: cpu str malformat %s\n", sysfs_path, cpu_effective_str);
> - goto err;
> + warnx("%s: cpu str malformat %s\n", sysfs_path, cpuset_buf);
> + goto out;
> }
> - return 0;
> + ret = 0;
>
> -err:
> +out:
> fclose(fp);
> - return -1;
> + free(cpuset_buf);
> + return ret;
> }
>
> void print_cpu_set(char *s, cpu_set_t *set)
> --
> 2.54.0
>
--
Len Brown, Intel Open Source Technology Center