* [PATCH v4 0/2] lib/string_helpers.c: fix infinite loop in string_get_size()
@ 2015-09-15 13:55 Vitaly Kuznetsov
2015-09-15 13:55 ` [PATCH v4 1/2] " Vitaly Kuznetsov
2015-09-15 13:55 ` [PATCH v4 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-15 13:55 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 v3:
- NULL termination fixes in PATCH 2/2 [Rasmus Villemoes, 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 v4 1/2] lib/string_helpers.c: fix infinite loop in string_get_size()
2015-09-15 13:55 [PATCH v4 0/2] lib/string_helpers.c: fix infinite loop in string_get_size() Vitaly Kuznetsov
@ 2015-09-15 13:55 ` Vitaly Kuznetsov
2015-09-15 13:55 ` [PATCH v4 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-15 13:55 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 v4 2/2] lib/test-string_helpers.c: add string_get_size() tests
2015-09-15 13:55 [PATCH v4 0/2] lib/string_helpers.c: fix infinite loop in string_get_size() Vitaly Kuznetsov
2015-09-15 13:55 ` [PATCH v4 1/2] " Vitaly Kuznetsov
@ 2015-09-15 13:55 ` Vitaly Kuznetsov
2015-09-15 15:20 ` Andy Shevchenko
1 sibling, 1 reply; 7+ messages in thread
From: Vitaly Kuznetsov @ 2015-09-15 13:55 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..b00fc0a 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[16];
+
+ string_get_size(size, blk_size, units, buf, sizeof(buf));
+ if (!memcmp(buf, exp_result, strnlen(exp_result, sizeof(buf) - 1) + 1))
+ return;
+
+ buf[sizeof(buf) - 1] = '\0';
+ 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 v4 2/2] lib/test-string_helpers.c: add string_get_size() tests
2015-09-15 13:55 ` [PATCH v4 2/2] lib/test-string_helpers.c: add string_get_size() tests Vitaly Kuznetsov
@ 2015-09-15 15:20 ` Andy Shevchenko
2015-09-16 11:21 ` Rasmus Villemoes
0 siblings, 1 reply; 7+ messages in thread
From: Andy Shevchenko @ 2015-09-15 15:20 UTC (permalink / raw)
To: Vitaly Kuznetsov, Andrew Morton
Cc: Rasmus Villemoes, James Bottomley, linux-kernel, K. Y. Srinivasan
On Tue, 2015-09-15 at 15:55 +0200, Vitaly Kuznetsov wrote:
> 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.
>
Minor comments to address, otherwise
Reviewed-by:
Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> 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..b00fc0a 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[16];
> +
> + string_get_size(size, blk_size, units, buf, sizeof(buf));
> + if (!memcmp(buf, exp_result, strnlen(exp_result, sizeof(buf)
> - 1) + 1))
Actually you don't need to do this +- 1. Either you will have '\0' or
not, it will be checked by memcmp() anyway.
Thus,
memcmp(buf, exp_result, strnlen(exp_result, sizeof(buf))).
> + return;
> +
Perhaps one line comment here
/* Make sure that buf will be always NULL-terminated */
> + buf[sizeof(buf) - 1] = '\0';
> + 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);
Here I recommend to use single quotes (just to see empty strings)
…expected '%s', got '%s'…
> +}
> +
> +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);
--
Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Intel Finland Oy
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4 2/2] lib/test-string_helpers.c: add string_get_size() tests
2015-09-15 15:20 ` Andy Shevchenko
@ 2015-09-16 11:21 ` Rasmus Villemoes
2015-09-16 11:33 ` Andy Shevchenko
0 siblings, 1 reply; 7+ messages in thread
From: Rasmus Villemoes @ 2015-09-16 11:21 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Vitaly Kuznetsov, Andrew Morton, James Bottomley, linux-kernel,
K. Y. Srinivasan
On Tue, Sep 15 2015, Andy Shevchenko <andriy.shevchenko@linux.intel.com> wrote:
> On Tue, 2015-09-15 at 15:55 +0200, Vitaly Kuznetsov 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[16];
>> +
>> + string_get_size(size, blk_size, units, buf, sizeof(buf));
>> + if (!memcmp(buf, exp_result, strnlen(exp_result, sizeof(buf)
>> - 1) + 1))
>
> Actually you don't need to do this +- 1. Either you will have '\0' or
> not, it will be checked by memcmp() anyway.
>
> Thus,
> memcmp(buf, exp_result, strnlen(exp_result, sizeof(buf))).
Huh? How does that ensure that string_get_size put a '\0' at the right
spot? We do need the comparison to also cover the terminating '\0' in
exp_result.
[It would be nice if we could assert at compile-time that
strlen(exp_result) < sizeof(buf).]
> Perhaps one line comment here
> /* Make sure that buf will be always NULL-terminated */
>
>> + buf[sizeof(buf) - 1] = '\0';
<bikeshed>Could we pretty-please use different names for 0 the pointer
and 0 the character, say in this case nul or NUL or '\0' or simply
0. Also, I don't see the value of the comment; that line is a totally
standard idiom.</bikeshed>.
>> + pr_warn("expected: %s, got %s\n", exp_result, buf);
>
> Here I recommend to use single quotes (just to see empty strings)
>
> …expected '%s', got '%s'…
Good idea.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4 2/2] lib/test-string_helpers.c: add string_get_size() tests
2015-09-16 11:21 ` Rasmus Villemoes
@ 2015-09-16 11:33 ` Andy Shevchenko
2015-09-16 16:34 ` Vitaly Kuznetsov
0 siblings, 1 reply; 7+ messages in thread
From: Andy Shevchenko @ 2015-09-16 11:33 UTC (permalink / raw)
To: Rasmus Villemoes
Cc: Vitaly Kuznetsov, Andrew Morton, James Bottomley, linux-kernel,
K. Y. Srinivasan
On Wed, 2015-09-16 at 13:21 +0200, Rasmus Villemoes wrote:
> On Tue, Sep 15 2015, Andy Shevchenko <
> andriy.shevchenko@linux.intel.com> wrote:
>
> > On Tue, 2015-09-15 at 15:55 +0200, Vitaly Kuznetsov 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[16];
> > > +
> > > + string_get_size(size, blk_size, units, buf,
> > > sizeof(buf));
> > > + if (!memcmp(buf, exp_result, strnlen(exp_result,
> > > sizeof(buf)
> > > - 1) + 1))
> >
> > Actually you don't need to do this +- 1. Either you will have '\0'
> > or
> > not, it will be checked by memcmp() anyway.
> >
> > Thus,
> > memcmp(buf, exp_result, strnlen(exp_result, sizeof(buf))).
>
> Huh? How does that ensure that string_get_size put a '\0' at the
> right
> spot? We do need the comparison to also cover the terminating '\0' in
> exp_result.
Ah, you are right.
But seems we have length of the exp_result always smaller than buffer
size, so, would we change this to
memcmp(…, strlen(exp_result) + 1);
?
> [It would be nice if we could assert at compile-time that
> strlen(exp_result) < sizeof(buf).]
Interesting if BUILD_BUG_ON can help here. Can we use
sizeof(exp_result) since all of them are literal constants?
>
> > Perhaps one line comment here
> > /* Make sure that buf will be always NULL-terminated */
> >
> > > + buf[sizeof(buf) - 1] = '\0';
>
> <bikeshed>Could we pretty-please use different names for 0 the
> pointer
> and 0 the character, say in this case nul or NUL or '\0' or simply
> 0. Also, I don't see the value of the comment; that line is a totally
> standard idiom.</bikeshed>.
Got your point.
--
Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Intel Finland Oy
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4 2/2] lib/test-string_helpers.c: add string_get_size() tests
2015-09-16 11:33 ` Andy Shevchenko
@ 2015-09-16 16:34 ` Vitaly Kuznetsov
0 siblings, 0 replies; 7+ messages in thread
From: Vitaly Kuznetsov @ 2015-09-16 16:34 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 Wed, 2015-09-16 at 13:21 +0200, Rasmus Villemoes wrote:
>> On Tue, Sep 15 2015, Andy Shevchenko <
>> andriy.shevchenko@linux.intel.com> wrote:
>>
>> > On Tue, 2015-09-15 at 15:55 +0200, Vitaly Kuznetsov 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[16];
>> > > +
>> > > + string_get_size(size, blk_size, units, buf,
>> > > sizeof(buf));
>> > > + if (!memcmp(buf, exp_result, strnlen(exp_result,
>> > > sizeof(buf)
>> > > - 1) + 1))
>> >
>> > Actually you don't need to do this +- 1. Either you will have '\0'
>> > or
>> > not, it will be checked by memcmp() anyway.
>> >
>> > Thus,
>> > memcmp(buf, exp_result, strnlen(exp_result, sizeof(buf))).
>>
>> Huh? How does that ensure that string_get_size put a '\0' at the
>> right
>> spot? We do need the comparison to also cover the terminating '\0' in
>> exp_result.
>
> Ah, you are right.
>
> But seems we have length of the exp_result always smaller than buffer
> size, so, would we change this to
> memcmp(…, strlen(exp_result) + 1);
> ?
>
>> [It would be nice if we could assert at compile-time that
>> strlen(exp_result) < sizeof(buf).]
>
> Interesting if BUILD_BUG_ON can help here. Can we use
> sizeof(exp_result) since all of them are literal constants?
>
Yes it can. The following seems to be working for me:
+#define string_get_size_maxbuf 16
+#define test_string_get_size_one(size, blk_size, units, exp_result) \
+ do { \
+ BUILD_BUG_ON(sizeof(exp_result) >= string_get_size_maxbuf); \
+ __test_string_get_size((size), (blk_size), (units), \
+ (exp_result)); \
+ } while(0);
+
+
+static __init void __test_string_get_size(u64 size, u64 blk_size,
+ const enum string_size_units units,
+ const char *exp_result)
+{
+ char buf[string_get_size_maxbuf];
+
+ string_get_size(size, blk_size, units, buf, sizeof(buf));
+ if (!memcmp(buf, exp_result, strlen(exp_result) + 1))
+ return;
+
+ buf[sizeof(buf) - 1] = '\0';
+ 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);
+}
>>
>> > Perhaps one line comment here
>> > /* Make sure that buf will be always NULL-terminated */
>> >
>> > > + buf[sizeof(buf) - 1] = '\0';
>>
>> <bikeshed>Could we pretty-please use different names for 0 the
>> pointer
>> and 0 the character, say in this case nul or NUL or '\0' or simply
>> 0. Also, I don't see the value of the comment; that line is a totally
>> standard idiom.</bikeshed>.
>
> Got your point.
I also wanted to avoid the comment as it is self-explanatory.
--
Vitaly
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2015-09-16 16:34 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-09-15 13:55 [PATCH v4 0/2] lib/string_helpers.c: fix infinite loop in string_get_size() Vitaly Kuznetsov
2015-09-15 13:55 ` [PATCH v4 1/2] " Vitaly Kuznetsov
2015-09-15 13:55 ` [PATCH v4 2/2] lib/test-string_helpers.c: add string_get_size() tests Vitaly Kuznetsov
2015-09-15 15:20 ` Andy Shevchenko
2015-09-16 11:21 ` Rasmus Villemoes
2015-09-16 11:33 ` Andy Shevchenko
2015-09-16 16:34 ` Vitaly Kuznetsov
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).