* [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).