diff options
author | delphij <delphij@FreeBSD.org> | 2011-03-23 22:08:01 +0000 |
---|---|---|
committer | delphij <delphij@FreeBSD.org> | 2011-03-23 22:08:01 +0000 |
commit | 0ad5e9d9f01929efc397a27954f0991b4daac53e (patch) | |
tree | f7277fe9ba7302c849010cbfe0797d26a5561479 /lib | |
parent | eb3b2f6984e44178fe44659cc64118f0000434c3 (diff) | |
download | FreeBSD-src-0ad5e9d9f01929efc397a27954f0991b4daac53e.zip FreeBSD-src-0ad5e9d9f01929efc397a27954f0991b4daac53e.tar.gz |
humanize_number(3) multiply the input number by 100, which could cause an
integer overflow when the input is very large (for example, 100 Pi would
become about 10 Ei which exceeded signed int64_t).
Solve this issue by splitting the division into two parts and avoid the
multiplication.
PR: bin/146205
Reviewed by: arundel
MFC after: 1 month
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libutil/humanize_number.c | 40 |
1 files changed, 24 insertions, 16 deletions
diff --git a/lib/libutil/humanize_number.c b/lib/libutil/humanize_number.c index de98587..75bcb46 100644 --- a/lib/libutil/humanize_number.c +++ b/lib/libutil/humanize_number.c @@ -43,11 +43,11 @@ __FBSDID("$FreeBSD$"); #include <libutil.h> int -humanize_number(char *buf, size_t len, int64_t bytes, +humanize_number(char *buf, size_t len, int64_t quotient, const char *suffix, int scale, int flags) { const char *prefixes, *sep; - int b, i, r, maxscale, s1, s2, sign; + int i, r, remainder, maxscale, s1, s2, sign; int64_t divisor, max; size_t baselen; @@ -55,6 +55,8 @@ humanize_number(char *buf, size_t len, int64_t bytes, assert(suffix != NULL); assert(scale >= 0); + remainder = 0; + if (flags & HN_DIVISOR_1000) { /* SI for decimal multiplies */ divisor = 1000; @@ -86,13 +88,12 @@ humanize_number(char *buf, size_t len, int64_t bytes, if (len > 0) buf[0] = '\0'; - if (bytes < 0) { + if (quotient < 0) { sign = -1; - bytes *= -100; + quotient = -quotient; baselen = 3; /* sign, digit, prefix */ } else { sign = 1; - bytes *= 100; baselen = 2; /* digit, prefix */ } if (flags & HN_NOSPACE) @@ -109,7 +110,7 @@ humanize_number(char *buf, size_t len, int64_t bytes, if (scale & (HN_AUTOSCALE | HN_GETSCALE)) { /* See if there is additional columns can be used. */ - for (max = 100, i = len - baselen; i-- > 0;) + for (max = 1, i = len - baselen; i-- > 0;) max *= 10; /* @@ -117,30 +118,37 @@ humanize_number(char *buf, size_t len, int64_t bytes, * If there will be an overflow by the rounding below, * divide once more. */ - for (i = 0; bytes >= max - 50 && i < maxscale; i++) - bytes /= divisor; + for (i = 0; + (quotient >= max || (quotient == max - 1 && remainder >= 950)) && + i < maxscale; i++) { + remainder = quotient % divisor; + quotient /= divisor; + } if (scale & HN_GETSCALE) return (i); - } else - for (i = 0; i < scale && i < maxscale; i++) - bytes /= divisor; + } else { + for (i = 0; i < scale && i < maxscale; i++) { + remainder = quotient % divisor; + quotient /= divisor; + } + } /* If a value <= 9.9 after rounding and ... */ - if (bytes < 995 && i > 0 && flags & HN_DECIMAL) { + if (quotient <= 9 && remainder < 950 && i > 0 && flags & HN_DECIMAL) { /* baselen + \0 + .N */ if (len < baselen + 1 + 2) return (-1); - b = ((int)bytes + 5) / 10; - s1 = b / 10; - s2 = b % 10; + s1 = (int)quotient + ((remainder + 50) / 1000); + s2 = ((remainder + 50) / 100) % 10; r = snprintf(buf, len, "%d%s%d%s%s%s", sign * s1, localeconv()->decimal_point, s2, sep, SCALE2PREFIX(i), suffix); } else r = snprintf(buf, len, "%" PRId64 "%s%s%s", - sign * ((bytes + 50) / 100), + sign * (quotient + (remainder + 50) / 1000), sep, SCALE2PREFIX(i), suffix); return (r); } + |