From c24362650f7a8573dc9a9991a034b8500af8eb19 Mon Sep 17 00:00:00 2001 From: Ali Ahmet Memis Date: Thu, 30 Jul 2026 06:00:31 +0300 Subject: [PATCH] tools/power turbostat: Read complete CPU lists 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@vger.kernel.org Signed-off-by: Ali Ahmet Memis Signed-off-by: Len Brown --- 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 920694c3c1ec..cec3f2fbba45 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 @@ end: 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)