Skip to content

Commit a069d0a

Browse files
mlylegregkh
authored andcommitted
bcache: fix bch_hprint crash and improve output
commit 9276717b9e297a62d1151a43d1cd286213f68eb7 upstream. Most importantly, solve a crash where %llu was used to format signed numbers. This would cause a buffer overflow when reading sysfs writeback_rate_debug, as only 20 bytes were allocated for this and %llu writes 20 characters plus a null. Always use the units mechanism rather than having different output paths for simplicity. Also, correct problems with display output where 1.10 was a larger number than 1.09, by multiplying by 10 and then dividing by 1024 instead of dividing by 100. (Remainders of >= 1000 would print as .10). Minor changes: Always display the decimal point instead of trying to omit it based on number of digits shown. Decide what units to use based on 1000 as a threshold, not 1024 (in other words, always print at most 3 digits before the decimal point). Signed-off-by: Michael Lyle <mlyle@lyle.org> Reported-by: Dmitry Yu Okunev <dyokunev@ut.mephi.ru> Acked-by: Kent Overstreet <kent.overstreet@gmail.com> Reviewed-by: Coly Li <colyli@suse.de> Signed-off-by: Jens Axboe <axboe@kernel.dk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent f522051 commit a069d0a

1 file changed

Lines changed: 35 additions & 15 deletions

File tree

drivers/md/bcache/util.c

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,24 +73,44 @@ STRTO_H(strtouint, unsigned int)
7373
STRTO_H(strtoll, long long)
7474
STRTO_H(strtoull, unsigned long long)
7575

76+
/**
77+
* bch_hprint() - formats @v to human readable string for sysfs.
78+
*
79+
* @v - signed 64 bit integer
80+
* @buf - the (at least 8 byte) buffer to format the result into.
81+
*
82+
* Returns the number of bytes used by format.
83+
*/
7684
ssize_t bch_hprint(char *buf, int64_t v)
7785
{
7886
static const char units[] = "?kMGTPEZY";
79-
char dec[4] = "";
80-
int u, t = 0;
81-
82-
for (u = 0; v >= 1024 || v <= -1024; u++) {
83-
t = v & ~(~0 << 10);
84-
v >>= 10;
85-
}
86-
87-
if (!u)
88-
return sprintf(buf, "%llu", v);
89-
90-
if (v < 100 && v > -100)
91-
snprintf(dec, sizeof(dec), ".%i", t / 100);
92-
93-
return sprintf(buf, "%lli%s%c", v, dec, units[u]);
87+
int u = 0, t;
88+
89+
uint64_t q;
90+
91+
if (v < 0)
92+
q = -v;
93+
else
94+
q = v;
95+
96+
/* For as long as the number is more than 3 digits, but at least
97+
* once, shift right / divide by 1024. Keep the remainder for
98+
* a digit after the decimal point.
99+
*/
100+
do {
101+
u++;
102+
103+
t = q & ~(~0 << 10);
104+
q >>= 10;
105+
} while (q >= 1000);
106+
107+
if (v < 0)
108+
/* '-', up to 3 digits, '.', 1 digit, 1 character, null;
109+
* yields 8 bytes.
110+
*/
111+
return sprintf(buf, "-%llu.%i%c", q, t * 10 / 1024, units[u]);
112+
else
113+
return sprintf(buf, "%llu.%i%c", q, t * 10 / 1024, units[u]);
94114
}
95115

96116
ssize_t bch_snprint_string_list(char *buf, size_t size, const char * const list[],

0 commit comments

Comments
 (0)