linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/2] lib/string_helpers.c: fix infinite loop in string_get_size()
@ 2015-09-14 16:45 Vitaly Kuznetsov
  2015-09-14 16:45 ` [PATCH v3 1/2] " Vitaly Kuznetsov
  2015-09-14 16:45 ` [PATCH v3 2/2] lib/test-string_helpers.c: add string_get_size() tests Vitaly Kuznetsov
  0 siblings, 2 replies; 7+ messages in thread
From: Vitaly Kuznetsov @ 2015-09-14 16:45 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Andy Shevchenko, Rasmus Villemoes, James Bottomley, linux-kernel,
	K. Y. Srinivasan

This patch series prevents string_get_size() from entering an infinite loop
on some inputs and adds several basic tests for string_get_size() including
one to test for the issue.

Changes since v2:
- Add a comment explaining the fix to string_get_size() [James Bottomley]
- Add string_get_size() tests [Andy Shevchenko]

Vitaly Kuznetsov (2):
  lib/string_helpers.c: fix infinite loop in string_get_size()
  lib/test-string_helpers.c: add string_get_size() tests

 lib/string_helpers.c      |  6 +++++-
 lib/test-string_helpers.c | 27 +++++++++++++++++++++++++++
 2 files changed, 32 insertions(+), 1 deletion(-)

-- 
2.4.3


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3 1/2] lib/string_helpers.c: fix infinite loop in string_get_size()
  2015-09-14 16:45 [PATCH v3 0/2] lib/string_helpers.c: fix infinite loop in string_get_size() Vitaly Kuznetsov
@ 2015-09-14 16:45 ` Vitaly Kuznetsov
  2015-09-14 16:45 ` [PATCH v3 2/2] lib/test-string_helpers.c: add string_get_size() tests Vitaly Kuznetsov
  1 sibling, 0 replies; 7+ messages in thread
From: Vitaly Kuznetsov @ 2015-09-14 16:45 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Andy Shevchenko, Rasmus Villemoes, James Bottomley, linux-kernel,
	K. Y. Srinivasan

Some string_get_size() calls (e.g.:
 string_get_size(1, 512, STRING_UNITS_10, ..., ...)
 string_get_size(15, 64, STRING_UNITS_10, ..., ...)
) result in an infinite loop. The problem is that if size is equal to
divisor[units]/blk_size and is smaller than divisor[units] we'll end
up with size == 0 when we start doing sf_cap calculations:

For string_get_size(1, 512, STRING_UNITS_10, ..., ...) case:
   ...
   remainder = do_div(size, divisor[units]); -> size is 0, remainder is 1
   remainder *= blk_size; -> remainder is 512
   ...
   size *= blk_size; -> size is still 0
   size += remainder / divisor[units]; -> size is still 0

The caller causing the issue is sd_read_capacity(), the problem was noticed
on Hyper-V, such weird size was reported by host when scanning collides
with device removal. This is probably a separate issue worth fixing, this
patch is intended to prevent the library routine from infinite looping.

Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Acked-by: James Bottomley <JBottomley@Odin.com>
---
 lib/string_helpers.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/lib/string_helpers.c b/lib/string_helpers.c
index c98ae81..bbf1bef 100644
--- a/lib/string_helpers.c
+++ b/lib/string_helpers.c
@@ -59,7 +59,11 @@ void string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
 	}
 
 	exp = divisor[units] / (u32)blk_size;
-	if (size >= exp) {
+	/*
+	 * size must be strictly greater than exp here to ensure that remainder
+	 * is greater than divisor[units] coming out of the if below.
+	 */
+	if (size > exp) {
 		remainder = do_div(size, divisor[units]);
 		remainder *= blk_size;
 		i++;
-- 
2.4.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v3 2/2] lib/test-string_helpers.c: add string_get_size() tests
  2015-09-14 16:45 [PATCH v3 0/2] lib/string_helpers.c: fix infinite loop in string_get_size() Vitaly Kuznetsov
  2015-09-14 16:45 ` [PATCH v3 1/2] " Vitaly Kuznetsov
@ 2015-09-14 16:45 ` Vitaly Kuznetsov
  2015-09-14 22:00   ` Rasmus Villemoes
  1 sibling, 1 reply; 7+ messages in thread
From: Vitaly Kuznetsov @ 2015-09-14 16:45 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Andy Shevchenko, Rasmus Villemoes, James Bottomley, linux-kernel,
	K. Y. Srinivasan

Add a couple of simple tests for string_get_size(). The last one will hang
the kernel without the 'lib/string_helpers.c: fix infinite loop in
string_get_size()' fix.

Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
---
 lib/test-string_helpers.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/lib/test-string_helpers.c b/lib/test-string_helpers.c
index 8e376ef..ee67ada 100644
--- a/lib/test-string_helpers.c
+++ b/lib/test-string_helpers.c
@@ -326,6 +326,30 @@ out:
 	kfree(out_test);
 }
 
+static __init void test_string_get_size_one(u64 size, u64 blk_size,
+					    const enum string_size_units units,
+					    const char *exp_result)
+{
+	char buf[256];
+
+	string_get_size(size, blk_size, units, buf, sizeof(buf));
+	if (!strncmp(buf, exp_result, min(sizeof(buf), strlen(exp_result))))
+		return;
+
+	pr_warn("Test 'test_string_get_size_one' failed!\n");
+	pr_warn("string_get_size(size = %llu, blk_size = %llu, units = %d\n",
+		size, blk_size, units);
+	pr_warn("expected: %s, got %s\n", exp_result, buf);
+
+}
+
+static __init void test_string_get_size(void)
+{
+	test_string_get_size_one(16384, 512, STRING_UNITS_2, "8.00 MiB");
+	test_string_get_size_one(8192, 4096, STRING_UNITS_10, "32.7 MB");
+	test_string_get_size_one(1, 512, STRING_UNITS_10, "512 B");
+}
+
 static int __init test_string_helpers_init(void)
 {
 	unsigned int i;
@@ -344,6 +368,9 @@ static int __init test_string_helpers_init(void)
 	for (i = 0; i < (ESCAPE_ANY_NP | ESCAPE_HEX) + 1; i++)
 		test_string_escape("escape 1", escape1, i, TEST_STRING_2_DICT_1);
 
+	/* Test string_get_size() */
+	test_string_get_size();
+
 	return -EINVAL;
 }
 module_init(test_string_helpers_init);
-- 
2.4.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 2/2] lib/test-string_helpers.c: add string_get_size() tests
  2015-09-14 16:45 ` [PATCH v3 2/2] lib/test-string_helpers.c: add string_get_size() tests Vitaly Kuznetsov
@ 2015-09-14 22:00   ` Rasmus Villemoes
  2015-09-15  6:43     ` Andy Shevchenko
  0 siblings, 1 reply; 7+ messages in thread
From: Rasmus Villemoes @ 2015-09-14 22:00 UTC (permalink / raw)
  To: Vitaly Kuznetsov
  Cc: Andrew Morton, Andy Shevchenko, James Bottomley, linux-kernel,
	K. Y. Srinivasan

On Mon, Sep 14 2015, Vitaly Kuznetsov <vkuznets@redhat.com> wrote:

> +static __init void test_string_get_size_one(u64 size, u64 blk_size,
> +					    const enum string_size_units units,
> +					    const char *exp_result)
> +{
> +	char buf[256];
> +
> +	string_get_size(size, blk_size, units, buf, sizeof(buf));
> +	if (!strncmp(buf, exp_result, min(sizeof(buf), strlen(exp_result))))
> +		return;

Nits: It probably makes sense to also test that string_get_size
'\0'-terminates the buffer, so I'd spell this

  if (!memcmp(buf, exp_result, min(sizeof(buf), strlen(exp_result)+1)))

With a generous stack buffer, that min() will always evaluate to the
strlen(exp_result)+1. On that note: Maybe 256 is a bit excessive. I
don't think this will run very deep in the kernel stack, but the code might
get copy-pasted somewhere else. 16 should be plenty.

> +	pr_warn("Test 'test_string_get_size_one' failed!\n");
> +	pr_warn("string_get_size(size = %llu, blk_size = %llu, units = %d\n",
> +		size, blk_size, units);

[There's probably no pretty way of getting from units to a text
representation, but it's slightly annoying to have to check the source
for the enum definition to figure out what units=0 or units=1 means.]

> +	pr_warn("expected: %s, got %s\n", exp_result, buf);

In case we failed to '\0'-terminate buf, we might want to print it with
"%.*s", (int)sizeof(buf), buf. But maybe I'm just overly paranoid.

Rasmus

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 2/2] lib/test-string_helpers.c: add string_get_size() tests
  2015-09-14 22:00   ` Rasmus Villemoes
@ 2015-09-15  6:43     ` Andy Shevchenko
  2015-09-15 12:10       ` Vitaly Kuznetsov
  0 siblings, 1 reply; 7+ messages in thread
From: Andy Shevchenko @ 2015-09-15  6:43 UTC (permalink / raw)
  To: Rasmus Villemoes, Vitaly Kuznetsov
  Cc: Andrew Morton, James Bottomley, linux-kernel, K. Y. Srinivasan

On Tue, 2015-09-15 at 00:00 +0200, Rasmus Villemoes wrote:
> On Mon, Sep 14 2015, Vitaly Kuznetsov <vkuznets@redhat.com> wrote:
> 

Vitaly, thanks for the test cases. My comments below.

> > +static __init void test_string_get_size_one(u64 size, u64 
> > blk_size,
> > +					    const enum 
> > string_size_units units,
> > +					    const char 
> > *exp_result)
> > +{
> > +	char buf[256];
> > +
> > +	string_get_size(size, blk_size, units, buf, sizeof(buf));
> > +	if (!strncmp(buf, exp_result, min(sizeof(buf), 
> > strlen(exp_result))))
> > +		return;
> 
> Nits: It probably makes sense to also test that string_get_size
> '\0'-terminates the buffer, so I'd spell this
> 
>   if (!memcmp(buf, exp_result, min(sizeof(buf), 
> strlen(exp_result)+1)))
> 
> With a generous stack buffer, that min() will always evaluate to the
> strlen(exp_result)+1. On that note: Maybe 256 is a bit excessive. I
> don't think this will run very deep in the kernel stack, but the code 
> might
> get copy-pasted somewhere else. 16 should be plenty.

Agree with Rasmus.

And just to make a side note that useless use of min() since we have
strnlen() :-)

> 
> > +	pr_warn("Test 'test_string_get_size_one' failed!\n");
> > +	pr_warn("string_get_size(size = %llu, blk_size = %llu, 
> > units = %d\n",
> > +		size, blk_size, units);
> 
> [There's probably no pretty way of getting from units to a text
> representation, but it's slightly annoying to have to check the 
> source
> for the enum definition to figure out what units=0 or units=1 means.]
> 
> > +	pr_warn("expected: %s, got %s\n", exp_result, buf);
> 
> In case we failed to '\0'-terminate buf, we might want to print it 
> with
> "%.*s", (int)sizeof(buf), buf. But maybe I'm just overly paranoid.

I prefer to put '\0' at the position after we expected have an actual
'\0'. In this case we always be NULL terminated. I did this for hexdump
test cases.

-- 
Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Intel Finland Oy

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 2/2] lib/test-string_helpers.c: add string_get_size() tests
  2015-09-15  6:43     ` Andy Shevchenko
@ 2015-09-15 12:10       ` Vitaly Kuznetsov
  2015-09-15 12:19         ` Andy Shevchenko
  0 siblings, 1 reply; 7+ messages in thread
From: Vitaly Kuznetsov @ 2015-09-15 12:10 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Rasmus Villemoes, Andrew Morton, James Bottomley, linux-kernel,
	K. Y. Srinivasan

Andy Shevchenko <andriy.shevchenko@linux.intel.com> writes:

> On Tue, 2015-09-15 at 00:00 +0200, Rasmus Villemoes wrote:
>> On Mon, Sep 14 2015, Vitaly Kuznetsov <vkuznets@redhat.com> wrote:
>> 
>
> Vitaly, thanks for the test cases. My comments below.
>
>> > +static __init void test_string_get_size_one(u64 size, u64 
>> > blk_size,
>> > +					    const enum 
>> > string_size_units units,
>> > +					    const char 
>> > *exp_result)
>> > +{
>> > +	char buf[256];
>> > +
>> > +	string_get_size(size, blk_size, units, buf, sizeof(buf));
>> > +	if (!strncmp(buf, exp_result, min(sizeof(buf), 
>> > strlen(exp_result))))
>> > +		return;
>> 
>> Nits: It probably makes sense to also test that string_get_size
>> '\0'-terminates the buffer, so I'd spell this
>> 
>>   if (!memcmp(buf, exp_result, min(sizeof(buf), 
>> strlen(exp_result)+1)))
>> 
>> With a generous stack buffer, that min() will always evaluate to the
>> strlen(exp_result)+1. On that note: Maybe 256 is a bit excessive. I
>> don't think this will run very deep in the kernel stack, but the code 
>> might
>> get copy-pasted somewhere else. 16 should be plenty.
>
> Agree with Rasmus.
>
> And just to make a side note that useless use of min() since we have
> strnlen() :-)
>
>> 
>> > +	pr_warn("Test 'test_string_get_size_one' failed!\n");
>> > +	pr_warn("string_get_size(size = %llu, blk_size = %llu, 
>> > units = %d\n",
>> > +		size, blk_size, units);
>> 
>> [There's probably no pretty way of getting from units to a text
>> representation, but it's slightly annoying to have to check the 
>> source
>> for the enum definition to figure out what units=0 or units=1 means.]
>> 
>> > +	pr_warn("expected: %s, got %s\n", exp_result, buf);
>> 
>> In case we failed to '\0'-terminate buf, we might want to print it 
>> with
>> "%.*s", (int)sizeof(buf), buf. But maybe I'm just overly paranoid.
>
> I prefer to put '\0' at the position after we expected have an actual
> '\0'. In this case we always be NULL terminated. I did this for hexdump
> test cases.

Just to check I got your suggestions right:

...
+       if (!memcmp(buf, exp_result, strnlen(exp_result, sizeof(buf) - 1) + 1))
+               return;
+
+       /* NULL terminate buf right after the expected '\0' */
+       buf[strnlen(exp_result, sizeof(buf) - 2) + 1] = '\0';
...

Alternatively, we could have avoided strnlen() by asserting
strlen(exp_result) < sizeof(buf) - 1 at the very beginning.

-- 
  Vitaly

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 2/2] lib/test-string_helpers.c: add string_get_size() tests
  2015-09-15 12:10       ` Vitaly Kuznetsov
@ 2015-09-15 12:19         ` Andy Shevchenko
  0 siblings, 0 replies; 7+ messages in thread
From: Andy Shevchenko @ 2015-09-15 12:19 UTC (permalink / raw)
  To: Vitaly Kuznetsov
  Cc: Rasmus Villemoes, Andrew Morton, James Bottomley, linux-kernel,
	K. Y. Srinivasan

On Tue, 2015-09-15 at 14:10 +0200, Vitaly Kuznetsov wrote:
> Andy Shevchenko <andriy.shevchenko@linux.intel.com> writes:
> 
> > I prefer to put '\0' at the position after we expected have an
> > actual
> > '\0'. In this case we always be NULL terminated. I did this for 
> > hexdump
> > test cases.
> 
> Just to check I got your suggestions right:
> 
> ...
> +       if (!memcmp(buf, exp_result, strnlen(exp_result, sizeof(buf) 
> - 1) + 1))
> +               return;
> +
> +       /* NULL terminate buf right after the expected '\0' */
> +       buf[strnlen(exp_result, sizeof(buf) - 2) + 1] = '\0';
> ...
> 
> Alternatively, we could have avoided strnlen() by asserting
> strlen(exp_result) < sizeof(buf) - 1 at the very beginning.
> 

Just 

buf[sizeof(buf) - 1] = '\0';

should be enough after you called the string_get_size().

And minimize the buffer to something like 16 (whatever is the biggest
possible length + '\0' + some space for the wrong algorithm aligned to
let's say 4.

-- 
Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Intel Finland Oy

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2015-09-15 12:19 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-09-14 16:45 [PATCH v3 0/2] lib/string_helpers.c: fix infinite loop in string_get_size() Vitaly Kuznetsov
2015-09-14 16:45 ` [PATCH v3 1/2] " Vitaly Kuznetsov
2015-09-14 16:45 ` [PATCH v3 2/2] lib/test-string_helpers.c: add string_get_size() tests Vitaly Kuznetsov
2015-09-14 22:00   ` Rasmus Villemoes
2015-09-15  6:43     ` Andy Shevchenko
2015-09-15 12:10       ` Vitaly Kuznetsov
2015-09-15 12:19         ` Andy Shevchenko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).