avoid out-of-bounds read after invalid %cpu conversion

In the case where no CPU number is given, skipping a character of
padding actually skips the null terminator, causing further iterations
through the loop to read out of bounds. Have sscanf() return the number
of characters read, instead of reconstructing it from the CPU number.

This was observed as a failure in test 024-cpu-usage-invalid-cpu.
This commit is contained in:
Samuel Holland 2020-03-28 20:58:34 -05:00
parent 3374e1605d
commit 585d0700c7

View File

@ -183,7 +183,8 @@ void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const
#if defined(__linux__) #if defined(__linux__)
else if (BEGINS_WITH(walk + 1, "cpu")) { else if (BEGINS_WITH(walk + 1, "cpu")) {
int number = -1; int number = -1;
sscanf(walk + 1, "cpu%d", &number); int length = strlen("cpu");
sscanf(walk + 1, "cpu%d%n", &number, &length);
if (number == -1) { if (number == -1) {
fprintf(stderr, "i3status: provided CPU number cannot be parsed\n"); fprintf(stderr, "i3status: provided CPU number cannot be parsed\n");
} else if (number >= cpu_count) { } else if (number >= cpu_count) {
@ -194,13 +195,7 @@ void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const
int cpu_diff_usage = (cpu_diff_total ? (1000 * (cpu_diff_total - cpu_diff_idle) / cpu_diff_total + 5) / 10 : 0); int cpu_diff_usage = (cpu_diff_total ? (1000 * (cpu_diff_total - cpu_diff_idle) / cpu_diff_total + 5) / 10 : 0);
outwalk += sprintf(outwalk, "%02d%s", cpu_diff_usage, pct_mark); outwalk += sprintf(outwalk, "%02d%s", cpu_diff_usage, pct_mark);
} }
int padding = 1; walk += length;
int step = 10;
while (step <= number) {
step *= 10;
padding++;
}
walk += strlen("cpu") + padding;
} }
#endif #endif
else { else {