Thread (4 messages) 4 messages, 2 authors, 2017-09-04

Re: [PATCH] bcache: fix bch_hprint crash and improve output

From: Coly Li <hidden>
Date: 2017-09-04 16:56:43

On 2017/9/5 上午12:31, Michael Lyle wrote:
[I'm sorry for the resend-- seems I can't send plain text mode from
inbox so sent HTML mail on accident]

OK, I don't like the formulation of the code either-- I will
restructure a bit to be clearer in a bit bigger of a patch today or
tomorrow.
Yes, using "v = -v" for negative numbers, then you don't need to worry
how bits are ordered in memory. Handling complemented code with bit
operations is quite easy to introduce bug(s).
The *critical* issue is this-- if v is between -1023 and -1, the line
return sprintf(buf, "%llu", v); is run. In turn, this prints out a 20
digit number plus terminating null. In turn, bch_hprint is called
places where it is provided with a 20 character buffer, which overruns
and causes a kernel panic. This doesn't require any privilege to
trigger, so it's a user-space denial of service vulnerability.
Oh, I see why you call it a stack overflow. Yes, this fix is critical,
and sounds important. Thanks for catching and fixing it.
I discovered this when running watch cat writeback_rate_debug to
troubleshoot the control system (which I am tempted to rewrite,
because it has various issues) and I encountered multiple panics.
Wow, I will review your next version patch once you send out. Do not
forget a more detailed patch comments, please :-)

Coly Li
quoted hunk ↗ jump to hunk
Mike

On Sun, Sep 3, 2017 at 11:07 PM Coly Li [off-list ref] wrote:

On 2017/9/2 上午4:37, Michael Lyle wrote:
quoted
Solve a crash where the stack is overrun when reading sysfs and
small negative values are present. Also correct output, as before
it would output "1.10" for numbers bigger than "1.9", and negative
fractional output was incorrect.
I suggest to provide more detailed comments here, to explain,
- the purpose and expected output of bch_hprint()
- when a small negative value comes, how bhc_hprint() goes wrong
- how your patch fixes the root cause.

The reason for my suggestion is, the original code is ambiguous on
positive and negative integer values. When you see the following
for-loop in bch_hprint(),
83 for (u = 0; v >= 1024 || v <= -1024; u++) {
84 t = v & ~(~0 << 10);
85 v >>= 10;
86 }
You may notice no matter v is positive or negative, variables 't' and
'v' are handled with the bit operations which taking for granted that
'v' is always positive integer values.

I don't have very deep insight of coding theory, my intuition of a first
glance on the code gives me a warning: the code might be buggy.
Example an integer N, most of time, -N and +N has different ordering in
non most-significant bits. The original code neglects when v is negative
value, v is in form of complement code, so value t here is not correct,
then the following calculation in wrong,
91 if (v < 100 && v > -100)
92 snprintf(dec, sizeof(dec), ".%i", t / 100);

I see your patch fixes the above two lines, but your patch is not simply
understandable for me, e.g. why you use 't > 103' (I guess it is because
103*10 > 1024).

A fix much simpler in logic maybe look like this, which is enlighten by
your patch,
diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c
index 8c3a938f4bf0..bf1dbb9c3ea8 100644
--- a/drivers/md/bcache/util.c
+++ b/drivers/md/bcache/util.c
@@ -78,20 +78,26 @@ ssize_t bch_hprint(char *buf, int64_t v)
{
static const char units[] = "?kMGTPEZY";
char dec[4] = "";
+ char sign[2] = "";
int u, t = 0;

- for (u = 0; v >= 1024 || v <= -1024; u++) {
+ if (v < 0) {
+ sign[0] = '-';
+ v = -v;
+ }
+
+ for (u = 0; v >= 1024; u++) {
t = v & ~(~0 << 10);
v >>= 10;
}

if (!u)
- return sprintf(buf, "%llu", v);
+ return sprintf(buf, "%s%lli", sign, v);

- if (v < 100 && v > -100)
- snprintf(dec, sizeof(dec), ".%i", t / 100);
+ if (t > 0)
+ snprintf(dec, sizeof(dec), ".%i", (t*10)/1024);

- return sprintf(buf, "%lli%s%c", v, dec, units[u]);
+ return sprintf(buf, "%s%lli%s%c", sign, v, dec, units[u]);
}

This is just an idea in my brain which is enlighten by your patch, not
correct maybe, just for your reference. I do like the idea "t * 10 /
1024", I have to say I feel happiness when I understand it, very smart :-)

Finally please offer a more detailed comments as I suggested, thank you
in advance.

Coly Li

quoted
Signed-off-by: Michael Lyle <redacted>
Reported-by: Dmitry Yu Okunev <redacted>
---
drivers/md/bcache/util.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c
index 8c3a938f4bf0..11957038c630 100644
--- a/drivers/md/bcache/util.c
+++ b/drivers/md/bcache/util.c
@@ -86,10 +86,14 @@ ssize_t bch_hprint(char *buf, int64_t v)
}

if (!u)
- return sprintf(buf, "%llu", v);
+ return sprintf(buf, "%lli", v);

- if (v < 100 && v > -100)
- snprintf(dec, sizeof(dec), ".%i", t / 100);
+ if (t > 103) {
+ if (v > 0)
+ snprintf(dec, sizeof(dec), ".%i", t * 10 / 1024);
+ else
+ snprintf(dec, sizeof(dec), ".%i", 10 - (t * 10 / 1024));
+ }

return sprintf(buf, "%lli%s%c", v, dec, units[u]);
}
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help