Mykola Ivanets
2019-Feb-07 13:21 UTC
[Libguestfs] [PATCH nbdkit] server: utils: Fix nbdkit_parse_size to correctly handle negative values
From: Nikolay Ivanets <stenavin@gmail.com> nbdkit_parse_size() uses strtoumax() function to parse input strings which states: "if there was a leading minus sign, the negation of the result of the conversion represented as an unsigned value, unless the original (nonnegated) value would overflow." Later validation doesn't catch the situation when parsed value is within the valid range but original string passed to a function represented negative number. Thus nbdkit_parse_size() incorrectly parsed values in a range [-UINT64_MAX; INT64_MIN -1] translating them into an unsigned range of a valid values: [1; INT64_MAX]. In this patch: 1. strtoll() is used instead of strtoumax() to reflect the nature of the 'size' which cannot be negative and isn't expected to exceed INT64_MAX. 2. Error reporting separates cases where the string is parsed to a negative value or parsed value overflows (greater then INT64_MAX). Tests: 1. Some more tests were added to cover found bug. 2. Input strings where grouped into a set which lead to valid/invalid/negative/overflow result. 3. Some strings with a leading '+' sign were added. --- server/test-utils.c | 28 ++++++++++++++++++++++++---- server/utils.c | 19 ++++++++++++++----- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/server/test-utils.c b/server/test-utils.c index f51f63a..3965fd3 100644 --- a/server/test-utils.c +++ b/server/test-utils.c @@ -62,14 +62,26 @@ test_nbdkit_parse_size (void) { "0x0", -1 }, { "garbage", -1 }, { "0garbage", -1 }, - { "-1", -1 }, - { "-2", -1 }, - { "9223372036854775808", -1 }, - { "-9223372036854775808", -1 }, { "8E", -1 }, { "8192P", -1 }, + + /* Strings leading to overflow */ + { "9223372036854775808", -1 }, /* INT_MAX + 1 */ + { "18446744073709551614", -1 }, /* UINT64_MAX - 1 */ + { "18446744073709551615", -1 }, /* UINT64_MAX */ + { "18446744073709551616", -1 }, /* UINT64_MAX + 1 */ { "999999999999999999999999", -1 }, + /* Strings representing negative values */ + { "-1", -1 }, + { "-2", -1 }, + { "-9223372036854775809", -1 }, /* INT64_MIN - 1 */ + { "-9223372036854775808", -1 }, /* INT64_MIN */ + { "-9223372036854775807", -1 }, /* INT64_MIN + 1 */ + { "-18446744073709551616", -1 }, /* -UINT64_MAX - 1 */ + { "-18446744073709551615", -1 }, /* -UINT64_MAX */ + { "-18446744073709551614", -1 }, /* -UINT64_MAX + 1 */ + /* Strings we may want to support in the future */ { "M", -1 }, { "1MB", -1 }, @@ -77,8 +89,14 @@ test_nbdkit_parse_size (void) { "1.5M", -1 }, /* Valid strings */ + { "-0", 0 }, { "0", 0 }, + { "+0", 0 }, { " 08", 8 }, + { "1", 1 }, + { "+1", 1 }, + { "1234567890", 1234567890 }, + { "+1234567890", 1234567890 }, { "9223372036854775807", INT64_MAX }, { "1s", 512 }, { "2S", 1024 }, @@ -88,12 +106,14 @@ test_nbdkit_parse_size (void) { "1K", 1024 }, { "1m", 1024 * 1024 }, { "1M", 1024 * 1024 }, + { "+1M", 1024 * 1024 }, { "1g", 1024 * 1024 * 1024 }, { "1G", 1024 * 1024 * 1024 }, { "1t", 1024LL * 1024 * 1024 * 1024 }, { "1T", 1024LL * 1024 * 1024 * 1024 }, { "1p", 1024LL * 1024 * 1024 * 1024 * 1024 }, { "1P", 1024LL * 1024 * 1024 * 1024 * 1024 }, + { "8191p", 1024LL * 1024 * 1024 * 1024 * 1024 * 8191 }, { "1e", 1024LL * 1024 * 1024 * 1024 * 1024 * 1024 }, { "1E", 1024LL * 1024 * 1024 * 1024 * 1024 * 1024 }, }; diff --git a/server/utils.c b/server/utils.c index 18011fd..d711b88 100644 --- a/server/utils.c +++ b/server/utils.c @@ -88,21 +88,30 @@ nbdkit_absolute_path (const char *path) int64_t nbdkit_parse_size (const char *str) { - uint64_t size; + int64_t size; char *end; uint64_t scale = 1; - /* Disk sizes cannot usefully exceed off_t (which is signed), so - * scan unsigned, then range check later that result fits. */ + /* Disk sizes cannot usefully exceed off_t (which is signed) and + * cannot be negative. */ /* XXX Should we also parse things like '1.5M'? */ /* XXX Should we allow hex? If so, hex cannot use scaling suffixes, * because some of them are valid hex digits */ errno = 0; - size = strtoumax (str, &end, 10); - if (errno || str == end) { + size = strtoll (str, &end, 10); + if (str == end) { nbdkit_error ("could not parse size string (%s)", str); return -1; } + if (size < 0) { + nbdkit_error ("size cannot be negative (%s)", str); + return -1; + } + if (errno) { + nbdkit_error ("size exceeds maximum value (%s)", str); + return -1; + } + switch (*end) { /* No suffix */ case '\0': -- 2.17.2
Eric Blake
2019-Feb-07 14:40 UTC
Re: [Libguestfs] [PATCH nbdkit] server: utils: Fix nbdkit_parse_size to correctly handle negative values
On 2/7/19 7:21 AM, Mykola Ivanets wrote:> From: Nikolay Ivanets <stenavin@gmail.com> > > nbdkit_parse_size() uses strtoumax() function to parse input strings > which states: > > "if there was a leading minus sign, the negation of the result of the > conversion represented as an unsigned value, unless the original > (nonnegated) value would overflow." > > Later validation doesn't catch the situation when parsed value is > within the valid range but original string passed to a function > represented negative number.In other words, you don't like -18446744073709551615 being parsed successfully as '1', and instead want it to cause an error because of the leading '-'. Seems reasonable.> > Thus nbdkit_parse_size() incorrectly parsed values in a range > [-UINT64_MAX; INT64_MIN -1] translating them into an unsigned range > of a valid values: [1; INT64_MAX].I wouldn't call that incorrect, so much as being lenient in what we accept, but I'm not opposed to taking the patch.> > In this patch: > > 1. strtoll() is used instead of strtoumax() to reflect the nature > of the 'size' which cannot be negative and isn't expected to exceed > INT64_MAX.Rather, I'd go with strtoimax(), not strtoll(). It may be the same function under the hood on current platforms, but future standards may have types longer than 'long long' which we may still want to support.> 2. Error reporting separates cases where the string is parsed to a > negative value or parsed value overflows (greater then INT64_MAX). > > Tests: > > 1. Some more tests were added to cover found bug. > 2. Input strings where grouped into a set which lead to > valid/invalid/negative/overflow result. > 3. Some strings with a leading '+' sign were added. > --- > server/test-utils.c | 28 ++++++++++++++++++++++++---- > server/utils.c | 19 ++++++++++++++----- > 2 files changed, 38 insertions(+), 9 deletions(-) >> +++ b/server/utils.c > @@ -88,21 +88,30 @@ nbdkit_absolute_path (const char *path) > int64_t > nbdkit_parse_size (const char *str) > { > - uint64_t size; > + int64_t size; > char *end; > uint64_t scale = 1; > > - /* Disk sizes cannot usefully exceed off_t (which is signed), so > - * scan unsigned, then range check later that result fits. */ > + /* Disk sizes cannot usefully exceed off_t (which is signed) and > + * cannot be negative. */ > /* XXX Should we also parse things like '1.5M'? */ > /* XXX Should we allow hex? If so, hex cannot use scaling suffixes, > * because some of them are valid hex digits */ > errno = 0; > - size = strtoumax (str, &end, 10); > - if (errno || str == end) { > + size = strtoll (str, &end, 10);strtoimax() seems better here.> + if (str == end) { > nbdkit_error ("could not parse size string (%s)", str); > return -1; > } > + if (size < 0) { > + nbdkit_error ("size cannot be negative (%s)", str); > + return -1; > + } > + if (errno) { > + nbdkit_error ("size exceeds maximum value (%s)", str);This reads awkwardly - it looks like the value in () is the maximum; better might be: "size %s exceeds maximum value".> + return -1; > + } > + > switch (*end) { > /* No suffix */ > case '\0': >-- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org
Maybe Matching Threads
- [PATCH nbdkit v2] server: utils: Make nbdkit_parse_size to reject negative values
- [PATCH nbdkit] server: Move size parsing code (nbdkit_parse_size) to common/include
- [RFC nbdkit PATCH] utils: Revamp nbdkit_parse_size
- [nbdkit PATCH v2 0/2] Improve nbdkit_parse_size
- [nbdkit PATCH v2 2/2] utils: Revamp nbdkit_parse_size