eta: fix buffer overflow in ETA output
authorSitsofe Wheeler <sitsofe@yahoo.com>
Thu, 21 Dec 2017 12:23:36 +0000 (12:23 +0000)
committerSitsofe Wheeler <sitsofe@yahoo.com>
Thu, 21 Dec 2017 14:30:43 +0000 (14:30 +0000)
When 900 or more jobs all have different states it is possible for the
space required to display the ETA line to be larger than the ETA output
buffer. fio initially truncates what it puts in the output buffer but
incorrectly advances where it thinks the end of the buffer is - when
truncating snprintf returns the number of characters that _would_ have
been converted if there had been enough space...

This patch fixes the post truncation calculation and adjusts the "left"
variable so there is room for the carriage return and a null terminator.

The following script reproduces the problem when fio has been compiled
with -fstack-protector or -fsanitize=address:

rw[0]='read'; rw[1]='write'; \
for i in {1..1000}; do \
 echo -e "[job$i]\nrw=${rw[$((i % 2))]}\n" \
         "ramp_time=$(((1000 - i) / 350 * 5))\n" \
         "runtime=$((60 - (i % 3) * 2))"; \
done | \
./fio --eta-newline=1s --group_reporting --ioengine=null --size=1g \
 --time_based --bs=512 --thread --rate_iops=123456 -

Fixes: https://github.com/axboe/fio/issues/500 ("Large number of threads
result in Seg faults")
Signed-off-by: Sitsofe Wheeler <sitsofe@yahoo.com>
eta.c

diff --git a/eta.c b/eta.c
index 8b77daf..087f57d 100644 (file)
--- a/eta.c
+++ b/eta.c
@@ -585,7 +585,7 @@ void display_thread_status(struct jobs_eta *je)
                        iops_str[ddir] = num2str(je->iops[ddir], 4, 1, 0, N2S_NONE);
                }
 
-               left = sizeof(output) - (p - output) - 1;
+               left = sizeof(output) - (p - output) - 2;
 
                if (je->rate[DDIR_TRIM] || je->iops[DDIR_TRIM])
                        l = snprintf(p, left,
@@ -601,6 +601,8 @@ void display_thread_status(struct jobs_eta *je)
                                rate_str[DDIR_READ], rate_str[DDIR_WRITE],
                                iops_str[DDIR_READ], iops_str[DDIR_WRITE],
                                eta_str);
+               if (l > left)
+                       l = left;
                p += l;
                if (l >= 0 && l < linelen_last)
                        p += sprintf(p, "%*s", linelen_last - l, "");